Thread (4 messages) flat view 4 messages, 2 authors, 12h ago
HOTtoday

[PATCH 2/2] drm/panel: novatek-nt51021: add the BOE TV101WUM-NM0

From: Ryan Brue <hidden>
Date: 2026-09-12 18:22:40
Also in: dri-devel, linux-devicetree, linux-mediatek, lkml
Subsystem: drm drivers, drm drivers and misc gpu patches, drm panel drivers, the rest · Maintainers: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Neil Armstrong, Linus Torvalds

Add the BOE module fitted to the Amazon Fire HD 10 (2017), an MT8173
tablet, as a third NT51021 variant. How the part was identified, since
its label is not readable without removing the main board, is set out in
the binding patch.

The register sequence is the BOE branch of init_lcm_registers() in
drivers/misc/mediatek/lcm/nt51021_wuxga_dsi_vdo/ in the Fire OS 3.18
tree, command for command, with only a 120 ms wait after sleep-out added
where the vendor waits its trailing 5 ms. The 0x8f 0xa5 and 0x8f 0x00
that bracket it are the vendor's command interface selection, sent on
every board that is not an early proto or HVT unit.

It is much shorter than boe_tv101wum_nx0_init() because this module is
left on its own factory trim. NT51021 R8CH selects whether V1/V14, gamma
and HAOP come from the panel's OTP memory or from control registers, and
the 0x8c 0x80 here leaves all three on OTP, where the NX0's 0x8e
switches
them to registers so that the three pages of gamma it then writes take
effect. So the absent gamma is deliberate rather than an omission, and
the differing 0x8c is its consequence. CABC is likewise untouched
because
the vendor drives it over I2C from the backlight path. What is left is
0xc0 and 0xc8, which the public NT51021 specification does not describe;
everything else this sequence touches carries the same value as the
other
two panels, including the terminal resistor, TP_SYNC, the MIPI Rx drive
strength and all three test modes.

The timings are the vendor's lcm_get_params(): hsync 1, hbp 32, hfp 110,
vsync 1, vbp 14, vfp 11, so htotal 1343 and vtotal 1946. The pixel clock
follows the vendor's PLL_CLOCK = 490, which after DDR is 980 Mbps per
lane and over four lanes at 24bpp is 163.33 MHz, so 62.5 Hz. The
bootloader leaves the MIPI TX PLL at that rate: read back before the
kernel touches it, this board's PLL decodes to exactly 980 Mbps per
lane, so the panel is already there when the kernel takes over.

The front porch is the vendor's declared 110 rather than the smaller
value its own DSI driver ends up running. That driver also sets
clk_lp_per_line_enable, which makes ddp_dsi_config() overwrite HFP_WC
after the declared timing is programmed, shrinking it to leave just
enough room to take the clock lane to LP and back. That is a
controller-side workaround rather than a panel property, and it
contradicts the cont_clock = 1 set beside it, which disables the very LP
transition it makes room for. Mainline mtk_dsi handles the clock lane
itself, so the panel's declared timing is the right thing to carry here.

The vendor asks for SYNC_EVENT_VDO_MODE with cont_clock = 1 rather than
burst, so make mode_flags per variant instead of fixed in probe(). The
two existing panels keep the flags they had. Burst was also tried on
this hardware and worked, but the vendor configuration is the one the
module is characterised for.

The physical size is the datasheet active area, 216.576 x 135.36 mm,
which measures 216 x 135 on the glass. The vendor's declared a
physical_width = 136 and physical_height = 221, which appears to be an
error. The size of the test unit was physically measured. 1200/136 and
1920/221 disagree by 1.5%.

The vendor's bootloader and kernel both carry a second register sequence
for the Innolux module this board second-sources against, selected from
two board ID straps. No Innolux unit was available, so that sequence is
left out rather than carried untested; however, the Innolux part number
was identified as "P101KDA-AB0", should someone with that glass need to
upstream support in the future.

Tested on the Amazon Fire HD 10 (2017) driven by mediatek-drm: 1200x1920
at 62.5 Hz across a cold boot, display off and on cycles and a
suspend/resume, with the MIPI TX PLL reading back at the intended rate
every time.

Assisted-by: LLM
Signed-off-by: Ryan Brue <redacted>
---
 drivers/gpu/drm/panel/panel-novatek-nt51021.c | 66 ++++++++++++++++++++++++++-
 1 file changed, 64 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/panel/panel-novatek-nt51021.c b/drivers/gpu/drm/panel/panel-novatek-nt51021.c
