diff options
Diffstat (limited to 'drivers/gpu/drm/bridge')
-rw-r--r-- | drivers/gpu/drm/bridge/Kconfig | 17 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/adv7511/adv7511.h | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/adv7511/adv7511_cec.c | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/analogix/anx7625.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/cdns-dsi.c | 26 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/chipone-icn6211.c | 42 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/lontium-lt9611.c | 24 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/lontium-lt9611uxc.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/nwl-dsi.c | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/parade-ps8640.c | 73 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/tc358767.c | 169 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/ti-dlpc3433.c | 417 |
13 files changed, 668 insertions, 134 deletions
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 307b135da2f6..8ffd601e68f9 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -78,6 +78,7 @@ config DRM_DISPLAY_CONNECTOR config DRM_FSL_LDB tristate "Freescale i.MX8MP LDB bridge" depends on OF + depends on ARCH_MXC || COMPILE_TEST select DRM_KMS_HELPER select DRM_PANEL_BRIDGE help @@ -321,6 +322,22 @@ config DRM_TOSHIBA_TC358775 help Toshiba TC358775 DSI/LVDS bridge chip driver. +config DRM_TI_DLPC3433 + tristate "TI DLPC3433 Display controller" + depends on DRM && DRM_PANEL + depends on OF + select DRM_MIPI_DSI + help + TI DLPC3433 is a MIPI DSI based display controller bridge + for processing high resolution DMD based projectors. + + It has a flexible configuration of MIPI DSI and DPI signal + input that produces a DMD output in RGB565, RGB666, RGB888 + formats. + + It supports upto 720p resolution with 60 and 120 Hz refresh + rates. + config DRM_TI_TFP410 tristate "TI TFP410 DVI/HDMI bridge" depends on OF diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index f6c0a95de549..043b499545e3 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o obj-$(CONFIG_DRM_TOSHIBA_TC358768) += tc358768.o obj-$(CONFIG_DRM_TOSHIBA_TC358775) += tc358775.o obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/ +obj-$(CONFIG_DRM_TI_DLPC3433) += ti-dlpc3433.o obj-$(CONFIG_DRM_TI_SN65DSI83) += ti-sn65dsi83.o obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h index 9e3bb8a8ee40..a031a0cd1f18 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511.h +++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h @@ -226,18 +226,6 @@ #define ADV7511_REG_CEC_CLK_DIV 0x4e #define ADV7511_REG_CEC_SOFT_RESET 0x50 -static const u8 ADV7511_REG_CEC_RX_FRAME_HDR[] = { - ADV7511_REG_CEC_RX1_FRAME_HDR, - ADV7511_REG_CEC_RX2_FRAME_HDR, - ADV7511_REG_CEC_RX3_FRAME_HDR, -}; - -static const u8 ADV7511_REG_CEC_RX_FRAME_LEN[] = { - ADV7511_REG_CEC_RX1_FRAME_LEN, - ADV7511_REG_CEC_RX2_FRAME_LEN, - ADV7511_REG_CEC_RX3_FRAME_LEN, -}; - #define ADV7533_REG_CEC_OFFSET 0x70 enum adv7511_input_clock { diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c b/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c index 399f625a50c8..0b266f28f150 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c @@ -15,6 +15,18 @@ #include "adv7511.h" +static const u8 ADV7511_REG_CEC_RX_FRAME_HDR[] = { + ADV7511_REG_CEC_RX1_FRAME_HDR, + ADV7511_REG_CEC_RX2_FRAME_HDR, + ADV7511_REG_CEC_RX3_FRAME_HDR, +}; + +static const u8 ADV7511_REG_CEC_RX_FRAME_LEN[] = { + ADV7511_REG_CEC_RX1_FRAME_LEN, + ADV7511_REG_CEC_RX2_FRAME_LEN, + ADV7511_REG_CEC_RX3_FRAME_LEN, +}; + #define ADV7511_INT1_CEC_MASK \ (ADV7511_INT1_CEC_TX_READY | ADV7511_INT1_CEC_TX_ARBIT_LOST | \ ADV7511_INT1_CEC_TX_RETRY_TIMEOUT | ADV7511_INT1_CEC_RX_READY1 | \ diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index 53a5da6c49dd..e92eb4a40745 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -1638,6 +1638,7 @@ static int anx7625_parse_dt(struct device *dev, bus_type = 0; mipi_lanes = of_property_count_u32_elems(ep0, "data-lanes"); + of_node_put(ep0); } if (bus_type == V4L2_FWNODE_BUS_TYPE_PARALLEL) /* bus type is Parallel(DSI) */ diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c index 829e1a144656..20bece84ff8c 100644 --- a/drivers/gpu/drm/bridge/cdns-dsi.c +++ b/drivers/gpu/drm/bridge/cdns-dsi.c @@ -462,6 +462,7 @@ struct cdns_dsi { struct reset_control *dsi_p_rst; struct clk *dsi_sys_clk; bool link_initialized; + bool phy_initialized; struct phy *dphy; }; @@ -711,11 +712,21 @@ static void cdns_dsi_bridge_disable(struct drm_bridge *bridge) pm_runtime_put(dsi->base.dev); } +static void cdns_dsi_bridge_post_disable(struct drm_bridge *bridge) +{ + struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); + struct cdns_dsi *dsi = input_to_dsi(input); + + pm_runtime_put(dsi->base.dev); +} + static void cdns_dsi_hs_init(struct cdns_dsi *dsi) { struct cdns_dsi_output *output = &dsi->output; u32 status; + if (dsi->phy_initialized) + return; /* * Power all internal DPHY blocks down and maintain their reset line * asserted before changing the DPHY config. @@ -739,6 +750,7 @@ static void cdns_dsi_hs_init(struct cdns_dsi *dsi) writel(DPHY_CMN_PSO | DPHY_ALL_D_PDN | DPHY_C_PDN | DPHY_CMN_PDN | DPHY_D_RSTB(output->dev->lanes) | DPHY_C_RSTB, dsi->regs + MCTL_DPHY_CFG0); + dsi->phy_initialized = true; } static void cdns_dsi_init_link(struct cdns_dsi *dsi) @@ -914,11 +926,25 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge) writel(tmp, dsi->regs + MCTL_MAIN_EN); } +static void cdns_dsi_bridge_pre_enable(struct drm_bridge *bridge) +{ + struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); + struct cdns_dsi *dsi = input_to_dsi(input); + + if (WARN_ON(pm_runtime_get_sync(dsi->base.dev) < 0)) + return; + + cdns_dsi_init_link(dsi); + cdns_dsi_hs_init(dsi); +} + static const struct drm_bridge_funcs cdns_dsi_bridge_funcs = { .attach = cdns_dsi_bridge_attach, .mode_valid = cdns_dsi_bridge_mode_valid, .disable = cdns_dsi_bridge_disable, + .pre_enable = cdns_dsi_bridge_pre_enable, .enable = cdns_dsi_bridge_enable, + .post_disable = cdns_dsi_bridge_post_disable, }; static int cdns_dsi_attach(struct mipi_dsi_host *host, diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c index 47dea657a752..2b8cf64cbd80 100644 --- a/drivers/gpu/drm/bridge/chipone-icn6211.c +++ b/drivers/gpu/drm/bridge/chipone-icn6211.c @@ -9,6 +9,8 @@ #include <drm/drm_print.h> #include <drm/drm_mipi_dsi.h> +#include <linux/bitfield.h> +#include <linux/bits.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> @@ -26,6 +28,11 @@ #define PD_CTRL(n) (0x0a + ((n) & 0x3)) /* 0..3 */ #define RST_CTRL(n) (0x0e + ((n) & 0x1)) /* 0..1 */ #define SYS_CTRL(n) (0x10 + ((n) & 0x7)) /* 0..4 */ +#define SYS_CTRL_1_CLK_PHASE_MSK GENMASK(5, 4) +#define CLK_PHASE_0 0 +#define CLK_PHASE_1_4 1 +#define CLK_PHASE_1_2 2 +#define CLK_PHASE_3_4 3 #define RGB_DRV(n) (0x18 + ((n) & 0x3)) /* 0..3 */ #define RGB_DLY(n) (0x1c + ((n) & 0x1)) /* 0..1 */ #define RGB_TEST_CTRL 0x1e @@ -100,7 +107,7 @@ #define MIPI_PN_SWAP 0x87 #define MIPI_PN_SWAP_CLK BIT(4) #define MIPI_PN_SWAP_D(n) BIT((n) & 0x3) -#define MIPI_SOT_SYNC_BIT_(n) (0x88 + ((n) & 0x1)) /* 0..1 */ +#define MIPI_SOT_SYNC_BIT(n) (0x88 + ((n) & 0x1)) /* 0..1 */ #define MIPI_ULPS_CTRL 0x8a #define MIPI_CLK_CHK_VAR 0x8e #define MIPI_CLK_CHK_INI 0x8f @@ -115,7 +122,7 @@ #define MIPI_T_CLK_SETTLE 0x9a #define MIPI_TO_HS_RX_L 0x9e #define MIPI_TO_HS_RX_H 0x9f -#define MIPI_PHY_(n) (0xa0 + ((n) & 0x7)) /* 0..5 */ +#define MIPI_PHY(n) (0xa0 + ((n) & 0x7)) /* 0..5 */ #define MIPI_PD_RX 0xb0 #define MIPI_PD_TERM 0xb1 #define MIPI_PD_HSRX 0xb2 @@ -125,13 +132,11 @@ #define MIPI_FORCE_0 0xb6 #define MIPI_RST_CTRL 0xb7 #define MIPI_RST_NUM 0xb8 -#define MIPI_DBG_SET_(n) (0xc0 + ((n) & 0xf)) /* 0..9 */ +#define MIPI_DBG_SET(n) (0xc0 + ((n) & 0xf)) /* 0..9 */ #define MIPI_DBG_SEL 0xe0 #define MIPI_DBG_DATA 0xe1 #define MIPI_ATE_TEST_SEL 0xe2 -#define MIPI_ATE_STATUS_(n) (0xe3 + ((n) & 0x1)) /* 0..1 */ -#define MIPI_ATE_STATUS_1 0xe4 -#define ICN6211_MAX_REGISTER MIPI_ATE_STATUS(1) +#define MIPI_ATE_STATUS(n) (0xe3 + ((n) & 0x1)) /* 0..1 */ struct chipone { struct device *dev; @@ -155,10 +160,10 @@ static const struct regmap_range chipone_dsi_readable_ranges[] = { regmap_reg_range(MIPI_CLK_CHK_VAR, MIPI_T_TA_SURE_PRE), regmap_reg_range(MIPI_T_LPX_SET, MIPI_INIT_TIME_H), regmap_reg_range(MIPI_T_CLK_TERM_EN, MIPI_T_CLK_SETTLE), - regmap_reg_range(MIPI_TO_HS_RX_L, MIPI_PHY_(5)), + regmap_reg_range(MIPI_TO_HS_RX_L, MIPI_PHY(5)), regmap_reg_range(MIPI_PD_RX, MIPI_RST_NUM), - regmap_reg_range(MIPI_DBG_SET_(0), MIPI_DBG_SET_(9)), - regmap_reg_range(MIPI_DBG_SEL, MIPI_ATE_STATUS_(1)), + regmap_reg_range(MIPI_DBG_SET(0), MIPI_DBG_SET(9)), + regmap_reg_range(MIPI_DBG_SEL, MIPI_ATE_STATUS(1)), }; static const struct regmap_access_table chipone_dsi_readable_table = { @@ -172,10 +177,10 @@ static const struct regmap_range chipone_dsi_writeable_ranges[] = { regmap_reg_range(MIPI_CLK_CHK_VAR, MIPI_T_TA_SURE_PRE), regmap_reg_range(MIPI_T_LPX_SET, MIPI_INIT_TIME_H), regmap_reg_range(MIPI_T_CLK_TERM_EN, MIPI_T_CLK_SETTLE), - regmap_reg_range(MIPI_TO_HS_RX_L, MIPI_PHY_(5)), + regmap_reg_range(MIPI_TO_HS_RX_L, MIPI_PHY(5)), regmap_reg_range(MIPI_PD_RX, MIPI_RST_NUM), - regmap_reg_range(MIPI_DBG_SET_(0), MIPI_DBG_SET_(9)), - regmap_reg_range(MIPI_DBG_SEL, MIPI_ATE_STATUS_(1)), + regmap_reg_range(MIPI_DBG_SET(0), MIPI_DBG_SET(9)), + regmap_reg_range(MIPI_DBG_SEL, MIPI_ATE_STATUS(1)), }; static const struct regmap_access_table chipone_dsi_writeable_table = { @@ -189,7 +194,7 @@ static const struct regmap_config chipone_regmap_config = { .rd_table = &chipone_dsi_readable_table, .wr_table = &chipone_dsi_writeable_table, .cache_type = REGCACHE_RBTREE, - .max_register = MIPI_ATE_STATUS_(1), + .max_register = MIPI_ATE_STATUS(1), }; static int chipone_dsi_read(void *context, @@ -336,7 +341,7 @@ static void chipone_atomic_enable(struct drm_bridge *bridge, const struct drm_bridge_state *bridge_state; u16 hfp, hbp, hsync; u32 bus_flags; - u8 pol, id[4]; + u8 pol, sys_ctrl_1, id[4]; chipone_readb(icn, VENDOR_ID, id); chipone_readb(icn, DEVICE_ID_H, id + 1); @@ -414,7 +419,14 @@ static void chipone_atomic_enable(struct drm_bridge *bridge, chipone_configure_pll(icn, mode); chipone_writeb(icn, SYS_CTRL(0), 0x40); - chipone_writeb(icn, SYS_CTRL(1), 0x88); + sys_ctrl_1 = 0x88; + + if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE) + sys_ctrl_1 |= FIELD_PREP(SYS_CTRL_1_CLK_PHASE_MSK, CLK_PHASE_0); + else + sys_ctrl_1 |= FIELD_PREP(SYS_CTRL_1_CLK_PHASE_MSK, CLK_PHASE_1_2); + + chipone_writeb(icn, SYS_CTRL(1), sys_ctrl_1); /* icn6211 specific sequence */ chipone_writeb(icn, MIPI_FORCE_0, 0x20); diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c index 7ef8fe5abc12..88f2a4f43cfb 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611.c @@ -578,15 +578,13 @@ static struct lt9611_mode *lt9611_find_mode(const struct drm_display_mode *mode) } /* connector funcs */ -static enum drm_connector_status -lt9611_connector_detect(struct drm_connector *connector, bool force) +static enum drm_connector_status __lt9611_detect(struct lt9611 *lt9611) { - struct lt9611 *lt9611 = connector_to_lt9611(connector); unsigned int reg_val = 0; int connected = 0; regmap_read(lt9611->regmap, 0x825e, ®_val); - connected = (reg_val & BIT(0)); + connected = (reg_val & (BIT(2) | BIT(0))); lt9611->status = connected ? connector_status_connected : connector_status_disconnected; @@ -594,6 +592,12 @@ lt9611_connector_detect(struct drm_connector *connector, bool force) return lt9611->status; } +static enum drm_connector_status +lt9611_connector_detect(struct drm_connector *connector, bool force) +{ + return __lt9611_detect(connector_to_lt9611(connector)); +} + static int lt9611_read_edid(struct lt9611 *lt9611) { unsigned int temp; @@ -893,17 +897,7 @@ static void lt9611_bridge_mode_set(struct drm_bridge *bridge, static enum drm_connector_status lt9611_bridge_detect(struct drm_bridge *bridge) { - struct lt9611 *lt9611 = bridge_to_lt9611(bridge); - unsigned int reg_val = 0; - int connected; - - regmap_read(lt9611->regmap, 0x825e, ®_val); - connected = reg_val & BIT(0); - - lt9611->status = connected ? connector_status_connected : - connector_status_disconnected; - - return lt9611->status; + return __lt9611_detect(bridge_to_lt9611(bridge)); } static struct edid *lt9611_bridge_get_edid(struct drm_bridge *bridge, diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c index 3d62e6bf6892..310b3b194491 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c @@ -982,7 +982,7 @@ static int lt9611uxc_remove(struct i2c_client *client) struct lt9611uxc *lt9611uxc = i2c_get_clientdata(client); disable_irq(client->irq); - flush_scheduled_work(); + cancel_work_sync(<9611uxc->work); lt9611uxc_audio_exit(lt9611uxc); drm_bridge_remove(<9611uxc->bridge); diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c index ad74e6558eb3..d83111be4829 100644 --- a/drivers/gpu/drm/bridge/nwl-dsi.c +++ b/drivers/gpu/drm/bridge/nwl-dsi.c @@ -665,6 +665,12 @@ static int nwl_dsi_mode_set(struct nwl_dsi *dsi) return ret; } + ret = phy_set_mode(dsi->phy, PHY_MODE_MIPI_DPHY); + if (ret < 0) { + DRM_DEV_ERROR(dev, "Failed to set DSI phy mode: %d\n", ret); + goto uninit_phy; + } + ret = phy_configure(dsi->phy, phy_cfg); if (ret < 0) { DRM_DEV_ERROR(dev, "Failed to configure DSI phy: %d\n", ret); diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c index edb939b14c04..ff4227f6d800 100644 --- a/drivers/gpu/drm/bridge/parade-ps8640.c +++ b/drivers/gpu/drm/bridge/parade-ps8640.c @@ -537,12 +537,11 @@ static const struct drm_bridge_funcs ps8640_bridge_funcs = { .pre_enable = ps8640_pre_enable, }; -static int ps8640_bridge_host_attach(struct device *dev, struct ps8640 *ps_bridge) +static int ps8640_bridge_get_dsi_resources(struct device *dev, struct ps8640 *ps_bridge) { struct device_node *in_ep, *dsi_node; struct mipi_dsi_device *dsi; struct mipi_dsi_host *host; - int ret; const struct mipi_dsi_device_info info = { .type = "ps8640", .channel = 0, .node = NULL, @@ -577,17 +576,40 @@ static int ps8640_bridge_host_attach(struct device *dev, struct ps8640 *ps_bridg dsi->format = MIPI_DSI_FMT_RGB888; dsi->lanes = NUM_MIPI_LANES; - ret = devm_mipi_dsi_attach(dev, dsi); + return 0; +} + +static int ps8640_bridge_link_panel(struct drm_dp_aux *aux) +{ + struct ps8640 *ps_bridge = aux_to_ps8640(aux); + struct device *dev = aux->dev; + struct device_node *np = dev->of_node; + int ret; + + /* + * NOTE about returning -EPROBE_DEFER from this function: if we + * return an error (most relevant to -EPROBE_DEFER) it will only + * be passed out to ps8640_probe() if it called this directly (AKA the + * panel isn't under the "aux-bus" node). That should be fine because + * if the panel is under "aux-bus" it's guaranteed to have probed by + * the time this function has been called. + */ + + /* port@1 is ps8640 output port */ + ps_bridge->panel_bridge = devm_drm_of_get_bridge(dev, np, 1, 0); + if (IS_ERR(ps_bridge->panel_bridge)) + return PTR_ERR(ps_bridge->panel_bridge); + + ret = devm_drm_bridge_add(dev, &ps_bridge->bridge); if (ret) return ret; - return 0; + return devm_mipi_dsi_attach(dev, ps_bridge->dsi); } static int ps8640_probe(struct i2c_client *client) { struct device *dev = &client->dev; - struct device_node *np = dev->of_node; struct ps8640 *ps_bridge; int ret; u32 i; @@ -628,6 +650,14 @@ static int ps8640_probe(struct i2c_client *client) if (!ps8640_of_panel_on_aux_bus(&client->dev)) ps_bridge->bridge.ops = DRM_BRIDGE_OP_EDID; + /* + * Get MIPI DSI resources early. These can return -EPROBE_DEFER so + * we want to get them out of the way sooner. + */ + ret = ps8640_bridge_get_dsi_resources(&client->dev, ps_bridge); + if (ret) + return ret; + ps_bridge->page[PAGE0_DP_CNTL] = client; ps_bridge->regmap[PAGE0_DP_CNTL] = devm_regmap_init_i2c(client, ps8640_regmap_config); @@ -670,35 +700,19 @@ static int ps8640_probe(struct i2c_client *client) if (ret) return ret; - devm_of_dp_aux_populate_ep_devices(&ps_bridge->aux); - - /* port@1 is ps8640 output port */ - ps_bridge->panel_bridge = devm_drm_of_get_bridge(dev, np, 1, 0); - if (IS_ERR(ps_bridge->panel_bridge)) - return PTR_ERR(ps_bridge->panel_bridge); - - drm_bridge_add(&ps_bridge->bridge); - - ret = ps8640_bridge_host_attach(dev, ps_bridge); - if (ret) - goto err_bridge_remove; + ret = devm_of_dp_aux_populate_bus(&ps_bridge->aux, ps8640_bridge_link_panel); - return 0; + /* + * If devm_of_dp_aux_populate_bus() returns -ENODEV then it's up to + * usa to call ps8640_bridge_link_panel() directly. NOTE: in this case + * the function is allowed to -EPROBE_DEFER. + */ + if (ret == -ENODEV) + return ps8640_bridge_link_panel(&ps_bridge->aux); -err_bridge_remove: - drm_bridge_remove(&ps_bridge->bridge); return ret; } -static int ps8640_remove(struct i2c_client *client) -{ - struct ps8640 *ps_bridge = i2c_get_clientdata(client); - - drm_bridge_remove(&ps_bridge->bridge); - - return 0; -} - static const struct of_device_id ps8640_match[] = { { .compatible = "parade,ps8640" }, { } @@ -707,7 +721,6 @@ MODULE_DEVICE_TABLE(of, ps8640_match); static struct i2c_driver ps8640_driver = { .probe_new = ps8640_probe, - .remove = ps8640_remove, .driver = { .name = "ps8640", .of_match_table = ps8640_match, diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 485717c8f0b4..6c6177028742 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -3,10 +3,7 @@ * TC358767/TC358867/TC9595 DSI/DPI-to-DPI/(e)DP bridge driver * * The TC358767/TC358867/TC9595 can operate in multiple modes. - * The following modes are supported: - * DPI->(e)DP -- supported - * DSI->DPI .... supported - * DSI->(e)DP .. NOT supported + * All modes are supported -- DPI->(e)DP / DSI->DPI / DSI->(e)DP . * * Copyright (C) 2016 CogentEmbedded Inc * Author: Andrey Gusakov <andrey.gusakov@cogentembedded.com> @@ -309,6 +306,9 @@ struct tc_data { /* do we have IRQ */ bool have_irq; + /* Input connector type, DSI and not DPI. */ + bool input_connector_dsi; + /* HPD pin number (0 or 1) or -ENODEV */ int hpd_pin; }; @@ -1247,11 +1247,60 @@ static int tc_main_link_disable(struct tc_data *tc) return regmap_write(tc->regmap, DP0CTL, 0); } -static int tc_dpi_stream_enable(struct tc_data *tc) +static int tc_dsi_rx_enable(struct tc_data *tc) { + u32 value; int ret; + + regmap_write(tc->regmap, PPI_D0S_CLRSIPOCOUNT, 3); + regmap_write(tc->regmap, PPI_D1S_CLRSIPOCOUNT, 3); + regmap_write(tc->regmap, PPI_D2S_CLRSIPOCOUNT, 3); + regmap_write(tc->regmap, PPI_D3S_CLRSIPOCOUNT, 3); + regmap_write(tc->regmap, PPI_D0S_ATMR, 0); + regmap_write(tc->regmap, PPI_D1S_ATMR, 0); + regmap_write(tc->regmap, PPI_TX_RX_TA, TTA_GET | TTA_SURE); + regmap_write(tc->regmap, PPI_LPTXTIMECNT, LPX_PERIOD); + + value = ((LANEENABLE_L0EN << tc->dsi_lanes) - LANEENABLE_L0EN) | + LANEENABLE_CLEN; + regmap_write(tc->regmap, PPI_LANEENABLE, value); + regmap_write(tc->regmap, DSI_LANEENABLE, value); + + /* Set input interface */ + value = DP0_AUDSRC_NO_INPUT; + if (tc_test_pattern) + value |= DP0_VIDSRC_COLOR_BAR; + else + value |= DP0_VIDSRC_DSI_RX; + ret = regmap_write(tc->regmap, SYSCTRL, value); + if (ret) + return ret; + + usleep_range(120, 150); + + regmap_write(tc->regmap, PPI_STARTPPI, PPI_START_FUNCTION); + regmap_write(tc->regmap, DSI_STARTDSI, DSI_RX_START); + + return 0; +} + +static int tc_dpi_rx_enable(struct tc_data *tc) +{ u32 value; + /* Set input interface */ + value = DP0_AUDSRC_NO_INPUT; + if (tc_test_pattern) + value |= DP0_VIDSRC_COLOR_BAR; + else + value |= DP0_VIDSRC_DPI_RX; + return regmap_write(tc->regmap, SYSCTRL, value); +} + +static int tc_dpi_stream_enable(struct tc_data *tc) +{ + int ret; + dev_dbg(tc->dev, "enable video stream\n"); /* Setup PLL */ @@ -1277,20 +1326,6 @@ static int tc_dpi_stream_enable(struct tc_data *tc) if (ret) return ret; - regmap_write(tc->regmap, PPI_D0S_CLRSIPOCOUNT, 3); - regmap_write(tc->regmap, PPI_D1S_CLRSIPOCOUNT, 3); - regmap_write(tc->regmap, PPI_D2S_CLRSIPOCOUNT, 3); - regmap_write(tc->regmap, PPI_D3S_CLRSIPOCOUNT, 3); - regmap_write(tc->regmap, PPI_D0S_ATMR, 0); - regmap_write(tc->regmap, PPI_D1S_ATMR, 0); - regmap_write(tc->regmap, PPI_TX_RX_TA, TTA_GET | TTA_SURE); - regmap_write(tc->regmap, PPI_LPTXTIMECNT, LPX_PERIOD); - - value = ((LANEENABLE_L0EN << tc->dsi_lanes) - LANEENABLE_L0EN) | - LANEENABLE_CLEN; - regmap_write(tc->regmap, PPI_LANEENABLE, value); - regmap_write(tc->regmap, DSI_LANEENABLE, value); - ret = tc_set_common_video_mode(tc, &tc->mode); if (ret) return ret; @@ -1299,22 +1334,7 @@ static int tc_dpi_stream_enable(struct tc_data *tc) if (ret) return ret; - /* Set input interface */ - value = DP0_AUDSRC_NO_INPUT; - if (tc_test_pattern) - value |= DP0_VIDSRC_COLOR_BAR; - else - value |= DP0_VIDSRC_DSI_RX; - ret = regmap_write(tc->regmap, SYSCTRL, value); - if (ret) - return ret; - - usleep_range(120, 150); - - regmap_write(tc->regmap, PPI_STARTPPI, PPI_START_FUNCTION); - regmap_write(tc->regmap, DSI_STARTDSI, DSI_RX_START); - - return 0; + return tc_dsi_rx_enable(tc); } static int tc_dpi_stream_disable(struct tc_data *tc) @@ -1333,8 +1353,18 @@ static int tc_edp_stream_enable(struct tc_data *tc) dev_dbg(tc->dev, "enable video stream\n"); - /* PXL PLL setup */ - if (tc_test_pattern) { + /* + * Pixel PLL must be enabled for DSI input mode and test pattern. + * + * Per TC9595XBG datasheet Revision 0.1 2018-12-27 Figure 4.18 + * "Clock Mode Selection and Clock Sources", either Pixel PLL + * or DPI_PCLK supplies StrmClk. DPI_PCLK is only available in + * case valid Pixel Clock are supplied to the chip DPI input. + * In case built-in test pattern is desired OR DSI input mode + * is used, DPI_PCLK is not available and thus Pixel PLL must + * be used instead. + */ + if (tc->input_connector_dsi || tc_test_pattern) { ret = tc_pxl_pll_en(tc, clk_get_rate(tc->refclk), 1000 * tc->mode.clock); if (ret) @@ -1372,17 +1402,12 @@ static int tc_edp_stream_enable(struct tc_data *tc) ret = regmap_write(tc->regmap, DP0CTL, value); if (ret) return ret; + /* Set input interface */ - value = DP0_AUDSRC_NO_INPUT; - if (tc_test_pattern) - value |= DP0_VIDSRC_COLOR_BAR; + if (tc->input_connector_dsi) + return tc_dsi_rx_enable(tc); else - value |= DP0_VIDSRC_DPI_RX; - ret = regmap_write(tc->regmap, SYSCTRL, value); - if (ret) - return ret; - - return 0; + return tc_dpi_rx_enable(tc); } static int tc_edp_stream_disable(struct tc_data *tc) @@ -1871,7 +1896,7 @@ static int tc_mipi_dsi_host_attach(struct tc_data *tc) of_node_put(host_node); of_node_put(endpoint); - if (dsi_lanes < 0 || dsi_lanes > 4) + if (dsi_lanes <= 0 || dsi_lanes > 4) return -EINVAL; if (!host) @@ -1992,18 +2017,29 @@ static int tc_probe_bridge_endpoint(struct tc_data *tc) mode |= BIT(endpoint.port); } - if (mode == mode_dpi_to_edp || mode == mode_dpi_to_dp) + if (mode == mode_dpi_to_edp || mode == mode_dpi_to_dp) { + tc->input_connector_dsi = false; return tc_probe_edp_bridge_endpoint(tc); - else if (mode == mode_dsi_to_dpi) + } else if (mode == mode_dsi_to_dpi) { + tc->input_connector_dsi = true; return tc_probe_dpi_bridge_endpoint(tc); - else if (mode == mode_dsi_to_edp || mode == mode_dsi_to_dp) - dev_warn(dev, "The mode DSI-to-(e)DP is not supported!\n"); - else - dev_warn(dev, "Invalid mode (0x%x) is not supported!\n", mode); + } else if (mode == mode_dsi_to_edp || mode == mode_dsi_to_dp) { + tc->input_connector_dsi = true; + return tc_probe_edp_bridge_endpoint(tc); + } + + dev_warn(dev, "Invalid mode (0x%x) is not supported!\n", mode); return -EINVAL; } +static void tc_clk_disable(void *data) +{ + struct clk *refclk = data; + + clk_disable_unprepare(refclk); +} + static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; @@ -2020,6 +2056,24 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) if (ret) return ret; + tc->refclk = devm_clk_get(dev, "ref"); + if (IS_ERR(tc->refclk)) { + ret = PTR_ERR(tc->refclk); + dev_err(dev, "Failed to get refclk: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(tc->refclk); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, tc_clk_disable, tc->refclk); + if (ret) + return ret; + + /* tRSTW = 100 cycles , at 13 MHz that is ~7.69 us */ + usleep_range(10, 15); + /* Shut down GPIO is optional */ tc->sd_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH); if (IS_ERR(tc->sd_gpio)) @@ -2040,13 +2094,6 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) usleep_range(5000, 10000); } - tc->refclk = devm_clk_get(dev, "ref"); - if (IS_ERR(tc->refclk)) { - ret = PTR_ERR(tc->refclk); - dev_err(dev, "Failed to get refclk: %d\n", ret); - return ret; - } - tc->regmap = devm_regmap_init_i2c(client, &tc_regmap_config); if (IS_ERR(tc->regmap)) { ret = PTR_ERR(tc->regmap); @@ -2137,7 +2184,7 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) i2c_set_clientdata(client, tc); - if (tc->bridge.type == DRM_MODE_CONNECTOR_DPI) { /* DPI output */ + if (tc->input_connector_dsi) { /* DSI input */ ret = tc_mipi_dsi_host_attach(tc); if (ret) { drm_bridge_remove(&tc->bridge); diff --git a/drivers/gpu/drm/bridge/ti-dlpc3433.c b/drivers/gpu/drm/bridge/ti-dlpc3433.c new file mode 100644 index 000000000000..06e519798ac5 --- /dev/null +++ b/drivers/gpu/drm/bridge/ti-dlpc3433.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 RenewOutReach + * Copyright (C) 2021 Amarula Solutions(India) + * + * Author: + * Jagan Teki <jagan@amarulasolutions.com> + * Christopher Vollo <chris@renewoutreach.org> + */ + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_of.h> +#include <drm/drm_print.h> +#include <drm/drm_mipi_dsi.h> + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + +enum cmd_registers { + WR_INPUT_SOURCE = 0x05, /* Write Input Source Select */ + WR_EXT_SOURCE_FMT = 0x07, /* Write External Video Source Format */ + WR_IMAGE_CROP = 0x10, /* Write Image Crop */ + WR_DISPLAY_SIZE = 0x12, /* Write Display Size */ + WR_IMAGE_FREEZE = 0x1A, /* Write Image Freeze */ + WR_INPUT_IMAGE_SIZE = 0x2E, /* Write External Input Image Size */ + WR_RGB_LED_EN = 0x52, /* Write RGB LED Enable */ + WR_RGB_LED_CURRENT = 0x54, /* Write RGB LED Current */ + WR_RGB_LED_MAX_CURRENT = 0x5C, /* Write RGB LED Max Current */ + WR_DSI_HS_CLK = 0xBD, /* Write DSI HS Clock */ + RD_DEVICE_ID = 0xD4, /* Read Controller Device ID */ + WR_DSI_PORT_EN = 0xD7, /* Write DSI Port Enable */ +}; + +enum input_source { + INPUT_EXTERNAL_VIDEO = 0, + INPUT_TEST_PATTERN, + INPUT_SPLASH_SCREEN, +}; + +#define DEV_ID_MASK GENMASK(3, 0) +#define IMAGE_FREESE_EN BIT(0) +#define DSI_PORT_EN 0 +#define EXT_SOURCE_FMT_DSI 0 +#define RED_LED_EN BIT(0) +#define GREEN_LED_EN BIT(1) +#define BLUE_LED_EN BIT(2) +#define LED_MASK GENMASK(2, 0) +#define MAX_BYTE_SIZE 8 + +struct dlpc { + struct device *dev; + struct drm_bridge bridge; + struct drm_bridge *next_bridge; + struct device_node *host_node; + struct mipi_dsi_device *dsi; + struct drm_display_mode mode; + + struct gpio_desc *enable_gpio; + struct regulator *vcc_intf; + struct regulator *vcc_flsh; + struct regmap *regmap; + unsigned int dsi_lanes; +}; + +static inline struct dlpc *bridge_to_dlpc(struct drm_bridge *bridge) +{ + return container_of(bridge, struct dlpc, bridge); +} + +static bool dlpc_writeable_noinc_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WR_IMAGE_CROP: + case WR_DISPLAY_SIZE: + case WR_INPUT_IMAGE_SIZE: + case WR_DSI_HS_CLK: + return true; + default: + return false; + } +} + +static const struct regmap_range dlpc_volatile_ranges[] = { + { .range_min = 0x10, .range_max = 0xBF }, +}; + +static const struct regmap_access_table dlpc_volatile_table = { + .yes_ranges = dlpc_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(dlpc_volatile_ranges), +}; + +static struct regmap_config dlpc_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = WR_DSI_PORT_EN, + .writeable_noinc_reg = dlpc_writeable_noinc_reg, + .volatile_table = &dlpc_volatile_table, + .cache_type = REGCACHE_RBTREE, + .name = "dlpc3433", +}; + +static void dlpc_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) +{ + struct dlpc *dlpc = bridge_to_dlpc(bridge); + struct device *dev = dlpc->dev; + struct drm_display_mode *mode = &dlpc->mode; + struct regmap *regmap = dlpc->regmap; + char buf[MAX_BYTE_SIZE]; + unsigned int devid; + + regmap_read(regmap, RD_DEVICE_ID, &devid); + devid &= DEV_ID_MASK; + + DRM_DEV_DEBUG(dev, "DLPC3433 device id: 0x%02x\n", devid); + + if (devid != 0x01) { + DRM_DEV_ERROR(dev, "Unsupported DLPC device id: 0x%02x\n", devid); + return; + } + + /* disable image freeze */ + regmap_write(regmap, WR_IMAGE_FREEZE, IMAGE_FREESE_EN); + + /* enable DSI port */ + regmap_write(regmap, WR_DSI_PORT_EN, DSI_PORT_EN); + + memset(buf, 0, MAX_BYTE_SIZE); + + /* set image crop */ + buf[4] = mode->hdisplay & 0xff; + buf[5] = (mode->hdisplay & 0xff00) >> 8; + buf[6] = mode->vdisplay & 0xff; + buf[7] = (mode->vdisplay & 0xff00) >> 8; + regmap_noinc_write(regmap, WR_IMAGE_CROP, buf, MAX_BYTE_SIZE); + + /* set display size */ + buf[4] = mode->hdisplay & 0xff; + buf[5] = (mode->hdisplay & 0xff00) >> 8; + buf[6] = mode->vdisplay & 0xff; + buf[7] = (mode->vdisplay & 0xff00) >> 8; + regmap_noinc_write(regmap, WR_DISPLAY_SIZE, buf, MAX_BYTE_SIZE); + + /* set input image size */ + buf[0] = mode->hdisplay & 0xff; + buf[1] = (mode->hdisplay & 0xff00) >> 8; + buf[2] = mode->vdisplay & 0xff; + buf[3] = (mode->vdisplay & 0xff00) >> 8; + regmap_noinc_write(regmap, WR_INPUT_IMAGE_SIZE, buf, 4); + + /* set external video port */ + regmap_write(regmap, WR_INPUT_SOURCE, INPUT_EXTERNAL_VIDEO); + + /* set external video format select as DSI */ + regmap_write(regmap, WR_EXT_SOURCE_FMT, EXT_SOURCE_FMT_DSI); + + /* disable image freeze */ + regmap_write(regmap, WR_IMAGE_FREEZE, 0x00); + + /* enable RGB led */ + regmap_update_bits(regmap, WR_RGB_LED_EN, LED_MASK, + RED_LED_EN | GREEN_LED_EN | BLUE_LED_EN); + + msleep(10); +} + +static void dlpc_atomic_pre_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) +{ + struct dlpc *dlpc = bridge_to_dlpc(bridge); + int ret; + + gpiod_set_value(dlpc->enable_gpio, 1); + + msleep(500); + + ret = regulator_enable(dlpc->vcc_intf); + if (ret) + DRM_DEV_ERROR(dlpc->dev, + "failed to enable VCC_INTF regulator: %d\n", ret); + + ret = regulator_enable(dlpc->vcc_flsh); + if (ret) + DRM_DEV_ERROR(dlpc->dev, + "failed to enable VCC_FLSH regulator: %d\n", ret); + + msleep(10); +} + +static void dlpc_atomic_post_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) +{ + struct dlpc *dlpc = bridge_to_dlpc(bridge); + + regulator_disable(dlpc->vcc_flsh); + regulator_disable(dlpc->vcc_intf); + + msleep(10); + + gpiod_set_value(dlpc->enable_gpio, 0); + + msleep(500); +} + +#define MAX_INPUT_SEL_FORMATS 1 + +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) +{ + u32 *input_fmts; + + *num_input_fmts = 0; + + input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts), + GFP_KERNEL); + if (!input_fmts) + return NULL; + + /* This is the DSI-end bus format */ + input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24; + *num_input_fmts = 1; + + return input_fmts; +} + +static void dlpc_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) +{ + struct dlpc *dlpc = bridge_to_dlpc(bridge); + + drm_mode_copy(&dlpc->mode, adjusted_mode); +} + +static int dlpc_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct dlpc *dlpc = bridge_to_dlpc(bridge); + + return drm_bridge_attach(bridge->encoder, dlpc->next_bridge, bridge, flags); +} + +static const struct drm_bridge_funcs dlpc_bridge_funcs = { + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_get_input_bus_fmts = dlpc_atomic_get_input_bus_fmts, + .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_pre_enable = dlpc_atomic_pre_enable, + .atomic_enable = dlpc_atomic_enable, + .atomic_post_disable = dlpc_atomic_post_disable, + .mode_set = dlpc_mode_set, + .attach = dlpc_attach, +}; + +static int dlpc3433_parse_dt(struct dlpc *dlpc) +{ + struct device *dev = dlpc->dev; + struct device_node *endpoint; + int ret; + + dlpc->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(dlpc->enable_gpio)) + return PTR_ERR(dlpc->enable_gpio); + + dlpc->vcc_intf = devm_regulator_get(dlpc->dev, "vcc_intf"); + if (IS_ERR(dlpc->vcc_intf)) + return dev_err_probe(dev, PTR_ERR(dlpc->vcc_intf), + "failed to get VCC_INTF supply\n"); + + dlpc->vcc_flsh = devm_regulator_get(dlpc->dev, "vcc_flsh"); + if (IS_ERR(dlpc->vcc_flsh)) + return dev_err_probe(dev, PTR_ERR(dlpc->vcc_flsh), + "failed to get VCC_FLSH supply\n"); + + dlpc->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0); + if (IS_ERR(dlpc->next_bridge)) + return PTR_ERR(dlpc->next_bridge); + + endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0); + dlpc->dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes"); + if (dlpc->dsi_lanes < 0 || dlpc->dsi_lanes > 4) { + ret = -EINVAL; + goto err_put_endpoint; + } + + dlpc->host_node = of_graph_get_remote_port_parent(endpoint); + if (!dlpc->host_node) { + ret = -ENODEV; + goto err_put_host; + } + + of_node_put(endpoint); + + return 0; + +err_put_host: + of_node_put(dlpc->host_node); +err_put_endpoint: + of_node_put(endpoint); + return ret; +} + +static int dlpc_host_attach(struct dlpc *dlpc) +{ + struct device *dev = dlpc->dev; + struct mipi_dsi_host *host; + struct mipi_dsi_device_info info = { + .type = "dlpc3433", + .channel = 0, + .node = NULL, + }; + + host = of_find_mipi_dsi_host_by_node(dlpc->host_node); + if (!host) { + DRM_DEV_ERROR(dev, "failed to find dsi host\n"); + return -EPROBE_DEFER; + } + + dlpc->dsi = mipi_dsi_device_register_full(host, &info); + if (IS_ERR(dlpc->dsi)) { + DRM_DEV_ERROR(dev, "failed to create dsi device\n"); + return PTR_ERR(dlpc->dsi); + } + + dlpc->dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST; + dlpc->dsi->format = MIPI_DSI_FMT_RGB565; + dlpc->dsi->lanes = dlpc->dsi_lanes; + + return devm_mipi_dsi_attach(dev, dlpc->dsi); +} + +static int dlpc3433_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct dlpc *dlpc; + int ret; + + dlpc = devm_kzalloc(dev, sizeof(*dlpc), GFP_KERNEL); + if (!dlpc) + return -ENOMEM; + + dlpc->dev = dev; + + dlpc->regmap = devm_regmap_init_i2c(client, &dlpc_regmap_config); + if (IS_ERR(dlpc->regmap)) + return PTR_ERR(dlpc->regmap); + + ret = dlpc3433_parse_dt(dlpc); + if (ret) + return ret; + + dev_set_drvdata(dev, dlpc); + i2c_set_clientdata(client, dlpc); + + dlpc->bridge.funcs = &dlpc_bridge_funcs; + dlpc->bridge.of_node = dev->of_node; + drm_bridge_add(&dlpc->bridge); + + ret = dlpc_host_attach(dlpc); + if (ret) { + DRM_DEV_ERROR(dev, "failed to attach dsi host\n"); + goto err_remove_bridge; + } + + return 0; + +err_remove_bridge: + drm_bridge_remove(&dlpc->bridge); + return ret; +} + +static int dlpc3433_remove(struct i2c_client *client) +{ + struct dlpc *dlpc = i2c_get_clientdata(client); + + drm_bridge_remove(&dlpc->bridge); + of_node_put(dlpc->host_node); + + return 0; +} + +static const struct i2c_device_id dlpc3433_id[] = { + { "ti,dlpc3433", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, dlpc3433_id); + +static const struct of_device_id dlpc3433_match_table[] = { + { .compatible = "ti,dlpc3433" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dlpc3433_match_table); + +static struct i2c_driver dlpc3433_driver = { + .probe_new = dlpc3433_probe, + .remove = dlpc3433_remove, + .id_table = dlpc3433_id, + .driver = { + .name = "ti-dlpc3433", + .of_match_table = dlpc3433_match_table, + }, +}; +module_i2c_driver(dlpc3433_driver); + +MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>"); +MODULE_AUTHOR("Christopher Vollo <chris@renewoutreach.org>"); +MODULE_DESCRIPTION("TI DLPC3433 MIPI DSI Display Controller Bridge"); +MODULE_LICENSE("GPL"); |