linuxOS_AP05/kernel/drivers/gpu/drm/bridge/icn6211.c

598 lines
17 KiB
C
Raw Normal View History

2025-06-02 05:59:07 +00:00
// 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");