From: Heiko Stuebner <heiko.stuebner@cherry.de>
This adds the glue code for the MIPI DSI2 bridge on Rockchip SoCs and
enables its use on the RK3588.
Right now the DSI2 controller is always paired with a DC-phy based on a
Samsung IP, so the interface values are set statically for now.
This stays true for the upcoming RK3576 as well.
Tested-by: Daniel Semkowicz <dse@thaumatec.com>
Tested-by: Dmitry Yashin <dmt.yashin@gmail.com>
Signed-off-by: Heiko Stuebner <heiko.stuebner@cherry.de>
---
drivers/gpu/drm/rockchip/Kconfig | 10 +
drivers/gpu/drm/rockchip/Makefile | 1 +
.../gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c | 487 ++++++++++++++++++
drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 2 +
drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 1 +
5 files changed, 501 insertions(+)
create mode 100644 drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c
diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 448fadd4ba15..adee24b4e7bc 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -10,6 +10,7 @@ config DRM_ROCKCHIP
select DRM_DW_HDMI if ROCKCHIP_DW_HDMI
select DRM_DW_HDMI_QP if ROCKCHIP_DW_HDMI_QP
select DRM_DW_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI
+ select DRM_DW_MIPI_DSI2 if ROCKCHIP_DW_MIPI_DSI2
select GENERIC_PHY if ROCKCHIP_DW_MIPI_DSI
select GENERIC_PHY_MIPI_DPHY if ROCKCHIP_DW_MIPI_DSI
select SND_SOC_HDMI_CODEC if ROCKCHIP_CDN_DP && SND_SOC
@@ -81,6 +82,15 @@ config ROCKCHIP_DW_MIPI_DSI
enable MIPI DSI on RK3288 or RK3399 based SoC, you should
select this option.
+config ROCKCHIP_DW_MIPI_DSI2
+ bool "Rockchip specific extensions for Synopsys DW MIPI DSI2"
+ select GENERIC_PHY_MIPI_DPHY
+ help
+ This selects support for Rockchip SoC specific extensions
+ for the Synopsys DesignWare DSI2 driver. If you want to
+ enable MIPI DSI on RK3576 or RK3588 based SoC, you should
+ select this option.
+
config ROCKCHIP_INNO_HDMI
bool "Rockchip specific extensions for Innosilicon HDMI"
select DRM_DISPLAY_HDMI_HELPER
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 3eab662a5a1d..2b867cebbc12 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -13,6 +13,7 @@ rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI_QP) += dw_hdmi_qp-rockchip.o
rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi-rockchip.o
+rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI2) += dw-mipi-dsi2-rockchip.o
rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o
rockchipdrm-$(CONFIG_ROCKCHIP_RGB) += rockchip_rgb.o
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c
new file mode 100644
index 000000000000..cdd490778756
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c
@@ -0,0 +1,487 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2024 Rockchip Electronics Co., Ltd.
+ * Author:
+ * Guochun Huang <hero.huang@rock-chips.com>
+ * Heiko Stuebner <heiko.stuebner@cherry.de>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/media-bus-format.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+
+#include <drm/bridge/dw_mipi_dsi2.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_of.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#include <uapi/linux/videodev2.h>
+
+#include "rockchip_drm_drv.h"
+
+#define PSEC_PER_SEC 1000000000000LL
+
+struct dsigrf_reg {
+ u16 offset;
+ u16 lsb;
+ u16 msb;
+};
+
+enum grf_reg_fields {
+ TXREQCLKHS_EN,
+ GATING_EN,
+ IPI_SHUTDN,
+ IPI_COLORM,
+ IPI_COLOR_DEPTH,
+ IPI_FORMAT,
+ MAX_FIELDS,
+};
+
+#define IPI_DEPTH_5_6_5_BITS 0x02
+#define IPI_DEPTH_6_BITS 0x03
+#define IPI_DEPTH_8_BITS 0x05
+#define IPI_DEPTH_10_BITS 0x06
+
+struct rockchip_dw_dsi2_chip_data {
+ u32 reg;
+ const struct dsigrf_reg *grf_regs;
+ unsigned long long max_bit_rate_per_lane;
+};
+
+struct dw_mipi_dsi2_rockchip {
+ struct device *dev;
+ struct rockchip_encoder encoder;
+ struct regmap *regmap;
+
+ unsigned int lane_mbps; /* per lane */
+ u32 format;
+
+ struct regmap *grf_regmap;
+ struct phy *phy;
+ union phy_configure_opts phy_opts;
+
+ struct dw_mipi_dsi2 *dmd;
+ struct dw_mipi_dsi2_plat_data pdata;
+ const struct rockchip_dw_dsi2_chip_data *cdata;
+};
+
+static inline struct dw_mipi_dsi2_rockchip *to_dsi2(struct drm_encoder *encoder)
+{
+ struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+ return container_of(rkencoder, struct dw_mipi_dsi2_rockchip, encoder);
+}
+
+static void grf_field_write(struct dw_mipi_dsi2_rockchip *dsi2, enum grf_reg_fields index,
+ unsigned int val)
+{
+ const struct dsigrf_reg *field = &dsi2->cdata->grf_regs[index];
+
+ if (!field)
+ return;
+
+ regmap_write(dsi2->grf_regmap, field->offset,
+ (val << field->lsb) | (GENMASK(field->msb, field->lsb) << 16));
+}
+
+static int dw_mipi_dsi2_phy_init(void *priv_data)
+{
+ return 0;
+}
+
+static void dw_mipi_dsi2_phy_power_on(void *priv_data)
+{
+ struct dw_mipi_dsi2_rockchip *dsi2 = priv_data;
+ int ret;
+
+ ret = phy_set_mode(dsi2->phy, PHY_MODE_MIPI_DPHY);
+ if (ret) {
+ dev_err(dsi2->dev, "Failed to set phy mode: %d\n", ret);
+ return;
+ }
+
+ phy_configure(dsi2->phy, &dsi2->phy_opts);
+ phy_power_on(dsi2->phy);
+}
+
+static void dw_mipi_dsi2_phy_power_off(void *priv_data)
+{
+ struct dw_mipi_dsi2_rockchip *dsi2 = priv_data;
+
+ phy_power_off(dsi2->phy);
+}
+
+static int
+dw_mipi_dsi2_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode,
+ unsigned long mode_flags, u32 lanes, u32 format,
+ unsigned int *lane_mbps)
+{
+ struct dw_mipi_dsi2_rockchip *dsi2 = priv_data;
+ u64 max_lane_rate, target_phyclk;
+ unsigned int lane_rate_kbps;
+ int bpp;
+
+ max_lane_rate = dsi2->cdata->max_bit_rate_per_lane;
+
+ dsi2->format = format;
+ bpp = mipi_dsi_pixel_format_to_bpp(format);
+ if (bpp < 0) {
+ dev_err(dsi2->dev, "failed to get bpp for pixel format %d\n", format);
+ return bpp;
+ }
+
+ lane_rate_kbps = mode->clock * bpp / lanes;
+
+ /*
+ * Set BW a little larger only in video burst mode in
+ * consideration of the protocol overhead and HS mode
+ * switching to BLLP mode, take 1 / 0.9, since Mbps must
+ * big than bandwidth of RGB
+ */
+ if (mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+ lane_rate_kbps = (lane_rate_kbps * 10) / 9;
+
+ if (lane_rate_kbps > max_lane_rate) {
+ dev_err(dsi2->dev, "DPHY clock frequency is out of range\n");
+ return -ERANGE;
+ }
+
+ dsi2->lane_mbps = lane_rate_kbps / 1000;
+ *lane_mbps = dsi2->lane_mbps;
+
+ if (dsi2->phy) {
+ target_phyclk = DIV_ROUND_CLOSEST_ULL(lane_rate_kbps * lanes * 1000, bpp);
+ phy_mipi_dphy_get_default_config(target_phyclk, bpp, lanes,
+ &dsi2->phy_opts.mipi_dphy);
+ }
+
+ return 0;
+}
+
+static void dw_mipi_dsi2_phy_get_iface(void *priv_data, struct dw_mipi_dsi2_phy_iface *iface)
+{
+ /* PPI width is fixed to 16 bits in DCPHY */
+ iface->ppi_width = 16;
+ iface->phy_type = DW_MIPI_DSI2_DPHY;
+}
+
+static int
+dw_mipi_dsi2_phy_get_timing(void *priv_data, unsigned int lane_mbps,
+ struct dw_mipi_dsi2_phy_timing *timing)
+{
+ struct dw_mipi_dsi2_rockchip *dsi2 = priv_data;
+ struct phy_configure_opts_mipi_dphy *cfg = &dsi2->phy_opts.mipi_dphy;
+ unsigned long long tmp, ui;
+ unsigned long long hstx_clk;
+
+ hstx_clk = DIV_ROUND_CLOSEST_ULL(dsi2->lane_mbps * USEC_PER_SEC, 16);
+
+ ui = ALIGN(PSEC_PER_SEC, hstx_clk);
+ do_div(ui, hstx_clk);
+
+ /* PHY_LP2HS_TIME = (TLPX + THS-PREPARE + THS-ZERO) / Tphy_hstx_clk */
+ tmp = cfg->lpx + cfg->hs_prepare + cfg->hs_zero;
+ tmp = DIV_ROUND_CLOSEST_ULL(tmp << 16, ui);
+ timing->data_lp2hs = tmp;
+
+ /* PHY_HS2LP_TIME = (THS-TRAIL + THS-EXIT) / Tphy_hstx_clk */
+ tmp = cfg->hs_trail + cfg->hs_exit;
+ tmp = DIV_ROUND_CLOSEST_ULL(tmp << 16, ui);
+ timing->data_hs2lp = tmp;
+
+ return 0;
+}
+
+static const struct dw_mipi_dsi2_phy_ops dw_mipi_dsi2_rockchip_phy_ops = {
+ .init = dw_mipi_dsi2_phy_init,
+ .power_on = dw_mipi_dsi2_phy_power_on,
+ .power_off = dw_mipi_dsi2_phy_power_off,
+ .get_interface = dw_mipi_dsi2_phy_get_iface,
+ .get_lane_mbps = dw_mipi_dsi2_get_lane_mbps,
+ .get_timing = dw_mipi_dsi2_phy_get_timing,
+};
+
+static void dw_mipi_dsi2_encoder_atomic_enable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
+{
+ struct dw_mipi_dsi2_rockchip *dsi2 = to_dsi2(encoder);
+ u32 color_depth;
+
+ switch (dsi2->format) {
+ case MIPI_DSI_FMT_RGB666:
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ color_depth = IPI_DEPTH_6_BITS;
+ break;
+ case MIPI_DSI_FMT_RGB565:
+ color_depth = IPI_DEPTH_5_6_5_BITS;
+ break;
+ case MIPI_DSI_FMT_RGB888:
+ color_depth = IPI_DEPTH_8_BITS;
+ break;
+ default:
+ /* Should've been caught by atomic_check */
+ WARN_ON(1);
+ return;
+ }
+
+ grf_field_write(dsi2, IPI_COLOR_DEPTH, color_depth);
+}
+
+static int
+dw_mipi_dsi2_encoder_atomic_check(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+ struct dw_mipi_dsi2_rockchip *dsi2 = to_dsi2(encoder);
+ struct drm_connector *connector = conn_state->connector;
+ struct drm_display_info *info = &connector->display_info;
+
+ switch (dsi2->format) {
+ case MIPI_DSI_FMT_RGB666:
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ s->output_mode = ROCKCHIP_OUT_MODE_P666;
+ break;
+ case MIPI_DSI_FMT_RGB565:
+ s->output_mode = ROCKCHIP_OUT_MODE_P565;
+ break;
+ case MIPI_DSI_FMT_RGB888:
+ s->output_mode = ROCKCHIP_OUT_MODE_P888;
+ break;
+ default:
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ if (info->num_bus_formats)
+ s->bus_format = info->bus_formats[0];
+ else
+ s->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+
+ s->output_type = DRM_MODE_CONNECTOR_DSI;
+ s->bus_flags = info->bus_flags;
+ s->color_space = V4L2_COLORSPACE_DEFAULT;
+
+ return 0;
+}
+
+static const struct drm_encoder_helper_funcs
+dw_mipi_dsi2_encoder_helper_funcs = {
+ .atomic_enable = dw_mipi_dsi2_encoder_atomic_enable,
+ .atomic_check = dw_mipi_dsi2_encoder_atomic_check,
+};
+
+static int rockchip_dsi2_drm_create_encoder(struct dw_mipi_dsi2_rockchip *dsi2,
+ struct drm_device *drm_dev)
+{
+ struct drm_encoder *encoder = &dsi2->encoder.encoder;
+ int ret;
+
+ encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+ dsi2->dev->of_node);
+
+ ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_DSI);
+ if (ret) {
+ dev_err(dsi2->dev, "Failed to initialize encoder with drm\n");
+ return ret;
+ }
+
+ drm_encoder_helper_add(encoder, &dw_mipi_dsi2_encoder_helper_funcs);
+
+ return 0;
+}
+
+static int dw_mipi_dsi2_rockchip_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct dw_mipi_dsi2_rockchip *dsi2 = dev_get_drvdata(dev);
+ struct drm_device *drm_dev = data;
+ int ret;
+
+ ret = rockchip_dsi2_drm_create_encoder(dsi2, drm_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to create drm encoder\n");
+
+ rockchip_drm_encoder_set_crtc_endpoint_id(&dsi2->encoder,
+ dev->of_node, 0, 0);
+
+ ret = dw_mipi_dsi2_bind(dsi2->dmd, &dsi2->encoder.encoder);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to bind\n");
+
+ return 0;
+}
+
+static void dw_mipi_dsi2_rockchip_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct dw_mipi_dsi2_rockchip *dsi2 = dev_get_drvdata(dev);
+
+ dw_mipi_dsi2_unbind(dsi2->dmd);
+}
+
+static const struct component_ops dw_mipi_dsi2_rockchip_ops = {
+ .bind = dw_mipi_dsi2_rockchip_bind,
+ .unbind = dw_mipi_dsi2_rockchip_unbind,
+};
+
+static int dw_mipi_dsi2_rockchip_host_attach(void *priv_data,
+ struct mipi_dsi_device *device)
+{
+ struct dw_mipi_dsi2_rockchip *dsi2 = priv_data;
+ int ret;
+
+ ret = component_add(dsi2->dev, &dw_mipi_dsi2_rockchip_ops);
+ if (ret)
+ return dev_err_probe(dsi2->dev, ret, "Failed to register component\n");
+
+ return 0;
+}
+
+static int dw_mipi_dsi2_rockchip_host_detach(void *priv_data,
+ struct mipi_dsi_device *device)
+{
+ struct dw_mipi_dsi2_rockchip *dsi2 = priv_data;
+
+ component_del(dsi2->dev, &dw_mipi_dsi2_rockchip_ops);
+
+ return 0;
+}
+
+static const struct dw_mipi_dsi2_host_ops dw_mipi_dsi2_rockchip_host_ops = {
+ .attach = dw_mipi_dsi2_rockchip_host_attach,
+ .detach = dw_mipi_dsi2_rockchip_host_detach,
+};
+
+static const struct regmap_config dw_mipi_dsi2_rockchip_regmap_config = {
+ .name = "dsi2-host",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .fast_io = true,
+};
+
+static int dw_mipi_dsi2_rockchip_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ const struct rockchip_dw_dsi2_chip_data *cdata =
+ of_device_get_match_data(dev);
+ struct dw_mipi_dsi2_rockchip *dsi2;
+ struct resource *res;
+ void __iomem *base;
+ int i;
+
+ dsi2 = devm_kzalloc(dev, sizeof(*dsi2), GFP_KERNEL);
+ if (!dsi2)
+ return -ENOMEM;
+
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(base))
+ return dev_err_probe(dev, PTR_ERR(base), "Unable to get dsi registers\n");
+
+ dsi2->regmap = devm_regmap_init_mmio(dev, base, &dw_mipi_dsi2_rockchip_regmap_config);
+ if (IS_ERR(dsi2->regmap))
+ return dev_err_probe(dev, PTR_ERR(dsi2->regmap), "failed to init register map\n");
+
+ i = 0;
+ while (cdata[i].reg) {
+ if (cdata[i].reg == res->start) {
+ dsi2->cdata = &cdata[i];
+ break;
+ }
+
+ i++;
+ }
+
+ if (!dsi2->cdata)
+ return dev_err_probe(dev, -EINVAL, "No dsi-config for %s node\n", np->name);
+
+ dsi2->grf_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
+ if (IS_ERR(dsi2->grf_regmap))
+ return dev_err_probe(dsi2->dev, PTR_ERR(dsi2->grf_regmap), "Unable to get grf\n");
+
+ dsi2->phy = devm_phy_optional_get(dev, "dcphy");
+ if (IS_ERR(dsi2->phy))
+ return dev_err_probe(dev, PTR_ERR(dsi2->phy), "failed to get mipi phy\n");
+
+ dsi2->dev = dev;
+ dsi2->pdata.regmap = dsi2->regmap;
+ dsi2->pdata.max_data_lanes = 4;
+ dsi2->pdata.phy_ops = &dw_mipi_dsi2_rockchip_phy_ops;
+ dsi2->pdata.host_ops = &dw_mipi_dsi2_rockchip_host_ops;
+ dsi2->pdata.priv_data = dsi2;
+ platform_set_drvdata(pdev, dsi2);
+
+ dsi2->dmd = dw_mipi_dsi2_probe(pdev, &dsi2->pdata);
+ if (IS_ERR(dsi2->dmd))
+ return dev_err_probe(dev, PTR_ERR(dsi2->dmd), "Failed to probe dw_mipi_dsi2\n");
+
+ return 0;
+}
+
+static void dw_mipi_dsi2_rockchip_remove(struct platform_device *pdev)
+{
+ struct dw_mipi_dsi2_rockchip *dsi2 = platform_get_drvdata(pdev);
+
+ dw_mipi_dsi2_remove(dsi2->dmd);
+}
+
+static const struct dsigrf_reg rk3588_dsi0_grf_reg_fields[MAX_FIELDS] = {
+ [TXREQCLKHS_EN] = { 0x0000, 11, 11 },
+ [GATING_EN] = { 0x0000, 10, 10 },
+ [IPI_SHUTDN] = { 0x0000, 9, 9 },
+ [IPI_COLORM] = { 0x0000, 8, 8 },
+ [IPI_COLOR_DEPTH] = { 0x0000, 4, 7 },
+ [IPI_FORMAT] = { 0x0000, 0, 3 },
+};
+
+static const struct dsigrf_reg rk3588_dsi1_grf_reg_fields[MAX_FIELDS] = {
+ [TXREQCLKHS_EN] = { 0x0004, 11, 11 },
+ [GATING_EN] = { 0x0004, 10, 10 },
+ [IPI_SHUTDN] = { 0x0004, 9, 9 },
+ [IPI_COLORM] = { 0x0004, 8, 8 },
+ [IPI_COLOR_DEPTH] = { 0x0004, 4, 7 },
+ [IPI_FORMAT] = { 0x0004, 0, 3 },
+};
+
+static const struct rockchip_dw_dsi2_chip_data rk3588_chip_data[] = {
+ {
+ .reg = 0xfde20000,
+ .grf_regs = rk3588_dsi0_grf_reg_fields,
+ .max_bit_rate_per_lane = 4500000ULL,
+ },
+ {
+ .reg = 0xfde30000,
+ .grf_regs = rk3588_dsi1_grf_reg_fields,
+ .max_bit_rate_per_lane = 4500000ULL,
+ }
+};
+
+static const struct of_device_id dw_mipi_dsi2_rockchip_dt_ids[] = {
+ {
+ .compatible = "rockchip,rk3588-mipi-dsi2",
+ .data = &rk3588_chip_data,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, dw_mipi_dsi2_rockchip_dt_ids);
+
+struct platform_driver dw_mipi_dsi2_rockchip_driver = {
+ .probe = dw_mipi_dsi2_rockchip_probe,
+ .remove = dw_mipi_dsi2_rockchip_remove,
+ .driver = {
+ .of_match_table = dw_mipi_dsi2_rockchip_dt_ids,
+ .name = "dw-mipi-dsi2",
+ },
+};
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index ddf0be331c0a..5327ce035003 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -511,6 +511,8 @@ static int __init rockchip_drm_init(void)
CONFIG_ROCKCHIP_DW_HDMI_QP);
ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver,
CONFIG_ROCKCHIP_DW_MIPI_DSI);
+ ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi2_rockchip_driver,
+ CONFIG_ROCKCHIP_DW_MIPI_DSI2);
ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI);
ADD_ROCKCHIP_SUB_DRIVER(rk3066_hdmi_driver,
CONFIG_ROCKCHIP_RK3066_HDMI);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index 24b4ce5ceaf1..9c9d38a06cdf 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -90,6 +90,7 @@ extern struct platform_driver cdn_dp_driver;
extern struct platform_driver dw_hdmi_rockchip_pltfm_driver;
extern struct platform_driver dw_hdmi_qp_rockchip_pltfm_driver;
extern struct platform_driver dw_mipi_dsi_rockchip_driver;
+extern struct platform_driver dw_mipi_dsi2_rockchip_driver;
extern struct platform_driver inno_hdmi_driver;
extern struct platform_driver rockchip_dp_driver;
extern struct platform_driver rockchip_lvds_driver;
--
2.45.2
Hello Heiko, On 12/10/24 07:10, Heiko Stuebner wrote: > From: Heiko Stuebner <heiko.stuebner@cherry.de> > > This adds the glue code for the MIPI DSI2 bridge on Rockchip SoCs and > enables its use on the RK3588. > > Right now the DSI2 controller is always paired with a DC-phy based on a > Samsung IP, so the interface values are set statically for now. > This stays true for the upcoming RK3576 as well. > > Tested-by: Daniel Semkowicz <dse@thaumatec.com> > Tested-by: Dmitry Yashin <dmt.yashin@gmail.com> > Signed-off-by: Heiko Stuebner <heiko.stuebner@cherry.de> Reviewed-by: Andy Yan <andy.yan@rock-chips.com> > --- > drivers/gpu/drm/rockchip/Kconfig | 10 + > drivers/gpu/drm/rockchip/Makefile | 1 + > .../gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c | 487 ++++++++++++++++++ > drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 2 + > drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 1 + > 5 files changed, 501 insertions(+) > create mode 100644 drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c > > diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig > index 448fadd4ba15..adee24b4e7bc 100644 > --- a/drivers/gpu/drm/rockchip/Kconfig > +++ b/drivers/gpu/drm/rockchip/Kconfig > @@ -10,6 +10,7 @@ config DRM_ROCKCHIP > select DRM_DW_HDMI if ROCKCHIP_DW_HDMI > select DRM_DW_HDMI_QP if ROCKCHIP_DW_HDMI_QP > select DRM_DW_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI > + select DRM_DW_MIPI_DSI2 if ROCKCHIP_DW_MIPI_DSI2 > select GENERIC_PHY if ROCKCHIP_DW_MIPI_DSI > select GENERIC_PHY_MIPI_DPHY if ROCKCHIP_DW_MIPI_DSI > select SND_SOC_HDMI_CODEC if ROCKCHIP_CDN_DP && SND_SOC > @@ -81,6 +82,15 @@ config ROCKCHIP_DW_MIPI_DSI > enable MIPI DSI on RK3288 or RK3399 based SoC, you should > select this option. > > +config ROCKCHIP_DW_MIPI_DSI2 > + bool "Rockchip specific extensions for Synopsys DW MIPI DSI2" > + select GENERIC_PHY_MIPI_DPHY > + help > + This selects support for Rockchip SoC specific extensions > + for the Synopsys DesignWare DSI2 driver. If you want to > + enable MIPI DSI on RK3576 or RK3588 based SoC, you should > + select this option. > + > config ROCKCHIP_INNO_HDMI > bool "Rockchip specific extensions for Innosilicon HDMI" > select DRM_DISPLAY_HDMI_HELPER > diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile > index 3eab662a5a1d..2b867cebbc12 100644 > --- a/drivers/gpu/drm/rockchip/Makefile > +++ b/drivers/gpu/drm/rockchip/Makefile > @@ -13,6 +13,7 @@ rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o > rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o > rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI_QP) += dw_hdmi_qp-rockchip.o > rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi-rockchip.o > +rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI2) += dw-mipi-dsi2-rockchip.o > rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o > rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o > rockchipdrm-$(CONFIG_ROCKCHIP_RGB) += rockchip_rgb.o > diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c > new file mode 100644 > index 000000000000..cdd490778756 > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c > @@ -0,0 +1,487 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright (C) 2024 Rockchip Electronics Co., Ltd. > + * Author: > + * Guochun Huang <hero.huang@rock-chips.com> > + * Heiko Stuebner <heiko.stuebner@cherry.de> > + */ > + > +#include <linux/bitfield.h> > +#include <linux/clk.h> > +#include <linux/component.h> > +#include <linux/media-bus-format.h> > +#include <linux/mod_devicetable.h> > +#include <linux/module.h> > +#include <linux/of.h> > +#include <linux/pm_runtime.h> > +#include <linux/platform_device.h> > +#include <linux/regmap.h> > +#include <linux/reset.h> > +#include <linux/mfd/syscon.h> > +#include <linux/phy/phy.h> > + > +#include <drm/bridge/dw_mipi_dsi2.h> > +#include <drm/drm_mipi_dsi.h> > +#include <drm/drm_of.h> > +#include <drm/drm_simple_kms_helper.h> > + > +#include <uapi/linux/videodev2.h> > + > +#include "rockchip_drm_drv.h" > + > +#define PSEC_PER_SEC 1000000000000LL > + > +struct dsigrf_reg { > + u16 offset; > + u16 lsb; > + u16 msb; > +}; > + > +enum grf_reg_fields { > + TXREQCLKHS_EN, > + GATING_EN, > + IPI_SHUTDN, > + IPI_COLORM, > + IPI_COLOR_DEPTH, > + IPI_FORMAT, > + MAX_FIELDS, > +}; > + > +#define IPI_DEPTH_5_6_5_BITS 0x02 > +#define IPI_DEPTH_6_BITS 0x03 > +#define IPI_DEPTH_8_BITS 0x05 > +#define IPI_DEPTH_10_BITS 0x06 > + > +struct rockchip_dw_dsi2_chip_data { > + u32 reg; > + const struct dsigrf_reg *grf_regs; > + unsigned long long max_bit_rate_per_lane; > +}; > + > +struct dw_mipi_dsi2_rockchip { > + struct device *dev; > + struct rockchip_encoder encoder; > + struct regmap *regmap; > + > + unsigned int lane_mbps; /* per lane */ > + u32 format; > + > + struct regmap *grf_regmap; > + struct phy *phy; > + union phy_configure_opts phy_opts; > + > + struct dw_mipi_dsi2 *dmd; > + struct dw_mipi_dsi2_plat_data pdata; > + const struct rockchip_dw_dsi2_chip_data *cdata; > +}; > + > +static inline struct dw_mipi_dsi2_rockchip *to_dsi2(struct drm_encoder *encoder) > +{ > + struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); > + > + return container_of(rkencoder, struct dw_mipi_dsi2_rockchip, encoder); > +} > + > +static void grf_field_write(struct dw_mipi_dsi2_rockchip *dsi2, enum grf_reg_fields index, > + unsigned int val) > +{ > + const struct dsigrf_reg *field = &dsi2->cdata->grf_regs[index]; > + > + if (!field) > + return; > + > + regmap_write(dsi2->grf_regmap, field->offset, > + (val << field->lsb) | (GENMASK(field->msb, field->lsb) << 16)); > +} > + > +static int dw_mipi_dsi2_phy_init(void *priv_data) > +{ > + return 0; > +} > + > +static void dw_mipi_dsi2_phy_power_on(void *priv_data) > +{ > + struct dw_mipi_dsi2_rockchip *dsi2 = priv_data; > + int ret; > + > + ret = phy_set_mode(dsi2->phy, PHY_MODE_MIPI_DPHY); > + if (ret) { > + dev_err(dsi2->dev, "Failed to set phy mode: %d\n", ret); > + return; > + } > + > + phy_configure(dsi2->phy, &dsi2->phy_opts); > + phy_power_on(dsi2->phy); > +} > + > +static void dw_mipi_dsi2_phy_power_off(void *priv_data) > +{ > + struct dw_mipi_dsi2_rockchip *dsi2 = priv_data; > + > + phy_power_off(dsi2->phy); > +} > + > +static int > +dw_mipi_dsi2_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode, > + unsigned long mode_flags, u32 lanes, u32 format, > + unsigned int *lane_mbps) > +{ > + struct dw_mipi_dsi2_rockchip *dsi2 = priv_data; > + u64 max_lane_rate, target_phyclk; > + unsigned int lane_rate_kbps; > + int bpp; > + > + max_lane_rate = dsi2->cdata->max_bit_rate_per_lane; > + > + dsi2->format = format; > + bpp = mipi_dsi_pixel_format_to_bpp(format); > + if (bpp < 0) { > + dev_err(dsi2->dev, "failed to get bpp for pixel format %d\n", format); > + return bpp; > + } > + > + lane_rate_kbps = mode->clock * bpp / lanes; > + > + /* > + * Set BW a little larger only in video burst mode in > + * consideration of the protocol overhead and HS mode > + * switching to BLLP mode, take 1 / 0.9, since Mbps must > + * big than bandwidth of RGB > + */ > + if (mode_flags & MIPI_DSI_MODE_VIDEO_BURST) > + lane_rate_kbps = (lane_rate_kbps * 10) / 9; > + > + if (lane_rate_kbps > max_lane_rate) { > + dev_err(dsi2->dev, "DPHY clock frequency is out of range\n"); > + return -ERANGE; > + } > + > + dsi2->lane_mbps = lane_rate_kbps / 1000; > + *lane_mbps = dsi2->lane_mbps; > + > + if (dsi2->phy) { > + target_phyclk = DIV_ROUND_CLOSEST_ULL(lane_rate_kbps * lanes * 1000, bpp); > + phy_mipi_dphy_get_default_config(target_phyclk, bpp, lanes, > + &dsi2->phy_opts.mipi_dphy); > + } > + > + return 0; > +} > + > +static void dw_mipi_dsi2_phy_get_iface(void *priv_data, struct dw_mipi_dsi2_phy_iface *iface) > +{ > + /* PPI width is fixed to 16 bits in DCPHY */ > + iface->ppi_width = 16; > + iface->phy_type = DW_MIPI_DSI2_DPHY; > +} > + > +static int > +dw_mipi_dsi2_phy_get_timing(void *priv_data, unsigned int lane_mbps, > + struct dw_mipi_dsi2_phy_timing *timing) > +{ > + struct dw_mipi_dsi2_rockchip *dsi2 = priv_data; > + struct phy_configure_opts_mipi_dphy *cfg = &dsi2->phy_opts.mipi_dphy; > + unsigned long long tmp, ui; > + unsigned long long hstx_clk; > + > + hstx_clk = DIV_ROUND_CLOSEST_ULL(dsi2->lane_mbps * USEC_PER_SEC, 16); > + > + ui = ALIGN(PSEC_PER_SEC, hstx_clk); > + do_div(ui, hstx_clk); > + > + /* PHY_LP2HS_TIME = (TLPX + THS-PREPARE + THS-ZERO) / Tphy_hstx_clk */ > + tmp = cfg->lpx + cfg->hs_prepare + cfg->hs_zero; > + tmp = DIV_ROUND_CLOSEST_ULL(tmp << 16, ui); > + timing->data_lp2hs = tmp; > + > + /* PHY_HS2LP_TIME = (THS-TRAIL + THS-EXIT) / Tphy_hstx_clk */ > + tmp = cfg->hs_trail + cfg->hs_exit; > + tmp = DIV_ROUND_CLOSEST_ULL(tmp << 16, ui); > + timing->data_hs2lp = tmp; > + > + return 0; > +} > + > +static const struct dw_mipi_dsi2_phy_ops dw_mipi_dsi2_rockchip_phy_ops = { > + .init = dw_mipi_dsi2_phy_init, > + .power_on = dw_mipi_dsi2_phy_power_on, > + .power_off = dw_mipi_dsi2_phy_power_off, > + .get_interface = dw_mipi_dsi2_phy_get_iface, > + .get_lane_mbps = dw_mipi_dsi2_get_lane_mbps, > + .get_timing = dw_mipi_dsi2_phy_get_timing, > +}; > + > +static void dw_mipi_dsi2_encoder_atomic_enable(struct drm_encoder *encoder, > + struct drm_atomic_state *state) > +{ > + struct dw_mipi_dsi2_rockchip *dsi2 = to_dsi2(encoder); > + u32 color_depth; > + > + switch (dsi2->format) { > + case MIPI_DSI_FMT_RGB666: > + case MIPI_DSI_FMT_RGB666_PACKED: > + color_depth = IPI_DEPTH_6_BITS; > + break; > + case MIPI_DSI_FMT_RGB565: > + color_depth = IPI_DEPTH_5_6_5_BITS; > + break; > + case MIPI_DSI_FMT_RGB888: > + color_depth = IPI_DEPTH_8_BITS; > + break; > + default: > + /* Should've been caught by atomic_check */ > + WARN_ON(1); > + return; > + } > + > + grf_field_write(dsi2, IPI_COLOR_DEPTH, color_depth); > +} > + > +static int > +dw_mipi_dsi2_encoder_atomic_check(struct drm_encoder *encoder, > + struct drm_crtc_state *crtc_state, > + struct drm_connector_state *conn_state) > +{ > + struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); > + struct dw_mipi_dsi2_rockchip *dsi2 = to_dsi2(encoder); > + struct drm_connector *connector = conn_state->connector; > + struct drm_display_info *info = &connector->display_info; > + > + switch (dsi2->format) { > + case MIPI_DSI_FMT_RGB666: > + case MIPI_DSI_FMT_RGB666_PACKED: > + s->output_mode = ROCKCHIP_OUT_MODE_P666; > + break; > + case MIPI_DSI_FMT_RGB565: > + s->output_mode = ROCKCHIP_OUT_MODE_P565; > + break; > + case MIPI_DSI_FMT_RGB888: > + s->output_mode = ROCKCHIP_OUT_MODE_P888; > + break; > + default: > + WARN_ON(1); > + return -EINVAL; > + } > + > + if (info->num_bus_formats) > + s->bus_format = info->bus_formats[0]; > + else > + s->bus_format = MEDIA_BUS_FMT_RGB888_1X24; > + > + s->output_type = DRM_MODE_CONNECTOR_DSI; > + s->bus_flags = info->bus_flags; > + s->color_space = V4L2_COLORSPACE_DEFAULT; > + > + return 0; > +} > + > +static const struct drm_encoder_helper_funcs > +dw_mipi_dsi2_encoder_helper_funcs = { > + .atomic_enable = dw_mipi_dsi2_encoder_atomic_enable, > + .atomic_check = dw_mipi_dsi2_encoder_atomic_check, > +}; > + > +static int rockchip_dsi2_drm_create_encoder(struct dw_mipi_dsi2_rockchip *dsi2, > + struct drm_device *drm_dev) > +{ > + struct drm_encoder *encoder = &dsi2->encoder.encoder; > + int ret; > + > + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, > + dsi2->dev->of_node); > + > + ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_DSI); > + if (ret) { > + dev_err(dsi2->dev, "Failed to initialize encoder with drm\n"); > + return ret; > + } > + > + drm_encoder_helper_add(encoder, &dw_mipi_dsi2_encoder_helper_funcs); > + > + return 0; > +} > + > +static int dw_mipi_dsi2_rockchip_bind(struct device *dev, struct device *master, > + void *data) > +{ > + struct dw_mipi_dsi2_rockchip *dsi2 = dev_get_drvdata(dev); > + struct drm_device *drm_dev = data; > + int ret; > + > + ret = rockchip_dsi2_drm_create_encoder(dsi2, drm_dev); > + if (ret) > + return dev_err_probe(dev, ret, "Failed to create drm encoder\n"); > + > + rockchip_drm_encoder_set_crtc_endpoint_id(&dsi2->encoder, > + dev->of_node, 0, 0); > + > + ret = dw_mipi_dsi2_bind(dsi2->dmd, &dsi2->encoder.encoder); > + if (ret) > + return dev_err_probe(dev, ret, "Failed to bind\n"); > + > + return 0; > +} > + > +static void dw_mipi_dsi2_rockchip_unbind(struct device *dev, struct device *master, > + void *data) > +{ > + struct dw_mipi_dsi2_rockchip *dsi2 = dev_get_drvdata(dev); > + > + dw_mipi_dsi2_unbind(dsi2->dmd); > +} > + > +static const struct component_ops dw_mipi_dsi2_rockchip_ops = { > + .bind = dw_mipi_dsi2_rockchip_bind, > + .unbind = dw_mipi_dsi2_rockchip_unbind, > +}; > + > +static int dw_mipi_dsi2_rockchip_host_attach(void *priv_data, > + struct mipi_dsi_device *device) > +{ > + struct dw_mipi_dsi2_rockchip *dsi2 = priv_data; > + int ret; > + > + ret = component_add(dsi2->dev, &dw_mipi_dsi2_rockchip_ops); > + if (ret) > + return dev_err_probe(dsi2->dev, ret, "Failed to register component\n"); > + > + return 0; > +} > + > +static int dw_mipi_dsi2_rockchip_host_detach(void *priv_data, > + struct mipi_dsi_device *device) > +{ > + struct dw_mipi_dsi2_rockchip *dsi2 = priv_data; > + > + component_del(dsi2->dev, &dw_mipi_dsi2_rockchip_ops); > + > + return 0; > +} > + > +static const struct dw_mipi_dsi2_host_ops dw_mipi_dsi2_rockchip_host_ops = { > + .attach = dw_mipi_dsi2_rockchip_host_attach, > + .detach = dw_mipi_dsi2_rockchip_host_detach, > +}; > + > +static const struct regmap_config dw_mipi_dsi2_rockchip_regmap_config = { > + .name = "dsi2-host", > + .reg_bits = 32, > + .val_bits = 32, > + .reg_stride = 4, > + .fast_io = true, > +}; > + > +static int dw_mipi_dsi2_rockchip_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct device_node *np = dev->of_node; > + const struct rockchip_dw_dsi2_chip_data *cdata = > + of_device_get_match_data(dev); > + struct dw_mipi_dsi2_rockchip *dsi2; > + struct resource *res; > + void __iomem *base; > + int i; > + > + dsi2 = devm_kzalloc(dev, sizeof(*dsi2), GFP_KERNEL); > + if (!dsi2) > + return -ENOMEM; > + > + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); > + if (IS_ERR(base)) > + return dev_err_probe(dev, PTR_ERR(base), "Unable to get dsi registers\n"); > + > + dsi2->regmap = devm_regmap_init_mmio(dev, base, &dw_mipi_dsi2_rockchip_regmap_config); > + if (IS_ERR(dsi2->regmap)) > + return dev_err_probe(dev, PTR_ERR(dsi2->regmap), "failed to init register map\n"); > + > + i = 0; > + while (cdata[i].reg) { > + if (cdata[i].reg == res->start) { > + dsi2->cdata = &cdata[i]; > + break; > + } > + > + i++; > + } > + > + if (!dsi2->cdata) > + return dev_err_probe(dev, -EINVAL, "No dsi-config for %s node\n", np->name); > + > + dsi2->grf_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); > + if (IS_ERR(dsi2->grf_regmap)) > + return dev_err_probe(dsi2->dev, PTR_ERR(dsi2->grf_regmap), "Unable to get grf\n"); > + > + dsi2->phy = devm_phy_optional_get(dev, "dcphy"); > + if (IS_ERR(dsi2->phy)) > + return dev_err_probe(dev, PTR_ERR(dsi2->phy), "failed to get mipi phy\n"); > + > + dsi2->dev = dev; > + dsi2->pdata.regmap = dsi2->regmap; > + dsi2->pdata.max_data_lanes = 4; > + dsi2->pdata.phy_ops = &dw_mipi_dsi2_rockchip_phy_ops; > + dsi2->pdata.host_ops = &dw_mipi_dsi2_rockchip_host_ops; > + dsi2->pdata.priv_data = dsi2; > + platform_set_drvdata(pdev, dsi2); > + > + dsi2->dmd = dw_mipi_dsi2_probe(pdev, &dsi2->pdata); > + if (IS_ERR(dsi2->dmd)) > + return dev_err_probe(dev, PTR_ERR(dsi2->dmd), "Failed to probe dw_mipi_dsi2\n"); > + > + return 0; > +} > + > +static void dw_mipi_dsi2_rockchip_remove(struct platform_device *pdev) > +{ > + struct dw_mipi_dsi2_rockchip *dsi2 = platform_get_drvdata(pdev); > + > + dw_mipi_dsi2_remove(dsi2->dmd); > +} > + > +static const struct dsigrf_reg rk3588_dsi0_grf_reg_fields[MAX_FIELDS] = { > + [TXREQCLKHS_EN] = { 0x0000, 11, 11 }, > + [GATING_EN] = { 0x0000, 10, 10 }, > + [IPI_SHUTDN] = { 0x0000, 9, 9 }, > + [IPI_COLORM] = { 0x0000, 8, 8 }, > + [IPI_COLOR_DEPTH] = { 0x0000, 4, 7 }, > + [IPI_FORMAT] = { 0x0000, 0, 3 }, > +}; > + > +static const struct dsigrf_reg rk3588_dsi1_grf_reg_fields[MAX_FIELDS] = { > + [TXREQCLKHS_EN] = { 0x0004, 11, 11 }, > + [GATING_EN] = { 0x0004, 10, 10 }, > + [IPI_SHUTDN] = { 0x0004, 9, 9 }, > + [IPI_COLORM] = { 0x0004, 8, 8 }, > + [IPI_COLOR_DEPTH] = { 0x0004, 4, 7 }, > + [IPI_FORMAT] = { 0x0004, 0, 3 }, > +}; > + > +static const struct rockchip_dw_dsi2_chip_data rk3588_chip_data[] = { > + { > + .reg = 0xfde20000, > + .grf_regs = rk3588_dsi0_grf_reg_fields, > + .max_bit_rate_per_lane = 4500000ULL, > + }, > + { > + .reg = 0xfde30000, > + .grf_regs = rk3588_dsi1_grf_reg_fields, > + .max_bit_rate_per_lane = 4500000ULL, > + } > +}; > + > +static const struct of_device_id dw_mipi_dsi2_rockchip_dt_ids[] = { > + { > + .compatible = "rockchip,rk3588-mipi-dsi2", > + .data = &rk3588_chip_data, > + }, > + {} > +}; > +MODULE_DEVICE_TABLE(of, dw_mipi_dsi2_rockchip_dt_ids); > + > +struct platform_driver dw_mipi_dsi2_rockchip_driver = { > + .probe = dw_mipi_dsi2_rockchip_probe, > + .remove = dw_mipi_dsi2_rockchip_remove, > + .driver = { > + .of_match_table = dw_mipi_dsi2_rockchip_dt_ids, > + .name = "dw-mipi-dsi2", > + }, > +}; > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c > index ddf0be331c0a..5327ce035003 100644 > --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c > @@ -511,6 +511,8 @@ static int __init rockchip_drm_init(void) > CONFIG_ROCKCHIP_DW_HDMI_QP); > ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver, > CONFIG_ROCKCHIP_DW_MIPI_DSI); > + ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi2_rockchip_driver, > + CONFIG_ROCKCHIP_DW_MIPI_DSI2); > ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI); > ADD_ROCKCHIP_SUB_DRIVER(rk3066_hdmi_driver, > CONFIG_ROCKCHIP_RK3066_HDMI); > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h > index 24b4ce5ceaf1..9c9d38a06cdf 100644 > --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h > @@ -90,6 +90,7 @@ extern struct platform_driver cdn_dp_driver; > extern struct platform_driver dw_hdmi_rockchip_pltfm_driver; > extern struct platform_driver dw_hdmi_qp_rockchip_pltfm_driver; > extern struct platform_driver dw_mipi_dsi_rockchip_driver; > +extern struct platform_driver dw_mipi_dsi2_rockchip_driver; > extern struct platform_driver inno_hdmi_driver; > extern struct platform_driver rockchip_dp_driver; > extern struct platform_driver rockchip_lvds_driver;
© 2016 - 2024 Red Hat, Inc.