16352cd45SJagan Teki // SPDX-License-Identifier: GPL-2.0+
26352cd45SJagan Teki /*
36352cd45SJagan Teki * Copyright (C) 2021 RenewOutReach
46352cd45SJagan Teki * Copyright (C) 2021 Amarula Solutions(India)
56352cd45SJagan Teki *
66352cd45SJagan Teki * Author:
76352cd45SJagan Teki * Jagan Teki <jagan@amarulasolutions.com>
86352cd45SJagan Teki * Christopher Vollo <chris@renewoutreach.org>
96352cd45SJagan Teki */
106352cd45SJagan Teki
116352cd45SJagan Teki #include <drm/drm_atomic_helper.h>
126352cd45SJagan Teki #include <drm/drm_of.h>
136352cd45SJagan Teki #include <drm/drm_print.h>
146352cd45SJagan Teki #include <drm/drm_mipi_dsi.h>
156352cd45SJagan Teki
166352cd45SJagan Teki #include <linux/delay.h>
176352cd45SJagan Teki #include <linux/gpio/consumer.h>
186352cd45SJagan Teki #include <linux/i2c.h>
1972bd9ea3SVille Syrjälä #include <linux/media-bus-format.h>
206352cd45SJagan Teki #include <linux/module.h>
216352cd45SJagan Teki #include <linux/regmap.h>
226352cd45SJagan Teki #include <linux/regulator/consumer.h>
236352cd45SJagan Teki
246352cd45SJagan Teki enum cmd_registers {
256352cd45SJagan Teki WR_INPUT_SOURCE = 0x05, /* Write Input Source Select */
266352cd45SJagan Teki WR_EXT_SOURCE_FMT = 0x07, /* Write External Video Source Format */
276352cd45SJagan Teki WR_IMAGE_CROP = 0x10, /* Write Image Crop */
286352cd45SJagan Teki WR_DISPLAY_SIZE = 0x12, /* Write Display Size */
296352cd45SJagan Teki WR_IMAGE_FREEZE = 0x1A, /* Write Image Freeze */
306352cd45SJagan Teki WR_INPUT_IMAGE_SIZE = 0x2E, /* Write External Input Image Size */
316352cd45SJagan Teki WR_RGB_LED_EN = 0x52, /* Write RGB LED Enable */
326352cd45SJagan Teki WR_RGB_LED_CURRENT = 0x54, /* Write RGB LED Current */
336352cd45SJagan Teki WR_RGB_LED_MAX_CURRENT = 0x5C, /* Write RGB LED Max Current */
346352cd45SJagan Teki WR_DSI_HS_CLK = 0xBD, /* Write DSI HS Clock */
356352cd45SJagan Teki RD_DEVICE_ID = 0xD4, /* Read Controller Device ID */
366352cd45SJagan Teki WR_DSI_PORT_EN = 0xD7, /* Write DSI Port Enable */
376352cd45SJagan Teki };
386352cd45SJagan Teki
396352cd45SJagan Teki enum input_source {
406352cd45SJagan Teki INPUT_EXTERNAL_VIDEO = 0,
416352cd45SJagan Teki INPUT_TEST_PATTERN,
426352cd45SJagan Teki INPUT_SPLASH_SCREEN,
436352cd45SJagan Teki };
446352cd45SJagan Teki
456352cd45SJagan Teki #define DEV_ID_MASK GENMASK(3, 0)
466352cd45SJagan Teki #define IMAGE_FREESE_EN BIT(0)
476352cd45SJagan Teki #define DSI_PORT_EN 0
486352cd45SJagan Teki #define EXT_SOURCE_FMT_DSI 0
496352cd45SJagan Teki #define RED_LED_EN BIT(0)
506352cd45SJagan Teki #define GREEN_LED_EN BIT(1)
516352cd45SJagan Teki #define BLUE_LED_EN BIT(2)
526352cd45SJagan Teki #define LED_MASK GENMASK(2, 0)
536352cd45SJagan Teki #define MAX_BYTE_SIZE 8
546352cd45SJagan Teki
556352cd45SJagan Teki struct dlpc {
566352cd45SJagan Teki struct device *dev;
576352cd45SJagan Teki struct drm_bridge bridge;
586352cd45SJagan Teki struct drm_bridge *next_bridge;
596352cd45SJagan Teki struct device_node *host_node;
606352cd45SJagan Teki struct mipi_dsi_device *dsi;
616352cd45SJagan Teki struct drm_display_mode mode;
626352cd45SJagan Teki
636352cd45SJagan Teki struct gpio_desc *enable_gpio;
646352cd45SJagan Teki struct regulator *vcc_intf;
656352cd45SJagan Teki struct regulator *vcc_flsh;
666352cd45SJagan Teki struct regmap *regmap;
676352cd45SJagan Teki unsigned int dsi_lanes;
686352cd45SJagan Teki };
696352cd45SJagan Teki
bridge_to_dlpc(struct drm_bridge * bridge)706352cd45SJagan Teki static inline struct dlpc *bridge_to_dlpc(struct drm_bridge *bridge)
716352cd45SJagan Teki {
726352cd45SJagan Teki return container_of(bridge, struct dlpc, bridge);
736352cd45SJagan Teki }
746352cd45SJagan Teki
dlpc_writeable_noinc_reg(struct device * dev,unsigned int reg)756352cd45SJagan Teki static bool dlpc_writeable_noinc_reg(struct device *dev, unsigned int reg)
766352cd45SJagan Teki {
776352cd45SJagan Teki switch (reg) {
786352cd45SJagan Teki case WR_IMAGE_CROP:
796352cd45SJagan Teki case WR_DISPLAY_SIZE:
806352cd45SJagan Teki case WR_INPUT_IMAGE_SIZE:
816352cd45SJagan Teki case WR_DSI_HS_CLK:
826352cd45SJagan Teki return true;
836352cd45SJagan Teki default:
846352cd45SJagan Teki return false;
856352cd45SJagan Teki }
866352cd45SJagan Teki }
876352cd45SJagan Teki
886352cd45SJagan Teki static const struct regmap_range dlpc_volatile_ranges[] = {
896352cd45SJagan Teki { .range_min = 0x10, .range_max = 0xBF },
906352cd45SJagan Teki };
916352cd45SJagan Teki
926352cd45SJagan Teki static const struct regmap_access_table dlpc_volatile_table = {
936352cd45SJagan Teki .yes_ranges = dlpc_volatile_ranges,
946352cd45SJagan Teki .n_yes_ranges = ARRAY_SIZE(dlpc_volatile_ranges),
956352cd45SJagan Teki };
966352cd45SJagan Teki
976352cd45SJagan Teki static struct regmap_config dlpc_regmap_config = {
986352cd45SJagan Teki .reg_bits = 8,
996352cd45SJagan Teki .val_bits = 8,
1006352cd45SJagan Teki .max_register = WR_DSI_PORT_EN,
1016352cd45SJagan Teki .writeable_noinc_reg = dlpc_writeable_noinc_reg,
1026352cd45SJagan Teki .volatile_table = &dlpc_volatile_table,
1036352cd45SJagan Teki .cache_type = REGCACHE_RBTREE,
1046352cd45SJagan Teki .name = "dlpc3433",
1056352cd45SJagan Teki };
1066352cd45SJagan Teki
dlpc_atomic_enable(struct drm_bridge * bridge,struct drm_bridge_state * old_bridge_state)1076352cd45SJagan Teki static void dlpc_atomic_enable(struct drm_bridge *bridge,
1086352cd45SJagan Teki struct drm_bridge_state *old_bridge_state)
1096352cd45SJagan Teki {
1106352cd45SJagan Teki struct dlpc *dlpc = bridge_to_dlpc(bridge);
1116352cd45SJagan Teki struct device *dev = dlpc->dev;
1126352cd45SJagan Teki struct drm_display_mode *mode = &dlpc->mode;
1136352cd45SJagan Teki struct regmap *regmap = dlpc->regmap;
1146352cd45SJagan Teki char buf[MAX_BYTE_SIZE];
1156352cd45SJagan Teki unsigned int devid;
1166352cd45SJagan Teki
1176352cd45SJagan Teki regmap_read(regmap, RD_DEVICE_ID, &devid);
1186352cd45SJagan Teki devid &= DEV_ID_MASK;
1196352cd45SJagan Teki
1206352cd45SJagan Teki DRM_DEV_DEBUG(dev, "DLPC3433 device id: 0x%02x\n", devid);
1216352cd45SJagan Teki
1226352cd45SJagan Teki if (devid != 0x01) {
1236352cd45SJagan Teki DRM_DEV_ERROR(dev, "Unsupported DLPC device id: 0x%02x\n", devid);
1246352cd45SJagan Teki return;
1256352cd45SJagan Teki }
1266352cd45SJagan Teki
1276352cd45SJagan Teki /* disable image freeze */
1286352cd45SJagan Teki regmap_write(regmap, WR_IMAGE_FREEZE, IMAGE_FREESE_EN);
1296352cd45SJagan Teki
1306352cd45SJagan Teki /* enable DSI port */
1316352cd45SJagan Teki regmap_write(regmap, WR_DSI_PORT_EN, DSI_PORT_EN);
1326352cd45SJagan Teki
1336352cd45SJagan Teki memset(buf, 0, MAX_BYTE_SIZE);
1346352cd45SJagan Teki
1356352cd45SJagan Teki /* set image crop */
1366352cd45SJagan Teki buf[4] = mode->hdisplay & 0xff;
1376352cd45SJagan Teki buf[5] = (mode->hdisplay & 0xff00) >> 8;
1386352cd45SJagan Teki buf[6] = mode->vdisplay & 0xff;
1396352cd45SJagan Teki buf[7] = (mode->vdisplay & 0xff00) >> 8;
1406352cd45SJagan Teki regmap_noinc_write(regmap, WR_IMAGE_CROP, buf, MAX_BYTE_SIZE);
1416352cd45SJagan Teki
1426352cd45SJagan Teki /* set display size */
1436352cd45SJagan Teki buf[4] = mode->hdisplay & 0xff;
1446352cd45SJagan Teki buf[5] = (mode->hdisplay & 0xff00) >> 8;
1456352cd45SJagan Teki buf[6] = mode->vdisplay & 0xff;
1466352cd45SJagan Teki buf[7] = (mode->vdisplay & 0xff00) >> 8;
1476352cd45SJagan Teki regmap_noinc_write(regmap, WR_DISPLAY_SIZE, buf, MAX_BYTE_SIZE);
1486352cd45SJagan Teki
1496352cd45SJagan Teki /* set input image size */
1506352cd45SJagan Teki buf[0] = mode->hdisplay & 0xff;
1516352cd45SJagan Teki buf[1] = (mode->hdisplay & 0xff00) >> 8;
1526352cd45SJagan Teki buf[2] = mode->vdisplay & 0xff;
1536352cd45SJagan Teki buf[3] = (mode->vdisplay & 0xff00) >> 8;
1546352cd45SJagan Teki regmap_noinc_write(regmap, WR_INPUT_IMAGE_SIZE, buf, 4);
1556352cd45SJagan Teki
1566352cd45SJagan Teki /* set external video port */
1576352cd45SJagan Teki regmap_write(regmap, WR_INPUT_SOURCE, INPUT_EXTERNAL_VIDEO);
1586352cd45SJagan Teki
1596352cd45SJagan Teki /* set external video format select as DSI */
1606352cd45SJagan Teki regmap_write(regmap, WR_EXT_SOURCE_FMT, EXT_SOURCE_FMT_DSI);
1616352cd45SJagan Teki
1626352cd45SJagan Teki /* disable image freeze */
1636352cd45SJagan Teki regmap_write(regmap, WR_IMAGE_FREEZE, 0x00);
1646352cd45SJagan Teki
1656352cd45SJagan Teki /* enable RGB led */
1666352cd45SJagan Teki regmap_update_bits(regmap, WR_RGB_LED_EN, LED_MASK,
1676352cd45SJagan Teki RED_LED_EN | GREEN_LED_EN | BLUE_LED_EN);
1686352cd45SJagan Teki
1696352cd45SJagan Teki msleep(10);
1706352cd45SJagan Teki }
1716352cd45SJagan Teki
dlpc_atomic_pre_enable(struct drm_bridge * bridge,struct drm_bridge_state * old_bridge_state)1726352cd45SJagan Teki static void dlpc_atomic_pre_enable(struct drm_bridge *bridge,
1736352cd45SJagan Teki struct drm_bridge_state *old_bridge_state)
1746352cd45SJagan Teki {
1756352cd45SJagan Teki struct dlpc *dlpc = bridge_to_dlpc(bridge);
1766352cd45SJagan Teki int ret;
1776352cd45SJagan Teki
1786352cd45SJagan Teki gpiod_set_value(dlpc->enable_gpio, 1);
1796352cd45SJagan Teki
1806352cd45SJagan Teki msleep(500);
1816352cd45SJagan Teki
1826352cd45SJagan Teki ret = regulator_enable(dlpc->vcc_intf);
1836352cd45SJagan Teki if (ret)
1846352cd45SJagan Teki DRM_DEV_ERROR(dlpc->dev,
1856352cd45SJagan Teki "failed to enable VCC_INTF regulator: %d\n", ret);
1866352cd45SJagan Teki
1876352cd45SJagan Teki ret = regulator_enable(dlpc->vcc_flsh);
1886352cd45SJagan Teki if (ret)
1896352cd45SJagan Teki DRM_DEV_ERROR(dlpc->dev,
1906352cd45SJagan Teki "failed to enable VCC_FLSH regulator: %d\n", ret);
1916352cd45SJagan Teki
1926352cd45SJagan Teki msleep(10);
1936352cd45SJagan Teki }
1946352cd45SJagan Teki
dlpc_atomic_post_disable(struct drm_bridge * bridge,struct drm_bridge_state * old_bridge_state)1956352cd45SJagan Teki static void dlpc_atomic_post_disable(struct drm_bridge *bridge,
1966352cd45SJagan Teki struct drm_bridge_state *old_bridge_state)
1976352cd45SJagan Teki {
1986352cd45SJagan Teki struct dlpc *dlpc = bridge_to_dlpc(bridge);
1996352cd45SJagan Teki
2006352cd45SJagan Teki regulator_disable(dlpc->vcc_flsh);
2016352cd45SJagan Teki regulator_disable(dlpc->vcc_intf);
2026352cd45SJagan Teki
2036352cd45SJagan Teki msleep(10);
2046352cd45SJagan Teki
2056352cd45SJagan Teki gpiod_set_value(dlpc->enable_gpio, 0);
2066352cd45SJagan Teki
2076352cd45SJagan Teki msleep(500);
2086352cd45SJagan Teki }
2096352cd45SJagan Teki
2106352cd45SJagan Teki #define MAX_INPUT_SEL_FORMATS 1
2116352cd45SJagan Teki
2126352cd45SJagan Teki static u32 *
dlpc_atomic_get_input_bus_fmts(struct drm_bridge * bridge,struct drm_bridge_state * bridge_state,struct drm_crtc_state * crtc_state,struct drm_connector_state * conn_state,u32 output_fmt,unsigned int * num_input_fmts)2136352cd45SJagan Teki dlpc_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
2146352cd45SJagan Teki struct drm_bridge_state *bridge_state,
2156352cd45SJagan Teki struct drm_crtc_state *crtc_state,
2166352cd45SJagan Teki struct drm_connector_state *conn_state,
2176352cd45SJagan Teki u32 output_fmt,
2186352cd45SJagan Teki unsigned int *num_input_fmts)
2196352cd45SJagan Teki {
2206352cd45SJagan Teki u32 *input_fmts;
2216352cd45SJagan Teki
2226352cd45SJagan Teki *num_input_fmts = 0;
2236352cd45SJagan Teki
2246352cd45SJagan Teki input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
2256352cd45SJagan Teki GFP_KERNEL);
2266352cd45SJagan Teki if (!input_fmts)
2276352cd45SJagan Teki return NULL;
2286352cd45SJagan Teki
2296352cd45SJagan Teki /* This is the DSI-end bus format */
2306352cd45SJagan Teki input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;
2316352cd45SJagan Teki *num_input_fmts = 1;
2326352cd45SJagan Teki
2336352cd45SJagan Teki return input_fmts;
2346352cd45SJagan Teki }
2356352cd45SJagan Teki
dlpc_mode_set(struct drm_bridge * bridge,const struct drm_display_mode * mode,const struct drm_display_mode * adjusted_mode)2366352cd45SJagan Teki static void dlpc_mode_set(struct drm_bridge *bridge,
2376352cd45SJagan Teki const struct drm_display_mode *mode,
2386352cd45SJagan Teki const struct drm_display_mode *adjusted_mode)
2396352cd45SJagan Teki {
2406352cd45SJagan Teki struct dlpc *dlpc = bridge_to_dlpc(bridge);
2416352cd45SJagan Teki
2426352cd45SJagan Teki drm_mode_copy(&dlpc->mode, adjusted_mode);
2436352cd45SJagan Teki }
2446352cd45SJagan Teki
dlpc_attach(struct drm_bridge * bridge,enum drm_bridge_attach_flags flags)2456352cd45SJagan Teki static int dlpc_attach(struct drm_bridge *bridge,
2466352cd45SJagan Teki enum drm_bridge_attach_flags flags)
2476352cd45SJagan Teki {
2486352cd45SJagan Teki struct dlpc *dlpc = bridge_to_dlpc(bridge);
2496352cd45SJagan Teki
2506352cd45SJagan Teki return drm_bridge_attach(bridge->encoder, dlpc->next_bridge, bridge, flags);
2516352cd45SJagan Teki }
2526352cd45SJagan Teki
2536352cd45SJagan Teki static const struct drm_bridge_funcs dlpc_bridge_funcs = {
2546352cd45SJagan Teki .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
2556352cd45SJagan Teki .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
2566352cd45SJagan Teki .atomic_get_input_bus_fmts = dlpc_atomic_get_input_bus_fmts,
2576352cd45SJagan Teki .atomic_reset = drm_atomic_helper_bridge_reset,
2586352cd45SJagan Teki .atomic_pre_enable = dlpc_atomic_pre_enable,
2596352cd45SJagan Teki .atomic_enable = dlpc_atomic_enable,
2606352cd45SJagan Teki .atomic_post_disable = dlpc_atomic_post_disable,
2616352cd45SJagan Teki .mode_set = dlpc_mode_set,
2626352cd45SJagan Teki .attach = dlpc_attach,
2636352cd45SJagan Teki };
2646352cd45SJagan Teki
dlpc3433_parse_dt(struct dlpc * dlpc)2656352cd45SJagan Teki static int dlpc3433_parse_dt(struct dlpc *dlpc)
2666352cd45SJagan Teki {
2676352cd45SJagan Teki struct device *dev = dlpc->dev;
2686352cd45SJagan Teki struct device_node *endpoint;
2696352cd45SJagan Teki int ret;
2706352cd45SJagan Teki
2716352cd45SJagan Teki dlpc->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
2726352cd45SJagan Teki if (IS_ERR(dlpc->enable_gpio))
2736352cd45SJagan Teki return PTR_ERR(dlpc->enable_gpio);
2746352cd45SJagan Teki
2756352cd45SJagan Teki dlpc->vcc_intf = devm_regulator_get(dlpc->dev, "vcc_intf");
2766352cd45SJagan Teki if (IS_ERR(dlpc->vcc_intf))
2776352cd45SJagan Teki return dev_err_probe(dev, PTR_ERR(dlpc->vcc_intf),
2786352cd45SJagan Teki "failed to get VCC_INTF supply\n");
2796352cd45SJagan Teki
2806352cd45SJagan Teki dlpc->vcc_flsh = devm_regulator_get(dlpc->dev, "vcc_flsh");
2816352cd45SJagan Teki if (IS_ERR(dlpc->vcc_flsh))
2826352cd45SJagan Teki return dev_err_probe(dev, PTR_ERR(dlpc->vcc_flsh),
2836352cd45SJagan Teki "failed to get VCC_FLSH supply\n");
2846352cd45SJagan Teki
2856352cd45SJagan Teki dlpc->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
2866352cd45SJagan Teki if (IS_ERR(dlpc->next_bridge))
2876352cd45SJagan Teki return PTR_ERR(dlpc->next_bridge);
2886352cd45SJagan Teki
2896352cd45SJagan Teki endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0);
2906352cd45SJagan Teki dlpc->dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes");
2916352cd45SJagan Teki if (dlpc->dsi_lanes < 0 || dlpc->dsi_lanes > 4) {
2926352cd45SJagan Teki ret = -EINVAL;
2936352cd45SJagan Teki goto err_put_endpoint;
2946352cd45SJagan Teki }
2956352cd45SJagan Teki
2966352cd45SJagan Teki dlpc->host_node = of_graph_get_remote_port_parent(endpoint);
2976352cd45SJagan Teki if (!dlpc->host_node) {
2986352cd45SJagan Teki ret = -ENODEV;
2996352cd45SJagan Teki goto err_put_host;
3006352cd45SJagan Teki }
3016352cd45SJagan Teki
3026352cd45SJagan Teki of_node_put(endpoint);
3036352cd45SJagan Teki
3046352cd45SJagan Teki return 0;
3056352cd45SJagan Teki
3066352cd45SJagan Teki err_put_host:
3076352cd45SJagan Teki of_node_put(dlpc->host_node);
3086352cd45SJagan Teki err_put_endpoint:
3096352cd45SJagan Teki of_node_put(endpoint);
3106352cd45SJagan Teki return ret;
3116352cd45SJagan Teki }
3126352cd45SJagan Teki
dlpc_host_attach(struct dlpc * dlpc)3136352cd45SJagan Teki static int dlpc_host_attach(struct dlpc *dlpc)
3146352cd45SJagan Teki {
3156352cd45SJagan Teki struct device *dev = dlpc->dev;
3166352cd45SJagan Teki struct mipi_dsi_host *host;
3176352cd45SJagan Teki struct mipi_dsi_device_info info = {
3186352cd45SJagan Teki .type = "dlpc3433",
3196352cd45SJagan Teki .channel = 0,
3206352cd45SJagan Teki .node = NULL,
3216352cd45SJagan Teki };
322*7962ae5aSNícolas F. R. A. Prado int ret;
3236352cd45SJagan Teki
3246352cd45SJagan Teki host = of_find_mipi_dsi_host_by_node(dlpc->host_node);
325*7962ae5aSNícolas F. R. A. Prado if (!host)
326*7962ae5aSNícolas F. R. A. Prado return dev_err_probe(dev, -EPROBE_DEFER, "failed to find dsi host\n");
3276352cd45SJagan Teki
3286352cd45SJagan Teki dlpc->dsi = mipi_dsi_device_register_full(host, &info);
3296352cd45SJagan Teki if (IS_ERR(dlpc->dsi)) {
3306352cd45SJagan Teki DRM_DEV_ERROR(dev, "failed to create dsi device\n");
3316352cd45SJagan Teki return PTR_ERR(dlpc->dsi);
3326352cd45SJagan Teki }
3336352cd45SJagan Teki
3346352cd45SJagan Teki dlpc->dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST;
3356352cd45SJagan Teki dlpc->dsi->format = MIPI_DSI_FMT_RGB565;
3366352cd45SJagan Teki dlpc->dsi->lanes = dlpc->dsi_lanes;
3376352cd45SJagan Teki
338*7962ae5aSNícolas F. R. A. Prado ret = devm_mipi_dsi_attach(dev, dlpc->dsi);
339*7962ae5aSNícolas F. R. A. Prado if (ret)
340*7962ae5aSNícolas F. R. A. Prado DRM_DEV_ERROR(dev, "failed to attach dsi host\n");
341*7962ae5aSNícolas F. R. A. Prado
342*7962ae5aSNícolas F. R. A. Prado return ret;
3436352cd45SJagan Teki }
3446352cd45SJagan Teki
dlpc3433_probe(struct i2c_client * client)3456352cd45SJagan Teki static int dlpc3433_probe(struct i2c_client *client)
3466352cd45SJagan Teki {
3476352cd45SJagan Teki struct device *dev = &client->dev;
3486352cd45SJagan Teki struct dlpc *dlpc;
3496352cd45SJagan Teki int ret;
3506352cd45SJagan Teki
3516352cd45SJagan Teki dlpc = devm_kzalloc(dev, sizeof(*dlpc), GFP_KERNEL);
3526352cd45SJagan Teki if (!dlpc)
3536352cd45SJagan Teki return -ENOMEM;
3546352cd45SJagan Teki
3556352cd45SJagan Teki dlpc->dev = dev;
3566352cd45SJagan Teki
3576352cd45SJagan Teki dlpc->regmap = devm_regmap_init_i2c(client, &dlpc_regmap_config);
3586352cd45SJagan Teki if (IS_ERR(dlpc->regmap))
3596352cd45SJagan Teki return PTR_ERR(dlpc->regmap);
3606352cd45SJagan Teki
3616352cd45SJagan Teki ret = dlpc3433_parse_dt(dlpc);
3626352cd45SJagan Teki if (ret)
3636352cd45SJagan Teki return ret;
3646352cd45SJagan Teki
3656352cd45SJagan Teki dev_set_drvdata(dev, dlpc);
3666352cd45SJagan Teki i2c_set_clientdata(client, dlpc);
3676352cd45SJagan Teki
3686352cd45SJagan Teki dlpc->bridge.funcs = &dlpc_bridge_funcs;
3696352cd45SJagan Teki dlpc->bridge.of_node = dev->of_node;
3706352cd45SJagan Teki drm_bridge_add(&dlpc->bridge);
3716352cd45SJagan Teki
3726352cd45SJagan Teki ret = dlpc_host_attach(dlpc);
373*7962ae5aSNícolas F. R. A. Prado if (ret)
3746352cd45SJagan Teki goto err_remove_bridge;
3756352cd45SJagan Teki
3766352cd45SJagan Teki return 0;
3776352cd45SJagan Teki
3786352cd45SJagan Teki err_remove_bridge:
3796352cd45SJagan Teki drm_bridge_remove(&dlpc->bridge);
3806352cd45SJagan Teki return ret;
3816352cd45SJagan Teki }
3826352cd45SJagan Teki
dlpc3433_remove(struct i2c_client * client)383ed5c2f5fSUwe Kleine-König static void dlpc3433_remove(struct i2c_client *client)
3846352cd45SJagan Teki {
3856352cd45SJagan Teki struct dlpc *dlpc = i2c_get_clientdata(client);
3866352cd45SJagan Teki
3876352cd45SJagan Teki drm_bridge_remove(&dlpc->bridge);
3886352cd45SJagan Teki of_node_put(dlpc->host_node);
3896352cd45SJagan Teki }
3906352cd45SJagan Teki
3916352cd45SJagan Teki static const struct i2c_device_id dlpc3433_id[] = {
3926352cd45SJagan Teki { "ti,dlpc3433", 0 },
3936352cd45SJagan Teki { /* sentinel */ }
3946352cd45SJagan Teki };
3956352cd45SJagan Teki MODULE_DEVICE_TABLE(i2c, dlpc3433_id);
3966352cd45SJagan Teki
3976352cd45SJagan Teki static const struct of_device_id dlpc3433_match_table[] = {
3986352cd45SJagan Teki { .compatible = "ti,dlpc3433" },
3996352cd45SJagan Teki { /* sentinel */ }
4006352cd45SJagan Teki };
4016352cd45SJagan Teki MODULE_DEVICE_TABLE(of, dlpc3433_match_table);
4026352cd45SJagan Teki
4036352cd45SJagan Teki static struct i2c_driver dlpc3433_driver = {
404332af828SUwe Kleine-König .probe = dlpc3433_probe,
4056352cd45SJagan Teki .remove = dlpc3433_remove,
4066352cd45SJagan Teki .id_table = dlpc3433_id,
4076352cd45SJagan Teki .driver = {
4086352cd45SJagan Teki .name = "ti-dlpc3433",
4096352cd45SJagan Teki .of_match_table = dlpc3433_match_table,
4106352cd45SJagan Teki },
4116352cd45SJagan Teki };
4126352cd45SJagan Teki module_i2c_driver(dlpc3433_driver);
4136352cd45SJagan Teki
4146352cd45SJagan Teki MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>");
4156352cd45SJagan Teki MODULE_AUTHOR("Christopher Vollo <chris@renewoutreach.org>");
4166352cd45SJagan Teki MODULE_DESCRIPTION("TI DLPC3433 MIPI DSI Display Controller Bridge");
4176352cd45SJagan Teki MODULE_LICENSE("GPL");
418