// 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");