Thread (22 messages) flat view 22 messages, 4 authors, 6d ago
COOLING6d

[PATCH 6/9] drm/panel: novatek-nt36672a: Add 3 panels used in Xiaomi phones

From: Alexey Minnekhanov <hidden>
Date: 2026-09-13 05:37:42
Also in: dri-devel, lkml, phone-devel
Subsystem: drm driver for novatek nt36672a panels, drm drivers, drm drivers and misc gpu patches, drm panel drivers, the rest · Maintainers: Sumit Semwal, David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Neil Armstrong, Linus Torvalds

Add support for 3 panels used in 2 Xiaomi smartphones, all based on
Qualcomm SDM636/660 SoC:

* Shenchao panel used in Xiaomi Redmi Note 7 (xiaomi-lavender), the
  exact model string is unknown
* Tianma TL063FVMCA01-00 for Xiaomi Redmi Note 7 (xiaomi-lavender)
* Tianma TL063FVMC43-02  for Xiaomi Redmi Note 6 Pro (xiaomi-tulip)

They all are DSI video mode panels using Novatek NT36672A controller,
so they can share almost exactly the same init/off sequences with
minimal differences between variants. This allows to avoid a lot of
code duplication.

Co-developed-by: Richard Acayan <redacted>
Signed-off-by: Richard Acayan <redacted>
Signed-off-by: Alexey Minnekhanov <redacted>
---
 drivers/gpu/drm/panel/panel-novatek-nt36672a.c | 166 +++++++++++++++++++++++++
 1 file changed, 166 insertions(+)
diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36672a.c b/drivers/gpu/drm/panel/panel-novatek-nt36672a.c
index 237a6a8699aef..e6971ba6ef464 100644
--- a/drivers/gpu/drm/panel/panel-novatek-nt36672a.c
+++ b/drivers/gpu/drm/panel/panel-novatek-nt36672a.c
@@ -361,6 +361,90 @@ static void tianma_fhd_video_send_deinit_cmds(struct mipi_dsi_multi_context *dsi
 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x10);
 }
 
