[PATCH v6 0/6] drm/verisilicon: add Nuvoton MA35D1 DCU Lite support

COLD23d

Revision v6 of 6 in this series.

17 messages, 3 authors, 23d ago · open the first message on its own page

[PATCH v6 0/6] drm/verisilicon: add Nuvoton MA35D1 DCU Lite support

From: Joey Lu <hidden>
Date: 2026-09-08 09:28:58

This series adds support for the Verisilicon DCUltraLite display
controller as integrated in the Nuvoton MA35D1 SoC.

The Verisilicon DC driver and its DT binding were originally written by
Icenowy Zheng [off-list ref] for the T-Head TH1520 SoC, which
carries a DC8200 IP block.  The present series builds on that foundation
with gratitude to Icenowy for the original work.

The DCUltraLite is a different variant in the DC IP family.  While the two
IPs share a broadly similar register layout, a number of differences
prevent the existing driver from working on the MA35D1 without
modification:

  - No CONFIG_EX commit path: the DC8200 staging registers
    (FB_CONFIG_EX, FB_TOP_LEFT, FB_BOTTOM_RIGHT, FB_BLEND_CONFIG,
    PANEL_CONFIG_EX) are absent.  The DCUltraLite uses enable (bit 0) and
    reset (bit 4) bits in FB_CONFIG for direct framebuffer updates, and
    requires a per-frame VALID bit toggle (FB_CONFIG bit 3) to latch
    configuration changes.

  - No PANEL_START register: panel output begins when
    PANEL_CONFIG.RUNNING is set; the DC8200 multi-display sync start
    register at 0x1CCC does not exist.

  - Different IRQ registers: DISP_IRQ_STA at 0x147C / DISP_IRQ_EN at
    0x1480, versus the DC8200's TOP_IRQ_ACK at 0x0010 / TOP_IRQ_EN at
    0x0014.

  - Simpler clock topology: the MA35D1 clock controller gates the core,
    AXI and AHB clocks with a single bit, so the devicetree supplies the
    same clock phandle for all three; only the pixel clock is distinct.
    No second output port is present, so no pix1 clock is needed either.

  - Single display output: no per-output indexing beyond index 0 is
    needed.

  - Hardware-discoverable identity: the DCUltraLite exposes chip identity
    registers whose model field reads 0x0 (revision 0x5560,
    customer_id 0x305), allowing the existing vs_fill_chip_identity()
    path to identify the variant purely through register reads.

Patch 1 adds the nuvoton,ma35d1-dcu compatible to the verisilicon,dc DT
binding and relaxes the top-level clock/reset item counts so per-variant
allOf/if blocks can constrain each compatible's actual topology.

Patch 2 adds the register-level macros needed by the DC8000 ops.

Patches 3-4 introduce the driver changes in two logical steps: the
vs_dc_funcs hardware ops vtable with DC8200 ops extracted into
vs_dc8200.c, and the DC8000 ops in vs_dc8000.c.  Patch 5 adds the
DCUltraLite HWDB entry that gates hardware recognition once all support
is in place.

Patch 6 adds the Kconfig dependency on ARCH_MA35, placed last because it
is only meaningful after the HWDB entry is added.

All patches have been tested on Nuvoton MA35D1 hardware.

Changes from v5:
  - [dt-bindings] Renamed the patch from "generalize for single-output
    variants" to "add support for nuvoton,ma35d1-dcu", since the binding
    topology itself isn't being generalised, only a new compatible is
    being added.
  - [dt-bindings] Moved clocks/clock-names minItems to 4 and
    resets/reset-names minItems to 1 at the top level (the lowest count
    any variant needs), instead of overriding both minItems and maxItems
    redundantly inside each allOf/if block.
  - [dt-bindings] Kept the thead,th1520-dc8200 allOf/if block, tightening
    it back up to minItems: 5 (clocks) / minItems: 3 (resets), since the
    outer constraint is now looser than what that compatible requires.
  - [dt-bindings] Dropped the nuvoton,ma35d1-dcu clock-names/reset-names
    item overrides entirely: the devicetree will supply all four clocks
    (core, axi, ahb, pix0) and the one core reset in the same order the
    top-level schema already expects, so only maxItems: 4 / maxItems: 1
    are needed to cap the count.
  - [dt-bindings] Dropped the redundant "required: resets/reset-names"
    sub-blocks, now that resets/reset-names are unconditionally required
    at the top level (landed separately by Icenowy Zheng).
  - [driver] Dropped "make axi and ahb clocks optional" entirely: since
    the devicetree will always supply distinct axi/ahb clock properties
    (sharing the core clock's phandle), vs_dc_probe() keeps treating them
    as mandatory via devm_clk_get_enabled(), same as core and pix0.
  - [driver] Added drm_WARN_ONCE() in both vs_dc8200_irq_ack() and
    vs_dc8000_irq_ack() to flag any hardware IRQ bit that doesn't
    translate to a known VSDC_IRQ_* definition.

Joey Lu (6):
  dt-bindings: display: verisilicon,dc: add support for
    nuvoton,ma35d1-dcu
  drm/verisilicon: add register-level macros for DC8000
  drm/verisilicon: introduce per-variant hardware ops table
  drm/verisilicon: add DC8000 (DCUltraLite) display controller support
  drm/verisilicon: add DCUltraLite chip identity to HWDB
  drm/verisilicon: extend Kconfig to support ARCH_MA35 platforms

 .../bindings/display/verisilicon,dc.yaml      |  44 +++++++
 drivers/gpu/drm/verisilicon/Kconfig           |   2 +-
 drivers/gpu/drm/verisilicon/Makefile          |   2 +-
 drivers/gpu/drm/verisilicon/vs_bridge.c       |  20 +--
 drivers/gpu/drm/verisilicon/vs_crtc.c         |  38 +++++-
 drivers/gpu/drm/verisilicon/vs_crtc_regs.h    |   1 +
 drivers/gpu/drm/verisilicon/vs_dc.c           |   9 +-
 drivers/gpu/drm/verisilicon/vs_dc.h           |  33 +++++
 drivers/gpu/drm/verisilicon/vs_dc8000.c       |  92 +++++++++++++
 drivers/gpu/drm/verisilicon/vs_dc8200.c       | 121 ++++++++++++++++++
 drivers/gpu/drm/verisilicon/vs_drm.c          |   5 +-
 drivers/gpu/drm/verisilicon/vs_drm.h          |   8 ++
 drivers/gpu/drm/verisilicon/vs_hwdb.c         |  14 ++
 drivers/gpu/drm/verisilicon/vs_hwdb.h         |   6 +
 .../gpu/drm/verisilicon/vs_primary_plane.c    |  32 +----
 .../drm/verisilicon/vs_primary_plane_regs.h   |   3 +
 16 files changed, 375 insertions(+), 55 deletions(-)
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc8000.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc8200.c

-- 
2.43.0

[PATCH v6 1/6] dt-bindings: display: verisilicon,dc: add support for nuvoton,ma35d1-dcu

From: Joey Lu <hidden>
Date: 2026-09-08 09:29:00

Add the Nuvoton MA35D1 DCUltraLite (nuvoton,ma35d1-dcu) to the binding.
The DCUltraLite uses only four clocks (core, axi, ahb, pix0) and one
reset (core), with a single output port.

The MA35D1 clock controller gates the core, AXI and AHB clocks with a
single bit, but each remains a distinct clock line feeding the IP with
its own rate constraints, so all four must still be listed individually
in the devicetree; core, axi and ahb happen to share the same clock
phandle.

Move the clocks/clock-names minItems to 4 and resets/reset-names
minItems to 1 at the top level, since that is the lowest count any
supported variant needs.  Add an allOf/if block that tightens the
constraint back up to the fixed 5-clock/3-reset topology required by
the existing thead,th1520-dc8200 compatible, and another one that caps
the new nuvoton,ma35d1-dcu compatible at the 4-clock/1-reset count it
actually wires up.

Signed-off-by: Joey Lu <redacted>
---
 .../bindings/display/verisilicon,dc.yaml      | 44 +++++++++++++++++++
 1 file changed, 44 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/verisilicon,dc.yaml b/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
index 919a900122012..773966677d0f4 100644
--- a/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
+++ b/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
@@ -17,6 +17,7 @@ properties:
     items:
       - enum:
           - thead,th1520-dc8200
+          - nuvoton,ma35d1-dcu
       - const: verisilicon,dc # DC IPs have discoverable ID/revision registers
 
   reg:
@@ -26,6 +27,7 @@ properties:
     maxItems: 1
 
   clocks:
+    minItems: 4
     items:
       - description: DC Core clock
       - description: DMA AXI bus clock
@@ -34,6 +36,7 @@ properties:
       - description: Pixel clock of output 1
 
   clock-names:
+    minItems: 4
     items:
       - const: core
       - const: axi
@@ -42,12 +45,14 @@ properties:
       - const: pix1
 
   resets:
+    minItems: 1
     items:
       - description: DC Core reset
       - description: DMA AXI bus reset
       - description: Configuration AHB bus reset
 
   reset-names:
+    minItems: 1
     items:
       - const: core
       - const: axi
@@ -79,6 +84,45 @@ required:
   - reset-names
   - ports
 
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: thead,th1520-dc8200
+    then:
+      properties:
+        clocks:
+          minItems: 5
+
+        clock-names:
+          minItems: 5
+
+        resets:
+          minItems: 3
+
+        reset-names:
+          minItems: 3
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: nuvoton,ma35d1-dcu
+    then:
+      properties:
+        clocks:
+          maxItems: 4
+
+        clock-names:
+          maxItems: 4
+
+        resets:
+          maxItems: 1
+
+        reset-names:
+          maxItems: 1
+
 additionalProperties: false
 
 examples:
-- 
2.43.0

[PATCH v6 2/6] drm/verisilicon: add register-level macros for DC8000

From: Joey Lu <hidden>
Date: 2026-09-08 09:29:05

Add register-level constants needed by the forthcoming DC8000 (DCUltraLite)
hardware ops:

  VSDC_DISP_IRQ_VSYNC(n) in vs_crtc_regs.h: bit mask for per-output
  VSYNC interrupt bits in DISP_IRQ_STA (0x147C) / DISP_IRQ_EN (0x1480),
  which are the IRQ registers used by DCUltraLite in place of the DC8200
  TOP_IRQ_ACK / TOP_IRQ_EN registers.

  VSDC_FB_CONFIG_ENABLE (bit 0), VSDC_FB_CONFIG_VALID (bit 3) and
  VSDC_FB_CONFIG_RESET (bit 4) in vs_primary_plane_regs.h: control bits
  in the FB_CONFIG register used by DCUltraLite for framebuffer enable
  and per-frame commit handshake.

No behaviour change for existing DC8200 platforms.

Signed-off-by: Joey Lu <redacted>
Reviewed-by: Icenowy Zheng <zhengxingda@iscas.ac.cn>
---
 drivers/gpu/drm/verisilicon/vs_crtc_regs.h          | 1 +
 drivers/gpu/drm/verisilicon/vs_primary_plane_regs.h | 3 +++
 2 files changed, 4 insertions(+)
diff --git a/drivers/gpu/drm/verisilicon/vs_crtc_regs.h b/drivers/gpu/drm/verisilicon/vs_crtc_regs.h
index c7930e817635c..d4da22b08cd5c 100644
--- a/drivers/gpu/drm/verisilicon/vs_crtc_regs.h
+++ b/drivers/gpu/drm/verisilicon/vs_crtc_regs.h
@@ -54,6 +54,7 @@
 #define VSDC_DISP_GAMMA_DATA(n)			(0x1460 + 0x4 * (n))
 
 #define VSDC_DISP_IRQ_STA			0x147C
+#define VSDC_DISP_IRQ_VSYNC(n)			BIT(n)
 
 #define VSDC_DISP_IRQ_EN			0x1480
 
diff --git a/drivers/gpu/drm/verisilicon/vs_primary_plane_regs.h b/drivers/gpu/drm/verisilicon/vs_primary_plane_regs.h
index cbb125c46b390..67d4b00f294e2 100644
--- a/drivers/gpu/drm/verisilicon/vs_primary_plane_regs.h
+++ b/drivers/gpu/drm/verisilicon/vs_primary_plane_regs.h
@@ -16,6 +16,9 @@
 #define VSDC_FB_STRIDE(n)			(0x1408 + 0x4 * (n))
 
 #define VSDC_FB_CONFIG(n)			(0x1518 + 0x4 * (n))
+#define VSDC_FB_CONFIG_ENABLE			BIT(0)
+#define VSDC_FB_CONFIG_VALID			BIT(3)
+#define VSDC_FB_CONFIG_RESET			BIT(4)
 #define VSDC_FB_CONFIG_CLEAR_EN			BIT(8)
 #define VSDC_FB_CONFIG_ROT_MASK			GENMASK(13, 11)
 #define VSDC_FB_CONFIG_ROT(v)			((v) << 11)
-- 
2.43.0

[PATCH v6 3/6] drm/verisilicon: introduce per-variant hardware ops table

From: Joey Lu <hidden>
Date: 2026-09-08 09:29:09

The DC8200 and DCUltraLite share a broadly similar register layout but
differ in how the bridge, CRTC, primary plane and IRQ paths are driven.
Introduce a vs_dc_funcs vtable so each variant can supply its own
implementation without scattering conditionals across multiple files.

Add a generation field to struct vs_chip_identity to distinguish variants.
Extract the DC8200-specific hardware ops into vs_dc8200.c and add unified
IRQ bit definitions so implementations can translate hardware-specific
bits to a common set.  Update the shared code to dispatch through
dc->funcs.

No behaviour change for existing DC8200 platforms.

Signed-off-by: Joey Lu <redacted>
Reviewed-by: Icenowy Zheng <zhengxingda@iscas.ac.cn>
---
 drivers/gpu/drm/verisilicon/Makefile          |   2 +-
 drivers/gpu/drm/verisilicon/vs_bridge.c       |  20 +--
 drivers/gpu/drm/verisilicon/vs_crtc.c         |  38 +++++-
 drivers/gpu/drm/verisilicon/vs_dc.c           |   6 +-
 drivers/gpu/drm/verisilicon/vs_dc.h           |  32 +++++
 drivers/gpu/drm/verisilicon/vs_dc8200.c       | 121 ++++++++++++++++++
 drivers/gpu/drm/verisilicon/vs_drm.c          |   5 +-
 drivers/gpu/drm/verisilicon/vs_drm.h          |   8 ++
 drivers/gpu/drm/verisilicon/vs_hwdb.c         |   4 +
 drivers/gpu/drm/verisilicon/vs_hwdb.h         |   6 +
 .../gpu/drm/verisilicon/vs_primary_plane.c    |  32 +----
 11 files changed, 220 insertions(+), 54 deletions(-)
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc8200.c
diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
index 426f4bcaa834d..9d4cd16452fa1 100644
--- a/drivers/gpu/drm/verisilicon/Makefile
+++ b/drivers/gpu/drm/verisilicon/Makefile
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
-verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_drm.o vs_hwdb.o \
+verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_dc8200.o vs_drm.o vs_hwdb.o \
 	vs_plane.o vs_primary_plane.o vs_cursor_plane.o
 
 obj-$(CONFIG_DRM_VERISILICON_DC) += verisilicon-dc.o
diff --git a/drivers/gpu/drm/verisilicon/vs_bridge.c b/drivers/gpu/drm/verisilicon/vs_bridge.c
index dc7c85b07fe32..3fbc8d57f8a1e 100644
--- a/drivers/gpu/drm/verisilicon/vs_bridge.c
+++ b/drivers/gpu/drm/verisilicon/vs_bridge.c
@@ -162,15 +162,8 @@ static void vs_bridge_enable_common(struct vs_crtc *crtc,
 			VSDC_DISP_PANEL_CONFIG_DE_EN |
 			VSDC_DISP_PANEL_CONFIG_DAT_EN |
 			VSDC_DISP_PANEL_CONFIG_CLK_EN);
-	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
-			VSDC_DISP_PANEL_CONFIG_RUNNING);
-	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
-			  VSDC_DISP_PANEL_START_MULTI_DISP_SYNC);
-	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_START,
-			VSDC_DISP_PANEL_START_RUNNING(output));
-
-	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(crtc->id),
-			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
+
+	dc->funcs->panel_enable_ex(dc, output);
 }
 
 static void vs_bridge_atomic_enable_dpi(struct drm_bridge *bridge,
@@ -228,14 +221,7 @@ static void vs_bridge_atomic_disable(struct drm_bridge *bridge,
 	struct vs_dc *dc = crtc->dc;
 	unsigned int output = crtc->id;
 
-	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
-			  VSDC_DISP_PANEL_START_MULTI_DISP_SYNC |
-			  VSDC_DISP_PANEL_START_RUNNING(output));
-	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
-			  VSDC_DISP_PANEL_CONFIG_RUNNING);
-
-	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(crtc->id),
-			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
+	dc->funcs->panel_disable_ex(dc, output);
 }
 
 static const struct drm_bridge_funcs vs_dpi_bridge_funcs = {
diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
index c24c1588cbe69..2d4bf6af1f56e 100644
--- a/drivers/gpu/drm/verisilicon/vs_crtc.c
+++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
@@ -16,10 +16,33 @@
 #include "vs_crtc_regs.h"
 #include "vs_crtc.h"
 #include "vs_dc.h"
-#include "vs_dc_top_regs.h"
 #include "vs_drm.h"
 #include "vs_plane.h"
 
+static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
+				  struct drm_atomic_commit *state)
+{
+	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
+	struct vs_dc *dc = vcrtc->dc;
+	unsigned int output = vcrtc->id;
+
+	if (dc->funcs->crtc_begin)
+		dc->funcs->crtc_begin(dc, output);
+}
+
+static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
+				  struct drm_atomic_commit *state)
+{
+	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
+	struct vs_dc *dc = vcrtc->dc;
+	unsigned int output = vcrtc->id;
+
+	if (dc->funcs->crtc_flush)
+		dc->funcs->crtc_flush(dc, output);
+
+	drm_crtc_vblank_atomic_flush(crtc, state);
+}
+
 static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
 				   struct drm_atomic_commit *state)
 {
@@ -30,6 +53,9 @@ static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
 	drm_crtc_vblank_off(crtc);
 
 	clk_disable_unprepare(dc->pix_clk[output]);
+
+	if (dc->funcs->crtc_disable_ex)
+		dc->funcs->crtc_disable_ex(dc, output);
 }
 
 static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
@@ -42,6 +68,9 @@ static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
 	drm_WARN_ON(&dc->drm_dev->base,
 		    clk_prepare_enable(dc->pix_clk[output]));
 
+	if (dc->funcs->crtc_enable_ex)
+		dc->funcs->crtc_enable_ex(dc, output);
+
 	drm_crtc_vblank_on(crtc);
 }
 
@@ -119,7 +148,8 @@ static bool vs_crtc_mode_fixup(struct drm_crtc *crtc,
 }
 
 static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
-	.atomic_flush	= drm_crtc_vblank_atomic_flush,
+	.atomic_begin	= vs_crtc_atomic_begin,
+	.atomic_flush	= vs_crtc_atomic_flush,
 	.atomic_enable	= vs_crtc_atomic_enable,
 	.atomic_disable	= vs_crtc_atomic_disable,
 	.mode_set_nofb	= vs_crtc_mode_set_nofb,
@@ -132,7 +162,7 @@ static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
 	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
 	struct vs_dc *dc = vcrtc->dc;
 
-	regmap_set_bits(dc->regs, VSDC_TOP_IRQ_EN, VSDC_TOP_IRQ_VSYNC(vcrtc->id));
+	dc->funcs->enable_vblank(dc, vcrtc->id);
 
 	return 0;
 }
@@ -142,7 +172,7 @@ static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
 	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
 	struct vs_dc *dc = vcrtc->dc;
 