index 61dd283769a7..2a8fa181ac37 100644
--- a/drivers/gpu/drm/panel/panel-novatek-nt51021.c
+++ b/drivers/gpu/drm/panel/panel-novatek-nt51021.c
@@ -23,6 +23,7 @@ struct novatek_nt51021_variant {
 	int (*init)(struct novatek_nt51021_desc *ctx);
 
 	const struct drm_display_mode *display_mode;
+	unsigned long mode_flags;
 };
 
 struct novatek_nt51021_desc {
@@ -607,6 +608,39 @@ static int boe_tv101wum_nx0_init(struct novatek_nt51021_desc *ctx)
 	return dsi_ctx.accum_err;
 }
 
+static int boe_tv101wum_nm0_init(struct novatek_nt51021_desc *ctx)
+{
+	struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0xa5);
+	mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000);
+	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x01, 0x00);
+	mipi_dsi_msleep(&dsi_ctx, 20);
+	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0xa5);
+	mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000);
+	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0x00);
+	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x00);
+	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8c, 0x80);
+	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x6c);
+	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc0, 0x8b);
+	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0xf0);
+	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x97, 0x00);
+	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8b, 0x10);
+	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa9, 0x20);
+	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xaa);
+	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x11);
+	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa9, 0x4b);
+	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x85, 0x04);
+	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x86, 0x08);
+	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9c, 0x10);
+	mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+	mipi_dsi_msleep(&dsi_ctx, 120);
+	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0x00);
+	mipi_dsi_msleep(&dsi_ctx, 5);
+
+	return dsi_ctx.accum_err;
+}
+
 static int nt51021_novatek_off(struct novatek_nt51021_desc *ctx)
 {
 	struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
@@ -713,8 +747,7 @@ static int nt51021_novatek_probe(struct mipi_dsi_device *dsi)
 
 	dsi->lanes = 4;
 	dsi->format = MIPI_DSI_FMT_RGB888;
-	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
-			  MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_MODE_LPM;
+	dsi->mode_flags = ctx->variant->mode_flags;
 
 	ctx->panel.prepare_prev_first = true;
 
@@ -744,6 +777,21 @@ static const struct drm_display_mode boe_tv080wum_nx2_mode = {
 	.type = DRM_MODE_TYPE_DRIVER,
 };
 
+static const struct drm_display_mode boe_tv101wum_nm0_mode = {
+	.clock = 163333,
+	.hdisplay = 1200,
+	.hsync_start = 1200 + 110,
+	.hsync_end = 1200 + 110 + 1,
+	.htotal = 1200 + 110 + 1 + 32,
+	.vdisplay = 1920,
+	.vsync_start = 1920 + 11,
+	.vsync_end = 1920 + 11 + 1,
+	.vtotal = 1920 + 11 + 1 + 14,
+	.width_mm = 135,
+	.height_mm = 216,
+	.type = DRM_MODE_TYPE_DRIVER,
+};
+
 static const struct drm_display_mode boe_tv101wum_nx0_mode = {
 	.clock = (1200 + 172 + 4 + 32) * (1920 + 25 + 1 + 14) * 60 / 1000,
 	.hdisplay = 1200,
@@ -759,18 +807,32 @@ static const struct drm_display_mode boe_tv101wum_nx0_mode = {
 	.type = DRM_MODE_TYPE_DRIVER,
 };
 
+#define NT51021_BURST_MODE_FLAGS	(MIPI_DSI_MODE_VIDEO | \
+					 MIPI_DSI_MODE_VIDEO_BURST | \
+					 MIPI_DSI_MODE_NO_EOT_PACKET | \
+					 MIPI_DSI_MODE_LPM)
+
 static const struct novatek_nt51021_variant boe_tv080wum_nx2_data = {
 	.init = boe_tv080wum_nx2_init,
 	.display_mode = &boe_tv080wum_nx2_mode,
+	.mode_flags = NT51021_BURST_MODE_FLAGS,
+};
+
+static const struct novatek_nt51021_variant boe_tv101wum_nm0_data = {
+	.init = boe_tv101wum_nm0_init,
+	.display_mode = &boe_tv101wum_nm0_mode,
+	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM,
 };
 
 static const struct novatek_nt51021_variant boe_tv101wum_nx0_data = {
 	.init = boe_tv101wum_nx0_init,
 	.display_mode = &boe_tv101wum_nx0_mode,
+	.mode_flags = NT51021_BURST_MODE_FLAGS,
 };
 
 static const struct of_device_id nt51021_novatek_of_match[] = {
 	{ .compatible = "boe,tv080wum-nx2", .data = &boe_tv080wum_nx2_data },
+	{ .compatible = "boe,tv101wum-nm0", .data = &boe_tv101wum_nm0_data },
 	{ .compatible = "boe,tv101wum-nx0", .data = &boe_tv101wum_nx0_data },
 	{ /* sentinel */ }
 };
-- 
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