diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index b4537addaf39..cbb21380439d 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -48,6 +48,16 @@ config DRM_LONTIUM_LT8912 help Lontium LT8912 MIPI-DSI to LVDS and HDMI/MHL bridge chip driver. +config DRM_CHIPONE_ICN6211 + tristate "Chipone ICN6211 MIPI-DSI to RGB bridge" + depends on OF + select DRM_KMS_HELPER + select REGMAP_I2C + select DRM_MIPI_DSI + select DRM_PANEL + help + Chipone ICN6211 MIPI-DSI TO RGB bridge chip driver. + source "drivers/gpu/drm/bridge/analogix/Kconfig" source "drivers/gpu/drm/bridge/synopsys/Kconfig" diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 946dd3418cef..19db905e7307 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o obj-$(CONFIG_DRM_RK1000) += rk1000.o obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o obj-$(CONFIG_DRM_LONTIUM_LT8912) += lt8912.o +obj-$(CONFIG_DRM_CHIPONE_ICN6211) += icn6211.o obj-y += analogix/ obj-y += synopsys/ diff --git a/drivers/gpu/drm/bridge/icn6211.c b/drivers/gpu/drm/bridge/icn6211.c new file mode 100644 index 000000000000..96963766d1d3 --- /dev/null +++ b/drivers/gpu/drm/bridge/icn6211.c @@ -0,0 +1,597 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018 Rockchip Electronics Co. Ltd. + * + * Author: Wyon Bi + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define VENDOR_ID 0x0000 +#define DEVICE_ID_H 0x0001 +#define DEVICE_ID_L 0x0002 +#define VERSION_ID 0x0003 +#define FIRMWARE_VERSION 0x0008 +#define CONFIG_FINISH 0x0009 +#define PD_CTRL_0 0x000a +#define PD_CTRL_1 0x000b +#define PD_CTRL_2 0x000c +#define PD_CTRL_3 0x000d +#define RST_CTRL_0 0x000e +#define RST_CTRL_1 0x000f +#define SYS_CTRL_0 0x0010 +#define SYS_CTRL_1 0x0011 +#define SYS_CTRL_2 0x0012 +#define SYS_CTRL_3 0x0013 +#define SYS_CTRL_4 0x0014 +#define RGB_DRV_0 0x0018 +#define RGB_DRV_1 0x0019 +#define RGB_DRV_2 0x001a +#define RGB_DRV_3 0x001b +#define RGB_DLY_0 0x001c +#define RGB_DLY_1 0x001d +#define RGB_TEST_CTRL 0x001e +#define ATE_PLL_EN 0x001f +#define HACTIVE_L 0x0020 +#define VACTIVE_L 0x0021 +#define VACTIVE_HACTIVE_H 0x0022 +#define HFP_L 0x0023 +#define HSW_L 0x0024 +#define HBP_L 0x0025 +#define HFP_HSW_HBP_H 0x0026 +#define VFP 0x0027 +#define VSW 0x0028 +#define VBP 0x0029 +#define BIST_POL 0x002a +#define BIST_RED 0x002b +#define BIST_GREEN 0x002c +#define BIST_BLUE 0x002d +#define BIST_CHESS_X 0x002e +#define BIST_CHESS_Y 0x002f +#define BIST_CHESS_XY_H 0x0030 +#define BIST_FRAME_TIME_L 0x0031 +#define BIST_FRAME_TIME_H 0x0032 +#define FIFO_MAX_ADDR_LOW 0x0033 +#define SYNC_EVENT_DLY_LOW 0x0034 +#define HSW_MIN 0x0035 +#define HFP_MIN 0x0036 +#define LOGIC_RST_NUM 0x0037 +#define OSC_CTRL_0 0x0048 +#define OSC_CTRL_1 0x0049 +#define OSC_CTRL_2 0x004a +#define OSC_CTRL_3 0x004b +#define OSC_CTRL_4 0x004c +#define OSC_CTRL_5 0x004d +#define BG_CTRL 0x004e +#define LDO_PLL 0x004f +#define PLL_CTRL_0 0x0050 +#define PLL_CTRL_1 0x0051 +#define PLL_CTRL_2 0x0052 +#define PLL_CTRL_3 0x0053 +#define PLL_CTRL_4 0x0054 +#define PLL_CTRL_5 0x0055 +#define PLL_CTRL_6 0x0056 +#define PLL_CTRL_7 0x0057 +#define PLL_CTRL_8 0x0058 +#define PLL_CTRL_9 0x0059 +#define PLL_CTRL_A 0x005a +#define PLL_CTRL_B 0x005b +#define PLL_CTRL_C 0x005c +#define PLL_CTRL_D 0x005d +#define PLL_CTRL_E 0x005e +#define PLL_CTRL_F 0x005f +#define PLL_REM_0 0x0060 +#define PLL_REM_1 0x0061 +#define PLL_REM_2 0x0062 +#define PLL_DIV_0 0x0063 +#define PLL_DIV_1 0x0064 +#define PLL_DIV_2 0x0065 +#define PLL_FRAC_0 0x0066 +#define PLL_FRAC_1 0x0067 +#define PLL_FRAC_2 0x0068 +#define PLL_INT_0 0x0069 +#define PLL_INT_1 0x006a +#define PLL_REF_DIV 0x006b +#define PLL_SSC_P0 0x006c +#define PLL_SSC_P1 0x006d +#define PLL_SSC_P2 0x006e +#define PLL_SSC_STEP0 0x006f +#define PLL_SSC_STEP1 0x0070 +#define PLL_SSC_STEP2 0x0071 +#define PLL_SSC_OFFSET0 0x0072 +#define PLL_SSC_OFFSET1 0x0073 +#define PLL_SSC_OFFSET2 0x0074 +#define PLL_SSC_OFFSET3 0x0075 +#define GPIO_OEN 0x0079 +#define MIPI_CFG_PW 0x007a +#define GPIO_0_SEL 0x007b +#define GPIO_1_SEL 0x007c +#define IRQ_SEL 0x007d +#define DBG_SEL 0x007e +#define DBG_SIGNAL 0x007f +#define MIPI_ERR_VECTOR_L 0x0080 +#define MIPI_ERR_VECTOR_H 0x0081 +#define MIPI_ERR_VECTOR_EN_L 0x0082 +#define MIPI_ERR_VECTOR_EN_H 0x0083 +#define MIPI_MAX_SIZE_L 0x0084 +#define MIPI_MAX_SIZE_H 0x0085 +#define DSI_CTRL 0x0086 +#define MIPI_PN_SWAP 0x0087 +#define MIPI_SOT_SYNC_BIT_0 0x0088 +#define MIPI_SOT_SYNC_BIT_1 0x0089 +#define MIPI_ULPS_CTRL 0x008a +#define MIPI_CLK_CHK_VAR 0x008e +#define MIPI_CLK_CHK_INI 0x008f +#define MIPI_T_TERM_EN 0x0090 +#define MIPI_T_HS_SETTLE 0x0091 +#define MIPI_T_TA_SURE_PRE 0x0092 +#define MIPI_T_LPX_SET 0x0094 +#define MIPI_T_CLK_MISS 0x0095 +#define MIPI_INIT_TIME_L 0x0096 +#define MIPI_INIT_TIME_H 0x0097 +#define MIPI_T_CLK_TERM_EN 0x0099 +#define MIPI_T_CLK_SETTLE 0x009a +#define MIPI_TO_HS_RX_L 0x009e +#define MIPI_TO_HS_RX_H 0x009f +#define MIPI_PHY_0 0x00a0 +#define MIPI_PHY_1 0x00a1 +#define MIPI_PHY_2 0x00a2 +#define MIPI_PHY_3 0x00a3 +#define MIPI_PHY_4 0x00a4 +#define MIPI_PHY_5 0x00a5 +#define MIPI_PD_RX 0x00b0 +#define MIPI_PD_TERM 0x00b1 +#define MIPI_PD_HSRX 0x00b2 +#define MIPI_PD_LPTX 0x00b3 +#define MIPI_PD_LPRX 0x00b4 +#define MIPI_PD_CK_LANE 0x00b5 +#define MIPI_FORCE_0 0x00b6 +#define MIPI_RST_CTRL 0x00b7 +#define MIPI_RST_NUM 0x00b8 +#define MIPI_DBG_SET_0 0x00c0 +#define MIPI_DBG_SET_1 0x00c1 +#define MIPI_DBG_SET_2 0x00c2 +#define MIPI_DBG_SET_3 0x00c3 +#define MIPI_DBG_SET_4 0x00c4 +#define MIPI_DBG_SET_5 0x00c5 +#define MIPI_DBG_SET_6 0x00c6 +#define MIPI_DBG_SET_7 0x00c7 +#define MIPI_DBG_SET_8 0x00c8 +#define MIPI_DBG_SET_9 0x00c9 +#define MIPI_DBG_SEL 0x00e0 +#define MIPI_DBG_DATA 0x00e1 +#define MIPI_ATE_TEST_SEL 0x00e2 +#define MIPI_ATE_STATUS_0 0x00e3 +#define MIPI_ATE_STATUS_1 0x00e4 +#define ICN6211_MAX_REGISTER MIPI_ATE_STATUS_1 + +struct icn6211 { + struct drm_bridge base; + struct drm_connector connector; + struct drm_panel *panel; + struct drm_bridge *bridge; + struct drm_display_mode mode; + + struct device *dev; + struct i2c_client *client; + struct mipi_dsi_device dsi; + struct regmap *regmap; + struct clk *refclk; /* reference clock for RGB output clock */ + struct regulator *vdd1; /* MIPI RX power supply, can be 1.8V-3.3V */ + struct regulator *vdd2; /* PLL power supply, can be 1.8V-3.3V */ + struct regulator *vdd3; /* RGB output power supply, can be 1.8V-3.3V */ + struct gpio_desc *enable_gpio; /* When EN is low, this chip is reset */ +}; + +static inline struct icn6211 *bridge_to_icn6211(struct drm_bridge *b) +{ + return container_of(b, struct icn6211, base); +} + +static inline struct icn6211 *connector_to_icn6211(struct drm_connector *c) +{ + return container_of(c, struct icn6211, connector); +} + +static enum drm_connector_status +icn6211_connector_detect(struct drm_connector *connector, bool force) +{ + return connector_status_connected; +} + +static const struct drm_connector_funcs icn6211_connector_funcs = { + .dpms = drm_atomic_helper_connector_dpms, + .detect = icn6211_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static struct drm_encoder * +icn6211_connector_best_encoder(struct drm_connector *connector) +{ + struct icn6211 *icn6211 = connector_to_icn6211(connector); + + return icn6211->base.encoder; +} + +static int icn6211_connector_get_modes(struct drm_connector *connector) +{ + struct icn6211 *icn6211 = connector_to_icn6211(connector); + + return drm_panel_get_modes(icn6211->panel); +} + +static const struct drm_connector_helper_funcs +icn6211_connector_helper_funcs = { + .get_modes = icn6211_connector_get_modes, + .best_encoder = icn6211_connector_best_encoder, +}; + +static void icn6211_bridge_disable(struct drm_bridge *bridge) +{ + struct icn6211 *icn6211 = bridge_to_icn6211(bridge); + + if (icn6211->panel) + drm_panel_disable(icn6211->panel); +} + +static void icn6211_bridge_enable(struct drm_bridge *bridge) +{ + struct icn6211 *icn6211 = bridge_to_icn6211(bridge); + + if (icn6211->panel) + drm_panel_enable(icn6211->panel); +} + +static void icn6211_bridge_post_disable(struct drm_bridge *bridge) +{ + struct icn6211 *icn6211 = bridge_to_icn6211(bridge); + + if (icn6211->panel) + drm_panel_unprepare(icn6211->panel); + + if (icn6211->enable_gpio) + gpiod_direction_output(icn6211->enable_gpio, 0); + + regulator_disable(icn6211->vdd3); + regulator_disable(icn6211->vdd2); + regulator_disable(icn6211->vdd1); + + clk_disable_unprepare(icn6211->refclk); +} + +static void icn6211_bridge_pre_enable(struct drm_bridge *bridge) +{ + struct icn6211 *icn6211 = bridge_to_icn6211(bridge); + const struct drm_display_mode *mode = &icn6211->mode; + u32 hactive, hfp, hsw, hbp, vactive, vfp, vsw, vbp; + u8 hactive_l, hactive_h, hfp_l, hfp_h, hbp_l, hbp_h, hsw_l, hsw_h; + u8 vactive_l, vactive_h; + u32 device_id_h, device_id_l; + u32 pll_refdiv, pll_extra_div, pll_dv, pll_int; + unsigned long refclk = clk_get_rate(icn6211->refclk); + int ret; + + clk_prepare_enable(icn6211->refclk); + + ret = regulator_enable(icn6211->vdd1); + if (ret) + dev_err(icn6211->dev, + "failed to enable vdd1 supply: %d\n", ret); + + ret = regulator_enable(icn6211->vdd2); + if (ret) + dev_err(icn6211->dev, + "failed to enable vdd2 supply: %d\n", ret); + + ret = regulator_enable(icn6211->vdd3); + if (ret) + dev_err(icn6211->dev, + "failed to enable vdd3 supply: %d\n", ret); + + if (icn6211->enable_gpio) + gpiod_direction_output(icn6211->enable_gpio, 1); + + usleep_range(10000, 11000); + + regmap_read(icn6211->regmap, DEVICE_ID_H, &device_id_h); + regmap_read(icn6211->regmap, DEVICE_ID_L, &device_id_l); + dev_info(icn6211->dev, "The ID of device: 0x%04x\n", + (device_id_h << 8) | device_id_l); + + hactive = mode->hdisplay; + hfp = mode->hsync_start - mode->hdisplay; + hsw = mode->hsync_end - mode->hsync_start; + hbp = mode->htotal - mode->hsync_end; + vactive = mode->vdisplay; + vfp = mode->vsync_start - mode->vdisplay; + vsw = mode->vsync_end - mode->vsync_start; + vbp = mode->vtotal - mode->vsync_end; + + hactive_l = hactive & 0xff; + hactive_h = (hactive >> 8) & 0xf; + vactive_l = vactive & 0xff; + vactive_h = (vactive >> 8) & 0xf; + hfp_l = hfp & 0xff; + hfp_h = (hfp >> 8) & 0x3; + hsw_l = hsw & 0xff; + hsw_h = (hsw >> 8) & 0x3; + hbp_l = hbp & 0xff; + hbp_h = (hbp >> 8) & 0x3; + + regmap_write(icn6211->regmap, HACTIVE_L, hactive_l); + regmap_write(icn6211->regmap, VACTIVE_L, vactive_l); + regmap_write(icn6211->regmap, VACTIVE_HACTIVE_H, + (vactive_h << 4) | hactive_h); + regmap_write(icn6211->regmap, HFP_L, hfp_l); + regmap_write(icn6211->regmap, HSW_L, hsw_l); + regmap_write(icn6211->regmap, HBP_L, hbp_l); + regmap_write(icn6211->regmap, HFP_HSW_HBP_H, + (hfp_h << 4) | (hsw_h << 2) | hbp_h); + regmap_write(icn6211->regmap, VFP, vfp); + regmap_write(icn6211->regmap, VSW, vsw); + regmap_write(icn6211->regmap, VBP, vbp); + regmap_write(icn6211->regmap, SYNC_EVENT_DLY_LOW, 0x80); + regmap_write(icn6211->regmap, HFP_MIN, hfp); + regmap_write(icn6211->regmap, MIPI_PD_CK_LANE, 0xa0); + regmap_write(icn6211->regmap, PLL_CTRL_C, 0xff); + regmap_write(icn6211->regmap, BIST_POL, 0x01); + regmap_write(icn6211->regmap, PLL_CTRL_6, 0x90); + + /* + * FIXME: + * fout = fin / pll_refdiv / pll_extra_div * pll_int / pll_dv / 2 + */ + pll_refdiv = 1; + pll_extra_div = 2; + + if (mode->clock <= 44000) { + pll_dv = 8; + regmap_write(icn6211->regmap, PLL_REF_DIV, 0x71); + } else if (mode->clock <= 88000) { + pll_dv = 4; + regmap_write(icn6211->regmap, PLL_REF_DIV, 0x51); + } else { + pll_dv = 2; + regmap_write(icn6211->regmap, PLL_REF_DIV, 0x31); + } + + pll_int = DIV_ROUND_UP(mode->clock * 1000 * 2 * pll_dv, + refclk / pll_refdiv / pll_extra_div); + regmap_write(icn6211->regmap, PLL_INT_0, pll_int); + + dev_dbg(icn6211->dev, + "pll_refdiv=%d, pll_extra_div=%d, pll_int=%d, pll_dv=%d\n", + pll_refdiv, pll_extra_div, pll_int, pll_dv); + dev_dbg(icn6211->dev, "fin=%ld, fout=%ld\n", refclk, + refclk / pll_refdiv / pll_extra_div * pll_int / pll_dv / 2); + + /* + * TODO: + * RGB color swap mode + * RGB_SWAP 000 001 010 011 100 101 + * Group_0 Red Red Green Green Blue Blue + * Group_1 Green Blue Red Blue Red Green + * Group_2 Blue Green Blue Red Green Red + * + * Data bit order mode + * BIT_ORDER 000 001 010 011 100 101 + * Group_X[7] invalid invalid Color[5] Color[0] Color[7] Color[0] + * Group_X[6] invalid invalid Color[4] Color[1] Color[6] Color[1] + * Group_X[5] Color[5] Color[0] Color[3] Color[2] Color[5] Color[2] + * Group_X[4] Color[4] Color[1] Color[2] Color[3] Color[4] Color[3] + * Group_X[3] Color[3] Color[2] Color[1] Color[4] Color[3] Color[4] + * Group_X[2] Color[2] Color[3] Color[0] Color[5] Color[2] Color[5] + * Group_X[1] Color[1] Color[4] invaild invaild Color[1] Color[6] + * Group_X[0] Color[0] Color[5] invaild invaild Color[0] Color[7] + * + * Note: Group_0[7:0] = DATA[7:0] + * Group_1[7:0] = DATA[15:8] + * Group_2[7:0] = DATA[23:16] + */ + regmap_write(icn6211->regmap, SYS_CTRL_0, 0x45); + regmap_write(icn6211->regmap, SYS_CTRL_1, 0x88); + regmap_write(icn6211->regmap, MIPI_FORCE_0, 0x20); + regmap_write(icn6211->regmap, PLL_CTRL_1, 0x20); + regmap_write(icn6211->regmap, CONFIG_FINISH, 0x10); + + if (icn6211->panel) + drm_panel_prepare(icn6211->panel); +} + +static void icn6211_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adj) +{ + struct icn6211 *icn6211 = bridge_to_icn6211(bridge); + + drm_mode_copy(&icn6211->mode, adj); +} + +static int icn6211_bridge_attach(struct drm_bridge *bridge) +{ + struct icn6211 *icn6211 = bridge_to_icn6211(bridge); + struct drm_connector *connector = &icn6211->connector; + struct drm_device *drm = bridge->dev; + struct mipi_dsi_host *host = bridge->driver_private; + struct mipi_dsi_device *dsi = &icn6211->dsi; + int ret; + + dsi->lanes = 4; + dsi->channel = 0; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET; + dsi->host = host; + + ret = mipi_dsi_attach(dsi); + if (ret) { + dev_err(icn6211->dev, "failed to attach dsi host: %d\n", ret); + return ret; + } + + if (icn6211->bridge) { + icn6211->bridge->encoder = bridge->encoder; + + ret = drm_bridge_attach(drm, icn6211->bridge); + if (ret) { + dev_err(icn6211->dev, + "failed to attach bridge: %d\n", ret); + return ret; + } + + bridge->next = icn6211->bridge; + } else { + ret = drm_connector_init(drm, connector, + &icn6211_connector_funcs, + DRM_MODE_CONNECTOR_DPI); + if (ret) { + dev_err(icn6211->dev, + "failed to initialize connector\n"); + return ret; + } + + drm_connector_helper_add(connector, + &icn6211_connector_helper_funcs); + drm_mode_connector_attach_encoder(connector, bridge->encoder); + drm_panel_attach(icn6211->panel, connector); + } + + return 0; +} + +static const struct drm_bridge_funcs icn6211_bridge_funcs = { + .attach = icn6211_bridge_attach, + .mode_set = icn6211_bridge_mode_set, + .pre_enable = icn6211_bridge_pre_enable, + .enable = icn6211_bridge_enable, + .disable = icn6211_bridge_disable, + .post_disable = icn6211_bridge_post_disable, +}; + +static const struct regmap_config icn6211_regmap_config = { + .name = "icn6211", + .reg_bits = 8, + .val_bits = 8, + .max_register = ICN6211_MAX_REGISTER, +}; + +static int icn6211_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct icn6211 *icn6211; + int ret; + + icn6211 = devm_kzalloc(dev, sizeof(*icn6211), GFP_KERNEL); + if (!icn6211) + return -ENOMEM; + + icn6211->dev = dev; + icn6211->client = client; + i2c_set_clientdata(client, icn6211); + + ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1, + &icn6211->panel, &icn6211->bridge); + if (ret) + return ret; + + icn6211->refclk = devm_clk_get(dev, "refclk"); + if (IS_ERR(icn6211->refclk)) { + ret = PTR_ERR(icn6211->refclk); + dev_err(dev, "failed to get ref clk: %d\n", ret); + return ret; + } + + icn6211->vdd1 = devm_regulator_get(dev, "vdd1"); + if (IS_ERR(icn6211->vdd1)) + return PTR_ERR(icn6211->vdd1); + + icn6211->vdd2 = devm_regulator_get(dev, "vdd2"); + if (IS_ERR(icn6211->vdd2)) + return PTR_ERR(icn6211->vdd2); + + icn6211->vdd3 = devm_regulator_get(dev, "vdd3"); + if (IS_ERR(icn6211->vdd3)) + return PTR_ERR(icn6211->vdd3); + + icn6211->enable_gpio = devm_gpiod_get_optional(dev, "enable", 0); + if (IS_ERR(icn6211->enable_gpio)) { + ret = PTR_ERR(icn6211->enable_gpio); + dev_err(dev, "failed to request enable GPIO: %d\n", ret); + return ret; + } + + icn6211->regmap = devm_regmap_init_i2c(client, &icn6211_regmap_config); + if (IS_ERR(icn6211->regmap)) { + ret = PTR_ERR(icn6211->regmap); + dev_err(dev, "failed to initialize regmap: %d\n", ret); + return ret; + } + + icn6211->base.funcs = &icn6211_bridge_funcs; + icn6211->base.of_node = dev->of_node; + ret = drm_bridge_add(&icn6211->base); + if (ret) { + dev_err(dev, "failed to add drm_bridge: %d\n", ret); + return ret; + } + + return 0; +} + +static int icn6211_i2c_remove(struct i2c_client *client) +{ + struct icn6211 *icn6211 = i2c_get_clientdata(client); + + drm_bridge_remove(&icn6211->base); + + return 0; +} + +static const struct i2c_device_id icn6211_i2c_table[] = { + { "icn6211", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, icn6211_i2c_table); + +static const struct of_device_id icn6211_of_match[] = { + { .compatible = "chipone,icn6211" }, + {} +}; +MODULE_DEVICE_TABLE(of, icn6211_of_match); + +static struct i2c_driver icn6211_i2c_driver = { + .driver = { + .name = "icn6211", + .of_match_table = icn6211_of_match, + }, + .probe = icn6211_i2c_probe, + .remove = icn6211_i2c_remove, + .id_table = icn6211_i2c_table, +}; +module_i2c_driver(icn6211_i2c_driver); + +MODULE_AUTHOR("Wyon Bi "); +MODULE_DESCRIPTION("Chipone ICN6211 MIPI-DSI to RGB bridge chip driver"); +MODULE_LICENSE("GPL v2");