-	regmap_clear_bits(dc->regs, VSDC_TOP_IRQ_EN, VSDC_TOP_IRQ_VSYNC(vcrtc->id));
+	dc->funcs->disable_vblank(dc, vcrtc->id);
 }
 
 static const struct drm_crtc_funcs vs_crtc_funcs = {
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
index dad9967bc10b8..9729b693d360e 100644
--- a/drivers/gpu/drm/verisilicon/vs_dc.c
+++ b/drivers/gpu/drm/verisilicon/vs_dc.c
@@ -8,9 +8,7 @@
 #include <linux/of.h>
 #include <linux/of_graph.h>
 
-#include "vs_crtc.h"
 #include "vs_dc.h"
-#include "vs_dc_top_regs.h"
 #include "vs_drm.h"
 #include "vs_hwdb.h"
 
@@ -33,7 +31,7 @@ static irqreturn_t vs_dc_irq_handler(int irq, void *private)
 	struct vs_dc *dc = private;
 	u32 irqs;
 
-	regmap_read(dc->regs, VSDC_TOP_IRQ_ACK, &irqs);
+	irqs = dc->funcs->irq_ack(dc);
 
 	vs_drm_handle_irq(dc, irqs);
 
@@ -136,6 +134,8 @@ static int vs_dc_probe(struct platform_device *pdev)
 	dev_info(dev, "Found DC%x rev %x customer %x\n", dc->identity.model,
 		 dc->identity.revision, dc->identity.customer_id);
 
+	dc->funcs = &vs_dc8200_funcs;
+
 	if (port_count > dc->identity.display_count) {
 		dev_err(dev, "too many downstream ports than HW capability\n");
 		ret = -EINVAL;
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
index ed1016f18758e..825f5dd6bf174 100644
--- a/drivers/gpu/drm/verisilicon/vs_dc.h
+++ b/drivers/gpu/drm/verisilicon/vs_dc.h
@@ -14,6 +14,7 @@
 #include <linux/reset.h>
 
 #include <drm/drm_device.h>
+#include <drm/drm_plane.h>
 
 #include "vs_hwdb.h"
 
@@ -22,6 +23,34 @@
 
 struct vs_drm_dev;
 struct vs_crtc;
+struct vs_dc;
+
+struct vs_dc_funcs {
+	/* Bridge: atomic_enable, atomic_disable */
+	void (*panel_enable_ex)(struct vs_dc *dc, unsigned int output);
+	void (*panel_disable_ex)(struct vs_dc *dc, unsigned int output);
+
+	/* CRTC: atomic_begin, atomic_flush */
+	void (*crtc_begin)(struct vs_dc *dc, unsigned int output);
+	void (*crtc_flush)(struct vs_dc *dc, unsigned int output);
+
+	/* CRTC: atomic_enable, atomic_disable */
+	void (*crtc_enable_ex)(struct vs_dc *dc, unsigned int output);
+	void (*crtc_disable_ex)(struct vs_dc *dc, unsigned int output);
+
+	/* CRTC: enable_vblank, disable_vblank */
+	void (*enable_vblank)(struct vs_dc *dc, unsigned int output);
+	void (*disable_vblank)(struct vs_dc *dc, unsigned int output);
+
+	/* Primary plane: atomic_enable, atomic_disable, atomic_update */
+	void (*primary_plane_enable_ex)(struct vs_dc *dc, unsigned int output);
+	void (*primary_plane_disable_ex)(struct vs_dc *dc, unsigned int output);
+	void (*primary_plane_update_ex)(struct vs_dc *dc, unsigned int output,
+					struct drm_plane_state *state);
+
+	/* IRQ acknowledge */
+	u32 (*irq_ack)(struct vs_dc *dc);
+};
 
 struct vs_dc {
 	struct regmap *regs;
@@ -33,6 +62,9 @@ struct vs_dc {
 
 	struct vs_drm_dev *drm_dev;
 	struct vs_chip_identity identity;
+	const struct vs_dc_funcs *funcs;
 };
 
+extern const struct vs_dc_funcs vs_dc8200_funcs;
+
 #endif /* _VS_DC_H_ */
diff --git a/drivers/gpu/drm/verisilicon/vs_dc8200.c b/drivers/gpu/drm/verisilicon/vs_dc8200.c
new file mode 100644
index 0000000000000..77752d0cb28b0
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc8200.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Icenowy Zheng <uwu@icenowy.me>
+ */
+
+#include <linux/regmap.h>
+
+#include <drm/drm_print.h>
+
+#include "vs_bridge_regs.h"
+#include "vs_dc.h"
+#include "vs_dc_top_regs.h"
+#include "vs_drm.h"
+#include "vs_plane.h"
+#include "vs_primary_plane_regs.h"
+
+static void vs_dc8200_panel_enable_ex(struct vs_dc *dc, unsigned int output)
+{
+	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
+			VSDC_DISP_PANEL_CONFIG_RUNNING);
+	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
+			  VSDC_DISP_PANEL_START_MULTI_DISP_SYNC);
+	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_START,
+			VSDC_DISP_PANEL_START_RUNNING(output));
+
+	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(output),
+			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
+}
+
+static void vs_dc8200_panel_disable_ex(struct vs_dc *dc, unsigned int output)
+{
+	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
+			  VSDC_DISP_PANEL_CONFIG_RUNNING);
+	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
+			  VSDC_DISP_PANEL_START_MULTI_DISP_SYNC |
+			  VSDC_DISP_PANEL_START_RUNNING(output));
+
+	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(output),
+			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
+}
+
+static void vs_dc8200_enable_vblank(struct vs_dc *dc, unsigned int output)
+{
+	regmap_set_bits(dc->regs, VSDC_TOP_IRQ_EN,
+			VSDC_TOP_IRQ_VSYNC(output));
+}
+
+static void vs_dc8200_disable_vblank(struct vs_dc *dc, unsigned int output)
+{
+	regmap_clear_bits(dc->regs, VSDC_TOP_IRQ_EN,
+			  VSDC_TOP_IRQ_VSYNC(output));
+}
+
+static void vs_dc8200_plane_commit(struct vs_dc *dc, unsigned int output)
+{
+	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
+			VSDC_FB_CONFIG_EX_COMMIT);
+}
+
+static void vs_dc8200_primary_plane_enable_ex(struct vs_dc *dc, unsigned int output)
+{
+	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
+			VSDC_FB_CONFIG_EX_FB_EN);
+	regmap_update_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
+			   VSDC_FB_CONFIG_EX_DISPLAY_ID_MASK,
+			   VSDC_FB_CONFIG_EX_DISPLAY_ID(output));
+
+	vs_dc8200_plane_commit(dc, output);
+}
+
+static void vs_dc8200_primary_plane_disable_ex(struct vs_dc *dc, unsigned int output)
+{
+	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
+			VSDC_FB_CONFIG_EX_FB_EN);
+
+	vs_dc8200_plane_commit(dc, output);
+}
+
+static void vs_dc8200_primary_plane_update_ex(struct vs_dc *dc, unsigned int output,
+				       struct drm_plane_state *state)
+{
+	regmap_write(dc->regs, VSDC_FB_TOP_LEFT(output),
+		     VSDC_MAKE_PLANE_POS(state->crtc_x, state->crtc_y));
+	regmap_write(dc->regs, VSDC_FB_BOTTOM_RIGHT(output),
+		     VSDC_MAKE_PLANE_POS(state->crtc_x + state->crtc_w,
+					 state->crtc_y + state->crtc_h));
+	regmap_write(dc->regs, VSDC_FB_BLEND_CONFIG(output),
+		     VSDC_FB_BLEND_CONFIG_BLEND_DISABLE);
+
+	vs_dc8200_plane_commit(dc, output);
+}
+
+static u32 vs_dc8200_irq_ack(struct vs_dc *dc)
+{
+	u32 hw_irqs, unified = 0, known = 0;
+	unsigned int i;
+
+	regmap_read(dc->regs, VSDC_TOP_IRQ_ACK, &hw_irqs);
+
+	for (i = 0; i < VSDC_MAX_OUTPUTS; i++) {
+		known |= VSDC_TOP_IRQ_VSYNC(i);
+		if (hw_irqs & VSDC_TOP_IRQ_VSYNC(i))
+			unified |= VSDC_IRQ_VSYNC(i);
+	}
+
+	drm_WARN_ONCE(&dc->drm_dev->base, hw_irqs & ~known,
+		      "Unknown hardware IRQ bits: %#x\n", hw_irqs & ~known);
+
+	return unified;
+}
+
+const struct vs_dc_funcs vs_dc8200_funcs = {
+	.panel_enable_ex		= vs_dc8200_panel_enable_ex,
+	.panel_disable_ex		= vs_dc8200_panel_disable_ex,
+	.enable_vblank			= vs_dc8200_enable_vblank,
+	.disable_vblank			= vs_dc8200_disable_vblank,
+	.primary_plane_enable_ex	= vs_dc8200_primary_plane_enable_ex,
+	.primary_plane_disable_ex	= vs_dc8200_primary_plane_disable_ex,
+	.primary_plane_update_ex	= vs_dc8200_primary_plane_update_ex,
+	.irq_ack			= vs_dc8200_irq_ack,
+};
diff --git a/drivers/gpu/drm/verisilicon/vs_drm.c b/drivers/gpu/drm/verisilicon/vs_drm.c
index fd259d53f49f1..24e9d0b008f31 100644
--- a/drivers/gpu/drm/verisilicon/vs_drm.c
+++ b/drivers/gpu/drm/verisilicon/vs_drm.c
@@ -25,7 +25,6 @@
 #include "vs_bridge.h"
 #include "vs_crtc.h"
 #include "vs_dc.h"
-#include "vs_dc_top_regs.h"
 #include "vs_drm.h"
 
 #define DRIVER_NAME	"verisilicon"