+static void lavender_tulip_init_cmds(struct mipi_dsi_multi_context *dsi_ctx)
+{
+	/*
+	 * This function supports 3 similar panels with minimal differences
+	 * in their init sequences. We assume base to be lavender-tianma
+	 * panel, and 2 variations (lavender-shenchao and tulip) are handled as
+	 * differences to base.
+	 */
+	const struct device_node *node = dev_of_node(&dsi_ctx->dsi->dev);
+	bool is_shenchao = of_device_is_compatible(node, "shenchao,fhdplus-video");
+	bool is_tulip = of_device_is_compatible(node, "tianma,tl063fvmc43-02");
+
+	dsi_ctx->dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x25);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x18, 0x96);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x05, 0x04);
+
+	if (is_shenchao)
+		mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x27);
+	else
+		mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x20);
+
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
+
+	if (is_shenchao)
+		mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd9, 0x10);
+	else
+		mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x78, 0x01);
+
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x24);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x82, 0x13);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x84, 0x31);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x88, 0x13);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x8a, 0x31);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x8e, 0xe4);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x8f, 0x01);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x90, 0x80);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x26);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xa9, 0x12);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xaa, 0x10);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xae, 0x8a);
+
+	if (is_shenchao)
+		mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x1c, 0xfa);
+
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x10);
+
+	if (is_shenchao)
+		mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb0, 0x01);
+
+	mipi_dsi_dcs_exit_sleep_mode_multi(dsi_ctx);
+	mipi_dsi_msleep(dsi_ctx, 80);
+
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb0, 0x01);
+	mipi_dsi_dcs_set_tear_on_multi(dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+
+	if (is_tulip) {
+		mipi_dsi_dcs_set_display_brightness_multi(dsi_ctx, 0x00ff);
+	} else {
+		/* lavender tianma + shenchao specific part */
+		mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x68, 0x03, 0x04);
+		mipi_dsi_dcs_set_display_brightness_multi(dsi_ctx, 0x00b8);
+	}
+
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x2c);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
+
+	mipi_dsi_dcs_set_display_on_multi(dsi_ctx);
+}
+
+static void lavender_tulip_deinit_cmds(struct mipi_dsi_multi_context *dsi_ctx)
+{
+	dsi_ctx->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+	mipi_dsi_dcs_set_display_off_multi(dsi_ctx);
+	mipi_dsi_msleep(dsi_ctx, 20);
+	mipi_dsi_dcs_enter_sleep_mode_multi(dsi_ctx);
+	mipi_dsi_msleep(dsi_ctx, 120);
+}
+
 static const struct drm_display_mode tianma_fhd_video_panel_default_mode = {
 	.clock		= 161331,
 
@@ -393,6 +477,85 @@ static const struct nt36672a_panel_desc tianma_fhd_video_panel_desc = {
 	.send_deinit_cmds = tianma_fhd_video_send_deinit_cmds,
 };
 
+/* common for both lavender-tianma and lavender-shenchao panels */
+static const struct drm_display_mode lavender_panel_default_mode = {
+	.clock		= (1080 + 90 + 2 + 120) * (2340 + 10 + 3 + 8) * 60 / 1000,
+
+	.hdisplay	= 1080,
+	.hsync_start	= 1080 + 90,
+	.hsync_end	= 1080 + 90 + 2,
+	.htotal		= 1080 + 90 + 2 + 120,
+
+	.vdisplay	= 2340,
+	.vsync_start	= 2340 + 10,
+	.vsync_end	= 2340 + 10 + 3,
+	.vtotal		= 2340 + 10 + 3 + 8,
+
+	.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+};
+
+static const struct nt36672a_panel_desc shenchao_lavender_panel_desc = {
+	.display_mode = &lavender_panel_default_mode,
+
+	.width_mm = 67,
+	.height_mm = 145,
+
+	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+			MIPI_DSI_CLOCK_NON_CONTINUOUS,
+	.format = MIPI_DSI_FMT_RGB888,
+	.lanes = 4,
+	.send_init_cmds = lavender_tulip_init_cmds,
+	.send_deinit_cmds = lavender_tulip_deinit_cmds,
+};
+
+static const struct nt36672a_panel_desc tianma_lavender_panel_desc = {
+	.display_mode = &lavender_panel_default_mode,
+
+	.width_mm = 67,
+	.height_mm = 145,
+
+	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+			MIPI_DSI_CLOCK_NON_CONTINUOUS,
+	.format = MIPI_DSI_FMT_RGB888,
+	.lanes = 4,
+	.send_init_cmds = lavender_tulip_init_cmds,
+	.send_deinit_cmds = lavender_tulip_deinit_cmds,
+};
+
+static const struct drm_display_mode tianmaplus_e7t_tulip_mode = {
+	.clock		= (1080 + 100 + 28 + 120) * (2280 + 10 + 3 + 8) * 60 / 1000,
+
+	.hdisplay	= 1080,
+	.hsync_start	= 1080 + 100,
+	.hsync_end	= 1080 + 100 + 28,
+	.htotal		= 1080 + 100 + 28 + 120,
+
+	.vdisplay	= 2280,
+	.vsync_start	= 2280 + 10,
+	.vsync_end	= 2280 + 10 + 3,
+	.vtotal		= 2280 + 10 + 3 + 8,
+
+	.width_mm = 68,
+	.height_mm = 143,
+
+	.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+};
+
+static const struct nt36672a_panel_desc tianmaplus_e7t_tulip_panel_desc = {
+	.display_mode = &tianmaplus_e7t_tulip_mode,
+
+	.width_mm = 68,
+	.height_mm = 143,
+
+	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+			MIPI_DSI_CLOCK_NON_CONTINUOUS,
+	.format = MIPI_DSI_FMT_RGB888,
+	.lanes = 4,
+	/* tulip panel uses almost the same init/deinit sequences as lavender */
+	.send_init_cmds = lavender_tulip_init_cmds,
+	.send_deinit_cmds = lavender_tulip_deinit_cmds,
+};
+
 static int nt36672a_panel_add(struct nt36672a_panel *pinfo)
 {
 	struct device *dev = &pinfo->link->dev;
@@ -469,7 +632,10 @@ static void nt36672a_panel_remove(struct mipi_dsi_device *dsi)
 }
 
 static const struct of_device_id panel_nt36672a_match[] = {
+	{ .compatible = "shenchao,fhdplus-video", .data = &shenchao_lavender_panel_desc },
 	{ .compatible = "tianma,fhd-video", .data = &tianma_fhd_video_panel_desc },
+	{ .compatible = "tianma,tl063fvmca01-00", .data = &tianma_lavender_panel_desc },
+	{ .compatible = "tianma,tl063fvmc43-02", .data = &tianmaplus_e7t_tulip_panel_desc },
 	{ },
 };
 MODULE_DEVICE_TABLE(of, panel_nt36672a_match);
-- 
2.55.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help