mfd: display-serdes: add split mode function support
Signed-off-by: Luo Wei <lw@rock-chips.com> Change-Id: I70f1707c8625d8d693b1134d178c68fcdfbdf268
This commit is contained in:
@@ -10,4 +10,4 @@ obj-$(CONFIG_SERDES_DISPLAY_CHIP_NOVO) += novo/
|
||||
|
||||
serdes-mfd-display-$(CONFIG_MFD_SERDES_DISPLAY) += serdes-core.o serdes-irq.o
|
||||
|
||||
obj-$(CONFIG_MFD_SERDES_DISPLAY) += serdes-mfd-display.o serdes-i2c.o serdes-bridge.o serdes-panel.o serdes-gpio.o serdes-pinctrl.o
|
||||
obj-$(CONFIG_MFD_SERDES_DISPLAY) += serdes-mfd-display.o serdes-i2c.o serdes-bridge.o serdes-bridge-split.o serdes-panel.o serdes-panel-split.o serdes-gpio.o serdes-pinctrl.o
|
||||
|
||||
@@ -102,8 +102,15 @@
|
||||
#endif
|
||||
|
||||
#define MFD_SERDES_DISPLAY_VERSION "serdes-mfd-displaly-v10-230901"
|
||||
|
||||
#define MAX_NUM_SERDES_SPLIT 8
|
||||
struct serdes;
|
||||
enum ser_link_mode {
|
||||
SER_DUAL_LINK,
|
||||
SER_LINKA,
|
||||
SER_LINKB,
|
||||
SER_SPLITTER_MODE,
|
||||
};
|
||||
|
||||
struct serdes_chip_pinctrl_info {
|
||||
struct pinctrl_pin_desc *pins;
|
||||
unsigned int num_pins;
|
||||
@@ -163,6 +170,12 @@ struct serdes_chip_gpio_ops {
|
||||
int (*to_irq)(struct serdes *serdes, int gpio);
|
||||
};
|
||||
|
||||
struct serdes_chip_split_ops {
|
||||
int (*select)(struct serdes *serdes, int chan);
|
||||
int (*deselect)(struct serdes *serdes, int chan);
|
||||
int (*set_i2c_addr)(struct serdes *serdes, int address, int link);
|
||||
};
|
||||
|
||||
struct serdes_chip_pm_ops {
|
||||
/* serdes chip function for suspend and resume */
|
||||
int (*suspend)(struct serdes *serdes);
|
||||
@@ -198,6 +211,7 @@ struct serdes_chip_data {
|
||||
struct serdes_chip_panel_ops *panel_ops;
|
||||
struct serdes_chip_pinctrl_ops *pinctrl_ops;
|
||||
struct serdes_chip_gpio_ops *gpio_ops;
|
||||
struct serdes_chip_split_ops *split_ops;
|
||||
struct serdes_chip_pm_ops *pm_ops;
|
||||
struct serdes_chip_irq_ops *irq_ops;
|
||||
};
|
||||
@@ -241,7 +255,35 @@ struct serdes_panel {
|
||||
struct serdes *parent;
|
||||
struct regmap *regmap;
|
||||
struct mipi_dsi_device *dsi;
|
||||
struct device_node *dsi_node;
|
||||
struct device_node *remote_node;
|
||||
struct drm_display_mode mode;
|
||||
struct backlight_device *backlight;
|
||||
struct serdes_init_seq *serdes_init_seq;
|
||||
bool sel_mipi;
|
||||
bool dv_swp_ab;
|
||||
bool dpi_deskew_en;
|
||||
bool split_mode;
|
||||
u32 num_lanes;
|
||||
u32 dsi_lane_map[4];
|
||||
};
|
||||
|
||||
struct serdes_panel_split {
|
||||
struct drm_panel panel;
|
||||
enum drm_connector_status status;
|
||||
struct drm_connector connector;
|
||||
|
||||
const char *name;
|
||||
u32 width_mm;
|
||||
u32 height_mm;
|
||||
u32 link_rate;
|
||||
u32 lane_count;
|
||||
bool ssc;
|
||||
|
||||
struct device *dev;
|
||||
struct serdes *parent;
|
||||
struct regmap *regmap;
|
||||
struct mipi_dsi_device *dsi;
|
||||
struct device_node *remote_node;
|
||||
struct drm_display_mode mode;
|
||||
struct backlight_device *backlight;
|
||||
struct serdes_init_seq *serdes_init_seq;
|
||||
@@ -265,7 +307,7 @@ struct serdes_bridge {
|
||||
struct serdes *parent;
|
||||
struct regmap *regmap;
|
||||
struct mipi_dsi_device *dsi;
|
||||
struct device_node *dsi_node;
|
||||
struct device_node *remote_node;
|
||||
struct drm_display_mode mode;
|
||||
struct backlight_device *backlight;
|
||||
|
||||
@@ -277,6 +319,29 @@ struct serdes_bridge {
|
||||
u32 dsi_lane_map[4];
|
||||
};
|
||||
|
||||
struct serdes_bridge_split {
|
||||
struct drm_bridge base_bridge;
|
||||
struct drm_bridge *next_bridge;
|
||||
enum drm_connector_status status;
|
||||
atomic_t triggered;
|
||||
struct drm_connector connector;
|
||||
struct drm_panel *panel;
|
||||
|
||||
struct device *dev;
|
||||
struct serdes *parent;
|
||||
struct regmap *regmap;
|
||||
struct mipi_dsi_device *dsi;
|
||||
struct device_node *remote_node;
|
||||
struct drm_display_mode mode;
|
||||
struct backlight_device *backlight;
|
||||
|
||||
bool sel_mipi;
|
||||
bool dv_swp_ab;
|
||||
bool dpi_deskew_en;
|
||||
u32 num_lanes;
|
||||
u32 dsi_lane_map[4];
|
||||
};
|
||||
|
||||
struct serdes {
|
||||
int num_gpio;
|
||||
struct mutex io_lock;
|
||||
@@ -307,6 +372,15 @@ struct serdes {
|
||||
struct delayed_work mfd_delay_work;
|
||||
bool route_enable;
|
||||
bool use_delay_work;
|
||||
|
||||
bool split_mode_enable;
|
||||
unsigned int reg_hw;
|
||||
unsigned int reg_use;
|
||||
unsigned int link_use;
|
||||
unsigned int id_serdes_bridge_split;
|
||||
unsigned int id_serdes_panel_split;
|
||||
struct serdes *g_serdes_bridge_split;
|
||||
|
||||
struct pinctrl *pinctrl_node;
|
||||
struct pinctrl_state *pins_default;
|
||||
struct pinctrl_state *pins_init;
|
||||
@@ -314,7 +388,9 @@ struct serdes {
|
||||
|
||||
struct serdes_init_seq *serdes_init_seq;
|
||||
struct serdes_bridge *serdes_bridge;
|
||||
struct serdes_bridge_split *serdes_bridge_split;
|
||||
struct serdes_panel *serdes_panel;
|
||||
struct serdes_panel_split *serdes_panel_split;
|
||||
struct serdes_pinctrl *pinctrl;
|
||||
struct serdes_chip_data *chip_data;
|
||||
};
|
||||
|
||||
@@ -762,6 +762,84 @@ static struct serdes_chip_gpio_ops max96745_gpio_ops = {
|
||||
.to_irq = max96745_gpio_to_irq,
|
||||
};
|
||||
|
||||
static int max96745_select(struct serdes *serdes, int chan)
|
||||
{
|
||||
/*0076 for linkA and 0086 for linkB*/
|
||||
if (chan == DUAL_LINK) {
|
||||
serdes_set_bits(serdes, 0x0076, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 0));
|
||||
serdes_set_bits(serdes, 0x0086, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 0));
|
||||
SERDES_DBG_CHIP("%s: enable %s remote i2c of linkA and linkB\n", __func__,
|
||||
serdes->chip_data->name);
|
||||
} else if (chan == LINKA) {
|
||||
serdes_set_bits(serdes, 0x0076, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 0));
|
||||
serdes_set_bits(serdes, 0x0086, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 1));
|
||||
SERDES_DBG_CHIP("%s: only enable %s remote i2c of linkA\n", __func__,
|
||||
serdes->chip_data->name);
|
||||
} else if (chan == LINKB) {
|
||||
serdes_set_bits(serdes, 0x0076, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 1));
|
||||
serdes_set_bits(serdes, 0x0086, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 0));
|
||||
SERDES_DBG_CHIP("%s: only enable %s remote i2c of linkB\n", __func__,
|
||||
serdes->chip_data->name);
|
||||
} else if (chan == SPLITTER_MODE) {
|
||||
serdes_set_bits(serdes, 0x0076, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 0));
|
||||
serdes_set_bits(serdes, 0x0086, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 0));
|
||||
SERDES_DBG_CHIP("%s: enable %s remote i2c of linkA and linkB\n", __func__,
|
||||
serdes->chip_data->name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max96745_deselect(struct serdes *serdes, int chan)
|
||||
{
|
||||
|
||||
if (chan == DUAL_LINK) {
|
||||
serdes_set_bits(serdes, 0x0076, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 1));
|
||||
serdes_set_bits(serdes, 0x0086, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 1));
|
||||
SERDES_DBG_CHIP("%s: disable %s remote i2c of linkA and linkB\n", __func__,
|
||||
serdes->chip_data->name);
|
||||
} else if (chan == LINKA) {
|
||||
serdes_set_bits(serdes, 0x0076, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 1));
|
||||
serdes_set_bits(serdes, 0x0086, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 0));
|
||||
SERDES_DBG_CHIP("%s: only disable %s remote i2c of linkA\n", __func__,
|
||||
serdes->chip_data->name);
|
||||
} else if (chan == LINKB) {
|
||||
serdes_set_bits(serdes, 0x0076, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 0));
|
||||
serdes_set_bits(serdes, 0x0086, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 1));
|
||||
SERDES_DBG_CHIP("%s: only disable %s remote i2c of linkB\n", __func__,
|
||||
serdes->chip_data->name);
|
||||
} else if (chan == SPLITTER_MODE) {
|
||||
serdes_set_bits(serdes, 0x0076, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 1));
|
||||
serdes_set_bits(serdes, 0x0086, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 1));
|
||||
SERDES_DBG_CHIP("%s: disable %s remote i2c of linkA and linkB\n", __func__,
|
||||
serdes->chip_data->name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct serdes_chip_split_ops max96745_split_ops = {
|
||||
.select = max96745_select,
|
||||
.deselect = max96745_deselect,
|
||||
};
|
||||
|
||||
static int max96745_pm_suspend(struct serdes *serdes)
|
||||
{
|
||||
return 0;
|
||||
@@ -802,6 +880,7 @@ struct serdes_chip_data serdes_max96745_data = {
|
||||
.bridge_ops = &max96745_bridge_ops,
|
||||
.pinctrl_ops = &max96745_pinctrl_ops,
|
||||
.gpio_ops = &max96745_gpio_ops,
|
||||
.split_ops = &max96745_split_ops,
|
||||
.pm_ops = &max96745_pm_ops,
|
||||
.irq_ops = &max96745_irq_ops,
|
||||
};
|
||||
|
||||
@@ -136,4 +136,11 @@
|
||||
/* 7074h */
|
||||
#define MAX_LINK_RATE GENMASK(7, 0)
|
||||
|
||||
enum link_mode {
|
||||
DUAL_LINK,
|
||||
LINKA,
|
||||
LINKB,
|
||||
SPLITTER_MODE,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -567,6 +567,37 @@ static struct serdes_chip_gpio_ops max96752_gpio_ops = {
|
||||
.to_irq = max96752_gpio_to_irq,
|
||||
};
|
||||
|
||||
static int max96752_set_i2c_addr(struct serdes *serdes, int address, int link)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (link == LINKA) {
|
||||
/* TX_SRC_ID[1] = 0 */
|
||||
ret = serdes_reg_write(serdes, 0x73, 0x31);
|
||||
/* Receive packets with this stream ID = 0 */
|
||||
ret = serdes_reg_write(serdes, 0x50, 0x00);
|
||||
ret = serdes_reg_write(serdes, 0x00, address << 1);
|
||||
} else if (link == LINKB) {
|
||||
/* TX_SRC_ID[1] = 1 */
|
||||
ret = serdes_reg_write(serdes, 0x73, 0x32);
|
||||
/* Receive packets with this stream ID = 1 */
|
||||
ret = serdes_reg_write(serdes, 0x50, 0x01);
|
||||
ret = serdes_reg_write(serdes, 0x00, address << 1);
|
||||
} else {
|
||||
dev_info(serdes->dev, "link %d is error\n", link);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
SERDES_DBG_CHIP("%s: set serdes chip %s i2c 7bit address to 0x%x\n", __func__,
|
||||
serdes->chip_data->name, address);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct serdes_chip_split_ops max96752_split_ops = {
|
||||
.set_i2c_addr = max96752_set_i2c_addr,
|
||||
};
|
||||
|
||||
static int max96752_pm_suspend(struct serdes *serdes)
|
||||
{
|
||||
return 0;
|
||||
@@ -607,6 +638,7 @@ struct serdes_chip_data serdes_max96752_data = {
|
||||
.panel_ops = &max96752_panel_ops,
|
||||
.bridge_ops = &max96752_bridge_ops,
|
||||
.pinctrl_ops = &max96752_pinctrl_ops,
|
||||
.split_ops = &max96752_split_ops,
|
||||
.gpio_ops = &max96752_gpio_ops,
|
||||
.pm_ops = &max96752_pm_ops,
|
||||
.irq_ops = &max96752_irq_ops,
|
||||
|
||||
@@ -35,4 +35,11 @@
|
||||
#define OVR_RES_CFG BIT(7)
|
||||
#define GPIO_RX_ID GENMASK(4, 0)
|
||||
|
||||
enum link_mode {
|
||||
DUAL_LINK,
|
||||
LINKA,
|
||||
LINKB,
|
||||
SPLITTER_MODE,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -679,6 +679,89 @@ static struct serdes_chip_gpio_ops max96789_gpio_ops = {
|
||||
.to_irq = max96789_gpio_to_irq,
|
||||
};
|
||||
|
||||
static int max96789_select(struct serdes *serdes, int chan)
|
||||
{
|
||||
u32 link_cfg, val;
|
||||
int ret;
|
||||
|
||||
serdes_set_bits(serdes, 0x0001, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 0));
|
||||
|
||||
serdes_reg_read(serdes, 0x0010, &link_cfg);
|
||||
if ((link_cfg & LINK_CFG) == SPLITTER_MODE)
|
||||
SERDES_DBG_CHIP("%s: serdes chip %s already split mode cfg=0x%x\n", __func__,
|
||||
serdes->chip_data->name, link_cfg);
|
||||
|
||||
if (chan == 0 && (link_cfg & LINK_CFG) != DUAL_LINK) {
|
||||
serdes_set_bits(serdes, 0x0004,
|
||||
LINK_EN_B | LINK_EN_A,
|
||||
FIELD_PREP(LINK_EN_A, 1) |
|
||||
FIELD_PREP(LINK_EN_B, 1));
|
||||
serdes_set_bits(serdes, 0x0010,
|
||||
RESET_ONESHOT | AUTO_LINK | LINK_CFG,
|
||||
FIELD_PREP(RESET_ONESHOT, 1) |
|
||||
FIELD_PREP(AUTO_LINK, 0) |
|
||||
FIELD_PREP(LINK_CFG, DUAL_LINK));
|
||||
SERDES_DBG_CHIP("%s: change to use dual link\n", __func__);
|
||||
} else if (chan == 1 && (link_cfg & LINK_CFG) != LINKA) {
|
||||
serdes_set_bits(serdes, 0x0004,
|
||||
LINK_EN_B | LINK_EN_A,
|
||||
FIELD_PREP(LINK_EN_A, 1) |
|
||||
FIELD_PREP(LINK_EN_B, 0));
|
||||
serdes_set_bits(serdes, 0x0010,
|
||||
RESET_ONESHOT | AUTO_LINK | LINK_CFG,
|
||||
FIELD_PREP(RESET_ONESHOT, 1) |
|
||||
FIELD_PREP(AUTO_LINK, 0) |
|
||||
FIELD_PREP(LINK_CFG, LINKA));
|
||||
SERDES_DBG_CHIP("%s: change to use linkA\n", __func__);
|
||||
} else if (chan == 2 && (link_cfg & LINK_CFG) != LINKB) {
|
||||
serdes_set_bits(serdes, 0x0004,
|
||||
LINK_EN_B | LINK_EN_A,
|
||||
FIELD_PREP(LINK_EN_A, 0) |
|
||||
FIELD_PREP(LINK_EN_B, 1));
|
||||
serdes_set_bits(serdes, 0x0010,
|
||||
RESET_ONESHOT | AUTO_LINK | LINK_CFG,
|
||||
FIELD_PREP(RESET_ONESHOT, 1) |
|
||||
FIELD_PREP(AUTO_LINK, 0) |
|
||||
FIELD_PREP(LINK_CFG, LINKB));
|
||||
SERDES_DBG_CHIP("%s: change to use linkB\n", __func__);
|
||||
} else if (chan == 3 && (link_cfg & LINK_CFG) != SPLITTER_MODE) {
|
||||
serdes_set_bits(serdes, 0x0004,
|
||||
LINK_EN_B | LINK_EN_A,
|
||||
FIELD_PREP(LINK_EN_A, 1) |
|
||||
FIELD_PREP(LINK_EN_B, 1));
|
||||
serdes_set_bits(serdes, 0x0010,
|
||||
RESET_ONESHOT | AUTO_LINK | LINK_CFG,
|
||||
FIELD_PREP(RESET_ONESHOT, 1) |
|
||||
FIELD_PREP(AUTO_LINK, 0) |
|
||||
FIELD_PREP(LINK_CFG, SPLITTER_MODE));
|
||||
SERDES_DBG_CHIP("%s: change to use split mode\n", __func__);
|
||||
}
|
||||
|
||||
ret = regmap_read_poll_timeout(serdes->regmap, 0x0013, val,
|
||||
val & LOCKED, 100,
|
||||
50 * USEC_PER_MSEC);
|
||||
if (ret < 0) {
|
||||
dev_err(serdes->dev, "GMSL2 link lock timeout\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max96789_deselect(struct serdes *serdes, int chan)
|
||||
{
|
||||
//serdes_set_bits(serdes, 0x0001, DIS_REM_CC,
|
||||
// FIELD_PREP(DIS_REM_CC, 1));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct serdes_chip_split_ops max96789_split_ops = {
|
||||
.select = max96789_select,
|
||||
.deselect = max96789_deselect,
|
||||
};
|
||||
|
||||
static int max96789_pm_suspend(struct serdes *serdes)
|
||||
{
|
||||
return 0;
|
||||
@@ -719,6 +802,7 @@ struct serdes_chip_data serdes_max96789_data = {
|
||||
.bridge_ops = &max96789_bridge_ops,
|
||||
.pinctrl_ops = &max96789_pinctrl_ops,
|
||||
.gpio_ops = &max96789_gpio_ops,
|
||||
.split_ops = &max96789_split_ops,
|
||||
.pm_ops = &max96789_pm_ops,
|
||||
.irq_ops = &max96789_irq_ops,
|
||||
};
|
||||
|
||||
@@ -39,6 +39,15 @@
|
||||
#define UART_2_EN BIT(5)
|
||||
#define UART_1_EN BIT(4)
|
||||
|
||||
/* 0004h */
|
||||
#define GMSL2_B BIT(7)
|
||||
#define GMSL2_A BIT(6)
|
||||
#define LINK_EN_B BIT(5)
|
||||
#define LINK_EN_A BIT(4)
|
||||
#define AUD_TX_SRC_Y BIT(1)
|
||||
#define AUD_TX_SRC_X BIT(0)
|
||||
|
||||
|
||||
/* 0005h */
|
||||
#define LOCK_EN BIT(7)
|
||||
#define ERRB_EN BIT(6)
|
||||
|
||||
@@ -0,0 +1,371 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* serdes-bridge.c -- drm bridge access for different serdes chips
|
||||
*
|
||||
* Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
|
||||
*
|
||||
* Author: luowei <lw@rock-chips.com>
|
||||
*/
|
||||
|
||||
#include "core.h"
|
||||
|
||||
static struct serdes_bridge_split *to_serdes_bridge_split(struct drm_bridge *bridge)
|
||||
{
|
||||
return container_of(bridge, struct serdes_bridge_split, base_bridge);
|
||||
}
|
||||
|
||||
static struct mipi_dsi_device *serdes_attach_dsi(struct serdes_bridge_split *serdes_bridge_split,
|
||||
struct device_node *remote_node)
|
||||
{
|
||||
struct mipi_dsi_device_info info = { "serdes", 0, NULL };
|
||||
struct serdes *serdes = serdes_bridge_split->parent;
|
||||
struct mipi_dsi_device *dsi;
|
||||
struct mipi_dsi_host *host;
|
||||
int ret;
|
||||
|
||||
if (serdes->chip_data->name)
|
||||
memcpy(&info.type, serdes->chip_data->name, ARRAY_SIZE(info.type));
|
||||
|
||||
SERDES_DBG_MFD("%s: type=%s, name=%s\n", __func__,
|
||||
info.type, serdes->chip_data->name);
|
||||
|
||||
host = of_find_mipi_dsi_host_by_node(remote_node);
|
||||
if (!host) {
|
||||
dev_err(serdes_bridge_split->dev, "failed to find serdes dsi host\n");
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
dsi = mipi_dsi_device_register_full(host, &info);
|
||||
if (IS_ERR(dsi)) {
|
||||
dev_err(serdes_bridge_split->dev, "failed to create serdes dsi device\n");
|
||||
return dsi;
|
||||
}
|
||||
|
||||
dsi->lanes = 4;
|
||||
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||
|
||||
if (serdes->chip_data->name) {
|
||||
if ((!strcmp(serdes->chip_data->name, "bu18tl82")) ||
|
||||
(!strcmp(serdes->chip_data->name, "bu18rl82"))) {
|
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
|
||||
MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET;
|
||||
SERDES_DBG_MFD("%s: %s dsi_mode MIPI_DSI_MODE_VIDEO_BURST 0x%lx\n",
|
||||
__func__, serdes->chip_data->name, dsi->mode_flags);
|
||||
} else {
|
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
|
||||
SERDES_DBG_MFD("%s: %s dsi_mode MIPI_DSI_MODE_VIDEO_SYNC_PULSE 0x%lx\n",
|
||||
__func__, serdes->chip_data->name, dsi->mode_flags);
|
||||
}
|
||||
} else {
|
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
|
||||
SERDES_DBG_MFD("%s: %s dsi_mode MIPI_DSI_MODE_VIDEO_SYNC_PULSE 0x%lx\n",
|
||||
__func__, serdes->chip_data->name, dsi->mode_flags);
|
||||
}
|
||||
|
||||
ret = mipi_dsi_attach(dsi);
|
||||
if (ret < 0) {
|
||||
dev_err(serdes_bridge_split->dev, "failed to attach serdes dsi to host\n");
|
||||
mipi_dsi_device_unregister(dsi);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return dsi;
|
||||
}
|
||||
|
||||
static int serdes_bridge_split_attach(struct drm_bridge *bridge,
|
||||
enum drm_bridge_attach_flags flags)
|
||||
{
|
||||
struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge);
|
||||
struct serdes *serdes = serdes_bridge_split->parent;
|
||||
int ret = 0;
|
||||
|
||||
ret = drm_of_find_panel_or_bridge(bridge->of_node, 1, -1,
|
||||
&serdes_bridge_split->panel,
|
||||
&serdes_bridge_split->next_bridge);
|
||||
if (ret) {
|
||||
dev_err(serdes_bridge_split->dev->parent,
|
||||
"failed to find serdes bridge, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (serdes_bridge_split->sel_mipi) {
|
||||
dev_info(serdes_bridge_split->dev->parent, "serdes sel_mipi %d\n",
|
||||
serdes_bridge_split->sel_mipi);
|
||||
/* Attach primary DSI */
|
||||
serdes_bridge_split->dsi = serdes_attach_dsi(serdes_bridge_split,
|
||||
serdes_bridge_split->remote_node);
|
||||
if (IS_ERR(serdes_bridge_split->dsi))
|
||||
return PTR_ERR(serdes_bridge_split->dsi);
|
||||
}
|
||||
|
||||
if (serdes_bridge_split->next_bridge) {
|
||||
ret = drm_bridge_attach(bridge->encoder, serdes_bridge_split->next_bridge,
|
||||
bridge, flags);
|
||||
if (ret) {
|
||||
if (serdes_bridge_split->sel_mipi)
|
||||
mipi_dsi_device_unregister(serdes_bridge_split->dsi);
|
||||
|
||||
dev_err(serdes_bridge_split->dev->parent,
|
||||
"failed to attach bridge, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (serdes->chip_data->bridge_ops->attach)
|
||||
ret = serdes->chip_data->bridge_ops->attach(serdes);
|
||||
|
||||
SERDES_DBG_MFD("%s: ret=%d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void serdes_bridge_split_detach(struct drm_bridge *bridge)
|
||||
{
|
||||
struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge);
|
||||
|
||||
if (serdes_bridge_split->sel_mipi) {
|
||||
mipi_dsi_detach(serdes_bridge_split->dsi);
|
||||
mipi_dsi_device_unregister(serdes_bridge_split->dsi);
|
||||
}
|
||||
|
||||
SERDES_DBG_MFD("%s\n", __func__);
|
||||
}
|
||||
|
||||
static void serdes_bridge_split_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge);
|
||||
struct serdes *serdes = serdes_bridge_split->parent;
|
||||
int ret = 0;
|
||||
|
||||
if (serdes_bridge_split->panel)
|
||||
drm_panel_disable(serdes_bridge_split->panel);
|
||||
|
||||
if (serdes->chip_data->bridge_ops->disable)
|
||||
ret = serdes->chip_data->bridge_ops->disable(serdes);
|
||||
|
||||
extcon_set_state_sync(serdes->extcon, EXTCON_JACK_VIDEO_OUT, false);
|
||||
|
||||
SERDES_DBG_MFD("%s: ret=%d\n", __func__, ret);
|
||||
}
|
||||
|
||||
static void serdes_bridge_split_post_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge);
|
||||
struct serdes *serdes = serdes_bridge_split->parent;
|
||||
int ret = 0;
|
||||
|
||||
serdes_set_pinctrl_sleep(serdes);
|
||||
|
||||
if (serdes_bridge_split->panel)
|
||||
ret = drm_panel_unprepare(serdes_bridge_split->panel);
|
||||
|
||||
if (serdes->chip_data->bridge_ops->post_disable)
|
||||
ret = serdes->chip_data->bridge_ops->post_disable(serdes);
|
||||
|
||||
SERDES_DBG_MFD("%s: ret=%d\n", __func__, ret);
|
||||
}
|
||||
|
||||
static void serdes_bridge_split_pre_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge);
|
||||
struct serdes *serdes = serdes_bridge_split->parent;
|
||||
int ret = 0;
|
||||
|
||||
if (serdes->chip_data->bridge_ops->init)
|
||||
ret = serdes->chip_data->bridge_ops->init(serdes);
|
||||
|
||||
if (serdes->chip_data->serdes_type == TYPE_DES) {
|
||||
if (serdes->chip_data->chip_init)
|
||||
serdes->chip_data->chip_init(serdes);
|
||||
ret = serdes_i2c_set_sequence(serdes);
|
||||
}
|
||||
|
||||
if (serdes->chip_data->bridge_ops->pre_enable)
|
||||
ret = serdes->chip_data->bridge_ops->pre_enable(serdes);
|
||||
|
||||
if (serdes_bridge_split->panel)
|
||||
ret = drm_panel_prepare(serdes_bridge_split->panel);
|
||||
|
||||
serdes_set_pinctrl_default(serdes);
|
||||
|
||||
SERDES_DBG_MFD("%s: %s ret=%d\n", __func__, dev_name(serdes->dev), ret);
|
||||
}
|
||||
|
||||
static void serdes_bridge_split_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge);
|
||||
struct serdes *serdes = serdes_bridge_split->parent;
|
||||
int ret = 0;
|
||||
|
||||
if (serdes_bridge_split->panel)
|
||||
ret = drm_panel_enable(serdes_bridge_split->panel);
|
||||
|
||||
if (serdes->chip_data->bridge_ops->enable)
|
||||
ret = serdes->chip_data->bridge_ops->enable(serdes);
|
||||
|
||||
if (!ret) {
|
||||
extcon_set_state_sync(serdes->extcon, EXTCON_JACK_VIDEO_OUT, true);
|
||||
SERDES_DBG_MFD("%s: extcon is true\n", __func__);
|
||||
}
|
||||
|
||||
SERDES_DBG_MFD("%s: %s-%s ret=%d\n", __func__, dev_name(serdes->dev),
|
||||
serdes->chip_data->name, ret);
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
serdes_bridge_split_detect(struct drm_bridge *bridge)
|
||||
{
|
||||
struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge);
|
||||
struct serdes *serdes = serdes_bridge_split->parent;
|
||||
enum drm_connector_status status = connector_status_connected;
|
||||
|
||||
if (serdes->chip_data->bridge_ops->detect)
|
||||
status = serdes->chip_data->bridge_ops->detect(serdes);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int serdes_bridge_split_get_modes(struct drm_bridge *bridge,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge);
|
||||
struct serdes *serdes = serdes_bridge_split->parent;
|
||||
int ret = 0;
|
||||
|
||||
if (serdes->chip_data->bridge_ops->get_modes)
|
||||
ret = serdes->chip_data->bridge_ops->get_modes(serdes);
|
||||
|
||||
if (serdes_bridge_split->next_bridge)
|
||||
ret = drm_bridge_get_modes(serdes_bridge_split->next_bridge, connector);
|
||||
|
||||
if (serdes_bridge_split->panel)
|
||||
ret = drm_panel_get_modes(serdes_bridge_split->panel, connector);
|
||||
|
||||
SERDES_DBG_MFD("%s:name=%s, node=%s\n", __func__,
|
||||
serdes->chip_data->name, serdes_bridge_split->dev->of_node->name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs serdes_bridge_split_funcs = {
|
||||
.attach = serdes_bridge_split_attach,
|
||||
.detach = serdes_bridge_split_detach,
|
||||
.disable = serdes_bridge_split_disable,
|
||||
.post_disable = serdes_bridge_split_post_disable,
|
||||
.pre_enable = serdes_bridge_split_pre_enable,
|
||||
.enable = serdes_bridge_split_enable,
|
||||
.detect = serdes_bridge_split_detect,
|
||||
.get_modes = serdes_bridge_split_get_modes,
|
||||
.atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
|
||||
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
|
||||
.atomic_reset = drm_atomic_helper_bridge_reset,
|
||||
};
|
||||
|
||||
static int serdes_bridge_split_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct serdes *serdes = dev_get_drvdata(pdev->dev.parent);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct serdes_bridge_split *serdes_bridge_split;
|
||||
|
||||
if (!serdes->dev)
|
||||
return -1;
|
||||
|
||||
serdes_bridge_split = devm_kzalloc(dev, sizeof(*serdes_bridge_split), GFP_KERNEL);
|
||||
if (!serdes_bridge_split)
|
||||
return -ENOMEM;
|
||||
|
||||
serdes->serdes_bridge_split = serdes_bridge_split;
|
||||
serdes_bridge_split->dev = dev;
|
||||
serdes_bridge_split->parent = dev_get_drvdata(dev->parent);
|
||||
platform_set_drvdata(pdev, serdes_bridge_split);
|
||||
serdes_bridge_split->regmap = dev_get_regmap(dev->parent, NULL);
|
||||
if (!serdes_bridge_split->regmap)
|
||||
return dev_err_probe(dev, -ENODEV, "failed to get serdes regmap\n");
|
||||
|
||||
serdes_bridge_split->sel_mipi = of_property_read_bool(dev->parent->of_node, "sel-mipi");
|
||||
SERDES_DBG_MFD("%s: sel_mipi=%d\n", __func__, serdes_bridge_split->sel_mipi);
|
||||
|
||||
serdes_bridge_split->base_bridge.of_node = dev->parent->of_node;
|
||||
serdes_bridge_split->remote_node = of_graph_get_remote_node(dev->parent->of_node, 0, -1);
|
||||
if (!serdes_bridge_split->remote_node) {
|
||||
serdes_bridge_split->base_bridge.of_node = dev->of_node;
|
||||
SERDES_DBG_MFD("warning: failed to get remote node for serdes on %s\n",
|
||||
dev_name(dev->parent));
|
||||
serdes_bridge_split->remote_node = of_graph_get_remote_node(dev->of_node, 0, -1);
|
||||
if (!serdes_bridge_split->remote_node) {
|
||||
return dev_err_probe(dev, -ENODEV,
|
||||
"failed to get remote node for serdes dsi\n");
|
||||
}
|
||||
}
|
||||
|
||||
serdes_bridge_split->base_bridge.funcs = &serdes_bridge_split_funcs;
|
||||
serdes_bridge_split->base_bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_MODES;
|
||||
|
||||
if (serdes_bridge_split->sel_mipi) {
|
||||
serdes_bridge_split->base_bridge.type = DRM_MODE_CONNECTOR_DSI;
|
||||
SERDES_DBG_MFD("%s: type DRM_MODE_CONNECTOR_DSI\n", __func__);
|
||||
} else if (serdes_bridge_split->parent->chip_data->connector_type) {
|
||||
serdes_bridge_split->base_bridge.type =
|
||||
serdes_bridge_split->parent->chip_data->connector_type;
|
||||
SERDES_DBG_MFD("%s: type %d\n", __func__, serdes_bridge_split->base_bridge.type);
|
||||
} else {
|
||||
serdes_bridge_split->base_bridge.type = DRM_MODE_CONNECTOR_eDP;
|
||||
SERDES_DBG_MFD("%s: type DRM_MODE_CONNECTOR_LVDS\n", __func__);
|
||||
}
|
||||
|
||||
drm_bridge_add(&serdes_bridge_split->base_bridge);
|
||||
|
||||
dev_info(dev, "serdes %s, %s successful mipi=%d, of_node=%s\n",
|
||||
serdes->chip_data->name, __func__, serdes_bridge_split->sel_mipi,
|
||||
serdes_bridge_split->base_bridge.of_node->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serdes_bridge_split_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct serdes_bridge_split *serdes_bridge_split = platform_get_drvdata(pdev);
|
||||
|
||||
drm_bridge_remove(&serdes_bridge_split->base_bridge);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id serdes_bridge_split_of_match[] = {
|
||||
{ .compatible = "rohm,bu18tl82-bridge-split", },
|
||||
{ .compatible = "rohm,bu18rl82-bridge-split", },
|
||||
{ .compatible = "maxim,max96745-bridge-split", },
|
||||
{ .compatible = "maxim,max96755-bridge-split", },
|
||||
{ .compatible = "maxim,max96752-bridge-split", },
|
||||
{ .compatible = "maxim,max96789-bridge-split", },
|
||||
{ .compatible = "rockchip,rkx111-bridge-split", },
|
||||
{ .compatible = "rockchip,rkx121-bridge-split", },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_driver serdes_bridge_split_driver = {
|
||||
.driver = {
|
||||
.name = "serdes-bridge-split",
|
||||
.of_match_table = of_match_ptr(serdes_bridge_split_of_match),
|
||||
},
|
||||
.probe = serdes_bridge_split_probe,
|
||||
.remove = serdes_bridge_split_remove,
|
||||
};
|
||||
|
||||
static int __init serdes_bridge_split_init(void)
|
||||
{
|
||||
return platform_driver_register(&serdes_bridge_split_driver);
|
||||
}
|
||||
device_initcall(serdes_bridge_split_init);
|
||||
|
||||
static void __exit serdes_bridge_split_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&serdes_bridge_split_driver);
|
||||
}
|
||||
module_exit(serdes_bridge_split_exit);
|
||||
|
||||
MODULE_AUTHOR("Luo Wei <lw@rock-chips.com>");
|
||||
MODULE_DESCRIPTION("display bridge interface for different serdes");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:serdes-bridge-split");
|
||||
@@ -15,7 +15,7 @@ static struct serdes_bridge *to_serdes_bridge(struct drm_bridge *bridge)
|
||||
}
|
||||
|
||||
static struct mipi_dsi_device *serdes_attach_dsi(struct serdes_bridge *serdes_bridge,
|
||||
struct device_node *dsi_node)
|
||||
struct device_node *remote_node)
|
||||
{
|
||||
struct mipi_dsi_device_info info = { "serdes", 0, NULL };
|
||||
struct serdes *serdes = serdes_bridge->parent;
|
||||
@@ -29,7 +29,7 @@ static struct mipi_dsi_device *serdes_attach_dsi(struct serdes_bridge *serdes_br
|
||||
SERDES_DBG_MFD("%s: type=%s, name=%s\n", __func__,
|
||||
info.type, serdes->chip_data->name);
|
||||
|
||||
host = of_find_mipi_dsi_host_by_node(dsi_node);
|
||||
host = of_find_mipi_dsi_host_by_node(remote_node);
|
||||
if (!host) {
|
||||
dev_err(serdes_bridge->dev, "failed to find serdes dsi host\n");
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
@@ -90,7 +90,7 @@ static int serdes_bridge_attach(struct drm_bridge *bridge,
|
||||
dev_info(serdes_bridge->dev->parent, "serdes sel_mipi %d\n",
|
||||
serdes_bridge->sel_mipi);
|
||||
/* Attach primary DSI */
|
||||
serdes_bridge->dsi = serdes_attach_dsi(serdes_bridge, serdes_bridge->dsi_node);
|
||||
serdes_bridge->dsi = serdes_attach_dsi(serdes_bridge, serdes_bridge->remote_node);
|
||||
if (IS_ERR(serdes_bridge->dsi))
|
||||
return PTR_ERR(serdes_bridge->dsi);
|
||||
}
|
||||
@@ -281,17 +281,22 @@ static int serdes_bridge_probe(struct platform_device *pdev)
|
||||
return dev_err_probe(dev, -ENODEV, "failed to get serdes regmap\n");
|
||||
|
||||
serdes_bridge->sel_mipi = of_property_read_bool(dev->parent->of_node, "sel-mipi");
|
||||
if (serdes_bridge->sel_mipi) {
|
||||
serdes_bridge->dsi_node = of_graph_get_remote_node(dev->parent->of_node, 0, -1);
|
||||
if (!serdes_bridge->dsi_node)
|
||||
return dev_err_probe(dev->parent, -ENODEV,
|
||||
"failed to get remote node for serdes dsi\n");
|
||||
SERDES_DBG_MFD("%s: sel_mipi=%d\n", __func__, serdes_bridge->sel_mipi);
|
||||
|
||||
SERDES_DBG_MFD("%s: sel_mipi=%d\n", __func__, serdes_bridge->sel_mipi);
|
||||
serdes_bridge->base_bridge.of_node = dev->parent->of_node;
|
||||
serdes_bridge->remote_node = of_graph_get_remote_node(dev->parent->of_node, 0, -1);
|
||||
if (!serdes_bridge->remote_node) {
|
||||
serdes_bridge->base_bridge.of_node = dev->of_node;
|
||||
SERDES_DBG_MFD("warning: failed to get remote node for serdes on %s\n",
|
||||
dev_name(dev->parent));
|
||||
serdes_bridge->remote_node = of_graph_get_remote_node(dev->of_node, 0, -1);
|
||||
if (!serdes_bridge->remote_node) {
|
||||
return dev_err_probe(dev, -ENODEV,
|
||||
"failed to get remote node for serdes dsi\n");
|
||||
}
|
||||
}
|
||||
|
||||
serdes_bridge->base_bridge.funcs = &serdes_bridge_funcs;
|
||||
serdes_bridge->base_bridge.of_node = dev->parent->of_node;
|
||||
serdes_bridge->base_bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_MODES;
|
||||
|
||||
if (serdes_bridge->sel_mipi) {
|
||||
|
||||
@@ -62,6 +62,10 @@ static const struct mfd_cell serdes_max96789_devs[] = {
|
||||
.name = "serdes-bridge",
|
||||
.of_compatible = "maxim,max96789-bridge",
|
||||
},
|
||||
{
|
||||
.name = "serdes-bridge-split",
|
||||
.of_compatible = "maxim,max96789-bridge-split",
|
||||
},
|
||||
};
|
||||
|
||||
static const struct mfd_cell serdes_max96752_devs[] = {
|
||||
@@ -73,6 +77,10 @@ static const struct mfd_cell serdes_max96752_devs[] = {
|
||||
.name = "serdes-panel",
|
||||
.of_compatible = "maxim,max96752-panel",
|
||||
},
|
||||
{
|
||||
.name = "serdes-panel-split",
|
||||
.of_compatible = "maxim,max96752-panel-split",
|
||||
},
|
||||
};
|
||||
|
||||
static const struct mfd_cell serdes_max96772_devs[] = {
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#include "core.h"
|
||||
|
||||
static struct serdes *g_serdes_ser_split[MAX_NUM_SERDES_SPLIT];
|
||||
|
||||
int serdes_i2c_set_sequence(struct serdes *serdes)
|
||||
{
|
||||
struct device *dev = serdes->dev;
|
||||
@@ -53,6 +55,42 @@ int serdes_i2c_set_sequence(struct serdes *serdes)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(serdes_i2c_set_sequence);
|
||||
|
||||
static int serdes_set_i2c_address(struct serdes *serdes, u32 reg_hw, u32 reg_use, int link)
|
||||
{
|
||||
int ret = 0;
|
||||
struct i2c_client *client_split;
|
||||
struct serdes *serdes_split = serdes->g_serdes_bridge_split;
|
||||
|
||||
if (!serdes_split) {
|
||||
pr_info("%s: serdes_split is null\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
client_split = to_i2c_client(serdes->regmap->dev);
|
||||
SERDES_DBG_MFD("%s: %s-%s addr=0x%x reg_hw=0x%x, reg_use=0x%x serdes_split=0x%p\n",
|
||||
__func__, dev_name(serdes_split->dev), client_split->name,
|
||||
client_split->addr, serdes->reg_hw, serdes->reg_use, serdes_split);
|
||||
|
||||
client_split->addr = serdes->reg_hw;
|
||||
|
||||
if (serdes_split && serdes_split->chip_data->split_ops &&
|
||||
serdes_split->chip_data->split_ops->select)
|
||||
ret = serdes_split->chip_data->split_ops->select(serdes_split, link);
|
||||
|
||||
if (serdes->chip_data->split_ops && serdes->chip_data->split_ops->set_i2c_addr)
|
||||
serdes->chip_data->split_ops->set_i2c_addr(serdes, reg_use, link);
|
||||
|
||||
if (serdes_split && serdes_split->chip_data->split_ops &&
|
||||
serdes_split->chip_data->split_ops->select)
|
||||
ret = serdes_split->chip_data->split_ops->select(serdes_split, SER_SPLITTER_MODE);
|
||||
|
||||
client_split->addr = serdes->reg_use;
|
||||
|
||||
serdes_i2c_set_sequence(serdes);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void serdes_mfd_work(struct work_struct *work)
|
||||
{
|
||||
struct serdes *serdes = container_of(work, struct serdes, mfd_delay_work.work);
|
||||
@@ -149,7 +187,8 @@ static int serdes_i2c_probe(struct i2c_client *client,
|
||||
serdes->chip_data = (struct serdes_chip_data *)of_device_get_match_data(dev);
|
||||
i2c_set_clientdata(client, serdes);
|
||||
|
||||
dev_info(dev, "serdes %s probe start\n", serdes->chip_data->name);
|
||||
dev_info(dev, "serdes %s probe start, id=%d\n", serdes->chip_data->name,
|
||||
serdes->chip_data->serdes_id);
|
||||
|
||||
serdes->type = serdes->chip_data->serdes_type;
|
||||
serdes->regmap = devm_regmap_init_i2c(client, serdes->chip_data->regmap_config);
|
||||
@@ -206,6 +245,31 @@ static int serdes_i2c_probe(struct i2c_client *client,
|
||||
return ret;
|
||||
}
|
||||
|
||||
of_property_read_u32(dev->of_node, "id-serdes-bridge-split",
|
||||
&serdes->id_serdes_bridge_split);
|
||||
if ((serdes->id_serdes_bridge_split < MAX_NUM_SERDES_SPLIT) && (serdes->type == TYPE_SER)) {
|
||||
g_serdes_ser_split[serdes->id_serdes_bridge_split] = serdes;
|
||||
SERDES_DBG_MFD("%s: %s-%s g_serdes_split[%d]=0x%p\n", __func__,
|
||||
dev_name(serdes->dev), serdes->chip_data->name,
|
||||
serdes->id_serdes_bridge_split, serdes);
|
||||
}
|
||||
|
||||
of_property_read_u32(dev->of_node, "reg-hw", &serdes->reg_hw);
|
||||
of_property_read_u32(dev->of_node, "reg", &serdes->reg_use);
|
||||
of_property_read_u32(dev->of_node, "link", &serdes->link_use);
|
||||
of_property_read_u32(dev->of_node, "id-serdes-panel-split", &serdes->id_serdes_panel_split);
|
||||
if ((serdes->id_serdes_panel_split) && (serdes->type == TYPE_DES)) {
|
||||
serdes->g_serdes_bridge_split = g_serdes_ser_split[serdes->id_serdes_panel_split];
|
||||
SERDES_DBG_MFD("%s: id=%d p=0x%p\n", __func__,
|
||||
serdes->id_serdes_panel_split, serdes->g_serdes_bridge_split);
|
||||
}
|
||||
|
||||
if (serdes->reg_hw) {
|
||||
SERDES_DBG_MFD("%s: %s start change i2c address from 0x%x to 0x%x\n",
|
||||
__func__, dev->of_node->name, serdes->reg_hw, serdes->reg_use);
|
||||
serdes_set_i2c_address(serdes, serdes->reg_hw, serdes->reg_use, serdes->link_use);
|
||||
}
|
||||
|
||||
serdes->use_delay_work = of_property_read_bool(dev->of_node, "use-delay-work");
|
||||
if (serdes->use_delay_work) {
|
||||
serdes->mfd_wq = alloc_ordered_workqueue("%s",
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* serdes-panel.c -- drm panel access for different serdes chips
|
||||
*
|
||||
* Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
|
||||
*
|
||||
* Author: luowei <lw@rock-chips.com>
|
||||
*/
|
||||
|
||||
#include "core.h"
|
||||
|
||||
static inline struct serdes_panel_split *to_serdes_panel_split(struct drm_panel *panel)
|
||||
{
|
||||
return container_of(panel, struct serdes_panel_split, panel);
|
||||
}
|
||||
|
||||
static int serdes_panel_split_prepare(struct drm_panel *panel)
|
||||
{
|
||||
struct serdes_panel_split *serdes_panel_split = to_serdes_panel_split(panel);
|
||||
struct serdes *serdes = serdes_panel_split->parent;
|
||||
int ret = 0;
|
||||
|
||||
if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->init)
|
||||
ret = serdes->chip_data->panel_ops->init(serdes);
|
||||
|
||||
if (serdes->chip_data->serdes_type == TYPE_DES)
|
||||
serdes_i2c_set_sequence(serdes);
|
||||
|
||||
if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->prepare)
|
||||
ret = serdes->chip_data->panel_ops->prepare(serdes);
|
||||
|
||||
serdes_set_pinctrl_default(serdes);
|
||||
|
||||
SERDES_DBG_MFD("%s: %s\n", __func__, serdes->chip_data->name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int serdes_panel_split_unprepare(struct drm_panel *panel)
|
||||
{
|
||||
struct serdes_panel_split *serdes_panel_split = to_serdes_panel_split(panel);
|
||||
struct serdes *serdes = serdes_panel_split->parent;
|
||||
int ret = 0;
|
||||
|
||||
if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->unprepare)
|
||||
ret = serdes->chip_data->panel_ops->unprepare(serdes);
|
||||
|
||||
serdes_set_pinctrl_sleep(serdes);
|
||||
|
||||
SERDES_DBG_MFD("%s: %s\n", __func__, serdes->chip_data->name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int serdes_panel_split_enable(struct drm_panel *panel)
|
||||
{
|
||||
struct serdes_panel_split *serdes_panel_split = to_serdes_panel_split(panel);
|
||||
struct serdes *serdes = serdes_panel_split->parent;
|
||||
int ret = 0;
|
||||
|
||||
if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->enable)
|
||||
ret = serdes->chip_data->panel_ops->enable(serdes);
|
||||
|
||||
backlight_enable(serdes_panel_split->backlight);
|
||||
|
||||
SERDES_DBG_MFD("%s: %s\n", __func__, serdes->chip_data->name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int serdes_panel_split_disable(struct drm_panel *panel)
|
||||
{
|
||||
struct serdes_panel_split *serdes_panel_split = to_serdes_panel_split(panel);
|
||||
struct serdes *serdes = serdes_panel_split->parent;
|
||||
int ret = 0;
|
||||
|
||||
if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->disable)
|
||||
ret = serdes->chip_data->panel_ops->disable(serdes);
|
||||
|
||||
backlight_disable(serdes_panel_split->backlight);
|
||||
|
||||
SERDES_DBG_MFD("%s: %s\n", __func__, serdes->chip_data->name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int serdes_panel_split_get_modes(struct drm_panel *panel,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct serdes_panel_split *serdes_panel_split = to_serdes_panel_split(panel);
|
||||
struct serdes *serdes = serdes_panel_split->parent;
|
||||
struct drm_display_mode *mode;
|
||||
u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
|
||||
int ret = 1;
|
||||
|
||||
connector->display_info.width_mm = serdes_panel_split->width_mm; //323; //346;
|
||||
connector->display_info.height_mm = serdes_panel_split->height_mm; //182; //194;
|
||||
drm_display_info_set_bus_formats(&connector->display_info, &bus_format, 1);
|
||||
|
||||
mode = drm_mode_duplicate(connector->dev, &serdes_panel_split->mode);
|
||||
mode->width_mm = serdes_panel_split->width_mm; //323; //346;
|
||||
mode->height_mm = serdes_panel_split->height_mm; //182; //194;
|
||||
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
|
||||
|
||||
drm_mode_set_name(mode);
|
||||
drm_mode_probed_add(connector, mode);
|
||||
|
||||
if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->get_modes)
|
||||
ret = serdes->chip_data->panel_ops->get_modes(serdes);
|
||||
|
||||
pr_info("%s: %s wxh=%dx%d mode clock %u kHz, flags[0x%x]\n"
|
||||
" H: %04d %04d %04d %04d\n"
|
||||
" V: %04d %04d %04d %04d\n"
|
||||
"bus_format: 0x%x\n",
|
||||
dev_name(serdes->dev),
|
||||
panel->dev->of_node->name,
|
||||
serdes_panel_split->width_mm, serdes_panel_split->height_mm,
|
||||
mode->clock, mode->flags,
|
||||
mode->hdisplay, mode->hsync_start,
|
||||
mode->hsync_end, mode->htotal,
|
||||
mode->vdisplay, mode->vsync_start,
|
||||
mode->vsync_end, mode->vtotal,
|
||||
bus_format);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct drm_panel_funcs serdes_panel_split_funcs = {
|
||||
.prepare = serdes_panel_split_prepare,
|
||||
.unprepare = serdes_panel_split_unprepare,
|
||||
.enable = serdes_panel_split_enable,
|
||||
.disable = serdes_panel_split_disable,
|
||||
.get_modes = serdes_panel_split_get_modes,
|
||||
};
|
||||
|
||||
static int serdes_panel_split_parse_dt(struct serdes_panel_split *serdes_panel_split)
|
||||
{
|
||||
struct device *dev = serdes_panel_split->dev;
|
||||
struct display_timing dt;
|
||||
struct videomode vm;
|
||||
int ret, len;
|
||||
unsigned int panel_size[2] = {320, 180};
|
||||
unsigned int link_rate_count_ssc[3] = {DP_LINK_BW_2_7, 4, 0};
|
||||
|
||||
//pr_info("%s: node=%s\n", __func__, dev->of_node->name);
|
||||
|
||||
serdes_panel_split->width_mm = panel_size[0];
|
||||
serdes_panel_split->height_mm = panel_size[1];
|
||||
|
||||
serdes_panel_split->link_rate = link_rate_count_ssc[0];
|
||||
serdes_panel_split->lane_count = link_rate_count_ssc[1];
|
||||
serdes_panel_split->ssc = link_rate_count_ssc[2];
|
||||
|
||||
if (of_find_property(dev->of_node, "panel-size", &len)) {
|
||||
len /= sizeof(unsigned int);
|
||||
ret = of_property_read_u32_array(dev->of_node, "panel-size",
|
||||
panel_size, len);
|
||||
if (!ret) {
|
||||
serdes_panel_split->width_mm = panel_size[0];
|
||||
serdes_panel_split->height_mm = panel_size[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (of_find_property(dev->of_node, "rate-count-ssc", &len)) {
|
||||
len /= sizeof(unsigned int);
|
||||
ret = of_property_read_u32_array(dev->of_node, "rate-count-ssc",
|
||||
panel_size, len);
|
||||
if (!ret) {
|
||||
serdes_panel_split->link_rate = link_rate_count_ssc[0];
|
||||
serdes_panel_split->lane_count = link_rate_count_ssc[1];
|
||||
serdes_panel_split->ssc = link_rate_count_ssc[2];
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(dev, "panle size %dx%d, rate=%d, cnt=%d, ssc=%d\n",
|
||||
serdes_panel_split->width_mm, serdes_panel_split->height_mm,
|
||||
serdes_panel_split->link_rate, serdes_panel_split->lane_count,
|
||||
serdes_panel_split->ssc);
|
||||
|
||||
ret = of_get_display_timing(dev->of_node, "panel-timing", &dt);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%pOF:serdes no panel-timing node found\n", dev->of_node);
|
||||
return ret;
|
||||
}
|
||||
|
||||
videomode_from_timing(&dt, &vm);
|
||||
drm_display_mode_from_videomode(&vm, &serdes_panel_split->mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serdes_panel_split_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct serdes *serdes = dev_get_drvdata(pdev->dev.parent);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct serdes_panel_split *serdes_panel_split;
|
||||
int ret;
|
||||
|
||||
serdes_panel_split = devm_kzalloc(dev, sizeof(*serdes_panel_split), GFP_KERNEL);
|
||||
if (!serdes_panel_split)
|
||||
return -ENOMEM;
|
||||
|
||||
serdes->serdes_panel_split = serdes_panel_split;
|
||||
serdes_panel_split->dev = dev;
|
||||
serdes_panel_split->parent = dev_get_drvdata(dev->parent);
|
||||
platform_set_drvdata(pdev, serdes_panel_split);
|
||||
|
||||
serdes_panel_split->regmap = dev_get_regmap(dev->parent, NULL);
|
||||
if (!serdes_panel_split->regmap)
|
||||
return dev_err_probe(dev, -ENODEV, "failed to get serdes regmap\n");
|
||||
|
||||
ret = serdes_panel_split_parse_dt(serdes_panel_split);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to parse serdes DT\n");
|
||||
|
||||
serdes_panel_split->backlight = devm_of_find_backlight(dev);
|
||||
if (IS_ERR(serdes_panel_split->backlight))
|
||||
return dev_err_probe(dev, PTR_ERR(serdes_panel_split->backlight),
|
||||
"failed to get serdes backlight\n");
|
||||
|
||||
if (serdes_panel_split->parent->chip_data->connector_type) {
|
||||
drm_panel_init(&serdes_panel_split->panel, dev, &serdes_panel_split_funcs,
|
||||
serdes_panel_split->parent->chip_data->connector_type);
|
||||
} else {
|
||||
drm_panel_init(&serdes_panel_split->panel, dev, &serdes_panel_split_funcs,
|
||||
DRM_MODE_CONNECTOR_LVDS);
|
||||
}
|
||||
drm_panel_add(&serdes_panel_split->panel);
|
||||
|
||||
dev_info(dev, "serdes %s-%s %s successful\n", dev_name(serdes->dev),
|
||||
serdes->chip_data->name, __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serdes_panel_split_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct serdes_panel_split *serdes_panel_split = platform_get_drvdata(pdev);
|
||||
|
||||
drm_panel_remove(&serdes_panel_split->panel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id serdes_panel_split_of_match[] = {
|
||||
{ .compatible = "rohm,bu18rl82-panel-split" },
|
||||
{ .compatible = "maxim,max96752-panel-split" },
|
||||
{ .compatible = "maxim,max96772-panel-split" },
|
||||
{ .compatible = "rockchip,rkx121-panel-split" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_driver serdes_panel_split_driver = {
|
||||
.driver = {
|
||||
.name = "serdes-panel-split",
|
||||
.of_match_table = of_match_ptr(serdes_panel_split_of_match),
|
||||
},
|
||||
.probe = serdes_panel_split_probe,
|
||||
.remove = serdes_panel_split_remove,
|
||||
};
|
||||
|
||||
static int __init serdes_panel_split_init(void)
|
||||
{
|
||||
return platform_driver_register(&serdes_panel_split_driver);
|
||||
}
|
||||
device_initcall(serdes_panel_split_init);
|
||||
|
||||
static void __exit serdes_panel_split_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&serdes_panel_split_driver);
|
||||
}
|
||||
module_exit(serdes_panel_split_exit);
|
||||
|
||||
MODULE_AUTHOR("Luo Wei <lw@rock-chips.com>");
|
||||
MODULE_DESCRIPTION("display panel interface for different serdes");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:serdes-panel-split");
|
||||
@@ -141,6 +141,8 @@ static int serdes_panel_parse_dt(struct serdes_panel *serdes_panel)
|
||||
unsigned int panel_size[2] = {320, 180};
|
||||
unsigned int link_rate_count_ssc[3] = {DP_LINK_BW_2_7, 4, 0};
|
||||
|
||||
//pr_info("%s: node=%s\n", __func__, dev->of_node->name);
|
||||
|
||||
serdes_panel->width_mm = panel_size[0];
|
||||
serdes_panel->height_mm = panel_size[1];
|
||||
|
||||
@@ -233,7 +235,8 @@ static int serdes_panel_probe(struct platform_device *pdev)
|
||||
}
|
||||
drm_panel_add(&serdes_panel->panel);
|
||||
|
||||
dev_info(dev, "serdes %s serdes_panel_probe successful\n", serdes->chip_data->name);
|
||||
dev_info(dev, "serdes %s-%s serdes_panel_probe successful\n",
|
||||
dev_name(serdes->dev), serdes->chip_data->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user