@@ -168,8 +167,8 @@ void vs_drm_handle_irq(struct vs_dc *dc, u32 irqs)
 	unsigned int i;
 
 	for (i = 0; i < dc->identity.display_count; i++) {
-		if (irqs & VSDC_TOP_IRQ_VSYNC(i)) {
-			irqs &= ~VSDC_TOP_IRQ_VSYNC(i);
+		if (irqs & VSDC_IRQ_VSYNC(i)) {
+			irqs &= ~VSDC_IRQ_VSYNC(i);
 			if (dc->drm_dev->crtcs[i])
 				drm_crtc_handle_vblank(&dc->drm_dev->crtcs[i]->base);
 		}
diff --git a/drivers/gpu/drm/verisilicon/vs_drm.h b/drivers/gpu/drm/verisilicon/vs_drm.h
index 606338206a427..6a89c20879df7 100644
--- a/drivers/gpu/drm/verisilicon/vs_drm.h
+++ b/drivers/gpu/drm/verisilicon/vs_drm.h
@@ -6,6 +6,7 @@
 #ifndef _VS_DRM_H_
 #define _VS_DRM_H_
 
+#include <linux/bits.h>
 #include <linux/platform_device.h>
 #include <linux/types.h>
 
@@ -13,6 +14,13 @@
 
 struct vs_dc;
 
+/*
+ * DC variants use different interrupt registers with diverging bit
+ * assignments; each irq_ack() implementation must translate its
+ * hardware-specific bits into these definitions.
+ */
+#define VSDC_IRQ_VSYNC(n)	BIT(n)
+
 struct vs_drm_dev {
 	struct drm_device base;
 
diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.c b/drivers/gpu/drm/verisilicon/vs_hwdb.c
index 2a0f7c59afa3a..91524d16f7785 100644
--- a/drivers/gpu/drm/verisilicon/vs_hwdb.c
+++ b/drivers/gpu/drm/verisilicon/vs_hwdb.c
@@ -94,6 +94,7 @@ static struct vs_chip_identity vs_chip_identities[] = {
 		.revision = 0x5720,
 		.customer_id = ~0U,
 
+		.generation = VSDC_GEN_DC8200,
 		.display_count = 2,
 		.max_cursor_size = 64,
 		.formats = &vs_formats_no_yuv444,
@@ -103,6 +104,7 @@ static struct vs_chip_identity vs_chip_identities[] = {
 		.revision = 0x5721,
 		.customer_id = 0x30B,
 
+		.generation = VSDC_GEN_DC8200,
 		.display_count = 2,
 		.max_cursor_size = 64,
 		.formats = &vs_formats_no_yuv444,
@@ -112,6 +114,7 @@ static struct vs_chip_identity vs_chip_identities[] = {
 		.revision = 0x5720,
 		.customer_id = 0x310,
 
+		.generation = VSDC_GEN_DC8200,
 		.display_count = 2,
 		.max_cursor_size = 64,
 		.formats = &vs_formats_with_yuv444,
@@ -121,6 +124,7 @@ static struct vs_chip_identity vs_chip_identities[] = {
 		.revision = 0x5720,
 		.customer_id = 0x311,
 
+		.generation = VSDC_GEN_DC8200,
 		.display_count = 2,
 		.max_cursor_size = 64,
 		.formats = &vs_formats_no_yuv444,
diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.h b/drivers/gpu/drm/verisilicon/vs_hwdb.h
index 2065ecb730437..a15c8b5656044 100644
--- a/drivers/gpu/drm/verisilicon/vs_hwdb.h
+++ b/drivers/gpu/drm/verisilicon/vs_hwdb.h
@@ -9,6 +9,11 @@
 #include <linux/regmap.h>
 #include <linux/types.h>
 
+enum vs_dc_generation {
+	VSDC_GEN_DC8000,
+	VSDC_GEN_DC8200,
+};
+
 struct vs_formats {
 	const u32 *array;
 	unsigned int num;
@@ -19,6 +24,7 @@ struct vs_chip_identity {
 	u32 revision;
 	u32 customer_id;
 
+	enum vs_dc_generation generation;
 	u32 display_count;
 	/*
 	 * The hardware only supports square cursor planes, so this field
diff --git a/drivers/gpu/drm/verisilicon/vs_primary_plane.c b/drivers/gpu/drm/verisilicon/vs_primary_plane.c
index 2750016a7f2c3..85ba2068172db 100644
--- a/drivers/gpu/drm/verisilicon/vs_primary_plane.c
+++ b/drivers/gpu/drm/verisilicon/vs_primary_plane.c
@@ -54,12 +54,6 @@ static int vs_primary_plane_atomic_check(struct drm_plane *plane,
 	return 0;
 }
 
-static void vs_primary_plane_commit(struct vs_dc *dc, unsigned int output)
-{
-	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
-			VSDC_FB_CONFIG_EX_COMMIT);
-}
-
 static void vs_primary_plane_atomic_enable(struct drm_plane *plane,
 					   struct drm_atomic_commit *atomic_state)
 {
@@ -70,13 +64,8 @@ static void vs_primary_plane_atomic_enable(struct drm_plane *plane,
 	unsigned int output = vcrtc->id;
 	struct vs_dc *dc = vcrtc->dc;
 
-	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
-			VSDC_FB_CONFIG_EX_FB_EN);
-	regmap_update_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
-			   VSDC_FB_CONFIG_EX_DISPLAY_ID_MASK,
-			   VSDC_FB_CONFIG_EX_DISPLAY_ID(output));
-
-	vs_primary_plane_commit(dc, output);
+	if (dc->funcs->primary_plane_enable_ex)
+		dc->funcs->primary_plane_enable_ex(dc, output);
 }
 
 static void vs_primary_plane_atomic_disable(struct drm_plane *plane,
@@ -89,10 +78,8 @@ static void vs_primary_plane_atomic_disable(struct drm_plane *plane,
 	unsigned int output = vcrtc->id;
 	struct vs_dc *dc = vcrtc->dc;
 
-	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
-			VSDC_FB_CONFIG_EX_FB_EN);
-
-	vs_primary_plane_commit(dc, output);
+	if (dc->funcs->primary_plane_disable_ex)
+		dc->funcs->primary_plane_disable_ex(dc, output);
 }
 
 static void vs_primary_plane_atomic_update(struct drm_plane *plane,
@@ -138,18 +125,11 @@ static void vs_primary_plane_atomic_update(struct drm_plane *plane,
 	regmap_write(dc->regs, VSDC_FB_STRIDE(output),
 		     fb->pitches[0]);
 
-	regmap_write(dc->regs, VSDC_FB_TOP_LEFT(output),
-		     VSDC_MAKE_PLANE_POS(state->crtc_x, state->crtc_y));
-	regmap_write(dc->regs, VSDC_FB_BOTTOM_RIGHT(output),
-		     VSDC_MAKE_PLANE_POS(state->crtc_x + state->crtc_w,
-					 state->crtc_y + state->crtc_h));
 	regmap_write(dc->regs, VSDC_FB_SIZE(output),
 		     VSDC_MAKE_PLANE_SIZE(state->crtc_w, state->crtc_h));
 
-	regmap_write(dc->regs, VSDC_FB_BLEND_CONFIG(output),
-		     VSDC_FB_BLEND_CONFIG_BLEND_DISABLE);
-
-	vs_primary_plane_commit(dc, output);
+	if (dc->funcs->primary_plane_update_ex)
+		dc->funcs->primary_plane_update_ex(dc, output, state);
 }
 
 static const struct drm_plane_helper_funcs vs_primary_plane_helper_funcs = {
-- 
2.43.0

[PATCH v6 5/6] drm/verisilicon: add DCUltraLite chip identity to HWDB

From: Joey Lu <hidden>
Date: 2026-09-08 09:29:16

The Nuvoton MA35D1 chip contains a DCUltraLite display controller with
model number 0x0 (sic, the model name contains no number either),
revision 0x5560 and customer ID 0x305.  It has a similar register map
with DC8000, only one display output and only 32x32 cursor supported.

Signed-off-by: Joey Lu <redacted>
Reviewed-by: Icenowy Zheng <zhengxingda@iscas.ac.cn>
---
 drivers/gpu/drm/verisilicon/vs_hwdb.c | 10 ++++++++++
 1 file changed, 10 insertions(+)
diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.c b/drivers/gpu/drm/verisilicon/vs_hwdb.c
index 91524d16f7785..7d630a667a3f0 100644
--- a/drivers/gpu/drm/verisilicon/vs_hwdb.c
+++ b/drivers/gpu/drm/verisilicon/vs_hwdb.c
@@ -129,6 +129,16 @@ static struct vs_chip_identity vs_chip_identities[] = {
 		.max_cursor_size = 64,
 		.formats = &vs_formats_no_yuv444,
 	},
+	{
+		.model = 0x0,		/* DCUltraLite */
+		.revision = 0x5560,
+		.customer_id = 0x305,
+
+		.generation = VSDC_GEN_DC8000,
+		.display_count = 1,
+		.max_cursor_size = 32,
+		.formats = &vs_formats_no_yuv444,
+	},
 };
 
 int vs_fill_chip_identity(struct regmap *regs,
-- 
2.43.0

[PATCH v6 4/6] drm/verisilicon: add DC8000 (DCUltraLite) display controller support

From: Joey Lu <hidden>
Date: 2026-09-08 09:29:18

The Verisilicon DC IP family also ships in a DCUltraLite (DC8000
generation) configuration that lacks the DC8200's CONFIG_EX staging
registers and PANEL_START sync register, and exposes its VSYNC
interrupt through a differently laid out status/enable register pair.

Add a vs_dc_funcs implementation for the DC8000 generation in
vs_dc8000.c: framebuffer updates are latched directly through
FB_CONFIG's enable/reset/valid bits instead of a staging commit, panel
output starts as soon as PANEL_CONFIG.RUNNING is set with no separate
sync register to arm, and the VSYNC interrupt is acknowledged through
DISP_IRQ_STA/DISP_IRQ_EN.  Select this vtable in the probe path based
on the chip identity's generation field.

Signed-off-by: Joey Lu <redacted>
---
 drivers/gpu/drm/verisilicon/Makefile    |  2 +-
 drivers/gpu/drm/verisilicon/vs_dc.c     |  5 +-
 drivers/gpu/drm/verisilicon/vs_dc.h     |  1 +
 drivers/gpu/drm/verisilicon/vs_dc8000.c | 92 +++++++++++++++++++++++++
 4 files changed, 98 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc8000.c
diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
index 9d4cd16452fa1..d2fd8e4dff242 100644
--- a/drivers/gpu/drm/verisilicon/Makefile
+++ b/drivers/gpu/drm/verisilicon/Makefile
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
-verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_dc8200.o vs_drm.o vs_hwdb.o \
+verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_dc8200.o vs_dc8000.o vs_drm.o vs_hwdb.o \
 	vs_plane.o vs_primary_plane.o vs_cursor_plane.o
 
 obj-$(CONFIG_DRM_VERISILICON_DC) += verisilicon-dc.o
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
index 9729b693d360e..3e9e2c0264f5d 100644
--- a/drivers/gpu/drm/verisilicon/vs_dc.c
+++ b/drivers/gpu/drm/verisilicon/vs_dc.c
@@ -134,7 +134,10 @@ static int vs_dc_probe(struct platform_device *pdev)
 	dev_info(dev, "Found DC%x rev %x customer %x\n", dc->identity.model,
 		 dc->identity.revision, dc->identity.customer_id);
 
-	dc->funcs = &vs_dc8200_funcs;
+	if (dc->identity.generation == VSDC_GEN_DC8200)
+		dc->funcs = &vs_dc8200_funcs;
+	else
+		dc->funcs = &vs_dc8000_funcs;
 
 	if (port_count > dc->identity.display_count) {
 		dev_err(dev, "too many downstream ports than HW capability\n");
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
index 825f5dd6bf174..ac96ad7011994 100644
--- a/drivers/gpu/drm/verisilicon/vs_dc.h
+++ b/drivers/gpu/drm/verisilicon/vs_dc.h
@@ -66,5 +66,6 @@ struct vs_dc {
 };
 
 extern const struct vs_dc_funcs vs_dc8200_funcs;
+extern const struct vs_dc_funcs vs_dc8000_funcs;
 
 #endif /* _VS_DC_H_ */
diff --git a/drivers/gpu/drm/verisilicon/vs_dc8000.c b/drivers/gpu/drm/verisilicon/vs_dc8000.c
new file mode 100644
index 0000000000000..df7bd5bdd7f2a
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc8000.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Joey Lu <yclu4@nuvoton.com>
+ */
+
+#include <linux/regmap.h>
+
+#include <drm/drm_print.h>
+
+#include "vs_crtc_regs.h"
+#include "vs_dc.h"
+#include "vs_drm.h"
+#include "vs_primary_plane_regs.h"
+
+static void vs_dc8000_panel_enable_ex(struct vs_dc *dc, unsigned int output)
+{
+	regmap_set_bits(dc->regs, VSDC_FB_CONFIG(output),
+			VSDC_FB_CONFIG_RESET);
+}
+
+static void vs_dc8000_panel_disable_ex(struct vs_dc *dc, unsigned int output)
+{
+	regmap_clear_bits(dc->regs, VSDC_FB_CONFIG(output),
+			  VSDC_FB_CONFIG_RESET);
+}
+
+static void vs_dc8000_crtc_begin(struct vs_dc *dc, unsigned int output)
+{
+	regmap_set_bits(dc->regs, VSDC_FB_CONFIG(output),
+			VSDC_FB_CONFIG_VALID);
+}
+
+static void vs_dc8000_crtc_flush(struct vs_dc *dc, unsigned int output)
+{
+	regmap_clear_bits(dc->regs, VSDC_FB_CONFIG(output),
+			  VSDC_FB_CONFIG_VALID);
+}
+
+static void vs_dc8000_crtc_enable_ex(struct vs_dc *dc, unsigned int output)
+{
+	regmap_set_bits(dc->regs, VSDC_FB_CONFIG(output),
+			VSDC_FB_CONFIG_ENABLE);
+}
+
+static void vs_dc8000_crtc_disable_ex(struct vs_dc *dc, unsigned int output)
+{
+	regmap_clear_bits(dc->regs, VSDC_FB_CONFIG(output),
+			  VSDC_FB_CONFIG_ENABLE);
+}
+
+static void vs_dc8000_enable_vblank(struct vs_dc *dc, unsigned int output)
+{
+	regmap_set_bits(dc->regs, VSDC_DISP_IRQ_EN,
+			VSDC_DISP_IRQ_VSYNC(output));
+}
+
+static void vs_dc8000_disable_vblank(struct vs_dc *dc, unsigned int output)
+{
+	regmap_clear_bits(dc->regs, VSDC_DISP_IRQ_EN,
+			  VSDC_DISP_IRQ_VSYNC(output));
+}
+
+static u32 vs_dc8000_irq_ack(struct vs_dc *dc)
+{
+	u32 hw_irqs, unified = 0, known = 0;
+	unsigned int i;
+
+	regmap_read(dc->regs, VSDC_DISP_IRQ_STA, &hw_irqs);
+
+	for (i = 0; i < VSDC_MAX_OUTPUTS; i++) {
+		known |= VSDC_DISP_IRQ_VSYNC(i);
+		if (hw_irqs & VSDC_DISP_IRQ_VSYNC(i))
+			unified |= VSDC_IRQ_VSYNC(i);
+	}
+
+	drm_WARN_ONCE(&dc->drm_dev->base, hw_irqs & ~known,
+		      "Unknown hardware IRQ bits: %#x\n", hw_irqs & ~known);
+
+	return unified;
+}
+
+const struct vs_dc_funcs vs_dc8000_funcs = {
+	.panel_enable_ex	= vs_dc8000_panel_enable_ex,
+	.panel_disable_ex	= vs_dc8000_panel_disable_ex,
+	.crtc_begin		= vs_dc8000_crtc_begin,
+	.crtc_flush		= vs_dc8000_crtc_flush,
+	.crtc_enable_ex		= vs_dc8000_crtc_enable_ex,
+	.crtc_disable_ex	= vs_dc8000_crtc_disable_ex,
+	.enable_vblank		= vs_dc8000_enable_vblank,
+	.disable_vblank		= vs_dc8000_disable_vblank,
+	.irq_ack		= vs_dc8000_irq_ack,
+};
-- 
2.43.0

[PATCH v6 6/6] drm/verisilicon: extend Kconfig to support ARCH_MA35 platforms

From: Joey Lu <hidden>
Date: 2026-09-08 09:29:25

Add ARCH_MA35 to the platform dependencies to allow the driver to be
built for Nuvoton MA35D1.

Signed-off-by: Joey Lu <redacted>
Reviewed-by: Icenowy Zheng <zhengxingda@iscas.ac.cn>
---
 drivers/gpu/drm/verisilicon/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
index 7cce86ec8603c..295d246eb4b40 100644
--- a/drivers/gpu/drm/verisilicon/Kconfig
+++ b/drivers/gpu/drm/verisilicon/Kconfig
@@ -2,7 +2,7 @@
 config DRM_VERISILICON_DC
 	tristate "DRM Support for Verisilicon DC-series display controllers"
 	depends on DRM && COMMON_CLK
-	depends on RISCV || COMPILE_TEST
+	depends on RISCV || ARCH_MA35 || COMPILE_TEST
 	select DRM_BRIDGE_CONNECTOR
 	select DRM_CLIENT_SELECTION
 	select DRM_DISPLAY_HELPER
-- 
2.43.0

Re: [PATCH v6 1/6] dt-bindings: display: verisilicon,dc: add support for nuvoton,ma35d1-dcu

From: Conor Dooley <conor@kernel.org>
Date: 2026-09-08 17:55:31

Acked-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: not-applicable

Re: [PATCH v6 1/6] dt-bindings: display: verisilicon,dc: add support for nuvoton,ma35d1-dcu

From: Icenowy Zheng <zhengxingda@iscas.ac.cn>
Date: 2026-09-09 05:44:21

在 2026-09-08二的 17:28 +0800,Joey Lu写道:
Add the Nuvoton MA35D1 DCUltraLite (nuvoton,ma35d1-dcu) to the
binding.
The DCUltraLite uses only four clocks (core, axi, ahb, pix0) and one
reset (core), with a single output port.

The MA35D1 clock controller gates the core, AXI and AHB clocks with a
single bit, but each remains a distinct clock line feeding the IP
with
its own rate constraints, so all four must still be listed
individually
in the devicetree; core, axi and ahb happen to share the same clock
phandle.
This is weird, but I must admit that we're limited by the Common Clock
Framework here, so I cannot give a better solution either.

Anyway let's settle with the current result.
quoted hunk
Move the clocks/clock-names minItems to 4 and resets/reset-names
minItems to 1 at the top level, since that is the lowest count any
supported variant needs.  Add an allOf/if block that tightens the
constraint back up to the fixed 5-clock/3-reset topology required by
the existing thead,th1520-dc8200 compatible, and another one that
caps
the new nuvoton,ma35d1-dcu compatible at the 4-clock/1-reset count it
actually wires up.

Signed-off-by: Joey Lu <redacted>
---
 .../bindings/display/verisilicon,dc.yaml      | 44
+++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git
a/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
b/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
index 919a900122012..773966677d0f4 100644
--- a/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
+++ b/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
@@ -17,6 +17,7 @@ properties:
     items:
       - enum:
           - thead,th1520-dc8200
+          - nuvoton,ma35d1-dcu
       - const: verisilicon,dc # DC IPs have discoverable ID/revision
registers
 
   reg:
@@ -26,6 +27,7 @@ properties:
     maxItems: 1
 
   clocks:
+    minItems: 4
     items:
       - description: DC Core clock
       - description: DMA AXI bus clock
@@ -34,6 +36,7 @@ properties:
       - description: Pixel clock of output 1
 
   clock-names:
+    minItems: 4
     items:
       - const: core
       - const: axi
@@ -42,12 +45,14 @@ properties:
       - const: pix1
 
   resets:
+    minItems: 1
     items:
       - description: DC Core reset
       - description: DMA AXI bus reset
       - description: Configuration AHB bus reset
 
   reset-names:
+    minItems: 1
     items:
       - const: core
       - const: axi
@@ -79,6 +84,45 @@ required:
   - reset-names
   - ports
 
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: thead,th1520-dc8200
+    then:
+      properties:
+        clocks:
+          minItems: 5
+
+        clock-names:
+          minItems: 5
+
+        resets:
+          minItems: 3
+
+        reset-names:
+          minItems: 3
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: nuvoton,ma35d1-dcu
+    then:
+      properties:
+        clocks:
+          maxItems: 4
+
+        clock-names:
+          maxItems: 4
+
+        resets:
+          maxItems: 1
+
+        reset-names:
+          maxItems: 1
Maybe it's reasonable to restrict max port count to 1 for MA35D1?
Although I am not sure about how to do this...

Thanks,
Icenowy
+
 additionalProperties: false
 
 examples:

Re: [PATCH v6 1/6] dt-bindings: display: verisilicon,dc: add support for nuvoton,ma35d1-dcu

From: Joey Lu <hidden>
Date: 2026-09-10 01:53:18

Icenowy Zheng 於 2026/9/9 下午 01:44 寫道:
在 2026-09-08二的 17:28 +0800,Joey Lu写道:
quoted
Add the Nuvoton MA35D1 DCUltraLite (nuvoton,ma35d1-dcu) to the
binding.
The DCUltraLite uses only four clocks (core, axi, ahb, pix0) and one
reset (core), with a single output port.

The MA35D1 clock controller gates the core, AXI and AHB clocks with a
single bit, but each remains a distinct clock line feeding the IP
with
its own rate constraints, so all four must still be listed
individually
in the devicetree; core, axi and ahb happen to share the same clock
phandle.
This is weird, but I must admit that we're limited by the Common Clock
Framework here, so I cannot give a better solution either.

Anyway let's settle with the current result.
quoted
Move the clocks/clock-names minItems to 4 and resets/reset-names
minItems to 1 at the top level, since that is the lowest count any
supported variant needs.  Add an allOf/if block that tightens the
constraint back up to the fixed 5-clock/3-reset topology required by
the existing thead,th1520-dc8200 compatible, and another one that
caps
the new nuvoton,ma35d1-dcu compatible at the 4-clock/1-reset count it
actually wires up.

Signed-off-by: Joey Lu <redacted>
---
  .../bindings/display/verisilicon,dc.yaml      | 44
+++++++++++++++++++
  1 file changed, 44 insertions(+)

diff --git
a/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
b/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
index 919a900122012..773966677d0f4 100644
--- a/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
+++ b/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
@@ -17,6 +17,7 @@ properties:
      items:
        - enum:
            - thead,th1520-dc8200
+          - nuvoton,ma35d1-dcu
        - const: verisilicon,dc # DC IPs have discoverable ID/revision
registers
  
    reg:
@@ -26,6 +27,7 @@ properties:
      maxItems: 1
  
    clocks:
+    minItems: 4
      items:
        - description: DC Core clock
        - description: DMA AXI bus clock
@@ -34,6 +36,7 @@ properties:
        - description: Pixel clock of output 1
  
    clock-names:
+    minItems: 4
      items:
        - const: core
        - const: axi
@@ -42,12 +45,14 @@ properties:
        - const: pix1
  
    resets:
+    minItems: 1
      items:
        - description: DC Core reset
        - description: DMA AXI bus reset
        - description: Configuration AHB bus reset
  
    reset-names:
+    minItems: 1
      items:
        - const: core
        - const: axi
@@ -79,6 +84,45 @@ required:
    - reset-names
    - ports
  
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: thead,th1520-dc8200
+    then:
+      properties:
+        clocks:
+          minItems: 5
+
+        clock-names:
+          minItems: 5
+
+        resets:
+          minItems: 3
+
+        reset-names:
+          minItems: 3
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: nuvoton,ma35d1-dcu
+    then:
+      properties:
+        clocks:
+          maxItems: 4
+
+        clock-names:
+          maxItems: 4
+
+        resets:
+          maxItems: 1
+
+        reset-names:
+          maxItems: 1
Maybe it's reasonable to restrict max port count to 1 for MA35D1?
Although I am not sure about how to do this...

Thanks,
Icenowy
 I found the same kind of per-compatible port restriction already used upstream in renesas,du.yaml, e.g.:

        ports:
          properties:
            port@2: false
            port@3: false
          required:
            - port@0
            - port@1

Applied to our binding, that would look like:

        ports:
          properties:
            port@1: false
          required:
            - port@0

in the existing nuvoton,ma35d1-dcu allOf/if/then block, so schema checks would reject a port@1 node on this compatible instead of silently accepting it.

Happy to add it if you'd like the schema to enforce this, but wanted to check whether you consider it worth the extra lines given it doesn't reflect an actual bug in any DT today. Let me know which way you'd prefer and I'll fold it into the next version.

Thanks.
quoted
+
  additionalProperties: false
  
  examples:

Re: [PATCH v6 1/6] dt-bindings: display: verisilicon,dc: add support for nuvoton,ma35d1-dcu

From: Icenowy Zheng <zhengxingda@iscas.ac.cn>
Date: 2026-09-10 07:08:53

在 2026-09-10四的 09:52 +0800,Joey Lu写道:
Icenowy Zheng 於 2026/9/9 下午 01:44 寫道:
quoted
在 2026-09-08二的 17:28 +0800,Joey Lu写道:
quoted
Add the Nuvoton MA35D1 DCUltraLite (nuvoton,ma35d1-dcu) to the
binding.
The DCUltraLite uses only four clocks (core, axi, ahb, pix0) and
one
reset (core), with a single output port.

The MA35D1 clock controller gates the core, AXI and AHB clocks
with a
single bit, but each remains a distinct clock line feeding the IP
with
its own rate constraints, so all four must still be listed
individually
in the devicetree; core, axi and ahb happen to share the same
clock
phandle.
This is weird, but I must admit that we're limited by the Common
Clock
Framework here, so I cannot give a better solution either.

Anyway let's settle with the current result.
quoted
Move the clocks/clock-names minItems to 4 and resets/reset-names
minItems to 1 at the top level, since that is the lowest count
any
supported variant needs.  Add an allOf/if block that tightens the
constraint back up to the fixed 5-clock/3-reset topology required
by
the existing thead,th1520-dc8200 compatible, and another one that
caps
the new nuvoton,ma35d1-dcu compatible at the 4-clock/1-reset
count it
actually wires up.

Signed-off-by: Joey Lu <redacted>
---
  .../bindings/display/verisilicon,dc.yaml      | 44
+++++++++++++++++++
  1 file changed, 44 insertions(+)

diff --git
a/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
b/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
index 919a900122012..773966677d0f4 100644
---
a/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
+++
b/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
@@ -17,6 +17,7 @@ properties:
      items:
        - enum:
            - thead,th1520-dc8200
+          - nuvoton,ma35d1-dcu
        - const: verisilicon,dc # DC IPs have discoverable
ID/revision
registers
  
    reg:
@@ -26,6 +27,7 @@ properties:
      maxItems: 1
  
    clocks:
+    minItems: 4
      items:
        - description: DC Core clock
        - description: DMA AXI bus clock
@@ -34,6 +36,7 @@ properties:
        - description: Pixel clock of output 1
  
    clock-names:
+    minItems: 4
      items:
        - const: core
        - const: axi
@@ -42,12 +45,14 @@ properties:
        - const: pix1
  
    resets:
+    minItems: 1
      items:
        - description: DC Core reset
        - description: DMA AXI bus reset
        - description: Configuration AHB bus reset
  
    reset-names:
+    minItems: 1
      items:
        - const: core
        - const: axi
@@ -79,6 +84,45 @@ required:
    - reset-names
    - ports
  
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: thead,th1520-dc8200
+    then:
+      properties:
+        clocks:
+          minItems: 5
+
+        clock-names:
+          minItems: 5
+
+        resets:
+          minItems: 3
+
+        reset-names:
+          minItems: 3
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: nuvoton,ma35d1-dcu
+    then:
+      properties:
+        clocks:
+          maxItems: 4
+
+        clock-names:
+          maxItems: 4
+
+        resets:
+          maxItems: 1
+
+        reset-names:
+          maxItems: 1
Maybe it's reasonable to restrict max port count to 1 for MA35D1?
Although I am not sure about how to do this...

Thanks,
Icenowy
  I found the same kind of per-compatible port restriction already
used 
upstream in renesas,du.yaml, e.g.:

         ports:
           properties:
             port@2: false
             port@3: false
           required:
             - port@0
             - port@1

Applied to our binding, that would look like:

         ports:
           properties:
             port@1: false
           required:
             - port@0

in the existing nuvoton,ma35d1-dcu allOf/if/then block, so schema
checks 
would reject a port@1 node on this compatible instead of silently 
accepting it.

Happy to add it if you'd like the schema to enforce this, but wanted
to 
check whether you consider it worth the extra lines given it doesn't 
reflect an actual bug in any DT today. Let me know which way you'd 
prefer and I'll fold it into the next version.
Personally I think this is good, but maybe adding it as an additional
patch after merging isn't a big problem, because this just plays as
kind of a safety guard.

Well this depends on how DT binding maintainers think, but as Conor has
dropped his Ack, this shouldn't be a big issue.

Thanks,
Icenowy
Thanks.
quoted
quoted
+
  additionalProperties: false
  
  examples:

Re: [PATCH v6 4/6] drm/verisilicon: add DC8000 (DCUltraLite) display controller support

From: Icenowy Zheng <zhengxingda@iscas.ac.cn>
Date: 2026-09-10 07:25:25

在 2026-09-08二的 17:28 +0800,Joey Lu写道:
The Verisilicon DC IP family also ships in a DCUltraLite (DC8000
generation) configuration that lacks the DC8200's CONFIG_EX staging
registers and PANEL_START sync register, and exposes its VSYNC
interrupt through a differently laid out status/enable register pair.

Add a vs_dc_funcs implementation for the DC8000 generation in
vs_dc8000.c: framebuffer updates are latched directly through
FB_CONFIG's enable/reset/valid bits instead of a staging commit,
panel
output starts as soon as PANEL_CONFIG.RUNNING is set with no separate
sync register to arm, and the VSYNC interrupt is acknowledged through
DISP_IRQ_STA/DISP_IRQ_EN.  Select this vtable in the probe path based
on the chip identity's generation field.

Signed-off-by: Joey Lu <redacted>
Reviewed-by: Icenowy Zheng <zhengxingda@iscas.ac.cn>

Thanks,
Icenowy
quoted hunk
---
 drivers/gpu/drm/verisilicon/Makefile    |  2 +-
 drivers/gpu/drm/verisilicon/vs_dc.c     |  5 +-
 drivers/gpu/drm/verisilicon/vs_dc.h     |  1 +
 drivers/gpu/drm/verisilicon/vs_dc8000.c | 92
+++++++++++++++++++++++++
 4 files changed, 98 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc8000.c
diff --git a/drivers/gpu/drm/verisilicon/Makefile
b/drivers/gpu/drm/verisilicon/Makefile
index 9d4cd16452fa1..d2fd8e4dff242 100644
--- a/drivers/gpu/drm/verisilicon/Makefile
+++ b/drivers/gpu/drm/verisilicon/Makefile
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
-verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_dc8200.o
vs_drm.o vs_hwdb.o \
+verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_dc8200.o
vs_dc8000.o vs_drm.o vs_hwdb.o \
 	vs_plane.o vs_primary_plane.o vs_cursor_plane.o
 
 obj-$(CONFIG_DRM_VERISILICON_DC) += verisilicon-dc.o
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c
b/drivers/gpu/drm/verisilicon/vs_dc.c
index 9729b693d360e..3e9e2c0264f5d 100644
--- a/drivers/gpu/drm/verisilicon/vs_dc.c
+++ b/drivers/gpu/drm/verisilicon/vs_dc.c
@@ -134,7 +134,10 @@ static int vs_dc_probe(struct platform_device
*pdev)
 	dev_info(dev, "Found DC%x rev %x customer %x\n", dc-
quoted
identity.model,
 		 dc->identity.revision, dc->identity.customer_id);
 
-	dc->funcs = &vs_dc8200_funcs;
+	if (dc->identity.generation == VSDC_GEN_DC8200)
+		dc->funcs = &vs_dc8200_funcs;
+	else
+		dc->funcs = &vs_dc8000_funcs;
 
 	if (port_count > dc->identity.display_count) {
 		dev_err(dev, "too many downstream ports than HW
capability\n");
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h
b/drivers/gpu/drm/verisilicon/vs_dc.h
index 825f5dd6bf174..ac96ad7011994 100644
--- a/drivers/gpu/drm/verisilicon/vs_dc.h
+++ b/drivers/gpu/drm/verisilicon/vs_dc.h
@@ -66,5 +66,6 @@ struct vs_dc {
 };
 
 extern const struct vs_dc_funcs vs_dc8200_funcs;
+extern const struct vs_dc_funcs vs_dc8000_funcs;
 
 #endif /* _VS_DC_H_ */
diff --git a/drivers/gpu/drm/verisilicon/vs_dc8000.c
b/drivers/gpu/drm/verisilicon/vs_dc8000.c
new file mode 100644
index 0000000000000..df7bd5bdd7f2a
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc8000.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Joey Lu <yclu4@nuvoton.com>
+ */
+
+#include <linux/regmap.h>
+
+#include <drm/drm_print.h>
+
+#include "vs_crtc_regs.h"
+#include "vs_dc.h"
+#include "vs_drm.h"
+#include "vs_primary_plane_regs.h"
+
+static void vs_dc8000_panel_enable_ex(struct vs_dc *dc, unsigned int
output)
+{
+	regmap_set_bits(dc->regs, VSDC_FB_CONFIG(output),
+			VSDC_FB_CONFIG_RESET);
+}
+
+static void vs_dc8000_panel_disable_ex(struct vs_dc *dc, unsigned
int output)
+{
+	regmap_clear_bits(dc->regs, VSDC_FB_CONFIG(output),
+			  VSDC_FB_CONFIG_RESET);
+}
+
+static void vs_dc8000_crtc_begin(struct vs_dc *dc, unsigned int
output)
+{
+	regmap_set_bits(dc->regs, VSDC_FB_CONFIG(output),
+			VSDC_FB_CONFIG_VALID);
+}
+
+static void vs_dc8000_crtc_flush(struct vs_dc *dc, unsigned int
output)
+{
+	regmap_clear_bits(dc->regs, VSDC_FB_CONFIG(output),
+			  VSDC_FB_CONFIG_VALID);
+}
+
+static void vs_dc8000_crtc_enable_ex(struct vs_dc *dc, unsigned int
output)
+{
+	regmap_set_bits(dc->regs, VSDC_FB_CONFIG(output),
+			VSDC_FB_CONFIG_ENABLE);
+}
+
+static void vs_dc8000_crtc_disable_ex(struct vs_dc *dc, unsigned int
output)
+{
+	regmap_clear_bits(dc->regs, VSDC_FB_CONFIG(output),
+			  VSDC_FB_CONFIG_ENABLE);
+}
+
+static void vs_dc8000_enable_vblank(struct vs_dc *dc, unsigned int
output)
+{
+	regmap_set_bits(dc->regs, VSDC_DISP_IRQ_EN,
+			VSDC_DISP_IRQ_VSYNC(output));
+}
+
+static void vs_dc8000_disable_vblank(struct vs_dc *dc, unsigned int
output)
+{
+	regmap_clear_bits(dc->regs, VSDC_DISP_IRQ_EN,
+			  VSDC_DISP_IRQ_VSYNC(output));
+}
+
+static u32 vs_dc8000_irq_ack(struct vs_dc *dc)
+{
+	u32 hw_irqs, unified = 0, known = 0;
+	unsigned int i;
+
+	regmap_read(dc->regs, VSDC_DISP_IRQ_STA, &hw_irqs);
+
+	for (i = 0; i < VSDC_MAX_OUTPUTS; i++) {
+		known |= VSDC_DISP_IRQ_VSYNC(i);
+		if (hw_irqs & VSDC_DISP_IRQ_VSYNC(i))
+			unified |= VSDC_IRQ_VSYNC(i);
+	}
+
+	drm_WARN_ONCE(&dc->drm_dev->base, hw_irqs & ~known,
+		      "Unknown hardware IRQ bits: %#x\n", hw_irqs &
~known);
+
+	return unified;
+}
+
+const struct vs_dc_funcs vs_dc8000_funcs = {
+	.panel_enable_ex	= vs_dc8000_panel_enable_ex,
+	.panel_disable_ex	= vs_dc8000_panel_disable_ex,
+	.crtc_begin		= vs_dc8000_crtc_begin,
+	.crtc_flush		= vs_dc8000_crtc_flush,
+	.crtc_enable_ex		= vs_dc8000_crtc_enable_ex,
+	.crtc_disable_ex	= vs_dc8000_crtc_disable_ex,
+	.enable_vblank		= vs_dc8000_enable_vblank,
+	.disable_vblank		= vs_dc8000_disable_vblank,
+	.irq_ack		= vs_dc8000_irq_ack,
+};

Re: [PATCH v6 1/6] dt-bindings: display: verisilicon,dc: add support for nuvoton,ma35d1-dcu

From: Joey Lu <hidden>
Date: 2026-09-14 08:15:53

Icenowy Zheng 於 2026/9/10 下午 03:08 寫道:
在 2026-09-10四的 09:52 +0800,Joey Lu写道:
quoted
Icenowy Zheng 於 2026/9/9 下午 01:44 寫道:
quoted
在 2026-09-08二的 17:28 +0800,Joey Lu写道:
quoted
Add the Nuvoton MA35D1 DCUltraLite (nuvoton,ma35d1-dcu) to the
binding.
The DCUltraLite uses only four clocks (core, axi, ahb, pix0) and
one
reset (core), with a single output port.

The MA35D1 clock controller gates the core, AXI and AHB clocks
with a
single bit, but each remains a distinct clock line feeding the IP
with
its own rate constraints, so all four must still be listed
individually
in the devicetree; core, axi and ahb happen to share the same
clock
phandle.
This is weird, but I must admit that we're limited by the Common
Clock
Framework here, so I cannot give a better solution either.

Anyway let's settle with the current result.
quoted
Move the clocks/clock-names minItems to 4 and resets/reset-names
minItems to 1 at the top level, since that is the lowest count
any
supported variant needs.  Add an allOf/if block that tightens the
constraint back up to the fixed 5-clock/3-reset topology required
by
the existing thead,th1520-dc8200 compatible, and another one that
caps
the new nuvoton,ma35d1-dcu compatible at the 4-clock/1-reset
count it
actually wires up.

Signed-off-by: Joey Lu <redacted>
---
   .../bindings/display/verisilicon,dc.yaml      | 44
+++++++++++++++++++
   1 file changed, 44 insertions(+)

diff --git
a/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
b/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
index 919a900122012..773966677d0f4 100644
---
a/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
+++
b/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
@@ -17,6 +17,7 @@ properties:
       items:
         - enum:
             - thead,th1520-dc8200
+          - nuvoton,ma35d1-dcu
         - const: verisilicon,dc # DC IPs have discoverable
ID/revision
registers
   
     reg:
@@ -26,6 +27,7 @@ properties:
       maxItems: 1
   
     clocks:
+    minItems: 4
       items:
         - description: DC Core clock
         - description: DMA AXI bus clock
@@ -34,6 +36,7 @@ properties:
         - description: Pixel clock of output 1
   
     clock-names:
+    minItems: 4
       items:
         - const: core
         - const: axi
@@ -42,12 +45,14 @@ properties:
         - const: pix1
   
     resets:
+    minItems: 1
       items:
         - description: DC Core reset
         - description: DMA AXI bus reset
         - description: Configuration AHB bus reset
   
     reset-names:
+    minItems: 1
       items:
         - const: core
         - const: axi
@@ -79,6 +84,45 @@ required:
     - reset-names
     - ports
   
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: thead,th1520-dc8200
+    then:
+      properties:
+        clocks:
+          minItems: 5
+
+        clock-names:
+          minItems: 5
+
+        resets:
+          minItems: 3
+
+        reset-names:
+          minItems: 3
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: nuvoton,ma35d1-dcu
+    then:
+      properties:
+        clocks:
+          maxItems: 4
+
+        clock-names:
+          maxItems: 4
+
+        resets:
+          maxItems: 1
+
+        reset-names:
+          maxItems: 1
Maybe it's reasonable to restrict max port count to 1 for MA35D1?
Although I am not sure about how to do this...

Thanks,
Icenowy
   I found the same kind of per-compatible port restriction already
used
upstream in renesas,du.yaml, e.g.:

          ports:
            properties:
              port@2: false
              port@3: false
            required:
              - port@0
              - port@1

Applied to our binding, that would look like:

          ports:
            properties:
              port@1: false
            required:
              - port@0

in the existing nuvoton,ma35d1-dcu allOf/if/then block, so schema
checks
would reject a port@1 node on this compatible instead of silently
accepting it.

Happy to add it if you'd like the schema to enforce this, but wanted
to
check whether you consider it worth the extra lines given it doesn't
reflect an actual bug in any DT today. Let me know which way you'd
prefer and I'll fold it into the next version.
Personally I think this is good, but maybe adding it as an additional
patch after merging isn't a big problem, because this just plays as
kind of a safety guard.

Well this depends on how DT binding maintainers think, but as Conor has
dropped his Ack, this shouldn't be a big issue.

Thanks,
Icenowy
I'll send the port@1: false restriction as a follow-up patch once this series lands.
Thanks!
quoted
Thanks.
quoted
quoted
+
   additionalProperties: false
   
   examples:

Re: [PATCH v6 3/6] drm/verisilicon: introduce per-variant hardware ops table

From: Icenowy Zheng <zhengxingda@iscas.ac.cn>
Date: 2026-09-14 15:00:56

在 2026-09-08二的 17:28 +0800,Joey Lu写道:
quoted hunk
The DC8200 and DCUltraLite share a broadly similar register layout
but
differ in how the bridge, CRTC, primary plane and IRQ paths are
driven.
Introduce a vs_dc_funcs vtable so each variant can supply its own
implementation without scattering conditionals across multiple files.

Add a generation field to struct vs_chip_identity to distinguish
variants.
Extract the DC8200-specific hardware ops into vs_dc8200.c and add
unified
IRQ bit definitions so implementations can translate hardware-
specific
bits to a common set.  Update the shared code to dispatch through
dc->funcs.

No behaviour change for existing DC8200 platforms.

Signed-off-by: Joey Lu <redacted>
Reviewed-by: Icenowy Zheng <zhengxingda@iscas.ac.cn>
---
 drivers/gpu/drm/verisilicon/Makefile          |   2 +-
 drivers/gpu/drm/verisilicon/vs_bridge.c       |  20 +--
 drivers/gpu/drm/verisilicon/vs_crtc.c         |  38 +++++-
 drivers/gpu/drm/verisilicon/vs_dc.c           |   6 +-
 drivers/gpu/drm/verisilicon/vs_dc.h           |  32 +++++
 drivers/gpu/drm/verisilicon/vs_dc8200.c       | 121
++++++++++++++++++
 drivers/gpu/drm/verisilicon/vs_drm.c          |   5 +-
 drivers/gpu/drm/verisilicon/vs_drm.h          |   8 ++
 drivers/gpu/drm/verisilicon/vs_hwdb.c         |   4 +
 drivers/gpu/drm/verisilicon/vs_hwdb.h         |   6 +
 .../gpu/drm/verisilicon/vs_primary_plane.c    |  32 +----
 11 files changed, 220 insertions(+), 54 deletions(-)
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc8200.c
diff --git a/drivers/gpu/drm/verisilicon/Makefile
b/drivers/gpu/drm/verisilicon/Makefile
index 426f4bcaa834d..9d4cd16452fa1 100644
--- a/drivers/gpu/drm/verisilicon/Makefile
+++ b/drivers/gpu/drm/verisilicon/Makefile
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
-verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_drm.o
vs_hwdb.o \
+verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_dc8200.o
vs_drm.o vs_hwdb.o \
 	vs_plane.o vs_primary_plane.o vs_cursor_plane.o
 
 obj-$(CONFIG_DRM_VERISILICON_DC) += verisilicon-dc.o
diff --git a/drivers/gpu/drm/verisilicon/vs_bridge.c
b/drivers/gpu/drm/verisilicon/vs_bridge.c
index dc7c85b07fe32..3fbc8d57f8a1e 100644
--- a/drivers/gpu/drm/verisilicon/vs_bridge.c
+++ b/drivers/gpu/drm/verisilicon/vs_bridge.c
@@ -162,15 +162,8 @@ static void vs_bridge_enable_common(struct
vs_crtc *crtc,
 			VSDC_DISP_PANEL_CONFIG_DE_EN |
 			VSDC_DISP_PANEL_CONFIG_DAT_EN |
 			VSDC_DISP_PANEL_CONFIG_CLK_EN);
-	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
-			VSDC_DISP_PANEL_CONFIG_RUNNING);
-	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
-			  VSDC_DISP_PANEL_START_MULTI_DISP_SYNC);
-	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_START,
-			VSDC_DISP_PANEL_START_RUNNING(output));
-
-	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(crtc-
quoted
id),
-			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
+
+	dc->funcs->panel_enable_ex(dc, output);
 }
 
 static void vs_bridge_atomic_enable_dpi(struct drm_bridge *bridge,
@@ -228,14 +221,7 @@ static void vs_bridge_atomic_disable(struct
drm_bridge *bridge,
 	struct vs_dc *dc = crtc->dc;
 	unsigned int output = crtc->id;
 
-	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
-			  VSDC_DISP_PANEL_START_MULTI_DISP_SYNC |
-			  VSDC_DISP_PANEL_START_RUNNING(output));
-	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
-			  VSDC_DISP_PANEL_CONFIG_RUNNING);
-
-	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(crtc-
quoted
id),
-			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
+	dc->funcs->panel_disable_ex(dc, output);
 }
 
 static const struct drm_bridge_funcs vs_dpi_bridge_funcs = {
diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c
b/drivers/gpu/drm/verisilicon/vs_crtc.c
index c24c1588cbe69..2d4bf6af1f56e 100644
--- a/drivers/gpu/drm/verisilicon/vs_crtc.c
+++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
@@ -16,10 +16,33 @@
 #include "vs_crtc_regs.h"
 #include "vs_crtc.h"
 #include "vs_dc.h"
-#include "vs_dc_top_regs.h"
 #include "vs_drm.h"
 #include "vs_plane.h"
 
+static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
+				  struct drm_atomic_commit *state)
+{
+	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
+	struct vs_dc *dc = vcrtc->dc;
+	unsigned int output = vcrtc->id;
+
+	if (dc->funcs->crtc_begin)
+		dc->funcs->crtc_begin(dc, output);
+}
+
+static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
+				  struct drm_atomic_commit *state)
+{
+	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
+	struct vs_dc *dc = vcrtc->dc;
+	unsigned int output = vcrtc->id;
+
+	if (dc->funcs->crtc_flush)
+		dc->funcs->crtc_flush(dc, output);
+
+	drm_crtc_vblank_atomic_flush(crtc, state);
+}
+
 static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
 				   struct drm_atomic_commit *state)
 {
@@ -30,6 +53,9 @@ static void vs_crtc_atomic_disable(struct drm_crtc
*crtc,
 	drm_crtc_vblank_off(crtc);
 
 	clk_disable_unprepare(dc->pix_clk[output]);
+
+	if (dc->funcs->crtc_disable_ex)
+		dc->funcs->crtc_disable_ex(dc, output);
 }
 
 static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
@@ -42,6 +68,9 @@ static void vs_crtc_atomic_enable(struct drm_crtc
*crtc,
 	drm_WARN_ON(&dc->drm_dev->base,
 		    clk_prepare_enable(dc->pix_clk[output]));
 
+	if (dc->funcs->crtc_enable_ex)
+		dc->funcs->crtc_enable_ex(dc, output);
+
 	drm_crtc_vblank_on(crtc);
 }
 
@@ -119,7 +148,8 @@ static bool vs_crtc_mode_fixup(struct drm_crtc
*crtc,
 }
 
 static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
-	.atomic_flush	= drm_crtc_vblank_atomic_flush,
+	.atomic_begin	= vs_crtc_atomic_begin,
+	.atomic_flush	= vs_crtc_atomic_flush,
 	.atomic_enable	= vs_crtc_atomic_enable,
 	.atomic_disable	= vs_crtc_atomic_disable,
 	.mode_set_nofb	= vs_crtc_mode_set_nofb,
@@ -132,7 +162,7 @@ static int vs_crtc_enable_vblank(struct drm_crtc
*crtc)
 	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
 	struct vs_dc *dc = vcrtc->dc;
 
-	regmap_set_bits(dc->regs, VSDC_TOP_IRQ_EN,
VSDC_TOP_IRQ_VSYNC(vcrtc->id));
+	dc->funcs->enable_vblank(dc, vcrtc->id);
 
 	return 0;
 }
@@ -142,7 +172,7 @@ static void vs_crtc_disable_vblank(struct
drm_crtc *crtc)
 	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
 	struct vs_dc *dc = vcrtc->dc;
 
-	regmap_clear_bits(dc->regs, VSDC_TOP_IRQ_EN,
VSDC_TOP_IRQ_VSYNC(vcrtc->id));
+	dc->funcs->disable_vblank(dc, vcrtc->id);
 }
 
 static const struct drm_crtc_funcs vs_crtc_funcs = {
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c
b/drivers/gpu/drm/verisilicon/vs_dc.c
index dad9967bc10b8..9729b693d360e 100644
--- a/drivers/gpu/drm/verisilicon/vs_dc.c
+++ b/drivers/gpu/drm/verisilicon/vs_dc.c
@@ -8,9 +8,7 @@
 #include <linux/of.h>
 #include <linux/of_graph.h>
 
-#include "vs_crtc.h"
 #include "vs_dc.h"
-#include "vs_dc_top_regs.h"
 #include "vs_drm.h"
 #include "vs_hwdb.h"
 
@@ -33,7 +31,7 @@ static irqreturn_t vs_dc_irq_handler(int irq, void
*private)
 	struct vs_dc *dc = private;
 	u32 irqs;
 
-	regmap_read(dc->regs, VSDC_TOP_IRQ_ACK, &irqs);
+	irqs = dc->funcs->irq_ack(dc);
 
 	vs_drm_handle_irq(dc, irqs);
 
@@ -136,6 +134,8 @@ static int vs_dc_probe(struct platform_device
*pdev)
 	dev_info(dev, "Found DC%x rev %x customer %x\n", dc-
quoted
identity.model,
 		 dc->identity.revision, dc->identity.customer_id);
 
+	dc->funcs = &vs_dc8200_funcs;
+
 	if (port_count > dc->identity.display_count) {
 		dev_err(dev, "too many downstream ports than HW
capability\n");
 		ret = -EINVAL;
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h
b/drivers/gpu/drm/verisilicon/vs_dc.h
index ed1016f18758e..825f5dd6bf174 100644
--- a/drivers/gpu/drm/verisilicon/vs_dc.h
+++ b/drivers/gpu/drm/verisilicon/vs_dc.h
@@ -14,6 +14,7 @@
 #include <linux/reset.h>
 
 #include <drm/drm_device.h>
+#include <drm/drm_plane.h>
 
 #include "vs_hwdb.h"
 
@@ -22,6 +23,34 @@
 
 struct vs_drm_dev;
 struct vs_crtc;
+struct vs_dc;
+
+struct vs_dc_funcs {
+	/* Bridge: atomic_enable, atomic_disable */
+	void (*panel_enable_ex)(struct vs_dc *dc, unsigned int
output);
+	void (*panel_disable_ex)(struct vs_dc *dc, unsigned int
output);
+
+	/* CRTC: atomic_begin, atomic_flush */
+	void (*crtc_begin)(struct vs_dc *dc, unsigned int output);
+	void (*crtc_flush)(struct vs_dc *dc, unsigned int output);
+
+	/* CRTC: atomic_enable, atomic_disable */
+	void (*crtc_enable_ex)(struct vs_dc *dc, unsigned int
output);
+	void (*crtc_disable_ex)(struct vs_dc *dc, unsigned int
output);
+
+	/* CRTC: enable_vblank, disable_vblank */
+	void (*enable_vblank)(struct vs_dc *dc, unsigned int
output);
+	void (*disable_vblank)(struct vs_dc *dc, unsigned int
output);
+
+	/* Primary plane: atomic_enable, atomic_disable,
atomic_update */
+	void (*primary_plane_enable_ex)(struct vs_dc *dc, unsigned
int output);
+	void (*primary_plane_disable_ex)(struct vs_dc *dc, unsigned
int output);
+	void (*primary_plane_update_ex)(struct vs_dc *dc, unsigned
int output,
+					struct drm_plane_state
*state);
+
+	/* IRQ acknowledge */
+	u32 (*irq_ack)(struct vs_dc *dc);
+};
 
 struct vs_dc {
 	struct regmap *regs;
@@ -33,6 +62,9 @@ struct vs_dc {
 
 	struct vs_drm_dev *drm_dev;
 	struct vs_chip_identity identity;
+	const struct vs_dc_funcs *funcs;
 };
 
+extern const struct vs_dc_funcs vs_dc8200_funcs;
+
 #endif /* _VS_DC_H_ */
diff --git a/drivers/gpu/drm/verisilicon/vs_dc8200.c
b/drivers/gpu/drm/verisilicon/vs_dc8200.c
new file mode 100644
index 0000000000000..77752d0cb28b0
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc8200.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Icenowy Zheng <uwu@icenowy.me>
+ */
+
+#include <linux/regmap.h>
+
+#include <drm/drm_print.h>
+
+#include "vs_bridge_regs.h"
+#include "vs_dc.h"
+#include "vs_dc_top_regs.h"
+#include "vs_drm.h"
+#include "vs_plane.h"
+#include "vs_primary_plane_regs.h"
+
+static void vs_dc8200_panel_enable_ex(struct vs_dc *dc, unsigned int
output)
+{
+	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
+			VSDC_DISP_PANEL_CONFIG_RUNNING);
+	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
+			  VSDC_DISP_PANEL_START_MULTI_DISP_SYNC);
+	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_START,
+			VSDC_DISP_PANEL_START_RUNNING(output));
+
+	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(output),
+			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
+}
+
+static void vs_dc8200_panel_disable_ex(struct vs_dc *dc, unsigned
int output)
+{
+	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
+			  VSDC_DISP_PANEL_CONFIG_RUNNING);
+	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
+			  VSDC_DISP_PANEL_START_MULTI_DISP_SYNC |
+			  VSDC_DISP_PANEL_START_RUNNING(output));
+
+	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(output),
+			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
+}
+
+static void vs_dc8200_enable_vblank(struct vs_dc *dc, unsigned int
output)
+{
+	regmap_set_bits(dc->regs, VSDC_TOP_IRQ_EN,
+			VSDC_TOP_IRQ_VSYNC(output));
+}
+
+static void vs_dc8200_disable_vblank(struct vs_dc *dc, unsigned int
output)
+{
+	regmap_clear_bits(dc->regs, VSDC_TOP_IRQ_EN,
+			  VSDC_TOP_IRQ_VSYNC(output));
+}
+
+static void vs_dc8200_plane_commit(struct vs_dc *dc, unsigned int
output)
+{
+	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
+			VSDC_FB_CONFIG_EX_COMMIT);
+}
+
+static void vs_dc8200_primary_plane_enable_ex(struct vs_dc *dc,
unsigned int output)
+{
+	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
+			VSDC_FB_CONFIG_EX_FB_EN);
+	regmap_update_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
+			   VSDC_FB_CONFIG_EX_DISPLAY_ID_MASK,
+			   VSDC_FB_CONFIG_EX_DISPLAY_ID(output));
+
+	vs_dc8200_plane_commit(dc, output);
+}
+
+static void vs_dc8200_primary_plane_disable_ex(struct vs_dc *dc,
unsigned int output)
+{
+	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
+			VSDC_FB_CONFIG_EX_FB_EN);
I think I messed up the DC8200 codepath here -- the FB_EN bit should be
cleared instead of set.

