* [PATCH v3 0/7] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface
@ 2026-10-19 9:56 Svyatoslav Ryhel
2026-10-19 9:56 ` [PATCH v3 1/7] drm/tegra: dc: Expand available registers layouts Svyatoslav Ryhel
` (6 more replies)
0 siblings, 7 replies; 12+ messages in thread
From: Svyatoslav Ryhel @ 2026-10-19 9:56 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
Jonathan Hunter, Mikko Perttunen, Svyatoslav Ryhel
Cc: dri-devel, devicetree, linux-kernel, linux-tegra
The display controller in Tegra20/30 SoCs features an 8-bit SPI interface
that closely resembles the MIPI DBI Type B protocol and is referred to as
'8-bit CPU'.
Add DC parametrization options needed for fine tuning by bridges.
Add 8-bit CPU interface in form of a bridge linking to RGB port.
Add 2 DBI Type B panels used in LG Optimus 2X which work with
the 8-bit CPU interface.
---
Changes in v2:
- convert enums in the dc header into definitions list
- add check in RGB if bridge_state is not NULL, if yes, use default flags
- inverted FLAG_PIXDATA_SAMPLE flag setup logic since default value is set to
be on NEGEDGE while v1 set default to be on POSEDGE
- 8bit-cpu schema renamed to dbi, removed compaible and set as DC child
- expanded list of required properties
- 8bit-cpu bridge remodelled into dbi interface as DC child
- improve gpio handling
- rework attach to get drvdata from parent
- add panel supplies description
- split panel driver into per-panel drivers
- get DBI interface from the patent directly
I have deliberately left GPIO parsing from device tree since it seems to
be the most convenient and mainstream method of working with GPIOs.
Changes in v3:
- rgb: fix default bus_flags to match original behavior
- dbi-schema: remove compatible from required props
remove dc@54200000 wrapper from example
restrict dbi nodes for tegra20/tegra30 only
- dbi: remove unused idx from tegra_dbi_priv
wrap dbi registration in dc with devm_add_action_or_reset()
separate dbi into separate object
export tegra_dc_commit() and trace_dc_readl()/dc_readl()
---
Svyatoslav Ryhel (7):
drm/tegra: dc: Expand available registers layouts
drm/tegra: rgb: Parameterize configuration based on bus flags
dt-bindings: display: tegra: Document 8-bit CPU parallel interface
drm/tegra: Add support for 8-bit CPU interface
dt-bindings: display: panel: Document Hitachi TX10D07VM0BAA and LG
LH400WV3 panels
drm/panel: Add Hitachi TX10D07VM0BAA MIPI DBI Type B panel driver
drm/panel: Add LG LH400WV3-SD04 MIPI DBI Type B panel driver
.../display/panel/hit,tx10d07vm0baa.yaml | 58 +++
.../display/tegra/nvidia,tegra-dbi.yaml | 138 ++++++
.../display/tegra/nvidia,tegra20-dc.yaml | 15 +
drivers/gpu/drm/panel/Kconfig | 27 ++
drivers/gpu/drm/panel/Makefile | 2 +
.../drm/panel/panel-hitachi-tx10d07vm0baa.c | 297 +++++++++++++
drivers/gpu/drm/panel/panel-lg-lh400wv3.c | 236 ++++++++++
drivers/gpu/drm/tegra/Kconfig | 7 +
drivers/gpu/drm/tegra/Makefile | 3 +
drivers/gpu/drm/tegra/dbi.c | 415 ++++++++++++++++++
drivers/gpu/drm/tegra/dc.c | 32 +-
drivers/gpu/drm/tegra/dc.h | 50 ++-
drivers/gpu/drm/tegra/rgb.c | 41 +-
drivers/gpu/drm/tegra/trace.c | 3 +
14 files changed, 1312 insertions(+), 12 deletions(-)
create mode 100644 Documentation/devicetree/bindings/display/panel/hit,tx10d07vm0baa.yaml
create mode 100644 Documentation/devicetree/bindings/display/tegra/nvidia,tegra-dbi.yaml
create mode 100644 drivers/gpu/drm/panel/panel-hitachi-tx10d07vm0baa.c
create mode 100644 drivers/gpu/drm/panel/panel-lg-lh400wv3.c
create mode 100644 drivers/gpu/drm/tegra/dbi.c
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 1/7] drm/tegra: dc: Expand available registers layouts
2026-10-19 9:56 [PATCH v3 0/7] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface Svyatoslav Ryhel
@ 2026-10-19 9:56 ` Svyatoslav Ryhel
2026-10-19 9:57 ` [PATCH v3 2/7] drm/tegra: rgb: Parameterize configuration based on bus flags Svyatoslav Ryhel
` (5 subsequent siblings)
6 siblings, 0 replies; 12+ messages in thread
From: Svyatoslav Ryhel @ 2026-10-19 9:56 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
Jonathan Hunter, Mikko Perttunen, Svyatoslav Ryhel
Cc: dri-devel, devicetree, linux-kernel, linux-tegra
Expand existing DC register definitions with additional fields in
preparation for adding the 8-bit CPU interface.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/gpu/drm/tegra/dc.c | 3 ++-
drivers/gpu/drm/tegra/dc.h | 49 +++++++++++++++++++++++++++++++++-----
2 files changed, 45 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index b0bfa946e6979..5c67928bcabfa 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -2384,7 +2384,8 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
if (dc->rgb) {
/* XXX: parameterize? */
- value = SC0_H_QUALIFIER_NONE | SC1_H_QUALIFIER_NONE;
+ value = SC0_H_QUALIFIER(SC_H_QUALIFIER_NONE) |
+ SC1_H_QUALIFIER(SC_H_QUALIFIER_NONE);
tegra_dc_writel(dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS);
}
diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h
index 0cb0515968b35..7005df32f485f 100644
--- a/drivers/gpu/drm/tegra/dc.h
+++ b/drivers/gpu/drm/tegra/dc.h
@@ -274,8 +274,11 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
#define DC_COM_CRC_CHECKSUM 0x301
#define DC_COM_PIN_OUTPUT_ENABLE(x) (0x302 + (x))
#define DC_COM_PIN_OUTPUT_POLARITY(x) (0x306 + (x))
+#define LSC0_OUTPUT_POLARITY_LOW BIT(24)
#define LVS_OUTPUT_POLARITY_LOW (1 << 28)
#define LHS_OUTPUT_POLARITY_LOW (1 << 30)
+#define LSPI_OUTPUT_POLARITY_LOW BIT(8)
+#define LDC_OUTPUT_SELECT_V_PULSE1 BIT(14)
#define DC_COM_PIN_OUTPUT_DATA(x) (0x30a + (x))
#define DC_COM_PIN_INPUT_ENABLE(x) (0x30e + (x))
#define DC_COM_PIN_INPUT_DATA(x) (0x312 + (x))
@@ -303,9 +306,15 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
#define UNDERFLOW_REPORT_ENABLE (1 << 0)
#define DC_DISP_DISP_SIGNAL_OPTIONS0 0x400
-#define H_PULSE0_ENABLE (1 << 8)
-#define H_PULSE1_ENABLE (1 << 10)
-#define H_PULSE2_ENABLE (1 << 12)
+#define H_PULSE0_ENABLE BIT(8)
+#define H_PULSE1_ENABLE BIT(10)
+#define H_PULSE2_ENABLE BIT(12)
+#define V_PULSE0_ENABLE BIT(16)
+#define V_PULSE1_ENABLE BIT(18)
+#define V_PULSE2_ENABLE BIT(19)
+#define V_PULSE3_ENABLE BIT(20)
+#define M0_ENABLE BIT(24)
+#define M1_ENABLE BIT(26)
#define DC_DISP_DISP_SIGNAL_OPTIONS1 0x401
@@ -451,12 +460,26 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
#define BASE_COLOR_SIZE_888 ( 8 << 0)
#define BASE_COLOR_SIZE_101010 ( 10 << 0)
#define BASE_COLOR_SIZE_121212 ( 12 << 0)
+#define DISP_COLOR_SWAP_BGR BIT(16)
#define CMU_ENABLE_ENABLE (1 << 20)
#define DC_DISP_SHIFT_CLOCK_OPTIONS 0x431
-#define SC1_H_QUALIFIER_NONE (1 << 16)
-#define SC0_H_QUALIFIER_NONE (1 << 0)
-
+#define SC0_H_QUALIFIER(x) (((x) & 0x7) << 0)
+#define SC1_H_QUALIFIER(x) (((x) & 0x7) << 16)
+#define SC_H_QUALIFIER_DISABLE 0
+#define SC_H_QUALIFIER_NONE 1
+#define SC_H_QUALIFIER_HACTIVE 2
+#define SC_H_QUALIFIER_EXT_HACTIVE 3
+#define SC_H_QUALIFIER_HPULSE 4
+#define SC_H_QUALIFIER_EXT_HPULSE 5
+#define SC0_V_QUALIFIER(x) (((x) & 0x7) << 3)
+#define SC1_V_QUALIFIER(x) (((x) & 0x7) << 19)
+#define SC_V_QUALIFIER_NONE 0
+#define SC_V_QUALIFIER_RSVD 1
+#define SC_V_QUALIFIER_VACTIVE 2
+#define SC_V_QUALIFIER_EXT_VACTIVE 3
+#define SC_V_QUALIFIER_VPULSE 4
+#define SC_V_QUALIFIER_EXT_VPULSE 5
#define DC_DISP_DATA_ENABLE_OPTIONS 0x432
#define DE_SELECT_ACTIVE_BLANK (0 << 0)
#define DE_SELECT_ACTIVE (1 << 0)
@@ -493,6 +516,20 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
#define DC_DISP_CURSOR_POSITION_NS 0x441
#define DC_DISP_INIT_SEQ_CONTROL 0x442
+#define SEND_INIT_SEQUENCE BIT(0)
+#define INIT_SEQUENCE_MODE_SPI BIT(1)
+#define INIT_SEQUENCE_MODE_PLCD 0x0
+#define INIT_SEQ_DC_SIGNAL_SHIFT 4
+#define INIT_SEQ_DC_SIGNAL_MASK (0x7 << INIT_SEQ_DC_SIGNAL_SHIFT)
+#define NO_DC_SIGNAL 0
+#define DC_SIGNAL_VSYNC 1
+#define DC_SIGNAL_VPULSE0 2
+#define DC_SIGNAL_VPULSE1 3
+#define DC_SIGNAL_VPULSE2 4
+#define DC_SIGNAL_VPULSE3 5
+#define INIT_SEQ_DC_CONTROL_SHIFT 7
+#define FRAME_INIT_SEQ_CYCLES_SHIFT 8
+
#define DC_DISP_SPI_INIT_SEQ_DATA_A 0x443
#define DC_DISP_SPI_INIT_SEQ_DATA_B 0x444
#define DC_DISP_SPI_INIT_SEQ_DATA_C 0x445
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 2/7] drm/tegra: rgb: Parameterize configuration based on bus flags
2026-10-19 9:56 [PATCH v3 0/7] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface Svyatoslav Ryhel
2026-10-19 9:56 ` [PATCH v3 1/7] drm/tegra: dc: Expand available registers layouts Svyatoslav Ryhel
@ 2026-10-19 9:57 ` Svyatoslav Ryhel
2026-10-19 10:15 ` sashiko-bot
2026-10-19 9:57 ` [PATCH v3 3/7] dt-bindings: display: tegra: Document 8-bit CPU parallel interface Svyatoslav Ryhel
` (4 subsequent siblings)
6 siblings, 1 reply; 12+ messages in thread
From: Svyatoslav Ryhel @ 2026-10-19 9:57 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
Jonathan Hunter, Mikko Perttunen, Svyatoslav Ryhel
Cc: dri-devel, devicetree, linux-kernel, linux-tegra
Parameterize configuration based on bus flags passed from the bridge or
panel. The list of supported flags includes now pixel clock polarity,
display enable (DE) polarity, and data alignment.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/gpu/drm/tegra/rgb.c | 41 ++++++++++++++++++++++++++++++++-----
1 file changed, 36 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index b6d09a8d8e5ea..da9e37fe36689 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -5,6 +5,7 @@
*/
#include <linux/clk.h>
+#include <linux/media-bus-format.h>
#include <linux/of.h>
#include <drm/drm_atomic_helper.h>
@@ -104,14 +105,24 @@ static void tegra_rgb_encoder_enable(struct drm_encoder *encoder)
struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
struct tegra_output *output = encoder_to_output(encoder);
struct tegra_rgb *rgb = to_rgb(output);
- u32 value;
+ struct drm_bridge_state *bridge_state;
+ u32 bus_flags, value;
+
+ /* Get but flags from the bridge state. */
+ bridge_state = drm_bridge_get_current_state(output->bridge);
+ if (bridge_state)
+ bus_flags = bridge_state->input_bus_cfg.flags;
+ else
+ bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE |
+ DRM_BUS_FLAG_DE_HIGH |
+ DRM_BUS_FLAG_DATA_MSB_TO_LSB;
tegra_dc_write_regs(rgb->dc, rgb_enable, ARRAY_SIZE(rgb_enable));
value = DE_SELECT_ACTIVE | DE_CONTROL_NORMAL;
tegra_dc_writel(rgb->dc, value, DC_DISP_DATA_ENABLE_OPTIONS);
- /* configure H- and V-sync signal polarities */
+ /* configure H- and V-sync and pixel clock signal polarities */
value = tegra_dc_readl(rgb->dc, DC_COM_PIN_OUTPUT_POLARITY(1));
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
@@ -124,11 +135,31 @@ static void tegra_rgb_encoder_enable(struct drm_encoder *encoder)
else
value &= ~LVS_OUTPUT_POLARITY_LOW;
+ if (bus_flags & DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE)
+ value &= ~LSC0_OUTPUT_POLARITY_LOW;
+ else
+ value |= LSC0_OUTPUT_POLARITY_LOW;
+
tegra_dc_writel(rgb->dc, value, DC_COM_PIN_OUTPUT_POLARITY(1));
- /* XXX: parameterize? */
- value = DISP_DATA_FORMAT_DF1P1C | DISP_ALIGNMENT_MSB |
- DISP_ORDER_RED_BLUE;
+ /* configure DE signal polarities */
+ value = tegra_dc_readl(rgb->dc, DC_COM_PIN_OUTPUT_POLARITY(3));
+
+ if (bus_flags & DRM_BUS_FLAG_DE_LOW)
+ value |= LSPI_OUTPUT_POLARITY_LOW;
+ else
+ value &= ~LSPI_OUTPUT_POLARITY_LOW;
+
+ tegra_dc_writel(rgb->dc, value, DC_COM_PIN_OUTPUT_POLARITY(3));
+
+ /* configure DATA order and alignment */
+ value = DISP_DATA_FORMAT_DF1P1C | DISP_ORDER_RED_BLUE;
+
+ if (bus_flags & DRM_BUS_FLAG_DATA_LSB_TO_MSB)
+ value |= DISP_ALIGNMENT_LSB;
+ else
+ value &= ~DISP_ALIGNMENT_LSB;
+
tegra_dc_writel(rgb->dc, value, DC_DISP_DISP_INTERFACE_CONTROL);
tegra_dc_commit(rgb->dc);
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 3/7] dt-bindings: display: tegra: Document 8-bit CPU parallel interface
2026-10-19 9:56 [PATCH v3 0/7] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface Svyatoslav Ryhel
2026-10-19 9:56 ` [PATCH v3 1/7] drm/tegra: dc: Expand available registers layouts Svyatoslav Ryhel
2026-10-19 9:57 ` [PATCH v3 2/7] drm/tegra: rgb: Parameterize configuration based on bus flags Svyatoslav Ryhel
@ 2026-10-19 9:57 ` Svyatoslav Ryhel
2026-10-19 10:09 ` sashiko-bot
2026-10-19 9:57 ` [PATCH v3 4/7] drm/tegra: Add support for 8-bit CPU interface Svyatoslav Ryhel
` (3 subsequent siblings)
6 siblings, 1 reply; 12+ messages in thread
From: Svyatoslav Ryhel @ 2026-10-19 9:57 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
Jonathan Hunter, Mikko Perttunen, Svyatoslav Ryhel
Cc: dri-devel, devicetree, linux-kernel, linux-tegra
Document 8-bit CPU parallel MIPI DBI Type B interface provided by
Tegra20/30 SoCs display controller.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
.../display/tegra/nvidia,tegra-dbi.yaml | 138 ++++++++++++++++++
.../display/tegra/nvidia,tegra20-dc.yaml | 15 ++
2 files changed, 153 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/tegra/nvidia,tegra-dbi.yaml
diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra-dbi.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra-dbi.yaml
new file mode 100644
index 0000000000000..ada7b35778933
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra-dbi.yaml
@@ -0,0 +1,138 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra-dbi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nvidia Tegra DC based MIPI DBI Type B interface
+
+maintainers:
+ - Svyatoslav Ryhel <clamor95@gmail.com>
+
+description: The display controller in Tegra20/30 SoCs features an
+ 8-bit SPI interface that closely resembles the MIPI DBI Type B
+ protocol and is referred to as '8-bit CPU'. Each display controller
+ provides two such interfaces, which can be used to send MIPI DCS
+ commands to initialize and control the panel while image data is
+ transmitted via 16/18/24-line RGB.
+
+properties:
+ dc-gpios:
+ description: Data/command selection pin.
+ maxItems: 1
+
+ rw-gpios:
+ description: Read/write pin.
+ maxItems: 1
+
+ cs-gpios:
+ description: Chip select pin.
+ maxItems: 1
+
+ data-gpios:
+ description: Specifies a set of 8 gpio pins used to transfer data.
+ minItems: 8
+ maxItems: 8
+
+ nvidia,init-sequence:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ description:
+ Device specific set of values used in DC DISP_SPI_INIT_SEQ registers.
+ minItems: 4
+ maxItems: 4
+
+ panel:
+ type: object
+ description: Node of supported panel driven by the bridge.
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description: Video port for RGB input.
+
+ properties:
+ endpoint:
+ $ref: /schemas/graph.yaml#/$defs/endpoint-base
+ unevaluatedProperties: false
+
+ properties:
+ bus-width:
+ enum: [ 16, 18, 24 ]
+
+ port@1:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Video port for DBI output (panel or connector).
+
+ required:
+ - port@0
+ - port@1
+
+required:
+ - dc-gpios
+ - rw-gpios
+ - cs-gpios
+ - data-gpios
+ - panel
+ - ports
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ dbi {
+ dc-gpios = <&gpio 110 GPIO_ACTIVE_HIGH>;
+ rw-gpios = <&gpio 11 GPIO_ACTIVE_HIGH>;
+ cs-gpios = <&gpio 108 GPIO_ACTIVE_HIGH>;
+
+ data-gpios = <&gpio 32 GPIO_ACTIVE_HIGH>, <&gpio 33 GPIO_ACTIVE_HIGH>,
+ <&gpio 34 GPIO_ACTIVE_HIGH>, <&gpio 35 GPIO_ACTIVE_HIGH>,
+ <&gpio 36 GPIO_ACTIVE_HIGH>, <&gpio 37 GPIO_ACTIVE_HIGH>,
+ <&gpio 38 GPIO_ACTIVE_HIGH>, <&gpio 39 GPIO_ACTIVE_HIGH>;
+
+ nvidia,init-sequence = <0x0000002c 0x0 0x0 0x00005000>;
+
+ panel {
+ compatible = "hit,tx10d07vm0baa";
+
+ reset-gpios = <&gpio 175 GPIO_ACTIVE_LOW>;
+
+ avci-supply = <&vcc_2v8_lcd>;
+ iovcc-supply = <&iovcc_1v8_lcd>;
+
+ backlight = <&backlight>;
+
+ port {
+ panel_input: endpoint {
+ remote-endpoint = <&bridge_output>;
+ };
+ };
+ };
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ bridge_input: endpoint {
+ remote-endpoint = <&dpi_output>;
+ bus-width = <24>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ bridge_output: endpoint {
+ remote-endpoint = <&panel_input>;
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml
index 69be95afd5627..a2b7279a17930 100644
--- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml
+++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml
@@ -76,6 +76,9 @@ properties:
$ref: /schemas/types.yaml#/definitions/phandle-array
description: A list of phandles of outputs that this display controller can drive.
+ dbi:
+ $ref: /schemas/display/tegra/nvidia,tegra-dbi.yaml#
+
rgb:
type: object
@@ -155,6 +158,18 @@ allOf:
- const: wind
- const: wint
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - nvidia,tegra114-dc
+ - nvidia,tegra124-dc
+ - nvidia,tegra210-dc
+ then:
+ properties:
+ dbi: false
+
additionalProperties: false
required:
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 4/7] drm/tegra: Add support for 8-bit CPU interface
2026-10-19 9:56 [PATCH v3 0/7] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface Svyatoslav Ryhel
` (2 preceding siblings ...)
2026-10-19 9:57 ` [PATCH v3 3/7] dt-bindings: display: tegra: Document 8-bit CPU parallel interface Svyatoslav Ryhel
@ 2026-10-19 9:57 ` Svyatoslav Ryhel
2026-10-19 10:20 ` sashiko-bot
2026-10-19 9:57 ` [PATCH v3 5/7] dt-bindings: display: panel: Document Hitachi TX10D07VM0BAA and LG LH400WV3 panels Svyatoslav Ryhel
` (2 subsequent siblings)
6 siblings, 1 reply; 12+ messages in thread
From: Svyatoslav Ryhel @ 2026-10-19 9:57 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
Jonathan Hunter, Mikko Perttunen, Svyatoslav Ryhel
Cc: dri-devel, devicetree, linux-kernel, linux-tegra
The display controller in Tegra20/30 SoCs features an 8-bit SPI interface
that closely resembles the MIPI DBI Type B protocol and is referred to as
'8-bit CPU'. Each display controller provides two such interfaces, which
can be used to send MIPI DCS commands to initialize and control the panel
while image data is transmitted via 16/18/24-line RGB.
The '8-bit CPU', although being the DC interface, is modeled using a
bridge framework for flexibility and convenience. This implementation does
not change the existing codebase besides registering a fragment and can be
enabled/disabled without losing other Tegra DRM functionality.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/gpu/drm/tegra/Kconfig | 7 +
drivers/gpu/drm/tegra/Makefile | 3 +
drivers/gpu/drm/tegra/dbi.c | 415 +++++++++++++++++++++++++++++++++
drivers/gpu/drm/tegra/dc.c | 29 +++
drivers/gpu/drm/tegra/dc.h | 1 +
drivers/gpu/drm/tegra/trace.c | 3 +
6 files changed, 458 insertions(+)
create mode 100644 drivers/gpu/drm/tegra/dbi.c
diff --git a/drivers/gpu/drm/tegra/Kconfig b/drivers/gpu/drm/tegra/Kconfig
index 8a3b16aac5d68..ce3a424398713 100644
--- a/drivers/gpu/drm/tegra/Kconfig
+++ b/drivers/gpu/drm/tegra/Kconfig
@@ -30,6 +30,13 @@ config DRM_TEGRA
if DRM_TEGRA
+config DRM_TEGRA_DBI
+ tristate "Enable MIPI DBI type B communication protocol for Tegra 20/30"
+ help
+ Tegra20 and Tegra30 feature 8 bit CPU driver panel control
+ protocol (similar to MIPI DBI Type B). This option allows use
+ it to set up and control compatible panel.
+
config DRM_TEGRA_DEBUG
bool "NVIDIA Tegra DRM debug support"
help
diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile
index e399b40d64a1d..d67e53248a082 100644
--- a/drivers/gpu/drm/tegra/Makefile
+++ b/drivers/gpu/drm/tegra/Makefile
@@ -33,3 +33,6 @@ tegra-drm-y += trace.o
tegra-drm-$(CONFIG_DRM_FBDEV_EMULATION) += fbdev.o
obj-$(CONFIG_DRM_TEGRA) += tegra-drm.o
+
+obj-$(CONFIG_DRM_TEGRA_DBI) += tegra-dbi.o
+tegra-dbi-y := dbi.o
diff --git a/drivers/gpu/drm/tegra/dbi.c b/drivers/gpu/drm/tegra/dbi.c
new file mode 100644
index 0000000000000..4b3966dea0438
--- /dev/null
+++ b/drivers/gpu/drm/tegra/dbi.c
@@ -0,0 +1,415 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/media-bus-format.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/property.h>
+#include <linux/platform_device.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_mipi_dbi.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+
+#include <video/mipi_display.h>
+
+#include "dc.h"
+
+#define TEGRA_DBI_COMMAND 0
+#define TEGRA_DBI_DATA 1
+#define TEGRA_DBI_DATA_PINS_MAX 8
+
+struct tegra_dbi_output {
+ struct drm_panel *panel;
+ struct drm_bridge *bridge;
+};
+
+struct tegra_dbi_priv {
+ struct mipi_dbi dbi; /* must be first */
+ struct device *dev;
+ struct tegra_dc *dc;
+
+ struct gpio_desc *dc_gpio;
+ struct gpio_desc *rw_gpio;
+ struct gpio_desc *cs_gpio;
+ struct gpio_descs *data_gpios;
+
+ struct drm_bridge bridge;
+ struct tegra_dbi_output output;
+
+ u32 spi_init_seq[4];
+ u32 bus_width;
+ bool prepared;
+};
+
+static inline struct tegra_dbi_priv *mipi_dbi_to_tegra_dbi(struct mipi_dbi *dbi)
+{
+ return container_of(dbi, struct tegra_dbi_priv, dbi);
+}
+
+static inline struct tegra_dbi_priv *bridge_to_tegra_dbi(struct drm_bridge *bridge)
+{
+ return container_of(bridge, struct tegra_dbi_priv, bridge);
+}
+
+static void tegra_dbi_write(struct tegra_dbi_priv *priv, u8 type, u8 value)
+{
+ DECLARE_BITMAP(value_bitmap, 8);
+
+ value_bitmap[0] = value;
+
+ gpiod_set_value(priv->dc_gpio, type);
+
+ gpiod_set_value(priv->cs_gpio, 0);
+ gpiod_set_value(priv->rw_gpio, 0);
+
+ gpiod_set_array_value(priv->data_gpios->ndescs, priv->data_gpios->desc,
+ priv->data_gpios->info, value_bitmap);
+
+ gpiod_set_value(priv->cs_gpio, 1);
+ gpiod_set_value(priv->rw_gpio, 1);
+
+ udelay(10);
+}
+
+static int tegra_dbi_dbi_command(struct mipi_dbi *dbi, u8 *cmd, u8 *param, size_t num)
+{
+ struct tegra_dbi_priv *priv = mipi_dbi_to_tegra_dbi(dbi);
+ u8 command = *cmd;
+
+ if (!priv->data_gpios)
+ return 0;
+
+ tegra_dbi_write(priv, TEGRA_DBI_COMMAND, command);
+
+ for (int i = 0; i < num; i++)
+ tegra_dbi_write(priv, TEGRA_DBI_DATA, param[i]);
+
+ return 0;
+}
+
+static int tegra_dbi_prepare_gpios(struct tegra_dbi_priv *priv)
+{
+ struct device *dev = priv->dev;
+ int ret;
+
+ if (priv->prepared)
+ return 0;
+
+ /*
+ * Control and data GPIOs are shared between SPI and DPI. The bridge
+ * driver must get and put the required GPIOs each time DCS commands
+ * are transmitted.
+ */
+
+ priv->cs_gpio = gpiod_get(dev, "cs", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->cs_gpio)) {
+ dev_err(dev, "Failed to get CS GPIO: %ld\n", PTR_ERR(priv->cs_gpio));
+ return PTR_ERR(priv->cs_gpio);
+ }
+
+ priv->rw_gpio = gpiod_get(dev, "rw", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->rw_gpio)) {
+ ret = PTR_ERR(priv->rw_gpio);
+ dev_err(dev, "Failed to get RW GPIO: %d\n", ret);
+ goto free_cs;
+ }
+
+ priv->dc_gpio = gpiod_get(dev, "dc", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->dc_gpio)) {
+ ret = PTR_ERR(priv->dc_gpio);
+ dev_err(dev, "Failed to get DC GPIO: %d\n", ret);
+ goto free_rw;
+ }
+
+ priv->data_gpios = gpiod_get_array(dev, "data", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->data_gpios)) {
+ ret = PTR_ERR(priv->data_gpios);
+ dev_err(dev, "Failed to get data gpios %d\n", ret);
+ goto free_dc;
+ }
+
+ if (priv->data_gpios && priv->data_gpios->ndescs > TEGRA_DBI_DATA_PINS_MAX) {
+ ret = -EINVAL;
+ dev_err(dev, "Too many data gpios\n");
+ goto free_data;
+ }
+
+ priv->prepared = true;
+
+ return 0;
+
+free_data:
+ gpiod_put_array(priv->data_gpios);
+free_dc:
+ gpiod_put(priv->dc_gpio);
+free_rw:
+ gpiod_put(priv->rw_gpio);
+free_cs:
+ gpiod_put(priv->cs_gpio);
+
+ return ret;
+}
+
+static void tegra_dbi_atomic_enable(struct drm_bridge *bridge,
+ struct drm_atomic_commit *state)
+{
+ struct tegra_dbi_priv *priv = bridge_to_tegra_dbi(bridge);
+ struct tegra_dc *dc = priv->dc;
+ u32 value;
+ int ret;
+
+ value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
+ value &= ~DISP_CTRL_MODE_MASK;
+ tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
+
+ /* Disable all options except cursor if enabled */
+ value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
+ value &= CURSOR_ENABLE;
+ tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
+
+ tegra_dc_commit(dc);
+
+ tegra_dc_writel(dc, V_PULSE1_ENABLE, DC_DISP_DISP_SIGNAL_OPTIONS0);
+ tegra_dc_writel(dc, PULSE_POLARITY_LOW, DC_DISP_V_PULSE1_CONTROL);
+
+ tegra_dc_writel(dc, PULSE_END(1), DC_DISP_V_PULSE0_POSITION_A);
+ tegra_dc_writel(dc, 0, DC_DISP_V_PULSE0_POSITION_B);
+ tegra_dc_writel(dc, 0, DC_DISP_V_PULSE0_POSITION_C);
+
+ value = 1 << FRAME_INIT_SEQ_CYCLES_SHIFT |
+ DC_SIGNAL_VPULSE1 << INIT_SEQ_DC_SIGNAL_SHIFT |
+ INIT_SEQUENCE_MODE_PLCD | SEND_INIT_SEQUENCE;
+ tegra_dc_writel(dc, value, DC_DISP_INIT_SEQ_CONTROL);
+
+ tegra_dc_writel(dc, priv->spi_init_seq[0], DC_DISP_SPI_INIT_SEQ_DATA_A);
+ tegra_dc_writel(dc, priv->spi_init_seq[1], DC_DISP_SPI_INIT_SEQ_DATA_B);
+ tegra_dc_writel(dc, priv->spi_init_seq[2], DC_DISP_SPI_INIT_SEQ_DATA_C);
+ tegra_dc_writel(dc, priv->spi_init_seq[3], DC_DISP_SPI_INIT_SEQ_DATA_D);
+
+ value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
+ value &= ~DISP_CTRL_MODE_MASK;
+ value |= DISP_CTRL_MODE_C_DISPLAY;
+ tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
+
+ /* set LDC pin to V Pulse 1 */
+ value = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_SELECT(6));
+ value |= LDC_OUTPUT_SELECT_V_PULSE1;
+ tegra_dc_writel(dc, value, DC_COM_PIN_OUTPUT_SELECT(6));
+
+ value = SC1_H_QUALIFIER(SC_H_QUALIFIER_NONE) |
+ SC0_V_QUALIFIER(SC_V_QUALIFIER_VACTIVE) |
+ SC0_H_QUALIFIER(SC_H_QUALIFIER_HACTIVE);
+ tegra_dc_writel(dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS);
+
+ value = tegra_dc_readl(dc, DC_DISP_DISP_COLOR_CONTROL);
+ value &= ~BASE_COLOR_SIZE_MASK;
+
+ switch (priv->bus_width) {
+ case 16:
+ value |= BASE_COLOR_SIZE_565;
+ break;
+ case 18:
+ value |= BASE_COLOR_SIZE_666;
+ break;
+ case 24:
+ default:
+ value |= BASE_COLOR_SIZE_888;
+ break;
+ }
+
+ tegra_dc_writel(dc, value, DC_DISP_DISP_COLOR_CONTROL);
+
+ ret = tegra_dbi_prepare_gpios(priv);
+ if (ret)
+ return;
+
+ if (priv->output.panel)
+ drm_panel_enable(priv->output.panel);
+
+ gpiod_set_value(priv->cs_gpio, 0);
+
+ gpiod_put(priv->dc_gpio);
+ gpiod_put(priv->rw_gpio);
+ gpiod_put(priv->cs_gpio);
+
+ gpiod_put_array(priv->data_gpios);
+ priv->prepared = false;
+}
+
+static void tegra_dbi_atomic_disable(struct drm_bridge *bridge,
+ struct drm_atomic_commit *state)
+{
+ struct tegra_dbi_priv *priv = bridge_to_tegra_dbi(bridge);
+ int ret;
+
+ ret = tegra_dbi_prepare_gpios(priv);
+ if (ret)
+ return;
+
+ if (priv->output.panel)
+ drm_panel_disable(priv->output.panel);
+
+ gpiod_set_value(priv->cs_gpio, 0);
+
+ gpiod_put(priv->dc_gpio);
+ gpiod_put(priv->rw_gpio);
+ gpiod_put(priv->cs_gpio);
+
+ gpiod_put_array(priv->data_gpios);
+ priv->prepared = false;
+}
+
+static int tegra_dbi_atomic_check(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
+
+ /* Set safe default bus flags if panel does not specify them */
+ if (!bridge_state->input_bus_cfg.flags)
+ bridge_state->input_bus_cfg.flags = bridge->timings->input_bus_flags;
+
+ /* Default to positive sync */
+ if (!(adjusted_mode->flags &
+ (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC)))
+ adjusted_mode->flags |= DRM_MODE_FLAG_PHSYNC;
+
+ if (!(adjusted_mode->flags &
+ (DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC)))
+ adjusted_mode->flags |= DRM_MODE_FLAG_PVSYNC;
+
+ return 0;
+}
+
+static int tegra_dbi_attach(struct drm_bridge *bridge,
+ struct drm_encoder *encoder,
+ enum drm_bridge_attach_flags flags)
+{
+ struct tegra_dbi_priv *priv = bridge_to_tegra_dbi(bridge);
+ struct device *dev = priv->dev;
+
+ priv->dc = dev_get_drvdata(dev->parent);
+ if (!priv->dc)
+ return -ENODEV;
+
+ return drm_bridge_attach(bridge->encoder, priv->output.bridge, bridge,
+ flags);
+}
+
+static enum drm_mode_status tegra_dbi_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode)
+{
+ if (mode->hdisplay > 800)
+ return MODE_H_ILLEGAL;
+
+ if (mode->vdisplay > 800)
+ return MODE_V_ILLEGAL;
+
+ return MODE_OK;
+}
+
+static const struct drm_bridge_funcs tegra_dbi_funcs = {
+ .attach = tegra_dbi_attach,
+ .mode_valid = tegra_dbi_mode_valid,
+
+ .atomic_enable = tegra_dbi_atomic_enable,
+ .atomic_disable = tegra_dbi_atomic_disable,
+ .atomic_check = tegra_dbi_atomic_check,
+
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+};
+
+static const struct drm_bridge_timings default_tegra_dbi_timings = {
+ .input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE |
+ DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE |
+ DRM_BUS_FLAG_DE_HIGH,
+};
+
+static int tegra_dbi_probe(struct platform_device *pdev)
+{
+ struct tegra_dbi_priv *priv;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct drm_bridge *bridge;
+ struct drm_panel *panel;
+ struct device_node *ep;
+ int ret;
+
+ priv = devm_drm_bridge_alloc(dev, struct tegra_dbi_priv, bridge,
+ &tegra_dbi_funcs);
+ if (IS_ERR(priv))
+ return PTR_ERR(priv);
+
+ priv->dev = dev;
+
+ ret = device_property_read_u32_array(dev, "nvidia,init-sequence",
+ priv->spi_init_seq, 4);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to get init sequence\n");
+
+ ret = devm_of_platform_populate(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to probe children\n");
+
+ /* Initialize MIPI DBI interface */
+ mutex_init(&priv->dbi.cmdlock);
+ priv->dbi.command = tegra_dbi_dbi_command;
+
+ ret = drm_of_find_panel_or_bridge(np, 1, 0, &panel, &bridge);
+ if (ret)
+ return ret;
+
+ if (panel) {
+ bridge = devm_drm_panel_bridge_add_typed(dev, panel,
+ DRM_MODE_CONNECTOR_DPI);
+ if (IS_ERR(bridge))
+ return PTR_ERR(bridge);
+ }
+
+ priv->output.bridge = bridge;
+ priv->output.panel = panel;
+
+ /* get input ep (port0/endpoint0) */
+ ret = -EINVAL;
+ ep = of_graph_get_endpoint_by_regs(np, 0, 0);
+ if (ep) {
+ ret = of_property_read_u32(ep, "bus-width", &priv->bus_width);
+ of_node_put(ep);
+ }
+
+ if (ret)
+ priv->bus_width = 24;
+
+ priv->bridge.of_node = np;
+ priv->bridge.timings = &default_tegra_dbi_timings;
+
+ ret = devm_drm_bridge_add(dev, &priv->bridge);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add bridge\n");
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+}
+
+static struct platform_driver tegra_dbi_driver = {
+ .driver.name = "tegra-dbi",
+ .probe = tegra_dbi_probe,
+};
+module_platform_driver(tegra_dbi_driver);
+
+MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
+MODULE_DESCRIPTION("Nvidia Tegra20/30 MIPI-DBI Type B driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 5c67928bcabfa..8f12d30cd2d8b 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -223,6 +223,7 @@ void tegra_dc_commit(struct tegra_dc *dc)
tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
}
+EXPORT_SYMBOL_GPL(tegra_dc_commit);
static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v,
unsigned int bpp)
@@ -3279,9 +3280,15 @@ static int tegra_dc_init_opp_table(struct tegra_dc *dc)
return 0;
}
+static void tegra_dc_release_dbi_pdev(void *pdev)
+{
+ platform_device_unregister(pdev);
+}
+
static int tegra_dc_probe(struct platform_device *pdev)
{
u64 dma_mask = dma_get_mask(pdev->dev.parent);
+ struct device_node *dbi_np;
struct tegra_dc *dc;
int err;
@@ -3382,6 +3389,28 @@ static int tegra_dc_probe(struct platform_device *pdev)
}
}
+ dbi_np = of_get_available_child_by_name(pdev->dev.of_node, "dbi");
+ if (dbi_np) {
+ struct platform_device_info dbi_info = {
+ .parent = &pdev->dev,
+ .name = "tegra-dbi",
+ .id = PLATFORM_DEVID_AUTO,
+ .fwnode = of_fwnode_handle(dbi_np),
+ .of_node_reused = true,
+ };
+
+ dc->dbi = platform_device_register_full(&dbi_info);
+ of_node_put(dbi_np);
+ if (IS_ERR(dc->dbi))
+ return dev_err_probe(&pdev->dev, PTR_ERR(dc->dbi),
+ "Failed to create DBI device\n");
+
+ err = devm_add_action_or_reset(&pdev->dev, tegra_dc_release_dbi_pdev,
+ dc->dbi);
+ if (err)
+ return err;
+ }
+
err = tegra_dc_rgb_probe(dc);
if (err < 0 && err != -ENODEV)
return dev_err_probe(&pdev->dev, err,
diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h
index 7005df32f485f..27dbee57f8f72 100644
--- a/drivers/gpu/drm/tegra/dc.h
+++ b/drivers/gpu/drm/tegra/dc.h
@@ -95,6 +95,7 @@ struct tegra_dc {
struct tegra_output *rgb;
struct tegra_pmc *pmc;
+ struct platform_device *dbi;
struct tegra_dc_stats stats;
struct list_head list;
diff --git a/drivers/gpu/drm/tegra/trace.c b/drivers/gpu/drm/tegra/trace.c
index 006f65c72a344..3aeb3825e2100 100644
--- a/drivers/gpu/drm/tegra/trace.c
+++ b/drivers/gpu/drm/tegra/trace.c
@@ -1,2 +1,5 @@
#define CREATE_TRACE_POINTS
#include "trace.h"
+
+EXPORT_TRACEPOINT_SYMBOL_GPL(dc_readl);
+EXPORT_TRACEPOINT_SYMBOL_GPL(dc_writel);
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 5/7] dt-bindings: display: panel: Document Hitachi TX10D07VM0BAA and LG LH400WV3 panels
2026-10-19 9:56 [PATCH v3 0/7] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface Svyatoslav Ryhel
` (3 preceding siblings ...)
2026-10-19 9:57 ` [PATCH v3 4/7] drm/tegra: Add support for 8-bit CPU interface Svyatoslav Ryhel
@ 2026-10-19 9:57 ` Svyatoslav Ryhel
2026-10-19 9:57 ` [PATCH v3 6/7] drm/panel: Add Hitachi TX10D07VM0BAA MIPI DBI Type B panel driver Svyatoslav Ryhel
2026-10-19 9:57 ` [PATCH v3 7/7] drm/panel: Add LG LH400WV3-SD04 " Svyatoslav Ryhel
6 siblings, 0 replies; 12+ messages in thread
From: Svyatoslav Ryhel @ 2026-10-19 9:57 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
Jonathan Hunter, Mikko Perttunen, Svyatoslav Ryhel
Cc: dri-devel, devicetree, linux-kernel, linux-tegra
Document Hitachi TX10D07VM0BAA and LG LH400WV3 MIPI DBI 4" WVGA panels
found in LG Optimus 2X P990.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
.../display/panel/hit,tx10d07vm0baa.yaml | 58 +++++++++++++++++++
1 file changed, 58 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/panel/hit,tx10d07vm0baa.yaml
diff --git a/Documentation/devicetree/bindings/display/panel/hit,tx10d07vm0baa.yaml b/Documentation/devicetree/bindings/display/panel/hit,tx10d07vm0baa.yaml
new file mode 100644
index 0000000000000..4642dc14c42eb
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/hit,tx10d07vm0baa.yaml
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/hit,tx10d07vm0baa.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Hitachi/LG 4" WVGA TFT LCD panel
+
+maintainers:
+ - Svyatoslav Ryhel <clamor95@gmail.com>
+
+allOf:
+ - $ref: panel-common.yaml#
+
+properties:
+ compatible:
+ items:
+ - enum:
+ - hit,tx10d07vm0baa
+ - lg,lh400wv3-sd04
+
+ avci-supply:
+ description: Panel power supply
+
+ iovcc-supply:
+ description: I/O voltage supply
+
+ reset-gpios: true
+
+ backlight: true
+ port: true
+
+required:
+ - compatible
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ panel {
+ compatible = "lg,lh400wv3-sd04";
+
+ reset-gpios = <&gpio 175 GPIO_ACTIVE_LOW>;
+
+ avci-supply = <&vcc_2v8_lcd>;
+ iovcc-supply = <&iovcc_1v8_lcd>;
+
+ backlight = <&backlight>;
+
+ port {
+ endpoint {
+ remote-endpoint = <&dbi_out>;
+ };
+ };
+ };
+...
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 6/7] drm/panel: Add Hitachi TX10D07VM0BAA MIPI DBI Type B panel driver
2026-10-19 9:56 [PATCH v3 0/7] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface Svyatoslav Ryhel
` (4 preceding siblings ...)
2026-10-19 9:57 ` [PATCH v3 5/7] dt-bindings: display: panel: Document Hitachi TX10D07VM0BAA and LG LH400WV3 panels Svyatoslav Ryhel
@ 2026-10-19 9:57 ` Svyatoslav Ryhel
2026-10-19 10:09 ` sashiko-bot
2026-10-19 9:57 ` [PATCH v3 7/7] drm/panel: Add LG LH400WV3-SD04 " Svyatoslav Ryhel
6 siblings, 1 reply; 12+ messages in thread
From: Svyatoslav Ryhel @ 2026-10-19 9:57 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
Jonathan Hunter, Mikko Perttunen, Svyatoslav Ryhel
Cc: dri-devel, devicetree, linux-kernel, linux-tegra
Add a driver for panel used in LG Optimus 2X P990. Panel is 4" WVGA MIPI
DBI Type B linked to DRM encoder via RGB to DBI bridge.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/gpu/drm/panel/Kconfig | 14 +
drivers/gpu/drm/panel/Makefile | 1 +
.../drm/panel/panel-hitachi-tx10d07vm0baa.c | 297 ++++++++++++++++++
3 files changed, 312 insertions(+)
create mode 100644 drivers/gpu/drm/panel/panel-hitachi-tx10d07vm0baa.c
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 53a1fc2913fee..5f68b78526c3e 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -274,6 +274,20 @@ config DRM_PANEL_HIMAX_HX8394
If M is selected the module will be called panel-himax-hx8394.
+config DRM_PANEL_HITACHI_TX10D07VM0BAA
+ tristate "Hitachi TX10D07VM0BAA MIPI DBI Type B panel"
+ depends on OF
+ depends on BACKLIGHT_CLASS_DEVICE
+ select DRM_MIPI_DBI
+ select VIDEOMODE_HELPERS
+ help
+ Say Y here if you want to enable support for the HITACHI
+ TX10D07VM0BAA MIPI DBI Type B panel found in the LG Optimus 2X
+ P990 smartphone.
+
+ To compile this driver as a module, choose M here: the module will
+ be called panel-hitachi-tx10d07vm0baa.
+
config DRM_PANEL_HYDIS_HV101HD1
tristate "Hydis HV101HD1 panel"
depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index f2c9c80a218f0..414267a49d922 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_DRM_PANEL_HIMAX_HX83112A) += panel-himax-hx83112a.o
obj-$(CONFIG_DRM_PANEL_HIMAX_HX83112B) += panel-himax-hx83112b.o
obj-$(CONFIG_DRM_PANEL_HIMAX_HX83121A) += panel-himax-hx83121a.o
obj-$(CONFIG_DRM_PANEL_HIMAX_HX8394) += panel-himax-hx8394.o
+obj-$(CONFIG_DRM_PANEL_HITACHI_TX10D07VM0BAA) += panel-hitachi-tx10d07vm0baa.o
obj-$(CONFIG_DRM_PANEL_HYDIS_HV101HD1) += panel-hydis-hv101hd1.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI7807S) += panel-ilitek-ili7807s.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI7836A) += panel-ilitek-ili7836a.o
diff --git a/drivers/gpu/drm/panel/panel-hitachi-tx10d07vm0baa.c b/drivers/gpu/drm/panel/panel-hitachi-tx10d07vm0baa.c
new file mode 100644
index 0000000000000..c3434cb96c86c
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-hitachi-tx10d07vm0baa.c
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/array_size.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/media-bus-format.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/drm_mipi_dbi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+static const struct regulator_bulk_data hitachi_tx10d07vm0baa_supplies[] = {
+ { .supply = "avci" }, { .supply = "iovcc" },
+};
+
+struct hitachi_tx10d07vm0baa {
+ struct drm_panel panel;
+ struct mipi_dbi *dbi;
+
+ struct regulator_bulk_data *supplies;
+ struct gpio_desc *reset_gpio;
+};
+
+static inline struct hitachi_tx10d07vm0baa *to_hitachi_tx10d07vm0baa(struct drm_panel *panel)
+{
+ return container_of(panel, struct hitachi_tx10d07vm0baa, panel);
+}
+
+#define panel_dbi_command(priv, cmd, seq...) \
+({ \
+ const u8 d[] = { seq }; \
+ struct drm_panel *panel = &(priv)->panel; \
+ struct device *dev = panel->dev; \
+ int ret; \
+ ret = mipi_dbi_command_stackbuf((priv)->dbi, cmd, d, ARRAY_SIZE(d)); \
+ if (ret) \
+ dev_err_ratelimited(dev, "error %d when sending command %#02x\n", ret, cmd); \
+ ret; \
+})
+
+static int hitachi_tx10d07vm0baa_prepare(struct drm_panel *panel)
+{
+ struct hitachi_tx10d07vm0baa *priv = to_hitachi_tx10d07vm0baa(panel);
+ struct device *dev = panel->dev;
+ int ret;
+
+ priv->dbi = dev_get_drvdata(dev->parent);
+ if (!priv->dbi)
+ return -ENODEV;
+
+ gpiod_set_value_cansleep(priv->reset_gpio, 1);
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(hitachi_tx10d07vm0baa_supplies), priv->supplies);
+ if (ret) {
+ dev_err(dev, "failed to enable power supplies: %d\n", ret);
+ return ret;
+ }
+
+ usleep_range(1000, 2000);
+
+ gpiod_set_value_cansleep(priv->reset_gpio, 0);
+
+ usleep_range(10000, 11000);
+
+ return 0;
+};
+
+static int hitachi_tx10d07vm0baa_enable(struct drm_panel *panel)
+{
+ struct hitachi_tx10d07vm0baa *priv = to_hitachi_tx10d07vm0baa(panel);
+
+ panel_dbi_command(priv, MIPI_DCS_SET_PARTIAL_ROWS, 0x00, 0x00, 0x03, 0x1f);
+ panel_dbi_command(priv, MIPI_DCS_SET_SCROLL_AREA, 0x00, 0x00, 0x03, 0x20,
+ 0x00, 0x00);
+
+ panel_dbi_command(priv, MIPI_DCS_SET_ADDRESS_MODE, 0x0a);
+ panel_dbi_command(priv, MIPI_DCS_SET_SCROLL_START, 0x00, 0x00);
+
+ panel_dbi_command(priv, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_24BIT);
+ panel_dbi_command(priv, MIPI_DCS_SET_TEAR_SCANLINE, 0x00, 0x00);
+
+ panel_dbi_command(priv, 0x71, 0x00); /* Ex_Vsync_en */
+
+ panel_dbi_command(priv, 0xb2, 0x00); /* VCSEL */
+ panel_dbi_command(priv, 0xb4, 0xaa); /* setvgmpm */
+ panel_dbi_command(priv, 0xb5, 0x33); /* rbias1 */
+ panel_dbi_command(priv, 0xb6, 0x03); /* rbias2 */
+
+ panel_dbi_command(priv, 0xb7, 0x1a, 0x33, 0x03, 0x03,
+ 0x03, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x00, 0x04, 0x00, 0x01, 0x01, 0x01); /* set_ddvdhp */
+ panel_dbi_command(priv, 0xb8, 0x1c, 0x53, 0x03, 0x03,
+ 0x00, 0x01, 0x02, 0x00, 0x00, 0x04,
+ 0x00, 0x01, 0x01); /* set_ddvdhm */
+
+ panel_dbi_command(priv, 0xb9, 0x0a, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x02, 0x01); /* set_vgh */
+ panel_dbi_command(priv, 0xba, 0x0f, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x02, 0x01); /* set_vgl */
+ panel_dbi_command(priv, 0xbb, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x01); /* set_vcl */
+
+ panel_dbi_command(priv, 0xc1, 0x01); /* number of lines */
+ panel_dbi_command(priv, 0xc2, 0x08); /* number of fp lines */
+ panel_dbi_command(priv, 0xc3, 0x04); /* gateset(1) */
+ panel_dbi_command(priv, 0xc4, 0x4c); /* 1h period */
+ panel_dbi_command(priv, 0xc5, 0x03); /* source precharge */
+ panel_dbi_command(priv, 0xc6, 0xc4, 0x04); /* source precharge timing */
+ panel_dbi_command(priv, 0xc7, 0x00); /* source level */
+ panel_dbi_command(priv, 0xc8, 0x02); /* number of bp lines */
+ panel_dbi_command(priv, 0xc9, 0x10); /* gateset(2) */
+ panel_dbi_command(priv, 0xca, 0x04, 0x04); /* gateset(3) */
+ panel_dbi_command(priv, 0xcb, 0x03); /* gateset(4) */
+ panel_dbi_command(priv, 0xcc, 0x12); /* gateset(5) */
+ panel_dbi_command(priv, 0xcd, 0x12); /* gateset(6) */
+ panel_dbi_command(priv, 0xce, 0x30); /* gateset(7) */
+ panel_dbi_command(priv, 0xcf, 0x30); /* gateset(8) */
+ panel_dbi_command(priv, 0xd0, 0x40); /* gateset(9) */
+ panel_dbi_command(priv, 0xd1, 0x22); /* flhw */
+ panel_dbi_command(priv, 0xd2, 0x22); /* vckhw */
+ panel_dbi_command(priv, 0xd3, 0x04); /* flt */
+ panel_dbi_command(priv, 0xd4, 0x14); /* tctrl */
+ panel_dbi_command(priv, 0xd6, 0x02); /* dotinv */
+ panel_dbi_command(priv, 0xd7, 0x00); /* on/off sequence period */
+
+ panel_dbi_command(priv, 0xd8, 0x01, 0x05, 0x06, 0x0d,
+ 0x18, 0x09, 0x22, 0x23, 0x00); /* ponseqa */
+ panel_dbi_command(priv, 0xd9, 0x24, 0x01); /* ponseqb */
+ panel_dbi_command(priv, 0xde, 0x09, 0x0f, 0x21, 0x12,
+ 0x04); /* ponseqc */
+
+ panel_dbi_command(priv, 0xdf, 0x02, 0x06, 0x06, 0x06,
+ 0x06, 0x00); /* pofseqa */
+ panel_dbi_command(priv, 0xe0, 0x01); /* pofseqb */
+
+ panel_dbi_command(priv, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 0xff);
+ panel_dbi_command(priv, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x40);
+
+ panel_dbi_command(priv, 0xe2, 0x00, 0x00); /* cabc pwm */
+ panel_dbi_command(priv, 0xe3, 0x03); /* cabc */
+ panel_dbi_command(priv, 0xe4, 0x66, 0x7b, 0x90, 0xa5,
+ 0xbb, 0xc7, 0xe1, 0xe5); /* cabc brightness */
+ panel_dbi_command(priv, 0xe5, 0xc5, 0xc5, 0xc9, 0xc9,
+ 0xd1, 0xe1, 0xf1, 0xfe); /* cabc brightness */
+ panel_dbi_command(priv, 0xe7, 0x2a); /* cabc */
+ panel_dbi_command(priv, 0xe8, 0x00); /* brt_rev */
+ panel_dbi_command(priv, 0xe9, 0x00); /* tefreq */
+
+ panel_dbi_command(priv, 0xea, 0x01); /* high speed ram */
+
+ panel_dbi_command(priv, 0xeb, 0x00, 0x33, 0x0e, 0x15,
+ 0xb7, 0x78, 0x88, 0x0f); /* gamma setting r pos */
+ panel_dbi_command(priv, 0xec, 0x00, 0x33, 0x0e, 0x15,
+ 0xb7, 0x78, 0x88, 0x0f); /* gamma setting r neg */
+ panel_dbi_command(priv, 0xed, 0x00, 0x33, 0x0e, 0x15,
+ 0xb7, 0x78, 0x88, 0x0f); /* gamma setting g pos */
+ panel_dbi_command(priv, 0xee, 0x00, 0x33, 0x0e, 0x15,
+ 0xb7, 0x78, 0x88, 0x0f); /* gamma setting g neg */
+ panel_dbi_command(priv, 0xef, 0x00, 0x33, 0x0e, 0x15,
+ 0xb7, 0x78, 0x88, 0x0f); /* gamma setting b pos */
+ panel_dbi_command(priv, 0xf0, 0x00, 0x33, 0x0e, 0x15,
+ 0xb7, 0x78, 0x88, 0x0f); /* gamma setting b neg */
+
+ panel_dbi_command(priv, MIPI_DCS_EXIT_SLEEP_MODE);
+ msleep(110);
+
+ panel_dbi_command(priv, MIPI_DCS_SET_DISPLAY_ON);
+
+ return 0;
+}
+
+static int hitachi_tx10d07vm0baa_disable(struct drm_panel *panel)
+{
+ struct hitachi_tx10d07vm0baa *priv = to_hitachi_tx10d07vm0baa(panel);
+
+ panel_dbi_command(priv, MIPI_DCS_SET_DISPLAY_OFF);
+ msleep(35);
+
+ panel_dbi_command(priv, MIPI_DCS_ENTER_SLEEP_MODE);
+ msleep(50);
+
+ return 0;
+}
+
+static int hitachi_tx10d07vm0baa_unprepare(struct drm_panel *panel)
+{
+ struct hitachi_tx10d07vm0baa *priv = to_hitachi_tx10d07vm0baa(panel);
+
+ gpiod_set_value_cansleep(priv->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(hitachi_tx10d07vm0baa_supplies), priv->supplies);
+
+ msleep(50);
+
+ return 0;
+}
+
+static const struct drm_display_mode hitachi_tx10d07vm0baa_mode = {
+ .clock = (480 + 10 + 10 + 10) * (800 + 4 + 4 + 4) * 60 / 1000,
+ .hdisplay = 480,
+ .hsync_start = 480 + 10,
+ .hsync_end = 480 + 10 + 10,
+ .htotal = 480 + 10 + 10 + 10,
+ .vdisplay = 800,
+ .vsync_start = 800 + 4,
+ .vsync_end = 800 + 4 + 4,
+ .vtotal = 800 + 4 + 4 + 4,
+ .width_mm = 86,
+ .height_mm = 52,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+};
+
+static int hitachi_tx10d07vm0baa_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+
+ drm_display_info_set_bus_formats(&connector->display_info,
+ &bus_format, 1);
+
+ connector->display_info.bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE |
+ DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE |
+ DRM_BUS_FLAG_DE_HIGH |
+ DRM_BUS_FLAG_DATA_LSB_TO_MSB;
+
+ return drm_connector_helper_get_modes_fixed(connector, &hitachi_tx10d07vm0baa_mode);
+}
+
+static const struct drm_panel_funcs hitachi_tx10d07vm0baa_panel_funcs = {
+ .prepare = hitachi_tx10d07vm0baa_prepare,
+ .enable = hitachi_tx10d07vm0baa_enable,
+ .disable = hitachi_tx10d07vm0baa_disable,
+ .unprepare = hitachi_tx10d07vm0baa_unprepare,
+ .get_modes = hitachi_tx10d07vm0baa_get_modes,
+};
+
+static int hitachi_tx10d07vm0baa_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct hitachi_tx10d07vm0baa *priv;
+ int ret;
+
+ priv = devm_drm_panel_alloc(dev, struct hitachi_tx10d07vm0baa, panel,
+ &hitachi_tx10d07vm0baa_panel_funcs, DRM_MODE_CONNECTOR_DPI);
+ if (IS_ERR(priv))
+ return PTR_ERR(priv);
+
+ ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(hitachi_tx10d07vm0baa_supplies),
+ hitachi_tx10d07vm0baa_supplies, &priv->supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get supplies\n");
+
+ priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(priv->reset_gpio),
+ "Failed to get reset gpio\n");
+
+ ret = drm_panel_of_backlight(&priv->panel);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get backlight\n");
+
+ ret = devm_drm_panel_add(dev, &priv->panel);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add panel\n");
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+}
+
+static const struct of_device_id hitachi_tx10d07vm0baa_of_match[] = {
+ { .compatible = "hit,tx10d07vm0baa" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, hitachi_tx10d07vm0baa_of_match);
+
+static struct platform_driver hitachi_tx10d07vm0baa_driver = {
+ .driver = {
+ .name = "panel-hitachi-tx10d07vm0baa",
+ .of_match_table = hitachi_tx10d07vm0baa_of_match,
+ },
+ .probe = hitachi_tx10d07vm0baa_probe,
+};
+module_platform_driver(hitachi_tx10d07vm0baa_driver);
+
+MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
+MODULE_DESCRIPTION("HITACHI TX10D07VM0BAA MIPI DBI Type B panel driver");
+MODULE_LICENSE("GPL");
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 7/7] drm/panel: Add LG LH400WV3-SD04 MIPI DBI Type B panel driver
2026-10-19 9:56 [PATCH v3 0/7] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface Svyatoslav Ryhel
` (5 preceding siblings ...)
2026-10-19 9:57 ` [PATCH v3 6/7] drm/panel: Add Hitachi TX10D07VM0BAA MIPI DBI Type B panel driver Svyatoslav Ryhel
@ 2026-10-19 9:57 ` Svyatoslav Ryhel
6 siblings, 0 replies; 12+ messages in thread
From: Svyatoslav Ryhel @ 2026-10-19 9:57 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
Jonathan Hunter, Mikko Perttunen, Svyatoslav Ryhel
Cc: dri-devel, devicetree, linux-kernel, linux-tegra
Add a driver for panel used in LG Optimus 2X P990. Panel is 4" WVGA MIPI
DBI Type B linked to DRM encoder via RGB to DBI bridge.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/gpu/drm/panel/Kconfig | 13 ++
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-lg-lh400wv3.c | 236 ++++++++++++++++++++++
3 files changed, 250 insertions(+)
create mode 100644 drivers/gpu/drm/panel/panel-lg-lh400wv3.c
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 5f68b78526c3e..f29b63ad8ba0f 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -554,6 +554,19 @@ config DRM_PANEL_LG_LG4573
Say Y here if you want to enable support for LG4573 RGB panel.
To compile this driver as a module, choose M here.
+config DRM_PANEL_LG_LH400WV3
+ tristate "LG LH400WV3 MIPI DBI Type B panel"
+ depends on OF
+ depends on BACKLIGHT_CLASS_DEVICE
+ select DRM_MIPI_DBI
+ select VIDEOMODE_HELPERS
+ help
+ Say Y here if you want to enable support for the LG LH400WV3 MIPI
+ DBI Type B panel found in the LG Optimus 2X P990 smartphone.
+
+ To compile this driver as a module, choose M here: the module will
+ be called panel-lg-lh400wv3.
+
config DRM_PANEL_LG_SW43408
tristate "LG SW43408 panel"
depends on GPIOLIB
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 414267a49d922..842a2a973777f 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_DRM_PANEL_LINCOLNTECH_LCD197) += panel-lincolntech-lcd197.o
obj-$(CONFIG_DRM_PANEL_LG_LB035Q02) += panel-lg-lb035q02.o
obj-$(CONFIG_DRM_PANEL_LG_LD070WX3) += panel-lg-ld070wx3.o
obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
+obj-$(CONFIG_DRM_PANEL_LG_LH400WV3) += panel-lg-lh400wv3.o
obj-$(CONFIG_DRM_PANEL_LG_SW43408) += panel-lg-sw43408.o
obj-$(CONFIG_DRM_PANEL_LXD_M9189A) += panel-lxd-m9189a.o
obj-$(CONFIG_DRM_PANEL_MAGNACHIP_D53E6EA8966) += panel-magnachip-d53e6ea8966.o
diff --git a/drivers/gpu/drm/panel/panel-lg-lh400wv3.c b/drivers/gpu/drm/panel/panel-lg-lh400wv3.c
new file mode 100644
index 0000000000000..cb2a09699fc6d
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-lg-lh400wv3.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/array_size.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/media-bus-format.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/drm_mipi_dbi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+static const struct regulator_bulk_data lg_lh400wv3_supplies[] = {
+ { .supply = "avci" }, { .supply = "iovcc" },
+};
+
+struct lg_lh400wv3 {
+ struct drm_panel panel;
+ struct mipi_dbi *dbi;
+
+ struct regulator_bulk_data *supplies;
+ struct gpio_desc *reset_gpio;
+};
+
+static inline struct lg_lh400wv3 *to_lg_lh400wv3(struct drm_panel *panel)
+{
+ return container_of(panel, struct lg_lh400wv3, panel);
+}
+
+#define panel_dbi_command(priv, cmd, seq...) \
+({ \
+ const u8 d[] = { seq }; \
+ struct drm_panel *panel = &(priv)->panel; \
+ struct device *dev = panel->dev; \
+ int ret; \
+ ret = mipi_dbi_command_stackbuf((priv)->dbi, cmd, d, ARRAY_SIZE(d)); \
+ if (ret) \
+ dev_err_ratelimited(dev, "error %d when sending command %#02x\n", ret, cmd); \
+ ret; \
+})
+
+static int lg_lh400wv3_prepare(struct drm_panel *panel)
+{
+ struct lg_lh400wv3 *priv = to_lg_lh400wv3(panel);
+ struct device *dev = panel->dev;
+ int ret;
+
+ priv->dbi = dev_get_drvdata(dev->parent);
+ if (!priv->dbi)
+ return -ENODEV;
+
+ gpiod_set_value_cansleep(priv->reset_gpio, 1);
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(lg_lh400wv3_supplies), priv->supplies);
+ if (ret) {
+ dev_err(dev, "failed to enable power supplies: %d\n", ret);
+ return ret;
+ }
+
+ usleep_range(1000, 2000);
+
+ gpiod_set_value_cansleep(priv->reset_gpio, 0);
+
+ usleep_range(10000, 11000);
+
+ return 0;
+};
+
+static int lg_lh400wv3_enable(struct drm_panel *panel)
+{
+ struct lg_lh400wv3 *priv = to_lg_lh400wv3(panel);
+
+ panel_dbi_command(priv, MIPI_DCS_EXIT_INVERT_MODE);
+ panel_dbi_command(priv, MIPI_DCS_SET_TEAR_ON);
+
+ panel_dbi_command(priv, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_24BIT |
+ MIPI_DCS_PIXEL_FMT_24BIT << 4);
+
+ panel_dbi_command(priv, 0xb2, 0x00, 0xc8);
+ panel_dbi_command(priv, 0xb3, 0x00);
+ panel_dbi_command(priv, 0xb4, 0x04);
+ panel_dbi_command(priv, 0xb5, 0x42, 0x10, 0x10, 0x00, 0x20);
+ panel_dbi_command(priv, 0xb6, 0x0b, 0x0f, 0x3c, 0x13, 0x13, 0xe8);
+ panel_dbi_command(priv, 0xb7, 0x4c, 0x06, 0x0c, 0x00, 0x00);
+
+ panel_dbi_command(priv, 0xc0, 0x01, 0x11);
+ panel_dbi_command(priv, 0xc3, 0x07, 0x03, 0x04, 0x04, 0x04);
+ panel_dbi_command(priv, 0xc4, 0x12, 0x24, 0x18, 0x18, 0x02, 0x49);
+ panel_dbi_command(priv, 0xc5, 0x65);
+ panel_dbi_command(priv, 0xc6, 0x41, 0x63);
+
+ panel_dbi_command(priv, 0xd0, 0x00, 0x46, 0x74, 0x32, 0x1d, 0x03, 0x51, 0x15, 0x04);
+ panel_dbi_command(priv, 0xd1, 0x00, 0x46, 0x74, 0x32, 0x1d, 0x03, 0x51, 0x15, 0x04);
+ panel_dbi_command(priv, 0xd2, 0x00, 0x46, 0x74, 0x32, 0x1f, 0x03, 0x51, 0x15, 0x04);
+ panel_dbi_command(priv, 0xd3, 0x00, 0x46, 0x74, 0x32, 0x1f, 0x03, 0x51, 0x15, 0x04);
+ panel_dbi_command(priv, 0xd4, 0x01, 0x46, 0x74, 0x25, 0x00, 0x03, 0x51, 0x15, 0x04);
+ panel_dbi_command(priv, 0xd5, 0x01, 0x46, 0x74, 0x25, 0x00, 0x03, 0x51, 0x15, 0x04);
+
+ panel_dbi_command(priv, MIPI_DCS_SET_COLUMN_ADDRESS, 0x00, 0x00, 0x01, 0xdf);
+ panel_dbi_command(priv, MIPI_DCS_SET_PAGE_ADDRESS, 0x00, 0x00, 0x03, 0x1f);
+
+ panel_dbi_command(priv, MIPI_DCS_EXIT_SLEEP_MODE);
+ msleep(120);
+
+ panel_dbi_command(priv, MIPI_DCS_WRITE_MEMORY_START);
+ panel_dbi_command(priv, MIPI_DCS_SET_DISPLAY_ON);
+
+ return 0;
+}
+
+static int lg_lh400wv3_disable(struct drm_panel *panel)
+{
+ struct lg_lh400wv3 *priv = to_lg_lh400wv3(panel);
+
+ panel_dbi_command(priv, MIPI_DCS_SET_DISPLAY_OFF);
+ panel_dbi_command(priv, MIPI_DCS_ENTER_SLEEP_MODE);
+ msleep(150);
+
+ panel_dbi_command(priv, 0xc1, 0x01);
+ usleep_range(10000, 11000);
+
+ return 0;
+};
+
+static int lg_lh400wv3_unprepare(struct drm_panel *panel)
+{
+ struct lg_lh400wv3 *priv = to_lg_lh400wv3(panel);
+
+ gpiod_set_value_cansleep(priv->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(lg_lh400wv3_supplies), priv->supplies);
+
+ msleep(50);
+
+ return 0;
+}
+
+static const struct drm_display_mode lg_lh400wv3_mode = {
+ .clock = (480 + 10 + 10 + 10) * (800 + 4 + 4 + 4) * 60 / 1000,
+ .hdisplay = 480,
+ .hsync_start = 480 + 10,
+ .hsync_end = 480 + 10 + 10,
+ .htotal = 480 + 10 + 10 + 10,
+ .vdisplay = 800,
+ .vsync_start = 800 + 4,
+ .vsync_end = 800 + 4 + 4,
+ .vtotal = 800 + 4 + 4 + 4,
+ .width_mm = 86,
+ .height_mm = 52,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+};
+
+static int lg_lh400wv3_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+
+ drm_display_info_set_bus_formats(&connector->display_info,
+ &bus_format, 1);
+
+ connector->display_info.bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE |
+ DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE |
+ DRM_BUS_FLAG_DE_HIGH |
+ DRM_BUS_FLAG_DATA_LSB_TO_MSB;
+
+ return drm_connector_helper_get_modes_fixed(connector, &lg_lh400wv3_mode);
+}
+
+static const struct drm_panel_funcs lg_lh400wv3_panel_funcs = {
+ .prepare = lg_lh400wv3_prepare,
+ .enable = lg_lh400wv3_enable,
+ .disable = lg_lh400wv3_disable,
+ .unprepare = lg_lh400wv3_unprepare,
+ .get_modes = lg_lh400wv3_get_modes,
+};
+
+static int lg_lh400wv3_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct lg_lh400wv3 *priv;
+ int ret;
+
+ priv = devm_drm_panel_alloc(dev, struct lg_lh400wv3, panel,
+ &lg_lh400wv3_panel_funcs, DRM_MODE_CONNECTOR_DPI);
+ if (IS_ERR(priv))
+ return PTR_ERR(priv);
+
+ ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(lg_lh400wv3_supplies),
+ lg_lh400wv3_supplies, &priv->supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get supplies\n");
+
+ priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(priv->reset_gpio),
+ "Failed to get reset gpio\n");
+
+ ret = drm_panel_of_backlight(&priv->panel);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get backlight\n");
+
+ ret = devm_drm_panel_add(dev, &priv->panel);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add panel\n");
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+}
+
+static const struct of_device_id lg_lh400wv3_of_match[] = {
+ { .compatible = "lg,lh400wv3-sd04" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, lg_lh400wv3_of_match);
+
+static struct platform_driver lg_lh400wv3_driver = {
+ .driver = {
+ .name = "panel-lg-lh400wv3",
+ .of_match_table = lg_lh400wv3_of_match,
+ },
+ .probe = lg_lh400wv3_probe,
+};
+module_platform_driver(lg_lh400wv3_driver);
+
+MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
+MODULE_DESCRIPTION("LG LH400WV3-SD04 MIPI DBI Type B panel driver");
+MODULE_LICENSE("GPL");
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v3 6/7] drm/panel: Add Hitachi TX10D07VM0BAA MIPI DBI Type B panel driver
2026-10-19 9:57 ` [PATCH v3 6/7] drm/panel: Add Hitachi TX10D07VM0BAA MIPI DBI Type B panel driver Svyatoslav Ryhel
@ 2026-10-19 10:09 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-10-19 10:09 UTC (permalink / raw)
To: Svyatoslav Ryhel; +Cc: dri-devel, devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] drm/panel: tx10d07vm0baa: DBI commands in disable() use freed tegra-dbi GPIOs
- [High] drm/panel: unchecked priv->dbi in hitachi_tx10d07vm0baa_enable()
- [Medium] drm/panel: hitachi-tx10d07vm0baa: swapped width_mm and height_mm in mode
--
commit a653f9af8d9f186ab20cfe9e9245165b2be8f4e3
Author: Svyatoslav Ryhel <clamor95@gmail.com>
drm/panel: Add Hitachi TX10D07VM0BAA MIPI DBI Type B panel driver
This commit adds a DRM panel driver for the Hitachi TX10D07VM0BAA 4" WVGA MIPI
DBI Type B panel used in the LG Optimus 2X P990 smartphone.
> diff --git a/drivers/gpu/drm/panel/panel-hitachi-tx10d07vm0baa.c b/drivers/gpu/drm/panel/panel-hitachi-tx10d07vm0baa.c
> new file mode 100644
> index 0000000000000..c3434cb96c86c
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-hitachi-tx10d07vm0baa.c
> @@ -0,0 +1,297 @@
[ ... ]
> +static int hitachi_tx10d07vm0baa_prepare(struct drm_panel *panel)
> +{
> + struct hitachi_tx10d07vm0baa *priv = to_hitachi_tx10d07vm0baa(panel);
> + struct device *dev = panel->dev;
> + int ret;
> +
> + priv->dbi = dev_get_drvdata(dev->parent);
> + if (!priv->dbi)
> + return -ENODEV;
> +
> + gpiod_set_value_cansleep(priv->reset_gpio, 1);
> +
> + ret = regulator_bulk_enable(ARRAY_SIZE(hitachi_tx10d07vm0baa_supplies), priv->supplies);
> + if (ret) {
> + dev_err(dev, "failed to enable power supplies: %d\n", ret);
> + return ret;
> + }
> +
> + usleep_range(1000, 2000);
> +
> + gpiod_set_value_cansleep(priv->reset_gpio, 0);
> +
> + usleep_range(10000, 11000);
> +
> + return 0;
> +};
> +
> +static int hitachi_tx10d07vm0baa_enable(struct drm_panel *panel)
> +{
> + struct hitachi_tx10d07vm0baa *priv = to_hitachi_tx10d07vm0baa(panel);
> +
> + panel_dbi_command(priv, MIPI_DCS_SET_PARTIAL_ROWS, 0x00, 0x00, 0x03, 0x1f);
[Severity: High]
Can priv->dbi be NULL when hitachi_tx10d07vm0baa_enable() (and later
hitachi_tx10d07vm0baa_disable()) calls panel_dbi_command()?
In hitachi_tx10d07vm0baa_probe(), priv->dbi is not initialized. Instead,
hitachi_tx10d07vm0baa_prepare() looks up dev_get_drvdata(dev->parent) and
returns -ENODEV if priv->dbi is NULL (for instance, if the panel device tree
node is not a child of a bound DBI parent device, or if the parent has no
drvdata set).
During an atomic modeset commit, the prepare path runs:
drm_atomic_helper_commit_modeset_enables()
drm_atomic_bridge_chain_pre_enable()
panel_bridge_atomic_pre_enable()
drm_panel_prepare()
hitachi_tx10d07vm0baa_prepare() -> returns -ENODEV
When hitachi_tx10d07vm0baa_prepare() returns an error, drm_panel_prepare()
logs the error and skips setting panel->prepared = true, but does not abort
the atomic commit. Next in the same commit:
drm_atomic_helper_commit_modeset_enables()
drm_atomic_bridge_chain_enable()
panel_bridge_atomic_enable()
drm_panel_enable()
hitachi_tx10d07vm0baa_enable()
Because drm_panel_enable() only checks panel and panel->enabled without
checking panel->prepared, it unconditionally calls
hitachi_tx10d07vm0baa_enable(). That passes a NULL priv->dbi to
panel_dbi_command() -> mipi_dbi_command_stackbuf() -> mipi_dbi_command_buf(),
which dereferences dbi at mutex_lock(&dbi->cmdlock).
> + panel_dbi_command(priv, MIPI_DCS_SET_SCROLL_AREA, 0x00, 0x00, 0x03, 0x20,
> + 0x00, 0x00);
[ ... ]
> +static int hitachi_tx10d07vm0baa_disable(struct drm_panel *panel)
> +{
> + struct hitachi_tx10d07vm0baa *priv = to_hitachi_tx10d07vm0baa(panel);
> +
> + panel_dbi_command(priv, MIPI_DCS_SET_DISPLAY_OFF);
> + msleep(35);
> +
> + panel_dbi_command(priv, MIPI_DCS_ENTER_SLEEP_MODE);
> + msleep(50);
> +
> + return 0;
> +}
[Severity: High]
When this panel is paired with its parent tegra-dbi bridge, does
hitachi_tx10d07vm0baa_disable() end up using freed GPIO descriptors when
sending MIPI_DCS_SET_DISPLAY_OFF and MIPI_DCS_ENTER_SLEEP_MODE?
In drivers/gpu/drm/tegra/dbi.c, tegra_dbi_probe() wraps the panel in a panel
bridge via devm_drm_panel_bridge_add_typed() and attaches it downstream of
tegra_dbi->bridge in tegra_dbi_attach(). At the end of
tegra_dbi_atomic_enable(), tegra-dbi releases its shared DBI GPIOs with
gpiod_put() and gpiod_put_array(priv->data_gpios) without clearing the
pointers.
When the display pipeline is disabled, drm_atomic_bridge_chain_disable()
iterates encoder->bridge_chain in reverse order:
drm_atomic_bridge_chain_disable()
panel_bridge_atomic_disable()
drm_panel_disable()
hitachi_tx10d07vm0baa_disable()
panel_dbi_command()
mipi_dbi_command_stackbuf()
tegra_dbi_dbi_command()
tegra_dbi_write()
Because panel_bridge_atomic_disable() runs before tegra_dbi_atomic_disable(),
tegra_dbi_write() sees the non-NULL dangling priv->data_gpios pointer and
dereferences the freed struct gpio_descs memory and freed GPIO descriptors
before tegra_dbi_atomic_disable() has a chance to call
tegra_dbi_prepare_gpios():
drivers/gpu/drm/tegra/dbi.c:tegra_dbi_atomic_disable() {
struct tegra_dbi_priv *priv = bridge_to_tegra_dbi(bridge);
int ret;
ret = tegra_dbi_prepare_gpios(priv);
if (ret)
return;
if (priv->output.panel)
drm_panel_disable(priv->output.panel);
...
}
When tegra_dbi_atomic_disable() subsequently runs, its call to
drm_panel_disable() becomes a no-op because panel->enabled is already false.
> +
> +static int hitachi_tx10d07vm0baa_unprepare(struct drm_panel *panel)
> +{
[ ... ]
> +static const struct drm_display_mode hitachi_tx10d07vm0baa_mode = {
> + .clock = (480 + 10 + 10 + 10) * (800 + 4 + 4 + 4) * 60 / 1000,
> + .hdisplay = 480,
> + .hsync_start = 480 + 10,
> + .hsync_end = 480 + 10 + 10,
> + .htotal = 480 + 10 + 10 + 10,
> + .vdisplay = 800,
> + .vsync_start = 800 + 4,
> + .vsync_end = 800 + 4 + 4,
> + .vtotal = 800 + 4 + 4 + 4,
> + .width_mm = 86,
> + .height_mm = 52,
> + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
> +};
[Severity: Medium]
Are width_mm and height_mm swapped in hitachi_tx10d07vm0baa_mode?
The active mode resolution is portrait (hdisplay = 480, vdisplay = 800,
matching MIPI_DCS_SET_PARTIAL_ROWS 0..799 and MIPI_DCS_SET_SCROLL_AREA 800 in
hitachi_tx10d07vm0baa_enable()), whereas width_mm = 86 and height_mm = 52 are
landscape dimensions. When hitachi_tx10d07vm0baa_get_modes() calls
drm_connector_helper_get_modes_fixed(), those values are copied to
connector->display_info, resulting in non-square DPI calculations in
userspace. Should width_mm be 52 and height_mm be 86?
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261019095705.91062-1-clamor95@gmail.com?part=6
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 3/7] dt-bindings: display: tegra: Document 8-bit CPU parallel interface
2026-10-19 9:57 ` [PATCH v3 3/7] dt-bindings: display: tegra: Document 8-bit CPU parallel interface Svyatoslav Ryhel
@ 2026-10-19 10:09 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-10-19 10:09 UTC (permalink / raw)
To: Svyatoslav Ryhel; +Cc: dri-devel, devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] dt-bindings: display: tegra: unvalidated dbi example and missing $nodename
--
commit 8ceaec80ccd97a96cf07da8add147a083a0f5944
Author: Svyatoslav Ryhel <clamor95@gmail.com>
dt-bindings: display: tegra: Document 8-bit CPU parallel interface
This commit adds a devicetree binding schema for the 8-bit CPU parallel
MIPI DBI Type B interface on Tegra20 and Tegra30 display controllers, and
references it from the Tegra20 display controller binding.
> diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra-dbi.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra-dbi.yaml
> new file mode 100644
> index 0000000000000..ada7b35778933
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra-dbi.yaml
> @@ -0,0 +1,138 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra-dbi.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Nvidia Tegra DC based MIPI DBI Type B interface
> +
> +maintainers:
> + - Svyatoslav Ryhel <clamor95@gmail.com>
> +
> +description: The display controller in Tegra20/30 SoCs features an
> + 8-bit SPI interface that closely resembles the MIPI DBI Type B
> + protocol and is referred to as '8-bit CPU'. Each display controller
> + provides two such interfaces, which can be used to send MIPI DCS
> + commands to initialize and control the panel while image data is
> + transmitted via 16/18/24-line RGB.
> +
> +properties:
> + dc-gpios:
> + description: Data/command selection pin.
> + maxItems: 1
> +
> + rw-gpios:
> + description: Read/write pin.
> + maxItems: 1
> +
> + cs-gpios:
> + description: Chip select pin.
> + maxItems: 1
> +
> + data-gpios:
> + description: Specifies a set of 8 gpio pins used to transfer data.
> + minItems: 8
> + maxItems: 8
> +
> + nvidia,init-sequence:
> + $ref: /schemas/types.yaml#/definitions/uint32-array
> + description:
> + Device specific set of values used in DC DISP_SPI_INIT_SEQ registers.
> + minItems: 4
> + maxItems: 4
> +
> + panel:
> + type: object
> + description: Node of supported panel driven by the bridge.
[ ... ]
> +examples:
> + - |
> + #include <dt-bindings/gpio/gpio.h>
> +
> + dbi {
> + dc-gpios = <&gpio 110 GPIO_ACTIVE_HIGH>;
> + rw-gpios = <&gpio 11 GPIO_ACTIVE_HIGH>;
> + cs-gpios = <&gpio 108 GPIO_ACTIVE_HIGH>;
> +
> + data-gpios = <&gpio 32 GPIO_ACTIVE_HIGH>, <&gpio 33 GPIO_ACTIVE_HIGH>,
> + <&gpio 34 GPIO_ACTIVE_HIGH>, <&gpio 35 GPIO_ACTIVE_HIGH>,
> + <&gpio 36 GPIO_ACTIVE_HIGH>, <&gpio 37 GPIO_ACTIVE_HIGH>,
> + <&gpio 38 GPIO_ACTIVE_HIGH>, <&gpio 39 GPIO_ACTIVE_HIGH>;
> +
> + nvidia,init-sequence = <0x0000002c 0x0 0x0 0x00005000>;
> +
> + panel {
> + compatible = "hit,tx10d07vm0baa";
> +
> + reset-gpios = <&gpio 175 GPIO_ACTIVE_LOW>;
> +
> + avci-supply = <&vcc_2v8_lcd>;
> + iovcc-supply = <&iovcc_1v8_lcd>;
> +
> + backlight = <&backlight>;
> +
> + port {
> + panel_input: endpoint {
> + remote-endpoint = <&bridge_output>;
> + };
> + };
> + };
> +
> + ports {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + port@0 {
> + reg = <0>;
> +
> + bridge_input: endpoint {
> + remote-endpoint = <&dpi_output>;
> + bus-width = <24>;
> + };
> + };
> +
> + port@1 {
> + reg = <1>;
> +
> + bridge_output: endpoint {
> + remote-endpoint = <&panel_input>;
> + };
> + };
> + };
> + };
[Severity: Low]
Because Documentation/devicetree/bindings/display/tegra/nvidia,tegra-dbi.yaml
defines neither compatible, $nodename (for example, $nodename: const: dbi),
nor a select block, dt-mk-schema generates select: false for this schema. As
a result, the schema is only evaluated when referenced via $ref from the dbi
property of nvidia,tegra20-dc.yaml, and the standalone dbi node in examples
is never selected or validated by dt-validate against nvidia,tegra-dbi.yaml.
In addition, at this point in the series, the panel node in the example uses
compatible = "hit,tx10d07vm0baa" and avci-supply = <&vcc_2v8_lcd>, which are
not documented until commit 5002ce93d3d5 ("dt-bindings: display: panel:
Document Hitachi TX10D07VM0BAA and LG LH400WV3 panels"), causing make
dt_binding_check to fail during bisection.
Also, the panel property is only defined as type: object without referencing
/schemas/display/panel/panel-common.yaml# or specifying additionalProperties
or unevaluatedProperties, and the bridge_input label on the port@0 endpoint
is unused.
Should properties in nvidia,tegra-dbi.yaml include $nodename: const: dbi (or
place the example inside a nvidia,tegra20-dc node) and reference
panel-common.yaml# for panel, with the panel binding ordered before this
patch so make dt_binding_check validates the example cleanly?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261019095705.91062-1-clamor95@gmail.com?part=3
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 2/7] drm/tegra: rgb: Parameterize configuration based on bus flags
2026-10-19 9:57 ` [PATCH v3 2/7] drm/tegra: rgb: Parameterize configuration based on bus flags Svyatoslav Ryhel
@ 2026-10-19 10:15 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-10-19 10:15 UTC (permalink / raw)
To: Svyatoslav Ryhel; +Cc: dri-devel, devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] drm/tegra: rgb: unlocked drm_bridge_get_current_state() in encoder enable
--
commit 62888cceae72e28c0425a5560b48d0bf56dccc9e
Author: Svyatoslav Ryhel <clamor95@gmail.com>
drm/tegra: rgb: Parameterize configuration based on bus flags
This commit parameterizes the Tegra RGB encoder configuration based on bus
flags from the bridge state, including pixel clock polarity, display enable
polarity, and data alignment.
> diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
> index b6d09a8d8e5ea..da9e37fe36689 100644
> --- a/drivers/gpu/drm/tegra/rgb.c
> +++ b/drivers/gpu/drm/tegra/rgb.c
[ ... ]
> @@ -104,14 +105,24 @@ static void tegra_rgb_encoder_enable(struct drm_encoder *encoder)
> struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
> struct tegra_output *output = encoder_to_output(encoder);
> struct tegra_rgb *rgb = to_rgb(output);
> - u32 value;
> + struct drm_bridge_state *bridge_state;
> + u32 bus_flags, value;
> +
> + /* Get but flags from the bridge state. */
> + bridge_state = drm_bridge_get_current_state(output->bridge);
> + if (bridge_state)
> + bus_flags = bridge_state->input_bus_cfg.flags;
> + else
> + bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE |
> + DRM_BUS_FLAG_DE_HIGH |
> + DRM_BUS_FLAG_DATA_MSB_TO_LSB;
[Severity: High]
Is it safe to call drm_bridge_get_current_state() from
tegra_rgb_encoder_enable()?
Looking at include/drm/drm_bridge.h, drm_bridge_get_current_state() asserts
that bridge->base.lock is held:
static inline struct drm_bridge_state *
drm_bridge_get_current_state(struct drm_bridge *bridge)
{
if (!bridge)
return NULL;
drm_modeset_lock_assert_held(&bridge->base.lock);
if (!bridge->base.state)
return NULL;
return drm_priv_to_bridge_state(bridge->base.state);
}
When userspace submits a non-blocking atomic modeset commit
(DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_ATOMIC_ALLOW_MODESET),
drm_atomic_helper_commit() swaps the state and schedules commit_work on
system_dfl_wq, after which drm_mode_atomic_ioctl() drops all modeset locks.
The worker thread then executes without holding bridge->base.lock:
commit_work() ->
commit_tail() ->
tegra_atomic_commit_tail() ->
drm_atomic_helper_commit_tail_rpm() ->
drm_atomic_helper_commit_modeset_enables() ->
drm_atomic_helper_commit_encoder_bridge_enable() ->
tegra_rgb_encoder_enable()
Won't calling drm_bridge_get_current_state(output->bridge) here trigger a
lockdep warning when CONFIG_PROVE_LOCKING is enabled and read
bridge->base.state without synchronization against concurrent atomic state
swaps?
Should this encoder implement .atomic_enable and retrieve the bridge state via
drm_atomic_get_new_bridge_state(state, output->bridge), or cache bus_flags
during .atomic_check instead?
[ ... ]
>
> tegra_dc_commit(rgb->dc);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261019095705.91062-1-clamor95@gmail.com?part=2
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 4/7] drm/tegra: Add support for 8-bit CPU interface
2026-10-19 9:57 ` [PATCH v3 4/7] drm/tegra: Add support for 8-bit CPU interface Svyatoslav Ryhel
@ 2026-10-19 10:20 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-10-19 10:20 UTC (permalink / raw)
To: Svyatoslav Ryhel; +Cc: dri-devel, devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [Critical] drm/tegra: dbi: use-after-free and ERR_PTR deref of priv->data_gpios
- [High] drm/tegra: dbi: duplicate panel enable/disable and broken disable order
- [High] drm/tegra: dbi: wrong V_PULSE position regs in tegra_dbi_atomic_enable()
- [High] drm/tegra: dc: unregistering dbi device on probe deferral blocks reprobe
- [Medium] drm/tegra: dbi: drm_panel reference leak in tegra_dbi_probe()
- [Medium] drm/tegra: dbi: missing MODULE_ALIAS() in tegra_dbi_driver
--
commit 3433ae394a211380cb312bda5b319e1df0574de3
Author: Svyatoslav Ryhel <clamor95@gmail.com>
drm/tegra: Add support for 8-bit CPU interface
This commit adds a DRM bridge driver for the Tegra20/30 8-bit CPU (MIPI DBI
Type B) display controller interface and registers its platform device from
the Tegra DC probe path.
> diff --git a/drivers/gpu/drm/tegra/dbi.c b/drivers/gpu/drm/tegra/dbi.c
> new file mode 100644
> index 0000000000000..4b3966dea0438
> --- /dev/null
> +++ b/drivers/gpu/drm/tegra/dbi.c
> @@ -0,0 +1,415 @@
[ ... ]
> +static void tegra_dbi_write(struct tegra_dbi_priv *priv, u8 type, u8 value)
> +{
> + DECLARE_BITMAP(value_bitmap, 8);
> +
> + value_bitmap[0] = value;
> +
> + gpiod_set_value(priv->dc_gpio, type);
> +
> + gpiod_set_value(priv->cs_gpio, 0);
> + gpiod_set_value(priv->rw_gpio, 0);
> +
> + gpiod_set_array_value(priv->data_gpios->ndescs, priv->data_gpios->desc,
> + priv->data_gpios->info, value_bitmap);
> +
> + gpiod_set_value(priv->cs_gpio, 1);
> + gpiod_set_value(priv->rw_gpio, 1);
> +
> + udelay(10);
> +}
> +
> +static int tegra_dbi_dbi_command(struct mipi_dbi *dbi, u8 *cmd, u8 *param, size_t num)
> +{
> + struct tegra_dbi_priv *priv = mipi_dbi_to_tegra_dbi(dbi);
> + u8 command = *cmd;
> +
> + if (!priv->data_gpios)
> + return 0;
> +
> + tegra_dbi_write(priv, TEGRA_DBI_COMMAND, command);
> +
> + for (int i = 0; i < num; i++)
> + tegra_dbi_write(priv, TEGRA_DBI_DATA, param[i]);
> +
> + return 0;
> +}
> +
> +static int tegra_dbi_prepare_gpios(struct tegra_dbi_priv *priv)
> +{
> + struct device *dev = priv->dev;
> + int ret;
> +
> + if (priv->prepared)
> + return 0;
[ ... ]
> + priv->data_gpios = gpiod_get_array(dev, "data", GPIOD_OUT_LOW);
> + if (IS_ERR(priv->data_gpios)) {
> + ret = PTR_ERR(priv->data_gpios);
> + dev_err(dev, "Failed to get data gpios %d\n", ret);
> + goto free_dc;
> + }
> +
> + if (priv->data_gpios && priv->data_gpios->ndescs > TEGRA_DBI_DATA_PINS_MAX) {
> + ret = -EINVAL;
> + dev_err(dev, "Too many data gpios\n");
> + goto free_data;
> + }
> +
> + priv->prepared = true;
> +
> + return 0;
> +
> +free_data:
> + gpiod_put_array(priv->data_gpios);
> +free_dc:
> + gpiod_put(priv->dc_gpio);
> +free_rw:
> + gpiod_put(priv->rw_gpio);
> +free_cs:
> + gpiod_put(priv->cs_gpio);
> +
> + return ret;
> +}
[Severity: Critical]
Can tegra_dbi_dbi_command() -> tegra_dbi_write() dereference a freed or
ERR_PTR priv->data_gpios pointer?
In tegra_dbi_atomic_enable() and tegra_dbi_atomic_disable(), the driver
releases priv->dc_gpio, priv->rw_gpio, and priv->cs_gpio via gpiod_put(),
frees priv->data_gpios via gpiod_put_array() (which calls kfree(descs)),
and sets priv->prepared = false without holding priv->dbi.cmdlock or
resetting the pointers to NULL.
Similarly, in tegra_dbi_prepare_gpios(), if gpiod_get_array() fails,
priv->data_gpios is left holding a non-NULL ERR_PTR(), and if
priv->data_gpios->ndescs > TEGRA_DBI_DATA_PINS_MAX, gpiod_put_array() frees
priv->data_gpios without clearing the pointer.
Because tegra_dbi_dbi_command() checks:
if (!priv->data_gpios)
return 0;
instead of checking priv->prepared, any MIPI DBI command sent while
priv->prepared is false (such as during modeset disable when
drm_atomic_bridge_chain_disable() runs panel_bridge_atomic_disable() ->
drm_panel_disable() before tegra_dbi_atomic_disable()) will pass a dangling
pointer or ERR_PTR to tegra_dbi_write(), dereferencing priv->data_gpios and
passing released GPIO descriptors to gpiod_set_value().
> +static void tegra_dbi_atomic_enable(struct drm_bridge *bridge,
> + struct drm_atomic_commit *state)
> +{
> + struct tegra_dbi_priv *priv = bridge_to_tegra_dbi(bridge);
> + struct tegra_dc *dc = priv->dc;
> + u32 value;
> + int ret;
[ ... ]
> + tegra_dc_writel(dc, V_PULSE1_ENABLE, DC_DISP_DISP_SIGNAL_OPTIONS0);
> + tegra_dc_writel(dc, PULSE_POLARITY_LOW, DC_DISP_V_PULSE1_CONTROL);
> +
> + tegra_dc_writel(dc, PULSE_END(1), DC_DISP_V_PULSE0_POSITION_A);
> + tegra_dc_writel(dc, 0, DC_DISP_V_PULSE0_POSITION_B);
> + tegra_dc_writel(dc, 0, DC_DISP_V_PULSE0_POSITION_C);
[Severity: High]
Should these writes in tegra_dbi_atomic_enable() target
DC_DISP_V_PULSE1_POSITION_A, DC_DISP_V_PULSE1_POSITION_B, and
DC_DISP_V_PULSE1_POSITION_C instead of the V_PULSE0 position registers?
The surrounding code enables V_PULSE1_ENABLE, configures
DC_DISP_V_PULSE1_CONTROL, selects DC_SIGNAL_VPULSE1 in
DC_DISP_INIT_SEQ_CONTROL, and routes LDC_OUTPUT_SELECT_V_PULSE1, but leaves
DC_DISP_V_PULSE1_POSITION_A/B/C unconfigured while overwriting V_PULSE0.
[ ... ]
> + ret = tegra_dbi_prepare_gpios(priv);
> + if (ret)
> + return;
> +
> + if (priv->output.panel)
> + drm_panel_enable(priv->output.panel);
> +
> + gpiod_set_value(priv->cs_gpio, 0);
> +
> + gpiod_put(priv->dc_gpio);
> + gpiod_put(priv->rw_gpio);
> + gpiod_put(priv->cs_gpio);
> +
> + gpiod_put_array(priv->data_gpios);
> + priv->prepared = false;
> +}
> +
> +static void tegra_dbi_atomic_disable(struct drm_bridge *bridge,
> + struct drm_atomic_commit *state)
> +{
> + struct tegra_dbi_priv *priv = bridge_to_tegra_dbi(bridge);
> + int ret;
> +
> + ret = tegra_dbi_prepare_gpios(priv);
> + if (ret)
> + return;
> +
> + if (priv->output.panel)
> + drm_panel_disable(priv->output.panel);
> +
> + gpiod_set_value(priv->cs_gpio, 0);
> +
> + gpiod_put(priv->dc_gpio);
> + gpiod_put(priv->rw_gpio);
> + gpiod_put(priv->cs_gpio);
> +
> + gpiod_put_array(priv->data_gpios);
> + priv->prepared = false;
> +}
[Severity: High]
Does wrapping the panel with devm_drm_panel_bridge_add_typed() in
tegra_dbi_probe() and attaching it in tegra_dbi_attach() cause duplicate
drm_panel_enable() and drm_panel_disable() calls and broken disable
ordering?
Because priv->output.bridge is attached to encoder->bridge_chain after
priv->bridge:
1. On enable, drm_atomic_bridge_chain_pre_enable() calls
panel_bridge_atomic_pre_enable() -> drm_panel_prepare() before
tegra_dbi_atomic_enable() acquires the DBI GPIOs. Then
drm_atomic_bridge_chain_enable() runs tegra_dbi_atomic_enable(), which calls
drm_panel_enable(priv->output.panel), and immediately afterward runs
panel_bridge_atomic_enable() -> drm_panel_enable(), triggering:
dev_warn(panel->dev, "Skipping enable of already enabled panel\n");
2. On disable, drm_atomic_bridge_chain_disable() walks the bridge chain in
reverse order. It runs panel_bridge_atomic_disable() -> drm_panel_disable()
before tegra_dbi_atomic_disable() has prepared the DBI GPIOs, and sets
panel->enabled = false. When tegra_dbi_atomic_disable() then runs, prepares
the GPIOs, and calls drm_panel_disable(priv->output.panel), drm_panel_disable()
sees !panel->enabled, logs:
dev_warn(panel->dev, "Skipping disable of already disabled panel\n");
and returns without invoking panel->funcs->disable().
[ ... ]
> +static int tegra_dbi_probe(struct platform_device *pdev)
> +{
[ ... ]
> + ret = devm_of_platform_populate(dev);
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to probe children\n");
> +
> + /* Initialize MIPI DBI interface */
> + mutex_init(&priv->dbi.cmdlock);
> + priv->dbi.command = tegra_dbi_dbi_command;
> +
> + ret = drm_of_find_panel_or_bridge(np, 1, 0, &panel, &bridge);
> + if (ret)
> + return ret;
> +
> + if (panel) {
> + bridge = devm_drm_panel_bridge_add_typed(dev, panel,
> + DRM_MODE_CONNECTOR_DPI);
> + if (IS_ERR(bridge))
> + return PTR_ERR(bridge);
> + }
[Severity: Medium]
Does tegra_dbi_probe() leak the drm_panel reference acquired by
drm_of_find_panel_or_bridge()?
drm_of_find_panel_or_bridge() -> of_drm_find_panel() increments the panel
refcount via drm_panel_get(), and devm_drm_panel_bridge_add_typed() takes an
additional devres-managed reference on &panel->bridge, so the reference
from drm_of_find_panel_or_bridge() is never dropped with drm_panel_put().
[ ... ]
> +static struct platform_driver tegra_dbi_driver = {
> + .driver.name = "tegra-dbi",
> + .probe = tegra_dbi_probe,
> +};
> +module_platform_driver(tegra_dbi_driver);
> +
> +MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
> +MODULE_DESCRIPTION("Nvidia Tegra20/30 MIPI-DBI Type B driver");
> +MODULE_LICENSE("GPL");
[Severity: Medium]
Should tegra_dbi_driver include MODULE_ALIAS("platform:tegra-dbi") (or a
platform_device_id table) so the module auto-loads when CONFIG_DRM_TEGRA_DBI=m?
In tegra_dc_probe(), the device is registered with .name = "tegra-dbi" and
.of_node_reused = true, which causes platform_uevent() to emit
MODALIAS=platform:tegra-dbi rather than an OF modalias.
> diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
> index 5c67928bcabfa..8f12d30cd2d8b 100644
> --- a/drivers/gpu/drm/tegra/dc.c
> +++ b/drivers/gpu/drm/tegra/dc.c
[ ... ]
> @@ -3382,6 +3389,28 @@ static int tegra_dc_probe(struct platform_device *pdev)
> }
> }
>
> + dbi_np = of_get_available_child_by_name(pdev->dev.of_node, "dbi");
> + if (dbi_np) {
> + struct platform_device_info dbi_info = {
> + .parent = &pdev->dev,
> + .name = "tegra-dbi",
> + .id = PLATFORM_DEVID_AUTO,
> + .fwnode = of_fwnode_handle(dbi_np),
> + .of_node_reused = true,
> + };
> +
> + dc->dbi = platform_device_register_full(&dbi_info);
> + of_node_put(dbi_np);
> + if (IS_ERR(dc->dbi))
> + return dev_err_probe(&pdev->dev, PTR_ERR(dc->dbi),
> + "Failed to create DBI device\n");
> +
> + err = devm_add_action_or_reset(&pdev->dev, tegra_dc_release_dbi_pdev,
> + dc->dbi);
> + if (err)
> + return err;
> + }
> +
> err = tegra_dc_rgb_probe(dc);
> if (err < 0 && err != -ENODEV)
> return dev_err_probe(&pdev->dev, err,
[Severity: High]
What happens when tegra_dc_rgb_probe() or tegra_dbi_probe() returns
-EPROBE_DEFER?
In tegra_dc_probe(), dc->dbi is registered right before tegra_dc_rgb_probe()
(which calls tegra_output_probe() -> drm_of_find_panel_or_bridge() to look up
the bridge on the dbi node). Similarly, tegra_dbi_probe() calls
devm_of_platform_populate(dev) and immediately requires the child panel in
drm_of_find_panel_or_bridge() before calling devm_drm_bridge_add().
If tegra-dbi or the child panel driver has not bound yet (for example, when
CONFIG_DRM_TEGRA_DBI=m, the panel driver is a module, or a panel dependency
defers), tegra_dbi_probe() unpopulates the child panel via devres and
tegra_dc_probe() returns -EPROBE_DEFER.
When tegra_dc_probe() fails with -EPROBE_DEFER, devres_release_all(&pdev->dev)
runs tegra_dc_release_dbi_pdev() -> platform_device_unregister(dc->dbi).
With both the tegra-dbi device and its child panel device removed from the
platform bus, what will match and bind to trigger a deferred reprobe of
tegra_dc?
>
完美! Let's double-check all constraints:
- Plain text only, no markdown, no backticks, no ALL CAPS in commentary.
- Wrapped at 78 characters.
- Ends with a blank line.
- No line numbers mentioned in commentary.
- Severity tags `[Severity: ...]` on their own line right above each inline comment.
- All 6 findings (VF1, VF2, VF3, VF4, VF5, PVF1) included. Everything is clean and accurate. Ready to output!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261019095705.91062-1-clamor95@gmail.com?part=4
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-10-19 10:20 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-19 9:56 [PATCH v3 0/7] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface Svyatoslav Ryhel
2026-10-19 9:56 ` [PATCH v3 1/7] drm/tegra: dc: Expand available registers layouts Svyatoslav Ryhel
2026-10-19 9:57 ` [PATCH v3 2/7] drm/tegra: rgb: Parameterize configuration based on bus flags Svyatoslav Ryhel
2026-10-19 10:15 ` sashiko-bot
2026-10-19 9:57 ` [PATCH v3 3/7] dt-bindings: display: tegra: Document 8-bit CPU parallel interface Svyatoslav Ryhel
2026-10-19 10:09 ` sashiko-bot
2026-10-19 9:57 ` [PATCH v3 4/7] drm/tegra: Add support for 8-bit CPU interface Svyatoslav Ryhel
2026-10-19 10:20 ` sashiko-bot
2026-10-19 9:57 ` [PATCH v3 5/7] dt-bindings: display: panel: Document Hitachi TX10D07VM0BAA and LG LH400WV3 panels Svyatoslav Ryhel
2026-10-19 9:57 ` [PATCH v3 6/7] drm/panel: Add Hitachi TX10D07VM0BAA MIPI DBI Type B panel driver Svyatoslav Ryhel
2026-10-19 10:09 ` sashiko-bot
2026-10-19 9:57 ` [PATCH v3 7/7] drm/panel: Add LG LH400WV3-SD04 " Svyatoslav Ryhel
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.