[PATCH v1 2/2] gpu: drm: bridge: cadence: Add a driver and platform ops for StarFive JH7110 SoC

From: Shengyang Chen
Date: Mon Nov 27 2023 - 06:34:59 EST


From: Keith Zhao <keith.zhao@xxxxxxxxxxxxxxxx>

In order to fit CDNS DSI module in StarFive JH7110 SoC,
The mainly modification is followed:

1.Add driver for StarFive JH7110 SoC to drive its CDNS DSI module.
2.Add platform ops in cdns-dsi.c for StarFive JH7110 SoC probing.

Signed-off-by: Keith Zhao <keith.zhao@xxxxxxxxxxxxxxxx>
---
MAINTAINERS | 8 +
drivers/gpu/drm/bridge/cadence/Kconfig | 7 +
drivers/gpu/drm/bridge/cadence/Makefile | 1 +
.../gpu/drm/bridge/cadence/cdns-dsi-core.c | 28 +-
.../gpu/drm/bridge/cadence/cdns-dsi-core.h | 19 +
.../gpu/drm/bridge/cadence/cdns-dsi-jh7110.c | 386 ++++++++++++++++++
.../gpu/drm/bridge/cadence/cdns-dsi-jh7110.h | 186 +++++++++
7 files changed, 634 insertions(+), 1 deletion(-)
create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-dsi-jh7110.c
create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-dsi-jh7110.h

diff --git a/MAINTAINERS b/MAINTAINERS
index ea790149af79..e3c8e81c7656 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6868,6 +6868,14 @@ F: Documentation/devicetree/bindings/display/solomon,ssd-common.yaml
F: Documentation/devicetree/bindings/display/solomon,ssd13*.yaml
F: drivers/gpu/drm/solomon/ssd130x*

+DRM DRIVERS FOR STARFIVE
+M: Keith Zhao <keith.zhao@xxxxxxxxxxxxxxxx>
+M: Shengyang Chen <shengyang.chen@xxxxxxxxxxxxxxxx>
+L: dri-devel@xxxxxxxxxxxxxxxxxxxxx
+S: Maintained
+T: git git://anongit.freedesktop.org/drm/drm-misc
+F: drivers/gpu/drm/bridge/cadence/cdns-dsi-jh7110*
+
DRM DRIVER FOR ST-ERICSSON MCDE
M: Linus Walleij <linus.walleij@xxxxxxxxxx>
S: Maintained
diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig b/drivers/gpu/drm/bridge/cadence/Kconfig
index cced81633ddc..ad9f572f4720 100644
--- a/drivers/gpu/drm/bridge/cadence/Kconfig
+++ b/drivers/gpu/drm/bridge/cadence/Kconfig
@@ -19,6 +19,13 @@ config DRM_CDNS_DSI_J721E
help
Support J721E Cadence DSI wrapper. The wrapper manages
the routing of the DSS DPI signal to the Cadence DSI.
+
+config DRM_CDNS_DSI_JH7110
+ bool "JH7110 SOC Cadence DSI support"
+ default n
+ help
+ Cadence DPI to DSI bridge which is embedded in the
+ StarFive JH7110 SoC.
endif

config DRM_CDNS_MHDP8546
diff --git a/drivers/gpu/drm/bridge/cadence/Makefile b/drivers/gpu/drm/bridge/cadence/Makefile
index c95fd5b81d13..87f603a9f4ad 100644
--- a/drivers/gpu/drm/bridge/cadence/Makefile
+++ b/drivers/gpu/drm/bridge/cadence/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o
cdns-dsi-y := cdns-dsi-core.o
cdns-dsi-$(CONFIG_DRM_CDNS_DSI_J721E) += cdns-dsi-j721e.o
+cdns-dsi-$(CONFIG_DRM_CDNS_DSI_JH7110) += cdns-dsi-jh7110.o
obj-$(CONFIG_DRM_CDNS_MHDP8546) += cdns-mhdp8546.o
cdns-mhdp8546-y := cdns-mhdp8546-core.o cdns-mhdp8546-hdcp.o
cdns-mhdp8546-$(CONFIG_DRM_CDNS_MHDP8546_J721E) += cdns-mhdp8546-j721e.o
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c
index 7457d38622b0..114385890dd1 100644
--- a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c
+++ b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c
@@ -27,6 +27,10 @@
#include "cdns-dsi-j721e.h"
#endif