Would you mind I fix this when applying, or would you send a new
revision fixing this?

In the latter case, should I send a fix to the current code before?

Thanks,
Icenowy
quoted hunk
+
+	vs_dc8200_plane_commit(dc, output);
+}
+
+static void vs_dc8200_primary_plane_update_ex(struct vs_dc *dc,
unsigned int output,
+				       struct drm_plane_state
*state)
+{
+	regmap_write(dc->regs, VSDC_FB_TOP_LEFT(output),
+		     VSDC_MAKE_PLANE_POS(state->crtc_x, state-
quoted
crtc_y));
+	regmap_write(dc->regs, VSDC_FB_BOTTOM_RIGHT(output),
+		     VSDC_MAKE_PLANE_POS(state->crtc_x + state-
quoted
crtc_w,
+					 state->crtc_y + state-
quoted
crtc_h));
+	regmap_write(dc->regs, VSDC_FB_BLEND_CONFIG(output),
+		     VSDC_FB_BLEND_CONFIG_BLEND_DISABLE);
+
+	vs_dc8200_plane_commit(dc, output);
+}
+
+static u32 vs_dc8200_irq_ack(struct vs_dc *dc)
+{
+	u32 hw_irqs, unified = 0, known = 0;
+	unsigned int i;
+
+	regmap_read(dc->regs, VSDC_TOP_IRQ_ACK, &hw_irqs);
+
+	for (i = 0; i < VSDC_MAX_OUTPUTS; i++) {
+		known |= VSDC_TOP_IRQ_VSYNC(i);
+		if (hw_irqs & VSDC_TOP_IRQ_VSYNC(i))
+			unified |= VSDC_IRQ_VSYNC(i);
+	}
+
+	drm_WARN_ONCE(&dc->drm_dev->base, hw_irqs & ~known,
+		      "Unknown hardware IRQ bits: %#x\n", hw_irqs &
~known);
+
+	return unified;
+}
+
+const struct vs_dc_funcs vs_dc8200_funcs = {
+	.panel_enable_ex		= vs_dc8200_panel_enable_ex,
+	.panel_disable_ex		=
vs_dc8200_panel_disable_ex,
+	.enable_vblank			= vs_dc8200_enable_vblank,
+	.disable_vblank			=
vs_dc8200_disable_vblank,
+	.primary_plane_enable_ex	=
vs_dc8200_primary_plane_enable_ex,
+	.primary_plane_disable_ex	=
vs_dc8200_primary_plane_disable_ex,
+	.primary_plane_update_ex	=
vs_dc8200_primary_plane_update_ex,
+	.irq_ack			= vs_dc8200_irq_ack,
+};
diff --git a/drivers/gpu/drm/verisilicon/vs_drm.c
b/drivers/gpu/drm/verisilicon/vs_drm.c
index fd259d53f49f1..24e9d0b008f31 100644
--- a/drivers/gpu/drm/verisilicon/vs_drm.c
+++ b/drivers/gpu/drm/verisilicon/vs_drm.c
@@ -25,7 +25,6 @@
 #include "vs_bridge.h"
 #include "vs_crtc.h"
 #include "vs_dc.h"
