From 80ebdebca0ae220ea0f33ef3b268622da7e05972 Mon Sep 17 00:00:00 2001 From: Luo Wei Date: Wed, 22 Nov 2023 20:51:49 +0800 Subject: [PATCH] mfd: display-serdes: add split mode function support Signed-off-by: Luo Wei Change-Id: I70f1707c8625d8d693b1134d178c68fcdfbdf268 --- drivers/mfd/display-serdes/Makefile | 2 +- drivers/mfd/display-serdes/core.h | 82 +++- .../mfd/display-serdes/maxim/maxim-max96745.c | 79 ++++ .../mfd/display-serdes/maxim/maxim-max96745.h | 7 + .../mfd/display-serdes/maxim/maxim-max96752.c | 32 ++ .../mfd/display-serdes/maxim/maxim-max96752.h | 7 + .../mfd/display-serdes/maxim/maxim-max96789.c | 84 ++++ .../mfd/display-serdes/maxim/maxim-max96789.h | 9 + .../mfd/display-serdes/serdes-bridge-split.c | 371 ++++++++++++++++++ drivers/mfd/display-serdes/serdes-bridge.c | 25 +- drivers/mfd/display-serdes/serdes-core.c | 8 + drivers/mfd/display-serdes/serdes-i2c.c | 66 +++- .../mfd/display-serdes/serdes-panel-split.c | 277 +++++++++++++ drivers/mfd/display-serdes/serdes-panel.c | 5 +- 14 files changed, 1038 insertions(+), 16 deletions(-) create mode 100644 drivers/mfd/display-serdes/serdes-bridge-split.c create mode 100644 drivers/mfd/display-serdes/serdes-panel-split.c diff --git a/drivers/mfd/display-serdes/Makefile b/drivers/mfd/display-serdes/Makefile index 420076ccc22e..f5a5a15642c8 100644 --- a/drivers/mfd/display-serdes/Makefile +++ b/drivers/mfd/display-serdes/Makefile @@ -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 diff --git a/drivers/mfd/display-serdes/core.h b/drivers/mfd/display-serdes/core.h index 22b962b35f2a..8563ae5e7fac 100644 --- a/drivers/mfd/display-serdes/core.h +++ b/drivers/mfd/display-serdes/core.h @@ -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; }; diff --git a/drivers/mfd/display-serdes/maxim/maxim-max96745.c b/drivers/mfd/display-serdes/maxim/maxim-max96745.c index 3c83a8be9968..ad0d43ffd89f 100644 --- a/drivers/mfd/display-serdes/maxim/maxim-max96745.c +++ b/drivers/mfd/display-serdes/maxim/maxim-max96745.c @@ -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, }; diff --git a/drivers/mfd/display-serdes/maxim/maxim-max96745.h b/drivers/mfd/display-serdes/maxim/maxim-max96745.h index 031f4f901596..5d17fe50c05c 100644 --- a/drivers/mfd/display-serdes/maxim/maxim-max96745.h +++ b/drivers/mfd/display-serdes/maxim/maxim-max96745.h @@ -136,4 +136,11 @@ /* 7074h */ #define MAX_LINK_RATE GENMASK(7, 0) +enum link_mode { + DUAL_LINK, + LINKA, + LINKB, + SPLITTER_MODE, +}; + #endif diff --git a/drivers/mfd/display-serdes/maxim/maxim-max96752.c b/drivers/mfd/display-serdes/maxim/maxim-max96752.c index 49515098946a..5b13ce2b7cdf 100644 --- a/drivers/mfd/display-serdes/maxim/maxim-max96752.c +++ b/drivers/mfd/display-serdes/maxim/maxim-max96752.c @@ -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, diff --git a/drivers/mfd/display-serdes/maxim/maxim-max96752.h b/drivers/mfd/display-serdes/maxim/maxim-max96752.h index 7e3e4aa551c3..dc1e960e350f 100644 --- a/drivers/mfd/display-serdes/maxim/maxim-max96752.h +++ b/drivers/mfd/display-serdes/maxim/maxim-max96752.h @@ -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 diff --git a/drivers/mfd/display-serdes/maxim/maxim-max96789.c b/drivers/mfd/display-serdes/maxim/maxim-max96789.c index b970f10ed786..50791118cac6 100644 --- a/drivers/mfd/display-serdes/maxim/maxim-max96789.c +++ b/drivers/mfd/display-serdes/maxim/maxim-max96789.c @@ -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, }; diff --git a/drivers/mfd/display-serdes/maxim/maxim-max96789.h b/drivers/mfd/display-serdes/maxim/maxim-max96789.h index 7026e506dc81..97beb6dc859a 100644 --- a/drivers/mfd/display-serdes/maxim/maxim-max96789.h +++ b/drivers/mfd/display-serdes/maxim/maxim-max96789.h @@ -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) diff --git a/drivers/mfd/display-serdes/serdes-bridge-split.c b/drivers/mfd/display-serdes/serdes-bridge-split.c new file mode 100644 index 000000000000..38d94692a327 --- /dev/null +++ b/drivers/mfd/display-serdes/serdes-bridge-split.c @@ -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 + */ + +#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 "); +MODULE_DESCRIPTION("display bridge interface for different serdes"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:serdes-bridge-split"); diff --git a/drivers/mfd/display-serdes/serdes-bridge.c b/drivers/mfd/display-serdes/serdes-bridge.c index a9448e609315..6dc82a8640be 100644 --- a/drivers/mfd/display-serdes/serdes-bridge.c +++ b/drivers/mfd/display-serdes/serdes-bridge.c @@ -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) { diff --git a/drivers/mfd/display-serdes/serdes-core.c b/drivers/mfd/display-serdes/serdes-core.c index ed671eb9892c..3d9a6e675673 100644 --- a/drivers/mfd/display-serdes/serdes-core.c +++ b/drivers/mfd/display-serdes/serdes-core.c @@ -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[] = { diff --git a/drivers/mfd/display-serdes/serdes-i2c.c b/drivers/mfd/display-serdes/serdes-i2c.c index 49748d3a8322..951d40d3db50 100644 --- a/drivers/mfd/display-serdes/serdes-i2c.c +++ b/drivers/mfd/display-serdes/serdes-i2c.c @@ -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", diff --git a/drivers/mfd/display-serdes/serdes-panel-split.c b/drivers/mfd/display-serdes/serdes-panel-split.c new file mode 100644 index 000000000000..537d28233ba4 --- /dev/null +++ b/drivers/mfd/display-serdes/serdes-panel-split.c @@ -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 + */ + +#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 "); +MODULE_DESCRIPTION("display panel interface for different serdes"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:serdes-panel-split"); diff --git a/drivers/mfd/display-serdes/serdes-panel.c b/drivers/mfd/display-serdes/serdes-panel.c index 2a8b9357c227..2793220cc691 100644 --- a/drivers/mfd/display-serdes/serdes-panel.c +++ b/drivers/mfd/display-serdes/serdes-panel.c @@ -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; }