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

Icenowy Zheng zhengxingda at iscas.ac.cn
Mon Jun 15 01:51:43 PDT 2026


在 2026-06-15一的 14:50 +0800,Joey Lu写道:
> The Nuvoton MA35D1 SoC integrates a Verisilicon DCUltraLite display
> controller whose register layout differs from the DC8200 in several
> important ways:
> 
> 1. No CONFIG_EX commit path: framebuffer updates use the enable (bit
> 0)
>    and reset (bit 4) bits in FB_CONFIG instead of the DC8200 staging
>    registers (FB_CONFIG_EX, FB_TOP_LEFT, FB_BOTTOM_RIGHT,
>    FB_BLEND_CONFIG, PANEL_CONFIG_EX).
> 
> 2. No PANEL_START register: panel output starts when
>    PANEL_CONFIG.RUNNING is set; there is no multi-display sync start
>    register.
> 
> 3. Different IRQ registers: DCUltraLite uses DISP_IRQ_STA (0x147C) /
>    DISP_IRQ_EN (0x1480) versus DC8200's TOP_IRQ_ACK (0x0010) /
>    TOP_IRQ_EN (0x0014).
> 
> 4. Per-frame commit cycle: DCUltraLite requires the VALID bit in
>    FB_CONFIG to be set at the start of each atomic commit
> (crtc_begin)
>    and cleared after (crtc_flush).
> 
> 5. Simpler clock topology: only 'core' (bus gate) and 'pix0' (pixel
>    divider) clocks; no axi or ahb clocks required.  Make axi_clk and
>    ahb_clk optional (devm_clk_get_optional_enabled) so DC8000 nodes
>    without those clocks are handled gracefully.
> 
> Add vs_dc8000.c implementing the vs_dc_funcs vtable for the above
> differences.  The probe now selects vs_dc8000_funcs when the
> identified
> generation is VSDC_GEN_DC8000 (DCUltraLite reads model 0x0,
> revision 0x5560, customer_id 0x305).
> 
> Signed-off-by: Joey Lu <a0987203069 at gmail.com>
> ---
>  drivers/gpu/drm/verisilicon/Makefile    |  2 +-
>  drivers/gpu/drm/verisilicon/vs_dc.c     |  9 ++-
>  drivers/gpu/drm/verisilicon/vs_dc.h     |  1 +
>  drivers/gpu/drm/verisilicon/vs_dc8000.c | 78
> +++++++++++++++++++++++++
>  4 files changed, 86 insertions(+), 4 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 9d4cd16452fa..d2fd8e4dff24 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 9729b693d360..9499fffbca58 100644
> --- a/drivers/gpu/drm/verisilicon/vs_dc.c
> +++ b/drivers/gpu/drm/verisilicon/vs_dc.c
> @@ -90,13 +90,13 @@ static int vs_dc_probe(struct platform_device
> *pdev)
>  		return PTR_ERR(dc->core_clk);
>  	}
>  
> -	dc->axi_clk = devm_clk_get_enabled(dev, "axi");
> +	dc->axi_clk = devm_clk_get_optional_enabled(dev, "axi");
>  	if (IS_ERR(dc->axi_clk)) {
>  		dev_err(dev, "can't get axi clock\n");
>  		return PTR_ERR(dc->axi_clk);
>  	}
>  
> -	dc->ahb_clk = devm_clk_get_enabled(dev, "ahb");
> +	dc->ahb_clk = devm_clk_get_optional_enabled(dev, "ahb");

Please make the clock change a separated patch for atomicity.

BTW the MA35D1 manual's clock tree shows that DCUltra appears on AXI2
ACLK, AHB_HCLK2, behind a mux of SYS-PLL/EPLL-DIV2 (which seems to be
the core clock), and behind a divider (which seems to be the pixel
clock).

However it's weird that only one DCUltra Clock Enable Bit exists
despite both bus clocks have "ICG" (I think it means "Integrated Clock
Gating"). In addition the linux clk-ma35d1 driver assigns "dcu_gate" as
a downstream of "dcu_mux", although the Figure 6.5-2 in the TRM shows
no ICG after the "Display core CLK" mux.

Is the two bus clocks controlled by a single gate bit, and is the bit
also gating DC core clock?

Thanks,
Icenowy

>  	if (IS_ERR(dc->ahb_clk)) {
>  		dev_err(dev, "can't get ahb clock\n");
>  		return PTR_ERR(dc->ahb_clk);
> @@ -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 544e1a37065b..5218e8cf63e2 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 000000000000..be0c0d7baf52
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc8000.c
> @@ -0,0 +1,78 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2026 Joey Lu <yclu4 at nuvoton.com>
> + */
> +
> +#include <linux/regmap.h>
> +
> +#include "vs_crtc_regs.h"
> +#include "vs_dc.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(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(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 irqs;
> +
> +	regmap_read(dc->regs, VSDC_DISP_IRQ_STA, &irqs);
> +	return irqs;
> +}
> +
> +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		= vs_dc8000_crtc_enable,
> +	.crtc_disable		= vs_dc8000_crtc_disable,
> +	.enable_vblank		= vs_dc8000_enable_vblank,
> +	.disable_vblank		= vs_dc8000_disable_vblank,
> +	.irq_ack		= vs_dc8000_irq_ack,
> +};




More information about the linux-arm-kernel mailing list