-#include "vs_dc_top_regs.h"
 #include "vs_drm.h"
 
 #define DRIVER_NAME	"verisilicon"
@@ -168,8 +167,8 @@ void vs_drm_handle_irq(struct vs_dc *dc, u32
irqs)
 	unsigned int i;
 
 	for (i = 0; i < dc->identity.display_count; i++) {
-		if (irqs & VSDC_TOP_IRQ_VSYNC(i)) {
-			irqs &= ~VSDC_TOP_IRQ_VSYNC(i);
+		if (irqs & VSDC_IRQ_VSYNC(i)) {
+			irqs &= ~VSDC_IRQ_VSYNC(i);
 			if (dc->drm_dev->crtcs[i])
 				drm_crtc_handle_vblank(&dc->drm_dev-
quoted
crtcs[i]->base);
 		}
diff --git a/drivers/gpu/drm/verisilicon/vs_drm.h
b/drivers/gpu/drm/verisilicon/vs_drm.h
index 606338206a427..6a89c20879df7 100644
--- a/drivers/gpu/drm/verisilicon/vs_drm.h
+++ b/drivers/gpu/drm/verisilicon/vs_drm.h
@@ -6,6 +6,7 @@
 #ifndef _VS_DRM_H_
 #define _VS_DRM_H_
 
+#include <linux/bits.h>
 #include <linux/platform_device.h>
 #include <linux/types.h>
 
@@ -13,6 +14,13 @@
 
 struct vs_dc;
 
+/*
+ * DC variants use different interrupt registers with diverging bit
+ * assignments; each irq_ack() implementation must translate its
+ * hardware-specific bits into these definitions.
+ */
+#define VSDC_IRQ_VSYNC(n)	BIT(n)
+
 struct vs_drm_dev {
 	struct drm_device base;
 
diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.c
b/drivers/gpu/drm/verisilicon/vs_hwdb.c
index 2a0f7c59afa3a..91524d16f7785 100644
--- a/drivers/gpu/drm/verisilicon/vs_hwdb.c
+++ b/drivers/gpu/drm/verisilicon/vs_hwdb.c
@@ -94,6 +94,7 @@ static struct vs_chip_identity vs_chip_identities[]
= {
 		.revision = 0x5720,
 		.customer_id = ~0U,
 
+		.generation = VSDC_GEN_DC8200,
 		.display_count = 2,
 		.max_cursor_size = 64,
 		.formats = &vs_formats_no_yuv444,
@@ -103,6 +104,7 @@ static struct vs_chip_identity
vs_chip_identities[] = {
 		.revision = 0x5721,
 		.customer_id = 0x30B,
 
+		.generation = VSDC_GEN_DC8200,
 		.display_count = 2,
 		.max_cursor_size = 64,
 		.formats = &vs_formats_no_yuv444,
@@ -112,6 +114,7 @@ static struct vs_chip_identity
vs_chip_identities[] = {
 		.revision = 0x5720,
 		.customer_id = 0x310,
 
+		.generation = VSDC_GEN_DC8200,
 		.display_count = 2,
 		.max_cursor_size = 64,
 		.formats = &vs_formats_with_yuv444,
@@ -121,6 +124,7 @@ static struct vs_chip_identity
vs_chip_identities[] = {
 		.revision = 0x5720,
 		.customer_id = 0x311,
 
+		.generation = VSDC_GEN_DC8200,
 		.display_count = 2,
 		.max_cursor_size = 64,
 		.formats = &vs_formats_no_yuv444,
diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.h
b/drivers/gpu/drm/verisilicon/vs_hwdb.h
index 2065ecb730437..a15c8b5656044 100644
--- a/drivers/gpu/drm/verisilicon/vs_hwdb.h
+++ b/drivers/gpu/drm/verisilicon/vs_hwdb.h
@@ -9,6 +9,11 @@
 #include <linux/regmap.h>
 #include <linux/types.h>
 
+enum vs_dc_generation {
+	VSDC_GEN_DC8000,
+	VSDC_GEN_DC8200,
+};
+
 struct vs_formats {
 	const u32 *array;
 	unsigned int num;
@@ -19,6 +24,7 @@ struct vs_chip_identity {
 	u32 revision;
 	u32 customer_id;
 
+	enum vs_dc_generation generation;
 	u32 display_count;
 	/*
 	 * The hardware only supports square cursor planes, so this
field
diff --git a/drivers/gpu/drm/verisilicon/vs_primary_plane.c
b/drivers/gpu/drm/verisilicon/vs_primary_plane.c
index 2750016a7f2c3..85ba2068172db 100644
--- a/drivers/gpu/drm/verisilicon/vs_primary_plane.c
+++ b/drivers/gpu/drm/verisilicon/vs_primary_plane.c
@@ -54,12 +54,6 @@ static int vs_primary_plane_atomic_check(struct
drm_plane *plane,
 	return 0;
 }
 
-static void vs_primary_plane_commit(struct vs_dc *dc, unsigned int
output)
-{
-	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
-			VSDC_FB_CONFIG_EX_COMMIT);
-}
-
 static void vs_primary_plane_atomic_enable(struct drm_plane *plane,
 					   struct drm_atomic_commit
*atomic_state)
 {
@@ -70,13 +64,8 @@ static void vs_primary_plane_atomic_enable(struct
drm_plane *plane,
 	unsigned int output = vcrtc->id;
 	struct vs_dc *dc = vcrtc->dc;
 
-	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
-			VSDC_FB_CONFIG_EX_FB_EN);
-	regmap_update_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
-			   VSDC_FB_CONFIG_EX_DISPLAY_ID_MASK,
-			   VSDC_FB_CONFIG_EX_DISPLAY_ID(output));
-
-	vs_primary_plane_commit(dc, output);
+	if (dc->funcs->primary_plane_enable_ex)
+		dc->funcs->primary_plane_enable_ex(dc, output);
 }
 
 static void vs_primary_plane_atomic_disable(struct drm_plane *plane,
@@ -89,10 +78,8 @@ static void vs_primary_plane_atomic_disable(struct
drm_plane *plane,
 	unsigned int output = vcrtc->id;
 	struct vs_dc *dc = vcrtc->dc;
 
-	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
-			VSDC_FB_CONFIG_EX_FB_EN);
-
-	vs_primary_plane_commit(dc, output);
+	if (dc->funcs->primary_plane_disable_ex)
+		dc->funcs->primary_plane_disable_ex(dc, output);
 }
 
 static void vs_primary_plane_atomic_update(struct drm_plane *plane,
@@ -138,18 +125,11 @@ static void
vs_primary_plane_atomic_update(struct drm_plane *plane,
 	regmap_write(dc->regs, VSDC_FB_STRIDE(output),
 		     fb->pitches[0]);
 
-	regmap_write(dc->regs, VSDC_FB_TOP_LEFT(output),
-		     VSDC_MAKE_PLANE_POS(state->crtc_x, state-
quoted
crtc_y));
-	regmap_write(dc->regs, VSDC_FB_BOTTOM_RIGHT(output),
-		     VSDC_MAKE_PLANE_POS(state->crtc_x + state-
quoted
crtc_w,
-					 state->crtc_y + state-
quoted
crtc_h));
 	regmap_write(dc->regs, VSDC_FB_SIZE(output),
 		     VSDC_MAKE_PLANE_SIZE(state->crtc_w, state-
quoted
crtc_h));
 
-	regmap_write(dc->regs, VSDC_FB_BLEND_CONFIG(output),
-		     VSDC_FB_BLEND_CONFIG_BLEND_DISABLE);
-
-	vs_primary_plane_commit(dc, output);
+	if (dc->funcs->primary_plane_update_ex)
+		dc->funcs->primary_plane_update_ex(dc, output,
state);
 }
 
 static const struct drm_plane_helper_funcs
vs_primary_plane_helper_funcs = {

Re: [PATCH v6 3/6] drm/verisilicon: introduce per-variant hardware ops table

From: Joey Lu <hidden>
Date: 2026-09-15 02:06:53

Icenowy Zheng 於 2026/9/14 下午 11:00 寫道:
在 2026-09-08二的 17:28 +0800,Joey Lu写道:
quoted
The DC8200 and DCUltraLite share a broadly similar register layout
but
differ in how the bridge, CRTC, primary plane and IRQ paths are
driven.
Introduce a vs_dc_funcs vtable so each variant can supply its own
implementation without scattering conditionals across multiple files.

Add a generation field to struct vs_chip_identity to distinguish
variants.
Extract the DC8200-specific hardware ops into vs_dc8200.c and add
unified
IRQ bit definitions so implementations can translate hardware-
specific
bits to a common set.  Update the shared code to dispatch through
dc->funcs.

No behaviour change for existing DC8200 platforms.

Signed-off-by: Joey Lu <redacted>
Reviewed-by: Icenowy Zheng <zhengxingda@iscas.ac.cn>
---
  drivers/gpu/drm/verisilicon/Makefile          |   2 +-
  drivers/gpu/drm/verisilicon/vs_bridge.c       |  20 +--
  drivers/gpu/drm/verisilicon/vs_crtc.c         |  38 +++++-
  drivers/gpu/drm/verisilicon/vs_dc.c           |   6 +-
  drivers/gpu/drm/verisilicon/vs_dc.h           |  32 +++++
  drivers/gpu/drm/verisilicon/vs_dc8200.c       | 121
++++++++++++++++++
  drivers/gpu/drm/verisilicon/vs_drm.c          |   5 +-
  drivers/gpu/drm/verisilicon/vs_drm.h          |   8 ++
  drivers/gpu/drm/verisilicon/vs_hwdb.c         |   4 +
  drivers/gpu/drm/verisilicon/vs_hwdb.h         |   6 +
  .../gpu/drm/verisilicon/vs_primary_plane.c    |  32 +----
  11 files changed, 220 insertions(+), 54 deletions(-)
  create mode 100644 drivers/gpu/drm/verisilicon/vs_dc8200.c
diff --git a/drivers/gpu/drm/verisilicon/Makefile
b/drivers/gpu/drm/verisilicon/Makefile
index 426f4bcaa834d..9d4cd16452fa1 100644
--- a/drivers/gpu/drm/verisilicon/Makefile
+++ b/drivers/gpu/drm/verisilicon/Makefile
@@ -1,6 +1,6 @@
  # SPDX-License-Identifier: GPL-2.0-only
  
-verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_drm.o
vs_hwdb.o \
+verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_dc8200.o
vs_drm.o vs_hwdb.o \
  	vs_plane.o vs_primary_plane.o vs_cursor_plane.o
  
  obj-$(CONFIG_DRM_VERISILICON_DC) += verisilicon-dc.o
diff --git a/drivers/gpu/drm/verisilicon/vs_bridge.c
b/drivers/gpu/drm/verisilicon/vs_bridge.c
index dc7c85b07fe32..3fbc8d57f8a1e 100644
--- a/drivers/gpu/drm/verisilicon/vs_bridge.c
+++ b/drivers/gpu/drm/verisilicon/vs_bridge.c
@@ -162,15 +162,8 @@ static void vs_bridge_enable_common(struct
vs_crtc *crtc,
  			VSDC_DISP_PANEL_CONFIG_DE_EN |
  			VSDC_DISP_PANEL_CONFIG_DAT_EN |
  			VSDC_DISP_PANEL_CONFIG_CLK_EN);
-	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
-			VSDC_DISP_PANEL_CONFIG_RUNNING);
-	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
-			  VSDC_DISP_PANEL_START_MULTI_DISP_SYNC);
-	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_START,
-			VSDC_DISP_PANEL_START_RUNNING(output));
-
-	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(crtc-
quoted
id),
-			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
+
+	dc->funcs->panel_enable_ex(dc, output);
  }
  
  static void vs_bridge_atomic_enable_dpi(struct drm_bridge *bridge,
@@ -228,14 +221,7 @@ static void vs_bridge_atomic_disable(struct
drm_bridge *bridge,
  	struct vs_dc *dc = crtc->dc;
  	unsigned int output = crtc->id;
  
-	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
-			  VSDC_DISP_PANEL_START_MULTI_DISP_SYNC |
-			  VSDC_DISP_PANEL_START_RUNNING(output));
-	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
-			  VSDC_DISP_PANEL_CONFIG_RUNNING);
-
-	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(crtc-
quoted
id),
-			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
+	dc->funcs->panel_disable_ex(dc, output);
  }
  
  static const struct drm_bridge_funcs vs_dpi_bridge_funcs = {
diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c
b/drivers/gpu/drm/verisilicon/vs_crtc.c
index c24c1588cbe69..2d4bf6af1f56e 100644
--- a/drivers/gpu/drm/verisilicon/vs_crtc.c
+++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
@@ -16,10 +16,33 @@
  #include "vs_crtc_regs.h"
  #include "vs_crtc.h"
  #include "vs_dc.h"
-#include "vs_dc_top_regs.h"
  #include "vs_drm.h"
  #include "vs_plane.h"
  
+static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
+				  struct drm_atomic_commit *state)
+{
+	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
+	struct vs_dc *dc = vcrtc->dc;
+	unsigned int output = vcrtc->id;
+
+	if (dc->funcs->crtc_begin)
+		dc->funcs->crtc_begin(dc, output);
+}
+
+static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
+				  struct drm_atomic_commit *state)
+{
+	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
+	struct vs_dc *dc = vcrtc->dc;
+	unsigned int output = vcrtc->id;
+
+	if (dc->funcs->crtc_flush)
+		dc->funcs->crtc_flush(dc, output);
+
+	drm_crtc_vblank_atomic_flush(crtc, state);
+}
+
  static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
  				   struct drm_atomic_commit *state)
  {
@@ -30,6 +53,9 @@ static void vs_crtc_atomic_disable(struct drm_crtc
*crtc,
  	drm_crtc_vblank_off(crtc);
  
  	clk_disable_unprepare(dc->pix_clk[output]);
+
+	if (dc->funcs->crtc_disable_ex)
+		dc->funcs->crtc_disable_ex(dc, output);
  }
  
  static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
@@ -42,6 +68,9 @@ static void vs_crtc_atomic_enable(struct drm_crtc
*crtc,
  	drm_WARN_ON(&dc->drm_dev->base,
  		    clk_prepare_enable(dc->pix_clk[output]));
  
+	if (dc->funcs->crtc_enable_ex)
+		dc->funcs->crtc_enable_ex(dc, output);
+
  	drm_crtc_vblank_on(crtc);
  }
  
@@ -119,7 +148,8 @@ static bool vs_crtc_mode_fixup(struct drm_crtc
*crtc,
  }
  
  static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
-	.atomic_flush	= drm_crtc_vblank_atomic_flush,
+	.atomic_begin	= vs_crtc_atomic_begin,
+	.atomic_flush	= vs_crtc_atomic_flush,
  	.atomic_enable	= vs_crtc_atomic_enable,
  	.atomic_disable	= vs_crtc_atomic_disable,
  	.mode_set_nofb	= vs_crtc_mode_set_nofb,
@@ -132,7 +162,7 @@ static int vs_crtc_enable_vblank(struct drm_crtc
*crtc)
  	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
  	struct vs_dc *dc = vcrtc->dc;
  
-	regmap_set_bits(dc->regs, VSDC_TOP_IRQ_EN,
VSDC_TOP_IRQ_VSYNC(vcrtc->id));
+	dc->funcs->enable_vblank(dc, vcrtc->id);
  
  	return 0;
  }
@@ -142,7 +172,7 @@ static void vs_crtc_disable_vblank(struct
drm_crtc *crtc)
  	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
  	struct vs_dc *dc = vcrtc->dc;
  
-	regmap_clear_bits(dc->regs, VSDC_TOP_IRQ_EN,
VSDC_TOP_IRQ_VSYNC(vcrtc->id));
+	dc->funcs->disable_vblank(dc, vcrtc->id);
  }
  
  static const struct drm_crtc_funcs vs_crtc_funcs = {
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c
b/drivers/gpu/drm/verisilicon/vs_dc.c
index dad9967bc10b8..9729b693d360e 100644
--- a/drivers/gpu/drm/verisilicon/vs_dc.c
+++ b/drivers/gpu/drm/verisilicon/vs_dc.c
@@ -8,9 +8,7 @@
  #include <linux/of.h>
  #include <linux/of_graph.h>
  
-#include "vs_crtc.h"
  #include "vs_dc.h"
-#include "vs_dc_top_regs.h"
  #include "vs_drm.h"
  #include "vs_hwdb.h"
  
@@ -33,7 +31,7 @@ static irqreturn_t vs_dc_irq_handler(int irq, void
*private)
  	struct vs_dc *dc = private;
  	u32 irqs;
  
-	regmap_read(dc->regs, VSDC_TOP_IRQ_ACK, &irqs);
+	irqs = dc->funcs->irq_ack(dc);
  
  	vs_drm_handle_irq(dc, irqs);
  
@@ -136,6 +134,8 @@ static int vs_dc_probe(struct platform_device
*pdev)
  	dev_info(dev, "Found DC%x rev %x customer %x\n", dc-
quoted
identity.model,
  		 dc->identity.revision, dc->identity.customer_id);
  
+	dc->funcs = &vs_dc8200_funcs;
+
  	if (port_count > dc->identity.display_count) {
  		dev_err(dev, "too many downstream ports than HW
capability\n");
  		ret = -EINVAL;
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h
b/drivers/gpu/drm/verisilicon/vs_dc.h
index ed1016f18758e..825f5dd6bf174 100644
--- a/drivers/gpu/drm/verisilicon/vs_dc.h
+++ b/drivers/gpu/drm/verisilicon/vs_dc.h
@@ -14,6 +14,7 @@
  #include <linux/reset.h>
  
  #include <drm/drm_device.h>
+#include <drm/drm_plane.h>
  
  #include "vs_hwdb.h"
  
@@ -22,6 +23,34 @@
  
  struct vs_drm_dev;
  struct vs_crtc;
+struct vs_dc;
+
+struct vs_dc_funcs {
+	/* Bridge: atomic_enable, atomic_disable */
+	void (*panel_enable_ex)(struct vs_dc *dc, unsigned int
output);
+	void (*panel_disable_ex)(struct vs_dc *dc, unsigned int
output);
+
+	/* CRTC: atomic_begin, atomic_flush */
+	void (*crtc_begin)(struct vs_dc *dc, unsigned int output);
+	void (*crtc_flush)(struct vs_dc *dc, unsigned int output);
+
+	/* CRTC: atomic_enable, atomic_disable */
+	void (*crtc_enable_ex)(struct vs_dc *dc, unsigned int
output);
+	void (*crtc_disable_ex)(struct vs_dc *dc, unsigned int
output);
+
+	/* CRTC: enable_vblank, disable_vblank */
+	void (*enable_vblank)(struct vs_dc *dc, unsigned int
output);
+	void (*disable_vblank)(struct vs_dc *dc, unsigned int
output);
+
+	/* Primary plane: atomic_enable, atomic_disable,
atomic_update */
+	void (*primary_plane_enable_ex)(struct vs_dc *dc, unsigned
int output);
+	void (*primary_plane_disable_ex)(struct vs_dc *dc, unsigned
int output);
+	void (*primary_plane_update_ex)(struct vs_dc *dc, unsigned
int output,
+					struct drm_plane_state
*state);
+
+	/* IRQ acknowledge */
+	u32 (*irq_ack)(struct vs_dc *dc);
+};
  
  struct vs_dc {
  	struct regmap *regs;
@@ -33,6 +62,9 @@ struct vs_dc {
  
  	struct vs_drm_dev *drm_dev;
  	struct vs_chip_identity identity;
+	const struct vs_dc_funcs *funcs;
  };
  
+extern const struct vs_dc_funcs vs_dc8200_funcs;
+
  #endif /* _VS_DC_H_ */
diff --git a/drivers/gpu/drm/verisilicon/vs_dc8200.c
b/drivers/gpu/drm/verisilicon/vs_dc8200.c
new file mode 100644
index 0000000000000..77752d0cb28b0
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc8200.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Icenowy Zheng <uwu@icenowy.me>
+ */
+
+#include <linux/regmap.h>
+
+#include <drm/drm_print.h>
+
+#include "vs_bridge_regs.h"
+#include "vs_dc.h"
+#include "vs_dc_top_regs.h"
+#include "vs_drm.h"
+#include "vs_plane.h"
+#include "vs_primary_plane_regs.h"
+
+static void vs_dc8200_panel_enable_ex(struct vs_dc *dc, unsigned int
output)
+{
+	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
+			VSDC_DISP_PANEL_CONFIG_RUNNING);
+	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
+			  VSDC_DISP_PANEL_START_MULTI_DISP_SYNC);
+	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_START,
+			VSDC_DISP_PANEL_START_RUNNING(output));
+
+	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(output),
+			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
+}
+
+static void vs_dc8200_panel_disable_ex(struct vs_dc *dc, unsigned
int output)
+{
+	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
+			  VSDC_DISP_PANEL_CONFIG_RUNNING);
+	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
+			  VSDC_DISP_PANEL_START_MULTI_DISP_SYNC |
+			  VSDC_DISP_PANEL_START_RUNNING(output));
+
+	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(output),
+			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
+}
+
+static void vs_dc8200_enable_vblank(struct vs_dc *dc, unsigned int
output)
+{
+	regmap_set_bits(dc->regs, VSDC_TOP_IRQ_EN,
+			VSDC_TOP_IRQ_VSYNC(output));
+}
+
+static void vs_dc8200_disable_vblank(struct vs_dc *dc, unsigned int
output)
+{
+	regmap_clear_bits(dc->regs, VSDC_TOP_IRQ_EN,
+			  VSDC_TOP_IRQ_VSYNC(output));
+}
+
+static void vs_dc8200_plane_commit(struct vs_dc *dc, unsigned int
output)
+{
+	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
+			VSDC_FB_CONFIG_EX_COMMIT);
+}
+
+static void vs_dc8200_primary_plane_enable_ex(struct vs_dc *dc,
unsigned int output)
+{
+	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
+			VSDC_FB_CONFIG_EX_FB_EN);
+	regmap_update_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
+			   VSDC_FB_CONFIG_EX_DISPLAY_ID_MASK,
+			   VSDC_FB_CONFIG_EX_DISPLAY_ID(output));
+
+	vs_dc8200_plane_commit(dc, output);
+}
+
+static void vs_dc8200_primary_plane_disable_ex(struct vs_dc *dc,
unsigned int output)
+{
+	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
+			VSDC_FB_CONFIG_EX_FB_EN);
I think I messed up the DC8200 codepath here -- the FB_EN bit should be
cleared instead of set.

Would you mind I fix this when applying, or would you send a new
revision fixing this?

In the latter case, should I send a fix to the current code before?

Thanks,
Icenowy
Please go ahead and fix it when applying -- I'm happy with that.
Thanks!
quoted
+
+	vs_dc8200_plane_commit(dc, output);
+}
+
+static void vs_dc8200_primary_plane_update_ex(struct vs_dc *dc,
unsigned int output,
+				       struct drm_plane_state
*state)
+{
+	regmap_write(dc->regs, VSDC_FB_TOP_LEFT(output),
+		     VSDC_MAKE_PLANE_POS(state->crtc_x, state-
quoted
crtc_y));
+	regmap_write(dc->regs, VSDC_FB_BOTTOM_RIGHT(output),
+		     VSDC_MAKE_PLANE_POS(state->crtc_x + state-
quoted
crtc_w,
+					 state->crtc_y + state-
quoted
crtc_h));
+	regmap_write(dc->regs, VSDC_FB_BLEND_CONFIG(output),
+		     VSDC_FB_BLEND_CONFIG_BLEND_DISABLE);
+
+	vs_dc8200_plane_commit(dc, output);
+}
+
+static u32 vs_dc8200_irq_ack(struct vs_dc *dc)
+{
+	u32 hw_irqs, unified = 0, known = 0;
+	unsigned int i;
+
+	regmap_read(dc->regs, VSDC_TOP_IRQ_ACK, &hw_irqs);
+
+	for (i = 0; i < VSDC_MAX_OUTPUTS; i++) {
+		known |= VSDC_TOP_IRQ_VSYNC(i);
+		if (hw_irqs & VSDC_TOP_IRQ_VSYNC(i))
+			unified |= VSDC_IRQ_VSYNC(i);
+	}
+
+	drm_WARN_ONCE(&dc->drm_dev->base, hw_irqs & ~known,
+		      "Unknown hardware IRQ bits: %#x\n", hw_irqs &
~known);
+
+	return unified;
+}
+
+const struct vs_dc_funcs vs_dc8200_funcs = {
+	.panel_enable_ex		= vs_dc8200_panel_enable_ex,
+	.panel_disable_ex		=
vs_dc8200_panel_disable_ex,
+	.enable_vblank			= vs_dc8200_enable_vblank,
+	.disable_vblank			=
vs_dc8200_disable_vblank,
+	.primary_plane_enable_ex	=
vs_dc8200_primary_plane_enable_ex,
+	.primary_plane_disable_ex	=
vs_dc8200_primary_plane_disable_ex,
+	.primary_plane_update_ex	=
vs_dc8200_primary_plane_update_ex,
+	.irq_ack			= vs_dc8200_irq_ack,
+};
diff --git a/drivers/gpu/drm/verisilicon/vs_drm.c
b/drivers/gpu/drm/verisilicon/vs_drm.c
index fd259d53f49f1..24e9d0b008f31 100644
--- a/drivers/gpu/drm/verisilicon/vs_drm.c
+++ b/drivers/gpu/drm/verisilicon/vs_drm.c
@@ -25,7 +25,6 @@
  #include "vs_bridge.h"
  #include "vs_crtc.h"
  #include "vs_dc.h"
