From a92ed345422cd66329b5e9d4e82b0452e185f36f Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 9 Sep 2026 17:13:46 -0300 Subject: [PATCH 001/109] drm/panel: simple: Add Raystar RFF500F-AWH-DNN enable delay The RFF500F-AWH-DNN datasheet requires a minimum delay of 250 ms between display signal output and backlight power-on. Without this delay, the backlight can expose unsettled panel contents during display startup, resulting in a brief bright flash. Set the panel enable delay to the datasheet-specified minimum. Cc: stable@vger.kernel.org Fixes: 1b2a9ec82099 ("drm/panel: simple: Add Raystar RFF500F-AWH-DNN panel entry") Signed-off-by: Fabio Estevam Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260909201346.937343-1-festevam@gmail.com --- drivers/gpu/drm/panel/panel-simple.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index d7dbd7442919..a944c34642dc 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -4379,6 +4379,9 @@ static const struct panel_desc raystar_rff500f_awh_dnn = { .width = 108, .height = 65, }, + .delay = { + .enable = 250, + }, .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, .connector_type = DRM_MODE_CONNECTOR_LVDS, }; From 7d2dffd72625784945b8a467b6d47fbed6fe7d74 Mon Sep 17 00:00:00 2001 From: Nickolay Goppen Date: Thu, 24 Sep 2026 18:08:14 +0300 Subject: [PATCH 002/109] dt-bindings: display: panel: Add Novatek NT51021 Novatek NT51021 is a display driver IC that can drive DSI panel. It is also present in the BOE TV101WUM-NX0 video mode panel, which is a WUXGA panel with a resolution of 1200x1920 and 10.1 inches size. It is found in Xiaomi Mi Pad 4 Plus. Also it's present in the BOE-TV080WUM-NX2 video mode panel, which also has a resolution of 1200x1920, but 8 inches size. It is found in Xiaomi Mi Pad 4. And also it's present in the BOE-TV101WUM-NM0. It's found in Amazon Fire HD 10 (2017). Add the bindings for display driver for the IC, with support added for this BOE TV101WUM-NX0 and TV080WUM-NX2 WUXGA video mode panels. Reviewed-by: Krzysztof Kozlowski Co-developed-by: Ryan Brue Signed-off-by: Ryan Brue Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Nickolay Goppen Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260924-qcom-novatek-nt51021-panels-v5-1-f5eaf5685c9c@mainlining.org --- .../display/panel/novatek,nt51021.yaml | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/novatek,nt51021.yaml diff --git a/Documentation/devicetree/bindings/display/panel/novatek,nt51021.yaml b/Documentation/devicetree/bindings/display/panel/novatek,nt51021.yaml new file mode 100644 index 000000000000..7754a8cb1e5c --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/novatek,nt51021.yaml @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/novatek,nt51021.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Novatek NT51021 based DSI display Panels + +maintainers: + - Nickolay Goppen + +description: + The NT51021 IC from Novatek is a generic DSI Panel IC used to drive dsi + panels. + Right now, support is added only for a BOE WUXGA LCD display panels with a + resolution of 1200x1920. They are video mode DSI panels. + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - enum: + - boe,tv101wum-nm0 + - boe,tv101wum-nx0 + - boe,tv080wum-nx2 + - const: novatek,nt51021 + + reg: + maxItems: 1 + + reset-gpios: + maxItems: 1 + description: phandle of gpio for reset line - This should be 8mA, gpio + can be configured using mux, pinctrl, pinctrl-names (active low) + + vdd-supply: + description: phandle of the regulator that provides the supply voltage + for digital circuits + + avdd-supply: + description: phandle of the regulator that provides the supply voltage + for analog circuits + + havdd-supply: + description: phandle of the regulator that provides the supply voltage + for analog circuits + +required: + - compatible + - reg + - vdd-supply + - avdd-supply + - havdd-supply + - reset-gpios + - port + +unevaluatedProperties: false + +examples: + - | + #include + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "boe,tv101wum-nx0", "novatek,nt51021"; + reg = <0>; + vdd-supply = <&lcd_pwr>; + avdd-supply = <&lcd_pwr>; + havdd-supply = <&lcd_pwr>; + + backlight = <&i2c_backlight>; + reset-gpios = <&tlmm 62 GPIO_ACTIVE_LOW>; + + port { + panel_in: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + }; + }; + +... From 94e45e3ad539291bdddb3cbf8f1803d42786c70f Mon Sep 17 00:00:00 2001 From: Nickolay Goppen Date: Thu, 24 Sep 2026 18:08:15 +0300 Subject: [PATCH 003/109] drivers: gpu: drm: panel: Add BOE NT51021 driver Add driver for BOE NT51021-based 8-inch and 10-inch DSI video mode panels used in Xiaomi Mi Pad 4, Mi Pad 4 Plus (xiaomi-clover) and Amazon Fire HD 10 (2017) (amazon-suez) tablets. NT51021-based panels also support content-adaptive brightness control by generating a PWM signal for backlight driver IC. Content adaptation means that content grey level scale can be increased while simultaneously decreasing brightness of the backlight to achieve same perceived brightness. This makes the power consumption of backlight be reduced with keeping acceptable display quality. Both panels are very similar, differences are only in init sequence and resolution. The register sequence of the TV101WUM-NM0 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 Tested-by: Ryan Brue # Amazon Fire HD 10 (2017), boe,tv101wum-nm0 Co-developed-by: Ryan Brue Signed-off-by: Ryan Brue Co-developed-by: Alexey Minnekhanov Signed-off-by: Alexey Minnekhanov Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Neil Armstrong Signed-off-by: Nickolay Goppen Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260924-qcom-novatek-nt51021-panels-v5-2-f5eaf5685c9c@mainlining.org --- drivers/gpu/drm/panel/Kconfig | 9 + drivers/gpu/drm/panel/Makefile | 1 + drivers/gpu/drm/panel/panel-novatek-nt51021.c | 875 ++++++++++++++++++ 3 files changed, 885 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-novatek-nt51021.c diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 53a1fc2913fe..a4ff4bef5eff 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -753,6 +753,15 @@ config DRM_PANEL_NOVATEK_NT39016 Say Y here if you want to enable support for the panels built around the Novatek NT39016 display controller. +config DRM_PANEL_NOVATEK_NT51021 + tristate "Novatek NT51021 DSI panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable support for the panels built + around the Novatek NT51021 Display controller. + config DRM_PANEL_OLIMEX_LCD_OLINUXINO tristate "Olimex LCD-OLinuXino panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index f2c9c80a218f..a271e6cef33d 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672E) += panel-novatek-nt36672e.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT37700F) += panel-novatek-nt37700f.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT37801) += panel-novatek-nt37801.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT39016) += panel-novatek-nt39016.o +obj-$(CONFIG_DRM_PANEL_NOVATEK_NT51021) += panel-novatek-nt51021.o obj-$(CONFIG_DRM_PANEL_MANTIX_MLAF057WE51) += panel-mantix-mlaf057we51.o obj-$(CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO) += panel-olimex-lcd-olinuxino.o obj-$(CONFIG_DRM_PANEL_ORISETECH_OTA5601A) += panel-orisetech-ota5601a.o diff --git a/drivers/gpu/drm/panel/panel-novatek-nt51021.c b/drivers/gpu/drm/panel/panel-novatek-nt51021.c new file mode 100644 index 000000000000..94e7d6aaf0cb --- /dev/null +++ b/drivers/gpu/drm/panel/panel-novatek-nt51021.c @@ -0,0 +1,875 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2026, Ryan Brue +// Copyright (c) 2025, Nickolay Goppen +// Copyright (c) 2024, Alexey Minnekhanov + +/* + * This panel IC doesn't use the set_display_on command although + * it uses set_display_off command. This is not an error. + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +struct novatek_nt51021_desc; + +struct novatek_nt51021_variant { + int (*init)(struct novatek_nt51021_desc *ctx); + int (*off)(struct novatek_nt51021_desc *ctx); + + const struct drm_display_mode *display_mode; + unsigned long mode_flags; +}; + +struct novatek_nt51021_desc { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + struct regulator_bulk_data *supplies; + struct gpio_desc *reset_gpio; + + const struct novatek_nt51021_variant *variant; +}; + +static const struct regulator_bulk_data nt51021_novatek_supplies[] = { + { .supply = "vdd" }, + { .supply = "avdd" }, + { .supply = "havdd" }, +}; + +static inline struct novatek_nt51021_desc *to_novatek_panel(struct drm_panel *panel) +{ + return container_of_const(panel, struct novatek_nt51021_desc, panel); +} + +static void nt51021_novatek_reset(struct novatek_nt51021_desc *ctx) +{ + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + msleep(20); + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + usleep_range(10000, 11000); +} + +static void nt51021_cabc_on(struct mipi_dsi_multi_context *dsi_ctx) +{ + if (dsi_ctx->accum_err) + return; + + mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x8f, 0x00); + mipi_dsi_usleep_range(dsi_ctx, 1000, 2000); + 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, 0xbb); + mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x84, 0x22); + mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x90, 0x40); + mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x8f, 0x00); + mipi_dsi_usleep_range(dsi_ctx, 1000, 2000); +} + +static void nt51021_cabc_off(struct mipi_dsi_multi_context *dsi_ctx) +{ + if (dsi_ctx->accum_err) + return; + + 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, 0xbb); + mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x84, 0x22); + mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x90, 0xc0); + mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x8f, 0x00); + mipi_dsi_usleep_range(dsi_ctx, 1000, 2000); +} + +static int boe_tv080wum_nx2_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, 0x8e); + 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, 0x8b, 0x10); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa9, 0x20); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x97, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0x15); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0x1b); + 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, 0x98, 0xc1); + 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, 0xc0, 0x05); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc1, 0x0c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc2, 0x22); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc3, 0x34); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc4, 0x44); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc5, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc6, 0x5d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc7, 0x66); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0x70); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc9, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xca, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcb, 0x3b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcc, 0x53); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x69); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xce, 0x6a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcf, 0x6c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd0, 0x6b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd1, 0x75); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd2, 0x83); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd3, 0x99); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd4, 0x9a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd5, 0xd0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd6, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd7, 0xdb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd8, 0xe1); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd9, 0xe7); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xda, 0xed); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdb, 0xf4); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdc, 0xfb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xde, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdf, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe0, 0x15); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe1, 0x1c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe2, 0x32); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe3, 0x44); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe4, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe5, 0x60); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe6, 0x6b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe7, 0x74); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe8, 0x7e); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe9, 0x14); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xea, 0x25); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xeb, 0x4d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xec, 0x63); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xed, 0x71); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xee, 0x6a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xef, 0x6c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x6b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf1, 0x75); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf2, 0x83); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf3, 0x99); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf4, 0x9a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf5, 0xd0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf6, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf7, 0xdb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf8, 0xe1); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf9, 0xe7); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0xed); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfb, 0xf4); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfc, 0xfb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfe, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xff, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xbb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x22); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa1, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa2, 0xfe); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa3, 0xfa); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa4, 0xf7); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa5, 0xf3); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa6, 0xf1); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa7, 0xed); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa8, 0xeb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa9, 0xe9); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xaa, 0xe6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xaf, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb0, 0x35); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb1, 0x89); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb2, 0x99); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb3, 0x99); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb4, 0x0d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb5, 0x1a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb6, 0x16); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9a, 0x10); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9b, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x96, 0xe6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x99, 0x06); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc0, 0x05); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc1, 0x0c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc2, 0x22); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc3, 0x34); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc4, 0x44); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc5, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc6, 0x5d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc7, 0x66); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0x70); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc9, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xca, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcb, 0x3b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcc, 0x53); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x69); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xce, 0x6a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcf, 0x6c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd0, 0x6b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd1, 0x75); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd2, 0x83); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd3, 0x99); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd4, 0x9a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd5, 0xd0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd6, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd7, 0xdb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd8, 0xe1); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd9, 0xe7); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xda, 0xed); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdb, 0xf4); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdc, 0xfb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xde, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdf, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe0, 0x15); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe1, 0x1c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe2, 0x32); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe3, 0x44); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe4, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe5, 0x60); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe6, 0x6b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe7, 0x74); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe8, 0x7e); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe9, 0x14); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xea, 0x25); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xeb, 0x4d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xec, 0x63); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xed, 0x71); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xee, 0x6a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xef, 0x6c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x6b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf1, 0x75); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf2, 0x83); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf3, 0x99); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf4, 0x9a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf5, 0xd0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf6, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf7, 0xdb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf8, 0xe1); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf9, 0xe7); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0xed); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfb, 0xf4); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfc, 0xfb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfe, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xff, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xcc); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x33); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc0, 0x05); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc1, 0x0c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc2, 0x22); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc3, 0x34); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc4, 0x44); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc5, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc6, 0x5d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc7, 0x66); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0x70); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc9, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xca, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcb, 0x3b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcc, 0x53); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x69); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xce, 0x6a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcf, 0x6c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd0, 0x6b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd1, 0x75); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd2, 0x83); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd3, 0x99); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd4, 0x9a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd5, 0xd0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd6, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd7, 0xdb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd8, 0xe1); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd9, 0xe7); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xda, 0xed); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdb, 0xf4); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdc, 0xfb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xde, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdf, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe0, 0x15); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe1, 0x1c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe2, 0x32); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe3, 0x44); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe4, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe5, 0x60); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe6, 0x6b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe7, 0x74); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe8, 0x7e); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe9, 0x14); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xea, 0x25); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xeb, 0x4d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xec, 0x63); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xed, 0x71); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xee, 0x6a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xef, 0x6c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x6b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf1, 0x75); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf2, 0x83); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf3, 0x99); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf4, 0x9a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf5, 0xd0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf6, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf7, 0xdb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf8, 0xe1); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf9, 0xe7); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0xed); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfb, 0xf4); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfc, 0xfb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfe, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xff, 0x0f); + 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, 0x9f, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xbb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x22); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x94, 0xba); + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + mipi_dsi_usleep_range(&dsi_ctx, 5000, 6000); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0x00); + mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000); + + nt51021_cabc_on(&dsi_ctx); + + return dsi_ctx.accum_err; +} + +static int boe_tv101wum_nx0_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, 0x8e); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0x12); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0x1b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x6c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0xfc); + 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, 0x98, 0xc1); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9c, 0x10); + 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, 0xc0, 0x0e); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc1, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc2, 0x1f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc3, 0x30); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc4, 0x3f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc5, 0x4b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc6, 0x55); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc7, 0x5d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0x68); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc9, 0xe3); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xca, 0xea); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcb, 0x01); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcc, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xce, 0x07); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcf, 0x08); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd0, 0x09); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd1, 0x1d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd2, 0x31); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd3, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd4, 0x5b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd5, 0xb6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd6, 0xbd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd7, 0xc5); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd8, 0xcd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd9, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xda, 0xe0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdb, 0xeb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdc, 0xf8); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xde, 0xfc); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdf, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe0, 0x0e); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe1, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe2, 0x1f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe3, 0x30); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe4, 0x3f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe5, 0x4b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe6, 0x55); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe7, 0x5d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe8, 0x68); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe9, 0xe3); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xea, 0xea); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xeb, 0x01); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xec, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xed, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xee, 0x07); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xef, 0x08); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x09); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf1, 0x1d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf2, 0x31); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf3, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf4, 0x5b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf5, 0xb6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf6, 0xbd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf7, 0xc5); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf8, 0xcd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf9, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0xe0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfb, 0xeb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfc, 0xf8); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfe, 0xfc); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xff, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xbb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x22); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa1, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa2, 0xfe); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa3, 0xfa); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa4, 0xf7); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa5, 0xf3); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa6, 0xf1); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa7, 0xed); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa8, 0xeb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa9, 0xe9); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xaa, 0xe6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xaf, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb0, 0x12); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb1, 0x23); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb2, 0x34); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb3, 0x77); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb4, 0x0d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb5, 0x1a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb6, 0x16); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9a, 0x10); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9b, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x96, 0xe6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x99, 0x06); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc0, 0x0e); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc1, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc2, 0x1f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc3, 0x30); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc4, 0x3f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc5, 0x4b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc6, 0x55); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc7, 0x5d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0x68); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc9, 0xe3); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xca, 0xea); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcb, 0x01); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcc, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xce, 0x07); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcf, 0x08); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd0, 0x09); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd1, 0x1d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd2, 0x31); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd3, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd4, 0x5b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd5, 0xb6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd6, 0xbd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd7, 0xc5); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd8, 0xcd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd9, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xda, 0xe0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdb, 0xeb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdc, 0xf8); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xde, 0xfc); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdf, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe0, 0x0e); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe1, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe2, 0x1f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe3, 0x30); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe4, 0x3f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe5, 0x4b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe6, 0x55); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe7, 0x5d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe8, 0x68); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe9, 0xe3); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xea, 0xea); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xeb, 0x01); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xec, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xed, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xee, 0x07); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xef, 0x08); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x09); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf1, 0x1d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf2, 0x31); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf3, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf4, 0x5b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf5, 0xb6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf6, 0xbd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf7, 0xc5); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf8, 0xcd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf9, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0xe0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfb, 0xeb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfc, 0xf8); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfe, 0xfc); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xff, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xcc); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x33); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc0, 0x0e); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc1, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc2, 0x1f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc3, 0x30); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc4, 0x3f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc5, 0x4b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc6, 0x55); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc7, 0x5d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0x68); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc9, 0xe3); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xca, 0xea); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcb, 0x01); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcc, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xce, 0x07); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcf, 0x08); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd0, 0x09); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd1, 0x1d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd2, 0x31); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd3, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd4, 0x5b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd5, 0xb6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd6, 0xbd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd7, 0xc5); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd8, 0xcd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd9, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xda, 0xe0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdb, 0xeb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdc, 0xf8); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xde, 0xfc); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdf, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe0, 0x0e); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe1, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe2, 0x1f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe3, 0x30); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe4, 0x3f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe5, 0x4b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe6, 0x55); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe7, 0x5d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe8, 0x68); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe9, 0xe3); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xea, 0xea); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xeb, 0x01); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xec, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xed, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xee, 0x07); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xef, 0x08); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x09); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf1, 0x1d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf2, 0x31); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf3, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf4, 0x5b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf5, 0xb6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf6, 0xbd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf7, 0xc5); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf8, 0xcd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf9, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0xe0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfb, 0xeb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfc, 0xf8); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfe, 0xfc); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xff, 0x0f); + 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, 0x9f, 0xff); + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + mipi_dsi_usleep_range(&dsi_ctx, 5000, 6000); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0x00); + mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000); + + nt51021_cabc_on(&dsi_ctx); + mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000); + + 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_with_cabc_off(struct novatek_nt51021_desc *ctx) +{ + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; + + nt51021_cabc_off(&dsi_ctx); + + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0xa5); + mipi_dsi_msleep(&dsi_ctx, 20); + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 130); + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 70); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0x00); + mipi_dsi_usleep_range(&dsi_ctx, 4000, 5000); + + 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 }; + + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0xa5); + mipi_dsi_msleep(&dsi_ctx, 20); + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 130); + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 70); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0x00); + mipi_dsi_usleep_range(&dsi_ctx, 4000, 5000); + + return dsi_ctx.accum_err; +} + +static int nt51021_novatek_prepare(struct drm_panel *panel) +{ + struct novatek_nt51021_desc *ctx = to_novatek_panel(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(nt51021_novatek_supplies), ctx->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + nt51021_novatek_reset(ctx); + + ret = ctx->variant->init(ctx); + if (ret < 0) { + dev_err(dev, "Failed to initialize panel: %d\n", ret); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(nt51021_novatek_supplies), ctx->supplies); + return ret; + } + + return 0; +} + +static int nt51021_novatek_unprepare(struct drm_panel *panel) +{ + struct novatek_nt51021_desc *ctx = to_novatek_panel(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + ret = ctx->variant->off(ctx); + if (ret < 0) + dev_err(dev, "Failed to un-initialize panel: %d\n", ret); + + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(nt51021_novatek_supplies), ctx->supplies); + + return 0; +} + +static int nt51021_novatek_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct novatek_nt51021_desc *ctx = to_novatek_panel(panel); + + return drm_connector_helper_get_modes_fixed(connector, + ctx->variant->display_mode); +} + +static const struct drm_panel_funcs nt51021_novatek_panel_funcs = { + .prepare = nt51021_novatek_prepare, + .unprepare = nt51021_novatek_unprepare, + .get_modes = nt51021_novatek_get_modes, +}; + +static int nt51021_novatek_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct novatek_nt51021_desc *ctx; + int ret; + + ctx = devm_drm_panel_alloc(dev, struct novatek_nt51021_desc, panel, + &nt51021_novatek_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + ctx->variant = of_device_get_match_data(dev); + if (!ctx->variant) + return dev_err_probe(dev, -ENODEV, "No match data\n"); + + ret = devm_regulator_bulk_get_const(dev, + ARRAY_SIZE(nt51021_novatek_supplies), + nt51021_novatek_supplies, + &ctx->supplies); + if (ret < 0) + return ret; + + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ctx->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), + "Failed to get reset-gpios\n"); + + ctx->dsi = dsi; + mipi_dsi_set_drvdata(dsi, ctx); + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = ctx->variant->mode_flags; + + ctx->panel.prepare_prev_first = true; + + ret = drm_panel_of_backlight(&ctx->panel); + if (ret) + return dev_err_probe(dev, ret, "Failed to get backlight\n"); + + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; + + return devm_mipi_dsi_attach(dev, dsi); +} + +static const struct drm_display_mode boe_tv080wum_nx2_mode = { + .clock = (1200 + 84 + 4 + 60) * (1920 + 36 + 1 + 25) * 60 / 1000, + .hdisplay = 1200, + .hsync_start = 1200 + 84, + .hsync_end = 1200 + 84 + 4, + .htotal = 1200 + 84 + 4 + 60, + .vdisplay = 1920, + .vsync_start = 1920 + 36, + .vsync_end = 1920 + 36 + 1, + .vtotal = 1920 + 36 + 1 + 25, + .width_mm = 107, + .height_mm = 172, + .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, + .hsync_start = 1200 + 172, + .hsync_end = 1200 + 172 + 4, + .htotal = 1200 + 172 + 4 + 32, + .vdisplay = 1920, + .vsync_start = 1920 + 25, + .vsync_end = 1920 + 25 + 1, + .vtotal = 1920 + 25 + 1 + 14, + .width_mm = 135, + .height_mm = 216, + .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, + .off = nt51021_novatek_with_cabc_off, + .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, + .off = nt51021_novatek_off, + .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, + .off = nt51021_novatek_with_cabc_off, + .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 */ } +}; +MODULE_DEVICE_TABLE(of, nt51021_novatek_of_match); + +static struct mipi_dsi_driver nt51021_boe_driver = { + .probe = nt51021_novatek_probe, + .driver = { + .name = "panel-novatek-nt51021", + .of_match_table = nt51021_novatek_of_match, + }, +}; +module_mipi_dsi_driver(nt51021_boe_driver); + +MODULE_AUTHOR("Nickolay Goppen "); +MODULE_AUTHOR("Alexey Minnekhanov "); +MODULE_AUTHOR("Ryan Brue "); +MODULE_DESCRIPTION("DRM driver for Novatek NT51021-based DSI panels"); +MODULE_LICENSE("GPL"); From 27282474cd58e274bc866fb881643cf93a1c01c5 Mon Sep 17 00:00:00 2001 From: Nickolay Goppen Date: Thu, 24 Sep 2026 18:08:16 +0300 Subject: [PATCH 004/109] MAINTAINERS: Add myself and Ryan Brue as maintainers of NT51021 panel driver Add a maintainer entry for the new NT51021 panel driver Signed-off-by: Nickolay Goppen Reviewed-by: Neil Armstrong Tested-by: Ryan Brue # Amazon Fire HD 10 Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260924-qcom-novatek-nt51021-panels-v5-3-f5eaf5685c9c@mainlining.org --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 4d108339f44e..6d8a64bc1e03 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8360,6 +8360,13 @@ S: Maintained F: Documentation/devicetree/bindings/display/panel/novatek,nt37801.yaml F: drivers/gpu/drm/panel/panel-novatek-nt37801.c +DRM DRIVER FOR NOVATEK NT51021 PANELS +M: Ryan Brue +M: Nickolay Goppen +S: Maintained +F: Documentation/devicetree/bindings/display/panel/novatek,nt51021.yaml +F: drivers/gpu/drm/panel/panel-novatek-nt51021.c + DRM DRIVER FOR NVIDIA GEFORCE/QUADRO GPUS M: Lyude Paul M: Danilo Krummrich From 4cfc23733f15d75a787bcb370c6739cd954e74d5 Mon Sep 17 00:00:00 2001 From: Aaron Kling Date: Fri, 25 Sep 2026 02:08:19 -0500 Subject: [PATCH 005/109] dt-bindings: display: visionox,vtdr6130: Add Retroid Pocket 6 panel The vtdr6130 is a display driver integrated circuit, a common chip used for various panels. One such panel is used in the Retroid Pocket 6. The RP6 panel only lists the regulators vddio and vci, so drop vdd as a required property. The panel is rotated 270 degrees to natural, so also add rotation as an allowed property. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Aaron Kling Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260925-rp6-panel-v2-1-19c425e523de@gmail.com --- .../bindings/display/panel/visionox,vtdr6130.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/display/panel/visionox,vtdr6130.yaml b/Documentation/devicetree/bindings/display/panel/visionox,vtdr6130.yaml index c99f4146f1bb..682f30dc9ee4 100644 --- a/Documentation/devicetree/bindings/display/panel/visionox,vtdr6130.yaml +++ b/Documentation/devicetree/bindings/display/panel/visionox,vtdr6130.yaml @@ -14,7 +14,11 @@ allOf: properties: compatible: - const: visionox,vtdr6130 + oneOf: + - const: visionox,vtdr6130 + - items: + - const: retroidpocket,rp6-panel + - const: visionox,vtdr6130 reg: maxItems: 1 @@ -25,13 +29,13 @@ properties: vdd-supply: true port: true reset-gpios: true + rotation: true required: - compatible - reg - vddio-supply - vci-supply - - vdd-supply - reset-gpios - port From 0de9104a85cb6efb624e6afacfc252855fdde8b0 Mon Sep 17 00:00:00 2001 From: Aaron Kling Date: Fri, 25 Sep 2026 02:08:20 -0500 Subject: [PATCH 006/109] drm/panel: visionox-vtdr6130: Add panel orientation support Fetch orientation from the device tree if provided. Reviewed-by: Neil Armstrong Signed-off-by: Aaron Kling Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260925-rp6-panel-v2-2-19c425e523de@gmail.com --- drivers/gpu/drm/panel/panel-visionox-vtdr6130.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c index 6733e0e1d061..b66d6c7128c7 100644 --- a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c +++ b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -22,6 +23,7 @@ struct visionox_vtdr6130 { struct mipi_dsi_device *dsi; struct gpio_desc *reset_gpio; struct regulator_bulk_data *supplies; + enum drm_panel_orientation orientation; }; static const struct regulator_bulk_data visionox_vtdr6130_supplies[] = { @@ -218,10 +220,18 @@ static int visionox_vtdr6130_get_modes(struct drm_panel *panel, return 1; } +static enum drm_panel_orientation visionox_vtdr6130_get_orientation(struct drm_panel *panel) +{ + struct visionox_vtdr6130 *ctx = to_visionox_vtdr6130(panel); + + return ctx->orientation; +} + static const struct drm_panel_funcs visionox_vtdr6130_panel_funcs = { .prepare = visionox_vtdr6130_prepare, .unprepare = visionox_vtdr6130_unprepare, .get_modes = visionox_vtdr6130_get_modes, + .get_orientation = visionox_vtdr6130_get_orientation, }; static int visionox_vtdr6130_bl_update_status(struct backlight_device *bl) @@ -293,6 +303,12 @@ static int visionox_vtdr6130_probe(struct mipi_dsi_device *dsi) MIPI_DSI_CLOCK_NON_CONTINUOUS; ctx->panel.prepare_prev_first = true; + ret = drm_of_get_panel_orientation(dev->of_node, &ctx->orientation); + if (ret < 0) { + dev_err(dev, "Failed to get orientation %d\n", ret); + return ret; + } + ctx->panel.backlight = visionox_vtdr6130_create_backlight(dsi); if (IS_ERR(ctx->panel.backlight)) return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), From 628e7bff43c7ee3234a309fc58741eeecf2477eb Mon Sep 17 00:00:00 2001 From: Aaron Kling Date: Fri, 25 Sep 2026 02:08:21 -0500 Subject: [PATCH 007/109] drm/panel: visionox-vtdr6130: Modularize panel config The vtdr6130 is a DDIC, a common chip that is used by multiple panels. Prepare the driver for more panels. Signed-off-by: Aaron Kling Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260925-rp6-panel-v2-3-19c425e523de@gmail.com --- .../gpu/drm/panel/panel-visionox-vtdr6130.c | 91 +++++++++++++------ 1 file changed, 61 insertions(+), 30 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c index b66d6c7128c7..278f5acc2d66 100644 --- a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c +++ b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c @@ -19,11 +19,24 @@ struct visionox_vtdr6130 { struct drm_panel panel; - struct drm_dsc_config dsc; struct mipi_dsi_device *dsi; struct gpio_desc *reset_gpio; struct regulator_bulk_data *supplies; enum drm_panel_orientation orientation; + struct visionox_vtdr6130_desc *desc; +}; + +struct visionox_vtdr6130_desc { + unsigned int bpc; + unsigned int lanes; + unsigned long mode_flags; + enum mipi_dsi_pixel_format format; + + const struct drm_display_mode *modes; + unsigned int num_modes; + int (*init_sequence)(struct visionox_vtdr6130 *ctx); + + struct drm_dsc_config dsc; }; static const struct regulator_bulk_data visionox_vtdr6130_supplies[] = { @@ -162,7 +175,7 @@ static int visionox_vtdr6130_prepare(struct drm_panel *panel) visionox_vtdr6130_reset(ctx); - ret = visionox_vtdr6130_on(ctx); + ret = ctx->desc->init_sequence(ctx); if (ret < 0) { gpiod_set_value_cansleep(ctx->reset_gpio, 1); regulator_bulk_disable(ARRAY_SIZE(visionox_vtdr6130_supplies), @@ -187,26 +200,50 @@ static int visionox_vtdr6130_unprepare(struct drm_panel *panel) return 0; } -static const struct drm_display_mode visionox_vtdr6130_mode = { - .clock = (1080 + 20 + 2 + 20) * (2400 + 20 + 2 + 18) * 144 / 1000, - .hdisplay = 1080, - .hsync_start = 1080 + 20, - .hsync_end = 1080 + 20 + 2, - .htotal = 1080 + 20 + 2 + 20, - .vdisplay = 2400, - .vsync_start = 2400 + 20, - .vsync_end = 2400 + 20 + 2, - .vtotal = 2400 + 20 + 2 + 18, - .width_mm = 71, - .height_mm = 157, +static const struct drm_display_mode visionox_vtdr6130_modes[] = { + { + .clock = (1080 + 20 + 2 + 20) * (2400 + 20 + 2 + 18) * 144 / 1000, + .hdisplay = 1080, + .hsync_start = 1080 + 20, + .hsync_end = 1080 + 20 + 2, + .htotal = 1080 + 20 + 2 + 20, + .vdisplay = 2400, + .vsync_start = 2400 + 20, + .vsync_end = 2400 + 20 + 2, + .vtotal = 2400 + 20 + 2 + 18, + .width_mm = 71, + .height_mm = 157, + }, +}; + +static struct visionox_vtdr6130_desc visionox_vtdr6130_panel_desc = { + .modes = visionox_vtdr6130_modes, + .num_modes = ARRAY_SIZE(visionox_vtdr6130_modes), + .bpc = 8, + .lanes = 4, + .format = MIPI_DSI_FMT_RGB888, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_NO_EOT_PACKET | + MIPI_DSI_CLOCK_NON_CONTINUOUS, + .init_sequence = visionox_vtdr6130_on, + .dsc = { + .dsc_version_major = 0x1, + .dsc_version_minor = 0x2, + .slice_height = 40, + .slice_width = 540, + .slice_count = 2, + .bits_per_component = 8, + .bits_per_pixel = 8 << 4, + .block_pred_enable = true, + }, }; static int visionox_vtdr6130_get_modes(struct drm_panel *panel, struct drm_connector *connector) { + struct visionox_vtdr6130 *ctx = to_visionox_vtdr6130(panel); struct drm_display_mode *mode; - mode = drm_mode_duplicate(connector->dev, &visionox_vtdr6130_mode); + mode = drm_mode_duplicate(connector->dev, ctx->desc->modes); if (!mode) return -ENOMEM; @@ -284,23 +321,17 @@ static int visionox_vtdr6130_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "Failed to get reset-gpios\n"); + ctx->desc = (struct visionox_vtdr6130_desc *)of_device_get_match_data(dev); + if (!ctx->desc) + return -ENODEV; + ctx->dsi = dsi; mipi_dsi_set_drvdata(dsi, ctx); - ctx->dsc.dsc_version_major = 0x1; - ctx->dsc.dsc_version_minor = 0x2; - ctx->dsc.slice_height = 40; - ctx->dsc.slice_width = 540; - ctx->dsc.slice_count = 2; - ctx->dsc.bits_per_component = 8; - ctx->dsc.bits_per_pixel = 8 << 4; - ctx->dsc.block_pred_enable = true; - - dsi->dsc = &ctx->dsc; - dsi->lanes = 4; - dsi->format = MIPI_DSI_FMT_RGB888; - dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_NO_EOT_PACKET | - MIPI_DSI_CLOCK_NON_CONTINUOUS; + dsi->dsc = &ctx->desc->dsc; + dsi->lanes = ctx->desc->lanes; + dsi->format = ctx->desc->format; + dsi->mode_flags = ctx->desc->mode_flags; ctx->panel.prepare_prev_first = true; ret = drm_of_get_panel_orientation(dev->of_node, &ctx->orientation); @@ -322,7 +353,7 @@ static int visionox_vtdr6130_probe(struct mipi_dsi_device *dsi) } static const struct of_device_id visionox_vtdr6130_of_match[] = { - { .compatible = "visionox,vtdr6130" }, + { .compatible = "visionox,vtdr6130", .data = &visionox_vtdr6130_panel_desc }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, visionox_vtdr6130_of_match); From 74b7c75f7d442cc00fa759c22f746abaa70f264f Mon Sep 17 00:00:00 2001 From: Aaron Kling Date: Fri, 25 Sep 2026 02:08:22 -0500 Subject: [PATCH 008/109] drm/panel: visionox-vtdr6130: Add Retroid Pocket 6 panel Add support for the 1080x1920 panel used in the Retroid Pocket 6. Reviewed-by: Neil Armstrong Signed-off-by: Aaron Kling Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260925-rp6-panel-v2-4-19c425e523de@gmail.com --- .../gpu/drm/panel/panel-visionox-vtdr6130.c | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c index 278f5acc2d66..d0db128cda81 100644 --- a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c +++ b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c @@ -60,6 +60,82 @@ static void visionox_vtdr6130_reset(struct visionox_vtdr6130 *ctx) usleep_range(10000, 11000); } +static int retroidpocket_rp6_on(struct visionox_vtdr6130 *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; + struct drm_dsc_picture_parameter_set pps; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x01); + mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, + MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20); + mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, 0xff07); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6c, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x00); + + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x11, 0x00, 0x00, 0x89, + 0x30, 0x80, 0x07, 0x80, 0x04, 0x38, 0x00, + 0x14, 0x02, 0x1c, 0x02, 0x1c, 0x02, 0x00, + 0x02, 0x25, 0x00, 0x20, 0x01, 0xd5, 0x00, + 0x07, 0x00, 0x0d, 0x05, 0x7a, 0x05, 0x16, + 0x18, 0x00, 0x10, 0xf0, 0x03, 0x0c, 0x20, + 0x00, 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, + 0x2a, 0x38, 0x46, 0x54, 0x62, 0x69, 0x70, + 0x77, 0x79, 0x7b, 0x7d, 0x7e, 0x01, 0x02, + 0x01, 0x00, 0x09, 0x40, 0x09, 0xbe, 0x19, + 0xfc, 0x19, 0xfa, 0x19, 0xf8, 0x1a, 0x38, + 0x1a, 0x78, 0x1a, 0xb6, 0x2a, 0xb6, 0x2a, + 0xf4, 0x2a, 0xf4, 0x4b, 0x34, 0x63, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xaa, 0x10); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb1, 0x02, 0x15, 0x00, 0x14, + 0x00, 0x20, 0x00, 0x02, 0x16, 0x00, 0x14, + 0x07, 0xb4, 0x00, 0x02, 0x16, 0x00, 0x14, + 0x00, 0x10, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xaa, 0x14); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb2, 0x03, 0x33); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb4, 0x0a, 0x72, 0x00, 0x00, + 0x0a, 0x72, 0x00, 0x00, 0x0a, 0x72, 0x00, + 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb5, 0x00, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x06, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb9, 0x00, 0x00, 0x08, 0x1e, + 0x1e, 0x1e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbc, 0x10, 0x00, 0x00, 0x08, + 0x11, 0x1e, 0x7c, 0x1e, 0x7c, 0x1e, 0x7c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbe, 0x10, 0x10, 0x00, 0x0c, + 0x22, 0x1e, 0x62, 0x1e, 0x62, 0x1e, 0x62); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x80); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x13); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfd, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x81); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf9, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x83); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf8, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xaa, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x00); + + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 150); + + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 50); + + drm_dsc_pps_payload_pack(&pps, dsi->dsc); + mipi_dsi_picture_parameter_set_multi(&dsi_ctx, &pps); + mipi_dsi_compression_mode_multi(&dsi_ctx, true); + + return dsi_ctx.accum_err; +} + static int visionox_vtdr6130_on(struct visionox_vtdr6130 *ctx) { struct mipi_dsi_device *dsi = ctx->dsi; @@ -200,6 +276,43 @@ static int visionox_vtdr6130_unprepare(struct drm_panel *panel) return 0; } +static const struct drm_display_mode retroidpocket_rp6_modes[] = { + { + .clock = (1080 + 22 + 2 + 16) * (1920 + 16 + 2 + 20) * 120 / 1000, + .hdisplay = 1080, + .hsync_start = 1080 + 22, + .hsync_end = 1080 + 22 + 2, + .htotal = 1080 + 22 + 2 + 16, + .vdisplay = 1920, + .vsync_start = 1920 + 16, + .vsync_end = 1920 + 16 + 2, + .vtotal = 1920 + 16 + 2 + 20, + .width_mm = 69, + .height_mm = 122, + }, +}; + +static struct visionox_vtdr6130_desc retroidpocket_rp6_panel_desc = { + .modes = retroidpocket_rp6_modes, + .num_modes = ARRAY_SIZE(retroidpocket_rp6_modes), + .bpc = 8, + .lanes = 4, + .format = MIPI_DSI_FMT_RGB888, + .mode_flags = MIPI_DSI_MODE_NO_EOT_PACKET | + MIPI_DSI_CLOCK_NON_CONTINUOUS, + .init_sequence = retroidpocket_rp6_on, + .dsc = { + .dsc_version_major = 0x1, + .dsc_version_minor = 0x1, + .slice_height = 12, + .slice_width = 540, + .slice_count = 2, + .bits_per_component = 8, + .bits_per_pixel = 8 << 4, + .block_pred_enable = true, + }, +}; + static const struct drm_display_mode visionox_vtdr6130_modes[] = { { .clock = (1080 + 20 + 2 + 20) * (2400 + 20 + 2 + 18) * 144 / 1000, @@ -353,6 +466,7 @@ static int visionox_vtdr6130_probe(struct mipi_dsi_device *dsi) } static const struct of_device_id visionox_vtdr6130_of_match[] = { + { .compatible = "retroidpocket,rp6-panel", .data = &retroidpocket_rp6_panel_desc }, { .compatible = "visionox,vtdr6130", .data = &visionox_vtdr6130_panel_desc }, { /* sentinel */ } }; From f11277810402d0e2f3f8e64618cdb5af2ade8d3f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 25 Sep 2026 15:01:00 +0200 Subject: [PATCH 009/109] drm/panel: fix novatek-nt36536 dependencies The recently added driver requires access to DSC helper code and fails to link without that: ld.lld: error: undefined symbol: drm_dsc_pps_payload_pack >>> referenced by panel-novatek-nt36536.c:109 (/home/arnd/arm-soc/drivers/gpu/drm/panel/panel-novatek-nt36536.c:109) >>> drivers/gpu/drm/panel/panel-novatek-nt36536.o:(novatek_prepare) in archive vmlinux.a Add the usual Kconfig dependency. Fixes: 75a5dbd1f4f7 ("drm/panel: Add Novatek NT36536 panel driver") Signed-off-by: Arnd Bergmann Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260925130125.3897046-1-arnd@kernel.org --- drivers/gpu/drm/panel/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index a4ff4bef5eff..aacff727904d 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -695,6 +695,8 @@ config DRM_PANEL_NOVATEK_NT36536 depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_DSC_HELPER + select DRM_DISPLAY_HELPER select DRM_KMS_HELPER help Say Y here if you want to enable support for Novatek NT36536-based From f5c3d702f4ccd1e953436c6a16e542e232a743ea Mon Sep 17 00:00:00 2001 From: Hsieh Hung-En Date: Tue, 29 Sep 2026 16:01:43 +0800 Subject: [PATCH 010/109] drm/panel: ronbo-rb070d30: Use mipi_dsi_*_multi() functions Replace the deprecated mipi_dsi_dcs_exit_sleep_mode() and mipi_dsi_dcs_enter_sleep_mode() calls in the enable and disable callbacks with their _multi variants, as suggested in Documentation/gpu/todo.rst. The callbacks retain the same return values. The new helpers additionally log an error if the command fails. Signed-off-by: Hsieh Hung-En Reviewed-by: Douglas Anderson Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260929080143.559685-1-hungen3108@gmail.com --- drivers/gpu/drm/panel/panel-ronbo-rb070d30.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c b/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c index c3fbc459c7e0..ebe6352db6c1 100644 --- a/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c +++ b/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c @@ -75,15 +75,19 @@ static int rb070d30_panel_unprepare(struct drm_panel *panel) static int rb070d30_panel_enable(struct drm_panel *panel) { struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel); + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; - return mipi_dsi_dcs_exit_sleep_mode(ctx->dsi); + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + return dsi_ctx.accum_err; } static int rb070d30_panel_disable(struct drm_panel *panel) { struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel); + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; - return mipi_dsi_dcs_enter_sleep_mode(ctx->dsi); + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); + return dsi_ctx.accum_err; } /* Default timings */ From 00efe8a685658deb7033d68fcdbf0a7e6796352e Mon Sep 17 00:00:00 2001 From: Hsieh Hung-En Date: Thu, 1 Oct 2026 08:49:40 +0800 Subject: [PATCH 011/109] drm/panel: ronbo-rb070d30: Ignore DCS errors in disable() If rb070d30_panel_disable() returns an error, drm_panel_disable() skips clearing panel->enabled. unprepare() still powers the panel off, but the next drm_panel_enable() sees the panel as already enabled and does nothing, so the panel stays dark. Always return 0 from disable(). A failed ENTER_SLEEP_MODE is still logged by mipi_dsi_dcs_enter_sleep_mode_multi(). Suggested-by: Douglas Anderson Signed-off-by: Hsieh Hung-En Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20261001004940.110776-1-hungen3108@gmail.com --- drivers/gpu/drm/panel/panel-ronbo-rb070d30.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c b/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c index ebe6352db6c1..9adf97aba5d3 100644 --- a/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c +++ b/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c @@ -87,7 +87,13 @@ static int rb070d30_panel_disable(struct drm_panel *panel) struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); - return dsi_ctx.accum_err; + + /* + * Returning an error would leave panel->enabled set, causing the + * next enable to be skipped even after unprepare powers off the panel. + * The helper above already logs any command failure. + */ + return 0; } /* Default timings */ From dfb7ce7880264ec69579db6c309c00f6c788256a Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Fri, 2 Oct 2026 01:51:41 +0000 Subject: [PATCH 012/109] dt-bindings: display: panel: Document Raydium RM69220 DDIC Document the Raydium RM69220 DDIC and the BOE BF068MWM-TD0 6.77" 1080x2392 AMOLED display panel used in the Nothing Phone (3a). Reviewed-by: Krzysztof Kozlowski Signed-off-by: Alexander Koskovich Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20261001-asteroids-panel-support-v4-1-fb228194ad91@pm.me --- .../display/panel/raydium,rm69220.yaml | 74 +++++++++++++++++++ MAINTAINERS | 5 ++ 2 files changed, 79 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/raydium,rm69220.yaml diff --git a/Documentation/devicetree/bindings/display/panel/raydium,rm69220.yaml b/Documentation/devicetree/bindings/display/panel/raydium,rm69220.yaml new file mode 100644 index 000000000000..3c94db88f797 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/raydium,rm69220.yaml @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/raydium,rm69220.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Raydium RM69220 based MIPI-DSI panels + +maintainers: + - Alexander Koskovich + +description: + The Raydium RM69220 is a generic DSI Panel IC used to control + AMOLED panels. + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - enum: + - boe,bf068mwm-td0 + - const: raydium,rm69220 + + reg: + maxItems: 1 + + dvdd-supply: + description: Digital voltage rail + + vci-supply: + description: Analog voltage rail + + vddio-supply: + description: I/O voltage rail + +required: + - compatible + - reg + - dvdd-supply + - vci-supply + - vddio-supply + - reset-gpios + - port + +unevaluatedProperties: false + +examples: + - | + #include + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "boe,bf068mwm-td0", "raydium,rm69220"; + reg = <0>; + + reset-gpios = <&tlmm 12 GPIO_ACTIVE_LOW>; + dvdd-supply = <&vreg_oled_dvdd_1p8>; + vci-supply = <&vreg_l19b>; + vddio-supply = <&vreg_l9b>; + + port { + panel_in_0: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + }; + }; + +... diff --git a/MAINTAINERS b/MAINTAINERS index 6d8a64bc1e03..d005573ae4bf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8456,6 +8456,11 @@ S: Maintained F: Documentation/devicetree/bindings/display/panel/raydium,rm67191.yaml F: drivers/gpu/drm/panel/panel-raydium-rm67191.c +DRM DRIVER FOR RAYDIUM RM69220 PANELS +M: Alexander Koskovich +S: Maintained +F: Documentation/devicetree/bindings/display/panel/raydium,rm69220.yaml + DRM DRIVER FOR SAMSUNG DB7430 PANELS M: Linus Walleij S: Maintained From 54e61ea6c492d69a1717018826d8340810c812eb Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Fri, 2 Oct 2026 01:51:46 +0000 Subject: [PATCH 013/109] drm/panel: Add driver for Raydium RM69220 DDIC Add a panel driver for Raydium RM69220 DDIC based displays such as the BOE BF068MWM-TD0 found in the Nothing Phone (3a). Signed-off-by: Alexander Koskovich Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20261001-asteroids-panel-support-v4-2-fb228194ad91@pm.me --- MAINTAINERS | 1 + drivers/gpu/drm/panel/Kconfig | 15 + drivers/gpu/drm/panel/Makefile | 1 + drivers/gpu/drm/panel/panel-raydium-rm69220.c | 417 ++++++++++++++++++ 4 files changed, 434 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-raydium-rm69220.c diff --git a/MAINTAINERS b/MAINTAINERS index d005573ae4bf..a08087fb6e67 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8460,6 +8460,7 @@ DRM DRIVER FOR RAYDIUM RM69220 PANELS M: Alexander Koskovich S: Maintained F: Documentation/devicetree/bindings/display/panel/raydium,rm69220.yaml +F: drivers/gpu/drm/panel/panel-raydium-rm69220.c DRM DRIVER FOR SAMSUNG DB7430 PANELS M: Linus Walleij diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index aacff727904d..7a520b4d60fe 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -861,6 +861,21 @@ config DRM_PANEL_RAYDIUM_RM692E5 Say Y here if you want to enable support for Raydium RM692E5-based display panels, such as the one found in the Fairphone 5 smartphone. +config DRM_PANEL_RAYDIUM_RM69220 + tristate "Raydium RM69220-based DSI panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_DSC_HELPER + select DRM_DISPLAY_HELPER + help + Say Y here if you want to enable support for Raydium RM69220-based + display panels, such as the BOE BF068MWM-TD0 which can be found in + the Nothing Phone (3a). + + To compile this driver as a module, choose M here: the module + will be called panel-raydium-rm69220. + config DRM_PANEL_RAYDIUM_RM69380 tristate "Raydium RM69380-based DSI panel" depends on GPIOLIB diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index a271e6cef33d..da5175909d54 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -84,6 +84,7 @@ obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM67191) += panel-raydium-rm67191.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM67200) += panel-raydium-rm67200.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM692E5) += panel-raydium-rm692e5.o +obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM69220) += panel-raydium-rm69220.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM69380) += panel-raydium-rm69380.o obj-$(CONFIG_DRM_PANEL_RENESAS_R61307) += panel-renesas-r61307.o obj-$(CONFIG_DRM_PANEL_RENESAS_R63419) += panel-renesas-r63419.o diff --git a/drivers/gpu/drm/panel/panel-raydium-rm69220.c b/drivers/gpu/drm/panel/panel-raydium-rm69220.c new file mode 100644 index 000000000000..3030f8b89760 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-raydium-rm69220.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree. + * Copyright (c) 2026 Alexander Koskovich + */ + +#include +#include +#include +#include +#include +#include + +#include