418 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Copyright (C) 2021 RenewOutReach4 * Copyright (C) 2021 Amarula Solutions(India)5 *6 * Author:7 * Jagan Teki <jagan@amarulasolutions.com>8 * Christopher Vollo <chris@renewoutreach.org>9 */10 11#include <drm/drm_atomic_helper.h>12#include <drm/drm_of.h>13#include <drm/drm_print.h>14#include <drm/drm_mipi_dsi.h>15 16#include <linux/delay.h>17#include <linux/gpio/consumer.h>18#include <linux/i2c.h>19#include <linux/media-bus-format.h>20#include <linux/module.h>21#include <linux/regmap.h>22#include <linux/regulator/consumer.h>23 24enum cmd_registers {25 WR_INPUT_SOURCE = 0x05, /* Write Input Source Select */26 WR_EXT_SOURCE_FMT = 0x07, /* Write External Video Source Format */27 WR_IMAGE_CROP = 0x10, /* Write Image Crop */28 WR_DISPLAY_SIZE = 0x12, /* Write Display Size */29 WR_IMAGE_FREEZE = 0x1A, /* Write Image Freeze */30 WR_INPUT_IMAGE_SIZE = 0x2E, /* Write External Input Image Size */31 WR_RGB_LED_EN = 0x52, /* Write RGB LED Enable */32 WR_RGB_LED_CURRENT = 0x54, /* Write RGB LED Current */33 WR_RGB_LED_MAX_CURRENT = 0x5C, /* Write RGB LED Max Current */34 WR_DSI_HS_CLK = 0xBD, /* Write DSI HS Clock */35 RD_DEVICE_ID = 0xD4, /* Read Controller Device ID */36 WR_DSI_PORT_EN = 0xD7, /* Write DSI Port Enable */37};38 39enum input_source {40 INPUT_EXTERNAL_VIDEO = 0,41 INPUT_TEST_PATTERN,42 INPUT_SPLASH_SCREEN,43};44 45#define DEV_ID_MASK GENMASK(3, 0)46#define IMAGE_FREESE_EN BIT(0)47#define DSI_PORT_EN 048#define EXT_SOURCE_FMT_DSI 049#define RED_LED_EN BIT(0)50#define GREEN_LED_EN BIT(1)51#define BLUE_LED_EN BIT(2)52#define LED_MASK GENMASK(2, 0)53#define MAX_BYTE_SIZE 854 55struct dlpc {56 struct device *dev;57 struct drm_bridge bridge;58 struct drm_bridge *next_bridge;59 struct device_node *host_node;60 struct mipi_dsi_device *dsi;61 struct drm_display_mode mode;62 63 struct gpio_desc *enable_gpio;64 struct regulator *vcc_intf;65 struct regulator *vcc_flsh;66 struct regmap *regmap;67 unsigned int dsi_lanes;68};69 70static inline struct dlpc *bridge_to_dlpc(struct drm_bridge *bridge)71{72 return container_of(bridge, struct dlpc, bridge);73}74 75static bool dlpc_writeable_noinc_reg(struct device *dev, unsigned int reg)76{77 switch (reg) {78 case WR_IMAGE_CROP:79 case WR_DISPLAY_SIZE:80 case WR_INPUT_IMAGE_SIZE:81 case WR_DSI_HS_CLK:82 return true;83 default:84 return false;85 }86}87 88static const struct regmap_range dlpc_volatile_ranges[] = {89 { .range_min = 0x10, .range_max = 0xBF },90};91 92static const struct regmap_access_table dlpc_volatile_table = {93 .yes_ranges = dlpc_volatile_ranges,94 .n_yes_ranges = ARRAY_SIZE(dlpc_volatile_ranges),95};96 97static struct regmap_config dlpc_regmap_config = {98 .reg_bits = 8,99 .val_bits = 8,100 .max_register = WR_DSI_PORT_EN,101 .writeable_noinc_reg = dlpc_writeable_noinc_reg,102 .volatile_table = &dlpc_volatile_table,103 .cache_type = REGCACHE_MAPLE,104 .name = "dlpc3433",105};106 107static void dlpc_atomic_enable(struct drm_bridge *bridge,108 struct drm_bridge_state *old_bridge_state)109{110 struct dlpc *dlpc = bridge_to_dlpc(bridge);111 struct device *dev = dlpc->dev;112 struct drm_display_mode *mode = &dlpc->mode;113 struct regmap *regmap = dlpc->regmap;114 char buf[MAX_BYTE_SIZE];115 unsigned int devid;116 117 regmap_read(regmap, RD_DEVICE_ID, &devid);118 devid &= DEV_ID_MASK;119 120 DRM_DEV_DEBUG(dev, "DLPC3433 device id: 0x%02x\n", devid);121 122 if (devid != 0x01) {123 DRM_DEV_ERROR(dev, "Unsupported DLPC device id: 0x%02x\n", devid);124 return;125 }126 127 /* disable image freeze */128 regmap_write(regmap, WR_IMAGE_FREEZE, IMAGE_FREESE_EN);129 130 /* enable DSI port */131 regmap_write(regmap, WR_DSI_PORT_EN, DSI_PORT_EN);132 133 memset(buf, 0, MAX_BYTE_SIZE);134 135 /* set image crop */136 buf[4] = mode->hdisplay & 0xff;137 buf[5] = (mode->hdisplay & 0xff00) >> 8;138 buf[6] = mode->vdisplay & 0xff;139 buf[7] = (mode->vdisplay & 0xff00) >> 8;140 regmap_noinc_write(regmap, WR_IMAGE_CROP, buf, MAX_BYTE_SIZE);141 142 /* set display size */143 buf[4] = mode->hdisplay & 0xff;144 buf[5] = (mode->hdisplay & 0xff00) >> 8;145 buf[6] = mode->vdisplay & 0xff;146 buf[7] = (mode->vdisplay & 0xff00) >> 8;147 regmap_noinc_write(regmap, WR_DISPLAY_SIZE, buf, MAX_BYTE_SIZE);148 149 /* set input image size */150 buf[0] = mode->hdisplay & 0xff;151 buf[1] = (mode->hdisplay & 0xff00) >> 8;152 buf[2] = mode->vdisplay & 0xff;153 buf[3] = (mode->vdisplay & 0xff00) >> 8;154 regmap_noinc_write(regmap, WR_INPUT_IMAGE_SIZE, buf, 4);155 156 /* set external video port */157 regmap_write(regmap, WR_INPUT_SOURCE, INPUT_EXTERNAL_VIDEO);158 159 /* set external video format select as DSI */160 regmap_write(regmap, WR_EXT_SOURCE_FMT, EXT_SOURCE_FMT_DSI);161 162 /* disable image freeze */163 regmap_write(regmap, WR_IMAGE_FREEZE, 0x00);164 165 /* enable RGB led */166 regmap_update_bits(regmap, WR_RGB_LED_EN, LED_MASK,167 RED_LED_EN | GREEN_LED_EN | BLUE_LED_EN);168 169 msleep(10);170}171 172static void dlpc_atomic_pre_enable(struct drm_bridge *bridge,173 struct drm_bridge_state *old_bridge_state)174{175 struct dlpc *dlpc = bridge_to_dlpc(bridge);176 int ret;177 178 gpiod_set_value(dlpc->enable_gpio, 1);179 180 msleep(500);181 182 ret = regulator_enable(dlpc->vcc_intf);183 if (ret)184 DRM_DEV_ERROR(dlpc->dev,185 "failed to enable VCC_INTF regulator: %d\n", ret);186 187 ret = regulator_enable(dlpc->vcc_flsh);188 if (ret)189 DRM_DEV_ERROR(dlpc->dev,190 "failed to enable VCC_FLSH regulator: %d\n", ret);191 192 msleep(10);193}194 195static void dlpc_atomic_post_disable(struct drm_bridge *bridge,196 struct drm_bridge_state *old_bridge_state)197{198 struct dlpc *dlpc = bridge_to_dlpc(bridge);199 200 regulator_disable(dlpc->vcc_flsh);201 regulator_disable(dlpc->vcc_intf);202 203 msleep(10);204 205 gpiod_set_value(dlpc->enable_gpio, 0);206 207 msleep(500);208}209 210#define MAX_INPUT_SEL_FORMATS 1211 212static u32 *213dlpc_atomic_get_input_bus_fmts(struct drm_bridge *bridge,214 struct drm_bridge_state *bridge_state,215 struct drm_crtc_state *crtc_state,216 struct drm_connector_state *conn_state,217 u32 output_fmt,218 unsigned int *num_input_fmts)219{220 u32 *input_fmts;221 222 *num_input_fmts = 0;223 224 input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),225 GFP_KERNEL);226 if (!input_fmts)227 return NULL;228 229 /* This is the DSI-end bus format */230 input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;231 *num_input_fmts = 1;232 233 return input_fmts;234}235 236static void dlpc_mode_set(struct drm_bridge *bridge,237 const struct drm_display_mode *mode,238 const struct drm_display_mode *adjusted_mode)239{240 struct dlpc *dlpc = bridge_to_dlpc(bridge);241 242 drm_mode_copy(&dlpc->mode, adjusted_mode);243}244 245static int dlpc_attach(struct drm_bridge *bridge,246 enum drm_bridge_attach_flags flags)247{248 struct dlpc *dlpc = bridge_to_dlpc(bridge);249 250 return drm_bridge_attach(bridge->encoder, dlpc->next_bridge, bridge, flags);251}252 253static const struct drm_bridge_funcs dlpc_bridge_funcs = {254 .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,255 .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,256 .atomic_get_input_bus_fmts = dlpc_atomic_get_input_bus_fmts,257 .atomic_reset = drm_atomic_helper_bridge_reset,258 .atomic_pre_enable = dlpc_atomic_pre_enable,259 .atomic_enable = dlpc_atomic_enable,260 .atomic_post_disable = dlpc_atomic_post_disable,261 .mode_set = dlpc_mode_set,262 .attach = dlpc_attach,263};264 265static int dlpc3433_parse_dt(struct dlpc *dlpc)266{267 struct device *dev = dlpc->dev;268 struct device_node *endpoint;269 int ret;270 271 dlpc->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);272 if (IS_ERR(dlpc->enable_gpio))273 return PTR_ERR(dlpc->enable_gpio);274 275 dlpc->vcc_intf = devm_regulator_get(dlpc->dev, "vcc_intf");276 if (IS_ERR(dlpc->vcc_intf))277 return dev_err_probe(dev, PTR_ERR(dlpc->vcc_intf),278 "failed to get VCC_INTF supply\n");279 280 dlpc->vcc_flsh = devm_regulator_get(dlpc->dev, "vcc_flsh");281 if (IS_ERR(dlpc->vcc_flsh))282 return dev_err_probe(dev, PTR_ERR(dlpc->vcc_flsh),283 "failed to get VCC_FLSH supply\n");284 285 dlpc->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);286 if (IS_ERR(dlpc->next_bridge))287 return PTR_ERR(dlpc->next_bridge);288 289 endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0);290 dlpc->dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes");291 if (dlpc->dsi_lanes < 0 || dlpc->dsi_lanes > 4) {292 ret = -EINVAL;293 goto err_put_endpoint;294 }295 296 dlpc->host_node = of_graph_get_remote_port_parent(endpoint);297 if (!dlpc->host_node) {298 ret = -ENODEV;299 goto err_put_host;300 }301 302 of_node_put(endpoint);303 304 return 0;305 306err_put_host:307 of_node_put(dlpc->host_node);308err_put_endpoint:309 of_node_put(endpoint);310 return ret;311}312 313static int dlpc_host_attach(struct dlpc *dlpc)314{315 struct device *dev = dlpc->dev;316 struct mipi_dsi_host *host;317 struct mipi_dsi_device_info info = {318 .type = "dlpc3433",319 .channel = 0,320 .node = NULL,321 };322 int ret;323 324 host = of_find_mipi_dsi_host_by_node(dlpc->host_node);325 if (!host)326 return dev_err_probe(dev, -EPROBE_DEFER, "failed to find dsi host\n");327 328 dlpc->dsi = mipi_dsi_device_register_full(host, &info);329 if (IS_ERR(dlpc->dsi)) {330 DRM_DEV_ERROR(dev, "failed to create dsi device\n");331 return PTR_ERR(dlpc->dsi);332 }333 334 dlpc->dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST;335 dlpc->dsi->format = MIPI_DSI_FMT_RGB565;336 dlpc->dsi->lanes = dlpc->dsi_lanes;337 338 ret = devm_mipi_dsi_attach(dev, dlpc->dsi);339 if (ret)340 DRM_DEV_ERROR(dev, "failed to attach dsi host\n");341 342 return ret;343}344 345static int dlpc3433_probe(struct i2c_client *client)346{347 struct device *dev = &client->dev;348 struct dlpc *dlpc;349 int ret;350 351 dlpc = devm_kzalloc(dev, sizeof(*dlpc), GFP_KERNEL);352 if (!dlpc)353 return -ENOMEM;354 355 dlpc->dev = dev;356 357 dlpc->regmap = devm_regmap_init_i2c(client, &dlpc_regmap_config);358 if (IS_ERR(dlpc->regmap))359 return PTR_ERR(dlpc->regmap);360 361 ret = dlpc3433_parse_dt(dlpc);362 if (ret)363 return ret;364 365 dev_set_drvdata(dev, dlpc);366 i2c_set_clientdata(client, dlpc);367 368 dlpc->bridge.funcs = &dlpc_bridge_funcs;369 dlpc->bridge.of_node = dev->of_node;370 drm_bridge_add(&dlpc->bridge);371 372 ret = dlpc_host_attach(dlpc);373 if (ret)374 goto err_remove_bridge;375 376 return 0;377 378err_remove_bridge:379 drm_bridge_remove(&dlpc->bridge);380 return ret;381}382 383static void dlpc3433_remove(struct i2c_client *client)384{385 struct dlpc *dlpc = i2c_get_clientdata(client);386 387 drm_bridge_remove(&dlpc->bridge);388 of_node_put(dlpc->host_node);389}390 391static const struct i2c_device_id dlpc3433_id[] = {392 { "ti,dlpc3433", 0 },393 { /* sentinel */ }394};395MODULE_DEVICE_TABLE(i2c, dlpc3433_id);396 397static const struct of_device_id dlpc3433_match_table[] = {398 { .compatible = "ti,dlpc3433" },399 { /* sentinel */ }400};401MODULE_DEVICE_TABLE(of, dlpc3433_match_table);402 403static struct i2c_driver dlpc3433_driver = {404 .probe = dlpc3433_probe,405 .remove = dlpc3433_remove,406 .id_table = dlpc3433_id,407 .driver = {408 .name = "ti-dlpc3433",409 .of_match_table = dlpc3433_match_table,410 },411};412module_i2c_driver(dlpc3433_driver);413 414MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>");415MODULE_AUTHOR("Christopher Vollo <chris@renewoutreach.org>");416MODULE_DESCRIPTION("TI DLPC3433 MIPI DSI Display Controller Bridge");417MODULE_LICENSE("GPL");418