-#include "vs_dc_top_regs.h"
  #include "vs_drm.h"
  
  #define DRIVER_NAME	"verisilicon"
@@ -168,8 +167,8 @@ void vs_drm_handle_irq(struct vs_dc *dc, u32
irqs)
  	unsigned int i;
  
  	for (i = 0; i < dc->identity.display_count; i++) {
-		if (irqs & VSDC_TOP_IRQ_VSYNC(i)) {
-			irqs &= ~VSDC_TOP_IRQ_VSYNC(i);
+		if (irqs & VSDC_IRQ_VSYNC(i)) {
+			irqs &= ~VSDC_IRQ_VSYNC(i);
  			if (dc->drm_dev->crtcs[i])
  				drm_crtc_handle_vblank(&dc->drm_dev-
quoted
crtcs[i]->base);
  		}
diff --git a/drivers/gpu/drm/verisilicon/vs_drm.h
b/drivers/gpu/drm/verisilicon/vs_drm.h
index 606338206a427..6a89c20879df7 100644
--- a/drivers/gpu/drm/verisilicon/vs_drm.h
+++ b/drivers/gpu/drm/verisilicon/vs_drm.h
@@ -6,6 +6,7 @@
  #ifndef _VS_DRM_H_
  #define _VS_DRM_H_
  
+#include <linux/bits.h>
  #include <linux/platform_device.h>
  #include <linux/types.h>
  
@@ -13,6 +14,13 @@
  
  struct vs_dc;
  
+/*
+ * DC variants use different interrupt registers with diverging bit
+ * assignments; each irq_ack() implementation must translate its
+ * hardware-specific bits into these definitions.
+ */
+#define VSDC_IRQ_VSYNC(n)	BIT(n)
+
  struct vs_drm_dev {
  	struct drm_device base;
  
diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.c
b/drivers/gpu/drm/verisilicon/vs_hwdb.c
index 2a0f7c59afa3a..91524d16f7785 100644
--- a/drivers/gpu/drm/verisilicon/vs_hwdb.c
+++ b/drivers/gpu/drm/verisilicon/vs_hwdb.c
@@ -94,6 +94,7 @@ static struct vs_chip_identity vs_chip_identities[]
= {
  		.revision = 0x5720,
  		.customer_id = ~0U,
  
+		.generation = VSDC_GEN_DC8200,
  		.display_count = 2,
  		.max_cursor_size = 64,
  		.formats = &vs_formats_no_yuv444,
@@ -103,6 +104,7 @@ static struct vs_chip_identity
vs_chip_identities[] = {
  		.revision = 0x5721,
  		.customer_id = 0x30B,
  
+		.generation = VSDC_GEN_DC8200,
  		.display_count = 2,
  		.max_cursor_size = 64,
  		.formats = &vs_formats_no_yuv444,
@@ -112,6 +114,7 @@ static struct vs_chip_identity
vs_chip_identities[] = {
  		.revision = 0x5720,
  		.customer_id = 0x310,
  
+		.generation = VSDC_GEN_DC8200,
  		.display_count = 2,
  		.max_cursor_size = 64,
  		.formats = &vs_formats_with_yuv444,
@@ -121,6 +124,7 @@ static struct vs_chip_identity
vs_chip_identities[] = {
  		.revision = 0x5720,
  		.customer_id = 0x311,
  
+		.generation = VSDC_GEN_DC8200,
  		.display_count = 2,
  		.max_cursor_size = 64,
  		.formats = &vs_formats_no_yuv444,
diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.h
b/drivers/gpu/drm/verisilicon/vs_hwdb.h
index 2065ecb730437..a15c8b5656044 100644
--- a/drivers/gpu/drm/verisilicon/vs_hwdb.h
+++ b/drivers/gpu/drm/verisilicon/vs_hwdb.h
@@ -9,6 +9,11 @@
  #include <linux/regmap.h>
  #include <linux/types.h>
  
+enum vs_dc_generation {
+	VSDC_GEN_DC8000,
+	VSDC_GEN_DC8200,
+};
+
  struct vs_formats {
  	const u32 *array;
  	unsigned int num;
@@ -19,6 +24,7 @@ struct vs_chip_identity {
  	u32 revision;
  	u32 customer_id;
  
+	enum vs_dc_generation generation;
  	u32 display_count;
  	/*
  	 * The hardware only supports square cursor planes, so this
field
diff --git a/drivers/gpu/drm/verisilicon/vs_primary_plane.c
b/drivers/gpu/drm/verisilicon/vs_primary_plane.c
index 2750016a7f2c3..85ba2068172db 100644
--- a/drivers/gpu/drm/verisilicon/vs_primary_plane.c
+++ b/drivers/gpu/drm/verisilicon/vs_primary_plane.c
@@ -54,12 +54,6 @@ static int vs_primary_plane_atomic_check(struct
drm_plane *plane,
  	return 0;
  }
  
-static void vs_primary_plane_commit(struct vs_dc *dc, unsigned int
output)
-{
-	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
-			VSDC_FB_CONFIG_EX_COMMIT);
-}
-
  static void vs_primary_plane_atomic_enable(struct drm_plane *plane,
  					   struct drm_atomic_commit
*atomic_state)
  {
@@ -70,13 +64,8 @@ static void vs_primary_plane_atomic_enable(struct
drm_plane *plane,
  	unsigned int output = vcrtc->id;
  	struct vs_dc *dc = vcrtc->dc;
  
-	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
-			VSDC_FB_CONFIG_EX_FB_EN);
-	regmap_update_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
-			   VSDC_FB_CONFIG_EX_DISPLAY_ID_MASK,
-			   VSDC_FB_CONFIG_EX_DISPLAY_ID(output));
-
-	vs_primary_plane_commit(dc, output);
+	if (dc->funcs->primary_plane_enable_ex)
+		dc->funcs->primary_plane_enable_ex(dc, output);
  }
  
  static void vs_primary_plane_atomic_disable(struct drm_plane *plane,
@@ -89,10 +78,8 @@ static void vs_primary_plane_atomic_disable(struct
drm_plane *plane,
  	unsigned int output = vcrtc->id;
  	struct vs_dc *dc = vcrtc->dc;
  
-	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
-			VSDC_FB_CONFIG_EX_FB_EN);
-
-	vs_primary_plane_commit(dc, output);
+	if (dc->funcs->primary_plane_disable_ex)
+		dc->funcs->primary_plane_disable_ex(dc, output);
  }
  
  static void vs_primary_plane_atomic_update(struct drm_plane *plane,
@@ -138,18 +125,11 @@ static void
vs_primary_plane_atomic_update(struct drm_plane *plane,
  	regmap_write(dc->regs, VSDC_FB_STRIDE(output),
  		     fb->pitches[0]);
  
-	regmap_write(dc->regs, VSDC_FB_TOP_LEFT(output),
-		     VSDC_MAKE_PLANE_POS(state->crtc_x, state-
quoted
crtc_y));
-	regmap_write(dc->regs, VSDC_FB_BOTTOM_RIGHT(output),
-		     VSDC_MAKE_PLANE_POS(state->crtc_x + state-
quoted
crtc_w,
-					 state->crtc_y + state-
quoted
crtc_h));
  	regmap_write(dc->regs, VSDC_FB_SIZE(output),
  		     VSDC_MAKE_PLANE_SIZE(state->crtc_w, state-
quoted
crtc_h));
  
-	regmap_write(dc->regs, VSDC_FB_BLEND_CONFIG(output),
-		     VSDC_FB_BLEND_CONFIG_BLEND_DISABLE);
-
-	vs_primary_plane_commit(dc, output);
+	if (dc->funcs->primary_plane_update_ex)
+		dc->funcs->primary_plane_update_ex(dc, output,
state);
  }
  
  static const struct drm_plane_helper_funcs
vs_primary_plane_helper_funcs = {

Re: [PATCH v6 3/6] drm/verisilicon: introduce per-variant hardware ops table

From: Icenowy Zheng <zhengxingda@iscas.ac.cn>
Date: 2026-09-16 09:39:29

在 2026-09-15二的 10:06 +0800,Joey Lu写道:
Icenowy Zheng 於 2026/9/14 下午 11:00 寫道:
quoted
在 2026-09-08二的 17:28 +0800,Joey Lu写道:
quoted
The DC8200 and DCUltraLite share a broadly similar register
layout
but
differ in how the bridge, CRTC, primary plane and IRQ paths are
driven.
Introduce a vs_dc_funcs vtable so each variant can supply its own
implementation without scattering conditionals across multiple
files.

Add a generation field to struct vs_chip_identity to distinguish
variants.
Extract the DC8200-specific hardware ops into vs_dc8200.c and add
unified
IRQ bit definitions so implementations can translate hardware-
specific
bits to a common set.  Update the shared code to dispatch through
dc->funcs.

No behaviour change for existing DC8200 platforms.

Signed-off-by: Joey Lu <redacted>
Reviewed-by: Icenowy Zheng <zhengxingda@iscas.ac.cn>
---
  drivers/gpu/drm/verisilicon/Makefile          |   2 +-
  drivers/gpu/drm/verisilicon/vs_bridge.c       |  20 +--
  drivers/gpu/drm/verisilicon/vs_crtc.c         |  38 +++++-
  drivers/gpu/drm/verisilicon/vs_dc.c           |   6 +-
  drivers/gpu/drm/verisilicon/vs_dc.h           |  32 +++++
  drivers/gpu/drm/verisilicon/vs_dc8200.c       | 121
++++++++++++++++++
  drivers/gpu/drm/verisilicon/vs_drm.c          |   5 +-
  drivers/gpu/drm/verisilicon/vs_drm.h          |   8 ++
  drivers/gpu/drm/verisilicon/vs_hwdb.c         |   4 +
  drivers/gpu/drm/verisilicon/vs_hwdb.h         |   6 +
  .../gpu/drm/verisilicon/vs_primary_plane.c    |  32 +----
  11 files changed, 220 insertions(+), 54 deletions(-)
  create mode 100644 drivers/gpu/drm/verisilicon/vs_dc8200.c
diff --git a/drivers/gpu/drm/verisilicon/Makefile
b/drivers/gpu/drm/verisilicon/Makefile
index 426f4bcaa834d..9d4cd16452fa1 100644
--- a/drivers/gpu/drm/verisilicon/Makefile
+++ b/drivers/gpu/drm/verisilicon/Makefile
@@ -1,6 +1,6 @@
  # SPDX-License-Identifier: GPL-2.0-only
  
-verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_drm.o
vs_hwdb.o \
+verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_dc8200.o
vs_drm.o vs_hwdb.o \
  	vs_plane.o vs_primary_plane.o vs_cursor_plane.o
  
  obj-$(CONFIG_DRM_VERISILICON_DC) += verisilicon-dc.o
diff --git a/drivers/gpu/drm/verisilicon/vs_bridge.c
b/drivers/gpu/drm/verisilicon/vs_bridge.c
index dc7c85b07fe32..3fbc8d57f8a1e 100644
--- a/drivers/gpu/drm/verisilicon/vs_bridge.c
+++ b/drivers/gpu/drm/verisilicon/vs_bridge.c
@@ -162,15 +162,8 @@ static void vs_bridge_enable_common(struct
vs_crtc *crtc,
  			VSDC_DISP_PANEL_CONFIG_DE_EN |
  			VSDC_DISP_PANEL_CONFIG_DAT_EN |
  			VSDC_DISP_PANEL_CONFIG_CLK_EN);
-	regmap_set_bits(dc->regs,
VSDC_DISP_PANEL_CONFIG(output),
-			VSDC_DISP_PANEL_CONFIG_RUNNING);
-	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
-			 
VSDC_DISP_PANEL_START_MULTI_DISP_SYNC);
-	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_START,
-			VSDC_DISP_PANEL_START_RUNNING(output));
-
-	regmap_set_bits(dc->regs,
VSDC_DISP_PANEL_CONFIG_EX(crtc-
quoted
id),
-			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
+
+	dc->funcs->panel_enable_ex(dc, output);
  }
  
  static void vs_bridge_atomic_enable_dpi(struct drm_bridge
*bridge,
@@ -228,14 +221,7 @@ static void vs_bridge_atomic_disable(struct
drm_bridge *bridge,
  	struct vs_dc *dc = crtc->dc;
  	unsigned int output = crtc->id;
  
-	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
-			  VSDC_DISP_PANEL_START_MULTI_DISP_SYNC
|
-			 
VSDC_DISP_PANEL_START_RUNNING(output));
-	regmap_clear_bits(dc->regs,
VSDC_DISP_PANEL_CONFIG(output),
-			  VSDC_DISP_PANEL_CONFIG_RUNNING);
-
-	regmap_set_bits(dc->regs,
VSDC_DISP_PANEL_CONFIG_EX(crtc-
quoted
id),
-			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
+	dc->funcs->panel_disable_ex(dc, output);
  }
  
  static const struct drm_bridge_funcs vs_dpi_bridge_funcs = {
diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c
b/drivers/gpu/drm/verisilicon/vs_crtc.c
index c24c1588cbe69..2d4bf6af1f56e 100644
--- a/drivers/gpu/drm/verisilicon/vs_crtc.c
+++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
@@ -16,10 +16,33 @@
  #include "vs_crtc_regs.h"
  #include "vs_crtc.h"
  #include "vs_dc.h"
-#include "vs_dc_top_regs.h"
  #include "vs_drm.h"
  #include "vs_plane.h"
  
+static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
+				  struct drm_atomic_commit
*state)
+{
+	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
+	struct vs_dc *dc = vcrtc->dc;
+	unsigned int output = vcrtc->id;
+
+	if (dc->funcs->crtc_begin)
+		dc->funcs->crtc_begin(dc, output);
+}
+
+static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
+				  struct drm_atomic_commit
*state)
+{
+	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
+	struct vs_dc *dc = vcrtc->dc;
+	unsigned int output = vcrtc->id;
+
+	if (dc->funcs->crtc_flush)
+		dc->funcs->crtc_flush(dc, output);
+
+	drm_crtc_vblank_atomic_flush(crtc, state);
+}
+
  static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
  				   struct drm_atomic_commit
*state)
  {
@@ -30,6 +53,9 @@ static void vs_crtc_atomic_disable(struct
drm_crtc
*crtc,
  	drm_crtc_vblank_off(crtc);
  
  	clk_disable_unprepare(dc->pix_clk[output]);
+
+	if (dc->funcs->crtc_disable_ex)
+		dc->funcs->crtc_disable_ex(dc, output);
  }
  
  static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
@@ -42,6 +68,9 @@ static void vs_crtc_atomic_enable(struct
drm_crtc
*crtc,
  	drm_WARN_ON(&dc->drm_dev->base,
  		    clk_prepare_enable(dc->pix_clk[output]));
  
+	if (dc->funcs->crtc_enable_ex)
+		dc->funcs->crtc_enable_ex(dc, output);
+
  	drm_crtc_vblank_on(crtc);
  }
  
@@ -119,7 +148,8 @@ static bool vs_crtc_mode_fixup(struct
drm_crtc
*crtc,
  }
  
  static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs
= {
-	.atomic_flush	= drm_crtc_vblank_atomic_flush,
+	.atomic_begin	= vs_crtc_atomic_begin,
+	.atomic_flush	= vs_crtc_atomic_flush,
  	.atomic_enable	= vs_crtc_atomic_enable,
  	.atomic_disable	= vs_crtc_atomic_disable,
  	.mode_set_nofb	= vs_crtc_mode_set_nofb,
@@ -132,7 +162,7 @@ static int vs_crtc_enable_vblank(struct
drm_crtc
*crtc)
  	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
  	struct vs_dc *dc = vcrtc->dc;
  
-	regmap_set_bits(dc->regs, VSDC_TOP_IRQ_EN,
VSDC_TOP_IRQ_VSYNC(vcrtc->id));
+	dc->funcs->enable_vblank(dc, vcrtc->id);
  
  	return 0;
  }
@@ -142,7 +172,7 @@ static void vs_crtc_disable_vblank(struct
drm_crtc *crtc)
  	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
  	struct vs_dc *dc = vcrtc->dc;
  
-	regmap_clear_bits(dc->regs, VSDC_TOP_IRQ_EN,
VSDC_TOP_IRQ_VSYNC(vcrtc->id));
+	dc->funcs->disable_vblank(dc, vcrtc->id);
  }
  
  static const struct drm_crtc_funcs vs_crtc_funcs = {
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c
b/drivers/gpu/drm/verisilicon/vs_dc.c
index dad9967bc10b8..9729b693d360e 100644
--- a/drivers/gpu/drm/verisilicon/vs_dc.c
+++ b/drivers/gpu/drm/verisilicon/vs_dc.c
@@ -8,9 +8,7 @@
  #include <linux/of.h>
  #include <linux/of_graph.h>
  
-#include "vs_crtc.h"
  #include "vs_dc.h"
-#include "vs_dc_top_regs.h"
  #include "vs_drm.h"
  #include "vs_hwdb.h"
  
@@ -33,7 +31,7 @@ static irqreturn_t vs_dc_irq_handler(int irq,
void
*private)
  	struct vs_dc *dc = private;
  	u32 irqs;
  
-	regmap_read(dc->regs, VSDC_TOP_IRQ_ACK, &irqs);
+	irqs = dc->funcs->irq_ack(dc);
  
  	vs_drm_handle_irq(dc, irqs);
  
@@ -136,6 +134,8 @@ static int vs_dc_probe(struct platform_device
*pdev)
  	dev_info(dev, "Found DC%x rev %x customer %x\n", dc-
quoted
identity.model,
  		 dc->identity.revision, dc-
quoted
identity.customer_id);
  
+	dc->funcs = &vs_dc8200_funcs;
+
  	if (port_count > dc->identity.display_count) {
  		dev_err(dev, "too many downstream ports than HW
capability\n");
  		ret = -EINVAL;
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h
b/drivers/gpu/drm/verisilicon/vs_dc.h
index ed1016f18758e..825f5dd6bf174 100644
--- a/drivers/gpu/drm/verisilicon/vs_dc.h
+++ b/drivers/gpu/drm/verisilicon/vs_dc.h
@@ -14,6 +14,7 @@
  #include <linux/reset.h>
  
  #include <drm/drm_device.h>
+#include <drm/drm_plane.h>
  
  #include "vs_hwdb.h"
  
@@ -22,6 +23,34 @@
  
  struct vs_drm_dev;
  struct vs_crtc;
+struct vs_dc;
+
+struct vs_dc_funcs {
+	/* Bridge: atomic_enable, atomic_disable */
+	void (*panel_enable_ex)(struct vs_dc *dc, unsigned int
output);
+	void (*panel_disable_ex)(struct vs_dc *dc, unsigned int
output);
+
+	/* CRTC: atomic_begin, atomic_flush */
+	void (*crtc_begin)(struct vs_dc *dc, unsigned int
output);
+	void (*crtc_flush)(struct vs_dc *dc, unsigned int
output);
+
+	/* CRTC: atomic_enable, atomic_disable */
+	void (*crtc_enable_ex)(struct vs_dc *dc, unsigned int
output);
+	void (*crtc_disable_ex)(struct vs_dc *dc, unsigned int
output);
+
+	/* CRTC: enable_vblank, disable_vblank */
+	void (*enable_vblank)(struct vs_dc *dc, unsigned int
output);
+	void (*disable_vblank)(struct vs_dc *dc, unsigned int
output);
+
+	/* Primary plane: atomic_enable, atomic_disable,
atomic_update */
+	void (*primary_plane_enable_ex)(struct vs_dc *dc,
unsigned
int output);
+	void (*primary_plane_disable_ex)(struct vs_dc *dc,
unsigned
int output);
+	void (*primary_plane_update_ex)(struct vs_dc *dc,
unsigned
int output,
+					struct drm_plane_state
*state);
+
+	/* IRQ acknowledge */
+	u32 (*irq_ack)(struct vs_dc *dc);
+};
  
  struct vs_dc {
  	struct regmap *regs;
@@ -33,6 +62,9 @@ struct vs_dc {
  
  	struct vs_drm_dev *drm_dev;
  	struct vs_chip_identity identity;
+	const struct vs_dc_funcs *funcs;
  };
  
+extern const struct vs_dc_funcs vs_dc8200_funcs;
+
  #endif /* _VS_DC_H_ */
diff --git a/drivers/gpu/drm/verisilicon/vs_dc8200.c
b/drivers/gpu/drm/verisilicon/vs_dc8200.c
new file mode 100644
index 0000000000000..77752d0cb28b0
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc8200.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Icenowy Zheng <uwu@icenowy.me>
+ */
+
+#include <linux/regmap.h>
+
+#include <drm/drm_print.h>
+
+#include "vs_bridge_regs.h"
+#include "vs_dc.h"
+#include "vs_dc_top_regs.h"
+#include "vs_drm.h"
+#include "vs_plane.h"
+#include "vs_primary_plane_regs.h"
+
+static void vs_dc8200_panel_enable_ex(struct vs_dc *dc, unsigned
int
output)
+{
+	regmap_set_bits(dc->regs,
VSDC_DISP_PANEL_CONFIG(output),
+			VSDC_DISP_PANEL_CONFIG_RUNNING);
+	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
+			 
VSDC_DISP_PANEL_START_MULTI_DISP_SYNC);
+	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_START,
+			VSDC_DISP_PANEL_START_RUNNING(output));
+
+	regmap_set_bits(dc->regs,
VSDC_DISP_PANEL_CONFIG_EX(output),
+			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
+}
+
+static void vs_dc8200_panel_disable_ex(struct vs_dc *dc,
unsigned
int output)
+{
+	regmap_clear_bits(dc->regs,
VSDC_DISP_PANEL_CONFIG(output),
+			  VSDC_DISP_PANEL_CONFIG_RUNNING);
+	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
+			  VSDC_DISP_PANEL_START_MULTI_DISP_SYNC
|
+			 
VSDC_DISP_PANEL_START_RUNNING(output));
+
+	regmap_set_bits(dc->regs,
VSDC_DISP_PANEL_CONFIG_EX(output),
+			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
+}
+
+static void vs_dc8200_enable_vblank(struct vs_dc *dc, unsigned
int
output)
+{
+	regmap_set_bits(dc->regs, VSDC_TOP_IRQ_EN,
+			VSDC_TOP_IRQ_VSYNC(output));
+}
+
+static void vs_dc8200_disable_vblank(struct vs_dc *dc, unsigned
int
output)
+{
+	regmap_clear_bits(dc->regs, VSDC_TOP_IRQ_EN,
+			  VSDC_TOP_IRQ_VSYNC(output));
+}
+
+static void vs_dc8200_plane_commit(struct vs_dc *dc, unsigned
int
output)
+{
+	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
+			VSDC_FB_CONFIG_EX_COMMIT);
+}
+
+static void vs_dc8200_primary_plane_enable_ex(struct vs_dc *dc,
unsigned int output)
+{
+	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
+			VSDC_FB_CONFIG_EX_FB_EN);
+	regmap_update_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
+			   VSDC_FB_CONFIG_EX_DISPLAY_ID_MASK,
+			  
VSDC_FB_CONFIG_EX_DISPLAY_ID(output));
+
+	vs_dc8200_plane_commit(dc, output);
+}
+
+static void vs_dc8200_primary_plane_disable_ex(struct vs_dc *dc,
unsigned int output)
+{
+	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
+			VSDC_FB_CONFIG_EX_FB_EN);
I think I messed up the DC8200 codepath here -- the FB_EN bit
should be
cleared instead of set.

Would you mind I fix this when applying, or would you send a new
revision fixing this?

In the latter case, should I send a fix to the current code before?

Thanks,
Icenowy
Please go ahead and fix it when applying -- I'm happy with that.
Thanks!
I'm going to apply [1] to the fixes branch, which seems to be
introducing merge conflict with this patch.

In addition, checkpatch.pl reports some parenthesis alignment issues on
this patch.

Maybe it's worth sending a v7 resolving all these problems on this
patch (DC8200 FB_EN clear_bits, parenthesis alignment, rebase on [1]).

Thanks,
Icenowy

[1]
https://lore.kernel.org/all/20260910095000.3505878-1-zhengxingda@iscas.ac.cn/
quoted
quoted
+
+	vs_dc8200_plane_commit(dc, output);
+}
+
+static void vs_dc8200_primary_plane_update_ex(struct vs_dc *dc,
unsigned int output,
+				       struct drm_plane_state
*state)
+{
+	regmap_write(dc->regs, VSDC_FB_TOP_LEFT(output),
+		     VSDC_MAKE_PLANE_POS(state->crtc_x, state-
quoted
crtc_y));
+	regmap_write(dc->regs, VSDC_FB_BOTTOM_RIGHT(output),
+		     VSDC_MAKE_PLANE_POS(state->crtc_x + state-
quoted
crtc_w,
+					 state->crtc_y + state-
quoted
crtc_h));
+	regmap_write(dc->regs, VSDC_FB_BLEND_CONFIG(output),
+		     VSDC_FB_BLEND_CONFIG_BLEND_DISABLE);
+
+	vs_dc8200_plane_commit(dc, output);
+}
+
+static u32 vs_dc8200_irq_ack(struct vs_dc *dc)
+{
+	u32 hw_irqs, unified = 0, known = 0;
+	unsigned int i;
+
+	regmap_read(dc->regs, VSDC_TOP_IRQ_ACK, &hw_irqs);
+
+	for (i = 0; i < VSDC_MAX_OUTPUTS; i++) {
+		known |= VSDC_TOP_IRQ_VSYNC(i);
+		if (hw_irqs & VSDC_TOP_IRQ_VSYNC(i))
+			unified |= VSDC_IRQ_VSYNC(i);
+	}
+
+	drm_WARN_ONCE(&dc->drm_dev->base, hw_irqs & ~known,
+		      "Unknown hardware IRQ bits: %#x\n",
hw_irqs &
~known);
+
+	return unified;
+}
+
+const struct vs_dc_funcs vs_dc8200_funcs = {
+	.panel_enable_ex		=
vs_dc8200_panel_enable_ex,
+	.panel_disable_ex		=
vs_dc8200_panel_disable_ex,
+	.enable_vblank			=
vs_dc8200_enable_vblank,
+	.disable_vblank			=
vs_dc8200_disable_vblank,
+	.primary_plane_enable_ex	=
vs_dc8200_primary_plane_enable_ex,
+	.primary_plane_disable_ex	=
vs_dc8200_primary_plane_disable_ex,
+	.primary_plane_update_ex	=
vs_dc8200_primary_plane_update_ex,
+	.irq_ack			= vs_dc8200_irq_ack,
+};
diff --git a/drivers/gpu/drm/verisilicon/vs_drm.c
b/drivers/gpu/drm/verisilicon/vs_drm.c
index fd259d53f49f1..24e9d0b008f31 100644
--- a/drivers/gpu/drm/verisilicon/vs_drm.c
+++ b/drivers/gpu/drm/verisilicon/vs_drm.c
@@ -25,7 +25,6 @@
  #include "vs_bridge.h"
  #include "vs_crtc.h"
  #include "vs_dc.h"
-#include "vs_dc_top_regs.h"
  #include "vs_drm.h"
  
  #define DRIVER_NAME	"verisilicon"
@@ -168,8 +167,8 @@ void vs_drm_handle_irq(struct vs_dc *dc, u32
irqs)
  	unsigned int i;
  
  	for (i = 0; i < dc->identity.display_count; i++) {
-		if (irqs & VSDC_TOP_IRQ_VSYNC(i)) {
-			irqs &= ~VSDC_TOP_IRQ_VSYNC(i);
+		if (irqs & VSDC_IRQ_VSYNC(i)) {
+			irqs &= ~VSDC_IRQ_VSYNC(i);
  			if (dc->drm_dev->crtcs[i])
  				drm_crtc_handle_vblank(&dc-
quoted
drm_dev-
crtcs[i]->base);
  		}
diff --git a/drivers/gpu/drm/verisilicon/vs_drm.h
b/drivers/gpu/drm/verisilicon/vs_drm.h
index 606338206a427..6a89c20879df7 100644
--- a/drivers/gpu/drm/verisilicon/vs_drm.h
+++ b/drivers/gpu/drm/verisilicon/vs_drm.h
@@ -6,6 +6,7 @@
  #ifndef _VS_DRM_H_
  #define _VS_DRM_H_
  
+#include <linux/bits.h>
  #include <linux/platform_device.h>
  #include <linux/types.h>
  
@@ -13,6 +14,13 @@
  
  struct vs_dc;
  
+/*
+ * DC variants use different interrupt registers with diverging
bit
+ * assignments; each irq_ack() implementation must translate its
+ * hardware-specific bits into these definitions.
+ */
+#define VSDC_IRQ_VSYNC(n)	BIT(n)
+
  struct vs_drm_dev {
  	struct drm_device base;
  
diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.c
b/drivers/gpu/drm/verisilicon/vs_hwdb.c
index 2a0f7c59afa3a..91524d16f7785 100644
--- a/drivers/gpu/drm/verisilicon/vs_hwdb.c
+++ b/drivers/gpu/drm/verisilicon/vs_hwdb.c
@@ -94,6 +94,7 @@ static struct vs_chip_identity
vs_chip_identities[]
= {
  		.revision = 0x5720,
  		.customer_id = ~0U,
  
+		.generation = VSDC_GEN_DC8200,
  		.display_count = 2,
  		.max_cursor_size = 64,
  		.formats = &vs_formats_no_yuv444,
@@ -103,6 +104,7 @@ static struct vs_chip_identity
vs_chip_identities[] = {
  		.revision = 0x5721,
  		.customer_id = 0x30B,
  
+		.generation = VSDC_GEN_DC8200,
  		.display_count = 2,
  		.max_cursor_size = 64,
  		.formats = &vs_formats_no_yuv444,
@@ -112,6 +114,7 @@ static struct vs_chip_identity
vs_chip_identities[] = {
  		.revision = 0x5720,
  		.customer_id = 0x310,
  
+		.generation = VSDC_GEN_DC8200,
  		.display_count = 2,
  		.max_cursor_size = 64,
  		.formats = &vs_formats_with_yuv444,
@@ -121,6 +124,7 @@ static struct vs_chip_identity
vs_chip_identities[] = {
  		.revision = 0x5720,
  		.customer_id = 0x311,
  
+		.generation = VSDC_GEN_DC8200,
  		.display_count = 2,
  		.max_cursor_size = 64,
  		.formats = &vs_formats_no_yuv444,
diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.h
b/drivers/gpu/drm/verisilicon/vs_hwdb.h
index 2065ecb730437..a15c8b5656044 100644
--- a/drivers/gpu/drm/verisilicon/vs_hwdb.h
+++ b/drivers/gpu/drm/verisilicon/vs_hwdb.h
@@ -9,6 +9,11 @@
  #include <linux/regmap.h>
  #include <linux/types.h>
  
+enum vs_dc_generation {
+	VSDC_GEN_DC8000,
+	VSDC_GEN_DC8200,
+};
+
  struct vs_formats {
  	const u32 *array;
  	unsigned int num;
@@ -19,6 +24,7 @@ struct vs_chip_identity {
  	u32 revision;
  	u32 customer_id;
  
+	enum vs_dc_generation generation;
  	u32 display_count;
  	/*
  	 * The hardware only supports square cursor planes, so
this
field
diff --git a/drivers/gpu/drm/verisilicon/vs_primary_plane.c
b/drivers/gpu/drm/verisilicon/vs_primary_plane.c
index 2750016a7f2c3..85ba2068172db 100644
--- a/drivers/gpu/drm/verisilicon/vs_primary_plane.c
+++ b/drivers/gpu/drm/verisilicon/vs_primary_plane.c
@@ -54,12 +54,6 @@ static int
vs_primary_plane_atomic_check(struct
drm_plane *plane,
  	return 0;
  }
  
-static void vs_primary_plane_commit(struct vs_dc *dc, unsigned
int
output)
-{
-	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
-			VSDC_FB_CONFIG_EX_COMMIT);
-}
-
  static void vs_primary_plane_atomic_enable(struct drm_plane
*plane,
  					   struct
drm_atomic_commit
*atomic_state)
  {
@@ -70,13 +64,8 @@ static void
vs_primary_plane_atomic_enable(struct
drm_plane *plane,
  	unsigned int output = vcrtc->id;
  	struct vs_dc *dc = vcrtc->dc;
  
-	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
-			VSDC_FB_CONFIG_EX_FB_EN);
-	regmap_update_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
-			   VSDC_FB_CONFIG_EX_DISPLAY_ID_MASK,
-			  
VSDC_FB_CONFIG_EX_DISPLAY_ID(output));
-
-	vs_primary_plane_commit(dc, output);
+	if (dc->funcs->primary_plane_enable_ex)
+		dc->funcs->primary_plane_enable_ex(dc, output);
  }
  
  static void vs_primary_plane_atomic_disable(struct drm_plane
*plane,
@@ -89,10 +78,8 @@ static void
vs_primary_plane_atomic_disable(struct
drm_plane *plane,
  	unsigned int output = vcrtc->id;
  	struct vs_dc *dc = vcrtc->dc;
  
-	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
-			VSDC_FB_CONFIG_EX_FB_EN);
-
-	vs_primary_plane_commit(dc, output);
+	if (dc->funcs->primary_plane_disable_ex)
+		dc->funcs->primary_plane_disable_ex(dc, output);
  }
  
  static void vs_primary_plane_atomic_update(struct drm_plane
*plane,
@@ -138,18 +125,11 @@ static void
vs_primary_plane_atomic_update(struct drm_plane *plane,
  	regmap_write(dc->regs, VSDC_FB_STRIDE(output),
  		     fb->pitches[0]);
  
-	regmap_write(dc->regs, VSDC_FB_TOP_LEFT(output),
-		     VSDC_MAKE_PLANE_POS(state->crtc_x, state-
quoted
crtc_y));
-	regmap_write(dc->regs, VSDC_FB_BOTTOM_RIGHT(output),
-		     VSDC_MAKE_PLANE_POS(state->crtc_x + state-
quoted
crtc_w,
-					 state->crtc_y + state-
quoted
crtc_h));
  	regmap_write(dc->regs, VSDC_FB_SIZE(output),
  		     VSDC_MAKE_PLANE_SIZE(state->crtc_w, state-
quoted
crtc_h));
  
-	regmap_write(dc->regs, VSDC_FB_BLEND_CONFIG(output),
-		     VSDC_FB_BLEND_CONFIG_BLEND_DISABLE);
-
-	vs_primary_plane_commit(dc, output);
+	if (dc->funcs->primary_plane_update_ex)
+		dc->funcs->primary_plane_update_ex(dc, output,
state);
  }
  
  static const struct drm_plane_helper_funcs
vs_primary_plane_helper_funcs = {

Re: [PATCH v6 3/6] drm/verisilicon: introduce per-variant hardware ops table

From: Joey Lu <hidden>
Date: 2026-09-17 05:48:08

Icenowy Zheng 於 2026/9/16 下午 05:39 寫道:
在 2026-09-15二的 10:06 +0800,Joey Lu写道:
quoted
Icenowy Zheng 於 2026/9/14 下午 11:00 寫道:
quoted
在 2026-09-08二的 17:28 +0800,Joey Lu写道:
quoted
The DC8200 and DCUltraLite share a broadly similar register
layout
but
differ in how the bridge, CRTC, primary plane and IRQ paths are
driven.
Introduce a vs_dc_funcs vtable so each variant can supply its own
implementation without scattering conditionals across multiple
files.

Add a generation field to struct vs_chip_identity to distinguish
variants.
Extract the DC8200-specific hardware ops into vs_dc8200.c and add
unified
IRQ bit definitions so implementations can translate hardware-
specific
bits to a common set.  Update the shared code to dispatch through
dc->funcs.

No behaviour change for existing DC8200 platforms.

Signed-off-by: Joey Lu <redacted>
Reviewed-by: Icenowy Zheng <zhengxingda@iscas.ac.cn>
---
   drivers/gpu/drm/verisilicon/Makefile          |   2 +-
   drivers/gpu/drm/verisilicon/vs_bridge.c       |  20 +--
   drivers/gpu/drm/verisilicon/vs_crtc.c         |  38 +++++-
   drivers/gpu/drm/verisilicon/vs_dc.c           |   6 +-
   drivers/gpu/drm/verisilicon/vs_dc.h           |  32 +++++
   drivers/gpu/drm/verisilicon/vs_dc8200.c       | 121
++++++++++++++++++
   drivers/gpu/drm/verisilicon/vs_drm.c          |   5 +-
   drivers/gpu/drm/verisilicon/vs_drm.h          |   8 ++
   drivers/gpu/drm/verisilicon/vs_hwdb.c         |   4 +
   drivers/gpu/drm/verisilicon/vs_hwdb.h         |   6 +
   .../gpu/drm/verisilicon/vs_primary_plane.c    |  32 +----
   11 files changed, 220 insertions(+), 54 deletions(-)
   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc8200.c
diff --git a/drivers/gpu/drm/verisilicon/Makefile
b/drivers/gpu/drm/verisilicon/Makefile
index 426f4bcaa834d..9d4cd16452fa1 100644
--- a/drivers/gpu/drm/verisilicon/Makefile
+++ b/drivers/gpu/drm/verisilicon/Makefile
@@ -1,6 +1,6 @@
   # SPDX-License-Identifier: GPL-2.0-only
   
-verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_drm.o
vs_hwdb.o \
+verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_dc8200.o
vs_drm.o vs_hwdb.o \
   	vs_plane.o vs_primary_plane.o vs_cursor_plane.o
   
   obj-$(CONFIG_DRM_VERISILICON_DC) += verisilicon-dc.o
diff --git a/drivers/gpu/drm/verisilicon/vs_bridge.c
b/drivers/gpu/drm/verisilicon/vs_bridge.c
index dc7c85b07fe32..3fbc8d57f8a1e 100644
--- a/drivers/gpu/drm/verisilicon/vs_bridge.c
+++ b/drivers/gpu/drm/verisilicon/vs_bridge.c
@@ -162,15 +162,8 @@ static void vs_bridge_enable_common(struct
vs_crtc *crtc,
   			VSDC_DISP_PANEL_CONFIG_DE_EN |
   			VSDC_DISP_PANEL_CONFIG_DAT_EN |
   			VSDC_DISP_PANEL_CONFIG_CLK_EN);
-	regmap_set_bits(dc->regs,
VSDC_DISP_PANEL_CONFIG(output),
-			VSDC_DISP_PANEL_CONFIG_RUNNING);
-	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
-			
VSDC_DISP_PANEL_START_MULTI_DISP_SYNC);
-	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_START,
-			VSDC_DISP_PANEL_START_RUNNING(output));
-
-	regmap_set_bits(dc->regs,
VSDC_DISP_PANEL_CONFIG_EX(crtc-
quoted
id),
-			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
+
+	dc->funcs->panel_enable_ex(dc, output);
   }
   
   static void vs_bridge_atomic_enable_dpi(struct drm_bridge
*bridge,
@@ -228,14 +221,7 @@ static void vs_bridge_atomic_disable(struct
drm_bridge *bridge,
   	struct vs_dc *dc = crtc->dc;
   	unsigned int output = crtc->id;
   
-	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
-			  VSDC_DISP_PANEL_START_MULTI_DISP_SYNC
|
-			
VSDC_DISP_PANEL_START_RUNNING(output));
-	regmap_clear_bits(dc->regs,
VSDC_DISP_PANEL_CONFIG(output),
-			  VSDC_DISP_PANEL_CONFIG_RUNNING);
-
-	regmap_set_bits(dc->regs,
VSDC_DISP_PANEL_CONFIG_EX(crtc-
quoted
id),
-			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
+	dc->funcs->panel_disable_ex(dc, output);
   }
   
   static const struct drm_bridge_funcs vs_dpi_bridge_funcs = {
diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c
b/drivers/gpu/drm/verisilicon/vs_crtc.c
index c24c1588cbe69..2d4bf6af1f56e 100644
--- a/drivers/gpu/drm/verisilicon/vs_crtc.c
+++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
@@ -16,10 +16,33 @@
   #include "vs_crtc_regs.h"
   #include "vs_crtc.h"
   #include "vs_dc.h"
-#include "vs_dc_top_regs.h"
   #include "vs_drm.h"
   #include "vs_plane.h"
   
+static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
+				  struct drm_atomic_commit
*state)
+{
+	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
+	struct vs_dc *dc = vcrtc->dc;
+	unsigned int output = vcrtc->id;
+
+	if (dc->funcs->crtc_begin)
+		dc->funcs->crtc_begin(dc, output);
+}
+
+static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
+				  struct drm_atomic_commit
*state)
+{
+	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
+	struct vs_dc *dc = vcrtc->dc;
+	unsigned int output = vcrtc->id;
+
+	if (dc->funcs->crtc_flush)
+		dc->funcs->crtc_flush(dc, output);
+
+	drm_crtc_vblank_atomic_flush(crtc, state);
+}
+
   static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
   				   struct drm_atomic_commit
*state)
   {
@@ -30,6 +53,9 @@ static void vs_crtc_atomic_disable(struct
drm_crtc
*crtc,
   	drm_crtc_vblank_off(crtc);
   
   	clk_disable_unprepare(dc->pix_clk[output]);
+
+	if (dc->funcs->crtc_disable_ex)
+		dc->funcs->crtc_disable_ex(dc, output);
   }
   
   static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
@@ -42,6 +68,9 @@ static void vs_crtc_atomic_enable(struct
drm_crtc
*crtc,
   	drm_WARN_ON(&dc->drm_dev->base,
   		    clk_prepare_enable(dc->pix_clk[output]));
   
+	if (dc->funcs->crtc_enable_ex)
+		dc->funcs->crtc_enable_ex(dc, output);
+
   	drm_crtc_vblank_on(crtc);
   }
   
@@ -119,7 +148,8 @@ static bool vs_crtc_mode_fixup(struct
drm_crtc
*crtc,
   }
   
   static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs
= {
-	.atomic_flush	= drm_crtc_vblank_atomic_flush,
+	.atomic_begin	= vs_crtc_atomic_begin,
+	.atomic_flush	= vs_crtc_atomic_flush,
   	.atomic_enable	= vs_crtc_atomic_enable,
   	.atomic_disable	= vs_crtc_atomic_disable,
   	.mode_set_nofb	= vs_crtc_mode_set_nofb,
@@ -132,7 +162,7 @@ static int vs_crtc_enable_vblank(struct
drm_crtc
*crtc)
   	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
   	struct vs_dc *dc = vcrtc->dc;
   
-	regmap_set_bits(dc->regs, VSDC_TOP_IRQ_EN,
VSDC_TOP_IRQ_VSYNC(vcrtc->id));
+	dc->funcs->enable_vblank(dc, vcrtc->id);
   
   	return 0;
   }
@@ -142,7 +172,7 @@ static void vs_crtc_disable_vblank(struct
drm_crtc *crtc)
   	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
   	struct vs_dc *dc = vcrtc->dc;
   
-	regmap_clear_bits(dc->regs, VSDC_TOP_IRQ_EN,
VSDC_TOP_IRQ_VSYNC(vcrtc->id));
+	dc->funcs->disable_vblank(dc, vcrtc->id);
   }
   
   static const struct drm_crtc_funcs vs_crtc_funcs = {
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c
b/drivers/gpu/drm/verisilicon/vs_dc.c
index dad9967bc10b8..9729b693d360e 100644
--- a/drivers/gpu/drm/verisilicon/vs_dc.c
+++ b/drivers/gpu/drm/verisilicon/vs_dc.c
@@ -8,9 +8,7 @@
   #include <linux/of.h>
   #include <linux/of_graph.h>
   
-#include "vs_crtc.h"
   #include "vs_dc.h"
-#include "vs_dc_top_regs.h"
   #include "vs_drm.h"
   #include "vs_hwdb.h"
   
@@ -33,7 +31,7 @@ static irqreturn_t vs_dc_irq_handler(int irq,
void
*private)
   	struct vs_dc *dc = private;
   	u32 irqs;
   
-	regmap_read(dc->regs, VSDC_TOP_IRQ_ACK, &irqs);
+	irqs = dc->funcs->irq_ack(dc);
   
   	vs_drm_handle_irq(dc, irqs);
   
@@ -136,6 +134,8 @@ static int vs_dc_probe(struct platform_device
*pdev)
   	dev_info(dev, "Found DC%x rev %x customer %x\n", dc-
quoted
identity.model,
   		 dc->identity.revision, dc-
quoted
identity.customer_id);
   
+	dc->funcs = &vs_dc8200_funcs;
+
   	if (port_count > dc->identity.display_count) {
   		dev_err(dev, "too many downstream ports than HW
capability\n");
   		ret = -EINVAL;
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h
b/drivers/gpu/drm/verisilicon/vs_dc.h
index ed1016f18758e..825f5dd6bf174 100644
--- a/drivers/gpu/drm/verisilicon/vs_dc.h
+++ b/drivers/gpu/drm/verisilicon/vs_dc.h
@@ -14,6 +14,7 @@
   #include <linux/reset.h>
   
   #include <drm/drm_device.h>
+#include <drm/drm_plane.h>
   
   #include "vs_hwdb.h"
   
@@ -22,6 +23,34 @@
   
   struct vs_drm_dev;
   struct vs_crtc;
+struct vs_dc;
+
+struct vs_dc_funcs {
+	/* Bridge: atomic_enable, atomic_disable */
+	void (*panel_enable_ex)(struct vs_dc *dc, unsigned int
output);
+	void (*panel_disable_ex)(struct vs_dc *dc, unsigned int
output);
+
+	/* CRTC: atomic_begin, atomic_flush */
+	void (*crtc_begin)(struct vs_dc *dc, unsigned int
output);
+	void (*crtc_flush)(struct vs_dc *dc, unsigned int
output);
+
+	/* CRTC: atomic_enable, atomic_disable */
+	void (*crtc_enable_ex)(struct vs_dc *dc, unsigned int
output);
+	void (*crtc_disable_ex)(struct vs_dc *dc, unsigned int
output);
+
+	/* CRTC: enable_vblank, disable_vblank */
+	void (*enable_vblank)(struct vs_dc *dc, unsigned int
output);
+	void (*disable_vblank)(struct vs_dc *dc, unsigned int
output);
+
+	/* Primary plane: atomic_enable, atomic_disable,
atomic_update */
+	void (*primary_plane_enable_ex)(struct vs_dc *dc,
unsigned
int output);
+	void (*primary_plane_disable_ex)(struct vs_dc *dc,
unsigned
int output);
+	void (*primary_plane_update_ex)(struct vs_dc *dc,
unsigned
int output,
+					struct drm_plane_state
*state);
+
+	/* IRQ acknowledge */
+	u32 (*irq_ack)(struct vs_dc *dc);
+};
   
   struct vs_dc {
   	struct regmap *regs;
@@ -33,6 +62,9 @@ struct vs_dc {
   
   	struct vs_drm_dev *drm_dev;
   	struct vs_chip_identity identity;
+	const struct vs_dc_funcs *funcs;
   };
   
+extern const struct vs_dc_funcs vs_dc8200_funcs;
+
   #endif /* _VS_DC_H_ */
diff --git a/drivers/gpu/drm/verisilicon/vs_dc8200.c
b/drivers/gpu/drm/verisilicon/vs_dc8200.c
new file mode 100644
index 0000000000000..77752d0cb28b0
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc8200.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Icenowy Zheng <uwu@icenowy.me>
+ */
+
+#include <linux/regmap.h>
+
+#include <drm/drm_print.h>
+
+#include "vs_bridge_regs.h"
+#include "vs_dc.h"
+#include "vs_dc_top_regs.h"
+#include "vs_drm.h"
+#include "vs_plane.h"
+#include "vs_primary_plane_regs.h"
+
+static void vs_dc8200_panel_enable_ex(struct vs_dc *dc, unsigned
int
output)
+{
+	regmap_set_bits(dc->regs,
VSDC_DISP_PANEL_CONFIG(output),
+			VSDC_DISP_PANEL_CONFIG_RUNNING);
+	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
+			
VSDC_DISP_PANEL_START_MULTI_DISP_SYNC);
+	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_START,
+			VSDC_DISP_PANEL_START_RUNNING(output));
+
+	regmap_set_bits(dc->regs,
VSDC_DISP_PANEL_CONFIG_EX(output),
+			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
+}
+
+static void vs_dc8200_panel_disable_ex(struct vs_dc *dc,
unsigned
int output)
+{
+	regmap_clear_bits(dc->regs,
VSDC_DISP_PANEL_CONFIG(output),
+			  VSDC_DISP_PANEL_CONFIG_RUNNING);
+	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
+			  VSDC_DISP_PANEL_START_MULTI_DISP_SYNC
|
+			
VSDC_DISP_PANEL_START_RUNNING(output));
+
+	regmap_set_bits(dc->regs,
VSDC_DISP_PANEL_CONFIG_EX(output),
+			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
+}
+
+static void vs_dc8200_enable_vblank(struct vs_dc *dc, unsigned
int
output)
+{
+	regmap_set_bits(dc->regs, VSDC_TOP_IRQ_EN,
+			VSDC_TOP_IRQ_VSYNC(output));
+}
+
+static void vs_dc8200_disable_vblank(struct vs_dc *dc, unsigned
int
output)
+{
+	regmap_clear_bits(dc->regs, VSDC_TOP_IRQ_EN,
+			  VSDC_TOP_IRQ_VSYNC(output));
+}
+
+static void vs_dc8200_plane_commit(struct vs_dc *dc, unsigned
int
output)
+{
+	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
+			VSDC_FB_CONFIG_EX_COMMIT);
+}
+
+static void vs_dc8200_primary_plane_enable_ex(struct vs_dc *dc,
unsigned int output)
+{
+	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
+			VSDC_FB_CONFIG_EX_FB_EN);
+	regmap_update_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
+			   VSDC_FB_CONFIG_EX_DISPLAY_ID_MASK,
+			
VSDC_FB_CONFIG_EX_DISPLAY_ID(output));
+
+	vs_dc8200_plane_commit(dc, output);
+}
+
+static void vs_dc8200_primary_plane_disable_ex(struct vs_dc *dc,
unsigned int output)
+{
+	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
+			VSDC_FB_CONFIG_EX_FB_EN);
I think I messed up the DC8200 codepath here -- the FB_EN bit
should be
cleared instead of set.

Would you mind I fix this when applying, or would you send a new
revision fixing this?

In the latter case, should I send a fix to the current code before?

Thanks,
Icenowy
Please go ahead and fix it when applying -- I'm happy with that.
Thanks!
I'm going to apply [1] to the fixes branch, which seems to be
introducing merge conflict with this patch.

In addition, checkpatch.pl reports some parenthesis alignment issues on
this patch.

Maybe it's worth sending a v7 resolving all these problems on this
patch (DC8200 FB_EN clear_bits, parenthesis alignment, rebase on [1]).

Thanks,
Icenowy

[1]
https://lore.kernel.org/all/20260910095000.3505878-1-zhengxingda@iscas.ac.cn/
Understood. The conflict is from a commit that landed after v6 went out, a rebase resolves it cleanly.
I'll fix the parenthesis-alignment issues too, and send v7 rebased on top, including the port@1
restriction from patch 1 and the DC8200 FB_EN set/clear fix as its own dedicated commit.

Thanks.
quoted
quoted
quoted
+
+	vs_dc8200_plane_commit(dc, output);
+}
+
+static void vs_dc8200_primary_plane_update_ex(struct vs_dc *dc,
unsigned int output,
+				       struct drm_plane_state
*state)
+{
+	regmap_write(dc->regs, VSDC_FB_TOP_LEFT(output),
+		     VSDC_MAKE_PLANE_POS(state->crtc_x, state-
quoted
crtc_y));
+	regmap_write(dc->regs, VSDC_FB_BOTTOM_RIGHT(output),
+		     VSDC_MAKE_PLANE_POS(state->crtc_x + state-
quoted
crtc_w,
+					 state->crtc_y + state-
quoted
crtc_h));
+	regmap_write(dc->regs, VSDC_FB_BLEND_CONFIG(output),
+		     VSDC_FB_BLEND_CONFIG_BLEND_DISABLE);
+
+	vs_dc8200_plane_commit(dc, output);
+}
+
+static u32 vs_dc8200_irq_ack(struct vs_dc *dc)
+{
+	u32 hw_irqs, unified = 0, known = 0;
+	unsigned int i;
+
+	regmap_read(dc->regs, VSDC_TOP_IRQ_ACK, &hw_irqs);
+
+	for (i = 0; i < VSDC_MAX_OUTPUTS; i++) {
+		known |= VSDC_TOP_IRQ_VSYNC(i);
+		if (hw_irqs & VSDC_TOP_IRQ_VSYNC(i))
+			unified |= VSDC_IRQ_VSYNC(i);
+	}
+
+	drm_WARN_ONCE(&dc->drm_dev->base, hw_irqs & ~known,
+		      "Unknown hardware IRQ bits: %#x\n",
hw_irqs &
~known);
+
+	return unified;
+}
+
+const struct vs_dc_funcs vs_dc8200_funcs = {
+	.panel_enable_ex		=
vs_dc8200_panel_enable_ex,
+	.panel_disable_ex		=
vs_dc8200_panel_disable_ex,
+	.enable_vblank			=
vs_dc8200_enable_vblank,
+	.disable_vblank			=
vs_dc8200_disable_vblank,
+	.primary_plane_enable_ex	=
vs_dc8200_primary_plane_enable_ex,
+	.primary_plane_disable_ex	=
vs_dc8200_primary_plane_disable_ex,
+	.primary_plane_update_ex	=
vs_dc8200_primary_plane_update_ex,
+	.irq_ack			= vs_dc8200_irq_ack,
+};
diff --git a/drivers/gpu/drm/verisilicon/vs_drm.c
b/drivers/gpu/drm/verisilicon/vs_drm.c
index fd259d53f49f1..24e9d0b008f31 100644
--- a/drivers/gpu/drm/verisilicon/vs_drm.c
+++ b/drivers/gpu/drm/verisilicon/vs_drm.c
@@ -25,7 +25,6 @@
   #include "vs_bridge.h"
   #include "vs_crtc.h"
   #include "vs_dc.h"
-#include "vs_dc_top_regs.h"
   #include "vs_drm.h"
   
   #define DRIVER_NAME	"verisilicon"
@@ -168,8 +167,8 @@ void vs_drm_handle_irq(struct vs_dc *dc, u32
irqs)
   	unsigned int i;
   
   	for (i = 0; i < dc->identity.display_count; i++) {
-		if (irqs & VSDC_TOP_IRQ_VSYNC(i)) {
-			irqs &= ~VSDC_TOP_IRQ_VSYNC(i);
+		if (irqs & VSDC_IRQ_VSYNC(i)) {
+			irqs &= ~VSDC_IRQ_VSYNC(i);
   			if (dc->drm_dev->crtcs[i])
   				drm_crtc_handle_vblank(&dc-
quoted
drm_dev-
crtcs[i]->base);
   		}
diff --git a/drivers/gpu/drm/verisilicon/vs_drm.h
b/drivers/gpu/drm/verisilicon/vs_drm.h
index 606338206a427..6a89c20879df7 100644
--- a/drivers/gpu/drm/verisilicon/vs_drm.h
+++ b/drivers/gpu/drm/verisilicon/vs_drm.h
@@ -6,6 +6,7 @@
   #ifndef _VS_DRM_H_
   #define _VS_DRM_H_
   
+#include <linux/bits.h>
   #include <linux/platform_device.h>
   #include <linux/types.h>
   
@@ -13,6 +14,13 @@
   
   struct vs_dc;
   
+/*
+ * DC variants use different interrupt registers with diverging
bit
+ * assignments; each irq_ack() implementation must translate its
+ * hardware-specific bits into these definitions.
+ */
+#define VSDC_IRQ_VSYNC(n)	BIT(n)
+
   struct vs_drm_dev {
   	struct drm_device base;
   
diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.c
b/drivers/gpu/drm/verisilicon/vs_hwdb.c
index 2a0f7c59afa3a..91524d16f7785 100644
--- a/drivers/gpu/drm/verisilicon/vs_hwdb.c
+++ b/drivers/gpu/drm/verisilicon/vs_hwdb.c
@@ -94,6 +94,7 @@ static struct vs_chip_identity
vs_chip_identities[]
= {
   		.revision = 0x5720,
   		.customer_id = ~0U,
   
+		.generation = VSDC_GEN_DC8200,
   		.display_count = 2,
   		.max_cursor_size = 64,
   		.formats = &vs_formats_no_yuv444,
@@ -103,6 +104,7 @@ static struct vs_chip_identity
vs_chip_identities[] = {
   		.revision = 0x5721,
   		.customer_id = 0x30B,
   
+		.generation = VSDC_GEN_DC8200,
   		.display_count = 2,
   		.max_cursor_size = 64,
   		.formats = &vs_formats_no_yuv444,
@@ -112,6 +114,7 @@ static struct vs_chip_identity
vs_chip_identities[] = {
   		.revision = 0x5720,
   		.customer_id = 0x310,
   
+		.generation = VSDC_GEN_DC8200,
   		.display_count = 2,
   		.max_cursor_size = 64,
   		.formats = &vs_formats_with_yuv444,
@@ -121,6 +124,7 @@ static struct vs_chip_identity
vs_chip_identities[] = {
   		.revision = 0x5720,
   		.customer_id = 0x311,
   
+		.generation = VSDC_GEN_DC8200,
   		.display_count = 2,
   		.max_cursor_size = 64,
   		.formats = &vs_formats_no_yuv444,
diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.h
b/drivers/gpu/drm/verisilicon/vs_hwdb.h
index 2065ecb730437..a15c8b5656044 100644
--- a/drivers/gpu/drm/verisilicon/vs_hwdb.h
+++ b/drivers/gpu/drm/verisilicon/vs_hwdb.h
@@ -9,6 +9,11 @@
   #include <linux/regmap.h>
   #include <linux/types.h>
   
+enum vs_dc_generation {
+	VSDC_GEN_DC8000,
+	VSDC_GEN_DC8200,
+};
+
   struct vs_formats {
   	const u32 *array;
   	unsigned int num;
@@ -19,6 +24,7 @@ struct vs_chip_identity {
   	u32 revision;
   	u32 customer_id;
   
+	enum vs_dc_generation generation;
   	u32 display_count;
   	/*
   	 * The hardware only supports square cursor planes, so
this
field
diff --git a/drivers/gpu/drm/verisilicon/vs_primary_plane.c
b/drivers/gpu/drm/verisilicon/vs_primary_plane.c
index 2750016a7f2c3..85ba2068172db 100644
--- a/drivers/gpu/drm/verisilicon/vs_primary_plane.c
+++ b/drivers/gpu/drm/verisilicon/vs_primary_plane.c
@@ -54,12 +54,6 @@ static int
vs_primary_plane_atomic_check(struct
drm_plane *plane,
   	return 0;
   }
   
-static void vs_primary_plane_commit(struct vs_dc *dc, unsigned
int
output)
-{
-	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
-			VSDC_FB_CONFIG_EX_COMMIT);
-}
-
   static void vs_primary_plane_atomic_enable(struct drm_plane
*plane,
   					   struct
drm_atomic_commit
*atomic_state)
   {
@@ -70,13 +64,8 @@ static void
vs_primary_plane_atomic_enable(struct
drm_plane *plane,
   	unsigned int output = vcrtc->id;
   	struct vs_dc *dc = vcrtc->dc;
   
-	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
-			VSDC_FB_CONFIG_EX_FB_EN);
-	regmap_update_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
-			   VSDC_FB_CONFIG_EX_DISPLAY_ID_MASK,
-			
VSDC_FB_CONFIG_EX_DISPLAY_ID(output));
-
-	vs_primary_plane_commit(dc, output);
+	if (dc->funcs->primary_plane_enable_ex)
+		dc->funcs->primary_plane_enable_ex(dc, output);
   }
   
   static void vs_primary_plane_atomic_disable(struct drm_plane
*plane,
@@ -89,10 +78,8 @@ static void
vs_primary_plane_atomic_disable(struct
drm_plane *plane,
   	unsigned int output = vcrtc->id;
   	struct vs_dc *dc = vcrtc->dc;
   
-	regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
-			VSDC_FB_CONFIG_EX_FB_EN);
-
-	vs_primary_plane_commit(dc, output);
+	if (dc->funcs->primary_plane_disable_ex)
+		dc->funcs->primary_plane_disable_ex(dc, output);
   }
   
   static void vs_primary_plane_atomic_update(struct drm_plane
*plane,
@@ -138,18 +125,11 @@ static void
vs_primary_plane_atomic_update(struct drm_plane *plane,
   	regmap_write(dc->regs, VSDC_FB_STRIDE(output),
   		     fb->pitches[0]);
   
-	regmap_write(dc->regs, VSDC_FB_TOP_LEFT(output),
-		     VSDC_MAKE_PLANE_POS(state->crtc_x, state-
quoted
crtc_y));
-	regmap_write(dc->regs, VSDC_FB_BOTTOM_RIGHT(output),
-		     VSDC_MAKE_PLANE_POS(state->crtc_x + state-
quoted
crtc_w,
-					 state->crtc_y + state-
quoted
crtc_h));
   	regmap_write(dc->regs, VSDC_FB_SIZE(output),
   		     VSDC_MAKE_PLANE_SIZE(state->crtc_w, state-
quoted
crtc_h));
   
-	regmap_write(dc->regs, VSDC_FB_BLEND_CONFIG(output),
-		     VSDC_FB_BLEND_CONFIG_BLEND_DISABLE);
-
-	vs_primary_plane_commit(dc, output);
+	if (dc->funcs->primary_plane_update_ex)
+		dc->funcs->primary_plane_update_ex(dc, output,
state);
   }
   
   static const struct drm_plane_helper_funcs
vs_primary_plane_helper_funcs = {

Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help