[PATCH 07/10] drm/mediatek: mtk_dsi: Add support for MT8196
AngeloGioacchino Del Regno
angelogioacchino.delregno at collabora.com
Wed Jul 1 05:19:47 PDT 2026
Add support for the new DSI IP found in the Kompanio Ultra MT8196
SoC and its Dimensity and Genio variants.
Differently from the older DSI IPs, the one from MT8196 requires
the initialization of all of the QoS parameters and can make use
of a DSI SRAM reserved buffer (present also on older SoCs but not
mandatory on those).
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno at collabora.com>
---
drivers/gpu/drm/mediatek/mtk_drm_drv.c | 2 +
drivers/gpu/drm/mediatek/mtk_dsi.c | 197 +++++++++++++++++++++++++
2 files changed, 199 insertions(+)
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index bd2d17017bd2..9a4c59849c4b 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -826,6 +826,8 @@ static const struct of_device_id mtk_ddp_comp_dt_ids[] = {
.data = (void *)MTK_DSI },
{ .compatible = "mediatek,mt8188-dsi",
.data = (void *)MTK_DSI },
+ { .compatible = "mediatek,mt8196-dsi",
+ .data = (void *)MTK_DSI },
{ }
};
diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c
index ba96c12a8ceb..fc6f846208d6 100644
--- a/drivers/gpu/drm/mediatek/mtk_dsi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dsi.c
@@ -153,6 +153,18 @@
#define FORCE_COMMIT BIT(0)
#define BYPASS_SHADOW BIT(1)
+/* DSI_VDE */
+#define VDE_BLOCK_ULTRA BIT(29)
+
+/* DSI_BUF_CON0 */
+#define DSI_QOS_BUF_EN BIT(0)
+
+/* DSI_BUF_CON1 */
+#define BUF_OUT_VALID_THRESH GENMASK(14, 0)
+
+/* DSI_BUF_SODI_HIGH, SODI_LOW and other BUF registers */
+#define BUF_THRESHOLD_PARAM GENMASK(19, 0)
+
/* CMDQ related bits */
#define CONFIG GENMASK(7, 0)
#define SHORT_PACKET 0
@@ -165,6 +177,16 @@
#define NS_TO_CYCLE(n, c) ((n) / (c) + (((n) % (c)) ? 1 : 0))
+/* HW QoS and Anti-Latency Buffer related bits */
+#define MTK_DSI_MAX_FIFO_BYTES 1554
+#define MTK_DSI_DEFAULT_QOS_VALID_FIFO_US 25
+#define MTK_DSI_DEFAULT_QOS_PREULTRA_HI_US 36
+#define MTK_DSI_DEFAULT_QOS_PREULTRA_LO_US 35
+#define MTK_DSI_DEFAULT_QOS_ULTRA_HI_US 26
+#define MTK_DSI_DEFAULT_QOS_ULTRA_LO_US 25
+#define MTK_DSI_DEFAULT_QOS_URGENT_LO_US 11
+#define MTK_DSI_DEFAULT_QOS_URGENT_HI_US 12
+
#define MTK_DSI_HOST_IS_READ(type) \
((type == MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM) || \
(type == MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM) || \
@@ -207,9 +229,26 @@ enum mtk_dsi_adv_regidx {
DSI_VM_CMD_CON,
DSI_SHADOW_DEBUG,
DSI_CMDQ,
+ DSI_VDE,
DSI_ADV_REG_MAX
};
+enum mtk_dsi_qos_regidx {
+ DSI_QOS_BUF_CON0,
+ DSI_QOS_BUF_CON1,
+ DSI_QOS_TX_BUF_RW_TIMES,
+ DSI_QOS_SODI_HIGH,
+ DSI_QOS_SODI_LOW,
+ DSI_QOS_PREULTRA_HIGH,
+ DSI_QOS_PREULTRA_LOW,
+ DSI_QOS_ULTRA_HIGH,
+ DSI_QOS_ULTRA_LOW,
+ DSI_QOS_URGENT_HIGH,
+ DSI_QOS_URGENT_LOW,
+ DSI_QOS_PREURGENT_HIGH,
+ DSI_QOS_REG_MAX
+};
+
struct mtk_phy_timing {
u32 lpx;
u32 da_hs_prepare;
@@ -234,8 +273,12 @@ struct phy;
struct mtk_dsi_driver_data {
const u16 *reg_main;
const u16 *reg_adv;
+ const u16 *reg_qos;
const u16 max_link_rate_mbps;
+ const u8 dsi_sram_bytes;
+ const u8 pixels_per_iter;
+ const u8 num_burst_lines;
bool has_size_ctl;
bool cmdq_long_packet_ctl;
@@ -329,6 +372,59 @@ static const u16 mtk_dsi_regs_mt8186[DSI_ADV_REG_MAX] = {
[DSI_CMDQ] = 0xd00,
};
+static const u16 mtk_dsi_regs_main_v2[DSI_MAIN_REG_MAX] = {
+ [DSI_START] = 0x00,
+ [DSI_INTEN] = 0x08,
+ [DSI_INTSTA] = 0x0c,
+ [DSI_CON_CTRL] = 0x30,
+ [DSI_MODE_CTRL] = 0x34,
+ [DSI_TXRX_CTRL] = 0x38,
+ [DSI_PSCTRL] = 0x3c,
+ [DSI_VSA_NL] = 0x60,
+ [DSI_VBP_NL] = 0x64,
+ [DSI_VFP_NL] = 0x68,
+ [DSI_VACT_NL] = 0x6c,
+ [DSI_SIZE_CON] = 0x2c,
+ [DSI_HSA_WC] = 0x80,
+ [DSI_HBP_WC] = 0x84,
+ [DSI_HFP_WC] = 0x88,
+ [DSI_CMDQ_SIZE] = 0x44,
+ [DSI_HSTX_CKL_WC] = 0x100,
+ [DSI_RX_DATA0] = 0xa4,
+ [DSI_RX_DATA1] = 0xa8,
+ [DSI_RX_DATA2] = 0xac,
+ [DSI_RX_DATA3] = 0xb0,
+ [DSI_RACK] = 0xb4,
+ [DSI_PHY_LCCON] = 0x7d0,
+ [DSI_PHY_LD0CON] = 0x7d4,
+ [DSI_PHY_TIMECON0] = 0x600,
+ [DSI_PHY_TIMECON1] = 0x604,
+ [DSI_PHY_TIMECON2] = 0x608,
+ [DSI_PHY_TIMECON3] = 0x60c,
+};
+
+static const u16 mtk_dsi_regs_qos_v2[DSI_QOS_REG_MAX] = {
+ [DSI_QOS_BUF_CON0] = 0x300,
+ [DSI_QOS_BUF_CON1] = 0x304,
+ [DSI_QOS_TX_BUF_RW_TIMES] = 0x310,
+ [DSI_QOS_SODI_HIGH] = 0x314,
+ [DSI_QOS_SODI_LOW] = 0x318,
+ [DSI_QOS_PREULTRA_HIGH] = 0x324,
+ [DSI_QOS_PREULTRA_LOW] = 0x328,
+ [DSI_QOS_ULTRA_HIGH] = 0x32c,
+ [DSI_QOS_ULTRA_LOW] = 0x330,
+ [DSI_QOS_URGENT_HIGH] = 0x334,
+ [DSI_QOS_URGENT_LOW] = 0x338,
+ [DSI_QOS_PREURGENT_HIGH] = 0x33c
+};
+
+static const u16 mtk_dsi_regs_mt8196[DSI_ADV_REG_MAX] = {
+ [DSI_VM_CMD_CON] = 0x110,
+ [DSI_SHADOW_DEBUG] = 0xd0,
+ [DSI_CMDQ] = 0x400,
+ [DSI_VDE] = 0x3f8,
+};
+
static inline struct mtk_dsi *bridge_to_dsi(struct drm_bridge *b)
{
return container_of(b, struct mtk_dsi, bridge);
@@ -752,6 +848,89 @@ static int mtk_dsi_set_dsc_params(struct mtk_dsi *dsi)
return drm_dsc_compute_rc_parameters(dsc);
}
+static void mtk_dsi_config_hw_buffers(struct mtk_dsi *dsi)
+{
+ const struct mtk_dsi_driver_data *data = dsi->driver_data;
+ const u16 *reg_qos = data->reg_qos;
+ u32 buffer_unit, sram_unit, num_hw_buffers;
+ u32 preultra_hi, preultra_lo;
+ u32 urgent_hi, urgent_lo;
+ u32 ultra_hi, ultra_lo;
+ u32 sodi_hi, sodi_lo;
+ u32 data_rate_per_buf;
+ u32 out_valid_thresh;
+ u32 dsi_buf_bpp;
+ u32 fill_rate;
+ u32 pclk_mhz;
+ u32 rw_times;
+ u32 val;
+
+ /*
+ * At the time of writing, only MT8196 is implemented and, for this SoC,
+ * the buffer unit is equal to the SRAM bytes.
+ *
+ * There are other SoCs already out in the wild that do support the HW
+ * buffers and that have different sizes, so keep the calculation as-is!
+ */
+ buffer_unit = data->dsi_sram_bytes;
+ sram_unit = data->dsi_sram_bytes;
+ num_hw_buffers = sram_unit / buffer_unit;
+
+ if (data->support_per_frame_lp)
+ val = CMDMODE_WAIT_DATA_EVERY_LINE_EN;
+ else
+ val = 0;
+
+ mtk_dsi_mask(dsi, data->reg_main[DSI_CON_CTRL],
+ CMDMODE_WAIT_DATA_EVERY_LINE_EN, val);
+
+ data_rate_per_buf = dsi->data_rate * dsi->lanes / 8 / buffer_unit;
+
+ /* Calculate valid threshold and avoid exceeding FIFO size */
+ out_valid_thresh = MTK_DSI_DEFAULT_QOS_VALID_FIFO_US * data_rate_per_buf;
+ out_valid_thresh = min(out_valid_thresh, MTK_DSI_MAX_FIFO_BYTES - 1);
+ mtk_dsi_mask(dsi, reg_qos[DSI_QOS_BUF_CON1], BUF_OUT_VALID_THRESH, out_valid_thresh);
+
+ /* Enable ULTRA signal trigger between SOF and VACT */
+ mtk_dsi_mask(dsi, data->reg_adv[DSI_VDE], VDE_BLOCK_ULTRA, 0);
+
+ /* Calculate fill rate with line counter mode for DSI Video Mode */
+ if (dsi->format == MIPI_DSI_FMT_RGB565)
+ dsi_buf_bpp = 2;
+ else
+ dsi_buf_bpp = 3;
+
+ pclk_mhz = dsi->vm.pixelclock / HZ_PER_MHZ;
+ fill_rate = pclk_mhz * data->pixels_per_iter * dsi_buf_bpp / buffer_unit;
+
+ /* Calculate QoS Anti-Latency parameters */
+ sodi_hi = MTK_DSI_MAX_FIFO_BYTES * num_hw_buffers;
+ sodi_hi -= (fill_rate - data_rate_per_buf) * 12 / 10;
+ sodi_lo = (23 + 5) * data_rate_per_buf;
+ preultra_hi = MTK_DSI_DEFAULT_QOS_PREULTRA_HI_US * data_rate_per_buf;
+ preultra_lo = MTK_DSI_DEFAULT_QOS_PREULTRA_LO_US * data_rate_per_buf;
+ ultra_hi = MTK_DSI_DEFAULT_QOS_ULTRA_HI_US * data_rate_per_buf;
+ ultra_lo = MTK_DSI_DEFAULT_QOS_ULTRA_LO_US * data_rate_per_buf;
+ urgent_hi = MTK_DSI_DEFAULT_QOS_URGENT_HI_US * data_rate_per_buf;
+ urgent_lo = MTK_DSI_DEFAULT_QOS_URGENT_LO_US * data_rate_per_buf;
+ rw_times = dsi->vm.vactive * dsi_buf_bpp;
+ rw_times /= data->num_burst_lines * data->pixels_per_iter;
+
+ /* Write all QoS parameters: Screen On Deep Idle, (pre)Ultra, Urgent, RW times */
+ mtk_dsi_mask(dsi, reg_qos[DSI_QOS_SODI_HIGH], BUF_THRESHOLD_PARAM, sodi_hi);
+ mtk_dsi_mask(dsi, reg_qos[DSI_QOS_SODI_LOW], BUF_THRESHOLD_PARAM, sodi_lo);
+ mtk_dsi_mask(dsi, reg_qos[DSI_QOS_PREULTRA_HIGH], BUF_THRESHOLD_PARAM, preultra_hi);
+ mtk_dsi_mask(dsi, reg_qos[DSI_QOS_PREULTRA_LOW], BUF_THRESHOLD_PARAM, preultra_lo);
+ mtk_dsi_mask(dsi, reg_qos[DSI_QOS_ULTRA_HIGH], BUF_THRESHOLD_PARAM, ultra_hi);
+ mtk_dsi_mask(dsi, reg_qos[DSI_QOS_ULTRA_LOW], BUF_THRESHOLD_PARAM, ultra_lo);
+ mtk_dsi_mask(dsi, reg_qos[DSI_QOS_URGENT_HIGH], BUF_THRESHOLD_PARAM, urgent_hi);
+ mtk_dsi_mask(dsi, reg_qos[DSI_QOS_URGENT_LOW], BUF_THRESHOLD_PARAM, urgent_lo);
+ writel(rw_times, dsi->regs + reg_qos[DSI_QOS_TX_BUF_RW_TIMES]);
+
+ /* Finally, activate internal line-buffering */
+ mtk_dsi_mask(dsi, reg_qos[DSI_QOS_BUF_CON0], DSI_QOS_BUF_EN, DSI_QOS_BUF_EN);
+}
+
static int mtk_dsi_config_vdo_timing(struct mtk_dsi *dsi)
{
const struct mtk_dsi_driver_data *data = dsi->driver_data;
@@ -929,6 +1108,10 @@ static int mtk_dsi_poweron(struct mtk_dsi *dsi)
mtk_dsi_reset_engine(dsi);
mtk_dsi_phy_timconfig(dsi);
+ /* Setup HW FIFO if DSI supports QoS Anti-Latency buffers */
+ if (data->dsi_sram_bytes && data->reg_qos)
+ mtk_dsi_config_hw_buffers(dsi);
+
mtk_dsi_ps_control(dsi, true);
mtk_dsi_set_vm_cmd(dsi);
ret = mtk_dsi_config_vdo_timing(dsi);
@@ -1549,6 +1732,19 @@ static const struct mtk_dsi_driver_data mt8189_dsi_driver_data = {
.support_per_frame_lp = true,
};
+static const struct mtk_dsi_driver_data mt8196_dsi_driver_data = {
+ .reg_main = mtk_dsi_regs_main_v2,
+ .reg_qos = mtk_dsi_regs_qos_v2,
+ .reg_adv = mtk_dsi_regs_mt8196,
+ .max_link_rate_mbps = 2000,
+ .dsi_sram_bytes = 32,
+ .pixels_per_iter = 2,
+ .num_burst_lines = 8,
+ .has_size_ctl = true,
+ .cmdq_long_packet_ctl = true,
+ .support_per_frame_lp = true,
+};
+
static const struct of_device_id mtk_dsi_of_match[] = {
{ .compatible = "mediatek,mt2701-dsi", .data = &mt2701_dsi_driver_data },
{ .compatible = "mediatek,mt8173-dsi", .data = &mt8173_dsi_driver_data },
@@ -1556,6 +1752,7 @@ static const struct of_device_id mtk_dsi_of_match[] = {
{ .compatible = "mediatek,mt8186-dsi", .data = &mt8186_dsi_driver_data },
{ .compatible = "mediatek,mt8188-dsi", .data = &mt8188_dsi_driver_data },
{ .compatible = "mediatek,mt8189-dsi", .data = &mt8189_dsi_driver_data },
+ { .compatible = "mediatek,mt8196-dsi", .data = &mt8196_dsi_driver_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mtk_dsi_of_match);
--
2.54.0
More information about the linux-arm-kernel
mailing list