+#ifdef CONFIG_DRM_CDNS_DSI_JH7110
+#include "cdns-dsi-jh7110.h"
+#endif
+
#define IP_CONF 0x0
#define SP_HS_FIFO_DEPTH(x) (((x) & GENMASK(30, 26)) >> 26)
#define SP_LP_FIFO_DEPTH(x) (((x) & GENMASK(25, 21)) >> 21)
@@ -586,6 +590,9 @@ static int cdns_dsi_check_conf(struct cdns_dsi *dsi,
if (ret)
return ret;

+ if (dsi->platform_ops && dsi->platform_ops->update)
+ dsi->platform_ops->update(dsi, dsi_cfg, mode);
+
dsi_hss_hsa_hse_hbp = dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD;
if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
dsi_hss_hsa_hse_hbp += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD;
@@ -683,7 +690,7 @@ static void cdns_dsi_bridge_post_disable(struct drm_bridge *bridge)
pm_runtime_put(dsi->base.dev);
}

-static void cdns_dsi_hs_init(struct cdns_dsi *dsi)
+void cdns_dsi_hs_init(struct cdns_dsi *dsi)
{
struct cdns_dsi_output *output = &dsi->output;
u32 status;
@@ -1026,6 +1033,14 @@ static ssize_t cdns_dsi_transfer(struct mipi_dsi_host *host,

cdns_dsi_init_link(dsi);

+ /*
+ * on Jh7110 soc , when transfer dsi command ,
+ * cdns_dsi_hs_init is needed.
+ * or the final ret will be error value.
+ */
+ if (dsi->platform_ops && dsi->platform_ops->transfer)
+ dsi->platform_ops->transfer(dsi);
+
ret = mipi_dsi_create_packet(&packet, msg);
if (ret)
goto out;
@@ -1142,6 +1157,9 @@ static int __maybe_unused cdns_dsi_resume(struct device *dev)
clk_prepare_enable(dsi->dsi_p_clk);
clk_prepare_enable(dsi->dsi_sys_clk);

+ if (dsi->platform_ops && dsi->platform_ops->resume)
+ dsi->platform_ops->resume(dsi);
+
return 0;
}

@@ -1152,6 +1170,10 @@ static int __maybe_unused cdns_dsi_suspend(struct device *dev)
clk_disable_unprepare(dsi->dsi_sys_clk);
clk_disable_unprepare(dsi->dsi_p_clk);
reset_control_assert(dsi->dsi_p_rst);
+
+ if (dsi->platform_ops && dsi->platform_ops->suspend)
+ dsi->platform_ops->suspend(dsi);
+
dsi->link_initialized = false;
return 0;
}
@@ -1294,6 +1316,10 @@ static const struct of_device_id cdns_dsi_of_match[] = {
#ifdef CONFIG_DRM_CDNS_DSI_J721E
{ .compatible = "ti,j721e-dsi", .data = &dsi_ti_j721e_ops, },
#endif
+#ifdef CONFIG_DRM_CDNS_DSI_JH7110
+ { .compatible = "starfive,cdns-dsi", .data = &dsi_ti_jh7110_ops, },
+#endif
+
{ },
};
MODULE_DEVICE_TABLE(of, cdns_dsi_of_match);
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.h b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.h
index ca7ea2da635c..9067703f1e49 100644
--- a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.h
+++ b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.h
@@ -53,12 +53,20 @@ struct cdns_dsi;
* @deinit: Called in the CDNS DSI remove
* @enable: Called at the beginning of CDNS DSI bridge enable
* @disable: Called at the end of CDNS DSI bridge disable
+ * @resume: Called at the resume of CDNS DSI
+ * @suspend: Called at the suspend of CDNS DSI
+ * @update: Called at the middle of CDNS DSI bridge enable
*/
struct cdns_dsi_platform_ops {
int (*init)(struct cdns_dsi *dsi);
void (*deinit)(struct cdns_dsi *dsi);
void (*enable)(struct cdns_dsi *dsi);
void (*disable)(struct cdns_dsi *dsi);
+ void (*resume)(struct cdns_dsi *dsi);
+ void (*suspend)(struct cdns_dsi *dsi);
+ void (*update)(struct cdns_dsi *dsi, struct cdns_dsi_cfg *dsi_cfg,
+ const struct drm_display_mode *mode);
+ void (*transfer)(struct cdns_dsi *dsi);
};

