From nobody Sat Sep 26 01:55:25 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 761DF2FF66A; Sun, 6 Sep 2026 01:34:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788658476; cv=none; b=Pw4X0Eh200qWsniVl9I2EkAEGQ+Z61mfIRh/jrA0B7PO3/A7EVXwKi/r32LBAIGq9E4CVHw7YCaHBNcUQEruL8PEU1hWOn5YAcqO58Cjt1NB2FvHLjuQ+Ndmd2f00OkexrdOxmMHMPKbsRPOBAGHa07YUhHY6PBlUpr4eN6NQ+w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788658476; c=relaxed/simple; bh=d9wUPYj1fcxFp3gWDGtuJ2V5k7ePRRGkBzD7EqpEMCE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ERje4/mq5oaK8BzeuUV6xpC0ujmZBCs9yB3mZeA/TMGCFKZV6FsS+Qnm0wUIwinRQ19O8WnOTHjW/hdue6MY6FP+xCLt/vrW9SgPWnc3AGaXfC9Foaji5E4mptpa8JYLPoczO704OmcgqQjatW98kOwS0vhmWVR+UroAe+ctKO8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=L46D8KSs; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="L46D8KSs" Received: by smtp.kernel.org (Postfix) with ESMTPS id 059C9C2BCFA; Sun, 6 Sep 2026 01:34:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1788658476; bh=d9wUPYj1fcxFp3gWDGtuJ2V5k7ePRRGkBzD7EqpEMCE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=L46D8KSsPQ6m5RQJrGzyVbUOq5P33crp9YIgw61zIpSiF3SJPONOe+eR6eUzpBFGf zsndtsZ3wQa+g4u52bi55E1d2ehoAkeFhV4jT4htV32QY8Vqbn48Xw3/AoZpqBKIt0 HF8URTVCzJOMG26l6VttxDEtsceUXJfY6aoqwLQ+M+6GJ70CkEKShRx/G6I5fw2UG4 jUXAPViFpw+hpRV9bb2v0jBb2GTNnO/OwIrqTQfp4sKJXuDK2uErNeyEPs7mj25n3X BbMzr0+je5pIftnTL3yxJjamT+eaxRUZA88F+YSgqNB8n7BF2Gst0AlDDa5Dy8BjrQ Xa41deH5cFS6Q== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id DAA58C624DB; Sun, 6 Sep 2026 01:34:35 +0000 (UTC) From: Ryan Brue via B4 Relay Date: Sat, 05 Sep 2026 20:34:34 -0500 Subject: [PATCH 1/2] dt-bindings: display: panel: add Novatek NT51021 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260905-rbrue-drm-panel-add-novatek-nt51021-v1-1-939f59e89fc9@gmail.com> References: <20260905-rbrue-drm-panel-add-novatek-nt51021-v1-0-939f59e89fc9@gmail.com> In-Reply-To: <20260905-rbrue-drm-panel-add-novatek-nt51021-v1-0-939f59e89fc9@gmail.com> To: Neil Armstrong , Jessica Zhang , David Airlie , Simona Vetter , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Matthias Brugger , AngeloGioacchino Del Regno Cc: dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org, Ryan Brue X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1788658475; l=3260; i=ryanbrue.dev@gmail.com; s=20260906; h=from:subject:message-id; bh=OucYPSbkdRKOXX71YqLEkAw3zmqM74CD8sy3ta2ahmU=; b=PO5JgYFHpu6BgI2fM/vrLk1wZB4hZ/5rErx2tt+ZqYUIvvlMj4yP4Y3HrLUwAg2A2X3qX421g G7XEq9T+aibC7+rL8hQK6/c9TtzLqO+v9sA48rNGBPQuNi3i5JUuimW X-Developer-Key: i=ryanbrue.dev@gmail.com; a=ed25519; pk=KsUvVaP//v/2q+ZBuacc7cLbsyEYn+AD71Sn28oZWKo= X-Endpoint-Received: by B4 Relay for ryanbrue.dev@gmail.com/20260906 with auth_id=1007 X-Original-From: Ryan Brue Reply-To: ryanbrue.dev@gmail.com From: Ryan Brue The Novatek NT51021 is a DSI panel controller driving 1200x1920 WUXGA panels over four lanes in video mode. It is fitted in the Amazon Fire HD 10 (2017), on a module carrying BOE glass. The controller has no readable panel identity. MediaTek's LCM framework identifies panels through a .compare_id callback and the vendor's driver for this one leaves it unset, and the glass part number is not recorded anywhere in the vendor tree -- it names the module only "BOE". I had the compatible name the module after the board it ships in, the way xiaomi,elish-boe-nt36523 and lenovo,j606f-boe-nt36523w do for this situation. The vendor's bootloader and kernel both carry a second register sequence for Innolux glass, selected from two board ID straps, so a second module exists as far as the vendor firmware is concerned. I didn't have an Innolux unit to test with, and I don't know its part number, so it is not described here. Naming the glass in the compatible leaves room for it as a second compatible should someone have the hardware and the need. The rest is panel-common: a power supply, a reset GPIO, an optional backlight and the DSI port. Assisted-by: LLM Signed-off-by: Ryan Brue --- .../bindings/display/panel/novatek,nt51021.yaml | 65 ++++++++++++++++++= ++++ 1 file changed, 65 insertions(+) diff --git a/Documentation/devicetree/bindings/display/panel/novatek,nt5102= 1.yaml b/Documentation/devicetree/bindings/display/panel/novatek,nt51021.ya= ml new file mode 100644 index 000000000000..ee3adc88ec9b --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/novatek,nt51021.yaml @@ -0,0 +1,65 @@ +# 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 1200x1920 DSI display panels + +maintainers: + - Ryan Brue + +description: + The Novatek NT51021 is a DSI panel controller driving 1200x1920 WUXGA + panels over four lanes in video mode. The controller carries no readable + panel identity, so each panel module it is fitted to needs its own + compatible naming the module. + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - const: amazon,suez-boe-nt51021 + - const: novatek,nt51021 + + reg: + maxItems: 1 + + power-supply: true + reset-gpios: true + backlight: true + port: true + +required: + - compatible + - reg + - power-supply + - reset-gpios + - port + +unevaluatedProperties: false + +examples: + - | + #include + + dsi { + #address-cells =3D <1>; + #size-cells =3D <0>; + + panel@0 { + compatible =3D "amazon,suez-boe-nt51021", "novatek,nt51021"; + reg =3D <0>; + backlight =3D <&backlight_lcd>; + power-supply =3D <&vgp6>; + reset-gpios =3D <&pio 105 GPIO_ACTIVE_LOW>; + + port { + endpoint { + remote-endpoint =3D <&dsi_out>; + }; + }; + }; + }; --=20 2.55.0 From nobody Sat Sep 26 01:55:25 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 76288306746; Sun, 6 Sep 2026 01:34:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788658476; cv=none; b=dSzcOOvPNQD8+CvLAtuvOMAxyIsl7Nf7p4n7lJCrz7LmTB3ZBxHC4qF1uj/13fWfzR9rr+6WX5p7wSJp+FWrcfqOjrt6ab88q3Xw7b0XAEN8mW1Yrjym7+oLqAqpOVdcVGoc6XYlxXteDWdA79tBycYeUZ3CXrWHn/iKvqdwnDc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788658476; c=relaxed/simple; bh=X/cAD1JaxVmULRWi7OAU303q7QUjTZ0yqVCXrZ7JU0k=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=u7ZrLdw9R+UYI5HCi1/8jgbIonG/ZSD2/iQ6qli4I461NE+dpGLIW5GGUVzzxxeHsNATGrfd5tWHKioa4g8gcr2zbXlD2HDLQV8irz1hGhzuvhk+L8Ofm3Kuse9yzQ9VvIbx5Yh0419bXbOmxNN9JMmHaNvNSkjSqCV+Q4AuOFk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Yf6h7CgM; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Yf6h7CgM" Received: by smtp.kernel.org (Postfix) with ESMTPS id 154D0C2BCF7; Sun, 6 Sep 2026 01:34:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1788658476; bh=X/cAD1JaxVmULRWi7OAU303q7QUjTZ0yqVCXrZ7JU0k=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=Yf6h7CgMOuu4wEzue8CgkmSyH25Ehm9bPQgHxb0Az4kA2fSjLKoyNnVcOUrBzk77t H/+/Rn9p5dhWyOZNJOQFhFdNUqiWq8ks1vPihpog6a7nA0HqQ4144qv60wxwOU3WnK HKg3z+52k7cL8Ked5ejqmRA8SEZxeNCSebVvntlRx12XKb3dzb8G779dkeEAYGhFSx QtfGMpIhy5BVrTz6Wpis2ikpZh7sKmrU2gU7DcXcBSoQIycaxhhcRw1GeS1BiZIiw9 Yu/J78jK1nwuDKvhOw5FdfcQV1aaabQbMN2++WEhGI3FwM2zebj1fx9ht/7h/A0ajV G4sStluIzE5/A== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id EBA17C79F82; Sun, 6 Sep 2026 01:34:35 +0000 (UTC) From: Ryan Brue via B4 Relay Date: Sat, 05 Sep 2026 20:34:35 -0500 Subject: [PATCH 2/2] drm/panel: add Novatek NT51021 1200x1920 DSI panel driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260905-rbrue-drm-panel-add-novatek-nt51021-v1-2-939f59e89fc9@gmail.com> References: <20260905-rbrue-drm-panel-add-novatek-nt51021-v1-0-939f59e89fc9@gmail.com> In-Reply-To: <20260905-rbrue-drm-panel-add-novatek-nt51021-v1-0-939f59e89fc9@gmail.com> To: Neil Armstrong , Jessica Zhang , David Airlie , Simona Vetter , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Matthias Brugger , AngeloGioacchino Del Regno Cc: dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org, Ryan Brue X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1788658475; l=15078; i=ryanbrue.dev@gmail.com; s=20260906; h=from:subject:message-id; bh=ReaDW0zn/YsThF3WOuoKFxMcsrbwQZd0qFM9fFL2zpA=; b=HUaT9GUvCQWmFA2DoE9UZ9+rcuz094rdNoEgvK4H3uF5QNZB0iB8xngvc/tL9MgRAjJNWX0W2 jjR9rI/dx72AfPtOiKtRvplJLfaCzb5Ah5+ujNU6tscfiSGNF70bgxM X-Developer-Key: i=ryanbrue.dev@gmail.com; a=ed25519; pk=KsUvVaP//v/2q+ZBuacc7cLbsyEYn+AD71Sn28oZWKo= X-Endpoint-Received: by B4 Relay for ryanbrue.dev@gmail.com/20260906 with auth_id=1007 X-Original-From: Ryan Brue Reply-To: ryanbrue.dev@gmail.com From: Ryan Brue Add a driver for the BOE module built around the Novatek NT51021, as fitted to the Amazon Fire HD 10 (2017). The panel is 1200x1920, four lanes, RGB888, sync-event video mode with a continuous HS clock, 136x221 mm. The timings are those of the vendor's LCM driver (drivers/misc/mediatek/lcm/nt51021_wuxga_dsi_vdo/ in the Fire OS 3.18 tree): hsync 1, hbp 32, hfp 110, vsync 1, vbp 14, vfp 11, so htotal 1343 and vtotal 1946. They are used verbatim. The pixel clock is the vendor's PLL_CLOCK =3D 490, which after DDR is 980 Mbps per lane and over four lanes at 24bpp is 163.33 MHz, so 62.5 Hz. That is also what the bootloader leaves running: read back before the kernel touches it, this board's MIPI TX PLL decodes to exactly 980 Mbps per lane, so the panel is already at this rate when the kernel takes over. This controller accepts commands either over I2C or in-band over DSI, and on the shipping boards it does not default to DSI. Writing 0xa5 to register 0x8f opens a window in which DSI commands are accepted and writing 0x00 closes it, so every command the driver sends, including sleep in/out and display on/off, has to sit inside such a window. That is why those are not simply issued from enable()/disable() as most panel drivers do. The vendor's bootloader and kernel both carry a second register sequence for Innolux glass, chosen from two board ID straps. No Innolux unit was available, so that sequence is left out rather than carried untested; adding it later is one compatible, one table and one desc. Tested on the Amazon Fire HD 10 (2017) driven by mediatek-drm: 1200x1920 at 62.5 Hz across a cold boot, three display off and on cycles and a suspend/resume, with the MIPI TX PLL reading back at the intended rate. Assisted-by: LLM Signed-off-by: Ryan Brue --- MAINTAINERS | 7 + drivers/gpu/drm/panel/Kconfig | 11 + drivers/gpu/drm/panel/Makefile | 1 + drivers/gpu/drm/panel/panel-novatek-nt51021.c | 329 ++++++++++++++++++++++= ++++ 4 files changed, 348 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 3a19da74d00c..1cef4dd31d03 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8353,6 +8353,13 @@ S: Maintained F: Documentation/devicetree/bindings/display/panel/novatek,nt37801.yaml F: drivers/gpu/drm/panel/panel-novatek-nt37801.c =20 +DRM DRIVER FOR NOVATEK NT51021 PANELS +M: Ryan Brue +S: Maintained +T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +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 diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index cbdf7b8f7f7a..50a5dc473138 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -727,6 +727,17 @@ config DRM_PANEL_NOVATEK_NT39016 Say Y here if you want to enable support for the panels built around the Novatek NT39016 display controller. =20 +config DRM_PANEL_NOVATEK_NT51021 + tristate "Novatek NT51021 1200x1920 DSI video mode 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 Novatek NT51021 + 1200x1920 DSI video mode panel, as found in the Amazon Fire HD 10 + (2017). The controller carries no readable panel identity, so the + module fitted to a board is named by its compatible string. + 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 3b523cf37833..3fe7c2e579d3 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672E) +=3D panel-novat= ek-nt36672e.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT37700F) +=3D panel-novatek-nt37700f.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT37801) +=3D panel-novatek-nt37801.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT39016) +=3D panel-novatek-nt39016.o +obj-$(CONFIG_DRM_PANEL_NOVATEK_NT51021) +=3D panel-novatek-nt51021.o obj-$(CONFIG_DRM_PANEL_MANTIX_MLAF057WE51) +=3D panel-mantix-mlaf057we51.o obj-$(CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO) +=3D panel-olimex-lcd-olinuxi= no.o obj-$(CONFIG_DRM_PANEL_ORISETECH_OTA5601A) +=3D panel-orisetech-ota5601a.o diff --git a/drivers/gpu/drm/panel/panel-novatek-nt51021.c b/drivers/gpu/dr= m/panel/panel-novatek-nt51021.c new file mode 100644 index 000000000000..d92f3e18f716 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-novatek-nt51021.c @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Novatek NT51021 1200x1920 DSI video-mode panel + * + * Derived from the vendor LCM driver in the Amazon 3.18 tree, + * drivers/misc/mediatek/lcm/nt51021_wuxga_dsi_vdo/. + * + * Note on the command interface: this panel can take its control commands + * either over I2C or in-band over MIPI, and on the shipping ("dvt") board= s it + * does NOT default to MIPI. Writing 0xa5 to register 0x8f forces commands= to + * be accepted over DSI, and writing 0x00 hands the interface back. Every + * command this driver sends therefore has to sit inside such a window -- + * including sleep in/out and display on/off, which is why they are not si= mply + * issued from enable()/disable() the way most panel drivers do it. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Register 0x8f selects where the panel listens for commands. */ +#define NT51021_CMD_IF 0x8f +#define NT51021_CMD_IF_FORCE_MIPI 0xa5 +#define NT51021_CMD_IF_RELEASE 0x00 + +struct nt51021_cmd { + u8 reg; + u8 val; + u16 delay_ms; +}; + +/* + * Taken verbatim from the vendor's init_lcm_registers() BOE branch, minus= the + * command-window writes, which this driver manages itself. + */ +static const struct nt51021_cmd nt51021_init_suez_boe[] =3D { + { 0x01, 0x00, 20 }, /* software reset */ + { 0x83, 0x00, 0 }, /* page select */ + { 0x84, 0x00, 0 }, + { 0x8c, 0x80, 0 }, /* GOP setting */ + { 0xcd, 0x6c, 0 }, /* 3 dummy */ + { 0xc0, 0x8b, 0 }, /* GCH */ + { 0xc8, 0xf0, 0 }, /* GCH */ + { 0x97, 0x00, 0 }, /* resistor setting, 100 ohm */ + { 0x8b, 0x10, 0 }, + { 0xa9, 0x20, 0 }, /* enable TP_SYNC */ + { 0x83, 0xaa, 0 }, /* page select */ + { 0x84, 0x11, 0 }, + { 0xa9, 0x4b, 0 }, /* MIPI Rx drive strength, 85% */ + { 0x85, 0x04, 0 }, /* test mode 1 */ + { 0x86, 0x08, 0 }, /* test mode 2 */ + { 0x9c, 0x10, 0 }, /* test mode 3 */ +}; + +struct nt51021_desc { + const struct nt51021_cmd *cmds; + unsigned int num_cmds; +}; + +static const struct nt51021_desc suez_boe_desc =3D { + .cmds =3D nt51021_init_suez_boe, + .num_cmds =3D ARRAY_SIZE(nt51021_init_suez_boe), +}; + +struct nt51021 { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + struct regulator *supply; + struct gpio_desc *reset_gpio; + const struct nt51021_desc *desc; +}; + +static inline struct nt51021 *to_nt51021(struct drm_panel *panel) +{ + return container_of(panel, struct nt51021, panel); +} + +/* + * A plain "write this value to that register" DCS write. The + * mipi_dsi_dcs_write_seq_multi() macro cannot be used for these: it stash= es its + * payload in a static const array, so every byte has to be a compile-time + * constant, and both the register and the value here come from a table. + */ +static void nt51021_write(struct mipi_dsi_multi_context *ctx, u8 reg, u8 v= al) +{ + const u8 d[] =3D { reg, val }; + + mipi_dsi_dcs_write_buffer_multi(ctx, d, sizeof(d)); +} + +/* + * Open or close the in-band command window. Outside it the panel ignores = DSI + * commands entirely, so every caller that wants to be heard must wrap its + * writes in a pair of these. + */ +static void nt51021_cmd_window(struct mipi_dsi_multi_context *ctx, bool op= en) +{ + nt51021_write(ctx, NT51021_CMD_IF, + open ? NT51021_CMD_IF_FORCE_MIPI : NT51021_CMD_IF_RELEASE); + if (open) + mipi_dsi_msleep(ctx, 1); +} + +static int nt51021_prepare(struct drm_panel *panel) +{ + struct nt51021 *ctx =3D to_nt51021(panel); + struct mipi_dsi_multi_context dsi_ctx =3D { .dsi =3D ctx->dsi }; + unsigned int i; + int ret; + + ret =3D regulator_enable(ctx->supply); + if (ret) + return ret; + + /* + * Vendor timing: reset held low for 20 ms, then 5 ms of settling before + * the first command. The GPIO is active low in the device tree, so + * "1" here means asserted. + */ + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + msleep(20); + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + usleep_range(5000, 6000); + + nt51021_cmd_window(&dsi_ctx, true); + + for (i =3D 0; i < ctx->desc->num_cmds; i++) { + const struct nt51021_cmd *cmd =3D &ctx->desc->cmds[i]; + + nt51021_write(&dsi_ctx, cmd->reg, cmd->val); + if (cmd->delay_ms) + mipi_dsi_msleep(&dsi_ctx, cmd->delay_ms); + } + + /* Vendor init_lcm_registers(): Sleep Out, then 120 ms. */ + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 120); + + nt51021_cmd_window(&dsi_ctx, false); + mipi_dsi_msleep(&dsi_ctx, 5); + + if (dsi_ctx.accum_err) { + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + regulator_disable(ctx->supply); + } + + return dsi_ctx.accum_err; +} + +static int nt51021_enable(struct drm_panel *panel) +{ + struct nt51021 *ctx =3D to_nt51021(panel); + struct mipi_dsi_multi_context dsi_ctx =3D { .dsi =3D ctx->dsi }; + + nt51021_cmd_window(&dsi_ctx, true); + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); + nt51021_cmd_window(&dsi_ctx, false); + mipi_dsi_msleep(&dsi_ctx, 20); + + return dsi_ctx.accum_err; +} + +static int nt51021_disable(struct drm_panel *panel) +{ + struct nt51021 *ctx =3D to_nt51021(panel); + struct mipi_dsi_multi_context dsi_ctx =3D { .dsi =3D ctx->dsi }; + + nt51021_cmd_window(&dsi_ctx, true); + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); + nt51021_cmd_window(&dsi_ctx, false); + + return dsi_ctx.accum_err; +} + +static int nt51021_unprepare(struct drm_panel *panel) +{ + struct nt51021 *ctx =3D to_nt51021(panel); + struct mipi_dsi_multi_context dsi_ctx =3D { .dsi =3D ctx->dsi }; + + /* Vendor lcm_suspend(): force MIPI, Sleep In, 50 ms. */ + nt51021_cmd_window(&dsi_ctx, true); + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 50); + + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + regulator_disable(ctx->supply); + + return dsi_ctx.accum_err; +} + +/* + * Timings from the vendor's lcm_get_params(): 1200x1920, hsync 1 / hbp 32= / + * hfp 110, vsync 1 / vbp 14 / vfp 11. That gives htotal 1343 and vtotal 1= 946. + * + * The pixel clock follows the vendor's PLL_CLOCK =3D 490, which after DDR= is + * 980 Mbps per lane, and over four lanes at 24bpp is 163.33 MHz, giving + * 62.5 Hz. That is also what the bootloader leaves behind: read back befo= re + * the kernel touches it, the MIPI TX PLL on this board decodes to exactly + * 980 Mbps per lane, so the panel is already running at this rate when the + * kernel takes over. + * + * Note for anyone tempted by the "fps=3D6025" the bootloader puts on the = kernel + * command line: it is not the DSI rate and does not describe this PLL. It= also + * is not stable across boots. + */ +static const struct drm_display_mode nt51021_mode =3D { + .clock =3D 163333, + .hdisplay =3D 1200, + .hsync_start =3D 1200 + 110, + .hsync_end =3D 1200 + 110 + 1, + .htotal =3D 1200 + 110 + 1 + 32, + .vdisplay =3D 1920, + .vsync_start =3D 1920 + 11, + .vsync_end =3D 1920 + 11 + 1, + .vtotal =3D 1920 + 11 + 1 + 14, + .width_mm =3D 136, + .height_mm =3D 221, + .type =3D DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, +}; + +static int nt51021_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + return drm_connector_helper_get_modes_fixed(connector, &nt51021_mode); +} + +static const struct drm_panel_funcs nt51021_panel_funcs =3D { + .prepare =3D nt51021_prepare, + .enable =3D nt51021_enable, + .disable =3D nt51021_disable, + .unprepare =3D nt51021_unprepare, + .get_modes =3D nt51021_get_modes, +}; + +static int nt51021_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev =3D &dsi->dev; + struct nt51021 *ctx; + int ret; + + ctx =3D devm_drm_panel_alloc(dev, struct nt51021, panel, + &nt51021_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + ctx->supply =3D devm_regulator_get(dev, "power"); + if (IS_ERR(ctx->supply)) + return dev_err_probe(dev, PTR_ERR(ctx->supply), + "failed to get power supply\n"); + + /* Active low in the device tree, so OUT_HIGH leaves reset asserted. */ + ctx->reset_gpio =3D 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 GPIO\n"); + + ctx->desc =3D of_device_get_match_data(dev); + if (!ctx->desc) + return -ENODEV; + + ctx->dsi =3D dsi; + mipi_dsi_set_drvdata(dsi, ctx); + + dsi->lanes =3D 4; + dsi->format =3D MIPI_DSI_FMT_RGB888; + /* + * Video mode with sync events rather than sync pulses + * (vendor SYNC_EVENT_VDO_MODE), and a continuously running HS clock + * (vendor cont_clock =3D 1), so MIPI_DSI_CLOCK_NON_CONTINUOUS is + * deliberately absent. Commands go out in LP. + */ + dsi->mode_flags =3D MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM; + + ret =3D drm_panel_of_backlight(&ctx->panel); + if (ret) + return dev_err_probe(dev, ret, "failed to get backlight\n"); + + /* The DSI host must be running before we can send the init sequence. */ + ctx->panel.prepare_prev_first =3D true; + + drm_panel_add(&ctx->panel); + + ret =3D mipi_dsi_attach(dsi); + if (ret < 0) { + drm_panel_remove(&ctx->panel); + return dev_err_probe(dev, ret, "failed to attach to DSI host\n"); + } + + return 0; +} + +static void nt51021_remove(struct mipi_dsi_device *dsi) +{ + struct nt51021 *ctx =3D mipi_dsi_get_drvdata(dsi); + int ret; + + ret =3D mipi_dsi_detach(dsi); + if (ret < 0) + dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret); + + drm_panel_remove(&ctx->panel); +} + +static const struct of_device_id nt51021_of_match[] =3D { + { .compatible =3D "amazon,suez-boe-nt51021", .data =3D &suez_boe_desc }, + { } +}; +MODULE_DEVICE_TABLE(of, nt51021_of_match); + +static struct mipi_dsi_driver nt51021_driver =3D { + .probe =3D nt51021_probe, + .remove =3D nt51021_remove, + .driver =3D { + .name =3D "panel-novatek-nt51021", + .of_match_table =3D nt51021_of_match, + }, +}; +module_mipi_dsi_driver(nt51021_driver); + +MODULE_DESCRIPTION("Novatek NT51021 1200x1920 DSI panel driver"); +MODULE_LICENSE("GPL"); --=20 2.55.0