mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 19:08:57 +09:00
drm/bridge: Add support for Chipone ICN6211
ICN6211 is a bridge chip which receives MIPI-DSI inputs and sends RGB outputs. MIPI-DSI supports up to 4 lanes and each lane operates at 1Gbps maximum; the totally maximum input bandwidth is 4Gbps; and the MIPI defined ULPS(ultra-low-power state) is also supported. ICN6211 decodes MIPI-DSI 16bpp RGB565 and 18bpp RGB666 and 24bpp RGB888 packets. The RGB output 18 or 24 bits pixel with pixel clock range of 2MHz to 154MHz. ICN6211 support video resolution up to FHD (1920x1080) and WUXGA(1920x1200). Change-Id: I85cc0dbc8e628b9b1a09371df1d92975202c5c10 Signed-off-by: Wyon Bi <bivvy.bi@rock-chips.com>
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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/
|
||||
|
||||
597
drivers/gpu/drm/bridge/icn6211.c
Normal file
597
drivers/gpu/drm/bridge/icn6211.c
Normal file
@@ -0,0 +1,597 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2018 Rockchip Electronics Co. Ltd.
|
||||
*
|
||||
* Author: Wyon Bi <bivvy.bi@rock-chips.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_mipi_dsi.h>
|
||||
|
||||
#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 <bivvy.bi@rock-chips.com>");
|
||||
MODULE_DESCRIPTION("Chipone ICN6211 MIPI-DSI to RGB bridge chip driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
Reference in New Issue
Block a user