struct cdns_dsi {
@@ -79,6 +87,17 @@ struct cdns_dsi {
bool link_initialized;
bool phy_initialized;
struct phy *dphy;
+
+#ifdef CONFIG_DRM_CDNS_DSI_JH7110
+ struct clk *apb_clk;
+ struct clk *txesc_clk;
+ struct reset_control *dpi_rst;
+ struct reset_control *apb_rst;
+ struct reset_control *txesc_rst;
+ struct reset_control *txbytehs_rst;
+#endif
};

+void cdns_dsi_hs_init(struct cdns_dsi *dsi);
+
#endif /* !__CDNS_DSI_H__ */
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dsi-jh7110.c b/drivers/gpu/drm/bridge/cadence/cdns-dsi-jh7110.c
new file mode 100644
index 000000000000..db082eefdf18
--- /dev/null
+++ b/drivers/gpu/drm/bridge/cadence/cdns-dsi-jh7110.c
@@ -0,0 +1,386 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * jh7110 soc Cadence DSI wrapper
+ *
+ * Copyright (C) 2023 StarFive Technology Co., Ltd.
+ */
+
+#include <linux/io.h>
+
+#include "cdns-dsi-jh7110.h"
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <linux/iopoll.h>
+
+static int cdns_dsi_clock_enable(struct cdns_dsi *dsi, struct device *dev)
+{
+ int ret;
+
+ ret = clk_prepare_enable(dsi->apb_clk);
+ if (ret) {
+ dev_err(dev, "failed to prepare/enable apb_clk\n");
+ return ret;
+ }
+ ret = clk_prepare_enable(dsi->txesc_clk);
+ if (ret) {
+ dev_err(dev, "failed to prepare/enable txesc_clk\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static void cdns_dsi_clock_disable(struct cdns_dsi *dsi)
+{
+ clk_disable_unprepare(dsi->apb_clk);
+ clk_disable_unprepare(dsi->txesc_clk);
+}
+
+static int cdns_dsi_resets_deassert(struct cdns_dsi *dsi, struct device *dev)
+{
+ int ret;
+
+ ret = reset_control_deassert(dsi->apb_rst);
+ if (ret < 0) {
+ dev_err(dev, "failed to deassert apb_rst\n");
+ return ret;
+ }
+
+ ret = reset_control_deassert(dsi->txesc_rst);
+ if (ret < 0) {
+ dev_err(dev, "failed to deassert txesc_rst\n");
+ return ret;
+ }
+
+ ret = reset_control_deassert(dsi->dpi_rst);
+ if (ret < 0) {
+ dev_err(dev, "failed to deassert dpi_rst\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int cdns_dsi_resets_assert(struct cdns_dsi *dsi, struct device *dev)
+{
+ int ret;
+
+ ret = reset_control_assert(dsi->apb_rst);
+ if (ret < 0) {
+ dev_err(dev, "failed to assert apb_rst\n");
+ return ret;
+ }
+ ret = reset_control_assert(dsi->txesc_rst);
+ if (ret < 0) {
+ dev_err(dev, "failed to assert txesc_rst\n");
+ return ret;
+ }
+
+ ret = reset_control_assert(dsi->dpi_rst);
+ if (ret < 0) {
+ dev_err(dev, "failed to assert dpi_rst\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int cdns_dsi_get_clock(struct device *dev, struct cdns_dsi *dsi)
+{
+ dsi->apb_clk = devm_clk_get(dev, "apb");
+ if (IS_ERR(dsi->apb_clk))
+ return PTR_ERR(dsi->apb_clk);
+
+ dsi->txesc_clk = devm_clk_get(dev, "txesc");
+ if (IS_ERR(dsi->txesc_clk))
+ return PTR_ERR(dsi->txesc_clk);
+
+ return 0;
+}
+
+static int cdns_dsi_get_reset(struct device *dev, struct cdns_dsi *dsi)
+{
+ dsi->dpi_rst = devm_reset_control_get(dev, "dsi_dpi");
+ if (IS_ERR(dsi->dpi_rst))
+ return PTR_ERR(dsi->dpi_rst);
+
+ dsi->apb_rst = devm_reset_control_get(dev, "dsi_apb");
+ if (IS_ERR(dsi->apb_rst))
+ return PTR_ERR(dsi->apb_rst);
+
+ dsi->txesc_rst = devm_reset_control_get(dev, "dsi_txesc");
+ if (IS_ERR(dsi->txesc_rst))
+ return PTR_ERR(dsi->txesc_rst);
+
+ dsi->txbytehs_rst = devm_reset_control_get(dev, "dsi_txbytehs");
+ if (IS_ERR(dsi->txbytehs_rst))
+ return PTR_ERR(dsi->txbytehs_rst);
+
+ return 0;
+}
+
+static int cdns_dsi_jh7110_init(struct cdns_dsi *dsi)
+{
+ int ret;
+
+ ret = cdns_dsi_get_clock(dsi->base.dev, dsi);
+ if (ret)
+ return ret;
+
+ ret = cdns_dsi_get_reset(dsi->base.dev, dsi);
+ return ret;
+}
+
+static void cdns_dsi_jh7110_resume(struct cdns_dsi *dsi)
+{
+ int ret;
+
+ ret = cdns_dsi_clock_enable(dsi, dsi->base.dev);
+ if (ret) {
+ dev_err(dsi->base.dev, "failed to enable clock\n");
+ return;
+ }
+ ret = cdns_dsi_resets_deassert(dsi, dsi->base.dev);
+ if (ret < 0) {
+ dev_err(dsi->base.dev, "failed to deassert reset\n");
+ return;
+ }
+}
+
+static void cdns_dsi_jh7110_suspend(struct cdns_dsi *dsi)
+{
+ int ret;
+
+ ret = cdns_dsi_resets_assert(dsi, dsi->base.dev);
+ if (ret < 0) {
+ dev_err(dsi->base.dev, "failed to deassert reset\n");
+ return;
+ }
+
+ cdns_dsi_clock_disable(dsi);
+}
+
+static void dpi_get_metrics(const struct dpi_params *dpi, struct dpi_metrics *m)
+{
+ unsigned int total_lines = dpi->vsync_len + dpi->vback_porch +
+ dpi->vactive + dpi->vfront_porch;
+ m->pixels_one_line = dpi->hsync_len + dpi->hback_porch +
+ dpi->hactive + dpi->hfront_porch;
+ m->fps = (double)dpi->pixelclock / m->pixels_one_line / total_lines;
+ m->pixelclock_period = (unsigned long)NSEC_PER_SEC /
+ ((unsigned long)dpi->pixelclock / 1000);
+ m->hact_time = m->pixelclock_period * dpi->hactive;
+ m->hfp_time = m->pixelclock_period * dpi->hfront_porch;
+ m->hbp_time = m->pixelclock_period * dpi->hback_porch;
+ m->hsa_time = m->pixelclock_period * dpi->hsync_len;
+ m->one_line_time = m->pixelclock_period * m->pixels_one_line;
+}
+
+static int gen_dsi_timing(const struct dpi_params *dpi, int lanes,
+ unsigned long bitrate, struct dsi_params *dsi,
+ const struct calc_ctrl *ctrl)
+{
+ unsigned long pixel_bytes = dpi->bpp / 8;
+ unsigned long pixels_hblk = dpi->hback_porch + dpi->hfront_porch + dpi->hsync_len;
+
+ dsi->dlanes = lanes;
+ dsi->bitrate = bitrate;
+
+ // 1. HACT(WC) = Active pixels per line * Bits per pixel/8
+ // VACT = Active lines per frame
+ unsigned long hact_wc = dpi->hactive * pixel_bytes;
+ unsigned long vact = dpi->vactive;
+
+ dsi->hact = hact_wc;
+ dsi->vact = vact;
+
+ /* 2. Get total line-time in pixel clock */
+ unsigned long pixelclock_period = NSEC_PER_SEC /
+ (dpi->pixelclock / 1000);
+ unsigned long pixels_in_one_line = dpi->hactive + pixels_hblk;
+ unsigned long total_line_time = pixelclock_period * pixels_in_one_line;
+
+ /* 3. Calculate blanking time */
+ unsigned long byteclock = bitrate / 8;
+ unsigned long byteclock_period = NSEC_PER_SEC / (byteclock / 1000);
+ unsigned long hact_duration = dpi->hactive * pixel_bytes *
+ byteclock_period / lanes;
+ unsigned long blanking_time = total_line_time - hact_duration;
+ unsigned long blanking_wc = blanking_time * lanes / byteclock_period;
+
+ /*
+ * 4. Get timing parameter based on Video mode
+ * Video mode: Sync Pulses
+ * One line is composed of HSS +HSA + HSE + HBP + HACT + HFP
+ * HSS/HSE -> Short packet -> 4 bytes
+ * HSA/HBP/HACT/HFP -> Long packet -> 4 bytes header + Payload + 2 bytes CRC
+ * Total of 2*4 + 4*6 = 32 bytes are covered in header and footer
+ * Available blanking WC = 1210- 32 = 1178
+ */
+ unsigned long avail_blanking_wc = blanking_wc - 32;
+
+ /*
+ * 5. Divide the total available WC across available blanking parameters HSA,HBP & HFP.
+ * The MIPI specification does not define the ratio. However, some
+ * may have specific requirements. Hence, please consult the data sheet for
+ * your display.
+ */
+ struct dsi_hblk_ratio hblk_ratio = {
+ .den = dpi->hsync_len + dpi->hback_porch + dpi->hfront_porch,
+ .hsa_num = dpi->hsync_len,
+ .hbp_num = dpi->hback_porch,
+ .hfp_num = dpi->hfront_porch,
+ };
+ if (ctrl->r_hsa && ctrl->r_hbp && ctrl->r_hfp) {
+ /* The following is implemented based on
+ * MIPI DSI Transmitter Subsystem v2.3 Page 30
+ */
+ hblk_ratio.den = ctrl->r_hsa + ctrl->r_hbp + ctrl->r_hfp;
+ hblk_ratio.hsa_num = ctrl->r_hsa;
+ hblk_ratio.hbp_num = ctrl->r_hbp;
+ hblk_ratio.hfp_num = ctrl->r_hfp;
+
+ dsi->hsa = DIV_ROUND_UP(avail_blanking_wc * hblk_ratio.hsa_num, hblk_ratio.den);
+ dsi->hbp = DIV_ROUND_UP(avail_blanking_wc * hblk_ratio.hbp_num, hblk_ratio.den);
+ dsi->hfp = avail_blanking_wc - dsi->hsa - dsi->hbp;
+
+ dsi->hsa += DSI_HSA_FRAME_OVERHEAD;
+ dsi->hbp += DSI_HBP_FRAME_OVERHEAD;
+ dsi->hfp += DSI_HFP_FRAME_OVERHEAD;
+ } else {
+ /* The following is implemented based on
+ * MIPI MIPI_DSI_v1.3.1_Host_Controller_User_Guide_v1p09.pdf
+ * page 95
+ */
+ dsi->hsa = dpi->hsync_len * dpi->bpp / 8;
+ dsi->hbp = dpi->hback_porch * dpi->bpp / 8;
+ dsi->hfp = blanking_wc - dsi->hsa - dsi->hbp;
+ }
+
+ /* vertical blanking lines */
+ dsi->vbp = dpi->vback_porch;
+ dsi->vfp = dpi->vfront_porch;
+ dsi->vsa = dpi->vsync_len;
+
+ return 0;
+}
+
+static int dsi_get_metrics(const struct dsi_params *dsi, struct dsi_metrics *m)
+{
+ if (!dsi || !m)
+ return -1;
+
+ m->bytes_one_line = dsi->hsa + dsi->hbp + dsi->hact + dsi->hfp;
+ m->byteclock = dsi->bitrate / 8;
+ m->byteclock_period = (unsigned long)NSEC_PER_SEC /
+ ((unsigned long)m->byteclock / 1000);
+ m->hsa_hbp_time = DIV_ROUND_UP((dsi->hsa + dsi->hbp), dsi->dlanes) *
+ m->byteclock_period;
+ m->hact_time = DIV_ROUND_UP(dsi->hact, dsi->dlanes) *
+ m->byteclock_period;
+ m->hfp_time = DIV_ROUND_UP(dsi->hfp, dsi->dlanes) *
+ m->byteclock_period;
+ m->one_line_time = DIV_ROUND_UP(m->bytes_one_line, dsi->dlanes) *
+ m->byteclock_period;
+ return 0;
+}
+
+static unsigned long dphy_adjust_bitrate(unsigned int dlanes,
+ unsigned long bitrate_alignment,
+ unsigned long bitrate_want)
+{
+ unsigned long bitrate = bitrate_want;
+
+ /* align to dphy timing */
+ if (bitrate_alignment > 0) {
+ unsigned long reminder = bitrate % bitrate_alignment;
+
+ if (reminder)
+ bitrate += bitrate_alignment - reminder;
+ }
+
+ return bitrate;
+}
+
+static int calc_gen_dsi(const struct calc_ctrl *ctrl, struct dpi_params *dpi,
+ struct dsi_params *dsi)
+{
+ struct dpi_metrics dpi_metrics;
+ unsigned long bitrate_want;
+ unsigned long bitrate;
+ unsigned long abs_line_time_delta;
+ struct dsi_metrics dsi_metrics;
+
+ dpi_get_metrics(dpi, &dpi_metrics);
+
+ bitrate_want = dpi->pixelclock / ctrl->dlanes * dpi->bpp;
+ bitrate = dphy_adjust_bitrate(ctrl->dlanes, ctrl->bitrate_alignment,
+ bitrate_want);
+
+ do {
+ gen_dsi_timing(dpi, ctrl->dlanes, bitrate, dsi, ctrl);
+ dsi_get_metrics(dsi, &dsi_metrics);
+ abs_line_time_delta = dsi_metrics.one_line_time - dpi_metrics.one_line_time;
+ if (dsi_metrics.one_line_time < dpi_metrics.one_line_time)
+ abs_line_time_delta = dpi_metrics.one_line_time - dsi_metrics.one_line_time;
+
+ if (abs_line_time_delta <= ctrl->line_time_tolerance)
+ return 0;
+
+ bitrate += ctrl->bitrate_alignment;
+ } while (bitrate < ctrl->max_bitrate);
+
+ return -1;
+}
+
+static void cdns_dsi_jh7110_update(struct cdns_dsi *dsi, struct cdns_dsi_cfg *dsi_cfg,
+ const struct drm_display_mode *mode)
+{
+ struct calc_ctrl ctrl = {
+ .fps_tolerance = 0.1f,
+ .max_bitrate = 1000000000,
+ .min_bitrate = 370000000,
+ .bitrate_alignment = 10000000,
+ .dsi_video_mode = DSI_Video_NonBurstPulse,
+ .line_time_tolerance = 2000,
+ .r_hsa = 2,
+ .r_hbp = 2,
+ .r_hfp = 2,
+ };
+ struct dsi_params dsi_p = {0};
+ struct dpi_params dpi = {0};
+
+ ctrl.dlanes = dsi->output.dev->lanes;
+
+ dpi.bpp = mipi_dsi_pixel_format_to_bpp(dsi->output.dev->format);
+ dpi.pixelclock = mode->clock * 1000;
+ dpi.hactive = mode->hdisplay;
+ dpi.hfront_porch = mode->hsync_start - mode->hdisplay;
+ dpi.hback_porch = mode->htotal - mode->hsync_end;
+ dpi.hsync_len = mode->hsync_end - mode->hsync_start;
+ dpi.vactive = mode->vdisplay;
+ dpi.vfront_porch = mode->vsync_start - mode->vdisplay;
+ dpi.vback_porch = mode->vtotal - mode->vsync_end;
+ dpi.vsync_len = mode->vsync_end - mode->vsync_start;
+
+ if (!calc_gen_dsi(&ctrl, &dpi, &dsi_p)) {
+ dsi_cfg->hbp = dsi_p.hbp - DSI_HBP_FRAME_OVERHEAD;
+ dsi_cfg->hsa = dsi_p.hsa - DSI_HSA_FRAME_OVERHEAD;
+ dsi_cfg->hfp = dsi_p.hfp - DSI_HFP_FRAME_OVERHEAD;
+ dsi->output.phy_opts.mipi_dphy.hs_clk_rate = dsi_p.bitrate;
+ }
+}
+
+static void jh7110_cdns_dsi_hs_init(struct cdns_dsi *dsi)
+{
+ cdns_dsi_hs_init(dsi);
+ reset_control_deassert(dsi->txbytehs_rst);
+}
+
+const struct cdns_dsi_platform_ops dsi_ti_jh7110_ops = {
+ .init = cdns_dsi_jh7110_init,
+ .resume = cdns_dsi_jh7110_resume,
+ .suspend = cdns_dsi_jh7110_suspend,
+ .update = cdns_dsi_jh7110_update,
+ .transfer = jh7110_cdns_dsi_hs_init,
+};
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dsi-jh7110.h b/drivers/gpu/drm/bridge/cadence/cdns-dsi-jh7110.h
new file mode 100644
index 000000000000..77db6cdd503f
--- /dev/null
+++ b/drivers/gpu/drm/bridge/cadence/cdns-dsi-jh7110.h
@@ -0,0 +1,186 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * JH7110 Cadence DSI
+ *
+ * Copyright (C) 2022-2023 StarFive Technology Co., Ltd.
+ * Author: keith.zhao <keith.zhao@xxxxxxxxxxxxxxxx>
+ */
+
+#ifndef __CDNS_DSI_JH7110_H__
+#define __CDNS_DSI_JH7110_H__
+
+#include "cdns-dsi-core.h"
+
+#define DSI_HSS 4
+#define DSI_HSE 4
+#define DSI_VSS 4
+#define DSI_VSE 4
+#define DSI_HDR 4
+#define DSI_CRC 2
+
+/*
+ * HBP should be reduced by 12 to account for the header
+ * and footer on the blanking packet (6 bytes) plus
+ * the header/footer on the active data packet (6 bytes)
+ */
+#define DSI_HBP_FRAME_OVERHEAD 12
+
+/*
+ * HSA should be reduced by 14 bytes to account for the HSS short packet (4 bytes),
+ * the long blanking packet
+ * header and CRC footer (4+2 bytes)
+ * and the HSE short packet (4 bytes)
+ */
+#define DSI_HSA_FRAME_OVERHEAD 14
+
+/*
+ * HFP should be reduced by 6 bytes to account
+ * for the long packet header and CRC footer
+ */
+#define DSI_HFP_FRAME_OVERHEAD 6
+
+#define DSI_HSS_VSS_VSE_FRAME_OVERHEAD 4
+#define DSI_BLANKING_FRAME_OVERHEAD 6
+#define DSI_NULL_FRAME_OVERHEAD 6
+#define DSI_EOT_PKT_SIZE 4
+
+#define DSI_REG_HSA_LIMIT (BIT(10) - 1 + DSI_HSA_FRAME_OVERHEAD)
+#define DSI_REG_HBP_LIMIT (BIT(16) - 1 + DSI_HBP_FRAME_OVERHEAD)
+#define DSI_REG_HFP_LIMIT (BIT(11) - 1 + DSI_HFP_FRAME_OVERHEAD)
+#define DSI_REG_VSA_LIMIT (BIT(6) - 1)
+#define DSI_REG_VBP_LIMIT (BIT(6) - 1)
+#define DSI_REG_VFP_LIMIT (BIT(8) - 1)
+
+struct dsi_regval_t {
+ /*
+ * Active line Pulse Mode:
+ * |____hsync_____|____hbp_____|________________hact_______________|_______hfp________|
+ * |_HSS_HSA_HSE__|HDR_HBP_CRC_|HDR_____________HACT____________CRC|HDR____HFP_____CRC|_HSS_
+ * Active line Event Mode:
+ * |____hsync_____|____hbp_____|________________hact_______________|_______hfp________|
+ * |_HSS_|HDR___HSA+HBP____CRC_|HDR_____________HACT____________CRC|HDR____HFP_____CRC|_HSS_
+ */
+ int hsa_length, hbp_length, hact_length, hfp_length; // register field value
+
+ /*
+ * Pulse mode Blank line
+ * |______________|_______________________blkline_pulse_pck___________________________|
+ * |_HSS_HSA_HSE__|HDR_____________________________________________________________CRC|_HSS_
+ */
+ unsigned int blkline_pulse_pck;
+
+ // BLKEOL_PCK: packet length (in byte) on end of line if burst mode (reg_blkeol_mode = 0b0x)
+ unsigned int blkeol_pck;
+
+ /*
+ * Event mode Blank line
+ * |_____|________________________blkline_event_pck___________________________|
+ * |_HSS_|HDR______________________________________________________________CRC|_HSS_
+ */
+ unsigned int blkline_event_pck;
+
+ /* BLKEOL_DURATION: specify the duration in clock cycles
+ * of the BLLP period (used for burst mode)
+ * unsigned int blkeol_duration;
+
+ * REG_LINE_DURATION: duration -in clock cycles - of the blanking area for VSA/VBP
+ * and VFP lines - considered when reg_blkline_mode = 1b1x
+
+ * Pulse mode Blank LP line EOT disabled
+ * |______________|_______________________reg_line_duration_______________________|
+ * |_HSS_HSA_HSE__|__________________________LP___________________________________|_HSS_
+ * Pulse mode Blank LP line EOT enabled
+ * |______________|___|___________________reg_line_duration_______________________|
+ * |_HSS_HSA_HSE__|EoT|______________________LP___________________________________|_HSS_
+ * Event mode Blank LP line EOT enabled
+ * |________|_____________________________reg_line_duration_______________________|
+ * |_HSS|EoT|________________________________LP___________________________________|_HSS_
+ */
+ unsigned int reg_line_duration;
+};
+
+enum dsi_video_mode {
+ DSI_Video_Burst,
+ DSI_Video_NonBurstPulse,
+ DSI_Video_NonBurstEvent,
+};
+
+struct dsi_params {
+ unsigned int dlanes;
+ unsigned long bitrate;
+ unsigned int hsa;
+ unsigned int hbp;
+ unsigned int hfp;
+ unsigned int hact;
+ unsigned int vsa;
+ unsigned int vbp;
+ unsigned int vfp;
+ unsigned int vact;
+};
+
+struct dsi_metrics {
+ unsigned int bytes_one_line;
+ unsigned long byteclock;
+
+ //period and time in ps
+ unsigned long byteclock_period;
+ unsigned long hsa_hbp_time;
+ unsigned long hact_time;
+ unsigned long hfp_time;
+ unsigned long one_line_time;
+};
+
+struct dpi_params {
+ unsigned int bpp;
+ unsigned long pixelclock;
+ unsigned int hactive;
+ unsigned int hfront_porch;
+ unsigned int hback_porch;
+ unsigned int hsync_len;
+ unsigned int vactive;
+ unsigned int vfront_porch;
+ unsigned int vback_porch;
+ unsigned int vsync_len;
+};
+
+struct dpi_metrics {
+ unsigned int pixels_one_line;
+ double fps;
+
+ unsigned long pixelclock_period;
+ unsigned long hact_time;
+ unsigned long hfp_time;
+ unsigned long hbp_time;
+ unsigned long hsa_time;
+ unsigned long one_line_time;
+};
+
+struct dsi_hblk_ratio {
+ int den;
+ int hsa_num;
+ int hbp_num;
+ int hfp_num;
+};
+
+struct calc_ctrl {
+ unsigned int hactive;
+ unsigned int vactive;
+ unsigned int bpp;
+ unsigned int fps;
+ double fps_tolerance;
+
+ unsigned int dlanes;
+ unsigned long bitrate_alignment;
+ unsigned long max_bitrate;
+ unsigned long min_bitrate;
+ unsigned int dsi_video_mode;
+
+ unsigned int r_hsa;
+ unsigned int r_hbp;
+ unsigned int r_hfp;
+ unsigned int line_time_tolerance;
+};
+
+extern const struct cdns_dsi_platform_ops dsi_ti_jh7110_ops;
+
+#endif /* !__CDNS_DSI_JH7110_H__ */
--
2.17.1