All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/6] DRM driver for verisilicon
@ 2023-10-25 10:39 ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-25 10:39 UTC (permalink / raw)
  To: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Keith Zhao, Jack Zhu, Shengyang Chen,
	Changhuang Liang

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="y", Size: 4417 bytes --]

This patch is a drm driver for Starfive Soc JH7110,
I am sending Drm driver part and HDMI driver part.

We used GEM framework for buffer management , and 
for buffer allocation,we use DMA APIs.

the Starfive HDMI servers as interface between a LCD 
Controller and a HDMI bus. A HDMI TX consists of one 
HDMI transmitter controller and one HDMI transmitter PHY.
(Sound support is not include in this patch)

This patchset should be applied after the patchset:
https://patchwork.kernel.org/project/linux-clk/cover/20230713113902.56519-1-xingyu.wu@starfivetech.com/

V1:
Changes since v1:
- Further standardize the yaml file.
- Dts naming convention improved.
- Fix the problem of compiling and loading ko files.
- Use drm new api to automatically manage resources.
- Drop struct vs_crtc_funcs&vs_plane_funcs£¬subdivide the plane's help interface
- Reduce the modifiers unused.
- Optimize the hdmi driver code

V2:
Changes since v2:
- fix the error about checking the yaml file.
- match drm driver GEM DMA API.
- Delete the custom crtc property .
- hdmi use drmm_ new api to automatically manage resources.
- update the modifiers comments.
- enabling KASAN, fix the error during removing module 

Keith Zhao (6):
  dt-bindings: display: Add yamls for JH7110 display system
  riscv: dts: starfive: jh7110: add dc controller and hdmi node
  drm/fourcc: Add drm/vs tiled modifiers
  drm/vs: Register DRM device
  drm/vs: Add KMS crtc&plane
  drm/vs: Add hdmi driver

 .../starfive/starfive,display-subsystem.yaml  |   41 +
 .../starfive/starfive,jh7110-dc8200.yaml      |  109 +
 .../starfive/starfive,jh7110-inno-hdmi.yaml   |   85 +
 MAINTAINERS                                   |    7 +
 .../jh7110-starfive-visionfive-2.dtsi         |   91 +
 arch/riscv/boot/dts/starfive/jh7110.dtsi      |   41 +
 drivers/gpu/drm/Kconfig                       |    2 +
 drivers/gpu/drm/Makefile                      |    1 +
 drivers/gpu/drm/verisilicon/Kconfig           |   21 +
 drivers/gpu/drm/verisilicon/Makefile          |   12 +
 drivers/gpu/drm/verisilicon/starfive_hdmi.c   |  949 ++++++++
 drivers/gpu/drm/verisilicon/starfive_hdmi.h   |  295 +++
 drivers/gpu/drm/verisilicon/vs_crtc.c         |  257 +++
 drivers/gpu/drm/verisilicon/vs_crtc.h         |   43 +
 drivers/gpu/drm/verisilicon/vs_dc.c           | 1002 +++++++++
 drivers/gpu/drm/verisilicon/vs_dc.h           |   80 +
 drivers/gpu/drm/verisilicon/vs_dc_hw.c        | 1959 +++++++++++++++++
 drivers/gpu/drm/verisilicon/vs_dc_hw.h        |  492 +++++
 drivers/gpu/drm/verisilicon/vs_drv.c          |  234 ++
 drivers/gpu/drm/verisilicon/vs_drv.h          |   31 +
 drivers/gpu/drm/verisilicon/vs_modeset.c      |   36 +
 drivers/gpu/drm/verisilicon/vs_modeset.h      |   10 +
 drivers/gpu/drm/verisilicon/vs_plane.c        |  526 +++++
 drivers/gpu/drm/verisilicon/vs_plane.h        |   58 +
 drivers/gpu/drm/verisilicon/vs_type.h         |   69 +
 include/uapi/drm/drm_fourcc.h                 |   57 +
 include/uapi/drm/vs_drm.h                     |   50 +
 27 files changed, 6558 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
 create mode 100644 drivers/gpu/drm/verisilicon/Kconfig
 create mode 100644 drivers/gpu/drm/verisilicon/Makefile
 create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.c
 create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
 create mode 100644 include/uapi/drm/vs_drm.h

-- 
2.34.1


^ permalink raw reply	[flat|nested] 117+ messages in thread

* [PATCH v2 0/6] DRM driver for verisilicon
@ 2023-10-25 10:39 ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-25 10:39 UTC (permalink / raw)
  To: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Keith Zhao, Jack Zhu, Shengyang Chen,
	Changhuang Liang

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="y", Size: 4513 bytes --]

This patch is a drm driver for Starfive Soc JH7110,
I am sending Drm driver part and HDMI driver part.

We used GEM framework for buffer management , and 
for buffer allocation,we use DMA APIs.

the Starfive HDMI servers as interface between a LCD 
Controller and a HDMI bus. A HDMI TX consists of one 
HDMI transmitter controller and one HDMI transmitter PHY.
(Sound support is not include in this patch)

This patchset should be applied after the patchset:
https://patchwork.kernel.org/project/linux-clk/cover/20230713113902.56519-1-xingyu.wu@starfivetech.com/

V1:
Changes since v1:
- Further standardize the yaml file.
- Dts naming convention improved.
- Fix the problem of compiling and loading ko files.
- Use drm new api to automatically manage resources.
- Drop struct vs_crtc_funcs&vs_plane_funcs£¬subdivide the plane's help interface
- Reduce the modifiers unused.
- Optimize the hdmi driver code

V2:
Changes since v2:
- fix the error about checking the yaml file.
- match drm driver GEM DMA API.
- Delete the custom crtc property .
- hdmi use drmm_ new api to automatically manage resources.
- update the modifiers comments.
- enabling KASAN, fix the error during removing module 

Keith Zhao (6):
  dt-bindings: display: Add yamls for JH7110 display system
  riscv: dts: starfive: jh7110: add dc controller and hdmi node
  drm/fourcc: Add drm/vs tiled modifiers
  drm/vs: Register DRM device
  drm/vs: Add KMS crtc&plane
  drm/vs: Add hdmi driver

 .../starfive/starfive,display-subsystem.yaml  |   41 +
 .../starfive/starfive,jh7110-dc8200.yaml      |  109 +
 .../starfive/starfive,jh7110-inno-hdmi.yaml   |   85 +
 MAINTAINERS                                   |    7 +
 .../jh7110-starfive-visionfive-2.dtsi         |   91 +
 arch/riscv/boot/dts/starfive/jh7110.dtsi      |   41 +
 drivers/gpu/drm/Kconfig                       |    2 +
 drivers/gpu/drm/Makefile                      |    1 +
 drivers/gpu/drm/verisilicon/Kconfig           |   21 +
 drivers/gpu/drm/verisilicon/Makefile          |   12 +
 drivers/gpu/drm/verisilicon/starfive_hdmi.c   |  949 ++++++++
 drivers/gpu/drm/verisilicon/starfive_hdmi.h   |  295 +++
 drivers/gpu/drm/verisilicon/vs_crtc.c         |  257 +++
 drivers/gpu/drm/verisilicon/vs_crtc.h         |   43 +
 drivers/gpu/drm/verisilicon/vs_dc.c           | 1002 +++++++++
 drivers/gpu/drm/verisilicon/vs_dc.h           |   80 +
 drivers/gpu/drm/verisilicon/vs_dc_hw.c        | 1959 +++++++++++++++++
 drivers/gpu/drm/verisilicon/vs_dc_hw.h        |  492 +++++
 drivers/gpu/drm/verisilicon/vs_drv.c          |  234 ++
 drivers/gpu/drm/verisilicon/vs_drv.h          |   31 +
 drivers/gpu/drm/verisilicon/vs_modeset.c      |   36 +
 drivers/gpu/drm/verisilicon/vs_modeset.h      |   10 +
 drivers/gpu/drm/verisilicon/vs_plane.c        |  526 +++++
 drivers/gpu/drm/verisilicon/vs_plane.h        |   58 +
 drivers/gpu/drm/verisilicon/vs_type.h         |   69 +
 include/uapi/drm/drm_fourcc.h                 |   57 +
 include/uapi/drm/vs_drm.h                     |   50 +
 27 files changed, 6558 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
 create mode 100644 drivers/gpu/drm/verisilicon/Kconfig
 create mode 100644 drivers/gpu/drm/verisilicon/Makefile
 create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.c
 create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
 create mode 100644 include/uapi/drm/vs_drm.h

-- 
2.34.1



[-- Attachment #2: Type: text/plain, Size: 161 bytes --]

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* [PATCH v2 0/6] DRM driver for verisilicon
@ 2023-10-25 10:39 ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-25 10:39 UTC (permalink / raw)
  To: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Keith Zhao, Bjorn Andersson, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Shawn Guo, christian.koenig

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="y", Size: 4511 bytes --]

This patch is a drm driver for Starfive Soc JH7110,
I am sending Drm driver part and HDMI driver part.

We used GEM framework for buffer management , and 
for buffer allocation,we use DMA APIs.

the Starfive HDMI servers as interface between a LCD 
Controller and a HDMI bus. A HDMI TX consists of one 
HDMI transmitter controller and one HDMI transmitter PHY.
(Sound support is not include in this patch)

This patchset should be applied after the patchset:
https://patchwork.kernel.org/project/linux-clk/cover/20230713113902.56519-1-xingyu.wu@starfivetech.com/

V1:
Changes since v1:
- Further standardize the yaml file.
- Dts naming convention improved.
- Fix the problem of compiling and loading ko files.
- Use drm new api to automatically manage resources.
- Drop struct vs_crtc_funcs&vs_plane_funcs£¬subdivide the plane's help interface
- Reduce the modifiers unused.
- Optimize the hdmi driver code

V2:
Changes since v2:
- fix the error about checking the yaml file.
- match drm driver GEM DMA API.
- Delete the custom crtc property .
- hdmi use drmm_ new api to automatically manage resources.
- update the modifiers comments.
- enabling KASAN, fix the error during removing module 

Keith Zhao (6):
  dt-bindings: display: Add yamls for JH7110 display system
  riscv: dts: starfive: jh7110: add dc controller and hdmi node
  drm/fourcc: Add drm/vs tiled modifiers
  drm/vs: Register DRM device
  drm/vs: Add KMS crtc&plane
  drm/vs: Add hdmi driver

 .../starfive/starfive,display-subsystem.yaml  |   41 +
 .../starfive/starfive,jh7110-dc8200.yaml      |  109 +
 .../starfive/starfive,jh7110-inno-hdmi.yaml   |   85 +
 MAINTAINERS                                   |    7 +
 .../jh7110-starfive-visionfive-2.dtsi         |   91 +
 arch/riscv/boot/dts/starfive/jh7110.dtsi      |   41 +
 drivers/gpu/drm/Kconfig                       |    2 +
 drivers/gpu/drm/Makefile                      |    1 +
 drivers/gpu/drm/verisilicon/Kconfig           |   21 +
 drivers/gpu/drm/verisilicon/Makefile          |   12 +
 drivers/gpu/drm/verisilicon/starfive_hdmi.c   |  949 ++++++++
 drivers/gpu/drm/verisilicon/starfive_hdmi.h   |  295 +++
 drivers/gpu/drm/verisilicon/vs_crtc.c         |  257 +++
 drivers/gpu/drm/verisilicon/vs_crtc.h         |   43 +
 drivers/gpu/drm/verisilicon/vs_dc.c           | 1002 +++++++++
 drivers/gpu/drm/verisilicon/vs_dc.h           |   80 +
 drivers/gpu/drm/verisilicon/vs_dc_hw.c        | 1959 +++++++++++++++++
 drivers/gpu/drm/verisilicon/vs_dc_hw.h        |  492 +++++
 drivers/gpu/drm/verisilicon/vs_drv.c          |  234 ++
 drivers/gpu/drm/verisilicon/vs_drv.h          |   31 +
 drivers/gpu/drm/verisilicon/vs_modeset.c      |   36 +
 drivers/gpu/drm/verisilicon/vs_modeset.h      |   10 +
 drivers/gpu/drm/verisilicon/vs_plane.c        |  526 +++++
 drivers/gpu/drm/verisilicon/vs_plane.h        |   58 +
 drivers/gpu/drm/verisilicon/vs_type.h         |   69 +
 include/uapi/drm/drm_fourcc.h                 |   57 +
 include/uapi/drm/vs_drm.h                     |   50 +
 27 files changed, 6558 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
 create mode 100644 drivers/gpu/drm/verisilicon/Kconfig
 create mode 100644 drivers/gpu/drm/verisilicon/Makefile
 create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.c
 create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
 create mode 100644 include/uapi/drm/vs_drm.h

-- 
2.34.1


^ permalink raw reply	[flat|nested] 117+ messages in thread

* [PATCH v2 1/6] dt-bindings: display: Add yamls for JH7110 display system
  2023-10-25 10:39 ` Keith Zhao
  (?)
@ 2023-10-25 10:39   ` Keith Zhao
  -1 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-25 10:39 UTC (permalink / raw)
  To: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Keith Zhao, Jack Zhu, Shengyang Chen,
	Changhuang Liang

StarFive SoCs JH7110 display system:
lcd-controller bases verisilicon dc8200 IP,
and hdmi bases Innosilicon IP. Add bindings for them.

also update MAINTAINERS for dt-bindings

about this patch, I tested the dtbs_check and dt_binding_check
with the result pass.
Based on the feedback of the previous version, the corresponding arrangement is made

Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
---
 .../starfive/starfive,display-subsystem.yaml  |  41 +++++++
 .../starfive/starfive,jh7110-dc8200.yaml      | 109 ++++++++++++++++++
 .../starfive/starfive,jh7110-inno-hdmi.yaml   |  85 ++++++++++++++
 MAINTAINERS                                   |   7 ++
 4 files changed, 242 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml

diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
new file mode 100644
index 000000000..f45b97b08
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
@@ -0,0 +1,41 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/starfive/starfive,display-subsystem.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Starfive DRM master device
+
+maintainers:
+  - Keith Zhao <keith.zhao@starfivetech.com>
+  - ShengYang Chen <shengyang.chen@starfivetech.com>
+
+description:
+  The Starfive DRM master device is a virtual device needed to list all
+  display controller or other display interface nodes that comprise the
+  graphics subsystem.
+
+properties:
+  compatible:
+    const: starfive,display-subsystem
+
+  ports:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    description:
+      Should contain a list of phandles pointing to display interface ports
+      of display controller devices. Display controller definitions as defined
+      in Documentation/devicetree/bindings/display/starfive/
+      starfive,jh7110-dc8200.yaml
+
+required:
+  - compatible
+  - ports
+
+additionalProperties: false
+
+examples:
+  - |
+    display-subsystem {
+        compatible = "starfive,display-subsystem";
+        ports = <&dc_out>;
+    };
diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
new file mode 100644
index 000000000..87051cddf
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
@@ -0,0 +1,109 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/starfive/starfive,jh7110-dc8200.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: StarFive display controller
+
+description:
+  The StarFive SoC uses the display controller based on Verisilicon IP
+  to transfer the image data from a video memory buffer to an external
+  LCD interface.
+
+maintainers:
+  - Keith Zhao <keith.zhao@starfivetech.com>
+
+properties:
+  compatible:
+    const: starfive,jh7110-dc8200
+
+  reg:
+    minItems: 1
+    items:
+      - description:
+          host interface
+      - description:
+          display physical base address and length.
+
+  interrupts:
+    items:
+      - description: The interrupt will be generated when DC finish one frame
+
+  clocks:
+    items:
+      - description: Clock for display system noc bus.
+      - description: Pixel clock for display channel 0.
+      - description: Pixel clock for display channel 1.
+      - description: Core clock for display controller.
+      - description: Clock for axi bus to access ddr.
+      - description: Clock for ahb bus to R/W the phy regs.
+      - description: External HDMI pixel clock.
+      - description: Parent clock for pixel clock
+
+  clock-names:
+    items:
+      - const: noc_bus
+      - const: channel0
+      - const: channel1
+      - const: dc_core
+      - const: axi_core
+      - const: ahb
+      - const: hdmi_tx
+      - const: dc_parent
+
+  resets:
+    items:
+      - description: Reset for axi bus.
+      - description: Reset for ahb bus.
+      - description: Core reset of display controller.
+
+  reset-names:
+    items:
+      - const: axi
+      - const: ahb
+      - const: core
+
+  port:
+    $ref: /schemas/graph.yaml#/properties/port
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - resets
+  - reset-names
+  - port
+
+additionalProperties: false
+
+examples:
+  - |
+    dc8200: lcd-controller@29400000 {
+        compatible = "starfive,jh7110-dc8200";
+        reg = <0x29400000 0x100>, <0x29400800 0x2000>;
+        interrupts = <95>;
+        clocks = <&syscrg 60>,
+               <&voutcrg 7>,
+               <&voutcrg 8>,
+               <&voutcrg 4>,
+               <&voutcrg 5>,
+               <&voutcrg 6>,
+               <&hdmitx0_pixelclk>,
+               <&voutcrg 1>;
+        clock-names = "noc_bus", "channel0", "channel1",
+                      "dc_core", "axi_core", "ahb",
+                      "hdmi_tx","dc_parent";
+        resets = <&voutcrg 0>, <&voutcrg 1>, <&voutcrg 2>;
+        reset-names = "axi", "ahb", "core";
+        dc_out: port {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            dc_out_hdmi: endpoint@0 {
+                reg = <0>;
+                remote-endpoint = <&hdmi_in_dc>;
+            };
+        };
+    };
diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
new file mode 100644
index 000000000..f6c473a10
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
@@ -0,0 +1,85 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/starfive/starfive,jh7110-inno-hdmi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Starfive JH7110 HDMI controller
+
+description:
+  The StarFive JH7110 SoC uses the HDMI signal transmiter based on innosilicon IP
+  to generate HDMI signal from its input and transmit the signal to the screen.
+
+maintainers:
+  - Keith Zhao <keith.zhao@starfivetech.com>
+
+properties:
+  compatible:
+    const: "starfive,jh7110-inno-hdmi"
+
+  reg:
+    minItems: 1
+
+  interrupts:
+    items:
+      - description: The HDMI hot plug detection interrupt.
+
+  clocks:
+    items:
+      - description: System clock of HDMI module.
+      - description: Mclk clock of HDMI audio.
+      - description: Bclk clock of HDMI audio.
+      - description: Pixel clock generated by HDMI module.
+
+  clock-names:
+    items:
+      - const: sysclk
+      - const: mclk
+      - const: bclk
+      - const: pclk
+
+  resets:
+    maxItems: 1
+
+  '#sound-dai-cells':
+    const: 0
+
+  port:
+    $ref: /schemas/graph.yaml#/properties/port
+    description:
+      Should contain a remote endpoint phandle of display controller device.
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - resets
+  - '#sound-dai-cells'
+  - port
+
+additionalProperties: false
+
+examples:
+  - |
+    hdmi: hdmi@29590000 {
+        compatible = "starfive,jh7110-inno-hdmi";
+        reg = <0x29590000 0x4000>;
+        interrupts = <99>;
+        clocks = <&voutcrg 17>,
+               <&voutcrg 15>,
+               <&voutcrg 16>,
+               <&hdmitx0_pixelclk>;
+        clock-names = "sysclk", "mclk","bclk","pclk";
+        resets = <&voutcrg 9>;
+        #sound-dai-cells = <0>;
+        hdmi_in: port {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            hdmi_in_dc: endpoint@0 {
+                reg = <0>;
+                remote-endpoint = <&dc_out_hdmi>;
+            };
+        };
+    };
diff --git a/MAINTAINERS b/MAINTAINERS
index 3be1bdfe8..46e51bca5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6751,6 +6751,13 @@ T:	git git://anongit.freedesktop.org/drm/drm-misc
 F:	Documentation/devicetree/bindings/display/panel/tpo,tpg110.yaml
 F:	drivers/gpu/drm/panel/panel-tpo-tpg110.c
 
+DRM DRIVERS FOR STARFIVE
+M:	Keith Zhao <keith.zhao@starfivetech.com>
+L:	dri-devel@lists.freedesktop.org
+S:	Maintained
+T:	git git://anongit.freedesktop.org/drm/drm-misc
+F:	Documentation/devicetree/bindings/display/starfive/
+
 DRM DRIVER FOR USB DISPLAYLINK VIDEO ADAPTERS
 M:	Dave Airlie <airlied@redhat.com>
 R:	Sean Paul <sean@poorly.run>
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 117+ messages in thread

* [PATCH v2 1/6] dt-bindings: display: Add yamls for JH7110 display system
@ 2023-10-25 10:39   ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-25 10:39 UTC (permalink / raw)
  To: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Keith Zhao, Jack Zhu, Shengyang Chen,
	Changhuang Liang

StarFive SoCs JH7110 display system:
lcd-controller bases verisilicon dc8200 IP,
and hdmi bases Innosilicon IP. Add bindings for them.

also update MAINTAINERS for dt-bindings

about this patch, I tested the dtbs_check and dt_binding_check
with the result pass.
Based on the feedback of the previous version, the corresponding arrangement is made

Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
---
 .../starfive/starfive,display-subsystem.yaml  |  41 +++++++
 .../starfive/starfive,jh7110-dc8200.yaml      | 109 ++++++++++++++++++
 .../starfive/starfive,jh7110-inno-hdmi.yaml   |  85 ++++++++++++++
 MAINTAINERS                                   |   7 ++
 4 files changed, 242 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml

diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
new file mode 100644
index 000000000..f45b97b08
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
@@ -0,0 +1,41 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/starfive/starfive,display-subsystem.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Starfive DRM master device
+
+maintainers:
+  - Keith Zhao <keith.zhao@starfivetech.com>
+  - ShengYang Chen <shengyang.chen@starfivetech.com>
+
+description:
+  The Starfive DRM master device is a virtual device needed to list all
+  display controller or other display interface nodes that comprise the
+  graphics subsystem.
+
+properties:
+  compatible:
+    const: starfive,display-subsystem
+
+  ports:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    description:
+      Should contain a list of phandles pointing to display interface ports
+      of display controller devices. Display controller definitions as defined
+      in Documentation/devicetree/bindings/display/starfive/
+      starfive,jh7110-dc8200.yaml
+
+required:
+  - compatible
+  - ports
+
+additionalProperties: false
+
+examples:
+  - |
+    display-subsystem {
+        compatible = "starfive,display-subsystem";
+        ports = <&dc_out>;
+    };
diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
new file mode 100644
index 000000000..87051cddf
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
@@ -0,0 +1,109 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/starfive/starfive,jh7110-dc8200.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: StarFive display controller
+
+description:
+  The StarFive SoC uses the display controller based on Verisilicon IP
+  to transfer the image data from a video memory buffer to an external
+  LCD interface.
+
+maintainers:
+  - Keith Zhao <keith.zhao@starfivetech.com>
+
+properties:
+  compatible:
+    const: starfive,jh7110-dc8200
+
+  reg:
+    minItems: 1
+    items:
+      - description:
+          host interface
+      - description:
+          display physical base address and length.
+
+  interrupts:
+    items:
+      - description: The interrupt will be generated when DC finish one frame
+
+  clocks:
+    items:
+      - description: Clock for display system noc bus.
+      - description: Pixel clock for display channel 0.
+      - description: Pixel clock for display channel 1.
+      - description: Core clock for display controller.
+      - description: Clock for axi bus to access ddr.
+      - description: Clock for ahb bus to R/W the phy regs.
+      - description: External HDMI pixel clock.
+      - description: Parent clock for pixel clock
+
+  clock-names:
+    items:
+      - const: noc_bus
+      - const: channel0
+      - const: channel1
+      - const: dc_core
+      - const: axi_core
+      - const: ahb
+      - const: hdmi_tx
+      - const: dc_parent
+
+  resets:
+    items:
+      - description: Reset for axi bus.
+      - description: Reset for ahb bus.
+      - description: Core reset of display controller.
+
+  reset-names:
+    items:
+      - const: axi
+      - const: ahb
+      - const: core
+
+  port:
+    $ref: /schemas/graph.yaml#/properties/port
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - resets
+  - reset-names
+  - port
+
+additionalProperties: false
+
+examples:
+  - |
+    dc8200: lcd-controller@29400000 {
+        compatible = "starfive,jh7110-dc8200";
+        reg = <0x29400000 0x100>, <0x29400800 0x2000>;
+        interrupts = <95>;
+        clocks = <&syscrg 60>,
+               <&voutcrg 7>,
+               <&voutcrg 8>,
+               <&voutcrg 4>,
+               <&voutcrg 5>,
+               <&voutcrg 6>,
+               <&hdmitx0_pixelclk>,
+               <&voutcrg 1>;
+        clock-names = "noc_bus", "channel0", "channel1",
+                      "dc_core", "axi_core", "ahb",
+                      "hdmi_tx","dc_parent";
+        resets = <&voutcrg 0>, <&voutcrg 1>, <&voutcrg 2>;
+        reset-names = "axi", "ahb", "core";
+        dc_out: port {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            dc_out_hdmi: endpoint@0 {
+                reg = <0>;
+                remote-endpoint = <&hdmi_in_dc>;
+            };
+        };
+    };
diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
new file mode 100644
index 000000000..f6c473a10
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
@@ -0,0 +1,85 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/starfive/starfive,jh7110-inno-hdmi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Starfive JH7110 HDMI controller
+
+description:
+  The StarFive JH7110 SoC uses the HDMI signal transmiter based on innosilicon IP
+  to generate HDMI signal from its input and transmit the signal to the screen.
+
+maintainers:
+  - Keith Zhao <keith.zhao@starfivetech.com>
+
+properties:
+  compatible:
+    const: "starfive,jh7110-inno-hdmi"
+
+  reg:
+    minItems: 1
+
+  interrupts:
+    items:
+      - description: The HDMI hot plug detection interrupt.
+
+  clocks:
+    items:
+      - description: System clock of HDMI module.
+      - description: Mclk clock of HDMI audio.
+      - description: Bclk clock of HDMI audio.
+      - description: Pixel clock generated by HDMI module.
+
+  clock-names:
+    items:
+      - const: sysclk
+      - const: mclk
+      - const: bclk
+      - const: pclk
+
+  resets:
+    maxItems: 1
+
+  '#sound-dai-cells':
+    const: 0
+
+  port:
+    $ref: /schemas/graph.yaml#/properties/port
+    description:
+      Should contain a remote endpoint phandle of display controller device.
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - resets
+  - '#sound-dai-cells'
+  - port
+
+additionalProperties: false
+
+examples:
+  - |
+    hdmi: hdmi@29590000 {
+        compatible = "starfive,jh7110-inno-hdmi";
+        reg = <0x29590000 0x4000>;
+        interrupts = <99>;
+        clocks = <&voutcrg 17>,
+               <&voutcrg 15>,
+               <&voutcrg 16>,
+               <&hdmitx0_pixelclk>;
+        clock-names = "sysclk", "mclk","bclk","pclk";
+        resets = <&voutcrg 9>;
+        #sound-dai-cells = <0>;
+        hdmi_in: port {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            hdmi_in_dc: endpoint@0 {
+                reg = <0>;
+                remote-endpoint = <&dc_out_hdmi>;
+            };
+        };
+    };
diff --git a/MAINTAINERS b/MAINTAINERS
index 3be1bdfe8..46e51bca5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6751,6 +6751,13 @@ T:	git git://anongit.freedesktop.org/drm/drm-misc
 F:	Documentation/devicetree/bindings/display/panel/tpo,tpg110.yaml
 F:	drivers/gpu/drm/panel/panel-tpo-tpg110.c
 
+DRM DRIVERS FOR STARFIVE
+M:	Keith Zhao <keith.zhao@starfivetech.com>
+L:	dri-devel@lists.freedesktop.org
+S:	Maintained
+T:	git git://anongit.freedesktop.org/drm/drm-misc
+F:	Documentation/devicetree/bindings/display/starfive/
+
 DRM DRIVER FOR USB DISPLAYLINK VIDEO ADAPTERS
 M:	Dave Airlie <airlied@redhat.com>
 R:	Sean Paul <sean@poorly.run>
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 117+ messages in thread

* [PATCH v2 1/6] dt-bindings: display: Add yamls for JH7110 display system
@ 2023-10-25 10:39   ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-25 10:39 UTC (permalink / raw)
  To: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Keith Zhao, Bjorn Andersson, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Shawn Guo, christian.koenig

StarFive SoCs JH7110 display system:
lcd-controller bases verisilicon dc8200 IP,
and hdmi bases Innosilicon IP. Add bindings for them.

also update MAINTAINERS for dt-bindings

about this patch, I tested the dtbs_check and dt_binding_check
with the result pass.
Based on the feedback of the previous version, the corresponding arrangement is made

Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
---
 .../starfive/starfive,display-subsystem.yaml  |  41 +++++++
 .../starfive/starfive,jh7110-dc8200.yaml      | 109 ++++++++++++++++++
 .../starfive/starfive,jh7110-inno-hdmi.yaml   |  85 ++++++++++++++
 MAINTAINERS                                   |   7 ++
 4 files changed, 242 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml

diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
new file mode 100644
index 000000000..f45b97b08
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
@@ -0,0 +1,41 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/starfive/starfive,display-subsystem.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Starfive DRM master device
+
+maintainers:
+  - Keith Zhao <keith.zhao@starfivetech.com>
+  - ShengYang Chen <shengyang.chen@starfivetech.com>
+
+description:
+  The Starfive DRM master device is a virtual device needed to list all
+  display controller or other display interface nodes that comprise the
+  graphics subsystem.
+
+properties:
+  compatible:
+    const: starfive,display-subsystem
+
+  ports:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    description:
+      Should contain a list of phandles pointing to display interface ports
+      of display controller devices. Display controller definitions as defined
+      in Documentation/devicetree/bindings/display/starfive/
+      starfive,jh7110-dc8200.yaml
+
+required:
+  - compatible
+  - ports
+
+additionalProperties: false
+
+examples:
+  - |
+    display-subsystem {
+        compatible = "starfive,display-subsystem";
+        ports = <&dc_out>;
+    };
diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
new file mode 100644
index 000000000..87051cddf
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
@@ -0,0 +1,109 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/starfive/starfive,jh7110-dc8200.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: StarFive display controller
+
+description:
+  The StarFive SoC uses the display controller based on Verisilicon IP
+  to transfer the image data from a video memory buffer to an external
+  LCD interface.
+
+maintainers:
+  - Keith Zhao <keith.zhao@starfivetech.com>
+
+properties:
+  compatible:
+    const: starfive,jh7110-dc8200
+
+  reg:
+    minItems: 1
+    items:
+      - description:
+          host interface
+      - description:
+          display physical base address and length.
+
+  interrupts:
+    items:
+      - description: The interrupt will be generated when DC finish one frame
+
+  clocks:
+    items:
+      - description: Clock for display system noc bus.
+      - description: Pixel clock for display channel 0.
+      - description: Pixel clock for display channel 1.
+      - description: Core clock for display controller.
+      - description: Clock for axi bus to access ddr.
+      - description: Clock for ahb bus to R/W the phy regs.
+      - description: External HDMI pixel clock.
+      - description: Parent clock for pixel clock
+
+  clock-names:
+    items:
+      - const: noc_bus
+      - const: channel0
+      - const: channel1
+      - const: dc_core
+      - const: axi_core
+      - const: ahb
+      - const: hdmi_tx
+      - const: dc_parent
+
+  resets:
+    items:
+      - description: Reset for axi bus.
+      - description: Reset for ahb bus.
+      - description: Core reset of display controller.
+
+  reset-names:
+    items:
+      - const: axi
+      - const: ahb
+      - const: core
+
+  port:
+    $ref: /schemas/graph.yaml#/properties/port
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - resets
+  - reset-names
+  - port
+
+additionalProperties: false
+
+examples:
+  - |
+    dc8200: lcd-controller@29400000 {
+        compatible = "starfive,jh7110-dc8200";
+        reg = <0x29400000 0x100>, <0x29400800 0x2000>;
+        interrupts = <95>;
+        clocks = <&syscrg 60>,
+               <&voutcrg 7>,
+               <&voutcrg 8>,
+               <&voutcrg 4>,
+               <&voutcrg 5>,
+               <&voutcrg 6>,
+               <&hdmitx0_pixelclk>,
+               <&voutcrg 1>;
+        clock-names = "noc_bus", "channel0", "channel1",
+                      "dc_core", "axi_core", "ahb",
+                      "hdmi_tx","dc_parent";
+        resets = <&voutcrg 0>, <&voutcrg 1>, <&voutcrg 2>;
+        reset-names = "axi", "ahb", "core";
+        dc_out: port {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            dc_out_hdmi: endpoint@0 {
+                reg = <0>;
+                remote-endpoint = <&hdmi_in_dc>;
+            };
+        };
+    };
diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
new file mode 100644
index 000000000..f6c473a10
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
@@ -0,0 +1,85 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/starfive/starfive,jh7110-inno-hdmi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Starfive JH7110 HDMI controller
+
+description:
+  The StarFive JH7110 SoC uses the HDMI signal transmiter based on innosilicon IP
+  to generate HDMI signal from its input and transmit the signal to the screen.
+
+maintainers:
+  - Keith Zhao <keith.zhao@starfivetech.com>
+
+properties:
+  compatible:
+    const: "starfive,jh7110-inno-hdmi"
+
+  reg:
+    minItems: 1
+
+  interrupts:
+    items:
+      - description: The HDMI hot plug detection interrupt.
+
+  clocks:
+    items:
+      - description: System clock of HDMI module.
+      - description: Mclk clock of HDMI audio.
+      - description: Bclk clock of HDMI audio.
+      - description: Pixel clock generated by HDMI module.
+
+  clock-names:
+    items:
+      - const: sysclk
+      - const: mclk
+      - const: bclk
+      - const: pclk
+
+  resets:
+    maxItems: 1
+
+  '#sound-dai-cells':
+    const: 0
+
+  port:
+    $ref: /schemas/graph.yaml#/properties/port
+    description:
+      Should contain a remote endpoint phandle of display controller device.
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - resets
+  - '#sound-dai-cells'
+  - port
+
+additionalProperties: false
+
+examples:
+  - |
+    hdmi: hdmi@29590000 {
+        compatible = "starfive,jh7110-inno-hdmi";
+        reg = <0x29590000 0x4000>;
+        interrupts = <99>;
+        clocks = <&voutcrg 17>,
+               <&voutcrg 15>,
+               <&voutcrg 16>,
+               <&hdmitx0_pixelclk>;
+        clock-names = "sysclk", "mclk","bclk","pclk";
+        resets = <&voutcrg 9>;
+        #sound-dai-cells = <0>;
+        hdmi_in: port {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            hdmi_in_dc: endpoint@0 {
+                reg = <0>;
+                remote-endpoint = <&dc_out_hdmi>;
+            };
+        };
+    };
diff --git a/MAINTAINERS b/MAINTAINERS
index 3be1bdfe8..46e51bca5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6751,6 +6751,13 @@ T:	git git://anongit.freedesktop.org/drm/drm-misc
 F:	Documentation/devicetree/bindings/display/panel/tpo,tpg110.yaml
 F:	drivers/gpu/drm/panel/panel-tpo-tpg110.c
 
+DRM DRIVERS FOR STARFIVE
+M:	Keith Zhao <keith.zhao@starfivetech.com>
+L:	dri-devel@lists.freedesktop.org
+S:	Maintained
+T:	git git://anongit.freedesktop.org/drm/drm-misc
+F:	Documentation/devicetree/bindings/display/starfive/
+
 DRM DRIVER FOR USB DISPLAYLINK VIDEO ADAPTERS
 M:	Dave Airlie <airlied@redhat.com>
 R:	Sean Paul <sean@poorly.run>
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 117+ messages in thread

* [PATCH v2 2/6] riscv: dts: starfive: jh7110: add dc controller and hdmi node
  2023-10-25 10:39 ` Keith Zhao
  (?)
@ 2023-10-25 10:39   ` Keith Zhao
  -1 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-25 10:39 UTC (permalink / raw)
  To: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Keith Zhao, Jack Zhu, Shengyang Chen,
	Changhuang Liang

Add the dc controller and hdmi node for the Starfive JH7110 SoC.

Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
---
 .../jh7110-starfive-visionfive-2.dtsi         | 91 +++++++++++++++++++
 arch/riscv/boot/dts/starfive/jh7110.dtsi      | 41 +++++++++
 2 files changed, 132 insertions(+)

diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
index de0f40a8b..97909b6d2 100644
--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
+++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
@@ -31,6 +31,25 @@ memory@40000000 {
 		reg = <0x0 0x40000000 0x1 0x0>;
 	};
 
+	reserved-memory {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+
+		/* vout applies for space from this CMA
+		 * Without this CMA reservation,
+		 * vout may not work properly.
+		 */
+		linux,cma {
+			compatible = "shared-dma-pool";
+			reusable;
+			size = <0x0 0x20000000>;
+			alignment = <0x0 0x1000>;
+			alloc-ranges = <0x0 0x70000000 0x0 0x20000000>;
+			linux,cma-default;
+		};
+	};
+
 	gpio-restart {
 		compatible = "gpio-restart";
 		gpios = <&sysgpio 35 GPIO_ACTIVE_HIGH>;
@@ -231,6 +250,41 @@ GPOEN_DISABLE,
 			slew-rate = <0>;
 		};
 	};
+
+	hdmi_pins: hdmi-0 {
+		hdmi-scl-pins {
+			pinmux = <GPIOMUX(0, GPOUT_SYS_HDMI_DDC_SCL,
+					     GPOEN_SYS_HDMI_DDC_SCL,
+					     GPI_SYS_HDMI_DDC_SCL)>;
+			input-enable;
+			bias-pull-up;
+		};
+
+		hdmi-sda-pins {
+			pinmux = <GPIOMUX(1, GPOUT_SYS_HDMI_DDC_SDA,
+					     GPOEN_SYS_HDMI_DDC_SDA,
+					     GPI_SYS_HDMI_DDC_SDA)>;
+			input-enable;
+			bias-pull-up;
+		};
+
+		hdmi-cec-pins {
+			pinmux = <GPIOMUX(14, GPOUT_SYS_HDMI_CEC_SDA,
+					     GPOEN_SYS_HDMI_CEC_SDA,
+					     GPI_SYS_HDMI_CEC_SDA)>;
+			input-enable;
+			bias-pull-up;
+		};
+
+		hdmi-hpd-pins {
+			pinmux = <GPIOMUX(15, GPOUT_HIGH,
+					     GPOEN_ENABLE,
+					     GPI_SYS_HDMI_HPD)>;
+			input-enable;
+			bias-disable; /* external pull-up */
+		};
+	};
+
 };
 
 &uart0 {
@@ -254,3 +308,40 @@ &U74_3 {
 &U74_4 {
 	cpu-supply = <&vdd_cpu>;
 };
+
+&voutcrg {
+	status = "okay";
+};
+
+&display {
+	status = "okay";
+};
+
+&hdmi {
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&hdmi_pins>;
+
+	hdmi_in: port {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		hdmi_in_dc: endpoint@0 {
+			reg = <0>;
+			remote-endpoint = <&dc_out_hdmi>;
+		};
+	};
+};
+
+&dc8200 {
+	status = "okay";
+
+	dc_out: port {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		dc_out_hdmi: endpoint@0 {
+			reg = <0>;
+			remote-endpoint = <&hdmi_in_dc>;
+		};
+
+	};
+};
diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi
index 0005fa163..1670452fb 100644
--- a/arch/riscv/boot/dts/starfive/jh7110.dtsi
+++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi
@@ -282,6 +282,11 @@ tdm_ext: tdm-ext-clock {
 		#clock-cells = <0>;
 	};
 
+	display: display-subsystem {
+		compatible = "starfive,display-subsystem";
+		ports = <&dc_out>;
+	};
+
 	soc {
 		compatible = "simple-bus";
 		interrupt-parent = <&plic>;
@@ -613,5 +618,41 @@ voutcrg: clock-controller@295c0000 {
 			#reset-cells = <1>;
 			power-domains = <&pwrc JH7110_PD_VOUT>;
 		};
+
+		dc8200: lcd-controller@29400000 {
+			compatible = "starfive,jh7110-dc8200";
+			reg = <0x0 0x29400000 0x0 0x100>,
+			      <0x0 0x29400800 0x0 0x2000>;
+			interrupts = <95>;
+			clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_DISP_AXI>,
+				<&voutcrg JH7110_VOUTCLK_DC8200_PIX0>,
+				<&voutcrg JH7110_VOUTCLK_DC8200_PIX1>,
+				<&voutcrg JH7110_VOUTCLK_DC8200_CORE>,
+				<&voutcrg JH7110_VOUTCLK_DC8200_AXI>,
+				<&voutcrg JH7110_VOUTCLK_DC8200_AHB>,
+				<&hdmitx0_pixelclk>,
+				<&voutcrg JH7110_VOUTCLK_DC8200_PIX>;
+			clock-names = "noc_bus", "channel0", "channel1",
+				      "dc_core", "axi_core", "ahb",
+				      "hdmi_tx", "dc_parent";
+			resets = <&voutcrg JH7110_VOUTRST_DC8200_AXI>,
+				 <&voutcrg JH7110_VOUTRST_DC8200_AHB>,
+				 <&voutcrg JH7110_VOUTRST_DC8200_CORE>;
+			reset-names = "axi","ahb", "core";
+		};
+
+		hdmi: hdmi@29590000 {
+			compatible = "starfive,jh7110-inno-hdmi";
+			reg = <0x0 0x29590000 0x0 0x4000>;
+			interrupts = <99>;
+
+			clocks = <&voutcrg JH7110_VOUTCLK_HDMI_TX_SYS>,
+				 <&voutcrg JH7110_VOUTCLK_HDMI_TX_MCLK>,
+				 <&voutcrg JH7110_VOUTCLK_HDMI_TX_BCLK>,
+				 <&hdmitx0_pixelclk>;
+			clock-names = "sysclk", "mclk", "bclk", "pclk";
+			resets = <&voutcrg JH7110_VOUTRST_HDMI_TX_HDMI>;
+			#sound-dai-cells = <0>;
+		};
 	};
 };
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 117+ messages in thread

* [PATCH v2 2/6] riscv: dts: starfive: jh7110: add dc controller and hdmi node
@ 2023-10-25 10:39   ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-25 10:39 UTC (permalink / raw)
  To: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Keith Zhao, Jack Zhu, Shengyang Chen,
	Changhuang Liang

Add the dc controller and hdmi node for the Starfive JH7110 SoC.

Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
---
 .../jh7110-starfive-visionfive-2.dtsi         | 91 +++++++++++++++++++
 arch/riscv/boot/dts/starfive/jh7110.dtsi      | 41 +++++++++
 2 files changed, 132 insertions(+)

diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
index de0f40a8b..97909b6d2 100644
--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
+++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
@@ -31,6 +31,25 @@ memory@40000000 {
 		reg = <0x0 0x40000000 0x1 0x0>;
 	};
 
+	reserved-memory {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+
+		/* vout applies for space from this CMA
+		 * Without this CMA reservation,
+		 * vout may not work properly.
+		 */
+		linux,cma {
+			compatible = "shared-dma-pool";
+			reusable;
+			size = <0x0 0x20000000>;
+			alignment = <0x0 0x1000>;
+			alloc-ranges = <0x0 0x70000000 0x0 0x20000000>;
+			linux,cma-default;
+		};
+	};
+
 	gpio-restart {
 		compatible = "gpio-restart";
 		gpios = <&sysgpio 35 GPIO_ACTIVE_HIGH>;
@@ -231,6 +250,41 @@ GPOEN_DISABLE,
 			slew-rate = <0>;
 		};
 	};
+
+	hdmi_pins: hdmi-0 {
+		hdmi-scl-pins {
+			pinmux = <GPIOMUX(0, GPOUT_SYS_HDMI_DDC_SCL,
+					     GPOEN_SYS_HDMI_DDC_SCL,
+					     GPI_SYS_HDMI_DDC_SCL)>;
+			input-enable;
+			bias-pull-up;
+		};
+
+		hdmi-sda-pins {
+			pinmux = <GPIOMUX(1, GPOUT_SYS_HDMI_DDC_SDA,
+					     GPOEN_SYS_HDMI_DDC_SDA,
+					     GPI_SYS_HDMI_DDC_SDA)>;
+			input-enable;
+			bias-pull-up;
+		};
+
+		hdmi-cec-pins {
+			pinmux = <GPIOMUX(14, GPOUT_SYS_HDMI_CEC_SDA,
+					     GPOEN_SYS_HDMI_CEC_SDA,
+					     GPI_SYS_HDMI_CEC_SDA)>;
+			input-enable;
+			bias-pull-up;
+		};
+
+		hdmi-hpd-pins {
+			pinmux = <GPIOMUX(15, GPOUT_HIGH,
+					     GPOEN_ENABLE,
+					     GPI_SYS_HDMI_HPD)>;
+			input-enable;
+			bias-disable; /* external pull-up */
+		};
+	};
+
 };
 
 &uart0 {
@@ -254,3 +308,40 @@ &U74_3 {
 &U74_4 {
 	cpu-supply = <&vdd_cpu>;
 };
+
+&voutcrg {
+	status = "okay";
+};
+
+&display {
+	status = "okay";
+};
+
+&hdmi {
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&hdmi_pins>;
+
+	hdmi_in: port {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		hdmi_in_dc: endpoint@0 {
+			reg = <0>;
+			remote-endpoint = <&dc_out_hdmi>;
+		};
+	};
+};
+
+&dc8200 {
+	status = "okay";
+
+	dc_out: port {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		dc_out_hdmi: endpoint@0 {
+			reg = <0>;
+			remote-endpoint = <&hdmi_in_dc>;
+		};
+
+	};
+};
diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi
index 0005fa163..1670452fb 100644
--- a/arch/riscv/boot/dts/starfive/jh7110.dtsi
+++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi
@@ -282,6 +282,11 @@ tdm_ext: tdm-ext-clock {
 		#clock-cells = <0>;
 	};
 
+	display: display-subsystem {
+		compatible = "starfive,display-subsystem";
+		ports = <&dc_out>;
+	};
+
 	soc {
 		compatible = "simple-bus";
 		interrupt-parent = <&plic>;
@@ -613,5 +618,41 @@ voutcrg: clock-controller@295c0000 {
 			#reset-cells = <1>;
 			power-domains = <&pwrc JH7110_PD_VOUT>;
 		};
+
+		dc8200: lcd-controller@29400000 {
+			compatible = "starfive,jh7110-dc8200";
+			reg = <0x0 0x29400000 0x0 0x100>,
+			      <0x0 0x29400800 0x0 0x2000>;
+			interrupts = <95>;
+			clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_DISP_AXI>,
+				<&voutcrg JH7110_VOUTCLK_DC8200_PIX0>,
+				<&voutcrg JH7110_VOUTCLK_DC8200_PIX1>,
+				<&voutcrg JH7110_VOUTCLK_DC8200_CORE>,
+				<&voutcrg JH7110_VOUTCLK_DC8200_AXI>,
+				<&voutcrg JH7110_VOUTCLK_DC8200_AHB>,
+				<&hdmitx0_pixelclk>,
+				<&voutcrg JH7110_VOUTCLK_DC8200_PIX>;
+			clock-names = "noc_bus", "channel0", "channel1",
+				      "dc_core", "axi_core", "ahb",
+				      "hdmi_tx", "dc_parent";
+			resets = <&voutcrg JH7110_VOUTRST_DC8200_AXI>,
+				 <&voutcrg JH7110_VOUTRST_DC8200_AHB>,
+				 <&voutcrg JH7110_VOUTRST_DC8200_CORE>;
+			reset-names = "axi","ahb", "core";
+		};
+
+		hdmi: hdmi@29590000 {
+			compatible = "starfive,jh7110-inno-hdmi";
+			reg = <0x0 0x29590000 0x0 0x4000>;
+			interrupts = <99>;
+
+			clocks = <&voutcrg JH7110_VOUTCLK_HDMI_TX_SYS>,
+				 <&voutcrg JH7110_VOUTCLK_HDMI_TX_MCLK>,
+				 <&voutcrg JH7110_VOUTCLK_HDMI_TX_BCLK>,
+				 <&hdmitx0_pixelclk>;
+			clock-names = "sysclk", "mclk", "bclk", "pclk";
+			resets = <&voutcrg JH7110_VOUTRST_HDMI_TX_HDMI>;
+			#sound-dai-cells = <0>;
+		};
 	};
 };
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 117+ messages in thread

* [PATCH v2 2/6] riscv: dts: starfive: jh7110: add dc controller and hdmi node
@ 2023-10-25 10:39   ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-25 10:39 UTC (permalink / raw)
  To: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Keith Zhao, Bjorn Andersson, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Shawn Guo, christian.koenig

Add the dc controller and hdmi node for the Starfive JH7110 SoC.

Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
---
 .../jh7110-starfive-visionfive-2.dtsi         | 91 +++++++++++++++++++
 arch/riscv/boot/dts/starfive/jh7110.dtsi      | 41 +++++++++
 2 files changed, 132 insertions(+)

diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
index de0f40a8b..97909b6d2 100644
--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
+++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
@@ -31,6 +31,25 @@ memory@40000000 {
 		reg = <0x0 0x40000000 0x1 0x0>;
 	};
 
+	reserved-memory {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+
+		/* vout applies for space from this CMA
+		 * Without this CMA reservation,
+		 * vout may not work properly.
+		 */
+		linux,cma {
+			compatible = "shared-dma-pool";
+			reusable;
+			size = <0x0 0x20000000>;
+			alignment = <0x0 0x1000>;
+			alloc-ranges = <0x0 0x70000000 0x0 0x20000000>;
+			linux,cma-default;
+		};
+	};
+
 	gpio-restart {
 		compatible = "gpio-restart";
 		gpios = <&sysgpio 35 GPIO_ACTIVE_HIGH>;
@@ -231,6 +250,41 @@ GPOEN_DISABLE,
 			slew-rate = <0>;
 		};
 	};
+
+	hdmi_pins: hdmi-0 {
+		hdmi-scl-pins {
+			pinmux = <GPIOMUX(0, GPOUT_SYS_HDMI_DDC_SCL,
+					     GPOEN_SYS_HDMI_DDC_SCL,
+					     GPI_SYS_HDMI_DDC_SCL)>;
+			input-enable;
+			bias-pull-up;
+		};
+
+		hdmi-sda-pins {
+			pinmux = <GPIOMUX(1, GPOUT_SYS_HDMI_DDC_SDA,
+					     GPOEN_SYS_HDMI_DDC_SDA,
+					     GPI_SYS_HDMI_DDC_SDA)>;
+			input-enable;
+			bias-pull-up;
+		};
+
+		hdmi-cec-pins {
+			pinmux = <GPIOMUX(14, GPOUT_SYS_HDMI_CEC_SDA,
+					     GPOEN_SYS_HDMI_CEC_SDA,
+					     GPI_SYS_HDMI_CEC_SDA)>;
+			input-enable;
+			bias-pull-up;
+		};
+
+		hdmi-hpd-pins {
+			pinmux = <GPIOMUX(15, GPOUT_HIGH,
+					     GPOEN_ENABLE,
+					     GPI_SYS_HDMI_HPD)>;
+			input-enable;
+			bias-disable; /* external pull-up */
+		};
+	};
+
 };
 
 &uart0 {
@@ -254,3 +308,40 @@ &U74_3 {
 &U74_4 {
 	cpu-supply = <&vdd_cpu>;
 };
+
+&voutcrg {
+	status = "okay";
+};
+
+&display {
+	status = "okay";
+};
+
+&hdmi {
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&hdmi_pins>;
+
+	hdmi_in: port {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		hdmi_in_dc: endpoint@0 {
+			reg = <0>;
+			remote-endpoint = <&dc_out_hdmi>;
+		};
+	};
+};
+
+&dc8200 {
+	status = "okay";
+
+	dc_out: port {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		dc_out_hdmi: endpoint@0 {
+			reg = <0>;
+			remote-endpoint = <&hdmi_in_dc>;
+		};
+
+	};
+};
diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi
index 0005fa163..1670452fb 100644
--- a/arch/riscv/boot/dts/starfive/jh7110.dtsi
+++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi
@@ -282,6 +282,11 @@ tdm_ext: tdm-ext-clock {
 		#clock-cells = <0>;
 	};
 
+	display: display-subsystem {
+		compatible = "starfive,display-subsystem";
+		ports = <&dc_out>;
+	};
+
 	soc {
 		compatible = "simple-bus";
 		interrupt-parent = <&plic>;
@@ -613,5 +618,41 @@ voutcrg: clock-controller@295c0000 {
 			#reset-cells = <1>;
 			power-domains = <&pwrc JH7110_PD_VOUT>;
 		};
+
+		dc8200: lcd-controller@29400000 {
+			compatible = "starfive,jh7110-dc8200";
+			reg = <0x0 0x29400000 0x0 0x100>,
+			      <0x0 0x29400800 0x0 0x2000>;
+			interrupts = <95>;
+			clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_DISP_AXI>,
+				<&voutcrg JH7110_VOUTCLK_DC8200_PIX0>,
+				<&voutcrg JH7110_VOUTCLK_DC8200_PIX1>,
+				<&voutcrg JH7110_VOUTCLK_DC8200_CORE>,
+				<&voutcrg JH7110_VOUTCLK_DC8200_AXI>,
+				<&voutcrg JH7110_VOUTCLK_DC8200_AHB>,
+				<&hdmitx0_pixelclk>,
+				<&voutcrg JH7110_VOUTCLK_DC8200_PIX>;
+			clock-names = "noc_bus", "channel0", "channel1",
+				      "dc_core", "axi_core", "ahb",
+				      "hdmi_tx", "dc_parent";
+			resets = <&voutcrg JH7110_VOUTRST_DC8200_AXI>,
+				 <&voutcrg JH7110_VOUTRST_DC8200_AHB>,
+				 <&voutcrg JH7110_VOUTRST_DC8200_CORE>;
+			reset-names = "axi","ahb", "core";
+		};
+
+		hdmi: hdmi@29590000 {
+			compatible = "starfive,jh7110-inno-hdmi";
+			reg = <0x0 0x29590000 0x0 0x4000>;
+			interrupts = <99>;
+
+			clocks = <&voutcrg JH7110_VOUTCLK_HDMI_TX_SYS>,
+				 <&voutcrg JH7110_VOUTCLK_HDMI_TX_MCLK>,
+				 <&voutcrg JH7110_VOUTCLK_HDMI_TX_BCLK>,
+				 <&hdmitx0_pixelclk>;
+			clock-names = "sysclk", "mclk", "bclk", "pclk";
+			resets = <&voutcrg JH7110_VOUTRST_HDMI_TX_HDMI>;
+			#sound-dai-cells = <0>;
+		};
 	};
 };
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 117+ messages in thread

* [PATCH v2 3/6] drm/fourcc: Add drm/vs tiled modifiers
  2023-10-25 10:39 ` Keith Zhao
  (?)
@ 2023-10-25 10:39   ` Keith Zhao
  -1 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-25 10:39 UTC (permalink / raw)
  To: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Keith Zhao, Jack Zhu, Shengyang Chen,
	Changhuang Liang

For each modifier, add the corresponding description

Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
---
 include/uapi/drm/drm_fourcc.h | 57 +++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h
index 8db7fd3f7..a580a848c 100644
--- a/include/uapi/drm/drm_fourcc.h
+++ b/include/uapi/drm/drm_fourcc.h
@@ -419,6 +419,7 @@ extern "C" {
 #define DRM_FORMAT_MOD_VENDOR_ARM     0x08
 #define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09
 #define DRM_FORMAT_MOD_VENDOR_AMLOGIC 0x0a
+#define DRM_FORMAT_MOD_VENDOR_VERISILICON 0x0b
 
 /* add more to the end as needed */
 
@@ -1562,6 +1563,62 @@ drm_fourcc_canonicalize_nvidia_format_mod(__u64 modifier)
 #define AMD_FMT_MOD_CLEAR(field) \
 	(~((__u64)AMD_FMT_MOD_##field##_MASK << AMD_FMT_MOD_##field##_SHIFT))
 
+#define DRM_FORMAT_MOD_VERISILICON_TYPE_NORMAL					0x00
+#define DRM_FORMAT_MOD_VERISILICON_TYPE_MASK					((__u64)0x3 << 54)
+
+#define fourcc_mod_vs_code(type, val) \
+	fourcc_mod_code(VERISILICON, ((((__u64)type) << 54) | (val)))
+
+#define DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK				0x1F
+
+/*
+ * An x-major 8x8 super tile consists of 64 8x8 sub-tiles in total.
+ * Each 8x8 sub-tile consists of four standard tiles .
+ * standard tiles (see Vivante 4x4 tiling layout)
+ */
+#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8		0x02
+
+/*
+ * A y-major 8x8 super tile consists of 64 8x8 sub-tiles in total.
+ * Each 8x8 sub-tile consists of four standard tiles .
+ * standard tiles (see Vivante 4x4 tiling layout)
+ */
+#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8		0x03
+
+/*
+ * An 8x8 tile consists of four standard tiles
+ * that are organized in Z-order.
+ * standard tiles (see Vivante 4x4 tiling layout)
+ */
+#define DRM_FORMAT_MOD_VERISILICON_TILE_8X8						0x04
+
+/*
+ * An 8x4 tile consists of two standard tiles
+ * that are organized in Z-order.
+ * standard tiles (see Vivante 4x4 tiling layout)
+ */
+#define DRM_FORMAT_MOD_VERISILICON_TILE_8X4						0x07
+
+/*
+ * An x-major 8x4 super tile consists of 128 8x4 sub-tiles in total.
+ * Each 8x4 sub-tile consists of two standard tiles.
+ * two standard tiles also same with DRM_FORMAT_MOD_VS_TILE_8X4
+ * standard tiles (see Vivante 4x4 tiling layout)
+ */
+#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4		0x0B
+
+/*
+ * A y-major 4x8 super tile consists of 128 4x8 sub-tiles in total.
+ * Each 4x8 sub-tile consists of two standard tiles.
+ * two standard tiles also same with DRM_FORMAT_MOD_VS_TILE_8X4
+ * standard tiles (see Vivante 4x4 tiling layout)
+ */
+#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8    0x0C
+
+#define fourcc_mod_vs_norm_code(tile) \
+	fourcc_mod_vs_code(DRM_FORMAT_MOD_VERISILICON_TYPE_NORMAL, \
+				(tile))
+
 #if defined(__cplusplus)
 }
 #endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 117+ messages in thread

* [PATCH v2 3/6] drm/fourcc: Add drm/vs tiled modifiers
@ 2023-10-25 10:39   ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-25 10:39 UTC (permalink / raw)
  To: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Keith Zhao, Jack Zhu, Shengyang Chen,
	Changhuang Liang

For each modifier, add the corresponding description

Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
---
 include/uapi/drm/drm_fourcc.h | 57 +++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h
index 8db7fd3f7..a580a848c 100644
--- a/include/uapi/drm/drm_fourcc.h
+++ b/include/uapi/drm/drm_fourcc.h
@@ -419,6 +419,7 @@ extern "C" {
 #define DRM_FORMAT_MOD_VENDOR_ARM     0x08
 #define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09
 #define DRM_FORMAT_MOD_VENDOR_AMLOGIC 0x0a
+#define DRM_FORMAT_MOD_VENDOR_VERISILICON 0x0b
 
 /* add more to the end as needed */
 
@@ -1562,6 +1563,62 @@ drm_fourcc_canonicalize_nvidia_format_mod(__u64 modifier)
 #define AMD_FMT_MOD_CLEAR(field) \
 	(~((__u64)AMD_FMT_MOD_##field##_MASK << AMD_FMT_MOD_##field##_SHIFT))
 
+#define DRM_FORMAT_MOD_VERISILICON_TYPE_NORMAL					0x00
+#define DRM_FORMAT_MOD_VERISILICON_TYPE_MASK					((__u64)0x3 << 54)
+
+#define fourcc_mod_vs_code(type, val) \
+	fourcc_mod_code(VERISILICON, ((((__u64)type) << 54) | (val)))
+
+#define DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK				0x1F
+
+/*
+ * An x-major 8x8 super tile consists of 64 8x8 sub-tiles in total.
+ * Each 8x8 sub-tile consists of four standard tiles .
+ * standard tiles (see Vivante 4x4 tiling layout)
+ */
+#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8		0x02
+
+/*
+ * A y-major 8x8 super tile consists of 64 8x8 sub-tiles in total.
+ * Each 8x8 sub-tile consists of four standard tiles .
+ * standard tiles (see Vivante 4x4 tiling layout)
+ */
+#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8		0x03
+
+/*
+ * An 8x8 tile consists of four standard tiles
+ * that are organized in Z-order.
+ * standard tiles (see Vivante 4x4 tiling layout)
+ */
+#define DRM_FORMAT_MOD_VERISILICON_TILE_8X8						0x04
+
+/*
+ * An 8x4 tile consists of two standard tiles
+ * that are organized in Z-order.
+ * standard tiles (see Vivante 4x4 tiling layout)
+ */
+#define DRM_FORMAT_MOD_VERISILICON_TILE_8X4						0x07
+
+/*
+ * An x-major 8x4 super tile consists of 128 8x4 sub-tiles in total.
+ * Each 8x4 sub-tile consists of two standard tiles.
+ * two standard tiles also same with DRM_FORMAT_MOD_VS_TILE_8X4
+ * standard tiles (see Vivante 4x4 tiling layout)
+ */
+#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4		0x0B
+
+/*
+ * A y-major 4x8 super tile consists of 128 4x8 sub-tiles in total.
+ * Each 4x8 sub-tile consists of two standard tiles.
+ * two standard tiles also same with DRM_FORMAT_MOD_VS_TILE_8X4
+ * standard tiles (see Vivante 4x4 tiling layout)
+ */
+#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8    0x0C
+
+#define fourcc_mod_vs_norm_code(tile) \
+	fourcc_mod_vs_code(DRM_FORMAT_MOD_VERISILICON_TYPE_NORMAL, \
+				(tile))
+
 #if defined(__cplusplus)
 }
 #endif
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 117+ messages in thread

* [PATCH v2 3/6] drm/fourcc: Add drm/vs tiled modifiers
@ 2023-10-25 10:39   ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-25 10:39 UTC (permalink / raw)
  To: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Keith Zhao, Bjorn Andersson, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Shawn Guo, christian.koenig

For each modifier, add the corresponding description

Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
---
 include/uapi/drm/drm_fourcc.h | 57 +++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h
index 8db7fd3f7..a580a848c 100644
--- a/include/uapi/drm/drm_fourcc.h
+++ b/include/uapi/drm/drm_fourcc.h
@@ -419,6 +419,7 @@ extern "C" {
 #define DRM_FORMAT_MOD_VENDOR_ARM     0x08
 #define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09
 #define DRM_FORMAT_MOD_VENDOR_AMLOGIC 0x0a
+#define DRM_FORMAT_MOD_VENDOR_VERISILICON 0x0b
 
 /* add more to the end as needed */
 
@@ -1562,6 +1563,62 @@ drm_fourcc_canonicalize_nvidia_format_mod(__u64 modifier)
 #define AMD_FMT_MOD_CLEAR(field) \
 	(~((__u64)AMD_FMT_MOD_##field##_MASK << AMD_FMT_MOD_##field##_SHIFT))
 
+#define DRM_FORMAT_MOD_VERISILICON_TYPE_NORMAL					0x00
+#define DRM_FORMAT_MOD_VERISILICON_TYPE_MASK					((__u64)0x3 << 54)
+
+#define fourcc_mod_vs_code(type, val) \
+	fourcc_mod_code(VERISILICON, ((((__u64)type) << 54) | (val)))
+
+#define DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK				0x1F
+
+/*
+ * An x-major 8x8 super tile consists of 64 8x8 sub-tiles in total.
+ * Each 8x8 sub-tile consists of four standard tiles .
+ * standard tiles (see Vivante 4x4 tiling layout)
+ */
+#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8		0x02
+
+/*
+ * A y-major 8x8 super tile consists of 64 8x8 sub-tiles in total.
+ * Each 8x8 sub-tile consists of four standard tiles .
+ * standard tiles (see Vivante 4x4 tiling layout)
+ */
+#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8		0x03
+
+/*
+ * An 8x8 tile consists of four standard tiles
+ * that are organized in Z-order.
+ * standard tiles (see Vivante 4x4 tiling layout)
+ */
+#define DRM_FORMAT_MOD_VERISILICON_TILE_8X8						0x04
+
+/*
+ * An 8x4 tile consists of two standard tiles
+ * that are organized in Z-order.
+ * standard tiles (see Vivante 4x4 tiling layout)
+ */
+#define DRM_FORMAT_MOD_VERISILICON_TILE_8X4						0x07
+
+/*
+ * An x-major 8x4 super tile consists of 128 8x4 sub-tiles in total.
+ * Each 8x4 sub-tile consists of two standard tiles.
+ * two standard tiles also same with DRM_FORMAT_MOD_VS_TILE_8X4
+ * standard tiles (see Vivante 4x4 tiling layout)
+ */
+#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4		0x0B
+
+/*
+ * A y-major 4x8 super tile consists of 128 4x8 sub-tiles in total.
+ * Each 4x8 sub-tile consists of two standard tiles.
+ * two standard tiles also same with DRM_FORMAT_MOD_VS_TILE_8X4
+ * standard tiles (see Vivante 4x4 tiling layout)
+ */
+#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8    0x0C
+
+#define fourcc_mod_vs_norm_code(tile) \
+	fourcc_mod_vs_code(DRM_FORMAT_MOD_VERISILICON_TYPE_NORMAL, \
+				(tile))
+
 #if defined(__cplusplus)
 }
 #endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 117+ messages in thread

* [PATCH v2 4/6] drm/vs: Register DRM device
  2023-10-25 10:39 ` Keith Zhao
  (?)
@ 2023-10-25 10:39   ` Keith Zhao
  -1 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-25 10:39 UTC (permalink / raw)
  To: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Keith Zhao, Jack Zhu, Shengyang Chen,
	Changhuang Liang

Implement drm device registration interface

Thomas Zimmermann wrote:
You are replacing almost all of the GEM DMA object's helper code.
Either inherit directly from drm_gem_object drop the dependency entirely,
or you could try to fit your code into GEM DMA as well

Eventually I found an answer and it was DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE
After I adapt this interface, the custom gem logic interfaces can be deleted.
It's a big shocking, clap!

Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
---
 drivers/gpu/drm/Kconfig                  |   2 +
 drivers/gpu/drm/Makefile                 |   1 +
 drivers/gpu/drm/verisilicon/Kconfig      |  15 ++
 drivers/gpu/drm/verisilicon/Makefile     |   7 +
 drivers/gpu/drm/verisilicon/vs_drv.c     | 227 +++++++++++++++++++++++
 drivers/gpu/drm/verisilicon/vs_drv.h     |  27 +++
 drivers/gpu/drm/verisilicon/vs_modeset.c |  36 ++++
 drivers/gpu/drm/verisilicon/vs_modeset.h |  10 +
 include/uapi/drm/vs_drm.h                |  50 +++++
 9 files changed, 375 insertions(+)
 create mode 100644 drivers/gpu/drm/verisilicon/Kconfig
 create mode 100644 drivers/gpu/drm/verisilicon/Makefile
 create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.h
 create mode 100644 include/uapi/drm/vs_drm.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index afb3b2f5f..9ede80ef9 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -363,6 +363,8 @@ source "drivers/gpu/drm/solomon/Kconfig"
 
 source "drivers/gpu/drm/sprd/Kconfig"
 
+source "drivers/gpu/drm/verisilicon/Kconfig"
+
 config DRM_HYPERV
 	tristate "DRM Support for Hyper-V synthetic video device"
 	depends on DRM && PCI && MMU && HYPERV
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 7a09a89b4..6db3bc397 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -194,3 +194,4 @@ obj-y			+= gud/
 obj-$(CONFIG_DRM_HYPERV) += hyperv/
 obj-y			+= solomon/
 obj-$(CONFIG_DRM_SPRD) += sprd/
+obj-$(CONFIG_DRM_VERISILICON) += verisilicon/
diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
new file mode 100644
index 000000000..3a361f8c8
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0
+config DRM_VERISILICON
+	tristate "DRM Support for VeriSilicon"
+	depends on DRM
+	select DRM_KMS_HELPER
+	select DRM_GEM_DMA_HELPER
+	select CMA
+	select DMA_CMA
+	help
+	  Choose this option if you have a VeriSilicon soc chipset.
+	  This driver provides VeriSilicon kernel mode
+	  setting and buffer management. It does not
+	  provide 2D or 3D acceleration.
+
+
diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
new file mode 100644
index 000000000..7d3be305b
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+
+vs_drm-objs := vs_drv.o \
+		vs_modeset.o 
+
+obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
+
diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
new file mode 100644
index 000000000..da7698c3d
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_drv.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/component.h>
+#include <linux/pm_runtime.h>
+
+#include <drm/drm_aperture.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_fbdev_generic.h>
+#include <drm/drm_file.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_module.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+
+#include "vs_drv.h"
+#include "vs_modeset.h"
+
+#define DRV_NAME	"verisilicon"
+#define DRV_DESC	"Verisilicon DRM driver"
+#define DRV_DATE	"20230516"
+#define DRV_MAJOR	1
+#define DRV_MINOR	0
+
+static int vs_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
+			      struct drm_mode_create_dumb *args)
+{
+	struct vs_drm_device *priv = to_vs_drm_private(dev);
+
+	unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+
+	if (args->bpp % 10)
+		args->pitch = ALIGN(pitch, priv->pitch_alignment);
+	else
+		/* for costum 10bit format with no bit gaps */
+		args->pitch = pitch;
+
+	return drm_gem_dma_dumb_create_internal(file, dev, args);
+}
+
+DEFINE_DRM_GEM_FOPS(vs_drm_fops);
+
+static struct drm_driver vs_drm_driver = {
+	.driver_features	= DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
+
+	DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(vs_gem_dumb_create),
+
+	.fops			= &vs_drm_fops,
+	.name			= DRV_NAME,
+	.desc			= DRV_DESC,
+	.date			= DRV_DATE,
+	.major			= DRV_MAJOR,
+	.minor			= DRV_MINOR,
+};
+
+static int vs_drm_bind(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct vs_drm_device *priv;
+	int ret;
+	struct drm_device *drm_dev;
+
+	/* Remove existing drivers that may own the framebuffer memory. */
+	ret = drm_aperture_remove_framebuffers(&vs_drm_driver);
+	if (ret)
+		return ret;
+
+	priv = devm_drm_dev_alloc(dev, &vs_drm_driver, struct vs_drm_device, base);
+	if (IS_ERR(priv))
+		return PTR_ERR(priv);
+
+	priv->pitch_alignment = 64;
+
+	ret = dma_set_coherent_mask(priv->base.dev, DMA_BIT_MASK(40));
+	if (ret)
+		return ret;
+
+	drm_dev = &priv->base;
+	platform_set_drvdata(pdev, drm_dev);
+
+	vs_mode_config_init(drm_dev);
+
+	/* Now try and bind all our sub-components */
+	ret = component_bind_all(dev, drm_dev);
+	if (ret)
+		return ret;
+
+	ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
+	if (ret)
+		return ret;
+
+	drm_mode_config_reset(drm_dev);
+
+	drm_kms_helper_poll_init(drm_dev);
+
+	ret = drm_dev_register(drm_dev, 0);
+	if (ret)
+		return ret;
+
+	drm_fbdev_generic_setup(drm_dev, 32);
+
+	return 0;
+}
+
+static void vs_drm_unbind(struct device *dev)
+{
+	struct drm_device *drm_dev = dev_get_drvdata(dev);
+
+	drm_dev_unregister(drm_dev);
+	drm_kms_helper_poll_fini(drm_dev);
+	component_unbind_all(drm_dev->dev, drm_dev);
+}
+
+static const struct component_master_ops vs_drm_ops = {
+	.bind = vs_drm_bind,
+	.unbind = vs_drm_unbind,
+};
+
+static struct platform_driver *drm_sub_drivers[] = {
+
+};
+
+static struct component_match *vs_drm_match_add(struct device *dev)
+{
+	struct component_match *match = NULL;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(drm_sub_drivers); ++i) {
+		struct platform_driver *drv = drm_sub_drivers[i];
+		struct device *p = NULL, *d;
+
+		while ((d = platform_find_device_by_driver(p, &drv->driver))) {
+			put_device(p);
+
+			component_match_add(dev, &match, component_compare_dev, d);
+			p = d;
+		}
+		put_device(p);
+	}
+
+	return match ? match : ERR_PTR(-ENODEV);
+}
+
+static int vs_drm_platform_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct component_match *match;
+
+	match = vs_drm_match_add(dev);
+	if (IS_ERR(match))
+		return PTR_ERR(match);
+
+	return component_master_add_with_match(dev, &vs_drm_ops, match);
+}
+
+static int vs_drm_platform_remove(struct platform_device *pdev)
+{
+	component_master_del(&pdev->dev, &vs_drm_ops);
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int vs_drm_suspend(struct device *dev)
+{
+	return drm_mode_config_helper_suspend(dev_get_drvdata(dev));
+}
+
+static int vs_drm_resume(struct device *dev)
+{
+	drm_mode_config_helper_resume(dev_get_drvdata(dev));
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(vs_drm_pm_ops, vs_drm_suspend, vs_drm_resume);
+
+static const struct of_device_id vs_drm_dt_ids[] = {
+	{ .compatible = "starfive,display-subsystem", },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(of, vs_drm_dt_ids);
+
+static struct platform_driver vs_drm_platform_driver = {
+	.probe = vs_drm_platform_probe,
+	.remove = vs_drm_platform_remove,
+
+	.driver = {
+		.name = DRV_NAME,
+		.of_match_table = vs_drm_dt_ids,
+		.pm = &vs_drm_pm_ops,
+	},
+};
+
+static int __init vs_drm_init(void)
+{
+	int ret;
+
+	ret = platform_register_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
+	if (ret)
+		return ret;
+
+	ret = drm_platform_driver_register(&vs_drm_platform_driver);
+	if (ret)
+		platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
+
+	return ret;
+}
+
+static void __exit vs_drm_fini(void)
+{
+	platform_driver_unregister(&vs_drm_platform_driver);
+	platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
+}
+
+module_init(vs_drm_init);
+module_exit(vs_drm_fini);
+
+MODULE_DESCRIPTION("VeriSilicon DRM Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
new file mode 100644
index 000000000..369ad22d6
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_drv.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_DRV_H__
+#define __VS_DRV_H__
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_managed.h>
+
+/*@pitch_alignment: buffer pitch alignment required by sub-devices.*/
+struct vs_drm_device {
+	struct drm_device base;
+	unsigned int pitch_alignment;
+};
+
+static inline struct vs_drm_device *
+to_vs_drm_private(const struct drm_device *dev)
+{
+	return container_of(dev, struct vs_drm_device, base);
+}
+
+#endif /* __VS_DRV_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.c b/drivers/gpu/drm/verisilicon/vs_modeset.c
new file mode 100644
index 000000000..151272067
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_modeset.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/module.h>
+
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+
+#include "vs_modeset.h"
+
+static const struct drm_mode_config_funcs vs_mode_config_funcs = {
+	.fb_create			 = drm_gem_fb_create,
+	.atomic_check		 = drm_atomic_helper_check,
+	.atomic_commit		 = drm_atomic_helper_commit,
+};
+
+static struct drm_mode_config_helper_funcs vs_mode_config_helpers = {
+	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
+};
+
+void vs_mode_config_init(struct drm_device *dev)
+{
+	drm_mode_config_init(dev);
+	dev->mode_config.fb_modifiers_not_supported = false;
+
+	dev->mode_config.min_width  = 0;
+	dev->mode_config.min_height = 0;
+	dev->mode_config.max_width  = 4096;
+	dev->mode_config.max_height = 4096;
+
+	dev->mode_config.funcs = &vs_mode_config_funcs;
+	dev->mode_config.helper_private = &vs_mode_config_helpers;
+}
diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.h b/drivers/gpu/drm/verisilicon/vs_modeset.h
new file mode 100644
index 000000000..ebfe6d612
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_modeset.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_FB_H__
+#define __VS_FB_H__
+
+void vs_mode_config_init(struct drm_device *dev);
+#endif /* __VS_FB_H__ */
diff --git a/include/uapi/drm/vs_drm.h b/include/uapi/drm/vs_drm.h
new file mode 100644
index 000000000..96b7fc95d
--- /dev/null
+++ b/include/uapi/drm/vs_drm.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_DRM_H__
+#define __VS_DRM_H__
+
+#include "drm.h"
+
+enum drm_vs_degamma_mode {
+	VS_DEGAMMA_DISABLE = 0,
+	VS_DEGAMMA_BT709 = 1,
+	VS_DEGAMMA_BT2020 = 2,
+};
+
+enum drm_vs_sync_dc_mode {
+	VS_SINGLE_DC = 0,
+	VS_MULTI_DC_PRIMARY = 1,
+	VS_MULTI_DC_SECONDARY = 2,
+};
+
+enum drm_vs_mmu_prefetch_mode {
+	VS_MMU_PREFETCH_DISABLE = 0,
+	VS_MMU_PREFETCH_ENABLE = 1,
+};
+
+struct drm_vs_watermark {
+	__u32 watermark;
+	__u8 qos_low;
+	__u8 qos_high;
+};
+
+struct drm_vs_color_mgmt {
+	__u32 colorkey;
+	__u32 colorkey_high;
+	__u32 clear_value;
+	bool  clear_enable;
+	bool  transparency;
+};
+
+struct drm_vs_roi {
+	bool enable;
+	__u16 roi_x;
+	__u16 roi_y;
+	__u16 roi_w;
+	__u16 roi_h;
+};
+
+#endif /* __VS_DRM_H__ */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 117+ messages in thread

* [PATCH v2 4/6] drm/vs: Register DRM device
@ 2023-10-25 10:39   ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-25 10:39 UTC (permalink / raw)
  To: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Keith Zhao, Jack Zhu, Shengyang Chen,
	Changhuang Liang

Implement drm device registration interface

Thomas Zimmermann wrote:
You are replacing almost all of the GEM DMA object's helper code.
Either inherit directly from drm_gem_object drop the dependency entirely,
or you could try to fit your code into GEM DMA as well

Eventually I found an answer and it was DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE
After I adapt this interface, the custom gem logic interfaces can be deleted.
It's a big shocking, clap!

Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
---
 drivers/gpu/drm/Kconfig                  |   2 +
 drivers/gpu/drm/Makefile                 |   1 +
 drivers/gpu/drm/verisilicon/Kconfig      |  15 ++
 drivers/gpu/drm/verisilicon/Makefile     |   7 +
 drivers/gpu/drm/verisilicon/vs_drv.c     | 227 +++++++++++++++++++++++
 drivers/gpu/drm/verisilicon/vs_drv.h     |  27 +++
 drivers/gpu/drm/verisilicon/vs_modeset.c |  36 ++++
 drivers/gpu/drm/verisilicon/vs_modeset.h |  10 +
 include/uapi/drm/vs_drm.h                |  50 +++++
 9 files changed, 375 insertions(+)
 create mode 100644 drivers/gpu/drm/verisilicon/Kconfig
 create mode 100644 drivers/gpu/drm/verisilicon/Makefile
 create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.h
 create mode 100644 include/uapi/drm/vs_drm.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index afb3b2f5f..9ede80ef9 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -363,6 +363,8 @@ source "drivers/gpu/drm/solomon/Kconfig"
 
 source "drivers/gpu/drm/sprd/Kconfig"
 
+source "drivers/gpu/drm/verisilicon/Kconfig"
+
 config DRM_HYPERV
 	tristate "DRM Support for Hyper-V synthetic video device"
 	depends on DRM && PCI && MMU && HYPERV
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 7a09a89b4..6db3bc397 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -194,3 +194,4 @@ obj-y			+= gud/
 obj-$(CONFIG_DRM_HYPERV) += hyperv/
 obj-y			+= solomon/
 obj-$(CONFIG_DRM_SPRD) += sprd/
+obj-$(CONFIG_DRM_VERISILICON) += verisilicon/
diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
new file mode 100644
index 000000000..3a361f8c8
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0
+config DRM_VERISILICON
+	tristate "DRM Support for VeriSilicon"
+	depends on DRM
+	select DRM_KMS_HELPER
+	select DRM_GEM_DMA_HELPER
+	select CMA
+	select DMA_CMA
+	help
+	  Choose this option if you have a VeriSilicon soc chipset.
+	  This driver provides VeriSilicon kernel mode
+	  setting and buffer management. It does not
+	  provide 2D or 3D acceleration.
+
+
diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
new file mode 100644
index 000000000..7d3be305b
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+
+vs_drm-objs := vs_drv.o \
+		vs_modeset.o 
+
+obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
+
diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
new file mode 100644
index 000000000..da7698c3d
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_drv.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/component.h>
+#include <linux/pm_runtime.h>
+
+#include <drm/drm_aperture.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_fbdev_generic.h>
+#include <drm/drm_file.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_module.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+
+#include "vs_drv.h"
+#include "vs_modeset.h"
+
+#define DRV_NAME	"verisilicon"
+#define DRV_DESC	"Verisilicon DRM driver"
+#define DRV_DATE	"20230516"
+#define DRV_MAJOR	1
+#define DRV_MINOR	0
+
+static int vs_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
+			      struct drm_mode_create_dumb *args)
+{
+	struct vs_drm_device *priv = to_vs_drm_private(dev);
+
+	unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+
+	if (args->bpp % 10)
+		args->pitch = ALIGN(pitch, priv->pitch_alignment);
+	else
+		/* for costum 10bit format with no bit gaps */
+		args->pitch = pitch;
+
+	return drm_gem_dma_dumb_create_internal(file, dev, args);
+}
+
+DEFINE_DRM_GEM_FOPS(vs_drm_fops);
+
+static struct drm_driver vs_drm_driver = {
+	.driver_features	= DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
+
+	DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(vs_gem_dumb_create),
+
+	.fops			= &vs_drm_fops,
+	.name			= DRV_NAME,
+	.desc			= DRV_DESC,
+	.date			= DRV_DATE,
+	.major			= DRV_MAJOR,
+	.minor			= DRV_MINOR,
+};
+
+static int vs_drm_bind(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct vs_drm_device *priv;
+	int ret;
+	struct drm_device *drm_dev;
+
+	/* Remove existing drivers that may own the framebuffer memory. */
+	ret = drm_aperture_remove_framebuffers(&vs_drm_driver);
+	if (ret)
+		return ret;
+
+	priv = devm_drm_dev_alloc(dev, &vs_drm_driver, struct vs_drm_device, base);
+	if (IS_ERR(priv))
+		return PTR_ERR(priv);
+
+	priv->pitch_alignment = 64;
+
+	ret = dma_set_coherent_mask(priv->base.dev, DMA_BIT_MASK(40));
+	if (ret)
+		return ret;
+
+	drm_dev = &priv->base;
+	platform_set_drvdata(pdev, drm_dev);
+
+	vs_mode_config_init(drm_dev);
+
+	/* Now try and bind all our sub-components */
+	ret = component_bind_all(dev, drm_dev);
+	if (ret)
+		return ret;
+
+	ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
+	if (ret)
+		return ret;
+
+	drm_mode_config_reset(drm_dev);
+
+	drm_kms_helper_poll_init(drm_dev);
+
+	ret = drm_dev_register(drm_dev, 0);
+	if (ret)
+		return ret;
+
+	drm_fbdev_generic_setup(drm_dev, 32);
+
+	return 0;
+}
+
+static void vs_drm_unbind(struct device *dev)
+{
+	struct drm_device *drm_dev = dev_get_drvdata(dev);
+
+	drm_dev_unregister(drm_dev);
+	drm_kms_helper_poll_fini(drm_dev);
+	component_unbind_all(drm_dev->dev, drm_dev);
+}
+
+static const struct component_master_ops vs_drm_ops = {
+	.bind = vs_drm_bind,
+	.unbind = vs_drm_unbind,
+};
+
+static struct platform_driver *drm_sub_drivers[] = {
+
+};
+
+static struct component_match *vs_drm_match_add(struct device *dev)
+{
+	struct component_match *match = NULL;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(drm_sub_drivers); ++i) {
+		struct platform_driver *drv = drm_sub_drivers[i];
+		struct device *p = NULL, *d;
+
+		while ((d = platform_find_device_by_driver(p, &drv->driver))) {
+			put_device(p);
+
+			component_match_add(dev, &match, component_compare_dev, d);
+			p = d;
+		}
+		put_device(p);
+	}
+
+	return match ? match : ERR_PTR(-ENODEV);
+}
+
+static int vs_drm_platform_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct component_match *match;
+
+	match = vs_drm_match_add(dev);
+	if (IS_ERR(match))
+		return PTR_ERR(match);
+
+	return component_master_add_with_match(dev, &vs_drm_ops, match);
+}
+
+static int vs_drm_platform_remove(struct platform_device *pdev)
+{
+	component_master_del(&pdev->dev, &vs_drm_ops);
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int vs_drm_suspend(struct device *dev)
+{
+	return drm_mode_config_helper_suspend(dev_get_drvdata(dev));
+}
+
+static int vs_drm_resume(struct device *dev)
+{
+	drm_mode_config_helper_resume(dev_get_drvdata(dev));
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(vs_drm_pm_ops, vs_drm_suspend, vs_drm_resume);
+
+static const struct of_device_id vs_drm_dt_ids[] = {
+	{ .compatible = "starfive,display-subsystem", },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(of, vs_drm_dt_ids);
+
+static struct platform_driver vs_drm_platform_driver = {
+	.probe = vs_drm_platform_probe,
+	.remove = vs_drm_platform_remove,
+
+	.driver = {
+		.name = DRV_NAME,
+		.of_match_table = vs_drm_dt_ids,
+		.pm = &vs_drm_pm_ops,
+	},
+};
+
+static int __init vs_drm_init(void)
+{
+	int ret;
+
+	ret = platform_register_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
+	if (ret)
+		return ret;
+
+	ret = drm_platform_driver_register(&vs_drm_platform_driver);
+	if (ret)
+		platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
+
+	return ret;
+}
+
+static void __exit vs_drm_fini(void)
+{
+	platform_driver_unregister(&vs_drm_platform_driver);
+	platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
+}
+
+module_init(vs_drm_init);
+module_exit(vs_drm_fini);
+
+MODULE_DESCRIPTION("VeriSilicon DRM Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
new file mode 100644
index 000000000..369ad22d6
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_drv.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_DRV_H__
+#define __VS_DRV_H__
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_managed.h>
+
+/*@pitch_alignment: buffer pitch alignment required by sub-devices.*/
+struct vs_drm_device {
+	struct drm_device base;
+	unsigned int pitch_alignment;
+};
+
+static inline struct vs_drm_device *
+to_vs_drm_private(const struct drm_device *dev)
+{
+	return container_of(dev, struct vs_drm_device, base);
+}
+
+#endif /* __VS_DRV_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.c b/drivers/gpu/drm/verisilicon/vs_modeset.c
new file mode 100644
index 000000000..151272067
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_modeset.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/module.h>
+
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+
+#include "vs_modeset.h"
+
+static const struct drm_mode_config_funcs vs_mode_config_funcs = {
+	.fb_create			 = drm_gem_fb_create,
+	.atomic_check		 = drm_atomic_helper_check,
+	.atomic_commit		 = drm_atomic_helper_commit,
+};
+
+static struct drm_mode_config_helper_funcs vs_mode_config_helpers = {
+	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
+};
+
+void vs_mode_config_init(struct drm_device *dev)
+{
+	drm_mode_config_init(dev);
+	dev->mode_config.fb_modifiers_not_supported = false;
+
+	dev->mode_config.min_width  = 0;
+	dev->mode_config.min_height = 0;
+	dev->mode_config.max_width  = 4096;
+	dev->mode_config.max_height = 4096;
+
+	dev->mode_config.funcs = &vs_mode_config_funcs;
+	dev->mode_config.helper_private = &vs_mode_config_helpers;
+}
diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.h b/drivers/gpu/drm/verisilicon/vs_modeset.h
new file mode 100644
index 000000000..ebfe6d612
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_modeset.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_FB_H__
+#define __VS_FB_H__
+
+void vs_mode_config_init(struct drm_device *dev);
+#endif /* __VS_FB_H__ */
diff --git a/include/uapi/drm/vs_drm.h b/include/uapi/drm/vs_drm.h
new file mode 100644
index 000000000..96b7fc95d
--- /dev/null
+++ b/include/uapi/drm/vs_drm.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_DRM_H__
+#define __VS_DRM_H__
+
+#include "drm.h"
+
+enum drm_vs_degamma_mode {
+	VS_DEGAMMA_DISABLE = 0,
+	VS_DEGAMMA_BT709 = 1,
+	VS_DEGAMMA_BT2020 = 2,
+};
+
+enum drm_vs_sync_dc_mode {
+	VS_SINGLE_DC = 0,
+	VS_MULTI_DC_PRIMARY = 1,
+	VS_MULTI_DC_SECONDARY = 2,
+};
+
+enum drm_vs_mmu_prefetch_mode {
+	VS_MMU_PREFETCH_DISABLE = 0,
+	VS_MMU_PREFETCH_ENABLE = 1,
+};
+
+struct drm_vs_watermark {
+	__u32 watermark;
+	__u8 qos_low;
+	__u8 qos_high;
+};
+
+struct drm_vs_color_mgmt {
+	__u32 colorkey;
+	__u32 colorkey_high;
+	__u32 clear_value;
+	bool  clear_enable;
+	bool  transparency;
+};
+
+struct drm_vs_roi {
+	bool enable;
+	__u16 roi_x;
+	__u16 roi_y;
+	__u16 roi_w;
+	__u16 roi_h;
+};
+
+#endif /* __VS_DRM_H__ */
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 117+ messages in thread

* [PATCH v2 4/6] drm/vs: Register DRM device
@ 2023-10-25 10:39   ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-25 10:39 UTC (permalink / raw)
  To: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Keith Zhao, Bjorn Andersson, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Shawn Guo, christian.koenig

Implement drm device registration interface

Thomas Zimmermann wrote:
You are replacing almost all of the GEM DMA object's helper code.
Either inherit directly from drm_gem_object drop the dependency entirely,
or you could try to fit your code into GEM DMA as well

Eventually I found an answer and it was DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE
After I adapt this interface, the custom gem logic interfaces can be deleted.
It's a big shocking, clap!

Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
---
 drivers/gpu/drm/Kconfig                  |   2 +
 drivers/gpu/drm/Makefile                 |   1 +
 drivers/gpu/drm/verisilicon/Kconfig      |  15 ++
 drivers/gpu/drm/verisilicon/Makefile     |   7 +
 drivers/gpu/drm/verisilicon/vs_drv.c     | 227 +++++++++++++++++++++++
 drivers/gpu/drm/verisilicon/vs_drv.h     |  27 +++
 drivers/gpu/drm/verisilicon/vs_modeset.c |  36 ++++
 drivers/gpu/drm/verisilicon/vs_modeset.h |  10 +
 include/uapi/drm/vs_drm.h                |  50 +++++
 9 files changed, 375 insertions(+)
 create mode 100644 drivers/gpu/drm/verisilicon/Kconfig
 create mode 100644 drivers/gpu/drm/verisilicon/Makefile
 create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.h
 create mode 100644 include/uapi/drm/vs_drm.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index afb3b2f5f..9ede80ef9 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -363,6 +363,8 @@ source "drivers/gpu/drm/solomon/Kconfig"
 
 source "drivers/gpu/drm/sprd/Kconfig"
 
+source "drivers/gpu/drm/verisilicon/Kconfig"
+
 config DRM_HYPERV
 	tristate "DRM Support for Hyper-V synthetic video device"
 	depends on DRM && PCI && MMU && HYPERV
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 7a09a89b4..6db3bc397 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -194,3 +194,4 @@ obj-y			+= gud/
 obj-$(CONFIG_DRM_HYPERV) += hyperv/
 obj-y			+= solomon/
 obj-$(CONFIG_DRM_SPRD) += sprd/
+obj-$(CONFIG_DRM_VERISILICON) += verisilicon/
diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
new file mode 100644
index 000000000..3a361f8c8
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0
+config DRM_VERISILICON
+	tristate "DRM Support for VeriSilicon"
+	depends on DRM
+	select DRM_KMS_HELPER
+	select DRM_GEM_DMA_HELPER
+	select CMA
+	select DMA_CMA
+	help
+	  Choose this option if you have a VeriSilicon soc chipset.
+	  This driver provides VeriSilicon kernel mode
+	  setting and buffer management. It does not
+	  provide 2D or 3D acceleration.
+
+
diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
new file mode 100644
index 000000000..7d3be305b
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+
+vs_drm-objs := vs_drv.o \
+		vs_modeset.o 
+
+obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
+
diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
new file mode 100644
index 000000000..da7698c3d
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_drv.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/component.h>
+#include <linux/pm_runtime.h>
+
+#include <drm/drm_aperture.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_fbdev_generic.h>
+#include <drm/drm_file.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_module.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+
+#include "vs_drv.h"
+#include "vs_modeset.h"
+
+#define DRV_NAME	"verisilicon"
+#define DRV_DESC	"Verisilicon DRM driver"
+#define DRV_DATE	"20230516"
+#define DRV_MAJOR	1
+#define DRV_MINOR	0
+
+static int vs_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
+			      struct drm_mode_create_dumb *args)
+{
+	struct vs_drm_device *priv = to_vs_drm_private(dev);
+
+	unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+
+	if (args->bpp % 10)
+		args->pitch = ALIGN(pitch, priv->pitch_alignment);
+	else
+		/* for costum 10bit format with no bit gaps */
+		args->pitch = pitch;
+
+	return drm_gem_dma_dumb_create_internal(file, dev, args);
+}
+
+DEFINE_DRM_GEM_FOPS(vs_drm_fops);
+
+static struct drm_driver vs_drm_driver = {
+	.driver_features	= DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
+
+	DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(vs_gem_dumb_create),
+
+	.fops			= &vs_drm_fops,
+	.name			= DRV_NAME,
+	.desc			= DRV_DESC,
+	.date			= DRV_DATE,
+	.major			= DRV_MAJOR,
+	.minor			= DRV_MINOR,
+};
+
+static int vs_drm_bind(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct vs_drm_device *priv;
+	int ret;
+	struct drm_device *drm_dev;
+
+	/* Remove existing drivers that may own the framebuffer memory. */
+	ret = drm_aperture_remove_framebuffers(&vs_drm_driver);
+	if (ret)
+		return ret;
+
+	priv = devm_drm_dev_alloc(dev, &vs_drm_driver, struct vs_drm_device, base);
+	if (IS_ERR(priv))
+		return PTR_ERR(priv);
+
+	priv->pitch_alignment = 64;
+
+	ret = dma_set_coherent_mask(priv->base.dev, DMA_BIT_MASK(40));
+	if (ret)
+		return ret;
+
+	drm_dev = &priv->base;
+	platform_set_drvdata(pdev, drm_dev);
+
+	vs_mode_config_init(drm_dev);
+
+	/* Now try and bind all our sub-components */
+	ret = component_bind_all(dev, drm_dev);
+	if (ret)
+		return ret;
+
+	ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
+	if (ret)
+		return ret;
+
+	drm_mode_config_reset(drm_dev);
+
+	drm_kms_helper_poll_init(drm_dev);
+
+	ret = drm_dev_register(drm_dev, 0);
+	if (ret)
+		return ret;
+
+	drm_fbdev_generic_setup(drm_dev, 32);
+
+	return 0;
+}
+
+static void vs_drm_unbind(struct device *dev)
+{
+	struct drm_device *drm_dev = dev_get_drvdata(dev);
+
+	drm_dev_unregister(drm_dev);
+	drm_kms_helper_poll_fini(drm_dev);
+	component_unbind_all(drm_dev->dev, drm_dev);
+}
+
+static const struct component_master_ops vs_drm_ops = {
+	.bind = vs_drm_bind,
+	.unbind = vs_drm_unbind,
+};
+
+static struct platform_driver *drm_sub_drivers[] = {
+
+};
+
+static struct component_match *vs_drm_match_add(struct device *dev)
+{
+	struct component_match *match = NULL;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(drm_sub_drivers); ++i) {
+		struct platform_driver *drv = drm_sub_drivers[i];
+		struct device *p = NULL, *d;
+
+		while ((d = platform_find_device_by_driver(p, &drv->driver))) {
+			put_device(p);
+
+			component_match_add(dev, &match, component_compare_dev, d);
+			p = d;
+		}
+		put_device(p);
+	}
+
+	return match ? match : ERR_PTR(-ENODEV);
+}
+
+static int vs_drm_platform_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct component_match *match;
+
+	match = vs_drm_match_add(dev);
+	if (IS_ERR(match))
+		return PTR_ERR(match);
+
+	return component_master_add_with_match(dev, &vs_drm_ops, match);
+}
+
+static int vs_drm_platform_remove(struct platform_device *pdev)
+{
+	component_master_del(&pdev->dev, &vs_drm_ops);
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int vs_drm_suspend(struct device *dev)
+{
+	return drm_mode_config_helper_suspend(dev_get_drvdata(dev));
+}
+
+static int vs_drm_resume(struct device *dev)
+{
+	drm_mode_config_helper_resume(dev_get_drvdata(dev));
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(vs_drm_pm_ops, vs_drm_suspend, vs_drm_resume);
+
+static const struct of_device_id vs_drm_dt_ids[] = {
+	{ .compatible = "starfive,display-subsystem", },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(of, vs_drm_dt_ids);
+
+static struct platform_driver vs_drm_platform_driver = {
+	.probe = vs_drm_platform_probe,
+	.remove = vs_drm_platform_remove,
+
+	.driver = {
+		.name = DRV_NAME,
+		.of_match_table = vs_drm_dt_ids,
+		.pm = &vs_drm_pm_ops,
+	},
+};
+
+static int __init vs_drm_init(void)
+{
+	int ret;
+
+	ret = platform_register_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
+	if (ret)
+		return ret;
+
+	ret = drm_platform_driver_register(&vs_drm_platform_driver);
+	if (ret)
+		platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
+
+	return ret;
+}
+
+static void __exit vs_drm_fini(void)
+{
+	platform_driver_unregister(&vs_drm_platform_driver);
+	platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
+}
+
+module_init(vs_drm_init);
+module_exit(vs_drm_fini);
+
+MODULE_DESCRIPTION("VeriSilicon DRM Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
new file mode 100644
index 000000000..369ad22d6
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_drv.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_DRV_H__
+#define __VS_DRV_H__
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_managed.h>
+
+/*@pitch_alignment: buffer pitch alignment required by sub-devices.*/
+struct vs_drm_device {
+	struct drm_device base;
+	unsigned int pitch_alignment;
+};
+
+static inline struct vs_drm_device *
+to_vs_drm_private(const struct drm_device *dev)
+{
+	return container_of(dev, struct vs_drm_device, base);
+}
+
+#endif /* __VS_DRV_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.c b/drivers/gpu/drm/verisilicon/vs_modeset.c
new file mode 100644
index 000000000..151272067
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_modeset.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/module.h>
+
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+
+#include "vs_modeset.h"
+
+static const struct drm_mode_config_funcs vs_mode_config_funcs = {
+	.fb_create			 = drm_gem_fb_create,
+	.atomic_check		 = drm_atomic_helper_check,
+	.atomic_commit		 = drm_atomic_helper_commit,
+};
+
+static struct drm_mode_config_helper_funcs vs_mode_config_helpers = {
+	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
+};
+
+void vs_mode_config_init(struct drm_device *dev)
+{
+	drm_mode_config_init(dev);
+	dev->mode_config.fb_modifiers_not_supported = false;
+
+	dev->mode_config.min_width  = 0;
+	dev->mode_config.min_height = 0;
+	dev->mode_config.max_width  = 4096;
+	dev->mode_config.max_height = 4096;
+
+	dev->mode_config.funcs = &vs_mode_config_funcs;
+	dev->mode_config.helper_private = &vs_mode_config_helpers;
+}
diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.h b/drivers/gpu/drm/verisilicon/vs_modeset.h
new file mode 100644
index 000000000..ebfe6d612
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_modeset.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_FB_H__
+#define __VS_FB_H__
+
+void vs_mode_config_init(struct drm_device *dev);
+#endif /* __VS_FB_H__ */
diff --git a/include/uapi/drm/vs_drm.h b/include/uapi/drm/vs_drm.h
new file mode 100644
index 000000000..96b7fc95d
--- /dev/null
+++ b/include/uapi/drm/vs_drm.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_DRM_H__
+#define __VS_DRM_H__
+
+#include "drm.h"
+
+enum drm_vs_degamma_mode {
+	VS_DEGAMMA_DISABLE = 0,
+	VS_DEGAMMA_BT709 = 1,
+	VS_DEGAMMA_BT2020 = 2,
+};
+
+enum drm_vs_sync_dc_mode {
+	VS_SINGLE_DC = 0,
+	VS_MULTI_DC_PRIMARY = 1,
+	VS_MULTI_DC_SECONDARY = 2,
+};
+
+enum drm_vs_mmu_prefetch_mode {
+	VS_MMU_PREFETCH_DISABLE = 0,
+	VS_MMU_PREFETCH_ENABLE = 1,
+};
+
+struct drm_vs_watermark {
+	__u32 watermark;
+	__u8 qos_low;
+	__u8 qos_high;
+};
+
+struct drm_vs_color_mgmt {
+	__u32 colorkey;
+	__u32 colorkey_high;
+	__u32 clear_value;
+	bool  clear_enable;
+	bool  transparency;
+};
+
+struct drm_vs_roi {
+	bool enable;
+	__u16 roi_x;
+	__u16 roi_y;
+	__u16 roi_w;
+	__u16 roi_h;
+};
+
+#endif /* __VS_DRM_H__ */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 117+ messages in thread

* [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
  2023-10-25 10:39 ` Keith Zhao
  (?)
@ 2023-10-25 10:39   ` Keith Zhao
  -1 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-25 10:39 UTC (permalink / raw)
  To: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Keith Zhao, Jack Zhu, Shengyang Chen,
	Changhuang Liang

add 2 crtcs and 8 planes in vs-drm

Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
---
 drivers/gpu/drm/verisilicon/Makefile   |    8 +-
 drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
 drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
 drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
 drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
 drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
 drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
 drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
 drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
 drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
 drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
 11 files changed, 4494 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h

diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
index 7d3be305b..1d48016ca 100644
--- a/drivers/gpu/drm/verisilicon/Makefile
+++ b/drivers/gpu/drm/verisilicon/Makefile
@@ -1,7 +1,11 @@
 # SPDX-License-Identifier: GPL-2.0
 
-vs_drm-objs := vs_drv.o \
-		vs_modeset.o 
+vs_drm-objs := vs_dc_hw.o \
+		vs_dc.o \
+		vs_crtc.o \
+		vs_drv.o \
+		vs_modeset.o \
+		vs_plane.o
 
 obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
 
diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
new file mode 100644
index 000000000..8a658ea77
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
@@ -0,0 +1,257 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/media-bus-format.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_vblank.h>
+#include <drm/vs_drm.h>
+
+#include "vs_crtc.h"
+#include "vs_dc.h"
+#include "vs_drv.h"
+
+static void vs_crtc_reset(struct drm_crtc *crtc)
+{
+	struct vs_crtc_state *state;
+
+	if (crtc->state) {
+		__drm_atomic_helper_crtc_destroy_state(crtc->state);
+
+		state = to_vs_crtc_state(crtc->state);
+		kfree(state);
+		crtc->state = NULL;
+	}
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return;
+
+	__drm_atomic_helper_crtc_reset(crtc, &state->base);
+}
+
+static struct drm_crtc_state *
+vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
+{
+	struct vs_crtc_state *ori_state;
+	struct vs_crtc_state *state;
+
+	if (!crtc->state)
+		return NULL;
+
+	ori_state = to_vs_crtc_state(crtc->state);
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return NULL;
+
+	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
+
+	state->output_fmt = ori_state->output_fmt;
+	state->encoder_type = ori_state->encoder_type;
+	state->bpp = ori_state->bpp;
+	state->underflow = ori_state->underflow;
+
+	return &state->base;
+}
+
+static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
+					 struct drm_crtc_state *state)
+{
+	__drm_atomic_helper_crtc_destroy_state(state);
+	kfree(to_vs_crtc_state(state));
+}
+
+static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	vs_dc_enable_vblank(dc, true);
+
+	return 0;
+}
+
+static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	vs_dc_enable_vblank(dc, false);
+}
+
+static const struct drm_crtc_funcs vs_crtc_funcs = {
+	.set_config		= drm_atomic_helper_set_config,
+	.page_flip		= drm_atomic_helper_page_flip,
+	.reset			= vs_crtc_reset,
+	.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
+	.atomic_destroy_state	= vs_crtc_atomic_destroy_state,
+	.enable_vblank		= vs_crtc_enable_vblank,
+	.disable_vblank		= vs_crtc_disable_vblank,
+};
+
+static u8 cal_pixel_bits(u32 bus_format)
+{
+	u8 bpp;
+
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_RGB565_1X16:
+	case MEDIA_BUS_FMT_UYVY8_1X16:
+		bpp = 16;
+		break;
+	case MEDIA_BUS_FMT_RGB666_1X18:
+	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+		bpp = 18;
+		break;
+	case MEDIA_BUS_FMT_UYVY10_1X20:
+		bpp = 20;
+		break;
+	case MEDIA_BUS_FMT_BGR888_1X24:
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+	case MEDIA_BUS_FMT_YUV8_1X24:
+		bpp = 24;
+		break;
+	case MEDIA_BUS_FMT_RGB101010_1X30:
+	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+	case MEDIA_BUS_FMT_YUV10_1X30:
+		bpp = 30;
+		break;
+	default:
+		bpp = 24;
+		break;
+	}
+
+	return bpp;
+}
+
+static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
+				  struct drm_atomic_state *state)
+{
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
+
+	vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
+
+	vs_dc_enable(dc, crtc);
+	drm_crtc_vblank_on(crtc);
+}
+
+static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
+				   struct drm_atomic_state *state)
+{
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	drm_crtc_vblank_off(crtc);
+
+	vs_dc_disable(dc, crtc);
+
+	if (crtc->state->event && !crtc->state->active) {
+		spin_lock_irq(&crtc->dev->event_lock);
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+		spin_unlock_irq(&crtc->dev->event_lock);
+
+		crtc->state->event = NULL;
+	}
+}
+
+static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
+				 struct drm_atomic_state *state)
+{
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
+
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct device *dev = vs_crtc->dev;
+	struct drm_property_blob *blob = crtc->state->gamma_lut;
+	struct drm_color_lut *lut;
+	struct vs_dc *dc = dev_get_drvdata(dev);
+
+	if (crtc_state->color_mgmt_changed) {
+		if (blob && blob->length) {
+			lut = blob->data;
+			vs_dc_set_gamma(dc, crtc, lut,
+					blob->length / sizeof(*lut));
+			vs_dc_enable_gamma(dc, crtc, true);
+		} else {
+			vs_dc_enable_gamma(dc, crtc, false);
+		}
+	}
+}
+
+static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
+				 struct drm_atomic_state *state)
+{
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct drm_pending_vblank_event *event = crtc->state->event;
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	vs_dc_commit(dc);
+
+	if (event) {
+		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+
+		spin_lock_irq(&crtc->dev->event_lock);
+		drm_crtc_arm_vblank_event(crtc, event);
+		spin_unlock_irq(&crtc->dev->event_lock);
+		crtc->state->event = NULL;
+	}
+}
+
+static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
+	.atomic_check = drm_crtc_helper_atomic_check,
+	.atomic_enable	= vs_crtc_atomic_enable,
+	.atomic_disable = vs_crtc_atomic_disable,
+	.atomic_begin	= vs_crtc_atomic_begin,
+	.atomic_flush	= vs_crtc_atomic_flush,
+};
+
+static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
+	{ VS_SINGLE_DC,				"single dc mode" },
+	{ VS_MULTI_DC_PRIMARY,		"primary dc for multi dc mode" },
+	{ VS_MULTI_DC_SECONDARY,	"secondary dc for multi dc mode" },
+};
+
+struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
+			       struct vs_dc_info *info)
+{
+	struct vs_crtc *crtc;
+	int ret;
+
+	if (!info)
+		return NULL;
+
+	crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
+	if (!crtc)
+		return NULL;
+
+	ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
+					 NULL, NULL, &vs_crtc_funcs,
+					 info->name ? info->name : NULL);
+	if (ret)
+		return NULL;
+
+	drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
+
+	if (info->gamma_size) {
+		ret = drm_mode_crtc_set_gamma_size(&crtc->base,
+						   info->gamma_size);
+		if (ret)
+			return NULL;
+
+		drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
+					   info->gamma_size);
+	}
+
+	crtc->max_bpc = info->max_bpc;
+	crtc->color_formats = info->color_formats;
+	return crtc;
+}
diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h
new file mode 100644
index 000000000..526dd63e5
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_crtc.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_CRTC_H__
+#define __VS_CRTC_H__
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "vs_type.h"
+
+struct vs_crtc_state {
+	struct drm_crtc_state base;
+
+	u32 output_fmt;
+	u8 encoder_type;
+	u8 bpp;
+	bool underflow;
+};
+
+struct vs_crtc {
+	struct drm_crtc base;
+	struct device *dev;
+	unsigned int max_bpc;
+	unsigned int color_formats;
+};
+
+struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
+			       struct vs_dc_info *info);
+
+static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc)
+{
+	return container_of(crtc, struct vs_crtc, base);
+}
+
+static inline struct vs_crtc_state *
+to_vs_crtc_state(struct drm_crtc_state *state)
+{
+	return container_of(state, struct vs_crtc_state, base);
+}
+#endif /* __VS_CRTC_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
new file mode 100644
index 000000000..b5ab92d98
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc.c
@@ -0,0 +1,1002 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_vblank.h>
+#include <drm/vs_drm.h>
+
+#include "vs_dc_hw.h"
+#include "vs_dc.h"
+#include "vs_drv.h"
+
+static const char * const vout_clocks[] = {
+	"noc_bus",
+	"channel0",
+	"channel1",
+	"dc_core",
+	"axi_core",
+	"ahb",
+	"hdmi_tx",
+	"dc_parent",
+
+};
+
+static const char * const vout_resets[] = {
+	"axi",
+	"ahb",
+	"core",
+};
+
+static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb)
+{
+	u8 f = FORMAT_A8R8G8B8;
+
+	switch (format) {
+	case DRM_FORMAT_XRGB4444:
+	case DRM_FORMAT_RGBX4444:
+	case DRM_FORMAT_XBGR4444:
+	case DRM_FORMAT_BGRX4444:
+		f = FORMAT_X4R4G4B4;
+		break;
+	case DRM_FORMAT_ARGB4444:
+	case DRM_FORMAT_RGBA4444:
+	case DRM_FORMAT_ABGR4444:
+	case DRM_FORMAT_BGRA4444:
+		f = FORMAT_A4R4G4B4;
+		break;
+	case DRM_FORMAT_XRGB1555:
+	case DRM_FORMAT_RGBX5551:
+	case DRM_FORMAT_XBGR1555:
+	case DRM_FORMAT_BGRX5551:
+		f = FORMAT_X1R5G5B5;
+		break;
+	case DRM_FORMAT_ARGB1555:
+	case DRM_FORMAT_RGBA5551:
+	case DRM_FORMAT_ABGR1555:
+	case DRM_FORMAT_BGRA5551:
+		f = FORMAT_A1R5G5B5;
+		break;
+	case DRM_FORMAT_RGB565:
+	case DRM_FORMAT_BGR565:
+		f = FORMAT_R5G6B5;
+		break;
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_RGBX8888:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_BGRX8888:
+		f = FORMAT_X8R8G8B8;
+		break;
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_RGBA8888:
+	case DRM_FORMAT_ABGR8888:
+	case DRM_FORMAT_BGRA8888:
+		f = FORMAT_A8R8G8B8;
+		break;
+	case DRM_FORMAT_YUYV:
+	case DRM_FORMAT_YVYU:
+		f = FORMAT_YUY2;
+		break;
+	case DRM_FORMAT_UYVY:
+	case DRM_FORMAT_VYUY:
+		f = FORMAT_UYVY;
+		break;
+	case DRM_FORMAT_YUV420:
+	case DRM_FORMAT_YVU420:
+		f = FORMAT_YV12;
+		break;
+	case DRM_FORMAT_NV21:
+		f = FORMAT_NV12;
+		break;
+	case DRM_FORMAT_NV16:
+	case DRM_FORMAT_NV61:
+		f = FORMAT_NV16;
+		break;
+	case DRM_FORMAT_P010:
+		f = FORMAT_P010;
+		break;
+	case DRM_FORMAT_ARGB2101010:
+	case DRM_FORMAT_RGBA1010102:
+	case DRM_FORMAT_ABGR2101010:
+	case DRM_FORMAT_BGRA1010102:
+		f = FORMAT_A2R10G10B10;
+		break;
+	case DRM_FORMAT_NV12:
+		f = FORMAT_NV12;
+		break;
+	case DRM_FORMAT_YUV444:
+		f = FORMAT_YUV444;
+		break;
+	default:
+		break;
+	}
+
+	fb->format = f;
+}
+
+static inline void update_swizzle(u32 format, struct dc_hw_fb *fb)
+{
+	fb->swizzle = SWIZZLE_ARGB;
+	fb->uv_swizzle = 0;
+
+	switch (format) {
+	case DRM_FORMAT_RGBX4444:
+	case DRM_FORMAT_RGBA4444:
+	case DRM_FORMAT_RGBX5551:
+	case DRM_FORMAT_RGBA5551:
+	case DRM_FORMAT_RGBX8888:
+	case DRM_FORMAT_RGBA8888:
+	case DRM_FORMAT_RGBA1010102:
+		fb->swizzle = SWIZZLE_RGBA;
+		break;
+	case DRM_FORMAT_XBGR4444:
+	case DRM_FORMAT_ABGR4444:
+	case DRM_FORMAT_XBGR1555:
+	case DRM_FORMAT_ABGR1555:
+	case DRM_FORMAT_BGR565:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_ABGR8888:
+	case DRM_FORMAT_ABGR2101010:
+		fb->swizzle = SWIZZLE_ABGR;
+		break;
+	case DRM_FORMAT_BGRX4444:
+	case DRM_FORMAT_BGRA4444:
+	case DRM_FORMAT_BGRX5551:
+	case DRM_FORMAT_BGRA5551:
+	case DRM_FORMAT_BGRX8888:
+	case DRM_FORMAT_BGRA8888:
+	case DRM_FORMAT_BGRA1010102:
+		fb->swizzle = SWIZZLE_BGRA;
+		break;
+	case DRM_FORMAT_YVYU:
+	case DRM_FORMAT_VYUY:
+	case DRM_FORMAT_NV21:
+	case DRM_FORMAT_NV61:
+		fb->uv_swizzle = 1;
+		break;
+	default:
+		break;
+	}
+}
+
+static inline void update_watermark(struct drm_property_blob *watermark,
+				    struct dc_hw_fb *fb)
+{
+	struct drm_vs_watermark *data;
+
+	fb->water_mark = 0;
+
+	if (watermark) {
+		data = watermark->data;
+		fb->water_mark = data->watermark & 0xFFFFF;
+	}
+}
+
+static inline u8 to_vs_rotation(unsigned int rotation)
+{
+	u8 rot;
+
+	switch (rotation & DRM_MODE_REFLECT_MASK) {
+	case DRM_MODE_REFLECT_X:
+		rot = FLIP_X;
+		return rot;
+	case DRM_MODE_REFLECT_Y:
+		rot = FLIP_Y;
+		return rot;
+	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
+		rot = FLIP_XY;
+		return rot;
+	default:
+		break;
+	}
+
+	switch (rotation & DRM_MODE_ROTATE_MASK) {
+	case DRM_MODE_ROTATE_0:
+		rot = ROT_0;
+		break;
+	case DRM_MODE_ROTATE_90:
+		rot = ROT_90;
+		break;
+	case DRM_MODE_ROTATE_180:
+		rot = ROT_180;
+		break;
+	case DRM_MODE_ROTATE_270:
+		rot = ROT_270;
+		break;
+	default:
+		rot = ROT_0;
+		break;
+	}
+
+	return rot;
+}
+
+static inline u8 to_vs_yuv_color_space(u32 color_space)
+{
+	u8 cs;
+
+	switch (color_space) {
+	case DRM_COLOR_YCBCR_BT601:
+		cs = COLOR_SPACE_601;
+		break;
+	case DRM_COLOR_YCBCR_BT709:
+		cs = COLOR_SPACE_709;
+		break;
+	case DRM_COLOR_YCBCR_BT2020:
+		cs = COLOR_SPACE_2020;
+		break;
+	default:
+		cs = COLOR_SPACE_601;
+		break;
+	}
+
+	return cs;
+}
+
+static inline u8 to_vs_tile_mode(u64 modifier)
+{
+	if (modifier == DRM_FORMAT_MOD_VIVANTE_TILED)
+		return DC_TILE_MODE4X4;
+
+	return (u8)(modifier & DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK);
+}
+
+static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc)
+{
+	u8 panel_num = dc->hw.info->panel_num;
+	u32 index = drm_crtc_index(crtc);
+	int i;
+
+	for (i = 0; i < panel_num; i++) {
+		if (index == dc->crtc[i]->base.index)
+			return i;
+	}
+
+	return 0;
+}
+
+static void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
+					  unsigned int alignment)
+{
+	struct vs_drm_device *priv = to_vs_drm_private(drm_dev);
+
+	if (alignment > priv->pitch_alignment)
+		priv->pitch_alignment = alignment;
+}
+
+static int plda_clk_rst_init(struct device *dev)
+{
+	int ret = 0;
+	struct vs_dc *dc = dev_get_drvdata(dev);
+
+	ret = clk_bulk_prepare_enable(dc->nclks, dc->clk_vout);
+	if (ret) {
+		dev_err(dev, "failed to enable clocks\n");
+		return ret;
+	}
+
+	ret = reset_control_bulk_deassert(dc->nrsts, dc->rst_vout);
+	return ret;
+}
+
+static void plda_clk_rst_deinit(struct device *dev)
+{
+	struct vs_dc *dc = dev_get_drvdata(dev);
+
+	reset_control_bulk_assert(dc->nrsts, dc->rst_vout);
+	clk_bulk_disable_unprepare(dc->nclks, dc->clk_vout);
+}
+
+static void dc_deinit(struct device *dev)
+{
+	struct vs_dc *dc = dev_get_drvdata(dev);
+
+	dc_hw_enable_interrupt(&dc->hw, 0);
+	dc_hw_deinit(&dc->hw);
+	plda_clk_rst_deinit(dev);
+}
+
+static int dc_init(struct device *dev)
+{
+	struct vs_dc *dc = dev_get_drvdata(dev);
+	int ret;
+
+	dc->first_frame = true;
+
+	ret = plda_clk_rst_init(dev);
+	if (ret < 0) {
+		dev_err(dev, "failed to init dc clk reset: %d\n", ret);
+		return ret;
+	}
+
+	ret = dc_hw_init(&dc->hw);
+	if (ret) {
+		dev_err(dev, "failed to init DC HW\n");
+		return ret;
+	}
+	return 0;
+}
+
+void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
+{
+	struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
+	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+	struct dc_hw_display display;
+
+	display.bus_format = crtc_state->output_fmt;
+	display.h_active = mode->hdisplay;
+	display.h_total = mode->htotal;
+	display.h_sync_start = mode->hsync_start;
+	display.h_sync_end = mode->hsync_end;
+	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+		display.h_sync_polarity = true;
+	else
+		display.h_sync_polarity = false;
+
+	display.v_active = mode->vdisplay;
+	display.v_total = mode->vtotal;
+	display.v_sync_start = mode->vsync_start;
+	display.v_sync_end = mode->vsync_end;
+
+	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+		display.v_sync_polarity = true;
+	else
+		display.v_sync_polarity = false;
+
+	display.id = to_vs_display_id(dc, crtc);
+
+	display.enable = true;
+
+	if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) {
+		dc_hw_set_out(&dc->hw, OUT_DPI, display.id);
+		clk_set_rate(dc->clk_vout[CLK_VOUT_SOC_PIX].clk, mode->clock * 1000);
+		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX1].clk,
+			       dc->clk_vout[CLK_VOUT_SOC_PIX].clk);
+	} else {
+		dc_hw_set_out(&dc->hw, OUT_DP, display.id);
+		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX0].clk,
+			       dc->clk_vout[CLK_VOUT_HDMI_PIX].clk);
+	}
+
+	dc_hw_setup_display(&dc->hw, &display);
+}
+
+void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc)
+{
+	struct dc_hw_display display;
+
+	display.id = to_vs_display_id(dc, crtc);
+	display.enable = false;
+
+	dc_hw_setup_display(&dc->hw, &display);
+}
+
+void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
+		     struct drm_color_lut *lut, unsigned int size)
+{
+	u16 i, r, g, b;
+	u8 bits, id;
+
+	if (size != dc->hw.info->gamma_size) {
+		drm_err(crtc->dev, "gamma size does not match!\n");
+		return;
+	}
+
+	id = to_vs_display_id(dc, crtc);
+
+	bits = dc->hw.info->gamma_bits;
+	for (i = 0; i < size; i++) {
+		r = drm_color_lut_extract(lut[i].red, bits);
+		g = drm_color_lut_extract(lut[i].green, bits);
+		b = drm_color_lut_extract(lut[i].blue, bits);
+		dc_hw_update_gamma(&dc->hw, id, i, r, g, b);
+	}
+}
+
+void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
+			bool enable)
+{
+	u8 id;
+
+	id = to_vs_display_id(dc, crtc);
+	dc_hw_enable_gamma(&dc->hw, id, enable);
+}
+
+void vs_dc_enable_vblank(struct vs_dc *dc, bool enable)
+{
+	dc_hw_enable_interrupt(&dc->hw, enable);
+}
+
+static u32 calc_factor(u32 src, u32 dest)
+{
+	u32 factor = 1 << 16;
+
+	if (src > 1 && dest > 1)
+		factor = ((src - 1) << 16) / (dest - 1);
+
+	return factor;
+}
+
+static void update_scale(struct drm_plane_state *state, struct dc_hw_roi *roi,
+			 struct dc_hw_scale *scale)
+{
+	int dst_w = drm_rect_width(&state->dst);
+	int dst_h = drm_rect_height(&state->dst);
+	int src_w, src_h, temp;
+
+	scale->enable = false;
+
+	if (roi->enable) {
+		src_w = roi->width;
+		src_h = roi->height;
+	} else {
+		src_w = drm_rect_width(&state->src) >> 16;
+		src_h = drm_rect_height(&state->src) >> 16;
+	}
+
+	if (drm_rotation_90_or_270(state->rotation)) {
+		temp = src_w;
+		src_w = src_h;
+		src_h = temp;
+	}
+
+	if (src_w != dst_w) {
+		scale->scale_factor_x = calc_factor(src_w, dst_w);
+		scale->enable = true;
+	} else {
+		scale->scale_factor_x = 1 << 16;
+	}
+	if (src_h != dst_h) {
+		scale->scale_factor_y = calc_factor(src_h, dst_h);
+		scale->enable = true;
+	} else {
+		scale->scale_factor_y = 1 << 16;
+	}
+}
+
+static void update_fb(struct vs_plane *plane, u8 display_id,
+		      struct dc_hw_fb *fb, struct drm_plane_state *state)
+{
+	struct vs_plane_state *plane_state = to_vs_plane_state(state);
+	struct drm_framebuffer *drm_fb = state->fb;
+	struct drm_rect *src = &state->src;
+
+	fb->display_id = display_id;
+	fb->y_address = plane_state->dma_addr[0];
+	fb->y_stride = drm_fb->pitches[0];
+	if (drm_fb->format->format == DRM_FORMAT_YVU420) {
+		fb->u_address = plane_state->dma_addr[2];
+		fb->v_address = plane_state->dma_addr[1];
+		fb->u_stride = drm_fb->pitches[2];
+		fb->v_stride = drm_fb->pitches[1];
+	} else {
+		fb->u_address = plane_state->dma_addr[1];
+		fb->v_address = plane_state->dma_addr[2];
+		fb->u_stride = drm_fb->pitches[1];
+		fb->v_stride = drm_fb->pitches[2];
+	}
+	fb->width = drm_rect_width(src) >> 16;
+	fb->height = drm_rect_height(src) >> 16;
+	fb->tile_mode = to_vs_tile_mode(drm_fb->modifier);
+	fb->rotation = to_vs_rotation(state->rotation);
+	fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding);
+	fb->zpos = state->zpos;
+	fb->enable = state->visible;
+	update_format(drm_fb->format->format, drm_fb->modifier, fb);
+	update_swizzle(drm_fb->format->format, fb);
+	update_watermark(plane_state->watermark, fb);
+	plane_state->status.tile_mode = fb->tile_mode;
+}
+
+static void update_degamma(struct vs_dc *dc, struct vs_plane *plane,
+			   struct vs_plane_state *plane_state)
+{
+	dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
+	plane_state->degamma_changed = false;
+}
+
+static void update_roi(struct vs_dc *dc, u8 id,
+		       struct vs_plane_state *plane_state,
+		       struct dc_hw_roi *roi,
+		       struct drm_plane_state *state)
+{
+	struct drm_vs_roi *data;
+	struct drm_rect *src = &state->src;
+	u16 src_w = drm_rect_width(src) >> 16;
+	u16 src_h = drm_rect_height(src) >> 16;
+
+	if (plane_state->roi) {
+		data = plane_state->roi->data;
+
+		if (data->enable) {
+			roi->x = data->roi_x;
+			roi->y = data->roi_y;
+			roi->width = (data->roi_x + data->roi_w > src_w) ?
+						 (src_w - data->roi_x) : data->roi_w;
+			roi->height = (data->roi_y + data->roi_h > src_h) ?
+						  (src_h - data->roi_y) : data->roi_h;
+			roi->enable = true;
+		} else {
+			roi->enable = false;
+		}
+
+		dc_hw_update_roi(&dc->hw, id, roi);
+	} else {
+		roi->enable = false;
+	}
+}
+
+static void update_color_mgmt(struct vs_dc *dc, u8 id,
+			      struct dc_hw_fb *fb,
+			      struct vs_plane_state *plane_state)
+{
+	struct drm_vs_color_mgmt *data;
+	struct dc_hw_colorkey colorkey;
+
+	if (plane_state->color_mgmt) {
+		data = plane_state->color_mgmt->data;
+
+		fb->clear_enable = data->clear_enable;
+		fb->clear_value = data->clear_value;
+
+		if (data->colorkey > data->colorkey_high)
+			data->colorkey = data->colorkey_high;
+
+		colorkey.colorkey = data->colorkey;
+		colorkey.colorkey_high = data->colorkey_high;
+		colorkey.transparency = (data->transparency) ?
+				DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE;
+		dc_hw_update_colorkey(&dc->hw, id, &colorkey);
+	}
+}
+
+static void update_plane(struct vs_dc *dc, struct vs_plane *plane,
+			 struct drm_plane *drm_plane,
+			 struct drm_atomic_state *drm_state)
+{
+	struct dc_hw_fb fb = {0};
+	struct dc_hw_scale scale;
+	struct dc_hw_position pos;
+	struct dc_hw_blend blend;
+	struct dc_hw_roi roi;
+	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
+									   drm_plane);
+	struct vs_plane_state *plane_state = to_vs_plane_state(state);
+	struct drm_rect *dest = &state->dst;
+	bool dec_enable = false;
+	u8 display_id = 0;
+
+	display_id = to_vs_display_id(dc, state->crtc);
+	update_fb(plane, display_id, &fb, state);
+	fb.dec_enable = dec_enable;
+
+	update_roi(dc, plane->id, plane_state, &roi, state);
+
+	update_scale(state, &roi, &scale);
+
+	if (plane_state->degamma_changed)
+		update_degamma(dc, plane, plane_state);
+
+	pos.start_x = dest->x1;
+	pos.start_y = dest->y1;
+	pos.end_x = dest->x2;
+	pos.end_y = dest->y2;
+
+	blend.alpha = (u8)(state->alpha >> 8);
+	blend.blend_mode = (u8)(state->pixel_blend_mode);
+
+	update_color_mgmt(dc, plane->id, &fb, plane_state);
+
+	dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
+}
+
+static void update_qos(struct vs_dc *dc, struct vs_plane *plane,
+		       struct drm_plane *drm_plane,
+		       struct drm_atomic_state *drm_state)
+{
+	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
+									   drm_plane);
+	struct vs_plane_state *plane_state = to_vs_plane_state(state);
+	struct drm_vs_watermark *data;
+	struct dc_hw_qos qos;
+
+	if (plane_state->watermark) {
+		data = plane_state->watermark->data;
+
+		if (data->qos_high) {
+			if (data->qos_low > data->qos_high)
+				data->qos_low = data->qos_high;
+
+			qos.low_value = data->qos_low & 0x0F;
+			qos.high_value = data->qos_high & 0x0F;
+			dc_hw_update_qos(&dc->hw, &qos);
+		}
+	}
+}
+
+static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor)
+{
+	u8 size_type;
+
+	switch (state->crtc_w) {
+	case 32:
+		size_type = CURSOR_SIZE_32X32;
+		break;
+	case 64:
+		size_type = CURSOR_SIZE_64X64;
+		break;
+	default:
+		size_type = CURSOR_SIZE_32X32;
+		break;
+	}
+
+	cursor->size = size_type;
+}
+
+static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
+				struct drm_plane *drm_plane,
+				struct drm_atomic_state *drm_state)
+{
+	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
+								       drm_plane);
+	struct vs_plane_state *plane_state = to_vs_plane_state(state);
+	struct drm_framebuffer *drm_fb = state->fb;
+	struct dc_hw_cursor cursor;
+
+	cursor.address = plane_state->dma_addr[0];
+	cursor.x = state->crtc_x;
+	cursor.y = state->crtc_y;
+	cursor.hot_x = drm_fb->hot_x;
+	cursor.hot_y = drm_fb->hot_y;
+	cursor.display_id = to_vs_display_id(dc, state->crtc);
+	update_cursor_size(state, &cursor);
+	cursor.enable = true;
+
+	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
+}
+
+void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
+			struct drm_plane *drm_plane,
+			struct drm_atomic_state *drm_state)
+{
+	update_plane(dc, plane, drm_plane, drm_state);
+	update_qos(dc, plane, drm_plane, drm_state);
+}
+
+void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
+			       struct drm_plane *drm_plane,
+			       struct drm_atomic_state *drm_state)
+{
+	update_cursor_plane(dc, plane, drm_plane, drm_state);
+}
+
+void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
+			 struct drm_plane_state *old_state)
+{
+	struct dc_hw_fb fb = {0};
+
+	fb.enable = false;
+	dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL, NULL, NULL);
+}
+
+void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
+				struct drm_plane_state *old_state)
+{
+	struct dc_hw_cursor cursor = {0};
+
+	cursor.enable = false;
+	cursor.display_id = to_vs_display_id(dc, old_state->crtc);
+	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
+}
+
+static bool vs_dc_mod_supported(const struct vs_plane_info *plane_info,
+				u64 modifier)
+{
+	const u64 *mods;
+
+	if (!plane_info->modifiers)
+		return false;
+
+	for (mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID; mods++) {
+		if (*mods == modifier)
+			return true;
+	}
+
+	return false;
+}
+
+int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
+		      struct drm_atomic_state *state)
+{
+	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+										 plane);
+
+	struct drm_framebuffer *fb = new_plane_state->fb;
+	const struct vs_plane_info *plane_info;
+	struct drm_crtc *crtc = new_plane_state->crtc;
+	struct drm_crtc_state *crtc_state;
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+
+	plane_info = &dc->hw.info->planes[vs_plane->id];
+
+	if (fb->width < plane_info->min_width ||
+	    fb->width > plane_info->max_width ||
+	    fb->height < plane_info->min_height ||
+	    fb->height > plane_info->max_height)
+		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n",
+			     vs_plane->id);
+
+	if (!vs_dc_mod_supported(plane_info, fb->modifier)) {
+		drm_err(plane->dev, "unsupported modifier on plane%d.\n", vs_plane->id);
+		return -EINVAL;
+	}
+
+	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
+	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
+						  plane_info->min_scale,
+						  plane_info->max_scale,
+						  true, true);
+}
+
+int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
+			     struct drm_atomic_state *state)
+{
+	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+									   plane);
+	struct drm_framebuffer *fb = new_plane_state->fb;
+	const struct vs_plane_info *plane_info;
+	struct drm_crtc *crtc = new_plane_state->crtc;
+	struct drm_crtc_state *crtc_state;
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+
+	plane_info = &dc->hw.info->planes[vs_plane->id];
+
+	if (fb->width < plane_info->min_width ||
+	    fb->width > plane_info->max_width ||
+	    fb->height < plane_info->min_height ||
+	    fb->height > plane_info->max_height)
+		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n", vs_plane->id);
+
+	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
+	if (IS_ERR(crtc_state))
+		return -EINVAL;
+
+	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
+						plane_info->min_scale,
+						plane_info->max_scale,
+						true, true);
+}
+
+static void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
+{
+	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
+
+	drm_crtc_handle_vblank(crtc);
+
+	vs_crtc_state->underflow = underflow;
+}
+
+static irqreturn_t dc_isr(int irq, void *data)
+{
+	struct vs_dc *dc = data;
+	struct vs_dc_info *dc_info = dc->hw.info;
+	u32 i, ret;
+
+	ret = dc_hw_get_interrupt(&dc->hw);
+
+	for (i = 0; i < dc_info->panel_num; i++)
+		vs_crtc_handle_vblank(&dc->crtc[i]->base, dc_hw_check_underflow(&dc->hw));
+
+	return IRQ_HANDLED;
+}
+
+void vs_dc_commit(struct vs_dc *dc)
+{
+	dc_hw_enable_shadow_register(&dc->hw, false);
+
+	dc_hw_commit(&dc->hw);
+
+	if (dc->first_frame)
+		dc->first_frame = false;
+
+	dc_hw_enable_shadow_register(&dc->hw, true);
+}
+
+static int dc_bind(struct device *dev, struct device *master, void *data)
+{
+	struct drm_device *drm_dev = data;
+	struct vs_dc *dc = dev_get_drvdata(dev);
+	struct device_node *port;
+	struct vs_crtc *crtc;
+	struct vs_dc_info *dc_info;
+	struct vs_plane *plane;
+	struct vs_plane_info *plane_info;
+	int i, ret;
+	u32 ctrc_mask = 0;
+
+	if (!drm_dev || !dc) {
+		dev_err(dev, "devices are not created.\n");
+		return -ENODEV;
+	}
+
+	ret = dc_init(dev);
+	if (ret < 0) {
+		drm_err(drm_dev, "Failed to initialize DC hardware.\n");
+		return ret;
+	}
+
+	port = of_get_child_by_name(dev->of_node, "port");
+	if (!port) {
+		drm_err(drm_dev, "no port node found\n");
+		return -ENODEV;
+	}
+	of_node_put(port);
+
+	dc_info = dc->hw.info;
+
+	for (i = 0; i < dc_info->panel_num; i++) {
+		crtc = vs_crtc_create(drm_dev, dc_info);
+		if (!crtc) {
+			drm_err(drm_dev, "Failed to create CRTC.\n");
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		crtc->base.port = port;
+		crtc->dev = dev;
+		dc->crtc[i] = crtc;
+		ctrc_mask |= drm_crtc_mask(&crtc->base);
+	}
+
+	for (i = 0; i < dc_info->plane_num; i++) {
+		plane_info = (struct vs_plane_info *)&dc_info->planes[i];
+
+		if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
+			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
+						drm_crtc_mask(&dc->crtc[0]->base));
+		} else if (!strcmp(plane_info->name, "Primary_1") ||
+				   !strcmp(plane_info->name, "Cursor_1")) {
+			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
+						drm_crtc_mask(&dc->crtc[1]->base));
+		} else {
+			plane = vs_plane_create(drm_dev, plane_info,
+						dc_info->layer_num, ctrc_mask);
+		}
+
+		if (IS_ERR(plane)) {
+			dev_err(dev, "failed to construct plane\n");
+			return PTR_ERR(plane);
+		}
+
+		plane->id = i;
+		dc->planes[i].id = plane_info->id;
+
+		if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
+			if (!strcmp(plane_info->name, "Primary"))
+				dc->crtc[0]->base.primary = &plane->base;
+			else
+				dc->crtc[1]->base.primary = &plane->base;
+			drm_dev->mode_config.min_width = plane_info->min_width;
+			drm_dev->mode_config.min_height =
+							plane_info->min_height;
+			drm_dev->mode_config.max_width = plane_info->max_width;
+			drm_dev->mode_config.max_height =
+							plane_info->max_height;
+		}
+
+		if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
+			if (!strcmp(plane_info->name, "Cursor"))
+				dc->crtc[0]->base.cursor = &plane->base;
+			else
+				dc->crtc[1]->base.cursor = &plane->base;
+			drm_dev->mode_config.cursor_width =
+							plane_info->max_width;
+			drm_dev->mode_config.cursor_height =
+							plane_info->max_height;
+		}
+	}
+
+	vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
+	return 0;
+}
+
+static void dc_unbind(struct device *dev, struct device *master, void *data)
+{
+	dc_deinit(dev);
+}
+
+const struct component_ops dc_component_ops = {
+	.bind = dc_bind,
+	.unbind = dc_unbind,
+};
+
+static const struct of_device_id dc_driver_dt_match[] = {
+	{ .compatible = "starfive,jh7110-dc8200", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
+
+static int dc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct vs_dc *dc;
+	int irq, ret, i;
+
+	dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
+	if (!dc)
+		return -ENOMEM;
+
+	dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(dc->hw.hi_base))
+		return PTR_ERR(dc->hw.hi_base);
+
+	dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
+	if (IS_ERR(dc->hw.reg_base))
+		return PTR_ERR(dc->hw.reg_base);
+
+	dc->nclks = ARRAY_SIZE(dc->clk_vout);
+	for (i = 0; i < dc->nclks; ++i)
+		dc->clk_vout[i].id = vout_clocks[i];
+	ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
+	if (ret) {
+		dev_err(dev, "Failed to get clk controls\n");
+		return ret;
+	}
+
+	dc->nrsts = ARRAY_SIZE(dc->rst_vout);
+	for (i = 0; i < dc->nrsts; ++i)
+		dc->rst_vout[i].id = vout_resets[i];
+	ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
+						 dc->rst_vout);
+	if (ret) {
+		dev_err(dev, "Failed to get reset controls\n");
+		return ret;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+
+	ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
+	if (ret < 0) {
+		dev_err(dev, "Failed to install irq:%u.\n", irq);
+		return ret;
+	}
+
+	dev_set_drvdata(dev, dc);
+
+	return component_add(dev, &dc_component_ops);
+}
+
+static int dc_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	component_del(dev, &dc_component_ops);
+
+	dev_set_drvdata(dev, NULL);
+
+	return 0;
+}
+
+struct platform_driver dc_platform_driver = {
+	.probe = dc_probe,
+	.remove = dc_remove,
+	.driver = {
+		.name = "vs-dc",
+		.of_match_table = of_match_ptr(dc_driver_dt_match),
+	},
+};
+
+MODULE_AUTHOR("StarFive Corporation");
+MODULE_DESCRIPTION("VeriSilicon DC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
new file mode 100644
index 000000000..8e96fd32c
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_DC_H__
+#define __VS_DC_H__
+
+#include <linux/clk.h>
+#include <linux/mm_types.h>
+#include <linux/reset.h>
+
+#include <drm/drm_fourcc.h>
+#include <drm/drm_modes.h>
+
+#include "vs_crtc.h"
+#include "vs_dc_hw.h"
+#include "vs_plane.h"
+
+struct vs_dc_plane {
+	enum dc_hw_plane_id id;
+};
+
+enum vout_clk {
+	CLK_VOUT_NOC_DISP = 0,
+	CLK_VOUT_PIX0,
+	CLK_VOUT_PIX1,
+	CLK_VOUT_CORE,
+	CLK_VOUT_AXI,
+	CLK_VOUT_AHB,
+	CLK_VOUT_HDMI_PIX,
+	CLK_VOUT_SOC_PIX,
+	CLK_VOUT_NUM
+};
+
+enum rst_vout {
+	RST_VOUT_AXI = 0,
+	RST_VOUT_AHB,
+	RST_VOUT_CORE,
+	RST_VOUT_NUM
+};
+
+struct vs_dc {
+	struct vs_crtc		*crtc[DC_DISPLAY_NUM];
+	struct dc_hw		hw;
+	bool			first_frame;
+
+	struct vs_dc_plane	planes[PLANE_NUM];
+	struct clk_bulk_data	clk_vout[CLK_VOUT_NUM];
+	int			nclks;
+	struct reset_control_bulk_data rst_vout[RST_VOUT_NUM];
+	int			nrsts;
+};
+
+void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc);
+void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc);
+
+void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
+		     struct drm_color_lut *lut, unsigned int size);
+void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc, bool enable);
+void vs_dc_enable_vblank(struct vs_dc *dc, bool enable);
+void vs_dc_commit(struct vs_dc *dc);
+void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
+			struct drm_plane *drm_plane,
+			struct drm_atomic_state *drm_state);
+void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
+			 struct drm_plane_state *old_state);
+int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
+		      struct drm_atomic_state *state);
+void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
+			       struct drm_plane *drm_plane,
+			       struct drm_atomic_state *drm_state);
+void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
+				struct drm_plane_state *old_state);
+int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
+			     struct drm_atomic_state *state);
+
+extern struct platform_driver dc_platform_driver;
+
+#endif /* __VS_DC_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
new file mode 100644
index 000000000..e5acb7eb4
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
@@ -0,0 +1,1959 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/bits.h>
+#include <linux/io.h>
+#include <linux/media-bus-format.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_fourcc.h>
+#include <drm/vs_drm.h>
+
+#include "vs_dc_hw.h"
+#include "vs_type.h"
+
+static const u32 horkernel[] = {
+	0x00000000, 0x20000000, 0x00002000, 0x00000000,
+	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
+	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
+	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
+	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
+	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
+	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
+	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
+	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
+	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
+	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
+	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
+	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
+	0x00000000, 0x00000000, 0x00000000, 0x01470000,
+	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
+	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
+	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
+	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00004000, 0x00000000,
+	0x00000000, 0x00000000, 0x20002000, 0x00000000,
+	0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
+	0x00000000, 0x00000000, 0x00000000, 0x27e1181f,
+	0x00000000, 0x00000000, 0x00000000, 0x14680000,
+	0x00002b98, 0x00000000, 0x00000000, 0x00000000,
+	0x2f1010f0, 0x00000000, 0x00000000, 0x00000000,
+	0x0dc70000, 0x00003239, 0x00000000, 0x00000000,
+	0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
+	0x00000000, 0x087f0000, 0x00003781, 0x00000000,
+	0x00000000, 0x00000000, 0x399a0666, 0x00000000,
+	0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
+	0x00000000, 0x00000000, 0x00000000, 0x3cc4033c,
+	0x00000000, 0x00000000, 0x00000000, 0x021f0000,
+};
+
+#define H_COEF_SIZE (sizeof(horkernel) / sizeof(u32))
+
+static const u32 verkernel[] = {
+	0x00000000, 0x20000000, 0x00002000, 0x00000000,
+	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
+	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
+	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
+	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
+	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
+	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
+	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
+	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
+	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
+	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
+	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
+	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
+	0x00000000, 0x00000000, 0x00000000, 0x01470000,
+	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
+	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
+	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
+	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00004000, 0x00000000,
+	0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab,
+	0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
+	0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+};
+
+#define V_COEF_SIZE (sizeof(verkernel) / sizeof(u32))
+
+/*
+ * RGB 709->2020 conversion parameters
+ */
+static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = {
+	10279,	5395,	709,
+	1132,	15065,	187,
+	269,	1442,	14674
+};
+
+/*
+ * YUV601 to RGB conversion parameters
+ * YUV2RGB[0]  - [8] : C0 - C8;
+ * YUV2RGB[9]  - [11]: D0 - D2;
+ * YUV2RGB[12] - [13]: Y clamp min & max calue;
+ * YUV2RGB[14] - [15]: UV clamp min & max calue;
+ */
+static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
+	1196,	0,			1640,	1196,
+	-404,	-836,		1196,	2076,
+	0,		-916224,	558336,	-1202944,
+	64,		 940,		64,		960
+};
+
+/*
+ * YUV709 to RGB conversion parameters
+ * YUV2RGB[0]  - [8] : C0 - C8;
+ * YUV2RGB[9]  - [11]: D0 - D2;
+ * YUV2RGB[12] - [13]: Y clamp min & max calue;
+ * YUV2RGB[14] - [15]: UV clamp min & max calue;
+ */
+static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
+	1196,		0,		1844,	1196,
+	-220,		-548,	1196,	2172,
+	0,			-1020672, 316672,  -1188608,
+	64,			940,		64,		960
+};
+
+/*
+ * YUV2020 to RGB conversion parameters
+ * YUV2RGB[0]  - [8] : C0 - C8;
+ * YUV2RGB[9]  - [11]: D0 - D2;
+ * YUV2RGB[12] - [13]: Y clamp min & max calue;
+ * YUV2RGB[14] - [15]: UV clamp min & max calue;
+ */
+static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
+	1196, 0, 1724, 1196,
+	-192, -668, 1196, 2200,
+	0, -959232, 363776, -1202944,
+	64, 940, 64, 960
+};
+
+/*
+ * RGB to YUV2020 conversion parameters
+ * RGB2YUV[0] - [8] : C0 - C8;
+ * RGB2YUV[9] - [11]: D0 - D2;
+ */
+static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = {
+	230,	594,	52,
+	-125,  -323,	448,
+	448,   -412,   -36,
+	64,		512,	512
+};
+
+/*
+ * Degamma table for 709 color space data.
+ */
+static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005,
+	0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024,
+	0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064,
+	0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce,
+	0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166,
+	0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230,
+	0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331,
+	0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c,
+	0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
+	0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d,
+	0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999,
+	0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb,
+	0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65,
+	0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a,
+	0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b,
+	0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc,
+	0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e,
+	0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
+	0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e,
+	0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf,
+	0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea,
+	0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f,
+	0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930,
+	0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f,
+	0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee,
+	0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde,
+	0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
+	0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9,
+	0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406,
+	0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a,
+	0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576,
+	0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd,
+	0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
+};
+
+/*
+ * Degamma table for 2020 color space data.
+ */
+static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
+	0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006,
+	0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a,
+	0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f,
+	0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
+	0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024,
+	0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035,
+	0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d,
+	0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d,
+	0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098,
+	0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3,
+	0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123,
+	0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d,
+	0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
+	0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6,
+	0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1,
+	0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d,
+	0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6,
+	0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920,
+	0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a,
+	0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033,
+	0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593,
+	0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
+	0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656,
+	0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332,
+	0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480,
+	0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1,
+	0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99,
+	0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
+};
+
+/* one is for primary plane and the other is for all overlay planes */
+static const struct dc_hw_plane_reg dc_plane_reg[] = {
+	{
+	.y_address		= DC_FRAMEBUFFER_ADDRESS,
+	.u_address		= DC_FRAMEBUFFER_U_ADDRESS,
+	.v_address		= DC_FRAMEBUFFER_V_ADDRESS,
+	.y_stride		= DC_FRAMEBUFFER_STRIDE,
+	.u_stride		= DC_FRAMEBUFFER_U_STRIDE,
+	.v_stride		= DC_FRAMEBUFFER_V_STRIDE,
+	.size			= DC_FRAMEBUFFER_SIZE,
+	.top_left		= DC_FRAMEBUFFER_TOP_LEFT,
+	.bottom_right	= DC_FRAMEBUFFER_BOTTOM_RIGHT,
+	.scale_factor_x			= DC_FRAMEBUFFER_SCALE_FACTOR_X,
+	.scale_factor_y			= DC_FRAMEBUFFER_SCALE_FACTOR_Y,
+	.h_filter_coef_index	= DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
+	.h_filter_coef_data		= DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
+	.v_filter_coef_index	= DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
+	.v_filter_coef_data		= DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
+	.init_offset			= DC_FRAMEBUFFER_INIT_OFFSET,
+	.color_key				= DC_FRAMEBUFFER_COLOR_KEY,
+	.color_key_high			= DC_FRAMEBUFFER_COLOR_KEY_HIGH,
+	.clear_value			= DC_FRAMEBUFFER_CLEAR_VALUE,
+	.color_table_index		= DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
+	.color_table_data		= DC_FRAMEBUFFER_COLOR_TABLE_DATA,
+	.scale_config			= DC_FRAMEBUFFER_SCALE_CONFIG,
+	.water_mark				= DC_FRAMEBUFFER_WATER_MARK,
+	.degamma_index			= DC_FRAMEBUFFER_DEGAMMA_INDEX,
+	.degamma_data			= DC_FRAMEBUFFER_DEGAMMA_DATA,
+	.degamma_ex_data		= DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
+	.src_global_color		= DC_FRAMEBUFFER_SRC_GLOBAL_COLOR,
+	.dst_global_color		= DC_FRAMEBUFFER_DST_GLOBAL_COLOR,
+	.blend_config			= DC_FRAMEBUFFER_BLEND_CONFIG,
+	.roi_origin				= DC_FRAMEBUFFER_ROI_ORIGIN,
+	.roi_size				= DC_FRAMEBUFFER_ROI_SIZE,
+	.yuv_to_rgb_coef0			= DC_FRAMEBUFFER_YUVTORGB_COEF0,
+	.yuv_to_rgb_coef1			= DC_FRAMEBUFFER_YUVTORGB_COEF1,
+	.yuv_to_rgb_coef2			= DC_FRAMEBUFFER_YUVTORGB_COEF2,
+	.yuv_to_rgb_coef3			= DC_FRAMEBUFFER_YUVTORGB_COEF3,
+	.yuv_to_rgb_coef4			= DC_FRAMEBUFFER_YUVTORGB_COEF4,
+	.yuv_to_rgb_coefd0			= DC_FRAMEBUFFER_YUVTORGB_COEFD0,
+	.yuv_to_rgb_coefd1			= DC_FRAMEBUFFER_YUVTORGB_COEFD1,
+	.yuv_to_rgb_coefd2			= DC_FRAMEBUFFER_YUVTORGB_COEFD2,
+	.y_clamp_bound				= DC_FRAMEBUFFER_Y_CLAMP_BOUND,
+	.uv_clamp_bound				= DC_FRAMEBUFFER_UV_CLAMP_BOUND,
+	.rgb_to_rgb_coef0			= DC_FRAMEBUFFER_RGBTORGB_COEF0,
+	.rgb_to_rgb_coef1			= DC_FRAMEBUFFER_RGBTORGB_COEF1,
+	.rgb_to_rgb_coef2			= DC_FRAMEBUFFER_RGBTORGB_COEF2,
+	.rgb_to_rgb_coef3			= DC_FRAMEBUFFER_RGBTORGB_COEF3,
+	.rgb_to_rgb_coef4			= DC_FRAMEBUFFER_RGBTORGB_COEF4,
+	},
+	{
+	.y_address		= DC_OVERLAY_ADDRESS,
+	.u_address		= DC_OVERLAY_U_ADDRESS,
+	.v_address		= DC_OVERLAY_V_ADDRESS,
+	.y_stride		= DC_OVERLAY_STRIDE,
+	.u_stride		= DC_OVERLAY_U_STRIDE,
+	.v_stride		= DC_OVERLAY_V_STRIDE,
+	.size			= DC_OVERLAY_SIZE,
+	.top_left		= DC_OVERLAY_TOP_LEFT,
+	.bottom_right	= DC_OVERLAY_BOTTOM_RIGHT,
+	.scale_factor_x	= DC_OVERLAY_SCALE_FACTOR_X,
+	.scale_factor_y	= DC_OVERLAY_SCALE_FACTOR_Y,
+	.h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
+	.h_filter_coef_data  = DC_OVERLAY_H_FILTER_COEF_DATA,
+	.v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
+	.v_filter_coef_data  = DC_OVERLAY_V_FILTER_COEF_DATA,
+	.init_offset		 = DC_OVERLAY_INIT_OFFSET,
+	.color_key			 = DC_OVERLAY_COLOR_KEY,
+	.color_key_high			= DC_OVERLAY_COLOR_KEY_HIGH,
+	.clear_value		 = DC_OVERLAY_CLEAR_VALUE,
+	.color_table_index	 = DC_OVERLAY_COLOR_TABLE_INDEX,
+	.color_table_data	 = DC_OVERLAY_COLOR_TABLE_DATA,
+	.scale_config		 = DC_OVERLAY_SCALE_CONFIG,
+	.water_mark				= DC_OVERLAY_WATER_MARK,
+	.degamma_index		 = DC_OVERLAY_DEGAMMA_INDEX,
+	.degamma_data		 = DC_OVERLAY_DEGAMMA_DATA,
+	.degamma_ex_data	 = DC_OVERLAY_DEGAMMA_EX_DATA,
+	.src_global_color	 = DC_OVERLAY_SRC_GLOBAL_COLOR,
+	.dst_global_color	 = DC_OVERLAY_DST_GLOBAL_COLOR,
+	.blend_config		 = DC_OVERLAY_BLEND_CONFIG,
+	.roi_origin				= DC_OVERLAY_ROI_ORIGIN,
+	.roi_size				= DC_OVERLAY_ROI_SIZE,
+	.yuv_to_rgb_coef0		 = DC_OVERLAY_YUVTORGB_COEF0,
+	.yuv_to_rgb_coef1		 = DC_OVERLAY_YUVTORGB_COEF1,
+	.yuv_to_rgb_coef2		 = DC_OVERLAY_YUVTORGB_COEF2,
+	.yuv_to_rgb_coef3		 = DC_OVERLAY_YUVTORGB_COEF3,
+	.yuv_to_rgb_coef4			= DC_OVERLAY_YUVTORGB_COEF4,
+	.yuv_to_rgb_coefd0			= DC_OVERLAY_YUVTORGB_COEFD0,
+	.yuv_to_rgb_coefd1			= DC_OVERLAY_YUVTORGB_COEFD1,
+	.yuv_to_rgb_coefd2			= DC_OVERLAY_YUVTORGB_COEFD2,
+	.y_clamp_bound		 = DC_OVERLAY_Y_CLAMP_BOUND,
+	.uv_clamp_bound		 = DC_OVERLAY_UV_CLAMP_BOUND,
+	.rgb_to_rgb_coef0		 = DC_OVERLAY_RGBTORGB_COEF0,
+	.rgb_to_rgb_coef1		 = DC_OVERLAY_RGBTORGB_COEF1,
+	.rgb_to_rgb_coef2		 = DC_OVERLAY_RGBTORGB_COEF2,
+	.rgb_to_rgb_coef3		 = DC_OVERLAY_RGBTORGB_COEF3,
+	.rgb_to_rgb_coef4		 = DC_OVERLAY_RGBTORGB_COEF4,
+	},
+};
+
+static const u32 primary_overlay_format0[] = {
+	DRM_FORMAT_XRGB4444,
+	DRM_FORMAT_XBGR4444,
+	DRM_FORMAT_RGBX4444,
+	DRM_FORMAT_BGRX4444,
+	DRM_FORMAT_ARGB4444,
+	DRM_FORMAT_ABGR4444,
+	DRM_FORMAT_RGBA4444,
+	DRM_FORMAT_BGRA4444,
+	DRM_FORMAT_XRGB1555,
+	DRM_FORMAT_XBGR1555,
+	DRM_FORMAT_RGBX5551,
+	DRM_FORMAT_BGRX5551,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_ABGR1555,
+	DRM_FORMAT_RGBA5551,
+	DRM_FORMAT_BGRA5551,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565,
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_RGBX8888,
+	DRM_FORMAT_BGRX8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_RGBA8888,
+	DRM_FORMAT_BGRA8888,
+	DRM_FORMAT_ARGB2101010,
+	DRM_FORMAT_ABGR2101010,
+	DRM_FORMAT_RGBA1010102,
+	DRM_FORMAT_BGRA1010102,
+	DRM_FORMAT_YUYV,
+	DRM_FORMAT_YVYU,
+	DRM_FORMAT_UYVY,
+	DRM_FORMAT_VYUY,
+	DRM_FORMAT_YVU420,
+	DRM_FORMAT_YUV420,
+	DRM_FORMAT_NV12,
+	DRM_FORMAT_NV21,
+	DRM_FORMAT_NV16,
+	DRM_FORMAT_NV61,
+	DRM_FORMAT_P010,
+};
+
+static const u32 primary_overlay_format1[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_RGBX8888,
+	DRM_FORMAT_BGRX8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_RGBA8888,
+	DRM_FORMAT_BGRA8888,
+	DRM_FORMAT_ARGB2101010,
+	DRM_FORMAT_ABGR2101010,
+	DRM_FORMAT_RGBA1010102,
+	DRM_FORMAT_BGRA1010102,
+	DRM_FORMAT_NV12,
+	DRM_FORMAT_NV21,
+	DRM_FORMAT_YUV444,
+};
+
+static const u32 cursor_formats[] = {
+	DRM_FORMAT_ARGB8888
+};
+
+static const u64 format_modifier0[] = {
+	DRM_FORMAT_MOD_LINEAR,
+	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8),
+	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8),
+	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X8),
+	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X4),
+	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4),
+	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8),
+	DRM_FORMAT_MOD_INVALID
+};
+
+static const u64 secondary_format_modifiers[] = {
+	DRM_FORMAT_MOD_LINEAR,
+	DRM_FORMAT_MOD_INVALID
+};
+
+#define FRAC_16_16(mult, div)	 (((mult) << 16) / (div))
+
+static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = {
+	{
+		/* DC_REV_0 */
+		{
+		.name			= "Primary",
+		.id				= PRIMARY_PLANE_0,
+		.type			= DRM_PLANE_TYPE_PRIMARY,
+		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
+		.formats		= primary_overlay_format0,
+		.num_modifiers	= ARRAY_SIZE(format_modifier0),
+		.modifiers		= format_modifier0,
+		.min_width		= 0,
+		.min_height		= 0,
+		.max_width		= 4096,
+		.max_height		= 4096,
+		.rotation		= DRM_MODE_ROTATE_0 |
+							DRM_MODE_ROTATE_90 |
+							DRM_MODE_ROTATE_180 |
+							DRM_MODE_ROTATE_270 |
+							DRM_MODE_REFLECT_X |
+							DRM_MODE_REFLECT_Y,
+		.blend_mode		= BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+							BIT(DRM_MODE_BLEND_PREMULTI) |
+							BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 0,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name			= "Overlay",
+		.id				= OVERLAY_PLANE_0,
+		.type			= DRM_PLANE_TYPE_OVERLAY,
+		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
+		.formats		= primary_overlay_format0,
+		.num_modifiers	= ARRAY_SIZE(format_modifier0),
+		.modifiers		= format_modifier0,
+		.min_width		= 0,
+		.min_height		= 0,
+		.max_width		= 4096,
+		.max_height		= 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 1,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name			= "Overlay_1",
+		.id				= OVERLAY_PLANE_1,
+		.type			= DRM_PLANE_TYPE_OVERLAY,
+		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
+		.formats		= primary_overlay_format0,
+		.num_modifiers	= ARRAY_SIZE(secondary_format_modifiers),
+		.modifiers		= secondary_format_modifiers,
+		.min_width		= 0,
+		.min_height		= 0,
+		.max_width		= 4096,
+		.max_height		= 4096,
+		.rotation		= 0,
+		.blend_mode		= BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+							BIT(DRM_MODE_BLEND_PREMULTI) |
+							BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 2,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Primary_1",
+		.id				= PRIMARY_PLANE_1,
+		.type		 = DRM_PLANE_TYPE_PRIMARY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format0),
+		.formats	 = primary_overlay_format0,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 3,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay_2",
+		.id				= OVERLAY_PLANE_2,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format0),
+		.formats	 = primary_overlay_format0,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 4,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay_3",
+		.id			= OVERLAY_PLANE_3,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format0),
+		.formats	 = primary_overlay_format0,
+		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
+		.modifiers	 = secondary_format_modifiers,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = 0,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 5,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Cursor",
+		.id				= CURSOR_PLANE_0,
+		.type		 = DRM_PLANE_TYPE_CURSOR,
+		.num_formats = ARRAY_SIZE(cursor_formats),
+		.formats	 = cursor_formats,
+		.num_modifiers = 0,
+		.modifiers	 = NULL,
+		.min_width	 = 32,
+		.min_height  = 32,
+		.max_width	 = 64,
+		.max_height  = 64,
+		.rotation	 = 0,
+		.degamma_size = 0,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 255,
+		.watermark	 = false,
+		.color_mgmt  = false,
+		.roi		 = false,
+		},
+		{
+		.name		 = "Cursor_1",
+		.id				= CURSOR_PLANE_1,
+		.type		 = DRM_PLANE_TYPE_CURSOR,
+		.num_formats = ARRAY_SIZE(cursor_formats),
+		.formats	 = cursor_formats,
+		.num_modifiers = 0,
+		.modifiers	 = NULL,
+		.min_width	 = 32,
+		.min_height  = 32,
+		.max_width	 = 64,
+		.max_height  = 64,
+		.rotation	 = 0,
+		.degamma_size = 0,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 255,
+		.watermark	 = false,
+		.color_mgmt  = false,
+		.roi		 = false,
+		},
+	},
+	{
+		/* DC_REV_1 */
+		{
+		.name		 = "Primary",
+		.id				= PRIMARY_PLANE_0,
+		.type		 = DRM_PLANE_TYPE_PRIMARY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format0),
+		.formats	 = primary_overlay_format0,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 0,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay",
+		.id				= OVERLAY_PLANE_0,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format0),
+		.formats	 = primary_overlay_format0,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 1,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Primary_1",
+		.id				= PRIMARY_PLANE_1,
+		.type		 = DRM_PLANE_TYPE_PRIMARY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format0),
+		.formats	 = primary_overlay_format0,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 2,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay_2",
+		.id				= OVERLAY_PLANE_2,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format0),
+		.formats	 = primary_overlay_format0,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 3,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Cursor",
+		.id				= CURSOR_PLANE_0,
+		.type		 = DRM_PLANE_TYPE_CURSOR,
+		.num_formats = ARRAY_SIZE(cursor_formats),
+		.formats	 = cursor_formats,
+		.num_modifiers = 0,
+		.modifiers	 = NULL,
+		.min_width	 = 32,
+		.min_height  = 32,
+		.max_width	 = 64,
+		.max_height  = 64,
+		.rotation	 = 0,
+		.degamma_size = 0,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 255,
+		.watermark	 = false,
+		.color_mgmt  = false,
+		.roi		 = false,
+		},
+		{
+		.name		 = "Cursor_1",
+		.id				= CURSOR_PLANE_1,
+		.type		 = DRM_PLANE_TYPE_CURSOR,
+		.num_formats = ARRAY_SIZE(cursor_formats),
+		.formats	 = cursor_formats,
+		.num_modifiers = 0,
+		.modifiers	 = NULL,
+		.min_width	 = 32,
+		.min_height  = 32,
+		.max_width	 = 64,
+		.max_height  = 64,
+		.rotation	 = 0,
+		.degamma_size = 0,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 255,
+		.watermark	 = false,
+		.color_mgmt  = false,
+		.roi		 = false,
+		},
+	},
+	{
+		/* DC_REV_2 */
+		{
+		.name		 = "Primary",
+		.id				= PRIMARY_PLANE_0,
+		.type		 = DRM_PLANE_TYPE_PRIMARY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format1),
+		.formats	 = primary_overlay_format1,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 0,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay",
+		.id				= OVERLAY_PLANE_0,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format1),
+		.formats	 = primary_overlay_format1,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 1,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay_1",
+		.id				= OVERLAY_PLANE_1,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format1),
+		.formats	 = primary_overlay_format1,
+		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
+		.modifiers	 = secondary_format_modifiers,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = 0,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 2,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Primary_1",
+		.id				= PRIMARY_PLANE_1,
+		.type		 = DRM_PLANE_TYPE_PRIMARY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format1),
+		.formats	 = primary_overlay_format1,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 3,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay_2",
+		.id				= OVERLAY_PLANE_2,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format1),
+		.formats	 = primary_overlay_format1,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 4,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay_3",
+		.id				= OVERLAY_PLANE_3,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format1),
+		.formats	 = primary_overlay_format1,
+		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
+		.modifiers	 = secondary_format_modifiers,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = 0,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 5,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Cursor",
+		.id				= CURSOR_PLANE_0,
+		.type		 = DRM_PLANE_TYPE_CURSOR,
+		.num_formats = ARRAY_SIZE(cursor_formats),
+		.formats	 = cursor_formats,
+		.num_modifiers = 0,
+		.modifiers	 = NULL,
+		.min_width	 = 32,
+		.min_height  = 32,
+		.max_width	 = 64,
+		.max_height  = 64,
+		.rotation	 = 0,
+		.degamma_size = 0,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 255,
+		.watermark	 = false,
+		.color_mgmt  = false,
+		.roi		 = false,
+		},
+		{
+		.name		 = "Cursor_1",
+		.id				= CURSOR_PLANE_1,
+		.type		 = DRM_PLANE_TYPE_CURSOR,
+		.num_formats = ARRAY_SIZE(cursor_formats),
+		.formats	 = cursor_formats,
+		.num_modifiers = 0,
+		.modifiers	 = NULL,
+		.min_width	 = 32,
+		.min_height  = 32,
+		.max_width	 = 64,
+		.max_height  = 64,
+		.rotation	 = 0,
+		.degamma_size = 0,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 255,
+		.watermark	 = false,
+		.color_mgmt  = false,
+		.roi		 = false,
+		},
+	},
+};
+
+static const struct vs_dc_info dc_info[] = {
+	{
+		/* DC_REV_0 */
+		.name			= "DC8200",
+		.panel_num		= 2,
+		.plane_num		= 8,
+		.planes			= dc_hw_planes[DC_REV_0],
+		.layer_num		= 6,
+		.max_bpc		= 10,
+		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
+						  DRM_COLOR_FORMAT_YCBCR444 |
+						  DRM_COLOR_FORMAT_YCBCR422 |
+						  DRM_COLOR_FORMAT_YCBCR420,
+		.gamma_size		= GAMMA_EX_SIZE,
+		.gamma_bits		= 12,
+		.pitch_alignment	= 128,
+		.pipe_sync		= false,
+		.background		= true,
+		.panel_sync		= true,
+		.cap_dec		= true,
+	},
+	{
+		/* DC_REV_1 */
+		.name			= "DC8200",
+		.panel_num		= 2,
+		.plane_num		= 6,
+		.planes			= dc_hw_planes[DC_REV_1],
+		.layer_num		= 4,
+		.max_bpc		= 10,
+		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
+						  DRM_COLOR_FORMAT_YCBCR444 |
+						  DRM_COLOR_FORMAT_YCBCR422 |
+						  DRM_COLOR_FORMAT_YCBCR420,
+		.gamma_size		= GAMMA_EX_SIZE,
+		.gamma_bits		= 12,
+		.pitch_alignment	= 128,
+		.pipe_sync		= false,
+		.background		= true,
+		.panel_sync		= true,
+		.cap_dec		= true,
+	},
+	{
+		/* DC_REV_2 */
+		.name			= "DC8200",
+		.panel_num		= 2,
+		.plane_num		= 8,
+		.planes			= dc_hw_planes[DC_REV_2],
+		.layer_num		= 6,
+		.max_bpc		= 10,
+		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
+						  DRM_COLOR_FORMAT_YCBCR444 |
+						  DRM_COLOR_FORMAT_YCBCR422 |
+						  DRM_COLOR_FORMAT_YCBCR420,
+		.gamma_size		= GAMMA_EX_SIZE,
+		.gamma_bits		= 12,
+		.pitch_alignment	= 128,
+		.pipe_sync		= false,
+		.background		= true,
+		.panel_sync		= true,
+		.cap_dec		= false,
+	},
+};
+
+static const struct dc_hw_funcs hw_func;
+
+static inline u32 hi_read(struct dc_hw *hw, u32 reg)
+{
+	return readl(hw->hi_base + reg);
+}
+
+static inline void hi_write(struct dc_hw *hw, u32 reg, u32 value)
+{
+	writel(value, hw->hi_base + reg);
+}
+
+static inline void dc_write(struct dc_hw *hw, u32 reg, u32 value)
+{
+	writel(value, hw->reg_base + reg - DC_REG_BASE);
+}
+
+static inline u32 dc_read(struct dc_hw *hw, u32 reg)
+{
+	u32 value = readl(hw->reg_base + reg - DC_REG_BASE);
+
+	return value;
+}
+
+static inline void dc_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear)
+{
+	u32 value = dc_read(hw, reg);
+
+	value &= ~clear;
+	value |= set;
+	dc_write(hw, reg, value);
+}
+
+static void load_default_filter(struct dc_hw *hw,
+				const struct dc_hw_plane_reg *reg, u32 offset)
+{
+	u8 i;
+
+	dc_write(hw, reg->scale_config + offset, 0x33);
+	dc_write(hw, reg->init_offset + offset, 0x80008000);
+	dc_write(hw, reg->h_filter_coef_index + offset, 0x00);
+	for (i = 0; i < H_COEF_SIZE; i++)
+		dc_write(hw, reg->h_filter_coef_data + offset, horkernel[i]);
+
+	dc_write(hw, reg->v_filter_coef_index + offset, 0x00);
+	for (i = 0; i < V_COEF_SIZE; i++)
+		dc_write(hw, reg->v_filter_coef_data + offset, verkernel[i]);
+}
+
+static void load_rgb_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
+			    u32 offset, u16 *table)
+{
+	dc_write(hw, reg->rgb_to_rgb_coef0 + offset, table[0] | (table[1] << 16));
+	dc_write(hw, reg->rgb_to_rgb_coef1 + offset, table[2] | (table[3] << 16));
+	dc_write(hw, reg->rgb_to_rgb_coef2 + offset, table[4] | (table[5] << 16));
+	dc_write(hw, reg->rgb_to_rgb_coef3 + offset, table[6] | (table[7] << 16));
+	dc_write(hw, reg->rgb_to_rgb_coef4 + offset, table[8]);
+}
+
+static void load_yuv_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
+			    u32 offset, s32 *table)
+{
+	dc_write(hw, reg->yuv_to_rgb_coef0 + offset,
+		 (0xFFFF & table[0]) | (table[1] << 16));
+	dc_write(hw, reg->yuv_to_rgb_coef1 + offset,
+		 (0xFFFF & table[2]) | (table[3] << 16));
+	dc_write(hw, reg->yuv_to_rgb_coef2 + offset,
+		 (0xFFFF & table[4]) | (table[5] << 16));
+	dc_write(hw, reg->yuv_to_rgb_coef3 + offset,
+		 (0xFFFF & table[6]) | (table[7] << 16));
+	dc_write(hw, reg->yuv_to_rgb_coef4 + offset, table[8]);
+	dc_write(hw, reg->yuv_to_rgb_coefd0 + offset, table[9]);
+	dc_write(hw, reg->yuv_to_rgb_coefd1 + offset, table[10]);
+	dc_write(hw, reg->yuv_to_rgb_coefd2 + offset, table[11]);
+	dc_write(hw, reg->y_clamp_bound + offset, table[12] | (table[13] << 16));
+	dc_write(hw, reg->uv_clamp_bound + offset, table[14] | (table[15] << 16));
+}
+
+static void load_rgb_to_yuv(struct dc_hw *hw, u32 offset, s16 *table)
+{
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF0 + offset,
+		 table[0] | (table[1] << 16));
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF1 + offset,
+		 table[2] | (table[3] << 16));
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF2 + offset,
+		 table[4] | (table[5] << 16));
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF3 + offset,
+		 table[6] | (table[7] << 16));
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF4 + offset, table[8]);
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD0 + offset, table[9]);
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD1 + offset, table[10]);
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD2 + offset, table[11]);
+}
+
+static bool is_rgb(enum dc_hw_color_format format)
+{
+	switch (format) {
+	case FORMAT_X4R4G4B4:
+	case FORMAT_A4R4G4B4:
+	case FORMAT_X1R5G5B5:
+	case FORMAT_A1R5G5B5:
+	case FORMAT_R5G6B5:
+	case FORMAT_X8R8G8B8:
+	case FORMAT_A8R8G8B8:
+	case FORMAT_A2R10G10B10:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static void load_degamma_table(struct dc_hw *hw,
+			       const struct dc_hw_plane_reg *reg,
+			       u32 offset, u16 *table)
+{
+	u16 i;
+	u32 value;
+
+	dc_write(hw, reg->degamma_index + offset, 0);
+
+	for (i = 0; i < DEGAMMA_SIZE; i++) {
+		value = table[i] | (table[i] << 16);
+		dc_write(hw, reg->degamma_data + offset, value);
+		dc_write(hw, reg->degamma_ex_data + offset, table[i]);
+	}
+}
+
+static u32 get_addr_offset(u32 id)
+{
+	u32 offset = 0;
+
+	switch (id) {
+	case PRIMARY_PLANE_1:
+	case OVERLAY_PLANE_1:
+		offset = 0x04;
+		break;
+	case OVERLAY_PLANE_2:
+		offset = 0x08;
+		break;
+	case OVERLAY_PLANE_3:
+		offset = 0x0C;
+		break;
+	default:
+		break;
+	}
+
+	return offset;
+}
+
+int dc_hw_init(struct dc_hw *hw)
+{
+	u8 i, id, panel_num, layer_num;
+	u32 offset;
+	u32 revision = hi_read(hw, DC_HW_REVISION);
+	u32 cid = hi_read(hw, DC_HW_CHIP_CID);
+	const struct dc_hw_plane_reg *reg;
+
+	switch (revision) {
+	case 0x5720:
+		hw->rev = DC_REV_0;
+		break;
+	case 0x5721:
+		switch (cid) {
+		case 0x30B:
+			hw->rev = DC_REV_1;
+			break;
+		case 0x310:
+			hw->rev = DC_REV_2;
+			break;
+		default:
+			hw->rev = DC_REV_0;
+			break;
+		}
+		break;
+	default:
+		return -ENXIO;
+	}
+
+	hw->info = (struct vs_dc_info *)&dc_info[hw->rev];
+	hw->func = (struct dc_hw_funcs *)&hw_func;
+
+	layer_num = hw->info->layer_num;
+	for (i = 0; i < layer_num; i++) {
+		id = hw->info->planes[i].id;
+		offset = get_addr_offset(id);
+		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
+			reg = &dc_plane_reg[0];
+		else
+			reg = &dc_plane_reg[1];
+
+		load_default_filter(hw, reg, offset);
+		load_rgb_to_rgb(hw, reg, offset, RGB2RGB);
+	}
+
+	panel_num = hw->info->panel_num;
+	for (i = 0; i < panel_num; i++) {
+		offset = i << 2;
+
+		load_rgb_to_yuv(hw, offset, RGB2YUV);
+		dc_write(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0x111);
+
+		offset = i ? DC_CURSOR_OFFSET : 0;
+		dc_write(hw, DC_CURSOR_BACKGROUND + offset, 0x00FFFFFF);
+		dc_write(hw, DC_CURSOR_FOREGROUND + offset, 0x00AAAAAA);
+	}
+
+	return 0;
+}
+
+void dc_hw_deinit(struct dc_hw *hw)
+{
+	/* Nothing to do */
+}
+
+void dc_hw_update_plane(struct dc_hw *hw, u8 id,
+			struct dc_hw_fb *fb, struct dc_hw_scale *scale,
+			struct dc_hw_position *pos, struct dc_hw_blend *blend)
+{
+	struct dc_hw_plane *plane = &hw->plane[id];
+
+	if (plane) {
+		if (fb) {
+			if (!fb->enable)
+				plane->fb.enable = false;
+			else
+				memcpy(&plane->fb, fb,
+				       sizeof(*fb) - sizeof(fb->dirty));
+			plane->fb.dirty = true;
+		}
+		if (scale) {
+			memcpy(&plane->scale, scale,
+			       sizeof(*scale) - sizeof(scale->dirty));
+			plane->scale.dirty = true;
+		}
+		if (pos) {
+			memcpy(&plane->pos, pos,
+			       sizeof(*pos) - sizeof(pos->dirty));
+			plane->pos.dirty = true;
+		}
+		if (blend) {
+			memcpy(&plane->blend, blend,
+			       sizeof(*blend) - sizeof(blend->dirty));
+			plane->blend.dirty = true;
+		}
+	}
+}
+
+void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode)
+{
+	struct dc_hw_plane *plane = &hw->plane[id];
+
+	if (plane) {
+		if (hw->info->planes[id].degamma_size) {
+			plane->degamma.mode = mode;
+			plane->degamma.dirty = true;
+		} else {
+			plane->degamma.dirty = false;
+		}
+	}
+}
+
+void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi)
+{
+	struct dc_hw_plane *plane = &hw->plane[id];
+
+	if (plane) {
+		memcpy(&plane->roi, roi, sizeof(*roi) - sizeof(roi->dirty));
+		plane->roi.dirty = true;
+	}
+}
+
+void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
+			   struct dc_hw_colorkey *colorkey)
+{
+	struct dc_hw_plane *plane = &hw->plane[id];
+
+	if (plane) {
+		memcpy(&plane->colorkey, colorkey,
+		       sizeof(*colorkey) - sizeof(colorkey->dirty));
+		plane->colorkey.dirty = true;
+	}
+}
+
+void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos)
+{
+	memcpy(&hw->qos, qos, sizeof(*qos) - sizeof(qos->dirty));
+	hw->qos.dirty = true;
+}
+
+void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor)
+{
+	memcpy(&hw->cursor[id], cursor, sizeof(*cursor) - sizeof(cursor->dirty));
+	hw->cursor[id].dirty = true;
+}
+
+void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
+			u16 r, u16 g, u16 b)
+{
+	if (index >= hw->info->gamma_size)
+		return;
+
+	hw->gamma[id].gamma[index][0] = r;
+	hw->gamma[id].gamma[index][1] = g;
+	hw->gamma[id].gamma[index][2] = b;
+	hw->gamma[id].dirty = true;
+}
+
+void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable)
+{
+	hw->gamma[id].enable = enable;
+	hw->gamma[id].dirty = true;
+}
+
+void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display)
+{
+	u8 id = display->id;
+
+	memcpy(&hw->display[id], display, sizeof(*display));
+
+	hw->func->display(hw, display);
+}
+
+void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable)
+{
+	if (enable)
+		hi_write(hw, AQ_INTR_ENBL, 0xFFFFFFFF);
+	else
+		hi_write(hw, AQ_INTR_ENBL, 0);
+}
+
+u32 dc_hw_get_interrupt(struct dc_hw *hw)
+{
+	return hi_read(hw, AQ_INTR_ACKNOWLEDGE);
+}
+
+bool dc_hw_check_underflow(struct dc_hw *hw)
+{
+	return dc_read(hw, DC_FRAMEBUFFER_CONFIG) & BIT(5);
+}
+
+void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable)
+{
+	u32 i, offset;
+	u8 id, layer_num = hw->info->layer_num;
+	u8 panel_num = hw->info->panel_num;
+
+	for (i = 0; i < layer_num; i++) {
+		id = hw->info->planes[i].id;
+		offset = get_addr_offset(id);
+		if (enable) {
+			if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
+				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, BIT(12), 0);
+			else
+				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, BIT(31), 0);
+		} else {
+			if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
+				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, 0, BIT(12));
+			else
+				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, 0, BIT(31));
+		}
+	}
+
+	for (i = 0; i < panel_num; i++) {
+		offset = i << 2;
+		if (enable)
+			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, BIT(0));
+		else
+			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, BIT(0), 0);
+	}
+}
+
+void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id)
+{
+	if (out <= OUT_DP)
+		hw->out[id] = out;
+}
+
+static void gamma_ex_commit(struct dc_hw *hw)
+{
+	u8 panel_num = hw->info->panel_num;
+	u16 i, j;
+	u32 value;
+
+	for (j = 0; j < panel_num; j++) {
+		if (hw->gamma[j].dirty) {
+			if (hw->gamma[j].enable) {
+				dc_write(hw, DC_DISPLAY_GAMMA_EX_INDEX + (j << 2), 0x00);
+				for (i = 0; i < GAMMA_EX_SIZE; i++) {
+					value = hw->gamma[j].gamma[i][2] |
+						(hw->gamma[j].gamma[i][1] << 12);
+					dc_write(hw, DC_DISPLAY_GAMMA_EX_DATA + (j << 2), value);
+					dc_write(hw, DC_DISPLAY_GAMMA_EX_ONE_DATA + (j << 2),
+						 hw->gamma[j].gamma[i][0]);
+				}
+				dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
+					     BIT(13), 0);
+			} else {
+				dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
+					     0, BIT(13));
+			}
+			hw->gamma[j].dirty = false;
+		}
+	}
+}
+
+static void plane_commit(struct dc_hw *hw)
+{
+	struct dc_hw_plane *plane;
+	const struct dc_hw_plane_reg *reg;
+	bool primary = false;
+	u8 id, layer_num = hw->info->layer_num;
+	u32 i, offset;
+
+	for (i = 0; i < layer_num; i++) {
+		plane = &hw->plane[i];
+		id = hw->info->planes[i].id;
+		offset = get_addr_offset(id);
+		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
+			reg = &dc_plane_reg[0];
+			primary = true;
+		} else {
+			reg = &dc_plane_reg[1];
+			primary = false;
+		}
+
+		if (plane->fb.dirty) {
+			if (plane->fb.enable) {
+				dc_write(hw, reg->y_address + offset,
+					 plane->fb.y_address);
+				dc_write(hw, reg->u_address + offset,
+					 plane->fb.u_address);
+				dc_write(hw, reg->v_address + offset,
+					 plane->fb.v_address);
+				dc_write(hw, reg->y_stride + offset,
+					 plane->fb.y_stride);
+				dc_write(hw, reg->u_stride + offset,
+					 plane->fb.u_stride);
+				dc_write(hw, reg->v_stride + offset,
+					 plane->fb.v_stride);
+				dc_write(hw, reg->size + offset,
+					 plane->fb.width |
+					 (plane->fb.height << 15));
+				dc_write(hw, reg->water_mark + offset,
+					 plane->fb.water_mark);
+
+				if (plane->fb.clear_enable)
+					dc_write(hw, reg->clear_value + offset,
+						 plane->fb.clear_value);
+			}
+
+			if (primary) {
+				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
+					     (plane->fb.format << 26) |
+					     (plane->fb.uv_swizzle << 25) |
+					     (plane->fb.swizzle << 23) |
+					     (plane->fb.tile_mode << 17) |
+					     (plane->fb.yuv_color_space << 14) |
+					     (plane->fb.rotation << 11) |
+					     (plane->fb.clear_enable << 8),
+					     (0x1F << 26) |
+					     BIT(25) |
+					     (0x03 << 23) |
+					     (0x1F << 17) |
+					     (0x07 << 14) |
+					     (0x07 << 11) |
+					     BIT(8));
+				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
+					     (plane->fb.dec_enable << 1) |
+					     (plane->fb.enable << 13) |
+					     (plane->fb.zpos << 16) |
+					     (plane->fb.display_id << 19),
+					     BIT(1) | BIT(13) | (0x07 << 16) | BIT(19));
+			} else {
+				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+					     (plane->fb.dec_enable << 27) |
+					     (plane->fb.clear_enable << 25) |
+					     (plane->fb.enable << 24) |
+					     (plane->fb.format << 16) |
+					     (plane->fb.uv_swizzle << 15) |
+					     (plane->fb.swizzle << 13) |
+					     (plane->fb.tile_mode << 8) |
+					     (plane->fb.yuv_color_space << 5) |
+					     (plane->fb.rotation << 2),
+					     BIT(27) |
+					     BIT(25) |
+					     BIT(24) |
+					     (0x1F << 16) |
+					     BIT(15) |
+					     (0x03 << 13) |
+					     (0x1F << 8) |
+					     (0x07 << 5) |
+					     (0x07 << 2));
+				dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset,
+					     plane->fb.zpos | (plane->fb.display_id << 3),
+					     0x07 | BIT(3));
+			}
+			plane->fb.dirty = false;
+		}
+
+		if (plane->scale.dirty) {
+			if (plane->scale.enable) {
+				dc_write(hw, reg->scale_factor_x + offset,
+					 plane->scale.scale_factor_x);
+				dc_write(hw, reg->scale_factor_y + offset,
+					 plane->scale.scale_factor_y);
+				if (primary)
+					dc_set_clear(hw,
+						     DC_FRAMEBUFFER_CONFIG + offset,
+						     BIT(22), 0);
+				else
+					dc_set_clear(hw,
+						     DC_OVERLAY_SCALE_CONFIG + offset,
+						     BIT(8), 0);
+			} else {
+				if (primary)
+					dc_set_clear(hw,
+						     DC_FRAMEBUFFER_CONFIG + offset,
+						     0, BIT(22));
+				else
+					dc_set_clear(hw,
+						     DC_OVERLAY_SCALE_CONFIG + offset,
+						     0, BIT(8));
+			}
+			plane->scale.dirty = false;
+		}
+
+		if (plane->pos.dirty) {
+			dc_write(hw, reg->top_left + offset,
+				 plane->pos.start_x |
+				 (plane->pos.start_y << 15));
+			dc_write(hw, reg->bottom_right + offset,
+				 plane->pos.end_x |
+				 (plane->pos.end_y << 15));
+			plane->pos.dirty = false;
+		}
+
+		if (plane->blend.dirty) {
+			dc_write(hw, reg->src_global_color + offset,
+				 plane->blend.alpha << 24);
+			dc_write(hw, reg->dst_global_color + offset,
+				 plane->blend.alpha << 24);
+			switch (plane->blend.blend_mode) {
+			case BLEND_PREMULTI:
+				dc_write(hw, reg->blend_config + offset, 0x3450);
+				break;
+			case BLEND_COVERAGE:
+				dc_write(hw, reg->blend_config + offset, 0x3950);
+				break;
+			case BLEND_PIXEL_NONE:
+				dc_write(hw, reg->blend_config + offset, 0x3548);
+				break;
+			default:
+				break;
+			}
+			plane->blend.dirty = false;
+		}
+
+		if (plane->colorkey.dirty) {
+			dc_write(hw, reg->color_key + offset, plane->colorkey.colorkey);
+			dc_write(hw, reg->color_key_high + offset,
+				 plane->colorkey.colorkey_high);
+
+			if (primary)
+				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
+					     plane->colorkey.transparency << 9, 0x03 << 9);
+			else
+				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+					     plane->colorkey.transparency, 0x03);
+
+			plane->colorkey.dirty = false;
+		}
+
+		if (plane->roi.dirty) {
+			if (plane->roi.enable) {
+				dc_write(hw, reg->roi_origin + offset,
+					 plane->roi.x | (plane->roi.y << 16));
+				dc_write(hw, reg->roi_size + offset,
+					 plane->roi.width | (plane->roi.height << 16));
+				if (primary)
+					dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
+						     BIT(0), 0);
+				else
+					dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+						     BIT(22), 0);
+			} else {
+				if (primary)
+					dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
+						     0, BIT(0));
+				else
+					dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+						     0, BIT(22));
+			}
+			plane->roi.dirty = false;
+		}
+	}
+}
+
+static void plane_ex_commit(struct dc_hw *hw)
+{
+	struct dc_hw_plane *plane;
+	const struct dc_hw_plane_reg *reg;
+	bool primary = false;
+	u8 id, layer_num = hw->info->layer_num;
+	u32 i, offset;
+
+	for (i = 0; i < layer_num; i++) {
+		plane = &hw->plane[i];
+		id = hw->info->planes[i].id;
+		offset = get_addr_offset(id);
+		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
+			reg = &dc_plane_reg[0];
+			primary = true;
+		} else {
+			reg = &dc_plane_reg[1];
+			primary = false;
+		}
+
+		if (plane->fb.dirty) {
+			if (is_rgb(plane->fb.format)) {
+				if (primary)
+					dc_set_clear(hw,
+						     DC_FRAMEBUFFER_CONFIG_EX + offset,
+						     BIT(6), BIT(8));
+				else
+					dc_set_clear(hw,
+						     DC_OVERLAY_CONFIG + offset,
+						     BIT(29), BIT(30));
+			} else {
+				if (primary)
+					dc_set_clear(hw,
+						     DC_FRAMEBUFFER_CONFIG_EX + offset,
+						     BIT(8), BIT(6));
+				else
+					dc_set_clear(hw,
+						     DC_OVERLAY_CONFIG + offset,
+						     BIT(30), BIT(29));
+				switch (plane->fb.yuv_color_space) {
+				case COLOR_SPACE_601:
+					load_yuv_to_rgb(hw, reg, offset, YUV601_2RGB);
+					break;
+				case COLOR_SPACE_709:
+					load_yuv_to_rgb(hw, reg, offset, YUV709_2RGB);
+					break;
+				case COLOR_SPACE_2020:
+					load_yuv_to_rgb(hw, reg, offset, YUV2020_2RGB);
+					break;
+				default:
+					break;
+				}
+			}
+		}
+		if (plane->degamma.dirty) {
+			switch (plane->degamma.mode) {
+			case VS_DEGAMMA_DISABLE:
+				if (primary)
+					dc_set_clear(hw,
+						     DC_FRAMEBUFFER_CONFIG_EX + offset,
+						     0, BIT(5));
+				else
+					dc_set_clear(hw,
+						     DC_OVERLAY_CONFIG + offset,
+						     0, BIT(28));
+				break;
+			case VS_DEGAMMA_BT709:
+				load_degamma_table(hw, reg, offset, DEGAMMA_709);
+				if (primary)
+					dc_set_clear(hw,
+						     DC_FRAMEBUFFER_CONFIG_EX + offset,
+						     BIT(5), 0);
+				else
+					dc_set_clear(hw,
+						     DC_OVERLAY_CONFIG + offset,
+						     BIT(28), 0);
+				break;
+			case VS_DEGAMMA_BT2020:
+				load_degamma_table(hw, reg, offset, DEGAMMA_2020);
+				if (primary)
+					dc_set_clear(hw,
+						     DC_FRAMEBUFFER_CONFIG_EX + offset,
+						     BIT(5), 0);
+				else
+					dc_set_clear(hw,
+						     DC_OVERLAY_CONFIG + offset,
+						     BIT(28), 0);
+				break;
+			default:
+				break;
+			}
+			plane->degamma.dirty = false;
+		}
+	}
+	plane_commit(hw);
+}
+
+static void setup_display(struct dc_hw *hw, struct dc_hw_display *display)
+{
+	u8 id = display->id;
+	u32 dpi_cfg, offset = id << 2;
+
+	if (hw->display[id].enable) {
+		switch (display->bus_format) {
+		case MEDIA_BUS_FMT_RGB565_1X16:
+			dpi_cfg = 0;
+			break;
+		case MEDIA_BUS_FMT_RGB666_1X18:
+			dpi_cfg = 3;
+			break;
+		case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+			dpi_cfg = 4;
+			break;
+		case MEDIA_BUS_FMT_RGB888_1X24:
+			dpi_cfg = 5;
+			break;
+		case MEDIA_BUS_FMT_RGB101010_1X30:
+			dpi_cfg = 6;
+			break;
+		default:
+			dpi_cfg = 5;
+			break;
+		}
+		dc_write(hw, DC_DISPLAY_DPI_CONFIG + offset, dpi_cfg);
+
+		if (id == 0)
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
+		else
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
+
+		dc_write(hw, DC_DISPLAY_H + offset, hw->display[id].h_active |
+			 (hw->display[id].h_total << 16));
+		dc_write(hw, DC_DISPLAY_H_SYNC + offset,
+			 hw->display[id].h_sync_start |
+			 (hw->display[id].h_sync_end << 15) |
+			 (hw->display[id].h_sync_polarity ? 0 : BIT(31)) |
+			 BIT(30));
+		dc_write(hw, DC_DISPLAY_V + offset, hw->display[id].v_active |
+				(hw->display[id].v_total << 16));
+		dc_write(hw, DC_DISPLAY_V_SYNC + offset,
+			 hw->display[id].v_sync_start |
+			 (hw->display[id].v_sync_end << 15) |
+			 (hw->display[id].v_sync_polarity ? 0 : BIT(31)) |
+			 BIT(30));
+
+		if (hw->info->pipe_sync)
+			dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
+				     0, BIT(3) | BIT(4));
+
+		dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(12), 0);
+		if (id == 0)
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(0), BIT(3));
+		else
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(1), BIT(3));
+	} else {
+		dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(12));
+		if (id == 0)
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
+		else
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
+	}
+}
+
+static void setup_display_ex(struct dc_hw *hw, struct dc_hw_display *display)
+{
+	u8 id = display->id;
+	u32 dp_cfg, offset = id << 2;
+	bool is_yuv = false;
+
+	if (hw->display[id].enable && hw->out[id] == OUT_DP) {
+		switch (display->bus_format) {
+		case MEDIA_BUS_FMT_RGB565_1X16:
+			dp_cfg = 0;
+			break;
+		case MEDIA_BUS_FMT_RGB666_1X18:
+			dp_cfg = 1;
+			break;
+		case MEDIA_BUS_FMT_RGB888_1X24:
+			dp_cfg = 2;
+			break;
+		case MEDIA_BUS_FMT_RGB101010_1X30:
+			dp_cfg = 3;
+			break;
+		case MEDIA_BUS_FMT_UYVY8_1X16:
+			dp_cfg = 2 << 4;
+			is_yuv = true;
+			break;
+		case MEDIA_BUS_FMT_YUV8_1X24:
+			dp_cfg = 4 << 4;
+			is_yuv = true;
+			break;
+		case MEDIA_BUS_FMT_UYVY10_1X20:
+			dp_cfg = 8 << 4;
+			is_yuv = true;
+			break;
+		case MEDIA_BUS_FMT_YUV10_1X30:
+			dp_cfg = 10 << 4;
+			is_yuv = true;
+			break;
+		case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+			dp_cfg = 12 << 4;
+			is_yuv = true;
+			break;
+		case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+			dp_cfg = 13 << 4;
+			is_yuv = true;
+			break;
+		default:
+			dp_cfg = 2;
+			break;
+		}
+		if (is_yuv)
+			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(16), 0);
+		else
+			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(16));
+		dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | BIT(3));
+	}
+
+	if (hw->out[id] == OUT_DPI)
+		dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, BIT(3));
+
+	setup_display(hw, display);
+}
+
+static const struct dc_hw_funcs hw_func = {
+	.gamma = &gamma_ex_commit,
+	.plane = &plane_ex_commit,
+	.display = setup_display_ex,
+};
+
+void dc_hw_commit(struct dc_hw *hw)
+{
+	u32 i, offset = 0;
+	u8 plane_num = hw->info->plane_num;
+	u8 layer_num = hw->info->layer_num;
+	u8 cursor_num = plane_num - layer_num;
+
+	hw->func->gamma(hw);
+	hw->func->plane(hw);
+
+	for (i = 0; i < cursor_num; i++) {
+		if (hw->cursor[i].dirty) {
+			offset = hw->cursor[i].display_id ? DC_CURSOR_OFFSET : 0;
+			if (hw->cursor[i].enable) {
+				dc_write(hw, DC_CURSOR_ADDRESS + offset,
+					 hw->cursor[i].address);
+				dc_write(hw, DC_CURSOR_LOCATION + offset, hw->cursor[i].x |
+					 (hw->cursor[i].y << 16));
+				dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
+					     (hw->cursor[i].hot_x << 16) |
+					     (hw->cursor[i].hot_y << 8) |
+					     (hw->cursor[i].size << 5) |
+					     BIT(3) | BIT(2) | 0x02,
+					     (0xFF << 16) |
+					     (0xFF << 8) |
+					     (0x07 << 5) | 0x1F);
+			} else {
+				dc_set_clear(hw, DC_CURSOR_CONFIG + offset, BIT(3), 0x03);
+			}
+			hw->cursor[i].dirty = false;
+		}
+	}
+
+	if (hw->qos.dirty) {
+		dc_set_clear(hw, DC_QOS_CONFIG, (hw->qos.high_value << 4) |
+					 hw->qos.low_value, 0xFF);
+		hw->qos.dirty = false;
+	}
+}
+
diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.h b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
new file mode 100644
index 000000000..9a1d767ae
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
@@ -0,0 +1,492 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_DC_HW_H__
+#define __VS_DC_HW_H__
+
+#define AQ_INTR_ACKNOWLEDGE				0x0010
+#define AQ_INTR_ENBL					0x0014
+#define DC_HW_REVISION					0x0024
+#define DC_HW_CHIP_CID					0x0030
+
+#define DC_REG_BASE						0x0800
+#define DC_REG_RANGE					0x2000
+#define DC_SEC_REG_OFFSET				0x100000
+
+#define DC_FRAMEBUFFER_CONFIG			0x1518
+#define DC_FRAMEBUFFER_CONFIG_EX		0x1CC0
+#define DC_FRAMEBUFFER_SCALE_CONFIG		0x1520
+#define DC_FRAMEBUFFER_TOP_LEFT			0x24D8
+#define DC_FRAMEBUFFER_BOTTOM_RIGHT		0x24E0
+#define DC_FRAMEBUFFER_ADDRESS			0x1400
+#define DC_FRAMEBUFFER_U_ADDRESS		0x1530
+#define DC_FRAMEBUFFER_V_ADDRESS		0x1538
+#define DC_FRAMEBUFFER_STRIDE			0x1408
+#define DC_FRAMEBUFFER_U_STRIDE			0x1800
+#define DC_FRAMEBUFFER_V_STRIDE			0x1808
+#define DC_FRAMEBUFFER_SIZE				0x1810
+#define DC_FRAMEBUFFER_SCALE_FACTOR_X		0x1828
+#define DC_FRAMEBUFFER_SCALE_FACTOR_Y		0x1830
+#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX	0x1838
+#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA	0x1A00
+#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX	0x1A08
+#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA	0x1A10
+#define DC_FRAMEBUFFER_INIT_OFFSET			0x1A20
+#define DC_FRAMEBUFFER_COLOR_KEY			0x1508
+#define DC_FRAMEBUFFER_COLOR_KEY_HIGH		0x1510
+#define DC_FRAMEBUFFER_CLEAR_VALUE			0x1A18
+#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX	0x1818
+#define DC_FRAMEBUFFER_COLOR_TABLE_DATA		0x1820
+#define DC_FRAMEBUFFER_BG_COLOR				0x1528
+#define DC_FRAMEBUFFER_ROI_ORIGIN			0x1CB0
+#define DC_FRAMEBUFFER_ROI_SIZE				0x1CB8
+#define DC_FRAMEBUFFER_WATER_MARK			0x1CE8
+#define DC_FRAMEBUFFER_DEGAMMA_INDEX		0x1D88
+#define DC_FRAMEBUFFER_DEGAMMA_DATA			0x1D90
+#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA		0x1D98
+#define DC_FRAMEBUFFER_YUVTORGB_COEF0		0x1DA0
+#define DC_FRAMEBUFFER_YUVTORGB_COEF1		0x1DA8
+#define DC_FRAMEBUFFER_YUVTORGB_COEF2		0x1DB0
+#define DC_FRAMEBUFFER_YUVTORGB_COEF3		0x1DB8
+#define DC_FRAMEBUFFER_YUVTORGB_COEF4		0x1E00
+#define DC_FRAMEBUFFER_YUVTORGB_COEFD0		0x1E08
+#define DC_FRAMEBUFFER_YUVTORGB_COEFD1		0x1E10
+#define DC_FRAMEBUFFER_YUVTORGB_COEFD2		0x1E18
+#define DC_FRAMEBUFFER_Y_CLAMP_BOUND		0x1E88
+#define DC_FRAMEBUFFER_UV_CLAMP_BOUND		0x1E90
+#define DC_FRAMEBUFFER_RGBTORGB_COEF0		0x1E20
+#define DC_FRAMEBUFFER_RGBTORGB_COEF1		0x1E28
+#define DC_FRAMEBUFFER_RGBTORGB_COEF2		0x1E30
+#define DC_FRAMEBUFFER_RGBTORGB_COEF3		0x1E38
+#define DC_FRAMEBUFFER_RGBTORGB_COEF4		0x1E40
+#define DC_FRAMEBUFFER_BLEND_CONFIG			0x2510
+#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR		0x2500
+#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR		0x2508
+
+#define DC_OVERLAY_CONFIG					0x1540
+#define DC_OVERLAY_CONFIG_EX				0x2540
+#define DC_OVERLAY_SCALE_CONFIG				0x1C00
+#define DC_OVERLAY_BLEND_CONFIG				0x1580
+#define DC_OVERLAY_TOP_LEFT					0x1640
+#define DC_OVERLAY_BOTTOM_RIGHT				0x1680
+#define DC_OVERLAY_ADDRESS					0x15C0
+#define DC_OVERLAY_U_ADDRESS				0x1840
+#define DC_OVERLAY_V_ADDRESS				0x1880
+#define DC_OVERLAY_STRIDE					0x1600
+#define DC_OVERLAY_U_STRIDE					0x18C0
+#define DC_OVERLAY_V_STRIDE					0x1900
+#define DC_OVERLAY_SIZE						0x17C0
+#define DC_OVERLAY_SCALE_FACTOR_X			0x1A40
+#define DC_OVERLAY_SCALE_FACTOR_Y			0x1A80
+#define DC_OVERLAY_H_FILTER_COEF_INDEX		0x1AC0
+#define DC_OVERLAY_H_FILTER_COEF_DATA		0x1B00
+#define DC_OVERLAY_V_FILTER_COEF_INDEX		0x1B40
+#define DC_OVERLAY_V_FILTER_COEF_DATA		0x1B80
+#define DC_OVERLAY_INIT_OFFSET				0x1BC0
+#define DC_OVERLAY_COLOR_KEY				0x1740
+#define DC_OVERLAY_COLOR_KEY_HIGH			0x1780
+#define DC_OVERLAY_CLEAR_VALUE				0x1940
+#define DC_OVERLAY_COLOR_TABLE_INDEX		0x1980
+#define DC_OVERLAY_COLOR_TABLE_DATA			0x19C0
+#define DC_OVERLAY_SRC_GLOBAL_COLOR			0x16C0
+#define DC_OVERLAY_DST_GLOBAL_COLOR			0x1700
+#define DC_OVERLAY_ROI_ORIGIN				0x1D00
+#define DC_OVERLAY_ROI_SIZE					0x1D40
+#define DC_OVERLAY_WATER_MARK				0x1DC0
+#define DC_OVERLAY_DEGAMMA_INDEX			0x2200
+#define DC_OVERLAY_DEGAMMA_DATA				0x2240
+#define DC_OVERLAY_DEGAMMA_EX_DATA			0x2280
+#define DC_OVERLAY_YUVTORGB_COEF0			0x1EC0
+#define DC_OVERLAY_YUVTORGB_COEF1			0x1F00
+#define DC_OVERLAY_YUVTORGB_COEF2			0x1F40
+#define DC_OVERLAY_YUVTORGB_COEF3			0x1F80
+#define DC_OVERLAY_YUVTORGB_COEF4			0x1FC0
+#define DC_OVERLAY_YUVTORGB_COEFD0			0x2000
+#define DC_OVERLAY_YUVTORGB_COEFD1			0x2040
+#define DC_OVERLAY_YUVTORGB_COEFD2			0x2080
+#define DC_OVERLAY_Y_CLAMP_BOUND			0x22C0
+#define DC_OVERLAY_UV_CLAMP_BOUND			0x2300
+#define DC_OVERLAY_RGBTORGB_COEF0			0x20C0
+#define DC_OVERLAY_RGBTORGB_COEF1			0x2100
+#define DC_OVERLAY_RGBTORGB_COEF2			0x2140
+#define DC_OVERLAY_RGBTORGB_COEF3			0x2180
+#define DC_OVERLAY_RGBTORGB_COEF4			0x21C0
+
+#define DC_CURSOR_CONFIG					0x1468
+#define DC_CURSOR_ADDRESS					0x146C
+#define DC_CURSOR_LOCATION					0x1470
+#define DC_CURSOR_BACKGROUND				0x1474
+#define DC_CURSOR_FOREGROUND				0x1478
+#define DC_CURSOR_CLK_GATING				0x1484
+#define DC_CURSOR_CONFIG_EX					0x24E8
+#define DC_CURSOR_OFFSET					0x1080
+
+#define DC_DISPLAY_DITHER_CONFIG		0x1410
+#define DC_DISPLAY_PANEL_CONFIG			0x1418
+#define DC_DISPLAY_PANEL_CONFIG_EX		0x2518
+#define DC_DISPLAY_DITHER_TABLE_LOW		0x1420
+#define DC_DISPLAY_DITHER_TABLE_HIGH	0x1428
+#define DC_DISPLAY_H					0x1430
+#define DC_DISPLAY_H_SYNC				0x1438
+#define DC_DISPLAY_V					0x1440
+#define DC_DISPLAY_V_SYNC				0x1448
+#define DC_DISPLAY_CURRENT_LOCATION		0x1450
+#define DC_DISPLAY_GAMMA_INDEX			0x1458
+#define DC_DISPLAY_GAMMA_DATA			0x1460
+#define DC_DISPLAY_INT					0x147C
+#define DC_DISPLAY_INT_ENABLE			0x1480
+#define DC_DISPLAY_DBI_CONFIG			0x1488
+#define DC_DISPLAY_GENERAL_CONFIG		0x14B0
+#define DC_DISPLAY_DPI_CONFIG			0x14B8
+#define DC_DISPLAY_PANEL_START			0x1CCC
+#define DC_DISPLAY_DEBUG_COUNTER_SELECT		0x14D0
+#define DC_DISPLAY_DEBUG_COUNTER_VALUE		0x14D8
+#define DC_DISPLAY_DP_CONFIG				0x1CD0
+#define DC_DISPLAY_GAMMA_EX_INDEX			0x1CF0
+#define DC_DISPLAY_GAMMA_EX_DATA			0x1CF8
+#define DC_DISPLAY_GAMMA_EX_ONE_DATA		0x1D80
+#define DC_DISPLAY_RGBTOYUV_COEF0			0x1E48
+#define DC_DISPLAY_RGBTOYUV_COEF1			0x1E50
+#define DC_DISPLAY_RGBTOYUV_COEF2			0x1E58
+#define DC_DISPLAY_RGBTOYUV_COEF3			0x1E60
+#define DC_DISPLAY_RGBTOYUV_COEF4			0x1E68
+#define DC_DISPLAY_RGBTOYUV_COEFD0			0x1E70
+#define DC_DISPLAY_RGBTOYUV_COEFD1			0x1E78
+#define DC_DISPLAY_RGBTOYUV_COEFD2			0x1E80
+
+#define DC_CLK_GATTING					0x1A28
+#define DC_QOS_CONFIG					0x1A38
+
+#define DC_TRANSPARENCY_OPAQUE			0x00
+#define DC_TRANSPARENCY_KEY				0x02
+#define DC_DISPLAY_DITHERTABLE_LOW		0x7B48F3C0
+#define DC_DISPLAY_DITHERTABLE_HIGH		0x596AD1E2
+
+#define GAMMA_SIZE						256
+#define GAMMA_EX_SIZE					300
+#define DEGAMMA_SIZE					260
+
+#define RGB_TO_RGB_TABLE_SIZE			9
+#define YUV_TO_RGB_TABLE_SIZE			16
+#define RGB_TO_YUV_TABLE_SIZE			12
+
+#define DC_LAYER_NUM	6
+#define DC_DISPLAY_NUM	2
+#define DC_CURSOR_NUM	2
+
+#define DC_TILE_MODE4X4	0x15
+
+enum dc_chip_rev {
+	DC_REV_0,/* For HW_REV_5720,HW_REV_5721_311 */
+	DC_REV_1,/* For HW_REV_5721_30B */
+	DC_REV_2,/* For HW_REV_5721_310 */
+};
+
+enum dc_hw_plane_id {
+	PRIMARY_PLANE_0,
+	OVERLAY_PLANE_0,
+	OVERLAY_PLANE_1,
+	PRIMARY_PLANE_1,
+	OVERLAY_PLANE_2,
+	OVERLAY_PLANE_3,
+	CURSOR_PLANE_0,
+	CURSOR_PLANE_1,
+	PLANE_NUM
+};
+
+enum dc_hw_color_format {
+	FORMAT_X4R4G4B4,//0
+	FORMAT_A4R4G4B4,//1
+	FORMAT_X1R5G5B5,//2
+	FORMAT_A1R5G5B5,//3
+	FORMAT_R5G6B5,//4
+	FORMAT_X8R8G8B8,//5
+	FORMAT_A8R8G8B8,//6
+	FORMAT_YUY2,//7
+	FORMAT_UYVY,//8
+	FORMAT_INDEX8,//9
+	FORMAT_MONOCHROME,//10
+	FORMAT_YV12 = 0xf,
+	FORMAT_A8,//16
+	FORMAT_NV12,//17
+	FORMAT_NV16,//18
+	FORMAT_RG16,//19
+	FORMAT_R8,//20
+	FORMAT_NV12_10BIT,//21
+	FORMAT_A2R10G10B10,//22
+	FORMAT_NV16_10BIT,//23
+	FORMAT_INDEX1,//24
+	FORMAT_INDEX2,//25
+	FORMAT_INDEX4,//26
+	FORMAT_P010,//27
+	FORMAT_YUV444,//28
+	FORMAT_YUV444_10BIT,//29
+};
+
+enum dc_hw_yuv_color_space {
+	COLOR_SPACE_601 = 0,
+	COLOR_SPACE_709 = 1,
+	COLOR_SPACE_2020 = 3,
+};
+
+enum dc_hw_rotation {
+	ROT_0 = 0,
+	ROT_90 = 4,
+	ROT_180 = 5,
+	ROT_270 = 6,
+	FLIP_X = 1,
+	FLIP_Y = 2,
+	FLIP_XY = 3,
+};
+
+enum dc_hw_swizzle {
+	SWIZZLE_ARGB = 0,
+	SWIZZLE_RGBA,
+	SWIZZLE_ABGR,
+	SWIZZLE_BGRA,
+};
+
+enum dc_hw_out {
+	OUT_DPI,
+	OUT_DP,
+};
+
+enum dc_hw_cursor_size {
+	CURSOR_SIZE_32X32 = 0,
+	CURSOR_SIZE_64X64,
+};
+
+enum dc_hw_blend_mode {
+	/* out.rgb = plane_alpha * fg.rgb +
+	 *		(1 - (plane_alpha * fg.alpha)) * bg.rgb
+	 */
+	BLEND_PREMULTI,
+	/* out.rgb = plane_alpha * fg.alpha * fg.rgb +
+	 *		(1 - (plane_alpha * fg.alpha)) * bg.rgb
+	 */
+	BLEND_COVERAGE,
+	/* out.rgb = plane_alpha * fg.rgb +
+	 *		(1 - plane_alpha) * bg.rgb
+	 */
+	BLEND_PIXEL_NONE,
+};
+
+struct dc_hw_plane_reg {
+	u32 y_address;
+	u32 u_address;
+	u32 v_address;
+	u32 y_stride;
+	u32 u_stride;
+	u32 v_stride;
+	u32 size;
+	u32 top_left;
+	u32 bottom_right;
+	u32 scale_factor_x;
+	u32 scale_factor_y;
+	u32 h_filter_coef_index;
+	u32 h_filter_coef_data;
+	u32 v_filter_coef_index;
+	u32 v_filter_coef_data;
+	u32 init_offset;
+	u32 color_key;
+	u32 color_key_high;
+	u32 clear_value;
+	u32 color_table_index;
+	u32 color_table_data;
+	u32 scale_config;
+	u32 water_mark;
+	u32 degamma_index;
+	u32 degamma_data;
+	u32 degamma_ex_data;
+	u32 src_global_color;
+	u32 dst_global_color;
+	u32 blend_config;
+	u32 roi_origin;
+	u32 roi_size;
+	u32 yuv_to_rgb_coef0;
+	u32 yuv_to_rgb_coef1;
+	u32 yuv_to_rgb_coef2;
+	u32 yuv_to_rgb_coef3;
+	u32 yuv_to_rgb_coef4;
+	u32 yuv_to_rgb_coefd0;
+	u32 yuv_to_rgb_coefd1;
+	u32 yuv_to_rgb_coefd2;
+	u32 y_clamp_bound;
+	u32 uv_clamp_bound;
+	u32 rgb_to_rgb_coef0;
+	u32 rgb_to_rgb_coef1;
+	u32 rgb_to_rgb_coef2;
+	u32 rgb_to_rgb_coef3;
+	u32 rgb_to_rgb_coef4;
+};
+
+struct dc_hw_fb {
+	u32 y_address;
+	u32 u_address;
+	u32 v_address;
+	u32 clear_value;
+	u32 water_mark;
+	u16 y_stride;
+	u16 u_stride;
+	u16 v_stride;
+	u16 width;
+	u16 height;
+	u8	format;
+	u8	tile_mode;
+	u8	rotation;
+	u8	yuv_color_space;
+	u8	swizzle;
+	u8	uv_swizzle;
+	u8	zpos;
+	u8	display_id;
+	bool	clear_enable;
+	bool	dec_enable;
+	bool	enable;
+	bool	dirty;
+};
+
+struct dc_hw_scale {
+	u32 scale_factor_x;
+	u32 scale_factor_y;
+	bool	enable;
+	bool	dirty;
+};
+
+struct dc_hw_position {
+	u16 start_x;
+	u16 start_y;
+	u16 end_x;
+	u16 end_y;
+	bool	dirty;
+};
+
+struct dc_hw_blend {
+	u8	alpha;
+	u8	blend_mode;
+	bool	dirty;
+};
+
+struct dc_hw_colorkey {
+	u32 colorkey;
+	u32 colorkey_high;
+	u8	transparency;
+	bool dirty;
+};
+
+struct dc_hw_roi {
+	u16 x;
+	u16 y;
+	u16 width;
+	u16 height;
+	bool enable;
+	bool dirty;
+};
+
+struct dc_hw_cursor {
+	u32 address;
+	u16 x;
+	u16 y;
+	u16 hot_x;
+	u16 hot_y;
+	u8	size;
+	u8	display_id;
+	bool	enable;
+	bool	dirty;
+};
+
+struct dc_hw_display {
+	u32 bus_format;
+	u16 h_active;
+	u16 h_total;
+	u16 h_sync_start;
+	u16 h_sync_end;
+	u16 v_active;
+	u16 v_total;
+	u16 v_sync_start;
+	u16 v_sync_end;
+	u8	id;
+	bool	h_sync_polarity;
+	bool	v_sync_polarity;
+	bool	enable;
+};
+
+struct dc_hw_gamma {
+	u16 gamma[GAMMA_EX_SIZE][3];
+	bool	enable;
+	bool	dirty;
+};
+
+struct dc_hw_degamma {
+	u16 degamma[DEGAMMA_SIZE][3];
+	u32 mode;
+	bool	dirty;
+};
+
+struct dc_hw_plane {
+	struct dc_hw_fb			fb;
+	struct dc_hw_position	pos;
+	struct dc_hw_scale		scale;
+	struct dc_hw_blend		blend;
+	struct dc_hw_roi		roi;
+	struct dc_hw_colorkey	colorkey;
+	struct dc_hw_degamma	degamma;
+};
+
+struct dc_hw_qos {
+	u8	  low_value;
+	u8	  high_value;
+	bool  dirty;
+};
+
+struct dc_hw_read {
+	u32			reg;
+	u32			value;
+};
+
+struct dc_hw;
+struct dc_hw_funcs {
+	void (*gamma)(struct dc_hw *hw);
+	void (*plane)(struct dc_hw *hw);
+	void (*display)(struct dc_hw *hw, struct dc_hw_display *display);
+};
+
+struct dc_hw {
+	enum dc_chip_rev rev;
+	enum dc_hw_out		out[DC_DISPLAY_NUM];
+	void			*hi_base;
+	void			*reg_base;
+
+	struct dc_hw_display display[DC_DISPLAY_NUM];
+	struct dc_hw_gamma	 gamma[DC_DISPLAY_NUM];
+	struct dc_hw_plane	 plane[DC_LAYER_NUM];
+	struct dc_hw_cursor  cursor[DC_CURSOR_NUM];
+	struct dc_hw_qos	 qos;
+	struct dc_hw_funcs	 *func;
+	struct vs_dc_info	 *info;
+};
+
+int dc_hw_init(struct dc_hw *hw);
+void dc_hw_deinit(struct dc_hw *hw);
+void dc_hw_update_plane(struct dc_hw *hw, u8 id,
+			struct dc_hw_fb *fb, struct dc_hw_scale *scale,
+			struct dc_hw_position *pos, struct dc_hw_blend *blend);
+void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode);
+void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi);
+void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
+			   struct dc_hw_colorkey *colorkey);
+void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos);
+void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor);
+void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
+			u16 r, u16 g, u16 b);
+void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable);
+void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display);
+void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable);
+u32 dc_hw_get_interrupt(struct dc_hw *hw);
+bool dc_hw_check_underflow(struct dc_hw *hw);
+void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable);
+void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id);
+void dc_hw_commit(struct dc_hw *hw);
+
+#endif /* __VS_DC_HW_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
index da7698c3d..3cd533cfa 100644
--- a/drivers/gpu/drm/verisilicon/vs_drv.c
+++ b/drivers/gpu/drm/verisilicon/vs_drv.c
@@ -21,6 +21,7 @@
 
 #include "vs_drv.h"
 #include "vs_modeset.h"
+#include "vs_dc.h"
 
 #define DRV_NAME	"verisilicon"
 #define DRV_DESC	"Verisilicon DRM driver"
@@ -123,6 +124,7 @@ static const struct component_master_ops vs_drm_ops = {
 };
 
 static struct platform_driver *drm_sub_drivers[] = {
+	&dc_platform_driver,
 
 };
 
diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c
new file mode 100644
index 000000000..9bd066015
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_plane.c
@@ -0,0 +1,526 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_fb_dma_helper.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_plane_helper.h>
+
+#include <drm/vs_drm.h>
+
+#include "vs_plane.h"
+#include "vs_drv.h"
+#include "vs_dc.h"
+
+static void vs_plane_reset(struct drm_plane *plane)
+{
+	struct vs_plane_state *state;
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+
+	if (plane->state) {
+		__drm_atomic_helper_plane_destroy_state(plane->state);
+
+		state = to_vs_plane_state(plane->state);
+		kfree(state);
+		plane->state = NULL;
+	}
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return;
+
+	state->degamma = VS_DEGAMMA_DISABLE;
+	state->degamma_changed = false;
+	state->base.zpos = vs_plane->id;
+	memset(&state->status, 0, sizeof(state->status));
+
+	__drm_atomic_helper_plane_reset(plane, &state->base);
+}
+
+static void _vs_plane_duplicate_blob(struct vs_plane_state *state,
+				     struct vs_plane_state *ori_state)
+{
+	state->watermark = ori_state->watermark;
+	state->color_mgmt = ori_state->color_mgmt;
+	state->roi = ori_state->roi;
+
+	if (state->watermark)
+		drm_property_blob_get(state->watermark);
+	if (state->color_mgmt)
+		drm_property_blob_get(state->color_mgmt);
+	if (state->roi)
+		drm_property_blob_get(state->roi);
+}
+
+static int
+_vs_plane_set_property_blob_from_id(struct drm_device *dev,
+				    struct drm_property_blob **blob,
+				    u64 blob_id,
+				    size_t expected_size)
+{
+	struct drm_property_blob *new_blob = NULL;
+
+	if (blob_id) {
+		new_blob = drm_property_lookup_blob(dev, blob_id);
+		if (!new_blob)
+			return -EINVAL;
+
+		if (new_blob->length != expected_size) {
+			drm_property_blob_put(new_blob);
+			return -EINVAL;
+		}
+	}
+
+	drm_property_replace_blob(blob, new_blob);
+	drm_property_blob_put(new_blob);
+
+	return 0;
+}
+
+static struct drm_plane_state *
+vs_plane_atomic_duplicate_state(struct drm_plane *plane)
+{
+	struct vs_plane_state *ori_state;
+	struct vs_plane_state *state;
+
+	if (WARN_ON(!plane->state))
+		return NULL;
+
+	ori_state = to_vs_plane_state(plane->state);
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return NULL;
+
+	__drm_atomic_helper_plane_duplicate_state(plane, &state->base);
+
+	state->degamma = ori_state->degamma;
+	state->degamma_changed = ori_state->degamma_changed;
+
+	_vs_plane_duplicate_blob(state, ori_state);
+	memcpy(&state->status, &ori_state->status, sizeof(ori_state->status));
+
+	return &state->base;
+}
+
+static void vs_plane_atomic_destroy_state(struct drm_plane *plane,
+					  struct drm_plane_state *state)
+{
+	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
+
+	__drm_atomic_helper_plane_destroy_state(state);
+
+	drm_property_blob_put(vs_plane_state->watermark);
+	drm_property_blob_put(vs_plane_state->color_mgmt);
+	drm_property_blob_put(vs_plane_state->roi);
+	kfree(vs_plane_state);
+}
+
+static int vs_plane_atomic_set_property(struct drm_plane *plane,
+					struct drm_plane_state *state,
+					struct drm_property *property,
+					uint64_t val)
+{
+	struct drm_device *dev = plane->dev;
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
+	int ret = 0;
+
+	if (property == vs_plane->degamma_mode) {
+		if (vs_plane_state->degamma != val) {
+			vs_plane_state->degamma = val;
+			vs_plane_state->degamma_changed = true;
+		} else {
+			vs_plane_state->degamma_changed = false;
+		}
+	} else if (property == vs_plane->watermark_prop) {
+		ret = _vs_plane_set_property_blob_from_id(dev,
+							  &vs_plane_state->watermark,
+							  val,
+							  sizeof(struct drm_vs_watermark));
+		return ret;
+	} else if (property == vs_plane->color_mgmt_prop) {
+		ret = _vs_plane_set_property_blob_from_id(dev,
+							  &vs_plane_state->color_mgmt,
+							  val,
+							  sizeof(struct drm_vs_color_mgmt));
+		return ret;
+	} else if (property == vs_plane->roi_prop) {
+		ret = _vs_plane_set_property_blob_from_id(dev,
+							  &vs_plane_state->roi,
+							  val,
+							  sizeof(struct drm_vs_roi));
+		return ret;
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int vs_plane_atomic_get_property(struct drm_plane *plane,
+					const struct drm_plane_state *state,
+					struct drm_property *property,
+					uint64_t *val)
+{
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+	const struct vs_plane_state *vs_plane_state =
+		container_of(state, const struct vs_plane_state, base);
+
+	if (property == vs_plane->degamma_mode)
+		*val = vs_plane_state->degamma;
+	else if (property == vs_plane->watermark_prop)
+		*val = (vs_plane_state->watermark) ?
+					vs_plane_state->watermark->base.id : 0;
+	else if (property == vs_plane->color_mgmt_prop)
+		*val = (vs_plane_state->color_mgmt) ?
+					vs_plane_state->color_mgmt->base.id : 0;
+	else if (property == vs_plane->roi_prop)
+		*val = (vs_plane_state->roi) ?
+					vs_plane_state->roi->base.id : 0;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static bool vs_format_mod_supported(struct drm_plane *plane,
+				    u32 format,
+				    u64 modifier)
+{
+	int i;
+
+	/* We always have to allow these modifiers:
+	 * 1. Core DRM checks for LINEAR support if userspace does not provide modifiers.
+	 * 2. Not passing any modifiers is the same as explicitly passing INVALID.
+	 */
+	if (modifier == DRM_FORMAT_MOD_LINEAR)
+		return true;
+
+	/* Check that the modifier is on the list of the plane's supported modifiers. */
+	for (i = 0; i < plane->modifier_count; i++) {
+		if (modifier == plane->modifiers[i])
+			break;
+	}
+
+	if (i == plane->modifier_count)
+		return false;
+
+	return true;
+}
+
+const struct drm_plane_funcs vs_plane_funcs = {
+	.update_plane		= drm_atomic_helper_update_plane,
+	.disable_plane		= drm_atomic_helper_disable_plane,
+	.reset			= vs_plane_reset,
+	.atomic_duplicate_state = vs_plane_atomic_duplicate_state,
+	.atomic_destroy_state	= vs_plane_atomic_destroy_state,
+	.atomic_set_property	= vs_plane_atomic_set_property,
+	.atomic_get_property	= vs_plane_atomic_get_property,
+	.format_mod_supported	= vs_format_mod_supported,
+};
+
+static unsigned char vs_get_plane_number(struct drm_framebuffer *fb)
+{
+	const struct drm_format_info *info;
+
+	if (!fb)
+		return 0;
+
+	info = drm_format_info(fb->format->format);
+	if (!info || info->num_planes > DRM_FORMAT_MAX_PLANES)
+		return 0;
+
+	return info->num_planes;
+}
+
+static int vs_plane_atomic_check(struct drm_plane *plane,
+				 struct drm_atomic_state *state)
+{
+	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+										 plane);
+	unsigned char i, num_planes;
+	struct drm_framebuffer *fb = new_plane_state->fb;
+	struct drm_crtc *crtc = new_plane_state->crtc;
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+	struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
+
+	if (!crtc || !fb)
+		return 0;
+
+	num_planes = vs_get_plane_number(fb);
+
+	for (i = 0; i < num_planes; i++) {
+		dma_addr_t dma_addr;
+
+		dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
+		plane_state->dma_addr[i] = dma_addr;
+	}
+
+	return vs_dc_check_plane(dc, plane, state);
+}
+
+static int vs_cursor_plane_atomic_check(struct drm_plane *plane,
+					struct drm_atomic_state *state)
+{
+	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+									  plane);
+	unsigned char i, num_planes;
+	struct drm_framebuffer *fb = new_plane_state->fb;
+	struct drm_crtc *crtc = new_plane_state->crtc;
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+	struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
+
+	if (!crtc || !fb)
+		return 0;
+
+	num_planes = vs_get_plane_number(fb);
+
+	for (i = 0; i < num_planes; i++) {
+		dma_addr_t dma_addr;
+
+		dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
+		plane_state->dma_addr[i] = dma_addr;
+	}
+
+	return vs_dc_check_cursor_plane(dc, plane, state);
+}
+
+static void vs_plane_atomic_update(struct drm_plane *plane,
+				   struct drm_atomic_state *state)
+{
+	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
+									  plane);
+	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
+									  plane);
+
+	unsigned char i, num_planes;
+	struct drm_framebuffer *fb;
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+	struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
+	struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	if (!new_state->fb || !new_state->crtc)
+		return;
+
+	fb = new_state->fb;
+
+	drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
+
+	num_planes = vs_get_plane_number(fb);
+
+	for (i = 0; i < num_planes; i++) {
+		dma_addr_t dma_addr;
+
+		dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
+		plane_state->dma_addr[i] = dma_addr;
+	}
+
+	plane_state->status.src = drm_plane_state_src(new_state);
+	plane_state->status.dest = drm_plane_state_dest(new_state);
+
+	vs_dc_update_plane(dc, vs_plane, plane, state);
+}
+
+static void vs_cursor_plane_atomic_update(struct drm_plane *plane,
+					  struct drm_atomic_state *state)
+{
+	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
+									   plane);
+	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
+									   plane);
+	unsigned char i, num_planes;
+	struct drm_framebuffer *fb;
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+	struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
+	struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	if (!new_state->fb || !new_state->crtc)
+		return;
+
+	fb = new_state->fb;
+	drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
+
+	num_planes = vs_get_plane_number(fb);
+
+	for (i = 0; i < num_planes; i++) {
+		dma_addr_t dma_addr;
+
+		dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
+		plane_state->dma_addr[i] = dma_addr;
+	}
+
+	plane_state->status.src = drm_plane_state_src(new_state);
+	plane_state->status.dest = drm_plane_state_dest(new_state);
+
+	vs_dc_update_cursor_plane(dc, vs_plane, plane, state);
+}
+
+static void vs_plane_atomic_disable(struct drm_plane *plane,
+				    struct drm_atomic_state *state)
+{
+	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
+									   plane);
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+	struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	vs_dc_disable_plane(dc, vs_plane, old_state);
+}
+
+static void vs_cursor_plane_atomic_disable(struct drm_plane *plane,
+					   struct drm_atomic_state *state)
+{
+	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
+									   plane);
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+	struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	vs_dc_disable_cursor_plane(dc, vs_plane, old_state);
+}
+
+const struct drm_plane_helper_funcs primary_plane_helpers = {
+	.atomic_check	= vs_plane_atomic_check,
+	.atomic_update	= vs_plane_atomic_update,
+	.atomic_disable = vs_plane_atomic_disable,
+};
+
+const struct drm_plane_helper_funcs overlay_plane_helpers = {
+	.atomic_check	= vs_plane_atomic_check,
+	.atomic_update	= vs_plane_atomic_update,
+	.atomic_disable = vs_plane_atomic_disable,
+};
+
+const struct drm_plane_helper_funcs cursor_plane_helpers = {
+	.atomic_check	= vs_cursor_plane_atomic_check,
+	.atomic_update	= vs_cursor_plane_atomic_update,
+	.atomic_disable = vs_cursor_plane_atomic_disable,
+};
+
+static const struct drm_prop_enum_list vs_degamma_mode_enum_list[] = {
+	{ VS_DEGAMMA_DISABLE,	"disabled" },
+	{ VS_DEGAMMA_BT709,	"preset degamma for BT709" },
+	{ VS_DEGAMMA_BT2020,	"preset degamma for BT2020" },
+};
+
+struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
+				 struct vs_plane_info *info,
+				 unsigned int layer_num,
+				 unsigned int possible_crtcs)
+{
+	struct vs_plane *plane;
+	int ret;
+
+	if (!info)
+		return NULL;
+
+	plane = drmm_universal_plane_alloc(drm_dev, struct vs_plane, base,
+					   possible_crtcs,
+					   &vs_plane_funcs,
+					   info->formats, info->num_formats,
+					   info->modifiers, info->type,
+					   info->name ? info->name : NULL);
+	if (IS_ERR(plane))
+		return ERR_CAST(plane);
+
+	if (info->type == DRM_PLANE_TYPE_PRIMARY)
+		drm_plane_helper_add(&plane->base, &primary_plane_helpers);
+	else if (info->type == DRM_PLANE_TYPE_CURSOR)
+		drm_plane_helper_add(&plane->base, &cursor_plane_helpers);
+	else
+		drm_plane_helper_add(&plane->base, &overlay_plane_helpers);
+
+	/* Set up the plane properties */
+	if (info->degamma_size) {
+		plane->degamma_mode =
+		drm_property_create_enum(drm_dev, 0,
+					 "DEGAMMA_MODE",
+					 vs_degamma_mode_enum_list,
+					 ARRAY_SIZE(vs_degamma_mode_enum_list));
+
+		if (!plane->degamma_mode)
+			return NULL;
+
+		drm_object_attach_property(&plane->base.base,
+					   plane->degamma_mode,
+					   VS_DEGAMMA_DISABLE);
+	}
+
+	if (info->rotation) {
+		ret = drm_plane_create_rotation_property(&plane->base,
+							 DRM_MODE_ROTATE_0,
+							 info->rotation);
+		if (ret)
+			return NULL;
+	}
+
+	if (info->blend_mode) {
+		ret = drm_plane_create_blend_mode_property(&plane->base,
+							   info->blend_mode);
+		if (ret)
+			return NULL;
+		ret = drm_plane_create_alpha_property(&plane->base);
+		if (ret)
+			return NULL;
+	}
+
+	if (info->color_encoding) {
+		ret = drm_plane_create_color_properties(&plane->base,
+							info->color_encoding,
+							BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
+							DRM_COLOR_YCBCR_BT709,
+							DRM_COLOR_YCBCR_LIMITED_RANGE);
+		if (ret)
+			return NULL;
+	}
+
+	if (info->zpos != 255) {
+		ret = drm_plane_create_zpos_property(&plane->base, info->zpos, 0,
+						     layer_num - 1);
+		if (ret)
+			return NULL;
+	} else {
+		ret = drm_plane_create_zpos_immutable_property(&plane->base,
+							       info->zpos);
+		if (ret)
+			return NULL;
+	}
+
+	if (info->watermark) {
+		plane->watermark_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
+							    "WATERMARK", 0);
+		if (!plane->watermark_prop)
+			return NULL;
+		drm_object_attach_property(&plane->base.base, plane->watermark_prop, 0);
+	}
+
+	if (info->color_mgmt) {
+		plane->color_mgmt_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
+							     "COLOR_CONFIG", 0);
+		if (!plane->color_mgmt_prop)
+			return NULL;
+
+		drm_object_attach_property(&plane->base.base, plane->color_mgmt_prop, 0);
+	}
+
+	if (info->roi) {
+		plane->roi_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
+						      "ROI", 0);
+		if (!plane->roi_prop)
+			return NULL;
+
+		drm_object_attach_property(&plane->base.base, plane->roi_prop, 0);
+	}
+
+	return plane;
+}
diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h
new file mode 100644
index 000000000..554b74e96
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_plane.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_PLANE_H__
+#define __VS_PLANE_H__
+
+#include <drm/drm_fourcc.h>
+#include <drm/drm_plane_helper.h>
+
+#include "vs_type.h"
+
+struct vs_plane_status {
+	u32 tile_mode;
+	struct drm_rect src;
+	struct drm_rect dest;
+};
+
+struct vs_plane_state {
+	struct drm_plane_state base;
+	struct vs_plane_status status; /* for debugfs */
+
+	struct drm_property_blob *watermark;
+	struct drm_property_blob *color_mgmt;
+	struct drm_property_blob *roi;
+	dma_addr_t dma_addr[DRM_FORMAT_MAX_PLANES];
+
+	u32 degamma;
+	bool degamma_changed;
+};
+
+struct vs_plane {
+	struct drm_plane base;
+	u8 id;
+
+	struct drm_property *degamma_mode;
+	struct drm_property *watermark_prop;
+	struct drm_property *color_mgmt_prop;
+	struct drm_property *roi_prop;
+};
+
+struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
+				 struct vs_plane_info *info,
+				 unsigned int layer_num,
+				 unsigned int possible_crtcs);
+
+static inline struct vs_plane *to_vs_plane(struct drm_plane *plane)
+{
+	return container_of(plane, struct vs_plane, base);
+}
+
+static inline struct vs_plane_state *
+to_vs_plane_state(struct drm_plane_state *state)
+{
+	return container_of(state, struct vs_plane_state, base);
+}
+#endif /* __VS_PLANE_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_type.h b/drivers/gpu/drm/verisilicon/vs_type.h
new file mode 100644
index 000000000..7d3378e29
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_type.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_TYPE_H__
+#define __VS_TYPE_H__
+
+#include <drm/drm_plane.h>
+#include <drm/drm_plane_helper.h>
+
+struct vs_plane_info {
+	const char *name;
+	u8 id;
+	enum drm_plane_type type;
+	unsigned int num_formats;
+	const u32 *formats;
+	u8 num_modifiers;
+	const u64 *modifiers;
+	unsigned int min_width;
+	unsigned int min_height;
+	unsigned int max_width;
+	unsigned int max_height;
+	unsigned int rotation;
+	unsigned int blend_mode;
+	unsigned int color_encoding;
+
+	/* 0 means no de-gamma LUT */
+	unsigned int degamma_size;
+
+	int min_scale; /* 16.16 fixed point */
+	int max_scale; /* 16.16 fixed point */
+
+	/* default zorder value,
+	 * and 255 means unsupported zorder capability
+	 */
+	u8	 zpos;
+
+	bool watermark;
+	bool color_mgmt;
+	bool roi;
+};
+
+struct vs_dc_info {
+	const char *name;
+
+	u8 panel_num;
+
+	/* planes */
+	u8 plane_num;
+	const struct vs_plane_info *planes;
+
+	u8 layer_num;
+	unsigned int max_bpc;
+	unsigned int color_formats;
+
+	/* 0 means no gamma LUT */
+	u16 gamma_size;
+	u8 gamma_bits;
+
+	u16 pitch_alignment;
+
+	bool pipe_sync;
+	bool background;
+	bool panel_sync;
+	bool cap_dec;
+};
+
+#endif /* __VS_TYPE_H__ */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 117+ messages in thread

* [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-10-25 10:39   ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-25 10:39 UTC (permalink / raw)
  To: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Keith Zhao, Bjorn Andersson, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Shawn Guo, christian.koenig

add 2 crtcs and 8 planes in vs-drm

Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
---
 drivers/gpu/drm/verisilicon/Makefile   |    8 +-
 drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
 drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
 drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
 drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
 drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
 drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
 drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
 drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
 drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
 drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
 11 files changed, 4494 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h

diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
index 7d3be305b..1d48016ca 100644
--- a/drivers/gpu/drm/verisilicon/Makefile
+++ b/drivers/gpu/drm/verisilicon/Makefile
@@ -1,7 +1,11 @@
 # SPDX-License-Identifier: GPL-2.0
 
-vs_drm-objs := vs_drv.o \
-		vs_modeset.o 
+vs_drm-objs := vs_dc_hw.o \
+		vs_dc.o \
+		vs_crtc.o \
+		vs_drv.o \
+		vs_modeset.o \
+		vs_plane.o
 
 obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
 
diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
new file mode 100644
index 000000000..8a658ea77
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
@@ -0,0 +1,257 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/media-bus-format.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_vblank.h>
+#include <drm/vs_drm.h>
+
+#include "vs_crtc.h"
+#include "vs_dc.h"
+#include "vs_drv.h"
+
+static void vs_crtc_reset(struct drm_crtc *crtc)
+{
+	struct vs_crtc_state *state;
+
+	if (crtc->state) {
+		__drm_atomic_helper_crtc_destroy_state(crtc->state);
+
+		state = to_vs_crtc_state(crtc->state);
+		kfree(state);
+		crtc->state = NULL;
+	}
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return;
+
+	__drm_atomic_helper_crtc_reset(crtc, &state->base);
+}
+
+static struct drm_crtc_state *
+vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
+{
+	struct vs_crtc_state *ori_state;
+	struct vs_crtc_state *state;
+
+	if (!crtc->state)
+		return NULL;
+
+	ori_state = to_vs_crtc_state(crtc->state);
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return NULL;
+
+	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
+
+	state->output_fmt = ori_state->output_fmt;
+	state->encoder_type = ori_state->encoder_type;
+	state->bpp = ori_state->bpp;
+	state->underflow = ori_state->underflow;
+
+	return &state->base;
+}
+
+static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
+					 struct drm_crtc_state *state)
+{
+	__drm_atomic_helper_crtc_destroy_state(state);
+	kfree(to_vs_crtc_state(state));
+}
+
+static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	vs_dc_enable_vblank(dc, true);
+
+	return 0;
+}
+
+static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	vs_dc_enable_vblank(dc, false);
+}
+
+static const struct drm_crtc_funcs vs_crtc_funcs = {
+	.set_config		= drm_atomic_helper_set_config,
+	.page_flip		= drm_atomic_helper_page_flip,
+	.reset			= vs_crtc_reset,
+	.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
+	.atomic_destroy_state	= vs_crtc_atomic_destroy_state,
+	.enable_vblank		= vs_crtc_enable_vblank,
+	.disable_vblank		= vs_crtc_disable_vblank,
+};
+
+static u8 cal_pixel_bits(u32 bus_format)
+{
+	u8 bpp;
+
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_RGB565_1X16:
+	case MEDIA_BUS_FMT_UYVY8_1X16:
+		bpp = 16;
+		break;
+	case MEDIA_BUS_FMT_RGB666_1X18:
+	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+		bpp = 18;
+		break;
+	case MEDIA_BUS_FMT_UYVY10_1X20:
+		bpp = 20;
+		break;
+	case MEDIA_BUS_FMT_BGR888_1X24:
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+	case MEDIA_BUS_FMT_YUV8_1X24:
+		bpp = 24;
+		break;
+	case MEDIA_BUS_FMT_RGB101010_1X30:
+	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+	case MEDIA_BUS_FMT_YUV10_1X30:
+		bpp = 30;
+		break;
+	default:
+		bpp = 24;
+		break;
+	}
+
+	return bpp;
+}
+
+static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
+				  struct drm_atomic_state *state)
+{
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
+
+	vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
+
+	vs_dc_enable(dc, crtc);
+	drm_crtc_vblank_on(crtc);
+}
+
+static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
+				   struct drm_atomic_state *state)
+{
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	drm_crtc_vblank_off(crtc);
+
+	vs_dc_disable(dc, crtc);
+
+	if (crtc->state->event && !crtc->state->active) {
+		spin_lock_irq(&crtc->dev->event_lock);
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+		spin_unlock_irq(&crtc->dev->event_lock);
+
+		crtc->state->event = NULL;
+	}
+}
+
+static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
+				 struct drm_atomic_state *state)
+{
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
+
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct device *dev = vs_crtc->dev;
+	struct drm_property_blob *blob = crtc->state->gamma_lut;
+	struct drm_color_lut *lut;
+	struct vs_dc *dc = dev_get_drvdata(dev);
+
+	if (crtc_state->color_mgmt_changed) {
+		if (blob && blob->length) {
+			lut = blob->data;
+			vs_dc_set_gamma(dc, crtc, lut,
+					blob->length / sizeof(*lut));
+			vs_dc_enable_gamma(dc, crtc, true);
+		} else {
+			vs_dc_enable_gamma(dc, crtc, false);
+		}
+	}
+}
+
+static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
+				 struct drm_atomic_state *state)
+{
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct drm_pending_vblank_event *event = crtc->state->event;
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	vs_dc_commit(dc);
+
+	if (event) {
+		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+
+		spin_lock_irq(&crtc->dev->event_lock);
+		drm_crtc_arm_vblank_event(crtc, event);
+		spin_unlock_irq(&crtc->dev->event_lock);
+		crtc->state->event = NULL;
+	}
+}
+
+static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
+	.atomic_check = drm_crtc_helper_atomic_check,
+	.atomic_enable	= vs_crtc_atomic_enable,
+	.atomic_disable = vs_crtc_atomic_disable,
+	.atomic_begin	= vs_crtc_atomic_begin,
+	.atomic_flush	= vs_crtc_atomic_flush,
+};
+
+static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
+	{ VS_SINGLE_DC,				"single dc mode" },
+	{ VS_MULTI_DC_PRIMARY,		"primary dc for multi dc mode" },
+	{ VS_MULTI_DC_SECONDARY,	"secondary dc for multi dc mode" },
+};
+
+struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
+			       struct vs_dc_info *info)
+{
+	struct vs_crtc *crtc;
+	int ret;
+
+	if (!info)
+		return NULL;
+
+	crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
+	if (!crtc)
+		return NULL;
+
+	ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
+					 NULL, NULL, &vs_crtc_funcs,
+					 info->name ? info->name : NULL);
+	if (ret)
+		return NULL;
+
+	drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
+
+	if (info->gamma_size) {
+		ret = drm_mode_crtc_set_gamma_size(&crtc->base,
+						   info->gamma_size);
+		if (ret)
+			return NULL;
+
+		drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
+					   info->gamma_size);
+	}
+
+	crtc->max_bpc = info->max_bpc;
+	crtc->color_formats = info->color_formats;
+	return crtc;
+}
diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h
new file mode 100644
index 000000000..526dd63e5
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_crtc.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_CRTC_H__
+#define __VS_CRTC_H__
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "vs_type.h"
+
+struct vs_crtc_state {
+	struct drm_crtc_state base;
+
+	u32 output_fmt;
+	u8 encoder_type;
+	u8 bpp;
+	bool underflow;
+};
+
+struct vs_crtc {
+	struct drm_crtc base;
+	struct device *dev;
+	unsigned int max_bpc;
+	unsigned int color_formats;
+};
+
+struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
+			       struct vs_dc_info *info);
+
+static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc)
+{
+	return container_of(crtc, struct vs_crtc, base);
+}
+
+static inline struct vs_crtc_state *
+to_vs_crtc_state(struct drm_crtc_state *state)
+{
+	return container_of(state, struct vs_crtc_state, base);
+}
+#endif /* __VS_CRTC_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
new file mode 100644
index 000000000..b5ab92d98
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc.c
@@ -0,0 +1,1002 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_vblank.h>
+#include <drm/vs_drm.h>
+
+#include "vs_dc_hw.h"
+#include "vs_dc.h"
+#include "vs_drv.h"
+
+static const char * const vout_clocks[] = {
+	"noc_bus",
+	"channel0",
+	"channel1",
+	"dc_core",
+	"axi_core",
+	"ahb",
+	"hdmi_tx",
+	"dc_parent",
+
+};
+
+static const char * const vout_resets[] = {
+	"axi",
+	"ahb",
+	"core",
+};
+
+static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb)
+{
+	u8 f = FORMAT_A8R8G8B8;
+
+	switch (format) {
+	case DRM_FORMAT_XRGB4444:
+	case DRM_FORMAT_RGBX4444:
+	case DRM_FORMAT_XBGR4444:
+	case DRM_FORMAT_BGRX4444:
+		f = FORMAT_X4R4G4B4;
+		break;
+	case DRM_FORMAT_ARGB4444:
+	case DRM_FORMAT_RGBA4444:
+	case DRM_FORMAT_ABGR4444:
+	case DRM_FORMAT_BGRA4444:
+		f = FORMAT_A4R4G4B4;
+		break;
+	case DRM_FORMAT_XRGB1555:
+	case DRM_FORMAT_RGBX5551:
+	case DRM_FORMAT_XBGR1555:
+	case DRM_FORMAT_BGRX5551:
+		f = FORMAT_X1R5G5B5;
+		break;
+	case DRM_FORMAT_ARGB1555:
+	case DRM_FORMAT_RGBA5551:
+	case DRM_FORMAT_ABGR1555:
+	case DRM_FORMAT_BGRA5551:
+		f = FORMAT_A1R5G5B5;
+		break;
+	case DRM_FORMAT_RGB565:
+	case DRM_FORMAT_BGR565:
+		f = FORMAT_R5G6B5;
+		break;
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_RGBX8888:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_BGRX8888:
+		f = FORMAT_X8R8G8B8;
+		break;
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_RGBA8888:
+	case DRM_FORMAT_ABGR8888:
+	case DRM_FORMAT_BGRA8888:
+		f = FORMAT_A8R8G8B8;
+		break;
+	case DRM_FORMAT_YUYV:
+	case DRM_FORMAT_YVYU:
+		f = FORMAT_YUY2;
+		break;
+	case DRM_FORMAT_UYVY:
+	case DRM_FORMAT_VYUY:
+		f = FORMAT_UYVY;
+		break;
+	case DRM_FORMAT_YUV420:
+	case DRM_FORMAT_YVU420:
+		f = FORMAT_YV12;
+		break;
+	case DRM_FORMAT_NV21:
+		f = FORMAT_NV12;
+		break;
+	case DRM_FORMAT_NV16:
+	case DRM_FORMAT_NV61:
+		f = FORMAT_NV16;
+		break;
+	case DRM_FORMAT_P010:
+		f = FORMAT_P010;
+		break;
+	case DRM_FORMAT_ARGB2101010:
+	case DRM_FORMAT_RGBA1010102:
+	case DRM_FORMAT_ABGR2101010:
+	case DRM_FORMAT_BGRA1010102:
+		f = FORMAT_A2R10G10B10;
+		break;
+	case DRM_FORMAT_NV12:
+		f = FORMAT_NV12;
+		break;
+	case DRM_FORMAT_YUV444:
+		f = FORMAT_YUV444;
+		break;
+	default:
+		break;
+	}
+
+	fb->format = f;
+}
+
+static inline void update_swizzle(u32 format, struct dc_hw_fb *fb)
+{
+	fb->swizzle = SWIZZLE_ARGB;
+	fb->uv_swizzle = 0;
+
+	switch (format) {
+	case DRM_FORMAT_RGBX4444:
+	case DRM_FORMAT_RGBA4444:
+	case DRM_FORMAT_RGBX5551:
+	case DRM_FORMAT_RGBA5551:
+	case DRM_FORMAT_RGBX8888:
+	case DRM_FORMAT_RGBA8888:
+	case DRM_FORMAT_RGBA1010102:
+		fb->swizzle = SWIZZLE_RGBA;
+		break;
+	case DRM_FORMAT_XBGR4444:
+	case DRM_FORMAT_ABGR4444:
+	case DRM_FORMAT_XBGR1555:
+	case DRM_FORMAT_ABGR1555:
+	case DRM_FORMAT_BGR565:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_ABGR8888:
+	case DRM_FORMAT_ABGR2101010:
+		fb->swizzle = SWIZZLE_ABGR;
+		break;
+	case DRM_FORMAT_BGRX4444:
+	case DRM_FORMAT_BGRA4444:
+	case DRM_FORMAT_BGRX5551:
+	case DRM_FORMAT_BGRA5551:
+	case DRM_FORMAT_BGRX8888:
+	case DRM_FORMAT_BGRA8888:
+	case DRM_FORMAT_BGRA1010102:
+		fb->swizzle = SWIZZLE_BGRA;
+		break;
+	case DRM_FORMAT_YVYU:
+	case DRM_FORMAT_VYUY:
+	case DRM_FORMAT_NV21:
+	case DRM_FORMAT_NV61:
+		fb->uv_swizzle = 1;
+		break;
+	default:
+		break;
+	}
+}
+
+static inline void update_watermark(struct drm_property_blob *watermark,
+				    struct dc_hw_fb *fb)
+{
+	struct drm_vs_watermark *data;
+
+	fb->water_mark = 0;
+
+	if (watermark) {
+		data = watermark->data;
+		fb->water_mark = data->watermark & 0xFFFFF;
+	}
+}
+
+static inline u8 to_vs_rotation(unsigned int rotation)
+{
+	u8 rot;
+
+	switch (rotation & DRM_MODE_REFLECT_MASK) {
+	case DRM_MODE_REFLECT_X:
+		rot = FLIP_X;
+		return rot;
+	case DRM_MODE_REFLECT_Y:
+		rot = FLIP_Y;
+		return rot;
+	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
+		rot = FLIP_XY;
+		return rot;
+	default:
+		break;
+	}
+
+	switch (rotation & DRM_MODE_ROTATE_MASK) {
+	case DRM_MODE_ROTATE_0:
+		rot = ROT_0;
+		break;
+	case DRM_MODE_ROTATE_90:
+		rot = ROT_90;
+		break;
+	case DRM_MODE_ROTATE_180:
+		rot = ROT_180;
+		break;
+	case DRM_MODE_ROTATE_270:
+		rot = ROT_270;
+		break;
+	default:
+		rot = ROT_0;
+		break;
+	}
+
+	return rot;
+}
+
+static inline u8 to_vs_yuv_color_space(u32 color_space)
+{
+	u8 cs;
+
+	switch (color_space) {
+	case DRM_COLOR_YCBCR_BT601:
+		cs = COLOR_SPACE_601;
+		break;
+	case DRM_COLOR_YCBCR_BT709:
+		cs = COLOR_SPACE_709;
+		break;
+	case DRM_COLOR_YCBCR_BT2020:
+		cs = COLOR_SPACE_2020;
+		break;
+	default:
+		cs = COLOR_SPACE_601;
+		break;
+	}
+
+	return cs;
+}
+
+static inline u8 to_vs_tile_mode(u64 modifier)
+{
+	if (modifier == DRM_FORMAT_MOD_VIVANTE_TILED)
+		return DC_TILE_MODE4X4;
+
+	return (u8)(modifier & DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK);
+}
+
+static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc)
+{
+	u8 panel_num = dc->hw.info->panel_num;
+	u32 index = drm_crtc_index(crtc);
+	int i;
+
+	for (i = 0; i < panel_num; i++) {
+		if (index == dc->crtc[i]->base.index)
+			return i;
+	}
+
+	return 0;
+}
+
+static void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
+					  unsigned int alignment)
+{
+	struct vs_drm_device *priv = to_vs_drm_private(drm_dev);
+
+	if (alignment > priv->pitch_alignment)
+		priv->pitch_alignment = alignment;
+}
+
+static int plda_clk_rst_init(struct device *dev)
+{
+	int ret = 0;
+	struct vs_dc *dc = dev_get_drvdata(dev);
+
+	ret = clk_bulk_prepare_enable(dc->nclks, dc->clk_vout);
+	if (ret) {
+		dev_err(dev, "failed to enable clocks\n");
+		return ret;
+	}
+
+	ret = reset_control_bulk_deassert(dc->nrsts, dc->rst_vout);
+	return ret;
+}
+
+static void plda_clk_rst_deinit(struct device *dev)
+{
+	struct vs_dc *dc = dev_get_drvdata(dev);
+
+	reset_control_bulk_assert(dc->nrsts, dc->rst_vout);
+	clk_bulk_disable_unprepare(dc->nclks, dc->clk_vout);
+}
+
+static void dc_deinit(struct device *dev)
+{
+	struct vs_dc *dc = dev_get_drvdata(dev);
+
+	dc_hw_enable_interrupt(&dc->hw, 0);
+	dc_hw_deinit(&dc->hw);
+	plda_clk_rst_deinit(dev);
+}
+
+static int dc_init(struct device *dev)
+{
+	struct vs_dc *dc = dev_get_drvdata(dev);
+	int ret;
+
+	dc->first_frame = true;
+
+	ret = plda_clk_rst_init(dev);
+	if (ret < 0) {
+		dev_err(dev, "failed to init dc clk reset: %d\n", ret);
+		return ret;
+	}
+
+	ret = dc_hw_init(&dc->hw);
+	if (ret) {
+		dev_err(dev, "failed to init DC HW\n");
+		return ret;
+	}
+	return 0;
+}
+
+void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
+{
+	struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
+	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+	struct dc_hw_display display;
+
+	display.bus_format = crtc_state->output_fmt;
+	display.h_active = mode->hdisplay;
+	display.h_total = mode->htotal;
+	display.h_sync_start = mode->hsync_start;
+	display.h_sync_end = mode->hsync_end;
+	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+		display.h_sync_polarity = true;
+	else
+		display.h_sync_polarity = false;
+
+	display.v_active = mode->vdisplay;
+	display.v_total = mode->vtotal;
+	display.v_sync_start = mode->vsync_start;
+	display.v_sync_end = mode->vsync_end;
+
+	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+		display.v_sync_polarity = true;
+	else
+		display.v_sync_polarity = false;
+
+	display.id = to_vs_display_id(dc, crtc);
+
+	display.enable = true;
+
+	if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) {
+		dc_hw_set_out(&dc->hw, OUT_DPI, display.id);
+		clk_set_rate(dc->clk_vout[CLK_VOUT_SOC_PIX].clk, mode->clock * 1000);
+		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX1].clk,
+			       dc->clk_vout[CLK_VOUT_SOC_PIX].clk);
+	} else {
+		dc_hw_set_out(&dc->hw, OUT_DP, display.id);
+		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX0].clk,
+			       dc->clk_vout[CLK_VOUT_HDMI_PIX].clk);
+	}
+
+	dc_hw_setup_display(&dc->hw, &display);
+}
+
+void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc)
+{
+	struct dc_hw_display display;
+
+	display.id = to_vs_display_id(dc, crtc);
+	display.enable = false;
+
+	dc_hw_setup_display(&dc->hw, &display);
+}
+
+void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
+		     struct drm_color_lut *lut, unsigned int size)
+{
+	u16 i, r, g, b;
+	u8 bits, id;
+
+	if (size != dc->hw.info->gamma_size) {
+		drm_err(crtc->dev, "gamma size does not match!\n");
+		return;
+	}
+
+	id = to_vs_display_id(dc, crtc);
+
+	bits = dc->hw.info->gamma_bits;
+	for (i = 0; i < size; i++) {
+		r = drm_color_lut_extract(lut[i].red, bits);
+		g = drm_color_lut_extract(lut[i].green, bits);
+		b = drm_color_lut_extract(lut[i].blue, bits);
+		dc_hw_update_gamma(&dc->hw, id, i, r, g, b);
+	}
+}
+
+void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
+			bool enable)
+{
+	u8 id;
+
+	id = to_vs_display_id(dc, crtc);
+	dc_hw_enable_gamma(&dc->hw, id, enable);
+}
+
+void vs_dc_enable_vblank(struct vs_dc *dc, bool enable)
+{
+	dc_hw_enable_interrupt(&dc->hw, enable);
+}
+
+static u32 calc_factor(u32 src, u32 dest)
+{
+	u32 factor = 1 << 16;
+
+	if (src > 1 && dest > 1)
+		factor = ((src - 1) << 16) / (dest - 1);
+
+	return factor;
+}
+
+static void update_scale(struct drm_plane_state *state, struct dc_hw_roi *roi,
+			 struct dc_hw_scale *scale)
+{
+	int dst_w = drm_rect_width(&state->dst);
+	int dst_h = drm_rect_height(&state->dst);
+	int src_w, src_h, temp;
+
+	scale->enable = false;
+
+	if (roi->enable) {
+		src_w = roi->width;
+		src_h = roi->height;
+	} else {
+		src_w = drm_rect_width(&state->src) >> 16;
+		src_h = drm_rect_height(&state->src) >> 16;
+	}
+
+	if (drm_rotation_90_or_270(state->rotation)) {
+		temp = src_w;
+		src_w = src_h;
+		src_h = temp;
+	}
+
+	if (src_w != dst_w) {
+		scale->scale_factor_x = calc_factor(src_w, dst_w);
+		scale->enable = true;
+	} else {
+		scale->scale_factor_x = 1 << 16;
+	}
+	if (src_h != dst_h) {
+		scale->scale_factor_y = calc_factor(src_h, dst_h);
+		scale->enable = true;
+	} else {
+		scale->scale_factor_y = 1 << 16;
+	}
+}
+
+static void update_fb(struct vs_plane *plane, u8 display_id,
+		      struct dc_hw_fb *fb, struct drm_plane_state *state)
+{
+	struct vs_plane_state *plane_state = to_vs_plane_state(state);
+	struct drm_framebuffer *drm_fb = state->fb;
+	struct drm_rect *src = &state->src;
+
+	fb->display_id = display_id;
+	fb->y_address = plane_state->dma_addr[0];
+	fb->y_stride = drm_fb->pitches[0];
+	if (drm_fb->format->format == DRM_FORMAT_YVU420) {
+		fb->u_address = plane_state->dma_addr[2];
+		fb->v_address = plane_state->dma_addr[1];
+		fb->u_stride = drm_fb->pitches[2];
+		fb->v_stride = drm_fb->pitches[1];
+	} else {
+		fb->u_address = plane_state->dma_addr[1];
+		fb->v_address = plane_state->dma_addr[2];
+		fb->u_stride = drm_fb->pitches[1];
+		fb->v_stride = drm_fb->pitches[2];
+	}
+	fb->width = drm_rect_width(src) >> 16;
+	fb->height = drm_rect_height(src) >> 16;
+	fb->tile_mode = to_vs_tile_mode(drm_fb->modifier);
+	fb->rotation = to_vs_rotation(state->rotation);
+	fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding);
+	fb->zpos = state->zpos;
+	fb->enable = state->visible;
+	update_format(drm_fb->format->format, drm_fb->modifier, fb);
+	update_swizzle(drm_fb->format->format, fb);
+	update_watermark(plane_state->watermark, fb);
+	plane_state->status.tile_mode = fb->tile_mode;
+}
+
+static void update_degamma(struct vs_dc *dc, struct vs_plane *plane,
+			   struct vs_plane_state *plane_state)
+{
+	dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
+	plane_state->degamma_changed = false;
+}
+
+static void update_roi(struct vs_dc *dc, u8 id,
+		       struct vs_plane_state *plane_state,
+		       struct dc_hw_roi *roi,
+		       struct drm_plane_state *state)
+{
+	struct drm_vs_roi *data;
+	struct drm_rect *src = &state->src;
+	u16 src_w = drm_rect_width(src) >> 16;
+	u16 src_h = drm_rect_height(src) >> 16;
+
+	if (plane_state->roi) {
+		data = plane_state->roi->data;
+
+		if (data->enable) {
+			roi->x = data->roi_x;
+			roi->y = data->roi_y;
+			roi->width = (data->roi_x + data->roi_w > src_w) ?
+						 (src_w - data->roi_x) : data->roi_w;
+			roi->height = (data->roi_y + data->roi_h > src_h) ?
+						  (src_h - data->roi_y) : data->roi_h;
+			roi->enable = true;
+		} else {
+			roi->enable = false;
+		}
+
+		dc_hw_update_roi(&dc->hw, id, roi);
+	} else {
+		roi->enable = false;
+	}
+}
+
+static void update_color_mgmt(struct vs_dc *dc, u8 id,
+			      struct dc_hw_fb *fb,
+			      struct vs_plane_state *plane_state)
+{
+	struct drm_vs_color_mgmt *data;
+	struct dc_hw_colorkey colorkey;
+
+	if (plane_state->color_mgmt) {
+		data = plane_state->color_mgmt->data;
+
+		fb->clear_enable = data->clear_enable;
+		fb->clear_value = data->clear_value;
+
+		if (data->colorkey > data->colorkey_high)
+			data->colorkey = data->colorkey_high;
+
+		colorkey.colorkey = data->colorkey;
+		colorkey.colorkey_high = data->colorkey_high;
+		colorkey.transparency = (data->transparency) ?
+				DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE;
+		dc_hw_update_colorkey(&dc->hw, id, &colorkey);
+	}
+}
+
+static void update_plane(struct vs_dc *dc, struct vs_plane *plane,
+			 struct drm_plane *drm_plane,
+			 struct drm_atomic_state *drm_state)
+{
+	struct dc_hw_fb fb = {0};
+	struct dc_hw_scale scale;
+	struct dc_hw_position pos;
+	struct dc_hw_blend blend;
+	struct dc_hw_roi roi;
+	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
+									   drm_plane);
+	struct vs_plane_state *plane_state = to_vs_plane_state(state);
+	struct drm_rect *dest = &state->dst;
+	bool dec_enable = false;
+	u8 display_id = 0;
+
+	display_id = to_vs_display_id(dc, state->crtc);
+	update_fb(plane, display_id, &fb, state);
+	fb.dec_enable = dec_enable;
+
+	update_roi(dc, plane->id, plane_state, &roi, state);
+
+	update_scale(state, &roi, &scale);
+
+	if (plane_state->degamma_changed)
+		update_degamma(dc, plane, plane_state);
+
+	pos.start_x = dest->x1;
+	pos.start_y = dest->y1;
+	pos.end_x = dest->x2;
+	pos.end_y = dest->y2;
+
+	blend.alpha = (u8)(state->alpha >> 8);
+	blend.blend_mode = (u8)(state->pixel_blend_mode);
+
+	update_color_mgmt(dc, plane->id, &fb, plane_state);
+
+	dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
+}
+
+static void update_qos(struct vs_dc *dc, struct vs_plane *plane,
+		       struct drm_plane *drm_plane,
+		       struct drm_atomic_state *drm_state)
+{
+	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
+									   drm_plane);
+	struct vs_plane_state *plane_state = to_vs_plane_state(state);
+	struct drm_vs_watermark *data;
+	struct dc_hw_qos qos;
+
+	if (plane_state->watermark) {
+		data = plane_state->watermark->data;
+
+		if (data->qos_high) {
+			if (data->qos_low > data->qos_high)
+				data->qos_low = data->qos_high;
+
+			qos.low_value = data->qos_low & 0x0F;
+			qos.high_value = data->qos_high & 0x0F;
+			dc_hw_update_qos(&dc->hw, &qos);
+		}
+	}
+}
+
+static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor)
+{
+	u8 size_type;
+
+	switch (state->crtc_w) {
+	case 32:
+		size_type = CURSOR_SIZE_32X32;
+		break;
+	case 64:
+		size_type = CURSOR_SIZE_64X64;
+		break;
+	default:
+		size_type = CURSOR_SIZE_32X32;
+		break;
+	}
+
+	cursor->size = size_type;
+}
+
+static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
+				struct drm_plane *drm_plane,
+				struct drm_atomic_state *drm_state)
+{
+	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
+								       drm_plane);
+	struct vs_plane_state *plane_state = to_vs_plane_state(state);
+	struct drm_framebuffer *drm_fb = state->fb;
+	struct dc_hw_cursor cursor;
+
+	cursor.address = plane_state->dma_addr[0];
+	cursor.x = state->crtc_x;
+	cursor.y = state->crtc_y;
+	cursor.hot_x = drm_fb->hot_x;
+	cursor.hot_y = drm_fb->hot_y;
+	cursor.display_id = to_vs_display_id(dc, state->crtc);
+	update_cursor_size(state, &cursor);
+	cursor.enable = true;
+
+	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
+}
+
+void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
+			struct drm_plane *drm_plane,
+			struct drm_atomic_state *drm_state)
+{
+	update_plane(dc, plane, drm_plane, drm_state);
+	update_qos(dc, plane, drm_plane, drm_state);
+}
+
+void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
+			       struct drm_plane *drm_plane,
+			       struct drm_atomic_state *drm_state)
+{
+	update_cursor_plane(dc, plane, drm_plane, drm_state);
+}
+
+void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
+			 struct drm_plane_state *old_state)
+{
+	struct dc_hw_fb fb = {0};
+
+	fb.enable = false;
+	dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL, NULL, NULL);
+}
+
+void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
+				struct drm_plane_state *old_state)
+{
+	struct dc_hw_cursor cursor = {0};
+
+	cursor.enable = false;
+	cursor.display_id = to_vs_display_id(dc, old_state->crtc);
+	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
+}
+
+static bool vs_dc_mod_supported(const struct vs_plane_info *plane_info,
+				u64 modifier)
+{
+	const u64 *mods;
+
+	if (!plane_info->modifiers)
+		return false;
+
+	for (mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID; mods++) {
+		if (*mods == modifier)
+			return true;
+	}
+
+	return false;
+}
+
+int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
+		      struct drm_atomic_state *state)
+{
+	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+										 plane);
+
+	struct drm_framebuffer *fb = new_plane_state->fb;
+	const struct vs_plane_info *plane_info;
+	struct drm_crtc *crtc = new_plane_state->crtc;
+	struct drm_crtc_state *crtc_state;
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+
+	plane_info = &dc->hw.info->planes[vs_plane->id];
+
+	if (fb->width < plane_info->min_width ||
+	    fb->width > plane_info->max_width ||
+	    fb->height < plane_info->min_height ||
+	    fb->height > plane_info->max_height)
+		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n",
+			     vs_plane->id);
+
+	if (!vs_dc_mod_supported(plane_info, fb->modifier)) {
+		drm_err(plane->dev, "unsupported modifier on plane%d.\n", vs_plane->id);
+		return -EINVAL;
+	}
+
+	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
+	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
+						  plane_info->min_scale,
+						  plane_info->max_scale,
+						  true, true);
+}
+
+int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
+			     struct drm_atomic_state *state)
+{
+	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+									   plane);
+	struct drm_framebuffer *fb = new_plane_state->fb;
+	const struct vs_plane_info *plane_info;
+	struct drm_crtc *crtc = new_plane_state->crtc;
+	struct drm_crtc_state *crtc_state;
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+
+	plane_info = &dc->hw.info->planes[vs_plane->id];
+
+	if (fb->width < plane_info->min_width ||
+	    fb->width > plane_info->max_width ||
+	    fb->height < plane_info->min_height ||
+	    fb->height > plane_info->max_height)
+		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n", vs_plane->id);
+
+	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
+	if (IS_ERR(crtc_state))
+		return -EINVAL;
+
+	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
+						plane_info->min_scale,
+						plane_info->max_scale,
+						true, true);
+}
+
+static void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
+{
+	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
+
+	drm_crtc_handle_vblank(crtc);
+
+	vs_crtc_state->underflow = underflow;
+}
+
+static irqreturn_t dc_isr(int irq, void *data)
+{
+	struct vs_dc *dc = data;
+	struct vs_dc_info *dc_info = dc->hw.info;
+	u32 i, ret;
+
+	ret = dc_hw_get_interrupt(&dc->hw);
+
+	for (i = 0; i < dc_info->panel_num; i++)
+		vs_crtc_handle_vblank(&dc->crtc[i]->base, dc_hw_check_underflow(&dc->hw));
+
+	return IRQ_HANDLED;
+}
+
+void vs_dc_commit(struct vs_dc *dc)
+{
+	dc_hw_enable_shadow_register(&dc->hw, false);
+
+	dc_hw_commit(&dc->hw);
+
+	if (dc->first_frame)
+		dc->first_frame = false;
+
+	dc_hw_enable_shadow_register(&dc->hw, true);
+}
+
+static int dc_bind(struct device *dev, struct device *master, void *data)
+{
+	struct drm_device *drm_dev = data;
+	struct vs_dc *dc = dev_get_drvdata(dev);
+	struct device_node *port;
+	struct vs_crtc *crtc;
+	struct vs_dc_info *dc_info;
+	struct vs_plane *plane;
+	struct vs_plane_info *plane_info;
+	int i, ret;
+	u32 ctrc_mask = 0;
+
+	if (!drm_dev || !dc) {
+		dev_err(dev, "devices are not created.\n");
+		return -ENODEV;
+	}
+
+	ret = dc_init(dev);
+	if (ret < 0) {
+		drm_err(drm_dev, "Failed to initialize DC hardware.\n");
+		return ret;
+	}
+
+	port = of_get_child_by_name(dev->of_node, "port");
+	if (!port) {
+		drm_err(drm_dev, "no port node found\n");
+		return -ENODEV;
+	}
+	of_node_put(port);
+
+	dc_info = dc->hw.info;
+
+	for (i = 0; i < dc_info->panel_num; i++) {
+		crtc = vs_crtc_create(drm_dev, dc_info);
+		if (!crtc) {
+			drm_err(drm_dev, "Failed to create CRTC.\n");
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		crtc->base.port = port;
+		crtc->dev = dev;
+		dc->crtc[i] = crtc;
+		ctrc_mask |= drm_crtc_mask(&crtc->base);
+	}
+
+	for (i = 0; i < dc_info->plane_num; i++) {
+		plane_info = (struct vs_plane_info *)&dc_info->planes[i];
+
+		if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
+			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
+						drm_crtc_mask(&dc->crtc[0]->base));
+		} else if (!strcmp(plane_info->name, "Primary_1") ||
+				   !strcmp(plane_info->name, "Cursor_1")) {
+			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
+						drm_crtc_mask(&dc->crtc[1]->base));
+		} else {
+			plane = vs_plane_create(drm_dev, plane_info,
+						dc_info->layer_num, ctrc_mask);
+		}
+
+		if (IS_ERR(plane)) {
+			dev_err(dev, "failed to construct plane\n");
+			return PTR_ERR(plane);
+		}
+
+		plane->id = i;
+		dc->planes[i].id = plane_info->id;
+
+		if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
+			if (!strcmp(plane_info->name, "Primary"))
+				dc->crtc[0]->base.primary = &plane->base;
+			else
+				dc->crtc[1]->base.primary = &plane->base;
+			drm_dev->mode_config.min_width = plane_info->min_width;
+			drm_dev->mode_config.min_height =
+							plane_info->min_height;
+			drm_dev->mode_config.max_width = plane_info->max_width;
+			drm_dev->mode_config.max_height =
+							plane_info->max_height;
+		}
+
+		if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
+			if (!strcmp(plane_info->name, "Cursor"))
+				dc->crtc[0]->base.cursor = &plane->base;
+			else
+				dc->crtc[1]->base.cursor = &plane->base;
+			drm_dev->mode_config.cursor_width =
+							plane_info->max_width;
+			drm_dev->mode_config.cursor_height =
+							plane_info->max_height;
+		}
+	}
+
+	vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
+	return 0;
+}
+
+static void dc_unbind(struct device *dev, struct device *master, void *data)
+{
+	dc_deinit(dev);
+}
+
+const struct component_ops dc_component_ops = {
+	.bind = dc_bind,
+	.unbind = dc_unbind,
+};
+
+static const struct of_device_id dc_driver_dt_match[] = {
+	{ .compatible = "starfive,jh7110-dc8200", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
+
+static int dc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct vs_dc *dc;
+	int irq, ret, i;
+
+	dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
+	if (!dc)
+		return -ENOMEM;
+
+	dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(dc->hw.hi_base))
+		return PTR_ERR(dc->hw.hi_base);
+
+	dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
+	if (IS_ERR(dc->hw.reg_base))
+		return PTR_ERR(dc->hw.reg_base);
+
+	dc->nclks = ARRAY_SIZE(dc->clk_vout);
+	for (i = 0; i < dc->nclks; ++i)
+		dc->clk_vout[i].id = vout_clocks[i];
+	ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
+	if (ret) {
+		dev_err(dev, "Failed to get clk controls\n");
+		return ret;
+	}
+
+	dc->nrsts = ARRAY_SIZE(dc->rst_vout);
+	for (i = 0; i < dc->nrsts; ++i)
+		dc->rst_vout[i].id = vout_resets[i];
+	ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
+						 dc->rst_vout);
+	if (ret) {
+		dev_err(dev, "Failed to get reset controls\n");
+		return ret;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+
+	ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
+	if (ret < 0) {
+		dev_err(dev, "Failed to install irq:%u.\n", irq);
+		return ret;
+	}
+
+	dev_set_drvdata(dev, dc);
+
+	return component_add(dev, &dc_component_ops);
+}
+
+static int dc_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	component_del(dev, &dc_component_ops);
+
+	dev_set_drvdata(dev, NULL);
+
+	return 0;
+}
+
+struct platform_driver dc_platform_driver = {
+	.probe = dc_probe,
+	.remove = dc_remove,
+	.driver = {
+		.name = "vs-dc",
+		.of_match_table = of_match_ptr(dc_driver_dt_match),
+	},
+};
+
+MODULE_AUTHOR("StarFive Corporation");
+MODULE_DESCRIPTION("VeriSilicon DC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
new file mode 100644
index 000000000..8e96fd32c
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_DC_H__
+#define __VS_DC_H__
+
+#include <linux/clk.h>
+#include <linux/mm_types.h>
+#include <linux/reset.h>
+
+#include <drm/drm_fourcc.h>
+#include <drm/drm_modes.h>
+
+#include "vs_crtc.h"
+#include "vs_dc_hw.h"
+#include "vs_plane.h"
+
+struct vs_dc_plane {
+	enum dc_hw_plane_id id;
+};
+
+enum vout_clk {
+	CLK_VOUT_NOC_DISP = 0,
+	CLK_VOUT_PIX0,
+	CLK_VOUT_PIX1,
+	CLK_VOUT_CORE,
+	CLK_VOUT_AXI,
+	CLK_VOUT_AHB,
+	CLK_VOUT_HDMI_PIX,
+	CLK_VOUT_SOC_PIX,
+	CLK_VOUT_NUM
+};
+
+enum rst_vout {
+	RST_VOUT_AXI = 0,
+	RST_VOUT_AHB,
+	RST_VOUT_CORE,
+	RST_VOUT_NUM
+};
+
+struct vs_dc {
+	struct vs_crtc		*crtc[DC_DISPLAY_NUM];
+	struct dc_hw		hw;
+	bool			first_frame;
+
+	struct vs_dc_plane	planes[PLANE_NUM];
+	struct clk_bulk_data	clk_vout[CLK_VOUT_NUM];
+	int			nclks;
+	struct reset_control_bulk_data rst_vout[RST_VOUT_NUM];
+	int			nrsts;
+};
+
+void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc);
+void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc);
+
+void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
+		     struct drm_color_lut *lut, unsigned int size);
+void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc, bool enable);
+void vs_dc_enable_vblank(struct vs_dc *dc, bool enable);
+void vs_dc_commit(struct vs_dc *dc);
+void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
+			struct drm_plane *drm_plane,
+			struct drm_atomic_state *drm_state);
+void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
+			 struct drm_plane_state *old_state);
+int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
+		      struct drm_atomic_state *state);
+void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
+			       struct drm_plane *drm_plane,
+			       struct drm_atomic_state *drm_state);
+void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
+				struct drm_plane_state *old_state);
+int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
+			     struct drm_atomic_state *state);
+
+extern struct platform_driver dc_platform_driver;
+
+#endif /* __VS_DC_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
new file mode 100644
index 000000000..e5acb7eb4
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
@@ -0,0 +1,1959 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/bits.h>
+#include <linux/io.h>
+#include <linux/media-bus-format.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_fourcc.h>
+#include <drm/vs_drm.h>
+
+#include "vs_dc_hw.h"
+#include "vs_type.h"
+
+static const u32 horkernel[] = {
+	0x00000000, 0x20000000, 0x00002000, 0x00000000,
+	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
+	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
+	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
+	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
+	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
+	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
+	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
+	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
+	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
+	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
+	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
+	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
+	0x00000000, 0x00000000, 0x00000000, 0x01470000,
+	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
+	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
+	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
+	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00004000, 0x00000000,
+	0x00000000, 0x00000000, 0x20002000, 0x00000000,
+	0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
+	0x00000000, 0x00000000, 0x00000000, 0x27e1181f,
+	0x00000000, 0x00000000, 0x00000000, 0x14680000,
+	0x00002b98, 0x00000000, 0x00000000, 0x00000000,
+	0x2f1010f0, 0x00000000, 0x00000000, 0x00000000,
+	0x0dc70000, 0x00003239, 0x00000000, 0x00000000,
+	0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
+	0x00000000, 0x087f0000, 0x00003781, 0x00000000,
+	0x00000000, 0x00000000, 0x399a0666, 0x00000000,
+	0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
+	0x00000000, 0x00000000, 0x00000000, 0x3cc4033c,
+	0x00000000, 0x00000000, 0x00000000, 0x021f0000,
+};
+
+#define H_COEF_SIZE (sizeof(horkernel) / sizeof(u32))
+
+static const u32 verkernel[] = {
+	0x00000000, 0x20000000, 0x00002000, 0x00000000,
+	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
+	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
+	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
+	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
+	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
+	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
+	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
+	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
+	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
+	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
+	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
+	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
+	0x00000000, 0x00000000, 0x00000000, 0x01470000,
+	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
+	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
+	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
+	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00004000, 0x00000000,
+	0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab,
+	0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
+	0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+};
+
+#define V_COEF_SIZE (sizeof(verkernel) / sizeof(u32))
+
+/*
+ * RGB 709->2020 conversion parameters
+ */
+static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = {
+	10279,	5395,	709,
+	1132,	15065,	187,
+	269,	1442,	14674
+};
+
+/*
+ * YUV601 to RGB conversion parameters
+ * YUV2RGB[0]  - [8] : C0 - C8;
+ * YUV2RGB[9]  - [11]: D0 - D2;
+ * YUV2RGB[12] - [13]: Y clamp min & max calue;
+ * YUV2RGB[14] - [15]: UV clamp min & max calue;
+ */
+static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
+	1196,	0,			1640,	1196,
+	-404,	-836,		1196,	2076,
+	0,		-916224,	558336,	-1202944,
+	64,		 940,		64,		960
+};
+
+/*
+ * YUV709 to RGB conversion parameters
+ * YUV2RGB[0]  - [8] : C0 - C8;
+ * YUV2RGB[9]  - [11]: D0 - D2;
+ * YUV2RGB[12] - [13]: Y clamp min & max calue;
+ * YUV2RGB[14] - [15]: UV clamp min & max calue;
+ */
+static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
+	1196,		0,		1844,	1196,
+	-220,		-548,	1196,	2172,
+	0,			-1020672, 316672,  -1188608,
+	64,			940,		64,		960
+};
+
+/*
+ * YUV2020 to RGB conversion parameters
+ * YUV2RGB[0]  - [8] : C0 - C8;
+ * YUV2RGB[9]  - [11]: D0 - D2;
+ * YUV2RGB[12] - [13]: Y clamp min & max calue;
+ * YUV2RGB[14] - [15]: UV clamp min & max calue;
+ */
+static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
+	1196, 0, 1724, 1196,
+	-192, -668, 1196, 2200,
+	0, -959232, 363776, -1202944,
+	64, 940, 64, 960
+};
+
+/*
+ * RGB to YUV2020 conversion parameters
+ * RGB2YUV[0] - [8] : C0 - C8;
+ * RGB2YUV[9] - [11]: D0 - D2;
+ */
+static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = {
+	230,	594,	52,
+	-125,  -323,	448,
+	448,   -412,   -36,
+	64,		512,	512
+};
+
+/*
+ * Degamma table for 709 color space data.
+ */
+static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005,
+	0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024,
+	0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064,
+	0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce,
+	0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166,
+	0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230,
+	0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331,
+	0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c,
+	0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
+	0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d,
+	0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999,
+	0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb,
+	0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65,
+	0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a,
+	0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b,
+	0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc,
+	0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e,
+	0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
+	0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e,
+	0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf,
+	0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea,
+	0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f,
+	0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930,
+	0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f,
+	0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee,
+	0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde,
+	0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
+	0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9,
+	0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406,
+	0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a,
+	0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576,
+	0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd,
+	0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
+};
+
+/*
+ * Degamma table for 2020 color space data.
+ */
+static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
+	0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006,
+	0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a,
+	0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f,
+	0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
+	0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024,
+	0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035,
+	0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d,
+	0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d,
+	0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098,
+	0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3,
+	0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123,
+	0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d,
+	0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
+	0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6,
+	0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1,
+	0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d,
+	0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6,
+	0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920,
+	0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a,
+	0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033,
+	0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593,
+	0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
+	0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656,
+	0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332,
+	0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480,
+	0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1,
+	0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99,
+	0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
+};
+
+/* one is for primary plane and the other is for all overlay planes */
+static const struct dc_hw_plane_reg dc_plane_reg[] = {
+	{
+	.y_address		= DC_FRAMEBUFFER_ADDRESS,
+	.u_address		= DC_FRAMEBUFFER_U_ADDRESS,
+	.v_address		= DC_FRAMEBUFFER_V_ADDRESS,
+	.y_stride		= DC_FRAMEBUFFER_STRIDE,
+	.u_stride		= DC_FRAMEBUFFER_U_STRIDE,
+	.v_stride		= DC_FRAMEBUFFER_V_STRIDE,
+	.size			= DC_FRAMEBUFFER_SIZE,
+	.top_left		= DC_FRAMEBUFFER_TOP_LEFT,
+	.bottom_right	= DC_FRAMEBUFFER_BOTTOM_RIGHT,
+	.scale_factor_x			= DC_FRAMEBUFFER_SCALE_FACTOR_X,
+	.scale_factor_y			= DC_FRAMEBUFFER_SCALE_FACTOR_Y,
+	.h_filter_coef_index	= DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
+	.h_filter_coef_data		= DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
+	.v_filter_coef_index	= DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
+	.v_filter_coef_data		= DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
+	.init_offset			= DC_FRAMEBUFFER_INIT_OFFSET,
+	.color_key				= DC_FRAMEBUFFER_COLOR_KEY,
+	.color_key_high			= DC_FRAMEBUFFER_COLOR_KEY_HIGH,
+	.clear_value			= DC_FRAMEBUFFER_CLEAR_VALUE,
+	.color_table_index		= DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
+	.color_table_data		= DC_FRAMEBUFFER_COLOR_TABLE_DATA,
+	.scale_config			= DC_FRAMEBUFFER_SCALE_CONFIG,
+	.water_mark				= DC_FRAMEBUFFER_WATER_MARK,
+	.degamma_index			= DC_FRAMEBUFFER_DEGAMMA_INDEX,
+	.degamma_data			= DC_FRAMEBUFFER_DEGAMMA_DATA,
+	.degamma_ex_data		= DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
+	.src_global_color		= DC_FRAMEBUFFER_SRC_GLOBAL_COLOR,
+	.dst_global_color		= DC_FRAMEBUFFER_DST_GLOBAL_COLOR,
+	.blend_config			= DC_FRAMEBUFFER_BLEND_CONFIG,
+	.roi_origin				= DC_FRAMEBUFFER_ROI_ORIGIN,
+	.roi_size				= DC_FRAMEBUFFER_ROI_SIZE,
+	.yuv_to_rgb_coef0			= DC_FRAMEBUFFER_YUVTORGB_COEF0,
+	.yuv_to_rgb_coef1			= DC_FRAMEBUFFER_YUVTORGB_COEF1,
+	.yuv_to_rgb_coef2			= DC_FRAMEBUFFER_YUVTORGB_COEF2,
+	.yuv_to_rgb_coef3			= DC_FRAMEBUFFER_YUVTORGB_COEF3,
+	.yuv_to_rgb_coef4			= DC_FRAMEBUFFER_YUVTORGB_COEF4,
+	.yuv_to_rgb_coefd0			= DC_FRAMEBUFFER_YUVTORGB_COEFD0,
+	.yuv_to_rgb_coefd1			= DC_FRAMEBUFFER_YUVTORGB_COEFD1,
+	.yuv_to_rgb_coefd2			= DC_FRAMEBUFFER_YUVTORGB_COEFD2,
+	.y_clamp_bound				= DC_FRAMEBUFFER_Y_CLAMP_BOUND,
+	.uv_clamp_bound				= DC_FRAMEBUFFER_UV_CLAMP_BOUND,
+	.rgb_to_rgb_coef0			= DC_FRAMEBUFFER_RGBTORGB_COEF0,
+	.rgb_to_rgb_coef1			= DC_FRAMEBUFFER_RGBTORGB_COEF1,
+	.rgb_to_rgb_coef2			= DC_FRAMEBUFFER_RGBTORGB_COEF2,
+	.rgb_to_rgb_coef3			= DC_FRAMEBUFFER_RGBTORGB_COEF3,
+	.rgb_to_rgb_coef4			= DC_FRAMEBUFFER_RGBTORGB_COEF4,
+	},
+	{
+	.y_address		= DC_OVERLAY_ADDRESS,
+	.u_address		= DC_OVERLAY_U_ADDRESS,
+	.v_address		= DC_OVERLAY_V_ADDRESS,
+	.y_stride		= DC_OVERLAY_STRIDE,
+	.u_stride		= DC_OVERLAY_U_STRIDE,
+	.v_stride		= DC_OVERLAY_V_STRIDE,
+	.size			= DC_OVERLAY_SIZE,
+	.top_left		= DC_OVERLAY_TOP_LEFT,
+	.bottom_right	= DC_OVERLAY_BOTTOM_RIGHT,
+	.scale_factor_x	= DC_OVERLAY_SCALE_FACTOR_X,
+	.scale_factor_y	= DC_OVERLAY_SCALE_FACTOR_Y,
+	.h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
+	.h_filter_coef_data  = DC_OVERLAY_H_FILTER_COEF_DATA,
+	.v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
+	.v_filter_coef_data  = DC_OVERLAY_V_FILTER_COEF_DATA,
+	.init_offset		 = DC_OVERLAY_INIT_OFFSET,
+	.color_key			 = DC_OVERLAY_COLOR_KEY,
+	.color_key_high			= DC_OVERLAY_COLOR_KEY_HIGH,
+	.clear_value		 = DC_OVERLAY_CLEAR_VALUE,
+	.color_table_index	 = DC_OVERLAY_COLOR_TABLE_INDEX,
+	.color_table_data	 = DC_OVERLAY_COLOR_TABLE_DATA,
+	.scale_config		 = DC_OVERLAY_SCALE_CONFIG,
+	.water_mark				= DC_OVERLAY_WATER_MARK,
+	.degamma_index		 = DC_OVERLAY_DEGAMMA_INDEX,
+	.degamma_data		 = DC_OVERLAY_DEGAMMA_DATA,
+	.degamma_ex_data	 = DC_OVERLAY_DEGAMMA_EX_DATA,
+	.src_global_color	 = DC_OVERLAY_SRC_GLOBAL_COLOR,
+	.dst_global_color	 = DC_OVERLAY_DST_GLOBAL_COLOR,
+	.blend_config		 = DC_OVERLAY_BLEND_CONFIG,
+	.roi_origin				= DC_OVERLAY_ROI_ORIGIN,
+	.roi_size				= DC_OVERLAY_ROI_SIZE,
+	.yuv_to_rgb_coef0		 = DC_OVERLAY_YUVTORGB_COEF0,
+	.yuv_to_rgb_coef1		 = DC_OVERLAY_YUVTORGB_COEF1,
+	.yuv_to_rgb_coef2		 = DC_OVERLAY_YUVTORGB_COEF2,
+	.yuv_to_rgb_coef3		 = DC_OVERLAY_YUVTORGB_COEF3,
+	.yuv_to_rgb_coef4			= DC_OVERLAY_YUVTORGB_COEF4,
+	.yuv_to_rgb_coefd0			= DC_OVERLAY_YUVTORGB_COEFD0,
+	.yuv_to_rgb_coefd1			= DC_OVERLAY_YUVTORGB_COEFD1,
+	.yuv_to_rgb_coefd2			= DC_OVERLAY_YUVTORGB_COEFD2,
+	.y_clamp_bound		 = DC_OVERLAY_Y_CLAMP_BOUND,
+	.uv_clamp_bound		 = DC_OVERLAY_UV_CLAMP_BOUND,
+	.rgb_to_rgb_coef0		 = DC_OVERLAY_RGBTORGB_COEF0,
+	.rgb_to_rgb_coef1		 = DC_OVERLAY_RGBTORGB_COEF1,
+	.rgb_to_rgb_coef2		 = DC_OVERLAY_RGBTORGB_COEF2,
+	.rgb_to_rgb_coef3		 = DC_OVERLAY_RGBTORGB_COEF3,
+	.rgb_to_rgb_coef4		 = DC_OVERLAY_RGBTORGB_COEF4,
+	},
+};
+
+static const u32 primary_overlay_format0[] = {
+	DRM_FORMAT_XRGB4444,
+	DRM_FORMAT_XBGR4444,
+	DRM_FORMAT_RGBX4444,
+	DRM_FORMAT_BGRX4444,
+	DRM_FORMAT_ARGB4444,
+	DRM_FORMAT_ABGR4444,
+	DRM_FORMAT_RGBA4444,
+	DRM_FORMAT_BGRA4444,
+	DRM_FORMAT_XRGB1555,
+	DRM_FORMAT_XBGR1555,
+	DRM_FORMAT_RGBX5551,
+	DRM_FORMAT_BGRX5551,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_ABGR1555,
+	DRM_FORMAT_RGBA5551,
+	DRM_FORMAT_BGRA5551,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565,
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_RGBX8888,
+	DRM_FORMAT_BGRX8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_RGBA8888,
+	DRM_FORMAT_BGRA8888,
+	DRM_FORMAT_ARGB2101010,
+	DRM_FORMAT_ABGR2101010,
+	DRM_FORMAT_RGBA1010102,
+	DRM_FORMAT_BGRA1010102,
+	DRM_FORMAT_YUYV,
+	DRM_FORMAT_YVYU,
+	DRM_FORMAT_UYVY,
+	DRM_FORMAT_VYUY,
+	DRM_FORMAT_YVU420,
+	DRM_FORMAT_YUV420,
+	DRM_FORMAT_NV12,
+	DRM_FORMAT_NV21,
+	DRM_FORMAT_NV16,
+	DRM_FORMAT_NV61,
+	DRM_FORMAT_P010,
+};
+
+static const u32 primary_overlay_format1[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_RGBX8888,
+	DRM_FORMAT_BGRX8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_RGBA8888,
+	DRM_FORMAT_BGRA8888,
+	DRM_FORMAT_ARGB2101010,
+	DRM_FORMAT_ABGR2101010,
+	DRM_FORMAT_RGBA1010102,
+	DRM_FORMAT_BGRA1010102,
+	DRM_FORMAT_NV12,
+	DRM_FORMAT_NV21,
+	DRM_FORMAT_YUV444,
+};
+
+static const u32 cursor_formats[] = {
+	DRM_FORMAT_ARGB8888
+};
+
+static const u64 format_modifier0[] = {
+	DRM_FORMAT_MOD_LINEAR,
+	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8),
+	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8),
+	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X8),
+	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X4),
+	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4),
+	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8),
+	DRM_FORMAT_MOD_INVALID
+};
+
+static const u64 secondary_format_modifiers[] = {
+	DRM_FORMAT_MOD_LINEAR,
+	DRM_FORMAT_MOD_INVALID
+};
+
+#define FRAC_16_16(mult, div)	 (((mult) << 16) / (div))
+
+static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = {
+	{
+		/* DC_REV_0 */
+		{
+		.name			= "Primary",
+		.id				= PRIMARY_PLANE_0,
+		.type			= DRM_PLANE_TYPE_PRIMARY,
+		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
+		.formats		= primary_overlay_format0,
+		.num_modifiers	= ARRAY_SIZE(format_modifier0),
+		.modifiers		= format_modifier0,
+		.min_width		= 0,
+		.min_height		= 0,
+		.max_width		= 4096,
+		.max_height		= 4096,
+		.rotation		= DRM_MODE_ROTATE_0 |
+							DRM_MODE_ROTATE_90 |
+							DRM_MODE_ROTATE_180 |
+							DRM_MODE_ROTATE_270 |
+							DRM_MODE_REFLECT_X |
+							DRM_MODE_REFLECT_Y,
+		.blend_mode		= BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+							BIT(DRM_MODE_BLEND_PREMULTI) |
+							BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 0,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name			= "Overlay",
+		.id				= OVERLAY_PLANE_0,
+		.type			= DRM_PLANE_TYPE_OVERLAY,
+		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
+		.formats		= primary_overlay_format0,
+		.num_modifiers	= ARRAY_SIZE(format_modifier0),
+		.modifiers		= format_modifier0,
+		.min_width		= 0,
+		.min_height		= 0,
+		.max_width		= 4096,
+		.max_height		= 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 1,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name			= "Overlay_1",
+		.id				= OVERLAY_PLANE_1,
+		.type			= DRM_PLANE_TYPE_OVERLAY,
+		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
+		.formats		= primary_overlay_format0,
+		.num_modifiers	= ARRAY_SIZE(secondary_format_modifiers),
+		.modifiers		= secondary_format_modifiers,
+		.min_width		= 0,
+		.min_height		= 0,
+		.max_width		= 4096,
+		.max_height		= 4096,
+		.rotation		= 0,
+		.blend_mode		= BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+							BIT(DRM_MODE_BLEND_PREMULTI) |
+							BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 2,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Primary_1",
+		.id				= PRIMARY_PLANE_1,
+		.type		 = DRM_PLANE_TYPE_PRIMARY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format0),
+		.formats	 = primary_overlay_format0,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 3,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay_2",
+		.id				= OVERLAY_PLANE_2,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format0),
+		.formats	 = primary_overlay_format0,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 4,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay_3",
+		.id			= OVERLAY_PLANE_3,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format0),
+		.formats	 = primary_overlay_format0,
+		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
+		.modifiers	 = secondary_format_modifiers,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = 0,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 5,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Cursor",
+		.id				= CURSOR_PLANE_0,
+		.type		 = DRM_PLANE_TYPE_CURSOR,
+		.num_formats = ARRAY_SIZE(cursor_formats),
+		.formats	 = cursor_formats,
+		.num_modifiers = 0,
+		.modifiers	 = NULL,
+		.min_width	 = 32,
+		.min_height  = 32,
+		.max_width	 = 64,
+		.max_height  = 64,
+		.rotation	 = 0,
+		.degamma_size = 0,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 255,
+		.watermark	 = false,
+		.color_mgmt  = false,
+		.roi		 = false,
+		},
+		{
+		.name		 = "Cursor_1",
+		.id				= CURSOR_PLANE_1,
+		.type		 = DRM_PLANE_TYPE_CURSOR,
+		.num_formats = ARRAY_SIZE(cursor_formats),
+		.formats	 = cursor_formats,
+		.num_modifiers = 0,
+		.modifiers	 = NULL,
+		.min_width	 = 32,
+		.min_height  = 32,
+		.max_width	 = 64,
+		.max_height  = 64,
+		.rotation	 = 0,
+		.degamma_size = 0,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 255,
+		.watermark	 = false,
+		.color_mgmt  = false,
+		.roi		 = false,
+		},
+	},
+	{
+		/* DC_REV_1 */
+		{
+		.name		 = "Primary",
+		.id				= PRIMARY_PLANE_0,
+		.type		 = DRM_PLANE_TYPE_PRIMARY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format0),
+		.formats	 = primary_overlay_format0,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 0,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay",
+		.id				= OVERLAY_PLANE_0,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format0),
+		.formats	 = primary_overlay_format0,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 1,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Primary_1",
+		.id				= PRIMARY_PLANE_1,
+		.type		 = DRM_PLANE_TYPE_PRIMARY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format0),
+		.formats	 = primary_overlay_format0,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 2,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay_2",
+		.id				= OVERLAY_PLANE_2,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format0),
+		.formats	 = primary_overlay_format0,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 3,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Cursor",
+		.id				= CURSOR_PLANE_0,
+		.type		 = DRM_PLANE_TYPE_CURSOR,
+		.num_formats = ARRAY_SIZE(cursor_formats),
+		.formats	 = cursor_formats,
+		.num_modifiers = 0,
+		.modifiers	 = NULL,
+		.min_width	 = 32,
+		.min_height  = 32,
+		.max_width	 = 64,
+		.max_height  = 64,
+		.rotation	 = 0,
+		.degamma_size = 0,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 255,
+		.watermark	 = false,
+		.color_mgmt  = false,
+		.roi		 = false,
+		},
+		{
+		.name		 = "Cursor_1",
+		.id				= CURSOR_PLANE_1,
+		.type		 = DRM_PLANE_TYPE_CURSOR,
+		.num_formats = ARRAY_SIZE(cursor_formats),
+		.formats	 = cursor_formats,
+		.num_modifiers = 0,
+		.modifiers	 = NULL,
+		.min_width	 = 32,
+		.min_height  = 32,
+		.max_width	 = 64,
+		.max_height  = 64,
+		.rotation	 = 0,
+		.degamma_size = 0,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 255,
+		.watermark	 = false,
+		.color_mgmt  = false,
+		.roi		 = false,
+		},
+	},
+	{
+		/* DC_REV_2 */
+		{
+		.name		 = "Primary",
+		.id				= PRIMARY_PLANE_0,
+		.type		 = DRM_PLANE_TYPE_PRIMARY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format1),
+		.formats	 = primary_overlay_format1,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 0,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay",
+		.id				= OVERLAY_PLANE_0,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format1),
+		.formats	 = primary_overlay_format1,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 1,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay_1",
+		.id				= OVERLAY_PLANE_1,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format1),
+		.formats	 = primary_overlay_format1,
+		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
+		.modifiers	 = secondary_format_modifiers,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = 0,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 2,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Primary_1",
+		.id				= PRIMARY_PLANE_1,
+		.type		 = DRM_PLANE_TYPE_PRIMARY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format1),
+		.formats	 = primary_overlay_format1,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 3,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay_2",
+		.id				= OVERLAY_PLANE_2,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format1),
+		.formats	 = primary_overlay_format1,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 4,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay_3",
+		.id				= OVERLAY_PLANE_3,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format1),
+		.formats	 = primary_overlay_format1,
+		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
+		.modifiers	 = secondary_format_modifiers,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = 0,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 5,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Cursor",
+		.id				= CURSOR_PLANE_0,
+		.type		 = DRM_PLANE_TYPE_CURSOR,
+		.num_formats = ARRAY_SIZE(cursor_formats),
+		.formats	 = cursor_formats,
+		.num_modifiers = 0,
+		.modifiers	 = NULL,
+		.min_width	 = 32,
+		.min_height  = 32,
+		.max_width	 = 64,
+		.max_height  = 64,
+		.rotation	 = 0,
+		.degamma_size = 0,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 255,
+		.watermark	 = false,
+		.color_mgmt  = false,
+		.roi		 = false,
+		},
+		{
+		.name		 = "Cursor_1",
+		.id				= CURSOR_PLANE_1,
+		.type		 = DRM_PLANE_TYPE_CURSOR,
+		.num_formats = ARRAY_SIZE(cursor_formats),
+		.formats	 = cursor_formats,
+		.num_modifiers = 0,
+		.modifiers	 = NULL,
+		.min_width	 = 32,
+		.min_height  = 32,
+		.max_width	 = 64,
+		.max_height  = 64,
+		.rotation	 = 0,
+		.degamma_size = 0,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 255,
+		.watermark	 = false,
+		.color_mgmt  = false,
+		.roi		 = false,
+		},
+	},
+};
+
+static const struct vs_dc_info dc_info[] = {
+	{
+		/* DC_REV_0 */
+		.name			= "DC8200",
+		.panel_num		= 2,
+		.plane_num		= 8,
+		.planes			= dc_hw_planes[DC_REV_0],
+		.layer_num		= 6,
+		.max_bpc		= 10,
+		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
+						  DRM_COLOR_FORMAT_YCBCR444 |
+						  DRM_COLOR_FORMAT_YCBCR422 |
+						  DRM_COLOR_FORMAT_YCBCR420,
+		.gamma_size		= GAMMA_EX_SIZE,
+		.gamma_bits		= 12,
+		.pitch_alignment	= 128,
+		.pipe_sync		= false,
+		.background		= true,
+		.panel_sync		= true,
+		.cap_dec		= true,
+	},
+	{
+		/* DC_REV_1 */
+		.name			= "DC8200",
+		.panel_num		= 2,
+		.plane_num		= 6,
+		.planes			= dc_hw_planes[DC_REV_1],
+		.layer_num		= 4,
+		.max_bpc		= 10,
+		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
+						  DRM_COLOR_FORMAT_YCBCR444 |
+						  DRM_COLOR_FORMAT_YCBCR422 |
+						  DRM_COLOR_FORMAT_YCBCR420,
+		.gamma_size		= GAMMA_EX_SIZE,
+		.gamma_bits		= 12,
+		.pitch_alignment	= 128,
+		.pipe_sync		= false,
+		.background		= true,
+		.panel_sync		= true,
+		.cap_dec		= true,
+	},
+	{
+		/* DC_REV_2 */
+		.name			= "DC8200",
+		.panel_num		= 2,
+		.plane_num		= 8,
+		.planes			= dc_hw_planes[DC_REV_2],
+		.layer_num		= 6,
+		.max_bpc		= 10,
+		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
+						  DRM_COLOR_FORMAT_YCBCR444 |
+						  DRM_COLOR_FORMAT_YCBCR422 |
+						  DRM_COLOR_FORMAT_YCBCR420,
+		.gamma_size		= GAMMA_EX_SIZE,
+		.gamma_bits		= 12,
+		.pitch_alignment	= 128,
+		.pipe_sync		= false,
+		.background		= true,
+		.panel_sync		= true,
+		.cap_dec		= false,
+	},
+};
+
+static const struct dc_hw_funcs hw_func;
+
+static inline u32 hi_read(struct dc_hw *hw, u32 reg)
+{
+	return readl(hw->hi_base + reg);
+}
+
+static inline void hi_write(struct dc_hw *hw, u32 reg, u32 value)
+{
+	writel(value, hw->hi_base + reg);
+}
+
+static inline void dc_write(struct dc_hw *hw, u32 reg, u32 value)
+{
+	writel(value, hw->reg_base + reg - DC_REG_BASE);
+}
+
+static inline u32 dc_read(struct dc_hw *hw, u32 reg)
+{
+	u32 value = readl(hw->reg_base + reg - DC_REG_BASE);
+
+	return value;
+}
+
+static inline void dc_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear)
+{
+	u32 value = dc_read(hw, reg);
+
+	value &= ~clear;
+	value |= set;
+	dc_write(hw, reg, value);
+}
+
+static void load_default_filter(struct dc_hw *hw,
+				const struct dc_hw_plane_reg *reg, u32 offset)
+{
+	u8 i;
+
+	dc_write(hw, reg->scale_config + offset, 0x33);
+	dc_write(hw, reg->init_offset + offset, 0x80008000);
+	dc_write(hw, reg->h_filter_coef_index + offset, 0x00);
+	for (i = 0; i < H_COEF_SIZE; i++)
+		dc_write(hw, reg->h_filter_coef_data + offset, horkernel[i]);
+
+	dc_write(hw, reg->v_filter_coef_index + offset, 0x00);
+	for (i = 0; i < V_COEF_SIZE; i++)
+		dc_write(hw, reg->v_filter_coef_data + offset, verkernel[i]);
+}
+
+static void load_rgb_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
+			    u32 offset, u16 *table)
+{
+	dc_write(hw, reg->rgb_to_rgb_coef0 + offset, table[0] | (table[1] << 16));
+	dc_write(hw, reg->rgb_to_rgb_coef1 + offset, table[2] | (table[3] << 16));
+	dc_write(hw, reg->rgb_to_rgb_coef2 + offset, table[4] | (table[5] << 16));
+	dc_write(hw, reg->rgb_to_rgb_coef3 + offset, table[6] | (table[7] << 16));
+	dc_write(hw, reg->rgb_to_rgb_coef4 + offset, table[8]);
+}
+
+static void load_yuv_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
+			    u32 offset, s32 *table)
+{
+	dc_write(hw, reg->yuv_to_rgb_coef0 + offset,
+		 (0xFFFF & table[0]) | (table[1] << 16));
+	dc_write(hw, reg->yuv_to_rgb_coef1 + offset,
+		 (0xFFFF & table[2]) | (table[3] << 16));
+	dc_write(hw, reg->yuv_to_rgb_coef2 + offset,
+		 (0xFFFF & table[4]) | (table[5] << 16));
+	dc_write(hw, reg->yuv_to_rgb_coef3 + offset,
+		 (0xFFFF & table[6]) | (table[7] << 16));
+	dc_write(hw, reg->yuv_to_rgb_coef4 + offset, table[8]);
+	dc_write(hw, reg->yuv_to_rgb_coefd0 + offset, table[9]);
+	dc_write(hw, reg->yuv_to_rgb_coefd1 + offset, table[10]);
+	dc_write(hw, reg->yuv_to_rgb_coefd2 + offset, table[11]);
+	dc_write(hw, reg->y_clamp_bound + offset, table[12] | (table[13] << 16));
+	dc_write(hw, reg->uv_clamp_bound + offset, table[14] | (table[15] << 16));
+}
+
+static void load_rgb_to_yuv(struct dc_hw *hw, u32 offset, s16 *table)
+{
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF0 + offset,
+		 table[0] | (table[1] << 16));
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF1 + offset,
+		 table[2] | (table[3] << 16));
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF2 + offset,
+		 table[4] | (table[5] << 16));
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF3 + offset,
+		 table[6] | (table[7] << 16));
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF4 + offset, table[8]);
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD0 + offset, table[9]);
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD1 + offset, table[10]);
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD2 + offset, table[11]);
+}
+
+static bool is_rgb(enum dc_hw_color_format format)
+{
+	switch (format) {
+	case FORMAT_X4R4G4B4:
+	case FORMAT_A4R4G4B4:
+	case FORMAT_X1R5G5B5:
+	case FORMAT_A1R5G5B5:
+	case FORMAT_R5G6B5:
+	case FORMAT_X8R8G8B8:
+	case FORMAT_A8R8G8B8:
+	case FORMAT_A2R10G10B10:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static void load_degamma_table(struct dc_hw *hw,
+			       const struct dc_hw_plane_reg *reg,
+			       u32 offset, u16 *table)
+{
+	u16 i;
+	u32 value;
+
+	dc_write(hw, reg->degamma_index + offset, 0);
+
+	for (i = 0; i < DEGAMMA_SIZE; i++) {
+		value = table[i] | (table[i] << 16);
+		dc_write(hw, reg->degamma_data + offset, value);
+		dc_write(hw, reg->degamma_ex_data + offset, table[i]);
+	}
+}
+
+static u32 get_addr_offset(u32 id)
+{
+	u32 offset = 0;
+
+	switch (id) {
+	case PRIMARY_PLANE_1:
+	case OVERLAY_PLANE_1:
+		offset = 0x04;
+		break;
+	case OVERLAY_PLANE_2:
+		offset = 0x08;
+		break;
+	case OVERLAY_PLANE_3:
+		offset = 0x0C;
+		break;
+	default:
+		break;
+	}
+
+	return offset;
+}
+
+int dc_hw_init(struct dc_hw *hw)
+{
+	u8 i, id, panel_num, layer_num;
+	u32 offset;
+	u32 revision = hi_read(hw, DC_HW_REVISION);
+	u32 cid = hi_read(hw, DC_HW_CHIP_CID);
+	const struct dc_hw_plane_reg *reg;
+
+	switch (revision) {
+	case 0x5720:
+		hw->rev = DC_REV_0;
+		break;
+	case 0x5721:
+		switch (cid) {
+		case 0x30B:
+			hw->rev = DC_REV_1;
+			break;
+		case 0x310:
+			hw->rev = DC_REV_2;
+			break;
+		default:
+			hw->rev = DC_REV_0;
+			break;
+		}
+		break;
+	default:
+		return -ENXIO;
+	}
+
+	hw->info = (struct vs_dc_info *)&dc_info[hw->rev];
+	hw->func = (struct dc_hw_funcs *)&hw_func;
+
+	layer_num = hw->info->layer_num;
+	for (i = 0; i < layer_num; i++) {
+		id = hw->info->planes[i].id;
+		offset = get_addr_offset(id);
+		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
+			reg = &dc_plane_reg[0];
+		else
+			reg = &dc_plane_reg[1];
+
+		load_default_filter(hw, reg, offset);
+		load_rgb_to_rgb(hw, reg, offset, RGB2RGB);
+	}
+
+	panel_num = hw->info->panel_num;
+	for (i = 0; i < panel_num; i++) {
+		offset = i << 2;
+
+		load_rgb_to_yuv(hw, offset, RGB2YUV);
+		dc_write(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0x111);
+
+		offset = i ? DC_CURSOR_OFFSET : 0;
+		dc_write(hw, DC_CURSOR_BACKGROUND + offset, 0x00FFFFFF);
+		dc_write(hw, DC_CURSOR_FOREGROUND + offset, 0x00AAAAAA);
+	}
+
+	return 0;
+}
+
+void dc_hw_deinit(struct dc_hw *hw)
+{
+	/* Nothing to do */
+}
+
+void dc_hw_update_plane(struct dc_hw *hw, u8 id,
+			struct dc_hw_fb *fb, struct dc_hw_scale *scale,
+			struct dc_hw_position *pos, struct dc_hw_blend *blend)
+{
+	struct dc_hw_plane *plane = &hw->plane[id];
+
+	if (plane) {
+		if (fb) {
+			if (!fb->enable)
+				plane->fb.enable = false;
+			else
+				memcpy(&plane->fb, fb,
+				       sizeof(*fb) - sizeof(fb->dirty));
+			plane->fb.dirty = true;
+		}
+		if (scale) {
+			memcpy(&plane->scale, scale,
+			       sizeof(*scale) - sizeof(scale->dirty));
+			plane->scale.dirty = true;
+		}
+		if (pos) {
+			memcpy(&plane->pos, pos,
+			       sizeof(*pos) - sizeof(pos->dirty));
+			plane->pos.dirty = true;
+		}
+		if (blend) {
+			memcpy(&plane->blend, blend,
+			       sizeof(*blend) - sizeof(blend->dirty));
+			plane->blend.dirty = true;
+		}
+	}
+}
+
+void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode)
+{
+	struct dc_hw_plane *plane = &hw->plane[id];
+
+	if (plane) {
+		if (hw->info->planes[id].degamma_size) {
+			plane->degamma.mode = mode;
+			plane->degamma.dirty = true;
+		} else {
+			plane->degamma.dirty = false;
+		}
+	}
+}
+
+void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi)
+{
+	struct dc_hw_plane *plane = &hw->plane[id];
+
+	if (plane) {
+		memcpy(&plane->roi, roi, sizeof(*roi) - sizeof(roi->dirty));
+		plane->roi.dirty = true;
+	}
+}
+
+void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
+			   struct dc_hw_colorkey *colorkey)
+{
+	struct dc_hw_plane *plane = &hw->plane[id];
+
+	if (plane) {
+		memcpy(&plane->colorkey, colorkey,
+		       sizeof(*colorkey) - sizeof(colorkey->dirty));
+		plane->colorkey.dirty = true;
+	}
+}
+
+void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos)
+{
+	memcpy(&hw->qos, qos, sizeof(*qos) - sizeof(qos->dirty));
+	hw->qos.dirty = true;
+}
+
+void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor)
+{
+	memcpy(&hw->cursor[id], cursor, sizeof(*cursor) - sizeof(cursor->dirty));
+	hw->cursor[id].dirty = true;
+}
+
+void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
+			u16 r, u16 g, u16 b)
+{
+	if (index >= hw->info->gamma_size)
+		return;
+
+	hw->gamma[id].gamma[index][0] = r;
+	hw->gamma[id].gamma[index][1] = g;
+	hw->gamma[id].gamma[index][2] = b;
+	hw->gamma[id].dirty = true;
+}
+
+void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable)
+{
+	hw->gamma[id].enable = enable;
+	hw->gamma[id].dirty = true;
+}
+
+void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display)
+{
+	u8 id = display->id;
+
+	memcpy(&hw->display[id], display, sizeof(*display));
+
+	hw->func->display(hw, display);
+}
+
+void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable)
+{
+	if (enable)
+		hi_write(hw, AQ_INTR_ENBL, 0xFFFFFFFF);
+	else
+		hi_write(hw, AQ_INTR_ENBL, 0);
+}
+
+u32 dc_hw_get_interrupt(struct dc_hw *hw)
+{
+	return hi_read(hw, AQ_INTR_ACKNOWLEDGE);
+}
+
+bool dc_hw_check_underflow(struct dc_hw *hw)
+{
+	return dc_read(hw, DC_FRAMEBUFFER_CONFIG) & BIT(5);
+}
+
+void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable)
+{
+	u32 i, offset;
+	u8 id, layer_num = hw->info->layer_num;
+	u8 panel_num = hw->info->panel_num;
+
+	for (i = 0; i < layer_num; i++) {
+		id = hw->info->planes[i].id;
+		offset = get_addr_offset(id);
+		if (enable) {
+			if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
+				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, BIT(12), 0);
+			else
+				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, BIT(31), 0);
+		} else {
+			if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
+				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, 0, BIT(12));
+			else
+				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, 0, BIT(31));
+		}
+	}
+
+	for (i = 0; i < panel_num; i++) {
+		offset = i << 2;
+		if (enable)
+			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, BIT(0));
+		else
+			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, BIT(0), 0);
+	}
+}
+
+void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id)
+{
+	if (out <= OUT_DP)
+		hw->out[id] = out;
+}
+
+static void gamma_ex_commit(struct dc_hw *hw)
+{
+	u8 panel_num = hw->info->panel_num;
+	u16 i, j;
+	u32 value;
+
+	for (j = 0; j < panel_num; j++) {
+		if (hw->gamma[j].dirty) {
+			if (hw->gamma[j].enable) {
+				dc_write(hw, DC_DISPLAY_GAMMA_EX_INDEX + (j << 2), 0x00);
+				for (i = 0; i < GAMMA_EX_SIZE; i++) {
+					value = hw->gamma[j].gamma[i][2] |
+						(hw->gamma[j].gamma[i][1] << 12);
+					dc_write(hw, DC_DISPLAY_GAMMA_EX_DATA + (j << 2), value);
+					dc_write(hw, DC_DISPLAY_GAMMA_EX_ONE_DATA + (j << 2),
+						 hw->gamma[j].gamma[i][0]);
+				}
+				dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
+					     BIT(13), 0);
+			} else {
+				dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
+					     0, BIT(13));
+			}
+			hw->gamma[j].dirty = false;
+		}
+	}
+}
+
+static void plane_commit(struct dc_hw *hw)
+{
+	struct dc_hw_plane *plane;
+	const struct dc_hw_plane_reg *reg;
+	bool primary = false;
+	u8 id, layer_num = hw->info->layer_num;
+	u32 i, offset;
+
+	for (i = 0; i < layer_num; i++) {
+		plane = &hw->plane[i];
+		id = hw->info->planes[i].id;
+		offset = get_addr_offset(id);
+		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
+			reg = &dc_plane_reg[0];
+			primary = true;
+		} else {
+			reg = &dc_plane_reg[1];
+			primary = false;
+		}
+
+		if (plane->fb.dirty) {
+			if (plane->fb.enable) {
+				dc_write(hw, reg->y_address + offset,
+					 plane->fb.y_address);
+				dc_write(hw, reg->u_address + offset,
+					 plane->fb.u_address);
+				dc_write(hw, reg->v_address + offset,
+					 plane->fb.v_address);
+				dc_write(hw, reg->y_stride + offset,
+					 plane->fb.y_stride);
+				dc_write(hw, reg->u_stride + offset,
+					 plane->fb.u_stride);
+				dc_write(hw, reg->v_stride + offset,
+					 plane->fb.v_stride);
+				dc_write(hw, reg->size + offset,
+					 plane->fb.width |
+					 (plane->fb.height << 15));
+				dc_write(hw, reg->water_mark + offset,
+					 plane->fb.water_mark);
+
+				if (plane->fb.clear_enable)
+					dc_write(hw, reg->clear_value + offset,
+						 plane->fb.clear_value);
+			}
+
+			if (primary) {
+				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
+					     (plane->fb.format << 26) |
+					     (plane->fb.uv_swizzle << 25) |
+					     (plane->fb.swizzle << 23) |
+					     (plane->fb.tile_mode << 17) |
+					     (plane->fb.yuv_color_space << 14) |
+					     (plane->fb.rotation << 11) |
+					     (plane->fb.clear_enable << 8),
+					     (0x1F << 26) |
+					     BIT(25) |
+					     (0x03 << 23) |
+					     (0x1F << 17) |
+					     (0x07 << 14) |
+					     (0x07 << 11) |
+					     BIT(8));
+				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
+					     (plane->fb.dec_enable << 1) |
+					     (plane->fb.enable << 13) |
+					     (plane->fb.zpos << 16) |
+					     (plane->fb.display_id << 19),
+					     BIT(1) | BIT(13) | (0x07 << 16) | BIT(19));
+			} else {
+				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+					     (plane->fb.dec_enable << 27) |
+					     (plane->fb.clear_enable << 25) |
+					     (plane->fb.enable << 24) |
+					     (plane->fb.format << 16) |
+					     (plane->fb.uv_swizzle << 15) |
+					     (plane->fb.swizzle << 13) |
+					     (plane->fb.tile_mode << 8) |
+					     (plane->fb.yuv_color_space << 5) |
+					     (plane->fb.rotation << 2),
+					     BIT(27) |
+					     BIT(25) |
+					     BIT(24) |
+					     (0x1F << 16) |
+					     BIT(15) |
+					     (0x03 << 13) |
+					     (0x1F << 8) |
+					     (0x07 << 5) |
+					     (0x07 << 2));
+				dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset,
+					     plane->fb.zpos | (plane->fb.display_id << 3),
+					     0x07 | BIT(3));
+			}
+			plane->fb.dirty = false;
+		}
+
+		if (plane->scale.dirty) {
+			if (plane->scale.enable) {
+				dc_write(hw, reg->scale_factor_x + offset,
+					 plane->scale.scale_factor_x);
+				dc_write(hw, reg->scale_factor_y + offset,
+					 plane->scale.scale_factor_y);
+				if (primary)
+					dc_set_clear(hw,
+						     DC_FRAMEBUFFER_CONFIG + offset,
+						     BIT(22), 0);
+				else
+					dc_set_clear(hw,
+						     DC_OVERLAY_SCALE_CONFIG + offset,
+						     BIT(8), 0);
+			} else {
+				if (primary)
+					dc_set_clear(hw,
+						     DC_FRAMEBUFFER_CONFIG + offset,
+						     0, BIT(22));
+				else
+					dc_set_clear(hw,
+						     DC_OVERLAY_SCALE_CONFIG + offset,
+						     0, BIT(8));
+			}
+			plane->scale.dirty = false;
+		}
+
+		if (plane->pos.dirty) {
+			dc_write(hw, reg->top_left + offset,
+				 plane->pos.start_x |
+				 (plane->pos.start_y << 15));
+			dc_write(hw, reg->bottom_right + offset,
+				 plane->pos.end_x |
+				 (plane->pos.end_y << 15));
+			plane->pos.dirty = false;
+		}
+
+		if (plane->blend.dirty) {
+			dc_write(hw, reg->src_global_color + offset,
+				 plane->blend.alpha << 24);
+			dc_write(hw, reg->dst_global_color + offset,
+				 plane->blend.alpha << 24);
+			switch (plane->blend.blend_mode) {
+			case BLEND_PREMULTI:
+				dc_write(hw, reg->blend_config + offset, 0x3450);
+				break;
+			case BLEND_COVERAGE:
+				dc_write(hw, reg->blend_config + offset, 0x3950);
+				break;
+			case BLEND_PIXEL_NONE:
+				dc_write(hw, reg->blend_config + offset, 0x3548);
+				break;
+			default:
+				break;
+			}
+			plane->blend.dirty = false;
+		}
+
+		if (plane->colorkey.dirty) {
+			dc_write(hw, reg->color_key + offset, plane->colorkey.colorkey);
+			dc_write(hw, reg->color_key_high + offset,
+				 plane->colorkey.colorkey_high);
+
+			if (primary)
+				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
+					     plane->colorkey.transparency << 9, 0x03 << 9);
+			else
+				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+					     plane->colorkey.transparency, 0x03);
+
+			plane->colorkey.dirty = false;
+		}
+
+		if (plane->roi.dirty) {
+			if (plane->roi.enable) {
+				dc_write(hw, reg->roi_origin + offset,
+					 plane->roi.x | (plane->roi.y << 16));
+				dc_write(hw, reg->roi_size + offset,
+					 plane->roi.width | (plane->roi.height << 16));
+				if (primary)
+					dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
+						     BIT(0), 0);
+				else
+					dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+						     BIT(22), 0);
+			} else {
+				if (primary)
+					dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
+						     0, BIT(0));
+				else
+					dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+						     0, BIT(22));
+			}
+			plane->roi.dirty = false;
+		}
+	}
+}
+
+static void plane_ex_commit(struct dc_hw *hw)
+{
+	struct dc_hw_plane *plane;
+	const struct dc_hw_plane_reg *reg;
+	bool primary = false;
+	u8 id, layer_num = hw->info->layer_num;
+	u32 i, offset;
+
+	for (i = 0; i < layer_num; i++) {
+		plane = &hw->plane[i];
+		id = hw->info->planes[i].id;
+		offset = get_addr_offset(id);
+		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
+			reg = &dc_plane_reg[0];
+			primary = true;
+		} else {
+			reg = &dc_plane_reg[1];
+			primary = false;
+		}
+
+		if (plane->fb.dirty) {
+			if (is_rgb(plane->fb.format)) {
+				if (primary)
+					dc_set_clear(hw,
+						     DC_FRAMEBUFFER_CONFIG_EX + offset,
+						     BIT(6), BIT(8));
+				else
+					dc_set_clear(hw,
+						     DC_OVERLAY_CONFIG + offset,
+						     BIT(29), BIT(30));
+			} else {
+				if (primary)
+					dc_set_clear(hw,
+						     DC_FRAMEBUFFER_CONFIG_EX + offset,
+						     BIT(8), BIT(6));
+				else
+					dc_set_clear(hw,
+						     DC_OVERLAY_CONFIG + offset,
+						     BIT(30), BIT(29));
+				switch (plane->fb.yuv_color_space) {
+				case COLOR_SPACE_601:
+					load_yuv_to_rgb(hw, reg, offset, YUV601_2RGB);
+					break;
+				case COLOR_SPACE_709:
+					load_yuv_to_rgb(hw, reg, offset, YUV709_2RGB);
+					break;
+				case COLOR_SPACE_2020:
+					load_yuv_to_rgb(hw, reg, offset, YUV2020_2RGB);
+					break;
+				default:
+					break;
+				}
+			}
+		}
+		if (plane->degamma.dirty) {
+			switch (plane->degamma.mode) {
+			case VS_DEGAMMA_DISABLE:
+				if (primary)
+					dc_set_clear(hw,
+						     DC_FRAMEBUFFER_CONFIG_EX + offset,
+						     0, BIT(5));
+				else
+					dc_set_clear(hw,
+						     DC_OVERLAY_CONFIG + offset,
+						     0, BIT(28));
+				break;
+			case VS_DEGAMMA_BT709:
+				load_degamma_table(hw, reg, offset, DEGAMMA_709);
+				if (primary)
+					dc_set_clear(hw,
+						     DC_FRAMEBUFFER_CONFIG_EX + offset,
+						     BIT(5), 0);
+				else
+					dc_set_clear(hw,
+						     DC_OVERLAY_CONFIG + offset,
+						     BIT(28), 0);
+				break;
+			case VS_DEGAMMA_BT2020:
+				load_degamma_table(hw, reg, offset, DEGAMMA_2020);
+				if (primary)
+					dc_set_clear(hw,
+						     DC_FRAMEBUFFER_CONFIG_EX + offset,
+						     BIT(5), 0);
+				else
+					dc_set_clear(hw,
+						     DC_OVERLAY_CONFIG + offset,
+						     BIT(28), 0);
+				break;
+			default:
+				break;
+			}
+			plane->degamma.dirty = false;
+		}
+	}
+	plane_commit(hw);
+}
+
+static void setup_display(struct dc_hw *hw, struct dc_hw_display *display)
+{
+	u8 id = display->id;
+	u32 dpi_cfg, offset = id << 2;
+
+	if (hw->display[id].enable) {
+		switch (display->bus_format) {
+		case MEDIA_BUS_FMT_RGB565_1X16:
+			dpi_cfg = 0;
+			break;
+		case MEDIA_BUS_FMT_RGB666_1X18:
+			dpi_cfg = 3;
+			break;
+		case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+			dpi_cfg = 4;
+			break;
+		case MEDIA_BUS_FMT_RGB888_1X24:
+			dpi_cfg = 5;
+			break;
+		case MEDIA_BUS_FMT_RGB101010_1X30:
+			dpi_cfg = 6;
+			break;
+		default:
+			dpi_cfg = 5;
+			break;
+		}
+		dc_write(hw, DC_DISPLAY_DPI_CONFIG + offset, dpi_cfg);
+
+		if (id == 0)
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
+		else
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
+
+		dc_write(hw, DC_DISPLAY_H + offset, hw->display[id].h_active |
+			 (hw->display[id].h_total << 16));
+		dc_write(hw, DC_DISPLAY_H_SYNC + offset,
+			 hw->display[id].h_sync_start |
+			 (hw->display[id].h_sync_end << 15) |
+			 (hw->display[id].h_sync_polarity ? 0 : BIT(31)) |
+			 BIT(30));
+		dc_write(hw, DC_DISPLAY_V + offset, hw->display[id].v_active |
+				(hw->display[id].v_total << 16));
+		dc_write(hw, DC_DISPLAY_V_SYNC + offset,
+			 hw->display[id].v_sync_start |
+			 (hw->display[id].v_sync_end << 15) |
+			 (hw->display[id].v_sync_polarity ? 0 : BIT(31)) |
+			 BIT(30));
+
+		if (hw->info->pipe_sync)
+			dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
+				     0, BIT(3) | BIT(4));
+
+		dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(12), 0);
+		if (id == 0)
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(0), BIT(3));
+		else
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(1), BIT(3));
+	} else {
+		dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(12));
+		if (id == 0)
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
+		else
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
+	}
+}
+
+static void setup_display_ex(struct dc_hw *hw, struct dc_hw_display *display)
+{
+	u8 id = display->id;
+	u32 dp_cfg, offset = id << 2;
+	bool is_yuv = false;
+
+	if (hw->display[id].enable && hw->out[id] == OUT_DP) {
+		switch (display->bus_format) {
+		case MEDIA_BUS_FMT_RGB565_1X16:
+			dp_cfg = 0;
+			break;
+		case MEDIA_BUS_FMT_RGB666_1X18:
+			dp_cfg = 1;
+			break;
+		case MEDIA_BUS_FMT_RGB888_1X24:
+			dp_cfg = 2;
+			break;
+		case MEDIA_BUS_FMT_RGB101010_1X30:
+			dp_cfg = 3;
+			break;
+		case MEDIA_BUS_FMT_UYVY8_1X16:
+			dp_cfg = 2 << 4;
+			is_yuv = true;
+			break;
+		case MEDIA_BUS_FMT_YUV8_1X24:
+			dp_cfg = 4 << 4;
+			is_yuv = true;
+			break;
+		case MEDIA_BUS_FMT_UYVY10_1X20:
+			dp_cfg = 8 << 4;
+			is_yuv = true;
+			break;
+		case MEDIA_BUS_FMT_YUV10_1X30:
+			dp_cfg = 10 << 4;
+			is_yuv = true;
+			break;
+		case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+			dp_cfg = 12 << 4;
+			is_yuv = true;
+			break;
+		case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+			dp_cfg = 13 << 4;
+			is_yuv = true;
+			break;
+		default:
+			dp_cfg = 2;
+			break;
+		}
+		if (is_yuv)
+			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(16), 0);
+		else
+			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(16));
+		dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | BIT(3));
+	}
+
+	if (hw->out[id] == OUT_DPI)
+		dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, BIT(3));
+
+	setup_display(hw, display);
+}
+
+static const struct dc_hw_funcs hw_func = {
+	.gamma = &gamma_ex_commit,
+	.plane = &plane_ex_commit,
+	.display = setup_display_ex,
+};
+
+void dc_hw_commit(struct dc_hw *hw)
+{
+	u32 i, offset = 0;
+	u8 plane_num = hw->info->plane_num;
+	u8 layer_num = hw->info->layer_num;
+	u8 cursor_num = plane_num - layer_num;
+
+	hw->func->gamma(hw);
+	hw->func->plane(hw);
+
+	for (i = 0; i < cursor_num; i++) {
+		if (hw->cursor[i].dirty) {
+			offset = hw->cursor[i].display_id ? DC_CURSOR_OFFSET : 0;
+			if (hw->cursor[i].enable) {
+				dc_write(hw, DC_CURSOR_ADDRESS + offset,
+					 hw->cursor[i].address);
+				dc_write(hw, DC_CURSOR_LOCATION + offset, hw->cursor[i].x |
+					 (hw->cursor[i].y << 16));
+				dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
+					     (hw->cursor[i].hot_x << 16) |
+					     (hw->cursor[i].hot_y << 8) |
+					     (hw->cursor[i].size << 5) |
+					     BIT(3) | BIT(2) | 0x02,
+					     (0xFF << 16) |
+					     (0xFF << 8) |
+					     (0x07 << 5) | 0x1F);
+			} else {
+				dc_set_clear(hw, DC_CURSOR_CONFIG + offset, BIT(3), 0x03);
+			}
+			hw->cursor[i].dirty = false;
+		}
+	}
+
+	if (hw->qos.dirty) {
+		dc_set_clear(hw, DC_QOS_CONFIG, (hw->qos.high_value << 4) |
+					 hw->qos.low_value, 0xFF);
+		hw->qos.dirty = false;
+	}
+}
+
diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.h b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
new file mode 100644
index 000000000..9a1d767ae
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
@@ -0,0 +1,492 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_DC_HW_H__
+#define __VS_DC_HW_H__
+
+#define AQ_INTR_ACKNOWLEDGE				0x0010
+#define AQ_INTR_ENBL					0x0014
+#define DC_HW_REVISION					0x0024
+#define DC_HW_CHIP_CID					0x0030
+
+#define DC_REG_BASE						0x0800
+#define DC_REG_RANGE					0x2000
+#define DC_SEC_REG_OFFSET				0x100000
+
+#define DC_FRAMEBUFFER_CONFIG			0x1518
+#define DC_FRAMEBUFFER_CONFIG_EX		0x1CC0
+#define DC_FRAMEBUFFER_SCALE_CONFIG		0x1520
+#define DC_FRAMEBUFFER_TOP_LEFT			0x24D8
+#define DC_FRAMEBUFFER_BOTTOM_RIGHT		0x24E0
+#define DC_FRAMEBUFFER_ADDRESS			0x1400
+#define DC_FRAMEBUFFER_U_ADDRESS		0x1530
+#define DC_FRAMEBUFFER_V_ADDRESS		0x1538
+#define DC_FRAMEBUFFER_STRIDE			0x1408
+#define DC_FRAMEBUFFER_U_STRIDE			0x1800
+#define DC_FRAMEBUFFER_V_STRIDE			0x1808
+#define DC_FRAMEBUFFER_SIZE				0x1810
+#define DC_FRAMEBUFFER_SCALE_FACTOR_X		0x1828
+#define DC_FRAMEBUFFER_SCALE_FACTOR_Y		0x1830
+#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX	0x1838
+#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA	0x1A00
+#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX	0x1A08
+#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA	0x1A10
+#define DC_FRAMEBUFFER_INIT_OFFSET			0x1A20
+#define DC_FRAMEBUFFER_COLOR_KEY			0x1508
+#define DC_FRAMEBUFFER_COLOR_KEY_HIGH		0x1510
+#define DC_FRAMEBUFFER_CLEAR_VALUE			0x1A18
+#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX	0x1818
+#define DC_FRAMEBUFFER_COLOR_TABLE_DATA		0x1820
+#define DC_FRAMEBUFFER_BG_COLOR				0x1528
+#define DC_FRAMEBUFFER_ROI_ORIGIN			0x1CB0
+#define DC_FRAMEBUFFER_ROI_SIZE				0x1CB8
+#define DC_FRAMEBUFFER_WATER_MARK			0x1CE8
+#define DC_FRAMEBUFFER_DEGAMMA_INDEX		0x1D88
+#define DC_FRAMEBUFFER_DEGAMMA_DATA			0x1D90
+#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA		0x1D98
+#define DC_FRAMEBUFFER_YUVTORGB_COEF0		0x1DA0
+#define DC_FRAMEBUFFER_YUVTORGB_COEF1		0x1DA8
+#define DC_FRAMEBUFFER_YUVTORGB_COEF2		0x1DB0
+#define DC_FRAMEBUFFER_YUVTORGB_COEF3		0x1DB8
+#define DC_FRAMEBUFFER_YUVTORGB_COEF4		0x1E00
+#define DC_FRAMEBUFFER_YUVTORGB_COEFD0		0x1E08
+#define DC_FRAMEBUFFER_YUVTORGB_COEFD1		0x1E10
+#define DC_FRAMEBUFFER_YUVTORGB_COEFD2		0x1E18
+#define DC_FRAMEBUFFER_Y_CLAMP_BOUND		0x1E88
+#define DC_FRAMEBUFFER_UV_CLAMP_BOUND		0x1E90
+#define DC_FRAMEBUFFER_RGBTORGB_COEF0		0x1E20
+#define DC_FRAMEBUFFER_RGBTORGB_COEF1		0x1E28
+#define DC_FRAMEBUFFER_RGBTORGB_COEF2		0x1E30
+#define DC_FRAMEBUFFER_RGBTORGB_COEF3		0x1E38
+#define DC_FRAMEBUFFER_RGBTORGB_COEF4		0x1E40
+#define DC_FRAMEBUFFER_BLEND_CONFIG			0x2510
+#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR		0x2500
+#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR		0x2508
+
+#define DC_OVERLAY_CONFIG					0x1540
+#define DC_OVERLAY_CONFIG_EX				0x2540
+#define DC_OVERLAY_SCALE_CONFIG				0x1C00
+#define DC_OVERLAY_BLEND_CONFIG				0x1580
+#define DC_OVERLAY_TOP_LEFT					0x1640
+#define DC_OVERLAY_BOTTOM_RIGHT				0x1680
+#define DC_OVERLAY_ADDRESS					0x15C0
+#define DC_OVERLAY_U_ADDRESS				0x1840
+#define DC_OVERLAY_V_ADDRESS				0x1880
+#define DC_OVERLAY_STRIDE					0x1600
+#define DC_OVERLAY_U_STRIDE					0x18C0
+#define DC_OVERLAY_V_STRIDE					0x1900
+#define DC_OVERLAY_SIZE						0x17C0
+#define DC_OVERLAY_SCALE_FACTOR_X			0x1A40
+#define DC_OVERLAY_SCALE_FACTOR_Y			0x1A80
+#define DC_OVERLAY_H_FILTER_COEF_INDEX		0x1AC0
+#define DC_OVERLAY_H_FILTER_COEF_DATA		0x1B00
+#define DC_OVERLAY_V_FILTER_COEF_INDEX		0x1B40
+#define DC_OVERLAY_V_FILTER_COEF_DATA		0x1B80
+#define DC_OVERLAY_INIT_OFFSET				0x1BC0
+#define DC_OVERLAY_COLOR_KEY				0x1740
+#define DC_OVERLAY_COLOR_KEY_HIGH			0x1780
+#define DC_OVERLAY_CLEAR_VALUE				0x1940
+#define DC_OVERLAY_COLOR_TABLE_INDEX		0x1980
+#define DC_OVERLAY_COLOR_TABLE_DATA			0x19C0
+#define DC_OVERLAY_SRC_GLOBAL_COLOR			0x16C0
+#define DC_OVERLAY_DST_GLOBAL_COLOR			0x1700
+#define DC_OVERLAY_ROI_ORIGIN				0x1D00
+#define DC_OVERLAY_ROI_SIZE					0x1D40
+#define DC_OVERLAY_WATER_MARK				0x1DC0
+#define DC_OVERLAY_DEGAMMA_INDEX			0x2200
+#define DC_OVERLAY_DEGAMMA_DATA				0x2240
+#define DC_OVERLAY_DEGAMMA_EX_DATA			0x2280
+#define DC_OVERLAY_YUVTORGB_COEF0			0x1EC0
+#define DC_OVERLAY_YUVTORGB_COEF1			0x1F00
+#define DC_OVERLAY_YUVTORGB_COEF2			0x1F40
+#define DC_OVERLAY_YUVTORGB_COEF3			0x1F80
+#define DC_OVERLAY_YUVTORGB_COEF4			0x1FC0
+#define DC_OVERLAY_YUVTORGB_COEFD0			0x2000
+#define DC_OVERLAY_YUVTORGB_COEFD1			0x2040
+#define DC_OVERLAY_YUVTORGB_COEFD2			0x2080
+#define DC_OVERLAY_Y_CLAMP_BOUND			0x22C0
+#define DC_OVERLAY_UV_CLAMP_BOUND			0x2300
+#define DC_OVERLAY_RGBTORGB_COEF0			0x20C0
+#define DC_OVERLAY_RGBTORGB_COEF1			0x2100
+#define DC_OVERLAY_RGBTORGB_COEF2			0x2140
+#define DC_OVERLAY_RGBTORGB_COEF3			0x2180
+#define DC_OVERLAY_RGBTORGB_COEF4			0x21C0
+
+#define DC_CURSOR_CONFIG					0x1468
+#define DC_CURSOR_ADDRESS					0x146C
+#define DC_CURSOR_LOCATION					0x1470
+#define DC_CURSOR_BACKGROUND				0x1474
+#define DC_CURSOR_FOREGROUND				0x1478
+#define DC_CURSOR_CLK_GATING				0x1484
+#define DC_CURSOR_CONFIG_EX					0x24E8
+#define DC_CURSOR_OFFSET					0x1080
+
+#define DC_DISPLAY_DITHER_CONFIG		0x1410
+#define DC_DISPLAY_PANEL_CONFIG			0x1418
+#define DC_DISPLAY_PANEL_CONFIG_EX		0x2518
+#define DC_DISPLAY_DITHER_TABLE_LOW		0x1420
+#define DC_DISPLAY_DITHER_TABLE_HIGH	0x1428
+#define DC_DISPLAY_H					0x1430
+#define DC_DISPLAY_H_SYNC				0x1438
+#define DC_DISPLAY_V					0x1440
+#define DC_DISPLAY_V_SYNC				0x1448
+#define DC_DISPLAY_CURRENT_LOCATION		0x1450
+#define DC_DISPLAY_GAMMA_INDEX			0x1458
+#define DC_DISPLAY_GAMMA_DATA			0x1460
+#define DC_DISPLAY_INT					0x147C
+#define DC_DISPLAY_INT_ENABLE			0x1480
+#define DC_DISPLAY_DBI_CONFIG			0x1488
+#define DC_DISPLAY_GENERAL_CONFIG		0x14B0
+#define DC_DISPLAY_DPI_CONFIG			0x14B8
+#define DC_DISPLAY_PANEL_START			0x1CCC
+#define DC_DISPLAY_DEBUG_COUNTER_SELECT		0x14D0
+#define DC_DISPLAY_DEBUG_COUNTER_VALUE		0x14D8
+#define DC_DISPLAY_DP_CONFIG				0x1CD0
+#define DC_DISPLAY_GAMMA_EX_INDEX			0x1CF0
+#define DC_DISPLAY_GAMMA_EX_DATA			0x1CF8
+#define DC_DISPLAY_GAMMA_EX_ONE_DATA		0x1D80
+#define DC_DISPLAY_RGBTOYUV_COEF0			0x1E48
+#define DC_DISPLAY_RGBTOYUV_COEF1			0x1E50
+#define DC_DISPLAY_RGBTOYUV_COEF2			0x1E58
+#define DC_DISPLAY_RGBTOYUV_COEF3			0x1E60
+#define DC_DISPLAY_RGBTOYUV_COEF4			0x1E68
+#define DC_DISPLAY_RGBTOYUV_COEFD0			0x1E70
+#define DC_DISPLAY_RGBTOYUV_COEFD1			0x1E78
+#define DC_DISPLAY_RGBTOYUV_COEFD2			0x1E80
+
+#define DC_CLK_GATTING					0x1A28
+#define DC_QOS_CONFIG					0x1A38
+
+#define DC_TRANSPARENCY_OPAQUE			0x00
+#define DC_TRANSPARENCY_KEY				0x02
+#define DC_DISPLAY_DITHERTABLE_LOW		0x7B48F3C0
+#define DC_DISPLAY_DITHERTABLE_HIGH		0x596AD1E2
+
+#define GAMMA_SIZE						256
+#define GAMMA_EX_SIZE					300
+#define DEGAMMA_SIZE					260
+
+#define RGB_TO_RGB_TABLE_SIZE			9
+#define YUV_TO_RGB_TABLE_SIZE			16
+#define RGB_TO_YUV_TABLE_SIZE			12
+
+#define DC_LAYER_NUM	6
+#define DC_DISPLAY_NUM	2
+#define DC_CURSOR_NUM	2
+
+#define DC_TILE_MODE4X4	0x15
+
+enum dc_chip_rev {
+	DC_REV_0,/* For HW_REV_5720,HW_REV_5721_311 */
+	DC_REV_1,/* For HW_REV_5721_30B */
+	DC_REV_2,/* For HW_REV_5721_310 */
+};
+
+enum dc_hw_plane_id {
+	PRIMARY_PLANE_0,
+	OVERLAY_PLANE_0,
+	OVERLAY_PLANE_1,
+	PRIMARY_PLANE_1,
+	OVERLAY_PLANE_2,
+	OVERLAY_PLANE_3,
+	CURSOR_PLANE_0,
+	CURSOR_PLANE_1,
+	PLANE_NUM
+};
+
+enum dc_hw_color_format {
+	FORMAT_X4R4G4B4,//0
+	FORMAT_A4R4G4B4,//1
+	FORMAT_X1R5G5B5,//2
+	FORMAT_A1R5G5B5,//3
+	FORMAT_R5G6B5,//4
+	FORMAT_X8R8G8B8,//5
+	FORMAT_A8R8G8B8,//6
+	FORMAT_YUY2,//7
+	FORMAT_UYVY,//8
+	FORMAT_INDEX8,//9
+	FORMAT_MONOCHROME,//10
+	FORMAT_YV12 = 0xf,
+	FORMAT_A8,//16
+	FORMAT_NV12,//17
+	FORMAT_NV16,//18
+	FORMAT_RG16,//19
+	FORMAT_R8,//20
+	FORMAT_NV12_10BIT,//21
+	FORMAT_A2R10G10B10,//22
+	FORMAT_NV16_10BIT,//23
+	FORMAT_INDEX1,//24
+	FORMAT_INDEX2,//25
+	FORMAT_INDEX4,//26
+	FORMAT_P010,//27
+	FORMAT_YUV444,//28
+	FORMAT_YUV444_10BIT,//29
+};
+
+enum dc_hw_yuv_color_space {
+	COLOR_SPACE_601 = 0,
+	COLOR_SPACE_709 = 1,
+	COLOR_SPACE_2020 = 3,
+};
+
+enum dc_hw_rotation {
+	ROT_0 = 0,
+	ROT_90 = 4,
+	ROT_180 = 5,
+	ROT_270 = 6,
+	FLIP_X = 1,
+	FLIP_Y = 2,
+	FLIP_XY = 3,
+};
+
+enum dc_hw_swizzle {
+	SWIZZLE_ARGB = 0,
+	SWIZZLE_RGBA,
+	SWIZZLE_ABGR,
+	SWIZZLE_BGRA,
+};
+
+enum dc_hw_out {
+	OUT_DPI,
+	OUT_DP,
+};
+
+enum dc_hw_cursor_size {
+	CURSOR_SIZE_32X32 = 0,
+	CURSOR_SIZE_64X64,
+};
+
+enum dc_hw_blend_mode {
+	/* out.rgb = plane_alpha * fg.rgb +
+	 *		(1 - (plane_alpha * fg.alpha)) * bg.rgb
+	 */
+	BLEND_PREMULTI,
+	/* out.rgb = plane_alpha * fg.alpha * fg.rgb +
+	 *		(1 - (plane_alpha * fg.alpha)) * bg.rgb
+	 */
+	BLEND_COVERAGE,
+	/* out.rgb = plane_alpha * fg.rgb +
+	 *		(1 - plane_alpha) * bg.rgb
+	 */
+	BLEND_PIXEL_NONE,
+};
+
+struct dc_hw_plane_reg {
+	u32 y_address;
+	u32 u_address;
+	u32 v_address;
+	u32 y_stride;
+	u32 u_stride;
+	u32 v_stride;
+	u32 size;
+	u32 top_left;
+	u32 bottom_right;
+	u32 scale_factor_x;
+	u32 scale_factor_y;
+	u32 h_filter_coef_index;
+	u32 h_filter_coef_data;
+	u32 v_filter_coef_index;
+	u32 v_filter_coef_data;
+	u32 init_offset;
+	u32 color_key;
+	u32 color_key_high;
+	u32 clear_value;
+	u32 color_table_index;
+	u32 color_table_data;
+	u32 scale_config;
+	u32 water_mark;
+	u32 degamma_index;
+	u32 degamma_data;
+	u32 degamma_ex_data;
+	u32 src_global_color;
+	u32 dst_global_color;
+	u32 blend_config;
+	u32 roi_origin;
+	u32 roi_size;
+	u32 yuv_to_rgb_coef0;
+	u32 yuv_to_rgb_coef1;
+	u32 yuv_to_rgb_coef2;
+	u32 yuv_to_rgb_coef3;
+	u32 yuv_to_rgb_coef4;
+	u32 yuv_to_rgb_coefd0;
+	u32 yuv_to_rgb_coefd1;
+	u32 yuv_to_rgb_coefd2;
+	u32 y_clamp_bound;
+	u32 uv_clamp_bound;
+	u32 rgb_to_rgb_coef0;
+	u32 rgb_to_rgb_coef1;
+	u32 rgb_to_rgb_coef2;
+	u32 rgb_to_rgb_coef3;
+	u32 rgb_to_rgb_coef4;
+};
+
+struct dc_hw_fb {
+	u32 y_address;
+	u32 u_address;
+	u32 v_address;
+	u32 clear_value;
+	u32 water_mark;
+	u16 y_stride;
+	u16 u_stride;
+	u16 v_stride;
+	u16 width;
+	u16 height;
+	u8	format;
+	u8	tile_mode;
+	u8	rotation;
+	u8	yuv_color_space;
+	u8	swizzle;
+	u8	uv_swizzle;
+	u8	zpos;
+	u8	display_id;
+	bool	clear_enable;
+	bool	dec_enable;
+	bool	enable;
+	bool	dirty;
+};
+
+struct dc_hw_scale {
+	u32 scale_factor_x;
+	u32 scale_factor_y;
+	bool	enable;
+	bool	dirty;
+};
+
+struct dc_hw_position {
+	u16 start_x;
+	u16 start_y;
+	u16 end_x;
+	u16 end_y;
+	bool	dirty;
+};
+
+struct dc_hw_blend {
+	u8	alpha;
+	u8	blend_mode;
+	bool	dirty;
+};
+
+struct dc_hw_colorkey {
+	u32 colorkey;
+	u32 colorkey_high;
+	u8	transparency;
+	bool dirty;
+};
+
+struct dc_hw_roi {
+	u16 x;
+	u16 y;
+	u16 width;
+	u16 height;
+	bool enable;
+	bool dirty;
+};
+
+struct dc_hw_cursor {
+	u32 address;
+	u16 x;
+	u16 y;
+	u16 hot_x;
+	u16 hot_y;
+	u8	size;
+	u8	display_id;
+	bool	enable;
+	bool	dirty;
+};
+
+struct dc_hw_display {
+	u32 bus_format;
+	u16 h_active;
+	u16 h_total;
+	u16 h_sync_start;
+	u16 h_sync_end;
+	u16 v_active;
+	u16 v_total;
+	u16 v_sync_start;
+	u16 v_sync_end;
+	u8	id;
+	bool	h_sync_polarity;
+	bool	v_sync_polarity;
+	bool	enable;
+};
+
+struct dc_hw_gamma {
+	u16 gamma[GAMMA_EX_SIZE][3];
+	bool	enable;
+	bool	dirty;
+};
+
+struct dc_hw_degamma {
+	u16 degamma[DEGAMMA_SIZE][3];
+	u32 mode;
+	bool	dirty;
+};
+
+struct dc_hw_plane {
+	struct dc_hw_fb			fb;
+	struct dc_hw_position	pos;
+	struct dc_hw_scale		scale;
+	struct dc_hw_blend		blend;
+	struct dc_hw_roi		roi;
+	struct dc_hw_colorkey	colorkey;
+	struct dc_hw_degamma	degamma;
+};
+
+struct dc_hw_qos {
+	u8	  low_value;
+	u8	  high_value;
+	bool  dirty;
+};
+
+struct dc_hw_read {
+	u32			reg;
+	u32			value;
+};
+
+struct dc_hw;
+struct dc_hw_funcs {
+	void (*gamma)(struct dc_hw *hw);
+	void (*plane)(struct dc_hw *hw);
+	void (*display)(struct dc_hw *hw, struct dc_hw_display *display);
+};
+
+struct dc_hw {
+	enum dc_chip_rev rev;
+	enum dc_hw_out		out[DC_DISPLAY_NUM];
+	void			*hi_base;
+	void			*reg_base;
+
+	struct dc_hw_display display[DC_DISPLAY_NUM];
+	struct dc_hw_gamma	 gamma[DC_DISPLAY_NUM];
+	struct dc_hw_plane	 plane[DC_LAYER_NUM];
+	struct dc_hw_cursor  cursor[DC_CURSOR_NUM];
+	struct dc_hw_qos	 qos;
+	struct dc_hw_funcs	 *func;
+	struct vs_dc_info	 *info;
+};
+
+int dc_hw_init(struct dc_hw *hw);
+void dc_hw_deinit(struct dc_hw *hw);
+void dc_hw_update_plane(struct dc_hw *hw, u8 id,
+			struct dc_hw_fb *fb, struct dc_hw_scale *scale,
+			struct dc_hw_position *pos, struct dc_hw_blend *blend);
+void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode);
+void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi);
+void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
+			   struct dc_hw_colorkey *colorkey);
+void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos);
+void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor);
+void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
+			u16 r, u16 g, u16 b);
+void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable);
+void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display);
+void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable);
+u32 dc_hw_get_interrupt(struct dc_hw *hw);
+bool dc_hw_check_underflow(struct dc_hw *hw);
+void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable);
+void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id);
+void dc_hw_commit(struct dc_hw *hw);
+
+#endif /* __VS_DC_HW_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
index da7698c3d..3cd533cfa 100644
--- a/drivers/gpu/drm/verisilicon/vs_drv.c
+++ b/drivers/gpu/drm/verisilicon/vs_drv.c
@@ -21,6 +21,7 @@
 
 #include "vs_drv.h"
 #include "vs_modeset.h"
+#include "vs_dc.h"
 
 #define DRV_NAME	"verisilicon"
 #define DRV_DESC	"Verisilicon DRM driver"
@@ -123,6 +124,7 @@ static const struct component_master_ops vs_drm_ops = {
 };
 
 static struct platform_driver *drm_sub_drivers[] = {
+	&dc_platform_driver,
 
 };
 
diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c
new file mode 100644
index 000000000..9bd066015
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_plane.c
@@ -0,0 +1,526 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_fb_dma_helper.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_plane_helper.h>
+
+#include <drm/vs_drm.h>
+
+#include "vs_plane.h"
+#include "vs_drv.h"
+#include "vs_dc.h"
+
+static void vs_plane_reset(struct drm_plane *plane)
+{
+	struct vs_plane_state *state;
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+
+	if (plane->state) {
+		__drm_atomic_helper_plane_destroy_state(plane->state);
+
+		state = to_vs_plane_state(plane->state);
+		kfree(state);
+		plane->state = NULL;
+	}
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return;
+
+	state->degamma = VS_DEGAMMA_DISABLE;
+	state->degamma_changed = false;
+	state->base.zpos = vs_plane->id;
+	memset(&state->status, 0, sizeof(state->status));
+
+	__drm_atomic_helper_plane_reset(plane, &state->base);
+}
+
+static void _vs_plane_duplicate_blob(struct vs_plane_state *state,
+				     struct vs_plane_state *ori_state)
+{
+	state->watermark = ori_state->watermark;
+	state->color_mgmt = ori_state->color_mgmt;
+	state->roi = ori_state->roi;
+
+	if (state->watermark)
+		drm_property_blob_get(state->watermark);
+	if (state->color_mgmt)
+		drm_property_blob_get(state->color_mgmt);
+	if (state->roi)
+		drm_property_blob_get(state->roi);
+}
+
+static int
+_vs_plane_set_property_blob_from_id(struct drm_device *dev,
+				    struct drm_property_blob **blob,
+				    u64 blob_id,
+				    size_t expected_size)
+{
+	struct drm_property_blob *new_blob = NULL;
+
+	if (blob_id) {
+		new_blob = drm_property_lookup_blob(dev, blob_id);
+		if (!new_blob)
+			return -EINVAL;
+
+		if (new_blob->length != expected_size) {
+			drm_property_blob_put(new_blob);
+			return -EINVAL;
+		}
+	}
+
+	drm_property_replace_blob(blob, new_blob);
+	drm_property_blob_put(new_blob);
+
+	return 0;
+}
+
+static struct drm_plane_state *
+vs_plane_atomic_duplicate_state(struct drm_plane *plane)
+{
+	struct vs_plane_state *ori_state;
+	struct vs_plane_state *state;
+
+	if (WARN_ON(!plane->state))
+		return NULL;
+
+	ori_state = to_vs_plane_state(plane->state);
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return NULL;
+
+	__drm_atomic_helper_plane_duplicate_state(plane, &state->base);
+
+	state->degamma = ori_state->degamma;
+	state->degamma_changed = ori_state->degamma_changed;
+
+	_vs_plane_duplicate_blob(state, ori_state);
+	memcpy(&state->status, &ori_state->status, sizeof(ori_state->status));
+
+	return &state->base;
+}
+
+static void vs_plane_atomic_destroy_state(struct drm_plane *plane,
+					  struct drm_plane_state *state)
+{
+	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
+
+	__drm_atomic_helper_plane_destroy_state(state);
+
+	drm_property_blob_put(vs_plane_state->watermark);
+	drm_property_blob_put(vs_plane_state->color_mgmt);
+	drm_property_blob_put(vs_plane_state->roi);
+	kfree(vs_plane_state);
+}
+
+static int vs_plane_atomic_set_property(struct drm_plane *plane,
+					struct drm_plane_state *state,
+					struct drm_property *property,
+					uint64_t val)
+{
+	struct drm_device *dev = plane->dev;
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
+	int ret = 0;
+
+	if (property == vs_plane->degamma_mode) {
+		if (vs_plane_state->degamma != val) {
+			vs_plane_state->degamma = val;
+			vs_plane_state->degamma_changed = true;
+		} else {
+			vs_plane_state->degamma_changed = false;
+		}
+	} else if (property == vs_plane->watermark_prop) {
+		ret = _vs_plane_set_property_blob_from_id(dev,
+							  &vs_plane_state->watermark,
+							  val,
+							  sizeof(struct drm_vs_watermark));
+		return ret;
+	} else if (property == vs_plane->color_mgmt_prop) {
+		ret = _vs_plane_set_property_blob_from_id(dev,
+							  &vs_plane_state->color_mgmt,
+							  val,
+							  sizeof(struct drm_vs_color_mgmt));
+		return ret;
+	} else if (property == vs_plane->roi_prop) {
+		ret = _vs_plane_set_property_blob_from_id(dev,
+							  &vs_plane_state->roi,
+							  val,
+							  sizeof(struct drm_vs_roi));
+		return ret;
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int vs_plane_atomic_get_property(struct drm_plane *plane,
+					const struct drm_plane_state *state,
+					struct drm_property *property,
+					uint64_t *val)
+{
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+	const struct vs_plane_state *vs_plane_state =
+		container_of(state, const struct vs_plane_state, base);
+
+	if (property == vs_plane->degamma_mode)
+		*val = vs_plane_state->degamma;
+	else if (property == vs_plane->watermark_prop)
+		*val = (vs_plane_state->watermark) ?
+					vs_plane_state->watermark->base.id : 0;
+	else if (property == vs_plane->color_mgmt_prop)
+		*val = (vs_plane_state->color_mgmt) ?
+					vs_plane_state->color_mgmt->base.id : 0;
+	else if (property == vs_plane->roi_prop)
+		*val = (vs_plane_state->roi) ?
+					vs_plane_state->roi->base.id : 0;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static bool vs_format_mod_supported(struct drm_plane *plane,
+				    u32 format,
+				    u64 modifier)
+{
+	int i;
+
+	/* We always have to allow these modifiers:
+	 * 1. Core DRM checks for LINEAR support if userspace does not provide modifiers.
+	 * 2. Not passing any modifiers is the same as explicitly passing INVALID.
+	 */
+	if (modifier == DRM_FORMAT_MOD_LINEAR)
+		return true;
+
+	/* Check that the modifier is on the list of the plane's supported modifiers. */
+	for (i = 0; i < plane->modifier_count; i++) {
+		if (modifier == plane->modifiers[i])
+			break;
+	}
+
+	if (i == plane->modifier_count)
+		return false;
+
+	return true;
+}
+
+const struct drm_plane_funcs vs_plane_funcs = {
+	.update_plane		= drm_atomic_helper_update_plane,
+	.disable_plane		= drm_atomic_helper_disable_plane,
+	.reset			= vs_plane_reset,
+	.atomic_duplicate_state = vs_plane_atomic_duplicate_state,
+	.atomic_destroy_state	= vs_plane_atomic_destroy_state,
+	.atomic_set_property	= vs_plane_atomic_set_property,
+	.atomic_get_property	= vs_plane_atomic_get_property,
+	.format_mod_supported	= vs_format_mod_supported,
+};
+
+static unsigned char vs_get_plane_number(struct drm_framebuffer *fb)
+{
+	const struct drm_format_info *info;
+
+	if (!fb)
+		return 0;
+
+	info = drm_format_info(fb->format->format);
+	if (!info || info->num_planes > DRM_FORMAT_MAX_PLANES)
+		return 0;
+
+	return info->num_planes;
+}
+
+static int vs_plane_atomic_check(struct drm_plane *plane,
+				 struct drm_atomic_state *state)
+{
+	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+										 plane);
+	unsigned char i, num_planes;
+	struct drm_framebuffer *fb = new_plane_state->fb;
+	struct drm_crtc *crtc = new_plane_state->crtc;
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+	struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
+
+	if (!crtc || !fb)
+		return 0;
+
+	num_planes = vs_get_plane_number(fb);
+
+	for (i = 0; i < num_planes; i++) {
+		dma_addr_t dma_addr;
+
+		dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
+		plane_state->dma_addr[i] = dma_addr;
+	}
+
+	return vs_dc_check_plane(dc, plane, state);
+}
+
+static int vs_cursor_plane_atomic_check(struct drm_plane *plane,
+					struct drm_atomic_state *state)
+{
+	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+									  plane);
+	unsigned char i, num_planes;
+	struct drm_framebuffer *fb = new_plane_state->fb;
+	struct drm_crtc *crtc = new_plane_state->crtc;
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+	struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
+
+	if (!crtc || !fb)
+		return 0;
+
+	num_planes = vs_get_plane_number(fb);
+
+	for (i = 0; i < num_planes; i++) {
+		dma_addr_t dma_addr;
+
+		dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
+		plane_state->dma_addr[i] = dma_addr;
+	}
+
+	return vs_dc_check_cursor_plane(dc, plane, state);
+}
+
+static void vs_plane_atomic_update(struct drm_plane *plane,
+				   struct drm_atomic_state *state)
+{
+	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
+									  plane);
+	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
+									  plane);
+
+	unsigned char i, num_planes;
+	struct drm_framebuffer *fb;
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+	struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
+	struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	if (!new_state->fb || !new_state->crtc)
+		return;
+
+	fb = new_state->fb;
+
+	drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
+
+	num_planes = vs_get_plane_number(fb);
+
+	for (i = 0; i < num_planes; i++) {
+		dma_addr_t dma_addr;
+
+		dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
+		plane_state->dma_addr[i] = dma_addr;
+	}
+
+	plane_state->status.src = drm_plane_state_src(new_state);
+	plane_state->status.dest = drm_plane_state_dest(new_state);
+
+	vs_dc_update_plane(dc, vs_plane, plane, state);
+}
+
+static void vs_cursor_plane_atomic_update(struct drm_plane *plane,
+					  struct drm_atomic_state *state)
+{
+	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
+									   plane);
+	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
+									   plane);
+	unsigned char i, num_planes;
+	struct drm_framebuffer *fb;
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+	struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
+	struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	if (!new_state->fb || !new_state->crtc)
+		return;
+
+	fb = new_state->fb;
+	drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
+
+	num_planes = vs_get_plane_number(fb);
+
+	for (i = 0; i < num_planes; i++) {
+		dma_addr_t dma_addr;
+
+		dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
+		plane_state->dma_addr[i] = dma_addr;
+	}
+
+	plane_state->status.src = drm_plane_state_src(new_state);
+	plane_state->status.dest = drm_plane_state_dest(new_state);
+
+	vs_dc_update_cursor_plane(dc, vs_plane, plane, state);
+}
+
+static void vs_plane_atomic_disable(struct drm_plane *plane,
+				    struct drm_atomic_state *state)
+{
+	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
+									   plane);
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+	struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	vs_dc_disable_plane(dc, vs_plane, old_state);
+}
+
+static void vs_cursor_plane_atomic_disable(struct drm_plane *plane,
+					   struct drm_atomic_state *state)
+{
+	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
+									   plane);
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+	struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	vs_dc_disable_cursor_plane(dc, vs_plane, old_state);
+}
+
+const struct drm_plane_helper_funcs primary_plane_helpers = {
+	.atomic_check	= vs_plane_atomic_check,
+	.atomic_update	= vs_plane_atomic_update,
+	.atomic_disable = vs_plane_atomic_disable,
+};
+
+const struct drm_plane_helper_funcs overlay_plane_helpers = {
+	.atomic_check	= vs_plane_atomic_check,
+	.atomic_update	= vs_plane_atomic_update,
+	.atomic_disable = vs_plane_atomic_disable,
+};
+
+const struct drm_plane_helper_funcs cursor_plane_helpers = {
+	.atomic_check	= vs_cursor_plane_atomic_check,
+	.atomic_update	= vs_cursor_plane_atomic_update,
+	.atomic_disable = vs_cursor_plane_atomic_disable,
+};
+
+static const struct drm_prop_enum_list vs_degamma_mode_enum_list[] = {
+	{ VS_DEGAMMA_DISABLE,	"disabled" },
+	{ VS_DEGAMMA_BT709,	"preset degamma for BT709" },
+	{ VS_DEGAMMA_BT2020,	"preset degamma for BT2020" },
+};
+
+struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
+				 struct vs_plane_info *info,
+				 unsigned int layer_num,
+				 unsigned int possible_crtcs)
+{
+	struct vs_plane *plane;
+	int ret;
+
+	if (!info)
+		return NULL;
+
+	plane = drmm_universal_plane_alloc(drm_dev, struct vs_plane, base,
+					   possible_crtcs,
+					   &vs_plane_funcs,
+					   info->formats, info->num_formats,
+					   info->modifiers, info->type,
+					   info->name ? info->name : NULL);
+	if (IS_ERR(plane))
+		return ERR_CAST(plane);
+
+	if (info->type == DRM_PLANE_TYPE_PRIMARY)
+		drm_plane_helper_add(&plane->base, &primary_plane_helpers);
+	else if (info->type == DRM_PLANE_TYPE_CURSOR)
+		drm_plane_helper_add(&plane->base, &cursor_plane_helpers);
+	else
+		drm_plane_helper_add(&plane->base, &overlay_plane_helpers);
+
+	/* Set up the plane properties */
+	if (info->degamma_size) {
+		plane->degamma_mode =
+		drm_property_create_enum(drm_dev, 0,
+					 "DEGAMMA_MODE",
+					 vs_degamma_mode_enum_list,
+					 ARRAY_SIZE(vs_degamma_mode_enum_list));
+
+		if (!plane->degamma_mode)
+			return NULL;
+
+		drm_object_attach_property(&plane->base.base,
+					   plane->degamma_mode,
+					   VS_DEGAMMA_DISABLE);
+	}
+
+	if (info->rotation) {
+		ret = drm_plane_create_rotation_property(&plane->base,
+							 DRM_MODE_ROTATE_0,
+							 info->rotation);
+		if (ret)
+			return NULL;
+	}
+
+	if (info->blend_mode) {
+		ret = drm_plane_create_blend_mode_property(&plane->base,
+							   info->blend_mode);
+		if (ret)
+			return NULL;
+		ret = drm_plane_create_alpha_property(&plane->base);
+		if (ret)
+			return NULL;
+	}
+
+	if (info->color_encoding) {
+		ret = drm_plane_create_color_properties(&plane->base,
+							info->color_encoding,
+							BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
+							DRM_COLOR_YCBCR_BT709,
+							DRM_COLOR_YCBCR_LIMITED_RANGE);
+		if (ret)
+			return NULL;
+	}
+
+	if (info->zpos != 255) {
+		ret = drm_plane_create_zpos_property(&plane->base, info->zpos, 0,
+						     layer_num - 1);
+		if (ret)
+			return NULL;
+	} else {
+		ret = drm_plane_create_zpos_immutable_property(&plane->base,
+							       info->zpos);
+		if (ret)
+			return NULL;
+	}
+
+	if (info->watermark) {
+		plane->watermark_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
+							    "WATERMARK", 0);
+		if (!plane->watermark_prop)
+			return NULL;
+		drm_object_attach_property(&plane->base.base, plane->watermark_prop, 0);
+	}
+
+	if (info->color_mgmt) {
+		plane->color_mgmt_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
+							     "COLOR_CONFIG", 0);
+		if (!plane->color_mgmt_prop)
+			return NULL;
+
+		drm_object_attach_property(&plane->base.base, plane->color_mgmt_prop, 0);
+	}
+
+	if (info->roi) {
+		plane->roi_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
+						      "ROI", 0);
+		if (!plane->roi_prop)
+			return NULL;
+
+		drm_object_attach_property(&plane->base.base, plane->roi_prop, 0);
+	}
+
+	return plane;
+}
diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h
new file mode 100644
index 000000000..554b74e96
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_plane.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_PLANE_H__
+#define __VS_PLANE_H__
+
+#include <drm/drm_fourcc.h>
+#include <drm/drm_plane_helper.h>
+
+#include "vs_type.h"
+
+struct vs_plane_status {
+	u32 tile_mode;
+	struct drm_rect src;
+	struct drm_rect dest;
+};
+
+struct vs_plane_state {
+	struct drm_plane_state base;
+	struct vs_plane_status status; /* for debugfs */
+
+	struct drm_property_blob *watermark;
+	struct drm_property_blob *color_mgmt;
+	struct drm_property_blob *roi;
+	dma_addr_t dma_addr[DRM_FORMAT_MAX_PLANES];
+
+	u32 degamma;
+	bool degamma_changed;
+};
+
+struct vs_plane {
+	struct drm_plane base;
+	u8 id;
+
+	struct drm_property *degamma_mode;
+	struct drm_property *watermark_prop;
+	struct drm_property *color_mgmt_prop;
+	struct drm_property *roi_prop;
+};
+
+struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
+				 struct vs_plane_info *info,
+				 unsigned int layer_num,
+				 unsigned int possible_crtcs);
+
+static inline struct vs_plane *to_vs_plane(struct drm_plane *plane)
+{
+	return container_of(plane, struct vs_plane, base);
+}
+
+static inline struct vs_plane_state *
+to_vs_plane_state(struct drm_plane_state *state)
+{
+	return container_of(state, struct vs_plane_state, base);
+}
+#endif /* __VS_PLANE_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_type.h b/drivers/gpu/drm/verisilicon/vs_type.h
new file mode 100644
index 000000000..7d3378e29
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_type.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_TYPE_H__
+#define __VS_TYPE_H__
+
+#include <drm/drm_plane.h>
+#include <drm/drm_plane_helper.h>
+
+struct vs_plane_info {
+	const char *name;
+	u8 id;
+	enum drm_plane_type type;
+	unsigned int num_formats;
+	const u32 *formats;
+	u8 num_modifiers;
+	const u64 *modifiers;
+	unsigned int min_width;
+	unsigned int min_height;
+	unsigned int max_width;
+	unsigned int max_height;
+	unsigned int rotation;
+	unsigned int blend_mode;
+	unsigned int color_encoding;
+
+	/* 0 means no de-gamma LUT */
+	unsigned int degamma_size;
+
+	int min_scale; /* 16.16 fixed point */
+	int max_scale; /* 16.16 fixed point */
+
+	/* default zorder value,
+	 * and 255 means unsupported zorder capability
+	 */
+	u8	 zpos;
+
+	bool watermark;
+	bool color_mgmt;
+	bool roi;
+};
+
+struct vs_dc_info {
+	const char *name;
+
+	u8 panel_num;
+
+	/* planes */
+	u8 plane_num;
+	const struct vs_plane_info *planes;
+
+	u8 layer_num;
+	unsigned int max_bpc;
+	unsigned int color_formats;
+
+	/* 0 means no gamma LUT */
+	u16 gamma_size;
+	u8 gamma_bits;
+
+	u16 pitch_alignment;
+
+	bool pipe_sync;
+	bool background;
+	bool panel_sync;
+	bool cap_dec;
+};
+
+#endif /* __VS_TYPE_H__ */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 117+ messages in thread

* [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-10-25 10:39   ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-25 10:39 UTC (permalink / raw)
  To: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Keith Zhao, Jack Zhu, Shengyang Chen,
	Changhuang Liang

add 2 crtcs and 8 planes in vs-drm

Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
---
 drivers/gpu/drm/verisilicon/Makefile   |    8 +-
 drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
 drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
 drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
 drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
 drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
 drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
 drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
 drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
 drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
 drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
 11 files changed, 4494 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h

diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
index 7d3be305b..1d48016ca 100644
--- a/drivers/gpu/drm/verisilicon/Makefile
+++ b/drivers/gpu/drm/verisilicon/Makefile
@@ -1,7 +1,11 @@
 # SPDX-License-Identifier: GPL-2.0
 
-vs_drm-objs := vs_drv.o \
-		vs_modeset.o 
+vs_drm-objs := vs_dc_hw.o \
+		vs_dc.o \
+		vs_crtc.o \
+		vs_drv.o \
+		vs_modeset.o \
+		vs_plane.o
 
 obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
 
diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
new file mode 100644
index 000000000..8a658ea77
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
@@ -0,0 +1,257 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/media-bus-format.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_vblank.h>
+#include <drm/vs_drm.h>
+
+#include "vs_crtc.h"
+#include "vs_dc.h"
+#include "vs_drv.h"
+
+static void vs_crtc_reset(struct drm_crtc *crtc)
+{
+	struct vs_crtc_state *state;
+
+	if (crtc->state) {
+		__drm_atomic_helper_crtc_destroy_state(crtc->state);
+
+		state = to_vs_crtc_state(crtc->state);
+		kfree(state);
+		crtc->state = NULL;
+	}
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return;
+
+	__drm_atomic_helper_crtc_reset(crtc, &state->base);
+}
+
+static struct drm_crtc_state *
+vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
+{
+	struct vs_crtc_state *ori_state;
+	struct vs_crtc_state *state;
+
+	if (!crtc->state)
+		return NULL;
+
+	ori_state = to_vs_crtc_state(crtc->state);
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return NULL;
+
+	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
+
+	state->output_fmt = ori_state->output_fmt;
+	state->encoder_type = ori_state->encoder_type;
+	state->bpp = ori_state->bpp;
+	state->underflow = ori_state->underflow;
+
+	return &state->base;
+}
+
+static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
+					 struct drm_crtc_state *state)
+{
+	__drm_atomic_helper_crtc_destroy_state(state);
+	kfree(to_vs_crtc_state(state));
+}
+
+static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	vs_dc_enable_vblank(dc, true);
+
+	return 0;
+}
+
+static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	vs_dc_enable_vblank(dc, false);
+}
+
+static const struct drm_crtc_funcs vs_crtc_funcs = {
+	.set_config		= drm_atomic_helper_set_config,
+	.page_flip		= drm_atomic_helper_page_flip,
+	.reset			= vs_crtc_reset,
+	.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
+	.atomic_destroy_state	= vs_crtc_atomic_destroy_state,
+	.enable_vblank		= vs_crtc_enable_vblank,
+	.disable_vblank		= vs_crtc_disable_vblank,
+};
+
+static u8 cal_pixel_bits(u32 bus_format)
+{
+	u8 bpp;
+
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_RGB565_1X16:
+	case MEDIA_BUS_FMT_UYVY8_1X16:
+		bpp = 16;
+		break;
+	case MEDIA_BUS_FMT_RGB666_1X18:
+	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+		bpp = 18;
+		break;
+	case MEDIA_BUS_FMT_UYVY10_1X20:
+		bpp = 20;
+		break;
+	case MEDIA_BUS_FMT_BGR888_1X24:
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+	case MEDIA_BUS_FMT_YUV8_1X24:
+		bpp = 24;
+		break;
+	case MEDIA_BUS_FMT_RGB101010_1X30:
+	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+	case MEDIA_BUS_FMT_YUV10_1X30:
+		bpp = 30;
+		break;
+	default:
+		bpp = 24;
+		break;
+	}
+
+	return bpp;
+}
+
+static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
+				  struct drm_atomic_state *state)
+{
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
+
+	vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
+
+	vs_dc_enable(dc, crtc);
+	drm_crtc_vblank_on(crtc);
+}
+
+static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
+				   struct drm_atomic_state *state)
+{
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	drm_crtc_vblank_off(crtc);
+
+	vs_dc_disable(dc, crtc);
+
+	if (crtc->state->event && !crtc->state->active) {
+		spin_lock_irq(&crtc->dev->event_lock);
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+		spin_unlock_irq(&crtc->dev->event_lock);
+
+		crtc->state->event = NULL;
+	}
+}
+
+static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
+				 struct drm_atomic_state *state)
+{
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
+
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct device *dev = vs_crtc->dev;
+	struct drm_property_blob *blob = crtc->state->gamma_lut;
+	struct drm_color_lut *lut;
+	struct vs_dc *dc = dev_get_drvdata(dev);
+
+	if (crtc_state->color_mgmt_changed) {
+		if (blob && blob->length) {
+			lut = blob->data;
+			vs_dc_set_gamma(dc, crtc, lut,
+					blob->length / sizeof(*lut));
+			vs_dc_enable_gamma(dc, crtc, true);
+		} else {
+			vs_dc_enable_gamma(dc, crtc, false);
+		}
+	}
+}
+
+static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
+				 struct drm_atomic_state *state)
+{
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct drm_pending_vblank_event *event = crtc->state->event;
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	vs_dc_commit(dc);
+
+	if (event) {
+		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+
+		spin_lock_irq(&crtc->dev->event_lock);
+		drm_crtc_arm_vblank_event(crtc, event);
+		spin_unlock_irq(&crtc->dev->event_lock);
+		crtc->state->event = NULL;
+	}
+}
+
+static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
+	.atomic_check = drm_crtc_helper_atomic_check,
+	.atomic_enable	= vs_crtc_atomic_enable,
+	.atomic_disable = vs_crtc_atomic_disable,
+	.atomic_begin	= vs_crtc_atomic_begin,
+	.atomic_flush	= vs_crtc_atomic_flush,
+};
+
+static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
+	{ VS_SINGLE_DC,				"single dc mode" },
+	{ VS_MULTI_DC_PRIMARY,		"primary dc for multi dc mode" },
+	{ VS_MULTI_DC_SECONDARY,	"secondary dc for multi dc mode" },
+};
+
+struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
+			       struct vs_dc_info *info)
+{
+	struct vs_crtc *crtc;
+	int ret;
+
+	if (!info)
+		return NULL;
+
+	crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
+	if (!crtc)
+		return NULL;
+
+	ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
+					 NULL, NULL, &vs_crtc_funcs,
+					 info->name ? info->name : NULL);
+	if (ret)
+		return NULL;
+
+	drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
+
+	if (info->gamma_size) {
+		ret = drm_mode_crtc_set_gamma_size(&crtc->base,
+						   info->gamma_size);
+		if (ret)
+			return NULL;
+
+		drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
+					   info->gamma_size);
+	}
+
+	crtc->max_bpc = info->max_bpc;
+	crtc->color_formats = info->color_formats;
+	return crtc;
+}
diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h
new file mode 100644
index 000000000..526dd63e5
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_crtc.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_CRTC_H__
+#define __VS_CRTC_H__
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "vs_type.h"
+
+struct vs_crtc_state {
+	struct drm_crtc_state base;
+
+	u32 output_fmt;
+	u8 encoder_type;
+	u8 bpp;
+	bool underflow;
+};
+
+struct vs_crtc {
+	struct drm_crtc base;
+	struct device *dev;
+	unsigned int max_bpc;
+	unsigned int color_formats;
+};
+
+struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
+			       struct vs_dc_info *info);
+
+static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc)
+{
+	return container_of(crtc, struct vs_crtc, base);
+}
+
+static inline struct vs_crtc_state *
+to_vs_crtc_state(struct drm_crtc_state *state)
+{
+	return container_of(state, struct vs_crtc_state, base);
+}
+#endif /* __VS_CRTC_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
new file mode 100644
index 000000000..b5ab92d98
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc.c
@@ -0,0 +1,1002 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_vblank.h>
+#include <drm/vs_drm.h>
+
+#include "vs_dc_hw.h"
+#include "vs_dc.h"
+#include "vs_drv.h"
+
+static const char * const vout_clocks[] = {
+	"noc_bus",
+	"channel0",
+	"channel1",
+	"dc_core",
+	"axi_core",
+	"ahb",
+	"hdmi_tx",
+	"dc_parent",
+
+};
+
+static const char * const vout_resets[] = {
+	"axi",
+	"ahb",
+	"core",
+};
+
+static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb)
+{
+	u8 f = FORMAT_A8R8G8B8;
+
+	switch (format) {
+	case DRM_FORMAT_XRGB4444:
+	case DRM_FORMAT_RGBX4444:
+	case DRM_FORMAT_XBGR4444:
+	case DRM_FORMAT_BGRX4444:
+		f = FORMAT_X4R4G4B4;
+		break;
+	case DRM_FORMAT_ARGB4444:
+	case DRM_FORMAT_RGBA4444:
+	case DRM_FORMAT_ABGR4444:
+	case DRM_FORMAT_BGRA4444:
+		f = FORMAT_A4R4G4B4;
+		break;
+	case DRM_FORMAT_XRGB1555:
+	case DRM_FORMAT_RGBX5551:
+	case DRM_FORMAT_XBGR1555:
+	case DRM_FORMAT_BGRX5551:
+		f = FORMAT_X1R5G5B5;
+		break;
+	case DRM_FORMAT_ARGB1555:
+	case DRM_FORMAT_RGBA5551:
+	case DRM_FORMAT_ABGR1555:
+	case DRM_FORMAT_BGRA5551:
+		f = FORMAT_A1R5G5B5;
+		break;
+	case DRM_FORMAT_RGB565:
+	case DRM_FORMAT_BGR565:
+		f = FORMAT_R5G6B5;
+		break;
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_RGBX8888:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_BGRX8888:
+		f = FORMAT_X8R8G8B8;
+		break;
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_RGBA8888:
+	case DRM_FORMAT_ABGR8888:
+	case DRM_FORMAT_BGRA8888:
+		f = FORMAT_A8R8G8B8;
+		break;
+	case DRM_FORMAT_YUYV:
+	case DRM_FORMAT_YVYU:
+		f = FORMAT_YUY2;
+		break;
+	case DRM_FORMAT_UYVY:
+	case DRM_FORMAT_VYUY:
+		f = FORMAT_UYVY;
+		break;
+	case DRM_FORMAT_YUV420:
+	case DRM_FORMAT_YVU420:
+		f = FORMAT_YV12;
+		break;
+	case DRM_FORMAT_NV21:
+		f = FORMAT_NV12;
+		break;
+	case DRM_FORMAT_NV16:
+	case DRM_FORMAT_NV61:
+		f = FORMAT_NV16;
+		break;
+	case DRM_FORMAT_P010:
+		f = FORMAT_P010;
+		break;
+	case DRM_FORMAT_ARGB2101010:
+	case DRM_FORMAT_RGBA1010102:
+	case DRM_FORMAT_ABGR2101010:
+	case DRM_FORMAT_BGRA1010102:
+		f = FORMAT_A2R10G10B10;
+		break;
+	case DRM_FORMAT_NV12:
+		f = FORMAT_NV12;
+		break;
+	case DRM_FORMAT_YUV444:
+		f = FORMAT_YUV444;
+		break;
+	default:
+		break;
+	}
+
+	fb->format = f;
+}
+
+static inline void update_swizzle(u32 format, struct dc_hw_fb *fb)
+{
+	fb->swizzle = SWIZZLE_ARGB;
+	fb->uv_swizzle = 0;
+
+	switch (format) {
+	case DRM_FORMAT_RGBX4444:
+	case DRM_FORMAT_RGBA4444:
+	case DRM_FORMAT_RGBX5551:
+	case DRM_FORMAT_RGBA5551:
+	case DRM_FORMAT_RGBX8888:
+	case DRM_FORMAT_RGBA8888:
+	case DRM_FORMAT_RGBA1010102:
+		fb->swizzle = SWIZZLE_RGBA;
+		break;
+	case DRM_FORMAT_XBGR4444:
+	case DRM_FORMAT_ABGR4444:
+	case DRM_FORMAT_XBGR1555:
+	case DRM_FORMAT_ABGR1555:
+	case DRM_FORMAT_BGR565:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_ABGR8888:
+	case DRM_FORMAT_ABGR2101010:
+		fb->swizzle = SWIZZLE_ABGR;
+		break;
+	case DRM_FORMAT_BGRX4444:
+	case DRM_FORMAT_BGRA4444:
+	case DRM_FORMAT_BGRX5551:
+	case DRM_FORMAT_BGRA5551:
+	case DRM_FORMAT_BGRX8888:
+	case DRM_FORMAT_BGRA8888:
+	case DRM_FORMAT_BGRA1010102:
+		fb->swizzle = SWIZZLE_BGRA;
+		break;
+	case DRM_FORMAT_YVYU:
+	case DRM_FORMAT_VYUY:
+	case DRM_FORMAT_NV21:
+	case DRM_FORMAT_NV61:
+		fb->uv_swizzle = 1;
+		break;
+	default:
+		break;
+	}
+}
+
+static inline void update_watermark(struct drm_property_blob *watermark,
+				    struct dc_hw_fb *fb)
+{
+	struct drm_vs_watermark *data;
+
+	fb->water_mark = 0;
+
+	if (watermark) {
+		data = watermark->data;
+		fb->water_mark = data->watermark & 0xFFFFF;
+	}
+}
+
+static inline u8 to_vs_rotation(unsigned int rotation)
+{
+	u8 rot;
+
+	switch (rotation & DRM_MODE_REFLECT_MASK) {
+	case DRM_MODE_REFLECT_X:
+		rot = FLIP_X;
+		return rot;
+	case DRM_MODE_REFLECT_Y:
+		rot = FLIP_Y;
+		return rot;
+	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
+		rot = FLIP_XY;
+		return rot;
+	default:
+		break;
+	}
+
+	switch (rotation & DRM_MODE_ROTATE_MASK) {
+	case DRM_MODE_ROTATE_0:
+		rot = ROT_0;
+		break;
+	case DRM_MODE_ROTATE_90:
+		rot = ROT_90;
+		break;
+	case DRM_MODE_ROTATE_180:
+		rot = ROT_180;
+		break;
+	case DRM_MODE_ROTATE_270:
+		rot = ROT_270;
+		break;
+	default:
+		rot = ROT_0;
+		break;
+	}
+
+	return rot;
+}
+
+static inline u8 to_vs_yuv_color_space(u32 color_space)
+{
+	u8 cs;
+
+	switch (color_space) {
+	case DRM_COLOR_YCBCR_BT601:
+		cs = COLOR_SPACE_601;
+		break;
+	case DRM_COLOR_YCBCR_BT709:
+		cs = COLOR_SPACE_709;
+		break;
+	case DRM_COLOR_YCBCR_BT2020:
+		cs = COLOR_SPACE_2020;
+		break;
+	default:
+		cs = COLOR_SPACE_601;
+		break;
+	}
+
+	return cs;
+}
+
+static inline u8 to_vs_tile_mode(u64 modifier)
+{
+	if (modifier == DRM_FORMAT_MOD_VIVANTE_TILED)
+		return DC_TILE_MODE4X4;
+
+	return (u8)(modifier & DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK);
+}
+
+static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc)
+{
+	u8 panel_num = dc->hw.info->panel_num;
+	u32 index = drm_crtc_index(crtc);
+	int i;
+
+	for (i = 0; i < panel_num; i++) {
+		if (index == dc->crtc[i]->base.index)
+			return i;
+	}
+
+	return 0;
+}
+
+static void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
+					  unsigned int alignment)
+{
+	struct vs_drm_device *priv = to_vs_drm_private(drm_dev);
+
+	if (alignment > priv->pitch_alignment)
+		priv->pitch_alignment = alignment;
+}
+
+static int plda_clk_rst_init(struct device *dev)
+{
+	int ret = 0;
+	struct vs_dc *dc = dev_get_drvdata(dev);
+
+	ret = clk_bulk_prepare_enable(dc->nclks, dc->clk_vout);
+	if (ret) {
+		dev_err(dev, "failed to enable clocks\n");
+		return ret;
+	}
+
+	ret = reset_control_bulk_deassert(dc->nrsts, dc->rst_vout);
+	return ret;
+}
+
+static void plda_clk_rst_deinit(struct device *dev)
+{
+	struct vs_dc *dc = dev_get_drvdata(dev);
+
+	reset_control_bulk_assert(dc->nrsts, dc->rst_vout);
+	clk_bulk_disable_unprepare(dc->nclks, dc->clk_vout);
+}
+
+static void dc_deinit(struct device *dev)
+{
+	struct vs_dc *dc = dev_get_drvdata(dev);
+
+	dc_hw_enable_interrupt(&dc->hw, 0);
+	dc_hw_deinit(&dc->hw);
+	plda_clk_rst_deinit(dev);
+}
+
+static int dc_init(struct device *dev)
+{
+	struct vs_dc *dc = dev_get_drvdata(dev);
+	int ret;
+
+	dc->first_frame = true;
+
+	ret = plda_clk_rst_init(dev);
+	if (ret < 0) {
+		dev_err(dev, "failed to init dc clk reset: %d\n", ret);
+		return ret;
+	}
+
+	ret = dc_hw_init(&dc->hw);
+	if (ret) {
+		dev_err(dev, "failed to init DC HW\n");
+		return ret;
+	}
+	return 0;
+}
+
+void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
+{
+	struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
+	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+	struct dc_hw_display display;
+
+	display.bus_format = crtc_state->output_fmt;
+	display.h_active = mode->hdisplay;
+	display.h_total = mode->htotal;
+	display.h_sync_start = mode->hsync_start;
+	display.h_sync_end = mode->hsync_end;
+	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+		display.h_sync_polarity = true;
+	else
+		display.h_sync_polarity = false;
+
+	display.v_active = mode->vdisplay;
+	display.v_total = mode->vtotal;
+	display.v_sync_start = mode->vsync_start;
+	display.v_sync_end = mode->vsync_end;
+
+	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+		display.v_sync_polarity = true;
+	else
+		display.v_sync_polarity = false;
+
+	display.id = to_vs_display_id(dc, crtc);
+
+	display.enable = true;
+
+	if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) {
+		dc_hw_set_out(&dc->hw, OUT_DPI, display.id);
+		clk_set_rate(dc->clk_vout[CLK_VOUT_SOC_PIX].clk, mode->clock * 1000);
+		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX1].clk,
+			       dc->clk_vout[CLK_VOUT_SOC_PIX].clk);
+	} else {
+		dc_hw_set_out(&dc->hw, OUT_DP, display.id);
+		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX0].clk,
+			       dc->clk_vout[CLK_VOUT_HDMI_PIX].clk);
+	}
+
+	dc_hw_setup_display(&dc->hw, &display);
+}
+
+void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc)
+{
+	struct dc_hw_display display;
+
+	display.id = to_vs_display_id(dc, crtc);
+	display.enable = false;
+
+	dc_hw_setup_display(&dc->hw, &display);
+}
+
+void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
+		     struct drm_color_lut *lut, unsigned int size)
+{
+	u16 i, r, g, b;
+	u8 bits, id;
+
+	if (size != dc->hw.info->gamma_size) {
+		drm_err(crtc->dev, "gamma size does not match!\n");
+		return;
+	}
+
+	id = to_vs_display_id(dc, crtc);
+
+	bits = dc->hw.info->gamma_bits;
+	for (i = 0; i < size; i++) {
+		r = drm_color_lut_extract(lut[i].red, bits);
+		g = drm_color_lut_extract(lut[i].green, bits);
+		b = drm_color_lut_extract(lut[i].blue, bits);
+		dc_hw_update_gamma(&dc->hw, id, i, r, g, b);
+	}
+}
+
+void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
+			bool enable)
+{
+	u8 id;
+
+	id = to_vs_display_id(dc, crtc);
+	dc_hw_enable_gamma(&dc->hw, id, enable);
+}
+
+void vs_dc_enable_vblank(struct vs_dc *dc, bool enable)
+{
+	dc_hw_enable_interrupt(&dc->hw, enable);
+}
+
+static u32 calc_factor(u32 src, u32 dest)
+{
+	u32 factor = 1 << 16;
+
+	if (src > 1 && dest > 1)
+		factor = ((src - 1) << 16) / (dest - 1);
+
+	return factor;
+}
+
+static void update_scale(struct drm_plane_state *state, struct dc_hw_roi *roi,
+			 struct dc_hw_scale *scale)
+{
+	int dst_w = drm_rect_width(&state->dst);
+	int dst_h = drm_rect_height(&state->dst);
+	int src_w, src_h, temp;
+
+	scale->enable = false;
+
+	if (roi->enable) {
+		src_w = roi->width;
+		src_h = roi->height;
+	} else {
+		src_w = drm_rect_width(&state->src) >> 16;
+		src_h = drm_rect_height(&state->src) >> 16;
+	}
+
+	if (drm_rotation_90_or_270(state->rotation)) {
+		temp = src_w;
+		src_w = src_h;
+		src_h = temp;
+	}
+
+	if (src_w != dst_w) {
+		scale->scale_factor_x = calc_factor(src_w, dst_w);
+		scale->enable = true;
+	} else {
+		scale->scale_factor_x = 1 << 16;
+	}
+	if (src_h != dst_h) {
+		scale->scale_factor_y = calc_factor(src_h, dst_h);
+		scale->enable = true;
+	} else {
+		scale->scale_factor_y = 1 << 16;
+	}
+}
+
+static void update_fb(struct vs_plane *plane, u8 display_id,
+		      struct dc_hw_fb *fb, struct drm_plane_state *state)
+{
+	struct vs_plane_state *plane_state = to_vs_plane_state(state);
+	struct drm_framebuffer *drm_fb = state->fb;
+	struct drm_rect *src = &state->src;
+
+	fb->display_id = display_id;
+	fb->y_address = plane_state->dma_addr[0];
+	fb->y_stride = drm_fb->pitches[0];
+	if (drm_fb->format->format == DRM_FORMAT_YVU420) {
+		fb->u_address = plane_state->dma_addr[2];
+		fb->v_address = plane_state->dma_addr[1];
+		fb->u_stride = drm_fb->pitches[2];
+		fb->v_stride = drm_fb->pitches[1];
+	} else {
+		fb->u_address = plane_state->dma_addr[1];
+		fb->v_address = plane_state->dma_addr[2];
+		fb->u_stride = drm_fb->pitches[1];
+		fb->v_stride = drm_fb->pitches[2];
+	}
+	fb->width = drm_rect_width(src) >> 16;
+	fb->height = drm_rect_height(src) >> 16;
+	fb->tile_mode = to_vs_tile_mode(drm_fb->modifier);
+	fb->rotation = to_vs_rotation(state->rotation);
+	fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding);
+	fb->zpos = state->zpos;
+	fb->enable = state->visible;
+	update_format(drm_fb->format->format, drm_fb->modifier, fb);
+	update_swizzle(drm_fb->format->format, fb);
+	update_watermark(plane_state->watermark, fb);
+	plane_state->status.tile_mode = fb->tile_mode;
+}
+
+static void update_degamma(struct vs_dc *dc, struct vs_plane *plane,
+			   struct vs_plane_state *plane_state)
+{
+	dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
+	plane_state->degamma_changed = false;
+}
+
+static void update_roi(struct vs_dc *dc, u8 id,
+		       struct vs_plane_state *plane_state,
+		       struct dc_hw_roi *roi,
+		       struct drm_plane_state *state)
+{
+	struct drm_vs_roi *data;
+	struct drm_rect *src = &state->src;
+	u16 src_w = drm_rect_width(src) >> 16;
+	u16 src_h = drm_rect_height(src) >> 16;
+
+	if (plane_state->roi) {
+		data = plane_state->roi->data;
+
+		if (data->enable) {
+			roi->x = data->roi_x;
+			roi->y = data->roi_y;
+			roi->width = (data->roi_x + data->roi_w > src_w) ?
+						 (src_w - data->roi_x) : data->roi_w;
+			roi->height = (data->roi_y + data->roi_h > src_h) ?
+						  (src_h - data->roi_y) : data->roi_h;
+			roi->enable = true;
+		} else {
+			roi->enable = false;
+		}
+
+		dc_hw_update_roi(&dc->hw, id, roi);
+	} else {
+		roi->enable = false;
+	}
+}
+
+static void update_color_mgmt(struct vs_dc *dc, u8 id,
+			      struct dc_hw_fb *fb,
+			      struct vs_plane_state *plane_state)
+{
+	struct drm_vs_color_mgmt *data;
+	struct dc_hw_colorkey colorkey;
+
+	if (plane_state->color_mgmt) {
+		data = plane_state->color_mgmt->data;
+
+		fb->clear_enable = data->clear_enable;
+		fb->clear_value = data->clear_value;
+
+		if (data->colorkey > data->colorkey_high)
+			data->colorkey = data->colorkey_high;
+
+		colorkey.colorkey = data->colorkey;
+		colorkey.colorkey_high = data->colorkey_high;
+		colorkey.transparency = (data->transparency) ?
+				DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE;
+		dc_hw_update_colorkey(&dc->hw, id, &colorkey);
+	}
+}
+
+static void update_plane(struct vs_dc *dc, struct vs_plane *plane,
+			 struct drm_plane *drm_plane,
+			 struct drm_atomic_state *drm_state)
+{
+	struct dc_hw_fb fb = {0};
+	struct dc_hw_scale scale;
+	struct dc_hw_position pos;
+	struct dc_hw_blend blend;
+	struct dc_hw_roi roi;
+	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
+									   drm_plane);
+	struct vs_plane_state *plane_state = to_vs_plane_state(state);
+	struct drm_rect *dest = &state->dst;
+	bool dec_enable = false;
+	u8 display_id = 0;
+
+	display_id = to_vs_display_id(dc, state->crtc);
+	update_fb(plane, display_id, &fb, state);
+	fb.dec_enable = dec_enable;
+
+	update_roi(dc, plane->id, plane_state, &roi, state);
+
+	update_scale(state, &roi, &scale);
+
+	if (plane_state->degamma_changed)
+		update_degamma(dc, plane, plane_state);
+
+	pos.start_x = dest->x1;
+	pos.start_y = dest->y1;
+	pos.end_x = dest->x2;
+	pos.end_y = dest->y2;
+
+	blend.alpha = (u8)(state->alpha >> 8);
+	blend.blend_mode = (u8)(state->pixel_blend_mode);
+
+	update_color_mgmt(dc, plane->id, &fb, plane_state);
+
+	dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
+}
+
+static void update_qos(struct vs_dc *dc, struct vs_plane *plane,
+		       struct drm_plane *drm_plane,
+		       struct drm_atomic_state *drm_state)
+{
+	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
+									   drm_plane);
+	struct vs_plane_state *plane_state = to_vs_plane_state(state);
+	struct drm_vs_watermark *data;
+	struct dc_hw_qos qos;
+
+	if (plane_state->watermark) {
+		data = plane_state->watermark->data;
+
+		if (data->qos_high) {
+			if (data->qos_low > data->qos_high)
+				data->qos_low = data->qos_high;
+
+			qos.low_value = data->qos_low & 0x0F;
+			qos.high_value = data->qos_high & 0x0F;
+			dc_hw_update_qos(&dc->hw, &qos);
+		}
+	}
+}
+
+static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor)
+{
+	u8 size_type;
+
+	switch (state->crtc_w) {
+	case 32:
+		size_type = CURSOR_SIZE_32X32;
+		break;
+	case 64:
+		size_type = CURSOR_SIZE_64X64;
+		break;
+	default:
+		size_type = CURSOR_SIZE_32X32;
+		break;
+	}
+
+	cursor->size = size_type;
+}
+
+static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
+				struct drm_plane *drm_plane,
+				struct drm_atomic_state *drm_state)
+{
+	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
+								       drm_plane);
+	struct vs_plane_state *plane_state = to_vs_plane_state(state);
+	struct drm_framebuffer *drm_fb = state->fb;
+	struct dc_hw_cursor cursor;
+
+	cursor.address = plane_state->dma_addr[0];
+	cursor.x = state->crtc_x;
+	cursor.y = state->crtc_y;
+	cursor.hot_x = drm_fb->hot_x;
+	cursor.hot_y = drm_fb->hot_y;
+	cursor.display_id = to_vs_display_id(dc, state->crtc);
+	update_cursor_size(state, &cursor);
+	cursor.enable = true;
+
+	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
+}
+
+void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
+			struct drm_plane *drm_plane,
+			struct drm_atomic_state *drm_state)
+{
+	update_plane(dc, plane, drm_plane, drm_state);
+	update_qos(dc, plane, drm_plane, drm_state);
+}
+
+void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
+			       struct drm_plane *drm_plane,
+			       struct drm_atomic_state *drm_state)
+{
+	update_cursor_plane(dc, plane, drm_plane, drm_state);
+}
+
+void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
+			 struct drm_plane_state *old_state)
+{
+	struct dc_hw_fb fb = {0};
+
+	fb.enable = false;
+	dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL, NULL, NULL);
+}
+
+void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
+				struct drm_plane_state *old_state)
+{
+	struct dc_hw_cursor cursor = {0};
+
+	cursor.enable = false;
+	cursor.display_id = to_vs_display_id(dc, old_state->crtc);
+	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
+}
+
+static bool vs_dc_mod_supported(const struct vs_plane_info *plane_info,
+				u64 modifier)
+{
+	const u64 *mods;
+
+	if (!plane_info->modifiers)
+		return false;
+
+	for (mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID; mods++) {
+		if (*mods == modifier)
+			return true;
+	}
+
+	return false;
+}
+
+int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
+		      struct drm_atomic_state *state)
+{
+	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+										 plane);
+
+	struct drm_framebuffer *fb = new_plane_state->fb;
+	const struct vs_plane_info *plane_info;
+	struct drm_crtc *crtc = new_plane_state->crtc;
+	struct drm_crtc_state *crtc_state;
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+
+	plane_info = &dc->hw.info->planes[vs_plane->id];
+
+	if (fb->width < plane_info->min_width ||
+	    fb->width > plane_info->max_width ||
+	    fb->height < plane_info->min_height ||
+	    fb->height > plane_info->max_height)
+		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n",
+			     vs_plane->id);
+
+	if (!vs_dc_mod_supported(plane_info, fb->modifier)) {
+		drm_err(plane->dev, "unsupported modifier on plane%d.\n", vs_plane->id);
+		return -EINVAL;
+	}
+
+	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
+	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
+						  plane_info->min_scale,
+						  plane_info->max_scale,
+						  true, true);
+}
+
+int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
+			     struct drm_atomic_state *state)
+{
+	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+									   plane);
+	struct drm_framebuffer *fb = new_plane_state->fb;
+	const struct vs_plane_info *plane_info;
+	struct drm_crtc *crtc = new_plane_state->crtc;
+	struct drm_crtc_state *crtc_state;
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+
+	plane_info = &dc->hw.info->planes[vs_plane->id];
+
+	if (fb->width < plane_info->min_width ||
+	    fb->width > plane_info->max_width ||
+	    fb->height < plane_info->min_height ||
+	    fb->height > plane_info->max_height)
+		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n", vs_plane->id);
+
+	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
+	if (IS_ERR(crtc_state))
+		return -EINVAL;
+
+	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
+						plane_info->min_scale,
+						plane_info->max_scale,
+						true, true);
+}
+
+static void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
+{
+	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
+
+	drm_crtc_handle_vblank(crtc);
+
+	vs_crtc_state->underflow = underflow;
+}
+
+static irqreturn_t dc_isr(int irq, void *data)
+{
+	struct vs_dc *dc = data;
+	struct vs_dc_info *dc_info = dc->hw.info;
+	u32 i, ret;
+
+	ret = dc_hw_get_interrupt(&dc->hw);
+
+	for (i = 0; i < dc_info->panel_num; i++)
+		vs_crtc_handle_vblank(&dc->crtc[i]->base, dc_hw_check_underflow(&dc->hw));
+
+	return IRQ_HANDLED;
+}
+
+void vs_dc_commit(struct vs_dc *dc)
+{
+	dc_hw_enable_shadow_register(&dc->hw, false);
+
+	dc_hw_commit(&dc->hw);
+
+	if (dc->first_frame)
+		dc->first_frame = false;
+
+	dc_hw_enable_shadow_register(&dc->hw, true);
+}
+
+static int dc_bind(struct device *dev, struct device *master, void *data)
+{
+	struct drm_device *drm_dev = data;
+	struct vs_dc *dc = dev_get_drvdata(dev);
+	struct device_node *port;
+	struct vs_crtc *crtc;
+	struct vs_dc_info *dc_info;
+	struct vs_plane *plane;
+	struct vs_plane_info *plane_info;
+	int i, ret;
+	u32 ctrc_mask = 0;
+
+	if (!drm_dev || !dc) {
+		dev_err(dev, "devices are not created.\n");
+		return -ENODEV;
+	}
+
+	ret = dc_init(dev);
+	if (ret < 0) {
+		drm_err(drm_dev, "Failed to initialize DC hardware.\n");
+		return ret;
+	}
+
+	port = of_get_child_by_name(dev->of_node, "port");
+	if (!port) {
+		drm_err(drm_dev, "no port node found\n");
+		return -ENODEV;
+	}
+	of_node_put(port);
+
+	dc_info = dc->hw.info;
+
+	for (i = 0; i < dc_info->panel_num; i++) {
+		crtc = vs_crtc_create(drm_dev, dc_info);
+		if (!crtc) {
+			drm_err(drm_dev, "Failed to create CRTC.\n");
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		crtc->base.port = port;
+		crtc->dev = dev;
+		dc->crtc[i] = crtc;
+		ctrc_mask |= drm_crtc_mask(&crtc->base);
+	}
+
+	for (i = 0; i < dc_info->plane_num; i++) {
+		plane_info = (struct vs_plane_info *)&dc_info->planes[i];
+
+		if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
+			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
+						drm_crtc_mask(&dc->crtc[0]->base));
+		} else if (!strcmp(plane_info->name, "Primary_1") ||
+				   !strcmp(plane_info->name, "Cursor_1")) {
+			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
+						drm_crtc_mask(&dc->crtc[1]->base));
+		} else {
+			plane = vs_plane_create(drm_dev, plane_info,
+						dc_info->layer_num, ctrc_mask);
+		}
+
+		if (IS_ERR(plane)) {
+			dev_err(dev, "failed to construct plane\n");
+			return PTR_ERR(plane);
+		}
+
+		plane->id = i;
+		dc->planes[i].id = plane_info->id;
+
+		if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
+			if (!strcmp(plane_info->name, "Primary"))
+				dc->crtc[0]->base.primary = &plane->base;
+			else
+				dc->crtc[1]->base.primary = &plane->base;
+			drm_dev->mode_config.min_width = plane_info->min_width;
+			drm_dev->mode_config.min_height =
+							plane_info->min_height;
+			drm_dev->mode_config.max_width = plane_info->max_width;
+			drm_dev->mode_config.max_height =
+							plane_info->max_height;
+		}
+
+		if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
+			if (!strcmp(plane_info->name, "Cursor"))
+				dc->crtc[0]->base.cursor = &plane->base;
+			else
+				dc->crtc[1]->base.cursor = &plane->base;
+			drm_dev->mode_config.cursor_width =
+							plane_info->max_width;
+			drm_dev->mode_config.cursor_height =
+							plane_info->max_height;
+		}
+	}
+
+	vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
+	return 0;
+}
+
+static void dc_unbind(struct device *dev, struct device *master, void *data)
+{
+	dc_deinit(dev);
+}
+
+const struct component_ops dc_component_ops = {
+	.bind = dc_bind,
+	.unbind = dc_unbind,
+};
+
+static const struct of_device_id dc_driver_dt_match[] = {
+	{ .compatible = "starfive,jh7110-dc8200", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
+
+static int dc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct vs_dc *dc;
+	int irq, ret, i;
+
+	dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
+	if (!dc)
+		return -ENOMEM;
+
+	dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(dc->hw.hi_base))
+		return PTR_ERR(dc->hw.hi_base);
+
+	dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
+	if (IS_ERR(dc->hw.reg_base))
+		return PTR_ERR(dc->hw.reg_base);
+
+	dc->nclks = ARRAY_SIZE(dc->clk_vout);
+	for (i = 0; i < dc->nclks; ++i)
+		dc->clk_vout[i].id = vout_clocks[i];
+	ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
+	if (ret) {
+		dev_err(dev, "Failed to get clk controls\n");
+		return ret;
+	}
+
+	dc->nrsts = ARRAY_SIZE(dc->rst_vout);
+	for (i = 0; i < dc->nrsts; ++i)
+		dc->rst_vout[i].id = vout_resets[i];
+	ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
+						 dc->rst_vout);
+	if (ret) {
+		dev_err(dev, "Failed to get reset controls\n");
+		return ret;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+
+	ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
+	if (ret < 0) {
+		dev_err(dev, "Failed to install irq:%u.\n", irq);
+		return ret;
+	}
+
+	dev_set_drvdata(dev, dc);
+
+	return component_add(dev, &dc_component_ops);
+}
+
+static int dc_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	component_del(dev, &dc_component_ops);
+
+	dev_set_drvdata(dev, NULL);
+
+	return 0;
+}
+
+struct platform_driver dc_platform_driver = {
+	.probe = dc_probe,
+	.remove = dc_remove,
+	.driver = {
+		.name = "vs-dc",
+		.of_match_table = of_match_ptr(dc_driver_dt_match),
+	},
+};
+
+MODULE_AUTHOR("StarFive Corporation");
+MODULE_DESCRIPTION("VeriSilicon DC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
new file mode 100644
index 000000000..8e96fd32c
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_DC_H__
+#define __VS_DC_H__
+
+#include <linux/clk.h>
+#include <linux/mm_types.h>
+#include <linux/reset.h>
+
+#include <drm/drm_fourcc.h>
+#include <drm/drm_modes.h>
+
+#include "vs_crtc.h"
+#include "vs_dc_hw.h"
+#include "vs_plane.h"
+
+struct vs_dc_plane {
+	enum dc_hw_plane_id id;
+};
+
+enum vout_clk {
+	CLK_VOUT_NOC_DISP = 0,
+	CLK_VOUT_PIX0,
+	CLK_VOUT_PIX1,
+	CLK_VOUT_CORE,
+	CLK_VOUT_AXI,
+	CLK_VOUT_AHB,
+	CLK_VOUT_HDMI_PIX,
+	CLK_VOUT_SOC_PIX,
+	CLK_VOUT_NUM
+};
+
+enum rst_vout {
+	RST_VOUT_AXI = 0,
+	RST_VOUT_AHB,
+	RST_VOUT_CORE,
+	RST_VOUT_NUM
+};
+
+struct vs_dc {
+	struct vs_crtc		*crtc[DC_DISPLAY_NUM];
+	struct dc_hw		hw;
+	bool			first_frame;
+
+	struct vs_dc_plane	planes[PLANE_NUM];
+	struct clk_bulk_data	clk_vout[CLK_VOUT_NUM];
+	int			nclks;
+	struct reset_control_bulk_data rst_vout[RST_VOUT_NUM];
+	int			nrsts;
+};
+
+void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc);
+void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc);
+
+void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
+		     struct drm_color_lut *lut, unsigned int size);
+void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc, bool enable);
+void vs_dc_enable_vblank(struct vs_dc *dc, bool enable);
+void vs_dc_commit(struct vs_dc *dc);
+void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
+			struct drm_plane *drm_plane,
+			struct drm_atomic_state *drm_state);
+void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
+			 struct drm_plane_state *old_state);
+int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
+		      struct drm_atomic_state *state);
+void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
+			       struct drm_plane *drm_plane,
+			       struct drm_atomic_state *drm_state);
+void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
+				struct drm_plane_state *old_state);
+int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
+			     struct drm_atomic_state *state);
+
+extern struct platform_driver dc_platform_driver;
+
+#endif /* __VS_DC_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
new file mode 100644
index 000000000..e5acb7eb4
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
@@ -0,0 +1,1959 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/bits.h>
+#include <linux/io.h>
+#include <linux/media-bus-format.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_fourcc.h>
+#include <drm/vs_drm.h>
+
+#include "vs_dc_hw.h"
+#include "vs_type.h"
+
+static const u32 horkernel[] = {
+	0x00000000, 0x20000000, 0x00002000, 0x00000000,
+	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
+	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
+	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
+	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
+	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
+	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
+	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
+	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
+	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
+	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
+	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
+	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
+	0x00000000, 0x00000000, 0x00000000, 0x01470000,
+	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
+	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
+	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
+	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00004000, 0x00000000,
+	0x00000000, 0x00000000, 0x20002000, 0x00000000,
+	0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
+	0x00000000, 0x00000000, 0x00000000, 0x27e1181f,
+	0x00000000, 0x00000000, 0x00000000, 0x14680000,
+	0x00002b98, 0x00000000, 0x00000000, 0x00000000,
+	0x2f1010f0, 0x00000000, 0x00000000, 0x00000000,
+	0x0dc70000, 0x00003239, 0x00000000, 0x00000000,
+	0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
+	0x00000000, 0x087f0000, 0x00003781, 0x00000000,
+	0x00000000, 0x00000000, 0x399a0666, 0x00000000,
+	0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
+	0x00000000, 0x00000000, 0x00000000, 0x3cc4033c,
+	0x00000000, 0x00000000, 0x00000000, 0x021f0000,
+};
+
+#define H_COEF_SIZE (sizeof(horkernel) / sizeof(u32))
+
+static const u32 verkernel[] = {
+	0x00000000, 0x20000000, 0x00002000, 0x00000000,
+	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
+	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
+	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
+	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
+	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
+	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
+	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
+	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
+	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
+	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
+	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
+	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
+	0x00000000, 0x00000000, 0x00000000, 0x01470000,
+	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
+	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
+	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
+	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00004000, 0x00000000,
+	0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab,
+	0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
+	0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+};
+
+#define V_COEF_SIZE (sizeof(verkernel) / sizeof(u32))
+
+/*
+ * RGB 709->2020 conversion parameters
+ */
+static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = {
+	10279,	5395,	709,
+	1132,	15065,	187,
+	269,	1442,	14674
+};
+
+/*
+ * YUV601 to RGB conversion parameters
+ * YUV2RGB[0]  - [8] : C0 - C8;
+ * YUV2RGB[9]  - [11]: D0 - D2;
+ * YUV2RGB[12] - [13]: Y clamp min & max calue;
+ * YUV2RGB[14] - [15]: UV clamp min & max calue;
+ */
+static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
+	1196,	0,			1640,	1196,
+	-404,	-836,		1196,	2076,
+	0,		-916224,	558336,	-1202944,
+	64,		 940,		64,		960
+};
+
+/*
+ * YUV709 to RGB conversion parameters
+ * YUV2RGB[0]  - [8] : C0 - C8;
+ * YUV2RGB[9]  - [11]: D0 - D2;
+ * YUV2RGB[12] - [13]: Y clamp min & max calue;
+ * YUV2RGB[14] - [15]: UV clamp min & max calue;
+ */
+static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
+	1196,		0,		1844,	1196,
+	-220,		-548,	1196,	2172,
+	0,			-1020672, 316672,  -1188608,
+	64,			940,		64,		960
+};
+
+/*
+ * YUV2020 to RGB conversion parameters
+ * YUV2RGB[0]  - [8] : C0 - C8;
+ * YUV2RGB[9]  - [11]: D0 - D2;
+ * YUV2RGB[12] - [13]: Y clamp min & max calue;
+ * YUV2RGB[14] - [15]: UV clamp min & max calue;
+ */
+static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
+	1196, 0, 1724, 1196,
+	-192, -668, 1196, 2200,
+	0, -959232, 363776, -1202944,
+	64, 940, 64, 960
+};
+
+/*
+ * RGB to YUV2020 conversion parameters
+ * RGB2YUV[0] - [8] : C0 - C8;
+ * RGB2YUV[9] - [11]: D0 - D2;
+ */
+static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = {
+	230,	594,	52,
+	-125,  -323,	448,
+	448,   -412,   -36,
+	64,		512,	512
+};
+
+/*
+ * Degamma table for 709 color space data.
+ */
+static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005,
+	0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024,
+	0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064,
+	0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce,
+	0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166,
+	0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230,
+	0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331,
+	0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c,
+	0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
+	0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d,
+	0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999,
+	0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb,
+	0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65,
+	0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a,
+	0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b,
+	0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc,
+	0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e,
+	0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
+	0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e,
+	0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf,
+	0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea,
+	0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f,
+	0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930,
+	0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f,
+	0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee,
+	0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde,
+	0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
+	0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9,
+	0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406,
+	0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a,
+	0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576,
+	0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd,
+	0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
+};
+
+/*
+ * Degamma table for 2020 color space data.
+ */
+static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
+	0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006,
+	0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a,
+	0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f,
+	0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
+	0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024,
+	0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035,
+	0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d,
+	0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d,
+	0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098,
+	0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3,
+	0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123,
+	0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d,
+	0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
+	0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6,
+	0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1,
+	0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d,
+	0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6,
+	0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920,
+	0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a,
+	0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033,
+	0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593,
+	0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
+	0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656,
+	0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332,
+	0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480,
+	0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1,
+	0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99,
+	0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
+};
+
+/* one is for primary plane and the other is for all overlay planes */
+static const struct dc_hw_plane_reg dc_plane_reg[] = {
+	{
+	.y_address		= DC_FRAMEBUFFER_ADDRESS,
+	.u_address		= DC_FRAMEBUFFER_U_ADDRESS,
+	.v_address		= DC_FRAMEBUFFER_V_ADDRESS,
+	.y_stride		= DC_FRAMEBUFFER_STRIDE,
+	.u_stride		= DC_FRAMEBUFFER_U_STRIDE,
+	.v_stride		= DC_FRAMEBUFFER_V_STRIDE,
+	.size			= DC_FRAMEBUFFER_SIZE,
+	.top_left		= DC_FRAMEBUFFER_TOP_LEFT,
+	.bottom_right	= DC_FRAMEBUFFER_BOTTOM_RIGHT,
+	.scale_factor_x			= DC_FRAMEBUFFER_SCALE_FACTOR_X,
+	.scale_factor_y			= DC_FRAMEBUFFER_SCALE_FACTOR_Y,
+	.h_filter_coef_index	= DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
+	.h_filter_coef_data		= DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
+	.v_filter_coef_index	= DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
+	.v_filter_coef_data		= DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
+	.init_offset			= DC_FRAMEBUFFER_INIT_OFFSET,
+	.color_key				= DC_FRAMEBUFFER_COLOR_KEY,
+	.color_key_high			= DC_FRAMEBUFFER_COLOR_KEY_HIGH,
+	.clear_value			= DC_FRAMEBUFFER_CLEAR_VALUE,
+	.color_table_index		= DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
+	.color_table_data		= DC_FRAMEBUFFER_COLOR_TABLE_DATA,
+	.scale_config			= DC_FRAMEBUFFER_SCALE_CONFIG,
+	.water_mark				= DC_FRAMEBUFFER_WATER_MARK,
+	.degamma_index			= DC_FRAMEBUFFER_DEGAMMA_INDEX,
+	.degamma_data			= DC_FRAMEBUFFER_DEGAMMA_DATA,
+	.degamma_ex_data		= DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
+	.src_global_color		= DC_FRAMEBUFFER_SRC_GLOBAL_COLOR,
+	.dst_global_color		= DC_FRAMEBUFFER_DST_GLOBAL_COLOR,
+	.blend_config			= DC_FRAMEBUFFER_BLEND_CONFIG,
+	.roi_origin				= DC_FRAMEBUFFER_ROI_ORIGIN,
+	.roi_size				= DC_FRAMEBUFFER_ROI_SIZE,
+	.yuv_to_rgb_coef0			= DC_FRAMEBUFFER_YUVTORGB_COEF0,
+	.yuv_to_rgb_coef1			= DC_FRAMEBUFFER_YUVTORGB_COEF1,
+	.yuv_to_rgb_coef2			= DC_FRAMEBUFFER_YUVTORGB_COEF2,
+	.yuv_to_rgb_coef3			= DC_FRAMEBUFFER_YUVTORGB_COEF3,
+	.yuv_to_rgb_coef4			= DC_FRAMEBUFFER_YUVTORGB_COEF4,
+	.yuv_to_rgb_coefd0			= DC_FRAMEBUFFER_YUVTORGB_COEFD0,
+	.yuv_to_rgb_coefd1			= DC_FRAMEBUFFER_YUVTORGB_COEFD1,
+	.yuv_to_rgb_coefd2			= DC_FRAMEBUFFER_YUVTORGB_COEFD2,
+	.y_clamp_bound				= DC_FRAMEBUFFER_Y_CLAMP_BOUND,
+	.uv_clamp_bound				= DC_FRAMEBUFFER_UV_CLAMP_BOUND,
+	.rgb_to_rgb_coef0			= DC_FRAMEBUFFER_RGBTORGB_COEF0,
+	.rgb_to_rgb_coef1			= DC_FRAMEBUFFER_RGBTORGB_COEF1,
+	.rgb_to_rgb_coef2			= DC_FRAMEBUFFER_RGBTORGB_COEF2,
+	.rgb_to_rgb_coef3			= DC_FRAMEBUFFER_RGBTORGB_COEF3,
+	.rgb_to_rgb_coef4			= DC_FRAMEBUFFER_RGBTORGB_COEF4,
+	},
+	{
+	.y_address		= DC_OVERLAY_ADDRESS,
+	.u_address		= DC_OVERLAY_U_ADDRESS,
+	.v_address		= DC_OVERLAY_V_ADDRESS,
+	.y_stride		= DC_OVERLAY_STRIDE,
+	.u_stride		= DC_OVERLAY_U_STRIDE,
+	.v_stride		= DC_OVERLAY_V_STRIDE,
+	.size			= DC_OVERLAY_SIZE,
+	.top_left		= DC_OVERLAY_TOP_LEFT,
+	.bottom_right	= DC_OVERLAY_BOTTOM_RIGHT,
+	.scale_factor_x	= DC_OVERLAY_SCALE_FACTOR_X,
+	.scale_factor_y	= DC_OVERLAY_SCALE_FACTOR_Y,
+	.h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
+	.h_filter_coef_data  = DC_OVERLAY_H_FILTER_COEF_DATA,
+	.v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
+	.v_filter_coef_data  = DC_OVERLAY_V_FILTER_COEF_DATA,
+	.init_offset		 = DC_OVERLAY_INIT_OFFSET,
+	.color_key			 = DC_OVERLAY_COLOR_KEY,
+	.color_key_high			= DC_OVERLAY_COLOR_KEY_HIGH,
+	.clear_value		 = DC_OVERLAY_CLEAR_VALUE,
+	.color_table_index	 = DC_OVERLAY_COLOR_TABLE_INDEX,
+	.color_table_data	 = DC_OVERLAY_COLOR_TABLE_DATA,
+	.scale_config		 = DC_OVERLAY_SCALE_CONFIG,
+	.water_mark				= DC_OVERLAY_WATER_MARK,
+	.degamma_index		 = DC_OVERLAY_DEGAMMA_INDEX,
+	.degamma_data		 = DC_OVERLAY_DEGAMMA_DATA,
+	.degamma_ex_data	 = DC_OVERLAY_DEGAMMA_EX_DATA,
+	.src_global_color	 = DC_OVERLAY_SRC_GLOBAL_COLOR,
+	.dst_global_color	 = DC_OVERLAY_DST_GLOBAL_COLOR,
+	.blend_config		 = DC_OVERLAY_BLEND_CONFIG,
+	.roi_origin				= DC_OVERLAY_ROI_ORIGIN,
+	.roi_size				= DC_OVERLAY_ROI_SIZE,
+	.yuv_to_rgb_coef0		 = DC_OVERLAY_YUVTORGB_COEF0,
+	.yuv_to_rgb_coef1		 = DC_OVERLAY_YUVTORGB_COEF1,
+	.yuv_to_rgb_coef2		 = DC_OVERLAY_YUVTORGB_COEF2,
+	.yuv_to_rgb_coef3		 = DC_OVERLAY_YUVTORGB_COEF3,
+	.yuv_to_rgb_coef4			= DC_OVERLAY_YUVTORGB_COEF4,
+	.yuv_to_rgb_coefd0			= DC_OVERLAY_YUVTORGB_COEFD0,
+	.yuv_to_rgb_coefd1			= DC_OVERLAY_YUVTORGB_COEFD1,
+	.yuv_to_rgb_coefd2			= DC_OVERLAY_YUVTORGB_COEFD2,
+	.y_clamp_bound		 = DC_OVERLAY_Y_CLAMP_BOUND,
+	.uv_clamp_bound		 = DC_OVERLAY_UV_CLAMP_BOUND,
+	.rgb_to_rgb_coef0		 = DC_OVERLAY_RGBTORGB_COEF0,
+	.rgb_to_rgb_coef1		 = DC_OVERLAY_RGBTORGB_COEF1,
+	.rgb_to_rgb_coef2		 = DC_OVERLAY_RGBTORGB_COEF2,
+	.rgb_to_rgb_coef3		 = DC_OVERLAY_RGBTORGB_COEF3,
+	.rgb_to_rgb_coef4		 = DC_OVERLAY_RGBTORGB_COEF4,
+	},
+};
+
+static const u32 primary_overlay_format0[] = {
+	DRM_FORMAT_XRGB4444,
+	DRM_FORMAT_XBGR4444,
+	DRM_FORMAT_RGBX4444,
+	DRM_FORMAT_BGRX4444,
+	DRM_FORMAT_ARGB4444,
+	DRM_FORMAT_ABGR4444,
+	DRM_FORMAT_RGBA4444,
+	DRM_FORMAT_BGRA4444,
+	DRM_FORMAT_XRGB1555,
+	DRM_FORMAT_XBGR1555,
+	DRM_FORMAT_RGBX5551,
+	DRM_FORMAT_BGRX5551,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_ABGR1555,
+	DRM_FORMAT_RGBA5551,
+	DRM_FORMAT_BGRA5551,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565,
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_RGBX8888,
+	DRM_FORMAT_BGRX8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_RGBA8888,
+	DRM_FORMAT_BGRA8888,
+	DRM_FORMAT_ARGB2101010,
+	DRM_FORMAT_ABGR2101010,
+	DRM_FORMAT_RGBA1010102,
+	DRM_FORMAT_BGRA1010102,
+	DRM_FORMAT_YUYV,
+	DRM_FORMAT_YVYU,
+	DRM_FORMAT_UYVY,
+	DRM_FORMAT_VYUY,
+	DRM_FORMAT_YVU420,
+	DRM_FORMAT_YUV420,
+	DRM_FORMAT_NV12,
+	DRM_FORMAT_NV21,
+	DRM_FORMAT_NV16,
+	DRM_FORMAT_NV61,
+	DRM_FORMAT_P010,
+};
+
+static const u32 primary_overlay_format1[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_RGBX8888,
+	DRM_FORMAT_BGRX8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_RGBA8888,
+	DRM_FORMAT_BGRA8888,
+	DRM_FORMAT_ARGB2101010,
+	DRM_FORMAT_ABGR2101010,
+	DRM_FORMAT_RGBA1010102,
+	DRM_FORMAT_BGRA1010102,
+	DRM_FORMAT_NV12,
+	DRM_FORMAT_NV21,
+	DRM_FORMAT_YUV444,
+};
+
+static const u32 cursor_formats[] = {
+	DRM_FORMAT_ARGB8888
+};
+
+static const u64 format_modifier0[] = {
+	DRM_FORMAT_MOD_LINEAR,
+	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8),
+	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8),
+	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X8),
+	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X4),
+	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4),
+	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8),
+	DRM_FORMAT_MOD_INVALID
+};
+
+static const u64 secondary_format_modifiers[] = {
+	DRM_FORMAT_MOD_LINEAR,
+	DRM_FORMAT_MOD_INVALID
+};
+
+#define FRAC_16_16(mult, div)	 (((mult) << 16) / (div))
+
+static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = {
+	{
+		/* DC_REV_0 */
+		{
+		.name			= "Primary",
+		.id				= PRIMARY_PLANE_0,
+		.type			= DRM_PLANE_TYPE_PRIMARY,
+		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
+		.formats		= primary_overlay_format0,
+		.num_modifiers	= ARRAY_SIZE(format_modifier0),
+		.modifiers		= format_modifier0,
+		.min_width		= 0,
+		.min_height		= 0,
+		.max_width		= 4096,
+		.max_height		= 4096,
+		.rotation		= DRM_MODE_ROTATE_0 |
+							DRM_MODE_ROTATE_90 |
+							DRM_MODE_ROTATE_180 |
+							DRM_MODE_ROTATE_270 |
+							DRM_MODE_REFLECT_X |
+							DRM_MODE_REFLECT_Y,
+		.blend_mode		= BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+							BIT(DRM_MODE_BLEND_PREMULTI) |
+							BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 0,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name			= "Overlay",
+		.id				= OVERLAY_PLANE_0,
+		.type			= DRM_PLANE_TYPE_OVERLAY,
+		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
+		.formats		= primary_overlay_format0,
+		.num_modifiers	= ARRAY_SIZE(format_modifier0),
+		.modifiers		= format_modifier0,
+		.min_width		= 0,
+		.min_height		= 0,
+		.max_width		= 4096,
+		.max_height		= 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 1,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name			= "Overlay_1",
+		.id				= OVERLAY_PLANE_1,
+		.type			= DRM_PLANE_TYPE_OVERLAY,
+		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
+		.formats		= primary_overlay_format0,
+		.num_modifiers	= ARRAY_SIZE(secondary_format_modifiers),
+		.modifiers		= secondary_format_modifiers,
+		.min_width		= 0,
+		.min_height		= 0,
+		.max_width		= 4096,
+		.max_height		= 4096,
+		.rotation		= 0,
+		.blend_mode		= BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+							BIT(DRM_MODE_BLEND_PREMULTI) |
+							BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 2,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Primary_1",
+		.id				= PRIMARY_PLANE_1,
+		.type		 = DRM_PLANE_TYPE_PRIMARY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format0),
+		.formats	 = primary_overlay_format0,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 3,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay_2",
+		.id				= OVERLAY_PLANE_2,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format0),
+		.formats	 = primary_overlay_format0,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 4,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay_3",
+		.id			= OVERLAY_PLANE_3,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format0),
+		.formats	 = primary_overlay_format0,
+		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
+		.modifiers	 = secondary_format_modifiers,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = 0,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 5,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Cursor",
+		.id				= CURSOR_PLANE_0,
+		.type		 = DRM_PLANE_TYPE_CURSOR,
+		.num_formats = ARRAY_SIZE(cursor_formats),
+		.formats	 = cursor_formats,
+		.num_modifiers = 0,
+		.modifiers	 = NULL,
+		.min_width	 = 32,
+		.min_height  = 32,
+		.max_width	 = 64,
+		.max_height  = 64,
+		.rotation	 = 0,
+		.degamma_size = 0,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 255,
+		.watermark	 = false,
+		.color_mgmt  = false,
+		.roi		 = false,
+		},
+		{
+		.name		 = "Cursor_1",
+		.id				= CURSOR_PLANE_1,
+		.type		 = DRM_PLANE_TYPE_CURSOR,
+		.num_formats = ARRAY_SIZE(cursor_formats),
+		.formats	 = cursor_formats,
+		.num_modifiers = 0,
+		.modifiers	 = NULL,
+		.min_width	 = 32,
+		.min_height  = 32,
+		.max_width	 = 64,
+		.max_height  = 64,
+		.rotation	 = 0,
+		.degamma_size = 0,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 255,
+		.watermark	 = false,
+		.color_mgmt  = false,
+		.roi		 = false,
+		},
+	},
+	{
+		/* DC_REV_1 */
+		{
+		.name		 = "Primary",
+		.id				= PRIMARY_PLANE_0,
+		.type		 = DRM_PLANE_TYPE_PRIMARY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format0),
+		.formats	 = primary_overlay_format0,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 0,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay",
+		.id				= OVERLAY_PLANE_0,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format0),
+		.formats	 = primary_overlay_format0,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 1,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Primary_1",
+		.id				= PRIMARY_PLANE_1,
+		.type		 = DRM_PLANE_TYPE_PRIMARY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format0),
+		.formats	 = primary_overlay_format0,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 2,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay_2",
+		.id				= OVERLAY_PLANE_2,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format0),
+		.formats	 = primary_overlay_format0,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 3,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Cursor",
+		.id				= CURSOR_PLANE_0,
+		.type		 = DRM_PLANE_TYPE_CURSOR,
+		.num_formats = ARRAY_SIZE(cursor_formats),
+		.formats	 = cursor_formats,
+		.num_modifiers = 0,
+		.modifiers	 = NULL,
+		.min_width	 = 32,
+		.min_height  = 32,
+		.max_width	 = 64,
+		.max_height  = 64,
+		.rotation	 = 0,
+		.degamma_size = 0,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 255,
+		.watermark	 = false,
+		.color_mgmt  = false,
+		.roi		 = false,
+		},
+		{
+		.name		 = "Cursor_1",
+		.id				= CURSOR_PLANE_1,
+		.type		 = DRM_PLANE_TYPE_CURSOR,
+		.num_formats = ARRAY_SIZE(cursor_formats),
+		.formats	 = cursor_formats,
+		.num_modifiers = 0,
+		.modifiers	 = NULL,
+		.min_width	 = 32,
+		.min_height  = 32,
+		.max_width	 = 64,
+		.max_height  = 64,
+		.rotation	 = 0,
+		.degamma_size = 0,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 255,
+		.watermark	 = false,
+		.color_mgmt  = false,
+		.roi		 = false,
+		},
+	},
+	{
+		/* DC_REV_2 */
+		{
+		.name		 = "Primary",
+		.id				= PRIMARY_PLANE_0,
+		.type		 = DRM_PLANE_TYPE_PRIMARY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format1),
+		.formats	 = primary_overlay_format1,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 0,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay",
+		.id				= OVERLAY_PLANE_0,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format1),
+		.formats	 = primary_overlay_format1,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 1,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay_1",
+		.id				= OVERLAY_PLANE_1,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format1),
+		.formats	 = primary_overlay_format1,
+		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
+		.modifiers	 = secondary_format_modifiers,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = 0,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 2,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Primary_1",
+		.id				= PRIMARY_PLANE_1,
+		.type		 = DRM_PLANE_TYPE_PRIMARY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format1),
+		.formats	 = primary_overlay_format1,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 3,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay_2",
+		.id				= OVERLAY_PLANE_2,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format1),
+		.formats	 = primary_overlay_format1,
+		.num_modifiers = ARRAY_SIZE(format_modifier0),
+		.modifiers	 = format_modifier0,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_90 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_ROTATE_270 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = FRAC_16_16(1, 3),
+		.max_scale	 = FRAC_16_16(10, 1),
+		.zpos		 = 4,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Overlay_3",
+		.id				= OVERLAY_PLANE_3,
+		.type		 = DRM_PLANE_TYPE_OVERLAY,
+		.num_formats = ARRAY_SIZE(primary_overlay_format1),
+		.formats	 = primary_overlay_format1,
+		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
+		.modifiers	 = secondary_format_modifiers,
+		.min_width	 = 0,
+		.min_height  = 0,
+		.max_width	 = 4096,
+		.max_height  = 4096,
+		.rotation	 = 0,
+		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+					   BIT(DRM_MODE_BLEND_PREMULTI) |
+					   BIT(DRM_MODE_BLEND_COVERAGE),
+		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+						  BIT(DRM_COLOR_YCBCR_BT2020),
+		.degamma_size	= DEGAMMA_SIZE,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 5,
+		.watermark	 = true,
+		.color_mgmt  = true,
+		.roi		 = true,
+		},
+		{
+		.name		 = "Cursor",
+		.id				= CURSOR_PLANE_0,
+		.type		 = DRM_PLANE_TYPE_CURSOR,
+		.num_formats = ARRAY_SIZE(cursor_formats),
+		.formats	 = cursor_formats,
+		.num_modifiers = 0,
+		.modifiers	 = NULL,
+		.min_width	 = 32,
+		.min_height  = 32,
+		.max_width	 = 64,
+		.max_height  = 64,
+		.rotation	 = 0,
+		.degamma_size = 0,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 255,
+		.watermark	 = false,
+		.color_mgmt  = false,
+		.roi		 = false,
+		},
+		{
+		.name		 = "Cursor_1",
+		.id				= CURSOR_PLANE_1,
+		.type		 = DRM_PLANE_TYPE_CURSOR,
+		.num_formats = ARRAY_SIZE(cursor_formats),
+		.formats	 = cursor_formats,
+		.num_modifiers = 0,
+		.modifiers	 = NULL,
+		.min_width	 = 32,
+		.min_height  = 32,
+		.max_width	 = 64,
+		.max_height  = 64,
+		.rotation	 = 0,
+		.degamma_size = 0,
+		.min_scale	 = DRM_PLANE_NO_SCALING,
+		.max_scale	 = DRM_PLANE_NO_SCALING,
+		.zpos		 = 255,
+		.watermark	 = false,
+		.color_mgmt  = false,
+		.roi		 = false,
+		},
+	},
+};
+
+static const struct vs_dc_info dc_info[] = {
+	{
+		/* DC_REV_0 */
+		.name			= "DC8200",
+		.panel_num		= 2,
+		.plane_num		= 8,
+		.planes			= dc_hw_planes[DC_REV_0],
+		.layer_num		= 6,
+		.max_bpc		= 10,
+		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
+						  DRM_COLOR_FORMAT_YCBCR444 |
+						  DRM_COLOR_FORMAT_YCBCR422 |
+						  DRM_COLOR_FORMAT_YCBCR420,
+		.gamma_size		= GAMMA_EX_SIZE,
+		.gamma_bits		= 12,
+		.pitch_alignment	= 128,
+		.pipe_sync		= false,
+		.background		= true,
+		.panel_sync		= true,
+		.cap_dec		= true,
+	},
+	{
+		/* DC_REV_1 */
+		.name			= "DC8200",
+		.panel_num		= 2,
+		.plane_num		= 6,
+		.planes			= dc_hw_planes[DC_REV_1],
+		.layer_num		= 4,
+		.max_bpc		= 10,
+		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
+						  DRM_COLOR_FORMAT_YCBCR444 |
+						  DRM_COLOR_FORMAT_YCBCR422 |
+						  DRM_COLOR_FORMAT_YCBCR420,
+		.gamma_size		= GAMMA_EX_SIZE,
+		.gamma_bits		= 12,
+		.pitch_alignment	= 128,
+		.pipe_sync		= false,
+		.background		= true,
+		.panel_sync		= true,
+		.cap_dec		= true,
+	},
+	{
+		/* DC_REV_2 */
+		.name			= "DC8200",
+		.panel_num		= 2,
+		.plane_num		= 8,
+		.planes			= dc_hw_planes[DC_REV_2],
+		.layer_num		= 6,
+		.max_bpc		= 10,
+		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
+						  DRM_COLOR_FORMAT_YCBCR444 |
+						  DRM_COLOR_FORMAT_YCBCR422 |
+						  DRM_COLOR_FORMAT_YCBCR420,
+		.gamma_size		= GAMMA_EX_SIZE,
+		.gamma_bits		= 12,
+		.pitch_alignment	= 128,
+		.pipe_sync		= false,
+		.background		= true,
+		.panel_sync		= true,
+		.cap_dec		= false,
+	},
+};
+
+static const struct dc_hw_funcs hw_func;
+
+static inline u32 hi_read(struct dc_hw *hw, u32 reg)
+{
+	return readl(hw->hi_base + reg);
+}
+
+static inline void hi_write(struct dc_hw *hw, u32 reg, u32 value)
+{
+	writel(value, hw->hi_base + reg);
+}
+
+static inline void dc_write(struct dc_hw *hw, u32 reg, u32 value)
+{
+	writel(value, hw->reg_base + reg - DC_REG_BASE);
+}
+
+static inline u32 dc_read(struct dc_hw *hw, u32 reg)
+{
+	u32 value = readl(hw->reg_base + reg - DC_REG_BASE);
+
+	return value;
+}
+
+static inline void dc_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear)
+{
+	u32 value = dc_read(hw, reg);
+
+	value &= ~clear;
+	value |= set;
+	dc_write(hw, reg, value);
+}
+
+static void load_default_filter(struct dc_hw *hw,
+				const struct dc_hw_plane_reg *reg, u32 offset)
+{
+	u8 i;
+
+	dc_write(hw, reg->scale_config + offset, 0x33);
+	dc_write(hw, reg->init_offset + offset, 0x80008000);
+	dc_write(hw, reg->h_filter_coef_index + offset, 0x00);
+	for (i = 0; i < H_COEF_SIZE; i++)
+		dc_write(hw, reg->h_filter_coef_data + offset, horkernel[i]);
+
+	dc_write(hw, reg->v_filter_coef_index + offset, 0x00);
+	for (i = 0; i < V_COEF_SIZE; i++)
+		dc_write(hw, reg->v_filter_coef_data + offset, verkernel[i]);
+}
+
+static void load_rgb_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
+			    u32 offset, u16 *table)
+{
+	dc_write(hw, reg->rgb_to_rgb_coef0 + offset, table[0] | (table[1] << 16));
+	dc_write(hw, reg->rgb_to_rgb_coef1 + offset, table[2] | (table[3] << 16));
+	dc_write(hw, reg->rgb_to_rgb_coef2 + offset, table[4] | (table[5] << 16));
+	dc_write(hw, reg->rgb_to_rgb_coef3 + offset, table[6] | (table[7] << 16));
+	dc_write(hw, reg->rgb_to_rgb_coef4 + offset, table[8]);
+}
+
+static void load_yuv_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
+			    u32 offset, s32 *table)
+{
+	dc_write(hw, reg->yuv_to_rgb_coef0 + offset,
+		 (0xFFFF & table[0]) | (table[1] << 16));
+	dc_write(hw, reg->yuv_to_rgb_coef1 + offset,
+		 (0xFFFF & table[2]) | (table[3] << 16));
+	dc_write(hw, reg->yuv_to_rgb_coef2 + offset,
+		 (0xFFFF & table[4]) | (table[5] << 16));
+	dc_write(hw, reg->yuv_to_rgb_coef3 + offset,
+		 (0xFFFF & table[6]) | (table[7] << 16));
+	dc_write(hw, reg->yuv_to_rgb_coef4 + offset, table[8]);
+	dc_write(hw, reg->yuv_to_rgb_coefd0 + offset, table[9]);
+	dc_write(hw, reg->yuv_to_rgb_coefd1 + offset, table[10]);
+	dc_write(hw, reg->yuv_to_rgb_coefd2 + offset, table[11]);
+	dc_write(hw, reg->y_clamp_bound + offset, table[12] | (table[13] << 16));
+	dc_write(hw, reg->uv_clamp_bound + offset, table[14] | (table[15] << 16));
+}
+
+static void load_rgb_to_yuv(struct dc_hw *hw, u32 offset, s16 *table)
+{
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF0 + offset,
+		 table[0] | (table[1] << 16));
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF1 + offset,
+		 table[2] | (table[3] << 16));
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF2 + offset,
+		 table[4] | (table[5] << 16));
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF3 + offset,
+		 table[6] | (table[7] << 16));
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF4 + offset, table[8]);
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD0 + offset, table[9]);
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD1 + offset, table[10]);
+	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD2 + offset, table[11]);
+}
+
+static bool is_rgb(enum dc_hw_color_format format)
+{
+	switch (format) {
+	case FORMAT_X4R4G4B4:
+	case FORMAT_A4R4G4B4:
+	case FORMAT_X1R5G5B5:
+	case FORMAT_A1R5G5B5:
+	case FORMAT_R5G6B5:
+	case FORMAT_X8R8G8B8:
+	case FORMAT_A8R8G8B8:
+	case FORMAT_A2R10G10B10:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static void load_degamma_table(struct dc_hw *hw,
+			       const struct dc_hw_plane_reg *reg,
+			       u32 offset, u16 *table)
+{
+	u16 i;
+	u32 value;
+
+	dc_write(hw, reg->degamma_index + offset, 0);
+
+	for (i = 0; i < DEGAMMA_SIZE; i++) {
+		value = table[i] | (table[i] << 16);
+		dc_write(hw, reg->degamma_data + offset, value);
+		dc_write(hw, reg->degamma_ex_data + offset, table[i]);
+	}
+}
+
+static u32 get_addr_offset(u32 id)
+{
+	u32 offset = 0;
+
+	switch (id) {
+	case PRIMARY_PLANE_1:
+	case OVERLAY_PLANE_1:
+		offset = 0x04;
+		break;
+	case OVERLAY_PLANE_2:
+		offset = 0x08;
+		break;
+	case OVERLAY_PLANE_3:
+		offset = 0x0C;
+		break;
+	default:
+		break;
+	}
+
+	return offset;
+}
+
+int dc_hw_init(struct dc_hw *hw)
+{
+	u8 i, id, panel_num, layer_num;
+	u32 offset;
+	u32 revision = hi_read(hw, DC_HW_REVISION);
+	u32 cid = hi_read(hw, DC_HW_CHIP_CID);
+	const struct dc_hw_plane_reg *reg;
+
+	switch (revision) {
+	case 0x5720:
+		hw->rev = DC_REV_0;
+		break;
+	case 0x5721:
+		switch (cid) {
+		case 0x30B:
+			hw->rev = DC_REV_1;
+			break;
+		case 0x310:
+			hw->rev = DC_REV_2;
+			break;
+		default:
+			hw->rev = DC_REV_0;
+			break;
+		}
+		break;
+	default:
+		return -ENXIO;
+	}
+
+	hw->info = (struct vs_dc_info *)&dc_info[hw->rev];
+	hw->func = (struct dc_hw_funcs *)&hw_func;
+
+	layer_num = hw->info->layer_num;
+	for (i = 0; i < layer_num; i++) {
+		id = hw->info->planes[i].id;
+		offset = get_addr_offset(id);
+		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
+			reg = &dc_plane_reg[0];
+		else
+			reg = &dc_plane_reg[1];
+
+		load_default_filter(hw, reg, offset);
+		load_rgb_to_rgb(hw, reg, offset, RGB2RGB);
+	}
+
+	panel_num = hw->info->panel_num;
+	for (i = 0; i < panel_num; i++) {
+		offset = i << 2;
+
+		load_rgb_to_yuv(hw, offset, RGB2YUV);
+		dc_write(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0x111);
+
+		offset = i ? DC_CURSOR_OFFSET : 0;
+		dc_write(hw, DC_CURSOR_BACKGROUND + offset, 0x00FFFFFF);
+		dc_write(hw, DC_CURSOR_FOREGROUND + offset, 0x00AAAAAA);
+	}
+
+	return 0;
+}
+
+void dc_hw_deinit(struct dc_hw *hw)
+{
+	/* Nothing to do */
+}
+
+void dc_hw_update_plane(struct dc_hw *hw, u8 id,
+			struct dc_hw_fb *fb, struct dc_hw_scale *scale,
+			struct dc_hw_position *pos, struct dc_hw_blend *blend)
+{
+	struct dc_hw_plane *plane = &hw->plane[id];
+
+	if (plane) {
+		if (fb) {
+			if (!fb->enable)
+				plane->fb.enable = false;
+			else
+				memcpy(&plane->fb, fb,
+				       sizeof(*fb) - sizeof(fb->dirty));
+			plane->fb.dirty = true;
+		}
+		if (scale) {
+			memcpy(&plane->scale, scale,
+			       sizeof(*scale) - sizeof(scale->dirty));
+			plane->scale.dirty = true;
+		}
+		if (pos) {
+			memcpy(&plane->pos, pos,
+			       sizeof(*pos) - sizeof(pos->dirty));
+			plane->pos.dirty = true;
+		}
+		if (blend) {
+			memcpy(&plane->blend, blend,
+			       sizeof(*blend) - sizeof(blend->dirty));
+			plane->blend.dirty = true;
+		}
+	}
+}
+
+void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode)
+{
+	struct dc_hw_plane *plane = &hw->plane[id];
+
+	if (plane) {
+		if (hw->info->planes[id].degamma_size) {
+			plane->degamma.mode = mode;
+			plane->degamma.dirty = true;
+		} else {
+			plane->degamma.dirty = false;
+		}
+	}
+}
+
+void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi)
+{
+	struct dc_hw_plane *plane = &hw->plane[id];
+
+	if (plane) {
+		memcpy(&plane->roi, roi, sizeof(*roi) - sizeof(roi->dirty));
+		plane->roi.dirty = true;
+	}
+}
+
+void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
+			   struct dc_hw_colorkey *colorkey)
+{
+	struct dc_hw_plane *plane = &hw->plane[id];
+
+	if (plane) {
+		memcpy(&plane->colorkey, colorkey,
+		       sizeof(*colorkey) - sizeof(colorkey->dirty));
+		plane->colorkey.dirty = true;
+	}
+}
+
+void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos)
+{
+	memcpy(&hw->qos, qos, sizeof(*qos) - sizeof(qos->dirty));
+	hw->qos.dirty = true;
+}
+
+void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor)
+{
+	memcpy(&hw->cursor[id], cursor, sizeof(*cursor) - sizeof(cursor->dirty));
+	hw->cursor[id].dirty = true;
+}
+
+void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
+			u16 r, u16 g, u16 b)
+{
+	if (index >= hw->info->gamma_size)
+		return;
+
+	hw->gamma[id].gamma[index][0] = r;
+	hw->gamma[id].gamma[index][1] = g;
+	hw->gamma[id].gamma[index][2] = b;
+	hw->gamma[id].dirty = true;
+}
+
+void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable)
+{
+	hw->gamma[id].enable = enable;
+	hw->gamma[id].dirty = true;
+}
+
+void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display)
+{
+	u8 id = display->id;
+
+	memcpy(&hw->display[id], display, sizeof(*display));
+
+	hw->func->display(hw, display);
+}
+
+void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable)
+{
+	if (enable)
+		hi_write(hw, AQ_INTR_ENBL, 0xFFFFFFFF);
+	else
+		hi_write(hw, AQ_INTR_ENBL, 0);
+}
+
+u32 dc_hw_get_interrupt(struct dc_hw *hw)
+{
+	return hi_read(hw, AQ_INTR_ACKNOWLEDGE);
+}
+
+bool dc_hw_check_underflow(struct dc_hw *hw)
+{
+	return dc_read(hw, DC_FRAMEBUFFER_CONFIG) & BIT(5);
+}
+
+void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable)
+{
+	u32 i, offset;
+	u8 id, layer_num = hw->info->layer_num;
+	u8 panel_num = hw->info->panel_num;
+
+	for (i = 0; i < layer_num; i++) {
+		id = hw->info->planes[i].id;
+		offset = get_addr_offset(id);
+		if (enable) {
+			if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
+				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, BIT(12), 0);
+			else
+				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, BIT(31), 0);
+		} else {
+			if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
+				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, 0, BIT(12));
+			else
+				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, 0, BIT(31));
+		}
+	}
+
+	for (i = 0; i < panel_num; i++) {
+		offset = i << 2;
+		if (enable)
+			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, BIT(0));
+		else
+			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, BIT(0), 0);
+	}
+}
+
+void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id)
+{
+	if (out <= OUT_DP)
+		hw->out[id] = out;
+}
+
+static void gamma_ex_commit(struct dc_hw *hw)
+{
+	u8 panel_num = hw->info->panel_num;
+	u16 i, j;
+	u32 value;
+
+	for (j = 0; j < panel_num; j++) {
+		if (hw->gamma[j].dirty) {
+			if (hw->gamma[j].enable) {
+				dc_write(hw, DC_DISPLAY_GAMMA_EX_INDEX + (j << 2), 0x00);
+				for (i = 0; i < GAMMA_EX_SIZE; i++) {
+					value = hw->gamma[j].gamma[i][2] |
+						(hw->gamma[j].gamma[i][1] << 12);
+					dc_write(hw, DC_DISPLAY_GAMMA_EX_DATA + (j << 2), value);
+					dc_write(hw, DC_DISPLAY_GAMMA_EX_ONE_DATA + (j << 2),
+						 hw->gamma[j].gamma[i][0]);
+				}
+				dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
+					     BIT(13), 0);
+			} else {
+				dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
+					     0, BIT(13));
+			}
+			hw->gamma[j].dirty = false;
+		}
+	}
+}
+
+static void plane_commit(struct dc_hw *hw)
+{
+	struct dc_hw_plane *plane;
+	const struct dc_hw_plane_reg *reg;
+	bool primary = false;
+	u8 id, layer_num = hw->info->layer_num;
+	u32 i, offset;
+
+	for (i = 0; i < layer_num; i++) {
+		plane = &hw->plane[i];
+		id = hw->info->planes[i].id;
+		offset = get_addr_offset(id);
+		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
+			reg = &dc_plane_reg[0];
+			primary = true;
+		} else {
+			reg = &dc_plane_reg[1];
+			primary = false;
+		}
+
+		if (plane->fb.dirty) {
+			if (plane->fb.enable) {
+				dc_write(hw, reg->y_address + offset,
+					 plane->fb.y_address);
+				dc_write(hw, reg->u_address + offset,
+					 plane->fb.u_address);
+				dc_write(hw, reg->v_address + offset,
+					 plane->fb.v_address);
+				dc_write(hw, reg->y_stride + offset,
+					 plane->fb.y_stride);
+				dc_write(hw, reg->u_stride + offset,
+					 plane->fb.u_stride);
+				dc_write(hw, reg->v_stride + offset,
+					 plane->fb.v_stride);
+				dc_write(hw, reg->size + offset,
+					 plane->fb.width |
+					 (plane->fb.height << 15));
+				dc_write(hw, reg->water_mark + offset,
+					 plane->fb.water_mark);
+
+				if (plane->fb.clear_enable)
+					dc_write(hw, reg->clear_value + offset,
+						 plane->fb.clear_value);
+			}
+
+			if (primary) {
+				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
+					     (plane->fb.format << 26) |
+					     (plane->fb.uv_swizzle << 25) |
+					     (plane->fb.swizzle << 23) |
+					     (plane->fb.tile_mode << 17) |
+					     (plane->fb.yuv_color_space << 14) |
+					     (plane->fb.rotation << 11) |
+					     (plane->fb.clear_enable << 8),
+					     (0x1F << 26) |
+					     BIT(25) |
+					     (0x03 << 23) |
+					     (0x1F << 17) |
+					     (0x07 << 14) |
+					     (0x07 << 11) |
+					     BIT(8));
+				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
+					     (plane->fb.dec_enable << 1) |
+					     (plane->fb.enable << 13) |
+					     (plane->fb.zpos << 16) |
+					     (plane->fb.display_id << 19),
+					     BIT(1) | BIT(13) | (0x07 << 16) | BIT(19));
+			} else {
+				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+					     (plane->fb.dec_enable << 27) |
+					     (plane->fb.clear_enable << 25) |
+					     (plane->fb.enable << 24) |
+					     (plane->fb.format << 16) |
+					     (plane->fb.uv_swizzle << 15) |
+					     (plane->fb.swizzle << 13) |
+					     (plane->fb.tile_mode << 8) |
+					     (plane->fb.yuv_color_space << 5) |
+					     (plane->fb.rotation << 2),
+					     BIT(27) |
+					     BIT(25) |
+					     BIT(24) |
+					     (0x1F << 16) |
+					     BIT(15) |
+					     (0x03 << 13) |
+					     (0x1F << 8) |
+					     (0x07 << 5) |
+					     (0x07 << 2));
+				dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset,
+					     plane->fb.zpos | (plane->fb.display_id << 3),
+					     0x07 | BIT(3));
+			}
+			plane->fb.dirty = false;
+		}
+
+		if (plane->scale.dirty) {
+			if (plane->scale.enable) {
+				dc_write(hw, reg->scale_factor_x + offset,
+					 plane->scale.scale_factor_x);
+				dc_write(hw, reg->scale_factor_y + offset,
+					 plane->scale.scale_factor_y);
+				if (primary)
+					dc_set_clear(hw,
+						     DC_FRAMEBUFFER_CONFIG + offset,
+						     BIT(22), 0);
+				else
+					dc_set_clear(hw,
+						     DC_OVERLAY_SCALE_CONFIG + offset,
+						     BIT(8), 0);
+			} else {
+				if (primary)
+					dc_set_clear(hw,
+						     DC_FRAMEBUFFER_CONFIG + offset,
+						     0, BIT(22));
+				else
+					dc_set_clear(hw,
+						     DC_OVERLAY_SCALE_CONFIG + offset,
+						     0, BIT(8));
+			}
+			plane->scale.dirty = false;
+		}
+
+		if (plane->pos.dirty) {
+			dc_write(hw, reg->top_left + offset,
+				 plane->pos.start_x |
+				 (plane->pos.start_y << 15));
+			dc_write(hw, reg->bottom_right + offset,
+				 plane->pos.end_x |
+				 (plane->pos.end_y << 15));
+			plane->pos.dirty = false;
+		}
+
+		if (plane->blend.dirty) {
+			dc_write(hw, reg->src_global_color + offset,
+				 plane->blend.alpha << 24);
+			dc_write(hw, reg->dst_global_color + offset,
+				 plane->blend.alpha << 24);
+			switch (plane->blend.blend_mode) {
+			case BLEND_PREMULTI:
+				dc_write(hw, reg->blend_config + offset, 0x3450);
+				break;
+			case BLEND_COVERAGE:
+				dc_write(hw, reg->blend_config + offset, 0x3950);
+				break;
+			case BLEND_PIXEL_NONE:
+				dc_write(hw, reg->blend_config + offset, 0x3548);
+				break;
+			default:
+				break;
+			}
+			plane->blend.dirty = false;
+		}
+
+		if (plane->colorkey.dirty) {
+			dc_write(hw, reg->color_key + offset, plane->colorkey.colorkey);
+			dc_write(hw, reg->color_key_high + offset,
+				 plane->colorkey.colorkey_high);
+
+			if (primary)
+				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
+					     plane->colorkey.transparency << 9, 0x03 << 9);
+			else
+				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+					     plane->colorkey.transparency, 0x03);
+
+			plane->colorkey.dirty = false;
+		}
+
+		if (plane->roi.dirty) {
+			if (plane->roi.enable) {
+				dc_write(hw, reg->roi_origin + offset,
+					 plane->roi.x | (plane->roi.y << 16));
+				dc_write(hw, reg->roi_size + offset,
+					 plane->roi.width | (plane->roi.height << 16));
+				if (primary)
+					dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
+						     BIT(0), 0);
+				else
+					dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+						     BIT(22), 0);
+			} else {
+				if (primary)
+					dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
+						     0, BIT(0));
+				else
+					dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+						     0, BIT(22));
+			}
+			plane->roi.dirty = false;
+		}
+	}
+}
+
+static void plane_ex_commit(struct dc_hw *hw)
+{
+	struct dc_hw_plane *plane;
+	const struct dc_hw_plane_reg *reg;
+	bool primary = false;
+	u8 id, layer_num = hw->info->layer_num;
+	u32 i, offset;
+
+	for (i = 0; i < layer_num; i++) {
+		plane = &hw->plane[i];
+		id = hw->info->planes[i].id;
+		offset = get_addr_offset(id);
+		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
+			reg = &dc_plane_reg[0];
+			primary = true;
+		} else {
+			reg = &dc_plane_reg[1];
+			primary = false;
+		}
+
+		if (plane->fb.dirty) {
+			if (is_rgb(plane->fb.format)) {
+				if (primary)
+					dc_set_clear(hw,
+						     DC_FRAMEBUFFER_CONFIG_EX + offset,
+						     BIT(6), BIT(8));
+				else
+					dc_set_clear(hw,
+						     DC_OVERLAY_CONFIG + offset,
+						     BIT(29), BIT(30));
+			} else {
+				if (primary)
+					dc_set_clear(hw,
+						     DC_FRAMEBUFFER_CONFIG_EX + offset,
+						     BIT(8), BIT(6));
+				else
+					dc_set_clear(hw,
+						     DC_OVERLAY_CONFIG + offset,
+						     BIT(30), BIT(29));
+				switch (plane->fb.yuv_color_space) {
+				case COLOR_SPACE_601:
+					load_yuv_to_rgb(hw, reg, offset, YUV601_2RGB);
+					break;
+				case COLOR_SPACE_709:
+					load_yuv_to_rgb(hw, reg, offset, YUV709_2RGB);
+					break;
+				case COLOR_SPACE_2020:
+					load_yuv_to_rgb(hw, reg, offset, YUV2020_2RGB);
+					break;
+				default:
+					break;
+				}
+			}
+		}
+		if (plane->degamma.dirty) {
+			switch (plane->degamma.mode) {
+			case VS_DEGAMMA_DISABLE:
+				if (primary)
+					dc_set_clear(hw,
+						     DC_FRAMEBUFFER_CONFIG_EX + offset,
+						     0, BIT(5));
+				else
+					dc_set_clear(hw,
+						     DC_OVERLAY_CONFIG + offset,
+						     0, BIT(28));
+				break;
+			case VS_DEGAMMA_BT709:
+				load_degamma_table(hw, reg, offset, DEGAMMA_709);
+				if (primary)
+					dc_set_clear(hw,
+						     DC_FRAMEBUFFER_CONFIG_EX + offset,
+						     BIT(5), 0);
+				else
+					dc_set_clear(hw,
+						     DC_OVERLAY_CONFIG + offset,
+						     BIT(28), 0);
+				break;
+			case VS_DEGAMMA_BT2020:
+				load_degamma_table(hw, reg, offset, DEGAMMA_2020);
+				if (primary)
+					dc_set_clear(hw,
+						     DC_FRAMEBUFFER_CONFIG_EX + offset,
+						     BIT(5), 0);
+				else
+					dc_set_clear(hw,
+						     DC_OVERLAY_CONFIG + offset,
+						     BIT(28), 0);
+				break;
+			default:
+				break;
+			}
+			plane->degamma.dirty = false;
+		}
+	}
+	plane_commit(hw);
+}
+
+static void setup_display(struct dc_hw *hw, struct dc_hw_display *display)
+{
+	u8 id = display->id;
+	u32 dpi_cfg, offset = id << 2;
+
+	if (hw->display[id].enable) {
+		switch (display->bus_format) {
+		case MEDIA_BUS_FMT_RGB565_1X16:
+			dpi_cfg = 0;
+			break;
+		case MEDIA_BUS_FMT_RGB666_1X18:
+			dpi_cfg = 3;
+			break;
+		case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+			dpi_cfg = 4;
+			break;
+		case MEDIA_BUS_FMT_RGB888_1X24:
+			dpi_cfg = 5;
+			break;
+		case MEDIA_BUS_FMT_RGB101010_1X30:
+			dpi_cfg = 6;
+			break;
+		default:
+			dpi_cfg = 5;
+			break;
+		}
+		dc_write(hw, DC_DISPLAY_DPI_CONFIG + offset, dpi_cfg);
+
+		if (id == 0)
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
+		else
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
+
+		dc_write(hw, DC_DISPLAY_H + offset, hw->display[id].h_active |
+			 (hw->display[id].h_total << 16));
+		dc_write(hw, DC_DISPLAY_H_SYNC + offset,
+			 hw->display[id].h_sync_start |
+			 (hw->display[id].h_sync_end << 15) |
+			 (hw->display[id].h_sync_polarity ? 0 : BIT(31)) |
+			 BIT(30));
+		dc_write(hw, DC_DISPLAY_V + offset, hw->display[id].v_active |
+				(hw->display[id].v_total << 16));
+		dc_write(hw, DC_DISPLAY_V_SYNC + offset,
+			 hw->display[id].v_sync_start |
+			 (hw->display[id].v_sync_end << 15) |
+			 (hw->display[id].v_sync_polarity ? 0 : BIT(31)) |
+			 BIT(30));
+
+		if (hw->info->pipe_sync)
+			dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
+				     0, BIT(3) | BIT(4));
+
+		dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(12), 0);
+		if (id == 0)
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(0), BIT(3));
+		else
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(1), BIT(3));
+	} else {
+		dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(12));
+		if (id == 0)
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
+		else
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
+	}
+}
+
+static void setup_display_ex(struct dc_hw *hw, struct dc_hw_display *display)
+{
+	u8 id = display->id;
+	u32 dp_cfg, offset = id << 2;
+	bool is_yuv = false;
+
+	if (hw->display[id].enable && hw->out[id] == OUT_DP) {
+		switch (display->bus_format) {
+		case MEDIA_BUS_FMT_RGB565_1X16:
+			dp_cfg = 0;
+			break;
+		case MEDIA_BUS_FMT_RGB666_1X18:
+			dp_cfg = 1;
+			break;
+		case MEDIA_BUS_FMT_RGB888_1X24:
+			dp_cfg = 2;
+			break;
+		case MEDIA_BUS_FMT_RGB101010_1X30:
+			dp_cfg = 3;
+			break;
+		case MEDIA_BUS_FMT_UYVY8_1X16:
+			dp_cfg = 2 << 4;
+			is_yuv = true;
+			break;
+		case MEDIA_BUS_FMT_YUV8_1X24:
+			dp_cfg = 4 << 4;
+			is_yuv = true;
+			break;
+		case MEDIA_BUS_FMT_UYVY10_1X20:
+			dp_cfg = 8 << 4;
+			is_yuv = true;
+			break;
+		case MEDIA_BUS_FMT_YUV10_1X30:
+			dp_cfg = 10 << 4;
+			is_yuv = true;
+			break;
+		case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+			dp_cfg = 12 << 4;
+			is_yuv = true;
+			break;
+		case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+			dp_cfg = 13 << 4;
+			is_yuv = true;
+			break;
+		default:
+			dp_cfg = 2;
+			break;
+		}
+		if (is_yuv)
+			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(16), 0);
+		else
+			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(16));
+		dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | BIT(3));
+	}
+
+	if (hw->out[id] == OUT_DPI)
+		dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, BIT(3));
+
+	setup_display(hw, display);
+}
+
+static const struct dc_hw_funcs hw_func = {
+	.gamma = &gamma_ex_commit,
+	.plane = &plane_ex_commit,
+	.display = setup_display_ex,
+};
+
+void dc_hw_commit(struct dc_hw *hw)
+{
+	u32 i, offset = 0;
+	u8 plane_num = hw->info->plane_num;
+	u8 layer_num = hw->info->layer_num;
+	u8 cursor_num = plane_num - layer_num;
+
+	hw->func->gamma(hw);
+	hw->func->plane(hw);
+
+	for (i = 0; i < cursor_num; i++) {
+		if (hw->cursor[i].dirty) {
+			offset = hw->cursor[i].display_id ? DC_CURSOR_OFFSET : 0;
+			if (hw->cursor[i].enable) {
+				dc_write(hw, DC_CURSOR_ADDRESS + offset,
+					 hw->cursor[i].address);
+				dc_write(hw, DC_CURSOR_LOCATION + offset, hw->cursor[i].x |
+					 (hw->cursor[i].y << 16));
+				dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
+					     (hw->cursor[i].hot_x << 16) |
+					     (hw->cursor[i].hot_y << 8) |
+					     (hw->cursor[i].size << 5) |
+					     BIT(3) | BIT(2) | 0x02,
+					     (0xFF << 16) |
+					     (0xFF << 8) |
+					     (0x07 << 5) | 0x1F);
+			} else {
+				dc_set_clear(hw, DC_CURSOR_CONFIG + offset, BIT(3), 0x03);
+			}
+			hw->cursor[i].dirty = false;
+		}
+	}
+
+	if (hw->qos.dirty) {
+		dc_set_clear(hw, DC_QOS_CONFIG, (hw->qos.high_value << 4) |
+					 hw->qos.low_value, 0xFF);
+		hw->qos.dirty = false;
+	}
+}
+
diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.h b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
new file mode 100644
index 000000000..9a1d767ae
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
@@ -0,0 +1,492 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_DC_HW_H__
+#define __VS_DC_HW_H__
+
+#define AQ_INTR_ACKNOWLEDGE				0x0010
+#define AQ_INTR_ENBL					0x0014
+#define DC_HW_REVISION					0x0024
+#define DC_HW_CHIP_CID					0x0030
+
+#define DC_REG_BASE						0x0800
+#define DC_REG_RANGE					0x2000
+#define DC_SEC_REG_OFFSET				0x100000
+
+#define DC_FRAMEBUFFER_CONFIG			0x1518
+#define DC_FRAMEBUFFER_CONFIG_EX		0x1CC0
+#define DC_FRAMEBUFFER_SCALE_CONFIG		0x1520
+#define DC_FRAMEBUFFER_TOP_LEFT			0x24D8
+#define DC_FRAMEBUFFER_BOTTOM_RIGHT		0x24E0
+#define DC_FRAMEBUFFER_ADDRESS			0x1400
+#define DC_FRAMEBUFFER_U_ADDRESS		0x1530
+#define DC_FRAMEBUFFER_V_ADDRESS		0x1538
+#define DC_FRAMEBUFFER_STRIDE			0x1408
+#define DC_FRAMEBUFFER_U_STRIDE			0x1800
+#define DC_FRAMEBUFFER_V_STRIDE			0x1808
+#define DC_FRAMEBUFFER_SIZE				0x1810
+#define DC_FRAMEBUFFER_SCALE_FACTOR_X		0x1828
+#define DC_FRAMEBUFFER_SCALE_FACTOR_Y		0x1830
+#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX	0x1838
+#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA	0x1A00
+#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX	0x1A08
+#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA	0x1A10
+#define DC_FRAMEBUFFER_INIT_OFFSET			0x1A20
+#define DC_FRAMEBUFFER_COLOR_KEY			0x1508
+#define DC_FRAMEBUFFER_COLOR_KEY_HIGH		0x1510
+#define DC_FRAMEBUFFER_CLEAR_VALUE			0x1A18
+#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX	0x1818
+#define DC_FRAMEBUFFER_COLOR_TABLE_DATA		0x1820
+#define DC_FRAMEBUFFER_BG_COLOR				0x1528
+#define DC_FRAMEBUFFER_ROI_ORIGIN			0x1CB0
+#define DC_FRAMEBUFFER_ROI_SIZE				0x1CB8
+#define DC_FRAMEBUFFER_WATER_MARK			0x1CE8
+#define DC_FRAMEBUFFER_DEGAMMA_INDEX		0x1D88
+#define DC_FRAMEBUFFER_DEGAMMA_DATA			0x1D90
+#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA		0x1D98
+#define DC_FRAMEBUFFER_YUVTORGB_COEF0		0x1DA0
+#define DC_FRAMEBUFFER_YUVTORGB_COEF1		0x1DA8
+#define DC_FRAMEBUFFER_YUVTORGB_COEF2		0x1DB0
+#define DC_FRAMEBUFFER_YUVTORGB_COEF3		0x1DB8
+#define DC_FRAMEBUFFER_YUVTORGB_COEF4		0x1E00
+#define DC_FRAMEBUFFER_YUVTORGB_COEFD0		0x1E08
+#define DC_FRAMEBUFFER_YUVTORGB_COEFD1		0x1E10
+#define DC_FRAMEBUFFER_YUVTORGB_COEFD2		0x1E18
+#define DC_FRAMEBUFFER_Y_CLAMP_BOUND		0x1E88
+#define DC_FRAMEBUFFER_UV_CLAMP_BOUND		0x1E90
+#define DC_FRAMEBUFFER_RGBTORGB_COEF0		0x1E20
+#define DC_FRAMEBUFFER_RGBTORGB_COEF1		0x1E28
+#define DC_FRAMEBUFFER_RGBTORGB_COEF2		0x1E30
+#define DC_FRAMEBUFFER_RGBTORGB_COEF3		0x1E38
+#define DC_FRAMEBUFFER_RGBTORGB_COEF4		0x1E40
+#define DC_FRAMEBUFFER_BLEND_CONFIG			0x2510
+#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR		0x2500
+#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR		0x2508
+
+#define DC_OVERLAY_CONFIG					0x1540
+#define DC_OVERLAY_CONFIG_EX				0x2540
+#define DC_OVERLAY_SCALE_CONFIG				0x1C00
+#define DC_OVERLAY_BLEND_CONFIG				0x1580
+#define DC_OVERLAY_TOP_LEFT					0x1640
+#define DC_OVERLAY_BOTTOM_RIGHT				0x1680
+#define DC_OVERLAY_ADDRESS					0x15C0
+#define DC_OVERLAY_U_ADDRESS				0x1840
+#define DC_OVERLAY_V_ADDRESS				0x1880
+#define DC_OVERLAY_STRIDE					0x1600
+#define DC_OVERLAY_U_STRIDE					0x18C0
+#define DC_OVERLAY_V_STRIDE					0x1900
+#define DC_OVERLAY_SIZE						0x17C0
+#define DC_OVERLAY_SCALE_FACTOR_X			0x1A40
+#define DC_OVERLAY_SCALE_FACTOR_Y			0x1A80
+#define DC_OVERLAY_H_FILTER_COEF_INDEX		0x1AC0
+#define DC_OVERLAY_H_FILTER_COEF_DATA		0x1B00
+#define DC_OVERLAY_V_FILTER_COEF_INDEX		0x1B40
+#define DC_OVERLAY_V_FILTER_COEF_DATA		0x1B80
+#define DC_OVERLAY_INIT_OFFSET				0x1BC0
+#define DC_OVERLAY_COLOR_KEY				0x1740
+#define DC_OVERLAY_COLOR_KEY_HIGH			0x1780
+#define DC_OVERLAY_CLEAR_VALUE				0x1940
+#define DC_OVERLAY_COLOR_TABLE_INDEX		0x1980
+#define DC_OVERLAY_COLOR_TABLE_DATA			0x19C0
+#define DC_OVERLAY_SRC_GLOBAL_COLOR			0x16C0
+#define DC_OVERLAY_DST_GLOBAL_COLOR			0x1700
+#define DC_OVERLAY_ROI_ORIGIN				0x1D00
+#define DC_OVERLAY_ROI_SIZE					0x1D40
+#define DC_OVERLAY_WATER_MARK				0x1DC0
+#define DC_OVERLAY_DEGAMMA_INDEX			0x2200
+#define DC_OVERLAY_DEGAMMA_DATA				0x2240
+#define DC_OVERLAY_DEGAMMA_EX_DATA			0x2280
+#define DC_OVERLAY_YUVTORGB_COEF0			0x1EC0
+#define DC_OVERLAY_YUVTORGB_COEF1			0x1F00
+#define DC_OVERLAY_YUVTORGB_COEF2			0x1F40
+#define DC_OVERLAY_YUVTORGB_COEF3			0x1F80
+#define DC_OVERLAY_YUVTORGB_COEF4			0x1FC0
+#define DC_OVERLAY_YUVTORGB_COEFD0			0x2000
+#define DC_OVERLAY_YUVTORGB_COEFD1			0x2040
+#define DC_OVERLAY_YUVTORGB_COEFD2			0x2080
+#define DC_OVERLAY_Y_CLAMP_BOUND			0x22C0
+#define DC_OVERLAY_UV_CLAMP_BOUND			0x2300
+#define DC_OVERLAY_RGBTORGB_COEF0			0x20C0
+#define DC_OVERLAY_RGBTORGB_COEF1			0x2100
+#define DC_OVERLAY_RGBTORGB_COEF2			0x2140
+#define DC_OVERLAY_RGBTORGB_COEF3			0x2180
+#define DC_OVERLAY_RGBTORGB_COEF4			0x21C0
+
+#define DC_CURSOR_CONFIG					0x1468
+#define DC_CURSOR_ADDRESS					0x146C
+#define DC_CURSOR_LOCATION					0x1470
+#define DC_CURSOR_BACKGROUND				0x1474
+#define DC_CURSOR_FOREGROUND				0x1478
+#define DC_CURSOR_CLK_GATING				0x1484
+#define DC_CURSOR_CONFIG_EX					0x24E8
+#define DC_CURSOR_OFFSET					0x1080
+
+#define DC_DISPLAY_DITHER_CONFIG		0x1410
+#define DC_DISPLAY_PANEL_CONFIG			0x1418
+#define DC_DISPLAY_PANEL_CONFIG_EX		0x2518
+#define DC_DISPLAY_DITHER_TABLE_LOW		0x1420
+#define DC_DISPLAY_DITHER_TABLE_HIGH	0x1428
+#define DC_DISPLAY_H					0x1430
+#define DC_DISPLAY_H_SYNC				0x1438
+#define DC_DISPLAY_V					0x1440
+#define DC_DISPLAY_V_SYNC				0x1448
+#define DC_DISPLAY_CURRENT_LOCATION		0x1450
+#define DC_DISPLAY_GAMMA_INDEX			0x1458
+#define DC_DISPLAY_GAMMA_DATA			0x1460
+#define DC_DISPLAY_INT					0x147C
+#define DC_DISPLAY_INT_ENABLE			0x1480
+#define DC_DISPLAY_DBI_CONFIG			0x1488
+#define DC_DISPLAY_GENERAL_CONFIG		0x14B0
+#define DC_DISPLAY_DPI_CONFIG			0x14B8
+#define DC_DISPLAY_PANEL_START			0x1CCC
+#define DC_DISPLAY_DEBUG_COUNTER_SELECT		0x14D0
+#define DC_DISPLAY_DEBUG_COUNTER_VALUE		0x14D8
+#define DC_DISPLAY_DP_CONFIG				0x1CD0
+#define DC_DISPLAY_GAMMA_EX_INDEX			0x1CF0
+#define DC_DISPLAY_GAMMA_EX_DATA			0x1CF8
+#define DC_DISPLAY_GAMMA_EX_ONE_DATA		0x1D80
+#define DC_DISPLAY_RGBTOYUV_COEF0			0x1E48
+#define DC_DISPLAY_RGBTOYUV_COEF1			0x1E50
+#define DC_DISPLAY_RGBTOYUV_COEF2			0x1E58
+#define DC_DISPLAY_RGBTOYUV_COEF3			0x1E60
+#define DC_DISPLAY_RGBTOYUV_COEF4			0x1E68
+#define DC_DISPLAY_RGBTOYUV_COEFD0			0x1E70
+#define DC_DISPLAY_RGBTOYUV_COEFD1			0x1E78
+#define DC_DISPLAY_RGBTOYUV_COEFD2			0x1E80
+
+#define DC_CLK_GATTING					0x1A28
+#define DC_QOS_CONFIG					0x1A38
+
+#define DC_TRANSPARENCY_OPAQUE			0x00
+#define DC_TRANSPARENCY_KEY				0x02
+#define DC_DISPLAY_DITHERTABLE_LOW		0x7B48F3C0
+#define DC_DISPLAY_DITHERTABLE_HIGH		0x596AD1E2
+
+#define GAMMA_SIZE						256
+#define GAMMA_EX_SIZE					300
+#define DEGAMMA_SIZE					260
+
+#define RGB_TO_RGB_TABLE_SIZE			9
+#define YUV_TO_RGB_TABLE_SIZE			16
+#define RGB_TO_YUV_TABLE_SIZE			12
+
+#define DC_LAYER_NUM	6
+#define DC_DISPLAY_NUM	2
+#define DC_CURSOR_NUM	2
+
+#define DC_TILE_MODE4X4	0x15
+
+enum dc_chip_rev {
+	DC_REV_0,/* For HW_REV_5720,HW_REV_5721_311 */
+	DC_REV_1,/* For HW_REV_5721_30B */
+	DC_REV_2,/* For HW_REV_5721_310 */
+};
+
+enum dc_hw_plane_id {
+	PRIMARY_PLANE_0,
+	OVERLAY_PLANE_0,
+	OVERLAY_PLANE_1,
+	PRIMARY_PLANE_1,
+	OVERLAY_PLANE_2,
+	OVERLAY_PLANE_3,
+	CURSOR_PLANE_0,
+	CURSOR_PLANE_1,
+	PLANE_NUM
+};
+
+enum dc_hw_color_format {
+	FORMAT_X4R4G4B4,//0
+	FORMAT_A4R4G4B4,//1
+	FORMAT_X1R5G5B5,//2
+	FORMAT_A1R5G5B5,//3
+	FORMAT_R5G6B5,//4
+	FORMAT_X8R8G8B8,//5
+	FORMAT_A8R8G8B8,//6
+	FORMAT_YUY2,//7
+	FORMAT_UYVY,//8
+	FORMAT_INDEX8,//9
+	FORMAT_MONOCHROME,//10
+	FORMAT_YV12 = 0xf,
+	FORMAT_A8,//16
+	FORMAT_NV12,//17
+	FORMAT_NV16,//18
+	FORMAT_RG16,//19
+	FORMAT_R8,//20
+	FORMAT_NV12_10BIT,//21
+	FORMAT_A2R10G10B10,//22
+	FORMAT_NV16_10BIT,//23
+	FORMAT_INDEX1,//24
+	FORMAT_INDEX2,//25
+	FORMAT_INDEX4,//26
+	FORMAT_P010,//27
+	FORMAT_YUV444,//28
+	FORMAT_YUV444_10BIT,//29
+};
+
+enum dc_hw_yuv_color_space {
+	COLOR_SPACE_601 = 0,
+	COLOR_SPACE_709 = 1,
+	COLOR_SPACE_2020 = 3,
+};
+
+enum dc_hw_rotation {
+	ROT_0 = 0,
+	ROT_90 = 4,
+	ROT_180 = 5,
+	ROT_270 = 6,
+	FLIP_X = 1,
+	FLIP_Y = 2,
+	FLIP_XY = 3,
+};
+
+enum dc_hw_swizzle {
+	SWIZZLE_ARGB = 0,
+	SWIZZLE_RGBA,
+	SWIZZLE_ABGR,
+	SWIZZLE_BGRA,
+};
+
+enum dc_hw_out {
+	OUT_DPI,
+	OUT_DP,
+};
+
+enum dc_hw_cursor_size {
+	CURSOR_SIZE_32X32 = 0,
+	CURSOR_SIZE_64X64,
+};
+
+enum dc_hw_blend_mode {
+	/* out.rgb = plane_alpha * fg.rgb +
+	 *		(1 - (plane_alpha * fg.alpha)) * bg.rgb
+	 */
+	BLEND_PREMULTI,
+	/* out.rgb = plane_alpha * fg.alpha * fg.rgb +
+	 *		(1 - (plane_alpha * fg.alpha)) * bg.rgb
+	 */
+	BLEND_COVERAGE,
+	/* out.rgb = plane_alpha * fg.rgb +
+	 *		(1 - plane_alpha) * bg.rgb
+	 */
+	BLEND_PIXEL_NONE,
+};
+
+struct dc_hw_plane_reg {
+	u32 y_address;
+	u32 u_address;
+	u32 v_address;
+	u32 y_stride;
+	u32 u_stride;
+	u32 v_stride;
+	u32 size;
+	u32 top_left;
+	u32 bottom_right;
+	u32 scale_factor_x;
+	u32 scale_factor_y;
+	u32 h_filter_coef_index;
+	u32 h_filter_coef_data;
+	u32 v_filter_coef_index;
+	u32 v_filter_coef_data;
+	u32 init_offset;
+	u32 color_key;
+	u32 color_key_high;
+	u32 clear_value;
+	u32 color_table_index;
+	u32 color_table_data;
+	u32 scale_config;
+	u32 water_mark;
+	u32 degamma_index;
+	u32 degamma_data;
+	u32 degamma_ex_data;
+	u32 src_global_color;
+	u32 dst_global_color;
+	u32 blend_config;
+	u32 roi_origin;
+	u32 roi_size;
+	u32 yuv_to_rgb_coef0;
+	u32 yuv_to_rgb_coef1;
+	u32 yuv_to_rgb_coef2;
+	u32 yuv_to_rgb_coef3;
+	u32 yuv_to_rgb_coef4;
+	u32 yuv_to_rgb_coefd0;
+	u32 yuv_to_rgb_coefd1;
+	u32 yuv_to_rgb_coefd2;
+	u32 y_clamp_bound;
+	u32 uv_clamp_bound;
+	u32 rgb_to_rgb_coef0;
+	u32 rgb_to_rgb_coef1;
+	u32 rgb_to_rgb_coef2;
+	u32 rgb_to_rgb_coef3;
+	u32 rgb_to_rgb_coef4;
+};
+
+struct dc_hw_fb {
+	u32 y_address;
+	u32 u_address;
+	u32 v_address;
+	u32 clear_value;
+	u32 water_mark;
+	u16 y_stride;
+	u16 u_stride;
+	u16 v_stride;
+	u16 width;
+	u16 height;
+	u8	format;
+	u8	tile_mode;
+	u8	rotation;
+	u8	yuv_color_space;
+	u8	swizzle;
+	u8	uv_swizzle;
+	u8	zpos;
+	u8	display_id;
+	bool	clear_enable;
+	bool	dec_enable;
+	bool	enable;
+	bool	dirty;
+};
+
+struct dc_hw_scale {
+	u32 scale_factor_x;
+	u32 scale_factor_y;
+	bool	enable;
+	bool	dirty;
+};
+
+struct dc_hw_position {
+	u16 start_x;
+	u16 start_y;
+	u16 end_x;
+	u16 end_y;
+	bool	dirty;
+};
+
+struct dc_hw_blend {
+	u8	alpha;
+	u8	blend_mode;
+	bool	dirty;
+};
+
+struct dc_hw_colorkey {
+	u32 colorkey;
+	u32 colorkey_high;
+	u8	transparency;
+	bool dirty;
+};
+
+struct dc_hw_roi {
+	u16 x;
+	u16 y;
+	u16 width;
+	u16 height;
+	bool enable;
+	bool dirty;
+};
+
+struct dc_hw_cursor {
+	u32 address;
+	u16 x;
+	u16 y;
+	u16 hot_x;
+	u16 hot_y;
+	u8	size;
+	u8	display_id;
+	bool	enable;
+	bool	dirty;
+};
+
+struct dc_hw_display {
+	u32 bus_format;
+	u16 h_active;
+	u16 h_total;
+	u16 h_sync_start;
+	u16 h_sync_end;
+	u16 v_active;
+	u16 v_total;
+	u16 v_sync_start;
+	u16 v_sync_end;
+	u8	id;
+	bool	h_sync_polarity;
+	bool	v_sync_polarity;
+	bool	enable;
+};
+
+struct dc_hw_gamma {
+	u16 gamma[GAMMA_EX_SIZE][3];
+	bool	enable;
+	bool	dirty;
+};
+
+struct dc_hw_degamma {
+	u16 degamma[DEGAMMA_SIZE][3];
+	u32 mode;
+	bool	dirty;
+};
+
+struct dc_hw_plane {
+	struct dc_hw_fb			fb;
+	struct dc_hw_position	pos;
+	struct dc_hw_scale		scale;
+	struct dc_hw_blend		blend;
+	struct dc_hw_roi		roi;
+	struct dc_hw_colorkey	colorkey;
+	struct dc_hw_degamma	degamma;
+};
+
+struct dc_hw_qos {
+	u8	  low_value;
+	u8	  high_value;
+	bool  dirty;
+};
+
+struct dc_hw_read {
+	u32			reg;
+	u32			value;
+};
+
+struct dc_hw;
+struct dc_hw_funcs {
+	void (*gamma)(struct dc_hw *hw);
+	void (*plane)(struct dc_hw *hw);
+	void (*display)(struct dc_hw *hw, struct dc_hw_display *display);
+};
+
+struct dc_hw {
+	enum dc_chip_rev rev;
+	enum dc_hw_out		out[DC_DISPLAY_NUM];
+	void			*hi_base;
+	void			*reg_base;
+
+	struct dc_hw_display display[DC_DISPLAY_NUM];
+	struct dc_hw_gamma	 gamma[DC_DISPLAY_NUM];
+	struct dc_hw_plane	 plane[DC_LAYER_NUM];
+	struct dc_hw_cursor  cursor[DC_CURSOR_NUM];
+	struct dc_hw_qos	 qos;
+	struct dc_hw_funcs	 *func;
+	struct vs_dc_info	 *info;
+};
+
+int dc_hw_init(struct dc_hw *hw);
+void dc_hw_deinit(struct dc_hw *hw);
+void dc_hw_update_plane(struct dc_hw *hw, u8 id,
+			struct dc_hw_fb *fb, struct dc_hw_scale *scale,
+			struct dc_hw_position *pos, struct dc_hw_blend *blend);
+void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode);
+void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi);
+void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
+			   struct dc_hw_colorkey *colorkey);
+void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos);
+void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor);
+void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
+			u16 r, u16 g, u16 b);
+void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable);
+void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display);
+void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable);
+u32 dc_hw_get_interrupt(struct dc_hw *hw);
+bool dc_hw_check_underflow(struct dc_hw *hw);
+void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable);
+void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id);
+void dc_hw_commit(struct dc_hw *hw);
+
+#endif /* __VS_DC_HW_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
index da7698c3d..3cd533cfa 100644
--- a/drivers/gpu/drm/verisilicon/vs_drv.c
+++ b/drivers/gpu/drm/verisilicon/vs_drv.c
@@ -21,6 +21,7 @@
 
 #include "vs_drv.h"
 #include "vs_modeset.h"
+#include "vs_dc.h"
 
 #define DRV_NAME	"verisilicon"
 #define DRV_DESC	"Verisilicon DRM driver"
@@ -123,6 +124,7 @@ static const struct component_master_ops vs_drm_ops = {
 };
 
 static struct platform_driver *drm_sub_drivers[] = {
+	&dc_platform_driver,
 
 };
 
diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c
new file mode 100644
index 000000000..9bd066015
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_plane.c
@@ -0,0 +1,526 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_fb_dma_helper.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_plane_helper.h>
+
+#include <drm/vs_drm.h>
+
+#include "vs_plane.h"
+#include "vs_drv.h"
+#include "vs_dc.h"
+
+static void vs_plane_reset(struct drm_plane *plane)
+{
+	struct vs_plane_state *state;
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+
+	if (plane->state) {
+		__drm_atomic_helper_plane_destroy_state(plane->state);
+
+		state = to_vs_plane_state(plane->state);
+		kfree(state);
+		plane->state = NULL;
+	}
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return;
+
+	state->degamma = VS_DEGAMMA_DISABLE;
+	state->degamma_changed = false;
+	state->base.zpos = vs_plane->id;
+	memset(&state->status, 0, sizeof(state->status));
+
+	__drm_atomic_helper_plane_reset(plane, &state->base);
+}
+
+static void _vs_plane_duplicate_blob(struct vs_plane_state *state,
+				     struct vs_plane_state *ori_state)
+{
+	state->watermark = ori_state->watermark;
+	state->color_mgmt = ori_state->color_mgmt;
+	state->roi = ori_state->roi;
+
+	if (state->watermark)
+		drm_property_blob_get(state->watermark);
+	if (state->color_mgmt)
+		drm_property_blob_get(state->color_mgmt);
+	if (state->roi)
+		drm_property_blob_get(state->roi);
+}
+
+static int
+_vs_plane_set_property_blob_from_id(struct drm_device *dev,
+				    struct drm_property_blob **blob,
+				    u64 blob_id,
+				    size_t expected_size)
+{
+	struct drm_property_blob *new_blob = NULL;
+
+	if (blob_id) {
+		new_blob = drm_property_lookup_blob(dev, blob_id);
+		if (!new_blob)
+			return -EINVAL;
+
+		if (new_blob->length != expected_size) {
+			drm_property_blob_put(new_blob);
+			return -EINVAL;
+		}
+	}
+
+	drm_property_replace_blob(blob, new_blob);
+	drm_property_blob_put(new_blob);
+
+	return 0;
+}
+
+static struct drm_plane_state *
+vs_plane_atomic_duplicate_state(struct drm_plane *plane)
+{
+	struct vs_plane_state *ori_state;
+	struct vs_plane_state *state;
+
+	if (WARN_ON(!plane->state))
+		return NULL;
+
+	ori_state = to_vs_plane_state(plane->state);
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return NULL;
+
+	__drm_atomic_helper_plane_duplicate_state(plane, &state->base);
+
+	state->degamma = ori_state->degamma;
+	state->degamma_changed = ori_state->degamma_changed;
+
+	_vs_plane_duplicate_blob(state, ori_state);
+	memcpy(&state->status, &ori_state->status, sizeof(ori_state->status));
+
+	return &state->base;
+}
+
+static void vs_plane_atomic_destroy_state(struct drm_plane *plane,
+					  struct drm_plane_state *state)
+{
+	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
+
+	__drm_atomic_helper_plane_destroy_state(state);
+
+	drm_property_blob_put(vs_plane_state->watermark);
+	drm_property_blob_put(vs_plane_state->color_mgmt);
+	drm_property_blob_put(vs_plane_state->roi);
+	kfree(vs_plane_state);
+}
+
+static int vs_plane_atomic_set_property(struct drm_plane *plane,
+					struct drm_plane_state *state,
+					struct drm_property *property,
+					uint64_t val)
+{
+	struct drm_device *dev = plane->dev;
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
+	int ret = 0;
+
+	if (property == vs_plane->degamma_mode) {
+		if (vs_plane_state->degamma != val) {
+			vs_plane_state->degamma = val;
+			vs_plane_state->degamma_changed = true;
+		} else {
+			vs_plane_state->degamma_changed = false;
+		}
+	} else if (property == vs_plane->watermark_prop) {
+		ret = _vs_plane_set_property_blob_from_id(dev,
+							  &vs_plane_state->watermark,
+							  val,
+							  sizeof(struct drm_vs_watermark));
+		return ret;
+	} else if (property == vs_plane->color_mgmt_prop) {
+		ret = _vs_plane_set_property_blob_from_id(dev,
+							  &vs_plane_state->color_mgmt,
+							  val,
+							  sizeof(struct drm_vs_color_mgmt));
+		return ret;
+	} else if (property == vs_plane->roi_prop) {
+		ret = _vs_plane_set_property_blob_from_id(dev,
+							  &vs_plane_state->roi,
+							  val,
+							  sizeof(struct drm_vs_roi));
+		return ret;
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int vs_plane_atomic_get_property(struct drm_plane *plane,
+					const struct drm_plane_state *state,
+					struct drm_property *property,
+					uint64_t *val)
+{
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+	const struct vs_plane_state *vs_plane_state =
+		container_of(state, const struct vs_plane_state, base);
+
+	if (property == vs_plane->degamma_mode)
+		*val = vs_plane_state->degamma;
+	else if (property == vs_plane->watermark_prop)
+		*val = (vs_plane_state->watermark) ?
+					vs_plane_state->watermark->base.id : 0;
+	else if (property == vs_plane->color_mgmt_prop)
+		*val = (vs_plane_state->color_mgmt) ?
+					vs_plane_state->color_mgmt->base.id : 0;
+	else if (property == vs_plane->roi_prop)
+		*val = (vs_plane_state->roi) ?
+					vs_plane_state->roi->base.id : 0;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static bool vs_format_mod_supported(struct drm_plane *plane,
+				    u32 format,
+				    u64 modifier)
+{
+	int i;
+
+	/* We always have to allow these modifiers:
+	 * 1. Core DRM checks for LINEAR support if userspace does not provide modifiers.
+	 * 2. Not passing any modifiers is the same as explicitly passing INVALID.
+	 */
+	if (modifier == DRM_FORMAT_MOD_LINEAR)
+		return true;
+
+	/* Check that the modifier is on the list of the plane's supported modifiers. */
+	for (i = 0; i < plane->modifier_count; i++) {
+		if (modifier == plane->modifiers[i])
+			break;
+	}
+
+	if (i == plane->modifier_count)
+		return false;
+
+	return true;
+}
+
+const struct drm_plane_funcs vs_plane_funcs = {
+	.update_plane		= drm_atomic_helper_update_plane,
+	.disable_plane		= drm_atomic_helper_disable_plane,
+	.reset			= vs_plane_reset,
+	.atomic_duplicate_state = vs_plane_atomic_duplicate_state,
+	.atomic_destroy_state	= vs_plane_atomic_destroy_state,
+	.atomic_set_property	= vs_plane_atomic_set_property,
+	.atomic_get_property	= vs_plane_atomic_get_property,
+	.format_mod_supported	= vs_format_mod_supported,
+};
+
+static unsigned char vs_get_plane_number(struct drm_framebuffer *fb)
+{
+	const struct drm_format_info *info;
+
+	if (!fb)
+		return 0;
+
+	info = drm_format_info(fb->format->format);
+	if (!info || info->num_planes > DRM_FORMAT_MAX_PLANES)
+		return 0;
+
+	return info->num_planes;
+}
+
+static int vs_plane_atomic_check(struct drm_plane *plane,
+				 struct drm_atomic_state *state)
+{
+	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+										 plane);
+	unsigned char i, num_planes;
+	struct drm_framebuffer *fb = new_plane_state->fb;
+	struct drm_crtc *crtc = new_plane_state->crtc;
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+	struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
+
+	if (!crtc || !fb)
+		return 0;
+
+	num_planes = vs_get_plane_number(fb);
+
+	for (i = 0; i < num_planes; i++) {
+		dma_addr_t dma_addr;
+
+		dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
+		plane_state->dma_addr[i] = dma_addr;
+	}
+
+	return vs_dc_check_plane(dc, plane, state);
+}
+
+static int vs_cursor_plane_atomic_check(struct drm_plane *plane,
+					struct drm_atomic_state *state)
+{
+	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+									  plane);
+	unsigned char i, num_planes;
+	struct drm_framebuffer *fb = new_plane_state->fb;
+	struct drm_crtc *crtc = new_plane_state->crtc;
+	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+	struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
+
+	if (!crtc || !fb)
+		return 0;
+
+	num_planes = vs_get_plane_number(fb);
+
+	for (i = 0; i < num_planes; i++) {
+		dma_addr_t dma_addr;
+
+		dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
+		plane_state->dma_addr[i] = dma_addr;
+	}
+
+	return vs_dc_check_cursor_plane(dc, plane, state);
+}
+
+static void vs_plane_atomic_update(struct drm_plane *plane,
+				   struct drm_atomic_state *state)
+{
+	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
+									  plane);
+	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
+									  plane);
+
+	unsigned char i, num_planes;
+	struct drm_framebuffer *fb;
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+	struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
+	struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	if (!new_state->fb || !new_state->crtc)
+		return;
+
+	fb = new_state->fb;
+
+	drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
+
+	num_planes = vs_get_plane_number(fb);
+
+	for (i = 0; i < num_planes; i++) {
+		dma_addr_t dma_addr;
+
+		dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
+		plane_state->dma_addr[i] = dma_addr;
+	}
+
+	plane_state->status.src = drm_plane_state_src(new_state);
+	plane_state->status.dest = drm_plane_state_dest(new_state);
+
+	vs_dc_update_plane(dc, vs_plane, plane, state);
+}
+
+static void vs_cursor_plane_atomic_update(struct drm_plane *plane,
+					  struct drm_atomic_state *state)
+{
+	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
+									   plane);
+	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
+									   plane);
+	unsigned char i, num_planes;
+	struct drm_framebuffer *fb;
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+	struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
+	struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	if (!new_state->fb || !new_state->crtc)
+		return;
+
+	fb = new_state->fb;
+	drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
+
+	num_planes = vs_get_plane_number(fb);
+
+	for (i = 0; i < num_planes; i++) {
+		dma_addr_t dma_addr;
+
+		dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
+		plane_state->dma_addr[i] = dma_addr;
+	}
+
+	plane_state->status.src = drm_plane_state_src(new_state);
+	plane_state->status.dest = drm_plane_state_dest(new_state);
+
+	vs_dc_update_cursor_plane(dc, vs_plane, plane, state);
+}
+
+static void vs_plane_atomic_disable(struct drm_plane *plane,
+				    struct drm_atomic_state *state)
+{
+	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
+									   plane);
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+	struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	vs_dc_disable_plane(dc, vs_plane, old_state);
+}
+
+static void vs_cursor_plane_atomic_disable(struct drm_plane *plane,
+					   struct drm_atomic_state *state)
+{
+	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
+									   plane);
+	struct vs_plane *vs_plane = to_vs_plane(plane);
+	struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
+	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
+
+	vs_dc_disable_cursor_plane(dc, vs_plane, old_state);
+}
+
+const struct drm_plane_helper_funcs primary_plane_helpers = {
+	.atomic_check	= vs_plane_atomic_check,
+	.atomic_update	= vs_plane_atomic_update,
+	.atomic_disable = vs_plane_atomic_disable,
+};
+
+const struct drm_plane_helper_funcs overlay_plane_helpers = {
+	.atomic_check	= vs_plane_atomic_check,
+	.atomic_update	= vs_plane_atomic_update,
+	.atomic_disable = vs_plane_atomic_disable,
+};
+
+const struct drm_plane_helper_funcs cursor_plane_helpers = {
+	.atomic_check	= vs_cursor_plane_atomic_check,
+	.atomic_update	= vs_cursor_plane_atomic_update,
+	.atomic_disable = vs_cursor_plane_atomic_disable,
+};
+
+static const struct drm_prop_enum_list vs_degamma_mode_enum_list[] = {
+	{ VS_DEGAMMA_DISABLE,	"disabled" },
+	{ VS_DEGAMMA_BT709,	"preset degamma for BT709" },
+	{ VS_DEGAMMA_BT2020,	"preset degamma for BT2020" },
+};
+
+struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
+				 struct vs_plane_info *info,
+				 unsigned int layer_num,
+				 unsigned int possible_crtcs)
+{
+	struct vs_plane *plane;
+	int ret;
+
+	if (!info)
+		return NULL;
+
+	plane = drmm_universal_plane_alloc(drm_dev, struct vs_plane, base,
+					   possible_crtcs,
+					   &vs_plane_funcs,
+					   info->formats, info->num_formats,
+					   info->modifiers, info->type,
+					   info->name ? info->name : NULL);
+	if (IS_ERR(plane))
+		return ERR_CAST(plane);
+
+	if (info->type == DRM_PLANE_TYPE_PRIMARY)
+		drm_plane_helper_add(&plane->base, &primary_plane_helpers);
+	else if (info->type == DRM_PLANE_TYPE_CURSOR)
+		drm_plane_helper_add(&plane->base, &cursor_plane_helpers);
+	else
+		drm_plane_helper_add(&plane->base, &overlay_plane_helpers);
+
+	/* Set up the plane properties */
+	if (info->degamma_size) {
+		plane->degamma_mode =
+		drm_property_create_enum(drm_dev, 0,
+					 "DEGAMMA_MODE",
+					 vs_degamma_mode_enum_list,
+					 ARRAY_SIZE(vs_degamma_mode_enum_list));
+
+		if (!plane->degamma_mode)
+			return NULL;
+
+		drm_object_attach_property(&plane->base.base,
+					   plane->degamma_mode,
+					   VS_DEGAMMA_DISABLE);
+	}
+
+	if (info->rotation) {
+		ret = drm_plane_create_rotation_property(&plane->base,
+							 DRM_MODE_ROTATE_0,
+							 info->rotation);
+		if (ret)
+			return NULL;
+	}
+
+	if (info->blend_mode) {
+		ret = drm_plane_create_blend_mode_property(&plane->base,
+							   info->blend_mode);
+		if (ret)
+			return NULL;
+		ret = drm_plane_create_alpha_property(&plane->base);
+		if (ret)
+			return NULL;
+	}
+
+	if (info->color_encoding) {
+		ret = drm_plane_create_color_properties(&plane->base,
+							info->color_encoding,
+							BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
+							DRM_COLOR_YCBCR_BT709,
+							DRM_COLOR_YCBCR_LIMITED_RANGE);
+		if (ret)
+			return NULL;
+	}
+
+	if (info->zpos != 255) {
+		ret = drm_plane_create_zpos_property(&plane->base, info->zpos, 0,
+						     layer_num - 1);
+		if (ret)
+			return NULL;
+	} else {
+		ret = drm_plane_create_zpos_immutable_property(&plane->base,
+							       info->zpos);
+		if (ret)
+			return NULL;
+	}
+
+	if (info->watermark) {
+		plane->watermark_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
+							    "WATERMARK", 0);
+		if (!plane->watermark_prop)
+			return NULL;
+		drm_object_attach_property(&plane->base.base, plane->watermark_prop, 0);
+	}
+
+	if (info->color_mgmt) {
+		plane->color_mgmt_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
+							     "COLOR_CONFIG", 0);
+		if (!plane->color_mgmt_prop)
+			return NULL;
+
+		drm_object_attach_property(&plane->base.base, plane->color_mgmt_prop, 0);
+	}
+
+	if (info->roi) {
+		plane->roi_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
+						      "ROI", 0);
+		if (!plane->roi_prop)
+			return NULL;
+
+		drm_object_attach_property(&plane->base.base, plane->roi_prop, 0);
+	}
+
+	return plane;
+}
diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h
new file mode 100644
index 000000000..554b74e96
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_plane.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_PLANE_H__
+#define __VS_PLANE_H__
+
+#include <drm/drm_fourcc.h>
+#include <drm/drm_plane_helper.h>
+
+#include "vs_type.h"
+
+struct vs_plane_status {
+	u32 tile_mode;
+	struct drm_rect src;
+	struct drm_rect dest;
+};
+
+struct vs_plane_state {
+	struct drm_plane_state base;
+	struct vs_plane_status status; /* for debugfs */
+
+	struct drm_property_blob *watermark;
+	struct drm_property_blob *color_mgmt;
+	struct drm_property_blob *roi;
+	dma_addr_t dma_addr[DRM_FORMAT_MAX_PLANES];
+
+	u32 degamma;
+	bool degamma_changed;
+};
+
+struct vs_plane {
+	struct drm_plane base;
+	u8 id;
+
+	struct drm_property *degamma_mode;
+	struct drm_property *watermark_prop;
+	struct drm_property *color_mgmt_prop;
+	struct drm_property *roi_prop;
+};
+
+struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
+				 struct vs_plane_info *info,
+				 unsigned int layer_num,
+				 unsigned int possible_crtcs);
+
+static inline struct vs_plane *to_vs_plane(struct drm_plane *plane)
+{
+	return container_of(plane, struct vs_plane, base);
+}
+
+static inline struct vs_plane_state *
+to_vs_plane_state(struct drm_plane_state *state)
+{
+	return container_of(state, struct vs_plane_state, base);
+}
+#endif /* __VS_PLANE_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_type.h b/drivers/gpu/drm/verisilicon/vs_type.h
new file mode 100644
index 000000000..7d3378e29
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_type.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_TYPE_H__
+#define __VS_TYPE_H__
+
+#include <drm/drm_plane.h>
+#include <drm/drm_plane_helper.h>
+
+struct vs_plane_info {
+	const char *name;
+	u8 id;
+	enum drm_plane_type type;
+	unsigned int num_formats;
+	const u32 *formats;
+	u8 num_modifiers;
+	const u64 *modifiers;
+	unsigned int min_width;
+	unsigned int min_height;
+	unsigned int max_width;
+	unsigned int max_height;
+	unsigned int rotation;
+	unsigned int blend_mode;
+	unsigned int color_encoding;
+
+	/* 0 means no de-gamma LUT */
+	unsigned int degamma_size;
+
+	int min_scale; /* 16.16 fixed point */
+	int max_scale; /* 16.16 fixed point */
+
+	/* default zorder value,
+	 * and 255 means unsupported zorder capability
+	 */
+	u8	 zpos;
+
+	bool watermark;
+	bool color_mgmt;
+	bool roi;
+};
+
+struct vs_dc_info {
+	const char *name;
+
+	u8 panel_num;
+
+	/* planes */
+	u8 plane_num;
+	const struct vs_plane_info *planes;
+
+	u8 layer_num;
+	unsigned int max_bpc;
+	unsigned int color_formats;
+
+	/* 0 means no gamma LUT */
+	u16 gamma_size;
+	u8 gamma_bits;
+
+	u16 pitch_alignment;
+
+	bool pipe_sync;
+	bool background;
+	bool panel_sync;
+	bool cap_dec;
+};
+
+#endif /* __VS_TYPE_H__ */
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 117+ messages in thread

* [PATCH v2 6/6] drm/vs: Add hdmi driver
  2023-10-25 10:39 ` Keith Zhao
  (?)
@ 2023-10-25 10:39   ` Keith Zhao
  -1 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-25 10:39 UTC (permalink / raw)
  To: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Keith Zhao, Jack Zhu, Shengyang Chen,
	Changhuang Liang

add hdmi driver as encoder and connect

Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
---
 drivers/gpu/drm/verisilicon/Kconfig         |   8 +-
 drivers/gpu/drm/verisilicon/Makefile        |   1 +
 drivers/gpu/drm/verisilicon/starfive_hdmi.c | 949 ++++++++++++++++++++
 drivers/gpu/drm/verisilicon/starfive_hdmi.h | 295 ++++++
 drivers/gpu/drm/verisilicon/vs_drv.c        |   5 +
 drivers/gpu/drm/verisilicon/vs_drv.h        |   4 +
 6 files changed, 1261 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.c
 create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.h

diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
index 3a361f8c8..122c786e3 100644
--- a/drivers/gpu/drm/verisilicon/Kconfig
+++ b/drivers/gpu/drm/verisilicon/Kconfig
@@ -12,4 +12,10 @@ config DRM_VERISILICON
 	  setting and buffer management. It does not
 	  provide 2D or 3D acceleration.
 
-
+config DRM_VERISILICON_STARFIVE_HDMI
+	bool "Starfive HDMI extensions"
+	depends on DRM_VERISILICON
+	help
+	   This selects support for StarFive soc specific extensions
+	   for the Innosilicon HDMI driver. If you want to enable
+	   HDMI on JH7110 based soc, you should select this option.
diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
index 1d48016ca..08350f25b 100644
--- a/drivers/gpu/drm/verisilicon/Makefile
+++ b/drivers/gpu/drm/verisilicon/Makefile
@@ -7,5 +7,6 @@ vs_drm-objs := vs_dc_hw.o \
 		vs_modeset.o \
 		vs_plane.o
 
+vs_drm-$(CONFIG_DRM_VERISILICON_STARFIVE_HDMI) += starfive_hdmi.o
 obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
 
diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.c b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
new file mode 100644
index 000000000..d296c4b71
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
@@ -0,0 +1,949 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 StarFive Technology Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/hdmi.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/media-bus-format.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#include <drm/bridge/dw_hdmi.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#include "starfive_hdmi.h"
+#include "vs_drv.h"
+#include "vs_crtc.h"
+
+static struct starfive_hdmi *encoder_to_hdmi(struct drm_encoder *encoder)
+{
+	return container_of(encoder, struct starfive_hdmi, encoder);
+}
+
+static struct starfive_hdmi *connector_to_hdmi(struct drm_connector *connector)
+{
+	return container_of(connector, struct starfive_hdmi, connector);
+}
+
+struct starfive_hdmi_i2c {
+	struct i2c_adapter adap;
+
+	u8 ddc_addr;
+	u8 segment_addr;
+	/* protects the edid data when use i2c cmd to read edid */
+	struct mutex lock;
+	struct completion cmp;
+};
+
+static const struct pre_pll_config pre_pll_cfg_table[] = {
+	{ 25175000,  25175000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0xf55555},
+	{ 25200000,  25200000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0},
+	{ 27000000,  27000000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0},
+	{ 27027000,  27027000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0x170a3d},
+	{ 28320000,  28320000, 1,  28, 2, 1, 1,  3, 0, 3, 4, 0, 0x51eb85},
+	{ 30240000,  30240000, 1,  30, 2, 1, 1,  3, 0, 3, 4, 0, 0x3d70a3},
+	{ 31500000,  31500000, 1,  31, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
+	{ 33750000,  33750000, 1,  33, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
+	{ 36000000,  36000000, 1,  36, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{ 40000000,  40000000, 1,  80, 2, 2, 2, 12, 2, 2, 2, 0, 0},
+	{ 46970000,  46970000, 1,  46, 2, 1, 1,  3, 0, 3, 4, 0, 0xf851eb},
+	{ 49500000,  49500000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
+	{ 49000000,  49000000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{ 50000000,  50000000, 1,  50, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{ 54000000,  54000000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{ 54054000,  54054000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0x0dd2f1},
+	{ 57284000,  57284000, 1,  57, 2, 1, 1,  3, 0, 3, 4, 0, 0x48b439},
+	{ 58230000,  58230000, 1,  58, 2, 1, 1,  3, 0, 3, 4, 0, 0x3ae147},
+	{ 59341000,  59341000, 1,  59, 2, 1, 1,  3, 0, 3, 4, 0, 0x574bc6},
+	{ 59400000,  59400000, 1,  99, 3, 1, 1,  1, 3, 3, 4, 0, 0},
+	{ 65000000,  65000000, 1, 130, 2, 2, 2,  12, 0, 2, 2, 0, 0},
+	{ 68250000,  68250000, 1, 68,  2, 1, 1,  3,  0, 3, 4, 0, 0x3fffff},
+	{ 71000000,  71000000, 1,  71, 2, 1, 1,  3, 0, 3,  4, 0, 0},
+	{ 74176000,  74176000, 1,  98, 1, 2, 2,  1, 2, 3, 4, 0, 0xe6ae6b},
+	{ 74250000,  74250000, 1,  99, 1, 2, 2,  1, 2, 3, 4, 0, 0},
+	{ 75000000,  75000000, 1,  75, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{ 78750000,  78750000, 1,  78, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
+	{ 79500000,  79500000, 1,  79, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
+	{ 83500000,  83500000, 2, 167, 2, 1, 1,  1, 0, 0,  6, 0, 0},
+	{ 83500000, 104375000, 1, 104, 2, 1, 1,  1, 1, 0,  5, 0, 0x600000},
+	{ 84858000,  84858000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xdba5e2},
+	{ 85500000,  85500000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
+	{ 85750000,  85750000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
+	{ 85800000,  85800000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcccccc},
+	{ 88750000,  88750000, 1,  88, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
+	{ 89910000,  89910000, 1,  89, 2, 1, 1,  3, 0, 3, 4, 0, 0xe8f5c1},
+	{ 90000000,  90000000, 1,  90, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{101000000, 101000000, 1, 101, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{102250000, 102250000, 1, 102, 2, 1, 1,  3, 0, 3, 4, 0, 0x3fffff},
+	{106500000, 106500000, 1, 106, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
+	{108000000, 108000000, 1,  90, 3, 0, 0,  5, 0, 2,  2, 0, 0},
+	{119000000, 119000000, 1, 119, 2, 1, 1,  3, 0, 3,  4, 0, 0},
+	{131481000, 131481000, 1,  131, 2, 1, 1,  3, 0, 3,  4, 0, 0x7b22d1},
+	{135000000, 135000000, 1,  135, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{136750000, 136750000, 1,  136, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
+	{147180000, 147180000, 1,  147, 2, 1, 1,  3, 0, 3, 4, 0, 0x2e147a},
+	{148352000, 148352000, 1,  98, 1, 1, 1,  1, 2, 2, 2, 0, 0xe6ae6b},
+	{148500000, 148500000, 1,  99, 1, 1, 1,  1, 2, 2, 2, 0, 0},
+	{154000000, 154000000, 1, 154, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{156000000, 156000000, 1, 156, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{157000000, 157000000, 1, 157, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{162000000, 162000000, 1, 162, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{174250000, 174250000, 1, 145, 3, 0, 0,  5, 0, 2, 2, 0, 0x355555},
+	{174500000, 174500000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
+	{174570000, 174570000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x91eb84},
+	{175500000, 175500000, 1, 175, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
+	{185590000, 185590000, 1, 185, 2, 1, 1,  3, 0, 3, 4, 0, 0x970a3c},
+	{187000000, 187000000, 1, 187, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{241500000, 241500000, 1, 161, 1, 1, 1,  4, 0, 2,  2, 0, 0},
+	{241700000, 241700000, 1, 241, 2, 1, 1,  3, 0, 3,  4, 0, 0xb33332},
+	{262750000, 262750000, 1, 262, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
+	{296500000, 296500000, 1, 296, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
+	{296703000, 296703000, 1,  98, 0, 1, 1,  1, 0, 2,  2, 0, 0xe6ae6b},
+	{297000000, 297000000, 1,  99, 0, 1, 1,  1, 0, 2,  2, 0, 0},
+	{594000000, 594000000, 1,  99, 0, 2, 0,  1, 0, 1,  1, 0, 0},
+	{0, 0, 0,  0, 0, 0, 0,  0, 0, 0,  0, 0, 0},
+};
+
+static const struct post_pll_config post_pll_cfg_table[] = {
+	{25200000,	1, 80, 13, 3, 1},
+	{27000000,	1, 40, 11, 3, 1},
+	{33750000,	1, 40, 11, 3, 1},
+	{49000000,	1, 20, 1, 3, 3},
+	{241700000, 1, 20, 1, 3, 3},
+	{297000000, 4, 20, 0, 0, 3},
+	{594000000, 4, 20, 0, 0, 0},
+	{ /* sentinel */ }
+};
+
+inline u8 hdmi_readb(struct starfive_hdmi *hdmi, u16 offset)
+{
+	return readl_relaxed(hdmi->regs + (offset) * 0x04);
+}
+
+inline void hdmi_writeb(struct starfive_hdmi *hdmi, u16 offset, u32 val)
+{
+	writel_relaxed(val, hdmi->regs + (offset) * 0x04);
+}
+
+inline void hdmi_modb(struct starfive_hdmi *hdmi, u16 offset,
+			     u32 msk, u32 val)
+{
+	u8 temp = hdmi_readb(hdmi, offset) & ~msk;
+
+	temp |= val & msk;
+	hdmi_writeb(hdmi, offset, temp);
+}
+
+static int starfive_hdmi_enable_clk_deassert_rst(struct device *dev, struct starfive_hdmi *hdmi)
+{
+	int ret;
+
+	ret = clk_prepare_enable(hdmi->sys_clk);
+	if (ret) {
+		dev_err(dev, "Cannot enable HDMI sys clock: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(hdmi->mclk);
+	if (ret) {
+		dev_err(dev, "Cannot enable HDMI mclk clock: %d\n", ret);
+		goto err_mclk;
+	}
+	ret = clk_prepare_enable(hdmi->bclk);
+	if (ret) {
+		dev_err(dev, "Cannot enable HDMI bclk clock: %d\n", ret);
+		goto err_bclk;
+	}
+	ret = reset_control_deassert(hdmi->tx_rst);
+	if (ret < 0) {
+		dev_err(dev, "failed to deassert tx_rst\n");
+		goto err_rst;
+	}
+	return 0;
+
+err_rst:
+	clk_disable_unprepare(hdmi->bclk);
+err_bclk:
+	clk_disable_unprepare(hdmi->mclk);
+err_mclk:
+	clk_disable_unprepare(hdmi->sys_clk);
+	return ret;
+}
+
+static void starfive_hdmi_disable_clk_assert_rst(struct device *dev, struct starfive_hdmi *hdmi)
+{
+	int ret;
+
+	ret = reset_control_assert(hdmi->tx_rst);
+	if (ret < 0)
+		dev_err(dev, "failed to assert tx_rst\n");
+
+	clk_disable_unprepare(hdmi->sys_clk);
+	clk_disable_unprepare(hdmi->mclk);
+	clk_disable_unprepare(hdmi->bclk);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int hdmi_system_pm_suspend(struct device *dev)
+{
+	return pm_runtime_force_suspend(dev);
+}
+
+static int hdmi_system_pm_resume(struct device *dev)
+{
+	return pm_runtime_force_resume(dev);
+}
+#endif
+
+#ifdef CONFIG_PM
+static int hdmi_runtime_suspend(struct device *dev)
+{
+	struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
+
+	starfive_hdmi_disable_clk_assert_rst(dev, hdmi);
+
+	return 0;
+}
+
+static int hdmi_runtime_resume(struct device *dev)
+{
+	struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
+
+	return starfive_hdmi_enable_clk_deassert_rst(dev, hdmi);
+}
+#endif
+
+static void starfive_hdmi_tx_phy_power_down(struct starfive_hdmi *hdmi)
+{
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
+}
+
+static void starfive_hdmi_tx_phy_power_on(struct starfive_hdmi *hdmi)
+{
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
+}
+
+static void starfive_hdmi_config_pll(struct starfive_hdmi *hdmi)
+{
+	u32 val;
+	u8 reg_1ad_value = hdmi->post_cfg->post_div_en ?
+		 hdmi->post_cfg->postdiv : 0x00;
+	u8 reg_1aa_value = hdmi->post_cfg->post_div_en ?
+		 0x0e : 0x02;
+
+	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN);
+	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1,
+		    STARFIVE_POST_PLL_POST_DIV_ENABLE |
+		    STARFIVE_POST_PLL_REFCLK_SEL_TMDS |
+		    STARFIVE_POST_PLL_POWER_DOWN);
+	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_1, STARFIVE_PRE_PLL_PRE_DIV(hdmi->pre_cfg->prediv));
+
+	val = STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE | STARFIVE_SPREAD_SPECTRUM_MOD_DOWN;
+	if (!hdmi->pre_cfg->fracdiv)
+		val |= STARFIVE_PRE_PLL_FRAC_DIV_DISABLE;
+	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_2,
+		    STARFIVE_PRE_PLL_FB_DIV_11_8(hdmi->pre_cfg->fbdiv) | val);
+	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_3,
+		    STARFIVE_PRE_PLL_FB_DIV_7_0(hdmi->pre_cfg->fbdiv));
+	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_4,
+		    STARFIVE_PRE_PLL_TMDSCLK_DIV_C(hdmi->pre_cfg->tmds_div_c) |
+		    STARFIVE_PRE_PLL_TMDSCLK_DIV_A(hdmi->pre_cfg->tmds_div_a) |
+		    STARFIVE_PRE_PLL_TMDSCLK_DIV_B(hdmi->pre_cfg->tmds_div_b));
+
+	if (hdmi->pre_cfg->fracdiv) {
+		hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_L,
+			    STARFIVE_PRE_PLL_FRAC_DIV_7_0(hdmi->pre_cfg->fracdiv));
+		hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_M,
+			    STARFIVE_PRE_PLL_FRAC_DIV_15_8(hdmi->pre_cfg->fracdiv));
+		hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_H,
+			    STARFIVE_PRE_PLL_FRAC_DIV_23_16(hdmi->pre_cfg->fracdiv));
+	}
+
+	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_5,
+		    STARFIVE_PRE_PLL_PCLK_DIV_A(hdmi->pre_cfg->pclk_div_a) |
+		    STARFIVE_PRE_PLL_PCLK_DIV_B(hdmi->pre_cfg->pclk_div_b));
+	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_6,
+		    STARFIVE_PRE_PLL_PCLK_DIV_C(hdmi->pre_cfg->pclk_div_c) |
+		    STARFIVE_PRE_PLL_PCLK_DIV_D(hdmi->pre_cfg->pclk_div_d));
+
+	/*pre-pll power down*/
+	hdmi_modb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN, 0);
+
+	hdmi_modb(hdmi, STARFIVE_POST_PLL_DIV_2, STARFIVE_POST_PLL_Pre_DIV_MASK,
+		  STARFIVE_POST_PLL_PRE_DIV(hdmi->post_cfg->prediv));
+	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_3, hdmi->post_cfg->fbdiv & 0xff);
+	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_4, reg_1ad_value);
+	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1, reg_1aa_value);
+}
+
+static void starfive_hdmi_tmds_driver_on(struct starfive_hdmi *hdmi)
+{
+	hdmi_modb(hdmi, STARFIVE_TMDS_CONTROL,
+		  STARFIVE_TMDS_DRIVER_ENABLE, STARFIVE_TMDS_DRIVER_ENABLE);
+}
+
+static void starfive_hdmi_sync_tmds(struct starfive_hdmi *hdmi)
+{
+	/*first send 0 to this bit, then send 1 and keep 1 into this bit*/
+	hdmi_writeb(hdmi, HDMI_SYNC, 0x0);
+	hdmi_writeb(hdmi, HDMI_SYNC, 0x1);
+}
+
+static void starfive_hdmi_i2c_init(struct starfive_hdmi *hdmi)
+{
+	int ddc_bus_freq;
+
+	ddc_bus_freq = (clk_get_rate(hdmi->sys_clk) >> 2) / HDMI_SCL_RATE;
+
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
+
+	/* Clear the EDID interrupt flag and mute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+}
+
+static const
+struct pre_pll_config *starfive_hdmi_phy_get_pre_pll_cfg(struct starfive_hdmi *hdmi,
+							 unsigned long rate)
+{
+	const struct pre_pll_config *cfg = pre_pll_cfg_table;
+
+	rate = (rate / 1000) * 1000;
+	for (; cfg->pixclock != 0; cfg++)
+		if (cfg->tmdsclock == rate && cfg->pixclock == rate)
+			break;
+
+	if (cfg->pixclock == 0)
+		return ERR_PTR(-EINVAL);
+
+	return cfg;
+}
+
+static int starfive_hdmi_phy_clk_set_rate(struct starfive_hdmi *hdmi)
+{
+	hdmi->post_cfg = post_pll_cfg_table;
+
+	hdmi->pre_cfg = starfive_hdmi_phy_get_pre_pll_cfg(hdmi, hdmi->tmds_rate);
+	if (IS_ERR(hdmi->pre_cfg))
+		return PTR_ERR(hdmi->pre_cfg);
+
+	for (; hdmi->post_cfg->tmdsclock != 0; hdmi->post_cfg++)
+		if (hdmi->tmds_rate <= hdmi->post_cfg->tmdsclock)
+			break;
+
+	starfive_hdmi_config_pll(hdmi);
+
+	return 0;
+}
+
+static int starfive_hdmi_config_video_timing(struct starfive_hdmi *hdmi,
+					     struct drm_display_mode *mode)
+{
+	int value;
+	/* Set detail external video timing */
+	value = mode->htotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->htotal - mode->hdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
+
+	value = mode->htotal - mode->hsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
+
+	value = mode->hsync_end - mode->hsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal - mode->vdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
+
+	value = mode->vtotal - mode->vsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
+
+	value = mode->vsync_end - mode->vsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
+
+	/* Set detail external video timing polarity and interlace mode */
+	value = v_EXTERANL_VIDEO(1);
+	value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
+		v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
+		v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
+		v_INETLACE(1) : v_INETLACE(0);
+
+	hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
+	return 0;
+}
+
+static int starfive_hdmi_setup(struct starfive_hdmi *hdmi,
+			       struct drm_display_mode *mode)
+{
+	hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
+	hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
+	hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
+
+	hdmi->tmds_rate = mode->clock * 1000;
+	starfive_hdmi_phy_clk_set_rate(hdmi);
+
+	while (!(hdmi_readb(hdmi, STARFIVE_PRE_PLL_LOCK_STATUS) & 0x1))
+		continue;
+	while (!(hdmi_readb(hdmi, STARFIVE_POST_PLL_LOCK_STATUS) & 0x1))
+		continue;
+
+	/*turn on LDO*/
+	hdmi_writeb(hdmi, STARFIVE_LDO_CONTROL, STARFIVE_LDO_ENABLE);
+	/*turn on serializer*/
+	hdmi_writeb(hdmi, STARFIVE_SERIALIER_CONTROL, STARFIVE_SERIALIER_ENABLE);
+
+	starfive_hdmi_tx_phy_power_down(hdmi);
+	starfive_hdmi_config_video_timing(hdmi, mode);
+	starfive_hdmi_tx_phy_power_on(hdmi);
+
+	starfive_hdmi_tmds_driver_on(hdmi);
+	starfive_hdmi_sync_tmds(hdmi);
+
+	return 0;
+}
+
+static void starfive_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+					   struct drm_display_mode *mode,
+					   struct drm_display_mode *adj_mode)
+{
+	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
+
+	drm_mode_copy(&hdmi->previous_mode, adj_mode);
+}
+
+static void starfive_hdmi_encoder_enable(struct drm_encoder *encoder)
+{
+	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
+	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
+	int ret, idx;
+	struct drm_device *drm = hdmi->connector.dev;
+
+	if (drm && !drm_dev_enter(drm, &idx))
+		return;
+
+	ret = pm_runtime_get_sync(hdmi->dev);
+	if (ret < 0)
+		return;
+	mdelay(10);
+	starfive_hdmi_setup(hdmi, mode);
+
+	if (drm)
+		drm_dev_exit(idx);
+}
+
+static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
+
+	int idx;
+	struct drm_device *drm = hdmi->connector.dev;
+
+	if (drm && !drm_dev_enter(drm, &idx))
+		return;
+
+	pm_runtime_put(hdmi->dev);
+
+	if (drm)
+		drm_dev_exit(idx);
+}
+
+static int
+starfive_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
+				   struct drm_crtc_state *crtc_state,
+				   struct drm_connector_state *conn_state)
+{
+	bool valid = false;
+	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
+	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
+
+	vs_crtc_state->encoder_type = encoder->encoder_type;
+	vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
+
+	const struct pre_pll_config *cfg = pre_pll_cfg_table;
+	int pclk = mode->clock * 1000;
+
+	for (; cfg->pixclock != 0; cfg++) {
+		if (pclk == cfg->pixclock) {
+			if (pclk > 297000000)
+				continue;
+
+			valid = true;
+			break;
+		}
+	}
+
+	return (valid) ? 0 : -EINVAL;
+}
+
+static const struct drm_encoder_helper_funcs starfive_hdmi_encoder_helper_funcs = {
+	.enable     = starfive_hdmi_encoder_enable,
+	.disable    = starfive_hdmi_encoder_disable,
+	.mode_set   = starfive_hdmi_encoder_mode_set,
+	.atomic_check = starfive_hdmi_encoder_atomic_check,
+};
+
+static enum drm_connector_status
+starfive_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
+	struct drm_device *drm = hdmi->connector.dev;
+	int ret;
+	int idx;
+
+	if (drm && !drm_dev_enter(drm, &idx))
+		return connector_status_disconnected;
+
+	ret = pm_runtime_get_sync(hdmi->dev);
+	if (ret < 0)
+		return ret;
+
+	ret = (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
+		connector_status_connected : connector_status_disconnected;
+	pm_runtime_put(hdmi->dev);
+
+	if (drm)
+		drm_dev_exit(idx);
+
+	return ret;
+}
+
+static int starfive_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+	struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
+	struct edid *edid;
+	int ret = 0;
+
+	if (!hdmi->ddc)
+		return 0;
+	ret = pm_runtime_get_sync(hdmi->dev);
+	if (ret < 0)
+		return ret;
+
+	edid = drm_get_edid(connector, hdmi->ddc);
+	if (edid) {
+		hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
+		hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
+		drm_connector_update_edid_property(connector, edid);
+		ret = drm_add_edid_modes(connector, edid);
+		kfree(edid);
+	}
+	pm_runtime_put(hdmi->dev);
+
+	return ret;
+}
+
+static enum drm_mode_status
+starfive_hdmi_connector_mode_valid(struct drm_connector *connector,
+				   struct drm_display_mode *mode)
+{
+	const struct pre_pll_config *cfg = pre_pll_cfg_table;
+	int pclk = mode->clock * 1000;
+	bool valid = false;
+
+	for (; cfg->pixclock != 0; cfg++) {
+		if (pclk == cfg->pixclock) {
+			if (pclk > 297000000)
+				continue;
+
+			valid = true;
+			break;
+		}
+	}
+
+	return (valid) ? MODE_OK : MODE_BAD;
+}
+
+static int
+starfive_hdmi_probe_single_connector_modes(struct drm_connector *connector,
+					   u32 maxX, u32 maxY)
+{
+	return drm_helper_probe_single_connector_modes(connector, 3840, 2160);
+}
+
+static const struct drm_connector_funcs starfive_hdmi_connector_funcs = {
+	.fill_modes = starfive_hdmi_probe_single_connector_modes,
+	.detect = starfive_hdmi_connector_detect,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static struct drm_connector_helper_funcs starfive_hdmi_connector_helper_funcs = {
+	.get_modes = starfive_hdmi_connector_get_modes,
+	.mode_valid = starfive_hdmi_connector_mode_valid,
+};
+
+static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
+{
+	struct drm_encoder *encoder = &hdmi->encoder;
+	struct device *dev = hdmi->dev;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+
+	/*
+	 * If we failed to find the CRTC(s) which this encoder is
+	 * supposed to be connected to, it's because the CRTC has
+	 * not been registered yet.  Defer probing, and hope that
+	 * the required CRTC is added later.
+	 */
+	if (encoder->possible_crtcs == 0)
+		return -EPROBE_DEFER;
+
+	drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
+
+	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+	drm_connector_helper_add(&hdmi->connector,
+				 &starfive_hdmi_connector_helper_funcs);
+	drmm_connector_init(drm, &hdmi->connector,
+			    &starfive_hdmi_connector_funcs,
+			    DRM_MODE_CONNECTOR_HDMIA,
+			    hdmi->ddc);
+
+	drm_connector_attach_encoder(&hdmi->connector, encoder);
+
+	return 0;
+}
+
+static irqreturn_t starfive_hdmi_i2c_irq(struct starfive_hdmi *hdmi)
+{
+	struct starfive_hdmi_i2c *i2c = hdmi->i2c;
+	u8 stat;
+
+	stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
+	if (!(stat & m_INT_EDID_READY))
+		return IRQ_NONE;
+
+	/* Clear HDMI EDID interrupt flag */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+	complete(&i2c->cmp);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t starfive_hdmi_hardirq(int irq, void *dev_id)
+{
+	struct starfive_hdmi *hdmi = dev_id;
+	irqreturn_t ret = IRQ_NONE;
+	u8 interrupt;
+
+	if (hdmi->i2c)
+		ret = starfive_hdmi_i2c_irq(hdmi);
+
+	interrupt = hdmi_readb(hdmi, HDMI_STATUS);
+	if (interrupt & m_INT_HOTPLUG) {
+		hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	return ret;
+}
+
+static irqreturn_t starfive_hdmi_irq(int irq, void *dev_id)
+{
+	struct starfive_hdmi *hdmi = dev_id;
+
+	drm_connector_helper_hpd_irq_event(&hdmi->connector);
+
+	return IRQ_HANDLED;
+}
+
+static int starfive_hdmi_i2c_read(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	int length = msgs->len;
+	u8 *buf = msgs->buf;
+	int ret;
+
+	ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10);
+	if (!ret)
+		return -EAGAIN;
+
+	while (length--)
+		*buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
+
+	return 0;
+}
+
+static int starfive_hdmi_i2c_write(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	/*
+	 * The DDC module only support read EDID message, so
+	 * we assume that each word write to this i2c adapter
+	 * should be the offset of EDID word address.
+	 */
+	if (msgs->len != 1 ||
+	    (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR))
+		return -EINVAL;
+
+	reinit_completion(&hdmi->i2c->cmp);
+
+	if (msgs->addr == DDC_SEGMENT_ADDR)
+		hdmi->i2c->segment_addr = msgs->buf[0];
+	if (msgs->addr == DDC_ADDR)
+		hdmi->i2c->ddc_addr = msgs->buf[0];
+
+	/* Set edid fifo first addr */
+	hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
+
+	/* Set edid word address 0x00/0x80 */
+	hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
+
+	/* Set edid segment pointer */
+	hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
+
+	return 0;
+}
+
+static int starfive_hdmi_i2c_xfer(struct i2c_adapter *adap,
+				  struct i2c_msg *msgs, int num)
+{
+	struct starfive_hdmi *hdmi = i2c_get_adapdata(adap);
+	struct starfive_hdmi_i2c *i2c = hdmi->i2c;
+	int i, ret = 0;
+
+	mutex_lock(&i2c->lock);
+
+	/* Clear the EDID interrupt flag and unmute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+	for (i = 0; i < num; i++) {
+		DRM_DEV_DEBUG(hdmi->dev,
+			      "xfer: num: %d/%d, len: %d, flags: %#x\n",
+			      i + 1, num, msgs[i].len, msgs[i].flags);
+
+		if (msgs[i].flags & I2C_M_RD)
+			ret = starfive_hdmi_i2c_read(hdmi, &msgs[i]);
+		else
+			ret = starfive_hdmi_i2c_write(hdmi, &msgs[i]);
+
+		if (ret < 0)
+			break;
+	}
+
+	if (!ret)
+		ret = num;
+
+	/* Mute HDMI EDID interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+
+	mutex_unlock(&i2c->lock);
+
+	return ret;
+}
+
+static u32 starfive_hdmi_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm starfive_hdmi_algorithm = {
+	.master_xfer	= starfive_hdmi_i2c_xfer,
+	.functionality	= starfive_hdmi_i2c_func,
+};
+
+static struct i2c_adapter *starfive_hdmi_i2c_adapter(struct starfive_hdmi *hdmi)
+{
+	struct i2c_adapter *adap;
+	struct starfive_hdmi_i2c *i2c;
+	int ret;
+
+	i2c = drmm_kzalloc(hdmi->drm_dev, sizeof(*i2c), GFP_KERNEL);
+	if (!i2c)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&i2c->lock);
+	init_completion(&i2c->cmp);
+
+	adap = &i2c->adap;
+	adap->class = I2C_CLASS_DDC;
+	adap->owner = THIS_MODULE;
+	adap->dev.parent = hdmi->dev;
+	adap->algo = &starfive_hdmi_algorithm;
+	strscpy(adap->name, "Starfive HDMI", sizeof(adap->name));
+	i2c_set_adapdata(adap, hdmi);
+
+	ret = devm_i2c_add_adapter(hdmi->dev, adap);
+	if (ret) {
+		drm_warn(hdmi->drm_dev, "cannot add %s I2C adapter\n", adap->name);
+		return ERR_PTR(ret);
+	}
+
+	hdmi->i2c = i2c;
+
+	drm_info(hdmi->drm_dev, "registered %s I2C bus driver success\n", adap->name);
+
+	return adap;
+}
+
+static int starfive_hdmi_get_clk_rst(struct device *dev, struct starfive_hdmi *hdmi)
+{
+	hdmi->sys_clk = devm_clk_get(dev, "sysclk");
+	if (IS_ERR(hdmi->sys_clk)) {
+		dev_err(dev, "Unable to get HDMI sysclk clk\n");
+		return PTR_ERR(hdmi->sys_clk);
+	}
+	hdmi->mclk = devm_clk_get(dev, "mclk");
+	if (IS_ERR(hdmi->mclk)) {
+		dev_err(dev, "Unable to get HDMI mclk clk\n");
+		return PTR_ERR(hdmi->mclk);
+	}
+	hdmi->bclk = devm_clk_get(dev, "bclk");
+	if (IS_ERR(hdmi->bclk)) {
+		dev_err(dev, "Unable to get HDMI bclk clk\n");
+		return PTR_ERR(hdmi->bclk);
+	}
+	hdmi->tx_rst = devm_reset_control_get_by_index(dev, 0);
+	if (IS_ERR(hdmi->tx_rst)) {
+		dev_err(dev, "failed to get tx_rst reset\n");
+		return PTR_ERR(hdmi->tx_rst);
+	}
+
+	return 0;
+}
+
+static int starfive_hdmi_bind(struct device *dev, struct device *master,
+			      void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = dev_get_drvdata(master);
+	struct starfive_hdmi *hdmi;
+	struct resource *iores;
+	int irq;
+	int ret;
+
+	hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return -ENOMEM;
+
+	hdmi = drmm_simple_encoder_alloc(drm, struct starfive_hdmi,
+					 encoder, DRM_MODE_ENCODER_TMDS);
+
+	hdmi->dev = dev;
+	hdmi->drm_dev = drm;
+	dev_set_drvdata(dev, hdmi);
+
+	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	hdmi->regs = devm_ioremap_resource(dev, iores);
+	if (IS_ERR(hdmi->regs))
+		return PTR_ERR(hdmi->regs);
+
+	ret = starfive_hdmi_get_clk_rst(dev, hdmi);
+	if (ret < 0)
+		return ret;
+
+	ret = devm_pm_runtime_enable(dev);
+	if (ret)
+		return ret;
+
+	ret = pm_runtime_resume_and_get(dev);
+	if (ret)
+		return ret;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		ret = irq;
+		goto err_put_runtime_pm;
+	}
+
+	hdmi->ddc = starfive_hdmi_i2c_adapter(hdmi);
+	if (IS_ERR(hdmi->ddc)) {
+		ret = PTR_ERR(hdmi->ddc);
+		hdmi->ddc = NULL;
+		goto err_put_runtime_pm;
+	}
+
+	starfive_hdmi_i2c_init(hdmi);
+
+	ret = starfive_hdmi_register(drm, hdmi);
+	if (ret)
+		goto err_put_adapter;
+
+	/* Unmute hotplug interrupt */
+	hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
+
+	ret = devm_request_threaded_irq(dev, irq, starfive_hdmi_hardirq,
+					starfive_hdmi_irq, IRQF_SHARED,
+					dev_name(dev), hdmi);
+	if (ret < 0)
+		goto err_put_adapter;
+
+	pm_runtime_put_sync(dev);
+
+	return 0;
+
+err_put_adapter:
+	i2c_put_adapter(hdmi->ddc);
+err_put_runtime_pm:
+	pm_runtime_put_sync(dev);
+
+	return ret;
+}
+
+static const struct component_ops starfive_hdmi_ops = {
+	.bind	= starfive_hdmi_bind,
+};
+
+static int starfive_hdmi_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &starfive_hdmi_ops);
+}
+
+static int starfive_hdmi_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &starfive_hdmi_ops);
+
+	return 0;
+}
+
+static const struct dev_pm_ops hdmi_pm_ops = {
+	SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
+	SET_LATE_SYSTEM_SLEEP_PM_OPS(hdmi_system_pm_suspend, hdmi_system_pm_resume)
+};
+
+static const struct of_device_id starfive_hdmi_dt_ids[] = {
+	{ .compatible = "starfive,jh7110-inno-hdmi",},
+	{},
+};
+MODULE_DEVICE_TABLE(of, starfive_hdmi_dt_ids);
+
+struct platform_driver starfive_hdmi_driver = {
+	.probe  = starfive_hdmi_probe,
+	.remove = starfive_hdmi_remove,
+	.driver = {
+		.name = "starfive-hdmi",
+		.of_match_table = starfive_hdmi_dt_ids,
+		.pm = &hdmi_pm_ops,
+	},
+};
+
+MODULE_AUTHOR("StarFive Corporation");
+MODULE_DESCRIPTION("Starfive HDMI Driver");
diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.h b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
new file mode 100644
index 000000000..1b9a11bca
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
@@ -0,0 +1,295 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2023 StarFive Technology Co., Ltd.
+ */
+
+#ifndef __STARFIVE_HDMI_H__
+#define __STARFIVE_HDMI_H__
+
+#include <drm/bridge/dw_hdmi.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+
+#define DDC_SEGMENT_ADDR		0x30
+
+#define HDMI_SCL_RATE			(100 * 1000)
+#define DDC_BUS_FREQ_L			0x4b
+#define DDC_BUS_FREQ_H			0x4c
+
+#define HDMI_SYS_CTRL			0x00
+#define m_RST_ANALOG			BIT(6)
+#define v_RST_ANALOG			0
+#define v_NOT_RST_ANALOG		BIT(6)
+#define m_RST_DIGITAL			BIT(5)
+#define v_RST_DIGITAL			0
+#define v_NOT_RST_DIGITAL		BIT(5)
+#define m_REG_CLK_INV			BIT(4)
+#define v_REG_CLK_NOT_INV		0
+#define v_REG_CLK_INV			BIT(4)
+#define m_VCLK_INV			BIT(3)
+#define v_VCLK_NOT_INV			0
+#define v_VCLK_INV			BIT(3)
+#define m_REG_CLK_SOURCE		BIT(2)
+#define v_REG_CLK_SOURCE_TMDS		0
+#define v_REG_CLK_SOURCE_SYS		BIT(2)
+#define m_POWER				BIT(1)
+#define v_PWR_ON			0
+#define v_PWR_OFF			BIT(1)
+#define m_INT_POL			BIT(0)
+#define v_INT_POL_HIGH			1
+#define v_INT_POL_LOW			0
+
+#define HDMI_AV_MUTE			0x05
+#define m_AVMUTE_CLEAR			BIT(7)
+#define m_AVMUTE_ENABLE			BIT(6)
+#define m_AUDIO_MUTE			BIT(1)
+#define m_VIDEO_BLACK			BIT(0)
+#define v_AVMUTE_CLEAR(n)		((n) << 7)
+#define v_AVMUTE_ENABLE(n)		((n) << 6)
+#define v_AUDIO_MUTE(n)			((n) << 1)
+#define v_VIDEO_MUTE(n)			((n) << 0)
+
+#define HDMI_VIDEO_TIMING_CTL		0x08
+#define v_VSYNC_POLARITY(n)		((n) << 3)
+#define v_HSYNC_POLARITY(n)		((n) << 2)
+#define v_INETLACE(n)			((n) << 1)
+#define v_EXTERANL_VIDEO(n)		((n) << 0)
+
+#define HDMI_VIDEO_EXT_HTOTAL_L		0x09
+#define HDMI_VIDEO_EXT_HTOTAL_H		0x0a
+#define HDMI_VIDEO_EXT_HBLANK_L		0x0b
+#define HDMI_VIDEO_EXT_HBLANK_H		0x0c
+#define HDMI_VIDEO_EXT_HDELAY_L		0x0d
+#define HDMI_VIDEO_EXT_HDELAY_H		0x0e
+#define HDMI_VIDEO_EXT_HDURATION_L	0x0f
+#define HDMI_VIDEO_EXT_HDURATION_H	0x10
+#define HDMI_VIDEO_EXT_VTOTAL_L		0x11
+#define HDMI_VIDEO_EXT_VTOTAL_H		0x12
+#define HDMI_VIDEO_EXT_VBLANK		0x13
+#define HDMI_VIDEO_EXT_VDELAY		0x14
+#define HDMI_VIDEO_EXT_VDURATION	0x15
+
+#define HDMI_EDID_SEGMENT_POINTER	0x4d
+#define HDMI_EDID_WORD_ADDR		0x4e
+#define HDMI_EDID_FIFO_OFFSET		0x4f
+#define HDMI_EDID_FIFO_ADDR		0x50
+
+#define HDMI_INTERRUPT_MASK1		0xc0
+#define HDMI_INTERRUPT_STATUS1		0xc1
+#define	m_INT_ACTIVE_VSYNC		BIT(5)
+#define m_INT_EDID_READY		BIT(2)
+
+#define HDMI_STATUS			0xc8
+#define m_HOTPLUG			BIT(7)
+#define m_MASK_INT_HOTPLUG		BIT(5)
+#define m_INT_HOTPLUG			BIT(1)
+#define v_MASK_INT_HOTPLUG(n)		(((n) & 0x1) << 5)
+
+#define HDMI_SYNC					0xce
+
+#define UPDATE(x, h, l)					FIELD_PREP(GENMASK(h, l), x)
+
+/* REG: 0x1a0 */
+#define STARFIVE_PRE_PLL_CONTROL			0x1a0
+#define STARFIVE_PCLK_VCO_DIV_5_MASK			BIT(1)
+#define STARFIVE_PCLK_VCO_DIV_5(x)			UPDATE(x, 1, 1)
+#define STARFIVE_PRE_PLL_POWER_DOWN			BIT(0)
+
+/* REG: 0x1a1 */
+#define STARFIVE_PRE_PLL_DIV_1				0x1a1
+#define STARFIVE_PRE_PLL_PRE_DIV_MASK			GENMASK(5, 0)
+#define STARFIVE_PRE_PLL_PRE_DIV(x)			UPDATE(x, 5, 0)
+
+/* REG: 0x1a2 */
+#define STARFIVE_PRE_PLL_DIV_2				0x1a2
+#define STARFIVE_SPREAD_SPECTRUM_MOD_DOWN		BIT(7)
+#define STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE		BIT(6)
+#define STARFIVE_PRE_PLL_FRAC_DIV_DISABLE		UPDATE(3, 5, 4)
+#define STARFIVE_PRE_PLL_FB_DIV_11_8_MASK		GENMASK(3, 0)
+#define STARFIVE_PRE_PLL_FB_DIV_11_8(x)			UPDATE((x) >> 8, 3, 0)
+
+/* REG: 0x1a3 */
+#define STARFIVE_PRE_PLL_DIV_3				0x1a3
+#define STARFIVE_PRE_PLL_FB_DIV_7_0(x)			UPDATE(x, 7, 0)
+
+/* REG: 0x1a4*/
+#define STARFIVE_PRE_PLL_DIV_4				0x1a4
+#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C_MASK		GENMASK(1, 0)
+#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C(x)		UPDATE(x, 1, 0)
+#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B_MASK		GENMASK(3, 2)
+#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B(x)		UPDATE(x, 3, 2)
+#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A_MASK		GENMASK(5, 4)
+#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A(x)		UPDATE(x, 5, 4)
+
+/* REG: 0x1a5 */
+#define STARFIVE_PRE_PLL_DIV_5				0x1a5
+#define STARFIVE_PRE_PLL_PCLK_DIV_B_SHIFT		5
+#define STARFIVE_PRE_PLL_PCLK_DIV_B_MASK		GENMASK(6, 5)
+#define STARFIVE_PRE_PLL_PCLK_DIV_B(x)			UPDATE(x, 6, 5)
+#define STARFIVE_PRE_PLL_PCLK_DIV_A_MASK		GENMASK(4, 0)
+#define STARFIVE_PRE_PLL_PCLK_DIV_A(x)			UPDATE(x, 4, 0)
+
+/* REG: 0x1a6 */
+#define STARFIVE_PRE_PLL_DIV_6				0x1a6
+#define STARFIVE_PRE_PLL_PCLK_DIV_C_SHIFT		5
+#define STARFIVE_PRE_PLL_PCLK_DIV_C_MASK		GENMASK(6, 5)
+#define STARFIVE_PRE_PLL_PCLK_DIV_C(x)			UPDATE(x, 6, 5)
+#define STARFIVE_PRE_PLL_PCLK_DIV_D_MASK		GENMASK(4, 0)
+#define STARFIVE_PRE_PLL_PCLK_DIV_D(x)			UPDATE(x, 4, 0)
+
+/* REG: 0x1a9 */
+#define STARFIVE_PRE_PLL_LOCK_STATUS			0x1a9
+
+/* REG: 0x1aa */
+#define STARFIVE_POST_PLL_DIV_1				0x1aa
+#define STARFIVE_POST_PLL_POST_DIV_ENABLE		GENMASK(3, 2)
+#define STARFIVE_POST_PLL_REFCLK_SEL_TMDS		BIT(1)
+#define STARFIVE_POST_PLL_POWER_DOWN			BIT(0)
+#define STARFIVE_POST_PLL_FB_DIV_8(x)			UPDATE(((x) >> 8) << 4, 4, 4)
+
+/* REG:0x1ab */
+#define STARFIVE_POST_PLL_DIV_2				0x1ab
+#define STARFIVE_POST_PLL_Pre_DIV_MASK			GENMASK(5, 0)
+#define STARFIVE_POST_PLL_PRE_DIV(x)			UPDATE(x, 5, 0)
+
+/* REG: 0x1ac */
+#define STARFIVE_POST_PLL_DIV_3				0x1ac
+#define STARFIVE_POST_PLL_FB_DIV_7_0(x)			UPDATE(x, 7, 0)
+
+/* REG: 0x1ad */
+#define STARFIVE_POST_PLL_DIV_4				0x1ad
+#define STARFIVE_POST_PLL_POST_DIV_MASK			GENMASK(2, 0)
+#define STARFIVE_POST_PLL_POST_DIV_2			0x0
+#define STARFIVE_POST_PLL_POST_DIV_4			0x1
+#define STARFIVE_POST_PLL_POST_DIV_8			0x3
+
+/* REG: 0x1af */
+#define STARFIVE_POST_PLL_LOCK_STATUS			0x1af
+
+/* REG: 0x1b0 */
+#define STARFIVE_BIAS_CONTROL				0x1b0
+#define STARFIVE_BIAS_ENABLE				BIT(2)
+
+/* REG: 0x1b2 */
+#define STARFIVE_TMDS_CONTROL				0x1b2
+#define STARFIVE_TMDS_CLK_DRIVER_EN			BIT(3)
+#define STARFIVE_TMDS_D2_DRIVER_EN			BIT(2)
+#define STARFIVE_TMDS_D1_DRIVER_EN			BIT(1)
+#define STARFIVE_TMDS_D0_DRIVER_EN			BIT(0)
+#define STARFIVE_TMDS_DRIVER_ENABLE			(STARFIVE_TMDS_CLK_DRIVER_EN | \
+							 STARFIVE_TMDS_D2_DRIVER_EN | \
+							 STARFIVE_TMDS_D1_DRIVER_EN | \
+							 STARFIVE_TMDS_D0_DRIVER_EN)
+
+/* REG: 0x1b4 */
+#define STARFIVE_LDO_CONTROL				0x1b4
+#define STARFIVE_LDO_D2_EN				BIT(2)
+#define STARFIVE_LDO_D1_EN				BIT(1)
+#define STARFIVE_LDO_D0_EN				BIT(0)
+#define STARFIVE_LDO_ENABLE				(STARFIVE_LDO_D2_EN | \
+							 STARFIVE_LDO_D1_EN | \
+							 STARFIVE_LDO_D0_EN)
+
+/* REG: 0x1be */
+#define STARFIVE_SERIALIER_CONTROL			0x1be
+#define STARFIVE_SERIALIER_D2_EN			BIT(6)
+#define STARFIVE_SERIALIER_D1_EN			BIT(5)
+#define STARFIVE_SERIALIER_D0_EN			BIT(4)
+#define STARFIVE_SERIALIER_EN				BIT(0)
+
+#define STARFIVE_SERIALIER_ENABLE			(STARFIVE_SERIALIER_D2_EN | \
+							 STARFIVE_SERIALIER_D1_EN | \
+							 STARFIVE_SERIALIER_D0_EN | \
+							 STARFIVE_SERIALIER_EN)
+
+/* REG: 0x1cc */
+#define STARFIVE_RX_CONTROL				0x1cc
+#define STARFIVE_RX_EN					BIT(3)
+#define STARFIVE_RX_CHANNEL_2_EN			BIT(2)
+#define STARFIVE_RX_CHANNEL_1_EN			BIT(1)
+#define STARFIVE_RX_CHANNEL_0_EN			BIT(0)
+#define STARFIVE_RX_ENABLE				(STARFIVE_RX_EN | \
+							 STARFIVE_RX_CHANNEL_2_EN | \
+							 STARFIVE_RX_CHANNEL_1_EN | \
+							 STARFIVE_RX_CHANNEL_0_EN)
+
+/* REG: 0x1d1 */
+#define STARFIVE_PRE_PLL_FRAC_DIV_H			0x1d1
+#define STARFIVE_PRE_PLL_FRAC_DIV_23_16(x)		UPDATE((x) >> 16, 7, 0)
+/* REG: 0x1d2 */
+#define STARFIVE_PRE_PLL_FRAC_DIV_M			0x1d2
+#define STARFIVE_PRE_PLL_FRAC_DIV_15_8(x)		UPDATE((x) >> 8, 7, 0)
+/* REG: 0x1d3 */
+#define STARFIVE_PRE_PLL_FRAC_DIV_L			0x1d3
+#define STARFIVE_PRE_PLL_FRAC_DIV_7_0(x)		UPDATE(x, 7, 0)
+
+struct pre_pll_config {
+	unsigned long pixclock;
+	unsigned long tmdsclock;
+	u8 prediv;
+	u16 fbdiv;
+	u8 tmds_div_a;
+	u8 tmds_div_b;
+	u8 tmds_div_c;
+	u8 pclk_div_a;
+	u8 pclk_div_b;
+	u8 pclk_div_c;
+	u8 pclk_div_d;
+	u8 vco_div_5_en;
+	u32 fracdiv;
+};
+
+struct post_pll_config {
+	unsigned long tmdsclock;
+	u8 prediv;
+	u16 fbdiv;
+	u8 postdiv;
+	u8 post_div_en;
+	u8 version;
+};
+
+struct phy_config {
+	unsigned long	tmdsclock;
+	u8		regs[14];
+};
+
+struct hdmi_data_info {
+	int vic;
+	bool sink_is_hdmi;
+	bool sink_has_audio;
+	unsigned int enc_in_format;
+	unsigned int enc_out_format;
+	unsigned int colorimetry;
+};
+
+struct starfive_hdmi {
+	struct device *dev;
+	struct drm_device *drm_dev;
+	struct drm_encoder	encoder;
+	struct drm_connector	connector;
+
+	struct starfive_hdmi_i2c *i2c;
+
+	int irq;
+	struct clk *sys_clk;
+	struct clk *mclk;
+	struct clk *bclk;
+	struct reset_control *tx_rst;
+	void __iomem *regs;
+
+	struct i2c_adapter *ddc;
+
+	unsigned long tmds_rate;
+
+	struct hdmi_data_info	hdmi_data;
+	struct drm_display_mode previous_mode;
+	const struct pre_pll_config	*pre_cfg;
+	const struct post_pll_config	*post_cfg;
+};
+
+#endif /* __STARFIVE_HDMI_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
index 3cd533cfa..9669354bd 100644
--- a/drivers/gpu/drm/verisilicon/vs_drv.c
+++ b/drivers/gpu/drm/verisilicon/vs_drv.c
@@ -126,6 +126,11 @@ static const struct component_master_ops vs_drm_ops = {
 static struct platform_driver *drm_sub_drivers[] = {
 	&dc_platform_driver,
 
+	/* connector + encoder*/
+#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
+	&starfive_hdmi_driver,
+#endif
+
 };
 
 static struct component_match *vs_drm_match_add(struct device *dev)
diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
index 369ad22d6..e90723419 100644
--- a/drivers/gpu/drm/verisilicon/vs_drv.h
+++ b/drivers/gpu/drm/verisilicon/vs_drv.h
@@ -24,4 +24,8 @@ to_vs_drm_private(const struct drm_device *dev)
 	return container_of(dev, struct vs_drm_device, base);
 }
 
+#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
+extern struct platform_driver starfive_hdmi_driver;
+#endif
+
 #endif /* __VS_DRV_H__ */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 117+ messages in thread

* [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-10-25 10:39   ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-25 10:39 UTC (permalink / raw)
  To: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Keith Zhao, Jack Zhu, Shengyang Chen,
	Changhuang Liang

add hdmi driver as encoder and connect

Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
---
 drivers/gpu/drm/verisilicon/Kconfig         |   8 +-
 drivers/gpu/drm/verisilicon/Makefile        |   1 +
 drivers/gpu/drm/verisilicon/starfive_hdmi.c | 949 ++++++++++++++++++++
 drivers/gpu/drm/verisilicon/starfive_hdmi.h | 295 ++++++
 drivers/gpu/drm/verisilicon/vs_drv.c        |   5 +
 drivers/gpu/drm/verisilicon/vs_drv.h        |   4 +
 6 files changed, 1261 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.c
 create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.h

diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
index 3a361f8c8..122c786e3 100644
--- a/drivers/gpu/drm/verisilicon/Kconfig
+++ b/drivers/gpu/drm/verisilicon/Kconfig
@@ -12,4 +12,10 @@ config DRM_VERISILICON
 	  setting and buffer management. It does not
 	  provide 2D or 3D acceleration.
 
-
+config DRM_VERISILICON_STARFIVE_HDMI
+	bool "Starfive HDMI extensions"
+	depends on DRM_VERISILICON
+	help
+	   This selects support for StarFive soc specific extensions
+	   for the Innosilicon HDMI driver. If you want to enable
+	   HDMI on JH7110 based soc, you should select this option.
diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
index 1d48016ca..08350f25b 100644
--- a/drivers/gpu/drm/verisilicon/Makefile
+++ b/drivers/gpu/drm/verisilicon/Makefile
@@ -7,5 +7,6 @@ vs_drm-objs := vs_dc_hw.o \
 		vs_modeset.o \
 		vs_plane.o
 
+vs_drm-$(CONFIG_DRM_VERISILICON_STARFIVE_HDMI) += starfive_hdmi.o
 obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
 
diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.c b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
new file mode 100644
index 000000000..d296c4b71
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
@@ -0,0 +1,949 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 StarFive Technology Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/hdmi.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/media-bus-format.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#include <drm/bridge/dw_hdmi.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#include "starfive_hdmi.h"
+#include "vs_drv.h"
+#include "vs_crtc.h"
+
+static struct starfive_hdmi *encoder_to_hdmi(struct drm_encoder *encoder)
+{
+	return container_of(encoder, struct starfive_hdmi, encoder);
+}
+
+static struct starfive_hdmi *connector_to_hdmi(struct drm_connector *connector)
+{
+	return container_of(connector, struct starfive_hdmi, connector);
+}
+
+struct starfive_hdmi_i2c {
+	struct i2c_adapter adap;
+
+	u8 ddc_addr;
+	u8 segment_addr;
+	/* protects the edid data when use i2c cmd to read edid */
+	struct mutex lock;
+	struct completion cmp;
+};
+
+static const struct pre_pll_config pre_pll_cfg_table[] = {
+	{ 25175000,  25175000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0xf55555},
+	{ 25200000,  25200000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0},
+	{ 27000000,  27000000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0},
+	{ 27027000,  27027000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0x170a3d},
+	{ 28320000,  28320000, 1,  28, 2, 1, 1,  3, 0, 3, 4, 0, 0x51eb85},
+	{ 30240000,  30240000, 1,  30, 2, 1, 1,  3, 0, 3, 4, 0, 0x3d70a3},
+	{ 31500000,  31500000, 1,  31, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
+	{ 33750000,  33750000, 1,  33, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
+	{ 36000000,  36000000, 1,  36, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{ 40000000,  40000000, 1,  80, 2, 2, 2, 12, 2, 2, 2, 0, 0},
+	{ 46970000,  46970000, 1,  46, 2, 1, 1,  3, 0, 3, 4, 0, 0xf851eb},
+	{ 49500000,  49500000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
+	{ 49000000,  49000000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{ 50000000,  50000000, 1,  50, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{ 54000000,  54000000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{ 54054000,  54054000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0x0dd2f1},
+	{ 57284000,  57284000, 1,  57, 2, 1, 1,  3, 0, 3, 4, 0, 0x48b439},
+	{ 58230000,  58230000, 1,  58, 2, 1, 1,  3, 0, 3, 4, 0, 0x3ae147},
+	{ 59341000,  59341000, 1,  59, 2, 1, 1,  3, 0, 3, 4, 0, 0x574bc6},
+	{ 59400000,  59400000, 1,  99, 3, 1, 1,  1, 3, 3, 4, 0, 0},
+	{ 65000000,  65000000, 1, 130, 2, 2, 2,  12, 0, 2, 2, 0, 0},
+	{ 68250000,  68250000, 1, 68,  2, 1, 1,  3,  0, 3, 4, 0, 0x3fffff},
+	{ 71000000,  71000000, 1,  71, 2, 1, 1,  3, 0, 3,  4, 0, 0},
+	{ 74176000,  74176000, 1,  98, 1, 2, 2,  1, 2, 3, 4, 0, 0xe6ae6b},
+	{ 74250000,  74250000, 1,  99, 1, 2, 2,  1, 2, 3, 4, 0, 0},
+	{ 75000000,  75000000, 1,  75, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{ 78750000,  78750000, 1,  78, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
+	{ 79500000,  79500000, 1,  79, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
+	{ 83500000,  83500000, 2, 167, 2, 1, 1,  1, 0, 0,  6, 0, 0},
+	{ 83500000, 104375000, 1, 104, 2, 1, 1,  1, 1, 0,  5, 0, 0x600000},
+	{ 84858000,  84858000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xdba5e2},
+	{ 85500000,  85500000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
+	{ 85750000,  85750000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
+	{ 85800000,  85800000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcccccc},
+	{ 88750000,  88750000, 1,  88, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
+	{ 89910000,  89910000, 1,  89, 2, 1, 1,  3, 0, 3, 4, 0, 0xe8f5c1},
+	{ 90000000,  90000000, 1,  90, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{101000000, 101000000, 1, 101, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{102250000, 102250000, 1, 102, 2, 1, 1,  3, 0, 3, 4, 0, 0x3fffff},
+	{106500000, 106500000, 1, 106, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
+	{108000000, 108000000, 1,  90, 3, 0, 0,  5, 0, 2,  2, 0, 0},
+	{119000000, 119000000, 1, 119, 2, 1, 1,  3, 0, 3,  4, 0, 0},
+	{131481000, 131481000, 1,  131, 2, 1, 1,  3, 0, 3,  4, 0, 0x7b22d1},
+	{135000000, 135000000, 1,  135, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{136750000, 136750000, 1,  136, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
+	{147180000, 147180000, 1,  147, 2, 1, 1,  3, 0, 3, 4, 0, 0x2e147a},
+	{148352000, 148352000, 1,  98, 1, 1, 1,  1, 2, 2, 2, 0, 0xe6ae6b},
+	{148500000, 148500000, 1,  99, 1, 1, 1,  1, 2, 2, 2, 0, 0},
+	{154000000, 154000000, 1, 154, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{156000000, 156000000, 1, 156, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{157000000, 157000000, 1, 157, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{162000000, 162000000, 1, 162, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{174250000, 174250000, 1, 145, 3, 0, 0,  5, 0, 2, 2, 0, 0x355555},
+	{174500000, 174500000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
+	{174570000, 174570000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x91eb84},
+	{175500000, 175500000, 1, 175, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
+	{185590000, 185590000, 1, 185, 2, 1, 1,  3, 0, 3, 4, 0, 0x970a3c},
+	{187000000, 187000000, 1, 187, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{241500000, 241500000, 1, 161, 1, 1, 1,  4, 0, 2,  2, 0, 0},
+	{241700000, 241700000, 1, 241, 2, 1, 1,  3, 0, 3,  4, 0, 0xb33332},
+	{262750000, 262750000, 1, 262, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
+	{296500000, 296500000, 1, 296, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
+	{296703000, 296703000, 1,  98, 0, 1, 1,  1, 0, 2,  2, 0, 0xe6ae6b},
+	{297000000, 297000000, 1,  99, 0, 1, 1,  1, 0, 2,  2, 0, 0},
+	{594000000, 594000000, 1,  99, 0, 2, 0,  1, 0, 1,  1, 0, 0},
+	{0, 0, 0,  0, 0, 0, 0,  0, 0, 0,  0, 0, 0},
+};
+
+static const struct post_pll_config post_pll_cfg_table[] = {
+	{25200000,	1, 80, 13, 3, 1},
+	{27000000,	1, 40, 11, 3, 1},
+	{33750000,	1, 40, 11, 3, 1},
+	{49000000,	1, 20, 1, 3, 3},
+	{241700000, 1, 20, 1, 3, 3},
+	{297000000, 4, 20, 0, 0, 3},
+	{594000000, 4, 20, 0, 0, 0},
+	{ /* sentinel */ }
+};
+
+inline u8 hdmi_readb(struct starfive_hdmi *hdmi, u16 offset)
+{
+	return readl_relaxed(hdmi->regs + (offset) * 0x04);
+}
+
+inline void hdmi_writeb(struct starfive_hdmi *hdmi, u16 offset, u32 val)
+{
+	writel_relaxed(val, hdmi->regs + (offset) * 0x04);
+}
+
+inline void hdmi_modb(struct starfive_hdmi *hdmi, u16 offset,
+			     u32 msk, u32 val)
+{
+	u8 temp = hdmi_readb(hdmi, offset) & ~msk;
+
+	temp |= val & msk;
+	hdmi_writeb(hdmi, offset, temp);
+}
+
+static int starfive_hdmi_enable_clk_deassert_rst(struct device *dev, struct starfive_hdmi *hdmi)
+{
+	int ret;
+
+	ret = clk_prepare_enable(hdmi->sys_clk);
+	if (ret) {
+		dev_err(dev, "Cannot enable HDMI sys clock: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(hdmi->mclk);
+	if (ret) {
+		dev_err(dev, "Cannot enable HDMI mclk clock: %d\n", ret);
+		goto err_mclk;
+	}
+	ret = clk_prepare_enable(hdmi->bclk);
+	if (ret) {
+		dev_err(dev, "Cannot enable HDMI bclk clock: %d\n", ret);
+		goto err_bclk;
+	}
+	ret = reset_control_deassert(hdmi->tx_rst);
+	if (ret < 0) {
+		dev_err(dev, "failed to deassert tx_rst\n");
+		goto err_rst;
+	}
+	return 0;
+
+err_rst:
+	clk_disable_unprepare(hdmi->bclk);
+err_bclk:
+	clk_disable_unprepare(hdmi->mclk);
+err_mclk:
+	clk_disable_unprepare(hdmi->sys_clk);
+	return ret;
+}
+
+static void starfive_hdmi_disable_clk_assert_rst(struct device *dev, struct starfive_hdmi *hdmi)
+{
+	int ret;
+
+	ret = reset_control_assert(hdmi->tx_rst);
+	if (ret < 0)
+		dev_err(dev, "failed to assert tx_rst\n");
+
+	clk_disable_unprepare(hdmi->sys_clk);
+	clk_disable_unprepare(hdmi->mclk);
+	clk_disable_unprepare(hdmi->bclk);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int hdmi_system_pm_suspend(struct device *dev)
+{
+	return pm_runtime_force_suspend(dev);
+}
+
+static int hdmi_system_pm_resume(struct device *dev)
+{
+	return pm_runtime_force_resume(dev);
+}
+#endif
+
+#ifdef CONFIG_PM
+static int hdmi_runtime_suspend(struct device *dev)
+{
+	struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
+
+	starfive_hdmi_disable_clk_assert_rst(dev, hdmi);
+
+	return 0;
+}
+
+static int hdmi_runtime_resume(struct device *dev)
+{
+	struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
+
+	return starfive_hdmi_enable_clk_deassert_rst(dev, hdmi);
+}
+#endif
+
+static void starfive_hdmi_tx_phy_power_down(struct starfive_hdmi *hdmi)
+{
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
+}
+
+static void starfive_hdmi_tx_phy_power_on(struct starfive_hdmi *hdmi)
+{
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
+}
+
+static void starfive_hdmi_config_pll(struct starfive_hdmi *hdmi)
+{
+	u32 val;
+	u8 reg_1ad_value = hdmi->post_cfg->post_div_en ?
+		 hdmi->post_cfg->postdiv : 0x00;
+	u8 reg_1aa_value = hdmi->post_cfg->post_div_en ?
+		 0x0e : 0x02;
+
+	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN);
+	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1,
+		    STARFIVE_POST_PLL_POST_DIV_ENABLE |
+		    STARFIVE_POST_PLL_REFCLK_SEL_TMDS |
+		    STARFIVE_POST_PLL_POWER_DOWN);
+	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_1, STARFIVE_PRE_PLL_PRE_DIV(hdmi->pre_cfg->prediv));
+
+	val = STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE | STARFIVE_SPREAD_SPECTRUM_MOD_DOWN;
+	if (!hdmi->pre_cfg->fracdiv)
+		val |= STARFIVE_PRE_PLL_FRAC_DIV_DISABLE;
+	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_2,
+		    STARFIVE_PRE_PLL_FB_DIV_11_8(hdmi->pre_cfg->fbdiv) | val);
+	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_3,
+		    STARFIVE_PRE_PLL_FB_DIV_7_0(hdmi->pre_cfg->fbdiv));
+	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_4,
+		    STARFIVE_PRE_PLL_TMDSCLK_DIV_C(hdmi->pre_cfg->tmds_div_c) |
+		    STARFIVE_PRE_PLL_TMDSCLK_DIV_A(hdmi->pre_cfg->tmds_div_a) |
+		    STARFIVE_PRE_PLL_TMDSCLK_DIV_B(hdmi->pre_cfg->tmds_div_b));
+
+	if (hdmi->pre_cfg->fracdiv) {
+		hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_L,
+			    STARFIVE_PRE_PLL_FRAC_DIV_7_0(hdmi->pre_cfg->fracdiv));
+		hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_M,
+			    STARFIVE_PRE_PLL_FRAC_DIV_15_8(hdmi->pre_cfg->fracdiv));
+		hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_H,
+			    STARFIVE_PRE_PLL_FRAC_DIV_23_16(hdmi->pre_cfg->fracdiv));
+	}
+
+	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_5,
+		    STARFIVE_PRE_PLL_PCLK_DIV_A(hdmi->pre_cfg->pclk_div_a) |
+		    STARFIVE_PRE_PLL_PCLK_DIV_B(hdmi->pre_cfg->pclk_div_b));
+	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_6,
+		    STARFIVE_PRE_PLL_PCLK_DIV_C(hdmi->pre_cfg->pclk_div_c) |
+		    STARFIVE_PRE_PLL_PCLK_DIV_D(hdmi->pre_cfg->pclk_div_d));
+
+	/*pre-pll power down*/
+	hdmi_modb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN, 0);
+
+	hdmi_modb(hdmi, STARFIVE_POST_PLL_DIV_2, STARFIVE_POST_PLL_Pre_DIV_MASK,
+		  STARFIVE_POST_PLL_PRE_DIV(hdmi->post_cfg->prediv));
+	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_3, hdmi->post_cfg->fbdiv & 0xff);
+	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_4, reg_1ad_value);
+	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1, reg_1aa_value);
+}
+
+static void starfive_hdmi_tmds_driver_on(struct starfive_hdmi *hdmi)
+{
+	hdmi_modb(hdmi, STARFIVE_TMDS_CONTROL,
+		  STARFIVE_TMDS_DRIVER_ENABLE, STARFIVE_TMDS_DRIVER_ENABLE);
+}
+
+static void starfive_hdmi_sync_tmds(struct starfive_hdmi *hdmi)
+{
+	/*first send 0 to this bit, then send 1 and keep 1 into this bit*/
+	hdmi_writeb(hdmi, HDMI_SYNC, 0x0);
+	hdmi_writeb(hdmi, HDMI_SYNC, 0x1);
+}
+
+static void starfive_hdmi_i2c_init(struct starfive_hdmi *hdmi)
+{
+	int ddc_bus_freq;
+
+	ddc_bus_freq = (clk_get_rate(hdmi->sys_clk) >> 2) / HDMI_SCL_RATE;
+
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
+
+	/* Clear the EDID interrupt flag and mute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+}
+
+static const
+struct pre_pll_config *starfive_hdmi_phy_get_pre_pll_cfg(struct starfive_hdmi *hdmi,
+							 unsigned long rate)
+{
+	const struct pre_pll_config *cfg = pre_pll_cfg_table;
+
+	rate = (rate / 1000) * 1000;
+	for (; cfg->pixclock != 0; cfg++)
+		if (cfg->tmdsclock == rate && cfg->pixclock == rate)
+			break;
+
+	if (cfg->pixclock == 0)
+		return ERR_PTR(-EINVAL);
+
+	return cfg;
+}
+
+static int starfive_hdmi_phy_clk_set_rate(struct starfive_hdmi *hdmi)
+{
+	hdmi->post_cfg = post_pll_cfg_table;
+
+	hdmi->pre_cfg = starfive_hdmi_phy_get_pre_pll_cfg(hdmi, hdmi->tmds_rate);
+	if (IS_ERR(hdmi->pre_cfg))
+		return PTR_ERR(hdmi->pre_cfg);
+
+	for (; hdmi->post_cfg->tmdsclock != 0; hdmi->post_cfg++)
+		if (hdmi->tmds_rate <= hdmi->post_cfg->tmdsclock)
+			break;
+
+	starfive_hdmi_config_pll(hdmi);
+
+	return 0;
+}
+
+static int starfive_hdmi_config_video_timing(struct starfive_hdmi *hdmi,
+					     struct drm_display_mode *mode)
+{
+	int value;
+	/* Set detail external video timing */
+	value = mode->htotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->htotal - mode->hdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
+
+	value = mode->htotal - mode->hsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
+
+	value = mode->hsync_end - mode->hsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal - mode->vdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
+
+	value = mode->vtotal - mode->vsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
+
+	value = mode->vsync_end - mode->vsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
+
+	/* Set detail external video timing polarity and interlace mode */
+	value = v_EXTERANL_VIDEO(1);
+	value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
+		v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
+		v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
+		v_INETLACE(1) : v_INETLACE(0);
+
+	hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
+	return 0;
+}
+
+static int starfive_hdmi_setup(struct starfive_hdmi *hdmi,
+			       struct drm_display_mode *mode)
+{
+	hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
+	hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
+	hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
+
+	hdmi->tmds_rate = mode->clock * 1000;
+	starfive_hdmi_phy_clk_set_rate(hdmi);
+
+	while (!(hdmi_readb(hdmi, STARFIVE_PRE_PLL_LOCK_STATUS) & 0x1))
+		continue;
+	while (!(hdmi_readb(hdmi, STARFIVE_POST_PLL_LOCK_STATUS) & 0x1))
+		continue;
+
+	/*turn on LDO*/
+	hdmi_writeb(hdmi, STARFIVE_LDO_CONTROL, STARFIVE_LDO_ENABLE);
+	/*turn on serializer*/
+	hdmi_writeb(hdmi, STARFIVE_SERIALIER_CONTROL, STARFIVE_SERIALIER_ENABLE);
+
+	starfive_hdmi_tx_phy_power_down(hdmi);
+	starfive_hdmi_config_video_timing(hdmi, mode);
+	starfive_hdmi_tx_phy_power_on(hdmi);
+
+	starfive_hdmi_tmds_driver_on(hdmi);
+	starfive_hdmi_sync_tmds(hdmi);
+
+	return 0;
+}
+
+static void starfive_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+					   struct drm_display_mode *mode,
+					   struct drm_display_mode *adj_mode)
+{
+	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
+
+	drm_mode_copy(&hdmi->previous_mode, adj_mode);
+}
+
+static void starfive_hdmi_encoder_enable(struct drm_encoder *encoder)
+{
+	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
+	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
+	int ret, idx;
+	struct drm_device *drm = hdmi->connector.dev;
+
+	if (drm && !drm_dev_enter(drm, &idx))
+		return;
+
+	ret = pm_runtime_get_sync(hdmi->dev);
+	if (ret < 0)
+		return;
+	mdelay(10);
+	starfive_hdmi_setup(hdmi, mode);
+
+	if (drm)
+		drm_dev_exit(idx);
+}
+
+static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
+
+	int idx;
+	struct drm_device *drm = hdmi->connector.dev;
+
+	if (drm && !drm_dev_enter(drm, &idx))
+		return;
+
+	pm_runtime_put(hdmi->dev);
+
+	if (drm)
+		drm_dev_exit(idx);
+}
+
+static int
+starfive_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
+				   struct drm_crtc_state *crtc_state,
+				   struct drm_connector_state *conn_state)
+{
+	bool valid = false;
+	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
+	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
+
+	vs_crtc_state->encoder_type = encoder->encoder_type;
+	vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
+
+	const struct pre_pll_config *cfg = pre_pll_cfg_table;
+	int pclk = mode->clock * 1000;
+
+	for (; cfg->pixclock != 0; cfg++) {
+		if (pclk == cfg->pixclock) {
+			if (pclk > 297000000)
+				continue;
+
+			valid = true;
+			break;
+		}
+	}
+
+	return (valid) ? 0 : -EINVAL;
+}
+
+static const struct drm_encoder_helper_funcs starfive_hdmi_encoder_helper_funcs = {
+	.enable     = starfive_hdmi_encoder_enable,
+	.disable    = starfive_hdmi_encoder_disable,
+	.mode_set   = starfive_hdmi_encoder_mode_set,
+	.atomic_check = starfive_hdmi_encoder_atomic_check,
+};
+
+static enum drm_connector_status
+starfive_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
+	struct drm_device *drm = hdmi->connector.dev;
+	int ret;
+	int idx;
+
+	if (drm && !drm_dev_enter(drm, &idx))
+		return connector_status_disconnected;
+
+	ret = pm_runtime_get_sync(hdmi->dev);
+	if (ret < 0)
+		return ret;
+
+	ret = (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
+		connector_status_connected : connector_status_disconnected;
+	pm_runtime_put(hdmi->dev);
+
+	if (drm)
+		drm_dev_exit(idx);
+
+	return ret;
+}
+
+static int starfive_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+	struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
+	struct edid *edid;
+	int ret = 0;
+
+	if (!hdmi->ddc)
+		return 0;
+	ret = pm_runtime_get_sync(hdmi->dev);
+	if (ret < 0)
+		return ret;
+
+	edid = drm_get_edid(connector, hdmi->ddc);
+	if (edid) {
+		hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
+		hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
+		drm_connector_update_edid_property(connector, edid);
+		ret = drm_add_edid_modes(connector, edid);
+		kfree(edid);
+	}
+	pm_runtime_put(hdmi->dev);
+
+	return ret;
+}
+
+static enum drm_mode_status
+starfive_hdmi_connector_mode_valid(struct drm_connector *connector,
+				   struct drm_display_mode *mode)
+{
+	const struct pre_pll_config *cfg = pre_pll_cfg_table;
+	int pclk = mode->clock * 1000;
+	bool valid = false;
+
+	for (; cfg->pixclock != 0; cfg++) {
+		if (pclk == cfg->pixclock) {
+			if (pclk > 297000000)
+				continue;
+
+			valid = true;
+			break;
+		}
+	}
+
+	return (valid) ? MODE_OK : MODE_BAD;
+}
+
+static int
+starfive_hdmi_probe_single_connector_modes(struct drm_connector *connector,
+					   u32 maxX, u32 maxY)
+{
+	return drm_helper_probe_single_connector_modes(connector, 3840, 2160);
+}
+
+static const struct drm_connector_funcs starfive_hdmi_connector_funcs = {
+	.fill_modes = starfive_hdmi_probe_single_connector_modes,
+	.detect = starfive_hdmi_connector_detect,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static struct drm_connector_helper_funcs starfive_hdmi_connector_helper_funcs = {
+	.get_modes = starfive_hdmi_connector_get_modes,
+	.mode_valid = starfive_hdmi_connector_mode_valid,
+};
+
+static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
+{
+	struct drm_encoder *encoder = &hdmi->encoder;
+	struct device *dev = hdmi->dev;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+
+	/*
+	 * If we failed to find the CRTC(s) which this encoder is
+	 * supposed to be connected to, it's because the CRTC has
+	 * not been registered yet.  Defer probing, and hope that
+	 * the required CRTC is added later.
+	 */
+	if (encoder->possible_crtcs == 0)
+		return -EPROBE_DEFER;
+
+	drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
+
+	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+	drm_connector_helper_add(&hdmi->connector,
+				 &starfive_hdmi_connector_helper_funcs);
+	drmm_connector_init(drm, &hdmi->connector,
+			    &starfive_hdmi_connector_funcs,
+			    DRM_MODE_CONNECTOR_HDMIA,
+			    hdmi->ddc);
+
+	drm_connector_attach_encoder(&hdmi->connector, encoder);
+
+	return 0;
+}
+
+static irqreturn_t starfive_hdmi_i2c_irq(struct starfive_hdmi *hdmi)
+{
+	struct starfive_hdmi_i2c *i2c = hdmi->i2c;
+	u8 stat;
+
+	stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
+	if (!(stat & m_INT_EDID_READY))
+		return IRQ_NONE;
+
+	/* Clear HDMI EDID interrupt flag */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+	complete(&i2c->cmp);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t starfive_hdmi_hardirq(int irq, void *dev_id)
+{
+	struct starfive_hdmi *hdmi = dev_id;
+	irqreturn_t ret = IRQ_NONE;
+	u8 interrupt;
+
+	if (hdmi->i2c)
+		ret = starfive_hdmi_i2c_irq(hdmi);
+
+	interrupt = hdmi_readb(hdmi, HDMI_STATUS);
+	if (interrupt & m_INT_HOTPLUG) {
+		hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	return ret;
+}
+
+static irqreturn_t starfive_hdmi_irq(int irq, void *dev_id)
+{
+	struct starfive_hdmi *hdmi = dev_id;
+
+	drm_connector_helper_hpd_irq_event(&hdmi->connector);
+
+	return IRQ_HANDLED;
+}
+
+static int starfive_hdmi_i2c_read(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	int length = msgs->len;
+	u8 *buf = msgs->buf;
+	int ret;
+
+	ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10);
+	if (!ret)
+		return -EAGAIN;
+
+	while (length--)
+		*buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
+
+	return 0;
+}
+
+static int starfive_hdmi_i2c_write(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	/*
+	 * The DDC module only support read EDID message, so
+	 * we assume that each word write to this i2c adapter
+	 * should be the offset of EDID word address.
+	 */
+	if (msgs->len != 1 ||
+	    (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR))
+		return -EINVAL;
+
+	reinit_completion(&hdmi->i2c->cmp);
+
+	if (msgs->addr == DDC_SEGMENT_ADDR)
+		hdmi->i2c->segment_addr = msgs->buf[0];
+	if (msgs->addr == DDC_ADDR)
+		hdmi->i2c->ddc_addr = msgs->buf[0];
+
+	/* Set edid fifo first addr */
+	hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
+
+	/* Set edid word address 0x00/0x80 */
+	hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
+
+	/* Set edid segment pointer */
+	hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
+
+	return 0;
+}
+
+static int starfive_hdmi_i2c_xfer(struct i2c_adapter *adap,
+				  struct i2c_msg *msgs, int num)
+{
+	struct starfive_hdmi *hdmi = i2c_get_adapdata(adap);
+	struct starfive_hdmi_i2c *i2c = hdmi->i2c;
+	int i, ret = 0;
+
+	mutex_lock(&i2c->lock);
+
+	/* Clear the EDID interrupt flag and unmute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+	for (i = 0; i < num; i++) {
+		DRM_DEV_DEBUG(hdmi->dev,
+			      "xfer: num: %d/%d, len: %d, flags: %#x\n",
+			      i + 1, num, msgs[i].len, msgs[i].flags);
+
+		if (msgs[i].flags & I2C_M_RD)
+			ret = starfive_hdmi_i2c_read(hdmi, &msgs[i]);
+		else
+			ret = starfive_hdmi_i2c_write(hdmi, &msgs[i]);
+
+		if (ret < 0)
+			break;
+	}
+
+	if (!ret)
+		ret = num;
+
+	/* Mute HDMI EDID interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+
+	mutex_unlock(&i2c->lock);
+
+	return ret;
+}
+
+static u32 starfive_hdmi_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm starfive_hdmi_algorithm = {
+	.master_xfer	= starfive_hdmi_i2c_xfer,
+	.functionality	= starfive_hdmi_i2c_func,
+};
+
+static struct i2c_adapter *starfive_hdmi_i2c_adapter(struct starfive_hdmi *hdmi)
+{
+	struct i2c_adapter *adap;
+	struct starfive_hdmi_i2c *i2c;
+	int ret;
+
+	i2c = drmm_kzalloc(hdmi->drm_dev, sizeof(*i2c), GFP_KERNEL);
+	if (!i2c)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&i2c->lock);
+	init_completion(&i2c->cmp);
+
+	adap = &i2c->adap;
+	adap->class = I2C_CLASS_DDC;
+	adap->owner = THIS_MODULE;
+	adap->dev.parent = hdmi->dev;
+	adap->algo = &starfive_hdmi_algorithm;
+	strscpy(adap->name, "Starfive HDMI", sizeof(adap->name));
+	i2c_set_adapdata(adap, hdmi);
+
+	ret = devm_i2c_add_adapter(hdmi->dev, adap);
+	if (ret) {
+		drm_warn(hdmi->drm_dev, "cannot add %s I2C adapter\n", adap->name);
+		return ERR_PTR(ret);
+	}
+
+	hdmi->i2c = i2c;
+
+	drm_info(hdmi->drm_dev, "registered %s I2C bus driver success\n", adap->name);
+
+	return adap;
+}
+
+static int starfive_hdmi_get_clk_rst(struct device *dev, struct starfive_hdmi *hdmi)
+{
+	hdmi->sys_clk = devm_clk_get(dev, "sysclk");
+	if (IS_ERR(hdmi->sys_clk)) {
+		dev_err(dev, "Unable to get HDMI sysclk clk\n");
+		return PTR_ERR(hdmi->sys_clk);
+	}
+	hdmi->mclk = devm_clk_get(dev, "mclk");
+	if (IS_ERR(hdmi->mclk)) {
+		dev_err(dev, "Unable to get HDMI mclk clk\n");
+		return PTR_ERR(hdmi->mclk);
+	}
+	hdmi->bclk = devm_clk_get(dev, "bclk");
+	if (IS_ERR(hdmi->bclk)) {
+		dev_err(dev, "Unable to get HDMI bclk clk\n");
+		return PTR_ERR(hdmi->bclk);
+	}
+	hdmi->tx_rst = devm_reset_control_get_by_index(dev, 0);
+	if (IS_ERR(hdmi->tx_rst)) {
+		dev_err(dev, "failed to get tx_rst reset\n");
+		return PTR_ERR(hdmi->tx_rst);
+	}
+
+	return 0;
+}
+
+static int starfive_hdmi_bind(struct device *dev, struct device *master,
+			      void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = dev_get_drvdata(master);
+	struct starfive_hdmi *hdmi;
+	struct resource *iores;
+	int irq;
+	int ret;
+
+	hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return -ENOMEM;
+
+	hdmi = drmm_simple_encoder_alloc(drm, struct starfive_hdmi,
+					 encoder, DRM_MODE_ENCODER_TMDS);
+
+	hdmi->dev = dev;
+	hdmi->drm_dev = drm;
+	dev_set_drvdata(dev, hdmi);
+
+	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	hdmi->regs = devm_ioremap_resource(dev, iores);
+	if (IS_ERR(hdmi->regs))
+		return PTR_ERR(hdmi->regs);
+
+	ret = starfive_hdmi_get_clk_rst(dev, hdmi);
+	if (ret < 0)
+		return ret;
+
+	ret = devm_pm_runtime_enable(dev);
+	if (ret)
+		return ret;
+
+	ret = pm_runtime_resume_and_get(dev);
+	if (ret)
+		return ret;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		ret = irq;
+		goto err_put_runtime_pm;
+	}
+
+	hdmi->ddc = starfive_hdmi_i2c_adapter(hdmi);
+	if (IS_ERR(hdmi->ddc)) {
+		ret = PTR_ERR(hdmi->ddc);
+		hdmi->ddc = NULL;
+		goto err_put_runtime_pm;
+	}
+
+	starfive_hdmi_i2c_init(hdmi);
+
+	ret = starfive_hdmi_register(drm, hdmi);
+	if (ret)
+		goto err_put_adapter;
+
+	/* Unmute hotplug interrupt */
+	hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
+
+	ret = devm_request_threaded_irq(dev, irq, starfive_hdmi_hardirq,
+					starfive_hdmi_irq, IRQF_SHARED,
+					dev_name(dev), hdmi);
+	if (ret < 0)
+		goto err_put_adapter;
+
+	pm_runtime_put_sync(dev);
+
+	return 0;
+
+err_put_adapter:
+	i2c_put_adapter(hdmi->ddc);
+err_put_runtime_pm:
+	pm_runtime_put_sync(dev);
+
+	return ret;
+}
+
+static const struct component_ops starfive_hdmi_ops = {
+	.bind	= starfive_hdmi_bind,
+};
+
+static int starfive_hdmi_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &starfive_hdmi_ops);
+}
+
+static int starfive_hdmi_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &starfive_hdmi_ops);
+
+	return 0;
+}
+
+static const struct dev_pm_ops hdmi_pm_ops = {
+	SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
+	SET_LATE_SYSTEM_SLEEP_PM_OPS(hdmi_system_pm_suspend, hdmi_system_pm_resume)
+};
+
+static const struct of_device_id starfive_hdmi_dt_ids[] = {
+	{ .compatible = "starfive,jh7110-inno-hdmi",},
+	{},
+};
+MODULE_DEVICE_TABLE(of, starfive_hdmi_dt_ids);
+
+struct platform_driver starfive_hdmi_driver = {
+	.probe  = starfive_hdmi_probe,
+	.remove = starfive_hdmi_remove,
+	.driver = {
+		.name = "starfive-hdmi",
+		.of_match_table = starfive_hdmi_dt_ids,
+		.pm = &hdmi_pm_ops,
+	},
+};
+
+MODULE_AUTHOR("StarFive Corporation");
+MODULE_DESCRIPTION("Starfive HDMI Driver");
diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.h b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
new file mode 100644
index 000000000..1b9a11bca
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
@@ -0,0 +1,295 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2023 StarFive Technology Co., Ltd.
+ */
+
+#ifndef __STARFIVE_HDMI_H__
+#define __STARFIVE_HDMI_H__
+
+#include <drm/bridge/dw_hdmi.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+
+#define DDC_SEGMENT_ADDR		0x30
+
+#define HDMI_SCL_RATE			(100 * 1000)
+#define DDC_BUS_FREQ_L			0x4b
+#define DDC_BUS_FREQ_H			0x4c
+
+#define HDMI_SYS_CTRL			0x00
+#define m_RST_ANALOG			BIT(6)
+#define v_RST_ANALOG			0
+#define v_NOT_RST_ANALOG		BIT(6)
+#define m_RST_DIGITAL			BIT(5)
+#define v_RST_DIGITAL			0
+#define v_NOT_RST_DIGITAL		BIT(5)
+#define m_REG_CLK_INV			BIT(4)
+#define v_REG_CLK_NOT_INV		0
+#define v_REG_CLK_INV			BIT(4)
+#define m_VCLK_INV			BIT(3)
+#define v_VCLK_NOT_INV			0
+#define v_VCLK_INV			BIT(3)
+#define m_REG_CLK_SOURCE		BIT(2)
+#define v_REG_CLK_SOURCE_TMDS		0
+#define v_REG_CLK_SOURCE_SYS		BIT(2)
+#define m_POWER				BIT(1)
+#define v_PWR_ON			0
+#define v_PWR_OFF			BIT(1)
+#define m_INT_POL			BIT(0)
+#define v_INT_POL_HIGH			1
+#define v_INT_POL_LOW			0
+
+#define HDMI_AV_MUTE			0x05
+#define m_AVMUTE_CLEAR			BIT(7)
+#define m_AVMUTE_ENABLE			BIT(6)
+#define m_AUDIO_MUTE			BIT(1)
+#define m_VIDEO_BLACK			BIT(0)
+#define v_AVMUTE_CLEAR(n)		((n) << 7)
+#define v_AVMUTE_ENABLE(n)		((n) << 6)
+#define v_AUDIO_MUTE(n)			((n) << 1)
+#define v_VIDEO_MUTE(n)			((n) << 0)
+
+#define HDMI_VIDEO_TIMING_CTL		0x08
+#define v_VSYNC_POLARITY(n)		((n) << 3)
+#define v_HSYNC_POLARITY(n)		((n) << 2)
+#define v_INETLACE(n)			((n) << 1)
+#define v_EXTERANL_VIDEO(n)		((n) << 0)
+
+#define HDMI_VIDEO_EXT_HTOTAL_L		0x09
+#define HDMI_VIDEO_EXT_HTOTAL_H		0x0a
+#define HDMI_VIDEO_EXT_HBLANK_L		0x0b
+#define HDMI_VIDEO_EXT_HBLANK_H		0x0c
+#define HDMI_VIDEO_EXT_HDELAY_L		0x0d
+#define HDMI_VIDEO_EXT_HDELAY_H		0x0e
+#define HDMI_VIDEO_EXT_HDURATION_L	0x0f
+#define HDMI_VIDEO_EXT_HDURATION_H	0x10
+#define HDMI_VIDEO_EXT_VTOTAL_L		0x11
+#define HDMI_VIDEO_EXT_VTOTAL_H		0x12
+#define HDMI_VIDEO_EXT_VBLANK		0x13
+#define HDMI_VIDEO_EXT_VDELAY		0x14
+#define HDMI_VIDEO_EXT_VDURATION	0x15
+
+#define HDMI_EDID_SEGMENT_POINTER	0x4d
+#define HDMI_EDID_WORD_ADDR		0x4e
+#define HDMI_EDID_FIFO_OFFSET		0x4f
+#define HDMI_EDID_FIFO_ADDR		0x50
+
+#define HDMI_INTERRUPT_MASK1		0xc0
+#define HDMI_INTERRUPT_STATUS1		0xc1
+#define	m_INT_ACTIVE_VSYNC		BIT(5)
+#define m_INT_EDID_READY		BIT(2)
+
+#define HDMI_STATUS			0xc8
+#define m_HOTPLUG			BIT(7)
+#define m_MASK_INT_HOTPLUG		BIT(5)
+#define m_INT_HOTPLUG			BIT(1)
+#define v_MASK_INT_HOTPLUG(n)		(((n) & 0x1) << 5)
+
+#define HDMI_SYNC					0xce
+
+#define UPDATE(x, h, l)					FIELD_PREP(GENMASK(h, l), x)
+
+/* REG: 0x1a0 */
+#define STARFIVE_PRE_PLL_CONTROL			0x1a0
+#define STARFIVE_PCLK_VCO_DIV_5_MASK			BIT(1)
+#define STARFIVE_PCLK_VCO_DIV_5(x)			UPDATE(x, 1, 1)
+#define STARFIVE_PRE_PLL_POWER_DOWN			BIT(0)
+
+/* REG: 0x1a1 */
+#define STARFIVE_PRE_PLL_DIV_1				0x1a1
+#define STARFIVE_PRE_PLL_PRE_DIV_MASK			GENMASK(5, 0)
+#define STARFIVE_PRE_PLL_PRE_DIV(x)			UPDATE(x, 5, 0)
+
+/* REG: 0x1a2 */
+#define STARFIVE_PRE_PLL_DIV_2				0x1a2
+#define STARFIVE_SPREAD_SPECTRUM_MOD_DOWN		BIT(7)
+#define STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE		BIT(6)
+#define STARFIVE_PRE_PLL_FRAC_DIV_DISABLE		UPDATE(3, 5, 4)
+#define STARFIVE_PRE_PLL_FB_DIV_11_8_MASK		GENMASK(3, 0)
+#define STARFIVE_PRE_PLL_FB_DIV_11_8(x)			UPDATE((x) >> 8, 3, 0)
+
+/* REG: 0x1a3 */
+#define STARFIVE_PRE_PLL_DIV_3				0x1a3
+#define STARFIVE_PRE_PLL_FB_DIV_7_0(x)			UPDATE(x, 7, 0)
+
+/* REG: 0x1a4*/
+#define STARFIVE_PRE_PLL_DIV_4				0x1a4
+#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C_MASK		GENMASK(1, 0)
+#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C(x)		UPDATE(x, 1, 0)
+#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B_MASK		GENMASK(3, 2)
+#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B(x)		UPDATE(x, 3, 2)
+#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A_MASK		GENMASK(5, 4)
+#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A(x)		UPDATE(x, 5, 4)
+
+/* REG: 0x1a5 */
+#define STARFIVE_PRE_PLL_DIV_5				0x1a5
+#define STARFIVE_PRE_PLL_PCLK_DIV_B_SHIFT		5
+#define STARFIVE_PRE_PLL_PCLK_DIV_B_MASK		GENMASK(6, 5)
+#define STARFIVE_PRE_PLL_PCLK_DIV_B(x)			UPDATE(x, 6, 5)
+#define STARFIVE_PRE_PLL_PCLK_DIV_A_MASK		GENMASK(4, 0)
+#define STARFIVE_PRE_PLL_PCLK_DIV_A(x)			UPDATE(x, 4, 0)
+
+/* REG: 0x1a6 */
+#define STARFIVE_PRE_PLL_DIV_6				0x1a6
+#define STARFIVE_PRE_PLL_PCLK_DIV_C_SHIFT		5
+#define STARFIVE_PRE_PLL_PCLK_DIV_C_MASK		GENMASK(6, 5)
+#define STARFIVE_PRE_PLL_PCLK_DIV_C(x)			UPDATE(x, 6, 5)
+#define STARFIVE_PRE_PLL_PCLK_DIV_D_MASK		GENMASK(4, 0)
+#define STARFIVE_PRE_PLL_PCLK_DIV_D(x)			UPDATE(x, 4, 0)
+
+/* REG: 0x1a9 */
+#define STARFIVE_PRE_PLL_LOCK_STATUS			0x1a9
+
+/* REG: 0x1aa */
+#define STARFIVE_POST_PLL_DIV_1				0x1aa
+#define STARFIVE_POST_PLL_POST_DIV_ENABLE		GENMASK(3, 2)
+#define STARFIVE_POST_PLL_REFCLK_SEL_TMDS		BIT(1)
+#define STARFIVE_POST_PLL_POWER_DOWN			BIT(0)
+#define STARFIVE_POST_PLL_FB_DIV_8(x)			UPDATE(((x) >> 8) << 4, 4, 4)
+
+/* REG:0x1ab */
+#define STARFIVE_POST_PLL_DIV_2				0x1ab
+#define STARFIVE_POST_PLL_Pre_DIV_MASK			GENMASK(5, 0)
+#define STARFIVE_POST_PLL_PRE_DIV(x)			UPDATE(x, 5, 0)
+
+/* REG: 0x1ac */
+#define STARFIVE_POST_PLL_DIV_3				0x1ac
+#define STARFIVE_POST_PLL_FB_DIV_7_0(x)			UPDATE(x, 7, 0)
+
+/* REG: 0x1ad */
+#define STARFIVE_POST_PLL_DIV_4				0x1ad
+#define STARFIVE_POST_PLL_POST_DIV_MASK			GENMASK(2, 0)
+#define STARFIVE_POST_PLL_POST_DIV_2			0x0
+#define STARFIVE_POST_PLL_POST_DIV_4			0x1
+#define STARFIVE_POST_PLL_POST_DIV_8			0x3
+
+/* REG: 0x1af */
+#define STARFIVE_POST_PLL_LOCK_STATUS			0x1af
+
+/* REG: 0x1b0 */
+#define STARFIVE_BIAS_CONTROL				0x1b0
+#define STARFIVE_BIAS_ENABLE				BIT(2)
+
+/* REG: 0x1b2 */
+#define STARFIVE_TMDS_CONTROL				0x1b2
+#define STARFIVE_TMDS_CLK_DRIVER_EN			BIT(3)
+#define STARFIVE_TMDS_D2_DRIVER_EN			BIT(2)
+#define STARFIVE_TMDS_D1_DRIVER_EN			BIT(1)
+#define STARFIVE_TMDS_D0_DRIVER_EN			BIT(0)
+#define STARFIVE_TMDS_DRIVER_ENABLE			(STARFIVE_TMDS_CLK_DRIVER_EN | \
+							 STARFIVE_TMDS_D2_DRIVER_EN | \
+							 STARFIVE_TMDS_D1_DRIVER_EN | \
+							 STARFIVE_TMDS_D0_DRIVER_EN)
+
+/* REG: 0x1b4 */
+#define STARFIVE_LDO_CONTROL				0x1b4
+#define STARFIVE_LDO_D2_EN				BIT(2)
+#define STARFIVE_LDO_D1_EN				BIT(1)
+#define STARFIVE_LDO_D0_EN				BIT(0)
+#define STARFIVE_LDO_ENABLE				(STARFIVE_LDO_D2_EN | \
+							 STARFIVE_LDO_D1_EN | \
+							 STARFIVE_LDO_D0_EN)
+
+/* REG: 0x1be */
+#define STARFIVE_SERIALIER_CONTROL			0x1be
+#define STARFIVE_SERIALIER_D2_EN			BIT(6)
+#define STARFIVE_SERIALIER_D1_EN			BIT(5)
+#define STARFIVE_SERIALIER_D0_EN			BIT(4)
+#define STARFIVE_SERIALIER_EN				BIT(0)
+
+#define STARFIVE_SERIALIER_ENABLE			(STARFIVE_SERIALIER_D2_EN | \
+							 STARFIVE_SERIALIER_D1_EN | \
+							 STARFIVE_SERIALIER_D0_EN | \
+							 STARFIVE_SERIALIER_EN)
+
+/* REG: 0x1cc */
+#define STARFIVE_RX_CONTROL				0x1cc
+#define STARFIVE_RX_EN					BIT(3)
+#define STARFIVE_RX_CHANNEL_2_EN			BIT(2)
+#define STARFIVE_RX_CHANNEL_1_EN			BIT(1)
+#define STARFIVE_RX_CHANNEL_0_EN			BIT(0)
+#define STARFIVE_RX_ENABLE				(STARFIVE_RX_EN | \
+							 STARFIVE_RX_CHANNEL_2_EN | \
+							 STARFIVE_RX_CHANNEL_1_EN | \
+							 STARFIVE_RX_CHANNEL_0_EN)
+
+/* REG: 0x1d1 */
+#define STARFIVE_PRE_PLL_FRAC_DIV_H			0x1d1
+#define STARFIVE_PRE_PLL_FRAC_DIV_23_16(x)		UPDATE((x) >> 16, 7, 0)
+/* REG: 0x1d2 */
+#define STARFIVE_PRE_PLL_FRAC_DIV_M			0x1d2
+#define STARFIVE_PRE_PLL_FRAC_DIV_15_8(x)		UPDATE((x) >> 8, 7, 0)
+/* REG: 0x1d3 */
+#define STARFIVE_PRE_PLL_FRAC_DIV_L			0x1d3
+#define STARFIVE_PRE_PLL_FRAC_DIV_7_0(x)		UPDATE(x, 7, 0)
+
+struct pre_pll_config {
+	unsigned long pixclock;
+	unsigned long tmdsclock;
+	u8 prediv;
+	u16 fbdiv;
+	u8 tmds_div_a;
+	u8 tmds_div_b;
+	u8 tmds_div_c;
+	u8 pclk_div_a;
+	u8 pclk_div_b;
+	u8 pclk_div_c;
+	u8 pclk_div_d;
+	u8 vco_div_5_en;
+	u32 fracdiv;
+};
+
+struct post_pll_config {
+	unsigned long tmdsclock;
+	u8 prediv;
+	u16 fbdiv;
+	u8 postdiv;
+	u8 post_div_en;
+	u8 version;
+};
+
+struct phy_config {
+	unsigned long	tmdsclock;
+	u8		regs[14];
+};
+
+struct hdmi_data_info {
+	int vic;
+	bool sink_is_hdmi;
+	bool sink_has_audio;
+	unsigned int enc_in_format;
+	unsigned int enc_out_format;
+	unsigned int colorimetry;
+};
+
+struct starfive_hdmi {
+	struct device *dev;
+	struct drm_device *drm_dev;
+	struct drm_encoder	encoder;
+	struct drm_connector	connector;
+
+	struct starfive_hdmi_i2c *i2c;
+
+	int irq;
+	struct clk *sys_clk;
+	struct clk *mclk;
+	struct clk *bclk;
+	struct reset_control *tx_rst;
+	void __iomem *regs;
+
+	struct i2c_adapter *ddc;
+
+	unsigned long tmds_rate;
+
+	struct hdmi_data_info	hdmi_data;
+	struct drm_display_mode previous_mode;
+	const struct pre_pll_config	*pre_cfg;
+	const struct post_pll_config	*post_cfg;
+};
+
+#endif /* __STARFIVE_HDMI_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
index 3cd533cfa..9669354bd 100644
--- a/drivers/gpu/drm/verisilicon/vs_drv.c
+++ b/drivers/gpu/drm/verisilicon/vs_drv.c
@@ -126,6 +126,11 @@ static const struct component_master_ops vs_drm_ops = {
 static struct platform_driver *drm_sub_drivers[] = {
 	&dc_platform_driver,
 
+	/* connector + encoder*/
+#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
+	&starfive_hdmi_driver,
+#endif
+
 };
 
 static struct component_match *vs_drm_match_add(struct device *dev)
diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
index 369ad22d6..e90723419 100644
--- a/drivers/gpu/drm/verisilicon/vs_drv.h
+++ b/drivers/gpu/drm/verisilicon/vs_drv.h
@@ -24,4 +24,8 @@ to_vs_drm_private(const struct drm_device *dev)
 	return container_of(dev, struct vs_drm_device, base);
 }
 
+#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
+extern struct platform_driver starfive_hdmi_driver;
+#endif
+
 #endif /* __VS_DRV_H__ */
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 117+ messages in thread

* [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-10-25 10:39   ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-25 10:39 UTC (permalink / raw)
  To: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Keith Zhao, Bjorn Andersson, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Shawn Guo, christian.koenig

add hdmi driver as encoder and connect

Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
---
 drivers/gpu/drm/verisilicon/Kconfig         |   8 +-
 drivers/gpu/drm/verisilicon/Makefile        |   1 +
 drivers/gpu/drm/verisilicon/starfive_hdmi.c | 949 ++++++++++++++++++++
 drivers/gpu/drm/verisilicon/starfive_hdmi.h | 295 ++++++
 drivers/gpu/drm/verisilicon/vs_drv.c        |   5 +
 drivers/gpu/drm/verisilicon/vs_drv.h        |   4 +
 6 files changed, 1261 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.c
 create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.h

diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
index 3a361f8c8..122c786e3 100644
--- a/drivers/gpu/drm/verisilicon/Kconfig
+++ b/drivers/gpu/drm/verisilicon/Kconfig
@@ -12,4 +12,10 @@ config DRM_VERISILICON
 	  setting and buffer management. It does not
 	  provide 2D or 3D acceleration.
 
-
+config DRM_VERISILICON_STARFIVE_HDMI
+	bool "Starfive HDMI extensions"
+	depends on DRM_VERISILICON
+	help
+	   This selects support for StarFive soc specific extensions
+	   for the Innosilicon HDMI driver. If you want to enable
+	   HDMI on JH7110 based soc, you should select this option.
diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
index 1d48016ca..08350f25b 100644
--- a/drivers/gpu/drm/verisilicon/Makefile
+++ b/drivers/gpu/drm/verisilicon/Makefile
@@ -7,5 +7,6 @@ vs_drm-objs := vs_dc_hw.o \
 		vs_modeset.o \
 		vs_plane.o
 
+vs_drm-$(CONFIG_DRM_VERISILICON_STARFIVE_HDMI) += starfive_hdmi.o
 obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
 
diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.c b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
new file mode 100644
index 000000000..d296c4b71
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
@@ -0,0 +1,949 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 StarFive Technology Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/hdmi.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/media-bus-format.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#include <drm/bridge/dw_hdmi.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#include "starfive_hdmi.h"
+#include "vs_drv.h"
+#include "vs_crtc.h"
+
+static struct starfive_hdmi *encoder_to_hdmi(struct drm_encoder *encoder)
+{
+	return container_of(encoder, struct starfive_hdmi, encoder);
+}
+
+static struct starfive_hdmi *connector_to_hdmi(struct drm_connector *connector)
+{
+	return container_of(connector, struct starfive_hdmi, connector);
+}
+
+struct starfive_hdmi_i2c {
+	struct i2c_adapter adap;
+
+	u8 ddc_addr;
+	u8 segment_addr;
+	/* protects the edid data when use i2c cmd to read edid */
+	struct mutex lock;
+	struct completion cmp;
+};
+
+static const struct pre_pll_config pre_pll_cfg_table[] = {
+	{ 25175000,  25175000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0xf55555},
+	{ 25200000,  25200000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0},
+	{ 27000000,  27000000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0},
+	{ 27027000,  27027000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0x170a3d},
+	{ 28320000,  28320000, 1,  28, 2, 1, 1,  3, 0, 3, 4, 0, 0x51eb85},
+	{ 30240000,  30240000, 1,  30, 2, 1, 1,  3, 0, 3, 4, 0, 0x3d70a3},
+	{ 31500000,  31500000, 1,  31, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
+	{ 33750000,  33750000, 1,  33, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
+	{ 36000000,  36000000, 1,  36, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{ 40000000,  40000000, 1,  80, 2, 2, 2, 12, 2, 2, 2, 0, 0},
+	{ 46970000,  46970000, 1,  46, 2, 1, 1,  3, 0, 3, 4, 0, 0xf851eb},
+	{ 49500000,  49500000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
+	{ 49000000,  49000000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{ 50000000,  50000000, 1,  50, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{ 54000000,  54000000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{ 54054000,  54054000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0x0dd2f1},
+	{ 57284000,  57284000, 1,  57, 2, 1, 1,  3, 0, 3, 4, 0, 0x48b439},
+	{ 58230000,  58230000, 1,  58, 2, 1, 1,  3, 0, 3, 4, 0, 0x3ae147},
+	{ 59341000,  59341000, 1,  59, 2, 1, 1,  3, 0, 3, 4, 0, 0x574bc6},
+	{ 59400000,  59400000, 1,  99, 3, 1, 1,  1, 3, 3, 4, 0, 0},
+	{ 65000000,  65000000, 1, 130, 2, 2, 2,  12, 0, 2, 2, 0, 0},
+	{ 68250000,  68250000, 1, 68,  2, 1, 1,  3,  0, 3, 4, 0, 0x3fffff},
+	{ 71000000,  71000000, 1,  71, 2, 1, 1,  3, 0, 3,  4, 0, 0},
+	{ 74176000,  74176000, 1,  98, 1, 2, 2,  1, 2, 3, 4, 0, 0xe6ae6b},
+	{ 74250000,  74250000, 1,  99, 1, 2, 2,  1, 2, 3, 4, 0, 0},
+	{ 75000000,  75000000, 1,  75, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{ 78750000,  78750000, 1,  78, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
+	{ 79500000,  79500000, 1,  79, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
+	{ 83500000,  83500000, 2, 167, 2, 1, 1,  1, 0, 0,  6, 0, 0},
+	{ 83500000, 104375000, 1, 104, 2, 1, 1,  1, 1, 0,  5, 0, 0x600000},
+	{ 84858000,  84858000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xdba5e2},
+	{ 85500000,  85500000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
+	{ 85750000,  85750000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
+	{ 85800000,  85800000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcccccc},
+	{ 88750000,  88750000, 1,  88, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
+	{ 89910000,  89910000, 1,  89, 2, 1, 1,  3, 0, 3, 4, 0, 0xe8f5c1},
+	{ 90000000,  90000000, 1,  90, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{101000000, 101000000, 1, 101, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{102250000, 102250000, 1, 102, 2, 1, 1,  3, 0, 3, 4, 0, 0x3fffff},
+	{106500000, 106500000, 1, 106, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
+	{108000000, 108000000, 1,  90, 3, 0, 0,  5, 0, 2,  2, 0, 0},
+	{119000000, 119000000, 1, 119, 2, 1, 1,  3, 0, 3,  4, 0, 0},
+	{131481000, 131481000, 1,  131, 2, 1, 1,  3, 0, 3,  4, 0, 0x7b22d1},
+	{135000000, 135000000, 1,  135, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{136750000, 136750000, 1,  136, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
+	{147180000, 147180000, 1,  147, 2, 1, 1,  3, 0, 3, 4, 0, 0x2e147a},
+	{148352000, 148352000, 1,  98, 1, 1, 1,  1, 2, 2, 2, 0, 0xe6ae6b},
+	{148500000, 148500000, 1,  99, 1, 1, 1,  1, 2, 2, 2, 0, 0},
+	{154000000, 154000000, 1, 154, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{156000000, 156000000, 1, 156, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{157000000, 157000000, 1, 157, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{162000000, 162000000, 1, 162, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{174250000, 174250000, 1, 145, 3, 0, 0,  5, 0, 2, 2, 0, 0x355555},
+	{174500000, 174500000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
+	{174570000, 174570000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x91eb84},
+	{175500000, 175500000, 1, 175, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
+	{185590000, 185590000, 1, 185, 2, 1, 1,  3, 0, 3, 4, 0, 0x970a3c},
+	{187000000, 187000000, 1, 187, 2, 1, 1,  3, 0, 3, 4, 0, 0},
+	{241500000, 241500000, 1, 161, 1, 1, 1,  4, 0, 2,  2, 0, 0},
+	{241700000, 241700000, 1, 241, 2, 1, 1,  3, 0, 3,  4, 0, 0xb33332},
+	{262750000, 262750000, 1, 262, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
+	{296500000, 296500000, 1, 296, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
+	{296703000, 296703000, 1,  98, 0, 1, 1,  1, 0, 2,  2, 0, 0xe6ae6b},
+	{297000000, 297000000, 1,  99, 0, 1, 1,  1, 0, 2,  2, 0, 0},
+	{594000000, 594000000, 1,  99, 0, 2, 0,  1, 0, 1,  1, 0, 0},
+	{0, 0, 0,  0, 0, 0, 0,  0, 0, 0,  0, 0, 0},
+};
+
+static const struct post_pll_config post_pll_cfg_table[] = {
+	{25200000,	1, 80, 13, 3, 1},
+	{27000000,	1, 40, 11, 3, 1},
+	{33750000,	1, 40, 11, 3, 1},
+	{49000000,	1, 20, 1, 3, 3},
+	{241700000, 1, 20, 1, 3, 3},
+	{297000000, 4, 20, 0, 0, 3},
+	{594000000, 4, 20, 0, 0, 0},
+	{ /* sentinel */ }
+};
+
+inline u8 hdmi_readb(struct starfive_hdmi *hdmi, u16 offset)
+{
+	return readl_relaxed(hdmi->regs + (offset) * 0x04);
+}
+
+inline void hdmi_writeb(struct starfive_hdmi *hdmi, u16 offset, u32 val)
+{
+	writel_relaxed(val, hdmi->regs + (offset) * 0x04);
+}
+
+inline void hdmi_modb(struct starfive_hdmi *hdmi, u16 offset,
+			     u32 msk, u32 val)
+{
+	u8 temp = hdmi_readb(hdmi, offset) & ~msk;
+
+	temp |= val & msk;
+	hdmi_writeb(hdmi, offset, temp);
+}
+
+static int starfive_hdmi_enable_clk_deassert_rst(struct device *dev, struct starfive_hdmi *hdmi)
+{
+	int ret;
+
+	ret = clk_prepare_enable(hdmi->sys_clk);
+	if (ret) {
+		dev_err(dev, "Cannot enable HDMI sys clock: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(hdmi->mclk);
+	if (ret) {
+		dev_err(dev, "Cannot enable HDMI mclk clock: %d\n", ret);
+		goto err_mclk;
+	}
+	ret = clk_prepare_enable(hdmi->bclk);
+	if (ret) {
+		dev_err(dev, "Cannot enable HDMI bclk clock: %d\n", ret);
+		goto err_bclk;
+	}
+	ret = reset_control_deassert(hdmi->tx_rst);
+	if (ret < 0) {
+		dev_err(dev, "failed to deassert tx_rst\n");
+		goto err_rst;
+	}
+	return 0;
+
+err_rst:
+	clk_disable_unprepare(hdmi->bclk);
+err_bclk:
+	clk_disable_unprepare(hdmi->mclk);
+err_mclk:
+	clk_disable_unprepare(hdmi->sys_clk);
+	return ret;
+}
+
+static void starfive_hdmi_disable_clk_assert_rst(struct device *dev, struct starfive_hdmi *hdmi)
+{
+	int ret;
+
+	ret = reset_control_assert(hdmi->tx_rst);
+	if (ret < 0)
+		dev_err(dev, "failed to assert tx_rst\n");
+
+	clk_disable_unprepare(hdmi->sys_clk);
+	clk_disable_unprepare(hdmi->mclk);
+	clk_disable_unprepare(hdmi->bclk);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int hdmi_system_pm_suspend(struct device *dev)
+{
+	return pm_runtime_force_suspend(dev);
+}
+
+static int hdmi_system_pm_resume(struct device *dev)
+{
+	return pm_runtime_force_resume(dev);
+}
+#endif
+
+#ifdef CONFIG_PM
+static int hdmi_runtime_suspend(struct device *dev)
+{
+	struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
+
+	starfive_hdmi_disable_clk_assert_rst(dev, hdmi);
+
+	return 0;
+}
+
+static int hdmi_runtime_resume(struct device *dev)
+{
+	struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
+
+	return starfive_hdmi_enable_clk_deassert_rst(dev, hdmi);
+}
+#endif
+
+static void starfive_hdmi_tx_phy_power_down(struct starfive_hdmi *hdmi)
+{
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
+}
+
+static void starfive_hdmi_tx_phy_power_on(struct starfive_hdmi *hdmi)
+{
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
+}
+
+static void starfive_hdmi_config_pll(struct starfive_hdmi *hdmi)
+{
+	u32 val;
+	u8 reg_1ad_value = hdmi->post_cfg->post_div_en ?
+		 hdmi->post_cfg->postdiv : 0x00;
+	u8 reg_1aa_value = hdmi->post_cfg->post_div_en ?
+		 0x0e : 0x02;
+
+	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN);
+	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1,
+		    STARFIVE_POST_PLL_POST_DIV_ENABLE |
+		    STARFIVE_POST_PLL_REFCLK_SEL_TMDS |
+		    STARFIVE_POST_PLL_POWER_DOWN);
+	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_1, STARFIVE_PRE_PLL_PRE_DIV(hdmi->pre_cfg->prediv));
+
+	val = STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE | STARFIVE_SPREAD_SPECTRUM_MOD_DOWN;
+	if (!hdmi->pre_cfg->fracdiv)
+		val |= STARFIVE_PRE_PLL_FRAC_DIV_DISABLE;
+	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_2,
+		    STARFIVE_PRE_PLL_FB_DIV_11_8(hdmi->pre_cfg->fbdiv) | val);
+	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_3,
+		    STARFIVE_PRE_PLL_FB_DIV_7_0(hdmi->pre_cfg->fbdiv));
+	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_4,
+		    STARFIVE_PRE_PLL_TMDSCLK_DIV_C(hdmi->pre_cfg->tmds_div_c) |
+		    STARFIVE_PRE_PLL_TMDSCLK_DIV_A(hdmi->pre_cfg->tmds_div_a) |
+		    STARFIVE_PRE_PLL_TMDSCLK_DIV_B(hdmi->pre_cfg->tmds_div_b));
+
+	if (hdmi->pre_cfg->fracdiv) {
+		hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_L,
+			    STARFIVE_PRE_PLL_FRAC_DIV_7_0(hdmi->pre_cfg->fracdiv));
+		hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_M,
+			    STARFIVE_PRE_PLL_FRAC_DIV_15_8(hdmi->pre_cfg->fracdiv));
+		hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_H,
+			    STARFIVE_PRE_PLL_FRAC_DIV_23_16(hdmi->pre_cfg->fracdiv));
+	}
+
+	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_5,
+		    STARFIVE_PRE_PLL_PCLK_DIV_A(hdmi->pre_cfg->pclk_div_a) |
+		    STARFIVE_PRE_PLL_PCLK_DIV_B(hdmi->pre_cfg->pclk_div_b));
+	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_6,
+		    STARFIVE_PRE_PLL_PCLK_DIV_C(hdmi->pre_cfg->pclk_div_c) |
+		    STARFIVE_PRE_PLL_PCLK_DIV_D(hdmi->pre_cfg->pclk_div_d));
+
+	/*pre-pll power down*/
+	hdmi_modb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN, 0);
+
+	hdmi_modb(hdmi, STARFIVE_POST_PLL_DIV_2, STARFIVE_POST_PLL_Pre_DIV_MASK,
+		  STARFIVE_POST_PLL_PRE_DIV(hdmi->post_cfg->prediv));
+	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_3, hdmi->post_cfg->fbdiv & 0xff);
+	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_4, reg_1ad_value);
+	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1, reg_1aa_value);
+}
+
+static void starfive_hdmi_tmds_driver_on(struct starfive_hdmi *hdmi)
+{
+	hdmi_modb(hdmi, STARFIVE_TMDS_CONTROL,
+		  STARFIVE_TMDS_DRIVER_ENABLE, STARFIVE_TMDS_DRIVER_ENABLE);
+}
+
+static void starfive_hdmi_sync_tmds(struct starfive_hdmi *hdmi)
+{
+	/*first send 0 to this bit, then send 1 and keep 1 into this bit*/
+	hdmi_writeb(hdmi, HDMI_SYNC, 0x0);
+	hdmi_writeb(hdmi, HDMI_SYNC, 0x1);
+}
+
+static void starfive_hdmi_i2c_init(struct starfive_hdmi *hdmi)
+{
+	int ddc_bus_freq;
+
+	ddc_bus_freq = (clk_get_rate(hdmi->sys_clk) >> 2) / HDMI_SCL_RATE;
+
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
+
+	/* Clear the EDID interrupt flag and mute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+}
+
+static const
+struct pre_pll_config *starfive_hdmi_phy_get_pre_pll_cfg(struct starfive_hdmi *hdmi,
+							 unsigned long rate)
+{
+	const struct pre_pll_config *cfg = pre_pll_cfg_table;
+
+	rate = (rate / 1000) * 1000;
+	for (; cfg->pixclock != 0; cfg++)
+		if (cfg->tmdsclock == rate && cfg->pixclock == rate)
+			break;
+
+	if (cfg->pixclock == 0)
+		return ERR_PTR(-EINVAL);
+
+	return cfg;
+}
+
+static int starfive_hdmi_phy_clk_set_rate(struct starfive_hdmi *hdmi)
+{
+	hdmi->post_cfg = post_pll_cfg_table;
+
+	hdmi->pre_cfg = starfive_hdmi_phy_get_pre_pll_cfg(hdmi, hdmi->tmds_rate);
+	if (IS_ERR(hdmi->pre_cfg))
+		return PTR_ERR(hdmi->pre_cfg);
+
+	for (; hdmi->post_cfg->tmdsclock != 0; hdmi->post_cfg++)
+		if (hdmi->tmds_rate <= hdmi->post_cfg->tmdsclock)
+			break;
+
+	starfive_hdmi_config_pll(hdmi);
+
+	return 0;
+}
+
+static int starfive_hdmi_config_video_timing(struct starfive_hdmi *hdmi,
+					     struct drm_display_mode *mode)
+{
+	int value;
+	/* Set detail external video timing */
+	value = mode->htotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->htotal - mode->hdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
+
+	value = mode->htotal - mode->hsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
+
+	value = mode->hsync_end - mode->hsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal - mode->vdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
+
+	value = mode->vtotal - mode->vsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
+
+	value = mode->vsync_end - mode->vsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
+
+	/* Set detail external video timing polarity and interlace mode */
+	value = v_EXTERANL_VIDEO(1);
+	value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
+		v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
+		v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
+		v_INETLACE(1) : v_INETLACE(0);
+
+	hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
+	return 0;
+}
+
+static int starfive_hdmi_setup(struct starfive_hdmi *hdmi,
+			       struct drm_display_mode *mode)
+{
+	hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
+	hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
+	hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
+
+	hdmi->tmds_rate = mode->clock * 1000;
+	starfive_hdmi_phy_clk_set_rate(hdmi);
+
+	while (!(hdmi_readb(hdmi, STARFIVE_PRE_PLL_LOCK_STATUS) & 0x1))
+		continue;
+	while (!(hdmi_readb(hdmi, STARFIVE_POST_PLL_LOCK_STATUS) & 0x1))
+		continue;
+
+	/*turn on LDO*/
+	hdmi_writeb(hdmi, STARFIVE_LDO_CONTROL, STARFIVE_LDO_ENABLE);
+	/*turn on serializer*/
+	hdmi_writeb(hdmi, STARFIVE_SERIALIER_CONTROL, STARFIVE_SERIALIER_ENABLE);
+
+	starfive_hdmi_tx_phy_power_down(hdmi);
+	starfive_hdmi_config_video_timing(hdmi, mode);
+	starfive_hdmi_tx_phy_power_on(hdmi);
+
+	starfive_hdmi_tmds_driver_on(hdmi);
+	starfive_hdmi_sync_tmds(hdmi);
+
+	return 0;
+}
+
+static void starfive_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+					   struct drm_display_mode *mode,
+					   struct drm_display_mode *adj_mode)
+{
+	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
+
+	drm_mode_copy(&hdmi->previous_mode, adj_mode);
+}
+
+static void starfive_hdmi_encoder_enable(struct drm_encoder *encoder)
+{
+	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
+	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
+	int ret, idx;
+	struct drm_device *drm = hdmi->connector.dev;
+
+	if (drm && !drm_dev_enter(drm, &idx))
+		return;
+
+	ret = pm_runtime_get_sync(hdmi->dev);
+	if (ret < 0)
+		return;
+	mdelay(10);
+	starfive_hdmi_setup(hdmi, mode);
+
+	if (drm)
+		drm_dev_exit(idx);
+}
+
+static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
+
+	int idx;
+	struct drm_device *drm = hdmi->connector.dev;
+
+	if (drm && !drm_dev_enter(drm, &idx))
+		return;
+
+	pm_runtime_put(hdmi->dev);
+
+	if (drm)
+		drm_dev_exit(idx);
+}
+
+static int
+starfive_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
+				   struct drm_crtc_state *crtc_state,
+				   struct drm_connector_state *conn_state)
+{
+	bool valid = false;
+	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
+	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
+
+	vs_crtc_state->encoder_type = encoder->encoder_type;
+	vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
+
+	const struct pre_pll_config *cfg = pre_pll_cfg_table;
+	int pclk = mode->clock * 1000;
+
+	for (; cfg->pixclock != 0; cfg++) {
+		if (pclk == cfg->pixclock) {
+			if (pclk > 297000000)
+				continue;
+
+			valid = true;
+			break;
+		}
+	}
+
+	return (valid) ? 0 : -EINVAL;
+}
+
+static const struct drm_encoder_helper_funcs starfive_hdmi_encoder_helper_funcs = {
+	.enable     = starfive_hdmi_encoder_enable,
+	.disable    = starfive_hdmi_encoder_disable,
+	.mode_set   = starfive_hdmi_encoder_mode_set,
+	.atomic_check = starfive_hdmi_encoder_atomic_check,
+};
+
+static enum drm_connector_status
+starfive_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
+	struct drm_device *drm = hdmi->connector.dev;
+	int ret;
+	int idx;
+
+	if (drm && !drm_dev_enter(drm, &idx))
+		return connector_status_disconnected;
+
+	ret = pm_runtime_get_sync(hdmi->dev);
+	if (ret < 0)
+		return ret;
+
+	ret = (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
+		connector_status_connected : connector_status_disconnected;
+	pm_runtime_put(hdmi->dev);
+
+	if (drm)
+		drm_dev_exit(idx);
+
+	return ret;
+}
+
+static int starfive_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+	struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
+	struct edid *edid;
+	int ret = 0;
+
+	if (!hdmi->ddc)
+		return 0;
+	ret = pm_runtime_get_sync(hdmi->dev);
+	if (ret < 0)
+		return ret;
+
+	edid = drm_get_edid(connector, hdmi->ddc);
+	if (edid) {
+		hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
+		hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
+		drm_connector_update_edid_property(connector, edid);
+		ret = drm_add_edid_modes(connector, edid);
+		kfree(edid);
+	}
+	pm_runtime_put(hdmi->dev);
+
+	return ret;
+}
+
+static enum drm_mode_status
+starfive_hdmi_connector_mode_valid(struct drm_connector *connector,
+				   struct drm_display_mode *mode)
+{
+	const struct pre_pll_config *cfg = pre_pll_cfg_table;
+	int pclk = mode->clock * 1000;
+	bool valid = false;
+
+	for (; cfg->pixclock != 0; cfg++) {
+		if (pclk == cfg->pixclock) {
+			if (pclk > 297000000)
+				continue;
+
+			valid = true;
+			break;
+		}
+	}
+
+	return (valid) ? MODE_OK : MODE_BAD;
+}
+
+static int
+starfive_hdmi_probe_single_connector_modes(struct drm_connector *connector,
+					   u32 maxX, u32 maxY)
+{
+	return drm_helper_probe_single_connector_modes(connector, 3840, 2160);
+}
+
+static const struct drm_connector_funcs starfive_hdmi_connector_funcs = {
+	.fill_modes = starfive_hdmi_probe_single_connector_modes,
+	.detect = starfive_hdmi_connector_detect,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static struct drm_connector_helper_funcs starfive_hdmi_connector_helper_funcs = {
+	.get_modes = starfive_hdmi_connector_get_modes,
+	.mode_valid = starfive_hdmi_connector_mode_valid,
+};
+
+static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
+{
+	struct drm_encoder *encoder = &hdmi->encoder;
+	struct device *dev = hdmi->dev;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+
+	/*
+	 * If we failed to find the CRTC(s) which this encoder is
+	 * supposed to be connected to, it's because the CRTC has
+	 * not been registered yet.  Defer probing, and hope that
+	 * the required CRTC is added later.
+	 */
+	if (encoder->possible_crtcs == 0)
+		return -EPROBE_DEFER;
+
+	drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
+
+	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+	drm_connector_helper_add(&hdmi->connector,
+				 &starfive_hdmi_connector_helper_funcs);
+	drmm_connector_init(drm, &hdmi->connector,
+			    &starfive_hdmi_connector_funcs,
+			    DRM_MODE_CONNECTOR_HDMIA,
+			    hdmi->ddc);
+
+	drm_connector_attach_encoder(&hdmi->connector, encoder);
+
+	return 0;
+}
+
+static irqreturn_t starfive_hdmi_i2c_irq(struct starfive_hdmi *hdmi)
+{
+	struct starfive_hdmi_i2c *i2c = hdmi->i2c;
+	u8 stat;
+
+	stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
+	if (!(stat & m_INT_EDID_READY))
+		return IRQ_NONE;
+
+	/* Clear HDMI EDID interrupt flag */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+	complete(&i2c->cmp);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t starfive_hdmi_hardirq(int irq, void *dev_id)
+{
+	struct starfive_hdmi *hdmi = dev_id;
+	irqreturn_t ret = IRQ_NONE;
+	u8 interrupt;
+
+	if (hdmi->i2c)
+		ret = starfive_hdmi_i2c_irq(hdmi);
+
+	interrupt = hdmi_readb(hdmi, HDMI_STATUS);
+	if (interrupt & m_INT_HOTPLUG) {
+		hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	return ret;
+}
+
+static irqreturn_t starfive_hdmi_irq(int irq, void *dev_id)
+{
+	struct starfive_hdmi *hdmi = dev_id;
+
+	drm_connector_helper_hpd_irq_event(&hdmi->connector);
+
+	return IRQ_HANDLED;
+}
+
+static int starfive_hdmi_i2c_read(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	int length = msgs->len;
+	u8 *buf = msgs->buf;
+	int ret;
+
+	ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10);
+	if (!ret)
+		return -EAGAIN;
+
+	while (length--)
+		*buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
+
+	return 0;
+}
+
+static int starfive_hdmi_i2c_write(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	/*
+	 * The DDC module only support read EDID message, so
+	 * we assume that each word write to this i2c adapter
+	 * should be the offset of EDID word address.
+	 */
+	if (msgs->len != 1 ||
+	    (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR))
+		return -EINVAL;
+
+	reinit_completion(&hdmi->i2c->cmp);
+
+	if (msgs->addr == DDC_SEGMENT_ADDR)
+		hdmi->i2c->segment_addr = msgs->buf[0];
+	if (msgs->addr == DDC_ADDR)
+		hdmi->i2c->ddc_addr = msgs->buf[0];
+
+	/* Set edid fifo first addr */
+	hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
+
+	/* Set edid word address 0x00/0x80 */
+	hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
+
+	/* Set edid segment pointer */
+	hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
+
+	return 0;
+}
+
+static int starfive_hdmi_i2c_xfer(struct i2c_adapter *adap,
+				  struct i2c_msg *msgs, int num)
+{
+	struct starfive_hdmi *hdmi = i2c_get_adapdata(adap);
+	struct starfive_hdmi_i2c *i2c = hdmi->i2c;
+	int i, ret = 0;
+
+	mutex_lock(&i2c->lock);
+
+	/* Clear the EDID interrupt flag and unmute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+	for (i = 0; i < num; i++) {
+		DRM_DEV_DEBUG(hdmi->dev,
+			      "xfer: num: %d/%d, len: %d, flags: %#x\n",
+			      i + 1, num, msgs[i].len, msgs[i].flags);
+
+		if (msgs[i].flags & I2C_M_RD)
+			ret = starfive_hdmi_i2c_read(hdmi, &msgs[i]);
+		else
+			ret = starfive_hdmi_i2c_write(hdmi, &msgs[i]);
+
+		if (ret < 0)
+			break;
+	}
+
+	if (!ret)
+		ret = num;
+
+	/* Mute HDMI EDID interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+
+	mutex_unlock(&i2c->lock);
+
+	return ret;
+}
+
+static u32 starfive_hdmi_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm starfive_hdmi_algorithm = {
+	.master_xfer	= starfive_hdmi_i2c_xfer,
+	.functionality	= starfive_hdmi_i2c_func,
+};
+
+static struct i2c_adapter *starfive_hdmi_i2c_adapter(struct starfive_hdmi *hdmi)
+{
+	struct i2c_adapter *adap;
+	struct starfive_hdmi_i2c *i2c;
+	int ret;
+
+	i2c = drmm_kzalloc(hdmi->drm_dev, sizeof(*i2c), GFP_KERNEL);
+	if (!i2c)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&i2c->lock);
+	init_completion(&i2c->cmp);
+
+	adap = &i2c->adap;
+	adap->class = I2C_CLASS_DDC;
+	adap->owner = THIS_MODULE;
+	adap->dev.parent = hdmi->dev;
+	adap->algo = &starfive_hdmi_algorithm;
+	strscpy(adap->name, "Starfive HDMI", sizeof(adap->name));
+	i2c_set_adapdata(adap, hdmi);
+
+	ret = devm_i2c_add_adapter(hdmi->dev, adap);
+	if (ret) {
+		drm_warn(hdmi->drm_dev, "cannot add %s I2C adapter\n", adap->name);
+		return ERR_PTR(ret);
+	}
+
+	hdmi->i2c = i2c;
+
+	drm_info(hdmi->drm_dev, "registered %s I2C bus driver success\n", adap->name);
+
+	return adap;
+}
+
+static int starfive_hdmi_get_clk_rst(struct device *dev, struct starfive_hdmi *hdmi)
+{
+	hdmi->sys_clk = devm_clk_get(dev, "sysclk");
+	if (IS_ERR(hdmi->sys_clk)) {
+		dev_err(dev, "Unable to get HDMI sysclk clk\n");
+		return PTR_ERR(hdmi->sys_clk);
+	}
+	hdmi->mclk = devm_clk_get(dev, "mclk");
+	if (IS_ERR(hdmi->mclk)) {
+		dev_err(dev, "Unable to get HDMI mclk clk\n");
+		return PTR_ERR(hdmi->mclk);
+	}
+	hdmi->bclk = devm_clk_get(dev, "bclk");
+	if (IS_ERR(hdmi->bclk)) {
+		dev_err(dev, "Unable to get HDMI bclk clk\n");
+		return PTR_ERR(hdmi->bclk);
+	}
+	hdmi->tx_rst = devm_reset_control_get_by_index(dev, 0);
+	if (IS_ERR(hdmi->tx_rst)) {
+		dev_err(dev, "failed to get tx_rst reset\n");
+		return PTR_ERR(hdmi->tx_rst);
+	}
+
+	return 0;
+}
+
+static int starfive_hdmi_bind(struct device *dev, struct device *master,
+			      void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = dev_get_drvdata(master);
+	struct starfive_hdmi *hdmi;
+	struct resource *iores;
+	int irq;
+	int ret;
+
+	hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return -ENOMEM;
+
+	hdmi = drmm_simple_encoder_alloc(drm, struct starfive_hdmi,
+					 encoder, DRM_MODE_ENCODER_TMDS);
+
+	hdmi->dev = dev;
+	hdmi->drm_dev = drm;
+	dev_set_drvdata(dev, hdmi);
+
+	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	hdmi->regs = devm_ioremap_resource(dev, iores);
+	if (IS_ERR(hdmi->regs))
+		return PTR_ERR(hdmi->regs);
+
+	ret = starfive_hdmi_get_clk_rst(dev, hdmi);
+	if (ret < 0)
+		return ret;
+
+	ret = devm_pm_runtime_enable(dev);
+	if (ret)
+		return ret;
+
+	ret = pm_runtime_resume_and_get(dev);
+	if (ret)
+		return ret;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		ret = irq;
+		goto err_put_runtime_pm;
+	}
+
+	hdmi->ddc = starfive_hdmi_i2c_adapter(hdmi);
+	if (IS_ERR(hdmi->ddc)) {
+		ret = PTR_ERR(hdmi->ddc);
+		hdmi->ddc = NULL;
+		goto err_put_runtime_pm;
+	}
+
+	starfive_hdmi_i2c_init(hdmi);
+
+	ret = starfive_hdmi_register(drm, hdmi);
+	if (ret)
+		goto err_put_adapter;
+
+	/* Unmute hotplug interrupt */
+	hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
+
+	ret = devm_request_threaded_irq(dev, irq, starfive_hdmi_hardirq,
+					starfive_hdmi_irq, IRQF_SHARED,
+					dev_name(dev), hdmi);
+	if (ret < 0)
+		goto err_put_adapter;
+
+	pm_runtime_put_sync(dev);
+
+	return 0;
+
+err_put_adapter:
+	i2c_put_adapter(hdmi->ddc);
+err_put_runtime_pm:
+	pm_runtime_put_sync(dev);
+
+	return ret;
+}
+
+static const struct component_ops starfive_hdmi_ops = {
+	.bind	= starfive_hdmi_bind,
+};
+
+static int starfive_hdmi_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &starfive_hdmi_ops);
+}
+
+static int starfive_hdmi_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &starfive_hdmi_ops);
+
+	return 0;
+}
+
+static const struct dev_pm_ops hdmi_pm_ops = {
+	SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
+	SET_LATE_SYSTEM_SLEEP_PM_OPS(hdmi_system_pm_suspend, hdmi_system_pm_resume)
+};
+
+static const struct of_device_id starfive_hdmi_dt_ids[] = {
+	{ .compatible = "starfive,jh7110-inno-hdmi",},
+	{},
+};
+MODULE_DEVICE_TABLE(of, starfive_hdmi_dt_ids);
+
+struct platform_driver starfive_hdmi_driver = {
+	.probe  = starfive_hdmi_probe,
+	.remove = starfive_hdmi_remove,
+	.driver = {
+		.name = "starfive-hdmi",
+		.of_match_table = starfive_hdmi_dt_ids,
+		.pm = &hdmi_pm_ops,
+	},
+};
+
+MODULE_AUTHOR("StarFive Corporation");
+MODULE_DESCRIPTION("Starfive HDMI Driver");
diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.h b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
new file mode 100644
index 000000000..1b9a11bca
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
@@ -0,0 +1,295 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2023 StarFive Technology Co., Ltd.
+ */
+
+#ifndef __STARFIVE_HDMI_H__
+#define __STARFIVE_HDMI_H__
+
+#include <drm/bridge/dw_hdmi.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+
+#define DDC_SEGMENT_ADDR		0x30
+
+#define HDMI_SCL_RATE			(100 * 1000)
+#define DDC_BUS_FREQ_L			0x4b
+#define DDC_BUS_FREQ_H			0x4c
+
+#define HDMI_SYS_CTRL			0x00
+#define m_RST_ANALOG			BIT(6)
+#define v_RST_ANALOG			0
+#define v_NOT_RST_ANALOG		BIT(6)
+#define m_RST_DIGITAL			BIT(5)
+#define v_RST_DIGITAL			0
+#define v_NOT_RST_DIGITAL		BIT(5)
+#define m_REG_CLK_INV			BIT(4)
+#define v_REG_CLK_NOT_INV		0
+#define v_REG_CLK_INV			BIT(4)
+#define m_VCLK_INV			BIT(3)
+#define v_VCLK_NOT_INV			0
+#define v_VCLK_INV			BIT(3)
+#define m_REG_CLK_SOURCE		BIT(2)
+#define v_REG_CLK_SOURCE_TMDS		0
+#define v_REG_CLK_SOURCE_SYS		BIT(2)
+#define m_POWER				BIT(1)
+#define v_PWR_ON			0
+#define v_PWR_OFF			BIT(1)
+#define m_INT_POL			BIT(0)
+#define v_INT_POL_HIGH			1
+#define v_INT_POL_LOW			0
+
+#define HDMI_AV_MUTE			0x05
+#define m_AVMUTE_CLEAR			BIT(7)
+#define m_AVMUTE_ENABLE			BIT(6)
+#define m_AUDIO_MUTE			BIT(1)
+#define m_VIDEO_BLACK			BIT(0)
+#define v_AVMUTE_CLEAR(n)		((n) << 7)
+#define v_AVMUTE_ENABLE(n)		((n) << 6)
+#define v_AUDIO_MUTE(n)			((n) << 1)
+#define v_VIDEO_MUTE(n)			((n) << 0)
+
+#define HDMI_VIDEO_TIMING_CTL		0x08
+#define v_VSYNC_POLARITY(n)		((n) << 3)
+#define v_HSYNC_POLARITY(n)		((n) << 2)
+#define v_INETLACE(n)			((n) << 1)
+#define v_EXTERANL_VIDEO(n)		((n) << 0)
+
+#define HDMI_VIDEO_EXT_HTOTAL_L		0x09
+#define HDMI_VIDEO_EXT_HTOTAL_H		0x0a
+#define HDMI_VIDEO_EXT_HBLANK_L		0x0b
+#define HDMI_VIDEO_EXT_HBLANK_H		0x0c
+#define HDMI_VIDEO_EXT_HDELAY_L		0x0d
+#define HDMI_VIDEO_EXT_HDELAY_H		0x0e
+#define HDMI_VIDEO_EXT_HDURATION_L	0x0f
+#define HDMI_VIDEO_EXT_HDURATION_H	0x10
+#define HDMI_VIDEO_EXT_VTOTAL_L		0x11
+#define HDMI_VIDEO_EXT_VTOTAL_H		0x12
+#define HDMI_VIDEO_EXT_VBLANK		0x13
+#define HDMI_VIDEO_EXT_VDELAY		0x14
+#define HDMI_VIDEO_EXT_VDURATION	0x15
+
+#define HDMI_EDID_SEGMENT_POINTER	0x4d
+#define HDMI_EDID_WORD_ADDR		0x4e
+#define HDMI_EDID_FIFO_OFFSET		0x4f
+#define HDMI_EDID_FIFO_ADDR		0x50
+
+#define HDMI_INTERRUPT_MASK1		0xc0
+#define HDMI_INTERRUPT_STATUS1		0xc1
+#define	m_INT_ACTIVE_VSYNC		BIT(5)
+#define m_INT_EDID_READY		BIT(2)
+
+#define HDMI_STATUS			0xc8
+#define m_HOTPLUG			BIT(7)
+#define m_MASK_INT_HOTPLUG		BIT(5)
+#define m_INT_HOTPLUG			BIT(1)
+#define v_MASK_INT_HOTPLUG(n)		(((n) & 0x1) << 5)
+
+#define HDMI_SYNC					0xce
+
+#define UPDATE(x, h, l)					FIELD_PREP(GENMASK(h, l), x)
+
+/* REG: 0x1a0 */
+#define STARFIVE_PRE_PLL_CONTROL			0x1a0
+#define STARFIVE_PCLK_VCO_DIV_5_MASK			BIT(1)
+#define STARFIVE_PCLK_VCO_DIV_5(x)			UPDATE(x, 1, 1)
+#define STARFIVE_PRE_PLL_POWER_DOWN			BIT(0)
+
+/* REG: 0x1a1 */
+#define STARFIVE_PRE_PLL_DIV_1				0x1a1
+#define STARFIVE_PRE_PLL_PRE_DIV_MASK			GENMASK(5, 0)
+#define STARFIVE_PRE_PLL_PRE_DIV(x)			UPDATE(x, 5, 0)
+
+/* REG: 0x1a2 */
+#define STARFIVE_PRE_PLL_DIV_2				0x1a2
+#define STARFIVE_SPREAD_SPECTRUM_MOD_DOWN		BIT(7)
+#define STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE		BIT(6)
+#define STARFIVE_PRE_PLL_FRAC_DIV_DISABLE		UPDATE(3, 5, 4)
+#define STARFIVE_PRE_PLL_FB_DIV_11_8_MASK		GENMASK(3, 0)
+#define STARFIVE_PRE_PLL_FB_DIV_11_8(x)			UPDATE((x) >> 8, 3, 0)
+
+/* REG: 0x1a3 */
+#define STARFIVE_PRE_PLL_DIV_3				0x1a3
+#define STARFIVE_PRE_PLL_FB_DIV_7_0(x)			UPDATE(x, 7, 0)
+
+/* REG: 0x1a4*/
+#define STARFIVE_PRE_PLL_DIV_4				0x1a4
+#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C_MASK		GENMASK(1, 0)
+#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C(x)		UPDATE(x, 1, 0)
+#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B_MASK		GENMASK(3, 2)
+#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B(x)		UPDATE(x, 3, 2)
+#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A_MASK		GENMASK(5, 4)
+#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A(x)		UPDATE(x, 5, 4)
+
+/* REG: 0x1a5 */
+#define STARFIVE_PRE_PLL_DIV_5				0x1a5
+#define STARFIVE_PRE_PLL_PCLK_DIV_B_SHIFT		5
+#define STARFIVE_PRE_PLL_PCLK_DIV_B_MASK		GENMASK(6, 5)
+#define STARFIVE_PRE_PLL_PCLK_DIV_B(x)			UPDATE(x, 6, 5)
+#define STARFIVE_PRE_PLL_PCLK_DIV_A_MASK		GENMASK(4, 0)
+#define STARFIVE_PRE_PLL_PCLK_DIV_A(x)			UPDATE(x, 4, 0)
+
+/* REG: 0x1a6 */
+#define STARFIVE_PRE_PLL_DIV_6				0x1a6
+#define STARFIVE_PRE_PLL_PCLK_DIV_C_SHIFT		5
+#define STARFIVE_PRE_PLL_PCLK_DIV_C_MASK		GENMASK(6, 5)
+#define STARFIVE_PRE_PLL_PCLK_DIV_C(x)			UPDATE(x, 6, 5)
+#define STARFIVE_PRE_PLL_PCLK_DIV_D_MASK		GENMASK(4, 0)
+#define STARFIVE_PRE_PLL_PCLK_DIV_D(x)			UPDATE(x, 4, 0)
+
+/* REG: 0x1a9 */
+#define STARFIVE_PRE_PLL_LOCK_STATUS			0x1a9
+
+/* REG: 0x1aa */
+#define STARFIVE_POST_PLL_DIV_1				0x1aa
+#define STARFIVE_POST_PLL_POST_DIV_ENABLE		GENMASK(3, 2)
+#define STARFIVE_POST_PLL_REFCLK_SEL_TMDS		BIT(1)
+#define STARFIVE_POST_PLL_POWER_DOWN			BIT(0)
+#define STARFIVE_POST_PLL_FB_DIV_8(x)			UPDATE(((x) >> 8) << 4, 4, 4)
+
+/* REG:0x1ab */
+#define STARFIVE_POST_PLL_DIV_2				0x1ab
+#define STARFIVE_POST_PLL_Pre_DIV_MASK			GENMASK(5, 0)
+#define STARFIVE_POST_PLL_PRE_DIV(x)			UPDATE(x, 5, 0)
+
+/* REG: 0x1ac */
+#define STARFIVE_POST_PLL_DIV_3				0x1ac
+#define STARFIVE_POST_PLL_FB_DIV_7_0(x)			UPDATE(x, 7, 0)
+
+/* REG: 0x1ad */
+#define STARFIVE_POST_PLL_DIV_4				0x1ad
+#define STARFIVE_POST_PLL_POST_DIV_MASK			GENMASK(2, 0)
+#define STARFIVE_POST_PLL_POST_DIV_2			0x0
+#define STARFIVE_POST_PLL_POST_DIV_4			0x1
+#define STARFIVE_POST_PLL_POST_DIV_8			0x3
+
+/* REG: 0x1af */
+#define STARFIVE_POST_PLL_LOCK_STATUS			0x1af
+
+/* REG: 0x1b0 */
+#define STARFIVE_BIAS_CONTROL				0x1b0
+#define STARFIVE_BIAS_ENABLE				BIT(2)
+
+/* REG: 0x1b2 */
+#define STARFIVE_TMDS_CONTROL				0x1b2
+#define STARFIVE_TMDS_CLK_DRIVER_EN			BIT(3)
+#define STARFIVE_TMDS_D2_DRIVER_EN			BIT(2)
+#define STARFIVE_TMDS_D1_DRIVER_EN			BIT(1)
+#define STARFIVE_TMDS_D0_DRIVER_EN			BIT(0)
+#define STARFIVE_TMDS_DRIVER_ENABLE			(STARFIVE_TMDS_CLK_DRIVER_EN | \
+							 STARFIVE_TMDS_D2_DRIVER_EN | \
+							 STARFIVE_TMDS_D1_DRIVER_EN | \
+							 STARFIVE_TMDS_D0_DRIVER_EN)
+
+/* REG: 0x1b4 */
+#define STARFIVE_LDO_CONTROL				0x1b4
+#define STARFIVE_LDO_D2_EN				BIT(2)
+#define STARFIVE_LDO_D1_EN				BIT(1)
+#define STARFIVE_LDO_D0_EN				BIT(0)
+#define STARFIVE_LDO_ENABLE				(STARFIVE_LDO_D2_EN | \
+							 STARFIVE_LDO_D1_EN | \
+							 STARFIVE_LDO_D0_EN)
+
+/* REG: 0x1be */
+#define STARFIVE_SERIALIER_CONTROL			0x1be
+#define STARFIVE_SERIALIER_D2_EN			BIT(6)
+#define STARFIVE_SERIALIER_D1_EN			BIT(5)
+#define STARFIVE_SERIALIER_D0_EN			BIT(4)
+#define STARFIVE_SERIALIER_EN				BIT(0)
+
+#define STARFIVE_SERIALIER_ENABLE			(STARFIVE_SERIALIER_D2_EN | \
+							 STARFIVE_SERIALIER_D1_EN | \
+							 STARFIVE_SERIALIER_D0_EN | \
+							 STARFIVE_SERIALIER_EN)
+
+/* REG: 0x1cc */
+#define STARFIVE_RX_CONTROL				0x1cc
+#define STARFIVE_RX_EN					BIT(3)
+#define STARFIVE_RX_CHANNEL_2_EN			BIT(2)
+#define STARFIVE_RX_CHANNEL_1_EN			BIT(1)
+#define STARFIVE_RX_CHANNEL_0_EN			BIT(0)
+#define STARFIVE_RX_ENABLE				(STARFIVE_RX_EN | \
+							 STARFIVE_RX_CHANNEL_2_EN | \
+							 STARFIVE_RX_CHANNEL_1_EN | \
+							 STARFIVE_RX_CHANNEL_0_EN)
+
+/* REG: 0x1d1 */
+#define STARFIVE_PRE_PLL_FRAC_DIV_H			0x1d1
+#define STARFIVE_PRE_PLL_FRAC_DIV_23_16(x)		UPDATE((x) >> 16, 7, 0)
+/* REG: 0x1d2 */
+#define STARFIVE_PRE_PLL_FRAC_DIV_M			0x1d2
+#define STARFIVE_PRE_PLL_FRAC_DIV_15_8(x)		UPDATE((x) >> 8, 7, 0)
+/* REG: 0x1d3 */
+#define STARFIVE_PRE_PLL_FRAC_DIV_L			0x1d3
+#define STARFIVE_PRE_PLL_FRAC_DIV_7_0(x)		UPDATE(x, 7, 0)
+
+struct pre_pll_config {
+	unsigned long pixclock;
+	unsigned long tmdsclock;
+	u8 prediv;
+	u16 fbdiv;
+	u8 tmds_div_a;
+	u8 tmds_div_b;
+	u8 tmds_div_c;
+	u8 pclk_div_a;
+	u8 pclk_div_b;
+	u8 pclk_div_c;
+	u8 pclk_div_d;
+	u8 vco_div_5_en;
+	u32 fracdiv;
+};
+
+struct post_pll_config {
+	unsigned long tmdsclock;
+	u8 prediv;
+	u16 fbdiv;
+	u8 postdiv;
+	u8 post_div_en;
+	u8 version;
+};
+
+struct phy_config {
+	unsigned long	tmdsclock;
+	u8		regs[14];
+};
+
+struct hdmi_data_info {
+	int vic;
+	bool sink_is_hdmi;
+	bool sink_has_audio;
+	unsigned int enc_in_format;
+	unsigned int enc_out_format;
+	unsigned int colorimetry;
+};
+
+struct starfive_hdmi {
+	struct device *dev;
+	struct drm_device *drm_dev;
+	struct drm_encoder	encoder;
+	struct drm_connector	connector;
+
+	struct starfive_hdmi_i2c *i2c;
+
+	int irq;
+	struct clk *sys_clk;
+	struct clk *mclk;
+	struct clk *bclk;
+	struct reset_control *tx_rst;
+	void __iomem *regs;
+
+	struct i2c_adapter *ddc;
+
+	unsigned long tmds_rate;
+
+	struct hdmi_data_info	hdmi_data;
+	struct drm_display_mode previous_mode;
+	const struct pre_pll_config	*pre_cfg;
+	const struct post_pll_config	*post_cfg;
+};
+
+#endif /* __STARFIVE_HDMI_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
index 3cd533cfa..9669354bd 100644
--- a/drivers/gpu/drm/verisilicon/vs_drv.c
+++ b/drivers/gpu/drm/verisilicon/vs_drv.c
@@ -126,6 +126,11 @@ static const struct component_master_ops vs_drm_ops = {
 static struct platform_driver *drm_sub_drivers[] = {
 	&dc_platform_driver,
 
+	/* connector + encoder*/
+#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
+	&starfive_hdmi_driver,
+#endif
+
 };
 
 static struct component_match *vs_drm_match_add(struct device *dev)
diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
index 369ad22d6..e90723419 100644
--- a/drivers/gpu/drm/verisilicon/vs_drv.h
+++ b/drivers/gpu/drm/verisilicon/vs_drv.h
@@ -24,4 +24,8 @@ to_vs_drm_private(const struct drm_device *dev)
 	return container_of(dev, struct vs_drm_device, base);
 }
 
+#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
+extern struct platform_driver starfive_hdmi_driver;
+#endif
+
 #endif /* __VS_DRV_H__ */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 1/6] dt-bindings: display: Add yamls for JH7110 display system
  2023-10-25 10:39   ` Keith Zhao
  (?)
@ 2023-10-25 12:50     ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 117+ messages in thread
From: Krzysztof Kozlowski @ 2023-10-25 12:50 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Jack Zhu, Shengyang Chen, Changhuang Liang

On 25/10/2023 12:39, Keith Zhao wrote:
> StarFive SoCs JH7110 display system:

A nit, subject: drop second/last, redundant "yamls for". The
"dt-bindings" prefix is already stating that these are bindings, so
format is fixed.

> lcd-controller bases verisilicon dc8200 IP,
> and hdmi bases Innosilicon IP. Add bindings for them.

Please make it a proper sentences, with proper wrapping.

> 
> also update MAINTAINERS for dt-bindings

Not a sentence, but also not really needed.

> 
> about this patch, I tested the dtbs_check and dt_binding_check
> with the result pass.
> Based on the feedback of the previous version, the corresponding arrangement is made

Not relevant, so not really suitable for commit msg.

> 
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>  .../starfive/starfive,display-subsystem.yaml  |  41 +++++++
>  .../starfive/starfive,jh7110-dc8200.yaml      | 109 ++++++++++++++++++
>  .../starfive/starfive,jh7110-inno-hdmi.yaml   |  85 ++++++++++++++
>  MAINTAINERS                                   |   7 ++
>  4 files changed, 242 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
> 
> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
> new file mode 100644
> index 000000000..f45b97b08
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
> @@ -0,0 +1,41 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/starfive/starfive,display-subsystem.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Starfive DRM master device

What is DRM in hardware? I know Digital Rights Management, but then
subsystem seems wrong. If you mean Linux DRM, then Linux is not a
hardware, so drop all Linuxisms and describe hardware.


> +
> +maintainers:
> +  - Keith Zhao <keith.zhao@starfivetech.com>
> +  - ShengYang Chen <shengyang.chen@starfivetech.com>
> +
> +description:
> +  The Starfive DRM master device is a virtual device needed to list all

Virtual device? Then not suitable for bindings, sorry.

> +  display controller or other display interface nodes that comprise the
> +  graphics subsystem.
> +
> +properties:
> +  compatible:
> +    const: starfive,display-subsystem
> +
> +  ports:
> +    $ref: /schemas/types.yaml#/definitions/phandle-array

No, ports is not phandle-array. ports is object, always.

> +    description:
> +      Should contain a list of phandles pointing to display interface ports
> +      of display controller devices. Display controller definitions as defined
> +      in Documentation/devicetree/bindings/display/starfive/
> +      starfive,jh7110-dc8200.yaml

Use standard graph ports, not some own, custom property.

Anyway, entire binding should be dropped. You do not need it even.

> +
> +required:
> +  - compatible
> +  - ports
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    display-subsystem {
> +        compatible = "starfive,display-subsystem";
> +        ports = <&dc_out>;
> +    };
> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
> new file mode 100644
> index 000000000..87051cddf
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
> @@ -0,0 +1,109 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/starfive/starfive,jh7110-dc8200.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: StarFive display controller
> +
> +description:
> +  The StarFive SoC uses the display controller based on Verisilicon IP
> +  to transfer the image data from a video memory buffer to an external
> +  LCD interface.
> +
> +maintainers:
> +  - Keith Zhao <keith.zhao@starfivetech.com>
> +
> +properties:
> +  compatible:
> +    const: starfive,jh7110-dc8200
> +
> +  reg:
> +    minItems: 1
> +    items:
> +      - description:
> +          host interface

In one line.
 - description: host interface

> +      - description:
> +          display physical base address and length.

Drop redundant parts: base address and length. Everything in reg for
MMIO would have it...

> +
> +  interrupts:
> +    items:
> +      - description: The interrupt will be generated when DC finish one frame
> +
> +  clocks:
> +    items:
> +      - description: Clock for display system noc bus.
> +      - description: Pixel clock for display channel 0.
> +      - description: Pixel clock for display channel 1.
> +      - description: Core clock for display controller.
> +      - description: Clock for axi bus to access ddr.
> +      - description: Clock for ahb bus to R/W the phy regs.
> +      - description: External HDMI pixel clock.
> +      - description: Parent clock for pixel clock
> +
> +  clock-names:
> +    items:
> +      - const: noc_bus
> +      - const: channel0
> +      - const: channel1
> +      - const: dc_core
> +      - const: axi_core
> +      - const: ahb
> +      - const: hdmi_tx
> +      - const: dc_parent
> +
> +  resets:
> +    items:
> +      - description: Reset for axi bus.
> +      - description: Reset for ahb bus.
> +      - description: Core reset of display controller.
> +
> +  reset-names:
> +    items:
> +      - const: axi
> +      - const: ahb
> +      - const: core
> +
> +  port:
> +    $ref: /schemas/graph.yaml#/properties/port
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - clocks
> +  - clock-names
> +  - resets
> +  - reset-names
> +  - port
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    dc8200: lcd-controller@29400000 {
> +        compatible = "starfive,jh7110-dc8200";
> +        reg = <0x29400000 0x100>, <0x29400800 0x2000>;
> +        interrupts = <95>;
> +        clocks = <&syscrg 60>,
> +               <&voutcrg 7>,
> +               <&voutcrg 8>,
> +               <&voutcrg 4>,
> +               <&voutcrg 5>,
> +               <&voutcrg 6>,
> +               <&hdmitx0_pixelclk>,
> +               <&voutcrg 1>;
> +        clock-names = "noc_bus", "channel0", "channel1",
> +                      "dc_core", "axi_core", "ahb",
> +                      "hdmi_tx","dc_parent";
> +        resets = <&voutcrg 0>, <&voutcrg 1>, <&voutcrg 2>;
> +        reset-names = "axi", "ahb", "core";
> +        dc_out: port {
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +            dc_out_hdmi: endpoint@0 {
> +                reg = <0>;
> +                remote-endpoint = <&hdmi_in_dc>;
> +            };
> +        };
> +    };
> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
> new file mode 100644
> index 000000000..f6c473a10
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
> @@ -0,0 +1,85 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/starfive/starfive,jh7110-inno-hdmi.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Starfive JH7110 HDMI controller
> +
> +description:
> +  The StarFive JH7110 SoC uses the HDMI signal transmiter based on innosilicon IP
> +  to generate HDMI signal from its input and transmit the signal to the screen.
> +
> +maintainers:
> +  - Keith Zhao <keith.zhao@starfivetech.com>
> +
> +properties:
> +  compatible:
> +    const: "starfive,jh7110-inno-hdmi"

It does not look like you tested the bindings, at least after quick
look. Please run `make dt_binding_check` (see
Documentation/devicetree/bindings/writing-schema.rst for instructions).
Maybe you need to update your dtschema and yamllint.

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 1/6] dt-bindings: display: Add yamls for JH7110 display system
@ 2023-10-25 12:50     ` Krzysztof Kozlowski
  0 siblings, 0 replies; 117+ messages in thread
From: Krzysztof Kozlowski @ 2023-10-25 12:50 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig

On 25/10/2023 12:39, Keith Zhao wrote:
> StarFive SoCs JH7110 display system:

A nit, subject: drop second/last, redundant "yamls for". The
"dt-bindings" prefix is already stating that these are bindings, so
format is fixed.

> lcd-controller bases verisilicon dc8200 IP,
> and hdmi bases Innosilicon IP. Add bindings for them.

Please make it a proper sentences, with proper wrapping.

> 
> also update MAINTAINERS for dt-bindings

Not a sentence, but also not really needed.

> 
> about this patch, I tested the dtbs_check and dt_binding_check
> with the result pass.
> Based on the feedback of the previous version, the corresponding arrangement is made

Not relevant, so not really suitable for commit msg.

> 
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>  .../starfive/starfive,display-subsystem.yaml  |  41 +++++++
>  .../starfive/starfive,jh7110-dc8200.yaml      | 109 ++++++++++++++++++
>  .../starfive/starfive,jh7110-inno-hdmi.yaml   |  85 ++++++++++++++
>  MAINTAINERS                                   |   7 ++
>  4 files changed, 242 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
> 
> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
> new file mode 100644
> index 000000000..f45b97b08
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
> @@ -0,0 +1,41 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/starfive/starfive,display-subsystem.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Starfive DRM master device

What is DRM in hardware? I know Digital Rights Management, but then
subsystem seems wrong. If you mean Linux DRM, then Linux is not a
hardware, so drop all Linuxisms and describe hardware.


> +
> +maintainers:
> +  - Keith Zhao <keith.zhao@starfivetech.com>
> +  - ShengYang Chen <shengyang.chen@starfivetech.com>
> +
> +description:
> +  The Starfive DRM master device is a virtual device needed to list all

Virtual device? Then not suitable for bindings, sorry.

> +  display controller or other display interface nodes that comprise the
> +  graphics subsystem.
> +
> +properties:
> +  compatible:
> +    const: starfive,display-subsystem
> +
> +  ports:
> +    $ref: /schemas/types.yaml#/definitions/phandle-array

No, ports is not phandle-array. ports is object, always.

> +    description:
> +      Should contain a list of phandles pointing to display interface ports
> +      of display controller devices. Display controller definitions as defined
> +      in Documentation/devicetree/bindings/display/starfive/
> +      starfive,jh7110-dc8200.yaml

Use standard graph ports, not some own, custom property.

Anyway, entire binding should be dropped. You do not need it even.

> +
> +required:
> +  - compatible
> +  - ports
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    display-subsystem {
> +        compatible = "starfive,display-subsystem";
> +        ports = <&dc_out>;
> +    };
> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
> new file mode 100644
> index 000000000..87051cddf
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
> @@ -0,0 +1,109 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/starfive/starfive,jh7110-dc8200.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: StarFive display controller
> +
> +description:
> +  The StarFive SoC uses the display controller based on Verisilicon IP
> +  to transfer the image data from a video memory buffer to an external
> +  LCD interface.
> +
> +maintainers:
> +  - Keith Zhao <keith.zhao@starfivetech.com>
> +
> +properties:
> +  compatible:
> +    const: starfive,jh7110-dc8200
> +
> +  reg:
> +    minItems: 1
> +    items:
> +      - description:
> +          host interface

In one line.
 - description: host interface

> +      - description:
> +          display physical base address and length.

Drop redundant parts: base address and length. Everything in reg for
MMIO would have it...

> +
> +  interrupts:
> +    items:
> +      - description: The interrupt will be generated when DC finish one frame
> +
> +  clocks:
> +    items:
> +      - description: Clock for display system noc bus.
> +      - description: Pixel clock for display channel 0.
> +      - description: Pixel clock for display channel 1.
> +      - description: Core clock for display controller.
> +      - description: Clock for axi bus to access ddr.
> +      - description: Clock for ahb bus to R/W the phy regs.
> +      - description: External HDMI pixel clock.
> +      - description: Parent clock for pixel clock
> +
> +  clock-names:
> +    items:
> +      - const: noc_bus
> +      - const: channel0
> +      - const: channel1
> +      - const: dc_core
> +      - const: axi_core
> +      - const: ahb
> +      - const: hdmi_tx
> +      - const: dc_parent
> +
> +  resets:
> +    items:
> +      - description: Reset for axi bus.
> +      - description: Reset for ahb bus.
> +      - description: Core reset of display controller.
> +
> +  reset-names:
> +    items:
> +      - const: axi
> +      - const: ahb
> +      - const: core
> +
> +  port:
> +    $ref: /schemas/graph.yaml#/properties/port
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - clocks
> +  - clock-names
> +  - resets
> +  - reset-names
> +  - port
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    dc8200: lcd-controller@29400000 {
> +        compatible = "starfive,jh7110-dc8200";
> +        reg = <0x29400000 0x100>, <0x29400800 0x2000>;
> +        interrupts = <95>;
> +        clocks = <&syscrg 60>,
> +               <&voutcrg 7>,
> +               <&voutcrg 8>,
> +               <&voutcrg 4>,
> +               <&voutcrg 5>,
> +               <&voutcrg 6>,
> +               <&hdmitx0_pixelclk>,
> +               <&voutcrg 1>;
> +        clock-names = "noc_bus", "channel0", "channel1",
> +                      "dc_core", "axi_core", "ahb",
> +                      "hdmi_tx","dc_parent";
> +        resets = <&voutcrg 0>, <&voutcrg 1>, <&voutcrg 2>;
> +        reset-names = "axi", "ahb", "core";
> +        dc_out: port {
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +            dc_out_hdmi: endpoint@0 {
> +                reg = <0>;
> +                remote-endpoint = <&hdmi_in_dc>;
> +            };
> +        };
> +    };
> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
> new file mode 100644
> index 000000000..f6c473a10
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
> @@ -0,0 +1,85 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/starfive/starfive,jh7110-inno-hdmi.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Starfive JH7110 HDMI controller
> +
> +description:
> +  The StarFive JH7110 SoC uses the HDMI signal transmiter based on innosilicon IP
> +  to generate HDMI signal from its input and transmit the signal to the screen.
> +
> +maintainers:
> +  - Keith Zhao <keith.zhao@starfivetech.com>
> +
> +properties:
> +  compatible:
> +    const: "starfive,jh7110-inno-hdmi"

It does not look like you tested the bindings, at least after quick
look. Please run `make dt_binding_check` (see
Documentation/devicetree/bindings/writing-schema.rst for instructions).
Maybe you need to update your dtschema and yamllint.

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 1/6] dt-bindings: display: Add yamls for JH7110 display system
@ 2023-10-25 12:50     ` Krzysztof Kozlowski
  0 siblings, 0 replies; 117+ messages in thread
From: Krzysztof Kozlowski @ 2023-10-25 12:50 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Jack Zhu, Shengyang Chen, Changhuang Liang

On 25/10/2023 12:39, Keith Zhao wrote:
> StarFive SoCs JH7110 display system:

A nit, subject: drop second/last, redundant "yamls for". The
"dt-bindings" prefix is already stating that these are bindings, so
format is fixed.

> lcd-controller bases verisilicon dc8200 IP,
> and hdmi bases Innosilicon IP. Add bindings for them.

Please make it a proper sentences, with proper wrapping.

> 
> also update MAINTAINERS for dt-bindings

Not a sentence, but also not really needed.

> 
> about this patch, I tested the dtbs_check and dt_binding_check
> with the result pass.
> Based on the feedback of the previous version, the corresponding arrangement is made

Not relevant, so not really suitable for commit msg.

> 
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>  .../starfive/starfive,display-subsystem.yaml  |  41 +++++++
>  .../starfive/starfive,jh7110-dc8200.yaml      | 109 ++++++++++++++++++
>  .../starfive/starfive,jh7110-inno-hdmi.yaml   |  85 ++++++++++++++
>  MAINTAINERS                                   |   7 ++
>  4 files changed, 242 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
> 
> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
> new file mode 100644
> index 000000000..f45b97b08
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
> @@ -0,0 +1,41 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/starfive/starfive,display-subsystem.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Starfive DRM master device

What is DRM in hardware? I know Digital Rights Management, but then
subsystem seems wrong. If you mean Linux DRM, then Linux is not a
hardware, so drop all Linuxisms and describe hardware.


> +
> +maintainers:
> +  - Keith Zhao <keith.zhao@starfivetech.com>
> +  - ShengYang Chen <shengyang.chen@starfivetech.com>
> +
> +description:
> +  The Starfive DRM master device is a virtual device needed to list all

Virtual device? Then not suitable for bindings, sorry.

> +  display controller or other display interface nodes that comprise the
> +  graphics subsystem.
> +
> +properties:
> +  compatible:
> +    const: starfive,display-subsystem
> +
> +  ports:
> +    $ref: /schemas/types.yaml#/definitions/phandle-array

No, ports is not phandle-array. ports is object, always.

> +    description:
> +      Should contain a list of phandles pointing to display interface ports
> +      of display controller devices. Display controller definitions as defined
> +      in Documentation/devicetree/bindings/display/starfive/
> +      starfive,jh7110-dc8200.yaml

Use standard graph ports, not some own, custom property.

Anyway, entire binding should be dropped. You do not need it even.

> +
> +required:
> +  - compatible
> +  - ports
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    display-subsystem {
> +        compatible = "starfive,display-subsystem";
> +        ports = <&dc_out>;
> +    };
> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
> new file mode 100644
> index 000000000..87051cddf
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
> @@ -0,0 +1,109 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/starfive/starfive,jh7110-dc8200.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: StarFive display controller
> +
> +description:
> +  The StarFive SoC uses the display controller based on Verisilicon IP
> +  to transfer the image data from a video memory buffer to an external
> +  LCD interface.
> +
> +maintainers:
> +  - Keith Zhao <keith.zhao@starfivetech.com>
> +
> +properties:
> +  compatible:
> +    const: starfive,jh7110-dc8200
> +
> +  reg:
> +    minItems: 1
> +    items:
> +      - description:
> +          host interface

In one line.
 - description: host interface

> +      - description:
> +          display physical base address and length.

Drop redundant parts: base address and length. Everything in reg for
MMIO would have it...

> +
> +  interrupts:
> +    items:
> +      - description: The interrupt will be generated when DC finish one frame
> +
> +  clocks:
> +    items:
> +      - description: Clock for display system noc bus.
> +      - description: Pixel clock for display channel 0.
> +      - description: Pixel clock for display channel 1.
> +      - description: Core clock for display controller.
> +      - description: Clock for axi bus to access ddr.
> +      - description: Clock for ahb bus to R/W the phy regs.
> +      - description: External HDMI pixel clock.
> +      - description: Parent clock for pixel clock
> +
> +  clock-names:
> +    items:
> +      - const: noc_bus
> +      - const: channel0
> +      - const: channel1
> +      - const: dc_core
> +      - const: axi_core
> +      - const: ahb
> +      - const: hdmi_tx
> +      - const: dc_parent
> +
> +  resets:
> +    items:
> +      - description: Reset for axi bus.
> +      - description: Reset for ahb bus.
> +      - description: Core reset of display controller.
> +
> +  reset-names:
> +    items:
> +      - const: axi
> +      - const: ahb
> +      - const: core
> +
> +  port:
> +    $ref: /schemas/graph.yaml#/properties/port
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - clocks
> +  - clock-names
> +  - resets
> +  - reset-names
> +  - port
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    dc8200: lcd-controller@29400000 {
> +        compatible = "starfive,jh7110-dc8200";
> +        reg = <0x29400000 0x100>, <0x29400800 0x2000>;
> +        interrupts = <95>;
> +        clocks = <&syscrg 60>,
> +               <&voutcrg 7>,
> +               <&voutcrg 8>,
> +               <&voutcrg 4>,
> +               <&voutcrg 5>,
> +               <&voutcrg 6>,
> +               <&hdmitx0_pixelclk>,
> +               <&voutcrg 1>;
> +        clock-names = "noc_bus", "channel0", "channel1",
> +                      "dc_core", "axi_core", "ahb",
> +                      "hdmi_tx","dc_parent";
> +        resets = <&voutcrg 0>, <&voutcrg 1>, <&voutcrg 2>;
> +        reset-names = "axi", "ahb", "core";
> +        dc_out: port {
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +            dc_out_hdmi: endpoint@0 {
> +                reg = <0>;
> +                remote-endpoint = <&hdmi_in_dc>;
> +            };
> +        };
> +    };
> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
> new file mode 100644
> index 000000000..f6c473a10
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
> @@ -0,0 +1,85 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/starfive/starfive,jh7110-inno-hdmi.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Starfive JH7110 HDMI controller
> +
> +description:
> +  The StarFive JH7110 SoC uses the HDMI signal transmiter based on innosilicon IP
> +  to generate HDMI signal from its input and transmit the signal to the screen.
> +
> +maintainers:
> +  - Keith Zhao <keith.zhao@starfivetech.com>
> +
> +properties:
> +  compatible:
> +    const: "starfive,jh7110-inno-hdmi"

It does not look like you tested the bindings, at least after quick
look. Please run `make dt_binding_check` (see
Documentation/devicetree/bindings/writing-schema.rst for instructions).
Maybe you need to update your dtschema and yamllint.

Best regards,
Krzysztof


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 2/6] riscv: dts: starfive: jh7110: add dc controller and hdmi node
  2023-10-25 10:39   ` Keith Zhao
  (?)
@ 2023-10-25 13:38     ` Emil Renner Berthing
  -1 siblings, 0 replies; 117+ messages in thread
From: Emil Renner Berthing @ 2023-10-25 13:38 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Jack Zhu, Shengyang Chen, Changhuang Liang

Keith Zhao wrote:
> Add the dc controller and hdmi node for the Starfive JH7110 SoC.
>
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>  .../jh7110-starfive-visionfive-2.dtsi         | 91 +++++++++++++++++++
>  arch/riscv/boot/dts/starfive/jh7110.dtsi      | 41 +++++++++
>  2 files changed, 132 insertions(+)
>
> diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
> index de0f40a8b..97909b6d2 100644
> --- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
> +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
> @@ -31,6 +31,25 @@ memory@40000000 {
>  		reg = <0x0 0x40000000 0x1 0x0>;
>  	};
>
> +	reserved-memory {
> +		#address-cells = <2>;
> +		#size-cells = <2>;
> +		ranges;
> +
> +		/* vout applies for space from this CMA
> +		 * Without this CMA reservation,
> +		 * vout may not work properly.
> +		 */
> +		linux,cma {
> +			compatible = "shared-dma-pool";
> +			reusable;
> +			size = <0x0 0x20000000>;
> +			alignment = <0x0 0x1000>;
> +			alloc-ranges = <0x0 0x70000000 0x0 0x20000000>;
> +			linux,cma-default;
> +		};
> +	};
> +
>  	gpio-restart {
>  		compatible = "gpio-restart";
>  		gpios = <&sysgpio 35 GPIO_ACTIVE_HIGH>;
> @@ -231,6 +250,41 @@ GPOEN_DISABLE,
>  			slew-rate = <0>;
>  		};
>  	};
> +
> +	hdmi_pins: hdmi-0 {
> +		hdmi-scl-pins {
> +			pinmux = <GPIOMUX(0, GPOUT_SYS_HDMI_DDC_SCL,
> +					     GPOEN_SYS_HDMI_DDC_SCL,
> +					     GPI_SYS_HDMI_DDC_SCL)>;
> +			input-enable;
> +			bias-pull-up;
> +		};
> +
> +		hdmi-sda-pins {
> +			pinmux = <GPIOMUX(1, GPOUT_SYS_HDMI_DDC_SDA,
> +					     GPOEN_SYS_HDMI_DDC_SDA,
> +					     GPI_SYS_HDMI_DDC_SDA)>;
> +			input-enable;
> +			bias-pull-up;
> +		};
> +
> +		hdmi-cec-pins {
> +			pinmux = <GPIOMUX(14, GPOUT_SYS_HDMI_CEC_SDA,
> +					     GPOEN_SYS_HDMI_CEC_SDA,
> +					     GPI_SYS_HDMI_CEC_SDA)>;
> +			input-enable;
> +			bias-pull-up;
> +		};
> +
> +		hdmi-hpd-pins {
> +			pinmux = <GPIOMUX(15, GPOUT_HIGH,
> +					     GPOEN_ENABLE,
> +					     GPI_SYS_HDMI_HPD)>;
> +			input-enable;
> +			bias-disable; /* external pull-up */
> +		};
> +	};
> +

Please don't break the alphabetical ordering of these nodes.

>  };
>
>  &uart0 {
> @@ -254,3 +308,40 @@ &U74_3 {
>  &U74_4 {
>  	cpu-supply = <&vdd_cpu>;
>  };
> +
> +&voutcrg {
> +	status = "okay";
> +};
> +
> +&display {
> +	status = "okay";
> +};
> +
> +&hdmi {
> +	status = "okay";
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&hdmi_pins>;
> +
> +	hdmi_in: port {
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		hdmi_in_dc: endpoint@0 {
> +			reg = <0>;
> +			remote-endpoint = <&dc_out_hdmi>;
> +		};
> +	};
> +};
> +
> +&dc8200 {
> +	status = "okay";
> +
> +	dc_out: port {
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		dc_out_hdmi: endpoint@0 {
> +			reg = <0>;
> +			remote-endpoint = <&hdmi_in_dc>;
> +		};
> +
> +	};
> +};

Some goes for these node references. The order is /-node, clocks, node refences
sorted alphabetically.


> diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi
> index 0005fa163..1670452fb 100644
> --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi
> +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi
> @@ -282,6 +282,11 @@ tdm_ext: tdm-ext-clock {
>  		#clock-cells = <0>;
>  	};
>
> +	display: display-subsystem {
> +		compatible = "starfive,display-subsystem";
> +		ports = <&dc_out>;
> +	};
> +
>  	soc {
>  		compatible = "simple-bus";
>  		interrupt-parent = <&plic>;
> @@ -613,5 +618,41 @@ voutcrg: clock-controller@295c0000 {
>  			#reset-cells = <1>;
>  			power-domains = <&pwrc JH7110_PD_VOUT>;
>  		};
> +
> +		dc8200: lcd-controller@29400000 {
> +			compatible = "starfive,jh7110-dc8200";
> +			reg = <0x0 0x29400000 0x0 0x100>,
> +			      <0x0 0x29400800 0x0 0x2000>;
> +			interrupts = <95>;
> +			clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_DISP_AXI>,
> +				<&voutcrg JH7110_VOUTCLK_DC8200_PIX0>,
> +				<&voutcrg JH7110_VOUTCLK_DC8200_PIX1>,
> +				<&voutcrg JH7110_VOUTCLK_DC8200_CORE>,
> +				<&voutcrg JH7110_VOUTCLK_DC8200_AXI>,
> +				<&voutcrg JH7110_VOUTCLK_DC8200_AHB>,
> +				<&hdmitx0_pixelclk>,
> +				<&voutcrg JH7110_VOUTCLK_DC8200_PIX>;
> +			clock-names = "noc_bus", "channel0", "channel1",
> +				      "dc_core", "axi_core", "ahb",
> +				      "hdmi_tx", "dc_parent";
> +			resets = <&voutcrg JH7110_VOUTRST_DC8200_AXI>,
> +				 <&voutcrg JH7110_VOUTRST_DC8200_AHB>,
> +				 <&voutcrg JH7110_VOUTRST_DC8200_CORE>;
> +			reset-names = "axi","ahb", "core";
> +		};
> +
> +		hdmi: hdmi@29590000 {
> +			compatible = "starfive,jh7110-inno-hdmi";
> +			reg = <0x0 0x29590000 0x0 0x4000>;
> +			interrupts = <99>;
> +
> +			clocks = <&voutcrg JH7110_VOUTCLK_HDMI_TX_SYS>,
> +				 <&voutcrg JH7110_VOUTCLK_HDMI_TX_MCLK>,
> +				 <&voutcrg JH7110_VOUTCLK_HDMI_TX_BCLK>,
> +				 <&hdmitx0_pixelclk>;
> +			clock-names = "sysclk", "mclk", "bclk", "pclk";
> +			resets = <&voutcrg JH7110_VOUTRST_HDMI_TX_HDMI>;
> +			#sound-dai-cells = <0>;
> +		};

These nodes, however, are sorted by their address which you alse break in this
patch :(

>  	};
>  };
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 2/6] riscv: dts: starfive: jh7110: add dc controller and hdmi node
@ 2023-10-25 13:38     ` Emil Renner Berthing
  0 siblings, 0 replies; 117+ messages in thread
From: Emil Renner Berthing @ 2023-10-25 13:38 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Jack Zhu, Shengyang Chen, Changhuang Liang

Keith Zhao wrote:
> Add the dc controller and hdmi node for the Starfive JH7110 SoC.
>
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>  .../jh7110-starfive-visionfive-2.dtsi         | 91 +++++++++++++++++++
>  arch/riscv/boot/dts/starfive/jh7110.dtsi      | 41 +++++++++
>  2 files changed, 132 insertions(+)
>
> diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
> index de0f40a8b..97909b6d2 100644
> --- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
> +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
> @@ -31,6 +31,25 @@ memory@40000000 {
>  		reg = <0x0 0x40000000 0x1 0x0>;
>  	};
>
> +	reserved-memory {
> +		#address-cells = <2>;
> +		#size-cells = <2>;
> +		ranges;
> +
> +		/* vout applies for space from this CMA
> +		 * Without this CMA reservation,
> +		 * vout may not work properly.
> +		 */
> +		linux,cma {
> +			compatible = "shared-dma-pool";
> +			reusable;
> +			size = <0x0 0x20000000>;
> +			alignment = <0x0 0x1000>;
> +			alloc-ranges = <0x0 0x70000000 0x0 0x20000000>;
> +			linux,cma-default;
> +		};
> +	};
> +
>  	gpio-restart {
>  		compatible = "gpio-restart";
>  		gpios = <&sysgpio 35 GPIO_ACTIVE_HIGH>;
> @@ -231,6 +250,41 @@ GPOEN_DISABLE,
>  			slew-rate = <0>;
>  		};
>  	};
> +
> +	hdmi_pins: hdmi-0 {
> +		hdmi-scl-pins {
> +			pinmux = <GPIOMUX(0, GPOUT_SYS_HDMI_DDC_SCL,
> +					     GPOEN_SYS_HDMI_DDC_SCL,
> +					     GPI_SYS_HDMI_DDC_SCL)>;
> +			input-enable;
> +			bias-pull-up;
> +		};
> +
> +		hdmi-sda-pins {
> +			pinmux = <GPIOMUX(1, GPOUT_SYS_HDMI_DDC_SDA,
> +					     GPOEN_SYS_HDMI_DDC_SDA,
> +					     GPI_SYS_HDMI_DDC_SDA)>;
> +			input-enable;
> +			bias-pull-up;
> +		};
> +
> +		hdmi-cec-pins {
> +			pinmux = <GPIOMUX(14, GPOUT_SYS_HDMI_CEC_SDA,
> +					     GPOEN_SYS_HDMI_CEC_SDA,
> +					     GPI_SYS_HDMI_CEC_SDA)>;
> +			input-enable;
> +			bias-pull-up;
> +		};
> +
> +		hdmi-hpd-pins {
> +			pinmux = <GPIOMUX(15, GPOUT_HIGH,
> +					     GPOEN_ENABLE,
> +					     GPI_SYS_HDMI_HPD)>;
> +			input-enable;
> +			bias-disable; /* external pull-up */
> +		};
> +	};
> +

Please don't break the alphabetical ordering of these nodes.

>  };
>
>  &uart0 {
> @@ -254,3 +308,40 @@ &U74_3 {
>  &U74_4 {
>  	cpu-supply = <&vdd_cpu>;
>  };
> +
> +&voutcrg {
> +	status = "okay";
> +};
> +
> +&display {
> +	status = "okay";
> +};
> +
> +&hdmi {
> +	status = "okay";
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&hdmi_pins>;
> +
> +	hdmi_in: port {
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		hdmi_in_dc: endpoint@0 {
> +			reg = <0>;
> +			remote-endpoint = <&dc_out_hdmi>;
> +		};
> +	};
> +};
> +
> +&dc8200 {
> +	status = "okay";
> +
> +	dc_out: port {
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		dc_out_hdmi: endpoint@0 {
> +			reg = <0>;
> +			remote-endpoint = <&hdmi_in_dc>;
> +		};
> +
> +	};
> +};

Some goes for these node references. The order is /-node, clocks, node refences
sorted alphabetically.


> diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi
> index 0005fa163..1670452fb 100644
> --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi
> +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi
> @@ -282,6 +282,11 @@ tdm_ext: tdm-ext-clock {
>  		#clock-cells = <0>;
>  	};
>
> +	display: display-subsystem {
> +		compatible = "starfive,display-subsystem";
> +		ports = <&dc_out>;
> +	};
> +
>  	soc {
>  		compatible = "simple-bus";
>  		interrupt-parent = <&plic>;
> @@ -613,5 +618,41 @@ voutcrg: clock-controller@295c0000 {
>  			#reset-cells = <1>;
>  			power-domains = <&pwrc JH7110_PD_VOUT>;
>  		};
> +
> +		dc8200: lcd-controller@29400000 {
> +			compatible = "starfive,jh7110-dc8200";
> +			reg = <0x0 0x29400000 0x0 0x100>,
> +			      <0x0 0x29400800 0x0 0x2000>;
> +			interrupts = <95>;
> +			clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_DISP_AXI>,
> +				<&voutcrg JH7110_VOUTCLK_DC8200_PIX0>,
> +				<&voutcrg JH7110_VOUTCLK_DC8200_PIX1>,
> +				<&voutcrg JH7110_VOUTCLK_DC8200_CORE>,
> +				<&voutcrg JH7110_VOUTCLK_DC8200_AXI>,
> +				<&voutcrg JH7110_VOUTCLK_DC8200_AHB>,
> +				<&hdmitx0_pixelclk>,
> +				<&voutcrg JH7110_VOUTCLK_DC8200_PIX>;
> +			clock-names = "noc_bus", "channel0", "channel1",
> +				      "dc_core", "axi_core", "ahb",
> +				      "hdmi_tx", "dc_parent";
> +			resets = <&voutcrg JH7110_VOUTRST_DC8200_AXI>,
> +				 <&voutcrg JH7110_VOUTRST_DC8200_AHB>,
> +				 <&voutcrg JH7110_VOUTRST_DC8200_CORE>;
> +			reset-names = "axi","ahb", "core";
> +		};
> +
> +		hdmi: hdmi@29590000 {
> +			compatible = "starfive,jh7110-inno-hdmi";
> +			reg = <0x0 0x29590000 0x0 0x4000>;
> +			interrupts = <99>;
> +
> +			clocks = <&voutcrg JH7110_VOUTCLK_HDMI_TX_SYS>,
> +				 <&voutcrg JH7110_VOUTCLK_HDMI_TX_MCLK>,
> +				 <&voutcrg JH7110_VOUTCLK_HDMI_TX_BCLK>,
> +				 <&hdmitx0_pixelclk>;
> +			clock-names = "sysclk", "mclk", "bclk", "pclk";
> +			resets = <&voutcrg JH7110_VOUTRST_HDMI_TX_HDMI>;
> +			#sound-dai-cells = <0>;
> +		};

These nodes, however, are sorted by their address which you alse break in this
patch :(

>  	};
>  };
> --
> 2.34.1
>

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 2/6] riscv: dts: starfive: jh7110: add dc controller and hdmi node
@ 2023-10-25 13:38     ` Emil Renner Berthing
  0 siblings, 0 replies; 117+ messages in thread
From: Emil Renner Berthing @ 2023-10-25 13:38 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig

Keith Zhao wrote:
> Add the dc controller and hdmi node for the Starfive JH7110 SoC.
>
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>  .../jh7110-starfive-visionfive-2.dtsi         | 91 +++++++++++++++++++
>  arch/riscv/boot/dts/starfive/jh7110.dtsi      | 41 +++++++++
>  2 files changed, 132 insertions(+)
>
> diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
> index de0f40a8b..97909b6d2 100644
> --- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
> +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
> @@ -31,6 +31,25 @@ memory@40000000 {
>  		reg = <0x0 0x40000000 0x1 0x0>;
>  	};
>
> +	reserved-memory {
> +		#address-cells = <2>;
> +		#size-cells = <2>;
> +		ranges;
> +
> +		/* vout applies for space from this CMA
> +		 * Without this CMA reservation,
> +		 * vout may not work properly.
> +		 */
> +		linux,cma {
> +			compatible = "shared-dma-pool";
> +			reusable;
> +			size = <0x0 0x20000000>;
> +			alignment = <0x0 0x1000>;
> +			alloc-ranges = <0x0 0x70000000 0x0 0x20000000>;
> +			linux,cma-default;
> +		};
> +	};
> +
>  	gpio-restart {
>  		compatible = "gpio-restart";
>  		gpios = <&sysgpio 35 GPIO_ACTIVE_HIGH>;
> @@ -231,6 +250,41 @@ GPOEN_DISABLE,
>  			slew-rate = <0>;
>  		};
>  	};
> +
> +	hdmi_pins: hdmi-0 {
> +		hdmi-scl-pins {
> +			pinmux = <GPIOMUX(0, GPOUT_SYS_HDMI_DDC_SCL,
> +					     GPOEN_SYS_HDMI_DDC_SCL,
> +					     GPI_SYS_HDMI_DDC_SCL)>;
> +			input-enable;
> +			bias-pull-up;
> +		};
> +
> +		hdmi-sda-pins {
> +			pinmux = <GPIOMUX(1, GPOUT_SYS_HDMI_DDC_SDA,
> +					     GPOEN_SYS_HDMI_DDC_SDA,
> +					     GPI_SYS_HDMI_DDC_SDA)>;
> +			input-enable;
> +			bias-pull-up;
> +		};
> +
> +		hdmi-cec-pins {
> +			pinmux = <GPIOMUX(14, GPOUT_SYS_HDMI_CEC_SDA,
> +					     GPOEN_SYS_HDMI_CEC_SDA,
> +					     GPI_SYS_HDMI_CEC_SDA)>;
> +			input-enable;
> +			bias-pull-up;
> +		};
> +
> +		hdmi-hpd-pins {
> +			pinmux = <GPIOMUX(15, GPOUT_HIGH,
> +					     GPOEN_ENABLE,
> +					     GPI_SYS_HDMI_HPD)>;
> +			input-enable;
> +			bias-disable; /* external pull-up */
> +		};
> +	};
> +

Please don't break the alphabetical ordering of these nodes.

>  };
>
>  &uart0 {
> @@ -254,3 +308,40 @@ &U74_3 {
>  &U74_4 {
>  	cpu-supply = <&vdd_cpu>;
>  };
> +
> +&voutcrg {
> +	status = "okay";
> +};
> +
> +&display {
> +	status = "okay";
> +};
> +
> +&hdmi {
> +	status = "okay";
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&hdmi_pins>;
> +
> +	hdmi_in: port {
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		hdmi_in_dc: endpoint@0 {
> +			reg = <0>;
> +			remote-endpoint = <&dc_out_hdmi>;
> +		};
> +	};
> +};
> +
> +&dc8200 {
> +	status = "okay";
> +
> +	dc_out: port {
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		dc_out_hdmi: endpoint@0 {
> +			reg = <0>;
> +			remote-endpoint = <&hdmi_in_dc>;
> +		};
> +
> +	};
> +};

Some goes for these node references. The order is /-node, clocks, node refences
sorted alphabetically.


> diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi
> index 0005fa163..1670452fb 100644
> --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi
> +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi
> @@ -282,6 +282,11 @@ tdm_ext: tdm-ext-clock {
>  		#clock-cells = <0>;
>  	};
>
> +	display: display-subsystem {
> +		compatible = "starfive,display-subsystem";
> +		ports = <&dc_out>;
> +	};
> +
>  	soc {
>  		compatible = "simple-bus";
>  		interrupt-parent = <&plic>;
> @@ -613,5 +618,41 @@ voutcrg: clock-controller@295c0000 {
>  			#reset-cells = <1>;
>  			power-domains = <&pwrc JH7110_PD_VOUT>;
>  		};
> +
> +		dc8200: lcd-controller@29400000 {
> +			compatible = "starfive,jh7110-dc8200";
> +			reg = <0x0 0x29400000 0x0 0x100>,
> +			      <0x0 0x29400800 0x0 0x2000>;
> +			interrupts = <95>;
> +			clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_DISP_AXI>,
> +				<&voutcrg JH7110_VOUTCLK_DC8200_PIX0>,
> +				<&voutcrg JH7110_VOUTCLK_DC8200_PIX1>,
> +				<&voutcrg JH7110_VOUTCLK_DC8200_CORE>,
> +				<&voutcrg JH7110_VOUTCLK_DC8200_AXI>,
> +				<&voutcrg JH7110_VOUTCLK_DC8200_AHB>,
> +				<&hdmitx0_pixelclk>,
> +				<&voutcrg JH7110_VOUTCLK_DC8200_PIX>;
> +			clock-names = "noc_bus", "channel0", "channel1",
> +				      "dc_core", "axi_core", "ahb",
> +				      "hdmi_tx", "dc_parent";
> +			resets = <&voutcrg JH7110_VOUTRST_DC8200_AXI>,
> +				 <&voutcrg JH7110_VOUTRST_DC8200_AHB>,
> +				 <&voutcrg JH7110_VOUTRST_DC8200_CORE>;
> +			reset-names = "axi","ahb", "core";
> +		};
> +
> +		hdmi: hdmi@29590000 {
> +			compatible = "starfive,jh7110-inno-hdmi";
> +			reg = <0x0 0x29590000 0x0 0x4000>;
> +			interrupts = <99>;
> +
> +			clocks = <&voutcrg JH7110_VOUTCLK_HDMI_TX_SYS>,
> +				 <&voutcrg JH7110_VOUTCLK_HDMI_TX_MCLK>,
> +				 <&voutcrg JH7110_VOUTCLK_HDMI_TX_BCLK>,
> +				 <&hdmitx0_pixelclk>;
> +			clock-names = "sysclk", "mclk", "bclk", "pclk";
> +			resets = <&voutcrg JH7110_VOUTRST_HDMI_TX_HDMI>;
> +			#sound-dai-cells = <0>;
> +		};

These nodes, however, are sorted by their address which you alse break in this
patch :(

>  	};
>  };
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 3/6] drm/fourcc: Add drm/vs tiled modifiers
  2023-10-25 10:39   ` Keith Zhao
  (?)
@ 2023-10-25 13:50     ` Emil Renner Berthing
  -1 siblings, 0 replies; 117+ messages in thread
From: Emil Renner Berthing @ 2023-10-25 13:50 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Jack Zhu, Shengyang Chen, Changhuang Liang

Keith Zhao wrote:
> For each modifier, add the corresponding description
>
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>  include/uapi/drm/drm_fourcc.h | 57 +++++++++++++++++++++++++++++++++++
>  1 file changed, 57 insertions(+)
>
> diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h
> index 8db7fd3f7..a580a848c 100644
> --- a/include/uapi/drm/drm_fourcc.h
> +++ b/include/uapi/drm/drm_fourcc.h
> @@ -419,6 +419,7 @@ extern "C" {
>  #define DRM_FORMAT_MOD_VENDOR_ARM     0x08
>  #define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09
>  #define DRM_FORMAT_MOD_VENDOR_AMLOGIC 0x0a
> +#define DRM_FORMAT_MOD_VENDOR_VERISILICON 0x0b
>
>  /* add more to the end as needed */
>
> @@ -1562,6 +1563,62 @@ drm_fourcc_canonicalize_nvidia_format_mod(__u64 modifier)
>  #define AMD_FMT_MOD_CLEAR(field) \
>  	(~((__u64)AMD_FMT_MOD_##field##_MASK << AMD_FMT_MOD_##field##_SHIFT))
>
> +#define DRM_FORMAT_MOD_VERISILICON_TYPE_NORMAL					0x00
> +#define DRM_FORMAT_MOD_VERISILICON_TYPE_MASK					((__u64)0x3 << 54)
> +
> +#define fourcc_mod_vs_code(type, val) \
> +	fourcc_mod_code(VERISILICON, ((((__u64)type) << 54) | (val)))
> +
> +#define DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK				0x1F
> +
> +/*
> + * An x-major 8x8 super tile consists of 64 8x8 sub-tiles in total.
> + * Each 8x8 sub-tile consists of four standard tiles .
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8		0x02
> +
> +/*
> + * A y-major 8x8 super tile consists of 64 8x8 sub-tiles in total.
> + * Each 8x8 sub-tile consists of four standard tiles .
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8		0x03
> +
> +/*
> + * An 8x8 tile consists of four standard tiles
> + * that are organized in Z-order.
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_TILE_8X8						0x04
> +
> +/*
> + * An 8x4 tile consists of two standard tiles
> + * that are organized in Z-order.
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_TILE_8X4						0x07
> +
> +/*
> + * An x-major 8x4 super tile consists of 128 8x4 sub-tiles in total.
> + * Each 8x4 sub-tile consists of two standard tiles.
> + * two standard tiles also same with DRM_FORMAT_MOD_VS_TILE_8X4
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4		0x0B

These indents are all over the place. Please either align them with tabs or use
a single space like the AMD defines above.

> +
> +/*
> + * A y-major 4x8 super tile consists of 128 4x8 sub-tiles in total.
> + * Each 4x8 sub-tile consists of two standard tiles.
> + * two standard tiles also same with DRM_FORMAT_MOD_VS_TILE_8X4
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8    0x0C
> +
> +#define fourcc_mod_vs_norm_code(tile) \
> +	fourcc_mod_vs_code(DRM_FORMAT_MOD_VERISILICON_TYPE_NORMAL, \
> +				(tile))
> +
>  #if defined(__cplusplus)
>  }
>  #endif
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 3/6] drm/fourcc: Add drm/vs tiled modifiers
@ 2023-10-25 13:50     ` Emil Renner Berthing
  0 siblings, 0 replies; 117+ messages in thread
From: Emil Renner Berthing @ 2023-10-25 13:50 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Jack Zhu, Shengyang Chen, Changhuang Liang

Keith Zhao wrote:
> For each modifier, add the corresponding description
>
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>  include/uapi/drm/drm_fourcc.h | 57 +++++++++++++++++++++++++++++++++++
>  1 file changed, 57 insertions(+)
>
> diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h
> index 8db7fd3f7..a580a848c 100644
> --- a/include/uapi/drm/drm_fourcc.h
> +++ b/include/uapi/drm/drm_fourcc.h
> @@ -419,6 +419,7 @@ extern "C" {
>  #define DRM_FORMAT_MOD_VENDOR_ARM     0x08
>  #define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09
>  #define DRM_FORMAT_MOD_VENDOR_AMLOGIC 0x0a
> +#define DRM_FORMAT_MOD_VENDOR_VERISILICON 0x0b
>
>  /* add more to the end as needed */
>
> @@ -1562,6 +1563,62 @@ drm_fourcc_canonicalize_nvidia_format_mod(__u64 modifier)
>  #define AMD_FMT_MOD_CLEAR(field) \
>  	(~((__u64)AMD_FMT_MOD_##field##_MASK << AMD_FMT_MOD_##field##_SHIFT))
>
> +#define DRM_FORMAT_MOD_VERISILICON_TYPE_NORMAL					0x00
> +#define DRM_FORMAT_MOD_VERISILICON_TYPE_MASK					((__u64)0x3 << 54)
> +
> +#define fourcc_mod_vs_code(type, val) \
> +	fourcc_mod_code(VERISILICON, ((((__u64)type) << 54) | (val)))
> +
> +#define DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK				0x1F
> +
> +/*
> + * An x-major 8x8 super tile consists of 64 8x8 sub-tiles in total.
> + * Each 8x8 sub-tile consists of four standard tiles .
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8		0x02
> +
> +/*
> + * A y-major 8x8 super tile consists of 64 8x8 sub-tiles in total.
> + * Each 8x8 sub-tile consists of four standard tiles .
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8		0x03
> +
> +/*
> + * An 8x8 tile consists of four standard tiles
> + * that are organized in Z-order.
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_TILE_8X8						0x04
> +
> +/*
> + * An 8x4 tile consists of two standard tiles
> + * that are organized in Z-order.
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_TILE_8X4						0x07
> +
> +/*
> + * An x-major 8x4 super tile consists of 128 8x4 sub-tiles in total.
> + * Each 8x4 sub-tile consists of two standard tiles.
> + * two standard tiles also same with DRM_FORMAT_MOD_VS_TILE_8X4
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4		0x0B

These indents are all over the place. Please either align them with tabs or use
a single space like the AMD defines above.

> +
> +/*
> + * A y-major 4x8 super tile consists of 128 4x8 sub-tiles in total.
> + * Each 4x8 sub-tile consists of two standard tiles.
> + * two standard tiles also same with DRM_FORMAT_MOD_VS_TILE_8X4
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8    0x0C
> +
> +#define fourcc_mod_vs_norm_code(tile) \
> +	fourcc_mod_vs_code(DRM_FORMAT_MOD_VERISILICON_TYPE_NORMAL, \
> +				(tile))
> +
>  #if defined(__cplusplus)
>  }
>  #endif
> --
> 2.34.1
>

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 3/6] drm/fourcc: Add drm/vs tiled modifiers
@ 2023-10-25 13:50     ` Emil Renner Berthing
  0 siblings, 0 replies; 117+ messages in thread
From: Emil Renner Berthing @ 2023-10-25 13:50 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig

Keith Zhao wrote:
> For each modifier, add the corresponding description
>
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>  include/uapi/drm/drm_fourcc.h | 57 +++++++++++++++++++++++++++++++++++
>  1 file changed, 57 insertions(+)
>
> diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h
> index 8db7fd3f7..a580a848c 100644
> --- a/include/uapi/drm/drm_fourcc.h
> +++ b/include/uapi/drm/drm_fourcc.h
> @@ -419,6 +419,7 @@ extern "C" {
>  #define DRM_FORMAT_MOD_VENDOR_ARM     0x08
>  #define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09
>  #define DRM_FORMAT_MOD_VENDOR_AMLOGIC 0x0a
> +#define DRM_FORMAT_MOD_VENDOR_VERISILICON 0x0b
>
>  /* add more to the end as needed */
>
> @@ -1562,6 +1563,62 @@ drm_fourcc_canonicalize_nvidia_format_mod(__u64 modifier)
>  #define AMD_FMT_MOD_CLEAR(field) \
>  	(~((__u64)AMD_FMT_MOD_##field##_MASK << AMD_FMT_MOD_##field##_SHIFT))
>
> +#define DRM_FORMAT_MOD_VERISILICON_TYPE_NORMAL					0x00
> +#define DRM_FORMAT_MOD_VERISILICON_TYPE_MASK					((__u64)0x3 << 54)
> +
> +#define fourcc_mod_vs_code(type, val) \
> +	fourcc_mod_code(VERISILICON, ((((__u64)type) << 54) | (val)))
> +
> +#define DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK				0x1F
> +
> +/*
> + * An x-major 8x8 super tile consists of 64 8x8 sub-tiles in total.
> + * Each 8x8 sub-tile consists of four standard tiles .
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8		0x02
> +
> +/*
> + * A y-major 8x8 super tile consists of 64 8x8 sub-tiles in total.
> + * Each 8x8 sub-tile consists of four standard tiles .
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8		0x03
> +
> +/*
> + * An 8x8 tile consists of four standard tiles
> + * that are organized in Z-order.
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_TILE_8X8						0x04
> +
> +/*
> + * An 8x4 tile consists of two standard tiles
> + * that are organized in Z-order.
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_TILE_8X4						0x07
> +
> +/*
> + * An x-major 8x4 super tile consists of 128 8x4 sub-tiles in total.
> + * Each 8x4 sub-tile consists of two standard tiles.
> + * two standard tiles also same with DRM_FORMAT_MOD_VS_TILE_8X4
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4		0x0B

These indents are all over the place. Please either align them with tabs or use
a single space like the AMD defines above.

> +
> +/*
> + * A y-major 4x8 super tile consists of 128 4x8 sub-tiles in total.
> + * Each 4x8 sub-tile consists of two standard tiles.
> + * two standard tiles also same with DRM_FORMAT_MOD_VS_TILE_8X4
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8    0x0C
> +
> +#define fourcc_mod_vs_norm_code(tile) \
> +	fourcc_mod_vs_code(DRM_FORMAT_MOD_VERISILICON_TYPE_NORMAL, \
> +				(tile))
> +
>  #if defined(__cplusplus)
>  }
>  #endif
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 4/6] drm/vs: Register DRM device
  2023-10-25 10:39   ` Keith Zhao
  (?)
@ 2023-10-25 13:55     ` Emil Renner Berthing
  -1 siblings, 0 replies; 117+ messages in thread
From: Emil Renner Berthing @ 2023-10-25 13:55 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Jack Zhu, Shengyang Chen, Changhuang Liang

Keith Zhao wrote:
> Implement drm device registration interface
>
> Thomas Zimmermann wrote:
> You are replacing almost all of the GEM DMA object's helper code.
> Either inherit directly from drm_gem_object drop the dependency entirely,
> or you could try to fit your code into GEM DMA as well
>
> Eventually I found an answer and it was DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE
> After I adapt this interface, the custom gem logic interfaces can be deleted.
> It's a big shocking, clap!
>
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>  drivers/gpu/drm/Kconfig                  |   2 +
>  drivers/gpu/drm/Makefile                 |   1 +
>  drivers/gpu/drm/verisilicon/Kconfig      |  15 ++
>  drivers/gpu/drm/verisilicon/Makefile     |   7 +
>  drivers/gpu/drm/verisilicon/vs_drv.c     | 227 +++++++++++++++++++++++
>  drivers/gpu/drm/verisilicon/vs_drv.h     |  27 +++
>  drivers/gpu/drm/verisilicon/vs_modeset.c |  36 ++++
>  drivers/gpu/drm/verisilicon/vs_modeset.h |  10 +
>  include/uapi/drm/vs_drm.h                |  50 +++++
>  9 files changed, 375 insertions(+)
>  create mode 100644 drivers/gpu/drm/verisilicon/Kconfig
>  create mode 100644 drivers/gpu/drm/verisilicon/Makefile
>  create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.c
>  create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.h
>  create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.c
>  create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.h
>  create mode 100644 include/uapi/drm/vs_drm.h
>
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index afb3b2f5f..9ede80ef9 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -363,6 +363,8 @@ source "drivers/gpu/drm/solomon/Kconfig"
>
>  source "drivers/gpu/drm/sprd/Kconfig"
>
> +source "drivers/gpu/drm/verisilicon/Kconfig"
> +
>  config DRM_HYPERV
>  	tristate "DRM Support for Hyper-V synthetic video device"
>  	depends on DRM && PCI && MMU && HYPERV
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 7a09a89b4..6db3bc397 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -194,3 +194,4 @@ obj-y			+= gud/
>  obj-$(CONFIG_DRM_HYPERV) += hyperv/
>  obj-y			+= solomon/
>  obj-$(CONFIG_DRM_SPRD) += sprd/
> +obj-$(CONFIG_DRM_VERISILICON) += verisilicon/
> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
> new file mode 100644
> index 000000000..3a361f8c8
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/Kconfig
> @@ -0,0 +1,15 @@
> +# SPDX-License-Identifier: GPL-2.0
> +config DRM_VERISILICON
> +	tristate "DRM Support for VeriSilicon"
> +	depends on DRM
> +	select DRM_KMS_HELPER
> +	select DRM_GEM_DMA_HELPER
> +	select CMA
> +	select DMA_CMA
> +	help
> +	  Choose this option if you have a VeriSilicon soc chipset.
> +	  This driver provides VeriSilicon kernel mode
> +	  setting and buffer management. It does not
> +	  provide 2D or 3D acceleration.
> +
> +
> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> new file mode 100644
> index 000000000..7d3be305b
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/Makefile
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +vs_drm-objs := vs_drv.o \
> +		vs_modeset.o
> +
> +obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
> +

Git complains about trailing whitespace and trailing newlines in this patch.
Here one of the instances.

> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
> new file mode 100644
> index 000000000..da7698c3d
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> @@ -0,0 +1,227 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/component.h>
> +#include <linux/pm_runtime.h>
> +
> +#include <drm/drm_aperture.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_fbdev_generic.h>
> +#include <drm/drm_file.h>
> +#include <drm/drm_gem_dma_helper.h>
> +#include <drm/drm_module.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_vblank.h>
> +
> +#include "vs_drv.h"
> +#include "vs_modeset.h"
> +
> +#define DRV_NAME	"verisilicon"
> +#define DRV_DESC	"Verisilicon DRM driver"
> +#define DRV_DATE	"20230516"
> +#define DRV_MAJOR	1
> +#define DRV_MINOR	0
> +
> +static int vs_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
> +			      struct drm_mode_create_dumb *args)
> +{
> +	struct vs_drm_device *priv = to_vs_drm_private(dev);
> +
> +	unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
> +
> +	if (args->bpp % 10)
> +		args->pitch = ALIGN(pitch, priv->pitch_alignment);
> +	else
> +		/* for costum 10bit format with no bit gaps */
> +		args->pitch = pitch;
> +
> +	return drm_gem_dma_dumb_create_internal(file, dev, args);
> +}
> +
> +DEFINE_DRM_GEM_FOPS(vs_drm_fops);
> +
> +static struct drm_driver vs_drm_driver = {
> +	.driver_features	= DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
> +
> +	DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(vs_gem_dumb_create),
> +
> +	.fops			= &vs_drm_fops,
> +	.name			= DRV_NAME,
> +	.desc			= DRV_DESC,
> +	.date			= DRV_DATE,
> +	.major			= DRV_MAJOR,
> +	.minor			= DRV_MINOR,
> +};
> +
> +static int vs_drm_bind(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct vs_drm_device *priv;
> +	int ret;
> +	struct drm_device *drm_dev;
> +
> +	/* Remove existing drivers that may own the framebuffer memory. */
> +	ret = drm_aperture_remove_framebuffers(&vs_drm_driver);
> +	if (ret)
> +		return ret;
> +
> +	priv = devm_drm_dev_alloc(dev, &vs_drm_driver, struct vs_drm_device, base);
> +	if (IS_ERR(priv))
> +		return PTR_ERR(priv);
> +
> +	priv->pitch_alignment = 64;
> +
> +	ret = dma_set_coherent_mask(priv->base.dev, DMA_BIT_MASK(40));
> +	if (ret)
> +		return ret;
> +
> +	drm_dev = &priv->base;
> +	platform_set_drvdata(pdev, drm_dev);
> +
> +	vs_mode_config_init(drm_dev);
> +
> +	/* Now try and bind all our sub-components */
> +	ret = component_bind_all(dev, drm_dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
> +	if (ret)
> +		return ret;
> +
> +	drm_mode_config_reset(drm_dev);
> +
> +	drm_kms_helper_poll_init(drm_dev);
> +
> +	ret = drm_dev_register(drm_dev, 0);
> +	if (ret)
> +		return ret;
> +
> +	drm_fbdev_generic_setup(drm_dev, 32);
> +
> +	return 0;
> +}
> +
> +static void vs_drm_unbind(struct device *dev)
> +{
> +	struct drm_device *drm_dev = dev_get_drvdata(dev);
> +
> +	drm_dev_unregister(drm_dev);
> +	drm_kms_helper_poll_fini(drm_dev);
> +	component_unbind_all(drm_dev->dev, drm_dev);
> +}
> +
> +static const struct component_master_ops vs_drm_ops = {
> +	.bind = vs_drm_bind,
> +	.unbind = vs_drm_unbind,
> +};
> +
> +static struct platform_driver *drm_sub_drivers[] = {
> +
> +};
> +
> +static struct component_match *vs_drm_match_add(struct device *dev)
> +{
> +	struct component_match *match = NULL;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(drm_sub_drivers); ++i) {
> +		struct platform_driver *drv = drm_sub_drivers[i];
> +		struct device *p = NULL, *d;
> +
> +		while ((d = platform_find_device_by_driver(p, &drv->driver))) {
> +			put_device(p);
> +
> +			component_match_add(dev, &match, component_compare_dev, d);
> +			p = d;
> +		}
> +		put_device(p);
> +	}
> +
> +	return match ? match : ERR_PTR(-ENODEV);
> +}
> +
> +static int vs_drm_platform_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct component_match *match;
> +
> +	match = vs_drm_match_add(dev);
> +	if (IS_ERR(match))
> +		return PTR_ERR(match);
> +
> +	return component_master_add_with_match(dev, &vs_drm_ops, match);
> +}
> +
> +static int vs_drm_platform_remove(struct platform_device *pdev)
> +{
> +	component_master_del(&pdev->dev, &vs_drm_ops);
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int vs_drm_suspend(struct device *dev)
> +{
> +	return drm_mode_config_helper_suspend(dev_get_drvdata(dev));
> +}
> +
> +static int vs_drm_resume(struct device *dev)
> +{
> +	drm_mode_config_helper_resume(dev_get_drvdata(dev));
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(vs_drm_pm_ops, vs_drm_suspend, vs_drm_resume);
> +
> +static const struct of_device_id vs_drm_dt_ids[] = {
> +	{ .compatible = "starfive,display-subsystem", },
> +	{ },
> +};
> +
> +MODULE_DEVICE_TABLE(of, vs_drm_dt_ids);
> +
> +static struct platform_driver vs_drm_platform_driver = {
> +	.probe = vs_drm_platform_probe,
> +	.remove = vs_drm_platform_remove,
> +
> +	.driver = {
> +		.name = DRV_NAME,
> +		.of_match_table = vs_drm_dt_ids,
> +		.pm = &vs_drm_pm_ops,
> +	},
> +};
> +
> +static int __init vs_drm_init(void)
> +{
> +	int ret;
> +
> +	ret = platform_register_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
> +	if (ret)
> +		return ret;
> +
> +	ret = drm_platform_driver_register(&vs_drm_platform_driver);
> +	if (ret)
> +		platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
> +
> +	return ret;
> +}
> +
> +static void __exit vs_drm_fini(void)
> +{
> +	platform_driver_unregister(&vs_drm_platform_driver);
> +	platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
> +}
> +
> +module_init(vs_drm_init);
> +module_exit(vs_drm_fini);
> +
> +MODULE_DESCRIPTION("VeriSilicon DRM Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
> new file mode 100644
> index 000000000..369ad22d6
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.h
> @@ -0,0 +1,27 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DRV_H__
> +#define __VS_DRV_H__
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <drm/drm_drv.h>
> +#include <drm/drm_gem.h>
> +#include <drm/drm_managed.h>
> +
> +/*@pitch_alignment: buffer pitch alignment required by sub-devices.*/
> +struct vs_drm_device {
> +	struct drm_device base;
> +	unsigned int pitch_alignment;
> +};
> +
> +static inline struct vs_drm_device *
> +to_vs_drm_private(const struct drm_device *dev)
> +{
> +	return container_of(dev, struct vs_drm_device, base);
> +}
> +
> +#endif /* __VS_DRV_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.c b/drivers/gpu/drm/verisilicon/vs_modeset.c
> new file mode 100644
> index 000000000..151272067
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_modeset.c
> @@ -0,0 +1,36 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/module.h>
> +
> +#include <drm/drm_damage_helper.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_gem_framebuffer_helper.h>
> +
> +#include "vs_modeset.h"
> +
> +static const struct drm_mode_config_funcs vs_mode_config_funcs = {
> +	.fb_create			 = drm_gem_fb_create,
> +	.atomic_check		 = drm_atomic_helper_check,
> +	.atomic_commit		 = drm_atomic_helper_commit,
> +};
> +
> +static struct drm_mode_config_helper_funcs vs_mode_config_helpers = {
> +	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
> +};
> +
> +void vs_mode_config_init(struct drm_device *dev)
> +{
> +	drm_mode_config_init(dev);
> +	dev->mode_config.fb_modifiers_not_supported = false;
> +
> +	dev->mode_config.min_width  = 0;
> +	dev->mode_config.min_height = 0;
> +	dev->mode_config.max_width  = 4096;
> +	dev->mode_config.max_height = 4096;
> +
> +	dev->mode_config.funcs = &vs_mode_config_funcs;
> +	dev->mode_config.helper_private = &vs_mode_config_helpers;
> +}
> diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.h b/drivers/gpu/drm/verisilicon/vs_modeset.h
> new file mode 100644
> index 000000000..ebfe6d612
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_modeset.h
> @@ -0,0 +1,10 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_FB_H__
> +#define __VS_FB_H__
> +
> +void vs_mode_config_init(struct drm_device *dev);
> +#endif /* __VS_FB_H__ */
> diff --git a/include/uapi/drm/vs_drm.h b/include/uapi/drm/vs_drm.h
> new file mode 100644
> index 000000000..96b7fc95d
> --- /dev/null
> +++ b/include/uapi/drm/vs_drm.h
> @@ -0,0 +1,50 @@
> +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> +/*
> + * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DRM_H__
> +#define __VS_DRM_H__
> +
> +#include "drm.h"
> +
> +enum drm_vs_degamma_mode {
> +	VS_DEGAMMA_DISABLE = 0,
> +	VS_DEGAMMA_BT709 = 1,
> +	VS_DEGAMMA_BT2020 = 2,
> +};
> +
> +enum drm_vs_sync_dc_mode {
> +	VS_SINGLE_DC = 0,
> +	VS_MULTI_DC_PRIMARY = 1,
> +	VS_MULTI_DC_SECONDARY = 2,
> +};
> +
> +enum drm_vs_mmu_prefetch_mode {
> +	VS_MMU_PREFETCH_DISABLE = 0,
> +	VS_MMU_PREFETCH_ENABLE = 1,
> +};
> +
> +struct drm_vs_watermark {
> +	__u32 watermark;
> +	__u8 qos_low;
> +	__u8 qos_high;
> +};
> +
> +struct drm_vs_color_mgmt {
> +	__u32 colorkey;
> +	__u32 colorkey_high;
> +	__u32 clear_value;
> +	bool  clear_enable;
> +	bool  transparency;
> +};
> +
> +struct drm_vs_roi {
> +	bool enable;
> +	__u16 roi_x;
> +	__u16 roi_y;
> +	__u16 roi_w;
> +	__u16 roi_h;
> +};
> +
> +#endif /* __VS_DRM_H__ */
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 4/6] drm/vs: Register DRM device
@ 2023-10-25 13:55     ` Emil Renner Berthing
  0 siblings, 0 replies; 117+ messages in thread
From: Emil Renner Berthing @ 2023-10-25 13:55 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Jack Zhu, Shengyang Chen, Changhuang Liang

Keith Zhao wrote:
> Implement drm device registration interface
>
> Thomas Zimmermann wrote:
> You are replacing almost all of the GEM DMA object's helper code.
> Either inherit directly from drm_gem_object drop the dependency entirely,
> or you could try to fit your code into GEM DMA as well
>
> Eventually I found an answer and it was DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE
> After I adapt this interface, the custom gem logic interfaces can be deleted.
> It's a big shocking, clap!
>
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>  drivers/gpu/drm/Kconfig                  |   2 +
>  drivers/gpu/drm/Makefile                 |   1 +
>  drivers/gpu/drm/verisilicon/Kconfig      |  15 ++
>  drivers/gpu/drm/verisilicon/Makefile     |   7 +
>  drivers/gpu/drm/verisilicon/vs_drv.c     | 227 +++++++++++++++++++++++
>  drivers/gpu/drm/verisilicon/vs_drv.h     |  27 +++
>  drivers/gpu/drm/verisilicon/vs_modeset.c |  36 ++++
>  drivers/gpu/drm/verisilicon/vs_modeset.h |  10 +
>  include/uapi/drm/vs_drm.h                |  50 +++++
>  9 files changed, 375 insertions(+)
>  create mode 100644 drivers/gpu/drm/verisilicon/Kconfig
>  create mode 100644 drivers/gpu/drm/verisilicon/Makefile
>  create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.c
>  create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.h
>  create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.c
>  create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.h
>  create mode 100644 include/uapi/drm/vs_drm.h
>
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index afb3b2f5f..9ede80ef9 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -363,6 +363,8 @@ source "drivers/gpu/drm/solomon/Kconfig"
>
>  source "drivers/gpu/drm/sprd/Kconfig"
>
> +source "drivers/gpu/drm/verisilicon/Kconfig"
> +
>  config DRM_HYPERV
>  	tristate "DRM Support for Hyper-V synthetic video device"
>  	depends on DRM && PCI && MMU && HYPERV
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 7a09a89b4..6db3bc397 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -194,3 +194,4 @@ obj-y			+= gud/
>  obj-$(CONFIG_DRM_HYPERV) += hyperv/
>  obj-y			+= solomon/
>  obj-$(CONFIG_DRM_SPRD) += sprd/
> +obj-$(CONFIG_DRM_VERISILICON) += verisilicon/
> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
> new file mode 100644
> index 000000000..3a361f8c8
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/Kconfig
> @@ -0,0 +1,15 @@
> +# SPDX-License-Identifier: GPL-2.0
> +config DRM_VERISILICON
> +	tristate "DRM Support for VeriSilicon"
> +	depends on DRM
> +	select DRM_KMS_HELPER
> +	select DRM_GEM_DMA_HELPER
> +	select CMA
> +	select DMA_CMA
> +	help
> +	  Choose this option if you have a VeriSilicon soc chipset.
> +	  This driver provides VeriSilicon kernel mode
> +	  setting and buffer management. It does not
> +	  provide 2D or 3D acceleration.
> +
> +
> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> new file mode 100644
> index 000000000..7d3be305b
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/Makefile
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +vs_drm-objs := vs_drv.o \
> +		vs_modeset.o
> +
> +obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
> +

Git complains about trailing whitespace and trailing newlines in this patch.
Here one of the instances.

> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
> new file mode 100644
> index 000000000..da7698c3d
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> @@ -0,0 +1,227 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/component.h>
> +#include <linux/pm_runtime.h>
> +
> +#include <drm/drm_aperture.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_fbdev_generic.h>
> +#include <drm/drm_file.h>
> +#include <drm/drm_gem_dma_helper.h>
> +#include <drm/drm_module.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_vblank.h>
> +
> +#include "vs_drv.h"
> +#include "vs_modeset.h"
> +
> +#define DRV_NAME	"verisilicon"
> +#define DRV_DESC	"Verisilicon DRM driver"
> +#define DRV_DATE	"20230516"
> +#define DRV_MAJOR	1
> +#define DRV_MINOR	0
> +
> +static int vs_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
> +			      struct drm_mode_create_dumb *args)
> +{
> +	struct vs_drm_device *priv = to_vs_drm_private(dev);
> +
> +	unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
> +
> +	if (args->bpp % 10)
> +		args->pitch = ALIGN(pitch, priv->pitch_alignment);
> +	else
> +		/* for costum 10bit format with no bit gaps */
> +		args->pitch = pitch;
> +
> +	return drm_gem_dma_dumb_create_internal(file, dev, args);
> +}
> +
> +DEFINE_DRM_GEM_FOPS(vs_drm_fops);
> +
> +static struct drm_driver vs_drm_driver = {
> +	.driver_features	= DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
> +
> +	DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(vs_gem_dumb_create),
> +
> +	.fops			= &vs_drm_fops,
> +	.name			= DRV_NAME,
> +	.desc			= DRV_DESC,
> +	.date			= DRV_DATE,
> +	.major			= DRV_MAJOR,
> +	.minor			= DRV_MINOR,
> +};
> +
> +static int vs_drm_bind(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct vs_drm_device *priv;
> +	int ret;
> +	struct drm_device *drm_dev;
> +
> +	/* Remove existing drivers that may own the framebuffer memory. */
> +	ret = drm_aperture_remove_framebuffers(&vs_drm_driver);
> +	if (ret)
> +		return ret;
> +
> +	priv = devm_drm_dev_alloc(dev, &vs_drm_driver, struct vs_drm_device, base);
> +	if (IS_ERR(priv))
> +		return PTR_ERR(priv);
> +
> +	priv->pitch_alignment = 64;
> +
> +	ret = dma_set_coherent_mask(priv->base.dev, DMA_BIT_MASK(40));
> +	if (ret)
> +		return ret;
> +
> +	drm_dev = &priv->base;
> +	platform_set_drvdata(pdev, drm_dev);
> +
> +	vs_mode_config_init(drm_dev);
> +
> +	/* Now try and bind all our sub-components */
> +	ret = component_bind_all(dev, drm_dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
> +	if (ret)
> +		return ret;
> +
> +	drm_mode_config_reset(drm_dev);
> +
> +	drm_kms_helper_poll_init(drm_dev);
> +
> +	ret = drm_dev_register(drm_dev, 0);
> +	if (ret)
> +		return ret;
> +
> +	drm_fbdev_generic_setup(drm_dev, 32);
> +
> +	return 0;
> +}
> +
> +static void vs_drm_unbind(struct device *dev)
> +{
> +	struct drm_device *drm_dev = dev_get_drvdata(dev);
> +
> +	drm_dev_unregister(drm_dev);
> +	drm_kms_helper_poll_fini(drm_dev);
> +	component_unbind_all(drm_dev->dev, drm_dev);
> +}
> +
> +static const struct component_master_ops vs_drm_ops = {
> +	.bind = vs_drm_bind,
> +	.unbind = vs_drm_unbind,
> +};
> +
> +static struct platform_driver *drm_sub_drivers[] = {
> +
> +};
> +
> +static struct component_match *vs_drm_match_add(struct device *dev)
> +{
> +	struct component_match *match = NULL;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(drm_sub_drivers); ++i) {
> +		struct platform_driver *drv = drm_sub_drivers[i];
> +		struct device *p = NULL, *d;
> +
> +		while ((d = platform_find_device_by_driver(p, &drv->driver))) {
> +			put_device(p);
> +
> +			component_match_add(dev, &match, component_compare_dev, d);
> +			p = d;
> +		}
> +		put_device(p);
> +	}
> +
> +	return match ? match : ERR_PTR(-ENODEV);
> +}
> +
> +static int vs_drm_platform_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct component_match *match;
> +
> +	match = vs_drm_match_add(dev);
> +	if (IS_ERR(match))
> +		return PTR_ERR(match);
> +
> +	return component_master_add_with_match(dev, &vs_drm_ops, match);
> +}
> +
> +static int vs_drm_platform_remove(struct platform_device *pdev)
> +{
> +	component_master_del(&pdev->dev, &vs_drm_ops);
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int vs_drm_suspend(struct device *dev)
> +{
> +	return drm_mode_config_helper_suspend(dev_get_drvdata(dev));
> +}
> +
> +static int vs_drm_resume(struct device *dev)
> +{
> +	drm_mode_config_helper_resume(dev_get_drvdata(dev));
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(vs_drm_pm_ops, vs_drm_suspend, vs_drm_resume);
> +
> +static const struct of_device_id vs_drm_dt_ids[] = {
> +	{ .compatible = "starfive,display-subsystem", },
> +	{ },
> +};
> +
> +MODULE_DEVICE_TABLE(of, vs_drm_dt_ids);
> +
> +static struct platform_driver vs_drm_platform_driver = {
> +	.probe = vs_drm_platform_probe,
> +	.remove = vs_drm_platform_remove,
> +
> +	.driver = {
> +		.name = DRV_NAME,
> +		.of_match_table = vs_drm_dt_ids,
> +		.pm = &vs_drm_pm_ops,
> +	},
> +};
> +
> +static int __init vs_drm_init(void)
> +{
> +	int ret;
> +
> +	ret = platform_register_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
> +	if (ret)
> +		return ret;
> +
> +	ret = drm_platform_driver_register(&vs_drm_platform_driver);
> +	if (ret)
> +		platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
> +
> +	return ret;
> +}
> +
> +static void __exit vs_drm_fini(void)
> +{
> +	platform_driver_unregister(&vs_drm_platform_driver);
> +	platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
> +}
> +
> +module_init(vs_drm_init);
> +module_exit(vs_drm_fini);
> +
> +MODULE_DESCRIPTION("VeriSilicon DRM Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
> new file mode 100644
> index 000000000..369ad22d6
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.h
> @@ -0,0 +1,27 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DRV_H__
> +#define __VS_DRV_H__
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <drm/drm_drv.h>
> +#include <drm/drm_gem.h>
> +#include <drm/drm_managed.h>
> +
> +/*@pitch_alignment: buffer pitch alignment required by sub-devices.*/
> +struct vs_drm_device {
> +	struct drm_device base;
> +	unsigned int pitch_alignment;
> +};
> +
> +static inline struct vs_drm_device *
> +to_vs_drm_private(const struct drm_device *dev)
> +{
> +	return container_of(dev, struct vs_drm_device, base);
> +}
> +
> +#endif /* __VS_DRV_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.c b/drivers/gpu/drm/verisilicon/vs_modeset.c
> new file mode 100644
> index 000000000..151272067
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_modeset.c
> @@ -0,0 +1,36 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/module.h>
> +
> +#include <drm/drm_damage_helper.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_gem_framebuffer_helper.h>
> +
> +#include "vs_modeset.h"
> +
> +static const struct drm_mode_config_funcs vs_mode_config_funcs = {
> +	.fb_create			 = drm_gem_fb_create,
> +	.atomic_check		 = drm_atomic_helper_check,
> +	.atomic_commit		 = drm_atomic_helper_commit,
> +};
> +
> +static struct drm_mode_config_helper_funcs vs_mode_config_helpers = {
> +	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
> +};
> +
> +void vs_mode_config_init(struct drm_device *dev)
> +{
> +	drm_mode_config_init(dev);
> +	dev->mode_config.fb_modifiers_not_supported = false;
> +
> +	dev->mode_config.min_width  = 0;
> +	dev->mode_config.min_height = 0;
> +	dev->mode_config.max_width  = 4096;
> +	dev->mode_config.max_height = 4096;
> +
> +	dev->mode_config.funcs = &vs_mode_config_funcs;
> +	dev->mode_config.helper_private = &vs_mode_config_helpers;
> +}
> diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.h b/drivers/gpu/drm/verisilicon/vs_modeset.h
> new file mode 100644
> index 000000000..ebfe6d612
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_modeset.h
> @@ -0,0 +1,10 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_FB_H__
> +#define __VS_FB_H__
> +
> +void vs_mode_config_init(struct drm_device *dev);
> +#endif /* __VS_FB_H__ */
> diff --git a/include/uapi/drm/vs_drm.h b/include/uapi/drm/vs_drm.h
> new file mode 100644
> index 000000000..96b7fc95d
> --- /dev/null
> +++ b/include/uapi/drm/vs_drm.h
> @@ -0,0 +1,50 @@
> +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> +/*
> + * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DRM_H__
> +#define __VS_DRM_H__
> +
> +#include "drm.h"
> +
> +enum drm_vs_degamma_mode {
> +	VS_DEGAMMA_DISABLE = 0,
> +	VS_DEGAMMA_BT709 = 1,
> +	VS_DEGAMMA_BT2020 = 2,
> +};
> +
> +enum drm_vs_sync_dc_mode {
> +	VS_SINGLE_DC = 0,
> +	VS_MULTI_DC_PRIMARY = 1,
> +	VS_MULTI_DC_SECONDARY = 2,
> +};
> +
> +enum drm_vs_mmu_prefetch_mode {
> +	VS_MMU_PREFETCH_DISABLE = 0,
> +	VS_MMU_PREFETCH_ENABLE = 1,
> +};
> +
> +struct drm_vs_watermark {
> +	__u32 watermark;
> +	__u8 qos_low;
> +	__u8 qos_high;
> +};
> +
> +struct drm_vs_color_mgmt {
> +	__u32 colorkey;
> +	__u32 colorkey_high;
> +	__u32 clear_value;
> +	bool  clear_enable;
> +	bool  transparency;
> +};
> +
> +struct drm_vs_roi {
> +	bool enable;
> +	__u16 roi_x;
> +	__u16 roi_y;
> +	__u16 roi_w;
> +	__u16 roi_h;
> +};
> +
> +#endif /* __VS_DRM_H__ */
> --
> 2.34.1
>

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 4/6] drm/vs: Register DRM device
@ 2023-10-25 13:55     ` Emil Renner Berthing
  0 siblings, 0 replies; 117+ messages in thread
From: Emil Renner Berthing @ 2023-10-25 13:55 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig

Keith Zhao wrote:
> Implement drm device registration interface
>
> Thomas Zimmermann wrote:
> You are replacing almost all of the GEM DMA object's helper code.
> Either inherit directly from drm_gem_object drop the dependency entirely,
> or you could try to fit your code into GEM DMA as well
>
> Eventually I found an answer and it was DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE
> After I adapt this interface, the custom gem logic interfaces can be deleted.
> It's a big shocking, clap!
>
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>  drivers/gpu/drm/Kconfig                  |   2 +
>  drivers/gpu/drm/Makefile                 |   1 +
>  drivers/gpu/drm/verisilicon/Kconfig      |  15 ++
>  drivers/gpu/drm/verisilicon/Makefile     |   7 +
>  drivers/gpu/drm/verisilicon/vs_drv.c     | 227 +++++++++++++++++++++++
>  drivers/gpu/drm/verisilicon/vs_drv.h     |  27 +++
>  drivers/gpu/drm/verisilicon/vs_modeset.c |  36 ++++
>  drivers/gpu/drm/verisilicon/vs_modeset.h |  10 +
>  include/uapi/drm/vs_drm.h                |  50 +++++
>  9 files changed, 375 insertions(+)
>  create mode 100644 drivers/gpu/drm/verisilicon/Kconfig
>  create mode 100644 drivers/gpu/drm/verisilicon/Makefile
>  create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.c
>  create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.h
>  create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.c
>  create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.h
>  create mode 100644 include/uapi/drm/vs_drm.h
>
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index afb3b2f5f..9ede80ef9 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -363,6 +363,8 @@ source "drivers/gpu/drm/solomon/Kconfig"
>
>  source "drivers/gpu/drm/sprd/Kconfig"
>
> +source "drivers/gpu/drm/verisilicon/Kconfig"
> +
>  config DRM_HYPERV
>  	tristate "DRM Support for Hyper-V synthetic video device"
>  	depends on DRM && PCI && MMU && HYPERV
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 7a09a89b4..6db3bc397 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -194,3 +194,4 @@ obj-y			+= gud/
>  obj-$(CONFIG_DRM_HYPERV) += hyperv/
>  obj-y			+= solomon/
>  obj-$(CONFIG_DRM_SPRD) += sprd/
> +obj-$(CONFIG_DRM_VERISILICON) += verisilicon/
> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
> new file mode 100644
> index 000000000..3a361f8c8
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/Kconfig
> @@ -0,0 +1,15 @@
> +# SPDX-License-Identifier: GPL-2.0
> +config DRM_VERISILICON
> +	tristate "DRM Support for VeriSilicon"
> +	depends on DRM
> +	select DRM_KMS_HELPER
> +	select DRM_GEM_DMA_HELPER
> +	select CMA
> +	select DMA_CMA
> +	help
> +	  Choose this option if you have a VeriSilicon soc chipset.
> +	  This driver provides VeriSilicon kernel mode
> +	  setting and buffer management. It does not
> +	  provide 2D or 3D acceleration.
> +
> +
> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> new file mode 100644
> index 000000000..7d3be305b
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/Makefile
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +vs_drm-objs := vs_drv.o \
> +		vs_modeset.o
> +
> +obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
> +

Git complains about trailing whitespace and trailing newlines in this patch.
Here one of the instances.

> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
> new file mode 100644
> index 000000000..da7698c3d
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> @@ -0,0 +1,227 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/component.h>
> +#include <linux/pm_runtime.h>
> +
> +#include <drm/drm_aperture.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_fbdev_generic.h>
> +#include <drm/drm_file.h>
> +#include <drm/drm_gem_dma_helper.h>
> +#include <drm/drm_module.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_vblank.h>
> +
> +#include "vs_drv.h"
> +#include "vs_modeset.h"
> +
> +#define DRV_NAME	"verisilicon"
> +#define DRV_DESC	"Verisilicon DRM driver"
> +#define DRV_DATE	"20230516"
> +#define DRV_MAJOR	1
> +#define DRV_MINOR	0
> +
> +static int vs_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
> +			      struct drm_mode_create_dumb *args)
> +{
> +	struct vs_drm_device *priv = to_vs_drm_private(dev);
> +
> +	unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
> +
> +	if (args->bpp % 10)
> +		args->pitch = ALIGN(pitch, priv->pitch_alignment);
> +	else
> +		/* for costum 10bit format with no bit gaps */
> +		args->pitch = pitch;
> +
> +	return drm_gem_dma_dumb_create_internal(file, dev, args);
> +}
> +
> +DEFINE_DRM_GEM_FOPS(vs_drm_fops);
> +
> +static struct drm_driver vs_drm_driver = {
> +	.driver_features	= DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
> +
> +	DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(vs_gem_dumb_create),
> +
> +	.fops			= &vs_drm_fops,
> +	.name			= DRV_NAME,
> +	.desc			= DRV_DESC,
> +	.date			= DRV_DATE,
> +	.major			= DRV_MAJOR,
> +	.minor			= DRV_MINOR,
> +};
> +
> +static int vs_drm_bind(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct vs_drm_device *priv;
> +	int ret;
> +	struct drm_device *drm_dev;
> +
> +	/* Remove existing drivers that may own the framebuffer memory. */
> +	ret = drm_aperture_remove_framebuffers(&vs_drm_driver);
> +	if (ret)
> +		return ret;
> +
> +	priv = devm_drm_dev_alloc(dev, &vs_drm_driver, struct vs_drm_device, base);
> +	if (IS_ERR(priv))
> +		return PTR_ERR(priv);
> +
> +	priv->pitch_alignment = 64;
> +
> +	ret = dma_set_coherent_mask(priv->base.dev, DMA_BIT_MASK(40));
> +	if (ret)
> +		return ret;
> +
> +	drm_dev = &priv->base;
> +	platform_set_drvdata(pdev, drm_dev);
> +
> +	vs_mode_config_init(drm_dev);
> +
> +	/* Now try and bind all our sub-components */
> +	ret = component_bind_all(dev, drm_dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
> +	if (ret)
> +		return ret;
> +
> +	drm_mode_config_reset(drm_dev);
> +
> +	drm_kms_helper_poll_init(drm_dev);
> +
> +	ret = drm_dev_register(drm_dev, 0);
> +	if (ret)
> +		return ret;
> +
> +	drm_fbdev_generic_setup(drm_dev, 32);
> +
> +	return 0;
> +}
> +
> +static void vs_drm_unbind(struct device *dev)
> +{
> +	struct drm_device *drm_dev = dev_get_drvdata(dev);
> +
> +	drm_dev_unregister(drm_dev);
> +	drm_kms_helper_poll_fini(drm_dev);
> +	component_unbind_all(drm_dev->dev, drm_dev);
> +}
> +
> +static const struct component_master_ops vs_drm_ops = {
> +	.bind = vs_drm_bind,
> +	.unbind = vs_drm_unbind,
> +};
> +
> +static struct platform_driver *drm_sub_drivers[] = {
> +
> +};
> +
> +static struct component_match *vs_drm_match_add(struct device *dev)
> +{
> +	struct component_match *match = NULL;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(drm_sub_drivers); ++i) {
> +		struct platform_driver *drv = drm_sub_drivers[i];
> +		struct device *p = NULL, *d;
> +
> +		while ((d = platform_find_device_by_driver(p, &drv->driver))) {
> +			put_device(p);
> +
> +			component_match_add(dev, &match, component_compare_dev, d);
> +			p = d;
> +		}
> +		put_device(p);
> +	}
> +
> +	return match ? match : ERR_PTR(-ENODEV);
> +}
> +
> +static int vs_drm_platform_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct component_match *match;
> +
> +	match = vs_drm_match_add(dev);
> +	if (IS_ERR(match))
> +		return PTR_ERR(match);
> +
> +	return component_master_add_with_match(dev, &vs_drm_ops, match);
> +}
> +
> +static int vs_drm_platform_remove(struct platform_device *pdev)
> +{
> +	component_master_del(&pdev->dev, &vs_drm_ops);
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int vs_drm_suspend(struct device *dev)
> +{
> +	return drm_mode_config_helper_suspend(dev_get_drvdata(dev));
> +}
> +
> +static int vs_drm_resume(struct device *dev)
> +{
> +	drm_mode_config_helper_resume(dev_get_drvdata(dev));
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(vs_drm_pm_ops, vs_drm_suspend, vs_drm_resume);
> +
> +static const struct of_device_id vs_drm_dt_ids[] = {
> +	{ .compatible = "starfive,display-subsystem", },
> +	{ },
> +};
> +
> +MODULE_DEVICE_TABLE(of, vs_drm_dt_ids);
> +
> +static struct platform_driver vs_drm_platform_driver = {
> +	.probe = vs_drm_platform_probe,
> +	.remove = vs_drm_platform_remove,
> +
> +	.driver = {
> +		.name = DRV_NAME,
> +		.of_match_table = vs_drm_dt_ids,
> +		.pm = &vs_drm_pm_ops,
> +	},
> +};
> +
> +static int __init vs_drm_init(void)
> +{
> +	int ret;
> +
> +	ret = platform_register_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
> +	if (ret)
> +		return ret;
> +
> +	ret = drm_platform_driver_register(&vs_drm_platform_driver);
> +	if (ret)
> +		platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
> +
> +	return ret;
> +}
> +
> +static void __exit vs_drm_fini(void)
> +{
> +	platform_driver_unregister(&vs_drm_platform_driver);
> +	platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
> +}
> +
> +module_init(vs_drm_init);
> +module_exit(vs_drm_fini);
> +
> +MODULE_DESCRIPTION("VeriSilicon DRM Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
> new file mode 100644
> index 000000000..369ad22d6
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.h
> @@ -0,0 +1,27 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DRV_H__
> +#define __VS_DRV_H__
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <drm/drm_drv.h>
> +#include <drm/drm_gem.h>
> +#include <drm/drm_managed.h>
> +
> +/*@pitch_alignment: buffer pitch alignment required by sub-devices.*/
> +struct vs_drm_device {
> +	struct drm_device base;
> +	unsigned int pitch_alignment;
> +};
> +
> +static inline struct vs_drm_device *
> +to_vs_drm_private(const struct drm_device *dev)
> +{
> +	return container_of(dev, struct vs_drm_device, base);
> +}
> +
> +#endif /* __VS_DRV_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.c b/drivers/gpu/drm/verisilicon/vs_modeset.c
> new file mode 100644
> index 000000000..151272067
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_modeset.c
> @@ -0,0 +1,36 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/module.h>
> +
> +#include <drm/drm_damage_helper.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_gem_framebuffer_helper.h>
> +
> +#include "vs_modeset.h"
> +
> +static const struct drm_mode_config_funcs vs_mode_config_funcs = {
> +	.fb_create			 = drm_gem_fb_create,
> +	.atomic_check		 = drm_atomic_helper_check,
> +	.atomic_commit		 = drm_atomic_helper_commit,
> +};
> +
> +static struct drm_mode_config_helper_funcs vs_mode_config_helpers = {
> +	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
> +};
> +
> +void vs_mode_config_init(struct drm_device *dev)
> +{
> +	drm_mode_config_init(dev);
> +	dev->mode_config.fb_modifiers_not_supported = false;
> +
> +	dev->mode_config.min_width  = 0;
> +	dev->mode_config.min_height = 0;
> +	dev->mode_config.max_width  = 4096;
> +	dev->mode_config.max_height = 4096;
> +
> +	dev->mode_config.funcs = &vs_mode_config_funcs;
> +	dev->mode_config.helper_private = &vs_mode_config_helpers;
> +}
> diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.h b/drivers/gpu/drm/verisilicon/vs_modeset.h
> new file mode 100644
> index 000000000..ebfe6d612
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_modeset.h
> @@ -0,0 +1,10 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_FB_H__
> +#define __VS_FB_H__
> +
> +void vs_mode_config_init(struct drm_device *dev);
> +#endif /* __VS_FB_H__ */
> diff --git a/include/uapi/drm/vs_drm.h b/include/uapi/drm/vs_drm.h
> new file mode 100644
> index 000000000..96b7fc95d
> --- /dev/null
> +++ b/include/uapi/drm/vs_drm.h
> @@ -0,0 +1,50 @@
> +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> +/*
> + * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DRM_H__
> +#define __VS_DRM_H__
> +
> +#include "drm.h"
> +
> +enum drm_vs_degamma_mode {
> +	VS_DEGAMMA_DISABLE = 0,
> +	VS_DEGAMMA_BT709 = 1,
> +	VS_DEGAMMA_BT2020 = 2,
> +};
> +
> +enum drm_vs_sync_dc_mode {
> +	VS_SINGLE_DC = 0,
> +	VS_MULTI_DC_PRIMARY = 1,
> +	VS_MULTI_DC_SECONDARY = 2,
> +};
> +
> +enum drm_vs_mmu_prefetch_mode {
> +	VS_MMU_PREFETCH_DISABLE = 0,
> +	VS_MMU_PREFETCH_ENABLE = 1,
> +};
> +
> +struct drm_vs_watermark {
> +	__u32 watermark;
> +	__u8 qos_low;
> +	__u8 qos_high;
> +};
> +
> +struct drm_vs_color_mgmt {
> +	__u32 colorkey;
> +	__u32 colorkey_high;
> +	__u32 clear_value;
> +	bool  clear_enable;
> +	bool  transparency;
> +};
> +
> +struct drm_vs_roi {
> +	bool enable;
> +	__u16 roi_x;
> +	__u16 roi_y;
> +	__u16 roi_w;
> +	__u16 roi_h;
> +};
> +
> +#endif /* __VS_DRM_H__ */
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
  2023-10-25 10:39   ` Keith Zhao
  (?)
@ 2023-10-25 13:57     ` Maxime Ripard
  -1 siblings, 0 replies; 117+ messages in thread
From: Maxime Ripard @ 2023-10-25 13:57 UTC (permalink / raw)
  To: Keith Zhao
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, David Airlie, Daniel Vetter, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Emil Renner Berthing,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Maarten Lankhorst,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Jack Zhu, Shengyang Chen, Changhuang Liang

[-- Attachment #1: Type: text/plain, Size: 5456 bytes --]

On Wed, Oct 25, 2023 at 06:39:56PM +0800, Keith Zhao wrote:
> +static struct drm_crtc_state *
> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc_state *ori_state;
> +	struct vs_crtc_state *state;
> +
> +	if (!crtc->state)
> +		return NULL;
> +
> +	ori_state = to_vs_crtc_state(crtc->state);
> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return NULL;
> +
> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
> +
> +	state->output_fmt = ori_state->output_fmt;

That field is never set in your patch.

> +	state->encoder_type = ori_state->encoder_type;

That isn't either, and it's not clear why you would need the
encoder_type stored in the CRTC?

> +	state->bpp = ori_state->bpp;

You seem to derive that from output_fmt, it doesn't need to be in the
CRTC state.

> +	state->underflow = ori_state->underflow;

Assuming you're setting this from the interrupt handler, it's unsafe,
you shouldn't do that. What are you using it for?

> +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
> +	{ VS_SINGLE_DC,				"single dc mode" },
> +	{ VS_MULTI_DC_PRIMARY,		"primary dc for multi dc mode" },
> +	{ VS_MULTI_DC_SECONDARY,	"secondary dc for multi dc mode" },
> +};

Custom driver properties are a no-go:
https://docs.kernel.org/gpu/drm-kms.html#requirements

And

https://docs.kernel.org/gpu/drm-uapi.html#open-source-userspace-requirements

> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +	struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	struct dc_hw_display display;

Why are you rolling your own structure here, if it's exactly equivalent
to what drm_display_mode and the crtc_state provide?

> +void vs_dc_commit(struct vs_dc *dc)
> +{
> +	dc_hw_enable_shadow_register(&dc->hw, false);
> +
> +	dc_hw_commit(&dc->hw);
> +
> +	if (dc->first_frame)
> +		dc->first_frame = false;
> +
> +	dc_hw_enable_shadow_register(&dc->hw, true);
> +}

It's not clear to me what you're trying to do here, does the hardware
have latched registers that are only updated during vblank?

> +static int dc_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct drm_device *drm_dev = data;
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +	struct device_node *port;
> +	struct vs_crtc *crtc;
> +	struct vs_dc_info *dc_info;
> +	struct vs_plane *plane;
> +	struct vs_plane_info *plane_info;
> +	int i, ret;
> +	u32 ctrc_mask = 0;
> +
> +	if (!drm_dev || !dc) {
> +		dev_err(dev, "devices are not created.\n");
> +		return -ENODEV;
> +	}
> +
> +	ret = dc_init(dev);
> +	if (ret < 0) {
> +		drm_err(drm_dev, "Failed to initialize DC hardware.\n");
> +		return ret;
> +	}
> +
> +	port = of_get_child_by_name(dev->of_node, "port");
> +	if (!port) {
> +		drm_err(drm_dev, "no port node found\n");
> +		return -ENODEV;
> +	}
> +	of_node_put(port);
> +
> +	dc_info = dc->hw.info;
> +
> +	for (i = 0; i < dc_info->panel_num; i++) {
> +		crtc = vs_crtc_create(drm_dev, dc_info);
> +		if (!crtc) {
> +			drm_err(drm_dev, "Failed to create CRTC.\n");
> +			ret = -ENOMEM;
> +			return ret;
> +		}
> +
> +		crtc->base.port = port;
> +		crtc->dev = dev;
> +		dc->crtc[i] = crtc;
> +		ctrc_mask |= drm_crtc_mask(&crtc->base);
> +	}
> +
> +	for (i = 0; i < dc_info->plane_num; i++) {
> +		plane_info = (struct vs_plane_info *)&dc_info->planes[i];
> +
> +		if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
> +						drm_crtc_mask(&dc->crtc[0]->base));
> +		} else if (!strcmp(plane_info->name, "Primary_1") ||
> +				   !strcmp(plane_info->name, "Cursor_1")) {

Please use an enum and an id there.

> +static int vs_plane_atomic_set_property(struct drm_plane *plane,
> +					struct drm_plane_state *state,
> +					struct drm_property *property,
> +					uint64_t val)
> +{
> +	struct drm_device *dev = plane->dev;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
> +	int ret = 0;
> +
> +	if (property == vs_plane->degamma_mode) {
> +		if (vs_plane_state->degamma != val) {
> +			vs_plane_state->degamma = val;
> +			vs_plane_state->degamma_changed = true;
> +		} else {
> +			vs_plane_state->degamma_changed = false;
> +		}
> +	} else if (property == vs_plane->watermark_prop) {
> +		ret = _vs_plane_set_property_blob_from_id(dev,
> +							  &vs_plane_state->watermark,
> +							  val,
> +							  sizeof(struct drm_vs_watermark));
> +		return ret;
> +	} else if (property == vs_plane->color_mgmt_prop) {
> +		ret = _vs_plane_set_property_blob_from_id(dev,
> +							  &vs_plane_state->color_mgmt,
> +							  val,
> +							  sizeof(struct drm_vs_color_mgmt));
> +		return ret;
> +	} else if (property == vs_plane->roi_prop) {
> +		ret = _vs_plane_set_property_blob_from_id(dev,
> +							  &vs_plane_state->roi,
> +							  val,
> +							  sizeof(struct drm_vs_roi));
> +		return ret;
> +	} else {
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}

Same story than above for properties


Honestly, that driver is pretty massive, and you should be simplifying
it a lot of you want the initial contribution to be as smooth as
possible.

Things like all the tiling formats, the underflowing handling, all those
properties, etc can (and should) be added in a second step once the
foundations are in.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-10-25 13:57     ` Maxime Ripard
  0 siblings, 0 replies; 117+ messages in thread
From: Maxime Ripard @ 2023-10-25 13:57 UTC (permalink / raw)
  To: Keith Zhao
  Cc: Emil Renner Berthing, dri-devel, Krzysztof Kozlowski,
	linux-riscv, Sumit Semwal, Shengyang Chen, linux-media,
	devicetree, Conor Dooley, Albert Ou, Jagan Teki, linaro-mm-sig,
	Rob Herring, Chris Morgan, Paul Walmsley, Bjorn Andersson,
	linux-kernel, Changhuang Liang, Jack Zhu, Palmer Dabbelt,
	Thomas Zimmermann, Shawn Guo, christian.koenig

[-- Attachment #1: Type: text/plain, Size: 5456 bytes --]

On Wed, Oct 25, 2023 at 06:39:56PM +0800, Keith Zhao wrote:
> +static struct drm_crtc_state *
> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc_state *ori_state;
> +	struct vs_crtc_state *state;
> +
> +	if (!crtc->state)
> +		return NULL;
> +
> +	ori_state = to_vs_crtc_state(crtc->state);
> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return NULL;
> +
> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
> +
> +	state->output_fmt = ori_state->output_fmt;

That field is never set in your patch.

> +	state->encoder_type = ori_state->encoder_type;

That isn't either, and it's not clear why you would need the
encoder_type stored in the CRTC?

> +	state->bpp = ori_state->bpp;

You seem to derive that from output_fmt, it doesn't need to be in the
CRTC state.

> +	state->underflow = ori_state->underflow;

Assuming you're setting this from the interrupt handler, it's unsafe,
you shouldn't do that. What are you using it for?

> +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
> +	{ VS_SINGLE_DC,				"single dc mode" },
> +	{ VS_MULTI_DC_PRIMARY,		"primary dc for multi dc mode" },
> +	{ VS_MULTI_DC_SECONDARY,	"secondary dc for multi dc mode" },
> +};

Custom driver properties are a no-go:
https://docs.kernel.org/gpu/drm-kms.html#requirements

And

https://docs.kernel.org/gpu/drm-uapi.html#open-source-userspace-requirements

> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +	struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	struct dc_hw_display display;

Why are you rolling your own structure here, if it's exactly equivalent
to what drm_display_mode and the crtc_state provide?

> +void vs_dc_commit(struct vs_dc *dc)
> +{
> +	dc_hw_enable_shadow_register(&dc->hw, false);
> +
> +	dc_hw_commit(&dc->hw);
> +
> +	if (dc->first_frame)
> +		dc->first_frame = false;
> +
> +	dc_hw_enable_shadow_register(&dc->hw, true);
> +}

It's not clear to me what you're trying to do here, does the hardware
have latched registers that are only updated during vblank?

> +static int dc_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct drm_device *drm_dev = data;
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +	struct device_node *port;
> +	struct vs_crtc *crtc;
> +	struct vs_dc_info *dc_info;
> +	struct vs_plane *plane;
> +	struct vs_plane_info *plane_info;
> +	int i, ret;
> +	u32 ctrc_mask = 0;
> +
> +	if (!drm_dev || !dc) {
> +		dev_err(dev, "devices are not created.\n");
> +		return -ENODEV;
> +	}
> +
> +	ret = dc_init(dev);
> +	if (ret < 0) {
> +		drm_err(drm_dev, "Failed to initialize DC hardware.\n");
> +		return ret;
> +	}
> +
> +	port = of_get_child_by_name(dev->of_node, "port");
> +	if (!port) {
> +		drm_err(drm_dev, "no port node found\n");
> +		return -ENODEV;
> +	}
> +	of_node_put(port);
> +
> +	dc_info = dc->hw.info;
> +
> +	for (i = 0; i < dc_info->panel_num; i++) {
> +		crtc = vs_crtc_create(drm_dev, dc_info);
> +		if (!crtc) {
> +			drm_err(drm_dev, "Failed to create CRTC.\n");
> +			ret = -ENOMEM;
> +			return ret;
> +		}
> +
> +		crtc->base.port = port;
> +		crtc->dev = dev;
> +		dc->crtc[i] = crtc;
> +		ctrc_mask |= drm_crtc_mask(&crtc->base);
> +	}
> +
> +	for (i = 0; i < dc_info->plane_num; i++) {
> +		plane_info = (struct vs_plane_info *)&dc_info->planes[i];
> +
> +		if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
> +						drm_crtc_mask(&dc->crtc[0]->base));
> +		} else if (!strcmp(plane_info->name, "Primary_1") ||
> +				   !strcmp(plane_info->name, "Cursor_1")) {

Please use an enum and an id there.

> +static int vs_plane_atomic_set_property(struct drm_plane *plane,
> +					struct drm_plane_state *state,
> +					struct drm_property *property,
> +					uint64_t val)
> +{
> +	struct drm_device *dev = plane->dev;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
> +	int ret = 0;
> +
> +	if (property == vs_plane->degamma_mode) {
> +		if (vs_plane_state->degamma != val) {
> +			vs_plane_state->degamma = val;
> +			vs_plane_state->degamma_changed = true;
> +		} else {
> +			vs_plane_state->degamma_changed = false;
> +		}
> +	} else if (property == vs_plane->watermark_prop) {
> +		ret = _vs_plane_set_property_blob_from_id(dev,
> +							  &vs_plane_state->watermark,
> +							  val,
> +							  sizeof(struct drm_vs_watermark));
> +		return ret;
> +	} else if (property == vs_plane->color_mgmt_prop) {
> +		ret = _vs_plane_set_property_blob_from_id(dev,
> +							  &vs_plane_state->color_mgmt,
> +							  val,
> +							  sizeof(struct drm_vs_color_mgmt));
> +		return ret;
> +	} else if (property == vs_plane->roi_prop) {
> +		ret = _vs_plane_set_property_blob_from_id(dev,
> +							  &vs_plane_state->roi,
> +							  val,
> +							  sizeof(struct drm_vs_roi));
> +		return ret;
> +	} else {
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}

Same story than above for properties


Honestly, that driver is pretty massive, and you should be simplifying
it a lot of you want the initial contribution to be as smooth as
possible.

Things like all the tiling formats, the underflowing handling, all those
properties, etc can (and should) be added in a second step once the
foundations are in.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-10-25 13:57     ` Maxime Ripard
  0 siblings, 0 replies; 117+ messages in thread
From: Maxime Ripard @ 2023-10-25 13:57 UTC (permalink / raw)
  To: Keith Zhao
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, David Airlie, Daniel Vetter, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Emil Renner Berthing,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Maarten Lankhorst,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Jack Zhu, Shengyang Chen, Changhuang Liang


[-- Attachment #1.1: Type: text/plain, Size: 5456 bytes --]

On Wed, Oct 25, 2023 at 06:39:56PM +0800, Keith Zhao wrote:
> +static struct drm_crtc_state *
> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc_state *ori_state;
> +	struct vs_crtc_state *state;
> +
> +	if (!crtc->state)
> +		return NULL;
> +
> +	ori_state = to_vs_crtc_state(crtc->state);
> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return NULL;
> +
> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
> +
> +	state->output_fmt = ori_state->output_fmt;

That field is never set in your patch.

> +	state->encoder_type = ori_state->encoder_type;

That isn't either, and it's not clear why you would need the
encoder_type stored in the CRTC?

> +	state->bpp = ori_state->bpp;

You seem to derive that from output_fmt, it doesn't need to be in the
CRTC state.

> +	state->underflow = ori_state->underflow;

Assuming you're setting this from the interrupt handler, it's unsafe,
you shouldn't do that. What are you using it for?

> +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
> +	{ VS_SINGLE_DC,				"single dc mode" },
> +	{ VS_MULTI_DC_PRIMARY,		"primary dc for multi dc mode" },
> +	{ VS_MULTI_DC_SECONDARY,	"secondary dc for multi dc mode" },
> +};

Custom driver properties are a no-go:
https://docs.kernel.org/gpu/drm-kms.html#requirements

And

https://docs.kernel.org/gpu/drm-uapi.html#open-source-userspace-requirements

> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +	struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	struct dc_hw_display display;

Why are you rolling your own structure here, if it's exactly equivalent
to what drm_display_mode and the crtc_state provide?

> +void vs_dc_commit(struct vs_dc *dc)
> +{
> +	dc_hw_enable_shadow_register(&dc->hw, false);
> +
> +	dc_hw_commit(&dc->hw);
> +
> +	if (dc->first_frame)
> +		dc->first_frame = false;
> +
> +	dc_hw_enable_shadow_register(&dc->hw, true);
> +}

It's not clear to me what you're trying to do here, does the hardware
have latched registers that are only updated during vblank?

> +static int dc_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct drm_device *drm_dev = data;
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +	struct device_node *port;
> +	struct vs_crtc *crtc;
> +	struct vs_dc_info *dc_info;
> +	struct vs_plane *plane;
> +	struct vs_plane_info *plane_info;
> +	int i, ret;
> +	u32 ctrc_mask = 0;
> +
> +	if (!drm_dev || !dc) {
> +		dev_err(dev, "devices are not created.\n");
> +		return -ENODEV;
> +	}
> +
> +	ret = dc_init(dev);
> +	if (ret < 0) {
> +		drm_err(drm_dev, "Failed to initialize DC hardware.\n");
> +		return ret;
> +	}
> +
> +	port = of_get_child_by_name(dev->of_node, "port");
> +	if (!port) {
> +		drm_err(drm_dev, "no port node found\n");
> +		return -ENODEV;
> +	}
> +	of_node_put(port);
> +
> +	dc_info = dc->hw.info;
> +
> +	for (i = 0; i < dc_info->panel_num; i++) {
> +		crtc = vs_crtc_create(drm_dev, dc_info);
> +		if (!crtc) {
> +			drm_err(drm_dev, "Failed to create CRTC.\n");
> +			ret = -ENOMEM;
> +			return ret;
> +		}
> +
> +		crtc->base.port = port;
> +		crtc->dev = dev;
> +		dc->crtc[i] = crtc;
> +		ctrc_mask |= drm_crtc_mask(&crtc->base);
> +	}
> +
> +	for (i = 0; i < dc_info->plane_num; i++) {
> +		plane_info = (struct vs_plane_info *)&dc_info->planes[i];
> +
> +		if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
> +						drm_crtc_mask(&dc->crtc[0]->base));
> +		} else if (!strcmp(plane_info->name, "Primary_1") ||
> +				   !strcmp(plane_info->name, "Cursor_1")) {

Please use an enum and an id there.

> +static int vs_plane_atomic_set_property(struct drm_plane *plane,
> +					struct drm_plane_state *state,
> +					struct drm_property *property,
> +					uint64_t val)
> +{
> +	struct drm_device *dev = plane->dev;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
> +	int ret = 0;
> +
> +	if (property == vs_plane->degamma_mode) {
> +		if (vs_plane_state->degamma != val) {
> +			vs_plane_state->degamma = val;
> +			vs_plane_state->degamma_changed = true;
> +		} else {
> +			vs_plane_state->degamma_changed = false;
> +		}
> +	} else if (property == vs_plane->watermark_prop) {
> +		ret = _vs_plane_set_property_blob_from_id(dev,
> +							  &vs_plane_state->watermark,
> +							  val,
> +							  sizeof(struct drm_vs_watermark));
> +		return ret;
> +	} else if (property == vs_plane->color_mgmt_prop) {
> +		ret = _vs_plane_set_property_blob_from_id(dev,
> +							  &vs_plane_state->color_mgmt,
> +							  val,
> +							  sizeof(struct drm_vs_color_mgmt));
> +		return ret;
> +	} else if (property == vs_plane->roi_prop) {
> +		ret = _vs_plane_set_property_blob_from_id(dev,
> +							  &vs_plane_state->roi,
> +							  val,
> +							  sizeof(struct drm_vs_roi));
> +		return ret;
> +	} else {
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}

Same story than above for properties


Honestly, that driver is pretty massive, and you should be simplifying
it a lot of you want the initial contribution to be as smooth as
possible.

Things like all the tiling formats, the underflowing handling, all those
properties, etc can (and should) be added in a second step once the
foundations are in.

Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 161 bytes --]

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
  2023-10-25 10:39   ` Keith Zhao
  (?)
@ 2023-10-25 14:07     ` Maxime Ripard
  -1 siblings, 0 replies; 117+ messages in thread
From: Maxime Ripard @ 2023-10-25 14:07 UTC (permalink / raw)
  To: Keith Zhao
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, David Airlie, Daniel Vetter, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Emil Renner Berthing,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Maarten Lankhorst,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Jack Zhu, Shengyang Chen, Changhuang Liang

[-- Attachment #1: Type: text/plain, Size: 5848 bytes --]

On Wed, Oct 25, 2023 at 06:39:57PM +0800, Keith Zhao wrote:
> +static int starfive_hdmi_setup(struct starfive_hdmi *hdmi,
> +			       struct drm_display_mode *mode)
> +{
> +	hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
> +	hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
> +	hdmi->hdmi_data.vic = drm_match_cea_mode(mode);

You're never using that variable.

> +	hdmi->tmds_rate = mode->clock * 1000;
> +	starfive_hdmi_phy_clk_set_rate(hdmi);
> +
> +	while (!(hdmi_readb(hdmi, STARFIVE_PRE_PLL_LOCK_STATUS) & 0x1))
> +		continue;
> +	while (!(hdmi_readb(hdmi, STARFIVE_POST_PLL_LOCK_STATUS) & 0x1))
> +		continue;
> +
> +	/*turn on LDO*/
> +	hdmi_writeb(hdmi, STARFIVE_LDO_CONTROL, STARFIVE_LDO_ENABLE);
> +	/*turn on serializer*/
> +	hdmi_writeb(hdmi, STARFIVE_SERIALIER_CONTROL, STARFIVE_SERIALIER_ENABLE);
> +
> +	starfive_hdmi_tx_phy_power_down(hdmi);
> +	starfive_hdmi_config_video_timing(hdmi, mode);
> +	starfive_hdmi_tx_phy_power_on(hdmi);
> +
> +	starfive_hdmi_tmds_driver_on(hdmi);
> +	starfive_hdmi_sync_tmds(hdmi);
> +
> +	return 0;
> +}
> +
> +static void starfive_hdmi_encoder_mode_set(struct drm_encoder *encoder,
> +					   struct drm_display_mode *mode,
> +					   struct drm_display_mode *adj_mode)
> +{
> +	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> +
> +	drm_mode_copy(&hdmi->previous_mode, adj_mode);
> +}

You're never using that field, and it's not the previous mode, but the current mode.

> +static void starfive_hdmi_encoder_enable(struct drm_encoder *encoder)
> +{
> +	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> +	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
> +	int ret, idx;
> +	struct drm_device *drm = hdmi->connector.dev;
> +
> +	if (drm && !drm_dev_enter(drm, &idx))
> +		return;
> +
> +	ret = pm_runtime_get_sync(hdmi->dev);
> +	if (ret < 0)
> +		return;

pm_runtime_resume_and_get is what you want here

> +	mdelay(10);

???

Sprinkling delays here and there isn't great. What is the issue this is
trying to workaround?

> +	starfive_hdmi_setup(hdmi, mode);
> +
> +	if (drm)
> +		drm_dev_exit(idx);
> +}
> +
> +static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
> +{
> +	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> +
> +	int idx;
> +	struct drm_device *drm = hdmi->connector.dev;
> +
> +	if (drm && !drm_dev_enter(drm, &idx))
> +		return;
> +
> +	pm_runtime_put(hdmi->dev);

pm_runtime calls should be safe anytime. If you need to protect them
through a drm_dev_enter call, that call should be in the pm_runtime hook
itself.

> +	if (drm)
> +		drm_dev_exit(idx);
> +}
> +
> +static int
> +starfive_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
> +				   struct drm_crtc_state *crtc_state,
> +				   struct drm_connector_state *conn_state)
> +{
> +	bool valid = false;
> +	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
> +
> +	vs_crtc_state->encoder_type = encoder->encoder_type;
> +	vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;

Ok, so those two will always be ENCODER_TMDS and RGB888. Drop them from
your CRTC state and use the proper defines there.

> +	const struct pre_pll_config *cfg = pre_pll_cfg_table;
> +	int pclk = mode->clock * 1000;
> +
> +	for (; cfg->pixclock != 0; cfg++) {
> +		if (pclk == cfg->pixclock) {
> +			if (pclk > 297000000)
> +				continue;
> +
> +			valid = true;
> +			break;
> +		}
> +	}
> +
> +	return (valid) ? 0 : -EINVAL;
> +}
> +
> +static const struct drm_encoder_helper_funcs starfive_hdmi_encoder_helper_funcs = {
> +	.enable     = starfive_hdmi_encoder_enable,
> +	.disable    = starfive_hdmi_encoder_disable,
> +	.mode_set   = starfive_hdmi_encoder_mode_set,
> +	.atomic_check = starfive_hdmi_encoder_atomic_check,
> +};
> +
> +static enum drm_connector_status
> +starfive_hdmi_connector_detect(struct drm_connector *connector, bool force)
> +{
> +	struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
> +	struct drm_device *drm = hdmi->connector.dev;
> +	int ret;
> +	int idx;
> +
> +	if (drm && !drm_dev_enter(drm, &idx))
> +		return connector_status_disconnected;
> +
> +	ret = pm_runtime_get_sync(hdmi->dev);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
> +		connector_status_connected : connector_status_disconnected;
> +	pm_runtime_put(hdmi->dev);
> +
> +	if (drm)
> +		drm_dev_exit(idx);
> +
> +	return ret;
> +}
> +
> +static int starfive_hdmi_connector_get_modes(struct drm_connector *connector)
> +{
> +	struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
> +	struct edid *edid;
> +	int ret = 0;
> +
> +	if (!hdmi->ddc)
> +		return 0;
> +	ret = pm_runtime_get_sync(hdmi->dev);
> +	if (ret < 0)
> +		return ret;
> +
> +	edid = drm_get_edid(connector, hdmi->ddc);
> +	if (edid) {
> +		hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);

You're not using that field anywhere, and it's available in drm_display_info already.

> +		hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);

Ditto

> +struct hdmi_data_info {
> +	int vic;
> +	bool sink_is_hdmi;
> +	bool sink_has_audio;
> +	unsigned int enc_in_format;

You're not using it

> +	unsigned int enc_out_format;

Ditto

> +	unsigned int colorimetry;

Ditto

Which means that as things stands, you can get rid of that entire
structure.

> +};
> +
> +struct starfive_hdmi {
> +	struct device *dev;
> +	struct drm_device *drm_dev;
> +	struct drm_encoder	encoder;
> +	struct drm_connector	connector;
> +
> +	struct starfive_hdmi_i2c *i2c;
> +
> +	int irq;
> +	struct clk *sys_clk;
> +	struct clk *mclk;
> +	struct clk *bclk;
> +	struct reset_control *tx_rst;
> +	void __iomem *regs;
> +
> +	struct i2c_adapter *ddc;
> +
> +	unsigned long tmds_rate;

You're not using that either.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-10-25 14:07     ` Maxime Ripard
  0 siblings, 0 replies; 117+ messages in thread
From: Maxime Ripard @ 2023-10-25 14:07 UTC (permalink / raw)
  To: Keith Zhao
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, David Airlie, Daniel Vetter, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Emil Renner Berthing,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Maarten Lankhorst,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Jack Zhu, Shengyang Chen, Changhuang Liang


[-- Attachment #1.1: Type: text/plain, Size: 5848 bytes --]

On Wed, Oct 25, 2023 at 06:39:57PM +0800, Keith Zhao wrote:
> +static int starfive_hdmi_setup(struct starfive_hdmi *hdmi,
> +			       struct drm_display_mode *mode)
> +{
> +	hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
> +	hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
> +	hdmi->hdmi_data.vic = drm_match_cea_mode(mode);

You're never using that variable.

> +	hdmi->tmds_rate = mode->clock * 1000;
> +	starfive_hdmi_phy_clk_set_rate(hdmi);
> +
> +	while (!(hdmi_readb(hdmi, STARFIVE_PRE_PLL_LOCK_STATUS) & 0x1))
> +		continue;
> +	while (!(hdmi_readb(hdmi, STARFIVE_POST_PLL_LOCK_STATUS) & 0x1))
> +		continue;
> +
> +	/*turn on LDO*/
> +	hdmi_writeb(hdmi, STARFIVE_LDO_CONTROL, STARFIVE_LDO_ENABLE);
> +	/*turn on serializer*/
> +	hdmi_writeb(hdmi, STARFIVE_SERIALIER_CONTROL, STARFIVE_SERIALIER_ENABLE);
> +
> +	starfive_hdmi_tx_phy_power_down(hdmi);
> +	starfive_hdmi_config_video_timing(hdmi, mode);
> +	starfive_hdmi_tx_phy_power_on(hdmi);
> +
> +	starfive_hdmi_tmds_driver_on(hdmi);
> +	starfive_hdmi_sync_tmds(hdmi);
> +
> +	return 0;
> +}
> +
> +static void starfive_hdmi_encoder_mode_set(struct drm_encoder *encoder,
> +					   struct drm_display_mode *mode,
> +					   struct drm_display_mode *adj_mode)
> +{
> +	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> +
> +	drm_mode_copy(&hdmi->previous_mode, adj_mode);
> +}

You're never using that field, and it's not the previous mode, but the current mode.

> +static void starfive_hdmi_encoder_enable(struct drm_encoder *encoder)
> +{
> +	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> +	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
> +	int ret, idx;
> +	struct drm_device *drm = hdmi->connector.dev;
> +
> +	if (drm && !drm_dev_enter(drm, &idx))
> +		return;
> +
> +	ret = pm_runtime_get_sync(hdmi->dev);
> +	if (ret < 0)
> +		return;

pm_runtime_resume_and_get is what you want here

> +	mdelay(10);

???

Sprinkling delays here and there isn't great. What is the issue this is
trying to workaround?

> +	starfive_hdmi_setup(hdmi, mode);
> +
> +	if (drm)
> +		drm_dev_exit(idx);
> +}
> +
> +static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
> +{
> +	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> +
> +	int idx;
> +	struct drm_device *drm = hdmi->connector.dev;
> +
> +	if (drm && !drm_dev_enter(drm, &idx))
> +		return;
> +
> +	pm_runtime_put(hdmi->dev);

pm_runtime calls should be safe anytime. If you need to protect them
through a drm_dev_enter call, that call should be in the pm_runtime hook
itself.

> +	if (drm)
> +		drm_dev_exit(idx);
> +}
> +
> +static int
> +starfive_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
> +				   struct drm_crtc_state *crtc_state,
> +				   struct drm_connector_state *conn_state)
> +{
> +	bool valid = false;
> +	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
> +
> +	vs_crtc_state->encoder_type = encoder->encoder_type;
> +	vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;

Ok, so those two will always be ENCODER_TMDS and RGB888. Drop them from
your CRTC state and use the proper defines there.

> +	const struct pre_pll_config *cfg = pre_pll_cfg_table;
> +	int pclk = mode->clock * 1000;
> +
> +	for (; cfg->pixclock != 0; cfg++) {
> +		if (pclk == cfg->pixclock) {
> +			if (pclk > 297000000)
> +				continue;
> +
> +			valid = true;
> +			break;
> +		}
> +	}
> +
> +	return (valid) ? 0 : -EINVAL;
> +}
> +
> +static const struct drm_encoder_helper_funcs starfive_hdmi_encoder_helper_funcs = {
> +	.enable     = starfive_hdmi_encoder_enable,
> +	.disable    = starfive_hdmi_encoder_disable,
> +	.mode_set   = starfive_hdmi_encoder_mode_set,
> +	.atomic_check = starfive_hdmi_encoder_atomic_check,
> +};
> +
> +static enum drm_connector_status
> +starfive_hdmi_connector_detect(struct drm_connector *connector, bool force)
> +{
> +	struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
> +	struct drm_device *drm = hdmi->connector.dev;
> +	int ret;
> +	int idx;
> +
> +	if (drm && !drm_dev_enter(drm, &idx))
> +		return connector_status_disconnected;
> +
> +	ret = pm_runtime_get_sync(hdmi->dev);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
> +		connector_status_connected : connector_status_disconnected;
> +	pm_runtime_put(hdmi->dev);
> +
> +	if (drm)
> +		drm_dev_exit(idx);
> +
> +	return ret;
> +}
> +
> +static int starfive_hdmi_connector_get_modes(struct drm_connector *connector)
> +{
> +	struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
> +	struct edid *edid;
> +	int ret = 0;
> +
> +	if (!hdmi->ddc)
> +		return 0;
> +	ret = pm_runtime_get_sync(hdmi->dev);
> +	if (ret < 0)
> +		return ret;
> +
> +	edid = drm_get_edid(connector, hdmi->ddc);
> +	if (edid) {
> +		hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);

You're not using that field anywhere, and it's available in drm_display_info already.

> +		hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);

Ditto

> +struct hdmi_data_info {
> +	int vic;
> +	bool sink_is_hdmi;
> +	bool sink_has_audio;
> +	unsigned int enc_in_format;

You're not using it

> +	unsigned int enc_out_format;

Ditto

> +	unsigned int colorimetry;

Ditto

Which means that as things stands, you can get rid of that entire
structure.

> +};
> +
> +struct starfive_hdmi {
> +	struct device *dev;
> +	struct drm_device *drm_dev;
> +	struct drm_encoder	encoder;
> +	struct drm_connector	connector;
> +
> +	struct starfive_hdmi_i2c *i2c;
> +
> +	int irq;
> +	struct clk *sys_clk;
> +	struct clk *mclk;
> +	struct clk *bclk;
> +	struct reset_control *tx_rst;
> +	void __iomem *regs;
> +
> +	struct i2c_adapter *ddc;
> +
> +	unsigned long tmds_rate;

You're not using that either.

Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 161 bytes --]

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-10-25 14:07     ` Maxime Ripard
  0 siblings, 0 replies; 117+ messages in thread
From: Maxime Ripard @ 2023-10-25 14:07 UTC (permalink / raw)
  To: Keith Zhao
  Cc: Emil Renner Berthing, dri-devel, Krzysztof Kozlowski,
	linux-riscv, Sumit Semwal, Shengyang Chen, linux-media,
	devicetree, Conor Dooley, Albert Ou, Jagan Teki, linaro-mm-sig,
	Rob Herring, Chris Morgan, Paul Walmsley, Bjorn Andersson,
	linux-kernel, Changhuang Liang, Jack Zhu, Palmer Dabbelt,
	Thomas Zimmermann, Shawn Guo, christian.koenig

[-- Attachment #1: Type: text/plain, Size: 5848 bytes --]

On Wed, Oct 25, 2023 at 06:39:57PM +0800, Keith Zhao wrote:
> +static int starfive_hdmi_setup(struct starfive_hdmi *hdmi,
> +			       struct drm_display_mode *mode)
> +{
> +	hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
> +	hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
> +	hdmi->hdmi_data.vic = drm_match_cea_mode(mode);

You're never using that variable.

> +	hdmi->tmds_rate = mode->clock * 1000;
> +	starfive_hdmi_phy_clk_set_rate(hdmi);
> +
> +	while (!(hdmi_readb(hdmi, STARFIVE_PRE_PLL_LOCK_STATUS) & 0x1))
> +		continue;
> +	while (!(hdmi_readb(hdmi, STARFIVE_POST_PLL_LOCK_STATUS) & 0x1))
> +		continue;
> +
> +	/*turn on LDO*/
> +	hdmi_writeb(hdmi, STARFIVE_LDO_CONTROL, STARFIVE_LDO_ENABLE);
> +	/*turn on serializer*/
> +	hdmi_writeb(hdmi, STARFIVE_SERIALIER_CONTROL, STARFIVE_SERIALIER_ENABLE);
> +
> +	starfive_hdmi_tx_phy_power_down(hdmi);
> +	starfive_hdmi_config_video_timing(hdmi, mode);
> +	starfive_hdmi_tx_phy_power_on(hdmi);
> +
> +	starfive_hdmi_tmds_driver_on(hdmi);
> +	starfive_hdmi_sync_tmds(hdmi);
> +
> +	return 0;
> +}
> +
> +static void starfive_hdmi_encoder_mode_set(struct drm_encoder *encoder,
> +					   struct drm_display_mode *mode,
> +					   struct drm_display_mode *adj_mode)
> +{
> +	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> +
> +	drm_mode_copy(&hdmi->previous_mode, adj_mode);
> +}

You're never using that field, and it's not the previous mode, but the current mode.

> +static void starfive_hdmi_encoder_enable(struct drm_encoder *encoder)
> +{
> +	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> +	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
> +	int ret, idx;
> +	struct drm_device *drm = hdmi->connector.dev;
> +
> +	if (drm && !drm_dev_enter(drm, &idx))
> +		return;
> +
> +	ret = pm_runtime_get_sync(hdmi->dev);
> +	if (ret < 0)
> +		return;

pm_runtime_resume_and_get is what you want here

> +	mdelay(10);

???

Sprinkling delays here and there isn't great. What is the issue this is
trying to workaround?

> +	starfive_hdmi_setup(hdmi, mode);
> +
> +	if (drm)
> +		drm_dev_exit(idx);
> +}
> +
> +static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
> +{
> +	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> +
> +	int idx;
> +	struct drm_device *drm = hdmi->connector.dev;
> +
> +	if (drm && !drm_dev_enter(drm, &idx))
> +		return;
> +
> +	pm_runtime_put(hdmi->dev);

pm_runtime calls should be safe anytime. If you need to protect them
through a drm_dev_enter call, that call should be in the pm_runtime hook
itself.

> +	if (drm)
> +		drm_dev_exit(idx);
> +}
> +
> +static int
> +starfive_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
> +				   struct drm_crtc_state *crtc_state,
> +				   struct drm_connector_state *conn_state)
> +{
> +	bool valid = false;
> +	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
> +
> +	vs_crtc_state->encoder_type = encoder->encoder_type;
> +	vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;

Ok, so those two will always be ENCODER_TMDS and RGB888. Drop them from
your CRTC state and use the proper defines there.

> +	const struct pre_pll_config *cfg = pre_pll_cfg_table;
> +	int pclk = mode->clock * 1000;
> +
> +	for (; cfg->pixclock != 0; cfg++) {
> +		if (pclk == cfg->pixclock) {
> +			if (pclk > 297000000)
> +				continue;
> +
> +			valid = true;
> +			break;
> +		}
> +	}
> +
> +	return (valid) ? 0 : -EINVAL;
> +}
> +
> +static const struct drm_encoder_helper_funcs starfive_hdmi_encoder_helper_funcs = {
> +	.enable     = starfive_hdmi_encoder_enable,
> +	.disable    = starfive_hdmi_encoder_disable,
> +	.mode_set   = starfive_hdmi_encoder_mode_set,
> +	.atomic_check = starfive_hdmi_encoder_atomic_check,
> +};
> +
> +static enum drm_connector_status
> +starfive_hdmi_connector_detect(struct drm_connector *connector, bool force)
> +{
> +	struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
> +	struct drm_device *drm = hdmi->connector.dev;
> +	int ret;
> +	int idx;
> +
> +	if (drm && !drm_dev_enter(drm, &idx))
> +		return connector_status_disconnected;
> +
> +	ret = pm_runtime_get_sync(hdmi->dev);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
> +		connector_status_connected : connector_status_disconnected;
> +	pm_runtime_put(hdmi->dev);
> +
> +	if (drm)
> +		drm_dev_exit(idx);
> +
> +	return ret;
> +}
> +
> +static int starfive_hdmi_connector_get_modes(struct drm_connector *connector)
> +{
> +	struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
> +	struct edid *edid;
> +	int ret = 0;
> +
> +	if (!hdmi->ddc)
> +		return 0;
> +	ret = pm_runtime_get_sync(hdmi->dev);
> +	if (ret < 0)
> +		return ret;
> +
> +	edid = drm_get_edid(connector, hdmi->ddc);
> +	if (edid) {
> +		hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);

You're not using that field anywhere, and it's available in drm_display_info already.

> +		hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);

Ditto

> +struct hdmi_data_info {
> +	int vic;
> +	bool sink_is_hdmi;
> +	bool sink_has_audio;
> +	unsigned int enc_in_format;

You're not using it

> +	unsigned int enc_out_format;

Ditto

> +	unsigned int colorimetry;

Ditto

Which means that as things stands, you can get rid of that entire
structure.

> +};
> +
> +struct starfive_hdmi {
> +	struct device *dev;
> +	struct drm_device *drm_dev;
> +	struct drm_encoder	encoder;
> +	struct drm_connector	connector;
> +
> +	struct starfive_hdmi_i2c *i2c;
> +
> +	int irq;
> +	struct clk *sys_clk;
> +	struct clk *mclk;
> +	struct clk *bclk;
> +	struct reset_control *tx_rst;
> +	void __iomem *regs;
> +
> +	struct i2c_adapter *ddc;
> +
> +	unsigned long tmds_rate;

You're not using that either.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 3/6] drm/fourcc: Add drm/vs tiled modifiers
  2023-10-25 10:39   ` Keith Zhao
  (?)
@ 2023-10-25 15:28     ` Dmitry Baryshkov
  -1 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-10-25 15:28 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig

On 25/10/2023 13:39, Keith Zhao wrote:
> For each modifier, add the corresponding description
> 
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>   include/uapi/drm/drm_fourcc.h | 57 +++++++++++++++++++++++++++++++++++
>   1 file changed, 57 insertions(+)
> 
> diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h
> index 8db7fd3f7..a580a848c 100644
> --- a/include/uapi/drm/drm_fourcc.h
> +++ b/include/uapi/drm/drm_fourcc.h
> @@ -419,6 +419,7 @@ extern "C" {
>   #define DRM_FORMAT_MOD_VENDOR_ARM     0x08
>   #define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09
>   #define DRM_FORMAT_MOD_VENDOR_AMLOGIC 0x0a
> +#define DRM_FORMAT_MOD_VENDOR_VERISILICON 0x0b
>   
>   /* add more to the end as needed */
>   
> @@ -1562,6 +1563,62 @@ drm_fourcc_canonicalize_nvidia_format_mod(__u64 modifier)
>   #define AMD_FMT_MOD_CLEAR(field) \
>   	(~((__u64)AMD_FMT_MOD_##field##_MASK << AMD_FMT_MOD_##field##_SHIFT))
>   
> +#define DRM_FORMAT_MOD_VERISILICON_TYPE_NORMAL					0x00
> +#define DRM_FORMAT_MOD_VERISILICON_TYPE_MASK					((__u64)0x3 << 54)
> +
> +#define fourcc_mod_vs_code(type, val) \
> +	fourcc_mod_code(VERISILICON, ((((__u64)type) << 54) | (val)))

Please use fourcc_mode_code directly.

> +
> +#define DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK				0x1F
> +
> +/*
> + * An x-major 8x8 super tile consists of 64 8x8 sub-tiles in total.
> + * Each 8x8 sub-tile consists of four standard tiles .
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8		0x02
> +
> +/*
> + * A y-major 8x8 super tile consists of 64 8x8 sub-tiles in total.
> + * Each 8x8 sub-tile consists of four standard tiles .
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8		0x03
> +
> +/*
> + * An 8x8 tile consists of four standard tiles
> + * that are organized in Z-order.
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_TILE_8X8						0x04
> +
> +/*
> + * An 8x4 tile consists of two standard tiles
> + * that are organized in Z-order.
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_TILE_8X4						0x07
> +
> +/*
> + * An x-major 8x4 super tile consists of 128 8x4 sub-tiles in total.
> + * Each 8x4 sub-tile consists of two standard tiles.
> + * two standard tiles also same with DRM_FORMAT_MOD_VS_TILE_8X4
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4		0x0B
> +
> +/*
> + * A y-major 4x8 super tile consists of 128 4x8 sub-tiles in total.
> + * Each 4x8 sub-tile consists of two standard tiles.
> + * two standard tiles also same with DRM_FORMAT_MOD_VS_TILE_8X4
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8    0x0C
> +
> +#define fourcc_mod_vs_norm_code(tile) \
> +	fourcc_mod_vs_code(DRM_FORMAT_MOD_VERISILICON_TYPE_NORMAL, \
> +				(tile))

1) this is not a part of uAPI
2) please use fourcc_mod_code directly.

> +
>   #if defined(__cplusplus)
>   }
>   #endif

-- 
With best wishes
Dmitry


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 3/6] drm/fourcc: Add drm/vs tiled modifiers
@ 2023-10-25 15:28     ` Dmitry Baryshkov
  0 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-10-25 15:28 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: Conor Dooley, Albert Ou, Emil Renner Berthing, christian.koenig,
	Thomas Zimmermann, Bjorn Andersson, Chris Morgan, Maxime Ripard,
	Jagan Teki, Jack Zhu, Rob Herring, Palmer Dabbelt,
	Krzysztof Kozlowski, Paul Walmsley, Shengyang Chen,
	Changhuang Liang, Shawn Guo, Sumit Semwal

On 25/10/2023 13:39, Keith Zhao wrote:
> For each modifier, add the corresponding description
> 
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>   include/uapi/drm/drm_fourcc.h | 57 +++++++++++++++++++++++++++++++++++
>   1 file changed, 57 insertions(+)
> 
> diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h
> index 8db7fd3f7..a580a848c 100644
> --- a/include/uapi/drm/drm_fourcc.h
> +++ b/include/uapi/drm/drm_fourcc.h
> @@ -419,6 +419,7 @@ extern "C" {
>   #define DRM_FORMAT_MOD_VENDOR_ARM     0x08
>   #define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09
>   #define DRM_FORMAT_MOD_VENDOR_AMLOGIC 0x0a
> +#define DRM_FORMAT_MOD_VENDOR_VERISILICON 0x0b
>   
>   /* add more to the end as needed */
>   
> @@ -1562,6 +1563,62 @@ drm_fourcc_canonicalize_nvidia_format_mod(__u64 modifier)
>   #define AMD_FMT_MOD_CLEAR(field) \
>   	(~((__u64)AMD_FMT_MOD_##field##_MASK << AMD_FMT_MOD_##field##_SHIFT))
>   
> +#define DRM_FORMAT_MOD_VERISILICON_TYPE_NORMAL					0x00
> +#define DRM_FORMAT_MOD_VERISILICON_TYPE_MASK					((__u64)0x3 << 54)
> +
> +#define fourcc_mod_vs_code(type, val) \
> +	fourcc_mod_code(VERISILICON, ((((__u64)type) << 54) | (val)))

Please use fourcc_mode_code directly.

> +
> +#define DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK				0x1F
> +
> +/*
> + * An x-major 8x8 super tile consists of 64 8x8 sub-tiles in total.
> + * Each 8x8 sub-tile consists of four standard tiles .
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8		0x02
> +
> +/*
> + * A y-major 8x8 super tile consists of 64 8x8 sub-tiles in total.
> + * Each 8x8 sub-tile consists of four standard tiles .
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8		0x03
> +
> +/*
> + * An 8x8 tile consists of four standard tiles
> + * that are organized in Z-order.
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_TILE_8X8						0x04
> +
> +/*
> + * An 8x4 tile consists of two standard tiles
> + * that are organized in Z-order.
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_TILE_8X4						0x07
> +
> +/*
> + * An x-major 8x4 super tile consists of 128 8x4 sub-tiles in total.
> + * Each 8x4 sub-tile consists of two standard tiles.
> + * two standard tiles also same with DRM_FORMAT_MOD_VS_TILE_8X4
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4		0x0B
> +
> +/*
> + * A y-major 4x8 super tile consists of 128 4x8 sub-tiles in total.
> + * Each 4x8 sub-tile consists of two standard tiles.
> + * two standard tiles also same with DRM_FORMAT_MOD_VS_TILE_8X4
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8    0x0C
> +
> +#define fourcc_mod_vs_norm_code(tile) \
> +	fourcc_mod_vs_code(DRM_FORMAT_MOD_VERISILICON_TYPE_NORMAL, \
> +				(tile))

1) this is not a part of uAPI
2) please use fourcc_mod_code directly.

> +
>   #if defined(__cplusplus)
>   }
>   #endif

-- 
With best wishes
Dmitry


^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 3/6] drm/fourcc: Add drm/vs tiled modifiers
@ 2023-10-25 15:28     ` Dmitry Baryshkov
  0 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-10-25 15:28 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig

On 25/10/2023 13:39, Keith Zhao wrote:
> For each modifier, add the corresponding description
> 
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>   include/uapi/drm/drm_fourcc.h | 57 +++++++++++++++++++++++++++++++++++
>   1 file changed, 57 insertions(+)
> 
> diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h
> index 8db7fd3f7..a580a848c 100644
> --- a/include/uapi/drm/drm_fourcc.h
> +++ b/include/uapi/drm/drm_fourcc.h
> @@ -419,6 +419,7 @@ extern "C" {
>   #define DRM_FORMAT_MOD_VENDOR_ARM     0x08
>   #define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09
>   #define DRM_FORMAT_MOD_VENDOR_AMLOGIC 0x0a
> +#define DRM_FORMAT_MOD_VENDOR_VERISILICON 0x0b
>   
>   /* add more to the end as needed */
>   
> @@ -1562,6 +1563,62 @@ drm_fourcc_canonicalize_nvidia_format_mod(__u64 modifier)
>   #define AMD_FMT_MOD_CLEAR(field) \
>   	(~((__u64)AMD_FMT_MOD_##field##_MASK << AMD_FMT_MOD_##field##_SHIFT))
>   
> +#define DRM_FORMAT_MOD_VERISILICON_TYPE_NORMAL					0x00
> +#define DRM_FORMAT_MOD_VERISILICON_TYPE_MASK					((__u64)0x3 << 54)
> +
> +#define fourcc_mod_vs_code(type, val) \
> +	fourcc_mod_code(VERISILICON, ((((__u64)type) << 54) | (val)))

Please use fourcc_mode_code directly.

> +
> +#define DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK				0x1F
> +
> +/*
> + * An x-major 8x8 super tile consists of 64 8x8 sub-tiles in total.
> + * Each 8x8 sub-tile consists of four standard tiles .
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8		0x02
> +
> +/*
> + * A y-major 8x8 super tile consists of 64 8x8 sub-tiles in total.
> + * Each 8x8 sub-tile consists of four standard tiles .
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8		0x03
> +
> +/*
> + * An 8x8 tile consists of four standard tiles
> + * that are organized in Z-order.
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_TILE_8X8						0x04
> +
> +/*
> + * An 8x4 tile consists of two standard tiles
> + * that are organized in Z-order.
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_TILE_8X4						0x07
> +
> +/*
> + * An x-major 8x4 super tile consists of 128 8x4 sub-tiles in total.
> + * Each 8x4 sub-tile consists of two standard tiles.
> + * two standard tiles also same with DRM_FORMAT_MOD_VS_TILE_8X4
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4		0x0B
> +
> +/*
> + * A y-major 4x8 super tile consists of 128 4x8 sub-tiles in total.
> + * Each 4x8 sub-tile consists of two standard tiles.
> + * two standard tiles also same with DRM_FORMAT_MOD_VS_TILE_8X4
> + * standard tiles (see Vivante 4x4 tiling layout)
> + */
> +#define DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8    0x0C
> +
> +#define fourcc_mod_vs_norm_code(tile) \
> +	fourcc_mod_vs_code(DRM_FORMAT_MOD_VERISILICON_TYPE_NORMAL, \
> +				(tile))

1) this is not a part of uAPI
2) please use fourcc_mod_code directly.

> +
>   #if defined(__cplusplus)
>   }
>   #endif

-- 
With best wishes
Dmitry


^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 3/6] drm/fourcc: Add drm/vs tiled modifiers
  2023-10-25 10:39   ` Keith Zhao
  (?)
@ 2023-10-25 15:38     ` Simon Ser
  -1 siblings, 0 replies; 117+ messages in thread
From: Simon Ser @ 2023-10-25 15:38 UTC (permalink / raw)
  To: Keith Zhao
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Maxime Ripard, Jagan Teki, Rob Herring,
	Chris Morgan, Paul Walmsley, Bjorn Andersson, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Shawn Guo, christian.koenig

Would be good to have an overview comment to explain how bits in the
modifier are used and how everything is tied up together, e.g. what the
type and tile mode mean. Also some docs for DRM_FORMAT_MOD_VERISILICON_TYPE_NORMAL
would be nice. (If there is no other type, this can be removed, the bits
will be left as zero and can be extended later if needed.)

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 3/6] drm/fourcc: Add drm/vs tiled modifiers
@ 2023-10-25 15:38     ` Simon Ser
  0 siblings, 0 replies; 117+ messages in thread
From: Simon Ser @ 2023-10-25 15:38 UTC (permalink / raw)
  To: Keith Zhao
  Cc: Emil Renner Berthing, dri-devel, Krzysztof Kozlowski,
	linux-riscv, Sumit Semwal, Shengyang Chen, linux-media,
	devicetree, Conor Dooley, Albert Ou, Maxime Ripard, Jagan Teki,
	linaro-mm-sig, Rob Herring, Chris Morgan, Paul Walmsley,
	Bjorn Andersson, linux-kernel, christian.koenig, Jack Zhu,
	Palmer Dabbelt, Thomas Zimmermann, Shawn Guo, Changhuang Liang

Would be good to have an overview comment to explain how bits in the
modifier are used and how everything is tied up together, e.g. what the
type and tile mode mean. Also some docs for DRM_FORMAT_MOD_VERISILICON_TYPE_NORMAL
would be nice. (If there is no other type, this can be removed, the bits
will be left as zero and can be extended later if needed.)

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 3/6] drm/fourcc: Add drm/vs tiled modifiers
@ 2023-10-25 15:38     ` Simon Ser
  0 siblings, 0 replies; 117+ messages in thread
From: Simon Ser @ 2023-10-25 15:38 UTC (permalink / raw)
  To: Keith Zhao
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Maxime Ripard, Jagan Teki, Rob Herring,
	Chris Morgan, Paul Walmsley, Bjorn Andersson, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Shawn Guo, christian.koenig

Would be good to have an overview comment to explain how bits in the
modifier are used and how everything is tied up together, e.g. what the
type and tile mode mean. Also some docs for DRM_FORMAT_MOD_VERISILICON_TYPE_NORMAL
would be nice. (If there is no other type, this can be removed, the bits
will be left as zero and can be extended later if needed.)

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 3/6] drm/fourcc: Add drm/vs tiled modifiers
  2023-10-25 15:38     ` Simon Ser
  (?)
@ 2023-10-25 15:44       ` Simon Ser
  -1 siblings, 0 replies; 117+ messages in thread
From: Simon Ser @ 2023-10-25 15:44 UTC (permalink / raw)
  To: Keith Zhao
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Maxime Ripard, Jagan Teki, Rob Herring,
	Chris Morgan, Paul Walmsley, Bjorn Andersson, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Shawn Guo, christian.koenig

Thinking about this again, it seems like you could start with just simple
enumerated modifiers like Intel does, and then only switch to more
complicated logic with macros and fields if there is an actual need in
the future.

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 3/6] drm/fourcc: Add drm/vs tiled modifiers
@ 2023-10-25 15:44       ` Simon Ser
  0 siblings, 0 replies; 117+ messages in thread
From: Simon Ser @ 2023-10-25 15:44 UTC (permalink / raw)
  To: Keith Zhao
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Maxime Ripard, Jagan Teki, Rob Herring,
	Chris Morgan, Paul Walmsley, Bjorn Andersson, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Shawn Guo, christian.koenig

Thinking about this again, it seems like you could start with just simple
enumerated modifiers like Intel does, and then only switch to more
complicated logic with macros and fields if there is an actual need in
the future.

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 3/6] drm/fourcc: Add drm/vs tiled modifiers
@ 2023-10-25 15:44       ` Simon Ser
  0 siblings, 0 replies; 117+ messages in thread
From: Simon Ser @ 2023-10-25 15:44 UTC (permalink / raw)
  To: Keith Zhao
  Cc: Emil Renner Berthing, dri-devel, Krzysztof Kozlowski,
	linux-riscv, Sumit Semwal, Shengyang Chen, linux-media,
	devicetree, Conor Dooley, Albert Ou, Maxime Ripard, Jagan Teki,
	linaro-mm-sig, Rob Herring, Chris Morgan, Paul Walmsley,
	Bjorn Andersson, linux-kernel, christian.koenig, Jack Zhu,
	Palmer Dabbelt, Thomas Zimmermann, Shawn Guo, Changhuang Liang

Thinking about this again, it seems like you could start with just simple
enumerated modifiers like Intel does, and then only switch to more
complicated logic with macros and fields if there is an actual need in
the future.

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 4/6] drm/vs: Register DRM device
  2023-10-25 10:39   ` Keith Zhao
  (?)
@ 2023-10-25 15:50     ` Dmitry Baryshkov
  -1 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-10-25 15:50 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig

On 25/10/2023 13:39, Keith Zhao wrote:
> Implement drm device registration interface
> 
> Thomas Zimmermann wrote:
> You are replacing almost all of the GEM DMA object's helper code.
> Either inherit directly from drm_gem_object drop the dependency entirely,
> or you could try to fit your code into GEM DMA as well
> 
> Eventually I found an answer and it was DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE
> After I adapt this interface, the custom gem logic interfaces can be deleted.
> It's a big shocking, clap!
> 
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>   drivers/gpu/drm/Kconfig                  |   2 +
>   drivers/gpu/drm/Makefile                 |   1 +
>   drivers/gpu/drm/verisilicon/Kconfig      |  15 ++
>   drivers/gpu/drm/verisilicon/Makefile     |   7 +
>   drivers/gpu/drm/verisilicon/vs_drv.c     | 227 +++++++++++++++++++++++
>   drivers/gpu/drm/verisilicon/vs_drv.h     |  27 +++
>   drivers/gpu/drm/verisilicon/vs_modeset.c |  36 ++++
>   drivers/gpu/drm/verisilicon/vs_modeset.h |  10 +
>   include/uapi/drm/vs_drm.h                |  50 +++++
>   9 files changed, 375 insertions(+)
>   create mode 100644 drivers/gpu/drm/verisilicon/Kconfig
>   create mode 100644 drivers/gpu/drm/verisilicon/Makefile
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.c
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.h
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.c
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.h
>   create mode 100644 include/uapi/drm/vs_drm.h
> 
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index afb3b2f5f..9ede80ef9 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -363,6 +363,8 @@ source "drivers/gpu/drm/solomon/Kconfig"
>   
>   source "drivers/gpu/drm/sprd/Kconfig"
>   
> +source "drivers/gpu/drm/verisilicon/Kconfig"
> +
>   config DRM_HYPERV
>   	tristate "DRM Support for Hyper-V synthetic video device"
>   	depends on DRM && PCI && MMU && HYPERV
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 7a09a89b4..6db3bc397 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -194,3 +194,4 @@ obj-y			+= gud/
>   obj-$(CONFIG_DRM_HYPERV) += hyperv/
>   obj-y			+= solomon/
>   obj-$(CONFIG_DRM_SPRD) += sprd/
> +obj-$(CONFIG_DRM_VERISILICON) += verisilicon/
> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
> new file mode 100644
> index 000000000..3a361f8c8
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/Kconfig
> @@ -0,0 +1,15 @@
> +# SPDX-License-Identifier: GPL-2.0
> +config DRM_VERISILICON
> +	tristate "DRM Support for VeriSilicon"
> +	depends on DRM
> +	select DRM_KMS_HELPER
> +	select DRM_GEM_DMA_HELPER
> +	select CMA
> +	select DMA_CMA
> +	help
> +	  Choose this option if you have a VeriSilicon soc chipset.
> +	  This driver provides VeriSilicon kernel mode
> +	  setting and buffer management. It does not
> +	  provide 2D or 3D acceleration.
> +
> +
> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> new file mode 100644
> index 000000000..7d3be305b
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/Makefile
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +vs_drm-objs := vs_drv.o \
> +		vs_modeset.o
> +
> +obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
> +
> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
> new file mode 100644
> index 000000000..da7698c3d
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> @@ -0,0 +1,227 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/component.h>
> +#include <linux/pm_runtime.h>
> +
> +#include <drm/drm_aperture.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_fbdev_generic.h>
> +#include <drm/drm_file.h>
> +#include <drm/drm_gem_dma_helper.h>
> +#include <drm/drm_module.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_vblank.h>
> +
> +#include "vs_drv.h"
> +#include "vs_modeset.h"
> +
> +#define DRV_NAME	"verisilicon"
> +#define DRV_DESC	"Verisilicon DRM driver"
> +#define DRV_DATE	"20230516"
> +#define DRV_MAJOR	1
> +#define DRV_MINOR	0
> +
> +static int vs_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
> +			      struct drm_mode_create_dumb *args)
> +{
> +	struct vs_drm_device *priv = to_vs_drm_private(dev);
> +
> +	unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
> +
> +	if (args->bpp % 10)
> +		args->pitch = ALIGN(pitch, priv->pitch_alignment);
> +	else
> +		/* for costum 10bit format with no bit gaps */

? I think you meant 'custom' here. If so, please define that format and 
use it explicitly here. There can be other 10bpp formats.

> +		args->pitch = pitch;
> +
> +	return drm_gem_dma_dumb_create_internal(file, dev, args);
> +}
> +
> +DEFINE_DRM_GEM_FOPS(vs_drm_fops);
> +
> +static struct drm_driver vs_drm_driver = {
> +	.driver_features	= DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
> +
> +	DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(vs_gem_dumb_create),
> +
> +	.fops			= &vs_drm_fops,
> +	.name			= DRV_NAME,
> +	.desc			= DRV_DESC,
> +	.date			= DRV_DATE,
> +	.major			= DRV_MAJOR,
> +	.minor			= DRV_MINOR,
> +};
> +
> +static int vs_drm_bind(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct vs_drm_device *priv;
> +	int ret;
> +	struct drm_device *drm_dev;
> +
> +	/* Remove existing drivers that may own the framebuffer memory. */
> +	ret = drm_aperture_remove_framebuffers(&vs_drm_driver);
> +	if (ret)
> +		return ret;
> +
> +	priv = devm_drm_dev_alloc(dev, &vs_drm_driver, struct vs_drm_device, base);
> +	if (IS_ERR(priv))
> +		return PTR_ERR(priv);
> +
> +	priv->pitch_alignment = 64;
> +
> +	ret = dma_set_coherent_mask(priv->base.dev, DMA_BIT_MASK(40));
> +	if (ret)
> +		return ret;
> +
> +	drm_dev = &priv->base;
> +	platform_set_drvdata(pdev, drm_dev);
> +
> +	vs_mode_config_init(drm_dev);
> +
> +	/* Now try and bind all our sub-components */
> +	ret = component_bind_all(dev, drm_dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
> +	if (ret)
> +		return ret;
> +
> +	drm_mode_config_reset(drm_dev);
> +
> +	drm_kms_helper_poll_init(drm_dev);
> +
> +	ret = drm_dev_register(drm_dev, 0);
> +	if (ret)
> +		return ret;
> +
> +	drm_fbdev_generic_setup(drm_dev, 32);
> +
> +	return 0;
> +}
> +
> +static void vs_drm_unbind(struct device *dev)
> +{
> +	struct drm_device *drm_dev = dev_get_drvdata(dev);
> +
> +	drm_dev_unregister(drm_dev);
> +	drm_kms_helper_poll_fini(drm_dev);
> +	component_unbind_all(drm_dev->dev, drm_dev);
> +}
> +
> +static const struct component_master_ops vs_drm_ops = {
> +	.bind = vs_drm_bind,
> +	.unbind = vs_drm_unbind,
> +};
> +
> +static struct platform_driver *drm_sub_drivers[] = {
> +
> +};
> +
> +static struct component_match *vs_drm_match_add(struct device *dev)
> +{
> +	struct component_match *match = NULL;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(drm_sub_drivers); ++i) {
> +		struct platform_driver *drv = drm_sub_drivers[i];
> +		struct device *p = NULL, *d;
> +
> +		while ((d = platform_find_device_by_driver(p, &drv->driver))) {
> +			put_device(p);
> +
> +			component_match_add(dev, &match, component_compare_dev, d);

drm_of_component_match_add ?

> +			p = d;
> +		}
> +		put_device(p);
> +	}
> +
> +	return match ? match : ERR_PTR(-ENODEV);
> +}
> +
> +static int vs_drm_platform_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct component_match *match;
> +
> +	match = vs_drm_match_add(dev);
> +	if (IS_ERR(match))
> +		return PTR_ERR(match);
> +
> +	return component_master_add_with_match(dev, &vs_drm_ops, match);
> +}
> +
> +static int vs_drm_platform_remove(struct platform_device *pdev)
> +{
> +	component_master_del(&pdev->dev, &vs_drm_ops);
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int vs_drm_suspend(struct device *dev)
> +{
> +	return drm_mode_config_helper_suspend(dev_get_drvdata(dev));
> +}
> +
> +static int vs_drm_resume(struct device *dev)
> +{
> +	drm_mode_config_helper_resume(dev_get_drvdata(dev));
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(vs_drm_pm_ops, vs_drm_suspend, vs_drm_resume);
> +
> +static const struct of_device_id vs_drm_dt_ids[] = {
> +	{ .compatible = "starfive,display-subsystem", },
> +	{ },
> +};
> +
> +MODULE_DEVICE_TABLE(of, vs_drm_dt_ids);
> +
> +static struct platform_driver vs_drm_platform_driver = {
> +	.probe = vs_drm_platform_probe,
> +	.remove = vs_drm_platform_remove,
> +
> +	.driver = {
> +		.name = DRV_NAME,
> +		.of_match_table = vs_drm_dt_ids,
> +		.pm = &vs_drm_pm_ops,
> +	},
> +};
> +
> +static int __init vs_drm_init(void)
> +{
> +	int ret;
> +
> +	ret = platform_register_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
> +	if (ret)
> +		return ret;
> +
> +	ret = drm_platform_driver_register(&vs_drm_platform_driver);
> +	if (ret)
> +		platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));

You can probably squash all the drivers into a single array.

> +
> +	return ret;
> +}
> +
> +static void __exit vs_drm_fini(void)
> +{
> +	platform_driver_unregister(&vs_drm_platform_driver);
> +	platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
> +}
> +
> +module_init(vs_drm_init);
> +module_exit(vs_drm_fini);
> +
> +MODULE_DESCRIPTION("VeriSilicon DRM Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
> new file mode 100644
> index 000000000..369ad22d6
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.h
> @@ -0,0 +1,27 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DRV_H__
> +#define __VS_DRV_H__
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <drm/drm_drv.h>
> +#include <drm/drm_gem.h>
> +#include <drm/drm_managed.h>
> +
> +/*@pitch_alignment: buffer pitch alignment required by sub-devices.*/
> +struct vs_drm_device {
> +	struct drm_device base;
> +	unsigned int pitch_alignment;
> +};
> +
> +static inline struct vs_drm_device *
> +to_vs_drm_private(const struct drm_device *dev)
> +{
> +	return container_of(dev, struct vs_drm_device, base);
> +}
> +
> +#endif /* __VS_DRV_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.c b/drivers/gpu/drm/verisilicon/vs_modeset.c
> new file mode 100644
> index 000000000..151272067
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_modeset.c
> @@ -0,0 +1,36 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/module.h>
> +
> +#include <drm/drm_damage_helper.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_gem_framebuffer_helper.h>
> +
> +#include "vs_modeset.h"
> +
> +static const struct drm_mode_config_funcs vs_mode_config_funcs = {
> +	.fb_create			 = drm_gem_fb_create,
> +	.atomic_check		 = drm_atomic_helper_check,
> +	.atomic_commit		 = drm_atomic_helper_commit,
> +};
> +
> +static struct drm_mode_config_helper_funcs vs_mode_config_helpers = {
> +	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
> +};
> +
> +void vs_mode_config_init(struct drm_device *dev)
> +{
> +	drm_mode_config_init(dev);

quoting docs:

  * FIXME: This function is deprecated and drivers should be converted 
over to
  * drmm_mode_config_init().

> +	dev->mode_config.fb_modifiers_not_supported = false;

This is the default, if I'm not mistaken

> +
> +	dev->mode_config.min_width  = 0;
> +	dev->mode_config.min_height = 0;
> +	dev->mode_config.max_width  = 4096;
> +	dev->mode_config.max_height = 4096;
> +
> +	dev->mode_config.funcs = &vs_mode_config_funcs;
> +	dev->mode_config.helper_private = &vs_mode_config_helpers;
> +}
> diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.h b/drivers/gpu/drm/verisilicon/vs_modeset.h
> new file mode 100644
> index 000000000..ebfe6d612
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_modeset.h
> @@ -0,0 +1,10 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_FB_H__
> +#define __VS_FB_H__

Please match guarding define and the header name.

> +
> +void vs_mode_config_init(struct drm_device *dev);
> +#endif /* __VS_FB_H__ */
> diff --git a/include/uapi/drm/vs_drm.h b/include/uapi/drm/vs_drm.h
> new file mode 100644
> index 000000000..96b7fc95d
> --- /dev/null
> +++ b/include/uapi/drm/vs_drm.h
> @@ -0,0 +1,50 @@
> +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> +/*
> + * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DRM_H__
> +#define __VS_DRM_H__
> +
> +#include "drm.h"

Which drm.h?

> +
> +enum drm_vs_degamma_mode {
> +	VS_DEGAMMA_DISABLE = 0,
> +	VS_DEGAMMA_BT709 = 1,
> +	VS_DEGAMMA_BT2020 = 2,
> +};
> +
> +enum drm_vs_sync_dc_mode {
> +	VS_SINGLE_DC = 0,
> +	VS_MULTI_DC_PRIMARY = 1,
> +	VS_MULTI_DC_SECONDARY = 2,
> +};
> +
> +enum drm_vs_mmu_prefetch_mode {
> +	VS_MMU_PREFETCH_DISABLE = 0,
> +	VS_MMU_PREFETCH_ENABLE = 1,
> +};
> +
> +struct drm_vs_watermark {
> +	__u32 watermark;
> +	__u8 qos_low;
> +	__u8 qos_high;
> +};
> +
> +struct drm_vs_color_mgmt {
> +	__u32 colorkey;
> +	__u32 colorkey_high;
> +	__u32 clear_value;
> +	bool  clear_enable;
> +	bool  transparency;
> +};
> +
> +struct drm_vs_roi {
> +	bool enable;
> +	__u16 roi_x;
> +	__u16 roi_y;
> +	__u16 roi_w;
> +	__u16 roi_h;
> +};

Could you please add corresponding defines in the patch that actually 
makes use of them? Otherwise it might be hard to review usage.

> +
> +#endif /* __VS_DRM_H__ */

-- 
With best wishes
Dmitry


^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 4/6] drm/vs: Register DRM device
@ 2023-10-25 15:50     ` Dmitry Baryshkov
  0 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-10-25 15:50 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig

On 25/10/2023 13:39, Keith Zhao wrote:
> Implement drm device registration interface
> 
> Thomas Zimmermann wrote:
> You are replacing almost all of the GEM DMA object's helper code.
> Either inherit directly from drm_gem_object drop the dependency entirely,
> or you could try to fit your code into GEM DMA as well
> 
> Eventually I found an answer and it was DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE
> After I adapt this interface, the custom gem logic interfaces can be deleted.
> It's a big shocking, clap!
> 
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>   drivers/gpu/drm/Kconfig                  |   2 +
>   drivers/gpu/drm/Makefile                 |   1 +
>   drivers/gpu/drm/verisilicon/Kconfig      |  15 ++
>   drivers/gpu/drm/verisilicon/Makefile     |   7 +
>   drivers/gpu/drm/verisilicon/vs_drv.c     | 227 +++++++++++++++++++++++
>   drivers/gpu/drm/verisilicon/vs_drv.h     |  27 +++
>   drivers/gpu/drm/verisilicon/vs_modeset.c |  36 ++++
>   drivers/gpu/drm/verisilicon/vs_modeset.h |  10 +
>   include/uapi/drm/vs_drm.h                |  50 +++++
>   9 files changed, 375 insertions(+)
>   create mode 100644 drivers/gpu/drm/verisilicon/Kconfig
>   create mode 100644 drivers/gpu/drm/verisilicon/Makefile
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.c
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.h
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.c
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.h
>   create mode 100644 include/uapi/drm/vs_drm.h
> 
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index afb3b2f5f..9ede80ef9 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -363,6 +363,8 @@ source "drivers/gpu/drm/solomon/Kconfig"
>   
>   source "drivers/gpu/drm/sprd/Kconfig"
>   
> +source "drivers/gpu/drm/verisilicon/Kconfig"
> +
>   config DRM_HYPERV
>   	tristate "DRM Support for Hyper-V synthetic video device"
>   	depends on DRM && PCI && MMU && HYPERV
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 7a09a89b4..6db3bc397 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -194,3 +194,4 @@ obj-y			+= gud/
>   obj-$(CONFIG_DRM_HYPERV) += hyperv/
>   obj-y			+= solomon/
>   obj-$(CONFIG_DRM_SPRD) += sprd/
> +obj-$(CONFIG_DRM_VERISILICON) += verisilicon/
> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
> new file mode 100644
> index 000000000..3a361f8c8
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/Kconfig
> @@ -0,0 +1,15 @@
> +# SPDX-License-Identifier: GPL-2.0
> +config DRM_VERISILICON
> +	tristate "DRM Support for VeriSilicon"
> +	depends on DRM
> +	select DRM_KMS_HELPER
> +	select DRM_GEM_DMA_HELPER
> +	select CMA
> +	select DMA_CMA
> +	help
> +	  Choose this option if you have a VeriSilicon soc chipset.
> +	  This driver provides VeriSilicon kernel mode
> +	  setting and buffer management. It does not
> +	  provide 2D or 3D acceleration.
> +
> +
> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> new file mode 100644
> index 000000000..7d3be305b
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/Makefile
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +vs_drm-objs := vs_drv.o \
> +		vs_modeset.o
> +
> +obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
> +
> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
> new file mode 100644
> index 000000000..da7698c3d
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> @@ -0,0 +1,227 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/component.h>
> +#include <linux/pm_runtime.h>
> +
> +#include <drm/drm_aperture.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_fbdev_generic.h>
> +#include <drm/drm_file.h>
> +#include <drm/drm_gem_dma_helper.h>
> +#include <drm/drm_module.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_vblank.h>
> +
> +#include "vs_drv.h"
> +#include "vs_modeset.h"
> +
> +#define DRV_NAME	"verisilicon"
> +#define DRV_DESC	"Verisilicon DRM driver"
> +#define DRV_DATE	"20230516"
> +#define DRV_MAJOR	1
> +#define DRV_MINOR	0
> +
> +static int vs_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
> +			      struct drm_mode_create_dumb *args)
> +{
> +	struct vs_drm_device *priv = to_vs_drm_private(dev);
> +
> +	unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
> +
> +	if (args->bpp % 10)
> +		args->pitch = ALIGN(pitch, priv->pitch_alignment);
> +	else
> +		/* for costum 10bit format with no bit gaps */

? I think you meant 'custom' here. If so, please define that format and 
use it explicitly here. There can be other 10bpp formats.

> +		args->pitch = pitch;
> +
> +	return drm_gem_dma_dumb_create_internal(file, dev, args);
> +}
> +
> +DEFINE_DRM_GEM_FOPS(vs_drm_fops);
> +
> +static struct drm_driver vs_drm_driver = {
> +	.driver_features	= DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
> +
> +	DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(vs_gem_dumb_create),
> +
> +	.fops			= &vs_drm_fops,
> +	.name			= DRV_NAME,
> +	.desc			= DRV_DESC,
> +	.date			= DRV_DATE,
> +	.major			= DRV_MAJOR,
> +	.minor			= DRV_MINOR,
> +};
> +
> +static int vs_drm_bind(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct vs_drm_device *priv;
> +	int ret;
> +	struct drm_device *drm_dev;
> +
> +	/* Remove existing drivers that may own the framebuffer memory. */
> +	ret = drm_aperture_remove_framebuffers(&vs_drm_driver);
> +	if (ret)
> +		return ret;
> +
> +	priv = devm_drm_dev_alloc(dev, &vs_drm_driver, struct vs_drm_device, base);
> +	if (IS_ERR(priv))
> +		return PTR_ERR(priv);
> +
> +	priv->pitch_alignment = 64;
> +
> +	ret = dma_set_coherent_mask(priv->base.dev, DMA_BIT_MASK(40));
> +	if (ret)
> +		return ret;
> +
> +	drm_dev = &priv->base;
> +	platform_set_drvdata(pdev, drm_dev);
> +
> +	vs_mode_config_init(drm_dev);
> +
> +	/* Now try and bind all our sub-components */
> +	ret = component_bind_all(dev, drm_dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
> +	if (ret)
> +		return ret;
> +
> +	drm_mode_config_reset(drm_dev);
> +
> +	drm_kms_helper_poll_init(drm_dev);
> +
> +	ret = drm_dev_register(drm_dev, 0);
> +	if (ret)
> +		return ret;
> +
> +	drm_fbdev_generic_setup(drm_dev, 32);
> +
> +	return 0;
> +}
> +
> +static void vs_drm_unbind(struct device *dev)
> +{
> +	struct drm_device *drm_dev = dev_get_drvdata(dev);
> +
> +	drm_dev_unregister(drm_dev);
> +	drm_kms_helper_poll_fini(drm_dev);
> +	component_unbind_all(drm_dev->dev, drm_dev);
> +}
> +
> +static const struct component_master_ops vs_drm_ops = {
> +	.bind = vs_drm_bind,
> +	.unbind = vs_drm_unbind,
> +};
> +
> +static struct platform_driver *drm_sub_drivers[] = {
> +
> +};
> +
> +static struct component_match *vs_drm_match_add(struct device *dev)
> +{
> +	struct component_match *match = NULL;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(drm_sub_drivers); ++i) {
> +		struct platform_driver *drv = drm_sub_drivers[i];
> +		struct device *p = NULL, *d;
> +
> +		while ((d = platform_find_device_by_driver(p, &drv->driver))) {
> +			put_device(p);
> +
> +			component_match_add(dev, &match, component_compare_dev, d);

drm_of_component_match_add ?

> +			p = d;
> +		}
> +		put_device(p);
> +	}
> +
> +	return match ? match : ERR_PTR(-ENODEV);
> +}
> +
> +static int vs_drm_platform_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct component_match *match;
> +
> +	match = vs_drm_match_add(dev);
> +	if (IS_ERR(match))
> +		return PTR_ERR(match);
> +
> +	return component_master_add_with_match(dev, &vs_drm_ops, match);
> +}
> +
> +static int vs_drm_platform_remove(struct platform_device *pdev)
> +{
> +	component_master_del(&pdev->dev, &vs_drm_ops);
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int vs_drm_suspend(struct device *dev)
> +{
> +	return drm_mode_config_helper_suspend(dev_get_drvdata(dev));
> +}
> +
> +static int vs_drm_resume(struct device *dev)
> +{
> +	drm_mode_config_helper_resume(dev_get_drvdata(dev));
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(vs_drm_pm_ops, vs_drm_suspend, vs_drm_resume);
> +
> +static const struct of_device_id vs_drm_dt_ids[] = {
> +	{ .compatible = "starfive,display-subsystem", },
> +	{ },
> +};
> +
> +MODULE_DEVICE_TABLE(of, vs_drm_dt_ids);
> +
> +static struct platform_driver vs_drm_platform_driver = {
> +	.probe = vs_drm_platform_probe,
> +	.remove = vs_drm_platform_remove,
> +
> +	.driver = {
> +		.name = DRV_NAME,
> +		.of_match_table = vs_drm_dt_ids,
> +		.pm = &vs_drm_pm_ops,
> +	},
> +};
> +
> +static int __init vs_drm_init(void)
> +{
> +	int ret;
> +
> +	ret = platform_register_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
> +	if (ret)
> +		return ret;
> +
> +	ret = drm_platform_driver_register(&vs_drm_platform_driver);
> +	if (ret)
> +		platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));

You can probably squash all the drivers into a single array.

> +
> +	return ret;
> +}
> +
> +static void __exit vs_drm_fini(void)
> +{
> +	platform_driver_unregister(&vs_drm_platform_driver);
> +	platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
> +}
> +
> +module_init(vs_drm_init);
> +module_exit(vs_drm_fini);
> +
> +MODULE_DESCRIPTION("VeriSilicon DRM Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
> new file mode 100644
> index 000000000..369ad22d6
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.h
> @@ -0,0 +1,27 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DRV_H__
> +#define __VS_DRV_H__
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <drm/drm_drv.h>
> +#include <drm/drm_gem.h>
> +#include <drm/drm_managed.h>
> +
> +/*@pitch_alignment: buffer pitch alignment required by sub-devices.*/
> +struct vs_drm_device {
> +	struct drm_device base;
> +	unsigned int pitch_alignment;
> +};
> +
> +static inline struct vs_drm_device *
> +to_vs_drm_private(const struct drm_device *dev)
> +{
> +	return container_of(dev, struct vs_drm_device, base);
> +}
> +
> +#endif /* __VS_DRV_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.c b/drivers/gpu/drm/verisilicon/vs_modeset.c
> new file mode 100644
> index 000000000..151272067
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_modeset.c
> @@ -0,0 +1,36 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/module.h>
> +
> +#include <drm/drm_damage_helper.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_gem_framebuffer_helper.h>
> +
> +#include "vs_modeset.h"
> +
> +static const struct drm_mode_config_funcs vs_mode_config_funcs = {
> +	.fb_create			 = drm_gem_fb_create,
> +	.atomic_check		 = drm_atomic_helper_check,
> +	.atomic_commit		 = drm_atomic_helper_commit,
> +};
> +
> +static struct drm_mode_config_helper_funcs vs_mode_config_helpers = {
> +	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
> +};
> +
> +void vs_mode_config_init(struct drm_device *dev)
> +{
> +	drm_mode_config_init(dev);

quoting docs:

  * FIXME: This function is deprecated and drivers should be converted 
over to
  * drmm_mode_config_init().

> +	dev->mode_config.fb_modifiers_not_supported = false;

This is the default, if I'm not mistaken

> +
> +	dev->mode_config.min_width  = 0;
> +	dev->mode_config.min_height = 0;
> +	dev->mode_config.max_width  = 4096;
> +	dev->mode_config.max_height = 4096;
> +
> +	dev->mode_config.funcs = &vs_mode_config_funcs;
> +	dev->mode_config.helper_private = &vs_mode_config_helpers;
> +}
> diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.h b/drivers/gpu/drm/verisilicon/vs_modeset.h
> new file mode 100644
> index 000000000..ebfe6d612
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_modeset.h
> @@ -0,0 +1,10 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_FB_H__
> +#define __VS_FB_H__

Please match guarding define and the header name.

> +
> +void vs_mode_config_init(struct drm_device *dev);
> +#endif /* __VS_FB_H__ */
> diff --git a/include/uapi/drm/vs_drm.h b/include/uapi/drm/vs_drm.h
> new file mode 100644
> index 000000000..96b7fc95d
> --- /dev/null
> +++ b/include/uapi/drm/vs_drm.h
> @@ -0,0 +1,50 @@
> +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> +/*
> + * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DRM_H__
> +#define __VS_DRM_H__
> +
> +#include "drm.h"

Which drm.h?

> +
> +enum drm_vs_degamma_mode {
> +	VS_DEGAMMA_DISABLE = 0,
> +	VS_DEGAMMA_BT709 = 1,
> +	VS_DEGAMMA_BT2020 = 2,
> +};
> +
> +enum drm_vs_sync_dc_mode {
> +	VS_SINGLE_DC = 0,
> +	VS_MULTI_DC_PRIMARY = 1,
> +	VS_MULTI_DC_SECONDARY = 2,
> +};
> +
> +enum drm_vs_mmu_prefetch_mode {
> +	VS_MMU_PREFETCH_DISABLE = 0,
> +	VS_MMU_PREFETCH_ENABLE = 1,
> +};
> +
> +struct drm_vs_watermark {
> +	__u32 watermark;
> +	__u8 qos_low;
> +	__u8 qos_high;
> +};
> +
> +struct drm_vs_color_mgmt {
> +	__u32 colorkey;
> +	__u32 colorkey_high;
> +	__u32 clear_value;
> +	bool  clear_enable;
> +	bool  transparency;
> +};
> +
> +struct drm_vs_roi {
> +	bool enable;
> +	__u16 roi_x;
> +	__u16 roi_y;
> +	__u16 roi_w;
> +	__u16 roi_h;
> +};

Could you please add corresponding defines in the patch that actually 
makes use of them? Otherwise it might be hard to review usage.

> +
> +#endif /* __VS_DRM_H__ */

-- 
With best wishes
Dmitry


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 4/6] drm/vs: Register DRM device
@ 2023-10-25 15:50     ` Dmitry Baryshkov
  0 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-10-25 15:50 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: Conor Dooley, Albert Ou, Emil Renner Berthing, christian.koenig,
	Thomas Zimmermann, Bjorn Andersson, Chris Morgan, Maxime Ripard,
	Jagan Teki, Jack Zhu, Rob Herring, Palmer Dabbelt,
	Krzysztof Kozlowski, Paul Walmsley, Shengyang Chen,
	Changhuang Liang, Shawn Guo, Sumit Semwal

On 25/10/2023 13:39, Keith Zhao wrote:
> Implement drm device registration interface
> 
> Thomas Zimmermann wrote:
> You are replacing almost all of the GEM DMA object's helper code.
> Either inherit directly from drm_gem_object drop the dependency entirely,
> or you could try to fit your code into GEM DMA as well
> 
> Eventually I found an answer and it was DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE
> After I adapt this interface, the custom gem logic interfaces can be deleted.
> It's a big shocking, clap!
> 
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>   drivers/gpu/drm/Kconfig                  |   2 +
>   drivers/gpu/drm/Makefile                 |   1 +
>   drivers/gpu/drm/verisilicon/Kconfig      |  15 ++
>   drivers/gpu/drm/verisilicon/Makefile     |   7 +
>   drivers/gpu/drm/verisilicon/vs_drv.c     | 227 +++++++++++++++++++++++
>   drivers/gpu/drm/verisilicon/vs_drv.h     |  27 +++
>   drivers/gpu/drm/verisilicon/vs_modeset.c |  36 ++++
>   drivers/gpu/drm/verisilicon/vs_modeset.h |  10 +
>   include/uapi/drm/vs_drm.h                |  50 +++++
>   9 files changed, 375 insertions(+)
>   create mode 100644 drivers/gpu/drm/verisilicon/Kconfig
>   create mode 100644 drivers/gpu/drm/verisilicon/Makefile
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.c
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.h
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.c
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.h
>   create mode 100644 include/uapi/drm/vs_drm.h
> 
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index afb3b2f5f..9ede80ef9 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -363,6 +363,8 @@ source "drivers/gpu/drm/solomon/Kconfig"
>   
>   source "drivers/gpu/drm/sprd/Kconfig"
>   
> +source "drivers/gpu/drm/verisilicon/Kconfig"
> +
>   config DRM_HYPERV
>   	tristate "DRM Support for Hyper-V synthetic video device"
>   	depends on DRM && PCI && MMU && HYPERV
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 7a09a89b4..6db3bc397 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -194,3 +194,4 @@ obj-y			+= gud/
>   obj-$(CONFIG_DRM_HYPERV) += hyperv/
>   obj-y			+= solomon/
>   obj-$(CONFIG_DRM_SPRD) += sprd/
> +obj-$(CONFIG_DRM_VERISILICON) += verisilicon/
> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
> new file mode 100644
> index 000000000..3a361f8c8
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/Kconfig
> @@ -0,0 +1,15 @@
> +# SPDX-License-Identifier: GPL-2.0
> +config DRM_VERISILICON
> +	tristate "DRM Support for VeriSilicon"
> +	depends on DRM
> +	select DRM_KMS_HELPER
> +	select DRM_GEM_DMA_HELPER
> +	select CMA
> +	select DMA_CMA
> +	help
> +	  Choose this option if you have a VeriSilicon soc chipset.
> +	  This driver provides VeriSilicon kernel mode
> +	  setting and buffer management. It does not
> +	  provide 2D or 3D acceleration.
> +
> +
> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> new file mode 100644
> index 000000000..7d3be305b
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/Makefile
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +vs_drm-objs := vs_drv.o \
> +		vs_modeset.o
> +
> +obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
> +
> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
> new file mode 100644
> index 000000000..da7698c3d
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> @@ -0,0 +1,227 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/component.h>
> +#include <linux/pm_runtime.h>
> +
> +#include <drm/drm_aperture.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_fbdev_generic.h>
> +#include <drm/drm_file.h>
> +#include <drm/drm_gem_dma_helper.h>
> +#include <drm/drm_module.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_vblank.h>
> +
> +#include "vs_drv.h"
> +#include "vs_modeset.h"
> +
> +#define DRV_NAME	"verisilicon"
> +#define DRV_DESC	"Verisilicon DRM driver"
> +#define DRV_DATE	"20230516"
> +#define DRV_MAJOR	1
> +#define DRV_MINOR	0
> +
> +static int vs_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
> +			      struct drm_mode_create_dumb *args)
> +{
> +	struct vs_drm_device *priv = to_vs_drm_private(dev);
> +
> +	unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
> +
> +	if (args->bpp % 10)
> +		args->pitch = ALIGN(pitch, priv->pitch_alignment);
> +	else
> +		/* for costum 10bit format with no bit gaps */

? I think you meant 'custom' here. If so, please define that format and 
use it explicitly here. There can be other 10bpp formats.

> +		args->pitch = pitch;
> +
> +	return drm_gem_dma_dumb_create_internal(file, dev, args);
> +}
> +
> +DEFINE_DRM_GEM_FOPS(vs_drm_fops);
> +
> +static struct drm_driver vs_drm_driver = {
> +	.driver_features	= DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
> +
> +	DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(vs_gem_dumb_create),
> +
> +	.fops			= &vs_drm_fops,
> +	.name			= DRV_NAME,
> +	.desc			= DRV_DESC,
> +	.date			= DRV_DATE,
> +	.major			= DRV_MAJOR,
> +	.minor			= DRV_MINOR,
> +};
> +
> +static int vs_drm_bind(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct vs_drm_device *priv;
> +	int ret;
> +	struct drm_device *drm_dev;
> +
> +	/* Remove existing drivers that may own the framebuffer memory. */
> +	ret = drm_aperture_remove_framebuffers(&vs_drm_driver);
> +	if (ret)
> +		return ret;
> +
> +	priv = devm_drm_dev_alloc(dev, &vs_drm_driver, struct vs_drm_device, base);
> +	if (IS_ERR(priv))
> +		return PTR_ERR(priv);
> +
> +	priv->pitch_alignment = 64;
> +
> +	ret = dma_set_coherent_mask(priv->base.dev, DMA_BIT_MASK(40));
> +	if (ret)
> +		return ret;
> +
> +	drm_dev = &priv->base;
> +	platform_set_drvdata(pdev, drm_dev);
> +
> +	vs_mode_config_init(drm_dev);
> +
> +	/* Now try and bind all our sub-components */
> +	ret = component_bind_all(dev, drm_dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
> +	if (ret)
> +		return ret;
> +
> +	drm_mode_config_reset(drm_dev);
> +
> +	drm_kms_helper_poll_init(drm_dev);
> +
> +	ret = drm_dev_register(drm_dev, 0);
> +	if (ret)
> +		return ret;
> +
> +	drm_fbdev_generic_setup(drm_dev, 32);
> +
> +	return 0;
> +}
> +
> +static void vs_drm_unbind(struct device *dev)
> +{
> +	struct drm_device *drm_dev = dev_get_drvdata(dev);
> +
> +	drm_dev_unregister(drm_dev);
> +	drm_kms_helper_poll_fini(drm_dev);
> +	component_unbind_all(drm_dev->dev, drm_dev);
> +}
> +
> +static const struct component_master_ops vs_drm_ops = {
> +	.bind = vs_drm_bind,
> +	.unbind = vs_drm_unbind,
> +};
> +
> +static struct platform_driver *drm_sub_drivers[] = {
> +
> +};
> +
> +static struct component_match *vs_drm_match_add(struct device *dev)
> +{
> +	struct component_match *match = NULL;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(drm_sub_drivers); ++i) {
> +		struct platform_driver *drv = drm_sub_drivers[i];
> +		struct device *p = NULL, *d;
> +
> +		while ((d = platform_find_device_by_driver(p, &drv->driver))) {
> +			put_device(p);
> +
> +			component_match_add(dev, &match, component_compare_dev, d);

drm_of_component_match_add ?

> +			p = d;
> +		}
> +		put_device(p);
> +	}
> +
> +	return match ? match : ERR_PTR(-ENODEV);
> +}
> +
> +static int vs_drm_platform_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct component_match *match;
> +
> +	match = vs_drm_match_add(dev);
> +	if (IS_ERR(match))
> +		return PTR_ERR(match);
> +
> +	return component_master_add_with_match(dev, &vs_drm_ops, match);
> +}
> +
> +static int vs_drm_platform_remove(struct platform_device *pdev)
> +{
> +	component_master_del(&pdev->dev, &vs_drm_ops);
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int vs_drm_suspend(struct device *dev)
> +{
> +	return drm_mode_config_helper_suspend(dev_get_drvdata(dev));
> +}
> +
> +static int vs_drm_resume(struct device *dev)
> +{
> +	drm_mode_config_helper_resume(dev_get_drvdata(dev));
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(vs_drm_pm_ops, vs_drm_suspend, vs_drm_resume);
> +
> +static const struct of_device_id vs_drm_dt_ids[] = {
> +	{ .compatible = "starfive,display-subsystem", },
> +	{ },
> +};
> +
> +MODULE_DEVICE_TABLE(of, vs_drm_dt_ids);
> +
> +static struct platform_driver vs_drm_platform_driver = {
> +	.probe = vs_drm_platform_probe,
> +	.remove = vs_drm_platform_remove,
> +
> +	.driver = {
> +		.name = DRV_NAME,
> +		.of_match_table = vs_drm_dt_ids,
> +		.pm = &vs_drm_pm_ops,
> +	},
> +};
> +
> +static int __init vs_drm_init(void)
> +{
> +	int ret;
> +
> +	ret = platform_register_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
> +	if (ret)
> +		return ret;
> +
> +	ret = drm_platform_driver_register(&vs_drm_platform_driver);
> +	if (ret)
> +		platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));

You can probably squash all the drivers into a single array.

> +
> +	return ret;
> +}
> +
> +static void __exit vs_drm_fini(void)
> +{
> +	platform_driver_unregister(&vs_drm_platform_driver);
> +	platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers));
> +}
> +
> +module_init(vs_drm_init);
> +module_exit(vs_drm_fini);
> +
> +MODULE_DESCRIPTION("VeriSilicon DRM Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
> new file mode 100644
> index 000000000..369ad22d6
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.h
> @@ -0,0 +1,27 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DRV_H__
> +#define __VS_DRV_H__
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <drm/drm_drv.h>
> +#include <drm/drm_gem.h>
> +#include <drm/drm_managed.h>
> +
> +/*@pitch_alignment: buffer pitch alignment required by sub-devices.*/
> +struct vs_drm_device {
> +	struct drm_device base;
> +	unsigned int pitch_alignment;
> +};
> +
> +static inline struct vs_drm_device *
> +to_vs_drm_private(const struct drm_device *dev)
> +{
> +	return container_of(dev, struct vs_drm_device, base);
> +}
> +
> +#endif /* __VS_DRV_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.c b/drivers/gpu/drm/verisilicon/vs_modeset.c
> new file mode 100644
> index 000000000..151272067
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_modeset.c
> @@ -0,0 +1,36 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/module.h>
> +
> +#include <drm/drm_damage_helper.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_gem_framebuffer_helper.h>
> +
> +#include "vs_modeset.h"
> +
> +static const struct drm_mode_config_funcs vs_mode_config_funcs = {
> +	.fb_create			 = drm_gem_fb_create,
> +	.atomic_check		 = drm_atomic_helper_check,
> +	.atomic_commit		 = drm_atomic_helper_commit,
> +};
> +
> +static struct drm_mode_config_helper_funcs vs_mode_config_helpers = {
> +	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
> +};
> +
> +void vs_mode_config_init(struct drm_device *dev)
> +{
> +	drm_mode_config_init(dev);

quoting docs:

  * FIXME: This function is deprecated and drivers should be converted 
over to
  * drmm_mode_config_init().

> +	dev->mode_config.fb_modifiers_not_supported = false;

This is the default, if I'm not mistaken

> +
> +	dev->mode_config.min_width  = 0;
> +	dev->mode_config.min_height = 0;
> +	dev->mode_config.max_width  = 4096;
> +	dev->mode_config.max_height = 4096;
> +
> +	dev->mode_config.funcs = &vs_mode_config_funcs;
> +	dev->mode_config.helper_private = &vs_mode_config_helpers;
> +}
> diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.h b/drivers/gpu/drm/verisilicon/vs_modeset.h
> new file mode 100644
> index 000000000..ebfe6d612
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_modeset.h
> @@ -0,0 +1,10 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_FB_H__
> +#define __VS_FB_H__

Please match guarding define and the header name.

> +
> +void vs_mode_config_init(struct drm_device *dev);
> +#endif /* __VS_FB_H__ */
> diff --git a/include/uapi/drm/vs_drm.h b/include/uapi/drm/vs_drm.h
> new file mode 100644
> index 000000000..96b7fc95d
> --- /dev/null
> +++ b/include/uapi/drm/vs_drm.h
> @@ -0,0 +1,50 @@
> +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> +/*
> + * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DRM_H__
> +#define __VS_DRM_H__
> +
> +#include "drm.h"

Which drm.h?

> +
> +enum drm_vs_degamma_mode {
> +	VS_DEGAMMA_DISABLE = 0,
> +	VS_DEGAMMA_BT709 = 1,
> +	VS_DEGAMMA_BT2020 = 2,
> +};
> +
> +enum drm_vs_sync_dc_mode {
> +	VS_SINGLE_DC = 0,
> +	VS_MULTI_DC_PRIMARY = 1,
> +	VS_MULTI_DC_SECONDARY = 2,
> +};
> +
> +enum drm_vs_mmu_prefetch_mode {
> +	VS_MMU_PREFETCH_DISABLE = 0,
> +	VS_MMU_PREFETCH_ENABLE = 1,
> +};
> +
> +struct drm_vs_watermark {
> +	__u32 watermark;
> +	__u8 qos_low;
> +	__u8 qos_high;
> +};
> +
> +struct drm_vs_color_mgmt {
> +	__u32 colorkey;
> +	__u32 colorkey_high;
> +	__u32 clear_value;
> +	bool  clear_enable;
> +	bool  transparency;
> +};
> +
> +struct drm_vs_roi {
> +	bool enable;
> +	__u16 roi_x;
> +	__u16 roi_y;
> +	__u16 roi_w;
> +	__u16 roi_h;
> +};

Could you please add corresponding defines in the patch that actually 
makes use of them? Otherwise it might be hard to review usage.

> +
> +#endif /* __VS_DRM_H__ */

-- 
With best wishes
Dmitry


^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
  2023-10-25 10:39   ` Keith Zhao
  (?)
@ 2023-10-25 19:28     ` Dmitry Baryshkov
  -1 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-10-25 19:28 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig

On 25/10/2023 13:39, Keith Zhao wrote:
> add 2 crtcs and 8 planes in vs-drm
> 
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
>   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
>   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
>   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
>   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
>   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
>   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
>   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
>   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
>   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
>   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
>   11 files changed, 4494 insertions(+), 2 deletions(-)
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
> 
> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> index 7d3be305b..1d48016ca 100644
> --- a/drivers/gpu/drm/verisilicon/Makefile
> +++ b/drivers/gpu/drm/verisilicon/Makefile
> @@ -1,7 +1,11 @@
>   # SPDX-License-Identifier: GPL-2.0
>   
> -vs_drm-objs := vs_drv.o \
> -		vs_modeset.o
> +vs_drm-objs := vs_dc_hw.o \
> +		vs_dc.o \
> +		vs_crtc.o \
> +		vs_drv.o \
> +		vs_modeset.o \
> +		vs_plane.o
>   
>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>   
> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
> new file mode 100644
> index 000000000..8a658ea77
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
> @@ -0,0 +1,257 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + *
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/debugfs.h>
> +#include <linux/media-bus-format.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_gem_atomic_helper.h>
> +#include <drm/drm_vblank.h>
> +#include <drm/vs_drm.h>
> +
> +#include "vs_crtc.h"
> +#include "vs_dc.h"
> +#include "vs_drv.h"
> +
> +static void vs_crtc_reset(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc_state *state;
> +
> +	if (crtc->state) {
> +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
> +
> +		state = to_vs_crtc_state(crtc->state);
> +		kfree(state);
> +		crtc->state = NULL;
> +	}

You can call your crtc_destroy_state function directly here.

> +
> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return;
> +
> +	__drm_atomic_helper_crtc_reset(crtc, &state->base);
> +}
> +
> +static struct drm_crtc_state *
> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc_state *ori_state;

It might be a matter of taste, but it is usually old_state.

> +	struct vs_crtc_state *state;
> +
> +	if (!crtc->state)
> +		return NULL;
> +
> +	ori_state = to_vs_crtc_state(crtc->state);
> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return NULL;
> +
> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
> +
> +	state->output_fmt = ori_state->output_fmt;
> +	state->encoder_type = ori_state->encoder_type;
> +	state->bpp = ori_state->bpp;
> +	state->underflow = ori_state->underflow;

Can you use kmemdup instead?

> +
> +	return &state->base;
> +}
> +
> +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
> +					 struct drm_crtc_state *state)
> +{
> +	__drm_atomic_helper_crtc_destroy_state(state);
> +	kfree(to_vs_crtc_state(state));
> +}
> +
> +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_enable_vblank(dc, true);
> +
> +	return 0;
> +}
> +
> +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_enable_vblank(dc, false);
> +}
> +
> +static const struct drm_crtc_funcs vs_crtc_funcs = {
> +	.set_config		= drm_atomic_helper_set_config,
> +	.page_flip		= drm_atomic_helper_page_flip,

destroy is required, see drm_mode_config_cleanup()

> +	.reset			= vs_crtc_reset,
> +	.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
> +	.atomic_destroy_state	= vs_crtc_atomic_destroy_state,

please consider adding atomic_print_state to output driver-specific bits.

> +	.enable_vblank		= vs_crtc_enable_vblank,
> +	.disable_vblank		= vs_crtc_disable_vblank,
> +};
> +
> +static u8 cal_pixel_bits(u32 bus_format)

This looks like a generic helper code, which can go to a common place.

> +{
> +	u8 bpp;
> +
> +	switch (bus_format) {
> +	case MEDIA_BUS_FMT_RGB565_1X16:
> +	case MEDIA_BUS_FMT_UYVY8_1X16:
> +		bpp = 16;
> +		break;
> +	case MEDIA_BUS_FMT_RGB666_1X18:
> +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> +		bpp = 18;
> +		break;
> +	case MEDIA_BUS_FMT_UYVY10_1X20:
> +		bpp = 20;
> +		break;
> +	case MEDIA_BUS_FMT_BGR888_1X24:
> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +	case MEDIA_BUS_FMT_YUV8_1X24:
> +		bpp = 24;
> +		break;
> +	case MEDIA_BUS_FMT_RGB101010_1X30:
> +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> +	case MEDIA_BUS_FMT_YUV10_1X30:
> +		bpp = 30;
> +		break;
> +	default:
> +		bpp = 24;
> +		break;
> +	}
> +
> +	return bpp;
> +}
> +
> +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
> +				  struct drm_atomic_state *state)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
> +
> +	vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
> +
> +	vs_dc_enable(dc, crtc);
> +	drm_crtc_vblank_on(crtc);
> +}
> +
> +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
> +				   struct drm_atomic_state *state)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	drm_crtc_vblank_off(crtc);
> +
> +	vs_dc_disable(dc, crtc);
> +
> +	if (crtc->state->event && !crtc->state->active) {
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +
> +		crtc->state->event = NULL;

I think even should be cleared within the lock.

> +	}
> +}
> +
> +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
> +				 struct drm_atomic_state *state)
> +{
> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
> +									  crtc);
> +
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct device *dev = vs_crtc->dev;
> +	struct drm_property_blob *blob = crtc->state->gamma_lut;
> +	struct drm_color_lut *lut;
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	if (crtc_state->color_mgmt_changed) {
> +		if (blob && blob->length) {
> +			lut = blob->data;
> +			vs_dc_set_gamma(dc, crtc, lut,
> +					blob->length / sizeof(*lut));
> +			vs_dc_enable_gamma(dc, crtc, true);
> +		} else {
> +			vs_dc_enable_gamma(dc, crtc, false);
> +		}
> +	}
> +}
> +
> +static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
> +				 struct drm_atomic_state *state)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct drm_pending_vblank_event *event = crtc->state->event;
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_commit(dc);
> +
> +	if (event) {
> +		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
> +
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		drm_crtc_arm_vblank_event(crtc, event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +		crtc->state->event = NULL;

I think even should be cleared within the lock.

> +	}
> +}
> +
> +static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
> +	.atomic_check = drm_crtc_helper_atomic_check,
> +	.atomic_enable	= vs_crtc_atomic_enable,
> +	.atomic_disable = vs_crtc_atomic_disable,
> +	.atomic_begin	= vs_crtc_atomic_begin,
> +	.atomic_flush	= vs_crtc_atomic_flush,
> +};
> +
> +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
> +	{ VS_SINGLE_DC,				"single dc mode" },
> +	{ VS_MULTI_DC_PRIMARY,		"primary dc for multi dc mode" },
> +	{ VS_MULTI_DC_SECONDARY,	"secondary dc for multi dc mode" },
> +};
> +
> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
> +			       struct vs_dc_info *info)
> +{
> +	struct vs_crtc *crtc;
> +	int ret;
> +
> +	if (!info)
> +		return NULL;
> +
> +	crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
> +	if (!crtc)
> +		return NULL;
> +
> +	ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
> +					 NULL, NULL, &vs_crtc_funcs,
> +					 info->name ? info->name : NULL);

It might be better to add drmm_crtc_init() helper.

> +	if (ret)
> +		return NULL;
> +
> +	drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
> +
> +	if (info->gamma_size) {
> +		ret = drm_mode_crtc_set_gamma_size(&crtc->base,
> +						   info->gamma_size);
> +		if (ret)
> +			return NULL;
> +
> +		drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
> +					   info->gamma_size);
> +	}
> +
> +	crtc->max_bpc = info->max_bpc;
> +	crtc->color_formats = info->color_formats;
> +	return crtc;
> +}
> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h
> new file mode 100644
> index 000000000..526dd63e5
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.h
> @@ -0,0 +1,43 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_CRTC_H__
> +#define __VS_CRTC_H__
> +
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "vs_type.h"
> +
> +struct vs_crtc_state {
> +	struct drm_crtc_state base;
> +
> +	u32 output_fmt;
> +	u8 encoder_type;
> +	u8 bpp;
> +	bool underflow;
> +};
> +
> +struct vs_crtc {
> +	struct drm_crtc base;
> +	struct device *dev;
> +	unsigned int max_bpc;
> +	unsigned int color_formats;
> +};
> +
> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
> +			       struct vs_dc_info *info);
> +
> +static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc)
> +{
> +	return container_of(crtc, struct vs_crtc, base);
> +}
> +
> +static inline struct vs_crtc_state *
> +to_vs_crtc_state(struct drm_crtc_state *state)
> +{
> +	return container_of(state, struct vs_crtc_state, base);
> +}
> +#endif /* __VS_CRTC_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
> new file mode 100644
> index 000000000..b5ab92d98
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc.c
> @@ -0,0 +1,1002 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/component.h>
> +#include <linux/clk.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/reset.h>
> +
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_blend.h>
> +#include <drm/drm_framebuffer.h>
> +#include <drm/drm_vblank.h>
> +#include <drm/vs_drm.h>
> +
> +#include "vs_dc_hw.h"
> +#include "vs_dc.h"
> +#include "vs_drv.h"
> +
> +static const char * const vout_clocks[] = {
> +	"noc_bus",
> +	"channel0",
> +	"channel1",
> +	"dc_core",
> +	"axi_core",
> +	"ahb",
> +	"hdmi_tx",
> +	"dc_parent",
> +
> +};
> +
> +static const char * const vout_resets[] = {
> +	"axi",
> +	"ahb",
> +	"core",
> +};
> +
> +static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb)
> +{
> +	u8 f = FORMAT_A8R8G8B8;
> +
> +	switch (format) {
> +	case DRM_FORMAT_XRGB4444:
> +	case DRM_FORMAT_RGBX4444:
> +	case DRM_FORMAT_XBGR4444:
> +	case DRM_FORMAT_BGRX4444:
> +		f = FORMAT_X4R4G4B4;
> +		break;
> +	case DRM_FORMAT_ARGB4444:
> +	case DRM_FORMAT_RGBA4444:
> +	case DRM_FORMAT_ABGR4444:
> +	case DRM_FORMAT_BGRA4444:
> +		f = FORMAT_A4R4G4B4;
> +		break;
> +	case DRM_FORMAT_XRGB1555:
> +	case DRM_FORMAT_RGBX5551:
> +	case DRM_FORMAT_XBGR1555:
> +	case DRM_FORMAT_BGRX5551:
> +		f = FORMAT_X1R5G5B5;
> +		break;
> +	case DRM_FORMAT_ARGB1555:
> +	case DRM_FORMAT_RGBA5551:
> +	case DRM_FORMAT_ABGR1555:
> +	case DRM_FORMAT_BGRA5551:
> +		f = FORMAT_A1R5G5B5;
> +		break;
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		f = FORMAT_R5G6B5;
> +		break;
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_RGBX8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_BGRX8888:
> +		f = FORMAT_X8R8G8B8;
> +		break;
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_RGBA8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_BGRA8888:
> +		f = FORMAT_A8R8G8B8;
> +		break;
> +	case DRM_FORMAT_YUYV:
> +	case DRM_FORMAT_YVYU:
> +		f = FORMAT_YUY2;
> +		break;
> +	case DRM_FORMAT_UYVY:
> +	case DRM_FORMAT_VYUY:
> +		f = FORMAT_UYVY;
> +		break;
> +	case DRM_FORMAT_YUV420:
> +	case DRM_FORMAT_YVU420:
> +		f = FORMAT_YV12;
> +		break;
> +	case DRM_FORMAT_NV21:
> +		f = FORMAT_NV12;
> +		break;
> +	case DRM_FORMAT_NV16:
> +	case DRM_FORMAT_NV61:
> +		f = FORMAT_NV16;
> +		break;
> +	case DRM_FORMAT_P010:
> +		f = FORMAT_P010;
> +		break;
> +	case DRM_FORMAT_ARGB2101010:
> +	case DRM_FORMAT_RGBA1010102:
> +	case DRM_FORMAT_ABGR2101010:
> +	case DRM_FORMAT_BGRA1010102:
> +		f = FORMAT_A2R10G10B10;
> +		break;
> +	case DRM_FORMAT_NV12:
> +		f = FORMAT_NV12;
> +		break;
> +	case DRM_FORMAT_YUV444:
> +		f = FORMAT_YUV444;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	fb->format = f;
> +}
> +
> +static inline void update_swizzle(u32 format, struct dc_hw_fb *fb)
> +{
> +	fb->swizzle = SWIZZLE_ARGB;
> +	fb->uv_swizzle = 0;
> +
> +	switch (format) {
> +	case DRM_FORMAT_RGBX4444:
> +	case DRM_FORMAT_RGBA4444:
> +	case DRM_FORMAT_RGBX5551:
> +	case DRM_FORMAT_RGBA5551:
> +	case DRM_FORMAT_RGBX8888:
> +	case DRM_FORMAT_RGBA8888:
> +	case DRM_FORMAT_RGBA1010102:
> +		fb->swizzle = SWIZZLE_RGBA;
> +		break;
> +	case DRM_FORMAT_XBGR4444:
> +	case DRM_FORMAT_ABGR4444:
> +	case DRM_FORMAT_XBGR1555:
> +	case DRM_FORMAT_ABGR1555:
> +	case DRM_FORMAT_BGR565:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_ABGR2101010:
> +		fb->swizzle = SWIZZLE_ABGR;
> +		break;
> +	case DRM_FORMAT_BGRX4444:
> +	case DRM_FORMAT_BGRA4444:
> +	case DRM_FORMAT_BGRX5551:
> +	case DRM_FORMAT_BGRA5551:
> +	case DRM_FORMAT_BGRX8888:
> +	case DRM_FORMAT_BGRA8888:
> +	case DRM_FORMAT_BGRA1010102:
> +		fb->swizzle = SWIZZLE_BGRA;
> +		break;
> +	case DRM_FORMAT_YVYU:
> +	case DRM_FORMAT_VYUY:
> +	case DRM_FORMAT_NV21:
> +	case DRM_FORMAT_NV61:
> +		fb->uv_swizzle = 1;
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +static inline void update_watermark(struct drm_property_blob *watermark,
> +				    struct dc_hw_fb *fb)
> +{
> +	struct drm_vs_watermark *data;
> +
> +	fb->water_mark = 0;
> +
> +	if (watermark) {
> +		data = watermark->data;
> +		fb->water_mark = data->watermark & 0xFFFFF;
> +	}
> +}
> +
> +static inline u8 to_vs_rotation(unsigned int rotation)
> +{
> +	u8 rot;
> +
> +	switch (rotation & DRM_MODE_REFLECT_MASK) {
> +	case DRM_MODE_REFLECT_X:
> +		rot = FLIP_X;
> +		return rot;
> +	case DRM_MODE_REFLECT_Y:
> +		rot = FLIP_Y;
> +		return rot;
> +	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
> +		rot = FLIP_XY;
> +		return rot;
> +	default:
> +		break;
> +	}
> +
> +	switch (rotation & DRM_MODE_ROTATE_MASK) {
> +	case DRM_MODE_ROTATE_0:
> +		rot = ROT_0;
> +		break;
> +	case DRM_MODE_ROTATE_90:
> +		rot = ROT_90;
> +		break;
> +	case DRM_MODE_ROTATE_180:
> +		rot = ROT_180;
> +		break;
> +	case DRM_MODE_ROTATE_270:
> +		rot = ROT_270;
> +		break;
> +	default:
> +		rot = ROT_0;
> +		break;
> +	}
> +
> +	return rot;
> +}
> +
> +static inline u8 to_vs_yuv_color_space(u32 color_space)
> +{
> +	u8 cs;
> +
> +	switch (color_space) {
> +	case DRM_COLOR_YCBCR_BT601:
> +		cs = COLOR_SPACE_601;
> +		break;
> +	case DRM_COLOR_YCBCR_BT709:
> +		cs = COLOR_SPACE_709;
> +		break;
> +	case DRM_COLOR_YCBCR_BT2020:
> +		cs = COLOR_SPACE_2020;
> +		break;
> +	default:
> +		cs = COLOR_SPACE_601;
> +		break;
> +	}
> +
> +	return cs;
> +}
> +
> +static inline u8 to_vs_tile_mode(u64 modifier)
> +{
> +	if (modifier == DRM_FORMAT_MOD_VIVANTE_TILED)
> +		return DC_TILE_MODE4X4;
> +
> +	return (u8)(modifier & DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK);
> +}
> +
> +static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +	u8 panel_num = dc->hw.info->panel_num;
> +	u32 index = drm_crtc_index(crtc);
> +	int i;
> +
> +	for (i = 0; i < panel_num; i++) {
> +		if (index == dc->crtc[i]->base.index)
> +			return i;
> +	}
> +
> +	return 0;
> +}
> +
> +static void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
> +					  unsigned int alignment)
> +{
> +	struct vs_drm_device *priv = to_vs_drm_private(drm_dev);
> +
> +	if (alignment > priv->pitch_alignment)
> +		priv->pitch_alignment = alignment;
> +}
> +
> +static int plda_clk_rst_init(struct device *dev)
> +{
> +	int ret = 0;
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	ret = clk_bulk_prepare_enable(dc->nclks, dc->clk_vout);
> +	if (ret) {
> +		dev_err(dev, "failed to enable clocks\n");
> +		return ret;
> +	}
> +
> +	ret = reset_control_bulk_deassert(dc->nrsts, dc->rst_vout);
> +	return ret;
> +}
> +
> +static void plda_clk_rst_deinit(struct device *dev)
> +{
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	reset_control_bulk_assert(dc->nrsts, dc->rst_vout);
> +	clk_bulk_disable_unprepare(dc->nclks, dc->clk_vout);
> +}
> +
> +static void dc_deinit(struct device *dev)
> +{
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	dc_hw_enable_interrupt(&dc->hw, 0);
> +	dc_hw_deinit(&dc->hw);
> +	plda_clk_rst_deinit(dev);
> +}
> +
> +static int dc_init(struct device *dev)
> +{
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +	int ret;
> +
> +	dc->first_frame = true;
> +
> +	ret = plda_clk_rst_init(dev);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to init dc clk reset: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = dc_hw_init(&dc->hw);
> +	if (ret) {
> +		dev_err(dev, "failed to init DC HW\n");
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +	struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	struct dc_hw_display display;
> +
> +	display.bus_format = crtc_state->output_fmt;
> +	display.h_active = mode->hdisplay;
> +	display.h_total = mode->htotal;
> +	display.h_sync_start = mode->hsync_start;
> +	display.h_sync_end = mode->hsync_end;
> +	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> +		display.h_sync_polarity = true;
> +	else
> +		display.h_sync_polarity = false;
> +
> +	display.v_active = mode->vdisplay;
> +	display.v_total = mode->vtotal;
> +	display.v_sync_start = mode->vsync_start;
> +	display.v_sync_end = mode->vsync_end;
> +
> +	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> +		display.v_sync_polarity = true;
> +	else
> +		display.v_sync_polarity = false;
> +
> +	display.id = to_vs_display_id(dc, crtc);
> +
> +	display.enable = true;
> +
> +	if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) {
> +		dc_hw_set_out(&dc->hw, OUT_DPI, display.id);
> +		clk_set_rate(dc->clk_vout[CLK_VOUT_SOC_PIX].clk, mode->clock * 1000);
> +		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX1].clk,
> +			       dc->clk_vout[CLK_VOUT_SOC_PIX].clk);
> +	} else {
> +		dc_hw_set_out(&dc->hw, OUT_DP, display.id);
> +		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX0].clk,
> +			       dc->clk_vout[CLK_VOUT_HDMI_PIX].clk);
> +	}
> +
> +	dc_hw_setup_display(&dc->hw, &display);
> +}
> +
> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +	struct dc_hw_display display;
> +
> +	display.id = to_vs_display_id(dc, crtc);
> +	display.enable = false;
> +
> +	dc_hw_setup_display(&dc->hw, &display);
> +}
> +
> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> +		     struct drm_color_lut *lut, unsigned int size)
> +{
> +	u16 i, r, g, b;
> +	u8 bits, id;
> +
> +	if (size != dc->hw.info->gamma_size) {
> +		drm_err(crtc->dev, "gamma size does not match!\n");
> +		return;
> +	}
> +
> +	id = to_vs_display_id(dc, crtc);
> +
> +	bits = dc->hw.info->gamma_bits;
> +	for (i = 0; i < size; i++) {
> +		r = drm_color_lut_extract(lut[i].red, bits);
> +		g = drm_color_lut_extract(lut[i].green, bits);
> +		b = drm_color_lut_extract(lut[i].blue, bits);
> +		dc_hw_update_gamma(&dc->hw, id, i, r, g, b);
> +	}
> +}
> +
> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> +			bool enable)
> +{
> +	u8 id;
> +
> +	id = to_vs_display_id(dc, crtc);
> +	dc_hw_enable_gamma(&dc->hw, id, enable);
> +}
> +
> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable)
> +{
> +	dc_hw_enable_interrupt(&dc->hw, enable);
> +}
> +
> +static u32 calc_factor(u32 src, u32 dest)
> +{
> +	u32 factor = 1 << 16;
> +
> +	if (src > 1 && dest > 1)
> +		factor = ((src - 1) << 16) / (dest - 1);
> +
> +	return factor;
> +}
> +
> +static void update_scale(struct drm_plane_state *state, struct dc_hw_roi *roi,
> +			 struct dc_hw_scale *scale)
> +{
> +	int dst_w = drm_rect_width(&state->dst);
> +	int dst_h = drm_rect_height(&state->dst);
> +	int src_w, src_h, temp;
> +
> +	scale->enable = false;
> +
> +	if (roi->enable) {
> +		src_w = roi->width;
> +		src_h = roi->height;
> +	} else {
> +		src_w = drm_rect_width(&state->src) >> 16;
> +		src_h = drm_rect_height(&state->src) >> 16;
> +	}
> +
> +	if (drm_rotation_90_or_270(state->rotation)) {
> +		temp = src_w;
> +		src_w = src_h;
> +		src_h = temp;
> +	}
> +
> +	if (src_w != dst_w) {
> +		scale->scale_factor_x = calc_factor(src_w, dst_w);
> +		scale->enable = true;
> +	} else {
> +		scale->scale_factor_x = 1 << 16;
> +	}
> +	if (src_h != dst_h) {
> +		scale->scale_factor_y = calc_factor(src_h, dst_h);
> +		scale->enable = true;
> +	} else {
> +		scale->scale_factor_y = 1 << 16;
> +	}
> +}
> +
> +static void update_fb(struct vs_plane *plane, u8 display_id,
> +		      struct dc_hw_fb *fb, struct drm_plane_state *state)
> +{
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_framebuffer *drm_fb = state->fb;
> +	struct drm_rect *src = &state->src;
> +
> +	fb->display_id = display_id;
> +	fb->y_address = plane_state->dma_addr[0];
> +	fb->y_stride = drm_fb->pitches[0];
> +	if (drm_fb->format->format == DRM_FORMAT_YVU420) {
> +		fb->u_address = plane_state->dma_addr[2];
> +		fb->v_address = plane_state->dma_addr[1];
> +		fb->u_stride = drm_fb->pitches[2];
> +		fb->v_stride = drm_fb->pitches[1];
> +	} else {
> +		fb->u_address = plane_state->dma_addr[1];
> +		fb->v_address = plane_state->dma_addr[2];
> +		fb->u_stride = drm_fb->pitches[1];
> +		fb->v_stride = drm_fb->pitches[2];
> +	}
> +	fb->width = drm_rect_width(src) >> 16;
> +	fb->height = drm_rect_height(src) >> 16;
> +	fb->tile_mode = to_vs_tile_mode(drm_fb->modifier);
> +	fb->rotation = to_vs_rotation(state->rotation);
> +	fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding);
> +	fb->zpos = state->zpos;
> +	fb->enable = state->visible;
> +	update_format(drm_fb->format->format, drm_fb->modifier, fb);
> +	update_swizzle(drm_fb->format->format, fb);
> +	update_watermark(plane_state->watermark, fb);
> +	plane_state->status.tile_mode = fb->tile_mode;
> +}
> +
> +static void update_degamma(struct vs_dc *dc, struct vs_plane *plane,
> +			   struct vs_plane_state *plane_state)
> +{
> +	dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
> +	plane_state->degamma_changed = false;
> +}
> +
> +static void update_roi(struct vs_dc *dc, u8 id,
> +		       struct vs_plane_state *plane_state,
> +		       struct dc_hw_roi *roi,
> +		       struct drm_plane_state *state)
> +{
> +	struct drm_vs_roi *data;
> +	struct drm_rect *src = &state->src;
> +	u16 src_w = drm_rect_width(src) >> 16;
> +	u16 src_h = drm_rect_height(src) >> 16;
> +
> +	if (plane_state->roi) {
> +		data = plane_state->roi->data;
> +
> +		if (data->enable) {
> +			roi->x = data->roi_x;
> +			roi->y = data->roi_y;
> +			roi->width = (data->roi_x + data->roi_w > src_w) ?
> +						 (src_w - data->roi_x) : data->roi_w;
> +			roi->height = (data->roi_y + data->roi_h > src_h) ?
> +						  (src_h - data->roi_y) : data->roi_h;
> +			roi->enable = true;
> +		} else {
> +			roi->enable = false;
> +		}
> +
> +		dc_hw_update_roi(&dc->hw, id, roi);
> +	} else {
> +		roi->enable = false;
> +	}
> +}
> +
> +static void update_color_mgmt(struct vs_dc *dc, u8 id,
> +			      struct dc_hw_fb *fb,
> +			      struct vs_plane_state *plane_state)
> +{
> +	struct drm_vs_color_mgmt *data;
> +	struct dc_hw_colorkey colorkey;
> +
> +	if (plane_state->color_mgmt) {
> +		data = plane_state->color_mgmt->data;
> +
> +		fb->clear_enable = data->clear_enable;
> +		fb->clear_value = data->clear_value;
> +
> +		if (data->colorkey > data->colorkey_high)
> +			data->colorkey = data->colorkey_high;
> +
> +		colorkey.colorkey = data->colorkey;
> +		colorkey.colorkey_high = data->colorkey_high;
> +		colorkey.transparency = (data->transparency) ?
> +				DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE;
> +		dc_hw_update_colorkey(&dc->hw, id, &colorkey);
> +	}
> +}
> +
> +static void update_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			 struct drm_plane *drm_plane,
> +			 struct drm_atomic_state *drm_state)
> +{
> +	struct dc_hw_fb fb = {0};
> +	struct dc_hw_scale scale;
> +	struct dc_hw_position pos;
> +	struct dc_hw_blend blend;
> +	struct dc_hw_roi roi;
> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> +									   drm_plane);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_rect *dest = &state->dst;
> +	bool dec_enable = false;
> +	u8 display_id = 0;
> +
> +	display_id = to_vs_display_id(dc, state->crtc);
> +	update_fb(plane, display_id, &fb, state);
> +	fb.dec_enable = dec_enable;
> +
> +	update_roi(dc, plane->id, plane_state, &roi, state);
> +
> +	update_scale(state, &roi, &scale);
> +
> +	if (plane_state->degamma_changed)
> +		update_degamma(dc, plane, plane_state);
> +
> +	pos.start_x = dest->x1;
> +	pos.start_y = dest->y1;
> +	pos.end_x = dest->x2;
> +	pos.end_y = dest->y2;
> +
> +	blend.alpha = (u8)(state->alpha >> 8);
> +	blend.blend_mode = (u8)(state->pixel_blend_mode);
> +
> +	update_color_mgmt(dc, plane->id, &fb, plane_state);
> +
> +	dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
> +}
> +
> +static void update_qos(struct vs_dc *dc, struct vs_plane *plane,
> +		       struct drm_plane *drm_plane,
> +		       struct drm_atomic_state *drm_state)
> +{
> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> +									   drm_plane);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_vs_watermark *data;
> +	struct dc_hw_qos qos;
> +
> +	if (plane_state->watermark) {
> +		data = plane_state->watermark->data;
> +
> +		if (data->qos_high) {
> +			if (data->qos_low > data->qos_high)
> +				data->qos_low = data->qos_high;
> +
> +			qos.low_value = data->qos_low & 0x0F;
> +			qos.high_value = data->qos_high & 0x0F;
> +			dc_hw_update_qos(&dc->hw, &qos);
> +		}
> +	}
> +}
> +
> +static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor)
> +{
> +	u8 size_type;
> +
> +	switch (state->crtc_w) {
> +	case 32:
> +		size_type = CURSOR_SIZE_32X32;
> +		break;
> +	case 64:
> +		size_type = CURSOR_SIZE_64X64;
> +		break;
> +	default:
> +		size_type = CURSOR_SIZE_32X32;
> +		break;
> +	}
> +
> +	cursor->size = size_type;
> +}
> +
> +static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +				struct drm_plane *drm_plane,
> +				struct drm_atomic_state *drm_state)
> +{
> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> +								       drm_plane);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_framebuffer *drm_fb = state->fb;
> +	struct dc_hw_cursor cursor;
> +
> +	cursor.address = plane_state->dma_addr[0];
> +	cursor.x = state->crtc_x;
> +	cursor.y = state->crtc_y;
> +	cursor.hot_x = drm_fb->hot_x;
> +	cursor.hot_y = drm_fb->hot_y;
> +	cursor.display_id = to_vs_display_id(dc, state->crtc);
> +	update_cursor_size(state, &cursor);
> +	cursor.enable = true;
> +
> +	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
> +}
> +
> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			struct drm_plane *drm_plane,
> +			struct drm_atomic_state *drm_state)
> +{
> +	update_plane(dc, plane, drm_plane, drm_state);
> +	update_qos(dc, plane, drm_plane, drm_state);
> +}
> +
> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			       struct drm_plane *drm_plane,
> +			       struct drm_atomic_state *drm_state)
> +{
> +	update_cursor_plane(dc, plane, drm_plane, drm_state);
> +}
> +
> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			 struct drm_plane_state *old_state)
> +{
> +	struct dc_hw_fb fb = {0};
> +
> +	fb.enable = false;
> +	dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL, NULL, NULL);
> +}
> +
> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +				struct drm_plane_state *old_state)
> +{
> +	struct dc_hw_cursor cursor = {0};
> +
> +	cursor.enable = false;
> +	cursor.display_id = to_vs_display_id(dc, old_state->crtc);
> +	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
> +}
> +
> +static bool vs_dc_mod_supported(const struct vs_plane_info *plane_info,
> +				u64 modifier)
> +{
> +	const u64 *mods;
> +
> +	if (!plane_info->modifiers)
> +		return false;
> +
> +	for (mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID; mods++) {
> +		if (*mods == modifier)
> +			return true;
> +	}
> +
> +	return false;
> +}
> +
> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
> +		      struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> +										 plane);
> +
> +	struct drm_framebuffer *fb = new_plane_state->fb;
> +	const struct vs_plane_info *plane_info;
> +	struct drm_crtc *crtc = new_plane_state->crtc;
> +	struct drm_crtc_state *crtc_state;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +
> +	plane_info = &dc->hw.info->planes[vs_plane->id];
> +
> +	if (fb->width < plane_info->min_width ||
> +	    fb->width > plane_info->max_width ||
> +	    fb->height < plane_info->min_height ||
> +	    fb->height > plane_info->max_height)
> +		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n",
> +			     vs_plane->id);
> +
> +	if (!vs_dc_mod_supported(plane_info, fb->modifier)) {
> +		drm_err(plane->dev, "unsupported modifier on plane%d.\n", vs_plane->id);
> +		return -EINVAL;
> +	}
> +
> +	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
> +	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
> +						  plane_info->min_scale,
> +						  plane_info->max_scale,
> +						  true, true);
> +}
> +
> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
> +			     struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> +									   plane);
> +	struct drm_framebuffer *fb = new_plane_state->fb;
> +	const struct vs_plane_info *plane_info;
> +	struct drm_crtc *crtc = new_plane_state->crtc;
> +	struct drm_crtc_state *crtc_state;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +
> +	plane_info = &dc->hw.info->planes[vs_plane->id];
> +
> +	if (fb->width < plane_info->min_width ||
> +	    fb->width > plane_info->max_width ||
> +	    fb->height < plane_info->min_height ||
> +	    fb->height > plane_info->max_height)
> +		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n", vs_plane->id);
> +
> +	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
> +	if (IS_ERR(crtc_state))
> +		return -EINVAL;
> +
> +	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
> +						plane_info->min_scale,
> +						plane_info->max_scale,
> +						true, true);
> +}
> +
> +static void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
> +{
> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
> +
> +	drm_crtc_handle_vblank(crtc);
> +
> +	vs_crtc_state->underflow = underflow;
> +}
> +
> +static irqreturn_t dc_isr(int irq, void *data)
> +{
> +	struct vs_dc *dc = data;
> +	struct vs_dc_info *dc_info = dc->hw.info;
> +	u32 i, ret;
> +
> +	ret = dc_hw_get_interrupt(&dc->hw);
> +
> +	for (i = 0; i < dc_info->panel_num; i++)
> +		vs_crtc_handle_vblank(&dc->crtc[i]->base, dc_hw_check_underflow(&dc->hw));
> +
> +	return IRQ_HANDLED;
> +}
> +
> +void vs_dc_commit(struct vs_dc *dc)
> +{
> +	dc_hw_enable_shadow_register(&dc->hw, false);
> +
> +	dc_hw_commit(&dc->hw);
> +
> +	if (dc->first_frame)
> +		dc->first_frame = false;
> +
> +	dc_hw_enable_shadow_register(&dc->hw, true);
> +}
> +
> +static int dc_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct drm_device *drm_dev = data;
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +	struct device_node *port;
> +	struct vs_crtc *crtc;
> +	struct vs_dc_info *dc_info;
> +	struct vs_plane *plane;
> +	struct vs_plane_info *plane_info;
> +	int i, ret;
> +	u32 ctrc_mask = 0;
> +
> +	if (!drm_dev || !dc) {
> +		dev_err(dev, "devices are not created.\n");
> +		return -ENODEV;
> +	}
> +
> +	ret = dc_init(dev);
> +	if (ret < 0) {
> +		drm_err(drm_dev, "Failed to initialize DC hardware.\n");
> +		return ret;
> +	}
> +
> +	port = of_get_child_by_name(dev->of_node, "port");
> +	if (!port) {
> +		drm_err(drm_dev, "no port node found\n");
> +		return -ENODEV;
> +	}
> +	of_node_put(port);
> +
> +	dc_info = dc->hw.info;
> +
> +	for (i = 0; i < dc_info->panel_num; i++) {
> +		crtc = vs_crtc_create(drm_dev, dc_info);
> +		if (!crtc) {
> +			drm_err(drm_dev, "Failed to create CRTC.\n");
> +			ret = -ENOMEM;
> +			return ret;
> +		}
> +
> +		crtc->base.port = port;
> +		crtc->dev = dev;
> +		dc->crtc[i] = crtc;
> +		ctrc_mask |= drm_crtc_mask(&crtc->base);
> +	}
> +
> +	for (i = 0; i < dc_info->plane_num; i++) {
> +		plane_info = (struct vs_plane_info *)&dc_info->planes[i];
> +
> +		if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
> +						drm_crtc_mask(&dc->crtc[0]->base));
> +		} else if (!strcmp(plane_info->name, "Primary_1") ||
> +				   !strcmp(plane_info->name, "Cursor_1")) {
> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
> +						drm_crtc_mask(&dc->crtc[1]->base));

I'd say, it's not typical to use name to distinguish objects. Maybe you 
can add indices to the info structure and drop the names instead?


> +		} else {
> +			plane = vs_plane_create(drm_dev, plane_info,
> +						dc_info->layer_num, ctrc_mask);
> +		}
> +
> +		if (IS_ERR(plane)) {
> +			dev_err(dev, "failed to construct plane\n");
> +			return PTR_ERR(plane);
> +		}
> +
> +		plane->id = i;
> +		dc->planes[i].id = plane_info->id;
> +
> +		if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
> +			if (!strcmp(plane_info->name, "Primary"))
> +				dc->crtc[0]->base.primary = &plane->base;
> +			else
> +				dc->crtc[1]->base.primary = &plane->base;

This definitely urges for an index in the plane info / plane_state.

> +			drm_dev->mode_config.min_width = plane_info->min_width;
> +			drm_dev->mode_config.min_height =
> +							plane_info->min_height;
> +			drm_dev->mode_config.max_width = plane_info->max_width;
> +			drm_dev->mode_config.max_height =
> +							plane_info->max_height;
> +		}
> +
> +		if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
> +			if (!strcmp(plane_info->name, "Cursor"))
> +				dc->crtc[0]->base.cursor = &plane->base;
> +			else
> +				dc->crtc[1]->base.cursor = &plane->base;
> +			drm_dev->mode_config.cursor_width =
> +							plane_info->max_width;
> +			drm_dev->mode_config.cursor_height =
> +							plane_info->max_height;
> +		}
> +	}
> +
> +	vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
> +	return 0;
> +}
> +
> +static void dc_unbind(struct device *dev, struct device *master, void *data)
> +{
> +	dc_deinit(dev);
> +}
> +
> +const struct component_ops dc_component_ops = {
> +	.bind = dc_bind,
> +	.unbind = dc_unbind,
> +};
> +
> +static const struct of_device_id dc_driver_dt_match[] = {
> +	{ .compatible = "starfive,jh7110-dc8200", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
> +
> +static int dc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct vs_dc *dc;
> +	int irq, ret, i;
> +
> +	dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
> +	if (!dc)
> +		return -ENOMEM;
> +
> +	dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(dc->hw.hi_base))
> +		return PTR_ERR(dc->hw.hi_base);
> +
> +	dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
> +	if (IS_ERR(dc->hw.reg_base))
> +		return PTR_ERR(dc->hw.reg_base);
> +
> +	dc->nclks = ARRAY_SIZE(dc->clk_vout);
> +	for (i = 0; i < dc->nclks; ++i)
> +		dc->clk_vout[i].id = vout_clocks[i];
> +	ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
> +	if (ret) {
> +		dev_err(dev, "Failed to get clk controls\n");
> +		return ret;
> +	}
> +
> +	dc->nrsts = ARRAY_SIZE(dc->rst_vout);
> +	for (i = 0; i < dc->nrsts; ++i)
> +		dc->rst_vout[i].id = vout_resets[i];
> +	ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
> +						 dc->rst_vout);
> +	if (ret) {
> +		dev_err(dev, "Failed to get reset controls\n");
> +		return ret;
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +
> +	ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);

Are you ready to handle the IRQ at this point?

> +	if (ret < 0) {
> +		dev_err(dev, "Failed to install irq:%u.\n", irq);
> +		return ret;
> +	}
> +
> +	dev_set_drvdata(dev, dc);
> +
> +	return component_add(dev, &dc_component_ops);
> +}
> +
> +static int dc_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +
> +	component_del(dev, &dc_component_ops);
> +
> +	dev_set_drvdata(dev, NULL);
> +
> +	return 0;
> +}
> +
> +struct platform_driver dc_platform_driver = {
> +	.probe = dc_probe,
> +	.remove = dc_remove,
> +	.driver = {
> +		.name = "vs-dc",
> +		.of_match_table = of_match_ptr(dc_driver_dt_match),
> +	},
> +};
> +
> +MODULE_AUTHOR("StarFive Corporation");
> +MODULE_DESCRIPTION("VeriSilicon DC Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
> new file mode 100644
> index 000000000..8e96fd32c
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc.h
> @@ -0,0 +1,80 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DC_H__
> +#define __VS_DC_H__
> +
> +#include <linux/clk.h>
> +#include <linux/mm_types.h>
> +#include <linux/reset.h>
> +
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_modes.h>
> +
> +#include "vs_crtc.h"
> +#include "vs_dc_hw.h"
> +#include "vs_plane.h"
> +
> +struct vs_dc_plane {
> +	enum dc_hw_plane_id id;
> +};
> +
> +enum vout_clk {
> +	CLK_VOUT_NOC_DISP = 0,
> +	CLK_VOUT_PIX0,
> +	CLK_VOUT_PIX1,
> +	CLK_VOUT_CORE,
> +	CLK_VOUT_AXI,
> +	CLK_VOUT_AHB,
> +	CLK_VOUT_HDMI_PIX,
> +	CLK_VOUT_SOC_PIX,
> +	CLK_VOUT_NUM
> +};
> +
> +enum rst_vout {
> +	RST_VOUT_AXI = 0,
> +	RST_VOUT_AHB,
> +	RST_VOUT_CORE,
> +	RST_VOUT_NUM
> +};
> +
> +struct vs_dc {
> +	struct vs_crtc		*crtc[DC_DISPLAY_NUM];
> +	struct dc_hw		hw;
> +	bool			first_frame;
> +
> +	struct vs_dc_plane	planes[PLANE_NUM];
> +	struct clk_bulk_data	clk_vout[CLK_VOUT_NUM];
> +	int			nclks;
> +	struct reset_control_bulk_data rst_vout[RST_VOUT_NUM];
> +	int			nrsts;
> +};
> +
> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc);
> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc);
> +
> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> +		     struct drm_color_lut *lut, unsigned int size);
> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc, bool enable);
> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable);
> +void vs_dc_commit(struct vs_dc *dc);
> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			struct drm_plane *drm_plane,
> +			struct drm_atomic_state *drm_state);
> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			 struct drm_plane_state *old_state);
> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
> +		      struct drm_atomic_state *state);
> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			       struct drm_plane *drm_plane,
> +			       struct drm_atomic_state *drm_state);
> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +				struct drm_plane_state *old_state);
> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
> +			     struct drm_atomic_state *state);
> +
> +extern struct platform_driver dc_platform_driver;
> +
> +#endif /* __VS_DC_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
> new file mode 100644
> index 000000000..e5acb7eb4
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
> @@ -0,0 +1,1959 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/bits.h>
> +#include <linux/io.h>
> +#include <linux/media-bus-format.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_blend.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/vs_drm.h>
> +
> +#include "vs_dc_hw.h"
> +#include "vs_type.h"
> +
> +static const u32 horkernel[] = {
> +	0x00000000, 0x20000000, 0x00002000, 0x00000000,
> +	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
> +	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
> +	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
> +	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
> +	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
> +	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
> +	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
> +	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
> +	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
> +	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
> +	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
> +	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
> +	0x00000000, 0x00000000, 0x00000000, 0x01470000,
> +	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
> +	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
> +	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
> +	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
> +	0x00000000, 0x00000000, 0x00004000, 0x00000000,
> +	0x00000000, 0x00000000, 0x20002000, 0x00000000,
> +	0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
> +	0x00000000, 0x00000000, 0x00000000, 0x27e1181f,
> +	0x00000000, 0x00000000, 0x00000000, 0x14680000,
> +	0x00002b98, 0x00000000, 0x00000000, 0x00000000,
> +	0x2f1010f0, 0x00000000, 0x00000000, 0x00000000,
> +	0x0dc70000, 0x00003239, 0x00000000, 0x00000000,
> +	0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
> +	0x00000000, 0x087f0000, 0x00003781, 0x00000000,
> +	0x00000000, 0x00000000, 0x399a0666, 0x00000000,
> +	0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
> +	0x00000000, 0x00000000, 0x00000000, 0x3cc4033c,
> +	0x00000000, 0x00000000, 0x00000000, 0x021f0000,
> +};
> +
> +#define H_COEF_SIZE (sizeof(horkernel) / sizeof(u32))
> +
> +static const u32 verkernel[] = {
> +	0x00000000, 0x20000000, 0x00002000, 0x00000000,
> +	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
> +	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
> +	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
> +	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
> +	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
> +	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
> +	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
> +	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
> +	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
> +	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
> +	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
> +	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
> +	0x00000000, 0x00000000, 0x00000000, 0x01470000,
> +	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
> +	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
> +	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
> +	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
> +	0x00000000, 0x00000000, 0x00004000, 0x00000000,
> +	0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab,
> +	0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
> +	0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +};
> +
> +#define V_COEF_SIZE (sizeof(verkernel) / sizeof(u32))
> +
> +/*
> + * RGB 709->2020 conversion parameters
> + */
> +static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = {
> +	10279,	5395,	709,
> +	1132,	15065,	187,
> +	269,	1442,	14674
> +};
> +
> +/*
> + * YUV601 to RGB conversion parameters
> + * YUV2RGB[0]  - [8] : C0 - C8;
> + * YUV2RGB[9]  - [11]: D0 - D2;
> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> + */
> +static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> +	1196,	0,			1640,	1196,
> +	-404,	-836,		1196,	2076,
> +	0,		-916224,	558336,	-1202944,
> +	64,		 940,		64,		960
> +};
> +
> +/*
> + * YUV709 to RGB conversion parameters
> + * YUV2RGB[0]  - [8] : C0 - C8;
> + * YUV2RGB[9]  - [11]: D0 - D2;
> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> + */
> +static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> +	1196,		0,		1844,	1196,
> +	-220,		-548,	1196,	2172,
> +	0,			-1020672, 316672,  -1188608,
> +	64,			940,		64,		960
> +};
> +
> +/*
> + * YUV2020 to RGB conversion parameters
> + * YUV2RGB[0]  - [8] : C0 - C8;
> + * YUV2RGB[9]  - [11]: D0 - D2;
> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> + */
> +static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> +	1196, 0, 1724, 1196,
> +	-192, -668, 1196, 2200,
> +	0, -959232, 363776, -1202944,
> +	64, 940, 64, 960
> +};
> +
> +/*
> + * RGB to YUV2020 conversion parameters
> + * RGB2YUV[0] - [8] : C0 - C8;
> + * RGB2YUV[9] - [11]: D0 - D2;
> + */
> +static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = {
> +	230,	594,	52,
> +	-125,  -323,	448,
> +	448,   -412,   -36,
> +	64,		512,	512
> +};
> +
> +/*
> + * Degamma table for 709 color space data.
> + */
> +static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005,
> +	0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024,
> +	0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064,
> +	0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce,
> +	0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166,
> +	0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230,
> +	0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331,
> +	0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c,
> +	0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
> +	0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d,
> +	0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999,
> +	0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb,
> +	0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65,
> +	0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a,
> +	0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b,
> +	0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc,
> +	0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e,
> +	0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
> +	0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e,
> +	0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf,
> +	0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea,
> +	0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f,
> +	0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930,
> +	0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f,
> +	0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee,
> +	0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde,
> +	0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
> +	0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9,
> +	0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406,
> +	0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a,
> +	0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576,
> +	0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd,
> +	0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
> +};
> +
> +/*
> + * Degamma table for 2020 color space data.
> + */
> +static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> +	0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
> +	0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
> +	0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006,
> +	0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a,
> +	0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f,
> +	0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
> +	0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024,
> +	0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035,
> +	0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d,
> +	0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d,
> +	0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098,
> +	0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3,
> +	0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123,
> +	0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d,
> +	0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
> +	0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6,
> +	0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1,
> +	0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d,
> +	0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6,
> +	0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920,
> +	0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a,
> +	0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033,
> +	0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593,
> +	0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
> +	0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656,
> +	0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332,
> +	0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480,
> +	0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1,
> +	0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99,
> +	0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
> +};
> +
> +/* one is for primary plane and the other is for all overlay planes */
> +static const struct dc_hw_plane_reg dc_plane_reg[] = {
> +	{
> +	.y_address		= DC_FRAMEBUFFER_ADDRESS,
> +	.u_address		= DC_FRAMEBUFFER_U_ADDRESS,
> +	.v_address		= DC_FRAMEBUFFER_V_ADDRESS,
> +	.y_stride		= DC_FRAMEBUFFER_STRIDE,
> +	.u_stride		= DC_FRAMEBUFFER_U_STRIDE,
> +	.v_stride		= DC_FRAMEBUFFER_V_STRIDE,
> +	.size			= DC_FRAMEBUFFER_SIZE,
> +	.top_left		= DC_FRAMEBUFFER_TOP_LEFT,
> +	.bottom_right	= DC_FRAMEBUFFER_BOTTOM_RIGHT,
> +	.scale_factor_x			= DC_FRAMEBUFFER_SCALE_FACTOR_X,
> +	.scale_factor_y			= DC_FRAMEBUFFER_SCALE_FACTOR_Y,
> +	.h_filter_coef_index	= DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
> +	.h_filter_coef_data		= DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
> +	.v_filter_coef_index	= DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
> +	.v_filter_coef_data		= DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
> +	.init_offset			= DC_FRAMEBUFFER_INIT_OFFSET,
> +	.color_key				= DC_FRAMEBUFFER_COLOR_KEY,
> +	.color_key_high			= DC_FRAMEBUFFER_COLOR_KEY_HIGH,
> +	.clear_value			= DC_FRAMEBUFFER_CLEAR_VALUE,
> +	.color_table_index		= DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
> +	.color_table_data		= DC_FRAMEBUFFER_COLOR_TABLE_DATA,
> +	.scale_config			= DC_FRAMEBUFFER_SCALE_CONFIG,
> +	.water_mark				= DC_FRAMEBUFFER_WATER_MARK,
> +	.degamma_index			= DC_FRAMEBUFFER_DEGAMMA_INDEX,
> +	.degamma_data			= DC_FRAMEBUFFER_DEGAMMA_DATA,
> +	.degamma_ex_data		= DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
> +	.src_global_color		= DC_FRAMEBUFFER_SRC_GLOBAL_COLOR,
> +	.dst_global_color		= DC_FRAMEBUFFER_DST_GLOBAL_COLOR,
> +	.blend_config			= DC_FRAMEBUFFER_BLEND_CONFIG,
> +	.roi_origin				= DC_FRAMEBUFFER_ROI_ORIGIN,
> +	.roi_size				= DC_FRAMEBUFFER_ROI_SIZE,
> +	.yuv_to_rgb_coef0			= DC_FRAMEBUFFER_YUVTORGB_COEF0,
> +	.yuv_to_rgb_coef1			= DC_FRAMEBUFFER_YUVTORGB_COEF1,
> +	.yuv_to_rgb_coef2			= DC_FRAMEBUFFER_YUVTORGB_COEF2,
> +	.yuv_to_rgb_coef3			= DC_FRAMEBUFFER_YUVTORGB_COEF3,
> +	.yuv_to_rgb_coef4			= DC_FRAMEBUFFER_YUVTORGB_COEF4,
> +	.yuv_to_rgb_coefd0			= DC_FRAMEBUFFER_YUVTORGB_COEFD0,
> +	.yuv_to_rgb_coefd1			= DC_FRAMEBUFFER_YUVTORGB_COEFD1,
> +	.yuv_to_rgb_coefd2			= DC_FRAMEBUFFER_YUVTORGB_COEFD2,
> +	.y_clamp_bound				= DC_FRAMEBUFFER_Y_CLAMP_BOUND,
> +	.uv_clamp_bound				= DC_FRAMEBUFFER_UV_CLAMP_BOUND,
> +	.rgb_to_rgb_coef0			= DC_FRAMEBUFFER_RGBTORGB_COEF0,
> +	.rgb_to_rgb_coef1			= DC_FRAMEBUFFER_RGBTORGB_COEF1,
> +	.rgb_to_rgb_coef2			= DC_FRAMEBUFFER_RGBTORGB_COEF2,
> +	.rgb_to_rgb_coef3			= DC_FRAMEBUFFER_RGBTORGB_COEF3,
> +	.rgb_to_rgb_coef4			= DC_FRAMEBUFFER_RGBTORGB_COEF4,
> +	},
> +	{
> +	.y_address		= DC_OVERLAY_ADDRESS,
> +	.u_address		= DC_OVERLAY_U_ADDRESS,
> +	.v_address		= DC_OVERLAY_V_ADDRESS,
> +	.y_stride		= DC_OVERLAY_STRIDE,
> +	.u_stride		= DC_OVERLAY_U_STRIDE,
> +	.v_stride		= DC_OVERLAY_V_STRIDE,
> +	.size			= DC_OVERLAY_SIZE,
> +	.top_left		= DC_OVERLAY_TOP_LEFT,
> +	.bottom_right	= DC_OVERLAY_BOTTOM_RIGHT,
> +	.scale_factor_x	= DC_OVERLAY_SCALE_FACTOR_X,
> +	.scale_factor_y	= DC_OVERLAY_SCALE_FACTOR_Y,
> +	.h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
> +	.h_filter_coef_data  = DC_OVERLAY_H_FILTER_COEF_DATA,
> +	.v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
> +	.v_filter_coef_data  = DC_OVERLAY_V_FILTER_COEF_DATA,
> +	.init_offset		 = DC_OVERLAY_INIT_OFFSET,
> +	.color_key			 = DC_OVERLAY_COLOR_KEY,
> +	.color_key_high			= DC_OVERLAY_COLOR_KEY_HIGH,
> +	.clear_value		 = DC_OVERLAY_CLEAR_VALUE,
> +	.color_table_index	 = DC_OVERLAY_COLOR_TABLE_INDEX,
> +	.color_table_data	 = DC_OVERLAY_COLOR_TABLE_DATA,
> +	.scale_config		 = DC_OVERLAY_SCALE_CONFIG,
> +	.water_mark				= DC_OVERLAY_WATER_MARK,
> +	.degamma_index		 = DC_OVERLAY_DEGAMMA_INDEX,
> +	.degamma_data		 = DC_OVERLAY_DEGAMMA_DATA,
> +	.degamma_ex_data	 = DC_OVERLAY_DEGAMMA_EX_DATA,
> +	.src_global_color	 = DC_OVERLAY_SRC_GLOBAL_COLOR,
> +	.dst_global_color	 = DC_OVERLAY_DST_GLOBAL_COLOR,
> +	.blend_config		 = DC_OVERLAY_BLEND_CONFIG,
> +	.roi_origin				= DC_OVERLAY_ROI_ORIGIN,
> +	.roi_size				= DC_OVERLAY_ROI_SIZE,
> +	.yuv_to_rgb_coef0		 = DC_OVERLAY_YUVTORGB_COEF0,
> +	.yuv_to_rgb_coef1		 = DC_OVERLAY_YUVTORGB_COEF1,
> +	.yuv_to_rgb_coef2		 = DC_OVERLAY_YUVTORGB_COEF2,
> +	.yuv_to_rgb_coef3		 = DC_OVERLAY_YUVTORGB_COEF3,
> +	.yuv_to_rgb_coef4			= DC_OVERLAY_YUVTORGB_COEF4,
> +	.yuv_to_rgb_coefd0			= DC_OVERLAY_YUVTORGB_COEFD0,
> +	.yuv_to_rgb_coefd1			= DC_OVERLAY_YUVTORGB_COEFD1,
> +	.yuv_to_rgb_coefd2			= DC_OVERLAY_YUVTORGB_COEFD2,
> +	.y_clamp_bound		 = DC_OVERLAY_Y_CLAMP_BOUND,
> +	.uv_clamp_bound		 = DC_OVERLAY_UV_CLAMP_BOUND,
> +	.rgb_to_rgb_coef0		 = DC_OVERLAY_RGBTORGB_COEF0,
> +	.rgb_to_rgb_coef1		 = DC_OVERLAY_RGBTORGB_COEF1,
> +	.rgb_to_rgb_coef2		 = DC_OVERLAY_RGBTORGB_COEF2,
> +	.rgb_to_rgb_coef3		 = DC_OVERLAY_RGBTORGB_COEF3,
> +	.rgb_to_rgb_coef4		 = DC_OVERLAY_RGBTORGB_COEF4,
> +	},
> +};
> +
> +static const u32 primary_overlay_format0[] = {
> +	DRM_FORMAT_XRGB4444,
> +	DRM_FORMAT_XBGR4444,
> +	DRM_FORMAT_RGBX4444,
> +	DRM_FORMAT_BGRX4444,
> +	DRM_FORMAT_ARGB4444,
> +	DRM_FORMAT_ABGR4444,
> +	DRM_FORMAT_RGBA4444,
> +	DRM_FORMAT_BGRA4444,
> +	DRM_FORMAT_XRGB1555,
> +	DRM_FORMAT_XBGR1555,
> +	DRM_FORMAT_RGBX5551,
> +	DRM_FORMAT_BGRX5551,
> +	DRM_FORMAT_ARGB1555,
> +	DRM_FORMAT_ABGR1555,
> +	DRM_FORMAT_RGBA5551,
> +	DRM_FORMAT_BGRA5551,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_RGBX8888,
> +	DRM_FORMAT_BGRX8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGBA8888,
> +	DRM_FORMAT_BGRA8888,
> +	DRM_FORMAT_ARGB2101010,
> +	DRM_FORMAT_ABGR2101010,
> +	DRM_FORMAT_RGBA1010102,
> +	DRM_FORMAT_BGRA1010102,
> +	DRM_FORMAT_YUYV,
> +	DRM_FORMAT_YVYU,
> +	DRM_FORMAT_UYVY,
> +	DRM_FORMAT_VYUY,
> +	DRM_FORMAT_YVU420,
> +	DRM_FORMAT_YUV420,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV21,
> +	DRM_FORMAT_NV16,
> +	DRM_FORMAT_NV61,
> +	DRM_FORMAT_P010,
> +};
> +
> +static const u32 primary_overlay_format1[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_RGBX8888,
> +	DRM_FORMAT_BGRX8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGBA8888,
> +	DRM_FORMAT_BGRA8888,
> +	DRM_FORMAT_ARGB2101010,
> +	DRM_FORMAT_ABGR2101010,
> +	DRM_FORMAT_RGBA1010102,
> +	DRM_FORMAT_BGRA1010102,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV21,
> +	DRM_FORMAT_YUV444,
> +};
> +
> +static const u32 cursor_formats[] = {
> +	DRM_FORMAT_ARGB8888
> +};
> +
> +static const u64 format_modifier0[] = {
> +	DRM_FORMAT_MOD_LINEAR,
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X8),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X4),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8),
> +	DRM_FORMAT_MOD_INVALID
> +};
> +
> +static const u64 secondary_format_modifiers[] = {
> +	DRM_FORMAT_MOD_LINEAR,
> +	DRM_FORMAT_MOD_INVALID
> +};
> +
> +#define FRAC_16_16(mult, div)	 (((mult) << 16) / (div))
> +
> +static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = {
> +	{
> +		/* DC_REV_0 */
> +		{
> +		.name			= "Primary",

I'd say that the indentation is strange here.
It might be more scalable to split a single array into per-generation 
data structures and reference them directly from the info struct.

> +		.id				= PRIMARY_PLANE_0,
> +		.type			= DRM_PLANE_TYPE_PRIMARY,
> +		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
> +		.formats		= primary_overlay_format0,
> +		.num_modifiers	= ARRAY_SIZE(format_modifier0),
> +		.modifiers		= format_modifier0,
> +		.min_width		= 0,
> +		.min_height		= 0,
> +		.max_width		= 4096,
> +		.max_height		= 4096,
> +		.rotation		= DRM_MODE_ROTATE_0 |
> +							DRM_MODE_ROTATE_90 |
> +							DRM_MODE_ROTATE_180 |
> +							DRM_MODE_ROTATE_270 |
> +							DRM_MODE_REFLECT_X |
> +							DRM_MODE_REFLECT_Y,
> +		.blend_mode		= BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +							BIT(DRM_MODE_BLEND_PREMULTI) |
> +							BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 0,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name			= "Overlay",
> +		.id				= OVERLAY_PLANE_0,
> +		.type			= DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
> +		.formats		= primary_overlay_format0,
> +		.num_modifiers	= ARRAY_SIZE(format_modifier0),
> +		.modifiers		= format_modifier0,
> +		.min_width		= 0,
> +		.min_height		= 0,
> +		.max_width		= 4096,
> +		.max_height		= 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 1,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name			= "Overlay_1",
> +		.id				= OVERLAY_PLANE_1,
> +		.type			= DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
> +		.formats		= primary_overlay_format0,
> +		.num_modifiers	= ARRAY_SIZE(secondary_format_modifiers),
> +		.modifiers		= secondary_format_modifiers,
> +		.min_width		= 0,
> +		.min_height		= 0,
> +		.max_width		= 4096,
> +		.max_height		= 4096,
> +		.rotation		= 0,
> +		.blend_mode		= BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +							BIT(DRM_MODE_BLEND_PREMULTI) |
> +							BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 2,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Primary_1",
> +		.id				= PRIMARY_PLANE_1,
> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
> +		.formats	 = primary_overlay_format0,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 3,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay_2",
> +		.id				= OVERLAY_PLANE_2,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
> +		.formats	 = primary_overlay_format0,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 4,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay_3",
> +		.id			= OVERLAY_PLANE_3,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
> +		.formats	 = primary_overlay_format0,
> +		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
> +		.modifiers	 = secondary_format_modifiers,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = 0,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 5,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Cursor",
> +		.id				= CURSOR_PLANE_0,
> +		.type		 = DRM_PLANE_TYPE_CURSOR,
> +		.num_formats = ARRAY_SIZE(cursor_formats),
> +		.formats	 = cursor_formats,
> +		.num_modifiers = 0,
> +		.modifiers	 = NULL,
> +		.min_width	 = 32,
> +		.min_height  = 32,
> +		.max_width	 = 64,
> +		.max_height  = 64,
> +		.rotation	 = 0,
> +		.degamma_size = 0,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 255,
> +		.watermark	 = false,
> +		.color_mgmt  = false,
> +		.roi		 = false,
> +		},
> +		{
> +		.name		 = "Cursor_1",
> +		.id				= CURSOR_PLANE_1,
> +		.type		 = DRM_PLANE_TYPE_CURSOR,
> +		.num_formats = ARRAY_SIZE(cursor_formats),
> +		.formats	 = cursor_formats,
> +		.num_modifiers = 0,
> +		.modifiers	 = NULL,
> +		.min_width	 = 32,
> +		.min_height  = 32,
> +		.max_width	 = 64,
> +		.max_height  = 64,
> +		.rotation	 = 0,
> +		.degamma_size = 0,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 255,
> +		.watermark	 = false,
> +		.color_mgmt  = false,
> +		.roi		 = false,
> +		},
> +	},
> +	{
> +		/* DC_REV_1 */
> +		{
> +		.name		 = "Primary",
> +		.id				= PRIMARY_PLANE_0,
> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
> +		.formats	 = primary_overlay_format0,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 0,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay",
> +		.id				= OVERLAY_PLANE_0,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
> +		.formats	 = primary_overlay_format0,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 1,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Primary_1",
> +		.id				= PRIMARY_PLANE_1,
> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
> +		.formats	 = primary_overlay_format0,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 2,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay_2",
> +		.id				= OVERLAY_PLANE_2,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
> +		.formats	 = primary_overlay_format0,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 3,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Cursor",
> +		.id				= CURSOR_PLANE_0,
> +		.type		 = DRM_PLANE_TYPE_CURSOR,
> +		.num_formats = ARRAY_SIZE(cursor_formats),
> +		.formats	 = cursor_formats,
> +		.num_modifiers = 0,
> +		.modifiers	 = NULL,
> +		.min_width	 = 32,
> +		.min_height  = 32,
> +		.max_width	 = 64,
> +		.max_height  = 64,
> +		.rotation	 = 0,
> +		.degamma_size = 0,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 255,
> +		.watermark	 = false,
> +		.color_mgmt  = false,
> +		.roi		 = false,
> +		},
> +		{
> +		.name		 = "Cursor_1",
> +		.id				= CURSOR_PLANE_1,
> +		.type		 = DRM_PLANE_TYPE_CURSOR,
> +		.num_formats = ARRAY_SIZE(cursor_formats),
> +		.formats	 = cursor_formats,
> +		.num_modifiers = 0,
> +		.modifiers	 = NULL,
> +		.min_width	 = 32,
> +		.min_height  = 32,
> +		.max_width	 = 64,
> +		.max_height  = 64,
> +		.rotation	 = 0,
> +		.degamma_size = 0,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 255,
> +		.watermark	 = false,
> +		.color_mgmt  = false,
> +		.roi		 = false,
> +		},
> +	},
> +	{
> +		/* DC_REV_2 */
> +		{
> +		.name		 = "Primary",
> +		.id				= PRIMARY_PLANE_0,
> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
> +		.formats	 = primary_overlay_format1,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 0,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay",
> +		.id				= OVERLAY_PLANE_0,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
> +		.formats	 = primary_overlay_format1,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 1,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay_1",
> +		.id				= OVERLAY_PLANE_1,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
> +		.formats	 = primary_overlay_format1,
> +		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
> +		.modifiers	 = secondary_format_modifiers,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = 0,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 2,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Primary_1",
> +		.id				= PRIMARY_PLANE_1,
> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
> +		.formats	 = primary_overlay_format1,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 3,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay_2",
> +		.id				= OVERLAY_PLANE_2,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
> +		.formats	 = primary_overlay_format1,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 4,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay_3",
> +		.id				= OVERLAY_PLANE_3,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
> +		.formats	 = primary_overlay_format1,
> +		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
> +		.modifiers	 = secondary_format_modifiers,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = 0,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 5,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Cursor",
> +		.id				= CURSOR_PLANE_0,
> +		.type		 = DRM_PLANE_TYPE_CURSOR,
> +		.num_formats = ARRAY_SIZE(cursor_formats),
> +		.formats	 = cursor_formats,
> +		.num_modifiers = 0,
> +		.modifiers	 = NULL,
> +		.min_width	 = 32,
> +		.min_height  = 32,
> +		.max_width	 = 64,
> +		.max_height  = 64,
> +		.rotation	 = 0,
> +		.degamma_size = 0,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 255,
> +		.watermark	 = false,
> +		.color_mgmt  = false,
> +		.roi		 = false,
> +		},
> +		{
> +		.name		 = "Cursor_1",
> +		.id				= CURSOR_PLANE_1,
> +		.type		 = DRM_PLANE_TYPE_CURSOR,
> +		.num_formats = ARRAY_SIZE(cursor_formats),
> +		.formats	 = cursor_formats,
> +		.num_modifiers = 0,
> +		.modifiers	 = NULL,
> +		.min_width	 = 32,
> +		.min_height  = 32,
> +		.max_width	 = 64,
> +		.max_height  = 64,
> +		.rotation	 = 0,
> +		.degamma_size = 0,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 255,
> +		.watermark	 = false,
> +		.color_mgmt  = false,
> +		.roi		 = false,
> +		},
> +	},
> +};
> +
> +static const struct vs_dc_info dc_info[] = {

Again, please split into per-generation instances.

> +	{
> +		/* DC_REV_0 */
> +		.name			= "DC8200",
> +		.panel_num		= 2,
> +		.plane_num		= 8,
> +		.planes			= dc_hw_planes[DC_REV_0],
> +		.layer_num		= 6,
> +		.max_bpc		= 10,
> +		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
> +						  DRM_COLOR_FORMAT_YCBCR444 |
> +						  DRM_COLOR_FORMAT_YCBCR422 |
> +						  DRM_COLOR_FORMAT_YCBCR420,
> +		.gamma_size		= GAMMA_EX_SIZE,
> +		.gamma_bits		= 12,
> +		.pitch_alignment	= 128,
> +		.pipe_sync		= false,
> +		.background		= true,
> +		.panel_sync		= true,
> +		.cap_dec		= true,
> +	},
> +	{
> +		/* DC_REV_1 */
> +		.name			= "DC8200",
> +		.panel_num		= 2,
> +		.plane_num		= 6,
> +		.planes			= dc_hw_planes[DC_REV_1],
> +		.layer_num		= 4,
> +		.max_bpc		= 10,
> +		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
> +						  DRM_COLOR_FORMAT_YCBCR444 |
> +						  DRM_COLOR_FORMAT_YCBCR422 |
> +						  DRM_COLOR_FORMAT_YCBCR420,
> +		.gamma_size		= GAMMA_EX_SIZE,
> +		.gamma_bits		= 12,
> +		.pitch_alignment	= 128,
> +		.pipe_sync		= false,
> +		.background		= true,
> +		.panel_sync		= true,
> +		.cap_dec		= true,
> +	},
> +	{
> +		/* DC_REV_2 */
> +		.name			= "DC8200",
> +		.panel_num		= 2,
> +		.plane_num		= 8,
> +		.planes			= dc_hw_planes[DC_REV_2],
> +		.layer_num		= 6,
> +		.max_bpc		= 10,
> +		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
> +						  DRM_COLOR_FORMAT_YCBCR444 |
> +						  DRM_COLOR_FORMAT_YCBCR422 |
> +						  DRM_COLOR_FORMAT_YCBCR420,
> +		.gamma_size		= GAMMA_EX_SIZE,
> +		.gamma_bits		= 12,
> +		.pitch_alignment	= 128,
> +		.pipe_sync		= false,
> +		.background		= true,
> +		.panel_sync		= true,
> +		.cap_dec		= false,
> +	},
> +};
> +
> +static const struct dc_hw_funcs hw_func;
> +
> +static inline u32 hi_read(struct dc_hw *hw, u32 reg)
> +{
> +	return readl(hw->hi_base + reg);
> +}
> +
> +static inline void hi_write(struct dc_hw *hw, u32 reg, u32 value)
> +{
> +	writel(value, hw->hi_base + reg);
> +}
> +
> +static inline void dc_write(struct dc_hw *hw, u32 reg, u32 value)
> +{
> +	writel(value, hw->reg_base + reg - DC_REG_BASE);
> +}
> +
> +static inline u32 dc_read(struct dc_hw *hw, u32 reg)
> +{
> +	u32 value = readl(hw->reg_base + reg - DC_REG_BASE);
> +
> +	return value;
> +}
> +
> +static inline void dc_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear)
> +{
> +	u32 value = dc_read(hw, reg);
> +
> +	value &= ~clear;
> +	value |= set;
> +	dc_write(hw, reg, value);
> +}
> +
> +static void load_default_filter(struct dc_hw *hw,
> +				const struct dc_hw_plane_reg *reg, u32 offset)
> +{
> +	u8 i;
> +
> +	dc_write(hw, reg->scale_config + offset, 0x33);
> +	dc_write(hw, reg->init_offset + offset, 0x80008000);
> +	dc_write(hw, reg->h_filter_coef_index + offset, 0x00);
> +	for (i = 0; i < H_COEF_SIZE; i++)
> +		dc_write(hw, reg->h_filter_coef_data + offset, horkernel[i]);
> +
> +	dc_write(hw, reg->v_filter_coef_index + offset, 0x00);
> +	for (i = 0; i < V_COEF_SIZE; i++)
> +		dc_write(hw, reg->v_filter_coef_data + offset, verkernel[i]);
> +}
> +
> +static void load_rgb_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
> +			    u32 offset, u16 *table)
> +{
> +	dc_write(hw, reg->rgb_to_rgb_coef0 + offset, table[0] | (table[1] << 16));
> +	dc_write(hw, reg->rgb_to_rgb_coef1 + offset, table[2] | (table[3] << 16));
> +	dc_write(hw, reg->rgb_to_rgb_coef2 + offset, table[4] | (table[5] << 16));
> +	dc_write(hw, reg->rgb_to_rgb_coef3 + offset, table[6] | (table[7] << 16));
> +	dc_write(hw, reg->rgb_to_rgb_coef4 + offset, table[8]);
> +}
> +
> +static void load_yuv_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
> +			    u32 offset, s32 *table)
> +{
> +	dc_write(hw, reg->yuv_to_rgb_coef0 + offset,
> +		 (0xFFFF & table[0]) | (table[1] << 16));
> +	dc_write(hw, reg->yuv_to_rgb_coef1 + offset,
> +		 (0xFFFF & table[2]) | (table[3] << 16));
> +	dc_write(hw, reg->yuv_to_rgb_coef2 + offset,
> +		 (0xFFFF & table[4]) | (table[5] << 16));
> +	dc_write(hw, reg->yuv_to_rgb_coef3 + offset,
> +		 (0xFFFF & table[6]) | (table[7] << 16));
> +	dc_write(hw, reg->yuv_to_rgb_coef4 + offset, table[8]);
> +	dc_write(hw, reg->yuv_to_rgb_coefd0 + offset, table[9]);
> +	dc_write(hw, reg->yuv_to_rgb_coefd1 + offset, table[10]);
> +	dc_write(hw, reg->yuv_to_rgb_coefd2 + offset, table[11]);
> +	dc_write(hw, reg->y_clamp_bound + offset, table[12] | (table[13] << 16));
> +	dc_write(hw, reg->uv_clamp_bound + offset, table[14] | (table[15] << 16));
> +}
> +
> +static void load_rgb_to_yuv(struct dc_hw *hw, u32 offset, s16 *table)
> +{
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF0 + offset,
> +		 table[0] | (table[1] << 16));
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF1 + offset,
> +		 table[2] | (table[3] << 16));
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF2 + offset,
> +		 table[4] | (table[5] << 16));
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF3 + offset,
> +		 table[6] | (table[7] << 16));
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF4 + offset, table[8]);
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD0 + offset, table[9]);
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD1 + offset, table[10]);
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD2 + offset, table[11]);
> +}
> +
> +static bool is_rgb(enum dc_hw_color_format format)
> +{
> +	switch (format) {
> +	case FORMAT_X4R4G4B4:
> +	case FORMAT_A4R4G4B4:
> +	case FORMAT_X1R5G5B5:
> +	case FORMAT_A1R5G5B5:
> +	case FORMAT_R5G6B5:
> +	case FORMAT_X8R8G8B8:
> +	case FORMAT_A8R8G8B8:
> +	case FORMAT_A2R10G10B10:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static void load_degamma_table(struct dc_hw *hw,
> +			       const struct dc_hw_plane_reg *reg,
> +			       u32 offset, u16 *table)
> +{
> +	u16 i;
> +	u32 value;
> +
> +	dc_write(hw, reg->degamma_index + offset, 0);
> +
> +	for (i = 0; i < DEGAMMA_SIZE; i++) {
> +		value = table[i] | (table[i] << 16);
> +		dc_write(hw, reg->degamma_data + offset, value);
> +		dc_write(hw, reg->degamma_ex_data + offset, table[i]);
> +	}
> +}
> +
> +static u32 get_addr_offset(u32 id)
> +{
> +	u32 offset = 0;
> +
> +	switch (id) {
> +	case PRIMARY_PLANE_1:
> +	case OVERLAY_PLANE_1:
> +		offset = 0x04;
> +		break;
> +	case OVERLAY_PLANE_2:
> +		offset = 0x08;
> +		break;
> +	case OVERLAY_PLANE_3:
> +		offset = 0x0C;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return offset;
> +}
> +
> +int dc_hw_init(struct dc_hw *hw)
> +{
> +	u8 i, id, panel_num, layer_num;
> +	u32 offset;
> +	u32 revision = hi_read(hw, DC_HW_REVISION);
> +	u32 cid = hi_read(hw, DC_HW_CHIP_CID);
> +	const struct dc_hw_plane_reg *reg;
> +
> +	switch (revision) {
> +	case 0x5720:
> +		hw->rev = DC_REV_0;
> +		break;
> +	case 0x5721:
> +		switch (cid) {
> +		case 0x30B:
> +			hw->rev = DC_REV_1;
> +			break;
> +		case 0x310:
> +			hw->rev = DC_REV_2;
> +			break;
> +		default:
> +			hw->rev = DC_REV_0;
> +			break;
> +		}
> +		break;
> +	default:
> +		return -ENXIO;
> +	}

We (drm/msm) had code like this for a while (and autodetection of the HW 
ID). And nowadays we are migrating to of_device_get_match_data and 
per-SoC compat names. It is just easier to handle, it allows one to 
specify device more precisely and also it simplifies DT schema.

> +
> +	hw->info = (struct vs_dc_info *)&dc_info[hw->rev];
> +	hw->func = (struct dc_hw_funcs *)&hw_func;
> +
> +	layer_num = hw->info->layer_num;
> +	for (i = 0; i < layer_num; i++) {
> +		id = hw->info->planes[i].id;
> +		offset = get_addr_offset(id);
> +		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
> +			reg = &dc_plane_reg[0];
> +		else
> +			reg = &dc_plane_reg[1];
> +
> +		load_default_filter(hw, reg, offset);
> +		load_rgb_to_rgb(hw, reg, offset, RGB2RGB);
> +	}
> +
> +	panel_num = hw->info->panel_num;
> +	for (i = 0; i < panel_num; i++) {
> +		offset = i << 2;
> +
> +		load_rgb_to_yuv(hw, offset, RGB2YUV);
> +		dc_write(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0x111);
> +
> +		offset = i ? DC_CURSOR_OFFSET : 0;
> +		dc_write(hw, DC_CURSOR_BACKGROUND + offset, 0x00FFFFFF);
> +		dc_write(hw, DC_CURSOR_FOREGROUND + offset, 0x00AAAAAA);
> +	}
> +
> +	return 0;
> +}
> +
> +void dc_hw_deinit(struct dc_hw *hw)
> +{
> +	/* Nothing to do */
> +}
> +
> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
> +			struct dc_hw_fb *fb, struct dc_hw_scale *scale,
> +			struct dc_hw_position *pos, struct dc_hw_blend *blend)
> +{
> +	struct dc_hw_plane *plane = &hw->plane[id];
> +
> +	if (plane) {
> +		if (fb) {
> +			if (!fb->enable)
> +				plane->fb.enable = false;
> +			else
> +				memcpy(&plane->fb, fb,
> +				       sizeof(*fb) - sizeof(fb->dirty));
> +			plane->fb.dirty = true;
> +		}
> +		if (scale) {
> +			memcpy(&plane->scale, scale,
> +			       sizeof(*scale) - sizeof(scale->dirty));
> +			plane->scale.dirty = true;
> +		}
> +		if (pos) {
> +			memcpy(&plane->pos, pos,
> +			       sizeof(*pos) - sizeof(pos->dirty));
> +			plane->pos.dirty = true;
> +		}
> +		if (blend) {
> +			memcpy(&plane->blend, blend,
> +			       sizeof(*blend) - sizeof(blend->dirty));
> +			plane->blend.dirty = true;
> +		}
> +	}
> +}
> +
> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode)
> +{
> +	struct dc_hw_plane *plane = &hw->plane[id];
> +
> +	if (plane) {
> +		if (hw->info->planes[id].degamma_size) {
> +			plane->degamma.mode = mode;
> +			plane->degamma.dirty = true;
> +		} else {
> +			plane->degamma.dirty = false;
> +		}
> +	}
> +}
> +
> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi)
> +{
> +	struct dc_hw_plane *plane = &hw->plane[id];
> +
> +	if (plane) {
> +		memcpy(&plane->roi, roi, sizeof(*roi) - sizeof(roi->dirty));
> +		plane->roi.dirty = true;
> +	}
> +}
> +
> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
> +			   struct dc_hw_colorkey *colorkey)
> +{
> +	struct dc_hw_plane *plane = &hw->plane[id];
> +
> +	if (plane) {
> +		memcpy(&plane->colorkey, colorkey,
> +		       sizeof(*colorkey) - sizeof(colorkey->dirty));
> +		plane->colorkey.dirty = true;
> +	}
> +}
> +
> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos)
> +{
> +	memcpy(&hw->qos, qos, sizeof(*qos) - sizeof(qos->dirty));
> +	hw->qos.dirty = true;
> +}
> +
> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor)
> +{
> +	memcpy(&hw->cursor[id], cursor, sizeof(*cursor) - sizeof(cursor->dirty));
> +	hw->cursor[id].dirty = true;
> +}
> +
> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
> +			u16 r, u16 g, u16 b)
> +{
> +	if (index >= hw->info->gamma_size)
> +		return;
> +
> +	hw->gamma[id].gamma[index][0] = r;
> +	hw->gamma[id].gamma[index][1] = g;
> +	hw->gamma[id].gamma[index][2] = b;
> +	hw->gamma[id].dirty = true;
> +}
> +
> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable)
> +{
> +	hw->gamma[id].enable = enable;
> +	hw->gamma[id].dirty = true;
> +}
> +
> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display)
> +{
> +	u8 id = display->id;
> +
> +	memcpy(&hw->display[id], display, sizeof(*display));
> +
> +	hw->func->display(hw, display);
> +}
> +
> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable)
> +{
> +	if (enable)
> +		hi_write(hw, AQ_INTR_ENBL, 0xFFFFFFFF);
> +	else
> +		hi_write(hw, AQ_INTR_ENBL, 0);
> +}
> +
> +u32 dc_hw_get_interrupt(struct dc_hw *hw)
> +{
> +	return hi_read(hw, AQ_INTR_ACKNOWLEDGE);
> +}
> +
> +bool dc_hw_check_underflow(struct dc_hw *hw)
> +{
> +	return dc_read(hw, DC_FRAMEBUFFER_CONFIG) & BIT(5);
> +}
> +
> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable)
> +{
> +	u32 i, offset;
> +	u8 id, layer_num = hw->info->layer_num;
> +	u8 panel_num = hw->info->panel_num;
> +
> +	for (i = 0; i < layer_num; i++) {
> +		id = hw->info->planes[i].id;
> +		offset = get_addr_offset(id);
> +		if (enable) {
> +			if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, BIT(12), 0);
> +			else
> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, BIT(31), 0);
> +		} else {
> +			if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, 0, BIT(12));
> +			else
> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, 0, BIT(31));
> +		}
> +	}
> +
> +	for (i = 0; i < panel_num; i++) {
> +		offset = i << 2;
> +		if (enable)
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, BIT(0));
> +		else
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, BIT(0), 0);
> +	}
> +}
> +
> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id)
> +{
> +	if (out <= OUT_DP)
> +		hw->out[id] = out;
> +}
> +
> +static void gamma_ex_commit(struct dc_hw *hw)
> +{
> +	u8 panel_num = hw->info->panel_num;
> +	u16 i, j;
> +	u32 value;
> +
> +	for (j = 0; j < panel_num; j++) {
> +		if (hw->gamma[j].dirty) {
> +			if (hw->gamma[j].enable) {
> +				dc_write(hw, DC_DISPLAY_GAMMA_EX_INDEX + (j << 2), 0x00);
> +				for (i = 0; i < GAMMA_EX_SIZE; i++) {
> +					value = hw->gamma[j].gamma[i][2] |
> +						(hw->gamma[j].gamma[i][1] << 12);
> +					dc_write(hw, DC_DISPLAY_GAMMA_EX_DATA + (j << 2), value);
> +					dc_write(hw, DC_DISPLAY_GAMMA_EX_ONE_DATA + (j << 2),
> +						 hw->gamma[j].gamma[i][0]);
> +				}
> +				dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
> +					     BIT(13), 0);
> +			} else {
> +				dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
> +					     0, BIT(13));
> +			}
> +			hw->gamma[j].dirty = false;
> +		}
> +	}
> +}
> +
> +static void plane_commit(struct dc_hw *hw)
> +{
> +	struct dc_hw_plane *plane;
> +	const struct dc_hw_plane_reg *reg;
> +	bool primary = false;
> +	u8 id, layer_num = hw->info->layer_num;
> +	u32 i, offset;
> +
> +	for (i = 0; i < layer_num; i++) {
> +		plane = &hw->plane[i];
> +		id = hw->info->planes[i].id;
> +		offset = get_addr_offset(id);
> +		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
> +			reg = &dc_plane_reg[0];
> +			primary = true;
> +		} else {
> +			reg = &dc_plane_reg[1];
> +			primary = false;
> +		}

Can you please move register defines to some internal hw_plane 
structure. These details are known at the time of creation of the plane. 
There is no need to determine them again and again.

> +
> +		if (plane->fb.dirty) {
> +			if (plane->fb.enable) {

if (plane->fb.dirty && plane->fb.enable) will save you one level of 
indentation.

However it might be better to use existing DRM flags instead of 
inventing your own flags.

> +				dc_write(hw, reg->y_address + offset,
> +					 plane->fb.y_address);
> +				dc_write(hw, reg->u_address + offset,
> +					 plane->fb.u_address);
> +				dc_write(hw, reg->v_address + offset,
> +					 plane->fb.v_address);
> +				dc_write(hw, reg->y_stride + offset,
> +					 plane->fb.y_stride);
> +				dc_write(hw, reg->u_stride + offset,
> +					 plane->fb.u_stride);
> +				dc_write(hw, reg->v_stride + offset,
> +					 plane->fb.v_stride);
> +				dc_write(hw, reg->size + offset,
> +					 plane->fb.width |
> +					 (plane->fb.height << 15));
> +				dc_write(hw, reg->water_mark + offset,
> +					 plane->fb.water_mark);
> +
> +				if (plane->fb.clear_enable)
> +					dc_write(hw, reg->clear_value + offset,
> +						 plane->fb.clear_value);
> +			}
> +
> +			if (primary) {

You have two kinds of planes. Primary and overlays. Could you please 
split this code into two separate functions and define two vtables, one 
for primary planes and one for overlays. There is no need to stick 
everything into a single funcion that handles everything.

> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
> +					     (plane->fb.format << 26) |
> +					     (plane->fb.uv_swizzle << 25) |
> +					     (plane->fb.swizzle << 23) |
> +					     (plane->fb.tile_mode << 17) |
> +					     (plane->fb.yuv_color_space << 14) |
> +					     (plane->fb.rotation << 11) |
> +					     (plane->fb.clear_enable << 8),
> +					     (0x1F << 26) |
> +					     BIT(25) |
> +					     (0x03 << 23) |
> +					     (0x1F << 17) |
> +					     (0x07 << 14) |
> +					     (0x07 << 11) |
> +					     BIT(8));
> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
> +					     (plane->fb.dec_enable << 1) |
> +					     (plane->fb.enable << 13) |
> +					     (plane->fb.zpos << 16) |
> +					     (plane->fb.display_id << 19),
> +					     BIT(1) | BIT(13) | (0x07 << 16) | BIT(19));

It looks like this code can enjoy defining names for all the magic 
values. Otherwise it is impossible to support it.

> +			} else {
> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> +					     (plane->fb.dec_enable << 27) |
> +					     (plane->fb.clear_enable << 25) |
> +					     (plane->fb.enable << 24) |
> +					     (plane->fb.format << 16) |
> +					     (plane->fb.uv_swizzle << 15) |
> +					     (plane->fb.swizzle << 13) |
> +					     (plane->fb.tile_mode << 8) |
> +					     (plane->fb.yuv_color_space << 5) |
> +					     (plane->fb.rotation << 2),
> +					     BIT(27) |
> +					     BIT(25) |
> +					     BIT(24) |
> +					     (0x1F << 16) |
> +					     BIT(15) |
> +					     (0x03 << 13) |
> +					     (0x1F << 8) |
> +					     (0x07 << 5) |
> +					     (0x07 << 2));
> +				dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset,
> +					     plane->fb.zpos | (plane->fb.display_id << 3),
> +					     0x07 | BIT(3));
> +			}
> +			plane->fb.dirty = false;
> +		}
> +
> +		if (plane->scale.dirty) {
> +			if (plane->scale.enable) {
> +				dc_write(hw, reg->scale_factor_x + offset,
> +					 plane->scale.scale_factor_x);
> +				dc_write(hw, reg->scale_factor_y + offset,
> +					 plane->scale.scale_factor_y);
> +				if (primary)
> +					dc_set_clear(hw,
> +						     DC_FRAMEBUFFER_CONFIG + offset,
> +						     BIT(22), 0);
> +				else
> +					dc_set_clear(hw,
> +						     DC_OVERLAY_SCALE_CONFIG + offset,
> +						     BIT(8), 0);
> +			} else {
> +				if (primary)
> +					dc_set_clear(hw,
> +						     DC_FRAMEBUFFER_CONFIG + offset,
> +						     0, BIT(22));
> +				else
> +					dc_set_clear(hw,
> +						     DC_OVERLAY_SCALE_CONFIG + offset,
> +						     0, BIT(8));
> +			}
> +			plane->scale.dirty = false;
> +		}
> +
> +		if (plane->pos.dirty) {
> +			dc_write(hw, reg->top_left + offset,
> +				 plane->pos.start_x |
> +				 (plane->pos.start_y << 15));
> +			dc_write(hw, reg->bottom_right + offset,
> +				 plane->pos.end_x |
> +				 (plane->pos.end_y << 15));
> +			plane->pos.dirty = false;
> +		}
> +
> +		if (plane->blend.dirty) {
> +			dc_write(hw, reg->src_global_color + offset,
> +				 plane->blend.alpha << 24);
> +			dc_write(hw, reg->dst_global_color + offset,
> +				 plane->blend.alpha << 24);
> +			switch (plane->blend.blend_mode) {
> +			case BLEND_PREMULTI:
> +				dc_write(hw, reg->blend_config + offset, 0x3450);
> +				break;
> +			case BLEND_COVERAGE:
> +				dc_write(hw, reg->blend_config + offset, 0x3950);
> +				break;
> +			case BLEND_PIXEL_NONE:
> +				dc_write(hw, reg->blend_config + offset, 0x3548);
> +				break;
> +			default:
> +				break;
> +			}
> +			plane->blend.dirty = false;
> +		}
> +
> +		if (plane->colorkey.dirty) {
> +			dc_write(hw, reg->color_key + offset, plane->colorkey.colorkey);
> +			dc_write(hw, reg->color_key_high + offset,
> +				 plane->colorkey.colorkey_high);
> +
> +			if (primary)
> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
> +					     plane->colorkey.transparency << 9, 0x03 << 9);
> +			else
> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> +					     plane->colorkey.transparency, 0x03);
> +
> +			plane->colorkey.dirty = false;
> +		}
> +
> +		if (plane->roi.dirty) {
> +			if (plane->roi.enable) {
> +				dc_write(hw, reg->roi_origin + offset,
> +					 plane->roi.x | (plane->roi.y << 16));
> +				dc_write(hw, reg->roi_size + offset,
> +					 plane->roi.width | (plane->roi.height << 16));
> +				if (primary)
> +					dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
> +						     BIT(0), 0);
> +				else
> +					dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> +						     BIT(22), 0);
> +			} else {
> +				if (primary)
> +					dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
> +						     0, BIT(0));
> +				else
> +					dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> +						     0, BIT(22));
> +			}
> +			plane->roi.dirty = false;
> +		}
> +	}
> +}
> +
> +static void plane_ex_commit(struct dc_hw *hw)
> +{
> +	struct dc_hw_plane *plane;
> +	const struct dc_hw_plane_reg *reg;
> +	bool primary = false;
> +	u8 id, layer_num = hw->info->layer_num;
> +	u32 i, offset;
> +
> +	for (i = 0; i < layer_num; i++) {
> +		plane = &hw->plane[i];
> +		id = hw->info->planes[i].id;
> +		offset = get_addr_offset(id);
> +		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
> +			reg = &dc_plane_reg[0];
> +			primary = true;
> +		} else {
> +			reg = &dc_plane_reg[1];
> +			primary = false;
> +		}
> +
> +		if (plane->fb.dirty) {
> +			if (is_rgb(plane->fb.format)) {
> +				if (primary)
> +					dc_set_clear(hw,
> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
> +						     BIT(6), BIT(8));
> +				else
> +					dc_set_clear(hw,
> +						     DC_OVERLAY_CONFIG + offset,
> +						     BIT(29), BIT(30));
> +			} else {
> +				if (primary)
> +					dc_set_clear(hw,
> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
> +						     BIT(8), BIT(6));
> +				else
> +					dc_set_clear(hw,
> +						     DC_OVERLAY_CONFIG + offset,
> +						     BIT(30), BIT(29));
> +				switch (plane->fb.yuv_color_space) {
> +				case COLOR_SPACE_601:
> +					load_yuv_to_rgb(hw, reg, offset, YUV601_2RGB);
> +					break;
> +				case COLOR_SPACE_709:
> +					load_yuv_to_rgb(hw, reg, offset, YUV709_2RGB);
> +					break;
> +				case COLOR_SPACE_2020:
> +					load_yuv_to_rgb(hw, reg, offset, YUV2020_2RGB);
> +					break;
> +				default:
> +					break;
> +				}
> +			}
> +		}
> +		if (plane->degamma.dirty) {
> +			switch (plane->degamma.mode) {
> +			case VS_DEGAMMA_DISABLE:
> +				if (primary)
> +					dc_set_clear(hw,
> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
> +						     0, BIT(5));
> +				else
> +					dc_set_clear(hw,
> +						     DC_OVERLAY_CONFIG + offset,
> +						     0, BIT(28));
> +				break;
> +			case VS_DEGAMMA_BT709:
> +				load_degamma_table(hw, reg, offset, DEGAMMA_709);
> +				if (primary)
> +					dc_set_clear(hw,
> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
> +						     BIT(5), 0);
> +				else
> +					dc_set_clear(hw,
> +						     DC_OVERLAY_CONFIG + offset,
> +						     BIT(28), 0);
> +				break;
> +			case VS_DEGAMMA_BT2020:
> +				load_degamma_table(hw, reg, offset, DEGAMMA_2020);
> +				if (primary)
> +					dc_set_clear(hw,
> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
> +						     BIT(5), 0);
> +				else
> +					dc_set_clear(hw,
> +						     DC_OVERLAY_CONFIG + offset,
> +						     BIT(28), 0);
> +				break;
> +			default:
> +				break;
> +			}
> +			plane->degamma.dirty = false;
> +		}
> +	}
> +	plane_commit(hw);
> +}
> +
> +static void setup_display(struct dc_hw *hw, struct dc_hw_display *display)
> +{
> +	u8 id = display->id;
> +	u32 dpi_cfg, offset = id << 2;
> +
> +	if (hw->display[id].enable) {
> +		switch (display->bus_format) {
> +		case MEDIA_BUS_FMT_RGB565_1X16:
> +			dpi_cfg = 0;
> +			break;
> +		case MEDIA_BUS_FMT_RGB666_1X18:
> +			dpi_cfg = 3;
> +			break;
> +		case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> +			dpi_cfg = 4;
> +			break;
> +		case MEDIA_BUS_FMT_RGB888_1X24:
> +			dpi_cfg = 5;
> +			break;
> +		case MEDIA_BUS_FMT_RGB101010_1X30:
> +			dpi_cfg = 6;
> +			break;
> +		default:
> +			dpi_cfg = 5;
> +			break;
> +		}
> +		dc_write(hw, DC_DISPLAY_DPI_CONFIG + offset, dpi_cfg);
> +
> +		if (id == 0)
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
> +		else
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
> +
> +		dc_write(hw, DC_DISPLAY_H + offset, hw->display[id].h_active |
> +			 (hw->display[id].h_total << 16));
> +		dc_write(hw, DC_DISPLAY_H_SYNC + offset,
> +			 hw->display[id].h_sync_start |
> +			 (hw->display[id].h_sync_end << 15) |
> +			 (hw->display[id].h_sync_polarity ? 0 : BIT(31)) |
> +			 BIT(30));
> +		dc_write(hw, DC_DISPLAY_V + offset, hw->display[id].v_active |
> +				(hw->display[id].v_total << 16));
> +		dc_write(hw, DC_DISPLAY_V_SYNC + offset,
> +			 hw->display[id].v_sync_start |
> +			 (hw->display[id].v_sync_end << 15) |
> +			 (hw->display[id].v_sync_polarity ? 0 : BIT(31)) |
> +			 BIT(30));
> +
> +		if (hw->info->pipe_sync)
> +			dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
> +				     0, BIT(3) | BIT(4));
> +
> +		dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(12), 0);
> +		if (id == 0)
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(0), BIT(3));
> +		else
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(1), BIT(3));
> +	} else {
> +		dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(12));
> +		if (id == 0)
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
> +		else
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
> +	}
> +}
> +
> +static void setup_display_ex(struct dc_hw *hw, struct dc_hw_display *display)
> +{
> +	u8 id = display->id;
> +	u32 dp_cfg, offset = id << 2;
> +	bool is_yuv = false;
> +
> +	if (hw->display[id].enable && hw->out[id] == OUT_DP) {
> +		switch (display->bus_format) {
> +		case MEDIA_BUS_FMT_RGB565_1X16:
> +			dp_cfg = 0;
> +			break;
> +		case MEDIA_BUS_FMT_RGB666_1X18:
> +			dp_cfg = 1;
> +			break;
> +		case MEDIA_BUS_FMT_RGB888_1X24:
> +			dp_cfg = 2;
> +			break;
> +		case MEDIA_BUS_FMT_RGB101010_1X30:
> +			dp_cfg = 3;
> +			break;
> +		case MEDIA_BUS_FMT_UYVY8_1X16:
> +			dp_cfg = 2 << 4;
> +			is_yuv = true;
> +			break;
> +		case MEDIA_BUS_FMT_YUV8_1X24:
> +			dp_cfg = 4 << 4;
> +			is_yuv = true;
> +			break;
> +		case MEDIA_BUS_FMT_UYVY10_1X20:
> +			dp_cfg = 8 << 4;
> +			is_yuv = true;
> +			break;
> +		case MEDIA_BUS_FMT_YUV10_1X30:
> +			dp_cfg = 10 << 4;
> +			is_yuv = true;
> +			break;
> +		case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +			dp_cfg = 12 << 4;
> +			is_yuv = true;
> +			break;
> +		case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> +			dp_cfg = 13 << 4;
> +			is_yuv = true;
> +			break;
> +		default:
> +			dp_cfg = 2;
> +			break;
> +		}
> +		if (is_yuv)
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(16), 0);
> +		else
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(16));
> +		dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | BIT(3));
> +	}
> +
> +	if (hw->out[id] == OUT_DPI)
> +		dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, BIT(3));
> +
> +	setup_display(hw, display);
> +}
> +
> +static const struct dc_hw_funcs hw_func = {
> +	.gamma = &gamma_ex_commit,
> +	.plane = &plane_ex_commit,
> +	.display = setup_display_ex,
> +};
> +
> +void dc_hw_commit(struct dc_hw *hw)
> +{
> +	u32 i, offset = 0;
> +	u8 plane_num = hw->info->plane_num;
> +	u8 layer_num = hw->info->layer_num;
> +	u8 cursor_num = plane_num - layer_num;
> +
> +	hw->func->gamma(hw);
> +	hw->func->plane(hw);
> +
> +	for (i = 0; i < cursor_num; i++) {
> +		if (hw->cursor[i].dirty) {
> +			offset = hw->cursor[i].display_id ? DC_CURSOR_OFFSET : 0;
> +			if (hw->cursor[i].enable) {
> +				dc_write(hw, DC_CURSOR_ADDRESS + offset,
> +					 hw->cursor[i].address);
> +				dc_write(hw, DC_CURSOR_LOCATION + offset, hw->cursor[i].x |
> +					 (hw->cursor[i].y << 16));
> +				dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
> +					     (hw->cursor[i].hot_x << 16) |
> +					     (hw->cursor[i].hot_y << 8) |
> +					     (hw->cursor[i].size << 5) |
> +					     BIT(3) | BIT(2) | 0x02,
> +					     (0xFF << 16) |
> +					     (0xFF << 8) |
> +					     (0x07 << 5) | 0x1F);
> +			} else {
> +				dc_set_clear(hw, DC_CURSOR_CONFIG + offset, BIT(3), 0x03);
> +			}
> +			hw->cursor[i].dirty = false;
> +		}
> +	}
> +
> +	if (hw->qos.dirty) {
> +		dc_set_clear(hw, DC_QOS_CONFIG, (hw->qos.high_value << 4) |
> +					 hw->qos.low_value, 0xFF);
> +		hw->qos.dirty = false;
> +	}
> +}
> +
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.h b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
> new file mode 100644
> index 000000000..9a1d767ae
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
> @@ -0,0 +1,492 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DC_HW_H__
> +#define __VS_DC_HW_H__
> +
> +#define AQ_INTR_ACKNOWLEDGE				0x0010
> +#define AQ_INTR_ENBL					0x0014

Please take care to align all the defines.

> +#define DC_HW_REVISION					0x0024
> +#define DC_HW_CHIP_CID					0x0030
> +
> +#define DC_REG_BASE						0x0800
> +#define DC_REG_RANGE					0x2000
> +#define DC_SEC_REG_OFFSET				0x100000
> +
> +#define DC_FRAMEBUFFER_CONFIG			0x1518
> +#define DC_FRAMEBUFFER_CONFIG_EX		0x1CC0
> +#define DC_FRAMEBUFFER_SCALE_CONFIG		0x1520
> +#define DC_FRAMEBUFFER_TOP_LEFT			0x24D8
> +#define DC_FRAMEBUFFER_BOTTOM_RIGHT		0x24E0
> +#define DC_FRAMEBUFFER_ADDRESS			0x1400
> +#define DC_FRAMEBUFFER_U_ADDRESS		0x1530
> +#define DC_FRAMEBUFFER_V_ADDRESS		0x1538
> +#define DC_FRAMEBUFFER_STRIDE			0x1408
> +#define DC_FRAMEBUFFER_U_STRIDE			0x1800
> +#define DC_FRAMEBUFFER_V_STRIDE			0x1808
> +#define DC_FRAMEBUFFER_SIZE				0x1810
> +#define DC_FRAMEBUFFER_SCALE_FACTOR_X		0x1828
> +#define DC_FRAMEBUFFER_SCALE_FACTOR_Y		0x1830
> +#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX	0x1838
> +#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA	0x1A00
> +#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX	0x1A08
> +#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA	0x1A10
> +#define DC_FRAMEBUFFER_INIT_OFFSET			0x1A20
> +#define DC_FRAMEBUFFER_COLOR_KEY			0x1508
> +#define DC_FRAMEBUFFER_COLOR_KEY_HIGH		0x1510
> +#define DC_FRAMEBUFFER_CLEAR_VALUE			0x1A18
> +#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX	0x1818
> +#define DC_FRAMEBUFFER_COLOR_TABLE_DATA		0x1820
> +#define DC_FRAMEBUFFER_BG_COLOR				0x1528
> +#define DC_FRAMEBUFFER_ROI_ORIGIN			0x1CB0
> +#define DC_FRAMEBUFFER_ROI_SIZE				0x1CB8
> +#define DC_FRAMEBUFFER_WATER_MARK			0x1CE8
> +#define DC_FRAMEBUFFER_DEGAMMA_INDEX		0x1D88
> +#define DC_FRAMEBUFFER_DEGAMMA_DATA			0x1D90
> +#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA		0x1D98
> +#define DC_FRAMEBUFFER_YUVTORGB_COEF0		0x1DA0
> +#define DC_FRAMEBUFFER_YUVTORGB_COEF1		0x1DA8
> +#define DC_FRAMEBUFFER_YUVTORGB_COEF2		0x1DB0
> +#define DC_FRAMEBUFFER_YUVTORGB_COEF3		0x1DB8
> +#define DC_FRAMEBUFFER_YUVTORGB_COEF4		0x1E00
> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD0		0x1E08
> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD1		0x1E10
> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD2		0x1E18
> +#define DC_FRAMEBUFFER_Y_CLAMP_BOUND		0x1E88
> +#define DC_FRAMEBUFFER_UV_CLAMP_BOUND		0x1E90
> +#define DC_FRAMEBUFFER_RGBTORGB_COEF0		0x1E20
> +#define DC_FRAMEBUFFER_RGBTORGB_COEF1		0x1E28
> +#define DC_FRAMEBUFFER_RGBTORGB_COEF2		0x1E30
> +#define DC_FRAMEBUFFER_RGBTORGB_COEF3		0x1E38
> +#define DC_FRAMEBUFFER_RGBTORGB_COEF4		0x1E40
> +#define DC_FRAMEBUFFER_BLEND_CONFIG			0x2510
> +#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR		0x2500
> +#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR		0x2508
> +
> +#define DC_OVERLAY_CONFIG					0x1540
> +#define DC_OVERLAY_CONFIG_EX				0x2540
> +#define DC_OVERLAY_SCALE_CONFIG				0x1C00
> +#define DC_OVERLAY_BLEND_CONFIG				0x1580
> +#define DC_OVERLAY_TOP_LEFT					0x1640
> +#define DC_OVERLAY_BOTTOM_RIGHT				0x1680
> +#define DC_OVERLAY_ADDRESS					0x15C0
> +#define DC_OVERLAY_U_ADDRESS				0x1840
> +#define DC_OVERLAY_V_ADDRESS				0x1880
> +#define DC_OVERLAY_STRIDE					0x1600
> +#define DC_OVERLAY_U_STRIDE					0x18C0
> +#define DC_OVERLAY_V_STRIDE					0x1900
> +#define DC_OVERLAY_SIZE						0x17C0
> +#define DC_OVERLAY_SCALE_FACTOR_X			0x1A40
> +#define DC_OVERLAY_SCALE_FACTOR_Y			0x1A80
> +#define DC_OVERLAY_H_FILTER_COEF_INDEX		0x1AC0
> +#define DC_OVERLAY_H_FILTER_COEF_DATA		0x1B00
> +#define DC_OVERLAY_V_FILTER_COEF_INDEX		0x1B40
> +#define DC_OVERLAY_V_FILTER_COEF_DATA		0x1B80
> +#define DC_OVERLAY_INIT_OFFSET				0x1BC0
> +#define DC_OVERLAY_COLOR_KEY				0x1740
> +#define DC_OVERLAY_COLOR_KEY_HIGH			0x1780
> +#define DC_OVERLAY_CLEAR_VALUE				0x1940
> +#define DC_OVERLAY_COLOR_TABLE_INDEX		0x1980
> +#define DC_OVERLAY_COLOR_TABLE_DATA			0x19C0
> +#define DC_OVERLAY_SRC_GLOBAL_COLOR			0x16C0
> +#define DC_OVERLAY_DST_GLOBAL_COLOR			0x1700
> +#define DC_OVERLAY_ROI_ORIGIN				0x1D00
> +#define DC_OVERLAY_ROI_SIZE					0x1D40
> +#define DC_OVERLAY_WATER_MARK				0x1DC0
> +#define DC_OVERLAY_DEGAMMA_INDEX			0x2200
> +#define DC_OVERLAY_DEGAMMA_DATA				0x2240
> +#define DC_OVERLAY_DEGAMMA_EX_DATA			0x2280
> +#define DC_OVERLAY_YUVTORGB_COEF0			0x1EC0
> +#define DC_OVERLAY_YUVTORGB_COEF1			0x1F00
> +#define DC_OVERLAY_YUVTORGB_COEF2			0x1F40
> +#define DC_OVERLAY_YUVTORGB_COEF3			0x1F80
> +#define DC_OVERLAY_YUVTORGB_COEF4			0x1FC0
> +#define DC_OVERLAY_YUVTORGB_COEFD0			0x2000
> +#define DC_OVERLAY_YUVTORGB_COEFD1			0x2040
> +#define DC_OVERLAY_YUVTORGB_COEFD2			0x2080
> +#define DC_OVERLAY_Y_CLAMP_BOUND			0x22C0
> +#define DC_OVERLAY_UV_CLAMP_BOUND			0x2300
> +#define DC_OVERLAY_RGBTORGB_COEF0			0x20C0
> +#define DC_OVERLAY_RGBTORGB_COEF1			0x2100
> +#define DC_OVERLAY_RGBTORGB_COEF2			0x2140
> +#define DC_OVERLAY_RGBTORGB_COEF3			0x2180
> +#define DC_OVERLAY_RGBTORGB_COEF4			0x21C0
> +
> +#define DC_CURSOR_CONFIG					0x1468
> +#define DC_CURSOR_ADDRESS					0x146C
> +#define DC_CURSOR_LOCATION					0x1470
> +#define DC_CURSOR_BACKGROUND				0x1474
> +#define DC_CURSOR_FOREGROUND				0x1478
> +#define DC_CURSOR_CLK_GATING				0x1484
> +#define DC_CURSOR_CONFIG_EX					0x24E8
> +#define DC_CURSOR_OFFSET					0x1080
> +
> +#define DC_DISPLAY_DITHER_CONFIG		0x1410
> +#define DC_DISPLAY_PANEL_CONFIG			0x1418
> +#define DC_DISPLAY_PANEL_CONFIG_EX		0x2518
> +#define DC_DISPLAY_DITHER_TABLE_LOW		0x1420
> +#define DC_DISPLAY_DITHER_TABLE_HIGH	0x1428
> +#define DC_DISPLAY_H					0x1430
> +#define DC_DISPLAY_H_SYNC				0x1438
> +#define DC_DISPLAY_V					0x1440
> +#define DC_DISPLAY_V_SYNC				0x1448
> +#define DC_DISPLAY_CURRENT_LOCATION		0x1450
> +#define DC_DISPLAY_GAMMA_INDEX			0x1458
> +#define DC_DISPLAY_GAMMA_DATA			0x1460
> +#define DC_DISPLAY_INT					0x147C
> +#define DC_DISPLAY_INT_ENABLE			0x1480
> +#define DC_DISPLAY_DBI_CONFIG			0x1488
> +#define DC_DISPLAY_GENERAL_CONFIG		0x14B0
> +#define DC_DISPLAY_DPI_CONFIG			0x14B8
> +#define DC_DISPLAY_PANEL_START			0x1CCC
> +#define DC_DISPLAY_DEBUG_COUNTER_SELECT		0x14D0
> +#define DC_DISPLAY_DEBUG_COUNTER_VALUE		0x14D8
> +#define DC_DISPLAY_DP_CONFIG				0x1CD0
> +#define DC_DISPLAY_GAMMA_EX_INDEX			0x1CF0
> +#define DC_DISPLAY_GAMMA_EX_DATA			0x1CF8
> +#define DC_DISPLAY_GAMMA_EX_ONE_DATA		0x1D80
> +#define DC_DISPLAY_RGBTOYUV_COEF0			0x1E48
> +#define DC_DISPLAY_RGBTOYUV_COEF1			0x1E50
> +#define DC_DISPLAY_RGBTOYUV_COEF2			0x1E58
> +#define DC_DISPLAY_RGBTOYUV_COEF3			0x1E60
> +#define DC_DISPLAY_RGBTOYUV_COEF4			0x1E68
> +#define DC_DISPLAY_RGBTOYUV_COEFD0			0x1E70
> +#define DC_DISPLAY_RGBTOYUV_COEFD1			0x1E78
> +#define DC_DISPLAY_RGBTOYUV_COEFD2			0x1E80
> +
> +#define DC_CLK_GATTING					0x1A28
> +#define DC_QOS_CONFIG					0x1A38
> +
> +#define DC_TRANSPARENCY_OPAQUE			0x00
> +#define DC_TRANSPARENCY_KEY				0x02
> +#define DC_DISPLAY_DITHERTABLE_LOW		0x7B48F3C0
> +#define DC_DISPLAY_DITHERTABLE_HIGH		0x596AD1E2
> +
> +#define GAMMA_SIZE						256
> +#define GAMMA_EX_SIZE					300
> +#define DEGAMMA_SIZE					260
> +
> +#define RGB_TO_RGB_TABLE_SIZE			9
> +#define YUV_TO_RGB_TABLE_SIZE			16
> +#define RGB_TO_YUV_TABLE_SIZE			12
> +
> +#define DC_LAYER_NUM	6
> +#define DC_DISPLAY_NUM	2
> +#define DC_CURSOR_NUM	2
> +
> +#define DC_TILE_MODE4X4	0x15
> +
> +enum dc_chip_rev {
> +	DC_REV_0,/* For HW_REV_5720,HW_REV_5721_311 */
> +	DC_REV_1,/* For HW_REV_5721_30B */
> +	DC_REV_2,/* For HW_REV_5721_310 */
> +};
> +
> +enum dc_hw_plane_id {
> +	PRIMARY_PLANE_0,
> +	OVERLAY_PLANE_0,
> +	OVERLAY_PLANE_1,
> +	PRIMARY_PLANE_1,
> +	OVERLAY_PLANE_2,
> +	OVERLAY_PLANE_3,
> +	CURSOR_PLANE_0,
> +	CURSOR_PLANE_1,
> +	PLANE_NUM
> +};
> +
> +enum dc_hw_color_format {
> +	FORMAT_X4R4G4B4,//0

Space after the comma will make it more readable. Or just define the 
values explicitly, it will also be more readable.

> +	FORMAT_A4R4G4B4,//1
> +	FORMAT_X1R5G5B5,//2
> +	FORMAT_A1R5G5B5,//3
> +	FORMAT_R5G6B5,//4
> +	FORMAT_X8R8G8B8,//5
> +	FORMAT_A8R8G8B8,//6
> +	FORMAT_YUY2,//7
> +	FORMAT_UYVY,//8
> +	FORMAT_INDEX8,//9
> +	FORMAT_MONOCHROME,//10
> +	FORMAT_YV12 = 0xf,
> +	FORMAT_A8,//16
> +	FORMAT_NV12,//17
> +	FORMAT_NV16,//18
> +	FORMAT_RG16,//19
> +	FORMAT_R8,//20
> +	FORMAT_NV12_10BIT,//21
> +	FORMAT_A2R10G10B10,//22
> +	FORMAT_NV16_10BIT,//23
> +	FORMAT_INDEX1,//24
> +	FORMAT_INDEX2,//25
> +	FORMAT_INDEX4,//26
> +	FORMAT_P010,//27
> +	FORMAT_YUV444,//28
> +	FORMAT_YUV444_10BIT,//29
> +};
> +
> +enum dc_hw_yuv_color_space {
> +	COLOR_SPACE_601 = 0,
> +	COLOR_SPACE_709 = 1,
> +	COLOR_SPACE_2020 = 3,
> +};
> +
> +enum dc_hw_rotation {
> +	ROT_0 = 0,
> +	ROT_90 = 4,
> +	ROT_180 = 5,
> +	ROT_270 = 6,
> +	FLIP_X = 1,
> +	FLIP_Y = 2,
> +	FLIP_XY = 3,
> +};
> +
> +enum dc_hw_swizzle {
> +	SWIZZLE_ARGB = 0,
> +	SWIZZLE_RGBA,
> +	SWIZZLE_ABGR,
> +	SWIZZLE_BGRA,
> +};
> +
> +enum dc_hw_out {
> +	OUT_DPI,
> +	OUT_DP,
> +};
> +
> +enum dc_hw_cursor_size {
> +	CURSOR_SIZE_32X32 = 0,
> +	CURSOR_SIZE_64X64,
> +};
> +
> +enum dc_hw_blend_mode {
> +	/* out.rgb = plane_alpha * fg.rgb +
> +	 *		(1 - (plane_alpha * fg.alpha)) * bg.rgb
> +	 */
> +	BLEND_PREMULTI,
> +	/* out.rgb = plane_alpha * fg.alpha * fg.rgb +
> +	 *		(1 - (plane_alpha * fg.alpha)) * bg.rgb
> +	 */
> +	BLEND_COVERAGE,
> +	/* out.rgb = plane_alpha * fg.rgb +
> +	 *		(1 - plane_alpha) * bg.rgb
> +	 */
> +	BLEND_PIXEL_NONE,

Can you use defines from <drm/drm_blend.h>?

> +};
> +
> +struct dc_hw_plane_reg {
> +	u32 y_address;
> +	u32 u_address;
> +	u32 v_address;
> +	u32 y_stride;
> +	u32 u_stride;
> +	u32 v_stride;
> +	u32 size;
> +	u32 top_left;
> +	u32 bottom_right;
> +	u32 scale_factor_x;
> +	u32 scale_factor_y;
> +	u32 h_filter_coef_index;
> +	u32 h_filter_coef_data;
> +	u32 v_filter_coef_index;
> +	u32 v_filter_coef_data;
> +	u32 init_offset;
> +	u32 color_key;
> +	u32 color_key_high;
> +	u32 clear_value;
> +	u32 color_table_index;
> +	u32 color_table_data;
> +	u32 scale_config;
> +	u32 water_mark;
> +	u32 degamma_index;
> +	u32 degamma_data;
> +	u32 degamma_ex_data;
> +	u32 src_global_color;
> +	u32 dst_global_color;
> +	u32 blend_config;
> +	u32 roi_origin;
> +	u32 roi_size;
> +	u32 yuv_to_rgb_coef0;
> +	u32 yuv_to_rgb_coef1;
> +	u32 yuv_to_rgb_coef2;
> +	u32 yuv_to_rgb_coef3;
> +	u32 yuv_to_rgb_coef4;
> +	u32 yuv_to_rgb_coefd0;
> +	u32 yuv_to_rgb_coefd1;
> +	u32 yuv_to_rgb_coefd2;
> +	u32 y_clamp_bound;
> +	u32 uv_clamp_bound;
> +	u32 rgb_to_rgb_coef0;
> +	u32 rgb_to_rgb_coef1;
> +	u32 rgb_to_rgb_coef2;
> +	u32 rgb_to_rgb_coef3;
> +	u32 rgb_to_rgb_coef4;
> +};
> +
> +struct dc_hw_fb {
> +	u32 y_address;
> +	u32 u_address;
> +	u32 v_address;
> +	u32 clear_value;
> +	u32 water_mark;
> +	u16 y_stride;
> +	u16 u_stride;
> +	u16 v_stride;
> +	u16 width;
> +	u16 height;
> +	u8	format;
> +	u8	tile_mode;
> +	u8	rotation;
> +	u8	yuv_color_space;
> +	u8	swizzle;
> +	u8	uv_swizzle;
> +	u8	zpos;
> +	u8	display_id;
> +	bool	clear_enable;
> +	bool	dec_enable;
> +	bool	enable;
> +	bool	dirty;
> +};
> +
> +struct dc_hw_scale {
> +	u32 scale_factor_x;
> +	u32 scale_factor_y;
> +	bool	enable;
> +	bool	dirty;
> +};
> +
> +struct dc_hw_position {
> +	u16 start_x;
> +	u16 start_y;
> +	u16 end_x;
> +	u16 end_y;
> +	bool	dirty;
> +};
> +
> +struct dc_hw_blend {
> +	u8	alpha;
> +	u8	blend_mode;
> +	bool	dirty;
> +};
> +
> +struct dc_hw_colorkey {
> +	u32 colorkey;
> +	u32 colorkey_high;
> +	u8	transparency;
> +	bool dirty;
> +};
> +
> +struct dc_hw_roi {
> +	u16 x;
> +	u16 y;
> +	u16 width;
> +	u16 height;
> +	bool enable;
> +	bool dirty;
> +};
> +
> +struct dc_hw_cursor {
> +	u32 address;
> +	u16 x;
> +	u16 y;
> +	u16 hot_x;
> +	u16 hot_y;
> +	u8	size;
> +	u8	display_id;
> +	bool	enable;
> +	bool	dirty;
> +};
> +
> +struct dc_hw_display {
> +	u32 bus_format;
> +	u16 h_active;
> +	u16 h_total;
> +	u16 h_sync_start;
> +	u16 h_sync_end;
> +	u16 v_active;
> +	u16 v_total;
> +	u16 v_sync_start;
> +	u16 v_sync_end;
> +	u8	id;
> +	bool	h_sync_polarity;
> +	bool	v_sync_polarity;
> +	bool	enable;
> +};
> +
> +struct dc_hw_gamma {
> +	u16 gamma[GAMMA_EX_SIZE][3];
> +	bool	enable;
> +	bool	dirty;
> +};
> +
> +struct dc_hw_degamma {
> +	u16 degamma[DEGAMMA_SIZE][3];
> +	u32 mode;
> +	bool	dirty;
> +};
> +
> +struct dc_hw_plane {
> +	struct dc_hw_fb			fb;
> +	struct dc_hw_position	pos;
> +	struct dc_hw_scale		scale;
> +	struct dc_hw_blend		blend;
> +	struct dc_hw_roi		roi;
> +	struct dc_hw_colorkey	colorkey;
> +	struct dc_hw_degamma	degamma;
> +};
> +
> +struct dc_hw_qos {
> +	u8	  low_value;
> +	u8	  high_value;
> +	bool  dirty;
> +};
> +
> +struct dc_hw_read {
> +	u32			reg;
> +	u32			value;
> +};
> +
> +struct dc_hw;
> +struct dc_hw_funcs {
> +	void (*gamma)(struct dc_hw *hw);
> +	void (*plane)(struct dc_hw *hw);
> +	void (*display)(struct dc_hw *hw, struct dc_hw_display *display);
> +};
> +
> +struct dc_hw {
> +	enum dc_chip_rev rev;
> +	enum dc_hw_out		out[DC_DISPLAY_NUM];
> +	void			*hi_base;
> +	void			*reg_base;
> +
> +	struct dc_hw_display display[DC_DISPLAY_NUM];
> +	struct dc_hw_gamma	 gamma[DC_DISPLAY_NUM];
> +	struct dc_hw_plane	 plane[DC_LAYER_NUM];
> +	struct dc_hw_cursor  cursor[DC_CURSOR_NUM];
> +	struct dc_hw_qos	 qos;
> +	struct dc_hw_funcs	 *func;
> +	struct vs_dc_info	 *info;
> +};
> +
> +int dc_hw_init(struct dc_hw *hw);
> +void dc_hw_deinit(struct dc_hw *hw);
> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
> +			struct dc_hw_fb *fb, struct dc_hw_scale *scale,
> +			struct dc_hw_position *pos, struct dc_hw_blend *blend);
> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode);
> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi);
> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
> +			   struct dc_hw_colorkey *colorkey);
> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos);
> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor);
> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
> +			u16 r, u16 g, u16 b);
> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable);
> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display);
> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable);
> +u32 dc_hw_get_interrupt(struct dc_hw *hw);
> +bool dc_hw_check_underflow(struct dc_hw *hw);
> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable);
> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id);
> +void dc_hw_commit(struct dc_hw *hw);
> +
> +#endif /* __VS_DC_HW_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
> index da7698c3d..3cd533cfa 100644
> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> @@ -21,6 +21,7 @@
>   
>   #include "vs_drv.h"
>   #include "vs_modeset.h"
> +#include "vs_dc.h"
>   
>   #define DRV_NAME	"verisilicon"
>   #define DRV_DESC	"Verisilicon DRM driver"
> @@ -123,6 +124,7 @@ static const struct component_master_ops vs_drm_ops = {
>   };
>   
>   static struct platform_driver *drm_sub_drivers[] = {
> +	&dc_platform_driver,
>   
>   };
>   
> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c
> new file mode 100644
> index 000000000..9bd066015
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_plane.c
> @@ -0,0 +1,526 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_blend.h>
> +#include <drm/drm_gem_dma_helper.h>
> +#include <drm/drm_fb_dma_helper.h>
> +#include <drm/drm_framebuffer.h>
> +#include <drm/drm_plane.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include <drm/vs_drm.h>
> +
> +#include "vs_plane.h"
> +#include "vs_drv.h"
> +#include "vs_dc.h"
> +
> +static void vs_plane_reset(struct drm_plane *plane)
> +{
> +	struct vs_plane_state *state;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +
> +	if (plane->state) {
> +		__drm_atomic_helper_plane_destroy_state(plane->state);
> +
> +		state = to_vs_plane_state(plane->state);
> +		kfree(state);
> +		plane->state = NULL;
> +	}
> +

Use vs_plane_destroy_state directly. This way you won't miss a refcount 
on blobs.

> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return;
> +
> +	state->degamma = VS_DEGAMMA_DISABLE;
> +	state->degamma_changed = false;
> +	state->base.zpos = vs_plane->id;
> +	memset(&state->status, 0, sizeof(state->status));
> +
> +	__drm_atomic_helper_plane_reset(plane, &state->base);
> +}
> +
> +static void _vs_plane_duplicate_blob(struct vs_plane_state *state,
> +				     struct vs_plane_state *ori_state)
> +{
> +	state->watermark = ori_state->watermark;
> +	state->color_mgmt = ori_state->color_mgmt;
> +	state->roi = ori_state->roi;
> +
> +	if (state->watermark)
> +		drm_property_blob_get(state->watermark);
> +	if (state->color_mgmt)
> +		drm_property_blob_get(state->color_mgmt);
> +	if (state->roi)
> +		drm_property_blob_get(state->roi);
> +}
> +
> +static int
> +_vs_plane_set_property_blob_from_id(struct drm_device *dev,
> +				    struct drm_property_blob **blob,
> +				    u64 blob_id,
> +				    size_t expected_size)
> +{
> +	struct drm_property_blob *new_blob = NULL;
> +
> +	if (blob_id) {
> +		new_blob = drm_property_lookup_blob(dev, blob_id);
> +		if (!new_blob)
> +			return -EINVAL;
> +
> +		if (new_blob->length != expected_size) {
> +			drm_property_blob_put(new_blob);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	drm_property_replace_blob(blob, new_blob);
> +	drm_property_blob_put(new_blob);
> +
> +	return 0;
> +}
> +
> +static struct drm_plane_state *
> +vs_plane_atomic_duplicate_state(struct drm_plane *plane)
> +{
> +	struct vs_plane_state *ori_state;
> +	struct vs_plane_state *state;
> +
> +	if (WARN_ON(!plane->state))
> +		return NULL;
> +
> +	ori_state = to_vs_plane_state(plane->state);
> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return NULL;
> +
> +	__drm_atomic_helper_plane_duplicate_state(plane, &state->base);
> +
> +	state->degamma = ori_state->degamma;
> +	state->degamma_changed = ori_state->degamma_changed;
> +
> +	_vs_plane_duplicate_blob(state, ori_state);
> +	memcpy(&state->status, &ori_state->status, sizeof(ori_state->status));
> +
> +	return &state->base;
> +}
> +
> +static void vs_plane_atomic_destroy_state(struct drm_plane *plane,
> +					  struct drm_plane_state *state)
> +{
> +	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
> +
> +	__drm_atomic_helper_plane_destroy_state(state);
> +
> +	drm_property_blob_put(vs_plane_state->watermark);
> +	drm_property_blob_put(vs_plane_state->color_mgmt);
> +	drm_property_blob_put(vs_plane_state->roi);
> +	kfree(vs_plane_state);
> +}
> +
> +static int vs_plane_atomic_set_property(struct drm_plane *plane,
> +					struct drm_plane_state *state,
> +					struct drm_property *property,
> +					uint64_t val)
> +{
> +	struct drm_device *dev = plane->dev;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
> +	int ret = 0;
> +
> +	if (property == vs_plane->degamma_mode) {
> +		if (vs_plane_state->degamma != val) {
> +			vs_plane_state->degamma = val;
> +			vs_plane_state->degamma_changed = true;
> +		} else {
> +			vs_plane_state->degamma_changed = false;
> +		}
> +	} else if (property == vs_plane->watermark_prop) {
> +		ret = _vs_plane_set_property_blob_from_id(dev,
> +							  &vs_plane_state->watermark,
> +							  val,
> +							  sizeof(struct drm_vs_watermark));
> +		return ret;
> +	} else if (property == vs_plane->color_mgmt_prop) {
> +		ret = _vs_plane_set_property_blob_from_id(dev,
> +							  &vs_plane_state->color_mgmt,
> +							  val,
> +							  sizeof(struct drm_vs_color_mgmt));
> +		return ret;
> +	} else if (property == vs_plane->roi_prop) {
> +		ret = _vs_plane_set_property_blob_from_id(dev,
> +							  &vs_plane_state->roi,
> +							  val,
> +							  sizeof(struct drm_vs_roi));
> +		return ret;
> +	} else {
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int vs_plane_atomic_get_property(struct drm_plane *plane,
> +					const struct drm_plane_state *state,
> +					struct drm_property *property,
> +					uint64_t *val)
> +{
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +	const struct vs_plane_state *vs_plane_state =
> +		container_of(state, const struct vs_plane_state, base);
> +
> +	if (property == vs_plane->degamma_mode)
> +		*val = vs_plane_state->degamma;
> +	else if (property == vs_plane->watermark_prop)
> +		*val = (vs_plane_state->watermark) ?
> +					vs_plane_state->watermark->base.id : 0;
> +	else if (property == vs_plane->color_mgmt_prop)
> +		*val = (vs_plane_state->color_mgmt) ?
> +					vs_plane_state->color_mgmt->base.id : 0;

degamma and color management should use standard properties.

> +	else if (property == vs_plane->roi_prop)
> +		*val = (vs_plane_state->roi) ?
> +					vs_plane_state->roi->base.id : 0;
> +	else
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static bool vs_format_mod_supported(struct drm_plane *plane,
> +				    u32 format,
> +				    u64 modifier)
> +{
> +	int i;
> +
> +	/* We always have to allow these modifiers:
> +	 * 1. Core DRM checks for LINEAR support if userspace does not provide modifiers.
> +	 * 2. Not passing any modifiers is the same as explicitly passing INVALID.
> +	 */
> +	if (modifier == DRM_FORMAT_MOD_LINEAR)
> +		return true;
> +
> +	/* Check that the modifier is on the list of the plane's supported modifiers. */
> +	for (i = 0; i < plane->modifier_count; i++) {
> +		if (modifier == plane->modifiers[i])
> +			break;
> +	}
> +
> +	if (i == plane->modifier_count)
> +		return false;
> +
> +	return true;
> +}
> +
> +const struct drm_plane_funcs vs_plane_funcs = {
> +	.update_plane		= drm_atomic_helper_update_plane,
> +	.disable_plane		= drm_atomic_helper_disable_plane,
> +	.reset			= vs_plane_reset,
> +	.atomic_duplicate_state = vs_plane_atomic_duplicate_state,
> +	.atomic_destroy_state	= vs_plane_atomic_destroy_state,
> +	.atomic_set_property	= vs_plane_atomic_set_property,
> +	.atomic_get_property	= vs_plane_atomic_get_property,
> +	.format_mod_supported	= vs_format_mod_supported,
> +};
> +
> +static unsigned char vs_get_plane_number(struct drm_framebuffer *fb)
> +{
> +	const struct drm_format_info *info;
> +
> +	if (!fb)
> +		return 0;
> +
> +	info = drm_format_info(fb->format->format);
> +	if (!info || info->num_planes > DRM_FORMAT_MAX_PLANES)
> +		return 0;
> +
> +	return info->num_planes;
> +}
> +
> +static int vs_plane_atomic_check(struct drm_plane *plane,
> +				 struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> +										 plane);
> +	unsigned char i, num_planes;
> +	struct drm_framebuffer *fb = new_plane_state->fb;
> +	struct drm_crtc *crtc = new_plane_state->crtc;
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
> +
> +	if (!crtc || !fb)
> +		return 0;
> +
> +	num_planes = vs_get_plane_number(fb);
> +
> +	for (i = 0; i < num_planes; i++) {
> +		dma_addr_t dma_addr;
> +
> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
> +		plane_state->dma_addr[i] = dma_addr;
> +	}
> +
> +	return vs_dc_check_plane(dc, plane, state);
> +}
> +
> +static int vs_cursor_plane_atomic_check(struct drm_plane *plane,
> +					struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> +									  plane);
> +	unsigned char i, num_planes;
> +	struct drm_framebuffer *fb = new_plane_state->fb;
> +	struct drm_crtc *crtc = new_plane_state->crtc;
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
> +
> +	if (!crtc || !fb)
> +		return 0;
> +
> +	num_planes = vs_get_plane_number(fb);
> +
> +	for (i = 0; i < num_planes; i++) {
> +		dma_addr_t dma_addr;
> +
> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
> +		plane_state->dma_addr[i] = dma_addr;
> +	}
> +
> +	return vs_dc_check_cursor_plane(dc, plane, state);
> +}
> +
> +static void vs_plane_atomic_update(struct drm_plane *plane,
> +				   struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
> +									  plane);

New line after the equal sign will be better.

> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> +									  plane);
> +
> +	unsigned char i, num_planes;
> +	struct drm_framebuffer *fb;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +	struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	if (!new_state->fb || !new_state->crtc)
> +		return;

if (!new_state->visible) ?

> +
> +	fb = new_state->fb;
> +
> +	drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
> +
> +	num_planes = vs_get_plane_number(fb);
> +
> +	for (i = 0; i < num_planes; i++) {
> +		dma_addr_t dma_addr;
> +
> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
> +		plane_state->dma_addr[i] = dma_addr;
> +	}
> +
> +	plane_state->status.src = drm_plane_state_src(new_state);
> +	plane_state->status.dest = drm_plane_state_dest(new_state);
> +
> +	vs_dc_update_plane(dc, vs_plane, plane, state);
> +}
> +
> +static void vs_cursor_plane_atomic_update(struct drm_plane *plane,
> +					  struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
> +									   plane);
> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> +									   plane);
> +	unsigned char i, num_planes;
> +	struct drm_framebuffer *fb;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +	struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	if (!new_state->fb || !new_state->crtc)

and here.

> +		return;
> +
> +	fb = new_state->fb;
> +	drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
> +
> +	num_planes = vs_get_plane_number(fb);
> +
> +	for (i = 0; i < num_planes; i++) {
> +		dma_addr_t dma_addr;
> +
> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
> +		plane_state->dma_addr[i] = dma_addr;
> +	}
> +
> +	plane_state->status.src = drm_plane_state_src(new_state);
> +	plane_state->status.dest = drm_plane_state_dest(new_state);
> +
> +	vs_dc_update_cursor_plane(dc, vs_plane, plane, state);
> +}
> +
> +static void vs_plane_atomic_disable(struct drm_plane *plane,
> +				    struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> +									   plane);
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +	struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_disable_plane(dc, vs_plane, old_state);
> +}
> +
> +static void vs_cursor_plane_atomic_disable(struct drm_plane *plane,
> +					   struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> +									   plane);
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +	struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_disable_cursor_plane(dc, vs_plane, old_state);
> +}
> +
> +const struct drm_plane_helper_funcs primary_plane_helpers = {
> +	.atomic_check	= vs_plane_atomic_check,
> +	.atomic_update	= vs_plane_atomic_update,
> +	.atomic_disable = vs_plane_atomic_disable,
> +};
> +
> +const struct drm_plane_helper_funcs overlay_plane_helpers = {
> +	.atomic_check	= vs_plane_atomic_check,
> +	.atomic_update	= vs_plane_atomic_update,
> +	.atomic_disable = vs_plane_atomic_disable,
> +};
> +
> +const struct drm_plane_helper_funcs cursor_plane_helpers = {
> +	.atomic_check	= vs_cursor_plane_atomic_check,
> +	.atomic_update	= vs_cursor_plane_atomic_update,
> +	.atomic_disable = vs_cursor_plane_atomic_disable,
> +};
> +
> +static const struct drm_prop_enum_list vs_degamma_mode_enum_list[] = {
> +	{ VS_DEGAMMA_DISABLE,	"disabled" },
> +	{ VS_DEGAMMA_BT709,	"preset degamma for BT709" },
> +	{ VS_DEGAMMA_BT2020,	"preset degamma for BT2020" },
> +};
> +
> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
> +				 struct vs_plane_info *info,
> +				 unsigned int layer_num,
> +				 unsigned int possible_crtcs)
> +{
> +	struct vs_plane *plane;
> +	int ret;
> +
> +	if (!info)
> +		return NULL;
> +
> +	plane = drmm_universal_plane_alloc(drm_dev, struct vs_plane, base,
> +					   possible_crtcs,
> +					   &vs_plane_funcs,
> +					   info->formats, info->num_formats,
> +					   info->modifiers, info->type,
> +					   info->name ? info->name : NULL);
> +	if (IS_ERR(plane))
> +		return ERR_CAST(plane);
> +
> +	if (info->type == DRM_PLANE_TYPE_PRIMARY)
> +		drm_plane_helper_add(&plane->base, &primary_plane_helpers);
> +	else if (info->type == DRM_PLANE_TYPE_CURSOR)
> +		drm_plane_helper_add(&plane->base, &cursor_plane_helpers);
> +	else
> +		drm_plane_helper_add(&plane->base, &overlay_plane_helpers);
> +
> +	/* Set up the plane properties */
> +	if (info->degamma_size) {
> +		plane->degamma_mode =
> +		drm_property_create_enum(drm_dev, 0,
> +					 "DEGAMMA_MODE",
> +					 vs_degamma_mode_enum_list,
> +					 ARRAY_SIZE(vs_degamma_mode_enum_list));
> +
> +		if (!plane->degamma_mode)
> +			return NULL;
> +
> +		drm_object_attach_property(&plane->base.base,
> +					   plane->degamma_mode,
> +					   VS_DEGAMMA_DISABLE);
> +	}
> +
> +	if (info->rotation) {
> +		ret = drm_plane_create_rotation_property(&plane->base,
> +							 DRM_MODE_ROTATE_0,
> +							 info->rotation);
> +		if (ret)
> +			return NULL;
> +	}
> +
> +	if (info->blend_mode) {
> +		ret = drm_plane_create_blend_mode_property(&plane->base,
> +							   info->blend_mode);
> +		if (ret)
> +			return NULL;
> +		ret = drm_plane_create_alpha_property(&plane->base);
> +		if (ret)
> +			return NULL;
> +	}
> +
> +	if (info->color_encoding) {
> +		ret = drm_plane_create_color_properties(&plane->base,
> +							info->color_encoding,
> +							BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
> +							DRM_COLOR_YCBCR_BT709,
> +							DRM_COLOR_YCBCR_LIMITED_RANGE);
> +		if (ret)
> +			return NULL;
> +	}
> +
> +	if (info->zpos != 255) {
> +		ret = drm_plane_create_zpos_property(&plane->base, info->zpos, 0,
> +						     layer_num - 1);
> +		if (ret)
> +			return NULL;
> +	} else {
> +		ret = drm_plane_create_zpos_immutable_property(&plane->base,
> +							       info->zpos);
> +		if (ret)
> +			return NULL;
> +	}
> +
> +	if (info->watermark) {
> +		plane->watermark_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
> +							    "WATERMARK", 0);
> +		if (!plane->watermark_prop)
> +			return NULL;
> +		drm_object_attach_property(&plane->base.base, plane->watermark_prop, 0);
> +	}
> +
> +	if (info->color_mgmt) {
> +		plane->color_mgmt_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
> +							     "COLOR_CONFIG", 0);
> +		if (!plane->color_mgmt_prop)
> +			return NULL;
> +
> +		drm_object_attach_property(&plane->base.base, plane->color_mgmt_prop, 0);
> +	}
> +
> +	if (info->roi) {
> +		plane->roi_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
> +						      "ROI", 0);
> +		if (!plane->roi_prop)
> +			return NULL;
> +
> +		drm_object_attach_property(&plane->base.base, plane->roi_prop, 0);
> +	}
> +
> +	return plane;
> +}
> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h
> new file mode 100644
> index 000000000..554b74e96
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_plane.h
> @@ -0,0 +1,58 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_PLANE_H__
> +#define __VS_PLANE_H__
> +
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include "vs_type.h"
> +
> +struct vs_plane_status {
> +	u32 tile_mode;
> +	struct drm_rect src;
> +	struct drm_rect dest;
> +};
> +
> +struct vs_plane_state {
> +	struct drm_plane_state base;
> +	struct vs_plane_status status; /* for debugfs */
> +
> +	struct drm_property_blob *watermark;
> +	struct drm_property_blob *color_mgmt;
> +	struct drm_property_blob *roi;
> +	dma_addr_t dma_addr[DRM_FORMAT_MAX_PLANES];
> +
> +	u32 degamma;
> +	bool degamma_changed;
> +};
> +
> +struct vs_plane {
> +	struct drm_plane base;
> +	u8 id;
> +
> +	struct drm_property *degamma_mode;
> +	struct drm_property *watermark_prop;
> +	struct drm_property *color_mgmt_prop;
> +	struct drm_property *roi_prop;
> +};
> +
> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
> +				 struct vs_plane_info *info,
> +				 unsigned int layer_num,
> +				 unsigned int possible_crtcs);
> +
> +static inline struct vs_plane *to_vs_plane(struct drm_plane *plane)
> +{
> +	return container_of(plane, struct vs_plane, base);
> +}
> +
> +static inline struct vs_plane_state *
> +to_vs_plane_state(struct drm_plane_state *state)
> +{
> +	return container_of(state, struct vs_plane_state, base);
> +}
> +#endif /* __VS_PLANE_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_type.h b/drivers/gpu/drm/verisilicon/vs_type.h
> new file mode 100644
> index 000000000..7d3378e29
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_type.h
> @@ -0,0 +1,69 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_TYPE_H__
> +#define __VS_TYPE_H__
> +
> +#include <drm/drm_plane.h>
> +#include <drm/drm_plane_helper.h>
> +
> +struct vs_plane_info {
> +	const char *name;
> +	u8 id;
> +	enum drm_plane_type type;
> +	unsigned int num_formats;
> +	const u32 *formats;
> +	u8 num_modifiers;
> +	const u64 *modifiers;
> +	unsigned int min_width;
> +	unsigned int min_height;
> +	unsigned int max_width;
> +	unsigned int max_height;
> +	unsigned int rotation;
> +	unsigned int blend_mode;
> +	unsigned int color_encoding;
> +
> +	/* 0 means no de-gamma LUT */
> +	unsigned int degamma_size;
> +
> +	int min_scale; /* 16.16 fixed point */
> +	int max_scale; /* 16.16 fixed point */
> +
> +	/* default zorder value,
> +	 * and 255 means unsupported zorder capability
> +	 */
> +	u8	 zpos;
> +
> +	bool watermark;
> +	bool color_mgmt;
> +	bool roi;
> +};
> +
> +struct vs_dc_info {
> +	const char *name;
> +
> +	u8 panel_num;
> +
> +	/* planes */
> +	u8 plane_num;
> +	const struct vs_plane_info *planes;
> +
> +	u8 layer_num;
> +	unsigned int max_bpc;
> +	unsigned int color_formats;
> +
> +	/* 0 means no gamma LUT */
> +	u16 gamma_size;
> +	u8 gamma_bits;
> +
> +	u16 pitch_alignment;
> +
> +	bool pipe_sync;
> +	bool background;
> +	bool panel_sync;
> +	bool cap_dec;
> +};
> +
> +#endif /* __VS_TYPE_H__ */

-- 
With best wishes
Dmitry


^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-10-25 19:28     ` Dmitry Baryshkov
  0 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-10-25 19:28 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: Conor Dooley, Albert Ou, Emil Renner Berthing, christian.koenig,
	Thomas Zimmermann, Bjorn Andersson, Chris Morgan, Maxime Ripard,
	Jagan Teki, Jack Zhu, Rob Herring, Palmer Dabbelt,
	Krzysztof Kozlowski, Paul Walmsley, Shengyang Chen,
	Changhuang Liang, Shawn Guo, Sumit Semwal

On 25/10/2023 13:39, Keith Zhao wrote:
> add 2 crtcs and 8 planes in vs-drm
> 
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
>   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
>   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
>   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
>   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
>   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
>   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
>   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
>   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
>   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
>   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
>   11 files changed, 4494 insertions(+), 2 deletions(-)
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
> 
> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> index 7d3be305b..1d48016ca 100644
> --- a/drivers/gpu/drm/verisilicon/Makefile
> +++ b/drivers/gpu/drm/verisilicon/Makefile
> @@ -1,7 +1,11 @@
>   # SPDX-License-Identifier: GPL-2.0
>   
> -vs_drm-objs := vs_drv.o \
> -		vs_modeset.o
> +vs_drm-objs := vs_dc_hw.o \
> +		vs_dc.o \
> +		vs_crtc.o \
> +		vs_drv.o \
> +		vs_modeset.o \
> +		vs_plane.o
>   
>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>   
> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
> new file mode 100644
> index 000000000..8a658ea77
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
> @@ -0,0 +1,257 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + *
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/debugfs.h>
> +#include <linux/media-bus-format.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_gem_atomic_helper.h>
> +#include <drm/drm_vblank.h>
> +#include <drm/vs_drm.h>
> +
> +#include "vs_crtc.h"
> +#include "vs_dc.h"
> +#include "vs_drv.h"
> +
> +static void vs_crtc_reset(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc_state *state;
> +
> +	if (crtc->state) {
> +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
> +
> +		state = to_vs_crtc_state(crtc->state);
> +		kfree(state);
> +		crtc->state = NULL;
> +	}

You can call your crtc_destroy_state function directly here.

> +
> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return;
> +
> +	__drm_atomic_helper_crtc_reset(crtc, &state->base);
> +}
> +
> +static struct drm_crtc_state *
> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc_state *ori_state;

It might be a matter of taste, but it is usually old_state.

> +	struct vs_crtc_state *state;
> +
> +	if (!crtc->state)
> +		return NULL;
> +
> +	ori_state = to_vs_crtc_state(crtc->state);
> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return NULL;
> +
> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
> +
> +	state->output_fmt = ori_state->output_fmt;
> +	state->encoder_type = ori_state->encoder_type;
> +	state->bpp = ori_state->bpp;
> +	state->underflow = ori_state->underflow;

Can you use kmemdup instead?

> +
> +	return &state->base;
> +}
> +
> +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
> +					 struct drm_crtc_state *state)
> +{
> +	__drm_atomic_helper_crtc_destroy_state(state);
> +	kfree(to_vs_crtc_state(state));
> +}
> +
> +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_enable_vblank(dc, true);
> +
> +	return 0;
> +}
> +
> +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_enable_vblank(dc, false);
> +}
> +
> +static const struct drm_crtc_funcs vs_crtc_funcs = {
> +	.set_config		= drm_atomic_helper_set_config,
> +	.page_flip		= drm_atomic_helper_page_flip,

destroy is required, see drm_mode_config_cleanup()

> +	.reset			= vs_crtc_reset,
> +	.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
> +	.atomic_destroy_state	= vs_crtc_atomic_destroy_state,

please consider adding atomic_print_state to output driver-specific bits.

> +	.enable_vblank		= vs_crtc_enable_vblank,
> +	.disable_vblank		= vs_crtc_disable_vblank,
> +};
> +
> +static u8 cal_pixel_bits(u32 bus_format)

This looks like a generic helper code, which can go to a common place.

> +{
> +	u8 bpp;
> +
> +	switch (bus_format) {
> +	case MEDIA_BUS_FMT_RGB565_1X16:
> +	case MEDIA_BUS_FMT_UYVY8_1X16:
> +		bpp = 16;
> +		break;
> +	case MEDIA_BUS_FMT_RGB666_1X18:
> +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> +		bpp = 18;
> +		break;
> +	case MEDIA_BUS_FMT_UYVY10_1X20:
> +		bpp = 20;
> +		break;
> +	case MEDIA_BUS_FMT_BGR888_1X24:
> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +	case MEDIA_BUS_FMT_YUV8_1X24:
> +		bpp = 24;
> +		break;
> +	case MEDIA_BUS_FMT_RGB101010_1X30:
> +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> +	case MEDIA_BUS_FMT_YUV10_1X30:
> +		bpp = 30;
> +		break;
> +	default:
> +		bpp = 24;
> +		break;
> +	}
> +
> +	return bpp;
> +}
> +
> +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
> +				  struct drm_atomic_state *state)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
> +
> +	vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
> +
> +	vs_dc_enable(dc, crtc);
> +	drm_crtc_vblank_on(crtc);
> +}
> +
> +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
> +				   struct drm_atomic_state *state)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	drm_crtc_vblank_off(crtc);
> +
> +	vs_dc_disable(dc, crtc);
> +
> +	if (crtc->state->event && !crtc->state->active) {
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +
> +		crtc->state->event = NULL;

I think even should be cleared within the lock.

> +	}
> +}
> +
> +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
> +				 struct drm_atomic_state *state)
> +{
> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
> +									  crtc);
> +
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct device *dev = vs_crtc->dev;
> +	struct drm_property_blob *blob = crtc->state->gamma_lut;
> +	struct drm_color_lut *lut;
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	if (crtc_state->color_mgmt_changed) {
> +		if (blob && blob->length) {
> +			lut = blob->data;
> +			vs_dc_set_gamma(dc, crtc, lut,
> +					blob->length / sizeof(*lut));
> +			vs_dc_enable_gamma(dc, crtc, true);
> +		} else {
> +			vs_dc_enable_gamma(dc, crtc, false);
> +		}
> +	}
> +}
> +
> +static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
> +				 struct drm_atomic_state *state)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct drm_pending_vblank_event *event = crtc->state->event;
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_commit(dc);
> +
> +	if (event) {
> +		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
> +
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		drm_crtc_arm_vblank_event(crtc, event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +		crtc->state->event = NULL;

I think even should be cleared within the lock.

> +	}
> +}
> +
> +static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
> +	.atomic_check = drm_crtc_helper_atomic_check,
> +	.atomic_enable	= vs_crtc_atomic_enable,
> +	.atomic_disable = vs_crtc_atomic_disable,
> +	.atomic_begin	= vs_crtc_atomic_begin,
> +	.atomic_flush	= vs_crtc_atomic_flush,
> +};
> +
> +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
> +	{ VS_SINGLE_DC,				"single dc mode" },
> +	{ VS_MULTI_DC_PRIMARY,		"primary dc for multi dc mode" },
> +	{ VS_MULTI_DC_SECONDARY,	"secondary dc for multi dc mode" },
> +};
> +
> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
> +			       struct vs_dc_info *info)
> +{
> +	struct vs_crtc *crtc;
> +	int ret;
> +
> +	if (!info)
> +		return NULL;
> +
> +	crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
> +	if (!crtc)
> +		return NULL;
> +
> +	ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
> +					 NULL, NULL, &vs_crtc_funcs,
> +					 info->name ? info->name : NULL);

It might be better to add drmm_crtc_init() helper.

> +	if (ret)
> +		return NULL;
> +
> +	drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
> +
> +	if (info->gamma_size) {
> +		ret = drm_mode_crtc_set_gamma_size(&crtc->base,
> +						   info->gamma_size);
> +		if (ret)
> +			return NULL;
> +
> +		drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
> +					   info->gamma_size);
> +	}
> +
> +	crtc->max_bpc = info->max_bpc;
> +	crtc->color_formats = info->color_formats;
> +	return crtc;
> +}
> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h
> new file mode 100644
> index 000000000..526dd63e5
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.h
> @@ -0,0 +1,43 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_CRTC_H__
> +#define __VS_CRTC_H__
> +
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "vs_type.h"
> +
> +struct vs_crtc_state {
> +	struct drm_crtc_state base;
> +
> +	u32 output_fmt;
> +	u8 encoder_type;
> +	u8 bpp;
> +	bool underflow;
> +};
> +
> +struct vs_crtc {
> +	struct drm_crtc base;
> +	struct device *dev;
> +	unsigned int max_bpc;
> +	unsigned int color_formats;
> +};
> +
> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
> +			       struct vs_dc_info *info);
> +
> +static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc)
> +{
> +	return container_of(crtc, struct vs_crtc, base);
> +}
> +
> +static inline struct vs_crtc_state *
> +to_vs_crtc_state(struct drm_crtc_state *state)
> +{
> +	return container_of(state, struct vs_crtc_state, base);
> +}
> +#endif /* __VS_CRTC_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
> new file mode 100644
> index 000000000..b5ab92d98
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc.c
> @@ -0,0 +1,1002 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/component.h>
> +#include <linux/clk.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/reset.h>
> +
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_blend.h>
> +#include <drm/drm_framebuffer.h>
> +#include <drm/drm_vblank.h>
> +#include <drm/vs_drm.h>
> +
> +#include "vs_dc_hw.h"
> +#include "vs_dc.h"
> +#include "vs_drv.h"
> +
> +static const char * const vout_clocks[] = {
> +	"noc_bus",
> +	"channel0",
> +	"channel1",
> +	"dc_core",
> +	"axi_core",
> +	"ahb",
> +	"hdmi_tx",
> +	"dc_parent",
> +
> +};
> +
> +static const char * const vout_resets[] = {
> +	"axi",
> +	"ahb",
> +	"core",
> +};
> +
> +static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb)
> +{
> +	u8 f = FORMAT_A8R8G8B8;
> +
> +	switch (format) {
> +	case DRM_FORMAT_XRGB4444:
> +	case DRM_FORMAT_RGBX4444:
> +	case DRM_FORMAT_XBGR4444:
> +	case DRM_FORMAT_BGRX4444:
> +		f = FORMAT_X4R4G4B4;
> +		break;
> +	case DRM_FORMAT_ARGB4444:
> +	case DRM_FORMAT_RGBA4444:
> +	case DRM_FORMAT_ABGR4444:
> +	case DRM_FORMAT_BGRA4444:
> +		f = FORMAT_A4R4G4B4;
> +		break;
> +	case DRM_FORMAT_XRGB1555:
> +	case DRM_FORMAT_RGBX5551:
> +	case DRM_FORMAT_XBGR1555:
> +	case DRM_FORMAT_BGRX5551:
> +		f = FORMAT_X1R5G5B5;
> +		break;
> +	case DRM_FORMAT_ARGB1555:
> +	case DRM_FORMAT_RGBA5551:
> +	case DRM_FORMAT_ABGR1555:
> +	case DRM_FORMAT_BGRA5551:
> +		f = FORMAT_A1R5G5B5;
> +		break;
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		f = FORMAT_R5G6B5;
> +		break;
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_RGBX8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_BGRX8888:
> +		f = FORMAT_X8R8G8B8;
> +		break;
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_RGBA8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_BGRA8888:
> +		f = FORMAT_A8R8G8B8;
> +		break;
> +	case DRM_FORMAT_YUYV:
> +	case DRM_FORMAT_YVYU:
> +		f = FORMAT_YUY2;
> +		break;
> +	case DRM_FORMAT_UYVY:
> +	case DRM_FORMAT_VYUY:
> +		f = FORMAT_UYVY;
> +		break;
> +	case DRM_FORMAT_YUV420:
> +	case DRM_FORMAT_YVU420:
> +		f = FORMAT_YV12;
> +		break;
> +	case DRM_FORMAT_NV21:
> +		f = FORMAT_NV12;
> +		break;
> +	case DRM_FORMAT_NV16:
> +	case DRM_FORMAT_NV61:
> +		f = FORMAT_NV16;
> +		break;
> +	case DRM_FORMAT_P010:
> +		f = FORMAT_P010;
> +		break;
> +	case DRM_FORMAT_ARGB2101010:
> +	case DRM_FORMAT_RGBA1010102:
> +	case DRM_FORMAT_ABGR2101010:
> +	case DRM_FORMAT_BGRA1010102:
> +		f = FORMAT_A2R10G10B10;
> +		break;
> +	case DRM_FORMAT_NV12:
> +		f = FORMAT_NV12;
> +		break;
> +	case DRM_FORMAT_YUV444:
> +		f = FORMAT_YUV444;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	fb->format = f;
> +}
> +
> +static inline void update_swizzle(u32 format, struct dc_hw_fb *fb)
> +{
> +	fb->swizzle = SWIZZLE_ARGB;
> +	fb->uv_swizzle = 0;
> +
> +	switch (format) {
> +	case DRM_FORMAT_RGBX4444:
> +	case DRM_FORMAT_RGBA4444:
> +	case DRM_FORMAT_RGBX5551:
> +	case DRM_FORMAT_RGBA5551:
> +	case DRM_FORMAT_RGBX8888:
> +	case DRM_FORMAT_RGBA8888:
> +	case DRM_FORMAT_RGBA1010102:
> +		fb->swizzle = SWIZZLE_RGBA;
> +		break;
> +	case DRM_FORMAT_XBGR4444:
> +	case DRM_FORMAT_ABGR4444:
> +	case DRM_FORMAT_XBGR1555:
> +	case DRM_FORMAT_ABGR1555:
> +	case DRM_FORMAT_BGR565:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_ABGR2101010:
> +		fb->swizzle = SWIZZLE_ABGR;
> +		break;
> +	case DRM_FORMAT_BGRX4444:
> +	case DRM_FORMAT_BGRA4444:
> +	case DRM_FORMAT_BGRX5551:
> +	case DRM_FORMAT_BGRA5551:
> +	case DRM_FORMAT_BGRX8888:
> +	case DRM_FORMAT_BGRA8888:
> +	case DRM_FORMAT_BGRA1010102:
> +		fb->swizzle = SWIZZLE_BGRA;
> +		break;
> +	case DRM_FORMAT_YVYU:
> +	case DRM_FORMAT_VYUY:
> +	case DRM_FORMAT_NV21:
> +	case DRM_FORMAT_NV61:
> +		fb->uv_swizzle = 1;
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +static inline void update_watermark(struct drm_property_blob *watermark,
> +				    struct dc_hw_fb *fb)
> +{
> +	struct drm_vs_watermark *data;
> +
> +	fb->water_mark = 0;
> +
> +	if (watermark) {
> +		data = watermark->data;
> +		fb->water_mark = data->watermark & 0xFFFFF;
> +	}
> +}
> +
> +static inline u8 to_vs_rotation(unsigned int rotation)
> +{
> +	u8 rot;
> +
> +	switch (rotation & DRM_MODE_REFLECT_MASK) {
> +	case DRM_MODE_REFLECT_X:
> +		rot = FLIP_X;
> +		return rot;
> +	case DRM_MODE_REFLECT_Y:
> +		rot = FLIP_Y;
> +		return rot;
> +	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
> +		rot = FLIP_XY;
> +		return rot;
> +	default:
> +		break;
> +	}
> +
> +	switch (rotation & DRM_MODE_ROTATE_MASK) {
> +	case DRM_MODE_ROTATE_0:
> +		rot = ROT_0;
> +		break;
> +	case DRM_MODE_ROTATE_90:
> +		rot = ROT_90;
> +		break;
> +	case DRM_MODE_ROTATE_180:
> +		rot = ROT_180;
> +		break;
> +	case DRM_MODE_ROTATE_270:
> +		rot = ROT_270;
> +		break;
> +	default:
> +		rot = ROT_0;
> +		break;
> +	}
> +
> +	return rot;
> +}
> +
> +static inline u8 to_vs_yuv_color_space(u32 color_space)
> +{
> +	u8 cs;
> +
> +	switch (color_space) {
> +	case DRM_COLOR_YCBCR_BT601:
> +		cs = COLOR_SPACE_601;
> +		break;
> +	case DRM_COLOR_YCBCR_BT709:
> +		cs = COLOR_SPACE_709;
> +		break;
> +	case DRM_COLOR_YCBCR_BT2020:
> +		cs = COLOR_SPACE_2020;
> +		break;
> +	default:
> +		cs = COLOR_SPACE_601;
> +		break;
> +	}
> +
> +	return cs;
> +}
> +
> +static inline u8 to_vs_tile_mode(u64 modifier)
> +{
> +	if (modifier == DRM_FORMAT_MOD_VIVANTE_TILED)
> +		return DC_TILE_MODE4X4;
> +
> +	return (u8)(modifier & DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK);
> +}
> +
> +static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +	u8 panel_num = dc->hw.info->panel_num;
> +	u32 index = drm_crtc_index(crtc);
> +	int i;
> +
> +	for (i = 0; i < panel_num; i++) {
> +		if (index == dc->crtc[i]->base.index)
> +			return i;
> +	}
> +
> +	return 0;
> +}
> +
> +static void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
> +					  unsigned int alignment)
> +{
> +	struct vs_drm_device *priv = to_vs_drm_private(drm_dev);
> +
> +	if (alignment > priv->pitch_alignment)
> +		priv->pitch_alignment = alignment;
> +}
> +
> +static int plda_clk_rst_init(struct device *dev)
> +{
> +	int ret = 0;
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	ret = clk_bulk_prepare_enable(dc->nclks, dc->clk_vout);
> +	if (ret) {
> +		dev_err(dev, "failed to enable clocks\n");
> +		return ret;
> +	}
> +
> +	ret = reset_control_bulk_deassert(dc->nrsts, dc->rst_vout);
> +	return ret;
> +}
> +
> +static void plda_clk_rst_deinit(struct device *dev)
> +{
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	reset_control_bulk_assert(dc->nrsts, dc->rst_vout);
> +	clk_bulk_disable_unprepare(dc->nclks, dc->clk_vout);
> +}
> +
> +static void dc_deinit(struct device *dev)
> +{
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	dc_hw_enable_interrupt(&dc->hw, 0);
> +	dc_hw_deinit(&dc->hw);
> +	plda_clk_rst_deinit(dev);
> +}
> +
> +static int dc_init(struct device *dev)
> +{
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +	int ret;
> +
> +	dc->first_frame = true;
> +
> +	ret = plda_clk_rst_init(dev);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to init dc clk reset: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = dc_hw_init(&dc->hw);
> +	if (ret) {
> +		dev_err(dev, "failed to init DC HW\n");
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +	struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	struct dc_hw_display display;
> +
> +	display.bus_format = crtc_state->output_fmt;
> +	display.h_active = mode->hdisplay;
> +	display.h_total = mode->htotal;
> +	display.h_sync_start = mode->hsync_start;
> +	display.h_sync_end = mode->hsync_end;
> +	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> +		display.h_sync_polarity = true;
> +	else
> +		display.h_sync_polarity = false;
> +
> +	display.v_active = mode->vdisplay;
> +	display.v_total = mode->vtotal;
> +	display.v_sync_start = mode->vsync_start;
> +	display.v_sync_end = mode->vsync_end;
> +
> +	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> +		display.v_sync_polarity = true;
> +	else
> +		display.v_sync_polarity = false;
> +
> +	display.id = to_vs_display_id(dc, crtc);
> +
> +	display.enable = true;
> +
> +	if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) {
> +		dc_hw_set_out(&dc->hw, OUT_DPI, display.id);
> +		clk_set_rate(dc->clk_vout[CLK_VOUT_SOC_PIX].clk, mode->clock * 1000);
> +		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX1].clk,
> +			       dc->clk_vout[CLK_VOUT_SOC_PIX].clk);
> +	} else {
> +		dc_hw_set_out(&dc->hw, OUT_DP, display.id);
> +		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX0].clk,
> +			       dc->clk_vout[CLK_VOUT_HDMI_PIX].clk);
> +	}
> +
> +	dc_hw_setup_display(&dc->hw, &display);
> +}
> +
> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +	struct dc_hw_display display;
> +
> +	display.id = to_vs_display_id(dc, crtc);
> +	display.enable = false;
> +
> +	dc_hw_setup_display(&dc->hw, &display);
> +}
> +
> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> +		     struct drm_color_lut *lut, unsigned int size)
> +{
> +	u16 i, r, g, b;
> +	u8 bits, id;
> +
> +	if (size != dc->hw.info->gamma_size) {
> +		drm_err(crtc->dev, "gamma size does not match!\n");
> +		return;
> +	}
> +
> +	id = to_vs_display_id(dc, crtc);
> +
> +	bits = dc->hw.info->gamma_bits;
> +	for (i = 0; i < size; i++) {
> +		r = drm_color_lut_extract(lut[i].red, bits);
> +		g = drm_color_lut_extract(lut[i].green, bits);
> +		b = drm_color_lut_extract(lut[i].blue, bits);
> +		dc_hw_update_gamma(&dc->hw, id, i, r, g, b);
> +	}
> +}
> +
> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> +			bool enable)
> +{
> +	u8 id;
> +
> +	id = to_vs_display_id(dc, crtc);
> +	dc_hw_enable_gamma(&dc->hw, id, enable);
> +}
> +
> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable)
> +{
> +	dc_hw_enable_interrupt(&dc->hw, enable);
> +}
> +
> +static u32 calc_factor(u32 src, u32 dest)
> +{
> +	u32 factor = 1 << 16;
> +
> +	if (src > 1 && dest > 1)
> +		factor = ((src - 1) << 16) / (dest - 1);
> +
> +	return factor;
> +}
> +
> +static void update_scale(struct drm_plane_state *state, struct dc_hw_roi *roi,
> +			 struct dc_hw_scale *scale)
> +{
> +	int dst_w = drm_rect_width(&state->dst);
> +	int dst_h = drm_rect_height(&state->dst);
> +	int src_w, src_h, temp;
> +
> +	scale->enable = false;
> +
> +	if (roi->enable) {
> +		src_w = roi->width;
> +		src_h = roi->height;
> +	} else {
> +		src_w = drm_rect_width(&state->src) >> 16;
> +		src_h = drm_rect_height(&state->src) >> 16;
> +	}
> +
> +	if (drm_rotation_90_or_270(state->rotation)) {
> +		temp = src_w;
> +		src_w = src_h;
> +		src_h = temp;
> +	}
> +
> +	if (src_w != dst_w) {
> +		scale->scale_factor_x = calc_factor(src_w, dst_w);
> +		scale->enable = true;
> +	} else {
> +		scale->scale_factor_x = 1 << 16;
> +	}
> +	if (src_h != dst_h) {
> +		scale->scale_factor_y = calc_factor(src_h, dst_h);
> +		scale->enable = true;
> +	} else {
> +		scale->scale_factor_y = 1 << 16;
> +	}
> +}
> +
> +static void update_fb(struct vs_plane *plane, u8 display_id,
> +		      struct dc_hw_fb *fb, struct drm_plane_state *state)
> +{
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_framebuffer *drm_fb = state->fb;
> +	struct drm_rect *src = &state->src;
> +
> +	fb->display_id = display_id;
> +	fb->y_address = plane_state->dma_addr[0];
> +	fb->y_stride = drm_fb->pitches[0];
> +	if (drm_fb->format->format == DRM_FORMAT_YVU420) {
> +		fb->u_address = plane_state->dma_addr[2];
> +		fb->v_address = plane_state->dma_addr[1];
> +		fb->u_stride = drm_fb->pitches[2];
> +		fb->v_stride = drm_fb->pitches[1];
> +	} else {
> +		fb->u_address = plane_state->dma_addr[1];
> +		fb->v_address = plane_state->dma_addr[2];
> +		fb->u_stride = drm_fb->pitches[1];
> +		fb->v_stride = drm_fb->pitches[2];
> +	}
> +	fb->width = drm_rect_width(src) >> 16;
> +	fb->height = drm_rect_height(src) >> 16;
> +	fb->tile_mode = to_vs_tile_mode(drm_fb->modifier);
> +	fb->rotation = to_vs_rotation(state->rotation);
> +	fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding);
> +	fb->zpos = state->zpos;
> +	fb->enable = state->visible;
> +	update_format(drm_fb->format->format, drm_fb->modifier, fb);
> +	update_swizzle(drm_fb->format->format, fb);
> +	update_watermark(plane_state->watermark, fb);
> +	plane_state->status.tile_mode = fb->tile_mode;
> +}
> +
> +static void update_degamma(struct vs_dc *dc, struct vs_plane *plane,
> +			   struct vs_plane_state *plane_state)
> +{
> +	dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
> +	plane_state->degamma_changed = false;
> +}
> +
> +static void update_roi(struct vs_dc *dc, u8 id,
> +		       struct vs_plane_state *plane_state,
> +		       struct dc_hw_roi *roi,
> +		       struct drm_plane_state *state)
> +{
> +	struct drm_vs_roi *data;
> +	struct drm_rect *src = &state->src;
> +	u16 src_w = drm_rect_width(src) >> 16;
> +	u16 src_h = drm_rect_height(src) >> 16;
> +
> +	if (plane_state->roi) {
> +		data = plane_state->roi->data;
> +
> +		if (data->enable) {
> +			roi->x = data->roi_x;
> +			roi->y = data->roi_y;
> +			roi->width = (data->roi_x + data->roi_w > src_w) ?
> +						 (src_w - data->roi_x) : data->roi_w;
> +			roi->height = (data->roi_y + data->roi_h > src_h) ?
> +						  (src_h - data->roi_y) : data->roi_h;
> +			roi->enable = true;
> +		} else {
> +			roi->enable = false;
> +		}
> +
> +		dc_hw_update_roi(&dc->hw, id, roi);
> +	} else {
> +		roi->enable = false;
> +	}
> +}
> +
> +static void update_color_mgmt(struct vs_dc *dc, u8 id,
> +			      struct dc_hw_fb *fb,
> +			      struct vs_plane_state *plane_state)
> +{
> +	struct drm_vs_color_mgmt *data;
> +	struct dc_hw_colorkey colorkey;
> +
> +	if (plane_state->color_mgmt) {
> +		data = plane_state->color_mgmt->data;
> +
> +		fb->clear_enable = data->clear_enable;
> +		fb->clear_value = data->clear_value;
> +
> +		if (data->colorkey > data->colorkey_high)
> +			data->colorkey = data->colorkey_high;
> +
> +		colorkey.colorkey = data->colorkey;
> +		colorkey.colorkey_high = data->colorkey_high;
> +		colorkey.transparency = (data->transparency) ?
> +				DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE;
> +		dc_hw_update_colorkey(&dc->hw, id, &colorkey);
> +	}
> +}
> +
> +static void update_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			 struct drm_plane *drm_plane,
> +			 struct drm_atomic_state *drm_state)
> +{
> +	struct dc_hw_fb fb = {0};
> +	struct dc_hw_scale scale;
> +	struct dc_hw_position pos;
> +	struct dc_hw_blend blend;
> +	struct dc_hw_roi roi;
> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> +									   drm_plane);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_rect *dest = &state->dst;
> +	bool dec_enable = false;
> +	u8 display_id = 0;
> +
> +	display_id = to_vs_display_id(dc, state->crtc);
> +	update_fb(plane, display_id, &fb, state);
> +	fb.dec_enable = dec_enable;
> +
> +	update_roi(dc, plane->id, plane_state, &roi, state);
> +
> +	update_scale(state, &roi, &scale);
> +
> +	if (plane_state->degamma_changed)
> +		update_degamma(dc, plane, plane_state);
> +
> +	pos.start_x = dest->x1;
> +	pos.start_y = dest->y1;
> +	pos.end_x = dest->x2;
> +	pos.end_y = dest->y2;
> +
> +	blend.alpha = (u8)(state->alpha >> 8);
> +	blend.blend_mode = (u8)(state->pixel_blend_mode);
> +
> +	update_color_mgmt(dc, plane->id, &fb, plane_state);
> +
> +	dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
> +}
> +
> +static void update_qos(struct vs_dc *dc, struct vs_plane *plane,
> +		       struct drm_plane *drm_plane,
> +		       struct drm_atomic_state *drm_state)
> +{
> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> +									   drm_plane);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_vs_watermark *data;
> +	struct dc_hw_qos qos;
> +
> +	if (plane_state->watermark) {
> +		data = plane_state->watermark->data;
> +
> +		if (data->qos_high) {
> +			if (data->qos_low > data->qos_high)
> +				data->qos_low = data->qos_high;
> +
> +			qos.low_value = data->qos_low & 0x0F;
> +			qos.high_value = data->qos_high & 0x0F;
> +			dc_hw_update_qos(&dc->hw, &qos);
> +		}
> +	}
> +}
> +
> +static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor)
> +{
> +	u8 size_type;
> +
> +	switch (state->crtc_w) {
> +	case 32:
> +		size_type = CURSOR_SIZE_32X32;
> +		break;
> +	case 64:
> +		size_type = CURSOR_SIZE_64X64;
> +		break;
> +	default:
> +		size_type = CURSOR_SIZE_32X32;
> +		break;
> +	}
> +
> +	cursor->size = size_type;
> +}
> +
> +static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +				struct drm_plane *drm_plane,
> +				struct drm_atomic_state *drm_state)
> +{
> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> +								       drm_plane);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_framebuffer *drm_fb = state->fb;
> +	struct dc_hw_cursor cursor;
> +
> +	cursor.address = plane_state->dma_addr[0];
> +	cursor.x = state->crtc_x;
> +	cursor.y = state->crtc_y;
> +	cursor.hot_x = drm_fb->hot_x;
> +	cursor.hot_y = drm_fb->hot_y;
> +	cursor.display_id = to_vs_display_id(dc, state->crtc);
> +	update_cursor_size(state, &cursor);
> +	cursor.enable = true;
> +
> +	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
> +}
> +
> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			struct drm_plane *drm_plane,
> +			struct drm_atomic_state *drm_state)
> +{
> +	update_plane(dc, plane, drm_plane, drm_state);
> +	update_qos(dc, plane, drm_plane, drm_state);
> +}
> +
> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			       struct drm_plane *drm_plane,
> +			       struct drm_atomic_state *drm_state)
> +{
> +	update_cursor_plane(dc, plane, drm_plane, drm_state);
> +}
> +
> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			 struct drm_plane_state *old_state)
> +{
> +	struct dc_hw_fb fb = {0};
> +
> +	fb.enable = false;
> +	dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL, NULL, NULL);
> +}
> +
> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +				struct drm_plane_state *old_state)
> +{
> +	struct dc_hw_cursor cursor = {0};
> +
> +	cursor.enable = false;
> +	cursor.display_id = to_vs_display_id(dc, old_state->crtc);
> +	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
> +}
> +
> +static bool vs_dc_mod_supported(const struct vs_plane_info *plane_info,
> +				u64 modifier)
> +{
> +	const u64 *mods;
> +
> +	if (!plane_info->modifiers)
> +		return false;
> +
> +	for (mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID; mods++) {
> +		if (*mods == modifier)
> +			return true;
> +	}
> +
> +	return false;
> +}
> +
> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
> +		      struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> +										 plane);
> +
> +	struct drm_framebuffer *fb = new_plane_state->fb;
> +	const struct vs_plane_info *plane_info;
> +	struct drm_crtc *crtc = new_plane_state->crtc;
> +	struct drm_crtc_state *crtc_state;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +
> +	plane_info = &dc->hw.info->planes[vs_plane->id];
> +
> +	if (fb->width < plane_info->min_width ||
> +	    fb->width > plane_info->max_width ||
> +	    fb->height < plane_info->min_height ||
> +	    fb->height > plane_info->max_height)
> +		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n",
> +			     vs_plane->id);
> +
> +	if (!vs_dc_mod_supported(plane_info, fb->modifier)) {
> +		drm_err(plane->dev, "unsupported modifier on plane%d.\n", vs_plane->id);
> +		return -EINVAL;
> +	}
> +
> +	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
> +	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
> +						  plane_info->min_scale,
> +						  plane_info->max_scale,
> +						  true, true);
> +}
> +
> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
> +			     struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> +									   plane);
> +	struct drm_framebuffer *fb = new_plane_state->fb;
> +	const struct vs_plane_info *plane_info;
> +	struct drm_crtc *crtc = new_plane_state->crtc;
> +	struct drm_crtc_state *crtc_state;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +
> +	plane_info = &dc->hw.info->planes[vs_plane->id];
> +
> +	if (fb->width < plane_info->min_width ||
> +	    fb->width > plane_info->max_width ||
> +	    fb->height < plane_info->min_height ||
> +	    fb->height > plane_info->max_height)
> +		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n", vs_plane->id);
> +
> +	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
> +	if (IS_ERR(crtc_state))
> +		return -EINVAL;
> +
> +	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
> +						plane_info->min_scale,
> +						plane_info->max_scale,
> +						true, true);
> +}
> +
> +static void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
> +{
> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
> +
> +	drm_crtc_handle_vblank(crtc);
> +
> +	vs_crtc_state->underflow = underflow;
> +}
> +
> +static irqreturn_t dc_isr(int irq, void *data)
> +{
> +	struct vs_dc *dc = data;
> +	struct vs_dc_info *dc_info = dc->hw.info;
> +	u32 i, ret;
> +
> +	ret = dc_hw_get_interrupt(&dc->hw);
> +
> +	for (i = 0; i < dc_info->panel_num; i++)
> +		vs_crtc_handle_vblank(&dc->crtc[i]->base, dc_hw_check_underflow(&dc->hw));
> +
> +	return IRQ_HANDLED;
> +}
> +
> +void vs_dc_commit(struct vs_dc *dc)
> +{
> +	dc_hw_enable_shadow_register(&dc->hw, false);
> +
> +	dc_hw_commit(&dc->hw);
> +
> +	if (dc->first_frame)
> +		dc->first_frame = false;
> +
> +	dc_hw_enable_shadow_register(&dc->hw, true);
> +}
> +
> +static int dc_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct drm_device *drm_dev = data;
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +	struct device_node *port;
> +	struct vs_crtc *crtc;
> +	struct vs_dc_info *dc_info;
> +	struct vs_plane *plane;
> +	struct vs_plane_info *plane_info;
> +	int i, ret;
> +	u32 ctrc_mask = 0;
> +
> +	if (!drm_dev || !dc) {
> +		dev_err(dev, "devices are not created.\n");
> +		return -ENODEV;
> +	}
> +
> +	ret = dc_init(dev);
> +	if (ret < 0) {
> +		drm_err(drm_dev, "Failed to initialize DC hardware.\n");
> +		return ret;
> +	}
> +
> +	port = of_get_child_by_name(dev->of_node, "port");
> +	if (!port) {
> +		drm_err(drm_dev, "no port node found\n");
> +		return -ENODEV;
> +	}
> +	of_node_put(port);
> +
> +	dc_info = dc->hw.info;
> +
> +	for (i = 0; i < dc_info->panel_num; i++) {
> +		crtc = vs_crtc_create(drm_dev, dc_info);
> +		if (!crtc) {
> +			drm_err(drm_dev, "Failed to create CRTC.\n");
> +			ret = -ENOMEM;
> +			return ret;
> +		}
> +
> +		crtc->base.port = port;
> +		crtc->dev = dev;
> +		dc->crtc[i] = crtc;
> +		ctrc_mask |= drm_crtc_mask(&crtc->base);
> +	}
> +
> +	for (i = 0; i < dc_info->plane_num; i++) {
> +		plane_info = (struct vs_plane_info *)&dc_info->planes[i];
> +
> +		if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
> +						drm_crtc_mask(&dc->crtc[0]->base));
> +		} else if (!strcmp(plane_info->name, "Primary_1") ||
> +				   !strcmp(plane_info->name, "Cursor_1")) {
> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
> +						drm_crtc_mask(&dc->crtc[1]->base));

I'd say, it's not typical to use name to distinguish objects. Maybe you 
can add indices to the info structure and drop the names instead?


> +		} else {
> +			plane = vs_plane_create(drm_dev, plane_info,
> +						dc_info->layer_num, ctrc_mask);
> +		}
> +
> +		if (IS_ERR(plane)) {
> +			dev_err(dev, "failed to construct plane\n");
> +			return PTR_ERR(plane);
> +		}
> +
> +		plane->id = i;
> +		dc->planes[i].id = plane_info->id;
> +
> +		if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
> +			if (!strcmp(plane_info->name, "Primary"))
> +				dc->crtc[0]->base.primary = &plane->base;
> +			else
> +				dc->crtc[1]->base.primary = &plane->base;

This definitely urges for an index in the plane info / plane_state.

> +			drm_dev->mode_config.min_width = plane_info->min_width;
> +			drm_dev->mode_config.min_height =
> +							plane_info->min_height;
> +			drm_dev->mode_config.max_width = plane_info->max_width;
> +			drm_dev->mode_config.max_height =
> +							plane_info->max_height;
> +		}
> +
> +		if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
> +			if (!strcmp(plane_info->name, "Cursor"))
> +				dc->crtc[0]->base.cursor = &plane->base;
> +			else
> +				dc->crtc[1]->base.cursor = &plane->base;
> +			drm_dev->mode_config.cursor_width =
> +							plane_info->max_width;
> +			drm_dev->mode_config.cursor_height =
> +							plane_info->max_height;
> +		}
> +	}
> +
> +	vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
> +	return 0;
> +}
> +
> +static void dc_unbind(struct device *dev, struct device *master, void *data)
> +{
> +	dc_deinit(dev);
> +}
> +
> +const struct component_ops dc_component_ops = {
> +	.bind = dc_bind,
> +	.unbind = dc_unbind,
> +};
> +
> +static const struct of_device_id dc_driver_dt_match[] = {
> +	{ .compatible = "starfive,jh7110-dc8200", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
> +
> +static int dc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct vs_dc *dc;
> +	int irq, ret, i;
> +
> +	dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
> +	if (!dc)
> +		return -ENOMEM;
> +
> +	dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(dc->hw.hi_base))
> +		return PTR_ERR(dc->hw.hi_base);
> +
> +	dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
> +	if (IS_ERR(dc->hw.reg_base))
> +		return PTR_ERR(dc->hw.reg_base);
> +
> +	dc->nclks = ARRAY_SIZE(dc->clk_vout);
> +	for (i = 0; i < dc->nclks; ++i)
> +		dc->clk_vout[i].id = vout_clocks[i];
> +	ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
> +	if (ret) {
> +		dev_err(dev, "Failed to get clk controls\n");
> +		return ret;
> +	}
> +
> +	dc->nrsts = ARRAY_SIZE(dc->rst_vout);
> +	for (i = 0; i < dc->nrsts; ++i)
> +		dc->rst_vout[i].id = vout_resets[i];
> +	ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
> +						 dc->rst_vout);
> +	if (ret) {
> +		dev_err(dev, "Failed to get reset controls\n");
> +		return ret;
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +
> +	ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);

Are you ready to handle the IRQ at this point?

> +	if (ret < 0) {
> +		dev_err(dev, "Failed to install irq:%u.\n", irq);
> +		return ret;
> +	}
> +
> +	dev_set_drvdata(dev, dc);
> +
> +	return component_add(dev, &dc_component_ops);
> +}
> +
> +static int dc_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +
> +	component_del(dev, &dc_component_ops);
> +
> +	dev_set_drvdata(dev, NULL);
> +
> +	return 0;
> +}
> +
> +struct platform_driver dc_platform_driver = {
> +	.probe = dc_probe,
> +	.remove = dc_remove,
> +	.driver = {
> +		.name = "vs-dc",
> +		.of_match_table = of_match_ptr(dc_driver_dt_match),
> +	},
> +};
> +
> +MODULE_AUTHOR("StarFive Corporation");
> +MODULE_DESCRIPTION("VeriSilicon DC Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
> new file mode 100644
> index 000000000..8e96fd32c
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc.h
> @@ -0,0 +1,80 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DC_H__
> +#define __VS_DC_H__
> +
> +#include <linux/clk.h>
> +#include <linux/mm_types.h>
> +#include <linux/reset.h>
> +
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_modes.h>
> +
> +#include "vs_crtc.h"
> +#include "vs_dc_hw.h"
> +#include "vs_plane.h"
> +
> +struct vs_dc_plane {
> +	enum dc_hw_plane_id id;
> +};
> +
> +enum vout_clk {
> +	CLK_VOUT_NOC_DISP = 0,
> +	CLK_VOUT_PIX0,
> +	CLK_VOUT_PIX1,
> +	CLK_VOUT_CORE,
> +	CLK_VOUT_AXI,
> +	CLK_VOUT_AHB,
> +	CLK_VOUT_HDMI_PIX,
> +	CLK_VOUT_SOC_PIX,
> +	CLK_VOUT_NUM
> +};
> +
> +enum rst_vout {
> +	RST_VOUT_AXI = 0,
> +	RST_VOUT_AHB,
> +	RST_VOUT_CORE,
> +	RST_VOUT_NUM
> +};
> +
> +struct vs_dc {
> +	struct vs_crtc		*crtc[DC_DISPLAY_NUM];
> +	struct dc_hw		hw;
> +	bool			first_frame;
> +
> +	struct vs_dc_plane	planes[PLANE_NUM];
> +	struct clk_bulk_data	clk_vout[CLK_VOUT_NUM];
> +	int			nclks;
> +	struct reset_control_bulk_data rst_vout[RST_VOUT_NUM];
> +	int			nrsts;
> +};
> +
> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc);
> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc);
> +
> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> +		     struct drm_color_lut *lut, unsigned int size);
> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc, bool enable);
> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable);
> +void vs_dc_commit(struct vs_dc *dc);
> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			struct drm_plane *drm_plane,
> +			struct drm_atomic_state *drm_state);
> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			 struct drm_plane_state *old_state);
> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
> +		      struct drm_atomic_state *state);
> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			       struct drm_plane *drm_plane,
> +			       struct drm_atomic_state *drm_state);
> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +				struct drm_plane_state *old_state);
> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
> +			     struct drm_atomic_state *state);
> +
> +extern struct platform_driver dc_platform_driver;
> +
> +#endif /* __VS_DC_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
> new file mode 100644
> index 000000000..e5acb7eb4
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
> @@ -0,0 +1,1959 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/bits.h>
> +#include <linux/io.h>
> +#include <linux/media-bus-format.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_blend.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/vs_drm.h>
> +
> +#include "vs_dc_hw.h"
> +#include "vs_type.h"
> +
> +static const u32 horkernel[] = {
> +	0x00000000, 0x20000000, 0x00002000, 0x00000000,
> +	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
> +	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
> +	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
> +	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
> +	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
> +	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
> +	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
> +	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
> +	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
> +	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
> +	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
> +	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
> +	0x00000000, 0x00000000, 0x00000000, 0x01470000,
> +	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
> +	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
> +	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
> +	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
> +	0x00000000, 0x00000000, 0x00004000, 0x00000000,
> +	0x00000000, 0x00000000, 0x20002000, 0x00000000,
> +	0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
> +	0x00000000, 0x00000000, 0x00000000, 0x27e1181f,
> +	0x00000000, 0x00000000, 0x00000000, 0x14680000,
> +	0x00002b98, 0x00000000, 0x00000000, 0x00000000,
> +	0x2f1010f0, 0x00000000, 0x00000000, 0x00000000,
> +	0x0dc70000, 0x00003239, 0x00000000, 0x00000000,
> +	0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
> +	0x00000000, 0x087f0000, 0x00003781, 0x00000000,
> +	0x00000000, 0x00000000, 0x399a0666, 0x00000000,
> +	0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
> +	0x00000000, 0x00000000, 0x00000000, 0x3cc4033c,
> +	0x00000000, 0x00000000, 0x00000000, 0x021f0000,
> +};
> +
> +#define H_COEF_SIZE (sizeof(horkernel) / sizeof(u32))
> +
> +static const u32 verkernel[] = {
> +	0x00000000, 0x20000000, 0x00002000, 0x00000000,
> +	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
> +	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
> +	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
> +	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
> +	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
> +	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
> +	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
> +	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
> +	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
> +	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
> +	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
> +	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
> +	0x00000000, 0x00000000, 0x00000000, 0x01470000,
> +	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
> +	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
> +	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
> +	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
> +	0x00000000, 0x00000000, 0x00004000, 0x00000000,
> +	0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab,
> +	0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
> +	0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +};
> +
> +#define V_COEF_SIZE (sizeof(verkernel) / sizeof(u32))
> +
> +/*
> + * RGB 709->2020 conversion parameters
> + */
> +static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = {
> +	10279,	5395,	709,
> +	1132,	15065,	187,
> +	269,	1442,	14674
> +};
> +
> +/*
> + * YUV601 to RGB conversion parameters
> + * YUV2RGB[0]  - [8] : C0 - C8;
> + * YUV2RGB[9]  - [11]: D0 - D2;
> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> + */
> +static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> +	1196,	0,			1640,	1196,
> +	-404,	-836,		1196,	2076,
> +	0,		-916224,	558336,	-1202944,
> +	64,		 940,		64,		960
> +};
> +
> +/*
> + * YUV709 to RGB conversion parameters
> + * YUV2RGB[0]  - [8] : C0 - C8;
> + * YUV2RGB[9]  - [11]: D0 - D2;
> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> + */
> +static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> +	1196,		0,		1844,	1196,
> +	-220,		-548,	1196,	2172,
> +	0,			-1020672, 316672,  -1188608,
> +	64,			940,		64,		960
> +};
> +
> +/*
> + * YUV2020 to RGB conversion parameters
> + * YUV2RGB[0]  - [8] : C0 - C8;
> + * YUV2RGB[9]  - [11]: D0 - D2;
> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> + */
> +static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> +	1196, 0, 1724, 1196,
> +	-192, -668, 1196, 2200,
> +	0, -959232, 363776, -1202944,
> +	64, 940, 64, 960
> +};
> +
> +/*
> + * RGB to YUV2020 conversion parameters
> + * RGB2YUV[0] - [8] : C0 - C8;
> + * RGB2YUV[9] - [11]: D0 - D2;
> + */
> +static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = {
> +	230,	594,	52,
> +	-125,  -323,	448,
> +	448,   -412,   -36,
> +	64,		512,	512
> +};
> +
> +/*
> + * Degamma table for 709 color space data.
> + */
> +static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005,
> +	0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024,
> +	0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064,
> +	0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce,
> +	0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166,
> +	0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230,
> +	0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331,
> +	0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c,
> +	0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
> +	0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d,
> +	0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999,
> +	0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb,
> +	0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65,
> +	0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a,
> +	0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b,
> +	0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc,
> +	0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e,
> +	0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
> +	0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e,
> +	0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf,
> +	0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea,
> +	0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f,
> +	0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930,
> +	0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f,
> +	0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee,
> +	0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde,
> +	0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
> +	0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9,
> +	0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406,
> +	0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a,
> +	0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576,
> +	0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd,
> +	0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
> +};
> +
> +/*
> + * Degamma table for 2020 color space data.
> + */
> +static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> +	0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
> +	0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
> +	0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006,
> +	0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a,
> +	0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f,
> +	0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
> +	0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024,
> +	0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035,
> +	0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d,
> +	0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d,
> +	0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098,
> +	0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3,
> +	0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123,
> +	0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d,
> +	0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
> +	0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6,
> +	0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1,
> +	0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d,
> +	0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6,
> +	0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920,
> +	0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a,
> +	0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033,
> +	0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593,
> +	0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
> +	0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656,
> +	0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332,
> +	0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480,
> +	0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1,
> +	0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99,
> +	0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
> +};
> +
> +/* one is for primary plane and the other is for all overlay planes */
> +static const struct dc_hw_plane_reg dc_plane_reg[] = {
> +	{
> +	.y_address		= DC_FRAMEBUFFER_ADDRESS,
> +	.u_address		= DC_FRAMEBUFFER_U_ADDRESS,
> +	.v_address		= DC_FRAMEBUFFER_V_ADDRESS,
> +	.y_stride		= DC_FRAMEBUFFER_STRIDE,
> +	.u_stride		= DC_FRAMEBUFFER_U_STRIDE,
> +	.v_stride		= DC_FRAMEBUFFER_V_STRIDE,
> +	.size			= DC_FRAMEBUFFER_SIZE,
> +	.top_left		= DC_FRAMEBUFFER_TOP_LEFT,
> +	.bottom_right	= DC_FRAMEBUFFER_BOTTOM_RIGHT,
> +	.scale_factor_x			= DC_FRAMEBUFFER_SCALE_FACTOR_X,
> +	.scale_factor_y			= DC_FRAMEBUFFER_SCALE_FACTOR_Y,
> +	.h_filter_coef_index	= DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
> +	.h_filter_coef_data		= DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
> +	.v_filter_coef_index	= DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
> +	.v_filter_coef_data		= DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
> +	.init_offset			= DC_FRAMEBUFFER_INIT_OFFSET,
> +	.color_key				= DC_FRAMEBUFFER_COLOR_KEY,
> +	.color_key_high			= DC_FRAMEBUFFER_COLOR_KEY_HIGH,
> +	.clear_value			= DC_FRAMEBUFFER_CLEAR_VALUE,
> +	.color_table_index		= DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
> +	.color_table_data		= DC_FRAMEBUFFER_COLOR_TABLE_DATA,
> +	.scale_config			= DC_FRAMEBUFFER_SCALE_CONFIG,
> +	.water_mark				= DC_FRAMEBUFFER_WATER_MARK,
> +	.degamma_index			= DC_FRAMEBUFFER_DEGAMMA_INDEX,
> +	.degamma_data			= DC_FRAMEBUFFER_DEGAMMA_DATA,
> +	.degamma_ex_data		= DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
> +	.src_global_color		= DC_FRAMEBUFFER_SRC_GLOBAL_COLOR,
> +	.dst_global_color		= DC_FRAMEBUFFER_DST_GLOBAL_COLOR,
> +	.blend_config			= DC_FRAMEBUFFER_BLEND_CONFIG,
> +	.roi_origin				= DC_FRAMEBUFFER_ROI_ORIGIN,
> +	.roi_size				= DC_FRAMEBUFFER_ROI_SIZE,
> +	.yuv_to_rgb_coef0			= DC_FRAMEBUFFER_YUVTORGB_COEF0,
> +	.yuv_to_rgb_coef1			= DC_FRAMEBUFFER_YUVTORGB_COEF1,
> +	.yuv_to_rgb_coef2			= DC_FRAMEBUFFER_YUVTORGB_COEF2,
> +	.yuv_to_rgb_coef3			= DC_FRAMEBUFFER_YUVTORGB_COEF3,
> +	.yuv_to_rgb_coef4			= DC_FRAMEBUFFER_YUVTORGB_COEF4,
> +	.yuv_to_rgb_coefd0			= DC_FRAMEBUFFER_YUVTORGB_COEFD0,
> +	.yuv_to_rgb_coefd1			= DC_FRAMEBUFFER_YUVTORGB_COEFD1,
> +	.yuv_to_rgb_coefd2			= DC_FRAMEBUFFER_YUVTORGB_COEFD2,
> +	.y_clamp_bound				= DC_FRAMEBUFFER_Y_CLAMP_BOUND,
> +	.uv_clamp_bound				= DC_FRAMEBUFFER_UV_CLAMP_BOUND,
> +	.rgb_to_rgb_coef0			= DC_FRAMEBUFFER_RGBTORGB_COEF0,
> +	.rgb_to_rgb_coef1			= DC_FRAMEBUFFER_RGBTORGB_COEF1,
> +	.rgb_to_rgb_coef2			= DC_FRAMEBUFFER_RGBTORGB_COEF2,
> +	.rgb_to_rgb_coef3			= DC_FRAMEBUFFER_RGBTORGB_COEF3,
> +	.rgb_to_rgb_coef4			= DC_FRAMEBUFFER_RGBTORGB_COEF4,
> +	},
> +	{
> +	.y_address		= DC_OVERLAY_ADDRESS,
> +	.u_address		= DC_OVERLAY_U_ADDRESS,
> +	.v_address		= DC_OVERLAY_V_ADDRESS,
> +	.y_stride		= DC_OVERLAY_STRIDE,
> +	.u_stride		= DC_OVERLAY_U_STRIDE,
> +	.v_stride		= DC_OVERLAY_V_STRIDE,
> +	.size			= DC_OVERLAY_SIZE,
> +	.top_left		= DC_OVERLAY_TOP_LEFT,
> +	.bottom_right	= DC_OVERLAY_BOTTOM_RIGHT,
> +	.scale_factor_x	= DC_OVERLAY_SCALE_FACTOR_X,
> +	.scale_factor_y	= DC_OVERLAY_SCALE_FACTOR_Y,
> +	.h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
> +	.h_filter_coef_data  = DC_OVERLAY_H_FILTER_COEF_DATA,
> +	.v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
> +	.v_filter_coef_data  = DC_OVERLAY_V_FILTER_COEF_DATA,
> +	.init_offset		 = DC_OVERLAY_INIT_OFFSET,
> +	.color_key			 = DC_OVERLAY_COLOR_KEY,
> +	.color_key_high			= DC_OVERLAY_COLOR_KEY_HIGH,
> +	.clear_value		 = DC_OVERLAY_CLEAR_VALUE,
> +	.color_table_index	 = DC_OVERLAY_COLOR_TABLE_INDEX,
> +	.color_table_data	 = DC_OVERLAY_COLOR_TABLE_DATA,
> +	.scale_config		 = DC_OVERLAY_SCALE_CONFIG,
> +	.water_mark				= DC_OVERLAY_WATER_MARK,
> +	.degamma_index		 = DC_OVERLAY_DEGAMMA_INDEX,
> +	.degamma_data		 = DC_OVERLAY_DEGAMMA_DATA,
> +	.degamma_ex_data	 = DC_OVERLAY_DEGAMMA_EX_DATA,
> +	.src_global_color	 = DC_OVERLAY_SRC_GLOBAL_COLOR,
> +	.dst_global_color	 = DC_OVERLAY_DST_GLOBAL_COLOR,
> +	.blend_config		 = DC_OVERLAY_BLEND_CONFIG,
> +	.roi_origin				= DC_OVERLAY_ROI_ORIGIN,
> +	.roi_size				= DC_OVERLAY_ROI_SIZE,
> +	.yuv_to_rgb_coef0		 = DC_OVERLAY_YUVTORGB_COEF0,
> +	.yuv_to_rgb_coef1		 = DC_OVERLAY_YUVTORGB_COEF1,
> +	.yuv_to_rgb_coef2		 = DC_OVERLAY_YUVTORGB_COEF2,
> +	.yuv_to_rgb_coef3		 = DC_OVERLAY_YUVTORGB_COEF3,
> +	.yuv_to_rgb_coef4			= DC_OVERLAY_YUVTORGB_COEF4,
> +	.yuv_to_rgb_coefd0			= DC_OVERLAY_YUVTORGB_COEFD0,
> +	.yuv_to_rgb_coefd1			= DC_OVERLAY_YUVTORGB_COEFD1,
> +	.yuv_to_rgb_coefd2			= DC_OVERLAY_YUVTORGB_COEFD2,
> +	.y_clamp_bound		 = DC_OVERLAY_Y_CLAMP_BOUND,
> +	.uv_clamp_bound		 = DC_OVERLAY_UV_CLAMP_BOUND,
> +	.rgb_to_rgb_coef0		 = DC_OVERLAY_RGBTORGB_COEF0,
> +	.rgb_to_rgb_coef1		 = DC_OVERLAY_RGBTORGB_COEF1,
> +	.rgb_to_rgb_coef2		 = DC_OVERLAY_RGBTORGB_COEF2,
> +	.rgb_to_rgb_coef3		 = DC_OVERLAY_RGBTORGB_COEF3,
> +	.rgb_to_rgb_coef4		 = DC_OVERLAY_RGBTORGB_COEF4,
> +	},
> +};
> +
> +static const u32 primary_overlay_format0[] = {
> +	DRM_FORMAT_XRGB4444,
> +	DRM_FORMAT_XBGR4444,
> +	DRM_FORMAT_RGBX4444,
> +	DRM_FORMAT_BGRX4444,
> +	DRM_FORMAT_ARGB4444,
> +	DRM_FORMAT_ABGR4444,
> +	DRM_FORMAT_RGBA4444,
> +	DRM_FORMAT_BGRA4444,
> +	DRM_FORMAT_XRGB1555,
> +	DRM_FORMAT_XBGR1555,
> +	DRM_FORMAT_RGBX5551,
> +	DRM_FORMAT_BGRX5551,
> +	DRM_FORMAT_ARGB1555,
> +	DRM_FORMAT_ABGR1555,
> +	DRM_FORMAT_RGBA5551,
> +	DRM_FORMAT_BGRA5551,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_RGBX8888,
> +	DRM_FORMAT_BGRX8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGBA8888,
> +	DRM_FORMAT_BGRA8888,
> +	DRM_FORMAT_ARGB2101010,
> +	DRM_FORMAT_ABGR2101010,
> +	DRM_FORMAT_RGBA1010102,
> +	DRM_FORMAT_BGRA1010102,
> +	DRM_FORMAT_YUYV,
> +	DRM_FORMAT_YVYU,
> +	DRM_FORMAT_UYVY,
> +	DRM_FORMAT_VYUY,
> +	DRM_FORMAT_YVU420,
> +	DRM_FORMAT_YUV420,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV21,
> +	DRM_FORMAT_NV16,
> +	DRM_FORMAT_NV61,
> +	DRM_FORMAT_P010,
> +};
> +
> +static const u32 primary_overlay_format1[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_RGBX8888,
> +	DRM_FORMAT_BGRX8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGBA8888,
> +	DRM_FORMAT_BGRA8888,
> +	DRM_FORMAT_ARGB2101010,
> +	DRM_FORMAT_ABGR2101010,
> +	DRM_FORMAT_RGBA1010102,
> +	DRM_FORMAT_BGRA1010102,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV21,
> +	DRM_FORMAT_YUV444,
> +};
> +
> +static const u32 cursor_formats[] = {
> +	DRM_FORMAT_ARGB8888
> +};
> +
> +static const u64 format_modifier0[] = {
> +	DRM_FORMAT_MOD_LINEAR,
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X8),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X4),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8),
> +	DRM_FORMAT_MOD_INVALID
> +};
> +
> +static const u64 secondary_format_modifiers[] = {
> +	DRM_FORMAT_MOD_LINEAR,
> +	DRM_FORMAT_MOD_INVALID
> +};
> +
> +#define FRAC_16_16(mult, div)	 (((mult) << 16) / (div))
> +
> +static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = {
> +	{
> +		/* DC_REV_0 */
> +		{
> +		.name			= "Primary",

I'd say that the indentation is strange here.
It might be more scalable to split a single array into per-generation 
data structures and reference them directly from the info struct.

> +		.id				= PRIMARY_PLANE_0,
> +		.type			= DRM_PLANE_TYPE_PRIMARY,
> +		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
> +		.formats		= primary_overlay_format0,
> +		.num_modifiers	= ARRAY_SIZE(format_modifier0),
> +		.modifiers		= format_modifier0,
> +		.min_width		= 0,
> +		.min_height		= 0,
> +		.max_width		= 4096,
> +		.max_height		= 4096,
> +		.rotation		= DRM_MODE_ROTATE_0 |
> +							DRM_MODE_ROTATE_90 |
> +							DRM_MODE_ROTATE_180 |
> +							DRM_MODE_ROTATE_270 |
> +							DRM_MODE_REFLECT_X |
> +							DRM_MODE_REFLECT_Y,
> +		.blend_mode		= BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +							BIT(DRM_MODE_BLEND_PREMULTI) |
> +							BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 0,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name			= "Overlay",
> +		.id				= OVERLAY_PLANE_0,
> +		.type			= DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
> +		.formats		= primary_overlay_format0,
> +		.num_modifiers	= ARRAY_SIZE(format_modifier0),
> +		.modifiers		= format_modifier0,
> +		.min_width		= 0,
> +		.min_height		= 0,
> +		.max_width		= 4096,
> +		.max_height		= 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 1,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name			= "Overlay_1",
> +		.id				= OVERLAY_PLANE_1,
> +		.type			= DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
> +		.formats		= primary_overlay_format0,
> +		.num_modifiers	= ARRAY_SIZE(secondary_format_modifiers),
> +		.modifiers		= secondary_format_modifiers,
> +		.min_width		= 0,
> +		.min_height		= 0,
> +		.max_width		= 4096,
> +		.max_height		= 4096,
> +		.rotation		= 0,
> +		.blend_mode		= BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +							BIT(DRM_MODE_BLEND_PREMULTI) |
> +							BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 2,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Primary_1",
> +		.id				= PRIMARY_PLANE_1,
> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
> +		.formats	 = primary_overlay_format0,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 3,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay_2",
> +		.id				= OVERLAY_PLANE_2,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
> +		.formats	 = primary_overlay_format0,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 4,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay_3",
> +		.id			= OVERLAY_PLANE_3,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
> +		.formats	 = primary_overlay_format0,
> +		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
> +		.modifiers	 = secondary_format_modifiers,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = 0,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 5,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Cursor",
> +		.id				= CURSOR_PLANE_0,
> +		.type		 = DRM_PLANE_TYPE_CURSOR,
> +		.num_formats = ARRAY_SIZE(cursor_formats),
> +		.formats	 = cursor_formats,
> +		.num_modifiers = 0,
> +		.modifiers	 = NULL,
> +		.min_width	 = 32,
> +		.min_height  = 32,
> +		.max_width	 = 64,
> +		.max_height  = 64,
> +		.rotation	 = 0,
> +		.degamma_size = 0,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 255,
> +		.watermark	 = false,
> +		.color_mgmt  = false,
> +		.roi		 = false,
> +		},
> +		{
> +		.name		 = "Cursor_1",
> +		.id				= CURSOR_PLANE_1,
> +		.type		 = DRM_PLANE_TYPE_CURSOR,
> +		.num_formats = ARRAY_SIZE(cursor_formats),
> +		.formats	 = cursor_formats,
> +		.num_modifiers = 0,
> +		.modifiers	 = NULL,
> +		.min_width	 = 32,
> +		.min_height  = 32,
> +		.max_width	 = 64,
> +		.max_height  = 64,
> +		.rotation	 = 0,
> +		.degamma_size = 0,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 255,
> +		.watermark	 = false,
> +		.color_mgmt  = false,
> +		.roi		 = false,
> +		},
> +	},
> +	{
> +		/* DC_REV_1 */
> +		{
> +		.name		 = "Primary",
> +		.id				= PRIMARY_PLANE_0,
> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
> +		.formats	 = primary_overlay_format0,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 0,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay",
> +		.id				= OVERLAY_PLANE_0,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
> +		.formats	 = primary_overlay_format0,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 1,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Primary_1",
> +		.id				= PRIMARY_PLANE_1,
> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
> +		.formats	 = primary_overlay_format0,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 2,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay_2",
> +		.id				= OVERLAY_PLANE_2,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
> +		.formats	 = primary_overlay_format0,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 3,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Cursor",
> +		.id				= CURSOR_PLANE_0,
> +		.type		 = DRM_PLANE_TYPE_CURSOR,
> +		.num_formats = ARRAY_SIZE(cursor_formats),
> +		.formats	 = cursor_formats,
> +		.num_modifiers = 0,
> +		.modifiers	 = NULL,
> +		.min_width	 = 32,
> +		.min_height  = 32,
> +		.max_width	 = 64,
> +		.max_height  = 64,
> +		.rotation	 = 0,
> +		.degamma_size = 0,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 255,
> +		.watermark	 = false,
> +		.color_mgmt  = false,
> +		.roi		 = false,
> +		},
> +		{
> +		.name		 = "Cursor_1",
> +		.id				= CURSOR_PLANE_1,
> +		.type		 = DRM_PLANE_TYPE_CURSOR,
> +		.num_formats = ARRAY_SIZE(cursor_formats),
> +		.formats	 = cursor_formats,
> +		.num_modifiers = 0,
> +		.modifiers	 = NULL,
> +		.min_width	 = 32,
> +		.min_height  = 32,
> +		.max_width	 = 64,
> +		.max_height  = 64,
> +		.rotation	 = 0,
> +		.degamma_size = 0,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 255,
> +		.watermark	 = false,
> +		.color_mgmt  = false,
> +		.roi		 = false,
> +		},
> +	},
> +	{
> +		/* DC_REV_2 */
> +		{
> +		.name		 = "Primary",
> +		.id				= PRIMARY_PLANE_0,
> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
> +		.formats	 = primary_overlay_format1,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 0,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay",
> +		.id				= OVERLAY_PLANE_0,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
> +		.formats	 = primary_overlay_format1,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 1,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay_1",
> +		.id				= OVERLAY_PLANE_1,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
> +		.formats	 = primary_overlay_format1,
> +		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
> +		.modifiers	 = secondary_format_modifiers,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = 0,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 2,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Primary_1",
> +		.id				= PRIMARY_PLANE_1,
> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
> +		.formats	 = primary_overlay_format1,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 3,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay_2",
> +		.id				= OVERLAY_PLANE_2,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
> +		.formats	 = primary_overlay_format1,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 4,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay_3",
> +		.id				= OVERLAY_PLANE_3,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
> +		.formats	 = primary_overlay_format1,
> +		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
> +		.modifiers	 = secondary_format_modifiers,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = 0,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 5,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Cursor",
> +		.id				= CURSOR_PLANE_0,
> +		.type		 = DRM_PLANE_TYPE_CURSOR,
> +		.num_formats = ARRAY_SIZE(cursor_formats),
> +		.formats	 = cursor_formats,
> +		.num_modifiers = 0,
> +		.modifiers	 = NULL,
> +		.min_width	 = 32,
> +		.min_height  = 32,
> +		.max_width	 = 64,
> +		.max_height  = 64,
> +		.rotation	 = 0,
> +		.degamma_size = 0,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 255,
> +		.watermark	 = false,
> +		.color_mgmt  = false,
> +		.roi		 = false,
> +		},
> +		{
> +		.name		 = "Cursor_1",
> +		.id				= CURSOR_PLANE_1,
> +		.type		 = DRM_PLANE_TYPE_CURSOR,
> +		.num_formats = ARRAY_SIZE(cursor_formats),
> +		.formats	 = cursor_formats,
> +		.num_modifiers = 0,
> +		.modifiers	 = NULL,
> +		.min_width	 = 32,
> +		.min_height  = 32,
> +		.max_width	 = 64,
> +		.max_height  = 64,
> +		.rotation	 = 0,
> +		.degamma_size = 0,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 255,
> +		.watermark	 = false,
> +		.color_mgmt  = false,
> +		.roi		 = false,
> +		},
> +	},
> +};
> +
> +static const struct vs_dc_info dc_info[] = {

Again, please split into per-generation instances.

> +	{
> +		/* DC_REV_0 */
> +		.name			= "DC8200",
> +		.panel_num		= 2,
> +		.plane_num		= 8,
> +		.planes			= dc_hw_planes[DC_REV_0],
> +		.layer_num		= 6,
> +		.max_bpc		= 10,
> +		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
> +						  DRM_COLOR_FORMAT_YCBCR444 |
> +						  DRM_COLOR_FORMAT_YCBCR422 |
> +						  DRM_COLOR_FORMAT_YCBCR420,
> +		.gamma_size		= GAMMA_EX_SIZE,
> +		.gamma_bits		= 12,
> +		.pitch_alignment	= 128,
> +		.pipe_sync		= false,
> +		.background		= true,
> +		.panel_sync		= true,
> +		.cap_dec		= true,
> +	},
> +	{
> +		/* DC_REV_1 */
> +		.name			= "DC8200",
> +		.panel_num		= 2,
> +		.plane_num		= 6,
> +		.planes			= dc_hw_planes[DC_REV_1],
> +		.layer_num		= 4,
> +		.max_bpc		= 10,
> +		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
> +						  DRM_COLOR_FORMAT_YCBCR444 |
> +						  DRM_COLOR_FORMAT_YCBCR422 |
> +						  DRM_COLOR_FORMAT_YCBCR420,
> +		.gamma_size		= GAMMA_EX_SIZE,
> +		.gamma_bits		= 12,
> +		.pitch_alignment	= 128,
> +		.pipe_sync		= false,
> +		.background		= true,
> +		.panel_sync		= true,
> +		.cap_dec		= true,
> +	},
> +	{
> +		/* DC_REV_2 */
> +		.name			= "DC8200",
> +		.panel_num		= 2,
> +		.plane_num		= 8,
> +		.planes			= dc_hw_planes[DC_REV_2],
> +		.layer_num		= 6,
> +		.max_bpc		= 10,
> +		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
> +						  DRM_COLOR_FORMAT_YCBCR444 |
> +						  DRM_COLOR_FORMAT_YCBCR422 |
> +						  DRM_COLOR_FORMAT_YCBCR420,
> +		.gamma_size		= GAMMA_EX_SIZE,
> +		.gamma_bits		= 12,
> +		.pitch_alignment	= 128,
> +		.pipe_sync		= false,
> +		.background		= true,
> +		.panel_sync		= true,
> +		.cap_dec		= false,
> +	},
> +};
> +
> +static const struct dc_hw_funcs hw_func;
> +
> +static inline u32 hi_read(struct dc_hw *hw, u32 reg)
> +{
> +	return readl(hw->hi_base + reg);
> +}
> +
> +static inline void hi_write(struct dc_hw *hw, u32 reg, u32 value)
> +{
> +	writel(value, hw->hi_base + reg);
> +}
> +
> +static inline void dc_write(struct dc_hw *hw, u32 reg, u32 value)
> +{
> +	writel(value, hw->reg_base + reg - DC_REG_BASE);
> +}
> +
> +static inline u32 dc_read(struct dc_hw *hw, u32 reg)
> +{
> +	u32 value = readl(hw->reg_base + reg - DC_REG_BASE);
> +
> +	return value;
> +}
> +
> +static inline void dc_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear)
> +{
> +	u32 value = dc_read(hw, reg);
> +
> +	value &= ~clear;
> +	value |= set;
> +	dc_write(hw, reg, value);
> +}
> +
> +static void load_default_filter(struct dc_hw *hw,
> +				const struct dc_hw_plane_reg *reg, u32 offset)
> +{
> +	u8 i;
> +
> +	dc_write(hw, reg->scale_config + offset, 0x33);
> +	dc_write(hw, reg->init_offset + offset, 0x80008000);
> +	dc_write(hw, reg->h_filter_coef_index + offset, 0x00);
> +	for (i = 0; i < H_COEF_SIZE; i++)
> +		dc_write(hw, reg->h_filter_coef_data + offset, horkernel[i]);
> +
> +	dc_write(hw, reg->v_filter_coef_index + offset, 0x00);
> +	for (i = 0; i < V_COEF_SIZE; i++)
> +		dc_write(hw, reg->v_filter_coef_data + offset, verkernel[i]);
> +}
> +
> +static void load_rgb_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
> +			    u32 offset, u16 *table)
> +{
> +	dc_write(hw, reg->rgb_to_rgb_coef0 + offset, table[0] | (table[1] << 16));
> +	dc_write(hw, reg->rgb_to_rgb_coef1 + offset, table[2] | (table[3] << 16));
> +	dc_write(hw, reg->rgb_to_rgb_coef2 + offset, table[4] | (table[5] << 16));
> +	dc_write(hw, reg->rgb_to_rgb_coef3 + offset, table[6] | (table[7] << 16));
> +	dc_write(hw, reg->rgb_to_rgb_coef4 + offset, table[8]);
> +}
> +
> +static void load_yuv_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
> +			    u32 offset, s32 *table)
> +{
> +	dc_write(hw, reg->yuv_to_rgb_coef0 + offset,
> +		 (0xFFFF & table[0]) | (table[1] << 16));
> +	dc_write(hw, reg->yuv_to_rgb_coef1 + offset,
> +		 (0xFFFF & table[2]) | (table[3] << 16));
> +	dc_write(hw, reg->yuv_to_rgb_coef2 + offset,
> +		 (0xFFFF & table[4]) | (table[5] << 16));
> +	dc_write(hw, reg->yuv_to_rgb_coef3 + offset,
> +		 (0xFFFF & table[6]) | (table[7] << 16));
> +	dc_write(hw, reg->yuv_to_rgb_coef4 + offset, table[8]);
> +	dc_write(hw, reg->yuv_to_rgb_coefd0 + offset, table[9]);
> +	dc_write(hw, reg->yuv_to_rgb_coefd1 + offset, table[10]);
> +	dc_write(hw, reg->yuv_to_rgb_coefd2 + offset, table[11]);
> +	dc_write(hw, reg->y_clamp_bound + offset, table[12] | (table[13] << 16));
> +	dc_write(hw, reg->uv_clamp_bound + offset, table[14] | (table[15] << 16));
> +}
> +
> +static void load_rgb_to_yuv(struct dc_hw *hw, u32 offset, s16 *table)
> +{
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF0 + offset,
> +		 table[0] | (table[1] << 16));
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF1 + offset,
> +		 table[2] | (table[3] << 16));
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF2 + offset,
> +		 table[4] | (table[5] << 16));
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF3 + offset,
> +		 table[6] | (table[7] << 16));
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF4 + offset, table[8]);
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD0 + offset, table[9]);
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD1 + offset, table[10]);
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD2 + offset, table[11]);
> +}
> +
> +static bool is_rgb(enum dc_hw_color_format format)
> +{
> +	switch (format) {
> +	case FORMAT_X4R4G4B4:
> +	case FORMAT_A4R4G4B4:
> +	case FORMAT_X1R5G5B5:
> +	case FORMAT_A1R5G5B5:
> +	case FORMAT_R5G6B5:
> +	case FORMAT_X8R8G8B8:
> +	case FORMAT_A8R8G8B8:
> +	case FORMAT_A2R10G10B10:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static void load_degamma_table(struct dc_hw *hw,
> +			       const struct dc_hw_plane_reg *reg,
> +			       u32 offset, u16 *table)
> +{
> +	u16 i;
> +	u32 value;
> +
> +	dc_write(hw, reg->degamma_index + offset, 0);
> +
> +	for (i = 0; i < DEGAMMA_SIZE; i++) {
> +		value = table[i] | (table[i] << 16);
> +		dc_write(hw, reg->degamma_data + offset, value);
> +		dc_write(hw, reg->degamma_ex_data + offset, table[i]);
> +	}
> +}
> +
> +static u32 get_addr_offset(u32 id)
> +{
> +	u32 offset = 0;
> +
> +	switch (id) {
> +	case PRIMARY_PLANE_1:
> +	case OVERLAY_PLANE_1:
> +		offset = 0x04;
> +		break;
> +	case OVERLAY_PLANE_2:
> +		offset = 0x08;
> +		break;
> +	case OVERLAY_PLANE_3:
> +		offset = 0x0C;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return offset;
> +}
> +
> +int dc_hw_init(struct dc_hw *hw)
> +{
> +	u8 i, id, panel_num, layer_num;
> +	u32 offset;
> +	u32 revision = hi_read(hw, DC_HW_REVISION);
> +	u32 cid = hi_read(hw, DC_HW_CHIP_CID);
> +	const struct dc_hw_plane_reg *reg;
> +
> +	switch (revision) {
> +	case 0x5720:
> +		hw->rev = DC_REV_0;
> +		break;
> +	case 0x5721:
> +		switch (cid) {
> +		case 0x30B:
> +			hw->rev = DC_REV_1;
> +			break;
> +		case 0x310:
> +			hw->rev = DC_REV_2;
> +			break;
> +		default:
> +			hw->rev = DC_REV_0;
> +			break;
> +		}
> +		break;
> +	default:
> +		return -ENXIO;
> +	}

We (drm/msm) had code like this for a while (and autodetection of the HW 
ID). And nowadays we are migrating to of_device_get_match_data and 
per-SoC compat names. It is just easier to handle, it allows one to 
specify device more precisely and also it simplifies DT schema.

> +
> +	hw->info = (struct vs_dc_info *)&dc_info[hw->rev];
> +	hw->func = (struct dc_hw_funcs *)&hw_func;
> +
> +	layer_num = hw->info->layer_num;
> +	for (i = 0; i < layer_num; i++) {
> +		id = hw->info->planes[i].id;
> +		offset = get_addr_offset(id);
> +		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
> +			reg = &dc_plane_reg[0];
> +		else
> +			reg = &dc_plane_reg[1];
> +
> +		load_default_filter(hw, reg, offset);
> +		load_rgb_to_rgb(hw, reg, offset, RGB2RGB);
> +	}
> +
> +	panel_num = hw->info->panel_num;
> +	for (i = 0; i < panel_num; i++) {
> +		offset = i << 2;
> +
> +		load_rgb_to_yuv(hw, offset, RGB2YUV);
> +		dc_write(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0x111);
> +
> +		offset = i ? DC_CURSOR_OFFSET : 0;
> +		dc_write(hw, DC_CURSOR_BACKGROUND + offset, 0x00FFFFFF);
> +		dc_write(hw, DC_CURSOR_FOREGROUND + offset, 0x00AAAAAA);
> +	}
> +
> +	return 0;
> +}
> +
> +void dc_hw_deinit(struct dc_hw *hw)
> +{
> +	/* Nothing to do */
> +}
> +
> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
> +			struct dc_hw_fb *fb, struct dc_hw_scale *scale,
> +			struct dc_hw_position *pos, struct dc_hw_blend *blend)
> +{
> +	struct dc_hw_plane *plane = &hw->plane[id];
> +
> +	if (plane) {
> +		if (fb) {
> +			if (!fb->enable)
> +				plane->fb.enable = false;
> +			else
> +				memcpy(&plane->fb, fb,
> +				       sizeof(*fb) - sizeof(fb->dirty));
> +			plane->fb.dirty = true;
> +		}
> +		if (scale) {
> +			memcpy(&plane->scale, scale,
> +			       sizeof(*scale) - sizeof(scale->dirty));
> +			plane->scale.dirty = true;
> +		}
> +		if (pos) {
> +			memcpy(&plane->pos, pos,
> +			       sizeof(*pos) - sizeof(pos->dirty));
> +			plane->pos.dirty = true;
> +		}
> +		if (blend) {
> +			memcpy(&plane->blend, blend,
> +			       sizeof(*blend) - sizeof(blend->dirty));
> +			plane->blend.dirty = true;
> +		}
> +	}
> +}
> +
> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode)
> +{
> +	struct dc_hw_plane *plane = &hw->plane[id];
> +
> +	if (plane) {
> +		if (hw->info->planes[id].degamma_size) {
> +			plane->degamma.mode = mode;
> +			plane->degamma.dirty = true;
> +		} else {
> +			plane->degamma.dirty = false;
> +		}
> +	}
> +}
> +
> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi)
> +{
> +	struct dc_hw_plane *plane = &hw->plane[id];
> +
> +	if (plane) {
> +		memcpy(&plane->roi, roi, sizeof(*roi) - sizeof(roi->dirty));
> +		plane->roi.dirty = true;
> +	}
> +}
> +
> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
> +			   struct dc_hw_colorkey *colorkey)
> +{
> +	struct dc_hw_plane *plane = &hw->plane[id];
> +
> +	if (plane) {
> +		memcpy(&plane->colorkey, colorkey,
> +		       sizeof(*colorkey) - sizeof(colorkey->dirty));
> +		plane->colorkey.dirty = true;
> +	}
> +}
> +
> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos)
> +{
> +	memcpy(&hw->qos, qos, sizeof(*qos) - sizeof(qos->dirty));
> +	hw->qos.dirty = true;
> +}
> +
> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor)
> +{
> +	memcpy(&hw->cursor[id], cursor, sizeof(*cursor) - sizeof(cursor->dirty));
> +	hw->cursor[id].dirty = true;
> +}
> +
> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
> +			u16 r, u16 g, u16 b)
> +{
> +	if (index >= hw->info->gamma_size)
> +		return;
> +
> +	hw->gamma[id].gamma[index][0] = r;
> +	hw->gamma[id].gamma[index][1] = g;
> +	hw->gamma[id].gamma[index][2] = b;
> +	hw->gamma[id].dirty = true;
> +}
> +
> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable)
> +{
> +	hw->gamma[id].enable = enable;
> +	hw->gamma[id].dirty = true;
> +}
> +
> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display)
> +{
> +	u8 id = display->id;
> +
> +	memcpy(&hw->display[id], display, sizeof(*display));
> +
> +	hw->func->display(hw, display);
> +}
> +
> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable)
> +{
> +	if (enable)
> +		hi_write(hw, AQ_INTR_ENBL, 0xFFFFFFFF);
> +	else
> +		hi_write(hw, AQ_INTR_ENBL, 0);
> +}
> +
> +u32 dc_hw_get_interrupt(struct dc_hw *hw)
> +{
> +	return hi_read(hw, AQ_INTR_ACKNOWLEDGE);
> +}
> +
> +bool dc_hw_check_underflow(struct dc_hw *hw)
> +{
> +	return dc_read(hw, DC_FRAMEBUFFER_CONFIG) & BIT(5);
> +}
> +
> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable)
> +{
> +	u32 i, offset;
> +	u8 id, layer_num = hw->info->layer_num;
> +	u8 panel_num = hw->info->panel_num;
> +
> +	for (i = 0; i < layer_num; i++) {
> +		id = hw->info->planes[i].id;
> +		offset = get_addr_offset(id);
> +		if (enable) {
> +			if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, BIT(12), 0);
> +			else
> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, BIT(31), 0);
> +		} else {
> +			if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, 0, BIT(12));
> +			else
> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, 0, BIT(31));
> +		}
> +	}
> +
> +	for (i = 0; i < panel_num; i++) {
> +		offset = i << 2;
> +		if (enable)
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, BIT(0));
> +		else
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, BIT(0), 0);
> +	}
> +}
> +
> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id)
> +{
> +	if (out <= OUT_DP)
> +		hw->out[id] = out;
> +}
> +
> +static void gamma_ex_commit(struct dc_hw *hw)
> +{
> +	u8 panel_num = hw->info->panel_num;
> +	u16 i, j;
> +	u32 value;
> +
> +	for (j = 0; j < panel_num; j++) {
> +		if (hw->gamma[j].dirty) {
> +			if (hw->gamma[j].enable) {
> +				dc_write(hw, DC_DISPLAY_GAMMA_EX_INDEX + (j << 2), 0x00);
> +				for (i = 0; i < GAMMA_EX_SIZE; i++) {
> +					value = hw->gamma[j].gamma[i][2] |
> +						(hw->gamma[j].gamma[i][1] << 12);
> +					dc_write(hw, DC_DISPLAY_GAMMA_EX_DATA + (j << 2), value);
> +					dc_write(hw, DC_DISPLAY_GAMMA_EX_ONE_DATA + (j << 2),
> +						 hw->gamma[j].gamma[i][0]);
> +				}
> +				dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
> +					     BIT(13), 0);
> +			} else {
> +				dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
> +					     0, BIT(13));
> +			}
> +			hw->gamma[j].dirty = false;
> +		}
> +	}
> +}
> +
> +static void plane_commit(struct dc_hw *hw)
> +{
> +	struct dc_hw_plane *plane;
> +	const struct dc_hw_plane_reg *reg;
> +	bool primary = false;
> +	u8 id, layer_num = hw->info->layer_num;
> +	u32 i, offset;
> +
> +	for (i = 0; i < layer_num; i++) {
> +		plane = &hw->plane[i];
> +		id = hw->info->planes[i].id;
> +		offset = get_addr_offset(id);
> +		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
> +			reg = &dc_plane_reg[0];
> +			primary = true;
> +		} else {
> +			reg = &dc_plane_reg[1];
> +			primary = false;
> +		}

Can you please move register defines to some internal hw_plane 
structure. These details are known at the time of creation of the plane. 
There is no need to determine them again and again.

> +
> +		if (plane->fb.dirty) {
> +			if (plane->fb.enable) {

if (plane->fb.dirty && plane->fb.enable) will save you one level of 
indentation.

However it might be better to use existing DRM flags instead of 
inventing your own flags.

> +				dc_write(hw, reg->y_address + offset,
> +					 plane->fb.y_address);
> +				dc_write(hw, reg->u_address + offset,
> +					 plane->fb.u_address);
> +				dc_write(hw, reg->v_address + offset,
> +					 plane->fb.v_address);
> +				dc_write(hw, reg->y_stride + offset,
> +					 plane->fb.y_stride);
> +				dc_write(hw, reg->u_stride + offset,
> +					 plane->fb.u_stride);
> +				dc_write(hw, reg->v_stride + offset,
> +					 plane->fb.v_stride);
> +				dc_write(hw, reg->size + offset,
> +					 plane->fb.width |
> +					 (plane->fb.height << 15));
> +				dc_write(hw, reg->water_mark + offset,
> +					 plane->fb.water_mark);
> +
> +				if (plane->fb.clear_enable)
> +					dc_write(hw, reg->clear_value + offset,
> +						 plane->fb.clear_value);
> +			}
> +
> +			if (primary) {

You have two kinds of planes. Primary and overlays. Could you please 
split this code into two separate functions and define two vtables, one 
for primary planes and one for overlays. There is no need to stick 
everything into a single funcion that handles everything.

> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
> +					     (plane->fb.format << 26) |
> +					     (plane->fb.uv_swizzle << 25) |
> +					     (plane->fb.swizzle << 23) |
> +					     (plane->fb.tile_mode << 17) |
> +					     (plane->fb.yuv_color_space << 14) |
> +					     (plane->fb.rotation << 11) |
> +					     (plane->fb.clear_enable << 8),
> +					     (0x1F << 26) |
> +					     BIT(25) |
> +					     (0x03 << 23) |
> +					     (0x1F << 17) |
> +					     (0x07 << 14) |
> +					     (0x07 << 11) |
> +					     BIT(8));
> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
> +					     (plane->fb.dec_enable << 1) |
> +					     (plane->fb.enable << 13) |
> +					     (plane->fb.zpos << 16) |
> +					     (plane->fb.display_id << 19),
> +					     BIT(1) | BIT(13) | (0x07 << 16) | BIT(19));

It looks like this code can enjoy defining names for all the magic 
values. Otherwise it is impossible to support it.

> +			} else {
> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> +					     (plane->fb.dec_enable << 27) |
> +					     (plane->fb.clear_enable << 25) |
> +					     (plane->fb.enable << 24) |
> +					     (plane->fb.format << 16) |
> +					     (plane->fb.uv_swizzle << 15) |
> +					     (plane->fb.swizzle << 13) |
> +					     (plane->fb.tile_mode << 8) |
> +					     (plane->fb.yuv_color_space << 5) |
> +					     (plane->fb.rotation << 2),
> +					     BIT(27) |
> +					     BIT(25) |
> +					     BIT(24) |
> +					     (0x1F << 16) |
> +					     BIT(15) |
> +					     (0x03 << 13) |
> +					     (0x1F << 8) |
> +					     (0x07 << 5) |
> +					     (0x07 << 2));
> +				dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset,
> +					     plane->fb.zpos | (plane->fb.display_id << 3),
> +					     0x07 | BIT(3));
> +			}
> +			plane->fb.dirty = false;
> +		}
> +
> +		if (plane->scale.dirty) {
> +			if (plane->scale.enable) {
> +				dc_write(hw, reg->scale_factor_x + offset,
> +					 plane->scale.scale_factor_x);
> +				dc_write(hw, reg->scale_factor_y + offset,
> +					 plane->scale.scale_factor_y);
> +				if (primary)
> +					dc_set_clear(hw,
> +						     DC_FRAMEBUFFER_CONFIG + offset,
> +						     BIT(22), 0);
> +				else
> +					dc_set_clear(hw,
> +						     DC_OVERLAY_SCALE_CONFIG + offset,
> +						     BIT(8), 0);
> +			} else {
> +				if (primary)
> +					dc_set_clear(hw,
> +						     DC_FRAMEBUFFER_CONFIG + offset,
> +						     0, BIT(22));
> +				else
> +					dc_set_clear(hw,
> +						     DC_OVERLAY_SCALE_CONFIG + offset,
> +						     0, BIT(8));
> +			}
> +			plane->scale.dirty = false;
> +		}
> +
> +		if (plane->pos.dirty) {
> +			dc_write(hw, reg->top_left + offset,
> +				 plane->pos.start_x |
> +				 (plane->pos.start_y << 15));
> +			dc_write(hw, reg->bottom_right + offset,
> +				 plane->pos.end_x |
> +				 (plane->pos.end_y << 15));
> +			plane->pos.dirty = false;
> +		}
> +
> +		if (plane->blend.dirty) {
> +			dc_write(hw, reg->src_global_color + offset,
> +				 plane->blend.alpha << 24);
> +			dc_write(hw, reg->dst_global_color + offset,
> +				 plane->blend.alpha << 24);
> +			switch (plane->blend.blend_mode) {
> +			case BLEND_PREMULTI:
> +				dc_write(hw, reg->blend_config + offset, 0x3450);
> +				break;
> +			case BLEND_COVERAGE:
> +				dc_write(hw, reg->blend_config + offset, 0x3950);
> +				break;
> +			case BLEND_PIXEL_NONE:
> +				dc_write(hw, reg->blend_config + offset, 0x3548);
> +				break;
> +			default:
> +				break;
> +			}
> +			plane->blend.dirty = false;
> +		}
> +
> +		if (plane->colorkey.dirty) {
> +			dc_write(hw, reg->color_key + offset, plane->colorkey.colorkey);
> +			dc_write(hw, reg->color_key_high + offset,
> +				 plane->colorkey.colorkey_high);
> +
> +			if (primary)
> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
> +					     plane->colorkey.transparency << 9, 0x03 << 9);
> +			else
> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> +					     plane->colorkey.transparency, 0x03);
> +
> +			plane->colorkey.dirty = false;
> +		}
> +
> +		if (plane->roi.dirty) {
> +			if (plane->roi.enable) {
> +				dc_write(hw, reg->roi_origin + offset,
> +					 plane->roi.x | (plane->roi.y << 16));
> +				dc_write(hw, reg->roi_size + offset,
> +					 plane->roi.width | (plane->roi.height << 16));
> +				if (primary)
> +					dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
> +						     BIT(0), 0);
> +				else
> +					dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> +						     BIT(22), 0);
> +			} else {
> +				if (primary)
> +					dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
> +						     0, BIT(0));
> +				else
> +					dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> +						     0, BIT(22));
> +			}
> +			plane->roi.dirty = false;
> +		}
> +	}
> +}
> +
> +static void plane_ex_commit(struct dc_hw *hw)
> +{
> +	struct dc_hw_plane *plane;
> +	const struct dc_hw_plane_reg *reg;
> +	bool primary = false;
> +	u8 id, layer_num = hw->info->layer_num;
> +	u32 i, offset;
> +
> +	for (i = 0; i < layer_num; i++) {
> +		plane = &hw->plane[i];
> +		id = hw->info->planes[i].id;
> +		offset = get_addr_offset(id);
> +		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
> +			reg = &dc_plane_reg[0];
> +			primary = true;
> +		} else {
> +			reg = &dc_plane_reg[1];
> +			primary = false;
> +		}
> +
> +		if (plane->fb.dirty) {
> +			if (is_rgb(plane->fb.format)) {
> +				if (primary)
> +					dc_set_clear(hw,
> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
> +						     BIT(6), BIT(8));
> +				else
> +					dc_set_clear(hw,
> +						     DC_OVERLAY_CONFIG + offset,
> +						     BIT(29), BIT(30));
> +			} else {
> +				if (primary)
> +					dc_set_clear(hw,
> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
> +						     BIT(8), BIT(6));
> +				else
> +					dc_set_clear(hw,
> +						     DC_OVERLAY_CONFIG + offset,
> +						     BIT(30), BIT(29));
> +				switch (plane->fb.yuv_color_space) {
> +				case COLOR_SPACE_601:
> +					load_yuv_to_rgb(hw, reg, offset, YUV601_2RGB);
> +					break;
> +				case COLOR_SPACE_709:
> +					load_yuv_to_rgb(hw, reg, offset, YUV709_2RGB);
> +					break;
> +				case COLOR_SPACE_2020:
> +					load_yuv_to_rgb(hw, reg, offset, YUV2020_2RGB);
> +					break;
> +				default:
> +					break;
> +				}
> +			}
> +		}
> +		if (plane->degamma.dirty) {
> +			switch (plane->degamma.mode) {
> +			case VS_DEGAMMA_DISABLE:
> +				if (primary)
> +					dc_set_clear(hw,
> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
> +						     0, BIT(5));
> +				else
> +					dc_set_clear(hw,
> +						     DC_OVERLAY_CONFIG + offset,
> +						     0, BIT(28));
> +				break;
> +			case VS_DEGAMMA_BT709:
> +				load_degamma_table(hw, reg, offset, DEGAMMA_709);
> +				if (primary)
> +					dc_set_clear(hw,
> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
> +						     BIT(5), 0);
> +				else
> +					dc_set_clear(hw,
> +						     DC_OVERLAY_CONFIG + offset,
> +						     BIT(28), 0);
> +				break;
> +			case VS_DEGAMMA_BT2020:
> +				load_degamma_table(hw, reg, offset, DEGAMMA_2020);
> +				if (primary)
> +					dc_set_clear(hw,
> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
> +						     BIT(5), 0);
> +				else
> +					dc_set_clear(hw,
> +						     DC_OVERLAY_CONFIG + offset,
> +						     BIT(28), 0);
> +				break;
> +			default:
> +				break;
> +			}
> +			plane->degamma.dirty = false;
> +		}
> +	}
> +	plane_commit(hw);
> +}
> +
> +static void setup_display(struct dc_hw *hw, struct dc_hw_display *display)
> +{
> +	u8 id = display->id;
> +	u32 dpi_cfg, offset = id << 2;
> +
> +	if (hw->display[id].enable) {
> +		switch (display->bus_format) {
> +		case MEDIA_BUS_FMT_RGB565_1X16:
> +			dpi_cfg = 0;
> +			break;
> +		case MEDIA_BUS_FMT_RGB666_1X18:
> +			dpi_cfg = 3;
> +			break;
> +		case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> +			dpi_cfg = 4;
> +			break;
> +		case MEDIA_BUS_FMT_RGB888_1X24:
> +			dpi_cfg = 5;
> +			break;
> +		case MEDIA_BUS_FMT_RGB101010_1X30:
> +			dpi_cfg = 6;
> +			break;
> +		default:
> +			dpi_cfg = 5;
> +			break;
> +		}
> +		dc_write(hw, DC_DISPLAY_DPI_CONFIG + offset, dpi_cfg);
> +
> +		if (id == 0)
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
> +		else
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
> +
> +		dc_write(hw, DC_DISPLAY_H + offset, hw->display[id].h_active |
> +			 (hw->display[id].h_total << 16));
> +		dc_write(hw, DC_DISPLAY_H_SYNC + offset,
> +			 hw->display[id].h_sync_start |
> +			 (hw->display[id].h_sync_end << 15) |
> +			 (hw->display[id].h_sync_polarity ? 0 : BIT(31)) |
> +			 BIT(30));
> +		dc_write(hw, DC_DISPLAY_V + offset, hw->display[id].v_active |
> +				(hw->display[id].v_total << 16));
> +		dc_write(hw, DC_DISPLAY_V_SYNC + offset,
> +			 hw->display[id].v_sync_start |
> +			 (hw->display[id].v_sync_end << 15) |
> +			 (hw->display[id].v_sync_polarity ? 0 : BIT(31)) |
> +			 BIT(30));
> +
> +		if (hw->info->pipe_sync)
> +			dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
> +				     0, BIT(3) | BIT(4));
> +
> +		dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(12), 0);
> +		if (id == 0)
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(0), BIT(3));
> +		else
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(1), BIT(3));
> +	} else {
> +		dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(12));
> +		if (id == 0)
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
> +		else
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
> +	}
> +}
> +
> +static void setup_display_ex(struct dc_hw *hw, struct dc_hw_display *display)
> +{
> +	u8 id = display->id;
> +	u32 dp_cfg, offset = id << 2;
> +	bool is_yuv = false;
> +
> +	if (hw->display[id].enable && hw->out[id] == OUT_DP) {
> +		switch (display->bus_format) {
> +		case MEDIA_BUS_FMT_RGB565_1X16:
> +			dp_cfg = 0;
> +			break;
> +		case MEDIA_BUS_FMT_RGB666_1X18:
> +			dp_cfg = 1;
> +			break;
> +		case MEDIA_BUS_FMT_RGB888_1X24:
> +			dp_cfg = 2;
> +			break;
> +		case MEDIA_BUS_FMT_RGB101010_1X30:
> +			dp_cfg = 3;
> +			break;
> +		case MEDIA_BUS_FMT_UYVY8_1X16:
> +			dp_cfg = 2 << 4;
> +			is_yuv = true;
> +			break;
> +		case MEDIA_BUS_FMT_YUV8_1X24:
> +			dp_cfg = 4 << 4;
> +			is_yuv = true;
> +			break;
> +		case MEDIA_BUS_FMT_UYVY10_1X20:
> +			dp_cfg = 8 << 4;
> +			is_yuv = true;
> +			break;
> +		case MEDIA_BUS_FMT_YUV10_1X30:
> +			dp_cfg = 10 << 4;
> +			is_yuv = true;
> +			break;
> +		case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +			dp_cfg = 12 << 4;
> +			is_yuv = true;
> +			break;
> +		case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> +			dp_cfg = 13 << 4;
> +			is_yuv = true;
> +			break;
> +		default:
> +			dp_cfg = 2;
> +			break;
> +		}
> +		if (is_yuv)
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(16), 0);
> +		else
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(16));
> +		dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | BIT(3));
> +	}
> +
> +	if (hw->out[id] == OUT_DPI)
> +		dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, BIT(3));
> +
> +	setup_display(hw, display);
> +}
> +
> +static const struct dc_hw_funcs hw_func = {
> +	.gamma = &gamma_ex_commit,
> +	.plane = &plane_ex_commit,
> +	.display = setup_display_ex,
> +};
> +
> +void dc_hw_commit(struct dc_hw *hw)
> +{
> +	u32 i, offset = 0;
> +	u8 plane_num = hw->info->plane_num;
> +	u8 layer_num = hw->info->layer_num;
> +	u8 cursor_num = plane_num - layer_num;
> +
> +	hw->func->gamma(hw);
> +	hw->func->plane(hw);
> +
> +	for (i = 0; i < cursor_num; i++) {
> +		if (hw->cursor[i].dirty) {
> +			offset = hw->cursor[i].display_id ? DC_CURSOR_OFFSET : 0;
> +			if (hw->cursor[i].enable) {
> +				dc_write(hw, DC_CURSOR_ADDRESS + offset,
> +					 hw->cursor[i].address);
> +				dc_write(hw, DC_CURSOR_LOCATION + offset, hw->cursor[i].x |
> +					 (hw->cursor[i].y << 16));
> +				dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
> +					     (hw->cursor[i].hot_x << 16) |
> +					     (hw->cursor[i].hot_y << 8) |
> +					     (hw->cursor[i].size << 5) |
> +					     BIT(3) | BIT(2) | 0x02,
> +					     (0xFF << 16) |
> +					     (0xFF << 8) |
> +					     (0x07 << 5) | 0x1F);
> +			} else {
> +				dc_set_clear(hw, DC_CURSOR_CONFIG + offset, BIT(3), 0x03);
> +			}
> +			hw->cursor[i].dirty = false;
> +		}
> +	}
> +
> +	if (hw->qos.dirty) {
> +		dc_set_clear(hw, DC_QOS_CONFIG, (hw->qos.high_value << 4) |
> +					 hw->qos.low_value, 0xFF);
> +		hw->qos.dirty = false;
> +	}
> +}
> +
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.h b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
> new file mode 100644
> index 000000000..9a1d767ae
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
> @@ -0,0 +1,492 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DC_HW_H__
> +#define __VS_DC_HW_H__
> +
> +#define AQ_INTR_ACKNOWLEDGE				0x0010
> +#define AQ_INTR_ENBL					0x0014

Please take care to align all the defines.

> +#define DC_HW_REVISION					0x0024
> +#define DC_HW_CHIP_CID					0x0030
> +
> +#define DC_REG_BASE						0x0800
> +#define DC_REG_RANGE					0x2000
> +#define DC_SEC_REG_OFFSET				0x100000
> +
> +#define DC_FRAMEBUFFER_CONFIG			0x1518
> +#define DC_FRAMEBUFFER_CONFIG_EX		0x1CC0
> +#define DC_FRAMEBUFFER_SCALE_CONFIG		0x1520
> +#define DC_FRAMEBUFFER_TOP_LEFT			0x24D8
> +#define DC_FRAMEBUFFER_BOTTOM_RIGHT		0x24E0
> +#define DC_FRAMEBUFFER_ADDRESS			0x1400
> +#define DC_FRAMEBUFFER_U_ADDRESS		0x1530
> +#define DC_FRAMEBUFFER_V_ADDRESS		0x1538
> +#define DC_FRAMEBUFFER_STRIDE			0x1408
> +#define DC_FRAMEBUFFER_U_STRIDE			0x1800
> +#define DC_FRAMEBUFFER_V_STRIDE			0x1808
> +#define DC_FRAMEBUFFER_SIZE				0x1810
> +#define DC_FRAMEBUFFER_SCALE_FACTOR_X		0x1828
> +#define DC_FRAMEBUFFER_SCALE_FACTOR_Y		0x1830
> +#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX	0x1838
> +#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA	0x1A00
> +#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX	0x1A08
> +#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA	0x1A10
> +#define DC_FRAMEBUFFER_INIT_OFFSET			0x1A20
> +#define DC_FRAMEBUFFER_COLOR_KEY			0x1508
> +#define DC_FRAMEBUFFER_COLOR_KEY_HIGH		0x1510
> +#define DC_FRAMEBUFFER_CLEAR_VALUE			0x1A18
> +#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX	0x1818
> +#define DC_FRAMEBUFFER_COLOR_TABLE_DATA		0x1820
> +#define DC_FRAMEBUFFER_BG_COLOR				0x1528
> +#define DC_FRAMEBUFFER_ROI_ORIGIN			0x1CB0
> +#define DC_FRAMEBUFFER_ROI_SIZE				0x1CB8
> +#define DC_FRAMEBUFFER_WATER_MARK			0x1CE8
> +#define DC_FRAMEBUFFER_DEGAMMA_INDEX		0x1D88
> +#define DC_FRAMEBUFFER_DEGAMMA_DATA			0x1D90
> +#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA		0x1D98
> +#define DC_FRAMEBUFFER_YUVTORGB_COEF0		0x1DA0
> +#define DC_FRAMEBUFFER_YUVTORGB_COEF1		0x1DA8
> +#define DC_FRAMEBUFFER_YUVTORGB_COEF2		0x1DB0
> +#define DC_FRAMEBUFFER_YUVTORGB_COEF3		0x1DB8
> +#define DC_FRAMEBUFFER_YUVTORGB_COEF4		0x1E00
> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD0		0x1E08
> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD1		0x1E10
> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD2		0x1E18
> +#define DC_FRAMEBUFFER_Y_CLAMP_BOUND		0x1E88
> +#define DC_FRAMEBUFFER_UV_CLAMP_BOUND		0x1E90
> +#define DC_FRAMEBUFFER_RGBTORGB_COEF0		0x1E20
> +#define DC_FRAMEBUFFER_RGBTORGB_COEF1		0x1E28
> +#define DC_FRAMEBUFFER_RGBTORGB_COEF2		0x1E30
> +#define DC_FRAMEBUFFER_RGBTORGB_COEF3		0x1E38
> +#define DC_FRAMEBUFFER_RGBTORGB_COEF4		0x1E40
> +#define DC_FRAMEBUFFER_BLEND_CONFIG			0x2510
> +#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR		0x2500
> +#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR		0x2508
> +
> +#define DC_OVERLAY_CONFIG					0x1540
> +#define DC_OVERLAY_CONFIG_EX				0x2540
> +#define DC_OVERLAY_SCALE_CONFIG				0x1C00
> +#define DC_OVERLAY_BLEND_CONFIG				0x1580
> +#define DC_OVERLAY_TOP_LEFT					0x1640
> +#define DC_OVERLAY_BOTTOM_RIGHT				0x1680
> +#define DC_OVERLAY_ADDRESS					0x15C0
> +#define DC_OVERLAY_U_ADDRESS				0x1840
> +#define DC_OVERLAY_V_ADDRESS				0x1880
> +#define DC_OVERLAY_STRIDE					0x1600
> +#define DC_OVERLAY_U_STRIDE					0x18C0
> +#define DC_OVERLAY_V_STRIDE					0x1900
> +#define DC_OVERLAY_SIZE						0x17C0
> +#define DC_OVERLAY_SCALE_FACTOR_X			0x1A40
> +#define DC_OVERLAY_SCALE_FACTOR_Y			0x1A80
> +#define DC_OVERLAY_H_FILTER_COEF_INDEX		0x1AC0
> +#define DC_OVERLAY_H_FILTER_COEF_DATA		0x1B00
> +#define DC_OVERLAY_V_FILTER_COEF_INDEX		0x1B40
> +#define DC_OVERLAY_V_FILTER_COEF_DATA		0x1B80
> +#define DC_OVERLAY_INIT_OFFSET				0x1BC0
> +#define DC_OVERLAY_COLOR_KEY				0x1740
> +#define DC_OVERLAY_COLOR_KEY_HIGH			0x1780
> +#define DC_OVERLAY_CLEAR_VALUE				0x1940
> +#define DC_OVERLAY_COLOR_TABLE_INDEX		0x1980
> +#define DC_OVERLAY_COLOR_TABLE_DATA			0x19C0
> +#define DC_OVERLAY_SRC_GLOBAL_COLOR			0x16C0
> +#define DC_OVERLAY_DST_GLOBAL_COLOR			0x1700
> +#define DC_OVERLAY_ROI_ORIGIN				0x1D00
> +#define DC_OVERLAY_ROI_SIZE					0x1D40
> +#define DC_OVERLAY_WATER_MARK				0x1DC0
> +#define DC_OVERLAY_DEGAMMA_INDEX			0x2200
> +#define DC_OVERLAY_DEGAMMA_DATA				0x2240
> +#define DC_OVERLAY_DEGAMMA_EX_DATA			0x2280
> +#define DC_OVERLAY_YUVTORGB_COEF0			0x1EC0
> +#define DC_OVERLAY_YUVTORGB_COEF1			0x1F00
> +#define DC_OVERLAY_YUVTORGB_COEF2			0x1F40
> +#define DC_OVERLAY_YUVTORGB_COEF3			0x1F80
> +#define DC_OVERLAY_YUVTORGB_COEF4			0x1FC0
> +#define DC_OVERLAY_YUVTORGB_COEFD0			0x2000
> +#define DC_OVERLAY_YUVTORGB_COEFD1			0x2040
> +#define DC_OVERLAY_YUVTORGB_COEFD2			0x2080
> +#define DC_OVERLAY_Y_CLAMP_BOUND			0x22C0
> +#define DC_OVERLAY_UV_CLAMP_BOUND			0x2300
> +#define DC_OVERLAY_RGBTORGB_COEF0			0x20C0
> +#define DC_OVERLAY_RGBTORGB_COEF1			0x2100
> +#define DC_OVERLAY_RGBTORGB_COEF2			0x2140
> +#define DC_OVERLAY_RGBTORGB_COEF3			0x2180
> +#define DC_OVERLAY_RGBTORGB_COEF4			0x21C0
> +
> +#define DC_CURSOR_CONFIG					0x1468
> +#define DC_CURSOR_ADDRESS					0x146C
> +#define DC_CURSOR_LOCATION					0x1470
> +#define DC_CURSOR_BACKGROUND				0x1474
> +#define DC_CURSOR_FOREGROUND				0x1478
> +#define DC_CURSOR_CLK_GATING				0x1484
> +#define DC_CURSOR_CONFIG_EX					0x24E8
> +#define DC_CURSOR_OFFSET					0x1080
> +
> +#define DC_DISPLAY_DITHER_CONFIG		0x1410
> +#define DC_DISPLAY_PANEL_CONFIG			0x1418
> +#define DC_DISPLAY_PANEL_CONFIG_EX		0x2518
> +#define DC_DISPLAY_DITHER_TABLE_LOW		0x1420
> +#define DC_DISPLAY_DITHER_TABLE_HIGH	0x1428
> +#define DC_DISPLAY_H					0x1430
> +#define DC_DISPLAY_H_SYNC				0x1438
> +#define DC_DISPLAY_V					0x1440
> +#define DC_DISPLAY_V_SYNC				0x1448
> +#define DC_DISPLAY_CURRENT_LOCATION		0x1450
> +#define DC_DISPLAY_GAMMA_INDEX			0x1458
> +#define DC_DISPLAY_GAMMA_DATA			0x1460
> +#define DC_DISPLAY_INT					0x147C
> +#define DC_DISPLAY_INT_ENABLE			0x1480
> +#define DC_DISPLAY_DBI_CONFIG			0x1488
> +#define DC_DISPLAY_GENERAL_CONFIG		0x14B0
> +#define DC_DISPLAY_DPI_CONFIG			0x14B8
> +#define DC_DISPLAY_PANEL_START			0x1CCC
> +#define DC_DISPLAY_DEBUG_COUNTER_SELECT		0x14D0
> +#define DC_DISPLAY_DEBUG_COUNTER_VALUE		0x14D8
> +#define DC_DISPLAY_DP_CONFIG				0x1CD0
> +#define DC_DISPLAY_GAMMA_EX_INDEX			0x1CF0
> +#define DC_DISPLAY_GAMMA_EX_DATA			0x1CF8
> +#define DC_DISPLAY_GAMMA_EX_ONE_DATA		0x1D80
> +#define DC_DISPLAY_RGBTOYUV_COEF0			0x1E48
> +#define DC_DISPLAY_RGBTOYUV_COEF1			0x1E50
> +#define DC_DISPLAY_RGBTOYUV_COEF2			0x1E58
> +#define DC_DISPLAY_RGBTOYUV_COEF3			0x1E60
> +#define DC_DISPLAY_RGBTOYUV_COEF4			0x1E68
> +#define DC_DISPLAY_RGBTOYUV_COEFD0			0x1E70
> +#define DC_DISPLAY_RGBTOYUV_COEFD1			0x1E78
> +#define DC_DISPLAY_RGBTOYUV_COEFD2			0x1E80
> +
> +#define DC_CLK_GATTING					0x1A28
> +#define DC_QOS_CONFIG					0x1A38
> +
> +#define DC_TRANSPARENCY_OPAQUE			0x00
> +#define DC_TRANSPARENCY_KEY				0x02
> +#define DC_DISPLAY_DITHERTABLE_LOW		0x7B48F3C0
> +#define DC_DISPLAY_DITHERTABLE_HIGH		0x596AD1E2
> +
> +#define GAMMA_SIZE						256
> +#define GAMMA_EX_SIZE					300
> +#define DEGAMMA_SIZE					260
> +
> +#define RGB_TO_RGB_TABLE_SIZE			9
> +#define YUV_TO_RGB_TABLE_SIZE			16
> +#define RGB_TO_YUV_TABLE_SIZE			12
> +
> +#define DC_LAYER_NUM	6
> +#define DC_DISPLAY_NUM	2
> +#define DC_CURSOR_NUM	2
> +
> +#define DC_TILE_MODE4X4	0x15
> +
> +enum dc_chip_rev {
> +	DC_REV_0,/* For HW_REV_5720,HW_REV_5721_311 */
> +	DC_REV_1,/* For HW_REV_5721_30B */
> +	DC_REV_2,/* For HW_REV_5721_310 */
> +};
> +
> +enum dc_hw_plane_id {
> +	PRIMARY_PLANE_0,
> +	OVERLAY_PLANE_0,
> +	OVERLAY_PLANE_1,
> +	PRIMARY_PLANE_1,
> +	OVERLAY_PLANE_2,
> +	OVERLAY_PLANE_3,
> +	CURSOR_PLANE_0,
> +	CURSOR_PLANE_1,
> +	PLANE_NUM
> +};
> +
> +enum dc_hw_color_format {
> +	FORMAT_X4R4G4B4,//0

Space after the comma will make it more readable. Or just define the 
values explicitly, it will also be more readable.

> +	FORMAT_A4R4G4B4,//1
> +	FORMAT_X1R5G5B5,//2
> +	FORMAT_A1R5G5B5,//3
> +	FORMAT_R5G6B5,//4
> +	FORMAT_X8R8G8B8,//5
> +	FORMAT_A8R8G8B8,//6
> +	FORMAT_YUY2,//7
> +	FORMAT_UYVY,//8
> +	FORMAT_INDEX8,//9
> +	FORMAT_MONOCHROME,//10
> +	FORMAT_YV12 = 0xf,
> +	FORMAT_A8,//16
> +	FORMAT_NV12,//17
> +	FORMAT_NV16,//18
> +	FORMAT_RG16,//19
> +	FORMAT_R8,//20
> +	FORMAT_NV12_10BIT,//21
> +	FORMAT_A2R10G10B10,//22
> +	FORMAT_NV16_10BIT,//23
> +	FORMAT_INDEX1,//24
> +	FORMAT_INDEX2,//25
> +	FORMAT_INDEX4,//26
> +	FORMAT_P010,//27
> +	FORMAT_YUV444,//28
> +	FORMAT_YUV444_10BIT,//29
> +};
> +
> +enum dc_hw_yuv_color_space {
> +	COLOR_SPACE_601 = 0,
> +	COLOR_SPACE_709 = 1,
> +	COLOR_SPACE_2020 = 3,
> +};
> +
> +enum dc_hw_rotation {
> +	ROT_0 = 0,
> +	ROT_90 = 4,
> +	ROT_180 = 5,
> +	ROT_270 = 6,
> +	FLIP_X = 1,
> +	FLIP_Y = 2,
> +	FLIP_XY = 3,
> +};
> +
> +enum dc_hw_swizzle {
> +	SWIZZLE_ARGB = 0,
> +	SWIZZLE_RGBA,
> +	SWIZZLE_ABGR,
> +	SWIZZLE_BGRA,
> +};
> +
> +enum dc_hw_out {
> +	OUT_DPI,
> +	OUT_DP,
> +};
> +
> +enum dc_hw_cursor_size {
> +	CURSOR_SIZE_32X32 = 0,
> +	CURSOR_SIZE_64X64,
> +};
> +
> +enum dc_hw_blend_mode {
> +	/* out.rgb = plane_alpha * fg.rgb +
> +	 *		(1 - (plane_alpha * fg.alpha)) * bg.rgb
> +	 */
> +	BLEND_PREMULTI,
> +	/* out.rgb = plane_alpha * fg.alpha * fg.rgb +
> +	 *		(1 - (plane_alpha * fg.alpha)) * bg.rgb
> +	 */
> +	BLEND_COVERAGE,
> +	/* out.rgb = plane_alpha * fg.rgb +
> +	 *		(1 - plane_alpha) * bg.rgb
> +	 */
> +	BLEND_PIXEL_NONE,

Can you use defines from <drm/drm_blend.h>?

> +};
> +
> +struct dc_hw_plane_reg {
> +	u32 y_address;
> +	u32 u_address;
> +	u32 v_address;
> +	u32 y_stride;
> +	u32 u_stride;
> +	u32 v_stride;
> +	u32 size;
> +	u32 top_left;
> +	u32 bottom_right;
> +	u32 scale_factor_x;
> +	u32 scale_factor_y;
> +	u32 h_filter_coef_index;
> +	u32 h_filter_coef_data;
> +	u32 v_filter_coef_index;
> +	u32 v_filter_coef_data;
> +	u32 init_offset;
> +	u32 color_key;
> +	u32 color_key_high;
> +	u32 clear_value;
> +	u32 color_table_index;
> +	u32 color_table_data;
> +	u32 scale_config;
> +	u32 water_mark;
> +	u32 degamma_index;
> +	u32 degamma_data;
> +	u32 degamma_ex_data;
> +	u32 src_global_color;
> +	u32 dst_global_color;
> +	u32 blend_config;
> +	u32 roi_origin;
> +	u32 roi_size;
> +	u32 yuv_to_rgb_coef0;
> +	u32 yuv_to_rgb_coef1;
> +	u32 yuv_to_rgb_coef2;
> +	u32 yuv_to_rgb_coef3;
> +	u32 yuv_to_rgb_coef4;
> +	u32 yuv_to_rgb_coefd0;
> +	u32 yuv_to_rgb_coefd1;
> +	u32 yuv_to_rgb_coefd2;
> +	u32 y_clamp_bound;
> +	u32 uv_clamp_bound;
> +	u32 rgb_to_rgb_coef0;
> +	u32 rgb_to_rgb_coef1;
> +	u32 rgb_to_rgb_coef2;
> +	u32 rgb_to_rgb_coef3;
> +	u32 rgb_to_rgb_coef4;
> +};
> +
> +struct dc_hw_fb {
> +	u32 y_address;
> +	u32 u_address;
> +	u32 v_address;
> +	u32 clear_value;
> +	u32 water_mark;
> +	u16 y_stride;
> +	u16 u_stride;
> +	u16 v_stride;
> +	u16 width;
> +	u16 height;
> +	u8	format;
> +	u8	tile_mode;
> +	u8	rotation;
> +	u8	yuv_color_space;
> +	u8	swizzle;
> +	u8	uv_swizzle;
> +	u8	zpos;
> +	u8	display_id;
> +	bool	clear_enable;
> +	bool	dec_enable;
> +	bool	enable;
> +	bool	dirty;
> +};
> +
> +struct dc_hw_scale {
> +	u32 scale_factor_x;
> +	u32 scale_factor_y;
> +	bool	enable;
> +	bool	dirty;
> +};
> +
> +struct dc_hw_position {
> +	u16 start_x;
> +	u16 start_y;
> +	u16 end_x;
> +	u16 end_y;
> +	bool	dirty;
> +};
> +
> +struct dc_hw_blend {
> +	u8	alpha;
> +	u8	blend_mode;
> +	bool	dirty;
> +};
> +
> +struct dc_hw_colorkey {
> +	u32 colorkey;
> +	u32 colorkey_high;
> +	u8	transparency;
> +	bool dirty;
> +};
> +
> +struct dc_hw_roi {
> +	u16 x;
> +	u16 y;
> +	u16 width;
> +	u16 height;
> +	bool enable;
> +	bool dirty;
> +};
> +
> +struct dc_hw_cursor {
> +	u32 address;
> +	u16 x;
> +	u16 y;
> +	u16 hot_x;
> +	u16 hot_y;
> +	u8	size;
> +	u8	display_id;
> +	bool	enable;
> +	bool	dirty;
> +};
> +
> +struct dc_hw_display {
> +	u32 bus_format;
> +	u16 h_active;
> +	u16 h_total;
> +	u16 h_sync_start;
> +	u16 h_sync_end;
> +	u16 v_active;
> +	u16 v_total;
> +	u16 v_sync_start;
> +	u16 v_sync_end;
> +	u8	id;
> +	bool	h_sync_polarity;
> +	bool	v_sync_polarity;
> +	bool	enable;
> +};
> +
> +struct dc_hw_gamma {
> +	u16 gamma[GAMMA_EX_SIZE][3];
> +	bool	enable;
> +	bool	dirty;
> +};
> +
> +struct dc_hw_degamma {
> +	u16 degamma[DEGAMMA_SIZE][3];
> +	u32 mode;
> +	bool	dirty;
> +};
> +
> +struct dc_hw_plane {
> +	struct dc_hw_fb			fb;
> +	struct dc_hw_position	pos;
> +	struct dc_hw_scale		scale;
> +	struct dc_hw_blend		blend;
> +	struct dc_hw_roi		roi;
> +	struct dc_hw_colorkey	colorkey;
> +	struct dc_hw_degamma	degamma;
> +};
> +
> +struct dc_hw_qos {
> +	u8	  low_value;
> +	u8	  high_value;
> +	bool  dirty;
> +};
> +
> +struct dc_hw_read {
> +	u32			reg;
> +	u32			value;
> +};
> +
> +struct dc_hw;
> +struct dc_hw_funcs {
> +	void (*gamma)(struct dc_hw *hw);
> +	void (*plane)(struct dc_hw *hw);
> +	void (*display)(struct dc_hw *hw, struct dc_hw_display *display);
> +};
> +
> +struct dc_hw {
> +	enum dc_chip_rev rev;
> +	enum dc_hw_out		out[DC_DISPLAY_NUM];
> +	void			*hi_base;
> +	void			*reg_base;
> +
> +	struct dc_hw_display display[DC_DISPLAY_NUM];
> +	struct dc_hw_gamma	 gamma[DC_DISPLAY_NUM];
> +	struct dc_hw_plane	 plane[DC_LAYER_NUM];
> +	struct dc_hw_cursor  cursor[DC_CURSOR_NUM];
> +	struct dc_hw_qos	 qos;
> +	struct dc_hw_funcs	 *func;
> +	struct vs_dc_info	 *info;
> +};
> +
> +int dc_hw_init(struct dc_hw *hw);
> +void dc_hw_deinit(struct dc_hw *hw);
> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
> +			struct dc_hw_fb *fb, struct dc_hw_scale *scale,
> +			struct dc_hw_position *pos, struct dc_hw_blend *blend);
> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode);
> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi);
> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
> +			   struct dc_hw_colorkey *colorkey);
> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos);
> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor);
> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
> +			u16 r, u16 g, u16 b);
> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable);
> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display);
> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable);
> +u32 dc_hw_get_interrupt(struct dc_hw *hw);
> +bool dc_hw_check_underflow(struct dc_hw *hw);
> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable);
> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id);
> +void dc_hw_commit(struct dc_hw *hw);
> +
> +#endif /* __VS_DC_HW_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
> index da7698c3d..3cd533cfa 100644
> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> @@ -21,6 +21,7 @@
>   
>   #include "vs_drv.h"
>   #include "vs_modeset.h"
> +#include "vs_dc.h"
>   
>   #define DRV_NAME	"verisilicon"
>   #define DRV_DESC	"Verisilicon DRM driver"
> @@ -123,6 +124,7 @@ static const struct component_master_ops vs_drm_ops = {
>   };
>   
>   static struct platform_driver *drm_sub_drivers[] = {
> +	&dc_platform_driver,
>   
>   };
>   
> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c
> new file mode 100644
> index 000000000..9bd066015
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_plane.c
> @@ -0,0 +1,526 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_blend.h>
> +#include <drm/drm_gem_dma_helper.h>
> +#include <drm/drm_fb_dma_helper.h>
> +#include <drm/drm_framebuffer.h>
> +#include <drm/drm_plane.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include <drm/vs_drm.h>
> +
> +#include "vs_plane.h"
> +#include "vs_drv.h"
> +#include "vs_dc.h"
> +
> +static void vs_plane_reset(struct drm_plane *plane)
> +{
> +	struct vs_plane_state *state;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +
> +	if (plane->state) {
> +		__drm_atomic_helper_plane_destroy_state(plane->state);
> +
> +		state = to_vs_plane_state(plane->state);
> +		kfree(state);
> +		plane->state = NULL;
> +	}
> +

Use vs_plane_destroy_state directly. This way you won't miss a refcount 
on blobs.

> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return;
> +
> +	state->degamma = VS_DEGAMMA_DISABLE;
> +	state->degamma_changed = false;
> +	state->base.zpos = vs_plane->id;
> +	memset(&state->status, 0, sizeof(state->status));
> +
> +	__drm_atomic_helper_plane_reset(plane, &state->base);
> +}
> +
> +static void _vs_plane_duplicate_blob(struct vs_plane_state *state,
> +				     struct vs_plane_state *ori_state)
> +{
> +	state->watermark = ori_state->watermark;
> +	state->color_mgmt = ori_state->color_mgmt;
> +	state->roi = ori_state->roi;
> +
> +	if (state->watermark)
> +		drm_property_blob_get(state->watermark);
> +	if (state->color_mgmt)
> +		drm_property_blob_get(state->color_mgmt);
> +	if (state->roi)
> +		drm_property_blob_get(state->roi);
> +}
> +
> +static int
> +_vs_plane_set_property_blob_from_id(struct drm_device *dev,
> +				    struct drm_property_blob **blob,
> +				    u64 blob_id,
> +				    size_t expected_size)
> +{
> +	struct drm_property_blob *new_blob = NULL;
> +
> +	if (blob_id) {
> +		new_blob = drm_property_lookup_blob(dev, blob_id);
> +		if (!new_blob)
> +			return -EINVAL;
> +
> +		if (new_blob->length != expected_size) {
> +			drm_property_blob_put(new_blob);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	drm_property_replace_blob(blob, new_blob);
> +	drm_property_blob_put(new_blob);
> +
> +	return 0;
> +}
> +
> +static struct drm_plane_state *
> +vs_plane_atomic_duplicate_state(struct drm_plane *plane)
> +{
> +	struct vs_plane_state *ori_state;
> +	struct vs_plane_state *state;
> +
> +	if (WARN_ON(!plane->state))
> +		return NULL;
> +
> +	ori_state = to_vs_plane_state(plane->state);
> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return NULL;
> +
> +	__drm_atomic_helper_plane_duplicate_state(plane, &state->base);
> +
> +	state->degamma = ori_state->degamma;
> +	state->degamma_changed = ori_state->degamma_changed;
> +
> +	_vs_plane_duplicate_blob(state, ori_state);
> +	memcpy(&state->status, &ori_state->status, sizeof(ori_state->status));
> +
> +	return &state->base;
> +}
> +
> +static void vs_plane_atomic_destroy_state(struct drm_plane *plane,
> +					  struct drm_plane_state *state)
> +{
> +	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
> +
> +	__drm_atomic_helper_plane_destroy_state(state);
> +
> +	drm_property_blob_put(vs_plane_state->watermark);
> +	drm_property_blob_put(vs_plane_state->color_mgmt);
> +	drm_property_blob_put(vs_plane_state->roi);
> +	kfree(vs_plane_state);
> +}
> +
> +static int vs_plane_atomic_set_property(struct drm_plane *plane,
> +					struct drm_plane_state *state,
> +					struct drm_property *property,
> +					uint64_t val)
> +{
> +	struct drm_device *dev = plane->dev;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
> +	int ret = 0;
> +
> +	if (property == vs_plane->degamma_mode) {
> +		if (vs_plane_state->degamma != val) {
> +			vs_plane_state->degamma = val;
> +			vs_plane_state->degamma_changed = true;
> +		} else {
> +			vs_plane_state->degamma_changed = false;
> +		}
> +	} else if (property == vs_plane->watermark_prop) {
> +		ret = _vs_plane_set_property_blob_from_id(dev,
> +							  &vs_plane_state->watermark,
> +							  val,
> +							  sizeof(struct drm_vs_watermark));
> +		return ret;
> +	} else if (property == vs_plane->color_mgmt_prop) {
> +		ret = _vs_plane_set_property_blob_from_id(dev,
> +							  &vs_plane_state->color_mgmt,
> +							  val,
> +							  sizeof(struct drm_vs_color_mgmt));
> +		return ret;
> +	} else if (property == vs_plane->roi_prop) {
> +		ret = _vs_plane_set_property_blob_from_id(dev,
> +							  &vs_plane_state->roi,
> +							  val,
> +							  sizeof(struct drm_vs_roi));
> +		return ret;
> +	} else {
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int vs_plane_atomic_get_property(struct drm_plane *plane,
> +					const struct drm_plane_state *state,
> +					struct drm_property *property,
> +					uint64_t *val)
> +{
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +	const struct vs_plane_state *vs_plane_state =
> +		container_of(state, const struct vs_plane_state, base);
> +
> +	if (property == vs_plane->degamma_mode)
> +		*val = vs_plane_state->degamma;
> +	else if (property == vs_plane->watermark_prop)
> +		*val = (vs_plane_state->watermark) ?
> +					vs_plane_state->watermark->base.id : 0;
> +	else if (property == vs_plane->color_mgmt_prop)
> +		*val = (vs_plane_state->color_mgmt) ?
> +					vs_plane_state->color_mgmt->base.id : 0;

degamma and color management should use standard properties.

> +	else if (property == vs_plane->roi_prop)
> +		*val = (vs_plane_state->roi) ?
> +					vs_plane_state->roi->base.id : 0;
> +	else
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static bool vs_format_mod_supported(struct drm_plane *plane,
> +				    u32 format,
> +				    u64 modifier)
> +{
> +	int i;
> +
> +	/* We always have to allow these modifiers:
> +	 * 1. Core DRM checks for LINEAR support if userspace does not provide modifiers.
> +	 * 2. Not passing any modifiers is the same as explicitly passing INVALID.
> +	 */
> +	if (modifier == DRM_FORMAT_MOD_LINEAR)
> +		return true;
> +
> +	/* Check that the modifier is on the list of the plane's supported modifiers. */
> +	for (i = 0; i < plane->modifier_count; i++) {
> +		if (modifier == plane->modifiers[i])
> +			break;
> +	}
> +
> +	if (i == plane->modifier_count)
> +		return false;
> +
> +	return true;
> +}
> +
> +const struct drm_plane_funcs vs_plane_funcs = {
> +	.update_plane		= drm_atomic_helper_update_plane,
> +	.disable_plane		= drm_atomic_helper_disable_plane,
> +	.reset			= vs_plane_reset,
> +	.atomic_duplicate_state = vs_plane_atomic_duplicate_state,
> +	.atomic_destroy_state	= vs_plane_atomic_destroy_state,
> +	.atomic_set_property	= vs_plane_atomic_set_property,
> +	.atomic_get_property	= vs_plane_atomic_get_property,
> +	.format_mod_supported	= vs_format_mod_supported,
> +};
> +
> +static unsigned char vs_get_plane_number(struct drm_framebuffer *fb)
> +{
> +	const struct drm_format_info *info;
> +
> +	if (!fb)
> +		return 0;
> +
> +	info = drm_format_info(fb->format->format);
> +	if (!info || info->num_planes > DRM_FORMAT_MAX_PLANES)
> +		return 0;
> +
> +	return info->num_planes;
> +}
> +
> +static int vs_plane_atomic_check(struct drm_plane *plane,
> +				 struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> +										 plane);
> +	unsigned char i, num_planes;
> +	struct drm_framebuffer *fb = new_plane_state->fb;
> +	struct drm_crtc *crtc = new_plane_state->crtc;
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
> +
> +	if (!crtc || !fb)
> +		return 0;
> +
> +	num_planes = vs_get_plane_number(fb);
> +
> +	for (i = 0; i < num_planes; i++) {
> +		dma_addr_t dma_addr;
> +
> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
> +		plane_state->dma_addr[i] = dma_addr;
> +	}
> +
> +	return vs_dc_check_plane(dc, plane, state);
> +}
> +
> +static int vs_cursor_plane_atomic_check(struct drm_plane *plane,
> +					struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> +									  plane);
> +	unsigned char i, num_planes;
> +	struct drm_framebuffer *fb = new_plane_state->fb;
> +	struct drm_crtc *crtc = new_plane_state->crtc;
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
> +
> +	if (!crtc || !fb)
> +		return 0;
> +
> +	num_planes = vs_get_plane_number(fb);
> +
> +	for (i = 0; i < num_planes; i++) {
> +		dma_addr_t dma_addr;
> +
> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
> +		plane_state->dma_addr[i] = dma_addr;
> +	}
> +
> +	return vs_dc_check_cursor_plane(dc, plane, state);
> +}
> +
> +static void vs_plane_atomic_update(struct drm_plane *plane,
> +				   struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
> +									  plane);

New line after the equal sign will be better.

> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> +									  plane);
> +
> +	unsigned char i, num_planes;
> +	struct drm_framebuffer *fb;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +	struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	if (!new_state->fb || !new_state->crtc)
> +		return;

if (!new_state->visible) ?

> +
> +	fb = new_state->fb;
> +
> +	drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
> +
> +	num_planes = vs_get_plane_number(fb);
> +
> +	for (i = 0; i < num_planes; i++) {
> +		dma_addr_t dma_addr;
> +
> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
> +		plane_state->dma_addr[i] = dma_addr;
> +	}
> +
> +	plane_state->status.src = drm_plane_state_src(new_state);
> +	plane_state->status.dest = drm_plane_state_dest(new_state);
> +
> +	vs_dc_update_plane(dc, vs_plane, plane, state);
> +}
> +
> +static void vs_cursor_plane_atomic_update(struct drm_plane *plane,
> +					  struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
> +									   plane);
> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> +									   plane);
> +	unsigned char i, num_planes;
> +	struct drm_framebuffer *fb;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +	struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	if (!new_state->fb || !new_state->crtc)

and here.

> +		return;
> +
> +	fb = new_state->fb;
> +	drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
> +
> +	num_planes = vs_get_plane_number(fb);
> +
> +	for (i = 0; i < num_planes; i++) {
> +		dma_addr_t dma_addr;
> +
> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
> +		plane_state->dma_addr[i] = dma_addr;
> +	}
> +
> +	plane_state->status.src = drm_plane_state_src(new_state);
> +	plane_state->status.dest = drm_plane_state_dest(new_state);
> +
> +	vs_dc_update_cursor_plane(dc, vs_plane, plane, state);
> +}
> +
> +static void vs_plane_atomic_disable(struct drm_plane *plane,
> +				    struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> +									   plane);
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +	struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_disable_plane(dc, vs_plane, old_state);
> +}
> +
> +static void vs_cursor_plane_atomic_disable(struct drm_plane *plane,
> +					   struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> +									   plane);
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +	struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_disable_cursor_plane(dc, vs_plane, old_state);
> +}
> +
> +const struct drm_plane_helper_funcs primary_plane_helpers = {
> +	.atomic_check	= vs_plane_atomic_check,
> +	.atomic_update	= vs_plane_atomic_update,
> +	.atomic_disable = vs_plane_atomic_disable,
> +};
> +
> +const struct drm_plane_helper_funcs overlay_plane_helpers = {
> +	.atomic_check	= vs_plane_atomic_check,
> +	.atomic_update	= vs_plane_atomic_update,
> +	.atomic_disable = vs_plane_atomic_disable,
> +};
> +
> +const struct drm_plane_helper_funcs cursor_plane_helpers = {
> +	.atomic_check	= vs_cursor_plane_atomic_check,
> +	.atomic_update	= vs_cursor_plane_atomic_update,
> +	.atomic_disable = vs_cursor_plane_atomic_disable,
> +};
> +
> +static const struct drm_prop_enum_list vs_degamma_mode_enum_list[] = {
> +	{ VS_DEGAMMA_DISABLE,	"disabled" },
> +	{ VS_DEGAMMA_BT709,	"preset degamma for BT709" },
> +	{ VS_DEGAMMA_BT2020,	"preset degamma for BT2020" },
> +};
> +
> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
> +				 struct vs_plane_info *info,
> +				 unsigned int layer_num,
> +				 unsigned int possible_crtcs)
> +{
> +	struct vs_plane *plane;
> +	int ret;
> +
> +	if (!info)
> +		return NULL;
> +
> +	plane = drmm_universal_plane_alloc(drm_dev, struct vs_plane, base,
> +					   possible_crtcs,
> +					   &vs_plane_funcs,
> +					   info->formats, info->num_formats,
> +					   info->modifiers, info->type,
> +					   info->name ? info->name : NULL);
> +	if (IS_ERR(plane))
> +		return ERR_CAST(plane);
> +
> +	if (info->type == DRM_PLANE_TYPE_PRIMARY)
> +		drm_plane_helper_add(&plane->base, &primary_plane_helpers);
> +	else if (info->type == DRM_PLANE_TYPE_CURSOR)
> +		drm_plane_helper_add(&plane->base, &cursor_plane_helpers);
> +	else
> +		drm_plane_helper_add(&plane->base, &overlay_plane_helpers);
> +
> +	/* Set up the plane properties */
> +	if (info->degamma_size) {
> +		plane->degamma_mode =
> +		drm_property_create_enum(drm_dev, 0,
> +					 "DEGAMMA_MODE",
> +					 vs_degamma_mode_enum_list,
> +					 ARRAY_SIZE(vs_degamma_mode_enum_list));
> +
> +		if (!plane->degamma_mode)
> +			return NULL;
> +
> +		drm_object_attach_property(&plane->base.base,
> +					   plane->degamma_mode,
> +					   VS_DEGAMMA_DISABLE);
> +	}
> +
> +	if (info->rotation) {
> +		ret = drm_plane_create_rotation_property(&plane->base,
> +							 DRM_MODE_ROTATE_0,
> +							 info->rotation);
> +		if (ret)
> +			return NULL;
> +	}
> +
> +	if (info->blend_mode) {
> +		ret = drm_plane_create_blend_mode_property(&plane->base,
> +							   info->blend_mode);
> +		if (ret)
> +			return NULL;
> +		ret = drm_plane_create_alpha_property(&plane->base);
> +		if (ret)
> +			return NULL;
> +	}
> +
> +	if (info->color_encoding) {
> +		ret = drm_plane_create_color_properties(&plane->base,
> +							info->color_encoding,
> +							BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
> +							DRM_COLOR_YCBCR_BT709,
> +							DRM_COLOR_YCBCR_LIMITED_RANGE);
> +		if (ret)
> +			return NULL;
> +	}
> +
> +	if (info->zpos != 255) {
> +		ret = drm_plane_create_zpos_property(&plane->base, info->zpos, 0,
> +						     layer_num - 1);
> +		if (ret)
> +			return NULL;
> +	} else {
> +		ret = drm_plane_create_zpos_immutable_property(&plane->base,
> +							       info->zpos);
> +		if (ret)
> +			return NULL;
> +	}
> +
> +	if (info->watermark) {
> +		plane->watermark_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
> +							    "WATERMARK", 0);
> +		if (!plane->watermark_prop)
> +			return NULL;
> +		drm_object_attach_property(&plane->base.base, plane->watermark_prop, 0);
> +	}
> +
> +	if (info->color_mgmt) {
> +		plane->color_mgmt_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
> +							     "COLOR_CONFIG", 0);
> +		if (!plane->color_mgmt_prop)
> +			return NULL;
> +
> +		drm_object_attach_property(&plane->base.base, plane->color_mgmt_prop, 0);
> +	}
> +
> +	if (info->roi) {
> +		plane->roi_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
> +						      "ROI", 0);
> +		if (!plane->roi_prop)
> +			return NULL;
> +
> +		drm_object_attach_property(&plane->base.base, plane->roi_prop, 0);
> +	}
> +
> +	return plane;
> +}
> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h
> new file mode 100644
> index 000000000..554b74e96
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_plane.h
> @@ -0,0 +1,58 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_PLANE_H__
> +#define __VS_PLANE_H__
> +
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include "vs_type.h"
> +
> +struct vs_plane_status {
> +	u32 tile_mode;
> +	struct drm_rect src;
> +	struct drm_rect dest;
> +};
> +
> +struct vs_plane_state {
> +	struct drm_plane_state base;
> +	struct vs_plane_status status; /* for debugfs */
> +
> +	struct drm_property_blob *watermark;
> +	struct drm_property_blob *color_mgmt;
> +	struct drm_property_blob *roi;
> +	dma_addr_t dma_addr[DRM_FORMAT_MAX_PLANES];
> +
> +	u32 degamma;
> +	bool degamma_changed;
> +};
> +
> +struct vs_plane {
> +	struct drm_plane base;
> +	u8 id;
> +
> +	struct drm_property *degamma_mode;
> +	struct drm_property *watermark_prop;
> +	struct drm_property *color_mgmt_prop;
> +	struct drm_property *roi_prop;
> +};
> +
> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
> +				 struct vs_plane_info *info,
> +				 unsigned int layer_num,
> +				 unsigned int possible_crtcs);
> +
> +static inline struct vs_plane *to_vs_plane(struct drm_plane *plane)
> +{
> +	return container_of(plane, struct vs_plane, base);
> +}
> +
> +static inline struct vs_plane_state *
> +to_vs_plane_state(struct drm_plane_state *state)
> +{
> +	return container_of(state, struct vs_plane_state, base);
> +}
> +#endif /* __VS_PLANE_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_type.h b/drivers/gpu/drm/verisilicon/vs_type.h
> new file mode 100644
> index 000000000..7d3378e29
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_type.h
> @@ -0,0 +1,69 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_TYPE_H__
> +#define __VS_TYPE_H__
> +
> +#include <drm/drm_plane.h>
> +#include <drm/drm_plane_helper.h>
> +
> +struct vs_plane_info {
> +	const char *name;
> +	u8 id;
> +	enum drm_plane_type type;
> +	unsigned int num_formats;
> +	const u32 *formats;
> +	u8 num_modifiers;
> +	const u64 *modifiers;
> +	unsigned int min_width;
> +	unsigned int min_height;
> +	unsigned int max_width;
> +	unsigned int max_height;
> +	unsigned int rotation;
> +	unsigned int blend_mode;
> +	unsigned int color_encoding;
> +
> +	/* 0 means no de-gamma LUT */
> +	unsigned int degamma_size;
> +
> +	int min_scale; /* 16.16 fixed point */
> +	int max_scale; /* 16.16 fixed point */
> +
> +	/* default zorder value,
> +	 * and 255 means unsupported zorder capability
> +	 */
> +	u8	 zpos;
> +
> +	bool watermark;
> +	bool color_mgmt;
> +	bool roi;
> +};
> +
> +struct vs_dc_info {
> +	const char *name;
> +
> +	u8 panel_num;
> +
> +	/* planes */
> +	u8 plane_num;
> +	const struct vs_plane_info *planes;
> +
> +	u8 layer_num;
> +	unsigned int max_bpc;
> +	unsigned int color_formats;
> +
> +	/* 0 means no gamma LUT */
> +	u16 gamma_size;
> +	u8 gamma_bits;
> +
> +	u16 pitch_alignment;
> +
> +	bool pipe_sync;
> +	bool background;
> +	bool panel_sync;
> +	bool cap_dec;
> +};
> +
> +#endif /* __VS_TYPE_H__ */

-- 
With best wishes
Dmitry


^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-10-25 19:28     ` Dmitry Baryshkov
  0 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-10-25 19:28 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig

On 25/10/2023 13:39, Keith Zhao wrote:
> add 2 crtcs and 8 planes in vs-drm
> 
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
>   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
>   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
>   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
>   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
>   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
>   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
>   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
>   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
>   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
>   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
>   11 files changed, 4494 insertions(+), 2 deletions(-)
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
> 
> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> index 7d3be305b..1d48016ca 100644
> --- a/drivers/gpu/drm/verisilicon/Makefile
> +++ b/drivers/gpu/drm/verisilicon/Makefile
> @@ -1,7 +1,11 @@
>   # SPDX-License-Identifier: GPL-2.0
>   
> -vs_drm-objs := vs_drv.o \
> -		vs_modeset.o
> +vs_drm-objs := vs_dc_hw.o \
> +		vs_dc.o \
> +		vs_crtc.o \
> +		vs_drv.o \
> +		vs_modeset.o \
> +		vs_plane.o
>   
>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>   
> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
> new file mode 100644
> index 000000000..8a658ea77
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
> @@ -0,0 +1,257 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + *
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/debugfs.h>
> +#include <linux/media-bus-format.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_gem_atomic_helper.h>
> +#include <drm/drm_vblank.h>
> +#include <drm/vs_drm.h>
> +
> +#include "vs_crtc.h"
> +#include "vs_dc.h"
> +#include "vs_drv.h"
> +
> +static void vs_crtc_reset(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc_state *state;
> +
> +	if (crtc->state) {
> +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
> +
> +		state = to_vs_crtc_state(crtc->state);
> +		kfree(state);
> +		crtc->state = NULL;
> +	}

You can call your crtc_destroy_state function directly here.

> +
> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return;
> +
> +	__drm_atomic_helper_crtc_reset(crtc, &state->base);
> +}
> +
> +static struct drm_crtc_state *
> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc_state *ori_state;

It might be a matter of taste, but it is usually old_state.

> +	struct vs_crtc_state *state;
> +
> +	if (!crtc->state)
> +		return NULL;
> +
> +	ori_state = to_vs_crtc_state(crtc->state);
> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return NULL;
> +
> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
> +
> +	state->output_fmt = ori_state->output_fmt;
> +	state->encoder_type = ori_state->encoder_type;
> +	state->bpp = ori_state->bpp;
> +	state->underflow = ori_state->underflow;

Can you use kmemdup instead?

> +
> +	return &state->base;
> +}
> +
> +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
> +					 struct drm_crtc_state *state)
> +{
> +	__drm_atomic_helper_crtc_destroy_state(state);
> +	kfree(to_vs_crtc_state(state));
> +}
> +
> +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_enable_vblank(dc, true);
> +
> +	return 0;
> +}
> +
> +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_enable_vblank(dc, false);
> +}
> +
> +static const struct drm_crtc_funcs vs_crtc_funcs = {
> +	.set_config		= drm_atomic_helper_set_config,
> +	.page_flip		= drm_atomic_helper_page_flip,

destroy is required, see drm_mode_config_cleanup()

> +	.reset			= vs_crtc_reset,
> +	.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
> +	.atomic_destroy_state	= vs_crtc_atomic_destroy_state,

please consider adding atomic_print_state to output driver-specific bits.

> +	.enable_vblank		= vs_crtc_enable_vblank,
> +	.disable_vblank		= vs_crtc_disable_vblank,
> +};
> +
> +static u8 cal_pixel_bits(u32 bus_format)

This looks like a generic helper code, which can go to a common place.

> +{
> +	u8 bpp;
> +
> +	switch (bus_format) {
> +	case MEDIA_BUS_FMT_RGB565_1X16:
> +	case MEDIA_BUS_FMT_UYVY8_1X16:
> +		bpp = 16;
> +		break;
> +	case MEDIA_BUS_FMT_RGB666_1X18:
> +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> +		bpp = 18;
> +		break;
> +	case MEDIA_BUS_FMT_UYVY10_1X20:
> +		bpp = 20;
> +		break;
> +	case MEDIA_BUS_FMT_BGR888_1X24:
> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +	case MEDIA_BUS_FMT_YUV8_1X24:
> +		bpp = 24;
> +		break;
> +	case MEDIA_BUS_FMT_RGB101010_1X30:
> +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> +	case MEDIA_BUS_FMT_YUV10_1X30:
> +		bpp = 30;
> +		break;
> +	default:
> +		bpp = 24;
> +		break;
> +	}
> +
> +	return bpp;
> +}
> +
> +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
> +				  struct drm_atomic_state *state)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
> +
> +	vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
> +
> +	vs_dc_enable(dc, crtc);
> +	drm_crtc_vblank_on(crtc);
> +}
> +
> +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
> +				   struct drm_atomic_state *state)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	drm_crtc_vblank_off(crtc);
> +
> +	vs_dc_disable(dc, crtc);
> +
> +	if (crtc->state->event && !crtc->state->active) {
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +
> +		crtc->state->event = NULL;

I think even should be cleared within the lock.

> +	}
> +}
> +
> +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
> +				 struct drm_atomic_state *state)
> +{
> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
> +									  crtc);
> +
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct device *dev = vs_crtc->dev;
> +	struct drm_property_blob *blob = crtc->state->gamma_lut;
> +	struct drm_color_lut *lut;
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	if (crtc_state->color_mgmt_changed) {
> +		if (blob && blob->length) {
> +			lut = blob->data;
> +			vs_dc_set_gamma(dc, crtc, lut,
> +					blob->length / sizeof(*lut));
> +			vs_dc_enable_gamma(dc, crtc, true);
> +		} else {
> +			vs_dc_enable_gamma(dc, crtc, false);
> +		}
> +	}
> +}
> +
> +static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
> +				 struct drm_atomic_state *state)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct drm_pending_vblank_event *event = crtc->state->event;
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_commit(dc);
> +
> +	if (event) {
> +		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
> +
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		drm_crtc_arm_vblank_event(crtc, event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +		crtc->state->event = NULL;

I think even should be cleared within the lock.

> +	}
> +}
> +
> +static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
> +	.atomic_check = drm_crtc_helper_atomic_check,
> +	.atomic_enable	= vs_crtc_atomic_enable,
> +	.atomic_disable = vs_crtc_atomic_disable,
> +	.atomic_begin	= vs_crtc_atomic_begin,
> +	.atomic_flush	= vs_crtc_atomic_flush,
> +};
> +
> +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
> +	{ VS_SINGLE_DC,				"single dc mode" },
> +	{ VS_MULTI_DC_PRIMARY,		"primary dc for multi dc mode" },
> +	{ VS_MULTI_DC_SECONDARY,	"secondary dc for multi dc mode" },
> +};
> +
> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
> +			       struct vs_dc_info *info)
> +{
> +	struct vs_crtc *crtc;
> +	int ret;
> +
> +	if (!info)
> +		return NULL;
> +
> +	crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
> +	if (!crtc)
> +		return NULL;
> +
> +	ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
> +					 NULL, NULL, &vs_crtc_funcs,
> +					 info->name ? info->name : NULL);

It might be better to add drmm_crtc_init() helper.

> +	if (ret)
> +		return NULL;
> +
> +	drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
> +
> +	if (info->gamma_size) {
> +		ret = drm_mode_crtc_set_gamma_size(&crtc->base,
> +						   info->gamma_size);
> +		if (ret)
> +			return NULL;
> +
> +		drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
> +					   info->gamma_size);
> +	}
> +
> +	crtc->max_bpc = info->max_bpc;
> +	crtc->color_formats = info->color_formats;
> +	return crtc;
> +}
> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h
> new file mode 100644
> index 000000000..526dd63e5
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.h
> @@ -0,0 +1,43 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_CRTC_H__
> +#define __VS_CRTC_H__
> +
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "vs_type.h"
> +
> +struct vs_crtc_state {
> +	struct drm_crtc_state base;
> +
> +	u32 output_fmt;
> +	u8 encoder_type;
> +	u8 bpp;
> +	bool underflow;
> +};
> +
> +struct vs_crtc {
> +	struct drm_crtc base;
> +	struct device *dev;
> +	unsigned int max_bpc;
> +	unsigned int color_formats;
> +};
> +
> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
> +			       struct vs_dc_info *info);
> +
> +static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc)
> +{
> +	return container_of(crtc, struct vs_crtc, base);
> +}
> +
> +static inline struct vs_crtc_state *
> +to_vs_crtc_state(struct drm_crtc_state *state)
> +{
> +	return container_of(state, struct vs_crtc_state, base);
> +}
> +#endif /* __VS_CRTC_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
> new file mode 100644
> index 000000000..b5ab92d98
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc.c
> @@ -0,0 +1,1002 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/component.h>
> +#include <linux/clk.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/reset.h>
> +
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_blend.h>
> +#include <drm/drm_framebuffer.h>
> +#include <drm/drm_vblank.h>
> +#include <drm/vs_drm.h>
> +
> +#include "vs_dc_hw.h"
> +#include "vs_dc.h"
> +#include "vs_drv.h"
> +
> +static const char * const vout_clocks[] = {
> +	"noc_bus",
> +	"channel0",
> +	"channel1",
> +	"dc_core",
> +	"axi_core",
> +	"ahb",
> +	"hdmi_tx",
> +	"dc_parent",
> +
> +};
> +
> +static const char * const vout_resets[] = {
> +	"axi",
> +	"ahb",
> +	"core",
> +};
> +
> +static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb)
> +{
> +	u8 f = FORMAT_A8R8G8B8;
> +
> +	switch (format) {
> +	case DRM_FORMAT_XRGB4444:
> +	case DRM_FORMAT_RGBX4444:
> +	case DRM_FORMAT_XBGR4444:
> +	case DRM_FORMAT_BGRX4444:
> +		f = FORMAT_X4R4G4B4;
> +		break;
> +	case DRM_FORMAT_ARGB4444:
> +	case DRM_FORMAT_RGBA4444:
> +	case DRM_FORMAT_ABGR4444:
> +	case DRM_FORMAT_BGRA4444:
> +		f = FORMAT_A4R4G4B4;
> +		break;
> +	case DRM_FORMAT_XRGB1555:
> +	case DRM_FORMAT_RGBX5551:
> +	case DRM_FORMAT_XBGR1555:
> +	case DRM_FORMAT_BGRX5551:
> +		f = FORMAT_X1R5G5B5;
> +		break;
> +	case DRM_FORMAT_ARGB1555:
> +	case DRM_FORMAT_RGBA5551:
> +	case DRM_FORMAT_ABGR1555:
> +	case DRM_FORMAT_BGRA5551:
> +		f = FORMAT_A1R5G5B5;
> +		break;
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		f = FORMAT_R5G6B5;
> +		break;
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_RGBX8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_BGRX8888:
> +		f = FORMAT_X8R8G8B8;
> +		break;
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_RGBA8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_BGRA8888:
> +		f = FORMAT_A8R8G8B8;
> +		break;
> +	case DRM_FORMAT_YUYV:
> +	case DRM_FORMAT_YVYU:
> +		f = FORMAT_YUY2;
> +		break;
> +	case DRM_FORMAT_UYVY:
> +	case DRM_FORMAT_VYUY:
> +		f = FORMAT_UYVY;
> +		break;
> +	case DRM_FORMAT_YUV420:
> +	case DRM_FORMAT_YVU420:
> +		f = FORMAT_YV12;
> +		break;
> +	case DRM_FORMAT_NV21:
> +		f = FORMAT_NV12;
> +		break;
> +	case DRM_FORMAT_NV16:
> +	case DRM_FORMAT_NV61:
> +		f = FORMAT_NV16;
> +		break;
> +	case DRM_FORMAT_P010:
> +		f = FORMAT_P010;
> +		break;
> +	case DRM_FORMAT_ARGB2101010:
> +	case DRM_FORMAT_RGBA1010102:
> +	case DRM_FORMAT_ABGR2101010:
> +	case DRM_FORMAT_BGRA1010102:
> +		f = FORMAT_A2R10G10B10;
> +		break;
> +	case DRM_FORMAT_NV12:
> +		f = FORMAT_NV12;
> +		break;
> +	case DRM_FORMAT_YUV444:
> +		f = FORMAT_YUV444;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	fb->format = f;
> +}
> +
> +static inline void update_swizzle(u32 format, struct dc_hw_fb *fb)
> +{
> +	fb->swizzle = SWIZZLE_ARGB;
> +	fb->uv_swizzle = 0;
> +
> +	switch (format) {
> +	case DRM_FORMAT_RGBX4444:
> +	case DRM_FORMAT_RGBA4444:
> +	case DRM_FORMAT_RGBX5551:
> +	case DRM_FORMAT_RGBA5551:
> +	case DRM_FORMAT_RGBX8888:
> +	case DRM_FORMAT_RGBA8888:
> +	case DRM_FORMAT_RGBA1010102:
> +		fb->swizzle = SWIZZLE_RGBA;
> +		break;
> +	case DRM_FORMAT_XBGR4444:
> +	case DRM_FORMAT_ABGR4444:
> +	case DRM_FORMAT_XBGR1555:
> +	case DRM_FORMAT_ABGR1555:
> +	case DRM_FORMAT_BGR565:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_ABGR2101010:
> +		fb->swizzle = SWIZZLE_ABGR;
> +		break;
> +	case DRM_FORMAT_BGRX4444:
> +	case DRM_FORMAT_BGRA4444:
> +	case DRM_FORMAT_BGRX5551:
> +	case DRM_FORMAT_BGRA5551:
> +	case DRM_FORMAT_BGRX8888:
> +	case DRM_FORMAT_BGRA8888:
> +	case DRM_FORMAT_BGRA1010102:
> +		fb->swizzle = SWIZZLE_BGRA;
> +		break;
> +	case DRM_FORMAT_YVYU:
> +	case DRM_FORMAT_VYUY:
> +	case DRM_FORMAT_NV21:
> +	case DRM_FORMAT_NV61:
> +		fb->uv_swizzle = 1;
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +static inline void update_watermark(struct drm_property_blob *watermark,
> +				    struct dc_hw_fb *fb)
> +{
> +	struct drm_vs_watermark *data;
> +
> +	fb->water_mark = 0;
> +
> +	if (watermark) {
> +		data = watermark->data;
> +		fb->water_mark = data->watermark & 0xFFFFF;
> +	}
> +}
> +
> +static inline u8 to_vs_rotation(unsigned int rotation)
> +{
> +	u8 rot;
> +
> +	switch (rotation & DRM_MODE_REFLECT_MASK) {
> +	case DRM_MODE_REFLECT_X:
> +		rot = FLIP_X;
> +		return rot;
> +	case DRM_MODE_REFLECT_Y:
> +		rot = FLIP_Y;
> +		return rot;
> +	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
> +		rot = FLIP_XY;
> +		return rot;
> +	default:
> +		break;
> +	}
> +
> +	switch (rotation & DRM_MODE_ROTATE_MASK) {
> +	case DRM_MODE_ROTATE_0:
> +		rot = ROT_0;
> +		break;
> +	case DRM_MODE_ROTATE_90:
> +		rot = ROT_90;
> +		break;
> +	case DRM_MODE_ROTATE_180:
> +		rot = ROT_180;
> +		break;
> +	case DRM_MODE_ROTATE_270:
> +		rot = ROT_270;
> +		break;
> +	default:
> +		rot = ROT_0;
> +		break;
> +	}
> +
> +	return rot;
> +}
> +
> +static inline u8 to_vs_yuv_color_space(u32 color_space)
> +{
> +	u8 cs;
> +
> +	switch (color_space) {
> +	case DRM_COLOR_YCBCR_BT601:
> +		cs = COLOR_SPACE_601;
> +		break;
> +	case DRM_COLOR_YCBCR_BT709:
> +		cs = COLOR_SPACE_709;
> +		break;
> +	case DRM_COLOR_YCBCR_BT2020:
> +		cs = COLOR_SPACE_2020;
> +		break;
> +	default:
> +		cs = COLOR_SPACE_601;
> +		break;
> +	}
> +
> +	return cs;
> +}
> +
> +static inline u8 to_vs_tile_mode(u64 modifier)
> +{
> +	if (modifier == DRM_FORMAT_MOD_VIVANTE_TILED)
> +		return DC_TILE_MODE4X4;
> +
> +	return (u8)(modifier & DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK);
> +}
> +
> +static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +	u8 panel_num = dc->hw.info->panel_num;
> +	u32 index = drm_crtc_index(crtc);
> +	int i;
> +
> +	for (i = 0; i < panel_num; i++) {
> +		if (index == dc->crtc[i]->base.index)
> +			return i;
> +	}
> +
> +	return 0;
> +}
> +
> +static void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
> +					  unsigned int alignment)
> +{
> +	struct vs_drm_device *priv = to_vs_drm_private(drm_dev);
> +
> +	if (alignment > priv->pitch_alignment)
> +		priv->pitch_alignment = alignment;
> +}
> +
> +static int plda_clk_rst_init(struct device *dev)
> +{
> +	int ret = 0;
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	ret = clk_bulk_prepare_enable(dc->nclks, dc->clk_vout);
> +	if (ret) {
> +		dev_err(dev, "failed to enable clocks\n");
> +		return ret;
> +	}
> +
> +	ret = reset_control_bulk_deassert(dc->nrsts, dc->rst_vout);
> +	return ret;
> +}
> +
> +static void plda_clk_rst_deinit(struct device *dev)
> +{
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	reset_control_bulk_assert(dc->nrsts, dc->rst_vout);
> +	clk_bulk_disable_unprepare(dc->nclks, dc->clk_vout);
> +}
> +
> +static void dc_deinit(struct device *dev)
> +{
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	dc_hw_enable_interrupt(&dc->hw, 0);
> +	dc_hw_deinit(&dc->hw);
> +	plda_clk_rst_deinit(dev);
> +}
> +
> +static int dc_init(struct device *dev)
> +{
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +	int ret;
> +
> +	dc->first_frame = true;
> +
> +	ret = plda_clk_rst_init(dev);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to init dc clk reset: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = dc_hw_init(&dc->hw);
> +	if (ret) {
> +		dev_err(dev, "failed to init DC HW\n");
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +	struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	struct dc_hw_display display;
> +
> +	display.bus_format = crtc_state->output_fmt;
> +	display.h_active = mode->hdisplay;
> +	display.h_total = mode->htotal;
> +	display.h_sync_start = mode->hsync_start;
> +	display.h_sync_end = mode->hsync_end;
> +	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> +		display.h_sync_polarity = true;
> +	else
> +		display.h_sync_polarity = false;
> +
> +	display.v_active = mode->vdisplay;
> +	display.v_total = mode->vtotal;
> +	display.v_sync_start = mode->vsync_start;
> +	display.v_sync_end = mode->vsync_end;
> +
> +	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> +		display.v_sync_polarity = true;
> +	else
> +		display.v_sync_polarity = false;
> +
> +	display.id = to_vs_display_id(dc, crtc);
> +
> +	display.enable = true;
> +
> +	if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) {
> +		dc_hw_set_out(&dc->hw, OUT_DPI, display.id);
> +		clk_set_rate(dc->clk_vout[CLK_VOUT_SOC_PIX].clk, mode->clock * 1000);
> +		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX1].clk,
> +			       dc->clk_vout[CLK_VOUT_SOC_PIX].clk);
> +	} else {
> +		dc_hw_set_out(&dc->hw, OUT_DP, display.id);
> +		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX0].clk,
> +			       dc->clk_vout[CLK_VOUT_HDMI_PIX].clk);
> +	}
> +
> +	dc_hw_setup_display(&dc->hw, &display);
> +}
> +
> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +	struct dc_hw_display display;
> +
> +	display.id = to_vs_display_id(dc, crtc);
> +	display.enable = false;
> +
> +	dc_hw_setup_display(&dc->hw, &display);
> +}
> +
> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> +		     struct drm_color_lut *lut, unsigned int size)
> +{
> +	u16 i, r, g, b;
> +	u8 bits, id;
> +
> +	if (size != dc->hw.info->gamma_size) {
> +		drm_err(crtc->dev, "gamma size does not match!\n");
> +		return;
> +	}
> +
> +	id = to_vs_display_id(dc, crtc);
> +
> +	bits = dc->hw.info->gamma_bits;
> +	for (i = 0; i < size; i++) {
> +		r = drm_color_lut_extract(lut[i].red, bits);
> +		g = drm_color_lut_extract(lut[i].green, bits);
> +		b = drm_color_lut_extract(lut[i].blue, bits);
> +		dc_hw_update_gamma(&dc->hw, id, i, r, g, b);
> +	}
> +}
> +
> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> +			bool enable)
> +{
> +	u8 id;
> +
> +	id = to_vs_display_id(dc, crtc);
> +	dc_hw_enable_gamma(&dc->hw, id, enable);
> +}
> +
> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable)
> +{
> +	dc_hw_enable_interrupt(&dc->hw, enable);
> +}
> +
> +static u32 calc_factor(u32 src, u32 dest)
> +{
> +	u32 factor = 1 << 16;
> +
> +	if (src > 1 && dest > 1)
> +		factor = ((src - 1) << 16) / (dest - 1);
> +
> +	return factor;
> +}
> +
> +static void update_scale(struct drm_plane_state *state, struct dc_hw_roi *roi,
> +			 struct dc_hw_scale *scale)
> +{
> +	int dst_w = drm_rect_width(&state->dst);
> +	int dst_h = drm_rect_height(&state->dst);
> +	int src_w, src_h, temp;
> +
> +	scale->enable = false;
> +
> +	if (roi->enable) {
> +		src_w = roi->width;
> +		src_h = roi->height;
> +	} else {
> +		src_w = drm_rect_width(&state->src) >> 16;
> +		src_h = drm_rect_height(&state->src) >> 16;
> +	}
> +
> +	if (drm_rotation_90_or_270(state->rotation)) {
> +		temp = src_w;
> +		src_w = src_h;
> +		src_h = temp;
> +	}
> +
> +	if (src_w != dst_w) {
> +		scale->scale_factor_x = calc_factor(src_w, dst_w);
> +		scale->enable = true;
> +	} else {
> +		scale->scale_factor_x = 1 << 16;
> +	}
> +	if (src_h != dst_h) {
> +		scale->scale_factor_y = calc_factor(src_h, dst_h);
> +		scale->enable = true;
> +	} else {
> +		scale->scale_factor_y = 1 << 16;
> +	}
> +}
> +
> +static void update_fb(struct vs_plane *plane, u8 display_id,
> +		      struct dc_hw_fb *fb, struct drm_plane_state *state)
> +{
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_framebuffer *drm_fb = state->fb;
> +	struct drm_rect *src = &state->src;
> +
> +	fb->display_id = display_id;
> +	fb->y_address = plane_state->dma_addr[0];
> +	fb->y_stride = drm_fb->pitches[0];
> +	if (drm_fb->format->format == DRM_FORMAT_YVU420) {
> +		fb->u_address = plane_state->dma_addr[2];
> +		fb->v_address = plane_state->dma_addr[1];
> +		fb->u_stride = drm_fb->pitches[2];
> +		fb->v_stride = drm_fb->pitches[1];
> +	} else {
> +		fb->u_address = plane_state->dma_addr[1];
> +		fb->v_address = plane_state->dma_addr[2];
> +		fb->u_stride = drm_fb->pitches[1];
> +		fb->v_stride = drm_fb->pitches[2];
> +	}
> +	fb->width = drm_rect_width(src) >> 16;
> +	fb->height = drm_rect_height(src) >> 16;
> +	fb->tile_mode = to_vs_tile_mode(drm_fb->modifier);
> +	fb->rotation = to_vs_rotation(state->rotation);
> +	fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding);
> +	fb->zpos = state->zpos;
> +	fb->enable = state->visible;
> +	update_format(drm_fb->format->format, drm_fb->modifier, fb);
> +	update_swizzle(drm_fb->format->format, fb);
> +	update_watermark(plane_state->watermark, fb);
> +	plane_state->status.tile_mode = fb->tile_mode;
> +}
> +
> +static void update_degamma(struct vs_dc *dc, struct vs_plane *plane,
> +			   struct vs_plane_state *plane_state)
> +{
> +	dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
> +	plane_state->degamma_changed = false;
> +}
> +
> +static void update_roi(struct vs_dc *dc, u8 id,
> +		       struct vs_plane_state *plane_state,
> +		       struct dc_hw_roi *roi,
> +		       struct drm_plane_state *state)
> +{
> +	struct drm_vs_roi *data;
> +	struct drm_rect *src = &state->src;
> +	u16 src_w = drm_rect_width(src) >> 16;
> +	u16 src_h = drm_rect_height(src) >> 16;
> +
> +	if (plane_state->roi) {
> +		data = plane_state->roi->data;
> +
> +		if (data->enable) {
> +			roi->x = data->roi_x;
> +			roi->y = data->roi_y;
> +			roi->width = (data->roi_x + data->roi_w > src_w) ?
> +						 (src_w - data->roi_x) : data->roi_w;
> +			roi->height = (data->roi_y + data->roi_h > src_h) ?
> +						  (src_h - data->roi_y) : data->roi_h;
> +			roi->enable = true;
> +		} else {
> +			roi->enable = false;
> +		}
> +
> +		dc_hw_update_roi(&dc->hw, id, roi);
> +	} else {
> +		roi->enable = false;
> +	}
> +}
> +
> +static void update_color_mgmt(struct vs_dc *dc, u8 id,
> +			      struct dc_hw_fb *fb,
> +			      struct vs_plane_state *plane_state)
> +{
> +	struct drm_vs_color_mgmt *data;
> +	struct dc_hw_colorkey colorkey;
> +
> +	if (plane_state->color_mgmt) {
> +		data = plane_state->color_mgmt->data;
> +
> +		fb->clear_enable = data->clear_enable;
> +		fb->clear_value = data->clear_value;
> +
> +		if (data->colorkey > data->colorkey_high)
> +			data->colorkey = data->colorkey_high;
> +
> +		colorkey.colorkey = data->colorkey;
> +		colorkey.colorkey_high = data->colorkey_high;
> +		colorkey.transparency = (data->transparency) ?
> +				DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE;
> +		dc_hw_update_colorkey(&dc->hw, id, &colorkey);
> +	}
> +}
> +
> +static void update_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			 struct drm_plane *drm_plane,
> +			 struct drm_atomic_state *drm_state)
> +{
> +	struct dc_hw_fb fb = {0};
> +	struct dc_hw_scale scale;
> +	struct dc_hw_position pos;
> +	struct dc_hw_blend blend;
> +	struct dc_hw_roi roi;
> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> +									   drm_plane);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_rect *dest = &state->dst;
> +	bool dec_enable = false;
> +	u8 display_id = 0;
> +
> +	display_id = to_vs_display_id(dc, state->crtc);
> +	update_fb(plane, display_id, &fb, state);
> +	fb.dec_enable = dec_enable;
> +
> +	update_roi(dc, plane->id, plane_state, &roi, state);
> +
> +	update_scale(state, &roi, &scale);
> +
> +	if (plane_state->degamma_changed)
> +		update_degamma(dc, plane, plane_state);
> +
> +	pos.start_x = dest->x1;
> +	pos.start_y = dest->y1;
> +	pos.end_x = dest->x2;
> +	pos.end_y = dest->y2;
> +
> +	blend.alpha = (u8)(state->alpha >> 8);
> +	blend.blend_mode = (u8)(state->pixel_blend_mode);
> +
> +	update_color_mgmt(dc, plane->id, &fb, plane_state);
> +
> +	dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
> +}
> +
> +static void update_qos(struct vs_dc *dc, struct vs_plane *plane,
> +		       struct drm_plane *drm_plane,
> +		       struct drm_atomic_state *drm_state)
> +{
> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> +									   drm_plane);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_vs_watermark *data;
> +	struct dc_hw_qos qos;
> +
> +	if (plane_state->watermark) {
> +		data = plane_state->watermark->data;
> +
> +		if (data->qos_high) {
> +			if (data->qos_low > data->qos_high)
> +				data->qos_low = data->qos_high;
> +
> +			qos.low_value = data->qos_low & 0x0F;
> +			qos.high_value = data->qos_high & 0x0F;
> +			dc_hw_update_qos(&dc->hw, &qos);
> +		}
> +	}
> +}
> +
> +static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor)
> +{
> +	u8 size_type;
> +
> +	switch (state->crtc_w) {
> +	case 32:
> +		size_type = CURSOR_SIZE_32X32;
> +		break;
> +	case 64:
> +		size_type = CURSOR_SIZE_64X64;
> +		break;
> +	default:
> +		size_type = CURSOR_SIZE_32X32;
> +		break;
> +	}
> +
> +	cursor->size = size_type;
> +}
> +
> +static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +				struct drm_plane *drm_plane,
> +				struct drm_atomic_state *drm_state)
> +{
> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> +								       drm_plane);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_framebuffer *drm_fb = state->fb;
> +	struct dc_hw_cursor cursor;
> +
> +	cursor.address = plane_state->dma_addr[0];
> +	cursor.x = state->crtc_x;
> +	cursor.y = state->crtc_y;
> +	cursor.hot_x = drm_fb->hot_x;
> +	cursor.hot_y = drm_fb->hot_y;
> +	cursor.display_id = to_vs_display_id(dc, state->crtc);
> +	update_cursor_size(state, &cursor);
> +	cursor.enable = true;
> +
> +	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
> +}
> +
> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			struct drm_plane *drm_plane,
> +			struct drm_atomic_state *drm_state)
> +{
> +	update_plane(dc, plane, drm_plane, drm_state);
> +	update_qos(dc, plane, drm_plane, drm_state);
> +}
> +
> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			       struct drm_plane *drm_plane,
> +			       struct drm_atomic_state *drm_state)
> +{
> +	update_cursor_plane(dc, plane, drm_plane, drm_state);
> +}
> +
> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			 struct drm_plane_state *old_state)
> +{
> +	struct dc_hw_fb fb = {0};
> +
> +	fb.enable = false;
> +	dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL, NULL, NULL);
> +}
> +
> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +				struct drm_plane_state *old_state)
> +{
> +	struct dc_hw_cursor cursor = {0};
> +
> +	cursor.enable = false;
> +	cursor.display_id = to_vs_display_id(dc, old_state->crtc);
> +	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
> +}
> +
> +static bool vs_dc_mod_supported(const struct vs_plane_info *plane_info,
> +				u64 modifier)
> +{
> +	const u64 *mods;
> +
> +	if (!plane_info->modifiers)
> +		return false;
> +
> +	for (mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID; mods++) {
> +		if (*mods == modifier)
> +			return true;
> +	}
> +
> +	return false;
> +}
> +
> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
> +		      struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> +										 plane);
> +
> +	struct drm_framebuffer *fb = new_plane_state->fb;
> +	const struct vs_plane_info *plane_info;
> +	struct drm_crtc *crtc = new_plane_state->crtc;
> +	struct drm_crtc_state *crtc_state;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +
> +	plane_info = &dc->hw.info->planes[vs_plane->id];
> +
> +	if (fb->width < plane_info->min_width ||
> +	    fb->width > plane_info->max_width ||
> +	    fb->height < plane_info->min_height ||
> +	    fb->height > plane_info->max_height)
> +		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n",
> +			     vs_plane->id);
> +
> +	if (!vs_dc_mod_supported(plane_info, fb->modifier)) {
> +		drm_err(plane->dev, "unsupported modifier on plane%d.\n", vs_plane->id);
> +		return -EINVAL;
> +	}
> +
> +	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
> +	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
> +						  plane_info->min_scale,
> +						  plane_info->max_scale,
> +						  true, true);
> +}
> +
> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
> +			     struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> +									   plane);
> +	struct drm_framebuffer *fb = new_plane_state->fb;
> +	const struct vs_plane_info *plane_info;
> +	struct drm_crtc *crtc = new_plane_state->crtc;
> +	struct drm_crtc_state *crtc_state;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +
> +	plane_info = &dc->hw.info->planes[vs_plane->id];
> +
> +	if (fb->width < plane_info->min_width ||
> +	    fb->width > plane_info->max_width ||
> +	    fb->height < plane_info->min_height ||
> +	    fb->height > plane_info->max_height)
> +		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n", vs_plane->id);
> +
> +	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
> +	if (IS_ERR(crtc_state))
> +		return -EINVAL;
> +
> +	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
> +						plane_info->min_scale,
> +						plane_info->max_scale,
> +						true, true);
> +}
> +
> +static void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
> +{
> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
> +
> +	drm_crtc_handle_vblank(crtc);
> +
> +	vs_crtc_state->underflow = underflow;
> +}
> +
> +static irqreturn_t dc_isr(int irq, void *data)
> +{
> +	struct vs_dc *dc = data;
> +	struct vs_dc_info *dc_info = dc->hw.info;
> +	u32 i, ret;
> +
> +	ret = dc_hw_get_interrupt(&dc->hw);
> +
> +	for (i = 0; i < dc_info->panel_num; i++)
> +		vs_crtc_handle_vblank(&dc->crtc[i]->base, dc_hw_check_underflow(&dc->hw));
> +
> +	return IRQ_HANDLED;
> +}
> +
> +void vs_dc_commit(struct vs_dc *dc)
> +{
> +	dc_hw_enable_shadow_register(&dc->hw, false);
> +
> +	dc_hw_commit(&dc->hw);
> +
> +	if (dc->first_frame)
> +		dc->first_frame = false;
> +
> +	dc_hw_enable_shadow_register(&dc->hw, true);
> +}
> +
> +static int dc_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct drm_device *drm_dev = data;
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +	struct device_node *port;
> +	struct vs_crtc *crtc;
> +	struct vs_dc_info *dc_info;
> +	struct vs_plane *plane;
> +	struct vs_plane_info *plane_info;
> +	int i, ret;
> +	u32 ctrc_mask = 0;
> +
> +	if (!drm_dev || !dc) {
> +		dev_err(dev, "devices are not created.\n");
> +		return -ENODEV;
> +	}
> +
> +	ret = dc_init(dev);
> +	if (ret < 0) {
> +		drm_err(drm_dev, "Failed to initialize DC hardware.\n");
> +		return ret;
> +	}
> +
> +	port = of_get_child_by_name(dev->of_node, "port");
> +	if (!port) {
> +		drm_err(drm_dev, "no port node found\n");
> +		return -ENODEV;
> +	}
> +	of_node_put(port);
> +
> +	dc_info = dc->hw.info;
> +
> +	for (i = 0; i < dc_info->panel_num; i++) {
> +		crtc = vs_crtc_create(drm_dev, dc_info);
> +		if (!crtc) {
> +			drm_err(drm_dev, "Failed to create CRTC.\n");
> +			ret = -ENOMEM;
> +			return ret;
> +		}
> +
> +		crtc->base.port = port;
> +		crtc->dev = dev;
> +		dc->crtc[i] = crtc;
> +		ctrc_mask |= drm_crtc_mask(&crtc->base);
> +	}
> +
> +	for (i = 0; i < dc_info->plane_num; i++) {
> +		plane_info = (struct vs_plane_info *)&dc_info->planes[i];
> +
> +		if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
> +						drm_crtc_mask(&dc->crtc[0]->base));
> +		} else if (!strcmp(plane_info->name, "Primary_1") ||
> +				   !strcmp(plane_info->name, "Cursor_1")) {
> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
> +						drm_crtc_mask(&dc->crtc[1]->base));

I'd say, it's not typical to use name to distinguish objects. Maybe you 
can add indices to the info structure and drop the names instead?


> +		} else {
> +			plane = vs_plane_create(drm_dev, plane_info,
> +						dc_info->layer_num, ctrc_mask);
> +		}
> +
> +		if (IS_ERR(plane)) {
> +			dev_err(dev, "failed to construct plane\n");
> +			return PTR_ERR(plane);
> +		}
> +
> +		plane->id = i;
> +		dc->planes[i].id = plane_info->id;
> +
> +		if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
> +			if (!strcmp(plane_info->name, "Primary"))
> +				dc->crtc[0]->base.primary = &plane->base;
> +			else
> +				dc->crtc[1]->base.primary = &plane->base;

This definitely urges for an index in the plane info / plane_state.

> +			drm_dev->mode_config.min_width = plane_info->min_width;
> +			drm_dev->mode_config.min_height =
> +							plane_info->min_height;
> +			drm_dev->mode_config.max_width = plane_info->max_width;
> +			drm_dev->mode_config.max_height =
> +							plane_info->max_height;
> +		}
> +
> +		if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
> +			if (!strcmp(plane_info->name, "Cursor"))
> +				dc->crtc[0]->base.cursor = &plane->base;
> +			else
> +				dc->crtc[1]->base.cursor = &plane->base;
> +			drm_dev->mode_config.cursor_width =
> +							plane_info->max_width;
> +			drm_dev->mode_config.cursor_height =
> +							plane_info->max_height;
> +		}
> +	}
> +
> +	vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
> +	return 0;
> +}
> +
> +static void dc_unbind(struct device *dev, struct device *master, void *data)
> +{
> +	dc_deinit(dev);
> +}
> +
> +const struct component_ops dc_component_ops = {
> +	.bind = dc_bind,
> +	.unbind = dc_unbind,
> +};
> +
> +static const struct of_device_id dc_driver_dt_match[] = {
> +	{ .compatible = "starfive,jh7110-dc8200", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
> +
> +static int dc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct vs_dc *dc;
> +	int irq, ret, i;
> +
> +	dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
> +	if (!dc)
> +		return -ENOMEM;
> +
> +	dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(dc->hw.hi_base))
> +		return PTR_ERR(dc->hw.hi_base);
> +
> +	dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
> +	if (IS_ERR(dc->hw.reg_base))
> +		return PTR_ERR(dc->hw.reg_base);
> +
> +	dc->nclks = ARRAY_SIZE(dc->clk_vout);
> +	for (i = 0; i < dc->nclks; ++i)
> +		dc->clk_vout[i].id = vout_clocks[i];
> +	ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
> +	if (ret) {
> +		dev_err(dev, "Failed to get clk controls\n");
> +		return ret;
> +	}
> +
> +	dc->nrsts = ARRAY_SIZE(dc->rst_vout);
> +	for (i = 0; i < dc->nrsts; ++i)
> +		dc->rst_vout[i].id = vout_resets[i];
> +	ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
> +						 dc->rst_vout);
> +	if (ret) {
> +		dev_err(dev, "Failed to get reset controls\n");
> +		return ret;
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +
> +	ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);

Are you ready to handle the IRQ at this point?

> +	if (ret < 0) {
> +		dev_err(dev, "Failed to install irq:%u.\n", irq);
> +		return ret;
> +	}
> +
> +	dev_set_drvdata(dev, dc);
> +
> +	return component_add(dev, &dc_component_ops);
> +}
> +
> +static int dc_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +
> +	component_del(dev, &dc_component_ops);
> +
> +	dev_set_drvdata(dev, NULL);
> +
> +	return 0;
> +}
> +
> +struct platform_driver dc_platform_driver = {
> +	.probe = dc_probe,
> +	.remove = dc_remove,
> +	.driver = {
> +		.name = "vs-dc",
> +		.of_match_table = of_match_ptr(dc_driver_dt_match),
> +	},
> +};
> +
> +MODULE_AUTHOR("StarFive Corporation");
> +MODULE_DESCRIPTION("VeriSilicon DC Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
> new file mode 100644
> index 000000000..8e96fd32c
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc.h
> @@ -0,0 +1,80 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DC_H__
> +#define __VS_DC_H__
> +
> +#include <linux/clk.h>
> +#include <linux/mm_types.h>
> +#include <linux/reset.h>
> +
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_modes.h>
> +
> +#include "vs_crtc.h"
> +#include "vs_dc_hw.h"
> +#include "vs_plane.h"
> +
> +struct vs_dc_plane {
> +	enum dc_hw_plane_id id;
> +};
> +
> +enum vout_clk {
> +	CLK_VOUT_NOC_DISP = 0,
> +	CLK_VOUT_PIX0,
> +	CLK_VOUT_PIX1,
> +	CLK_VOUT_CORE,
> +	CLK_VOUT_AXI,
> +	CLK_VOUT_AHB,
> +	CLK_VOUT_HDMI_PIX,
> +	CLK_VOUT_SOC_PIX,
> +	CLK_VOUT_NUM
> +};
> +
> +enum rst_vout {
> +	RST_VOUT_AXI = 0,
> +	RST_VOUT_AHB,
> +	RST_VOUT_CORE,
> +	RST_VOUT_NUM
> +};
> +
> +struct vs_dc {
> +	struct vs_crtc		*crtc[DC_DISPLAY_NUM];
> +	struct dc_hw		hw;
> +	bool			first_frame;
> +
> +	struct vs_dc_plane	planes[PLANE_NUM];
> +	struct clk_bulk_data	clk_vout[CLK_VOUT_NUM];
> +	int			nclks;
> +	struct reset_control_bulk_data rst_vout[RST_VOUT_NUM];
> +	int			nrsts;
> +};
> +
> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc);
> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc);
> +
> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> +		     struct drm_color_lut *lut, unsigned int size);
> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc, bool enable);
> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable);
> +void vs_dc_commit(struct vs_dc *dc);
> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			struct drm_plane *drm_plane,
> +			struct drm_atomic_state *drm_state);
> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			 struct drm_plane_state *old_state);
> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
> +		      struct drm_atomic_state *state);
> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			       struct drm_plane *drm_plane,
> +			       struct drm_atomic_state *drm_state);
> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +				struct drm_plane_state *old_state);
> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
> +			     struct drm_atomic_state *state);
> +
> +extern struct platform_driver dc_platform_driver;
> +
> +#endif /* __VS_DC_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
> new file mode 100644
> index 000000000..e5acb7eb4
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
> @@ -0,0 +1,1959 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/bits.h>
> +#include <linux/io.h>
> +#include <linux/media-bus-format.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_blend.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/vs_drm.h>
> +
> +#include "vs_dc_hw.h"
> +#include "vs_type.h"
> +
> +static const u32 horkernel[] = {
> +	0x00000000, 0x20000000, 0x00002000, 0x00000000,
> +	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
> +	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
> +	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
> +	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
> +	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
> +	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
> +	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
> +	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
> +	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
> +	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
> +	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
> +	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
> +	0x00000000, 0x00000000, 0x00000000, 0x01470000,
> +	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
> +	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
> +	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
> +	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
> +	0x00000000, 0x00000000, 0x00004000, 0x00000000,
> +	0x00000000, 0x00000000, 0x20002000, 0x00000000,
> +	0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
> +	0x00000000, 0x00000000, 0x00000000, 0x27e1181f,
> +	0x00000000, 0x00000000, 0x00000000, 0x14680000,
> +	0x00002b98, 0x00000000, 0x00000000, 0x00000000,
> +	0x2f1010f0, 0x00000000, 0x00000000, 0x00000000,
> +	0x0dc70000, 0x00003239, 0x00000000, 0x00000000,
> +	0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
> +	0x00000000, 0x087f0000, 0x00003781, 0x00000000,
> +	0x00000000, 0x00000000, 0x399a0666, 0x00000000,
> +	0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
> +	0x00000000, 0x00000000, 0x00000000, 0x3cc4033c,
> +	0x00000000, 0x00000000, 0x00000000, 0x021f0000,
> +};
> +
> +#define H_COEF_SIZE (sizeof(horkernel) / sizeof(u32))
> +
> +static const u32 verkernel[] = {
> +	0x00000000, 0x20000000, 0x00002000, 0x00000000,
> +	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
> +	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
> +	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
> +	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
> +	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
> +	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
> +	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
> +	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
> +	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
> +	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
> +	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
> +	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
> +	0x00000000, 0x00000000, 0x00000000, 0x01470000,
> +	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
> +	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
> +	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
> +	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
> +	0x00000000, 0x00000000, 0x00004000, 0x00000000,
> +	0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab,
> +	0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
> +	0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +};
> +
> +#define V_COEF_SIZE (sizeof(verkernel) / sizeof(u32))
> +
> +/*
> + * RGB 709->2020 conversion parameters
> + */
> +static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = {
> +	10279,	5395,	709,
> +	1132,	15065,	187,
> +	269,	1442,	14674
> +};
> +
> +/*
> + * YUV601 to RGB conversion parameters
> + * YUV2RGB[0]  - [8] : C0 - C8;
> + * YUV2RGB[9]  - [11]: D0 - D2;
> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> + */
> +static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> +	1196,	0,			1640,	1196,
> +	-404,	-836,		1196,	2076,
> +	0,		-916224,	558336,	-1202944,
> +	64,		 940,		64,		960
> +};
> +
> +/*
> + * YUV709 to RGB conversion parameters
> + * YUV2RGB[0]  - [8] : C0 - C8;
> + * YUV2RGB[9]  - [11]: D0 - D2;
> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> + */
> +static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> +	1196,		0,		1844,	1196,
> +	-220,		-548,	1196,	2172,
> +	0,			-1020672, 316672,  -1188608,
> +	64,			940,		64,		960
> +};
> +
> +/*
> + * YUV2020 to RGB conversion parameters
> + * YUV2RGB[0]  - [8] : C0 - C8;
> + * YUV2RGB[9]  - [11]: D0 - D2;
> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> + */
> +static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> +	1196, 0, 1724, 1196,
> +	-192, -668, 1196, 2200,
> +	0, -959232, 363776, -1202944,
> +	64, 940, 64, 960
> +};
> +
> +/*
> + * RGB to YUV2020 conversion parameters
> + * RGB2YUV[0] - [8] : C0 - C8;
> + * RGB2YUV[9] - [11]: D0 - D2;
> + */
> +static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = {
> +	230,	594,	52,
> +	-125,  -323,	448,
> +	448,   -412,   -36,
> +	64,		512,	512
> +};
> +
> +/*
> + * Degamma table for 709 color space data.
> + */
> +static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005,
> +	0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024,
> +	0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064,
> +	0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce,
> +	0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166,
> +	0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230,
> +	0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331,
> +	0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c,
> +	0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
> +	0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d,
> +	0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999,
> +	0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb,
> +	0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65,
> +	0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a,
> +	0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b,
> +	0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc,
> +	0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e,
> +	0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
> +	0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e,
> +	0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf,
> +	0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea,
> +	0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f,
> +	0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930,
> +	0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f,
> +	0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee,
> +	0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde,
> +	0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
> +	0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9,
> +	0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406,
> +	0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a,
> +	0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576,
> +	0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd,
> +	0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
> +};
> +
> +/*
> + * Degamma table for 2020 color space data.
> + */
> +static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> +	0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
> +	0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
> +	0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006,
> +	0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a,
> +	0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f,
> +	0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
> +	0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024,
> +	0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035,
> +	0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d,
> +	0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d,
> +	0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098,
> +	0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3,
> +	0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123,
> +	0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d,
> +	0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
> +	0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6,
> +	0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1,
> +	0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d,
> +	0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6,
> +	0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920,
> +	0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a,
> +	0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033,
> +	0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593,
> +	0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
> +	0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656,
> +	0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332,
> +	0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480,
> +	0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1,
> +	0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99,
> +	0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
> +};
> +
> +/* one is for primary plane and the other is for all overlay planes */
> +static const struct dc_hw_plane_reg dc_plane_reg[] = {
> +	{
> +	.y_address		= DC_FRAMEBUFFER_ADDRESS,
> +	.u_address		= DC_FRAMEBUFFER_U_ADDRESS,
> +	.v_address		= DC_FRAMEBUFFER_V_ADDRESS,
> +	.y_stride		= DC_FRAMEBUFFER_STRIDE,
> +	.u_stride		= DC_FRAMEBUFFER_U_STRIDE,
> +	.v_stride		= DC_FRAMEBUFFER_V_STRIDE,
> +	.size			= DC_FRAMEBUFFER_SIZE,
> +	.top_left		= DC_FRAMEBUFFER_TOP_LEFT,
> +	.bottom_right	= DC_FRAMEBUFFER_BOTTOM_RIGHT,
> +	.scale_factor_x			= DC_FRAMEBUFFER_SCALE_FACTOR_X,
> +	.scale_factor_y			= DC_FRAMEBUFFER_SCALE_FACTOR_Y,
> +	.h_filter_coef_index	= DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
> +	.h_filter_coef_data		= DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
> +	.v_filter_coef_index	= DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
> +	.v_filter_coef_data		= DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
> +	.init_offset			= DC_FRAMEBUFFER_INIT_OFFSET,
> +	.color_key				= DC_FRAMEBUFFER_COLOR_KEY,
> +	.color_key_high			= DC_FRAMEBUFFER_COLOR_KEY_HIGH,
> +	.clear_value			= DC_FRAMEBUFFER_CLEAR_VALUE,
> +	.color_table_index		= DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
> +	.color_table_data		= DC_FRAMEBUFFER_COLOR_TABLE_DATA,
> +	.scale_config			= DC_FRAMEBUFFER_SCALE_CONFIG,
> +	.water_mark				= DC_FRAMEBUFFER_WATER_MARK,
> +	.degamma_index			= DC_FRAMEBUFFER_DEGAMMA_INDEX,
> +	.degamma_data			= DC_FRAMEBUFFER_DEGAMMA_DATA,
> +	.degamma_ex_data		= DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
> +	.src_global_color		= DC_FRAMEBUFFER_SRC_GLOBAL_COLOR,
> +	.dst_global_color		= DC_FRAMEBUFFER_DST_GLOBAL_COLOR,
> +	.blend_config			= DC_FRAMEBUFFER_BLEND_CONFIG,
> +	.roi_origin				= DC_FRAMEBUFFER_ROI_ORIGIN,
> +	.roi_size				= DC_FRAMEBUFFER_ROI_SIZE,
> +	.yuv_to_rgb_coef0			= DC_FRAMEBUFFER_YUVTORGB_COEF0,
> +	.yuv_to_rgb_coef1			= DC_FRAMEBUFFER_YUVTORGB_COEF1,
> +	.yuv_to_rgb_coef2			= DC_FRAMEBUFFER_YUVTORGB_COEF2,
> +	.yuv_to_rgb_coef3			= DC_FRAMEBUFFER_YUVTORGB_COEF3,
> +	.yuv_to_rgb_coef4			= DC_FRAMEBUFFER_YUVTORGB_COEF4,
> +	.yuv_to_rgb_coefd0			= DC_FRAMEBUFFER_YUVTORGB_COEFD0,
> +	.yuv_to_rgb_coefd1			= DC_FRAMEBUFFER_YUVTORGB_COEFD1,
> +	.yuv_to_rgb_coefd2			= DC_FRAMEBUFFER_YUVTORGB_COEFD2,
> +	.y_clamp_bound				= DC_FRAMEBUFFER_Y_CLAMP_BOUND,
> +	.uv_clamp_bound				= DC_FRAMEBUFFER_UV_CLAMP_BOUND,
> +	.rgb_to_rgb_coef0			= DC_FRAMEBUFFER_RGBTORGB_COEF0,
> +	.rgb_to_rgb_coef1			= DC_FRAMEBUFFER_RGBTORGB_COEF1,
> +	.rgb_to_rgb_coef2			= DC_FRAMEBUFFER_RGBTORGB_COEF2,
> +	.rgb_to_rgb_coef3			= DC_FRAMEBUFFER_RGBTORGB_COEF3,
> +	.rgb_to_rgb_coef4			= DC_FRAMEBUFFER_RGBTORGB_COEF4,
> +	},
> +	{
> +	.y_address		= DC_OVERLAY_ADDRESS,
> +	.u_address		= DC_OVERLAY_U_ADDRESS,
> +	.v_address		= DC_OVERLAY_V_ADDRESS,
> +	.y_stride		= DC_OVERLAY_STRIDE,
> +	.u_stride		= DC_OVERLAY_U_STRIDE,
> +	.v_stride		= DC_OVERLAY_V_STRIDE,
> +	.size			= DC_OVERLAY_SIZE,
> +	.top_left		= DC_OVERLAY_TOP_LEFT,
> +	.bottom_right	= DC_OVERLAY_BOTTOM_RIGHT,
> +	.scale_factor_x	= DC_OVERLAY_SCALE_FACTOR_X,
> +	.scale_factor_y	= DC_OVERLAY_SCALE_FACTOR_Y,
> +	.h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
> +	.h_filter_coef_data  = DC_OVERLAY_H_FILTER_COEF_DATA,
> +	.v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
> +	.v_filter_coef_data  = DC_OVERLAY_V_FILTER_COEF_DATA,
> +	.init_offset		 = DC_OVERLAY_INIT_OFFSET,
> +	.color_key			 = DC_OVERLAY_COLOR_KEY,
> +	.color_key_high			= DC_OVERLAY_COLOR_KEY_HIGH,
> +	.clear_value		 = DC_OVERLAY_CLEAR_VALUE,
> +	.color_table_index	 = DC_OVERLAY_COLOR_TABLE_INDEX,
> +	.color_table_data	 = DC_OVERLAY_COLOR_TABLE_DATA,
> +	.scale_config		 = DC_OVERLAY_SCALE_CONFIG,
> +	.water_mark				= DC_OVERLAY_WATER_MARK,
> +	.degamma_index		 = DC_OVERLAY_DEGAMMA_INDEX,
> +	.degamma_data		 = DC_OVERLAY_DEGAMMA_DATA,
> +	.degamma_ex_data	 = DC_OVERLAY_DEGAMMA_EX_DATA,
> +	.src_global_color	 = DC_OVERLAY_SRC_GLOBAL_COLOR,
> +	.dst_global_color	 = DC_OVERLAY_DST_GLOBAL_COLOR,
> +	.blend_config		 = DC_OVERLAY_BLEND_CONFIG,
> +	.roi_origin				= DC_OVERLAY_ROI_ORIGIN,
> +	.roi_size				= DC_OVERLAY_ROI_SIZE,
> +	.yuv_to_rgb_coef0		 = DC_OVERLAY_YUVTORGB_COEF0,
> +	.yuv_to_rgb_coef1		 = DC_OVERLAY_YUVTORGB_COEF1,
> +	.yuv_to_rgb_coef2		 = DC_OVERLAY_YUVTORGB_COEF2,
> +	.yuv_to_rgb_coef3		 = DC_OVERLAY_YUVTORGB_COEF3,
> +	.yuv_to_rgb_coef4			= DC_OVERLAY_YUVTORGB_COEF4,
> +	.yuv_to_rgb_coefd0			= DC_OVERLAY_YUVTORGB_COEFD0,
> +	.yuv_to_rgb_coefd1			= DC_OVERLAY_YUVTORGB_COEFD1,
> +	.yuv_to_rgb_coefd2			= DC_OVERLAY_YUVTORGB_COEFD2,
> +	.y_clamp_bound		 = DC_OVERLAY_Y_CLAMP_BOUND,
> +	.uv_clamp_bound		 = DC_OVERLAY_UV_CLAMP_BOUND,
> +	.rgb_to_rgb_coef0		 = DC_OVERLAY_RGBTORGB_COEF0,
> +	.rgb_to_rgb_coef1		 = DC_OVERLAY_RGBTORGB_COEF1,
> +	.rgb_to_rgb_coef2		 = DC_OVERLAY_RGBTORGB_COEF2,
> +	.rgb_to_rgb_coef3		 = DC_OVERLAY_RGBTORGB_COEF3,
> +	.rgb_to_rgb_coef4		 = DC_OVERLAY_RGBTORGB_COEF4,
> +	},
> +};
> +
> +static const u32 primary_overlay_format0[] = {
> +	DRM_FORMAT_XRGB4444,
> +	DRM_FORMAT_XBGR4444,
> +	DRM_FORMAT_RGBX4444,
> +	DRM_FORMAT_BGRX4444,
> +	DRM_FORMAT_ARGB4444,
> +	DRM_FORMAT_ABGR4444,
> +	DRM_FORMAT_RGBA4444,
> +	DRM_FORMAT_BGRA4444,
> +	DRM_FORMAT_XRGB1555,
> +	DRM_FORMAT_XBGR1555,
> +	DRM_FORMAT_RGBX5551,
> +	DRM_FORMAT_BGRX5551,
> +	DRM_FORMAT_ARGB1555,
> +	DRM_FORMAT_ABGR1555,
> +	DRM_FORMAT_RGBA5551,
> +	DRM_FORMAT_BGRA5551,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_RGBX8888,
> +	DRM_FORMAT_BGRX8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGBA8888,
> +	DRM_FORMAT_BGRA8888,
> +	DRM_FORMAT_ARGB2101010,
> +	DRM_FORMAT_ABGR2101010,
> +	DRM_FORMAT_RGBA1010102,
> +	DRM_FORMAT_BGRA1010102,
> +	DRM_FORMAT_YUYV,
> +	DRM_FORMAT_YVYU,
> +	DRM_FORMAT_UYVY,
> +	DRM_FORMAT_VYUY,
> +	DRM_FORMAT_YVU420,
> +	DRM_FORMAT_YUV420,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV21,
> +	DRM_FORMAT_NV16,
> +	DRM_FORMAT_NV61,
> +	DRM_FORMAT_P010,
> +};
> +
> +static const u32 primary_overlay_format1[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_RGBX8888,
> +	DRM_FORMAT_BGRX8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGBA8888,
> +	DRM_FORMAT_BGRA8888,
> +	DRM_FORMAT_ARGB2101010,
> +	DRM_FORMAT_ABGR2101010,
> +	DRM_FORMAT_RGBA1010102,
> +	DRM_FORMAT_BGRA1010102,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV21,
> +	DRM_FORMAT_YUV444,
> +};
> +
> +static const u32 cursor_formats[] = {
> +	DRM_FORMAT_ARGB8888
> +};
> +
> +static const u64 format_modifier0[] = {
> +	DRM_FORMAT_MOD_LINEAR,
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X8),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X4),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8),
> +	DRM_FORMAT_MOD_INVALID
> +};
> +
> +static const u64 secondary_format_modifiers[] = {
> +	DRM_FORMAT_MOD_LINEAR,
> +	DRM_FORMAT_MOD_INVALID
> +};
> +
> +#define FRAC_16_16(mult, div)	 (((mult) << 16) / (div))
> +
> +static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = {
> +	{
> +		/* DC_REV_0 */
> +		{
> +		.name			= "Primary",

I'd say that the indentation is strange here.
It might be more scalable to split a single array into per-generation 
data structures and reference them directly from the info struct.

> +		.id				= PRIMARY_PLANE_0,
> +		.type			= DRM_PLANE_TYPE_PRIMARY,
> +		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
> +		.formats		= primary_overlay_format0,
> +		.num_modifiers	= ARRAY_SIZE(format_modifier0),
> +		.modifiers		= format_modifier0,
> +		.min_width		= 0,
> +		.min_height		= 0,
> +		.max_width		= 4096,
> +		.max_height		= 4096,
> +		.rotation		= DRM_MODE_ROTATE_0 |
> +							DRM_MODE_ROTATE_90 |
> +							DRM_MODE_ROTATE_180 |
> +							DRM_MODE_ROTATE_270 |
> +							DRM_MODE_REFLECT_X |
> +							DRM_MODE_REFLECT_Y,
> +		.blend_mode		= BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +							BIT(DRM_MODE_BLEND_PREMULTI) |
> +							BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 0,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name			= "Overlay",
> +		.id				= OVERLAY_PLANE_0,
> +		.type			= DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
> +		.formats		= primary_overlay_format0,
> +		.num_modifiers	= ARRAY_SIZE(format_modifier0),
> +		.modifiers		= format_modifier0,
> +		.min_width		= 0,
> +		.min_height		= 0,
> +		.max_width		= 4096,
> +		.max_height		= 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 1,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name			= "Overlay_1",
> +		.id				= OVERLAY_PLANE_1,
> +		.type			= DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
> +		.formats		= primary_overlay_format0,
> +		.num_modifiers	= ARRAY_SIZE(secondary_format_modifiers),
> +		.modifiers		= secondary_format_modifiers,
> +		.min_width		= 0,
> +		.min_height		= 0,
> +		.max_width		= 4096,
> +		.max_height		= 4096,
> +		.rotation		= 0,
> +		.blend_mode		= BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +							BIT(DRM_MODE_BLEND_PREMULTI) |
> +							BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 2,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Primary_1",
> +		.id				= PRIMARY_PLANE_1,
> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
> +		.formats	 = primary_overlay_format0,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 3,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay_2",
> +		.id				= OVERLAY_PLANE_2,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
> +		.formats	 = primary_overlay_format0,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 4,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay_3",
> +		.id			= OVERLAY_PLANE_3,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
> +		.formats	 = primary_overlay_format0,
> +		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
> +		.modifiers	 = secondary_format_modifiers,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = 0,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 5,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Cursor",
> +		.id				= CURSOR_PLANE_0,
> +		.type		 = DRM_PLANE_TYPE_CURSOR,
> +		.num_formats = ARRAY_SIZE(cursor_formats),
> +		.formats	 = cursor_formats,
> +		.num_modifiers = 0,
> +		.modifiers	 = NULL,
> +		.min_width	 = 32,
> +		.min_height  = 32,
> +		.max_width	 = 64,
> +		.max_height  = 64,
> +		.rotation	 = 0,
> +		.degamma_size = 0,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 255,
> +		.watermark	 = false,
> +		.color_mgmt  = false,
> +		.roi		 = false,
> +		},
> +		{
> +		.name		 = "Cursor_1",
> +		.id				= CURSOR_PLANE_1,
> +		.type		 = DRM_PLANE_TYPE_CURSOR,
> +		.num_formats = ARRAY_SIZE(cursor_formats),
> +		.formats	 = cursor_formats,
> +		.num_modifiers = 0,
> +		.modifiers	 = NULL,
> +		.min_width	 = 32,
> +		.min_height  = 32,
> +		.max_width	 = 64,
> +		.max_height  = 64,
> +		.rotation	 = 0,
> +		.degamma_size = 0,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 255,
> +		.watermark	 = false,
> +		.color_mgmt  = false,
> +		.roi		 = false,
> +		},
> +	},
> +	{
> +		/* DC_REV_1 */
> +		{
> +		.name		 = "Primary",
> +		.id				= PRIMARY_PLANE_0,
> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
> +		.formats	 = primary_overlay_format0,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 0,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay",
> +		.id				= OVERLAY_PLANE_0,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
> +		.formats	 = primary_overlay_format0,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 1,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Primary_1",
> +		.id				= PRIMARY_PLANE_1,
> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
> +		.formats	 = primary_overlay_format0,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 2,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay_2",
> +		.id				= OVERLAY_PLANE_2,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
> +		.formats	 = primary_overlay_format0,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 3,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Cursor",
> +		.id				= CURSOR_PLANE_0,
> +		.type		 = DRM_PLANE_TYPE_CURSOR,
> +		.num_formats = ARRAY_SIZE(cursor_formats),
> +		.formats	 = cursor_formats,
> +		.num_modifiers = 0,
> +		.modifiers	 = NULL,
> +		.min_width	 = 32,
> +		.min_height  = 32,
> +		.max_width	 = 64,
> +		.max_height  = 64,
> +		.rotation	 = 0,
> +		.degamma_size = 0,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 255,
> +		.watermark	 = false,
> +		.color_mgmt  = false,
> +		.roi		 = false,
> +		},
> +		{
> +		.name		 = "Cursor_1",
> +		.id				= CURSOR_PLANE_1,
> +		.type		 = DRM_PLANE_TYPE_CURSOR,
> +		.num_formats = ARRAY_SIZE(cursor_formats),
> +		.formats	 = cursor_formats,
> +		.num_modifiers = 0,
> +		.modifiers	 = NULL,
> +		.min_width	 = 32,
> +		.min_height  = 32,
> +		.max_width	 = 64,
> +		.max_height  = 64,
> +		.rotation	 = 0,
> +		.degamma_size = 0,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 255,
> +		.watermark	 = false,
> +		.color_mgmt  = false,
> +		.roi		 = false,
> +		},
> +	},
> +	{
> +		/* DC_REV_2 */
> +		{
> +		.name		 = "Primary",
> +		.id				= PRIMARY_PLANE_0,
> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
> +		.formats	 = primary_overlay_format1,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 0,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay",
> +		.id				= OVERLAY_PLANE_0,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
> +		.formats	 = primary_overlay_format1,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 1,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay_1",
> +		.id				= OVERLAY_PLANE_1,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
> +		.formats	 = primary_overlay_format1,
> +		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
> +		.modifiers	 = secondary_format_modifiers,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = 0,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 2,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Primary_1",
> +		.id				= PRIMARY_PLANE_1,
> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
> +		.formats	 = primary_overlay_format1,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 3,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay_2",
> +		.id				= OVERLAY_PLANE_2,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
> +		.formats	 = primary_overlay_format1,
> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
> +		.modifiers	 = format_modifier0,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = DRM_MODE_ROTATE_0 |
> +					   DRM_MODE_ROTATE_90 |
> +					   DRM_MODE_ROTATE_180 |
> +					   DRM_MODE_ROTATE_270 |
> +					   DRM_MODE_REFLECT_X |
> +					   DRM_MODE_REFLECT_Y,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = FRAC_16_16(1, 3),
> +		.max_scale	 = FRAC_16_16(10, 1),
> +		.zpos		 = 4,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Overlay_3",
> +		.id				= OVERLAY_PLANE_3,
> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
> +		.formats	 = primary_overlay_format1,
> +		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
> +		.modifiers	 = secondary_format_modifiers,
> +		.min_width	 = 0,
> +		.min_height  = 0,
> +		.max_width	 = 4096,
> +		.max_height  = 4096,
> +		.rotation	 = 0,
> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
> +					   BIT(DRM_MODE_BLEND_COVERAGE),
> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> +						  BIT(DRM_COLOR_YCBCR_BT2020),
> +		.degamma_size	= DEGAMMA_SIZE,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 5,
> +		.watermark	 = true,
> +		.color_mgmt  = true,
> +		.roi		 = true,
> +		},
> +		{
> +		.name		 = "Cursor",
> +		.id				= CURSOR_PLANE_0,
> +		.type		 = DRM_PLANE_TYPE_CURSOR,
> +		.num_formats = ARRAY_SIZE(cursor_formats),
> +		.formats	 = cursor_formats,
> +		.num_modifiers = 0,
> +		.modifiers	 = NULL,
> +		.min_width	 = 32,
> +		.min_height  = 32,
> +		.max_width	 = 64,
> +		.max_height  = 64,
> +		.rotation	 = 0,
> +		.degamma_size = 0,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 255,
> +		.watermark	 = false,
> +		.color_mgmt  = false,
> +		.roi		 = false,
> +		},
> +		{
> +		.name		 = "Cursor_1",
> +		.id				= CURSOR_PLANE_1,
> +		.type		 = DRM_PLANE_TYPE_CURSOR,
> +		.num_formats = ARRAY_SIZE(cursor_formats),
> +		.formats	 = cursor_formats,
> +		.num_modifiers = 0,
> +		.modifiers	 = NULL,
> +		.min_width	 = 32,
> +		.min_height  = 32,
> +		.max_width	 = 64,
> +		.max_height  = 64,
> +		.rotation	 = 0,
> +		.degamma_size = 0,
> +		.min_scale	 = DRM_PLANE_NO_SCALING,
> +		.max_scale	 = DRM_PLANE_NO_SCALING,
> +		.zpos		 = 255,
> +		.watermark	 = false,
> +		.color_mgmt  = false,
> +		.roi		 = false,
> +		},
> +	},
> +};
> +
> +static const struct vs_dc_info dc_info[] = {

Again, please split into per-generation instances.

> +	{
> +		/* DC_REV_0 */
> +		.name			= "DC8200",
> +		.panel_num		= 2,
> +		.plane_num		= 8,
> +		.planes			= dc_hw_planes[DC_REV_0],
> +		.layer_num		= 6,
> +		.max_bpc		= 10,
> +		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
> +						  DRM_COLOR_FORMAT_YCBCR444 |
> +						  DRM_COLOR_FORMAT_YCBCR422 |
> +						  DRM_COLOR_FORMAT_YCBCR420,
> +		.gamma_size		= GAMMA_EX_SIZE,
> +		.gamma_bits		= 12,
> +		.pitch_alignment	= 128,
> +		.pipe_sync		= false,
> +		.background		= true,
> +		.panel_sync		= true,
> +		.cap_dec		= true,
> +	},
> +	{
> +		/* DC_REV_1 */
> +		.name			= "DC8200",
> +		.panel_num		= 2,
> +		.plane_num		= 6,
> +		.planes			= dc_hw_planes[DC_REV_1],
> +		.layer_num		= 4,
> +		.max_bpc		= 10,
> +		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
> +						  DRM_COLOR_FORMAT_YCBCR444 |
> +						  DRM_COLOR_FORMAT_YCBCR422 |
> +						  DRM_COLOR_FORMAT_YCBCR420,
> +		.gamma_size		= GAMMA_EX_SIZE,
> +		.gamma_bits		= 12,
> +		.pitch_alignment	= 128,
> +		.pipe_sync		= false,
> +		.background		= true,
> +		.panel_sync		= true,
> +		.cap_dec		= true,
> +	},
> +	{
> +		/* DC_REV_2 */
> +		.name			= "DC8200",
> +		.panel_num		= 2,
> +		.plane_num		= 8,
> +		.planes			= dc_hw_planes[DC_REV_2],
> +		.layer_num		= 6,
> +		.max_bpc		= 10,
> +		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
> +						  DRM_COLOR_FORMAT_YCBCR444 |
> +						  DRM_COLOR_FORMAT_YCBCR422 |
> +						  DRM_COLOR_FORMAT_YCBCR420,
> +		.gamma_size		= GAMMA_EX_SIZE,
> +		.gamma_bits		= 12,
> +		.pitch_alignment	= 128,
> +		.pipe_sync		= false,
> +		.background		= true,
> +		.panel_sync		= true,
> +		.cap_dec		= false,
> +	},
> +};
> +
> +static const struct dc_hw_funcs hw_func;
> +
> +static inline u32 hi_read(struct dc_hw *hw, u32 reg)
> +{
> +	return readl(hw->hi_base + reg);
> +}
> +
> +static inline void hi_write(struct dc_hw *hw, u32 reg, u32 value)
> +{
> +	writel(value, hw->hi_base + reg);
> +}
> +
> +static inline void dc_write(struct dc_hw *hw, u32 reg, u32 value)
> +{
> +	writel(value, hw->reg_base + reg - DC_REG_BASE);
> +}
> +
> +static inline u32 dc_read(struct dc_hw *hw, u32 reg)
> +{
> +	u32 value = readl(hw->reg_base + reg - DC_REG_BASE);
> +
> +	return value;
> +}
> +
> +static inline void dc_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear)
> +{
> +	u32 value = dc_read(hw, reg);
> +
> +	value &= ~clear;
> +	value |= set;
> +	dc_write(hw, reg, value);
> +}
> +
> +static void load_default_filter(struct dc_hw *hw,
> +				const struct dc_hw_plane_reg *reg, u32 offset)
> +{
> +	u8 i;
> +
> +	dc_write(hw, reg->scale_config + offset, 0x33);
> +	dc_write(hw, reg->init_offset + offset, 0x80008000);
> +	dc_write(hw, reg->h_filter_coef_index + offset, 0x00);
> +	for (i = 0; i < H_COEF_SIZE; i++)
> +		dc_write(hw, reg->h_filter_coef_data + offset, horkernel[i]);
> +
> +	dc_write(hw, reg->v_filter_coef_index + offset, 0x00);
> +	for (i = 0; i < V_COEF_SIZE; i++)
> +		dc_write(hw, reg->v_filter_coef_data + offset, verkernel[i]);
> +}
> +
> +static void load_rgb_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
> +			    u32 offset, u16 *table)
> +{
> +	dc_write(hw, reg->rgb_to_rgb_coef0 + offset, table[0] | (table[1] << 16));
> +	dc_write(hw, reg->rgb_to_rgb_coef1 + offset, table[2] | (table[3] << 16));
> +	dc_write(hw, reg->rgb_to_rgb_coef2 + offset, table[4] | (table[5] << 16));
> +	dc_write(hw, reg->rgb_to_rgb_coef3 + offset, table[6] | (table[7] << 16));
> +	dc_write(hw, reg->rgb_to_rgb_coef4 + offset, table[8]);
> +}
> +
> +static void load_yuv_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
> +			    u32 offset, s32 *table)
> +{
> +	dc_write(hw, reg->yuv_to_rgb_coef0 + offset,
> +		 (0xFFFF & table[0]) | (table[1] << 16));
> +	dc_write(hw, reg->yuv_to_rgb_coef1 + offset,
> +		 (0xFFFF & table[2]) | (table[3] << 16));
> +	dc_write(hw, reg->yuv_to_rgb_coef2 + offset,
> +		 (0xFFFF & table[4]) | (table[5] << 16));
> +	dc_write(hw, reg->yuv_to_rgb_coef3 + offset,
> +		 (0xFFFF & table[6]) | (table[7] << 16));
> +	dc_write(hw, reg->yuv_to_rgb_coef4 + offset, table[8]);
> +	dc_write(hw, reg->yuv_to_rgb_coefd0 + offset, table[9]);
> +	dc_write(hw, reg->yuv_to_rgb_coefd1 + offset, table[10]);
> +	dc_write(hw, reg->yuv_to_rgb_coefd2 + offset, table[11]);
> +	dc_write(hw, reg->y_clamp_bound + offset, table[12] | (table[13] << 16));
> +	dc_write(hw, reg->uv_clamp_bound + offset, table[14] | (table[15] << 16));
> +}
> +
> +static void load_rgb_to_yuv(struct dc_hw *hw, u32 offset, s16 *table)
> +{
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF0 + offset,
> +		 table[0] | (table[1] << 16));
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF1 + offset,
> +		 table[2] | (table[3] << 16));
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF2 + offset,
> +		 table[4] | (table[5] << 16));
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF3 + offset,
> +		 table[6] | (table[7] << 16));
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF4 + offset, table[8]);
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD0 + offset, table[9]);
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD1 + offset, table[10]);
> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD2 + offset, table[11]);
> +}
> +
> +static bool is_rgb(enum dc_hw_color_format format)
> +{
> +	switch (format) {
> +	case FORMAT_X4R4G4B4:
> +	case FORMAT_A4R4G4B4:
> +	case FORMAT_X1R5G5B5:
> +	case FORMAT_A1R5G5B5:
> +	case FORMAT_R5G6B5:
> +	case FORMAT_X8R8G8B8:
> +	case FORMAT_A8R8G8B8:
> +	case FORMAT_A2R10G10B10:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static void load_degamma_table(struct dc_hw *hw,
> +			       const struct dc_hw_plane_reg *reg,
> +			       u32 offset, u16 *table)
> +{
> +	u16 i;
> +	u32 value;
> +
> +	dc_write(hw, reg->degamma_index + offset, 0);
> +
> +	for (i = 0; i < DEGAMMA_SIZE; i++) {
> +		value = table[i] | (table[i] << 16);
> +		dc_write(hw, reg->degamma_data + offset, value);
> +		dc_write(hw, reg->degamma_ex_data + offset, table[i]);
> +	}
> +}
> +
> +static u32 get_addr_offset(u32 id)
> +{
> +	u32 offset = 0;
> +
> +	switch (id) {
> +	case PRIMARY_PLANE_1:
> +	case OVERLAY_PLANE_1:
> +		offset = 0x04;
> +		break;
> +	case OVERLAY_PLANE_2:
> +		offset = 0x08;
> +		break;
> +	case OVERLAY_PLANE_3:
> +		offset = 0x0C;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return offset;
> +}
> +
> +int dc_hw_init(struct dc_hw *hw)
> +{
> +	u8 i, id, panel_num, layer_num;
> +	u32 offset;
> +	u32 revision = hi_read(hw, DC_HW_REVISION);
> +	u32 cid = hi_read(hw, DC_HW_CHIP_CID);
> +	const struct dc_hw_plane_reg *reg;
> +
> +	switch (revision) {
> +	case 0x5720:
> +		hw->rev = DC_REV_0;
> +		break;
> +	case 0x5721:
> +		switch (cid) {
> +		case 0x30B:
> +			hw->rev = DC_REV_1;
> +			break;
> +		case 0x310:
> +			hw->rev = DC_REV_2;
> +			break;
> +		default:
> +			hw->rev = DC_REV_0;
> +			break;
> +		}
> +		break;
> +	default:
> +		return -ENXIO;
> +	}

We (drm/msm) had code like this for a while (and autodetection of the HW 
ID). And nowadays we are migrating to of_device_get_match_data and 
per-SoC compat names. It is just easier to handle, it allows one to 
specify device more precisely and also it simplifies DT schema.

> +
> +	hw->info = (struct vs_dc_info *)&dc_info[hw->rev];
> +	hw->func = (struct dc_hw_funcs *)&hw_func;
> +
> +	layer_num = hw->info->layer_num;
> +	for (i = 0; i < layer_num; i++) {
> +		id = hw->info->planes[i].id;
> +		offset = get_addr_offset(id);
> +		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
> +			reg = &dc_plane_reg[0];
> +		else
> +			reg = &dc_plane_reg[1];
> +
> +		load_default_filter(hw, reg, offset);
> +		load_rgb_to_rgb(hw, reg, offset, RGB2RGB);
> +	}
> +
> +	panel_num = hw->info->panel_num;
> +	for (i = 0; i < panel_num; i++) {
> +		offset = i << 2;
> +
> +		load_rgb_to_yuv(hw, offset, RGB2YUV);
> +		dc_write(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0x111);
> +
> +		offset = i ? DC_CURSOR_OFFSET : 0;
> +		dc_write(hw, DC_CURSOR_BACKGROUND + offset, 0x00FFFFFF);
> +		dc_write(hw, DC_CURSOR_FOREGROUND + offset, 0x00AAAAAA);
> +	}
> +
> +	return 0;
> +}
> +
> +void dc_hw_deinit(struct dc_hw *hw)
> +{
> +	/* Nothing to do */
> +}
> +
> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
> +			struct dc_hw_fb *fb, struct dc_hw_scale *scale,
> +			struct dc_hw_position *pos, struct dc_hw_blend *blend)
> +{
> +	struct dc_hw_plane *plane = &hw->plane[id];
> +
> +	if (plane) {
> +		if (fb) {
> +			if (!fb->enable)
> +				plane->fb.enable = false;
> +			else
> +				memcpy(&plane->fb, fb,
> +				       sizeof(*fb) - sizeof(fb->dirty));
> +			plane->fb.dirty = true;
> +		}
> +		if (scale) {
> +			memcpy(&plane->scale, scale,
> +			       sizeof(*scale) - sizeof(scale->dirty));
> +			plane->scale.dirty = true;
> +		}
> +		if (pos) {
> +			memcpy(&plane->pos, pos,
> +			       sizeof(*pos) - sizeof(pos->dirty));
> +			plane->pos.dirty = true;
> +		}
> +		if (blend) {
> +			memcpy(&plane->blend, blend,
> +			       sizeof(*blend) - sizeof(blend->dirty));
> +			plane->blend.dirty = true;
> +		}
> +	}
> +}
> +
> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode)
> +{
> +	struct dc_hw_plane *plane = &hw->plane[id];
> +
> +	if (plane) {
> +		if (hw->info->planes[id].degamma_size) {
> +			plane->degamma.mode = mode;
> +			plane->degamma.dirty = true;
> +		} else {
> +			plane->degamma.dirty = false;
> +		}
> +	}
> +}
> +
> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi)
> +{
> +	struct dc_hw_plane *plane = &hw->plane[id];
> +
> +	if (plane) {
> +		memcpy(&plane->roi, roi, sizeof(*roi) - sizeof(roi->dirty));
> +		plane->roi.dirty = true;
> +	}
> +}
> +
> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
> +			   struct dc_hw_colorkey *colorkey)
> +{
> +	struct dc_hw_plane *plane = &hw->plane[id];
> +
> +	if (plane) {
> +		memcpy(&plane->colorkey, colorkey,
> +		       sizeof(*colorkey) - sizeof(colorkey->dirty));
> +		plane->colorkey.dirty = true;
> +	}
> +}
> +
> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos)
> +{
> +	memcpy(&hw->qos, qos, sizeof(*qos) - sizeof(qos->dirty));
> +	hw->qos.dirty = true;
> +}
> +
> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor)
> +{
> +	memcpy(&hw->cursor[id], cursor, sizeof(*cursor) - sizeof(cursor->dirty));
> +	hw->cursor[id].dirty = true;
> +}
> +
> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
> +			u16 r, u16 g, u16 b)
> +{
> +	if (index >= hw->info->gamma_size)
> +		return;
> +
> +	hw->gamma[id].gamma[index][0] = r;
> +	hw->gamma[id].gamma[index][1] = g;
> +	hw->gamma[id].gamma[index][2] = b;
> +	hw->gamma[id].dirty = true;
> +}
> +
> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable)
> +{
> +	hw->gamma[id].enable = enable;
> +	hw->gamma[id].dirty = true;
> +}
> +
> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display)
> +{
> +	u8 id = display->id;
> +
> +	memcpy(&hw->display[id], display, sizeof(*display));
> +
> +	hw->func->display(hw, display);
> +}
> +
> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable)
> +{
> +	if (enable)
> +		hi_write(hw, AQ_INTR_ENBL, 0xFFFFFFFF);
> +	else
> +		hi_write(hw, AQ_INTR_ENBL, 0);
> +}
> +
> +u32 dc_hw_get_interrupt(struct dc_hw *hw)
> +{
> +	return hi_read(hw, AQ_INTR_ACKNOWLEDGE);
> +}
> +
> +bool dc_hw_check_underflow(struct dc_hw *hw)
> +{
> +	return dc_read(hw, DC_FRAMEBUFFER_CONFIG) & BIT(5);
> +}
> +
> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable)
> +{
> +	u32 i, offset;
> +	u8 id, layer_num = hw->info->layer_num;
> +	u8 panel_num = hw->info->panel_num;
> +
> +	for (i = 0; i < layer_num; i++) {
> +		id = hw->info->planes[i].id;
> +		offset = get_addr_offset(id);
> +		if (enable) {
> +			if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, BIT(12), 0);
> +			else
> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, BIT(31), 0);
> +		} else {
> +			if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, 0, BIT(12));
> +			else
> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, 0, BIT(31));
> +		}
> +	}
> +
> +	for (i = 0; i < panel_num; i++) {
> +		offset = i << 2;
> +		if (enable)
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, BIT(0));
> +		else
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, BIT(0), 0);
> +	}
> +}
> +
> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id)
> +{
> +	if (out <= OUT_DP)
> +		hw->out[id] = out;
> +}
> +
> +static void gamma_ex_commit(struct dc_hw *hw)
> +{
> +	u8 panel_num = hw->info->panel_num;
> +	u16 i, j;
> +	u32 value;
> +
> +	for (j = 0; j < panel_num; j++) {
> +		if (hw->gamma[j].dirty) {
> +			if (hw->gamma[j].enable) {
> +				dc_write(hw, DC_DISPLAY_GAMMA_EX_INDEX + (j << 2), 0x00);
> +				for (i = 0; i < GAMMA_EX_SIZE; i++) {
> +					value = hw->gamma[j].gamma[i][2] |
> +						(hw->gamma[j].gamma[i][1] << 12);
> +					dc_write(hw, DC_DISPLAY_GAMMA_EX_DATA + (j << 2), value);
> +					dc_write(hw, DC_DISPLAY_GAMMA_EX_ONE_DATA + (j << 2),
> +						 hw->gamma[j].gamma[i][0]);
> +				}
> +				dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
> +					     BIT(13), 0);
> +			} else {
> +				dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
> +					     0, BIT(13));
> +			}
> +			hw->gamma[j].dirty = false;
> +		}
> +	}
> +}
> +
> +static void plane_commit(struct dc_hw *hw)
> +{
> +	struct dc_hw_plane *plane;
> +	const struct dc_hw_plane_reg *reg;
> +	bool primary = false;
> +	u8 id, layer_num = hw->info->layer_num;
> +	u32 i, offset;
> +
> +	for (i = 0; i < layer_num; i++) {
> +		plane = &hw->plane[i];
> +		id = hw->info->planes[i].id;
> +		offset = get_addr_offset(id);
> +		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
> +			reg = &dc_plane_reg[0];
> +			primary = true;
> +		} else {
> +			reg = &dc_plane_reg[1];
> +			primary = false;
> +		}

Can you please move register defines to some internal hw_plane 
structure. These details are known at the time of creation of the plane. 
There is no need to determine them again and again.

> +
> +		if (plane->fb.dirty) {
> +			if (plane->fb.enable) {

if (plane->fb.dirty && plane->fb.enable) will save you one level of 
indentation.

However it might be better to use existing DRM flags instead of 
inventing your own flags.

> +				dc_write(hw, reg->y_address + offset,
> +					 plane->fb.y_address);
> +				dc_write(hw, reg->u_address + offset,
> +					 plane->fb.u_address);
> +				dc_write(hw, reg->v_address + offset,
> +					 plane->fb.v_address);
> +				dc_write(hw, reg->y_stride + offset,
> +					 plane->fb.y_stride);
> +				dc_write(hw, reg->u_stride + offset,
> +					 plane->fb.u_stride);
> +				dc_write(hw, reg->v_stride + offset,
> +					 plane->fb.v_stride);
> +				dc_write(hw, reg->size + offset,
> +					 plane->fb.width |
> +					 (plane->fb.height << 15));
> +				dc_write(hw, reg->water_mark + offset,
> +					 plane->fb.water_mark);
> +
> +				if (plane->fb.clear_enable)
> +					dc_write(hw, reg->clear_value + offset,
> +						 plane->fb.clear_value);
> +			}
> +
> +			if (primary) {

You have two kinds of planes. Primary and overlays. Could you please 
split this code into two separate functions and define two vtables, one 
for primary planes and one for overlays. There is no need to stick 
everything into a single funcion that handles everything.

> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
> +					     (plane->fb.format << 26) |
> +					     (plane->fb.uv_swizzle << 25) |
> +					     (plane->fb.swizzle << 23) |
> +					     (plane->fb.tile_mode << 17) |
> +					     (plane->fb.yuv_color_space << 14) |
> +					     (plane->fb.rotation << 11) |
> +					     (plane->fb.clear_enable << 8),
> +					     (0x1F << 26) |
> +					     BIT(25) |
> +					     (0x03 << 23) |
> +					     (0x1F << 17) |
> +					     (0x07 << 14) |
> +					     (0x07 << 11) |
> +					     BIT(8));
> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
> +					     (plane->fb.dec_enable << 1) |
> +					     (plane->fb.enable << 13) |
> +					     (plane->fb.zpos << 16) |
> +					     (plane->fb.display_id << 19),
> +					     BIT(1) | BIT(13) | (0x07 << 16) | BIT(19));

It looks like this code can enjoy defining names for all the magic 
values. Otherwise it is impossible to support it.

> +			} else {
> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> +					     (plane->fb.dec_enable << 27) |
> +					     (plane->fb.clear_enable << 25) |
> +					     (plane->fb.enable << 24) |
> +					     (plane->fb.format << 16) |
> +					     (plane->fb.uv_swizzle << 15) |
> +					     (plane->fb.swizzle << 13) |
> +					     (plane->fb.tile_mode << 8) |
> +					     (plane->fb.yuv_color_space << 5) |
> +					     (plane->fb.rotation << 2),
> +					     BIT(27) |
> +					     BIT(25) |
> +					     BIT(24) |
> +					     (0x1F << 16) |
> +					     BIT(15) |
> +					     (0x03 << 13) |
> +					     (0x1F << 8) |
> +					     (0x07 << 5) |
> +					     (0x07 << 2));
> +				dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset,
> +					     plane->fb.zpos | (plane->fb.display_id << 3),
> +					     0x07 | BIT(3));
> +			}
> +			plane->fb.dirty = false;
> +		}
> +
> +		if (plane->scale.dirty) {
> +			if (plane->scale.enable) {
> +				dc_write(hw, reg->scale_factor_x + offset,
> +					 plane->scale.scale_factor_x);
> +				dc_write(hw, reg->scale_factor_y + offset,
> +					 plane->scale.scale_factor_y);
> +				if (primary)
> +					dc_set_clear(hw,
> +						     DC_FRAMEBUFFER_CONFIG + offset,
> +						     BIT(22), 0);
> +				else
> +					dc_set_clear(hw,
> +						     DC_OVERLAY_SCALE_CONFIG + offset,
> +						     BIT(8), 0);
> +			} else {
> +				if (primary)
> +					dc_set_clear(hw,
> +						     DC_FRAMEBUFFER_CONFIG + offset,
> +						     0, BIT(22));
> +				else
> +					dc_set_clear(hw,
> +						     DC_OVERLAY_SCALE_CONFIG + offset,
> +						     0, BIT(8));
> +			}
> +			plane->scale.dirty = false;
> +		}
> +
> +		if (plane->pos.dirty) {
> +			dc_write(hw, reg->top_left + offset,
> +				 plane->pos.start_x |
> +				 (plane->pos.start_y << 15));
> +			dc_write(hw, reg->bottom_right + offset,
> +				 plane->pos.end_x |
> +				 (plane->pos.end_y << 15));
> +			plane->pos.dirty = false;
> +		}
> +
> +		if (plane->blend.dirty) {
> +			dc_write(hw, reg->src_global_color + offset,
> +				 plane->blend.alpha << 24);
> +			dc_write(hw, reg->dst_global_color + offset,
> +				 plane->blend.alpha << 24);
> +			switch (plane->blend.blend_mode) {
> +			case BLEND_PREMULTI:
> +				dc_write(hw, reg->blend_config + offset, 0x3450);
> +				break;
> +			case BLEND_COVERAGE:
> +				dc_write(hw, reg->blend_config + offset, 0x3950);
> +				break;
> +			case BLEND_PIXEL_NONE:
> +				dc_write(hw, reg->blend_config + offset, 0x3548);
> +				break;
> +			default:
> +				break;
> +			}
> +			plane->blend.dirty = false;
> +		}
> +
> +		if (plane->colorkey.dirty) {
> +			dc_write(hw, reg->color_key + offset, plane->colorkey.colorkey);
> +			dc_write(hw, reg->color_key_high + offset,
> +				 plane->colorkey.colorkey_high);
> +
> +			if (primary)
> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
> +					     plane->colorkey.transparency << 9, 0x03 << 9);
> +			else
> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> +					     plane->colorkey.transparency, 0x03);
> +
> +			plane->colorkey.dirty = false;
> +		}
> +
> +		if (plane->roi.dirty) {
> +			if (plane->roi.enable) {
> +				dc_write(hw, reg->roi_origin + offset,
> +					 plane->roi.x | (plane->roi.y << 16));
> +				dc_write(hw, reg->roi_size + offset,
> +					 plane->roi.width | (plane->roi.height << 16));
> +				if (primary)
> +					dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
> +						     BIT(0), 0);
> +				else
> +					dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> +						     BIT(22), 0);
> +			} else {
> +				if (primary)
> +					dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
> +						     0, BIT(0));
> +				else
> +					dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> +						     0, BIT(22));
> +			}
> +			plane->roi.dirty = false;
> +		}
> +	}
> +}
> +
> +static void plane_ex_commit(struct dc_hw *hw)
> +{
> +	struct dc_hw_plane *plane;
> +	const struct dc_hw_plane_reg *reg;
> +	bool primary = false;
> +	u8 id, layer_num = hw->info->layer_num;
> +	u32 i, offset;
> +
> +	for (i = 0; i < layer_num; i++) {
> +		plane = &hw->plane[i];
> +		id = hw->info->planes[i].id;
> +		offset = get_addr_offset(id);
> +		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
> +			reg = &dc_plane_reg[0];
> +			primary = true;
> +		} else {
> +			reg = &dc_plane_reg[1];
> +			primary = false;
> +		}
> +
> +		if (plane->fb.dirty) {
> +			if (is_rgb(plane->fb.format)) {
> +				if (primary)
> +					dc_set_clear(hw,
> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
> +						     BIT(6), BIT(8));
> +				else
> +					dc_set_clear(hw,
> +						     DC_OVERLAY_CONFIG + offset,
> +						     BIT(29), BIT(30));
> +			} else {
> +				if (primary)
> +					dc_set_clear(hw,
> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
> +						     BIT(8), BIT(6));
> +				else
> +					dc_set_clear(hw,
> +						     DC_OVERLAY_CONFIG + offset,
> +						     BIT(30), BIT(29));
> +				switch (plane->fb.yuv_color_space) {
> +				case COLOR_SPACE_601:
> +					load_yuv_to_rgb(hw, reg, offset, YUV601_2RGB);
> +					break;
> +				case COLOR_SPACE_709:
> +					load_yuv_to_rgb(hw, reg, offset, YUV709_2RGB);
> +					break;
> +				case COLOR_SPACE_2020:
> +					load_yuv_to_rgb(hw, reg, offset, YUV2020_2RGB);
> +					break;
> +				default:
> +					break;
> +				}
> +			}
> +		}
> +		if (plane->degamma.dirty) {
> +			switch (plane->degamma.mode) {
> +			case VS_DEGAMMA_DISABLE:
> +				if (primary)
> +					dc_set_clear(hw,
> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
> +						     0, BIT(5));
> +				else
> +					dc_set_clear(hw,
> +						     DC_OVERLAY_CONFIG + offset,
> +						     0, BIT(28));
> +				break;
> +			case VS_DEGAMMA_BT709:
> +				load_degamma_table(hw, reg, offset, DEGAMMA_709);
> +				if (primary)
> +					dc_set_clear(hw,
> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
> +						     BIT(5), 0);
> +				else
> +					dc_set_clear(hw,
> +						     DC_OVERLAY_CONFIG + offset,
> +						     BIT(28), 0);
> +				break;
> +			case VS_DEGAMMA_BT2020:
> +				load_degamma_table(hw, reg, offset, DEGAMMA_2020);
> +				if (primary)
> +					dc_set_clear(hw,
> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
> +						     BIT(5), 0);
> +				else
> +					dc_set_clear(hw,
> +						     DC_OVERLAY_CONFIG + offset,
> +						     BIT(28), 0);
> +				break;
> +			default:
> +				break;
> +			}
> +			plane->degamma.dirty = false;
> +		}
> +	}
> +	plane_commit(hw);
> +}
> +
> +static void setup_display(struct dc_hw *hw, struct dc_hw_display *display)
> +{
> +	u8 id = display->id;
> +	u32 dpi_cfg, offset = id << 2;
> +
> +	if (hw->display[id].enable) {
> +		switch (display->bus_format) {
> +		case MEDIA_BUS_FMT_RGB565_1X16:
> +			dpi_cfg = 0;
> +			break;
> +		case MEDIA_BUS_FMT_RGB666_1X18:
> +			dpi_cfg = 3;
> +			break;
> +		case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> +			dpi_cfg = 4;
> +			break;
> +		case MEDIA_BUS_FMT_RGB888_1X24:
> +			dpi_cfg = 5;
> +			break;
> +		case MEDIA_BUS_FMT_RGB101010_1X30:
> +			dpi_cfg = 6;
> +			break;
> +		default:
> +			dpi_cfg = 5;
> +			break;
> +		}
> +		dc_write(hw, DC_DISPLAY_DPI_CONFIG + offset, dpi_cfg);
> +
> +		if (id == 0)
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
> +		else
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
> +
> +		dc_write(hw, DC_DISPLAY_H + offset, hw->display[id].h_active |
> +			 (hw->display[id].h_total << 16));
> +		dc_write(hw, DC_DISPLAY_H_SYNC + offset,
> +			 hw->display[id].h_sync_start |
> +			 (hw->display[id].h_sync_end << 15) |
> +			 (hw->display[id].h_sync_polarity ? 0 : BIT(31)) |
> +			 BIT(30));
> +		dc_write(hw, DC_DISPLAY_V + offset, hw->display[id].v_active |
> +				(hw->display[id].v_total << 16));
> +		dc_write(hw, DC_DISPLAY_V_SYNC + offset,
> +			 hw->display[id].v_sync_start |
> +			 (hw->display[id].v_sync_end << 15) |
> +			 (hw->display[id].v_sync_polarity ? 0 : BIT(31)) |
> +			 BIT(30));
> +
> +		if (hw->info->pipe_sync)
> +			dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
> +				     0, BIT(3) | BIT(4));
> +
> +		dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(12), 0);
> +		if (id == 0)
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(0), BIT(3));
> +		else
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(1), BIT(3));
> +	} else {
> +		dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(12));
> +		if (id == 0)
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
> +		else
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
> +	}
> +}
> +
> +static void setup_display_ex(struct dc_hw *hw, struct dc_hw_display *display)
> +{
> +	u8 id = display->id;
> +	u32 dp_cfg, offset = id << 2;
> +	bool is_yuv = false;
> +
> +	if (hw->display[id].enable && hw->out[id] == OUT_DP) {
> +		switch (display->bus_format) {
> +		case MEDIA_BUS_FMT_RGB565_1X16:
> +			dp_cfg = 0;
> +			break;
> +		case MEDIA_BUS_FMT_RGB666_1X18:
> +			dp_cfg = 1;
> +			break;
> +		case MEDIA_BUS_FMT_RGB888_1X24:
> +			dp_cfg = 2;
> +			break;
> +		case MEDIA_BUS_FMT_RGB101010_1X30:
> +			dp_cfg = 3;
> +			break;
> +		case MEDIA_BUS_FMT_UYVY8_1X16:
> +			dp_cfg = 2 << 4;
> +			is_yuv = true;
> +			break;
> +		case MEDIA_BUS_FMT_YUV8_1X24:
> +			dp_cfg = 4 << 4;
> +			is_yuv = true;
> +			break;
> +		case MEDIA_BUS_FMT_UYVY10_1X20:
> +			dp_cfg = 8 << 4;
> +			is_yuv = true;
> +			break;
> +		case MEDIA_BUS_FMT_YUV10_1X30:
> +			dp_cfg = 10 << 4;
> +			is_yuv = true;
> +			break;
> +		case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +			dp_cfg = 12 << 4;
> +			is_yuv = true;
> +			break;
> +		case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> +			dp_cfg = 13 << 4;
> +			is_yuv = true;
> +			break;
> +		default:
> +			dp_cfg = 2;
> +			break;
> +		}
> +		if (is_yuv)
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(16), 0);
> +		else
> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(16));
> +		dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | BIT(3));
> +	}
> +
> +	if (hw->out[id] == OUT_DPI)
> +		dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, BIT(3));
> +
> +	setup_display(hw, display);
> +}
> +
> +static const struct dc_hw_funcs hw_func = {
> +	.gamma = &gamma_ex_commit,
> +	.plane = &plane_ex_commit,
> +	.display = setup_display_ex,
> +};
> +
> +void dc_hw_commit(struct dc_hw *hw)
> +{
> +	u32 i, offset = 0;
> +	u8 plane_num = hw->info->plane_num;
> +	u8 layer_num = hw->info->layer_num;
> +	u8 cursor_num = plane_num - layer_num;
> +
> +	hw->func->gamma(hw);
> +	hw->func->plane(hw);
> +
> +	for (i = 0; i < cursor_num; i++) {
> +		if (hw->cursor[i].dirty) {
> +			offset = hw->cursor[i].display_id ? DC_CURSOR_OFFSET : 0;
> +			if (hw->cursor[i].enable) {
> +				dc_write(hw, DC_CURSOR_ADDRESS + offset,
> +					 hw->cursor[i].address);
> +				dc_write(hw, DC_CURSOR_LOCATION + offset, hw->cursor[i].x |
> +					 (hw->cursor[i].y << 16));
> +				dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
> +					     (hw->cursor[i].hot_x << 16) |
> +					     (hw->cursor[i].hot_y << 8) |
> +					     (hw->cursor[i].size << 5) |
> +					     BIT(3) | BIT(2) | 0x02,
> +					     (0xFF << 16) |
> +					     (0xFF << 8) |
> +					     (0x07 << 5) | 0x1F);
> +			} else {
> +				dc_set_clear(hw, DC_CURSOR_CONFIG + offset, BIT(3), 0x03);
> +			}
> +			hw->cursor[i].dirty = false;
> +		}
> +	}
> +
> +	if (hw->qos.dirty) {
> +		dc_set_clear(hw, DC_QOS_CONFIG, (hw->qos.high_value << 4) |
> +					 hw->qos.low_value, 0xFF);
> +		hw->qos.dirty = false;
> +	}
> +}
> +
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.h b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
> new file mode 100644
> index 000000000..9a1d767ae
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
> @@ -0,0 +1,492 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DC_HW_H__
> +#define __VS_DC_HW_H__
> +
> +#define AQ_INTR_ACKNOWLEDGE				0x0010
> +#define AQ_INTR_ENBL					0x0014

Please take care to align all the defines.

> +#define DC_HW_REVISION					0x0024
> +#define DC_HW_CHIP_CID					0x0030
> +
> +#define DC_REG_BASE						0x0800
> +#define DC_REG_RANGE					0x2000
> +#define DC_SEC_REG_OFFSET				0x100000
> +
> +#define DC_FRAMEBUFFER_CONFIG			0x1518
> +#define DC_FRAMEBUFFER_CONFIG_EX		0x1CC0
> +#define DC_FRAMEBUFFER_SCALE_CONFIG		0x1520
> +#define DC_FRAMEBUFFER_TOP_LEFT			0x24D8
> +#define DC_FRAMEBUFFER_BOTTOM_RIGHT		0x24E0
> +#define DC_FRAMEBUFFER_ADDRESS			0x1400
> +#define DC_FRAMEBUFFER_U_ADDRESS		0x1530
> +#define DC_FRAMEBUFFER_V_ADDRESS		0x1538
> +#define DC_FRAMEBUFFER_STRIDE			0x1408
> +#define DC_FRAMEBUFFER_U_STRIDE			0x1800
> +#define DC_FRAMEBUFFER_V_STRIDE			0x1808
> +#define DC_FRAMEBUFFER_SIZE				0x1810
> +#define DC_FRAMEBUFFER_SCALE_FACTOR_X		0x1828
> +#define DC_FRAMEBUFFER_SCALE_FACTOR_Y		0x1830
> +#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX	0x1838
> +#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA	0x1A00
> +#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX	0x1A08
> +#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA	0x1A10
> +#define DC_FRAMEBUFFER_INIT_OFFSET			0x1A20
> +#define DC_FRAMEBUFFER_COLOR_KEY			0x1508
> +#define DC_FRAMEBUFFER_COLOR_KEY_HIGH		0x1510
> +#define DC_FRAMEBUFFER_CLEAR_VALUE			0x1A18
> +#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX	0x1818
> +#define DC_FRAMEBUFFER_COLOR_TABLE_DATA		0x1820
> +#define DC_FRAMEBUFFER_BG_COLOR				0x1528
> +#define DC_FRAMEBUFFER_ROI_ORIGIN			0x1CB0
> +#define DC_FRAMEBUFFER_ROI_SIZE				0x1CB8
> +#define DC_FRAMEBUFFER_WATER_MARK			0x1CE8
> +#define DC_FRAMEBUFFER_DEGAMMA_INDEX		0x1D88
> +#define DC_FRAMEBUFFER_DEGAMMA_DATA			0x1D90
> +#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA		0x1D98
> +#define DC_FRAMEBUFFER_YUVTORGB_COEF0		0x1DA0
> +#define DC_FRAMEBUFFER_YUVTORGB_COEF1		0x1DA8
> +#define DC_FRAMEBUFFER_YUVTORGB_COEF2		0x1DB0
> +#define DC_FRAMEBUFFER_YUVTORGB_COEF3		0x1DB8
> +#define DC_FRAMEBUFFER_YUVTORGB_COEF4		0x1E00
> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD0		0x1E08
> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD1		0x1E10
> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD2		0x1E18
> +#define DC_FRAMEBUFFER_Y_CLAMP_BOUND		0x1E88
> +#define DC_FRAMEBUFFER_UV_CLAMP_BOUND		0x1E90
> +#define DC_FRAMEBUFFER_RGBTORGB_COEF0		0x1E20
> +#define DC_FRAMEBUFFER_RGBTORGB_COEF1		0x1E28
> +#define DC_FRAMEBUFFER_RGBTORGB_COEF2		0x1E30
> +#define DC_FRAMEBUFFER_RGBTORGB_COEF3		0x1E38
> +#define DC_FRAMEBUFFER_RGBTORGB_COEF4		0x1E40
> +#define DC_FRAMEBUFFER_BLEND_CONFIG			0x2510
> +#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR		0x2500
> +#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR		0x2508
> +
> +#define DC_OVERLAY_CONFIG					0x1540
> +#define DC_OVERLAY_CONFIG_EX				0x2540
> +#define DC_OVERLAY_SCALE_CONFIG				0x1C00
> +#define DC_OVERLAY_BLEND_CONFIG				0x1580
> +#define DC_OVERLAY_TOP_LEFT					0x1640
> +#define DC_OVERLAY_BOTTOM_RIGHT				0x1680
> +#define DC_OVERLAY_ADDRESS					0x15C0
> +#define DC_OVERLAY_U_ADDRESS				0x1840
> +#define DC_OVERLAY_V_ADDRESS				0x1880
> +#define DC_OVERLAY_STRIDE					0x1600
> +#define DC_OVERLAY_U_STRIDE					0x18C0
> +#define DC_OVERLAY_V_STRIDE					0x1900
> +#define DC_OVERLAY_SIZE						0x17C0
> +#define DC_OVERLAY_SCALE_FACTOR_X			0x1A40
> +#define DC_OVERLAY_SCALE_FACTOR_Y			0x1A80
> +#define DC_OVERLAY_H_FILTER_COEF_INDEX		0x1AC0
> +#define DC_OVERLAY_H_FILTER_COEF_DATA		0x1B00
> +#define DC_OVERLAY_V_FILTER_COEF_INDEX		0x1B40
> +#define DC_OVERLAY_V_FILTER_COEF_DATA		0x1B80
> +#define DC_OVERLAY_INIT_OFFSET				0x1BC0
> +#define DC_OVERLAY_COLOR_KEY				0x1740
> +#define DC_OVERLAY_COLOR_KEY_HIGH			0x1780
> +#define DC_OVERLAY_CLEAR_VALUE				0x1940
> +#define DC_OVERLAY_COLOR_TABLE_INDEX		0x1980
> +#define DC_OVERLAY_COLOR_TABLE_DATA			0x19C0
> +#define DC_OVERLAY_SRC_GLOBAL_COLOR			0x16C0
> +#define DC_OVERLAY_DST_GLOBAL_COLOR			0x1700
> +#define DC_OVERLAY_ROI_ORIGIN				0x1D00
> +#define DC_OVERLAY_ROI_SIZE					0x1D40
> +#define DC_OVERLAY_WATER_MARK				0x1DC0
> +#define DC_OVERLAY_DEGAMMA_INDEX			0x2200
> +#define DC_OVERLAY_DEGAMMA_DATA				0x2240
> +#define DC_OVERLAY_DEGAMMA_EX_DATA			0x2280
> +#define DC_OVERLAY_YUVTORGB_COEF0			0x1EC0
> +#define DC_OVERLAY_YUVTORGB_COEF1			0x1F00
> +#define DC_OVERLAY_YUVTORGB_COEF2			0x1F40
> +#define DC_OVERLAY_YUVTORGB_COEF3			0x1F80
> +#define DC_OVERLAY_YUVTORGB_COEF4			0x1FC0
> +#define DC_OVERLAY_YUVTORGB_COEFD0			0x2000
> +#define DC_OVERLAY_YUVTORGB_COEFD1			0x2040
> +#define DC_OVERLAY_YUVTORGB_COEFD2			0x2080
> +#define DC_OVERLAY_Y_CLAMP_BOUND			0x22C0
> +#define DC_OVERLAY_UV_CLAMP_BOUND			0x2300
> +#define DC_OVERLAY_RGBTORGB_COEF0			0x20C0
> +#define DC_OVERLAY_RGBTORGB_COEF1			0x2100
> +#define DC_OVERLAY_RGBTORGB_COEF2			0x2140
> +#define DC_OVERLAY_RGBTORGB_COEF3			0x2180
> +#define DC_OVERLAY_RGBTORGB_COEF4			0x21C0
> +
> +#define DC_CURSOR_CONFIG					0x1468
> +#define DC_CURSOR_ADDRESS					0x146C
> +#define DC_CURSOR_LOCATION					0x1470
> +#define DC_CURSOR_BACKGROUND				0x1474
> +#define DC_CURSOR_FOREGROUND				0x1478
> +#define DC_CURSOR_CLK_GATING				0x1484
> +#define DC_CURSOR_CONFIG_EX					0x24E8
> +#define DC_CURSOR_OFFSET					0x1080
> +
> +#define DC_DISPLAY_DITHER_CONFIG		0x1410
> +#define DC_DISPLAY_PANEL_CONFIG			0x1418
> +#define DC_DISPLAY_PANEL_CONFIG_EX		0x2518
> +#define DC_DISPLAY_DITHER_TABLE_LOW		0x1420
> +#define DC_DISPLAY_DITHER_TABLE_HIGH	0x1428
> +#define DC_DISPLAY_H					0x1430
> +#define DC_DISPLAY_H_SYNC				0x1438
> +#define DC_DISPLAY_V					0x1440
> +#define DC_DISPLAY_V_SYNC				0x1448
> +#define DC_DISPLAY_CURRENT_LOCATION		0x1450
> +#define DC_DISPLAY_GAMMA_INDEX			0x1458
> +#define DC_DISPLAY_GAMMA_DATA			0x1460
> +#define DC_DISPLAY_INT					0x147C
> +#define DC_DISPLAY_INT_ENABLE			0x1480
> +#define DC_DISPLAY_DBI_CONFIG			0x1488
> +#define DC_DISPLAY_GENERAL_CONFIG		0x14B0
> +#define DC_DISPLAY_DPI_CONFIG			0x14B8
> +#define DC_DISPLAY_PANEL_START			0x1CCC
> +#define DC_DISPLAY_DEBUG_COUNTER_SELECT		0x14D0
> +#define DC_DISPLAY_DEBUG_COUNTER_VALUE		0x14D8
> +#define DC_DISPLAY_DP_CONFIG				0x1CD0
> +#define DC_DISPLAY_GAMMA_EX_INDEX			0x1CF0
> +#define DC_DISPLAY_GAMMA_EX_DATA			0x1CF8
> +#define DC_DISPLAY_GAMMA_EX_ONE_DATA		0x1D80
> +#define DC_DISPLAY_RGBTOYUV_COEF0			0x1E48
> +#define DC_DISPLAY_RGBTOYUV_COEF1			0x1E50
> +#define DC_DISPLAY_RGBTOYUV_COEF2			0x1E58
> +#define DC_DISPLAY_RGBTOYUV_COEF3			0x1E60
> +#define DC_DISPLAY_RGBTOYUV_COEF4			0x1E68
> +#define DC_DISPLAY_RGBTOYUV_COEFD0			0x1E70
> +#define DC_DISPLAY_RGBTOYUV_COEFD1			0x1E78
> +#define DC_DISPLAY_RGBTOYUV_COEFD2			0x1E80
> +
> +#define DC_CLK_GATTING					0x1A28
> +#define DC_QOS_CONFIG					0x1A38
> +
> +#define DC_TRANSPARENCY_OPAQUE			0x00
> +#define DC_TRANSPARENCY_KEY				0x02
> +#define DC_DISPLAY_DITHERTABLE_LOW		0x7B48F3C0
> +#define DC_DISPLAY_DITHERTABLE_HIGH		0x596AD1E2
> +
> +#define GAMMA_SIZE						256
> +#define GAMMA_EX_SIZE					300
> +#define DEGAMMA_SIZE					260
> +
> +#define RGB_TO_RGB_TABLE_SIZE			9
> +#define YUV_TO_RGB_TABLE_SIZE			16
> +#define RGB_TO_YUV_TABLE_SIZE			12
> +
> +#define DC_LAYER_NUM	6
> +#define DC_DISPLAY_NUM	2
> +#define DC_CURSOR_NUM	2
> +
> +#define DC_TILE_MODE4X4	0x15
> +
> +enum dc_chip_rev {
> +	DC_REV_0,/* For HW_REV_5720,HW_REV_5721_311 */
> +	DC_REV_1,/* For HW_REV_5721_30B */
> +	DC_REV_2,/* For HW_REV_5721_310 */
> +};
> +
> +enum dc_hw_plane_id {
> +	PRIMARY_PLANE_0,
> +	OVERLAY_PLANE_0,
> +	OVERLAY_PLANE_1,
> +	PRIMARY_PLANE_1,
> +	OVERLAY_PLANE_2,
> +	OVERLAY_PLANE_3,
> +	CURSOR_PLANE_0,
> +	CURSOR_PLANE_1,
> +	PLANE_NUM
> +};
> +
> +enum dc_hw_color_format {
> +	FORMAT_X4R4G4B4,//0

Space after the comma will make it more readable. Or just define the 
values explicitly, it will also be more readable.

> +	FORMAT_A4R4G4B4,//1
> +	FORMAT_X1R5G5B5,//2
> +	FORMAT_A1R5G5B5,//3
> +	FORMAT_R5G6B5,//4
> +	FORMAT_X8R8G8B8,//5
> +	FORMAT_A8R8G8B8,//6
> +	FORMAT_YUY2,//7
> +	FORMAT_UYVY,//8
> +	FORMAT_INDEX8,//9
> +	FORMAT_MONOCHROME,//10
> +	FORMAT_YV12 = 0xf,
> +	FORMAT_A8,//16
> +	FORMAT_NV12,//17
> +	FORMAT_NV16,//18
> +	FORMAT_RG16,//19
> +	FORMAT_R8,//20
> +	FORMAT_NV12_10BIT,//21
> +	FORMAT_A2R10G10B10,//22
> +	FORMAT_NV16_10BIT,//23
> +	FORMAT_INDEX1,//24
> +	FORMAT_INDEX2,//25
> +	FORMAT_INDEX4,//26
> +	FORMAT_P010,//27
> +	FORMAT_YUV444,//28
> +	FORMAT_YUV444_10BIT,//29
> +};
> +
> +enum dc_hw_yuv_color_space {
> +	COLOR_SPACE_601 = 0,
> +	COLOR_SPACE_709 = 1,
> +	COLOR_SPACE_2020 = 3,
> +};
> +
> +enum dc_hw_rotation {
> +	ROT_0 = 0,
> +	ROT_90 = 4,
> +	ROT_180 = 5,
> +	ROT_270 = 6,
> +	FLIP_X = 1,
> +	FLIP_Y = 2,
> +	FLIP_XY = 3,
> +};
> +
> +enum dc_hw_swizzle {
> +	SWIZZLE_ARGB = 0,
> +	SWIZZLE_RGBA,
> +	SWIZZLE_ABGR,
> +	SWIZZLE_BGRA,
> +};
> +
> +enum dc_hw_out {
> +	OUT_DPI,
> +	OUT_DP,
> +};
> +
> +enum dc_hw_cursor_size {
> +	CURSOR_SIZE_32X32 = 0,
> +	CURSOR_SIZE_64X64,
> +};
> +
> +enum dc_hw_blend_mode {
> +	/* out.rgb = plane_alpha * fg.rgb +
> +	 *		(1 - (plane_alpha * fg.alpha)) * bg.rgb
> +	 */
> +	BLEND_PREMULTI,
> +	/* out.rgb = plane_alpha * fg.alpha * fg.rgb +
> +	 *		(1 - (plane_alpha * fg.alpha)) * bg.rgb
> +	 */
> +	BLEND_COVERAGE,
> +	/* out.rgb = plane_alpha * fg.rgb +
> +	 *		(1 - plane_alpha) * bg.rgb
> +	 */
> +	BLEND_PIXEL_NONE,

Can you use defines from <drm/drm_blend.h>?

> +};
> +
> +struct dc_hw_plane_reg {
> +	u32 y_address;
> +	u32 u_address;
> +	u32 v_address;
> +	u32 y_stride;
> +	u32 u_stride;
> +	u32 v_stride;
> +	u32 size;
> +	u32 top_left;
> +	u32 bottom_right;
> +	u32 scale_factor_x;
> +	u32 scale_factor_y;
> +	u32 h_filter_coef_index;
> +	u32 h_filter_coef_data;
> +	u32 v_filter_coef_index;
> +	u32 v_filter_coef_data;
> +	u32 init_offset;
> +	u32 color_key;
> +	u32 color_key_high;
> +	u32 clear_value;
> +	u32 color_table_index;
> +	u32 color_table_data;
> +	u32 scale_config;
> +	u32 water_mark;
> +	u32 degamma_index;
> +	u32 degamma_data;
> +	u32 degamma_ex_data;
> +	u32 src_global_color;
> +	u32 dst_global_color;
> +	u32 blend_config;
> +	u32 roi_origin;
> +	u32 roi_size;
> +	u32 yuv_to_rgb_coef0;
> +	u32 yuv_to_rgb_coef1;
> +	u32 yuv_to_rgb_coef2;
> +	u32 yuv_to_rgb_coef3;
> +	u32 yuv_to_rgb_coef4;
> +	u32 yuv_to_rgb_coefd0;
> +	u32 yuv_to_rgb_coefd1;
> +	u32 yuv_to_rgb_coefd2;
> +	u32 y_clamp_bound;
> +	u32 uv_clamp_bound;
> +	u32 rgb_to_rgb_coef0;
> +	u32 rgb_to_rgb_coef1;
> +	u32 rgb_to_rgb_coef2;
> +	u32 rgb_to_rgb_coef3;
> +	u32 rgb_to_rgb_coef4;
> +};
> +
> +struct dc_hw_fb {
> +	u32 y_address;
> +	u32 u_address;
> +	u32 v_address;
> +	u32 clear_value;
> +	u32 water_mark;
> +	u16 y_stride;
> +	u16 u_stride;
> +	u16 v_stride;
> +	u16 width;
> +	u16 height;
> +	u8	format;
> +	u8	tile_mode;
> +	u8	rotation;
> +	u8	yuv_color_space;
> +	u8	swizzle;
> +	u8	uv_swizzle;
> +	u8	zpos;
> +	u8	display_id;
> +	bool	clear_enable;
> +	bool	dec_enable;
> +	bool	enable;
> +	bool	dirty;
> +};
> +
> +struct dc_hw_scale {
> +	u32 scale_factor_x;
> +	u32 scale_factor_y;
> +	bool	enable;
> +	bool	dirty;
> +};
> +
> +struct dc_hw_position {
> +	u16 start_x;
> +	u16 start_y;
> +	u16 end_x;
> +	u16 end_y;
> +	bool	dirty;
> +};
> +
> +struct dc_hw_blend {
> +	u8	alpha;
> +	u8	blend_mode;
> +	bool	dirty;
> +};
> +
> +struct dc_hw_colorkey {
> +	u32 colorkey;
> +	u32 colorkey_high;
> +	u8	transparency;
> +	bool dirty;
> +};
> +
> +struct dc_hw_roi {
> +	u16 x;
> +	u16 y;
> +	u16 width;
> +	u16 height;
> +	bool enable;
> +	bool dirty;
> +};
> +
> +struct dc_hw_cursor {
> +	u32 address;
> +	u16 x;
> +	u16 y;
> +	u16 hot_x;
> +	u16 hot_y;
> +	u8	size;
> +	u8	display_id;
> +	bool	enable;
> +	bool	dirty;
> +};
> +
> +struct dc_hw_display {
> +	u32 bus_format;
> +	u16 h_active;
> +	u16 h_total;
> +	u16 h_sync_start;
> +	u16 h_sync_end;
> +	u16 v_active;
> +	u16 v_total;
> +	u16 v_sync_start;
> +	u16 v_sync_end;
> +	u8	id;
> +	bool	h_sync_polarity;
> +	bool	v_sync_polarity;
> +	bool	enable;
> +};
> +
> +struct dc_hw_gamma {
> +	u16 gamma[GAMMA_EX_SIZE][3];
> +	bool	enable;
> +	bool	dirty;
> +};
> +
> +struct dc_hw_degamma {
> +	u16 degamma[DEGAMMA_SIZE][3];
> +	u32 mode;
> +	bool	dirty;
> +};
> +
> +struct dc_hw_plane {
> +	struct dc_hw_fb			fb;
> +	struct dc_hw_position	pos;
> +	struct dc_hw_scale		scale;
> +	struct dc_hw_blend		blend;
> +	struct dc_hw_roi		roi;
> +	struct dc_hw_colorkey	colorkey;
> +	struct dc_hw_degamma	degamma;
> +};
> +
> +struct dc_hw_qos {
> +	u8	  low_value;
> +	u8	  high_value;
> +	bool  dirty;
> +};
> +
> +struct dc_hw_read {
> +	u32			reg;
> +	u32			value;
> +};
> +
> +struct dc_hw;
> +struct dc_hw_funcs {
> +	void (*gamma)(struct dc_hw *hw);
> +	void (*plane)(struct dc_hw *hw);
> +	void (*display)(struct dc_hw *hw, struct dc_hw_display *display);
> +};
> +
> +struct dc_hw {
> +	enum dc_chip_rev rev;
> +	enum dc_hw_out		out[DC_DISPLAY_NUM];
> +	void			*hi_base;
> +	void			*reg_base;
> +
> +	struct dc_hw_display display[DC_DISPLAY_NUM];
> +	struct dc_hw_gamma	 gamma[DC_DISPLAY_NUM];
> +	struct dc_hw_plane	 plane[DC_LAYER_NUM];
> +	struct dc_hw_cursor  cursor[DC_CURSOR_NUM];
> +	struct dc_hw_qos	 qos;
> +	struct dc_hw_funcs	 *func;
> +	struct vs_dc_info	 *info;
> +};
> +
> +int dc_hw_init(struct dc_hw *hw);
> +void dc_hw_deinit(struct dc_hw *hw);
> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
> +			struct dc_hw_fb *fb, struct dc_hw_scale *scale,
> +			struct dc_hw_position *pos, struct dc_hw_blend *blend);
> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode);
> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi);
> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
> +			   struct dc_hw_colorkey *colorkey);
> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos);
> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor);
> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
> +			u16 r, u16 g, u16 b);
> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable);
> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display);
> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable);
> +u32 dc_hw_get_interrupt(struct dc_hw *hw);
> +bool dc_hw_check_underflow(struct dc_hw *hw);
> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable);
> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id);
> +void dc_hw_commit(struct dc_hw *hw);
> +
> +#endif /* __VS_DC_HW_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
> index da7698c3d..3cd533cfa 100644
> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> @@ -21,6 +21,7 @@
>   
>   #include "vs_drv.h"
>   #include "vs_modeset.h"
> +#include "vs_dc.h"
>   
>   #define DRV_NAME	"verisilicon"
>   #define DRV_DESC	"Verisilicon DRM driver"
> @@ -123,6 +124,7 @@ static const struct component_master_ops vs_drm_ops = {
>   };
>   
>   static struct platform_driver *drm_sub_drivers[] = {
> +	&dc_platform_driver,
>   
>   };
>   
> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c
> new file mode 100644
> index 000000000..9bd066015
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_plane.c
> @@ -0,0 +1,526 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_blend.h>
> +#include <drm/drm_gem_dma_helper.h>
> +#include <drm/drm_fb_dma_helper.h>
> +#include <drm/drm_framebuffer.h>
> +#include <drm/drm_plane.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include <drm/vs_drm.h>
> +
> +#include "vs_plane.h"
> +#include "vs_drv.h"
> +#include "vs_dc.h"
> +
> +static void vs_plane_reset(struct drm_plane *plane)
> +{
> +	struct vs_plane_state *state;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +
> +	if (plane->state) {
> +		__drm_atomic_helper_plane_destroy_state(plane->state);
> +
> +		state = to_vs_plane_state(plane->state);
> +		kfree(state);
> +		plane->state = NULL;
> +	}
> +

Use vs_plane_destroy_state directly. This way you won't miss a refcount 
on blobs.

> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return;
> +
> +	state->degamma = VS_DEGAMMA_DISABLE;
> +	state->degamma_changed = false;
> +	state->base.zpos = vs_plane->id;
> +	memset(&state->status, 0, sizeof(state->status));
> +
> +	__drm_atomic_helper_plane_reset(plane, &state->base);
> +}
> +
> +static void _vs_plane_duplicate_blob(struct vs_plane_state *state,
> +				     struct vs_plane_state *ori_state)
> +{
> +	state->watermark = ori_state->watermark;
> +	state->color_mgmt = ori_state->color_mgmt;
> +	state->roi = ori_state->roi;
> +
> +	if (state->watermark)
> +		drm_property_blob_get(state->watermark);
> +	if (state->color_mgmt)
> +		drm_property_blob_get(state->color_mgmt);
> +	if (state->roi)
> +		drm_property_blob_get(state->roi);
> +}
> +
> +static int
> +_vs_plane_set_property_blob_from_id(struct drm_device *dev,
> +				    struct drm_property_blob **blob,
> +				    u64 blob_id,
> +				    size_t expected_size)
> +{
> +	struct drm_property_blob *new_blob = NULL;
> +
> +	if (blob_id) {
> +		new_blob = drm_property_lookup_blob(dev, blob_id);
> +		if (!new_blob)
> +			return -EINVAL;
> +
> +		if (new_blob->length != expected_size) {
> +			drm_property_blob_put(new_blob);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	drm_property_replace_blob(blob, new_blob);
> +	drm_property_blob_put(new_blob);
> +
> +	return 0;
> +}
> +
> +static struct drm_plane_state *
> +vs_plane_atomic_duplicate_state(struct drm_plane *plane)
> +{
> +	struct vs_plane_state *ori_state;
> +	struct vs_plane_state *state;
> +
> +	if (WARN_ON(!plane->state))
> +		return NULL;
> +
> +	ori_state = to_vs_plane_state(plane->state);
> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return NULL;
> +
> +	__drm_atomic_helper_plane_duplicate_state(plane, &state->base);
> +
> +	state->degamma = ori_state->degamma;
> +	state->degamma_changed = ori_state->degamma_changed;
> +
> +	_vs_plane_duplicate_blob(state, ori_state);
> +	memcpy(&state->status, &ori_state->status, sizeof(ori_state->status));
> +
> +	return &state->base;
> +}
> +
> +static void vs_plane_atomic_destroy_state(struct drm_plane *plane,
> +					  struct drm_plane_state *state)
> +{
> +	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
> +
> +	__drm_atomic_helper_plane_destroy_state(state);
> +
> +	drm_property_blob_put(vs_plane_state->watermark);
> +	drm_property_blob_put(vs_plane_state->color_mgmt);
> +	drm_property_blob_put(vs_plane_state->roi);
> +	kfree(vs_plane_state);
> +}
> +
> +static int vs_plane_atomic_set_property(struct drm_plane *plane,
> +					struct drm_plane_state *state,
> +					struct drm_property *property,
> +					uint64_t val)
> +{
> +	struct drm_device *dev = plane->dev;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
> +	int ret = 0;
> +
> +	if (property == vs_plane->degamma_mode) {
> +		if (vs_plane_state->degamma != val) {
> +			vs_plane_state->degamma = val;
> +			vs_plane_state->degamma_changed = true;
> +		} else {
> +			vs_plane_state->degamma_changed = false;
> +		}
> +	} else if (property == vs_plane->watermark_prop) {
> +		ret = _vs_plane_set_property_blob_from_id(dev,
> +							  &vs_plane_state->watermark,
> +							  val,
> +							  sizeof(struct drm_vs_watermark));
> +		return ret;
> +	} else if (property == vs_plane->color_mgmt_prop) {
> +		ret = _vs_plane_set_property_blob_from_id(dev,
> +							  &vs_plane_state->color_mgmt,
> +							  val,
> +							  sizeof(struct drm_vs_color_mgmt));
> +		return ret;
> +	} else if (property == vs_plane->roi_prop) {
> +		ret = _vs_plane_set_property_blob_from_id(dev,
> +							  &vs_plane_state->roi,
> +							  val,
> +							  sizeof(struct drm_vs_roi));
> +		return ret;
> +	} else {
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int vs_plane_atomic_get_property(struct drm_plane *plane,
> +					const struct drm_plane_state *state,
> +					struct drm_property *property,
> +					uint64_t *val)
> +{
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +	const struct vs_plane_state *vs_plane_state =
> +		container_of(state, const struct vs_plane_state, base);
> +
> +	if (property == vs_plane->degamma_mode)
> +		*val = vs_plane_state->degamma;
> +	else if (property == vs_plane->watermark_prop)
> +		*val = (vs_plane_state->watermark) ?
> +					vs_plane_state->watermark->base.id : 0;
> +	else if (property == vs_plane->color_mgmt_prop)
> +		*val = (vs_plane_state->color_mgmt) ?
> +					vs_plane_state->color_mgmt->base.id : 0;

degamma and color management should use standard properties.

> +	else if (property == vs_plane->roi_prop)
> +		*val = (vs_plane_state->roi) ?
> +					vs_plane_state->roi->base.id : 0;
> +	else
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static bool vs_format_mod_supported(struct drm_plane *plane,
> +				    u32 format,
> +				    u64 modifier)
> +{
> +	int i;
> +
> +	/* We always have to allow these modifiers:
> +	 * 1. Core DRM checks for LINEAR support if userspace does not provide modifiers.
> +	 * 2. Not passing any modifiers is the same as explicitly passing INVALID.
> +	 */
> +	if (modifier == DRM_FORMAT_MOD_LINEAR)
> +		return true;
> +
> +	/* Check that the modifier is on the list of the plane's supported modifiers. */
> +	for (i = 0; i < plane->modifier_count; i++) {
> +		if (modifier == plane->modifiers[i])
> +			break;
> +	}
> +
> +	if (i == plane->modifier_count)
> +		return false;
> +
> +	return true;
> +}
> +
> +const struct drm_plane_funcs vs_plane_funcs = {
> +	.update_plane		= drm_atomic_helper_update_plane,
> +	.disable_plane		= drm_atomic_helper_disable_plane,
> +	.reset			= vs_plane_reset,
> +	.atomic_duplicate_state = vs_plane_atomic_duplicate_state,
> +	.atomic_destroy_state	= vs_plane_atomic_destroy_state,
> +	.atomic_set_property	= vs_plane_atomic_set_property,
> +	.atomic_get_property	= vs_plane_atomic_get_property,
> +	.format_mod_supported	= vs_format_mod_supported,
> +};
> +
> +static unsigned char vs_get_plane_number(struct drm_framebuffer *fb)
> +{
> +	const struct drm_format_info *info;
> +
> +	if (!fb)
> +		return 0;
> +
> +	info = drm_format_info(fb->format->format);
> +	if (!info || info->num_planes > DRM_FORMAT_MAX_PLANES)
> +		return 0;
> +
> +	return info->num_planes;
> +}
> +
> +static int vs_plane_atomic_check(struct drm_plane *plane,
> +				 struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> +										 plane);
> +	unsigned char i, num_planes;
> +	struct drm_framebuffer *fb = new_plane_state->fb;
> +	struct drm_crtc *crtc = new_plane_state->crtc;
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
> +
> +	if (!crtc || !fb)
> +		return 0;
> +
> +	num_planes = vs_get_plane_number(fb);
> +
> +	for (i = 0; i < num_planes; i++) {
> +		dma_addr_t dma_addr;
> +
> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
> +		plane_state->dma_addr[i] = dma_addr;
> +	}
> +
> +	return vs_dc_check_plane(dc, plane, state);
> +}
> +
> +static int vs_cursor_plane_atomic_check(struct drm_plane *plane,
> +					struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> +									  plane);
> +	unsigned char i, num_planes;
> +	struct drm_framebuffer *fb = new_plane_state->fb;
> +	struct drm_crtc *crtc = new_plane_state->crtc;
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
> +
> +	if (!crtc || !fb)
> +		return 0;
> +
> +	num_planes = vs_get_plane_number(fb);
> +
> +	for (i = 0; i < num_planes; i++) {
> +		dma_addr_t dma_addr;
> +
> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
> +		plane_state->dma_addr[i] = dma_addr;
> +	}
> +
> +	return vs_dc_check_cursor_plane(dc, plane, state);
> +}
> +
> +static void vs_plane_atomic_update(struct drm_plane *plane,
> +				   struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
> +									  plane);

New line after the equal sign will be better.

> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> +									  plane);
> +
> +	unsigned char i, num_planes;
> +	struct drm_framebuffer *fb;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +	struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	if (!new_state->fb || !new_state->crtc)
> +		return;

if (!new_state->visible) ?

> +
> +	fb = new_state->fb;
> +
> +	drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
> +
> +	num_planes = vs_get_plane_number(fb);
> +
> +	for (i = 0; i < num_planes; i++) {
> +		dma_addr_t dma_addr;
> +
> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
> +		plane_state->dma_addr[i] = dma_addr;
> +	}
> +
> +	plane_state->status.src = drm_plane_state_src(new_state);
> +	plane_state->status.dest = drm_plane_state_dest(new_state);
> +
> +	vs_dc_update_plane(dc, vs_plane, plane, state);
> +}
> +
> +static void vs_cursor_plane_atomic_update(struct drm_plane *plane,
> +					  struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
> +									   plane);
> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> +									   plane);
> +	unsigned char i, num_planes;
> +	struct drm_framebuffer *fb;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +	struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	if (!new_state->fb || !new_state->crtc)

and here.

> +		return;
> +
> +	fb = new_state->fb;
> +	drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
> +
> +	num_planes = vs_get_plane_number(fb);
> +
> +	for (i = 0; i < num_planes; i++) {
> +		dma_addr_t dma_addr;
> +
> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
> +		plane_state->dma_addr[i] = dma_addr;
> +	}
> +
> +	plane_state->status.src = drm_plane_state_src(new_state);
> +	plane_state->status.dest = drm_plane_state_dest(new_state);
> +
> +	vs_dc_update_cursor_plane(dc, vs_plane, plane, state);
> +}
> +
> +static void vs_plane_atomic_disable(struct drm_plane *plane,
> +				    struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> +									   plane);
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +	struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_disable_plane(dc, vs_plane, old_state);
> +}
> +
> +static void vs_cursor_plane_atomic_disable(struct drm_plane *plane,
> +					   struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> +									   plane);
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +	struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_disable_cursor_plane(dc, vs_plane, old_state);
> +}
> +
> +const struct drm_plane_helper_funcs primary_plane_helpers = {
> +	.atomic_check	= vs_plane_atomic_check,
> +	.atomic_update	= vs_plane_atomic_update,
> +	.atomic_disable = vs_plane_atomic_disable,
> +};
> +
> +const struct drm_plane_helper_funcs overlay_plane_helpers = {
> +	.atomic_check	= vs_plane_atomic_check,
> +	.atomic_update	= vs_plane_atomic_update,
> +	.atomic_disable = vs_plane_atomic_disable,
> +};
> +
> +const struct drm_plane_helper_funcs cursor_plane_helpers = {
> +	.atomic_check	= vs_cursor_plane_atomic_check,
> +	.atomic_update	= vs_cursor_plane_atomic_update,
> +	.atomic_disable = vs_cursor_plane_atomic_disable,
> +};
> +
> +static const struct drm_prop_enum_list vs_degamma_mode_enum_list[] = {
> +	{ VS_DEGAMMA_DISABLE,	"disabled" },
> +	{ VS_DEGAMMA_BT709,	"preset degamma for BT709" },
> +	{ VS_DEGAMMA_BT2020,	"preset degamma for BT2020" },
> +};
> +
> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
> +				 struct vs_plane_info *info,
> +				 unsigned int layer_num,
> +				 unsigned int possible_crtcs)
> +{
> +	struct vs_plane *plane;
> +	int ret;
> +
> +	if (!info)
> +		return NULL;
> +
> +	plane = drmm_universal_plane_alloc(drm_dev, struct vs_plane, base,
> +					   possible_crtcs,
> +					   &vs_plane_funcs,
> +					   info->formats, info->num_formats,
> +					   info->modifiers, info->type,
> +					   info->name ? info->name : NULL);
> +	if (IS_ERR(plane))
> +		return ERR_CAST(plane);
> +
> +	if (info->type == DRM_PLANE_TYPE_PRIMARY)
> +		drm_plane_helper_add(&plane->base, &primary_plane_helpers);
> +	else if (info->type == DRM_PLANE_TYPE_CURSOR)
> +		drm_plane_helper_add(&plane->base, &cursor_plane_helpers);
> +	else
> +		drm_plane_helper_add(&plane->base, &overlay_plane_helpers);
> +
> +	/* Set up the plane properties */
> +	if (info->degamma_size) {
> +		plane->degamma_mode =
> +		drm_property_create_enum(drm_dev, 0,
> +					 "DEGAMMA_MODE",
> +					 vs_degamma_mode_enum_list,
> +					 ARRAY_SIZE(vs_degamma_mode_enum_list));
> +
> +		if (!plane->degamma_mode)
> +			return NULL;
> +
> +		drm_object_attach_property(&plane->base.base,
> +					   plane->degamma_mode,
> +					   VS_DEGAMMA_DISABLE);
> +	}
> +
> +	if (info->rotation) {
> +		ret = drm_plane_create_rotation_property(&plane->base,
> +							 DRM_MODE_ROTATE_0,
> +							 info->rotation);
> +		if (ret)
> +			return NULL;
> +	}
> +
> +	if (info->blend_mode) {
> +		ret = drm_plane_create_blend_mode_property(&plane->base,
> +							   info->blend_mode);
> +		if (ret)
> +			return NULL;
> +		ret = drm_plane_create_alpha_property(&plane->base);
> +		if (ret)
> +			return NULL;
> +	}
> +
> +	if (info->color_encoding) {
> +		ret = drm_plane_create_color_properties(&plane->base,
> +							info->color_encoding,
> +							BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
> +							DRM_COLOR_YCBCR_BT709,
> +							DRM_COLOR_YCBCR_LIMITED_RANGE);
> +		if (ret)
> +			return NULL;
> +	}
> +
> +	if (info->zpos != 255) {
> +		ret = drm_plane_create_zpos_property(&plane->base, info->zpos, 0,
> +						     layer_num - 1);
> +		if (ret)
> +			return NULL;
> +	} else {
> +		ret = drm_plane_create_zpos_immutable_property(&plane->base,
> +							       info->zpos);
> +		if (ret)
> +			return NULL;
> +	}
> +
> +	if (info->watermark) {
> +		plane->watermark_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
> +							    "WATERMARK", 0);
> +		if (!plane->watermark_prop)
> +			return NULL;
> +		drm_object_attach_property(&plane->base.base, plane->watermark_prop, 0);
> +	}
> +
> +	if (info->color_mgmt) {
> +		plane->color_mgmt_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
> +							     "COLOR_CONFIG", 0);
> +		if (!plane->color_mgmt_prop)
> +			return NULL;
> +
> +		drm_object_attach_property(&plane->base.base, plane->color_mgmt_prop, 0);
> +	}
> +
> +	if (info->roi) {
> +		plane->roi_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
> +						      "ROI", 0);
> +		if (!plane->roi_prop)
> +			return NULL;
> +
> +		drm_object_attach_property(&plane->base.base, plane->roi_prop, 0);
> +	}
> +
> +	return plane;
> +}
> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h
> new file mode 100644
> index 000000000..554b74e96
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_plane.h
> @@ -0,0 +1,58 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_PLANE_H__
> +#define __VS_PLANE_H__
> +
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include "vs_type.h"
> +
> +struct vs_plane_status {
> +	u32 tile_mode;
> +	struct drm_rect src;
> +	struct drm_rect dest;
> +};
> +
> +struct vs_plane_state {
> +	struct drm_plane_state base;
> +	struct vs_plane_status status; /* for debugfs */
> +
> +	struct drm_property_blob *watermark;
> +	struct drm_property_blob *color_mgmt;
> +	struct drm_property_blob *roi;
> +	dma_addr_t dma_addr[DRM_FORMAT_MAX_PLANES];
> +
> +	u32 degamma;
> +	bool degamma_changed;
> +};
> +
> +struct vs_plane {
> +	struct drm_plane base;
> +	u8 id;
> +
> +	struct drm_property *degamma_mode;
> +	struct drm_property *watermark_prop;
> +	struct drm_property *color_mgmt_prop;
> +	struct drm_property *roi_prop;
> +};
> +
> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
> +				 struct vs_plane_info *info,
> +				 unsigned int layer_num,
> +				 unsigned int possible_crtcs);
> +
> +static inline struct vs_plane *to_vs_plane(struct drm_plane *plane)
> +{
> +	return container_of(plane, struct vs_plane, base);
> +}
> +
> +static inline struct vs_plane_state *
> +to_vs_plane_state(struct drm_plane_state *state)
> +{
> +	return container_of(state, struct vs_plane_state, base);
> +}
> +#endif /* __VS_PLANE_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_type.h b/drivers/gpu/drm/verisilicon/vs_type.h
> new file mode 100644
> index 000000000..7d3378e29
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_type.h
> @@ -0,0 +1,69 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_TYPE_H__
> +#define __VS_TYPE_H__
> +
> +#include <drm/drm_plane.h>
> +#include <drm/drm_plane_helper.h>
> +
> +struct vs_plane_info {
> +	const char *name;
> +	u8 id;
> +	enum drm_plane_type type;
> +	unsigned int num_formats;
> +	const u32 *formats;
> +	u8 num_modifiers;
> +	const u64 *modifiers;
> +	unsigned int min_width;
> +	unsigned int min_height;
> +	unsigned int max_width;
> +	unsigned int max_height;
> +	unsigned int rotation;
> +	unsigned int blend_mode;
> +	unsigned int color_encoding;
> +
> +	/* 0 means no de-gamma LUT */
> +	unsigned int degamma_size;
> +
> +	int min_scale; /* 16.16 fixed point */
> +	int max_scale; /* 16.16 fixed point */
> +
> +	/* default zorder value,
> +	 * and 255 means unsupported zorder capability
> +	 */
> +	u8	 zpos;
> +
> +	bool watermark;
> +	bool color_mgmt;
> +	bool roi;
> +};
> +
> +struct vs_dc_info {
> +	const char *name;
> +
> +	u8 panel_num;
> +
> +	/* planes */
> +	u8 plane_num;
> +	const struct vs_plane_info *planes;
> +
> +	u8 layer_num;
> +	unsigned int max_bpc;
> +	unsigned int color_formats;
> +
> +	/* 0 means no gamma LUT */
> +	u16 gamma_size;
> +	u8 gamma_bits;
> +
> +	u16 pitch_alignment;
> +
> +	bool pipe_sync;
> +	bool background;
> +	bool panel_sync;
> +	bool cap_dec;
> +};
> +
> +#endif /* __VS_TYPE_H__ */

-- 
With best wishes
Dmitry


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
  2023-10-25 19:28     ` Dmitry Baryshkov
  (?)
@ 2023-10-25 19:49       ` Ville Syrjälä
  -1 siblings, 0 replies; 117+ messages in thread
From: Ville Syrjälä @ 2023-10-25 19:49 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig, Conor Dooley, Albert Ou,
	Emil Renner Berthing, christian.koenig, Thomas Zimmermann,
	Bjorn Andersson, Chris Morgan, Maxime Ripard, Jagan Teki,
	Jack Zhu, Rob Herring, Palmer Dabbelt, Krzysztof Kozlowski,
	Paul Walmsley, Shengyang Chen, Changhuang Liang, Shawn Guo,
	Sumit Semwal

On Wed, Oct 25, 2023 at 10:28:56PM +0300, Dmitry Baryshkov wrote:
> On 25/10/2023 13:39, Keith Zhao wrote:
> > add 2 crtcs and 8 planes in vs-drm
> > 
> > Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> > ---
> >   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
> >   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
> >   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
> >   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
> >   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
> >   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
> >   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
> >   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
> >   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
> >   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
> >   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
> >   11 files changed, 4494 insertions(+), 2 deletions(-)
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
> > 
> > diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> > index 7d3be305b..1d48016ca 100644
> > --- a/drivers/gpu/drm/verisilicon/Makefile
> > +++ b/drivers/gpu/drm/verisilicon/Makefile
> > @@ -1,7 +1,11 @@
> >   # SPDX-License-Identifier: GPL-2.0
> >   
> > -vs_drm-objs := vs_drv.o \
> > -		vs_modeset.o
> > +vs_drm-objs := vs_dc_hw.o \
> > +		vs_dc.o \
> > +		vs_crtc.o \
> > +		vs_drv.o \
> > +		vs_modeset.o \
> > +		vs_plane.o
> >   
> >   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
> >   
> > diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
> > new file mode 100644
> > index 000000000..8a658ea77
> > --- /dev/null
> > +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
> > @@ -0,0 +1,257 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> > + *
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/debugfs.h>
> > +#include <linux/media-bus-format.h>
> > +
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_atomic.h>
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_gem_atomic_helper.h>
> > +#include <drm/drm_vblank.h>
> > +#include <drm/vs_drm.h>
> > +
> > +#include "vs_crtc.h"
> > +#include "vs_dc.h"
> > +#include "vs_drv.h"
> > +
> > +static void vs_crtc_reset(struct drm_crtc *crtc)
> > +{
> > +	struct vs_crtc_state *state;
> > +
> > +	if (crtc->state) {
> > +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
> > +
> > +		state = to_vs_crtc_state(crtc->state);
> > +		kfree(state);
> > +		crtc->state = NULL;
> > +	}
> 
> You can call your crtc_destroy_state function directly here.
> 
> > +
> > +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> > +	if (!state)
> > +		return;
> > +
> > +	__drm_atomic_helper_crtc_reset(crtc, &state->base);
> > +}
> > +
> > +static struct drm_crtc_state *
> > +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
> > +{
> > +	struct vs_crtc_state *ori_state;
> 
> It might be a matter of taste, but it is usually old_state.
> 
> > +	struct vs_crtc_state *state;
> > +
> > +	if (!crtc->state)
> > +		return NULL;
> > +
> > +	ori_state = to_vs_crtc_state(crtc->state);
> > +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> > +	if (!state)
> > +		return NULL;
> > +
> > +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
> > +
> > +	state->output_fmt = ori_state->output_fmt;
> > +	state->encoder_type = ori_state->encoder_type;
> > +	state->bpp = ori_state->bpp;
> > +	state->underflow = ori_state->underflow;
> 
> Can you use kmemdup instead?
> 
> > +
> > +	return &state->base;
> > +}
> > +
> > +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
> > +					 struct drm_crtc_state *state)
> > +{
> > +	__drm_atomic_helper_crtc_destroy_state(state);
> > +	kfree(to_vs_crtc_state(state));
> > +}
> > +
> > +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
> > +{
> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> > +
> > +	vs_dc_enable_vblank(dc, true);
> > +
> > +	return 0;
> > +}
> > +
> > +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
> > +{
> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> > +
> > +	vs_dc_enable_vblank(dc, false);
> > +}
> > +
> > +static const struct drm_crtc_funcs vs_crtc_funcs = {
> > +	.set_config		= drm_atomic_helper_set_config,
> > +	.page_flip		= drm_atomic_helper_page_flip,
> 
> destroy is required, see drm_mode_config_cleanup()
> 
> > +	.reset			= vs_crtc_reset,
> > +	.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
> > +	.atomic_destroy_state	= vs_crtc_atomic_destroy_state,
> 
> please consider adding atomic_print_state to output driver-specific bits.
> 
> > +	.enable_vblank		= vs_crtc_enable_vblank,
> > +	.disable_vblank		= vs_crtc_disable_vblank,
> > +};
> > +
> > +static u8 cal_pixel_bits(u32 bus_format)
> 
> This looks like a generic helper code, which can go to a common place.
> 
> > +{
> > +	u8 bpp;
> > +
> > +	switch (bus_format) {
> > +	case MEDIA_BUS_FMT_RGB565_1X16:
> > +	case MEDIA_BUS_FMT_UYVY8_1X16:
> > +		bpp = 16;
> > +		break;
> > +	case MEDIA_BUS_FMT_RGB666_1X18:
> > +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> > +		bpp = 18;
> > +		break;
> > +	case MEDIA_BUS_FMT_UYVY10_1X20:
> > +		bpp = 20;
> > +		break;
> > +	case MEDIA_BUS_FMT_BGR888_1X24:
> > +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> > +	case MEDIA_BUS_FMT_YUV8_1X24:
> > +		bpp = 24;
> > +		break;
> > +	case MEDIA_BUS_FMT_RGB101010_1X30:
> > +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> > +	case MEDIA_BUS_FMT_YUV10_1X30:
> > +		bpp = 30;
> > +		break;
> > +	default:
> > +		bpp = 24;
> > +		break;
> > +	}
> > +
> > +	return bpp;
> > +}
> > +
> > +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
> > +				  struct drm_atomic_state *state)
> > +{
> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> > +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
> > +
> > +	vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
> > +
> > +	vs_dc_enable(dc, crtc);
> > +	drm_crtc_vblank_on(crtc);
> > +}
> > +
> > +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
> > +				   struct drm_atomic_state *state)
> > +{
> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> > +
> > +	drm_crtc_vblank_off(crtc);
> > +
> > +	vs_dc_disable(dc, crtc);
> > +
> > +	if (crtc->state->event && !crtc->state->active) {
> > +		spin_lock_irq(&crtc->dev->event_lock);
> > +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > +		spin_unlock_irq(&crtc->dev->event_lock);
> > +
> > +		crtc->state->event = NULL;
> 
> I think even should be cleared within the lock.

event_lock doesn't protect anything in the crtc state.

But the bigger problem in this code is the prevalent crtc->state
usage. That should pretty much never be done, especially in anything
that can get called from the actual commit phase where you no longer
have the locks held. Instead one should almost always use the
get_{new,old}_state() stuff, or the old/new/oldnew state iterators.

> 
> > +	}
> > +}
> > +
> > +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
> > +				 struct drm_atomic_state *state)
> > +{
> > +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
> > +									  crtc);
> > +
> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> > +	struct device *dev = vs_crtc->dev;
> > +	struct drm_property_blob *blob = crtc->state->gamma_lut;

Eg. here you are using drm_atomic_get_new_crtc_state() correctly, but
then proceed to directly access crtc->state anyway.

> > +	struct drm_color_lut *lut;
> > +	struct vs_dc *dc = dev_get_drvdata(dev);
> > +
> > +	if (crtc_state->color_mgmt_changed) {
> > +		if (blob && blob->length) {
> > +			lut = blob->data;
> > +			vs_dc_set_gamma(dc, crtc, lut,
> > +					blob->length / sizeof(*lut));
> > +			vs_dc_enable_gamma(dc, crtc, true);
> > +		} else {
> > +			vs_dc_enable_gamma(dc, crtc, false);
> > +		}
> > +	}
> > +}

-- 
Ville Syrjälä
Intel

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-10-25 19:49       ` Ville Syrjälä
  0 siblings, 0 replies; 117+ messages in thread
From: Ville Syrjälä @ 2023-10-25 19:49 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig, Conor Dooley, Albert Ou,
	Emil Renner Berthing, christian.koenig, Thomas Zimmermann,
	Bjorn Andersson, Chris Morgan, Maxime Ripard, Jagan Teki,
	Jack Zhu, Rob Herring, Palmer Dabbelt, Krzysztof Kozlowski,
	Paul Walmsley, Shengyang Chen, Changhuang Liang, Shawn Guo,
	Sumit Semwal

On Wed, Oct 25, 2023 at 10:28:56PM +0300, Dmitry Baryshkov wrote:
> On 25/10/2023 13:39, Keith Zhao wrote:
> > add 2 crtcs and 8 planes in vs-drm
> > 
> > Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> > ---
> >   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
> >   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
> >   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
> >   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
> >   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
> >   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
> >   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
> >   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
> >   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
> >   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
> >   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
> >   11 files changed, 4494 insertions(+), 2 deletions(-)
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
> > 
> > diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> > index 7d3be305b..1d48016ca 100644
> > --- a/drivers/gpu/drm/verisilicon/Makefile
> > +++ b/drivers/gpu/drm/verisilicon/Makefile
> > @@ -1,7 +1,11 @@
> >   # SPDX-License-Identifier: GPL-2.0
> >   
> > -vs_drm-objs := vs_drv.o \
> > -		vs_modeset.o
> > +vs_drm-objs := vs_dc_hw.o \
> > +		vs_dc.o \
> > +		vs_crtc.o \
> > +		vs_drv.o \
> > +		vs_modeset.o \
> > +		vs_plane.o
> >   
> >   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
> >   
> > diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
> > new file mode 100644
> > index 000000000..8a658ea77
> > --- /dev/null
> > +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
> > @@ -0,0 +1,257 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> > + *
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/debugfs.h>
> > +#include <linux/media-bus-format.h>
> > +
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_atomic.h>
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_gem_atomic_helper.h>
> > +#include <drm/drm_vblank.h>
> > +#include <drm/vs_drm.h>
> > +
> > +#include "vs_crtc.h"
> > +#include "vs_dc.h"
> > +#include "vs_drv.h"
> > +
> > +static void vs_crtc_reset(struct drm_crtc *crtc)
> > +{
> > +	struct vs_crtc_state *state;
> > +
> > +	if (crtc->state) {
> > +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
> > +
> > +		state = to_vs_crtc_state(crtc->state);
> > +		kfree(state);
> > +		crtc->state = NULL;
> > +	}
> 
> You can call your crtc_destroy_state function directly here.
> 
> > +
> > +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> > +	if (!state)
> > +		return;
> > +
> > +	__drm_atomic_helper_crtc_reset(crtc, &state->base);
> > +}
> > +
> > +static struct drm_crtc_state *
> > +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
> > +{
> > +	struct vs_crtc_state *ori_state;
> 
> It might be a matter of taste, but it is usually old_state.
> 
> > +	struct vs_crtc_state *state;
> > +
> > +	if (!crtc->state)
> > +		return NULL;
> > +
> > +	ori_state = to_vs_crtc_state(crtc->state);
> > +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> > +	if (!state)
> > +		return NULL;
> > +
> > +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
> > +
> > +	state->output_fmt = ori_state->output_fmt;
> > +	state->encoder_type = ori_state->encoder_type;
> > +	state->bpp = ori_state->bpp;
> > +	state->underflow = ori_state->underflow;
> 
> Can you use kmemdup instead?
> 
> > +
> > +	return &state->base;
> > +}
> > +
> > +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
> > +					 struct drm_crtc_state *state)
> > +{
> > +	__drm_atomic_helper_crtc_destroy_state(state);
> > +	kfree(to_vs_crtc_state(state));
> > +}
> > +
> > +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
> > +{
> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> > +
> > +	vs_dc_enable_vblank(dc, true);
> > +
> > +	return 0;
> > +}
> > +
> > +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
> > +{
> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> > +
> > +	vs_dc_enable_vblank(dc, false);
> > +}
> > +
> > +static const struct drm_crtc_funcs vs_crtc_funcs = {
> > +	.set_config		= drm_atomic_helper_set_config,
> > +	.page_flip		= drm_atomic_helper_page_flip,
> 
> destroy is required, see drm_mode_config_cleanup()
> 
> > +	.reset			= vs_crtc_reset,
> > +	.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
> > +	.atomic_destroy_state	= vs_crtc_atomic_destroy_state,
> 
> please consider adding atomic_print_state to output driver-specific bits.
> 
> > +	.enable_vblank		= vs_crtc_enable_vblank,
> > +	.disable_vblank		= vs_crtc_disable_vblank,
> > +};
> > +
> > +static u8 cal_pixel_bits(u32 bus_format)
> 
> This looks like a generic helper code, which can go to a common place.
> 
> > +{
> > +	u8 bpp;
> > +
> > +	switch (bus_format) {
> > +	case MEDIA_BUS_FMT_RGB565_1X16:
> > +	case MEDIA_BUS_FMT_UYVY8_1X16:
> > +		bpp = 16;
> > +		break;
> > +	case MEDIA_BUS_FMT_RGB666_1X18:
> > +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> > +		bpp = 18;
> > +		break;
> > +	case MEDIA_BUS_FMT_UYVY10_1X20:
> > +		bpp = 20;
> > +		break;
> > +	case MEDIA_BUS_FMT_BGR888_1X24:
> > +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> > +	case MEDIA_BUS_FMT_YUV8_1X24:
> > +		bpp = 24;
> > +		break;
> > +	case MEDIA_BUS_FMT_RGB101010_1X30:
> > +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> > +	case MEDIA_BUS_FMT_YUV10_1X30:
> > +		bpp = 30;
> > +		break;
> > +	default:
> > +		bpp = 24;
> > +		break;
> > +	}
> > +
> > +	return bpp;
> > +}
> > +
> > +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
> > +				  struct drm_atomic_state *state)
> > +{
> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> > +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
> > +
> > +	vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
> > +
> > +	vs_dc_enable(dc, crtc);
> > +	drm_crtc_vblank_on(crtc);
> > +}
> > +
> > +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
> > +				   struct drm_atomic_state *state)
> > +{
> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> > +
> > +	drm_crtc_vblank_off(crtc);
> > +
> > +	vs_dc_disable(dc, crtc);
> > +
> > +	if (crtc->state->event && !crtc->state->active) {
> > +		spin_lock_irq(&crtc->dev->event_lock);
> > +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > +		spin_unlock_irq(&crtc->dev->event_lock);
> > +
> > +		crtc->state->event = NULL;
> 
> I think even should be cleared within the lock.

event_lock doesn't protect anything in the crtc state.

But the bigger problem in this code is the prevalent crtc->state
usage. That should pretty much never be done, especially in anything
that can get called from the actual commit phase where you no longer
have the locks held. Instead one should almost always use the
get_{new,old}_state() stuff, or the old/new/oldnew state iterators.

> 
> > +	}
> > +}
> > +
> > +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
> > +				 struct drm_atomic_state *state)
> > +{
> > +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
> > +									  crtc);
> > +
> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> > +	struct device *dev = vs_crtc->dev;
> > +	struct drm_property_blob *blob = crtc->state->gamma_lut;

Eg. here you are using drm_atomic_get_new_crtc_state() correctly, but
then proceed to directly access crtc->state anyway.

> > +	struct drm_color_lut *lut;
> > +	struct vs_dc *dc = dev_get_drvdata(dev);
> > +
> > +	if (crtc_state->color_mgmt_changed) {
> > +		if (blob && blob->length) {
> > +			lut = blob->data;
> > +			vs_dc_set_gamma(dc, crtc, lut,
> > +					blob->length / sizeof(*lut));
> > +			vs_dc_enable_gamma(dc, crtc, true);
> > +		} else {
> > +			vs_dc_enable_gamma(dc, crtc, false);
> > +		}
> > +	}
> > +}

-- 
Ville Syrjälä
Intel

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-10-25 19:49       ` Ville Syrjälä
  0 siblings, 0 replies; 117+ messages in thread
From: Ville Syrjälä @ 2023-10-25 19:49 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Emil Renner Berthing, dri-devel, Krzysztof Kozlowski,
	linux-riscv, Sumit Semwal, Shengyang Chen, linux-media,
	devicetree, Conor Dooley, Albert Ou, Maxime Ripard, Jagan Teki,
	linaro-mm-sig, Rob Herring, Chris Morgan, Paul Walmsley,
	Keith Zhao, Bjorn Andersson, linux-kernel, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Thomas Zimmermann, Shawn Guo,
	christian.koenig

On Wed, Oct 25, 2023 at 10:28:56PM +0300, Dmitry Baryshkov wrote:
> On 25/10/2023 13:39, Keith Zhao wrote:
> > add 2 crtcs and 8 planes in vs-drm
> > 
> > Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> > ---
> >   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
> >   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
> >   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
> >   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
> >   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
> >   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
> >   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
> >   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
> >   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
> >   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
> >   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
> >   11 files changed, 4494 insertions(+), 2 deletions(-)
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
> > 
> > diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> > index 7d3be305b..1d48016ca 100644
> > --- a/drivers/gpu/drm/verisilicon/Makefile
> > +++ b/drivers/gpu/drm/verisilicon/Makefile
> > @@ -1,7 +1,11 @@
> >   # SPDX-License-Identifier: GPL-2.0
> >   
> > -vs_drm-objs := vs_drv.o \
> > -		vs_modeset.o
> > +vs_drm-objs := vs_dc_hw.o \
> > +		vs_dc.o \
> > +		vs_crtc.o \
> > +		vs_drv.o \
> > +		vs_modeset.o \
> > +		vs_plane.o
> >   
> >   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
> >   
> > diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
> > new file mode 100644
> > index 000000000..8a658ea77
> > --- /dev/null
> > +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
> > @@ -0,0 +1,257 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> > + *
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/debugfs.h>
> > +#include <linux/media-bus-format.h>
> > +
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_atomic.h>
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_gem_atomic_helper.h>
> > +#include <drm/drm_vblank.h>
> > +#include <drm/vs_drm.h>
> > +
> > +#include "vs_crtc.h"
> > +#include "vs_dc.h"
> > +#include "vs_drv.h"
> > +
> > +static void vs_crtc_reset(struct drm_crtc *crtc)
> > +{
> > +	struct vs_crtc_state *state;
> > +
> > +	if (crtc->state) {
> > +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
> > +
> > +		state = to_vs_crtc_state(crtc->state);
> > +		kfree(state);
> > +		crtc->state = NULL;
> > +	}
> 
> You can call your crtc_destroy_state function directly here.
> 
> > +
> > +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> > +	if (!state)
> > +		return;
> > +
> > +	__drm_atomic_helper_crtc_reset(crtc, &state->base);
> > +}
> > +
> > +static struct drm_crtc_state *
> > +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
> > +{
> > +	struct vs_crtc_state *ori_state;
> 
> It might be a matter of taste, but it is usually old_state.
> 
> > +	struct vs_crtc_state *state;
> > +
> > +	if (!crtc->state)
> > +		return NULL;
> > +
> > +	ori_state = to_vs_crtc_state(crtc->state);
> > +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> > +	if (!state)
> > +		return NULL;
> > +
> > +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
> > +
> > +	state->output_fmt = ori_state->output_fmt;
> > +	state->encoder_type = ori_state->encoder_type;
> > +	state->bpp = ori_state->bpp;
> > +	state->underflow = ori_state->underflow;
> 
> Can you use kmemdup instead?
> 
> > +
> > +	return &state->base;
> > +}
> > +
> > +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
> > +					 struct drm_crtc_state *state)
> > +{
> > +	__drm_atomic_helper_crtc_destroy_state(state);
> > +	kfree(to_vs_crtc_state(state));
> > +}
> > +
> > +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
> > +{
> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> > +
> > +	vs_dc_enable_vblank(dc, true);
> > +
> > +	return 0;
> > +}
> > +
> > +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
> > +{
> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> > +
> > +	vs_dc_enable_vblank(dc, false);
> > +}
> > +
> > +static const struct drm_crtc_funcs vs_crtc_funcs = {
> > +	.set_config		= drm_atomic_helper_set_config,
> > +	.page_flip		= drm_atomic_helper_page_flip,
> 
> destroy is required, see drm_mode_config_cleanup()
> 
> > +	.reset			= vs_crtc_reset,
> > +	.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
> > +	.atomic_destroy_state	= vs_crtc_atomic_destroy_state,
> 
> please consider adding atomic_print_state to output driver-specific bits.
> 
> > +	.enable_vblank		= vs_crtc_enable_vblank,
> > +	.disable_vblank		= vs_crtc_disable_vblank,
> > +};
> > +
> > +static u8 cal_pixel_bits(u32 bus_format)
> 
> This looks like a generic helper code, which can go to a common place.
> 
> > +{
> > +	u8 bpp;
> > +
> > +	switch (bus_format) {
> > +	case MEDIA_BUS_FMT_RGB565_1X16:
> > +	case MEDIA_BUS_FMT_UYVY8_1X16:
> > +		bpp = 16;
> > +		break;
> > +	case MEDIA_BUS_FMT_RGB666_1X18:
> > +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> > +		bpp = 18;
> > +		break;
> > +	case MEDIA_BUS_FMT_UYVY10_1X20:
> > +		bpp = 20;
> > +		break;
> > +	case MEDIA_BUS_FMT_BGR888_1X24:
> > +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> > +	case MEDIA_BUS_FMT_YUV8_1X24:
> > +		bpp = 24;
> > +		break;
> > +	case MEDIA_BUS_FMT_RGB101010_1X30:
> > +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> > +	case MEDIA_BUS_FMT_YUV10_1X30:
> > +		bpp = 30;
> > +		break;
> > +	default:
> > +		bpp = 24;
> > +		break;
> > +	}
> > +
> > +	return bpp;
> > +}
> > +
> > +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
> > +				  struct drm_atomic_state *state)
> > +{
> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> > +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
> > +
> > +	vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
> > +
> > +	vs_dc_enable(dc, crtc);
> > +	drm_crtc_vblank_on(crtc);
> > +}
> > +
> > +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
> > +				   struct drm_atomic_state *state)
> > +{
> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> > +
> > +	drm_crtc_vblank_off(crtc);
> > +
> > +	vs_dc_disable(dc, crtc);
> > +
> > +	if (crtc->state->event && !crtc->state->active) {
> > +		spin_lock_irq(&crtc->dev->event_lock);
> > +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > +		spin_unlock_irq(&crtc->dev->event_lock);
> > +
> > +		crtc->state->event = NULL;
> 
> I think even should be cleared within the lock.

event_lock doesn't protect anything in the crtc state.

But the bigger problem in this code is the prevalent crtc->state
usage. That should pretty much never be done, especially in anything
that can get called from the actual commit phase where you no longer
have the locks held. Instead one should almost always use the
get_{new,old}_state() stuff, or the old/new/oldnew state iterators.

> 
> > +	}
> > +}
> > +
> > +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
> > +				 struct drm_atomic_state *state)
> > +{
> > +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
> > +									  crtc);
> > +
> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> > +	struct device *dev = vs_crtc->dev;
> > +	struct drm_property_blob *blob = crtc->state->gamma_lut;

Eg. here you are using drm_atomic_get_new_crtc_state() correctly, but
then proceed to directly access crtc->state anyway.

> > +	struct drm_color_lut *lut;
> > +	struct vs_dc *dc = dev_get_drvdata(dev);
> > +
> > +	if (crtc_state->color_mgmt_changed) {
> > +		if (blob && blob->length) {
> > +			lut = blob->data;
> > +			vs_dc_set_gamma(dc, crtc, lut,
> > +					blob->length / sizeof(*lut));
> > +			vs_dc_enable_gamma(dc, crtc, true);
> > +		} else {
> > +			vs_dc_enable_gamma(dc, crtc, false);
> > +		}
> > +	}
> > +}

-- 
Ville Syrjälä
Intel

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
  2023-10-25 10:39   ` Keith Zhao
  (?)
@ 2023-10-25 22:23     ` Dmitry Baryshkov
  -1 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-10-25 22:23 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig

On 25/10/2023 13:39, Keith Zhao wrote:
> add hdmi driver as encoder and connect
> 
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>   drivers/gpu/drm/verisilicon/Kconfig         |   8 +-
>   drivers/gpu/drm/verisilicon/Makefile        |   1 +
>   drivers/gpu/drm/verisilicon/starfive_hdmi.c | 949 ++++++++++++++++++++
>   drivers/gpu/drm/verisilicon/starfive_hdmi.h | 295 ++++++
>   drivers/gpu/drm/verisilicon/vs_drv.c        |   5 +
>   drivers/gpu/drm/verisilicon/vs_drv.h        |   4 +
>   6 files changed, 1261 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.c
>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.h
> 
> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
> index 3a361f8c8..122c786e3 100644
> --- a/drivers/gpu/drm/verisilicon/Kconfig
> +++ b/drivers/gpu/drm/verisilicon/Kconfig
> @@ -12,4 +12,10 @@ config DRM_VERISILICON
>   	  setting and buffer management. It does not
>   	  provide 2D or 3D acceleration.
>   
> -
> +config DRM_VERISILICON_STARFIVE_HDMI
> +	bool "Starfive HDMI extensions"
> +	depends on DRM_VERISILICON
> +	help
> +	   This selects support for StarFive soc specific extensions
> +	   for the Innosilicon HDMI driver. If you want to enable
> +	   HDMI on JH7110 based soc, you should select this option.
> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> index 1d48016ca..08350f25b 100644
> --- a/drivers/gpu/drm/verisilicon/Makefile
> +++ b/drivers/gpu/drm/verisilicon/Makefile
> @@ -7,5 +7,6 @@ vs_drm-objs := vs_dc_hw.o \
>   		vs_modeset.o \
>   		vs_plane.o
>   
> +vs_drm-$(CONFIG_DRM_VERISILICON_STARFIVE_HDMI) += starfive_hdmi.o
>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>   
> diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.c b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
> new file mode 100644
> index 000000000..d296c4b71
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
> @@ -0,0 +1,949 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/hdmi.h>
> +#include <linux/i2c.h>
> +#include <linux/irq.h>
> +#include <linux/media-bus-format.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/reset.h>
> +
> +#include <drm/bridge/dw_hdmi.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_managed.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_simple_kms_helper.h>
> +
> +#include "starfive_hdmi.h"
> +#include "vs_drv.h"
> +#include "vs_crtc.h"
> +
> +static struct starfive_hdmi *encoder_to_hdmi(struct drm_encoder *encoder)
> +{
> +	return container_of(encoder, struct starfive_hdmi, encoder);
> +}
> +
> +static struct starfive_hdmi *connector_to_hdmi(struct drm_connector *connector)
> +{
> +	return container_of(connector, struct starfive_hdmi, connector);
> +}
> +
> +struct starfive_hdmi_i2c {
> +	struct i2c_adapter adap;
> +
> +	u8 ddc_addr;
> +	u8 segment_addr;
> +	/* protects the edid data when use i2c cmd to read edid */
> +	struct mutex lock;
> +	struct completion cmp;
> +};
> +
> +static const struct pre_pll_config pre_pll_cfg_table[] = {
> +	{ 25175000,  25175000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0xf55555},
> +	{ 25200000,  25200000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0},
> +	{ 27000000,  27000000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0},

Such data structures are usually pretyt limited and hard to handle. 
Could you please replace it with the runtime calculations of

> +	{ 27027000,  27027000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0x170a3d},
> +	{ 28320000,  28320000, 1,  28, 2, 1, 1,  3, 0, 3, 4, 0, 0x51eb85},
> +	{ 30240000,  30240000, 1,  30, 2, 1, 1,  3, 0, 3, 4, 0, 0x3d70a3},
> +	{ 31500000,  31500000, 1,  31, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> +	{ 33750000,  33750000, 1,  33, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
> +	{ 36000000,  36000000, 1,  36, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{ 40000000,  40000000, 1,  80, 2, 2, 2, 12, 2, 2, 2, 0, 0},
> +	{ 46970000,  46970000, 1,  46, 2, 1, 1,  3, 0, 3, 4, 0, 0xf851eb},
> +	{ 49500000,  49500000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> +	{ 49000000,  49000000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{ 50000000,  50000000, 1,  50, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{ 54000000,  54000000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{ 54054000,  54054000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0x0dd2f1},
> +	{ 57284000,  57284000, 1,  57, 2, 1, 1,  3, 0, 3, 4, 0, 0x48b439},
> +	{ 58230000,  58230000, 1,  58, 2, 1, 1,  3, 0, 3, 4, 0, 0x3ae147},
> +	{ 59341000,  59341000, 1,  59, 2, 1, 1,  3, 0, 3, 4, 0, 0x574bc6},
> +	{ 59400000,  59400000, 1,  99, 3, 1, 1,  1, 3, 3, 4, 0, 0},
> +	{ 65000000,  65000000, 1, 130, 2, 2, 2,  12, 0, 2, 2, 0, 0},
> +	{ 68250000,  68250000, 1, 68,  2, 1, 1,  3,  0, 3, 4, 0, 0x3fffff},
> +	{ 71000000,  71000000, 1,  71, 2, 1, 1,  3, 0, 3,  4, 0, 0},
> +	{ 74176000,  74176000, 1,  98, 1, 2, 2,  1, 2, 3, 4, 0, 0xe6ae6b},
> +	{ 74250000,  74250000, 1,  99, 1, 2, 2,  1, 2, 3, 4, 0, 0},
> +	{ 75000000,  75000000, 1,  75, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{ 78750000,  78750000, 1,  78, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
> +	{ 79500000,  79500000, 1,  79, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> +	{ 83500000,  83500000, 2, 167, 2, 1, 1,  1, 0, 0,  6, 0, 0},
> +	{ 83500000, 104375000, 1, 104, 2, 1, 1,  1, 1, 0,  5, 0, 0x600000},
> +	{ 84858000,  84858000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xdba5e2},
> +	{ 85500000,  85500000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
> +	{ 85750000,  85750000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
> +	{ 85800000,  85800000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcccccc},
> +	{ 88750000,  88750000, 1,  88, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
> +	{ 89910000,  89910000, 1,  89, 2, 1, 1,  3, 0, 3, 4, 0, 0xe8f5c1},
> +	{ 90000000,  90000000, 1,  90, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{101000000, 101000000, 1, 101, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{102250000, 102250000, 1, 102, 2, 1, 1,  3, 0, 3, 4, 0, 0x3fffff},
> +	{106500000, 106500000, 1, 106, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> +	{108000000, 108000000, 1,  90, 3, 0, 0,  5, 0, 2,  2, 0, 0},
> +	{119000000, 119000000, 1, 119, 2, 1, 1,  3, 0, 3,  4, 0, 0},
> +	{131481000, 131481000, 1,  131, 2, 1, 1,  3, 0, 3,  4, 0, 0x7b22d1},
> +	{135000000, 135000000, 1,  135, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{136750000, 136750000, 1,  136, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
> +	{147180000, 147180000, 1,  147, 2, 1, 1,  3, 0, 3, 4, 0, 0x2e147a},
> +	{148352000, 148352000, 1,  98, 1, 1, 1,  1, 2, 2, 2, 0, 0xe6ae6b},
> +	{148500000, 148500000, 1,  99, 1, 1, 1,  1, 2, 2, 2, 0, 0},
> +	{154000000, 154000000, 1, 154, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{156000000, 156000000, 1, 156, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{157000000, 157000000, 1, 157, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{162000000, 162000000, 1, 162, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{174250000, 174250000, 1, 145, 3, 0, 0,  5, 0, 2, 2, 0, 0x355555},
> +	{174500000, 174500000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> +	{174570000, 174570000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x91eb84},
> +	{175500000, 175500000, 1, 175, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> +	{185590000, 185590000, 1, 185, 2, 1, 1,  3, 0, 3, 4, 0, 0x970a3c},
> +	{187000000, 187000000, 1, 187, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{241500000, 241500000, 1, 161, 1, 1, 1,  4, 0, 2,  2, 0, 0},
> +	{241700000, 241700000, 1, 241, 2, 1, 1,  3, 0, 3,  4, 0, 0xb33332},
> +	{262750000, 262750000, 1, 262, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
> +	{296500000, 296500000, 1, 296, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
> +	{296703000, 296703000, 1,  98, 0, 1, 1,  1, 0, 2,  2, 0, 0xe6ae6b},
> +	{297000000, 297000000, 1,  99, 0, 1, 1,  1, 0, 2,  2, 0, 0},
> +	{594000000, 594000000, 1,  99, 0, 2, 0,  1, 0, 1,  1, 0, 0},
> +	{0, 0, 0,  0, 0, 0, 0,  0, 0, 0,  0, 0, 0},
> +};
> +
> +static const struct post_pll_config post_pll_cfg_table[] = {
> +	{25200000,	1, 80, 13, 3, 1},
> +	{27000000,	1, 40, 11, 3, 1},
> +	{33750000,	1, 40, 11, 3, 1},
> +	{49000000,	1, 20, 1, 3, 3},
> +	{241700000, 1, 20, 1, 3, 3},
> +	{297000000, 4, 20, 0, 0, 3},
> +	{594000000, 4, 20, 0, 0, 0},
> +	{ /* sentinel */ }
> +};
> +
> +inline u8 hdmi_readb(struct starfive_hdmi *hdmi, u16 offset)
> +{
> +	return readl_relaxed(hdmi->regs + (offset) * 0x04);
> +}
> +
> +inline void hdmi_writeb(struct starfive_hdmi *hdmi, u16 offset, u32 val)
> +{
> +	writel_relaxed(val, hdmi->regs + (offset) * 0x04);
> +}
> +
> +inline void hdmi_modb(struct starfive_hdmi *hdmi, u16 offset,
> +			     u32 msk, u32 val)
> +{
> +	u8 temp = hdmi_readb(hdmi, offset) & ~msk;
> +
> +	temp |= val & msk;
> +	hdmi_writeb(hdmi, offset, temp);
> +}
> +
> +static int starfive_hdmi_enable_clk_deassert_rst(struct device *dev, struct starfive_hdmi *hdmi)
> +{
> +	int ret;
> +
> +	ret = clk_prepare_enable(hdmi->sys_clk);
> +	if (ret) {
> +		dev_err(dev, "Cannot enable HDMI sys clock: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(hdmi->mclk);
> +	if (ret) {
> +		dev_err(dev, "Cannot enable HDMI mclk clock: %d\n", ret);
> +		goto err_mclk;
> +	}
> +	ret = clk_prepare_enable(hdmi->bclk);

This code begs to use clk_bulk instead.

> +	if (ret) {
> +		dev_err(dev, "Cannot enable HDMI bclk clock: %d\n", ret);
> +		goto err_bclk;
> +	}
> +	ret = reset_control_deassert(hdmi->tx_rst);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to deassert tx_rst\n");
> +		goto err_rst;
> +	}
> +	return 0;
> +
> +err_rst:
> +	clk_disable_unprepare(hdmi->bclk);
> +err_bclk:
> +	clk_disable_unprepare(hdmi->mclk);
> +err_mclk:
> +	clk_disable_unprepare(hdmi->sys_clk);
> +	return ret;
> +}
> +
> +static void starfive_hdmi_disable_clk_assert_rst(struct device *dev, struct starfive_hdmi *hdmi)
> +{
> +	int ret;
> +
> +	ret = reset_control_assert(hdmi->tx_rst);
> +	if (ret < 0)
> +		dev_err(dev, "failed to assert tx_rst\n");
> +
> +	clk_disable_unprepare(hdmi->sys_clk);
> +	clk_disable_unprepare(hdmi->mclk);
> +	clk_disable_unprepare(hdmi->bclk);
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int hdmi_system_pm_suspend(struct device *dev)
> +{
> +	return pm_runtime_force_suspend(dev);
> +}
> +
> +static int hdmi_system_pm_resume(struct device *dev)
> +{
> +	return pm_runtime_force_resume(dev);
> +}
> +#endif
> +
> +#ifdef CONFIG_PM
> +static int hdmi_runtime_suspend(struct device *dev)
> +{
> +	struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
> +
> +	starfive_hdmi_disable_clk_assert_rst(dev, hdmi);
> +
> +	return 0;
> +}
> +
> +static int hdmi_runtime_resume(struct device *dev)
> +{
> +	struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
> +
> +	return starfive_hdmi_enable_clk_deassert_rst(dev, hdmi);
> +}
> +#endif
> +
> +static void starfive_hdmi_tx_phy_power_down(struct starfive_hdmi *hdmi)
> +{
> +	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
> +}
> +
> +static void starfive_hdmi_tx_phy_power_on(struct starfive_hdmi *hdmi)
> +{
> +	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);

It looks like one can inline these two helpers w/o too much troubles.

> +}
> +
> +static void starfive_hdmi_config_pll(struct starfive_hdmi *hdmi)
> +{
> +	u32 val;
> +	u8 reg_1ad_value = hdmi->post_cfg->post_div_en ?
> +		 hdmi->post_cfg->postdiv : 0x00;
> +	u8 reg_1aa_value = hdmi->post_cfg->post_div_en ?
> +		 0x0e : 0x02;
> +
> +	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN);
> +	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1,
> +		    STARFIVE_POST_PLL_POST_DIV_ENABLE |
> +		    STARFIVE_POST_PLL_REFCLK_SEL_TMDS |
> +		    STARFIVE_POST_PLL_POWER_DOWN);
> +	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_1, STARFIVE_PRE_PLL_PRE_DIV(hdmi->pre_cfg->prediv));
> +
> +	val = STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE | STARFIVE_SPREAD_SPECTRUM_MOD_DOWN;
> +	if (!hdmi->pre_cfg->fracdiv)
> +		val |= STARFIVE_PRE_PLL_FRAC_DIV_DISABLE;
> +	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_2,
> +		    STARFIVE_PRE_PLL_FB_DIV_11_8(hdmi->pre_cfg->fbdiv) | val);
> +	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_3,
> +		    STARFIVE_PRE_PLL_FB_DIV_7_0(hdmi->pre_cfg->fbdiv));
> +	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_4,
> +		    STARFIVE_PRE_PLL_TMDSCLK_DIV_C(hdmi->pre_cfg->tmds_div_c) |
> +		    STARFIVE_PRE_PLL_TMDSCLK_DIV_A(hdmi->pre_cfg->tmds_div_a) |
> +		    STARFIVE_PRE_PLL_TMDSCLK_DIV_B(hdmi->pre_cfg->tmds_div_b));
> +
> +	if (hdmi->pre_cfg->fracdiv) {
> +		hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_L,
> +			    STARFIVE_PRE_PLL_FRAC_DIV_7_0(hdmi->pre_cfg->fracdiv));
> +		hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_M,
> +			    STARFIVE_PRE_PLL_FRAC_DIV_15_8(hdmi->pre_cfg->fracdiv));
> +		hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_H,
> +			    STARFIVE_PRE_PLL_FRAC_DIV_23_16(hdmi->pre_cfg->fracdiv));
> +	}
> +
> +	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_5,
> +		    STARFIVE_PRE_PLL_PCLK_DIV_A(hdmi->pre_cfg->pclk_div_a) |
> +		    STARFIVE_PRE_PLL_PCLK_DIV_B(hdmi->pre_cfg->pclk_div_b));
> +	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_6,
> +		    STARFIVE_PRE_PLL_PCLK_DIV_C(hdmi->pre_cfg->pclk_div_c) |
> +		    STARFIVE_PRE_PLL_PCLK_DIV_D(hdmi->pre_cfg->pclk_div_d));
> +
> +	/*pre-pll power down*/
> +	hdmi_modb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN, 0);
> +
> +	hdmi_modb(hdmi, STARFIVE_POST_PLL_DIV_2, STARFIVE_POST_PLL_Pre_DIV_MASK,
> +		  STARFIVE_POST_PLL_PRE_DIV(hdmi->post_cfg->prediv));
> +	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_3, hdmi->post_cfg->fbdiv & 0xff);
> +	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_4, reg_1ad_value);
> +	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1, reg_1aa_value);
> +}
> +
> +static void starfive_hdmi_tmds_driver_on(struct starfive_hdmi *hdmi)
> +{
> +	hdmi_modb(hdmi, STARFIVE_TMDS_CONTROL,
> +		  STARFIVE_TMDS_DRIVER_ENABLE, STARFIVE_TMDS_DRIVER_ENABLE);
> +}
> +
> +static void starfive_hdmi_sync_tmds(struct starfive_hdmi *hdmi)
> +{
> +	/*first send 0 to this bit, then send 1 and keep 1 into this bit*/
> +	hdmi_writeb(hdmi, HDMI_SYNC, 0x0);
> +	hdmi_writeb(hdmi, HDMI_SYNC, 0x1);
> +}
> +
> +static void starfive_hdmi_i2c_init(struct starfive_hdmi *hdmi)
> +{
> +	int ddc_bus_freq;
> +
> +	ddc_bus_freq = (clk_get_rate(hdmi->sys_clk) >> 2) / HDMI_SCL_RATE;
> +
> +	hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
> +	hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
> +
> +	/* Clear the EDID interrupt flag and mute the interrupt */
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
> +}
> +
> +static const
> +struct pre_pll_config *starfive_hdmi_phy_get_pre_pll_cfg(struct starfive_hdmi *hdmi,
> +							 unsigned long rate)
> +{
> +	const struct pre_pll_config *cfg = pre_pll_cfg_table;
> +
> +	rate = (rate / 1000) * 1000;
> +	for (; cfg->pixclock != 0; cfg++)
> +		if (cfg->tmdsclock == rate && cfg->pixclock == rate)
> +			break;
> +
> +	if (cfg->pixclock == 0)
> +		return ERR_PTR(-EINVAL);
> +
> +	return cfg;
> +}
> +
> +static int starfive_hdmi_phy_clk_set_rate(struct starfive_hdmi *hdmi)
> +{
> +	hdmi->post_cfg = post_pll_cfg_table;
> +
> +	hdmi->pre_cfg = starfive_hdmi_phy_get_pre_pll_cfg(hdmi, hdmi->tmds_rate);
> +	if (IS_ERR(hdmi->pre_cfg))
> +		return PTR_ERR(hdmi->pre_cfg);
> +
> +	for (; hdmi->post_cfg->tmdsclock != 0; hdmi->post_cfg++)
> +		if (hdmi->tmds_rate <= hdmi->post_cfg->tmdsclock)
> +			break;
> +
> +	starfive_hdmi_config_pll(hdmi);
> +
> +	return 0;
> +}
> +
> +static int starfive_hdmi_config_video_timing(struct starfive_hdmi *hdmi,
> +					     struct drm_display_mode *mode)
> +{
> +	int value;
> +	/* Set detail external video timing */
> +	value = mode->htotal;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);

As you have hdmi_writeb already, adding hdmi_writew will help here.

> +
> +	value = mode->htotal - mode->hdisplay;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
> +
> +	value = mode->htotal - mode->hsync_start;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
> +
> +	value = mode->hsync_end - mode->hsync_start;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
> +
> +	value = mode->vtotal;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
> +
> +	value = mode->vtotal - mode->vdisplay;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
> +
> +	value = mode->vtotal - mode->vsync_start;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
> +
> +	value = mode->vsync_end - mode->vsync_start;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
> +
> +	/* Set detail external video timing polarity and interlace mode */
> +	value = v_EXTERANL_VIDEO(1);
> +	value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
> +		v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
> +	value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
> +		v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
> +	value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
> +		v_INETLACE(1) : v_INETLACE(0);
> +
> +	hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
> +	return 0;
> +}
> +
> +static int starfive_hdmi_setup(struct starfive_hdmi *hdmi,
> +			       struct drm_display_mode *mode)
> +{
> +	hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
> +	hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
> +	hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
> +
> +	hdmi->tmds_rate = mode->clock * 1000;
> +	starfive_hdmi_phy_clk_set_rate(hdmi);
> +
> +	while (!(hdmi_readb(hdmi, STARFIVE_PRE_PLL_LOCK_STATUS) & 0x1))
> +		continue;
> +	while (!(hdmi_readb(hdmi, STARFIVE_POST_PLL_LOCK_STATUS) & 0x1))
> +		continue;

Please use read_iopoll_timeout here.

> +
> +	/*turn on LDO*/
> +	hdmi_writeb(hdmi, STARFIVE_LDO_CONTROL, STARFIVE_LDO_ENABLE);
> +	/*turn on serializer*/
> +	hdmi_writeb(hdmi, STARFIVE_SERIALIER_CONTROL, STARFIVE_SERIALIER_ENABLE);
> +
> +	starfive_hdmi_tx_phy_power_down(hdmi);
> +	starfive_hdmi_config_video_timing(hdmi, mode);
> +	starfive_hdmi_tx_phy_power_on(hdmi);
> +
> +	starfive_hdmi_tmds_driver_on(hdmi);
> +	starfive_hdmi_sync_tmds(hdmi);
> +
> +	return 0;
> +}
> +
> +static void starfive_hdmi_encoder_mode_set(struct drm_encoder *encoder,
> +					   struct drm_display_mode *mode,
> +					   struct drm_display_mode *adj_mode)
> +{
> +	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> +
> +	drm_mode_copy(&hdmi->previous_mode, adj_mode);

Why do you need it?

> +}
> +
> +static void starfive_hdmi_encoder_enable(struct drm_encoder *encoder)
> +{
> +	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> +	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
> +	int ret, idx;
> +	struct drm_device *drm = hdmi->connector.dev;
> +
> +	if (drm && !drm_dev_enter(drm, &idx))
> +		return;
> +
> +	ret = pm_runtime_get_sync(hdmi->dev);
> +	if (ret < 0)
> +		return;
> +	mdelay(10);
> +	starfive_hdmi_setup(hdmi, mode);
> +
> +	if (drm)
> +		drm_dev_exit(idx);
> +}
> +
> +static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
> +{
> +	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> +
> +	int idx;
> +	struct drm_device *drm = hdmi->connector.dev;
> +
> +	if (drm && !drm_dev_enter(drm, &idx))
> +		return;
> +
> +	pm_runtime_put(hdmi->dev);
> +
> +	if (drm)
> +		drm_dev_exit(idx);
> +}
> +
> +static int
> +starfive_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
> +				   struct drm_crtc_state *crtc_state,
> +				   struct drm_connector_state *conn_state)
> +{
> +	bool valid = false;
> +	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
> +
> +	vs_crtc_state->encoder_type = encoder->encoder_type;
> +	vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
> +
> +	const struct pre_pll_config *cfg = pre_pll_cfg_table;
> +	int pclk = mode->clock * 1000;
> +
> +	for (; cfg->pixclock != 0; cfg++) {
> +		if (pclk == cfg->pixclock) {
> +			if (pclk > 297000000)

Magic value.

> +				continue;
> +
> +			valid = true;
> +			break;
> +		}
> +	}
> +
> +	return (valid) ? 0 : -EINVAL;
> +}
> +
> +static const struct drm_encoder_helper_funcs starfive_hdmi_encoder_helper_funcs = {
> +	.enable     = starfive_hdmi_encoder_enable,
> +	.disable    = starfive_hdmi_encoder_disable,
> +	.mode_set   = starfive_hdmi_encoder_mode_set,
> +	.atomic_check = starfive_hdmi_encoder_atomic_check,
> +};
> +
> +static enum drm_connector_status
> +starfive_hdmi_connector_detect(struct drm_connector *connector, bool force)
> +{
> +	struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
> +	struct drm_device *drm = hdmi->connector.dev;
> +	int ret;
> +	int idx;
> +
> +	if (drm && !drm_dev_enter(drm, &idx))
> +		return connector_status_disconnected;
> +
> +	ret = pm_runtime_get_sync(hdmi->dev);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
> +		connector_status_connected : connector_status_disconnected;
> +	pm_runtime_put(hdmi->dev);
> +
> +	if (drm)
> +		drm_dev_exit(idx);
> +
> +	return ret;
> +}
> +
> +static int starfive_hdmi_connector_get_modes(struct drm_connector *connector)
> +{
> +	struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
> +	struct edid *edid;
> +	int ret = 0;
> +
> +	if (!hdmi->ddc)
> +		return 0;
> +	ret = pm_runtime_get_sync(hdmi->dev);
> +	if (ret < 0)
> +		return ret;
> +
> +	edid = drm_get_edid(connector, hdmi->ddc);
> +	if (edid) {
> +		hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
> +		hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
> +		drm_connector_update_edid_property(connector, edid);
> +		ret = drm_add_edid_modes(connector, edid);
> +		kfree(edid);
> +	}
> +	pm_runtime_put(hdmi->dev);

Can you use drm_connector_helper_get_modes_from_ddc() here? And then 
drm_display_info.is_hdmi and .has_audio.

> +
> +	return ret;
> +}
> +
> +static enum drm_mode_status
> +starfive_hdmi_connector_mode_valid(struct drm_connector *connector,
> +				   struct drm_display_mode *mode)
> +{
> +	const struct pre_pll_config *cfg = pre_pll_cfg_table;
> +	int pclk = mode->clock * 1000;
> +	bool valid = false;
> +
> +	for (; cfg->pixclock != 0; cfg++) {
> +		if (pclk == cfg->pixclock) {
> +			if (pclk > 297000000)

magic value

> +				continue;
> +
> +			valid = true;
> +			break;
> +		}
> +	}
> +
> +	return (valid) ? MODE_OK : MODE_BAD;
> +}
> +
> +static int
> +starfive_hdmi_probe_single_connector_modes(struct drm_connector *connector,
> +					   u32 maxX, u32 maxY)
> +{
> +	return drm_helper_probe_single_connector_modes(connector, 3840, 2160);
> +}
> +
> +static const struct drm_connector_funcs starfive_hdmi_connector_funcs = {
> +	.fill_modes = starfive_hdmi_probe_single_connector_modes,
> +	.detect = starfive_hdmi_connector_detect,
> +	.reset = drm_atomic_helper_connector_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static struct drm_connector_helper_funcs starfive_hdmi_connector_helper_funcs = {
> +	.get_modes = starfive_hdmi_connector_get_modes,
> +	.mode_valid = starfive_hdmi_connector_mode_valid,
> +};
> +
> +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
> +{
> +	struct drm_encoder *encoder = &hdmi->encoder;
> +	struct device *dev = hdmi->dev;
> +
> +	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
> +
> +	/*
> +	 * If we failed to find the CRTC(s) which this encoder is
> +	 * supposed to be connected to, it's because the CRTC has
> +	 * not been registered yet.  Defer probing, and hope that
> +	 * the required CRTC is added later.
> +	 */
> +	if (encoder->possible_crtcs == 0)
> +		return -EPROBE_DEFER;
> +
> +	drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
> +
> +	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> +
> +	drm_connector_helper_add(&hdmi->connector,
> +				 &starfive_hdmi_connector_helper_funcs);
> +	drmm_connector_init(drm, &hdmi->connector,
> +			    &starfive_hdmi_connector_funcs,
> +			    DRM_MODE_CONNECTOR_HDMIA,

On an embedded device one can not be so sure. There can be MHL or HDMI 
Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.

> +			    hdmi->ddc);
> +
> +	drm_connector_attach_encoder(&hdmi->connector, encoder);
> +
> +	return 0;
> +}
> +
> +static irqreturn_t starfive_hdmi_i2c_irq(struct starfive_hdmi *hdmi)
> +{
> +	struct starfive_hdmi_i2c *i2c = hdmi->i2c;
> +	u8 stat;
> +
> +	stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
> +	if (!(stat & m_INT_EDID_READY))
> +		return IRQ_NONE;
> +
> +	/* Clear HDMI EDID interrupt flag */
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
> +
> +	complete(&i2c->cmp);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t starfive_hdmi_hardirq(int irq, void *dev_id)
> +{
> +	struct starfive_hdmi *hdmi = dev_id;
> +	irqreturn_t ret = IRQ_NONE;
> +	u8 interrupt;
> +
> +	if (hdmi->i2c)
> +		ret = starfive_hdmi_i2c_irq(hdmi);
> +
> +	interrupt = hdmi_readb(hdmi, HDMI_STATUS);
> +	if (interrupt & m_INT_HOTPLUG) {
> +		hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
> +		ret = IRQ_WAKE_THREAD;
> +	}
> +
> +	return ret;
> +}
> +
> +static irqreturn_t starfive_hdmi_irq(int irq, void *dev_id)
> +{
> +	struct starfive_hdmi *hdmi = dev_id;
> +
> +	drm_connector_helper_hpd_irq_event(&hdmi->connector);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int starfive_hdmi_i2c_read(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
> +{
> +	int length = msgs->len;
> +	u8 *buf = msgs->buf;
> +	int ret;
> +
> +	ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10);

read_poll_timeout

> +	if (!ret)
> +		return -EAGAIN;
> +
> +	while (length--)
> +		*buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
> +
> +	return 0;
> +}
> +
> +static int starfive_hdmi_i2c_write(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
> +{
> +	/*
> +	 * The DDC module only support read EDID message, so
> +	 * we assume that each word write to this i2c adapter
> +	 * should be the offset of EDID word address.
> +	 */
> +	if (msgs->len != 1 ||
> +	    (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR))
> +		return -EINVAL;
> +
> +	reinit_completion(&hdmi->i2c->cmp);
> +
> +	if (msgs->addr == DDC_SEGMENT_ADDR)
> +		hdmi->i2c->segment_addr = msgs->buf[0];
> +	if (msgs->addr == DDC_ADDR)
> +		hdmi->i2c->ddc_addr = msgs->buf[0];
> +
> +	/* Set edid fifo first addr */
> +	hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
> +
> +	/* Set edid word address 0x00/0x80 */
> +	hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
> +
> +	/* Set edid segment pointer */
> +	hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
> +
> +	return 0;
> +}
> +
> +static int starfive_hdmi_i2c_xfer(struct i2c_adapter *adap,
> +				  struct i2c_msg *msgs, int num)
> +{
> +	struct starfive_hdmi *hdmi = i2c_get_adapdata(adap);
> +	struct starfive_hdmi_i2c *i2c = hdmi->i2c;
> +	int i, ret = 0;
> +
> +	mutex_lock(&i2c->lock);
> +
> +	/* Clear the EDID interrupt flag and unmute the interrupt */
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
> +
> +	for (i = 0; i < num; i++) {
> +		DRM_DEV_DEBUG(hdmi->dev,
> +			      "xfer: num: %d/%d, len: %d, flags: %#x\n",
> +			      i + 1, num, msgs[i].len, msgs[i].flags);
> +
> +		if (msgs[i].flags & I2C_M_RD)
> +			ret = starfive_hdmi_i2c_read(hdmi, &msgs[i]);
> +		else
> +			ret = starfive_hdmi_i2c_write(hdmi, &msgs[i]);
> +
> +		if (ret < 0)
> +			break;
> +	}
> +
> +	if (!ret)
> +		ret = num;
> +
> +	/* Mute HDMI EDID interrupt */
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
> +
> +	mutex_unlock(&i2c->lock);
> +
> +	return ret;
> +}
> +
> +static u32 starfive_hdmi_i2c_func(struct i2c_adapter *adapter)
> +{
> +	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static const struct i2c_algorithm starfive_hdmi_algorithm = {
> +	.master_xfer	= starfive_hdmi_i2c_xfer,
> +	.functionality	= starfive_hdmi_i2c_func,
> +};
> +
> +static struct i2c_adapter *starfive_hdmi_i2c_adapter(struct starfive_hdmi *hdmi)
> +{
> +	struct i2c_adapter *adap;
> +	struct starfive_hdmi_i2c *i2c;
> +	int ret;
> +
> +	i2c = drmm_kzalloc(hdmi->drm_dev, sizeof(*i2c), GFP_KERNEL);
> +	if (!i2c)
> +		return ERR_PTR(-ENOMEM);
> +
> +	mutex_init(&i2c->lock);
> +	init_completion(&i2c->cmp);
> +
> +	adap = &i2c->adap;
> +	adap->class = I2C_CLASS_DDC;
> +	adap->owner = THIS_MODULE;
> +	adap->dev.parent = hdmi->dev;
> +	adap->algo = &starfive_hdmi_algorithm;
> +	strscpy(adap->name, "Starfive HDMI", sizeof(adap->name));
> +	i2c_set_adapdata(adap, hdmi);
> +
> +	ret = devm_i2c_add_adapter(hdmi->dev, adap);
> +	if (ret) {
> +		drm_warn(hdmi->drm_dev, "cannot add %s I2C adapter\n", adap->name);
> +		return ERR_PTR(ret);
> +	}
> +
> +	hdmi->i2c = i2c;
> +
> +	drm_info(hdmi->drm_dev, "registered %s I2C bus driver success\n", adap->name);
> +
> +	return adap;
> +}
> +
> +static int starfive_hdmi_get_clk_rst(struct device *dev, struct starfive_hdmi *hdmi)
> +{
> +	hdmi->sys_clk = devm_clk_get(dev, "sysclk");
> +	if (IS_ERR(hdmi->sys_clk)) {
> +		dev_err(dev, "Unable to get HDMI sysclk clk\n");
> +		return PTR_ERR(hdmi->sys_clk);
> +	}
> +	hdmi->mclk = devm_clk_get(dev, "mclk");
> +	if (IS_ERR(hdmi->mclk)) {
> +		dev_err(dev, "Unable to get HDMI mclk clk\n");
> +		return PTR_ERR(hdmi->mclk);
> +	}
> +	hdmi->bclk = devm_clk_get(dev, "bclk");
> +	if (IS_ERR(hdmi->bclk)) {
> +		dev_err(dev, "Unable to get HDMI bclk clk\n");
> +		return PTR_ERR(hdmi->bclk);
> +	}
> +	hdmi->tx_rst = devm_reset_control_get_by_index(dev, 0);
> +	if (IS_ERR(hdmi->tx_rst)) {
> +		dev_err(dev, "failed to get tx_rst reset\n");
> +		return PTR_ERR(hdmi->tx_rst);
> +	}
> +
> +	return 0;
> +}
> +
> +static int starfive_hdmi_bind(struct device *dev, struct device *master,
> +			      void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm = dev_get_drvdata(master);
> +	struct starfive_hdmi *hdmi;
> +	struct resource *iores;
> +	int irq;
> +	int ret;
> +
> +	hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
> +	if (!hdmi)
> +		return -ENOMEM;
> +
> +	hdmi = drmm_simple_encoder_alloc(drm, struct starfive_hdmi,
> +					 encoder, DRM_MODE_ENCODER_TMDS);
> +
> +	hdmi->dev = dev;
> +	hdmi->drm_dev = drm;
> +	dev_set_drvdata(dev, hdmi);
> +
> +	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	hdmi->regs = devm_ioremap_resource(dev, iores);

This should go to probe

> +	if (IS_ERR(hdmi->regs))
> +		return PTR_ERR(hdmi->regs);
> +
> +	ret = starfive_hdmi_get_clk_rst(dev, hdmi);
> +	if (ret < 0)
> +		return ret;

And this

> +
> +	ret = devm_pm_runtime_enable(dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = pm_runtime_resume_and_get(dev);
> +	if (ret)
> +		return ret;
> +
> +	irq = platform_get_irq(pdev, 0);

And this

> +	if (irq < 0) {
> +		ret = irq;
> +		goto err_put_runtime_pm;
> +	}
> +
> +	hdmi->ddc = starfive_hdmi_i2c_adapter(hdmi);
> +	if (IS_ERR(hdmi->ddc)) {
> +		ret = PTR_ERR(hdmi->ddc);
> +		hdmi->ddc = NULL;
> +		goto err_put_runtime_pm;
> +	}
> +
> +	starfive_hdmi_i2c_init(hdmi);
> +
> +	ret = starfive_hdmi_register(drm, hdmi);
> +	if (ret)
> +		goto err_put_adapter;
> +
> +	/* Unmute hotplug interrupt */
> +	hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
> +
> +	ret = devm_request_threaded_irq(dev, irq, starfive_hdmi_hardirq,
> +					starfive_hdmi_irq, IRQF_SHARED,
> +					dev_name(dev), hdmi);
> +	if (ret < 0)
> +		goto err_put_adapter;
> +
> +	pm_runtime_put_sync(dev);
> +
> +	return 0;
> +
> +err_put_adapter:
> +	i2c_put_adapter(hdmi->ddc);
> +err_put_runtime_pm:
> +	pm_runtime_put_sync(dev);
> +
> +	return ret;
> +}
> +
> +static const struct component_ops starfive_hdmi_ops = {
> +	.bind	= starfive_hdmi_bind,
> +};
> +
> +static int starfive_hdmi_probe(struct platform_device *pdev)
> +{
> +	return component_add(&pdev->dev, &starfive_hdmi_ops);
> +}
> +
> +static int starfive_hdmi_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &starfive_hdmi_ops);
> +
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops hdmi_pm_ops = {
> +	SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
> +	SET_LATE_SYSTEM_SLEEP_PM_OPS(hdmi_system_pm_suspend, hdmi_system_pm_resume)
> +};
> +
> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
> +	{ .compatible = "starfive,jh7110-inno-hdmi",},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, starfive_hdmi_dt_ids);
> +
> +struct platform_driver starfive_hdmi_driver = {
> +	.probe  = starfive_hdmi_probe,
> +	.remove = starfive_hdmi_remove,
> +	.driver = {
> +		.name = "starfive-hdmi",
> +		.of_match_table = starfive_hdmi_dt_ids,
> +		.pm = &hdmi_pm_ops,
> +	},
> +};
> +
> +MODULE_AUTHOR("StarFive Corporation");
> +MODULE_DESCRIPTION("Starfive HDMI Driver");
> diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.h b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
> new file mode 100644
> index 000000000..1b9a11bca
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
> @@ -0,0 +1,295 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
> + */
> +
> +#ifndef __STARFIVE_HDMI_H__
> +#define __STARFIVE_HDMI_H__
> +
> +#include <drm/bridge/dw_hdmi.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_simple_kms_helper.h>
> +#include <linux/bitfield.h>
> +#include <linux/bits.h>
> +
> +#define DDC_SEGMENT_ADDR		0x30
> +
> +#define HDMI_SCL_RATE			(100 * 1000)
> +#define DDC_BUS_FREQ_L			0x4b
> +#define DDC_BUS_FREQ_H			0x4c
> +
> +#define HDMI_SYS_CTRL			0x00
> +#define m_RST_ANALOG			BIT(6)
> +#define v_RST_ANALOG			0
> +#define v_NOT_RST_ANALOG		BIT(6)
> +#define m_RST_DIGITAL			BIT(5)
> +#define v_RST_DIGITAL			0
> +#define v_NOT_RST_DIGITAL		BIT(5)
> +#define m_REG_CLK_INV			BIT(4)
> +#define v_REG_CLK_NOT_INV		0
> +#define v_REG_CLK_INV			BIT(4)
> +#define m_VCLK_INV			BIT(3)
> +#define v_VCLK_NOT_INV			0
> +#define v_VCLK_INV			BIT(3)
> +#define m_REG_CLK_SOURCE		BIT(2)
> +#define v_REG_CLK_SOURCE_TMDS		0
> +#define v_REG_CLK_SOURCE_SYS		BIT(2)
> +#define m_POWER				BIT(1)
> +#define v_PWR_ON			0
> +#define v_PWR_OFF			BIT(1)
> +#define m_INT_POL			BIT(0)
> +#define v_INT_POL_HIGH			1
> +#define v_INT_POL_LOW			0
> +
> +#define HDMI_AV_MUTE			0x05
> +#define m_AVMUTE_CLEAR			BIT(7)
> +#define m_AVMUTE_ENABLE			BIT(6)
> +#define m_AUDIO_MUTE			BIT(1)
> +#define m_VIDEO_BLACK			BIT(0)
> +#define v_AVMUTE_CLEAR(n)		((n) << 7)
> +#define v_AVMUTE_ENABLE(n)		((n) << 6)
> +#define v_AUDIO_MUTE(n)			((n) << 1)
> +#define v_VIDEO_MUTE(n)			((n) << 0)
> +
> +#define HDMI_VIDEO_TIMING_CTL		0x08
> +#define v_VSYNC_POLARITY(n)		((n) << 3)
> +#define v_HSYNC_POLARITY(n)		((n) << 2)
> +#define v_INETLACE(n)			((n) << 1)
> +#define v_EXTERANL_VIDEO(n)		((n) << 0)
> +
> +#define HDMI_VIDEO_EXT_HTOTAL_L		0x09
> +#define HDMI_VIDEO_EXT_HTOTAL_H		0x0a
> +#define HDMI_VIDEO_EXT_HBLANK_L		0x0b
> +#define HDMI_VIDEO_EXT_HBLANK_H		0x0c
> +#define HDMI_VIDEO_EXT_HDELAY_L		0x0d
> +#define HDMI_VIDEO_EXT_HDELAY_H		0x0e
> +#define HDMI_VIDEO_EXT_HDURATION_L	0x0f
> +#define HDMI_VIDEO_EXT_HDURATION_H	0x10
> +#define HDMI_VIDEO_EXT_VTOTAL_L		0x11
> +#define HDMI_VIDEO_EXT_VTOTAL_H		0x12
> +#define HDMI_VIDEO_EXT_VBLANK		0x13
> +#define HDMI_VIDEO_EXT_VDELAY		0x14
> +#define HDMI_VIDEO_EXT_VDURATION	0x15
> +
> +#define HDMI_EDID_SEGMENT_POINTER	0x4d
> +#define HDMI_EDID_WORD_ADDR		0x4e
> +#define HDMI_EDID_FIFO_OFFSET		0x4f
> +#define HDMI_EDID_FIFO_ADDR		0x50
> +
> +#define HDMI_INTERRUPT_MASK1		0xc0
> +#define HDMI_INTERRUPT_STATUS1		0xc1
> +#define	m_INT_ACTIVE_VSYNC		BIT(5)
> +#define m_INT_EDID_READY		BIT(2)
> +
> +#define HDMI_STATUS			0xc8
> +#define m_HOTPLUG			BIT(7)
> +#define m_MASK_INT_HOTPLUG		BIT(5)
> +#define m_INT_HOTPLUG			BIT(1)
> +#define v_MASK_INT_HOTPLUG(n)		(((n) & 0x1) << 5)

It is untypical to have field defines which start with lowercase letter.

> +
> +#define HDMI_SYNC					0xce
> +
> +#define UPDATE(x, h, l)					FIELD_PREP(GENMASK(h, l), x)
> +
> +/* REG: 0x1a0 */
> +#define STARFIVE_PRE_PLL_CONTROL			0x1a0
> +#define STARFIVE_PCLK_VCO_DIV_5_MASK			BIT(1)
> +#define STARFIVE_PCLK_VCO_DIV_5(x)			UPDATE(x, 1, 1)
> +#define STARFIVE_PRE_PLL_POWER_DOWN			BIT(0)
> +
> +/* REG: 0x1a1 */
> +#define STARFIVE_PRE_PLL_DIV_1				0x1a1
> +#define STARFIVE_PRE_PLL_PRE_DIV_MASK			GENMASK(5, 0)
> +#define STARFIVE_PRE_PLL_PRE_DIV(x)			UPDATE(x, 5, 0)
> +
> +/* REG: 0x1a2 */
> +#define STARFIVE_PRE_PLL_DIV_2				0x1a2
> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DOWN		BIT(7)
> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE		BIT(6)
> +#define STARFIVE_PRE_PLL_FRAC_DIV_DISABLE		UPDATE(3, 5, 4)
> +#define STARFIVE_PRE_PLL_FB_DIV_11_8_MASK		GENMASK(3, 0)
> +#define STARFIVE_PRE_PLL_FB_DIV_11_8(x)			UPDATE((x) >> 8, 3, 0)
> +
> +/* REG: 0x1a3 */
> +#define STARFIVE_PRE_PLL_DIV_3				0x1a3
> +#define STARFIVE_PRE_PLL_FB_DIV_7_0(x)			UPDATE(x, 7, 0)
> +
> +/* REG: 0x1a4*/
> +#define STARFIVE_PRE_PLL_DIV_4				0x1a4
> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C_MASK		GENMASK(1, 0)
> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C(x)		UPDATE(x, 1, 0)
> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B_MASK		GENMASK(3, 2)
> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B(x)		UPDATE(x, 3, 2)
> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A_MASK		GENMASK(5, 4)
> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A(x)		UPDATE(x, 5, 4)
> +
> +/* REG: 0x1a5 */
> +#define STARFIVE_PRE_PLL_DIV_5				0x1a5
> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_SHIFT		5
> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_MASK		GENMASK(6, 5)
> +#define STARFIVE_PRE_PLL_PCLK_DIV_B(x)			UPDATE(x, 6, 5)
> +#define STARFIVE_PRE_PLL_PCLK_DIV_A_MASK		GENMASK(4, 0)
> +#define STARFIVE_PRE_PLL_PCLK_DIV_A(x)			UPDATE(x, 4, 0)
> +
> +/* REG: 0x1a6 */
> +#define STARFIVE_PRE_PLL_DIV_6				0x1a6
> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_SHIFT		5
> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_MASK		GENMASK(6, 5)
> +#define STARFIVE_PRE_PLL_PCLK_DIV_C(x)			UPDATE(x, 6, 5)
> +#define STARFIVE_PRE_PLL_PCLK_DIV_D_MASK		GENMASK(4, 0)
> +#define STARFIVE_PRE_PLL_PCLK_DIV_D(x)			UPDATE(x, 4, 0)
> +
> +/* REG: 0x1a9 */
> +#define STARFIVE_PRE_PLL_LOCK_STATUS			0x1a9
> +
> +/* REG: 0x1aa */
> +#define STARFIVE_POST_PLL_DIV_1				0x1aa
> +#define STARFIVE_POST_PLL_POST_DIV_ENABLE		GENMASK(3, 2)
> +#define STARFIVE_POST_PLL_REFCLK_SEL_TMDS		BIT(1)
> +#define STARFIVE_POST_PLL_POWER_DOWN			BIT(0)
> +#define STARFIVE_POST_PLL_FB_DIV_8(x)			UPDATE(((x) >> 8) << 4, 4, 4)
> +
> +/* REG:0x1ab */
> +#define STARFIVE_POST_PLL_DIV_2				0x1ab
> +#define STARFIVE_POST_PLL_Pre_DIV_MASK			GENMASK(5, 0)
> +#define STARFIVE_POST_PLL_PRE_DIV(x)			UPDATE(x, 5, 0)
> +
> +/* REG: 0x1ac */
> +#define STARFIVE_POST_PLL_DIV_3				0x1ac
> +#define STARFIVE_POST_PLL_FB_DIV_7_0(x)			UPDATE(x, 7, 0)
> +
> +/* REG: 0x1ad */
> +#define STARFIVE_POST_PLL_DIV_4				0x1ad
> +#define STARFIVE_POST_PLL_POST_DIV_MASK			GENMASK(2, 0)
> +#define STARFIVE_POST_PLL_POST_DIV_2			0x0
> +#define STARFIVE_POST_PLL_POST_DIV_4			0x1
> +#define STARFIVE_POST_PLL_POST_DIV_8			0x3
> +
> +/* REG: 0x1af */
> +#define STARFIVE_POST_PLL_LOCK_STATUS			0x1af
> +
> +/* REG: 0x1b0 */
> +#define STARFIVE_BIAS_CONTROL				0x1b0
> +#define STARFIVE_BIAS_ENABLE				BIT(2)
> +
> +/* REG: 0x1b2 */
> +#define STARFIVE_TMDS_CONTROL				0x1b2
> +#define STARFIVE_TMDS_CLK_DRIVER_EN			BIT(3)
> +#define STARFIVE_TMDS_D2_DRIVER_EN			BIT(2)
> +#define STARFIVE_TMDS_D1_DRIVER_EN			BIT(1)
> +#define STARFIVE_TMDS_D0_DRIVER_EN			BIT(0)
> +#define STARFIVE_TMDS_DRIVER_ENABLE			(STARFIVE_TMDS_CLK_DRIVER_EN | \
> +							 STARFIVE_TMDS_D2_DRIVER_EN | \
> +							 STARFIVE_TMDS_D1_DRIVER_EN | \
> +							 STARFIVE_TMDS_D0_DRIVER_EN)
> +
> +/* REG: 0x1b4 */
> +#define STARFIVE_LDO_CONTROL				0x1b4
> +#define STARFIVE_LDO_D2_EN				BIT(2)
> +#define STARFIVE_LDO_D1_EN				BIT(1)
> +#define STARFIVE_LDO_D0_EN				BIT(0)
> +#define STARFIVE_LDO_ENABLE				(STARFIVE_LDO_D2_EN | \
> +							 STARFIVE_LDO_D1_EN | \
> +							 STARFIVE_LDO_D0_EN)
> +
> +/* REG: 0x1be */
> +#define STARFIVE_SERIALIER_CONTROL			0x1be
> +#define STARFIVE_SERIALIER_D2_EN			BIT(6)
> +#define STARFIVE_SERIALIER_D1_EN			BIT(5)
> +#define STARFIVE_SERIALIER_D0_EN			BIT(4)
> +#define STARFIVE_SERIALIER_EN				BIT(0)
> +
> +#define STARFIVE_SERIALIER_ENABLE			(STARFIVE_SERIALIER_D2_EN | \
> +							 STARFIVE_SERIALIER_D1_EN | \
> +							 STARFIVE_SERIALIER_D0_EN | \
> +							 STARFIVE_SERIALIER_EN)
> +
> +/* REG: 0x1cc */
> +#define STARFIVE_RX_CONTROL				0x1cc
> +#define STARFIVE_RX_EN					BIT(3)
> +#define STARFIVE_RX_CHANNEL_2_EN			BIT(2)
> +#define STARFIVE_RX_CHANNEL_1_EN			BIT(1)
> +#define STARFIVE_RX_CHANNEL_0_EN			BIT(0)
> +#define STARFIVE_RX_ENABLE				(STARFIVE_RX_EN | \
> +							 STARFIVE_RX_CHANNEL_2_EN | \
> +							 STARFIVE_RX_CHANNEL_1_EN | \
> +							 STARFIVE_RX_CHANNEL_0_EN)
> +
> +/* REG: 0x1d1 */
> +#define STARFIVE_PRE_PLL_FRAC_DIV_H			0x1d1
> +#define STARFIVE_PRE_PLL_FRAC_DIV_23_16(x)		UPDATE((x) >> 16, 7, 0)
> +/* REG: 0x1d2 */
> +#define STARFIVE_PRE_PLL_FRAC_DIV_M			0x1d2
> +#define STARFIVE_PRE_PLL_FRAC_DIV_15_8(x)		UPDATE((x) >> 8, 7, 0)
> +/* REG: 0x1d3 */
> +#define STARFIVE_PRE_PLL_FRAC_DIV_L			0x1d3
> +#define STARFIVE_PRE_PLL_FRAC_DIV_7_0(x)		UPDATE(x, 7, 0)
> +
> +struct pre_pll_config {
> +	unsigned long pixclock;
> +	unsigned long tmdsclock;
> +	u8 prediv;
> +	u16 fbdiv;
> +	u8 tmds_div_a;
> +	u8 tmds_div_b;
> +	u8 tmds_div_c;
> +	u8 pclk_div_a;
> +	u8 pclk_div_b;
> +	u8 pclk_div_c;
> +	u8 pclk_div_d;
> +	u8 vco_div_5_en;
> +	u32 fracdiv;
> +};
> +
> +struct post_pll_config {
> +	unsigned long tmdsclock;
> +	u8 prediv;
> +	u16 fbdiv;
> +	u8 postdiv;
> +	u8 post_div_en;
> +	u8 version;
> +};
> +
> +struct phy_config {
> +	unsigned long	tmdsclock;
> +	u8		regs[14];
> +};
> +
> +struct hdmi_data_info {
> +	int vic;
> +	bool sink_is_hdmi;
> +	bool sink_has_audio;
> +	unsigned int enc_in_format;
> +	unsigned int enc_out_format;
> +	unsigned int colorimetry;
> +};
> +
> +struct starfive_hdmi {
> +	struct device *dev;
> +	struct drm_device *drm_dev;
> +	struct drm_encoder	encoder;
> +	struct drm_connector	connector;
> +
> +	struct starfive_hdmi_i2c *i2c;
> +
> +	int irq;
> +	struct clk *sys_clk;
> +	struct clk *mclk;
> +	struct clk *bclk;
> +	struct reset_control *tx_rst;
> +	void __iomem *regs;
> +
> +	struct i2c_adapter *ddc;
> +
> +	unsigned long tmds_rate;
> +
> +	struct hdmi_data_info	hdmi_data;
> +	struct drm_display_mode previous_mode;
> +	const struct pre_pll_config	*pre_cfg;
> +	const struct post_pll_config	*post_cfg;
> +};
> +
> +#endif /* __STARFIVE_HDMI_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
> index 3cd533cfa..9669354bd 100644
> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> @@ -126,6 +126,11 @@ static const struct component_master_ops vs_drm_ops = {
>   static struct platform_driver *drm_sub_drivers[] = {
>   	&dc_platform_driver,
>   
> +	/* connector + encoder*/
> +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
> +	&starfive_hdmi_driver,
> +#endif
> +
>   };
>   
>   static struct component_match *vs_drm_match_add(struct device *dev)
> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
> index 369ad22d6..e90723419 100644
> --- a/drivers/gpu/drm/verisilicon/vs_drv.h
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.h
> @@ -24,4 +24,8 @@ to_vs_drm_private(const struct drm_device *dev)
>   	return container_of(dev, struct vs_drm_device, base);
>   }
>   
> +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
> +extern struct platform_driver starfive_hdmi_driver;
> +#endif
> +
>   #endif /* __VS_DRV_H__ */

-- 
With best wishes
Dmitry


^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-10-25 22:23     ` Dmitry Baryshkov
  0 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-10-25 22:23 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig

On 25/10/2023 13:39, Keith Zhao wrote:
> add hdmi driver as encoder and connect
> 
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>   drivers/gpu/drm/verisilicon/Kconfig         |   8 +-
>   drivers/gpu/drm/verisilicon/Makefile        |   1 +
>   drivers/gpu/drm/verisilicon/starfive_hdmi.c | 949 ++++++++++++++++++++
>   drivers/gpu/drm/verisilicon/starfive_hdmi.h | 295 ++++++
>   drivers/gpu/drm/verisilicon/vs_drv.c        |   5 +
>   drivers/gpu/drm/verisilicon/vs_drv.h        |   4 +
>   6 files changed, 1261 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.c
>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.h
> 
> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
> index 3a361f8c8..122c786e3 100644
> --- a/drivers/gpu/drm/verisilicon/Kconfig
> +++ b/drivers/gpu/drm/verisilicon/Kconfig
> @@ -12,4 +12,10 @@ config DRM_VERISILICON
>   	  setting and buffer management. It does not
>   	  provide 2D or 3D acceleration.
>   
> -
> +config DRM_VERISILICON_STARFIVE_HDMI
> +	bool "Starfive HDMI extensions"
> +	depends on DRM_VERISILICON
> +	help
> +	   This selects support for StarFive soc specific extensions
> +	   for the Innosilicon HDMI driver. If you want to enable
> +	   HDMI on JH7110 based soc, you should select this option.
> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> index 1d48016ca..08350f25b 100644
> --- a/drivers/gpu/drm/verisilicon/Makefile
> +++ b/drivers/gpu/drm/verisilicon/Makefile
> @@ -7,5 +7,6 @@ vs_drm-objs := vs_dc_hw.o \
>   		vs_modeset.o \
>   		vs_plane.o
>   
> +vs_drm-$(CONFIG_DRM_VERISILICON_STARFIVE_HDMI) += starfive_hdmi.o
>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>   
> diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.c b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
> new file mode 100644
> index 000000000..d296c4b71
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
> @@ -0,0 +1,949 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/hdmi.h>
> +#include <linux/i2c.h>
> +#include <linux/irq.h>
> +#include <linux/media-bus-format.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/reset.h>
> +
> +#include <drm/bridge/dw_hdmi.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_managed.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_simple_kms_helper.h>
> +
> +#include "starfive_hdmi.h"
> +#include "vs_drv.h"
> +#include "vs_crtc.h"
> +
> +static struct starfive_hdmi *encoder_to_hdmi(struct drm_encoder *encoder)
> +{
> +	return container_of(encoder, struct starfive_hdmi, encoder);
> +}
> +
> +static struct starfive_hdmi *connector_to_hdmi(struct drm_connector *connector)
> +{
> +	return container_of(connector, struct starfive_hdmi, connector);
> +}
> +
> +struct starfive_hdmi_i2c {
> +	struct i2c_adapter adap;
> +
> +	u8 ddc_addr;
> +	u8 segment_addr;
> +	/* protects the edid data when use i2c cmd to read edid */
> +	struct mutex lock;
> +	struct completion cmp;
> +};
> +
> +static const struct pre_pll_config pre_pll_cfg_table[] = {
> +	{ 25175000,  25175000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0xf55555},
> +	{ 25200000,  25200000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0},
> +	{ 27000000,  27000000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0},

Such data structures are usually pretyt limited and hard to handle. 
Could you please replace it with the runtime calculations of

> +	{ 27027000,  27027000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0x170a3d},
> +	{ 28320000,  28320000, 1,  28, 2, 1, 1,  3, 0, 3, 4, 0, 0x51eb85},
> +	{ 30240000,  30240000, 1,  30, 2, 1, 1,  3, 0, 3, 4, 0, 0x3d70a3},
> +	{ 31500000,  31500000, 1,  31, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> +	{ 33750000,  33750000, 1,  33, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
> +	{ 36000000,  36000000, 1,  36, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{ 40000000,  40000000, 1,  80, 2, 2, 2, 12, 2, 2, 2, 0, 0},
> +	{ 46970000,  46970000, 1,  46, 2, 1, 1,  3, 0, 3, 4, 0, 0xf851eb},
> +	{ 49500000,  49500000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> +	{ 49000000,  49000000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{ 50000000,  50000000, 1,  50, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{ 54000000,  54000000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{ 54054000,  54054000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0x0dd2f1},
> +	{ 57284000,  57284000, 1,  57, 2, 1, 1,  3, 0, 3, 4, 0, 0x48b439},
> +	{ 58230000,  58230000, 1,  58, 2, 1, 1,  3, 0, 3, 4, 0, 0x3ae147},
> +	{ 59341000,  59341000, 1,  59, 2, 1, 1,  3, 0, 3, 4, 0, 0x574bc6},
> +	{ 59400000,  59400000, 1,  99, 3, 1, 1,  1, 3, 3, 4, 0, 0},
> +	{ 65000000,  65000000, 1, 130, 2, 2, 2,  12, 0, 2, 2, 0, 0},
> +	{ 68250000,  68250000, 1, 68,  2, 1, 1,  3,  0, 3, 4, 0, 0x3fffff},
> +	{ 71000000,  71000000, 1,  71, 2, 1, 1,  3, 0, 3,  4, 0, 0},
> +	{ 74176000,  74176000, 1,  98, 1, 2, 2,  1, 2, 3, 4, 0, 0xe6ae6b},
> +	{ 74250000,  74250000, 1,  99, 1, 2, 2,  1, 2, 3, 4, 0, 0},
> +	{ 75000000,  75000000, 1,  75, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{ 78750000,  78750000, 1,  78, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
> +	{ 79500000,  79500000, 1,  79, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> +	{ 83500000,  83500000, 2, 167, 2, 1, 1,  1, 0, 0,  6, 0, 0},
> +	{ 83500000, 104375000, 1, 104, 2, 1, 1,  1, 1, 0,  5, 0, 0x600000},
> +	{ 84858000,  84858000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xdba5e2},
> +	{ 85500000,  85500000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
> +	{ 85750000,  85750000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
> +	{ 85800000,  85800000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcccccc},
> +	{ 88750000,  88750000, 1,  88, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
> +	{ 89910000,  89910000, 1,  89, 2, 1, 1,  3, 0, 3, 4, 0, 0xe8f5c1},
> +	{ 90000000,  90000000, 1,  90, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{101000000, 101000000, 1, 101, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{102250000, 102250000, 1, 102, 2, 1, 1,  3, 0, 3, 4, 0, 0x3fffff},
> +	{106500000, 106500000, 1, 106, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> +	{108000000, 108000000, 1,  90, 3, 0, 0,  5, 0, 2,  2, 0, 0},
> +	{119000000, 119000000, 1, 119, 2, 1, 1,  3, 0, 3,  4, 0, 0},
> +	{131481000, 131481000, 1,  131, 2, 1, 1,  3, 0, 3,  4, 0, 0x7b22d1},
> +	{135000000, 135000000, 1,  135, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{136750000, 136750000, 1,  136, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
> +	{147180000, 147180000, 1,  147, 2, 1, 1,  3, 0, 3, 4, 0, 0x2e147a},
> +	{148352000, 148352000, 1,  98, 1, 1, 1,  1, 2, 2, 2, 0, 0xe6ae6b},
> +	{148500000, 148500000, 1,  99, 1, 1, 1,  1, 2, 2, 2, 0, 0},
> +	{154000000, 154000000, 1, 154, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{156000000, 156000000, 1, 156, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{157000000, 157000000, 1, 157, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{162000000, 162000000, 1, 162, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{174250000, 174250000, 1, 145, 3, 0, 0,  5, 0, 2, 2, 0, 0x355555},
> +	{174500000, 174500000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> +	{174570000, 174570000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x91eb84},
> +	{175500000, 175500000, 1, 175, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> +	{185590000, 185590000, 1, 185, 2, 1, 1,  3, 0, 3, 4, 0, 0x970a3c},
> +	{187000000, 187000000, 1, 187, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{241500000, 241500000, 1, 161, 1, 1, 1,  4, 0, 2,  2, 0, 0},
> +	{241700000, 241700000, 1, 241, 2, 1, 1,  3, 0, 3,  4, 0, 0xb33332},
> +	{262750000, 262750000, 1, 262, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
> +	{296500000, 296500000, 1, 296, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
> +	{296703000, 296703000, 1,  98, 0, 1, 1,  1, 0, 2,  2, 0, 0xe6ae6b},
> +	{297000000, 297000000, 1,  99, 0, 1, 1,  1, 0, 2,  2, 0, 0},
> +	{594000000, 594000000, 1,  99, 0, 2, 0,  1, 0, 1,  1, 0, 0},
> +	{0, 0, 0,  0, 0, 0, 0,  0, 0, 0,  0, 0, 0},
> +};
> +
> +static const struct post_pll_config post_pll_cfg_table[] = {
> +	{25200000,	1, 80, 13, 3, 1},
> +	{27000000,	1, 40, 11, 3, 1},
> +	{33750000,	1, 40, 11, 3, 1},
> +	{49000000,	1, 20, 1, 3, 3},
> +	{241700000, 1, 20, 1, 3, 3},
> +	{297000000, 4, 20, 0, 0, 3},
> +	{594000000, 4, 20, 0, 0, 0},
> +	{ /* sentinel */ }
> +};
> +
> +inline u8 hdmi_readb(struct starfive_hdmi *hdmi, u16 offset)
> +{
> +	return readl_relaxed(hdmi->regs + (offset) * 0x04);
> +}
> +
> +inline void hdmi_writeb(struct starfive_hdmi *hdmi, u16 offset, u32 val)
> +{
> +	writel_relaxed(val, hdmi->regs + (offset) * 0x04);
> +}
> +
> +inline void hdmi_modb(struct starfive_hdmi *hdmi, u16 offset,
> +			     u32 msk, u32 val)
> +{
> +	u8 temp = hdmi_readb(hdmi, offset) & ~msk;
> +
> +	temp |= val & msk;
> +	hdmi_writeb(hdmi, offset, temp);
> +}
> +
> +static int starfive_hdmi_enable_clk_deassert_rst(struct device *dev, struct starfive_hdmi *hdmi)
> +{
> +	int ret;
> +
> +	ret = clk_prepare_enable(hdmi->sys_clk);
> +	if (ret) {
> +		dev_err(dev, "Cannot enable HDMI sys clock: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(hdmi->mclk);
> +	if (ret) {
> +		dev_err(dev, "Cannot enable HDMI mclk clock: %d\n", ret);
> +		goto err_mclk;
> +	}
> +	ret = clk_prepare_enable(hdmi->bclk);

This code begs to use clk_bulk instead.

> +	if (ret) {
> +		dev_err(dev, "Cannot enable HDMI bclk clock: %d\n", ret);
> +		goto err_bclk;
> +	}
> +	ret = reset_control_deassert(hdmi->tx_rst);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to deassert tx_rst\n");
> +		goto err_rst;
> +	}
> +	return 0;
> +
> +err_rst:
> +	clk_disable_unprepare(hdmi->bclk);
> +err_bclk:
> +	clk_disable_unprepare(hdmi->mclk);
> +err_mclk:
> +	clk_disable_unprepare(hdmi->sys_clk);
> +	return ret;
> +}
> +
> +static void starfive_hdmi_disable_clk_assert_rst(struct device *dev, struct starfive_hdmi *hdmi)
> +{
> +	int ret;
> +
> +	ret = reset_control_assert(hdmi->tx_rst);
> +	if (ret < 0)
> +		dev_err(dev, "failed to assert tx_rst\n");
> +
> +	clk_disable_unprepare(hdmi->sys_clk);
> +	clk_disable_unprepare(hdmi->mclk);
> +	clk_disable_unprepare(hdmi->bclk);
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int hdmi_system_pm_suspend(struct device *dev)
> +{
> +	return pm_runtime_force_suspend(dev);
> +}
> +
> +static int hdmi_system_pm_resume(struct device *dev)
> +{
> +	return pm_runtime_force_resume(dev);
> +}
> +#endif
> +
> +#ifdef CONFIG_PM
> +static int hdmi_runtime_suspend(struct device *dev)
> +{
> +	struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
> +
> +	starfive_hdmi_disable_clk_assert_rst(dev, hdmi);
> +
> +	return 0;
> +}
> +
> +static int hdmi_runtime_resume(struct device *dev)
> +{
> +	struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
> +
> +	return starfive_hdmi_enable_clk_deassert_rst(dev, hdmi);
> +}
> +#endif
> +
> +static void starfive_hdmi_tx_phy_power_down(struct starfive_hdmi *hdmi)
> +{
> +	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
> +}
> +
> +static void starfive_hdmi_tx_phy_power_on(struct starfive_hdmi *hdmi)
> +{
> +	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);

It looks like one can inline these two helpers w/o too much troubles.

> +}
> +
> +static void starfive_hdmi_config_pll(struct starfive_hdmi *hdmi)
> +{
> +	u32 val;
> +	u8 reg_1ad_value = hdmi->post_cfg->post_div_en ?
> +		 hdmi->post_cfg->postdiv : 0x00;
> +	u8 reg_1aa_value = hdmi->post_cfg->post_div_en ?
> +		 0x0e : 0x02;
> +
> +	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN);
> +	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1,
> +		    STARFIVE_POST_PLL_POST_DIV_ENABLE |
> +		    STARFIVE_POST_PLL_REFCLK_SEL_TMDS |
> +		    STARFIVE_POST_PLL_POWER_DOWN);
> +	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_1, STARFIVE_PRE_PLL_PRE_DIV(hdmi->pre_cfg->prediv));
> +
> +	val = STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE | STARFIVE_SPREAD_SPECTRUM_MOD_DOWN;
> +	if (!hdmi->pre_cfg->fracdiv)
> +		val |= STARFIVE_PRE_PLL_FRAC_DIV_DISABLE;
> +	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_2,
> +		    STARFIVE_PRE_PLL_FB_DIV_11_8(hdmi->pre_cfg->fbdiv) | val);
> +	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_3,
> +		    STARFIVE_PRE_PLL_FB_DIV_7_0(hdmi->pre_cfg->fbdiv));
> +	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_4,
> +		    STARFIVE_PRE_PLL_TMDSCLK_DIV_C(hdmi->pre_cfg->tmds_div_c) |
> +		    STARFIVE_PRE_PLL_TMDSCLK_DIV_A(hdmi->pre_cfg->tmds_div_a) |
> +		    STARFIVE_PRE_PLL_TMDSCLK_DIV_B(hdmi->pre_cfg->tmds_div_b));
> +
> +	if (hdmi->pre_cfg->fracdiv) {
> +		hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_L,
> +			    STARFIVE_PRE_PLL_FRAC_DIV_7_0(hdmi->pre_cfg->fracdiv));
> +		hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_M,
> +			    STARFIVE_PRE_PLL_FRAC_DIV_15_8(hdmi->pre_cfg->fracdiv));
> +		hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_H,
> +			    STARFIVE_PRE_PLL_FRAC_DIV_23_16(hdmi->pre_cfg->fracdiv));
> +	}
> +
> +	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_5,
> +		    STARFIVE_PRE_PLL_PCLK_DIV_A(hdmi->pre_cfg->pclk_div_a) |
> +		    STARFIVE_PRE_PLL_PCLK_DIV_B(hdmi->pre_cfg->pclk_div_b));
> +	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_6,
> +		    STARFIVE_PRE_PLL_PCLK_DIV_C(hdmi->pre_cfg->pclk_div_c) |
> +		    STARFIVE_PRE_PLL_PCLK_DIV_D(hdmi->pre_cfg->pclk_div_d));
> +
> +	/*pre-pll power down*/
> +	hdmi_modb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN, 0);
> +
> +	hdmi_modb(hdmi, STARFIVE_POST_PLL_DIV_2, STARFIVE_POST_PLL_Pre_DIV_MASK,
> +		  STARFIVE_POST_PLL_PRE_DIV(hdmi->post_cfg->prediv));
> +	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_3, hdmi->post_cfg->fbdiv & 0xff);
> +	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_4, reg_1ad_value);
> +	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1, reg_1aa_value);
> +}
> +
> +static void starfive_hdmi_tmds_driver_on(struct starfive_hdmi *hdmi)
> +{
> +	hdmi_modb(hdmi, STARFIVE_TMDS_CONTROL,
> +		  STARFIVE_TMDS_DRIVER_ENABLE, STARFIVE_TMDS_DRIVER_ENABLE);
> +}
> +
> +static void starfive_hdmi_sync_tmds(struct starfive_hdmi *hdmi)
> +{
> +	/*first send 0 to this bit, then send 1 and keep 1 into this bit*/
> +	hdmi_writeb(hdmi, HDMI_SYNC, 0x0);
> +	hdmi_writeb(hdmi, HDMI_SYNC, 0x1);
> +}
> +
> +static void starfive_hdmi_i2c_init(struct starfive_hdmi *hdmi)
> +{
> +	int ddc_bus_freq;
> +
> +	ddc_bus_freq = (clk_get_rate(hdmi->sys_clk) >> 2) / HDMI_SCL_RATE;
> +
> +	hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
> +	hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
> +
> +	/* Clear the EDID interrupt flag and mute the interrupt */
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
> +}
> +
> +static const
> +struct pre_pll_config *starfive_hdmi_phy_get_pre_pll_cfg(struct starfive_hdmi *hdmi,
> +							 unsigned long rate)
> +{
> +	const struct pre_pll_config *cfg = pre_pll_cfg_table;
> +
> +	rate = (rate / 1000) * 1000;
> +	for (; cfg->pixclock != 0; cfg++)
> +		if (cfg->tmdsclock == rate && cfg->pixclock == rate)
> +			break;
> +
> +	if (cfg->pixclock == 0)
> +		return ERR_PTR(-EINVAL);
> +
> +	return cfg;
> +}
> +
> +static int starfive_hdmi_phy_clk_set_rate(struct starfive_hdmi *hdmi)
> +{
> +	hdmi->post_cfg = post_pll_cfg_table;
> +
> +	hdmi->pre_cfg = starfive_hdmi_phy_get_pre_pll_cfg(hdmi, hdmi->tmds_rate);
> +	if (IS_ERR(hdmi->pre_cfg))
> +		return PTR_ERR(hdmi->pre_cfg);
> +
> +	for (; hdmi->post_cfg->tmdsclock != 0; hdmi->post_cfg++)
> +		if (hdmi->tmds_rate <= hdmi->post_cfg->tmdsclock)
> +			break;
> +
> +	starfive_hdmi_config_pll(hdmi);
> +
> +	return 0;
> +}
> +
> +static int starfive_hdmi_config_video_timing(struct starfive_hdmi *hdmi,
> +					     struct drm_display_mode *mode)
> +{
> +	int value;
> +	/* Set detail external video timing */
> +	value = mode->htotal;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);

As you have hdmi_writeb already, adding hdmi_writew will help here.

> +
> +	value = mode->htotal - mode->hdisplay;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
> +
> +	value = mode->htotal - mode->hsync_start;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
> +
> +	value = mode->hsync_end - mode->hsync_start;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
> +
> +	value = mode->vtotal;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
> +
> +	value = mode->vtotal - mode->vdisplay;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
> +
> +	value = mode->vtotal - mode->vsync_start;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
> +
> +	value = mode->vsync_end - mode->vsync_start;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
> +
> +	/* Set detail external video timing polarity and interlace mode */
> +	value = v_EXTERANL_VIDEO(1);
> +	value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
> +		v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
> +	value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
> +		v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
> +	value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
> +		v_INETLACE(1) : v_INETLACE(0);
> +
> +	hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
> +	return 0;
> +}
> +
> +static int starfive_hdmi_setup(struct starfive_hdmi *hdmi,
> +			       struct drm_display_mode *mode)
> +{
> +	hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
> +	hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
> +	hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
> +
> +	hdmi->tmds_rate = mode->clock * 1000;
> +	starfive_hdmi_phy_clk_set_rate(hdmi);
> +
> +	while (!(hdmi_readb(hdmi, STARFIVE_PRE_PLL_LOCK_STATUS) & 0x1))
> +		continue;
> +	while (!(hdmi_readb(hdmi, STARFIVE_POST_PLL_LOCK_STATUS) & 0x1))
> +		continue;

Please use read_iopoll_timeout here.

> +
> +	/*turn on LDO*/
> +	hdmi_writeb(hdmi, STARFIVE_LDO_CONTROL, STARFIVE_LDO_ENABLE);
> +	/*turn on serializer*/
> +	hdmi_writeb(hdmi, STARFIVE_SERIALIER_CONTROL, STARFIVE_SERIALIER_ENABLE);
> +
> +	starfive_hdmi_tx_phy_power_down(hdmi);
> +	starfive_hdmi_config_video_timing(hdmi, mode);
> +	starfive_hdmi_tx_phy_power_on(hdmi);
> +
> +	starfive_hdmi_tmds_driver_on(hdmi);
> +	starfive_hdmi_sync_tmds(hdmi);
> +
> +	return 0;
> +}
> +
> +static void starfive_hdmi_encoder_mode_set(struct drm_encoder *encoder,
> +					   struct drm_display_mode *mode,
> +					   struct drm_display_mode *adj_mode)
> +{
> +	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> +
> +	drm_mode_copy(&hdmi->previous_mode, adj_mode);

Why do you need it?

> +}
> +
> +static void starfive_hdmi_encoder_enable(struct drm_encoder *encoder)
> +{
> +	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> +	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
> +	int ret, idx;
> +	struct drm_device *drm = hdmi->connector.dev;
> +
> +	if (drm && !drm_dev_enter(drm, &idx))
> +		return;
> +
> +	ret = pm_runtime_get_sync(hdmi->dev);
> +	if (ret < 0)
> +		return;
> +	mdelay(10);
> +	starfive_hdmi_setup(hdmi, mode);
> +
> +	if (drm)
> +		drm_dev_exit(idx);
> +}
> +
> +static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
> +{
> +	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> +
> +	int idx;
> +	struct drm_device *drm = hdmi->connector.dev;
> +
> +	if (drm && !drm_dev_enter(drm, &idx))
> +		return;
> +
> +	pm_runtime_put(hdmi->dev);
> +
> +	if (drm)
> +		drm_dev_exit(idx);
> +}
> +
> +static int
> +starfive_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
> +				   struct drm_crtc_state *crtc_state,
> +				   struct drm_connector_state *conn_state)
> +{
> +	bool valid = false;
> +	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
> +
> +	vs_crtc_state->encoder_type = encoder->encoder_type;
> +	vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
> +
> +	const struct pre_pll_config *cfg = pre_pll_cfg_table;
> +	int pclk = mode->clock * 1000;
> +
> +	for (; cfg->pixclock != 0; cfg++) {
> +		if (pclk == cfg->pixclock) {
> +			if (pclk > 297000000)

Magic value.

> +				continue;
> +
> +			valid = true;
> +			break;
> +		}
> +	}
> +
> +	return (valid) ? 0 : -EINVAL;
> +}
> +
> +static const struct drm_encoder_helper_funcs starfive_hdmi_encoder_helper_funcs = {
> +	.enable     = starfive_hdmi_encoder_enable,
> +	.disable    = starfive_hdmi_encoder_disable,
> +	.mode_set   = starfive_hdmi_encoder_mode_set,
> +	.atomic_check = starfive_hdmi_encoder_atomic_check,
> +};
> +
> +static enum drm_connector_status
> +starfive_hdmi_connector_detect(struct drm_connector *connector, bool force)
> +{
> +	struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
> +	struct drm_device *drm = hdmi->connector.dev;
> +	int ret;
> +	int idx;
> +
> +	if (drm && !drm_dev_enter(drm, &idx))
> +		return connector_status_disconnected;
> +
> +	ret = pm_runtime_get_sync(hdmi->dev);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
> +		connector_status_connected : connector_status_disconnected;
> +	pm_runtime_put(hdmi->dev);
> +
> +	if (drm)
> +		drm_dev_exit(idx);
> +
> +	return ret;
> +}
> +
> +static int starfive_hdmi_connector_get_modes(struct drm_connector *connector)
> +{
> +	struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
> +	struct edid *edid;
> +	int ret = 0;
> +
> +	if (!hdmi->ddc)
> +		return 0;
> +	ret = pm_runtime_get_sync(hdmi->dev);
> +	if (ret < 0)
> +		return ret;
> +
> +	edid = drm_get_edid(connector, hdmi->ddc);
> +	if (edid) {
> +		hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
> +		hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
> +		drm_connector_update_edid_property(connector, edid);
> +		ret = drm_add_edid_modes(connector, edid);
> +		kfree(edid);
> +	}
> +	pm_runtime_put(hdmi->dev);

Can you use drm_connector_helper_get_modes_from_ddc() here? And then 
drm_display_info.is_hdmi and .has_audio.

> +
> +	return ret;
> +}
> +
> +static enum drm_mode_status
> +starfive_hdmi_connector_mode_valid(struct drm_connector *connector,
> +				   struct drm_display_mode *mode)
> +{
> +	const struct pre_pll_config *cfg = pre_pll_cfg_table;
> +	int pclk = mode->clock * 1000;
> +	bool valid = false;
> +
> +	for (; cfg->pixclock != 0; cfg++) {
> +		if (pclk == cfg->pixclock) {
> +			if (pclk > 297000000)

magic value

> +				continue;
> +
> +			valid = true;
> +			break;
> +		}
> +	}
> +
> +	return (valid) ? MODE_OK : MODE_BAD;
> +}
> +
> +static int
> +starfive_hdmi_probe_single_connector_modes(struct drm_connector *connector,
> +					   u32 maxX, u32 maxY)
> +{
> +	return drm_helper_probe_single_connector_modes(connector, 3840, 2160);
> +}
> +
> +static const struct drm_connector_funcs starfive_hdmi_connector_funcs = {
> +	.fill_modes = starfive_hdmi_probe_single_connector_modes,
> +	.detect = starfive_hdmi_connector_detect,
> +	.reset = drm_atomic_helper_connector_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static struct drm_connector_helper_funcs starfive_hdmi_connector_helper_funcs = {
> +	.get_modes = starfive_hdmi_connector_get_modes,
> +	.mode_valid = starfive_hdmi_connector_mode_valid,
> +};
> +
> +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
> +{
> +	struct drm_encoder *encoder = &hdmi->encoder;
> +	struct device *dev = hdmi->dev;
> +
> +	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
> +
> +	/*
> +	 * If we failed to find the CRTC(s) which this encoder is
> +	 * supposed to be connected to, it's because the CRTC has
> +	 * not been registered yet.  Defer probing, and hope that
> +	 * the required CRTC is added later.
> +	 */
> +	if (encoder->possible_crtcs == 0)
> +		return -EPROBE_DEFER;
> +
> +	drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
> +
> +	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> +
> +	drm_connector_helper_add(&hdmi->connector,
> +				 &starfive_hdmi_connector_helper_funcs);
> +	drmm_connector_init(drm, &hdmi->connector,
> +			    &starfive_hdmi_connector_funcs,
> +			    DRM_MODE_CONNECTOR_HDMIA,

On an embedded device one can not be so sure. There can be MHL or HDMI 
Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.

> +			    hdmi->ddc);
> +
> +	drm_connector_attach_encoder(&hdmi->connector, encoder);
> +
> +	return 0;
> +}
> +
> +static irqreturn_t starfive_hdmi_i2c_irq(struct starfive_hdmi *hdmi)
> +{
> +	struct starfive_hdmi_i2c *i2c = hdmi->i2c;
> +	u8 stat;
> +
> +	stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
> +	if (!(stat & m_INT_EDID_READY))
> +		return IRQ_NONE;
> +
> +	/* Clear HDMI EDID interrupt flag */
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
> +
> +	complete(&i2c->cmp);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t starfive_hdmi_hardirq(int irq, void *dev_id)
> +{
> +	struct starfive_hdmi *hdmi = dev_id;
> +	irqreturn_t ret = IRQ_NONE;
> +	u8 interrupt;
> +
> +	if (hdmi->i2c)
> +		ret = starfive_hdmi_i2c_irq(hdmi);
> +
> +	interrupt = hdmi_readb(hdmi, HDMI_STATUS);
> +	if (interrupt & m_INT_HOTPLUG) {
> +		hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
> +		ret = IRQ_WAKE_THREAD;
> +	}
> +
> +	return ret;
> +}
> +
> +static irqreturn_t starfive_hdmi_irq(int irq, void *dev_id)
> +{
> +	struct starfive_hdmi *hdmi = dev_id;
> +
> +	drm_connector_helper_hpd_irq_event(&hdmi->connector);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int starfive_hdmi_i2c_read(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
> +{
> +	int length = msgs->len;
> +	u8 *buf = msgs->buf;
> +	int ret;
> +
> +	ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10);

read_poll_timeout

> +	if (!ret)
> +		return -EAGAIN;
> +
> +	while (length--)
> +		*buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
> +
> +	return 0;
> +}
> +
> +static int starfive_hdmi_i2c_write(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
> +{
> +	/*
> +	 * The DDC module only support read EDID message, so
> +	 * we assume that each word write to this i2c adapter
> +	 * should be the offset of EDID word address.
> +	 */
> +	if (msgs->len != 1 ||
> +	    (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR))
> +		return -EINVAL;
> +
> +	reinit_completion(&hdmi->i2c->cmp);
> +
> +	if (msgs->addr == DDC_SEGMENT_ADDR)
> +		hdmi->i2c->segment_addr = msgs->buf[0];
> +	if (msgs->addr == DDC_ADDR)
> +		hdmi->i2c->ddc_addr = msgs->buf[0];
> +
> +	/* Set edid fifo first addr */
> +	hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
> +
> +	/* Set edid word address 0x00/0x80 */
> +	hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
> +
> +	/* Set edid segment pointer */
> +	hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
> +
> +	return 0;
> +}
> +
> +static int starfive_hdmi_i2c_xfer(struct i2c_adapter *adap,
> +				  struct i2c_msg *msgs, int num)
> +{
> +	struct starfive_hdmi *hdmi = i2c_get_adapdata(adap);
> +	struct starfive_hdmi_i2c *i2c = hdmi->i2c;
> +	int i, ret = 0;
> +
> +	mutex_lock(&i2c->lock);
> +
> +	/* Clear the EDID interrupt flag and unmute the interrupt */
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
> +
> +	for (i = 0; i < num; i++) {
> +		DRM_DEV_DEBUG(hdmi->dev,
> +			      "xfer: num: %d/%d, len: %d, flags: %#x\n",
> +			      i + 1, num, msgs[i].len, msgs[i].flags);
> +
> +		if (msgs[i].flags & I2C_M_RD)
> +			ret = starfive_hdmi_i2c_read(hdmi, &msgs[i]);
> +		else
> +			ret = starfive_hdmi_i2c_write(hdmi, &msgs[i]);
> +
> +		if (ret < 0)
> +			break;
> +	}
> +
> +	if (!ret)
> +		ret = num;
> +
> +	/* Mute HDMI EDID interrupt */
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
> +
> +	mutex_unlock(&i2c->lock);
> +
> +	return ret;
> +}
> +
> +static u32 starfive_hdmi_i2c_func(struct i2c_adapter *adapter)
> +{
> +	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static const struct i2c_algorithm starfive_hdmi_algorithm = {
> +	.master_xfer	= starfive_hdmi_i2c_xfer,
> +	.functionality	= starfive_hdmi_i2c_func,
> +};
> +
> +static struct i2c_adapter *starfive_hdmi_i2c_adapter(struct starfive_hdmi *hdmi)
> +{
> +	struct i2c_adapter *adap;
> +	struct starfive_hdmi_i2c *i2c;
> +	int ret;
> +
> +	i2c = drmm_kzalloc(hdmi->drm_dev, sizeof(*i2c), GFP_KERNEL);
> +	if (!i2c)
> +		return ERR_PTR(-ENOMEM);
> +
> +	mutex_init(&i2c->lock);
> +	init_completion(&i2c->cmp);
> +
> +	adap = &i2c->adap;
> +	adap->class = I2C_CLASS_DDC;
> +	adap->owner = THIS_MODULE;
> +	adap->dev.parent = hdmi->dev;
> +	adap->algo = &starfive_hdmi_algorithm;
> +	strscpy(adap->name, "Starfive HDMI", sizeof(adap->name));
> +	i2c_set_adapdata(adap, hdmi);
> +
> +	ret = devm_i2c_add_adapter(hdmi->dev, adap);
> +	if (ret) {
> +		drm_warn(hdmi->drm_dev, "cannot add %s I2C adapter\n", adap->name);
> +		return ERR_PTR(ret);
> +	}
> +
> +	hdmi->i2c = i2c;
> +
> +	drm_info(hdmi->drm_dev, "registered %s I2C bus driver success\n", adap->name);
> +
> +	return adap;
> +}
> +
> +static int starfive_hdmi_get_clk_rst(struct device *dev, struct starfive_hdmi *hdmi)
> +{
> +	hdmi->sys_clk = devm_clk_get(dev, "sysclk");
> +	if (IS_ERR(hdmi->sys_clk)) {
> +		dev_err(dev, "Unable to get HDMI sysclk clk\n");
> +		return PTR_ERR(hdmi->sys_clk);
> +	}
> +	hdmi->mclk = devm_clk_get(dev, "mclk");
> +	if (IS_ERR(hdmi->mclk)) {
> +		dev_err(dev, "Unable to get HDMI mclk clk\n");
> +		return PTR_ERR(hdmi->mclk);
> +	}
> +	hdmi->bclk = devm_clk_get(dev, "bclk");
> +	if (IS_ERR(hdmi->bclk)) {
> +		dev_err(dev, "Unable to get HDMI bclk clk\n");
> +		return PTR_ERR(hdmi->bclk);
> +	}
> +	hdmi->tx_rst = devm_reset_control_get_by_index(dev, 0);
> +	if (IS_ERR(hdmi->tx_rst)) {
> +		dev_err(dev, "failed to get tx_rst reset\n");
> +		return PTR_ERR(hdmi->tx_rst);
> +	}
> +
> +	return 0;
> +}
> +
> +static int starfive_hdmi_bind(struct device *dev, struct device *master,
> +			      void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm = dev_get_drvdata(master);
> +	struct starfive_hdmi *hdmi;
> +	struct resource *iores;
> +	int irq;
> +	int ret;
> +
> +	hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
> +	if (!hdmi)
> +		return -ENOMEM;
> +
> +	hdmi = drmm_simple_encoder_alloc(drm, struct starfive_hdmi,
> +					 encoder, DRM_MODE_ENCODER_TMDS);
> +
> +	hdmi->dev = dev;
> +	hdmi->drm_dev = drm;
> +	dev_set_drvdata(dev, hdmi);
> +
> +	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	hdmi->regs = devm_ioremap_resource(dev, iores);

This should go to probe

> +	if (IS_ERR(hdmi->regs))
> +		return PTR_ERR(hdmi->regs);
> +
> +	ret = starfive_hdmi_get_clk_rst(dev, hdmi);
> +	if (ret < 0)
> +		return ret;

And this

> +
> +	ret = devm_pm_runtime_enable(dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = pm_runtime_resume_and_get(dev);
> +	if (ret)
> +		return ret;
> +
> +	irq = platform_get_irq(pdev, 0);

And this

> +	if (irq < 0) {
> +		ret = irq;
> +		goto err_put_runtime_pm;
> +	}
> +
> +	hdmi->ddc = starfive_hdmi_i2c_adapter(hdmi);
> +	if (IS_ERR(hdmi->ddc)) {
> +		ret = PTR_ERR(hdmi->ddc);
> +		hdmi->ddc = NULL;
> +		goto err_put_runtime_pm;
> +	}
> +
> +	starfive_hdmi_i2c_init(hdmi);
> +
> +	ret = starfive_hdmi_register(drm, hdmi);
> +	if (ret)
> +		goto err_put_adapter;
> +
> +	/* Unmute hotplug interrupt */
> +	hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
> +
> +	ret = devm_request_threaded_irq(dev, irq, starfive_hdmi_hardirq,
> +					starfive_hdmi_irq, IRQF_SHARED,
> +					dev_name(dev), hdmi);
> +	if (ret < 0)
> +		goto err_put_adapter;
> +
> +	pm_runtime_put_sync(dev);
> +
> +	return 0;
> +
> +err_put_adapter:
> +	i2c_put_adapter(hdmi->ddc);
> +err_put_runtime_pm:
> +	pm_runtime_put_sync(dev);
> +
> +	return ret;
> +}
> +
> +static const struct component_ops starfive_hdmi_ops = {
> +	.bind	= starfive_hdmi_bind,
> +};
> +
> +static int starfive_hdmi_probe(struct platform_device *pdev)
> +{
> +	return component_add(&pdev->dev, &starfive_hdmi_ops);
> +}
> +
> +static int starfive_hdmi_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &starfive_hdmi_ops);
> +
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops hdmi_pm_ops = {
> +	SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
> +	SET_LATE_SYSTEM_SLEEP_PM_OPS(hdmi_system_pm_suspend, hdmi_system_pm_resume)
> +};
> +
> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
> +	{ .compatible = "starfive,jh7110-inno-hdmi",},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, starfive_hdmi_dt_ids);
> +
> +struct platform_driver starfive_hdmi_driver = {
> +	.probe  = starfive_hdmi_probe,
> +	.remove = starfive_hdmi_remove,
> +	.driver = {
> +		.name = "starfive-hdmi",
> +		.of_match_table = starfive_hdmi_dt_ids,
> +		.pm = &hdmi_pm_ops,
> +	},
> +};
> +
> +MODULE_AUTHOR("StarFive Corporation");
> +MODULE_DESCRIPTION("Starfive HDMI Driver");
> diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.h b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
> new file mode 100644
> index 000000000..1b9a11bca
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
> @@ -0,0 +1,295 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
> + */
> +
> +#ifndef __STARFIVE_HDMI_H__
> +#define __STARFIVE_HDMI_H__
> +
> +#include <drm/bridge/dw_hdmi.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_simple_kms_helper.h>
> +#include <linux/bitfield.h>
> +#include <linux/bits.h>
> +
> +#define DDC_SEGMENT_ADDR		0x30
> +
> +#define HDMI_SCL_RATE			(100 * 1000)
> +#define DDC_BUS_FREQ_L			0x4b
> +#define DDC_BUS_FREQ_H			0x4c
> +
> +#define HDMI_SYS_CTRL			0x00
> +#define m_RST_ANALOG			BIT(6)
> +#define v_RST_ANALOG			0
> +#define v_NOT_RST_ANALOG		BIT(6)
> +#define m_RST_DIGITAL			BIT(5)
> +#define v_RST_DIGITAL			0
> +#define v_NOT_RST_DIGITAL		BIT(5)
> +#define m_REG_CLK_INV			BIT(4)
> +#define v_REG_CLK_NOT_INV		0
> +#define v_REG_CLK_INV			BIT(4)
> +#define m_VCLK_INV			BIT(3)
> +#define v_VCLK_NOT_INV			0
> +#define v_VCLK_INV			BIT(3)
> +#define m_REG_CLK_SOURCE		BIT(2)
> +#define v_REG_CLK_SOURCE_TMDS		0
> +#define v_REG_CLK_SOURCE_SYS		BIT(2)
> +#define m_POWER				BIT(1)
> +#define v_PWR_ON			0
> +#define v_PWR_OFF			BIT(1)
> +#define m_INT_POL			BIT(0)
> +#define v_INT_POL_HIGH			1
> +#define v_INT_POL_LOW			0
> +
> +#define HDMI_AV_MUTE			0x05
> +#define m_AVMUTE_CLEAR			BIT(7)
> +#define m_AVMUTE_ENABLE			BIT(6)
> +#define m_AUDIO_MUTE			BIT(1)
> +#define m_VIDEO_BLACK			BIT(0)
> +#define v_AVMUTE_CLEAR(n)		((n) << 7)
> +#define v_AVMUTE_ENABLE(n)		((n) << 6)
> +#define v_AUDIO_MUTE(n)			((n) << 1)
> +#define v_VIDEO_MUTE(n)			((n) << 0)
> +
> +#define HDMI_VIDEO_TIMING_CTL		0x08
> +#define v_VSYNC_POLARITY(n)		((n) << 3)
> +#define v_HSYNC_POLARITY(n)		((n) << 2)
> +#define v_INETLACE(n)			((n) << 1)
> +#define v_EXTERANL_VIDEO(n)		((n) << 0)
> +
> +#define HDMI_VIDEO_EXT_HTOTAL_L		0x09
> +#define HDMI_VIDEO_EXT_HTOTAL_H		0x0a
> +#define HDMI_VIDEO_EXT_HBLANK_L		0x0b
> +#define HDMI_VIDEO_EXT_HBLANK_H		0x0c
> +#define HDMI_VIDEO_EXT_HDELAY_L		0x0d
> +#define HDMI_VIDEO_EXT_HDELAY_H		0x0e
> +#define HDMI_VIDEO_EXT_HDURATION_L	0x0f
> +#define HDMI_VIDEO_EXT_HDURATION_H	0x10
> +#define HDMI_VIDEO_EXT_VTOTAL_L		0x11
> +#define HDMI_VIDEO_EXT_VTOTAL_H		0x12
> +#define HDMI_VIDEO_EXT_VBLANK		0x13
> +#define HDMI_VIDEO_EXT_VDELAY		0x14
> +#define HDMI_VIDEO_EXT_VDURATION	0x15
> +
> +#define HDMI_EDID_SEGMENT_POINTER	0x4d
> +#define HDMI_EDID_WORD_ADDR		0x4e
> +#define HDMI_EDID_FIFO_OFFSET		0x4f
> +#define HDMI_EDID_FIFO_ADDR		0x50
> +
> +#define HDMI_INTERRUPT_MASK1		0xc0
> +#define HDMI_INTERRUPT_STATUS1		0xc1
> +#define	m_INT_ACTIVE_VSYNC		BIT(5)
> +#define m_INT_EDID_READY		BIT(2)
> +
> +#define HDMI_STATUS			0xc8
> +#define m_HOTPLUG			BIT(7)
> +#define m_MASK_INT_HOTPLUG		BIT(5)
> +#define m_INT_HOTPLUG			BIT(1)
> +#define v_MASK_INT_HOTPLUG(n)		(((n) & 0x1) << 5)

It is untypical to have field defines which start with lowercase letter.

> +
> +#define HDMI_SYNC					0xce
> +
> +#define UPDATE(x, h, l)					FIELD_PREP(GENMASK(h, l), x)
> +
> +/* REG: 0x1a0 */
> +#define STARFIVE_PRE_PLL_CONTROL			0x1a0
> +#define STARFIVE_PCLK_VCO_DIV_5_MASK			BIT(1)
> +#define STARFIVE_PCLK_VCO_DIV_5(x)			UPDATE(x, 1, 1)
> +#define STARFIVE_PRE_PLL_POWER_DOWN			BIT(0)
> +
> +/* REG: 0x1a1 */
> +#define STARFIVE_PRE_PLL_DIV_1				0x1a1
> +#define STARFIVE_PRE_PLL_PRE_DIV_MASK			GENMASK(5, 0)
> +#define STARFIVE_PRE_PLL_PRE_DIV(x)			UPDATE(x, 5, 0)
> +
> +/* REG: 0x1a2 */
> +#define STARFIVE_PRE_PLL_DIV_2				0x1a2
> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DOWN		BIT(7)
> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE		BIT(6)
> +#define STARFIVE_PRE_PLL_FRAC_DIV_DISABLE		UPDATE(3, 5, 4)
> +#define STARFIVE_PRE_PLL_FB_DIV_11_8_MASK		GENMASK(3, 0)
> +#define STARFIVE_PRE_PLL_FB_DIV_11_8(x)			UPDATE((x) >> 8, 3, 0)
> +
> +/* REG: 0x1a3 */
> +#define STARFIVE_PRE_PLL_DIV_3				0x1a3
> +#define STARFIVE_PRE_PLL_FB_DIV_7_0(x)			UPDATE(x, 7, 0)
> +
> +/* REG: 0x1a4*/
> +#define STARFIVE_PRE_PLL_DIV_4				0x1a4
> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C_MASK		GENMASK(1, 0)
> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C(x)		UPDATE(x, 1, 0)
> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B_MASK		GENMASK(3, 2)
> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B(x)		UPDATE(x, 3, 2)
> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A_MASK		GENMASK(5, 4)
> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A(x)		UPDATE(x, 5, 4)
> +
> +/* REG: 0x1a5 */
> +#define STARFIVE_PRE_PLL_DIV_5				0x1a5
> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_SHIFT		5
> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_MASK		GENMASK(6, 5)
> +#define STARFIVE_PRE_PLL_PCLK_DIV_B(x)			UPDATE(x, 6, 5)
> +#define STARFIVE_PRE_PLL_PCLK_DIV_A_MASK		GENMASK(4, 0)
> +#define STARFIVE_PRE_PLL_PCLK_DIV_A(x)			UPDATE(x, 4, 0)
> +
> +/* REG: 0x1a6 */
> +#define STARFIVE_PRE_PLL_DIV_6				0x1a6
> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_SHIFT		5
> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_MASK		GENMASK(6, 5)
> +#define STARFIVE_PRE_PLL_PCLK_DIV_C(x)			UPDATE(x, 6, 5)
> +#define STARFIVE_PRE_PLL_PCLK_DIV_D_MASK		GENMASK(4, 0)
> +#define STARFIVE_PRE_PLL_PCLK_DIV_D(x)			UPDATE(x, 4, 0)
> +
> +/* REG: 0x1a9 */
> +#define STARFIVE_PRE_PLL_LOCK_STATUS			0x1a9
> +
> +/* REG: 0x1aa */
> +#define STARFIVE_POST_PLL_DIV_1				0x1aa
> +#define STARFIVE_POST_PLL_POST_DIV_ENABLE		GENMASK(3, 2)
> +#define STARFIVE_POST_PLL_REFCLK_SEL_TMDS		BIT(1)
> +#define STARFIVE_POST_PLL_POWER_DOWN			BIT(0)
> +#define STARFIVE_POST_PLL_FB_DIV_8(x)			UPDATE(((x) >> 8) << 4, 4, 4)
> +
> +/* REG:0x1ab */
> +#define STARFIVE_POST_PLL_DIV_2				0x1ab
> +#define STARFIVE_POST_PLL_Pre_DIV_MASK			GENMASK(5, 0)
> +#define STARFIVE_POST_PLL_PRE_DIV(x)			UPDATE(x, 5, 0)
> +
> +/* REG: 0x1ac */
> +#define STARFIVE_POST_PLL_DIV_3				0x1ac
> +#define STARFIVE_POST_PLL_FB_DIV_7_0(x)			UPDATE(x, 7, 0)
> +
> +/* REG: 0x1ad */
> +#define STARFIVE_POST_PLL_DIV_4				0x1ad
> +#define STARFIVE_POST_PLL_POST_DIV_MASK			GENMASK(2, 0)
> +#define STARFIVE_POST_PLL_POST_DIV_2			0x0
> +#define STARFIVE_POST_PLL_POST_DIV_4			0x1
> +#define STARFIVE_POST_PLL_POST_DIV_8			0x3
> +
> +/* REG: 0x1af */
> +#define STARFIVE_POST_PLL_LOCK_STATUS			0x1af
> +
> +/* REG: 0x1b0 */
> +#define STARFIVE_BIAS_CONTROL				0x1b0
> +#define STARFIVE_BIAS_ENABLE				BIT(2)
> +
> +/* REG: 0x1b2 */
> +#define STARFIVE_TMDS_CONTROL				0x1b2
> +#define STARFIVE_TMDS_CLK_DRIVER_EN			BIT(3)
> +#define STARFIVE_TMDS_D2_DRIVER_EN			BIT(2)
> +#define STARFIVE_TMDS_D1_DRIVER_EN			BIT(1)
> +#define STARFIVE_TMDS_D0_DRIVER_EN			BIT(0)
> +#define STARFIVE_TMDS_DRIVER_ENABLE			(STARFIVE_TMDS_CLK_DRIVER_EN | \
> +							 STARFIVE_TMDS_D2_DRIVER_EN | \
> +							 STARFIVE_TMDS_D1_DRIVER_EN | \
> +							 STARFIVE_TMDS_D0_DRIVER_EN)
> +
> +/* REG: 0x1b4 */
> +#define STARFIVE_LDO_CONTROL				0x1b4
> +#define STARFIVE_LDO_D2_EN				BIT(2)
> +#define STARFIVE_LDO_D1_EN				BIT(1)
> +#define STARFIVE_LDO_D0_EN				BIT(0)
> +#define STARFIVE_LDO_ENABLE				(STARFIVE_LDO_D2_EN | \
> +							 STARFIVE_LDO_D1_EN | \
> +							 STARFIVE_LDO_D0_EN)
> +
> +/* REG: 0x1be */
> +#define STARFIVE_SERIALIER_CONTROL			0x1be
> +#define STARFIVE_SERIALIER_D2_EN			BIT(6)
> +#define STARFIVE_SERIALIER_D1_EN			BIT(5)
> +#define STARFIVE_SERIALIER_D0_EN			BIT(4)
> +#define STARFIVE_SERIALIER_EN				BIT(0)
> +
> +#define STARFIVE_SERIALIER_ENABLE			(STARFIVE_SERIALIER_D2_EN | \
> +							 STARFIVE_SERIALIER_D1_EN | \
> +							 STARFIVE_SERIALIER_D0_EN | \
> +							 STARFIVE_SERIALIER_EN)
> +
> +/* REG: 0x1cc */
> +#define STARFIVE_RX_CONTROL				0x1cc
> +#define STARFIVE_RX_EN					BIT(3)
> +#define STARFIVE_RX_CHANNEL_2_EN			BIT(2)
> +#define STARFIVE_RX_CHANNEL_1_EN			BIT(1)
> +#define STARFIVE_RX_CHANNEL_0_EN			BIT(0)
> +#define STARFIVE_RX_ENABLE				(STARFIVE_RX_EN | \
> +							 STARFIVE_RX_CHANNEL_2_EN | \
> +							 STARFIVE_RX_CHANNEL_1_EN | \
> +							 STARFIVE_RX_CHANNEL_0_EN)
> +
> +/* REG: 0x1d1 */
> +#define STARFIVE_PRE_PLL_FRAC_DIV_H			0x1d1
> +#define STARFIVE_PRE_PLL_FRAC_DIV_23_16(x)		UPDATE((x) >> 16, 7, 0)
> +/* REG: 0x1d2 */
> +#define STARFIVE_PRE_PLL_FRAC_DIV_M			0x1d2
> +#define STARFIVE_PRE_PLL_FRAC_DIV_15_8(x)		UPDATE((x) >> 8, 7, 0)
> +/* REG: 0x1d3 */
> +#define STARFIVE_PRE_PLL_FRAC_DIV_L			0x1d3
> +#define STARFIVE_PRE_PLL_FRAC_DIV_7_0(x)		UPDATE(x, 7, 0)
> +
> +struct pre_pll_config {
> +	unsigned long pixclock;
> +	unsigned long tmdsclock;
> +	u8 prediv;
> +	u16 fbdiv;
> +	u8 tmds_div_a;
> +	u8 tmds_div_b;
> +	u8 tmds_div_c;
> +	u8 pclk_div_a;
> +	u8 pclk_div_b;
> +	u8 pclk_div_c;
> +	u8 pclk_div_d;
> +	u8 vco_div_5_en;
> +	u32 fracdiv;
> +};
> +
> +struct post_pll_config {
> +	unsigned long tmdsclock;
> +	u8 prediv;
> +	u16 fbdiv;
> +	u8 postdiv;
> +	u8 post_div_en;
> +	u8 version;
> +};
> +
> +struct phy_config {
> +	unsigned long	tmdsclock;
> +	u8		regs[14];
> +};
> +
> +struct hdmi_data_info {
> +	int vic;
> +	bool sink_is_hdmi;
> +	bool sink_has_audio;
> +	unsigned int enc_in_format;
> +	unsigned int enc_out_format;
> +	unsigned int colorimetry;
> +};
> +
> +struct starfive_hdmi {
> +	struct device *dev;
> +	struct drm_device *drm_dev;
> +	struct drm_encoder	encoder;
> +	struct drm_connector	connector;
> +
> +	struct starfive_hdmi_i2c *i2c;
> +
> +	int irq;
> +	struct clk *sys_clk;
> +	struct clk *mclk;
> +	struct clk *bclk;
> +	struct reset_control *tx_rst;
> +	void __iomem *regs;
> +
> +	struct i2c_adapter *ddc;
> +
> +	unsigned long tmds_rate;
> +
> +	struct hdmi_data_info	hdmi_data;
> +	struct drm_display_mode previous_mode;
> +	const struct pre_pll_config	*pre_cfg;
> +	const struct post_pll_config	*post_cfg;
> +};
> +
> +#endif /* __STARFIVE_HDMI_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
> index 3cd533cfa..9669354bd 100644
> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> @@ -126,6 +126,11 @@ static const struct component_master_ops vs_drm_ops = {
>   static struct platform_driver *drm_sub_drivers[] = {
>   	&dc_platform_driver,
>   
> +	/* connector + encoder*/
> +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
> +	&starfive_hdmi_driver,
> +#endif
> +
>   };
>   
>   static struct component_match *vs_drm_match_add(struct device *dev)
> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
> index 369ad22d6..e90723419 100644
> --- a/drivers/gpu/drm/verisilicon/vs_drv.h
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.h
> @@ -24,4 +24,8 @@ to_vs_drm_private(const struct drm_device *dev)
>   	return container_of(dev, struct vs_drm_device, base);
>   }
>   
> +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
> +extern struct platform_driver starfive_hdmi_driver;
> +#endif
> +
>   #endif /* __VS_DRV_H__ */

-- 
With best wishes
Dmitry


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-10-25 22:23     ` Dmitry Baryshkov
  0 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-10-25 22:23 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: Conor Dooley, Albert Ou, Emil Renner Berthing, christian.koenig,
	Thomas Zimmermann, Bjorn Andersson, Chris Morgan, Maxime Ripard,
	Jagan Teki, Jack Zhu, Rob Herring, Palmer Dabbelt,
	Krzysztof Kozlowski, Paul Walmsley, Shengyang Chen,
	Changhuang Liang, Shawn Guo, Sumit Semwal

On 25/10/2023 13:39, Keith Zhao wrote:
> add hdmi driver as encoder and connect
> 
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>   drivers/gpu/drm/verisilicon/Kconfig         |   8 +-
>   drivers/gpu/drm/verisilicon/Makefile        |   1 +
>   drivers/gpu/drm/verisilicon/starfive_hdmi.c | 949 ++++++++++++++++++++
>   drivers/gpu/drm/verisilicon/starfive_hdmi.h | 295 ++++++
>   drivers/gpu/drm/verisilicon/vs_drv.c        |   5 +
>   drivers/gpu/drm/verisilicon/vs_drv.h        |   4 +
>   6 files changed, 1261 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.c
>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.h
> 
> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
> index 3a361f8c8..122c786e3 100644
> --- a/drivers/gpu/drm/verisilicon/Kconfig
> +++ b/drivers/gpu/drm/verisilicon/Kconfig
> @@ -12,4 +12,10 @@ config DRM_VERISILICON
>   	  setting and buffer management. It does not
>   	  provide 2D or 3D acceleration.
>   
> -
> +config DRM_VERISILICON_STARFIVE_HDMI
> +	bool "Starfive HDMI extensions"
> +	depends on DRM_VERISILICON
> +	help
> +	   This selects support for StarFive soc specific extensions
> +	   for the Innosilicon HDMI driver. If you want to enable
> +	   HDMI on JH7110 based soc, you should select this option.
> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> index 1d48016ca..08350f25b 100644
> --- a/drivers/gpu/drm/verisilicon/Makefile
> +++ b/drivers/gpu/drm/verisilicon/Makefile
> @@ -7,5 +7,6 @@ vs_drm-objs := vs_dc_hw.o \
>   		vs_modeset.o \
>   		vs_plane.o
>   
> +vs_drm-$(CONFIG_DRM_VERISILICON_STARFIVE_HDMI) += starfive_hdmi.o
>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>   
> diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.c b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
> new file mode 100644
> index 000000000..d296c4b71
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
> @@ -0,0 +1,949 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/hdmi.h>
> +#include <linux/i2c.h>
> +#include <linux/irq.h>
> +#include <linux/media-bus-format.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/reset.h>
> +
> +#include <drm/bridge/dw_hdmi.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_managed.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_simple_kms_helper.h>
> +
> +#include "starfive_hdmi.h"
> +#include "vs_drv.h"
> +#include "vs_crtc.h"
> +
> +static struct starfive_hdmi *encoder_to_hdmi(struct drm_encoder *encoder)
> +{
> +	return container_of(encoder, struct starfive_hdmi, encoder);
> +}
> +
> +static struct starfive_hdmi *connector_to_hdmi(struct drm_connector *connector)
> +{
> +	return container_of(connector, struct starfive_hdmi, connector);
> +}
> +
> +struct starfive_hdmi_i2c {
> +	struct i2c_adapter adap;
> +
> +	u8 ddc_addr;
> +	u8 segment_addr;
> +	/* protects the edid data when use i2c cmd to read edid */
> +	struct mutex lock;
> +	struct completion cmp;
> +};
> +
> +static const struct pre_pll_config pre_pll_cfg_table[] = {
> +	{ 25175000,  25175000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0xf55555},
> +	{ 25200000,  25200000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0},
> +	{ 27000000,  27000000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0},

Such data structures are usually pretyt limited and hard to handle. 
Could you please replace it with the runtime calculations of

> +	{ 27027000,  27027000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0x170a3d},
> +	{ 28320000,  28320000, 1,  28, 2, 1, 1,  3, 0, 3, 4, 0, 0x51eb85},
> +	{ 30240000,  30240000, 1,  30, 2, 1, 1,  3, 0, 3, 4, 0, 0x3d70a3},
> +	{ 31500000,  31500000, 1,  31, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> +	{ 33750000,  33750000, 1,  33, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
> +	{ 36000000,  36000000, 1,  36, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{ 40000000,  40000000, 1,  80, 2, 2, 2, 12, 2, 2, 2, 0, 0},
> +	{ 46970000,  46970000, 1,  46, 2, 1, 1,  3, 0, 3, 4, 0, 0xf851eb},
> +	{ 49500000,  49500000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> +	{ 49000000,  49000000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{ 50000000,  50000000, 1,  50, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{ 54000000,  54000000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{ 54054000,  54054000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0x0dd2f1},
> +	{ 57284000,  57284000, 1,  57, 2, 1, 1,  3, 0, 3, 4, 0, 0x48b439},
> +	{ 58230000,  58230000, 1,  58, 2, 1, 1,  3, 0, 3, 4, 0, 0x3ae147},
> +	{ 59341000,  59341000, 1,  59, 2, 1, 1,  3, 0, 3, 4, 0, 0x574bc6},
> +	{ 59400000,  59400000, 1,  99, 3, 1, 1,  1, 3, 3, 4, 0, 0},
> +	{ 65000000,  65000000, 1, 130, 2, 2, 2,  12, 0, 2, 2, 0, 0},
> +	{ 68250000,  68250000, 1, 68,  2, 1, 1,  3,  0, 3, 4, 0, 0x3fffff},
> +	{ 71000000,  71000000, 1,  71, 2, 1, 1,  3, 0, 3,  4, 0, 0},
> +	{ 74176000,  74176000, 1,  98, 1, 2, 2,  1, 2, 3, 4, 0, 0xe6ae6b},
> +	{ 74250000,  74250000, 1,  99, 1, 2, 2,  1, 2, 3, 4, 0, 0},
> +	{ 75000000,  75000000, 1,  75, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{ 78750000,  78750000, 1,  78, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
> +	{ 79500000,  79500000, 1,  79, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> +	{ 83500000,  83500000, 2, 167, 2, 1, 1,  1, 0, 0,  6, 0, 0},
> +	{ 83500000, 104375000, 1, 104, 2, 1, 1,  1, 1, 0,  5, 0, 0x600000},
> +	{ 84858000,  84858000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xdba5e2},
> +	{ 85500000,  85500000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
> +	{ 85750000,  85750000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
> +	{ 85800000,  85800000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcccccc},
> +	{ 88750000,  88750000, 1,  88, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
> +	{ 89910000,  89910000, 1,  89, 2, 1, 1,  3, 0, 3, 4, 0, 0xe8f5c1},
> +	{ 90000000,  90000000, 1,  90, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{101000000, 101000000, 1, 101, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{102250000, 102250000, 1, 102, 2, 1, 1,  3, 0, 3, 4, 0, 0x3fffff},
> +	{106500000, 106500000, 1, 106, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> +	{108000000, 108000000, 1,  90, 3, 0, 0,  5, 0, 2,  2, 0, 0},
> +	{119000000, 119000000, 1, 119, 2, 1, 1,  3, 0, 3,  4, 0, 0},
> +	{131481000, 131481000, 1,  131, 2, 1, 1,  3, 0, 3,  4, 0, 0x7b22d1},
> +	{135000000, 135000000, 1,  135, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{136750000, 136750000, 1,  136, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
> +	{147180000, 147180000, 1,  147, 2, 1, 1,  3, 0, 3, 4, 0, 0x2e147a},
> +	{148352000, 148352000, 1,  98, 1, 1, 1,  1, 2, 2, 2, 0, 0xe6ae6b},
> +	{148500000, 148500000, 1,  99, 1, 1, 1,  1, 2, 2, 2, 0, 0},
> +	{154000000, 154000000, 1, 154, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{156000000, 156000000, 1, 156, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{157000000, 157000000, 1, 157, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{162000000, 162000000, 1, 162, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{174250000, 174250000, 1, 145, 3, 0, 0,  5, 0, 2, 2, 0, 0x355555},
> +	{174500000, 174500000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> +	{174570000, 174570000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x91eb84},
> +	{175500000, 175500000, 1, 175, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> +	{185590000, 185590000, 1, 185, 2, 1, 1,  3, 0, 3, 4, 0, 0x970a3c},
> +	{187000000, 187000000, 1, 187, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> +	{241500000, 241500000, 1, 161, 1, 1, 1,  4, 0, 2,  2, 0, 0},
> +	{241700000, 241700000, 1, 241, 2, 1, 1,  3, 0, 3,  4, 0, 0xb33332},
> +	{262750000, 262750000, 1, 262, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
> +	{296500000, 296500000, 1, 296, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
> +	{296703000, 296703000, 1,  98, 0, 1, 1,  1, 0, 2,  2, 0, 0xe6ae6b},
> +	{297000000, 297000000, 1,  99, 0, 1, 1,  1, 0, 2,  2, 0, 0},
> +	{594000000, 594000000, 1,  99, 0, 2, 0,  1, 0, 1,  1, 0, 0},
> +	{0, 0, 0,  0, 0, 0, 0,  0, 0, 0,  0, 0, 0},
> +};
> +
> +static const struct post_pll_config post_pll_cfg_table[] = {
> +	{25200000,	1, 80, 13, 3, 1},
> +	{27000000,	1, 40, 11, 3, 1},
> +	{33750000,	1, 40, 11, 3, 1},
> +	{49000000,	1, 20, 1, 3, 3},
> +	{241700000, 1, 20, 1, 3, 3},
> +	{297000000, 4, 20, 0, 0, 3},
> +	{594000000, 4, 20, 0, 0, 0},
> +	{ /* sentinel */ }
> +};
> +
> +inline u8 hdmi_readb(struct starfive_hdmi *hdmi, u16 offset)
> +{
> +	return readl_relaxed(hdmi->regs + (offset) * 0x04);
> +}
> +
> +inline void hdmi_writeb(struct starfive_hdmi *hdmi, u16 offset, u32 val)
> +{
> +	writel_relaxed(val, hdmi->regs + (offset) * 0x04);
> +}
> +
> +inline void hdmi_modb(struct starfive_hdmi *hdmi, u16 offset,
> +			     u32 msk, u32 val)
> +{
> +	u8 temp = hdmi_readb(hdmi, offset) & ~msk;
> +
> +	temp |= val & msk;
> +	hdmi_writeb(hdmi, offset, temp);
> +}
> +
> +static int starfive_hdmi_enable_clk_deassert_rst(struct device *dev, struct starfive_hdmi *hdmi)
> +{
> +	int ret;
> +
> +	ret = clk_prepare_enable(hdmi->sys_clk);
> +	if (ret) {
> +		dev_err(dev, "Cannot enable HDMI sys clock: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(hdmi->mclk);
> +	if (ret) {
> +		dev_err(dev, "Cannot enable HDMI mclk clock: %d\n", ret);
> +		goto err_mclk;
> +	}
> +	ret = clk_prepare_enable(hdmi->bclk);

This code begs to use clk_bulk instead.

> +	if (ret) {
> +		dev_err(dev, "Cannot enable HDMI bclk clock: %d\n", ret);
> +		goto err_bclk;
> +	}
> +	ret = reset_control_deassert(hdmi->tx_rst);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to deassert tx_rst\n");
> +		goto err_rst;
> +	}
> +	return 0;
> +
> +err_rst:
> +	clk_disable_unprepare(hdmi->bclk);
> +err_bclk:
> +	clk_disable_unprepare(hdmi->mclk);
> +err_mclk:
> +	clk_disable_unprepare(hdmi->sys_clk);
> +	return ret;
> +}
> +
> +static void starfive_hdmi_disable_clk_assert_rst(struct device *dev, struct starfive_hdmi *hdmi)
> +{
> +	int ret;
> +
> +	ret = reset_control_assert(hdmi->tx_rst);
> +	if (ret < 0)
> +		dev_err(dev, "failed to assert tx_rst\n");
> +
> +	clk_disable_unprepare(hdmi->sys_clk);
> +	clk_disable_unprepare(hdmi->mclk);
> +	clk_disable_unprepare(hdmi->bclk);
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int hdmi_system_pm_suspend(struct device *dev)
> +{
> +	return pm_runtime_force_suspend(dev);
> +}
> +
> +static int hdmi_system_pm_resume(struct device *dev)
> +{
> +	return pm_runtime_force_resume(dev);
> +}
> +#endif
> +
> +#ifdef CONFIG_PM
> +static int hdmi_runtime_suspend(struct device *dev)
> +{
> +	struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
> +
> +	starfive_hdmi_disable_clk_assert_rst(dev, hdmi);
> +
> +	return 0;
> +}
> +
> +static int hdmi_runtime_resume(struct device *dev)
> +{
> +	struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
> +
> +	return starfive_hdmi_enable_clk_deassert_rst(dev, hdmi);
> +}
> +#endif
> +
> +static void starfive_hdmi_tx_phy_power_down(struct starfive_hdmi *hdmi)
> +{
> +	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
> +}
> +
> +static void starfive_hdmi_tx_phy_power_on(struct starfive_hdmi *hdmi)
> +{
> +	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);

It looks like one can inline these two helpers w/o too much troubles.

> +}
> +
> +static void starfive_hdmi_config_pll(struct starfive_hdmi *hdmi)
> +{
> +	u32 val;
> +	u8 reg_1ad_value = hdmi->post_cfg->post_div_en ?
> +		 hdmi->post_cfg->postdiv : 0x00;
> +	u8 reg_1aa_value = hdmi->post_cfg->post_div_en ?
> +		 0x0e : 0x02;
> +
> +	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN);
> +	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1,
> +		    STARFIVE_POST_PLL_POST_DIV_ENABLE |
> +		    STARFIVE_POST_PLL_REFCLK_SEL_TMDS |
> +		    STARFIVE_POST_PLL_POWER_DOWN);
> +	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_1, STARFIVE_PRE_PLL_PRE_DIV(hdmi->pre_cfg->prediv));
> +
> +	val = STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE | STARFIVE_SPREAD_SPECTRUM_MOD_DOWN;
> +	if (!hdmi->pre_cfg->fracdiv)
> +		val |= STARFIVE_PRE_PLL_FRAC_DIV_DISABLE;
> +	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_2,
> +		    STARFIVE_PRE_PLL_FB_DIV_11_8(hdmi->pre_cfg->fbdiv) | val);
> +	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_3,
> +		    STARFIVE_PRE_PLL_FB_DIV_7_0(hdmi->pre_cfg->fbdiv));
> +	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_4,
> +		    STARFIVE_PRE_PLL_TMDSCLK_DIV_C(hdmi->pre_cfg->tmds_div_c) |
> +		    STARFIVE_PRE_PLL_TMDSCLK_DIV_A(hdmi->pre_cfg->tmds_div_a) |
> +		    STARFIVE_PRE_PLL_TMDSCLK_DIV_B(hdmi->pre_cfg->tmds_div_b));
> +
> +	if (hdmi->pre_cfg->fracdiv) {
> +		hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_L,
> +			    STARFIVE_PRE_PLL_FRAC_DIV_7_0(hdmi->pre_cfg->fracdiv));
> +		hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_M,
> +			    STARFIVE_PRE_PLL_FRAC_DIV_15_8(hdmi->pre_cfg->fracdiv));
> +		hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_H,
> +			    STARFIVE_PRE_PLL_FRAC_DIV_23_16(hdmi->pre_cfg->fracdiv));
> +	}
> +
> +	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_5,
> +		    STARFIVE_PRE_PLL_PCLK_DIV_A(hdmi->pre_cfg->pclk_div_a) |
> +		    STARFIVE_PRE_PLL_PCLK_DIV_B(hdmi->pre_cfg->pclk_div_b));
> +	hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_6,
> +		    STARFIVE_PRE_PLL_PCLK_DIV_C(hdmi->pre_cfg->pclk_div_c) |
> +		    STARFIVE_PRE_PLL_PCLK_DIV_D(hdmi->pre_cfg->pclk_div_d));
> +
> +	/*pre-pll power down*/
> +	hdmi_modb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN, 0);
> +
> +	hdmi_modb(hdmi, STARFIVE_POST_PLL_DIV_2, STARFIVE_POST_PLL_Pre_DIV_MASK,
> +		  STARFIVE_POST_PLL_PRE_DIV(hdmi->post_cfg->prediv));
> +	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_3, hdmi->post_cfg->fbdiv & 0xff);
> +	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_4, reg_1ad_value);
> +	hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1, reg_1aa_value);
> +}
> +
> +static void starfive_hdmi_tmds_driver_on(struct starfive_hdmi *hdmi)
> +{
> +	hdmi_modb(hdmi, STARFIVE_TMDS_CONTROL,
> +		  STARFIVE_TMDS_DRIVER_ENABLE, STARFIVE_TMDS_DRIVER_ENABLE);
> +}
> +
> +static void starfive_hdmi_sync_tmds(struct starfive_hdmi *hdmi)
> +{
> +	/*first send 0 to this bit, then send 1 and keep 1 into this bit*/
> +	hdmi_writeb(hdmi, HDMI_SYNC, 0x0);
> +	hdmi_writeb(hdmi, HDMI_SYNC, 0x1);
> +}
> +
> +static void starfive_hdmi_i2c_init(struct starfive_hdmi *hdmi)
> +{
> +	int ddc_bus_freq;
> +
> +	ddc_bus_freq = (clk_get_rate(hdmi->sys_clk) >> 2) / HDMI_SCL_RATE;
> +
> +	hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
> +	hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
> +
> +	/* Clear the EDID interrupt flag and mute the interrupt */
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
> +}
> +
> +static const
> +struct pre_pll_config *starfive_hdmi_phy_get_pre_pll_cfg(struct starfive_hdmi *hdmi,
> +							 unsigned long rate)
> +{
> +	const struct pre_pll_config *cfg = pre_pll_cfg_table;
> +
> +	rate = (rate / 1000) * 1000;
> +	for (; cfg->pixclock != 0; cfg++)
> +		if (cfg->tmdsclock == rate && cfg->pixclock == rate)
> +			break;
> +
> +	if (cfg->pixclock == 0)
> +		return ERR_PTR(-EINVAL);
> +
> +	return cfg;
> +}
> +
> +static int starfive_hdmi_phy_clk_set_rate(struct starfive_hdmi *hdmi)
> +{
> +	hdmi->post_cfg = post_pll_cfg_table;
> +
> +	hdmi->pre_cfg = starfive_hdmi_phy_get_pre_pll_cfg(hdmi, hdmi->tmds_rate);
> +	if (IS_ERR(hdmi->pre_cfg))
> +		return PTR_ERR(hdmi->pre_cfg);
> +
> +	for (; hdmi->post_cfg->tmdsclock != 0; hdmi->post_cfg++)
> +		if (hdmi->tmds_rate <= hdmi->post_cfg->tmdsclock)
> +			break;
> +
> +	starfive_hdmi_config_pll(hdmi);
> +
> +	return 0;
> +}
> +
> +static int starfive_hdmi_config_video_timing(struct starfive_hdmi *hdmi,
> +					     struct drm_display_mode *mode)
> +{
> +	int value;
> +	/* Set detail external video timing */
> +	value = mode->htotal;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);

As you have hdmi_writeb already, adding hdmi_writew will help here.

> +
> +	value = mode->htotal - mode->hdisplay;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
> +
> +	value = mode->htotal - mode->hsync_start;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
> +
> +	value = mode->hsync_end - mode->hsync_start;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
> +
> +	value = mode->vtotal;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
> +
> +	value = mode->vtotal - mode->vdisplay;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
> +
> +	value = mode->vtotal - mode->vsync_start;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
> +
> +	value = mode->vsync_end - mode->vsync_start;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
> +
> +	/* Set detail external video timing polarity and interlace mode */
> +	value = v_EXTERANL_VIDEO(1);
> +	value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
> +		v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
> +	value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
> +		v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
> +	value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
> +		v_INETLACE(1) : v_INETLACE(0);
> +
> +	hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
> +	return 0;
> +}
> +
> +static int starfive_hdmi_setup(struct starfive_hdmi *hdmi,
> +			       struct drm_display_mode *mode)
> +{
> +	hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
> +	hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
> +	hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
> +
> +	hdmi->tmds_rate = mode->clock * 1000;
> +	starfive_hdmi_phy_clk_set_rate(hdmi);
> +
> +	while (!(hdmi_readb(hdmi, STARFIVE_PRE_PLL_LOCK_STATUS) & 0x1))
> +		continue;
> +	while (!(hdmi_readb(hdmi, STARFIVE_POST_PLL_LOCK_STATUS) & 0x1))
> +		continue;

Please use read_iopoll_timeout here.

> +
> +	/*turn on LDO*/
> +	hdmi_writeb(hdmi, STARFIVE_LDO_CONTROL, STARFIVE_LDO_ENABLE);
> +	/*turn on serializer*/
> +	hdmi_writeb(hdmi, STARFIVE_SERIALIER_CONTROL, STARFIVE_SERIALIER_ENABLE);
> +
> +	starfive_hdmi_tx_phy_power_down(hdmi);
> +	starfive_hdmi_config_video_timing(hdmi, mode);
> +	starfive_hdmi_tx_phy_power_on(hdmi);
> +
> +	starfive_hdmi_tmds_driver_on(hdmi);
> +	starfive_hdmi_sync_tmds(hdmi);
> +
> +	return 0;
> +}
> +
> +static void starfive_hdmi_encoder_mode_set(struct drm_encoder *encoder,
> +					   struct drm_display_mode *mode,
> +					   struct drm_display_mode *adj_mode)
> +{
> +	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> +
> +	drm_mode_copy(&hdmi->previous_mode, adj_mode);

Why do you need it?

> +}
> +
> +static void starfive_hdmi_encoder_enable(struct drm_encoder *encoder)
> +{
> +	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> +	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
> +	int ret, idx;
> +	struct drm_device *drm = hdmi->connector.dev;
> +
> +	if (drm && !drm_dev_enter(drm, &idx))
> +		return;
> +
> +	ret = pm_runtime_get_sync(hdmi->dev);
> +	if (ret < 0)
> +		return;
> +	mdelay(10);
> +	starfive_hdmi_setup(hdmi, mode);
> +
> +	if (drm)
> +		drm_dev_exit(idx);
> +}
> +
> +static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
> +{
> +	struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> +
> +	int idx;
> +	struct drm_device *drm = hdmi->connector.dev;
> +
> +	if (drm && !drm_dev_enter(drm, &idx))
> +		return;
> +
> +	pm_runtime_put(hdmi->dev);
> +
> +	if (drm)
> +		drm_dev_exit(idx);
> +}
> +
> +static int
> +starfive_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
> +				   struct drm_crtc_state *crtc_state,
> +				   struct drm_connector_state *conn_state)
> +{
> +	bool valid = false;
> +	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
> +
> +	vs_crtc_state->encoder_type = encoder->encoder_type;
> +	vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
> +
> +	const struct pre_pll_config *cfg = pre_pll_cfg_table;
> +	int pclk = mode->clock * 1000;
> +
> +	for (; cfg->pixclock != 0; cfg++) {
> +		if (pclk == cfg->pixclock) {
> +			if (pclk > 297000000)

Magic value.

> +				continue;
> +
> +			valid = true;
> +			break;
> +		}
> +	}
> +
> +	return (valid) ? 0 : -EINVAL;
> +}
> +
> +static const struct drm_encoder_helper_funcs starfive_hdmi_encoder_helper_funcs = {
> +	.enable     = starfive_hdmi_encoder_enable,
> +	.disable    = starfive_hdmi_encoder_disable,
> +	.mode_set   = starfive_hdmi_encoder_mode_set,
> +	.atomic_check = starfive_hdmi_encoder_atomic_check,
> +};
> +
> +static enum drm_connector_status
> +starfive_hdmi_connector_detect(struct drm_connector *connector, bool force)
> +{
> +	struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
> +	struct drm_device *drm = hdmi->connector.dev;
> +	int ret;
> +	int idx;
> +
> +	if (drm && !drm_dev_enter(drm, &idx))
> +		return connector_status_disconnected;
> +
> +	ret = pm_runtime_get_sync(hdmi->dev);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
> +		connector_status_connected : connector_status_disconnected;
> +	pm_runtime_put(hdmi->dev);
> +
> +	if (drm)
> +		drm_dev_exit(idx);
> +
> +	return ret;
> +}
> +
> +static int starfive_hdmi_connector_get_modes(struct drm_connector *connector)
> +{
> +	struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
> +	struct edid *edid;
> +	int ret = 0;
> +
> +	if (!hdmi->ddc)
> +		return 0;
> +	ret = pm_runtime_get_sync(hdmi->dev);
> +	if (ret < 0)
> +		return ret;
> +
> +	edid = drm_get_edid(connector, hdmi->ddc);
> +	if (edid) {
> +		hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
> +		hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
> +		drm_connector_update_edid_property(connector, edid);
> +		ret = drm_add_edid_modes(connector, edid);
> +		kfree(edid);
> +	}
> +	pm_runtime_put(hdmi->dev);

Can you use drm_connector_helper_get_modes_from_ddc() here? And then 
drm_display_info.is_hdmi and .has_audio.

> +
> +	return ret;
> +}
> +
> +static enum drm_mode_status
> +starfive_hdmi_connector_mode_valid(struct drm_connector *connector,
> +				   struct drm_display_mode *mode)
> +{
> +	const struct pre_pll_config *cfg = pre_pll_cfg_table;
> +	int pclk = mode->clock * 1000;
> +	bool valid = false;
> +
> +	for (; cfg->pixclock != 0; cfg++) {
> +		if (pclk == cfg->pixclock) {
> +			if (pclk > 297000000)

magic value

> +				continue;
> +
> +			valid = true;
> +			break;
> +		}
> +	}
> +
> +	return (valid) ? MODE_OK : MODE_BAD;
> +}
> +
> +static int
> +starfive_hdmi_probe_single_connector_modes(struct drm_connector *connector,
> +					   u32 maxX, u32 maxY)
> +{
> +	return drm_helper_probe_single_connector_modes(connector, 3840, 2160);
> +}
> +
> +static const struct drm_connector_funcs starfive_hdmi_connector_funcs = {
> +	.fill_modes = starfive_hdmi_probe_single_connector_modes,
> +	.detect = starfive_hdmi_connector_detect,
> +	.reset = drm_atomic_helper_connector_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static struct drm_connector_helper_funcs starfive_hdmi_connector_helper_funcs = {
> +	.get_modes = starfive_hdmi_connector_get_modes,
> +	.mode_valid = starfive_hdmi_connector_mode_valid,
> +};
> +
> +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
> +{
> +	struct drm_encoder *encoder = &hdmi->encoder;
> +	struct device *dev = hdmi->dev;
> +
> +	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
> +
> +	/*
> +	 * If we failed to find the CRTC(s) which this encoder is
> +	 * supposed to be connected to, it's because the CRTC has
> +	 * not been registered yet.  Defer probing, and hope that
> +	 * the required CRTC is added later.
> +	 */
> +	if (encoder->possible_crtcs == 0)
> +		return -EPROBE_DEFER;
> +
> +	drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
> +
> +	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> +
> +	drm_connector_helper_add(&hdmi->connector,
> +				 &starfive_hdmi_connector_helper_funcs);
> +	drmm_connector_init(drm, &hdmi->connector,
> +			    &starfive_hdmi_connector_funcs,
> +			    DRM_MODE_CONNECTOR_HDMIA,

On an embedded device one can not be so sure. There can be MHL or HDMI 
Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.

> +			    hdmi->ddc);
> +
> +	drm_connector_attach_encoder(&hdmi->connector, encoder);
> +
> +	return 0;
> +}
> +
> +static irqreturn_t starfive_hdmi_i2c_irq(struct starfive_hdmi *hdmi)
> +{
> +	struct starfive_hdmi_i2c *i2c = hdmi->i2c;
> +	u8 stat;
> +
> +	stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
> +	if (!(stat & m_INT_EDID_READY))
> +		return IRQ_NONE;
> +
> +	/* Clear HDMI EDID interrupt flag */
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
> +
> +	complete(&i2c->cmp);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t starfive_hdmi_hardirq(int irq, void *dev_id)
> +{
> +	struct starfive_hdmi *hdmi = dev_id;
> +	irqreturn_t ret = IRQ_NONE;
> +	u8 interrupt;
> +
> +	if (hdmi->i2c)
> +		ret = starfive_hdmi_i2c_irq(hdmi);
> +
> +	interrupt = hdmi_readb(hdmi, HDMI_STATUS);
> +	if (interrupt & m_INT_HOTPLUG) {
> +		hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
> +		ret = IRQ_WAKE_THREAD;
> +	}
> +
> +	return ret;
> +}
> +
> +static irqreturn_t starfive_hdmi_irq(int irq, void *dev_id)
> +{
> +	struct starfive_hdmi *hdmi = dev_id;
> +
> +	drm_connector_helper_hpd_irq_event(&hdmi->connector);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int starfive_hdmi_i2c_read(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
> +{
> +	int length = msgs->len;
> +	u8 *buf = msgs->buf;
> +	int ret;
> +
> +	ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10);

read_poll_timeout

> +	if (!ret)
> +		return -EAGAIN;
> +
> +	while (length--)
> +		*buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
> +
> +	return 0;
> +}
> +
> +static int starfive_hdmi_i2c_write(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
> +{
> +	/*
> +	 * The DDC module only support read EDID message, so
> +	 * we assume that each word write to this i2c adapter
> +	 * should be the offset of EDID word address.
> +	 */
> +	if (msgs->len != 1 ||
> +	    (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR))
> +		return -EINVAL;
> +
> +	reinit_completion(&hdmi->i2c->cmp);
> +
> +	if (msgs->addr == DDC_SEGMENT_ADDR)
> +		hdmi->i2c->segment_addr = msgs->buf[0];
> +	if (msgs->addr == DDC_ADDR)
> +		hdmi->i2c->ddc_addr = msgs->buf[0];
> +
> +	/* Set edid fifo first addr */
> +	hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
> +
> +	/* Set edid word address 0x00/0x80 */
> +	hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
> +
> +	/* Set edid segment pointer */
> +	hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
> +
> +	return 0;
> +}
> +
> +static int starfive_hdmi_i2c_xfer(struct i2c_adapter *adap,
> +				  struct i2c_msg *msgs, int num)
> +{
> +	struct starfive_hdmi *hdmi = i2c_get_adapdata(adap);
> +	struct starfive_hdmi_i2c *i2c = hdmi->i2c;
> +	int i, ret = 0;
> +
> +	mutex_lock(&i2c->lock);
> +
> +	/* Clear the EDID interrupt flag and unmute the interrupt */
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
> +
> +	for (i = 0; i < num; i++) {
> +		DRM_DEV_DEBUG(hdmi->dev,
> +			      "xfer: num: %d/%d, len: %d, flags: %#x\n",
> +			      i + 1, num, msgs[i].len, msgs[i].flags);
> +
> +		if (msgs[i].flags & I2C_M_RD)
> +			ret = starfive_hdmi_i2c_read(hdmi, &msgs[i]);
> +		else
> +			ret = starfive_hdmi_i2c_write(hdmi, &msgs[i]);
> +
> +		if (ret < 0)
> +			break;
> +	}
> +
> +	if (!ret)
> +		ret = num;
> +
> +	/* Mute HDMI EDID interrupt */
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
> +
> +	mutex_unlock(&i2c->lock);
> +
> +	return ret;
> +}
> +
> +static u32 starfive_hdmi_i2c_func(struct i2c_adapter *adapter)
> +{
> +	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static const struct i2c_algorithm starfive_hdmi_algorithm = {
> +	.master_xfer	= starfive_hdmi_i2c_xfer,
> +	.functionality	= starfive_hdmi_i2c_func,
> +};
> +
> +static struct i2c_adapter *starfive_hdmi_i2c_adapter(struct starfive_hdmi *hdmi)
> +{
> +	struct i2c_adapter *adap;
> +	struct starfive_hdmi_i2c *i2c;
> +	int ret;
> +
> +	i2c = drmm_kzalloc(hdmi->drm_dev, sizeof(*i2c), GFP_KERNEL);
> +	if (!i2c)
> +		return ERR_PTR(-ENOMEM);
> +
> +	mutex_init(&i2c->lock);
> +	init_completion(&i2c->cmp);
> +
> +	adap = &i2c->adap;
> +	adap->class = I2C_CLASS_DDC;
> +	adap->owner = THIS_MODULE;
> +	adap->dev.parent = hdmi->dev;
> +	adap->algo = &starfive_hdmi_algorithm;
> +	strscpy(adap->name, "Starfive HDMI", sizeof(adap->name));
> +	i2c_set_adapdata(adap, hdmi);
> +
> +	ret = devm_i2c_add_adapter(hdmi->dev, adap);
> +	if (ret) {
> +		drm_warn(hdmi->drm_dev, "cannot add %s I2C adapter\n", adap->name);
> +		return ERR_PTR(ret);
> +	}
> +
> +	hdmi->i2c = i2c;
> +
> +	drm_info(hdmi->drm_dev, "registered %s I2C bus driver success\n", adap->name);
> +
> +	return adap;
> +}
> +
> +static int starfive_hdmi_get_clk_rst(struct device *dev, struct starfive_hdmi *hdmi)
> +{
> +	hdmi->sys_clk = devm_clk_get(dev, "sysclk");
> +	if (IS_ERR(hdmi->sys_clk)) {
> +		dev_err(dev, "Unable to get HDMI sysclk clk\n");
> +		return PTR_ERR(hdmi->sys_clk);
> +	}
> +	hdmi->mclk = devm_clk_get(dev, "mclk");
> +	if (IS_ERR(hdmi->mclk)) {
> +		dev_err(dev, "Unable to get HDMI mclk clk\n");
> +		return PTR_ERR(hdmi->mclk);
> +	}
> +	hdmi->bclk = devm_clk_get(dev, "bclk");
> +	if (IS_ERR(hdmi->bclk)) {
> +		dev_err(dev, "Unable to get HDMI bclk clk\n");
> +		return PTR_ERR(hdmi->bclk);
> +	}
> +	hdmi->tx_rst = devm_reset_control_get_by_index(dev, 0);
> +	if (IS_ERR(hdmi->tx_rst)) {
> +		dev_err(dev, "failed to get tx_rst reset\n");
> +		return PTR_ERR(hdmi->tx_rst);
> +	}
> +
> +	return 0;
> +}
> +
> +static int starfive_hdmi_bind(struct device *dev, struct device *master,
> +			      void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm = dev_get_drvdata(master);
> +	struct starfive_hdmi *hdmi;
> +	struct resource *iores;
> +	int irq;
> +	int ret;
> +
> +	hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
> +	if (!hdmi)
> +		return -ENOMEM;
> +
> +	hdmi = drmm_simple_encoder_alloc(drm, struct starfive_hdmi,
> +					 encoder, DRM_MODE_ENCODER_TMDS);
> +
> +	hdmi->dev = dev;
> +	hdmi->drm_dev = drm;
> +	dev_set_drvdata(dev, hdmi);
> +
> +	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	hdmi->regs = devm_ioremap_resource(dev, iores);

This should go to probe

> +	if (IS_ERR(hdmi->regs))
> +		return PTR_ERR(hdmi->regs);
> +
> +	ret = starfive_hdmi_get_clk_rst(dev, hdmi);
> +	if (ret < 0)
> +		return ret;

And this

> +
> +	ret = devm_pm_runtime_enable(dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = pm_runtime_resume_and_get(dev);
> +	if (ret)
> +		return ret;
> +
> +	irq = platform_get_irq(pdev, 0);

And this

> +	if (irq < 0) {
> +		ret = irq;
> +		goto err_put_runtime_pm;
> +	}
> +
> +	hdmi->ddc = starfive_hdmi_i2c_adapter(hdmi);
> +	if (IS_ERR(hdmi->ddc)) {
> +		ret = PTR_ERR(hdmi->ddc);
> +		hdmi->ddc = NULL;
> +		goto err_put_runtime_pm;
> +	}
> +
> +	starfive_hdmi_i2c_init(hdmi);
> +
> +	ret = starfive_hdmi_register(drm, hdmi);
> +	if (ret)
> +		goto err_put_adapter;
> +
> +	/* Unmute hotplug interrupt */
> +	hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
> +
> +	ret = devm_request_threaded_irq(dev, irq, starfive_hdmi_hardirq,
> +					starfive_hdmi_irq, IRQF_SHARED,
> +					dev_name(dev), hdmi);
> +	if (ret < 0)
> +		goto err_put_adapter;
> +
> +	pm_runtime_put_sync(dev);
> +
> +	return 0;
> +
> +err_put_adapter:
> +	i2c_put_adapter(hdmi->ddc);
> +err_put_runtime_pm:
> +	pm_runtime_put_sync(dev);
> +
> +	return ret;
> +}
> +
> +static const struct component_ops starfive_hdmi_ops = {
> +	.bind	= starfive_hdmi_bind,
> +};
> +
> +static int starfive_hdmi_probe(struct platform_device *pdev)
> +{
> +	return component_add(&pdev->dev, &starfive_hdmi_ops);
> +}
> +
> +static int starfive_hdmi_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &starfive_hdmi_ops);
> +
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops hdmi_pm_ops = {
> +	SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
> +	SET_LATE_SYSTEM_SLEEP_PM_OPS(hdmi_system_pm_suspend, hdmi_system_pm_resume)
> +};
> +
> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
> +	{ .compatible = "starfive,jh7110-inno-hdmi",},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, starfive_hdmi_dt_ids);
> +
> +struct platform_driver starfive_hdmi_driver = {
> +	.probe  = starfive_hdmi_probe,
> +	.remove = starfive_hdmi_remove,
> +	.driver = {
> +		.name = "starfive-hdmi",
> +		.of_match_table = starfive_hdmi_dt_ids,
> +		.pm = &hdmi_pm_ops,
> +	},
> +};
> +
> +MODULE_AUTHOR("StarFive Corporation");
> +MODULE_DESCRIPTION("Starfive HDMI Driver");
> diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.h b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
> new file mode 100644
> index 000000000..1b9a11bca
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
> @@ -0,0 +1,295 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
> + */
> +
> +#ifndef __STARFIVE_HDMI_H__
> +#define __STARFIVE_HDMI_H__
> +
> +#include <drm/bridge/dw_hdmi.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_simple_kms_helper.h>
> +#include <linux/bitfield.h>
> +#include <linux/bits.h>
> +
> +#define DDC_SEGMENT_ADDR		0x30
> +
> +#define HDMI_SCL_RATE			(100 * 1000)
> +#define DDC_BUS_FREQ_L			0x4b
> +#define DDC_BUS_FREQ_H			0x4c
> +
> +#define HDMI_SYS_CTRL			0x00
> +#define m_RST_ANALOG			BIT(6)
> +#define v_RST_ANALOG			0
> +#define v_NOT_RST_ANALOG		BIT(6)
> +#define m_RST_DIGITAL			BIT(5)
> +#define v_RST_DIGITAL			0
> +#define v_NOT_RST_DIGITAL		BIT(5)
> +#define m_REG_CLK_INV			BIT(4)
> +#define v_REG_CLK_NOT_INV		0
> +#define v_REG_CLK_INV			BIT(4)
> +#define m_VCLK_INV			BIT(3)
> +#define v_VCLK_NOT_INV			0
> +#define v_VCLK_INV			BIT(3)
> +#define m_REG_CLK_SOURCE		BIT(2)
> +#define v_REG_CLK_SOURCE_TMDS		0
> +#define v_REG_CLK_SOURCE_SYS		BIT(2)
> +#define m_POWER				BIT(1)
> +#define v_PWR_ON			0
> +#define v_PWR_OFF			BIT(1)
> +#define m_INT_POL			BIT(0)
> +#define v_INT_POL_HIGH			1
> +#define v_INT_POL_LOW			0
> +
> +#define HDMI_AV_MUTE			0x05
> +#define m_AVMUTE_CLEAR			BIT(7)
> +#define m_AVMUTE_ENABLE			BIT(6)
> +#define m_AUDIO_MUTE			BIT(1)
> +#define m_VIDEO_BLACK			BIT(0)
> +#define v_AVMUTE_CLEAR(n)		((n) << 7)
> +#define v_AVMUTE_ENABLE(n)		((n) << 6)
> +#define v_AUDIO_MUTE(n)			((n) << 1)
> +#define v_VIDEO_MUTE(n)			((n) << 0)
> +
> +#define HDMI_VIDEO_TIMING_CTL		0x08
> +#define v_VSYNC_POLARITY(n)		((n) << 3)
> +#define v_HSYNC_POLARITY(n)		((n) << 2)
> +#define v_INETLACE(n)			((n) << 1)
> +#define v_EXTERANL_VIDEO(n)		((n) << 0)
> +
> +#define HDMI_VIDEO_EXT_HTOTAL_L		0x09
> +#define HDMI_VIDEO_EXT_HTOTAL_H		0x0a
> +#define HDMI_VIDEO_EXT_HBLANK_L		0x0b
> +#define HDMI_VIDEO_EXT_HBLANK_H		0x0c
> +#define HDMI_VIDEO_EXT_HDELAY_L		0x0d
> +#define HDMI_VIDEO_EXT_HDELAY_H		0x0e
> +#define HDMI_VIDEO_EXT_HDURATION_L	0x0f
> +#define HDMI_VIDEO_EXT_HDURATION_H	0x10
> +#define HDMI_VIDEO_EXT_VTOTAL_L		0x11
> +#define HDMI_VIDEO_EXT_VTOTAL_H		0x12
> +#define HDMI_VIDEO_EXT_VBLANK		0x13
> +#define HDMI_VIDEO_EXT_VDELAY		0x14
> +#define HDMI_VIDEO_EXT_VDURATION	0x15
> +
> +#define HDMI_EDID_SEGMENT_POINTER	0x4d
> +#define HDMI_EDID_WORD_ADDR		0x4e
> +#define HDMI_EDID_FIFO_OFFSET		0x4f
> +#define HDMI_EDID_FIFO_ADDR		0x50
> +
> +#define HDMI_INTERRUPT_MASK1		0xc0
> +#define HDMI_INTERRUPT_STATUS1		0xc1
> +#define	m_INT_ACTIVE_VSYNC		BIT(5)
> +#define m_INT_EDID_READY		BIT(2)
> +
> +#define HDMI_STATUS			0xc8
> +#define m_HOTPLUG			BIT(7)
> +#define m_MASK_INT_HOTPLUG		BIT(5)
> +#define m_INT_HOTPLUG			BIT(1)
> +#define v_MASK_INT_HOTPLUG(n)		(((n) & 0x1) << 5)

It is untypical to have field defines which start with lowercase letter.

> +
> +#define HDMI_SYNC					0xce
> +
> +#define UPDATE(x, h, l)					FIELD_PREP(GENMASK(h, l), x)
> +
> +/* REG: 0x1a0 */
> +#define STARFIVE_PRE_PLL_CONTROL			0x1a0
> +#define STARFIVE_PCLK_VCO_DIV_5_MASK			BIT(1)
> +#define STARFIVE_PCLK_VCO_DIV_5(x)			UPDATE(x, 1, 1)
> +#define STARFIVE_PRE_PLL_POWER_DOWN			BIT(0)
> +
> +/* REG: 0x1a1 */
> +#define STARFIVE_PRE_PLL_DIV_1				0x1a1
> +#define STARFIVE_PRE_PLL_PRE_DIV_MASK			GENMASK(5, 0)
> +#define STARFIVE_PRE_PLL_PRE_DIV(x)			UPDATE(x, 5, 0)
> +
> +/* REG: 0x1a2 */
> +#define STARFIVE_PRE_PLL_DIV_2				0x1a2
> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DOWN		BIT(7)
> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE		BIT(6)
> +#define STARFIVE_PRE_PLL_FRAC_DIV_DISABLE		UPDATE(3, 5, 4)
> +#define STARFIVE_PRE_PLL_FB_DIV_11_8_MASK		GENMASK(3, 0)
> +#define STARFIVE_PRE_PLL_FB_DIV_11_8(x)			UPDATE((x) >> 8, 3, 0)
> +
> +/* REG: 0x1a3 */
> +#define STARFIVE_PRE_PLL_DIV_3				0x1a3
> +#define STARFIVE_PRE_PLL_FB_DIV_7_0(x)			UPDATE(x, 7, 0)
> +
> +/* REG: 0x1a4*/
> +#define STARFIVE_PRE_PLL_DIV_4				0x1a4
> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C_MASK		GENMASK(1, 0)
> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C(x)		UPDATE(x, 1, 0)
> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B_MASK		GENMASK(3, 2)
> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B(x)		UPDATE(x, 3, 2)
> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A_MASK		GENMASK(5, 4)
> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A(x)		UPDATE(x, 5, 4)
> +
> +/* REG: 0x1a5 */
> +#define STARFIVE_PRE_PLL_DIV_5				0x1a5
> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_SHIFT		5
> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_MASK		GENMASK(6, 5)
> +#define STARFIVE_PRE_PLL_PCLK_DIV_B(x)			UPDATE(x, 6, 5)
> +#define STARFIVE_PRE_PLL_PCLK_DIV_A_MASK		GENMASK(4, 0)
> +#define STARFIVE_PRE_PLL_PCLK_DIV_A(x)			UPDATE(x, 4, 0)
> +
> +/* REG: 0x1a6 */
> +#define STARFIVE_PRE_PLL_DIV_6				0x1a6
> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_SHIFT		5
> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_MASK		GENMASK(6, 5)
> +#define STARFIVE_PRE_PLL_PCLK_DIV_C(x)			UPDATE(x, 6, 5)
> +#define STARFIVE_PRE_PLL_PCLK_DIV_D_MASK		GENMASK(4, 0)
> +#define STARFIVE_PRE_PLL_PCLK_DIV_D(x)			UPDATE(x, 4, 0)
> +
> +/* REG: 0x1a9 */
> +#define STARFIVE_PRE_PLL_LOCK_STATUS			0x1a9
> +
> +/* REG: 0x1aa */
> +#define STARFIVE_POST_PLL_DIV_1				0x1aa
> +#define STARFIVE_POST_PLL_POST_DIV_ENABLE		GENMASK(3, 2)
> +#define STARFIVE_POST_PLL_REFCLK_SEL_TMDS		BIT(1)
> +#define STARFIVE_POST_PLL_POWER_DOWN			BIT(0)
> +#define STARFIVE_POST_PLL_FB_DIV_8(x)			UPDATE(((x) >> 8) << 4, 4, 4)
> +
> +/* REG:0x1ab */
> +#define STARFIVE_POST_PLL_DIV_2				0x1ab
> +#define STARFIVE_POST_PLL_Pre_DIV_MASK			GENMASK(5, 0)
> +#define STARFIVE_POST_PLL_PRE_DIV(x)			UPDATE(x, 5, 0)
> +
> +/* REG: 0x1ac */
> +#define STARFIVE_POST_PLL_DIV_3				0x1ac
> +#define STARFIVE_POST_PLL_FB_DIV_7_0(x)			UPDATE(x, 7, 0)
> +
> +/* REG: 0x1ad */
> +#define STARFIVE_POST_PLL_DIV_4				0x1ad
> +#define STARFIVE_POST_PLL_POST_DIV_MASK			GENMASK(2, 0)
> +#define STARFIVE_POST_PLL_POST_DIV_2			0x0
> +#define STARFIVE_POST_PLL_POST_DIV_4			0x1
> +#define STARFIVE_POST_PLL_POST_DIV_8			0x3
> +
> +/* REG: 0x1af */
> +#define STARFIVE_POST_PLL_LOCK_STATUS			0x1af
> +
> +/* REG: 0x1b0 */
> +#define STARFIVE_BIAS_CONTROL				0x1b0
> +#define STARFIVE_BIAS_ENABLE				BIT(2)
> +
> +/* REG: 0x1b2 */
> +#define STARFIVE_TMDS_CONTROL				0x1b2
> +#define STARFIVE_TMDS_CLK_DRIVER_EN			BIT(3)
> +#define STARFIVE_TMDS_D2_DRIVER_EN			BIT(2)
> +#define STARFIVE_TMDS_D1_DRIVER_EN			BIT(1)
> +#define STARFIVE_TMDS_D0_DRIVER_EN			BIT(0)
> +#define STARFIVE_TMDS_DRIVER_ENABLE			(STARFIVE_TMDS_CLK_DRIVER_EN | \
> +							 STARFIVE_TMDS_D2_DRIVER_EN | \
> +							 STARFIVE_TMDS_D1_DRIVER_EN | \
> +							 STARFIVE_TMDS_D0_DRIVER_EN)
> +
> +/* REG: 0x1b4 */
> +#define STARFIVE_LDO_CONTROL				0x1b4
> +#define STARFIVE_LDO_D2_EN				BIT(2)
> +#define STARFIVE_LDO_D1_EN				BIT(1)
> +#define STARFIVE_LDO_D0_EN				BIT(0)
> +#define STARFIVE_LDO_ENABLE				(STARFIVE_LDO_D2_EN | \
> +							 STARFIVE_LDO_D1_EN | \
> +							 STARFIVE_LDO_D0_EN)
> +
> +/* REG: 0x1be */
> +#define STARFIVE_SERIALIER_CONTROL			0x1be
> +#define STARFIVE_SERIALIER_D2_EN			BIT(6)
> +#define STARFIVE_SERIALIER_D1_EN			BIT(5)
> +#define STARFIVE_SERIALIER_D0_EN			BIT(4)
> +#define STARFIVE_SERIALIER_EN				BIT(0)
> +
> +#define STARFIVE_SERIALIER_ENABLE			(STARFIVE_SERIALIER_D2_EN | \
> +							 STARFIVE_SERIALIER_D1_EN | \
> +							 STARFIVE_SERIALIER_D0_EN | \
> +							 STARFIVE_SERIALIER_EN)
> +
> +/* REG: 0x1cc */
> +#define STARFIVE_RX_CONTROL				0x1cc
> +#define STARFIVE_RX_EN					BIT(3)
> +#define STARFIVE_RX_CHANNEL_2_EN			BIT(2)
> +#define STARFIVE_RX_CHANNEL_1_EN			BIT(1)
> +#define STARFIVE_RX_CHANNEL_0_EN			BIT(0)
> +#define STARFIVE_RX_ENABLE				(STARFIVE_RX_EN | \
> +							 STARFIVE_RX_CHANNEL_2_EN | \
> +							 STARFIVE_RX_CHANNEL_1_EN | \
> +							 STARFIVE_RX_CHANNEL_0_EN)
> +
> +/* REG: 0x1d1 */
> +#define STARFIVE_PRE_PLL_FRAC_DIV_H			0x1d1
> +#define STARFIVE_PRE_PLL_FRAC_DIV_23_16(x)		UPDATE((x) >> 16, 7, 0)
> +/* REG: 0x1d2 */
> +#define STARFIVE_PRE_PLL_FRAC_DIV_M			0x1d2
> +#define STARFIVE_PRE_PLL_FRAC_DIV_15_8(x)		UPDATE((x) >> 8, 7, 0)
> +/* REG: 0x1d3 */
> +#define STARFIVE_PRE_PLL_FRAC_DIV_L			0x1d3
> +#define STARFIVE_PRE_PLL_FRAC_DIV_7_0(x)		UPDATE(x, 7, 0)
> +
> +struct pre_pll_config {
> +	unsigned long pixclock;
> +	unsigned long tmdsclock;
> +	u8 prediv;
> +	u16 fbdiv;
> +	u8 tmds_div_a;
> +	u8 tmds_div_b;
> +	u8 tmds_div_c;
> +	u8 pclk_div_a;
> +	u8 pclk_div_b;
> +	u8 pclk_div_c;
> +	u8 pclk_div_d;
> +	u8 vco_div_5_en;
> +	u32 fracdiv;
> +};
> +
> +struct post_pll_config {
> +	unsigned long tmdsclock;
> +	u8 prediv;
> +	u16 fbdiv;
> +	u8 postdiv;
> +	u8 post_div_en;
> +	u8 version;
> +};
> +
> +struct phy_config {
> +	unsigned long	tmdsclock;
> +	u8		regs[14];
> +};
> +
> +struct hdmi_data_info {
> +	int vic;
> +	bool sink_is_hdmi;
> +	bool sink_has_audio;
> +	unsigned int enc_in_format;
> +	unsigned int enc_out_format;
> +	unsigned int colorimetry;
> +};
> +
> +struct starfive_hdmi {
> +	struct device *dev;
> +	struct drm_device *drm_dev;
> +	struct drm_encoder	encoder;
> +	struct drm_connector	connector;
> +
> +	struct starfive_hdmi_i2c *i2c;
> +
> +	int irq;
> +	struct clk *sys_clk;
> +	struct clk *mclk;
> +	struct clk *bclk;
> +	struct reset_control *tx_rst;
> +	void __iomem *regs;
> +
> +	struct i2c_adapter *ddc;
> +
> +	unsigned long tmds_rate;
> +
> +	struct hdmi_data_info	hdmi_data;
> +	struct drm_display_mode previous_mode;
> +	const struct pre_pll_config	*pre_cfg;
> +	const struct post_pll_config	*post_cfg;
> +};
> +
> +#endif /* __STARFIVE_HDMI_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
> index 3cd533cfa..9669354bd 100644
> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> @@ -126,6 +126,11 @@ static const struct component_master_ops vs_drm_ops = {
>   static struct platform_driver *drm_sub_drivers[] = {
>   	&dc_platform_driver,
>   
> +	/* connector + encoder*/
> +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
> +	&starfive_hdmi_driver,
> +#endif
> +
>   };
>   
>   static struct component_match *vs_drm_match_add(struct device *dev)
> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
> index 369ad22d6..e90723419 100644
> --- a/drivers/gpu/drm/verisilicon/vs_drv.h
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.h
> @@ -24,4 +24,8 @@ to_vs_drm_private(const struct drm_device *dev)
>   	return container_of(dev, struct vs_drm_device, base);
>   }
>   
> +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
> +extern struct platform_driver starfive_hdmi_driver;
> +#endif
> +
>   #endif /* __VS_DRV_H__ */

-- 
With best wishes
Dmitry


^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
  2023-10-25 22:23     ` Dmitry Baryshkov
  (?)
@ 2023-10-26  8:07       ` Maxime Ripard
  -1 siblings, 0 replies; 117+ messages in thread
From: Maxime Ripard @ 2023-10-26  8:07 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig

[-- Attachment #1: Type: text/plain, Size: 1344 bytes --]

On Thu, Oct 26, 2023 at 01:23:53AM +0300, Dmitry Baryshkov wrote:
> > +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
> > +{
> > +	struct drm_encoder *encoder = &hdmi->encoder;
> > +	struct device *dev = hdmi->dev;
> > +
> > +	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
> > +
> > +	/*
> > +	 * If we failed to find the CRTC(s) which this encoder is
> > +	 * supposed to be connected to, it's because the CRTC has
> > +	 * not been registered yet.  Defer probing, and hope that
> > +	 * the required CRTC is added later.
> > +	 */
> > +	if (encoder->possible_crtcs == 0)
> > +		return -EPROBE_DEFER;
> > +
> > +	drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
> > +
> > +	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> > +
> > +	drm_connector_helper_add(&hdmi->connector,
> > +				 &starfive_hdmi_connector_helper_funcs);
> > +	drmm_connector_init(drm, &hdmi->connector,
> > +			    &starfive_hdmi_connector_funcs,
> > +			    DRM_MODE_CONNECTOR_HDMIA,
> 
> On an embedded device one can not be so sure. There can be MHL or HDMI
> Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.

On an HDMI driver, it's far from being a requirement, especially given
the limitations bridges have.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-10-26  8:07       ` Maxime Ripard
  0 siblings, 0 replies; 117+ messages in thread
From: Maxime Ripard @ 2023-10-26  8:07 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Emil Renner Berthing, dri-devel, Krzysztof Kozlowski,
	linux-riscv, Sumit Semwal, Shengyang Chen, linux-media,
	devicetree, Conor Dooley, Albert Ou, Jagan Teki, linaro-mm-sig,
	Rob Herring, Chris Morgan, Paul Walmsley, Keith Zhao,
	Bjorn Andersson, linux-kernel, christian.koenig, Jack Zhu,
	Palmer Dabbelt, Thomas Zimmermann, Shawn Guo, Changhuang Liang

[-- Attachment #1: Type: text/plain, Size: 1344 bytes --]

On Thu, Oct 26, 2023 at 01:23:53AM +0300, Dmitry Baryshkov wrote:
> > +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
> > +{
> > +	struct drm_encoder *encoder = &hdmi->encoder;
> > +	struct device *dev = hdmi->dev;
> > +
> > +	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
> > +
> > +	/*
> > +	 * If we failed to find the CRTC(s) which this encoder is
> > +	 * supposed to be connected to, it's because the CRTC has
> > +	 * not been registered yet.  Defer probing, and hope that
> > +	 * the required CRTC is added later.
> > +	 */
> > +	if (encoder->possible_crtcs == 0)
> > +		return -EPROBE_DEFER;
> > +
> > +	drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
> > +
> > +	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> > +
> > +	drm_connector_helper_add(&hdmi->connector,
> > +				 &starfive_hdmi_connector_helper_funcs);
> > +	drmm_connector_init(drm, &hdmi->connector,
> > +			    &starfive_hdmi_connector_funcs,
> > +			    DRM_MODE_CONNECTOR_HDMIA,
> 
> On an embedded device one can not be so sure. There can be MHL or HDMI
> Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.

On an HDMI driver, it's far from being a requirement, especially given
the limitations bridges have.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-10-26  8:07       ` Maxime Ripard
  0 siblings, 0 replies; 117+ messages in thread
From: Maxime Ripard @ 2023-10-26  8:07 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig


[-- Attachment #1.1: Type: text/plain, Size: 1344 bytes --]

On Thu, Oct 26, 2023 at 01:23:53AM +0300, Dmitry Baryshkov wrote:
> > +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
> > +{
> > +	struct drm_encoder *encoder = &hdmi->encoder;
> > +	struct device *dev = hdmi->dev;
> > +
> > +	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
> > +
> > +	/*
> > +	 * If we failed to find the CRTC(s) which this encoder is
> > +	 * supposed to be connected to, it's because the CRTC has
> > +	 * not been registered yet.  Defer probing, and hope that
> > +	 * the required CRTC is added later.
> > +	 */
> > +	if (encoder->possible_crtcs == 0)
> > +		return -EPROBE_DEFER;
> > +
> > +	drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
> > +
> > +	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> > +
> > +	drm_connector_helper_add(&hdmi->connector,
> > +				 &starfive_hdmi_connector_helper_funcs);
> > +	drmm_connector_init(drm, &hdmi->connector,
> > +			    &starfive_hdmi_connector_funcs,
> > +			    DRM_MODE_CONNECTOR_HDMIA,
> 
> On an embedded device one can not be so sure. There can be MHL or HDMI
> Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.

On an HDMI driver, it's far from being a requirement, especially given
the limitations bridges have.

Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 161 bytes --]

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
  2023-10-26  8:07       ` Maxime Ripard
  (?)
@ 2023-10-26  8:57         ` Dmitry Baryshkov
  -1 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-10-26  8:57 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig

On Thu, 26 Oct 2023 at 11:07, Maxime Ripard <mripard@kernel.org> wrote:
>
> On Thu, Oct 26, 2023 at 01:23:53AM +0300, Dmitry Baryshkov wrote:
> > > +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
> > > +{
> > > +   struct drm_encoder *encoder = &hdmi->encoder;
> > > +   struct device *dev = hdmi->dev;
> > > +
> > > +   encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
> > > +
> > > +   /*
> > > +    * If we failed to find the CRTC(s) which this encoder is
> > > +    * supposed to be connected to, it's because the CRTC has
> > > +    * not been registered yet.  Defer probing, and hope that
> > > +    * the required CRTC is added later.
> > > +    */
> > > +   if (encoder->possible_crtcs == 0)
> > > +           return -EPROBE_DEFER;
> > > +
> > > +   drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
> > > +
> > > +   hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> > > +
> > > +   drm_connector_helper_add(&hdmi->connector,
> > > +                            &starfive_hdmi_connector_helper_funcs);
> > > +   drmm_connector_init(drm, &hdmi->connector,
> > > +                       &starfive_hdmi_connector_funcs,
> > > +                       DRM_MODE_CONNECTOR_HDMIA,
> >
> > On an embedded device one can not be so sure. There can be MHL or HDMI
> > Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.
>
> On an HDMI driver, it's far from being a requirement, especially given
> the limitations bridges have.

It's a blessing that things like MHL / HDMI-in-USB-C / HDMI-to-MyDP
are not widely used in the wild and are mostly non-existing except
several phones that preate wide DP usage.
Using drm_connector directly prevents one from handling possible
modifications on the board level. For example, with the DRM connector
in place, handling a separate HPD GPIO will result in code duplication
from the hdmi-connector driver. Handling any other variations in the
board design (which are pretty common in the embedded world) will also
require changing the driver itself. drm_bridge / drm_bridge_connector
save us from those issues.

BTW: what are the limitations of the drm_bridge wrt. HDMI output? I'm
asking because we heavily depend on the bridge infrastructure for HDMI
output. Maybe we are missing something there, which went unnoticed to
me and my colleagues.

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-10-26  8:57         ` Dmitry Baryshkov
  0 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-10-26  8:57 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig

On Thu, 26 Oct 2023 at 11:07, Maxime Ripard <mripard@kernel.org> wrote:
>
> On Thu, Oct 26, 2023 at 01:23:53AM +0300, Dmitry Baryshkov wrote:
> > > +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
> > > +{
> > > +   struct drm_encoder *encoder = &hdmi->encoder;
> > > +   struct device *dev = hdmi->dev;
> > > +
> > > +   encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
> > > +
> > > +   /*
> > > +    * If we failed to find the CRTC(s) which this encoder is
> > > +    * supposed to be connected to, it's because the CRTC has
> > > +    * not been registered yet.  Defer probing, and hope that
> > > +    * the required CRTC is added later.
> > > +    */
> > > +   if (encoder->possible_crtcs == 0)
> > > +           return -EPROBE_DEFER;
> > > +
> > > +   drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
> > > +
> > > +   hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> > > +
> > > +   drm_connector_helper_add(&hdmi->connector,
> > > +                            &starfive_hdmi_connector_helper_funcs);
> > > +   drmm_connector_init(drm, &hdmi->connector,
> > > +                       &starfive_hdmi_connector_funcs,
> > > +                       DRM_MODE_CONNECTOR_HDMIA,
> >
> > On an embedded device one can not be so sure. There can be MHL or HDMI
> > Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.
>
> On an HDMI driver, it's far from being a requirement, especially given
> the limitations bridges have.

It's a blessing that things like MHL / HDMI-in-USB-C / HDMI-to-MyDP
are not widely used in the wild and are mostly non-existing except
several phones that preate wide DP usage.
Using drm_connector directly prevents one from handling possible
modifications on the board level. For example, with the DRM connector
in place, handling a separate HPD GPIO will result in code duplication
from the hdmi-connector driver. Handling any other variations in the
board design (which are pretty common in the embedded world) will also
require changing the driver itself. drm_bridge / drm_bridge_connector
save us from those issues.

BTW: what are the limitations of the drm_bridge wrt. HDMI output? I'm
asking because we heavily depend on the bridge infrastructure for HDMI
output. Maybe we are missing something there, which went unnoticed to
me and my colleagues.

-- 
With best wishes
Dmitry

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-10-26  8:57         ` Dmitry Baryshkov
  0 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-10-26  8:57 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Emil Renner Berthing, dri-devel, Krzysztof Kozlowski,
	linux-riscv, Sumit Semwal, Shengyang Chen, linux-media,
	devicetree, Conor Dooley, Albert Ou, Jagan Teki, linaro-mm-sig,
	Rob Herring, Chris Morgan, Paul Walmsley, Keith Zhao,
	Bjorn Andersson, linux-kernel, christian.koenig, Jack Zhu,
	Palmer Dabbelt, Thomas Zimmermann, Shawn Guo, Changhuang Liang

On Thu, 26 Oct 2023 at 11:07, Maxime Ripard <mripard@kernel.org> wrote:
>
> On Thu, Oct 26, 2023 at 01:23:53AM +0300, Dmitry Baryshkov wrote:
> > > +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
> > > +{
> > > +   struct drm_encoder *encoder = &hdmi->encoder;
> > > +   struct device *dev = hdmi->dev;
> > > +
> > > +   encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
> > > +
> > > +   /*
> > > +    * If we failed to find the CRTC(s) which this encoder is
> > > +    * supposed to be connected to, it's because the CRTC has
> > > +    * not been registered yet.  Defer probing, and hope that
> > > +    * the required CRTC is added later.
> > > +    */
> > > +   if (encoder->possible_crtcs == 0)
> > > +           return -EPROBE_DEFER;
> > > +
> > > +   drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
> > > +
> > > +   hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> > > +
> > > +   drm_connector_helper_add(&hdmi->connector,
> > > +                            &starfive_hdmi_connector_helper_funcs);
> > > +   drmm_connector_init(drm, &hdmi->connector,
> > > +                       &starfive_hdmi_connector_funcs,
> > > +                       DRM_MODE_CONNECTOR_HDMIA,
> >
> > On an embedded device one can not be so sure. There can be MHL or HDMI
> > Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.
>
> On an HDMI driver, it's far from being a requirement, especially given
> the limitations bridges have.

It's a blessing that things like MHL / HDMI-in-USB-C / HDMI-to-MyDP
are not widely used in the wild and are mostly non-existing except
several phones that preate wide DP usage.
Using drm_connector directly prevents one from handling possible
modifications on the board level. For example, with the DRM connector
in place, handling a separate HPD GPIO will result in code duplication
from the hdmi-connector driver. Handling any other variations in the
board design (which are pretty common in the embedded world) will also
require changing the driver itself. drm_bridge / drm_bridge_connector
save us from those issues.

BTW: what are the limitations of the drm_bridge wrt. HDMI output? I'm
asking because we heavily depend on the bridge infrastructure for HDMI
output. Maybe we are missing something there, which went unnoticed to
me and my colleagues.

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
  2023-10-25 19:49       ` Ville Syrjälä
  (?)
@ 2023-10-26  9:42         ` Keith Zhao
  -1 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-26  9:42 UTC (permalink / raw)
  To: Ville Syrjälä, Dmitry Baryshkov
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, Conor Dooley, Albert Ou, Emil Renner Berthing,
	christian.koenig, Thomas Zimmermann, Bjorn Andersson,
	Chris Morgan, Maxime Ripard, Jagan Teki, Jack Zhu, Rob Herring,
	Palmer Dabbelt, Krzysztof Kozlowski, Paul Walmsley,
	Shengyang Chen, Changhuang Liang, Shawn Guo, Sumit Semwal

hi Ville:
very glad to receive your feedback
Some of them are very good ideas.
Some are not very clear and hope to get your further reply!


On 2023/10/26 3:49, Ville Syrjälä wrote:
> On Wed, Oct 25, 2023 at 10:28:56PM +0300, Dmitry Baryshkov wrote:
>> On 25/10/2023 13:39, Keith Zhao wrote:
>> > add 2 crtcs and 8 planes in vs-drm
>> > 
>> > Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>> > ---
>> >   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
>> >   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
>> >   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
>> >   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
>> >   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
>> >   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
>> >   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
>> >   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
>> >   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
>> >   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
>> >   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
>> >   11 files changed, 4494 insertions(+), 2 deletions(-)
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
>> > 
>> > diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
>> > index 7d3be305b..1d48016ca 100644
>> > --- a/drivers/gpu/drm/verisilicon/Makefile
>> > +++ b/drivers/gpu/drm/verisilicon/Makefile
>> > @@ -1,7 +1,11 @@
>> >   # SPDX-License-Identifier: GPL-2.0
>> >   
>> > -vs_drm-objs := vs_drv.o \
>> > -		vs_modeset.o
>> > +vs_drm-objs := vs_dc_hw.o \
>> > +		vs_dc.o \
>> > +		vs_crtc.o \
>> > +		vs_drv.o \
>> > +		vs_modeset.o \
>> > +		vs_plane.o
>> >   
>> >   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>> >   
>> > diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> > new file mode 100644
>> > index 000000000..8a658ea77
>> > --- /dev/null
>> > +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> > @@ -0,0 +1,257 @@
>> > +// SPDX-License-Identifier: GPL-2.0
>> > +/*
>> > + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> > + *
>> > + */
>> > +
>> > +#include <linux/clk.h>
>> > +#include <linux/debugfs.h>
>> > +#include <linux/media-bus-format.h>
>> > +
>> > +#include <drm/drm_atomic_helper.h>
>> > +#include <drm/drm_atomic.h>
>> > +#include <drm/drm_crtc.h>
>> > +#include <drm/drm_gem_atomic_helper.h>
>> > +#include <drm/drm_vblank.h>
>> > +#include <drm/vs_drm.h>
>> > +
>> > +#include "vs_crtc.h"
>> > +#include "vs_dc.h"
>> > +#include "vs_drv.h"
>> > +
>> > +static void vs_crtc_reset(struct drm_crtc *crtc)
>> > +{
>> > +	struct vs_crtc_state *state;
>> > +
>> > +	if (crtc->state) {
>> > +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
>> > +
>> > +		state = to_vs_crtc_state(crtc->state);
>> > +		kfree(state);
>> > +		crtc->state = NULL;
>> > +	}
>> 
>> You can call your crtc_destroy_state function directly here.
ok got it !
>> 
>> > +
>> > +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> > +	if (!state)
>> > +		return;
>> > +
>> > +	__drm_atomic_helper_crtc_reset(crtc, &state->base);
>> > +}
>> > +
>> > +static struct drm_crtc_state *
>> > +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
>> > +{
>> > +	struct vs_crtc_state *ori_state;
>> 
>> It might be a matter of taste, but it is usually old_state.
>> 
>> > +	struct vs_crtc_state *state;
>> > +
>> > +	if (!crtc->state)
>> > +		return NULL;
>> > +
>> > +	ori_state = to_vs_crtc_state(crtc->state);
>> > +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> > +	if (!state)
>> > +		return NULL;
>> > +
>> > +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
>> > +
>> > +	state->output_fmt = ori_state->output_fmt;
>> > +	state->encoder_type = ori_state->encoder_type;
>> > +	state->bpp = ori_state->bpp;
>> > +	state->underflow = ori_state->underflow;
>> 
>> Can you use kmemdup instead?
ok 
>> 
>> > +
>> > +	return &state->base;
>> > +}
>> > +
>> > +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
>> > +					 struct drm_crtc_state *state)
>> > +{
>> > +	__drm_atomic_helper_crtc_destroy_state(state);
>> > +	kfree(to_vs_crtc_state(state));
>> > +}
>> > +
>> > +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
>> > +{
>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> > +
>> > +	vs_dc_enable_vblank(dc, true);
>> > +
>> > +	return 0;
>> > +}
>> > +
>> > +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
>> > +{
>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> > +
>> > +	vs_dc_enable_vblank(dc, false);
>> > +}
>> > +
>> > +static const struct drm_crtc_funcs vs_crtc_funcs = {
>> > +	.set_config		= drm_atomic_helper_set_config,
>> > +	.page_flip		= drm_atomic_helper_page_flip,
>> 
>> destroy is required, see drm_mode_config_cleanup()
will add destory 
>> 
>> > +	.reset			= vs_crtc_reset,
>> > +	.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
>> > +	.atomic_destroy_state	= vs_crtc_atomic_destroy_state,
>> 
>> please consider adding atomic_print_state to output driver-specific bits.
>> 
will add atomic_print_state  to print my private definitions
>> > +	.enable_vblank		= vs_crtc_enable_vblank,
>> > +	.disable_vblank		= vs_crtc_disable_vblank,
>> > +};
>> > +
>> > +static u8 cal_pixel_bits(u32 bus_format)
>> 
>> This looks like a generic helper code, which can go to a common place.
>> 
>> > +{
>> > +	u8 bpp;
>> > +
>> > +	switch (bus_format) {
>> > +	case MEDIA_BUS_FMT_RGB565_1X16:
>> > +	case MEDIA_BUS_FMT_UYVY8_1X16:
>> > +		bpp = 16;
>> > +		break;
>> > +	case MEDIA_BUS_FMT_RGB666_1X18:
>> > +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
>> > +		bpp = 18;
>> > +		break;
>> > +	case MEDIA_BUS_FMT_UYVY10_1X20:
>> > +		bpp = 20;
>> > +		break;
>> > +	case MEDIA_BUS_FMT_BGR888_1X24:
>> > +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
>> > +	case MEDIA_BUS_FMT_YUV8_1X24:
>> > +		bpp = 24;
>> > +		break;
>> > +	case MEDIA_BUS_FMT_RGB101010_1X30:
>> > +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
>> > +	case MEDIA_BUS_FMT_YUV10_1X30:
>> > +		bpp = 30;
>> > +		break;
>> > +	default:
>> > +		bpp = 24;
>> > +		break;
>> > +	}
>> > +
>> > +	return bpp;
>> > +}
>> > +
>> > +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
>> > +				  struct drm_atomic_state *state)
>> > +{
>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> > +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
>> > +
>> > +	vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
>> > +
>> > +	vs_dc_enable(dc, crtc);
>> > +	drm_crtc_vblank_on(crtc);
>> > +}
>> > +
>> > +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
>> > +				   struct drm_atomic_state *state)
>> > +{
>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> > +
>> > +	drm_crtc_vblank_off(crtc);
>> > +
>> > +	vs_dc_disable(dc, crtc);
>> > +
>> > +	if (crtc->state->event && !crtc->state->active) {
>> > +		spin_lock_irq(&crtc->dev->event_lock);
>> > +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
>> > +		spin_unlock_irq(&crtc->dev->event_lock);
>> > +
>> > +		crtc->state->event = NULL;
>> 
>> I think even should be cleared within the lock.
> 
> event_lock doesn't protect anything in the crtc state.

how about match like this:
spin_lock_irq(&crtc->dev->event_lock);
if (crtc->state->event) {
	drm_crtc_send_vblank_event(crtc, crtc->state->event);
	crtc->state->event = NULL;
}
spin_unlock_irq(&crtc->dev->event_lock);

> 
> But the bigger problem in this code is the prevalent crtc->state
> usage. That should pretty much never be done, especially in anything
> that can get called from the actual commit phase where you no longer
> have the locks held. Instead one should almost always use the
> get_{new,old}_state() stuff, or the old/new/oldnew state iterators.

the prevalent crtc->state usage :
crtc->state should not be used directly?
need replace it by get_{new,old}_state()

for example:
drm_crtc_send_vblank_event(crtc, crtc->state->event);


should do like this :
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
...
drm_crtc_send_vblank_event(crtc, crtc_state->event);
...

If there is a problem, help further correct
wish give a example
Thanks

> 
>> 
>> > +	}
>> > +}
>> > +
>> > +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
>> > +				 struct drm_atomic_state *state)
>> > +{
>> > +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
>> > +									  crtc);
>> > +
>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> > +	struct device *dev = vs_crtc->dev;
>> > +	struct drm_property_blob *blob = crtc->state->gamma_lut;
> 
> Eg. here you are using drm_atomic_get_new_crtc_state() correctly, but
> then proceed to directly access crtc->state anyway.
> 
struct drm_property_blob *blob = crtc->state->gamma_lut;
change to 
struct drm_property_blob *blob = crtc_state ->gamma_lut;

>> > +	struct drm_color_lut *lut;
>> > +	struct vs_dc *dc = dev_get_drvdata(dev);
>> > +
>> > +	if (crtc_state->color_mgmt_changed) {
>> > +		if (blob && blob->length) {
>> > +			lut = blob->data;
>> > +			vs_dc_set_gamma(dc, crtc, lut,
>> > +					blob->length / sizeof(*lut));
>> > +			vs_dc_enable_gamma(dc, crtc, true);
>> > +		} else {
>> > +			vs_dc_enable_gamma(dc, crtc, false);
>> > +		}
>> > +	}
>> > +}
> 

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-10-26  9:42         ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-26  9:42 UTC (permalink / raw)
  To: Ville Syrjälä, Dmitry Baryshkov
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, Conor Dooley, Albert Ou, Emil Renner Berthing,
	christian.koenig, Thomas Zimmermann, Bjorn Andersson,
	Chris Morgan, Maxime Ripard, Jagan Teki, Jack Zhu, Rob Herring,
	Palmer Dabbelt, Krzysztof Kozlowski, Paul Walmsley,
	Shengyang Chen, Changhuang Liang, Shawn Guo, Sumit Semwal

hi Ville:
very glad to receive your feedback
Some of them are very good ideas.
Some are not very clear and hope to get your further reply!


On 2023/10/26 3:49, Ville Syrjälä wrote:
> On Wed, Oct 25, 2023 at 10:28:56PM +0300, Dmitry Baryshkov wrote:
>> On 25/10/2023 13:39, Keith Zhao wrote:
>> > add 2 crtcs and 8 planes in vs-drm
>> > 
>> > Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>> > ---
>> >   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
>> >   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
>> >   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
>> >   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
>> >   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
>> >   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
>> >   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
>> >   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
>> >   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
>> >   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
>> >   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
>> >   11 files changed, 4494 insertions(+), 2 deletions(-)
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
>> > 
>> > diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
>> > index 7d3be305b..1d48016ca 100644
>> > --- a/drivers/gpu/drm/verisilicon/Makefile
>> > +++ b/drivers/gpu/drm/verisilicon/Makefile
>> > @@ -1,7 +1,11 @@
>> >   # SPDX-License-Identifier: GPL-2.0
>> >   
>> > -vs_drm-objs := vs_drv.o \
>> > -		vs_modeset.o
>> > +vs_drm-objs := vs_dc_hw.o \
>> > +		vs_dc.o \
>> > +		vs_crtc.o \
>> > +		vs_drv.o \
>> > +		vs_modeset.o \
>> > +		vs_plane.o
>> >   
>> >   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>> >   
>> > diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> > new file mode 100644
>> > index 000000000..8a658ea77
>> > --- /dev/null
>> > +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> > @@ -0,0 +1,257 @@
>> > +// SPDX-License-Identifier: GPL-2.0
>> > +/*
>> > + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> > + *
>> > + */
>> > +
>> > +#include <linux/clk.h>
>> > +#include <linux/debugfs.h>
>> > +#include <linux/media-bus-format.h>
>> > +
>> > +#include <drm/drm_atomic_helper.h>
>> > +#include <drm/drm_atomic.h>
>> > +#include <drm/drm_crtc.h>
>> > +#include <drm/drm_gem_atomic_helper.h>
>> > +#include <drm/drm_vblank.h>
>> > +#include <drm/vs_drm.h>
>> > +
>> > +#include "vs_crtc.h"
>> > +#include "vs_dc.h"
>> > +#include "vs_drv.h"
>> > +
>> > +static void vs_crtc_reset(struct drm_crtc *crtc)
>> > +{
>> > +	struct vs_crtc_state *state;
>> > +
>> > +	if (crtc->state) {
>> > +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
>> > +
>> > +		state = to_vs_crtc_state(crtc->state);
>> > +		kfree(state);
>> > +		crtc->state = NULL;
>> > +	}
>> 
>> You can call your crtc_destroy_state function directly here.
ok got it !
>> 
>> > +
>> > +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> > +	if (!state)
>> > +		return;
>> > +
>> > +	__drm_atomic_helper_crtc_reset(crtc, &state->base);
>> > +}
>> > +
>> > +static struct drm_crtc_state *
>> > +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
>> > +{
>> > +	struct vs_crtc_state *ori_state;
>> 
>> It might be a matter of taste, but it is usually old_state.
>> 
>> > +	struct vs_crtc_state *state;
>> > +
>> > +	if (!crtc->state)
>> > +		return NULL;
>> > +
>> > +	ori_state = to_vs_crtc_state(crtc->state);
>> > +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> > +	if (!state)
>> > +		return NULL;
>> > +
>> > +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
>> > +
>> > +	state->output_fmt = ori_state->output_fmt;
>> > +	state->encoder_type = ori_state->encoder_type;
>> > +	state->bpp = ori_state->bpp;
>> > +	state->underflow = ori_state->underflow;
>> 
>> Can you use kmemdup instead?
ok 
>> 
>> > +
>> > +	return &state->base;
>> > +}
>> > +
>> > +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
>> > +					 struct drm_crtc_state *state)
>> > +{
>> > +	__drm_atomic_helper_crtc_destroy_state(state);
>> > +	kfree(to_vs_crtc_state(state));
>> > +}
>> > +
>> > +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
>> > +{
>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> > +
>> > +	vs_dc_enable_vblank(dc, true);
>> > +
>> > +	return 0;
>> > +}
>> > +
>> > +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
>> > +{
>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> > +
>> > +	vs_dc_enable_vblank(dc, false);
>> > +}
>> > +
>> > +static const struct drm_crtc_funcs vs_crtc_funcs = {
>> > +	.set_config		= drm_atomic_helper_set_config,
>> > +	.page_flip		= drm_atomic_helper_page_flip,
>> 
>> destroy is required, see drm_mode_config_cleanup()
will add destory 
>> 
>> > +	.reset			= vs_crtc_reset,
>> > +	.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
>> > +	.atomic_destroy_state	= vs_crtc_atomic_destroy_state,
>> 
>> please consider adding atomic_print_state to output driver-specific bits.
>> 
will add atomic_print_state  to print my private definitions
>> > +	.enable_vblank		= vs_crtc_enable_vblank,
>> > +	.disable_vblank		= vs_crtc_disable_vblank,
>> > +};
>> > +
>> > +static u8 cal_pixel_bits(u32 bus_format)
>> 
>> This looks like a generic helper code, which can go to a common place.
>> 
>> > +{
>> > +	u8 bpp;
>> > +
>> > +	switch (bus_format) {
>> > +	case MEDIA_BUS_FMT_RGB565_1X16:
>> > +	case MEDIA_BUS_FMT_UYVY8_1X16:
>> > +		bpp = 16;
>> > +		break;
>> > +	case MEDIA_BUS_FMT_RGB666_1X18:
>> > +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
>> > +		bpp = 18;
>> > +		break;
>> > +	case MEDIA_BUS_FMT_UYVY10_1X20:
>> > +		bpp = 20;
>> > +		break;
>> > +	case MEDIA_BUS_FMT_BGR888_1X24:
>> > +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
>> > +	case MEDIA_BUS_FMT_YUV8_1X24:
>> > +		bpp = 24;
>> > +		break;
>> > +	case MEDIA_BUS_FMT_RGB101010_1X30:
>> > +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
>> > +	case MEDIA_BUS_FMT_YUV10_1X30:
>> > +		bpp = 30;
>> > +		break;
>> > +	default:
>> > +		bpp = 24;
>> > +		break;
>> > +	}
>> > +
>> > +	return bpp;
>> > +}
>> > +
>> > +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
>> > +				  struct drm_atomic_state *state)
>> > +{
>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> > +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
>> > +
>> > +	vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
>> > +
>> > +	vs_dc_enable(dc, crtc);
>> > +	drm_crtc_vblank_on(crtc);
>> > +}
>> > +
>> > +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
>> > +				   struct drm_atomic_state *state)
>> > +{
>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> > +
>> > +	drm_crtc_vblank_off(crtc);
>> > +
>> > +	vs_dc_disable(dc, crtc);
>> > +
>> > +	if (crtc->state->event && !crtc->state->active) {
>> > +		spin_lock_irq(&crtc->dev->event_lock);
>> > +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
>> > +		spin_unlock_irq(&crtc->dev->event_lock);
>> > +
>> > +		crtc->state->event = NULL;
>> 
>> I think even should be cleared within the lock.
> 
> event_lock doesn't protect anything in the crtc state.

how about match like this:
spin_lock_irq(&crtc->dev->event_lock);
if (crtc->state->event) {
	drm_crtc_send_vblank_event(crtc, crtc->state->event);
	crtc->state->event = NULL;
}
spin_unlock_irq(&crtc->dev->event_lock);

> 
> But the bigger problem in this code is the prevalent crtc->state
> usage. That should pretty much never be done, especially in anything
> that can get called from the actual commit phase where you no longer
> have the locks held. Instead one should almost always use the
> get_{new,old}_state() stuff, or the old/new/oldnew state iterators.

the prevalent crtc->state usage :
crtc->state should not be used directly?
need replace it by get_{new,old}_state()

for example:
drm_crtc_send_vblank_event(crtc, crtc->state->event);


should do like this :
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
...
drm_crtc_send_vblank_event(crtc, crtc_state->event);
...

If there is a problem, help further correct
wish give a example
Thanks

> 
>> 
>> > +	}
>> > +}
>> > +
>> > +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
>> > +				 struct drm_atomic_state *state)
>> > +{
>> > +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
>> > +									  crtc);
>> > +
>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> > +	struct device *dev = vs_crtc->dev;
>> > +	struct drm_property_blob *blob = crtc->state->gamma_lut;
> 
> Eg. here you are using drm_atomic_get_new_crtc_state() correctly, but
> then proceed to directly access crtc->state anyway.
> 
struct drm_property_blob *blob = crtc->state->gamma_lut;
change to 
struct drm_property_blob *blob = crtc_state ->gamma_lut;

>> > +	struct drm_color_lut *lut;
>> > +	struct vs_dc *dc = dev_get_drvdata(dev);
>> > +
>> > +	if (crtc_state->color_mgmt_changed) {
>> > +		if (blob && blob->length) {
>> > +			lut = blob->data;
>> > +			vs_dc_set_gamma(dc, crtc, lut,
>> > +					blob->length / sizeof(*lut));
>> > +			vs_dc_enable_gamma(dc, crtc, true);
>> > +		} else {
>> > +			vs_dc_enable_gamma(dc, crtc, false);
>> > +		}
>> > +	}
>> > +}
> 

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-10-26  9:42         ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-26  9:42 UTC (permalink / raw)
  To: Ville Syrjälä, Dmitry Baryshkov
  Cc: Emil Renner Berthing, dri-devel, Krzysztof Kozlowski,
	linux-riscv, Sumit Semwal, Shengyang Chen, linux-media,
	devicetree, Conor Dooley, Albert Ou, Maxime Ripard, Jagan Teki,
	linaro-mm-sig, Rob Herring, Chris Morgan, Paul Walmsley,
	Bjorn Andersson, linux-kernel, christian.koenig, Jack Zhu,
	Palmer Dabbelt, Thomas Zimmermann, Shawn Guo, Changhuang Liang

hi Ville:
very glad to receive your feedback
Some of them are very good ideas.
Some are not very clear and hope to get your further reply!


On 2023/10/26 3:49, Ville Syrjälä wrote:
> On Wed, Oct 25, 2023 at 10:28:56PM +0300, Dmitry Baryshkov wrote:
>> On 25/10/2023 13:39, Keith Zhao wrote:
>> > add 2 crtcs and 8 planes in vs-drm
>> > 
>> > Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>> > ---
>> >   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
>> >   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
>> >   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
>> >   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
>> >   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
>> >   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
>> >   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
>> >   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
>> >   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
>> >   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
>> >   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
>> >   11 files changed, 4494 insertions(+), 2 deletions(-)
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
>> > 
>> > diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
>> > index 7d3be305b..1d48016ca 100644
>> > --- a/drivers/gpu/drm/verisilicon/Makefile
>> > +++ b/drivers/gpu/drm/verisilicon/Makefile
>> > @@ -1,7 +1,11 @@
>> >   # SPDX-License-Identifier: GPL-2.0
>> >   
>> > -vs_drm-objs := vs_drv.o \
>> > -		vs_modeset.o
>> > +vs_drm-objs := vs_dc_hw.o \
>> > +		vs_dc.o \
>> > +		vs_crtc.o \
>> > +		vs_drv.o \
>> > +		vs_modeset.o \
>> > +		vs_plane.o
>> >   
>> >   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>> >   
>> > diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> > new file mode 100644
>> > index 000000000..8a658ea77
>> > --- /dev/null
>> > +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> > @@ -0,0 +1,257 @@
>> > +// SPDX-License-Identifier: GPL-2.0
>> > +/*
>> > + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> > + *
>> > + */
>> > +
>> > +#include <linux/clk.h>
>> > +#include <linux/debugfs.h>
>> > +#include <linux/media-bus-format.h>
>> > +
>> > +#include <drm/drm_atomic_helper.h>
>> > +#include <drm/drm_atomic.h>
>> > +#include <drm/drm_crtc.h>
>> > +#include <drm/drm_gem_atomic_helper.h>
>> > +#include <drm/drm_vblank.h>
>> > +#include <drm/vs_drm.h>
>> > +
>> > +#include "vs_crtc.h"
>> > +#include "vs_dc.h"
>> > +#include "vs_drv.h"
>> > +
>> > +static void vs_crtc_reset(struct drm_crtc *crtc)
>> > +{
>> > +	struct vs_crtc_state *state;
>> > +
>> > +	if (crtc->state) {
>> > +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
>> > +
>> > +		state = to_vs_crtc_state(crtc->state);
>> > +		kfree(state);
>> > +		crtc->state = NULL;
>> > +	}
>> 
>> You can call your crtc_destroy_state function directly here.
ok got it !
>> 
>> > +
>> > +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> > +	if (!state)
>> > +		return;
>> > +
>> > +	__drm_atomic_helper_crtc_reset(crtc, &state->base);
>> > +}
>> > +
>> > +static struct drm_crtc_state *
>> > +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
>> > +{
>> > +	struct vs_crtc_state *ori_state;
>> 
>> It might be a matter of taste, but it is usually old_state.
>> 
>> > +	struct vs_crtc_state *state;
>> > +
>> > +	if (!crtc->state)
>> > +		return NULL;
>> > +
>> > +	ori_state = to_vs_crtc_state(crtc->state);
>> > +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> > +	if (!state)
>> > +		return NULL;
>> > +
>> > +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
>> > +
>> > +	state->output_fmt = ori_state->output_fmt;
>> > +	state->encoder_type = ori_state->encoder_type;
>> > +	state->bpp = ori_state->bpp;
>> > +	state->underflow = ori_state->underflow;
>> 
>> Can you use kmemdup instead?
ok 
>> 
>> > +
>> > +	return &state->base;
>> > +}
>> > +
>> > +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
>> > +					 struct drm_crtc_state *state)
>> > +{
>> > +	__drm_atomic_helper_crtc_destroy_state(state);
>> > +	kfree(to_vs_crtc_state(state));
>> > +}
>> > +
>> > +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
>> > +{
>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> > +
>> > +	vs_dc_enable_vblank(dc, true);
>> > +
>> > +	return 0;
>> > +}
>> > +
>> > +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
>> > +{
>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> > +
>> > +	vs_dc_enable_vblank(dc, false);
>> > +}
>> > +
>> > +static const struct drm_crtc_funcs vs_crtc_funcs = {
>> > +	.set_config		= drm_atomic_helper_set_config,
>> > +	.page_flip		= drm_atomic_helper_page_flip,
>> 
>> destroy is required, see drm_mode_config_cleanup()
will add destory 
>> 
>> > +	.reset			= vs_crtc_reset,
>> > +	.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
>> > +	.atomic_destroy_state	= vs_crtc_atomic_destroy_state,
>> 
>> please consider adding atomic_print_state to output driver-specific bits.
>> 
will add atomic_print_state  to print my private definitions
>> > +	.enable_vblank		= vs_crtc_enable_vblank,
>> > +	.disable_vblank		= vs_crtc_disable_vblank,
>> > +};
>> > +
>> > +static u8 cal_pixel_bits(u32 bus_format)
>> 
>> This looks like a generic helper code, which can go to a common place.
>> 
>> > +{
>> > +	u8 bpp;
>> > +
>> > +	switch (bus_format) {
>> > +	case MEDIA_BUS_FMT_RGB565_1X16:
>> > +	case MEDIA_BUS_FMT_UYVY8_1X16:
>> > +		bpp = 16;
>> > +		break;
>> > +	case MEDIA_BUS_FMT_RGB666_1X18:
>> > +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
>> > +		bpp = 18;
>> > +		break;
>> > +	case MEDIA_BUS_FMT_UYVY10_1X20:
>> > +		bpp = 20;
>> > +		break;
>> > +	case MEDIA_BUS_FMT_BGR888_1X24:
>> > +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
>> > +	case MEDIA_BUS_FMT_YUV8_1X24:
>> > +		bpp = 24;
>> > +		break;
>> > +	case MEDIA_BUS_FMT_RGB101010_1X30:
>> > +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
>> > +	case MEDIA_BUS_FMT_YUV10_1X30:
>> > +		bpp = 30;
>> > +		break;
>> > +	default:
>> > +		bpp = 24;
>> > +		break;
>> > +	}
>> > +
>> > +	return bpp;
>> > +}
>> > +
>> > +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
>> > +				  struct drm_atomic_state *state)
>> > +{
>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> > +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
>> > +
>> > +	vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
>> > +
>> > +	vs_dc_enable(dc, crtc);
>> > +	drm_crtc_vblank_on(crtc);
>> > +}
>> > +
>> > +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
>> > +				   struct drm_atomic_state *state)
>> > +{
>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> > +
>> > +	drm_crtc_vblank_off(crtc);
>> > +
>> > +	vs_dc_disable(dc, crtc);
>> > +
>> > +	if (crtc->state->event && !crtc->state->active) {
>> > +		spin_lock_irq(&crtc->dev->event_lock);
>> > +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
>> > +		spin_unlock_irq(&crtc->dev->event_lock);
>> > +
>> > +		crtc->state->event = NULL;
>> 
>> I think even should be cleared within the lock.
> 
> event_lock doesn't protect anything in the crtc state.

how about match like this:
spin_lock_irq(&crtc->dev->event_lock);
if (crtc->state->event) {
	drm_crtc_send_vblank_event(crtc, crtc->state->event);
	crtc->state->event = NULL;
}
spin_unlock_irq(&crtc->dev->event_lock);

> 
> But the bigger problem in this code is the prevalent crtc->state
> usage. That should pretty much never be done, especially in anything
> that can get called from the actual commit phase where you no longer
> have the locks held. Instead one should almost always use the
> get_{new,old}_state() stuff, or the old/new/oldnew state iterators.

the prevalent crtc->state usage :
crtc->state should not be used directly?
need replace it by get_{new,old}_state()

for example:
drm_crtc_send_vblank_event(crtc, crtc->state->event);


should do like this :
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
...
drm_crtc_send_vblank_event(crtc, crtc_state->event);
...

If there is a problem, help further correct
wish give a example
Thanks

> 
>> 
>> > +	}
>> > +}
>> > +
>> > +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
>> > +				 struct drm_atomic_state *state)
>> > +{
>> > +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
>> > +									  crtc);
>> > +
>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> > +	struct device *dev = vs_crtc->dev;
>> > +	struct drm_property_blob *blob = crtc->state->gamma_lut;
> 
> Eg. here you are using drm_atomic_get_new_crtc_state() correctly, but
> then proceed to directly access crtc->state anyway.
> 
struct drm_property_blob *blob = crtc->state->gamma_lut;
change to 
struct drm_property_blob *blob = crtc_state ->gamma_lut;

>> > +	struct drm_color_lut *lut;
>> > +	struct vs_dc *dc = dev_get_drvdata(dev);
>> > +
>> > +	if (crtc_state->color_mgmt_changed) {
>> > +		if (blob && blob->length) {
>> > +			lut = blob->data;
>> > +			vs_dc_set_gamma(dc, crtc, lut,
>> > +					blob->length / sizeof(*lut));
>> > +			vs_dc_enable_gamma(dc, crtc, true);
>> > +		} else {
>> > +			vs_dc_enable_gamma(dc, crtc, false);
>> > +		}
>> > +	}
>> > +}
> 

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
  2023-10-26  9:42         ` Keith Zhao
  (?)
@ 2023-10-26 10:09           ` Keith Zhao
  -1 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-26 10:09 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, Conor Dooley, Albert Ou, Emil Renner Berthing,
	christian.koenig, Thomas Zimmermann, Bjorn Andersson,
	Chris Morgan, Maxime Ripard, Jagan Teki, Jack Zhu, Rob Herring,
	Palmer Dabbelt, Krzysztof Kozlowski, Paul Walmsley,
	Shengyang Chen, Changhuang Liang, Shawn Guo, Sumit Semwal

sorry 
Dmitry ,accidentally wrote the wrong name
Take no offense

On 2023/10/26 17:42, Keith Zhao wrote:
> hi Ville:
> very glad to receive your feedback
> Some of them are very good ideas.
> Some are not very clear and hope to get your further reply!
> 
> 
> On 2023/10/26 3:49, Ville Syrjälä wrote:
>> On Wed, Oct 25, 2023 at 10:28:56PM +0300, Dmitry Baryshkov wrote:
>>> On 25/10/2023 13:39, Keith Zhao wrote:
>>> > add 2 crtcs and 8 planes in vs-drm
>>> > 
>>> > Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>>> > ---
>>> >   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
>>> >   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
>>> >   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
>>> >   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
>>> >   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
>>> >   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
>>> >   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
>>> >   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
>>> >   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
>>> >   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
>>> >   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
>>> >   11 files changed, 4494 insertions(+), 2 deletions(-)
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
>>> > 
>>> > diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
>>> > index 7d3be305b..1d48016ca 100644
>>> > --- a/drivers/gpu/drm/verisilicon/Makefile
>>> > +++ b/drivers/gpu/drm/verisilicon/Makefile
>>> > @@ -1,7 +1,11 @@
>>> >   # SPDX-License-Identifier: GPL-2.0
>>> >   
>>> > -vs_drm-objs := vs_drv.o \
>>> > -		vs_modeset.o
>>> > +vs_drm-objs := vs_dc_hw.o \
>>> > +		vs_dc.o \
>>> > +		vs_crtc.o \
>>> > +		vs_drv.o \
>>> > +		vs_modeset.o \
>>> > +		vs_plane.o
>>> >   
>>> >   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>>> >   
>>> > diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
>>> > new file mode 100644
>>> > index 000000000..8a658ea77
>>> > --- /dev/null
>>> > +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
>>> > @@ -0,0 +1,257 @@
>>> > +// SPDX-License-Identifier: GPL-2.0
>>> > +/*
>>> > + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>>> > + *
>>> > + */
>>> > +
>>> > +#include <linux/clk.h>
>>> > +#include <linux/debugfs.h>
>>> > +#include <linux/media-bus-format.h>
>>> > +
>>> > +#include <drm/drm_atomic_helper.h>
>>> > +#include <drm/drm_atomic.h>
>>> > +#include <drm/drm_crtc.h>
>>> > +#include <drm/drm_gem_atomic_helper.h>
>>> > +#include <drm/drm_vblank.h>
>>> > +#include <drm/vs_drm.h>
>>> > +
>>> > +#include "vs_crtc.h"
>>> > +#include "vs_dc.h"
>>> > +#include "vs_drv.h"
>>> > +
>>> > +static void vs_crtc_reset(struct drm_crtc *crtc)
>>> > +{
>>> > +	struct vs_crtc_state *state;
>>> > +
>>> > +	if (crtc->state) {
>>> > +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
>>> > +
>>> > +		state = to_vs_crtc_state(crtc->state);
>>> > +		kfree(state);
>>> > +		crtc->state = NULL;
>>> > +	}
>>> 
>>> You can call your crtc_destroy_state function directly here.
> ok got it !
>>> 
>>> > +
>>> > +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>>> > +	if (!state)
>>> > +		return;
>>> > +
>>> > +	__drm_atomic_helper_crtc_reset(crtc, &state->base);
>>> > +}
>>> > +
>>> > +static struct drm_crtc_state *
>>> > +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
>>> > +{
>>> > +	struct vs_crtc_state *ori_state;
>>> 
>>> It might be a matter of taste, but it is usually old_state.
>>> 
>>> > +	struct vs_crtc_state *state;
>>> > +
>>> > +	if (!crtc->state)
>>> > +		return NULL;
>>> > +
>>> > +	ori_state = to_vs_crtc_state(crtc->state);
>>> > +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>>> > +	if (!state)
>>> > +		return NULL;
>>> > +
>>> > +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
>>> > +
>>> > +	state->output_fmt = ori_state->output_fmt;
>>> > +	state->encoder_type = ori_state->encoder_type;
>>> > +	state->bpp = ori_state->bpp;
>>> > +	state->underflow = ori_state->underflow;
>>> 
>>> Can you use kmemdup instead?
> ok 
>>> 
>>> > +
>>> > +	return &state->base;
>>> > +}
>>> > +
>>> > +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
>>> > +					 struct drm_crtc_state *state)
>>> > +{
>>> > +	__drm_atomic_helper_crtc_destroy_state(state);
>>> > +	kfree(to_vs_crtc_state(state));
>>> > +}
>>> > +
>>> > +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
>>> > +{
>>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>>> > +
>>> > +	vs_dc_enable_vblank(dc, true);
>>> > +
>>> > +	return 0;
>>> > +}
>>> > +
>>> > +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
>>> > +{
>>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>>> > +
>>> > +	vs_dc_enable_vblank(dc, false);
>>> > +}
>>> > +
>>> > +static const struct drm_crtc_funcs vs_crtc_funcs = {
>>> > +	.set_config		= drm_atomic_helper_set_config,
>>> > +	.page_flip		= drm_atomic_helper_page_flip,
>>> 
>>> destroy is required, see drm_mode_config_cleanup()
> will add destory 
>>> 
>>> > +	.reset			= vs_crtc_reset,
>>> > +	.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
>>> > +	.atomic_destroy_state	= vs_crtc_atomic_destroy_state,
>>> 
>>> please consider adding atomic_print_state to output driver-specific bits.
>>> 
> will add atomic_print_state  to print my private definitions
>>> > +	.enable_vblank		= vs_crtc_enable_vblank,
>>> > +	.disable_vblank		= vs_crtc_disable_vblank,
>>> > +};
>>> > +
>>> > +static u8 cal_pixel_bits(u32 bus_format)
>>> 
>>> This looks like a generic helper code, which can go to a common place.
>>> 
>>> > +{
>>> > +	u8 bpp;
>>> > +
>>> > +	switch (bus_format) {
>>> > +	case MEDIA_BUS_FMT_RGB565_1X16:
>>> > +	case MEDIA_BUS_FMT_UYVY8_1X16:
>>> > +		bpp = 16;
>>> > +		break;
>>> > +	case MEDIA_BUS_FMT_RGB666_1X18:
>>> > +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
>>> > +		bpp = 18;
>>> > +		break;
>>> > +	case MEDIA_BUS_FMT_UYVY10_1X20:
>>> > +		bpp = 20;
>>> > +		break;
>>> > +	case MEDIA_BUS_FMT_BGR888_1X24:
>>> > +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
>>> > +	case MEDIA_BUS_FMT_YUV8_1X24:
>>> > +		bpp = 24;
>>> > +		break;
>>> > +	case MEDIA_BUS_FMT_RGB101010_1X30:
>>> > +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
>>> > +	case MEDIA_BUS_FMT_YUV10_1X30:
>>> > +		bpp = 30;
>>> > +		break;
>>> > +	default:
>>> > +		bpp = 24;
>>> > +		break;
>>> > +	}
>>> > +
>>> > +	return bpp;
>>> > +}
>>> > +
>>> > +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
>>> > +				  struct drm_atomic_state *state)
>>> > +{
>>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>>> > +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
>>> > +
>>> > +	vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
>>> > +
>>> > +	vs_dc_enable(dc, crtc);
>>> > +	drm_crtc_vblank_on(crtc);
>>> > +}
>>> > +
>>> > +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
>>> > +				   struct drm_atomic_state *state)
>>> > +{
>>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>>> > +
>>> > +	drm_crtc_vblank_off(crtc);
>>> > +
>>> > +	vs_dc_disable(dc, crtc);
>>> > +
>>> > +	if (crtc->state->event && !crtc->state->active) {
>>> > +		spin_lock_irq(&crtc->dev->event_lock);
>>> > +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
>>> > +		spin_unlock_irq(&crtc->dev->event_lock);
>>> > +
>>> > +		crtc->state->event = NULL;
>>> 
>>> I think even should be cleared within the lock.
>> 
>> event_lock doesn't protect anything in the crtc state.
> 
> how about match like this:
> spin_lock_irq(&crtc->dev->event_lock);
> if (crtc->state->event) {
> 	drm_crtc_send_vblank_event(crtc, crtc->state->event);
> 	crtc->state->event = NULL;
> }
> spin_unlock_irq(&crtc->dev->event_lock);
> 
>> 
>> But the bigger problem in this code is the prevalent crtc->state
>> usage. That should pretty much never be done, especially in anything
>> that can get called from the actual commit phase where you no longer
>> have the locks held. Instead one should almost always use the
>> get_{new,old}_state() stuff, or the old/new/oldnew state iterators.
> 
> the prevalent crtc->state usage :
> crtc->state should not be used directly?
> need replace it by get_{new,old}_state()
> 
> for example:
> drm_crtc_send_vblank_event(crtc, crtc->state->event);
> 
> 
> should do like this :
> struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> ...
> drm_crtc_send_vblank_event(crtc, crtc_state->event);
> ...
> 
> If there is a problem, help further correct
> wish give a example
> Thanks
> 
>> 
>>> 
>>> > +	}
>>> > +}
>>> > +
>>> > +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
>>> > +				 struct drm_atomic_state *state)
>>> > +{
>>> > +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
>>> > +									  crtc);
>>> > +
>>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>>> > +	struct device *dev = vs_crtc->dev;
>>> > +	struct drm_property_blob *blob = crtc->state->gamma_lut;
>> 
>> Eg. here you are using drm_atomic_get_new_crtc_state() correctly, but
>> then proceed to directly access crtc->state anyway.
>> 
> struct drm_property_blob *blob = crtc->state->gamma_lut;
> change to 
> struct drm_property_blob *blob = crtc_state ->gamma_lut;
> 
>>> > +	struct drm_color_lut *lut;
>>> > +	struct vs_dc *dc = dev_get_drvdata(dev);
>>> > +
>>> > +	if (crtc_state->color_mgmt_changed) {
>>> > +		if (blob && blob->length) {
>>> > +			lut = blob->data;
>>> > +			vs_dc_set_gamma(dc, crtc, lut,
>>> > +					blob->length / sizeof(*lut));
>>> > +			vs_dc_enable_gamma(dc, crtc, true);
>>> > +		} else {
>>> > +			vs_dc_enable_gamma(dc, crtc, false);
>>> > +		}
>>> > +	}
>>> > +}
>> 

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-10-26 10:09           ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-26 10:09 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, Conor Dooley, Albert Ou, Emil Renner Berthing,
	christian.koenig, Thomas Zimmermann, Bjorn Andersson,
	Chris Morgan, Maxime Ripard, Jagan Teki, Jack Zhu, Rob Herring,
	Palmer Dabbelt, Krzysztof Kozlowski, Paul Walmsley,
	Shengyang Chen, Changhuang Liang, Shawn Guo, Sumit Semwal

sorry 
Dmitry ,accidentally wrote the wrong name
Take no offense

On 2023/10/26 17:42, Keith Zhao wrote:
> hi Ville:
> very glad to receive your feedback
> Some of them are very good ideas.
> Some are not very clear and hope to get your further reply!
> 
> 
> On 2023/10/26 3:49, Ville Syrjälä wrote:
>> On Wed, Oct 25, 2023 at 10:28:56PM +0300, Dmitry Baryshkov wrote:
>>> On 25/10/2023 13:39, Keith Zhao wrote:
>>> > add 2 crtcs and 8 planes in vs-drm
>>> > 
>>> > Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>>> > ---
>>> >   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
>>> >   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
>>> >   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
>>> >   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
>>> >   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
>>> >   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
>>> >   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
>>> >   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
>>> >   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
>>> >   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
>>> >   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
>>> >   11 files changed, 4494 insertions(+), 2 deletions(-)
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
>>> > 
>>> > diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
>>> > index 7d3be305b..1d48016ca 100644
>>> > --- a/drivers/gpu/drm/verisilicon/Makefile
>>> > +++ b/drivers/gpu/drm/verisilicon/Makefile
>>> > @@ -1,7 +1,11 @@
>>> >   # SPDX-License-Identifier: GPL-2.0
>>> >   
>>> > -vs_drm-objs := vs_drv.o \
>>> > -		vs_modeset.o
>>> > +vs_drm-objs := vs_dc_hw.o \
>>> > +		vs_dc.o \
>>> > +		vs_crtc.o \
>>> > +		vs_drv.o \
>>> > +		vs_modeset.o \
>>> > +		vs_plane.o
>>> >   
>>> >   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>>> >   
>>> > diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
>>> > new file mode 100644
>>> > index 000000000..8a658ea77
>>> > --- /dev/null
>>> > +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
>>> > @@ -0,0 +1,257 @@
>>> > +// SPDX-License-Identifier: GPL-2.0
>>> > +/*
>>> > + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>>> > + *
>>> > + */
>>> > +
>>> > +#include <linux/clk.h>
>>> > +#include <linux/debugfs.h>
>>> > +#include <linux/media-bus-format.h>
>>> > +
>>> > +#include <drm/drm_atomic_helper.h>
>>> > +#include <drm/drm_atomic.h>
>>> > +#include <drm/drm_crtc.h>
>>> > +#include <drm/drm_gem_atomic_helper.h>
>>> > +#include <drm/drm_vblank.h>
>>> > +#include <drm/vs_drm.h>
>>> > +
>>> > +#include "vs_crtc.h"
>>> > +#include "vs_dc.h"
>>> > +#include "vs_drv.h"
>>> > +
>>> > +static void vs_crtc_reset(struct drm_crtc *crtc)
>>> > +{
>>> > +	struct vs_crtc_state *state;
>>> > +
>>> > +	if (crtc->state) {
>>> > +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
>>> > +
>>> > +		state = to_vs_crtc_state(crtc->state);
>>> > +		kfree(state);
>>> > +		crtc->state = NULL;
>>> > +	}
>>> 
>>> You can call your crtc_destroy_state function directly here.
> ok got it !
>>> 
>>> > +
>>> > +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>>> > +	if (!state)
>>> > +		return;
>>> > +
>>> > +	__drm_atomic_helper_crtc_reset(crtc, &state->base);
>>> > +}
>>> > +
>>> > +static struct drm_crtc_state *
>>> > +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
>>> > +{
>>> > +	struct vs_crtc_state *ori_state;
>>> 
>>> It might be a matter of taste, but it is usually old_state.
>>> 
>>> > +	struct vs_crtc_state *state;
>>> > +
>>> > +	if (!crtc->state)
>>> > +		return NULL;
>>> > +
>>> > +	ori_state = to_vs_crtc_state(crtc->state);
>>> > +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>>> > +	if (!state)
>>> > +		return NULL;
>>> > +
>>> > +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
>>> > +
>>> > +	state->output_fmt = ori_state->output_fmt;
>>> > +	state->encoder_type = ori_state->encoder_type;
>>> > +	state->bpp = ori_state->bpp;
>>> > +	state->underflow = ori_state->underflow;
>>> 
>>> Can you use kmemdup instead?
> ok 
>>> 
>>> > +
>>> > +	return &state->base;
>>> > +}
>>> > +
>>> > +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
>>> > +					 struct drm_crtc_state *state)
>>> > +{
>>> > +	__drm_atomic_helper_crtc_destroy_state(state);
>>> > +	kfree(to_vs_crtc_state(state));
>>> > +}
>>> > +
>>> > +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
>>> > +{
>>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>>> > +
>>> > +	vs_dc_enable_vblank(dc, true);
>>> > +
>>> > +	return 0;
>>> > +}
>>> > +
>>> > +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
>>> > +{
>>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>>> > +
>>> > +	vs_dc_enable_vblank(dc, false);
>>> > +}
>>> > +
>>> > +static const struct drm_crtc_funcs vs_crtc_funcs = {
>>> > +	.set_config		= drm_atomic_helper_set_config,
>>> > +	.page_flip		= drm_atomic_helper_page_flip,
>>> 
>>> destroy is required, see drm_mode_config_cleanup()
> will add destory 
>>> 
>>> > +	.reset			= vs_crtc_reset,
>>> > +	.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
>>> > +	.atomic_destroy_state	= vs_crtc_atomic_destroy_state,
>>> 
>>> please consider adding atomic_print_state to output driver-specific bits.
>>> 
> will add atomic_print_state  to print my private definitions
>>> > +	.enable_vblank		= vs_crtc_enable_vblank,
>>> > +	.disable_vblank		= vs_crtc_disable_vblank,
>>> > +};
>>> > +
>>> > +static u8 cal_pixel_bits(u32 bus_format)
>>> 
>>> This looks like a generic helper code, which can go to a common place.
>>> 
>>> > +{
>>> > +	u8 bpp;
>>> > +
>>> > +	switch (bus_format) {
>>> > +	case MEDIA_BUS_FMT_RGB565_1X16:
>>> > +	case MEDIA_BUS_FMT_UYVY8_1X16:
>>> > +		bpp = 16;
>>> > +		break;
>>> > +	case MEDIA_BUS_FMT_RGB666_1X18:
>>> > +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
>>> > +		bpp = 18;
>>> > +		break;
>>> > +	case MEDIA_BUS_FMT_UYVY10_1X20:
>>> > +		bpp = 20;
>>> > +		break;
>>> > +	case MEDIA_BUS_FMT_BGR888_1X24:
>>> > +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
>>> > +	case MEDIA_BUS_FMT_YUV8_1X24:
>>> > +		bpp = 24;
>>> > +		break;
>>> > +	case MEDIA_BUS_FMT_RGB101010_1X30:
>>> > +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
>>> > +	case MEDIA_BUS_FMT_YUV10_1X30:
>>> > +		bpp = 30;
>>> > +		break;
>>> > +	default:
>>> > +		bpp = 24;
>>> > +		break;
>>> > +	}
>>> > +
>>> > +	return bpp;
>>> > +}
>>> > +
>>> > +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
>>> > +				  struct drm_atomic_state *state)
>>> > +{
>>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>>> > +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
>>> > +
>>> > +	vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
>>> > +
>>> > +	vs_dc_enable(dc, crtc);
>>> > +	drm_crtc_vblank_on(crtc);
>>> > +}
>>> > +
>>> > +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
>>> > +				   struct drm_atomic_state *state)
>>> > +{
>>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>>> > +
>>> > +	drm_crtc_vblank_off(crtc);
>>> > +
>>> > +	vs_dc_disable(dc, crtc);
>>> > +
>>> > +	if (crtc->state->event && !crtc->state->active) {
>>> > +		spin_lock_irq(&crtc->dev->event_lock);
>>> > +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
>>> > +		spin_unlock_irq(&crtc->dev->event_lock);
>>> > +
>>> > +		crtc->state->event = NULL;
>>> 
>>> I think even should be cleared within the lock.
>> 
>> event_lock doesn't protect anything in the crtc state.
> 
> how about match like this:
> spin_lock_irq(&crtc->dev->event_lock);
> if (crtc->state->event) {
> 	drm_crtc_send_vblank_event(crtc, crtc->state->event);
> 	crtc->state->event = NULL;
> }
> spin_unlock_irq(&crtc->dev->event_lock);
> 
>> 
>> But the bigger problem in this code is the prevalent crtc->state
>> usage. That should pretty much never be done, especially in anything
>> that can get called from the actual commit phase where you no longer
>> have the locks held. Instead one should almost always use the
>> get_{new,old}_state() stuff, or the old/new/oldnew state iterators.
> 
> the prevalent crtc->state usage :
> crtc->state should not be used directly?
> need replace it by get_{new,old}_state()
> 
> for example:
> drm_crtc_send_vblank_event(crtc, crtc->state->event);
> 
> 
> should do like this :
> struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> ...
> drm_crtc_send_vblank_event(crtc, crtc_state->event);
> ...
> 
> If there is a problem, help further correct
> wish give a example
> Thanks
> 
>> 
>>> 
>>> > +	}
>>> > +}
>>> > +
>>> > +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
>>> > +				 struct drm_atomic_state *state)
>>> > +{
>>> > +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
>>> > +									  crtc);
>>> > +
>>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>>> > +	struct device *dev = vs_crtc->dev;
>>> > +	struct drm_property_blob *blob = crtc->state->gamma_lut;
>> 
>> Eg. here you are using drm_atomic_get_new_crtc_state() correctly, but
>> then proceed to directly access crtc->state anyway.
>> 
> struct drm_property_blob *blob = crtc->state->gamma_lut;
> change to 
> struct drm_property_blob *blob = crtc_state ->gamma_lut;
> 
>>> > +	struct drm_color_lut *lut;
>>> > +	struct vs_dc *dc = dev_get_drvdata(dev);
>>> > +
>>> > +	if (crtc_state->color_mgmt_changed) {
>>> > +		if (blob && blob->length) {
>>> > +			lut = blob->data;
>>> > +			vs_dc_set_gamma(dc, crtc, lut,
>>> > +					blob->length / sizeof(*lut));
>>> > +			vs_dc_enable_gamma(dc, crtc, true);
>>> > +		} else {
>>> > +			vs_dc_enable_gamma(dc, crtc, false);
>>> > +		}
>>> > +	}
>>> > +}
>> 

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-10-26 10:09           ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-10-26 10:09 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Emil Renner Berthing, dri-devel, Krzysztof Kozlowski,
	linux-riscv, Sumit Semwal, Shengyang Chen, linux-media,
	devicetree, Conor Dooley, Albert Ou, Maxime Ripard, Jagan Teki,
	linaro-mm-sig, Rob Herring, Chris Morgan, Paul Walmsley,
	Bjorn Andersson, linux-kernel, christian.koenig, Jack Zhu,
	Palmer Dabbelt, Thomas Zimmermann, Shawn Guo, Changhuang Liang

sorry 
Dmitry ,accidentally wrote the wrong name
Take no offense

On 2023/10/26 17:42, Keith Zhao wrote:
> hi Ville:
> very glad to receive your feedback
> Some of them are very good ideas.
> Some are not very clear and hope to get your further reply!
> 
> 
> On 2023/10/26 3:49, Ville Syrjälä wrote:
>> On Wed, Oct 25, 2023 at 10:28:56PM +0300, Dmitry Baryshkov wrote:
>>> On 25/10/2023 13:39, Keith Zhao wrote:
>>> > add 2 crtcs and 8 planes in vs-drm
>>> > 
>>> > Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>>> > ---
>>> >   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
>>> >   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
>>> >   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
>>> >   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
>>> >   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
>>> >   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
>>> >   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
>>> >   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
>>> >   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
>>> >   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
>>> >   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
>>> >   11 files changed, 4494 insertions(+), 2 deletions(-)
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
>>> >   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
>>> > 
>>> > diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
>>> > index 7d3be305b..1d48016ca 100644
>>> > --- a/drivers/gpu/drm/verisilicon/Makefile
>>> > +++ b/drivers/gpu/drm/verisilicon/Makefile
>>> > @@ -1,7 +1,11 @@
>>> >   # SPDX-License-Identifier: GPL-2.0
>>> >   
>>> > -vs_drm-objs := vs_drv.o \
>>> > -		vs_modeset.o
>>> > +vs_drm-objs := vs_dc_hw.o \
>>> > +		vs_dc.o \
>>> > +		vs_crtc.o \
>>> > +		vs_drv.o \
>>> > +		vs_modeset.o \
>>> > +		vs_plane.o
>>> >   
>>> >   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>>> >   
>>> > diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
>>> > new file mode 100644
>>> > index 000000000..8a658ea77
>>> > --- /dev/null
>>> > +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
>>> > @@ -0,0 +1,257 @@
>>> > +// SPDX-License-Identifier: GPL-2.0
>>> > +/*
>>> > + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>>> > + *
>>> > + */
>>> > +
>>> > +#include <linux/clk.h>
>>> > +#include <linux/debugfs.h>
>>> > +#include <linux/media-bus-format.h>
>>> > +
>>> > +#include <drm/drm_atomic_helper.h>
>>> > +#include <drm/drm_atomic.h>
>>> > +#include <drm/drm_crtc.h>
>>> > +#include <drm/drm_gem_atomic_helper.h>
>>> > +#include <drm/drm_vblank.h>
>>> > +#include <drm/vs_drm.h>
>>> > +
>>> > +#include "vs_crtc.h"
>>> > +#include "vs_dc.h"
>>> > +#include "vs_drv.h"
>>> > +
>>> > +static void vs_crtc_reset(struct drm_crtc *crtc)
>>> > +{
>>> > +	struct vs_crtc_state *state;
>>> > +
>>> > +	if (crtc->state) {
>>> > +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
>>> > +
>>> > +		state = to_vs_crtc_state(crtc->state);
>>> > +		kfree(state);
>>> > +		crtc->state = NULL;
>>> > +	}
>>> 
>>> You can call your crtc_destroy_state function directly here.
> ok got it !
>>> 
>>> > +
>>> > +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>>> > +	if (!state)
>>> > +		return;
>>> > +
>>> > +	__drm_atomic_helper_crtc_reset(crtc, &state->base);
>>> > +}
>>> > +
>>> > +static struct drm_crtc_state *
>>> > +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
>>> > +{
>>> > +	struct vs_crtc_state *ori_state;
>>> 
>>> It might be a matter of taste, but it is usually old_state.
>>> 
>>> > +	struct vs_crtc_state *state;
>>> > +
>>> > +	if (!crtc->state)
>>> > +		return NULL;
>>> > +
>>> > +	ori_state = to_vs_crtc_state(crtc->state);
>>> > +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>>> > +	if (!state)
>>> > +		return NULL;
>>> > +
>>> > +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
>>> > +
>>> > +	state->output_fmt = ori_state->output_fmt;
>>> > +	state->encoder_type = ori_state->encoder_type;
>>> > +	state->bpp = ori_state->bpp;
>>> > +	state->underflow = ori_state->underflow;
>>> 
>>> Can you use kmemdup instead?
> ok 
>>> 
>>> > +
>>> > +	return &state->base;
>>> > +}
>>> > +
>>> > +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
>>> > +					 struct drm_crtc_state *state)
>>> > +{
>>> > +	__drm_atomic_helper_crtc_destroy_state(state);
>>> > +	kfree(to_vs_crtc_state(state));
>>> > +}
>>> > +
>>> > +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
>>> > +{
>>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>>> > +
>>> > +	vs_dc_enable_vblank(dc, true);
>>> > +
>>> > +	return 0;
>>> > +}
>>> > +
>>> > +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
>>> > +{
>>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>>> > +
>>> > +	vs_dc_enable_vblank(dc, false);
>>> > +}
>>> > +
>>> > +static const struct drm_crtc_funcs vs_crtc_funcs = {
>>> > +	.set_config		= drm_atomic_helper_set_config,
>>> > +	.page_flip		= drm_atomic_helper_page_flip,
>>> 
>>> destroy is required, see drm_mode_config_cleanup()
> will add destory 
>>> 
>>> > +	.reset			= vs_crtc_reset,
>>> > +	.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
>>> > +	.atomic_destroy_state	= vs_crtc_atomic_destroy_state,
>>> 
>>> please consider adding atomic_print_state to output driver-specific bits.
>>> 
> will add atomic_print_state  to print my private definitions
>>> > +	.enable_vblank		= vs_crtc_enable_vblank,
>>> > +	.disable_vblank		= vs_crtc_disable_vblank,
>>> > +};
>>> > +
>>> > +static u8 cal_pixel_bits(u32 bus_format)
>>> 
>>> This looks like a generic helper code, which can go to a common place.
>>> 
>>> > +{
>>> > +	u8 bpp;
>>> > +
>>> > +	switch (bus_format) {
>>> > +	case MEDIA_BUS_FMT_RGB565_1X16:
>>> > +	case MEDIA_BUS_FMT_UYVY8_1X16:
>>> > +		bpp = 16;
>>> > +		break;
>>> > +	case MEDIA_BUS_FMT_RGB666_1X18:
>>> > +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
>>> > +		bpp = 18;
>>> > +		break;
>>> > +	case MEDIA_BUS_FMT_UYVY10_1X20:
>>> > +		bpp = 20;
>>> > +		break;
>>> > +	case MEDIA_BUS_FMT_BGR888_1X24:
>>> > +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
>>> > +	case MEDIA_BUS_FMT_YUV8_1X24:
>>> > +		bpp = 24;
>>> > +		break;
>>> > +	case MEDIA_BUS_FMT_RGB101010_1X30:
>>> > +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
>>> > +	case MEDIA_BUS_FMT_YUV10_1X30:
>>> > +		bpp = 30;
>>> > +		break;
>>> > +	default:
>>> > +		bpp = 24;
>>> > +		break;
>>> > +	}
>>> > +
>>> > +	return bpp;
>>> > +}
>>> > +
>>> > +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
>>> > +				  struct drm_atomic_state *state)
>>> > +{
>>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>>> > +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
>>> > +
>>> > +	vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
>>> > +
>>> > +	vs_dc_enable(dc, crtc);
>>> > +	drm_crtc_vblank_on(crtc);
>>> > +}
>>> > +
>>> > +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
>>> > +				   struct drm_atomic_state *state)
>>> > +{
>>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>>> > +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>>> > +
>>> > +	drm_crtc_vblank_off(crtc);
>>> > +
>>> > +	vs_dc_disable(dc, crtc);
>>> > +
>>> > +	if (crtc->state->event && !crtc->state->active) {
>>> > +		spin_lock_irq(&crtc->dev->event_lock);
>>> > +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
>>> > +		spin_unlock_irq(&crtc->dev->event_lock);
>>> > +
>>> > +		crtc->state->event = NULL;
>>> 
>>> I think even should be cleared within the lock.
>> 
>> event_lock doesn't protect anything in the crtc state.
> 
> how about match like this:
> spin_lock_irq(&crtc->dev->event_lock);
> if (crtc->state->event) {
> 	drm_crtc_send_vblank_event(crtc, crtc->state->event);
> 	crtc->state->event = NULL;
> }
> spin_unlock_irq(&crtc->dev->event_lock);
> 
>> 
>> But the bigger problem in this code is the prevalent crtc->state
>> usage. That should pretty much never be done, especially in anything
>> that can get called from the actual commit phase where you no longer
>> have the locks held. Instead one should almost always use the
>> get_{new,old}_state() stuff, or the old/new/oldnew state iterators.
> 
> the prevalent crtc->state usage :
> crtc->state should not be used directly?
> need replace it by get_{new,old}_state()
> 
> for example:
> drm_crtc_send_vblank_event(crtc, crtc->state->event);
> 
> 
> should do like this :
> struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> ...
> drm_crtc_send_vblank_event(crtc, crtc_state->event);
> ...
> 
> If there is a problem, help further correct
> wish give a example
> Thanks
> 
>> 
>>> 
>>> > +	}
>>> > +}
>>> > +
>>> > +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
>>> > +				 struct drm_atomic_state *state)
>>> > +{
>>> > +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
>>> > +									  crtc);
>>> > +
>>> > +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>>> > +	struct device *dev = vs_crtc->dev;
>>> > +	struct drm_property_blob *blob = crtc->state->gamma_lut;
>> 
>> Eg. here you are using drm_atomic_get_new_crtc_state() correctly, but
>> then proceed to directly access crtc->state anyway.
>> 
> struct drm_property_blob *blob = crtc->state->gamma_lut;
> change to 
> struct drm_property_blob *blob = crtc_state ->gamma_lut;
> 
>>> > +	struct drm_color_lut *lut;
>>> > +	struct vs_dc *dc = dev_get_drvdata(dev);
>>> > +
>>> > +	if (crtc_state->color_mgmt_changed) {
>>> > +		if (blob && blob->length) {
>>> > +			lut = blob->data;
>>> > +			vs_dc_set_gamma(dc, crtc, lut,
>>> > +					blob->length / sizeof(*lut));
>>> > +			vs_dc_enable_gamma(dc, crtc, true);
>>> > +		} else {
>>> > +			vs_dc_enable_gamma(dc, crtc, false);
>>> > +		}
>>> > +	}
>>> > +}
>> 

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
  2023-10-26  8:57         ` Dmitry Baryshkov
  (?)
@ 2023-10-26 11:53           ` Maxime Ripard
  -1 siblings, 0 replies; 117+ messages in thread
From: Maxime Ripard @ 2023-10-26 11:53 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig

[-- Attachment #1: Type: text/plain, Size: 3096 bytes --]

On Thu, Oct 26, 2023 at 11:57:22AM +0300, Dmitry Baryshkov wrote:
> On Thu, 26 Oct 2023 at 11:07, Maxime Ripard <mripard@kernel.org> wrote:
> >
> > On Thu, Oct 26, 2023 at 01:23:53AM +0300, Dmitry Baryshkov wrote:
> > > > +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
> > > > +{
> > > > +   struct drm_encoder *encoder = &hdmi->encoder;
> > > > +   struct device *dev = hdmi->dev;
> > > > +
> > > > +   encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
> > > > +
> > > > +   /*
> > > > +    * If we failed to find the CRTC(s) which this encoder is
> > > > +    * supposed to be connected to, it's because the CRTC has
> > > > +    * not been registered yet.  Defer probing, and hope that
> > > > +    * the required CRTC is added later.
> > > > +    */
> > > > +   if (encoder->possible_crtcs == 0)
> > > > +           return -EPROBE_DEFER;
> > > > +
> > > > +   drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
> > > > +
> > > > +   hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> > > > +
> > > > +   drm_connector_helper_add(&hdmi->connector,
> > > > +                            &starfive_hdmi_connector_helper_funcs);
> > > > +   drmm_connector_init(drm, &hdmi->connector,
> > > > +                       &starfive_hdmi_connector_funcs,
> > > > +                       DRM_MODE_CONNECTOR_HDMIA,
> > >
> > > On an embedded device one can not be so sure. There can be MHL or HDMI
> > > Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.
> >
> > On an HDMI driver, it's far from being a requirement, especially given
> > the limitations bridges have.
> 
> It's a blessing that things like MHL / HDMI-in-USB-C / HDMI-to-MyDP
> are not widely used in the wild and are mostly non-existing except
> several phones that preate wide DP usage.

And those can be supported without relying on bridges.

> Using drm_connector directly prevents one from handling possible
> modifications on the board level. For example, with the DRM connector
> in place, handling a separate HPD GPIO will result in code duplication
> from the hdmi-connector driver. Handling any other variations in the
> board design (which are pretty common in the embedded world) will also
> require changing the driver itself. drm_bridge / drm_bridge_connector
> save us from those issues.

And we have other solutions there too. Like, EDIDs are pretty much in
the same spot with a lot of device variations, but it also works without
a common driver. I'd really wish we were having less bridges and more
helpers, but here we are.

> BTW: what are the limitations of the drm_bridge wrt. HDMI output? I'm
> asking because we heavily depend on the bridge infrastructure for HDMI
> output. Maybe we are missing something there, which went unnoticed to
> me and my colleagues.

A bridge cannot extend the connector state or use properties, for
example. It works for basic stuff but falls apart as soon as you're
trying to do something slightly advanced.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-10-26 11:53           ` Maxime Ripard
  0 siblings, 0 replies; 117+ messages in thread
From: Maxime Ripard @ 2023-10-26 11:53 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig


[-- Attachment #1.1: Type: text/plain, Size: 3096 bytes --]

On Thu, Oct 26, 2023 at 11:57:22AM +0300, Dmitry Baryshkov wrote:
> On Thu, 26 Oct 2023 at 11:07, Maxime Ripard <mripard@kernel.org> wrote:
> >
> > On Thu, Oct 26, 2023 at 01:23:53AM +0300, Dmitry Baryshkov wrote:
> > > > +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
> > > > +{
> > > > +   struct drm_encoder *encoder = &hdmi->encoder;
> > > > +   struct device *dev = hdmi->dev;
> > > > +
> > > > +   encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
> > > > +
> > > > +   /*
> > > > +    * If we failed to find the CRTC(s) which this encoder is
> > > > +    * supposed to be connected to, it's because the CRTC has
> > > > +    * not been registered yet.  Defer probing, and hope that
> > > > +    * the required CRTC is added later.
> > > > +    */
> > > > +   if (encoder->possible_crtcs == 0)
> > > > +           return -EPROBE_DEFER;
> > > > +
> > > > +   drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
> > > > +
> > > > +   hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> > > > +
> > > > +   drm_connector_helper_add(&hdmi->connector,
> > > > +                            &starfive_hdmi_connector_helper_funcs);
> > > > +   drmm_connector_init(drm, &hdmi->connector,
> > > > +                       &starfive_hdmi_connector_funcs,
> > > > +                       DRM_MODE_CONNECTOR_HDMIA,
> > >
> > > On an embedded device one can not be so sure. There can be MHL or HDMI
> > > Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.
> >
> > On an HDMI driver, it's far from being a requirement, especially given
> > the limitations bridges have.
> 
> It's a blessing that things like MHL / HDMI-in-USB-C / HDMI-to-MyDP
> are not widely used in the wild and are mostly non-existing except
> several phones that preate wide DP usage.

And those can be supported without relying on bridges.

> Using drm_connector directly prevents one from handling possible
> modifications on the board level. For example, with the DRM connector
> in place, handling a separate HPD GPIO will result in code duplication
> from the hdmi-connector driver. Handling any other variations in the
> board design (which are pretty common in the embedded world) will also
> require changing the driver itself. drm_bridge / drm_bridge_connector
> save us from those issues.

And we have other solutions there too. Like, EDIDs are pretty much in
the same spot with a lot of device variations, but it also works without
a common driver. I'd really wish we were having less bridges and more
helpers, but here we are.

> BTW: what are the limitations of the drm_bridge wrt. HDMI output? I'm
> asking because we heavily depend on the bridge infrastructure for HDMI
> output. Maybe we are missing something there, which went unnoticed to
> me and my colleagues.

A bridge cannot extend the connector state or use properties, for
example. It works for basic stuff but falls apart as soon as you're
trying to do something slightly advanced.

Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 161 bytes --]

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-10-26 11:53           ` Maxime Ripard
  0 siblings, 0 replies; 117+ messages in thread
From: Maxime Ripard @ 2023-10-26 11:53 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Emil Renner Berthing, dri-devel, Krzysztof Kozlowski,
	linux-riscv, Sumit Semwal, Shengyang Chen, linux-media,
	devicetree, Conor Dooley, Albert Ou, Jagan Teki, linaro-mm-sig,
	Rob Herring, Chris Morgan, Paul Walmsley, Keith Zhao,
	Bjorn Andersson, linux-kernel, christian.koenig, Jack Zhu,
	Palmer Dabbelt, Thomas Zimmermann, Shawn Guo, Changhuang Liang

[-- Attachment #1: Type: text/plain, Size: 3096 bytes --]

On Thu, Oct 26, 2023 at 11:57:22AM +0300, Dmitry Baryshkov wrote:
> On Thu, 26 Oct 2023 at 11:07, Maxime Ripard <mripard@kernel.org> wrote:
> >
> > On Thu, Oct 26, 2023 at 01:23:53AM +0300, Dmitry Baryshkov wrote:
> > > > +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
> > > > +{
> > > > +   struct drm_encoder *encoder = &hdmi->encoder;
> > > > +   struct device *dev = hdmi->dev;
> > > > +
> > > > +   encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
> > > > +
> > > > +   /*
> > > > +    * If we failed to find the CRTC(s) which this encoder is
> > > > +    * supposed to be connected to, it's because the CRTC has
> > > > +    * not been registered yet.  Defer probing, and hope that
> > > > +    * the required CRTC is added later.
> > > > +    */
> > > > +   if (encoder->possible_crtcs == 0)
> > > > +           return -EPROBE_DEFER;
> > > > +
> > > > +   drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
> > > > +
> > > > +   hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> > > > +
> > > > +   drm_connector_helper_add(&hdmi->connector,
> > > > +                            &starfive_hdmi_connector_helper_funcs);
> > > > +   drmm_connector_init(drm, &hdmi->connector,
> > > > +                       &starfive_hdmi_connector_funcs,
> > > > +                       DRM_MODE_CONNECTOR_HDMIA,
> > >
> > > On an embedded device one can not be so sure. There can be MHL or HDMI
> > > Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.
> >
> > On an HDMI driver, it's far from being a requirement, especially given
> > the limitations bridges have.
> 
> It's a blessing that things like MHL / HDMI-in-USB-C / HDMI-to-MyDP
> are not widely used in the wild and are mostly non-existing except
> several phones that preate wide DP usage.

And those can be supported without relying on bridges.

> Using drm_connector directly prevents one from handling possible
> modifications on the board level. For example, with the DRM connector
> in place, handling a separate HPD GPIO will result in code duplication
> from the hdmi-connector driver. Handling any other variations in the
> board design (which are pretty common in the embedded world) will also
> require changing the driver itself. drm_bridge / drm_bridge_connector
> save us from those issues.

And we have other solutions there too. Like, EDIDs are pretty much in
the same spot with a lot of device variations, but it also works without
a common driver. I'd really wish we were having less bridges and more
helpers, but here we are.

> BTW: what are the limitations of the drm_bridge wrt. HDMI output? I'm
> asking because we heavily depend on the bridge infrastructure for HDMI
> output. Maybe we are missing something there, which went unnoticed to
> me and my colleagues.

A bridge cannot extend the connector state or use properties, for
example. It works for basic stuff but falls apart as soon as you're
trying to do something slightly advanced.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
  2023-10-26 11:53           ` Maxime Ripard
  (?)
@ 2023-10-29 16:52             ` Dmitry Baryshkov
  -1 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-10-29 16:52 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig

On Thu, 26 Oct 2023 at 14:53, Maxime Ripard <mripard@kernel.org> wrote:
>
> On Thu, Oct 26, 2023 at 11:57:22AM +0300, Dmitry Baryshkov wrote:
> > On Thu, 26 Oct 2023 at 11:07, Maxime Ripard <mripard@kernel.org> wrote:
> > >
> > > On Thu, Oct 26, 2023 at 01:23:53AM +0300, Dmitry Baryshkov wrote:
> > > > > +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
> > > > > +{
> > > > > +   struct drm_encoder *encoder = &hdmi->encoder;
> > > > > +   struct device *dev = hdmi->dev;
> > > > > +
> > > > > +   encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
> > > > > +
> > > > > +   /*
> > > > > +    * If we failed to find the CRTC(s) which this encoder is
> > > > > +    * supposed to be connected to, it's because the CRTC has
> > > > > +    * not been registered yet.  Defer probing, and hope that
> > > > > +    * the required CRTC is added later.
> > > > > +    */
> > > > > +   if (encoder->possible_crtcs == 0)
> > > > > +           return -EPROBE_DEFER;
> > > > > +
> > > > > +   drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
> > > > > +
> > > > > +   hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> > > > > +
> > > > > +   drm_connector_helper_add(&hdmi->connector,
> > > > > +                            &starfive_hdmi_connector_helper_funcs);
> > > > > +   drmm_connector_init(drm, &hdmi->connector,
> > > > > +                       &starfive_hdmi_connector_funcs,
> > > > > +                       DRM_MODE_CONNECTOR_HDMIA,
> > > >
> > > > On an embedded device one can not be so sure. There can be MHL or HDMI
> > > > Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.
> > >
> > > On an HDMI driver, it's far from being a requirement, especially given
> > > the limitations bridges have.
> >
> > It's a blessing that things like MHL / HDMI-in-USB-C / HDMI-to-MyDP
> > are not widely used in the wild and are mostly non-existing except
> > several phones that preate wide DP usage.
>
> And those can be supported without relying on bridges.

Yes, they likely can, in the way that nouveau handles I2C TV encoders.
But I don't think this can scale. We can have different devices
attached to the DSI, LVDS, HDMI and even DP image sources. I don't see
a scalable solution for either of them. E.g. by switching drm/msm to
use panel bridges for DSI panels we were able to significantly unify
and simplify code paths.

> > Using drm_connector directly prevents one from handling possible
> > modifications on the board level. For example, with the DRM connector
> > in place, handling a separate HPD GPIO will result in code duplication
> > from the hdmi-connector driver. Handling any other variations in the
> > board design (which are pretty common in the embedded world) will also
> > require changing the driver itself. drm_bridge / drm_bridge_connector
> > save us from those issues.
>
> And we have other solutions there too. Like, EDIDs are pretty much in
> the same spot with a lot of device variations, but it also works without
> a common driver. I'd really wish we were having less bridges and more
> helpers, but here we are.
>
> > BTW: what are the limitations of the drm_bridge wrt. HDMI output? I'm
> > asking because we heavily depend on the bridge infrastructure for HDMI
> > output. Maybe we are missing something there, which went unnoticed to
> > me and my colleagues.
>
> A bridge cannot extend the connector state or use properties, for
> example. It works for basic stuff but falls apart as soon as you're
> trying to do something slightly advanced.

Ack. I agree, we didn't have a necessity to implement properties up to
now. But that sounds like an interesting topic for DSI-to-HDMI bridges
and HDCP support. I'll need to check if any of the RB3/RB5/Dragonboard
bridges are programmed with the HDCP keys.
--
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-10-29 16:52             ` Dmitry Baryshkov
  0 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-10-29 16:52 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Emil Renner Berthing, dri-devel, Krzysztof Kozlowski,
	linux-riscv, Sumit Semwal, Shengyang Chen, linux-media,
	devicetree, Conor Dooley, Albert Ou, Jagan Teki, linaro-mm-sig,
	Rob Herring, Chris Morgan, Paul Walmsley, Keith Zhao,
	Bjorn Andersson, linux-kernel, christian.koenig, Jack Zhu,
	Palmer Dabbelt, Thomas Zimmermann, Shawn Guo, Changhuang Liang

On Thu, 26 Oct 2023 at 14:53, Maxime Ripard <mripard@kernel.org> wrote:
>
> On Thu, Oct 26, 2023 at 11:57:22AM +0300, Dmitry Baryshkov wrote:
> > On Thu, 26 Oct 2023 at 11:07, Maxime Ripard <mripard@kernel.org> wrote:
> > >
> > > On Thu, Oct 26, 2023 at 01:23:53AM +0300, Dmitry Baryshkov wrote:
> > > > > +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
> > > > > +{
> > > > > +   struct drm_encoder *encoder = &hdmi->encoder;
> > > > > +   struct device *dev = hdmi->dev;
> > > > > +
> > > > > +   encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
> > > > > +
> > > > > +   /*
> > > > > +    * If we failed to find the CRTC(s) which this encoder is
> > > > > +    * supposed to be connected to, it's because the CRTC has
> > > > > +    * not been registered yet.  Defer probing, and hope that
> > > > > +    * the required CRTC is added later.
> > > > > +    */
> > > > > +   if (encoder->possible_crtcs == 0)
> > > > > +           return -EPROBE_DEFER;
> > > > > +
> > > > > +   drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
> > > > > +
> > > > > +   hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> > > > > +
> > > > > +   drm_connector_helper_add(&hdmi->connector,
> > > > > +                            &starfive_hdmi_connector_helper_funcs);
> > > > > +   drmm_connector_init(drm, &hdmi->connector,
> > > > > +                       &starfive_hdmi_connector_funcs,
> > > > > +                       DRM_MODE_CONNECTOR_HDMIA,
> > > >
> > > > On an embedded device one can not be so sure. There can be MHL or HDMI
> > > > Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.
> > >
> > > On an HDMI driver, it's far from being a requirement, especially given
> > > the limitations bridges have.
> >
> > It's a blessing that things like MHL / HDMI-in-USB-C / HDMI-to-MyDP
> > are not widely used in the wild and are mostly non-existing except
> > several phones that preate wide DP usage.
>
> And those can be supported without relying on bridges.

Yes, they likely can, in the way that nouveau handles I2C TV encoders.
But I don't think this can scale. We can have different devices
attached to the DSI, LVDS, HDMI and even DP image sources. I don't see
a scalable solution for either of them. E.g. by switching drm/msm to
use panel bridges for DSI panels we were able to significantly unify
and simplify code paths.

> > Using drm_connector directly prevents one from handling possible
> > modifications on the board level. For example, with the DRM connector
> > in place, handling a separate HPD GPIO will result in code duplication
> > from the hdmi-connector driver. Handling any other variations in the
> > board design (which are pretty common in the embedded world) will also
> > require changing the driver itself. drm_bridge / drm_bridge_connector
> > save us from those issues.
>
> And we have other solutions there too. Like, EDIDs are pretty much in
> the same spot with a lot of device variations, but it also works without
> a common driver. I'd really wish we were having less bridges and more
> helpers, but here we are.
>
> > BTW: what are the limitations of the drm_bridge wrt. HDMI output? I'm
> > asking because we heavily depend on the bridge infrastructure for HDMI
> > output. Maybe we are missing something there, which went unnoticed to
> > me and my colleagues.
>
> A bridge cannot extend the connector state or use properties, for
> example. It works for basic stuff but falls apart as soon as you're
> trying to do something slightly advanced.

Ack. I agree, we didn't have a necessity to implement properties up to
now. But that sounds like an interesting topic for DSI-to-HDMI bridges
and HDCP support. I'll need to check if any of the RB3/RB5/Dragonboard
bridges are programmed with the HDCP keys.
--
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-10-29 16:52             ` Dmitry Baryshkov
  0 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-10-29 16:52 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig

On Thu, 26 Oct 2023 at 14:53, Maxime Ripard <mripard@kernel.org> wrote:
>
> On Thu, Oct 26, 2023 at 11:57:22AM +0300, Dmitry Baryshkov wrote:
> > On Thu, 26 Oct 2023 at 11:07, Maxime Ripard <mripard@kernel.org> wrote:
> > >
> > > On Thu, Oct 26, 2023 at 01:23:53AM +0300, Dmitry Baryshkov wrote:
> > > > > +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
> > > > > +{
> > > > > +   struct drm_encoder *encoder = &hdmi->encoder;
> > > > > +   struct device *dev = hdmi->dev;
> > > > > +
> > > > > +   encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
> > > > > +
> > > > > +   /*
> > > > > +    * If we failed to find the CRTC(s) which this encoder is
> > > > > +    * supposed to be connected to, it's because the CRTC has
> > > > > +    * not been registered yet.  Defer probing, and hope that
> > > > > +    * the required CRTC is added later.
> > > > > +    */
> > > > > +   if (encoder->possible_crtcs == 0)
> > > > > +           return -EPROBE_DEFER;
> > > > > +
> > > > > +   drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
> > > > > +
> > > > > +   hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> > > > > +
> > > > > +   drm_connector_helper_add(&hdmi->connector,
> > > > > +                            &starfive_hdmi_connector_helper_funcs);
> > > > > +   drmm_connector_init(drm, &hdmi->connector,
> > > > > +                       &starfive_hdmi_connector_funcs,
> > > > > +                       DRM_MODE_CONNECTOR_HDMIA,
> > > >
> > > > On an embedded device one can not be so sure. There can be MHL or HDMI
> > > > Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.
> > >
> > > On an HDMI driver, it's far from being a requirement, especially given
> > > the limitations bridges have.
> >
> > It's a blessing that things like MHL / HDMI-in-USB-C / HDMI-to-MyDP
> > are not widely used in the wild and are mostly non-existing except
> > several phones that preate wide DP usage.
>
> And those can be supported without relying on bridges.

Yes, they likely can, in the way that nouveau handles I2C TV encoders.
But I don't think this can scale. We can have different devices
attached to the DSI, LVDS, HDMI and even DP image sources. I don't see
a scalable solution for either of them. E.g. by switching drm/msm to
use panel bridges for DSI panels we were able to significantly unify
and simplify code paths.

> > Using drm_connector directly prevents one from handling possible
> > modifications on the board level. For example, with the DRM connector
> > in place, handling a separate HPD GPIO will result in code duplication
> > from the hdmi-connector driver. Handling any other variations in the
> > board design (which are pretty common in the embedded world) will also
> > require changing the driver itself. drm_bridge / drm_bridge_connector
> > save us from those issues.
>
> And we have other solutions there too. Like, EDIDs are pretty much in
> the same spot with a lot of device variations, but it also works without
> a common driver. I'd really wish we were having less bridges and more
> helpers, but here we are.
>
> > BTW: what are the limitations of the drm_bridge wrt. HDMI output? I'm
> > asking because we heavily depend on the bridge infrastructure for HDMI
> > output. Maybe we are missing something there, which went unnoticed to
> > me and my colleagues.
>
> A bridge cannot extend the connector state or use properties, for
> example. It works for basic stuff but falls apart as soon as you're
> trying to do something slightly advanced.

Ack. I agree, we didn't have a necessity to implement properties up to
now. But that sounds like an interesting topic for DSI-to-HDMI bridges
and HDCP support. I'll need to check if any of the RB3/RB5/Dragonboard
bridges are programmed with the HDCP keys.
--
With best wishes
Dmitry

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
  2023-10-29 16:52             ` Dmitry Baryshkov
  (?)
@ 2023-11-07  9:19               ` Maxime Ripard
  -1 siblings, 0 replies; 117+ messages in thread
From: Maxime Ripard @ 2023-11-07  9:19 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig

[-- Attachment #1: Type: text/plain, Size: 4433 bytes --]

Hi,

On Sun, Oct 29, 2023 at 06:52:24PM +0200, Dmitry Baryshkov wrote:
> On Thu, 26 Oct 2023 at 14:53, Maxime Ripard <mripard@kernel.org> wrote:
> >
> > On Thu, Oct 26, 2023 at 11:57:22AM +0300, Dmitry Baryshkov wrote:
> > > On Thu, 26 Oct 2023 at 11:07, Maxime Ripard <mripard@kernel.org> wrote:
> > > >
> > > > On Thu, Oct 26, 2023 at 01:23:53AM +0300, Dmitry Baryshkov wrote:
> > > > > > +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
> > > > > > +{
> > > > > > +   struct drm_encoder *encoder = &hdmi->encoder;
> > > > > > +   struct device *dev = hdmi->dev;
> > > > > > +
> > > > > > +   encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
> > > > > > +
> > > > > > +   /*
> > > > > > +    * If we failed to find the CRTC(s) which this encoder is
> > > > > > +    * supposed to be connected to, it's because the CRTC has
> > > > > > +    * not been registered yet.  Defer probing, and hope that
> > > > > > +    * the required CRTC is added later.
> > > > > > +    */
> > > > > > +   if (encoder->possible_crtcs == 0)
> > > > > > +           return -EPROBE_DEFER;
> > > > > > +
> > > > > > +   drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
> > > > > > +
> > > > > > +   hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> > > > > > +
> > > > > > +   drm_connector_helper_add(&hdmi->connector,
> > > > > > +                            &starfive_hdmi_connector_helper_funcs);
> > > > > > +   drmm_connector_init(drm, &hdmi->connector,
> > > > > > +                       &starfive_hdmi_connector_funcs,
> > > > > > +                       DRM_MODE_CONNECTOR_HDMIA,
> > > > >
> > > > > On an embedded device one can not be so sure. There can be MHL or HDMI
> > > > > Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.
> > > >
> > > > On an HDMI driver, it's far from being a requirement, especially given
> > > > the limitations bridges have.
> > >
> > > It's a blessing that things like MHL / HDMI-in-USB-C / HDMI-to-MyDP
> > > are not widely used in the wild and are mostly non-existing except
> > > several phones that preate wide DP usage.
> >
> > And those can be supported without relying on bridges.
> 
> Yes, they likely can, in the way that nouveau handles I2C TV encoders.
> But I don't think this can scale. We can have different devices
> attached to the DSI, LVDS, HDMI and even DP image sources. I don't see
> a scalable solution for either of them. E.g. by switching drm/msm to
> use panel bridges for DSI panels we were able to significantly unify
> and simplify code paths.

I'm glad it worked fine for drm/msm, but what I don't really like is the
current dogma that *everything* should be a bridge, and that's just a
poor guideline.

> > > Using drm_connector directly prevents one from handling possible
> > > modifications on the board level. For example, with the DRM connector
> > > in place, handling a separate HPD GPIO will result in code duplication
> > > from the hdmi-connector driver. Handling any other variations in the
> > > board design (which are pretty common in the embedded world) will also
> > > require changing the driver itself. drm_bridge / drm_bridge_connector
> > > save us from those issues.
> >
> > And we have other solutions there too. Like, EDIDs are pretty much in
> > the same spot with a lot of device variations, but it also works without
> > a common driver. I'd really wish we were having less bridges and more
> > helpers, but here we are.
> >
> > > BTW: what are the limitations of the drm_bridge wrt. HDMI output? I'm
> > > asking because we heavily depend on the bridge infrastructure for HDMI
> > > output. Maybe we are missing something there, which went unnoticed to
> > > me and my colleagues.
> >
> > A bridge cannot extend the connector state or use properties, for
> > example. It works for basic stuff but falls apart as soon as you're
> > trying to do something slightly advanced.
> 
> Ack. I agree, we didn't have a necessity to implement properties up to
> now. But that sounds like an interesting topic for DSI-to-HDMI bridges
> and HDCP support. I'll need to check if any of the RB3/RB5/Dragonboard
> bridges are programmed with the HDCP keys.

Aside from HDCP, the current color management work will also require to
expose properties on the connectors.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-11-07  9:19               ` Maxime Ripard
  0 siblings, 0 replies; 117+ messages in thread
From: Maxime Ripard @ 2023-11-07  9:19 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Emil Renner Berthing, dri-devel, Krzysztof Kozlowski,
	linux-riscv, Sumit Semwal, Shengyang Chen, linux-media,
	devicetree, Conor Dooley, Albert Ou, Jagan Teki, linaro-mm-sig,
	Rob Herring, Chris Morgan, Paul Walmsley, Keith Zhao,
	Bjorn Andersson, linux-kernel, christian.koenig, Jack Zhu,
	Palmer Dabbelt, Thomas Zimmermann, Shawn Guo, Changhuang Liang

[-- Attachment #1: Type: text/plain, Size: 4433 bytes --]

Hi,

On Sun, Oct 29, 2023 at 06:52:24PM +0200, Dmitry Baryshkov wrote:
> On Thu, 26 Oct 2023 at 14:53, Maxime Ripard <mripard@kernel.org> wrote:
> >
> > On Thu, Oct 26, 2023 at 11:57:22AM +0300, Dmitry Baryshkov wrote:
> > > On Thu, 26 Oct 2023 at 11:07, Maxime Ripard <mripard@kernel.org> wrote:
> > > >
> > > > On Thu, Oct 26, 2023 at 01:23:53AM +0300, Dmitry Baryshkov wrote:
> > > > > > +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
> > > > > > +{
> > > > > > +   struct drm_encoder *encoder = &hdmi->encoder;
> > > > > > +   struct device *dev = hdmi->dev;
> > > > > > +
> > > > > > +   encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
> > > > > > +
> > > > > > +   /*
> > > > > > +    * If we failed to find the CRTC(s) which this encoder is
> > > > > > +    * supposed to be connected to, it's because the CRTC has
> > > > > > +    * not been registered yet.  Defer probing, and hope that
> > > > > > +    * the required CRTC is added later.
> > > > > > +    */
> > > > > > +   if (encoder->possible_crtcs == 0)
> > > > > > +           return -EPROBE_DEFER;
> > > > > > +
> > > > > > +   drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
> > > > > > +
> > > > > > +   hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> > > > > > +
> > > > > > +   drm_connector_helper_add(&hdmi->connector,
> > > > > > +                            &starfive_hdmi_connector_helper_funcs);
> > > > > > +   drmm_connector_init(drm, &hdmi->connector,
> > > > > > +                       &starfive_hdmi_connector_funcs,
> > > > > > +                       DRM_MODE_CONNECTOR_HDMIA,
> > > > >
> > > > > On an embedded device one can not be so sure. There can be MHL or HDMI
> > > > > Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.
> > > >
> > > > On an HDMI driver, it's far from being a requirement, especially given
> > > > the limitations bridges have.
> > >
> > > It's a blessing that things like MHL / HDMI-in-USB-C / HDMI-to-MyDP
> > > are not widely used in the wild and are mostly non-existing except
> > > several phones that preate wide DP usage.
> >
> > And those can be supported without relying on bridges.
> 
> Yes, they likely can, in the way that nouveau handles I2C TV encoders.
> But I don't think this can scale. We can have different devices
> attached to the DSI, LVDS, HDMI and even DP image sources. I don't see
> a scalable solution for either of them. E.g. by switching drm/msm to
> use panel bridges for DSI panels we were able to significantly unify
> and simplify code paths.

I'm glad it worked fine for drm/msm, but what I don't really like is the
current dogma that *everything* should be a bridge, and that's just a
poor guideline.

> > > Using drm_connector directly prevents one from handling possible
> > > modifications on the board level. For example, with the DRM connector
> > > in place, handling a separate HPD GPIO will result in code duplication
> > > from the hdmi-connector driver. Handling any other variations in the
> > > board design (which are pretty common in the embedded world) will also
> > > require changing the driver itself. drm_bridge / drm_bridge_connector
> > > save us from those issues.
> >
> > And we have other solutions there too. Like, EDIDs are pretty much in
> > the same spot with a lot of device variations, but it also works without
> > a common driver. I'd really wish we were having less bridges and more
> > helpers, but here we are.
> >
> > > BTW: what are the limitations of the drm_bridge wrt. HDMI output? I'm
> > > asking because we heavily depend on the bridge infrastructure for HDMI
> > > output. Maybe we are missing something there, which went unnoticed to
> > > me and my colleagues.
> >
> > A bridge cannot extend the connector state or use properties, for
> > example. It works for basic stuff but falls apart as soon as you're
> > trying to do something slightly advanced.
> 
> Ack. I agree, we didn't have a necessity to implement properties up to
> now. But that sounds like an interesting topic for DSI-to-HDMI bridges
> and HDCP support. I'll need to check if any of the RB3/RB5/Dragonboard
> bridges are programmed with the HDCP keys.

Aside from HDCP, the current color management work will also require to
expose properties on the connectors.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-11-07  9:19               ` Maxime Ripard
  0 siblings, 0 replies; 117+ messages in thread
From: Maxime Ripard @ 2023-11-07  9:19 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig


[-- Attachment #1.1: Type: text/plain, Size: 4433 bytes --]

Hi,

On Sun, Oct 29, 2023 at 06:52:24PM +0200, Dmitry Baryshkov wrote:
> On Thu, 26 Oct 2023 at 14:53, Maxime Ripard <mripard@kernel.org> wrote:
> >
> > On Thu, Oct 26, 2023 at 11:57:22AM +0300, Dmitry Baryshkov wrote:
> > > On Thu, 26 Oct 2023 at 11:07, Maxime Ripard <mripard@kernel.org> wrote:
> > > >
> > > > On Thu, Oct 26, 2023 at 01:23:53AM +0300, Dmitry Baryshkov wrote:
> > > > > > +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
> > > > > > +{
> > > > > > +   struct drm_encoder *encoder = &hdmi->encoder;
> > > > > > +   struct device *dev = hdmi->dev;
> > > > > > +
> > > > > > +   encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
> > > > > > +
> > > > > > +   /*
> > > > > > +    * If we failed to find the CRTC(s) which this encoder is
> > > > > > +    * supposed to be connected to, it's because the CRTC has
> > > > > > +    * not been registered yet.  Defer probing, and hope that
> > > > > > +    * the required CRTC is added later.
> > > > > > +    */
> > > > > > +   if (encoder->possible_crtcs == 0)
> > > > > > +           return -EPROBE_DEFER;
> > > > > > +
> > > > > > +   drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
> > > > > > +
> > > > > > +   hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> > > > > > +
> > > > > > +   drm_connector_helper_add(&hdmi->connector,
> > > > > > +                            &starfive_hdmi_connector_helper_funcs);
> > > > > > +   drmm_connector_init(drm, &hdmi->connector,
> > > > > > +                       &starfive_hdmi_connector_funcs,
> > > > > > +                       DRM_MODE_CONNECTOR_HDMIA,
> > > > >
> > > > > On an embedded device one can not be so sure. There can be MHL or HDMI
> > > > > Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.
> > > >
> > > > On an HDMI driver, it's far from being a requirement, especially given
> > > > the limitations bridges have.
> > >
> > > It's a blessing that things like MHL / HDMI-in-USB-C / HDMI-to-MyDP
> > > are not widely used in the wild and are mostly non-existing except
> > > several phones that preate wide DP usage.
> >
> > And those can be supported without relying on bridges.
> 
> Yes, they likely can, in the way that nouveau handles I2C TV encoders.
> But I don't think this can scale. We can have different devices
> attached to the DSI, LVDS, HDMI and even DP image sources. I don't see
> a scalable solution for either of them. E.g. by switching drm/msm to
> use panel bridges for DSI panels we were able to significantly unify
> and simplify code paths.

I'm glad it worked fine for drm/msm, but what I don't really like is the
current dogma that *everything* should be a bridge, and that's just a
poor guideline.

> > > Using drm_connector directly prevents one from handling possible
> > > modifications on the board level. For example, with the DRM connector
> > > in place, handling a separate HPD GPIO will result in code duplication
> > > from the hdmi-connector driver. Handling any other variations in the
> > > board design (which are pretty common in the embedded world) will also
> > > require changing the driver itself. drm_bridge / drm_bridge_connector
> > > save us from those issues.
> >
> > And we have other solutions there too. Like, EDIDs are pretty much in
> > the same spot with a lot of device variations, but it also works without
> > a common driver. I'd really wish we were having less bridges and more
> > helpers, but here we are.
> >
> > > BTW: what are the limitations of the drm_bridge wrt. HDMI output? I'm
> > > asking because we heavily depend on the bridge infrastructure for HDMI
> > > output. Maybe we are missing something there, which went unnoticed to
> > > me and my colleagues.
> >
> > A bridge cannot extend the connector state or use properties, for
> > example. It works for basic stuff but falls apart as soon as you're
> > trying to do something slightly advanced.
> 
> Ack. I agree, we didn't have a necessity to implement properties up to
> now. But that sounds like an interesting topic for DSI-to-HDMI bridges
> and HDCP support. I'll need to check if any of the RB3/RB5/Dragonboard
> bridges are programmed with the HDCP keys.

Aside from HDCP, the current color management work will also require to
expose properties on the connectors.

Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 161 bytes --]

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
  2023-10-25 22:23     ` Dmitry Baryshkov
  (?)
@ 2023-11-13 12:11       ` Keith Zhao
  -1 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-11-13 12:11 UTC (permalink / raw)
  To: Dmitry Baryshkov, dri-devel, devicetree, linux-kernel,
	linux-riscv, linux-media, linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig



On 2023/10/26 6:23, Dmitry Baryshkov wrote:
> On 25/10/2023 13:39, Keith Zhao wrote:
>> add hdmi driver as encoder and connect
>>
>> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>> ---
>>   drivers/gpu/drm/verisilicon/Kconfig         |   8 +-
>>   drivers/gpu/drm/verisilicon/Makefile        |   1 +
>>   drivers/gpu/drm/verisilicon/starfive_hdmi.c | 949 ++++++++++++++++++++
>>   drivers/gpu/drm/verisilicon/starfive_hdmi.h | 295 ++++++
>>   drivers/gpu/drm/verisilicon/vs_drv.c        |   5 +
>>   drivers/gpu/drm/verisilicon/vs_drv.h        |   4 +
>>   6 files changed, 1261 insertions(+), 1 deletion(-)
>>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.c
>>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.h
>>
>> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
>> index 3a361f8c8..122c786e3 100644
>> --- a/drivers/gpu/drm/verisilicon/Kconfig
>> +++ b/drivers/gpu/drm/verisilicon/Kconfig
>> @@ -12,4 +12,10 @@ config DRM_VERISILICON
>>         setting and buffer management. It does not
>>         provide 2D or 3D acceleration.
>>   -
>> +config DRM_VERISILICON_STARFIVE_HDMI
>> +    bool "Starfive HDMI extensions"
>> +    depends on DRM_VERISILICON
>> +    help
>> +       This selects support for StarFive soc specific extensions
>> +       for the Innosilicon HDMI driver. If you want to enable
>> +       HDMI on JH7110 based soc, you should select this option.
>> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
>> index 1d48016ca..08350f25b 100644
>> --- a/drivers/gpu/drm/verisilicon/Makefile
>> +++ b/drivers/gpu/drm/verisilicon/Makefile
>> @@ -7,5 +7,6 @@ vs_drm-objs := vs_dc_hw.o \
>>           vs_modeset.o \
>>           vs_plane.o
>>   +vs_drm-$(CONFIG_DRM_VERISILICON_STARFIVE_HDMI) += starfive_hdmi.o
>>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>>   diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.c b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
>> new file mode 100644
>> index 000000000..d296c4b71
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
>> @@ -0,0 +1,949 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/component.h>
>> +#include <linux/delay.h>
>> +#include <linux/err.h>
>> +#include <linux/hdmi.h>
>> +#include <linux/i2c.h>
>> +#include <linux/irq.h>
>> +#include <linux/media-bus-format.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of_device.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/reset.h>
>> +
>> +#include <drm/bridge/dw_hdmi.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_edid.h>
>> +#include <drm/drm_managed.h>
>> +#include <drm/drm_of.h>
>> +#include <drm/drm_probe_helper.h>
>> +#include <drm/drm_simple_kms_helper.h>
>> +
>> +#include "starfive_hdmi.h"
>> +#include "vs_drv.h"
>> +#include "vs_crtc.h"
>> +
>> +static struct starfive_hdmi *encoder_to_hdmi(struct drm_encoder *encoder)
>> +{
>> +    return container_of(encoder, struct starfive_hdmi, encoder);
>> +}
>> +
>> +static struct starfive_hdmi *connector_to_hdmi(struct drm_connector *connector)
>> +{
>> +    return container_of(connector, struct starfive_hdmi, connector);
>> +}
>> +
>> +struct starfive_hdmi_i2c {
>> +    struct i2c_adapter adap;
>> +
>> +    u8 ddc_addr;
>> +    u8 segment_addr;
>> +    /* protects the edid data when use i2c cmd to read edid */
>> +    struct mutex lock;
>> +    struct completion cmp;
>> +};
>> +
>> +static const struct pre_pll_config pre_pll_cfg_table[] = {
>> +    { 25175000,  25175000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0xf55555},
>> +    { 25200000,  25200000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0},
>> +    { 27000000,  27000000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0},
> 
> Such data structures are usually pretyt limited and hard to handle. Could you please replace it with the runtime calculations of
> 
>> +    { 27027000,  27027000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0x170a3d},
>> +    { 28320000,  28320000, 1,  28, 2, 1, 1,  3, 0, 3, 4, 0, 0x51eb85},
>> +    { 30240000,  30240000, 1,  30, 2, 1, 1,  3, 0, 3, 4, 0, 0x3d70a3},
>> +    { 31500000,  31500000, 1,  31, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> +    { 33750000,  33750000, 1,  33, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
>> +    { 36000000,  36000000, 1,  36, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    { 40000000,  40000000, 1,  80, 2, 2, 2, 12, 2, 2, 2, 0, 0},
>> +    { 46970000,  46970000, 1,  46, 2, 1, 1,  3, 0, 3, 4, 0, 0xf851eb},
>> +    { 49500000,  49500000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> +    { 49000000,  49000000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    { 50000000,  50000000, 1,  50, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    { 54000000,  54000000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    { 54054000,  54054000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0x0dd2f1},
>> +    { 57284000,  57284000, 1,  57, 2, 1, 1,  3, 0, 3, 4, 0, 0x48b439},
>> +    { 58230000,  58230000, 1,  58, 2, 1, 1,  3, 0, 3, 4, 0, 0x3ae147},
>> +    { 59341000,  59341000, 1,  59, 2, 1, 1,  3, 0, 3, 4, 0, 0x574bc6},
>> +    { 59400000,  59400000, 1,  99, 3, 1, 1,  1, 3, 3, 4, 0, 0},
>> +    { 65000000,  65000000, 1, 130, 2, 2, 2,  12, 0, 2, 2, 0, 0},
>> +    { 68250000,  68250000, 1, 68,  2, 1, 1,  3,  0, 3, 4, 0, 0x3fffff},
>> +    { 71000000,  71000000, 1,  71, 2, 1, 1,  3, 0, 3,  4, 0, 0},
>> +    { 74176000,  74176000, 1,  98, 1, 2, 2,  1, 2, 3, 4, 0, 0xe6ae6b},
>> +    { 74250000,  74250000, 1,  99, 1, 2, 2,  1, 2, 3, 4, 0, 0},
>> +    { 75000000,  75000000, 1,  75, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    { 78750000,  78750000, 1,  78, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
>> +    { 79500000,  79500000, 1,  79, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> +    { 83500000,  83500000, 2, 167, 2, 1, 1,  1, 0, 0,  6, 0, 0},
>> +    { 83500000, 104375000, 1, 104, 2, 1, 1,  1, 1, 0,  5, 0, 0x600000},
>> +    { 84858000,  84858000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xdba5e2},
>> +    { 85500000,  85500000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
>> +    { 85750000,  85750000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
>> +    { 85800000,  85800000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcccccc},
>> +    { 88750000,  88750000, 1,  88, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
>> +    { 89910000,  89910000, 1,  89, 2, 1, 1,  3, 0, 3, 4, 0, 0xe8f5c1},
>> +    { 90000000,  90000000, 1,  90, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {101000000, 101000000, 1, 101, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {102250000, 102250000, 1, 102, 2, 1, 1,  3, 0, 3, 4, 0, 0x3fffff},
>> +    {106500000, 106500000, 1, 106, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> +    {108000000, 108000000, 1,  90, 3, 0, 0,  5, 0, 2,  2, 0, 0},
>> +    {119000000, 119000000, 1, 119, 2, 1, 1,  3, 0, 3,  4, 0, 0},
>> +    {131481000, 131481000, 1,  131, 2, 1, 1,  3, 0, 3,  4, 0, 0x7b22d1},
>> +    {135000000, 135000000, 1,  135, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {136750000, 136750000, 1,  136, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
>> +    {147180000, 147180000, 1,  147, 2, 1, 1,  3, 0, 3, 4, 0, 0x2e147a},
>> +    {148352000, 148352000, 1,  98, 1, 1, 1,  1, 2, 2, 2, 0, 0xe6ae6b},
>> +    {148500000, 148500000, 1,  99, 1, 1, 1,  1, 2, 2, 2, 0, 0},
>> +    {154000000, 154000000, 1, 154, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {156000000, 156000000, 1, 156, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {157000000, 157000000, 1, 157, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {162000000, 162000000, 1, 162, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {174250000, 174250000, 1, 145, 3, 0, 0,  5, 0, 2, 2, 0, 0x355555},
>> +    {174500000, 174500000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> +    {174570000, 174570000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x91eb84},
>> +    {175500000, 175500000, 1, 175, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> +    {185590000, 185590000, 1, 185, 2, 1, 1,  3, 0, 3, 4, 0, 0x970a3c},
>> +    {187000000, 187000000, 1, 187, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {241500000, 241500000, 1, 161, 1, 1, 1,  4, 0, 2,  2, 0, 0},
>> +    {241700000, 241700000, 1, 241, 2, 1, 1,  3, 0, 3,  4, 0, 0xb33332},
>> +    {262750000, 262750000, 1, 262, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
>> +    {296500000, 296500000, 1, 296, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
>> +    {296703000, 296703000, 1,  98, 0, 1, 1,  1, 0, 2,  2, 0, 0xe6ae6b},
>> +    {297000000, 297000000, 1,  99, 0, 1, 1,  1, 0, 2,  2, 0, 0},
>> +    {594000000, 594000000, 1,  99, 0, 2, 0,  1, 0, 1,  1, 0, 0},
>> +    {0, 0, 0,  0, 0, 0, 0,  0, 0, 0,  0, 0, 0},
>> +};
>> +
>> +static const struct post_pll_config post_pll_cfg_table[] = {
>> +    {25200000,    1, 80, 13, 3, 1},
>> +    {27000000,    1, 40, 11, 3, 1},
>> +    {33750000,    1, 40, 11, 3, 1},
>> +    {49000000,    1, 20, 1, 3, 3},
>> +    {241700000, 1, 20, 1, 3, 3},
>> +    {297000000, 4, 20, 0, 0, 3},
>> +    {594000000, 4, 20, 0, 0, 0},
>> +    { /* sentinel */ }
>> +};
>> +
>> +inline u8 hdmi_readb(struct starfive_hdmi *hdmi, u16 offset)
>> +{
>> +    return readl_relaxed(hdmi->regs + (offset) * 0x04);
>> +}
>> +
>> +inline void hdmi_writeb(struct starfive_hdmi *hdmi, u16 offset, u32 val)
>> +{
>> +    writel_relaxed(val, hdmi->regs + (offset) * 0x04);
>> +}
>> +
>> +inline void hdmi_modb(struct starfive_hdmi *hdmi, u16 offset,
>> +                 u32 msk, u32 val)
>> +{
>> +    u8 temp = hdmi_readb(hdmi, offset) & ~msk;
>> +
>> +    temp |= val & msk;
>> +    hdmi_writeb(hdmi, offset, temp);
>> +}
>> +
>> +static int starfive_hdmi_enable_clk_deassert_rst(struct device *dev, struct starfive_hdmi *hdmi)
>> +{
>> +    int ret;
>> +
>> +    ret = clk_prepare_enable(hdmi->sys_clk);
>> +    if (ret) {
>> +        dev_err(dev, "Cannot enable HDMI sys clock: %d\n", ret);
>> +        return ret;
>> +    }
>> +
>> +    ret = clk_prepare_enable(hdmi->mclk);
>> +    if (ret) {
>> +        dev_err(dev, "Cannot enable HDMI mclk clock: %d\n", ret);
>> +        goto err_mclk;
>> +    }
>> +    ret = clk_prepare_enable(hdmi->bclk);
> 
> This code begs to use clk_bulk instead.
> 
>> +    if (ret) {
>> +        dev_err(dev, "Cannot enable HDMI bclk clock: %d\n", ret);
>> +        goto err_bclk;
>> +    }
>> +    ret = reset_control_deassert(hdmi->tx_rst);
>> +    if (ret < 0) {
>> +        dev_err(dev, "failed to deassert tx_rst\n");
>> +        goto err_rst;
>> +    }
>> +    return 0;
>> +
>> +err_rst:
>> +    clk_disable_unprepare(hdmi->bclk);
>> +err_bclk:
>> +    clk_disable_unprepare(hdmi->mclk);
>> +err_mclk:
>> +    clk_disable_unprepare(hdmi->sys_clk);
>> +    return ret;
>> +}
>> +
>> +static void starfive_hdmi_disable_clk_assert_rst(struct device *dev, struct starfive_hdmi *hdmi)
>> +{
>> +    int ret;
>> +
>> +    ret = reset_control_assert(hdmi->tx_rst);
>> +    if (ret < 0)
>> +        dev_err(dev, "failed to assert tx_rst\n");
>> +
>> +    clk_disable_unprepare(hdmi->sys_clk);
>> +    clk_disable_unprepare(hdmi->mclk);
>> +    clk_disable_unprepare(hdmi->bclk);
>> +}
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int hdmi_system_pm_suspend(struct device *dev)
>> +{
>> +    return pm_runtime_force_suspend(dev);
>> +}
>> +
>> +static int hdmi_system_pm_resume(struct device *dev)
>> +{
>> +    return pm_runtime_force_resume(dev);
>> +}
>> +#endif
>> +
>> +#ifdef CONFIG_PM
>> +static int hdmi_runtime_suspend(struct device *dev)
>> +{
>> +    struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
>> +
>> +    starfive_hdmi_disable_clk_assert_rst(dev, hdmi);
>> +
>> +    return 0;
>> +}
>> +
>> +static int hdmi_runtime_resume(struct device *dev)
>> +{
>> +    struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
>> +
>> +    return starfive_hdmi_enable_clk_deassert_rst(dev, hdmi);
>> +}
>> +#endif
>> +
>> +static void starfive_hdmi_tx_phy_power_down(struct starfive_hdmi *hdmi)
>> +{
>> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
>> +}
>> +
>> +static void starfive_hdmi_tx_phy_power_on(struct starfive_hdmi *hdmi)
>> +{
>> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
> 
> It looks like one can inline these two helpers w/o too much troubles.
> 
>> +}
>> +
>> +static void starfive_hdmi_config_pll(struct starfive_hdmi *hdmi)
>> +{
>> +    u32 val;
>> +    u8 reg_1ad_value = hdmi->post_cfg->post_div_en ?
>> +         hdmi->post_cfg->postdiv : 0x00;
>> +    u8 reg_1aa_value = hdmi->post_cfg->post_div_en ?
>> +         0x0e : 0x02;
>> +
>> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN);
>> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1,
>> +            STARFIVE_POST_PLL_POST_DIV_ENABLE |
>> +            STARFIVE_POST_PLL_REFCLK_SEL_TMDS |
>> +            STARFIVE_POST_PLL_POWER_DOWN);
>> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_1, STARFIVE_PRE_PLL_PRE_DIV(hdmi->pre_cfg->prediv));
>> +
>> +    val = STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE | STARFIVE_SPREAD_SPECTRUM_MOD_DOWN;
>> +    if (!hdmi->pre_cfg->fracdiv)
>> +        val |= STARFIVE_PRE_PLL_FRAC_DIV_DISABLE;
>> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_2,
>> +            STARFIVE_PRE_PLL_FB_DIV_11_8(hdmi->pre_cfg->fbdiv) | val);
>> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_3,
>> +            STARFIVE_PRE_PLL_FB_DIV_7_0(hdmi->pre_cfg->fbdiv));
>> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_4,
>> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_C(hdmi->pre_cfg->tmds_div_c) |
>> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_A(hdmi->pre_cfg->tmds_div_a) |
>> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_B(hdmi->pre_cfg->tmds_div_b));
>> +
>> +    if (hdmi->pre_cfg->fracdiv) {
>> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_L,
>> +                STARFIVE_PRE_PLL_FRAC_DIV_7_0(hdmi->pre_cfg->fracdiv));
>> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_M,
>> +                STARFIVE_PRE_PLL_FRAC_DIV_15_8(hdmi->pre_cfg->fracdiv));
>> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_H,
>> +                STARFIVE_PRE_PLL_FRAC_DIV_23_16(hdmi->pre_cfg->fracdiv));
>> +    }
>> +
>> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_5,
>> +            STARFIVE_PRE_PLL_PCLK_DIV_A(hdmi->pre_cfg->pclk_div_a) |
>> +            STARFIVE_PRE_PLL_PCLK_DIV_B(hdmi->pre_cfg->pclk_div_b));
>> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_6,
>> +            STARFIVE_PRE_PLL_PCLK_DIV_C(hdmi->pre_cfg->pclk_div_c) |
>> +            STARFIVE_PRE_PLL_PCLK_DIV_D(hdmi->pre_cfg->pclk_div_d));
>> +
>> +    /*pre-pll power down*/
>> +    hdmi_modb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN, 0);
>> +
>> +    hdmi_modb(hdmi, STARFIVE_POST_PLL_DIV_2, STARFIVE_POST_PLL_Pre_DIV_MASK,
>> +          STARFIVE_POST_PLL_PRE_DIV(hdmi->post_cfg->prediv));
>> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_3, hdmi->post_cfg->fbdiv & 0xff);
>> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_4, reg_1ad_value);
>> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1, reg_1aa_value);
>> +}
>> +
>> +static void starfive_hdmi_tmds_driver_on(struct starfive_hdmi *hdmi)
>> +{
>> +    hdmi_modb(hdmi, STARFIVE_TMDS_CONTROL,
>> +          STARFIVE_TMDS_DRIVER_ENABLE, STARFIVE_TMDS_DRIVER_ENABLE);
>> +}
>> +
>> +static void starfive_hdmi_sync_tmds(struct starfive_hdmi *hdmi)
>> +{
>> +    /*first send 0 to this bit, then send 1 and keep 1 into this bit*/
>> +    hdmi_writeb(hdmi, HDMI_SYNC, 0x0);
>> +    hdmi_writeb(hdmi, HDMI_SYNC, 0x1);
>> +}
>> +
>> +static void starfive_hdmi_i2c_init(struct starfive_hdmi *hdmi)
>> +{
>> +    int ddc_bus_freq;
>> +
>> +    ddc_bus_freq = (clk_get_rate(hdmi->sys_clk) >> 2) / HDMI_SCL_RATE;
>> +
>> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
>> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
>> +
>> +    /* Clear the EDID interrupt flag and mute the interrupt */
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> +}
>> +
>> +static const
>> +struct pre_pll_config *starfive_hdmi_phy_get_pre_pll_cfg(struct starfive_hdmi *hdmi,
>> +                             unsigned long rate)
>> +{
>> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
>> +
>> +    rate = (rate / 1000) * 1000;
>> +    for (; cfg->pixclock != 0; cfg++)
>> +        if (cfg->tmdsclock == rate && cfg->pixclock == rate)
>> +            break;
>> +
>> +    if (cfg->pixclock == 0)
>> +        return ERR_PTR(-EINVAL);
>> +
>> +    return cfg;
>> +}
>> +
>> +static int starfive_hdmi_phy_clk_set_rate(struct starfive_hdmi *hdmi)
>> +{
>> +    hdmi->post_cfg = post_pll_cfg_table;
>> +
>> +    hdmi->pre_cfg = starfive_hdmi_phy_get_pre_pll_cfg(hdmi, hdmi->tmds_rate);
>> +    if (IS_ERR(hdmi->pre_cfg))
>> +        return PTR_ERR(hdmi->pre_cfg);
>> +
>> +    for (; hdmi->post_cfg->tmdsclock != 0; hdmi->post_cfg++)
>> +        if (hdmi->tmds_rate <= hdmi->post_cfg->tmdsclock)
>> +            break;
>> +
>> +    starfive_hdmi_config_pll(hdmi);
>> +
>> +    return 0;
>> +}
>> +
>> +static int starfive_hdmi_config_video_timing(struct starfive_hdmi *hdmi,
>> +                         struct drm_display_mode *mode)
>> +{
>> +    int value;
>> +    /* Set detail external video timing */
>> +    value = mode->htotal;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
> 
> As you have hdmi_writeb already, adding hdmi_writew will help here.
> 
>> +
>> +    value = mode->htotal - mode->hdisplay;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->htotal - mode->hsync_start;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->hsync_end - mode->hsync_start;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->vtotal;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->vtotal - mode->vdisplay;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
>> +
>> +    value = mode->vtotal - mode->vsync_start;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
>> +
>> +    value = mode->vsync_end - mode->vsync_start;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
>> +
>> +    /* Set detail external video timing polarity and interlace mode */
>> +    value = v_EXTERANL_VIDEO(1);
>> +    value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
>> +        v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
>> +    value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
>> +        v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
>> +    value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
>> +        v_INETLACE(1) : v_INETLACE(0);
>> +
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
>> +    return 0;
>> +}
>> +
>> +static int starfive_hdmi_setup(struct starfive_hdmi *hdmi,
>> +                   struct drm_display_mode *mode)
>> +{
>> +    hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
>> +    hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
>> +    hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
>> +
>> +    hdmi->tmds_rate = mode->clock * 1000;
>> +    starfive_hdmi_phy_clk_set_rate(hdmi);
>> +
>> +    while (!(hdmi_readb(hdmi, STARFIVE_PRE_PLL_LOCK_STATUS) & 0x1))
>> +        continue;
>> +    while (!(hdmi_readb(hdmi, STARFIVE_POST_PLL_LOCK_STATUS) & 0x1))
>> +        continue;
> 
> Please use read_iopoll_timeout here.
> 
>> +
>> +    /*turn on LDO*/
>> +    hdmi_writeb(hdmi, STARFIVE_LDO_CONTROL, STARFIVE_LDO_ENABLE);
>> +    /*turn on serializer*/
>> +    hdmi_writeb(hdmi, STARFIVE_SERIALIER_CONTROL, STARFIVE_SERIALIER_ENABLE);
>> +
>> +    starfive_hdmi_tx_phy_power_down(hdmi);
>> +    starfive_hdmi_config_video_timing(hdmi, mode);
>> +    starfive_hdmi_tx_phy_power_on(hdmi);
>> +
>> +    starfive_hdmi_tmds_driver_on(hdmi);
>> +    starfive_hdmi_sync_tmds(hdmi);
>> +
>> +    return 0;
>> +}
>> +
>> +static void starfive_hdmi_encoder_mode_set(struct drm_encoder *encoder,
>> +                       struct drm_display_mode *mode,
>> +                       struct drm_display_mode *adj_mode)
>> +{
>> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
>> +
>> +    drm_mode_copy(&hdmi->previous_mode, adj_mode);
> 
> Why do you need it?
> 
>> +}
>> +
>> +static void starfive_hdmi_encoder_enable(struct drm_encoder *encoder)
>> +{
>> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
>> +    struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
>> +    int ret, idx;
>> +    struct drm_device *drm = hdmi->connector.dev;
>> +
>> +    if (drm && !drm_dev_enter(drm, &idx))
>> +        return;
>> +
>> +    ret = pm_runtime_get_sync(hdmi->dev);
>> +    if (ret < 0)
>> +        return;
>> +    mdelay(10);
>> +    starfive_hdmi_setup(hdmi, mode);
>> +
>> +    if (drm)
>> +        drm_dev_exit(idx);
>> +}
>> +
>> +static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
>> +{
>> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
>> +
>> +    int idx;
>> +    struct drm_device *drm = hdmi->connector.dev;
>> +
>> +    if (drm && !drm_dev_enter(drm, &idx))
>> +        return;
>> +
>> +    pm_runtime_put(hdmi->dev);
>> +
>> +    if (drm)
>> +        drm_dev_exit(idx);
>> +}
>> +
>> +static int
>> +starfive_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
>> +                   struct drm_crtc_state *crtc_state,
>> +                   struct drm_connector_state *conn_state)
>> +{
>> +    bool valid = false;
>> +    struct drm_display_mode *mode = &crtc_state->adjusted_mode;
>> +    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
>> +
>> +    vs_crtc_state->encoder_type = encoder->encoder_type;
>> +    vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
>> +
>> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
>> +    int pclk = mode->clock * 1000;
>> +
>> +    for (; cfg->pixclock != 0; cfg++) {
>> +        if (pclk == cfg->pixclock) {
>> +            if (pclk > 297000000)
> 
> Magic value.
> 
>> +                continue;
>> +
>> +            valid = true;
>> +            break;
>> +        }
>> +    }
>> +
>> +    return (valid) ? 0 : -EINVAL;
>> +}
>> +
>> +static const struct drm_encoder_helper_funcs starfive_hdmi_encoder_helper_funcs = {
>> +    .enable     = starfive_hdmi_encoder_enable,
>> +    .disable    = starfive_hdmi_encoder_disable,
>> +    .mode_set   = starfive_hdmi_encoder_mode_set,
>> +    .atomic_check = starfive_hdmi_encoder_atomic_check,
>> +};
>> +
>> +static enum drm_connector_status
>> +starfive_hdmi_connector_detect(struct drm_connector *connector, bool force)
>> +{
>> +    struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
>> +    struct drm_device *drm = hdmi->connector.dev;
>> +    int ret;
>> +    int idx;
>> +
>> +    if (drm && !drm_dev_enter(drm, &idx))
>> +        return connector_status_disconnected;
>> +
>> +    ret = pm_runtime_get_sync(hdmi->dev);
>> +    if (ret < 0)
>> +        return ret;
>> +
>> +    ret = (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
>> +        connector_status_connected : connector_status_disconnected;
>> +    pm_runtime_put(hdmi->dev);
>> +
>> +    if (drm)
>> +        drm_dev_exit(idx);
>> +
>> +    return ret;
>> +}
>> +
>> +static int starfive_hdmi_connector_get_modes(struct drm_connector *connector)
>> +{
>> +    struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
>> +    struct edid *edid;
>> +    int ret = 0;
>> +
>> +    if (!hdmi->ddc)
>> +        return 0;
>> +    ret = pm_runtime_get_sync(hdmi->dev);
>> +    if (ret < 0)
>> +        return ret;
>> +
>> +    edid = drm_get_edid(connector, hdmi->ddc);
>> +    if (edid) {
>> +        hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
>> +        hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
>> +        drm_connector_update_edid_property(connector, edid);
>> +        ret = drm_add_edid_modes(connector, edid);
>> +        kfree(edid);
>> +    }
>> +    pm_runtime_put(hdmi->dev);
> 
> Can you use drm_connector_helper_get_modes_from_ddc() here? And then drm_display_info.is_hdmi and .has_audio.
> 
>> +
>> +    return ret;
>> +}
>> +
>> +static enum drm_mode_status
>> +starfive_hdmi_connector_mode_valid(struct drm_connector *connector,
>> +                   struct drm_display_mode *mode)
>> +{
>> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
>> +    int pclk = mode->clock * 1000;
>> +    bool valid = false;
>> +
>> +    for (; cfg->pixclock != 0; cfg++) {
>> +        if (pclk == cfg->pixclock) {
>> +            if (pclk > 297000000)
> 
> magic value
> 
>> +                continue;
>> +
>> +            valid = true;
>> +            break;
>> +        }
>> +    }
>> +
>> +    return (valid) ? MODE_OK : MODE_BAD;
>> +}
>> +
>> +static int
>> +starfive_hdmi_probe_single_connector_modes(struct drm_connector *connector,
>> +                       u32 maxX, u32 maxY)
>> +{
>> +    return drm_helper_probe_single_connector_modes(connector, 3840, 2160);
>> +}
>> +
>> +static const struct drm_connector_funcs starfive_hdmi_connector_funcs = {
>> +    .fill_modes = starfive_hdmi_probe_single_connector_modes,
>> +    .detect = starfive_hdmi_connector_detect,
>> +    .reset = drm_atomic_helper_connector_reset,
>> +    .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
>> +    .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>> +};
>> +
>> +static struct drm_connector_helper_funcs starfive_hdmi_connector_helper_funcs = {
>> +    .get_modes = starfive_hdmi_connector_get_modes,
>> +    .mode_valid = starfive_hdmi_connector_mode_valid,
>> +};
>> +
>> +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
>> +{
>> +    struct drm_encoder *encoder = &hdmi->encoder;
>> +    struct device *dev = hdmi->dev;
>> +
>> +    encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
>> +
>> +    /*
>> +     * If we failed to find the CRTC(s) which this encoder is
>> +     * supposed to be connected to, it's because the CRTC has
>> +     * not been registered yet.  Defer probing, and hope that
>> +     * the required CRTC is added later.
>> +     */
>> +    if (encoder->possible_crtcs == 0)
>> +        return -EPROBE_DEFER;
>> +
>> +    drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
>> +
>> +    hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
>> +
>> +    drm_connector_helper_add(&hdmi->connector,
>> +                 &starfive_hdmi_connector_helper_funcs);
>> +    drmm_connector_init(drm, &hdmi->connector,
>> +                &starfive_hdmi_connector_funcs,
>> +                DRM_MODE_CONNECTOR_HDMIA,
> 
> On an embedded device one can not be so sure. There can be MHL or HDMI Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.
> 
>> +                hdmi->ddc);
>> +
>> +    drm_connector_attach_encoder(&hdmi->connector, encoder);
>> +
>> +    return 0;
>> +}
>> +
>> +static irqreturn_t starfive_hdmi_i2c_irq(struct starfive_hdmi *hdmi)
>> +{
>> +    struct starfive_hdmi_i2c *i2c = hdmi->i2c;
>> +    u8 stat;
>> +
>> +    stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
>> +    if (!(stat & m_INT_EDID_READY))
>> +        return IRQ_NONE;
>> +
>> +    /* Clear HDMI EDID interrupt flag */
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> +
>> +    complete(&i2c->cmp);
>> +
>> +    return IRQ_HANDLED;
>> +}
>> +
>> +static irqreturn_t starfive_hdmi_hardirq(int irq, void *dev_id)
>> +{
>> +    struct starfive_hdmi *hdmi = dev_id;
>> +    irqreturn_t ret = IRQ_NONE;
>> +    u8 interrupt;
>> +
>> +    if (hdmi->i2c)
>> +        ret = starfive_hdmi_i2c_irq(hdmi);
>> +
>> +    interrupt = hdmi_readb(hdmi, HDMI_STATUS);
>> +    if (interrupt & m_INT_HOTPLUG) {
>> +        hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
>> +        ret = IRQ_WAKE_THREAD;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static irqreturn_t starfive_hdmi_irq(int irq, void *dev_id)
>> +{
>> +    struct starfive_hdmi *hdmi = dev_id;
>> +
>> +    drm_connector_helper_hpd_irq_event(&hdmi->connector);
>> +
>> +    return IRQ_HANDLED;
>> +}
>> +
>> +static int starfive_hdmi_i2c_read(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
>> +{
>> +    int length = msgs->len;
>> +    u8 *buf = msgs->buf;
>> +    int ret;
>> +
>> +    ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10);
> 
> read_poll_timeout
Hi Dmitry:
I think using wait_for_completion_timeout here is a bit clearer than read_poll_timeout logic
I2c_read has hardware interrupt dependency. so need wait the interrupt completion.
also it comes with timeout detection.

> 
>> +    if (!ret)
>> +        return -EAGAIN;
>> +
>> +    while (length--)
>> +        *buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
>> +
>> +    return 0;
>> +}
>> +
>> +static int starfive_hdmi_i2c_write(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
>> +{
>> +    /*
>> +     * The DDC module only support read EDID message, so
>> +     * we assume that each word write to this i2c adapter
>> +     * should be the offset of EDID word address.
>> +     */
>> +    if (msgs->len != 1 ||
>> +        (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR))
>> +        return -EINVAL;
>> +
>> +    reinit_completion(&hdmi->i2c->cmp);
>> +
>> +    if (msgs->addr == DDC_SEGMENT_ADDR)
>> +        hdmi->i2c->segment_addr = msgs->buf[0];
>> +    if (msgs->addr == DDC_ADDR)
>> +        hdmi->i2c->ddc_addr = msgs->buf[0];
>> +
>> +    /* Set edid fifo first addr */
>> +    hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
>> +
>> +    /* Set edid word address 0x00/0x80 */
>> +    hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
>> +
>> +    /* Set edid segment pointer */
>> +    hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
>> +
>> +    return 0;
>> +}
>> +
>> +static int starfive_hdmi_i2c_xfer(struct i2c_adapter *adap,
>> +                  struct i2c_msg *msgs, int num)
>> +{
>> +    struct starfive_hdmi *hdmi = i2c_get_adapdata(adap);
>> +    struct starfive_hdmi_i2c *i2c = hdmi->i2c;
>> +    int i, ret = 0;
>> +
>> +    mutex_lock(&i2c->lock);
>> +
>> +    /* Clear the EDID interrupt flag and unmute the interrupt */
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> +
>> +    for (i = 0; i < num; i++) {
>> +        DRM_DEV_DEBUG(hdmi->dev,
>> +                  "xfer: num: %d/%d, len: %d, flags: %#x\n",
>> +                  i + 1, num, msgs[i].len, msgs[i].flags);
>> +
>> +        if (msgs[i].flags & I2C_M_RD)
>> +            ret = starfive_hdmi_i2c_read(hdmi, &msgs[i]);
>> +        else
>> +            ret = starfive_hdmi_i2c_write(hdmi, &msgs[i]);
>> +
>> +        if (ret < 0)
>> +            break;
>> +    }
>> +
>> +    if (!ret)
>> +        ret = num;
>> +
>> +    /* Mute HDMI EDID interrupt */
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
>> +
>> +    mutex_unlock(&i2c->lock);
>> +
>> +    return ret;
>> +}
>> +
>> +static u32 starfive_hdmi_i2c_func(struct i2c_adapter *adapter)
>> +{
>> +    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
>> +}
>> +
>> +static const struct i2c_algorithm starfive_hdmi_algorithm = {
>> +    .master_xfer    = starfive_hdmi_i2c_xfer,
>> +    .functionality    = starfive_hdmi_i2c_func,
>> +};
>> +
>> +static struct i2c_adapter *starfive_hdmi_i2c_adapter(struct starfive_hdmi *hdmi)
>> +{
>> +    struct i2c_adapter *adap;
>> +    struct starfive_hdmi_i2c *i2c;
>> +    int ret;
>> +
>> +    i2c = drmm_kzalloc(hdmi->drm_dev, sizeof(*i2c), GFP_KERNEL);
>> +    if (!i2c)
>> +        return ERR_PTR(-ENOMEM);
>> +
>> +    mutex_init(&i2c->lock);
>> +    init_completion(&i2c->cmp);
>> +
>> +    adap = &i2c->adap;
>> +    adap->class = I2C_CLASS_DDC;
>> +    adap->owner = THIS_MODULE;
>> +    adap->dev.parent = hdmi->dev;
>> +    adap->algo = &starfive_hdmi_algorithm;
>> +    strscpy(adap->name, "Starfive HDMI", sizeof(adap->name));
>> +    i2c_set_adapdata(adap, hdmi);
>> +
>> +    ret = devm_i2c_add_adapter(hdmi->dev, adap);
>> +    if (ret) {
>> +        drm_warn(hdmi->drm_dev, "cannot add %s I2C adapter\n", adap->name);
>> +        return ERR_PTR(ret);
>> +    }
>> +
>> +    hdmi->i2c = i2c;
>> +
>> +    drm_info(hdmi->drm_dev, "registered %s I2C bus driver success\n", adap->name);
>> +
>> +    return adap;
>> +}
>> +
>> +static int starfive_hdmi_get_clk_rst(struct device *dev, struct starfive_hdmi *hdmi)
>> +{
>> +    hdmi->sys_clk = devm_clk_get(dev, "sysclk");
>> +    if (IS_ERR(hdmi->sys_clk)) {
>> +        dev_err(dev, "Unable to get HDMI sysclk clk\n");
>> +        return PTR_ERR(hdmi->sys_clk);
>> +    }
>> +    hdmi->mclk = devm_clk_get(dev, "mclk");
>> +    if (IS_ERR(hdmi->mclk)) {
>> +        dev_err(dev, "Unable to get HDMI mclk clk\n");
>> +        return PTR_ERR(hdmi->mclk);
>> +    }
>> +    hdmi->bclk = devm_clk_get(dev, "bclk");
>> +    if (IS_ERR(hdmi->bclk)) {
>> +        dev_err(dev, "Unable to get HDMI bclk clk\n");
>> +        return PTR_ERR(hdmi->bclk);
>> +    }
>> +    hdmi->tx_rst = devm_reset_control_get_by_index(dev, 0);
>> +    if (IS_ERR(hdmi->tx_rst)) {
>> +        dev_err(dev, "failed to get tx_rst reset\n");
>> +        return PTR_ERR(hdmi->tx_rst);
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int starfive_hdmi_bind(struct device *dev, struct device *master,
>> +                  void *data)
>> +{
>> +    struct platform_device *pdev = to_platform_device(dev);
>> +    struct drm_device *drm = dev_get_drvdata(master);
>> +    struct starfive_hdmi *hdmi;
>> +    struct resource *iores;
>> +    int irq;
>> +    int ret;
>> +
>> +    hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
>> +    if (!hdmi)
>> +        return -ENOMEM;
>> +
>> +    hdmi = drmm_simple_encoder_alloc(drm, struct starfive_hdmi,
>> +                     encoder, DRM_MODE_ENCODER_TMDS);
>> +
>> +    hdmi->dev = dev;
>> +    hdmi->drm_dev = drm;
>> +    dev_set_drvdata(dev, hdmi);
>> +
>> +    iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +    hdmi->regs = devm_ioremap_resource(dev, iores);
> 
> This should go to probe
> 
>> +    if (IS_ERR(hdmi->regs))
>> +        return PTR_ERR(hdmi->regs);
>> +
>> +    ret = starfive_hdmi_get_clk_rst(dev, hdmi);
>> +    if (ret < 0)
>> +        return ret;
> 
> And this
> 
>> +
>> +    ret = devm_pm_runtime_enable(dev);
>> +    if (ret)
>> +        return ret;
>> +
>> +    ret = pm_runtime_resume_and_get(dev);
>> +    if (ret)
>> +        return ret;
>> +
>> +    irq = platform_get_irq(pdev, 0);
> 
> And this
> 
>> +    if (irq < 0) {
>> +        ret = irq;
>> +        goto err_put_runtime_pm;
>> +    }
>> +
>> +    hdmi->ddc = starfive_hdmi_i2c_adapter(hdmi);
>> +    if (IS_ERR(hdmi->ddc)) {
>> +        ret = PTR_ERR(hdmi->ddc);
>> +        hdmi->ddc = NULL;
>> +        goto err_put_runtime_pm;
>> +    }
>> +
>> +    starfive_hdmi_i2c_init(hdmi);
>> +
>> +    ret = starfive_hdmi_register(drm, hdmi);
>> +    if (ret)
>> +        goto err_put_adapter;
>> +
>> +    /* Unmute hotplug interrupt */
>> +    hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
>> +
>> +    ret = devm_request_threaded_irq(dev, irq, starfive_hdmi_hardirq,
>> +                    starfive_hdmi_irq, IRQF_SHARED,
>> +                    dev_name(dev), hdmi);
>> +    if (ret < 0)
>> +        goto err_put_adapter;
>> +
>> +    pm_runtime_put_sync(dev);
>> +
>> +    return 0;
>> +
>> +err_put_adapter:
>> +    i2c_put_adapter(hdmi->ddc);
>> +err_put_runtime_pm:
>> +    pm_runtime_put_sync(dev);
>> +
>> +    return ret;
>> +}
>> +
>> +static const struct component_ops starfive_hdmi_ops = {
>> +    .bind    = starfive_hdmi_bind,
>> +};
>> +
>> +static int starfive_hdmi_probe(struct platform_device *pdev)
>> +{
>> +    return component_add(&pdev->dev, &starfive_hdmi_ops);
>> +}
>> +
>> +static int starfive_hdmi_remove(struct platform_device *pdev)
>> +{
>> +    component_del(&pdev->dev, &starfive_hdmi_ops);
>> +
>> +    return 0;
>> +}
>> +
>> +static const struct dev_pm_ops hdmi_pm_ops = {
>> +    SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
>> +    SET_LATE_SYSTEM_SLEEP_PM_OPS(hdmi_system_pm_suspend, hdmi_system_pm_resume)
>> +};
>> +
>> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
>> +    { .compatible = "starfive,jh7110-inno-hdmi",},
>> +    {},
>> +};
>> +MODULE_DEVICE_TABLE(of, starfive_hdmi_dt_ids);
>> +
>> +struct platform_driver starfive_hdmi_driver = {
>> +    .probe  = starfive_hdmi_probe,
>> +    .remove = starfive_hdmi_remove,
>> +    .driver = {
>> +        .name = "starfive-hdmi",
>> +        .of_match_table = starfive_hdmi_dt_ids,
>> +        .pm = &hdmi_pm_ops,
>> +    },
>> +};
>> +
>> +MODULE_AUTHOR("StarFive Corporation");
>> +MODULE_DESCRIPTION("Starfive HDMI Driver");
>> diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.h b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
>> new file mode 100644
>> index 000000000..1b9a11bca
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
>> @@ -0,0 +1,295 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
>> + */
>> +
>> +#ifndef __STARFIVE_HDMI_H__
>> +#define __STARFIVE_HDMI_H__
>> +
>> +#include <drm/bridge/dw_hdmi.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_edid.h>
>> +#include <drm/drm_of.h>
>> +#include <drm/drm_probe_helper.h>
>> +#include <drm/drm_simple_kms_helper.h>
>> +#include <linux/bitfield.h>
>> +#include <linux/bits.h>
>> +
>> +#define DDC_SEGMENT_ADDR        0x30
>> +
>> +#define HDMI_SCL_RATE            (100 * 1000)
>> +#define DDC_BUS_FREQ_L            0x4b
>> +#define DDC_BUS_FREQ_H            0x4c
>> +
>> +#define HDMI_SYS_CTRL            0x00
>> +#define m_RST_ANALOG            BIT(6)
>> +#define v_RST_ANALOG            0
>> +#define v_NOT_RST_ANALOG        BIT(6)
>> +#define m_RST_DIGITAL            BIT(5)
>> +#define v_RST_DIGITAL            0
>> +#define v_NOT_RST_DIGITAL        BIT(5)
>> +#define m_REG_CLK_INV            BIT(4)
>> +#define v_REG_CLK_NOT_INV        0
>> +#define v_REG_CLK_INV            BIT(4)
>> +#define m_VCLK_INV            BIT(3)
>> +#define v_VCLK_NOT_INV            0
>> +#define v_VCLK_INV            BIT(3)
>> +#define m_REG_CLK_SOURCE        BIT(2)
>> +#define v_REG_CLK_SOURCE_TMDS        0
>> +#define v_REG_CLK_SOURCE_SYS        BIT(2)
>> +#define m_POWER                BIT(1)
>> +#define v_PWR_ON            0
>> +#define v_PWR_OFF            BIT(1)
>> +#define m_INT_POL            BIT(0)
>> +#define v_INT_POL_HIGH            1
>> +#define v_INT_POL_LOW            0
>> +
>> +#define HDMI_AV_MUTE            0x05
>> +#define m_AVMUTE_CLEAR            BIT(7)
>> +#define m_AVMUTE_ENABLE            BIT(6)
>> +#define m_AUDIO_MUTE            BIT(1)
>> +#define m_VIDEO_BLACK            BIT(0)
>> +#define v_AVMUTE_CLEAR(n)        ((n) << 7)
>> +#define v_AVMUTE_ENABLE(n)        ((n) << 6)
>> +#define v_AUDIO_MUTE(n)            ((n) << 1)
>> +#define v_VIDEO_MUTE(n)            ((n) << 0)
>> +
>> +#define HDMI_VIDEO_TIMING_CTL        0x08
>> +#define v_VSYNC_POLARITY(n)        ((n) << 3)
>> +#define v_HSYNC_POLARITY(n)        ((n) << 2)
>> +#define v_INETLACE(n)            ((n) << 1)
>> +#define v_EXTERANL_VIDEO(n)        ((n) << 0)
>> +
>> +#define HDMI_VIDEO_EXT_HTOTAL_L        0x09
>> +#define HDMI_VIDEO_EXT_HTOTAL_H        0x0a
>> +#define HDMI_VIDEO_EXT_HBLANK_L        0x0b
>> +#define HDMI_VIDEO_EXT_HBLANK_H        0x0c
>> +#define HDMI_VIDEO_EXT_HDELAY_L        0x0d
>> +#define HDMI_VIDEO_EXT_HDELAY_H        0x0e
>> +#define HDMI_VIDEO_EXT_HDURATION_L    0x0f
>> +#define HDMI_VIDEO_EXT_HDURATION_H    0x10
>> +#define HDMI_VIDEO_EXT_VTOTAL_L        0x11
>> +#define HDMI_VIDEO_EXT_VTOTAL_H        0x12
>> +#define HDMI_VIDEO_EXT_VBLANK        0x13
>> +#define HDMI_VIDEO_EXT_VDELAY        0x14
>> +#define HDMI_VIDEO_EXT_VDURATION    0x15
>> +
>> +#define HDMI_EDID_SEGMENT_POINTER    0x4d
>> +#define HDMI_EDID_WORD_ADDR        0x4e
>> +#define HDMI_EDID_FIFO_OFFSET        0x4f
>> +#define HDMI_EDID_FIFO_ADDR        0x50
>> +
>> +#define HDMI_INTERRUPT_MASK1        0xc0
>> +#define HDMI_INTERRUPT_STATUS1        0xc1
>> +#define    m_INT_ACTIVE_VSYNC        BIT(5)
>> +#define m_INT_EDID_READY        BIT(2)
>> +
>> +#define HDMI_STATUS            0xc8
>> +#define m_HOTPLUG            BIT(7)
>> +#define m_MASK_INT_HOTPLUG        BIT(5)
>> +#define m_INT_HOTPLUG            BIT(1)
>> +#define v_MASK_INT_HOTPLUG(n)        (((n) & 0x1) << 5)
> 
> It is untypical to have field defines which start with lowercase letter.
> 
>> +
>> +#define HDMI_SYNC                    0xce
>> +
>> +#define UPDATE(x, h, l)                    FIELD_PREP(GENMASK(h, l), x)
>> +
>> +/* REG: 0x1a0 */
>> +#define STARFIVE_PRE_PLL_CONTROL            0x1a0
>> +#define STARFIVE_PCLK_VCO_DIV_5_MASK            BIT(1)
>> +#define STARFIVE_PCLK_VCO_DIV_5(x)            UPDATE(x, 1, 1)
>> +#define STARFIVE_PRE_PLL_POWER_DOWN            BIT(0)
>> +
>> +/* REG: 0x1a1 */
>> +#define STARFIVE_PRE_PLL_DIV_1                0x1a1
>> +#define STARFIVE_PRE_PLL_PRE_DIV_MASK            GENMASK(5, 0)
>> +#define STARFIVE_PRE_PLL_PRE_DIV(x)            UPDATE(x, 5, 0)
>> +
>> +/* REG: 0x1a2 */
>> +#define STARFIVE_PRE_PLL_DIV_2                0x1a2
>> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DOWN        BIT(7)
>> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE        BIT(6)
>> +#define STARFIVE_PRE_PLL_FRAC_DIV_DISABLE        UPDATE(3, 5, 4)
>> +#define STARFIVE_PRE_PLL_FB_DIV_11_8_MASK        GENMASK(3, 0)
>> +#define STARFIVE_PRE_PLL_FB_DIV_11_8(x)            UPDATE((x) >> 8, 3, 0)
>> +
>> +/* REG: 0x1a3 */
>> +#define STARFIVE_PRE_PLL_DIV_3                0x1a3
>> +#define STARFIVE_PRE_PLL_FB_DIV_7_0(x)            UPDATE(x, 7, 0)
>> +
>> +/* REG: 0x1a4*/
>> +#define STARFIVE_PRE_PLL_DIV_4                0x1a4
>> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C_MASK        GENMASK(1, 0)
>> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C(x)        UPDATE(x, 1, 0)
>> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B_MASK        GENMASK(3, 2)
>> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B(x)        UPDATE(x, 3, 2)
>> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A_MASK        GENMASK(5, 4)
>> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A(x)        UPDATE(x, 5, 4)
>> +
>> +/* REG: 0x1a5 */
>> +#define STARFIVE_PRE_PLL_DIV_5                0x1a5
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_SHIFT        5
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_MASK        GENMASK(6, 5)
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_B(x)            UPDATE(x, 6, 5)
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_A_MASK        GENMASK(4, 0)
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_A(x)            UPDATE(x, 4, 0)
>> +
>> +/* REG: 0x1a6 */
>> +#define STARFIVE_PRE_PLL_DIV_6                0x1a6
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_SHIFT        5
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_MASK        GENMASK(6, 5)
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_C(x)            UPDATE(x, 6, 5)
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_D_MASK        GENMASK(4, 0)
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_D(x)            UPDATE(x, 4, 0)
>> +
>> +/* REG: 0x1a9 */
>> +#define STARFIVE_PRE_PLL_LOCK_STATUS            0x1a9
>> +
>> +/* REG: 0x1aa */
>> +#define STARFIVE_POST_PLL_DIV_1                0x1aa
>> +#define STARFIVE_POST_PLL_POST_DIV_ENABLE        GENMASK(3, 2)
>> +#define STARFIVE_POST_PLL_REFCLK_SEL_TMDS        BIT(1)
>> +#define STARFIVE_POST_PLL_POWER_DOWN            BIT(0)
>> +#define STARFIVE_POST_PLL_FB_DIV_8(x)            UPDATE(((x) >> 8) << 4, 4, 4)
>> +
>> +/* REG:0x1ab */
>> +#define STARFIVE_POST_PLL_DIV_2                0x1ab
>> +#define STARFIVE_POST_PLL_Pre_DIV_MASK            GENMASK(5, 0)
>> +#define STARFIVE_POST_PLL_PRE_DIV(x)            UPDATE(x, 5, 0)
>> +
>> +/* REG: 0x1ac */
>> +#define STARFIVE_POST_PLL_DIV_3                0x1ac
>> +#define STARFIVE_POST_PLL_FB_DIV_7_0(x)            UPDATE(x, 7, 0)
>> +
>> +/* REG: 0x1ad */
>> +#define STARFIVE_POST_PLL_DIV_4                0x1ad
>> +#define STARFIVE_POST_PLL_POST_DIV_MASK            GENMASK(2, 0)
>> +#define STARFIVE_POST_PLL_POST_DIV_2            0x0
>> +#define STARFIVE_POST_PLL_POST_DIV_4            0x1
>> +#define STARFIVE_POST_PLL_POST_DIV_8            0x3
>> +
>> +/* REG: 0x1af */
>> +#define STARFIVE_POST_PLL_LOCK_STATUS            0x1af
>> +
>> +/* REG: 0x1b0 */
>> +#define STARFIVE_BIAS_CONTROL                0x1b0
>> +#define STARFIVE_BIAS_ENABLE                BIT(2)
>> +
>> +/* REG: 0x1b2 */
>> +#define STARFIVE_TMDS_CONTROL                0x1b2
>> +#define STARFIVE_TMDS_CLK_DRIVER_EN            BIT(3)
>> +#define STARFIVE_TMDS_D2_DRIVER_EN            BIT(2)
>> +#define STARFIVE_TMDS_D1_DRIVER_EN            BIT(1)
>> +#define STARFIVE_TMDS_D0_DRIVER_EN            BIT(0)
>> +#define STARFIVE_TMDS_DRIVER_ENABLE            (STARFIVE_TMDS_CLK_DRIVER_EN | \
>> +                             STARFIVE_TMDS_D2_DRIVER_EN | \
>> +                             STARFIVE_TMDS_D1_DRIVER_EN | \
>> +                             STARFIVE_TMDS_D0_DRIVER_EN)
>> +
>> +/* REG: 0x1b4 */
>> +#define STARFIVE_LDO_CONTROL                0x1b4
>> +#define STARFIVE_LDO_D2_EN                BIT(2)
>> +#define STARFIVE_LDO_D1_EN                BIT(1)
>> +#define STARFIVE_LDO_D0_EN                BIT(0)
>> +#define STARFIVE_LDO_ENABLE                (STARFIVE_LDO_D2_EN | \
>> +                             STARFIVE_LDO_D1_EN | \
>> +                             STARFIVE_LDO_D0_EN)
>> +
>> +/* REG: 0x1be */
>> +#define STARFIVE_SERIALIER_CONTROL            0x1be
>> +#define STARFIVE_SERIALIER_D2_EN            BIT(6)
>> +#define STARFIVE_SERIALIER_D1_EN            BIT(5)
>> +#define STARFIVE_SERIALIER_D0_EN            BIT(4)
>> +#define STARFIVE_SERIALIER_EN                BIT(0)
>> +
>> +#define STARFIVE_SERIALIER_ENABLE            (STARFIVE_SERIALIER_D2_EN | \
>> +                             STARFIVE_SERIALIER_D1_EN | \
>> +                             STARFIVE_SERIALIER_D0_EN | \
>> +                             STARFIVE_SERIALIER_EN)
>> +
>> +/* REG: 0x1cc */
>> +#define STARFIVE_RX_CONTROL                0x1cc
>> +#define STARFIVE_RX_EN                    BIT(3)
>> +#define STARFIVE_RX_CHANNEL_2_EN            BIT(2)
>> +#define STARFIVE_RX_CHANNEL_1_EN            BIT(1)
>> +#define STARFIVE_RX_CHANNEL_0_EN            BIT(0)
>> +#define STARFIVE_RX_ENABLE                (STARFIVE_RX_EN | \
>> +                             STARFIVE_RX_CHANNEL_2_EN | \
>> +                             STARFIVE_RX_CHANNEL_1_EN | \
>> +                             STARFIVE_RX_CHANNEL_0_EN)
>> +
>> +/* REG: 0x1d1 */
>> +#define STARFIVE_PRE_PLL_FRAC_DIV_H            0x1d1
>> +#define STARFIVE_PRE_PLL_FRAC_DIV_23_16(x)        UPDATE((x) >> 16, 7, 0)
>> +/* REG: 0x1d2 */
>> +#define STARFIVE_PRE_PLL_FRAC_DIV_M            0x1d2
>> +#define STARFIVE_PRE_PLL_FRAC_DIV_15_8(x)        UPDATE((x) >> 8, 7, 0)
>> +/* REG: 0x1d3 */
>> +#define STARFIVE_PRE_PLL_FRAC_DIV_L            0x1d3
>> +#define STARFIVE_PRE_PLL_FRAC_DIV_7_0(x)        UPDATE(x, 7, 0)
>> +
>> +struct pre_pll_config {
>> +    unsigned long pixclock;
>> +    unsigned long tmdsclock;
>> +    u8 prediv;
>> +    u16 fbdiv;
>> +    u8 tmds_div_a;
>> +    u8 tmds_div_b;
>> +    u8 tmds_div_c;
>> +    u8 pclk_div_a;
>> +    u8 pclk_div_b;
>> +    u8 pclk_div_c;
>> +    u8 pclk_div_d;
>> +    u8 vco_div_5_en;
>> +    u32 fracdiv;
>> +};
>> +
>> +struct post_pll_config {
>> +    unsigned long tmdsclock;
>> +    u8 prediv;
>> +    u16 fbdiv;
>> +    u8 postdiv;
>> +    u8 post_div_en;
>> +    u8 version;
>> +};
>> +
>> +struct phy_config {
>> +    unsigned long    tmdsclock;
>> +    u8        regs[14];
>> +};
>> +
>> +struct hdmi_data_info {
>> +    int vic;
>> +    bool sink_is_hdmi;
>> +    bool sink_has_audio;
>> +    unsigned int enc_in_format;
>> +    unsigned int enc_out_format;
>> +    unsigned int colorimetry;
>> +};
>> +
>> +struct starfive_hdmi {
>> +    struct device *dev;
>> +    struct drm_device *drm_dev;
>> +    struct drm_encoder    encoder;
>> +    struct drm_connector    connector;
>> +
>> +    struct starfive_hdmi_i2c *i2c;
>> +
>> +    int irq;
>> +    struct clk *sys_clk;
>> +    struct clk *mclk;
>> +    struct clk *bclk;
>> +    struct reset_control *tx_rst;
>> +    void __iomem *regs;
>> +
>> +    struct i2c_adapter *ddc;
>> +
>> +    unsigned long tmds_rate;
>> +
>> +    struct hdmi_data_info    hdmi_data;
>> +    struct drm_display_mode previous_mode;
>> +    const struct pre_pll_config    *pre_cfg;
>> +    const struct post_pll_config    *post_cfg;
>> +};
>> +
>> +#endif /* __STARFIVE_HDMI_H__ */
>> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
>> index 3cd533cfa..9669354bd 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
>> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
>> @@ -126,6 +126,11 @@ static const struct component_master_ops vs_drm_ops = {
>>   static struct platform_driver *drm_sub_drivers[] = {
>>       &dc_platform_driver,
>>   +    /* connector + encoder*/
>> +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
>> +    &starfive_hdmi_driver,
>> +#endif
>> +
>>   };
>>     static struct component_match *vs_drm_match_add(struct device *dev)
>> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
>> index 369ad22d6..e90723419 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_drv.h
>> +++ b/drivers/gpu/drm/verisilicon/vs_drv.h
>> @@ -24,4 +24,8 @@ to_vs_drm_private(const struct drm_device *dev)
>>       return container_of(dev, struct vs_drm_device, base);
>>   }
>>   +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
>> +extern struct platform_driver starfive_hdmi_driver;
>> +#endif
>> +
>>   #endif /* __VS_DRV_H__ */
> 

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-11-13 12:11       ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-11-13 12:11 UTC (permalink / raw)
  To: Dmitry Baryshkov, dri-devel, devicetree, linux-kernel,
	linux-riscv, linux-media, linaro-mm-sig
  Cc: Conor Dooley, Albert Ou, Emil Renner Berthing, christian.koenig,
	Thomas Zimmermann, Bjorn Andersson, Chris Morgan, Maxime Ripard,
	Jagan Teki, Jack Zhu, Rob Herring, Palmer Dabbelt,
	Krzysztof Kozlowski, Paul Walmsley, Shengyang Chen,
	Changhuang Liang, Shawn Guo, Sumit Semwal



On 2023/10/26 6:23, Dmitry Baryshkov wrote:
> On 25/10/2023 13:39, Keith Zhao wrote:
>> add hdmi driver as encoder and connect
>>
>> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>> ---
>>   drivers/gpu/drm/verisilicon/Kconfig         |   8 +-
>>   drivers/gpu/drm/verisilicon/Makefile        |   1 +
>>   drivers/gpu/drm/verisilicon/starfive_hdmi.c | 949 ++++++++++++++++++++
>>   drivers/gpu/drm/verisilicon/starfive_hdmi.h | 295 ++++++
>>   drivers/gpu/drm/verisilicon/vs_drv.c        |   5 +
>>   drivers/gpu/drm/verisilicon/vs_drv.h        |   4 +
>>   6 files changed, 1261 insertions(+), 1 deletion(-)
>>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.c
>>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.h
>>
>> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
>> index 3a361f8c8..122c786e3 100644
>> --- a/drivers/gpu/drm/verisilicon/Kconfig
>> +++ b/drivers/gpu/drm/verisilicon/Kconfig
>> @@ -12,4 +12,10 @@ config DRM_VERISILICON
>>         setting and buffer management. It does not
>>         provide 2D or 3D acceleration.
>>   -
>> +config DRM_VERISILICON_STARFIVE_HDMI
>> +    bool "Starfive HDMI extensions"
>> +    depends on DRM_VERISILICON
>> +    help
>> +       This selects support for StarFive soc specific extensions
>> +       for the Innosilicon HDMI driver. If you want to enable
>> +       HDMI on JH7110 based soc, you should select this option.
>> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
>> index 1d48016ca..08350f25b 100644
>> --- a/drivers/gpu/drm/verisilicon/Makefile
>> +++ b/drivers/gpu/drm/verisilicon/Makefile
>> @@ -7,5 +7,6 @@ vs_drm-objs := vs_dc_hw.o \
>>           vs_modeset.o \
>>           vs_plane.o
>>   +vs_drm-$(CONFIG_DRM_VERISILICON_STARFIVE_HDMI) += starfive_hdmi.o
>>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>>   diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.c b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
>> new file mode 100644
>> index 000000000..d296c4b71
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
>> @@ -0,0 +1,949 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/component.h>
>> +#include <linux/delay.h>
>> +#include <linux/err.h>
>> +#include <linux/hdmi.h>
>> +#include <linux/i2c.h>
>> +#include <linux/irq.h>
>> +#include <linux/media-bus-format.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of_device.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/reset.h>
>> +
>> +#include <drm/bridge/dw_hdmi.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_edid.h>
>> +#include <drm/drm_managed.h>
>> +#include <drm/drm_of.h>
>> +#include <drm/drm_probe_helper.h>
>> +#include <drm/drm_simple_kms_helper.h>
>> +
>> +#include "starfive_hdmi.h"
>> +#include "vs_drv.h"
>> +#include "vs_crtc.h"
>> +
>> +static struct starfive_hdmi *encoder_to_hdmi(struct drm_encoder *encoder)
>> +{
>> +    return container_of(encoder, struct starfive_hdmi, encoder);
>> +}
>> +
>> +static struct starfive_hdmi *connector_to_hdmi(struct drm_connector *connector)
>> +{
>> +    return container_of(connector, struct starfive_hdmi, connector);
>> +}
>> +
>> +struct starfive_hdmi_i2c {
>> +    struct i2c_adapter adap;
>> +
>> +    u8 ddc_addr;
>> +    u8 segment_addr;
>> +    /* protects the edid data when use i2c cmd to read edid */
>> +    struct mutex lock;
>> +    struct completion cmp;
>> +};
>> +
>> +static const struct pre_pll_config pre_pll_cfg_table[] = {
>> +    { 25175000,  25175000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0xf55555},
>> +    { 25200000,  25200000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0},
>> +    { 27000000,  27000000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0},
> 
> Such data structures are usually pretyt limited and hard to handle. Could you please replace it with the runtime calculations of
> 
>> +    { 27027000,  27027000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0x170a3d},
>> +    { 28320000,  28320000, 1,  28, 2, 1, 1,  3, 0, 3, 4, 0, 0x51eb85},
>> +    { 30240000,  30240000, 1,  30, 2, 1, 1,  3, 0, 3, 4, 0, 0x3d70a3},
>> +    { 31500000,  31500000, 1,  31, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> +    { 33750000,  33750000, 1,  33, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
>> +    { 36000000,  36000000, 1,  36, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    { 40000000,  40000000, 1,  80, 2, 2, 2, 12, 2, 2, 2, 0, 0},
>> +    { 46970000,  46970000, 1,  46, 2, 1, 1,  3, 0, 3, 4, 0, 0xf851eb},
>> +    { 49500000,  49500000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> +    { 49000000,  49000000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    { 50000000,  50000000, 1,  50, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    { 54000000,  54000000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    { 54054000,  54054000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0x0dd2f1},
>> +    { 57284000,  57284000, 1,  57, 2, 1, 1,  3, 0, 3, 4, 0, 0x48b439},
>> +    { 58230000,  58230000, 1,  58, 2, 1, 1,  3, 0, 3, 4, 0, 0x3ae147},
>> +    { 59341000,  59341000, 1,  59, 2, 1, 1,  3, 0, 3, 4, 0, 0x574bc6},
>> +    { 59400000,  59400000, 1,  99, 3, 1, 1,  1, 3, 3, 4, 0, 0},
>> +    { 65000000,  65000000, 1, 130, 2, 2, 2,  12, 0, 2, 2, 0, 0},
>> +    { 68250000,  68250000, 1, 68,  2, 1, 1,  3,  0, 3, 4, 0, 0x3fffff},
>> +    { 71000000,  71000000, 1,  71, 2, 1, 1,  3, 0, 3,  4, 0, 0},
>> +    { 74176000,  74176000, 1,  98, 1, 2, 2,  1, 2, 3, 4, 0, 0xe6ae6b},
>> +    { 74250000,  74250000, 1,  99, 1, 2, 2,  1, 2, 3, 4, 0, 0},
>> +    { 75000000,  75000000, 1,  75, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    { 78750000,  78750000, 1,  78, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
>> +    { 79500000,  79500000, 1,  79, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> +    { 83500000,  83500000, 2, 167, 2, 1, 1,  1, 0, 0,  6, 0, 0},
>> +    { 83500000, 104375000, 1, 104, 2, 1, 1,  1, 1, 0,  5, 0, 0x600000},
>> +    { 84858000,  84858000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xdba5e2},
>> +    { 85500000,  85500000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
>> +    { 85750000,  85750000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
>> +    { 85800000,  85800000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcccccc},
>> +    { 88750000,  88750000, 1,  88, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
>> +    { 89910000,  89910000, 1,  89, 2, 1, 1,  3, 0, 3, 4, 0, 0xe8f5c1},
>> +    { 90000000,  90000000, 1,  90, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {101000000, 101000000, 1, 101, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {102250000, 102250000, 1, 102, 2, 1, 1,  3, 0, 3, 4, 0, 0x3fffff},
>> +    {106500000, 106500000, 1, 106, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> +    {108000000, 108000000, 1,  90, 3, 0, 0,  5, 0, 2,  2, 0, 0},
>> +    {119000000, 119000000, 1, 119, 2, 1, 1,  3, 0, 3,  4, 0, 0},
>> +    {131481000, 131481000, 1,  131, 2, 1, 1,  3, 0, 3,  4, 0, 0x7b22d1},
>> +    {135000000, 135000000, 1,  135, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {136750000, 136750000, 1,  136, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
>> +    {147180000, 147180000, 1,  147, 2, 1, 1,  3, 0, 3, 4, 0, 0x2e147a},
>> +    {148352000, 148352000, 1,  98, 1, 1, 1,  1, 2, 2, 2, 0, 0xe6ae6b},
>> +    {148500000, 148500000, 1,  99, 1, 1, 1,  1, 2, 2, 2, 0, 0},
>> +    {154000000, 154000000, 1, 154, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {156000000, 156000000, 1, 156, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {157000000, 157000000, 1, 157, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {162000000, 162000000, 1, 162, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {174250000, 174250000, 1, 145, 3, 0, 0,  5, 0, 2, 2, 0, 0x355555},
>> +    {174500000, 174500000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> +    {174570000, 174570000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x91eb84},
>> +    {175500000, 175500000, 1, 175, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> +    {185590000, 185590000, 1, 185, 2, 1, 1,  3, 0, 3, 4, 0, 0x970a3c},
>> +    {187000000, 187000000, 1, 187, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {241500000, 241500000, 1, 161, 1, 1, 1,  4, 0, 2,  2, 0, 0},
>> +    {241700000, 241700000, 1, 241, 2, 1, 1,  3, 0, 3,  4, 0, 0xb33332},
>> +    {262750000, 262750000, 1, 262, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
>> +    {296500000, 296500000, 1, 296, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
>> +    {296703000, 296703000, 1,  98, 0, 1, 1,  1, 0, 2,  2, 0, 0xe6ae6b},
>> +    {297000000, 297000000, 1,  99, 0, 1, 1,  1, 0, 2,  2, 0, 0},
>> +    {594000000, 594000000, 1,  99, 0, 2, 0,  1, 0, 1,  1, 0, 0},
>> +    {0, 0, 0,  0, 0, 0, 0,  0, 0, 0,  0, 0, 0},
>> +};
>> +
>> +static const struct post_pll_config post_pll_cfg_table[] = {
>> +    {25200000,    1, 80, 13, 3, 1},
>> +    {27000000,    1, 40, 11, 3, 1},
>> +    {33750000,    1, 40, 11, 3, 1},
>> +    {49000000,    1, 20, 1, 3, 3},
>> +    {241700000, 1, 20, 1, 3, 3},
>> +    {297000000, 4, 20, 0, 0, 3},
>> +    {594000000, 4, 20, 0, 0, 0},
>> +    { /* sentinel */ }
>> +};
>> +
>> +inline u8 hdmi_readb(struct starfive_hdmi *hdmi, u16 offset)
>> +{
>> +    return readl_relaxed(hdmi->regs + (offset) * 0x04);
>> +}
>> +
>> +inline void hdmi_writeb(struct starfive_hdmi *hdmi, u16 offset, u32 val)
>> +{
>> +    writel_relaxed(val, hdmi->regs + (offset) * 0x04);
>> +}
>> +
>> +inline void hdmi_modb(struct starfive_hdmi *hdmi, u16 offset,
>> +                 u32 msk, u32 val)
>> +{
>> +    u8 temp = hdmi_readb(hdmi, offset) & ~msk;
>> +
>> +    temp |= val & msk;
>> +    hdmi_writeb(hdmi, offset, temp);
>> +}
>> +
>> +static int starfive_hdmi_enable_clk_deassert_rst(struct device *dev, struct starfive_hdmi *hdmi)
>> +{
>> +    int ret;
>> +
>> +    ret = clk_prepare_enable(hdmi->sys_clk);
>> +    if (ret) {
>> +        dev_err(dev, "Cannot enable HDMI sys clock: %d\n", ret);
>> +        return ret;
>> +    }
>> +
>> +    ret = clk_prepare_enable(hdmi->mclk);
>> +    if (ret) {
>> +        dev_err(dev, "Cannot enable HDMI mclk clock: %d\n", ret);
>> +        goto err_mclk;
>> +    }
>> +    ret = clk_prepare_enable(hdmi->bclk);
> 
> This code begs to use clk_bulk instead.
> 
>> +    if (ret) {
>> +        dev_err(dev, "Cannot enable HDMI bclk clock: %d\n", ret);
>> +        goto err_bclk;
>> +    }
>> +    ret = reset_control_deassert(hdmi->tx_rst);
>> +    if (ret < 0) {
>> +        dev_err(dev, "failed to deassert tx_rst\n");
>> +        goto err_rst;
>> +    }
>> +    return 0;
>> +
>> +err_rst:
>> +    clk_disable_unprepare(hdmi->bclk);
>> +err_bclk:
>> +    clk_disable_unprepare(hdmi->mclk);
>> +err_mclk:
>> +    clk_disable_unprepare(hdmi->sys_clk);
>> +    return ret;
>> +}
>> +
>> +static void starfive_hdmi_disable_clk_assert_rst(struct device *dev, struct starfive_hdmi *hdmi)
>> +{
>> +    int ret;
>> +
>> +    ret = reset_control_assert(hdmi->tx_rst);
>> +    if (ret < 0)
>> +        dev_err(dev, "failed to assert tx_rst\n");
>> +
>> +    clk_disable_unprepare(hdmi->sys_clk);
>> +    clk_disable_unprepare(hdmi->mclk);
>> +    clk_disable_unprepare(hdmi->bclk);
>> +}
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int hdmi_system_pm_suspend(struct device *dev)
>> +{
>> +    return pm_runtime_force_suspend(dev);
>> +}
>> +
>> +static int hdmi_system_pm_resume(struct device *dev)
>> +{
>> +    return pm_runtime_force_resume(dev);
>> +}
>> +#endif
>> +
>> +#ifdef CONFIG_PM
>> +static int hdmi_runtime_suspend(struct device *dev)
>> +{
>> +    struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
>> +
>> +    starfive_hdmi_disable_clk_assert_rst(dev, hdmi);
>> +
>> +    return 0;
>> +}
>> +
>> +static int hdmi_runtime_resume(struct device *dev)
>> +{
>> +    struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
>> +
>> +    return starfive_hdmi_enable_clk_deassert_rst(dev, hdmi);
>> +}
>> +#endif
>> +
>> +static void starfive_hdmi_tx_phy_power_down(struct starfive_hdmi *hdmi)
>> +{
>> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
>> +}
>> +
>> +static void starfive_hdmi_tx_phy_power_on(struct starfive_hdmi *hdmi)
>> +{
>> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
> 
> It looks like one can inline these two helpers w/o too much troubles.
> 
>> +}
>> +
>> +static void starfive_hdmi_config_pll(struct starfive_hdmi *hdmi)
>> +{
>> +    u32 val;
>> +    u8 reg_1ad_value = hdmi->post_cfg->post_div_en ?
>> +         hdmi->post_cfg->postdiv : 0x00;
>> +    u8 reg_1aa_value = hdmi->post_cfg->post_div_en ?
>> +         0x0e : 0x02;
>> +
>> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN);
>> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1,
>> +            STARFIVE_POST_PLL_POST_DIV_ENABLE |
>> +            STARFIVE_POST_PLL_REFCLK_SEL_TMDS |
>> +            STARFIVE_POST_PLL_POWER_DOWN);
>> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_1, STARFIVE_PRE_PLL_PRE_DIV(hdmi->pre_cfg->prediv));
>> +
>> +    val = STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE | STARFIVE_SPREAD_SPECTRUM_MOD_DOWN;
>> +    if (!hdmi->pre_cfg->fracdiv)
>> +        val |= STARFIVE_PRE_PLL_FRAC_DIV_DISABLE;
>> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_2,
>> +            STARFIVE_PRE_PLL_FB_DIV_11_8(hdmi->pre_cfg->fbdiv) | val);
>> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_3,
>> +            STARFIVE_PRE_PLL_FB_DIV_7_0(hdmi->pre_cfg->fbdiv));
>> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_4,
>> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_C(hdmi->pre_cfg->tmds_div_c) |
>> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_A(hdmi->pre_cfg->tmds_div_a) |
>> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_B(hdmi->pre_cfg->tmds_div_b));
>> +
>> +    if (hdmi->pre_cfg->fracdiv) {
>> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_L,
>> +                STARFIVE_PRE_PLL_FRAC_DIV_7_0(hdmi->pre_cfg->fracdiv));
>> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_M,
>> +                STARFIVE_PRE_PLL_FRAC_DIV_15_8(hdmi->pre_cfg->fracdiv));
>> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_H,
>> +                STARFIVE_PRE_PLL_FRAC_DIV_23_16(hdmi->pre_cfg->fracdiv));
>> +    }
>> +
>> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_5,
>> +            STARFIVE_PRE_PLL_PCLK_DIV_A(hdmi->pre_cfg->pclk_div_a) |
>> +            STARFIVE_PRE_PLL_PCLK_DIV_B(hdmi->pre_cfg->pclk_div_b));
>> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_6,
>> +            STARFIVE_PRE_PLL_PCLK_DIV_C(hdmi->pre_cfg->pclk_div_c) |
>> +            STARFIVE_PRE_PLL_PCLK_DIV_D(hdmi->pre_cfg->pclk_div_d));
>> +
>> +    /*pre-pll power down*/
>> +    hdmi_modb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN, 0);
>> +
>> +    hdmi_modb(hdmi, STARFIVE_POST_PLL_DIV_2, STARFIVE_POST_PLL_Pre_DIV_MASK,
>> +          STARFIVE_POST_PLL_PRE_DIV(hdmi->post_cfg->prediv));
>> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_3, hdmi->post_cfg->fbdiv & 0xff);
>> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_4, reg_1ad_value);
>> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1, reg_1aa_value);
>> +}
>> +
>> +static void starfive_hdmi_tmds_driver_on(struct starfive_hdmi *hdmi)
>> +{
>> +    hdmi_modb(hdmi, STARFIVE_TMDS_CONTROL,
>> +          STARFIVE_TMDS_DRIVER_ENABLE, STARFIVE_TMDS_DRIVER_ENABLE);
>> +}
>> +
>> +static void starfive_hdmi_sync_tmds(struct starfive_hdmi *hdmi)
>> +{
>> +    /*first send 0 to this bit, then send 1 and keep 1 into this bit*/
>> +    hdmi_writeb(hdmi, HDMI_SYNC, 0x0);
>> +    hdmi_writeb(hdmi, HDMI_SYNC, 0x1);
>> +}
>> +
>> +static void starfive_hdmi_i2c_init(struct starfive_hdmi *hdmi)
>> +{
>> +    int ddc_bus_freq;
>> +
>> +    ddc_bus_freq = (clk_get_rate(hdmi->sys_clk) >> 2) / HDMI_SCL_RATE;
>> +
>> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
>> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
>> +
>> +    /* Clear the EDID interrupt flag and mute the interrupt */
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> +}
>> +
>> +static const
>> +struct pre_pll_config *starfive_hdmi_phy_get_pre_pll_cfg(struct starfive_hdmi *hdmi,
>> +                             unsigned long rate)
>> +{
>> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
>> +
>> +    rate = (rate / 1000) * 1000;
>> +    for (; cfg->pixclock != 0; cfg++)
>> +        if (cfg->tmdsclock == rate && cfg->pixclock == rate)
>> +            break;
>> +
>> +    if (cfg->pixclock == 0)
>> +        return ERR_PTR(-EINVAL);
>> +
>> +    return cfg;
>> +}
>> +
>> +static int starfive_hdmi_phy_clk_set_rate(struct starfive_hdmi *hdmi)
>> +{
>> +    hdmi->post_cfg = post_pll_cfg_table;
>> +
>> +    hdmi->pre_cfg = starfive_hdmi_phy_get_pre_pll_cfg(hdmi, hdmi->tmds_rate);
>> +    if (IS_ERR(hdmi->pre_cfg))
>> +        return PTR_ERR(hdmi->pre_cfg);
>> +
>> +    for (; hdmi->post_cfg->tmdsclock != 0; hdmi->post_cfg++)
>> +        if (hdmi->tmds_rate <= hdmi->post_cfg->tmdsclock)
>> +            break;
>> +
>> +    starfive_hdmi_config_pll(hdmi);
>> +
>> +    return 0;
>> +}
>> +
>> +static int starfive_hdmi_config_video_timing(struct starfive_hdmi *hdmi,
>> +                         struct drm_display_mode *mode)
>> +{
>> +    int value;
>> +    /* Set detail external video timing */
>> +    value = mode->htotal;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
> 
> As you have hdmi_writeb already, adding hdmi_writew will help here.
> 
>> +
>> +    value = mode->htotal - mode->hdisplay;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->htotal - mode->hsync_start;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->hsync_end - mode->hsync_start;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->vtotal;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->vtotal - mode->vdisplay;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
>> +
>> +    value = mode->vtotal - mode->vsync_start;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
>> +
>> +    value = mode->vsync_end - mode->vsync_start;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
>> +
>> +    /* Set detail external video timing polarity and interlace mode */
>> +    value = v_EXTERANL_VIDEO(1);
>> +    value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
>> +        v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
>> +    value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
>> +        v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
>> +    value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
>> +        v_INETLACE(1) : v_INETLACE(0);
>> +
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
>> +    return 0;
>> +}
>> +
>> +static int starfive_hdmi_setup(struct starfive_hdmi *hdmi,
>> +                   struct drm_display_mode *mode)
>> +{
>> +    hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
>> +    hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
>> +    hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
>> +
>> +    hdmi->tmds_rate = mode->clock * 1000;
>> +    starfive_hdmi_phy_clk_set_rate(hdmi);
>> +
>> +    while (!(hdmi_readb(hdmi, STARFIVE_PRE_PLL_LOCK_STATUS) & 0x1))
>> +        continue;
>> +    while (!(hdmi_readb(hdmi, STARFIVE_POST_PLL_LOCK_STATUS) & 0x1))
>> +        continue;
> 
> Please use read_iopoll_timeout here.
> 
>> +
>> +    /*turn on LDO*/
>> +    hdmi_writeb(hdmi, STARFIVE_LDO_CONTROL, STARFIVE_LDO_ENABLE);
>> +    /*turn on serializer*/
>> +    hdmi_writeb(hdmi, STARFIVE_SERIALIER_CONTROL, STARFIVE_SERIALIER_ENABLE);
>> +
>> +    starfive_hdmi_tx_phy_power_down(hdmi);
>> +    starfive_hdmi_config_video_timing(hdmi, mode);
>> +    starfive_hdmi_tx_phy_power_on(hdmi);
>> +
>> +    starfive_hdmi_tmds_driver_on(hdmi);
>> +    starfive_hdmi_sync_tmds(hdmi);
>> +
>> +    return 0;
>> +}
>> +
>> +static void starfive_hdmi_encoder_mode_set(struct drm_encoder *encoder,
>> +                       struct drm_display_mode *mode,
>> +                       struct drm_display_mode *adj_mode)
>> +{
>> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
>> +
>> +    drm_mode_copy(&hdmi->previous_mode, adj_mode);
> 
> Why do you need it?
> 
>> +}
>> +
>> +static void starfive_hdmi_encoder_enable(struct drm_encoder *encoder)
>> +{
>> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
>> +    struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
>> +    int ret, idx;
>> +    struct drm_device *drm = hdmi->connector.dev;
>> +
>> +    if (drm && !drm_dev_enter(drm, &idx))
>> +        return;
>> +
>> +    ret = pm_runtime_get_sync(hdmi->dev);
>> +    if (ret < 0)
>> +        return;
>> +    mdelay(10);
>> +    starfive_hdmi_setup(hdmi, mode);
>> +
>> +    if (drm)
>> +        drm_dev_exit(idx);
>> +}
>> +
>> +static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
>> +{
>> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
>> +
>> +    int idx;
>> +    struct drm_device *drm = hdmi->connector.dev;
>> +
>> +    if (drm && !drm_dev_enter(drm, &idx))
>> +        return;
>> +
>> +    pm_runtime_put(hdmi->dev);
>> +
>> +    if (drm)
>> +        drm_dev_exit(idx);
>> +}
>> +
>> +static int
>> +starfive_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
>> +                   struct drm_crtc_state *crtc_state,
>> +                   struct drm_connector_state *conn_state)
>> +{
>> +    bool valid = false;
>> +    struct drm_display_mode *mode = &crtc_state->adjusted_mode;
>> +    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
>> +
>> +    vs_crtc_state->encoder_type = encoder->encoder_type;
>> +    vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
>> +
>> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
>> +    int pclk = mode->clock * 1000;
>> +
>> +    for (; cfg->pixclock != 0; cfg++) {
>> +        if (pclk == cfg->pixclock) {
>> +            if (pclk > 297000000)
> 
> Magic value.
> 
>> +                continue;
>> +
>> +            valid = true;
>> +            break;
>> +        }
>> +    }
>> +
>> +    return (valid) ? 0 : -EINVAL;
>> +}
>> +
>> +static const struct drm_encoder_helper_funcs starfive_hdmi_encoder_helper_funcs = {
>> +    .enable     = starfive_hdmi_encoder_enable,
>> +    .disable    = starfive_hdmi_encoder_disable,
>> +    .mode_set   = starfive_hdmi_encoder_mode_set,
>> +    .atomic_check = starfive_hdmi_encoder_atomic_check,
>> +};
>> +
>> +static enum drm_connector_status
>> +starfive_hdmi_connector_detect(struct drm_connector *connector, bool force)
>> +{
>> +    struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
>> +    struct drm_device *drm = hdmi->connector.dev;
>> +    int ret;
>> +    int idx;
>> +
>> +    if (drm && !drm_dev_enter(drm, &idx))
>> +        return connector_status_disconnected;
>> +
>> +    ret = pm_runtime_get_sync(hdmi->dev);
>> +    if (ret < 0)
>> +        return ret;
>> +
>> +    ret = (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
>> +        connector_status_connected : connector_status_disconnected;
>> +    pm_runtime_put(hdmi->dev);
>> +
>> +    if (drm)
>> +        drm_dev_exit(idx);
>> +
>> +    return ret;
>> +}
>> +
>> +static int starfive_hdmi_connector_get_modes(struct drm_connector *connector)
>> +{
>> +    struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
>> +    struct edid *edid;
>> +    int ret = 0;
>> +
>> +    if (!hdmi->ddc)
>> +        return 0;
>> +    ret = pm_runtime_get_sync(hdmi->dev);
>> +    if (ret < 0)
>> +        return ret;
>> +
>> +    edid = drm_get_edid(connector, hdmi->ddc);
>> +    if (edid) {
>> +        hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
>> +        hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
>> +        drm_connector_update_edid_property(connector, edid);
>> +        ret = drm_add_edid_modes(connector, edid);
>> +        kfree(edid);
>> +    }
>> +    pm_runtime_put(hdmi->dev);
> 
> Can you use drm_connector_helper_get_modes_from_ddc() here? And then drm_display_info.is_hdmi and .has_audio.
> 
>> +
>> +    return ret;
>> +}
>> +
>> +static enum drm_mode_status
>> +starfive_hdmi_connector_mode_valid(struct drm_connector *connector,
>> +                   struct drm_display_mode *mode)
>> +{
>> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
>> +    int pclk = mode->clock * 1000;
>> +    bool valid = false;
>> +
>> +    for (; cfg->pixclock != 0; cfg++) {
>> +        if (pclk == cfg->pixclock) {
>> +            if (pclk > 297000000)
> 
> magic value
> 
>> +                continue;
>> +
>> +            valid = true;
>> +            break;
>> +        }
>> +    }
>> +
>> +    return (valid) ? MODE_OK : MODE_BAD;
>> +}
>> +
>> +static int
>> +starfive_hdmi_probe_single_connector_modes(struct drm_connector *connector,
>> +                       u32 maxX, u32 maxY)
>> +{
>> +    return drm_helper_probe_single_connector_modes(connector, 3840, 2160);
>> +}
>> +
>> +static const struct drm_connector_funcs starfive_hdmi_connector_funcs = {
>> +    .fill_modes = starfive_hdmi_probe_single_connector_modes,
>> +    .detect = starfive_hdmi_connector_detect,
>> +    .reset = drm_atomic_helper_connector_reset,
>> +    .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
>> +    .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>> +};
>> +
>> +static struct drm_connector_helper_funcs starfive_hdmi_connector_helper_funcs = {
>> +    .get_modes = starfive_hdmi_connector_get_modes,
>> +    .mode_valid = starfive_hdmi_connector_mode_valid,
>> +};
>> +
>> +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
>> +{
>> +    struct drm_encoder *encoder = &hdmi->encoder;
>> +    struct device *dev = hdmi->dev;
>> +
>> +    encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
>> +
>> +    /*
>> +     * If we failed to find the CRTC(s) which this encoder is
>> +     * supposed to be connected to, it's because the CRTC has
>> +     * not been registered yet.  Defer probing, and hope that
>> +     * the required CRTC is added later.
>> +     */
>> +    if (encoder->possible_crtcs == 0)
>> +        return -EPROBE_DEFER;
>> +
>> +    drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
>> +
>> +    hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
>> +
>> +    drm_connector_helper_add(&hdmi->connector,
>> +                 &starfive_hdmi_connector_helper_funcs);
>> +    drmm_connector_init(drm, &hdmi->connector,
>> +                &starfive_hdmi_connector_funcs,
>> +                DRM_MODE_CONNECTOR_HDMIA,
> 
> On an embedded device one can not be so sure. There can be MHL or HDMI Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.
> 
>> +                hdmi->ddc);
>> +
>> +    drm_connector_attach_encoder(&hdmi->connector, encoder);
>> +
>> +    return 0;
>> +}
>> +
>> +static irqreturn_t starfive_hdmi_i2c_irq(struct starfive_hdmi *hdmi)
>> +{
>> +    struct starfive_hdmi_i2c *i2c = hdmi->i2c;
>> +    u8 stat;
>> +
>> +    stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
>> +    if (!(stat & m_INT_EDID_READY))
>> +        return IRQ_NONE;
>> +
>> +    /* Clear HDMI EDID interrupt flag */
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> +
>> +    complete(&i2c->cmp);
>> +
>> +    return IRQ_HANDLED;
>> +}
>> +
>> +static irqreturn_t starfive_hdmi_hardirq(int irq, void *dev_id)
>> +{
>> +    struct starfive_hdmi *hdmi = dev_id;
>> +    irqreturn_t ret = IRQ_NONE;
>> +    u8 interrupt;
>> +
>> +    if (hdmi->i2c)
>> +        ret = starfive_hdmi_i2c_irq(hdmi);
>> +
>> +    interrupt = hdmi_readb(hdmi, HDMI_STATUS);
>> +    if (interrupt & m_INT_HOTPLUG) {
>> +        hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
>> +        ret = IRQ_WAKE_THREAD;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static irqreturn_t starfive_hdmi_irq(int irq, void *dev_id)
>> +{
>> +    struct starfive_hdmi *hdmi = dev_id;
>> +
>> +    drm_connector_helper_hpd_irq_event(&hdmi->connector);
>> +
>> +    return IRQ_HANDLED;
>> +}
>> +
>> +static int starfive_hdmi_i2c_read(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
>> +{
>> +    int length = msgs->len;
>> +    u8 *buf = msgs->buf;
>> +    int ret;
>> +
>> +    ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10);
> 
> read_poll_timeout
Hi Dmitry:
I think using wait_for_completion_timeout here is a bit clearer than read_poll_timeout logic
I2c_read has hardware interrupt dependency. so need wait the interrupt completion.
also it comes with timeout detection.

> 
>> +    if (!ret)
>> +        return -EAGAIN;
>> +
>> +    while (length--)
>> +        *buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
>> +
>> +    return 0;
>> +}
>> +
>> +static int starfive_hdmi_i2c_write(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
>> +{
>> +    /*
>> +     * The DDC module only support read EDID message, so
>> +     * we assume that each word write to this i2c adapter
>> +     * should be the offset of EDID word address.
>> +     */
>> +    if (msgs->len != 1 ||
>> +        (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR))
>> +        return -EINVAL;
>> +
>> +    reinit_completion(&hdmi->i2c->cmp);
>> +
>> +    if (msgs->addr == DDC_SEGMENT_ADDR)
>> +        hdmi->i2c->segment_addr = msgs->buf[0];
>> +    if (msgs->addr == DDC_ADDR)
>> +        hdmi->i2c->ddc_addr = msgs->buf[0];
>> +
>> +    /* Set edid fifo first addr */
>> +    hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
>> +
>> +    /* Set edid word address 0x00/0x80 */
>> +    hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
>> +
>> +    /* Set edid segment pointer */
>> +    hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
>> +
>> +    return 0;
>> +}
>> +
>> +static int starfive_hdmi_i2c_xfer(struct i2c_adapter *adap,
>> +                  struct i2c_msg *msgs, int num)
>> +{
>> +    struct starfive_hdmi *hdmi = i2c_get_adapdata(adap);
>> +    struct starfive_hdmi_i2c *i2c = hdmi->i2c;
>> +    int i, ret = 0;
>> +
>> +    mutex_lock(&i2c->lock);
>> +
>> +    /* Clear the EDID interrupt flag and unmute the interrupt */
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> +
>> +    for (i = 0; i < num; i++) {
>> +        DRM_DEV_DEBUG(hdmi->dev,
>> +                  "xfer: num: %d/%d, len: %d, flags: %#x\n",
>> +                  i + 1, num, msgs[i].len, msgs[i].flags);
>> +
>> +        if (msgs[i].flags & I2C_M_RD)
>> +            ret = starfive_hdmi_i2c_read(hdmi, &msgs[i]);
>> +        else
>> +            ret = starfive_hdmi_i2c_write(hdmi, &msgs[i]);
>> +
>> +        if (ret < 0)
>> +            break;
>> +    }
>> +
>> +    if (!ret)
>> +        ret = num;
>> +
>> +    /* Mute HDMI EDID interrupt */
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
>> +
>> +    mutex_unlock(&i2c->lock);
>> +
>> +    return ret;
>> +}
>> +
>> +static u32 starfive_hdmi_i2c_func(struct i2c_adapter *adapter)
>> +{
>> +    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
>> +}
>> +
>> +static const struct i2c_algorithm starfive_hdmi_algorithm = {
>> +    .master_xfer    = starfive_hdmi_i2c_xfer,
>> +    .functionality    = starfive_hdmi_i2c_func,
>> +};
>> +
>> +static struct i2c_adapter *starfive_hdmi_i2c_adapter(struct starfive_hdmi *hdmi)
>> +{
>> +    struct i2c_adapter *adap;
>> +    struct starfive_hdmi_i2c *i2c;
>> +    int ret;
>> +
>> +    i2c = drmm_kzalloc(hdmi->drm_dev, sizeof(*i2c), GFP_KERNEL);
>> +    if (!i2c)
>> +        return ERR_PTR(-ENOMEM);
>> +
>> +    mutex_init(&i2c->lock);
>> +    init_completion(&i2c->cmp);
>> +
>> +    adap = &i2c->adap;
>> +    adap->class = I2C_CLASS_DDC;
>> +    adap->owner = THIS_MODULE;
>> +    adap->dev.parent = hdmi->dev;
>> +    adap->algo = &starfive_hdmi_algorithm;
>> +    strscpy(adap->name, "Starfive HDMI", sizeof(adap->name));
>> +    i2c_set_adapdata(adap, hdmi);
>> +
>> +    ret = devm_i2c_add_adapter(hdmi->dev, adap);
>> +    if (ret) {
>> +        drm_warn(hdmi->drm_dev, "cannot add %s I2C adapter\n", adap->name);
>> +        return ERR_PTR(ret);
>> +    }
>> +
>> +    hdmi->i2c = i2c;
>> +
>> +    drm_info(hdmi->drm_dev, "registered %s I2C bus driver success\n", adap->name);
>> +
>> +    return adap;
>> +}
>> +
>> +static int starfive_hdmi_get_clk_rst(struct device *dev, struct starfive_hdmi *hdmi)
>> +{
>> +    hdmi->sys_clk = devm_clk_get(dev, "sysclk");
>> +    if (IS_ERR(hdmi->sys_clk)) {
>> +        dev_err(dev, "Unable to get HDMI sysclk clk\n");
>> +        return PTR_ERR(hdmi->sys_clk);
>> +    }
>> +    hdmi->mclk = devm_clk_get(dev, "mclk");
>> +    if (IS_ERR(hdmi->mclk)) {
>> +        dev_err(dev, "Unable to get HDMI mclk clk\n");
>> +        return PTR_ERR(hdmi->mclk);
>> +    }
>> +    hdmi->bclk = devm_clk_get(dev, "bclk");
>> +    if (IS_ERR(hdmi->bclk)) {
>> +        dev_err(dev, "Unable to get HDMI bclk clk\n");
>> +        return PTR_ERR(hdmi->bclk);
>> +    }
>> +    hdmi->tx_rst = devm_reset_control_get_by_index(dev, 0);
>> +    if (IS_ERR(hdmi->tx_rst)) {
>> +        dev_err(dev, "failed to get tx_rst reset\n");
>> +        return PTR_ERR(hdmi->tx_rst);
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int starfive_hdmi_bind(struct device *dev, struct device *master,
>> +                  void *data)
>> +{
>> +    struct platform_device *pdev = to_platform_device(dev);
>> +    struct drm_device *drm = dev_get_drvdata(master);
>> +    struct starfive_hdmi *hdmi;
>> +    struct resource *iores;
>> +    int irq;
>> +    int ret;
>> +
>> +    hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
>> +    if (!hdmi)
>> +        return -ENOMEM;
>> +
>> +    hdmi = drmm_simple_encoder_alloc(drm, struct starfive_hdmi,
>> +                     encoder, DRM_MODE_ENCODER_TMDS);
>> +
>> +    hdmi->dev = dev;
>> +    hdmi->drm_dev = drm;
>> +    dev_set_drvdata(dev, hdmi);
>> +
>> +    iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +    hdmi->regs = devm_ioremap_resource(dev, iores);
> 
> This should go to probe
> 
>> +    if (IS_ERR(hdmi->regs))
>> +        return PTR_ERR(hdmi->regs);
>> +
>> +    ret = starfive_hdmi_get_clk_rst(dev, hdmi);
>> +    if (ret < 0)
>> +        return ret;
> 
> And this
> 
>> +
>> +    ret = devm_pm_runtime_enable(dev);
>> +    if (ret)
>> +        return ret;
>> +
>> +    ret = pm_runtime_resume_and_get(dev);
>> +    if (ret)
>> +        return ret;
>> +
>> +    irq = platform_get_irq(pdev, 0);
> 
> And this
> 
>> +    if (irq < 0) {
>> +        ret = irq;
>> +        goto err_put_runtime_pm;
>> +    }
>> +
>> +    hdmi->ddc = starfive_hdmi_i2c_adapter(hdmi);
>> +    if (IS_ERR(hdmi->ddc)) {
>> +        ret = PTR_ERR(hdmi->ddc);
>> +        hdmi->ddc = NULL;
>> +        goto err_put_runtime_pm;
>> +    }
>> +
>> +    starfive_hdmi_i2c_init(hdmi);
>> +
>> +    ret = starfive_hdmi_register(drm, hdmi);
>> +    if (ret)
>> +        goto err_put_adapter;
>> +
>> +    /* Unmute hotplug interrupt */
>> +    hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
>> +
>> +    ret = devm_request_threaded_irq(dev, irq, starfive_hdmi_hardirq,
>> +                    starfive_hdmi_irq, IRQF_SHARED,
>> +                    dev_name(dev), hdmi);
>> +    if (ret < 0)
>> +        goto err_put_adapter;
>> +
>> +    pm_runtime_put_sync(dev);
>> +
>> +    return 0;
>> +
>> +err_put_adapter:
>> +    i2c_put_adapter(hdmi->ddc);
>> +err_put_runtime_pm:
>> +    pm_runtime_put_sync(dev);
>> +
>> +    return ret;
>> +}
>> +
>> +static const struct component_ops starfive_hdmi_ops = {
>> +    .bind    = starfive_hdmi_bind,
>> +};
>> +
>> +static int starfive_hdmi_probe(struct platform_device *pdev)
>> +{
>> +    return component_add(&pdev->dev, &starfive_hdmi_ops);
>> +}
>> +
>> +static int starfive_hdmi_remove(struct platform_device *pdev)
>> +{
>> +    component_del(&pdev->dev, &starfive_hdmi_ops);
>> +
>> +    return 0;
>> +}
>> +
>> +static const struct dev_pm_ops hdmi_pm_ops = {
>> +    SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
>> +    SET_LATE_SYSTEM_SLEEP_PM_OPS(hdmi_system_pm_suspend, hdmi_system_pm_resume)
>> +};
>> +
>> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
>> +    { .compatible = "starfive,jh7110-inno-hdmi",},
>> +    {},
>> +};
>> +MODULE_DEVICE_TABLE(of, starfive_hdmi_dt_ids);
>> +
>> +struct platform_driver starfive_hdmi_driver = {
>> +    .probe  = starfive_hdmi_probe,
>> +    .remove = starfive_hdmi_remove,
>> +    .driver = {
>> +        .name = "starfive-hdmi",
>> +        .of_match_table = starfive_hdmi_dt_ids,
>> +        .pm = &hdmi_pm_ops,
>> +    },
>> +};
>> +
>> +MODULE_AUTHOR("StarFive Corporation");
>> +MODULE_DESCRIPTION("Starfive HDMI Driver");
>> diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.h b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
>> new file mode 100644
>> index 000000000..1b9a11bca
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
>> @@ -0,0 +1,295 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
>> + */
>> +
>> +#ifndef __STARFIVE_HDMI_H__
>> +#define __STARFIVE_HDMI_H__
>> +
>> +#include <drm/bridge/dw_hdmi.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_edid.h>
>> +#include <drm/drm_of.h>
>> +#include <drm/drm_probe_helper.h>
>> +#include <drm/drm_simple_kms_helper.h>
>> +#include <linux/bitfield.h>
>> +#include <linux/bits.h>
>> +
>> +#define DDC_SEGMENT_ADDR        0x30
>> +
>> +#define HDMI_SCL_RATE            (100 * 1000)
>> +#define DDC_BUS_FREQ_L            0x4b
>> +#define DDC_BUS_FREQ_H            0x4c
>> +
>> +#define HDMI_SYS_CTRL            0x00
>> +#define m_RST_ANALOG            BIT(6)
>> +#define v_RST_ANALOG            0
>> +#define v_NOT_RST_ANALOG        BIT(6)
>> +#define m_RST_DIGITAL            BIT(5)
>> +#define v_RST_DIGITAL            0
>> +#define v_NOT_RST_DIGITAL        BIT(5)
>> +#define m_REG_CLK_INV            BIT(4)
>> +#define v_REG_CLK_NOT_INV        0
>> +#define v_REG_CLK_INV            BIT(4)
>> +#define m_VCLK_INV            BIT(3)
>> +#define v_VCLK_NOT_INV            0
>> +#define v_VCLK_INV            BIT(3)
>> +#define m_REG_CLK_SOURCE        BIT(2)
>> +#define v_REG_CLK_SOURCE_TMDS        0
>> +#define v_REG_CLK_SOURCE_SYS        BIT(2)
>> +#define m_POWER                BIT(1)
>> +#define v_PWR_ON            0
>> +#define v_PWR_OFF            BIT(1)
>> +#define m_INT_POL            BIT(0)
>> +#define v_INT_POL_HIGH            1
>> +#define v_INT_POL_LOW            0
>> +
>> +#define HDMI_AV_MUTE            0x05
>> +#define m_AVMUTE_CLEAR            BIT(7)
>> +#define m_AVMUTE_ENABLE            BIT(6)
>> +#define m_AUDIO_MUTE            BIT(1)
>> +#define m_VIDEO_BLACK            BIT(0)
>> +#define v_AVMUTE_CLEAR(n)        ((n) << 7)
>> +#define v_AVMUTE_ENABLE(n)        ((n) << 6)
>> +#define v_AUDIO_MUTE(n)            ((n) << 1)
>> +#define v_VIDEO_MUTE(n)            ((n) << 0)
>> +
>> +#define HDMI_VIDEO_TIMING_CTL        0x08
>> +#define v_VSYNC_POLARITY(n)        ((n) << 3)
>> +#define v_HSYNC_POLARITY(n)        ((n) << 2)
>> +#define v_INETLACE(n)            ((n) << 1)
>> +#define v_EXTERANL_VIDEO(n)        ((n) << 0)
>> +
>> +#define HDMI_VIDEO_EXT_HTOTAL_L        0x09
>> +#define HDMI_VIDEO_EXT_HTOTAL_H        0x0a
>> +#define HDMI_VIDEO_EXT_HBLANK_L        0x0b
>> +#define HDMI_VIDEO_EXT_HBLANK_H        0x0c
>> +#define HDMI_VIDEO_EXT_HDELAY_L        0x0d
>> +#define HDMI_VIDEO_EXT_HDELAY_H        0x0e
>> +#define HDMI_VIDEO_EXT_HDURATION_L    0x0f
>> +#define HDMI_VIDEO_EXT_HDURATION_H    0x10
>> +#define HDMI_VIDEO_EXT_VTOTAL_L        0x11
>> +#define HDMI_VIDEO_EXT_VTOTAL_H        0x12
>> +#define HDMI_VIDEO_EXT_VBLANK        0x13
>> +#define HDMI_VIDEO_EXT_VDELAY        0x14
>> +#define HDMI_VIDEO_EXT_VDURATION    0x15
>> +
>> +#define HDMI_EDID_SEGMENT_POINTER    0x4d
>> +#define HDMI_EDID_WORD_ADDR        0x4e
>> +#define HDMI_EDID_FIFO_OFFSET        0x4f
>> +#define HDMI_EDID_FIFO_ADDR        0x50
>> +
>> +#define HDMI_INTERRUPT_MASK1        0xc0
>> +#define HDMI_INTERRUPT_STATUS1        0xc1
>> +#define    m_INT_ACTIVE_VSYNC        BIT(5)
>> +#define m_INT_EDID_READY        BIT(2)
>> +
>> +#define HDMI_STATUS            0xc8
>> +#define m_HOTPLUG            BIT(7)
>> +#define m_MASK_INT_HOTPLUG        BIT(5)
>> +#define m_INT_HOTPLUG            BIT(1)
>> +#define v_MASK_INT_HOTPLUG(n)        (((n) & 0x1) << 5)
> 
> It is untypical to have field defines which start with lowercase letter.
> 
>> +
>> +#define HDMI_SYNC                    0xce
>> +
>> +#define UPDATE(x, h, l)                    FIELD_PREP(GENMASK(h, l), x)
>> +
>> +/* REG: 0x1a0 */
>> +#define STARFIVE_PRE_PLL_CONTROL            0x1a0
>> +#define STARFIVE_PCLK_VCO_DIV_5_MASK            BIT(1)
>> +#define STARFIVE_PCLK_VCO_DIV_5(x)            UPDATE(x, 1, 1)
>> +#define STARFIVE_PRE_PLL_POWER_DOWN            BIT(0)
>> +
>> +/* REG: 0x1a1 */
>> +#define STARFIVE_PRE_PLL_DIV_1                0x1a1
>> +#define STARFIVE_PRE_PLL_PRE_DIV_MASK            GENMASK(5, 0)
>> +#define STARFIVE_PRE_PLL_PRE_DIV(x)            UPDATE(x, 5, 0)
>> +
>> +/* REG: 0x1a2 */
>> +#define STARFIVE_PRE_PLL_DIV_2                0x1a2
>> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DOWN        BIT(7)
>> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE        BIT(6)
>> +#define STARFIVE_PRE_PLL_FRAC_DIV_DISABLE        UPDATE(3, 5, 4)
>> +#define STARFIVE_PRE_PLL_FB_DIV_11_8_MASK        GENMASK(3, 0)
>> +#define STARFIVE_PRE_PLL_FB_DIV_11_8(x)            UPDATE((x) >> 8, 3, 0)
>> +
>> +/* REG: 0x1a3 */
>> +#define STARFIVE_PRE_PLL_DIV_3                0x1a3
>> +#define STARFIVE_PRE_PLL_FB_DIV_7_0(x)            UPDATE(x, 7, 0)
>> +
>> +/* REG: 0x1a4*/
>> +#define STARFIVE_PRE_PLL_DIV_4                0x1a4
>> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C_MASK        GENMASK(1, 0)
>> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C(x)        UPDATE(x, 1, 0)
>> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B_MASK        GENMASK(3, 2)
>> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B(x)        UPDATE(x, 3, 2)
>> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A_MASK        GENMASK(5, 4)
>> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A(x)        UPDATE(x, 5, 4)
>> +
>> +/* REG: 0x1a5 */
>> +#define STARFIVE_PRE_PLL_DIV_5                0x1a5
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_SHIFT        5
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_MASK        GENMASK(6, 5)
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_B(x)            UPDATE(x, 6, 5)
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_A_MASK        GENMASK(4, 0)
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_A(x)            UPDATE(x, 4, 0)
>> +
>> +/* REG: 0x1a6 */
>> +#define STARFIVE_PRE_PLL_DIV_6                0x1a6
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_SHIFT        5
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_MASK        GENMASK(6, 5)
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_C(x)            UPDATE(x, 6, 5)
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_D_MASK        GENMASK(4, 0)
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_D(x)            UPDATE(x, 4, 0)
>> +
>> +/* REG: 0x1a9 */
>> +#define STARFIVE_PRE_PLL_LOCK_STATUS            0x1a9
>> +
>> +/* REG: 0x1aa */
>> +#define STARFIVE_POST_PLL_DIV_1                0x1aa
>> +#define STARFIVE_POST_PLL_POST_DIV_ENABLE        GENMASK(3, 2)
>> +#define STARFIVE_POST_PLL_REFCLK_SEL_TMDS        BIT(1)
>> +#define STARFIVE_POST_PLL_POWER_DOWN            BIT(0)
>> +#define STARFIVE_POST_PLL_FB_DIV_8(x)            UPDATE(((x) >> 8) << 4, 4, 4)
>> +
>> +/* REG:0x1ab */
>> +#define STARFIVE_POST_PLL_DIV_2                0x1ab
>> +#define STARFIVE_POST_PLL_Pre_DIV_MASK            GENMASK(5, 0)
>> +#define STARFIVE_POST_PLL_PRE_DIV(x)            UPDATE(x, 5, 0)
>> +
>> +/* REG: 0x1ac */
>> +#define STARFIVE_POST_PLL_DIV_3                0x1ac
>> +#define STARFIVE_POST_PLL_FB_DIV_7_0(x)            UPDATE(x, 7, 0)
>> +
>> +/* REG: 0x1ad */
>> +#define STARFIVE_POST_PLL_DIV_4                0x1ad
>> +#define STARFIVE_POST_PLL_POST_DIV_MASK            GENMASK(2, 0)
>> +#define STARFIVE_POST_PLL_POST_DIV_2            0x0
>> +#define STARFIVE_POST_PLL_POST_DIV_4            0x1
>> +#define STARFIVE_POST_PLL_POST_DIV_8            0x3
>> +
>> +/* REG: 0x1af */
>> +#define STARFIVE_POST_PLL_LOCK_STATUS            0x1af
>> +
>> +/* REG: 0x1b0 */
>> +#define STARFIVE_BIAS_CONTROL                0x1b0
>> +#define STARFIVE_BIAS_ENABLE                BIT(2)
>> +
>> +/* REG: 0x1b2 */
>> +#define STARFIVE_TMDS_CONTROL                0x1b2
>> +#define STARFIVE_TMDS_CLK_DRIVER_EN            BIT(3)
>> +#define STARFIVE_TMDS_D2_DRIVER_EN            BIT(2)
>> +#define STARFIVE_TMDS_D1_DRIVER_EN            BIT(1)
>> +#define STARFIVE_TMDS_D0_DRIVER_EN            BIT(0)
>> +#define STARFIVE_TMDS_DRIVER_ENABLE            (STARFIVE_TMDS_CLK_DRIVER_EN | \
>> +                             STARFIVE_TMDS_D2_DRIVER_EN | \
>> +                             STARFIVE_TMDS_D1_DRIVER_EN | \
>> +                             STARFIVE_TMDS_D0_DRIVER_EN)
>> +
>> +/* REG: 0x1b4 */
>> +#define STARFIVE_LDO_CONTROL                0x1b4
>> +#define STARFIVE_LDO_D2_EN                BIT(2)
>> +#define STARFIVE_LDO_D1_EN                BIT(1)
>> +#define STARFIVE_LDO_D0_EN                BIT(0)
>> +#define STARFIVE_LDO_ENABLE                (STARFIVE_LDO_D2_EN | \
>> +                             STARFIVE_LDO_D1_EN | \
>> +                             STARFIVE_LDO_D0_EN)
>> +
>> +/* REG: 0x1be */
>> +#define STARFIVE_SERIALIER_CONTROL            0x1be
>> +#define STARFIVE_SERIALIER_D2_EN            BIT(6)
>> +#define STARFIVE_SERIALIER_D1_EN            BIT(5)
>> +#define STARFIVE_SERIALIER_D0_EN            BIT(4)
>> +#define STARFIVE_SERIALIER_EN                BIT(0)
>> +
>> +#define STARFIVE_SERIALIER_ENABLE            (STARFIVE_SERIALIER_D2_EN | \
>> +                             STARFIVE_SERIALIER_D1_EN | \
>> +                             STARFIVE_SERIALIER_D0_EN | \
>> +                             STARFIVE_SERIALIER_EN)
>> +
>> +/* REG: 0x1cc */
>> +#define STARFIVE_RX_CONTROL                0x1cc
>> +#define STARFIVE_RX_EN                    BIT(3)
>> +#define STARFIVE_RX_CHANNEL_2_EN            BIT(2)
>> +#define STARFIVE_RX_CHANNEL_1_EN            BIT(1)
>> +#define STARFIVE_RX_CHANNEL_0_EN            BIT(0)
>> +#define STARFIVE_RX_ENABLE                (STARFIVE_RX_EN | \
>> +                             STARFIVE_RX_CHANNEL_2_EN | \
>> +                             STARFIVE_RX_CHANNEL_1_EN | \
>> +                             STARFIVE_RX_CHANNEL_0_EN)
>> +
>> +/* REG: 0x1d1 */
>> +#define STARFIVE_PRE_PLL_FRAC_DIV_H            0x1d1
>> +#define STARFIVE_PRE_PLL_FRAC_DIV_23_16(x)        UPDATE((x) >> 16, 7, 0)
>> +/* REG: 0x1d2 */
>> +#define STARFIVE_PRE_PLL_FRAC_DIV_M            0x1d2
>> +#define STARFIVE_PRE_PLL_FRAC_DIV_15_8(x)        UPDATE((x) >> 8, 7, 0)
>> +/* REG: 0x1d3 */
>> +#define STARFIVE_PRE_PLL_FRAC_DIV_L            0x1d3
>> +#define STARFIVE_PRE_PLL_FRAC_DIV_7_0(x)        UPDATE(x, 7, 0)
>> +
>> +struct pre_pll_config {
>> +    unsigned long pixclock;
>> +    unsigned long tmdsclock;
>> +    u8 prediv;
>> +    u16 fbdiv;
>> +    u8 tmds_div_a;
>> +    u8 tmds_div_b;
>> +    u8 tmds_div_c;
>> +    u8 pclk_div_a;
>> +    u8 pclk_div_b;
>> +    u8 pclk_div_c;
>> +    u8 pclk_div_d;
>> +    u8 vco_div_5_en;
>> +    u32 fracdiv;
>> +};
>> +
>> +struct post_pll_config {
>> +    unsigned long tmdsclock;
>> +    u8 prediv;
>> +    u16 fbdiv;
>> +    u8 postdiv;
>> +    u8 post_div_en;
>> +    u8 version;
>> +};
>> +
>> +struct phy_config {
>> +    unsigned long    tmdsclock;
>> +    u8        regs[14];
>> +};
>> +
>> +struct hdmi_data_info {
>> +    int vic;
>> +    bool sink_is_hdmi;
>> +    bool sink_has_audio;
>> +    unsigned int enc_in_format;
>> +    unsigned int enc_out_format;
>> +    unsigned int colorimetry;
>> +};
>> +
>> +struct starfive_hdmi {
>> +    struct device *dev;
>> +    struct drm_device *drm_dev;
>> +    struct drm_encoder    encoder;
>> +    struct drm_connector    connector;
>> +
>> +    struct starfive_hdmi_i2c *i2c;
>> +
>> +    int irq;
>> +    struct clk *sys_clk;
>> +    struct clk *mclk;
>> +    struct clk *bclk;
>> +    struct reset_control *tx_rst;
>> +    void __iomem *regs;
>> +
>> +    struct i2c_adapter *ddc;
>> +
>> +    unsigned long tmds_rate;
>> +
>> +    struct hdmi_data_info    hdmi_data;
>> +    struct drm_display_mode previous_mode;
>> +    const struct pre_pll_config    *pre_cfg;
>> +    const struct post_pll_config    *post_cfg;
>> +};
>> +
>> +#endif /* __STARFIVE_HDMI_H__ */
>> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
>> index 3cd533cfa..9669354bd 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
>> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
>> @@ -126,6 +126,11 @@ static const struct component_master_ops vs_drm_ops = {
>>   static struct platform_driver *drm_sub_drivers[] = {
>>       &dc_platform_driver,
>>   +    /* connector + encoder*/
>> +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
>> +    &starfive_hdmi_driver,
>> +#endif
>> +
>>   };
>>     static struct component_match *vs_drm_match_add(struct device *dev)
>> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
>> index 369ad22d6..e90723419 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_drv.h
>> +++ b/drivers/gpu/drm/verisilicon/vs_drv.h
>> @@ -24,4 +24,8 @@ to_vs_drm_private(const struct drm_device *dev)
>>       return container_of(dev, struct vs_drm_device, base);
>>   }
>>   +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
>> +extern struct platform_driver starfive_hdmi_driver;
>> +#endif
>> +
>>   #endif /* __VS_DRV_H__ */
> 

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-11-13 12:11       ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-11-13 12:11 UTC (permalink / raw)
  To: Dmitry Baryshkov, dri-devel, devicetree, linux-kernel,
	linux-riscv, linux-media, linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig



On 2023/10/26 6:23, Dmitry Baryshkov wrote:
> On 25/10/2023 13:39, Keith Zhao wrote:
>> add hdmi driver as encoder and connect
>>
>> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>> ---
>>   drivers/gpu/drm/verisilicon/Kconfig         |   8 +-
>>   drivers/gpu/drm/verisilicon/Makefile        |   1 +
>>   drivers/gpu/drm/verisilicon/starfive_hdmi.c | 949 ++++++++++++++++++++
>>   drivers/gpu/drm/verisilicon/starfive_hdmi.h | 295 ++++++
>>   drivers/gpu/drm/verisilicon/vs_drv.c        |   5 +
>>   drivers/gpu/drm/verisilicon/vs_drv.h        |   4 +
>>   6 files changed, 1261 insertions(+), 1 deletion(-)
>>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.c
>>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.h
>>
>> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
>> index 3a361f8c8..122c786e3 100644
>> --- a/drivers/gpu/drm/verisilicon/Kconfig
>> +++ b/drivers/gpu/drm/verisilicon/Kconfig
>> @@ -12,4 +12,10 @@ config DRM_VERISILICON
>>         setting and buffer management. It does not
>>         provide 2D or 3D acceleration.
>>   -
>> +config DRM_VERISILICON_STARFIVE_HDMI
>> +    bool "Starfive HDMI extensions"
>> +    depends on DRM_VERISILICON
>> +    help
>> +       This selects support for StarFive soc specific extensions
>> +       for the Innosilicon HDMI driver. If you want to enable
>> +       HDMI on JH7110 based soc, you should select this option.
>> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
>> index 1d48016ca..08350f25b 100644
>> --- a/drivers/gpu/drm/verisilicon/Makefile
>> +++ b/drivers/gpu/drm/verisilicon/Makefile
>> @@ -7,5 +7,6 @@ vs_drm-objs := vs_dc_hw.o \
>>           vs_modeset.o \
>>           vs_plane.o
>>   +vs_drm-$(CONFIG_DRM_VERISILICON_STARFIVE_HDMI) += starfive_hdmi.o
>>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>>   diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.c b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
>> new file mode 100644
>> index 000000000..d296c4b71
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
>> @@ -0,0 +1,949 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/component.h>
>> +#include <linux/delay.h>
>> +#include <linux/err.h>
>> +#include <linux/hdmi.h>
>> +#include <linux/i2c.h>
>> +#include <linux/irq.h>
>> +#include <linux/media-bus-format.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of_device.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/reset.h>
>> +
>> +#include <drm/bridge/dw_hdmi.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_edid.h>
>> +#include <drm/drm_managed.h>
>> +#include <drm/drm_of.h>
>> +#include <drm/drm_probe_helper.h>
>> +#include <drm/drm_simple_kms_helper.h>
>> +
>> +#include "starfive_hdmi.h"
>> +#include "vs_drv.h"
>> +#include "vs_crtc.h"
>> +
>> +static struct starfive_hdmi *encoder_to_hdmi(struct drm_encoder *encoder)
>> +{
>> +    return container_of(encoder, struct starfive_hdmi, encoder);
>> +}
>> +
>> +static struct starfive_hdmi *connector_to_hdmi(struct drm_connector *connector)
>> +{
>> +    return container_of(connector, struct starfive_hdmi, connector);
>> +}
>> +
>> +struct starfive_hdmi_i2c {
>> +    struct i2c_adapter adap;
>> +
>> +    u8 ddc_addr;
>> +    u8 segment_addr;
>> +    /* protects the edid data when use i2c cmd to read edid */
>> +    struct mutex lock;
>> +    struct completion cmp;
>> +};
>> +
>> +static const struct pre_pll_config pre_pll_cfg_table[] = {
>> +    { 25175000,  25175000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0xf55555},
>> +    { 25200000,  25200000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0},
>> +    { 27000000,  27000000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0},
> 
> Such data structures are usually pretyt limited and hard to handle. Could you please replace it with the runtime calculations of
> 
>> +    { 27027000,  27027000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0x170a3d},
>> +    { 28320000,  28320000, 1,  28, 2, 1, 1,  3, 0, 3, 4, 0, 0x51eb85},
>> +    { 30240000,  30240000, 1,  30, 2, 1, 1,  3, 0, 3, 4, 0, 0x3d70a3},
>> +    { 31500000,  31500000, 1,  31, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> +    { 33750000,  33750000, 1,  33, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
>> +    { 36000000,  36000000, 1,  36, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    { 40000000,  40000000, 1,  80, 2, 2, 2, 12, 2, 2, 2, 0, 0},
>> +    { 46970000,  46970000, 1,  46, 2, 1, 1,  3, 0, 3, 4, 0, 0xf851eb},
>> +    { 49500000,  49500000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> +    { 49000000,  49000000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    { 50000000,  50000000, 1,  50, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    { 54000000,  54000000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    { 54054000,  54054000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0x0dd2f1},
>> +    { 57284000,  57284000, 1,  57, 2, 1, 1,  3, 0, 3, 4, 0, 0x48b439},
>> +    { 58230000,  58230000, 1,  58, 2, 1, 1,  3, 0, 3, 4, 0, 0x3ae147},
>> +    { 59341000,  59341000, 1,  59, 2, 1, 1,  3, 0, 3, 4, 0, 0x574bc6},
>> +    { 59400000,  59400000, 1,  99, 3, 1, 1,  1, 3, 3, 4, 0, 0},
>> +    { 65000000,  65000000, 1, 130, 2, 2, 2,  12, 0, 2, 2, 0, 0},
>> +    { 68250000,  68250000, 1, 68,  2, 1, 1,  3,  0, 3, 4, 0, 0x3fffff},
>> +    { 71000000,  71000000, 1,  71, 2, 1, 1,  3, 0, 3,  4, 0, 0},
>> +    { 74176000,  74176000, 1,  98, 1, 2, 2,  1, 2, 3, 4, 0, 0xe6ae6b},
>> +    { 74250000,  74250000, 1,  99, 1, 2, 2,  1, 2, 3, 4, 0, 0},
>> +    { 75000000,  75000000, 1,  75, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    { 78750000,  78750000, 1,  78, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
>> +    { 79500000,  79500000, 1,  79, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> +    { 83500000,  83500000, 2, 167, 2, 1, 1,  1, 0, 0,  6, 0, 0},
>> +    { 83500000, 104375000, 1, 104, 2, 1, 1,  1, 1, 0,  5, 0, 0x600000},
>> +    { 84858000,  84858000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xdba5e2},
>> +    { 85500000,  85500000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
>> +    { 85750000,  85750000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
>> +    { 85800000,  85800000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcccccc},
>> +    { 88750000,  88750000, 1,  88, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
>> +    { 89910000,  89910000, 1,  89, 2, 1, 1,  3, 0, 3, 4, 0, 0xe8f5c1},
>> +    { 90000000,  90000000, 1,  90, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {101000000, 101000000, 1, 101, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {102250000, 102250000, 1, 102, 2, 1, 1,  3, 0, 3, 4, 0, 0x3fffff},
>> +    {106500000, 106500000, 1, 106, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> +    {108000000, 108000000, 1,  90, 3, 0, 0,  5, 0, 2,  2, 0, 0},
>> +    {119000000, 119000000, 1, 119, 2, 1, 1,  3, 0, 3,  4, 0, 0},
>> +    {131481000, 131481000, 1,  131, 2, 1, 1,  3, 0, 3,  4, 0, 0x7b22d1},
>> +    {135000000, 135000000, 1,  135, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {136750000, 136750000, 1,  136, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
>> +    {147180000, 147180000, 1,  147, 2, 1, 1,  3, 0, 3, 4, 0, 0x2e147a},
>> +    {148352000, 148352000, 1,  98, 1, 1, 1,  1, 2, 2, 2, 0, 0xe6ae6b},
>> +    {148500000, 148500000, 1,  99, 1, 1, 1,  1, 2, 2, 2, 0, 0},
>> +    {154000000, 154000000, 1, 154, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {156000000, 156000000, 1, 156, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {157000000, 157000000, 1, 157, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {162000000, 162000000, 1, 162, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {174250000, 174250000, 1, 145, 3, 0, 0,  5, 0, 2, 2, 0, 0x355555},
>> +    {174500000, 174500000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> +    {174570000, 174570000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x91eb84},
>> +    {175500000, 175500000, 1, 175, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> +    {185590000, 185590000, 1, 185, 2, 1, 1,  3, 0, 3, 4, 0, 0x970a3c},
>> +    {187000000, 187000000, 1, 187, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> +    {241500000, 241500000, 1, 161, 1, 1, 1,  4, 0, 2,  2, 0, 0},
>> +    {241700000, 241700000, 1, 241, 2, 1, 1,  3, 0, 3,  4, 0, 0xb33332},
>> +    {262750000, 262750000, 1, 262, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
>> +    {296500000, 296500000, 1, 296, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
>> +    {296703000, 296703000, 1,  98, 0, 1, 1,  1, 0, 2,  2, 0, 0xe6ae6b},
>> +    {297000000, 297000000, 1,  99, 0, 1, 1,  1, 0, 2,  2, 0, 0},
>> +    {594000000, 594000000, 1,  99, 0, 2, 0,  1, 0, 1,  1, 0, 0},
>> +    {0, 0, 0,  0, 0, 0, 0,  0, 0, 0,  0, 0, 0},
>> +};
>> +
>> +static const struct post_pll_config post_pll_cfg_table[] = {
>> +    {25200000,    1, 80, 13, 3, 1},
>> +    {27000000,    1, 40, 11, 3, 1},
>> +    {33750000,    1, 40, 11, 3, 1},
>> +    {49000000,    1, 20, 1, 3, 3},
>> +    {241700000, 1, 20, 1, 3, 3},
>> +    {297000000, 4, 20, 0, 0, 3},
>> +    {594000000, 4, 20, 0, 0, 0},
>> +    { /* sentinel */ }
>> +};
>> +
>> +inline u8 hdmi_readb(struct starfive_hdmi *hdmi, u16 offset)
>> +{
>> +    return readl_relaxed(hdmi->regs + (offset) * 0x04);
>> +}
>> +
>> +inline void hdmi_writeb(struct starfive_hdmi *hdmi, u16 offset, u32 val)
>> +{
>> +    writel_relaxed(val, hdmi->regs + (offset) * 0x04);
>> +}
>> +
>> +inline void hdmi_modb(struct starfive_hdmi *hdmi, u16 offset,
>> +                 u32 msk, u32 val)
>> +{
>> +    u8 temp = hdmi_readb(hdmi, offset) & ~msk;
>> +
>> +    temp |= val & msk;
>> +    hdmi_writeb(hdmi, offset, temp);
>> +}
>> +
>> +static int starfive_hdmi_enable_clk_deassert_rst(struct device *dev, struct starfive_hdmi *hdmi)
>> +{
>> +    int ret;
>> +
>> +    ret = clk_prepare_enable(hdmi->sys_clk);
>> +    if (ret) {
>> +        dev_err(dev, "Cannot enable HDMI sys clock: %d\n", ret);
>> +        return ret;
>> +    }
>> +
>> +    ret = clk_prepare_enable(hdmi->mclk);
>> +    if (ret) {
>> +        dev_err(dev, "Cannot enable HDMI mclk clock: %d\n", ret);
>> +        goto err_mclk;
>> +    }
>> +    ret = clk_prepare_enable(hdmi->bclk);
> 
> This code begs to use clk_bulk instead.
> 
>> +    if (ret) {
>> +        dev_err(dev, "Cannot enable HDMI bclk clock: %d\n", ret);
>> +        goto err_bclk;
>> +    }
>> +    ret = reset_control_deassert(hdmi->tx_rst);
>> +    if (ret < 0) {
>> +        dev_err(dev, "failed to deassert tx_rst\n");
>> +        goto err_rst;
>> +    }
>> +    return 0;
>> +
>> +err_rst:
>> +    clk_disable_unprepare(hdmi->bclk);
>> +err_bclk:
>> +    clk_disable_unprepare(hdmi->mclk);
>> +err_mclk:
>> +    clk_disable_unprepare(hdmi->sys_clk);
>> +    return ret;
>> +}
>> +
>> +static void starfive_hdmi_disable_clk_assert_rst(struct device *dev, struct starfive_hdmi *hdmi)
>> +{
>> +    int ret;
>> +
>> +    ret = reset_control_assert(hdmi->tx_rst);
>> +    if (ret < 0)
>> +        dev_err(dev, "failed to assert tx_rst\n");
>> +
>> +    clk_disable_unprepare(hdmi->sys_clk);
>> +    clk_disable_unprepare(hdmi->mclk);
>> +    clk_disable_unprepare(hdmi->bclk);
>> +}
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int hdmi_system_pm_suspend(struct device *dev)
>> +{
>> +    return pm_runtime_force_suspend(dev);
>> +}
>> +
>> +static int hdmi_system_pm_resume(struct device *dev)
>> +{
>> +    return pm_runtime_force_resume(dev);
>> +}
>> +#endif
>> +
>> +#ifdef CONFIG_PM
>> +static int hdmi_runtime_suspend(struct device *dev)
>> +{
>> +    struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
>> +
>> +    starfive_hdmi_disable_clk_assert_rst(dev, hdmi);
>> +
>> +    return 0;
>> +}
>> +
>> +static int hdmi_runtime_resume(struct device *dev)
>> +{
>> +    struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
>> +
>> +    return starfive_hdmi_enable_clk_deassert_rst(dev, hdmi);
>> +}
>> +#endif
>> +
>> +static void starfive_hdmi_tx_phy_power_down(struct starfive_hdmi *hdmi)
>> +{
>> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
>> +}
>> +
>> +static void starfive_hdmi_tx_phy_power_on(struct starfive_hdmi *hdmi)
>> +{
>> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
> 
> It looks like one can inline these two helpers w/o too much troubles.
> 
>> +}
>> +
>> +static void starfive_hdmi_config_pll(struct starfive_hdmi *hdmi)
>> +{
>> +    u32 val;
>> +    u8 reg_1ad_value = hdmi->post_cfg->post_div_en ?
>> +         hdmi->post_cfg->postdiv : 0x00;
>> +    u8 reg_1aa_value = hdmi->post_cfg->post_div_en ?
>> +         0x0e : 0x02;
>> +
>> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN);
>> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1,
>> +            STARFIVE_POST_PLL_POST_DIV_ENABLE |
>> +            STARFIVE_POST_PLL_REFCLK_SEL_TMDS |
>> +            STARFIVE_POST_PLL_POWER_DOWN);
>> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_1, STARFIVE_PRE_PLL_PRE_DIV(hdmi->pre_cfg->prediv));
>> +
>> +    val = STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE | STARFIVE_SPREAD_SPECTRUM_MOD_DOWN;
>> +    if (!hdmi->pre_cfg->fracdiv)
>> +        val |= STARFIVE_PRE_PLL_FRAC_DIV_DISABLE;
>> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_2,
>> +            STARFIVE_PRE_PLL_FB_DIV_11_8(hdmi->pre_cfg->fbdiv) | val);
>> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_3,
>> +            STARFIVE_PRE_PLL_FB_DIV_7_0(hdmi->pre_cfg->fbdiv));
>> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_4,
>> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_C(hdmi->pre_cfg->tmds_div_c) |
>> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_A(hdmi->pre_cfg->tmds_div_a) |
>> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_B(hdmi->pre_cfg->tmds_div_b));
>> +
>> +    if (hdmi->pre_cfg->fracdiv) {
>> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_L,
>> +                STARFIVE_PRE_PLL_FRAC_DIV_7_0(hdmi->pre_cfg->fracdiv));
>> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_M,
>> +                STARFIVE_PRE_PLL_FRAC_DIV_15_8(hdmi->pre_cfg->fracdiv));
>> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_H,
>> +                STARFIVE_PRE_PLL_FRAC_DIV_23_16(hdmi->pre_cfg->fracdiv));
>> +    }
>> +
>> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_5,
>> +            STARFIVE_PRE_PLL_PCLK_DIV_A(hdmi->pre_cfg->pclk_div_a) |
>> +            STARFIVE_PRE_PLL_PCLK_DIV_B(hdmi->pre_cfg->pclk_div_b));
>> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_6,
>> +            STARFIVE_PRE_PLL_PCLK_DIV_C(hdmi->pre_cfg->pclk_div_c) |
>> +            STARFIVE_PRE_PLL_PCLK_DIV_D(hdmi->pre_cfg->pclk_div_d));
>> +
>> +    /*pre-pll power down*/
>> +    hdmi_modb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN, 0);
>> +
>> +    hdmi_modb(hdmi, STARFIVE_POST_PLL_DIV_2, STARFIVE_POST_PLL_Pre_DIV_MASK,
>> +          STARFIVE_POST_PLL_PRE_DIV(hdmi->post_cfg->prediv));
>> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_3, hdmi->post_cfg->fbdiv & 0xff);
>> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_4, reg_1ad_value);
>> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1, reg_1aa_value);
>> +}
>> +
>> +static void starfive_hdmi_tmds_driver_on(struct starfive_hdmi *hdmi)
>> +{
>> +    hdmi_modb(hdmi, STARFIVE_TMDS_CONTROL,
>> +          STARFIVE_TMDS_DRIVER_ENABLE, STARFIVE_TMDS_DRIVER_ENABLE);
>> +}
>> +
>> +static void starfive_hdmi_sync_tmds(struct starfive_hdmi *hdmi)
>> +{
>> +    /*first send 0 to this bit, then send 1 and keep 1 into this bit*/
>> +    hdmi_writeb(hdmi, HDMI_SYNC, 0x0);
>> +    hdmi_writeb(hdmi, HDMI_SYNC, 0x1);
>> +}
>> +
>> +static void starfive_hdmi_i2c_init(struct starfive_hdmi *hdmi)
>> +{
>> +    int ddc_bus_freq;
>> +
>> +    ddc_bus_freq = (clk_get_rate(hdmi->sys_clk) >> 2) / HDMI_SCL_RATE;
>> +
>> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
>> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
>> +
>> +    /* Clear the EDID interrupt flag and mute the interrupt */
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> +}
>> +
>> +static const
>> +struct pre_pll_config *starfive_hdmi_phy_get_pre_pll_cfg(struct starfive_hdmi *hdmi,
>> +                             unsigned long rate)
>> +{
>> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
>> +
>> +    rate = (rate / 1000) * 1000;
>> +    for (; cfg->pixclock != 0; cfg++)
>> +        if (cfg->tmdsclock == rate && cfg->pixclock == rate)
>> +            break;
>> +
>> +    if (cfg->pixclock == 0)
>> +        return ERR_PTR(-EINVAL);
>> +
>> +    return cfg;
>> +}
>> +
>> +static int starfive_hdmi_phy_clk_set_rate(struct starfive_hdmi *hdmi)
>> +{
>> +    hdmi->post_cfg = post_pll_cfg_table;
>> +
>> +    hdmi->pre_cfg = starfive_hdmi_phy_get_pre_pll_cfg(hdmi, hdmi->tmds_rate);
>> +    if (IS_ERR(hdmi->pre_cfg))
>> +        return PTR_ERR(hdmi->pre_cfg);
>> +
>> +    for (; hdmi->post_cfg->tmdsclock != 0; hdmi->post_cfg++)
>> +        if (hdmi->tmds_rate <= hdmi->post_cfg->tmdsclock)
>> +            break;
>> +
>> +    starfive_hdmi_config_pll(hdmi);
>> +
>> +    return 0;
>> +}
>> +
>> +static int starfive_hdmi_config_video_timing(struct starfive_hdmi *hdmi,
>> +                         struct drm_display_mode *mode)
>> +{
>> +    int value;
>> +    /* Set detail external video timing */
>> +    value = mode->htotal;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
> 
> As you have hdmi_writeb already, adding hdmi_writew will help here.
> 
>> +
>> +    value = mode->htotal - mode->hdisplay;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->htotal - mode->hsync_start;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->hsync_end - mode->hsync_start;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->vtotal;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->vtotal - mode->vdisplay;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
>> +
>> +    value = mode->vtotal - mode->vsync_start;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
>> +
>> +    value = mode->vsync_end - mode->vsync_start;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
>> +
>> +    /* Set detail external video timing polarity and interlace mode */
>> +    value = v_EXTERANL_VIDEO(1);
>> +    value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
>> +        v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
>> +    value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
>> +        v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
>> +    value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
>> +        v_INETLACE(1) : v_INETLACE(0);
>> +
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
>> +    return 0;
>> +}
>> +
>> +static int starfive_hdmi_setup(struct starfive_hdmi *hdmi,
>> +                   struct drm_display_mode *mode)
>> +{
>> +    hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
>> +    hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
>> +    hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
>> +
>> +    hdmi->tmds_rate = mode->clock * 1000;
>> +    starfive_hdmi_phy_clk_set_rate(hdmi);
>> +
>> +    while (!(hdmi_readb(hdmi, STARFIVE_PRE_PLL_LOCK_STATUS) & 0x1))
>> +        continue;
>> +    while (!(hdmi_readb(hdmi, STARFIVE_POST_PLL_LOCK_STATUS) & 0x1))
>> +        continue;
> 
> Please use read_iopoll_timeout here.
> 
>> +
>> +    /*turn on LDO*/
>> +    hdmi_writeb(hdmi, STARFIVE_LDO_CONTROL, STARFIVE_LDO_ENABLE);
>> +    /*turn on serializer*/
>> +    hdmi_writeb(hdmi, STARFIVE_SERIALIER_CONTROL, STARFIVE_SERIALIER_ENABLE);
>> +
>> +    starfive_hdmi_tx_phy_power_down(hdmi);
>> +    starfive_hdmi_config_video_timing(hdmi, mode);
>> +    starfive_hdmi_tx_phy_power_on(hdmi);
>> +
>> +    starfive_hdmi_tmds_driver_on(hdmi);
>> +    starfive_hdmi_sync_tmds(hdmi);
>> +
>> +    return 0;
>> +}
>> +
>> +static void starfive_hdmi_encoder_mode_set(struct drm_encoder *encoder,
>> +                       struct drm_display_mode *mode,
>> +                       struct drm_display_mode *adj_mode)
>> +{
>> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
>> +
>> +    drm_mode_copy(&hdmi->previous_mode, adj_mode);
> 
> Why do you need it?
> 
>> +}
>> +
>> +static void starfive_hdmi_encoder_enable(struct drm_encoder *encoder)
>> +{
>> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
>> +    struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
>> +    int ret, idx;
>> +    struct drm_device *drm = hdmi->connector.dev;
>> +
>> +    if (drm && !drm_dev_enter(drm, &idx))
>> +        return;
>> +
>> +    ret = pm_runtime_get_sync(hdmi->dev);
>> +    if (ret < 0)
>> +        return;
>> +    mdelay(10);
>> +    starfive_hdmi_setup(hdmi, mode);
>> +
>> +    if (drm)
>> +        drm_dev_exit(idx);
>> +}
>> +
>> +static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
>> +{
>> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
>> +
>> +    int idx;
>> +    struct drm_device *drm = hdmi->connector.dev;
>> +
>> +    if (drm && !drm_dev_enter(drm, &idx))
>> +        return;
>> +
>> +    pm_runtime_put(hdmi->dev);
>> +
>> +    if (drm)
>> +        drm_dev_exit(idx);
>> +}
>> +
>> +static int
>> +starfive_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
>> +                   struct drm_crtc_state *crtc_state,
>> +                   struct drm_connector_state *conn_state)
>> +{
>> +    bool valid = false;
>> +    struct drm_display_mode *mode = &crtc_state->adjusted_mode;
>> +    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
>> +
>> +    vs_crtc_state->encoder_type = encoder->encoder_type;
>> +    vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
>> +
>> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
>> +    int pclk = mode->clock * 1000;
>> +
>> +    for (; cfg->pixclock != 0; cfg++) {
>> +        if (pclk == cfg->pixclock) {
>> +            if (pclk > 297000000)
> 
> Magic value.
> 
>> +                continue;
>> +
>> +            valid = true;
>> +            break;
>> +        }
>> +    }
>> +
>> +    return (valid) ? 0 : -EINVAL;
>> +}
>> +
>> +static const struct drm_encoder_helper_funcs starfive_hdmi_encoder_helper_funcs = {
>> +    .enable     = starfive_hdmi_encoder_enable,
>> +    .disable    = starfive_hdmi_encoder_disable,
>> +    .mode_set   = starfive_hdmi_encoder_mode_set,
>> +    .atomic_check = starfive_hdmi_encoder_atomic_check,
>> +};
>> +
>> +static enum drm_connector_status
>> +starfive_hdmi_connector_detect(struct drm_connector *connector, bool force)
>> +{
>> +    struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
>> +    struct drm_device *drm = hdmi->connector.dev;
>> +    int ret;
>> +    int idx;
>> +
>> +    if (drm && !drm_dev_enter(drm, &idx))
>> +        return connector_status_disconnected;
>> +
>> +    ret = pm_runtime_get_sync(hdmi->dev);
>> +    if (ret < 0)
>> +        return ret;
>> +
>> +    ret = (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
>> +        connector_status_connected : connector_status_disconnected;
>> +    pm_runtime_put(hdmi->dev);
>> +
>> +    if (drm)
>> +        drm_dev_exit(idx);
>> +
>> +    return ret;
>> +}
>> +
>> +static int starfive_hdmi_connector_get_modes(struct drm_connector *connector)
>> +{
>> +    struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
>> +    struct edid *edid;
>> +    int ret = 0;
>> +
>> +    if (!hdmi->ddc)
>> +        return 0;
>> +    ret = pm_runtime_get_sync(hdmi->dev);
>> +    if (ret < 0)
>> +        return ret;
>> +
>> +    edid = drm_get_edid(connector, hdmi->ddc);
>> +    if (edid) {
>> +        hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
>> +        hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
>> +        drm_connector_update_edid_property(connector, edid);
>> +        ret = drm_add_edid_modes(connector, edid);
>> +        kfree(edid);
>> +    }
>> +    pm_runtime_put(hdmi->dev);
> 
> Can you use drm_connector_helper_get_modes_from_ddc() here? And then drm_display_info.is_hdmi and .has_audio.
> 
>> +
>> +    return ret;
>> +}
>> +
>> +static enum drm_mode_status
>> +starfive_hdmi_connector_mode_valid(struct drm_connector *connector,
>> +                   struct drm_display_mode *mode)
>> +{
>> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
>> +    int pclk = mode->clock * 1000;
>> +    bool valid = false;
>> +
>> +    for (; cfg->pixclock != 0; cfg++) {
>> +        if (pclk == cfg->pixclock) {
>> +            if (pclk > 297000000)
> 
> magic value
> 
>> +                continue;
>> +
>> +            valid = true;
>> +            break;
>> +        }
>> +    }
>> +
>> +    return (valid) ? MODE_OK : MODE_BAD;
>> +}
>> +
>> +static int
>> +starfive_hdmi_probe_single_connector_modes(struct drm_connector *connector,
>> +                       u32 maxX, u32 maxY)
>> +{
>> +    return drm_helper_probe_single_connector_modes(connector, 3840, 2160);
>> +}
>> +
>> +static const struct drm_connector_funcs starfive_hdmi_connector_funcs = {
>> +    .fill_modes = starfive_hdmi_probe_single_connector_modes,
>> +    .detect = starfive_hdmi_connector_detect,
>> +    .reset = drm_atomic_helper_connector_reset,
>> +    .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
>> +    .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>> +};
>> +
>> +static struct drm_connector_helper_funcs starfive_hdmi_connector_helper_funcs = {
>> +    .get_modes = starfive_hdmi_connector_get_modes,
>> +    .mode_valid = starfive_hdmi_connector_mode_valid,
>> +};
>> +
>> +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
>> +{
>> +    struct drm_encoder *encoder = &hdmi->encoder;
>> +    struct device *dev = hdmi->dev;
>> +
>> +    encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
>> +
>> +    /*
>> +     * If we failed to find the CRTC(s) which this encoder is
>> +     * supposed to be connected to, it's because the CRTC has
>> +     * not been registered yet.  Defer probing, and hope that
>> +     * the required CRTC is added later.
>> +     */
>> +    if (encoder->possible_crtcs == 0)
>> +        return -EPROBE_DEFER;
>> +
>> +    drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
>> +
>> +    hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
>> +
>> +    drm_connector_helper_add(&hdmi->connector,
>> +                 &starfive_hdmi_connector_helper_funcs);
>> +    drmm_connector_init(drm, &hdmi->connector,
>> +                &starfive_hdmi_connector_funcs,
>> +                DRM_MODE_CONNECTOR_HDMIA,
> 
> On an embedded device one can not be so sure. There can be MHL or HDMI Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.
> 
>> +                hdmi->ddc);
>> +
>> +    drm_connector_attach_encoder(&hdmi->connector, encoder);
>> +
>> +    return 0;
>> +}
>> +
>> +static irqreturn_t starfive_hdmi_i2c_irq(struct starfive_hdmi *hdmi)
>> +{
>> +    struct starfive_hdmi_i2c *i2c = hdmi->i2c;
>> +    u8 stat;
>> +
>> +    stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
>> +    if (!(stat & m_INT_EDID_READY))
>> +        return IRQ_NONE;
>> +
>> +    /* Clear HDMI EDID interrupt flag */
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> +
>> +    complete(&i2c->cmp);
>> +
>> +    return IRQ_HANDLED;
>> +}
>> +
>> +static irqreturn_t starfive_hdmi_hardirq(int irq, void *dev_id)
>> +{
>> +    struct starfive_hdmi *hdmi = dev_id;
>> +    irqreturn_t ret = IRQ_NONE;
>> +    u8 interrupt;
>> +
>> +    if (hdmi->i2c)
>> +        ret = starfive_hdmi_i2c_irq(hdmi);
>> +
>> +    interrupt = hdmi_readb(hdmi, HDMI_STATUS);
>> +    if (interrupt & m_INT_HOTPLUG) {
>> +        hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
>> +        ret = IRQ_WAKE_THREAD;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static irqreturn_t starfive_hdmi_irq(int irq, void *dev_id)
>> +{
>> +    struct starfive_hdmi *hdmi = dev_id;
>> +
>> +    drm_connector_helper_hpd_irq_event(&hdmi->connector);
>> +
>> +    return IRQ_HANDLED;
>> +}
>> +
>> +static int starfive_hdmi_i2c_read(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
>> +{
>> +    int length = msgs->len;
>> +    u8 *buf = msgs->buf;
>> +    int ret;
>> +
>> +    ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10);
> 
> read_poll_timeout
Hi Dmitry:
I think using wait_for_completion_timeout here is a bit clearer than read_poll_timeout logic
I2c_read has hardware interrupt dependency. so need wait the interrupt completion.
also it comes with timeout detection.

> 
>> +    if (!ret)
>> +        return -EAGAIN;
>> +
>> +    while (length--)
>> +        *buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
>> +
>> +    return 0;
>> +}
>> +
>> +static int starfive_hdmi_i2c_write(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
>> +{
>> +    /*
>> +     * The DDC module only support read EDID message, so
>> +     * we assume that each word write to this i2c adapter
>> +     * should be the offset of EDID word address.
>> +     */
>> +    if (msgs->len != 1 ||
>> +        (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR))
>> +        return -EINVAL;
>> +
>> +    reinit_completion(&hdmi->i2c->cmp);
>> +
>> +    if (msgs->addr == DDC_SEGMENT_ADDR)
>> +        hdmi->i2c->segment_addr = msgs->buf[0];
>> +    if (msgs->addr == DDC_ADDR)
>> +        hdmi->i2c->ddc_addr = msgs->buf[0];
>> +
>> +    /* Set edid fifo first addr */
>> +    hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
>> +
>> +    /* Set edid word address 0x00/0x80 */
>> +    hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
>> +
>> +    /* Set edid segment pointer */
>> +    hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
>> +
>> +    return 0;
>> +}
>> +
>> +static int starfive_hdmi_i2c_xfer(struct i2c_adapter *adap,
>> +                  struct i2c_msg *msgs, int num)
>> +{
>> +    struct starfive_hdmi *hdmi = i2c_get_adapdata(adap);
>> +    struct starfive_hdmi_i2c *i2c = hdmi->i2c;
>> +    int i, ret = 0;
>> +
>> +    mutex_lock(&i2c->lock);
>> +
>> +    /* Clear the EDID interrupt flag and unmute the interrupt */
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> +
>> +    for (i = 0; i < num; i++) {
>> +        DRM_DEV_DEBUG(hdmi->dev,
>> +                  "xfer: num: %d/%d, len: %d, flags: %#x\n",
>> +                  i + 1, num, msgs[i].len, msgs[i].flags);
>> +
>> +        if (msgs[i].flags & I2C_M_RD)
>> +            ret = starfive_hdmi_i2c_read(hdmi, &msgs[i]);
>> +        else
>> +            ret = starfive_hdmi_i2c_write(hdmi, &msgs[i]);
>> +
>> +        if (ret < 0)
>> +            break;
>> +    }
>> +
>> +    if (!ret)
>> +        ret = num;
>> +
>> +    /* Mute HDMI EDID interrupt */
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
>> +
>> +    mutex_unlock(&i2c->lock);
>> +
>> +    return ret;
>> +}
>> +
>> +static u32 starfive_hdmi_i2c_func(struct i2c_adapter *adapter)
>> +{
>> +    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
>> +}
>> +
>> +static const struct i2c_algorithm starfive_hdmi_algorithm = {
>> +    .master_xfer    = starfive_hdmi_i2c_xfer,
>> +    .functionality    = starfive_hdmi_i2c_func,
>> +};
>> +
>> +static struct i2c_adapter *starfive_hdmi_i2c_adapter(struct starfive_hdmi *hdmi)
>> +{
>> +    struct i2c_adapter *adap;
>> +    struct starfive_hdmi_i2c *i2c;
>> +    int ret;
>> +
>> +    i2c = drmm_kzalloc(hdmi->drm_dev, sizeof(*i2c), GFP_KERNEL);
>> +    if (!i2c)
>> +        return ERR_PTR(-ENOMEM);
>> +
>> +    mutex_init(&i2c->lock);
>> +    init_completion(&i2c->cmp);
>> +
>> +    adap = &i2c->adap;
>> +    adap->class = I2C_CLASS_DDC;
>> +    adap->owner = THIS_MODULE;
>> +    adap->dev.parent = hdmi->dev;
>> +    adap->algo = &starfive_hdmi_algorithm;
>> +    strscpy(adap->name, "Starfive HDMI", sizeof(adap->name));
>> +    i2c_set_adapdata(adap, hdmi);
>> +
>> +    ret = devm_i2c_add_adapter(hdmi->dev, adap);
>> +    if (ret) {
>> +        drm_warn(hdmi->drm_dev, "cannot add %s I2C adapter\n", adap->name);
>> +        return ERR_PTR(ret);
>> +    }
>> +
>> +    hdmi->i2c = i2c;
>> +
>> +    drm_info(hdmi->drm_dev, "registered %s I2C bus driver success\n", adap->name);
>> +
>> +    return adap;
>> +}
>> +
>> +static int starfive_hdmi_get_clk_rst(struct device *dev, struct starfive_hdmi *hdmi)
>> +{
>> +    hdmi->sys_clk = devm_clk_get(dev, "sysclk");
>> +    if (IS_ERR(hdmi->sys_clk)) {
>> +        dev_err(dev, "Unable to get HDMI sysclk clk\n");
>> +        return PTR_ERR(hdmi->sys_clk);
>> +    }
>> +    hdmi->mclk = devm_clk_get(dev, "mclk");
>> +    if (IS_ERR(hdmi->mclk)) {
>> +        dev_err(dev, "Unable to get HDMI mclk clk\n");
>> +        return PTR_ERR(hdmi->mclk);
>> +    }
>> +    hdmi->bclk = devm_clk_get(dev, "bclk");
>> +    if (IS_ERR(hdmi->bclk)) {
>> +        dev_err(dev, "Unable to get HDMI bclk clk\n");
>> +        return PTR_ERR(hdmi->bclk);
>> +    }
>> +    hdmi->tx_rst = devm_reset_control_get_by_index(dev, 0);
>> +    if (IS_ERR(hdmi->tx_rst)) {
>> +        dev_err(dev, "failed to get tx_rst reset\n");
>> +        return PTR_ERR(hdmi->tx_rst);
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int starfive_hdmi_bind(struct device *dev, struct device *master,
>> +                  void *data)
>> +{
>> +    struct platform_device *pdev = to_platform_device(dev);
>> +    struct drm_device *drm = dev_get_drvdata(master);
>> +    struct starfive_hdmi *hdmi;
>> +    struct resource *iores;
>> +    int irq;
>> +    int ret;
>> +
>> +    hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
>> +    if (!hdmi)
>> +        return -ENOMEM;
>> +
>> +    hdmi = drmm_simple_encoder_alloc(drm, struct starfive_hdmi,
>> +                     encoder, DRM_MODE_ENCODER_TMDS);
>> +
>> +    hdmi->dev = dev;
>> +    hdmi->drm_dev = drm;
>> +    dev_set_drvdata(dev, hdmi);
>> +
>> +    iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +    hdmi->regs = devm_ioremap_resource(dev, iores);
> 
> This should go to probe
> 
>> +    if (IS_ERR(hdmi->regs))
>> +        return PTR_ERR(hdmi->regs);
>> +
>> +    ret = starfive_hdmi_get_clk_rst(dev, hdmi);
>> +    if (ret < 0)
>> +        return ret;
> 
> And this
> 
>> +
>> +    ret = devm_pm_runtime_enable(dev);
>> +    if (ret)
>> +        return ret;
>> +
>> +    ret = pm_runtime_resume_and_get(dev);
>> +    if (ret)
>> +        return ret;
>> +
>> +    irq = platform_get_irq(pdev, 0);
> 
> And this
> 
>> +    if (irq < 0) {
>> +        ret = irq;
>> +        goto err_put_runtime_pm;
>> +    }
>> +
>> +    hdmi->ddc = starfive_hdmi_i2c_adapter(hdmi);
>> +    if (IS_ERR(hdmi->ddc)) {
>> +        ret = PTR_ERR(hdmi->ddc);
>> +        hdmi->ddc = NULL;
>> +        goto err_put_runtime_pm;
>> +    }
>> +
>> +    starfive_hdmi_i2c_init(hdmi);
>> +
>> +    ret = starfive_hdmi_register(drm, hdmi);
>> +    if (ret)
>> +        goto err_put_adapter;
>> +
>> +    /* Unmute hotplug interrupt */
>> +    hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
>> +
>> +    ret = devm_request_threaded_irq(dev, irq, starfive_hdmi_hardirq,
>> +                    starfive_hdmi_irq, IRQF_SHARED,
>> +                    dev_name(dev), hdmi);
>> +    if (ret < 0)
>> +        goto err_put_adapter;
>> +
>> +    pm_runtime_put_sync(dev);
>> +
>> +    return 0;
>> +
>> +err_put_adapter:
>> +    i2c_put_adapter(hdmi->ddc);
>> +err_put_runtime_pm:
>> +    pm_runtime_put_sync(dev);
>> +
>> +    return ret;
>> +}
>> +
>> +static const struct component_ops starfive_hdmi_ops = {
>> +    .bind    = starfive_hdmi_bind,
>> +};
>> +
>> +static int starfive_hdmi_probe(struct platform_device *pdev)
>> +{
>> +    return component_add(&pdev->dev, &starfive_hdmi_ops);
>> +}
>> +
>> +static int starfive_hdmi_remove(struct platform_device *pdev)
>> +{
>> +    component_del(&pdev->dev, &starfive_hdmi_ops);
>> +
>> +    return 0;
>> +}
>> +
>> +static const struct dev_pm_ops hdmi_pm_ops = {
>> +    SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
>> +    SET_LATE_SYSTEM_SLEEP_PM_OPS(hdmi_system_pm_suspend, hdmi_system_pm_resume)
>> +};
>> +
>> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
>> +    { .compatible = "starfive,jh7110-inno-hdmi",},
>> +    {},
>> +};
>> +MODULE_DEVICE_TABLE(of, starfive_hdmi_dt_ids);
>> +
>> +struct platform_driver starfive_hdmi_driver = {
>> +    .probe  = starfive_hdmi_probe,
>> +    .remove = starfive_hdmi_remove,
>> +    .driver = {
>> +        .name = "starfive-hdmi",
>> +        .of_match_table = starfive_hdmi_dt_ids,
>> +        .pm = &hdmi_pm_ops,
>> +    },
>> +};
>> +
>> +MODULE_AUTHOR("StarFive Corporation");
>> +MODULE_DESCRIPTION("Starfive HDMI Driver");
>> diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.h b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
>> new file mode 100644
>> index 000000000..1b9a11bca
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
>> @@ -0,0 +1,295 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
>> + */
>> +
>> +#ifndef __STARFIVE_HDMI_H__
>> +#define __STARFIVE_HDMI_H__
>> +
>> +#include <drm/bridge/dw_hdmi.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_edid.h>
>> +#include <drm/drm_of.h>
>> +#include <drm/drm_probe_helper.h>
>> +#include <drm/drm_simple_kms_helper.h>
>> +#include <linux/bitfield.h>
>> +#include <linux/bits.h>
>> +
>> +#define DDC_SEGMENT_ADDR        0x30
>> +
>> +#define HDMI_SCL_RATE            (100 * 1000)
>> +#define DDC_BUS_FREQ_L            0x4b
>> +#define DDC_BUS_FREQ_H            0x4c
>> +
>> +#define HDMI_SYS_CTRL            0x00
>> +#define m_RST_ANALOG            BIT(6)
>> +#define v_RST_ANALOG            0
>> +#define v_NOT_RST_ANALOG        BIT(6)
>> +#define m_RST_DIGITAL            BIT(5)
>> +#define v_RST_DIGITAL            0
>> +#define v_NOT_RST_DIGITAL        BIT(5)
>> +#define m_REG_CLK_INV            BIT(4)
>> +#define v_REG_CLK_NOT_INV        0
>> +#define v_REG_CLK_INV            BIT(4)
>> +#define m_VCLK_INV            BIT(3)
>> +#define v_VCLK_NOT_INV            0
>> +#define v_VCLK_INV            BIT(3)
>> +#define m_REG_CLK_SOURCE        BIT(2)
>> +#define v_REG_CLK_SOURCE_TMDS        0
>> +#define v_REG_CLK_SOURCE_SYS        BIT(2)
>> +#define m_POWER                BIT(1)
>> +#define v_PWR_ON            0
>> +#define v_PWR_OFF            BIT(1)
>> +#define m_INT_POL            BIT(0)
>> +#define v_INT_POL_HIGH            1
>> +#define v_INT_POL_LOW            0
>> +
>> +#define HDMI_AV_MUTE            0x05
>> +#define m_AVMUTE_CLEAR            BIT(7)
>> +#define m_AVMUTE_ENABLE            BIT(6)
>> +#define m_AUDIO_MUTE            BIT(1)
>> +#define m_VIDEO_BLACK            BIT(0)
>> +#define v_AVMUTE_CLEAR(n)        ((n) << 7)
>> +#define v_AVMUTE_ENABLE(n)        ((n) << 6)
>> +#define v_AUDIO_MUTE(n)            ((n) << 1)
>> +#define v_VIDEO_MUTE(n)            ((n) << 0)
>> +
>> +#define HDMI_VIDEO_TIMING_CTL        0x08
>> +#define v_VSYNC_POLARITY(n)        ((n) << 3)
>> +#define v_HSYNC_POLARITY(n)        ((n) << 2)
>> +#define v_INETLACE(n)            ((n) << 1)
>> +#define v_EXTERANL_VIDEO(n)        ((n) << 0)
>> +
>> +#define HDMI_VIDEO_EXT_HTOTAL_L        0x09
>> +#define HDMI_VIDEO_EXT_HTOTAL_H        0x0a
>> +#define HDMI_VIDEO_EXT_HBLANK_L        0x0b
>> +#define HDMI_VIDEO_EXT_HBLANK_H        0x0c
>> +#define HDMI_VIDEO_EXT_HDELAY_L        0x0d
>> +#define HDMI_VIDEO_EXT_HDELAY_H        0x0e
>> +#define HDMI_VIDEO_EXT_HDURATION_L    0x0f
>> +#define HDMI_VIDEO_EXT_HDURATION_H    0x10
>> +#define HDMI_VIDEO_EXT_VTOTAL_L        0x11
>> +#define HDMI_VIDEO_EXT_VTOTAL_H        0x12
>> +#define HDMI_VIDEO_EXT_VBLANK        0x13
>> +#define HDMI_VIDEO_EXT_VDELAY        0x14
>> +#define HDMI_VIDEO_EXT_VDURATION    0x15
>> +
>> +#define HDMI_EDID_SEGMENT_POINTER    0x4d
>> +#define HDMI_EDID_WORD_ADDR        0x4e
>> +#define HDMI_EDID_FIFO_OFFSET        0x4f
>> +#define HDMI_EDID_FIFO_ADDR        0x50
>> +
>> +#define HDMI_INTERRUPT_MASK1        0xc0
>> +#define HDMI_INTERRUPT_STATUS1        0xc1
>> +#define    m_INT_ACTIVE_VSYNC        BIT(5)
>> +#define m_INT_EDID_READY        BIT(2)
>> +
>> +#define HDMI_STATUS            0xc8
>> +#define m_HOTPLUG            BIT(7)
>> +#define m_MASK_INT_HOTPLUG        BIT(5)
>> +#define m_INT_HOTPLUG            BIT(1)
>> +#define v_MASK_INT_HOTPLUG(n)        (((n) & 0x1) << 5)
> 
> It is untypical to have field defines which start with lowercase letter.
> 
>> +
>> +#define HDMI_SYNC                    0xce
>> +
>> +#define UPDATE(x, h, l)                    FIELD_PREP(GENMASK(h, l), x)
>> +
>> +/* REG: 0x1a0 */
>> +#define STARFIVE_PRE_PLL_CONTROL            0x1a0
>> +#define STARFIVE_PCLK_VCO_DIV_5_MASK            BIT(1)
>> +#define STARFIVE_PCLK_VCO_DIV_5(x)            UPDATE(x, 1, 1)
>> +#define STARFIVE_PRE_PLL_POWER_DOWN            BIT(0)
>> +
>> +/* REG: 0x1a1 */
>> +#define STARFIVE_PRE_PLL_DIV_1                0x1a1
>> +#define STARFIVE_PRE_PLL_PRE_DIV_MASK            GENMASK(5, 0)
>> +#define STARFIVE_PRE_PLL_PRE_DIV(x)            UPDATE(x, 5, 0)
>> +
>> +/* REG: 0x1a2 */
>> +#define STARFIVE_PRE_PLL_DIV_2                0x1a2
>> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DOWN        BIT(7)
>> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE        BIT(6)
>> +#define STARFIVE_PRE_PLL_FRAC_DIV_DISABLE        UPDATE(3, 5, 4)
>> +#define STARFIVE_PRE_PLL_FB_DIV_11_8_MASK        GENMASK(3, 0)
>> +#define STARFIVE_PRE_PLL_FB_DIV_11_8(x)            UPDATE((x) >> 8, 3, 0)
>> +
>> +/* REG: 0x1a3 */
>> +#define STARFIVE_PRE_PLL_DIV_3                0x1a3
>> +#define STARFIVE_PRE_PLL_FB_DIV_7_0(x)            UPDATE(x, 7, 0)
>> +
>> +/* REG: 0x1a4*/
>> +#define STARFIVE_PRE_PLL_DIV_4                0x1a4
>> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C_MASK        GENMASK(1, 0)
>> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C(x)        UPDATE(x, 1, 0)
>> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B_MASK        GENMASK(3, 2)
>> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B(x)        UPDATE(x, 3, 2)
>> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A_MASK        GENMASK(5, 4)
>> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A(x)        UPDATE(x, 5, 4)
>> +
>> +/* REG: 0x1a5 */
>> +#define STARFIVE_PRE_PLL_DIV_5                0x1a5
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_SHIFT        5
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_MASK        GENMASK(6, 5)
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_B(x)            UPDATE(x, 6, 5)
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_A_MASK        GENMASK(4, 0)
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_A(x)            UPDATE(x, 4, 0)
>> +
>> +/* REG: 0x1a6 */
>> +#define STARFIVE_PRE_PLL_DIV_6                0x1a6
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_SHIFT        5
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_MASK        GENMASK(6, 5)
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_C(x)            UPDATE(x, 6, 5)
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_D_MASK        GENMASK(4, 0)
>> +#define STARFIVE_PRE_PLL_PCLK_DIV_D(x)            UPDATE(x, 4, 0)
>> +
>> +/* REG: 0x1a9 */
>> +#define STARFIVE_PRE_PLL_LOCK_STATUS            0x1a9
>> +
>> +/* REG: 0x1aa */
>> +#define STARFIVE_POST_PLL_DIV_1                0x1aa
>> +#define STARFIVE_POST_PLL_POST_DIV_ENABLE        GENMASK(3, 2)
>> +#define STARFIVE_POST_PLL_REFCLK_SEL_TMDS        BIT(1)
>> +#define STARFIVE_POST_PLL_POWER_DOWN            BIT(0)
>> +#define STARFIVE_POST_PLL_FB_DIV_8(x)            UPDATE(((x) >> 8) << 4, 4, 4)
>> +
>> +/* REG:0x1ab */
>> +#define STARFIVE_POST_PLL_DIV_2                0x1ab
>> +#define STARFIVE_POST_PLL_Pre_DIV_MASK            GENMASK(5, 0)
>> +#define STARFIVE_POST_PLL_PRE_DIV(x)            UPDATE(x, 5, 0)
>> +
>> +/* REG: 0x1ac */
>> +#define STARFIVE_POST_PLL_DIV_3                0x1ac
>> +#define STARFIVE_POST_PLL_FB_DIV_7_0(x)            UPDATE(x, 7, 0)
>> +
>> +/* REG: 0x1ad */
>> +#define STARFIVE_POST_PLL_DIV_4                0x1ad
>> +#define STARFIVE_POST_PLL_POST_DIV_MASK            GENMASK(2, 0)
>> +#define STARFIVE_POST_PLL_POST_DIV_2            0x0
>> +#define STARFIVE_POST_PLL_POST_DIV_4            0x1
>> +#define STARFIVE_POST_PLL_POST_DIV_8            0x3
>> +
>> +/* REG: 0x1af */
>> +#define STARFIVE_POST_PLL_LOCK_STATUS            0x1af
>> +
>> +/* REG: 0x1b0 */
>> +#define STARFIVE_BIAS_CONTROL                0x1b0
>> +#define STARFIVE_BIAS_ENABLE                BIT(2)
>> +
>> +/* REG: 0x1b2 */
>> +#define STARFIVE_TMDS_CONTROL                0x1b2
>> +#define STARFIVE_TMDS_CLK_DRIVER_EN            BIT(3)
>> +#define STARFIVE_TMDS_D2_DRIVER_EN            BIT(2)
>> +#define STARFIVE_TMDS_D1_DRIVER_EN            BIT(1)
>> +#define STARFIVE_TMDS_D0_DRIVER_EN            BIT(0)
>> +#define STARFIVE_TMDS_DRIVER_ENABLE            (STARFIVE_TMDS_CLK_DRIVER_EN | \
>> +                             STARFIVE_TMDS_D2_DRIVER_EN | \
>> +                             STARFIVE_TMDS_D1_DRIVER_EN | \
>> +                             STARFIVE_TMDS_D0_DRIVER_EN)
>> +
>> +/* REG: 0x1b4 */
>> +#define STARFIVE_LDO_CONTROL                0x1b4
>> +#define STARFIVE_LDO_D2_EN                BIT(2)
>> +#define STARFIVE_LDO_D1_EN                BIT(1)
>> +#define STARFIVE_LDO_D0_EN                BIT(0)
>> +#define STARFIVE_LDO_ENABLE                (STARFIVE_LDO_D2_EN | \
>> +                             STARFIVE_LDO_D1_EN | \
>> +                             STARFIVE_LDO_D0_EN)
>> +
>> +/* REG: 0x1be */
>> +#define STARFIVE_SERIALIER_CONTROL            0x1be
>> +#define STARFIVE_SERIALIER_D2_EN            BIT(6)
>> +#define STARFIVE_SERIALIER_D1_EN            BIT(5)
>> +#define STARFIVE_SERIALIER_D0_EN            BIT(4)
>> +#define STARFIVE_SERIALIER_EN                BIT(0)
>> +
>> +#define STARFIVE_SERIALIER_ENABLE            (STARFIVE_SERIALIER_D2_EN | \
>> +                             STARFIVE_SERIALIER_D1_EN | \
>> +                             STARFIVE_SERIALIER_D0_EN | \
>> +                             STARFIVE_SERIALIER_EN)
>> +
>> +/* REG: 0x1cc */
>> +#define STARFIVE_RX_CONTROL                0x1cc
>> +#define STARFIVE_RX_EN                    BIT(3)
>> +#define STARFIVE_RX_CHANNEL_2_EN            BIT(2)
>> +#define STARFIVE_RX_CHANNEL_1_EN            BIT(1)
>> +#define STARFIVE_RX_CHANNEL_0_EN            BIT(0)
>> +#define STARFIVE_RX_ENABLE                (STARFIVE_RX_EN | \
>> +                             STARFIVE_RX_CHANNEL_2_EN | \
>> +                             STARFIVE_RX_CHANNEL_1_EN | \
>> +                             STARFIVE_RX_CHANNEL_0_EN)
>> +
>> +/* REG: 0x1d1 */
>> +#define STARFIVE_PRE_PLL_FRAC_DIV_H            0x1d1
>> +#define STARFIVE_PRE_PLL_FRAC_DIV_23_16(x)        UPDATE((x) >> 16, 7, 0)
>> +/* REG: 0x1d2 */
>> +#define STARFIVE_PRE_PLL_FRAC_DIV_M            0x1d2
>> +#define STARFIVE_PRE_PLL_FRAC_DIV_15_8(x)        UPDATE((x) >> 8, 7, 0)
>> +/* REG: 0x1d3 */
>> +#define STARFIVE_PRE_PLL_FRAC_DIV_L            0x1d3
>> +#define STARFIVE_PRE_PLL_FRAC_DIV_7_0(x)        UPDATE(x, 7, 0)
>> +
>> +struct pre_pll_config {
>> +    unsigned long pixclock;
>> +    unsigned long tmdsclock;
>> +    u8 prediv;
>> +    u16 fbdiv;
>> +    u8 tmds_div_a;
>> +    u8 tmds_div_b;
>> +    u8 tmds_div_c;
>> +    u8 pclk_div_a;
>> +    u8 pclk_div_b;
>> +    u8 pclk_div_c;
>> +    u8 pclk_div_d;
>> +    u8 vco_div_5_en;
>> +    u32 fracdiv;
>> +};
>> +
>> +struct post_pll_config {
>> +    unsigned long tmdsclock;
>> +    u8 prediv;
>> +    u16 fbdiv;
>> +    u8 postdiv;
>> +    u8 post_div_en;
>> +    u8 version;
>> +};
>> +
>> +struct phy_config {
>> +    unsigned long    tmdsclock;
>> +    u8        regs[14];
>> +};
>> +
>> +struct hdmi_data_info {
>> +    int vic;
>> +    bool sink_is_hdmi;
>> +    bool sink_has_audio;
>> +    unsigned int enc_in_format;
>> +    unsigned int enc_out_format;
>> +    unsigned int colorimetry;
>> +};
>> +
>> +struct starfive_hdmi {
>> +    struct device *dev;
>> +    struct drm_device *drm_dev;
>> +    struct drm_encoder    encoder;
>> +    struct drm_connector    connector;
>> +
>> +    struct starfive_hdmi_i2c *i2c;
>> +
>> +    int irq;
>> +    struct clk *sys_clk;
>> +    struct clk *mclk;
>> +    struct clk *bclk;
>> +    struct reset_control *tx_rst;
>> +    void __iomem *regs;
>> +
>> +    struct i2c_adapter *ddc;
>> +
>> +    unsigned long tmds_rate;
>> +
>> +    struct hdmi_data_info    hdmi_data;
>> +    struct drm_display_mode previous_mode;
>> +    const struct pre_pll_config    *pre_cfg;
>> +    const struct post_pll_config    *post_cfg;
>> +};
>> +
>> +#endif /* __STARFIVE_HDMI_H__ */
>> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
>> index 3cd533cfa..9669354bd 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
>> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
>> @@ -126,6 +126,11 @@ static const struct component_master_ops vs_drm_ops = {
>>   static struct platform_driver *drm_sub_drivers[] = {
>>       &dc_platform_driver,
>>   +    /* connector + encoder*/
>> +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
>> +    &starfive_hdmi_driver,
>> +#endif
>> +
>>   };
>>     static struct component_match *vs_drm_match_add(struct device *dev)
>> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
>> index 369ad22d6..e90723419 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_drv.h
>> +++ b/drivers/gpu/drm/verisilicon/vs_drv.h
>> @@ -24,4 +24,8 @@ to_vs_drm_private(const struct drm_device *dev)
>>       return container_of(dev, struct vs_drm_device, base);
>>   }
>>   +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
>> +extern struct platform_driver starfive_hdmi_driver;
>> +#endif
>> +
>>   #endif /* __VS_DRV_H__ */
> 

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
  2023-11-13 12:11       ` Keith Zhao
  (?)
@ 2023-11-13 13:31         ` Dmitry Baryshkov
  -1 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-11-13 13:31 UTC (permalink / raw)
  To: Keith Zhao
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Maxime Ripard, Jagan Teki, Rob Herring,
	Chris Morgan, Paul Walmsley, Bjorn Andersson, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Shawn Guo, christian.koenig

On Mon, 13 Nov 2023 at 14:11, Keith Zhao <keith.zhao@starfivetech.com> wrote:
>
>
>
> On 2023/10/26 6:23, Dmitry Baryshkov wrote:
> > On 25/10/2023 13:39, Keith Zhao wrote:
> >> add hdmi driver as encoder and connect
> >>
> >> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> >> ---
> >>   drivers/gpu/drm/verisilicon/Kconfig         |   8 +-
> >>   drivers/gpu/drm/verisilicon/Makefile        |   1 +
> >>   drivers/gpu/drm/verisilicon/starfive_hdmi.c | 949 ++++++++++++++++++++
> >>   drivers/gpu/drm/verisilicon/starfive_hdmi.h | 295 ++++++
> >>   drivers/gpu/drm/verisilicon/vs_drv.c        |   5 +
> >>   drivers/gpu/drm/verisilicon/vs_drv.h        |   4 +
> >>   6 files changed, 1261 insertions(+), 1 deletion(-)
> >>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.c
> >>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.h
> >>
> >> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
> >> index 3a361f8c8..122c786e3 100644
> >> --- a/drivers/gpu/drm/verisilicon/Kconfig
> >> +++ b/drivers/gpu/drm/verisilicon/Kconfig
> >> @@ -12,4 +12,10 @@ config DRM_VERISILICON
> >>         setting and buffer management. It does not
> >>         provide 2D or 3D acceleration.
> >>   -
> >> +config DRM_VERISILICON_STARFIVE_HDMI
> >> +    bool "Starfive HDMI extensions"
> >> +    depends on DRM_VERISILICON
> >> +    help
> >> +       This selects support for StarFive soc specific extensions
> >> +       for the Innosilicon HDMI driver. If you want to enable
> >> +       HDMI on JH7110 based soc, you should select this option.
> >> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> >> index 1d48016ca..08350f25b 100644
> >> --- a/drivers/gpu/drm/verisilicon/Makefile
> >> +++ b/drivers/gpu/drm/verisilicon/Makefile
> >> @@ -7,5 +7,6 @@ vs_drm-objs := vs_dc_hw.o \
> >>           vs_modeset.o \
> >>           vs_plane.o
> >>   +vs_drm-$(CONFIG_DRM_VERISILICON_STARFIVE_HDMI) += starfive_hdmi.o
> >>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
> >>   diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.c b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
> >> new file mode 100644
> >> index 000000000..d296c4b71
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
> >> @@ -0,0 +1,949 @@
> >> +// SPDX-License-Identifier: GPL-2.0-only
> >> +/*
> >> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
> >> + */
> >> +
> >> +#include <linux/clk.h>
> >> +#include <linux/component.h>
> >> +#include <linux/delay.h>
> >> +#include <linux/err.h>
> >> +#include <linux/hdmi.h>
> >> +#include <linux/i2c.h>
> >> +#include <linux/irq.h>
> >> +#include <linux/media-bus-format.h>
> >> +#include <linux/mfd/syscon.h>
> >> +#include <linux/module.h>
> >> +#include <linux/mutex.h>
> >> +#include <linux/of_device.h>
> >> +#include <linux/pm_runtime.h>
> >> +#include <linux/reset.h>
> >> +
> >> +#include <drm/bridge/dw_hdmi.h>
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_edid.h>
> >> +#include <drm/drm_managed.h>
> >> +#include <drm/drm_of.h>
> >> +#include <drm/drm_probe_helper.h>
> >> +#include <drm/drm_simple_kms_helper.h>
> >> +
> >> +#include "starfive_hdmi.h"
> >> +#include "vs_drv.h"
> >> +#include "vs_crtc.h"
> >> +
> >> +static struct starfive_hdmi *encoder_to_hdmi(struct drm_encoder *encoder)
> >> +{
> >> +    return container_of(encoder, struct starfive_hdmi, encoder);
> >> +}
> >> +
> >> +static struct starfive_hdmi *connector_to_hdmi(struct drm_connector *connector)
> >> +{
> >> +    return container_of(connector, struct starfive_hdmi, connector);
> >> +}
> >> +
> >> +struct starfive_hdmi_i2c {
> >> +    struct i2c_adapter adap;
> >> +
> >> +    u8 ddc_addr;
> >> +    u8 segment_addr;
> >> +    /* protects the edid data when use i2c cmd to read edid */
> >> +    struct mutex lock;
> >> +    struct completion cmp;
> >> +};
> >> +
> >> +static const struct pre_pll_config pre_pll_cfg_table[] = {
> >> +    { 25175000,  25175000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0xf55555},
> >> +    { 25200000,  25200000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0},
> >> +    { 27000000,  27000000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0},
> >
> > Such data structures are usually pretyt limited and hard to handle. Could you please replace it with the runtime calculations of
> >
> >> +    { 27027000,  27027000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0x170a3d},
> >> +    { 28320000,  28320000, 1,  28, 2, 1, 1,  3, 0, 3, 4, 0, 0x51eb85},
> >> +    { 30240000,  30240000, 1,  30, 2, 1, 1,  3, 0, 3, 4, 0, 0x3d70a3},
> >> +    { 31500000,  31500000, 1,  31, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> >> +    { 33750000,  33750000, 1,  33, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
> >> +    { 36000000,  36000000, 1,  36, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    { 40000000,  40000000, 1,  80, 2, 2, 2, 12, 2, 2, 2, 0, 0},
> >> +    { 46970000,  46970000, 1,  46, 2, 1, 1,  3, 0, 3, 4, 0, 0xf851eb},
> >> +    { 49500000,  49500000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> >> +    { 49000000,  49000000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    { 50000000,  50000000, 1,  50, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    { 54000000,  54000000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    { 54054000,  54054000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0x0dd2f1},
> >> +    { 57284000,  57284000, 1,  57, 2, 1, 1,  3, 0, 3, 4, 0, 0x48b439},
> >> +    { 58230000,  58230000, 1,  58, 2, 1, 1,  3, 0, 3, 4, 0, 0x3ae147},
> >> +    { 59341000,  59341000, 1,  59, 2, 1, 1,  3, 0, 3, 4, 0, 0x574bc6},
> >> +    { 59400000,  59400000, 1,  99, 3, 1, 1,  1, 3, 3, 4, 0, 0},
> >> +    { 65000000,  65000000, 1, 130, 2, 2, 2,  12, 0, 2, 2, 0, 0},
> >> +    { 68250000,  68250000, 1, 68,  2, 1, 1,  3,  0, 3, 4, 0, 0x3fffff},
> >> +    { 71000000,  71000000, 1,  71, 2, 1, 1,  3, 0, 3,  4, 0, 0},
> >> +    { 74176000,  74176000, 1,  98, 1, 2, 2,  1, 2, 3, 4, 0, 0xe6ae6b},
> >> +    { 74250000,  74250000, 1,  99, 1, 2, 2,  1, 2, 3, 4, 0, 0},
> >> +    { 75000000,  75000000, 1,  75, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    { 78750000,  78750000, 1,  78, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
> >> +    { 79500000,  79500000, 1,  79, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> >> +    { 83500000,  83500000, 2, 167, 2, 1, 1,  1, 0, 0,  6, 0, 0},
> >> +    { 83500000, 104375000, 1, 104, 2, 1, 1,  1, 1, 0,  5, 0, 0x600000},
> >> +    { 84858000,  84858000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xdba5e2},
> >> +    { 85500000,  85500000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
> >> +    { 85750000,  85750000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
> >> +    { 85800000,  85800000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcccccc},
> >> +    { 88750000,  88750000, 1,  88, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
> >> +    { 89910000,  89910000, 1,  89, 2, 1, 1,  3, 0, 3, 4, 0, 0xe8f5c1},
> >> +    { 90000000,  90000000, 1,  90, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {101000000, 101000000, 1, 101, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {102250000, 102250000, 1, 102, 2, 1, 1,  3, 0, 3, 4, 0, 0x3fffff},
> >> +    {106500000, 106500000, 1, 106, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> >> +    {108000000, 108000000, 1,  90, 3, 0, 0,  5, 0, 2,  2, 0, 0},
> >> +    {119000000, 119000000, 1, 119, 2, 1, 1,  3, 0, 3,  4, 0, 0},
> >> +    {131481000, 131481000, 1,  131, 2, 1, 1,  3, 0, 3,  4, 0, 0x7b22d1},
> >> +    {135000000, 135000000, 1,  135, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {136750000, 136750000, 1,  136, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
> >> +    {147180000, 147180000, 1,  147, 2, 1, 1,  3, 0, 3, 4, 0, 0x2e147a},
> >> +    {148352000, 148352000, 1,  98, 1, 1, 1,  1, 2, 2, 2, 0, 0xe6ae6b},
> >> +    {148500000, 148500000, 1,  99, 1, 1, 1,  1, 2, 2, 2, 0, 0},
> >> +    {154000000, 154000000, 1, 154, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {156000000, 156000000, 1, 156, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {157000000, 157000000, 1, 157, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {162000000, 162000000, 1, 162, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {174250000, 174250000, 1, 145, 3, 0, 0,  5, 0, 2, 2, 0, 0x355555},
> >> +    {174500000, 174500000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> >> +    {174570000, 174570000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x91eb84},
> >> +    {175500000, 175500000, 1, 175, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> >> +    {185590000, 185590000, 1, 185, 2, 1, 1,  3, 0, 3, 4, 0, 0x970a3c},
> >> +    {187000000, 187000000, 1, 187, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {241500000, 241500000, 1, 161, 1, 1, 1,  4, 0, 2,  2, 0, 0},
> >> +    {241700000, 241700000, 1, 241, 2, 1, 1,  3, 0, 3,  4, 0, 0xb33332},
> >> +    {262750000, 262750000, 1, 262, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
> >> +    {296500000, 296500000, 1, 296, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
> >> +    {296703000, 296703000, 1,  98, 0, 1, 1,  1, 0, 2,  2, 0, 0xe6ae6b},
> >> +    {297000000, 297000000, 1,  99, 0, 1, 1,  1, 0, 2,  2, 0, 0},
> >> +    {594000000, 594000000, 1,  99, 0, 2, 0,  1, 0, 1,  1, 0, 0},
> >> +    {0, 0, 0,  0, 0, 0, 0,  0, 0, 0,  0, 0, 0},
> >> +};
> >> +
> >> +static const struct post_pll_config post_pll_cfg_table[] = {
> >> +    {25200000,    1, 80, 13, 3, 1},
> >> +    {27000000,    1, 40, 11, 3, 1},
> >> +    {33750000,    1, 40, 11, 3, 1},
> >> +    {49000000,    1, 20, 1, 3, 3},
> >> +    {241700000, 1, 20, 1, 3, 3},
> >> +    {297000000, 4, 20, 0, 0, 3},
> >> +    {594000000, 4, 20, 0, 0, 0},
> >> +    { /* sentinel */ }
> >> +};
> >> +
> >> +inline u8 hdmi_readb(struct starfive_hdmi *hdmi, u16 offset)
> >> +{
> >> +    return readl_relaxed(hdmi->regs + (offset) * 0x04);
> >> +}
> >> +
> >> +inline void hdmi_writeb(struct starfive_hdmi *hdmi, u16 offset, u32 val)
> >> +{
> >> +    writel_relaxed(val, hdmi->regs + (offset) * 0x04);
> >> +}
> >> +
> >> +inline void hdmi_modb(struct starfive_hdmi *hdmi, u16 offset,
> >> +                 u32 msk, u32 val)
> >> +{
> >> +    u8 temp = hdmi_readb(hdmi, offset) & ~msk;
> >> +
> >> +    temp |= val & msk;
> >> +    hdmi_writeb(hdmi, offset, temp);
> >> +}
> >> +
> >> +static int starfive_hdmi_enable_clk_deassert_rst(struct device *dev, struct starfive_hdmi *hdmi)
> >> +{
> >> +    int ret;
> >> +
> >> +    ret = clk_prepare_enable(hdmi->sys_clk);
> >> +    if (ret) {
> >> +        dev_err(dev, "Cannot enable HDMI sys clock: %d\n", ret);
> >> +        return ret;
> >> +    }
> >> +
> >> +    ret = clk_prepare_enable(hdmi->mclk);
> >> +    if (ret) {
> >> +        dev_err(dev, "Cannot enable HDMI mclk clock: %d\n", ret);
> >> +        goto err_mclk;
> >> +    }
> >> +    ret = clk_prepare_enable(hdmi->bclk);
> >
> > This code begs to use clk_bulk instead.
> >
> >> +    if (ret) {
> >> +        dev_err(dev, "Cannot enable HDMI bclk clock: %d\n", ret);
> >> +        goto err_bclk;
> >> +    }
> >> +    ret = reset_control_deassert(hdmi->tx_rst);
> >> +    if (ret < 0) {
> >> +        dev_err(dev, "failed to deassert tx_rst\n");
> >> +        goto err_rst;
> >> +    }
> >> +    return 0;
> >> +
> >> +err_rst:
> >> +    clk_disable_unprepare(hdmi->bclk);
> >> +err_bclk:
> >> +    clk_disable_unprepare(hdmi->mclk);
> >> +err_mclk:
> >> +    clk_disable_unprepare(hdmi->sys_clk);
> >> +    return ret;
> >> +}
> >> +
> >> +static void starfive_hdmi_disable_clk_assert_rst(struct device *dev, struct starfive_hdmi *hdmi)
> >> +{
> >> +    int ret;
> >> +
> >> +    ret = reset_control_assert(hdmi->tx_rst);
> >> +    if (ret < 0)
> >> +        dev_err(dev, "failed to assert tx_rst\n");
> >> +
> >> +    clk_disable_unprepare(hdmi->sys_clk);
> >> +    clk_disable_unprepare(hdmi->mclk);
> >> +    clk_disable_unprepare(hdmi->bclk);
> >> +}
> >> +
> >> +#ifdef CONFIG_PM_SLEEP
> >> +static int hdmi_system_pm_suspend(struct device *dev)
> >> +{
> >> +    return pm_runtime_force_suspend(dev);
> >> +}
> >> +
> >> +static int hdmi_system_pm_resume(struct device *dev)
> >> +{
> >> +    return pm_runtime_force_resume(dev);
> >> +}
> >> +#endif
> >> +
> >> +#ifdef CONFIG_PM
> >> +static int hdmi_runtime_suspend(struct device *dev)
> >> +{
> >> +    struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
> >> +
> >> +    starfive_hdmi_disable_clk_assert_rst(dev, hdmi);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int hdmi_runtime_resume(struct device *dev)
> >> +{
> >> +    struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
> >> +
> >> +    return starfive_hdmi_enable_clk_deassert_rst(dev, hdmi);
> >> +}
> >> +#endif
> >> +
> >> +static void starfive_hdmi_tx_phy_power_down(struct starfive_hdmi *hdmi)
> >> +{
> >> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
> >> +}
> >> +
> >> +static void starfive_hdmi_tx_phy_power_on(struct starfive_hdmi *hdmi)
> >> +{
> >> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
> >
> > It looks like one can inline these two helpers w/o too much troubles.
> >
> >> +}
> >> +
> >> +static void starfive_hdmi_config_pll(struct starfive_hdmi *hdmi)
> >> +{
> >> +    u32 val;
> >> +    u8 reg_1ad_value = hdmi->post_cfg->post_div_en ?
> >> +         hdmi->post_cfg->postdiv : 0x00;
> >> +    u8 reg_1aa_value = hdmi->post_cfg->post_div_en ?
> >> +         0x0e : 0x02;
> >> +
> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN);
> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1,
> >> +            STARFIVE_POST_PLL_POST_DIV_ENABLE |
> >> +            STARFIVE_POST_PLL_REFCLK_SEL_TMDS |
> >> +            STARFIVE_POST_PLL_POWER_DOWN);
> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_1, STARFIVE_PRE_PLL_PRE_DIV(hdmi->pre_cfg->prediv));
> >> +
> >> +    val = STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE | STARFIVE_SPREAD_SPECTRUM_MOD_DOWN;
> >> +    if (!hdmi->pre_cfg->fracdiv)
> >> +        val |= STARFIVE_PRE_PLL_FRAC_DIV_DISABLE;
> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_2,
> >> +            STARFIVE_PRE_PLL_FB_DIV_11_8(hdmi->pre_cfg->fbdiv) | val);
> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_3,
> >> +            STARFIVE_PRE_PLL_FB_DIV_7_0(hdmi->pre_cfg->fbdiv));
> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_4,
> >> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_C(hdmi->pre_cfg->tmds_div_c) |
> >> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_A(hdmi->pre_cfg->tmds_div_a) |
> >> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_B(hdmi->pre_cfg->tmds_div_b));
> >> +
> >> +    if (hdmi->pre_cfg->fracdiv) {
> >> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_L,
> >> +                STARFIVE_PRE_PLL_FRAC_DIV_7_0(hdmi->pre_cfg->fracdiv));
> >> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_M,
> >> +                STARFIVE_PRE_PLL_FRAC_DIV_15_8(hdmi->pre_cfg->fracdiv));
> >> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_H,
> >> +                STARFIVE_PRE_PLL_FRAC_DIV_23_16(hdmi->pre_cfg->fracdiv));
> >> +    }
> >> +
> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_5,
> >> +            STARFIVE_PRE_PLL_PCLK_DIV_A(hdmi->pre_cfg->pclk_div_a) |
> >> +            STARFIVE_PRE_PLL_PCLK_DIV_B(hdmi->pre_cfg->pclk_div_b));
> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_6,
> >> +            STARFIVE_PRE_PLL_PCLK_DIV_C(hdmi->pre_cfg->pclk_div_c) |
> >> +            STARFIVE_PRE_PLL_PCLK_DIV_D(hdmi->pre_cfg->pclk_div_d));
> >> +
> >> +    /*pre-pll power down*/
> >> +    hdmi_modb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN, 0);
> >> +
> >> +    hdmi_modb(hdmi, STARFIVE_POST_PLL_DIV_2, STARFIVE_POST_PLL_Pre_DIV_MASK,
> >> +          STARFIVE_POST_PLL_PRE_DIV(hdmi->post_cfg->prediv));
> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_3, hdmi->post_cfg->fbdiv & 0xff);
> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_4, reg_1ad_value);
> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1, reg_1aa_value);
> >> +}
> >> +
> >> +static void starfive_hdmi_tmds_driver_on(struct starfive_hdmi *hdmi)
> >> +{
> >> +    hdmi_modb(hdmi, STARFIVE_TMDS_CONTROL,
> >> +          STARFIVE_TMDS_DRIVER_ENABLE, STARFIVE_TMDS_DRIVER_ENABLE);
> >> +}
> >> +
> >> +static void starfive_hdmi_sync_tmds(struct starfive_hdmi *hdmi)
> >> +{
> >> +    /*first send 0 to this bit, then send 1 and keep 1 into this bit*/
> >> +    hdmi_writeb(hdmi, HDMI_SYNC, 0x0);
> >> +    hdmi_writeb(hdmi, HDMI_SYNC, 0x1);
> >> +}
> >> +
> >> +static void starfive_hdmi_i2c_init(struct starfive_hdmi *hdmi)
> >> +{
> >> +    int ddc_bus_freq;
> >> +
> >> +    ddc_bus_freq = (clk_get_rate(hdmi->sys_clk) >> 2) / HDMI_SCL_RATE;
> >> +
> >> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
> >> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
> >> +
> >> +    /* Clear the EDID interrupt flag and mute the interrupt */
> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
> >> +}
> >> +
> >> +static const
> >> +struct pre_pll_config *starfive_hdmi_phy_get_pre_pll_cfg(struct starfive_hdmi *hdmi,
> >> +                             unsigned long rate)
> >> +{
> >> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
> >> +
> >> +    rate = (rate / 1000) * 1000;
> >> +    for (; cfg->pixclock != 0; cfg++)
> >> +        if (cfg->tmdsclock == rate && cfg->pixclock == rate)
> >> +            break;
> >> +
> >> +    if (cfg->pixclock == 0)
> >> +        return ERR_PTR(-EINVAL);
> >> +
> >> +    return cfg;
> >> +}
> >> +
> >> +static int starfive_hdmi_phy_clk_set_rate(struct starfive_hdmi *hdmi)
> >> +{
> >> +    hdmi->post_cfg = post_pll_cfg_table;
> >> +
> >> +    hdmi->pre_cfg = starfive_hdmi_phy_get_pre_pll_cfg(hdmi, hdmi->tmds_rate);
> >> +    if (IS_ERR(hdmi->pre_cfg))
> >> +        return PTR_ERR(hdmi->pre_cfg);
> >> +
> >> +    for (; hdmi->post_cfg->tmdsclock != 0; hdmi->post_cfg++)
> >> +        if (hdmi->tmds_rate <= hdmi->post_cfg->tmdsclock)
> >> +            break;
> >> +
> >> +    starfive_hdmi_config_pll(hdmi);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int starfive_hdmi_config_video_timing(struct starfive_hdmi *hdmi,
> >> +                         struct drm_display_mode *mode)
> >> +{
> >> +    int value;
> >> +    /* Set detail external video timing */
> >> +    value = mode->htotal;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
> >
> > As you have hdmi_writeb already, adding hdmi_writew will help here.
> >
> >> +
> >> +    value = mode->htotal - mode->hdisplay;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
> >> +
> >> +    value = mode->htotal - mode->hsync_start;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
> >> +
> >> +    value = mode->hsync_end - mode->hsync_start;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
> >> +
> >> +    value = mode->vtotal;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
> >> +
> >> +    value = mode->vtotal - mode->vdisplay;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
> >> +
> >> +    value = mode->vtotal - mode->vsync_start;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
> >> +
> >> +    value = mode->vsync_end - mode->vsync_start;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
> >> +
> >> +    /* Set detail external video timing polarity and interlace mode */
> >> +    value = v_EXTERANL_VIDEO(1);
> >> +    value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
> >> +        v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
> >> +    value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
> >> +        v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
> >> +    value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
> >> +        v_INETLACE(1) : v_INETLACE(0);
> >> +
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
> >> +    return 0;
> >> +}
> >> +
> >> +static int starfive_hdmi_setup(struct starfive_hdmi *hdmi,
> >> +                   struct drm_display_mode *mode)
> >> +{
> >> +    hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
> >> +    hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
> >> +    hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
> >> +
> >> +    hdmi->tmds_rate = mode->clock * 1000;
> >> +    starfive_hdmi_phy_clk_set_rate(hdmi);
> >> +
> >> +    while (!(hdmi_readb(hdmi, STARFIVE_PRE_PLL_LOCK_STATUS) & 0x1))
> >> +        continue;
> >> +    while (!(hdmi_readb(hdmi, STARFIVE_POST_PLL_LOCK_STATUS) & 0x1))
> >> +        continue;
> >
> > Please use read_iopoll_timeout here.
> >
> >> +
> >> +    /*turn on LDO*/
> >> +    hdmi_writeb(hdmi, STARFIVE_LDO_CONTROL, STARFIVE_LDO_ENABLE);
> >> +    /*turn on serializer*/
> >> +    hdmi_writeb(hdmi, STARFIVE_SERIALIER_CONTROL, STARFIVE_SERIALIER_ENABLE);
> >> +
> >> +    starfive_hdmi_tx_phy_power_down(hdmi);
> >> +    starfive_hdmi_config_video_timing(hdmi, mode);
> >> +    starfive_hdmi_tx_phy_power_on(hdmi);
> >> +
> >> +    starfive_hdmi_tmds_driver_on(hdmi);
> >> +    starfive_hdmi_sync_tmds(hdmi);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static void starfive_hdmi_encoder_mode_set(struct drm_encoder *encoder,
> >> +                       struct drm_display_mode *mode,
> >> +                       struct drm_display_mode *adj_mode)
> >> +{
> >> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> >> +
> >> +    drm_mode_copy(&hdmi->previous_mode, adj_mode);
> >
> > Why do you need it?
> >
> >> +}
> >> +
> >> +static void starfive_hdmi_encoder_enable(struct drm_encoder *encoder)
> >> +{
> >> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> >> +    struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
> >> +    int ret, idx;
> >> +    struct drm_device *drm = hdmi->connector.dev;
> >> +
> >> +    if (drm && !drm_dev_enter(drm, &idx))
> >> +        return;
> >> +
> >> +    ret = pm_runtime_get_sync(hdmi->dev);
> >> +    if (ret < 0)
> >> +        return;
> >> +    mdelay(10);
> >> +    starfive_hdmi_setup(hdmi, mode);
> >> +
> >> +    if (drm)
> >> +        drm_dev_exit(idx);
> >> +}
> >> +
> >> +static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
> >> +{
> >> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> >> +
> >> +    int idx;
> >> +    struct drm_device *drm = hdmi->connector.dev;
> >> +
> >> +    if (drm && !drm_dev_enter(drm, &idx))
> >> +        return;
> >> +
> >> +    pm_runtime_put(hdmi->dev);
> >> +
> >> +    if (drm)
> >> +        drm_dev_exit(idx);
> >> +}
> >> +
> >> +static int
> >> +starfive_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
> >> +                   struct drm_crtc_state *crtc_state,
> >> +                   struct drm_connector_state *conn_state)
> >> +{
> >> +    bool valid = false;
> >> +    struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> >> +    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
> >> +
> >> +    vs_crtc_state->encoder_type = encoder->encoder_type;
> >> +    vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
> >> +
> >> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
> >> +    int pclk = mode->clock * 1000;
> >> +
> >> +    for (; cfg->pixclock != 0; cfg++) {
> >> +        if (pclk == cfg->pixclock) {
> >> +            if (pclk > 297000000)
> >
> > Magic value.
> >
> >> +                continue;
> >> +
> >> +            valid = true;
> >> +            break;
> >> +        }
> >> +    }
> >> +
> >> +    return (valid) ? 0 : -EINVAL;
> >> +}
> >> +
> >> +static const struct drm_encoder_helper_funcs starfive_hdmi_encoder_helper_funcs = {
> >> +    .enable     = starfive_hdmi_encoder_enable,
> >> +    .disable    = starfive_hdmi_encoder_disable,
> >> +    .mode_set   = starfive_hdmi_encoder_mode_set,
> >> +    .atomic_check = starfive_hdmi_encoder_atomic_check,
> >> +};
> >> +
> >> +static enum drm_connector_status
> >> +starfive_hdmi_connector_detect(struct drm_connector *connector, bool force)
> >> +{
> >> +    struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
> >> +    struct drm_device *drm = hdmi->connector.dev;
> >> +    int ret;
> >> +    int idx;
> >> +
> >> +    if (drm && !drm_dev_enter(drm, &idx))
> >> +        return connector_status_disconnected;
> >> +
> >> +    ret = pm_runtime_get_sync(hdmi->dev);
> >> +    if (ret < 0)
> >> +        return ret;
> >> +
> >> +    ret = (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
> >> +        connector_status_connected : connector_status_disconnected;
> >> +    pm_runtime_put(hdmi->dev);
> >> +
> >> +    if (drm)
> >> +        drm_dev_exit(idx);
> >> +
> >> +    return ret;
> >> +}
> >> +
> >> +static int starfive_hdmi_connector_get_modes(struct drm_connector *connector)
> >> +{
> >> +    struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
> >> +    struct edid *edid;
> >> +    int ret = 0;
> >> +
> >> +    if (!hdmi->ddc)
> >> +        return 0;
> >> +    ret = pm_runtime_get_sync(hdmi->dev);
> >> +    if (ret < 0)
> >> +        return ret;
> >> +
> >> +    edid = drm_get_edid(connector, hdmi->ddc);
> >> +    if (edid) {
> >> +        hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
> >> +        hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
> >> +        drm_connector_update_edid_property(connector, edid);
> >> +        ret = drm_add_edid_modes(connector, edid);
> >> +        kfree(edid);
> >> +    }
> >> +    pm_runtime_put(hdmi->dev);
> >
> > Can you use drm_connector_helper_get_modes_from_ddc() here? And then drm_display_info.is_hdmi and .has_audio.
> >
> >> +
> >> +    return ret;
> >> +}
> >> +
> >> +static enum drm_mode_status
> >> +starfive_hdmi_connector_mode_valid(struct drm_connector *connector,
> >> +                   struct drm_display_mode *mode)
> >> +{
> >> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
> >> +    int pclk = mode->clock * 1000;
> >> +    bool valid = false;
> >> +
> >> +    for (; cfg->pixclock != 0; cfg++) {
> >> +        if (pclk == cfg->pixclock) {
> >> +            if (pclk > 297000000)
> >
> > magic value
> >
> >> +                continue;
> >> +
> >> +            valid = true;
> >> +            break;
> >> +        }
> >> +    }
> >> +
> >> +    return (valid) ? MODE_OK : MODE_BAD;
> >> +}
> >> +
> >> +static int
> >> +starfive_hdmi_probe_single_connector_modes(struct drm_connector *connector,
> >> +                       u32 maxX, u32 maxY)
> >> +{
> >> +    return drm_helper_probe_single_connector_modes(connector, 3840, 2160);
> >> +}
> >> +
> >> +static const struct drm_connector_funcs starfive_hdmi_connector_funcs = {
> >> +    .fill_modes = starfive_hdmi_probe_single_connector_modes,
> >> +    .detect = starfive_hdmi_connector_detect,
> >> +    .reset = drm_atomic_helper_connector_reset,
> >> +    .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> >> +    .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> >> +};
> >> +
> >> +static struct drm_connector_helper_funcs starfive_hdmi_connector_helper_funcs = {
> >> +    .get_modes = starfive_hdmi_connector_get_modes,
> >> +    .mode_valid = starfive_hdmi_connector_mode_valid,
> >> +};
> >> +
> >> +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
> >> +{
> >> +    struct drm_encoder *encoder = &hdmi->encoder;
> >> +    struct device *dev = hdmi->dev;
> >> +
> >> +    encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
> >> +
> >> +    /*
> >> +     * If we failed to find the CRTC(s) which this encoder is
> >> +     * supposed to be connected to, it's because the CRTC has
> >> +     * not been registered yet.  Defer probing, and hope that
> >> +     * the required CRTC is added later.
> >> +     */
> >> +    if (encoder->possible_crtcs == 0)
> >> +        return -EPROBE_DEFER;
> >> +
> >> +    drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
> >> +
> >> +    hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> >> +
> >> +    drm_connector_helper_add(&hdmi->connector,
> >> +                 &starfive_hdmi_connector_helper_funcs);
> >> +    drmm_connector_init(drm, &hdmi->connector,
> >> +                &starfive_hdmi_connector_funcs,
> >> +                DRM_MODE_CONNECTOR_HDMIA,
> >
> > On an embedded device one can not be so sure. There can be MHL or HDMI Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.
> >
> >> +                hdmi->ddc);
> >> +
> >> +    drm_connector_attach_encoder(&hdmi->connector, encoder);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static irqreturn_t starfive_hdmi_i2c_irq(struct starfive_hdmi *hdmi)
> >> +{
> >> +    struct starfive_hdmi_i2c *i2c = hdmi->i2c;
> >> +    u8 stat;
> >> +
> >> +    stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
> >> +    if (!(stat & m_INT_EDID_READY))
> >> +        return IRQ_NONE;
> >> +
> >> +    /* Clear HDMI EDID interrupt flag */
> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
> >> +
> >> +    complete(&i2c->cmp);
> >> +
> >> +    return IRQ_HANDLED;
> >> +}
> >> +
> >> +static irqreturn_t starfive_hdmi_hardirq(int irq, void *dev_id)
> >> +{
> >> +    struct starfive_hdmi *hdmi = dev_id;
> >> +    irqreturn_t ret = IRQ_NONE;
> >> +    u8 interrupt;
> >> +
> >> +    if (hdmi->i2c)
> >> +        ret = starfive_hdmi_i2c_irq(hdmi);
> >> +
> >> +    interrupt = hdmi_readb(hdmi, HDMI_STATUS);
> >> +    if (interrupt & m_INT_HOTPLUG) {
> >> +        hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
> >> +        ret = IRQ_WAKE_THREAD;
> >> +    }
> >> +
> >> +    return ret;
> >> +}
> >> +
> >> +static irqreturn_t starfive_hdmi_irq(int irq, void *dev_id)
> >> +{
> >> +    struct starfive_hdmi *hdmi = dev_id;
> >> +
> >> +    drm_connector_helper_hpd_irq_event(&hdmi->connector);
> >> +
> >> +    return IRQ_HANDLED;
> >> +}
> >> +
> >> +static int starfive_hdmi_i2c_read(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
> >> +{
> >> +    int length = msgs->len;
> >> +    u8 *buf = msgs->buf;
> >> +    int ret;
> >> +
> >> +    ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10);
> >
> > read_poll_timeout
> Hi Dmitry:
> I think using wait_for_completion_timeout here is a bit clearer than read_poll_timeout logic
> I2c_read has hardware interrupt dependency. so need wait the interrupt completion.
> also it comes with timeout detection.

Hi,

Yes, maybe I was thinking about a different piece of code here.
wait_for_completion looks good here, indeed.

But this actually brings up another question. You reinit_comletion in
starfive_hdmi_i2c_write() and then wait_for_completion in
starfive_hdmi_i2c_read(). How does that work?



>
> >
> >> +    if (!ret)
> >> +        return -EAGAIN;
> >> +
> >> +    while (length--)
> >> +        *buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int starfive_hdmi_i2c_write(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
> >> +{
> >> +    /*
> >> +     * The DDC module only support read EDID message, so
> >> +     * we assume that each word write to this i2c adapter
> >> +     * should be the offset of EDID word address.
> >> +     */
> >> +    if (msgs->len != 1 ||
> >> +        (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR))
> >> +        return -EINVAL;
> >> +
> >> +    reinit_completion(&hdmi->i2c->cmp);
> >> +
> >> +    if (msgs->addr == DDC_SEGMENT_ADDR)
> >> +        hdmi->i2c->segment_addr = msgs->buf[0];
> >> +    if (msgs->addr == DDC_ADDR)
> >> +        hdmi->i2c->ddc_addr = msgs->buf[0];
> >> +
> >> +    /* Set edid fifo first addr */
> >> +    hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
> >> +
> >> +    /* Set edid word address 0x00/0x80 */
> >> +    hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
> >> +
> >> +    /* Set edid segment pointer */
> >> +    hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int starfive_hdmi_i2c_xfer(struct i2c_adapter *adap,
> >> +                  struct i2c_msg *msgs, int num)
> >> +{
> >> +    struct starfive_hdmi *hdmi = i2c_get_adapdata(adap);
> >> +    struct starfive_hdmi_i2c *i2c = hdmi->i2c;
> >> +    int i, ret = 0;
> >> +
> >> +    mutex_lock(&i2c->lock);
> >> +
> >> +    /* Clear the EDID interrupt flag and unmute the interrupt */
> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
> >> +
> >> +    for (i = 0; i < num; i++) {
> >> +        DRM_DEV_DEBUG(hdmi->dev,
> >> +                  "xfer: num: %d/%d, len: %d, flags: %#x\n",
> >> +                  i + 1, num, msgs[i].len, msgs[i].flags);
> >> +
> >> +        if (msgs[i].flags & I2C_M_RD)
> >> +            ret = starfive_hdmi_i2c_read(hdmi, &msgs[i]);
> >> +        else
> >> +            ret = starfive_hdmi_i2c_write(hdmi, &msgs[i]);
> >> +
> >> +        if (ret < 0)
> >> +            break;
> >> +    }
> >> +
> >> +    if (!ret)
> >> +        ret = num;
> >> +
> >> +    /* Mute HDMI EDID interrupt */
> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
> >> +
> >> +    mutex_unlock(&i2c->lock);
> >> +
> >> +    return ret;
> >> +}
> >> +
> >> +static u32 starfive_hdmi_i2c_func(struct i2c_adapter *adapter)
> >> +{
> >> +    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> >> +}
> >> +
> >> +static const struct i2c_algorithm starfive_hdmi_algorithm = {
> >> +    .master_xfer    = starfive_hdmi_i2c_xfer,
> >> +    .functionality    = starfive_hdmi_i2c_func,
> >> +};
> >> +
> >> +static struct i2c_adapter *starfive_hdmi_i2c_adapter(struct starfive_hdmi *hdmi)
> >> +{
> >> +    struct i2c_adapter *adap;
> >> +    struct starfive_hdmi_i2c *i2c;
> >> +    int ret;
> >> +
> >> +    i2c = drmm_kzalloc(hdmi->drm_dev, sizeof(*i2c), GFP_KERNEL);
> >> +    if (!i2c)
> >> +        return ERR_PTR(-ENOMEM);
> >> +
> >> +    mutex_init(&i2c->lock);
> >> +    init_completion(&i2c->cmp);
> >> +
> >> +    adap = &i2c->adap;
> >> +    adap->class = I2C_CLASS_DDC;
> >> +    adap->owner = THIS_MODULE;
> >> +    adap->dev.parent = hdmi->dev;
> >> +    adap->algo = &starfive_hdmi_algorithm;
> >> +    strscpy(adap->name, "Starfive HDMI", sizeof(adap->name));
> >> +    i2c_set_adapdata(adap, hdmi);
> >> +
> >> +    ret = devm_i2c_add_adapter(hdmi->dev, adap);
> >> +    if (ret) {
> >> +        drm_warn(hdmi->drm_dev, "cannot add %s I2C adapter\n", adap->name);
> >> +        return ERR_PTR(ret);
> >> +    }
> >> +
> >> +    hdmi->i2c = i2c;
> >> +
> >> +    drm_info(hdmi->drm_dev, "registered %s I2C bus driver success\n", adap->name);
> >> +
> >> +    return adap;
> >> +}
> >> +
> >> +static int starfive_hdmi_get_clk_rst(struct device *dev, struct starfive_hdmi *hdmi)
> >> +{
> >> +    hdmi->sys_clk = devm_clk_get(dev, "sysclk");
> >> +    if (IS_ERR(hdmi->sys_clk)) {
> >> +        dev_err(dev, "Unable to get HDMI sysclk clk\n");
> >> +        return PTR_ERR(hdmi->sys_clk);
> >> +    }
> >> +    hdmi->mclk = devm_clk_get(dev, "mclk");
> >> +    if (IS_ERR(hdmi->mclk)) {
> >> +        dev_err(dev, "Unable to get HDMI mclk clk\n");
> >> +        return PTR_ERR(hdmi->mclk);
> >> +    }
> >> +    hdmi->bclk = devm_clk_get(dev, "bclk");
> >> +    if (IS_ERR(hdmi->bclk)) {
> >> +        dev_err(dev, "Unable to get HDMI bclk clk\n");
> >> +        return PTR_ERR(hdmi->bclk);
> >> +    }
> >> +    hdmi->tx_rst = devm_reset_control_get_by_index(dev, 0);
> >> +    if (IS_ERR(hdmi->tx_rst)) {
> >> +        dev_err(dev, "failed to get tx_rst reset\n");
> >> +        return PTR_ERR(hdmi->tx_rst);
> >> +    }
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int starfive_hdmi_bind(struct device *dev, struct device *master,
> >> +                  void *data)
> >> +{
> >> +    struct platform_device *pdev = to_platform_device(dev);
> >> +    struct drm_device *drm = dev_get_drvdata(master);
> >> +    struct starfive_hdmi *hdmi;
> >> +    struct resource *iores;
> >> +    int irq;
> >> +    int ret;
> >> +
> >> +    hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
> >> +    if (!hdmi)
> >> +        return -ENOMEM;
> >> +
> >> +    hdmi = drmm_simple_encoder_alloc(drm, struct starfive_hdmi,
> >> +                     encoder, DRM_MODE_ENCODER_TMDS);
> >> +
> >> +    hdmi->dev = dev;
> >> +    hdmi->drm_dev = drm;
> >> +    dev_set_drvdata(dev, hdmi);
> >> +
> >> +    iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >> +    hdmi->regs = devm_ioremap_resource(dev, iores);
> >
> > This should go to probe
> >
> >> +    if (IS_ERR(hdmi->regs))
> >> +        return PTR_ERR(hdmi->regs);
> >> +
> >> +    ret = starfive_hdmi_get_clk_rst(dev, hdmi);
> >> +    if (ret < 0)
> >> +        return ret;
> >
> > And this
> >
> >> +
> >> +    ret = devm_pm_runtime_enable(dev);
> >> +    if (ret)
> >> +        return ret;
> >> +
> >> +    ret = pm_runtime_resume_and_get(dev);
> >> +    if (ret)
> >> +        return ret;
> >> +
> >> +    irq = platform_get_irq(pdev, 0);
> >
> > And this
> >
> >> +    if (irq < 0) {
> >> +        ret = irq;
> >> +        goto err_put_runtime_pm;
> >> +    }
> >> +
> >> +    hdmi->ddc = starfive_hdmi_i2c_adapter(hdmi);
> >> +    if (IS_ERR(hdmi->ddc)) {
> >> +        ret = PTR_ERR(hdmi->ddc);
> >> +        hdmi->ddc = NULL;
> >> +        goto err_put_runtime_pm;
> >> +    }
> >> +
> >> +    starfive_hdmi_i2c_init(hdmi);
> >> +
> >> +    ret = starfive_hdmi_register(drm, hdmi);
> >> +    if (ret)
> >> +        goto err_put_adapter;
> >> +
> >> +    /* Unmute hotplug interrupt */
> >> +    hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
> >> +
> >> +    ret = devm_request_threaded_irq(dev, irq, starfive_hdmi_hardirq,
> >> +                    starfive_hdmi_irq, IRQF_SHARED,
> >> +                    dev_name(dev), hdmi);
> >> +    if (ret < 0)
> >> +        goto err_put_adapter;
> >> +
> >> +    pm_runtime_put_sync(dev);
> >> +
> >> +    return 0;
> >> +
> >> +err_put_adapter:
> >> +    i2c_put_adapter(hdmi->ddc);
> >> +err_put_runtime_pm:
> >> +    pm_runtime_put_sync(dev);
> >> +
> >> +    return ret;
> >> +}
> >> +
> >> +static const struct component_ops starfive_hdmi_ops = {
> >> +    .bind    = starfive_hdmi_bind,
> >> +};
> >> +
> >> +static int starfive_hdmi_probe(struct platform_device *pdev)
> >> +{
> >> +    return component_add(&pdev->dev, &starfive_hdmi_ops);
> >> +}
> >> +
> >> +static int starfive_hdmi_remove(struct platform_device *pdev)
> >> +{
> >> +    component_del(&pdev->dev, &starfive_hdmi_ops);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static const struct dev_pm_ops hdmi_pm_ops = {
> >> +    SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
> >> +    SET_LATE_SYSTEM_SLEEP_PM_OPS(hdmi_system_pm_suspend, hdmi_system_pm_resume)
> >> +};
> >> +
> >> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
> >> +    { .compatible = "starfive,jh7110-inno-hdmi",},
> >> +    {},
> >> +};
> >> +MODULE_DEVICE_TABLE(of, starfive_hdmi_dt_ids);
> >> +
> >> +struct platform_driver starfive_hdmi_driver = {
> >> +    .probe  = starfive_hdmi_probe,
> >> +    .remove = starfive_hdmi_remove,
> >> +    .driver = {
> >> +        .name = "starfive-hdmi",
> >> +        .of_match_table = starfive_hdmi_dt_ids,
> >> +        .pm = &hdmi_pm_ops,
> >> +    },
> >> +};
> >> +
> >> +MODULE_AUTHOR("StarFive Corporation");
> >> +MODULE_DESCRIPTION("Starfive HDMI Driver");
> >> diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.h b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
> >> new file mode 100644
> >> index 000000000..1b9a11bca
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
> >> @@ -0,0 +1,295 @@
> >> +/* SPDX-License-Identifier: GPL-2.0-only */
> >> +/*
> >> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
> >> + */
> >> +
> >> +#ifndef __STARFIVE_HDMI_H__
> >> +#define __STARFIVE_HDMI_H__
> >> +
> >> +#include <drm/bridge/dw_hdmi.h>
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_edid.h>
> >> +#include <drm/drm_of.h>
> >> +#include <drm/drm_probe_helper.h>
> >> +#include <drm/drm_simple_kms_helper.h>
> >> +#include <linux/bitfield.h>
> >> +#include <linux/bits.h>
> >> +
> >> +#define DDC_SEGMENT_ADDR        0x30
> >> +
> >> +#define HDMI_SCL_RATE            (100 * 1000)
> >> +#define DDC_BUS_FREQ_L            0x4b
> >> +#define DDC_BUS_FREQ_H            0x4c
> >> +
> >> +#define HDMI_SYS_CTRL            0x00
> >> +#define m_RST_ANALOG            BIT(6)
> >> +#define v_RST_ANALOG            0
> >> +#define v_NOT_RST_ANALOG        BIT(6)
> >> +#define m_RST_DIGITAL            BIT(5)
> >> +#define v_RST_DIGITAL            0
> >> +#define v_NOT_RST_DIGITAL        BIT(5)
> >> +#define m_REG_CLK_INV            BIT(4)
> >> +#define v_REG_CLK_NOT_INV        0
> >> +#define v_REG_CLK_INV            BIT(4)
> >> +#define m_VCLK_INV            BIT(3)
> >> +#define v_VCLK_NOT_INV            0
> >> +#define v_VCLK_INV            BIT(3)
> >> +#define m_REG_CLK_SOURCE        BIT(2)
> >> +#define v_REG_CLK_SOURCE_TMDS        0
> >> +#define v_REG_CLK_SOURCE_SYS        BIT(2)
> >> +#define m_POWER                BIT(1)
> >> +#define v_PWR_ON            0
> >> +#define v_PWR_OFF            BIT(1)
> >> +#define m_INT_POL            BIT(0)
> >> +#define v_INT_POL_HIGH            1
> >> +#define v_INT_POL_LOW            0
> >> +
> >> +#define HDMI_AV_MUTE            0x05
> >> +#define m_AVMUTE_CLEAR            BIT(7)
> >> +#define m_AVMUTE_ENABLE            BIT(6)
> >> +#define m_AUDIO_MUTE            BIT(1)
> >> +#define m_VIDEO_BLACK            BIT(0)
> >> +#define v_AVMUTE_CLEAR(n)        ((n) << 7)
> >> +#define v_AVMUTE_ENABLE(n)        ((n) << 6)
> >> +#define v_AUDIO_MUTE(n)            ((n) << 1)
> >> +#define v_VIDEO_MUTE(n)            ((n) << 0)
> >> +
> >> +#define HDMI_VIDEO_TIMING_CTL        0x08
> >> +#define v_VSYNC_POLARITY(n)        ((n) << 3)
> >> +#define v_HSYNC_POLARITY(n)        ((n) << 2)
> >> +#define v_INETLACE(n)            ((n) << 1)
> >> +#define v_EXTERANL_VIDEO(n)        ((n) << 0)
> >> +
> >> +#define HDMI_VIDEO_EXT_HTOTAL_L        0x09
> >> +#define HDMI_VIDEO_EXT_HTOTAL_H        0x0a
> >> +#define HDMI_VIDEO_EXT_HBLANK_L        0x0b
> >> +#define HDMI_VIDEO_EXT_HBLANK_H        0x0c
> >> +#define HDMI_VIDEO_EXT_HDELAY_L        0x0d
> >> +#define HDMI_VIDEO_EXT_HDELAY_H        0x0e
> >> +#define HDMI_VIDEO_EXT_HDURATION_L    0x0f
> >> +#define HDMI_VIDEO_EXT_HDURATION_H    0x10
> >> +#define HDMI_VIDEO_EXT_VTOTAL_L        0x11
> >> +#define HDMI_VIDEO_EXT_VTOTAL_H        0x12
> >> +#define HDMI_VIDEO_EXT_VBLANK        0x13
> >> +#define HDMI_VIDEO_EXT_VDELAY        0x14
> >> +#define HDMI_VIDEO_EXT_VDURATION    0x15
> >> +
> >> +#define HDMI_EDID_SEGMENT_POINTER    0x4d
> >> +#define HDMI_EDID_WORD_ADDR        0x4e
> >> +#define HDMI_EDID_FIFO_OFFSET        0x4f
> >> +#define HDMI_EDID_FIFO_ADDR        0x50
> >> +
> >> +#define HDMI_INTERRUPT_MASK1        0xc0
> >> +#define HDMI_INTERRUPT_STATUS1        0xc1
> >> +#define    m_INT_ACTIVE_VSYNC        BIT(5)
> >> +#define m_INT_EDID_READY        BIT(2)
> >> +
> >> +#define HDMI_STATUS            0xc8
> >> +#define m_HOTPLUG            BIT(7)
> >> +#define m_MASK_INT_HOTPLUG        BIT(5)
> >> +#define m_INT_HOTPLUG            BIT(1)
> >> +#define v_MASK_INT_HOTPLUG(n)        (((n) & 0x1) << 5)
> >
> > It is untypical to have field defines which start with lowercase letter.
> >
> >> +
> >> +#define HDMI_SYNC                    0xce
> >> +
> >> +#define UPDATE(x, h, l)                    FIELD_PREP(GENMASK(h, l), x)
> >> +
> >> +/* REG: 0x1a0 */
> >> +#define STARFIVE_PRE_PLL_CONTROL            0x1a0
> >> +#define STARFIVE_PCLK_VCO_DIV_5_MASK            BIT(1)
> >> +#define STARFIVE_PCLK_VCO_DIV_5(x)            UPDATE(x, 1, 1)
> >> +#define STARFIVE_PRE_PLL_POWER_DOWN            BIT(0)
> >> +
> >> +/* REG: 0x1a1 */
> >> +#define STARFIVE_PRE_PLL_DIV_1                0x1a1
> >> +#define STARFIVE_PRE_PLL_PRE_DIV_MASK            GENMASK(5, 0)
> >> +#define STARFIVE_PRE_PLL_PRE_DIV(x)            UPDATE(x, 5, 0)
> >> +
> >> +/* REG: 0x1a2 */
> >> +#define STARFIVE_PRE_PLL_DIV_2                0x1a2
> >> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DOWN        BIT(7)
> >> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE        BIT(6)
> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_DISABLE        UPDATE(3, 5, 4)
> >> +#define STARFIVE_PRE_PLL_FB_DIV_11_8_MASK        GENMASK(3, 0)
> >> +#define STARFIVE_PRE_PLL_FB_DIV_11_8(x)            UPDATE((x) >> 8, 3, 0)
> >> +
> >> +/* REG: 0x1a3 */
> >> +#define STARFIVE_PRE_PLL_DIV_3                0x1a3
> >> +#define STARFIVE_PRE_PLL_FB_DIV_7_0(x)            UPDATE(x, 7, 0)
> >> +
> >> +/* REG: 0x1a4*/
> >> +#define STARFIVE_PRE_PLL_DIV_4                0x1a4
> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C_MASK        GENMASK(1, 0)
> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C(x)        UPDATE(x, 1, 0)
> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B_MASK        GENMASK(3, 2)
> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B(x)        UPDATE(x, 3, 2)
> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A_MASK        GENMASK(5, 4)
> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A(x)        UPDATE(x, 5, 4)
> >> +
> >> +/* REG: 0x1a5 */
> >> +#define STARFIVE_PRE_PLL_DIV_5                0x1a5
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_SHIFT        5
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_MASK        GENMASK(6, 5)
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_B(x)            UPDATE(x, 6, 5)
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_A_MASK        GENMASK(4, 0)
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_A(x)            UPDATE(x, 4, 0)
> >> +
> >> +/* REG: 0x1a6 */
> >> +#define STARFIVE_PRE_PLL_DIV_6                0x1a6
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_SHIFT        5
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_MASK        GENMASK(6, 5)
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_C(x)            UPDATE(x, 6, 5)
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_D_MASK        GENMASK(4, 0)
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_D(x)            UPDATE(x, 4, 0)
> >> +
> >> +/* REG: 0x1a9 */
> >> +#define STARFIVE_PRE_PLL_LOCK_STATUS            0x1a9
> >> +
> >> +/* REG: 0x1aa */
> >> +#define STARFIVE_POST_PLL_DIV_1                0x1aa
> >> +#define STARFIVE_POST_PLL_POST_DIV_ENABLE        GENMASK(3, 2)
> >> +#define STARFIVE_POST_PLL_REFCLK_SEL_TMDS        BIT(1)
> >> +#define STARFIVE_POST_PLL_POWER_DOWN            BIT(0)
> >> +#define STARFIVE_POST_PLL_FB_DIV_8(x)            UPDATE(((x) >> 8) << 4, 4, 4)
> >> +
> >> +/* REG:0x1ab */
> >> +#define STARFIVE_POST_PLL_DIV_2                0x1ab
> >> +#define STARFIVE_POST_PLL_Pre_DIV_MASK            GENMASK(5, 0)
> >> +#define STARFIVE_POST_PLL_PRE_DIV(x)            UPDATE(x, 5, 0)
> >> +
> >> +/* REG: 0x1ac */
> >> +#define STARFIVE_POST_PLL_DIV_3                0x1ac
> >> +#define STARFIVE_POST_PLL_FB_DIV_7_0(x)            UPDATE(x, 7, 0)
> >> +
> >> +/* REG: 0x1ad */
> >> +#define STARFIVE_POST_PLL_DIV_4                0x1ad
> >> +#define STARFIVE_POST_PLL_POST_DIV_MASK            GENMASK(2, 0)
> >> +#define STARFIVE_POST_PLL_POST_DIV_2            0x0
> >> +#define STARFIVE_POST_PLL_POST_DIV_4            0x1
> >> +#define STARFIVE_POST_PLL_POST_DIV_8            0x3
> >> +
> >> +/* REG: 0x1af */
> >> +#define STARFIVE_POST_PLL_LOCK_STATUS            0x1af
> >> +
> >> +/* REG: 0x1b0 */
> >> +#define STARFIVE_BIAS_CONTROL                0x1b0
> >> +#define STARFIVE_BIAS_ENABLE                BIT(2)
> >> +
> >> +/* REG: 0x1b2 */
> >> +#define STARFIVE_TMDS_CONTROL                0x1b2
> >> +#define STARFIVE_TMDS_CLK_DRIVER_EN            BIT(3)
> >> +#define STARFIVE_TMDS_D2_DRIVER_EN            BIT(2)
> >> +#define STARFIVE_TMDS_D1_DRIVER_EN            BIT(1)
> >> +#define STARFIVE_TMDS_D0_DRIVER_EN            BIT(0)
> >> +#define STARFIVE_TMDS_DRIVER_ENABLE            (STARFIVE_TMDS_CLK_DRIVER_EN | \
> >> +                             STARFIVE_TMDS_D2_DRIVER_EN | \
> >> +                             STARFIVE_TMDS_D1_DRIVER_EN | \
> >> +                             STARFIVE_TMDS_D0_DRIVER_EN)
> >> +
> >> +/* REG: 0x1b4 */
> >> +#define STARFIVE_LDO_CONTROL                0x1b4
> >> +#define STARFIVE_LDO_D2_EN                BIT(2)
> >> +#define STARFIVE_LDO_D1_EN                BIT(1)
> >> +#define STARFIVE_LDO_D0_EN                BIT(0)
> >> +#define STARFIVE_LDO_ENABLE                (STARFIVE_LDO_D2_EN | \
> >> +                             STARFIVE_LDO_D1_EN | \
> >> +                             STARFIVE_LDO_D0_EN)
> >> +
> >> +/* REG: 0x1be */
> >> +#define STARFIVE_SERIALIER_CONTROL            0x1be
> >> +#define STARFIVE_SERIALIER_D2_EN            BIT(6)
> >> +#define STARFIVE_SERIALIER_D1_EN            BIT(5)
> >> +#define STARFIVE_SERIALIER_D0_EN            BIT(4)
> >> +#define STARFIVE_SERIALIER_EN                BIT(0)
> >> +
> >> +#define STARFIVE_SERIALIER_ENABLE            (STARFIVE_SERIALIER_D2_EN | \
> >> +                             STARFIVE_SERIALIER_D1_EN | \
> >> +                             STARFIVE_SERIALIER_D0_EN | \
> >> +                             STARFIVE_SERIALIER_EN)
> >> +
> >> +/* REG: 0x1cc */
> >> +#define STARFIVE_RX_CONTROL                0x1cc
> >> +#define STARFIVE_RX_EN                    BIT(3)
> >> +#define STARFIVE_RX_CHANNEL_2_EN            BIT(2)
> >> +#define STARFIVE_RX_CHANNEL_1_EN            BIT(1)
> >> +#define STARFIVE_RX_CHANNEL_0_EN            BIT(0)
> >> +#define STARFIVE_RX_ENABLE                (STARFIVE_RX_EN | \
> >> +                             STARFIVE_RX_CHANNEL_2_EN | \
> >> +                             STARFIVE_RX_CHANNEL_1_EN | \
> >> +                             STARFIVE_RX_CHANNEL_0_EN)
> >> +
> >> +/* REG: 0x1d1 */
> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_H            0x1d1
> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_23_16(x)        UPDATE((x) >> 16, 7, 0)
> >> +/* REG: 0x1d2 */
> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_M            0x1d2
> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_15_8(x)        UPDATE((x) >> 8, 7, 0)
> >> +/* REG: 0x1d3 */
> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_L            0x1d3
> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_7_0(x)        UPDATE(x, 7, 0)
> >> +
> >> +struct pre_pll_config {
> >> +    unsigned long pixclock;
> >> +    unsigned long tmdsclock;
> >> +    u8 prediv;
> >> +    u16 fbdiv;
> >> +    u8 tmds_div_a;
> >> +    u8 tmds_div_b;
> >> +    u8 tmds_div_c;
> >> +    u8 pclk_div_a;
> >> +    u8 pclk_div_b;
> >> +    u8 pclk_div_c;
> >> +    u8 pclk_div_d;
> >> +    u8 vco_div_5_en;
> >> +    u32 fracdiv;
> >> +};
> >> +
> >> +struct post_pll_config {
> >> +    unsigned long tmdsclock;
> >> +    u8 prediv;
> >> +    u16 fbdiv;
> >> +    u8 postdiv;
> >> +    u8 post_div_en;
> >> +    u8 version;
> >> +};
> >> +
> >> +struct phy_config {
> >> +    unsigned long    tmdsclock;
> >> +    u8        regs[14];
> >> +};
> >> +
> >> +struct hdmi_data_info {
> >> +    int vic;
> >> +    bool sink_is_hdmi;
> >> +    bool sink_has_audio;
> >> +    unsigned int enc_in_format;
> >> +    unsigned int enc_out_format;
> >> +    unsigned int colorimetry;
> >> +};
> >> +
> >> +struct starfive_hdmi {
> >> +    struct device *dev;
> >> +    struct drm_device *drm_dev;
> >> +    struct drm_encoder    encoder;
> >> +    struct drm_connector    connector;
> >> +
> >> +    struct starfive_hdmi_i2c *i2c;
> >> +
> >> +    int irq;
> >> +    struct clk *sys_clk;
> >> +    struct clk *mclk;
> >> +    struct clk *bclk;
> >> +    struct reset_control *tx_rst;
> >> +    void __iomem *regs;
> >> +
> >> +    struct i2c_adapter *ddc;
> >> +
> >> +    unsigned long tmds_rate;
> >> +
> >> +    struct hdmi_data_info    hdmi_data;
> >> +    struct drm_display_mode previous_mode;
> >> +    const struct pre_pll_config    *pre_cfg;
> >> +    const struct post_pll_config    *post_cfg;
> >> +};
> >> +
> >> +#endif /* __STARFIVE_HDMI_H__ */
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
> >> index 3cd533cfa..9669354bd 100644
> >> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
> >> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> >> @@ -126,6 +126,11 @@ static const struct component_master_ops vs_drm_ops = {
> >>   static struct platform_driver *drm_sub_drivers[] = {
> >>       &dc_platform_driver,
> >>   +    /* connector + encoder*/
> >> +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
> >> +    &starfive_hdmi_driver,
> >> +#endif
> >> +
> >>   };
> >>     static struct component_match *vs_drm_match_add(struct device *dev)
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
> >> index 369ad22d6..e90723419 100644
> >> --- a/drivers/gpu/drm/verisilicon/vs_drv.h
> >> +++ b/drivers/gpu/drm/verisilicon/vs_drv.h
> >> @@ -24,4 +24,8 @@ to_vs_drm_private(const struct drm_device *dev)
> >>       return container_of(dev, struct vs_drm_device, base);
> >>   }
> >>   +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
> >> +extern struct platform_driver starfive_hdmi_driver;
> >> +#endif
> >> +
> >>   #endif /* __VS_DRV_H__ */
> >



-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-11-13 13:31         ` Dmitry Baryshkov
  0 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-11-13 13:31 UTC (permalink / raw)
  To: Keith Zhao
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Maxime Ripard, Jagan Teki, Rob Herring,
	Chris Morgan, Paul Walmsley, Bjorn Andersson, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Shawn Guo, christian.koenig

On Mon, 13 Nov 2023 at 14:11, Keith Zhao <keith.zhao@starfivetech.com> wrote:
>
>
>
> On 2023/10/26 6:23, Dmitry Baryshkov wrote:
> > On 25/10/2023 13:39, Keith Zhao wrote:
> >> add hdmi driver as encoder and connect
> >>
> >> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> >> ---
> >>   drivers/gpu/drm/verisilicon/Kconfig         |   8 +-
> >>   drivers/gpu/drm/verisilicon/Makefile        |   1 +
> >>   drivers/gpu/drm/verisilicon/starfive_hdmi.c | 949 ++++++++++++++++++++
> >>   drivers/gpu/drm/verisilicon/starfive_hdmi.h | 295 ++++++
> >>   drivers/gpu/drm/verisilicon/vs_drv.c        |   5 +
> >>   drivers/gpu/drm/verisilicon/vs_drv.h        |   4 +
> >>   6 files changed, 1261 insertions(+), 1 deletion(-)
> >>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.c
> >>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.h
> >>
> >> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
> >> index 3a361f8c8..122c786e3 100644
> >> --- a/drivers/gpu/drm/verisilicon/Kconfig
> >> +++ b/drivers/gpu/drm/verisilicon/Kconfig
> >> @@ -12,4 +12,10 @@ config DRM_VERISILICON
> >>         setting and buffer management. It does not
> >>         provide 2D or 3D acceleration.
> >>   -
> >> +config DRM_VERISILICON_STARFIVE_HDMI
> >> +    bool "Starfive HDMI extensions"
> >> +    depends on DRM_VERISILICON
> >> +    help
> >> +       This selects support for StarFive soc specific extensions
> >> +       for the Innosilicon HDMI driver. If you want to enable
> >> +       HDMI on JH7110 based soc, you should select this option.
> >> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> >> index 1d48016ca..08350f25b 100644
> >> --- a/drivers/gpu/drm/verisilicon/Makefile
> >> +++ b/drivers/gpu/drm/verisilicon/Makefile
> >> @@ -7,5 +7,6 @@ vs_drm-objs := vs_dc_hw.o \
> >>           vs_modeset.o \
> >>           vs_plane.o
> >>   +vs_drm-$(CONFIG_DRM_VERISILICON_STARFIVE_HDMI) += starfive_hdmi.o
> >>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
> >>   diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.c b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
> >> new file mode 100644
> >> index 000000000..d296c4b71
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
> >> @@ -0,0 +1,949 @@
> >> +// SPDX-License-Identifier: GPL-2.0-only
> >> +/*
> >> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
> >> + */
> >> +
> >> +#include <linux/clk.h>
> >> +#include <linux/component.h>
> >> +#include <linux/delay.h>
> >> +#include <linux/err.h>
> >> +#include <linux/hdmi.h>
> >> +#include <linux/i2c.h>
> >> +#include <linux/irq.h>
> >> +#include <linux/media-bus-format.h>
> >> +#include <linux/mfd/syscon.h>
> >> +#include <linux/module.h>
> >> +#include <linux/mutex.h>
> >> +#include <linux/of_device.h>
> >> +#include <linux/pm_runtime.h>
> >> +#include <linux/reset.h>
> >> +
> >> +#include <drm/bridge/dw_hdmi.h>
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_edid.h>
> >> +#include <drm/drm_managed.h>
> >> +#include <drm/drm_of.h>
> >> +#include <drm/drm_probe_helper.h>
> >> +#include <drm/drm_simple_kms_helper.h>
> >> +
> >> +#include "starfive_hdmi.h"
> >> +#include "vs_drv.h"
> >> +#include "vs_crtc.h"
> >> +
> >> +static struct starfive_hdmi *encoder_to_hdmi(struct drm_encoder *encoder)
> >> +{
> >> +    return container_of(encoder, struct starfive_hdmi, encoder);
> >> +}
> >> +
> >> +static struct starfive_hdmi *connector_to_hdmi(struct drm_connector *connector)
> >> +{
> >> +    return container_of(connector, struct starfive_hdmi, connector);
> >> +}
> >> +
> >> +struct starfive_hdmi_i2c {
> >> +    struct i2c_adapter adap;
> >> +
> >> +    u8 ddc_addr;
> >> +    u8 segment_addr;
> >> +    /* protects the edid data when use i2c cmd to read edid */
> >> +    struct mutex lock;
> >> +    struct completion cmp;
> >> +};
> >> +
> >> +static const struct pre_pll_config pre_pll_cfg_table[] = {
> >> +    { 25175000,  25175000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0xf55555},
> >> +    { 25200000,  25200000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0},
> >> +    { 27000000,  27000000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0},
> >
> > Such data structures are usually pretyt limited and hard to handle. Could you please replace it with the runtime calculations of
> >
> >> +    { 27027000,  27027000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0x170a3d},
> >> +    { 28320000,  28320000, 1,  28, 2, 1, 1,  3, 0, 3, 4, 0, 0x51eb85},
> >> +    { 30240000,  30240000, 1,  30, 2, 1, 1,  3, 0, 3, 4, 0, 0x3d70a3},
> >> +    { 31500000,  31500000, 1,  31, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> >> +    { 33750000,  33750000, 1,  33, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
> >> +    { 36000000,  36000000, 1,  36, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    { 40000000,  40000000, 1,  80, 2, 2, 2, 12, 2, 2, 2, 0, 0},
> >> +    { 46970000,  46970000, 1,  46, 2, 1, 1,  3, 0, 3, 4, 0, 0xf851eb},
> >> +    { 49500000,  49500000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> >> +    { 49000000,  49000000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    { 50000000,  50000000, 1,  50, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    { 54000000,  54000000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    { 54054000,  54054000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0x0dd2f1},
> >> +    { 57284000,  57284000, 1,  57, 2, 1, 1,  3, 0, 3, 4, 0, 0x48b439},
> >> +    { 58230000,  58230000, 1,  58, 2, 1, 1,  3, 0, 3, 4, 0, 0x3ae147},
> >> +    { 59341000,  59341000, 1,  59, 2, 1, 1,  3, 0, 3, 4, 0, 0x574bc6},
> >> +    { 59400000,  59400000, 1,  99, 3, 1, 1,  1, 3, 3, 4, 0, 0},
> >> +    { 65000000,  65000000, 1, 130, 2, 2, 2,  12, 0, 2, 2, 0, 0},
> >> +    { 68250000,  68250000, 1, 68,  2, 1, 1,  3,  0, 3, 4, 0, 0x3fffff},
> >> +    { 71000000,  71000000, 1,  71, 2, 1, 1,  3, 0, 3,  4, 0, 0},
> >> +    { 74176000,  74176000, 1,  98, 1, 2, 2,  1, 2, 3, 4, 0, 0xe6ae6b},
> >> +    { 74250000,  74250000, 1,  99, 1, 2, 2,  1, 2, 3, 4, 0, 0},
> >> +    { 75000000,  75000000, 1,  75, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    { 78750000,  78750000, 1,  78, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
> >> +    { 79500000,  79500000, 1,  79, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> >> +    { 83500000,  83500000, 2, 167, 2, 1, 1,  1, 0, 0,  6, 0, 0},
> >> +    { 83500000, 104375000, 1, 104, 2, 1, 1,  1, 1, 0,  5, 0, 0x600000},
> >> +    { 84858000,  84858000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xdba5e2},
> >> +    { 85500000,  85500000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
> >> +    { 85750000,  85750000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
> >> +    { 85800000,  85800000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcccccc},
> >> +    { 88750000,  88750000, 1,  88, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
> >> +    { 89910000,  89910000, 1,  89, 2, 1, 1,  3, 0, 3, 4, 0, 0xe8f5c1},
> >> +    { 90000000,  90000000, 1,  90, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {101000000, 101000000, 1, 101, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {102250000, 102250000, 1, 102, 2, 1, 1,  3, 0, 3, 4, 0, 0x3fffff},
> >> +    {106500000, 106500000, 1, 106, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> >> +    {108000000, 108000000, 1,  90, 3, 0, 0,  5, 0, 2,  2, 0, 0},
> >> +    {119000000, 119000000, 1, 119, 2, 1, 1,  3, 0, 3,  4, 0, 0},
> >> +    {131481000, 131481000, 1,  131, 2, 1, 1,  3, 0, 3,  4, 0, 0x7b22d1},
> >> +    {135000000, 135000000, 1,  135, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {136750000, 136750000, 1,  136, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
> >> +    {147180000, 147180000, 1,  147, 2, 1, 1,  3, 0, 3, 4, 0, 0x2e147a},
> >> +    {148352000, 148352000, 1,  98, 1, 1, 1,  1, 2, 2, 2, 0, 0xe6ae6b},
> >> +    {148500000, 148500000, 1,  99, 1, 1, 1,  1, 2, 2, 2, 0, 0},
> >> +    {154000000, 154000000, 1, 154, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {156000000, 156000000, 1, 156, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {157000000, 157000000, 1, 157, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {162000000, 162000000, 1, 162, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {174250000, 174250000, 1, 145, 3, 0, 0,  5, 0, 2, 2, 0, 0x355555},
> >> +    {174500000, 174500000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> >> +    {174570000, 174570000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x91eb84},
> >> +    {175500000, 175500000, 1, 175, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> >> +    {185590000, 185590000, 1, 185, 2, 1, 1,  3, 0, 3, 4, 0, 0x970a3c},
> >> +    {187000000, 187000000, 1, 187, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {241500000, 241500000, 1, 161, 1, 1, 1,  4, 0, 2,  2, 0, 0},
> >> +    {241700000, 241700000, 1, 241, 2, 1, 1,  3, 0, 3,  4, 0, 0xb33332},
> >> +    {262750000, 262750000, 1, 262, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
> >> +    {296500000, 296500000, 1, 296, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
> >> +    {296703000, 296703000, 1,  98, 0, 1, 1,  1, 0, 2,  2, 0, 0xe6ae6b},
> >> +    {297000000, 297000000, 1,  99, 0, 1, 1,  1, 0, 2,  2, 0, 0},
> >> +    {594000000, 594000000, 1,  99, 0, 2, 0,  1, 0, 1,  1, 0, 0},
> >> +    {0, 0, 0,  0, 0, 0, 0,  0, 0, 0,  0, 0, 0},
> >> +};
> >> +
> >> +static const struct post_pll_config post_pll_cfg_table[] = {
> >> +    {25200000,    1, 80, 13, 3, 1},
> >> +    {27000000,    1, 40, 11, 3, 1},
> >> +    {33750000,    1, 40, 11, 3, 1},
> >> +    {49000000,    1, 20, 1, 3, 3},
> >> +    {241700000, 1, 20, 1, 3, 3},
> >> +    {297000000, 4, 20, 0, 0, 3},
> >> +    {594000000, 4, 20, 0, 0, 0},
> >> +    { /* sentinel */ }
> >> +};
> >> +
> >> +inline u8 hdmi_readb(struct starfive_hdmi *hdmi, u16 offset)
> >> +{
> >> +    return readl_relaxed(hdmi->regs + (offset) * 0x04);
> >> +}
> >> +
> >> +inline void hdmi_writeb(struct starfive_hdmi *hdmi, u16 offset, u32 val)
> >> +{
> >> +    writel_relaxed(val, hdmi->regs + (offset) * 0x04);
> >> +}
> >> +
> >> +inline void hdmi_modb(struct starfive_hdmi *hdmi, u16 offset,
> >> +                 u32 msk, u32 val)
> >> +{
> >> +    u8 temp = hdmi_readb(hdmi, offset) & ~msk;
> >> +
> >> +    temp |= val & msk;
> >> +    hdmi_writeb(hdmi, offset, temp);
> >> +}
> >> +
> >> +static int starfive_hdmi_enable_clk_deassert_rst(struct device *dev, struct starfive_hdmi *hdmi)
> >> +{
> >> +    int ret;
> >> +
> >> +    ret = clk_prepare_enable(hdmi->sys_clk);
> >> +    if (ret) {
> >> +        dev_err(dev, "Cannot enable HDMI sys clock: %d\n", ret);
> >> +        return ret;
> >> +    }
> >> +
> >> +    ret = clk_prepare_enable(hdmi->mclk);
> >> +    if (ret) {
> >> +        dev_err(dev, "Cannot enable HDMI mclk clock: %d\n", ret);
> >> +        goto err_mclk;
> >> +    }
> >> +    ret = clk_prepare_enable(hdmi->bclk);
> >
> > This code begs to use clk_bulk instead.
> >
> >> +    if (ret) {
> >> +        dev_err(dev, "Cannot enable HDMI bclk clock: %d\n", ret);
> >> +        goto err_bclk;
> >> +    }
> >> +    ret = reset_control_deassert(hdmi->tx_rst);
> >> +    if (ret < 0) {
> >> +        dev_err(dev, "failed to deassert tx_rst\n");
> >> +        goto err_rst;
> >> +    }
> >> +    return 0;
> >> +
> >> +err_rst:
> >> +    clk_disable_unprepare(hdmi->bclk);
> >> +err_bclk:
> >> +    clk_disable_unprepare(hdmi->mclk);
> >> +err_mclk:
> >> +    clk_disable_unprepare(hdmi->sys_clk);
> >> +    return ret;
> >> +}
> >> +
> >> +static void starfive_hdmi_disable_clk_assert_rst(struct device *dev, struct starfive_hdmi *hdmi)
> >> +{
> >> +    int ret;
> >> +
> >> +    ret = reset_control_assert(hdmi->tx_rst);
> >> +    if (ret < 0)
> >> +        dev_err(dev, "failed to assert tx_rst\n");
> >> +
> >> +    clk_disable_unprepare(hdmi->sys_clk);
> >> +    clk_disable_unprepare(hdmi->mclk);
> >> +    clk_disable_unprepare(hdmi->bclk);
> >> +}
> >> +
> >> +#ifdef CONFIG_PM_SLEEP
> >> +static int hdmi_system_pm_suspend(struct device *dev)
> >> +{
> >> +    return pm_runtime_force_suspend(dev);
> >> +}
> >> +
> >> +static int hdmi_system_pm_resume(struct device *dev)
> >> +{
> >> +    return pm_runtime_force_resume(dev);
> >> +}
> >> +#endif
> >> +
> >> +#ifdef CONFIG_PM
> >> +static int hdmi_runtime_suspend(struct device *dev)
> >> +{
> >> +    struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
> >> +
> >> +    starfive_hdmi_disable_clk_assert_rst(dev, hdmi);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int hdmi_runtime_resume(struct device *dev)
> >> +{
> >> +    struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
> >> +
> >> +    return starfive_hdmi_enable_clk_deassert_rst(dev, hdmi);
> >> +}
> >> +#endif
> >> +
> >> +static void starfive_hdmi_tx_phy_power_down(struct starfive_hdmi *hdmi)
> >> +{
> >> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
> >> +}
> >> +
> >> +static void starfive_hdmi_tx_phy_power_on(struct starfive_hdmi *hdmi)
> >> +{
> >> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
> >
> > It looks like one can inline these two helpers w/o too much troubles.
> >
> >> +}
> >> +
> >> +static void starfive_hdmi_config_pll(struct starfive_hdmi *hdmi)
> >> +{
> >> +    u32 val;
> >> +    u8 reg_1ad_value = hdmi->post_cfg->post_div_en ?
> >> +         hdmi->post_cfg->postdiv : 0x00;
> >> +    u8 reg_1aa_value = hdmi->post_cfg->post_div_en ?
> >> +         0x0e : 0x02;
> >> +
> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN);
> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1,
> >> +            STARFIVE_POST_PLL_POST_DIV_ENABLE |
> >> +            STARFIVE_POST_PLL_REFCLK_SEL_TMDS |
> >> +            STARFIVE_POST_PLL_POWER_DOWN);
> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_1, STARFIVE_PRE_PLL_PRE_DIV(hdmi->pre_cfg->prediv));
> >> +
> >> +    val = STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE | STARFIVE_SPREAD_SPECTRUM_MOD_DOWN;
> >> +    if (!hdmi->pre_cfg->fracdiv)
> >> +        val |= STARFIVE_PRE_PLL_FRAC_DIV_DISABLE;
> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_2,
> >> +            STARFIVE_PRE_PLL_FB_DIV_11_8(hdmi->pre_cfg->fbdiv) | val);
> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_3,
> >> +            STARFIVE_PRE_PLL_FB_DIV_7_0(hdmi->pre_cfg->fbdiv));
> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_4,
> >> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_C(hdmi->pre_cfg->tmds_div_c) |
> >> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_A(hdmi->pre_cfg->tmds_div_a) |
> >> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_B(hdmi->pre_cfg->tmds_div_b));
> >> +
> >> +    if (hdmi->pre_cfg->fracdiv) {
> >> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_L,
> >> +                STARFIVE_PRE_PLL_FRAC_DIV_7_0(hdmi->pre_cfg->fracdiv));
> >> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_M,
> >> +                STARFIVE_PRE_PLL_FRAC_DIV_15_8(hdmi->pre_cfg->fracdiv));
> >> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_H,
> >> +                STARFIVE_PRE_PLL_FRAC_DIV_23_16(hdmi->pre_cfg->fracdiv));
> >> +    }
> >> +
> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_5,
> >> +            STARFIVE_PRE_PLL_PCLK_DIV_A(hdmi->pre_cfg->pclk_div_a) |
> >> +            STARFIVE_PRE_PLL_PCLK_DIV_B(hdmi->pre_cfg->pclk_div_b));
> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_6,
> >> +            STARFIVE_PRE_PLL_PCLK_DIV_C(hdmi->pre_cfg->pclk_div_c) |
> >> +            STARFIVE_PRE_PLL_PCLK_DIV_D(hdmi->pre_cfg->pclk_div_d));
> >> +
> >> +    /*pre-pll power down*/
> >> +    hdmi_modb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN, 0);
> >> +
> >> +    hdmi_modb(hdmi, STARFIVE_POST_PLL_DIV_2, STARFIVE_POST_PLL_Pre_DIV_MASK,
> >> +          STARFIVE_POST_PLL_PRE_DIV(hdmi->post_cfg->prediv));
> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_3, hdmi->post_cfg->fbdiv & 0xff);
> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_4, reg_1ad_value);
> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1, reg_1aa_value);
> >> +}
> >> +
> >> +static void starfive_hdmi_tmds_driver_on(struct starfive_hdmi *hdmi)
> >> +{
> >> +    hdmi_modb(hdmi, STARFIVE_TMDS_CONTROL,
> >> +          STARFIVE_TMDS_DRIVER_ENABLE, STARFIVE_TMDS_DRIVER_ENABLE);
> >> +}
> >> +
> >> +static void starfive_hdmi_sync_tmds(struct starfive_hdmi *hdmi)
> >> +{
> >> +    /*first send 0 to this bit, then send 1 and keep 1 into this bit*/
> >> +    hdmi_writeb(hdmi, HDMI_SYNC, 0x0);
> >> +    hdmi_writeb(hdmi, HDMI_SYNC, 0x1);
> >> +}
> >> +
> >> +static void starfive_hdmi_i2c_init(struct starfive_hdmi *hdmi)
> >> +{
> >> +    int ddc_bus_freq;
> >> +
> >> +    ddc_bus_freq = (clk_get_rate(hdmi->sys_clk) >> 2) / HDMI_SCL_RATE;
> >> +
> >> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
> >> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
> >> +
> >> +    /* Clear the EDID interrupt flag and mute the interrupt */
> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
> >> +}
> >> +
> >> +static const
> >> +struct pre_pll_config *starfive_hdmi_phy_get_pre_pll_cfg(struct starfive_hdmi *hdmi,
> >> +                             unsigned long rate)
> >> +{
> >> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
> >> +
> >> +    rate = (rate / 1000) * 1000;
> >> +    for (; cfg->pixclock != 0; cfg++)
> >> +        if (cfg->tmdsclock == rate && cfg->pixclock == rate)
> >> +            break;
> >> +
> >> +    if (cfg->pixclock == 0)
> >> +        return ERR_PTR(-EINVAL);
> >> +
> >> +    return cfg;
> >> +}
> >> +
> >> +static int starfive_hdmi_phy_clk_set_rate(struct starfive_hdmi *hdmi)
> >> +{
> >> +    hdmi->post_cfg = post_pll_cfg_table;
> >> +
> >> +    hdmi->pre_cfg = starfive_hdmi_phy_get_pre_pll_cfg(hdmi, hdmi->tmds_rate);
> >> +    if (IS_ERR(hdmi->pre_cfg))
> >> +        return PTR_ERR(hdmi->pre_cfg);
> >> +
> >> +    for (; hdmi->post_cfg->tmdsclock != 0; hdmi->post_cfg++)
> >> +        if (hdmi->tmds_rate <= hdmi->post_cfg->tmdsclock)
> >> +            break;
> >> +
> >> +    starfive_hdmi_config_pll(hdmi);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int starfive_hdmi_config_video_timing(struct starfive_hdmi *hdmi,
> >> +                         struct drm_display_mode *mode)
> >> +{
> >> +    int value;
> >> +    /* Set detail external video timing */
> >> +    value = mode->htotal;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
> >
> > As you have hdmi_writeb already, adding hdmi_writew will help here.
> >
> >> +
> >> +    value = mode->htotal - mode->hdisplay;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
> >> +
> >> +    value = mode->htotal - mode->hsync_start;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
> >> +
> >> +    value = mode->hsync_end - mode->hsync_start;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
> >> +
> >> +    value = mode->vtotal;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
> >> +
> >> +    value = mode->vtotal - mode->vdisplay;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
> >> +
> >> +    value = mode->vtotal - mode->vsync_start;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
> >> +
> >> +    value = mode->vsync_end - mode->vsync_start;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
> >> +
> >> +    /* Set detail external video timing polarity and interlace mode */
> >> +    value = v_EXTERANL_VIDEO(1);
> >> +    value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
> >> +        v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
> >> +    value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
> >> +        v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
> >> +    value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
> >> +        v_INETLACE(1) : v_INETLACE(0);
> >> +
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
> >> +    return 0;
> >> +}
> >> +
> >> +static int starfive_hdmi_setup(struct starfive_hdmi *hdmi,
> >> +                   struct drm_display_mode *mode)
> >> +{
> >> +    hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
> >> +    hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
> >> +    hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
> >> +
> >> +    hdmi->tmds_rate = mode->clock * 1000;
> >> +    starfive_hdmi_phy_clk_set_rate(hdmi);
> >> +
> >> +    while (!(hdmi_readb(hdmi, STARFIVE_PRE_PLL_LOCK_STATUS) & 0x1))
> >> +        continue;
> >> +    while (!(hdmi_readb(hdmi, STARFIVE_POST_PLL_LOCK_STATUS) & 0x1))
> >> +        continue;
> >
> > Please use read_iopoll_timeout here.
> >
> >> +
> >> +    /*turn on LDO*/
> >> +    hdmi_writeb(hdmi, STARFIVE_LDO_CONTROL, STARFIVE_LDO_ENABLE);
> >> +    /*turn on serializer*/
> >> +    hdmi_writeb(hdmi, STARFIVE_SERIALIER_CONTROL, STARFIVE_SERIALIER_ENABLE);
> >> +
> >> +    starfive_hdmi_tx_phy_power_down(hdmi);
> >> +    starfive_hdmi_config_video_timing(hdmi, mode);
> >> +    starfive_hdmi_tx_phy_power_on(hdmi);
> >> +
> >> +    starfive_hdmi_tmds_driver_on(hdmi);
> >> +    starfive_hdmi_sync_tmds(hdmi);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static void starfive_hdmi_encoder_mode_set(struct drm_encoder *encoder,
> >> +                       struct drm_display_mode *mode,
> >> +                       struct drm_display_mode *adj_mode)
> >> +{
> >> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> >> +
> >> +    drm_mode_copy(&hdmi->previous_mode, adj_mode);
> >
> > Why do you need it?
> >
> >> +}
> >> +
> >> +static void starfive_hdmi_encoder_enable(struct drm_encoder *encoder)
> >> +{
> >> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> >> +    struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
> >> +    int ret, idx;
> >> +    struct drm_device *drm = hdmi->connector.dev;
> >> +
> >> +    if (drm && !drm_dev_enter(drm, &idx))
> >> +        return;
> >> +
> >> +    ret = pm_runtime_get_sync(hdmi->dev);
> >> +    if (ret < 0)
> >> +        return;
> >> +    mdelay(10);
> >> +    starfive_hdmi_setup(hdmi, mode);
> >> +
> >> +    if (drm)
> >> +        drm_dev_exit(idx);
> >> +}
> >> +
> >> +static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
> >> +{
> >> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> >> +
> >> +    int idx;
> >> +    struct drm_device *drm = hdmi->connector.dev;
> >> +
> >> +    if (drm && !drm_dev_enter(drm, &idx))
> >> +        return;
> >> +
> >> +    pm_runtime_put(hdmi->dev);
> >> +
> >> +    if (drm)
> >> +        drm_dev_exit(idx);
> >> +}
> >> +
> >> +static int
> >> +starfive_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
> >> +                   struct drm_crtc_state *crtc_state,
> >> +                   struct drm_connector_state *conn_state)
> >> +{
> >> +    bool valid = false;
> >> +    struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> >> +    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
> >> +
> >> +    vs_crtc_state->encoder_type = encoder->encoder_type;
> >> +    vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
> >> +
> >> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
> >> +    int pclk = mode->clock * 1000;
> >> +
> >> +    for (; cfg->pixclock != 0; cfg++) {
> >> +        if (pclk == cfg->pixclock) {
> >> +            if (pclk > 297000000)
> >
> > Magic value.
> >
> >> +                continue;
> >> +
> >> +            valid = true;
> >> +            break;
> >> +        }
> >> +    }
> >> +
> >> +    return (valid) ? 0 : -EINVAL;
> >> +}
> >> +
> >> +static const struct drm_encoder_helper_funcs starfive_hdmi_encoder_helper_funcs = {
> >> +    .enable     = starfive_hdmi_encoder_enable,
> >> +    .disable    = starfive_hdmi_encoder_disable,
> >> +    .mode_set   = starfive_hdmi_encoder_mode_set,
> >> +    .atomic_check = starfive_hdmi_encoder_atomic_check,
> >> +};
> >> +
> >> +static enum drm_connector_status
> >> +starfive_hdmi_connector_detect(struct drm_connector *connector, bool force)
> >> +{
> >> +    struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
> >> +    struct drm_device *drm = hdmi->connector.dev;
> >> +    int ret;
> >> +    int idx;
> >> +
> >> +    if (drm && !drm_dev_enter(drm, &idx))
> >> +        return connector_status_disconnected;
> >> +
> >> +    ret = pm_runtime_get_sync(hdmi->dev);
> >> +    if (ret < 0)
> >> +        return ret;
> >> +
> >> +    ret = (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
> >> +        connector_status_connected : connector_status_disconnected;
> >> +    pm_runtime_put(hdmi->dev);
> >> +
> >> +    if (drm)
> >> +        drm_dev_exit(idx);
> >> +
> >> +    return ret;
> >> +}
> >> +
> >> +static int starfive_hdmi_connector_get_modes(struct drm_connector *connector)
> >> +{
> >> +    struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
> >> +    struct edid *edid;
> >> +    int ret = 0;
> >> +
> >> +    if (!hdmi->ddc)
> >> +        return 0;
> >> +    ret = pm_runtime_get_sync(hdmi->dev);
> >> +    if (ret < 0)
> >> +        return ret;
> >> +
> >> +    edid = drm_get_edid(connector, hdmi->ddc);
> >> +    if (edid) {
> >> +        hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
> >> +        hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
> >> +        drm_connector_update_edid_property(connector, edid);
> >> +        ret = drm_add_edid_modes(connector, edid);
> >> +        kfree(edid);
> >> +    }
> >> +    pm_runtime_put(hdmi->dev);
> >
> > Can you use drm_connector_helper_get_modes_from_ddc() here? And then drm_display_info.is_hdmi and .has_audio.
> >
> >> +
> >> +    return ret;
> >> +}
> >> +
> >> +static enum drm_mode_status
> >> +starfive_hdmi_connector_mode_valid(struct drm_connector *connector,
> >> +                   struct drm_display_mode *mode)
> >> +{
> >> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
> >> +    int pclk = mode->clock * 1000;
> >> +    bool valid = false;
> >> +
> >> +    for (; cfg->pixclock != 0; cfg++) {
> >> +        if (pclk == cfg->pixclock) {
> >> +            if (pclk > 297000000)
> >
> > magic value
> >
> >> +                continue;
> >> +
> >> +            valid = true;
> >> +            break;
> >> +        }
> >> +    }
> >> +
> >> +    return (valid) ? MODE_OK : MODE_BAD;
> >> +}
> >> +
> >> +static int
> >> +starfive_hdmi_probe_single_connector_modes(struct drm_connector *connector,
> >> +                       u32 maxX, u32 maxY)
> >> +{
> >> +    return drm_helper_probe_single_connector_modes(connector, 3840, 2160);
> >> +}
> >> +
> >> +static const struct drm_connector_funcs starfive_hdmi_connector_funcs = {
> >> +    .fill_modes = starfive_hdmi_probe_single_connector_modes,
> >> +    .detect = starfive_hdmi_connector_detect,
> >> +    .reset = drm_atomic_helper_connector_reset,
> >> +    .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> >> +    .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> >> +};
> >> +
> >> +static struct drm_connector_helper_funcs starfive_hdmi_connector_helper_funcs = {
> >> +    .get_modes = starfive_hdmi_connector_get_modes,
> >> +    .mode_valid = starfive_hdmi_connector_mode_valid,
> >> +};
> >> +
> >> +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
> >> +{
> >> +    struct drm_encoder *encoder = &hdmi->encoder;
> >> +    struct device *dev = hdmi->dev;
> >> +
> >> +    encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
> >> +
> >> +    /*
> >> +     * If we failed to find the CRTC(s) which this encoder is
> >> +     * supposed to be connected to, it's because the CRTC has
> >> +     * not been registered yet.  Defer probing, and hope that
> >> +     * the required CRTC is added later.
> >> +     */
> >> +    if (encoder->possible_crtcs == 0)
> >> +        return -EPROBE_DEFER;
> >> +
> >> +    drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
> >> +
> >> +    hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> >> +
> >> +    drm_connector_helper_add(&hdmi->connector,
> >> +                 &starfive_hdmi_connector_helper_funcs);
> >> +    drmm_connector_init(drm, &hdmi->connector,
> >> +                &starfive_hdmi_connector_funcs,
> >> +                DRM_MODE_CONNECTOR_HDMIA,
> >
> > On an embedded device one can not be so sure. There can be MHL or HDMI Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.
> >
> >> +                hdmi->ddc);
> >> +
> >> +    drm_connector_attach_encoder(&hdmi->connector, encoder);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static irqreturn_t starfive_hdmi_i2c_irq(struct starfive_hdmi *hdmi)
> >> +{
> >> +    struct starfive_hdmi_i2c *i2c = hdmi->i2c;
> >> +    u8 stat;
> >> +
> >> +    stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
> >> +    if (!(stat & m_INT_EDID_READY))
> >> +        return IRQ_NONE;
> >> +
> >> +    /* Clear HDMI EDID interrupt flag */
> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
> >> +
> >> +    complete(&i2c->cmp);
> >> +
> >> +    return IRQ_HANDLED;
> >> +}
> >> +
> >> +static irqreturn_t starfive_hdmi_hardirq(int irq, void *dev_id)
> >> +{
> >> +    struct starfive_hdmi *hdmi = dev_id;
> >> +    irqreturn_t ret = IRQ_NONE;
> >> +    u8 interrupt;
> >> +
> >> +    if (hdmi->i2c)
> >> +        ret = starfive_hdmi_i2c_irq(hdmi);
> >> +
> >> +    interrupt = hdmi_readb(hdmi, HDMI_STATUS);
> >> +    if (interrupt & m_INT_HOTPLUG) {
> >> +        hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
> >> +        ret = IRQ_WAKE_THREAD;
> >> +    }
> >> +
> >> +    return ret;
> >> +}
> >> +
> >> +static irqreturn_t starfive_hdmi_irq(int irq, void *dev_id)
> >> +{
> >> +    struct starfive_hdmi *hdmi = dev_id;
> >> +
> >> +    drm_connector_helper_hpd_irq_event(&hdmi->connector);
> >> +
> >> +    return IRQ_HANDLED;
> >> +}
> >> +
> >> +static int starfive_hdmi_i2c_read(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
> >> +{
> >> +    int length = msgs->len;
> >> +    u8 *buf = msgs->buf;
> >> +    int ret;
> >> +
> >> +    ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10);
> >
> > read_poll_timeout
> Hi Dmitry:
> I think using wait_for_completion_timeout here is a bit clearer than read_poll_timeout logic
> I2c_read has hardware interrupt dependency. so need wait the interrupt completion.
> also it comes with timeout detection.

Hi,

Yes, maybe I was thinking about a different piece of code here.
wait_for_completion looks good here, indeed.

But this actually brings up another question. You reinit_comletion in
starfive_hdmi_i2c_write() and then wait_for_completion in
starfive_hdmi_i2c_read(). How does that work?



>
> >
> >> +    if (!ret)
> >> +        return -EAGAIN;
> >> +
> >> +    while (length--)
> >> +        *buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int starfive_hdmi_i2c_write(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
> >> +{
> >> +    /*
> >> +     * The DDC module only support read EDID message, so
> >> +     * we assume that each word write to this i2c adapter
> >> +     * should be the offset of EDID word address.
> >> +     */
> >> +    if (msgs->len != 1 ||
> >> +        (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR))
> >> +        return -EINVAL;
> >> +
> >> +    reinit_completion(&hdmi->i2c->cmp);
> >> +
> >> +    if (msgs->addr == DDC_SEGMENT_ADDR)
> >> +        hdmi->i2c->segment_addr = msgs->buf[0];
> >> +    if (msgs->addr == DDC_ADDR)
> >> +        hdmi->i2c->ddc_addr = msgs->buf[0];
> >> +
> >> +    /* Set edid fifo first addr */
> >> +    hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
> >> +
> >> +    /* Set edid word address 0x00/0x80 */
> >> +    hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
> >> +
> >> +    /* Set edid segment pointer */
> >> +    hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int starfive_hdmi_i2c_xfer(struct i2c_adapter *adap,
> >> +                  struct i2c_msg *msgs, int num)
> >> +{
> >> +    struct starfive_hdmi *hdmi = i2c_get_adapdata(adap);
> >> +    struct starfive_hdmi_i2c *i2c = hdmi->i2c;
> >> +    int i, ret = 0;
> >> +
> >> +    mutex_lock(&i2c->lock);
> >> +
> >> +    /* Clear the EDID interrupt flag and unmute the interrupt */
> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
> >> +
> >> +    for (i = 0; i < num; i++) {
> >> +        DRM_DEV_DEBUG(hdmi->dev,
> >> +                  "xfer: num: %d/%d, len: %d, flags: %#x\n",
> >> +                  i + 1, num, msgs[i].len, msgs[i].flags);
> >> +
> >> +        if (msgs[i].flags & I2C_M_RD)
> >> +            ret = starfive_hdmi_i2c_read(hdmi, &msgs[i]);
> >> +        else
> >> +            ret = starfive_hdmi_i2c_write(hdmi, &msgs[i]);
> >> +
> >> +        if (ret < 0)
> >> +            break;
> >> +    }
> >> +
> >> +    if (!ret)
> >> +        ret = num;
> >> +
> >> +    /* Mute HDMI EDID interrupt */
> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
> >> +
> >> +    mutex_unlock(&i2c->lock);
> >> +
> >> +    return ret;
> >> +}
> >> +
> >> +static u32 starfive_hdmi_i2c_func(struct i2c_adapter *adapter)
> >> +{
> >> +    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> >> +}
> >> +
> >> +static const struct i2c_algorithm starfive_hdmi_algorithm = {
> >> +    .master_xfer    = starfive_hdmi_i2c_xfer,
> >> +    .functionality    = starfive_hdmi_i2c_func,
> >> +};
> >> +
> >> +static struct i2c_adapter *starfive_hdmi_i2c_adapter(struct starfive_hdmi *hdmi)
> >> +{
> >> +    struct i2c_adapter *adap;
> >> +    struct starfive_hdmi_i2c *i2c;
> >> +    int ret;
> >> +
> >> +    i2c = drmm_kzalloc(hdmi->drm_dev, sizeof(*i2c), GFP_KERNEL);
> >> +    if (!i2c)
> >> +        return ERR_PTR(-ENOMEM);
> >> +
> >> +    mutex_init(&i2c->lock);
> >> +    init_completion(&i2c->cmp);
> >> +
> >> +    adap = &i2c->adap;
> >> +    adap->class = I2C_CLASS_DDC;
> >> +    adap->owner = THIS_MODULE;
> >> +    adap->dev.parent = hdmi->dev;
> >> +    adap->algo = &starfive_hdmi_algorithm;
> >> +    strscpy(adap->name, "Starfive HDMI", sizeof(adap->name));
> >> +    i2c_set_adapdata(adap, hdmi);
> >> +
> >> +    ret = devm_i2c_add_adapter(hdmi->dev, adap);
> >> +    if (ret) {
> >> +        drm_warn(hdmi->drm_dev, "cannot add %s I2C adapter\n", adap->name);
> >> +        return ERR_PTR(ret);
> >> +    }
> >> +
> >> +    hdmi->i2c = i2c;
> >> +
> >> +    drm_info(hdmi->drm_dev, "registered %s I2C bus driver success\n", adap->name);
> >> +
> >> +    return adap;
> >> +}
> >> +
> >> +static int starfive_hdmi_get_clk_rst(struct device *dev, struct starfive_hdmi *hdmi)
> >> +{
> >> +    hdmi->sys_clk = devm_clk_get(dev, "sysclk");
> >> +    if (IS_ERR(hdmi->sys_clk)) {
> >> +        dev_err(dev, "Unable to get HDMI sysclk clk\n");
> >> +        return PTR_ERR(hdmi->sys_clk);
> >> +    }
> >> +    hdmi->mclk = devm_clk_get(dev, "mclk");
> >> +    if (IS_ERR(hdmi->mclk)) {
> >> +        dev_err(dev, "Unable to get HDMI mclk clk\n");
> >> +        return PTR_ERR(hdmi->mclk);
> >> +    }
> >> +    hdmi->bclk = devm_clk_get(dev, "bclk");
> >> +    if (IS_ERR(hdmi->bclk)) {
> >> +        dev_err(dev, "Unable to get HDMI bclk clk\n");
> >> +        return PTR_ERR(hdmi->bclk);
> >> +    }
> >> +    hdmi->tx_rst = devm_reset_control_get_by_index(dev, 0);
> >> +    if (IS_ERR(hdmi->tx_rst)) {
> >> +        dev_err(dev, "failed to get tx_rst reset\n");
> >> +        return PTR_ERR(hdmi->tx_rst);
> >> +    }
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int starfive_hdmi_bind(struct device *dev, struct device *master,
> >> +                  void *data)
> >> +{
> >> +    struct platform_device *pdev = to_platform_device(dev);
> >> +    struct drm_device *drm = dev_get_drvdata(master);
> >> +    struct starfive_hdmi *hdmi;
> >> +    struct resource *iores;
> >> +    int irq;
> >> +    int ret;
> >> +
> >> +    hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
> >> +    if (!hdmi)
> >> +        return -ENOMEM;
> >> +
> >> +    hdmi = drmm_simple_encoder_alloc(drm, struct starfive_hdmi,
> >> +                     encoder, DRM_MODE_ENCODER_TMDS);
> >> +
> >> +    hdmi->dev = dev;
> >> +    hdmi->drm_dev = drm;
> >> +    dev_set_drvdata(dev, hdmi);
> >> +
> >> +    iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >> +    hdmi->regs = devm_ioremap_resource(dev, iores);
> >
> > This should go to probe
> >
> >> +    if (IS_ERR(hdmi->regs))
> >> +        return PTR_ERR(hdmi->regs);
> >> +
> >> +    ret = starfive_hdmi_get_clk_rst(dev, hdmi);
> >> +    if (ret < 0)
> >> +        return ret;
> >
> > And this
> >
> >> +
> >> +    ret = devm_pm_runtime_enable(dev);
> >> +    if (ret)
> >> +        return ret;
> >> +
> >> +    ret = pm_runtime_resume_and_get(dev);
> >> +    if (ret)
> >> +        return ret;
> >> +
> >> +    irq = platform_get_irq(pdev, 0);
> >
> > And this
> >
> >> +    if (irq < 0) {
> >> +        ret = irq;
> >> +        goto err_put_runtime_pm;
> >> +    }
> >> +
> >> +    hdmi->ddc = starfive_hdmi_i2c_adapter(hdmi);
> >> +    if (IS_ERR(hdmi->ddc)) {
> >> +        ret = PTR_ERR(hdmi->ddc);
> >> +        hdmi->ddc = NULL;
> >> +        goto err_put_runtime_pm;
> >> +    }
> >> +
> >> +    starfive_hdmi_i2c_init(hdmi);
> >> +
> >> +    ret = starfive_hdmi_register(drm, hdmi);
> >> +    if (ret)
> >> +        goto err_put_adapter;
> >> +
> >> +    /* Unmute hotplug interrupt */
> >> +    hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
> >> +
> >> +    ret = devm_request_threaded_irq(dev, irq, starfive_hdmi_hardirq,
> >> +                    starfive_hdmi_irq, IRQF_SHARED,
> >> +                    dev_name(dev), hdmi);
> >> +    if (ret < 0)
> >> +        goto err_put_adapter;
> >> +
> >> +    pm_runtime_put_sync(dev);
> >> +
> >> +    return 0;
> >> +
> >> +err_put_adapter:
> >> +    i2c_put_adapter(hdmi->ddc);
> >> +err_put_runtime_pm:
> >> +    pm_runtime_put_sync(dev);
> >> +
> >> +    return ret;
> >> +}
> >> +
> >> +static const struct component_ops starfive_hdmi_ops = {
> >> +    .bind    = starfive_hdmi_bind,
> >> +};
> >> +
> >> +static int starfive_hdmi_probe(struct platform_device *pdev)
> >> +{
> >> +    return component_add(&pdev->dev, &starfive_hdmi_ops);
> >> +}
> >> +
> >> +static int starfive_hdmi_remove(struct platform_device *pdev)
> >> +{
> >> +    component_del(&pdev->dev, &starfive_hdmi_ops);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static const struct dev_pm_ops hdmi_pm_ops = {
> >> +    SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
> >> +    SET_LATE_SYSTEM_SLEEP_PM_OPS(hdmi_system_pm_suspend, hdmi_system_pm_resume)
> >> +};
> >> +
> >> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
> >> +    { .compatible = "starfive,jh7110-inno-hdmi",},
> >> +    {},
> >> +};
> >> +MODULE_DEVICE_TABLE(of, starfive_hdmi_dt_ids);
> >> +
> >> +struct platform_driver starfive_hdmi_driver = {
> >> +    .probe  = starfive_hdmi_probe,
> >> +    .remove = starfive_hdmi_remove,
> >> +    .driver = {
> >> +        .name = "starfive-hdmi",
> >> +        .of_match_table = starfive_hdmi_dt_ids,
> >> +        .pm = &hdmi_pm_ops,
> >> +    },
> >> +};
> >> +
> >> +MODULE_AUTHOR("StarFive Corporation");
> >> +MODULE_DESCRIPTION("Starfive HDMI Driver");
> >> diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.h b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
> >> new file mode 100644
> >> index 000000000..1b9a11bca
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
> >> @@ -0,0 +1,295 @@
> >> +/* SPDX-License-Identifier: GPL-2.0-only */
> >> +/*
> >> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
> >> + */
> >> +
> >> +#ifndef __STARFIVE_HDMI_H__
> >> +#define __STARFIVE_HDMI_H__
> >> +
> >> +#include <drm/bridge/dw_hdmi.h>
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_edid.h>
> >> +#include <drm/drm_of.h>
> >> +#include <drm/drm_probe_helper.h>
> >> +#include <drm/drm_simple_kms_helper.h>
> >> +#include <linux/bitfield.h>
> >> +#include <linux/bits.h>
> >> +
> >> +#define DDC_SEGMENT_ADDR        0x30
> >> +
> >> +#define HDMI_SCL_RATE            (100 * 1000)
> >> +#define DDC_BUS_FREQ_L            0x4b
> >> +#define DDC_BUS_FREQ_H            0x4c
> >> +
> >> +#define HDMI_SYS_CTRL            0x00
> >> +#define m_RST_ANALOG            BIT(6)
> >> +#define v_RST_ANALOG            0
> >> +#define v_NOT_RST_ANALOG        BIT(6)
> >> +#define m_RST_DIGITAL            BIT(5)
> >> +#define v_RST_DIGITAL            0
> >> +#define v_NOT_RST_DIGITAL        BIT(5)
> >> +#define m_REG_CLK_INV            BIT(4)
> >> +#define v_REG_CLK_NOT_INV        0
> >> +#define v_REG_CLK_INV            BIT(4)
> >> +#define m_VCLK_INV            BIT(3)
> >> +#define v_VCLK_NOT_INV            0
> >> +#define v_VCLK_INV            BIT(3)
> >> +#define m_REG_CLK_SOURCE        BIT(2)
> >> +#define v_REG_CLK_SOURCE_TMDS        0
> >> +#define v_REG_CLK_SOURCE_SYS        BIT(2)
> >> +#define m_POWER                BIT(1)
> >> +#define v_PWR_ON            0
> >> +#define v_PWR_OFF            BIT(1)
> >> +#define m_INT_POL            BIT(0)
> >> +#define v_INT_POL_HIGH            1
> >> +#define v_INT_POL_LOW            0
> >> +
> >> +#define HDMI_AV_MUTE            0x05
> >> +#define m_AVMUTE_CLEAR            BIT(7)
> >> +#define m_AVMUTE_ENABLE            BIT(6)
> >> +#define m_AUDIO_MUTE            BIT(1)
> >> +#define m_VIDEO_BLACK            BIT(0)
> >> +#define v_AVMUTE_CLEAR(n)        ((n) << 7)
> >> +#define v_AVMUTE_ENABLE(n)        ((n) << 6)
> >> +#define v_AUDIO_MUTE(n)            ((n) << 1)
> >> +#define v_VIDEO_MUTE(n)            ((n) << 0)
> >> +
> >> +#define HDMI_VIDEO_TIMING_CTL        0x08
> >> +#define v_VSYNC_POLARITY(n)        ((n) << 3)
> >> +#define v_HSYNC_POLARITY(n)        ((n) << 2)
> >> +#define v_INETLACE(n)            ((n) << 1)
> >> +#define v_EXTERANL_VIDEO(n)        ((n) << 0)
> >> +
> >> +#define HDMI_VIDEO_EXT_HTOTAL_L        0x09
> >> +#define HDMI_VIDEO_EXT_HTOTAL_H        0x0a
> >> +#define HDMI_VIDEO_EXT_HBLANK_L        0x0b
> >> +#define HDMI_VIDEO_EXT_HBLANK_H        0x0c
> >> +#define HDMI_VIDEO_EXT_HDELAY_L        0x0d
> >> +#define HDMI_VIDEO_EXT_HDELAY_H        0x0e
> >> +#define HDMI_VIDEO_EXT_HDURATION_L    0x0f
> >> +#define HDMI_VIDEO_EXT_HDURATION_H    0x10
> >> +#define HDMI_VIDEO_EXT_VTOTAL_L        0x11
> >> +#define HDMI_VIDEO_EXT_VTOTAL_H        0x12
> >> +#define HDMI_VIDEO_EXT_VBLANK        0x13
> >> +#define HDMI_VIDEO_EXT_VDELAY        0x14
> >> +#define HDMI_VIDEO_EXT_VDURATION    0x15
> >> +
> >> +#define HDMI_EDID_SEGMENT_POINTER    0x4d
> >> +#define HDMI_EDID_WORD_ADDR        0x4e
> >> +#define HDMI_EDID_FIFO_OFFSET        0x4f
> >> +#define HDMI_EDID_FIFO_ADDR        0x50
> >> +
> >> +#define HDMI_INTERRUPT_MASK1        0xc0
> >> +#define HDMI_INTERRUPT_STATUS1        0xc1
> >> +#define    m_INT_ACTIVE_VSYNC        BIT(5)
> >> +#define m_INT_EDID_READY        BIT(2)
> >> +
> >> +#define HDMI_STATUS            0xc8
> >> +#define m_HOTPLUG            BIT(7)
> >> +#define m_MASK_INT_HOTPLUG        BIT(5)
> >> +#define m_INT_HOTPLUG            BIT(1)
> >> +#define v_MASK_INT_HOTPLUG(n)        (((n) & 0x1) << 5)
> >
> > It is untypical to have field defines which start with lowercase letter.
> >
> >> +
> >> +#define HDMI_SYNC                    0xce
> >> +
> >> +#define UPDATE(x, h, l)                    FIELD_PREP(GENMASK(h, l), x)
> >> +
> >> +/* REG: 0x1a0 */
> >> +#define STARFIVE_PRE_PLL_CONTROL            0x1a0
> >> +#define STARFIVE_PCLK_VCO_DIV_5_MASK            BIT(1)
> >> +#define STARFIVE_PCLK_VCO_DIV_5(x)            UPDATE(x, 1, 1)
> >> +#define STARFIVE_PRE_PLL_POWER_DOWN            BIT(0)
> >> +
> >> +/* REG: 0x1a1 */
> >> +#define STARFIVE_PRE_PLL_DIV_1                0x1a1
> >> +#define STARFIVE_PRE_PLL_PRE_DIV_MASK            GENMASK(5, 0)
> >> +#define STARFIVE_PRE_PLL_PRE_DIV(x)            UPDATE(x, 5, 0)
> >> +
> >> +/* REG: 0x1a2 */
> >> +#define STARFIVE_PRE_PLL_DIV_2                0x1a2
> >> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DOWN        BIT(7)
> >> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE        BIT(6)
> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_DISABLE        UPDATE(3, 5, 4)
> >> +#define STARFIVE_PRE_PLL_FB_DIV_11_8_MASK        GENMASK(3, 0)
> >> +#define STARFIVE_PRE_PLL_FB_DIV_11_8(x)            UPDATE((x) >> 8, 3, 0)
> >> +
> >> +/* REG: 0x1a3 */
> >> +#define STARFIVE_PRE_PLL_DIV_3                0x1a3
> >> +#define STARFIVE_PRE_PLL_FB_DIV_7_0(x)            UPDATE(x, 7, 0)
> >> +
> >> +/* REG: 0x1a4*/
> >> +#define STARFIVE_PRE_PLL_DIV_4                0x1a4
> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C_MASK        GENMASK(1, 0)
> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C(x)        UPDATE(x, 1, 0)
> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B_MASK        GENMASK(3, 2)
> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B(x)        UPDATE(x, 3, 2)
> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A_MASK        GENMASK(5, 4)
> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A(x)        UPDATE(x, 5, 4)
> >> +
> >> +/* REG: 0x1a5 */
> >> +#define STARFIVE_PRE_PLL_DIV_5                0x1a5
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_SHIFT        5
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_MASK        GENMASK(6, 5)
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_B(x)            UPDATE(x, 6, 5)
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_A_MASK        GENMASK(4, 0)
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_A(x)            UPDATE(x, 4, 0)
> >> +
> >> +/* REG: 0x1a6 */
> >> +#define STARFIVE_PRE_PLL_DIV_6                0x1a6
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_SHIFT        5
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_MASK        GENMASK(6, 5)
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_C(x)            UPDATE(x, 6, 5)
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_D_MASK        GENMASK(4, 0)
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_D(x)            UPDATE(x, 4, 0)
> >> +
> >> +/* REG: 0x1a9 */
> >> +#define STARFIVE_PRE_PLL_LOCK_STATUS            0x1a9
> >> +
> >> +/* REG: 0x1aa */
> >> +#define STARFIVE_POST_PLL_DIV_1                0x1aa
> >> +#define STARFIVE_POST_PLL_POST_DIV_ENABLE        GENMASK(3, 2)
> >> +#define STARFIVE_POST_PLL_REFCLK_SEL_TMDS        BIT(1)
> >> +#define STARFIVE_POST_PLL_POWER_DOWN            BIT(0)
> >> +#define STARFIVE_POST_PLL_FB_DIV_8(x)            UPDATE(((x) >> 8) << 4, 4, 4)
> >> +
> >> +/* REG:0x1ab */
> >> +#define STARFIVE_POST_PLL_DIV_2                0x1ab
> >> +#define STARFIVE_POST_PLL_Pre_DIV_MASK            GENMASK(5, 0)
> >> +#define STARFIVE_POST_PLL_PRE_DIV(x)            UPDATE(x, 5, 0)
> >> +
> >> +/* REG: 0x1ac */
> >> +#define STARFIVE_POST_PLL_DIV_3                0x1ac
> >> +#define STARFIVE_POST_PLL_FB_DIV_7_0(x)            UPDATE(x, 7, 0)
> >> +
> >> +/* REG: 0x1ad */
> >> +#define STARFIVE_POST_PLL_DIV_4                0x1ad
> >> +#define STARFIVE_POST_PLL_POST_DIV_MASK            GENMASK(2, 0)
> >> +#define STARFIVE_POST_PLL_POST_DIV_2            0x0
> >> +#define STARFIVE_POST_PLL_POST_DIV_4            0x1
> >> +#define STARFIVE_POST_PLL_POST_DIV_8            0x3
> >> +
> >> +/* REG: 0x1af */
> >> +#define STARFIVE_POST_PLL_LOCK_STATUS            0x1af
> >> +
> >> +/* REG: 0x1b0 */
> >> +#define STARFIVE_BIAS_CONTROL                0x1b0
> >> +#define STARFIVE_BIAS_ENABLE                BIT(2)
> >> +
> >> +/* REG: 0x1b2 */
> >> +#define STARFIVE_TMDS_CONTROL                0x1b2
> >> +#define STARFIVE_TMDS_CLK_DRIVER_EN            BIT(3)
> >> +#define STARFIVE_TMDS_D2_DRIVER_EN            BIT(2)
> >> +#define STARFIVE_TMDS_D1_DRIVER_EN            BIT(1)
> >> +#define STARFIVE_TMDS_D0_DRIVER_EN            BIT(0)
> >> +#define STARFIVE_TMDS_DRIVER_ENABLE            (STARFIVE_TMDS_CLK_DRIVER_EN | \
> >> +                             STARFIVE_TMDS_D2_DRIVER_EN | \
> >> +                             STARFIVE_TMDS_D1_DRIVER_EN | \
> >> +                             STARFIVE_TMDS_D0_DRIVER_EN)
> >> +
> >> +/* REG: 0x1b4 */
> >> +#define STARFIVE_LDO_CONTROL                0x1b4
> >> +#define STARFIVE_LDO_D2_EN                BIT(2)
> >> +#define STARFIVE_LDO_D1_EN                BIT(1)
> >> +#define STARFIVE_LDO_D0_EN                BIT(0)
> >> +#define STARFIVE_LDO_ENABLE                (STARFIVE_LDO_D2_EN | \
> >> +                             STARFIVE_LDO_D1_EN | \
> >> +                             STARFIVE_LDO_D0_EN)
> >> +
> >> +/* REG: 0x1be */
> >> +#define STARFIVE_SERIALIER_CONTROL            0x1be
> >> +#define STARFIVE_SERIALIER_D2_EN            BIT(6)
> >> +#define STARFIVE_SERIALIER_D1_EN            BIT(5)
> >> +#define STARFIVE_SERIALIER_D0_EN            BIT(4)
> >> +#define STARFIVE_SERIALIER_EN                BIT(0)
> >> +
> >> +#define STARFIVE_SERIALIER_ENABLE            (STARFIVE_SERIALIER_D2_EN | \
> >> +                             STARFIVE_SERIALIER_D1_EN | \
> >> +                             STARFIVE_SERIALIER_D0_EN | \
> >> +                             STARFIVE_SERIALIER_EN)
> >> +
> >> +/* REG: 0x1cc */
> >> +#define STARFIVE_RX_CONTROL                0x1cc
> >> +#define STARFIVE_RX_EN                    BIT(3)
> >> +#define STARFIVE_RX_CHANNEL_2_EN            BIT(2)
> >> +#define STARFIVE_RX_CHANNEL_1_EN            BIT(1)
> >> +#define STARFIVE_RX_CHANNEL_0_EN            BIT(0)
> >> +#define STARFIVE_RX_ENABLE                (STARFIVE_RX_EN | \
> >> +                             STARFIVE_RX_CHANNEL_2_EN | \
> >> +                             STARFIVE_RX_CHANNEL_1_EN | \
> >> +                             STARFIVE_RX_CHANNEL_0_EN)
> >> +
> >> +/* REG: 0x1d1 */
> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_H            0x1d1
> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_23_16(x)        UPDATE((x) >> 16, 7, 0)
> >> +/* REG: 0x1d2 */
> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_M            0x1d2
> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_15_8(x)        UPDATE((x) >> 8, 7, 0)
> >> +/* REG: 0x1d3 */
> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_L            0x1d3
> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_7_0(x)        UPDATE(x, 7, 0)
> >> +
> >> +struct pre_pll_config {
> >> +    unsigned long pixclock;
> >> +    unsigned long tmdsclock;
> >> +    u8 prediv;
> >> +    u16 fbdiv;
> >> +    u8 tmds_div_a;
> >> +    u8 tmds_div_b;
> >> +    u8 tmds_div_c;
> >> +    u8 pclk_div_a;
> >> +    u8 pclk_div_b;
> >> +    u8 pclk_div_c;
> >> +    u8 pclk_div_d;
> >> +    u8 vco_div_5_en;
> >> +    u32 fracdiv;
> >> +};
> >> +
> >> +struct post_pll_config {
> >> +    unsigned long tmdsclock;
> >> +    u8 prediv;
> >> +    u16 fbdiv;
> >> +    u8 postdiv;
> >> +    u8 post_div_en;
> >> +    u8 version;
> >> +};
> >> +
> >> +struct phy_config {
> >> +    unsigned long    tmdsclock;
> >> +    u8        regs[14];
> >> +};
> >> +
> >> +struct hdmi_data_info {
> >> +    int vic;
> >> +    bool sink_is_hdmi;
> >> +    bool sink_has_audio;
> >> +    unsigned int enc_in_format;
> >> +    unsigned int enc_out_format;
> >> +    unsigned int colorimetry;
> >> +};
> >> +
> >> +struct starfive_hdmi {
> >> +    struct device *dev;
> >> +    struct drm_device *drm_dev;
> >> +    struct drm_encoder    encoder;
> >> +    struct drm_connector    connector;
> >> +
> >> +    struct starfive_hdmi_i2c *i2c;
> >> +
> >> +    int irq;
> >> +    struct clk *sys_clk;
> >> +    struct clk *mclk;
> >> +    struct clk *bclk;
> >> +    struct reset_control *tx_rst;
> >> +    void __iomem *regs;
> >> +
> >> +    struct i2c_adapter *ddc;
> >> +
> >> +    unsigned long tmds_rate;
> >> +
> >> +    struct hdmi_data_info    hdmi_data;
> >> +    struct drm_display_mode previous_mode;
> >> +    const struct pre_pll_config    *pre_cfg;
> >> +    const struct post_pll_config    *post_cfg;
> >> +};
> >> +
> >> +#endif /* __STARFIVE_HDMI_H__ */
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
> >> index 3cd533cfa..9669354bd 100644
> >> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
> >> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> >> @@ -126,6 +126,11 @@ static const struct component_master_ops vs_drm_ops = {
> >>   static struct platform_driver *drm_sub_drivers[] = {
> >>       &dc_platform_driver,
> >>   +    /* connector + encoder*/
> >> +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
> >> +    &starfive_hdmi_driver,
> >> +#endif
> >> +
> >>   };
> >>     static struct component_match *vs_drm_match_add(struct device *dev)
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
> >> index 369ad22d6..e90723419 100644
> >> --- a/drivers/gpu/drm/verisilicon/vs_drv.h
> >> +++ b/drivers/gpu/drm/verisilicon/vs_drv.h
> >> @@ -24,4 +24,8 @@ to_vs_drm_private(const struct drm_device *dev)
> >>       return container_of(dev, struct vs_drm_device, base);
> >>   }
> >>   +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
> >> +extern struct platform_driver starfive_hdmi_driver;
> >> +#endif
> >> +
> >>   #endif /* __VS_DRV_H__ */
> >



-- 
With best wishes
Dmitry

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-11-13 13:31         ` Dmitry Baryshkov
  0 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-11-13 13:31 UTC (permalink / raw)
  To: Keith Zhao
  Cc: Emil Renner Berthing, dri-devel, Krzysztof Kozlowski,
	linux-riscv, Sumit Semwal, Shengyang Chen, linux-media,
	devicetree, Conor Dooley, Albert Ou, Maxime Ripard, Jagan Teki,
	linaro-mm-sig, Rob Herring, Chris Morgan, Paul Walmsley,
	Bjorn Andersson, linux-kernel, christian.koenig, Jack Zhu,
	Palmer Dabbelt, Thomas Zimmermann, Shawn Guo, Changhuang Liang

On Mon, 13 Nov 2023 at 14:11, Keith Zhao <keith.zhao@starfivetech.com> wrote:
>
>
>
> On 2023/10/26 6:23, Dmitry Baryshkov wrote:
> > On 25/10/2023 13:39, Keith Zhao wrote:
> >> add hdmi driver as encoder and connect
> >>
> >> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> >> ---
> >>   drivers/gpu/drm/verisilicon/Kconfig         |   8 +-
> >>   drivers/gpu/drm/verisilicon/Makefile        |   1 +
> >>   drivers/gpu/drm/verisilicon/starfive_hdmi.c | 949 ++++++++++++++++++++
> >>   drivers/gpu/drm/verisilicon/starfive_hdmi.h | 295 ++++++
> >>   drivers/gpu/drm/verisilicon/vs_drv.c        |   5 +
> >>   drivers/gpu/drm/verisilicon/vs_drv.h        |   4 +
> >>   6 files changed, 1261 insertions(+), 1 deletion(-)
> >>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.c
> >>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.h
> >>
> >> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
> >> index 3a361f8c8..122c786e3 100644
> >> --- a/drivers/gpu/drm/verisilicon/Kconfig
> >> +++ b/drivers/gpu/drm/verisilicon/Kconfig
> >> @@ -12,4 +12,10 @@ config DRM_VERISILICON
> >>         setting and buffer management. It does not
> >>         provide 2D or 3D acceleration.
> >>   -
> >> +config DRM_VERISILICON_STARFIVE_HDMI
> >> +    bool "Starfive HDMI extensions"
> >> +    depends on DRM_VERISILICON
> >> +    help
> >> +       This selects support for StarFive soc specific extensions
> >> +       for the Innosilicon HDMI driver. If you want to enable
> >> +       HDMI on JH7110 based soc, you should select this option.
> >> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> >> index 1d48016ca..08350f25b 100644
> >> --- a/drivers/gpu/drm/verisilicon/Makefile
> >> +++ b/drivers/gpu/drm/verisilicon/Makefile
> >> @@ -7,5 +7,6 @@ vs_drm-objs := vs_dc_hw.o \
> >>           vs_modeset.o \
> >>           vs_plane.o
> >>   +vs_drm-$(CONFIG_DRM_VERISILICON_STARFIVE_HDMI) += starfive_hdmi.o
> >>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
> >>   diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.c b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
> >> new file mode 100644
> >> index 000000000..d296c4b71
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
> >> @@ -0,0 +1,949 @@
> >> +// SPDX-License-Identifier: GPL-2.0-only
> >> +/*
> >> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
> >> + */
> >> +
> >> +#include <linux/clk.h>
> >> +#include <linux/component.h>
> >> +#include <linux/delay.h>
> >> +#include <linux/err.h>
> >> +#include <linux/hdmi.h>
> >> +#include <linux/i2c.h>
> >> +#include <linux/irq.h>
> >> +#include <linux/media-bus-format.h>
> >> +#include <linux/mfd/syscon.h>
> >> +#include <linux/module.h>
> >> +#include <linux/mutex.h>
> >> +#include <linux/of_device.h>
> >> +#include <linux/pm_runtime.h>
> >> +#include <linux/reset.h>
> >> +
> >> +#include <drm/bridge/dw_hdmi.h>
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_edid.h>
> >> +#include <drm/drm_managed.h>
> >> +#include <drm/drm_of.h>
> >> +#include <drm/drm_probe_helper.h>
> >> +#include <drm/drm_simple_kms_helper.h>
> >> +
> >> +#include "starfive_hdmi.h"
> >> +#include "vs_drv.h"
> >> +#include "vs_crtc.h"
> >> +
> >> +static struct starfive_hdmi *encoder_to_hdmi(struct drm_encoder *encoder)
> >> +{
> >> +    return container_of(encoder, struct starfive_hdmi, encoder);
> >> +}
> >> +
> >> +static struct starfive_hdmi *connector_to_hdmi(struct drm_connector *connector)
> >> +{
> >> +    return container_of(connector, struct starfive_hdmi, connector);
> >> +}
> >> +
> >> +struct starfive_hdmi_i2c {
> >> +    struct i2c_adapter adap;
> >> +
> >> +    u8 ddc_addr;
> >> +    u8 segment_addr;
> >> +    /* protects the edid data when use i2c cmd to read edid */
> >> +    struct mutex lock;
> >> +    struct completion cmp;
> >> +};
> >> +
> >> +static const struct pre_pll_config pre_pll_cfg_table[] = {
> >> +    { 25175000,  25175000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0xf55555},
> >> +    { 25200000,  25200000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0},
> >> +    { 27000000,  27000000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0},
> >
> > Such data structures are usually pretyt limited and hard to handle. Could you please replace it with the runtime calculations of
> >
> >> +    { 27027000,  27027000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0x170a3d},
> >> +    { 28320000,  28320000, 1,  28, 2, 1, 1,  3, 0, 3, 4, 0, 0x51eb85},
> >> +    { 30240000,  30240000, 1,  30, 2, 1, 1,  3, 0, 3, 4, 0, 0x3d70a3},
> >> +    { 31500000,  31500000, 1,  31, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> >> +    { 33750000,  33750000, 1,  33, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
> >> +    { 36000000,  36000000, 1,  36, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    { 40000000,  40000000, 1,  80, 2, 2, 2, 12, 2, 2, 2, 0, 0},
> >> +    { 46970000,  46970000, 1,  46, 2, 1, 1,  3, 0, 3, 4, 0, 0xf851eb},
> >> +    { 49500000,  49500000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> >> +    { 49000000,  49000000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    { 50000000,  50000000, 1,  50, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    { 54000000,  54000000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    { 54054000,  54054000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0x0dd2f1},
> >> +    { 57284000,  57284000, 1,  57, 2, 1, 1,  3, 0, 3, 4, 0, 0x48b439},
> >> +    { 58230000,  58230000, 1,  58, 2, 1, 1,  3, 0, 3, 4, 0, 0x3ae147},
> >> +    { 59341000,  59341000, 1,  59, 2, 1, 1,  3, 0, 3, 4, 0, 0x574bc6},
> >> +    { 59400000,  59400000, 1,  99, 3, 1, 1,  1, 3, 3, 4, 0, 0},
> >> +    { 65000000,  65000000, 1, 130, 2, 2, 2,  12, 0, 2, 2, 0, 0},
> >> +    { 68250000,  68250000, 1, 68,  2, 1, 1,  3,  0, 3, 4, 0, 0x3fffff},
> >> +    { 71000000,  71000000, 1,  71, 2, 1, 1,  3, 0, 3,  4, 0, 0},
> >> +    { 74176000,  74176000, 1,  98, 1, 2, 2,  1, 2, 3, 4, 0, 0xe6ae6b},
> >> +    { 74250000,  74250000, 1,  99, 1, 2, 2,  1, 2, 3, 4, 0, 0},
> >> +    { 75000000,  75000000, 1,  75, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    { 78750000,  78750000, 1,  78, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
> >> +    { 79500000,  79500000, 1,  79, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> >> +    { 83500000,  83500000, 2, 167, 2, 1, 1,  1, 0, 0,  6, 0, 0},
> >> +    { 83500000, 104375000, 1, 104, 2, 1, 1,  1, 1, 0,  5, 0, 0x600000},
> >> +    { 84858000,  84858000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xdba5e2},
> >> +    { 85500000,  85500000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
> >> +    { 85750000,  85750000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
> >> +    { 85800000,  85800000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcccccc},
> >> +    { 88750000,  88750000, 1,  88, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
> >> +    { 89910000,  89910000, 1,  89, 2, 1, 1,  3, 0, 3, 4, 0, 0xe8f5c1},
> >> +    { 90000000,  90000000, 1,  90, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {101000000, 101000000, 1, 101, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {102250000, 102250000, 1, 102, 2, 1, 1,  3, 0, 3, 4, 0, 0x3fffff},
> >> +    {106500000, 106500000, 1, 106, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> >> +    {108000000, 108000000, 1,  90, 3, 0, 0,  5, 0, 2,  2, 0, 0},
> >> +    {119000000, 119000000, 1, 119, 2, 1, 1,  3, 0, 3,  4, 0, 0},
> >> +    {131481000, 131481000, 1,  131, 2, 1, 1,  3, 0, 3,  4, 0, 0x7b22d1},
> >> +    {135000000, 135000000, 1,  135, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {136750000, 136750000, 1,  136, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
> >> +    {147180000, 147180000, 1,  147, 2, 1, 1,  3, 0, 3, 4, 0, 0x2e147a},
> >> +    {148352000, 148352000, 1,  98, 1, 1, 1,  1, 2, 2, 2, 0, 0xe6ae6b},
> >> +    {148500000, 148500000, 1,  99, 1, 1, 1,  1, 2, 2, 2, 0, 0},
> >> +    {154000000, 154000000, 1, 154, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {156000000, 156000000, 1, 156, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {157000000, 157000000, 1, 157, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {162000000, 162000000, 1, 162, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {174250000, 174250000, 1, 145, 3, 0, 0,  5, 0, 2, 2, 0, 0x355555},
> >> +    {174500000, 174500000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> >> +    {174570000, 174570000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x91eb84},
> >> +    {175500000, 175500000, 1, 175, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
> >> +    {185590000, 185590000, 1, 185, 2, 1, 1,  3, 0, 3, 4, 0, 0x970a3c},
> >> +    {187000000, 187000000, 1, 187, 2, 1, 1,  3, 0, 3, 4, 0, 0},
> >> +    {241500000, 241500000, 1, 161, 1, 1, 1,  4, 0, 2,  2, 0, 0},
> >> +    {241700000, 241700000, 1, 241, 2, 1, 1,  3, 0, 3,  4, 0, 0xb33332},
> >> +    {262750000, 262750000, 1, 262, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
> >> +    {296500000, 296500000, 1, 296, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
> >> +    {296703000, 296703000, 1,  98, 0, 1, 1,  1, 0, 2,  2, 0, 0xe6ae6b},
> >> +    {297000000, 297000000, 1,  99, 0, 1, 1,  1, 0, 2,  2, 0, 0},
> >> +    {594000000, 594000000, 1,  99, 0, 2, 0,  1, 0, 1,  1, 0, 0},
> >> +    {0, 0, 0,  0, 0, 0, 0,  0, 0, 0,  0, 0, 0},
> >> +};
> >> +
> >> +static const struct post_pll_config post_pll_cfg_table[] = {
> >> +    {25200000,    1, 80, 13, 3, 1},
> >> +    {27000000,    1, 40, 11, 3, 1},
> >> +    {33750000,    1, 40, 11, 3, 1},
> >> +    {49000000,    1, 20, 1, 3, 3},
> >> +    {241700000, 1, 20, 1, 3, 3},
> >> +    {297000000, 4, 20, 0, 0, 3},
> >> +    {594000000, 4, 20, 0, 0, 0},
> >> +    { /* sentinel */ }
> >> +};
> >> +
> >> +inline u8 hdmi_readb(struct starfive_hdmi *hdmi, u16 offset)
> >> +{
> >> +    return readl_relaxed(hdmi->regs + (offset) * 0x04);
> >> +}
> >> +
> >> +inline void hdmi_writeb(struct starfive_hdmi *hdmi, u16 offset, u32 val)
> >> +{
> >> +    writel_relaxed(val, hdmi->regs + (offset) * 0x04);
> >> +}
> >> +
> >> +inline void hdmi_modb(struct starfive_hdmi *hdmi, u16 offset,
> >> +                 u32 msk, u32 val)
> >> +{
> >> +    u8 temp = hdmi_readb(hdmi, offset) & ~msk;
> >> +
> >> +    temp |= val & msk;
> >> +    hdmi_writeb(hdmi, offset, temp);
> >> +}
> >> +
> >> +static int starfive_hdmi_enable_clk_deassert_rst(struct device *dev, struct starfive_hdmi *hdmi)
> >> +{
> >> +    int ret;
> >> +
> >> +    ret = clk_prepare_enable(hdmi->sys_clk);
> >> +    if (ret) {
> >> +        dev_err(dev, "Cannot enable HDMI sys clock: %d\n", ret);
> >> +        return ret;
> >> +    }
> >> +
> >> +    ret = clk_prepare_enable(hdmi->mclk);
> >> +    if (ret) {
> >> +        dev_err(dev, "Cannot enable HDMI mclk clock: %d\n", ret);
> >> +        goto err_mclk;
> >> +    }
> >> +    ret = clk_prepare_enable(hdmi->bclk);
> >
> > This code begs to use clk_bulk instead.
> >
> >> +    if (ret) {
> >> +        dev_err(dev, "Cannot enable HDMI bclk clock: %d\n", ret);
> >> +        goto err_bclk;
> >> +    }
> >> +    ret = reset_control_deassert(hdmi->tx_rst);
> >> +    if (ret < 0) {
> >> +        dev_err(dev, "failed to deassert tx_rst\n");
> >> +        goto err_rst;
> >> +    }
> >> +    return 0;
> >> +
> >> +err_rst:
> >> +    clk_disable_unprepare(hdmi->bclk);
> >> +err_bclk:
> >> +    clk_disable_unprepare(hdmi->mclk);
> >> +err_mclk:
> >> +    clk_disable_unprepare(hdmi->sys_clk);
> >> +    return ret;
> >> +}
> >> +
> >> +static void starfive_hdmi_disable_clk_assert_rst(struct device *dev, struct starfive_hdmi *hdmi)
> >> +{
> >> +    int ret;
> >> +
> >> +    ret = reset_control_assert(hdmi->tx_rst);
> >> +    if (ret < 0)
> >> +        dev_err(dev, "failed to assert tx_rst\n");
> >> +
> >> +    clk_disable_unprepare(hdmi->sys_clk);
> >> +    clk_disable_unprepare(hdmi->mclk);
> >> +    clk_disable_unprepare(hdmi->bclk);
> >> +}
> >> +
> >> +#ifdef CONFIG_PM_SLEEP
> >> +static int hdmi_system_pm_suspend(struct device *dev)
> >> +{
> >> +    return pm_runtime_force_suspend(dev);
> >> +}
> >> +
> >> +static int hdmi_system_pm_resume(struct device *dev)
> >> +{
> >> +    return pm_runtime_force_resume(dev);
> >> +}
> >> +#endif
> >> +
> >> +#ifdef CONFIG_PM
> >> +static int hdmi_runtime_suspend(struct device *dev)
> >> +{
> >> +    struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
> >> +
> >> +    starfive_hdmi_disable_clk_assert_rst(dev, hdmi);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int hdmi_runtime_resume(struct device *dev)
> >> +{
> >> +    struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
> >> +
> >> +    return starfive_hdmi_enable_clk_deassert_rst(dev, hdmi);
> >> +}
> >> +#endif
> >> +
> >> +static void starfive_hdmi_tx_phy_power_down(struct starfive_hdmi *hdmi)
> >> +{
> >> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
> >> +}
> >> +
> >> +static void starfive_hdmi_tx_phy_power_on(struct starfive_hdmi *hdmi)
> >> +{
> >> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
> >
> > It looks like one can inline these two helpers w/o too much troubles.
> >
> >> +}
> >> +
> >> +static void starfive_hdmi_config_pll(struct starfive_hdmi *hdmi)
> >> +{
> >> +    u32 val;
> >> +    u8 reg_1ad_value = hdmi->post_cfg->post_div_en ?
> >> +         hdmi->post_cfg->postdiv : 0x00;
> >> +    u8 reg_1aa_value = hdmi->post_cfg->post_div_en ?
> >> +         0x0e : 0x02;
> >> +
> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN);
> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1,
> >> +            STARFIVE_POST_PLL_POST_DIV_ENABLE |
> >> +            STARFIVE_POST_PLL_REFCLK_SEL_TMDS |
> >> +            STARFIVE_POST_PLL_POWER_DOWN);
> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_1, STARFIVE_PRE_PLL_PRE_DIV(hdmi->pre_cfg->prediv));
> >> +
> >> +    val = STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE | STARFIVE_SPREAD_SPECTRUM_MOD_DOWN;
> >> +    if (!hdmi->pre_cfg->fracdiv)
> >> +        val |= STARFIVE_PRE_PLL_FRAC_DIV_DISABLE;
> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_2,
> >> +            STARFIVE_PRE_PLL_FB_DIV_11_8(hdmi->pre_cfg->fbdiv) | val);
> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_3,
> >> +            STARFIVE_PRE_PLL_FB_DIV_7_0(hdmi->pre_cfg->fbdiv));
> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_4,
> >> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_C(hdmi->pre_cfg->tmds_div_c) |
> >> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_A(hdmi->pre_cfg->tmds_div_a) |
> >> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_B(hdmi->pre_cfg->tmds_div_b));
> >> +
> >> +    if (hdmi->pre_cfg->fracdiv) {
> >> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_L,
> >> +                STARFIVE_PRE_PLL_FRAC_DIV_7_0(hdmi->pre_cfg->fracdiv));
> >> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_M,
> >> +                STARFIVE_PRE_PLL_FRAC_DIV_15_8(hdmi->pre_cfg->fracdiv));
> >> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_H,
> >> +                STARFIVE_PRE_PLL_FRAC_DIV_23_16(hdmi->pre_cfg->fracdiv));
> >> +    }
> >> +
> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_5,
> >> +            STARFIVE_PRE_PLL_PCLK_DIV_A(hdmi->pre_cfg->pclk_div_a) |
> >> +            STARFIVE_PRE_PLL_PCLK_DIV_B(hdmi->pre_cfg->pclk_div_b));
> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_6,
> >> +            STARFIVE_PRE_PLL_PCLK_DIV_C(hdmi->pre_cfg->pclk_div_c) |
> >> +            STARFIVE_PRE_PLL_PCLK_DIV_D(hdmi->pre_cfg->pclk_div_d));
> >> +
> >> +    /*pre-pll power down*/
> >> +    hdmi_modb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN, 0);
> >> +
> >> +    hdmi_modb(hdmi, STARFIVE_POST_PLL_DIV_2, STARFIVE_POST_PLL_Pre_DIV_MASK,
> >> +          STARFIVE_POST_PLL_PRE_DIV(hdmi->post_cfg->prediv));
> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_3, hdmi->post_cfg->fbdiv & 0xff);
> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_4, reg_1ad_value);
> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1, reg_1aa_value);
> >> +}
> >> +
> >> +static void starfive_hdmi_tmds_driver_on(struct starfive_hdmi *hdmi)
> >> +{
> >> +    hdmi_modb(hdmi, STARFIVE_TMDS_CONTROL,
> >> +          STARFIVE_TMDS_DRIVER_ENABLE, STARFIVE_TMDS_DRIVER_ENABLE);
> >> +}
> >> +
> >> +static void starfive_hdmi_sync_tmds(struct starfive_hdmi *hdmi)
> >> +{
> >> +    /*first send 0 to this bit, then send 1 and keep 1 into this bit*/
> >> +    hdmi_writeb(hdmi, HDMI_SYNC, 0x0);
> >> +    hdmi_writeb(hdmi, HDMI_SYNC, 0x1);
> >> +}
> >> +
> >> +static void starfive_hdmi_i2c_init(struct starfive_hdmi *hdmi)
> >> +{
> >> +    int ddc_bus_freq;
> >> +
> >> +    ddc_bus_freq = (clk_get_rate(hdmi->sys_clk) >> 2) / HDMI_SCL_RATE;
> >> +
> >> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
> >> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
> >> +
> >> +    /* Clear the EDID interrupt flag and mute the interrupt */
> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
> >> +}
> >> +
> >> +static const
> >> +struct pre_pll_config *starfive_hdmi_phy_get_pre_pll_cfg(struct starfive_hdmi *hdmi,
> >> +                             unsigned long rate)
> >> +{
> >> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
> >> +
> >> +    rate = (rate / 1000) * 1000;
> >> +    for (; cfg->pixclock != 0; cfg++)
> >> +        if (cfg->tmdsclock == rate && cfg->pixclock == rate)
> >> +            break;
> >> +
> >> +    if (cfg->pixclock == 0)
> >> +        return ERR_PTR(-EINVAL);
> >> +
> >> +    return cfg;
> >> +}
> >> +
> >> +static int starfive_hdmi_phy_clk_set_rate(struct starfive_hdmi *hdmi)
> >> +{
> >> +    hdmi->post_cfg = post_pll_cfg_table;
> >> +
> >> +    hdmi->pre_cfg = starfive_hdmi_phy_get_pre_pll_cfg(hdmi, hdmi->tmds_rate);
> >> +    if (IS_ERR(hdmi->pre_cfg))
> >> +        return PTR_ERR(hdmi->pre_cfg);
> >> +
> >> +    for (; hdmi->post_cfg->tmdsclock != 0; hdmi->post_cfg++)
> >> +        if (hdmi->tmds_rate <= hdmi->post_cfg->tmdsclock)
> >> +            break;
> >> +
> >> +    starfive_hdmi_config_pll(hdmi);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int starfive_hdmi_config_video_timing(struct starfive_hdmi *hdmi,
> >> +                         struct drm_display_mode *mode)
> >> +{
> >> +    int value;
> >> +    /* Set detail external video timing */
> >> +    value = mode->htotal;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
> >
> > As you have hdmi_writeb already, adding hdmi_writew will help here.
> >
> >> +
> >> +    value = mode->htotal - mode->hdisplay;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
> >> +
> >> +    value = mode->htotal - mode->hsync_start;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
> >> +
> >> +    value = mode->hsync_end - mode->hsync_start;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
> >> +
> >> +    value = mode->vtotal;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
> >> +
> >> +    value = mode->vtotal - mode->vdisplay;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
> >> +
> >> +    value = mode->vtotal - mode->vsync_start;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
> >> +
> >> +    value = mode->vsync_end - mode->vsync_start;
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
> >> +
> >> +    /* Set detail external video timing polarity and interlace mode */
> >> +    value = v_EXTERANL_VIDEO(1);
> >> +    value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
> >> +        v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
> >> +    value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
> >> +        v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
> >> +    value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
> >> +        v_INETLACE(1) : v_INETLACE(0);
> >> +
> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
> >> +    return 0;
> >> +}
> >> +
> >> +static int starfive_hdmi_setup(struct starfive_hdmi *hdmi,
> >> +                   struct drm_display_mode *mode)
> >> +{
> >> +    hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
> >> +    hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
> >> +    hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
> >> +
> >> +    hdmi->tmds_rate = mode->clock * 1000;
> >> +    starfive_hdmi_phy_clk_set_rate(hdmi);
> >> +
> >> +    while (!(hdmi_readb(hdmi, STARFIVE_PRE_PLL_LOCK_STATUS) & 0x1))
> >> +        continue;
> >> +    while (!(hdmi_readb(hdmi, STARFIVE_POST_PLL_LOCK_STATUS) & 0x1))
> >> +        continue;
> >
> > Please use read_iopoll_timeout here.
> >
> >> +
> >> +    /*turn on LDO*/
> >> +    hdmi_writeb(hdmi, STARFIVE_LDO_CONTROL, STARFIVE_LDO_ENABLE);
> >> +    /*turn on serializer*/
> >> +    hdmi_writeb(hdmi, STARFIVE_SERIALIER_CONTROL, STARFIVE_SERIALIER_ENABLE);
> >> +
> >> +    starfive_hdmi_tx_phy_power_down(hdmi);
> >> +    starfive_hdmi_config_video_timing(hdmi, mode);
> >> +    starfive_hdmi_tx_phy_power_on(hdmi);
> >> +
> >> +    starfive_hdmi_tmds_driver_on(hdmi);
> >> +    starfive_hdmi_sync_tmds(hdmi);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static void starfive_hdmi_encoder_mode_set(struct drm_encoder *encoder,
> >> +                       struct drm_display_mode *mode,
> >> +                       struct drm_display_mode *adj_mode)
> >> +{
> >> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> >> +
> >> +    drm_mode_copy(&hdmi->previous_mode, adj_mode);
> >
> > Why do you need it?
> >
> >> +}
> >> +
> >> +static void starfive_hdmi_encoder_enable(struct drm_encoder *encoder)
> >> +{
> >> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> >> +    struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
> >> +    int ret, idx;
> >> +    struct drm_device *drm = hdmi->connector.dev;
> >> +
> >> +    if (drm && !drm_dev_enter(drm, &idx))
> >> +        return;
> >> +
> >> +    ret = pm_runtime_get_sync(hdmi->dev);
> >> +    if (ret < 0)
> >> +        return;
> >> +    mdelay(10);
> >> +    starfive_hdmi_setup(hdmi, mode);
> >> +
> >> +    if (drm)
> >> +        drm_dev_exit(idx);
> >> +}
> >> +
> >> +static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
> >> +{
> >> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
> >> +
> >> +    int idx;
> >> +    struct drm_device *drm = hdmi->connector.dev;
> >> +
> >> +    if (drm && !drm_dev_enter(drm, &idx))
> >> +        return;
> >> +
> >> +    pm_runtime_put(hdmi->dev);
> >> +
> >> +    if (drm)
> >> +        drm_dev_exit(idx);
> >> +}
> >> +
> >> +static int
> >> +starfive_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
> >> +                   struct drm_crtc_state *crtc_state,
> >> +                   struct drm_connector_state *conn_state)
> >> +{
> >> +    bool valid = false;
> >> +    struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> >> +    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
> >> +
> >> +    vs_crtc_state->encoder_type = encoder->encoder_type;
> >> +    vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
> >> +
> >> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
> >> +    int pclk = mode->clock * 1000;
> >> +
> >> +    for (; cfg->pixclock != 0; cfg++) {
> >> +        if (pclk == cfg->pixclock) {
> >> +            if (pclk > 297000000)
> >
> > Magic value.
> >
> >> +                continue;
> >> +
> >> +            valid = true;
> >> +            break;
> >> +        }
> >> +    }
> >> +
> >> +    return (valid) ? 0 : -EINVAL;
> >> +}
> >> +
> >> +static const struct drm_encoder_helper_funcs starfive_hdmi_encoder_helper_funcs = {
> >> +    .enable     = starfive_hdmi_encoder_enable,
> >> +    .disable    = starfive_hdmi_encoder_disable,
> >> +    .mode_set   = starfive_hdmi_encoder_mode_set,
> >> +    .atomic_check = starfive_hdmi_encoder_atomic_check,
> >> +};
> >> +
> >> +static enum drm_connector_status
> >> +starfive_hdmi_connector_detect(struct drm_connector *connector, bool force)
> >> +{
> >> +    struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
> >> +    struct drm_device *drm = hdmi->connector.dev;
> >> +    int ret;
> >> +    int idx;
> >> +
> >> +    if (drm && !drm_dev_enter(drm, &idx))
> >> +        return connector_status_disconnected;
> >> +
> >> +    ret = pm_runtime_get_sync(hdmi->dev);
> >> +    if (ret < 0)
> >> +        return ret;
> >> +
> >> +    ret = (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
> >> +        connector_status_connected : connector_status_disconnected;
> >> +    pm_runtime_put(hdmi->dev);
> >> +
> >> +    if (drm)
> >> +        drm_dev_exit(idx);
> >> +
> >> +    return ret;
> >> +}
> >> +
> >> +static int starfive_hdmi_connector_get_modes(struct drm_connector *connector)
> >> +{
> >> +    struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
> >> +    struct edid *edid;
> >> +    int ret = 0;
> >> +
> >> +    if (!hdmi->ddc)
> >> +        return 0;
> >> +    ret = pm_runtime_get_sync(hdmi->dev);
> >> +    if (ret < 0)
> >> +        return ret;
> >> +
> >> +    edid = drm_get_edid(connector, hdmi->ddc);
> >> +    if (edid) {
> >> +        hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
> >> +        hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
> >> +        drm_connector_update_edid_property(connector, edid);
> >> +        ret = drm_add_edid_modes(connector, edid);
> >> +        kfree(edid);
> >> +    }
> >> +    pm_runtime_put(hdmi->dev);
> >
> > Can you use drm_connector_helper_get_modes_from_ddc() here? And then drm_display_info.is_hdmi and .has_audio.
> >
> >> +
> >> +    return ret;
> >> +}
> >> +
> >> +static enum drm_mode_status
> >> +starfive_hdmi_connector_mode_valid(struct drm_connector *connector,
> >> +                   struct drm_display_mode *mode)
> >> +{
> >> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
> >> +    int pclk = mode->clock * 1000;
> >> +    bool valid = false;
> >> +
> >> +    for (; cfg->pixclock != 0; cfg++) {
> >> +        if (pclk == cfg->pixclock) {
> >> +            if (pclk > 297000000)
> >
> > magic value
> >
> >> +                continue;
> >> +
> >> +            valid = true;
> >> +            break;
> >> +        }
> >> +    }
> >> +
> >> +    return (valid) ? MODE_OK : MODE_BAD;
> >> +}
> >> +
> >> +static int
> >> +starfive_hdmi_probe_single_connector_modes(struct drm_connector *connector,
> >> +                       u32 maxX, u32 maxY)
> >> +{
> >> +    return drm_helper_probe_single_connector_modes(connector, 3840, 2160);
> >> +}
> >> +
> >> +static const struct drm_connector_funcs starfive_hdmi_connector_funcs = {
> >> +    .fill_modes = starfive_hdmi_probe_single_connector_modes,
> >> +    .detect = starfive_hdmi_connector_detect,
> >> +    .reset = drm_atomic_helper_connector_reset,
> >> +    .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> >> +    .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> >> +};
> >> +
> >> +static struct drm_connector_helper_funcs starfive_hdmi_connector_helper_funcs = {
> >> +    .get_modes = starfive_hdmi_connector_get_modes,
> >> +    .mode_valid = starfive_hdmi_connector_mode_valid,
> >> +};
> >> +
> >> +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
> >> +{
> >> +    struct drm_encoder *encoder = &hdmi->encoder;
> >> +    struct device *dev = hdmi->dev;
> >> +
> >> +    encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
> >> +
> >> +    /*
> >> +     * If we failed to find the CRTC(s) which this encoder is
> >> +     * supposed to be connected to, it's because the CRTC has
> >> +     * not been registered yet.  Defer probing, and hope that
> >> +     * the required CRTC is added later.
> >> +     */
> >> +    if (encoder->possible_crtcs == 0)
> >> +        return -EPROBE_DEFER;
> >> +
> >> +    drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
> >> +
> >> +    hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> >> +
> >> +    drm_connector_helper_add(&hdmi->connector,
> >> +                 &starfive_hdmi_connector_helper_funcs);
> >> +    drmm_connector_init(drm, &hdmi->connector,
> >> +                &starfive_hdmi_connector_funcs,
> >> +                DRM_MODE_CONNECTOR_HDMIA,
> >
> > On an embedded device one can not be so sure. There can be MHL or HDMI Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.
> >
> >> +                hdmi->ddc);
> >> +
> >> +    drm_connector_attach_encoder(&hdmi->connector, encoder);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static irqreturn_t starfive_hdmi_i2c_irq(struct starfive_hdmi *hdmi)
> >> +{
> >> +    struct starfive_hdmi_i2c *i2c = hdmi->i2c;
> >> +    u8 stat;
> >> +
> >> +    stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
> >> +    if (!(stat & m_INT_EDID_READY))
> >> +        return IRQ_NONE;
> >> +
> >> +    /* Clear HDMI EDID interrupt flag */
> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
> >> +
> >> +    complete(&i2c->cmp);
> >> +
> >> +    return IRQ_HANDLED;
> >> +}
> >> +
> >> +static irqreturn_t starfive_hdmi_hardirq(int irq, void *dev_id)
> >> +{
> >> +    struct starfive_hdmi *hdmi = dev_id;
> >> +    irqreturn_t ret = IRQ_NONE;
> >> +    u8 interrupt;
> >> +
> >> +    if (hdmi->i2c)
> >> +        ret = starfive_hdmi_i2c_irq(hdmi);
> >> +
> >> +    interrupt = hdmi_readb(hdmi, HDMI_STATUS);
> >> +    if (interrupt & m_INT_HOTPLUG) {
> >> +        hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
> >> +        ret = IRQ_WAKE_THREAD;
> >> +    }
> >> +
> >> +    return ret;
> >> +}
> >> +
> >> +static irqreturn_t starfive_hdmi_irq(int irq, void *dev_id)
> >> +{
> >> +    struct starfive_hdmi *hdmi = dev_id;
> >> +
> >> +    drm_connector_helper_hpd_irq_event(&hdmi->connector);
> >> +
> >> +    return IRQ_HANDLED;
> >> +}
> >> +
> >> +static int starfive_hdmi_i2c_read(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
> >> +{
> >> +    int length = msgs->len;
> >> +    u8 *buf = msgs->buf;
> >> +    int ret;
> >> +
> >> +    ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10);
> >
> > read_poll_timeout
> Hi Dmitry:
> I think using wait_for_completion_timeout here is a bit clearer than read_poll_timeout logic
> I2c_read has hardware interrupt dependency. so need wait the interrupt completion.
> also it comes with timeout detection.

Hi,

Yes, maybe I was thinking about a different piece of code here.
wait_for_completion looks good here, indeed.

But this actually brings up another question. You reinit_comletion in
starfive_hdmi_i2c_write() and then wait_for_completion in
starfive_hdmi_i2c_read(). How does that work?



>
> >
> >> +    if (!ret)
> >> +        return -EAGAIN;
> >> +
> >> +    while (length--)
> >> +        *buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int starfive_hdmi_i2c_write(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
> >> +{
> >> +    /*
> >> +     * The DDC module only support read EDID message, so
> >> +     * we assume that each word write to this i2c adapter
> >> +     * should be the offset of EDID word address.
> >> +     */
> >> +    if (msgs->len != 1 ||
> >> +        (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR))
> >> +        return -EINVAL;
> >> +
> >> +    reinit_completion(&hdmi->i2c->cmp);
> >> +
> >> +    if (msgs->addr == DDC_SEGMENT_ADDR)
> >> +        hdmi->i2c->segment_addr = msgs->buf[0];
> >> +    if (msgs->addr == DDC_ADDR)
> >> +        hdmi->i2c->ddc_addr = msgs->buf[0];
> >> +
> >> +    /* Set edid fifo first addr */
> >> +    hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
> >> +
> >> +    /* Set edid word address 0x00/0x80 */
> >> +    hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
> >> +
> >> +    /* Set edid segment pointer */
> >> +    hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int starfive_hdmi_i2c_xfer(struct i2c_adapter *adap,
> >> +                  struct i2c_msg *msgs, int num)
> >> +{
> >> +    struct starfive_hdmi *hdmi = i2c_get_adapdata(adap);
> >> +    struct starfive_hdmi_i2c *i2c = hdmi->i2c;
> >> +    int i, ret = 0;
> >> +
> >> +    mutex_lock(&i2c->lock);
> >> +
> >> +    /* Clear the EDID interrupt flag and unmute the interrupt */
> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
> >> +
> >> +    for (i = 0; i < num; i++) {
> >> +        DRM_DEV_DEBUG(hdmi->dev,
> >> +                  "xfer: num: %d/%d, len: %d, flags: %#x\n",
> >> +                  i + 1, num, msgs[i].len, msgs[i].flags);
> >> +
> >> +        if (msgs[i].flags & I2C_M_RD)
> >> +            ret = starfive_hdmi_i2c_read(hdmi, &msgs[i]);
> >> +        else
> >> +            ret = starfive_hdmi_i2c_write(hdmi, &msgs[i]);
> >> +
> >> +        if (ret < 0)
> >> +            break;
> >> +    }
> >> +
> >> +    if (!ret)
> >> +        ret = num;
> >> +
> >> +    /* Mute HDMI EDID interrupt */
> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
> >> +
> >> +    mutex_unlock(&i2c->lock);
> >> +
> >> +    return ret;
> >> +}
> >> +
> >> +static u32 starfive_hdmi_i2c_func(struct i2c_adapter *adapter)
> >> +{
> >> +    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> >> +}
> >> +
> >> +static const struct i2c_algorithm starfive_hdmi_algorithm = {
> >> +    .master_xfer    = starfive_hdmi_i2c_xfer,
> >> +    .functionality    = starfive_hdmi_i2c_func,
> >> +};
> >> +
> >> +static struct i2c_adapter *starfive_hdmi_i2c_adapter(struct starfive_hdmi *hdmi)
> >> +{
> >> +    struct i2c_adapter *adap;
> >> +    struct starfive_hdmi_i2c *i2c;
> >> +    int ret;
> >> +
> >> +    i2c = drmm_kzalloc(hdmi->drm_dev, sizeof(*i2c), GFP_KERNEL);
> >> +    if (!i2c)
> >> +        return ERR_PTR(-ENOMEM);
> >> +
> >> +    mutex_init(&i2c->lock);
> >> +    init_completion(&i2c->cmp);
> >> +
> >> +    adap = &i2c->adap;
> >> +    adap->class = I2C_CLASS_DDC;
> >> +    adap->owner = THIS_MODULE;
> >> +    adap->dev.parent = hdmi->dev;
> >> +    adap->algo = &starfive_hdmi_algorithm;
> >> +    strscpy(adap->name, "Starfive HDMI", sizeof(adap->name));
> >> +    i2c_set_adapdata(adap, hdmi);
> >> +
> >> +    ret = devm_i2c_add_adapter(hdmi->dev, adap);
> >> +    if (ret) {
> >> +        drm_warn(hdmi->drm_dev, "cannot add %s I2C adapter\n", adap->name);
> >> +        return ERR_PTR(ret);
> >> +    }
> >> +
> >> +    hdmi->i2c = i2c;
> >> +
> >> +    drm_info(hdmi->drm_dev, "registered %s I2C bus driver success\n", adap->name);
> >> +
> >> +    return adap;
> >> +}
> >> +
> >> +static int starfive_hdmi_get_clk_rst(struct device *dev, struct starfive_hdmi *hdmi)
> >> +{
> >> +    hdmi->sys_clk = devm_clk_get(dev, "sysclk");
> >> +    if (IS_ERR(hdmi->sys_clk)) {
> >> +        dev_err(dev, "Unable to get HDMI sysclk clk\n");
> >> +        return PTR_ERR(hdmi->sys_clk);
> >> +    }
> >> +    hdmi->mclk = devm_clk_get(dev, "mclk");
> >> +    if (IS_ERR(hdmi->mclk)) {
> >> +        dev_err(dev, "Unable to get HDMI mclk clk\n");
> >> +        return PTR_ERR(hdmi->mclk);
> >> +    }
> >> +    hdmi->bclk = devm_clk_get(dev, "bclk");
> >> +    if (IS_ERR(hdmi->bclk)) {
> >> +        dev_err(dev, "Unable to get HDMI bclk clk\n");
> >> +        return PTR_ERR(hdmi->bclk);
> >> +    }
> >> +    hdmi->tx_rst = devm_reset_control_get_by_index(dev, 0);
> >> +    if (IS_ERR(hdmi->tx_rst)) {
> >> +        dev_err(dev, "failed to get tx_rst reset\n");
> >> +        return PTR_ERR(hdmi->tx_rst);
> >> +    }
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int starfive_hdmi_bind(struct device *dev, struct device *master,
> >> +                  void *data)
> >> +{
> >> +    struct platform_device *pdev = to_platform_device(dev);
> >> +    struct drm_device *drm = dev_get_drvdata(master);
> >> +    struct starfive_hdmi *hdmi;
> >> +    struct resource *iores;
> >> +    int irq;
> >> +    int ret;
> >> +
> >> +    hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
> >> +    if (!hdmi)
> >> +        return -ENOMEM;
> >> +
> >> +    hdmi = drmm_simple_encoder_alloc(drm, struct starfive_hdmi,
> >> +                     encoder, DRM_MODE_ENCODER_TMDS);
> >> +
> >> +    hdmi->dev = dev;
> >> +    hdmi->drm_dev = drm;
> >> +    dev_set_drvdata(dev, hdmi);
> >> +
> >> +    iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >> +    hdmi->regs = devm_ioremap_resource(dev, iores);
> >
> > This should go to probe
> >
> >> +    if (IS_ERR(hdmi->regs))
> >> +        return PTR_ERR(hdmi->regs);
> >> +
> >> +    ret = starfive_hdmi_get_clk_rst(dev, hdmi);
> >> +    if (ret < 0)
> >> +        return ret;
> >
> > And this
> >
> >> +
> >> +    ret = devm_pm_runtime_enable(dev);
> >> +    if (ret)
> >> +        return ret;
> >> +
> >> +    ret = pm_runtime_resume_and_get(dev);
> >> +    if (ret)
> >> +        return ret;
> >> +
> >> +    irq = platform_get_irq(pdev, 0);
> >
> > And this
> >
> >> +    if (irq < 0) {
> >> +        ret = irq;
> >> +        goto err_put_runtime_pm;
> >> +    }
> >> +
> >> +    hdmi->ddc = starfive_hdmi_i2c_adapter(hdmi);
> >> +    if (IS_ERR(hdmi->ddc)) {
> >> +        ret = PTR_ERR(hdmi->ddc);
> >> +        hdmi->ddc = NULL;
> >> +        goto err_put_runtime_pm;
> >> +    }
> >> +
> >> +    starfive_hdmi_i2c_init(hdmi);
> >> +
> >> +    ret = starfive_hdmi_register(drm, hdmi);
> >> +    if (ret)
> >> +        goto err_put_adapter;
> >> +
> >> +    /* Unmute hotplug interrupt */
> >> +    hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
> >> +
> >> +    ret = devm_request_threaded_irq(dev, irq, starfive_hdmi_hardirq,
> >> +                    starfive_hdmi_irq, IRQF_SHARED,
> >> +                    dev_name(dev), hdmi);
> >> +    if (ret < 0)
> >> +        goto err_put_adapter;
> >> +
> >> +    pm_runtime_put_sync(dev);
> >> +
> >> +    return 0;
> >> +
> >> +err_put_adapter:
> >> +    i2c_put_adapter(hdmi->ddc);
> >> +err_put_runtime_pm:
> >> +    pm_runtime_put_sync(dev);
> >> +
> >> +    return ret;
> >> +}
> >> +
> >> +static const struct component_ops starfive_hdmi_ops = {
> >> +    .bind    = starfive_hdmi_bind,
> >> +};
> >> +
> >> +static int starfive_hdmi_probe(struct platform_device *pdev)
> >> +{
> >> +    return component_add(&pdev->dev, &starfive_hdmi_ops);
> >> +}
> >> +
> >> +static int starfive_hdmi_remove(struct platform_device *pdev)
> >> +{
> >> +    component_del(&pdev->dev, &starfive_hdmi_ops);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static const struct dev_pm_ops hdmi_pm_ops = {
> >> +    SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
> >> +    SET_LATE_SYSTEM_SLEEP_PM_OPS(hdmi_system_pm_suspend, hdmi_system_pm_resume)
> >> +};
> >> +
> >> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
> >> +    { .compatible = "starfive,jh7110-inno-hdmi",},
> >> +    {},
> >> +};
> >> +MODULE_DEVICE_TABLE(of, starfive_hdmi_dt_ids);
> >> +
> >> +struct platform_driver starfive_hdmi_driver = {
> >> +    .probe  = starfive_hdmi_probe,
> >> +    .remove = starfive_hdmi_remove,
> >> +    .driver = {
> >> +        .name = "starfive-hdmi",
> >> +        .of_match_table = starfive_hdmi_dt_ids,
> >> +        .pm = &hdmi_pm_ops,
> >> +    },
> >> +};
> >> +
> >> +MODULE_AUTHOR("StarFive Corporation");
> >> +MODULE_DESCRIPTION("Starfive HDMI Driver");
> >> diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.h b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
> >> new file mode 100644
> >> index 000000000..1b9a11bca
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
> >> @@ -0,0 +1,295 @@
> >> +/* SPDX-License-Identifier: GPL-2.0-only */
> >> +/*
> >> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
> >> + */
> >> +
> >> +#ifndef __STARFIVE_HDMI_H__
> >> +#define __STARFIVE_HDMI_H__
> >> +
> >> +#include <drm/bridge/dw_hdmi.h>
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_edid.h>
> >> +#include <drm/drm_of.h>
> >> +#include <drm/drm_probe_helper.h>
> >> +#include <drm/drm_simple_kms_helper.h>
> >> +#include <linux/bitfield.h>
> >> +#include <linux/bits.h>
> >> +
> >> +#define DDC_SEGMENT_ADDR        0x30
> >> +
> >> +#define HDMI_SCL_RATE            (100 * 1000)
> >> +#define DDC_BUS_FREQ_L            0x4b
> >> +#define DDC_BUS_FREQ_H            0x4c
> >> +
> >> +#define HDMI_SYS_CTRL            0x00
> >> +#define m_RST_ANALOG            BIT(6)
> >> +#define v_RST_ANALOG            0
> >> +#define v_NOT_RST_ANALOG        BIT(6)
> >> +#define m_RST_DIGITAL            BIT(5)
> >> +#define v_RST_DIGITAL            0
> >> +#define v_NOT_RST_DIGITAL        BIT(5)
> >> +#define m_REG_CLK_INV            BIT(4)
> >> +#define v_REG_CLK_NOT_INV        0
> >> +#define v_REG_CLK_INV            BIT(4)
> >> +#define m_VCLK_INV            BIT(3)
> >> +#define v_VCLK_NOT_INV            0
> >> +#define v_VCLK_INV            BIT(3)
> >> +#define m_REG_CLK_SOURCE        BIT(2)
> >> +#define v_REG_CLK_SOURCE_TMDS        0
> >> +#define v_REG_CLK_SOURCE_SYS        BIT(2)
> >> +#define m_POWER                BIT(1)
> >> +#define v_PWR_ON            0
> >> +#define v_PWR_OFF            BIT(1)
> >> +#define m_INT_POL            BIT(0)
> >> +#define v_INT_POL_HIGH            1
> >> +#define v_INT_POL_LOW            0
> >> +
> >> +#define HDMI_AV_MUTE            0x05
> >> +#define m_AVMUTE_CLEAR            BIT(7)
> >> +#define m_AVMUTE_ENABLE            BIT(6)
> >> +#define m_AUDIO_MUTE            BIT(1)
> >> +#define m_VIDEO_BLACK            BIT(0)
> >> +#define v_AVMUTE_CLEAR(n)        ((n) << 7)
> >> +#define v_AVMUTE_ENABLE(n)        ((n) << 6)
> >> +#define v_AUDIO_MUTE(n)            ((n) << 1)
> >> +#define v_VIDEO_MUTE(n)            ((n) << 0)
> >> +
> >> +#define HDMI_VIDEO_TIMING_CTL        0x08
> >> +#define v_VSYNC_POLARITY(n)        ((n) << 3)
> >> +#define v_HSYNC_POLARITY(n)        ((n) << 2)
> >> +#define v_INETLACE(n)            ((n) << 1)
> >> +#define v_EXTERANL_VIDEO(n)        ((n) << 0)
> >> +
> >> +#define HDMI_VIDEO_EXT_HTOTAL_L        0x09
> >> +#define HDMI_VIDEO_EXT_HTOTAL_H        0x0a
> >> +#define HDMI_VIDEO_EXT_HBLANK_L        0x0b
> >> +#define HDMI_VIDEO_EXT_HBLANK_H        0x0c
> >> +#define HDMI_VIDEO_EXT_HDELAY_L        0x0d
> >> +#define HDMI_VIDEO_EXT_HDELAY_H        0x0e
> >> +#define HDMI_VIDEO_EXT_HDURATION_L    0x0f
> >> +#define HDMI_VIDEO_EXT_HDURATION_H    0x10
> >> +#define HDMI_VIDEO_EXT_VTOTAL_L        0x11
> >> +#define HDMI_VIDEO_EXT_VTOTAL_H        0x12
> >> +#define HDMI_VIDEO_EXT_VBLANK        0x13
> >> +#define HDMI_VIDEO_EXT_VDELAY        0x14
> >> +#define HDMI_VIDEO_EXT_VDURATION    0x15
> >> +
> >> +#define HDMI_EDID_SEGMENT_POINTER    0x4d
> >> +#define HDMI_EDID_WORD_ADDR        0x4e
> >> +#define HDMI_EDID_FIFO_OFFSET        0x4f
> >> +#define HDMI_EDID_FIFO_ADDR        0x50
> >> +
> >> +#define HDMI_INTERRUPT_MASK1        0xc0
> >> +#define HDMI_INTERRUPT_STATUS1        0xc1
> >> +#define    m_INT_ACTIVE_VSYNC        BIT(5)
> >> +#define m_INT_EDID_READY        BIT(2)
> >> +
> >> +#define HDMI_STATUS            0xc8
> >> +#define m_HOTPLUG            BIT(7)
> >> +#define m_MASK_INT_HOTPLUG        BIT(5)
> >> +#define m_INT_HOTPLUG            BIT(1)
> >> +#define v_MASK_INT_HOTPLUG(n)        (((n) & 0x1) << 5)
> >
> > It is untypical to have field defines which start with lowercase letter.
> >
> >> +
> >> +#define HDMI_SYNC                    0xce
> >> +
> >> +#define UPDATE(x, h, l)                    FIELD_PREP(GENMASK(h, l), x)
> >> +
> >> +/* REG: 0x1a0 */
> >> +#define STARFIVE_PRE_PLL_CONTROL            0x1a0
> >> +#define STARFIVE_PCLK_VCO_DIV_5_MASK            BIT(1)
> >> +#define STARFIVE_PCLK_VCO_DIV_5(x)            UPDATE(x, 1, 1)
> >> +#define STARFIVE_PRE_PLL_POWER_DOWN            BIT(0)
> >> +
> >> +/* REG: 0x1a1 */
> >> +#define STARFIVE_PRE_PLL_DIV_1                0x1a1
> >> +#define STARFIVE_PRE_PLL_PRE_DIV_MASK            GENMASK(5, 0)
> >> +#define STARFIVE_PRE_PLL_PRE_DIV(x)            UPDATE(x, 5, 0)
> >> +
> >> +/* REG: 0x1a2 */
> >> +#define STARFIVE_PRE_PLL_DIV_2                0x1a2
> >> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DOWN        BIT(7)
> >> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE        BIT(6)
> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_DISABLE        UPDATE(3, 5, 4)
> >> +#define STARFIVE_PRE_PLL_FB_DIV_11_8_MASK        GENMASK(3, 0)
> >> +#define STARFIVE_PRE_PLL_FB_DIV_11_8(x)            UPDATE((x) >> 8, 3, 0)
> >> +
> >> +/* REG: 0x1a3 */
> >> +#define STARFIVE_PRE_PLL_DIV_3                0x1a3
> >> +#define STARFIVE_PRE_PLL_FB_DIV_7_0(x)            UPDATE(x, 7, 0)
> >> +
> >> +/* REG: 0x1a4*/
> >> +#define STARFIVE_PRE_PLL_DIV_4                0x1a4
> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C_MASK        GENMASK(1, 0)
> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C(x)        UPDATE(x, 1, 0)
> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B_MASK        GENMASK(3, 2)
> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B(x)        UPDATE(x, 3, 2)
> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A_MASK        GENMASK(5, 4)
> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A(x)        UPDATE(x, 5, 4)
> >> +
> >> +/* REG: 0x1a5 */
> >> +#define STARFIVE_PRE_PLL_DIV_5                0x1a5
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_SHIFT        5
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_MASK        GENMASK(6, 5)
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_B(x)            UPDATE(x, 6, 5)
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_A_MASK        GENMASK(4, 0)
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_A(x)            UPDATE(x, 4, 0)
> >> +
> >> +/* REG: 0x1a6 */
> >> +#define STARFIVE_PRE_PLL_DIV_6                0x1a6
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_SHIFT        5
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_MASK        GENMASK(6, 5)
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_C(x)            UPDATE(x, 6, 5)
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_D_MASK        GENMASK(4, 0)
> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_D(x)            UPDATE(x, 4, 0)
> >> +
> >> +/* REG: 0x1a9 */
> >> +#define STARFIVE_PRE_PLL_LOCK_STATUS            0x1a9
> >> +
> >> +/* REG: 0x1aa */
> >> +#define STARFIVE_POST_PLL_DIV_1                0x1aa
> >> +#define STARFIVE_POST_PLL_POST_DIV_ENABLE        GENMASK(3, 2)
> >> +#define STARFIVE_POST_PLL_REFCLK_SEL_TMDS        BIT(1)
> >> +#define STARFIVE_POST_PLL_POWER_DOWN            BIT(0)
> >> +#define STARFIVE_POST_PLL_FB_DIV_8(x)            UPDATE(((x) >> 8) << 4, 4, 4)
> >> +
> >> +/* REG:0x1ab */
> >> +#define STARFIVE_POST_PLL_DIV_2                0x1ab
> >> +#define STARFIVE_POST_PLL_Pre_DIV_MASK            GENMASK(5, 0)
> >> +#define STARFIVE_POST_PLL_PRE_DIV(x)            UPDATE(x, 5, 0)
> >> +
> >> +/* REG: 0x1ac */
> >> +#define STARFIVE_POST_PLL_DIV_3                0x1ac
> >> +#define STARFIVE_POST_PLL_FB_DIV_7_0(x)            UPDATE(x, 7, 0)
> >> +
> >> +/* REG: 0x1ad */
> >> +#define STARFIVE_POST_PLL_DIV_4                0x1ad
> >> +#define STARFIVE_POST_PLL_POST_DIV_MASK            GENMASK(2, 0)
> >> +#define STARFIVE_POST_PLL_POST_DIV_2            0x0
> >> +#define STARFIVE_POST_PLL_POST_DIV_4            0x1
> >> +#define STARFIVE_POST_PLL_POST_DIV_8            0x3
> >> +
> >> +/* REG: 0x1af */
> >> +#define STARFIVE_POST_PLL_LOCK_STATUS            0x1af
> >> +
> >> +/* REG: 0x1b0 */
> >> +#define STARFIVE_BIAS_CONTROL                0x1b0
> >> +#define STARFIVE_BIAS_ENABLE                BIT(2)
> >> +
> >> +/* REG: 0x1b2 */
> >> +#define STARFIVE_TMDS_CONTROL                0x1b2
> >> +#define STARFIVE_TMDS_CLK_DRIVER_EN            BIT(3)
> >> +#define STARFIVE_TMDS_D2_DRIVER_EN            BIT(2)
> >> +#define STARFIVE_TMDS_D1_DRIVER_EN            BIT(1)
> >> +#define STARFIVE_TMDS_D0_DRIVER_EN            BIT(0)
> >> +#define STARFIVE_TMDS_DRIVER_ENABLE            (STARFIVE_TMDS_CLK_DRIVER_EN | \
> >> +                             STARFIVE_TMDS_D2_DRIVER_EN | \
> >> +                             STARFIVE_TMDS_D1_DRIVER_EN | \
> >> +                             STARFIVE_TMDS_D0_DRIVER_EN)
> >> +
> >> +/* REG: 0x1b4 */
> >> +#define STARFIVE_LDO_CONTROL                0x1b4
> >> +#define STARFIVE_LDO_D2_EN                BIT(2)
> >> +#define STARFIVE_LDO_D1_EN                BIT(1)
> >> +#define STARFIVE_LDO_D0_EN                BIT(0)
> >> +#define STARFIVE_LDO_ENABLE                (STARFIVE_LDO_D2_EN | \
> >> +                             STARFIVE_LDO_D1_EN | \
> >> +                             STARFIVE_LDO_D0_EN)
> >> +
> >> +/* REG: 0x1be */
> >> +#define STARFIVE_SERIALIER_CONTROL            0x1be
> >> +#define STARFIVE_SERIALIER_D2_EN            BIT(6)
> >> +#define STARFIVE_SERIALIER_D1_EN            BIT(5)
> >> +#define STARFIVE_SERIALIER_D0_EN            BIT(4)
> >> +#define STARFIVE_SERIALIER_EN                BIT(0)
> >> +
> >> +#define STARFIVE_SERIALIER_ENABLE            (STARFIVE_SERIALIER_D2_EN | \
> >> +                             STARFIVE_SERIALIER_D1_EN | \
> >> +                             STARFIVE_SERIALIER_D0_EN | \
> >> +                             STARFIVE_SERIALIER_EN)
> >> +
> >> +/* REG: 0x1cc */
> >> +#define STARFIVE_RX_CONTROL                0x1cc
> >> +#define STARFIVE_RX_EN                    BIT(3)
> >> +#define STARFIVE_RX_CHANNEL_2_EN            BIT(2)
> >> +#define STARFIVE_RX_CHANNEL_1_EN            BIT(1)
> >> +#define STARFIVE_RX_CHANNEL_0_EN            BIT(0)
> >> +#define STARFIVE_RX_ENABLE                (STARFIVE_RX_EN | \
> >> +                             STARFIVE_RX_CHANNEL_2_EN | \
> >> +                             STARFIVE_RX_CHANNEL_1_EN | \
> >> +                             STARFIVE_RX_CHANNEL_0_EN)
> >> +
> >> +/* REG: 0x1d1 */
> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_H            0x1d1
> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_23_16(x)        UPDATE((x) >> 16, 7, 0)
> >> +/* REG: 0x1d2 */
> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_M            0x1d2
> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_15_8(x)        UPDATE((x) >> 8, 7, 0)
> >> +/* REG: 0x1d3 */
> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_L            0x1d3
> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_7_0(x)        UPDATE(x, 7, 0)
> >> +
> >> +struct pre_pll_config {
> >> +    unsigned long pixclock;
> >> +    unsigned long tmdsclock;
> >> +    u8 prediv;
> >> +    u16 fbdiv;
> >> +    u8 tmds_div_a;
> >> +    u8 tmds_div_b;
> >> +    u8 tmds_div_c;
> >> +    u8 pclk_div_a;
> >> +    u8 pclk_div_b;
> >> +    u8 pclk_div_c;
> >> +    u8 pclk_div_d;
> >> +    u8 vco_div_5_en;
> >> +    u32 fracdiv;
> >> +};
> >> +
> >> +struct post_pll_config {
> >> +    unsigned long tmdsclock;
> >> +    u8 prediv;
> >> +    u16 fbdiv;
> >> +    u8 postdiv;
> >> +    u8 post_div_en;
> >> +    u8 version;
> >> +};
> >> +
> >> +struct phy_config {
> >> +    unsigned long    tmdsclock;
> >> +    u8        regs[14];
> >> +};
> >> +
> >> +struct hdmi_data_info {
> >> +    int vic;
> >> +    bool sink_is_hdmi;
> >> +    bool sink_has_audio;
> >> +    unsigned int enc_in_format;
> >> +    unsigned int enc_out_format;
> >> +    unsigned int colorimetry;
> >> +};
> >> +
> >> +struct starfive_hdmi {
> >> +    struct device *dev;
> >> +    struct drm_device *drm_dev;
> >> +    struct drm_encoder    encoder;
> >> +    struct drm_connector    connector;
> >> +
> >> +    struct starfive_hdmi_i2c *i2c;
> >> +
> >> +    int irq;
> >> +    struct clk *sys_clk;
> >> +    struct clk *mclk;
> >> +    struct clk *bclk;
> >> +    struct reset_control *tx_rst;
> >> +    void __iomem *regs;
> >> +
> >> +    struct i2c_adapter *ddc;
> >> +
> >> +    unsigned long tmds_rate;
> >> +
> >> +    struct hdmi_data_info    hdmi_data;
> >> +    struct drm_display_mode previous_mode;
> >> +    const struct pre_pll_config    *pre_cfg;
> >> +    const struct post_pll_config    *post_cfg;
> >> +};
> >> +
> >> +#endif /* __STARFIVE_HDMI_H__ */
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
> >> index 3cd533cfa..9669354bd 100644
> >> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
> >> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> >> @@ -126,6 +126,11 @@ static const struct component_master_ops vs_drm_ops = {
> >>   static struct platform_driver *drm_sub_drivers[] = {
> >>       &dc_platform_driver,
> >>   +    /* connector + encoder*/
> >> +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
> >> +    &starfive_hdmi_driver,
> >> +#endif
> >> +
> >>   };
> >>     static struct component_match *vs_drm_match_add(struct device *dev)
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
> >> index 369ad22d6..e90723419 100644
> >> --- a/drivers/gpu/drm/verisilicon/vs_drv.h
> >> +++ b/drivers/gpu/drm/verisilicon/vs_drv.h
> >> @@ -24,4 +24,8 @@ to_vs_drm_private(const struct drm_device *dev)
> >>       return container_of(dev, struct vs_drm_device, base);
> >>   }
> >>   +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
> >> +extern struct platform_driver starfive_hdmi_driver;
> >> +#endif
> >> +
> >>   #endif /* __VS_DRV_H__ */
> >



-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
  2023-11-13 13:31         ` Dmitry Baryshkov
  (?)
@ 2023-11-14  8:27           ` Keith Zhao
  -1 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-11-14  8:27 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Maxime Ripard, Jagan Teki, Rob Herring,
	Chris Morgan, Paul Walmsley, Bjorn Andersson, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Shawn Guo, christian.koenig



On 2023/11/13 21:31, Dmitry Baryshkov wrote:
> On Mon, 13 Nov 2023 at 14:11, Keith Zhao <keith.zhao@starfivetech.com> wrote:
>>
>>
>>
>> On 2023/10/26 6:23, Dmitry Baryshkov wrote:
>> > On 25/10/2023 13:39, Keith Zhao wrote:
>> >> add hdmi driver as encoder and connect
>> >>
>> >> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>> >> ---
>> >>   drivers/gpu/drm/verisilicon/Kconfig         |   8 +-
>> >>   drivers/gpu/drm/verisilicon/Makefile        |   1 +
>> >>   drivers/gpu/drm/verisilicon/starfive_hdmi.c | 949 ++++++++++++++++++++
>> >>   drivers/gpu/drm/verisilicon/starfive_hdmi.h | 295 ++++++
>> >>   drivers/gpu/drm/verisilicon/vs_drv.c        |   5 +
>> >>   drivers/gpu/drm/verisilicon/vs_drv.h        |   4 +
>> >>   6 files changed, 1261 insertions(+), 1 deletion(-)
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.c
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.h
>> >>
>> >> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
>> >> index 3a361f8c8..122c786e3 100644
>> >> --- a/drivers/gpu/drm/verisilicon/Kconfig
>> >> +++ b/drivers/gpu/drm/verisilicon/Kconfig
>> >> @@ -12,4 +12,10 @@ config DRM_VERISILICON
>> >>         setting and buffer management. It does not
>> >>         provide 2D or 3D acceleration.
>> >>   -
>> >> +config DRM_VERISILICON_STARFIVE_HDMI
>> >> +    bool "Starfive HDMI extensions"
>> >> +    depends on DRM_VERISILICON
>> >> +    help
>> >> +       This selects support for StarFive soc specific extensions
>> >> +       for the Innosilicon HDMI driver. If you want to enable
>> >> +       HDMI on JH7110 based soc, you should select this option.
>> >> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
>> >> index 1d48016ca..08350f25b 100644
>> >> --- a/drivers/gpu/drm/verisilicon/Makefile
>> >> +++ b/drivers/gpu/drm/verisilicon/Makefile
>> >> @@ -7,5 +7,6 @@ vs_drm-objs := vs_dc_hw.o \
>> >>           vs_modeset.o \
>> >>           vs_plane.o
>> >>   +vs_drm-$(CONFIG_DRM_VERISILICON_STARFIVE_HDMI) += starfive_hdmi.o
>> >>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>> >>   diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.c b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
>> >> new file mode 100644
>> >> index 000000000..d296c4b71
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
>> >> @@ -0,0 +1,949 @@
>> >> +// SPDX-License-Identifier: GPL-2.0-only
>> >> +/*
>> >> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
>> >> + */
>> >> +
>> >> +#include <linux/clk.h>
>> >> +#include <linux/component.h>
>> >> +#include <linux/delay.h>
>> >> +#include <linux/err.h>
>> >> +#include <linux/hdmi.h>
>> >> +#include <linux/i2c.h>
>> >> +#include <linux/irq.h>
>> >> +#include <linux/media-bus-format.h>
>> >> +#include <linux/mfd/syscon.h>
>> >> +#include <linux/module.h>
>> >> +#include <linux/mutex.h>
>> >> +#include <linux/of_device.h>
>> >> +#include <linux/pm_runtime.h>
>> >> +#include <linux/reset.h>
>> >> +
>> >> +#include <drm/bridge/dw_hdmi.h>
>> >> +#include <drm/drm_atomic_helper.h>
>> >> +#include <drm/drm_edid.h>
>> >> +#include <drm/drm_managed.h>
>> >> +#include <drm/drm_of.h>
>> >> +#include <drm/drm_probe_helper.h>
>> >> +#include <drm/drm_simple_kms_helper.h>
>> >> +
>> >> +#include "starfive_hdmi.h"
>> >> +#include "vs_drv.h"
>> >> +#include "vs_crtc.h"
>> >> +
>> >> +static struct starfive_hdmi *encoder_to_hdmi(struct drm_encoder *encoder)
>> >> +{
>> >> +    return container_of(encoder, struct starfive_hdmi, encoder);
>> >> +}
>> >> +
>> >> +static struct starfive_hdmi *connector_to_hdmi(struct drm_connector *connector)
>> >> +{
>> >> +    return container_of(connector, struct starfive_hdmi, connector);
>> >> +}
>> >> +
>> >> +struct starfive_hdmi_i2c {
>> >> +    struct i2c_adapter adap;
>> >> +
>> >> +    u8 ddc_addr;
>> >> +    u8 segment_addr;
>> >> +    /* protects the edid data when use i2c cmd to read edid */
>> >> +    struct mutex lock;
>> >> +    struct completion cmp;
>> >> +};
>> >> +
>> >> +static const struct pre_pll_config pre_pll_cfg_table[] = {
>> >> +    { 25175000,  25175000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0xf55555},
>> >> +    { 25200000,  25200000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0},
>> >> +    { 27000000,  27000000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0},
>> >
>> > Such data structures are usually pretyt limited and hard to handle. Could you please replace it with the runtime calculations of
>> >
>> >> +    { 27027000,  27027000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0x170a3d},
>> >> +    { 28320000,  28320000, 1,  28, 2, 1, 1,  3, 0, 3, 4, 0, 0x51eb85},
>> >> +    { 30240000,  30240000, 1,  30, 2, 1, 1,  3, 0, 3, 4, 0, 0x3d70a3},
>> >> +    { 31500000,  31500000, 1,  31, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> >> +    { 33750000,  33750000, 1,  33, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
>> >> +    { 36000000,  36000000, 1,  36, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    { 40000000,  40000000, 1,  80, 2, 2, 2, 12, 2, 2, 2, 0, 0},
>> >> +    { 46970000,  46970000, 1,  46, 2, 1, 1,  3, 0, 3, 4, 0, 0xf851eb},
>> >> +    { 49500000,  49500000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> >> +    { 49000000,  49000000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    { 50000000,  50000000, 1,  50, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    { 54000000,  54000000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    { 54054000,  54054000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0x0dd2f1},
>> >> +    { 57284000,  57284000, 1,  57, 2, 1, 1,  3, 0, 3, 4, 0, 0x48b439},
>> >> +    { 58230000,  58230000, 1,  58, 2, 1, 1,  3, 0, 3, 4, 0, 0x3ae147},
>> >> +    { 59341000,  59341000, 1,  59, 2, 1, 1,  3, 0, 3, 4, 0, 0x574bc6},
>> >> +    { 59400000,  59400000, 1,  99, 3, 1, 1,  1, 3, 3, 4, 0, 0},
>> >> +    { 65000000,  65000000, 1, 130, 2, 2, 2,  12, 0, 2, 2, 0, 0},
>> >> +    { 68250000,  68250000, 1, 68,  2, 1, 1,  3,  0, 3, 4, 0, 0x3fffff},
>> >> +    { 71000000,  71000000, 1,  71, 2, 1, 1,  3, 0, 3,  4, 0, 0},
>> >> +    { 74176000,  74176000, 1,  98, 1, 2, 2,  1, 2, 3, 4, 0, 0xe6ae6b},
>> >> +    { 74250000,  74250000, 1,  99, 1, 2, 2,  1, 2, 3, 4, 0, 0},
>> >> +    { 75000000,  75000000, 1,  75, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    { 78750000,  78750000, 1,  78, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
>> >> +    { 79500000,  79500000, 1,  79, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> >> +    { 83500000,  83500000, 2, 167, 2, 1, 1,  1, 0, 0,  6, 0, 0},
>> >> +    { 83500000, 104375000, 1, 104, 2, 1, 1,  1, 1, 0,  5, 0, 0x600000},
>> >> +    { 84858000,  84858000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xdba5e2},
>> >> +    { 85500000,  85500000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
>> >> +    { 85750000,  85750000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
>> >> +    { 85800000,  85800000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcccccc},
>> >> +    { 88750000,  88750000, 1,  88, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
>> >> +    { 89910000,  89910000, 1,  89, 2, 1, 1,  3, 0, 3, 4, 0, 0xe8f5c1},
>> >> +    { 90000000,  90000000, 1,  90, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {101000000, 101000000, 1, 101, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {102250000, 102250000, 1, 102, 2, 1, 1,  3, 0, 3, 4, 0, 0x3fffff},
>> >> +    {106500000, 106500000, 1, 106, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> >> +    {108000000, 108000000, 1,  90, 3, 0, 0,  5, 0, 2,  2, 0, 0},
>> >> +    {119000000, 119000000, 1, 119, 2, 1, 1,  3, 0, 3,  4, 0, 0},
>> >> +    {131481000, 131481000, 1,  131, 2, 1, 1,  3, 0, 3,  4, 0, 0x7b22d1},
>> >> +    {135000000, 135000000, 1,  135, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {136750000, 136750000, 1,  136, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
>> >> +    {147180000, 147180000, 1,  147, 2, 1, 1,  3, 0, 3, 4, 0, 0x2e147a},
>> >> +    {148352000, 148352000, 1,  98, 1, 1, 1,  1, 2, 2, 2, 0, 0xe6ae6b},
>> >> +    {148500000, 148500000, 1,  99, 1, 1, 1,  1, 2, 2, 2, 0, 0},
>> >> +    {154000000, 154000000, 1, 154, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {156000000, 156000000, 1, 156, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {157000000, 157000000, 1, 157, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {162000000, 162000000, 1, 162, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {174250000, 174250000, 1, 145, 3, 0, 0,  5, 0, 2, 2, 0, 0x355555},
>> >> +    {174500000, 174500000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> >> +    {174570000, 174570000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x91eb84},
>> >> +    {175500000, 175500000, 1, 175, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> >> +    {185590000, 185590000, 1, 185, 2, 1, 1,  3, 0, 3, 4, 0, 0x970a3c},
>> >> +    {187000000, 187000000, 1, 187, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {241500000, 241500000, 1, 161, 1, 1, 1,  4, 0, 2,  2, 0, 0},
>> >> +    {241700000, 241700000, 1, 241, 2, 1, 1,  3, 0, 3,  4, 0, 0xb33332},
>> >> +    {262750000, 262750000, 1, 262, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
>> >> +    {296500000, 296500000, 1, 296, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
>> >> +    {296703000, 296703000, 1,  98, 0, 1, 1,  1, 0, 2,  2, 0, 0xe6ae6b},
>> >> +    {297000000, 297000000, 1,  99, 0, 1, 1,  1, 0, 2,  2, 0, 0},
>> >> +    {594000000, 594000000, 1,  99, 0, 2, 0,  1, 0, 1,  1, 0, 0},
>> >> +    {0, 0, 0,  0, 0, 0, 0,  0, 0, 0,  0, 0, 0},
>> >> +};
>> >> +
>> >> +static const struct post_pll_config post_pll_cfg_table[] = {
>> >> +    {25200000,    1, 80, 13, 3, 1},
>> >> +    {27000000,    1, 40, 11, 3, 1},
>> >> +    {33750000,    1, 40, 11, 3, 1},
>> >> +    {49000000,    1, 20, 1, 3, 3},
>> >> +    {241700000, 1, 20, 1, 3, 3},
>> >> +    {297000000, 4, 20, 0, 0, 3},
>> >> +    {594000000, 4, 20, 0, 0, 0},
>> >> +    { /* sentinel */ }
>> >> +};
>> >> +
>> >> +inline u8 hdmi_readb(struct starfive_hdmi *hdmi, u16 offset)
>> >> +{
>> >> +    return readl_relaxed(hdmi->regs + (offset) * 0x04);
>> >> +}
>> >> +
>> >> +inline void hdmi_writeb(struct starfive_hdmi *hdmi, u16 offset, u32 val)
>> >> +{
>> >> +    writel_relaxed(val, hdmi->regs + (offset) * 0x04);
>> >> +}
>> >> +
>> >> +inline void hdmi_modb(struct starfive_hdmi *hdmi, u16 offset,
>> >> +                 u32 msk, u32 val)
>> >> +{
>> >> +    u8 temp = hdmi_readb(hdmi, offset) & ~msk;
>> >> +
>> >> +    temp |= val & msk;
>> >> +    hdmi_writeb(hdmi, offset, temp);
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_enable_clk_deassert_rst(struct device *dev, struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    int ret;
>> >> +
>> >> +    ret = clk_prepare_enable(hdmi->sys_clk);
>> >> +    if (ret) {
>> >> +        dev_err(dev, "Cannot enable HDMI sys clock: %d\n", ret);
>> >> +        return ret;
>> >> +    }
>> >> +
>> >> +    ret = clk_prepare_enable(hdmi->mclk);
>> >> +    if (ret) {
>> >> +        dev_err(dev, "Cannot enable HDMI mclk clock: %d\n", ret);
>> >> +        goto err_mclk;
>> >> +    }
>> >> +    ret = clk_prepare_enable(hdmi->bclk);
>> >
>> > This code begs to use clk_bulk instead.
>> >
>> >> +    if (ret) {
>> >> +        dev_err(dev, "Cannot enable HDMI bclk clock: %d\n", ret);
>> >> +        goto err_bclk;
>> >> +    }
>> >> +    ret = reset_control_deassert(hdmi->tx_rst);
>> >> +    if (ret < 0) {
>> >> +        dev_err(dev, "failed to deassert tx_rst\n");
>> >> +        goto err_rst;
>> >> +    }
>> >> +    return 0;
>> >> +
>> >> +err_rst:
>> >> +    clk_disable_unprepare(hdmi->bclk);
>> >> +err_bclk:
>> >> +    clk_disable_unprepare(hdmi->mclk);
>> >> +err_mclk:
>> >> +    clk_disable_unprepare(hdmi->sys_clk);
>> >> +    return ret;
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_disable_clk_assert_rst(struct device *dev, struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    int ret;
>> >> +
>> >> +    ret = reset_control_assert(hdmi->tx_rst);
>> >> +    if (ret < 0)
>> >> +        dev_err(dev, "failed to assert tx_rst\n");
>> >> +
>> >> +    clk_disable_unprepare(hdmi->sys_clk);
>> >> +    clk_disable_unprepare(hdmi->mclk);
>> >> +    clk_disable_unprepare(hdmi->bclk);
>> >> +}
>> >> +
>> >> +#ifdef CONFIG_PM_SLEEP
>> >> +static int hdmi_system_pm_suspend(struct device *dev)
>> >> +{
>> >> +    return pm_runtime_force_suspend(dev);
>> >> +}
>> >> +
>> >> +static int hdmi_system_pm_resume(struct device *dev)
>> >> +{
>> >> +    return pm_runtime_force_resume(dev);
>> >> +}
>> >> +#endif
>> >> +
>> >> +#ifdef CONFIG_PM
>> >> +static int hdmi_runtime_suspend(struct device *dev)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
>> >> +
>> >> +    starfive_hdmi_disable_clk_assert_rst(dev, hdmi);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int hdmi_runtime_resume(struct device *dev)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
>> >> +
>> >> +    return starfive_hdmi_enable_clk_deassert_rst(dev, hdmi);
>> >> +}
>> >> +#endif
>> >> +
>> >> +static void starfive_hdmi_tx_phy_power_down(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_tx_phy_power_on(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
>> >
>> > It looks like one can inline these two helpers w/o too much troubles.
>> >
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_config_pll(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    u32 val;
>> >> +    u8 reg_1ad_value = hdmi->post_cfg->post_div_en ?
>> >> +         hdmi->post_cfg->postdiv : 0x00;
>> >> +    u8 reg_1aa_value = hdmi->post_cfg->post_div_en ?
>> >> +         0x0e : 0x02;
>> >> +
>> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN);
>> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1,
>> >> +            STARFIVE_POST_PLL_POST_DIV_ENABLE |
>> >> +            STARFIVE_POST_PLL_REFCLK_SEL_TMDS |
>> >> +            STARFIVE_POST_PLL_POWER_DOWN);
>> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_1, STARFIVE_PRE_PLL_PRE_DIV(hdmi->pre_cfg->prediv));
>> >> +
>> >> +    val = STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE | STARFIVE_SPREAD_SPECTRUM_MOD_DOWN;
>> >> +    if (!hdmi->pre_cfg->fracdiv)
>> >> +        val |= STARFIVE_PRE_PLL_FRAC_DIV_DISABLE;
>> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_2,
>> >> +            STARFIVE_PRE_PLL_FB_DIV_11_8(hdmi->pre_cfg->fbdiv) | val);
>> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_3,
>> >> +            STARFIVE_PRE_PLL_FB_DIV_7_0(hdmi->pre_cfg->fbdiv));
>> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_4,
>> >> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_C(hdmi->pre_cfg->tmds_div_c) |
>> >> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_A(hdmi->pre_cfg->tmds_div_a) |
>> >> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_B(hdmi->pre_cfg->tmds_div_b));
>> >> +
>> >> +    if (hdmi->pre_cfg->fracdiv) {
>> >> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_L,
>> >> +                STARFIVE_PRE_PLL_FRAC_DIV_7_0(hdmi->pre_cfg->fracdiv));
>> >> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_M,
>> >> +                STARFIVE_PRE_PLL_FRAC_DIV_15_8(hdmi->pre_cfg->fracdiv));
>> >> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_H,
>> >> +                STARFIVE_PRE_PLL_FRAC_DIV_23_16(hdmi->pre_cfg->fracdiv));
>> >> +    }
>> >> +
>> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_5,
>> >> +            STARFIVE_PRE_PLL_PCLK_DIV_A(hdmi->pre_cfg->pclk_div_a) |
>> >> +            STARFIVE_PRE_PLL_PCLK_DIV_B(hdmi->pre_cfg->pclk_div_b));
>> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_6,
>> >> +            STARFIVE_PRE_PLL_PCLK_DIV_C(hdmi->pre_cfg->pclk_div_c) |
>> >> +            STARFIVE_PRE_PLL_PCLK_DIV_D(hdmi->pre_cfg->pclk_div_d));
>> >> +
>> >> +    /*pre-pll power down*/
>> >> +    hdmi_modb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN, 0);
>> >> +
>> >> +    hdmi_modb(hdmi, STARFIVE_POST_PLL_DIV_2, STARFIVE_POST_PLL_Pre_DIV_MASK,
>> >> +          STARFIVE_POST_PLL_PRE_DIV(hdmi->post_cfg->prediv));
>> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_3, hdmi->post_cfg->fbdiv & 0xff);
>> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_4, reg_1ad_value);
>> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1, reg_1aa_value);
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_tmds_driver_on(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    hdmi_modb(hdmi, STARFIVE_TMDS_CONTROL,
>> >> +          STARFIVE_TMDS_DRIVER_ENABLE, STARFIVE_TMDS_DRIVER_ENABLE);
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_sync_tmds(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    /*first send 0 to this bit, then send 1 and keep 1 into this bit*/
>> >> +    hdmi_writeb(hdmi, HDMI_SYNC, 0x0);
>> >> +    hdmi_writeb(hdmi, HDMI_SYNC, 0x1);
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_i2c_init(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    int ddc_bus_freq;
>> >> +
>> >> +    ddc_bus_freq = (clk_get_rate(hdmi->sys_clk) >> 2) / HDMI_SCL_RATE;
>> >> +
>> >> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
>> >> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
>> >> +
>> >> +    /* Clear the EDID interrupt flag and mute the interrupt */
>> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
>> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> >> +}
>> >> +
>> >> +static const
>> >> +struct pre_pll_config *starfive_hdmi_phy_get_pre_pll_cfg(struct starfive_hdmi *hdmi,
>> >> +                             unsigned long rate)
>> >> +{
>> >> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
>> >> +
>> >> +    rate = (rate / 1000) * 1000;
>> >> +    for (; cfg->pixclock != 0; cfg++)
>> >> +        if (cfg->tmdsclock == rate && cfg->pixclock == rate)
>> >> +            break;
>> >> +
>> >> +    if (cfg->pixclock == 0)
>> >> +        return ERR_PTR(-EINVAL);
>> >> +
>> >> +    return cfg;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_phy_clk_set_rate(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    hdmi->post_cfg = post_pll_cfg_table;
>> >> +
>> >> +    hdmi->pre_cfg = starfive_hdmi_phy_get_pre_pll_cfg(hdmi, hdmi->tmds_rate);
>> >> +    if (IS_ERR(hdmi->pre_cfg))
>> >> +        return PTR_ERR(hdmi->pre_cfg);
>> >> +
>> >> +    for (; hdmi->post_cfg->tmdsclock != 0; hdmi->post_cfg++)
>> >> +        if (hdmi->tmds_rate <= hdmi->post_cfg->tmdsclock)
>> >> +            break;
>> >> +
>> >> +    starfive_hdmi_config_pll(hdmi);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_config_video_timing(struct starfive_hdmi *hdmi,
>> >> +                         struct drm_display_mode *mode)
>> >> +{
>> >> +    int value;
>> >> +    /* Set detail external video timing */
>> >> +    value = mode->htotal;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
>> >
>> > As you have hdmi_writeb already, adding hdmi_writew will help here.
>> >
>> >> +
>> >> +    value = mode->htotal - mode->hdisplay;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
>> >> +
>> >> +    value = mode->htotal - mode->hsync_start;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
>> >> +
>> >> +    value = mode->hsync_end - mode->hsync_start;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
>> >> +
>> >> +    value = mode->vtotal;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
>> >> +
>> >> +    value = mode->vtotal - mode->vdisplay;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
>> >> +
>> >> +    value = mode->vtotal - mode->vsync_start;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
>> >> +
>> >> +    value = mode->vsync_end - mode->vsync_start;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
>> >> +
>> >> +    /* Set detail external video timing polarity and interlace mode */
>> >> +    value = v_EXTERANL_VIDEO(1);
>> >> +    value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
>> >> +        v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
>> >> +    value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
>> >> +        v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
>> >> +    value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
>> >> +        v_INETLACE(1) : v_INETLACE(0);
>> >> +
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_setup(struct starfive_hdmi *hdmi,
>> >> +                   struct drm_display_mode *mode)
>> >> +{
>> >> +    hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
>> >> +    hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
>> >> +    hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
>> >> +
>> >> +    hdmi->tmds_rate = mode->clock * 1000;
>> >> +    starfive_hdmi_phy_clk_set_rate(hdmi);
>> >> +
>> >> +    while (!(hdmi_readb(hdmi, STARFIVE_PRE_PLL_LOCK_STATUS) & 0x1))
>> >> +        continue;
>> >> +    while (!(hdmi_readb(hdmi, STARFIVE_POST_PLL_LOCK_STATUS) & 0x1))
>> >> +        continue;
>> >
>> > Please use read_iopoll_timeout here.
>> >
>> >> +
>> >> +    /*turn on LDO*/
>> >> +    hdmi_writeb(hdmi, STARFIVE_LDO_CONTROL, STARFIVE_LDO_ENABLE);
>> >> +    /*turn on serializer*/
>> >> +    hdmi_writeb(hdmi, STARFIVE_SERIALIER_CONTROL, STARFIVE_SERIALIER_ENABLE);
>> >> +
>> >> +    starfive_hdmi_tx_phy_power_down(hdmi);
>> >> +    starfive_hdmi_config_video_timing(hdmi, mode);
>> >> +    starfive_hdmi_tx_phy_power_on(hdmi);
>> >> +
>> >> +    starfive_hdmi_tmds_driver_on(hdmi);
>> >> +    starfive_hdmi_sync_tmds(hdmi);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_encoder_mode_set(struct drm_encoder *encoder,
>> >> +                       struct drm_display_mode *mode,
>> >> +                       struct drm_display_mode *adj_mode)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
>> >> +
>> >> +    drm_mode_copy(&hdmi->previous_mode, adj_mode);
>> >
>> > Why do you need it?
>> >
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_encoder_enable(struct drm_encoder *encoder)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
>> >> +    struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
>> >> +    int ret, idx;
>> >> +    struct drm_device *drm = hdmi->connector.dev;
>> >> +
>> >> +    if (drm && !drm_dev_enter(drm, &idx))
>> >> +        return;
>> >> +
>> >> +    ret = pm_runtime_get_sync(hdmi->dev);
>> >> +    if (ret < 0)
>> >> +        return;
>> >> +    mdelay(10);
>> >> +    starfive_hdmi_setup(hdmi, mode);
>> >> +
>> >> +    if (drm)
>> >> +        drm_dev_exit(idx);
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
>> >> +
>> >> +    int idx;
>> >> +    struct drm_device *drm = hdmi->connector.dev;
>> >> +
>> >> +    if (drm && !drm_dev_enter(drm, &idx))
>> >> +        return;
>> >> +
>> >> +    pm_runtime_put(hdmi->dev);
>> >> +
>> >> +    if (drm)
>> >> +        drm_dev_exit(idx);
>> >> +}
>> >> +
>> >> +static int
>> >> +starfive_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
>> >> +                   struct drm_crtc_state *crtc_state,
>> >> +                   struct drm_connector_state *conn_state)
>> >> +{
>> >> +    bool valid = false;
>> >> +    struct drm_display_mode *mode = &crtc_state->adjusted_mode;
>> >> +    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
>> >> +
>> >> +    vs_crtc_state->encoder_type = encoder->encoder_type;
>> >> +    vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
>> >> +
>> >> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
>> >> +    int pclk = mode->clock * 1000;
>> >> +
>> >> +    for (; cfg->pixclock != 0; cfg++) {
>> >> +        if (pclk == cfg->pixclock) {
>> >> +            if (pclk > 297000000)
>> >
>> > Magic value.
>> >
>> >> +                continue;
>> >> +
>> >> +            valid = true;
>> >> +            break;
>> >> +        }
>> >> +    }
>> >> +
>> >> +    return (valid) ? 0 : -EINVAL;
>> >> +}
>> >> +
>> >> +static const struct drm_encoder_helper_funcs starfive_hdmi_encoder_helper_funcs = {
>> >> +    .enable     = starfive_hdmi_encoder_enable,
>> >> +    .disable    = starfive_hdmi_encoder_disable,
>> >> +    .mode_set   = starfive_hdmi_encoder_mode_set,
>> >> +    .atomic_check = starfive_hdmi_encoder_atomic_check,
>> >> +};
>> >> +
>> >> +static enum drm_connector_status
>> >> +starfive_hdmi_connector_detect(struct drm_connector *connector, bool force)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
>> >> +    struct drm_device *drm = hdmi->connector.dev;
>> >> +    int ret;
>> >> +    int idx;
>> >> +
>> >> +    if (drm && !drm_dev_enter(drm, &idx))
>> >> +        return connector_status_disconnected;
>> >> +
>> >> +    ret = pm_runtime_get_sync(hdmi->dev);
>> >> +    if (ret < 0)
>> >> +        return ret;
>> >> +
>> >> +    ret = (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
>> >> +        connector_status_connected : connector_status_disconnected;
>> >> +    pm_runtime_put(hdmi->dev);
>> >> +
>> >> +    if (drm)
>> >> +        drm_dev_exit(idx);
>> >> +
>> >> +    return ret;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_connector_get_modes(struct drm_connector *connector)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
>> >> +    struct edid *edid;
>> >> +    int ret = 0;
>> >> +
>> >> +    if (!hdmi->ddc)
>> >> +        return 0;
>> >> +    ret = pm_runtime_get_sync(hdmi->dev);
>> >> +    if (ret < 0)
>> >> +        return ret;
>> >> +
>> >> +    edid = drm_get_edid(connector, hdmi->ddc);
>> >> +    if (edid) {
>> >> +        hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
>> >> +        hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
>> >> +        drm_connector_update_edid_property(connector, edid);
>> >> +        ret = drm_add_edid_modes(connector, edid);
>> >> +        kfree(edid);
>> >> +    }
>> >> +    pm_runtime_put(hdmi->dev);
>> >
>> > Can you use drm_connector_helper_get_modes_from_ddc() here? And then drm_display_info.is_hdmi and .has_audio.
>> >
>> >> +
>> >> +    return ret;
>> >> +}
>> >> +
>> >> +static enum drm_mode_status
>> >> +starfive_hdmi_connector_mode_valid(struct drm_connector *connector,
>> >> +                   struct drm_display_mode *mode)
>> >> +{
>> >> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
>> >> +    int pclk = mode->clock * 1000;
>> >> +    bool valid = false;
>> >> +
>> >> +    for (; cfg->pixclock != 0; cfg++) {
>> >> +        if (pclk == cfg->pixclock) {
>> >> +            if (pclk > 297000000)
>> >
>> > magic value
>> >
>> >> +                continue;
>> >> +
>> >> +            valid = true;
>> >> +            break;
>> >> +        }
>> >> +    }
>> >> +
>> >> +    return (valid) ? MODE_OK : MODE_BAD;
>> >> +}
>> >> +
>> >> +static int
>> >> +starfive_hdmi_probe_single_connector_modes(struct drm_connector *connector,
>> >> +                       u32 maxX, u32 maxY)
>> >> +{
>> >> +    return drm_helper_probe_single_connector_modes(connector, 3840, 2160);
>> >> +}
>> >> +
>> >> +static const struct drm_connector_funcs starfive_hdmi_connector_funcs = {
>> >> +    .fill_modes = starfive_hdmi_probe_single_connector_modes,
>> >> +    .detect = starfive_hdmi_connector_detect,
>> >> +    .reset = drm_atomic_helper_connector_reset,
>> >> +    .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
>> >> +    .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>> >> +};
>> >> +
>> >> +static struct drm_connector_helper_funcs starfive_hdmi_connector_helper_funcs = {
>> >> +    .get_modes = starfive_hdmi_connector_get_modes,
>> >> +    .mode_valid = starfive_hdmi_connector_mode_valid,
>> >> +};
>> >> +
>> >> +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    struct drm_encoder *encoder = &hdmi->encoder;
>> >> +    struct device *dev = hdmi->dev;
>> >> +
>> >> +    encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
>> >> +
>> >> +    /*
>> >> +     * If we failed to find the CRTC(s) which this encoder is
>> >> +     * supposed to be connected to, it's because the CRTC has
>> >> +     * not been registered yet.  Defer probing, and hope that
>> >> +     * the required CRTC is added later.
>> >> +     */
>> >> +    if (encoder->possible_crtcs == 0)
>> >> +        return -EPROBE_DEFER;
>> >> +
>> >> +    drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
>> >> +
>> >> +    hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
>> >> +
>> >> +    drm_connector_helper_add(&hdmi->connector,
>> >> +                 &starfive_hdmi_connector_helper_funcs);
>> >> +    drmm_connector_init(drm, &hdmi->connector,
>> >> +                &starfive_hdmi_connector_funcs,
>> >> +                DRM_MODE_CONNECTOR_HDMIA,
>> >
>> > On an embedded device one can not be so sure. There can be MHL or HDMI Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.
>> >
>> >> +                hdmi->ddc);
>> >> +
>> >> +    drm_connector_attach_encoder(&hdmi->connector, encoder);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static irqreturn_t starfive_hdmi_i2c_irq(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    struct starfive_hdmi_i2c *i2c = hdmi->i2c;
>> >> +    u8 stat;
>> >> +
>> >> +    stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
>> >> +    if (!(stat & m_INT_EDID_READY))
>> >> +        return IRQ_NONE;
>> >> +
>> >> +    /* Clear HDMI EDID interrupt flag */
>> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> >> +
>> >> +    complete(&i2c->cmp);
>> >> +
>> >> +    return IRQ_HANDLED;
>> >> +}
>> >> +
>> >> +static irqreturn_t starfive_hdmi_hardirq(int irq, void *dev_id)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = dev_id;
>> >> +    irqreturn_t ret = IRQ_NONE;
>> >> +    u8 interrupt;
>> >> +
>> >> +    if (hdmi->i2c)
>> >> +        ret = starfive_hdmi_i2c_irq(hdmi);
>> >> +
>> >> +    interrupt = hdmi_readb(hdmi, HDMI_STATUS);
>> >> +    if (interrupt & m_INT_HOTPLUG) {
>> >> +        hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
>> >> +        ret = IRQ_WAKE_THREAD;
>> >> +    }
>> >> +
>> >> +    return ret;
>> >> +}
>> >> +
>> >> +static irqreturn_t starfive_hdmi_irq(int irq, void *dev_id)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = dev_id;
>> >> +
>> >> +    drm_connector_helper_hpd_irq_event(&hdmi->connector);
>> >> +
>> >> +    return IRQ_HANDLED;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_i2c_read(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
>> >> +{
>> >> +    int length = msgs->len;
>> >> +    u8 *buf = msgs->buf;
>> >> +    int ret;
>> >> +
>> >> +    ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10);
>> >
>> > read_poll_timeout
>> Hi Dmitry:
>> I think using wait_for_completion_timeout here is a bit clearer than read_poll_timeout logic
>> I2c_read has hardware interrupt dependency. so need wait the interrupt completion.
>> also it comes with timeout detection.
> 
> Hi,
> 
> Yes, maybe I was thinking about a different piece of code here.
> wait_for_completion looks good here, indeed.
> 
> But this actually brings up another question. You reinit_comletion in
> starfive_hdmi_i2c_write() and then wait_for_completion in
> starfive_hdmi_i2c_read(). How does that work?
> 
> 
struct i2c_msg msgs[2] = {
        {
            .addr = DDC_ADDR,
            .flags = 0,
            .len = 1,
            .buf = (void *)&start,
        },{
            .addr = DDC_ADDR,
            .flags = I2C_M_RD,
            .len = DATA_LEN,
            .buf = buf,
        }
    };

For i2c devices, when reading edid information, the master device needs to send two msg to the slave device. 
The first msg contains the write operation flag(0), the written data length (start length) and the register address that needs to be read (start addr) ; 
The second msg contains the required read operation (I2C_M_RD), the length of the read data (equal to the length of buf) and the address (buf) where the data is read.

the first msg call starfive_hdmi_i2c_write 
and the second call starfive_hdmi_i2c_read

after the first msg send done , A valid interrupt will be generated (complete) ,then  the second msg will continue to read(wait_for_completion)
so that is the reason why nend do the reinit_comletion in starfive_hdmi_i2c_write.
if not do that , the second msg maybe read the edid at a wrong time.

This is my understanding. If there is anything wrong, I hope you will be corrected.

Thanks
> 
>>
>> >
>> >> +    if (!ret)
>> >> +        return -EAGAIN;
>> >> +
>> >> +    while (length--)
>> >> +        *buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_i2c_write(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
>> >> +{
>> >> +    /*
>> >> +     * The DDC module only support read EDID message, so
>> >> +     * we assume that each word write to this i2c adapter
>> >> +     * should be the offset of EDID word address.
>> >> +     */
>> >> +    if (msgs->len != 1 ||
>> >> +        (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR))
>> >> +        return -EINVAL;
>> >> +
>> >> +    reinit_completion(&hdmi->i2c->cmp);
>> >> +
>> >> +    if (msgs->addr == DDC_SEGMENT_ADDR)
>> >> +        hdmi->i2c->segment_addr = msgs->buf[0];
>> >> +    if (msgs->addr == DDC_ADDR)
>> >> +        hdmi->i2c->ddc_addr = msgs->buf[0];
>> >> +
>> >> +    /* Set edid fifo first addr */
>> >> +    hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
>> >> +
>> >> +    /* Set edid word address 0x00/0x80 */
>> >> +    hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
>> >> +
>> >> +    /* Set edid segment pointer */
>> >> +    hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_i2c_xfer(struct i2c_adapter *adap,
>> >> +                  struct i2c_msg *msgs, int num)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = i2c_get_adapdata(adap);
>> >> +    struct starfive_hdmi_i2c *i2c = hdmi->i2c;
>> >> +    int i, ret = 0;
>> >> +
>> >> +    mutex_lock(&i2c->lock);
>> >> +
>> >> +    /* Clear the EDID interrupt flag and unmute the interrupt */
>> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
>> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> >> +
>> >> +    for (i = 0; i < num; i++) {
>> >> +        DRM_DEV_DEBUG(hdmi->dev,
>> >> +                  "xfer: num: %d/%d, len: %d, flags: %#x\n",
>> >> +                  i + 1, num, msgs[i].len, msgs[i].flags);
>> >> +
>> >> +        if (msgs[i].flags & I2C_M_RD)
>> >> +            ret = starfive_hdmi_i2c_read(hdmi, &msgs[i]);
>> >> +        else
>> >> +            ret = starfive_hdmi_i2c_write(hdmi, &msgs[i]);
>> >> +
>> >> +        if (ret < 0)
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    if (!ret)
>> >> +        ret = num;
>> >> +
>> >> +    /* Mute HDMI EDID interrupt */
>> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
>> >> +
>> >> +    mutex_unlock(&i2c->lock);
>> >> +
>> >> +    return ret;
>> >> +}
>> >> +
>> >> +static u32 starfive_hdmi_i2c_func(struct i2c_adapter *adapter)
>> >> +{
>> >> +    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
>> >> +}
>> >> +
>> >> +static const struct i2c_algorithm starfive_hdmi_algorithm = {
>> >> +    .master_xfer    = starfive_hdmi_i2c_xfer,
>> >> +    .functionality    = starfive_hdmi_i2c_func,
>> >> +};
>> >> +
>> >> +static struct i2c_adapter *starfive_hdmi_i2c_adapter(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    struct i2c_adapter *adap;
>> >> +    struct starfive_hdmi_i2c *i2c;
>> >> +    int ret;
>> >> +
>> >> +    i2c = drmm_kzalloc(hdmi->drm_dev, sizeof(*i2c), GFP_KERNEL);
>> >> +    if (!i2c)
>> >> +        return ERR_PTR(-ENOMEM);
>> >> +
>> >> +    mutex_init(&i2c->lock);
>> >> +    init_completion(&i2c->cmp);
>> >> +
>> >> +    adap = &i2c->adap;
>> >> +    adap->class = I2C_CLASS_DDC;
>> >> +    adap->owner = THIS_MODULE;
>> >> +    adap->dev.parent = hdmi->dev;
>> >> +    adap->algo = &starfive_hdmi_algorithm;
>> >> +    strscpy(adap->name, "Starfive HDMI", sizeof(adap->name));
>> >> +    i2c_set_adapdata(adap, hdmi);
>> >> +
>> >> +    ret = devm_i2c_add_adapter(hdmi->dev, adap);
>> >> +    if (ret) {
>> >> +        drm_warn(hdmi->drm_dev, "cannot add %s I2C adapter\n", adap->name);
>> >> +        return ERR_PTR(ret);
>> >> +    }
>> >> +
>> >> +    hdmi->i2c = i2c;
>> >> +
>> >> +    drm_info(hdmi->drm_dev, "registered %s I2C bus driver success\n", adap->name);
>> >> +
>> >> +    return adap;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_get_clk_rst(struct device *dev, struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    hdmi->sys_clk = devm_clk_get(dev, "sysclk");
>> >> +    if (IS_ERR(hdmi->sys_clk)) {
>> >> +        dev_err(dev, "Unable to get HDMI sysclk clk\n");
>> >> +        return PTR_ERR(hdmi->sys_clk);
>> >> +    }
>> >> +    hdmi->mclk = devm_clk_get(dev, "mclk");
>> >> +    if (IS_ERR(hdmi->mclk)) {
>> >> +        dev_err(dev, "Unable to get HDMI mclk clk\n");
>> >> +        return PTR_ERR(hdmi->mclk);
>> >> +    }
>> >> +    hdmi->bclk = devm_clk_get(dev, "bclk");
>> >> +    if (IS_ERR(hdmi->bclk)) {
>> >> +        dev_err(dev, "Unable to get HDMI bclk clk\n");
>> >> +        return PTR_ERR(hdmi->bclk);
>> >> +    }
>> >> +    hdmi->tx_rst = devm_reset_control_get_by_index(dev, 0);
>> >> +    if (IS_ERR(hdmi->tx_rst)) {
>> >> +        dev_err(dev, "failed to get tx_rst reset\n");
>> >> +        return PTR_ERR(hdmi->tx_rst);
>> >> +    }
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_bind(struct device *dev, struct device *master,
>> >> +                  void *data)
>> >> +{
>> >> +    struct platform_device *pdev = to_platform_device(dev);
>> >> +    struct drm_device *drm = dev_get_drvdata(master);
>> >> +    struct starfive_hdmi *hdmi;
>> >> +    struct resource *iores;
>> >> +    int irq;
>> >> +    int ret;
>> >> +
>> >> +    hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
>> >> +    if (!hdmi)
>> >> +        return -ENOMEM;
>> >> +
>> >> +    hdmi = drmm_simple_encoder_alloc(drm, struct starfive_hdmi,
>> >> +                     encoder, DRM_MODE_ENCODER_TMDS);
>> >> +
>> >> +    hdmi->dev = dev;
>> >> +    hdmi->drm_dev = drm;
>> >> +    dev_set_drvdata(dev, hdmi);
>> >> +
>> >> +    iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> >> +    hdmi->regs = devm_ioremap_resource(dev, iores);
>> >
>> > This should go to probe
>> >
>> >> +    if (IS_ERR(hdmi->regs))
>> >> +        return PTR_ERR(hdmi->regs);
>> >> +
>> >> +    ret = starfive_hdmi_get_clk_rst(dev, hdmi);
>> >> +    if (ret < 0)
>> >> +        return ret;
>> >
>> > And this
>> >
>> >> +
>> >> +    ret = devm_pm_runtime_enable(dev);
>> >> +    if (ret)
>> >> +        return ret;
>> >> +
>> >> +    ret = pm_runtime_resume_and_get(dev);
>> >> +    if (ret)
>> >> +        return ret;
>> >> +
>> >> +    irq = platform_get_irq(pdev, 0);
>> >
>> > And this
>> >
>> >> +    if (irq < 0) {
>> >> +        ret = irq;
>> >> +        goto err_put_runtime_pm;
>> >> +    }
>> >> +
>> >> +    hdmi->ddc = starfive_hdmi_i2c_adapter(hdmi);
>> >> +    if (IS_ERR(hdmi->ddc)) {
>> >> +        ret = PTR_ERR(hdmi->ddc);
>> >> +        hdmi->ddc = NULL;
>> >> +        goto err_put_runtime_pm;
>> >> +    }
>> >> +
>> >> +    starfive_hdmi_i2c_init(hdmi);
>> >> +
>> >> +    ret = starfive_hdmi_register(drm, hdmi);
>> >> +    if (ret)
>> >> +        goto err_put_adapter;
>> >> +
>> >> +    /* Unmute hotplug interrupt */
>> >> +    hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
>> >> +
>> >> +    ret = devm_request_threaded_irq(dev, irq, starfive_hdmi_hardirq,
>> >> +                    starfive_hdmi_irq, IRQF_SHARED,
>> >> +                    dev_name(dev), hdmi);
>> >> +    if (ret < 0)
>> >> +        goto err_put_adapter;
>> >> +
>> >> +    pm_runtime_put_sync(dev);
>> >> +
>> >> +    return 0;
>> >> +
>> >> +err_put_adapter:
>> >> +    i2c_put_adapter(hdmi->ddc);
>> >> +err_put_runtime_pm:
>> >> +    pm_runtime_put_sync(dev);
>> >> +
>> >> +    return ret;
>> >> +}
>> >> +
>> >> +static const struct component_ops starfive_hdmi_ops = {
>> >> +    .bind    = starfive_hdmi_bind,
>> >> +};
>> >> +
>> >> +static int starfive_hdmi_probe(struct platform_device *pdev)
>> >> +{
>> >> +    return component_add(&pdev->dev, &starfive_hdmi_ops);
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_remove(struct platform_device *pdev)
>> >> +{
>> >> +    component_del(&pdev->dev, &starfive_hdmi_ops);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static const struct dev_pm_ops hdmi_pm_ops = {
>> >> +    SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
>> >> +    SET_LATE_SYSTEM_SLEEP_PM_OPS(hdmi_system_pm_suspend, hdmi_system_pm_resume)
>> >> +};
>> >> +
>> >> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
>> >> +    { .compatible = "starfive,jh7110-inno-hdmi",},
>> >> +    {},
>> >> +};
>> >> +MODULE_DEVICE_TABLE(of, starfive_hdmi_dt_ids);
>> >> +
>> >> +struct platform_driver starfive_hdmi_driver = {
>> >> +    .probe  = starfive_hdmi_probe,
>> >> +    .remove = starfive_hdmi_remove,
>> >> +    .driver = {
>> >> +        .name = "starfive-hdmi",
>> >> +        .of_match_table = starfive_hdmi_dt_ids,
>> >> +        .pm = &hdmi_pm_ops,
>> >> +    },
>> >> +};
>> >> +
>> >> +MODULE_AUTHOR("StarFive Corporation");
>> >> +MODULE_DESCRIPTION("Starfive HDMI Driver");
>> >> diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.h b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
>> >> new file mode 100644
>> >> index 000000000..1b9a11bca
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
>> >> @@ -0,0 +1,295 @@
>> >> +/* SPDX-License-Identifier: GPL-2.0-only */
>> >> +/*
>> >> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
>> >> + */
>> >> +
>> >> +#ifndef __STARFIVE_HDMI_H__
>> >> +#define __STARFIVE_HDMI_H__
>> >> +
>> >> +#include <drm/bridge/dw_hdmi.h>
>> >> +#include <drm/drm_atomic_helper.h>
>> >> +#include <drm/drm_edid.h>
>> >> +#include <drm/drm_of.h>
>> >> +#include <drm/drm_probe_helper.h>
>> >> +#include <drm/drm_simple_kms_helper.h>
>> >> +#include <linux/bitfield.h>
>> >> +#include <linux/bits.h>
>> >> +
>> >> +#define DDC_SEGMENT_ADDR        0x30
>> >> +
>> >> +#define HDMI_SCL_RATE            (100 * 1000)
>> >> +#define DDC_BUS_FREQ_L            0x4b
>> >> +#define DDC_BUS_FREQ_H            0x4c
>> >> +
>> >> +#define HDMI_SYS_CTRL            0x00
>> >> +#define m_RST_ANALOG            BIT(6)
>> >> +#define v_RST_ANALOG            0
>> >> +#define v_NOT_RST_ANALOG        BIT(6)
>> >> +#define m_RST_DIGITAL            BIT(5)
>> >> +#define v_RST_DIGITAL            0
>> >> +#define v_NOT_RST_DIGITAL        BIT(5)
>> >> +#define m_REG_CLK_INV            BIT(4)
>> >> +#define v_REG_CLK_NOT_INV        0
>> >> +#define v_REG_CLK_INV            BIT(4)
>> >> +#define m_VCLK_INV            BIT(3)
>> >> +#define v_VCLK_NOT_INV            0
>> >> +#define v_VCLK_INV            BIT(3)
>> >> +#define m_REG_CLK_SOURCE        BIT(2)
>> >> +#define v_REG_CLK_SOURCE_TMDS        0
>> >> +#define v_REG_CLK_SOURCE_SYS        BIT(2)
>> >> +#define m_POWER                BIT(1)
>> >> +#define v_PWR_ON            0
>> >> +#define v_PWR_OFF            BIT(1)
>> >> +#define m_INT_POL            BIT(0)
>> >> +#define v_INT_POL_HIGH            1
>> >> +#define v_INT_POL_LOW            0
>> >> +
>> >> +#define HDMI_AV_MUTE            0x05
>> >> +#define m_AVMUTE_CLEAR            BIT(7)
>> >> +#define m_AVMUTE_ENABLE            BIT(6)
>> >> +#define m_AUDIO_MUTE            BIT(1)
>> >> +#define m_VIDEO_BLACK            BIT(0)
>> >> +#define v_AVMUTE_CLEAR(n)        ((n) << 7)
>> >> +#define v_AVMUTE_ENABLE(n)        ((n) << 6)
>> >> +#define v_AUDIO_MUTE(n)            ((n) << 1)
>> >> +#define v_VIDEO_MUTE(n)            ((n) << 0)
>> >> +
>> >> +#define HDMI_VIDEO_TIMING_CTL        0x08
>> >> +#define v_VSYNC_POLARITY(n)        ((n) << 3)
>> >> +#define v_HSYNC_POLARITY(n)        ((n) << 2)
>> >> +#define v_INETLACE(n)            ((n) << 1)
>> >> +#define v_EXTERANL_VIDEO(n)        ((n) << 0)
>> >> +
>> >> +#define HDMI_VIDEO_EXT_HTOTAL_L        0x09
>> >> +#define HDMI_VIDEO_EXT_HTOTAL_H        0x0a
>> >> +#define HDMI_VIDEO_EXT_HBLANK_L        0x0b
>> >> +#define HDMI_VIDEO_EXT_HBLANK_H        0x0c
>> >> +#define HDMI_VIDEO_EXT_HDELAY_L        0x0d
>> >> +#define HDMI_VIDEO_EXT_HDELAY_H        0x0e
>> >> +#define HDMI_VIDEO_EXT_HDURATION_L    0x0f
>> >> +#define HDMI_VIDEO_EXT_HDURATION_H    0x10
>> >> +#define HDMI_VIDEO_EXT_VTOTAL_L        0x11
>> >> +#define HDMI_VIDEO_EXT_VTOTAL_H        0x12
>> >> +#define HDMI_VIDEO_EXT_VBLANK        0x13
>> >> +#define HDMI_VIDEO_EXT_VDELAY        0x14
>> >> +#define HDMI_VIDEO_EXT_VDURATION    0x15
>> >> +
>> >> +#define HDMI_EDID_SEGMENT_POINTER    0x4d
>> >> +#define HDMI_EDID_WORD_ADDR        0x4e
>> >> +#define HDMI_EDID_FIFO_OFFSET        0x4f
>> >> +#define HDMI_EDID_FIFO_ADDR        0x50
>> >> +
>> >> +#define HDMI_INTERRUPT_MASK1        0xc0
>> >> +#define HDMI_INTERRUPT_STATUS1        0xc1
>> >> +#define    m_INT_ACTIVE_VSYNC        BIT(5)
>> >> +#define m_INT_EDID_READY        BIT(2)
>> >> +
>> >> +#define HDMI_STATUS            0xc8
>> >> +#define m_HOTPLUG            BIT(7)
>> >> +#define m_MASK_INT_HOTPLUG        BIT(5)
>> >> +#define m_INT_HOTPLUG            BIT(1)
>> >> +#define v_MASK_INT_HOTPLUG(n)        (((n) & 0x1) << 5)
>> >
>> > It is untypical to have field defines which start with lowercase letter.
>> >
>> >> +
>> >> +#define HDMI_SYNC                    0xce
>> >> +
>> >> +#define UPDATE(x, h, l)                    FIELD_PREP(GENMASK(h, l), x)
>> >> +
>> >> +/* REG: 0x1a0 */
>> >> +#define STARFIVE_PRE_PLL_CONTROL            0x1a0
>> >> +#define STARFIVE_PCLK_VCO_DIV_5_MASK            BIT(1)
>> >> +#define STARFIVE_PCLK_VCO_DIV_5(x)            UPDATE(x, 1, 1)
>> >> +#define STARFIVE_PRE_PLL_POWER_DOWN            BIT(0)
>> >> +
>> >> +/* REG: 0x1a1 */
>> >> +#define STARFIVE_PRE_PLL_DIV_1                0x1a1
>> >> +#define STARFIVE_PRE_PLL_PRE_DIV_MASK            GENMASK(5, 0)
>> >> +#define STARFIVE_PRE_PLL_PRE_DIV(x)            UPDATE(x, 5, 0)
>> >> +
>> >> +/* REG: 0x1a2 */
>> >> +#define STARFIVE_PRE_PLL_DIV_2                0x1a2
>> >> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DOWN        BIT(7)
>> >> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE        BIT(6)
>> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_DISABLE        UPDATE(3, 5, 4)
>> >> +#define STARFIVE_PRE_PLL_FB_DIV_11_8_MASK        GENMASK(3, 0)
>> >> +#define STARFIVE_PRE_PLL_FB_DIV_11_8(x)            UPDATE((x) >> 8, 3, 0)
>> >> +
>> >> +/* REG: 0x1a3 */
>> >> +#define STARFIVE_PRE_PLL_DIV_3                0x1a3
>> >> +#define STARFIVE_PRE_PLL_FB_DIV_7_0(x)            UPDATE(x, 7, 0)
>> >> +
>> >> +/* REG: 0x1a4*/
>> >> +#define STARFIVE_PRE_PLL_DIV_4                0x1a4
>> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C_MASK        GENMASK(1, 0)
>> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C(x)        UPDATE(x, 1, 0)
>> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B_MASK        GENMASK(3, 2)
>> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B(x)        UPDATE(x, 3, 2)
>> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A_MASK        GENMASK(5, 4)
>> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A(x)        UPDATE(x, 5, 4)
>> >> +
>> >> +/* REG: 0x1a5 */
>> >> +#define STARFIVE_PRE_PLL_DIV_5                0x1a5
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_SHIFT        5
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_MASK        GENMASK(6, 5)
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_B(x)            UPDATE(x, 6, 5)
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_A_MASK        GENMASK(4, 0)
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_A(x)            UPDATE(x, 4, 0)
>> >> +
>> >> +/* REG: 0x1a6 */
>> >> +#define STARFIVE_PRE_PLL_DIV_6                0x1a6
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_SHIFT        5
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_MASK        GENMASK(6, 5)
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_C(x)            UPDATE(x, 6, 5)
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_D_MASK        GENMASK(4, 0)
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_D(x)            UPDATE(x, 4, 0)
>> >> +
>> >> +/* REG: 0x1a9 */
>> >> +#define STARFIVE_PRE_PLL_LOCK_STATUS            0x1a9
>> >> +
>> >> +/* REG: 0x1aa */
>> >> +#define STARFIVE_POST_PLL_DIV_1                0x1aa
>> >> +#define STARFIVE_POST_PLL_POST_DIV_ENABLE        GENMASK(3, 2)
>> >> +#define STARFIVE_POST_PLL_REFCLK_SEL_TMDS        BIT(1)
>> >> +#define STARFIVE_POST_PLL_POWER_DOWN            BIT(0)
>> >> +#define STARFIVE_POST_PLL_FB_DIV_8(x)            UPDATE(((x) >> 8) << 4, 4, 4)
>> >> +
>> >> +/* REG:0x1ab */
>> >> +#define STARFIVE_POST_PLL_DIV_2                0x1ab
>> >> +#define STARFIVE_POST_PLL_Pre_DIV_MASK            GENMASK(5, 0)
>> >> +#define STARFIVE_POST_PLL_PRE_DIV(x)            UPDATE(x, 5, 0)
>> >> +
>> >> +/* REG: 0x1ac */
>> >> +#define STARFIVE_POST_PLL_DIV_3                0x1ac
>> >> +#define STARFIVE_POST_PLL_FB_DIV_7_0(x)            UPDATE(x, 7, 0)
>> >> +
>> >> +/* REG: 0x1ad */
>> >> +#define STARFIVE_POST_PLL_DIV_4                0x1ad
>> >> +#define STARFIVE_POST_PLL_POST_DIV_MASK            GENMASK(2, 0)
>> >> +#define STARFIVE_POST_PLL_POST_DIV_2            0x0
>> >> +#define STARFIVE_POST_PLL_POST_DIV_4            0x1
>> >> +#define STARFIVE_POST_PLL_POST_DIV_8            0x3
>> >> +
>> >> +/* REG: 0x1af */
>> >> +#define STARFIVE_POST_PLL_LOCK_STATUS            0x1af
>> >> +
>> >> +/* REG: 0x1b0 */
>> >> +#define STARFIVE_BIAS_CONTROL                0x1b0
>> >> +#define STARFIVE_BIAS_ENABLE                BIT(2)
>> >> +
>> >> +/* REG: 0x1b2 */
>> >> +#define STARFIVE_TMDS_CONTROL                0x1b2
>> >> +#define STARFIVE_TMDS_CLK_DRIVER_EN            BIT(3)
>> >> +#define STARFIVE_TMDS_D2_DRIVER_EN            BIT(2)
>> >> +#define STARFIVE_TMDS_D1_DRIVER_EN            BIT(1)
>> >> +#define STARFIVE_TMDS_D0_DRIVER_EN            BIT(0)
>> >> +#define STARFIVE_TMDS_DRIVER_ENABLE            (STARFIVE_TMDS_CLK_DRIVER_EN | \
>> >> +                             STARFIVE_TMDS_D2_DRIVER_EN | \
>> >> +                             STARFIVE_TMDS_D1_DRIVER_EN | \
>> >> +                             STARFIVE_TMDS_D0_DRIVER_EN)
>> >> +
>> >> +/* REG: 0x1b4 */
>> >> +#define STARFIVE_LDO_CONTROL                0x1b4
>> >> +#define STARFIVE_LDO_D2_EN                BIT(2)
>> >> +#define STARFIVE_LDO_D1_EN                BIT(1)
>> >> +#define STARFIVE_LDO_D0_EN                BIT(0)
>> >> +#define STARFIVE_LDO_ENABLE                (STARFIVE_LDO_D2_EN | \
>> >> +                             STARFIVE_LDO_D1_EN | \
>> >> +                             STARFIVE_LDO_D0_EN)
>> >> +
>> >> +/* REG: 0x1be */
>> >> +#define STARFIVE_SERIALIER_CONTROL            0x1be
>> >> +#define STARFIVE_SERIALIER_D2_EN            BIT(6)
>> >> +#define STARFIVE_SERIALIER_D1_EN            BIT(5)
>> >> +#define STARFIVE_SERIALIER_D0_EN            BIT(4)
>> >> +#define STARFIVE_SERIALIER_EN                BIT(0)
>> >> +
>> >> +#define STARFIVE_SERIALIER_ENABLE            (STARFIVE_SERIALIER_D2_EN | \
>> >> +                             STARFIVE_SERIALIER_D1_EN | \
>> >> +                             STARFIVE_SERIALIER_D0_EN | \
>> >> +                             STARFIVE_SERIALIER_EN)
>> >> +
>> >> +/* REG: 0x1cc */
>> >> +#define STARFIVE_RX_CONTROL                0x1cc
>> >> +#define STARFIVE_RX_EN                    BIT(3)
>> >> +#define STARFIVE_RX_CHANNEL_2_EN            BIT(2)
>> >> +#define STARFIVE_RX_CHANNEL_1_EN            BIT(1)
>> >> +#define STARFIVE_RX_CHANNEL_0_EN            BIT(0)
>> >> +#define STARFIVE_RX_ENABLE                (STARFIVE_RX_EN | \
>> >> +                             STARFIVE_RX_CHANNEL_2_EN | \
>> >> +                             STARFIVE_RX_CHANNEL_1_EN | \
>> >> +                             STARFIVE_RX_CHANNEL_0_EN)
>> >> +
>> >> +/* REG: 0x1d1 */
>> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_H            0x1d1
>> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_23_16(x)        UPDATE((x) >> 16, 7, 0)
>> >> +/* REG: 0x1d2 */
>> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_M            0x1d2
>> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_15_8(x)        UPDATE((x) >> 8, 7, 0)
>> >> +/* REG: 0x1d3 */
>> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_L            0x1d3
>> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_7_0(x)        UPDATE(x, 7, 0)
>> >> +
>> >> +struct pre_pll_config {
>> >> +    unsigned long pixclock;
>> >> +    unsigned long tmdsclock;
>> >> +    u8 prediv;
>> >> +    u16 fbdiv;
>> >> +    u8 tmds_div_a;
>> >> +    u8 tmds_div_b;
>> >> +    u8 tmds_div_c;
>> >> +    u8 pclk_div_a;
>> >> +    u8 pclk_div_b;
>> >> +    u8 pclk_div_c;
>> >> +    u8 pclk_div_d;
>> >> +    u8 vco_div_5_en;
>> >> +    u32 fracdiv;
>> >> +};
>> >> +
>> >> +struct post_pll_config {
>> >> +    unsigned long tmdsclock;
>> >> +    u8 prediv;
>> >> +    u16 fbdiv;
>> >> +    u8 postdiv;
>> >> +    u8 post_div_en;
>> >> +    u8 version;
>> >> +};
>> >> +
>> >> +struct phy_config {
>> >> +    unsigned long    tmdsclock;
>> >> +    u8        regs[14];
>> >> +};
>> >> +
>> >> +struct hdmi_data_info {
>> >> +    int vic;
>> >> +    bool sink_is_hdmi;
>> >> +    bool sink_has_audio;
>> >> +    unsigned int enc_in_format;
>> >> +    unsigned int enc_out_format;
>> >> +    unsigned int colorimetry;
>> >> +};
>> >> +
>> >> +struct starfive_hdmi {
>> >> +    struct device *dev;
>> >> +    struct drm_device *drm_dev;
>> >> +    struct drm_encoder    encoder;
>> >> +    struct drm_connector    connector;
>> >> +
>> >> +    struct starfive_hdmi_i2c *i2c;
>> >> +
>> >> +    int irq;
>> >> +    struct clk *sys_clk;
>> >> +    struct clk *mclk;
>> >> +    struct clk *bclk;
>> >> +    struct reset_control *tx_rst;
>> >> +    void __iomem *regs;
>> >> +
>> >> +    struct i2c_adapter *ddc;
>> >> +
>> >> +    unsigned long tmds_rate;
>> >> +
>> >> +    struct hdmi_data_info    hdmi_data;
>> >> +    struct drm_display_mode previous_mode;
>> >> +    const struct pre_pll_config    *pre_cfg;
>> >> +    const struct post_pll_config    *post_cfg;
>> >> +};
>> >> +
>> >> +#endif /* __STARFIVE_HDMI_H__ */
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
>> >> index 3cd533cfa..9669354bd 100644
>> >> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
>> >> @@ -126,6 +126,11 @@ static const struct component_master_ops vs_drm_ops = {
>> >>   static struct platform_driver *drm_sub_drivers[] = {
>> >>       &dc_platform_driver,
>> >>   +    /* connector + encoder*/
>> >> +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
>> >> +    &starfive_hdmi_driver,
>> >> +#endif
>> >> +
>> >>   };
>> >>     static struct component_match *vs_drm_match_add(struct device *dev)
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
>> >> index 369ad22d6..e90723419 100644
>> >> --- a/drivers/gpu/drm/verisilicon/vs_drv.h
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_drv.h
>> >> @@ -24,4 +24,8 @@ to_vs_drm_private(const struct drm_device *dev)
>> >>       return container_of(dev, struct vs_drm_device, base);
>> >>   }
>> >>   +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
>> >> +extern struct platform_driver starfive_hdmi_driver;
>> >> +#endif
>> >> +
>> >>   #endif /* __VS_DRV_H__ */
>> >
> 
> 
> 

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-11-14  8:27           ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-11-14  8:27 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Emil Renner Berthing, dri-devel, Krzysztof Kozlowski,
	linux-riscv, Sumit Semwal, Shengyang Chen, linux-media,
	devicetree, Conor Dooley, Albert Ou, Maxime Ripard, Jagan Teki,
	linaro-mm-sig, Rob Herring, Chris Morgan, Paul Walmsley,
	Bjorn Andersson, linux-kernel, christian.koenig, Jack Zhu,
	Palmer Dabbelt, Thomas Zimmermann, Shawn Guo, Changhuang Liang



On 2023/11/13 21:31, Dmitry Baryshkov wrote:
> On Mon, 13 Nov 2023 at 14:11, Keith Zhao <keith.zhao@starfivetech.com> wrote:
>>
>>
>>
>> On 2023/10/26 6:23, Dmitry Baryshkov wrote:
>> > On 25/10/2023 13:39, Keith Zhao wrote:
>> >> add hdmi driver as encoder and connect
>> >>
>> >> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>> >> ---
>> >>   drivers/gpu/drm/verisilicon/Kconfig         |   8 +-
>> >>   drivers/gpu/drm/verisilicon/Makefile        |   1 +
>> >>   drivers/gpu/drm/verisilicon/starfive_hdmi.c | 949 ++++++++++++++++++++
>> >>   drivers/gpu/drm/verisilicon/starfive_hdmi.h | 295 ++++++
>> >>   drivers/gpu/drm/verisilicon/vs_drv.c        |   5 +
>> >>   drivers/gpu/drm/verisilicon/vs_drv.h        |   4 +
>> >>   6 files changed, 1261 insertions(+), 1 deletion(-)
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.c
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.h
>> >>
>> >> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
>> >> index 3a361f8c8..122c786e3 100644
>> >> --- a/drivers/gpu/drm/verisilicon/Kconfig
>> >> +++ b/drivers/gpu/drm/verisilicon/Kconfig
>> >> @@ -12,4 +12,10 @@ config DRM_VERISILICON
>> >>         setting and buffer management. It does not
>> >>         provide 2D or 3D acceleration.
>> >>   -
>> >> +config DRM_VERISILICON_STARFIVE_HDMI
>> >> +    bool "Starfive HDMI extensions"
>> >> +    depends on DRM_VERISILICON
>> >> +    help
>> >> +       This selects support for StarFive soc specific extensions
>> >> +       for the Innosilicon HDMI driver. If you want to enable
>> >> +       HDMI on JH7110 based soc, you should select this option.
>> >> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
>> >> index 1d48016ca..08350f25b 100644
>> >> --- a/drivers/gpu/drm/verisilicon/Makefile
>> >> +++ b/drivers/gpu/drm/verisilicon/Makefile
>> >> @@ -7,5 +7,6 @@ vs_drm-objs := vs_dc_hw.o \
>> >>           vs_modeset.o \
>> >>           vs_plane.o
>> >>   +vs_drm-$(CONFIG_DRM_VERISILICON_STARFIVE_HDMI) += starfive_hdmi.o
>> >>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>> >>   diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.c b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
>> >> new file mode 100644
>> >> index 000000000..d296c4b71
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
>> >> @@ -0,0 +1,949 @@
>> >> +// SPDX-License-Identifier: GPL-2.0-only
>> >> +/*
>> >> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
>> >> + */
>> >> +
>> >> +#include <linux/clk.h>
>> >> +#include <linux/component.h>
>> >> +#include <linux/delay.h>
>> >> +#include <linux/err.h>
>> >> +#include <linux/hdmi.h>
>> >> +#include <linux/i2c.h>
>> >> +#include <linux/irq.h>
>> >> +#include <linux/media-bus-format.h>
>> >> +#include <linux/mfd/syscon.h>
>> >> +#include <linux/module.h>
>> >> +#include <linux/mutex.h>
>> >> +#include <linux/of_device.h>
>> >> +#include <linux/pm_runtime.h>
>> >> +#include <linux/reset.h>
>> >> +
>> >> +#include <drm/bridge/dw_hdmi.h>
>> >> +#include <drm/drm_atomic_helper.h>
>> >> +#include <drm/drm_edid.h>
>> >> +#include <drm/drm_managed.h>
>> >> +#include <drm/drm_of.h>
>> >> +#include <drm/drm_probe_helper.h>
>> >> +#include <drm/drm_simple_kms_helper.h>
>> >> +
>> >> +#include "starfive_hdmi.h"
>> >> +#include "vs_drv.h"
>> >> +#include "vs_crtc.h"
>> >> +
>> >> +static struct starfive_hdmi *encoder_to_hdmi(struct drm_encoder *encoder)
>> >> +{
>> >> +    return container_of(encoder, struct starfive_hdmi, encoder);
>> >> +}
>> >> +
>> >> +static struct starfive_hdmi *connector_to_hdmi(struct drm_connector *connector)
>> >> +{
>> >> +    return container_of(connector, struct starfive_hdmi, connector);
>> >> +}
>> >> +
>> >> +struct starfive_hdmi_i2c {
>> >> +    struct i2c_adapter adap;
>> >> +
>> >> +    u8 ddc_addr;
>> >> +    u8 segment_addr;
>> >> +    /* protects the edid data when use i2c cmd to read edid */
>> >> +    struct mutex lock;
>> >> +    struct completion cmp;
>> >> +};
>> >> +
>> >> +static const struct pre_pll_config pre_pll_cfg_table[] = {
>> >> +    { 25175000,  25175000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0xf55555},
>> >> +    { 25200000,  25200000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0},
>> >> +    { 27000000,  27000000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0},
>> >
>> > Such data structures are usually pretyt limited and hard to handle. Could you please replace it with the runtime calculations of
>> >
>> >> +    { 27027000,  27027000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0x170a3d},
>> >> +    { 28320000,  28320000, 1,  28, 2, 1, 1,  3, 0, 3, 4, 0, 0x51eb85},
>> >> +    { 30240000,  30240000, 1,  30, 2, 1, 1,  3, 0, 3, 4, 0, 0x3d70a3},
>> >> +    { 31500000,  31500000, 1,  31, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> >> +    { 33750000,  33750000, 1,  33, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
>> >> +    { 36000000,  36000000, 1,  36, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    { 40000000,  40000000, 1,  80, 2, 2, 2, 12, 2, 2, 2, 0, 0},
>> >> +    { 46970000,  46970000, 1,  46, 2, 1, 1,  3, 0, 3, 4, 0, 0xf851eb},
>> >> +    { 49500000,  49500000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> >> +    { 49000000,  49000000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    { 50000000,  50000000, 1,  50, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    { 54000000,  54000000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    { 54054000,  54054000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0x0dd2f1},
>> >> +    { 57284000,  57284000, 1,  57, 2, 1, 1,  3, 0, 3, 4, 0, 0x48b439},
>> >> +    { 58230000,  58230000, 1,  58, 2, 1, 1,  3, 0, 3, 4, 0, 0x3ae147},
>> >> +    { 59341000,  59341000, 1,  59, 2, 1, 1,  3, 0, 3, 4, 0, 0x574bc6},
>> >> +    { 59400000,  59400000, 1,  99, 3, 1, 1,  1, 3, 3, 4, 0, 0},
>> >> +    { 65000000,  65000000, 1, 130, 2, 2, 2,  12, 0, 2, 2, 0, 0},
>> >> +    { 68250000,  68250000, 1, 68,  2, 1, 1,  3,  0, 3, 4, 0, 0x3fffff},
>> >> +    { 71000000,  71000000, 1,  71, 2, 1, 1,  3, 0, 3,  4, 0, 0},
>> >> +    { 74176000,  74176000, 1,  98, 1, 2, 2,  1, 2, 3, 4, 0, 0xe6ae6b},
>> >> +    { 74250000,  74250000, 1,  99, 1, 2, 2,  1, 2, 3, 4, 0, 0},
>> >> +    { 75000000,  75000000, 1,  75, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    { 78750000,  78750000, 1,  78, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
>> >> +    { 79500000,  79500000, 1,  79, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> >> +    { 83500000,  83500000, 2, 167, 2, 1, 1,  1, 0, 0,  6, 0, 0},
>> >> +    { 83500000, 104375000, 1, 104, 2, 1, 1,  1, 1, 0,  5, 0, 0x600000},
>> >> +    { 84858000,  84858000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xdba5e2},
>> >> +    { 85500000,  85500000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
>> >> +    { 85750000,  85750000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
>> >> +    { 85800000,  85800000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcccccc},
>> >> +    { 88750000,  88750000, 1,  88, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
>> >> +    { 89910000,  89910000, 1,  89, 2, 1, 1,  3, 0, 3, 4, 0, 0xe8f5c1},
>> >> +    { 90000000,  90000000, 1,  90, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {101000000, 101000000, 1, 101, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {102250000, 102250000, 1, 102, 2, 1, 1,  3, 0, 3, 4, 0, 0x3fffff},
>> >> +    {106500000, 106500000, 1, 106, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> >> +    {108000000, 108000000, 1,  90, 3, 0, 0,  5, 0, 2,  2, 0, 0},
>> >> +    {119000000, 119000000, 1, 119, 2, 1, 1,  3, 0, 3,  4, 0, 0},
>> >> +    {131481000, 131481000, 1,  131, 2, 1, 1,  3, 0, 3,  4, 0, 0x7b22d1},
>> >> +    {135000000, 135000000, 1,  135, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {136750000, 136750000, 1,  136, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
>> >> +    {147180000, 147180000, 1,  147, 2, 1, 1,  3, 0, 3, 4, 0, 0x2e147a},
>> >> +    {148352000, 148352000, 1,  98, 1, 1, 1,  1, 2, 2, 2, 0, 0xe6ae6b},
>> >> +    {148500000, 148500000, 1,  99, 1, 1, 1,  1, 2, 2, 2, 0, 0},
>> >> +    {154000000, 154000000, 1, 154, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {156000000, 156000000, 1, 156, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {157000000, 157000000, 1, 157, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {162000000, 162000000, 1, 162, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {174250000, 174250000, 1, 145, 3, 0, 0,  5, 0, 2, 2, 0, 0x355555},
>> >> +    {174500000, 174500000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> >> +    {174570000, 174570000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x91eb84},
>> >> +    {175500000, 175500000, 1, 175, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> >> +    {185590000, 185590000, 1, 185, 2, 1, 1,  3, 0, 3, 4, 0, 0x970a3c},
>> >> +    {187000000, 187000000, 1, 187, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {241500000, 241500000, 1, 161, 1, 1, 1,  4, 0, 2,  2, 0, 0},
>> >> +    {241700000, 241700000, 1, 241, 2, 1, 1,  3, 0, 3,  4, 0, 0xb33332},
>> >> +    {262750000, 262750000, 1, 262, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
>> >> +    {296500000, 296500000, 1, 296, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
>> >> +    {296703000, 296703000, 1,  98, 0, 1, 1,  1, 0, 2,  2, 0, 0xe6ae6b},
>> >> +    {297000000, 297000000, 1,  99, 0, 1, 1,  1, 0, 2,  2, 0, 0},
>> >> +    {594000000, 594000000, 1,  99, 0, 2, 0,  1, 0, 1,  1, 0, 0},
>> >> +    {0, 0, 0,  0, 0, 0, 0,  0, 0, 0,  0, 0, 0},
>> >> +};
>> >> +
>> >> +static const struct post_pll_config post_pll_cfg_table[] = {
>> >> +    {25200000,    1, 80, 13, 3, 1},
>> >> +    {27000000,    1, 40, 11, 3, 1},
>> >> +    {33750000,    1, 40, 11, 3, 1},
>> >> +    {49000000,    1, 20, 1, 3, 3},
>> >> +    {241700000, 1, 20, 1, 3, 3},
>> >> +    {297000000, 4, 20, 0, 0, 3},
>> >> +    {594000000, 4, 20, 0, 0, 0},
>> >> +    { /* sentinel */ }
>> >> +};
>> >> +
>> >> +inline u8 hdmi_readb(struct starfive_hdmi *hdmi, u16 offset)
>> >> +{
>> >> +    return readl_relaxed(hdmi->regs + (offset) * 0x04);
>> >> +}
>> >> +
>> >> +inline void hdmi_writeb(struct starfive_hdmi *hdmi, u16 offset, u32 val)
>> >> +{
>> >> +    writel_relaxed(val, hdmi->regs + (offset) * 0x04);
>> >> +}
>> >> +
>> >> +inline void hdmi_modb(struct starfive_hdmi *hdmi, u16 offset,
>> >> +                 u32 msk, u32 val)
>> >> +{
>> >> +    u8 temp = hdmi_readb(hdmi, offset) & ~msk;
>> >> +
>> >> +    temp |= val & msk;
>> >> +    hdmi_writeb(hdmi, offset, temp);
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_enable_clk_deassert_rst(struct device *dev, struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    int ret;
>> >> +
>> >> +    ret = clk_prepare_enable(hdmi->sys_clk);
>> >> +    if (ret) {
>> >> +        dev_err(dev, "Cannot enable HDMI sys clock: %d\n", ret);
>> >> +        return ret;
>> >> +    }
>> >> +
>> >> +    ret = clk_prepare_enable(hdmi->mclk);
>> >> +    if (ret) {
>> >> +        dev_err(dev, "Cannot enable HDMI mclk clock: %d\n", ret);
>> >> +        goto err_mclk;
>> >> +    }
>> >> +    ret = clk_prepare_enable(hdmi->bclk);
>> >
>> > This code begs to use clk_bulk instead.
>> >
>> >> +    if (ret) {
>> >> +        dev_err(dev, "Cannot enable HDMI bclk clock: %d\n", ret);
>> >> +        goto err_bclk;
>> >> +    }
>> >> +    ret = reset_control_deassert(hdmi->tx_rst);
>> >> +    if (ret < 0) {
>> >> +        dev_err(dev, "failed to deassert tx_rst\n");
>> >> +        goto err_rst;
>> >> +    }
>> >> +    return 0;
>> >> +
>> >> +err_rst:
>> >> +    clk_disable_unprepare(hdmi->bclk);
>> >> +err_bclk:
>> >> +    clk_disable_unprepare(hdmi->mclk);
>> >> +err_mclk:
>> >> +    clk_disable_unprepare(hdmi->sys_clk);
>> >> +    return ret;
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_disable_clk_assert_rst(struct device *dev, struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    int ret;
>> >> +
>> >> +    ret = reset_control_assert(hdmi->tx_rst);
>> >> +    if (ret < 0)
>> >> +        dev_err(dev, "failed to assert tx_rst\n");
>> >> +
>> >> +    clk_disable_unprepare(hdmi->sys_clk);
>> >> +    clk_disable_unprepare(hdmi->mclk);
>> >> +    clk_disable_unprepare(hdmi->bclk);
>> >> +}
>> >> +
>> >> +#ifdef CONFIG_PM_SLEEP
>> >> +static int hdmi_system_pm_suspend(struct device *dev)
>> >> +{
>> >> +    return pm_runtime_force_suspend(dev);
>> >> +}
>> >> +
>> >> +static int hdmi_system_pm_resume(struct device *dev)
>> >> +{
>> >> +    return pm_runtime_force_resume(dev);
>> >> +}
>> >> +#endif
>> >> +
>> >> +#ifdef CONFIG_PM
>> >> +static int hdmi_runtime_suspend(struct device *dev)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
>> >> +
>> >> +    starfive_hdmi_disable_clk_assert_rst(dev, hdmi);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int hdmi_runtime_resume(struct device *dev)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
>> >> +
>> >> +    return starfive_hdmi_enable_clk_deassert_rst(dev, hdmi);
>> >> +}
>> >> +#endif
>> >> +
>> >> +static void starfive_hdmi_tx_phy_power_down(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_tx_phy_power_on(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
>> >
>> > It looks like one can inline these two helpers w/o too much troubles.
>> >
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_config_pll(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    u32 val;
>> >> +    u8 reg_1ad_value = hdmi->post_cfg->post_div_en ?
>> >> +         hdmi->post_cfg->postdiv : 0x00;
>> >> +    u8 reg_1aa_value = hdmi->post_cfg->post_div_en ?
>> >> +         0x0e : 0x02;
>> >> +
>> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN);
>> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1,
>> >> +            STARFIVE_POST_PLL_POST_DIV_ENABLE |
>> >> +            STARFIVE_POST_PLL_REFCLK_SEL_TMDS |
>> >> +            STARFIVE_POST_PLL_POWER_DOWN);
>> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_1, STARFIVE_PRE_PLL_PRE_DIV(hdmi->pre_cfg->prediv));
>> >> +
>> >> +    val = STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE | STARFIVE_SPREAD_SPECTRUM_MOD_DOWN;
>> >> +    if (!hdmi->pre_cfg->fracdiv)
>> >> +        val |= STARFIVE_PRE_PLL_FRAC_DIV_DISABLE;
>> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_2,
>> >> +            STARFIVE_PRE_PLL_FB_DIV_11_8(hdmi->pre_cfg->fbdiv) | val);
>> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_3,
>> >> +            STARFIVE_PRE_PLL_FB_DIV_7_0(hdmi->pre_cfg->fbdiv));
>> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_4,
>> >> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_C(hdmi->pre_cfg->tmds_div_c) |
>> >> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_A(hdmi->pre_cfg->tmds_div_a) |
>> >> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_B(hdmi->pre_cfg->tmds_div_b));
>> >> +
>> >> +    if (hdmi->pre_cfg->fracdiv) {
>> >> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_L,
>> >> +                STARFIVE_PRE_PLL_FRAC_DIV_7_0(hdmi->pre_cfg->fracdiv));
>> >> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_M,
>> >> +                STARFIVE_PRE_PLL_FRAC_DIV_15_8(hdmi->pre_cfg->fracdiv));
>> >> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_H,
>> >> +                STARFIVE_PRE_PLL_FRAC_DIV_23_16(hdmi->pre_cfg->fracdiv));
>> >> +    }
>> >> +
>> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_5,
>> >> +            STARFIVE_PRE_PLL_PCLK_DIV_A(hdmi->pre_cfg->pclk_div_a) |
>> >> +            STARFIVE_PRE_PLL_PCLK_DIV_B(hdmi->pre_cfg->pclk_div_b));
>> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_6,
>> >> +            STARFIVE_PRE_PLL_PCLK_DIV_C(hdmi->pre_cfg->pclk_div_c) |
>> >> +            STARFIVE_PRE_PLL_PCLK_DIV_D(hdmi->pre_cfg->pclk_div_d));
>> >> +
>> >> +    /*pre-pll power down*/
>> >> +    hdmi_modb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN, 0);
>> >> +
>> >> +    hdmi_modb(hdmi, STARFIVE_POST_PLL_DIV_2, STARFIVE_POST_PLL_Pre_DIV_MASK,
>> >> +          STARFIVE_POST_PLL_PRE_DIV(hdmi->post_cfg->prediv));
>> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_3, hdmi->post_cfg->fbdiv & 0xff);
>> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_4, reg_1ad_value);
>> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1, reg_1aa_value);
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_tmds_driver_on(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    hdmi_modb(hdmi, STARFIVE_TMDS_CONTROL,
>> >> +          STARFIVE_TMDS_DRIVER_ENABLE, STARFIVE_TMDS_DRIVER_ENABLE);
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_sync_tmds(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    /*first send 0 to this bit, then send 1 and keep 1 into this bit*/
>> >> +    hdmi_writeb(hdmi, HDMI_SYNC, 0x0);
>> >> +    hdmi_writeb(hdmi, HDMI_SYNC, 0x1);
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_i2c_init(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    int ddc_bus_freq;
>> >> +
>> >> +    ddc_bus_freq = (clk_get_rate(hdmi->sys_clk) >> 2) / HDMI_SCL_RATE;
>> >> +
>> >> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
>> >> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
>> >> +
>> >> +    /* Clear the EDID interrupt flag and mute the interrupt */
>> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
>> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> >> +}
>> >> +
>> >> +static const
>> >> +struct pre_pll_config *starfive_hdmi_phy_get_pre_pll_cfg(struct starfive_hdmi *hdmi,
>> >> +                             unsigned long rate)
>> >> +{
>> >> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
>> >> +
>> >> +    rate = (rate / 1000) * 1000;
>> >> +    for (; cfg->pixclock != 0; cfg++)
>> >> +        if (cfg->tmdsclock == rate && cfg->pixclock == rate)
>> >> +            break;
>> >> +
>> >> +    if (cfg->pixclock == 0)
>> >> +        return ERR_PTR(-EINVAL);
>> >> +
>> >> +    return cfg;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_phy_clk_set_rate(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    hdmi->post_cfg = post_pll_cfg_table;
>> >> +
>> >> +    hdmi->pre_cfg = starfive_hdmi_phy_get_pre_pll_cfg(hdmi, hdmi->tmds_rate);
>> >> +    if (IS_ERR(hdmi->pre_cfg))
>> >> +        return PTR_ERR(hdmi->pre_cfg);
>> >> +
>> >> +    for (; hdmi->post_cfg->tmdsclock != 0; hdmi->post_cfg++)
>> >> +        if (hdmi->tmds_rate <= hdmi->post_cfg->tmdsclock)
>> >> +            break;
>> >> +
>> >> +    starfive_hdmi_config_pll(hdmi);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_config_video_timing(struct starfive_hdmi *hdmi,
>> >> +                         struct drm_display_mode *mode)
>> >> +{
>> >> +    int value;
>> >> +    /* Set detail external video timing */
>> >> +    value = mode->htotal;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
>> >
>> > As you have hdmi_writeb already, adding hdmi_writew will help here.
>> >
>> >> +
>> >> +    value = mode->htotal - mode->hdisplay;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
>> >> +
>> >> +    value = mode->htotal - mode->hsync_start;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
>> >> +
>> >> +    value = mode->hsync_end - mode->hsync_start;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
>> >> +
>> >> +    value = mode->vtotal;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
>> >> +
>> >> +    value = mode->vtotal - mode->vdisplay;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
>> >> +
>> >> +    value = mode->vtotal - mode->vsync_start;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
>> >> +
>> >> +    value = mode->vsync_end - mode->vsync_start;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
>> >> +
>> >> +    /* Set detail external video timing polarity and interlace mode */
>> >> +    value = v_EXTERANL_VIDEO(1);
>> >> +    value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
>> >> +        v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
>> >> +    value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
>> >> +        v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
>> >> +    value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
>> >> +        v_INETLACE(1) : v_INETLACE(0);
>> >> +
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_setup(struct starfive_hdmi *hdmi,
>> >> +                   struct drm_display_mode *mode)
>> >> +{
>> >> +    hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
>> >> +    hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
>> >> +    hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
>> >> +
>> >> +    hdmi->tmds_rate = mode->clock * 1000;
>> >> +    starfive_hdmi_phy_clk_set_rate(hdmi);
>> >> +
>> >> +    while (!(hdmi_readb(hdmi, STARFIVE_PRE_PLL_LOCK_STATUS) & 0x1))
>> >> +        continue;
>> >> +    while (!(hdmi_readb(hdmi, STARFIVE_POST_PLL_LOCK_STATUS) & 0x1))
>> >> +        continue;
>> >
>> > Please use read_iopoll_timeout here.
>> >
>> >> +
>> >> +    /*turn on LDO*/
>> >> +    hdmi_writeb(hdmi, STARFIVE_LDO_CONTROL, STARFIVE_LDO_ENABLE);
>> >> +    /*turn on serializer*/
>> >> +    hdmi_writeb(hdmi, STARFIVE_SERIALIER_CONTROL, STARFIVE_SERIALIER_ENABLE);
>> >> +
>> >> +    starfive_hdmi_tx_phy_power_down(hdmi);
>> >> +    starfive_hdmi_config_video_timing(hdmi, mode);
>> >> +    starfive_hdmi_tx_phy_power_on(hdmi);
>> >> +
>> >> +    starfive_hdmi_tmds_driver_on(hdmi);
>> >> +    starfive_hdmi_sync_tmds(hdmi);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_encoder_mode_set(struct drm_encoder *encoder,
>> >> +                       struct drm_display_mode *mode,
>> >> +                       struct drm_display_mode *adj_mode)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
>> >> +
>> >> +    drm_mode_copy(&hdmi->previous_mode, adj_mode);
>> >
>> > Why do you need it?
>> >
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_encoder_enable(struct drm_encoder *encoder)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
>> >> +    struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
>> >> +    int ret, idx;
>> >> +    struct drm_device *drm = hdmi->connector.dev;
>> >> +
>> >> +    if (drm && !drm_dev_enter(drm, &idx))
>> >> +        return;
>> >> +
>> >> +    ret = pm_runtime_get_sync(hdmi->dev);
>> >> +    if (ret < 0)
>> >> +        return;
>> >> +    mdelay(10);
>> >> +    starfive_hdmi_setup(hdmi, mode);
>> >> +
>> >> +    if (drm)
>> >> +        drm_dev_exit(idx);
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
>> >> +
>> >> +    int idx;
>> >> +    struct drm_device *drm = hdmi->connector.dev;
>> >> +
>> >> +    if (drm && !drm_dev_enter(drm, &idx))
>> >> +        return;
>> >> +
>> >> +    pm_runtime_put(hdmi->dev);
>> >> +
>> >> +    if (drm)
>> >> +        drm_dev_exit(idx);
>> >> +}
>> >> +
>> >> +static int
>> >> +starfive_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
>> >> +                   struct drm_crtc_state *crtc_state,
>> >> +                   struct drm_connector_state *conn_state)
>> >> +{
>> >> +    bool valid = false;
>> >> +    struct drm_display_mode *mode = &crtc_state->adjusted_mode;
>> >> +    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
>> >> +
>> >> +    vs_crtc_state->encoder_type = encoder->encoder_type;
>> >> +    vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
>> >> +
>> >> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
>> >> +    int pclk = mode->clock * 1000;
>> >> +
>> >> +    for (; cfg->pixclock != 0; cfg++) {
>> >> +        if (pclk == cfg->pixclock) {
>> >> +            if (pclk > 297000000)
>> >
>> > Magic value.
>> >
>> >> +                continue;
>> >> +
>> >> +            valid = true;
>> >> +            break;
>> >> +        }
>> >> +    }
>> >> +
>> >> +    return (valid) ? 0 : -EINVAL;
>> >> +}
>> >> +
>> >> +static const struct drm_encoder_helper_funcs starfive_hdmi_encoder_helper_funcs = {
>> >> +    .enable     = starfive_hdmi_encoder_enable,
>> >> +    .disable    = starfive_hdmi_encoder_disable,
>> >> +    .mode_set   = starfive_hdmi_encoder_mode_set,
>> >> +    .atomic_check = starfive_hdmi_encoder_atomic_check,
>> >> +};
>> >> +
>> >> +static enum drm_connector_status
>> >> +starfive_hdmi_connector_detect(struct drm_connector *connector, bool force)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
>> >> +    struct drm_device *drm = hdmi->connector.dev;
>> >> +    int ret;
>> >> +    int idx;
>> >> +
>> >> +    if (drm && !drm_dev_enter(drm, &idx))
>> >> +        return connector_status_disconnected;
>> >> +
>> >> +    ret = pm_runtime_get_sync(hdmi->dev);
>> >> +    if (ret < 0)
>> >> +        return ret;
>> >> +
>> >> +    ret = (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
>> >> +        connector_status_connected : connector_status_disconnected;
>> >> +    pm_runtime_put(hdmi->dev);
>> >> +
>> >> +    if (drm)
>> >> +        drm_dev_exit(idx);
>> >> +
>> >> +    return ret;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_connector_get_modes(struct drm_connector *connector)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
>> >> +    struct edid *edid;
>> >> +    int ret = 0;
>> >> +
>> >> +    if (!hdmi->ddc)
>> >> +        return 0;
>> >> +    ret = pm_runtime_get_sync(hdmi->dev);
>> >> +    if (ret < 0)
>> >> +        return ret;
>> >> +
>> >> +    edid = drm_get_edid(connector, hdmi->ddc);
>> >> +    if (edid) {
>> >> +        hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
>> >> +        hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
>> >> +        drm_connector_update_edid_property(connector, edid);
>> >> +        ret = drm_add_edid_modes(connector, edid);
>> >> +        kfree(edid);
>> >> +    }
>> >> +    pm_runtime_put(hdmi->dev);
>> >
>> > Can you use drm_connector_helper_get_modes_from_ddc() here? And then drm_display_info.is_hdmi and .has_audio.
>> >
>> >> +
>> >> +    return ret;
>> >> +}
>> >> +
>> >> +static enum drm_mode_status
>> >> +starfive_hdmi_connector_mode_valid(struct drm_connector *connector,
>> >> +                   struct drm_display_mode *mode)
>> >> +{
>> >> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
>> >> +    int pclk = mode->clock * 1000;
>> >> +    bool valid = false;
>> >> +
>> >> +    for (; cfg->pixclock != 0; cfg++) {
>> >> +        if (pclk == cfg->pixclock) {
>> >> +            if (pclk > 297000000)
>> >
>> > magic value
>> >
>> >> +                continue;
>> >> +
>> >> +            valid = true;
>> >> +            break;
>> >> +        }
>> >> +    }
>> >> +
>> >> +    return (valid) ? MODE_OK : MODE_BAD;
>> >> +}
>> >> +
>> >> +static int
>> >> +starfive_hdmi_probe_single_connector_modes(struct drm_connector *connector,
>> >> +                       u32 maxX, u32 maxY)
>> >> +{
>> >> +    return drm_helper_probe_single_connector_modes(connector, 3840, 2160);
>> >> +}
>> >> +
>> >> +static const struct drm_connector_funcs starfive_hdmi_connector_funcs = {
>> >> +    .fill_modes = starfive_hdmi_probe_single_connector_modes,
>> >> +    .detect = starfive_hdmi_connector_detect,
>> >> +    .reset = drm_atomic_helper_connector_reset,
>> >> +    .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
>> >> +    .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>> >> +};
>> >> +
>> >> +static struct drm_connector_helper_funcs starfive_hdmi_connector_helper_funcs = {
>> >> +    .get_modes = starfive_hdmi_connector_get_modes,
>> >> +    .mode_valid = starfive_hdmi_connector_mode_valid,
>> >> +};
>> >> +
>> >> +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    struct drm_encoder *encoder = &hdmi->encoder;
>> >> +    struct device *dev = hdmi->dev;
>> >> +
>> >> +    encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
>> >> +
>> >> +    /*
>> >> +     * If we failed to find the CRTC(s) which this encoder is
>> >> +     * supposed to be connected to, it's because the CRTC has
>> >> +     * not been registered yet.  Defer probing, and hope that
>> >> +     * the required CRTC is added later.
>> >> +     */
>> >> +    if (encoder->possible_crtcs == 0)
>> >> +        return -EPROBE_DEFER;
>> >> +
>> >> +    drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
>> >> +
>> >> +    hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
>> >> +
>> >> +    drm_connector_helper_add(&hdmi->connector,
>> >> +                 &starfive_hdmi_connector_helper_funcs);
>> >> +    drmm_connector_init(drm, &hdmi->connector,
>> >> +                &starfive_hdmi_connector_funcs,
>> >> +                DRM_MODE_CONNECTOR_HDMIA,
>> >
>> > On an embedded device one can not be so sure. There can be MHL or HDMI Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.
>> >
>> >> +                hdmi->ddc);
>> >> +
>> >> +    drm_connector_attach_encoder(&hdmi->connector, encoder);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static irqreturn_t starfive_hdmi_i2c_irq(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    struct starfive_hdmi_i2c *i2c = hdmi->i2c;
>> >> +    u8 stat;
>> >> +
>> >> +    stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
>> >> +    if (!(stat & m_INT_EDID_READY))
>> >> +        return IRQ_NONE;
>> >> +
>> >> +    /* Clear HDMI EDID interrupt flag */
>> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> >> +
>> >> +    complete(&i2c->cmp);
>> >> +
>> >> +    return IRQ_HANDLED;
>> >> +}
>> >> +
>> >> +static irqreturn_t starfive_hdmi_hardirq(int irq, void *dev_id)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = dev_id;
>> >> +    irqreturn_t ret = IRQ_NONE;
>> >> +    u8 interrupt;
>> >> +
>> >> +    if (hdmi->i2c)
>> >> +        ret = starfive_hdmi_i2c_irq(hdmi);
>> >> +
>> >> +    interrupt = hdmi_readb(hdmi, HDMI_STATUS);
>> >> +    if (interrupt & m_INT_HOTPLUG) {
>> >> +        hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
>> >> +        ret = IRQ_WAKE_THREAD;
>> >> +    }
>> >> +
>> >> +    return ret;
>> >> +}
>> >> +
>> >> +static irqreturn_t starfive_hdmi_irq(int irq, void *dev_id)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = dev_id;
>> >> +
>> >> +    drm_connector_helper_hpd_irq_event(&hdmi->connector);
>> >> +
>> >> +    return IRQ_HANDLED;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_i2c_read(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
>> >> +{
>> >> +    int length = msgs->len;
>> >> +    u8 *buf = msgs->buf;
>> >> +    int ret;
>> >> +
>> >> +    ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10);
>> >
>> > read_poll_timeout
>> Hi Dmitry:
>> I think using wait_for_completion_timeout here is a bit clearer than read_poll_timeout logic
>> I2c_read has hardware interrupt dependency. so need wait the interrupt completion.
>> also it comes with timeout detection.
> 
> Hi,
> 
> Yes, maybe I was thinking about a different piece of code here.
> wait_for_completion looks good here, indeed.
> 
> But this actually brings up another question. You reinit_comletion in
> starfive_hdmi_i2c_write() and then wait_for_completion in
> starfive_hdmi_i2c_read(). How does that work?
> 
> 
struct i2c_msg msgs[2] = {
        {
            .addr = DDC_ADDR,
            .flags = 0,
            .len = 1,
            .buf = (void *)&start,
        },{
            .addr = DDC_ADDR,
            .flags = I2C_M_RD,
            .len = DATA_LEN,
            .buf = buf,
        }
    };

For i2c devices, when reading edid information, the master device needs to send two msg to the slave device. 
The first msg contains the write operation flag(0), the written data length (start length) and the register address that needs to be read (start addr) ; 
The second msg contains the required read operation (I2C_M_RD), the length of the read data (equal to the length of buf) and the address (buf) where the data is read.

the first msg call starfive_hdmi_i2c_write 
and the second call starfive_hdmi_i2c_read

after the first msg send done , A valid interrupt will be generated (complete) ,then  the second msg will continue to read(wait_for_completion)
so that is the reason why nend do the reinit_comletion in starfive_hdmi_i2c_write.
if not do that , the second msg maybe read the edid at a wrong time.

This is my understanding. If there is anything wrong, I hope you will be corrected.

Thanks
> 
>>
>> >
>> >> +    if (!ret)
>> >> +        return -EAGAIN;
>> >> +
>> >> +    while (length--)
>> >> +        *buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_i2c_write(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
>> >> +{
>> >> +    /*
>> >> +     * The DDC module only support read EDID message, so
>> >> +     * we assume that each word write to this i2c adapter
>> >> +     * should be the offset of EDID word address.
>> >> +     */
>> >> +    if (msgs->len != 1 ||
>> >> +        (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR))
>> >> +        return -EINVAL;
>> >> +
>> >> +    reinit_completion(&hdmi->i2c->cmp);
>> >> +
>> >> +    if (msgs->addr == DDC_SEGMENT_ADDR)
>> >> +        hdmi->i2c->segment_addr = msgs->buf[0];
>> >> +    if (msgs->addr == DDC_ADDR)
>> >> +        hdmi->i2c->ddc_addr = msgs->buf[0];
>> >> +
>> >> +    /* Set edid fifo first addr */
>> >> +    hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
>> >> +
>> >> +    /* Set edid word address 0x00/0x80 */
>> >> +    hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
>> >> +
>> >> +    /* Set edid segment pointer */
>> >> +    hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_i2c_xfer(struct i2c_adapter *adap,
>> >> +                  struct i2c_msg *msgs, int num)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = i2c_get_adapdata(adap);
>> >> +    struct starfive_hdmi_i2c *i2c = hdmi->i2c;
>> >> +    int i, ret = 0;
>> >> +
>> >> +    mutex_lock(&i2c->lock);
>> >> +
>> >> +    /* Clear the EDID interrupt flag and unmute the interrupt */
>> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
>> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> >> +
>> >> +    for (i = 0; i < num; i++) {
>> >> +        DRM_DEV_DEBUG(hdmi->dev,
>> >> +                  "xfer: num: %d/%d, len: %d, flags: %#x\n",
>> >> +                  i + 1, num, msgs[i].len, msgs[i].flags);
>> >> +
>> >> +        if (msgs[i].flags & I2C_M_RD)
>> >> +            ret = starfive_hdmi_i2c_read(hdmi, &msgs[i]);
>> >> +        else
>> >> +            ret = starfive_hdmi_i2c_write(hdmi, &msgs[i]);
>> >> +
>> >> +        if (ret < 0)
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    if (!ret)
>> >> +        ret = num;
>> >> +
>> >> +    /* Mute HDMI EDID interrupt */
>> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
>> >> +
>> >> +    mutex_unlock(&i2c->lock);
>> >> +
>> >> +    return ret;
>> >> +}
>> >> +
>> >> +static u32 starfive_hdmi_i2c_func(struct i2c_adapter *adapter)
>> >> +{
>> >> +    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
>> >> +}
>> >> +
>> >> +static const struct i2c_algorithm starfive_hdmi_algorithm = {
>> >> +    .master_xfer    = starfive_hdmi_i2c_xfer,
>> >> +    .functionality    = starfive_hdmi_i2c_func,
>> >> +};
>> >> +
>> >> +static struct i2c_adapter *starfive_hdmi_i2c_adapter(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    struct i2c_adapter *adap;
>> >> +    struct starfive_hdmi_i2c *i2c;
>> >> +    int ret;
>> >> +
>> >> +    i2c = drmm_kzalloc(hdmi->drm_dev, sizeof(*i2c), GFP_KERNEL);
>> >> +    if (!i2c)
>> >> +        return ERR_PTR(-ENOMEM);
>> >> +
>> >> +    mutex_init(&i2c->lock);
>> >> +    init_completion(&i2c->cmp);
>> >> +
>> >> +    adap = &i2c->adap;
>> >> +    adap->class = I2C_CLASS_DDC;
>> >> +    adap->owner = THIS_MODULE;
>> >> +    adap->dev.parent = hdmi->dev;
>> >> +    adap->algo = &starfive_hdmi_algorithm;
>> >> +    strscpy(adap->name, "Starfive HDMI", sizeof(adap->name));
>> >> +    i2c_set_adapdata(adap, hdmi);
>> >> +
>> >> +    ret = devm_i2c_add_adapter(hdmi->dev, adap);
>> >> +    if (ret) {
>> >> +        drm_warn(hdmi->drm_dev, "cannot add %s I2C adapter\n", adap->name);
>> >> +        return ERR_PTR(ret);
>> >> +    }
>> >> +
>> >> +    hdmi->i2c = i2c;
>> >> +
>> >> +    drm_info(hdmi->drm_dev, "registered %s I2C bus driver success\n", adap->name);
>> >> +
>> >> +    return adap;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_get_clk_rst(struct device *dev, struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    hdmi->sys_clk = devm_clk_get(dev, "sysclk");
>> >> +    if (IS_ERR(hdmi->sys_clk)) {
>> >> +        dev_err(dev, "Unable to get HDMI sysclk clk\n");
>> >> +        return PTR_ERR(hdmi->sys_clk);
>> >> +    }
>> >> +    hdmi->mclk = devm_clk_get(dev, "mclk");
>> >> +    if (IS_ERR(hdmi->mclk)) {
>> >> +        dev_err(dev, "Unable to get HDMI mclk clk\n");
>> >> +        return PTR_ERR(hdmi->mclk);
>> >> +    }
>> >> +    hdmi->bclk = devm_clk_get(dev, "bclk");
>> >> +    if (IS_ERR(hdmi->bclk)) {
>> >> +        dev_err(dev, "Unable to get HDMI bclk clk\n");
>> >> +        return PTR_ERR(hdmi->bclk);
>> >> +    }
>> >> +    hdmi->tx_rst = devm_reset_control_get_by_index(dev, 0);
>> >> +    if (IS_ERR(hdmi->tx_rst)) {
>> >> +        dev_err(dev, "failed to get tx_rst reset\n");
>> >> +        return PTR_ERR(hdmi->tx_rst);
>> >> +    }
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_bind(struct device *dev, struct device *master,
>> >> +                  void *data)
>> >> +{
>> >> +    struct platform_device *pdev = to_platform_device(dev);
>> >> +    struct drm_device *drm = dev_get_drvdata(master);
>> >> +    struct starfive_hdmi *hdmi;
>> >> +    struct resource *iores;
>> >> +    int irq;
>> >> +    int ret;
>> >> +
>> >> +    hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
>> >> +    if (!hdmi)
>> >> +        return -ENOMEM;
>> >> +
>> >> +    hdmi = drmm_simple_encoder_alloc(drm, struct starfive_hdmi,
>> >> +                     encoder, DRM_MODE_ENCODER_TMDS);
>> >> +
>> >> +    hdmi->dev = dev;
>> >> +    hdmi->drm_dev = drm;
>> >> +    dev_set_drvdata(dev, hdmi);
>> >> +
>> >> +    iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> >> +    hdmi->regs = devm_ioremap_resource(dev, iores);
>> >
>> > This should go to probe
>> >
>> >> +    if (IS_ERR(hdmi->regs))
>> >> +        return PTR_ERR(hdmi->regs);
>> >> +
>> >> +    ret = starfive_hdmi_get_clk_rst(dev, hdmi);
>> >> +    if (ret < 0)
>> >> +        return ret;
>> >
>> > And this
>> >
>> >> +
>> >> +    ret = devm_pm_runtime_enable(dev);
>> >> +    if (ret)
>> >> +        return ret;
>> >> +
>> >> +    ret = pm_runtime_resume_and_get(dev);
>> >> +    if (ret)
>> >> +        return ret;
>> >> +
>> >> +    irq = platform_get_irq(pdev, 0);
>> >
>> > And this
>> >
>> >> +    if (irq < 0) {
>> >> +        ret = irq;
>> >> +        goto err_put_runtime_pm;
>> >> +    }
>> >> +
>> >> +    hdmi->ddc = starfive_hdmi_i2c_adapter(hdmi);
>> >> +    if (IS_ERR(hdmi->ddc)) {
>> >> +        ret = PTR_ERR(hdmi->ddc);
>> >> +        hdmi->ddc = NULL;
>> >> +        goto err_put_runtime_pm;
>> >> +    }
>> >> +
>> >> +    starfive_hdmi_i2c_init(hdmi);
>> >> +
>> >> +    ret = starfive_hdmi_register(drm, hdmi);
>> >> +    if (ret)
>> >> +        goto err_put_adapter;
>> >> +
>> >> +    /* Unmute hotplug interrupt */
>> >> +    hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
>> >> +
>> >> +    ret = devm_request_threaded_irq(dev, irq, starfive_hdmi_hardirq,
>> >> +                    starfive_hdmi_irq, IRQF_SHARED,
>> >> +                    dev_name(dev), hdmi);
>> >> +    if (ret < 0)
>> >> +        goto err_put_adapter;
>> >> +
>> >> +    pm_runtime_put_sync(dev);
>> >> +
>> >> +    return 0;
>> >> +
>> >> +err_put_adapter:
>> >> +    i2c_put_adapter(hdmi->ddc);
>> >> +err_put_runtime_pm:
>> >> +    pm_runtime_put_sync(dev);
>> >> +
>> >> +    return ret;
>> >> +}
>> >> +
>> >> +static const struct component_ops starfive_hdmi_ops = {
>> >> +    .bind    = starfive_hdmi_bind,
>> >> +};
>> >> +
>> >> +static int starfive_hdmi_probe(struct platform_device *pdev)
>> >> +{
>> >> +    return component_add(&pdev->dev, &starfive_hdmi_ops);
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_remove(struct platform_device *pdev)
>> >> +{
>> >> +    component_del(&pdev->dev, &starfive_hdmi_ops);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static const struct dev_pm_ops hdmi_pm_ops = {
>> >> +    SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
>> >> +    SET_LATE_SYSTEM_SLEEP_PM_OPS(hdmi_system_pm_suspend, hdmi_system_pm_resume)
>> >> +};
>> >> +
>> >> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
>> >> +    { .compatible = "starfive,jh7110-inno-hdmi",},
>> >> +    {},
>> >> +};
>> >> +MODULE_DEVICE_TABLE(of, starfive_hdmi_dt_ids);
>> >> +
>> >> +struct platform_driver starfive_hdmi_driver = {
>> >> +    .probe  = starfive_hdmi_probe,
>> >> +    .remove = starfive_hdmi_remove,
>> >> +    .driver = {
>> >> +        .name = "starfive-hdmi",
>> >> +        .of_match_table = starfive_hdmi_dt_ids,
>> >> +        .pm = &hdmi_pm_ops,
>> >> +    },
>> >> +};
>> >> +
>> >> +MODULE_AUTHOR("StarFive Corporation");
>> >> +MODULE_DESCRIPTION("Starfive HDMI Driver");
>> >> diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.h b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
>> >> new file mode 100644
>> >> index 000000000..1b9a11bca
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
>> >> @@ -0,0 +1,295 @@
>> >> +/* SPDX-License-Identifier: GPL-2.0-only */
>> >> +/*
>> >> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
>> >> + */
>> >> +
>> >> +#ifndef __STARFIVE_HDMI_H__
>> >> +#define __STARFIVE_HDMI_H__
>> >> +
>> >> +#include <drm/bridge/dw_hdmi.h>
>> >> +#include <drm/drm_atomic_helper.h>
>> >> +#include <drm/drm_edid.h>
>> >> +#include <drm/drm_of.h>
>> >> +#include <drm/drm_probe_helper.h>
>> >> +#include <drm/drm_simple_kms_helper.h>
>> >> +#include <linux/bitfield.h>
>> >> +#include <linux/bits.h>
>> >> +
>> >> +#define DDC_SEGMENT_ADDR        0x30
>> >> +
>> >> +#define HDMI_SCL_RATE            (100 * 1000)
>> >> +#define DDC_BUS_FREQ_L            0x4b
>> >> +#define DDC_BUS_FREQ_H            0x4c
>> >> +
>> >> +#define HDMI_SYS_CTRL            0x00
>> >> +#define m_RST_ANALOG            BIT(6)
>> >> +#define v_RST_ANALOG            0
>> >> +#define v_NOT_RST_ANALOG        BIT(6)
>> >> +#define m_RST_DIGITAL            BIT(5)
>> >> +#define v_RST_DIGITAL            0
>> >> +#define v_NOT_RST_DIGITAL        BIT(5)
>> >> +#define m_REG_CLK_INV            BIT(4)
>> >> +#define v_REG_CLK_NOT_INV        0
>> >> +#define v_REG_CLK_INV            BIT(4)
>> >> +#define m_VCLK_INV            BIT(3)
>> >> +#define v_VCLK_NOT_INV            0
>> >> +#define v_VCLK_INV            BIT(3)
>> >> +#define m_REG_CLK_SOURCE        BIT(2)
>> >> +#define v_REG_CLK_SOURCE_TMDS        0
>> >> +#define v_REG_CLK_SOURCE_SYS        BIT(2)
>> >> +#define m_POWER                BIT(1)
>> >> +#define v_PWR_ON            0
>> >> +#define v_PWR_OFF            BIT(1)
>> >> +#define m_INT_POL            BIT(0)
>> >> +#define v_INT_POL_HIGH            1
>> >> +#define v_INT_POL_LOW            0
>> >> +
>> >> +#define HDMI_AV_MUTE            0x05
>> >> +#define m_AVMUTE_CLEAR            BIT(7)
>> >> +#define m_AVMUTE_ENABLE            BIT(6)
>> >> +#define m_AUDIO_MUTE            BIT(1)
>> >> +#define m_VIDEO_BLACK            BIT(0)
>> >> +#define v_AVMUTE_CLEAR(n)        ((n) << 7)
>> >> +#define v_AVMUTE_ENABLE(n)        ((n) << 6)
>> >> +#define v_AUDIO_MUTE(n)            ((n) << 1)
>> >> +#define v_VIDEO_MUTE(n)            ((n) << 0)
>> >> +
>> >> +#define HDMI_VIDEO_TIMING_CTL        0x08
>> >> +#define v_VSYNC_POLARITY(n)        ((n) << 3)
>> >> +#define v_HSYNC_POLARITY(n)        ((n) << 2)
>> >> +#define v_INETLACE(n)            ((n) << 1)
>> >> +#define v_EXTERANL_VIDEO(n)        ((n) << 0)
>> >> +
>> >> +#define HDMI_VIDEO_EXT_HTOTAL_L        0x09
>> >> +#define HDMI_VIDEO_EXT_HTOTAL_H        0x0a
>> >> +#define HDMI_VIDEO_EXT_HBLANK_L        0x0b
>> >> +#define HDMI_VIDEO_EXT_HBLANK_H        0x0c
>> >> +#define HDMI_VIDEO_EXT_HDELAY_L        0x0d
>> >> +#define HDMI_VIDEO_EXT_HDELAY_H        0x0e
>> >> +#define HDMI_VIDEO_EXT_HDURATION_L    0x0f
>> >> +#define HDMI_VIDEO_EXT_HDURATION_H    0x10
>> >> +#define HDMI_VIDEO_EXT_VTOTAL_L        0x11
>> >> +#define HDMI_VIDEO_EXT_VTOTAL_H        0x12
>> >> +#define HDMI_VIDEO_EXT_VBLANK        0x13
>> >> +#define HDMI_VIDEO_EXT_VDELAY        0x14
>> >> +#define HDMI_VIDEO_EXT_VDURATION    0x15
>> >> +
>> >> +#define HDMI_EDID_SEGMENT_POINTER    0x4d
>> >> +#define HDMI_EDID_WORD_ADDR        0x4e
>> >> +#define HDMI_EDID_FIFO_OFFSET        0x4f
>> >> +#define HDMI_EDID_FIFO_ADDR        0x50
>> >> +
>> >> +#define HDMI_INTERRUPT_MASK1        0xc0
>> >> +#define HDMI_INTERRUPT_STATUS1        0xc1
>> >> +#define    m_INT_ACTIVE_VSYNC        BIT(5)
>> >> +#define m_INT_EDID_READY        BIT(2)
>> >> +
>> >> +#define HDMI_STATUS            0xc8
>> >> +#define m_HOTPLUG            BIT(7)
>> >> +#define m_MASK_INT_HOTPLUG        BIT(5)
>> >> +#define m_INT_HOTPLUG            BIT(1)
>> >> +#define v_MASK_INT_HOTPLUG(n)        (((n) & 0x1) << 5)
>> >
>> > It is untypical to have field defines which start with lowercase letter.
>> >
>> >> +
>> >> +#define HDMI_SYNC                    0xce
>> >> +
>> >> +#define UPDATE(x, h, l)                    FIELD_PREP(GENMASK(h, l), x)
>> >> +
>> >> +/* REG: 0x1a0 */
>> >> +#define STARFIVE_PRE_PLL_CONTROL            0x1a0
>> >> +#define STARFIVE_PCLK_VCO_DIV_5_MASK            BIT(1)
>> >> +#define STARFIVE_PCLK_VCO_DIV_5(x)            UPDATE(x, 1, 1)
>> >> +#define STARFIVE_PRE_PLL_POWER_DOWN            BIT(0)
>> >> +
>> >> +/* REG: 0x1a1 */
>> >> +#define STARFIVE_PRE_PLL_DIV_1                0x1a1
>> >> +#define STARFIVE_PRE_PLL_PRE_DIV_MASK            GENMASK(5, 0)
>> >> +#define STARFIVE_PRE_PLL_PRE_DIV(x)            UPDATE(x, 5, 0)
>> >> +
>> >> +/* REG: 0x1a2 */
>> >> +#define STARFIVE_PRE_PLL_DIV_2                0x1a2
>> >> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DOWN        BIT(7)
>> >> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE        BIT(6)
>> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_DISABLE        UPDATE(3, 5, 4)
>> >> +#define STARFIVE_PRE_PLL_FB_DIV_11_8_MASK        GENMASK(3, 0)
>> >> +#define STARFIVE_PRE_PLL_FB_DIV_11_8(x)            UPDATE((x) >> 8, 3, 0)
>> >> +
>> >> +/* REG: 0x1a3 */
>> >> +#define STARFIVE_PRE_PLL_DIV_3                0x1a3
>> >> +#define STARFIVE_PRE_PLL_FB_DIV_7_0(x)            UPDATE(x, 7, 0)
>> >> +
>> >> +/* REG: 0x1a4*/
>> >> +#define STARFIVE_PRE_PLL_DIV_4                0x1a4
>> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C_MASK        GENMASK(1, 0)
>> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C(x)        UPDATE(x, 1, 0)
>> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B_MASK        GENMASK(3, 2)
>> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B(x)        UPDATE(x, 3, 2)
>> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A_MASK        GENMASK(5, 4)
>> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A(x)        UPDATE(x, 5, 4)
>> >> +
>> >> +/* REG: 0x1a5 */
>> >> +#define STARFIVE_PRE_PLL_DIV_5                0x1a5
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_SHIFT        5
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_MASK        GENMASK(6, 5)
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_B(x)            UPDATE(x, 6, 5)
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_A_MASK        GENMASK(4, 0)
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_A(x)            UPDATE(x, 4, 0)
>> >> +
>> >> +/* REG: 0x1a6 */
>> >> +#define STARFIVE_PRE_PLL_DIV_6                0x1a6
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_SHIFT        5
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_MASK        GENMASK(6, 5)
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_C(x)            UPDATE(x, 6, 5)
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_D_MASK        GENMASK(4, 0)
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_D(x)            UPDATE(x, 4, 0)
>> >> +
>> >> +/* REG: 0x1a9 */
>> >> +#define STARFIVE_PRE_PLL_LOCK_STATUS            0x1a9
>> >> +
>> >> +/* REG: 0x1aa */
>> >> +#define STARFIVE_POST_PLL_DIV_1                0x1aa
>> >> +#define STARFIVE_POST_PLL_POST_DIV_ENABLE        GENMASK(3, 2)
>> >> +#define STARFIVE_POST_PLL_REFCLK_SEL_TMDS        BIT(1)
>> >> +#define STARFIVE_POST_PLL_POWER_DOWN            BIT(0)
>> >> +#define STARFIVE_POST_PLL_FB_DIV_8(x)            UPDATE(((x) >> 8) << 4, 4, 4)
>> >> +
>> >> +/* REG:0x1ab */
>> >> +#define STARFIVE_POST_PLL_DIV_2                0x1ab
>> >> +#define STARFIVE_POST_PLL_Pre_DIV_MASK            GENMASK(5, 0)
>> >> +#define STARFIVE_POST_PLL_PRE_DIV(x)            UPDATE(x, 5, 0)
>> >> +
>> >> +/* REG: 0x1ac */
>> >> +#define STARFIVE_POST_PLL_DIV_3                0x1ac
>> >> +#define STARFIVE_POST_PLL_FB_DIV_7_0(x)            UPDATE(x, 7, 0)
>> >> +
>> >> +/* REG: 0x1ad */
>> >> +#define STARFIVE_POST_PLL_DIV_4                0x1ad
>> >> +#define STARFIVE_POST_PLL_POST_DIV_MASK            GENMASK(2, 0)
>> >> +#define STARFIVE_POST_PLL_POST_DIV_2            0x0
>> >> +#define STARFIVE_POST_PLL_POST_DIV_4            0x1
>> >> +#define STARFIVE_POST_PLL_POST_DIV_8            0x3
>> >> +
>> >> +/* REG: 0x1af */
>> >> +#define STARFIVE_POST_PLL_LOCK_STATUS            0x1af
>> >> +
>> >> +/* REG: 0x1b0 */
>> >> +#define STARFIVE_BIAS_CONTROL                0x1b0
>> >> +#define STARFIVE_BIAS_ENABLE                BIT(2)
>> >> +
>> >> +/* REG: 0x1b2 */
>> >> +#define STARFIVE_TMDS_CONTROL                0x1b2
>> >> +#define STARFIVE_TMDS_CLK_DRIVER_EN            BIT(3)
>> >> +#define STARFIVE_TMDS_D2_DRIVER_EN            BIT(2)
>> >> +#define STARFIVE_TMDS_D1_DRIVER_EN            BIT(1)
>> >> +#define STARFIVE_TMDS_D0_DRIVER_EN            BIT(0)
>> >> +#define STARFIVE_TMDS_DRIVER_ENABLE            (STARFIVE_TMDS_CLK_DRIVER_EN | \
>> >> +                             STARFIVE_TMDS_D2_DRIVER_EN | \
>> >> +                             STARFIVE_TMDS_D1_DRIVER_EN | \
>> >> +                             STARFIVE_TMDS_D0_DRIVER_EN)
>> >> +
>> >> +/* REG: 0x1b4 */
>> >> +#define STARFIVE_LDO_CONTROL                0x1b4
>> >> +#define STARFIVE_LDO_D2_EN                BIT(2)
>> >> +#define STARFIVE_LDO_D1_EN                BIT(1)
>> >> +#define STARFIVE_LDO_D0_EN                BIT(0)
>> >> +#define STARFIVE_LDO_ENABLE                (STARFIVE_LDO_D2_EN | \
>> >> +                             STARFIVE_LDO_D1_EN | \
>> >> +                             STARFIVE_LDO_D0_EN)
>> >> +
>> >> +/* REG: 0x1be */
>> >> +#define STARFIVE_SERIALIER_CONTROL            0x1be
>> >> +#define STARFIVE_SERIALIER_D2_EN            BIT(6)
>> >> +#define STARFIVE_SERIALIER_D1_EN            BIT(5)
>> >> +#define STARFIVE_SERIALIER_D0_EN            BIT(4)
>> >> +#define STARFIVE_SERIALIER_EN                BIT(0)
>> >> +
>> >> +#define STARFIVE_SERIALIER_ENABLE            (STARFIVE_SERIALIER_D2_EN | \
>> >> +                             STARFIVE_SERIALIER_D1_EN | \
>> >> +                             STARFIVE_SERIALIER_D0_EN | \
>> >> +                             STARFIVE_SERIALIER_EN)
>> >> +
>> >> +/* REG: 0x1cc */
>> >> +#define STARFIVE_RX_CONTROL                0x1cc
>> >> +#define STARFIVE_RX_EN                    BIT(3)
>> >> +#define STARFIVE_RX_CHANNEL_2_EN            BIT(2)
>> >> +#define STARFIVE_RX_CHANNEL_1_EN            BIT(1)
>> >> +#define STARFIVE_RX_CHANNEL_0_EN            BIT(0)
>> >> +#define STARFIVE_RX_ENABLE                (STARFIVE_RX_EN | \
>> >> +                             STARFIVE_RX_CHANNEL_2_EN | \
>> >> +                             STARFIVE_RX_CHANNEL_1_EN | \
>> >> +                             STARFIVE_RX_CHANNEL_0_EN)
>> >> +
>> >> +/* REG: 0x1d1 */
>> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_H            0x1d1
>> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_23_16(x)        UPDATE((x) >> 16, 7, 0)
>> >> +/* REG: 0x1d2 */
>> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_M            0x1d2
>> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_15_8(x)        UPDATE((x) >> 8, 7, 0)
>> >> +/* REG: 0x1d3 */
>> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_L            0x1d3
>> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_7_0(x)        UPDATE(x, 7, 0)
>> >> +
>> >> +struct pre_pll_config {
>> >> +    unsigned long pixclock;
>> >> +    unsigned long tmdsclock;
>> >> +    u8 prediv;
>> >> +    u16 fbdiv;
>> >> +    u8 tmds_div_a;
>> >> +    u8 tmds_div_b;
>> >> +    u8 tmds_div_c;
>> >> +    u8 pclk_div_a;
>> >> +    u8 pclk_div_b;
>> >> +    u8 pclk_div_c;
>> >> +    u8 pclk_div_d;
>> >> +    u8 vco_div_5_en;
>> >> +    u32 fracdiv;
>> >> +};
>> >> +
>> >> +struct post_pll_config {
>> >> +    unsigned long tmdsclock;
>> >> +    u8 prediv;
>> >> +    u16 fbdiv;
>> >> +    u8 postdiv;
>> >> +    u8 post_div_en;
>> >> +    u8 version;
>> >> +};
>> >> +
>> >> +struct phy_config {
>> >> +    unsigned long    tmdsclock;
>> >> +    u8        regs[14];
>> >> +};
>> >> +
>> >> +struct hdmi_data_info {
>> >> +    int vic;
>> >> +    bool sink_is_hdmi;
>> >> +    bool sink_has_audio;
>> >> +    unsigned int enc_in_format;
>> >> +    unsigned int enc_out_format;
>> >> +    unsigned int colorimetry;
>> >> +};
>> >> +
>> >> +struct starfive_hdmi {
>> >> +    struct device *dev;
>> >> +    struct drm_device *drm_dev;
>> >> +    struct drm_encoder    encoder;
>> >> +    struct drm_connector    connector;
>> >> +
>> >> +    struct starfive_hdmi_i2c *i2c;
>> >> +
>> >> +    int irq;
>> >> +    struct clk *sys_clk;
>> >> +    struct clk *mclk;
>> >> +    struct clk *bclk;
>> >> +    struct reset_control *tx_rst;
>> >> +    void __iomem *regs;
>> >> +
>> >> +    struct i2c_adapter *ddc;
>> >> +
>> >> +    unsigned long tmds_rate;
>> >> +
>> >> +    struct hdmi_data_info    hdmi_data;
>> >> +    struct drm_display_mode previous_mode;
>> >> +    const struct pre_pll_config    *pre_cfg;
>> >> +    const struct post_pll_config    *post_cfg;
>> >> +};
>> >> +
>> >> +#endif /* __STARFIVE_HDMI_H__ */
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
>> >> index 3cd533cfa..9669354bd 100644
>> >> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
>> >> @@ -126,6 +126,11 @@ static const struct component_master_ops vs_drm_ops = {
>> >>   static struct platform_driver *drm_sub_drivers[] = {
>> >>       &dc_platform_driver,
>> >>   +    /* connector + encoder*/
>> >> +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
>> >> +    &starfive_hdmi_driver,
>> >> +#endif
>> >> +
>> >>   };
>> >>     static struct component_match *vs_drm_match_add(struct device *dev)
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
>> >> index 369ad22d6..e90723419 100644
>> >> --- a/drivers/gpu/drm/verisilicon/vs_drv.h
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_drv.h
>> >> @@ -24,4 +24,8 @@ to_vs_drm_private(const struct drm_device *dev)
>> >>       return container_of(dev, struct vs_drm_device, base);
>> >>   }
>> >>   +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
>> >> +extern struct platform_driver starfive_hdmi_driver;
>> >> +#endif
>> >> +
>> >>   #endif /* __VS_DRV_H__ */
>> >
> 
> 
> 

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 6/6] drm/vs: Add hdmi driver
@ 2023-11-14  8:27           ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-11-14  8:27 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Maxime Ripard, Jagan Teki, Rob Herring,
	Chris Morgan, Paul Walmsley, Bjorn Andersson, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Shawn Guo, christian.koenig



On 2023/11/13 21:31, Dmitry Baryshkov wrote:
> On Mon, 13 Nov 2023 at 14:11, Keith Zhao <keith.zhao@starfivetech.com> wrote:
>>
>>
>>
>> On 2023/10/26 6:23, Dmitry Baryshkov wrote:
>> > On 25/10/2023 13:39, Keith Zhao wrote:
>> >> add hdmi driver as encoder and connect
>> >>
>> >> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>> >> ---
>> >>   drivers/gpu/drm/verisilicon/Kconfig         |   8 +-
>> >>   drivers/gpu/drm/verisilicon/Makefile        |   1 +
>> >>   drivers/gpu/drm/verisilicon/starfive_hdmi.c | 949 ++++++++++++++++++++
>> >>   drivers/gpu/drm/verisilicon/starfive_hdmi.h | 295 ++++++
>> >>   drivers/gpu/drm/verisilicon/vs_drv.c        |   5 +
>> >>   drivers/gpu/drm/verisilicon/vs_drv.h        |   4 +
>> >>   6 files changed, 1261 insertions(+), 1 deletion(-)
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.c
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.h
>> >>
>> >> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
>> >> index 3a361f8c8..122c786e3 100644
>> >> --- a/drivers/gpu/drm/verisilicon/Kconfig
>> >> +++ b/drivers/gpu/drm/verisilicon/Kconfig
>> >> @@ -12,4 +12,10 @@ config DRM_VERISILICON
>> >>         setting and buffer management. It does not
>> >>         provide 2D or 3D acceleration.
>> >>   -
>> >> +config DRM_VERISILICON_STARFIVE_HDMI
>> >> +    bool "Starfive HDMI extensions"
>> >> +    depends on DRM_VERISILICON
>> >> +    help
>> >> +       This selects support for StarFive soc specific extensions
>> >> +       for the Innosilicon HDMI driver. If you want to enable
>> >> +       HDMI on JH7110 based soc, you should select this option.
>> >> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
>> >> index 1d48016ca..08350f25b 100644
>> >> --- a/drivers/gpu/drm/verisilicon/Makefile
>> >> +++ b/drivers/gpu/drm/verisilicon/Makefile
>> >> @@ -7,5 +7,6 @@ vs_drm-objs := vs_dc_hw.o \
>> >>           vs_modeset.o \
>> >>           vs_plane.o
>> >>   +vs_drm-$(CONFIG_DRM_VERISILICON_STARFIVE_HDMI) += starfive_hdmi.o
>> >>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>> >>   diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.c b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
>> >> new file mode 100644
>> >> index 000000000..d296c4b71
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
>> >> @@ -0,0 +1,949 @@
>> >> +// SPDX-License-Identifier: GPL-2.0-only
>> >> +/*
>> >> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
>> >> + */
>> >> +
>> >> +#include <linux/clk.h>
>> >> +#include <linux/component.h>
>> >> +#include <linux/delay.h>
>> >> +#include <linux/err.h>
>> >> +#include <linux/hdmi.h>
>> >> +#include <linux/i2c.h>
>> >> +#include <linux/irq.h>
>> >> +#include <linux/media-bus-format.h>
>> >> +#include <linux/mfd/syscon.h>
>> >> +#include <linux/module.h>
>> >> +#include <linux/mutex.h>
>> >> +#include <linux/of_device.h>
>> >> +#include <linux/pm_runtime.h>
>> >> +#include <linux/reset.h>
>> >> +
>> >> +#include <drm/bridge/dw_hdmi.h>
>> >> +#include <drm/drm_atomic_helper.h>
>> >> +#include <drm/drm_edid.h>
>> >> +#include <drm/drm_managed.h>
>> >> +#include <drm/drm_of.h>
>> >> +#include <drm/drm_probe_helper.h>
>> >> +#include <drm/drm_simple_kms_helper.h>
>> >> +
>> >> +#include "starfive_hdmi.h"
>> >> +#include "vs_drv.h"
>> >> +#include "vs_crtc.h"
>> >> +
>> >> +static struct starfive_hdmi *encoder_to_hdmi(struct drm_encoder *encoder)
>> >> +{
>> >> +    return container_of(encoder, struct starfive_hdmi, encoder);
>> >> +}
>> >> +
>> >> +static struct starfive_hdmi *connector_to_hdmi(struct drm_connector *connector)
>> >> +{
>> >> +    return container_of(connector, struct starfive_hdmi, connector);
>> >> +}
>> >> +
>> >> +struct starfive_hdmi_i2c {
>> >> +    struct i2c_adapter adap;
>> >> +
>> >> +    u8 ddc_addr;
>> >> +    u8 segment_addr;
>> >> +    /* protects the edid data when use i2c cmd to read edid */
>> >> +    struct mutex lock;
>> >> +    struct completion cmp;
>> >> +};
>> >> +
>> >> +static const struct pre_pll_config pre_pll_cfg_table[] = {
>> >> +    { 25175000,  25175000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0xf55555},
>> >> +    { 25200000,  25200000, 1,  100, 2, 3, 3, 12, 3, 3, 4, 0, 0},
>> >> +    { 27000000,  27000000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0},
>> >
>> > Such data structures are usually pretyt limited and hard to handle. Could you please replace it with the runtime calculations of
>> >
>> >> +    { 27027000,  27027000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0x170a3d},
>> >> +    { 28320000,  28320000, 1,  28, 2, 1, 1,  3, 0, 3, 4, 0, 0x51eb85},
>> >> +    { 30240000,  30240000, 1,  30, 2, 1, 1,  3, 0, 3, 4, 0, 0x3d70a3},
>> >> +    { 31500000,  31500000, 1,  31, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> >> +    { 33750000,  33750000, 1,  33, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
>> >> +    { 36000000,  36000000, 1,  36, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    { 40000000,  40000000, 1,  80, 2, 2, 2, 12, 2, 2, 2, 0, 0},
>> >> +    { 46970000,  46970000, 1,  46, 2, 1, 1,  3, 0, 3, 4, 0, 0xf851eb},
>> >> +    { 49500000,  49500000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> >> +    { 49000000,  49000000, 1,  49, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    { 50000000,  50000000, 1,  50, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    { 54000000,  54000000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    { 54054000,  54054000, 1,  54, 2, 1, 1,  3, 0, 3, 4, 0, 0x0dd2f1},
>> >> +    { 57284000,  57284000, 1,  57, 2, 1, 1,  3, 0, 3, 4, 0, 0x48b439},
>> >> +    { 58230000,  58230000, 1,  58, 2, 1, 1,  3, 0, 3, 4, 0, 0x3ae147},
>> >> +    { 59341000,  59341000, 1,  59, 2, 1, 1,  3, 0, 3, 4, 0, 0x574bc6},
>> >> +    { 59400000,  59400000, 1,  99, 3, 1, 1,  1, 3, 3, 4, 0, 0},
>> >> +    { 65000000,  65000000, 1, 130, 2, 2, 2,  12, 0, 2, 2, 0, 0},
>> >> +    { 68250000,  68250000, 1, 68,  2, 1, 1,  3,  0, 3, 4, 0, 0x3fffff},
>> >> +    { 71000000,  71000000, 1,  71, 2, 1, 1,  3, 0, 3,  4, 0, 0},
>> >> +    { 74176000,  74176000, 1,  98, 1, 2, 2,  1, 2, 3, 4, 0, 0xe6ae6b},
>> >> +    { 74250000,  74250000, 1,  99, 1, 2, 2,  1, 2, 3, 4, 0, 0},
>> >> +    { 75000000,  75000000, 1,  75, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    { 78750000,  78750000, 1,  78, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
>> >> +    { 79500000,  79500000, 1,  79, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> >> +    { 83500000,  83500000, 2, 167, 2, 1, 1,  1, 0, 0,  6, 0, 0},
>> >> +    { 83500000, 104375000, 1, 104, 2, 1, 1,  1, 1, 0,  5, 0, 0x600000},
>> >> +    { 84858000,  84858000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xdba5e2},
>> >> +    { 85500000,  85500000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
>> >> +    { 85750000,  85750000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
>> >> +    { 85800000,  85800000, 1,  85, 2, 1, 1,  3, 0, 3,  4, 0, 0xcccccc},
>> >> +    { 88750000,  88750000, 1,  88, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
>> >> +    { 89910000,  89910000, 1,  89, 2, 1, 1,  3, 0, 3, 4, 0, 0xe8f5c1},
>> >> +    { 90000000,  90000000, 1,  90, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {101000000, 101000000, 1, 101, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {102250000, 102250000, 1, 102, 2, 1, 1,  3, 0, 3, 4, 0, 0x3fffff},
>> >> +    {106500000, 106500000, 1, 106, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> >> +    {108000000, 108000000, 1,  90, 3, 0, 0,  5, 0, 2,  2, 0, 0},
>> >> +    {119000000, 119000000, 1, 119, 2, 1, 1,  3, 0, 3,  4, 0, 0},
>> >> +    {131481000, 131481000, 1,  131, 2, 1, 1,  3, 0, 3,  4, 0, 0x7b22d1},
>> >> +    {135000000, 135000000, 1,  135, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {136750000, 136750000, 1,  136, 2, 1, 1,  3, 0, 3, 4, 0, 0xcfffff},
>> >> +    {147180000, 147180000, 1,  147, 2, 1, 1,  3, 0, 3, 4, 0, 0x2e147a},
>> >> +    {148352000, 148352000, 1,  98, 1, 1, 1,  1, 2, 2, 2, 0, 0xe6ae6b},
>> >> +    {148500000, 148500000, 1,  99, 1, 1, 1,  1, 2, 2, 2, 0, 0},
>> >> +    {154000000, 154000000, 1, 154, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {156000000, 156000000, 1, 156, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {157000000, 157000000, 1, 157, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {162000000, 162000000, 1, 162, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {174250000, 174250000, 1, 145, 3, 0, 0,  5, 0, 2, 2, 0, 0x355555},
>> >> +    {174500000, 174500000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> >> +    {174570000, 174570000, 1, 174, 2, 1, 1,  3, 0, 3, 4, 0, 0x91eb84},
>> >> +    {175500000, 175500000, 1, 175, 2, 1, 1,  3, 0, 3, 4, 0, 0x7fffff},
>> >> +    {185590000, 185590000, 1, 185, 2, 1, 1,  3, 0, 3, 4, 0, 0x970a3c},
>> >> +    {187000000, 187000000, 1, 187, 2, 1, 1,  3, 0, 3, 4, 0, 0},
>> >> +    {241500000, 241500000, 1, 161, 1, 1, 1,  4, 0, 2,  2, 0, 0},
>> >> +    {241700000, 241700000, 1, 241, 2, 1, 1,  3, 0, 3,  4, 0, 0xb33332},
>> >> +    {262750000, 262750000, 1, 262, 2, 1, 1,  3, 0, 3,  4, 0, 0xcfffff},
>> >> +    {296500000, 296500000, 1, 296, 2, 1, 1,  3, 0, 3,  4, 0, 0x7fffff},
>> >> +    {296703000, 296703000, 1,  98, 0, 1, 1,  1, 0, 2,  2, 0, 0xe6ae6b},
>> >> +    {297000000, 297000000, 1,  99, 0, 1, 1,  1, 0, 2,  2, 0, 0},
>> >> +    {594000000, 594000000, 1,  99, 0, 2, 0,  1, 0, 1,  1, 0, 0},
>> >> +    {0, 0, 0,  0, 0, 0, 0,  0, 0, 0,  0, 0, 0},
>> >> +};
>> >> +
>> >> +static const struct post_pll_config post_pll_cfg_table[] = {
>> >> +    {25200000,    1, 80, 13, 3, 1},
>> >> +    {27000000,    1, 40, 11, 3, 1},
>> >> +    {33750000,    1, 40, 11, 3, 1},
>> >> +    {49000000,    1, 20, 1, 3, 3},
>> >> +    {241700000, 1, 20, 1, 3, 3},
>> >> +    {297000000, 4, 20, 0, 0, 3},
>> >> +    {594000000, 4, 20, 0, 0, 0},
>> >> +    { /* sentinel */ }
>> >> +};
>> >> +
>> >> +inline u8 hdmi_readb(struct starfive_hdmi *hdmi, u16 offset)
>> >> +{
>> >> +    return readl_relaxed(hdmi->regs + (offset) * 0x04);
>> >> +}
>> >> +
>> >> +inline void hdmi_writeb(struct starfive_hdmi *hdmi, u16 offset, u32 val)
>> >> +{
>> >> +    writel_relaxed(val, hdmi->regs + (offset) * 0x04);
>> >> +}
>> >> +
>> >> +inline void hdmi_modb(struct starfive_hdmi *hdmi, u16 offset,
>> >> +                 u32 msk, u32 val)
>> >> +{
>> >> +    u8 temp = hdmi_readb(hdmi, offset) & ~msk;
>> >> +
>> >> +    temp |= val & msk;
>> >> +    hdmi_writeb(hdmi, offset, temp);
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_enable_clk_deassert_rst(struct device *dev, struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    int ret;
>> >> +
>> >> +    ret = clk_prepare_enable(hdmi->sys_clk);
>> >> +    if (ret) {
>> >> +        dev_err(dev, "Cannot enable HDMI sys clock: %d\n", ret);
>> >> +        return ret;
>> >> +    }
>> >> +
>> >> +    ret = clk_prepare_enable(hdmi->mclk);
>> >> +    if (ret) {
>> >> +        dev_err(dev, "Cannot enable HDMI mclk clock: %d\n", ret);
>> >> +        goto err_mclk;
>> >> +    }
>> >> +    ret = clk_prepare_enable(hdmi->bclk);
>> >
>> > This code begs to use clk_bulk instead.
>> >
>> >> +    if (ret) {
>> >> +        dev_err(dev, "Cannot enable HDMI bclk clock: %d\n", ret);
>> >> +        goto err_bclk;
>> >> +    }
>> >> +    ret = reset_control_deassert(hdmi->tx_rst);
>> >> +    if (ret < 0) {
>> >> +        dev_err(dev, "failed to deassert tx_rst\n");
>> >> +        goto err_rst;
>> >> +    }
>> >> +    return 0;
>> >> +
>> >> +err_rst:
>> >> +    clk_disable_unprepare(hdmi->bclk);
>> >> +err_bclk:
>> >> +    clk_disable_unprepare(hdmi->mclk);
>> >> +err_mclk:
>> >> +    clk_disable_unprepare(hdmi->sys_clk);
>> >> +    return ret;
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_disable_clk_assert_rst(struct device *dev, struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    int ret;
>> >> +
>> >> +    ret = reset_control_assert(hdmi->tx_rst);
>> >> +    if (ret < 0)
>> >> +        dev_err(dev, "failed to assert tx_rst\n");
>> >> +
>> >> +    clk_disable_unprepare(hdmi->sys_clk);
>> >> +    clk_disable_unprepare(hdmi->mclk);
>> >> +    clk_disable_unprepare(hdmi->bclk);
>> >> +}
>> >> +
>> >> +#ifdef CONFIG_PM_SLEEP
>> >> +static int hdmi_system_pm_suspend(struct device *dev)
>> >> +{
>> >> +    return pm_runtime_force_suspend(dev);
>> >> +}
>> >> +
>> >> +static int hdmi_system_pm_resume(struct device *dev)
>> >> +{
>> >> +    return pm_runtime_force_resume(dev);
>> >> +}
>> >> +#endif
>> >> +
>> >> +#ifdef CONFIG_PM
>> >> +static int hdmi_runtime_suspend(struct device *dev)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
>> >> +
>> >> +    starfive_hdmi_disable_clk_assert_rst(dev, hdmi);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int hdmi_runtime_resume(struct device *dev)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = dev_get_drvdata(dev);
>> >> +
>> >> +    return starfive_hdmi_enable_clk_deassert_rst(dev, hdmi);
>> >> +}
>> >> +#endif
>> >> +
>> >> +static void starfive_hdmi_tx_phy_power_down(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_tx_phy_power_on(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
>> >
>> > It looks like one can inline these two helpers w/o too much troubles.
>> >
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_config_pll(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    u32 val;
>> >> +    u8 reg_1ad_value = hdmi->post_cfg->post_div_en ?
>> >> +         hdmi->post_cfg->postdiv : 0x00;
>> >> +    u8 reg_1aa_value = hdmi->post_cfg->post_div_en ?
>> >> +         0x0e : 0x02;
>> >> +
>> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN);
>> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1,
>> >> +            STARFIVE_POST_PLL_POST_DIV_ENABLE |
>> >> +            STARFIVE_POST_PLL_REFCLK_SEL_TMDS |
>> >> +            STARFIVE_POST_PLL_POWER_DOWN);
>> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_1, STARFIVE_PRE_PLL_PRE_DIV(hdmi->pre_cfg->prediv));
>> >> +
>> >> +    val = STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE | STARFIVE_SPREAD_SPECTRUM_MOD_DOWN;
>> >> +    if (!hdmi->pre_cfg->fracdiv)
>> >> +        val |= STARFIVE_PRE_PLL_FRAC_DIV_DISABLE;
>> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_2,
>> >> +            STARFIVE_PRE_PLL_FB_DIV_11_8(hdmi->pre_cfg->fbdiv) | val);
>> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_3,
>> >> +            STARFIVE_PRE_PLL_FB_DIV_7_0(hdmi->pre_cfg->fbdiv));
>> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_4,
>> >> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_C(hdmi->pre_cfg->tmds_div_c) |
>> >> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_A(hdmi->pre_cfg->tmds_div_a) |
>> >> +            STARFIVE_PRE_PLL_TMDSCLK_DIV_B(hdmi->pre_cfg->tmds_div_b));
>> >> +
>> >> +    if (hdmi->pre_cfg->fracdiv) {
>> >> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_L,
>> >> +                STARFIVE_PRE_PLL_FRAC_DIV_7_0(hdmi->pre_cfg->fracdiv));
>> >> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_M,
>> >> +                STARFIVE_PRE_PLL_FRAC_DIV_15_8(hdmi->pre_cfg->fracdiv));
>> >> +        hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_H,
>> >> +                STARFIVE_PRE_PLL_FRAC_DIV_23_16(hdmi->pre_cfg->fracdiv));
>> >> +    }
>> >> +
>> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_5,
>> >> +            STARFIVE_PRE_PLL_PCLK_DIV_A(hdmi->pre_cfg->pclk_div_a) |
>> >> +            STARFIVE_PRE_PLL_PCLK_DIV_B(hdmi->pre_cfg->pclk_div_b));
>> >> +    hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_6,
>> >> +            STARFIVE_PRE_PLL_PCLK_DIV_C(hdmi->pre_cfg->pclk_div_c) |
>> >> +            STARFIVE_PRE_PLL_PCLK_DIV_D(hdmi->pre_cfg->pclk_div_d));
>> >> +
>> >> +    /*pre-pll power down*/
>> >> +    hdmi_modb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN, 0);
>> >> +
>> >> +    hdmi_modb(hdmi, STARFIVE_POST_PLL_DIV_2, STARFIVE_POST_PLL_Pre_DIV_MASK,
>> >> +          STARFIVE_POST_PLL_PRE_DIV(hdmi->post_cfg->prediv));
>> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_3, hdmi->post_cfg->fbdiv & 0xff);
>> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_4, reg_1ad_value);
>> >> +    hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1, reg_1aa_value);
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_tmds_driver_on(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    hdmi_modb(hdmi, STARFIVE_TMDS_CONTROL,
>> >> +          STARFIVE_TMDS_DRIVER_ENABLE, STARFIVE_TMDS_DRIVER_ENABLE);
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_sync_tmds(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    /*first send 0 to this bit, then send 1 and keep 1 into this bit*/
>> >> +    hdmi_writeb(hdmi, HDMI_SYNC, 0x0);
>> >> +    hdmi_writeb(hdmi, HDMI_SYNC, 0x1);
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_i2c_init(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    int ddc_bus_freq;
>> >> +
>> >> +    ddc_bus_freq = (clk_get_rate(hdmi->sys_clk) >> 2) / HDMI_SCL_RATE;
>> >> +
>> >> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
>> >> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
>> >> +
>> >> +    /* Clear the EDID interrupt flag and mute the interrupt */
>> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
>> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> >> +}
>> >> +
>> >> +static const
>> >> +struct pre_pll_config *starfive_hdmi_phy_get_pre_pll_cfg(struct starfive_hdmi *hdmi,
>> >> +                             unsigned long rate)
>> >> +{
>> >> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
>> >> +
>> >> +    rate = (rate / 1000) * 1000;
>> >> +    for (; cfg->pixclock != 0; cfg++)
>> >> +        if (cfg->tmdsclock == rate && cfg->pixclock == rate)
>> >> +            break;
>> >> +
>> >> +    if (cfg->pixclock == 0)
>> >> +        return ERR_PTR(-EINVAL);
>> >> +
>> >> +    return cfg;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_phy_clk_set_rate(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    hdmi->post_cfg = post_pll_cfg_table;
>> >> +
>> >> +    hdmi->pre_cfg = starfive_hdmi_phy_get_pre_pll_cfg(hdmi, hdmi->tmds_rate);
>> >> +    if (IS_ERR(hdmi->pre_cfg))
>> >> +        return PTR_ERR(hdmi->pre_cfg);
>> >> +
>> >> +    for (; hdmi->post_cfg->tmdsclock != 0; hdmi->post_cfg++)
>> >> +        if (hdmi->tmds_rate <= hdmi->post_cfg->tmdsclock)
>> >> +            break;
>> >> +
>> >> +    starfive_hdmi_config_pll(hdmi);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_config_video_timing(struct starfive_hdmi *hdmi,
>> >> +                         struct drm_display_mode *mode)
>> >> +{
>> >> +    int value;
>> >> +    /* Set detail external video timing */
>> >> +    value = mode->htotal;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
>> >
>> > As you have hdmi_writeb already, adding hdmi_writew will help here.
>> >
>> >> +
>> >> +    value = mode->htotal - mode->hdisplay;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
>> >> +
>> >> +    value = mode->htotal - mode->hsync_start;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
>> >> +
>> >> +    value = mode->hsync_end - mode->hsync_start;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
>> >> +
>> >> +    value = mode->vtotal;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
>> >> +
>> >> +    value = mode->vtotal - mode->vdisplay;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
>> >> +
>> >> +    value = mode->vtotal - mode->vsync_start;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
>> >> +
>> >> +    value = mode->vsync_end - mode->vsync_start;
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
>> >> +
>> >> +    /* Set detail external video timing polarity and interlace mode */
>> >> +    value = v_EXTERANL_VIDEO(1);
>> >> +    value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
>> >> +        v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
>> >> +    value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
>> >> +        v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
>> >> +    value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
>> >> +        v_INETLACE(1) : v_INETLACE(0);
>> >> +
>> >> +    hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_setup(struct starfive_hdmi *hdmi,
>> >> +                   struct drm_display_mode *mode)
>> >> +{
>> >> +    hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
>> >> +    hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
>> >> +    hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
>> >> +
>> >> +    hdmi->tmds_rate = mode->clock * 1000;
>> >> +    starfive_hdmi_phy_clk_set_rate(hdmi);
>> >> +
>> >> +    while (!(hdmi_readb(hdmi, STARFIVE_PRE_PLL_LOCK_STATUS) & 0x1))
>> >> +        continue;
>> >> +    while (!(hdmi_readb(hdmi, STARFIVE_POST_PLL_LOCK_STATUS) & 0x1))
>> >> +        continue;
>> >
>> > Please use read_iopoll_timeout here.
>> >
>> >> +
>> >> +    /*turn on LDO*/
>> >> +    hdmi_writeb(hdmi, STARFIVE_LDO_CONTROL, STARFIVE_LDO_ENABLE);
>> >> +    /*turn on serializer*/
>> >> +    hdmi_writeb(hdmi, STARFIVE_SERIALIER_CONTROL, STARFIVE_SERIALIER_ENABLE);
>> >> +
>> >> +    starfive_hdmi_tx_phy_power_down(hdmi);
>> >> +    starfive_hdmi_config_video_timing(hdmi, mode);
>> >> +    starfive_hdmi_tx_phy_power_on(hdmi);
>> >> +
>> >> +    starfive_hdmi_tmds_driver_on(hdmi);
>> >> +    starfive_hdmi_sync_tmds(hdmi);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_encoder_mode_set(struct drm_encoder *encoder,
>> >> +                       struct drm_display_mode *mode,
>> >> +                       struct drm_display_mode *adj_mode)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
>> >> +
>> >> +    drm_mode_copy(&hdmi->previous_mode, adj_mode);
>> >
>> > Why do you need it?
>> >
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_encoder_enable(struct drm_encoder *encoder)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
>> >> +    struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
>> >> +    int ret, idx;
>> >> +    struct drm_device *drm = hdmi->connector.dev;
>> >> +
>> >> +    if (drm && !drm_dev_enter(drm, &idx))
>> >> +        return;
>> >> +
>> >> +    ret = pm_runtime_get_sync(hdmi->dev);
>> >> +    if (ret < 0)
>> >> +        return;
>> >> +    mdelay(10);
>> >> +    starfive_hdmi_setup(hdmi, mode);
>> >> +
>> >> +    if (drm)
>> >> +        drm_dev_exit(idx);
>> >> +}
>> >> +
>> >> +static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder);
>> >> +
>> >> +    int idx;
>> >> +    struct drm_device *drm = hdmi->connector.dev;
>> >> +
>> >> +    if (drm && !drm_dev_enter(drm, &idx))
>> >> +        return;
>> >> +
>> >> +    pm_runtime_put(hdmi->dev);
>> >> +
>> >> +    if (drm)
>> >> +        drm_dev_exit(idx);
>> >> +}
>> >> +
>> >> +static int
>> >> +starfive_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
>> >> +                   struct drm_crtc_state *crtc_state,
>> >> +                   struct drm_connector_state *conn_state)
>> >> +{
>> >> +    bool valid = false;
>> >> +    struct drm_display_mode *mode = &crtc_state->adjusted_mode;
>> >> +    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
>> >> +
>> >> +    vs_crtc_state->encoder_type = encoder->encoder_type;
>> >> +    vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
>> >> +
>> >> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
>> >> +    int pclk = mode->clock * 1000;
>> >> +
>> >> +    for (; cfg->pixclock != 0; cfg++) {
>> >> +        if (pclk == cfg->pixclock) {
>> >> +            if (pclk > 297000000)
>> >
>> > Magic value.
>> >
>> >> +                continue;
>> >> +
>> >> +            valid = true;
>> >> +            break;
>> >> +        }
>> >> +    }
>> >> +
>> >> +    return (valid) ? 0 : -EINVAL;
>> >> +}
>> >> +
>> >> +static const struct drm_encoder_helper_funcs starfive_hdmi_encoder_helper_funcs = {
>> >> +    .enable     = starfive_hdmi_encoder_enable,
>> >> +    .disable    = starfive_hdmi_encoder_disable,
>> >> +    .mode_set   = starfive_hdmi_encoder_mode_set,
>> >> +    .atomic_check = starfive_hdmi_encoder_atomic_check,
>> >> +};
>> >> +
>> >> +static enum drm_connector_status
>> >> +starfive_hdmi_connector_detect(struct drm_connector *connector, bool force)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
>> >> +    struct drm_device *drm = hdmi->connector.dev;
>> >> +    int ret;
>> >> +    int idx;
>> >> +
>> >> +    if (drm && !drm_dev_enter(drm, &idx))
>> >> +        return connector_status_disconnected;
>> >> +
>> >> +    ret = pm_runtime_get_sync(hdmi->dev);
>> >> +    if (ret < 0)
>> >> +        return ret;
>> >> +
>> >> +    ret = (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
>> >> +        connector_status_connected : connector_status_disconnected;
>> >> +    pm_runtime_put(hdmi->dev);
>> >> +
>> >> +    if (drm)
>> >> +        drm_dev_exit(idx);
>> >> +
>> >> +    return ret;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_connector_get_modes(struct drm_connector *connector)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = connector_to_hdmi(connector);
>> >> +    struct edid *edid;
>> >> +    int ret = 0;
>> >> +
>> >> +    if (!hdmi->ddc)
>> >> +        return 0;
>> >> +    ret = pm_runtime_get_sync(hdmi->dev);
>> >> +    if (ret < 0)
>> >> +        return ret;
>> >> +
>> >> +    edid = drm_get_edid(connector, hdmi->ddc);
>> >> +    if (edid) {
>> >> +        hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
>> >> +        hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
>> >> +        drm_connector_update_edid_property(connector, edid);
>> >> +        ret = drm_add_edid_modes(connector, edid);
>> >> +        kfree(edid);
>> >> +    }
>> >> +    pm_runtime_put(hdmi->dev);
>> >
>> > Can you use drm_connector_helper_get_modes_from_ddc() here? And then drm_display_info.is_hdmi and .has_audio.
>> >
>> >> +
>> >> +    return ret;
>> >> +}
>> >> +
>> >> +static enum drm_mode_status
>> >> +starfive_hdmi_connector_mode_valid(struct drm_connector *connector,
>> >> +                   struct drm_display_mode *mode)
>> >> +{
>> >> +    const struct pre_pll_config *cfg = pre_pll_cfg_table;
>> >> +    int pclk = mode->clock * 1000;
>> >> +    bool valid = false;
>> >> +
>> >> +    for (; cfg->pixclock != 0; cfg++) {
>> >> +        if (pclk == cfg->pixclock) {
>> >> +            if (pclk > 297000000)
>> >
>> > magic value
>> >
>> >> +                continue;
>> >> +
>> >> +            valid = true;
>> >> +            break;
>> >> +        }
>> >> +    }
>> >> +
>> >> +    return (valid) ? MODE_OK : MODE_BAD;
>> >> +}
>> >> +
>> >> +static int
>> >> +starfive_hdmi_probe_single_connector_modes(struct drm_connector *connector,
>> >> +                       u32 maxX, u32 maxY)
>> >> +{
>> >> +    return drm_helper_probe_single_connector_modes(connector, 3840, 2160);
>> >> +}
>> >> +
>> >> +static const struct drm_connector_funcs starfive_hdmi_connector_funcs = {
>> >> +    .fill_modes = starfive_hdmi_probe_single_connector_modes,
>> >> +    .detect = starfive_hdmi_connector_detect,
>> >> +    .reset = drm_atomic_helper_connector_reset,
>> >> +    .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
>> >> +    .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>> >> +};
>> >> +
>> >> +static struct drm_connector_helper_funcs starfive_hdmi_connector_helper_funcs = {
>> >> +    .get_modes = starfive_hdmi_connector_get_modes,
>> >> +    .mode_valid = starfive_hdmi_connector_mode_valid,
>> >> +};
>> >> +
>> >> +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    struct drm_encoder *encoder = &hdmi->encoder;
>> >> +    struct device *dev = hdmi->dev;
>> >> +
>> >> +    encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
>> >> +
>> >> +    /*
>> >> +     * If we failed to find the CRTC(s) which this encoder is
>> >> +     * supposed to be connected to, it's because the CRTC has
>> >> +     * not been registered yet.  Defer probing, and hope that
>> >> +     * the required CRTC is added later.
>> >> +     */
>> >> +    if (encoder->possible_crtcs == 0)
>> >> +        return -EPROBE_DEFER;
>> >> +
>> >> +    drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs);
>> >> +
>> >> +    hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
>> >> +
>> >> +    drm_connector_helper_add(&hdmi->connector,
>> >> +                 &starfive_hdmi_connector_helper_funcs);
>> >> +    drmm_connector_init(drm, &hdmi->connector,
>> >> +                &starfive_hdmi_connector_funcs,
>> >> +                DRM_MODE_CONNECTOR_HDMIA,
>> >
>> > On an embedded device one can not be so sure. There can be MHL or HDMI Alternative Mode. Usually we use drm_bridge here and drm_bridge_connector.
>> >
>> >> +                hdmi->ddc);
>> >> +
>> >> +    drm_connector_attach_encoder(&hdmi->connector, encoder);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static irqreturn_t starfive_hdmi_i2c_irq(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    struct starfive_hdmi_i2c *i2c = hdmi->i2c;
>> >> +    u8 stat;
>> >> +
>> >> +    stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
>> >> +    if (!(stat & m_INT_EDID_READY))
>> >> +        return IRQ_NONE;
>> >> +
>> >> +    /* Clear HDMI EDID interrupt flag */
>> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> >> +
>> >> +    complete(&i2c->cmp);
>> >> +
>> >> +    return IRQ_HANDLED;
>> >> +}
>> >> +
>> >> +static irqreturn_t starfive_hdmi_hardirq(int irq, void *dev_id)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = dev_id;
>> >> +    irqreturn_t ret = IRQ_NONE;
>> >> +    u8 interrupt;
>> >> +
>> >> +    if (hdmi->i2c)
>> >> +        ret = starfive_hdmi_i2c_irq(hdmi);
>> >> +
>> >> +    interrupt = hdmi_readb(hdmi, HDMI_STATUS);
>> >> +    if (interrupt & m_INT_HOTPLUG) {
>> >> +        hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
>> >> +        ret = IRQ_WAKE_THREAD;
>> >> +    }
>> >> +
>> >> +    return ret;
>> >> +}
>> >> +
>> >> +static irqreturn_t starfive_hdmi_irq(int irq, void *dev_id)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = dev_id;
>> >> +
>> >> +    drm_connector_helper_hpd_irq_event(&hdmi->connector);
>> >> +
>> >> +    return IRQ_HANDLED;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_i2c_read(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
>> >> +{
>> >> +    int length = msgs->len;
>> >> +    u8 *buf = msgs->buf;
>> >> +    int ret;
>> >> +
>> >> +    ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10);
>> >
>> > read_poll_timeout
>> Hi Dmitry:
>> I think using wait_for_completion_timeout here is a bit clearer than read_poll_timeout logic
>> I2c_read has hardware interrupt dependency. so need wait the interrupt completion.
>> also it comes with timeout detection.
> 
> Hi,
> 
> Yes, maybe I was thinking about a different piece of code here.
> wait_for_completion looks good here, indeed.
> 
> But this actually brings up another question. You reinit_comletion in
> starfive_hdmi_i2c_write() and then wait_for_completion in
> starfive_hdmi_i2c_read(). How does that work?
> 
> 
struct i2c_msg msgs[2] = {
        {
            .addr = DDC_ADDR,
            .flags = 0,
            .len = 1,
            .buf = (void *)&start,
        },{
            .addr = DDC_ADDR,
            .flags = I2C_M_RD,
            .len = DATA_LEN,
            .buf = buf,
        }
    };

For i2c devices, when reading edid information, the master device needs to send two msg to the slave device. 
The first msg contains the write operation flag(0), the written data length (start length) and the register address that needs to be read (start addr) ; 
The second msg contains the required read operation (I2C_M_RD), the length of the read data (equal to the length of buf) and the address (buf) where the data is read.

the first msg call starfive_hdmi_i2c_write 
and the second call starfive_hdmi_i2c_read

after the first msg send done , A valid interrupt will be generated (complete) ,then  the second msg will continue to read(wait_for_completion)
so that is the reason why nend do the reinit_comletion in starfive_hdmi_i2c_write.
if not do that , the second msg maybe read the edid at a wrong time.

This is my understanding. If there is anything wrong, I hope you will be corrected.

Thanks
> 
>>
>> >
>> >> +    if (!ret)
>> >> +        return -EAGAIN;
>> >> +
>> >> +    while (length--)
>> >> +        *buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_i2c_write(struct starfive_hdmi *hdmi, struct i2c_msg *msgs)
>> >> +{
>> >> +    /*
>> >> +     * The DDC module only support read EDID message, so
>> >> +     * we assume that each word write to this i2c adapter
>> >> +     * should be the offset of EDID word address.
>> >> +     */
>> >> +    if (msgs->len != 1 ||
>> >> +        (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR))
>> >> +        return -EINVAL;
>> >> +
>> >> +    reinit_completion(&hdmi->i2c->cmp);
>> >> +
>> >> +    if (msgs->addr == DDC_SEGMENT_ADDR)
>> >> +        hdmi->i2c->segment_addr = msgs->buf[0];
>> >> +    if (msgs->addr == DDC_ADDR)
>> >> +        hdmi->i2c->ddc_addr = msgs->buf[0];
>> >> +
>> >> +    /* Set edid fifo first addr */
>> >> +    hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
>> >> +
>> >> +    /* Set edid word address 0x00/0x80 */
>> >> +    hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
>> >> +
>> >> +    /* Set edid segment pointer */
>> >> +    hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_i2c_xfer(struct i2c_adapter *adap,
>> >> +                  struct i2c_msg *msgs, int num)
>> >> +{
>> >> +    struct starfive_hdmi *hdmi = i2c_get_adapdata(adap);
>> >> +    struct starfive_hdmi_i2c *i2c = hdmi->i2c;
>> >> +    int i, ret = 0;
>> >> +
>> >> +    mutex_lock(&i2c->lock);
>> >> +
>> >> +    /* Clear the EDID interrupt flag and unmute the interrupt */
>> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
>> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> >> +
>> >> +    for (i = 0; i < num; i++) {
>> >> +        DRM_DEV_DEBUG(hdmi->dev,
>> >> +                  "xfer: num: %d/%d, len: %d, flags: %#x\n",
>> >> +                  i + 1, num, msgs[i].len, msgs[i].flags);
>> >> +
>> >> +        if (msgs[i].flags & I2C_M_RD)
>> >> +            ret = starfive_hdmi_i2c_read(hdmi, &msgs[i]);
>> >> +        else
>> >> +            ret = starfive_hdmi_i2c_write(hdmi, &msgs[i]);
>> >> +
>> >> +        if (ret < 0)
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    if (!ret)
>> >> +        ret = num;
>> >> +
>> >> +    /* Mute HDMI EDID interrupt */
>> >> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
>> >> +
>> >> +    mutex_unlock(&i2c->lock);
>> >> +
>> >> +    return ret;
>> >> +}
>> >> +
>> >> +static u32 starfive_hdmi_i2c_func(struct i2c_adapter *adapter)
>> >> +{
>> >> +    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
>> >> +}
>> >> +
>> >> +static const struct i2c_algorithm starfive_hdmi_algorithm = {
>> >> +    .master_xfer    = starfive_hdmi_i2c_xfer,
>> >> +    .functionality    = starfive_hdmi_i2c_func,
>> >> +};
>> >> +
>> >> +static struct i2c_adapter *starfive_hdmi_i2c_adapter(struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    struct i2c_adapter *adap;
>> >> +    struct starfive_hdmi_i2c *i2c;
>> >> +    int ret;
>> >> +
>> >> +    i2c = drmm_kzalloc(hdmi->drm_dev, sizeof(*i2c), GFP_KERNEL);
>> >> +    if (!i2c)
>> >> +        return ERR_PTR(-ENOMEM);
>> >> +
>> >> +    mutex_init(&i2c->lock);
>> >> +    init_completion(&i2c->cmp);
>> >> +
>> >> +    adap = &i2c->adap;
>> >> +    adap->class = I2C_CLASS_DDC;
>> >> +    adap->owner = THIS_MODULE;
>> >> +    adap->dev.parent = hdmi->dev;
>> >> +    adap->algo = &starfive_hdmi_algorithm;
>> >> +    strscpy(adap->name, "Starfive HDMI", sizeof(adap->name));
>> >> +    i2c_set_adapdata(adap, hdmi);
>> >> +
>> >> +    ret = devm_i2c_add_adapter(hdmi->dev, adap);
>> >> +    if (ret) {
>> >> +        drm_warn(hdmi->drm_dev, "cannot add %s I2C adapter\n", adap->name);
>> >> +        return ERR_PTR(ret);
>> >> +    }
>> >> +
>> >> +    hdmi->i2c = i2c;
>> >> +
>> >> +    drm_info(hdmi->drm_dev, "registered %s I2C bus driver success\n", adap->name);
>> >> +
>> >> +    return adap;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_get_clk_rst(struct device *dev, struct starfive_hdmi *hdmi)
>> >> +{
>> >> +    hdmi->sys_clk = devm_clk_get(dev, "sysclk");
>> >> +    if (IS_ERR(hdmi->sys_clk)) {
>> >> +        dev_err(dev, "Unable to get HDMI sysclk clk\n");
>> >> +        return PTR_ERR(hdmi->sys_clk);
>> >> +    }
>> >> +    hdmi->mclk = devm_clk_get(dev, "mclk");
>> >> +    if (IS_ERR(hdmi->mclk)) {
>> >> +        dev_err(dev, "Unable to get HDMI mclk clk\n");
>> >> +        return PTR_ERR(hdmi->mclk);
>> >> +    }
>> >> +    hdmi->bclk = devm_clk_get(dev, "bclk");
>> >> +    if (IS_ERR(hdmi->bclk)) {
>> >> +        dev_err(dev, "Unable to get HDMI bclk clk\n");
>> >> +        return PTR_ERR(hdmi->bclk);
>> >> +    }
>> >> +    hdmi->tx_rst = devm_reset_control_get_by_index(dev, 0);
>> >> +    if (IS_ERR(hdmi->tx_rst)) {
>> >> +        dev_err(dev, "failed to get tx_rst reset\n");
>> >> +        return PTR_ERR(hdmi->tx_rst);
>> >> +    }
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_bind(struct device *dev, struct device *master,
>> >> +                  void *data)
>> >> +{
>> >> +    struct platform_device *pdev = to_platform_device(dev);
>> >> +    struct drm_device *drm = dev_get_drvdata(master);
>> >> +    struct starfive_hdmi *hdmi;
>> >> +    struct resource *iores;
>> >> +    int irq;
>> >> +    int ret;
>> >> +
>> >> +    hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
>> >> +    if (!hdmi)
>> >> +        return -ENOMEM;
>> >> +
>> >> +    hdmi = drmm_simple_encoder_alloc(drm, struct starfive_hdmi,
>> >> +                     encoder, DRM_MODE_ENCODER_TMDS);
>> >> +
>> >> +    hdmi->dev = dev;
>> >> +    hdmi->drm_dev = drm;
>> >> +    dev_set_drvdata(dev, hdmi);
>> >> +
>> >> +    iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> >> +    hdmi->regs = devm_ioremap_resource(dev, iores);
>> >
>> > This should go to probe
>> >
>> >> +    if (IS_ERR(hdmi->regs))
>> >> +        return PTR_ERR(hdmi->regs);
>> >> +
>> >> +    ret = starfive_hdmi_get_clk_rst(dev, hdmi);
>> >> +    if (ret < 0)
>> >> +        return ret;
>> >
>> > And this
>> >
>> >> +
>> >> +    ret = devm_pm_runtime_enable(dev);
>> >> +    if (ret)
>> >> +        return ret;
>> >> +
>> >> +    ret = pm_runtime_resume_and_get(dev);
>> >> +    if (ret)
>> >> +        return ret;
>> >> +
>> >> +    irq = platform_get_irq(pdev, 0);
>> >
>> > And this
>> >
>> >> +    if (irq < 0) {
>> >> +        ret = irq;
>> >> +        goto err_put_runtime_pm;
>> >> +    }
>> >> +
>> >> +    hdmi->ddc = starfive_hdmi_i2c_adapter(hdmi);
>> >> +    if (IS_ERR(hdmi->ddc)) {
>> >> +        ret = PTR_ERR(hdmi->ddc);
>> >> +        hdmi->ddc = NULL;
>> >> +        goto err_put_runtime_pm;
>> >> +    }
>> >> +
>> >> +    starfive_hdmi_i2c_init(hdmi);
>> >> +
>> >> +    ret = starfive_hdmi_register(drm, hdmi);
>> >> +    if (ret)
>> >> +        goto err_put_adapter;
>> >> +
>> >> +    /* Unmute hotplug interrupt */
>> >> +    hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
>> >> +
>> >> +    ret = devm_request_threaded_irq(dev, irq, starfive_hdmi_hardirq,
>> >> +                    starfive_hdmi_irq, IRQF_SHARED,
>> >> +                    dev_name(dev), hdmi);
>> >> +    if (ret < 0)
>> >> +        goto err_put_adapter;
>> >> +
>> >> +    pm_runtime_put_sync(dev);
>> >> +
>> >> +    return 0;
>> >> +
>> >> +err_put_adapter:
>> >> +    i2c_put_adapter(hdmi->ddc);
>> >> +err_put_runtime_pm:
>> >> +    pm_runtime_put_sync(dev);
>> >> +
>> >> +    return ret;
>> >> +}
>> >> +
>> >> +static const struct component_ops starfive_hdmi_ops = {
>> >> +    .bind    = starfive_hdmi_bind,
>> >> +};
>> >> +
>> >> +static int starfive_hdmi_probe(struct platform_device *pdev)
>> >> +{
>> >> +    return component_add(&pdev->dev, &starfive_hdmi_ops);
>> >> +}
>> >> +
>> >> +static int starfive_hdmi_remove(struct platform_device *pdev)
>> >> +{
>> >> +    component_del(&pdev->dev, &starfive_hdmi_ops);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static const struct dev_pm_ops hdmi_pm_ops = {
>> >> +    SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
>> >> +    SET_LATE_SYSTEM_SLEEP_PM_OPS(hdmi_system_pm_suspend, hdmi_system_pm_resume)
>> >> +};
>> >> +
>> >> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
>> >> +    { .compatible = "starfive,jh7110-inno-hdmi",},
>> >> +    {},
>> >> +};
>> >> +MODULE_DEVICE_TABLE(of, starfive_hdmi_dt_ids);
>> >> +
>> >> +struct platform_driver starfive_hdmi_driver = {
>> >> +    .probe  = starfive_hdmi_probe,
>> >> +    .remove = starfive_hdmi_remove,
>> >> +    .driver = {
>> >> +        .name = "starfive-hdmi",
>> >> +        .of_match_table = starfive_hdmi_dt_ids,
>> >> +        .pm = &hdmi_pm_ops,
>> >> +    },
>> >> +};
>> >> +
>> >> +MODULE_AUTHOR("StarFive Corporation");
>> >> +MODULE_DESCRIPTION("Starfive HDMI Driver");
>> >> diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.h b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
>> >> new file mode 100644
>> >> index 000000000..1b9a11bca
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
>> >> @@ -0,0 +1,295 @@
>> >> +/* SPDX-License-Identifier: GPL-2.0-only */
>> >> +/*
>> >> + * Copyright (C) 2023 StarFive Technology Co., Ltd.
>> >> + */
>> >> +
>> >> +#ifndef __STARFIVE_HDMI_H__
>> >> +#define __STARFIVE_HDMI_H__
>> >> +
>> >> +#include <drm/bridge/dw_hdmi.h>
>> >> +#include <drm/drm_atomic_helper.h>
>> >> +#include <drm/drm_edid.h>
>> >> +#include <drm/drm_of.h>
>> >> +#include <drm/drm_probe_helper.h>
>> >> +#include <drm/drm_simple_kms_helper.h>
>> >> +#include <linux/bitfield.h>
>> >> +#include <linux/bits.h>
>> >> +
>> >> +#define DDC_SEGMENT_ADDR        0x30
>> >> +
>> >> +#define HDMI_SCL_RATE            (100 * 1000)
>> >> +#define DDC_BUS_FREQ_L            0x4b
>> >> +#define DDC_BUS_FREQ_H            0x4c
>> >> +
>> >> +#define HDMI_SYS_CTRL            0x00
>> >> +#define m_RST_ANALOG            BIT(6)
>> >> +#define v_RST_ANALOG            0
>> >> +#define v_NOT_RST_ANALOG        BIT(6)
>> >> +#define m_RST_DIGITAL            BIT(5)
>> >> +#define v_RST_DIGITAL            0
>> >> +#define v_NOT_RST_DIGITAL        BIT(5)
>> >> +#define m_REG_CLK_INV            BIT(4)
>> >> +#define v_REG_CLK_NOT_INV        0
>> >> +#define v_REG_CLK_INV            BIT(4)
>> >> +#define m_VCLK_INV            BIT(3)
>> >> +#define v_VCLK_NOT_INV            0
>> >> +#define v_VCLK_INV            BIT(3)
>> >> +#define m_REG_CLK_SOURCE        BIT(2)
>> >> +#define v_REG_CLK_SOURCE_TMDS        0
>> >> +#define v_REG_CLK_SOURCE_SYS        BIT(2)
>> >> +#define m_POWER                BIT(1)
>> >> +#define v_PWR_ON            0
>> >> +#define v_PWR_OFF            BIT(1)
>> >> +#define m_INT_POL            BIT(0)
>> >> +#define v_INT_POL_HIGH            1
>> >> +#define v_INT_POL_LOW            0
>> >> +
>> >> +#define HDMI_AV_MUTE            0x05
>> >> +#define m_AVMUTE_CLEAR            BIT(7)
>> >> +#define m_AVMUTE_ENABLE            BIT(6)
>> >> +#define m_AUDIO_MUTE            BIT(1)
>> >> +#define m_VIDEO_BLACK            BIT(0)
>> >> +#define v_AVMUTE_CLEAR(n)        ((n) << 7)
>> >> +#define v_AVMUTE_ENABLE(n)        ((n) << 6)
>> >> +#define v_AUDIO_MUTE(n)            ((n) << 1)
>> >> +#define v_VIDEO_MUTE(n)            ((n) << 0)
>> >> +
>> >> +#define HDMI_VIDEO_TIMING_CTL        0x08
>> >> +#define v_VSYNC_POLARITY(n)        ((n) << 3)
>> >> +#define v_HSYNC_POLARITY(n)        ((n) << 2)
>> >> +#define v_INETLACE(n)            ((n) << 1)
>> >> +#define v_EXTERANL_VIDEO(n)        ((n) << 0)
>> >> +
>> >> +#define HDMI_VIDEO_EXT_HTOTAL_L        0x09
>> >> +#define HDMI_VIDEO_EXT_HTOTAL_H        0x0a
>> >> +#define HDMI_VIDEO_EXT_HBLANK_L        0x0b
>> >> +#define HDMI_VIDEO_EXT_HBLANK_H        0x0c
>> >> +#define HDMI_VIDEO_EXT_HDELAY_L        0x0d
>> >> +#define HDMI_VIDEO_EXT_HDELAY_H        0x0e
>> >> +#define HDMI_VIDEO_EXT_HDURATION_L    0x0f
>> >> +#define HDMI_VIDEO_EXT_HDURATION_H    0x10
>> >> +#define HDMI_VIDEO_EXT_VTOTAL_L        0x11
>> >> +#define HDMI_VIDEO_EXT_VTOTAL_H        0x12
>> >> +#define HDMI_VIDEO_EXT_VBLANK        0x13
>> >> +#define HDMI_VIDEO_EXT_VDELAY        0x14
>> >> +#define HDMI_VIDEO_EXT_VDURATION    0x15
>> >> +
>> >> +#define HDMI_EDID_SEGMENT_POINTER    0x4d
>> >> +#define HDMI_EDID_WORD_ADDR        0x4e
>> >> +#define HDMI_EDID_FIFO_OFFSET        0x4f
>> >> +#define HDMI_EDID_FIFO_ADDR        0x50
>> >> +
>> >> +#define HDMI_INTERRUPT_MASK1        0xc0
>> >> +#define HDMI_INTERRUPT_STATUS1        0xc1
>> >> +#define    m_INT_ACTIVE_VSYNC        BIT(5)
>> >> +#define m_INT_EDID_READY        BIT(2)
>> >> +
>> >> +#define HDMI_STATUS            0xc8
>> >> +#define m_HOTPLUG            BIT(7)
>> >> +#define m_MASK_INT_HOTPLUG        BIT(5)
>> >> +#define m_INT_HOTPLUG            BIT(1)
>> >> +#define v_MASK_INT_HOTPLUG(n)        (((n) & 0x1) << 5)
>> >
>> > It is untypical to have field defines which start with lowercase letter.
>> >
>> >> +
>> >> +#define HDMI_SYNC                    0xce
>> >> +
>> >> +#define UPDATE(x, h, l)                    FIELD_PREP(GENMASK(h, l), x)
>> >> +
>> >> +/* REG: 0x1a0 */
>> >> +#define STARFIVE_PRE_PLL_CONTROL            0x1a0
>> >> +#define STARFIVE_PCLK_VCO_DIV_5_MASK            BIT(1)
>> >> +#define STARFIVE_PCLK_VCO_DIV_5(x)            UPDATE(x, 1, 1)
>> >> +#define STARFIVE_PRE_PLL_POWER_DOWN            BIT(0)
>> >> +
>> >> +/* REG: 0x1a1 */
>> >> +#define STARFIVE_PRE_PLL_DIV_1                0x1a1
>> >> +#define STARFIVE_PRE_PLL_PRE_DIV_MASK            GENMASK(5, 0)
>> >> +#define STARFIVE_PRE_PLL_PRE_DIV(x)            UPDATE(x, 5, 0)
>> >> +
>> >> +/* REG: 0x1a2 */
>> >> +#define STARFIVE_PRE_PLL_DIV_2                0x1a2
>> >> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DOWN        BIT(7)
>> >> +#define STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE        BIT(6)
>> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_DISABLE        UPDATE(3, 5, 4)
>> >> +#define STARFIVE_PRE_PLL_FB_DIV_11_8_MASK        GENMASK(3, 0)
>> >> +#define STARFIVE_PRE_PLL_FB_DIV_11_8(x)            UPDATE((x) >> 8, 3, 0)
>> >> +
>> >> +/* REG: 0x1a3 */
>> >> +#define STARFIVE_PRE_PLL_DIV_3                0x1a3
>> >> +#define STARFIVE_PRE_PLL_FB_DIV_7_0(x)            UPDATE(x, 7, 0)
>> >> +
>> >> +/* REG: 0x1a4*/
>> >> +#define STARFIVE_PRE_PLL_DIV_4                0x1a4
>> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C_MASK        GENMASK(1, 0)
>> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C(x)        UPDATE(x, 1, 0)
>> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B_MASK        GENMASK(3, 2)
>> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B(x)        UPDATE(x, 3, 2)
>> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A_MASK        GENMASK(5, 4)
>> >> +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A(x)        UPDATE(x, 5, 4)
>> >> +
>> >> +/* REG: 0x1a5 */
>> >> +#define STARFIVE_PRE_PLL_DIV_5                0x1a5
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_SHIFT        5
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_B_MASK        GENMASK(6, 5)
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_B(x)            UPDATE(x, 6, 5)
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_A_MASK        GENMASK(4, 0)
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_A(x)            UPDATE(x, 4, 0)
>> >> +
>> >> +/* REG: 0x1a6 */
>> >> +#define STARFIVE_PRE_PLL_DIV_6                0x1a6
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_SHIFT        5
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_C_MASK        GENMASK(6, 5)
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_C(x)            UPDATE(x, 6, 5)
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_D_MASK        GENMASK(4, 0)
>> >> +#define STARFIVE_PRE_PLL_PCLK_DIV_D(x)            UPDATE(x, 4, 0)
>> >> +
>> >> +/* REG: 0x1a9 */
>> >> +#define STARFIVE_PRE_PLL_LOCK_STATUS            0x1a9
>> >> +
>> >> +/* REG: 0x1aa */
>> >> +#define STARFIVE_POST_PLL_DIV_1                0x1aa
>> >> +#define STARFIVE_POST_PLL_POST_DIV_ENABLE        GENMASK(3, 2)
>> >> +#define STARFIVE_POST_PLL_REFCLK_SEL_TMDS        BIT(1)
>> >> +#define STARFIVE_POST_PLL_POWER_DOWN            BIT(0)
>> >> +#define STARFIVE_POST_PLL_FB_DIV_8(x)            UPDATE(((x) >> 8) << 4, 4, 4)
>> >> +
>> >> +/* REG:0x1ab */
>> >> +#define STARFIVE_POST_PLL_DIV_2                0x1ab
>> >> +#define STARFIVE_POST_PLL_Pre_DIV_MASK            GENMASK(5, 0)
>> >> +#define STARFIVE_POST_PLL_PRE_DIV(x)            UPDATE(x, 5, 0)
>> >> +
>> >> +/* REG: 0x1ac */
>> >> +#define STARFIVE_POST_PLL_DIV_3                0x1ac
>> >> +#define STARFIVE_POST_PLL_FB_DIV_7_0(x)            UPDATE(x, 7, 0)
>> >> +
>> >> +/* REG: 0x1ad */
>> >> +#define STARFIVE_POST_PLL_DIV_4                0x1ad
>> >> +#define STARFIVE_POST_PLL_POST_DIV_MASK            GENMASK(2, 0)
>> >> +#define STARFIVE_POST_PLL_POST_DIV_2            0x0
>> >> +#define STARFIVE_POST_PLL_POST_DIV_4            0x1
>> >> +#define STARFIVE_POST_PLL_POST_DIV_8            0x3
>> >> +
>> >> +/* REG: 0x1af */
>> >> +#define STARFIVE_POST_PLL_LOCK_STATUS            0x1af
>> >> +
>> >> +/* REG: 0x1b0 */
>> >> +#define STARFIVE_BIAS_CONTROL                0x1b0
>> >> +#define STARFIVE_BIAS_ENABLE                BIT(2)
>> >> +
>> >> +/* REG: 0x1b2 */
>> >> +#define STARFIVE_TMDS_CONTROL                0x1b2
>> >> +#define STARFIVE_TMDS_CLK_DRIVER_EN            BIT(3)
>> >> +#define STARFIVE_TMDS_D2_DRIVER_EN            BIT(2)
>> >> +#define STARFIVE_TMDS_D1_DRIVER_EN            BIT(1)
>> >> +#define STARFIVE_TMDS_D0_DRIVER_EN            BIT(0)
>> >> +#define STARFIVE_TMDS_DRIVER_ENABLE            (STARFIVE_TMDS_CLK_DRIVER_EN | \
>> >> +                             STARFIVE_TMDS_D2_DRIVER_EN | \
>> >> +                             STARFIVE_TMDS_D1_DRIVER_EN | \
>> >> +                             STARFIVE_TMDS_D0_DRIVER_EN)
>> >> +
>> >> +/* REG: 0x1b4 */
>> >> +#define STARFIVE_LDO_CONTROL                0x1b4
>> >> +#define STARFIVE_LDO_D2_EN                BIT(2)
>> >> +#define STARFIVE_LDO_D1_EN                BIT(1)
>> >> +#define STARFIVE_LDO_D0_EN                BIT(0)
>> >> +#define STARFIVE_LDO_ENABLE                (STARFIVE_LDO_D2_EN | \
>> >> +                             STARFIVE_LDO_D1_EN | \
>> >> +                             STARFIVE_LDO_D0_EN)
>> >> +
>> >> +/* REG: 0x1be */
>> >> +#define STARFIVE_SERIALIER_CONTROL            0x1be
>> >> +#define STARFIVE_SERIALIER_D2_EN            BIT(6)
>> >> +#define STARFIVE_SERIALIER_D1_EN            BIT(5)
>> >> +#define STARFIVE_SERIALIER_D0_EN            BIT(4)
>> >> +#define STARFIVE_SERIALIER_EN                BIT(0)
>> >> +
>> >> +#define STARFIVE_SERIALIER_ENABLE            (STARFIVE_SERIALIER_D2_EN | \
>> >> +                             STARFIVE_SERIALIER_D1_EN | \
>> >> +                             STARFIVE_SERIALIER_D0_EN | \
>> >> +                             STARFIVE_SERIALIER_EN)
>> >> +
>> >> +/* REG: 0x1cc */
>> >> +#define STARFIVE_RX_CONTROL                0x1cc
>> >> +#define STARFIVE_RX_EN                    BIT(3)
>> >> +#define STARFIVE_RX_CHANNEL_2_EN            BIT(2)
>> >> +#define STARFIVE_RX_CHANNEL_1_EN            BIT(1)
>> >> +#define STARFIVE_RX_CHANNEL_0_EN            BIT(0)
>> >> +#define STARFIVE_RX_ENABLE                (STARFIVE_RX_EN | \
>> >> +                             STARFIVE_RX_CHANNEL_2_EN | \
>> >> +                             STARFIVE_RX_CHANNEL_1_EN | \
>> >> +                             STARFIVE_RX_CHANNEL_0_EN)
>> >> +
>> >> +/* REG: 0x1d1 */
>> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_H            0x1d1
>> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_23_16(x)        UPDATE((x) >> 16, 7, 0)
>> >> +/* REG: 0x1d2 */
>> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_M            0x1d2
>> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_15_8(x)        UPDATE((x) >> 8, 7, 0)
>> >> +/* REG: 0x1d3 */
>> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_L            0x1d3
>> >> +#define STARFIVE_PRE_PLL_FRAC_DIV_7_0(x)        UPDATE(x, 7, 0)
>> >> +
>> >> +struct pre_pll_config {
>> >> +    unsigned long pixclock;
>> >> +    unsigned long tmdsclock;
>> >> +    u8 prediv;
>> >> +    u16 fbdiv;
>> >> +    u8 tmds_div_a;
>> >> +    u8 tmds_div_b;
>> >> +    u8 tmds_div_c;
>> >> +    u8 pclk_div_a;
>> >> +    u8 pclk_div_b;
>> >> +    u8 pclk_div_c;
>> >> +    u8 pclk_div_d;
>> >> +    u8 vco_div_5_en;
>> >> +    u32 fracdiv;
>> >> +};
>> >> +
>> >> +struct post_pll_config {
>> >> +    unsigned long tmdsclock;
>> >> +    u8 prediv;
>> >> +    u16 fbdiv;
>> >> +    u8 postdiv;
>> >> +    u8 post_div_en;
>> >> +    u8 version;
>> >> +};
>> >> +
>> >> +struct phy_config {
>> >> +    unsigned long    tmdsclock;
>> >> +    u8        regs[14];
>> >> +};
>> >> +
>> >> +struct hdmi_data_info {
>> >> +    int vic;
>> >> +    bool sink_is_hdmi;
>> >> +    bool sink_has_audio;
>> >> +    unsigned int enc_in_format;
>> >> +    unsigned int enc_out_format;
>> >> +    unsigned int colorimetry;
>> >> +};
>> >> +
>> >> +struct starfive_hdmi {
>> >> +    struct device *dev;
>> >> +    struct drm_device *drm_dev;
>> >> +    struct drm_encoder    encoder;
>> >> +    struct drm_connector    connector;
>> >> +
>> >> +    struct starfive_hdmi_i2c *i2c;
>> >> +
>> >> +    int irq;
>> >> +    struct clk *sys_clk;
>> >> +    struct clk *mclk;
>> >> +    struct clk *bclk;
>> >> +    struct reset_control *tx_rst;
>> >> +    void __iomem *regs;
>> >> +
>> >> +    struct i2c_adapter *ddc;
>> >> +
>> >> +    unsigned long tmds_rate;
>> >> +
>> >> +    struct hdmi_data_info    hdmi_data;
>> >> +    struct drm_display_mode previous_mode;
>> >> +    const struct pre_pll_config    *pre_cfg;
>> >> +    const struct post_pll_config    *post_cfg;
>> >> +};
>> >> +
>> >> +#endif /* __STARFIVE_HDMI_H__ */
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
>> >> index 3cd533cfa..9669354bd 100644
>> >> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
>> >> @@ -126,6 +126,11 @@ static const struct component_master_ops vs_drm_ops = {
>> >>   static struct platform_driver *drm_sub_drivers[] = {
>> >>       &dc_platform_driver,
>> >>   +    /* connector + encoder*/
>> >> +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
>> >> +    &starfive_hdmi_driver,
>> >> +#endif
>> >> +
>> >>   };
>> >>     static struct component_match *vs_drm_match_add(struct device *dev)
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
>> >> index 369ad22d6..e90723419 100644
>> >> --- a/drivers/gpu/drm/verisilicon/vs_drv.h
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_drv.h
>> >> @@ -24,4 +24,8 @@ to_vs_drm_private(const struct drm_device *dev)
>> >>       return container_of(dev, struct vs_drm_device, base);
>> >>   }
>> >>   +#ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
>> >> +extern struct platform_driver starfive_hdmi_driver;
>> >> +#endif
>> >> +
>> >>   #endif /* __VS_DRV_H__ */
>> >
> 
> 
> 

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
  2023-10-25 19:28     ` Dmitry Baryshkov
  (?)
@ 2023-11-14 10:42       ` Keith Zhao
  -1 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-11-14 10:42 UTC (permalink / raw)
  To: Dmitry Baryshkov, dri-devel, devicetree, linux-kernel,
	linux-riscv, linux-media, linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig



On 2023/10/26 3:28, Dmitry Baryshkov wrote:
> On 25/10/2023 13:39, Keith Zhao wrote:
>> add 2 crtcs and 8 planes in vs-drm
>> 
>> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>> ---
>>   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
>>   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
>>   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
>>   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
>>   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
>>   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
>>   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
>>   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
>>   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
>>   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
>>   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
>>   11 files changed, 4494 insertions(+), 2 deletions(-)
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
>> 
>> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
>> index 7d3be305b..1d48016ca 100644
>> --- a/drivers/gpu/drm/verisilicon/Makefile
>> +++ b/drivers/gpu/drm/verisilicon/Makefile
>> @@ -1,7 +1,11 @@
>>   # SPDX-License-Identifier: GPL-2.0
>>   
>> -vs_drm-objs := vs_drv.o \
>> -		vs_modeset.o
>> +vs_drm-objs := vs_dc_hw.o \
>> +		vs_dc.o \
>> +		vs_crtc.o \
>> +		vs_drv.o \
>> +		vs_modeset.o \
>> +		vs_plane.o
>>   
>>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>>   
>> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> new file mode 100644
>> index 000000000..8a658ea77
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> @@ -0,0 +1,257 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + *
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/debugfs.h>
>> +#include <linux/media-bus-format.h>
>> +
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_atomic.h>
>> +#include <drm/drm_crtc.h>
>> +#include <drm/drm_gem_atomic_helper.h>
>> +#include <drm/drm_vblank.h>
>> +#include <drm/vs_drm.h>
>> +
>> +#include "vs_crtc.h"
>> +#include "vs_dc.h"
>> +#include "vs_drv.h"
>> +
>> +static void vs_crtc_reset(struct drm_crtc *crtc)
>> +{
>> +	struct vs_crtc_state *state;
>> +
>> +	if (crtc->state) {
>> +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
>> +
>> +		state = to_vs_crtc_state(crtc->state);
>> +		kfree(state);
>> +		crtc->state = NULL;
>> +	}
> 
> You can call your crtc_destroy_state function directly here.
> 
>> +
>> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> +	if (!state)
>> +		return;
>> +
>> +	__drm_atomic_helper_crtc_reset(crtc, &state->base);
>> +}
>> +
>> +static struct drm_crtc_state *
>> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
>> +{
>> +	struct vs_crtc_state *ori_state;
> 
> It might be a matter of taste, but it is usually old_state.
> 
>> +	struct vs_crtc_state *state;
>> +
>> +	if (!crtc->state)
>> +		return NULL;
>> +
>> +	ori_state = to_vs_crtc_state(crtc->state);
>> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> +	if (!state)
>> +		return NULL;
>> +
>> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
>> +
>> +	state->output_fmt = ori_state->output_fmt;
>> +	state->encoder_type = ori_state->encoder_type;
>> +	state->bpp = ori_state->bpp;
>> +	state->underflow = ori_state->underflow;
> 
> Can you use kmemdup instead?
> 
>> +
>> +	return &state->base;
>> +}
>> +
>> +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
>> +					 struct drm_crtc_state *state)
>> +{
>> +	__drm_atomic_helper_crtc_destroy_state(state);
>> +	kfree(to_vs_crtc_state(state));
>> +}
>> +
>> +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
>> +{
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	vs_dc_enable_vblank(dc, true);
>> +
>> +	return 0;
>> +}
>> +
>> +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
>> +{
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	vs_dc_enable_vblank(dc, false);
>> +}
>> +
>> +static const struct drm_crtc_funcs vs_crtc_funcs = {
>> +	.set_config		= drm_atomic_helper_set_config,
>> +	.page_flip		= drm_atomic_helper_page_flip,
> 
> destroy is required, see drm_mode_config_cleanup()

hi Dmitry:
if define destroy in drm_crtc_funcs,
it will make __drmm_crtc_init_with_planes unhappy

see:
__printf(6, 0)
static int __drmm_crtc_init_with_planes(struct drm_device *dev,
					struct drm_crtc *crtc,
					struct drm_plane *primary,
					struct drm_plane *cursor,
					const struct drm_crtc_funcs *funcs,
					const char *name,
					va_list args)
{
	int ret;

	drm_WARN_ON(dev, funcs && funcs->destroy);

........
}

It should not need to be defined here, I think

> 
>> +	.reset			= vs_crtc_reset,
>> +	.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
>> +	.atomic_destroy_state	= vs_crtc_atomic_destroy_state,
> 
> please consider adding atomic_print_state to output driver-specific bits.
> 
>> +	.enable_vblank		= vs_crtc_enable_vblank,
>> +	.disable_vblank		= vs_crtc_disable_vblank,
>> +};
>> +
>> +static u8 cal_pixel_bits(u32 bus_format)
> 
> This looks like a generic helper code, which can go to a common place.
> 
>> +{
>> +	u8 bpp;
>> +
>> +	switch (bus_format) {
>> +	case MEDIA_BUS_FMT_RGB565_1X16:
>> +	case MEDIA_BUS_FMT_UYVY8_1X16:
>> +		bpp = 16;
>> +		break;
>> +	case MEDIA_BUS_FMT_RGB666_1X18:
>> +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
>> +		bpp = 18;
>> +		break;
>> +	case MEDIA_BUS_FMT_UYVY10_1X20:
>> +		bpp = 20;
>> +		break;
>> +	case MEDIA_BUS_FMT_BGR888_1X24:
>> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
>> +	case MEDIA_BUS_FMT_YUV8_1X24:
>> +		bpp = 24;
>> +		break;
>> +	case MEDIA_BUS_FMT_RGB101010_1X30:
>> +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
>> +	case MEDIA_BUS_FMT_YUV10_1X30:
>> +		bpp = 30;
>> +		break;
>> +	default:
>> +		bpp = 24;
>> +		break;
>> +	}
>> +
>> +	return bpp;
>> +}
>> +
>> +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
>> +				  struct drm_atomic_state *state)
>> +{
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
>> +
>> +	vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
>> +
>> +	vs_dc_enable(dc, crtc);
>> +	drm_crtc_vblank_on(crtc);
>> +}
>> +
>> +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
>> +				   struct drm_atomic_state *state)
>> +{
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	drm_crtc_vblank_off(crtc);
>> +
>> +	vs_dc_disable(dc, crtc);
>> +
>> +	if (crtc->state->event && !crtc->state->active) {
>> +		spin_lock_irq(&crtc->dev->event_lock);
>> +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
>> +		spin_unlock_irq(&crtc->dev->event_lock);
>> +
>> +		crtc->state->event = NULL;
> 
> I think even should be cleared within the lock.
> 
>> +	}
>> +}
>> +
>> +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
>> +				 struct drm_atomic_state *state)
>> +{
>> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
>> +									  crtc);
>> +
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct device *dev = vs_crtc->dev;
>> +	struct drm_property_blob *blob = crtc->state->gamma_lut;
>> +	struct drm_color_lut *lut;
>> +	struct vs_dc *dc = dev_get_drvdata(dev);
>> +
>> +	if (crtc_state->color_mgmt_changed) {
>> +		if (blob && blob->length) {
>> +			lut = blob->data;
>> +			vs_dc_set_gamma(dc, crtc, lut,
>> +					blob->length / sizeof(*lut));
>> +			vs_dc_enable_gamma(dc, crtc, true);
>> +		} else {
>> +			vs_dc_enable_gamma(dc, crtc, false);
>> +		}
>> +	}
>> +}
>> +
>> +static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
>> +				 struct drm_atomic_state *state)
>> +{
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct drm_pending_vblank_event *event = crtc->state->event;
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	vs_dc_commit(dc);
>> +
>> +	if (event) {
>> +		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
>> +
>> +		spin_lock_irq(&crtc->dev->event_lock);
>> +		drm_crtc_arm_vblank_event(crtc, event);
>> +		spin_unlock_irq(&crtc->dev->event_lock);
>> +		crtc->state->event = NULL;
> 
> I think even should be cleared within the lock.
> 
>> +	}
>> +}
>> +
>> +static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
>> +	.atomic_check = drm_crtc_helper_atomic_check,
>> +	.atomic_enable	= vs_crtc_atomic_enable,
>> +	.atomic_disable = vs_crtc_atomic_disable,
>> +	.atomic_begin	= vs_crtc_atomic_begin,
>> +	.atomic_flush	= vs_crtc_atomic_flush,
>> +};
>> +
>> +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
>> +	{ VS_SINGLE_DC,				"single dc mode" },
>> +	{ VS_MULTI_DC_PRIMARY,		"primary dc for multi dc mode" },
>> +	{ VS_MULTI_DC_SECONDARY,	"secondary dc for multi dc mode" },
>> +};
>> +
>> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
>> +			       struct vs_dc_info *info)
>> +{
>> +	struct vs_crtc *crtc;
>> +	int ret;
>> +
>> +	if (!info)
>> +		return NULL;
>> +
>> +	crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
>> +	if (!crtc)
>> +		return NULL;
>> +
>> +	ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
>> +					 NULL, NULL, &vs_crtc_funcs,
>> +					 info->name ? info->name : NULL);
> 
> It might be better to add drmm_crtc_init() helper.
> 
>> +	if (ret)
>> +		return NULL;
>> +
>> +	drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
>> +
>> +	if (info->gamma_size) {
>> +		ret = drm_mode_crtc_set_gamma_size(&crtc->base,
>> +						   info->gamma_size);
>> +		if (ret)
>> +			return NULL;
>> +
>> +		drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
>> +					   info->gamma_size);
>> +	}
>> +
>> +	crtc->max_bpc = info->max_bpc;
>> +	crtc->color_formats = info->color_formats;
>> +	return crtc;
>> +}
>> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h
>> new file mode 100644
>> index 000000000..526dd63e5
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.h
>> @@ -0,0 +1,43 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#ifndef __VS_CRTC_H__
>> +#define __VS_CRTC_H__
>> +
>> +#include <drm/drm_crtc.h>
>> +#include <drm/drm_crtc_helper.h>
>> +
>> +#include "vs_type.h"
>> +
>> +struct vs_crtc_state {
>> +	struct drm_crtc_state base;
>> +
>> +	u32 output_fmt;
>> +	u8 encoder_type;
>> +	u8 bpp;
>> +	bool underflow;
>> +};
>> +
>> +struct vs_crtc {
>> +	struct drm_crtc base;
>> +	struct device *dev;
>> +	unsigned int max_bpc;
>> +	unsigned int color_formats;
>> +};
>> +
>> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
>> +			       struct vs_dc_info *info);
>> +
>> +static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc)
>> +{
>> +	return container_of(crtc, struct vs_crtc, base);
>> +}
>> +
>> +static inline struct vs_crtc_state *
>> +to_vs_crtc_state(struct drm_crtc_state *state)
>> +{
>> +	return container_of(state, struct vs_crtc_state, base);
>> +}
>> +#endif /* __VS_CRTC_H__ */
>> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
>> new file mode 100644
>> index 000000000..b5ab92d98
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_dc.c
>> @@ -0,0 +1,1002 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#include <linux/component.h>
>> +#include <linux/clk.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/reset.h>
>> +
>> +#include <drm/drm_atomic.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_blend.h>
>> +#include <drm/drm_framebuffer.h>
>> +#include <drm/drm_vblank.h>
>> +#include <drm/vs_drm.h>
>> +
>> +#include "vs_dc_hw.h"
>> +#include "vs_dc.h"
>> +#include "vs_drv.h"
>> +
>> +static const char * const vout_clocks[] = {
>> +	"noc_bus",
>> +	"channel0",
>> +	"channel1",
>> +	"dc_core",
>> +	"axi_core",
>> +	"ahb",
>> +	"hdmi_tx",
>> +	"dc_parent",
>> +
>> +};
>> +
>> +static const char * const vout_resets[] = {
>> +	"axi",
>> +	"ahb",
>> +	"core",
>> +};
>> +
>> +static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb)
>> +{
>> +	u8 f = FORMAT_A8R8G8B8;
>> +
>> +	switch (format) {
>> +	case DRM_FORMAT_XRGB4444:
>> +	case DRM_FORMAT_RGBX4444:
>> +	case DRM_FORMAT_XBGR4444:
>> +	case DRM_FORMAT_BGRX4444:
>> +		f = FORMAT_X4R4G4B4;
>> +		break;
>> +	case DRM_FORMAT_ARGB4444:
>> +	case DRM_FORMAT_RGBA4444:
>> +	case DRM_FORMAT_ABGR4444:
>> +	case DRM_FORMAT_BGRA4444:
>> +		f = FORMAT_A4R4G4B4;
>> +		break;
>> +	case DRM_FORMAT_XRGB1555:
>> +	case DRM_FORMAT_RGBX5551:
>> +	case DRM_FORMAT_XBGR1555:
>> +	case DRM_FORMAT_BGRX5551:
>> +		f = FORMAT_X1R5G5B5;
>> +		break;
>> +	case DRM_FORMAT_ARGB1555:
>> +	case DRM_FORMAT_RGBA5551:
>> +	case DRM_FORMAT_ABGR1555:
>> +	case DRM_FORMAT_BGRA5551:
>> +		f = FORMAT_A1R5G5B5;
>> +		break;
>> +	case DRM_FORMAT_RGB565:
>> +	case DRM_FORMAT_BGR565:
>> +		f = FORMAT_R5G6B5;
>> +		break;
>> +	case DRM_FORMAT_XRGB8888:
>> +	case DRM_FORMAT_RGBX8888:
>> +	case DRM_FORMAT_XBGR8888:
>> +	case DRM_FORMAT_BGRX8888:
>> +		f = FORMAT_X8R8G8B8;
>> +		break;
>> +	case DRM_FORMAT_ARGB8888:
>> +	case DRM_FORMAT_RGBA8888:
>> +	case DRM_FORMAT_ABGR8888:
>> +	case DRM_FORMAT_BGRA8888:
>> +		f = FORMAT_A8R8G8B8;
>> +		break;
>> +	case DRM_FORMAT_YUYV:
>> +	case DRM_FORMAT_YVYU:
>> +		f = FORMAT_YUY2;
>> +		break;
>> +	case DRM_FORMAT_UYVY:
>> +	case DRM_FORMAT_VYUY:
>> +		f = FORMAT_UYVY;
>> +		break;
>> +	case DRM_FORMAT_YUV420:
>> +	case DRM_FORMAT_YVU420:
>> +		f = FORMAT_YV12;
>> +		break;
>> +	case DRM_FORMAT_NV21:
>> +		f = FORMAT_NV12;
>> +		break;
>> +	case DRM_FORMAT_NV16:
>> +	case DRM_FORMAT_NV61:
>> +		f = FORMAT_NV16;
>> +		break;
>> +	case DRM_FORMAT_P010:
>> +		f = FORMAT_P010;
>> +		break;
>> +	case DRM_FORMAT_ARGB2101010:
>> +	case DRM_FORMAT_RGBA1010102:
>> +	case DRM_FORMAT_ABGR2101010:
>> +	case DRM_FORMAT_BGRA1010102:
>> +		f = FORMAT_A2R10G10B10;
>> +		break;
>> +	case DRM_FORMAT_NV12:
>> +		f = FORMAT_NV12;
>> +		break;
>> +	case DRM_FORMAT_YUV444:
>> +		f = FORMAT_YUV444;
>> +		break;
>> +	default:
>> +		break;
>> +	}
>> +
>> +	fb->format = f;
>> +}
>> +
>> +static inline void update_swizzle(u32 format, struct dc_hw_fb *fb)
>> +{
>> +	fb->swizzle = SWIZZLE_ARGB;
>> +	fb->uv_swizzle = 0;
>> +
>> +	switch (format) {
>> +	case DRM_FORMAT_RGBX4444:
>> +	case DRM_FORMAT_RGBA4444:
>> +	case DRM_FORMAT_RGBX5551:
>> +	case DRM_FORMAT_RGBA5551:
>> +	case DRM_FORMAT_RGBX8888:
>> +	case DRM_FORMAT_RGBA8888:
>> +	case DRM_FORMAT_RGBA1010102:
>> +		fb->swizzle = SWIZZLE_RGBA;
>> +		break;
>> +	case DRM_FORMAT_XBGR4444:
>> +	case DRM_FORMAT_ABGR4444:
>> +	case DRM_FORMAT_XBGR1555:
>> +	case DRM_FORMAT_ABGR1555:
>> +	case DRM_FORMAT_BGR565:
>> +	case DRM_FORMAT_XBGR8888:
>> +	case DRM_FORMAT_ABGR8888:
>> +	case DRM_FORMAT_ABGR2101010:
>> +		fb->swizzle = SWIZZLE_ABGR;
>> +		break;
>> +	case DRM_FORMAT_BGRX4444:
>> +	case DRM_FORMAT_BGRA4444:
>> +	case DRM_FORMAT_BGRX5551:
>> +	case DRM_FORMAT_BGRA5551:
>> +	case DRM_FORMAT_BGRX8888:
>> +	case DRM_FORMAT_BGRA8888:
>> +	case DRM_FORMAT_BGRA1010102:
>> +		fb->swizzle = SWIZZLE_BGRA;
>> +		break;
>> +	case DRM_FORMAT_YVYU:
>> +	case DRM_FORMAT_VYUY:
>> +	case DRM_FORMAT_NV21:
>> +	case DRM_FORMAT_NV61:
>> +		fb->uv_swizzle = 1;
>> +		break;
>> +	default:
>> +		break;
>> +	}
>> +}
>> +
>> +static inline void update_watermark(struct drm_property_blob *watermark,
>> +				    struct dc_hw_fb *fb)
>> +{
>> +	struct drm_vs_watermark *data;
>> +
>> +	fb->water_mark = 0;
>> +
>> +	if (watermark) {
>> +		data = watermark->data;
>> +		fb->water_mark = data->watermark & 0xFFFFF;
>> +	}
>> +}
>> +
>> +static inline u8 to_vs_rotation(unsigned int rotation)
>> +{
>> +	u8 rot;
>> +
>> +	switch (rotation & DRM_MODE_REFLECT_MASK) {
>> +	case DRM_MODE_REFLECT_X:
>> +		rot = FLIP_X;
>> +		return rot;
>> +	case DRM_MODE_REFLECT_Y:
>> +		rot = FLIP_Y;
>> +		return rot;
>> +	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
>> +		rot = FLIP_XY;
>> +		return rot;
>> +	default:
>> +		break;
>> +	}
>> +
>> +	switch (rotation & DRM_MODE_ROTATE_MASK) {
>> +	case DRM_MODE_ROTATE_0:
>> +		rot = ROT_0;
>> +		break;
>> +	case DRM_MODE_ROTATE_90:
>> +		rot = ROT_90;
>> +		break;
>> +	case DRM_MODE_ROTATE_180:
>> +		rot = ROT_180;
>> +		break;
>> +	case DRM_MODE_ROTATE_270:
>> +		rot = ROT_270;
>> +		break;
>> +	default:
>> +		rot = ROT_0;
>> +		break;
>> +	}
>> +
>> +	return rot;
>> +}
>> +
>> +static inline u8 to_vs_yuv_color_space(u32 color_space)
>> +{
>> +	u8 cs;
>> +
>> +	switch (color_space) {
>> +	case DRM_COLOR_YCBCR_BT601:
>> +		cs = COLOR_SPACE_601;
>> +		break;
>> +	case DRM_COLOR_YCBCR_BT709:
>> +		cs = COLOR_SPACE_709;
>> +		break;
>> +	case DRM_COLOR_YCBCR_BT2020:
>> +		cs = COLOR_SPACE_2020;
>> +		break;
>> +	default:
>> +		cs = COLOR_SPACE_601;
>> +		break;
>> +	}
>> +
>> +	return cs;
>> +}
>> +
>> +static inline u8 to_vs_tile_mode(u64 modifier)
>> +{
>> +	if (modifier == DRM_FORMAT_MOD_VIVANTE_TILED)
>> +		return DC_TILE_MODE4X4;
>> +
>> +	return (u8)(modifier & DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK);
>> +}
>> +
>> +static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc)
>> +{
>> +	u8 panel_num = dc->hw.info->panel_num;
>> +	u32 index = drm_crtc_index(crtc);
>> +	int i;
>> +
>> +	for (i = 0; i < panel_num; i++) {
>> +		if (index == dc->crtc[i]->base.index)
>> +			return i;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
>> +					  unsigned int alignment)
>> +{
>> +	struct vs_drm_device *priv = to_vs_drm_private(drm_dev);
>> +
>> +	if (alignment > priv->pitch_alignment)
>> +		priv->pitch_alignment = alignment;
>> +}
>> +
>> +static int plda_clk_rst_init(struct device *dev)
>> +{
>> +	int ret = 0;
>> +	struct vs_dc *dc = dev_get_drvdata(dev);
>> +
>> +	ret = clk_bulk_prepare_enable(dc->nclks, dc->clk_vout);
>> +	if (ret) {
>> +		dev_err(dev, "failed to enable clocks\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = reset_control_bulk_deassert(dc->nrsts, dc->rst_vout);
>> +	return ret;
>> +}
>> +
>> +static void plda_clk_rst_deinit(struct device *dev)
>> +{
>> +	struct vs_dc *dc = dev_get_drvdata(dev);
>> +
>> +	reset_control_bulk_assert(dc->nrsts, dc->rst_vout);
>> +	clk_bulk_disable_unprepare(dc->nclks, dc->clk_vout);
>> +}
>> +
>> +static void dc_deinit(struct device *dev)
>> +{
>> +	struct vs_dc *dc = dev_get_drvdata(dev);
>> +
>> +	dc_hw_enable_interrupt(&dc->hw, 0);
>> +	dc_hw_deinit(&dc->hw);
>> +	plda_clk_rst_deinit(dev);
>> +}
>> +
>> +static int dc_init(struct device *dev)
>> +{
>> +	struct vs_dc *dc = dev_get_drvdata(dev);
>> +	int ret;
>> +
>> +	dc->first_frame = true;
>> +
>> +	ret = plda_clk_rst_init(dev);
>> +	if (ret < 0) {
>> +		dev_err(dev, "failed to init dc clk reset: %d\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	ret = dc_hw_init(&dc->hw);
>> +	if (ret) {
>> +		dev_err(dev, "failed to init DC HW\n");
>> +		return ret;
>> +	}
>> +	return 0;
>> +}
>> +
>> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
>> +{
>> +	struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
>> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
>> +	struct dc_hw_display display;
>> +
>> +	display.bus_format = crtc_state->output_fmt;
>> +	display.h_active = mode->hdisplay;
>> +	display.h_total = mode->htotal;
>> +	display.h_sync_start = mode->hsync_start;
>> +	display.h_sync_end = mode->hsync_end;
>> +	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
>> +		display.h_sync_polarity = true;
>> +	else
>> +		display.h_sync_polarity = false;
>> +
>> +	display.v_active = mode->vdisplay;
>> +	display.v_total = mode->vtotal;
>> +	display.v_sync_start = mode->vsync_start;
>> +	display.v_sync_end = mode->vsync_end;
>> +
>> +	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
>> +		display.v_sync_polarity = true;
>> +	else
>> +		display.v_sync_polarity = false;
>> +
>> +	display.id = to_vs_display_id(dc, crtc);
>> +
>> +	display.enable = true;
>> +
>> +	if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) {
>> +		dc_hw_set_out(&dc->hw, OUT_DPI, display.id);
>> +		clk_set_rate(dc->clk_vout[CLK_VOUT_SOC_PIX].clk, mode->clock * 1000);
>> +		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX1].clk,
>> +			       dc->clk_vout[CLK_VOUT_SOC_PIX].clk);
>> +	} else {
>> +		dc_hw_set_out(&dc->hw, OUT_DP, display.id);
>> +		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX0].clk,
>> +			       dc->clk_vout[CLK_VOUT_HDMI_PIX].clk);
>> +	}
>> +
>> +	dc_hw_setup_display(&dc->hw, &display);
>> +}
>> +
>> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc)
>> +{
>> +	struct dc_hw_display display;
>> +
>> +	display.id = to_vs_display_id(dc, crtc);
>> +	display.enable = false;
>> +
>> +	dc_hw_setup_display(&dc->hw, &display);
>> +}
>> +
>> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
>> +		     struct drm_color_lut *lut, unsigned int size)
>> +{
>> +	u16 i, r, g, b;
>> +	u8 bits, id;
>> +
>> +	if (size != dc->hw.info->gamma_size) {
>> +		drm_err(crtc->dev, "gamma size does not match!\n");
>> +		return;
>> +	}
>> +
>> +	id = to_vs_display_id(dc, crtc);
>> +
>> +	bits = dc->hw.info->gamma_bits;
>> +	for (i = 0; i < size; i++) {
>> +		r = drm_color_lut_extract(lut[i].red, bits);
>> +		g = drm_color_lut_extract(lut[i].green, bits);
>> +		b = drm_color_lut_extract(lut[i].blue, bits);
>> +		dc_hw_update_gamma(&dc->hw, id, i, r, g, b);
>> +	}
>> +}
>> +
>> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
>> +			bool enable)
>> +{
>> +	u8 id;
>> +
>> +	id = to_vs_display_id(dc, crtc);
>> +	dc_hw_enable_gamma(&dc->hw, id, enable);
>> +}
>> +
>> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable)
>> +{
>> +	dc_hw_enable_interrupt(&dc->hw, enable);
>> +}
>> +
>> +static u32 calc_factor(u32 src, u32 dest)
>> +{
>> +	u32 factor = 1 << 16;
>> +
>> +	if (src > 1 && dest > 1)
>> +		factor = ((src - 1) << 16) / (dest - 1);
>> +
>> +	return factor;
>> +}
>> +
>> +static void update_scale(struct drm_plane_state *state, struct dc_hw_roi *roi,
>> +			 struct dc_hw_scale *scale)
>> +{
>> +	int dst_w = drm_rect_width(&state->dst);
>> +	int dst_h = drm_rect_height(&state->dst);
>> +	int src_w, src_h, temp;
>> +
>> +	scale->enable = false;
>> +
>> +	if (roi->enable) {
>> +		src_w = roi->width;
>> +		src_h = roi->height;
>> +	} else {
>> +		src_w = drm_rect_width(&state->src) >> 16;
>> +		src_h = drm_rect_height(&state->src) >> 16;
>> +	}
>> +
>> +	if (drm_rotation_90_or_270(state->rotation)) {
>> +		temp = src_w;
>> +		src_w = src_h;
>> +		src_h = temp;
>> +	}
>> +
>> +	if (src_w != dst_w) {
>> +		scale->scale_factor_x = calc_factor(src_w, dst_w);
>> +		scale->enable = true;
>> +	} else {
>> +		scale->scale_factor_x = 1 << 16;
>> +	}
>> +	if (src_h != dst_h) {
>> +		scale->scale_factor_y = calc_factor(src_h, dst_h);
>> +		scale->enable = true;
>> +	} else {
>> +		scale->scale_factor_y = 1 << 16;
>> +	}
>> +}
>> +
>> +static void update_fb(struct vs_plane *plane, u8 display_id,
>> +		      struct dc_hw_fb *fb, struct drm_plane_state *state)
>> +{
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> +	struct drm_framebuffer *drm_fb = state->fb;
>> +	struct drm_rect *src = &state->src;
>> +
>> +	fb->display_id = display_id;
>> +	fb->y_address = plane_state->dma_addr[0];
>> +	fb->y_stride = drm_fb->pitches[0];
>> +	if (drm_fb->format->format == DRM_FORMAT_YVU420) {
>> +		fb->u_address = plane_state->dma_addr[2];
>> +		fb->v_address = plane_state->dma_addr[1];
>> +		fb->u_stride = drm_fb->pitches[2];
>> +		fb->v_stride = drm_fb->pitches[1];
>> +	} else {
>> +		fb->u_address = plane_state->dma_addr[1];
>> +		fb->v_address = plane_state->dma_addr[2];
>> +		fb->u_stride = drm_fb->pitches[1];
>> +		fb->v_stride = drm_fb->pitches[2];
>> +	}
>> +	fb->width = drm_rect_width(src) >> 16;
>> +	fb->height = drm_rect_height(src) >> 16;
>> +	fb->tile_mode = to_vs_tile_mode(drm_fb->modifier);
>> +	fb->rotation = to_vs_rotation(state->rotation);
>> +	fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding);
>> +	fb->zpos = state->zpos;
>> +	fb->enable = state->visible;
>> +	update_format(drm_fb->format->format, drm_fb->modifier, fb);
>> +	update_swizzle(drm_fb->format->format, fb);
>> +	update_watermark(plane_state->watermark, fb);
>> +	plane_state->status.tile_mode = fb->tile_mode;
>> +}
>> +
>> +static void update_degamma(struct vs_dc *dc, struct vs_plane *plane,
>> +			   struct vs_plane_state *plane_state)
>> +{
>> +	dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
>> +	plane_state->degamma_changed = false;
>> +}
>> +
>> +static void update_roi(struct vs_dc *dc, u8 id,
>> +		       struct vs_plane_state *plane_state,
>> +		       struct dc_hw_roi *roi,
>> +		       struct drm_plane_state *state)
>> +{
>> +	struct drm_vs_roi *data;
>> +	struct drm_rect *src = &state->src;
>> +	u16 src_w = drm_rect_width(src) >> 16;
>> +	u16 src_h = drm_rect_height(src) >> 16;
>> +
>> +	if (plane_state->roi) {
>> +		data = plane_state->roi->data;
>> +
>> +		if (data->enable) {
>> +			roi->x = data->roi_x;
>> +			roi->y = data->roi_y;
>> +			roi->width = (data->roi_x + data->roi_w > src_w) ?
>> +						 (src_w - data->roi_x) : data->roi_w;
>> +			roi->height = (data->roi_y + data->roi_h > src_h) ?
>> +						  (src_h - data->roi_y) : data->roi_h;
>> +			roi->enable = true;
>> +		} else {
>> +			roi->enable = false;
>> +		}
>> +
>> +		dc_hw_update_roi(&dc->hw, id, roi);
>> +	} else {
>> +		roi->enable = false;
>> +	}
>> +}
>> +
>> +static void update_color_mgmt(struct vs_dc *dc, u8 id,
>> +			      struct dc_hw_fb *fb,
>> +			      struct vs_plane_state *plane_state)
>> +{
>> +	struct drm_vs_color_mgmt *data;
>> +	struct dc_hw_colorkey colorkey;
>> +
>> +	if (plane_state->color_mgmt) {
>> +		data = plane_state->color_mgmt->data;
>> +
>> +		fb->clear_enable = data->clear_enable;
>> +		fb->clear_value = data->clear_value;
>> +
>> +		if (data->colorkey > data->colorkey_high)
>> +			data->colorkey = data->colorkey_high;
>> +
>> +		colorkey.colorkey = data->colorkey;
>> +		colorkey.colorkey_high = data->colorkey_high;
>> +		colorkey.transparency = (data->transparency) ?
>> +				DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE;
>> +		dc_hw_update_colorkey(&dc->hw, id, &colorkey);
>> +	}
>> +}
>> +
>> +static void update_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +			 struct drm_plane *drm_plane,
>> +			 struct drm_atomic_state *drm_state)
>> +{
>> +	struct dc_hw_fb fb = {0};
>> +	struct dc_hw_scale scale;
>> +	struct dc_hw_position pos;
>> +	struct dc_hw_blend blend;
>> +	struct dc_hw_roi roi;
>> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
>> +									   drm_plane);
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> +	struct drm_rect *dest = &state->dst;
>> +	bool dec_enable = false;
>> +	u8 display_id = 0;
>> +
>> +	display_id = to_vs_display_id(dc, state->crtc);
>> +	update_fb(plane, display_id, &fb, state);
>> +	fb.dec_enable = dec_enable;
>> +
>> +	update_roi(dc, plane->id, plane_state, &roi, state);
>> +
>> +	update_scale(state, &roi, &scale);
>> +
>> +	if (plane_state->degamma_changed)
>> +		update_degamma(dc, plane, plane_state);
>> +
>> +	pos.start_x = dest->x1;
>> +	pos.start_y = dest->y1;
>> +	pos.end_x = dest->x2;
>> +	pos.end_y = dest->y2;
>> +
>> +	blend.alpha = (u8)(state->alpha >> 8);
>> +	blend.blend_mode = (u8)(state->pixel_blend_mode);
>> +
>> +	update_color_mgmt(dc, plane->id, &fb, plane_state);
>> +
>> +	dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
>> +}
>> +
>> +static void update_qos(struct vs_dc *dc, struct vs_plane *plane,
>> +		       struct drm_plane *drm_plane,
>> +		       struct drm_atomic_state *drm_state)
>> +{
>> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
>> +									   drm_plane);
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> +	struct drm_vs_watermark *data;
>> +	struct dc_hw_qos qos;
>> +
>> +	if (plane_state->watermark) {
>> +		data = plane_state->watermark->data;
>> +
>> +		if (data->qos_high) {
>> +			if (data->qos_low > data->qos_high)
>> +				data->qos_low = data->qos_high;
>> +
>> +			qos.low_value = data->qos_low & 0x0F;
>> +			qos.high_value = data->qos_high & 0x0F;
>> +			dc_hw_update_qos(&dc->hw, &qos);
>> +		}
>> +	}
>> +}
>> +
>> +static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor)
>> +{
>> +	u8 size_type;
>> +
>> +	switch (state->crtc_w) {
>> +	case 32:
>> +		size_type = CURSOR_SIZE_32X32;
>> +		break;
>> +	case 64:
>> +		size_type = CURSOR_SIZE_64X64;
>> +		break;
>> +	default:
>> +		size_type = CURSOR_SIZE_32X32;
>> +		break;
>> +	}
>> +
>> +	cursor->size = size_type;
>> +}
>> +
>> +static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +				struct drm_plane *drm_plane,
>> +				struct drm_atomic_state *drm_state)
>> +{
>> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
>> +								       drm_plane);
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> +	struct drm_framebuffer *drm_fb = state->fb;
>> +	struct dc_hw_cursor cursor;
>> +
>> +	cursor.address = plane_state->dma_addr[0];
>> +	cursor.x = state->crtc_x;
>> +	cursor.y = state->crtc_y;
>> +	cursor.hot_x = drm_fb->hot_x;
>> +	cursor.hot_y = drm_fb->hot_y;
>> +	cursor.display_id = to_vs_display_id(dc, state->crtc);
>> +	update_cursor_size(state, &cursor);
>> +	cursor.enable = true;
>> +
>> +	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
>> +}
>> +
>> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +			struct drm_plane *drm_plane,
>> +			struct drm_atomic_state *drm_state)
>> +{
>> +	update_plane(dc, plane, drm_plane, drm_state);
>> +	update_qos(dc, plane, drm_plane, drm_state);
>> +}
>> +
>> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +			       struct drm_plane *drm_plane,
>> +			       struct drm_atomic_state *drm_state)
>> +{
>> +	update_cursor_plane(dc, plane, drm_plane, drm_state);
>> +}
>> +
>> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +			 struct drm_plane_state *old_state)
>> +{
>> +	struct dc_hw_fb fb = {0};
>> +
>> +	fb.enable = false;
>> +	dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL, NULL, NULL);
>> +}
>> +
>> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +				struct drm_plane_state *old_state)
>> +{
>> +	struct dc_hw_cursor cursor = {0};
>> +
>> +	cursor.enable = false;
>> +	cursor.display_id = to_vs_display_id(dc, old_state->crtc);
>> +	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
>> +}
>> +
>> +static bool vs_dc_mod_supported(const struct vs_plane_info *plane_info,
>> +				u64 modifier)
>> +{
>> +	const u64 *mods;
>> +
>> +	if (!plane_info->modifiers)
>> +		return false;
>> +
>> +	for (mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID; mods++) {
>> +		if (*mods == modifier)
>> +			return true;
>> +	}
>> +
>> +	return false;
>> +}
>> +
>> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
>> +		      struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> +										 plane);
>> +
>> +	struct drm_framebuffer *fb = new_plane_state->fb;
>> +	const struct vs_plane_info *plane_info;
>> +	struct drm_crtc *crtc = new_plane_state->crtc;
>> +	struct drm_crtc_state *crtc_state;
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +
>> +	plane_info = &dc->hw.info->planes[vs_plane->id];
>> +
>> +	if (fb->width < plane_info->min_width ||
>> +	    fb->width > plane_info->max_width ||
>> +	    fb->height < plane_info->min_height ||
>> +	    fb->height > plane_info->max_height)
>> +		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n",
>> +			     vs_plane->id);
>> +
>> +	if (!vs_dc_mod_supported(plane_info, fb->modifier)) {
>> +		drm_err(plane->dev, "unsupported modifier on plane%d.\n", vs_plane->id);
>> +		return -EINVAL;
>> +	}
>> +
>> +	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
>> +	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
>> +						  plane_info->min_scale,
>> +						  plane_info->max_scale,
>> +						  true, true);
>> +}
>> +
>> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
>> +			     struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> +									   plane);
>> +	struct drm_framebuffer *fb = new_plane_state->fb;
>> +	const struct vs_plane_info *plane_info;
>> +	struct drm_crtc *crtc = new_plane_state->crtc;
>> +	struct drm_crtc_state *crtc_state;
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +
>> +	plane_info = &dc->hw.info->planes[vs_plane->id];
>> +
>> +	if (fb->width < plane_info->min_width ||
>> +	    fb->width > plane_info->max_width ||
>> +	    fb->height < plane_info->min_height ||
>> +	    fb->height > plane_info->max_height)
>> +		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n", vs_plane->id);
>> +
>> +	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
>> +	if (IS_ERR(crtc_state))
>> +		return -EINVAL;
>> +
>> +	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
>> +						plane_info->min_scale,
>> +						plane_info->max_scale,
>> +						true, true);
>> +}
>> +
>> +static void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
>> +{
>> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
>> +
>> +	drm_crtc_handle_vblank(crtc);
>> +
>> +	vs_crtc_state->underflow = underflow;
>> +}
>> +
>> +static irqreturn_t dc_isr(int irq, void *data)
>> +{
>> +	struct vs_dc *dc = data;
>> +	struct vs_dc_info *dc_info = dc->hw.info;
>> +	u32 i, ret;
>> +
>> +	ret = dc_hw_get_interrupt(&dc->hw);
>> +
>> +	for (i = 0; i < dc_info->panel_num; i++)
>> +		vs_crtc_handle_vblank(&dc->crtc[i]->base, dc_hw_check_underflow(&dc->hw));
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +void vs_dc_commit(struct vs_dc *dc)
>> +{
>> +	dc_hw_enable_shadow_register(&dc->hw, false);
>> +
>> +	dc_hw_commit(&dc->hw);
>> +
>> +	if (dc->first_frame)
>> +		dc->first_frame = false;
>> +
>> +	dc_hw_enable_shadow_register(&dc->hw, true);
>> +}
>> +
>> +static int dc_bind(struct device *dev, struct device *master, void *data)
>> +{
>> +	struct drm_device *drm_dev = data;
>> +	struct vs_dc *dc = dev_get_drvdata(dev);
>> +	struct device_node *port;
>> +	struct vs_crtc *crtc;
>> +	struct vs_dc_info *dc_info;
>> +	struct vs_plane *plane;
>> +	struct vs_plane_info *plane_info;
>> +	int i, ret;
>> +	u32 ctrc_mask = 0;
>> +
>> +	if (!drm_dev || !dc) {
>> +		dev_err(dev, "devices are not created.\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	ret = dc_init(dev);
>> +	if (ret < 0) {
>> +		drm_err(drm_dev, "Failed to initialize DC hardware.\n");
>> +		return ret;
>> +	}
>> +
>> +	port = of_get_child_by_name(dev->of_node, "port");
>> +	if (!port) {
>> +		drm_err(drm_dev, "no port node found\n");
>> +		return -ENODEV;
>> +	}
>> +	of_node_put(port);
>> +
>> +	dc_info = dc->hw.info;
>> +
>> +	for (i = 0; i < dc_info->panel_num; i++) {
>> +		crtc = vs_crtc_create(drm_dev, dc_info);
>> +		if (!crtc) {
>> +			drm_err(drm_dev, "Failed to create CRTC.\n");
>> +			ret = -ENOMEM;
>> +			return ret;
>> +		}
>> +
>> +		crtc->base.port = port;
>> +		crtc->dev = dev;
>> +		dc->crtc[i] = crtc;
>> +		ctrc_mask |= drm_crtc_mask(&crtc->base);
>> +	}
>> +
>> +	for (i = 0; i < dc_info->plane_num; i++) {
>> +		plane_info = (struct vs_plane_info *)&dc_info->planes[i];
>> +
>> +		if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
>> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
>> +						drm_crtc_mask(&dc->crtc[0]->base));
>> +		} else if (!strcmp(plane_info->name, "Primary_1") ||
>> +				   !strcmp(plane_info->name, "Cursor_1")) {
>> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
>> +						drm_crtc_mask(&dc->crtc[1]->base));
> 
> I'd say, it's not typical to use name to distinguish objects. Maybe you 
> can add indices to the info structure and drop the names instead?
> 
> 
>> +		} else {
>> +			plane = vs_plane_create(drm_dev, plane_info,
>> +						dc_info->layer_num, ctrc_mask);
>> +		}
>> +
>> +		if (IS_ERR(plane)) {
>> +			dev_err(dev, "failed to construct plane\n");
>> +			return PTR_ERR(plane);
>> +		}
>> +
>> +		plane->id = i;
>> +		dc->planes[i].id = plane_info->id;
>> +
>> +		if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
>> +			if (!strcmp(plane_info->name, "Primary"))
>> +				dc->crtc[0]->base.primary = &plane->base;
>> +			else
>> +				dc->crtc[1]->base.primary = &plane->base;
> 
> This definitely urges for an index in the plane info / plane_state.
> 
>> +			drm_dev->mode_config.min_width = plane_info->min_width;
>> +			drm_dev->mode_config.min_height =
>> +							plane_info->min_height;
>> +			drm_dev->mode_config.max_width = plane_info->max_width;
>> +			drm_dev->mode_config.max_height =
>> +							plane_info->max_height;
>> +		}
>> +
>> +		if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
>> +			if (!strcmp(plane_info->name, "Cursor"))
>> +				dc->crtc[0]->base.cursor = &plane->base;
>> +			else
>> +				dc->crtc[1]->base.cursor = &plane->base;
>> +			drm_dev->mode_config.cursor_width =
>> +							plane_info->max_width;
>> +			drm_dev->mode_config.cursor_height =
>> +							plane_info->max_height;
>> +		}
>> +	}
>> +
>> +	vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
>> +	return 0;
>> +}
>> +
>> +static void dc_unbind(struct device *dev, struct device *master, void *data)
>> +{
>> +	dc_deinit(dev);
>> +}
>> +
>> +const struct component_ops dc_component_ops = {
>> +	.bind = dc_bind,
>> +	.unbind = dc_unbind,
>> +};
>> +
>> +static const struct of_device_id dc_driver_dt_match[] = {
>> +	{ .compatible = "starfive,jh7110-dc8200", },
>> +	{},
>> +};
>> +MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
>> +
>> +static int dc_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct vs_dc *dc;
>> +	int irq, ret, i;
>> +
>> +	dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
>> +	if (!dc)
>> +		return -ENOMEM;
>> +
>> +	dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
>> +	if (IS_ERR(dc->hw.hi_base))
>> +		return PTR_ERR(dc->hw.hi_base);
>> +
>> +	dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
>> +	if (IS_ERR(dc->hw.reg_base))
>> +		return PTR_ERR(dc->hw.reg_base);
>> +
>> +	dc->nclks = ARRAY_SIZE(dc->clk_vout);
>> +	for (i = 0; i < dc->nclks; ++i)
>> +		dc->clk_vout[i].id = vout_clocks[i];
>> +	ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
>> +	if (ret) {
>> +		dev_err(dev, "Failed to get clk controls\n");
>> +		return ret;
>> +	}
>> +
>> +	dc->nrsts = ARRAY_SIZE(dc->rst_vout);
>> +	for (i = 0; i < dc->nrsts; ++i)
>> +		dc->rst_vout[i].id = vout_resets[i];
>> +	ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
>> +						 dc->rst_vout);
>> +	if (ret) {
>> +		dev_err(dev, "Failed to get reset controls\n");
>> +		return ret;
>> +	}
>> +
>> +	irq = platform_get_irq(pdev, 0);
>> +
>> +	ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
> 
> Are you ready to handle the IRQ at this point?
> 
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to install irq:%u.\n", irq);
>> +		return ret;
>> +	}
>> +
>> +	dev_set_drvdata(dev, dc);
>> +
>> +	return component_add(dev, &dc_component_ops);
>> +}
>> +
>> +static int dc_remove(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +
>> +	component_del(dev, &dc_component_ops);
>> +
>> +	dev_set_drvdata(dev, NULL);
>> +
>> +	return 0;
>> +}
>> +
>> +struct platform_driver dc_platform_driver = {
>> +	.probe = dc_probe,
>> +	.remove = dc_remove,
>> +	.driver = {
>> +		.name = "vs-dc",
>> +		.of_match_table = of_match_ptr(dc_driver_dt_match),
>> +	},
>> +};
>> +
>> +MODULE_AUTHOR("StarFive Corporation");
>> +MODULE_DESCRIPTION("VeriSilicon DC Driver");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
>> new file mode 100644
>> index 000000000..8e96fd32c
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_dc.h
>> @@ -0,0 +1,80 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#ifndef __VS_DC_H__
>> +#define __VS_DC_H__
>> +
>> +#include <linux/clk.h>
>> +#include <linux/mm_types.h>
>> +#include <linux/reset.h>
>> +
>> +#include <drm/drm_fourcc.h>
>> +#include <drm/drm_modes.h>
>> +
>> +#include "vs_crtc.h"
>> +#include "vs_dc_hw.h"
>> +#include "vs_plane.h"
>> +
>> +struct vs_dc_plane {
>> +	enum dc_hw_plane_id id;
>> +};
>> +
>> +enum vout_clk {
>> +	CLK_VOUT_NOC_DISP = 0,
>> +	CLK_VOUT_PIX0,
>> +	CLK_VOUT_PIX1,
>> +	CLK_VOUT_CORE,
>> +	CLK_VOUT_AXI,
>> +	CLK_VOUT_AHB,
>> +	CLK_VOUT_HDMI_PIX,
>> +	CLK_VOUT_SOC_PIX,
>> +	CLK_VOUT_NUM
>> +};
>> +
>> +enum rst_vout {
>> +	RST_VOUT_AXI = 0,
>> +	RST_VOUT_AHB,
>> +	RST_VOUT_CORE,
>> +	RST_VOUT_NUM
>> +};
>> +
>> +struct vs_dc {
>> +	struct vs_crtc		*crtc[DC_DISPLAY_NUM];
>> +	struct dc_hw		hw;
>> +	bool			first_frame;
>> +
>> +	struct vs_dc_plane	planes[PLANE_NUM];
>> +	struct clk_bulk_data	clk_vout[CLK_VOUT_NUM];
>> +	int			nclks;
>> +	struct reset_control_bulk_data rst_vout[RST_VOUT_NUM];
>> +	int			nrsts;
>> +};
>> +
>> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc);
>> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc);
>> +
>> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
>> +		     struct drm_color_lut *lut, unsigned int size);
>> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc, bool enable);
>> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable);
>> +void vs_dc_commit(struct vs_dc *dc);
>> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +			struct drm_plane *drm_plane,
>> +			struct drm_atomic_state *drm_state);
>> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +			 struct drm_plane_state *old_state);
>> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
>> +		      struct drm_atomic_state *state);
>> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +			       struct drm_plane *drm_plane,
>> +			       struct drm_atomic_state *drm_state);
>> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +				struct drm_plane_state *old_state);
>> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
>> +			     struct drm_atomic_state *state);
>> +
>> +extern struct platform_driver dc_platform_driver;
>> +
>> +#endif /* __VS_DC_H__ */
>> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
>> new file mode 100644
>> index 000000000..e5acb7eb4
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
>> @@ -0,0 +1,1959 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#include <linux/bits.h>
>> +#include <linux/io.h>
>> +#include <linux/media-bus-format.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_blend.h>
>> +#include <drm/drm_fourcc.h>
>> +#include <drm/vs_drm.h>
>> +
>> +#include "vs_dc_hw.h"
>> +#include "vs_type.h"
>> +
>> +static const u32 horkernel[] = {
>> +	0x00000000, 0x20000000, 0x00002000, 0x00000000,
>> +	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
>> +	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
>> +	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
>> +	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
>> +	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
>> +	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
>> +	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
>> +	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
>> +	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
>> +	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
>> +	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
>> +	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
>> +	0x00000000, 0x00000000, 0x00000000, 0x01470000,
>> +	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
>> +	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
>> +	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
>> +	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
>> +	0x00000000, 0x00000000, 0x00004000, 0x00000000,
>> +	0x00000000, 0x00000000, 0x20002000, 0x00000000,
>> +	0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
>> +	0x00000000, 0x00000000, 0x00000000, 0x27e1181f,
>> +	0x00000000, 0x00000000, 0x00000000, 0x14680000,
>> +	0x00002b98, 0x00000000, 0x00000000, 0x00000000,
>> +	0x2f1010f0, 0x00000000, 0x00000000, 0x00000000,
>> +	0x0dc70000, 0x00003239, 0x00000000, 0x00000000,
>> +	0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
>> +	0x00000000, 0x087f0000, 0x00003781, 0x00000000,
>> +	0x00000000, 0x00000000, 0x399a0666, 0x00000000,
>> +	0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
>> +	0x00000000, 0x00000000, 0x00000000, 0x3cc4033c,
>> +	0x00000000, 0x00000000, 0x00000000, 0x021f0000,
>> +};
>> +
>> +#define H_COEF_SIZE (sizeof(horkernel) / sizeof(u32))
>> +
>> +static const u32 verkernel[] = {
>> +	0x00000000, 0x20000000, 0x00002000, 0x00000000,
>> +	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
>> +	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
>> +	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
>> +	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
>> +	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
>> +	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
>> +	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
>> +	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
>> +	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
>> +	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
>> +	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
>> +	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
>> +	0x00000000, 0x00000000, 0x00000000, 0x01470000,
>> +	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
>> +	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
>> +	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
>> +	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
>> +	0x00000000, 0x00000000, 0x00004000, 0x00000000,
>> +	0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab,
>> +	0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
>> +	0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +};
>> +
>> +#define V_COEF_SIZE (sizeof(verkernel) / sizeof(u32))
>> +
>> +/*
>> + * RGB 709->2020 conversion parameters
>> + */
>> +static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = {
>> +	10279,	5395,	709,
>> +	1132,	15065,	187,
>> +	269,	1442,	14674
>> +};
>> +
>> +/*
>> + * YUV601 to RGB conversion parameters
>> + * YUV2RGB[0]  - [8] : C0 - C8;
>> + * YUV2RGB[9]  - [11]: D0 - D2;
>> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
>> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
>> + */
>> +static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
>> +	1196,	0,			1640,	1196,
>> +	-404,	-836,		1196,	2076,
>> +	0,		-916224,	558336,	-1202944,
>> +	64,		 940,		64,		960
>> +};
>> +
>> +/*
>> + * YUV709 to RGB conversion parameters
>> + * YUV2RGB[0]  - [8] : C0 - C8;
>> + * YUV2RGB[9]  - [11]: D0 - D2;
>> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
>> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
>> + */
>> +static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
>> +	1196,		0,		1844,	1196,
>> +	-220,		-548,	1196,	2172,
>> +	0,			-1020672, 316672,  -1188608,
>> +	64,			940,		64,		960
>> +};
>> +
>> +/*
>> + * YUV2020 to RGB conversion parameters
>> + * YUV2RGB[0]  - [8] : C0 - C8;
>> + * YUV2RGB[9]  - [11]: D0 - D2;
>> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
>> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
>> + */
>> +static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
>> +	1196, 0, 1724, 1196,
>> +	-192, -668, 1196, 2200,
>> +	0, -959232, 363776, -1202944,
>> +	64, 940, 64, 960
>> +};
>> +
>> +/*
>> + * RGB to YUV2020 conversion parameters
>> + * RGB2YUV[0] - [8] : C0 - C8;
>> + * RGB2YUV[9] - [11]: D0 - D2;
>> + */
>> +static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = {
>> +	230,	594,	52,
>> +	-125,  -323,	448,
>> +	448,   -412,   -36,
>> +	64,		512,	512
>> +};
>> +
>> +/*
>> + * Degamma table for 709 color space data.
>> + */
>> +static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
>> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005,
>> +	0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024,
>> +	0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064,
>> +	0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce,
>> +	0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166,
>> +	0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230,
>> +	0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331,
>> +	0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c,
>> +	0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
>> +	0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d,
>> +	0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999,
>> +	0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb,
>> +	0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65,
>> +	0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a,
>> +	0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b,
>> +	0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc,
>> +	0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e,
>> +	0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
>> +	0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e,
>> +	0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf,
>> +	0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea,
>> +	0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f,
>> +	0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930,
>> +	0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f,
>> +	0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee,
>> +	0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde,
>> +	0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
>> +	0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9,
>> +	0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406,
>> +	0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a,
>> +	0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576,
>> +	0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd,
>> +	0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
>> +};
>> +
>> +/*
>> + * Degamma table for 2020 color space data.
>> + */
>> +static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
>> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
>> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
>> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
>> +	0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
>> +	0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
>> +	0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006,
>> +	0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a,
>> +	0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f,
>> +	0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
>> +	0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024,
>> +	0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035,
>> +	0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d,
>> +	0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d,
>> +	0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098,
>> +	0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3,
>> +	0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123,
>> +	0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d,
>> +	0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
>> +	0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6,
>> +	0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1,
>> +	0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d,
>> +	0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6,
>> +	0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920,
>> +	0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a,
>> +	0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033,
>> +	0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593,
>> +	0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
>> +	0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656,
>> +	0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332,
>> +	0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480,
>> +	0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1,
>> +	0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99,
>> +	0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
>> +};
>> +
>> +/* one is for primary plane and the other is for all overlay planes */
>> +static const struct dc_hw_plane_reg dc_plane_reg[] = {
>> +	{
>> +	.y_address		= DC_FRAMEBUFFER_ADDRESS,
>> +	.u_address		= DC_FRAMEBUFFER_U_ADDRESS,
>> +	.v_address		= DC_FRAMEBUFFER_V_ADDRESS,
>> +	.y_stride		= DC_FRAMEBUFFER_STRIDE,
>> +	.u_stride		= DC_FRAMEBUFFER_U_STRIDE,
>> +	.v_stride		= DC_FRAMEBUFFER_V_STRIDE,
>> +	.size			= DC_FRAMEBUFFER_SIZE,
>> +	.top_left		= DC_FRAMEBUFFER_TOP_LEFT,
>> +	.bottom_right	= DC_FRAMEBUFFER_BOTTOM_RIGHT,
>> +	.scale_factor_x			= DC_FRAMEBUFFER_SCALE_FACTOR_X,
>> +	.scale_factor_y			= DC_FRAMEBUFFER_SCALE_FACTOR_Y,
>> +	.h_filter_coef_index	= DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
>> +	.h_filter_coef_data		= DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
>> +	.v_filter_coef_index	= DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
>> +	.v_filter_coef_data		= DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
>> +	.init_offset			= DC_FRAMEBUFFER_INIT_OFFSET,
>> +	.color_key				= DC_FRAMEBUFFER_COLOR_KEY,
>> +	.color_key_high			= DC_FRAMEBUFFER_COLOR_KEY_HIGH,
>> +	.clear_value			= DC_FRAMEBUFFER_CLEAR_VALUE,
>> +	.color_table_index		= DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
>> +	.color_table_data		= DC_FRAMEBUFFER_COLOR_TABLE_DATA,
>> +	.scale_config			= DC_FRAMEBUFFER_SCALE_CONFIG,
>> +	.water_mark				= DC_FRAMEBUFFER_WATER_MARK,
>> +	.degamma_index			= DC_FRAMEBUFFER_DEGAMMA_INDEX,
>> +	.degamma_data			= DC_FRAMEBUFFER_DEGAMMA_DATA,
>> +	.degamma_ex_data		= DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
>> +	.src_global_color		= DC_FRAMEBUFFER_SRC_GLOBAL_COLOR,
>> +	.dst_global_color		= DC_FRAMEBUFFER_DST_GLOBAL_COLOR,
>> +	.blend_config			= DC_FRAMEBUFFER_BLEND_CONFIG,
>> +	.roi_origin				= DC_FRAMEBUFFER_ROI_ORIGIN,
>> +	.roi_size				= DC_FRAMEBUFFER_ROI_SIZE,
>> +	.yuv_to_rgb_coef0			= DC_FRAMEBUFFER_YUVTORGB_COEF0,
>> +	.yuv_to_rgb_coef1			= DC_FRAMEBUFFER_YUVTORGB_COEF1,
>> +	.yuv_to_rgb_coef2			= DC_FRAMEBUFFER_YUVTORGB_COEF2,
>> +	.yuv_to_rgb_coef3			= DC_FRAMEBUFFER_YUVTORGB_COEF3,
>> +	.yuv_to_rgb_coef4			= DC_FRAMEBUFFER_YUVTORGB_COEF4,
>> +	.yuv_to_rgb_coefd0			= DC_FRAMEBUFFER_YUVTORGB_COEFD0,
>> +	.yuv_to_rgb_coefd1			= DC_FRAMEBUFFER_YUVTORGB_COEFD1,
>> +	.yuv_to_rgb_coefd2			= DC_FRAMEBUFFER_YUVTORGB_COEFD2,
>> +	.y_clamp_bound				= DC_FRAMEBUFFER_Y_CLAMP_BOUND,
>> +	.uv_clamp_bound				= DC_FRAMEBUFFER_UV_CLAMP_BOUND,
>> +	.rgb_to_rgb_coef0			= DC_FRAMEBUFFER_RGBTORGB_COEF0,
>> +	.rgb_to_rgb_coef1			= DC_FRAMEBUFFER_RGBTORGB_COEF1,
>> +	.rgb_to_rgb_coef2			= DC_FRAMEBUFFER_RGBTORGB_COEF2,
>> +	.rgb_to_rgb_coef3			= DC_FRAMEBUFFER_RGBTORGB_COEF3,
>> +	.rgb_to_rgb_coef4			= DC_FRAMEBUFFER_RGBTORGB_COEF4,
>> +	},
>> +	{
>> +	.y_address		= DC_OVERLAY_ADDRESS,
>> +	.u_address		= DC_OVERLAY_U_ADDRESS,
>> +	.v_address		= DC_OVERLAY_V_ADDRESS,
>> +	.y_stride		= DC_OVERLAY_STRIDE,
>> +	.u_stride		= DC_OVERLAY_U_STRIDE,
>> +	.v_stride		= DC_OVERLAY_V_STRIDE,
>> +	.size			= DC_OVERLAY_SIZE,
>> +	.top_left		= DC_OVERLAY_TOP_LEFT,
>> +	.bottom_right	= DC_OVERLAY_BOTTOM_RIGHT,
>> +	.scale_factor_x	= DC_OVERLAY_SCALE_FACTOR_X,
>> +	.scale_factor_y	= DC_OVERLAY_SCALE_FACTOR_Y,
>> +	.h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
>> +	.h_filter_coef_data  = DC_OVERLAY_H_FILTER_COEF_DATA,
>> +	.v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
>> +	.v_filter_coef_data  = DC_OVERLAY_V_FILTER_COEF_DATA,
>> +	.init_offset		 = DC_OVERLAY_INIT_OFFSET,
>> +	.color_key			 = DC_OVERLAY_COLOR_KEY,
>> +	.color_key_high			= DC_OVERLAY_COLOR_KEY_HIGH,
>> +	.clear_value		 = DC_OVERLAY_CLEAR_VALUE,
>> +	.color_table_index	 = DC_OVERLAY_COLOR_TABLE_INDEX,
>> +	.color_table_data	 = DC_OVERLAY_COLOR_TABLE_DATA,
>> +	.scale_config		 = DC_OVERLAY_SCALE_CONFIG,
>> +	.water_mark				= DC_OVERLAY_WATER_MARK,
>> +	.degamma_index		 = DC_OVERLAY_DEGAMMA_INDEX,
>> +	.degamma_data		 = DC_OVERLAY_DEGAMMA_DATA,
>> +	.degamma_ex_data	 = DC_OVERLAY_DEGAMMA_EX_DATA,
>> +	.src_global_color	 = DC_OVERLAY_SRC_GLOBAL_COLOR,
>> +	.dst_global_color	 = DC_OVERLAY_DST_GLOBAL_COLOR,
>> +	.blend_config		 = DC_OVERLAY_BLEND_CONFIG,
>> +	.roi_origin				= DC_OVERLAY_ROI_ORIGIN,
>> +	.roi_size				= DC_OVERLAY_ROI_SIZE,
>> +	.yuv_to_rgb_coef0		 = DC_OVERLAY_YUVTORGB_COEF0,
>> +	.yuv_to_rgb_coef1		 = DC_OVERLAY_YUVTORGB_COEF1,
>> +	.yuv_to_rgb_coef2		 = DC_OVERLAY_YUVTORGB_COEF2,
>> +	.yuv_to_rgb_coef3		 = DC_OVERLAY_YUVTORGB_COEF3,
>> +	.yuv_to_rgb_coef4			= DC_OVERLAY_YUVTORGB_COEF4,
>> +	.yuv_to_rgb_coefd0			= DC_OVERLAY_YUVTORGB_COEFD0,
>> +	.yuv_to_rgb_coefd1			= DC_OVERLAY_YUVTORGB_COEFD1,
>> +	.yuv_to_rgb_coefd2			= DC_OVERLAY_YUVTORGB_COEFD2,
>> +	.y_clamp_bound		 = DC_OVERLAY_Y_CLAMP_BOUND,
>> +	.uv_clamp_bound		 = DC_OVERLAY_UV_CLAMP_BOUND,
>> +	.rgb_to_rgb_coef0		 = DC_OVERLAY_RGBTORGB_COEF0,
>> +	.rgb_to_rgb_coef1		 = DC_OVERLAY_RGBTORGB_COEF1,
>> +	.rgb_to_rgb_coef2		 = DC_OVERLAY_RGBTORGB_COEF2,
>> +	.rgb_to_rgb_coef3		 = DC_OVERLAY_RGBTORGB_COEF3,
>> +	.rgb_to_rgb_coef4		 = DC_OVERLAY_RGBTORGB_COEF4,
>> +	},
>> +};
>> +
>> +static const u32 primary_overlay_format0[] = {
>> +	DRM_FORMAT_XRGB4444,
>> +	DRM_FORMAT_XBGR4444,
>> +	DRM_FORMAT_RGBX4444,
>> +	DRM_FORMAT_BGRX4444,
>> +	DRM_FORMAT_ARGB4444,
>> +	DRM_FORMAT_ABGR4444,
>> +	DRM_FORMAT_RGBA4444,
>> +	DRM_FORMAT_BGRA4444,
>> +	DRM_FORMAT_XRGB1555,
>> +	DRM_FORMAT_XBGR1555,
>> +	DRM_FORMAT_RGBX5551,
>> +	DRM_FORMAT_BGRX5551,
>> +	DRM_FORMAT_ARGB1555,
>> +	DRM_FORMAT_ABGR1555,
>> +	DRM_FORMAT_RGBA5551,
>> +	DRM_FORMAT_BGRA5551,
>> +	DRM_FORMAT_RGB565,
>> +	DRM_FORMAT_BGR565,
>> +	DRM_FORMAT_XRGB8888,
>> +	DRM_FORMAT_XBGR8888,
>> +	DRM_FORMAT_RGBX8888,
>> +	DRM_FORMAT_BGRX8888,
>> +	DRM_FORMAT_ARGB8888,
>> +	DRM_FORMAT_ABGR8888,
>> +	DRM_FORMAT_RGBA8888,
>> +	DRM_FORMAT_BGRA8888,
>> +	DRM_FORMAT_ARGB2101010,
>> +	DRM_FORMAT_ABGR2101010,
>> +	DRM_FORMAT_RGBA1010102,
>> +	DRM_FORMAT_BGRA1010102,
>> +	DRM_FORMAT_YUYV,
>> +	DRM_FORMAT_YVYU,
>> +	DRM_FORMAT_UYVY,
>> +	DRM_FORMAT_VYUY,
>> +	DRM_FORMAT_YVU420,
>> +	DRM_FORMAT_YUV420,
>> +	DRM_FORMAT_NV12,
>> +	DRM_FORMAT_NV21,
>> +	DRM_FORMAT_NV16,
>> +	DRM_FORMAT_NV61,
>> +	DRM_FORMAT_P010,
>> +};
>> +
>> +static const u32 primary_overlay_format1[] = {
>> +	DRM_FORMAT_XRGB8888,
>> +	DRM_FORMAT_XBGR8888,
>> +	DRM_FORMAT_RGBX8888,
>> +	DRM_FORMAT_BGRX8888,
>> +	DRM_FORMAT_ARGB8888,
>> +	DRM_FORMAT_ABGR8888,
>> +	DRM_FORMAT_RGBA8888,
>> +	DRM_FORMAT_BGRA8888,
>> +	DRM_FORMAT_ARGB2101010,
>> +	DRM_FORMAT_ABGR2101010,
>> +	DRM_FORMAT_RGBA1010102,
>> +	DRM_FORMAT_BGRA1010102,
>> +	DRM_FORMAT_NV12,
>> +	DRM_FORMAT_NV21,
>> +	DRM_FORMAT_YUV444,
>> +};
>> +
>> +static const u32 cursor_formats[] = {
>> +	DRM_FORMAT_ARGB8888
>> +};
>> +
>> +static const u64 format_modifier0[] = {
>> +	DRM_FORMAT_MOD_LINEAR,
>> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8),
>> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8),
>> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X8),
>> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X4),
>> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4),
>> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8),
>> +	DRM_FORMAT_MOD_INVALID
>> +};
>> +
>> +static const u64 secondary_format_modifiers[] = {
>> +	DRM_FORMAT_MOD_LINEAR,
>> +	DRM_FORMAT_MOD_INVALID
>> +};
>> +
>> +#define FRAC_16_16(mult, div)	 (((mult) << 16) / (div))
>> +
>> +static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = {
>> +	{
>> +		/* DC_REV_0 */
>> +		{
>> +		.name			= "Primary",
> 
> I'd say that the indentation is strange here.
> It might be more scalable to split a single array into per-generation 
> data structures and reference them directly from the info struct.
> 
>> +		.id				= PRIMARY_PLANE_0,
>> +		.type			= DRM_PLANE_TYPE_PRIMARY,
>> +		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
>> +		.formats		= primary_overlay_format0,
>> +		.num_modifiers	= ARRAY_SIZE(format_modifier0),
>> +		.modifiers		= format_modifier0,
>> +		.min_width		= 0,
>> +		.min_height		= 0,
>> +		.max_width		= 4096,
>> +		.max_height		= 4096,
>> +		.rotation		= DRM_MODE_ROTATE_0 |
>> +							DRM_MODE_ROTATE_90 |
>> +							DRM_MODE_ROTATE_180 |
>> +							DRM_MODE_ROTATE_270 |
>> +							DRM_MODE_REFLECT_X |
>> +							DRM_MODE_REFLECT_Y,
>> +		.blend_mode		= BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +							BIT(DRM_MODE_BLEND_PREMULTI) |
>> +							BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 0,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name			= "Overlay",
>> +		.id				= OVERLAY_PLANE_0,
>> +		.type			= DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
>> +		.formats		= primary_overlay_format0,
>> +		.num_modifiers	= ARRAY_SIZE(format_modifier0),
>> +		.modifiers		= format_modifier0,
>> +		.min_width		= 0,
>> +		.min_height		= 0,
>> +		.max_width		= 4096,
>> +		.max_height		= 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 1,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name			= "Overlay_1",
>> +		.id				= OVERLAY_PLANE_1,
>> +		.type			= DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
>> +		.formats		= primary_overlay_format0,
>> +		.num_modifiers	= ARRAY_SIZE(secondary_format_modifiers),
>> +		.modifiers		= secondary_format_modifiers,
>> +		.min_width		= 0,
>> +		.min_height		= 0,
>> +		.max_width		= 4096,
>> +		.max_height		= 4096,
>> +		.rotation		= 0,
>> +		.blend_mode		= BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +							BIT(DRM_MODE_BLEND_PREMULTI) |
>> +							BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 2,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Primary_1",
>> +		.id				= PRIMARY_PLANE_1,
>> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
>> +		.formats	 = primary_overlay_format0,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 3,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay_2",
>> +		.id				= OVERLAY_PLANE_2,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
>> +		.formats	 = primary_overlay_format0,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 4,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay_3",
>> +		.id			= OVERLAY_PLANE_3,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
>> +		.formats	 = primary_overlay_format0,
>> +		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
>> +		.modifiers	 = secondary_format_modifiers,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = 0,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 5,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Cursor",
>> +		.id				= CURSOR_PLANE_0,
>> +		.type		 = DRM_PLANE_TYPE_CURSOR,
>> +		.num_formats = ARRAY_SIZE(cursor_formats),
>> +		.formats	 = cursor_formats,
>> +		.num_modifiers = 0,
>> +		.modifiers	 = NULL,
>> +		.min_width	 = 32,
>> +		.min_height  = 32,
>> +		.max_width	 = 64,
>> +		.max_height  = 64,
>> +		.rotation	 = 0,
>> +		.degamma_size = 0,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 255,
>> +		.watermark	 = false,
>> +		.color_mgmt  = false,
>> +		.roi		 = false,
>> +		},
>> +		{
>> +		.name		 = "Cursor_1",
>> +		.id				= CURSOR_PLANE_1,
>> +		.type		 = DRM_PLANE_TYPE_CURSOR,
>> +		.num_formats = ARRAY_SIZE(cursor_formats),
>> +		.formats	 = cursor_formats,
>> +		.num_modifiers = 0,
>> +		.modifiers	 = NULL,
>> +		.min_width	 = 32,
>> +		.min_height  = 32,
>> +		.max_width	 = 64,
>> +		.max_height  = 64,
>> +		.rotation	 = 0,
>> +		.degamma_size = 0,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 255,
>> +		.watermark	 = false,
>> +		.color_mgmt  = false,
>> +		.roi		 = false,
>> +		},
>> +	},
>> +	{
>> +		/* DC_REV_1 */
>> +		{
>> +		.name		 = "Primary",
>> +		.id				= PRIMARY_PLANE_0,
>> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
>> +		.formats	 = primary_overlay_format0,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 0,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay",
>> +		.id				= OVERLAY_PLANE_0,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
>> +		.formats	 = primary_overlay_format0,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 1,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Primary_1",
>> +		.id				= PRIMARY_PLANE_1,
>> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
>> +		.formats	 = primary_overlay_format0,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 2,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay_2",
>> +		.id				= OVERLAY_PLANE_2,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
>> +		.formats	 = primary_overlay_format0,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 3,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Cursor",
>> +		.id				= CURSOR_PLANE_0,
>> +		.type		 = DRM_PLANE_TYPE_CURSOR,
>> +		.num_formats = ARRAY_SIZE(cursor_formats),
>> +		.formats	 = cursor_formats,
>> +		.num_modifiers = 0,
>> +		.modifiers	 = NULL,
>> +		.min_width	 = 32,
>> +		.min_height  = 32,
>> +		.max_width	 = 64,
>> +		.max_height  = 64,
>> +		.rotation	 = 0,
>> +		.degamma_size = 0,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 255,
>> +		.watermark	 = false,
>> +		.color_mgmt  = false,
>> +		.roi		 = false,
>> +		},
>> +		{
>> +		.name		 = "Cursor_1",
>> +		.id				= CURSOR_PLANE_1,
>> +		.type		 = DRM_PLANE_TYPE_CURSOR,
>> +		.num_formats = ARRAY_SIZE(cursor_formats),
>> +		.formats	 = cursor_formats,
>> +		.num_modifiers = 0,
>> +		.modifiers	 = NULL,
>> +		.min_width	 = 32,
>> +		.min_height  = 32,
>> +		.max_width	 = 64,
>> +		.max_height  = 64,
>> +		.rotation	 = 0,
>> +		.degamma_size = 0,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 255,
>> +		.watermark	 = false,
>> +		.color_mgmt  = false,
>> +		.roi		 = false,
>> +		},
>> +	},
>> +	{
>> +		/* DC_REV_2 */
>> +		{
>> +		.name		 = "Primary",
>> +		.id				= PRIMARY_PLANE_0,
>> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
>> +		.formats	 = primary_overlay_format1,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 0,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay",
>> +		.id				= OVERLAY_PLANE_0,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
>> +		.formats	 = primary_overlay_format1,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 1,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay_1",
>> +		.id				= OVERLAY_PLANE_1,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
>> +		.formats	 = primary_overlay_format1,
>> +		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
>> +		.modifiers	 = secondary_format_modifiers,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = 0,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 2,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Primary_1",
>> +		.id				= PRIMARY_PLANE_1,
>> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
>> +		.formats	 = primary_overlay_format1,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 3,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay_2",
>> +		.id				= OVERLAY_PLANE_2,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
>> +		.formats	 = primary_overlay_format1,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 4,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay_3",
>> +		.id				= OVERLAY_PLANE_3,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
>> +		.formats	 = primary_overlay_format1,
>> +		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
>> +		.modifiers	 = secondary_format_modifiers,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = 0,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 5,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Cursor",
>> +		.id				= CURSOR_PLANE_0,
>> +		.type		 = DRM_PLANE_TYPE_CURSOR,
>> +		.num_formats = ARRAY_SIZE(cursor_formats),
>> +		.formats	 = cursor_formats,
>> +		.num_modifiers = 0,
>> +		.modifiers	 = NULL,
>> +		.min_width	 = 32,
>> +		.min_height  = 32,
>> +		.max_width	 = 64,
>> +		.max_height  = 64,
>> +		.rotation	 = 0,
>> +		.degamma_size = 0,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 255,
>> +		.watermark	 = false,
>> +		.color_mgmt  = false,
>> +		.roi		 = false,
>> +		},
>> +		{
>> +		.name		 = "Cursor_1",
>> +		.id				= CURSOR_PLANE_1,
>> +		.type		 = DRM_PLANE_TYPE_CURSOR,
>> +		.num_formats = ARRAY_SIZE(cursor_formats),
>> +		.formats	 = cursor_formats,
>> +		.num_modifiers = 0,
>> +		.modifiers	 = NULL,
>> +		.min_width	 = 32,
>> +		.min_height  = 32,
>> +		.max_width	 = 64,
>> +		.max_height  = 64,
>> +		.rotation	 = 0,
>> +		.degamma_size = 0,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 255,
>> +		.watermark	 = false,
>> +		.color_mgmt  = false,
>> +		.roi		 = false,
>> +		},
>> +	},
>> +};
>> +
>> +static const struct vs_dc_info dc_info[] = {
> 
> Again, please split into per-generation instances.
> 
>> +	{
>> +		/* DC_REV_0 */
>> +		.name			= "DC8200",
>> +		.panel_num		= 2,
>> +		.plane_num		= 8,
>> +		.planes			= dc_hw_planes[DC_REV_0],
>> +		.layer_num		= 6,
>> +		.max_bpc		= 10,
>> +		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
>> +						  DRM_COLOR_FORMAT_YCBCR444 |
>> +						  DRM_COLOR_FORMAT_YCBCR422 |
>> +						  DRM_COLOR_FORMAT_YCBCR420,
>> +		.gamma_size		= GAMMA_EX_SIZE,
>> +		.gamma_bits		= 12,
>> +		.pitch_alignment	= 128,
>> +		.pipe_sync		= false,
>> +		.background		= true,
>> +		.panel_sync		= true,
>> +		.cap_dec		= true,
>> +	},
>> +	{
>> +		/* DC_REV_1 */
>> +		.name			= "DC8200",
>> +		.panel_num		= 2,
>> +		.plane_num		= 6,
>> +		.planes			= dc_hw_planes[DC_REV_1],
>> +		.layer_num		= 4,
>> +		.max_bpc		= 10,
>> +		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
>> +						  DRM_COLOR_FORMAT_YCBCR444 |
>> +						  DRM_COLOR_FORMAT_YCBCR422 |
>> +						  DRM_COLOR_FORMAT_YCBCR420,
>> +		.gamma_size		= GAMMA_EX_SIZE,
>> +		.gamma_bits		= 12,
>> +		.pitch_alignment	= 128,
>> +		.pipe_sync		= false,
>> +		.background		= true,
>> +		.panel_sync		= true,
>> +		.cap_dec		= true,
>> +	},
>> +	{
>> +		/* DC_REV_2 */
>> +		.name			= "DC8200",
>> +		.panel_num		= 2,
>> +		.plane_num		= 8,
>> +		.planes			= dc_hw_planes[DC_REV_2],
>> +		.layer_num		= 6,
>> +		.max_bpc		= 10,
>> +		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
>> +						  DRM_COLOR_FORMAT_YCBCR444 |
>> +						  DRM_COLOR_FORMAT_YCBCR422 |
>> +						  DRM_COLOR_FORMAT_YCBCR420,
>> +		.gamma_size		= GAMMA_EX_SIZE,
>> +		.gamma_bits		= 12,
>> +		.pitch_alignment	= 128,
>> +		.pipe_sync		= false,
>> +		.background		= true,
>> +		.panel_sync		= true,
>> +		.cap_dec		= false,
>> +	},
>> +};
>> +
>> +static const struct dc_hw_funcs hw_func;
>> +
>> +static inline u32 hi_read(struct dc_hw *hw, u32 reg)
>> +{
>> +	return readl(hw->hi_base + reg);
>> +}
>> +
>> +static inline void hi_write(struct dc_hw *hw, u32 reg, u32 value)
>> +{
>> +	writel(value, hw->hi_base + reg);
>> +}
>> +
>> +static inline void dc_write(struct dc_hw *hw, u32 reg, u32 value)
>> +{
>> +	writel(value, hw->reg_base + reg - DC_REG_BASE);
>> +}
>> +
>> +static inline u32 dc_read(struct dc_hw *hw, u32 reg)
>> +{
>> +	u32 value = readl(hw->reg_base + reg - DC_REG_BASE);
>> +
>> +	return value;
>> +}
>> +
>> +static inline void dc_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear)
>> +{
>> +	u32 value = dc_read(hw, reg);
>> +
>> +	value &= ~clear;
>> +	value |= set;
>> +	dc_write(hw, reg, value);
>> +}
>> +
>> +static void load_default_filter(struct dc_hw *hw,
>> +				const struct dc_hw_plane_reg *reg, u32 offset)
>> +{
>> +	u8 i;
>> +
>> +	dc_write(hw, reg->scale_config + offset, 0x33);
>> +	dc_write(hw, reg->init_offset + offset, 0x80008000);
>> +	dc_write(hw, reg->h_filter_coef_index + offset, 0x00);
>> +	for (i = 0; i < H_COEF_SIZE; i++)
>> +		dc_write(hw, reg->h_filter_coef_data + offset, horkernel[i]);
>> +
>> +	dc_write(hw, reg->v_filter_coef_index + offset, 0x00);
>> +	for (i = 0; i < V_COEF_SIZE; i++)
>> +		dc_write(hw, reg->v_filter_coef_data + offset, verkernel[i]);
>> +}
>> +
>> +static void load_rgb_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
>> +			    u32 offset, u16 *table)
>> +{
>> +	dc_write(hw, reg->rgb_to_rgb_coef0 + offset, table[0] | (table[1] << 16));
>> +	dc_write(hw, reg->rgb_to_rgb_coef1 + offset, table[2] | (table[3] << 16));
>> +	dc_write(hw, reg->rgb_to_rgb_coef2 + offset, table[4] | (table[5] << 16));
>> +	dc_write(hw, reg->rgb_to_rgb_coef3 + offset, table[6] | (table[7] << 16));
>> +	dc_write(hw, reg->rgb_to_rgb_coef4 + offset, table[8]);
>> +}
>> +
>> +static void load_yuv_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
>> +			    u32 offset, s32 *table)
>> +{
>> +	dc_write(hw, reg->yuv_to_rgb_coef0 + offset,
>> +		 (0xFFFF & table[0]) | (table[1] << 16));
>> +	dc_write(hw, reg->yuv_to_rgb_coef1 + offset,
>> +		 (0xFFFF & table[2]) | (table[3] << 16));
>> +	dc_write(hw, reg->yuv_to_rgb_coef2 + offset,
>> +		 (0xFFFF & table[4]) | (table[5] << 16));
>> +	dc_write(hw, reg->yuv_to_rgb_coef3 + offset,
>> +		 (0xFFFF & table[6]) | (table[7] << 16));
>> +	dc_write(hw, reg->yuv_to_rgb_coef4 + offset, table[8]);
>> +	dc_write(hw, reg->yuv_to_rgb_coefd0 + offset, table[9]);
>> +	dc_write(hw, reg->yuv_to_rgb_coefd1 + offset, table[10]);
>> +	dc_write(hw, reg->yuv_to_rgb_coefd2 + offset, table[11]);
>> +	dc_write(hw, reg->y_clamp_bound + offset, table[12] | (table[13] << 16));
>> +	dc_write(hw, reg->uv_clamp_bound + offset, table[14] | (table[15] << 16));
>> +}
>> +
>> +static void load_rgb_to_yuv(struct dc_hw *hw, u32 offset, s16 *table)
>> +{
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF0 + offset,
>> +		 table[0] | (table[1] << 16));
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF1 + offset,
>> +		 table[2] | (table[3] << 16));
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF2 + offset,
>> +		 table[4] | (table[5] << 16));
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF3 + offset,
>> +		 table[6] | (table[7] << 16));
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF4 + offset, table[8]);
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD0 + offset, table[9]);
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD1 + offset, table[10]);
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD2 + offset, table[11]);
>> +}
>> +
>> +static bool is_rgb(enum dc_hw_color_format format)
>> +{
>> +	switch (format) {
>> +	case FORMAT_X4R4G4B4:
>> +	case FORMAT_A4R4G4B4:
>> +	case FORMAT_X1R5G5B5:
>> +	case FORMAT_A1R5G5B5:
>> +	case FORMAT_R5G6B5:
>> +	case FORMAT_X8R8G8B8:
>> +	case FORMAT_A8R8G8B8:
>> +	case FORMAT_A2R10G10B10:
>> +		return true;
>> +	default:
>> +		return false;
>> +	}
>> +}
>> +
>> +static void load_degamma_table(struct dc_hw *hw,
>> +			       const struct dc_hw_plane_reg *reg,
>> +			       u32 offset, u16 *table)
>> +{
>> +	u16 i;
>> +	u32 value;
>> +
>> +	dc_write(hw, reg->degamma_index + offset, 0);
>> +
>> +	for (i = 0; i < DEGAMMA_SIZE; i++) {
>> +		value = table[i] | (table[i] << 16);
>> +		dc_write(hw, reg->degamma_data + offset, value);
>> +		dc_write(hw, reg->degamma_ex_data + offset, table[i]);
>> +	}
>> +}
>> +
>> +static u32 get_addr_offset(u32 id)
>> +{
>> +	u32 offset = 0;
>> +
>> +	switch (id) {
>> +	case PRIMARY_PLANE_1:
>> +	case OVERLAY_PLANE_1:
>> +		offset = 0x04;
>> +		break;
>> +	case OVERLAY_PLANE_2:
>> +		offset = 0x08;
>> +		break;
>> +	case OVERLAY_PLANE_3:
>> +		offset = 0x0C;
>> +		break;
>> +	default:
>> +		break;
>> +	}
>> +
>> +	return offset;
>> +}
>> +
>> +int dc_hw_init(struct dc_hw *hw)
>> +{
>> +	u8 i, id, panel_num, layer_num;
>> +	u32 offset;
>> +	u32 revision = hi_read(hw, DC_HW_REVISION);
>> +	u32 cid = hi_read(hw, DC_HW_CHIP_CID);
>> +	const struct dc_hw_plane_reg *reg;
>> +
>> +	switch (revision) {
>> +	case 0x5720:
>> +		hw->rev = DC_REV_0;
>> +		break;
>> +	case 0x5721:
>> +		switch (cid) {
>> +		case 0x30B:
>> +			hw->rev = DC_REV_1;
>> +			break;
>> +		case 0x310:
>> +			hw->rev = DC_REV_2;
>> +			break;
>> +		default:
>> +			hw->rev = DC_REV_0;
>> +			break;
>> +		}
>> +		break;
>> +	default:
>> +		return -ENXIO;
>> +	}
> 
> We (drm/msm) had code like this for a while (and autodetection of the HW 
> ID). And nowadays we are migrating to of_device_get_match_data and 
> per-SoC compat names. It is just easier to handle, it allows one to 
> specify device more precisely and also it simplifies DT schema.
> 
>> +
>> +	hw->info = (struct vs_dc_info *)&dc_info[hw->rev];
>> +	hw->func = (struct dc_hw_funcs *)&hw_func;
>> +
>> +	layer_num = hw->info->layer_num;
>> +	for (i = 0; i < layer_num; i++) {
>> +		id = hw->info->planes[i].id;
>> +		offset = get_addr_offset(id);
>> +		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
>> +			reg = &dc_plane_reg[0];
>> +		else
>> +			reg = &dc_plane_reg[1];
>> +
>> +		load_default_filter(hw, reg, offset);
>> +		load_rgb_to_rgb(hw, reg, offset, RGB2RGB);
>> +	}
>> +
>> +	panel_num = hw->info->panel_num;
>> +	for (i = 0; i < panel_num; i++) {
>> +		offset = i << 2;
>> +
>> +		load_rgb_to_yuv(hw, offset, RGB2YUV);
>> +		dc_write(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0x111);
>> +
>> +		offset = i ? DC_CURSOR_OFFSET : 0;
>> +		dc_write(hw, DC_CURSOR_BACKGROUND + offset, 0x00FFFFFF);
>> +		dc_write(hw, DC_CURSOR_FOREGROUND + offset, 0x00AAAAAA);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +void dc_hw_deinit(struct dc_hw *hw)
>> +{
>> +	/* Nothing to do */
>> +}
>> +
>> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
>> +			struct dc_hw_fb *fb, struct dc_hw_scale *scale,
>> +			struct dc_hw_position *pos, struct dc_hw_blend *blend)
>> +{
>> +	struct dc_hw_plane *plane = &hw->plane[id];
>> +
>> +	if (plane) {
>> +		if (fb) {
>> +			if (!fb->enable)
>> +				plane->fb.enable = false;
>> +			else
>> +				memcpy(&plane->fb, fb,
>> +				       sizeof(*fb) - sizeof(fb->dirty));
>> +			plane->fb.dirty = true;
>> +		}
>> +		if (scale) {
>> +			memcpy(&plane->scale, scale,
>> +			       sizeof(*scale) - sizeof(scale->dirty));
>> +			plane->scale.dirty = true;
>> +		}
>> +		if (pos) {
>> +			memcpy(&plane->pos, pos,
>> +			       sizeof(*pos) - sizeof(pos->dirty));
>> +			plane->pos.dirty = true;
>> +		}
>> +		if (blend) {
>> +			memcpy(&plane->blend, blend,
>> +			       sizeof(*blend) - sizeof(blend->dirty));
>> +			plane->blend.dirty = true;
>> +		}
>> +	}
>> +}
>> +
>> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode)
>> +{
>> +	struct dc_hw_plane *plane = &hw->plane[id];
>> +
>> +	if (plane) {
>> +		if (hw->info->planes[id].degamma_size) {
>> +			plane->degamma.mode = mode;
>> +			plane->degamma.dirty = true;
>> +		} else {
>> +			plane->degamma.dirty = false;
>> +		}
>> +	}
>> +}
>> +
>> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi)
>> +{
>> +	struct dc_hw_plane *plane = &hw->plane[id];
>> +
>> +	if (plane) {
>> +		memcpy(&plane->roi, roi, sizeof(*roi) - sizeof(roi->dirty));
>> +		plane->roi.dirty = true;
>> +	}
>> +}
>> +
>> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
>> +			   struct dc_hw_colorkey *colorkey)
>> +{
>> +	struct dc_hw_plane *plane = &hw->plane[id];
>> +
>> +	if (plane) {
>> +		memcpy(&plane->colorkey, colorkey,
>> +		       sizeof(*colorkey) - sizeof(colorkey->dirty));
>> +		plane->colorkey.dirty = true;
>> +	}
>> +}
>> +
>> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos)
>> +{
>> +	memcpy(&hw->qos, qos, sizeof(*qos) - sizeof(qos->dirty));
>> +	hw->qos.dirty = true;
>> +}
>> +
>> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor)
>> +{
>> +	memcpy(&hw->cursor[id], cursor, sizeof(*cursor) - sizeof(cursor->dirty));
>> +	hw->cursor[id].dirty = true;
>> +}
>> +
>> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
>> +			u16 r, u16 g, u16 b)
>> +{
>> +	if (index >= hw->info->gamma_size)
>> +		return;
>> +
>> +	hw->gamma[id].gamma[index][0] = r;
>> +	hw->gamma[id].gamma[index][1] = g;
>> +	hw->gamma[id].gamma[index][2] = b;
>> +	hw->gamma[id].dirty = true;
>> +}
>> +
>> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable)
>> +{
>> +	hw->gamma[id].enable = enable;
>> +	hw->gamma[id].dirty = true;
>> +}
>> +
>> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display)
>> +{
>> +	u8 id = display->id;
>> +
>> +	memcpy(&hw->display[id], display, sizeof(*display));
>> +
>> +	hw->func->display(hw, display);
>> +}
>> +
>> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable)
>> +{
>> +	if (enable)
>> +		hi_write(hw, AQ_INTR_ENBL, 0xFFFFFFFF);
>> +	else
>> +		hi_write(hw, AQ_INTR_ENBL, 0);
>> +}
>> +
>> +u32 dc_hw_get_interrupt(struct dc_hw *hw)
>> +{
>> +	return hi_read(hw, AQ_INTR_ACKNOWLEDGE);
>> +}
>> +
>> +bool dc_hw_check_underflow(struct dc_hw *hw)
>> +{
>> +	return dc_read(hw, DC_FRAMEBUFFER_CONFIG) & BIT(5);
>> +}
>> +
>> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable)
>> +{
>> +	u32 i, offset;
>> +	u8 id, layer_num = hw->info->layer_num;
>> +	u8 panel_num = hw->info->panel_num;
>> +
>> +	for (i = 0; i < layer_num; i++) {
>> +		id = hw->info->planes[i].id;
>> +		offset = get_addr_offset(id);
>> +		if (enable) {
>> +			if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
>> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, BIT(12), 0);
>> +			else
>> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, BIT(31), 0);
>> +		} else {
>> +			if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
>> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, 0, BIT(12));
>> +			else
>> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, 0, BIT(31));
>> +		}
>> +	}
>> +
>> +	for (i = 0; i < panel_num; i++) {
>> +		offset = i << 2;
>> +		if (enable)
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, BIT(0));
>> +		else
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, BIT(0), 0);
>> +	}
>> +}
>> +
>> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id)
>> +{
>> +	if (out <= OUT_DP)
>> +		hw->out[id] = out;
>> +}
>> +
>> +static void gamma_ex_commit(struct dc_hw *hw)
>> +{
>> +	u8 panel_num = hw->info->panel_num;
>> +	u16 i, j;
>> +	u32 value;
>> +
>> +	for (j = 0; j < panel_num; j++) {
>> +		if (hw->gamma[j].dirty) {
>> +			if (hw->gamma[j].enable) {
>> +				dc_write(hw, DC_DISPLAY_GAMMA_EX_INDEX + (j << 2), 0x00);
>> +				for (i = 0; i < GAMMA_EX_SIZE; i++) {
>> +					value = hw->gamma[j].gamma[i][2] |
>> +						(hw->gamma[j].gamma[i][1] << 12);
>> +					dc_write(hw, DC_DISPLAY_GAMMA_EX_DATA + (j << 2), value);
>> +					dc_write(hw, DC_DISPLAY_GAMMA_EX_ONE_DATA + (j << 2),
>> +						 hw->gamma[j].gamma[i][0]);
>> +				}
>> +				dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
>> +					     BIT(13), 0);
>> +			} else {
>> +				dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
>> +					     0, BIT(13));
>> +			}
>> +			hw->gamma[j].dirty = false;
>> +		}
>> +	}
>> +}
>> +
>> +static void plane_commit(struct dc_hw *hw)
>> +{
>> +	struct dc_hw_plane *plane;
>> +	const struct dc_hw_plane_reg *reg;
>> +	bool primary = false;
>> +	u8 id, layer_num = hw->info->layer_num;
>> +	u32 i, offset;
>> +
>> +	for (i = 0; i < layer_num; i++) {
>> +		plane = &hw->plane[i];
>> +		id = hw->info->planes[i].id;
>> +		offset = get_addr_offset(id);
>> +		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
>> +			reg = &dc_plane_reg[0];
>> +			primary = true;
>> +		} else {
>> +			reg = &dc_plane_reg[1];
>> +			primary = false;
>> +		}
> 
> Can you please move register defines to some internal hw_plane 
> structure. These details are known at the time of creation of the plane. 
> There is no need to determine them again and again.
> 
>> +
>> +		if (plane->fb.dirty) {
>> +			if (plane->fb.enable) {
> 
> if (plane->fb.dirty && plane->fb.enable) will save you one level of 
> indentation.
> 
> However it might be better to use existing DRM flags instead of 
> inventing your own flags.
> 
>> +				dc_write(hw, reg->y_address + offset,
>> +					 plane->fb.y_address);
>> +				dc_write(hw, reg->u_address + offset,
>> +					 plane->fb.u_address);
>> +				dc_write(hw, reg->v_address + offset,
>> +					 plane->fb.v_address);
>> +				dc_write(hw, reg->y_stride + offset,
>> +					 plane->fb.y_stride);
>> +				dc_write(hw, reg->u_stride + offset,
>> +					 plane->fb.u_stride);
>> +				dc_write(hw, reg->v_stride + offset,
>> +					 plane->fb.v_stride);
>> +				dc_write(hw, reg->size + offset,
>> +					 plane->fb.width |
>> +					 (plane->fb.height << 15));
>> +				dc_write(hw, reg->water_mark + offset,
>> +					 plane->fb.water_mark);
>> +
>> +				if (plane->fb.clear_enable)
>> +					dc_write(hw, reg->clear_value + offset,
>> +						 plane->fb.clear_value);
>> +			}
>> +
>> +			if (primary) {
> 
> You have two kinds of planes. Primary and overlays. Could you please 
> split this code into two separate functions and define two vtables, one 
> for primary planes and one for overlays. There is no need to stick 
> everything into a single funcion that handles everything.
> 
>> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
>> +					     (plane->fb.format << 26) |
>> +					     (plane->fb.uv_swizzle << 25) |
>> +					     (plane->fb.swizzle << 23) |
>> +					     (plane->fb.tile_mode << 17) |
>> +					     (plane->fb.yuv_color_space << 14) |
>> +					     (plane->fb.rotation << 11) |
>> +					     (plane->fb.clear_enable << 8),
>> +					     (0x1F << 26) |
>> +					     BIT(25) |
>> +					     (0x03 << 23) |
>> +					     (0x1F << 17) |
>> +					     (0x07 << 14) |
>> +					     (0x07 << 11) |
>> +					     BIT(8));
>> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +					     (plane->fb.dec_enable << 1) |
>> +					     (plane->fb.enable << 13) |
>> +					     (plane->fb.zpos << 16) |
>> +					     (plane->fb.display_id << 19),
>> +					     BIT(1) | BIT(13) | (0x07 << 16) | BIT(19));
> 
> It looks like this code can enjoy defining names for all the magic 
> values. Otherwise it is impossible to support it.
> 
>> +			} else {
>> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> +					     (plane->fb.dec_enable << 27) |
>> +					     (plane->fb.clear_enable << 25) |
>> +					     (plane->fb.enable << 24) |
>> +					     (plane->fb.format << 16) |
>> +					     (plane->fb.uv_swizzle << 15) |
>> +					     (plane->fb.swizzle << 13) |
>> +					     (plane->fb.tile_mode << 8) |
>> +					     (plane->fb.yuv_color_space << 5) |
>> +					     (plane->fb.rotation << 2),
>> +					     BIT(27) |
>> +					     BIT(25) |
>> +					     BIT(24) |
>> +					     (0x1F << 16) |
>> +					     BIT(15) |
>> +					     (0x03 << 13) |
>> +					     (0x1F << 8) |
>> +					     (0x07 << 5) |
>> +					     (0x07 << 2));
>> +				dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset,
>> +					     plane->fb.zpos | (plane->fb.display_id << 3),
>> +					     0x07 | BIT(3));
>> +			}
>> +			plane->fb.dirty = false;
>> +		}
>> +
>> +		if (plane->scale.dirty) {
>> +			if (plane->scale.enable) {
>> +				dc_write(hw, reg->scale_factor_x + offset,
>> +					 plane->scale.scale_factor_x);
>> +				dc_write(hw, reg->scale_factor_y + offset,
>> +					 plane->scale.scale_factor_y);
>> +				if (primary)
>> +					dc_set_clear(hw,
>> +						     DC_FRAMEBUFFER_CONFIG + offset,
>> +						     BIT(22), 0);
>> +				else
>> +					dc_set_clear(hw,
>> +						     DC_OVERLAY_SCALE_CONFIG + offset,
>> +						     BIT(8), 0);
>> +			} else {
>> +				if (primary)
>> +					dc_set_clear(hw,
>> +						     DC_FRAMEBUFFER_CONFIG + offset,
>> +						     0, BIT(22));
>> +				else
>> +					dc_set_clear(hw,
>> +						     DC_OVERLAY_SCALE_CONFIG + offset,
>> +						     0, BIT(8));
>> +			}
>> +			plane->scale.dirty = false;
>> +		}
>> +
>> +		if (plane->pos.dirty) {
>> +			dc_write(hw, reg->top_left + offset,
>> +				 plane->pos.start_x |
>> +				 (plane->pos.start_y << 15));
>> +			dc_write(hw, reg->bottom_right + offset,
>> +				 plane->pos.end_x |
>> +				 (plane->pos.end_y << 15));
>> +			plane->pos.dirty = false;
>> +		}
>> +
>> +		if (plane->blend.dirty) {
>> +			dc_write(hw, reg->src_global_color + offset,
>> +				 plane->blend.alpha << 24);
>> +			dc_write(hw, reg->dst_global_color + offset,
>> +				 plane->blend.alpha << 24);
>> +			switch (plane->blend.blend_mode) {
>> +			case BLEND_PREMULTI:
>> +				dc_write(hw, reg->blend_config + offset, 0x3450);
>> +				break;
>> +			case BLEND_COVERAGE:
>> +				dc_write(hw, reg->blend_config + offset, 0x3950);
>> +				break;
>> +			case BLEND_PIXEL_NONE:
>> +				dc_write(hw, reg->blend_config + offset, 0x3548);
>> +				break;
>> +			default:
>> +				break;
>> +			}
>> +			plane->blend.dirty = false;
>> +		}
>> +
>> +		if (plane->colorkey.dirty) {
>> +			dc_write(hw, reg->color_key + offset, plane->colorkey.colorkey);
>> +			dc_write(hw, reg->color_key_high + offset,
>> +				 plane->colorkey.colorkey_high);
>> +
>> +			if (primary)
>> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
>> +					     plane->colorkey.transparency << 9, 0x03 << 9);
>> +			else
>> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> +					     plane->colorkey.transparency, 0x03);
>> +
>> +			plane->colorkey.dirty = false;
>> +		}
>> +
>> +		if (plane->roi.dirty) {
>> +			if (plane->roi.enable) {
>> +				dc_write(hw, reg->roi_origin + offset,
>> +					 plane->roi.x | (plane->roi.y << 16));
>> +				dc_write(hw, reg->roi_size + offset,
>> +					 plane->roi.width | (plane->roi.height << 16));
>> +				if (primary)
>> +					dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +						     BIT(0), 0);
>> +				else
>> +					dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> +						     BIT(22), 0);
>> +			} else {
>> +				if (primary)
>> +					dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +						     0, BIT(0));
>> +				else
>> +					dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> +						     0, BIT(22));
>> +			}
>> +			plane->roi.dirty = false;
>> +		}
>> +	}
>> +}
>> +
>> +static void plane_ex_commit(struct dc_hw *hw)
>> +{
>> +	struct dc_hw_plane *plane;
>> +	const struct dc_hw_plane_reg *reg;
>> +	bool primary = false;
>> +	u8 id, layer_num = hw->info->layer_num;
>> +	u32 i, offset;
>> +
>> +	for (i = 0; i < layer_num; i++) {
>> +		plane = &hw->plane[i];
>> +		id = hw->info->planes[i].id;
>> +		offset = get_addr_offset(id);
>> +		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
>> +			reg = &dc_plane_reg[0];
>> +			primary = true;
>> +		} else {
>> +			reg = &dc_plane_reg[1];
>> +			primary = false;
>> +		}
>> +
>> +		if (plane->fb.dirty) {
>> +			if (is_rgb(plane->fb.format)) {
>> +				if (primary)
>> +					dc_set_clear(hw,
>> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +						     BIT(6), BIT(8));
>> +				else
>> +					dc_set_clear(hw,
>> +						     DC_OVERLAY_CONFIG + offset,
>> +						     BIT(29), BIT(30));
>> +			} else {
>> +				if (primary)
>> +					dc_set_clear(hw,
>> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +						     BIT(8), BIT(6));
>> +				else
>> +					dc_set_clear(hw,
>> +						     DC_OVERLAY_CONFIG + offset,
>> +						     BIT(30), BIT(29));
>> +				switch (plane->fb.yuv_color_space) {
>> +				case COLOR_SPACE_601:
>> +					load_yuv_to_rgb(hw, reg, offset, YUV601_2RGB);
>> +					break;
>> +				case COLOR_SPACE_709:
>> +					load_yuv_to_rgb(hw, reg, offset, YUV709_2RGB);
>> +					break;
>> +				case COLOR_SPACE_2020:
>> +					load_yuv_to_rgb(hw, reg, offset, YUV2020_2RGB);
>> +					break;
>> +				default:
>> +					break;
>> +				}
>> +			}
>> +		}
>> +		if (plane->degamma.dirty) {
>> +			switch (plane->degamma.mode) {
>> +			case VS_DEGAMMA_DISABLE:
>> +				if (primary)
>> +					dc_set_clear(hw,
>> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +						     0, BIT(5));
>> +				else
>> +					dc_set_clear(hw,
>> +						     DC_OVERLAY_CONFIG + offset,
>> +						     0, BIT(28));
>> +				break;
>> +			case VS_DEGAMMA_BT709:
>> +				load_degamma_table(hw, reg, offset, DEGAMMA_709);
>> +				if (primary)
>> +					dc_set_clear(hw,
>> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +						     BIT(5), 0);
>> +				else
>> +					dc_set_clear(hw,
>> +						     DC_OVERLAY_CONFIG + offset,
>> +						     BIT(28), 0);
>> +				break;
>> +			case VS_DEGAMMA_BT2020:
>> +				load_degamma_table(hw, reg, offset, DEGAMMA_2020);
>> +				if (primary)
>> +					dc_set_clear(hw,
>> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +						     BIT(5), 0);
>> +				else
>> +					dc_set_clear(hw,
>> +						     DC_OVERLAY_CONFIG + offset,
>> +						     BIT(28), 0);
>> +				break;
>> +			default:
>> +				break;
>> +			}
>> +			plane->degamma.dirty = false;
>> +		}
>> +	}
>> +	plane_commit(hw);
>> +}
>> +
>> +static void setup_display(struct dc_hw *hw, struct dc_hw_display *display)
>> +{
>> +	u8 id = display->id;
>> +	u32 dpi_cfg, offset = id << 2;
>> +
>> +	if (hw->display[id].enable) {
>> +		switch (display->bus_format) {
>> +		case MEDIA_BUS_FMT_RGB565_1X16:
>> +			dpi_cfg = 0;
>> +			break;
>> +		case MEDIA_BUS_FMT_RGB666_1X18:
>> +			dpi_cfg = 3;
>> +			break;
>> +		case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
>> +			dpi_cfg = 4;
>> +			break;
>> +		case MEDIA_BUS_FMT_RGB888_1X24:
>> +			dpi_cfg = 5;
>> +			break;
>> +		case MEDIA_BUS_FMT_RGB101010_1X30:
>> +			dpi_cfg = 6;
>> +			break;
>> +		default:
>> +			dpi_cfg = 5;
>> +			break;
>> +		}
>> +		dc_write(hw, DC_DISPLAY_DPI_CONFIG + offset, dpi_cfg);
>> +
>> +		if (id == 0)
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
>> +		else
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
>> +
>> +		dc_write(hw, DC_DISPLAY_H + offset, hw->display[id].h_active |
>> +			 (hw->display[id].h_total << 16));
>> +		dc_write(hw, DC_DISPLAY_H_SYNC + offset,
>> +			 hw->display[id].h_sync_start |
>> +			 (hw->display[id].h_sync_end << 15) |
>> +			 (hw->display[id].h_sync_polarity ? 0 : BIT(31)) |
>> +			 BIT(30));
>> +		dc_write(hw, DC_DISPLAY_V + offset, hw->display[id].v_active |
>> +				(hw->display[id].v_total << 16));
>> +		dc_write(hw, DC_DISPLAY_V_SYNC + offset,
>> +			 hw->display[id].v_sync_start |
>> +			 (hw->display[id].v_sync_end << 15) |
>> +			 (hw->display[id].v_sync_polarity ? 0 : BIT(31)) |
>> +			 BIT(30));
>> +
>> +		if (hw->info->pipe_sync)
>> +			dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
>> +				     0, BIT(3) | BIT(4));
>> +
>> +		dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(12), 0);
>> +		if (id == 0)
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(0), BIT(3));
>> +		else
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(1), BIT(3));
>> +	} else {
>> +		dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(12));
>> +		if (id == 0)
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
>> +		else
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
>> +	}
>> +}
>> +
>> +static void setup_display_ex(struct dc_hw *hw, struct dc_hw_display *display)
>> +{
>> +	u8 id = display->id;
>> +	u32 dp_cfg, offset = id << 2;
>> +	bool is_yuv = false;
>> +
>> +	if (hw->display[id].enable && hw->out[id] == OUT_DP) {
>> +		switch (display->bus_format) {
>> +		case MEDIA_BUS_FMT_RGB565_1X16:
>> +			dp_cfg = 0;
>> +			break;
>> +		case MEDIA_BUS_FMT_RGB666_1X18:
>> +			dp_cfg = 1;
>> +			break;
>> +		case MEDIA_BUS_FMT_RGB888_1X24:
>> +			dp_cfg = 2;
>> +			break;
>> +		case MEDIA_BUS_FMT_RGB101010_1X30:
>> +			dp_cfg = 3;
>> +			break;
>> +		case MEDIA_BUS_FMT_UYVY8_1X16:
>> +			dp_cfg = 2 << 4;
>> +			is_yuv = true;
>> +			break;
>> +		case MEDIA_BUS_FMT_YUV8_1X24:
>> +			dp_cfg = 4 << 4;
>> +			is_yuv = true;
>> +			break;
>> +		case MEDIA_BUS_FMT_UYVY10_1X20:
>> +			dp_cfg = 8 << 4;
>> +			is_yuv = true;
>> +			break;
>> +		case MEDIA_BUS_FMT_YUV10_1X30:
>> +			dp_cfg = 10 << 4;
>> +			is_yuv = true;
>> +			break;
>> +		case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
>> +			dp_cfg = 12 << 4;
>> +			is_yuv = true;
>> +			break;
>> +		case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
>> +			dp_cfg = 13 << 4;
>> +			is_yuv = true;
>> +			break;
>> +		default:
>> +			dp_cfg = 2;
>> +			break;
>> +		}
>> +		if (is_yuv)
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(16), 0);
>> +		else
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(16));
>> +		dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | BIT(3));
>> +	}
>> +
>> +	if (hw->out[id] == OUT_DPI)
>> +		dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, BIT(3));
>> +
>> +	setup_display(hw, display);
>> +}
>> +
>> +static const struct dc_hw_funcs hw_func = {
>> +	.gamma = &gamma_ex_commit,
>> +	.plane = &plane_ex_commit,
>> +	.display = setup_display_ex,
>> +};
>> +
>> +void dc_hw_commit(struct dc_hw *hw)
>> +{
>> +	u32 i, offset = 0;
>> +	u8 plane_num = hw->info->plane_num;
>> +	u8 layer_num = hw->info->layer_num;
>> +	u8 cursor_num = plane_num - layer_num;
>> +
>> +	hw->func->gamma(hw);
>> +	hw->func->plane(hw);
>> +
>> +	for (i = 0; i < cursor_num; i++) {
>> +		if (hw->cursor[i].dirty) {
>> +			offset = hw->cursor[i].display_id ? DC_CURSOR_OFFSET : 0;
>> +			if (hw->cursor[i].enable) {
>> +				dc_write(hw, DC_CURSOR_ADDRESS + offset,
>> +					 hw->cursor[i].address);
>> +				dc_write(hw, DC_CURSOR_LOCATION + offset, hw->cursor[i].x |
>> +					 (hw->cursor[i].y << 16));
>> +				dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
>> +					     (hw->cursor[i].hot_x << 16) |
>> +					     (hw->cursor[i].hot_y << 8) |
>> +					     (hw->cursor[i].size << 5) |
>> +					     BIT(3) | BIT(2) | 0x02,
>> +					     (0xFF << 16) |
>> +					     (0xFF << 8) |
>> +					     (0x07 << 5) | 0x1F);
>> +			} else {
>> +				dc_set_clear(hw, DC_CURSOR_CONFIG + offset, BIT(3), 0x03);
>> +			}
>> +			hw->cursor[i].dirty = false;
>> +		}
>> +	}
>> +
>> +	if (hw->qos.dirty) {
>> +		dc_set_clear(hw, DC_QOS_CONFIG, (hw->qos.high_value << 4) |
>> +					 hw->qos.low_value, 0xFF);
>> +		hw->qos.dirty = false;
>> +	}
>> +}
>> +
>> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.h b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
>> new file mode 100644
>> index 000000000..9a1d767ae
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
>> @@ -0,0 +1,492 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#ifndef __VS_DC_HW_H__
>> +#define __VS_DC_HW_H__
>> +
>> +#define AQ_INTR_ACKNOWLEDGE				0x0010
>> +#define AQ_INTR_ENBL					0x0014
> 
> Please take care to align all the defines.
> 
>> +#define DC_HW_REVISION					0x0024
>> +#define DC_HW_CHIP_CID					0x0030
>> +
>> +#define DC_REG_BASE						0x0800
>> +#define DC_REG_RANGE					0x2000
>> +#define DC_SEC_REG_OFFSET				0x100000
>> +
>> +#define DC_FRAMEBUFFER_CONFIG			0x1518
>> +#define DC_FRAMEBUFFER_CONFIG_EX		0x1CC0
>> +#define DC_FRAMEBUFFER_SCALE_CONFIG		0x1520
>> +#define DC_FRAMEBUFFER_TOP_LEFT			0x24D8
>> +#define DC_FRAMEBUFFER_BOTTOM_RIGHT		0x24E0
>> +#define DC_FRAMEBUFFER_ADDRESS			0x1400
>> +#define DC_FRAMEBUFFER_U_ADDRESS		0x1530
>> +#define DC_FRAMEBUFFER_V_ADDRESS		0x1538
>> +#define DC_FRAMEBUFFER_STRIDE			0x1408
>> +#define DC_FRAMEBUFFER_U_STRIDE			0x1800
>> +#define DC_FRAMEBUFFER_V_STRIDE			0x1808
>> +#define DC_FRAMEBUFFER_SIZE				0x1810
>> +#define DC_FRAMEBUFFER_SCALE_FACTOR_X		0x1828
>> +#define DC_FRAMEBUFFER_SCALE_FACTOR_Y		0x1830
>> +#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX	0x1838
>> +#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA	0x1A00
>> +#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX	0x1A08
>> +#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA	0x1A10
>> +#define DC_FRAMEBUFFER_INIT_OFFSET			0x1A20
>> +#define DC_FRAMEBUFFER_COLOR_KEY			0x1508
>> +#define DC_FRAMEBUFFER_COLOR_KEY_HIGH		0x1510
>> +#define DC_FRAMEBUFFER_CLEAR_VALUE			0x1A18
>> +#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX	0x1818
>> +#define DC_FRAMEBUFFER_COLOR_TABLE_DATA		0x1820
>> +#define DC_FRAMEBUFFER_BG_COLOR				0x1528
>> +#define DC_FRAMEBUFFER_ROI_ORIGIN			0x1CB0
>> +#define DC_FRAMEBUFFER_ROI_SIZE				0x1CB8
>> +#define DC_FRAMEBUFFER_WATER_MARK			0x1CE8
>> +#define DC_FRAMEBUFFER_DEGAMMA_INDEX		0x1D88
>> +#define DC_FRAMEBUFFER_DEGAMMA_DATA			0x1D90
>> +#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA		0x1D98
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEF0		0x1DA0
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEF1		0x1DA8
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEF2		0x1DB0
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEF3		0x1DB8
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEF4		0x1E00
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD0		0x1E08
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD1		0x1E10
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD2		0x1E18
>> +#define DC_FRAMEBUFFER_Y_CLAMP_BOUND		0x1E88
>> +#define DC_FRAMEBUFFER_UV_CLAMP_BOUND		0x1E90
>> +#define DC_FRAMEBUFFER_RGBTORGB_COEF0		0x1E20
>> +#define DC_FRAMEBUFFER_RGBTORGB_COEF1		0x1E28
>> +#define DC_FRAMEBUFFER_RGBTORGB_COEF2		0x1E30
>> +#define DC_FRAMEBUFFER_RGBTORGB_COEF3		0x1E38
>> +#define DC_FRAMEBUFFER_RGBTORGB_COEF4		0x1E40
>> +#define DC_FRAMEBUFFER_BLEND_CONFIG			0x2510
>> +#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR		0x2500
>> +#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR		0x2508
>> +
>> +#define DC_OVERLAY_CONFIG					0x1540
>> +#define DC_OVERLAY_CONFIG_EX				0x2540
>> +#define DC_OVERLAY_SCALE_CONFIG				0x1C00
>> +#define DC_OVERLAY_BLEND_CONFIG				0x1580
>> +#define DC_OVERLAY_TOP_LEFT					0x1640
>> +#define DC_OVERLAY_BOTTOM_RIGHT				0x1680
>> +#define DC_OVERLAY_ADDRESS					0x15C0
>> +#define DC_OVERLAY_U_ADDRESS				0x1840
>> +#define DC_OVERLAY_V_ADDRESS				0x1880
>> +#define DC_OVERLAY_STRIDE					0x1600
>> +#define DC_OVERLAY_U_STRIDE					0x18C0
>> +#define DC_OVERLAY_V_STRIDE					0x1900
>> +#define DC_OVERLAY_SIZE						0x17C0
>> +#define DC_OVERLAY_SCALE_FACTOR_X			0x1A40
>> +#define DC_OVERLAY_SCALE_FACTOR_Y			0x1A80
>> +#define DC_OVERLAY_H_FILTER_COEF_INDEX		0x1AC0
>> +#define DC_OVERLAY_H_FILTER_COEF_DATA		0x1B00
>> +#define DC_OVERLAY_V_FILTER_COEF_INDEX		0x1B40
>> +#define DC_OVERLAY_V_FILTER_COEF_DATA		0x1B80
>> +#define DC_OVERLAY_INIT_OFFSET				0x1BC0
>> +#define DC_OVERLAY_COLOR_KEY				0x1740
>> +#define DC_OVERLAY_COLOR_KEY_HIGH			0x1780
>> +#define DC_OVERLAY_CLEAR_VALUE				0x1940
>> +#define DC_OVERLAY_COLOR_TABLE_INDEX		0x1980
>> +#define DC_OVERLAY_COLOR_TABLE_DATA			0x19C0
>> +#define DC_OVERLAY_SRC_GLOBAL_COLOR			0x16C0
>> +#define DC_OVERLAY_DST_GLOBAL_COLOR			0x1700
>> +#define DC_OVERLAY_ROI_ORIGIN				0x1D00
>> +#define DC_OVERLAY_ROI_SIZE					0x1D40
>> +#define DC_OVERLAY_WATER_MARK				0x1DC0
>> +#define DC_OVERLAY_DEGAMMA_INDEX			0x2200
>> +#define DC_OVERLAY_DEGAMMA_DATA				0x2240
>> +#define DC_OVERLAY_DEGAMMA_EX_DATA			0x2280
>> +#define DC_OVERLAY_YUVTORGB_COEF0			0x1EC0
>> +#define DC_OVERLAY_YUVTORGB_COEF1			0x1F00
>> +#define DC_OVERLAY_YUVTORGB_COEF2			0x1F40
>> +#define DC_OVERLAY_YUVTORGB_COEF3			0x1F80
>> +#define DC_OVERLAY_YUVTORGB_COEF4			0x1FC0
>> +#define DC_OVERLAY_YUVTORGB_COEFD0			0x2000
>> +#define DC_OVERLAY_YUVTORGB_COEFD1			0x2040
>> +#define DC_OVERLAY_YUVTORGB_COEFD2			0x2080
>> +#define DC_OVERLAY_Y_CLAMP_BOUND			0x22C0
>> +#define DC_OVERLAY_UV_CLAMP_BOUND			0x2300
>> +#define DC_OVERLAY_RGBTORGB_COEF0			0x20C0
>> +#define DC_OVERLAY_RGBTORGB_COEF1			0x2100
>> +#define DC_OVERLAY_RGBTORGB_COEF2			0x2140
>> +#define DC_OVERLAY_RGBTORGB_COEF3			0x2180
>> +#define DC_OVERLAY_RGBTORGB_COEF4			0x21C0
>> +
>> +#define DC_CURSOR_CONFIG					0x1468
>> +#define DC_CURSOR_ADDRESS					0x146C
>> +#define DC_CURSOR_LOCATION					0x1470
>> +#define DC_CURSOR_BACKGROUND				0x1474
>> +#define DC_CURSOR_FOREGROUND				0x1478
>> +#define DC_CURSOR_CLK_GATING				0x1484
>> +#define DC_CURSOR_CONFIG_EX					0x24E8
>> +#define DC_CURSOR_OFFSET					0x1080
>> +
>> +#define DC_DISPLAY_DITHER_CONFIG		0x1410
>> +#define DC_DISPLAY_PANEL_CONFIG			0x1418
>> +#define DC_DISPLAY_PANEL_CONFIG_EX		0x2518
>> +#define DC_DISPLAY_DITHER_TABLE_LOW		0x1420
>> +#define DC_DISPLAY_DITHER_TABLE_HIGH	0x1428
>> +#define DC_DISPLAY_H					0x1430
>> +#define DC_DISPLAY_H_SYNC				0x1438
>> +#define DC_DISPLAY_V					0x1440
>> +#define DC_DISPLAY_V_SYNC				0x1448
>> +#define DC_DISPLAY_CURRENT_LOCATION		0x1450
>> +#define DC_DISPLAY_GAMMA_INDEX			0x1458
>> +#define DC_DISPLAY_GAMMA_DATA			0x1460
>> +#define DC_DISPLAY_INT					0x147C
>> +#define DC_DISPLAY_INT_ENABLE			0x1480
>> +#define DC_DISPLAY_DBI_CONFIG			0x1488
>> +#define DC_DISPLAY_GENERAL_CONFIG		0x14B0
>> +#define DC_DISPLAY_DPI_CONFIG			0x14B8
>> +#define DC_DISPLAY_PANEL_START			0x1CCC
>> +#define DC_DISPLAY_DEBUG_COUNTER_SELECT		0x14D0
>> +#define DC_DISPLAY_DEBUG_COUNTER_VALUE		0x14D8
>> +#define DC_DISPLAY_DP_CONFIG				0x1CD0
>> +#define DC_DISPLAY_GAMMA_EX_INDEX			0x1CF0
>> +#define DC_DISPLAY_GAMMA_EX_DATA			0x1CF8
>> +#define DC_DISPLAY_GAMMA_EX_ONE_DATA		0x1D80
>> +#define DC_DISPLAY_RGBTOYUV_COEF0			0x1E48
>> +#define DC_DISPLAY_RGBTOYUV_COEF1			0x1E50
>> +#define DC_DISPLAY_RGBTOYUV_COEF2			0x1E58
>> +#define DC_DISPLAY_RGBTOYUV_COEF3			0x1E60
>> +#define DC_DISPLAY_RGBTOYUV_COEF4			0x1E68
>> +#define DC_DISPLAY_RGBTOYUV_COEFD0			0x1E70
>> +#define DC_DISPLAY_RGBTOYUV_COEFD1			0x1E78
>> +#define DC_DISPLAY_RGBTOYUV_COEFD2			0x1E80
>> +
>> +#define DC_CLK_GATTING					0x1A28
>> +#define DC_QOS_CONFIG					0x1A38
>> +
>> +#define DC_TRANSPARENCY_OPAQUE			0x00
>> +#define DC_TRANSPARENCY_KEY				0x02
>> +#define DC_DISPLAY_DITHERTABLE_LOW		0x7B48F3C0
>> +#define DC_DISPLAY_DITHERTABLE_HIGH		0x596AD1E2
>> +
>> +#define GAMMA_SIZE						256
>> +#define GAMMA_EX_SIZE					300
>> +#define DEGAMMA_SIZE					260
>> +
>> +#define RGB_TO_RGB_TABLE_SIZE			9
>> +#define YUV_TO_RGB_TABLE_SIZE			16
>> +#define RGB_TO_YUV_TABLE_SIZE			12
>> +
>> +#define DC_LAYER_NUM	6
>> +#define DC_DISPLAY_NUM	2
>> +#define DC_CURSOR_NUM	2
>> +
>> +#define DC_TILE_MODE4X4	0x15
>> +
>> +enum dc_chip_rev {
>> +	DC_REV_0,/* For HW_REV_5720,HW_REV_5721_311 */
>> +	DC_REV_1,/* For HW_REV_5721_30B */
>> +	DC_REV_2,/* For HW_REV_5721_310 */
>> +};
>> +
>> +enum dc_hw_plane_id {
>> +	PRIMARY_PLANE_0,
>> +	OVERLAY_PLANE_0,
>> +	OVERLAY_PLANE_1,
>> +	PRIMARY_PLANE_1,
>> +	OVERLAY_PLANE_2,
>> +	OVERLAY_PLANE_3,
>> +	CURSOR_PLANE_0,
>> +	CURSOR_PLANE_1,
>> +	PLANE_NUM
>> +};
>> +
>> +enum dc_hw_color_format {
>> +	FORMAT_X4R4G4B4,//0
> 
> Space after the comma will make it more readable. Or just define the 
> values explicitly, it will also be more readable.
> 
>> +	FORMAT_A4R4G4B4,//1
>> +	FORMAT_X1R5G5B5,//2
>> +	FORMAT_A1R5G5B5,//3
>> +	FORMAT_R5G6B5,//4
>> +	FORMAT_X8R8G8B8,//5
>> +	FORMAT_A8R8G8B8,//6
>> +	FORMAT_YUY2,//7
>> +	FORMAT_UYVY,//8
>> +	FORMAT_INDEX8,//9
>> +	FORMAT_MONOCHROME,//10
>> +	FORMAT_YV12 = 0xf,
>> +	FORMAT_A8,//16
>> +	FORMAT_NV12,//17
>> +	FORMAT_NV16,//18
>> +	FORMAT_RG16,//19
>> +	FORMAT_R8,//20
>> +	FORMAT_NV12_10BIT,//21
>> +	FORMAT_A2R10G10B10,//22
>> +	FORMAT_NV16_10BIT,//23
>> +	FORMAT_INDEX1,//24
>> +	FORMAT_INDEX2,//25
>> +	FORMAT_INDEX4,//26
>> +	FORMAT_P010,//27
>> +	FORMAT_YUV444,//28
>> +	FORMAT_YUV444_10BIT,//29
>> +};
>> +
>> +enum dc_hw_yuv_color_space {
>> +	COLOR_SPACE_601 = 0,
>> +	COLOR_SPACE_709 = 1,
>> +	COLOR_SPACE_2020 = 3,
>> +};
>> +
>> +enum dc_hw_rotation {
>> +	ROT_0 = 0,
>> +	ROT_90 = 4,
>> +	ROT_180 = 5,
>> +	ROT_270 = 6,
>> +	FLIP_X = 1,
>> +	FLIP_Y = 2,
>> +	FLIP_XY = 3,
>> +};
>> +
>> +enum dc_hw_swizzle {
>> +	SWIZZLE_ARGB = 0,
>> +	SWIZZLE_RGBA,
>> +	SWIZZLE_ABGR,
>> +	SWIZZLE_BGRA,
>> +};
>> +
>> +enum dc_hw_out {
>> +	OUT_DPI,
>> +	OUT_DP,
>> +};
>> +
>> +enum dc_hw_cursor_size {
>> +	CURSOR_SIZE_32X32 = 0,
>> +	CURSOR_SIZE_64X64,
>> +};
>> +
>> +enum dc_hw_blend_mode {
>> +	/* out.rgb = plane_alpha * fg.rgb +
>> +	 *		(1 - (plane_alpha * fg.alpha)) * bg.rgb
>> +	 */
>> +	BLEND_PREMULTI,
>> +	/* out.rgb = plane_alpha * fg.alpha * fg.rgb +
>> +	 *		(1 - (plane_alpha * fg.alpha)) * bg.rgb
>> +	 */
>> +	BLEND_COVERAGE,
>> +	/* out.rgb = plane_alpha * fg.rgb +
>> +	 *		(1 - plane_alpha) * bg.rgb
>> +	 */
>> +	BLEND_PIXEL_NONE,
> 
> Can you use defines from <drm/drm_blend.h>?
> 
>> +};
>> +
>> +struct dc_hw_plane_reg {
>> +	u32 y_address;
>> +	u32 u_address;
>> +	u32 v_address;
>> +	u32 y_stride;
>> +	u32 u_stride;
>> +	u32 v_stride;
>> +	u32 size;
>> +	u32 top_left;
>> +	u32 bottom_right;
>> +	u32 scale_factor_x;
>> +	u32 scale_factor_y;
>> +	u32 h_filter_coef_index;
>> +	u32 h_filter_coef_data;
>> +	u32 v_filter_coef_index;
>> +	u32 v_filter_coef_data;
>> +	u32 init_offset;
>> +	u32 color_key;
>> +	u32 color_key_high;
>> +	u32 clear_value;
>> +	u32 color_table_index;
>> +	u32 color_table_data;
>> +	u32 scale_config;
>> +	u32 water_mark;
>> +	u32 degamma_index;
>> +	u32 degamma_data;
>> +	u32 degamma_ex_data;
>> +	u32 src_global_color;
>> +	u32 dst_global_color;
>> +	u32 blend_config;
>> +	u32 roi_origin;
>> +	u32 roi_size;
>> +	u32 yuv_to_rgb_coef0;
>> +	u32 yuv_to_rgb_coef1;
>> +	u32 yuv_to_rgb_coef2;
>> +	u32 yuv_to_rgb_coef3;
>> +	u32 yuv_to_rgb_coef4;
>> +	u32 yuv_to_rgb_coefd0;
>> +	u32 yuv_to_rgb_coefd1;
>> +	u32 yuv_to_rgb_coefd2;
>> +	u32 y_clamp_bound;
>> +	u32 uv_clamp_bound;
>> +	u32 rgb_to_rgb_coef0;
>> +	u32 rgb_to_rgb_coef1;
>> +	u32 rgb_to_rgb_coef2;
>> +	u32 rgb_to_rgb_coef3;
>> +	u32 rgb_to_rgb_coef4;
>> +};
>> +
>> +struct dc_hw_fb {
>> +	u32 y_address;
>> +	u32 u_address;
>> +	u32 v_address;
>> +	u32 clear_value;
>> +	u32 water_mark;
>> +	u16 y_stride;
>> +	u16 u_stride;
>> +	u16 v_stride;
>> +	u16 width;
>> +	u16 height;
>> +	u8	format;
>> +	u8	tile_mode;
>> +	u8	rotation;
>> +	u8	yuv_color_space;
>> +	u8	swizzle;
>> +	u8	uv_swizzle;
>> +	u8	zpos;
>> +	u8	display_id;
>> +	bool	clear_enable;
>> +	bool	dec_enable;
>> +	bool	enable;
>> +	bool	dirty;
>> +};
>> +
>> +struct dc_hw_scale {
>> +	u32 scale_factor_x;
>> +	u32 scale_factor_y;
>> +	bool	enable;
>> +	bool	dirty;
>> +};
>> +
>> +struct dc_hw_position {
>> +	u16 start_x;
>> +	u16 start_y;
>> +	u16 end_x;
>> +	u16 end_y;
>> +	bool	dirty;
>> +};
>> +
>> +struct dc_hw_blend {
>> +	u8	alpha;
>> +	u8	blend_mode;
>> +	bool	dirty;
>> +};
>> +
>> +struct dc_hw_colorkey {
>> +	u32 colorkey;
>> +	u32 colorkey_high;
>> +	u8	transparency;
>> +	bool dirty;
>> +};
>> +
>> +struct dc_hw_roi {
>> +	u16 x;
>> +	u16 y;
>> +	u16 width;
>> +	u16 height;
>> +	bool enable;
>> +	bool dirty;
>> +};
>> +
>> +struct dc_hw_cursor {
>> +	u32 address;
>> +	u16 x;
>> +	u16 y;
>> +	u16 hot_x;
>> +	u16 hot_y;
>> +	u8	size;
>> +	u8	display_id;
>> +	bool	enable;
>> +	bool	dirty;
>> +};
>> +
>> +struct dc_hw_display {
>> +	u32 bus_format;
>> +	u16 h_active;
>> +	u16 h_total;
>> +	u16 h_sync_start;
>> +	u16 h_sync_end;
>> +	u16 v_active;
>> +	u16 v_total;
>> +	u16 v_sync_start;
>> +	u16 v_sync_end;
>> +	u8	id;
>> +	bool	h_sync_polarity;
>> +	bool	v_sync_polarity;
>> +	bool	enable;
>> +};
>> +
>> +struct dc_hw_gamma {
>> +	u16 gamma[GAMMA_EX_SIZE][3];
>> +	bool	enable;
>> +	bool	dirty;
>> +};
>> +
>> +struct dc_hw_degamma {
>> +	u16 degamma[DEGAMMA_SIZE][3];
>> +	u32 mode;
>> +	bool	dirty;
>> +};
>> +
>> +struct dc_hw_plane {
>> +	struct dc_hw_fb			fb;
>> +	struct dc_hw_position	pos;
>> +	struct dc_hw_scale		scale;
>> +	struct dc_hw_blend		blend;
>> +	struct dc_hw_roi		roi;
>> +	struct dc_hw_colorkey	colorkey;
>> +	struct dc_hw_degamma	degamma;
>> +};
>> +
>> +struct dc_hw_qos {
>> +	u8	  low_value;
>> +	u8	  high_value;
>> +	bool  dirty;
>> +};
>> +
>> +struct dc_hw_read {
>> +	u32			reg;
>> +	u32			value;
>> +};
>> +
>> +struct dc_hw;
>> +struct dc_hw_funcs {
>> +	void (*gamma)(struct dc_hw *hw);
>> +	void (*plane)(struct dc_hw *hw);
>> +	void (*display)(struct dc_hw *hw, struct dc_hw_display *display);
>> +};
>> +
>> +struct dc_hw {
>> +	enum dc_chip_rev rev;
>> +	enum dc_hw_out		out[DC_DISPLAY_NUM];
>> +	void			*hi_base;
>> +	void			*reg_base;
>> +
>> +	struct dc_hw_display display[DC_DISPLAY_NUM];
>> +	struct dc_hw_gamma	 gamma[DC_DISPLAY_NUM];
>> +	struct dc_hw_plane	 plane[DC_LAYER_NUM];
>> +	struct dc_hw_cursor  cursor[DC_CURSOR_NUM];
>> +	struct dc_hw_qos	 qos;
>> +	struct dc_hw_funcs	 *func;
>> +	struct vs_dc_info	 *info;
>> +};
>> +
>> +int dc_hw_init(struct dc_hw *hw);
>> +void dc_hw_deinit(struct dc_hw *hw);
>> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
>> +			struct dc_hw_fb *fb, struct dc_hw_scale *scale,
>> +			struct dc_hw_position *pos, struct dc_hw_blend *blend);
>> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode);
>> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi);
>> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
>> +			   struct dc_hw_colorkey *colorkey);
>> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos);
>> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor);
>> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
>> +			u16 r, u16 g, u16 b);
>> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable);
>> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display);
>> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable);
>> +u32 dc_hw_get_interrupt(struct dc_hw *hw);
>> +bool dc_hw_check_underflow(struct dc_hw *hw);
>> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable);
>> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id);
>> +void dc_hw_commit(struct dc_hw *hw);
>> +
>> +#endif /* __VS_DC_HW_H__ */
>> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
>> index da7698c3d..3cd533cfa 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
>> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
>> @@ -21,6 +21,7 @@
>>   
>>   #include "vs_drv.h"
>>   #include "vs_modeset.h"
>> +#include "vs_dc.h"
>>   
>>   #define DRV_NAME	"verisilicon"
>>   #define DRV_DESC	"Verisilicon DRM driver"
>> @@ -123,6 +124,7 @@ static const struct component_master_ops vs_drm_ops = {
>>   };
>>   
>>   static struct platform_driver *drm_sub_drivers[] = {
>> +	&dc_platform_driver,
>>   
>>   };
>>   
>> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c
>> new file mode 100644
>> index 000000000..9bd066015
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_plane.c
>> @@ -0,0 +1,526 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#include <drm/drm_atomic.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_blend.h>
>> +#include <drm/drm_gem_dma_helper.h>
>> +#include <drm/drm_fb_dma_helper.h>
>> +#include <drm/drm_framebuffer.h>
>> +#include <drm/drm_plane.h>
>> +#include <drm/drm_plane_helper.h>
>> +
>> +#include <drm/vs_drm.h>
>> +
>> +#include "vs_plane.h"
>> +#include "vs_drv.h"
>> +#include "vs_dc.h"
>> +
>> +static void vs_plane_reset(struct drm_plane *plane)
>> +{
>> +	struct vs_plane_state *state;
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +
>> +	if (plane->state) {
>> +		__drm_atomic_helper_plane_destroy_state(plane->state);
>> +
>> +		state = to_vs_plane_state(plane->state);
>> +		kfree(state);
>> +		plane->state = NULL;
>> +	}
>> +
> 
> Use vs_plane_destroy_state directly. This way you won't miss a refcount 
> on blobs.
> 
>> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> +	if (!state)
>> +		return;
>> +
>> +	state->degamma = VS_DEGAMMA_DISABLE;
>> +	state->degamma_changed = false;
>> +	state->base.zpos = vs_plane->id;
>> +	memset(&state->status, 0, sizeof(state->status));
>> +
>> +	__drm_atomic_helper_plane_reset(plane, &state->base);
>> +}
>> +
>> +static void _vs_plane_duplicate_blob(struct vs_plane_state *state,
>> +				     struct vs_plane_state *ori_state)
>> +{
>> +	state->watermark = ori_state->watermark;
>> +	state->color_mgmt = ori_state->color_mgmt;
>> +	state->roi = ori_state->roi;
>> +
>> +	if (state->watermark)
>> +		drm_property_blob_get(state->watermark);
>> +	if (state->color_mgmt)
>> +		drm_property_blob_get(state->color_mgmt);
>> +	if (state->roi)
>> +		drm_property_blob_get(state->roi);
>> +}
>> +
>> +static int
>> +_vs_plane_set_property_blob_from_id(struct drm_device *dev,
>> +				    struct drm_property_blob **blob,
>> +				    u64 blob_id,
>> +				    size_t expected_size)
>> +{
>> +	struct drm_property_blob *new_blob = NULL;
>> +
>> +	if (blob_id) {
>> +		new_blob = drm_property_lookup_blob(dev, blob_id);
>> +		if (!new_blob)
>> +			return -EINVAL;
>> +
>> +		if (new_blob->length != expected_size) {
>> +			drm_property_blob_put(new_blob);
>> +			return -EINVAL;
>> +		}
>> +	}
>> +
>> +	drm_property_replace_blob(blob, new_blob);
>> +	drm_property_blob_put(new_blob);
>> +
>> +	return 0;
>> +}
>> +
>> +static struct drm_plane_state *
>> +vs_plane_atomic_duplicate_state(struct drm_plane *plane)
>> +{
>> +	struct vs_plane_state *ori_state;
>> +	struct vs_plane_state *state;
>> +
>> +	if (WARN_ON(!plane->state))
>> +		return NULL;
>> +
>> +	ori_state = to_vs_plane_state(plane->state);
>> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> +	if (!state)
>> +		return NULL;
>> +
>> +	__drm_atomic_helper_plane_duplicate_state(plane, &state->base);
>> +
>> +	state->degamma = ori_state->degamma;
>> +	state->degamma_changed = ori_state->degamma_changed;
>> +
>> +	_vs_plane_duplicate_blob(state, ori_state);
>> +	memcpy(&state->status, &ori_state->status, sizeof(ori_state->status));
>> +
>> +	return &state->base;
>> +}
>> +
>> +static void vs_plane_atomic_destroy_state(struct drm_plane *plane,
>> +					  struct drm_plane_state *state)
>> +{
>> +	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
>> +
>> +	__drm_atomic_helper_plane_destroy_state(state);
>> +
>> +	drm_property_blob_put(vs_plane_state->watermark);
>> +	drm_property_blob_put(vs_plane_state->color_mgmt);
>> +	drm_property_blob_put(vs_plane_state->roi);
>> +	kfree(vs_plane_state);
>> +}
>> +
>> +static int vs_plane_atomic_set_property(struct drm_plane *plane,
>> +					struct drm_plane_state *state,
>> +					struct drm_property *property,
>> +					uint64_t val)
>> +{
>> +	struct drm_device *dev = plane->dev;
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
>> +	int ret = 0;
>> +
>> +	if (property == vs_plane->degamma_mode) {
>> +		if (vs_plane_state->degamma != val) {
>> +			vs_plane_state->degamma = val;
>> +			vs_plane_state->degamma_changed = true;
>> +		} else {
>> +			vs_plane_state->degamma_changed = false;
>> +		}
>> +	} else if (property == vs_plane->watermark_prop) {
>> +		ret = _vs_plane_set_property_blob_from_id(dev,
>> +							  &vs_plane_state->watermark,
>> +							  val,
>> +							  sizeof(struct drm_vs_watermark));
>> +		return ret;
>> +	} else if (property == vs_plane->color_mgmt_prop) {
>> +		ret = _vs_plane_set_property_blob_from_id(dev,
>> +							  &vs_plane_state->color_mgmt,
>> +							  val,
>> +							  sizeof(struct drm_vs_color_mgmt));
>> +		return ret;
>> +	} else if (property == vs_plane->roi_prop) {
>> +		ret = _vs_plane_set_property_blob_from_id(dev,
>> +							  &vs_plane_state->roi,
>> +							  val,
>> +							  sizeof(struct drm_vs_roi));
>> +		return ret;
>> +	} else {
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int vs_plane_atomic_get_property(struct drm_plane *plane,
>> +					const struct drm_plane_state *state,
>> +					struct drm_property *property,
>> +					uint64_t *val)
>> +{
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +	const struct vs_plane_state *vs_plane_state =
>> +		container_of(state, const struct vs_plane_state, base);
>> +
>> +	if (property == vs_plane->degamma_mode)
>> +		*val = vs_plane_state->degamma;
>> +	else if (property == vs_plane->watermark_prop)
>> +		*val = (vs_plane_state->watermark) ?
>> +					vs_plane_state->watermark->base.id : 0;
>> +	else if (property == vs_plane->color_mgmt_prop)
>> +		*val = (vs_plane_state->color_mgmt) ?
>> +					vs_plane_state->color_mgmt->base.id : 0;
> 
> degamma and color management should use standard properties.
> 
>> +	else if (property == vs_plane->roi_prop)
>> +		*val = (vs_plane_state->roi) ?
>> +					vs_plane_state->roi->base.id : 0;
>> +	else
>> +		return -EINVAL;
>> +
>> +	return 0;
>> +}
>> +
>> +static bool vs_format_mod_supported(struct drm_plane *plane,
>> +				    u32 format,
>> +				    u64 modifier)
>> +{
>> +	int i;
>> +
>> +	/* We always have to allow these modifiers:
>> +	 * 1. Core DRM checks for LINEAR support if userspace does not provide modifiers.
>> +	 * 2. Not passing any modifiers is the same as explicitly passing INVALID.
>> +	 */
>> +	if (modifier == DRM_FORMAT_MOD_LINEAR)
>> +		return true;
>> +
>> +	/* Check that the modifier is on the list of the plane's supported modifiers. */
>> +	for (i = 0; i < plane->modifier_count; i++) {
>> +		if (modifier == plane->modifiers[i])
>> +			break;
>> +	}
>> +
>> +	if (i == plane->modifier_count)
>> +		return false;
>> +
>> +	return true;
>> +}
>> +
>> +const struct drm_plane_funcs vs_plane_funcs = {
>> +	.update_plane		= drm_atomic_helper_update_plane,
>> +	.disable_plane		= drm_atomic_helper_disable_plane,
>> +	.reset			= vs_plane_reset,
>> +	.atomic_duplicate_state = vs_plane_atomic_duplicate_state,
>> +	.atomic_destroy_state	= vs_plane_atomic_destroy_state,
>> +	.atomic_set_property	= vs_plane_atomic_set_property,
>> +	.atomic_get_property	= vs_plane_atomic_get_property,
>> +	.format_mod_supported	= vs_format_mod_supported,
>> +};
>> +
>> +static unsigned char vs_get_plane_number(struct drm_framebuffer *fb)
>> +{
>> +	const struct drm_format_info *info;
>> +
>> +	if (!fb)
>> +		return 0;
>> +
>> +	info = drm_format_info(fb->format->format);
>> +	if (!info || info->num_planes > DRM_FORMAT_MAX_PLANES)
>> +		return 0;
>> +
>> +	return info->num_planes;
>> +}
>> +
>> +static int vs_plane_atomic_check(struct drm_plane *plane,
>> +				 struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> +										 plane);
>> +	unsigned char i, num_planes;
>> +	struct drm_framebuffer *fb = new_plane_state->fb;
>> +	struct drm_crtc *crtc = new_plane_state->crtc;
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
>> +
>> +	if (!crtc || !fb)
>> +		return 0;
>> +
>> +	num_planes = vs_get_plane_number(fb);
>> +
>> +	for (i = 0; i < num_planes; i++) {
>> +		dma_addr_t dma_addr;
>> +
>> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
>> +		plane_state->dma_addr[i] = dma_addr;
>> +	}
>> +
>> +	return vs_dc_check_plane(dc, plane, state);
>> +}
>> +
>> +static int vs_cursor_plane_atomic_check(struct drm_plane *plane,
>> +					struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> +									  plane);
>> +	unsigned char i, num_planes;
>> +	struct drm_framebuffer *fb = new_plane_state->fb;
>> +	struct drm_crtc *crtc = new_plane_state->crtc;
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
>> +
>> +	if (!crtc || !fb)
>> +		return 0;
>> +
>> +	num_planes = vs_get_plane_number(fb);
>> +
>> +	for (i = 0; i < num_planes; i++) {
>> +		dma_addr_t dma_addr;
>> +
>> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
>> +		plane_state->dma_addr[i] = dma_addr;
>> +	}
>> +
>> +	return vs_dc_check_cursor_plane(dc, plane, state);
>> +}
>> +
>> +static void vs_plane_atomic_update(struct drm_plane *plane,
>> +				   struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
>> +									  plane);
> 
> New line after the equal sign will be better.
> 
>> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> +									  plane);
>> +
>> +	unsigned char i, num_planes;
>> +	struct drm_framebuffer *fb;
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	if (!new_state->fb || !new_state->crtc)
>> +		return;
> 
> if (!new_state->visible) ?
> 
>> +
>> +	fb = new_state->fb;
>> +
>> +	drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
>> +
>> +	num_planes = vs_get_plane_number(fb);
>> +
>> +	for (i = 0; i < num_planes; i++) {
>> +		dma_addr_t dma_addr;
>> +
>> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
>> +		plane_state->dma_addr[i] = dma_addr;
>> +	}
>> +
>> +	plane_state->status.src = drm_plane_state_src(new_state);
>> +	plane_state->status.dest = drm_plane_state_dest(new_state);
>> +
>> +	vs_dc_update_plane(dc, vs_plane, plane, state);
>> +}
>> +
>> +static void vs_cursor_plane_atomic_update(struct drm_plane *plane,
>> +					  struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
>> +									   plane);
>> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> +									   plane);
>> +	unsigned char i, num_planes;
>> +	struct drm_framebuffer *fb;
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	if (!new_state->fb || !new_state->crtc)
> 
> and here.
> 
>> +		return;
>> +
>> +	fb = new_state->fb;
>> +	drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
>> +
>> +	num_planes = vs_get_plane_number(fb);
>> +
>> +	for (i = 0; i < num_planes; i++) {
>> +		dma_addr_t dma_addr;
>> +
>> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
>> +		plane_state->dma_addr[i] = dma_addr;
>> +	}
>> +
>> +	plane_state->status.src = drm_plane_state_src(new_state);
>> +	plane_state->status.dest = drm_plane_state_dest(new_state);
>> +
>> +	vs_dc_update_cursor_plane(dc, vs_plane, plane, state);
>> +}
>> +
>> +static void vs_plane_atomic_disable(struct drm_plane *plane,
>> +				    struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> +									   plane);
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	vs_dc_disable_plane(dc, vs_plane, old_state);
>> +}
>> +
>> +static void vs_cursor_plane_atomic_disable(struct drm_plane *plane,
>> +					   struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> +									   plane);
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	vs_dc_disable_cursor_plane(dc, vs_plane, old_state);
>> +}
>> +
>> +const struct drm_plane_helper_funcs primary_plane_helpers = {
>> +	.atomic_check	= vs_plane_atomic_check,
>> +	.atomic_update	= vs_plane_atomic_update,
>> +	.atomic_disable = vs_plane_atomic_disable,
>> +};
>> +
>> +const struct drm_plane_helper_funcs overlay_plane_helpers = {
>> +	.atomic_check	= vs_plane_atomic_check,
>> +	.atomic_update	= vs_plane_atomic_update,
>> +	.atomic_disable = vs_plane_atomic_disable,
>> +};
>> +
>> +const struct drm_plane_helper_funcs cursor_plane_helpers = {
>> +	.atomic_check	= vs_cursor_plane_atomic_check,
>> +	.atomic_update	= vs_cursor_plane_atomic_update,
>> +	.atomic_disable = vs_cursor_plane_atomic_disable,
>> +};
>> +
>> +static const struct drm_prop_enum_list vs_degamma_mode_enum_list[] = {
>> +	{ VS_DEGAMMA_DISABLE,	"disabled" },
>> +	{ VS_DEGAMMA_BT709,	"preset degamma for BT709" },
>> +	{ VS_DEGAMMA_BT2020,	"preset degamma for BT2020" },
>> +};
>> +
>> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
>> +				 struct vs_plane_info *info,
>> +				 unsigned int layer_num,
>> +				 unsigned int possible_crtcs)
>> +{
>> +	struct vs_plane *plane;
>> +	int ret;
>> +
>> +	if (!info)
>> +		return NULL;
>> +
>> +	plane = drmm_universal_plane_alloc(drm_dev, struct vs_plane, base,
>> +					   possible_crtcs,
>> +					   &vs_plane_funcs,
>> +					   info->formats, info->num_formats,
>> +					   info->modifiers, info->type,
>> +					   info->name ? info->name : NULL);
>> +	if (IS_ERR(plane))
>> +		return ERR_CAST(plane);
>> +
>> +	if (info->type == DRM_PLANE_TYPE_PRIMARY)
>> +		drm_plane_helper_add(&plane->base, &primary_plane_helpers);
>> +	else if (info->type == DRM_PLANE_TYPE_CURSOR)
>> +		drm_plane_helper_add(&plane->base, &cursor_plane_helpers);
>> +	else
>> +		drm_plane_helper_add(&plane->base, &overlay_plane_helpers);
>> +
>> +	/* Set up the plane properties */
>> +	if (info->degamma_size) {
>> +		plane->degamma_mode =
>> +		drm_property_create_enum(drm_dev, 0,
>> +					 "DEGAMMA_MODE",
>> +					 vs_degamma_mode_enum_list,
>> +					 ARRAY_SIZE(vs_degamma_mode_enum_list));
>> +
>> +		if (!plane->degamma_mode)
>> +			return NULL;
>> +
>> +		drm_object_attach_property(&plane->base.base,
>> +					   plane->degamma_mode,
>> +					   VS_DEGAMMA_DISABLE);
>> +	}
>> +
>> +	if (info->rotation) {
>> +		ret = drm_plane_create_rotation_property(&plane->base,
>> +							 DRM_MODE_ROTATE_0,
>> +							 info->rotation);
>> +		if (ret)
>> +			return NULL;
>> +	}
>> +
>> +	if (info->blend_mode) {
>> +		ret = drm_plane_create_blend_mode_property(&plane->base,
>> +							   info->blend_mode);
>> +		if (ret)
>> +			return NULL;
>> +		ret = drm_plane_create_alpha_property(&plane->base);
>> +		if (ret)
>> +			return NULL;
>> +	}
>> +
>> +	if (info->color_encoding) {
>> +		ret = drm_plane_create_color_properties(&plane->base,
>> +							info->color_encoding,
>> +							BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
>> +							DRM_COLOR_YCBCR_BT709,
>> +							DRM_COLOR_YCBCR_LIMITED_RANGE);
>> +		if (ret)
>> +			return NULL;
>> +	}
>> +
>> +	if (info->zpos != 255) {
>> +		ret = drm_plane_create_zpos_property(&plane->base, info->zpos, 0,
>> +						     layer_num - 1);
>> +		if (ret)
>> +			return NULL;
>> +	} else {
>> +		ret = drm_plane_create_zpos_immutable_property(&plane->base,
>> +							       info->zpos);
>> +		if (ret)
>> +			return NULL;
>> +	}
>> +
>> +	if (info->watermark) {
>> +		plane->watermark_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
>> +							    "WATERMARK", 0);
>> +		if (!plane->watermark_prop)
>> +			return NULL;
>> +		drm_object_attach_property(&plane->base.base, plane->watermark_prop, 0);
>> +	}
>> +
>> +	if (info->color_mgmt) {
>> +		plane->color_mgmt_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
>> +							     "COLOR_CONFIG", 0);
>> +		if (!plane->color_mgmt_prop)
>> +			return NULL;
>> +
>> +		drm_object_attach_property(&plane->base.base, plane->color_mgmt_prop, 0);
>> +	}
>> +
>> +	if (info->roi) {
>> +		plane->roi_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
>> +						      "ROI", 0);
>> +		if (!plane->roi_prop)
>> +			return NULL;
>> +
>> +		drm_object_attach_property(&plane->base.base, plane->roi_prop, 0);
>> +	}
>> +
>> +	return plane;
>> +}
>> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h
>> new file mode 100644
>> index 000000000..554b74e96
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_plane.h
>> @@ -0,0 +1,58 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#ifndef __VS_PLANE_H__
>> +#define __VS_PLANE_H__
>> +
>> +#include <drm/drm_fourcc.h>
>> +#include <drm/drm_plane_helper.h>
>> +
>> +#include "vs_type.h"
>> +
>> +struct vs_plane_status {
>> +	u32 tile_mode;
>> +	struct drm_rect src;
>> +	struct drm_rect dest;
>> +};
>> +
>> +struct vs_plane_state {
>> +	struct drm_plane_state base;
>> +	struct vs_plane_status status; /* for debugfs */
>> +
>> +	struct drm_property_blob *watermark;
>> +	struct drm_property_blob *color_mgmt;
>> +	struct drm_property_blob *roi;
>> +	dma_addr_t dma_addr[DRM_FORMAT_MAX_PLANES];
>> +
>> +	u32 degamma;
>> +	bool degamma_changed;
>> +};
>> +
>> +struct vs_plane {
>> +	struct drm_plane base;
>> +	u8 id;
>> +
>> +	struct drm_property *degamma_mode;
>> +	struct drm_property *watermark_prop;
>> +	struct drm_property *color_mgmt_prop;
>> +	struct drm_property *roi_prop;
>> +};
>> +
>> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
>> +				 struct vs_plane_info *info,
>> +				 unsigned int layer_num,
>> +				 unsigned int possible_crtcs);
>> +
>> +static inline struct vs_plane *to_vs_plane(struct drm_plane *plane)
>> +{
>> +	return container_of(plane, struct vs_plane, base);
>> +}
>> +
>> +static inline struct vs_plane_state *
>> +to_vs_plane_state(struct drm_plane_state *state)
>> +{
>> +	return container_of(state, struct vs_plane_state, base);
>> +}
>> +#endif /* __VS_PLANE_H__ */
>> diff --git a/drivers/gpu/drm/verisilicon/vs_type.h b/drivers/gpu/drm/verisilicon/vs_type.h
>> new file mode 100644
>> index 000000000..7d3378e29
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_type.h
>> @@ -0,0 +1,69 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#ifndef __VS_TYPE_H__
>> +#define __VS_TYPE_H__
>> +
>> +#include <drm/drm_plane.h>
>> +#include <drm/drm_plane_helper.h>
>> +
>> +struct vs_plane_info {
>> +	const char *name;
>> +	u8 id;
>> +	enum drm_plane_type type;
>> +	unsigned int num_formats;
>> +	const u32 *formats;
>> +	u8 num_modifiers;
>> +	const u64 *modifiers;
>> +	unsigned int min_width;
>> +	unsigned int min_height;
>> +	unsigned int max_width;
>> +	unsigned int max_height;
>> +	unsigned int rotation;
>> +	unsigned int blend_mode;
>> +	unsigned int color_encoding;
>> +
>> +	/* 0 means no de-gamma LUT */
>> +	unsigned int degamma_size;
>> +
>> +	int min_scale; /* 16.16 fixed point */
>> +	int max_scale; /* 16.16 fixed point */
>> +
>> +	/* default zorder value,
>> +	 * and 255 means unsupported zorder capability
>> +	 */
>> +	u8	 zpos;
>> +
>> +	bool watermark;
>> +	bool color_mgmt;
>> +	bool roi;
>> +};
>> +
>> +struct vs_dc_info {
>> +	const char *name;
>> +
>> +	u8 panel_num;
>> +
>> +	/* planes */
>> +	u8 plane_num;
>> +	const struct vs_plane_info *planes;
>> +
>> +	u8 layer_num;
>> +	unsigned int max_bpc;
>> +	unsigned int color_formats;
>> +
>> +	/* 0 means no gamma LUT */
>> +	u16 gamma_size;
>> +	u8 gamma_bits;
>> +
>> +	u16 pitch_alignment;
>> +
>> +	bool pipe_sync;
>> +	bool background;
>> +	bool panel_sync;
>> +	bool cap_dec;
>> +};
>> +
>> +#endif /* __VS_TYPE_H__ */
> 

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-11-14 10:42       ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-11-14 10:42 UTC (permalink / raw)
  To: Dmitry Baryshkov, dri-devel, devicetree, linux-kernel,
	linux-riscv, linux-media, linaro-mm-sig
  Cc: Conor Dooley, Albert Ou, Emil Renner Berthing, christian.koenig,
	Thomas Zimmermann, Bjorn Andersson, Chris Morgan, Maxime Ripard,
	Jagan Teki, Jack Zhu, Rob Herring, Palmer Dabbelt,
	Krzysztof Kozlowski, Paul Walmsley, Shengyang Chen,
	Changhuang Liang, Shawn Guo, Sumit Semwal



On 2023/10/26 3:28, Dmitry Baryshkov wrote:
> On 25/10/2023 13:39, Keith Zhao wrote:
>> add 2 crtcs and 8 planes in vs-drm
>> 
>> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>> ---
>>   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
>>   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
>>   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
>>   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
>>   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
>>   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
>>   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
>>   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
>>   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
>>   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
>>   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
>>   11 files changed, 4494 insertions(+), 2 deletions(-)
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
>> 
>> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
>> index 7d3be305b..1d48016ca 100644
>> --- a/drivers/gpu/drm/verisilicon/Makefile
>> +++ b/drivers/gpu/drm/verisilicon/Makefile
>> @@ -1,7 +1,11 @@
>>   # SPDX-License-Identifier: GPL-2.0
>>   
>> -vs_drm-objs := vs_drv.o \
>> -		vs_modeset.o
>> +vs_drm-objs := vs_dc_hw.o \
>> +		vs_dc.o \
>> +		vs_crtc.o \
>> +		vs_drv.o \
>> +		vs_modeset.o \
>> +		vs_plane.o
>>   
>>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>>   
>> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> new file mode 100644
>> index 000000000..8a658ea77
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> @@ -0,0 +1,257 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + *
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/debugfs.h>
>> +#include <linux/media-bus-format.h>
>> +
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_atomic.h>
>> +#include <drm/drm_crtc.h>
>> +#include <drm/drm_gem_atomic_helper.h>
>> +#include <drm/drm_vblank.h>
>> +#include <drm/vs_drm.h>
>> +
>> +#include "vs_crtc.h"
>> +#include "vs_dc.h"
>> +#include "vs_drv.h"
>> +
>> +static void vs_crtc_reset(struct drm_crtc *crtc)
>> +{
>> +	struct vs_crtc_state *state;
>> +
>> +	if (crtc->state) {
>> +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
>> +
>> +		state = to_vs_crtc_state(crtc->state);
>> +		kfree(state);
>> +		crtc->state = NULL;
>> +	}
> 
> You can call your crtc_destroy_state function directly here.
> 
>> +
>> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> +	if (!state)
>> +		return;
>> +
>> +	__drm_atomic_helper_crtc_reset(crtc, &state->base);
>> +}
>> +
>> +static struct drm_crtc_state *
>> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
>> +{
>> +	struct vs_crtc_state *ori_state;
> 
> It might be a matter of taste, but it is usually old_state.
> 
>> +	struct vs_crtc_state *state;
>> +
>> +	if (!crtc->state)
>> +		return NULL;
>> +
>> +	ori_state = to_vs_crtc_state(crtc->state);
>> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> +	if (!state)
>> +		return NULL;
>> +
>> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
>> +
>> +	state->output_fmt = ori_state->output_fmt;
>> +	state->encoder_type = ori_state->encoder_type;
>> +	state->bpp = ori_state->bpp;
>> +	state->underflow = ori_state->underflow;
> 
> Can you use kmemdup instead?
> 
>> +
>> +	return &state->base;
>> +}
>> +
>> +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
>> +					 struct drm_crtc_state *state)
>> +{
>> +	__drm_atomic_helper_crtc_destroy_state(state);
>> +	kfree(to_vs_crtc_state(state));
>> +}
>> +
>> +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
>> +{
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	vs_dc_enable_vblank(dc, true);
>> +
>> +	return 0;
>> +}
>> +
>> +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
>> +{
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	vs_dc_enable_vblank(dc, false);
>> +}
>> +
>> +static const struct drm_crtc_funcs vs_crtc_funcs = {
>> +	.set_config		= drm_atomic_helper_set_config,
>> +	.page_flip		= drm_atomic_helper_page_flip,
> 
> destroy is required, see drm_mode_config_cleanup()

hi Dmitry:
if define destroy in drm_crtc_funcs,
it will make __drmm_crtc_init_with_planes unhappy

see:
__printf(6, 0)
static int __drmm_crtc_init_with_planes(struct drm_device *dev,
					struct drm_crtc *crtc,
					struct drm_plane *primary,
					struct drm_plane *cursor,
					const struct drm_crtc_funcs *funcs,
					const char *name,
					va_list args)
{
	int ret;

	drm_WARN_ON(dev, funcs && funcs->destroy);

........
}

It should not need to be defined here, I think

> 
>> +	.reset			= vs_crtc_reset,
>> +	.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
>> +	.atomic_destroy_state	= vs_crtc_atomic_destroy_state,
> 
> please consider adding atomic_print_state to output driver-specific bits.
> 
>> +	.enable_vblank		= vs_crtc_enable_vblank,
>> +	.disable_vblank		= vs_crtc_disable_vblank,
>> +};
>> +
>> +static u8 cal_pixel_bits(u32 bus_format)
> 
> This looks like a generic helper code, which can go to a common place.
> 
>> +{
>> +	u8 bpp;
>> +
>> +	switch (bus_format) {
>> +	case MEDIA_BUS_FMT_RGB565_1X16:
>> +	case MEDIA_BUS_FMT_UYVY8_1X16:
>> +		bpp = 16;
>> +		break;
>> +	case MEDIA_BUS_FMT_RGB666_1X18:
>> +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
>> +		bpp = 18;
>> +		break;
>> +	case MEDIA_BUS_FMT_UYVY10_1X20:
>> +		bpp = 20;
>> +		break;
>> +	case MEDIA_BUS_FMT_BGR888_1X24:
>> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
>> +	case MEDIA_BUS_FMT_YUV8_1X24:
>> +		bpp = 24;
>> +		break;
>> +	case MEDIA_BUS_FMT_RGB101010_1X30:
>> +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
>> +	case MEDIA_BUS_FMT_YUV10_1X30:
>> +		bpp = 30;
>> +		break;
>> +	default:
>> +		bpp = 24;
>> +		break;
>> +	}
>> +
>> +	return bpp;
>> +}
>> +
>> +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
>> +				  struct drm_atomic_state *state)
>> +{
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
>> +
>> +	vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
>> +
>> +	vs_dc_enable(dc, crtc);
>> +	drm_crtc_vblank_on(crtc);
>> +}
>> +
>> +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
>> +				   struct drm_atomic_state *state)
>> +{
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	drm_crtc_vblank_off(crtc);
>> +
>> +	vs_dc_disable(dc, crtc);
>> +
>> +	if (crtc->state->event && !crtc->state->active) {
>> +		spin_lock_irq(&crtc->dev->event_lock);
>> +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
>> +		spin_unlock_irq(&crtc->dev->event_lock);
>> +
>> +		crtc->state->event = NULL;
> 
> I think even should be cleared within the lock.
> 
>> +	}
>> +}
>> +
>> +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
>> +				 struct drm_atomic_state *state)
>> +{
>> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
>> +									  crtc);
>> +
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct device *dev = vs_crtc->dev;
>> +	struct drm_property_blob *blob = crtc->state->gamma_lut;
>> +	struct drm_color_lut *lut;
>> +	struct vs_dc *dc = dev_get_drvdata(dev);
>> +
>> +	if (crtc_state->color_mgmt_changed) {
>> +		if (blob && blob->length) {
>> +			lut = blob->data;
>> +			vs_dc_set_gamma(dc, crtc, lut,
>> +					blob->length / sizeof(*lut));
>> +			vs_dc_enable_gamma(dc, crtc, true);
>> +		} else {
>> +			vs_dc_enable_gamma(dc, crtc, false);
>> +		}
>> +	}
>> +}
>> +
>> +static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
>> +				 struct drm_atomic_state *state)
>> +{
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct drm_pending_vblank_event *event = crtc->state->event;
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	vs_dc_commit(dc);
>> +
>> +	if (event) {
>> +		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
>> +
>> +		spin_lock_irq(&crtc->dev->event_lock);
>> +		drm_crtc_arm_vblank_event(crtc, event);
>> +		spin_unlock_irq(&crtc->dev->event_lock);
>> +		crtc->state->event = NULL;
> 
> I think even should be cleared within the lock.
> 
>> +	}
>> +}
>> +
>> +static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
>> +	.atomic_check = drm_crtc_helper_atomic_check,
>> +	.atomic_enable	= vs_crtc_atomic_enable,
>> +	.atomic_disable = vs_crtc_atomic_disable,
>> +	.atomic_begin	= vs_crtc_atomic_begin,
>> +	.atomic_flush	= vs_crtc_atomic_flush,
>> +};
>> +
>> +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
>> +	{ VS_SINGLE_DC,				"single dc mode" },
>> +	{ VS_MULTI_DC_PRIMARY,		"primary dc for multi dc mode" },
>> +	{ VS_MULTI_DC_SECONDARY,	"secondary dc for multi dc mode" },
>> +};
>> +
>> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
>> +			       struct vs_dc_info *info)
>> +{
>> +	struct vs_crtc *crtc;
>> +	int ret;
>> +
>> +	if (!info)
>> +		return NULL;
>> +
>> +	crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
>> +	if (!crtc)
>> +		return NULL;
>> +
>> +	ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
>> +					 NULL, NULL, &vs_crtc_funcs,
>> +					 info->name ? info->name : NULL);
> 
> It might be better to add drmm_crtc_init() helper.
> 
>> +	if (ret)
>> +		return NULL;
>> +
>> +	drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
>> +
>> +	if (info->gamma_size) {
>> +		ret = drm_mode_crtc_set_gamma_size(&crtc->base,
>> +						   info->gamma_size);
>> +		if (ret)
>> +			return NULL;
>> +
>> +		drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
>> +					   info->gamma_size);
>> +	}
>> +
>> +	crtc->max_bpc = info->max_bpc;
>> +	crtc->color_formats = info->color_formats;
>> +	return crtc;
>> +}
>> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h
>> new file mode 100644
>> index 000000000..526dd63e5
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.h
>> @@ -0,0 +1,43 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#ifndef __VS_CRTC_H__
>> +#define __VS_CRTC_H__
>> +
>> +#include <drm/drm_crtc.h>
>> +#include <drm/drm_crtc_helper.h>
>> +
>> +#include "vs_type.h"
>> +
>> +struct vs_crtc_state {
>> +	struct drm_crtc_state base;
>> +
>> +	u32 output_fmt;
>> +	u8 encoder_type;
>> +	u8 bpp;
>> +	bool underflow;
>> +};
>> +
>> +struct vs_crtc {
>> +	struct drm_crtc base;
>> +	struct device *dev;
>> +	unsigned int max_bpc;
>> +	unsigned int color_formats;
>> +};
>> +
>> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
>> +			       struct vs_dc_info *info);
>> +
>> +static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc)
>> +{
>> +	return container_of(crtc, struct vs_crtc, base);
>> +}
>> +
>> +static inline struct vs_crtc_state *
>> +to_vs_crtc_state(struct drm_crtc_state *state)
>> +{
>> +	return container_of(state, struct vs_crtc_state, base);
>> +}
>> +#endif /* __VS_CRTC_H__ */
>> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
>> new file mode 100644
>> index 000000000..b5ab92d98
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_dc.c
>> @@ -0,0 +1,1002 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#include <linux/component.h>
>> +#include <linux/clk.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/reset.h>
>> +
>> +#include <drm/drm_atomic.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_blend.h>
>> +#include <drm/drm_framebuffer.h>
>> +#include <drm/drm_vblank.h>
>> +#include <drm/vs_drm.h>
>> +
>> +#include "vs_dc_hw.h"
>> +#include "vs_dc.h"
>> +#include "vs_drv.h"
>> +
>> +static const char * const vout_clocks[] = {
>> +	"noc_bus",
>> +	"channel0",
>> +	"channel1",
>> +	"dc_core",
>> +	"axi_core",
>> +	"ahb",
>> +	"hdmi_tx",
>> +	"dc_parent",
>> +
>> +};
>> +
>> +static const char * const vout_resets[] = {
>> +	"axi",
>> +	"ahb",
>> +	"core",
>> +};
>> +
>> +static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb)
>> +{
>> +	u8 f = FORMAT_A8R8G8B8;
>> +
>> +	switch (format) {
>> +	case DRM_FORMAT_XRGB4444:
>> +	case DRM_FORMAT_RGBX4444:
>> +	case DRM_FORMAT_XBGR4444:
>> +	case DRM_FORMAT_BGRX4444:
>> +		f = FORMAT_X4R4G4B4;
>> +		break;
>> +	case DRM_FORMAT_ARGB4444:
>> +	case DRM_FORMAT_RGBA4444:
>> +	case DRM_FORMAT_ABGR4444:
>> +	case DRM_FORMAT_BGRA4444:
>> +		f = FORMAT_A4R4G4B4;
>> +		break;
>> +	case DRM_FORMAT_XRGB1555:
>> +	case DRM_FORMAT_RGBX5551:
>> +	case DRM_FORMAT_XBGR1555:
>> +	case DRM_FORMAT_BGRX5551:
>> +		f = FORMAT_X1R5G5B5;
>> +		break;
>> +	case DRM_FORMAT_ARGB1555:
>> +	case DRM_FORMAT_RGBA5551:
>> +	case DRM_FORMAT_ABGR1555:
>> +	case DRM_FORMAT_BGRA5551:
>> +		f = FORMAT_A1R5G5B5;
>> +		break;
>> +	case DRM_FORMAT_RGB565:
>> +	case DRM_FORMAT_BGR565:
>> +		f = FORMAT_R5G6B5;
>> +		break;
>> +	case DRM_FORMAT_XRGB8888:
>> +	case DRM_FORMAT_RGBX8888:
>> +	case DRM_FORMAT_XBGR8888:
>> +	case DRM_FORMAT_BGRX8888:
>> +		f = FORMAT_X8R8G8B8;
>> +		break;
>> +	case DRM_FORMAT_ARGB8888:
>> +	case DRM_FORMAT_RGBA8888:
>> +	case DRM_FORMAT_ABGR8888:
>> +	case DRM_FORMAT_BGRA8888:
>> +		f = FORMAT_A8R8G8B8;
>> +		break;
>> +	case DRM_FORMAT_YUYV:
>> +	case DRM_FORMAT_YVYU:
>> +		f = FORMAT_YUY2;
>> +		break;
>> +	case DRM_FORMAT_UYVY:
>> +	case DRM_FORMAT_VYUY:
>> +		f = FORMAT_UYVY;
>> +		break;
>> +	case DRM_FORMAT_YUV420:
>> +	case DRM_FORMAT_YVU420:
>> +		f = FORMAT_YV12;
>> +		break;
>> +	case DRM_FORMAT_NV21:
>> +		f = FORMAT_NV12;
>> +		break;
>> +	case DRM_FORMAT_NV16:
>> +	case DRM_FORMAT_NV61:
>> +		f = FORMAT_NV16;
>> +		break;
>> +	case DRM_FORMAT_P010:
>> +		f = FORMAT_P010;
>> +		break;
>> +	case DRM_FORMAT_ARGB2101010:
>> +	case DRM_FORMAT_RGBA1010102:
>> +	case DRM_FORMAT_ABGR2101010:
>> +	case DRM_FORMAT_BGRA1010102:
>> +		f = FORMAT_A2R10G10B10;
>> +		break;
>> +	case DRM_FORMAT_NV12:
>> +		f = FORMAT_NV12;
>> +		break;
>> +	case DRM_FORMAT_YUV444:
>> +		f = FORMAT_YUV444;
>> +		break;
>> +	default:
>> +		break;
>> +	}
>> +
>> +	fb->format = f;
>> +}
>> +
>> +static inline void update_swizzle(u32 format, struct dc_hw_fb *fb)
>> +{
>> +	fb->swizzle = SWIZZLE_ARGB;
>> +	fb->uv_swizzle = 0;
>> +
>> +	switch (format) {
>> +	case DRM_FORMAT_RGBX4444:
>> +	case DRM_FORMAT_RGBA4444:
>> +	case DRM_FORMAT_RGBX5551:
>> +	case DRM_FORMAT_RGBA5551:
>> +	case DRM_FORMAT_RGBX8888:
>> +	case DRM_FORMAT_RGBA8888:
>> +	case DRM_FORMAT_RGBA1010102:
>> +		fb->swizzle = SWIZZLE_RGBA;
>> +		break;
>> +	case DRM_FORMAT_XBGR4444:
>> +	case DRM_FORMAT_ABGR4444:
>> +	case DRM_FORMAT_XBGR1555:
>> +	case DRM_FORMAT_ABGR1555:
>> +	case DRM_FORMAT_BGR565:
>> +	case DRM_FORMAT_XBGR8888:
>> +	case DRM_FORMAT_ABGR8888:
>> +	case DRM_FORMAT_ABGR2101010:
>> +		fb->swizzle = SWIZZLE_ABGR;
>> +		break;
>> +	case DRM_FORMAT_BGRX4444:
>> +	case DRM_FORMAT_BGRA4444:
>> +	case DRM_FORMAT_BGRX5551:
>> +	case DRM_FORMAT_BGRA5551:
>> +	case DRM_FORMAT_BGRX8888:
>> +	case DRM_FORMAT_BGRA8888:
>> +	case DRM_FORMAT_BGRA1010102:
>> +		fb->swizzle = SWIZZLE_BGRA;
>> +		break;
>> +	case DRM_FORMAT_YVYU:
>> +	case DRM_FORMAT_VYUY:
>> +	case DRM_FORMAT_NV21:
>> +	case DRM_FORMAT_NV61:
>> +		fb->uv_swizzle = 1;
>> +		break;
>> +	default:
>> +		break;
>> +	}
>> +}
>> +
>> +static inline void update_watermark(struct drm_property_blob *watermark,
>> +				    struct dc_hw_fb *fb)
>> +{
>> +	struct drm_vs_watermark *data;
>> +
>> +	fb->water_mark = 0;
>> +
>> +	if (watermark) {
>> +		data = watermark->data;
>> +		fb->water_mark = data->watermark & 0xFFFFF;
>> +	}
>> +}
>> +
>> +static inline u8 to_vs_rotation(unsigned int rotation)
>> +{
>> +	u8 rot;
>> +
>> +	switch (rotation & DRM_MODE_REFLECT_MASK) {
>> +	case DRM_MODE_REFLECT_X:
>> +		rot = FLIP_X;
>> +		return rot;
>> +	case DRM_MODE_REFLECT_Y:
>> +		rot = FLIP_Y;
>> +		return rot;
>> +	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
>> +		rot = FLIP_XY;
>> +		return rot;
>> +	default:
>> +		break;
>> +	}
>> +
>> +	switch (rotation & DRM_MODE_ROTATE_MASK) {
>> +	case DRM_MODE_ROTATE_0:
>> +		rot = ROT_0;
>> +		break;
>> +	case DRM_MODE_ROTATE_90:
>> +		rot = ROT_90;
>> +		break;
>> +	case DRM_MODE_ROTATE_180:
>> +		rot = ROT_180;
>> +		break;
>> +	case DRM_MODE_ROTATE_270:
>> +		rot = ROT_270;
>> +		break;
>> +	default:
>> +		rot = ROT_0;
>> +		break;
>> +	}
>> +
>> +	return rot;
>> +}
>> +
>> +static inline u8 to_vs_yuv_color_space(u32 color_space)
>> +{
>> +	u8 cs;
>> +
>> +	switch (color_space) {
>> +	case DRM_COLOR_YCBCR_BT601:
>> +		cs = COLOR_SPACE_601;
>> +		break;
>> +	case DRM_COLOR_YCBCR_BT709:
>> +		cs = COLOR_SPACE_709;
>> +		break;
>> +	case DRM_COLOR_YCBCR_BT2020:
>> +		cs = COLOR_SPACE_2020;
>> +		break;
>> +	default:
>> +		cs = COLOR_SPACE_601;
>> +		break;
>> +	}
>> +
>> +	return cs;
>> +}
>> +
>> +static inline u8 to_vs_tile_mode(u64 modifier)
>> +{
>> +	if (modifier == DRM_FORMAT_MOD_VIVANTE_TILED)
>> +		return DC_TILE_MODE4X4;
>> +
>> +	return (u8)(modifier & DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK);
>> +}
>> +
>> +static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc)
>> +{
>> +	u8 panel_num = dc->hw.info->panel_num;
>> +	u32 index = drm_crtc_index(crtc);
>> +	int i;
>> +
>> +	for (i = 0; i < panel_num; i++) {
>> +		if (index == dc->crtc[i]->base.index)
>> +			return i;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
>> +					  unsigned int alignment)
>> +{
>> +	struct vs_drm_device *priv = to_vs_drm_private(drm_dev);
>> +
>> +	if (alignment > priv->pitch_alignment)
>> +		priv->pitch_alignment = alignment;
>> +}
>> +
>> +static int plda_clk_rst_init(struct device *dev)
>> +{
>> +	int ret = 0;
>> +	struct vs_dc *dc = dev_get_drvdata(dev);
>> +
>> +	ret = clk_bulk_prepare_enable(dc->nclks, dc->clk_vout);
>> +	if (ret) {
>> +		dev_err(dev, "failed to enable clocks\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = reset_control_bulk_deassert(dc->nrsts, dc->rst_vout);
>> +	return ret;
>> +}
>> +
>> +static void plda_clk_rst_deinit(struct device *dev)
>> +{
>> +	struct vs_dc *dc = dev_get_drvdata(dev);
>> +
>> +	reset_control_bulk_assert(dc->nrsts, dc->rst_vout);
>> +	clk_bulk_disable_unprepare(dc->nclks, dc->clk_vout);
>> +}
>> +
>> +static void dc_deinit(struct device *dev)
>> +{
>> +	struct vs_dc *dc = dev_get_drvdata(dev);
>> +
>> +	dc_hw_enable_interrupt(&dc->hw, 0);
>> +	dc_hw_deinit(&dc->hw);
>> +	plda_clk_rst_deinit(dev);
>> +}
>> +
>> +static int dc_init(struct device *dev)
>> +{
>> +	struct vs_dc *dc = dev_get_drvdata(dev);
>> +	int ret;
>> +
>> +	dc->first_frame = true;
>> +
>> +	ret = plda_clk_rst_init(dev);
>> +	if (ret < 0) {
>> +		dev_err(dev, "failed to init dc clk reset: %d\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	ret = dc_hw_init(&dc->hw);
>> +	if (ret) {
>> +		dev_err(dev, "failed to init DC HW\n");
>> +		return ret;
>> +	}
>> +	return 0;
>> +}
>> +
>> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
>> +{
>> +	struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
>> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
>> +	struct dc_hw_display display;
>> +
>> +	display.bus_format = crtc_state->output_fmt;
>> +	display.h_active = mode->hdisplay;
>> +	display.h_total = mode->htotal;
>> +	display.h_sync_start = mode->hsync_start;
>> +	display.h_sync_end = mode->hsync_end;
>> +	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
>> +		display.h_sync_polarity = true;
>> +	else
>> +		display.h_sync_polarity = false;
>> +
>> +	display.v_active = mode->vdisplay;
>> +	display.v_total = mode->vtotal;
>> +	display.v_sync_start = mode->vsync_start;
>> +	display.v_sync_end = mode->vsync_end;
>> +
>> +	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
>> +		display.v_sync_polarity = true;
>> +	else
>> +		display.v_sync_polarity = false;
>> +
>> +	display.id = to_vs_display_id(dc, crtc);
>> +
>> +	display.enable = true;
>> +
>> +	if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) {
>> +		dc_hw_set_out(&dc->hw, OUT_DPI, display.id);
>> +		clk_set_rate(dc->clk_vout[CLK_VOUT_SOC_PIX].clk, mode->clock * 1000);
>> +		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX1].clk,
>> +			       dc->clk_vout[CLK_VOUT_SOC_PIX].clk);
>> +	} else {
>> +		dc_hw_set_out(&dc->hw, OUT_DP, display.id);
>> +		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX0].clk,
>> +			       dc->clk_vout[CLK_VOUT_HDMI_PIX].clk);
>> +	}
>> +
>> +	dc_hw_setup_display(&dc->hw, &display);
>> +}
>> +
>> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc)
>> +{
>> +	struct dc_hw_display display;
>> +
>> +	display.id = to_vs_display_id(dc, crtc);
>> +	display.enable = false;
>> +
>> +	dc_hw_setup_display(&dc->hw, &display);
>> +}
>> +
>> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
>> +		     struct drm_color_lut *lut, unsigned int size)
>> +{
>> +	u16 i, r, g, b;
>> +	u8 bits, id;
>> +
>> +	if (size != dc->hw.info->gamma_size) {
>> +		drm_err(crtc->dev, "gamma size does not match!\n");
>> +		return;
>> +	}
>> +
>> +	id = to_vs_display_id(dc, crtc);
>> +
>> +	bits = dc->hw.info->gamma_bits;
>> +	for (i = 0; i < size; i++) {
>> +		r = drm_color_lut_extract(lut[i].red, bits);
>> +		g = drm_color_lut_extract(lut[i].green, bits);
>> +		b = drm_color_lut_extract(lut[i].blue, bits);
>> +		dc_hw_update_gamma(&dc->hw, id, i, r, g, b);
>> +	}
>> +}
>> +
>> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
>> +			bool enable)
>> +{
>> +	u8 id;
>> +
>> +	id = to_vs_display_id(dc, crtc);
>> +	dc_hw_enable_gamma(&dc->hw, id, enable);
>> +}
>> +
>> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable)
>> +{
>> +	dc_hw_enable_interrupt(&dc->hw, enable);
>> +}
>> +
>> +static u32 calc_factor(u32 src, u32 dest)
>> +{
>> +	u32 factor = 1 << 16;
>> +
>> +	if (src > 1 && dest > 1)
>> +		factor = ((src - 1) << 16) / (dest - 1);
>> +
>> +	return factor;
>> +}
>> +
>> +static void update_scale(struct drm_plane_state *state, struct dc_hw_roi *roi,
>> +			 struct dc_hw_scale *scale)
>> +{
>> +	int dst_w = drm_rect_width(&state->dst);
>> +	int dst_h = drm_rect_height(&state->dst);
>> +	int src_w, src_h, temp;
>> +
>> +	scale->enable = false;
>> +
>> +	if (roi->enable) {
>> +		src_w = roi->width;
>> +		src_h = roi->height;
>> +	} else {
>> +		src_w = drm_rect_width(&state->src) >> 16;
>> +		src_h = drm_rect_height(&state->src) >> 16;
>> +	}
>> +
>> +	if (drm_rotation_90_or_270(state->rotation)) {
>> +		temp = src_w;
>> +		src_w = src_h;
>> +		src_h = temp;
>> +	}
>> +
>> +	if (src_w != dst_w) {
>> +		scale->scale_factor_x = calc_factor(src_w, dst_w);
>> +		scale->enable = true;
>> +	} else {
>> +		scale->scale_factor_x = 1 << 16;
>> +	}
>> +	if (src_h != dst_h) {
>> +		scale->scale_factor_y = calc_factor(src_h, dst_h);
>> +		scale->enable = true;
>> +	} else {
>> +		scale->scale_factor_y = 1 << 16;
>> +	}
>> +}
>> +
>> +static void update_fb(struct vs_plane *plane, u8 display_id,
>> +		      struct dc_hw_fb *fb, struct drm_plane_state *state)
>> +{
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> +	struct drm_framebuffer *drm_fb = state->fb;
>> +	struct drm_rect *src = &state->src;
>> +
>> +	fb->display_id = display_id;
>> +	fb->y_address = plane_state->dma_addr[0];
>> +	fb->y_stride = drm_fb->pitches[0];
>> +	if (drm_fb->format->format == DRM_FORMAT_YVU420) {
>> +		fb->u_address = plane_state->dma_addr[2];
>> +		fb->v_address = plane_state->dma_addr[1];
>> +		fb->u_stride = drm_fb->pitches[2];
>> +		fb->v_stride = drm_fb->pitches[1];
>> +	} else {
>> +		fb->u_address = plane_state->dma_addr[1];
>> +		fb->v_address = plane_state->dma_addr[2];
>> +		fb->u_stride = drm_fb->pitches[1];
>> +		fb->v_stride = drm_fb->pitches[2];
>> +	}
>> +	fb->width = drm_rect_width(src) >> 16;
>> +	fb->height = drm_rect_height(src) >> 16;
>> +	fb->tile_mode = to_vs_tile_mode(drm_fb->modifier);
>> +	fb->rotation = to_vs_rotation(state->rotation);
>> +	fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding);
>> +	fb->zpos = state->zpos;
>> +	fb->enable = state->visible;
>> +	update_format(drm_fb->format->format, drm_fb->modifier, fb);
>> +	update_swizzle(drm_fb->format->format, fb);
>> +	update_watermark(plane_state->watermark, fb);
>> +	plane_state->status.tile_mode = fb->tile_mode;
>> +}
>> +
>> +static void update_degamma(struct vs_dc *dc, struct vs_plane *plane,
>> +			   struct vs_plane_state *plane_state)
>> +{
>> +	dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
>> +	plane_state->degamma_changed = false;
>> +}
>> +
>> +static void update_roi(struct vs_dc *dc, u8 id,
>> +		       struct vs_plane_state *plane_state,
>> +		       struct dc_hw_roi *roi,
>> +		       struct drm_plane_state *state)
>> +{
>> +	struct drm_vs_roi *data;
>> +	struct drm_rect *src = &state->src;
>> +	u16 src_w = drm_rect_width(src) >> 16;
>> +	u16 src_h = drm_rect_height(src) >> 16;
>> +
>> +	if (plane_state->roi) {
>> +		data = plane_state->roi->data;
>> +
>> +		if (data->enable) {
>> +			roi->x = data->roi_x;
>> +			roi->y = data->roi_y;
>> +			roi->width = (data->roi_x + data->roi_w > src_w) ?
>> +						 (src_w - data->roi_x) : data->roi_w;
>> +			roi->height = (data->roi_y + data->roi_h > src_h) ?
>> +						  (src_h - data->roi_y) : data->roi_h;
>> +			roi->enable = true;
>> +		} else {
>> +			roi->enable = false;
>> +		}
>> +
>> +		dc_hw_update_roi(&dc->hw, id, roi);
>> +	} else {
>> +		roi->enable = false;
>> +	}
>> +}
>> +
>> +static void update_color_mgmt(struct vs_dc *dc, u8 id,
>> +			      struct dc_hw_fb *fb,
>> +			      struct vs_plane_state *plane_state)
>> +{
>> +	struct drm_vs_color_mgmt *data;
>> +	struct dc_hw_colorkey colorkey;
>> +
>> +	if (plane_state->color_mgmt) {
>> +		data = plane_state->color_mgmt->data;
>> +
>> +		fb->clear_enable = data->clear_enable;
>> +		fb->clear_value = data->clear_value;
>> +
>> +		if (data->colorkey > data->colorkey_high)
>> +			data->colorkey = data->colorkey_high;
>> +
>> +		colorkey.colorkey = data->colorkey;
>> +		colorkey.colorkey_high = data->colorkey_high;
>> +		colorkey.transparency = (data->transparency) ?
>> +				DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE;
>> +		dc_hw_update_colorkey(&dc->hw, id, &colorkey);
>> +	}
>> +}
>> +
>> +static void update_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +			 struct drm_plane *drm_plane,
>> +			 struct drm_atomic_state *drm_state)
>> +{
>> +	struct dc_hw_fb fb = {0};
>> +	struct dc_hw_scale scale;
>> +	struct dc_hw_position pos;
>> +	struct dc_hw_blend blend;
>> +	struct dc_hw_roi roi;
>> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
>> +									   drm_plane);
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> +	struct drm_rect *dest = &state->dst;
>> +	bool dec_enable = false;
>> +	u8 display_id = 0;
>> +
>> +	display_id = to_vs_display_id(dc, state->crtc);
>> +	update_fb(plane, display_id, &fb, state);
>> +	fb.dec_enable = dec_enable;
>> +
>> +	update_roi(dc, plane->id, plane_state, &roi, state);
>> +
>> +	update_scale(state, &roi, &scale);
>> +
>> +	if (plane_state->degamma_changed)
>> +		update_degamma(dc, plane, plane_state);
>> +
>> +	pos.start_x = dest->x1;
>> +	pos.start_y = dest->y1;
>> +	pos.end_x = dest->x2;
>> +	pos.end_y = dest->y2;
>> +
>> +	blend.alpha = (u8)(state->alpha >> 8);
>> +	blend.blend_mode = (u8)(state->pixel_blend_mode);
>> +
>> +	update_color_mgmt(dc, plane->id, &fb, plane_state);
>> +
>> +	dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
>> +}
>> +
>> +static void update_qos(struct vs_dc *dc, struct vs_plane *plane,
>> +		       struct drm_plane *drm_plane,
>> +		       struct drm_atomic_state *drm_state)
>> +{
>> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
>> +									   drm_plane);
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> +	struct drm_vs_watermark *data;
>> +	struct dc_hw_qos qos;
>> +
>> +	if (plane_state->watermark) {
>> +		data = plane_state->watermark->data;
>> +
>> +		if (data->qos_high) {
>> +			if (data->qos_low > data->qos_high)
>> +				data->qos_low = data->qos_high;
>> +
>> +			qos.low_value = data->qos_low & 0x0F;
>> +			qos.high_value = data->qos_high & 0x0F;
>> +			dc_hw_update_qos(&dc->hw, &qos);
>> +		}
>> +	}
>> +}
>> +
>> +static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor)
>> +{
>> +	u8 size_type;
>> +
>> +	switch (state->crtc_w) {
>> +	case 32:
>> +		size_type = CURSOR_SIZE_32X32;
>> +		break;
>> +	case 64:
>> +		size_type = CURSOR_SIZE_64X64;
>> +		break;
>> +	default:
>> +		size_type = CURSOR_SIZE_32X32;
>> +		break;
>> +	}
>> +
>> +	cursor->size = size_type;
>> +}
>> +
>> +static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +				struct drm_plane *drm_plane,
>> +				struct drm_atomic_state *drm_state)
>> +{
>> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
>> +								       drm_plane);
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> +	struct drm_framebuffer *drm_fb = state->fb;
>> +	struct dc_hw_cursor cursor;
>> +
>> +	cursor.address = plane_state->dma_addr[0];
>> +	cursor.x = state->crtc_x;
>> +	cursor.y = state->crtc_y;
>> +	cursor.hot_x = drm_fb->hot_x;
>> +	cursor.hot_y = drm_fb->hot_y;
>> +	cursor.display_id = to_vs_display_id(dc, state->crtc);
>> +	update_cursor_size(state, &cursor);
>> +	cursor.enable = true;
>> +
>> +	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
>> +}
>> +
>> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +			struct drm_plane *drm_plane,
>> +			struct drm_atomic_state *drm_state)
>> +{
>> +	update_plane(dc, plane, drm_plane, drm_state);
>> +	update_qos(dc, plane, drm_plane, drm_state);
>> +}
>> +
>> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +			       struct drm_plane *drm_plane,
>> +			       struct drm_atomic_state *drm_state)
>> +{
>> +	update_cursor_plane(dc, plane, drm_plane, drm_state);
>> +}
>> +
>> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +			 struct drm_plane_state *old_state)
>> +{
>> +	struct dc_hw_fb fb = {0};
>> +
>> +	fb.enable = false;
>> +	dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL, NULL, NULL);
>> +}
>> +
>> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +				struct drm_plane_state *old_state)
>> +{
>> +	struct dc_hw_cursor cursor = {0};
>> +
>> +	cursor.enable = false;
>> +	cursor.display_id = to_vs_display_id(dc, old_state->crtc);
>> +	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
>> +}
>> +
>> +static bool vs_dc_mod_supported(const struct vs_plane_info *plane_info,
>> +				u64 modifier)
>> +{
>> +	const u64 *mods;
>> +
>> +	if (!plane_info->modifiers)
>> +		return false;
>> +
>> +	for (mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID; mods++) {
>> +		if (*mods == modifier)
>> +			return true;
>> +	}
>> +
>> +	return false;
>> +}
>> +
>> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
>> +		      struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> +										 plane);
>> +
>> +	struct drm_framebuffer *fb = new_plane_state->fb;
>> +	const struct vs_plane_info *plane_info;
>> +	struct drm_crtc *crtc = new_plane_state->crtc;
>> +	struct drm_crtc_state *crtc_state;
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +
>> +	plane_info = &dc->hw.info->planes[vs_plane->id];
>> +
>> +	if (fb->width < plane_info->min_width ||
>> +	    fb->width > plane_info->max_width ||
>> +	    fb->height < plane_info->min_height ||
>> +	    fb->height > plane_info->max_height)
>> +		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n",
>> +			     vs_plane->id);
>> +
>> +	if (!vs_dc_mod_supported(plane_info, fb->modifier)) {
>> +		drm_err(plane->dev, "unsupported modifier on plane%d.\n", vs_plane->id);
>> +		return -EINVAL;
>> +	}
>> +
>> +	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
>> +	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
>> +						  plane_info->min_scale,
>> +						  plane_info->max_scale,
>> +						  true, true);
>> +}
>> +
>> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
>> +			     struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> +									   plane);
>> +	struct drm_framebuffer *fb = new_plane_state->fb;
>> +	const struct vs_plane_info *plane_info;
>> +	struct drm_crtc *crtc = new_plane_state->crtc;
>> +	struct drm_crtc_state *crtc_state;
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +
>> +	plane_info = &dc->hw.info->planes[vs_plane->id];
>> +
>> +	if (fb->width < plane_info->min_width ||
>> +	    fb->width > plane_info->max_width ||
>> +	    fb->height < plane_info->min_height ||
>> +	    fb->height > plane_info->max_height)
>> +		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n", vs_plane->id);
>> +
>> +	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
>> +	if (IS_ERR(crtc_state))
>> +		return -EINVAL;
>> +
>> +	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
>> +						plane_info->min_scale,
>> +						plane_info->max_scale,
>> +						true, true);
>> +}
>> +
>> +static void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
>> +{
>> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
>> +
>> +	drm_crtc_handle_vblank(crtc);
>> +
>> +	vs_crtc_state->underflow = underflow;
>> +}
>> +
>> +static irqreturn_t dc_isr(int irq, void *data)
>> +{
>> +	struct vs_dc *dc = data;
>> +	struct vs_dc_info *dc_info = dc->hw.info;
>> +	u32 i, ret;
>> +
>> +	ret = dc_hw_get_interrupt(&dc->hw);
>> +
>> +	for (i = 0; i < dc_info->panel_num; i++)
>> +		vs_crtc_handle_vblank(&dc->crtc[i]->base, dc_hw_check_underflow(&dc->hw));
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +void vs_dc_commit(struct vs_dc *dc)
>> +{
>> +	dc_hw_enable_shadow_register(&dc->hw, false);
>> +
>> +	dc_hw_commit(&dc->hw);
>> +
>> +	if (dc->first_frame)
>> +		dc->first_frame = false;
>> +
>> +	dc_hw_enable_shadow_register(&dc->hw, true);
>> +}
>> +
>> +static int dc_bind(struct device *dev, struct device *master, void *data)
>> +{
>> +	struct drm_device *drm_dev = data;
>> +	struct vs_dc *dc = dev_get_drvdata(dev);
>> +	struct device_node *port;
>> +	struct vs_crtc *crtc;
>> +	struct vs_dc_info *dc_info;
>> +	struct vs_plane *plane;
>> +	struct vs_plane_info *plane_info;
>> +	int i, ret;
>> +	u32 ctrc_mask = 0;
>> +
>> +	if (!drm_dev || !dc) {
>> +		dev_err(dev, "devices are not created.\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	ret = dc_init(dev);
>> +	if (ret < 0) {
>> +		drm_err(drm_dev, "Failed to initialize DC hardware.\n");
>> +		return ret;
>> +	}
>> +
>> +	port = of_get_child_by_name(dev->of_node, "port");
>> +	if (!port) {
>> +		drm_err(drm_dev, "no port node found\n");
>> +		return -ENODEV;
>> +	}
>> +	of_node_put(port);
>> +
>> +	dc_info = dc->hw.info;
>> +
>> +	for (i = 0; i < dc_info->panel_num; i++) {
>> +		crtc = vs_crtc_create(drm_dev, dc_info);
>> +		if (!crtc) {
>> +			drm_err(drm_dev, "Failed to create CRTC.\n");
>> +			ret = -ENOMEM;
>> +			return ret;
>> +		}
>> +
>> +		crtc->base.port = port;
>> +		crtc->dev = dev;
>> +		dc->crtc[i] = crtc;
>> +		ctrc_mask |= drm_crtc_mask(&crtc->base);
>> +	}
>> +
>> +	for (i = 0; i < dc_info->plane_num; i++) {
>> +		plane_info = (struct vs_plane_info *)&dc_info->planes[i];
>> +
>> +		if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
>> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
>> +						drm_crtc_mask(&dc->crtc[0]->base));
>> +		} else if (!strcmp(plane_info->name, "Primary_1") ||
>> +				   !strcmp(plane_info->name, "Cursor_1")) {
>> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
>> +						drm_crtc_mask(&dc->crtc[1]->base));
> 
> I'd say, it's not typical to use name to distinguish objects. Maybe you 
> can add indices to the info structure and drop the names instead?
> 
> 
>> +		} else {
>> +			plane = vs_plane_create(drm_dev, plane_info,
>> +						dc_info->layer_num, ctrc_mask);
>> +		}
>> +
>> +		if (IS_ERR(plane)) {
>> +			dev_err(dev, "failed to construct plane\n");
>> +			return PTR_ERR(plane);
>> +		}
>> +
>> +		plane->id = i;
>> +		dc->planes[i].id = plane_info->id;
>> +
>> +		if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
>> +			if (!strcmp(plane_info->name, "Primary"))
>> +				dc->crtc[0]->base.primary = &plane->base;
>> +			else
>> +				dc->crtc[1]->base.primary = &plane->base;
> 
> This definitely urges for an index in the plane info / plane_state.
> 
>> +			drm_dev->mode_config.min_width = plane_info->min_width;
>> +			drm_dev->mode_config.min_height =
>> +							plane_info->min_height;
>> +			drm_dev->mode_config.max_width = plane_info->max_width;
>> +			drm_dev->mode_config.max_height =
>> +							plane_info->max_height;
>> +		}
>> +
>> +		if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
>> +			if (!strcmp(plane_info->name, "Cursor"))
>> +				dc->crtc[0]->base.cursor = &plane->base;
>> +			else
>> +				dc->crtc[1]->base.cursor = &plane->base;
>> +			drm_dev->mode_config.cursor_width =
>> +							plane_info->max_width;
>> +			drm_dev->mode_config.cursor_height =
>> +							plane_info->max_height;
>> +		}
>> +	}
>> +
>> +	vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
>> +	return 0;
>> +}
>> +
>> +static void dc_unbind(struct device *dev, struct device *master, void *data)
>> +{
>> +	dc_deinit(dev);
>> +}
>> +
>> +const struct component_ops dc_component_ops = {
>> +	.bind = dc_bind,
>> +	.unbind = dc_unbind,
>> +};
>> +
>> +static const struct of_device_id dc_driver_dt_match[] = {
>> +	{ .compatible = "starfive,jh7110-dc8200", },
>> +	{},
>> +};
>> +MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
>> +
>> +static int dc_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct vs_dc *dc;
>> +	int irq, ret, i;
>> +
>> +	dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
>> +	if (!dc)
>> +		return -ENOMEM;
>> +
>> +	dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
>> +	if (IS_ERR(dc->hw.hi_base))
>> +		return PTR_ERR(dc->hw.hi_base);
>> +
>> +	dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
>> +	if (IS_ERR(dc->hw.reg_base))
>> +		return PTR_ERR(dc->hw.reg_base);
>> +
>> +	dc->nclks = ARRAY_SIZE(dc->clk_vout);
>> +	for (i = 0; i < dc->nclks; ++i)
>> +		dc->clk_vout[i].id = vout_clocks[i];
>> +	ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
>> +	if (ret) {
>> +		dev_err(dev, "Failed to get clk controls\n");
>> +		return ret;
>> +	}
>> +
>> +	dc->nrsts = ARRAY_SIZE(dc->rst_vout);
>> +	for (i = 0; i < dc->nrsts; ++i)
>> +		dc->rst_vout[i].id = vout_resets[i];
>> +	ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
>> +						 dc->rst_vout);
>> +	if (ret) {
>> +		dev_err(dev, "Failed to get reset controls\n");
>> +		return ret;
>> +	}
>> +
>> +	irq = platform_get_irq(pdev, 0);
>> +
>> +	ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
> 
> Are you ready to handle the IRQ at this point?
> 
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to install irq:%u.\n", irq);
>> +		return ret;
>> +	}
>> +
>> +	dev_set_drvdata(dev, dc);
>> +
>> +	return component_add(dev, &dc_component_ops);
>> +}
>> +
>> +static int dc_remove(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +
>> +	component_del(dev, &dc_component_ops);
>> +
>> +	dev_set_drvdata(dev, NULL);
>> +
>> +	return 0;
>> +}
>> +
>> +struct platform_driver dc_platform_driver = {
>> +	.probe = dc_probe,
>> +	.remove = dc_remove,
>> +	.driver = {
>> +		.name = "vs-dc",
>> +		.of_match_table = of_match_ptr(dc_driver_dt_match),
>> +	},
>> +};
>> +
>> +MODULE_AUTHOR("StarFive Corporation");
>> +MODULE_DESCRIPTION("VeriSilicon DC Driver");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
>> new file mode 100644
>> index 000000000..8e96fd32c
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_dc.h
>> @@ -0,0 +1,80 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#ifndef __VS_DC_H__
>> +#define __VS_DC_H__
>> +
>> +#include <linux/clk.h>
>> +#include <linux/mm_types.h>
>> +#include <linux/reset.h>
>> +
>> +#include <drm/drm_fourcc.h>
>> +#include <drm/drm_modes.h>
>> +
>> +#include "vs_crtc.h"
>> +#include "vs_dc_hw.h"
>> +#include "vs_plane.h"
>> +
>> +struct vs_dc_plane {
>> +	enum dc_hw_plane_id id;
>> +};
>> +
>> +enum vout_clk {
>> +	CLK_VOUT_NOC_DISP = 0,
>> +	CLK_VOUT_PIX0,
>> +	CLK_VOUT_PIX1,
>> +	CLK_VOUT_CORE,
>> +	CLK_VOUT_AXI,
>> +	CLK_VOUT_AHB,
>> +	CLK_VOUT_HDMI_PIX,
>> +	CLK_VOUT_SOC_PIX,
>> +	CLK_VOUT_NUM
>> +};
>> +
>> +enum rst_vout {
>> +	RST_VOUT_AXI = 0,
>> +	RST_VOUT_AHB,
>> +	RST_VOUT_CORE,
>> +	RST_VOUT_NUM
>> +};
>> +
>> +struct vs_dc {
>> +	struct vs_crtc		*crtc[DC_DISPLAY_NUM];
>> +	struct dc_hw		hw;
>> +	bool			first_frame;
>> +
>> +	struct vs_dc_plane	planes[PLANE_NUM];
>> +	struct clk_bulk_data	clk_vout[CLK_VOUT_NUM];
>> +	int			nclks;
>> +	struct reset_control_bulk_data rst_vout[RST_VOUT_NUM];
>> +	int			nrsts;
>> +};
>> +
>> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc);
>> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc);
>> +
>> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
>> +		     struct drm_color_lut *lut, unsigned int size);
>> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc, bool enable);
>> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable);
>> +void vs_dc_commit(struct vs_dc *dc);
>> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +			struct drm_plane *drm_plane,
>> +			struct drm_atomic_state *drm_state);
>> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +			 struct drm_plane_state *old_state);
>> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
>> +		      struct drm_atomic_state *state);
>> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +			       struct drm_plane *drm_plane,
>> +			       struct drm_atomic_state *drm_state);
>> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +				struct drm_plane_state *old_state);
>> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
>> +			     struct drm_atomic_state *state);
>> +
>> +extern struct platform_driver dc_platform_driver;
>> +
>> +#endif /* __VS_DC_H__ */
>> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
>> new file mode 100644
>> index 000000000..e5acb7eb4
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
>> @@ -0,0 +1,1959 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#include <linux/bits.h>
>> +#include <linux/io.h>
>> +#include <linux/media-bus-format.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_blend.h>
>> +#include <drm/drm_fourcc.h>
>> +#include <drm/vs_drm.h>
>> +
>> +#include "vs_dc_hw.h"
>> +#include "vs_type.h"
>> +
>> +static const u32 horkernel[] = {
>> +	0x00000000, 0x20000000, 0x00002000, 0x00000000,
>> +	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
>> +	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
>> +	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
>> +	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
>> +	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
>> +	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
>> +	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
>> +	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
>> +	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
>> +	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
>> +	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
>> +	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
>> +	0x00000000, 0x00000000, 0x00000000, 0x01470000,
>> +	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
>> +	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
>> +	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
>> +	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
>> +	0x00000000, 0x00000000, 0x00004000, 0x00000000,
>> +	0x00000000, 0x00000000, 0x20002000, 0x00000000,
>> +	0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
>> +	0x00000000, 0x00000000, 0x00000000, 0x27e1181f,
>> +	0x00000000, 0x00000000, 0x00000000, 0x14680000,
>> +	0x00002b98, 0x00000000, 0x00000000, 0x00000000,
>> +	0x2f1010f0, 0x00000000, 0x00000000, 0x00000000,
>> +	0x0dc70000, 0x00003239, 0x00000000, 0x00000000,
>> +	0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
>> +	0x00000000, 0x087f0000, 0x00003781, 0x00000000,
>> +	0x00000000, 0x00000000, 0x399a0666, 0x00000000,
>> +	0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
>> +	0x00000000, 0x00000000, 0x00000000, 0x3cc4033c,
>> +	0x00000000, 0x00000000, 0x00000000, 0x021f0000,
>> +};
>> +
>> +#define H_COEF_SIZE (sizeof(horkernel) / sizeof(u32))
>> +
>> +static const u32 verkernel[] = {
>> +	0x00000000, 0x20000000, 0x00002000, 0x00000000,
>> +	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
>> +	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
>> +	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
>> +	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
>> +	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
>> +	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
>> +	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
>> +	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
>> +	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
>> +	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
>> +	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
>> +	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
>> +	0x00000000, 0x00000000, 0x00000000, 0x01470000,
>> +	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
>> +	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
>> +	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
>> +	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
>> +	0x00000000, 0x00000000, 0x00004000, 0x00000000,
>> +	0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab,
>> +	0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
>> +	0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +};
>> +
>> +#define V_COEF_SIZE (sizeof(verkernel) / sizeof(u32))
>> +
>> +/*
>> + * RGB 709->2020 conversion parameters
>> + */
>> +static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = {
>> +	10279,	5395,	709,
>> +	1132,	15065,	187,
>> +	269,	1442,	14674
>> +};
>> +
>> +/*
>> + * YUV601 to RGB conversion parameters
>> + * YUV2RGB[0]  - [8] : C0 - C8;
>> + * YUV2RGB[9]  - [11]: D0 - D2;
>> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
>> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
>> + */
>> +static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
>> +	1196,	0,			1640,	1196,
>> +	-404,	-836,		1196,	2076,
>> +	0,		-916224,	558336,	-1202944,
>> +	64,		 940,		64,		960
>> +};
>> +
>> +/*
>> + * YUV709 to RGB conversion parameters
>> + * YUV2RGB[0]  - [8] : C0 - C8;
>> + * YUV2RGB[9]  - [11]: D0 - D2;
>> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
>> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
>> + */
>> +static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
>> +	1196,		0,		1844,	1196,
>> +	-220,		-548,	1196,	2172,
>> +	0,			-1020672, 316672,  -1188608,
>> +	64,			940,		64,		960
>> +};
>> +
>> +/*
>> + * YUV2020 to RGB conversion parameters
>> + * YUV2RGB[0]  - [8] : C0 - C8;
>> + * YUV2RGB[9]  - [11]: D0 - D2;
>> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
>> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
>> + */
>> +static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
>> +	1196, 0, 1724, 1196,
>> +	-192, -668, 1196, 2200,
>> +	0, -959232, 363776, -1202944,
>> +	64, 940, 64, 960
>> +};
>> +
>> +/*
>> + * RGB to YUV2020 conversion parameters
>> + * RGB2YUV[0] - [8] : C0 - C8;
>> + * RGB2YUV[9] - [11]: D0 - D2;
>> + */
>> +static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = {
>> +	230,	594,	52,
>> +	-125,  -323,	448,
>> +	448,   -412,   -36,
>> +	64,		512,	512
>> +};
>> +
>> +/*
>> + * Degamma table for 709 color space data.
>> + */
>> +static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
>> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005,
>> +	0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024,
>> +	0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064,
>> +	0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce,
>> +	0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166,
>> +	0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230,
>> +	0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331,
>> +	0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c,
>> +	0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
>> +	0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d,
>> +	0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999,
>> +	0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb,
>> +	0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65,
>> +	0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a,
>> +	0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b,
>> +	0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc,
>> +	0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e,
>> +	0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
>> +	0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e,
>> +	0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf,
>> +	0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea,
>> +	0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f,
>> +	0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930,
>> +	0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f,
>> +	0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee,
>> +	0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde,
>> +	0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
>> +	0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9,
>> +	0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406,
>> +	0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a,
>> +	0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576,
>> +	0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd,
>> +	0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
>> +};
>> +
>> +/*
>> + * Degamma table for 2020 color space data.
>> + */
>> +static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
>> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
>> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
>> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
>> +	0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
>> +	0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
>> +	0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006,
>> +	0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a,
>> +	0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f,
>> +	0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
>> +	0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024,
>> +	0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035,
>> +	0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d,
>> +	0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d,
>> +	0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098,
>> +	0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3,
>> +	0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123,
>> +	0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d,
>> +	0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
>> +	0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6,
>> +	0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1,
>> +	0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d,
>> +	0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6,
>> +	0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920,
>> +	0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a,
>> +	0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033,
>> +	0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593,
>> +	0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
>> +	0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656,
>> +	0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332,
>> +	0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480,
>> +	0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1,
>> +	0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99,
>> +	0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
>> +};
>> +
>> +/* one is for primary plane and the other is for all overlay planes */
>> +static const struct dc_hw_plane_reg dc_plane_reg[] = {
>> +	{
>> +	.y_address		= DC_FRAMEBUFFER_ADDRESS,
>> +	.u_address		= DC_FRAMEBUFFER_U_ADDRESS,
>> +	.v_address		= DC_FRAMEBUFFER_V_ADDRESS,
>> +	.y_stride		= DC_FRAMEBUFFER_STRIDE,
>> +	.u_stride		= DC_FRAMEBUFFER_U_STRIDE,
>> +	.v_stride		= DC_FRAMEBUFFER_V_STRIDE,
>> +	.size			= DC_FRAMEBUFFER_SIZE,
>> +	.top_left		= DC_FRAMEBUFFER_TOP_LEFT,
>> +	.bottom_right	= DC_FRAMEBUFFER_BOTTOM_RIGHT,
>> +	.scale_factor_x			= DC_FRAMEBUFFER_SCALE_FACTOR_X,
>> +	.scale_factor_y			= DC_FRAMEBUFFER_SCALE_FACTOR_Y,
>> +	.h_filter_coef_index	= DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
>> +	.h_filter_coef_data		= DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
>> +	.v_filter_coef_index	= DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
>> +	.v_filter_coef_data		= DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
>> +	.init_offset			= DC_FRAMEBUFFER_INIT_OFFSET,
>> +	.color_key				= DC_FRAMEBUFFER_COLOR_KEY,
>> +	.color_key_high			= DC_FRAMEBUFFER_COLOR_KEY_HIGH,
>> +	.clear_value			= DC_FRAMEBUFFER_CLEAR_VALUE,
>> +	.color_table_index		= DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
>> +	.color_table_data		= DC_FRAMEBUFFER_COLOR_TABLE_DATA,
>> +	.scale_config			= DC_FRAMEBUFFER_SCALE_CONFIG,
>> +	.water_mark				= DC_FRAMEBUFFER_WATER_MARK,
>> +	.degamma_index			= DC_FRAMEBUFFER_DEGAMMA_INDEX,
>> +	.degamma_data			= DC_FRAMEBUFFER_DEGAMMA_DATA,
>> +	.degamma_ex_data		= DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
>> +	.src_global_color		= DC_FRAMEBUFFER_SRC_GLOBAL_COLOR,
>> +	.dst_global_color		= DC_FRAMEBUFFER_DST_GLOBAL_COLOR,
>> +	.blend_config			= DC_FRAMEBUFFER_BLEND_CONFIG,
>> +	.roi_origin				= DC_FRAMEBUFFER_ROI_ORIGIN,
>> +	.roi_size				= DC_FRAMEBUFFER_ROI_SIZE,
>> +	.yuv_to_rgb_coef0			= DC_FRAMEBUFFER_YUVTORGB_COEF0,
>> +	.yuv_to_rgb_coef1			= DC_FRAMEBUFFER_YUVTORGB_COEF1,
>> +	.yuv_to_rgb_coef2			= DC_FRAMEBUFFER_YUVTORGB_COEF2,
>> +	.yuv_to_rgb_coef3			= DC_FRAMEBUFFER_YUVTORGB_COEF3,
>> +	.yuv_to_rgb_coef4			= DC_FRAMEBUFFER_YUVTORGB_COEF4,
>> +	.yuv_to_rgb_coefd0			= DC_FRAMEBUFFER_YUVTORGB_COEFD0,
>> +	.yuv_to_rgb_coefd1			= DC_FRAMEBUFFER_YUVTORGB_COEFD1,
>> +	.yuv_to_rgb_coefd2			= DC_FRAMEBUFFER_YUVTORGB_COEFD2,
>> +	.y_clamp_bound				= DC_FRAMEBUFFER_Y_CLAMP_BOUND,
>> +	.uv_clamp_bound				= DC_FRAMEBUFFER_UV_CLAMP_BOUND,
>> +	.rgb_to_rgb_coef0			= DC_FRAMEBUFFER_RGBTORGB_COEF0,
>> +	.rgb_to_rgb_coef1			= DC_FRAMEBUFFER_RGBTORGB_COEF1,
>> +	.rgb_to_rgb_coef2			= DC_FRAMEBUFFER_RGBTORGB_COEF2,
>> +	.rgb_to_rgb_coef3			= DC_FRAMEBUFFER_RGBTORGB_COEF3,
>> +	.rgb_to_rgb_coef4			= DC_FRAMEBUFFER_RGBTORGB_COEF4,
>> +	},
>> +	{
>> +	.y_address		= DC_OVERLAY_ADDRESS,
>> +	.u_address		= DC_OVERLAY_U_ADDRESS,
>> +	.v_address		= DC_OVERLAY_V_ADDRESS,
>> +	.y_stride		= DC_OVERLAY_STRIDE,
>> +	.u_stride		= DC_OVERLAY_U_STRIDE,
>> +	.v_stride		= DC_OVERLAY_V_STRIDE,
>> +	.size			= DC_OVERLAY_SIZE,
>> +	.top_left		= DC_OVERLAY_TOP_LEFT,
>> +	.bottom_right	= DC_OVERLAY_BOTTOM_RIGHT,
>> +	.scale_factor_x	= DC_OVERLAY_SCALE_FACTOR_X,
>> +	.scale_factor_y	= DC_OVERLAY_SCALE_FACTOR_Y,
>> +	.h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
>> +	.h_filter_coef_data  = DC_OVERLAY_H_FILTER_COEF_DATA,
>> +	.v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
>> +	.v_filter_coef_data  = DC_OVERLAY_V_FILTER_COEF_DATA,
>> +	.init_offset		 = DC_OVERLAY_INIT_OFFSET,
>> +	.color_key			 = DC_OVERLAY_COLOR_KEY,
>> +	.color_key_high			= DC_OVERLAY_COLOR_KEY_HIGH,
>> +	.clear_value		 = DC_OVERLAY_CLEAR_VALUE,
>> +	.color_table_index	 = DC_OVERLAY_COLOR_TABLE_INDEX,
>> +	.color_table_data	 = DC_OVERLAY_COLOR_TABLE_DATA,
>> +	.scale_config		 = DC_OVERLAY_SCALE_CONFIG,
>> +	.water_mark				= DC_OVERLAY_WATER_MARK,
>> +	.degamma_index		 = DC_OVERLAY_DEGAMMA_INDEX,
>> +	.degamma_data		 = DC_OVERLAY_DEGAMMA_DATA,
>> +	.degamma_ex_data	 = DC_OVERLAY_DEGAMMA_EX_DATA,
>> +	.src_global_color	 = DC_OVERLAY_SRC_GLOBAL_COLOR,
>> +	.dst_global_color	 = DC_OVERLAY_DST_GLOBAL_COLOR,
>> +	.blend_config		 = DC_OVERLAY_BLEND_CONFIG,
>> +	.roi_origin				= DC_OVERLAY_ROI_ORIGIN,
>> +	.roi_size				= DC_OVERLAY_ROI_SIZE,
>> +	.yuv_to_rgb_coef0		 = DC_OVERLAY_YUVTORGB_COEF0,
>> +	.yuv_to_rgb_coef1		 = DC_OVERLAY_YUVTORGB_COEF1,
>> +	.yuv_to_rgb_coef2		 = DC_OVERLAY_YUVTORGB_COEF2,
>> +	.yuv_to_rgb_coef3		 = DC_OVERLAY_YUVTORGB_COEF3,
>> +	.yuv_to_rgb_coef4			= DC_OVERLAY_YUVTORGB_COEF4,
>> +	.yuv_to_rgb_coefd0			= DC_OVERLAY_YUVTORGB_COEFD0,
>> +	.yuv_to_rgb_coefd1			= DC_OVERLAY_YUVTORGB_COEFD1,
>> +	.yuv_to_rgb_coefd2			= DC_OVERLAY_YUVTORGB_COEFD2,
>> +	.y_clamp_bound		 = DC_OVERLAY_Y_CLAMP_BOUND,
>> +	.uv_clamp_bound		 = DC_OVERLAY_UV_CLAMP_BOUND,
>> +	.rgb_to_rgb_coef0		 = DC_OVERLAY_RGBTORGB_COEF0,
>> +	.rgb_to_rgb_coef1		 = DC_OVERLAY_RGBTORGB_COEF1,
>> +	.rgb_to_rgb_coef2		 = DC_OVERLAY_RGBTORGB_COEF2,
>> +	.rgb_to_rgb_coef3		 = DC_OVERLAY_RGBTORGB_COEF3,
>> +	.rgb_to_rgb_coef4		 = DC_OVERLAY_RGBTORGB_COEF4,
>> +	},
>> +};
>> +
>> +static const u32 primary_overlay_format0[] = {
>> +	DRM_FORMAT_XRGB4444,
>> +	DRM_FORMAT_XBGR4444,
>> +	DRM_FORMAT_RGBX4444,
>> +	DRM_FORMAT_BGRX4444,
>> +	DRM_FORMAT_ARGB4444,
>> +	DRM_FORMAT_ABGR4444,
>> +	DRM_FORMAT_RGBA4444,
>> +	DRM_FORMAT_BGRA4444,
>> +	DRM_FORMAT_XRGB1555,
>> +	DRM_FORMAT_XBGR1555,
>> +	DRM_FORMAT_RGBX5551,
>> +	DRM_FORMAT_BGRX5551,
>> +	DRM_FORMAT_ARGB1555,
>> +	DRM_FORMAT_ABGR1555,
>> +	DRM_FORMAT_RGBA5551,
>> +	DRM_FORMAT_BGRA5551,
>> +	DRM_FORMAT_RGB565,
>> +	DRM_FORMAT_BGR565,
>> +	DRM_FORMAT_XRGB8888,
>> +	DRM_FORMAT_XBGR8888,
>> +	DRM_FORMAT_RGBX8888,
>> +	DRM_FORMAT_BGRX8888,
>> +	DRM_FORMAT_ARGB8888,
>> +	DRM_FORMAT_ABGR8888,
>> +	DRM_FORMAT_RGBA8888,
>> +	DRM_FORMAT_BGRA8888,
>> +	DRM_FORMAT_ARGB2101010,
>> +	DRM_FORMAT_ABGR2101010,
>> +	DRM_FORMAT_RGBA1010102,
>> +	DRM_FORMAT_BGRA1010102,
>> +	DRM_FORMAT_YUYV,
>> +	DRM_FORMAT_YVYU,
>> +	DRM_FORMAT_UYVY,
>> +	DRM_FORMAT_VYUY,
>> +	DRM_FORMAT_YVU420,
>> +	DRM_FORMAT_YUV420,
>> +	DRM_FORMAT_NV12,
>> +	DRM_FORMAT_NV21,
>> +	DRM_FORMAT_NV16,
>> +	DRM_FORMAT_NV61,
>> +	DRM_FORMAT_P010,
>> +};
>> +
>> +static const u32 primary_overlay_format1[] = {
>> +	DRM_FORMAT_XRGB8888,
>> +	DRM_FORMAT_XBGR8888,
>> +	DRM_FORMAT_RGBX8888,
>> +	DRM_FORMAT_BGRX8888,
>> +	DRM_FORMAT_ARGB8888,
>> +	DRM_FORMAT_ABGR8888,
>> +	DRM_FORMAT_RGBA8888,
>> +	DRM_FORMAT_BGRA8888,
>> +	DRM_FORMAT_ARGB2101010,
>> +	DRM_FORMAT_ABGR2101010,
>> +	DRM_FORMAT_RGBA1010102,
>> +	DRM_FORMAT_BGRA1010102,
>> +	DRM_FORMAT_NV12,
>> +	DRM_FORMAT_NV21,
>> +	DRM_FORMAT_YUV444,
>> +};
>> +
>> +static const u32 cursor_formats[] = {
>> +	DRM_FORMAT_ARGB8888
>> +};
>> +
>> +static const u64 format_modifier0[] = {
>> +	DRM_FORMAT_MOD_LINEAR,
>> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8),
>> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8),
>> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X8),
>> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X4),
>> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4),
>> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8),
>> +	DRM_FORMAT_MOD_INVALID
>> +};
>> +
>> +static const u64 secondary_format_modifiers[] = {
>> +	DRM_FORMAT_MOD_LINEAR,
>> +	DRM_FORMAT_MOD_INVALID
>> +};
>> +
>> +#define FRAC_16_16(mult, div)	 (((mult) << 16) / (div))
>> +
>> +static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = {
>> +	{
>> +		/* DC_REV_0 */
>> +		{
>> +		.name			= "Primary",
> 
> I'd say that the indentation is strange here.
> It might be more scalable to split a single array into per-generation 
> data structures and reference them directly from the info struct.
> 
>> +		.id				= PRIMARY_PLANE_0,
>> +		.type			= DRM_PLANE_TYPE_PRIMARY,
>> +		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
>> +		.formats		= primary_overlay_format0,
>> +		.num_modifiers	= ARRAY_SIZE(format_modifier0),
>> +		.modifiers		= format_modifier0,
>> +		.min_width		= 0,
>> +		.min_height		= 0,
>> +		.max_width		= 4096,
>> +		.max_height		= 4096,
>> +		.rotation		= DRM_MODE_ROTATE_0 |
>> +							DRM_MODE_ROTATE_90 |
>> +							DRM_MODE_ROTATE_180 |
>> +							DRM_MODE_ROTATE_270 |
>> +							DRM_MODE_REFLECT_X |
>> +							DRM_MODE_REFLECT_Y,
>> +		.blend_mode		= BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +							BIT(DRM_MODE_BLEND_PREMULTI) |
>> +							BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 0,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name			= "Overlay",
>> +		.id				= OVERLAY_PLANE_0,
>> +		.type			= DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
>> +		.formats		= primary_overlay_format0,
>> +		.num_modifiers	= ARRAY_SIZE(format_modifier0),
>> +		.modifiers		= format_modifier0,
>> +		.min_width		= 0,
>> +		.min_height		= 0,
>> +		.max_width		= 4096,
>> +		.max_height		= 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 1,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name			= "Overlay_1",
>> +		.id				= OVERLAY_PLANE_1,
>> +		.type			= DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
>> +		.formats		= primary_overlay_format0,
>> +		.num_modifiers	= ARRAY_SIZE(secondary_format_modifiers),
>> +		.modifiers		= secondary_format_modifiers,
>> +		.min_width		= 0,
>> +		.min_height		= 0,
>> +		.max_width		= 4096,
>> +		.max_height		= 4096,
>> +		.rotation		= 0,
>> +		.blend_mode		= BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +							BIT(DRM_MODE_BLEND_PREMULTI) |
>> +							BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 2,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Primary_1",
>> +		.id				= PRIMARY_PLANE_1,
>> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
>> +		.formats	 = primary_overlay_format0,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 3,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay_2",
>> +		.id				= OVERLAY_PLANE_2,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
>> +		.formats	 = primary_overlay_format0,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 4,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay_3",
>> +		.id			= OVERLAY_PLANE_3,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
>> +		.formats	 = primary_overlay_format0,
>> +		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
>> +		.modifiers	 = secondary_format_modifiers,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = 0,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 5,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Cursor",
>> +		.id				= CURSOR_PLANE_0,
>> +		.type		 = DRM_PLANE_TYPE_CURSOR,
>> +		.num_formats = ARRAY_SIZE(cursor_formats),
>> +		.formats	 = cursor_formats,
>> +		.num_modifiers = 0,
>> +		.modifiers	 = NULL,
>> +		.min_width	 = 32,
>> +		.min_height  = 32,
>> +		.max_width	 = 64,
>> +		.max_height  = 64,
>> +		.rotation	 = 0,
>> +		.degamma_size = 0,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 255,
>> +		.watermark	 = false,
>> +		.color_mgmt  = false,
>> +		.roi		 = false,
>> +		},
>> +		{
>> +		.name		 = "Cursor_1",
>> +		.id				= CURSOR_PLANE_1,
>> +		.type		 = DRM_PLANE_TYPE_CURSOR,
>> +		.num_formats = ARRAY_SIZE(cursor_formats),
>> +		.formats	 = cursor_formats,
>> +		.num_modifiers = 0,
>> +		.modifiers	 = NULL,
>> +		.min_width	 = 32,
>> +		.min_height  = 32,
>> +		.max_width	 = 64,
>> +		.max_height  = 64,
>> +		.rotation	 = 0,
>> +		.degamma_size = 0,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 255,
>> +		.watermark	 = false,
>> +		.color_mgmt  = false,
>> +		.roi		 = false,
>> +		},
>> +	},
>> +	{
>> +		/* DC_REV_1 */
>> +		{
>> +		.name		 = "Primary",
>> +		.id				= PRIMARY_PLANE_0,
>> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
>> +		.formats	 = primary_overlay_format0,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 0,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay",
>> +		.id				= OVERLAY_PLANE_0,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
>> +		.formats	 = primary_overlay_format0,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 1,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Primary_1",
>> +		.id				= PRIMARY_PLANE_1,
>> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
>> +		.formats	 = primary_overlay_format0,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 2,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay_2",
>> +		.id				= OVERLAY_PLANE_2,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
>> +		.formats	 = primary_overlay_format0,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 3,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Cursor",
>> +		.id				= CURSOR_PLANE_0,
>> +		.type		 = DRM_PLANE_TYPE_CURSOR,
>> +		.num_formats = ARRAY_SIZE(cursor_formats),
>> +		.formats	 = cursor_formats,
>> +		.num_modifiers = 0,
>> +		.modifiers	 = NULL,
>> +		.min_width	 = 32,
>> +		.min_height  = 32,
>> +		.max_width	 = 64,
>> +		.max_height  = 64,
>> +		.rotation	 = 0,
>> +		.degamma_size = 0,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 255,
>> +		.watermark	 = false,
>> +		.color_mgmt  = false,
>> +		.roi		 = false,
>> +		},
>> +		{
>> +		.name		 = "Cursor_1",
>> +		.id				= CURSOR_PLANE_1,
>> +		.type		 = DRM_PLANE_TYPE_CURSOR,
>> +		.num_formats = ARRAY_SIZE(cursor_formats),
>> +		.formats	 = cursor_formats,
>> +		.num_modifiers = 0,
>> +		.modifiers	 = NULL,
>> +		.min_width	 = 32,
>> +		.min_height  = 32,
>> +		.max_width	 = 64,
>> +		.max_height  = 64,
>> +		.rotation	 = 0,
>> +		.degamma_size = 0,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 255,
>> +		.watermark	 = false,
>> +		.color_mgmt  = false,
>> +		.roi		 = false,
>> +		},
>> +	},
>> +	{
>> +		/* DC_REV_2 */
>> +		{
>> +		.name		 = "Primary",
>> +		.id				= PRIMARY_PLANE_0,
>> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
>> +		.formats	 = primary_overlay_format1,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 0,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay",
>> +		.id				= OVERLAY_PLANE_0,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
>> +		.formats	 = primary_overlay_format1,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 1,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay_1",
>> +		.id				= OVERLAY_PLANE_1,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
>> +		.formats	 = primary_overlay_format1,
>> +		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
>> +		.modifiers	 = secondary_format_modifiers,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = 0,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 2,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Primary_1",
>> +		.id				= PRIMARY_PLANE_1,
>> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
>> +		.formats	 = primary_overlay_format1,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 3,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay_2",
>> +		.id				= OVERLAY_PLANE_2,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
>> +		.formats	 = primary_overlay_format1,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 4,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay_3",
>> +		.id				= OVERLAY_PLANE_3,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
>> +		.formats	 = primary_overlay_format1,
>> +		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
>> +		.modifiers	 = secondary_format_modifiers,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = 0,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 5,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Cursor",
>> +		.id				= CURSOR_PLANE_0,
>> +		.type		 = DRM_PLANE_TYPE_CURSOR,
>> +		.num_formats = ARRAY_SIZE(cursor_formats),
>> +		.formats	 = cursor_formats,
>> +		.num_modifiers = 0,
>> +		.modifiers	 = NULL,
>> +		.min_width	 = 32,
>> +		.min_height  = 32,
>> +		.max_width	 = 64,
>> +		.max_height  = 64,
>> +		.rotation	 = 0,
>> +		.degamma_size = 0,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 255,
>> +		.watermark	 = false,
>> +		.color_mgmt  = false,
>> +		.roi		 = false,
>> +		},
>> +		{
>> +		.name		 = "Cursor_1",
>> +		.id				= CURSOR_PLANE_1,
>> +		.type		 = DRM_PLANE_TYPE_CURSOR,
>> +		.num_formats = ARRAY_SIZE(cursor_formats),
>> +		.formats	 = cursor_formats,
>> +		.num_modifiers = 0,
>> +		.modifiers	 = NULL,
>> +		.min_width	 = 32,
>> +		.min_height  = 32,
>> +		.max_width	 = 64,
>> +		.max_height  = 64,
>> +		.rotation	 = 0,
>> +		.degamma_size = 0,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 255,
>> +		.watermark	 = false,
>> +		.color_mgmt  = false,
>> +		.roi		 = false,
>> +		},
>> +	},
>> +};
>> +
>> +static const struct vs_dc_info dc_info[] = {
> 
> Again, please split into per-generation instances.
> 
>> +	{
>> +		/* DC_REV_0 */
>> +		.name			= "DC8200",
>> +		.panel_num		= 2,
>> +		.plane_num		= 8,
>> +		.planes			= dc_hw_planes[DC_REV_0],
>> +		.layer_num		= 6,
>> +		.max_bpc		= 10,
>> +		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
>> +						  DRM_COLOR_FORMAT_YCBCR444 |
>> +						  DRM_COLOR_FORMAT_YCBCR422 |
>> +						  DRM_COLOR_FORMAT_YCBCR420,
>> +		.gamma_size		= GAMMA_EX_SIZE,
>> +		.gamma_bits		= 12,
>> +		.pitch_alignment	= 128,
>> +		.pipe_sync		= false,
>> +		.background		= true,
>> +		.panel_sync		= true,
>> +		.cap_dec		= true,
>> +	},
>> +	{
>> +		/* DC_REV_1 */
>> +		.name			= "DC8200",
>> +		.panel_num		= 2,
>> +		.plane_num		= 6,
>> +		.planes			= dc_hw_planes[DC_REV_1],
>> +		.layer_num		= 4,
>> +		.max_bpc		= 10,
>> +		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
>> +						  DRM_COLOR_FORMAT_YCBCR444 |
>> +						  DRM_COLOR_FORMAT_YCBCR422 |
>> +						  DRM_COLOR_FORMAT_YCBCR420,
>> +		.gamma_size		= GAMMA_EX_SIZE,
>> +		.gamma_bits		= 12,
>> +		.pitch_alignment	= 128,
>> +		.pipe_sync		= false,
>> +		.background		= true,
>> +		.panel_sync		= true,
>> +		.cap_dec		= true,
>> +	},
>> +	{
>> +		/* DC_REV_2 */
>> +		.name			= "DC8200",
>> +		.panel_num		= 2,
>> +		.plane_num		= 8,
>> +		.planes			= dc_hw_planes[DC_REV_2],
>> +		.layer_num		= 6,
>> +		.max_bpc		= 10,
>> +		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
>> +						  DRM_COLOR_FORMAT_YCBCR444 |
>> +						  DRM_COLOR_FORMAT_YCBCR422 |
>> +						  DRM_COLOR_FORMAT_YCBCR420,
>> +		.gamma_size		= GAMMA_EX_SIZE,
>> +		.gamma_bits		= 12,
>> +		.pitch_alignment	= 128,
>> +		.pipe_sync		= false,
>> +		.background		= true,
>> +		.panel_sync		= true,
>> +		.cap_dec		= false,
>> +	},
>> +};
>> +
>> +static const struct dc_hw_funcs hw_func;
>> +
>> +static inline u32 hi_read(struct dc_hw *hw, u32 reg)
>> +{
>> +	return readl(hw->hi_base + reg);
>> +}
>> +
>> +static inline void hi_write(struct dc_hw *hw, u32 reg, u32 value)
>> +{
>> +	writel(value, hw->hi_base + reg);
>> +}
>> +
>> +static inline void dc_write(struct dc_hw *hw, u32 reg, u32 value)
>> +{
>> +	writel(value, hw->reg_base + reg - DC_REG_BASE);
>> +}
>> +
>> +static inline u32 dc_read(struct dc_hw *hw, u32 reg)
>> +{
>> +	u32 value = readl(hw->reg_base + reg - DC_REG_BASE);
>> +
>> +	return value;
>> +}
>> +
>> +static inline void dc_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear)
>> +{
>> +	u32 value = dc_read(hw, reg);
>> +
>> +	value &= ~clear;
>> +	value |= set;
>> +	dc_write(hw, reg, value);
>> +}
>> +
>> +static void load_default_filter(struct dc_hw *hw,
>> +				const struct dc_hw_plane_reg *reg, u32 offset)
>> +{
>> +	u8 i;
>> +
>> +	dc_write(hw, reg->scale_config + offset, 0x33);
>> +	dc_write(hw, reg->init_offset + offset, 0x80008000);
>> +	dc_write(hw, reg->h_filter_coef_index + offset, 0x00);
>> +	for (i = 0; i < H_COEF_SIZE; i++)
>> +		dc_write(hw, reg->h_filter_coef_data + offset, horkernel[i]);
>> +
>> +	dc_write(hw, reg->v_filter_coef_index + offset, 0x00);
>> +	for (i = 0; i < V_COEF_SIZE; i++)
>> +		dc_write(hw, reg->v_filter_coef_data + offset, verkernel[i]);
>> +}
>> +
>> +static void load_rgb_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
>> +			    u32 offset, u16 *table)
>> +{
>> +	dc_write(hw, reg->rgb_to_rgb_coef0 + offset, table[0] | (table[1] << 16));
>> +	dc_write(hw, reg->rgb_to_rgb_coef1 + offset, table[2] | (table[3] << 16));
>> +	dc_write(hw, reg->rgb_to_rgb_coef2 + offset, table[4] | (table[5] << 16));
>> +	dc_write(hw, reg->rgb_to_rgb_coef3 + offset, table[6] | (table[7] << 16));
>> +	dc_write(hw, reg->rgb_to_rgb_coef4 + offset, table[8]);
>> +}
>> +
>> +static void load_yuv_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
>> +			    u32 offset, s32 *table)
>> +{
>> +	dc_write(hw, reg->yuv_to_rgb_coef0 + offset,
>> +		 (0xFFFF & table[0]) | (table[1] << 16));
>> +	dc_write(hw, reg->yuv_to_rgb_coef1 + offset,
>> +		 (0xFFFF & table[2]) | (table[3] << 16));
>> +	dc_write(hw, reg->yuv_to_rgb_coef2 + offset,
>> +		 (0xFFFF & table[4]) | (table[5] << 16));
>> +	dc_write(hw, reg->yuv_to_rgb_coef3 + offset,
>> +		 (0xFFFF & table[6]) | (table[7] << 16));
>> +	dc_write(hw, reg->yuv_to_rgb_coef4 + offset, table[8]);
>> +	dc_write(hw, reg->yuv_to_rgb_coefd0 + offset, table[9]);
>> +	dc_write(hw, reg->yuv_to_rgb_coefd1 + offset, table[10]);
>> +	dc_write(hw, reg->yuv_to_rgb_coefd2 + offset, table[11]);
>> +	dc_write(hw, reg->y_clamp_bound + offset, table[12] | (table[13] << 16));
>> +	dc_write(hw, reg->uv_clamp_bound + offset, table[14] | (table[15] << 16));
>> +}
>> +
>> +static void load_rgb_to_yuv(struct dc_hw *hw, u32 offset, s16 *table)
>> +{
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF0 + offset,
>> +		 table[0] | (table[1] << 16));
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF1 + offset,
>> +		 table[2] | (table[3] << 16));
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF2 + offset,
>> +		 table[4] | (table[5] << 16));
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF3 + offset,
>> +		 table[6] | (table[7] << 16));
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF4 + offset, table[8]);
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD0 + offset, table[9]);
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD1 + offset, table[10]);
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD2 + offset, table[11]);
>> +}
>> +
>> +static bool is_rgb(enum dc_hw_color_format format)
>> +{
>> +	switch (format) {
>> +	case FORMAT_X4R4G4B4:
>> +	case FORMAT_A4R4G4B4:
>> +	case FORMAT_X1R5G5B5:
>> +	case FORMAT_A1R5G5B5:
>> +	case FORMAT_R5G6B5:
>> +	case FORMAT_X8R8G8B8:
>> +	case FORMAT_A8R8G8B8:
>> +	case FORMAT_A2R10G10B10:
>> +		return true;
>> +	default:
>> +		return false;
>> +	}
>> +}
>> +
>> +static void load_degamma_table(struct dc_hw *hw,
>> +			       const struct dc_hw_plane_reg *reg,
>> +			       u32 offset, u16 *table)
>> +{
>> +	u16 i;
>> +	u32 value;
>> +
>> +	dc_write(hw, reg->degamma_index + offset, 0);
>> +
>> +	for (i = 0; i < DEGAMMA_SIZE; i++) {
>> +		value = table[i] | (table[i] << 16);
>> +		dc_write(hw, reg->degamma_data + offset, value);
>> +		dc_write(hw, reg->degamma_ex_data + offset, table[i]);
>> +	}
>> +}
>> +
>> +static u32 get_addr_offset(u32 id)
>> +{
>> +	u32 offset = 0;
>> +
>> +	switch (id) {
>> +	case PRIMARY_PLANE_1:
>> +	case OVERLAY_PLANE_1:
>> +		offset = 0x04;
>> +		break;
>> +	case OVERLAY_PLANE_2:
>> +		offset = 0x08;
>> +		break;
>> +	case OVERLAY_PLANE_3:
>> +		offset = 0x0C;
>> +		break;
>> +	default:
>> +		break;
>> +	}
>> +
>> +	return offset;
>> +}
>> +
>> +int dc_hw_init(struct dc_hw *hw)
>> +{
>> +	u8 i, id, panel_num, layer_num;
>> +	u32 offset;
>> +	u32 revision = hi_read(hw, DC_HW_REVISION);
>> +	u32 cid = hi_read(hw, DC_HW_CHIP_CID);
>> +	const struct dc_hw_plane_reg *reg;
>> +
>> +	switch (revision) {
>> +	case 0x5720:
>> +		hw->rev = DC_REV_0;
>> +		break;
>> +	case 0x5721:
>> +		switch (cid) {
>> +		case 0x30B:
>> +			hw->rev = DC_REV_1;
>> +			break;
>> +		case 0x310:
>> +			hw->rev = DC_REV_2;
>> +			break;
>> +		default:
>> +			hw->rev = DC_REV_0;
>> +			break;
>> +		}
>> +		break;
>> +	default:
>> +		return -ENXIO;
>> +	}
> 
> We (drm/msm) had code like this for a while (and autodetection of the HW 
> ID). And nowadays we are migrating to of_device_get_match_data and 
> per-SoC compat names. It is just easier to handle, it allows one to 
> specify device more precisely and also it simplifies DT schema.
> 
>> +
>> +	hw->info = (struct vs_dc_info *)&dc_info[hw->rev];
>> +	hw->func = (struct dc_hw_funcs *)&hw_func;
>> +
>> +	layer_num = hw->info->layer_num;
>> +	for (i = 0; i < layer_num; i++) {
>> +		id = hw->info->planes[i].id;
>> +		offset = get_addr_offset(id);
>> +		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
>> +			reg = &dc_plane_reg[0];
>> +		else
>> +			reg = &dc_plane_reg[1];
>> +
>> +		load_default_filter(hw, reg, offset);
>> +		load_rgb_to_rgb(hw, reg, offset, RGB2RGB);
>> +	}
>> +
>> +	panel_num = hw->info->panel_num;
>> +	for (i = 0; i < panel_num; i++) {
>> +		offset = i << 2;
>> +
>> +		load_rgb_to_yuv(hw, offset, RGB2YUV);
>> +		dc_write(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0x111);
>> +
>> +		offset = i ? DC_CURSOR_OFFSET : 0;
>> +		dc_write(hw, DC_CURSOR_BACKGROUND + offset, 0x00FFFFFF);
>> +		dc_write(hw, DC_CURSOR_FOREGROUND + offset, 0x00AAAAAA);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +void dc_hw_deinit(struct dc_hw *hw)
>> +{
>> +	/* Nothing to do */
>> +}
>> +
>> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
>> +			struct dc_hw_fb *fb, struct dc_hw_scale *scale,
>> +			struct dc_hw_position *pos, struct dc_hw_blend *blend)
>> +{
>> +	struct dc_hw_plane *plane = &hw->plane[id];
>> +
>> +	if (plane) {
>> +		if (fb) {
>> +			if (!fb->enable)
>> +				plane->fb.enable = false;
>> +			else
>> +				memcpy(&plane->fb, fb,
>> +				       sizeof(*fb) - sizeof(fb->dirty));
>> +			plane->fb.dirty = true;
>> +		}
>> +		if (scale) {
>> +			memcpy(&plane->scale, scale,
>> +			       sizeof(*scale) - sizeof(scale->dirty));
>> +			plane->scale.dirty = true;
>> +		}
>> +		if (pos) {
>> +			memcpy(&plane->pos, pos,
>> +			       sizeof(*pos) - sizeof(pos->dirty));
>> +			plane->pos.dirty = true;
>> +		}
>> +		if (blend) {
>> +			memcpy(&plane->blend, blend,
>> +			       sizeof(*blend) - sizeof(blend->dirty));
>> +			plane->blend.dirty = true;
>> +		}
>> +	}
>> +}
>> +
>> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode)
>> +{
>> +	struct dc_hw_plane *plane = &hw->plane[id];
>> +
>> +	if (plane) {
>> +		if (hw->info->planes[id].degamma_size) {
>> +			plane->degamma.mode = mode;
>> +			plane->degamma.dirty = true;
>> +		} else {
>> +			plane->degamma.dirty = false;
>> +		}
>> +	}
>> +}
>> +
>> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi)
>> +{
>> +	struct dc_hw_plane *plane = &hw->plane[id];
>> +
>> +	if (plane) {
>> +		memcpy(&plane->roi, roi, sizeof(*roi) - sizeof(roi->dirty));
>> +		plane->roi.dirty = true;
>> +	}
>> +}
>> +
>> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
>> +			   struct dc_hw_colorkey *colorkey)
>> +{
>> +	struct dc_hw_plane *plane = &hw->plane[id];
>> +
>> +	if (plane) {
>> +		memcpy(&plane->colorkey, colorkey,
>> +		       sizeof(*colorkey) - sizeof(colorkey->dirty));
>> +		plane->colorkey.dirty = true;
>> +	}
>> +}
>> +
>> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos)
>> +{
>> +	memcpy(&hw->qos, qos, sizeof(*qos) - sizeof(qos->dirty));
>> +	hw->qos.dirty = true;
>> +}
>> +
>> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor)
>> +{
>> +	memcpy(&hw->cursor[id], cursor, sizeof(*cursor) - sizeof(cursor->dirty));
>> +	hw->cursor[id].dirty = true;
>> +}
>> +
>> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
>> +			u16 r, u16 g, u16 b)
>> +{
>> +	if (index >= hw->info->gamma_size)
>> +		return;
>> +
>> +	hw->gamma[id].gamma[index][0] = r;
>> +	hw->gamma[id].gamma[index][1] = g;
>> +	hw->gamma[id].gamma[index][2] = b;
>> +	hw->gamma[id].dirty = true;
>> +}
>> +
>> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable)
>> +{
>> +	hw->gamma[id].enable = enable;
>> +	hw->gamma[id].dirty = true;
>> +}
>> +
>> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display)
>> +{
>> +	u8 id = display->id;
>> +
>> +	memcpy(&hw->display[id], display, sizeof(*display));
>> +
>> +	hw->func->display(hw, display);
>> +}
>> +
>> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable)
>> +{
>> +	if (enable)
>> +		hi_write(hw, AQ_INTR_ENBL, 0xFFFFFFFF);
>> +	else
>> +		hi_write(hw, AQ_INTR_ENBL, 0);
>> +}
>> +
>> +u32 dc_hw_get_interrupt(struct dc_hw *hw)
>> +{
>> +	return hi_read(hw, AQ_INTR_ACKNOWLEDGE);
>> +}
>> +
>> +bool dc_hw_check_underflow(struct dc_hw *hw)
>> +{
>> +	return dc_read(hw, DC_FRAMEBUFFER_CONFIG) & BIT(5);
>> +}
>> +
>> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable)
>> +{
>> +	u32 i, offset;
>> +	u8 id, layer_num = hw->info->layer_num;
>> +	u8 panel_num = hw->info->panel_num;
>> +
>> +	for (i = 0; i < layer_num; i++) {
>> +		id = hw->info->planes[i].id;
>> +		offset = get_addr_offset(id);
>> +		if (enable) {
>> +			if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
>> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, BIT(12), 0);
>> +			else
>> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, BIT(31), 0);
>> +		} else {
>> +			if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
>> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, 0, BIT(12));
>> +			else
>> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, 0, BIT(31));
>> +		}
>> +	}
>> +
>> +	for (i = 0; i < panel_num; i++) {
>> +		offset = i << 2;
>> +		if (enable)
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, BIT(0));
>> +		else
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, BIT(0), 0);
>> +	}
>> +}
>> +
>> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id)
>> +{
>> +	if (out <= OUT_DP)
>> +		hw->out[id] = out;
>> +}
>> +
>> +static void gamma_ex_commit(struct dc_hw *hw)
>> +{
>> +	u8 panel_num = hw->info->panel_num;
>> +	u16 i, j;
>> +	u32 value;
>> +
>> +	for (j = 0; j < panel_num; j++) {
>> +		if (hw->gamma[j].dirty) {
>> +			if (hw->gamma[j].enable) {
>> +				dc_write(hw, DC_DISPLAY_GAMMA_EX_INDEX + (j << 2), 0x00);
>> +				for (i = 0; i < GAMMA_EX_SIZE; i++) {
>> +					value = hw->gamma[j].gamma[i][2] |
>> +						(hw->gamma[j].gamma[i][1] << 12);
>> +					dc_write(hw, DC_DISPLAY_GAMMA_EX_DATA + (j << 2), value);
>> +					dc_write(hw, DC_DISPLAY_GAMMA_EX_ONE_DATA + (j << 2),
>> +						 hw->gamma[j].gamma[i][0]);
>> +				}
>> +				dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
>> +					     BIT(13), 0);
>> +			} else {
>> +				dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
>> +					     0, BIT(13));
>> +			}
>> +			hw->gamma[j].dirty = false;
>> +		}
>> +	}
>> +}
>> +
>> +static void plane_commit(struct dc_hw *hw)
>> +{
>> +	struct dc_hw_plane *plane;
>> +	const struct dc_hw_plane_reg *reg;
>> +	bool primary = false;
>> +	u8 id, layer_num = hw->info->layer_num;
>> +	u32 i, offset;
>> +
>> +	for (i = 0; i < layer_num; i++) {
>> +		plane = &hw->plane[i];
>> +		id = hw->info->planes[i].id;
>> +		offset = get_addr_offset(id);
>> +		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
>> +			reg = &dc_plane_reg[0];
>> +			primary = true;
>> +		} else {
>> +			reg = &dc_plane_reg[1];
>> +			primary = false;
>> +		}
> 
> Can you please move register defines to some internal hw_plane 
> structure. These details are known at the time of creation of the plane. 
> There is no need to determine them again and again.
> 
>> +
>> +		if (plane->fb.dirty) {
>> +			if (plane->fb.enable) {
> 
> if (plane->fb.dirty && plane->fb.enable) will save you one level of 
> indentation.
> 
> However it might be better to use existing DRM flags instead of 
> inventing your own flags.
> 
>> +				dc_write(hw, reg->y_address + offset,
>> +					 plane->fb.y_address);
>> +				dc_write(hw, reg->u_address + offset,
>> +					 plane->fb.u_address);
>> +				dc_write(hw, reg->v_address + offset,
>> +					 plane->fb.v_address);
>> +				dc_write(hw, reg->y_stride + offset,
>> +					 plane->fb.y_stride);
>> +				dc_write(hw, reg->u_stride + offset,
>> +					 plane->fb.u_stride);
>> +				dc_write(hw, reg->v_stride + offset,
>> +					 plane->fb.v_stride);
>> +				dc_write(hw, reg->size + offset,
>> +					 plane->fb.width |
>> +					 (plane->fb.height << 15));
>> +				dc_write(hw, reg->water_mark + offset,
>> +					 plane->fb.water_mark);
>> +
>> +				if (plane->fb.clear_enable)
>> +					dc_write(hw, reg->clear_value + offset,
>> +						 plane->fb.clear_value);
>> +			}
>> +
>> +			if (primary) {
> 
> You have two kinds of planes. Primary and overlays. Could you please 
> split this code into two separate functions and define two vtables, one 
> for primary planes and one for overlays. There is no need to stick 
> everything into a single funcion that handles everything.
> 
>> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
>> +					     (plane->fb.format << 26) |
>> +					     (plane->fb.uv_swizzle << 25) |
>> +					     (plane->fb.swizzle << 23) |
>> +					     (plane->fb.tile_mode << 17) |
>> +					     (plane->fb.yuv_color_space << 14) |
>> +					     (plane->fb.rotation << 11) |
>> +					     (plane->fb.clear_enable << 8),
>> +					     (0x1F << 26) |
>> +					     BIT(25) |
>> +					     (0x03 << 23) |
>> +					     (0x1F << 17) |
>> +					     (0x07 << 14) |
>> +					     (0x07 << 11) |
>> +					     BIT(8));
>> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +					     (plane->fb.dec_enable << 1) |
>> +					     (plane->fb.enable << 13) |
>> +					     (plane->fb.zpos << 16) |
>> +					     (plane->fb.display_id << 19),
>> +					     BIT(1) | BIT(13) | (0x07 << 16) | BIT(19));
> 
> It looks like this code can enjoy defining names for all the magic 
> values. Otherwise it is impossible to support it.
> 
>> +			} else {
>> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> +					     (plane->fb.dec_enable << 27) |
>> +					     (plane->fb.clear_enable << 25) |
>> +					     (plane->fb.enable << 24) |
>> +					     (plane->fb.format << 16) |
>> +					     (plane->fb.uv_swizzle << 15) |
>> +					     (plane->fb.swizzle << 13) |
>> +					     (plane->fb.tile_mode << 8) |
>> +					     (plane->fb.yuv_color_space << 5) |
>> +					     (plane->fb.rotation << 2),
>> +					     BIT(27) |
>> +					     BIT(25) |
>> +					     BIT(24) |
>> +					     (0x1F << 16) |
>> +					     BIT(15) |
>> +					     (0x03 << 13) |
>> +					     (0x1F << 8) |
>> +					     (0x07 << 5) |
>> +					     (0x07 << 2));
>> +				dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset,
>> +					     plane->fb.zpos | (plane->fb.display_id << 3),
>> +					     0x07 | BIT(3));
>> +			}
>> +			plane->fb.dirty = false;
>> +		}
>> +
>> +		if (plane->scale.dirty) {
>> +			if (plane->scale.enable) {
>> +				dc_write(hw, reg->scale_factor_x + offset,
>> +					 plane->scale.scale_factor_x);
>> +				dc_write(hw, reg->scale_factor_y + offset,
>> +					 plane->scale.scale_factor_y);
>> +				if (primary)
>> +					dc_set_clear(hw,
>> +						     DC_FRAMEBUFFER_CONFIG + offset,
>> +						     BIT(22), 0);
>> +				else
>> +					dc_set_clear(hw,
>> +						     DC_OVERLAY_SCALE_CONFIG + offset,
>> +						     BIT(8), 0);
>> +			} else {
>> +				if (primary)
>> +					dc_set_clear(hw,
>> +						     DC_FRAMEBUFFER_CONFIG + offset,
>> +						     0, BIT(22));
>> +				else
>> +					dc_set_clear(hw,
>> +						     DC_OVERLAY_SCALE_CONFIG + offset,
>> +						     0, BIT(8));
>> +			}
>> +			plane->scale.dirty = false;
>> +		}
>> +
>> +		if (plane->pos.dirty) {
>> +			dc_write(hw, reg->top_left + offset,
>> +				 plane->pos.start_x |
>> +				 (plane->pos.start_y << 15));
>> +			dc_write(hw, reg->bottom_right + offset,
>> +				 plane->pos.end_x |
>> +				 (plane->pos.end_y << 15));
>> +			plane->pos.dirty = false;
>> +		}
>> +
>> +		if (plane->blend.dirty) {
>> +			dc_write(hw, reg->src_global_color + offset,
>> +				 plane->blend.alpha << 24);
>> +			dc_write(hw, reg->dst_global_color + offset,
>> +				 plane->blend.alpha << 24);
>> +			switch (plane->blend.blend_mode) {
>> +			case BLEND_PREMULTI:
>> +				dc_write(hw, reg->blend_config + offset, 0x3450);
>> +				break;
>> +			case BLEND_COVERAGE:
>> +				dc_write(hw, reg->blend_config + offset, 0x3950);
>> +				break;
>> +			case BLEND_PIXEL_NONE:
>> +				dc_write(hw, reg->blend_config + offset, 0x3548);
>> +				break;
>> +			default:
>> +				break;
>> +			}
>> +			plane->blend.dirty = false;
>> +		}
>> +
>> +		if (plane->colorkey.dirty) {
>> +			dc_write(hw, reg->color_key + offset, plane->colorkey.colorkey);
>> +			dc_write(hw, reg->color_key_high + offset,
>> +				 plane->colorkey.colorkey_high);
>> +
>> +			if (primary)
>> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
>> +					     plane->colorkey.transparency << 9, 0x03 << 9);
>> +			else
>> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> +					     plane->colorkey.transparency, 0x03);
>> +
>> +			plane->colorkey.dirty = false;
>> +		}
>> +
>> +		if (plane->roi.dirty) {
>> +			if (plane->roi.enable) {
>> +				dc_write(hw, reg->roi_origin + offset,
>> +					 plane->roi.x | (plane->roi.y << 16));
>> +				dc_write(hw, reg->roi_size + offset,
>> +					 plane->roi.width | (plane->roi.height << 16));
>> +				if (primary)
>> +					dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +						     BIT(0), 0);
>> +				else
>> +					dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> +						     BIT(22), 0);
>> +			} else {
>> +				if (primary)
>> +					dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +						     0, BIT(0));
>> +				else
>> +					dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> +						     0, BIT(22));
>> +			}
>> +			plane->roi.dirty = false;
>> +		}
>> +	}
>> +}
>> +
>> +static void plane_ex_commit(struct dc_hw *hw)
>> +{
>> +	struct dc_hw_plane *plane;
>> +	const struct dc_hw_plane_reg *reg;
>> +	bool primary = false;
>> +	u8 id, layer_num = hw->info->layer_num;
>> +	u32 i, offset;
>> +
>> +	for (i = 0; i < layer_num; i++) {
>> +		plane = &hw->plane[i];
>> +		id = hw->info->planes[i].id;
>> +		offset = get_addr_offset(id);
>> +		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
>> +			reg = &dc_plane_reg[0];
>> +			primary = true;
>> +		} else {
>> +			reg = &dc_plane_reg[1];
>> +			primary = false;
>> +		}
>> +
>> +		if (plane->fb.dirty) {
>> +			if (is_rgb(plane->fb.format)) {
>> +				if (primary)
>> +					dc_set_clear(hw,
>> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +						     BIT(6), BIT(8));
>> +				else
>> +					dc_set_clear(hw,
>> +						     DC_OVERLAY_CONFIG + offset,
>> +						     BIT(29), BIT(30));
>> +			} else {
>> +				if (primary)
>> +					dc_set_clear(hw,
>> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +						     BIT(8), BIT(6));
>> +				else
>> +					dc_set_clear(hw,
>> +						     DC_OVERLAY_CONFIG + offset,
>> +						     BIT(30), BIT(29));
>> +				switch (plane->fb.yuv_color_space) {
>> +				case COLOR_SPACE_601:
>> +					load_yuv_to_rgb(hw, reg, offset, YUV601_2RGB);
>> +					break;
>> +				case COLOR_SPACE_709:
>> +					load_yuv_to_rgb(hw, reg, offset, YUV709_2RGB);
>> +					break;
>> +				case COLOR_SPACE_2020:
>> +					load_yuv_to_rgb(hw, reg, offset, YUV2020_2RGB);
>> +					break;
>> +				default:
>> +					break;
>> +				}
>> +			}
>> +		}
>> +		if (plane->degamma.dirty) {
>> +			switch (plane->degamma.mode) {
>> +			case VS_DEGAMMA_DISABLE:
>> +				if (primary)
>> +					dc_set_clear(hw,
>> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +						     0, BIT(5));
>> +				else
>> +					dc_set_clear(hw,
>> +						     DC_OVERLAY_CONFIG + offset,
>> +						     0, BIT(28));
>> +				break;
>> +			case VS_DEGAMMA_BT709:
>> +				load_degamma_table(hw, reg, offset, DEGAMMA_709);
>> +				if (primary)
>> +					dc_set_clear(hw,
>> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +						     BIT(5), 0);
>> +				else
>> +					dc_set_clear(hw,
>> +						     DC_OVERLAY_CONFIG + offset,
>> +						     BIT(28), 0);
>> +				break;
>> +			case VS_DEGAMMA_BT2020:
>> +				load_degamma_table(hw, reg, offset, DEGAMMA_2020);
>> +				if (primary)
>> +					dc_set_clear(hw,
>> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +						     BIT(5), 0);
>> +				else
>> +					dc_set_clear(hw,
>> +						     DC_OVERLAY_CONFIG + offset,
>> +						     BIT(28), 0);
>> +				break;
>> +			default:
>> +				break;
>> +			}
>> +			plane->degamma.dirty = false;
>> +		}
>> +	}
>> +	plane_commit(hw);
>> +}
>> +
>> +static void setup_display(struct dc_hw *hw, struct dc_hw_display *display)
>> +{
>> +	u8 id = display->id;
>> +	u32 dpi_cfg, offset = id << 2;
>> +
>> +	if (hw->display[id].enable) {
>> +		switch (display->bus_format) {
>> +		case MEDIA_BUS_FMT_RGB565_1X16:
>> +			dpi_cfg = 0;
>> +			break;
>> +		case MEDIA_BUS_FMT_RGB666_1X18:
>> +			dpi_cfg = 3;
>> +			break;
>> +		case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
>> +			dpi_cfg = 4;
>> +			break;
>> +		case MEDIA_BUS_FMT_RGB888_1X24:
>> +			dpi_cfg = 5;
>> +			break;
>> +		case MEDIA_BUS_FMT_RGB101010_1X30:
>> +			dpi_cfg = 6;
>> +			break;
>> +		default:
>> +			dpi_cfg = 5;
>> +			break;
>> +		}
>> +		dc_write(hw, DC_DISPLAY_DPI_CONFIG + offset, dpi_cfg);
>> +
>> +		if (id == 0)
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
>> +		else
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
>> +
>> +		dc_write(hw, DC_DISPLAY_H + offset, hw->display[id].h_active |
>> +			 (hw->display[id].h_total << 16));
>> +		dc_write(hw, DC_DISPLAY_H_SYNC + offset,
>> +			 hw->display[id].h_sync_start |
>> +			 (hw->display[id].h_sync_end << 15) |
>> +			 (hw->display[id].h_sync_polarity ? 0 : BIT(31)) |
>> +			 BIT(30));
>> +		dc_write(hw, DC_DISPLAY_V + offset, hw->display[id].v_active |
>> +				(hw->display[id].v_total << 16));
>> +		dc_write(hw, DC_DISPLAY_V_SYNC + offset,
>> +			 hw->display[id].v_sync_start |
>> +			 (hw->display[id].v_sync_end << 15) |
>> +			 (hw->display[id].v_sync_polarity ? 0 : BIT(31)) |
>> +			 BIT(30));
>> +
>> +		if (hw->info->pipe_sync)
>> +			dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
>> +				     0, BIT(3) | BIT(4));
>> +
>> +		dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(12), 0);
>> +		if (id == 0)
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(0), BIT(3));
>> +		else
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(1), BIT(3));
>> +	} else {
>> +		dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(12));
>> +		if (id == 0)
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
>> +		else
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
>> +	}
>> +}
>> +
>> +static void setup_display_ex(struct dc_hw *hw, struct dc_hw_display *display)
>> +{
>> +	u8 id = display->id;
>> +	u32 dp_cfg, offset = id << 2;
>> +	bool is_yuv = false;
>> +
>> +	if (hw->display[id].enable && hw->out[id] == OUT_DP) {
>> +		switch (display->bus_format) {
>> +		case MEDIA_BUS_FMT_RGB565_1X16:
>> +			dp_cfg = 0;
>> +			break;
>> +		case MEDIA_BUS_FMT_RGB666_1X18:
>> +			dp_cfg = 1;
>> +			break;
>> +		case MEDIA_BUS_FMT_RGB888_1X24:
>> +			dp_cfg = 2;
>> +			break;
>> +		case MEDIA_BUS_FMT_RGB101010_1X30:
>> +			dp_cfg = 3;
>> +			break;
>> +		case MEDIA_BUS_FMT_UYVY8_1X16:
>> +			dp_cfg = 2 << 4;
>> +			is_yuv = true;
>> +			break;
>> +		case MEDIA_BUS_FMT_YUV8_1X24:
>> +			dp_cfg = 4 << 4;
>> +			is_yuv = true;
>> +			break;
>> +		case MEDIA_BUS_FMT_UYVY10_1X20:
>> +			dp_cfg = 8 << 4;
>> +			is_yuv = true;
>> +			break;
>> +		case MEDIA_BUS_FMT_YUV10_1X30:
>> +			dp_cfg = 10 << 4;
>> +			is_yuv = true;
>> +			break;
>> +		case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
>> +			dp_cfg = 12 << 4;
>> +			is_yuv = true;
>> +			break;
>> +		case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
>> +			dp_cfg = 13 << 4;
>> +			is_yuv = true;
>> +			break;
>> +		default:
>> +			dp_cfg = 2;
>> +			break;
>> +		}
>> +		if (is_yuv)
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(16), 0);
>> +		else
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(16));
>> +		dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | BIT(3));
>> +	}
>> +
>> +	if (hw->out[id] == OUT_DPI)
>> +		dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, BIT(3));
>> +
>> +	setup_display(hw, display);
>> +}
>> +
>> +static const struct dc_hw_funcs hw_func = {
>> +	.gamma = &gamma_ex_commit,
>> +	.plane = &plane_ex_commit,
>> +	.display = setup_display_ex,
>> +};
>> +
>> +void dc_hw_commit(struct dc_hw *hw)
>> +{
>> +	u32 i, offset = 0;
>> +	u8 plane_num = hw->info->plane_num;
>> +	u8 layer_num = hw->info->layer_num;
>> +	u8 cursor_num = plane_num - layer_num;
>> +
>> +	hw->func->gamma(hw);
>> +	hw->func->plane(hw);
>> +
>> +	for (i = 0; i < cursor_num; i++) {
>> +		if (hw->cursor[i].dirty) {
>> +			offset = hw->cursor[i].display_id ? DC_CURSOR_OFFSET : 0;
>> +			if (hw->cursor[i].enable) {
>> +				dc_write(hw, DC_CURSOR_ADDRESS + offset,
>> +					 hw->cursor[i].address);
>> +				dc_write(hw, DC_CURSOR_LOCATION + offset, hw->cursor[i].x |
>> +					 (hw->cursor[i].y << 16));
>> +				dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
>> +					     (hw->cursor[i].hot_x << 16) |
>> +					     (hw->cursor[i].hot_y << 8) |
>> +					     (hw->cursor[i].size << 5) |
>> +					     BIT(3) | BIT(2) | 0x02,
>> +					     (0xFF << 16) |
>> +					     (0xFF << 8) |
>> +					     (0x07 << 5) | 0x1F);
>> +			} else {
>> +				dc_set_clear(hw, DC_CURSOR_CONFIG + offset, BIT(3), 0x03);
>> +			}
>> +			hw->cursor[i].dirty = false;
>> +		}
>> +	}
>> +
>> +	if (hw->qos.dirty) {
>> +		dc_set_clear(hw, DC_QOS_CONFIG, (hw->qos.high_value << 4) |
>> +					 hw->qos.low_value, 0xFF);
>> +		hw->qos.dirty = false;
>> +	}
>> +}
>> +
>> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.h b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
>> new file mode 100644
>> index 000000000..9a1d767ae
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
>> @@ -0,0 +1,492 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#ifndef __VS_DC_HW_H__
>> +#define __VS_DC_HW_H__
>> +
>> +#define AQ_INTR_ACKNOWLEDGE				0x0010
>> +#define AQ_INTR_ENBL					0x0014
> 
> Please take care to align all the defines.
> 
>> +#define DC_HW_REVISION					0x0024
>> +#define DC_HW_CHIP_CID					0x0030
>> +
>> +#define DC_REG_BASE						0x0800
>> +#define DC_REG_RANGE					0x2000
>> +#define DC_SEC_REG_OFFSET				0x100000
>> +
>> +#define DC_FRAMEBUFFER_CONFIG			0x1518
>> +#define DC_FRAMEBUFFER_CONFIG_EX		0x1CC0
>> +#define DC_FRAMEBUFFER_SCALE_CONFIG		0x1520
>> +#define DC_FRAMEBUFFER_TOP_LEFT			0x24D8
>> +#define DC_FRAMEBUFFER_BOTTOM_RIGHT		0x24E0
>> +#define DC_FRAMEBUFFER_ADDRESS			0x1400
>> +#define DC_FRAMEBUFFER_U_ADDRESS		0x1530
>> +#define DC_FRAMEBUFFER_V_ADDRESS		0x1538
>> +#define DC_FRAMEBUFFER_STRIDE			0x1408
>> +#define DC_FRAMEBUFFER_U_STRIDE			0x1800
>> +#define DC_FRAMEBUFFER_V_STRIDE			0x1808
>> +#define DC_FRAMEBUFFER_SIZE				0x1810
>> +#define DC_FRAMEBUFFER_SCALE_FACTOR_X		0x1828
>> +#define DC_FRAMEBUFFER_SCALE_FACTOR_Y		0x1830
>> +#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX	0x1838
>> +#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA	0x1A00
>> +#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX	0x1A08
>> +#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA	0x1A10
>> +#define DC_FRAMEBUFFER_INIT_OFFSET			0x1A20
>> +#define DC_FRAMEBUFFER_COLOR_KEY			0x1508
>> +#define DC_FRAMEBUFFER_COLOR_KEY_HIGH		0x1510
>> +#define DC_FRAMEBUFFER_CLEAR_VALUE			0x1A18
>> +#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX	0x1818
>> +#define DC_FRAMEBUFFER_COLOR_TABLE_DATA		0x1820
>> +#define DC_FRAMEBUFFER_BG_COLOR				0x1528
>> +#define DC_FRAMEBUFFER_ROI_ORIGIN			0x1CB0
>> +#define DC_FRAMEBUFFER_ROI_SIZE				0x1CB8
>> +#define DC_FRAMEBUFFER_WATER_MARK			0x1CE8
>> +#define DC_FRAMEBUFFER_DEGAMMA_INDEX		0x1D88
>> +#define DC_FRAMEBUFFER_DEGAMMA_DATA			0x1D90
>> +#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA		0x1D98
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEF0		0x1DA0
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEF1		0x1DA8
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEF2		0x1DB0
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEF3		0x1DB8
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEF4		0x1E00
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD0		0x1E08
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD1		0x1E10
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD2		0x1E18
>> +#define DC_FRAMEBUFFER_Y_CLAMP_BOUND		0x1E88
>> +#define DC_FRAMEBUFFER_UV_CLAMP_BOUND		0x1E90
>> +#define DC_FRAMEBUFFER_RGBTORGB_COEF0		0x1E20
>> +#define DC_FRAMEBUFFER_RGBTORGB_COEF1		0x1E28
>> +#define DC_FRAMEBUFFER_RGBTORGB_COEF2		0x1E30
>> +#define DC_FRAMEBUFFER_RGBTORGB_COEF3		0x1E38
>> +#define DC_FRAMEBUFFER_RGBTORGB_COEF4		0x1E40
>> +#define DC_FRAMEBUFFER_BLEND_CONFIG			0x2510
>> +#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR		0x2500
>> +#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR		0x2508
>> +
>> +#define DC_OVERLAY_CONFIG					0x1540
>> +#define DC_OVERLAY_CONFIG_EX				0x2540
>> +#define DC_OVERLAY_SCALE_CONFIG				0x1C00
>> +#define DC_OVERLAY_BLEND_CONFIG				0x1580
>> +#define DC_OVERLAY_TOP_LEFT					0x1640
>> +#define DC_OVERLAY_BOTTOM_RIGHT				0x1680
>> +#define DC_OVERLAY_ADDRESS					0x15C0
>> +#define DC_OVERLAY_U_ADDRESS				0x1840
>> +#define DC_OVERLAY_V_ADDRESS				0x1880
>> +#define DC_OVERLAY_STRIDE					0x1600
>> +#define DC_OVERLAY_U_STRIDE					0x18C0
>> +#define DC_OVERLAY_V_STRIDE					0x1900
>> +#define DC_OVERLAY_SIZE						0x17C0
>> +#define DC_OVERLAY_SCALE_FACTOR_X			0x1A40
>> +#define DC_OVERLAY_SCALE_FACTOR_Y			0x1A80
>> +#define DC_OVERLAY_H_FILTER_COEF_INDEX		0x1AC0
>> +#define DC_OVERLAY_H_FILTER_COEF_DATA		0x1B00
>> +#define DC_OVERLAY_V_FILTER_COEF_INDEX		0x1B40
>> +#define DC_OVERLAY_V_FILTER_COEF_DATA		0x1B80
>> +#define DC_OVERLAY_INIT_OFFSET				0x1BC0
>> +#define DC_OVERLAY_COLOR_KEY				0x1740
>> +#define DC_OVERLAY_COLOR_KEY_HIGH			0x1780
>> +#define DC_OVERLAY_CLEAR_VALUE				0x1940
>> +#define DC_OVERLAY_COLOR_TABLE_INDEX		0x1980
>> +#define DC_OVERLAY_COLOR_TABLE_DATA			0x19C0
>> +#define DC_OVERLAY_SRC_GLOBAL_COLOR			0x16C0
>> +#define DC_OVERLAY_DST_GLOBAL_COLOR			0x1700
>> +#define DC_OVERLAY_ROI_ORIGIN				0x1D00
>> +#define DC_OVERLAY_ROI_SIZE					0x1D40
>> +#define DC_OVERLAY_WATER_MARK				0x1DC0
>> +#define DC_OVERLAY_DEGAMMA_INDEX			0x2200
>> +#define DC_OVERLAY_DEGAMMA_DATA				0x2240
>> +#define DC_OVERLAY_DEGAMMA_EX_DATA			0x2280
>> +#define DC_OVERLAY_YUVTORGB_COEF0			0x1EC0
>> +#define DC_OVERLAY_YUVTORGB_COEF1			0x1F00
>> +#define DC_OVERLAY_YUVTORGB_COEF2			0x1F40
>> +#define DC_OVERLAY_YUVTORGB_COEF3			0x1F80
>> +#define DC_OVERLAY_YUVTORGB_COEF4			0x1FC0
>> +#define DC_OVERLAY_YUVTORGB_COEFD0			0x2000
>> +#define DC_OVERLAY_YUVTORGB_COEFD1			0x2040
>> +#define DC_OVERLAY_YUVTORGB_COEFD2			0x2080
>> +#define DC_OVERLAY_Y_CLAMP_BOUND			0x22C0
>> +#define DC_OVERLAY_UV_CLAMP_BOUND			0x2300
>> +#define DC_OVERLAY_RGBTORGB_COEF0			0x20C0
>> +#define DC_OVERLAY_RGBTORGB_COEF1			0x2100
>> +#define DC_OVERLAY_RGBTORGB_COEF2			0x2140
>> +#define DC_OVERLAY_RGBTORGB_COEF3			0x2180
>> +#define DC_OVERLAY_RGBTORGB_COEF4			0x21C0
>> +
>> +#define DC_CURSOR_CONFIG					0x1468
>> +#define DC_CURSOR_ADDRESS					0x146C
>> +#define DC_CURSOR_LOCATION					0x1470
>> +#define DC_CURSOR_BACKGROUND				0x1474
>> +#define DC_CURSOR_FOREGROUND				0x1478
>> +#define DC_CURSOR_CLK_GATING				0x1484
>> +#define DC_CURSOR_CONFIG_EX					0x24E8
>> +#define DC_CURSOR_OFFSET					0x1080
>> +
>> +#define DC_DISPLAY_DITHER_CONFIG		0x1410
>> +#define DC_DISPLAY_PANEL_CONFIG			0x1418
>> +#define DC_DISPLAY_PANEL_CONFIG_EX		0x2518
>> +#define DC_DISPLAY_DITHER_TABLE_LOW		0x1420
>> +#define DC_DISPLAY_DITHER_TABLE_HIGH	0x1428
>> +#define DC_DISPLAY_H					0x1430
>> +#define DC_DISPLAY_H_SYNC				0x1438
>> +#define DC_DISPLAY_V					0x1440
>> +#define DC_DISPLAY_V_SYNC				0x1448
>> +#define DC_DISPLAY_CURRENT_LOCATION		0x1450
>> +#define DC_DISPLAY_GAMMA_INDEX			0x1458
>> +#define DC_DISPLAY_GAMMA_DATA			0x1460
>> +#define DC_DISPLAY_INT					0x147C
>> +#define DC_DISPLAY_INT_ENABLE			0x1480
>> +#define DC_DISPLAY_DBI_CONFIG			0x1488
>> +#define DC_DISPLAY_GENERAL_CONFIG		0x14B0
>> +#define DC_DISPLAY_DPI_CONFIG			0x14B8
>> +#define DC_DISPLAY_PANEL_START			0x1CCC
>> +#define DC_DISPLAY_DEBUG_COUNTER_SELECT		0x14D0
>> +#define DC_DISPLAY_DEBUG_COUNTER_VALUE		0x14D8
>> +#define DC_DISPLAY_DP_CONFIG				0x1CD0
>> +#define DC_DISPLAY_GAMMA_EX_INDEX			0x1CF0
>> +#define DC_DISPLAY_GAMMA_EX_DATA			0x1CF8
>> +#define DC_DISPLAY_GAMMA_EX_ONE_DATA		0x1D80
>> +#define DC_DISPLAY_RGBTOYUV_COEF0			0x1E48
>> +#define DC_DISPLAY_RGBTOYUV_COEF1			0x1E50
>> +#define DC_DISPLAY_RGBTOYUV_COEF2			0x1E58
>> +#define DC_DISPLAY_RGBTOYUV_COEF3			0x1E60
>> +#define DC_DISPLAY_RGBTOYUV_COEF4			0x1E68
>> +#define DC_DISPLAY_RGBTOYUV_COEFD0			0x1E70
>> +#define DC_DISPLAY_RGBTOYUV_COEFD1			0x1E78
>> +#define DC_DISPLAY_RGBTOYUV_COEFD2			0x1E80
>> +
>> +#define DC_CLK_GATTING					0x1A28
>> +#define DC_QOS_CONFIG					0x1A38
>> +
>> +#define DC_TRANSPARENCY_OPAQUE			0x00
>> +#define DC_TRANSPARENCY_KEY				0x02
>> +#define DC_DISPLAY_DITHERTABLE_LOW		0x7B48F3C0
>> +#define DC_DISPLAY_DITHERTABLE_HIGH		0x596AD1E2
>> +
>> +#define GAMMA_SIZE						256
>> +#define GAMMA_EX_SIZE					300
>> +#define DEGAMMA_SIZE					260
>> +
>> +#define RGB_TO_RGB_TABLE_SIZE			9
>> +#define YUV_TO_RGB_TABLE_SIZE			16
>> +#define RGB_TO_YUV_TABLE_SIZE			12
>> +
>> +#define DC_LAYER_NUM	6
>> +#define DC_DISPLAY_NUM	2
>> +#define DC_CURSOR_NUM	2
>> +
>> +#define DC_TILE_MODE4X4	0x15
>> +
>> +enum dc_chip_rev {
>> +	DC_REV_0,/* For HW_REV_5720,HW_REV_5721_311 */
>> +	DC_REV_1,/* For HW_REV_5721_30B */
>> +	DC_REV_2,/* For HW_REV_5721_310 */
>> +};
>> +
>> +enum dc_hw_plane_id {
>> +	PRIMARY_PLANE_0,
>> +	OVERLAY_PLANE_0,
>> +	OVERLAY_PLANE_1,
>> +	PRIMARY_PLANE_1,
>> +	OVERLAY_PLANE_2,
>> +	OVERLAY_PLANE_3,
>> +	CURSOR_PLANE_0,
>> +	CURSOR_PLANE_1,
>> +	PLANE_NUM
>> +};
>> +
>> +enum dc_hw_color_format {
>> +	FORMAT_X4R4G4B4,//0
> 
> Space after the comma will make it more readable. Or just define the 
> values explicitly, it will also be more readable.
> 
>> +	FORMAT_A4R4G4B4,//1
>> +	FORMAT_X1R5G5B5,//2
>> +	FORMAT_A1R5G5B5,//3
>> +	FORMAT_R5G6B5,//4
>> +	FORMAT_X8R8G8B8,//5
>> +	FORMAT_A8R8G8B8,//6
>> +	FORMAT_YUY2,//7
>> +	FORMAT_UYVY,//8
>> +	FORMAT_INDEX8,//9
>> +	FORMAT_MONOCHROME,//10
>> +	FORMAT_YV12 = 0xf,
>> +	FORMAT_A8,//16
>> +	FORMAT_NV12,//17
>> +	FORMAT_NV16,//18
>> +	FORMAT_RG16,//19
>> +	FORMAT_R8,//20
>> +	FORMAT_NV12_10BIT,//21
>> +	FORMAT_A2R10G10B10,//22
>> +	FORMAT_NV16_10BIT,//23
>> +	FORMAT_INDEX1,//24
>> +	FORMAT_INDEX2,//25
>> +	FORMAT_INDEX4,//26
>> +	FORMAT_P010,//27
>> +	FORMAT_YUV444,//28
>> +	FORMAT_YUV444_10BIT,//29
>> +};
>> +
>> +enum dc_hw_yuv_color_space {
>> +	COLOR_SPACE_601 = 0,
>> +	COLOR_SPACE_709 = 1,
>> +	COLOR_SPACE_2020 = 3,
>> +};
>> +
>> +enum dc_hw_rotation {
>> +	ROT_0 = 0,
>> +	ROT_90 = 4,
>> +	ROT_180 = 5,
>> +	ROT_270 = 6,
>> +	FLIP_X = 1,
>> +	FLIP_Y = 2,
>> +	FLIP_XY = 3,
>> +};
>> +
>> +enum dc_hw_swizzle {
>> +	SWIZZLE_ARGB = 0,
>> +	SWIZZLE_RGBA,
>> +	SWIZZLE_ABGR,
>> +	SWIZZLE_BGRA,
>> +};
>> +
>> +enum dc_hw_out {
>> +	OUT_DPI,
>> +	OUT_DP,
>> +};
>> +
>> +enum dc_hw_cursor_size {
>> +	CURSOR_SIZE_32X32 = 0,
>> +	CURSOR_SIZE_64X64,
>> +};
>> +
>> +enum dc_hw_blend_mode {
>> +	/* out.rgb = plane_alpha * fg.rgb +
>> +	 *		(1 - (plane_alpha * fg.alpha)) * bg.rgb
>> +	 */
>> +	BLEND_PREMULTI,
>> +	/* out.rgb = plane_alpha * fg.alpha * fg.rgb +
>> +	 *		(1 - (plane_alpha * fg.alpha)) * bg.rgb
>> +	 */
>> +	BLEND_COVERAGE,
>> +	/* out.rgb = plane_alpha * fg.rgb +
>> +	 *		(1 - plane_alpha) * bg.rgb
>> +	 */
>> +	BLEND_PIXEL_NONE,
> 
> Can you use defines from <drm/drm_blend.h>?
> 
>> +};
>> +
>> +struct dc_hw_plane_reg {
>> +	u32 y_address;
>> +	u32 u_address;
>> +	u32 v_address;
>> +	u32 y_stride;
>> +	u32 u_stride;
>> +	u32 v_stride;
>> +	u32 size;
>> +	u32 top_left;
>> +	u32 bottom_right;
>> +	u32 scale_factor_x;
>> +	u32 scale_factor_y;
>> +	u32 h_filter_coef_index;
>> +	u32 h_filter_coef_data;
>> +	u32 v_filter_coef_index;
>> +	u32 v_filter_coef_data;
>> +	u32 init_offset;
>> +	u32 color_key;
>> +	u32 color_key_high;
>> +	u32 clear_value;
>> +	u32 color_table_index;
>> +	u32 color_table_data;
>> +	u32 scale_config;
>> +	u32 water_mark;
>> +	u32 degamma_index;
>> +	u32 degamma_data;
>> +	u32 degamma_ex_data;
>> +	u32 src_global_color;
>> +	u32 dst_global_color;
>> +	u32 blend_config;
>> +	u32 roi_origin;
>> +	u32 roi_size;
>> +	u32 yuv_to_rgb_coef0;
>> +	u32 yuv_to_rgb_coef1;
>> +	u32 yuv_to_rgb_coef2;
>> +	u32 yuv_to_rgb_coef3;
>> +	u32 yuv_to_rgb_coef4;
>> +	u32 yuv_to_rgb_coefd0;
>> +	u32 yuv_to_rgb_coefd1;
>> +	u32 yuv_to_rgb_coefd2;
>> +	u32 y_clamp_bound;
>> +	u32 uv_clamp_bound;
>> +	u32 rgb_to_rgb_coef0;
>> +	u32 rgb_to_rgb_coef1;
>> +	u32 rgb_to_rgb_coef2;
>> +	u32 rgb_to_rgb_coef3;
>> +	u32 rgb_to_rgb_coef4;
>> +};
>> +
>> +struct dc_hw_fb {
>> +	u32 y_address;
>> +	u32 u_address;
>> +	u32 v_address;
>> +	u32 clear_value;
>> +	u32 water_mark;
>> +	u16 y_stride;
>> +	u16 u_stride;
>> +	u16 v_stride;
>> +	u16 width;
>> +	u16 height;
>> +	u8	format;
>> +	u8	tile_mode;
>> +	u8	rotation;
>> +	u8	yuv_color_space;
>> +	u8	swizzle;
>> +	u8	uv_swizzle;
>> +	u8	zpos;
>> +	u8	display_id;
>> +	bool	clear_enable;
>> +	bool	dec_enable;
>> +	bool	enable;
>> +	bool	dirty;
>> +};
>> +
>> +struct dc_hw_scale {
>> +	u32 scale_factor_x;
>> +	u32 scale_factor_y;
>> +	bool	enable;
>> +	bool	dirty;
>> +};
>> +
>> +struct dc_hw_position {
>> +	u16 start_x;
>> +	u16 start_y;
>> +	u16 end_x;
>> +	u16 end_y;
>> +	bool	dirty;
>> +};
>> +
>> +struct dc_hw_blend {
>> +	u8	alpha;
>> +	u8	blend_mode;
>> +	bool	dirty;
>> +};
>> +
>> +struct dc_hw_colorkey {
>> +	u32 colorkey;
>> +	u32 colorkey_high;
>> +	u8	transparency;
>> +	bool dirty;
>> +};
>> +
>> +struct dc_hw_roi {
>> +	u16 x;
>> +	u16 y;
>> +	u16 width;
>> +	u16 height;
>> +	bool enable;
>> +	bool dirty;
>> +};
>> +
>> +struct dc_hw_cursor {
>> +	u32 address;
>> +	u16 x;
>> +	u16 y;
>> +	u16 hot_x;
>> +	u16 hot_y;
>> +	u8	size;
>> +	u8	display_id;
>> +	bool	enable;
>> +	bool	dirty;
>> +};
>> +
>> +struct dc_hw_display {
>> +	u32 bus_format;
>> +	u16 h_active;
>> +	u16 h_total;
>> +	u16 h_sync_start;
>> +	u16 h_sync_end;
>> +	u16 v_active;
>> +	u16 v_total;
>> +	u16 v_sync_start;
>> +	u16 v_sync_end;
>> +	u8	id;
>> +	bool	h_sync_polarity;
>> +	bool	v_sync_polarity;
>> +	bool	enable;
>> +};
>> +
>> +struct dc_hw_gamma {
>> +	u16 gamma[GAMMA_EX_SIZE][3];
>> +	bool	enable;
>> +	bool	dirty;
>> +};
>> +
>> +struct dc_hw_degamma {
>> +	u16 degamma[DEGAMMA_SIZE][3];
>> +	u32 mode;
>> +	bool	dirty;
>> +};
>> +
>> +struct dc_hw_plane {
>> +	struct dc_hw_fb			fb;
>> +	struct dc_hw_position	pos;
>> +	struct dc_hw_scale		scale;
>> +	struct dc_hw_blend		blend;
>> +	struct dc_hw_roi		roi;
>> +	struct dc_hw_colorkey	colorkey;
>> +	struct dc_hw_degamma	degamma;
>> +};
>> +
>> +struct dc_hw_qos {
>> +	u8	  low_value;
>> +	u8	  high_value;
>> +	bool  dirty;
>> +};
>> +
>> +struct dc_hw_read {
>> +	u32			reg;
>> +	u32			value;
>> +};
>> +
>> +struct dc_hw;
>> +struct dc_hw_funcs {
>> +	void (*gamma)(struct dc_hw *hw);
>> +	void (*plane)(struct dc_hw *hw);
>> +	void (*display)(struct dc_hw *hw, struct dc_hw_display *display);
>> +};
>> +
>> +struct dc_hw {
>> +	enum dc_chip_rev rev;
>> +	enum dc_hw_out		out[DC_DISPLAY_NUM];
>> +	void			*hi_base;
>> +	void			*reg_base;
>> +
>> +	struct dc_hw_display display[DC_DISPLAY_NUM];
>> +	struct dc_hw_gamma	 gamma[DC_DISPLAY_NUM];
>> +	struct dc_hw_plane	 plane[DC_LAYER_NUM];
>> +	struct dc_hw_cursor  cursor[DC_CURSOR_NUM];
>> +	struct dc_hw_qos	 qos;
>> +	struct dc_hw_funcs	 *func;
>> +	struct vs_dc_info	 *info;
>> +};
>> +
>> +int dc_hw_init(struct dc_hw *hw);
>> +void dc_hw_deinit(struct dc_hw *hw);
>> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
>> +			struct dc_hw_fb *fb, struct dc_hw_scale *scale,
>> +			struct dc_hw_position *pos, struct dc_hw_blend *blend);
>> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode);
>> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi);
>> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
>> +			   struct dc_hw_colorkey *colorkey);
>> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos);
>> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor);
>> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
>> +			u16 r, u16 g, u16 b);
>> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable);
>> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display);
>> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable);
>> +u32 dc_hw_get_interrupt(struct dc_hw *hw);
>> +bool dc_hw_check_underflow(struct dc_hw *hw);
>> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable);
>> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id);
>> +void dc_hw_commit(struct dc_hw *hw);
>> +
>> +#endif /* __VS_DC_HW_H__ */
>> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
>> index da7698c3d..3cd533cfa 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
>> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
>> @@ -21,6 +21,7 @@
>>   
>>   #include "vs_drv.h"
>>   #include "vs_modeset.h"
>> +#include "vs_dc.h"
>>   
>>   #define DRV_NAME	"verisilicon"
>>   #define DRV_DESC	"Verisilicon DRM driver"
>> @@ -123,6 +124,7 @@ static const struct component_master_ops vs_drm_ops = {
>>   };
>>   
>>   static struct platform_driver *drm_sub_drivers[] = {
>> +	&dc_platform_driver,
>>   
>>   };
>>   
>> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c
>> new file mode 100644
>> index 000000000..9bd066015
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_plane.c
>> @@ -0,0 +1,526 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#include <drm/drm_atomic.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_blend.h>
>> +#include <drm/drm_gem_dma_helper.h>
>> +#include <drm/drm_fb_dma_helper.h>
>> +#include <drm/drm_framebuffer.h>
>> +#include <drm/drm_plane.h>
>> +#include <drm/drm_plane_helper.h>
>> +
>> +#include <drm/vs_drm.h>
>> +
>> +#include "vs_plane.h"
>> +#include "vs_drv.h"
>> +#include "vs_dc.h"
>> +
>> +static void vs_plane_reset(struct drm_plane *plane)
>> +{
>> +	struct vs_plane_state *state;
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +
>> +	if (plane->state) {
>> +		__drm_atomic_helper_plane_destroy_state(plane->state);
>> +
>> +		state = to_vs_plane_state(plane->state);
>> +		kfree(state);
>> +		plane->state = NULL;
>> +	}
>> +
> 
> Use vs_plane_destroy_state directly. This way you won't miss a refcount 
> on blobs.
> 
>> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> +	if (!state)
>> +		return;
>> +
>> +	state->degamma = VS_DEGAMMA_DISABLE;
>> +	state->degamma_changed = false;
>> +	state->base.zpos = vs_plane->id;
>> +	memset(&state->status, 0, sizeof(state->status));
>> +
>> +	__drm_atomic_helper_plane_reset(plane, &state->base);
>> +}
>> +
>> +static void _vs_plane_duplicate_blob(struct vs_plane_state *state,
>> +				     struct vs_plane_state *ori_state)
>> +{
>> +	state->watermark = ori_state->watermark;
>> +	state->color_mgmt = ori_state->color_mgmt;
>> +	state->roi = ori_state->roi;
>> +
>> +	if (state->watermark)
>> +		drm_property_blob_get(state->watermark);
>> +	if (state->color_mgmt)
>> +		drm_property_blob_get(state->color_mgmt);
>> +	if (state->roi)
>> +		drm_property_blob_get(state->roi);
>> +}
>> +
>> +static int
>> +_vs_plane_set_property_blob_from_id(struct drm_device *dev,
>> +				    struct drm_property_blob **blob,
>> +				    u64 blob_id,
>> +				    size_t expected_size)
>> +{
>> +	struct drm_property_blob *new_blob = NULL;
>> +
>> +	if (blob_id) {
>> +		new_blob = drm_property_lookup_blob(dev, blob_id);
>> +		if (!new_blob)
>> +			return -EINVAL;
>> +
>> +		if (new_blob->length != expected_size) {
>> +			drm_property_blob_put(new_blob);
>> +			return -EINVAL;
>> +		}
>> +	}
>> +
>> +	drm_property_replace_blob(blob, new_blob);
>> +	drm_property_blob_put(new_blob);
>> +
>> +	return 0;
>> +}
>> +
>> +static struct drm_plane_state *
>> +vs_plane_atomic_duplicate_state(struct drm_plane *plane)
>> +{
>> +	struct vs_plane_state *ori_state;
>> +	struct vs_plane_state *state;
>> +
>> +	if (WARN_ON(!plane->state))
>> +		return NULL;
>> +
>> +	ori_state = to_vs_plane_state(plane->state);
>> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> +	if (!state)
>> +		return NULL;
>> +
>> +	__drm_atomic_helper_plane_duplicate_state(plane, &state->base);
>> +
>> +	state->degamma = ori_state->degamma;
>> +	state->degamma_changed = ori_state->degamma_changed;
>> +
>> +	_vs_plane_duplicate_blob(state, ori_state);
>> +	memcpy(&state->status, &ori_state->status, sizeof(ori_state->status));
>> +
>> +	return &state->base;
>> +}
>> +
>> +static void vs_plane_atomic_destroy_state(struct drm_plane *plane,
>> +					  struct drm_plane_state *state)
>> +{
>> +	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
>> +
>> +	__drm_atomic_helper_plane_destroy_state(state);
>> +
>> +	drm_property_blob_put(vs_plane_state->watermark);
>> +	drm_property_blob_put(vs_plane_state->color_mgmt);
>> +	drm_property_blob_put(vs_plane_state->roi);
>> +	kfree(vs_plane_state);
>> +}
>> +
>> +static int vs_plane_atomic_set_property(struct drm_plane *plane,
>> +					struct drm_plane_state *state,
>> +					struct drm_property *property,
>> +					uint64_t val)
>> +{
>> +	struct drm_device *dev = plane->dev;
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
>> +	int ret = 0;
>> +
>> +	if (property == vs_plane->degamma_mode) {
>> +		if (vs_plane_state->degamma != val) {
>> +			vs_plane_state->degamma = val;
>> +			vs_plane_state->degamma_changed = true;
>> +		} else {
>> +			vs_plane_state->degamma_changed = false;
>> +		}
>> +	} else if (property == vs_plane->watermark_prop) {
>> +		ret = _vs_plane_set_property_blob_from_id(dev,
>> +							  &vs_plane_state->watermark,
>> +							  val,
>> +							  sizeof(struct drm_vs_watermark));
>> +		return ret;
>> +	} else if (property == vs_plane->color_mgmt_prop) {
>> +		ret = _vs_plane_set_property_blob_from_id(dev,
>> +							  &vs_plane_state->color_mgmt,
>> +							  val,
>> +							  sizeof(struct drm_vs_color_mgmt));
>> +		return ret;
>> +	} else if (property == vs_plane->roi_prop) {
>> +		ret = _vs_plane_set_property_blob_from_id(dev,
>> +							  &vs_plane_state->roi,
>> +							  val,
>> +							  sizeof(struct drm_vs_roi));
>> +		return ret;
>> +	} else {
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int vs_plane_atomic_get_property(struct drm_plane *plane,
>> +					const struct drm_plane_state *state,
>> +					struct drm_property *property,
>> +					uint64_t *val)
>> +{
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +	const struct vs_plane_state *vs_plane_state =
>> +		container_of(state, const struct vs_plane_state, base);
>> +
>> +	if (property == vs_plane->degamma_mode)
>> +		*val = vs_plane_state->degamma;
>> +	else if (property == vs_plane->watermark_prop)
>> +		*val = (vs_plane_state->watermark) ?
>> +					vs_plane_state->watermark->base.id : 0;
>> +	else if (property == vs_plane->color_mgmt_prop)
>> +		*val = (vs_plane_state->color_mgmt) ?
>> +					vs_plane_state->color_mgmt->base.id : 0;
> 
> degamma and color management should use standard properties.
> 
>> +	else if (property == vs_plane->roi_prop)
>> +		*val = (vs_plane_state->roi) ?
>> +					vs_plane_state->roi->base.id : 0;
>> +	else
>> +		return -EINVAL;
>> +
>> +	return 0;
>> +}
>> +
>> +static bool vs_format_mod_supported(struct drm_plane *plane,
>> +				    u32 format,
>> +				    u64 modifier)
>> +{
>> +	int i;
>> +
>> +	/* We always have to allow these modifiers:
>> +	 * 1. Core DRM checks for LINEAR support if userspace does not provide modifiers.
>> +	 * 2. Not passing any modifiers is the same as explicitly passing INVALID.
>> +	 */
>> +	if (modifier == DRM_FORMAT_MOD_LINEAR)
>> +		return true;
>> +
>> +	/* Check that the modifier is on the list of the plane's supported modifiers. */
>> +	for (i = 0; i < plane->modifier_count; i++) {
>> +		if (modifier == plane->modifiers[i])
>> +			break;
>> +	}
>> +
>> +	if (i == plane->modifier_count)
>> +		return false;
>> +
>> +	return true;
>> +}
>> +
>> +const struct drm_plane_funcs vs_plane_funcs = {
>> +	.update_plane		= drm_atomic_helper_update_plane,
>> +	.disable_plane		= drm_atomic_helper_disable_plane,
>> +	.reset			= vs_plane_reset,
>> +	.atomic_duplicate_state = vs_plane_atomic_duplicate_state,
>> +	.atomic_destroy_state	= vs_plane_atomic_destroy_state,
>> +	.atomic_set_property	= vs_plane_atomic_set_property,
>> +	.atomic_get_property	= vs_plane_atomic_get_property,
>> +	.format_mod_supported	= vs_format_mod_supported,
>> +};
>> +
>> +static unsigned char vs_get_plane_number(struct drm_framebuffer *fb)
>> +{
>> +	const struct drm_format_info *info;
>> +
>> +	if (!fb)
>> +		return 0;
>> +
>> +	info = drm_format_info(fb->format->format);
>> +	if (!info || info->num_planes > DRM_FORMAT_MAX_PLANES)
>> +		return 0;
>> +
>> +	return info->num_planes;
>> +}
>> +
>> +static int vs_plane_atomic_check(struct drm_plane *plane,
>> +				 struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> +										 plane);
>> +	unsigned char i, num_planes;
>> +	struct drm_framebuffer *fb = new_plane_state->fb;
>> +	struct drm_crtc *crtc = new_plane_state->crtc;
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
>> +
>> +	if (!crtc || !fb)
>> +		return 0;
>> +
>> +	num_planes = vs_get_plane_number(fb);
>> +
>> +	for (i = 0; i < num_planes; i++) {
>> +		dma_addr_t dma_addr;
>> +
>> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
>> +		plane_state->dma_addr[i] = dma_addr;
>> +	}
>> +
>> +	return vs_dc_check_plane(dc, plane, state);
>> +}
>> +
>> +static int vs_cursor_plane_atomic_check(struct drm_plane *plane,
>> +					struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> +									  plane);
>> +	unsigned char i, num_planes;
>> +	struct drm_framebuffer *fb = new_plane_state->fb;
>> +	struct drm_crtc *crtc = new_plane_state->crtc;
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
>> +
>> +	if (!crtc || !fb)
>> +		return 0;
>> +
>> +	num_planes = vs_get_plane_number(fb);
>> +
>> +	for (i = 0; i < num_planes; i++) {
>> +		dma_addr_t dma_addr;
>> +
>> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
>> +		plane_state->dma_addr[i] = dma_addr;
>> +	}
>> +
>> +	return vs_dc_check_cursor_plane(dc, plane, state);
>> +}
>> +
>> +static void vs_plane_atomic_update(struct drm_plane *plane,
>> +				   struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
>> +									  plane);
> 
> New line after the equal sign will be better.
> 
>> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> +									  plane);
>> +
>> +	unsigned char i, num_planes;
>> +	struct drm_framebuffer *fb;
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	if (!new_state->fb || !new_state->crtc)
>> +		return;
> 
> if (!new_state->visible) ?
> 
>> +
>> +	fb = new_state->fb;
>> +
>> +	drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
>> +
>> +	num_planes = vs_get_plane_number(fb);
>> +
>> +	for (i = 0; i < num_planes; i++) {
>> +		dma_addr_t dma_addr;
>> +
>> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
>> +		plane_state->dma_addr[i] = dma_addr;
>> +	}
>> +
>> +	plane_state->status.src = drm_plane_state_src(new_state);
>> +	plane_state->status.dest = drm_plane_state_dest(new_state);
>> +
>> +	vs_dc_update_plane(dc, vs_plane, plane, state);
>> +}
>> +
>> +static void vs_cursor_plane_atomic_update(struct drm_plane *plane,
>> +					  struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
>> +									   plane);
>> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> +									   plane);
>> +	unsigned char i, num_planes;
>> +	struct drm_framebuffer *fb;
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	if (!new_state->fb || !new_state->crtc)
> 
> and here.
> 
>> +		return;
>> +
>> +	fb = new_state->fb;
>> +	drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
>> +
>> +	num_planes = vs_get_plane_number(fb);
>> +
>> +	for (i = 0; i < num_planes; i++) {
>> +		dma_addr_t dma_addr;
>> +
>> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
>> +		plane_state->dma_addr[i] = dma_addr;
>> +	}
>> +
>> +	plane_state->status.src = drm_plane_state_src(new_state);
>> +	plane_state->status.dest = drm_plane_state_dest(new_state);
>> +
>> +	vs_dc_update_cursor_plane(dc, vs_plane, plane, state);
>> +}
>> +
>> +static void vs_plane_atomic_disable(struct drm_plane *plane,
>> +				    struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> +									   plane);
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	vs_dc_disable_plane(dc, vs_plane, old_state);
>> +}
>> +
>> +static void vs_cursor_plane_atomic_disable(struct drm_plane *plane,
>> +					   struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> +									   plane);
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	vs_dc_disable_cursor_plane(dc, vs_plane, old_state);
>> +}
>> +
>> +const struct drm_plane_helper_funcs primary_plane_helpers = {
>> +	.atomic_check	= vs_plane_atomic_check,
>> +	.atomic_update	= vs_plane_atomic_update,
>> +	.atomic_disable = vs_plane_atomic_disable,
>> +};
>> +
>> +const struct drm_plane_helper_funcs overlay_plane_helpers = {
>> +	.atomic_check	= vs_plane_atomic_check,
>> +	.atomic_update	= vs_plane_atomic_update,
>> +	.atomic_disable = vs_plane_atomic_disable,
>> +};
>> +
>> +const struct drm_plane_helper_funcs cursor_plane_helpers = {
>> +	.atomic_check	= vs_cursor_plane_atomic_check,
>> +	.atomic_update	= vs_cursor_plane_atomic_update,
>> +	.atomic_disable = vs_cursor_plane_atomic_disable,
>> +};
>> +
>> +static const struct drm_prop_enum_list vs_degamma_mode_enum_list[] = {
>> +	{ VS_DEGAMMA_DISABLE,	"disabled" },
>> +	{ VS_DEGAMMA_BT709,	"preset degamma for BT709" },
>> +	{ VS_DEGAMMA_BT2020,	"preset degamma for BT2020" },
>> +};
>> +
>> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
>> +				 struct vs_plane_info *info,
>> +				 unsigned int layer_num,
>> +				 unsigned int possible_crtcs)
>> +{
>> +	struct vs_plane *plane;
>> +	int ret;
>> +
>> +	if (!info)
>> +		return NULL;
>> +
>> +	plane = drmm_universal_plane_alloc(drm_dev, struct vs_plane, base,
>> +					   possible_crtcs,
>> +					   &vs_plane_funcs,
>> +					   info->formats, info->num_formats,
>> +					   info->modifiers, info->type,
>> +					   info->name ? info->name : NULL);
>> +	if (IS_ERR(plane))
>> +		return ERR_CAST(plane);
>> +
>> +	if (info->type == DRM_PLANE_TYPE_PRIMARY)
>> +		drm_plane_helper_add(&plane->base, &primary_plane_helpers);
>> +	else if (info->type == DRM_PLANE_TYPE_CURSOR)
>> +		drm_plane_helper_add(&plane->base, &cursor_plane_helpers);
>> +	else
>> +		drm_plane_helper_add(&plane->base, &overlay_plane_helpers);
>> +
>> +	/* Set up the plane properties */
>> +	if (info->degamma_size) {
>> +		plane->degamma_mode =
>> +		drm_property_create_enum(drm_dev, 0,
>> +					 "DEGAMMA_MODE",
>> +					 vs_degamma_mode_enum_list,
>> +					 ARRAY_SIZE(vs_degamma_mode_enum_list));
>> +
>> +		if (!plane->degamma_mode)
>> +			return NULL;
>> +
>> +		drm_object_attach_property(&plane->base.base,
>> +					   plane->degamma_mode,
>> +					   VS_DEGAMMA_DISABLE);
>> +	}
>> +
>> +	if (info->rotation) {
>> +		ret = drm_plane_create_rotation_property(&plane->base,
>> +							 DRM_MODE_ROTATE_0,
>> +							 info->rotation);
>> +		if (ret)
>> +			return NULL;
>> +	}
>> +
>> +	if (info->blend_mode) {
>> +		ret = drm_plane_create_blend_mode_property(&plane->base,
>> +							   info->blend_mode);
>> +		if (ret)
>> +			return NULL;
>> +		ret = drm_plane_create_alpha_property(&plane->base);
>> +		if (ret)
>> +			return NULL;
>> +	}
>> +
>> +	if (info->color_encoding) {
>> +		ret = drm_plane_create_color_properties(&plane->base,
>> +							info->color_encoding,
>> +							BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
>> +							DRM_COLOR_YCBCR_BT709,
>> +							DRM_COLOR_YCBCR_LIMITED_RANGE);
>> +		if (ret)
>> +			return NULL;
>> +	}
>> +
>> +	if (info->zpos != 255) {
>> +		ret = drm_plane_create_zpos_property(&plane->base, info->zpos, 0,
>> +						     layer_num - 1);
>> +		if (ret)
>> +			return NULL;
>> +	} else {
>> +		ret = drm_plane_create_zpos_immutable_property(&plane->base,
>> +							       info->zpos);
>> +		if (ret)
>> +			return NULL;
>> +	}
>> +
>> +	if (info->watermark) {
>> +		plane->watermark_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
>> +							    "WATERMARK", 0);
>> +		if (!plane->watermark_prop)
>> +			return NULL;
>> +		drm_object_attach_property(&plane->base.base, plane->watermark_prop, 0);
>> +	}
>> +
>> +	if (info->color_mgmt) {
>> +		plane->color_mgmt_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
>> +							     "COLOR_CONFIG", 0);
>> +		if (!plane->color_mgmt_prop)
>> +			return NULL;
>> +
>> +		drm_object_attach_property(&plane->base.base, plane->color_mgmt_prop, 0);
>> +	}
>> +
>> +	if (info->roi) {
>> +		plane->roi_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
>> +						      "ROI", 0);
>> +		if (!plane->roi_prop)
>> +			return NULL;
>> +
>> +		drm_object_attach_property(&plane->base.base, plane->roi_prop, 0);
>> +	}
>> +
>> +	return plane;
>> +}
>> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h
>> new file mode 100644
>> index 000000000..554b74e96
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_plane.h
>> @@ -0,0 +1,58 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#ifndef __VS_PLANE_H__
>> +#define __VS_PLANE_H__
>> +
>> +#include <drm/drm_fourcc.h>
>> +#include <drm/drm_plane_helper.h>
>> +
>> +#include "vs_type.h"
>> +
>> +struct vs_plane_status {
>> +	u32 tile_mode;
>> +	struct drm_rect src;
>> +	struct drm_rect dest;
>> +};
>> +
>> +struct vs_plane_state {
>> +	struct drm_plane_state base;
>> +	struct vs_plane_status status; /* for debugfs */
>> +
>> +	struct drm_property_blob *watermark;
>> +	struct drm_property_blob *color_mgmt;
>> +	struct drm_property_blob *roi;
>> +	dma_addr_t dma_addr[DRM_FORMAT_MAX_PLANES];
>> +
>> +	u32 degamma;
>> +	bool degamma_changed;
>> +};
>> +
>> +struct vs_plane {
>> +	struct drm_plane base;
>> +	u8 id;
>> +
>> +	struct drm_property *degamma_mode;
>> +	struct drm_property *watermark_prop;
>> +	struct drm_property *color_mgmt_prop;
>> +	struct drm_property *roi_prop;
>> +};
>> +
>> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
>> +				 struct vs_plane_info *info,
>> +				 unsigned int layer_num,
>> +				 unsigned int possible_crtcs);
>> +
>> +static inline struct vs_plane *to_vs_plane(struct drm_plane *plane)
>> +{
>> +	return container_of(plane, struct vs_plane, base);
>> +}
>> +
>> +static inline struct vs_plane_state *
>> +to_vs_plane_state(struct drm_plane_state *state)
>> +{
>> +	return container_of(state, struct vs_plane_state, base);
>> +}
>> +#endif /* __VS_PLANE_H__ */
>> diff --git a/drivers/gpu/drm/verisilicon/vs_type.h b/drivers/gpu/drm/verisilicon/vs_type.h
>> new file mode 100644
>> index 000000000..7d3378e29
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_type.h
>> @@ -0,0 +1,69 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#ifndef __VS_TYPE_H__
>> +#define __VS_TYPE_H__
>> +
>> +#include <drm/drm_plane.h>
>> +#include <drm/drm_plane_helper.h>
>> +
>> +struct vs_plane_info {
>> +	const char *name;
>> +	u8 id;
>> +	enum drm_plane_type type;
>> +	unsigned int num_formats;
>> +	const u32 *formats;
>> +	u8 num_modifiers;
>> +	const u64 *modifiers;
>> +	unsigned int min_width;
>> +	unsigned int min_height;
>> +	unsigned int max_width;
>> +	unsigned int max_height;
>> +	unsigned int rotation;
>> +	unsigned int blend_mode;
>> +	unsigned int color_encoding;
>> +
>> +	/* 0 means no de-gamma LUT */
>> +	unsigned int degamma_size;
>> +
>> +	int min_scale; /* 16.16 fixed point */
>> +	int max_scale; /* 16.16 fixed point */
>> +
>> +	/* default zorder value,
>> +	 * and 255 means unsupported zorder capability
>> +	 */
>> +	u8	 zpos;
>> +
>> +	bool watermark;
>> +	bool color_mgmt;
>> +	bool roi;
>> +};
>> +
>> +struct vs_dc_info {
>> +	const char *name;
>> +
>> +	u8 panel_num;
>> +
>> +	/* planes */
>> +	u8 plane_num;
>> +	const struct vs_plane_info *planes;
>> +
>> +	u8 layer_num;
>> +	unsigned int max_bpc;
>> +	unsigned int color_formats;
>> +
>> +	/* 0 means no gamma LUT */
>> +	u16 gamma_size;
>> +	u8 gamma_bits;
>> +
>> +	u16 pitch_alignment;
>> +
>> +	bool pipe_sync;
>> +	bool background;
>> +	bool panel_sync;
>> +	bool cap_dec;
>> +};
>> +
>> +#endif /* __VS_TYPE_H__ */
> 

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-11-14 10:42       ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-11-14 10:42 UTC (permalink / raw)
  To: Dmitry Baryshkov, dri-devel, devicetree, linux-kernel,
	linux-riscv, linux-media, linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig



On 2023/10/26 3:28, Dmitry Baryshkov wrote:
> On 25/10/2023 13:39, Keith Zhao wrote:
>> add 2 crtcs and 8 planes in vs-drm
>> 
>> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>> ---
>>   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
>>   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
>>   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
>>   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
>>   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
>>   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
>>   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
>>   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
>>   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
>>   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
>>   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
>>   11 files changed, 4494 insertions(+), 2 deletions(-)
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
>>   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
>> 
>> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
>> index 7d3be305b..1d48016ca 100644
>> --- a/drivers/gpu/drm/verisilicon/Makefile
>> +++ b/drivers/gpu/drm/verisilicon/Makefile
>> @@ -1,7 +1,11 @@
>>   # SPDX-License-Identifier: GPL-2.0
>>   
>> -vs_drm-objs := vs_drv.o \
>> -		vs_modeset.o
>> +vs_drm-objs := vs_dc_hw.o \
>> +		vs_dc.o \
>> +		vs_crtc.o \
>> +		vs_drv.o \
>> +		vs_modeset.o \
>> +		vs_plane.o
>>   
>>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>>   
>> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> new file mode 100644
>> index 000000000..8a658ea77
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> @@ -0,0 +1,257 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + *
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/debugfs.h>
>> +#include <linux/media-bus-format.h>
>> +
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_atomic.h>
>> +#include <drm/drm_crtc.h>
>> +#include <drm/drm_gem_atomic_helper.h>
>> +#include <drm/drm_vblank.h>
>> +#include <drm/vs_drm.h>
>> +
>> +#include "vs_crtc.h"
>> +#include "vs_dc.h"
>> +#include "vs_drv.h"
>> +
>> +static void vs_crtc_reset(struct drm_crtc *crtc)
>> +{
>> +	struct vs_crtc_state *state;
>> +
>> +	if (crtc->state) {
>> +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
>> +
>> +		state = to_vs_crtc_state(crtc->state);
>> +		kfree(state);
>> +		crtc->state = NULL;
>> +	}
> 
> You can call your crtc_destroy_state function directly here.
> 
>> +
>> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> +	if (!state)
>> +		return;
>> +
>> +	__drm_atomic_helper_crtc_reset(crtc, &state->base);
>> +}
>> +
>> +static struct drm_crtc_state *
>> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
>> +{
>> +	struct vs_crtc_state *ori_state;
> 
> It might be a matter of taste, but it is usually old_state.
> 
>> +	struct vs_crtc_state *state;
>> +
>> +	if (!crtc->state)
>> +		return NULL;
>> +
>> +	ori_state = to_vs_crtc_state(crtc->state);
>> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> +	if (!state)
>> +		return NULL;
>> +
>> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
>> +
>> +	state->output_fmt = ori_state->output_fmt;
>> +	state->encoder_type = ori_state->encoder_type;
>> +	state->bpp = ori_state->bpp;
>> +	state->underflow = ori_state->underflow;
> 
> Can you use kmemdup instead?
> 
>> +
>> +	return &state->base;
>> +}
>> +
>> +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
>> +					 struct drm_crtc_state *state)
>> +{
>> +	__drm_atomic_helper_crtc_destroy_state(state);
>> +	kfree(to_vs_crtc_state(state));
>> +}
>> +
>> +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
>> +{
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	vs_dc_enable_vblank(dc, true);
>> +
>> +	return 0;
>> +}
>> +
>> +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
>> +{
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	vs_dc_enable_vblank(dc, false);
>> +}
>> +
>> +static const struct drm_crtc_funcs vs_crtc_funcs = {
>> +	.set_config		= drm_atomic_helper_set_config,
>> +	.page_flip		= drm_atomic_helper_page_flip,
> 
> destroy is required, see drm_mode_config_cleanup()

hi Dmitry:
if define destroy in drm_crtc_funcs,
it will make __drmm_crtc_init_with_planes unhappy

see:
__printf(6, 0)
static int __drmm_crtc_init_with_planes(struct drm_device *dev,
					struct drm_crtc *crtc,
					struct drm_plane *primary,
					struct drm_plane *cursor,
					const struct drm_crtc_funcs *funcs,
					const char *name,
					va_list args)
{
	int ret;

	drm_WARN_ON(dev, funcs && funcs->destroy);

........
}

It should not need to be defined here, I think

> 
>> +	.reset			= vs_crtc_reset,
>> +	.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
>> +	.atomic_destroy_state	= vs_crtc_atomic_destroy_state,
> 
> please consider adding atomic_print_state to output driver-specific bits.
> 
>> +	.enable_vblank		= vs_crtc_enable_vblank,
>> +	.disable_vblank		= vs_crtc_disable_vblank,
>> +};
>> +
>> +static u8 cal_pixel_bits(u32 bus_format)
> 
> This looks like a generic helper code, which can go to a common place.
> 
>> +{
>> +	u8 bpp;
>> +
>> +	switch (bus_format) {
>> +	case MEDIA_BUS_FMT_RGB565_1X16:
>> +	case MEDIA_BUS_FMT_UYVY8_1X16:
>> +		bpp = 16;
>> +		break;
>> +	case MEDIA_BUS_FMT_RGB666_1X18:
>> +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
>> +		bpp = 18;
>> +		break;
>> +	case MEDIA_BUS_FMT_UYVY10_1X20:
>> +		bpp = 20;
>> +		break;
>> +	case MEDIA_BUS_FMT_BGR888_1X24:
>> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
>> +	case MEDIA_BUS_FMT_YUV8_1X24:
>> +		bpp = 24;
>> +		break;
>> +	case MEDIA_BUS_FMT_RGB101010_1X30:
>> +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
>> +	case MEDIA_BUS_FMT_YUV10_1X30:
>> +		bpp = 30;
>> +		break;
>> +	default:
>> +		bpp = 24;
>> +		break;
>> +	}
>> +
>> +	return bpp;
>> +}
>> +
>> +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
>> +				  struct drm_atomic_state *state)
>> +{
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
>> +
>> +	vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
>> +
>> +	vs_dc_enable(dc, crtc);
>> +	drm_crtc_vblank_on(crtc);
>> +}
>> +
>> +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
>> +				   struct drm_atomic_state *state)
>> +{
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	drm_crtc_vblank_off(crtc);
>> +
>> +	vs_dc_disable(dc, crtc);
>> +
>> +	if (crtc->state->event && !crtc->state->active) {
>> +		spin_lock_irq(&crtc->dev->event_lock);
>> +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
>> +		spin_unlock_irq(&crtc->dev->event_lock);
>> +
>> +		crtc->state->event = NULL;
> 
> I think even should be cleared within the lock.
> 
>> +	}
>> +}
>> +
>> +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
>> +				 struct drm_atomic_state *state)
>> +{
>> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
>> +									  crtc);
>> +
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct device *dev = vs_crtc->dev;
>> +	struct drm_property_blob *blob = crtc->state->gamma_lut;
>> +	struct drm_color_lut *lut;
>> +	struct vs_dc *dc = dev_get_drvdata(dev);
>> +
>> +	if (crtc_state->color_mgmt_changed) {
>> +		if (blob && blob->length) {
>> +			lut = blob->data;
>> +			vs_dc_set_gamma(dc, crtc, lut,
>> +					blob->length / sizeof(*lut));
>> +			vs_dc_enable_gamma(dc, crtc, true);
>> +		} else {
>> +			vs_dc_enable_gamma(dc, crtc, false);
>> +		}
>> +	}
>> +}
>> +
>> +static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
>> +				 struct drm_atomic_state *state)
>> +{
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct drm_pending_vblank_event *event = crtc->state->event;
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	vs_dc_commit(dc);
>> +
>> +	if (event) {
>> +		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
>> +
>> +		spin_lock_irq(&crtc->dev->event_lock);
>> +		drm_crtc_arm_vblank_event(crtc, event);
>> +		spin_unlock_irq(&crtc->dev->event_lock);
>> +		crtc->state->event = NULL;
> 
> I think even should be cleared within the lock.
> 
>> +	}
>> +}
>> +
>> +static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
>> +	.atomic_check = drm_crtc_helper_atomic_check,
>> +	.atomic_enable	= vs_crtc_atomic_enable,
>> +	.atomic_disable = vs_crtc_atomic_disable,
>> +	.atomic_begin	= vs_crtc_atomic_begin,
>> +	.atomic_flush	= vs_crtc_atomic_flush,
>> +};
>> +
>> +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
>> +	{ VS_SINGLE_DC,				"single dc mode" },
>> +	{ VS_MULTI_DC_PRIMARY,		"primary dc for multi dc mode" },
>> +	{ VS_MULTI_DC_SECONDARY,	"secondary dc for multi dc mode" },
>> +};
>> +
>> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
>> +			       struct vs_dc_info *info)
>> +{
>> +	struct vs_crtc *crtc;
>> +	int ret;
>> +
>> +	if (!info)
>> +		return NULL;
>> +
>> +	crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
>> +	if (!crtc)
>> +		return NULL;
>> +
>> +	ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
>> +					 NULL, NULL, &vs_crtc_funcs,
>> +					 info->name ? info->name : NULL);
> 
> It might be better to add drmm_crtc_init() helper.
> 
>> +	if (ret)
>> +		return NULL;
>> +
>> +	drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
>> +
>> +	if (info->gamma_size) {
>> +		ret = drm_mode_crtc_set_gamma_size(&crtc->base,
>> +						   info->gamma_size);
>> +		if (ret)
>> +			return NULL;
>> +
>> +		drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
>> +					   info->gamma_size);
>> +	}
>> +
>> +	crtc->max_bpc = info->max_bpc;
>> +	crtc->color_formats = info->color_formats;
>> +	return crtc;
>> +}
>> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h
>> new file mode 100644
>> index 000000000..526dd63e5
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.h
>> @@ -0,0 +1,43 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#ifndef __VS_CRTC_H__
>> +#define __VS_CRTC_H__
>> +
>> +#include <drm/drm_crtc.h>
>> +#include <drm/drm_crtc_helper.h>
>> +
>> +#include "vs_type.h"
>> +
>> +struct vs_crtc_state {
>> +	struct drm_crtc_state base;
>> +
>> +	u32 output_fmt;
>> +	u8 encoder_type;
>> +	u8 bpp;
>> +	bool underflow;
>> +};
>> +
>> +struct vs_crtc {
>> +	struct drm_crtc base;
>> +	struct device *dev;
>> +	unsigned int max_bpc;
>> +	unsigned int color_formats;
>> +};
>> +
>> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
>> +			       struct vs_dc_info *info);
>> +
>> +static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc)
>> +{
>> +	return container_of(crtc, struct vs_crtc, base);
>> +}
>> +
>> +static inline struct vs_crtc_state *
>> +to_vs_crtc_state(struct drm_crtc_state *state)
>> +{
>> +	return container_of(state, struct vs_crtc_state, base);
>> +}
>> +#endif /* __VS_CRTC_H__ */
>> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
>> new file mode 100644
>> index 000000000..b5ab92d98
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_dc.c
>> @@ -0,0 +1,1002 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#include <linux/component.h>
>> +#include <linux/clk.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/reset.h>
>> +
>> +#include <drm/drm_atomic.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_blend.h>
>> +#include <drm/drm_framebuffer.h>
>> +#include <drm/drm_vblank.h>
>> +#include <drm/vs_drm.h>
>> +
>> +#include "vs_dc_hw.h"
>> +#include "vs_dc.h"
>> +#include "vs_drv.h"
>> +
>> +static const char * const vout_clocks[] = {
>> +	"noc_bus",
>> +	"channel0",
>> +	"channel1",
>> +	"dc_core",
>> +	"axi_core",
>> +	"ahb",
>> +	"hdmi_tx",
>> +	"dc_parent",
>> +
>> +};
>> +
>> +static const char * const vout_resets[] = {
>> +	"axi",
>> +	"ahb",
>> +	"core",
>> +};
>> +
>> +static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb)
>> +{
>> +	u8 f = FORMAT_A8R8G8B8;
>> +
>> +	switch (format) {
>> +	case DRM_FORMAT_XRGB4444:
>> +	case DRM_FORMAT_RGBX4444:
>> +	case DRM_FORMAT_XBGR4444:
>> +	case DRM_FORMAT_BGRX4444:
>> +		f = FORMAT_X4R4G4B4;
>> +		break;
>> +	case DRM_FORMAT_ARGB4444:
>> +	case DRM_FORMAT_RGBA4444:
>> +	case DRM_FORMAT_ABGR4444:
>> +	case DRM_FORMAT_BGRA4444:
>> +		f = FORMAT_A4R4G4B4;
>> +		break;
>> +	case DRM_FORMAT_XRGB1555:
>> +	case DRM_FORMAT_RGBX5551:
>> +	case DRM_FORMAT_XBGR1555:
>> +	case DRM_FORMAT_BGRX5551:
>> +		f = FORMAT_X1R5G5B5;
>> +		break;
>> +	case DRM_FORMAT_ARGB1555:
>> +	case DRM_FORMAT_RGBA5551:
>> +	case DRM_FORMAT_ABGR1555:
>> +	case DRM_FORMAT_BGRA5551:
>> +		f = FORMAT_A1R5G5B5;
>> +		break;
>> +	case DRM_FORMAT_RGB565:
>> +	case DRM_FORMAT_BGR565:
>> +		f = FORMAT_R5G6B5;
>> +		break;
>> +	case DRM_FORMAT_XRGB8888:
>> +	case DRM_FORMAT_RGBX8888:
>> +	case DRM_FORMAT_XBGR8888:
>> +	case DRM_FORMAT_BGRX8888:
>> +		f = FORMAT_X8R8G8B8;
>> +		break;
>> +	case DRM_FORMAT_ARGB8888:
>> +	case DRM_FORMAT_RGBA8888:
>> +	case DRM_FORMAT_ABGR8888:
>> +	case DRM_FORMAT_BGRA8888:
>> +		f = FORMAT_A8R8G8B8;
>> +		break;
>> +	case DRM_FORMAT_YUYV:
>> +	case DRM_FORMAT_YVYU:
>> +		f = FORMAT_YUY2;
>> +		break;
>> +	case DRM_FORMAT_UYVY:
>> +	case DRM_FORMAT_VYUY:
>> +		f = FORMAT_UYVY;
>> +		break;
>> +	case DRM_FORMAT_YUV420:
>> +	case DRM_FORMAT_YVU420:
>> +		f = FORMAT_YV12;
>> +		break;
>> +	case DRM_FORMAT_NV21:
>> +		f = FORMAT_NV12;
>> +		break;
>> +	case DRM_FORMAT_NV16:
>> +	case DRM_FORMAT_NV61:
>> +		f = FORMAT_NV16;
>> +		break;
>> +	case DRM_FORMAT_P010:
>> +		f = FORMAT_P010;
>> +		break;
>> +	case DRM_FORMAT_ARGB2101010:
>> +	case DRM_FORMAT_RGBA1010102:
>> +	case DRM_FORMAT_ABGR2101010:
>> +	case DRM_FORMAT_BGRA1010102:
>> +		f = FORMAT_A2R10G10B10;
>> +		break;
>> +	case DRM_FORMAT_NV12:
>> +		f = FORMAT_NV12;
>> +		break;
>> +	case DRM_FORMAT_YUV444:
>> +		f = FORMAT_YUV444;
>> +		break;
>> +	default:
>> +		break;
>> +	}
>> +
>> +	fb->format = f;
>> +}
>> +
>> +static inline void update_swizzle(u32 format, struct dc_hw_fb *fb)
>> +{
>> +	fb->swizzle = SWIZZLE_ARGB;
>> +	fb->uv_swizzle = 0;
>> +
>> +	switch (format) {
>> +	case DRM_FORMAT_RGBX4444:
>> +	case DRM_FORMAT_RGBA4444:
>> +	case DRM_FORMAT_RGBX5551:
>> +	case DRM_FORMAT_RGBA5551:
>> +	case DRM_FORMAT_RGBX8888:
>> +	case DRM_FORMAT_RGBA8888:
>> +	case DRM_FORMAT_RGBA1010102:
>> +		fb->swizzle = SWIZZLE_RGBA;
>> +		break;
>> +	case DRM_FORMAT_XBGR4444:
>> +	case DRM_FORMAT_ABGR4444:
>> +	case DRM_FORMAT_XBGR1555:
>> +	case DRM_FORMAT_ABGR1555:
>> +	case DRM_FORMAT_BGR565:
>> +	case DRM_FORMAT_XBGR8888:
>> +	case DRM_FORMAT_ABGR8888:
>> +	case DRM_FORMAT_ABGR2101010:
>> +		fb->swizzle = SWIZZLE_ABGR;
>> +		break;
>> +	case DRM_FORMAT_BGRX4444:
>> +	case DRM_FORMAT_BGRA4444:
>> +	case DRM_FORMAT_BGRX5551:
>> +	case DRM_FORMAT_BGRA5551:
>> +	case DRM_FORMAT_BGRX8888:
>> +	case DRM_FORMAT_BGRA8888:
>> +	case DRM_FORMAT_BGRA1010102:
>> +		fb->swizzle = SWIZZLE_BGRA;
>> +		break;
>> +	case DRM_FORMAT_YVYU:
>> +	case DRM_FORMAT_VYUY:
>> +	case DRM_FORMAT_NV21:
>> +	case DRM_FORMAT_NV61:
>> +		fb->uv_swizzle = 1;
>> +		break;
>> +	default:
>> +		break;
>> +	}
>> +}
>> +
>> +static inline void update_watermark(struct drm_property_blob *watermark,
>> +				    struct dc_hw_fb *fb)
>> +{
>> +	struct drm_vs_watermark *data;
>> +
>> +	fb->water_mark = 0;
>> +
>> +	if (watermark) {
>> +		data = watermark->data;
>> +		fb->water_mark = data->watermark & 0xFFFFF;
>> +	}
>> +}
>> +
>> +static inline u8 to_vs_rotation(unsigned int rotation)
>> +{
>> +	u8 rot;
>> +
>> +	switch (rotation & DRM_MODE_REFLECT_MASK) {
>> +	case DRM_MODE_REFLECT_X:
>> +		rot = FLIP_X;
>> +		return rot;
>> +	case DRM_MODE_REFLECT_Y:
>> +		rot = FLIP_Y;
>> +		return rot;
>> +	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
>> +		rot = FLIP_XY;
>> +		return rot;
>> +	default:
>> +		break;
>> +	}
>> +
>> +	switch (rotation & DRM_MODE_ROTATE_MASK) {
>> +	case DRM_MODE_ROTATE_0:
>> +		rot = ROT_0;
>> +		break;
>> +	case DRM_MODE_ROTATE_90:
>> +		rot = ROT_90;
>> +		break;
>> +	case DRM_MODE_ROTATE_180:
>> +		rot = ROT_180;
>> +		break;
>> +	case DRM_MODE_ROTATE_270:
>> +		rot = ROT_270;
>> +		break;
>> +	default:
>> +		rot = ROT_0;
>> +		break;
>> +	}
>> +
>> +	return rot;
>> +}
>> +
>> +static inline u8 to_vs_yuv_color_space(u32 color_space)
>> +{
>> +	u8 cs;
>> +
>> +	switch (color_space) {
>> +	case DRM_COLOR_YCBCR_BT601:
>> +		cs = COLOR_SPACE_601;
>> +		break;
>> +	case DRM_COLOR_YCBCR_BT709:
>> +		cs = COLOR_SPACE_709;
>> +		break;
>> +	case DRM_COLOR_YCBCR_BT2020:
>> +		cs = COLOR_SPACE_2020;
>> +		break;
>> +	default:
>> +		cs = COLOR_SPACE_601;
>> +		break;
>> +	}
>> +
>> +	return cs;
>> +}
>> +
>> +static inline u8 to_vs_tile_mode(u64 modifier)
>> +{
>> +	if (modifier == DRM_FORMAT_MOD_VIVANTE_TILED)
>> +		return DC_TILE_MODE4X4;
>> +
>> +	return (u8)(modifier & DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK);
>> +}
>> +
>> +static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc)
>> +{
>> +	u8 panel_num = dc->hw.info->panel_num;
>> +	u32 index = drm_crtc_index(crtc);
>> +	int i;
>> +
>> +	for (i = 0; i < panel_num; i++) {
>> +		if (index == dc->crtc[i]->base.index)
>> +			return i;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
>> +					  unsigned int alignment)
>> +{
>> +	struct vs_drm_device *priv = to_vs_drm_private(drm_dev);
>> +
>> +	if (alignment > priv->pitch_alignment)
>> +		priv->pitch_alignment = alignment;
>> +}
>> +
>> +static int plda_clk_rst_init(struct device *dev)
>> +{
>> +	int ret = 0;
>> +	struct vs_dc *dc = dev_get_drvdata(dev);
>> +
>> +	ret = clk_bulk_prepare_enable(dc->nclks, dc->clk_vout);
>> +	if (ret) {
>> +		dev_err(dev, "failed to enable clocks\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = reset_control_bulk_deassert(dc->nrsts, dc->rst_vout);
>> +	return ret;
>> +}
>> +
>> +static void plda_clk_rst_deinit(struct device *dev)
>> +{
>> +	struct vs_dc *dc = dev_get_drvdata(dev);
>> +
>> +	reset_control_bulk_assert(dc->nrsts, dc->rst_vout);
>> +	clk_bulk_disable_unprepare(dc->nclks, dc->clk_vout);
>> +}
>> +
>> +static void dc_deinit(struct device *dev)
>> +{
>> +	struct vs_dc *dc = dev_get_drvdata(dev);
>> +
>> +	dc_hw_enable_interrupt(&dc->hw, 0);
>> +	dc_hw_deinit(&dc->hw);
>> +	plda_clk_rst_deinit(dev);
>> +}
>> +
>> +static int dc_init(struct device *dev)
>> +{
>> +	struct vs_dc *dc = dev_get_drvdata(dev);
>> +	int ret;
>> +
>> +	dc->first_frame = true;
>> +
>> +	ret = plda_clk_rst_init(dev);
>> +	if (ret < 0) {
>> +		dev_err(dev, "failed to init dc clk reset: %d\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	ret = dc_hw_init(&dc->hw);
>> +	if (ret) {
>> +		dev_err(dev, "failed to init DC HW\n");
>> +		return ret;
>> +	}
>> +	return 0;
>> +}
>> +
>> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
>> +{
>> +	struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
>> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
>> +	struct dc_hw_display display;
>> +
>> +	display.bus_format = crtc_state->output_fmt;
>> +	display.h_active = mode->hdisplay;
>> +	display.h_total = mode->htotal;
>> +	display.h_sync_start = mode->hsync_start;
>> +	display.h_sync_end = mode->hsync_end;
>> +	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
>> +		display.h_sync_polarity = true;
>> +	else
>> +		display.h_sync_polarity = false;
>> +
>> +	display.v_active = mode->vdisplay;
>> +	display.v_total = mode->vtotal;
>> +	display.v_sync_start = mode->vsync_start;
>> +	display.v_sync_end = mode->vsync_end;
>> +
>> +	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
>> +		display.v_sync_polarity = true;
>> +	else
>> +		display.v_sync_polarity = false;
>> +
>> +	display.id = to_vs_display_id(dc, crtc);
>> +
>> +	display.enable = true;
>> +
>> +	if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) {
>> +		dc_hw_set_out(&dc->hw, OUT_DPI, display.id);
>> +		clk_set_rate(dc->clk_vout[CLK_VOUT_SOC_PIX].clk, mode->clock * 1000);
>> +		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX1].clk,
>> +			       dc->clk_vout[CLK_VOUT_SOC_PIX].clk);
>> +	} else {
>> +		dc_hw_set_out(&dc->hw, OUT_DP, display.id);
>> +		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX0].clk,
>> +			       dc->clk_vout[CLK_VOUT_HDMI_PIX].clk);
>> +	}
>> +
>> +	dc_hw_setup_display(&dc->hw, &display);
>> +}
>> +
>> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc)
>> +{
>> +	struct dc_hw_display display;
>> +
>> +	display.id = to_vs_display_id(dc, crtc);
>> +	display.enable = false;
>> +
>> +	dc_hw_setup_display(&dc->hw, &display);
>> +}
>> +
>> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
>> +		     struct drm_color_lut *lut, unsigned int size)
>> +{
>> +	u16 i, r, g, b;
>> +	u8 bits, id;
>> +
>> +	if (size != dc->hw.info->gamma_size) {
>> +		drm_err(crtc->dev, "gamma size does not match!\n");
>> +		return;
>> +	}
>> +
>> +	id = to_vs_display_id(dc, crtc);
>> +
>> +	bits = dc->hw.info->gamma_bits;
>> +	for (i = 0; i < size; i++) {
>> +		r = drm_color_lut_extract(lut[i].red, bits);
>> +		g = drm_color_lut_extract(lut[i].green, bits);
>> +		b = drm_color_lut_extract(lut[i].blue, bits);
>> +		dc_hw_update_gamma(&dc->hw, id, i, r, g, b);
>> +	}
>> +}
>> +
>> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
>> +			bool enable)
>> +{
>> +	u8 id;
>> +
>> +	id = to_vs_display_id(dc, crtc);
>> +	dc_hw_enable_gamma(&dc->hw, id, enable);
>> +}
>> +
>> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable)
>> +{
>> +	dc_hw_enable_interrupt(&dc->hw, enable);
>> +}
>> +
>> +static u32 calc_factor(u32 src, u32 dest)
>> +{
>> +	u32 factor = 1 << 16;
>> +
>> +	if (src > 1 && dest > 1)
>> +		factor = ((src - 1) << 16) / (dest - 1);
>> +
>> +	return factor;
>> +}
>> +
>> +static void update_scale(struct drm_plane_state *state, struct dc_hw_roi *roi,
>> +			 struct dc_hw_scale *scale)
>> +{
>> +	int dst_w = drm_rect_width(&state->dst);
>> +	int dst_h = drm_rect_height(&state->dst);
>> +	int src_w, src_h, temp;
>> +
>> +	scale->enable = false;
>> +
>> +	if (roi->enable) {
>> +		src_w = roi->width;
>> +		src_h = roi->height;
>> +	} else {
>> +		src_w = drm_rect_width(&state->src) >> 16;
>> +		src_h = drm_rect_height(&state->src) >> 16;
>> +	}
>> +
>> +	if (drm_rotation_90_or_270(state->rotation)) {
>> +		temp = src_w;
>> +		src_w = src_h;
>> +		src_h = temp;
>> +	}
>> +
>> +	if (src_w != dst_w) {
>> +		scale->scale_factor_x = calc_factor(src_w, dst_w);
>> +		scale->enable = true;
>> +	} else {
>> +		scale->scale_factor_x = 1 << 16;
>> +	}
>> +	if (src_h != dst_h) {
>> +		scale->scale_factor_y = calc_factor(src_h, dst_h);
>> +		scale->enable = true;
>> +	} else {
>> +		scale->scale_factor_y = 1 << 16;
>> +	}
>> +}
>> +
>> +static void update_fb(struct vs_plane *plane, u8 display_id,
>> +		      struct dc_hw_fb *fb, struct drm_plane_state *state)
>> +{
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> +	struct drm_framebuffer *drm_fb = state->fb;
>> +	struct drm_rect *src = &state->src;
>> +
>> +	fb->display_id = display_id;
>> +	fb->y_address = plane_state->dma_addr[0];
>> +	fb->y_stride = drm_fb->pitches[0];
>> +	if (drm_fb->format->format == DRM_FORMAT_YVU420) {
>> +		fb->u_address = plane_state->dma_addr[2];
>> +		fb->v_address = plane_state->dma_addr[1];
>> +		fb->u_stride = drm_fb->pitches[2];
>> +		fb->v_stride = drm_fb->pitches[1];
>> +	} else {
>> +		fb->u_address = plane_state->dma_addr[1];
>> +		fb->v_address = plane_state->dma_addr[2];
>> +		fb->u_stride = drm_fb->pitches[1];
>> +		fb->v_stride = drm_fb->pitches[2];
>> +	}
>> +	fb->width = drm_rect_width(src) >> 16;
>> +	fb->height = drm_rect_height(src) >> 16;
>> +	fb->tile_mode = to_vs_tile_mode(drm_fb->modifier);
>> +	fb->rotation = to_vs_rotation(state->rotation);
>> +	fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding);
>> +	fb->zpos = state->zpos;
>> +	fb->enable = state->visible;
>> +	update_format(drm_fb->format->format, drm_fb->modifier, fb);
>> +	update_swizzle(drm_fb->format->format, fb);
>> +	update_watermark(plane_state->watermark, fb);
>> +	plane_state->status.tile_mode = fb->tile_mode;
>> +}
>> +
>> +static void update_degamma(struct vs_dc *dc, struct vs_plane *plane,
>> +			   struct vs_plane_state *plane_state)
>> +{
>> +	dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
>> +	plane_state->degamma_changed = false;
>> +}
>> +
>> +static void update_roi(struct vs_dc *dc, u8 id,
>> +		       struct vs_plane_state *plane_state,
>> +		       struct dc_hw_roi *roi,
>> +		       struct drm_plane_state *state)
>> +{
>> +	struct drm_vs_roi *data;
>> +	struct drm_rect *src = &state->src;
>> +	u16 src_w = drm_rect_width(src) >> 16;
>> +	u16 src_h = drm_rect_height(src) >> 16;
>> +
>> +	if (plane_state->roi) {
>> +		data = plane_state->roi->data;
>> +
>> +		if (data->enable) {
>> +			roi->x = data->roi_x;
>> +			roi->y = data->roi_y;
>> +			roi->width = (data->roi_x + data->roi_w > src_w) ?
>> +						 (src_w - data->roi_x) : data->roi_w;
>> +			roi->height = (data->roi_y + data->roi_h > src_h) ?
>> +						  (src_h - data->roi_y) : data->roi_h;
>> +			roi->enable = true;
>> +		} else {
>> +			roi->enable = false;
>> +		}
>> +
>> +		dc_hw_update_roi(&dc->hw, id, roi);
>> +	} else {
>> +		roi->enable = false;
>> +	}
>> +}
>> +
>> +static void update_color_mgmt(struct vs_dc *dc, u8 id,
>> +			      struct dc_hw_fb *fb,
>> +			      struct vs_plane_state *plane_state)
>> +{
>> +	struct drm_vs_color_mgmt *data;
>> +	struct dc_hw_colorkey colorkey;
>> +
>> +	if (plane_state->color_mgmt) {
>> +		data = plane_state->color_mgmt->data;
>> +
>> +		fb->clear_enable = data->clear_enable;
>> +		fb->clear_value = data->clear_value;
>> +
>> +		if (data->colorkey > data->colorkey_high)
>> +			data->colorkey = data->colorkey_high;
>> +
>> +		colorkey.colorkey = data->colorkey;
>> +		colorkey.colorkey_high = data->colorkey_high;
>> +		colorkey.transparency = (data->transparency) ?
>> +				DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE;
>> +		dc_hw_update_colorkey(&dc->hw, id, &colorkey);
>> +	}
>> +}
>> +
>> +static void update_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +			 struct drm_plane *drm_plane,
>> +			 struct drm_atomic_state *drm_state)
>> +{
>> +	struct dc_hw_fb fb = {0};
>> +	struct dc_hw_scale scale;
>> +	struct dc_hw_position pos;
>> +	struct dc_hw_blend blend;
>> +	struct dc_hw_roi roi;
>> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
>> +									   drm_plane);
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> +	struct drm_rect *dest = &state->dst;
>> +	bool dec_enable = false;
>> +	u8 display_id = 0;
>> +
>> +	display_id = to_vs_display_id(dc, state->crtc);
>> +	update_fb(plane, display_id, &fb, state);
>> +	fb.dec_enable = dec_enable;
>> +
>> +	update_roi(dc, plane->id, plane_state, &roi, state);
>> +
>> +	update_scale(state, &roi, &scale);
>> +
>> +	if (plane_state->degamma_changed)
>> +		update_degamma(dc, plane, plane_state);
>> +
>> +	pos.start_x = dest->x1;
>> +	pos.start_y = dest->y1;
>> +	pos.end_x = dest->x2;
>> +	pos.end_y = dest->y2;
>> +
>> +	blend.alpha = (u8)(state->alpha >> 8);
>> +	blend.blend_mode = (u8)(state->pixel_blend_mode);
>> +
>> +	update_color_mgmt(dc, plane->id, &fb, plane_state);
>> +
>> +	dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
>> +}
>> +
>> +static void update_qos(struct vs_dc *dc, struct vs_plane *plane,
>> +		       struct drm_plane *drm_plane,
>> +		       struct drm_atomic_state *drm_state)
>> +{
>> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
>> +									   drm_plane);
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> +	struct drm_vs_watermark *data;
>> +	struct dc_hw_qos qos;
>> +
>> +	if (plane_state->watermark) {
>> +		data = plane_state->watermark->data;
>> +
>> +		if (data->qos_high) {
>> +			if (data->qos_low > data->qos_high)
>> +				data->qos_low = data->qos_high;
>> +
>> +			qos.low_value = data->qos_low & 0x0F;
>> +			qos.high_value = data->qos_high & 0x0F;
>> +			dc_hw_update_qos(&dc->hw, &qos);
>> +		}
>> +	}
>> +}
>> +
>> +static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor)
>> +{
>> +	u8 size_type;
>> +
>> +	switch (state->crtc_w) {
>> +	case 32:
>> +		size_type = CURSOR_SIZE_32X32;
>> +		break;
>> +	case 64:
>> +		size_type = CURSOR_SIZE_64X64;
>> +		break;
>> +	default:
>> +		size_type = CURSOR_SIZE_32X32;
>> +		break;
>> +	}
>> +
>> +	cursor->size = size_type;
>> +}
>> +
>> +static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +				struct drm_plane *drm_plane,
>> +				struct drm_atomic_state *drm_state)
>> +{
>> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
>> +								       drm_plane);
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> +	struct drm_framebuffer *drm_fb = state->fb;
>> +	struct dc_hw_cursor cursor;
>> +
>> +	cursor.address = plane_state->dma_addr[0];
>> +	cursor.x = state->crtc_x;
>> +	cursor.y = state->crtc_y;
>> +	cursor.hot_x = drm_fb->hot_x;
>> +	cursor.hot_y = drm_fb->hot_y;
>> +	cursor.display_id = to_vs_display_id(dc, state->crtc);
>> +	update_cursor_size(state, &cursor);
>> +	cursor.enable = true;
>> +
>> +	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
>> +}
>> +
>> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +			struct drm_plane *drm_plane,
>> +			struct drm_atomic_state *drm_state)
>> +{
>> +	update_plane(dc, plane, drm_plane, drm_state);
>> +	update_qos(dc, plane, drm_plane, drm_state);
>> +}
>> +
>> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +			       struct drm_plane *drm_plane,
>> +			       struct drm_atomic_state *drm_state)
>> +{
>> +	update_cursor_plane(dc, plane, drm_plane, drm_state);
>> +}
>> +
>> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +			 struct drm_plane_state *old_state)
>> +{
>> +	struct dc_hw_fb fb = {0};
>> +
>> +	fb.enable = false;
>> +	dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL, NULL, NULL);
>> +}
>> +
>> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +				struct drm_plane_state *old_state)
>> +{
>> +	struct dc_hw_cursor cursor = {0};
>> +
>> +	cursor.enable = false;
>> +	cursor.display_id = to_vs_display_id(dc, old_state->crtc);
>> +	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
>> +}
>> +
>> +static bool vs_dc_mod_supported(const struct vs_plane_info *plane_info,
>> +				u64 modifier)
>> +{
>> +	const u64 *mods;
>> +
>> +	if (!plane_info->modifiers)
>> +		return false;
>> +
>> +	for (mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID; mods++) {
>> +		if (*mods == modifier)
>> +			return true;
>> +	}
>> +
>> +	return false;
>> +}
>> +
>> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
>> +		      struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> +										 plane);
>> +
>> +	struct drm_framebuffer *fb = new_plane_state->fb;
>> +	const struct vs_plane_info *plane_info;
>> +	struct drm_crtc *crtc = new_plane_state->crtc;
>> +	struct drm_crtc_state *crtc_state;
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +
>> +	plane_info = &dc->hw.info->planes[vs_plane->id];
>> +
>> +	if (fb->width < plane_info->min_width ||
>> +	    fb->width > plane_info->max_width ||
>> +	    fb->height < plane_info->min_height ||
>> +	    fb->height > plane_info->max_height)
>> +		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n",
>> +			     vs_plane->id);
>> +
>> +	if (!vs_dc_mod_supported(plane_info, fb->modifier)) {
>> +		drm_err(plane->dev, "unsupported modifier on plane%d.\n", vs_plane->id);
>> +		return -EINVAL;
>> +	}
>> +
>> +	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
>> +	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
>> +						  plane_info->min_scale,
>> +						  plane_info->max_scale,
>> +						  true, true);
>> +}
>> +
>> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
>> +			     struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> +									   plane);
>> +	struct drm_framebuffer *fb = new_plane_state->fb;
>> +	const struct vs_plane_info *plane_info;
>> +	struct drm_crtc *crtc = new_plane_state->crtc;
>> +	struct drm_crtc_state *crtc_state;
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +
>> +	plane_info = &dc->hw.info->planes[vs_plane->id];
>> +
>> +	if (fb->width < plane_info->min_width ||
>> +	    fb->width > plane_info->max_width ||
>> +	    fb->height < plane_info->min_height ||
>> +	    fb->height > plane_info->max_height)
>> +		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n", vs_plane->id);
>> +
>> +	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
>> +	if (IS_ERR(crtc_state))
>> +		return -EINVAL;
>> +
>> +	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
>> +						plane_info->min_scale,
>> +						plane_info->max_scale,
>> +						true, true);
>> +}
>> +
>> +static void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
>> +{
>> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
>> +
>> +	drm_crtc_handle_vblank(crtc);
>> +
>> +	vs_crtc_state->underflow = underflow;
>> +}
>> +
>> +static irqreturn_t dc_isr(int irq, void *data)
>> +{
>> +	struct vs_dc *dc = data;
>> +	struct vs_dc_info *dc_info = dc->hw.info;
>> +	u32 i, ret;
>> +
>> +	ret = dc_hw_get_interrupt(&dc->hw);
>> +
>> +	for (i = 0; i < dc_info->panel_num; i++)
>> +		vs_crtc_handle_vblank(&dc->crtc[i]->base, dc_hw_check_underflow(&dc->hw));
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +void vs_dc_commit(struct vs_dc *dc)
>> +{
>> +	dc_hw_enable_shadow_register(&dc->hw, false);
>> +
>> +	dc_hw_commit(&dc->hw);
>> +
>> +	if (dc->first_frame)
>> +		dc->first_frame = false;
>> +
>> +	dc_hw_enable_shadow_register(&dc->hw, true);
>> +}
>> +
>> +static int dc_bind(struct device *dev, struct device *master, void *data)
>> +{
>> +	struct drm_device *drm_dev = data;
>> +	struct vs_dc *dc = dev_get_drvdata(dev);
>> +	struct device_node *port;
>> +	struct vs_crtc *crtc;
>> +	struct vs_dc_info *dc_info;
>> +	struct vs_plane *plane;
>> +	struct vs_plane_info *plane_info;
>> +	int i, ret;
>> +	u32 ctrc_mask = 0;
>> +
>> +	if (!drm_dev || !dc) {
>> +		dev_err(dev, "devices are not created.\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	ret = dc_init(dev);
>> +	if (ret < 0) {
>> +		drm_err(drm_dev, "Failed to initialize DC hardware.\n");
>> +		return ret;
>> +	}
>> +
>> +	port = of_get_child_by_name(dev->of_node, "port");
>> +	if (!port) {
>> +		drm_err(drm_dev, "no port node found\n");
>> +		return -ENODEV;
>> +	}
>> +	of_node_put(port);
>> +
>> +	dc_info = dc->hw.info;
>> +
>> +	for (i = 0; i < dc_info->panel_num; i++) {
>> +		crtc = vs_crtc_create(drm_dev, dc_info);
>> +		if (!crtc) {
>> +			drm_err(drm_dev, "Failed to create CRTC.\n");
>> +			ret = -ENOMEM;
>> +			return ret;
>> +		}
>> +
>> +		crtc->base.port = port;
>> +		crtc->dev = dev;
>> +		dc->crtc[i] = crtc;
>> +		ctrc_mask |= drm_crtc_mask(&crtc->base);
>> +	}
>> +
>> +	for (i = 0; i < dc_info->plane_num; i++) {
>> +		plane_info = (struct vs_plane_info *)&dc_info->planes[i];
>> +
>> +		if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
>> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
>> +						drm_crtc_mask(&dc->crtc[0]->base));
>> +		} else if (!strcmp(plane_info->name, "Primary_1") ||
>> +				   !strcmp(plane_info->name, "Cursor_1")) {
>> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
>> +						drm_crtc_mask(&dc->crtc[1]->base));
> 
> I'd say, it's not typical to use name to distinguish objects. Maybe you 
> can add indices to the info structure and drop the names instead?
> 
> 
>> +		} else {
>> +			plane = vs_plane_create(drm_dev, plane_info,
>> +						dc_info->layer_num, ctrc_mask);
>> +		}
>> +
>> +		if (IS_ERR(plane)) {
>> +			dev_err(dev, "failed to construct plane\n");
>> +			return PTR_ERR(plane);
>> +		}
>> +
>> +		plane->id = i;
>> +		dc->planes[i].id = plane_info->id;
>> +
>> +		if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
>> +			if (!strcmp(plane_info->name, "Primary"))
>> +				dc->crtc[0]->base.primary = &plane->base;
>> +			else
>> +				dc->crtc[1]->base.primary = &plane->base;
> 
> This definitely urges for an index in the plane info / plane_state.
> 
>> +			drm_dev->mode_config.min_width = plane_info->min_width;
>> +			drm_dev->mode_config.min_height =
>> +							plane_info->min_height;
>> +			drm_dev->mode_config.max_width = plane_info->max_width;
>> +			drm_dev->mode_config.max_height =
>> +							plane_info->max_height;
>> +		}
>> +
>> +		if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
>> +			if (!strcmp(plane_info->name, "Cursor"))
>> +				dc->crtc[0]->base.cursor = &plane->base;
>> +			else
>> +				dc->crtc[1]->base.cursor = &plane->base;
>> +			drm_dev->mode_config.cursor_width =
>> +							plane_info->max_width;
>> +			drm_dev->mode_config.cursor_height =
>> +							plane_info->max_height;
>> +		}
>> +	}
>> +
>> +	vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
>> +	return 0;
>> +}
>> +
>> +static void dc_unbind(struct device *dev, struct device *master, void *data)
>> +{
>> +	dc_deinit(dev);
>> +}
>> +
>> +const struct component_ops dc_component_ops = {
>> +	.bind = dc_bind,
>> +	.unbind = dc_unbind,
>> +};
>> +
>> +static const struct of_device_id dc_driver_dt_match[] = {
>> +	{ .compatible = "starfive,jh7110-dc8200", },
>> +	{},
>> +};
>> +MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
>> +
>> +static int dc_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct vs_dc *dc;
>> +	int irq, ret, i;
>> +
>> +	dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
>> +	if (!dc)
>> +		return -ENOMEM;
>> +
>> +	dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
>> +	if (IS_ERR(dc->hw.hi_base))
>> +		return PTR_ERR(dc->hw.hi_base);
>> +
>> +	dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
>> +	if (IS_ERR(dc->hw.reg_base))
>> +		return PTR_ERR(dc->hw.reg_base);
>> +
>> +	dc->nclks = ARRAY_SIZE(dc->clk_vout);
>> +	for (i = 0; i < dc->nclks; ++i)
>> +		dc->clk_vout[i].id = vout_clocks[i];
>> +	ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
>> +	if (ret) {
>> +		dev_err(dev, "Failed to get clk controls\n");
>> +		return ret;
>> +	}
>> +
>> +	dc->nrsts = ARRAY_SIZE(dc->rst_vout);
>> +	for (i = 0; i < dc->nrsts; ++i)
>> +		dc->rst_vout[i].id = vout_resets[i];
>> +	ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
>> +						 dc->rst_vout);
>> +	if (ret) {
>> +		dev_err(dev, "Failed to get reset controls\n");
>> +		return ret;
>> +	}
>> +
>> +	irq = platform_get_irq(pdev, 0);
>> +
>> +	ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
> 
> Are you ready to handle the IRQ at this point?
> 
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to install irq:%u.\n", irq);
>> +		return ret;
>> +	}
>> +
>> +	dev_set_drvdata(dev, dc);
>> +
>> +	return component_add(dev, &dc_component_ops);
>> +}
>> +
>> +static int dc_remove(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +
>> +	component_del(dev, &dc_component_ops);
>> +
>> +	dev_set_drvdata(dev, NULL);
>> +
>> +	return 0;
>> +}
>> +
>> +struct platform_driver dc_platform_driver = {
>> +	.probe = dc_probe,
>> +	.remove = dc_remove,
>> +	.driver = {
>> +		.name = "vs-dc",
>> +		.of_match_table = of_match_ptr(dc_driver_dt_match),
>> +	},
>> +};
>> +
>> +MODULE_AUTHOR("StarFive Corporation");
>> +MODULE_DESCRIPTION("VeriSilicon DC Driver");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
>> new file mode 100644
>> index 000000000..8e96fd32c
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_dc.h
>> @@ -0,0 +1,80 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#ifndef __VS_DC_H__
>> +#define __VS_DC_H__
>> +
>> +#include <linux/clk.h>
>> +#include <linux/mm_types.h>
>> +#include <linux/reset.h>
>> +
>> +#include <drm/drm_fourcc.h>
>> +#include <drm/drm_modes.h>
>> +
>> +#include "vs_crtc.h"
>> +#include "vs_dc_hw.h"
>> +#include "vs_plane.h"
>> +
>> +struct vs_dc_plane {
>> +	enum dc_hw_plane_id id;
>> +};
>> +
>> +enum vout_clk {
>> +	CLK_VOUT_NOC_DISP = 0,
>> +	CLK_VOUT_PIX0,
>> +	CLK_VOUT_PIX1,
>> +	CLK_VOUT_CORE,
>> +	CLK_VOUT_AXI,
>> +	CLK_VOUT_AHB,
>> +	CLK_VOUT_HDMI_PIX,
>> +	CLK_VOUT_SOC_PIX,
>> +	CLK_VOUT_NUM
>> +};
>> +
>> +enum rst_vout {
>> +	RST_VOUT_AXI = 0,
>> +	RST_VOUT_AHB,
>> +	RST_VOUT_CORE,
>> +	RST_VOUT_NUM
>> +};
>> +
>> +struct vs_dc {
>> +	struct vs_crtc		*crtc[DC_DISPLAY_NUM];
>> +	struct dc_hw		hw;
>> +	bool			first_frame;
>> +
>> +	struct vs_dc_plane	planes[PLANE_NUM];
>> +	struct clk_bulk_data	clk_vout[CLK_VOUT_NUM];
>> +	int			nclks;
>> +	struct reset_control_bulk_data rst_vout[RST_VOUT_NUM];
>> +	int			nrsts;
>> +};
>> +
>> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc);
>> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc);
>> +
>> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
>> +		     struct drm_color_lut *lut, unsigned int size);
>> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc, bool enable);
>> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable);
>> +void vs_dc_commit(struct vs_dc *dc);
>> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +			struct drm_plane *drm_plane,
>> +			struct drm_atomic_state *drm_state);
>> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +			 struct drm_plane_state *old_state);
>> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
>> +		      struct drm_atomic_state *state);
>> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +			       struct drm_plane *drm_plane,
>> +			       struct drm_atomic_state *drm_state);
>> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> +				struct drm_plane_state *old_state);
>> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
>> +			     struct drm_atomic_state *state);
>> +
>> +extern struct platform_driver dc_platform_driver;
>> +
>> +#endif /* __VS_DC_H__ */
>> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
>> new file mode 100644
>> index 000000000..e5acb7eb4
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
>> @@ -0,0 +1,1959 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#include <linux/bits.h>
>> +#include <linux/io.h>
>> +#include <linux/media-bus-format.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_blend.h>
>> +#include <drm/drm_fourcc.h>
>> +#include <drm/vs_drm.h>
>> +
>> +#include "vs_dc_hw.h"
>> +#include "vs_type.h"
>> +
>> +static const u32 horkernel[] = {
>> +	0x00000000, 0x20000000, 0x00002000, 0x00000000,
>> +	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
>> +	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
>> +	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
>> +	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
>> +	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
>> +	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
>> +	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
>> +	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
>> +	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
>> +	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
>> +	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
>> +	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
>> +	0x00000000, 0x00000000, 0x00000000, 0x01470000,
>> +	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
>> +	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
>> +	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
>> +	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
>> +	0x00000000, 0x00000000, 0x00004000, 0x00000000,
>> +	0x00000000, 0x00000000, 0x20002000, 0x00000000,
>> +	0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
>> +	0x00000000, 0x00000000, 0x00000000, 0x27e1181f,
>> +	0x00000000, 0x00000000, 0x00000000, 0x14680000,
>> +	0x00002b98, 0x00000000, 0x00000000, 0x00000000,
>> +	0x2f1010f0, 0x00000000, 0x00000000, 0x00000000,
>> +	0x0dc70000, 0x00003239, 0x00000000, 0x00000000,
>> +	0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
>> +	0x00000000, 0x087f0000, 0x00003781, 0x00000000,
>> +	0x00000000, 0x00000000, 0x399a0666, 0x00000000,
>> +	0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
>> +	0x00000000, 0x00000000, 0x00000000, 0x3cc4033c,
>> +	0x00000000, 0x00000000, 0x00000000, 0x021f0000,
>> +};
>> +
>> +#define H_COEF_SIZE (sizeof(horkernel) / sizeof(u32))
>> +
>> +static const u32 verkernel[] = {
>> +	0x00000000, 0x20000000, 0x00002000, 0x00000000,
>> +	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
>> +	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
>> +	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
>> +	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
>> +	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
>> +	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
>> +	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
>> +	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
>> +	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
>> +	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
>> +	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
>> +	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
>> +	0x00000000, 0x00000000, 0x00000000, 0x01470000,
>> +	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
>> +	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
>> +	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
>> +	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
>> +	0x00000000, 0x00000000, 0x00004000, 0x00000000,
>> +	0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab,
>> +	0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
>> +	0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> +};
>> +
>> +#define V_COEF_SIZE (sizeof(verkernel) / sizeof(u32))
>> +
>> +/*
>> + * RGB 709->2020 conversion parameters
>> + */
>> +static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = {
>> +	10279,	5395,	709,
>> +	1132,	15065,	187,
>> +	269,	1442,	14674
>> +};
>> +
>> +/*
>> + * YUV601 to RGB conversion parameters
>> + * YUV2RGB[0]  - [8] : C0 - C8;
>> + * YUV2RGB[9]  - [11]: D0 - D2;
>> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
>> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
>> + */
>> +static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
>> +	1196,	0,			1640,	1196,
>> +	-404,	-836,		1196,	2076,
>> +	0,		-916224,	558336,	-1202944,
>> +	64,		 940,		64,		960
>> +};
>> +
>> +/*
>> + * YUV709 to RGB conversion parameters
>> + * YUV2RGB[0]  - [8] : C0 - C8;
>> + * YUV2RGB[9]  - [11]: D0 - D2;
>> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
>> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
>> + */
>> +static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
>> +	1196,		0,		1844,	1196,
>> +	-220,		-548,	1196,	2172,
>> +	0,			-1020672, 316672,  -1188608,
>> +	64,			940,		64,		960
>> +};
>> +
>> +/*
>> + * YUV2020 to RGB conversion parameters
>> + * YUV2RGB[0]  - [8] : C0 - C8;
>> + * YUV2RGB[9]  - [11]: D0 - D2;
>> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
>> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
>> + */
>> +static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
>> +	1196, 0, 1724, 1196,
>> +	-192, -668, 1196, 2200,
>> +	0, -959232, 363776, -1202944,
>> +	64, 940, 64, 960
>> +};
>> +
>> +/*
>> + * RGB to YUV2020 conversion parameters
>> + * RGB2YUV[0] - [8] : C0 - C8;
>> + * RGB2YUV[9] - [11]: D0 - D2;
>> + */
>> +static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = {
>> +	230,	594,	52,
>> +	-125,  -323,	448,
>> +	448,   -412,   -36,
>> +	64,		512,	512
>> +};
>> +
>> +/*
>> + * Degamma table for 709 color space data.
>> + */
>> +static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
>> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005,
>> +	0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024,
>> +	0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064,
>> +	0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce,
>> +	0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166,
>> +	0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230,
>> +	0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331,
>> +	0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c,
>> +	0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
>> +	0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d,
>> +	0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999,
>> +	0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb,
>> +	0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65,
>> +	0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a,
>> +	0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b,
>> +	0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc,
>> +	0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e,
>> +	0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
>> +	0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e,
>> +	0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf,
>> +	0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea,
>> +	0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f,
>> +	0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930,
>> +	0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f,
>> +	0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee,
>> +	0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde,
>> +	0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
>> +	0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9,
>> +	0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406,
>> +	0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a,
>> +	0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576,
>> +	0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd,
>> +	0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
>> +};
>> +
>> +/*
>> + * Degamma table for 2020 color space data.
>> + */
>> +static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
>> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
>> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
>> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
>> +	0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
>> +	0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
>> +	0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006,
>> +	0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a,
>> +	0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f,
>> +	0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
>> +	0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024,
>> +	0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035,
>> +	0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d,
>> +	0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d,
>> +	0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098,
>> +	0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3,
>> +	0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123,
>> +	0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d,
>> +	0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
>> +	0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6,
>> +	0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1,
>> +	0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d,
>> +	0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6,
>> +	0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920,
>> +	0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a,
>> +	0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033,
>> +	0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593,
>> +	0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
>> +	0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656,
>> +	0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332,
>> +	0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480,
>> +	0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1,
>> +	0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99,
>> +	0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
>> +};
>> +
>> +/* one is for primary plane and the other is for all overlay planes */
>> +static const struct dc_hw_plane_reg dc_plane_reg[] = {
>> +	{
>> +	.y_address		= DC_FRAMEBUFFER_ADDRESS,
>> +	.u_address		= DC_FRAMEBUFFER_U_ADDRESS,
>> +	.v_address		= DC_FRAMEBUFFER_V_ADDRESS,
>> +	.y_stride		= DC_FRAMEBUFFER_STRIDE,
>> +	.u_stride		= DC_FRAMEBUFFER_U_STRIDE,
>> +	.v_stride		= DC_FRAMEBUFFER_V_STRIDE,
>> +	.size			= DC_FRAMEBUFFER_SIZE,
>> +	.top_left		= DC_FRAMEBUFFER_TOP_LEFT,
>> +	.bottom_right	= DC_FRAMEBUFFER_BOTTOM_RIGHT,
>> +	.scale_factor_x			= DC_FRAMEBUFFER_SCALE_FACTOR_X,
>> +	.scale_factor_y			= DC_FRAMEBUFFER_SCALE_FACTOR_Y,
>> +	.h_filter_coef_index	= DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
>> +	.h_filter_coef_data		= DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
>> +	.v_filter_coef_index	= DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
>> +	.v_filter_coef_data		= DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
>> +	.init_offset			= DC_FRAMEBUFFER_INIT_OFFSET,
>> +	.color_key				= DC_FRAMEBUFFER_COLOR_KEY,
>> +	.color_key_high			= DC_FRAMEBUFFER_COLOR_KEY_HIGH,
>> +	.clear_value			= DC_FRAMEBUFFER_CLEAR_VALUE,
>> +	.color_table_index		= DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
>> +	.color_table_data		= DC_FRAMEBUFFER_COLOR_TABLE_DATA,
>> +	.scale_config			= DC_FRAMEBUFFER_SCALE_CONFIG,
>> +	.water_mark				= DC_FRAMEBUFFER_WATER_MARK,
>> +	.degamma_index			= DC_FRAMEBUFFER_DEGAMMA_INDEX,
>> +	.degamma_data			= DC_FRAMEBUFFER_DEGAMMA_DATA,
>> +	.degamma_ex_data		= DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
>> +	.src_global_color		= DC_FRAMEBUFFER_SRC_GLOBAL_COLOR,
>> +	.dst_global_color		= DC_FRAMEBUFFER_DST_GLOBAL_COLOR,
>> +	.blend_config			= DC_FRAMEBUFFER_BLEND_CONFIG,
>> +	.roi_origin				= DC_FRAMEBUFFER_ROI_ORIGIN,
>> +	.roi_size				= DC_FRAMEBUFFER_ROI_SIZE,
>> +	.yuv_to_rgb_coef0			= DC_FRAMEBUFFER_YUVTORGB_COEF0,
>> +	.yuv_to_rgb_coef1			= DC_FRAMEBUFFER_YUVTORGB_COEF1,
>> +	.yuv_to_rgb_coef2			= DC_FRAMEBUFFER_YUVTORGB_COEF2,
>> +	.yuv_to_rgb_coef3			= DC_FRAMEBUFFER_YUVTORGB_COEF3,
>> +	.yuv_to_rgb_coef4			= DC_FRAMEBUFFER_YUVTORGB_COEF4,
>> +	.yuv_to_rgb_coefd0			= DC_FRAMEBUFFER_YUVTORGB_COEFD0,
>> +	.yuv_to_rgb_coefd1			= DC_FRAMEBUFFER_YUVTORGB_COEFD1,
>> +	.yuv_to_rgb_coefd2			= DC_FRAMEBUFFER_YUVTORGB_COEFD2,
>> +	.y_clamp_bound				= DC_FRAMEBUFFER_Y_CLAMP_BOUND,
>> +	.uv_clamp_bound				= DC_FRAMEBUFFER_UV_CLAMP_BOUND,
>> +	.rgb_to_rgb_coef0			= DC_FRAMEBUFFER_RGBTORGB_COEF0,
>> +	.rgb_to_rgb_coef1			= DC_FRAMEBUFFER_RGBTORGB_COEF1,
>> +	.rgb_to_rgb_coef2			= DC_FRAMEBUFFER_RGBTORGB_COEF2,
>> +	.rgb_to_rgb_coef3			= DC_FRAMEBUFFER_RGBTORGB_COEF3,
>> +	.rgb_to_rgb_coef4			= DC_FRAMEBUFFER_RGBTORGB_COEF4,
>> +	},
>> +	{
>> +	.y_address		= DC_OVERLAY_ADDRESS,
>> +	.u_address		= DC_OVERLAY_U_ADDRESS,
>> +	.v_address		= DC_OVERLAY_V_ADDRESS,
>> +	.y_stride		= DC_OVERLAY_STRIDE,
>> +	.u_stride		= DC_OVERLAY_U_STRIDE,
>> +	.v_stride		= DC_OVERLAY_V_STRIDE,
>> +	.size			= DC_OVERLAY_SIZE,
>> +	.top_left		= DC_OVERLAY_TOP_LEFT,
>> +	.bottom_right	= DC_OVERLAY_BOTTOM_RIGHT,
>> +	.scale_factor_x	= DC_OVERLAY_SCALE_FACTOR_X,
>> +	.scale_factor_y	= DC_OVERLAY_SCALE_FACTOR_Y,
>> +	.h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
>> +	.h_filter_coef_data  = DC_OVERLAY_H_FILTER_COEF_DATA,
>> +	.v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
>> +	.v_filter_coef_data  = DC_OVERLAY_V_FILTER_COEF_DATA,
>> +	.init_offset		 = DC_OVERLAY_INIT_OFFSET,
>> +	.color_key			 = DC_OVERLAY_COLOR_KEY,
>> +	.color_key_high			= DC_OVERLAY_COLOR_KEY_HIGH,
>> +	.clear_value		 = DC_OVERLAY_CLEAR_VALUE,
>> +	.color_table_index	 = DC_OVERLAY_COLOR_TABLE_INDEX,
>> +	.color_table_data	 = DC_OVERLAY_COLOR_TABLE_DATA,
>> +	.scale_config		 = DC_OVERLAY_SCALE_CONFIG,
>> +	.water_mark				= DC_OVERLAY_WATER_MARK,
>> +	.degamma_index		 = DC_OVERLAY_DEGAMMA_INDEX,
>> +	.degamma_data		 = DC_OVERLAY_DEGAMMA_DATA,
>> +	.degamma_ex_data	 = DC_OVERLAY_DEGAMMA_EX_DATA,
>> +	.src_global_color	 = DC_OVERLAY_SRC_GLOBAL_COLOR,
>> +	.dst_global_color	 = DC_OVERLAY_DST_GLOBAL_COLOR,
>> +	.blend_config		 = DC_OVERLAY_BLEND_CONFIG,
>> +	.roi_origin				= DC_OVERLAY_ROI_ORIGIN,
>> +	.roi_size				= DC_OVERLAY_ROI_SIZE,
>> +	.yuv_to_rgb_coef0		 = DC_OVERLAY_YUVTORGB_COEF0,
>> +	.yuv_to_rgb_coef1		 = DC_OVERLAY_YUVTORGB_COEF1,
>> +	.yuv_to_rgb_coef2		 = DC_OVERLAY_YUVTORGB_COEF2,
>> +	.yuv_to_rgb_coef3		 = DC_OVERLAY_YUVTORGB_COEF3,
>> +	.yuv_to_rgb_coef4			= DC_OVERLAY_YUVTORGB_COEF4,
>> +	.yuv_to_rgb_coefd0			= DC_OVERLAY_YUVTORGB_COEFD0,
>> +	.yuv_to_rgb_coefd1			= DC_OVERLAY_YUVTORGB_COEFD1,
>> +	.yuv_to_rgb_coefd2			= DC_OVERLAY_YUVTORGB_COEFD2,
>> +	.y_clamp_bound		 = DC_OVERLAY_Y_CLAMP_BOUND,
>> +	.uv_clamp_bound		 = DC_OVERLAY_UV_CLAMP_BOUND,
>> +	.rgb_to_rgb_coef0		 = DC_OVERLAY_RGBTORGB_COEF0,
>> +	.rgb_to_rgb_coef1		 = DC_OVERLAY_RGBTORGB_COEF1,
>> +	.rgb_to_rgb_coef2		 = DC_OVERLAY_RGBTORGB_COEF2,
>> +	.rgb_to_rgb_coef3		 = DC_OVERLAY_RGBTORGB_COEF3,
>> +	.rgb_to_rgb_coef4		 = DC_OVERLAY_RGBTORGB_COEF4,
>> +	},
>> +};
>> +
>> +static const u32 primary_overlay_format0[] = {
>> +	DRM_FORMAT_XRGB4444,
>> +	DRM_FORMAT_XBGR4444,
>> +	DRM_FORMAT_RGBX4444,
>> +	DRM_FORMAT_BGRX4444,
>> +	DRM_FORMAT_ARGB4444,
>> +	DRM_FORMAT_ABGR4444,
>> +	DRM_FORMAT_RGBA4444,
>> +	DRM_FORMAT_BGRA4444,
>> +	DRM_FORMAT_XRGB1555,
>> +	DRM_FORMAT_XBGR1555,
>> +	DRM_FORMAT_RGBX5551,
>> +	DRM_FORMAT_BGRX5551,
>> +	DRM_FORMAT_ARGB1555,
>> +	DRM_FORMAT_ABGR1555,
>> +	DRM_FORMAT_RGBA5551,
>> +	DRM_FORMAT_BGRA5551,
>> +	DRM_FORMAT_RGB565,
>> +	DRM_FORMAT_BGR565,
>> +	DRM_FORMAT_XRGB8888,
>> +	DRM_FORMAT_XBGR8888,
>> +	DRM_FORMAT_RGBX8888,
>> +	DRM_FORMAT_BGRX8888,
>> +	DRM_FORMAT_ARGB8888,
>> +	DRM_FORMAT_ABGR8888,
>> +	DRM_FORMAT_RGBA8888,
>> +	DRM_FORMAT_BGRA8888,
>> +	DRM_FORMAT_ARGB2101010,
>> +	DRM_FORMAT_ABGR2101010,
>> +	DRM_FORMAT_RGBA1010102,
>> +	DRM_FORMAT_BGRA1010102,
>> +	DRM_FORMAT_YUYV,
>> +	DRM_FORMAT_YVYU,
>> +	DRM_FORMAT_UYVY,
>> +	DRM_FORMAT_VYUY,
>> +	DRM_FORMAT_YVU420,
>> +	DRM_FORMAT_YUV420,
>> +	DRM_FORMAT_NV12,
>> +	DRM_FORMAT_NV21,
>> +	DRM_FORMAT_NV16,
>> +	DRM_FORMAT_NV61,
>> +	DRM_FORMAT_P010,
>> +};
>> +
>> +static const u32 primary_overlay_format1[] = {
>> +	DRM_FORMAT_XRGB8888,
>> +	DRM_FORMAT_XBGR8888,
>> +	DRM_FORMAT_RGBX8888,
>> +	DRM_FORMAT_BGRX8888,
>> +	DRM_FORMAT_ARGB8888,
>> +	DRM_FORMAT_ABGR8888,
>> +	DRM_FORMAT_RGBA8888,
>> +	DRM_FORMAT_BGRA8888,
>> +	DRM_FORMAT_ARGB2101010,
>> +	DRM_FORMAT_ABGR2101010,
>> +	DRM_FORMAT_RGBA1010102,
>> +	DRM_FORMAT_BGRA1010102,
>> +	DRM_FORMAT_NV12,
>> +	DRM_FORMAT_NV21,
>> +	DRM_FORMAT_YUV444,
>> +};
>> +
>> +static const u32 cursor_formats[] = {
>> +	DRM_FORMAT_ARGB8888
>> +};
>> +
>> +static const u64 format_modifier0[] = {
>> +	DRM_FORMAT_MOD_LINEAR,
>> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8),
>> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8),
>> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X8),
>> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X4),
>> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4),
>> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8),
>> +	DRM_FORMAT_MOD_INVALID
>> +};
>> +
>> +static const u64 secondary_format_modifiers[] = {
>> +	DRM_FORMAT_MOD_LINEAR,
>> +	DRM_FORMAT_MOD_INVALID
>> +};
>> +
>> +#define FRAC_16_16(mult, div)	 (((mult) << 16) / (div))
>> +
>> +static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = {
>> +	{
>> +		/* DC_REV_0 */
>> +		{
>> +		.name			= "Primary",
> 
> I'd say that the indentation is strange here.
> It might be more scalable to split a single array into per-generation 
> data structures and reference them directly from the info struct.
> 
>> +		.id				= PRIMARY_PLANE_0,
>> +		.type			= DRM_PLANE_TYPE_PRIMARY,
>> +		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
>> +		.formats		= primary_overlay_format0,
>> +		.num_modifiers	= ARRAY_SIZE(format_modifier0),
>> +		.modifiers		= format_modifier0,
>> +		.min_width		= 0,
>> +		.min_height		= 0,
>> +		.max_width		= 4096,
>> +		.max_height		= 4096,
>> +		.rotation		= DRM_MODE_ROTATE_0 |
>> +							DRM_MODE_ROTATE_90 |
>> +							DRM_MODE_ROTATE_180 |
>> +							DRM_MODE_ROTATE_270 |
>> +							DRM_MODE_REFLECT_X |
>> +							DRM_MODE_REFLECT_Y,
>> +		.blend_mode		= BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +							BIT(DRM_MODE_BLEND_PREMULTI) |
>> +							BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 0,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name			= "Overlay",
>> +		.id				= OVERLAY_PLANE_0,
>> +		.type			= DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
>> +		.formats		= primary_overlay_format0,
>> +		.num_modifiers	= ARRAY_SIZE(format_modifier0),
>> +		.modifiers		= format_modifier0,
>> +		.min_width		= 0,
>> +		.min_height		= 0,
>> +		.max_width		= 4096,
>> +		.max_height		= 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 1,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name			= "Overlay_1",
>> +		.id				= OVERLAY_PLANE_1,
>> +		.type			= DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats	= ARRAY_SIZE(primary_overlay_format0),
>> +		.formats		= primary_overlay_format0,
>> +		.num_modifiers	= ARRAY_SIZE(secondary_format_modifiers),
>> +		.modifiers		= secondary_format_modifiers,
>> +		.min_width		= 0,
>> +		.min_height		= 0,
>> +		.max_width		= 4096,
>> +		.max_height		= 4096,
>> +		.rotation		= 0,
>> +		.blend_mode		= BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +							BIT(DRM_MODE_BLEND_PREMULTI) |
>> +							BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 2,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Primary_1",
>> +		.id				= PRIMARY_PLANE_1,
>> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
>> +		.formats	 = primary_overlay_format0,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 3,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay_2",
>> +		.id				= OVERLAY_PLANE_2,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
>> +		.formats	 = primary_overlay_format0,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 4,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay_3",
>> +		.id			= OVERLAY_PLANE_3,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
>> +		.formats	 = primary_overlay_format0,
>> +		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
>> +		.modifiers	 = secondary_format_modifiers,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = 0,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 5,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Cursor",
>> +		.id				= CURSOR_PLANE_0,
>> +		.type		 = DRM_PLANE_TYPE_CURSOR,
>> +		.num_formats = ARRAY_SIZE(cursor_formats),
>> +		.formats	 = cursor_formats,
>> +		.num_modifiers = 0,
>> +		.modifiers	 = NULL,
>> +		.min_width	 = 32,
>> +		.min_height  = 32,
>> +		.max_width	 = 64,
>> +		.max_height  = 64,
>> +		.rotation	 = 0,
>> +		.degamma_size = 0,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 255,
>> +		.watermark	 = false,
>> +		.color_mgmt  = false,
>> +		.roi		 = false,
>> +		},
>> +		{
>> +		.name		 = "Cursor_1",
>> +		.id				= CURSOR_PLANE_1,
>> +		.type		 = DRM_PLANE_TYPE_CURSOR,
>> +		.num_formats = ARRAY_SIZE(cursor_formats),
>> +		.formats	 = cursor_formats,
>> +		.num_modifiers = 0,
>> +		.modifiers	 = NULL,
>> +		.min_width	 = 32,
>> +		.min_height  = 32,
>> +		.max_width	 = 64,
>> +		.max_height  = 64,
>> +		.rotation	 = 0,
>> +		.degamma_size = 0,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 255,
>> +		.watermark	 = false,
>> +		.color_mgmt  = false,
>> +		.roi		 = false,
>> +		},
>> +	},
>> +	{
>> +		/* DC_REV_1 */
>> +		{
>> +		.name		 = "Primary",
>> +		.id				= PRIMARY_PLANE_0,
>> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
>> +		.formats	 = primary_overlay_format0,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 0,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay",
>> +		.id				= OVERLAY_PLANE_0,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
>> +		.formats	 = primary_overlay_format0,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 1,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Primary_1",
>> +		.id				= PRIMARY_PLANE_1,
>> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
>> +		.formats	 = primary_overlay_format0,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 2,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay_2",
>> +		.id				= OVERLAY_PLANE_2,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format0),
>> +		.formats	 = primary_overlay_format0,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 3,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Cursor",
>> +		.id				= CURSOR_PLANE_0,
>> +		.type		 = DRM_PLANE_TYPE_CURSOR,
>> +		.num_formats = ARRAY_SIZE(cursor_formats),
>> +		.formats	 = cursor_formats,
>> +		.num_modifiers = 0,
>> +		.modifiers	 = NULL,
>> +		.min_width	 = 32,
>> +		.min_height  = 32,
>> +		.max_width	 = 64,
>> +		.max_height  = 64,
>> +		.rotation	 = 0,
>> +		.degamma_size = 0,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 255,
>> +		.watermark	 = false,
>> +		.color_mgmt  = false,
>> +		.roi		 = false,
>> +		},
>> +		{
>> +		.name		 = "Cursor_1",
>> +		.id				= CURSOR_PLANE_1,
>> +		.type		 = DRM_PLANE_TYPE_CURSOR,
>> +		.num_formats = ARRAY_SIZE(cursor_formats),
>> +		.formats	 = cursor_formats,
>> +		.num_modifiers = 0,
>> +		.modifiers	 = NULL,
>> +		.min_width	 = 32,
>> +		.min_height  = 32,
>> +		.max_width	 = 64,
>> +		.max_height  = 64,
>> +		.rotation	 = 0,
>> +		.degamma_size = 0,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 255,
>> +		.watermark	 = false,
>> +		.color_mgmt  = false,
>> +		.roi		 = false,
>> +		},
>> +	},
>> +	{
>> +		/* DC_REV_2 */
>> +		{
>> +		.name		 = "Primary",
>> +		.id				= PRIMARY_PLANE_0,
>> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
>> +		.formats	 = primary_overlay_format1,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 0,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay",
>> +		.id				= OVERLAY_PLANE_0,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
>> +		.formats	 = primary_overlay_format1,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 1,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay_1",
>> +		.id				= OVERLAY_PLANE_1,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
>> +		.formats	 = primary_overlay_format1,
>> +		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
>> +		.modifiers	 = secondary_format_modifiers,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = 0,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 2,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Primary_1",
>> +		.id				= PRIMARY_PLANE_1,
>> +		.type		 = DRM_PLANE_TYPE_PRIMARY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
>> +		.formats	 = primary_overlay_format1,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 3,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay_2",
>> +		.id				= OVERLAY_PLANE_2,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
>> +		.formats	 = primary_overlay_format1,
>> +		.num_modifiers = ARRAY_SIZE(format_modifier0),
>> +		.modifiers	 = format_modifier0,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = DRM_MODE_ROTATE_0 |
>> +					   DRM_MODE_ROTATE_90 |
>> +					   DRM_MODE_ROTATE_180 |
>> +					   DRM_MODE_ROTATE_270 |
>> +					   DRM_MODE_REFLECT_X |
>> +					   DRM_MODE_REFLECT_Y,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = FRAC_16_16(1, 3),
>> +		.max_scale	 = FRAC_16_16(10, 1),
>> +		.zpos		 = 4,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Overlay_3",
>> +		.id				= OVERLAY_PLANE_3,
>> +		.type		 = DRM_PLANE_TYPE_OVERLAY,
>> +		.num_formats = ARRAY_SIZE(primary_overlay_format1),
>> +		.formats	 = primary_overlay_format1,
>> +		.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
>> +		.modifiers	 = secondary_format_modifiers,
>> +		.min_width	 = 0,
>> +		.min_height  = 0,
>> +		.max_width	 = 4096,
>> +		.max_height  = 4096,
>> +		.rotation	 = 0,
>> +		.blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> +					   BIT(DRM_MODE_BLEND_PREMULTI) |
>> +					   BIT(DRM_MODE_BLEND_COVERAGE),
>> +		.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> +						  BIT(DRM_COLOR_YCBCR_BT2020),
>> +		.degamma_size	= DEGAMMA_SIZE,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 5,
>> +		.watermark	 = true,
>> +		.color_mgmt  = true,
>> +		.roi		 = true,
>> +		},
>> +		{
>> +		.name		 = "Cursor",
>> +		.id				= CURSOR_PLANE_0,
>> +		.type		 = DRM_PLANE_TYPE_CURSOR,
>> +		.num_formats = ARRAY_SIZE(cursor_formats),
>> +		.formats	 = cursor_formats,
>> +		.num_modifiers = 0,
>> +		.modifiers	 = NULL,
>> +		.min_width	 = 32,
>> +		.min_height  = 32,
>> +		.max_width	 = 64,
>> +		.max_height  = 64,
>> +		.rotation	 = 0,
>> +		.degamma_size = 0,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 255,
>> +		.watermark	 = false,
>> +		.color_mgmt  = false,
>> +		.roi		 = false,
>> +		},
>> +		{
>> +		.name		 = "Cursor_1",
>> +		.id				= CURSOR_PLANE_1,
>> +		.type		 = DRM_PLANE_TYPE_CURSOR,
>> +		.num_formats = ARRAY_SIZE(cursor_formats),
>> +		.formats	 = cursor_formats,
>> +		.num_modifiers = 0,
>> +		.modifiers	 = NULL,
>> +		.min_width	 = 32,
>> +		.min_height  = 32,
>> +		.max_width	 = 64,
>> +		.max_height  = 64,
>> +		.rotation	 = 0,
>> +		.degamma_size = 0,
>> +		.min_scale	 = DRM_PLANE_NO_SCALING,
>> +		.max_scale	 = DRM_PLANE_NO_SCALING,
>> +		.zpos		 = 255,
>> +		.watermark	 = false,
>> +		.color_mgmt  = false,
>> +		.roi		 = false,
>> +		},
>> +	},
>> +};
>> +
>> +static const struct vs_dc_info dc_info[] = {
> 
> Again, please split into per-generation instances.
> 
>> +	{
>> +		/* DC_REV_0 */
>> +		.name			= "DC8200",
>> +		.panel_num		= 2,
>> +		.plane_num		= 8,
>> +		.planes			= dc_hw_planes[DC_REV_0],
>> +		.layer_num		= 6,
>> +		.max_bpc		= 10,
>> +		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
>> +						  DRM_COLOR_FORMAT_YCBCR444 |
>> +						  DRM_COLOR_FORMAT_YCBCR422 |
>> +						  DRM_COLOR_FORMAT_YCBCR420,
>> +		.gamma_size		= GAMMA_EX_SIZE,
>> +		.gamma_bits		= 12,
>> +		.pitch_alignment	= 128,
>> +		.pipe_sync		= false,
>> +		.background		= true,
>> +		.panel_sync		= true,
>> +		.cap_dec		= true,
>> +	},
>> +	{
>> +		/* DC_REV_1 */
>> +		.name			= "DC8200",
>> +		.panel_num		= 2,
>> +		.plane_num		= 6,
>> +		.planes			= dc_hw_planes[DC_REV_1],
>> +		.layer_num		= 4,
>> +		.max_bpc		= 10,
>> +		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
>> +						  DRM_COLOR_FORMAT_YCBCR444 |
>> +						  DRM_COLOR_FORMAT_YCBCR422 |
>> +						  DRM_COLOR_FORMAT_YCBCR420,
>> +		.gamma_size		= GAMMA_EX_SIZE,
>> +		.gamma_bits		= 12,
>> +		.pitch_alignment	= 128,
>> +		.pipe_sync		= false,
>> +		.background		= true,
>> +		.panel_sync		= true,
>> +		.cap_dec		= true,
>> +	},
>> +	{
>> +		/* DC_REV_2 */
>> +		.name			= "DC8200",
>> +		.panel_num		= 2,
>> +		.plane_num		= 8,
>> +		.planes			= dc_hw_planes[DC_REV_2],
>> +		.layer_num		= 6,
>> +		.max_bpc		= 10,
>> +		.color_formats	= DRM_COLOR_FORMAT_RGB444 |
>> +						  DRM_COLOR_FORMAT_YCBCR444 |
>> +						  DRM_COLOR_FORMAT_YCBCR422 |
>> +						  DRM_COLOR_FORMAT_YCBCR420,
>> +		.gamma_size		= GAMMA_EX_SIZE,
>> +		.gamma_bits		= 12,
>> +		.pitch_alignment	= 128,
>> +		.pipe_sync		= false,
>> +		.background		= true,
>> +		.panel_sync		= true,
>> +		.cap_dec		= false,
>> +	},
>> +};
>> +
>> +static const struct dc_hw_funcs hw_func;
>> +
>> +static inline u32 hi_read(struct dc_hw *hw, u32 reg)
>> +{
>> +	return readl(hw->hi_base + reg);
>> +}
>> +
>> +static inline void hi_write(struct dc_hw *hw, u32 reg, u32 value)
>> +{
>> +	writel(value, hw->hi_base + reg);
>> +}
>> +
>> +static inline void dc_write(struct dc_hw *hw, u32 reg, u32 value)
>> +{
>> +	writel(value, hw->reg_base + reg - DC_REG_BASE);
>> +}
>> +
>> +static inline u32 dc_read(struct dc_hw *hw, u32 reg)
>> +{
>> +	u32 value = readl(hw->reg_base + reg - DC_REG_BASE);
>> +
>> +	return value;
>> +}
>> +
>> +static inline void dc_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear)
>> +{
>> +	u32 value = dc_read(hw, reg);
>> +
>> +	value &= ~clear;
>> +	value |= set;
>> +	dc_write(hw, reg, value);
>> +}
>> +
>> +static void load_default_filter(struct dc_hw *hw,
>> +				const struct dc_hw_plane_reg *reg, u32 offset)
>> +{
>> +	u8 i;
>> +
>> +	dc_write(hw, reg->scale_config + offset, 0x33);
>> +	dc_write(hw, reg->init_offset + offset, 0x80008000);
>> +	dc_write(hw, reg->h_filter_coef_index + offset, 0x00);
>> +	for (i = 0; i < H_COEF_SIZE; i++)
>> +		dc_write(hw, reg->h_filter_coef_data + offset, horkernel[i]);
>> +
>> +	dc_write(hw, reg->v_filter_coef_index + offset, 0x00);
>> +	for (i = 0; i < V_COEF_SIZE; i++)
>> +		dc_write(hw, reg->v_filter_coef_data + offset, verkernel[i]);
>> +}
>> +
>> +static void load_rgb_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
>> +			    u32 offset, u16 *table)
>> +{
>> +	dc_write(hw, reg->rgb_to_rgb_coef0 + offset, table[0] | (table[1] << 16));
>> +	dc_write(hw, reg->rgb_to_rgb_coef1 + offset, table[2] | (table[3] << 16));
>> +	dc_write(hw, reg->rgb_to_rgb_coef2 + offset, table[4] | (table[5] << 16));
>> +	dc_write(hw, reg->rgb_to_rgb_coef3 + offset, table[6] | (table[7] << 16));
>> +	dc_write(hw, reg->rgb_to_rgb_coef4 + offset, table[8]);
>> +}
>> +
>> +static void load_yuv_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
>> +			    u32 offset, s32 *table)
>> +{
>> +	dc_write(hw, reg->yuv_to_rgb_coef0 + offset,
>> +		 (0xFFFF & table[0]) | (table[1] << 16));
>> +	dc_write(hw, reg->yuv_to_rgb_coef1 + offset,
>> +		 (0xFFFF & table[2]) | (table[3] << 16));
>> +	dc_write(hw, reg->yuv_to_rgb_coef2 + offset,
>> +		 (0xFFFF & table[4]) | (table[5] << 16));
>> +	dc_write(hw, reg->yuv_to_rgb_coef3 + offset,
>> +		 (0xFFFF & table[6]) | (table[7] << 16));
>> +	dc_write(hw, reg->yuv_to_rgb_coef4 + offset, table[8]);
>> +	dc_write(hw, reg->yuv_to_rgb_coefd0 + offset, table[9]);
>> +	dc_write(hw, reg->yuv_to_rgb_coefd1 + offset, table[10]);
>> +	dc_write(hw, reg->yuv_to_rgb_coefd2 + offset, table[11]);
>> +	dc_write(hw, reg->y_clamp_bound + offset, table[12] | (table[13] << 16));
>> +	dc_write(hw, reg->uv_clamp_bound + offset, table[14] | (table[15] << 16));
>> +}
>> +
>> +static void load_rgb_to_yuv(struct dc_hw *hw, u32 offset, s16 *table)
>> +{
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF0 + offset,
>> +		 table[0] | (table[1] << 16));
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF1 + offset,
>> +		 table[2] | (table[3] << 16));
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF2 + offset,
>> +		 table[4] | (table[5] << 16));
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF3 + offset,
>> +		 table[6] | (table[7] << 16));
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF4 + offset, table[8]);
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD0 + offset, table[9]);
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD1 + offset, table[10]);
>> +	dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD2 + offset, table[11]);
>> +}
>> +
>> +static bool is_rgb(enum dc_hw_color_format format)
>> +{
>> +	switch (format) {
>> +	case FORMAT_X4R4G4B4:
>> +	case FORMAT_A4R4G4B4:
>> +	case FORMAT_X1R5G5B5:
>> +	case FORMAT_A1R5G5B5:
>> +	case FORMAT_R5G6B5:
>> +	case FORMAT_X8R8G8B8:
>> +	case FORMAT_A8R8G8B8:
>> +	case FORMAT_A2R10G10B10:
>> +		return true;
>> +	default:
>> +		return false;
>> +	}
>> +}
>> +
>> +static void load_degamma_table(struct dc_hw *hw,
>> +			       const struct dc_hw_plane_reg *reg,
>> +			       u32 offset, u16 *table)
>> +{
>> +	u16 i;
>> +	u32 value;
>> +
>> +	dc_write(hw, reg->degamma_index + offset, 0);
>> +
>> +	for (i = 0; i < DEGAMMA_SIZE; i++) {
>> +		value = table[i] | (table[i] << 16);
>> +		dc_write(hw, reg->degamma_data + offset, value);
>> +		dc_write(hw, reg->degamma_ex_data + offset, table[i]);
>> +	}
>> +}
>> +
>> +static u32 get_addr_offset(u32 id)
>> +{
>> +	u32 offset = 0;
>> +
>> +	switch (id) {
>> +	case PRIMARY_PLANE_1:
>> +	case OVERLAY_PLANE_1:
>> +		offset = 0x04;
>> +		break;
>> +	case OVERLAY_PLANE_2:
>> +		offset = 0x08;
>> +		break;
>> +	case OVERLAY_PLANE_3:
>> +		offset = 0x0C;
>> +		break;
>> +	default:
>> +		break;
>> +	}
>> +
>> +	return offset;
>> +}
>> +
>> +int dc_hw_init(struct dc_hw *hw)
>> +{
>> +	u8 i, id, panel_num, layer_num;
>> +	u32 offset;
>> +	u32 revision = hi_read(hw, DC_HW_REVISION);
>> +	u32 cid = hi_read(hw, DC_HW_CHIP_CID);
>> +	const struct dc_hw_plane_reg *reg;
>> +
>> +	switch (revision) {
>> +	case 0x5720:
>> +		hw->rev = DC_REV_0;
>> +		break;
>> +	case 0x5721:
>> +		switch (cid) {
>> +		case 0x30B:
>> +			hw->rev = DC_REV_1;
>> +			break;
>> +		case 0x310:
>> +			hw->rev = DC_REV_2;
>> +			break;
>> +		default:
>> +			hw->rev = DC_REV_0;
>> +			break;
>> +		}
>> +		break;
>> +	default:
>> +		return -ENXIO;
>> +	}
> 
> We (drm/msm) had code like this for a while (and autodetection of the HW 
> ID). And nowadays we are migrating to of_device_get_match_data and 
> per-SoC compat names. It is just easier to handle, it allows one to 
> specify device more precisely and also it simplifies DT schema.
> 
>> +
>> +	hw->info = (struct vs_dc_info *)&dc_info[hw->rev];
>> +	hw->func = (struct dc_hw_funcs *)&hw_func;
>> +
>> +	layer_num = hw->info->layer_num;
>> +	for (i = 0; i < layer_num; i++) {
>> +		id = hw->info->planes[i].id;
>> +		offset = get_addr_offset(id);
>> +		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
>> +			reg = &dc_plane_reg[0];
>> +		else
>> +			reg = &dc_plane_reg[1];
>> +
>> +		load_default_filter(hw, reg, offset);
>> +		load_rgb_to_rgb(hw, reg, offset, RGB2RGB);
>> +	}
>> +
>> +	panel_num = hw->info->panel_num;
>> +	for (i = 0; i < panel_num; i++) {
>> +		offset = i << 2;
>> +
>> +		load_rgb_to_yuv(hw, offset, RGB2YUV);
>> +		dc_write(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0x111);
>> +
>> +		offset = i ? DC_CURSOR_OFFSET : 0;
>> +		dc_write(hw, DC_CURSOR_BACKGROUND + offset, 0x00FFFFFF);
>> +		dc_write(hw, DC_CURSOR_FOREGROUND + offset, 0x00AAAAAA);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +void dc_hw_deinit(struct dc_hw *hw)
>> +{
>> +	/* Nothing to do */
>> +}
>> +
>> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
>> +			struct dc_hw_fb *fb, struct dc_hw_scale *scale,
>> +			struct dc_hw_position *pos, struct dc_hw_blend *blend)
>> +{
>> +	struct dc_hw_plane *plane = &hw->plane[id];
>> +
>> +	if (plane) {
>> +		if (fb) {
>> +			if (!fb->enable)
>> +				plane->fb.enable = false;
>> +			else
>> +				memcpy(&plane->fb, fb,
>> +				       sizeof(*fb) - sizeof(fb->dirty));
>> +			plane->fb.dirty = true;
>> +		}
>> +		if (scale) {
>> +			memcpy(&plane->scale, scale,
>> +			       sizeof(*scale) - sizeof(scale->dirty));
>> +			plane->scale.dirty = true;
>> +		}
>> +		if (pos) {
>> +			memcpy(&plane->pos, pos,
>> +			       sizeof(*pos) - sizeof(pos->dirty));
>> +			plane->pos.dirty = true;
>> +		}
>> +		if (blend) {
>> +			memcpy(&plane->blend, blend,
>> +			       sizeof(*blend) - sizeof(blend->dirty));
>> +			plane->blend.dirty = true;
>> +		}
>> +	}
>> +}
>> +
>> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode)
>> +{
>> +	struct dc_hw_plane *plane = &hw->plane[id];
>> +
>> +	if (plane) {
>> +		if (hw->info->planes[id].degamma_size) {
>> +			plane->degamma.mode = mode;
>> +			plane->degamma.dirty = true;
>> +		} else {
>> +			plane->degamma.dirty = false;
>> +		}
>> +	}
>> +}
>> +
>> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi)
>> +{
>> +	struct dc_hw_plane *plane = &hw->plane[id];
>> +
>> +	if (plane) {
>> +		memcpy(&plane->roi, roi, sizeof(*roi) - sizeof(roi->dirty));
>> +		plane->roi.dirty = true;
>> +	}
>> +}
>> +
>> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
>> +			   struct dc_hw_colorkey *colorkey)
>> +{
>> +	struct dc_hw_plane *plane = &hw->plane[id];
>> +
>> +	if (plane) {
>> +		memcpy(&plane->colorkey, colorkey,
>> +		       sizeof(*colorkey) - sizeof(colorkey->dirty));
>> +		plane->colorkey.dirty = true;
>> +	}
>> +}
>> +
>> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos)
>> +{
>> +	memcpy(&hw->qos, qos, sizeof(*qos) - sizeof(qos->dirty));
>> +	hw->qos.dirty = true;
>> +}
>> +
>> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor)
>> +{
>> +	memcpy(&hw->cursor[id], cursor, sizeof(*cursor) - sizeof(cursor->dirty));
>> +	hw->cursor[id].dirty = true;
>> +}
>> +
>> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
>> +			u16 r, u16 g, u16 b)
>> +{
>> +	if (index >= hw->info->gamma_size)
>> +		return;
>> +
>> +	hw->gamma[id].gamma[index][0] = r;
>> +	hw->gamma[id].gamma[index][1] = g;
>> +	hw->gamma[id].gamma[index][2] = b;
>> +	hw->gamma[id].dirty = true;
>> +}
>> +
>> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable)
>> +{
>> +	hw->gamma[id].enable = enable;
>> +	hw->gamma[id].dirty = true;
>> +}
>> +
>> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display)
>> +{
>> +	u8 id = display->id;
>> +
>> +	memcpy(&hw->display[id], display, sizeof(*display));
>> +
>> +	hw->func->display(hw, display);
>> +}
>> +
>> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable)
>> +{
>> +	if (enable)
>> +		hi_write(hw, AQ_INTR_ENBL, 0xFFFFFFFF);
>> +	else
>> +		hi_write(hw, AQ_INTR_ENBL, 0);
>> +}
>> +
>> +u32 dc_hw_get_interrupt(struct dc_hw *hw)
>> +{
>> +	return hi_read(hw, AQ_INTR_ACKNOWLEDGE);
>> +}
>> +
>> +bool dc_hw_check_underflow(struct dc_hw *hw)
>> +{
>> +	return dc_read(hw, DC_FRAMEBUFFER_CONFIG) & BIT(5);
>> +}
>> +
>> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable)
>> +{
>> +	u32 i, offset;
>> +	u8 id, layer_num = hw->info->layer_num;
>> +	u8 panel_num = hw->info->panel_num;
>> +
>> +	for (i = 0; i < layer_num; i++) {
>> +		id = hw->info->planes[i].id;
>> +		offset = get_addr_offset(id);
>> +		if (enable) {
>> +			if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
>> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, BIT(12), 0);
>> +			else
>> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, BIT(31), 0);
>> +		} else {
>> +			if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
>> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, 0, BIT(12));
>> +			else
>> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, 0, BIT(31));
>> +		}
>> +	}
>> +
>> +	for (i = 0; i < panel_num; i++) {
>> +		offset = i << 2;
>> +		if (enable)
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, BIT(0));
>> +		else
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, BIT(0), 0);
>> +	}
>> +}
>> +
>> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id)
>> +{
>> +	if (out <= OUT_DP)
>> +		hw->out[id] = out;
>> +}
>> +
>> +static void gamma_ex_commit(struct dc_hw *hw)
>> +{
>> +	u8 panel_num = hw->info->panel_num;
>> +	u16 i, j;
>> +	u32 value;
>> +
>> +	for (j = 0; j < panel_num; j++) {
>> +		if (hw->gamma[j].dirty) {
>> +			if (hw->gamma[j].enable) {
>> +				dc_write(hw, DC_DISPLAY_GAMMA_EX_INDEX + (j << 2), 0x00);
>> +				for (i = 0; i < GAMMA_EX_SIZE; i++) {
>> +					value = hw->gamma[j].gamma[i][2] |
>> +						(hw->gamma[j].gamma[i][1] << 12);
>> +					dc_write(hw, DC_DISPLAY_GAMMA_EX_DATA + (j << 2), value);
>> +					dc_write(hw, DC_DISPLAY_GAMMA_EX_ONE_DATA + (j << 2),
>> +						 hw->gamma[j].gamma[i][0]);
>> +				}
>> +				dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
>> +					     BIT(13), 0);
>> +			} else {
>> +				dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
>> +					     0, BIT(13));
>> +			}
>> +			hw->gamma[j].dirty = false;
>> +		}
>> +	}
>> +}
>> +
>> +static void plane_commit(struct dc_hw *hw)
>> +{
>> +	struct dc_hw_plane *plane;
>> +	const struct dc_hw_plane_reg *reg;
>> +	bool primary = false;
>> +	u8 id, layer_num = hw->info->layer_num;
>> +	u32 i, offset;
>> +
>> +	for (i = 0; i < layer_num; i++) {
>> +		plane = &hw->plane[i];
>> +		id = hw->info->planes[i].id;
>> +		offset = get_addr_offset(id);
>> +		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
>> +			reg = &dc_plane_reg[0];
>> +			primary = true;
>> +		} else {
>> +			reg = &dc_plane_reg[1];
>> +			primary = false;
>> +		}
> 
> Can you please move register defines to some internal hw_plane 
> structure. These details are known at the time of creation of the plane. 
> There is no need to determine them again and again.
> 
>> +
>> +		if (plane->fb.dirty) {
>> +			if (plane->fb.enable) {
> 
> if (plane->fb.dirty && plane->fb.enable) will save you one level of 
> indentation.
> 
> However it might be better to use existing DRM flags instead of 
> inventing your own flags.
> 
>> +				dc_write(hw, reg->y_address + offset,
>> +					 plane->fb.y_address);
>> +				dc_write(hw, reg->u_address + offset,
>> +					 plane->fb.u_address);
>> +				dc_write(hw, reg->v_address + offset,
>> +					 plane->fb.v_address);
>> +				dc_write(hw, reg->y_stride + offset,
>> +					 plane->fb.y_stride);
>> +				dc_write(hw, reg->u_stride + offset,
>> +					 plane->fb.u_stride);
>> +				dc_write(hw, reg->v_stride + offset,
>> +					 plane->fb.v_stride);
>> +				dc_write(hw, reg->size + offset,
>> +					 plane->fb.width |
>> +					 (plane->fb.height << 15));
>> +				dc_write(hw, reg->water_mark + offset,
>> +					 plane->fb.water_mark);
>> +
>> +				if (plane->fb.clear_enable)
>> +					dc_write(hw, reg->clear_value + offset,
>> +						 plane->fb.clear_value);
>> +			}
>> +
>> +			if (primary) {
> 
> You have two kinds of planes. Primary and overlays. Could you please 
> split this code into two separate functions and define two vtables, one 
> for primary planes and one for overlays. There is no need to stick 
> everything into a single funcion that handles everything.
> 
>> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
>> +					     (plane->fb.format << 26) |
>> +					     (plane->fb.uv_swizzle << 25) |
>> +					     (plane->fb.swizzle << 23) |
>> +					     (plane->fb.tile_mode << 17) |
>> +					     (plane->fb.yuv_color_space << 14) |
>> +					     (plane->fb.rotation << 11) |
>> +					     (plane->fb.clear_enable << 8),
>> +					     (0x1F << 26) |
>> +					     BIT(25) |
>> +					     (0x03 << 23) |
>> +					     (0x1F << 17) |
>> +					     (0x07 << 14) |
>> +					     (0x07 << 11) |
>> +					     BIT(8));
>> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +					     (plane->fb.dec_enable << 1) |
>> +					     (plane->fb.enable << 13) |
>> +					     (plane->fb.zpos << 16) |
>> +					     (plane->fb.display_id << 19),
>> +					     BIT(1) | BIT(13) | (0x07 << 16) | BIT(19));
> 
> It looks like this code can enjoy defining names for all the magic 
> values. Otherwise it is impossible to support it.
> 
>> +			} else {
>> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> +					     (plane->fb.dec_enable << 27) |
>> +					     (plane->fb.clear_enable << 25) |
>> +					     (plane->fb.enable << 24) |
>> +					     (plane->fb.format << 16) |
>> +					     (plane->fb.uv_swizzle << 15) |
>> +					     (plane->fb.swizzle << 13) |
>> +					     (plane->fb.tile_mode << 8) |
>> +					     (plane->fb.yuv_color_space << 5) |
>> +					     (plane->fb.rotation << 2),
>> +					     BIT(27) |
>> +					     BIT(25) |
>> +					     BIT(24) |
>> +					     (0x1F << 16) |
>> +					     BIT(15) |
>> +					     (0x03 << 13) |
>> +					     (0x1F << 8) |
>> +					     (0x07 << 5) |
>> +					     (0x07 << 2));
>> +				dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset,
>> +					     plane->fb.zpos | (plane->fb.display_id << 3),
>> +					     0x07 | BIT(3));
>> +			}
>> +			plane->fb.dirty = false;
>> +		}
>> +
>> +		if (plane->scale.dirty) {
>> +			if (plane->scale.enable) {
>> +				dc_write(hw, reg->scale_factor_x + offset,
>> +					 plane->scale.scale_factor_x);
>> +				dc_write(hw, reg->scale_factor_y + offset,
>> +					 plane->scale.scale_factor_y);
>> +				if (primary)
>> +					dc_set_clear(hw,
>> +						     DC_FRAMEBUFFER_CONFIG + offset,
>> +						     BIT(22), 0);
>> +				else
>> +					dc_set_clear(hw,
>> +						     DC_OVERLAY_SCALE_CONFIG + offset,
>> +						     BIT(8), 0);
>> +			} else {
>> +				if (primary)
>> +					dc_set_clear(hw,
>> +						     DC_FRAMEBUFFER_CONFIG + offset,
>> +						     0, BIT(22));
>> +				else
>> +					dc_set_clear(hw,
>> +						     DC_OVERLAY_SCALE_CONFIG + offset,
>> +						     0, BIT(8));
>> +			}
>> +			plane->scale.dirty = false;
>> +		}
>> +
>> +		if (plane->pos.dirty) {
>> +			dc_write(hw, reg->top_left + offset,
>> +				 plane->pos.start_x |
>> +				 (plane->pos.start_y << 15));
>> +			dc_write(hw, reg->bottom_right + offset,
>> +				 plane->pos.end_x |
>> +				 (plane->pos.end_y << 15));
>> +			plane->pos.dirty = false;
>> +		}
>> +
>> +		if (plane->blend.dirty) {
>> +			dc_write(hw, reg->src_global_color + offset,
>> +				 plane->blend.alpha << 24);
>> +			dc_write(hw, reg->dst_global_color + offset,
>> +				 plane->blend.alpha << 24);
>> +			switch (plane->blend.blend_mode) {
>> +			case BLEND_PREMULTI:
>> +				dc_write(hw, reg->blend_config + offset, 0x3450);
>> +				break;
>> +			case BLEND_COVERAGE:
>> +				dc_write(hw, reg->blend_config + offset, 0x3950);
>> +				break;
>> +			case BLEND_PIXEL_NONE:
>> +				dc_write(hw, reg->blend_config + offset, 0x3548);
>> +				break;
>> +			default:
>> +				break;
>> +			}
>> +			plane->blend.dirty = false;
>> +		}
>> +
>> +		if (plane->colorkey.dirty) {
>> +			dc_write(hw, reg->color_key + offset, plane->colorkey.colorkey);
>> +			dc_write(hw, reg->color_key_high + offset,
>> +				 plane->colorkey.colorkey_high);
>> +
>> +			if (primary)
>> +				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
>> +					     plane->colorkey.transparency << 9, 0x03 << 9);
>> +			else
>> +				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> +					     plane->colorkey.transparency, 0x03);
>> +
>> +			plane->colorkey.dirty = false;
>> +		}
>> +
>> +		if (plane->roi.dirty) {
>> +			if (plane->roi.enable) {
>> +				dc_write(hw, reg->roi_origin + offset,
>> +					 plane->roi.x | (plane->roi.y << 16));
>> +				dc_write(hw, reg->roi_size + offset,
>> +					 plane->roi.width | (plane->roi.height << 16));
>> +				if (primary)
>> +					dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +						     BIT(0), 0);
>> +				else
>> +					dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> +						     BIT(22), 0);
>> +			} else {
>> +				if (primary)
>> +					dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +						     0, BIT(0));
>> +				else
>> +					dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> +						     0, BIT(22));
>> +			}
>> +			plane->roi.dirty = false;
>> +		}
>> +	}
>> +}
>> +
>> +static void plane_ex_commit(struct dc_hw *hw)
>> +{
>> +	struct dc_hw_plane *plane;
>> +	const struct dc_hw_plane_reg *reg;
>> +	bool primary = false;
>> +	u8 id, layer_num = hw->info->layer_num;
>> +	u32 i, offset;
>> +
>> +	for (i = 0; i < layer_num; i++) {
>> +		plane = &hw->plane[i];
>> +		id = hw->info->planes[i].id;
>> +		offset = get_addr_offset(id);
>> +		if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
>> +			reg = &dc_plane_reg[0];
>> +			primary = true;
>> +		} else {
>> +			reg = &dc_plane_reg[1];
>> +			primary = false;
>> +		}
>> +
>> +		if (plane->fb.dirty) {
>> +			if (is_rgb(plane->fb.format)) {
>> +				if (primary)
>> +					dc_set_clear(hw,
>> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +						     BIT(6), BIT(8));
>> +				else
>> +					dc_set_clear(hw,
>> +						     DC_OVERLAY_CONFIG + offset,
>> +						     BIT(29), BIT(30));
>> +			} else {
>> +				if (primary)
>> +					dc_set_clear(hw,
>> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +						     BIT(8), BIT(6));
>> +				else
>> +					dc_set_clear(hw,
>> +						     DC_OVERLAY_CONFIG + offset,
>> +						     BIT(30), BIT(29));
>> +				switch (plane->fb.yuv_color_space) {
>> +				case COLOR_SPACE_601:
>> +					load_yuv_to_rgb(hw, reg, offset, YUV601_2RGB);
>> +					break;
>> +				case COLOR_SPACE_709:
>> +					load_yuv_to_rgb(hw, reg, offset, YUV709_2RGB);
>> +					break;
>> +				case COLOR_SPACE_2020:
>> +					load_yuv_to_rgb(hw, reg, offset, YUV2020_2RGB);
>> +					break;
>> +				default:
>> +					break;
>> +				}
>> +			}
>> +		}
>> +		if (plane->degamma.dirty) {
>> +			switch (plane->degamma.mode) {
>> +			case VS_DEGAMMA_DISABLE:
>> +				if (primary)
>> +					dc_set_clear(hw,
>> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +						     0, BIT(5));
>> +				else
>> +					dc_set_clear(hw,
>> +						     DC_OVERLAY_CONFIG + offset,
>> +						     0, BIT(28));
>> +				break;
>> +			case VS_DEGAMMA_BT709:
>> +				load_degamma_table(hw, reg, offset, DEGAMMA_709);
>> +				if (primary)
>> +					dc_set_clear(hw,
>> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +						     BIT(5), 0);
>> +				else
>> +					dc_set_clear(hw,
>> +						     DC_OVERLAY_CONFIG + offset,
>> +						     BIT(28), 0);
>> +				break;
>> +			case VS_DEGAMMA_BT2020:
>> +				load_degamma_table(hw, reg, offset, DEGAMMA_2020);
>> +				if (primary)
>> +					dc_set_clear(hw,
>> +						     DC_FRAMEBUFFER_CONFIG_EX + offset,
>> +						     BIT(5), 0);
>> +				else
>> +					dc_set_clear(hw,
>> +						     DC_OVERLAY_CONFIG + offset,
>> +						     BIT(28), 0);
>> +				break;
>> +			default:
>> +				break;
>> +			}
>> +			plane->degamma.dirty = false;
>> +		}
>> +	}
>> +	plane_commit(hw);
>> +}
>> +
>> +static void setup_display(struct dc_hw *hw, struct dc_hw_display *display)
>> +{
>> +	u8 id = display->id;
>> +	u32 dpi_cfg, offset = id << 2;
>> +
>> +	if (hw->display[id].enable) {
>> +		switch (display->bus_format) {
>> +		case MEDIA_BUS_FMT_RGB565_1X16:
>> +			dpi_cfg = 0;
>> +			break;
>> +		case MEDIA_BUS_FMT_RGB666_1X18:
>> +			dpi_cfg = 3;
>> +			break;
>> +		case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
>> +			dpi_cfg = 4;
>> +			break;
>> +		case MEDIA_BUS_FMT_RGB888_1X24:
>> +			dpi_cfg = 5;
>> +			break;
>> +		case MEDIA_BUS_FMT_RGB101010_1X30:
>> +			dpi_cfg = 6;
>> +			break;
>> +		default:
>> +			dpi_cfg = 5;
>> +			break;
>> +		}
>> +		dc_write(hw, DC_DISPLAY_DPI_CONFIG + offset, dpi_cfg);
>> +
>> +		if (id == 0)
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
>> +		else
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
>> +
>> +		dc_write(hw, DC_DISPLAY_H + offset, hw->display[id].h_active |
>> +			 (hw->display[id].h_total << 16));
>> +		dc_write(hw, DC_DISPLAY_H_SYNC + offset,
>> +			 hw->display[id].h_sync_start |
>> +			 (hw->display[id].h_sync_end << 15) |
>> +			 (hw->display[id].h_sync_polarity ? 0 : BIT(31)) |
>> +			 BIT(30));
>> +		dc_write(hw, DC_DISPLAY_V + offset, hw->display[id].v_active |
>> +				(hw->display[id].v_total << 16));
>> +		dc_write(hw, DC_DISPLAY_V_SYNC + offset,
>> +			 hw->display[id].v_sync_start |
>> +			 (hw->display[id].v_sync_end << 15) |
>> +			 (hw->display[id].v_sync_polarity ? 0 : BIT(31)) |
>> +			 BIT(30));
>> +
>> +		if (hw->info->pipe_sync)
>> +			dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
>> +				     0, BIT(3) | BIT(4));
>> +
>> +		dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(12), 0);
>> +		if (id == 0)
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(0), BIT(3));
>> +		else
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(1), BIT(3));
>> +	} else {
>> +		dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(12));
>> +		if (id == 0)
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
>> +		else
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
>> +	}
>> +}
>> +
>> +static void setup_display_ex(struct dc_hw *hw, struct dc_hw_display *display)
>> +{
>> +	u8 id = display->id;
>> +	u32 dp_cfg, offset = id << 2;
>> +	bool is_yuv = false;
>> +
>> +	if (hw->display[id].enable && hw->out[id] == OUT_DP) {
>> +		switch (display->bus_format) {
>> +		case MEDIA_BUS_FMT_RGB565_1X16:
>> +			dp_cfg = 0;
>> +			break;
>> +		case MEDIA_BUS_FMT_RGB666_1X18:
>> +			dp_cfg = 1;
>> +			break;
>> +		case MEDIA_BUS_FMT_RGB888_1X24:
>> +			dp_cfg = 2;
>> +			break;
>> +		case MEDIA_BUS_FMT_RGB101010_1X30:
>> +			dp_cfg = 3;
>> +			break;
>> +		case MEDIA_BUS_FMT_UYVY8_1X16:
>> +			dp_cfg = 2 << 4;
>> +			is_yuv = true;
>> +			break;
>> +		case MEDIA_BUS_FMT_YUV8_1X24:
>> +			dp_cfg = 4 << 4;
>> +			is_yuv = true;
>> +			break;
>> +		case MEDIA_BUS_FMT_UYVY10_1X20:
>> +			dp_cfg = 8 << 4;
>> +			is_yuv = true;
>> +			break;
>> +		case MEDIA_BUS_FMT_YUV10_1X30:
>> +			dp_cfg = 10 << 4;
>> +			is_yuv = true;
>> +			break;
>> +		case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
>> +			dp_cfg = 12 << 4;
>> +			is_yuv = true;
>> +			break;
>> +		case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
>> +			dp_cfg = 13 << 4;
>> +			is_yuv = true;
>> +			break;
>> +		default:
>> +			dp_cfg = 2;
>> +			break;
>> +		}
>> +		if (is_yuv)
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(16), 0);
>> +		else
>> +			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(16));
>> +		dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | BIT(3));
>> +	}
>> +
>> +	if (hw->out[id] == OUT_DPI)
>> +		dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, BIT(3));
>> +
>> +	setup_display(hw, display);
>> +}
>> +
>> +static const struct dc_hw_funcs hw_func = {
>> +	.gamma = &gamma_ex_commit,
>> +	.plane = &plane_ex_commit,
>> +	.display = setup_display_ex,
>> +};
>> +
>> +void dc_hw_commit(struct dc_hw *hw)
>> +{
>> +	u32 i, offset = 0;
>> +	u8 plane_num = hw->info->plane_num;
>> +	u8 layer_num = hw->info->layer_num;
>> +	u8 cursor_num = plane_num - layer_num;
>> +
>> +	hw->func->gamma(hw);
>> +	hw->func->plane(hw);
>> +
>> +	for (i = 0; i < cursor_num; i++) {
>> +		if (hw->cursor[i].dirty) {
>> +			offset = hw->cursor[i].display_id ? DC_CURSOR_OFFSET : 0;
>> +			if (hw->cursor[i].enable) {
>> +				dc_write(hw, DC_CURSOR_ADDRESS + offset,
>> +					 hw->cursor[i].address);
>> +				dc_write(hw, DC_CURSOR_LOCATION + offset, hw->cursor[i].x |
>> +					 (hw->cursor[i].y << 16));
>> +				dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
>> +					     (hw->cursor[i].hot_x << 16) |
>> +					     (hw->cursor[i].hot_y << 8) |
>> +					     (hw->cursor[i].size << 5) |
>> +					     BIT(3) | BIT(2) | 0x02,
>> +					     (0xFF << 16) |
>> +					     (0xFF << 8) |
>> +					     (0x07 << 5) | 0x1F);
>> +			} else {
>> +				dc_set_clear(hw, DC_CURSOR_CONFIG + offset, BIT(3), 0x03);
>> +			}
>> +			hw->cursor[i].dirty = false;
>> +		}
>> +	}
>> +
>> +	if (hw->qos.dirty) {
>> +		dc_set_clear(hw, DC_QOS_CONFIG, (hw->qos.high_value << 4) |
>> +					 hw->qos.low_value, 0xFF);
>> +		hw->qos.dirty = false;
>> +	}
>> +}
>> +
>> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.h b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
>> new file mode 100644
>> index 000000000..9a1d767ae
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
>> @@ -0,0 +1,492 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#ifndef __VS_DC_HW_H__
>> +#define __VS_DC_HW_H__
>> +
>> +#define AQ_INTR_ACKNOWLEDGE				0x0010
>> +#define AQ_INTR_ENBL					0x0014
> 
> Please take care to align all the defines.
> 
>> +#define DC_HW_REVISION					0x0024
>> +#define DC_HW_CHIP_CID					0x0030
>> +
>> +#define DC_REG_BASE						0x0800
>> +#define DC_REG_RANGE					0x2000
>> +#define DC_SEC_REG_OFFSET				0x100000
>> +
>> +#define DC_FRAMEBUFFER_CONFIG			0x1518
>> +#define DC_FRAMEBUFFER_CONFIG_EX		0x1CC0
>> +#define DC_FRAMEBUFFER_SCALE_CONFIG		0x1520
>> +#define DC_FRAMEBUFFER_TOP_LEFT			0x24D8
>> +#define DC_FRAMEBUFFER_BOTTOM_RIGHT		0x24E0
>> +#define DC_FRAMEBUFFER_ADDRESS			0x1400
>> +#define DC_FRAMEBUFFER_U_ADDRESS		0x1530
>> +#define DC_FRAMEBUFFER_V_ADDRESS		0x1538
>> +#define DC_FRAMEBUFFER_STRIDE			0x1408
>> +#define DC_FRAMEBUFFER_U_STRIDE			0x1800
>> +#define DC_FRAMEBUFFER_V_STRIDE			0x1808
>> +#define DC_FRAMEBUFFER_SIZE				0x1810
>> +#define DC_FRAMEBUFFER_SCALE_FACTOR_X		0x1828
>> +#define DC_FRAMEBUFFER_SCALE_FACTOR_Y		0x1830
>> +#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX	0x1838
>> +#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA	0x1A00
>> +#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX	0x1A08
>> +#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA	0x1A10
>> +#define DC_FRAMEBUFFER_INIT_OFFSET			0x1A20
>> +#define DC_FRAMEBUFFER_COLOR_KEY			0x1508
>> +#define DC_FRAMEBUFFER_COLOR_KEY_HIGH		0x1510
>> +#define DC_FRAMEBUFFER_CLEAR_VALUE			0x1A18
>> +#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX	0x1818
>> +#define DC_FRAMEBUFFER_COLOR_TABLE_DATA		0x1820
>> +#define DC_FRAMEBUFFER_BG_COLOR				0x1528
>> +#define DC_FRAMEBUFFER_ROI_ORIGIN			0x1CB0
>> +#define DC_FRAMEBUFFER_ROI_SIZE				0x1CB8
>> +#define DC_FRAMEBUFFER_WATER_MARK			0x1CE8
>> +#define DC_FRAMEBUFFER_DEGAMMA_INDEX		0x1D88
>> +#define DC_FRAMEBUFFER_DEGAMMA_DATA			0x1D90
>> +#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA		0x1D98
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEF0		0x1DA0
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEF1		0x1DA8
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEF2		0x1DB0
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEF3		0x1DB8
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEF4		0x1E00
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD0		0x1E08
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD1		0x1E10
>> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD2		0x1E18
>> +#define DC_FRAMEBUFFER_Y_CLAMP_BOUND		0x1E88
>> +#define DC_FRAMEBUFFER_UV_CLAMP_BOUND		0x1E90
>> +#define DC_FRAMEBUFFER_RGBTORGB_COEF0		0x1E20
>> +#define DC_FRAMEBUFFER_RGBTORGB_COEF1		0x1E28
>> +#define DC_FRAMEBUFFER_RGBTORGB_COEF2		0x1E30
>> +#define DC_FRAMEBUFFER_RGBTORGB_COEF3		0x1E38
>> +#define DC_FRAMEBUFFER_RGBTORGB_COEF4		0x1E40
>> +#define DC_FRAMEBUFFER_BLEND_CONFIG			0x2510
>> +#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR		0x2500
>> +#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR		0x2508
>> +
>> +#define DC_OVERLAY_CONFIG					0x1540
>> +#define DC_OVERLAY_CONFIG_EX				0x2540
>> +#define DC_OVERLAY_SCALE_CONFIG				0x1C00
>> +#define DC_OVERLAY_BLEND_CONFIG				0x1580
>> +#define DC_OVERLAY_TOP_LEFT					0x1640
>> +#define DC_OVERLAY_BOTTOM_RIGHT				0x1680
>> +#define DC_OVERLAY_ADDRESS					0x15C0
>> +#define DC_OVERLAY_U_ADDRESS				0x1840
>> +#define DC_OVERLAY_V_ADDRESS				0x1880
>> +#define DC_OVERLAY_STRIDE					0x1600
>> +#define DC_OVERLAY_U_STRIDE					0x18C0
>> +#define DC_OVERLAY_V_STRIDE					0x1900
>> +#define DC_OVERLAY_SIZE						0x17C0
>> +#define DC_OVERLAY_SCALE_FACTOR_X			0x1A40
>> +#define DC_OVERLAY_SCALE_FACTOR_Y			0x1A80
>> +#define DC_OVERLAY_H_FILTER_COEF_INDEX		0x1AC0
>> +#define DC_OVERLAY_H_FILTER_COEF_DATA		0x1B00
>> +#define DC_OVERLAY_V_FILTER_COEF_INDEX		0x1B40
>> +#define DC_OVERLAY_V_FILTER_COEF_DATA		0x1B80
>> +#define DC_OVERLAY_INIT_OFFSET				0x1BC0
>> +#define DC_OVERLAY_COLOR_KEY				0x1740
>> +#define DC_OVERLAY_COLOR_KEY_HIGH			0x1780
>> +#define DC_OVERLAY_CLEAR_VALUE				0x1940
>> +#define DC_OVERLAY_COLOR_TABLE_INDEX		0x1980
>> +#define DC_OVERLAY_COLOR_TABLE_DATA			0x19C0
>> +#define DC_OVERLAY_SRC_GLOBAL_COLOR			0x16C0
>> +#define DC_OVERLAY_DST_GLOBAL_COLOR			0x1700
>> +#define DC_OVERLAY_ROI_ORIGIN				0x1D00
>> +#define DC_OVERLAY_ROI_SIZE					0x1D40
>> +#define DC_OVERLAY_WATER_MARK				0x1DC0
>> +#define DC_OVERLAY_DEGAMMA_INDEX			0x2200
>> +#define DC_OVERLAY_DEGAMMA_DATA				0x2240
>> +#define DC_OVERLAY_DEGAMMA_EX_DATA			0x2280
>> +#define DC_OVERLAY_YUVTORGB_COEF0			0x1EC0
>> +#define DC_OVERLAY_YUVTORGB_COEF1			0x1F00
>> +#define DC_OVERLAY_YUVTORGB_COEF2			0x1F40
>> +#define DC_OVERLAY_YUVTORGB_COEF3			0x1F80
>> +#define DC_OVERLAY_YUVTORGB_COEF4			0x1FC0
>> +#define DC_OVERLAY_YUVTORGB_COEFD0			0x2000
>> +#define DC_OVERLAY_YUVTORGB_COEFD1			0x2040
>> +#define DC_OVERLAY_YUVTORGB_COEFD2			0x2080
>> +#define DC_OVERLAY_Y_CLAMP_BOUND			0x22C0
>> +#define DC_OVERLAY_UV_CLAMP_BOUND			0x2300
>> +#define DC_OVERLAY_RGBTORGB_COEF0			0x20C0
>> +#define DC_OVERLAY_RGBTORGB_COEF1			0x2100
>> +#define DC_OVERLAY_RGBTORGB_COEF2			0x2140
>> +#define DC_OVERLAY_RGBTORGB_COEF3			0x2180
>> +#define DC_OVERLAY_RGBTORGB_COEF4			0x21C0
>> +
>> +#define DC_CURSOR_CONFIG					0x1468
>> +#define DC_CURSOR_ADDRESS					0x146C
>> +#define DC_CURSOR_LOCATION					0x1470
>> +#define DC_CURSOR_BACKGROUND				0x1474
>> +#define DC_CURSOR_FOREGROUND				0x1478
>> +#define DC_CURSOR_CLK_GATING				0x1484
>> +#define DC_CURSOR_CONFIG_EX					0x24E8
>> +#define DC_CURSOR_OFFSET					0x1080
>> +
>> +#define DC_DISPLAY_DITHER_CONFIG		0x1410
>> +#define DC_DISPLAY_PANEL_CONFIG			0x1418
>> +#define DC_DISPLAY_PANEL_CONFIG_EX		0x2518
>> +#define DC_DISPLAY_DITHER_TABLE_LOW		0x1420
>> +#define DC_DISPLAY_DITHER_TABLE_HIGH	0x1428
>> +#define DC_DISPLAY_H					0x1430
>> +#define DC_DISPLAY_H_SYNC				0x1438
>> +#define DC_DISPLAY_V					0x1440
>> +#define DC_DISPLAY_V_SYNC				0x1448
>> +#define DC_DISPLAY_CURRENT_LOCATION		0x1450
>> +#define DC_DISPLAY_GAMMA_INDEX			0x1458
>> +#define DC_DISPLAY_GAMMA_DATA			0x1460
>> +#define DC_DISPLAY_INT					0x147C
>> +#define DC_DISPLAY_INT_ENABLE			0x1480
>> +#define DC_DISPLAY_DBI_CONFIG			0x1488
>> +#define DC_DISPLAY_GENERAL_CONFIG		0x14B0
>> +#define DC_DISPLAY_DPI_CONFIG			0x14B8
>> +#define DC_DISPLAY_PANEL_START			0x1CCC
>> +#define DC_DISPLAY_DEBUG_COUNTER_SELECT		0x14D0
>> +#define DC_DISPLAY_DEBUG_COUNTER_VALUE		0x14D8
>> +#define DC_DISPLAY_DP_CONFIG				0x1CD0
>> +#define DC_DISPLAY_GAMMA_EX_INDEX			0x1CF0
>> +#define DC_DISPLAY_GAMMA_EX_DATA			0x1CF8
>> +#define DC_DISPLAY_GAMMA_EX_ONE_DATA		0x1D80
>> +#define DC_DISPLAY_RGBTOYUV_COEF0			0x1E48
>> +#define DC_DISPLAY_RGBTOYUV_COEF1			0x1E50
>> +#define DC_DISPLAY_RGBTOYUV_COEF2			0x1E58
>> +#define DC_DISPLAY_RGBTOYUV_COEF3			0x1E60
>> +#define DC_DISPLAY_RGBTOYUV_COEF4			0x1E68
>> +#define DC_DISPLAY_RGBTOYUV_COEFD0			0x1E70
>> +#define DC_DISPLAY_RGBTOYUV_COEFD1			0x1E78
>> +#define DC_DISPLAY_RGBTOYUV_COEFD2			0x1E80
>> +
>> +#define DC_CLK_GATTING					0x1A28
>> +#define DC_QOS_CONFIG					0x1A38
>> +
>> +#define DC_TRANSPARENCY_OPAQUE			0x00
>> +#define DC_TRANSPARENCY_KEY				0x02
>> +#define DC_DISPLAY_DITHERTABLE_LOW		0x7B48F3C0
>> +#define DC_DISPLAY_DITHERTABLE_HIGH		0x596AD1E2
>> +
>> +#define GAMMA_SIZE						256
>> +#define GAMMA_EX_SIZE					300
>> +#define DEGAMMA_SIZE					260
>> +
>> +#define RGB_TO_RGB_TABLE_SIZE			9
>> +#define YUV_TO_RGB_TABLE_SIZE			16
>> +#define RGB_TO_YUV_TABLE_SIZE			12
>> +
>> +#define DC_LAYER_NUM	6
>> +#define DC_DISPLAY_NUM	2
>> +#define DC_CURSOR_NUM	2
>> +
>> +#define DC_TILE_MODE4X4	0x15
>> +
>> +enum dc_chip_rev {
>> +	DC_REV_0,/* For HW_REV_5720,HW_REV_5721_311 */
>> +	DC_REV_1,/* For HW_REV_5721_30B */
>> +	DC_REV_2,/* For HW_REV_5721_310 */
>> +};
>> +
>> +enum dc_hw_plane_id {
>> +	PRIMARY_PLANE_0,
>> +	OVERLAY_PLANE_0,
>> +	OVERLAY_PLANE_1,
>> +	PRIMARY_PLANE_1,
>> +	OVERLAY_PLANE_2,
>> +	OVERLAY_PLANE_3,
>> +	CURSOR_PLANE_0,
>> +	CURSOR_PLANE_1,
>> +	PLANE_NUM
>> +};
>> +
>> +enum dc_hw_color_format {
>> +	FORMAT_X4R4G4B4,//0
> 
> Space after the comma will make it more readable. Or just define the 
> values explicitly, it will also be more readable.
> 
>> +	FORMAT_A4R4G4B4,//1
>> +	FORMAT_X1R5G5B5,//2
>> +	FORMAT_A1R5G5B5,//3
>> +	FORMAT_R5G6B5,//4
>> +	FORMAT_X8R8G8B8,//5
>> +	FORMAT_A8R8G8B8,//6
>> +	FORMAT_YUY2,//7
>> +	FORMAT_UYVY,//8
>> +	FORMAT_INDEX8,//9
>> +	FORMAT_MONOCHROME,//10
>> +	FORMAT_YV12 = 0xf,
>> +	FORMAT_A8,//16
>> +	FORMAT_NV12,//17
>> +	FORMAT_NV16,//18
>> +	FORMAT_RG16,//19
>> +	FORMAT_R8,//20
>> +	FORMAT_NV12_10BIT,//21
>> +	FORMAT_A2R10G10B10,//22
>> +	FORMAT_NV16_10BIT,//23
>> +	FORMAT_INDEX1,//24
>> +	FORMAT_INDEX2,//25
>> +	FORMAT_INDEX4,//26
>> +	FORMAT_P010,//27
>> +	FORMAT_YUV444,//28
>> +	FORMAT_YUV444_10BIT,//29
>> +};
>> +
>> +enum dc_hw_yuv_color_space {
>> +	COLOR_SPACE_601 = 0,
>> +	COLOR_SPACE_709 = 1,
>> +	COLOR_SPACE_2020 = 3,
>> +};
>> +
>> +enum dc_hw_rotation {
>> +	ROT_0 = 0,
>> +	ROT_90 = 4,
>> +	ROT_180 = 5,
>> +	ROT_270 = 6,
>> +	FLIP_X = 1,
>> +	FLIP_Y = 2,
>> +	FLIP_XY = 3,
>> +};
>> +
>> +enum dc_hw_swizzle {
>> +	SWIZZLE_ARGB = 0,
>> +	SWIZZLE_RGBA,
>> +	SWIZZLE_ABGR,
>> +	SWIZZLE_BGRA,
>> +};
>> +
>> +enum dc_hw_out {
>> +	OUT_DPI,
>> +	OUT_DP,
>> +};
>> +
>> +enum dc_hw_cursor_size {
>> +	CURSOR_SIZE_32X32 = 0,
>> +	CURSOR_SIZE_64X64,
>> +};
>> +
>> +enum dc_hw_blend_mode {
>> +	/* out.rgb = plane_alpha * fg.rgb +
>> +	 *		(1 - (plane_alpha * fg.alpha)) * bg.rgb
>> +	 */
>> +	BLEND_PREMULTI,
>> +	/* out.rgb = plane_alpha * fg.alpha * fg.rgb +
>> +	 *		(1 - (plane_alpha * fg.alpha)) * bg.rgb
>> +	 */
>> +	BLEND_COVERAGE,
>> +	/* out.rgb = plane_alpha * fg.rgb +
>> +	 *		(1 - plane_alpha) * bg.rgb
>> +	 */
>> +	BLEND_PIXEL_NONE,
> 
> Can you use defines from <drm/drm_blend.h>?
> 
>> +};
>> +
>> +struct dc_hw_plane_reg {
>> +	u32 y_address;
>> +	u32 u_address;
>> +	u32 v_address;
>> +	u32 y_stride;
>> +	u32 u_stride;
>> +	u32 v_stride;
>> +	u32 size;
>> +	u32 top_left;
>> +	u32 bottom_right;
>> +	u32 scale_factor_x;
>> +	u32 scale_factor_y;
>> +	u32 h_filter_coef_index;
>> +	u32 h_filter_coef_data;
>> +	u32 v_filter_coef_index;
>> +	u32 v_filter_coef_data;
>> +	u32 init_offset;
>> +	u32 color_key;
>> +	u32 color_key_high;
>> +	u32 clear_value;
>> +	u32 color_table_index;
>> +	u32 color_table_data;
>> +	u32 scale_config;
>> +	u32 water_mark;
>> +	u32 degamma_index;
>> +	u32 degamma_data;
>> +	u32 degamma_ex_data;
>> +	u32 src_global_color;
>> +	u32 dst_global_color;
>> +	u32 blend_config;
>> +	u32 roi_origin;
>> +	u32 roi_size;
>> +	u32 yuv_to_rgb_coef0;
>> +	u32 yuv_to_rgb_coef1;
>> +	u32 yuv_to_rgb_coef2;
>> +	u32 yuv_to_rgb_coef3;
>> +	u32 yuv_to_rgb_coef4;
>> +	u32 yuv_to_rgb_coefd0;
>> +	u32 yuv_to_rgb_coefd1;
>> +	u32 yuv_to_rgb_coefd2;
>> +	u32 y_clamp_bound;
>> +	u32 uv_clamp_bound;
>> +	u32 rgb_to_rgb_coef0;
>> +	u32 rgb_to_rgb_coef1;
>> +	u32 rgb_to_rgb_coef2;
>> +	u32 rgb_to_rgb_coef3;
>> +	u32 rgb_to_rgb_coef4;
>> +};
>> +
>> +struct dc_hw_fb {
>> +	u32 y_address;
>> +	u32 u_address;
>> +	u32 v_address;
>> +	u32 clear_value;
>> +	u32 water_mark;
>> +	u16 y_stride;
>> +	u16 u_stride;
>> +	u16 v_stride;
>> +	u16 width;
>> +	u16 height;
>> +	u8	format;
>> +	u8	tile_mode;
>> +	u8	rotation;
>> +	u8	yuv_color_space;
>> +	u8	swizzle;
>> +	u8	uv_swizzle;
>> +	u8	zpos;
>> +	u8	display_id;
>> +	bool	clear_enable;
>> +	bool	dec_enable;
>> +	bool	enable;
>> +	bool	dirty;
>> +};
>> +
>> +struct dc_hw_scale {
>> +	u32 scale_factor_x;
>> +	u32 scale_factor_y;
>> +	bool	enable;
>> +	bool	dirty;
>> +};
>> +
>> +struct dc_hw_position {
>> +	u16 start_x;
>> +	u16 start_y;
>> +	u16 end_x;
>> +	u16 end_y;
>> +	bool	dirty;
>> +};
>> +
>> +struct dc_hw_blend {
>> +	u8	alpha;
>> +	u8	blend_mode;
>> +	bool	dirty;
>> +};
>> +
>> +struct dc_hw_colorkey {
>> +	u32 colorkey;
>> +	u32 colorkey_high;
>> +	u8	transparency;
>> +	bool dirty;
>> +};
>> +
>> +struct dc_hw_roi {
>> +	u16 x;
>> +	u16 y;
>> +	u16 width;
>> +	u16 height;
>> +	bool enable;
>> +	bool dirty;
>> +};
>> +
>> +struct dc_hw_cursor {
>> +	u32 address;
>> +	u16 x;
>> +	u16 y;
>> +	u16 hot_x;
>> +	u16 hot_y;
>> +	u8	size;
>> +	u8	display_id;
>> +	bool	enable;
>> +	bool	dirty;
>> +};
>> +
>> +struct dc_hw_display {
>> +	u32 bus_format;
>> +	u16 h_active;
>> +	u16 h_total;
>> +	u16 h_sync_start;
>> +	u16 h_sync_end;
>> +	u16 v_active;
>> +	u16 v_total;
>> +	u16 v_sync_start;
>> +	u16 v_sync_end;
>> +	u8	id;
>> +	bool	h_sync_polarity;
>> +	bool	v_sync_polarity;
>> +	bool	enable;
>> +};
>> +
>> +struct dc_hw_gamma {
>> +	u16 gamma[GAMMA_EX_SIZE][3];
>> +	bool	enable;
>> +	bool	dirty;
>> +};
>> +
>> +struct dc_hw_degamma {
>> +	u16 degamma[DEGAMMA_SIZE][3];
>> +	u32 mode;
>> +	bool	dirty;
>> +};
>> +
>> +struct dc_hw_plane {
>> +	struct dc_hw_fb			fb;
>> +	struct dc_hw_position	pos;
>> +	struct dc_hw_scale		scale;
>> +	struct dc_hw_blend		blend;
>> +	struct dc_hw_roi		roi;
>> +	struct dc_hw_colorkey	colorkey;
>> +	struct dc_hw_degamma	degamma;
>> +};
>> +
>> +struct dc_hw_qos {
>> +	u8	  low_value;
>> +	u8	  high_value;
>> +	bool  dirty;
>> +};
>> +
>> +struct dc_hw_read {
>> +	u32			reg;
>> +	u32			value;
>> +};
>> +
>> +struct dc_hw;
>> +struct dc_hw_funcs {
>> +	void (*gamma)(struct dc_hw *hw);
>> +	void (*plane)(struct dc_hw *hw);
>> +	void (*display)(struct dc_hw *hw, struct dc_hw_display *display);
>> +};
>> +
>> +struct dc_hw {
>> +	enum dc_chip_rev rev;
>> +	enum dc_hw_out		out[DC_DISPLAY_NUM];
>> +	void			*hi_base;
>> +	void			*reg_base;
>> +
>> +	struct dc_hw_display display[DC_DISPLAY_NUM];
>> +	struct dc_hw_gamma	 gamma[DC_DISPLAY_NUM];
>> +	struct dc_hw_plane	 plane[DC_LAYER_NUM];
>> +	struct dc_hw_cursor  cursor[DC_CURSOR_NUM];
>> +	struct dc_hw_qos	 qos;
>> +	struct dc_hw_funcs	 *func;
>> +	struct vs_dc_info	 *info;
>> +};
>> +
>> +int dc_hw_init(struct dc_hw *hw);
>> +void dc_hw_deinit(struct dc_hw *hw);
>> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
>> +			struct dc_hw_fb *fb, struct dc_hw_scale *scale,
>> +			struct dc_hw_position *pos, struct dc_hw_blend *blend);
>> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode);
>> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi);
>> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
>> +			   struct dc_hw_colorkey *colorkey);
>> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos);
>> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor);
>> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
>> +			u16 r, u16 g, u16 b);
>> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable);
>> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display);
>> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable);
>> +u32 dc_hw_get_interrupt(struct dc_hw *hw);
>> +bool dc_hw_check_underflow(struct dc_hw *hw);
>> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable);
>> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id);
>> +void dc_hw_commit(struct dc_hw *hw);
>> +
>> +#endif /* __VS_DC_HW_H__ */
>> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
>> index da7698c3d..3cd533cfa 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
>> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
>> @@ -21,6 +21,7 @@
>>   
>>   #include "vs_drv.h"
>>   #include "vs_modeset.h"
>> +#include "vs_dc.h"
>>   
>>   #define DRV_NAME	"verisilicon"
>>   #define DRV_DESC	"Verisilicon DRM driver"
>> @@ -123,6 +124,7 @@ static const struct component_master_ops vs_drm_ops = {
>>   };
>>   
>>   static struct platform_driver *drm_sub_drivers[] = {
>> +	&dc_platform_driver,
>>   
>>   };
>>   
>> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c
>> new file mode 100644
>> index 000000000..9bd066015
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_plane.c
>> @@ -0,0 +1,526 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#include <drm/drm_atomic.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_blend.h>
>> +#include <drm/drm_gem_dma_helper.h>
>> +#include <drm/drm_fb_dma_helper.h>
>> +#include <drm/drm_framebuffer.h>
>> +#include <drm/drm_plane.h>
>> +#include <drm/drm_plane_helper.h>
>> +
>> +#include <drm/vs_drm.h>
>> +
>> +#include "vs_plane.h"
>> +#include "vs_drv.h"
>> +#include "vs_dc.h"
>> +
>> +static void vs_plane_reset(struct drm_plane *plane)
>> +{
>> +	struct vs_plane_state *state;
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +
>> +	if (plane->state) {
>> +		__drm_atomic_helper_plane_destroy_state(plane->state);
>> +
>> +		state = to_vs_plane_state(plane->state);
>> +		kfree(state);
>> +		plane->state = NULL;
>> +	}
>> +
> 
> Use vs_plane_destroy_state directly. This way you won't miss a refcount 
> on blobs.
> 
>> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> +	if (!state)
>> +		return;
>> +
>> +	state->degamma = VS_DEGAMMA_DISABLE;
>> +	state->degamma_changed = false;
>> +	state->base.zpos = vs_plane->id;
>> +	memset(&state->status, 0, sizeof(state->status));
>> +
>> +	__drm_atomic_helper_plane_reset(plane, &state->base);
>> +}
>> +
>> +static void _vs_plane_duplicate_blob(struct vs_plane_state *state,
>> +				     struct vs_plane_state *ori_state)
>> +{
>> +	state->watermark = ori_state->watermark;
>> +	state->color_mgmt = ori_state->color_mgmt;
>> +	state->roi = ori_state->roi;
>> +
>> +	if (state->watermark)
>> +		drm_property_blob_get(state->watermark);
>> +	if (state->color_mgmt)
>> +		drm_property_blob_get(state->color_mgmt);
>> +	if (state->roi)
>> +		drm_property_blob_get(state->roi);
>> +}
>> +
>> +static int
>> +_vs_plane_set_property_blob_from_id(struct drm_device *dev,
>> +				    struct drm_property_blob **blob,
>> +				    u64 blob_id,
>> +				    size_t expected_size)
>> +{
>> +	struct drm_property_blob *new_blob = NULL;
>> +
>> +	if (blob_id) {
>> +		new_blob = drm_property_lookup_blob(dev, blob_id);
>> +		if (!new_blob)
>> +			return -EINVAL;
>> +
>> +		if (new_blob->length != expected_size) {
>> +			drm_property_blob_put(new_blob);
>> +			return -EINVAL;
>> +		}
>> +	}
>> +
>> +	drm_property_replace_blob(blob, new_blob);
>> +	drm_property_blob_put(new_blob);
>> +
>> +	return 0;
>> +}
>> +
>> +static struct drm_plane_state *
>> +vs_plane_atomic_duplicate_state(struct drm_plane *plane)
>> +{
>> +	struct vs_plane_state *ori_state;
>> +	struct vs_plane_state *state;
>> +
>> +	if (WARN_ON(!plane->state))
>> +		return NULL;
>> +
>> +	ori_state = to_vs_plane_state(plane->state);
>> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> +	if (!state)
>> +		return NULL;
>> +
>> +	__drm_atomic_helper_plane_duplicate_state(plane, &state->base);
>> +
>> +	state->degamma = ori_state->degamma;
>> +	state->degamma_changed = ori_state->degamma_changed;
>> +
>> +	_vs_plane_duplicate_blob(state, ori_state);
>> +	memcpy(&state->status, &ori_state->status, sizeof(ori_state->status));
>> +
>> +	return &state->base;
>> +}
>> +
>> +static void vs_plane_atomic_destroy_state(struct drm_plane *plane,
>> +					  struct drm_plane_state *state)
>> +{
>> +	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
>> +
>> +	__drm_atomic_helper_plane_destroy_state(state);
>> +
>> +	drm_property_blob_put(vs_plane_state->watermark);
>> +	drm_property_blob_put(vs_plane_state->color_mgmt);
>> +	drm_property_blob_put(vs_plane_state->roi);
>> +	kfree(vs_plane_state);
>> +}
>> +
>> +static int vs_plane_atomic_set_property(struct drm_plane *plane,
>> +					struct drm_plane_state *state,
>> +					struct drm_property *property,
>> +					uint64_t val)
>> +{
>> +	struct drm_device *dev = plane->dev;
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
>> +	int ret = 0;
>> +
>> +	if (property == vs_plane->degamma_mode) {
>> +		if (vs_plane_state->degamma != val) {
>> +			vs_plane_state->degamma = val;
>> +			vs_plane_state->degamma_changed = true;
>> +		} else {
>> +			vs_plane_state->degamma_changed = false;
>> +		}
>> +	} else if (property == vs_plane->watermark_prop) {
>> +		ret = _vs_plane_set_property_blob_from_id(dev,
>> +							  &vs_plane_state->watermark,
>> +							  val,
>> +							  sizeof(struct drm_vs_watermark));
>> +		return ret;
>> +	} else if (property == vs_plane->color_mgmt_prop) {
>> +		ret = _vs_plane_set_property_blob_from_id(dev,
>> +							  &vs_plane_state->color_mgmt,
>> +							  val,
>> +							  sizeof(struct drm_vs_color_mgmt));
>> +		return ret;
>> +	} else if (property == vs_plane->roi_prop) {
>> +		ret = _vs_plane_set_property_blob_from_id(dev,
>> +							  &vs_plane_state->roi,
>> +							  val,
>> +							  sizeof(struct drm_vs_roi));
>> +		return ret;
>> +	} else {
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int vs_plane_atomic_get_property(struct drm_plane *plane,
>> +					const struct drm_plane_state *state,
>> +					struct drm_property *property,
>> +					uint64_t *val)
>> +{
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +	const struct vs_plane_state *vs_plane_state =
>> +		container_of(state, const struct vs_plane_state, base);
>> +
>> +	if (property == vs_plane->degamma_mode)
>> +		*val = vs_plane_state->degamma;
>> +	else if (property == vs_plane->watermark_prop)
>> +		*val = (vs_plane_state->watermark) ?
>> +					vs_plane_state->watermark->base.id : 0;
>> +	else if (property == vs_plane->color_mgmt_prop)
>> +		*val = (vs_plane_state->color_mgmt) ?
>> +					vs_plane_state->color_mgmt->base.id : 0;
> 
> degamma and color management should use standard properties.
> 
>> +	else if (property == vs_plane->roi_prop)
>> +		*val = (vs_plane_state->roi) ?
>> +					vs_plane_state->roi->base.id : 0;
>> +	else
>> +		return -EINVAL;
>> +
>> +	return 0;
>> +}
>> +
>> +static bool vs_format_mod_supported(struct drm_plane *plane,
>> +				    u32 format,
>> +				    u64 modifier)
>> +{
>> +	int i;
>> +
>> +	/* We always have to allow these modifiers:
>> +	 * 1. Core DRM checks for LINEAR support if userspace does not provide modifiers.
>> +	 * 2. Not passing any modifiers is the same as explicitly passing INVALID.
>> +	 */
>> +	if (modifier == DRM_FORMAT_MOD_LINEAR)
>> +		return true;
>> +
>> +	/* Check that the modifier is on the list of the plane's supported modifiers. */
>> +	for (i = 0; i < plane->modifier_count; i++) {
>> +		if (modifier == plane->modifiers[i])
>> +			break;
>> +	}
>> +
>> +	if (i == plane->modifier_count)
>> +		return false;
>> +
>> +	return true;
>> +}
>> +
>> +const struct drm_plane_funcs vs_plane_funcs = {
>> +	.update_plane		= drm_atomic_helper_update_plane,
>> +	.disable_plane		= drm_atomic_helper_disable_plane,
>> +	.reset			= vs_plane_reset,
>> +	.atomic_duplicate_state = vs_plane_atomic_duplicate_state,
>> +	.atomic_destroy_state	= vs_plane_atomic_destroy_state,
>> +	.atomic_set_property	= vs_plane_atomic_set_property,
>> +	.atomic_get_property	= vs_plane_atomic_get_property,
>> +	.format_mod_supported	= vs_format_mod_supported,
>> +};
>> +
>> +static unsigned char vs_get_plane_number(struct drm_framebuffer *fb)
>> +{
>> +	const struct drm_format_info *info;
>> +
>> +	if (!fb)
>> +		return 0;
>> +
>> +	info = drm_format_info(fb->format->format);
>> +	if (!info || info->num_planes > DRM_FORMAT_MAX_PLANES)
>> +		return 0;
>> +
>> +	return info->num_planes;
>> +}
>> +
>> +static int vs_plane_atomic_check(struct drm_plane *plane,
>> +				 struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> +										 plane);
>> +	unsigned char i, num_planes;
>> +	struct drm_framebuffer *fb = new_plane_state->fb;
>> +	struct drm_crtc *crtc = new_plane_state->crtc;
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
>> +
>> +	if (!crtc || !fb)
>> +		return 0;
>> +
>> +	num_planes = vs_get_plane_number(fb);
>> +
>> +	for (i = 0; i < num_planes; i++) {
>> +		dma_addr_t dma_addr;
>> +
>> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
>> +		plane_state->dma_addr[i] = dma_addr;
>> +	}
>> +
>> +	return vs_dc_check_plane(dc, plane, state);
>> +}
>> +
>> +static int vs_cursor_plane_atomic_check(struct drm_plane *plane,
>> +					struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> +									  plane);
>> +	unsigned char i, num_planes;
>> +	struct drm_framebuffer *fb = new_plane_state->fb;
>> +	struct drm_crtc *crtc = new_plane_state->crtc;
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
>> +
>> +	if (!crtc || !fb)
>> +		return 0;
>> +
>> +	num_planes = vs_get_plane_number(fb);
>> +
>> +	for (i = 0; i < num_planes; i++) {
>> +		dma_addr_t dma_addr;
>> +
>> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
>> +		plane_state->dma_addr[i] = dma_addr;
>> +	}
>> +
>> +	return vs_dc_check_cursor_plane(dc, plane, state);
>> +}
>> +
>> +static void vs_plane_atomic_update(struct drm_plane *plane,
>> +				   struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
>> +									  plane);
> 
> New line after the equal sign will be better.
> 
>> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> +									  plane);
>> +
>> +	unsigned char i, num_planes;
>> +	struct drm_framebuffer *fb;
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	if (!new_state->fb || !new_state->crtc)
>> +		return;
> 
> if (!new_state->visible) ?
> 
>> +
>> +	fb = new_state->fb;
>> +
>> +	drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
>> +
>> +	num_planes = vs_get_plane_number(fb);
>> +
>> +	for (i = 0; i < num_planes; i++) {
>> +		dma_addr_t dma_addr;
>> +
>> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
>> +		plane_state->dma_addr[i] = dma_addr;
>> +	}
>> +
>> +	plane_state->status.src = drm_plane_state_src(new_state);
>> +	plane_state->status.dest = drm_plane_state_dest(new_state);
>> +
>> +	vs_dc_update_plane(dc, vs_plane, plane, state);
>> +}
>> +
>> +static void vs_cursor_plane_atomic_update(struct drm_plane *plane,
>> +					  struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
>> +									   plane);
>> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> +									   plane);
>> +	unsigned char i, num_planes;
>> +	struct drm_framebuffer *fb;
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
>> +	struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	if (!new_state->fb || !new_state->crtc)
> 
> and here.
> 
>> +		return;
>> +
>> +	fb = new_state->fb;
>> +	drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
>> +
>> +	num_planes = vs_get_plane_number(fb);
>> +
>> +	for (i = 0; i < num_planes; i++) {
>> +		dma_addr_t dma_addr;
>> +
>> +		dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
>> +		plane_state->dma_addr[i] = dma_addr;
>> +	}
>> +
>> +	plane_state->status.src = drm_plane_state_src(new_state);
>> +	plane_state->status.dest = drm_plane_state_dest(new_state);
>> +
>> +	vs_dc_update_cursor_plane(dc, vs_plane, plane, state);
>> +}
>> +
>> +static void vs_plane_atomic_disable(struct drm_plane *plane,
>> +				    struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> +									   plane);
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	vs_dc_disable_plane(dc, vs_plane, old_state);
>> +}
>> +
>> +static void vs_cursor_plane_atomic_disable(struct drm_plane *plane,
>> +					   struct drm_atomic_state *state)
>> +{
>> +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> +									   plane);
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +	struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
>> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> +
>> +	vs_dc_disable_cursor_plane(dc, vs_plane, old_state);
>> +}
>> +
>> +const struct drm_plane_helper_funcs primary_plane_helpers = {
>> +	.atomic_check	= vs_plane_atomic_check,
>> +	.atomic_update	= vs_plane_atomic_update,
>> +	.atomic_disable = vs_plane_atomic_disable,
>> +};
>> +
>> +const struct drm_plane_helper_funcs overlay_plane_helpers = {
>> +	.atomic_check	= vs_plane_atomic_check,
>> +	.atomic_update	= vs_plane_atomic_update,
>> +	.atomic_disable = vs_plane_atomic_disable,
>> +};
>> +
>> +const struct drm_plane_helper_funcs cursor_plane_helpers = {
>> +	.atomic_check	= vs_cursor_plane_atomic_check,
>> +	.atomic_update	= vs_cursor_plane_atomic_update,
>> +	.atomic_disable = vs_cursor_plane_atomic_disable,
>> +};
>> +
>> +static const struct drm_prop_enum_list vs_degamma_mode_enum_list[] = {
>> +	{ VS_DEGAMMA_DISABLE,	"disabled" },
>> +	{ VS_DEGAMMA_BT709,	"preset degamma for BT709" },
>> +	{ VS_DEGAMMA_BT2020,	"preset degamma for BT2020" },
>> +};
>> +
>> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
>> +				 struct vs_plane_info *info,
>> +				 unsigned int layer_num,
>> +				 unsigned int possible_crtcs)
>> +{
>> +	struct vs_plane *plane;
>> +	int ret;
>> +
>> +	if (!info)
>> +		return NULL;
>> +
>> +	plane = drmm_universal_plane_alloc(drm_dev, struct vs_plane, base,
>> +					   possible_crtcs,
>> +					   &vs_plane_funcs,
>> +					   info->formats, info->num_formats,
>> +					   info->modifiers, info->type,
>> +					   info->name ? info->name : NULL);
>> +	if (IS_ERR(plane))
>> +		return ERR_CAST(plane);
>> +
>> +	if (info->type == DRM_PLANE_TYPE_PRIMARY)
>> +		drm_plane_helper_add(&plane->base, &primary_plane_helpers);
>> +	else if (info->type == DRM_PLANE_TYPE_CURSOR)
>> +		drm_plane_helper_add(&plane->base, &cursor_plane_helpers);
>> +	else
>> +		drm_plane_helper_add(&plane->base, &overlay_plane_helpers);
>> +
>> +	/* Set up the plane properties */
>> +	if (info->degamma_size) {
>> +		plane->degamma_mode =
>> +		drm_property_create_enum(drm_dev, 0,
>> +					 "DEGAMMA_MODE",
>> +					 vs_degamma_mode_enum_list,
>> +					 ARRAY_SIZE(vs_degamma_mode_enum_list));
>> +
>> +		if (!plane->degamma_mode)
>> +			return NULL;
>> +
>> +		drm_object_attach_property(&plane->base.base,
>> +					   plane->degamma_mode,
>> +					   VS_DEGAMMA_DISABLE);
>> +	}
>> +
>> +	if (info->rotation) {
>> +		ret = drm_plane_create_rotation_property(&plane->base,
>> +							 DRM_MODE_ROTATE_0,
>> +							 info->rotation);
>> +		if (ret)
>> +			return NULL;
>> +	}
>> +
>> +	if (info->blend_mode) {
>> +		ret = drm_plane_create_blend_mode_property(&plane->base,
>> +							   info->blend_mode);
>> +		if (ret)
>> +			return NULL;
>> +		ret = drm_plane_create_alpha_property(&plane->base);
>> +		if (ret)
>> +			return NULL;
>> +	}
>> +
>> +	if (info->color_encoding) {
>> +		ret = drm_plane_create_color_properties(&plane->base,
>> +							info->color_encoding,
>> +							BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
>> +							DRM_COLOR_YCBCR_BT709,
>> +							DRM_COLOR_YCBCR_LIMITED_RANGE);
>> +		if (ret)
>> +			return NULL;
>> +	}
>> +
>> +	if (info->zpos != 255) {
>> +		ret = drm_plane_create_zpos_property(&plane->base, info->zpos, 0,
>> +						     layer_num - 1);
>> +		if (ret)
>> +			return NULL;
>> +	} else {
>> +		ret = drm_plane_create_zpos_immutable_property(&plane->base,
>> +							       info->zpos);
>> +		if (ret)
>> +			return NULL;
>> +	}
>> +
>> +	if (info->watermark) {
>> +		plane->watermark_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
>> +							    "WATERMARK", 0);
>> +		if (!plane->watermark_prop)
>> +			return NULL;
>> +		drm_object_attach_property(&plane->base.base, plane->watermark_prop, 0);
>> +	}
>> +
>> +	if (info->color_mgmt) {
>> +		plane->color_mgmt_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
>> +							     "COLOR_CONFIG", 0);
>> +		if (!plane->color_mgmt_prop)
>> +			return NULL;
>> +
>> +		drm_object_attach_property(&plane->base.base, plane->color_mgmt_prop, 0);
>> +	}
>> +
>> +	if (info->roi) {
>> +		plane->roi_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
>> +						      "ROI", 0);
>> +		if (!plane->roi_prop)
>> +			return NULL;
>> +
>> +		drm_object_attach_property(&plane->base.base, plane->roi_prop, 0);
>> +	}
>> +
>> +	return plane;
>> +}
>> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h
>> new file mode 100644
>> index 000000000..554b74e96
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_plane.h
>> @@ -0,0 +1,58 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#ifndef __VS_PLANE_H__
>> +#define __VS_PLANE_H__
>> +
>> +#include <drm/drm_fourcc.h>
>> +#include <drm/drm_plane_helper.h>
>> +
>> +#include "vs_type.h"
>> +
>> +struct vs_plane_status {
>> +	u32 tile_mode;
>> +	struct drm_rect src;
>> +	struct drm_rect dest;
>> +};
>> +
>> +struct vs_plane_state {
>> +	struct drm_plane_state base;
>> +	struct vs_plane_status status; /* for debugfs */
>> +
>> +	struct drm_property_blob *watermark;
>> +	struct drm_property_blob *color_mgmt;
>> +	struct drm_property_blob *roi;
>> +	dma_addr_t dma_addr[DRM_FORMAT_MAX_PLANES];
>> +
>> +	u32 degamma;
>> +	bool degamma_changed;
>> +};
>> +
>> +struct vs_plane {
>> +	struct drm_plane base;
>> +	u8 id;
>> +
>> +	struct drm_property *degamma_mode;
>> +	struct drm_property *watermark_prop;
>> +	struct drm_property *color_mgmt_prop;
>> +	struct drm_property *roi_prop;
>> +};
>> +
>> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
>> +				 struct vs_plane_info *info,
>> +				 unsigned int layer_num,
>> +				 unsigned int possible_crtcs);
>> +
>> +static inline struct vs_plane *to_vs_plane(struct drm_plane *plane)
>> +{
>> +	return container_of(plane, struct vs_plane, base);
>> +}
>> +
>> +static inline struct vs_plane_state *
>> +to_vs_plane_state(struct drm_plane_state *state)
>> +{
>> +	return container_of(state, struct vs_plane_state, base);
>> +}
>> +#endif /* __VS_PLANE_H__ */
>> diff --git a/drivers/gpu/drm/verisilicon/vs_type.h b/drivers/gpu/drm/verisilicon/vs_type.h
>> new file mode 100644
>> index 000000000..7d3378e29
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_type.h
>> @@ -0,0 +1,69 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> + */
>> +
>> +#ifndef __VS_TYPE_H__
>> +#define __VS_TYPE_H__
>> +
>> +#include <drm/drm_plane.h>
>> +#include <drm/drm_plane_helper.h>
>> +
>> +struct vs_plane_info {
>> +	const char *name;
>> +	u8 id;
>> +	enum drm_plane_type type;
>> +	unsigned int num_formats;
>> +	const u32 *formats;
>> +	u8 num_modifiers;
>> +	const u64 *modifiers;
>> +	unsigned int min_width;
>> +	unsigned int min_height;
>> +	unsigned int max_width;
>> +	unsigned int max_height;
>> +	unsigned int rotation;
>> +	unsigned int blend_mode;
>> +	unsigned int color_encoding;
>> +
>> +	/* 0 means no de-gamma LUT */
>> +	unsigned int degamma_size;
>> +
>> +	int min_scale; /* 16.16 fixed point */
>> +	int max_scale; /* 16.16 fixed point */
>> +
>> +	/* default zorder value,
>> +	 * and 255 means unsupported zorder capability
>> +	 */
>> +	u8	 zpos;
>> +
>> +	bool watermark;
>> +	bool color_mgmt;
>> +	bool roi;
>> +};
>> +
>> +struct vs_dc_info {
>> +	const char *name;
>> +
>> +	u8 panel_num;
>> +
>> +	/* planes */
>> +	u8 plane_num;
>> +	const struct vs_plane_info *planes;
>> +
>> +	u8 layer_num;
>> +	unsigned int max_bpc;
>> +	unsigned int color_formats;
>> +
>> +	/* 0 means no gamma LUT */
>> +	u16 gamma_size;
>> +	u8 gamma_bits;
>> +
>> +	u16 pitch_alignment;
>> +
>> +	bool pipe_sync;
>> +	bool background;
>> +	bool panel_sync;
>> +	bool cap_dec;
>> +};
>> +
>> +#endif /* __VS_TYPE_H__ */
> 

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
  2023-11-14 10:42       ` Keith Zhao
  (?)
@ 2023-11-14 10:59         ` Dmitry Baryshkov
  -1 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-11-14 10:59 UTC (permalink / raw)
  To: Keith Zhao
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Maxime Ripard, Jagan Teki, Rob Herring,
	Chris Morgan, Paul Walmsley, Bjorn Andersson, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Shawn Guo, christian.koenig

On Tue, 14 Nov 2023 at 12:42, Keith Zhao <keith.zhao@starfivetech.com> wrote:
>
>
>
> On 2023/10/26 3:28, Dmitry Baryshkov wrote:
> > On 25/10/2023 13:39, Keith Zhao wrote:
> >> add 2 crtcs and 8 planes in vs-drm
> >>
> >> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> >> ---
> >>   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
> >>   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
> >>   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
> >>   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
> >>   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
> >>   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
> >>   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
> >>   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
> >>   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
> >>   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
> >>   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
> >>   11 files changed, 4494 insertions(+), 2 deletions(-)
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
> >>
> >> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> >> index 7d3be305b..1d48016ca 100644
> >> --- a/drivers/gpu/drm/verisilicon/Makefile
> >> +++ b/drivers/gpu/drm/verisilicon/Makefile
> >> @@ -1,7 +1,11 @@
> >>   # SPDX-License-Identifier: GPL-2.0
> >>
> >> -vs_drm-objs := vs_drv.o \
> >> -            vs_modeset.o
> >> +vs_drm-objs := vs_dc_hw.o \
> >> +            vs_dc.o \
> >> +            vs_crtc.o \
> >> +            vs_drv.o \
> >> +            vs_modeset.o \
> >> +            vs_plane.o
> >>
> >>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
> >>
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
> >> new file mode 100644
> >> index 000000000..8a658ea77
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
> >> @@ -0,0 +1,257 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + *
> >> + */
> >> +
> >> +#include <linux/clk.h>
> >> +#include <linux/debugfs.h>
> >> +#include <linux/media-bus-format.h>
> >> +
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_atomic.h>
> >> +#include <drm/drm_crtc.h>
> >> +#include <drm/drm_gem_atomic_helper.h>
> >> +#include <drm/drm_vblank.h>
> >> +#include <drm/vs_drm.h>
> >> +
> >> +#include "vs_crtc.h"
> >> +#include "vs_dc.h"
> >> +#include "vs_drv.h"
> >> +
> >> +static void vs_crtc_reset(struct drm_crtc *crtc)
> >> +{
> >> +    struct vs_crtc_state *state;
> >> +
> >> +    if (crtc->state) {
> >> +            __drm_atomic_helper_crtc_destroy_state(crtc->state);
> >> +
> >> +            state = to_vs_crtc_state(crtc->state);
> >> +            kfree(state);
> >> +            crtc->state = NULL;
> >> +    }
> >
> > You can call your crtc_destroy_state function directly here.
> >
> >> +
> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
> >> +    if (!state)
> >> +            return;
> >> +
> >> +    __drm_atomic_helper_crtc_reset(crtc, &state->base);
> >> +}
> >> +
> >> +static struct drm_crtc_state *
> >> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
> >> +{
> >> +    struct vs_crtc_state *ori_state;
> >
> > It might be a matter of taste, but it is usually old_state.
> >
> >> +    struct vs_crtc_state *state;
> >> +
> >> +    if (!crtc->state)
> >> +            return NULL;
> >> +
> >> +    ori_state = to_vs_crtc_state(crtc->state);
> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
> >> +    if (!state)
> >> +            return NULL;
> >> +
> >> +    __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
> >> +
> >> +    state->output_fmt = ori_state->output_fmt;
> >> +    state->encoder_type = ori_state->encoder_type;
> >> +    state->bpp = ori_state->bpp;
> >> +    state->underflow = ori_state->underflow;
> >
> > Can you use kmemdup instead?
> >
> >> +
> >> +    return &state->base;
> >> +}
> >> +
> >> +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
> >> +                                     struct drm_crtc_state *state)
> >> +{
> >> +    __drm_atomic_helper_crtc_destroy_state(state);
> >> +    kfree(to_vs_crtc_state(state));
> >> +}
> >> +
> >> +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
> >> +{
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    vs_dc_enable_vblank(dc, true);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
> >> +{
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    vs_dc_enable_vblank(dc, false);
> >> +}
> >> +
> >> +static const struct drm_crtc_funcs vs_crtc_funcs = {
> >> +    .set_config             = drm_atomic_helper_set_config,
> >> +    .page_flip              = drm_atomic_helper_page_flip,
> >
> > destroy is required, see drm_mode_config_cleanup()
>
> hi Dmitry:
> if define destroy in drm_crtc_funcs,
> it will make __drmm_crtc_init_with_planes unhappy

Ack, I missed that you have been using drmm_crtc_init. BTW, I checked
your code, you should be able to switch drm
drmm_crtc_alloc_with_planes().

>
> see:
> __printf(6, 0)
> static int __drmm_crtc_init_with_planes(struct drm_device *dev,
>                                         struct drm_crtc *crtc,
>                                         struct drm_plane *primary,
>                                         struct drm_plane *cursor,
>                                         const struct drm_crtc_funcs *funcs,
>                                         const char *name,
>                                         va_list args)
> {
>         int ret;
>
>         drm_WARN_ON(dev, funcs && funcs->destroy);
>
> ........
> }
>
> It should not need to be defined here, I think
>
> >
> >> +    .reset                  = vs_crtc_reset,
> >> +    .atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
> >> +    .atomic_destroy_state   = vs_crtc_atomic_destroy_state,
> >
> > please consider adding atomic_print_state to output driver-specific bits.
> >
> >> +    .enable_vblank          = vs_crtc_enable_vblank,
> >> +    .disable_vblank         = vs_crtc_disable_vblank,
> >> +};
> >> +
> >> +static u8 cal_pixel_bits(u32 bus_format)
> >
> > This looks like a generic helper code, which can go to a common place.
> >
> >> +{
> >> +    u8 bpp;
> >> +
> >> +    switch (bus_format) {
> >> +    case MEDIA_BUS_FMT_RGB565_1X16:
> >> +    case MEDIA_BUS_FMT_UYVY8_1X16:
> >> +            bpp = 16;
> >> +            break;
> >> +    case MEDIA_BUS_FMT_RGB666_1X18:
> >> +    case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> >> +            bpp = 18;
> >> +            break;
> >> +    case MEDIA_BUS_FMT_UYVY10_1X20:
> >> +            bpp = 20;
> >> +            break;
> >> +    case MEDIA_BUS_FMT_BGR888_1X24:
> >> +    case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> >> +    case MEDIA_BUS_FMT_YUV8_1X24:
> >> +            bpp = 24;
> >> +            break;
> >> +    case MEDIA_BUS_FMT_RGB101010_1X30:
> >> +    case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> >> +    case MEDIA_BUS_FMT_YUV10_1X30:
> >> +            bpp = 30;
> >> +            break;
> >> +    default:
> >> +            bpp = 24;
> >> +            break;
> >> +    }
> >> +
> >> +    return bpp;
> >> +}
> >> +
> >> +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
> >> +                              struct drm_atomic_state *state)
> >> +{
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
> >> +
> >> +    vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
> >> +
> >> +    vs_dc_enable(dc, crtc);
> >> +    drm_crtc_vblank_on(crtc);
> >> +}
> >> +
> >> +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
> >> +                               struct drm_atomic_state *state)
> >> +{
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    drm_crtc_vblank_off(crtc);
> >> +
> >> +    vs_dc_disable(dc, crtc);
> >> +
> >> +    if (crtc->state->event && !crtc->state->active) {
> >> +            spin_lock_irq(&crtc->dev->event_lock);
> >> +            drm_crtc_send_vblank_event(crtc, crtc->state->event);
> >> +            spin_unlock_irq(&crtc->dev->event_lock);
> >> +
> >> +            crtc->state->event = NULL;
> >
> > I think even should be cleared within the lock.
> >
> >> +    }
> >> +}
> >> +
> >> +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
> >> +                             struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
> >> +                                                                      crtc);
> >> +
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct device *dev = vs_crtc->dev;
> >> +    struct drm_property_blob *blob = crtc->state->gamma_lut;
> >> +    struct drm_color_lut *lut;
> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
> >> +
> >> +    if (crtc_state->color_mgmt_changed) {
> >> +            if (blob && blob->length) {
> >> +                    lut = blob->data;
> >> +                    vs_dc_set_gamma(dc, crtc, lut,
> >> +                                    blob->length / sizeof(*lut));
> >> +                    vs_dc_enable_gamma(dc, crtc, true);
> >> +            } else {
> >> +                    vs_dc_enable_gamma(dc, crtc, false);
> >> +            }
> >> +    }
> >> +}
> >> +
> >> +static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
> >> +                             struct drm_atomic_state *state)
> >> +{
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct drm_pending_vblank_event *event = crtc->state->event;
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    vs_dc_commit(dc);
> >> +
> >> +    if (event) {
> >> +            WARN_ON(drm_crtc_vblank_get(crtc) != 0);
> >> +
> >> +            spin_lock_irq(&crtc->dev->event_lock);
> >> +            drm_crtc_arm_vblank_event(crtc, event);
> >> +            spin_unlock_irq(&crtc->dev->event_lock);
> >> +            crtc->state->event = NULL;
> >
> > I think even should be cleared within the lock.
> >
> >> +    }
> >> +}
> >> +
> >> +static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
> >> +    .atomic_check = drm_crtc_helper_atomic_check,
> >> +    .atomic_enable  = vs_crtc_atomic_enable,
> >> +    .atomic_disable = vs_crtc_atomic_disable,
> >> +    .atomic_begin   = vs_crtc_atomic_begin,
> >> +    .atomic_flush   = vs_crtc_atomic_flush,
> >> +};
> >> +
> >> +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
> >> +    { VS_SINGLE_DC,                         "single dc mode" },
> >> +    { VS_MULTI_DC_PRIMARY,          "primary dc for multi dc mode" },
> >> +    { VS_MULTI_DC_SECONDARY,        "secondary dc for multi dc mode" },
> >> +};
> >> +
> >> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
> >> +                           struct vs_dc_info *info)
> >> +{
> >> +    struct vs_crtc *crtc;
> >> +    int ret;
> >> +
> >> +    if (!info)
> >> +            return NULL;
> >> +
> >> +    crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
> >> +    if (!crtc)
> >> +            return NULL;
> >> +
> >> +    ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
> >> +                                     NULL, NULL, &vs_crtc_funcs,
> >> +                                     info->name ? info->name : NULL);
> >
> > It might be better to add drmm_crtc_init() helper.
> >
> >> +    if (ret)
> >> +            return NULL;
> >> +
> >> +    drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
> >> +
> >> +    if (info->gamma_size) {
> >> +            ret = drm_mode_crtc_set_gamma_size(&crtc->base,
> >> +                                               info->gamma_size);
> >> +            if (ret)
> >> +                    return NULL;
> >> +
> >> +            drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
> >> +                                       info->gamma_size);
> >> +    }
> >> +
> >> +    crtc->max_bpc = info->max_bpc;
> >> +    crtc->color_formats = info->color_formats;
> >> +    return crtc;
> >> +}
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h
> >> new file mode 100644
> >> index 000000000..526dd63e5
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.h
> >> @@ -0,0 +1,43 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#ifndef __VS_CRTC_H__
> >> +#define __VS_CRTC_H__
> >> +
> >> +#include <drm/drm_crtc.h>
> >> +#include <drm/drm_crtc_helper.h>
> >> +
> >> +#include "vs_type.h"
> >> +
> >> +struct vs_crtc_state {
> >> +    struct drm_crtc_state base;
> >> +
> >> +    u32 output_fmt;
> >> +    u8 encoder_type;
> >> +    u8 bpp;
> >> +    bool underflow;
> >> +};
> >> +
> >> +struct vs_crtc {
> >> +    struct drm_crtc base;
> >> +    struct device *dev;
> >> +    unsigned int max_bpc;
> >> +    unsigned int color_formats;
> >> +};
> >> +
> >> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
> >> +                           struct vs_dc_info *info);
> >> +
> >> +static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc)
> >> +{
> >> +    return container_of(crtc, struct vs_crtc, base);
> >> +}
> >> +
> >> +static inline struct vs_crtc_state *
> >> +to_vs_crtc_state(struct drm_crtc_state *state)
> >> +{
> >> +    return container_of(state, struct vs_crtc_state, base);
> >> +}
> >> +#endif /* __VS_CRTC_H__ */
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
> >> new file mode 100644
> >> index 000000000..b5ab92d98
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc.c
> >> @@ -0,0 +1,1002 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#include <linux/component.h>
> >> +#include <linux/clk.h>
> >> +#include <linux/of.h>
> >> +#include <linux/of_device.h>
> >> +#include <linux/pm_runtime.h>
> >> +#include <linux/reset.h>
> >> +
> >> +#include <drm/drm_atomic.h>
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_blend.h>
> >> +#include <drm/drm_framebuffer.h>
> >> +#include <drm/drm_vblank.h>
> >> +#include <drm/vs_drm.h>
> >> +
> >> +#include "vs_dc_hw.h"
> >> +#include "vs_dc.h"
> >> +#include "vs_drv.h"
> >> +
> >> +static const char * const vout_clocks[] = {
> >> +    "noc_bus",
> >> +    "channel0",
> >> +    "channel1",
> >> +    "dc_core",
> >> +    "axi_core",
> >> +    "ahb",
> >> +    "hdmi_tx",
> >> +    "dc_parent",
> >> +
> >> +};
> >> +
> >> +static const char * const vout_resets[] = {
> >> +    "axi",
> >> +    "ahb",
> >> +    "core",
> >> +};
> >> +
> >> +static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb)
> >> +{
> >> +    u8 f = FORMAT_A8R8G8B8;
> >> +
> >> +    switch (format) {
> >> +    case DRM_FORMAT_XRGB4444:
> >> +    case DRM_FORMAT_RGBX4444:
> >> +    case DRM_FORMAT_XBGR4444:
> >> +    case DRM_FORMAT_BGRX4444:
> >> +            f = FORMAT_X4R4G4B4;
> >> +            break;
> >> +    case DRM_FORMAT_ARGB4444:
> >> +    case DRM_FORMAT_RGBA4444:
> >> +    case DRM_FORMAT_ABGR4444:
> >> +    case DRM_FORMAT_BGRA4444:
> >> +            f = FORMAT_A4R4G4B4;
> >> +            break;
> >> +    case DRM_FORMAT_XRGB1555:
> >> +    case DRM_FORMAT_RGBX5551:
> >> +    case DRM_FORMAT_XBGR1555:
> >> +    case DRM_FORMAT_BGRX5551:
> >> +            f = FORMAT_X1R5G5B5;
> >> +            break;
> >> +    case DRM_FORMAT_ARGB1555:
> >> +    case DRM_FORMAT_RGBA5551:
> >> +    case DRM_FORMAT_ABGR1555:
> >> +    case DRM_FORMAT_BGRA5551:
> >> +            f = FORMAT_A1R5G5B5;
> >> +            break;
> >> +    case DRM_FORMAT_RGB565:
> >> +    case DRM_FORMAT_BGR565:
> >> +            f = FORMAT_R5G6B5;
> >> +            break;
> >> +    case DRM_FORMAT_XRGB8888:
> >> +    case DRM_FORMAT_RGBX8888:
> >> +    case DRM_FORMAT_XBGR8888:
> >> +    case DRM_FORMAT_BGRX8888:
> >> +            f = FORMAT_X8R8G8B8;
> >> +            break;
> >> +    case DRM_FORMAT_ARGB8888:
> >> +    case DRM_FORMAT_RGBA8888:
> >> +    case DRM_FORMAT_ABGR8888:
> >> +    case DRM_FORMAT_BGRA8888:
> >> +            f = FORMAT_A8R8G8B8;
> >> +            break;
> >> +    case DRM_FORMAT_YUYV:
> >> +    case DRM_FORMAT_YVYU:
> >> +            f = FORMAT_YUY2;
> >> +            break;
> >> +    case DRM_FORMAT_UYVY:
> >> +    case DRM_FORMAT_VYUY:
> >> +            f = FORMAT_UYVY;
> >> +            break;
> >> +    case DRM_FORMAT_YUV420:
> >> +    case DRM_FORMAT_YVU420:
> >> +            f = FORMAT_YV12;
> >> +            break;
> >> +    case DRM_FORMAT_NV21:
> >> +            f = FORMAT_NV12;
> >> +            break;
> >> +    case DRM_FORMAT_NV16:
> >> +    case DRM_FORMAT_NV61:
> >> +            f = FORMAT_NV16;
> >> +            break;
> >> +    case DRM_FORMAT_P010:
> >> +            f = FORMAT_P010;
> >> +            break;
> >> +    case DRM_FORMAT_ARGB2101010:
> >> +    case DRM_FORMAT_RGBA1010102:
> >> +    case DRM_FORMAT_ABGR2101010:
> >> +    case DRM_FORMAT_BGRA1010102:
> >> +            f = FORMAT_A2R10G10B10;
> >> +            break;
> >> +    case DRM_FORMAT_NV12:
> >> +            f = FORMAT_NV12;
> >> +            break;
> >> +    case DRM_FORMAT_YUV444:
> >> +            f = FORMAT_YUV444;
> >> +            break;
> >> +    default:
> >> +            break;
> >> +    }
> >> +
> >> +    fb->format = f;
> >> +}
> >> +
> >> +static inline void update_swizzle(u32 format, struct dc_hw_fb *fb)
> >> +{
> >> +    fb->swizzle = SWIZZLE_ARGB;
> >> +    fb->uv_swizzle = 0;
> >> +
> >> +    switch (format) {
> >> +    case DRM_FORMAT_RGBX4444:
> >> +    case DRM_FORMAT_RGBA4444:
> >> +    case DRM_FORMAT_RGBX5551:
> >> +    case DRM_FORMAT_RGBA5551:
> >> +    case DRM_FORMAT_RGBX8888:
> >> +    case DRM_FORMAT_RGBA8888:
> >> +    case DRM_FORMAT_RGBA1010102:
> >> +            fb->swizzle = SWIZZLE_RGBA;
> >> +            break;
> >> +    case DRM_FORMAT_XBGR4444:
> >> +    case DRM_FORMAT_ABGR4444:
> >> +    case DRM_FORMAT_XBGR1555:
> >> +    case DRM_FORMAT_ABGR1555:
> >> +    case DRM_FORMAT_BGR565:
> >> +    case DRM_FORMAT_XBGR8888:
> >> +    case DRM_FORMAT_ABGR8888:
> >> +    case DRM_FORMAT_ABGR2101010:
> >> +            fb->swizzle = SWIZZLE_ABGR;
> >> +            break;
> >> +    case DRM_FORMAT_BGRX4444:
> >> +    case DRM_FORMAT_BGRA4444:
> >> +    case DRM_FORMAT_BGRX5551:
> >> +    case DRM_FORMAT_BGRA5551:
> >> +    case DRM_FORMAT_BGRX8888:
> >> +    case DRM_FORMAT_BGRA8888:
> >> +    case DRM_FORMAT_BGRA1010102:
> >> +            fb->swizzle = SWIZZLE_BGRA;
> >> +            break;
> >> +    case DRM_FORMAT_YVYU:
> >> +    case DRM_FORMAT_VYUY:
> >> +    case DRM_FORMAT_NV21:
> >> +    case DRM_FORMAT_NV61:
> >> +            fb->uv_swizzle = 1;
> >> +            break;
> >> +    default:
> >> +            break;
> >> +    }
> >> +}
> >> +
> >> +static inline void update_watermark(struct drm_property_blob *watermark,
> >> +                                struct dc_hw_fb *fb)
> >> +{
> >> +    struct drm_vs_watermark *data;
> >> +
> >> +    fb->water_mark = 0;
> >> +
> >> +    if (watermark) {
> >> +            data = watermark->data;
> >> +            fb->water_mark = data->watermark & 0xFFFFF;
> >> +    }
> >> +}
> >> +
> >> +static inline u8 to_vs_rotation(unsigned int rotation)
> >> +{
> >> +    u8 rot;
> >> +
> >> +    switch (rotation & DRM_MODE_REFLECT_MASK) {
> >> +    case DRM_MODE_REFLECT_X:
> >> +            rot = FLIP_X;
> >> +            return rot;
> >> +    case DRM_MODE_REFLECT_Y:
> >> +            rot = FLIP_Y;
> >> +            return rot;
> >> +    case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
> >> +            rot = FLIP_XY;
> >> +            return rot;
> >> +    default:
> >> +            break;
> >> +    }
> >> +
> >> +    switch (rotation & DRM_MODE_ROTATE_MASK) {
> >> +    case DRM_MODE_ROTATE_0:
> >> +            rot = ROT_0;
> >> +            break;
> >> +    case DRM_MODE_ROTATE_90:
> >> +            rot = ROT_90;
> >> +            break;
> >> +    case DRM_MODE_ROTATE_180:
> >> +            rot = ROT_180;
> >> +            break;
> >> +    case DRM_MODE_ROTATE_270:
> >> +            rot = ROT_270;
> >> +            break;
> >> +    default:
> >> +            rot = ROT_0;
> >> +            break;
> >> +    }
> >> +
> >> +    return rot;
> >> +}
> >> +
> >> +static inline u8 to_vs_yuv_color_space(u32 color_space)
> >> +{
> >> +    u8 cs;
> >> +
> >> +    switch (color_space) {
> >> +    case DRM_COLOR_YCBCR_BT601:
> >> +            cs = COLOR_SPACE_601;
> >> +            break;
> >> +    case DRM_COLOR_YCBCR_BT709:
> >> +            cs = COLOR_SPACE_709;
> >> +            break;
> >> +    case DRM_COLOR_YCBCR_BT2020:
> >> +            cs = COLOR_SPACE_2020;
> >> +            break;
> >> +    default:
> >> +            cs = COLOR_SPACE_601;
> >> +            break;
> >> +    }
> >> +
> >> +    return cs;
> >> +}
> >> +
> >> +static inline u8 to_vs_tile_mode(u64 modifier)
> >> +{
> >> +    if (modifier == DRM_FORMAT_MOD_VIVANTE_TILED)
> >> +            return DC_TILE_MODE4X4;
> >> +
> >> +    return (u8)(modifier & DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK);
> >> +}
> >> +
> >> +static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc)
> >> +{
> >> +    u8 panel_num = dc->hw.info->panel_num;
> >> +    u32 index = drm_crtc_index(crtc);
> >> +    int i;
> >> +
> >> +    for (i = 0; i < panel_num; i++) {
> >> +            if (index == dc->crtc[i]->base.index)
> >> +                    return i;
> >> +    }
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
> >> +                                      unsigned int alignment)
> >> +{
> >> +    struct vs_drm_device *priv = to_vs_drm_private(drm_dev);
> >> +
> >> +    if (alignment > priv->pitch_alignment)
> >> +            priv->pitch_alignment = alignment;
> >> +}
> >> +
> >> +static int plda_clk_rst_init(struct device *dev)
> >> +{
> >> +    int ret = 0;
> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
> >> +
> >> +    ret = clk_bulk_prepare_enable(dc->nclks, dc->clk_vout);
> >> +    if (ret) {
> >> +            dev_err(dev, "failed to enable clocks\n");
> >> +            return ret;
> >> +    }
> >> +
> >> +    ret = reset_control_bulk_deassert(dc->nrsts, dc->rst_vout);
> >> +    return ret;
> >> +}
> >> +
> >> +static void plda_clk_rst_deinit(struct device *dev)
> >> +{
> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
> >> +
> >> +    reset_control_bulk_assert(dc->nrsts, dc->rst_vout);
> >> +    clk_bulk_disable_unprepare(dc->nclks, dc->clk_vout);
> >> +}
> >> +
> >> +static void dc_deinit(struct device *dev)
> >> +{
> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
> >> +
> >> +    dc_hw_enable_interrupt(&dc->hw, 0);
> >> +    dc_hw_deinit(&dc->hw);
> >> +    plda_clk_rst_deinit(dev);
> >> +}
> >> +
> >> +static int dc_init(struct device *dev)
> >> +{
> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
> >> +    int ret;
> >> +
> >> +    dc->first_frame = true;
> >> +
> >> +    ret = plda_clk_rst_init(dev);
> >> +    if (ret < 0) {
> >> +            dev_err(dev, "failed to init dc clk reset: %d\n", ret);
> >> +            return ret;
> >> +    }
> >> +
> >> +    ret = dc_hw_init(&dc->hw);
> >> +    if (ret) {
> >> +            dev_err(dev, "failed to init DC HW\n");
> >> +            return ret;
> >> +    }
> >> +    return 0;
> >> +}
> >> +
> >> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
> >> +{
> >> +    struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
> >> +    struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> >> +    struct dc_hw_display display;
> >> +
> >> +    display.bus_format = crtc_state->output_fmt;
> >> +    display.h_active = mode->hdisplay;
> >> +    display.h_total = mode->htotal;
> >> +    display.h_sync_start = mode->hsync_start;
> >> +    display.h_sync_end = mode->hsync_end;
> >> +    if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> >> +            display.h_sync_polarity = true;
> >> +    else
> >> +            display.h_sync_polarity = false;
> >> +
> >> +    display.v_active = mode->vdisplay;
> >> +    display.v_total = mode->vtotal;
> >> +    display.v_sync_start = mode->vsync_start;
> >> +    display.v_sync_end = mode->vsync_end;
> >> +
> >> +    if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> >> +            display.v_sync_polarity = true;
> >> +    else
> >> +            display.v_sync_polarity = false;
> >> +
> >> +    display.id = to_vs_display_id(dc, crtc);
> >> +
> >> +    display.enable = true;
> >> +
> >> +    if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) {
> >> +            dc_hw_set_out(&dc->hw, OUT_DPI, display.id);
> >> +            clk_set_rate(dc->clk_vout[CLK_VOUT_SOC_PIX].clk, mode->clock * 1000);
> >> +            clk_set_parent(dc->clk_vout[CLK_VOUT_PIX1].clk,
> >> +                           dc->clk_vout[CLK_VOUT_SOC_PIX].clk);
> >> +    } else {
> >> +            dc_hw_set_out(&dc->hw, OUT_DP, display.id);
> >> +            clk_set_parent(dc->clk_vout[CLK_VOUT_PIX0].clk,
> >> +                           dc->clk_vout[CLK_VOUT_HDMI_PIX].clk);
> >> +    }
> >> +
> >> +    dc_hw_setup_display(&dc->hw, &display);
> >> +}
> >> +
> >> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc)
> >> +{
> >> +    struct dc_hw_display display;
> >> +
> >> +    display.id = to_vs_display_id(dc, crtc);
> >> +    display.enable = false;
> >> +
> >> +    dc_hw_setup_display(&dc->hw, &display);
> >> +}
> >> +
> >> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> >> +                 struct drm_color_lut *lut, unsigned int size)
> >> +{
> >> +    u16 i, r, g, b;
> >> +    u8 bits, id;
> >> +
> >> +    if (size != dc->hw.info->gamma_size) {
> >> +            drm_err(crtc->dev, "gamma size does not match!\n");
> >> +            return;
> >> +    }
> >> +
> >> +    id = to_vs_display_id(dc, crtc);
> >> +
> >> +    bits = dc->hw.info->gamma_bits;
> >> +    for (i = 0; i < size; i++) {
> >> +            r = drm_color_lut_extract(lut[i].red, bits);
> >> +            g = drm_color_lut_extract(lut[i].green, bits);
> >> +            b = drm_color_lut_extract(lut[i].blue, bits);
> >> +            dc_hw_update_gamma(&dc->hw, id, i, r, g, b);
> >> +    }
> >> +}
> >> +
> >> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> >> +                    bool enable)
> >> +{
> >> +    u8 id;
> >> +
> >> +    id = to_vs_display_id(dc, crtc);
> >> +    dc_hw_enable_gamma(&dc->hw, id, enable);
> >> +}
> >> +
> >> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable)
> >> +{
> >> +    dc_hw_enable_interrupt(&dc->hw, enable);
> >> +}
> >> +
> >> +static u32 calc_factor(u32 src, u32 dest)
> >> +{
> >> +    u32 factor = 1 << 16;
> >> +
> >> +    if (src > 1 && dest > 1)
> >> +            factor = ((src - 1) << 16) / (dest - 1);
> >> +
> >> +    return factor;
> >> +}
> >> +
> >> +static void update_scale(struct drm_plane_state *state, struct dc_hw_roi *roi,
> >> +                     struct dc_hw_scale *scale)
> >> +{
> >> +    int dst_w = drm_rect_width(&state->dst);
> >> +    int dst_h = drm_rect_height(&state->dst);
> >> +    int src_w, src_h, temp;
> >> +
> >> +    scale->enable = false;
> >> +
> >> +    if (roi->enable) {
> >> +            src_w = roi->width;
> >> +            src_h = roi->height;
> >> +    } else {
> >> +            src_w = drm_rect_width(&state->src) >> 16;
> >> +            src_h = drm_rect_height(&state->src) >> 16;
> >> +    }
> >> +
> >> +    if (drm_rotation_90_or_270(state->rotation)) {
> >> +            temp = src_w;
> >> +            src_w = src_h;
> >> +            src_h = temp;
> >> +    }
> >> +
> >> +    if (src_w != dst_w) {
> >> +            scale->scale_factor_x = calc_factor(src_w, dst_w);
> >> +            scale->enable = true;
> >> +    } else {
> >> +            scale->scale_factor_x = 1 << 16;
> >> +    }
> >> +    if (src_h != dst_h) {
> >> +            scale->scale_factor_y = calc_factor(src_h, dst_h);
> >> +            scale->enable = true;
> >> +    } else {
> >> +            scale->scale_factor_y = 1 << 16;
> >> +    }
> >> +}
> >> +
> >> +static void update_fb(struct vs_plane *plane, u8 display_id,
> >> +                  struct dc_hw_fb *fb, struct drm_plane_state *state)
> >> +{
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
> >> +    struct drm_framebuffer *drm_fb = state->fb;
> >> +    struct drm_rect *src = &state->src;
> >> +
> >> +    fb->display_id = display_id;
> >> +    fb->y_address = plane_state->dma_addr[0];
> >> +    fb->y_stride = drm_fb->pitches[0];
> >> +    if (drm_fb->format->format == DRM_FORMAT_YVU420) {
> >> +            fb->u_address = plane_state->dma_addr[2];
> >> +            fb->v_address = plane_state->dma_addr[1];
> >> +            fb->u_stride = drm_fb->pitches[2];
> >> +            fb->v_stride = drm_fb->pitches[1];
> >> +    } else {
> >> +            fb->u_address = plane_state->dma_addr[1];
> >> +            fb->v_address = plane_state->dma_addr[2];
> >> +            fb->u_stride = drm_fb->pitches[1];
> >> +            fb->v_stride = drm_fb->pitches[2];
> >> +    }
> >> +    fb->width = drm_rect_width(src) >> 16;
> >> +    fb->height = drm_rect_height(src) >> 16;
> >> +    fb->tile_mode = to_vs_tile_mode(drm_fb->modifier);
> >> +    fb->rotation = to_vs_rotation(state->rotation);
> >> +    fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding);
> >> +    fb->zpos = state->zpos;
> >> +    fb->enable = state->visible;
> >> +    update_format(drm_fb->format->format, drm_fb->modifier, fb);
> >> +    update_swizzle(drm_fb->format->format, fb);
> >> +    update_watermark(plane_state->watermark, fb);
> >> +    plane_state->status.tile_mode = fb->tile_mode;
> >> +}
> >> +
> >> +static void update_degamma(struct vs_dc *dc, struct vs_plane *plane,
> >> +                       struct vs_plane_state *plane_state)
> >> +{
> >> +    dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
> >> +    plane_state->degamma_changed = false;
> >> +}
> >> +
> >> +static void update_roi(struct vs_dc *dc, u8 id,
> >> +                   struct vs_plane_state *plane_state,
> >> +                   struct dc_hw_roi *roi,
> >> +                   struct drm_plane_state *state)
> >> +{
> >> +    struct drm_vs_roi *data;
> >> +    struct drm_rect *src = &state->src;
> >> +    u16 src_w = drm_rect_width(src) >> 16;
> >> +    u16 src_h = drm_rect_height(src) >> 16;
> >> +
> >> +    if (plane_state->roi) {
> >> +            data = plane_state->roi->data;
> >> +
> >> +            if (data->enable) {
> >> +                    roi->x = data->roi_x;
> >> +                    roi->y = data->roi_y;
> >> +                    roi->width = (data->roi_x + data->roi_w > src_w) ?
> >> +                                             (src_w - data->roi_x) : data->roi_w;
> >> +                    roi->height = (data->roi_y + data->roi_h > src_h) ?
> >> +                                              (src_h - data->roi_y) : data->roi_h;
> >> +                    roi->enable = true;
> >> +            } else {
> >> +                    roi->enable = false;
> >> +            }
> >> +
> >> +            dc_hw_update_roi(&dc->hw, id, roi);
> >> +    } else {
> >> +            roi->enable = false;
> >> +    }
> >> +}
> >> +
> >> +static void update_color_mgmt(struct vs_dc *dc, u8 id,
> >> +                          struct dc_hw_fb *fb,
> >> +                          struct vs_plane_state *plane_state)
> >> +{
> >> +    struct drm_vs_color_mgmt *data;
> >> +    struct dc_hw_colorkey colorkey;
> >> +
> >> +    if (plane_state->color_mgmt) {
> >> +            data = plane_state->color_mgmt->data;
> >> +
> >> +            fb->clear_enable = data->clear_enable;
> >> +            fb->clear_value = data->clear_value;
> >> +
> >> +            if (data->colorkey > data->colorkey_high)
> >> +                    data->colorkey = data->colorkey_high;
> >> +
> >> +            colorkey.colorkey = data->colorkey;
> >> +            colorkey.colorkey_high = data->colorkey_high;
> >> +            colorkey.transparency = (data->transparency) ?
> >> +                            DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE;
> >> +            dc_hw_update_colorkey(&dc->hw, id, &colorkey);
> >> +    }
> >> +}
> >> +
> >> +static void update_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                     struct drm_plane *drm_plane,
> >> +                     struct drm_atomic_state *drm_state)
> >> +{
> >> +    struct dc_hw_fb fb = {0};
> >> +    struct dc_hw_scale scale;
> >> +    struct dc_hw_position pos;
> >> +    struct dc_hw_blend blend;
> >> +    struct dc_hw_roi roi;
> >> +    struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> >> +                                                                       drm_plane);
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
> >> +    struct drm_rect *dest = &state->dst;
> >> +    bool dec_enable = false;
> >> +    u8 display_id = 0;
> >> +
> >> +    display_id = to_vs_display_id(dc, state->crtc);
> >> +    update_fb(plane, display_id, &fb, state);
> >> +    fb.dec_enable = dec_enable;
> >> +
> >> +    update_roi(dc, plane->id, plane_state, &roi, state);
> >> +
> >> +    update_scale(state, &roi, &scale);
> >> +
> >> +    if (plane_state->degamma_changed)
> >> +            update_degamma(dc, plane, plane_state);
> >> +
> >> +    pos.start_x = dest->x1;
> >> +    pos.start_y = dest->y1;
> >> +    pos.end_x = dest->x2;
> >> +    pos.end_y = dest->y2;
> >> +
> >> +    blend.alpha = (u8)(state->alpha >> 8);
> >> +    blend.blend_mode = (u8)(state->pixel_blend_mode);
> >> +
> >> +    update_color_mgmt(dc, plane->id, &fb, plane_state);
> >> +
> >> +    dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
> >> +}
> >> +
> >> +static void update_qos(struct vs_dc *dc, struct vs_plane *plane,
> >> +                   struct drm_plane *drm_plane,
> >> +                   struct drm_atomic_state *drm_state)
> >> +{
> >> +    struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> >> +                                                                       drm_plane);
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
> >> +    struct drm_vs_watermark *data;
> >> +    struct dc_hw_qos qos;
> >> +
> >> +    if (plane_state->watermark) {
> >> +            data = plane_state->watermark->data;
> >> +
> >> +            if (data->qos_high) {
> >> +                    if (data->qos_low > data->qos_high)
> >> +                            data->qos_low = data->qos_high;
> >> +
> >> +                    qos.low_value = data->qos_low & 0x0F;
> >> +                    qos.high_value = data->qos_high & 0x0F;
> >> +                    dc_hw_update_qos(&dc->hw, &qos);
> >> +            }
> >> +    }
> >> +}
> >> +
> >> +static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor)
> >> +{
> >> +    u8 size_type;
> >> +
> >> +    switch (state->crtc_w) {
> >> +    case 32:
> >> +            size_type = CURSOR_SIZE_32X32;
> >> +            break;
> >> +    case 64:
> >> +            size_type = CURSOR_SIZE_64X64;
> >> +            break;
> >> +    default:
> >> +            size_type = CURSOR_SIZE_32X32;
> >> +            break;
> >> +    }
> >> +
> >> +    cursor->size = size_type;
> >> +}
> >> +
> >> +static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                            struct drm_plane *drm_plane,
> >> +                            struct drm_atomic_state *drm_state)
> >> +{
> >> +    struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> >> +                                                                   drm_plane);
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
> >> +    struct drm_framebuffer *drm_fb = state->fb;
> >> +    struct dc_hw_cursor cursor;
> >> +
> >> +    cursor.address = plane_state->dma_addr[0];
> >> +    cursor.x = state->crtc_x;
> >> +    cursor.y = state->crtc_y;
> >> +    cursor.hot_x = drm_fb->hot_x;
> >> +    cursor.hot_y = drm_fb->hot_y;
> >> +    cursor.display_id = to_vs_display_id(dc, state->crtc);
> >> +    update_cursor_size(state, &cursor);
> >> +    cursor.enable = true;
> >> +
> >> +    dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
> >> +}
> >> +
> >> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                    struct drm_plane *drm_plane,
> >> +                    struct drm_atomic_state *drm_state)
> >> +{
> >> +    update_plane(dc, plane, drm_plane, drm_state);
> >> +    update_qos(dc, plane, drm_plane, drm_state);
> >> +}
> >> +
> >> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                           struct drm_plane *drm_plane,
> >> +                           struct drm_atomic_state *drm_state)
> >> +{
> >> +    update_cursor_plane(dc, plane, drm_plane, drm_state);
> >> +}
> >> +
> >> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                     struct drm_plane_state *old_state)
> >> +{
> >> +    struct dc_hw_fb fb = {0};
> >> +
> >> +    fb.enable = false;
> >> +    dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL, NULL, NULL);
> >> +}
> >> +
> >> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                            struct drm_plane_state *old_state)
> >> +{
> >> +    struct dc_hw_cursor cursor = {0};
> >> +
> >> +    cursor.enable = false;
> >> +    cursor.display_id = to_vs_display_id(dc, old_state->crtc);
> >> +    dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
> >> +}
> >> +
> >> +static bool vs_dc_mod_supported(const struct vs_plane_info *plane_info,
> >> +                            u64 modifier)
> >> +{
> >> +    const u64 *mods;
> >> +
> >> +    if (!plane_info->modifiers)
> >> +            return false;
> >> +
> >> +    for (mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID; mods++) {
> >> +            if (*mods == modifier)
> >> +                    return true;
> >> +    }
> >> +
> >> +    return false;
> >> +}
> >> +
> >> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
> >> +                  struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> >> +                                                                             plane);
> >> +
> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
> >> +    const struct vs_plane_info *plane_info;
> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
> >> +    struct drm_crtc_state *crtc_state;
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +
> >> +    plane_info = &dc->hw.info->planes[vs_plane->id];
> >> +
> >> +    if (fb->width < plane_info->min_width ||
> >> +        fb->width > plane_info->max_width ||
> >> +        fb->height < plane_info->min_height ||
> >> +        fb->height > plane_info->max_height)
> >> +            drm_err_once(plane->dev, "buffer size may not support on plane%d.\n",
> >> +                         vs_plane->id);
> >> +
> >> +    if (!vs_dc_mod_supported(plane_info, fb->modifier)) {
> >> +            drm_err(plane->dev, "unsupported modifier on plane%d.\n", vs_plane->id);
> >> +            return -EINVAL;
> >> +    }
> >> +
> >> +    crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
> >> +    return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
> >> +                                              plane_info->min_scale,
> >> +                                              plane_info->max_scale,
> >> +                                              true, true);
> >> +}
> >> +
> >> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
> >> +                         struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> >> +                                                                       plane);
> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
> >> +    const struct vs_plane_info *plane_info;
> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
> >> +    struct drm_crtc_state *crtc_state;
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +
> >> +    plane_info = &dc->hw.info->planes[vs_plane->id];
> >> +
> >> +    if (fb->width < plane_info->min_width ||
> >> +        fb->width > plane_info->max_width ||
> >> +        fb->height < plane_info->min_height ||
> >> +        fb->height > plane_info->max_height)
> >> +            drm_err_once(plane->dev, "buffer size may not support on plane%d.\n", vs_plane->id);
> >> +
> >> +    crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
> >> +    if (IS_ERR(crtc_state))
> >> +            return -EINVAL;
> >> +
> >> +    return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
> >> +                                            plane_info->min_scale,
> >> +                                            plane_info->max_scale,
> >> +                                            true, true);
> >> +}
> >> +
> >> +static void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
> >> +{
> >> +    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
> >> +
> >> +    drm_crtc_handle_vblank(crtc);
> >> +
> >> +    vs_crtc_state->underflow = underflow;
> >> +}
> >> +
> >> +static irqreturn_t dc_isr(int irq, void *data)
> >> +{
> >> +    struct vs_dc *dc = data;
> >> +    struct vs_dc_info *dc_info = dc->hw.info;
> >> +    u32 i, ret;
> >> +
> >> +    ret = dc_hw_get_interrupt(&dc->hw);
> >> +
> >> +    for (i = 0; i < dc_info->panel_num; i++)
> >> +            vs_crtc_handle_vblank(&dc->crtc[i]->base, dc_hw_check_underflow(&dc->hw));
> >> +
> >> +    return IRQ_HANDLED;
> >> +}
> >> +
> >> +void vs_dc_commit(struct vs_dc *dc)
> >> +{
> >> +    dc_hw_enable_shadow_register(&dc->hw, false);
> >> +
> >> +    dc_hw_commit(&dc->hw);
> >> +
> >> +    if (dc->first_frame)
> >> +            dc->first_frame = false;
> >> +
> >> +    dc_hw_enable_shadow_register(&dc->hw, true);
> >> +}
> >> +
> >> +static int dc_bind(struct device *dev, struct device *master, void *data)
> >> +{
> >> +    struct drm_device *drm_dev = data;
> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
> >> +    struct device_node *port;
> >> +    struct vs_crtc *crtc;
> >> +    struct vs_dc_info *dc_info;
> >> +    struct vs_plane *plane;
> >> +    struct vs_plane_info *plane_info;
> >> +    int i, ret;
> >> +    u32 ctrc_mask = 0;
> >> +
> >> +    if (!drm_dev || !dc) {
> >> +            dev_err(dev, "devices are not created.\n");
> >> +            return -ENODEV;
> >> +    }
> >> +
> >> +    ret = dc_init(dev);
> >> +    if (ret < 0) {
> >> +            drm_err(drm_dev, "Failed to initialize DC hardware.\n");
> >> +            return ret;
> >> +    }
> >> +
> >> +    port = of_get_child_by_name(dev->of_node, "port");
> >> +    if (!port) {
> >> +            drm_err(drm_dev, "no port node found\n");
> >> +            return -ENODEV;
> >> +    }
> >> +    of_node_put(port);
> >> +
> >> +    dc_info = dc->hw.info;
> >> +
> >> +    for (i = 0; i < dc_info->panel_num; i++) {
> >> +            crtc = vs_crtc_create(drm_dev, dc_info);
> >> +            if (!crtc) {
> >> +                    drm_err(drm_dev, "Failed to create CRTC.\n");
> >> +                    ret = -ENOMEM;
> >> +                    return ret;
> >> +            }
> >> +
> >> +            crtc->base.port = port;
> >> +            crtc->dev = dev;
> >> +            dc->crtc[i] = crtc;
> >> +            ctrc_mask |= drm_crtc_mask(&crtc->base);
> >> +    }
> >> +
> >> +    for (i = 0; i < dc_info->plane_num; i++) {
> >> +            plane_info = (struct vs_plane_info *)&dc_info->planes[i];
> >> +
> >> +            if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
> >> +                    plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
> >> +                                            drm_crtc_mask(&dc->crtc[0]->base));
> >> +            } else if (!strcmp(plane_info->name, "Primary_1") ||
> >> +                               !strcmp(plane_info->name, "Cursor_1")) {
> >> +                    plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
> >> +                                            drm_crtc_mask(&dc->crtc[1]->base));
> >
> > I'd say, it's not typical to use name to distinguish objects. Maybe you
> > can add indices to the info structure and drop the names instead?
> >
> >
> >> +            } else {
> >> +                    plane = vs_plane_create(drm_dev, plane_info,
> >> +                                            dc_info->layer_num, ctrc_mask);
> >> +            }
> >> +
> >> +            if (IS_ERR(plane)) {
> >> +                    dev_err(dev, "failed to construct plane\n");
> >> +                    return PTR_ERR(plane);
> >> +            }
> >> +
> >> +            plane->id = i;
> >> +            dc->planes[i].id = plane_info->id;
> >> +
> >> +            if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
> >> +                    if (!strcmp(plane_info->name, "Primary"))
> >> +                            dc->crtc[0]->base.primary = &plane->base;
> >> +                    else
> >> +                            dc->crtc[1]->base.primary = &plane->base;
> >
> > This definitely urges for an index in the plane info / plane_state.
> >
> >> +                    drm_dev->mode_config.min_width = plane_info->min_width;
> >> +                    drm_dev->mode_config.min_height =
> >> +                                                    plane_info->min_height;
> >> +                    drm_dev->mode_config.max_width = plane_info->max_width;
> >> +                    drm_dev->mode_config.max_height =
> >> +                                                    plane_info->max_height;
> >> +            }
> >> +
> >> +            if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
> >> +                    if (!strcmp(plane_info->name, "Cursor"))
> >> +                            dc->crtc[0]->base.cursor = &plane->base;
> >> +                    else
> >> +                            dc->crtc[1]->base.cursor = &plane->base;
> >> +                    drm_dev->mode_config.cursor_width =
> >> +                                                    plane_info->max_width;
> >> +                    drm_dev->mode_config.cursor_height =
> >> +                                                    plane_info->max_height;
> >> +            }
> >> +    }
> >> +
> >> +    vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
> >> +    return 0;
> >> +}
> >> +
> >> +static void dc_unbind(struct device *dev, struct device *master, void *data)
> >> +{
> >> +    dc_deinit(dev);
> >> +}
> >> +
> >> +const struct component_ops dc_component_ops = {
> >> +    .bind = dc_bind,
> >> +    .unbind = dc_unbind,
> >> +};
> >> +
> >> +static const struct of_device_id dc_driver_dt_match[] = {
> >> +    { .compatible = "starfive,jh7110-dc8200", },
> >> +    {},
> >> +};
> >> +MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
> >> +
> >> +static int dc_probe(struct platform_device *pdev)
> >> +{
> >> +    struct device *dev = &pdev->dev;
> >> +    struct vs_dc *dc;
> >> +    int irq, ret, i;
> >> +
> >> +    dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
> >> +    if (!dc)
> >> +            return -ENOMEM;
> >> +
> >> +    dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
> >> +    if (IS_ERR(dc->hw.hi_base))
> >> +            return PTR_ERR(dc->hw.hi_base);
> >> +
> >> +    dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
> >> +    if (IS_ERR(dc->hw.reg_base))
> >> +            return PTR_ERR(dc->hw.reg_base);
> >> +
> >> +    dc->nclks = ARRAY_SIZE(dc->clk_vout);
> >> +    for (i = 0; i < dc->nclks; ++i)
> >> +            dc->clk_vout[i].id = vout_clocks[i];
> >> +    ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
> >> +    if (ret) {
> >> +            dev_err(dev, "Failed to get clk controls\n");
> >> +            return ret;
> >> +    }
> >> +
> >> +    dc->nrsts = ARRAY_SIZE(dc->rst_vout);
> >> +    for (i = 0; i < dc->nrsts; ++i)
> >> +            dc->rst_vout[i].id = vout_resets[i];
> >> +    ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
> >> +                                             dc->rst_vout);
> >> +    if (ret) {
> >> +            dev_err(dev, "Failed to get reset controls\n");
> >> +            return ret;
> >> +    }
> >> +
> >> +    irq = platform_get_irq(pdev, 0);
> >> +
> >> +    ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
> >
> > Are you ready to handle the IRQ at this point?
> >
> >> +    if (ret < 0) {
> >> +            dev_err(dev, "Failed to install irq:%u.\n", irq);
> >> +            return ret;
> >> +    }
> >> +
> >> +    dev_set_drvdata(dev, dc);
> >> +
> >> +    return component_add(dev, &dc_component_ops);
> >> +}
> >> +
> >> +static int dc_remove(struct platform_device *pdev)
> >> +{
> >> +    struct device *dev = &pdev->dev;
> >> +
> >> +    component_del(dev, &dc_component_ops);
> >> +
> >> +    dev_set_drvdata(dev, NULL);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +struct platform_driver dc_platform_driver = {
> >> +    .probe = dc_probe,
> >> +    .remove = dc_remove,
> >> +    .driver = {
> >> +            .name = "vs-dc",
> >> +            .of_match_table = of_match_ptr(dc_driver_dt_match),
> >> +    },
> >> +};
> >> +
> >> +MODULE_AUTHOR("StarFive Corporation");
> >> +MODULE_DESCRIPTION("VeriSilicon DC Driver");
> >> +MODULE_LICENSE("GPL");
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
> >> new file mode 100644
> >> index 000000000..8e96fd32c
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc.h
> >> @@ -0,0 +1,80 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#ifndef __VS_DC_H__
> >> +#define __VS_DC_H__
> >> +
> >> +#include <linux/clk.h>
> >> +#include <linux/mm_types.h>
> >> +#include <linux/reset.h>
> >> +
> >> +#include <drm/drm_fourcc.h>
> >> +#include <drm/drm_modes.h>
> >> +
> >> +#include "vs_crtc.h"
> >> +#include "vs_dc_hw.h"
> >> +#include "vs_plane.h"
> >> +
> >> +struct vs_dc_plane {
> >> +    enum dc_hw_plane_id id;
> >> +};
> >> +
> >> +enum vout_clk {
> >> +    CLK_VOUT_NOC_DISP = 0,
> >> +    CLK_VOUT_PIX0,
> >> +    CLK_VOUT_PIX1,
> >> +    CLK_VOUT_CORE,
> >> +    CLK_VOUT_AXI,
> >> +    CLK_VOUT_AHB,
> >> +    CLK_VOUT_HDMI_PIX,
> >> +    CLK_VOUT_SOC_PIX,
> >> +    CLK_VOUT_NUM
> >> +};
> >> +
> >> +enum rst_vout {
> >> +    RST_VOUT_AXI = 0,
> >> +    RST_VOUT_AHB,
> >> +    RST_VOUT_CORE,
> >> +    RST_VOUT_NUM
> >> +};
> >> +
> >> +struct vs_dc {
> >> +    struct vs_crtc          *crtc[DC_DISPLAY_NUM];
> >> +    struct dc_hw            hw;
> >> +    bool                    first_frame;
> >> +
> >> +    struct vs_dc_plane      planes[PLANE_NUM];
> >> +    struct clk_bulk_data    clk_vout[CLK_VOUT_NUM];
> >> +    int                     nclks;
> >> +    struct reset_control_bulk_data rst_vout[RST_VOUT_NUM];
> >> +    int                     nrsts;
> >> +};
> >> +
> >> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc);
> >> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc);
> >> +
> >> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> >> +                 struct drm_color_lut *lut, unsigned int size);
> >> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc, bool enable);
> >> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable);
> >> +void vs_dc_commit(struct vs_dc *dc);
> >> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                    struct drm_plane *drm_plane,
> >> +                    struct drm_atomic_state *drm_state);
> >> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                     struct drm_plane_state *old_state);
> >> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
> >> +                  struct drm_atomic_state *state);
> >> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                           struct drm_plane *drm_plane,
> >> +                           struct drm_atomic_state *drm_state);
> >> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                            struct drm_plane_state *old_state);
> >> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
> >> +                         struct drm_atomic_state *state);
> >> +
> >> +extern struct platform_driver dc_platform_driver;
> >> +
> >> +#endif /* __VS_DC_H__ */
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
> >> new file mode 100644
> >> index 000000000..e5acb7eb4
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
> >> @@ -0,0 +1,1959 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#include <linux/bits.h>
> >> +#include <linux/io.h>
> >> +#include <linux/media-bus-format.h>
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_blend.h>
> >> +#include <drm/drm_fourcc.h>
> >> +#include <drm/vs_drm.h>
> >> +
> >> +#include "vs_dc_hw.h"
> >> +#include "vs_type.h"
> >> +
> >> +static const u32 horkernel[] = {
> >> +    0x00000000, 0x20000000, 0x00002000, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x2b981468,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x10f00000,
> >> +    0x00002f10, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x3781087f, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x06660000, 0x0000399a, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x01470000,
> >> +    0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x00004000, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x20002000, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x27e1181f,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x14680000,
> >> +    0x00002b98, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x2f1010f0, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x0dc70000, 0x00003239, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x087f0000, 0x00003781, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x399a0666, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x3cc4033c,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x021f0000,
> >> +};
> >> +
> >> +#define H_COEF_SIZE (sizeof(horkernel) / sizeof(u32))
> >> +
> >> +static const u32 verkernel[] = {
> >> +    0x00000000, 0x20000000, 0x00002000, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x2b981468,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x10f00000,
> >> +    0x00002f10, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x3781087f, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x06660000, 0x0000399a, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x01470000,
> >> +    0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x00004000, 0x00000000,
> >> +    0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab,
> >> +    0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
> >> +    0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +};
> >> +
> >> +#define V_COEF_SIZE (sizeof(verkernel) / sizeof(u32))
> >> +
> >> +/*
> >> + * RGB 709->2020 conversion parameters
> >> + */
> >> +static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = {
> >> +    10279,  5395,   709,
> >> +    1132,   15065,  187,
> >> +    269,    1442,   14674
> >> +};
> >> +
> >> +/*
> >> + * YUV601 to RGB conversion parameters
> >> + * YUV2RGB[0]  - [8] : C0 - C8;
> >> + * YUV2RGB[9]  - [11]: D0 - D2;
> >> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> >> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> >> + */
> >> +static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> >> +    1196,   0,                      1640,   1196,
> >> +    -404,   -836,           1196,   2076,
> >> +    0,              -916224,        558336, -1202944,
> >> +    64,              940,           64,             960
> >> +};
> >> +
> >> +/*
> >> + * YUV709 to RGB conversion parameters
> >> + * YUV2RGB[0]  - [8] : C0 - C8;
> >> + * YUV2RGB[9]  - [11]: D0 - D2;
> >> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> >> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> >> + */
> >> +static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> >> +    1196,           0,              1844,   1196,
> >> +    -220,           -548,   1196,   2172,
> >> +    0,                      -1020672, 316672,  -1188608,
> >> +    64,                     940,            64,             960
> >> +};
> >> +
> >> +/*
> >> + * YUV2020 to RGB conversion parameters
> >> + * YUV2RGB[0]  - [8] : C0 - C8;
> >> + * YUV2RGB[9]  - [11]: D0 - D2;
> >> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> >> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> >> + */
> >> +static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> >> +    1196, 0, 1724, 1196,
> >> +    -192, -668, 1196, 2200,
> >> +    0, -959232, 363776, -1202944,
> >> +    64, 940, 64, 960
> >> +};
> >> +
> >> +/*
> >> + * RGB to YUV2020 conversion parameters
> >> + * RGB2YUV[0] - [8] : C0 - C8;
> >> + * RGB2YUV[9] - [11]: D0 - D2;
> >> + */
> >> +static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = {
> >> +    230,    594,    52,
> >> +    -125,  -323,    448,
> >> +    448,   -412,   -36,
> >> +    64,             512,    512
> >> +};
> >> +
> >> +/*
> >> + * Degamma table for 709 color space data.
> >> + */
> >> +static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005,
> >> +    0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024,
> >> +    0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064,
> >> +    0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce,
> >> +    0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166,
> >> +    0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230,
> >> +    0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331,
> >> +    0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c,
> >> +    0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
> >> +    0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d,
> >> +    0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999,
> >> +    0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb,
> >> +    0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65,
> >> +    0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a,
> >> +    0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b,
> >> +    0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc,
> >> +    0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e,
> >> +    0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
> >> +    0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e,
> >> +    0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf,
> >> +    0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea,
> >> +    0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f,
> >> +    0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930,
> >> +    0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f,
> >> +    0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee,
> >> +    0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde,
> >> +    0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
> >> +    0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9,
> >> +    0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406,
> >> +    0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a,
> >> +    0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576,
> >> +    0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd,
> >> +    0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
> >> +};
> >> +
> >> +/*
> >> + * Degamma table for 2020 color space data.
> >> + */
> >> +static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> >> +    0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
> >> +    0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
> >> +    0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006,
> >> +    0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a,
> >> +    0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f,
> >> +    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
> >> +    0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024,
> >> +    0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035,
> >> +    0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d,
> >> +    0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d,
> >> +    0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098,
> >> +    0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3,
> >> +    0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123,
> >> +    0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d,
> >> +    0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
> >> +    0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6,
> >> +    0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1,
> >> +    0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d,
> >> +    0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6,
> >> +    0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920,
> >> +    0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a,
> >> +    0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033,
> >> +    0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593,
> >> +    0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
> >> +    0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656,
> >> +    0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332,
> >> +    0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480,
> >> +    0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1,
> >> +    0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99,
> >> +    0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
> >> +};
> >> +
> >> +/* one is for primary plane and the other is for all overlay planes */
> >> +static const struct dc_hw_plane_reg dc_plane_reg[] = {
> >> +    {
> >> +    .y_address              = DC_FRAMEBUFFER_ADDRESS,
> >> +    .u_address              = DC_FRAMEBUFFER_U_ADDRESS,
> >> +    .v_address              = DC_FRAMEBUFFER_V_ADDRESS,
> >> +    .y_stride               = DC_FRAMEBUFFER_STRIDE,
> >> +    .u_stride               = DC_FRAMEBUFFER_U_STRIDE,
> >> +    .v_stride               = DC_FRAMEBUFFER_V_STRIDE,
> >> +    .size                   = DC_FRAMEBUFFER_SIZE,
> >> +    .top_left               = DC_FRAMEBUFFER_TOP_LEFT,
> >> +    .bottom_right   = DC_FRAMEBUFFER_BOTTOM_RIGHT,
> >> +    .scale_factor_x                 = DC_FRAMEBUFFER_SCALE_FACTOR_X,
> >> +    .scale_factor_y                 = DC_FRAMEBUFFER_SCALE_FACTOR_Y,
> >> +    .h_filter_coef_index    = DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
> >> +    .h_filter_coef_data             = DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
> >> +    .v_filter_coef_index    = DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
> >> +    .v_filter_coef_data             = DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
> >> +    .init_offset                    = DC_FRAMEBUFFER_INIT_OFFSET,
> >> +    .color_key                              = DC_FRAMEBUFFER_COLOR_KEY,
> >> +    .color_key_high                 = DC_FRAMEBUFFER_COLOR_KEY_HIGH,
> >> +    .clear_value                    = DC_FRAMEBUFFER_CLEAR_VALUE,
> >> +    .color_table_index              = DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
> >> +    .color_table_data               = DC_FRAMEBUFFER_COLOR_TABLE_DATA,
> >> +    .scale_config                   = DC_FRAMEBUFFER_SCALE_CONFIG,
> >> +    .water_mark                             = DC_FRAMEBUFFER_WATER_MARK,
> >> +    .degamma_index                  = DC_FRAMEBUFFER_DEGAMMA_INDEX,
> >> +    .degamma_data                   = DC_FRAMEBUFFER_DEGAMMA_DATA,
> >> +    .degamma_ex_data                = DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
> >> +    .src_global_color               = DC_FRAMEBUFFER_SRC_GLOBAL_COLOR,
> >> +    .dst_global_color               = DC_FRAMEBUFFER_DST_GLOBAL_COLOR,
> >> +    .blend_config                   = DC_FRAMEBUFFER_BLEND_CONFIG,
> >> +    .roi_origin                             = DC_FRAMEBUFFER_ROI_ORIGIN,
> >> +    .roi_size                               = DC_FRAMEBUFFER_ROI_SIZE,
> >> +    .yuv_to_rgb_coef0                       = DC_FRAMEBUFFER_YUVTORGB_COEF0,
> >> +    .yuv_to_rgb_coef1                       = DC_FRAMEBUFFER_YUVTORGB_COEF1,
> >> +    .yuv_to_rgb_coef2                       = DC_FRAMEBUFFER_YUVTORGB_COEF2,
> >> +    .yuv_to_rgb_coef3                       = DC_FRAMEBUFFER_YUVTORGB_COEF3,
> >> +    .yuv_to_rgb_coef4                       = DC_FRAMEBUFFER_YUVTORGB_COEF4,
> >> +    .yuv_to_rgb_coefd0                      = DC_FRAMEBUFFER_YUVTORGB_COEFD0,
> >> +    .yuv_to_rgb_coefd1                      = DC_FRAMEBUFFER_YUVTORGB_COEFD1,
> >> +    .yuv_to_rgb_coefd2                      = DC_FRAMEBUFFER_YUVTORGB_COEFD2,
> >> +    .y_clamp_bound                          = DC_FRAMEBUFFER_Y_CLAMP_BOUND,
> >> +    .uv_clamp_bound                         = DC_FRAMEBUFFER_UV_CLAMP_BOUND,
> >> +    .rgb_to_rgb_coef0                       = DC_FRAMEBUFFER_RGBTORGB_COEF0,
> >> +    .rgb_to_rgb_coef1                       = DC_FRAMEBUFFER_RGBTORGB_COEF1,
> >> +    .rgb_to_rgb_coef2                       = DC_FRAMEBUFFER_RGBTORGB_COEF2,
> >> +    .rgb_to_rgb_coef3                       = DC_FRAMEBUFFER_RGBTORGB_COEF3,
> >> +    .rgb_to_rgb_coef4                       = DC_FRAMEBUFFER_RGBTORGB_COEF4,
> >> +    },
> >> +    {
> >> +    .y_address              = DC_OVERLAY_ADDRESS,
> >> +    .u_address              = DC_OVERLAY_U_ADDRESS,
> >> +    .v_address              = DC_OVERLAY_V_ADDRESS,
> >> +    .y_stride               = DC_OVERLAY_STRIDE,
> >> +    .u_stride               = DC_OVERLAY_U_STRIDE,
> >> +    .v_stride               = DC_OVERLAY_V_STRIDE,
> >> +    .size                   = DC_OVERLAY_SIZE,
> >> +    .top_left               = DC_OVERLAY_TOP_LEFT,
> >> +    .bottom_right   = DC_OVERLAY_BOTTOM_RIGHT,
> >> +    .scale_factor_x = DC_OVERLAY_SCALE_FACTOR_X,
> >> +    .scale_factor_y = DC_OVERLAY_SCALE_FACTOR_Y,
> >> +    .h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
> >> +    .h_filter_coef_data  = DC_OVERLAY_H_FILTER_COEF_DATA,
> >> +    .v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
> >> +    .v_filter_coef_data  = DC_OVERLAY_V_FILTER_COEF_DATA,
> >> +    .init_offset             = DC_OVERLAY_INIT_OFFSET,
> >> +    .color_key                       = DC_OVERLAY_COLOR_KEY,
> >> +    .color_key_high                 = DC_OVERLAY_COLOR_KEY_HIGH,
> >> +    .clear_value             = DC_OVERLAY_CLEAR_VALUE,
> >> +    .color_table_index       = DC_OVERLAY_COLOR_TABLE_INDEX,
> >> +    .color_table_data        = DC_OVERLAY_COLOR_TABLE_DATA,
> >> +    .scale_config            = DC_OVERLAY_SCALE_CONFIG,
> >> +    .water_mark                             = DC_OVERLAY_WATER_MARK,
> >> +    .degamma_index           = DC_OVERLAY_DEGAMMA_INDEX,
> >> +    .degamma_data            = DC_OVERLAY_DEGAMMA_DATA,
> >> +    .degamma_ex_data         = DC_OVERLAY_DEGAMMA_EX_DATA,
> >> +    .src_global_color        = DC_OVERLAY_SRC_GLOBAL_COLOR,
> >> +    .dst_global_color        = DC_OVERLAY_DST_GLOBAL_COLOR,
> >> +    .blend_config            = DC_OVERLAY_BLEND_CONFIG,
> >> +    .roi_origin                             = DC_OVERLAY_ROI_ORIGIN,
> >> +    .roi_size                               = DC_OVERLAY_ROI_SIZE,
> >> +    .yuv_to_rgb_coef0                = DC_OVERLAY_YUVTORGB_COEF0,
> >> +    .yuv_to_rgb_coef1                = DC_OVERLAY_YUVTORGB_COEF1,
> >> +    .yuv_to_rgb_coef2                = DC_OVERLAY_YUVTORGB_COEF2,
> >> +    .yuv_to_rgb_coef3                = DC_OVERLAY_YUVTORGB_COEF3,
> >> +    .yuv_to_rgb_coef4                       = DC_OVERLAY_YUVTORGB_COEF4,
> >> +    .yuv_to_rgb_coefd0                      = DC_OVERLAY_YUVTORGB_COEFD0,
> >> +    .yuv_to_rgb_coefd1                      = DC_OVERLAY_YUVTORGB_COEFD1,
> >> +    .yuv_to_rgb_coefd2                      = DC_OVERLAY_YUVTORGB_COEFD2,
> >> +    .y_clamp_bound           = DC_OVERLAY_Y_CLAMP_BOUND,
> >> +    .uv_clamp_bound          = DC_OVERLAY_UV_CLAMP_BOUND,
> >> +    .rgb_to_rgb_coef0                = DC_OVERLAY_RGBTORGB_COEF0,
> >> +    .rgb_to_rgb_coef1                = DC_OVERLAY_RGBTORGB_COEF1,
> >> +    .rgb_to_rgb_coef2                = DC_OVERLAY_RGBTORGB_COEF2,
> >> +    .rgb_to_rgb_coef3                = DC_OVERLAY_RGBTORGB_COEF3,
> >> +    .rgb_to_rgb_coef4                = DC_OVERLAY_RGBTORGB_COEF4,
> >> +    },
> >> +};
> >> +
> >> +static const u32 primary_overlay_format0[] = {
> >> +    DRM_FORMAT_XRGB4444,
> >> +    DRM_FORMAT_XBGR4444,
> >> +    DRM_FORMAT_RGBX4444,
> >> +    DRM_FORMAT_BGRX4444,
> >> +    DRM_FORMAT_ARGB4444,
> >> +    DRM_FORMAT_ABGR4444,
> >> +    DRM_FORMAT_RGBA4444,
> >> +    DRM_FORMAT_BGRA4444,
> >> +    DRM_FORMAT_XRGB1555,
> >> +    DRM_FORMAT_XBGR1555,
> >> +    DRM_FORMAT_RGBX5551,
> >> +    DRM_FORMAT_BGRX5551,
> >> +    DRM_FORMAT_ARGB1555,
> >> +    DRM_FORMAT_ABGR1555,
> >> +    DRM_FORMAT_RGBA5551,
> >> +    DRM_FORMAT_BGRA5551,
> >> +    DRM_FORMAT_RGB565,
> >> +    DRM_FORMAT_BGR565,
> >> +    DRM_FORMAT_XRGB8888,
> >> +    DRM_FORMAT_XBGR8888,
> >> +    DRM_FORMAT_RGBX8888,
> >> +    DRM_FORMAT_BGRX8888,
> >> +    DRM_FORMAT_ARGB8888,
> >> +    DRM_FORMAT_ABGR8888,
> >> +    DRM_FORMAT_RGBA8888,
> >> +    DRM_FORMAT_BGRA8888,
> >> +    DRM_FORMAT_ARGB2101010,
> >> +    DRM_FORMAT_ABGR2101010,
> >> +    DRM_FORMAT_RGBA1010102,
> >> +    DRM_FORMAT_BGRA1010102,
> >> +    DRM_FORMAT_YUYV,
> >> +    DRM_FORMAT_YVYU,
> >> +    DRM_FORMAT_UYVY,
> >> +    DRM_FORMAT_VYUY,
> >> +    DRM_FORMAT_YVU420,
> >> +    DRM_FORMAT_YUV420,
> >> +    DRM_FORMAT_NV12,
> >> +    DRM_FORMAT_NV21,
> >> +    DRM_FORMAT_NV16,
> >> +    DRM_FORMAT_NV61,
> >> +    DRM_FORMAT_P010,
> >> +};
> >> +
> >> +static const u32 primary_overlay_format1[] = {
> >> +    DRM_FORMAT_XRGB8888,
> >> +    DRM_FORMAT_XBGR8888,
> >> +    DRM_FORMAT_RGBX8888,
> >> +    DRM_FORMAT_BGRX8888,
> >> +    DRM_FORMAT_ARGB8888,
> >> +    DRM_FORMAT_ABGR8888,
> >> +    DRM_FORMAT_RGBA8888,
> >> +    DRM_FORMAT_BGRA8888,
> >> +    DRM_FORMAT_ARGB2101010,
> >> +    DRM_FORMAT_ABGR2101010,
> >> +    DRM_FORMAT_RGBA1010102,
> >> +    DRM_FORMAT_BGRA1010102,
> >> +    DRM_FORMAT_NV12,
> >> +    DRM_FORMAT_NV21,
> >> +    DRM_FORMAT_YUV444,
> >> +};
> >> +
> >> +static const u32 cursor_formats[] = {
> >> +    DRM_FORMAT_ARGB8888
> >> +};
> >> +
> >> +static const u64 format_modifier0[] = {
> >> +    DRM_FORMAT_MOD_LINEAR,
> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8),
> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8),
> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X8),
> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X4),
> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4),
> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8),
> >> +    DRM_FORMAT_MOD_INVALID
> >> +};
> >> +
> >> +static const u64 secondary_format_modifiers[] = {
> >> +    DRM_FORMAT_MOD_LINEAR,
> >> +    DRM_FORMAT_MOD_INVALID
> >> +};
> >> +
> >> +#define FRAC_16_16(mult, div)        (((mult) << 16) / (div))
> >> +
> >> +static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = {
> >> +    {
> >> +            /* DC_REV_0 */
> >> +            {
> >> +            .name                   = "Primary",
> >
> > I'd say that the indentation is strange here.
> > It might be more scalable to split a single array into per-generation
> > data structures and reference them directly from the info struct.
> >
> >> +            .id                             = PRIMARY_PLANE_0,
> >> +            .type                   = DRM_PLANE_TYPE_PRIMARY,
> >> +            .num_formats    = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats                = primary_overlay_format0,
> >> +            .num_modifiers  = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers              = format_modifier0,
> >> +            .min_width              = 0,
> >> +            .min_height             = 0,
> >> +            .max_width              = 4096,
> >> +            .max_height             = 4096,
> >> +            .rotation               = DRM_MODE_ROTATE_0 |
> >> +                                                    DRM_MODE_ROTATE_90 |
> >> +                                                    DRM_MODE_ROTATE_180 |
> >> +                                                    DRM_MODE_ROTATE_270 |
> >> +                                                    DRM_MODE_REFLECT_X |
> >> +                                                    DRM_MODE_REFLECT_Y,
> >> +            .blend_mode             = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                                    BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                                    BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 0,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name                   = "Overlay",
> >> +            .id                             = OVERLAY_PLANE_0,
> >> +            .type                   = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats    = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats                = primary_overlay_format0,
> >> +            .num_modifiers  = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers              = format_modifier0,
> >> +            .min_width              = 0,
> >> +            .min_height             = 0,
> >> +            .max_width              = 4096,
> >> +            .max_height             = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 1,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name                   = "Overlay_1",
> >> +            .id                             = OVERLAY_PLANE_1,
> >> +            .type                   = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats    = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats                = primary_overlay_format0,
> >> +            .num_modifiers  = ARRAY_SIZE(secondary_format_modifiers),
> >> +            .modifiers              = secondary_format_modifiers,
> >> +            .min_width              = 0,
> >> +            .min_height             = 0,
> >> +            .max_width              = 4096,
> >> +            .max_height             = 4096,
> >> +            .rotation               = 0,
> >> +            .blend_mode             = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                                    BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                                    BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 2,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Primary_1",
> >> +            .id                             = PRIMARY_PLANE_1,
> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats         = primary_overlay_format0,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 3,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay_2",
> >> +            .id                             = OVERLAY_PLANE_2,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats         = primary_overlay_format0,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 4,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay_3",
> >> +            .id                     = OVERLAY_PLANE_3,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats         = primary_overlay_format0,
> >> +            .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
> >> +            .modifiers       = secondary_format_modifiers,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = 0,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 5,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Cursor",
> >> +            .id                             = CURSOR_PLANE_0,
> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
> >> +            .formats         = cursor_formats,
> >> +            .num_modifiers = 0,
> >> +            .modifiers       = NULL,
> >> +            .min_width       = 32,
> >> +            .min_height  = 32,
> >> +            .max_width       = 64,
> >> +            .max_height  = 64,
> >> +            .rotation        = 0,
> >> +            .degamma_size = 0,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 255,
> >> +            .watermark       = false,
> >> +            .color_mgmt  = false,
> >> +            .roi             = false,
> >> +            },
> >> +            {
> >> +            .name            = "Cursor_1",
> >> +            .id                             = CURSOR_PLANE_1,
> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
> >> +            .formats         = cursor_formats,
> >> +            .num_modifiers = 0,
> >> +            .modifiers       = NULL,
> >> +            .min_width       = 32,
> >> +            .min_height  = 32,
> >> +            .max_width       = 64,
> >> +            .max_height  = 64,
> >> +            .rotation        = 0,
> >> +            .degamma_size = 0,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 255,
> >> +            .watermark       = false,
> >> +            .color_mgmt  = false,
> >> +            .roi             = false,
> >> +            },
> >> +    },
> >> +    {
> >> +            /* DC_REV_1 */
> >> +            {
> >> +            .name            = "Primary",
> >> +            .id                             = PRIMARY_PLANE_0,
> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats         = primary_overlay_format0,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 0,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay",
> >> +            .id                             = OVERLAY_PLANE_0,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats         = primary_overlay_format0,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 1,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Primary_1",
> >> +            .id                             = PRIMARY_PLANE_1,
> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats         = primary_overlay_format0,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 2,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay_2",
> >> +            .id                             = OVERLAY_PLANE_2,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats         = primary_overlay_format0,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 3,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Cursor",
> >> +            .id                             = CURSOR_PLANE_0,
> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
> >> +            .formats         = cursor_formats,
> >> +            .num_modifiers = 0,
> >> +            .modifiers       = NULL,
> >> +            .min_width       = 32,
> >> +            .min_height  = 32,
> >> +            .max_width       = 64,
> >> +            .max_height  = 64,
> >> +            .rotation        = 0,
> >> +            .degamma_size = 0,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 255,
> >> +            .watermark       = false,
> >> +            .color_mgmt  = false,
> >> +            .roi             = false,
> >> +            },
> >> +            {
> >> +            .name            = "Cursor_1",
> >> +            .id                             = CURSOR_PLANE_1,
> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
> >> +            .formats         = cursor_formats,
> >> +            .num_modifiers = 0,
> >> +            .modifiers       = NULL,
> >> +            .min_width       = 32,
> >> +            .min_height  = 32,
> >> +            .max_width       = 64,
> >> +            .max_height  = 64,
> >> +            .rotation        = 0,
> >> +            .degamma_size = 0,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 255,
> >> +            .watermark       = false,
> >> +            .color_mgmt  = false,
> >> +            .roi             = false,
> >> +            },
> >> +    },
> >> +    {
> >> +            /* DC_REV_2 */
> >> +            {
> >> +            .name            = "Primary",
> >> +            .id                             = PRIMARY_PLANE_0,
> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
> >> +            .formats         = primary_overlay_format1,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 0,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay",
> >> +            .id                             = OVERLAY_PLANE_0,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
> >> +            .formats         = primary_overlay_format1,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 1,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay_1",
> >> +            .id                             = OVERLAY_PLANE_1,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
> >> +            .formats         = primary_overlay_format1,
> >> +            .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
> >> +            .modifiers       = secondary_format_modifiers,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = 0,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 2,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Primary_1",
> >> +            .id                             = PRIMARY_PLANE_1,
> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
> >> +            .formats         = primary_overlay_format1,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 3,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay_2",
> >> +            .id                             = OVERLAY_PLANE_2,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
> >> +            .formats         = primary_overlay_format1,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 4,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay_3",
> >> +            .id                             = OVERLAY_PLANE_3,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
> >> +            .formats         = primary_overlay_format1,
> >> +            .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
> >> +            .modifiers       = secondary_format_modifiers,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = 0,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 5,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Cursor",
> >> +            .id                             = CURSOR_PLANE_0,
> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
> >> +            .formats         = cursor_formats,
> >> +            .num_modifiers = 0,
> >> +            .modifiers       = NULL,
> >> +            .min_width       = 32,
> >> +            .min_height  = 32,
> >> +            .max_width       = 64,
> >> +            .max_height  = 64,
> >> +            .rotation        = 0,
> >> +            .degamma_size = 0,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 255,
> >> +            .watermark       = false,
> >> +            .color_mgmt  = false,
> >> +            .roi             = false,
> >> +            },
> >> +            {
> >> +            .name            = "Cursor_1",
> >> +            .id                             = CURSOR_PLANE_1,
> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
> >> +            .formats         = cursor_formats,
> >> +            .num_modifiers = 0,
> >> +            .modifiers       = NULL,
> >> +            .min_width       = 32,
> >> +            .min_height  = 32,
> >> +            .max_width       = 64,
> >> +            .max_height  = 64,
> >> +            .rotation        = 0,
> >> +            .degamma_size = 0,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 255,
> >> +            .watermark       = false,
> >> +            .color_mgmt  = false,
> >> +            .roi             = false,
> >> +            },
> >> +    },
> >> +};
> >> +
> >> +static const struct vs_dc_info dc_info[] = {
> >
> > Again, please split into per-generation instances.
> >
> >> +    {
> >> +            /* DC_REV_0 */
> >> +            .name                   = "DC8200",
> >> +            .panel_num              = 2,
> >> +            .plane_num              = 8,
> >> +            .planes                 = dc_hw_planes[DC_REV_0],
> >> +            .layer_num              = 6,
> >> +            .max_bpc                = 10,
> >> +            .color_formats  = DRM_COLOR_FORMAT_RGB444 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR444 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR422 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR420,
> >> +            .gamma_size             = GAMMA_EX_SIZE,
> >> +            .gamma_bits             = 12,
> >> +            .pitch_alignment        = 128,
> >> +            .pipe_sync              = false,
> >> +            .background             = true,
> >> +            .panel_sync             = true,
> >> +            .cap_dec                = true,
> >> +    },
> >> +    {
> >> +            /* DC_REV_1 */
> >> +            .name                   = "DC8200",
> >> +            .panel_num              = 2,
> >> +            .plane_num              = 6,
> >> +            .planes                 = dc_hw_planes[DC_REV_1],
> >> +            .layer_num              = 4,
> >> +            .max_bpc                = 10,
> >> +            .color_formats  = DRM_COLOR_FORMAT_RGB444 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR444 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR422 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR420,
> >> +            .gamma_size             = GAMMA_EX_SIZE,
> >> +            .gamma_bits             = 12,
> >> +            .pitch_alignment        = 128,
> >> +            .pipe_sync              = false,
> >> +            .background             = true,
> >> +            .panel_sync             = true,
> >> +            .cap_dec                = true,
> >> +    },
> >> +    {
> >> +            /* DC_REV_2 */
> >> +            .name                   = "DC8200",
> >> +            .panel_num              = 2,
> >> +            .plane_num              = 8,
> >> +            .planes                 = dc_hw_planes[DC_REV_2],
> >> +            .layer_num              = 6,
> >> +            .max_bpc                = 10,
> >> +            .color_formats  = DRM_COLOR_FORMAT_RGB444 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR444 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR422 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR420,
> >> +            .gamma_size             = GAMMA_EX_SIZE,
> >> +            .gamma_bits             = 12,
> >> +            .pitch_alignment        = 128,
> >> +            .pipe_sync              = false,
> >> +            .background             = true,
> >> +            .panel_sync             = true,
> >> +            .cap_dec                = false,
> >> +    },
> >> +};
> >> +
> >> +static const struct dc_hw_funcs hw_func;
> >> +
> >> +static inline u32 hi_read(struct dc_hw *hw, u32 reg)
> >> +{
> >> +    return readl(hw->hi_base + reg);
> >> +}
> >> +
> >> +static inline void hi_write(struct dc_hw *hw, u32 reg, u32 value)
> >> +{
> >> +    writel(value, hw->hi_base + reg);
> >> +}
> >> +
> >> +static inline void dc_write(struct dc_hw *hw, u32 reg, u32 value)
> >> +{
> >> +    writel(value, hw->reg_base + reg - DC_REG_BASE);
> >> +}
> >> +
> >> +static inline u32 dc_read(struct dc_hw *hw, u32 reg)
> >> +{
> >> +    u32 value = readl(hw->reg_base + reg - DC_REG_BASE);
> >> +
> >> +    return value;
> >> +}
> >> +
> >> +static inline void dc_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear)
> >> +{
> >> +    u32 value = dc_read(hw, reg);
> >> +
> >> +    value &= ~clear;
> >> +    value |= set;
> >> +    dc_write(hw, reg, value);
> >> +}
> >> +
> >> +static void load_default_filter(struct dc_hw *hw,
> >> +                            const struct dc_hw_plane_reg *reg, u32 offset)
> >> +{
> >> +    u8 i;
> >> +
> >> +    dc_write(hw, reg->scale_config + offset, 0x33);
> >> +    dc_write(hw, reg->init_offset + offset, 0x80008000);
> >> +    dc_write(hw, reg->h_filter_coef_index + offset, 0x00);
> >> +    for (i = 0; i < H_COEF_SIZE; i++)
> >> +            dc_write(hw, reg->h_filter_coef_data + offset, horkernel[i]);
> >> +
> >> +    dc_write(hw, reg->v_filter_coef_index + offset, 0x00);
> >> +    for (i = 0; i < V_COEF_SIZE; i++)
> >> +            dc_write(hw, reg->v_filter_coef_data + offset, verkernel[i]);
> >> +}
> >> +
> >> +static void load_rgb_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
> >> +                        u32 offset, u16 *table)
> >> +{
> >> +    dc_write(hw, reg->rgb_to_rgb_coef0 + offset, table[0] | (table[1] << 16));
> >> +    dc_write(hw, reg->rgb_to_rgb_coef1 + offset, table[2] | (table[3] << 16));
> >> +    dc_write(hw, reg->rgb_to_rgb_coef2 + offset, table[4] | (table[5] << 16));
> >> +    dc_write(hw, reg->rgb_to_rgb_coef3 + offset, table[6] | (table[7] << 16));
> >> +    dc_write(hw, reg->rgb_to_rgb_coef4 + offset, table[8]);
> >> +}
> >> +
> >> +static void load_yuv_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
> >> +                        u32 offset, s32 *table)
> >> +{
> >> +    dc_write(hw, reg->yuv_to_rgb_coef0 + offset,
> >> +             (0xFFFF & table[0]) | (table[1] << 16));
> >> +    dc_write(hw, reg->yuv_to_rgb_coef1 + offset,
> >> +             (0xFFFF & table[2]) | (table[3] << 16));
> >> +    dc_write(hw, reg->yuv_to_rgb_coef2 + offset,
> >> +             (0xFFFF & table[4]) | (table[5] << 16));
> >> +    dc_write(hw, reg->yuv_to_rgb_coef3 + offset,
> >> +             (0xFFFF & table[6]) | (table[7] << 16));
> >> +    dc_write(hw, reg->yuv_to_rgb_coef4 + offset, table[8]);
> >> +    dc_write(hw, reg->yuv_to_rgb_coefd0 + offset, table[9]);
> >> +    dc_write(hw, reg->yuv_to_rgb_coefd1 + offset, table[10]);
> >> +    dc_write(hw, reg->yuv_to_rgb_coefd2 + offset, table[11]);
> >> +    dc_write(hw, reg->y_clamp_bound + offset, table[12] | (table[13] << 16));
> >> +    dc_write(hw, reg->uv_clamp_bound + offset, table[14] | (table[15] << 16));
> >> +}
> >> +
> >> +static void load_rgb_to_yuv(struct dc_hw *hw, u32 offset, s16 *table)
> >> +{
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF0 + offset,
> >> +             table[0] | (table[1] << 16));
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF1 + offset,
> >> +             table[2] | (table[3] << 16));
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF2 + offset,
> >> +             table[4] | (table[5] << 16));
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF3 + offset,
> >> +             table[6] | (table[7] << 16));
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF4 + offset, table[8]);
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD0 + offset, table[9]);
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD1 + offset, table[10]);
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD2 + offset, table[11]);
> >> +}
> >> +
> >> +static bool is_rgb(enum dc_hw_color_format format)
> >> +{
> >> +    switch (format) {
> >> +    case FORMAT_X4R4G4B4:
> >> +    case FORMAT_A4R4G4B4:
> >> +    case FORMAT_X1R5G5B5:
> >> +    case FORMAT_A1R5G5B5:
> >> +    case FORMAT_R5G6B5:
> >> +    case FORMAT_X8R8G8B8:
> >> +    case FORMAT_A8R8G8B8:
> >> +    case FORMAT_A2R10G10B10:
> >> +            return true;
> >> +    default:
> >> +            return false;
> >> +    }
> >> +}
> >> +
> >> +static void load_degamma_table(struct dc_hw *hw,
> >> +                           const struct dc_hw_plane_reg *reg,
> >> +                           u32 offset, u16 *table)
> >> +{
> >> +    u16 i;
> >> +    u32 value;
> >> +
> >> +    dc_write(hw, reg->degamma_index + offset, 0);
> >> +
> >> +    for (i = 0; i < DEGAMMA_SIZE; i++) {
> >> +            value = table[i] | (table[i] << 16);
> >> +            dc_write(hw, reg->degamma_data + offset, value);
> >> +            dc_write(hw, reg->degamma_ex_data + offset, table[i]);
> >> +    }
> >> +}
> >> +
> >> +static u32 get_addr_offset(u32 id)
> >> +{
> >> +    u32 offset = 0;
> >> +
> >> +    switch (id) {
> >> +    case PRIMARY_PLANE_1:
> >> +    case OVERLAY_PLANE_1:
> >> +            offset = 0x04;
> >> +            break;
> >> +    case OVERLAY_PLANE_2:
> >> +            offset = 0x08;
> >> +            break;
> >> +    case OVERLAY_PLANE_3:
> >> +            offset = 0x0C;
> >> +            break;
> >> +    default:
> >> +            break;
> >> +    }
> >> +
> >> +    return offset;
> >> +}
> >> +
> >> +int dc_hw_init(struct dc_hw *hw)
> >> +{
> >> +    u8 i, id, panel_num, layer_num;
> >> +    u32 offset;
> >> +    u32 revision = hi_read(hw, DC_HW_REVISION);
> >> +    u32 cid = hi_read(hw, DC_HW_CHIP_CID);
> >> +    const struct dc_hw_plane_reg *reg;
> >> +
> >> +    switch (revision) {
> >> +    case 0x5720:
> >> +            hw->rev = DC_REV_0;
> >> +            break;
> >> +    case 0x5721:
> >> +            switch (cid) {
> >> +            case 0x30B:
> >> +                    hw->rev = DC_REV_1;
> >> +                    break;
> >> +            case 0x310:
> >> +                    hw->rev = DC_REV_2;
> >> +                    break;
> >> +            default:
> >> +                    hw->rev = DC_REV_0;
> >> +                    break;
> >> +            }
> >> +            break;
> >> +    default:
> >> +            return -ENXIO;
> >> +    }
> >
> > We (drm/msm) had code like this for a while (and autodetection of the HW
> > ID). And nowadays we are migrating to of_device_get_match_data and
> > per-SoC compat names. It is just easier to handle, it allows one to
> > specify device more precisely and also it simplifies DT schema.
> >
> >> +
> >> +    hw->info = (struct vs_dc_info *)&dc_info[hw->rev];
> >> +    hw->func = (struct dc_hw_funcs *)&hw_func;
> >> +
> >> +    layer_num = hw->info->layer_num;
> >> +    for (i = 0; i < layer_num; i++) {
> >> +            id = hw->info->planes[i].id;
> >> +            offset = get_addr_offset(id);
> >> +            if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
> >> +                    reg = &dc_plane_reg[0];
> >> +            else
> >> +                    reg = &dc_plane_reg[1];
> >> +
> >> +            load_default_filter(hw, reg, offset);
> >> +            load_rgb_to_rgb(hw, reg, offset, RGB2RGB);
> >> +    }
> >> +
> >> +    panel_num = hw->info->panel_num;
> >> +    for (i = 0; i < panel_num; i++) {
> >> +            offset = i << 2;
> >> +
> >> +            load_rgb_to_yuv(hw, offset, RGB2YUV);
> >> +            dc_write(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0x111);
> >> +
> >> +            offset = i ? DC_CURSOR_OFFSET : 0;
> >> +            dc_write(hw, DC_CURSOR_BACKGROUND + offset, 0x00FFFFFF);
> >> +            dc_write(hw, DC_CURSOR_FOREGROUND + offset, 0x00AAAAAA);
> >> +    }
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +void dc_hw_deinit(struct dc_hw *hw)
> >> +{
> >> +    /* Nothing to do */
> >> +}
> >> +
> >> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
> >> +                    struct dc_hw_fb *fb, struct dc_hw_scale *scale,
> >> +                    struct dc_hw_position *pos, struct dc_hw_blend *blend)
> >> +{
> >> +    struct dc_hw_plane *plane = &hw->plane[id];
> >> +
> >> +    if (plane) {
> >> +            if (fb) {
> >> +                    if (!fb->enable)
> >> +                            plane->fb.enable = false;
> >> +                    else
> >> +                            memcpy(&plane->fb, fb,
> >> +                                   sizeof(*fb) - sizeof(fb->dirty));
> >> +                    plane->fb.dirty = true;
> >> +            }
> >> +            if (scale) {
> >> +                    memcpy(&plane->scale, scale,
> >> +                           sizeof(*scale) - sizeof(scale->dirty));
> >> +                    plane->scale.dirty = true;
> >> +            }
> >> +            if (pos) {
> >> +                    memcpy(&plane->pos, pos,
> >> +                           sizeof(*pos) - sizeof(pos->dirty));
> >> +                    plane->pos.dirty = true;
> >> +            }
> >> +            if (blend) {
> >> +                    memcpy(&plane->blend, blend,
> >> +                           sizeof(*blend) - sizeof(blend->dirty));
> >> +                    plane->blend.dirty = true;
> >> +            }
> >> +    }
> >> +}
> >> +
> >> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode)
> >> +{
> >> +    struct dc_hw_plane *plane = &hw->plane[id];
> >> +
> >> +    if (plane) {
> >> +            if (hw->info->planes[id].degamma_size) {
> >> +                    plane->degamma.mode = mode;
> >> +                    plane->degamma.dirty = true;
> >> +            } else {
> >> +                    plane->degamma.dirty = false;
> >> +            }
> >> +    }
> >> +}
> >> +
> >> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi)
> >> +{
> >> +    struct dc_hw_plane *plane = &hw->plane[id];
> >> +
> >> +    if (plane) {
> >> +            memcpy(&plane->roi, roi, sizeof(*roi) - sizeof(roi->dirty));
> >> +            plane->roi.dirty = true;
> >> +    }
> >> +}
> >> +
> >> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
> >> +                       struct dc_hw_colorkey *colorkey)
> >> +{
> >> +    struct dc_hw_plane *plane = &hw->plane[id];
> >> +
> >> +    if (plane) {
> >> +            memcpy(&plane->colorkey, colorkey,
> >> +                   sizeof(*colorkey) - sizeof(colorkey->dirty));
> >> +            plane->colorkey.dirty = true;
> >> +    }
> >> +}
> >> +
> >> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos)
> >> +{
> >> +    memcpy(&hw->qos, qos, sizeof(*qos) - sizeof(qos->dirty));
> >> +    hw->qos.dirty = true;
> >> +}
> >> +
> >> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor)
> >> +{
> >> +    memcpy(&hw->cursor[id], cursor, sizeof(*cursor) - sizeof(cursor->dirty));
> >> +    hw->cursor[id].dirty = true;
> >> +}
> >> +
> >> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
> >> +                    u16 r, u16 g, u16 b)
> >> +{
> >> +    if (index >= hw->info->gamma_size)
> >> +            return;
> >> +
> >> +    hw->gamma[id].gamma[index][0] = r;
> >> +    hw->gamma[id].gamma[index][1] = g;
> >> +    hw->gamma[id].gamma[index][2] = b;
> >> +    hw->gamma[id].dirty = true;
> >> +}
> >> +
> >> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable)
> >> +{
> >> +    hw->gamma[id].enable = enable;
> >> +    hw->gamma[id].dirty = true;
> >> +}
> >> +
> >> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display)
> >> +{
> >> +    u8 id = display->id;
> >> +
> >> +    memcpy(&hw->display[id], display, sizeof(*display));
> >> +
> >> +    hw->func->display(hw, display);
> >> +}
> >> +
> >> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable)
> >> +{
> >> +    if (enable)
> >> +            hi_write(hw, AQ_INTR_ENBL, 0xFFFFFFFF);
> >> +    else
> >> +            hi_write(hw, AQ_INTR_ENBL, 0);
> >> +}
> >> +
> >> +u32 dc_hw_get_interrupt(struct dc_hw *hw)
> >> +{
> >> +    return hi_read(hw, AQ_INTR_ACKNOWLEDGE);
> >> +}
> >> +
> >> +bool dc_hw_check_underflow(struct dc_hw *hw)
> >> +{
> >> +    return dc_read(hw, DC_FRAMEBUFFER_CONFIG) & BIT(5);
> >> +}
> >> +
> >> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable)
> >> +{
> >> +    u32 i, offset;
> >> +    u8 id, layer_num = hw->info->layer_num;
> >> +    u8 panel_num = hw->info->panel_num;
> >> +
> >> +    for (i = 0; i < layer_num; i++) {
> >> +            id = hw->info->planes[i].id;
> >> +            offset = get_addr_offset(id);
> >> +            if (enable) {
> >> +                    if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, BIT(12), 0);
> >> +                    else
> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, BIT(31), 0);
> >> +            } else {
> >> +                    if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, 0, BIT(12));
> >> +                    else
> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, 0, BIT(31));
> >> +            }
> >> +    }
> >> +
> >> +    for (i = 0; i < panel_num; i++) {
> >> +            offset = i << 2;
> >> +            if (enable)
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, BIT(0));
> >> +            else
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, BIT(0), 0);
> >> +    }
> >> +}
> >> +
> >> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id)
> >> +{
> >> +    if (out <= OUT_DP)
> >> +            hw->out[id] = out;
> >> +}
> >> +
> >> +static void gamma_ex_commit(struct dc_hw *hw)
> >> +{
> >> +    u8 panel_num = hw->info->panel_num;
> >> +    u16 i, j;
> >> +    u32 value;
> >> +
> >> +    for (j = 0; j < panel_num; j++) {
> >> +            if (hw->gamma[j].dirty) {
> >> +                    if (hw->gamma[j].enable) {
> >> +                            dc_write(hw, DC_DISPLAY_GAMMA_EX_INDEX + (j << 2), 0x00);
> >> +                            for (i = 0; i < GAMMA_EX_SIZE; i++) {
> >> +                                    value = hw->gamma[j].gamma[i][2] |
> >> +                                            (hw->gamma[j].gamma[i][1] << 12);
> >> +                                    dc_write(hw, DC_DISPLAY_GAMMA_EX_DATA + (j << 2), value);
> >> +                                    dc_write(hw, DC_DISPLAY_GAMMA_EX_ONE_DATA + (j << 2),
> >> +                                             hw->gamma[j].gamma[i][0]);
> >> +                            }
> >> +                            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
> >> +                                         BIT(13), 0);
> >> +                    } else {
> >> +                            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
> >> +                                         0, BIT(13));
> >> +                    }
> >> +                    hw->gamma[j].dirty = false;
> >> +            }
> >> +    }
> >> +}
> >> +
> >> +static void plane_commit(struct dc_hw *hw)
> >> +{
> >> +    struct dc_hw_plane *plane;
> >> +    const struct dc_hw_plane_reg *reg;
> >> +    bool primary = false;
> >> +    u8 id, layer_num = hw->info->layer_num;
> >> +    u32 i, offset;
> >> +
> >> +    for (i = 0; i < layer_num; i++) {
> >> +            plane = &hw->plane[i];
> >> +            id = hw->info->planes[i].id;
> >> +            offset = get_addr_offset(id);
> >> +            if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
> >> +                    reg = &dc_plane_reg[0];
> >> +                    primary = true;
> >> +            } else {
> >> +                    reg = &dc_plane_reg[1];
> >> +                    primary = false;
> >> +            }
> >
> > Can you please move register defines to some internal hw_plane
> > structure. These details are known at the time of creation of the plane.
> > There is no need to determine them again and again.
> >
> >> +
> >> +            if (plane->fb.dirty) {
> >> +                    if (plane->fb.enable) {
> >
> > if (plane->fb.dirty && plane->fb.enable) will save you one level of
> > indentation.
> >
> > However it might be better to use existing DRM flags instead of
> > inventing your own flags.
> >
> >> +                            dc_write(hw, reg->y_address + offset,
> >> +                                     plane->fb.y_address);
> >> +                            dc_write(hw, reg->u_address + offset,
> >> +                                     plane->fb.u_address);
> >> +                            dc_write(hw, reg->v_address + offset,
> >> +                                     plane->fb.v_address);
> >> +                            dc_write(hw, reg->y_stride + offset,
> >> +                                     plane->fb.y_stride);
> >> +                            dc_write(hw, reg->u_stride + offset,
> >> +                                     plane->fb.u_stride);
> >> +                            dc_write(hw, reg->v_stride + offset,
> >> +                                     plane->fb.v_stride);
> >> +                            dc_write(hw, reg->size + offset,
> >> +                                     plane->fb.width |
> >> +                                     (plane->fb.height << 15));
> >> +                            dc_write(hw, reg->water_mark + offset,
> >> +                                     plane->fb.water_mark);
> >> +
> >> +                            if (plane->fb.clear_enable)
> >> +                                    dc_write(hw, reg->clear_value + offset,
> >> +                                             plane->fb.clear_value);
> >> +                    }
> >> +
> >> +                    if (primary) {
> >
> > You have two kinds of planes. Primary and overlays. Could you please
> > split this code into two separate functions and define two vtables, one
> > for primary planes and one for overlays. There is no need to stick
> > everything into a single funcion that handles everything.
> >
> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
> >> +                                         (plane->fb.format << 26) |
> >> +                                         (plane->fb.uv_swizzle << 25) |
> >> +                                         (plane->fb.swizzle << 23) |
> >> +                                         (plane->fb.tile_mode << 17) |
> >> +                                         (plane->fb.yuv_color_space << 14) |
> >> +                                         (plane->fb.rotation << 11) |
> >> +                                         (plane->fb.clear_enable << 8),
> >> +                                         (0x1F << 26) |
> >> +                                         BIT(25) |
> >> +                                         (0x03 << 23) |
> >> +                                         (0x1F << 17) |
> >> +                                         (0x07 << 14) |
> >> +                                         (0x07 << 11) |
> >> +                                         BIT(8));
> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                         (plane->fb.dec_enable << 1) |
> >> +                                         (plane->fb.enable << 13) |
> >> +                                         (plane->fb.zpos << 16) |
> >> +                                         (plane->fb.display_id << 19),
> >> +                                         BIT(1) | BIT(13) | (0x07 << 16) | BIT(19));
> >
> > It looks like this code can enjoy defining names for all the magic
> > values. Otherwise it is impossible to support it.
> >
> >> +                    } else {
> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> >> +                                         (plane->fb.dec_enable << 27) |
> >> +                                         (plane->fb.clear_enable << 25) |
> >> +                                         (plane->fb.enable << 24) |
> >> +                                         (plane->fb.format << 16) |
> >> +                                         (plane->fb.uv_swizzle << 15) |
> >> +                                         (plane->fb.swizzle << 13) |
> >> +                                         (plane->fb.tile_mode << 8) |
> >> +                                         (plane->fb.yuv_color_space << 5) |
> >> +                                         (plane->fb.rotation << 2),
> >> +                                         BIT(27) |
> >> +                                         BIT(25) |
> >> +                                         BIT(24) |
> >> +                                         (0x1F << 16) |
> >> +                                         BIT(15) |
> >> +                                         (0x03 << 13) |
> >> +                                         (0x1F << 8) |
> >> +                                         (0x07 << 5) |
> >> +                                         (0x07 << 2));
> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset,
> >> +                                         plane->fb.zpos | (plane->fb.display_id << 3),
> >> +                                         0x07 | BIT(3));
> >> +                    }
> >> +                    plane->fb.dirty = false;
> >> +            }
> >> +
> >> +            if (plane->scale.dirty) {
> >> +                    if (plane->scale.enable) {
> >> +                            dc_write(hw, reg->scale_factor_x + offset,
> >> +                                     plane->scale.scale_factor_x);
> >> +                            dc_write(hw, reg->scale_factor_y + offset,
> >> +                                     plane->scale.scale_factor_y);
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_FRAMEBUFFER_CONFIG + offset,
> >> +                                                 BIT(22), 0);
> >> +                            else
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_OVERLAY_SCALE_CONFIG + offset,
> >> +                                                 BIT(8), 0);
> >> +                    } else {
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_FRAMEBUFFER_CONFIG + offset,
> >> +                                                 0, BIT(22));
> >> +                            else
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_OVERLAY_SCALE_CONFIG + offset,
> >> +                                                 0, BIT(8));
> >> +                    }
> >> +                    plane->scale.dirty = false;
> >> +            }
> >> +
> >> +            if (plane->pos.dirty) {
> >> +                    dc_write(hw, reg->top_left + offset,
> >> +                             plane->pos.start_x |
> >> +                             (plane->pos.start_y << 15));
> >> +                    dc_write(hw, reg->bottom_right + offset,
> >> +                             plane->pos.end_x |
> >> +                             (plane->pos.end_y << 15));
> >> +                    plane->pos.dirty = false;
> >> +            }
> >> +
> >> +            if (plane->blend.dirty) {
> >> +                    dc_write(hw, reg->src_global_color + offset,
> >> +                             plane->blend.alpha << 24);
> >> +                    dc_write(hw, reg->dst_global_color + offset,
> >> +                             plane->blend.alpha << 24);
> >> +                    switch (plane->blend.blend_mode) {
> >> +                    case BLEND_PREMULTI:
> >> +                            dc_write(hw, reg->blend_config + offset, 0x3450);
> >> +                            break;
> >> +                    case BLEND_COVERAGE:
> >> +                            dc_write(hw, reg->blend_config + offset, 0x3950);
> >> +                            break;
> >> +                    case BLEND_PIXEL_NONE:
> >> +                            dc_write(hw, reg->blend_config + offset, 0x3548);
> >> +                            break;
> >> +                    default:
> >> +                            break;
> >> +                    }
> >> +                    plane->blend.dirty = false;
> >> +            }
> >> +
> >> +            if (plane->colorkey.dirty) {
> >> +                    dc_write(hw, reg->color_key + offset, plane->colorkey.colorkey);
> >> +                    dc_write(hw, reg->color_key_high + offset,
> >> +                             plane->colorkey.colorkey_high);
> >> +
> >> +                    if (primary)
> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
> >> +                                         plane->colorkey.transparency << 9, 0x03 << 9);
> >> +                    else
> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> >> +                                         plane->colorkey.transparency, 0x03);
> >> +
> >> +                    plane->colorkey.dirty = false;
> >> +            }
> >> +
> >> +            if (plane->roi.dirty) {
> >> +                    if (plane->roi.enable) {
> >> +                            dc_write(hw, reg->roi_origin + offset,
> >> +                                     plane->roi.x | (plane->roi.y << 16));
> >> +                            dc_write(hw, reg->roi_size + offset,
> >> +                                     plane->roi.width | (plane->roi.height << 16));
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                                 BIT(0), 0);
> >> +                            else
> >> +                                    dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> >> +                                                 BIT(22), 0);
> >> +                    } else {
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                                 0, BIT(0));
> >> +                            else
> >> +                                    dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> >> +                                                 0, BIT(22));
> >> +                    }
> >> +                    plane->roi.dirty = false;
> >> +            }
> >> +    }
> >> +}
> >> +
> >> +static void plane_ex_commit(struct dc_hw *hw)
> >> +{
> >> +    struct dc_hw_plane *plane;
> >> +    const struct dc_hw_plane_reg *reg;
> >> +    bool primary = false;
> >> +    u8 id, layer_num = hw->info->layer_num;
> >> +    u32 i, offset;
> >> +
> >> +    for (i = 0; i < layer_num; i++) {
> >> +            plane = &hw->plane[i];
> >> +            id = hw->info->planes[i].id;
> >> +            offset = get_addr_offset(id);
> >> +            if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
> >> +                    reg = &dc_plane_reg[0];
> >> +                    primary = true;
> >> +            } else {
> >> +                    reg = &dc_plane_reg[1];
> >> +                    primary = false;
> >> +            }
> >> +
> >> +            if (plane->fb.dirty) {
> >> +                    if (is_rgb(plane->fb.format)) {
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                                 BIT(6), BIT(8));
> >> +                            else
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_OVERLAY_CONFIG + offset,
> >> +                                                 BIT(29), BIT(30));
> >> +                    } else {
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                                 BIT(8), BIT(6));
> >> +                            else
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_OVERLAY_CONFIG + offset,
> >> +                                                 BIT(30), BIT(29));
> >> +                            switch (plane->fb.yuv_color_space) {
> >> +                            case COLOR_SPACE_601:
> >> +                                    load_yuv_to_rgb(hw, reg, offset, YUV601_2RGB);
> >> +                                    break;
> >> +                            case COLOR_SPACE_709:
> >> +                                    load_yuv_to_rgb(hw, reg, offset, YUV709_2RGB);
> >> +                                    break;
> >> +                            case COLOR_SPACE_2020:
> >> +                                    load_yuv_to_rgb(hw, reg, offset, YUV2020_2RGB);
> >> +                                    break;
> >> +                            default:
> >> +                                    break;
> >> +                            }
> >> +                    }
> >> +            }
> >> +            if (plane->degamma.dirty) {
> >> +                    switch (plane->degamma.mode) {
> >> +                    case VS_DEGAMMA_DISABLE:
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                                 0, BIT(5));
> >> +                            else
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_OVERLAY_CONFIG + offset,
> >> +                                                 0, BIT(28));
> >> +                            break;
> >> +                    case VS_DEGAMMA_BT709:
> >> +                            load_degamma_table(hw, reg, offset, DEGAMMA_709);
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                                 BIT(5), 0);
> >> +                            else
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_OVERLAY_CONFIG + offset,
> >> +                                                 BIT(28), 0);
> >> +                            break;
> >> +                    case VS_DEGAMMA_BT2020:
> >> +                            load_degamma_table(hw, reg, offset, DEGAMMA_2020);
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                                 BIT(5), 0);
> >> +                            else
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_OVERLAY_CONFIG + offset,
> >> +                                                 BIT(28), 0);
> >> +                            break;
> >> +                    default:
> >> +                            break;
> >> +                    }
> >> +                    plane->degamma.dirty = false;
> >> +            }
> >> +    }
> >> +    plane_commit(hw);
> >> +}
> >> +
> >> +static void setup_display(struct dc_hw *hw, struct dc_hw_display *display)
> >> +{
> >> +    u8 id = display->id;
> >> +    u32 dpi_cfg, offset = id << 2;
> >> +
> >> +    if (hw->display[id].enable) {
> >> +            switch (display->bus_format) {
> >> +            case MEDIA_BUS_FMT_RGB565_1X16:
> >> +                    dpi_cfg = 0;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_RGB666_1X18:
> >> +                    dpi_cfg = 3;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> >> +                    dpi_cfg = 4;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_RGB888_1X24:
> >> +                    dpi_cfg = 5;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_RGB101010_1X30:
> >> +                    dpi_cfg = 6;
> >> +                    break;
> >> +            default:
> >> +                    dpi_cfg = 5;
> >> +                    break;
> >> +            }
> >> +            dc_write(hw, DC_DISPLAY_DPI_CONFIG + offset, dpi_cfg);
> >> +
> >> +            if (id == 0)
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
> >> +            else
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
> >> +
> >> +            dc_write(hw, DC_DISPLAY_H + offset, hw->display[id].h_active |
> >> +                     (hw->display[id].h_total << 16));
> >> +            dc_write(hw, DC_DISPLAY_H_SYNC + offset,
> >> +                     hw->display[id].h_sync_start |
> >> +                     (hw->display[id].h_sync_end << 15) |
> >> +                     (hw->display[id].h_sync_polarity ? 0 : BIT(31)) |
> >> +                     BIT(30));
> >> +            dc_write(hw, DC_DISPLAY_V + offset, hw->display[id].v_active |
> >> +                            (hw->display[id].v_total << 16));
> >> +            dc_write(hw, DC_DISPLAY_V_SYNC + offset,
> >> +                     hw->display[id].v_sync_start |
> >> +                     (hw->display[id].v_sync_end << 15) |
> >> +                     (hw->display[id].v_sync_polarity ? 0 : BIT(31)) |
> >> +                     BIT(30));
> >> +
> >> +            if (hw->info->pipe_sync)
> >> +                    dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
> >> +                                 0, BIT(3) | BIT(4));
> >> +
> >> +            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(12), 0);
> >> +            if (id == 0)
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(0), BIT(3));
> >> +            else
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(1), BIT(3));
> >> +    } else {
> >> +            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(12));
> >> +            if (id == 0)
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
> >> +            else
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
> >> +    }
> >> +}
> >> +
> >> +static void setup_display_ex(struct dc_hw *hw, struct dc_hw_display *display)
> >> +{
> >> +    u8 id = display->id;
> >> +    u32 dp_cfg, offset = id << 2;
> >> +    bool is_yuv = false;
> >> +
> >> +    if (hw->display[id].enable && hw->out[id] == OUT_DP) {
> >> +            switch (display->bus_format) {
> >> +            case MEDIA_BUS_FMT_RGB565_1X16:
> >> +                    dp_cfg = 0;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_RGB666_1X18:
> >> +                    dp_cfg = 1;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_RGB888_1X24:
> >> +                    dp_cfg = 2;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_RGB101010_1X30:
> >> +                    dp_cfg = 3;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_UYVY8_1X16:
> >> +                    dp_cfg = 2 << 4;
> >> +                    is_yuv = true;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_YUV8_1X24:
> >> +                    dp_cfg = 4 << 4;
> >> +                    is_yuv = true;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_UYVY10_1X20:
> >> +                    dp_cfg = 8 << 4;
> >> +                    is_yuv = true;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_YUV10_1X30:
> >> +                    dp_cfg = 10 << 4;
> >> +                    is_yuv = true;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> >> +                    dp_cfg = 12 << 4;
> >> +                    is_yuv = true;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> >> +                    dp_cfg = 13 << 4;
> >> +                    is_yuv = true;
> >> +                    break;
> >> +            default:
> >> +                    dp_cfg = 2;
> >> +                    break;
> >> +            }
> >> +            if (is_yuv)
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(16), 0);
> >> +            else
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(16));
> >> +            dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | BIT(3));
> >> +    }
> >> +
> >> +    if (hw->out[id] == OUT_DPI)
> >> +            dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, BIT(3));
> >> +
> >> +    setup_display(hw, display);
> >> +}
> >> +
> >> +static const struct dc_hw_funcs hw_func = {
> >> +    .gamma = &gamma_ex_commit,
> >> +    .plane = &plane_ex_commit,
> >> +    .display = setup_display_ex,
> >> +};
> >> +
> >> +void dc_hw_commit(struct dc_hw *hw)
> >> +{
> >> +    u32 i, offset = 0;
> >> +    u8 plane_num = hw->info->plane_num;
> >> +    u8 layer_num = hw->info->layer_num;
> >> +    u8 cursor_num = plane_num - layer_num;
> >> +
> >> +    hw->func->gamma(hw);
> >> +    hw->func->plane(hw);
> >> +
> >> +    for (i = 0; i < cursor_num; i++) {
> >> +            if (hw->cursor[i].dirty) {
> >> +                    offset = hw->cursor[i].display_id ? DC_CURSOR_OFFSET : 0;
> >> +                    if (hw->cursor[i].enable) {
> >> +                            dc_write(hw, DC_CURSOR_ADDRESS + offset,
> >> +                                     hw->cursor[i].address);
> >> +                            dc_write(hw, DC_CURSOR_LOCATION + offset, hw->cursor[i].x |
> >> +                                     (hw->cursor[i].y << 16));
> >> +                            dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
> >> +                                         (hw->cursor[i].hot_x << 16) |
> >> +                                         (hw->cursor[i].hot_y << 8) |
> >> +                                         (hw->cursor[i].size << 5) |
> >> +                                         BIT(3) | BIT(2) | 0x02,
> >> +                                         (0xFF << 16) |
> >> +                                         (0xFF << 8) |
> >> +                                         (0x07 << 5) | 0x1F);
> >> +                    } else {
> >> +                            dc_set_clear(hw, DC_CURSOR_CONFIG + offset, BIT(3), 0x03);
> >> +                    }
> >> +                    hw->cursor[i].dirty = false;
> >> +            }
> >> +    }
> >> +
> >> +    if (hw->qos.dirty) {
> >> +            dc_set_clear(hw, DC_QOS_CONFIG, (hw->qos.high_value << 4) |
> >> +                                     hw->qos.low_value, 0xFF);
> >> +            hw->qos.dirty = false;
> >> +    }
> >> +}
> >> +
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.h b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
> >> new file mode 100644
> >> index 000000000..9a1d767ae
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
> >> @@ -0,0 +1,492 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#ifndef __VS_DC_HW_H__
> >> +#define __VS_DC_HW_H__
> >> +
> >> +#define AQ_INTR_ACKNOWLEDGE                         0x0010
> >> +#define AQ_INTR_ENBL                                        0x0014
> >
> > Please take care to align all the defines.
> >
> >> +#define DC_HW_REVISION                                      0x0024
> >> +#define DC_HW_CHIP_CID                                      0x0030
> >> +
> >> +#define DC_REG_BASE                                         0x0800
> >> +#define DC_REG_RANGE                                        0x2000
> >> +#define DC_SEC_REG_OFFSET                           0x100000
> >> +
> >> +#define DC_FRAMEBUFFER_CONFIG                       0x1518
> >> +#define DC_FRAMEBUFFER_CONFIG_EX            0x1CC0
> >> +#define DC_FRAMEBUFFER_SCALE_CONFIG         0x1520
> >> +#define DC_FRAMEBUFFER_TOP_LEFT                     0x24D8
> >> +#define DC_FRAMEBUFFER_BOTTOM_RIGHT         0x24E0
> >> +#define DC_FRAMEBUFFER_ADDRESS                      0x1400
> >> +#define DC_FRAMEBUFFER_U_ADDRESS            0x1530
> >> +#define DC_FRAMEBUFFER_V_ADDRESS            0x1538
> >> +#define DC_FRAMEBUFFER_STRIDE                       0x1408
> >> +#define DC_FRAMEBUFFER_U_STRIDE                     0x1800
> >> +#define DC_FRAMEBUFFER_V_STRIDE                     0x1808
> >> +#define DC_FRAMEBUFFER_SIZE                         0x1810
> >> +#define DC_FRAMEBUFFER_SCALE_FACTOR_X               0x1828
> >> +#define DC_FRAMEBUFFER_SCALE_FACTOR_Y               0x1830
> >> +#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX  0x1838
> >> +#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA   0x1A00
> >> +#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX  0x1A08
> >> +#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA   0x1A10
> >> +#define DC_FRAMEBUFFER_INIT_OFFSET                  0x1A20
> >> +#define DC_FRAMEBUFFER_COLOR_KEY                    0x1508
> >> +#define DC_FRAMEBUFFER_COLOR_KEY_HIGH               0x1510
> >> +#define DC_FRAMEBUFFER_CLEAR_VALUE                  0x1A18
> >> +#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX    0x1818
> >> +#define DC_FRAMEBUFFER_COLOR_TABLE_DATA             0x1820
> >> +#define DC_FRAMEBUFFER_BG_COLOR                             0x1528
> >> +#define DC_FRAMEBUFFER_ROI_ORIGIN                   0x1CB0
> >> +#define DC_FRAMEBUFFER_ROI_SIZE                             0x1CB8
> >> +#define DC_FRAMEBUFFER_WATER_MARK                   0x1CE8
> >> +#define DC_FRAMEBUFFER_DEGAMMA_INDEX                0x1D88
> >> +#define DC_FRAMEBUFFER_DEGAMMA_DATA                 0x1D90
> >> +#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA              0x1D98
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF0               0x1DA0
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF1               0x1DA8
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF2               0x1DB0
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF3               0x1DB8
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF4               0x1E00
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD0              0x1E08
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD1              0x1E10
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD2              0x1E18
> >> +#define DC_FRAMEBUFFER_Y_CLAMP_BOUND                0x1E88
> >> +#define DC_FRAMEBUFFER_UV_CLAMP_BOUND               0x1E90
> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF0               0x1E20
> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF1               0x1E28
> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF2               0x1E30
> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF3               0x1E38
> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF4               0x1E40
> >> +#define DC_FRAMEBUFFER_BLEND_CONFIG                 0x2510
> >> +#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR             0x2500
> >> +#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR             0x2508
> >> +
> >> +#define DC_OVERLAY_CONFIG                                   0x1540
> >> +#define DC_OVERLAY_CONFIG_EX                                0x2540
> >> +#define DC_OVERLAY_SCALE_CONFIG                             0x1C00
> >> +#define DC_OVERLAY_BLEND_CONFIG                             0x1580
> >> +#define DC_OVERLAY_TOP_LEFT                                 0x1640
> >> +#define DC_OVERLAY_BOTTOM_RIGHT                             0x1680
> >> +#define DC_OVERLAY_ADDRESS                                  0x15C0
> >> +#define DC_OVERLAY_U_ADDRESS                                0x1840
> >> +#define DC_OVERLAY_V_ADDRESS                                0x1880
> >> +#define DC_OVERLAY_STRIDE                                   0x1600
> >> +#define DC_OVERLAY_U_STRIDE                                 0x18C0
> >> +#define DC_OVERLAY_V_STRIDE                                 0x1900
> >> +#define DC_OVERLAY_SIZE                                             0x17C0
> >> +#define DC_OVERLAY_SCALE_FACTOR_X                   0x1A40
> >> +#define DC_OVERLAY_SCALE_FACTOR_Y                   0x1A80
> >> +#define DC_OVERLAY_H_FILTER_COEF_INDEX              0x1AC0
> >> +#define DC_OVERLAY_H_FILTER_COEF_DATA               0x1B00
> >> +#define DC_OVERLAY_V_FILTER_COEF_INDEX              0x1B40
> >> +#define DC_OVERLAY_V_FILTER_COEF_DATA               0x1B80
> >> +#define DC_OVERLAY_INIT_OFFSET                              0x1BC0
> >> +#define DC_OVERLAY_COLOR_KEY                                0x1740
> >> +#define DC_OVERLAY_COLOR_KEY_HIGH                   0x1780
> >> +#define DC_OVERLAY_CLEAR_VALUE                              0x1940
> >> +#define DC_OVERLAY_COLOR_TABLE_INDEX                0x1980
> >> +#define DC_OVERLAY_COLOR_TABLE_DATA                 0x19C0
> >> +#define DC_OVERLAY_SRC_GLOBAL_COLOR                 0x16C0
> >> +#define DC_OVERLAY_DST_GLOBAL_COLOR                 0x1700
> >> +#define DC_OVERLAY_ROI_ORIGIN                               0x1D00
> >> +#define DC_OVERLAY_ROI_SIZE                                 0x1D40
> >> +#define DC_OVERLAY_WATER_MARK                               0x1DC0
> >> +#define DC_OVERLAY_DEGAMMA_INDEX                    0x2200
> >> +#define DC_OVERLAY_DEGAMMA_DATA                             0x2240
> >> +#define DC_OVERLAY_DEGAMMA_EX_DATA                  0x2280
> >> +#define DC_OVERLAY_YUVTORGB_COEF0                   0x1EC0
> >> +#define DC_OVERLAY_YUVTORGB_COEF1                   0x1F00
> >> +#define DC_OVERLAY_YUVTORGB_COEF2                   0x1F40
> >> +#define DC_OVERLAY_YUVTORGB_COEF3                   0x1F80
> >> +#define DC_OVERLAY_YUVTORGB_COEF4                   0x1FC0
> >> +#define DC_OVERLAY_YUVTORGB_COEFD0                  0x2000
> >> +#define DC_OVERLAY_YUVTORGB_COEFD1                  0x2040
> >> +#define DC_OVERLAY_YUVTORGB_COEFD2                  0x2080
> >> +#define DC_OVERLAY_Y_CLAMP_BOUND                    0x22C0
> >> +#define DC_OVERLAY_UV_CLAMP_BOUND                   0x2300
> >> +#define DC_OVERLAY_RGBTORGB_COEF0                   0x20C0
> >> +#define DC_OVERLAY_RGBTORGB_COEF1                   0x2100
> >> +#define DC_OVERLAY_RGBTORGB_COEF2                   0x2140
> >> +#define DC_OVERLAY_RGBTORGB_COEF3                   0x2180
> >> +#define DC_OVERLAY_RGBTORGB_COEF4                   0x21C0
> >> +
> >> +#define DC_CURSOR_CONFIG                                    0x1468
> >> +#define DC_CURSOR_ADDRESS                                   0x146C
> >> +#define DC_CURSOR_LOCATION                                  0x1470
> >> +#define DC_CURSOR_BACKGROUND                                0x1474
> >> +#define DC_CURSOR_FOREGROUND                                0x1478
> >> +#define DC_CURSOR_CLK_GATING                                0x1484
> >> +#define DC_CURSOR_CONFIG_EX                                 0x24E8
> >> +#define DC_CURSOR_OFFSET                                    0x1080
> >> +
> >> +#define DC_DISPLAY_DITHER_CONFIG            0x1410
> >> +#define DC_DISPLAY_PANEL_CONFIG                     0x1418
> >> +#define DC_DISPLAY_PANEL_CONFIG_EX          0x2518
> >> +#define DC_DISPLAY_DITHER_TABLE_LOW         0x1420
> >> +#define DC_DISPLAY_DITHER_TABLE_HIGH        0x1428
> >> +#define DC_DISPLAY_H                                        0x1430
> >> +#define DC_DISPLAY_H_SYNC                           0x1438
> >> +#define DC_DISPLAY_V                                        0x1440
> >> +#define DC_DISPLAY_V_SYNC                           0x1448
> >> +#define DC_DISPLAY_CURRENT_LOCATION         0x1450
> >> +#define DC_DISPLAY_GAMMA_INDEX                      0x1458
> >> +#define DC_DISPLAY_GAMMA_DATA                       0x1460
> >> +#define DC_DISPLAY_INT                                      0x147C
> >> +#define DC_DISPLAY_INT_ENABLE                       0x1480
> >> +#define DC_DISPLAY_DBI_CONFIG                       0x1488
> >> +#define DC_DISPLAY_GENERAL_CONFIG           0x14B0
> >> +#define DC_DISPLAY_DPI_CONFIG                       0x14B8
> >> +#define DC_DISPLAY_PANEL_START                      0x1CCC
> >> +#define DC_DISPLAY_DEBUG_COUNTER_SELECT             0x14D0
> >> +#define DC_DISPLAY_DEBUG_COUNTER_VALUE              0x14D8
> >> +#define DC_DISPLAY_DP_CONFIG                                0x1CD0
> >> +#define DC_DISPLAY_GAMMA_EX_INDEX                   0x1CF0
> >> +#define DC_DISPLAY_GAMMA_EX_DATA                    0x1CF8
> >> +#define DC_DISPLAY_GAMMA_EX_ONE_DATA                0x1D80
> >> +#define DC_DISPLAY_RGBTOYUV_COEF0                   0x1E48
> >> +#define DC_DISPLAY_RGBTOYUV_COEF1                   0x1E50
> >> +#define DC_DISPLAY_RGBTOYUV_COEF2                   0x1E58
> >> +#define DC_DISPLAY_RGBTOYUV_COEF3                   0x1E60
> >> +#define DC_DISPLAY_RGBTOYUV_COEF4                   0x1E68
> >> +#define DC_DISPLAY_RGBTOYUV_COEFD0                  0x1E70
> >> +#define DC_DISPLAY_RGBTOYUV_COEFD1                  0x1E78
> >> +#define DC_DISPLAY_RGBTOYUV_COEFD2                  0x1E80
> >> +
> >> +#define DC_CLK_GATTING                                      0x1A28
> >> +#define DC_QOS_CONFIG                                       0x1A38
> >> +
> >> +#define DC_TRANSPARENCY_OPAQUE                      0x00
> >> +#define DC_TRANSPARENCY_KEY                         0x02
> >> +#define DC_DISPLAY_DITHERTABLE_LOW          0x7B48F3C0
> >> +#define DC_DISPLAY_DITHERTABLE_HIGH         0x596AD1E2
> >> +
> >> +#define GAMMA_SIZE                                          256
> >> +#define GAMMA_EX_SIZE                                       300
> >> +#define DEGAMMA_SIZE                                        260
> >> +
> >> +#define RGB_TO_RGB_TABLE_SIZE                       9
> >> +#define YUV_TO_RGB_TABLE_SIZE                       16
> >> +#define RGB_TO_YUV_TABLE_SIZE                       12
> >> +
> >> +#define DC_LAYER_NUM        6
> >> +#define DC_DISPLAY_NUM      2
> >> +#define DC_CURSOR_NUM       2
> >> +
> >> +#define DC_TILE_MODE4X4     0x15
> >> +
> >> +enum dc_chip_rev {
> >> +    DC_REV_0,/* For HW_REV_5720,HW_REV_5721_311 */
> >> +    DC_REV_1,/* For HW_REV_5721_30B */
> >> +    DC_REV_2,/* For HW_REV_5721_310 */
> >> +};
> >> +
> >> +enum dc_hw_plane_id {
> >> +    PRIMARY_PLANE_0,
> >> +    OVERLAY_PLANE_0,
> >> +    OVERLAY_PLANE_1,
> >> +    PRIMARY_PLANE_1,
> >> +    OVERLAY_PLANE_2,
> >> +    OVERLAY_PLANE_3,
> >> +    CURSOR_PLANE_0,
> >> +    CURSOR_PLANE_1,
> >> +    PLANE_NUM
> >> +};
> >> +
> >> +enum dc_hw_color_format {
> >> +    FORMAT_X4R4G4B4,//0
> >
> > Space after the comma will make it more readable. Or just define the
> > values explicitly, it will also be more readable.
> >
> >> +    FORMAT_A4R4G4B4,//1
> >> +    FORMAT_X1R5G5B5,//2
> >> +    FORMAT_A1R5G5B5,//3
> >> +    FORMAT_R5G6B5,//4
> >> +    FORMAT_X8R8G8B8,//5
> >> +    FORMAT_A8R8G8B8,//6
> >> +    FORMAT_YUY2,//7
> >> +    FORMAT_UYVY,//8
> >> +    FORMAT_INDEX8,//9
> >> +    FORMAT_MONOCHROME,//10
> >> +    FORMAT_YV12 = 0xf,
> >> +    FORMAT_A8,//16
> >> +    FORMAT_NV12,//17
> >> +    FORMAT_NV16,//18
> >> +    FORMAT_RG16,//19
> >> +    FORMAT_R8,//20
> >> +    FORMAT_NV12_10BIT,//21
> >> +    FORMAT_A2R10G10B10,//22
> >> +    FORMAT_NV16_10BIT,//23
> >> +    FORMAT_INDEX1,//24
> >> +    FORMAT_INDEX2,//25
> >> +    FORMAT_INDEX4,//26
> >> +    FORMAT_P010,//27
> >> +    FORMAT_YUV444,//28
> >> +    FORMAT_YUV444_10BIT,//29
> >> +};
> >> +
> >> +enum dc_hw_yuv_color_space {
> >> +    COLOR_SPACE_601 = 0,
> >> +    COLOR_SPACE_709 = 1,
> >> +    COLOR_SPACE_2020 = 3,
> >> +};
> >> +
> >> +enum dc_hw_rotation {
> >> +    ROT_0 = 0,
> >> +    ROT_90 = 4,
> >> +    ROT_180 = 5,
> >> +    ROT_270 = 6,
> >> +    FLIP_X = 1,
> >> +    FLIP_Y = 2,
> >> +    FLIP_XY = 3,
> >> +};
> >> +
> >> +enum dc_hw_swizzle {
> >> +    SWIZZLE_ARGB = 0,
> >> +    SWIZZLE_RGBA,
> >> +    SWIZZLE_ABGR,
> >> +    SWIZZLE_BGRA,
> >> +};
> >> +
> >> +enum dc_hw_out {
> >> +    OUT_DPI,
> >> +    OUT_DP,
> >> +};
> >> +
> >> +enum dc_hw_cursor_size {
> >> +    CURSOR_SIZE_32X32 = 0,
> >> +    CURSOR_SIZE_64X64,
> >> +};
> >> +
> >> +enum dc_hw_blend_mode {
> >> +    /* out.rgb = plane_alpha * fg.rgb +
> >> +     *              (1 - (plane_alpha * fg.alpha)) * bg.rgb
> >> +     */
> >> +    BLEND_PREMULTI,
> >> +    /* out.rgb = plane_alpha * fg.alpha * fg.rgb +
> >> +     *              (1 - (plane_alpha * fg.alpha)) * bg.rgb
> >> +     */
> >> +    BLEND_COVERAGE,
> >> +    /* out.rgb = plane_alpha * fg.rgb +
> >> +     *              (1 - plane_alpha) * bg.rgb
> >> +     */
> >> +    BLEND_PIXEL_NONE,
> >
> > Can you use defines from <drm/drm_blend.h>?
> >
> >> +};
> >> +
> >> +struct dc_hw_plane_reg {
> >> +    u32 y_address;
> >> +    u32 u_address;
> >> +    u32 v_address;
> >> +    u32 y_stride;
> >> +    u32 u_stride;
> >> +    u32 v_stride;
> >> +    u32 size;
> >> +    u32 top_left;
> >> +    u32 bottom_right;
> >> +    u32 scale_factor_x;
> >> +    u32 scale_factor_y;
> >> +    u32 h_filter_coef_index;
> >> +    u32 h_filter_coef_data;
> >> +    u32 v_filter_coef_index;
> >> +    u32 v_filter_coef_data;
> >> +    u32 init_offset;
> >> +    u32 color_key;
> >> +    u32 color_key_high;
> >> +    u32 clear_value;
> >> +    u32 color_table_index;
> >> +    u32 color_table_data;
> >> +    u32 scale_config;
> >> +    u32 water_mark;
> >> +    u32 degamma_index;
> >> +    u32 degamma_data;
> >> +    u32 degamma_ex_data;
> >> +    u32 src_global_color;
> >> +    u32 dst_global_color;
> >> +    u32 blend_config;
> >> +    u32 roi_origin;
> >> +    u32 roi_size;
> >> +    u32 yuv_to_rgb_coef0;
> >> +    u32 yuv_to_rgb_coef1;
> >> +    u32 yuv_to_rgb_coef2;
> >> +    u32 yuv_to_rgb_coef3;
> >> +    u32 yuv_to_rgb_coef4;
> >> +    u32 yuv_to_rgb_coefd0;
> >> +    u32 yuv_to_rgb_coefd1;
> >> +    u32 yuv_to_rgb_coefd2;
> >> +    u32 y_clamp_bound;
> >> +    u32 uv_clamp_bound;
> >> +    u32 rgb_to_rgb_coef0;
> >> +    u32 rgb_to_rgb_coef1;
> >> +    u32 rgb_to_rgb_coef2;
> >> +    u32 rgb_to_rgb_coef3;
> >> +    u32 rgb_to_rgb_coef4;
> >> +};
> >> +
> >> +struct dc_hw_fb {
> >> +    u32 y_address;
> >> +    u32 u_address;
> >> +    u32 v_address;
> >> +    u32 clear_value;
> >> +    u32 water_mark;
> >> +    u16 y_stride;
> >> +    u16 u_stride;
> >> +    u16 v_stride;
> >> +    u16 width;
> >> +    u16 height;
> >> +    u8      format;
> >> +    u8      tile_mode;
> >> +    u8      rotation;
> >> +    u8      yuv_color_space;
> >> +    u8      swizzle;
> >> +    u8      uv_swizzle;
> >> +    u8      zpos;
> >> +    u8      display_id;
> >> +    bool    clear_enable;
> >> +    bool    dec_enable;
> >> +    bool    enable;
> >> +    bool    dirty;
> >> +};
> >> +
> >> +struct dc_hw_scale {
> >> +    u32 scale_factor_x;
> >> +    u32 scale_factor_y;
> >> +    bool    enable;
> >> +    bool    dirty;
> >> +};
> >> +
> >> +struct dc_hw_position {
> >> +    u16 start_x;
> >> +    u16 start_y;
> >> +    u16 end_x;
> >> +    u16 end_y;
> >> +    bool    dirty;
> >> +};
> >> +
> >> +struct dc_hw_blend {
> >> +    u8      alpha;
> >> +    u8      blend_mode;
> >> +    bool    dirty;
> >> +};
> >> +
> >> +struct dc_hw_colorkey {
> >> +    u32 colorkey;
> >> +    u32 colorkey_high;
> >> +    u8      transparency;
> >> +    bool dirty;
> >> +};
> >> +
> >> +struct dc_hw_roi {
> >> +    u16 x;
> >> +    u16 y;
> >> +    u16 width;
> >> +    u16 height;
> >> +    bool enable;
> >> +    bool dirty;
> >> +};
> >> +
> >> +struct dc_hw_cursor {
> >> +    u32 address;
> >> +    u16 x;
> >> +    u16 y;
> >> +    u16 hot_x;
> >> +    u16 hot_y;
> >> +    u8      size;
> >> +    u8      display_id;
> >> +    bool    enable;
> >> +    bool    dirty;
> >> +};
> >> +
> >> +struct dc_hw_display {
> >> +    u32 bus_format;
> >> +    u16 h_active;
> >> +    u16 h_total;
> >> +    u16 h_sync_start;
> >> +    u16 h_sync_end;
> >> +    u16 v_active;
> >> +    u16 v_total;
> >> +    u16 v_sync_start;
> >> +    u16 v_sync_end;
> >> +    u8      id;
> >> +    bool    h_sync_polarity;
> >> +    bool    v_sync_polarity;
> >> +    bool    enable;
> >> +};
> >> +
> >> +struct dc_hw_gamma {
> >> +    u16 gamma[GAMMA_EX_SIZE][3];
> >> +    bool    enable;
> >> +    bool    dirty;
> >> +};
> >> +
> >> +struct dc_hw_degamma {
> >> +    u16 degamma[DEGAMMA_SIZE][3];
> >> +    u32 mode;
> >> +    bool    dirty;
> >> +};
> >> +
> >> +struct dc_hw_plane {
> >> +    struct dc_hw_fb                 fb;
> >> +    struct dc_hw_position   pos;
> >> +    struct dc_hw_scale              scale;
> >> +    struct dc_hw_blend              blend;
> >> +    struct dc_hw_roi                roi;
> >> +    struct dc_hw_colorkey   colorkey;
> >> +    struct dc_hw_degamma    degamma;
> >> +};
> >> +
> >> +struct dc_hw_qos {
> >> +    u8        low_value;
> >> +    u8        high_value;
> >> +    bool  dirty;
> >> +};
> >> +
> >> +struct dc_hw_read {
> >> +    u32                     reg;
> >> +    u32                     value;
> >> +};
> >> +
> >> +struct dc_hw;
> >> +struct dc_hw_funcs {
> >> +    void (*gamma)(struct dc_hw *hw);
> >> +    void (*plane)(struct dc_hw *hw);
> >> +    void (*display)(struct dc_hw *hw, struct dc_hw_display *display);
> >> +};
> >> +
> >> +struct dc_hw {
> >> +    enum dc_chip_rev rev;
> >> +    enum dc_hw_out          out[DC_DISPLAY_NUM];
> >> +    void                    *hi_base;
> >> +    void                    *reg_base;
> >> +
> >> +    struct dc_hw_display display[DC_DISPLAY_NUM];
> >> +    struct dc_hw_gamma       gamma[DC_DISPLAY_NUM];
> >> +    struct dc_hw_plane       plane[DC_LAYER_NUM];
> >> +    struct dc_hw_cursor  cursor[DC_CURSOR_NUM];
> >> +    struct dc_hw_qos         qos;
> >> +    struct dc_hw_funcs       *func;
> >> +    struct vs_dc_info        *info;
> >> +};
> >> +
> >> +int dc_hw_init(struct dc_hw *hw);
> >> +void dc_hw_deinit(struct dc_hw *hw);
> >> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
> >> +                    struct dc_hw_fb *fb, struct dc_hw_scale *scale,
> >> +                    struct dc_hw_position *pos, struct dc_hw_blend *blend);
> >> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode);
> >> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi);
> >> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
> >> +                       struct dc_hw_colorkey *colorkey);
> >> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos);
> >> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor);
> >> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
> >> +                    u16 r, u16 g, u16 b);
> >> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable);
> >> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display);
> >> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable);
> >> +u32 dc_hw_get_interrupt(struct dc_hw *hw);
> >> +bool dc_hw_check_underflow(struct dc_hw *hw);
> >> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable);
> >> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id);
> >> +void dc_hw_commit(struct dc_hw *hw);
> >> +
> >> +#endif /* __VS_DC_HW_H__ */
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
> >> index da7698c3d..3cd533cfa 100644
> >> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
> >> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> >> @@ -21,6 +21,7 @@
> >>
> >>   #include "vs_drv.h"
> >>   #include "vs_modeset.h"
> >> +#include "vs_dc.h"
> >>
> >>   #define DRV_NAME   "verisilicon"
> >>   #define DRV_DESC   "Verisilicon DRM driver"
> >> @@ -123,6 +124,7 @@ static const struct component_master_ops vs_drm_ops = {
> >>   };
> >>
> >>   static struct platform_driver *drm_sub_drivers[] = {
> >> +    &dc_platform_driver,
> >>
> >>   };
> >>
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c
> >> new file mode 100644
> >> index 000000000..9bd066015
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_plane.c
> >> @@ -0,0 +1,526 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#include <drm/drm_atomic.h>
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_blend.h>
> >> +#include <drm/drm_gem_dma_helper.h>
> >> +#include <drm/drm_fb_dma_helper.h>
> >> +#include <drm/drm_framebuffer.h>
> >> +#include <drm/drm_plane.h>
> >> +#include <drm/drm_plane_helper.h>
> >> +
> >> +#include <drm/vs_drm.h>
> >> +
> >> +#include "vs_plane.h"
> >> +#include "vs_drv.h"
> >> +#include "vs_dc.h"
> >> +
> >> +static void vs_plane_reset(struct drm_plane *plane)
> >> +{
> >> +    struct vs_plane_state *state;
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +
> >> +    if (plane->state) {
> >> +            __drm_atomic_helper_plane_destroy_state(plane->state);
> >> +
> >> +            state = to_vs_plane_state(plane->state);
> >> +            kfree(state);
> >> +            plane->state = NULL;
> >> +    }
> >> +
> >
> > Use vs_plane_destroy_state directly. This way you won't miss a refcount
> > on blobs.
> >
> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
> >> +    if (!state)
> >> +            return;
> >> +
> >> +    state->degamma = VS_DEGAMMA_DISABLE;
> >> +    state->degamma_changed = false;
> >> +    state->base.zpos = vs_plane->id;
> >> +    memset(&state->status, 0, sizeof(state->status));
> >> +
> >> +    __drm_atomic_helper_plane_reset(plane, &state->base);
> >> +}
> >> +
> >> +static void _vs_plane_duplicate_blob(struct vs_plane_state *state,
> >> +                                 struct vs_plane_state *ori_state)
> >> +{
> >> +    state->watermark = ori_state->watermark;
> >> +    state->color_mgmt = ori_state->color_mgmt;
> >> +    state->roi = ori_state->roi;
> >> +
> >> +    if (state->watermark)
> >> +            drm_property_blob_get(state->watermark);
> >> +    if (state->color_mgmt)
> >> +            drm_property_blob_get(state->color_mgmt);
> >> +    if (state->roi)
> >> +            drm_property_blob_get(state->roi);
> >> +}
> >> +
> >> +static int
> >> +_vs_plane_set_property_blob_from_id(struct drm_device *dev,
> >> +                                struct drm_property_blob **blob,
> >> +                                u64 blob_id,
> >> +                                size_t expected_size)
> >> +{
> >> +    struct drm_property_blob *new_blob = NULL;
> >> +
> >> +    if (blob_id) {
> >> +            new_blob = drm_property_lookup_blob(dev, blob_id);
> >> +            if (!new_blob)
> >> +                    return -EINVAL;
> >> +
> >> +            if (new_blob->length != expected_size) {
> >> +                    drm_property_blob_put(new_blob);
> >> +                    return -EINVAL;
> >> +            }
> >> +    }
> >> +
> >> +    drm_property_replace_blob(blob, new_blob);
> >> +    drm_property_blob_put(new_blob);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static struct drm_plane_state *
> >> +vs_plane_atomic_duplicate_state(struct drm_plane *plane)
> >> +{
> >> +    struct vs_plane_state *ori_state;
> >> +    struct vs_plane_state *state;
> >> +
> >> +    if (WARN_ON(!plane->state))
> >> +            return NULL;
> >> +
> >> +    ori_state = to_vs_plane_state(plane->state);
> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
> >> +    if (!state)
> >> +            return NULL;
> >> +
> >> +    __drm_atomic_helper_plane_duplicate_state(plane, &state->base);
> >> +
> >> +    state->degamma = ori_state->degamma;
> >> +    state->degamma_changed = ori_state->degamma_changed;
> >> +
> >> +    _vs_plane_duplicate_blob(state, ori_state);
> >> +    memcpy(&state->status, &ori_state->status, sizeof(ori_state->status));
> >> +
> >> +    return &state->base;
> >> +}
> >> +
> >> +static void vs_plane_atomic_destroy_state(struct drm_plane *plane,
> >> +                                      struct drm_plane_state *state)
> >> +{
> >> +    struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
> >> +
> >> +    __drm_atomic_helper_plane_destroy_state(state);
> >> +
> >> +    drm_property_blob_put(vs_plane_state->watermark);
> >> +    drm_property_blob_put(vs_plane_state->color_mgmt);
> >> +    drm_property_blob_put(vs_plane_state->roi);
> >> +    kfree(vs_plane_state);
> >> +}
> >> +
> >> +static int vs_plane_atomic_set_property(struct drm_plane *plane,
> >> +                                    struct drm_plane_state *state,
> >> +                                    struct drm_property *property,
> >> +                                    uint64_t val)
> >> +{
> >> +    struct drm_device *dev = plane->dev;
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +    struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
> >> +    int ret = 0;
> >> +
> >> +    if (property == vs_plane->degamma_mode) {
> >> +            if (vs_plane_state->degamma != val) {
> >> +                    vs_plane_state->degamma = val;
> >> +                    vs_plane_state->degamma_changed = true;
> >> +            } else {
> >> +                    vs_plane_state->degamma_changed = false;
> >> +            }
> >> +    } else if (property == vs_plane->watermark_prop) {
> >> +            ret = _vs_plane_set_property_blob_from_id(dev,
> >> +                                                      &vs_plane_state->watermark,
> >> +                                                      val,
> >> +                                                      sizeof(struct drm_vs_watermark));
> >> +            return ret;
> >> +    } else if (property == vs_plane->color_mgmt_prop) {
> >> +            ret = _vs_plane_set_property_blob_from_id(dev,
> >> +                                                      &vs_plane_state->color_mgmt,
> >> +                                                      val,
> >> +                                                      sizeof(struct drm_vs_color_mgmt));
> >> +            return ret;
> >> +    } else if (property == vs_plane->roi_prop) {
> >> +            ret = _vs_plane_set_property_blob_from_id(dev,
> >> +                                                      &vs_plane_state->roi,
> >> +                                                      val,
> >> +                                                      sizeof(struct drm_vs_roi));
> >> +            return ret;
> >> +    } else {
> >> +            return -EINVAL;
> >> +    }
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int vs_plane_atomic_get_property(struct drm_plane *plane,
> >> +                                    const struct drm_plane_state *state,
> >> +                                    struct drm_property *property,
> >> +                                    uint64_t *val)
> >> +{
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +    const struct vs_plane_state *vs_plane_state =
> >> +            container_of(state, const struct vs_plane_state, base);
> >> +
> >> +    if (property == vs_plane->degamma_mode)
> >> +            *val = vs_plane_state->degamma;
> >> +    else if (property == vs_plane->watermark_prop)
> >> +            *val = (vs_plane_state->watermark) ?
> >> +                                    vs_plane_state->watermark->base.id : 0;
> >> +    else if (property == vs_plane->color_mgmt_prop)
> >> +            *val = (vs_plane_state->color_mgmt) ?
> >> +                                    vs_plane_state->color_mgmt->base.id : 0;
> >
> > degamma and color management should use standard properties.
> >
> >> +    else if (property == vs_plane->roi_prop)
> >> +            *val = (vs_plane_state->roi) ?
> >> +                                    vs_plane_state->roi->base.id : 0;
> >> +    else
> >> +            return -EINVAL;
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static bool vs_format_mod_supported(struct drm_plane *plane,
> >> +                                u32 format,
> >> +                                u64 modifier)
> >> +{
> >> +    int i;
> >> +
> >> +    /* We always have to allow these modifiers:
> >> +     * 1. Core DRM checks for LINEAR support if userspace does not provide modifiers.
> >> +     * 2. Not passing any modifiers is the same as explicitly passing INVALID.
> >> +     */
> >> +    if (modifier == DRM_FORMAT_MOD_LINEAR)
> >> +            return true;
> >> +
> >> +    /* Check that the modifier is on the list of the plane's supported modifiers. */
> >> +    for (i = 0; i < plane->modifier_count; i++) {
> >> +            if (modifier == plane->modifiers[i])
> >> +                    break;
> >> +    }
> >> +
> >> +    if (i == plane->modifier_count)
> >> +            return false;
> >> +
> >> +    return true;
> >> +}
> >> +
> >> +const struct drm_plane_funcs vs_plane_funcs = {
> >> +    .update_plane           = drm_atomic_helper_update_plane,
> >> +    .disable_plane          = drm_atomic_helper_disable_plane,
> >> +    .reset                  = vs_plane_reset,
> >> +    .atomic_duplicate_state = vs_plane_atomic_duplicate_state,
> >> +    .atomic_destroy_state   = vs_plane_atomic_destroy_state,
> >> +    .atomic_set_property    = vs_plane_atomic_set_property,
> >> +    .atomic_get_property    = vs_plane_atomic_get_property,
> >> +    .format_mod_supported   = vs_format_mod_supported,
> >> +};
> >> +
> >> +static unsigned char vs_get_plane_number(struct drm_framebuffer *fb)
> >> +{
> >> +    const struct drm_format_info *info;
> >> +
> >> +    if (!fb)
> >> +            return 0;
> >> +
> >> +    info = drm_format_info(fb->format->format);
> >> +    if (!info || info->num_planes > DRM_FORMAT_MAX_PLANES)
> >> +            return 0;
> >> +
> >> +    return info->num_planes;
> >> +}
> >> +
> >> +static int vs_plane_atomic_check(struct drm_plane *plane,
> >> +                             struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> >> +                                                                             plane);
> >> +    unsigned char i, num_planes;
> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
> >> +
> >> +    if (!crtc || !fb)
> >> +            return 0;
> >> +
> >> +    num_planes = vs_get_plane_number(fb);
> >> +
> >> +    for (i = 0; i < num_planes; i++) {
> >> +            dma_addr_t dma_addr;
> >> +
> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
> >> +            plane_state->dma_addr[i] = dma_addr;
> >> +    }
> >> +
> >> +    return vs_dc_check_plane(dc, plane, state);
> >> +}
> >> +
> >> +static int vs_cursor_plane_atomic_check(struct drm_plane *plane,
> >> +                                    struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> >> +                                                                      plane);
> >> +    unsigned char i, num_planes;
> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
> >> +
> >> +    if (!crtc || !fb)
> >> +            return 0;
> >> +
> >> +    num_planes = vs_get_plane_number(fb);
> >> +
> >> +    for (i = 0; i < num_planes; i++) {
> >> +            dma_addr_t dma_addr;
> >> +
> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
> >> +            plane_state->dma_addr[i] = dma_addr;
> >> +    }
> >> +
> >> +    return vs_dc_check_cursor_plane(dc, plane, state);
> >> +}
> >> +
> >> +static void vs_plane_atomic_update(struct drm_plane *plane,
> >> +                               struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
> >> +                                                                      plane);
> >
> > New line after the equal sign will be better.
> >
> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> >> +                                                                      plane);
> >> +
> >> +    unsigned char i, num_planes;
> >> +    struct drm_framebuffer *fb;
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    if (!new_state->fb || !new_state->crtc)
> >> +            return;
> >
> > if (!new_state->visible) ?
> >
> >> +
> >> +    fb = new_state->fb;
> >> +
> >> +    drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
> >> +
> >> +    num_planes = vs_get_plane_number(fb);
> >> +
> >> +    for (i = 0; i < num_planes; i++) {
> >> +            dma_addr_t dma_addr;
> >> +
> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
> >> +            plane_state->dma_addr[i] = dma_addr;
> >> +    }
> >> +
> >> +    plane_state->status.src = drm_plane_state_src(new_state);
> >> +    plane_state->status.dest = drm_plane_state_dest(new_state);
> >> +
> >> +    vs_dc_update_plane(dc, vs_plane, plane, state);
> >> +}
> >> +
> >> +static void vs_cursor_plane_atomic_update(struct drm_plane *plane,
> >> +                                      struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
> >> +                                                                       plane);
> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> >> +                                                                       plane);
> >> +    unsigned char i, num_planes;
> >> +    struct drm_framebuffer *fb;
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    if (!new_state->fb || !new_state->crtc)
> >
> > and here.
> >
> >> +            return;
> >> +
> >> +    fb = new_state->fb;
> >> +    drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
> >> +
> >> +    num_planes = vs_get_plane_number(fb);
> >> +
> >> +    for (i = 0; i < num_planes; i++) {
> >> +            dma_addr_t dma_addr;
> >> +
> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
> >> +            plane_state->dma_addr[i] = dma_addr;
> >> +    }
> >> +
> >> +    plane_state->status.src = drm_plane_state_src(new_state);
> >> +    plane_state->status.dest = drm_plane_state_dest(new_state);
> >> +
> >> +    vs_dc_update_cursor_plane(dc, vs_plane, plane, state);
> >> +}
> >> +
> >> +static void vs_plane_atomic_disable(struct drm_plane *plane,
> >> +                                struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> >> +                                                                       plane);
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    vs_dc_disable_plane(dc, vs_plane, old_state);
> >> +}
> >> +
> >> +static void vs_cursor_plane_atomic_disable(struct drm_plane *plane,
> >> +                                       struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> >> +                                                                       plane);
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    vs_dc_disable_cursor_plane(dc, vs_plane, old_state);
> >> +}
> >> +
> >> +const struct drm_plane_helper_funcs primary_plane_helpers = {
> >> +    .atomic_check   = vs_plane_atomic_check,
> >> +    .atomic_update  = vs_plane_atomic_update,
> >> +    .atomic_disable = vs_plane_atomic_disable,
> >> +};
> >> +
> >> +const struct drm_plane_helper_funcs overlay_plane_helpers = {
> >> +    .atomic_check   = vs_plane_atomic_check,
> >> +    .atomic_update  = vs_plane_atomic_update,
> >> +    .atomic_disable = vs_plane_atomic_disable,
> >> +};
> >> +
> >> +const struct drm_plane_helper_funcs cursor_plane_helpers = {
> >> +    .atomic_check   = vs_cursor_plane_atomic_check,
> >> +    .atomic_update  = vs_cursor_plane_atomic_update,
> >> +    .atomic_disable = vs_cursor_plane_atomic_disable,
> >> +};
> >> +
> >> +static const struct drm_prop_enum_list vs_degamma_mode_enum_list[] = {
> >> +    { VS_DEGAMMA_DISABLE,   "disabled" },
> >> +    { VS_DEGAMMA_BT709,     "preset degamma for BT709" },
> >> +    { VS_DEGAMMA_BT2020,    "preset degamma for BT2020" },
> >> +};
> >> +
> >> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
> >> +                             struct vs_plane_info *info,
> >> +                             unsigned int layer_num,
> >> +                             unsigned int possible_crtcs)
> >> +{
> >> +    struct vs_plane *plane;
> >> +    int ret;
> >> +
> >> +    if (!info)
> >> +            return NULL;
> >> +
> >> +    plane = drmm_universal_plane_alloc(drm_dev, struct vs_plane, base,
> >> +                                       possible_crtcs,
> >> +                                       &vs_plane_funcs,
> >> +                                       info->formats, info->num_formats,
> >> +                                       info->modifiers, info->type,
> >> +                                       info->name ? info->name : NULL);
> >> +    if (IS_ERR(plane))
> >> +            return ERR_CAST(plane);
> >> +
> >> +    if (info->type == DRM_PLANE_TYPE_PRIMARY)
> >> +            drm_plane_helper_add(&plane->base, &primary_plane_helpers);
> >> +    else if (info->type == DRM_PLANE_TYPE_CURSOR)
> >> +            drm_plane_helper_add(&plane->base, &cursor_plane_helpers);
> >> +    else
> >> +            drm_plane_helper_add(&plane->base, &overlay_plane_helpers);
> >> +
> >> +    /* Set up the plane properties */
> >> +    if (info->degamma_size) {
> >> +            plane->degamma_mode =
> >> +            drm_property_create_enum(drm_dev, 0,
> >> +                                     "DEGAMMA_MODE",
> >> +                                     vs_degamma_mode_enum_list,
> >> +                                     ARRAY_SIZE(vs_degamma_mode_enum_list));
> >> +
> >> +            if (!plane->degamma_mode)
> >> +                    return NULL;
> >> +
> >> +            drm_object_attach_property(&plane->base.base,
> >> +                                       plane->degamma_mode,
> >> +                                       VS_DEGAMMA_DISABLE);
> >> +    }
> >> +
> >> +    if (info->rotation) {
> >> +            ret = drm_plane_create_rotation_property(&plane->base,
> >> +                                                     DRM_MODE_ROTATE_0,
> >> +                                                     info->rotation);
> >> +            if (ret)
> >> +                    return NULL;
> >> +    }
> >> +
> >> +    if (info->blend_mode) {
> >> +            ret = drm_plane_create_blend_mode_property(&plane->base,
> >> +                                                       info->blend_mode);
> >> +            if (ret)
> >> +                    return NULL;
> >> +            ret = drm_plane_create_alpha_property(&plane->base);
> >> +            if (ret)
> >> +                    return NULL;
> >> +    }
> >> +
> >> +    if (info->color_encoding) {
> >> +            ret = drm_plane_create_color_properties(&plane->base,
> >> +                                                    info->color_encoding,
> >> +                                                    BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
> >> +                                                    DRM_COLOR_YCBCR_BT709,
> >> +                                                    DRM_COLOR_YCBCR_LIMITED_RANGE);
> >> +            if (ret)
> >> +                    return NULL;
> >> +    }
> >> +
> >> +    if (info->zpos != 255) {
> >> +            ret = drm_plane_create_zpos_property(&plane->base, info->zpos, 0,
> >> +                                                 layer_num - 1);
> >> +            if (ret)
> >> +                    return NULL;
> >> +    } else {
> >> +            ret = drm_plane_create_zpos_immutable_property(&plane->base,
> >> +                                                           info->zpos);
> >> +            if (ret)
> >> +                    return NULL;
> >> +    }
> >> +
> >> +    if (info->watermark) {
> >> +            plane->watermark_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
> >> +                                                        "WATERMARK", 0);
> >> +            if (!plane->watermark_prop)
> >> +                    return NULL;
> >> +            drm_object_attach_property(&plane->base.base, plane->watermark_prop, 0);
> >> +    }
> >> +
> >> +    if (info->color_mgmt) {
> >> +            plane->color_mgmt_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
> >> +                                                         "COLOR_CONFIG", 0);
> >> +            if (!plane->color_mgmt_prop)
> >> +                    return NULL;
> >> +
> >> +            drm_object_attach_property(&plane->base.base, plane->color_mgmt_prop, 0);
> >> +    }
> >> +
> >> +    if (info->roi) {
> >> +            plane->roi_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
> >> +                                                  "ROI", 0);
> >> +            if (!plane->roi_prop)
> >> +                    return NULL;
> >> +
> >> +            drm_object_attach_property(&plane->base.base, plane->roi_prop, 0);
> >> +    }
> >> +
> >> +    return plane;
> >> +}
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h
> >> new file mode 100644
> >> index 000000000..554b74e96
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_plane.h
> >> @@ -0,0 +1,58 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#ifndef __VS_PLANE_H__
> >> +#define __VS_PLANE_H__
> >> +
> >> +#include <drm/drm_fourcc.h>
> >> +#include <drm/drm_plane_helper.h>
> >> +
> >> +#include "vs_type.h"
> >> +
> >> +struct vs_plane_status {
> >> +    u32 tile_mode;
> >> +    struct drm_rect src;
> >> +    struct drm_rect dest;
> >> +};
> >> +
> >> +struct vs_plane_state {
> >> +    struct drm_plane_state base;
> >> +    struct vs_plane_status status; /* for debugfs */
> >> +
> >> +    struct drm_property_blob *watermark;
> >> +    struct drm_property_blob *color_mgmt;
> >> +    struct drm_property_blob *roi;
> >> +    dma_addr_t dma_addr[DRM_FORMAT_MAX_PLANES];
> >> +
> >> +    u32 degamma;
> >> +    bool degamma_changed;
> >> +};
> >> +
> >> +struct vs_plane {
> >> +    struct drm_plane base;
> >> +    u8 id;
> >> +
> >> +    struct drm_property *degamma_mode;
> >> +    struct drm_property *watermark_prop;
> >> +    struct drm_property *color_mgmt_prop;
> >> +    struct drm_property *roi_prop;
> >> +};
> >> +
> >> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
> >> +                             struct vs_plane_info *info,
> >> +                             unsigned int layer_num,
> >> +                             unsigned int possible_crtcs);
> >> +
> >> +static inline struct vs_plane *to_vs_plane(struct drm_plane *plane)
> >> +{
> >> +    return container_of(plane, struct vs_plane, base);
> >> +}
> >> +
> >> +static inline struct vs_plane_state *
> >> +to_vs_plane_state(struct drm_plane_state *state)
> >> +{
> >> +    return container_of(state, struct vs_plane_state, base);
> >> +}
> >> +#endif /* __VS_PLANE_H__ */
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_type.h b/drivers/gpu/drm/verisilicon/vs_type.h
> >> new file mode 100644
> >> index 000000000..7d3378e29
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_type.h
> >> @@ -0,0 +1,69 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#ifndef __VS_TYPE_H__
> >> +#define __VS_TYPE_H__
> >> +
> >> +#include <drm/drm_plane.h>
> >> +#include <drm/drm_plane_helper.h>
> >> +
> >> +struct vs_plane_info {
> >> +    const char *name;
> >> +    u8 id;
> >> +    enum drm_plane_type type;
> >> +    unsigned int num_formats;
> >> +    const u32 *formats;
> >> +    u8 num_modifiers;
> >> +    const u64 *modifiers;
> >> +    unsigned int min_width;
> >> +    unsigned int min_height;
> >> +    unsigned int max_width;
> >> +    unsigned int max_height;
> >> +    unsigned int rotation;
> >> +    unsigned int blend_mode;
> >> +    unsigned int color_encoding;
> >> +
> >> +    /* 0 means no de-gamma LUT */
> >> +    unsigned int degamma_size;
> >> +
> >> +    int min_scale; /* 16.16 fixed point */
> >> +    int max_scale; /* 16.16 fixed point */
> >> +
> >> +    /* default zorder value,
> >> +     * and 255 means unsupported zorder capability
> >> +     */
> >> +    u8       zpos;
> >> +
> >> +    bool watermark;
> >> +    bool color_mgmt;
> >> +    bool roi;
> >> +};
> >> +
> >> +struct vs_dc_info {
> >> +    const char *name;
> >> +
> >> +    u8 panel_num;
> >> +
> >> +    /* planes */
> >> +    u8 plane_num;
> >> +    const struct vs_plane_info *planes;
> >> +
> >> +    u8 layer_num;
> >> +    unsigned int max_bpc;
> >> +    unsigned int color_formats;
> >> +
> >> +    /* 0 means no gamma LUT */
> >> +    u16 gamma_size;
> >> +    u8 gamma_bits;
> >> +
> >> +    u16 pitch_alignment;
> >> +
> >> +    bool pipe_sync;
> >> +    bool background;
> >> +    bool panel_sync;
> >> +    bool cap_dec;
> >> +};
> >> +
> >> +#endif /* __VS_TYPE_H__ */
> >



-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-11-14 10:59         ` Dmitry Baryshkov
  0 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-11-14 10:59 UTC (permalink / raw)
  To: Keith Zhao
  Cc: Emil Renner Berthing, dri-devel, Krzysztof Kozlowski,
	linux-riscv, Sumit Semwal, Shengyang Chen, linux-media,
	devicetree, Conor Dooley, Albert Ou, Maxime Ripard, Jagan Teki,
	linaro-mm-sig, Rob Herring, Chris Morgan, Paul Walmsley,
	Bjorn Andersson, linux-kernel, christian.koenig, Jack Zhu,
	Palmer Dabbelt, Thomas Zimmermann, Shawn Guo, Changhuang Liang

On Tue, 14 Nov 2023 at 12:42, Keith Zhao <keith.zhao@starfivetech.com> wrote:
>
>
>
> On 2023/10/26 3:28, Dmitry Baryshkov wrote:
> > On 25/10/2023 13:39, Keith Zhao wrote:
> >> add 2 crtcs and 8 planes in vs-drm
> >>
> >> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> >> ---
> >>   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
> >>   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
> >>   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
> >>   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
> >>   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
> >>   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
> >>   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
> >>   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
> >>   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
> >>   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
> >>   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
> >>   11 files changed, 4494 insertions(+), 2 deletions(-)
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
> >>
> >> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> >> index 7d3be305b..1d48016ca 100644
> >> --- a/drivers/gpu/drm/verisilicon/Makefile
> >> +++ b/drivers/gpu/drm/verisilicon/Makefile
> >> @@ -1,7 +1,11 @@
> >>   # SPDX-License-Identifier: GPL-2.0
> >>
> >> -vs_drm-objs := vs_drv.o \
> >> -            vs_modeset.o
> >> +vs_drm-objs := vs_dc_hw.o \
> >> +            vs_dc.o \
> >> +            vs_crtc.o \
> >> +            vs_drv.o \
> >> +            vs_modeset.o \
> >> +            vs_plane.o
> >>
> >>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
> >>
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
> >> new file mode 100644
> >> index 000000000..8a658ea77
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
> >> @@ -0,0 +1,257 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + *
> >> + */
> >> +
> >> +#include <linux/clk.h>
> >> +#include <linux/debugfs.h>
> >> +#include <linux/media-bus-format.h>
> >> +
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_atomic.h>
> >> +#include <drm/drm_crtc.h>
> >> +#include <drm/drm_gem_atomic_helper.h>
> >> +#include <drm/drm_vblank.h>
> >> +#include <drm/vs_drm.h>
> >> +
> >> +#include "vs_crtc.h"
> >> +#include "vs_dc.h"
> >> +#include "vs_drv.h"
> >> +
> >> +static void vs_crtc_reset(struct drm_crtc *crtc)
> >> +{
> >> +    struct vs_crtc_state *state;
> >> +
> >> +    if (crtc->state) {
> >> +            __drm_atomic_helper_crtc_destroy_state(crtc->state);
> >> +
> >> +            state = to_vs_crtc_state(crtc->state);
> >> +            kfree(state);
> >> +            crtc->state = NULL;
> >> +    }
> >
> > You can call your crtc_destroy_state function directly here.
> >
> >> +
> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
> >> +    if (!state)
> >> +            return;
> >> +
> >> +    __drm_atomic_helper_crtc_reset(crtc, &state->base);
> >> +}
> >> +
> >> +static struct drm_crtc_state *
> >> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
> >> +{
> >> +    struct vs_crtc_state *ori_state;
> >
> > It might be a matter of taste, but it is usually old_state.
> >
> >> +    struct vs_crtc_state *state;
> >> +
> >> +    if (!crtc->state)
> >> +            return NULL;
> >> +
> >> +    ori_state = to_vs_crtc_state(crtc->state);
> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
> >> +    if (!state)
> >> +            return NULL;
> >> +
> >> +    __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
> >> +
> >> +    state->output_fmt = ori_state->output_fmt;
> >> +    state->encoder_type = ori_state->encoder_type;
> >> +    state->bpp = ori_state->bpp;
> >> +    state->underflow = ori_state->underflow;
> >
> > Can you use kmemdup instead?
> >
> >> +
> >> +    return &state->base;
> >> +}
> >> +
> >> +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
> >> +                                     struct drm_crtc_state *state)
> >> +{
> >> +    __drm_atomic_helper_crtc_destroy_state(state);
> >> +    kfree(to_vs_crtc_state(state));
> >> +}
> >> +
> >> +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
> >> +{
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    vs_dc_enable_vblank(dc, true);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
> >> +{
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    vs_dc_enable_vblank(dc, false);
> >> +}
> >> +
> >> +static const struct drm_crtc_funcs vs_crtc_funcs = {
> >> +    .set_config             = drm_atomic_helper_set_config,
> >> +    .page_flip              = drm_atomic_helper_page_flip,
> >
> > destroy is required, see drm_mode_config_cleanup()
>
> hi Dmitry:
> if define destroy in drm_crtc_funcs,
> it will make __drmm_crtc_init_with_planes unhappy

Ack, I missed that you have been using drmm_crtc_init. BTW, I checked
your code, you should be able to switch drm
drmm_crtc_alloc_with_planes().

>
> see:
> __printf(6, 0)
> static int __drmm_crtc_init_with_planes(struct drm_device *dev,
>                                         struct drm_crtc *crtc,
>                                         struct drm_plane *primary,
>                                         struct drm_plane *cursor,
>                                         const struct drm_crtc_funcs *funcs,
>                                         const char *name,
>                                         va_list args)
> {
>         int ret;
>
>         drm_WARN_ON(dev, funcs && funcs->destroy);
>
> ........
> }
>
> It should not need to be defined here, I think
>
> >
> >> +    .reset                  = vs_crtc_reset,
> >> +    .atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
> >> +    .atomic_destroy_state   = vs_crtc_atomic_destroy_state,
> >
> > please consider adding atomic_print_state to output driver-specific bits.
> >
> >> +    .enable_vblank          = vs_crtc_enable_vblank,
> >> +    .disable_vblank         = vs_crtc_disable_vblank,
> >> +};
> >> +
> >> +static u8 cal_pixel_bits(u32 bus_format)
> >
> > This looks like a generic helper code, which can go to a common place.
> >
> >> +{
> >> +    u8 bpp;
> >> +
> >> +    switch (bus_format) {
> >> +    case MEDIA_BUS_FMT_RGB565_1X16:
> >> +    case MEDIA_BUS_FMT_UYVY8_1X16:
> >> +            bpp = 16;
> >> +            break;
> >> +    case MEDIA_BUS_FMT_RGB666_1X18:
> >> +    case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> >> +            bpp = 18;
> >> +            break;
> >> +    case MEDIA_BUS_FMT_UYVY10_1X20:
> >> +            bpp = 20;
> >> +            break;
> >> +    case MEDIA_BUS_FMT_BGR888_1X24:
> >> +    case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> >> +    case MEDIA_BUS_FMT_YUV8_1X24:
> >> +            bpp = 24;
> >> +            break;
> >> +    case MEDIA_BUS_FMT_RGB101010_1X30:
> >> +    case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> >> +    case MEDIA_BUS_FMT_YUV10_1X30:
> >> +            bpp = 30;
> >> +            break;
> >> +    default:
> >> +            bpp = 24;
> >> +            break;
> >> +    }
> >> +
> >> +    return bpp;
> >> +}
> >> +
> >> +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
> >> +                              struct drm_atomic_state *state)
> >> +{
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
> >> +
> >> +    vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
> >> +
> >> +    vs_dc_enable(dc, crtc);
> >> +    drm_crtc_vblank_on(crtc);
> >> +}
> >> +
> >> +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
> >> +                               struct drm_atomic_state *state)
> >> +{
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    drm_crtc_vblank_off(crtc);
> >> +
> >> +    vs_dc_disable(dc, crtc);
> >> +
> >> +    if (crtc->state->event && !crtc->state->active) {
> >> +            spin_lock_irq(&crtc->dev->event_lock);
> >> +            drm_crtc_send_vblank_event(crtc, crtc->state->event);
> >> +            spin_unlock_irq(&crtc->dev->event_lock);
> >> +
> >> +            crtc->state->event = NULL;
> >
> > I think even should be cleared within the lock.
> >
> >> +    }
> >> +}
> >> +
> >> +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
> >> +                             struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
> >> +                                                                      crtc);
> >> +
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct device *dev = vs_crtc->dev;
> >> +    struct drm_property_blob *blob = crtc->state->gamma_lut;
> >> +    struct drm_color_lut *lut;
> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
> >> +
> >> +    if (crtc_state->color_mgmt_changed) {
> >> +            if (blob && blob->length) {
> >> +                    lut = blob->data;
> >> +                    vs_dc_set_gamma(dc, crtc, lut,
> >> +                                    blob->length / sizeof(*lut));
> >> +                    vs_dc_enable_gamma(dc, crtc, true);
> >> +            } else {
> >> +                    vs_dc_enable_gamma(dc, crtc, false);
> >> +            }
> >> +    }
> >> +}
> >> +
> >> +static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
> >> +                             struct drm_atomic_state *state)
> >> +{
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct drm_pending_vblank_event *event = crtc->state->event;
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    vs_dc_commit(dc);
> >> +
> >> +    if (event) {
> >> +            WARN_ON(drm_crtc_vblank_get(crtc) != 0);
> >> +
> >> +            spin_lock_irq(&crtc->dev->event_lock);
> >> +            drm_crtc_arm_vblank_event(crtc, event);
> >> +            spin_unlock_irq(&crtc->dev->event_lock);
> >> +            crtc->state->event = NULL;
> >
> > I think even should be cleared within the lock.
> >
> >> +    }
> >> +}
> >> +
> >> +static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
> >> +    .atomic_check = drm_crtc_helper_atomic_check,
> >> +    .atomic_enable  = vs_crtc_atomic_enable,
> >> +    .atomic_disable = vs_crtc_atomic_disable,
> >> +    .atomic_begin   = vs_crtc_atomic_begin,
> >> +    .atomic_flush   = vs_crtc_atomic_flush,
> >> +};
> >> +
> >> +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
> >> +    { VS_SINGLE_DC,                         "single dc mode" },
> >> +    { VS_MULTI_DC_PRIMARY,          "primary dc for multi dc mode" },
> >> +    { VS_MULTI_DC_SECONDARY,        "secondary dc for multi dc mode" },
> >> +};
> >> +
> >> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
> >> +                           struct vs_dc_info *info)
> >> +{
> >> +    struct vs_crtc *crtc;
> >> +    int ret;
> >> +
> >> +    if (!info)
> >> +            return NULL;
> >> +
> >> +    crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
> >> +    if (!crtc)
> >> +            return NULL;
> >> +
> >> +    ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
> >> +                                     NULL, NULL, &vs_crtc_funcs,
> >> +                                     info->name ? info->name : NULL);
> >
> > It might be better to add drmm_crtc_init() helper.
> >
> >> +    if (ret)
> >> +            return NULL;
> >> +
> >> +    drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
> >> +
> >> +    if (info->gamma_size) {
> >> +            ret = drm_mode_crtc_set_gamma_size(&crtc->base,
> >> +                                               info->gamma_size);
> >> +            if (ret)
> >> +                    return NULL;
> >> +
> >> +            drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
> >> +                                       info->gamma_size);
> >> +    }
> >> +
> >> +    crtc->max_bpc = info->max_bpc;
> >> +    crtc->color_formats = info->color_formats;
> >> +    return crtc;
> >> +}
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h
> >> new file mode 100644
> >> index 000000000..526dd63e5
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.h
> >> @@ -0,0 +1,43 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#ifndef __VS_CRTC_H__
> >> +#define __VS_CRTC_H__
> >> +
> >> +#include <drm/drm_crtc.h>
> >> +#include <drm/drm_crtc_helper.h>
> >> +
> >> +#include "vs_type.h"
> >> +
> >> +struct vs_crtc_state {
> >> +    struct drm_crtc_state base;
> >> +
> >> +    u32 output_fmt;
> >> +    u8 encoder_type;
> >> +    u8 bpp;
> >> +    bool underflow;
> >> +};
> >> +
> >> +struct vs_crtc {
> >> +    struct drm_crtc base;
> >> +    struct device *dev;
> >> +    unsigned int max_bpc;
> >> +    unsigned int color_formats;
> >> +};
> >> +
> >> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
> >> +                           struct vs_dc_info *info);
> >> +
> >> +static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc)
> >> +{
> >> +    return container_of(crtc, struct vs_crtc, base);
> >> +}
> >> +
> >> +static inline struct vs_crtc_state *
> >> +to_vs_crtc_state(struct drm_crtc_state *state)
> >> +{
> >> +    return container_of(state, struct vs_crtc_state, base);
> >> +}
> >> +#endif /* __VS_CRTC_H__ */
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
> >> new file mode 100644
> >> index 000000000..b5ab92d98
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc.c
> >> @@ -0,0 +1,1002 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#include <linux/component.h>
> >> +#include <linux/clk.h>
> >> +#include <linux/of.h>
> >> +#include <linux/of_device.h>
> >> +#include <linux/pm_runtime.h>
> >> +#include <linux/reset.h>
> >> +
> >> +#include <drm/drm_atomic.h>
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_blend.h>
> >> +#include <drm/drm_framebuffer.h>
> >> +#include <drm/drm_vblank.h>
> >> +#include <drm/vs_drm.h>
> >> +
> >> +#include "vs_dc_hw.h"
> >> +#include "vs_dc.h"
> >> +#include "vs_drv.h"
> >> +
> >> +static const char * const vout_clocks[] = {
> >> +    "noc_bus",
> >> +    "channel0",
> >> +    "channel1",
> >> +    "dc_core",
> >> +    "axi_core",
> >> +    "ahb",
> >> +    "hdmi_tx",
> >> +    "dc_parent",
> >> +
> >> +};
> >> +
> >> +static const char * const vout_resets[] = {
> >> +    "axi",
> >> +    "ahb",
> >> +    "core",
> >> +};
> >> +
> >> +static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb)
> >> +{
> >> +    u8 f = FORMAT_A8R8G8B8;
> >> +
> >> +    switch (format) {
> >> +    case DRM_FORMAT_XRGB4444:
> >> +    case DRM_FORMAT_RGBX4444:
> >> +    case DRM_FORMAT_XBGR4444:
> >> +    case DRM_FORMAT_BGRX4444:
> >> +            f = FORMAT_X4R4G4B4;
> >> +            break;
> >> +    case DRM_FORMAT_ARGB4444:
> >> +    case DRM_FORMAT_RGBA4444:
> >> +    case DRM_FORMAT_ABGR4444:
> >> +    case DRM_FORMAT_BGRA4444:
> >> +            f = FORMAT_A4R4G4B4;
> >> +            break;
> >> +    case DRM_FORMAT_XRGB1555:
> >> +    case DRM_FORMAT_RGBX5551:
> >> +    case DRM_FORMAT_XBGR1555:
> >> +    case DRM_FORMAT_BGRX5551:
> >> +            f = FORMAT_X1R5G5B5;
> >> +            break;
> >> +    case DRM_FORMAT_ARGB1555:
> >> +    case DRM_FORMAT_RGBA5551:
> >> +    case DRM_FORMAT_ABGR1555:
> >> +    case DRM_FORMAT_BGRA5551:
> >> +            f = FORMAT_A1R5G5B5;
> >> +            break;
> >> +    case DRM_FORMAT_RGB565:
> >> +    case DRM_FORMAT_BGR565:
> >> +            f = FORMAT_R5G6B5;
> >> +            break;
> >> +    case DRM_FORMAT_XRGB8888:
> >> +    case DRM_FORMAT_RGBX8888:
> >> +    case DRM_FORMAT_XBGR8888:
> >> +    case DRM_FORMAT_BGRX8888:
> >> +            f = FORMAT_X8R8G8B8;
> >> +            break;
> >> +    case DRM_FORMAT_ARGB8888:
> >> +    case DRM_FORMAT_RGBA8888:
> >> +    case DRM_FORMAT_ABGR8888:
> >> +    case DRM_FORMAT_BGRA8888:
> >> +            f = FORMAT_A8R8G8B8;
> >> +            break;
> >> +    case DRM_FORMAT_YUYV:
> >> +    case DRM_FORMAT_YVYU:
> >> +            f = FORMAT_YUY2;
> >> +            break;
> >> +    case DRM_FORMAT_UYVY:
> >> +    case DRM_FORMAT_VYUY:
> >> +            f = FORMAT_UYVY;
> >> +            break;
> >> +    case DRM_FORMAT_YUV420:
> >> +    case DRM_FORMAT_YVU420:
> >> +            f = FORMAT_YV12;
> >> +            break;
> >> +    case DRM_FORMAT_NV21:
> >> +            f = FORMAT_NV12;
> >> +            break;
> >> +    case DRM_FORMAT_NV16:
> >> +    case DRM_FORMAT_NV61:
> >> +            f = FORMAT_NV16;
> >> +            break;
> >> +    case DRM_FORMAT_P010:
> >> +            f = FORMAT_P010;
> >> +            break;
> >> +    case DRM_FORMAT_ARGB2101010:
> >> +    case DRM_FORMAT_RGBA1010102:
> >> +    case DRM_FORMAT_ABGR2101010:
> >> +    case DRM_FORMAT_BGRA1010102:
> >> +            f = FORMAT_A2R10G10B10;
> >> +            break;
> >> +    case DRM_FORMAT_NV12:
> >> +            f = FORMAT_NV12;
> >> +            break;
> >> +    case DRM_FORMAT_YUV444:
> >> +            f = FORMAT_YUV444;
> >> +            break;
> >> +    default:
> >> +            break;
> >> +    }
> >> +
> >> +    fb->format = f;
> >> +}
> >> +
> >> +static inline void update_swizzle(u32 format, struct dc_hw_fb *fb)
> >> +{
> >> +    fb->swizzle = SWIZZLE_ARGB;
> >> +    fb->uv_swizzle = 0;
> >> +
> >> +    switch (format) {
> >> +    case DRM_FORMAT_RGBX4444:
> >> +    case DRM_FORMAT_RGBA4444:
> >> +    case DRM_FORMAT_RGBX5551:
> >> +    case DRM_FORMAT_RGBA5551:
> >> +    case DRM_FORMAT_RGBX8888:
> >> +    case DRM_FORMAT_RGBA8888:
> >> +    case DRM_FORMAT_RGBA1010102:
> >> +            fb->swizzle = SWIZZLE_RGBA;
> >> +            break;
> >> +    case DRM_FORMAT_XBGR4444:
> >> +    case DRM_FORMAT_ABGR4444:
> >> +    case DRM_FORMAT_XBGR1555:
> >> +    case DRM_FORMAT_ABGR1555:
> >> +    case DRM_FORMAT_BGR565:
> >> +    case DRM_FORMAT_XBGR8888:
> >> +    case DRM_FORMAT_ABGR8888:
> >> +    case DRM_FORMAT_ABGR2101010:
> >> +            fb->swizzle = SWIZZLE_ABGR;
> >> +            break;
> >> +    case DRM_FORMAT_BGRX4444:
> >> +    case DRM_FORMAT_BGRA4444:
> >> +    case DRM_FORMAT_BGRX5551:
> >> +    case DRM_FORMAT_BGRA5551:
> >> +    case DRM_FORMAT_BGRX8888:
> >> +    case DRM_FORMAT_BGRA8888:
> >> +    case DRM_FORMAT_BGRA1010102:
> >> +            fb->swizzle = SWIZZLE_BGRA;
> >> +            break;
> >> +    case DRM_FORMAT_YVYU:
> >> +    case DRM_FORMAT_VYUY:
> >> +    case DRM_FORMAT_NV21:
> >> +    case DRM_FORMAT_NV61:
> >> +            fb->uv_swizzle = 1;
> >> +            break;
> >> +    default:
> >> +            break;
> >> +    }
> >> +}
> >> +
> >> +static inline void update_watermark(struct drm_property_blob *watermark,
> >> +                                struct dc_hw_fb *fb)
> >> +{
> >> +    struct drm_vs_watermark *data;
> >> +
> >> +    fb->water_mark = 0;
> >> +
> >> +    if (watermark) {
> >> +            data = watermark->data;
> >> +            fb->water_mark = data->watermark & 0xFFFFF;
> >> +    }
> >> +}
> >> +
> >> +static inline u8 to_vs_rotation(unsigned int rotation)
> >> +{
> >> +    u8 rot;
> >> +
> >> +    switch (rotation & DRM_MODE_REFLECT_MASK) {
> >> +    case DRM_MODE_REFLECT_X:
> >> +            rot = FLIP_X;
> >> +            return rot;
> >> +    case DRM_MODE_REFLECT_Y:
> >> +            rot = FLIP_Y;
> >> +            return rot;
> >> +    case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
> >> +            rot = FLIP_XY;
> >> +            return rot;
> >> +    default:
> >> +            break;
> >> +    }
> >> +
> >> +    switch (rotation & DRM_MODE_ROTATE_MASK) {
> >> +    case DRM_MODE_ROTATE_0:
> >> +            rot = ROT_0;
> >> +            break;
> >> +    case DRM_MODE_ROTATE_90:
> >> +            rot = ROT_90;
> >> +            break;
> >> +    case DRM_MODE_ROTATE_180:
> >> +            rot = ROT_180;
> >> +            break;
> >> +    case DRM_MODE_ROTATE_270:
> >> +            rot = ROT_270;
> >> +            break;
> >> +    default:
> >> +            rot = ROT_0;
> >> +            break;
> >> +    }
> >> +
> >> +    return rot;
> >> +}
> >> +
> >> +static inline u8 to_vs_yuv_color_space(u32 color_space)
> >> +{
> >> +    u8 cs;
> >> +
> >> +    switch (color_space) {
> >> +    case DRM_COLOR_YCBCR_BT601:
> >> +            cs = COLOR_SPACE_601;
> >> +            break;
> >> +    case DRM_COLOR_YCBCR_BT709:
> >> +            cs = COLOR_SPACE_709;
> >> +            break;
> >> +    case DRM_COLOR_YCBCR_BT2020:
> >> +            cs = COLOR_SPACE_2020;
> >> +            break;
> >> +    default:
> >> +            cs = COLOR_SPACE_601;
> >> +            break;
> >> +    }
> >> +
> >> +    return cs;
> >> +}
> >> +
> >> +static inline u8 to_vs_tile_mode(u64 modifier)
> >> +{
> >> +    if (modifier == DRM_FORMAT_MOD_VIVANTE_TILED)
> >> +            return DC_TILE_MODE4X4;
> >> +
> >> +    return (u8)(modifier & DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK);
> >> +}
> >> +
> >> +static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc)
> >> +{
> >> +    u8 panel_num = dc->hw.info->panel_num;
> >> +    u32 index = drm_crtc_index(crtc);
> >> +    int i;
> >> +
> >> +    for (i = 0; i < panel_num; i++) {
> >> +            if (index == dc->crtc[i]->base.index)
> >> +                    return i;
> >> +    }
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
> >> +                                      unsigned int alignment)
> >> +{
> >> +    struct vs_drm_device *priv = to_vs_drm_private(drm_dev);
> >> +
> >> +    if (alignment > priv->pitch_alignment)
> >> +            priv->pitch_alignment = alignment;
> >> +}
> >> +
> >> +static int plda_clk_rst_init(struct device *dev)
> >> +{
> >> +    int ret = 0;
> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
> >> +
> >> +    ret = clk_bulk_prepare_enable(dc->nclks, dc->clk_vout);
> >> +    if (ret) {
> >> +            dev_err(dev, "failed to enable clocks\n");
> >> +            return ret;
> >> +    }
> >> +
> >> +    ret = reset_control_bulk_deassert(dc->nrsts, dc->rst_vout);
> >> +    return ret;
> >> +}
> >> +
> >> +static void plda_clk_rst_deinit(struct device *dev)
> >> +{
> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
> >> +
> >> +    reset_control_bulk_assert(dc->nrsts, dc->rst_vout);
> >> +    clk_bulk_disable_unprepare(dc->nclks, dc->clk_vout);
> >> +}
> >> +
> >> +static void dc_deinit(struct device *dev)
> >> +{
> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
> >> +
> >> +    dc_hw_enable_interrupt(&dc->hw, 0);
> >> +    dc_hw_deinit(&dc->hw);
> >> +    plda_clk_rst_deinit(dev);
> >> +}
> >> +
> >> +static int dc_init(struct device *dev)
> >> +{
> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
> >> +    int ret;
> >> +
> >> +    dc->first_frame = true;
> >> +
> >> +    ret = plda_clk_rst_init(dev);
> >> +    if (ret < 0) {
> >> +            dev_err(dev, "failed to init dc clk reset: %d\n", ret);
> >> +            return ret;
> >> +    }
> >> +
> >> +    ret = dc_hw_init(&dc->hw);
> >> +    if (ret) {
> >> +            dev_err(dev, "failed to init DC HW\n");
> >> +            return ret;
> >> +    }
> >> +    return 0;
> >> +}
> >> +
> >> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
> >> +{
> >> +    struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
> >> +    struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> >> +    struct dc_hw_display display;
> >> +
> >> +    display.bus_format = crtc_state->output_fmt;
> >> +    display.h_active = mode->hdisplay;
> >> +    display.h_total = mode->htotal;
> >> +    display.h_sync_start = mode->hsync_start;
> >> +    display.h_sync_end = mode->hsync_end;
> >> +    if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> >> +            display.h_sync_polarity = true;
> >> +    else
> >> +            display.h_sync_polarity = false;
> >> +
> >> +    display.v_active = mode->vdisplay;
> >> +    display.v_total = mode->vtotal;
> >> +    display.v_sync_start = mode->vsync_start;
> >> +    display.v_sync_end = mode->vsync_end;
> >> +
> >> +    if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> >> +            display.v_sync_polarity = true;
> >> +    else
> >> +            display.v_sync_polarity = false;
> >> +
> >> +    display.id = to_vs_display_id(dc, crtc);
> >> +
> >> +    display.enable = true;
> >> +
> >> +    if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) {
> >> +            dc_hw_set_out(&dc->hw, OUT_DPI, display.id);
> >> +            clk_set_rate(dc->clk_vout[CLK_VOUT_SOC_PIX].clk, mode->clock * 1000);
> >> +            clk_set_parent(dc->clk_vout[CLK_VOUT_PIX1].clk,
> >> +                           dc->clk_vout[CLK_VOUT_SOC_PIX].clk);
> >> +    } else {
> >> +            dc_hw_set_out(&dc->hw, OUT_DP, display.id);
> >> +            clk_set_parent(dc->clk_vout[CLK_VOUT_PIX0].clk,
> >> +                           dc->clk_vout[CLK_VOUT_HDMI_PIX].clk);
> >> +    }
> >> +
> >> +    dc_hw_setup_display(&dc->hw, &display);
> >> +}
> >> +
> >> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc)
> >> +{
> >> +    struct dc_hw_display display;
> >> +
> >> +    display.id = to_vs_display_id(dc, crtc);
> >> +    display.enable = false;
> >> +
> >> +    dc_hw_setup_display(&dc->hw, &display);
> >> +}
> >> +
> >> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> >> +                 struct drm_color_lut *lut, unsigned int size)
> >> +{
> >> +    u16 i, r, g, b;
> >> +    u8 bits, id;
> >> +
> >> +    if (size != dc->hw.info->gamma_size) {
> >> +            drm_err(crtc->dev, "gamma size does not match!\n");
> >> +            return;
> >> +    }
> >> +
> >> +    id = to_vs_display_id(dc, crtc);
> >> +
> >> +    bits = dc->hw.info->gamma_bits;
> >> +    for (i = 0; i < size; i++) {
> >> +            r = drm_color_lut_extract(lut[i].red, bits);
> >> +            g = drm_color_lut_extract(lut[i].green, bits);
> >> +            b = drm_color_lut_extract(lut[i].blue, bits);
> >> +            dc_hw_update_gamma(&dc->hw, id, i, r, g, b);
> >> +    }
> >> +}
> >> +
> >> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> >> +                    bool enable)
> >> +{
> >> +    u8 id;
> >> +
> >> +    id = to_vs_display_id(dc, crtc);
> >> +    dc_hw_enable_gamma(&dc->hw, id, enable);
> >> +}
> >> +
> >> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable)
> >> +{
> >> +    dc_hw_enable_interrupt(&dc->hw, enable);
> >> +}
> >> +
> >> +static u32 calc_factor(u32 src, u32 dest)
> >> +{
> >> +    u32 factor = 1 << 16;
> >> +
> >> +    if (src > 1 && dest > 1)
> >> +            factor = ((src - 1) << 16) / (dest - 1);
> >> +
> >> +    return factor;
> >> +}
> >> +
> >> +static void update_scale(struct drm_plane_state *state, struct dc_hw_roi *roi,
> >> +                     struct dc_hw_scale *scale)
> >> +{
> >> +    int dst_w = drm_rect_width(&state->dst);
> >> +    int dst_h = drm_rect_height(&state->dst);
> >> +    int src_w, src_h, temp;
> >> +
> >> +    scale->enable = false;
> >> +
> >> +    if (roi->enable) {
> >> +            src_w = roi->width;
> >> +            src_h = roi->height;
> >> +    } else {
> >> +            src_w = drm_rect_width(&state->src) >> 16;
> >> +            src_h = drm_rect_height(&state->src) >> 16;
> >> +    }
> >> +
> >> +    if (drm_rotation_90_or_270(state->rotation)) {
> >> +            temp = src_w;
> >> +            src_w = src_h;
> >> +            src_h = temp;
> >> +    }
> >> +
> >> +    if (src_w != dst_w) {
> >> +            scale->scale_factor_x = calc_factor(src_w, dst_w);
> >> +            scale->enable = true;
> >> +    } else {
> >> +            scale->scale_factor_x = 1 << 16;
> >> +    }
> >> +    if (src_h != dst_h) {
> >> +            scale->scale_factor_y = calc_factor(src_h, dst_h);
> >> +            scale->enable = true;
> >> +    } else {
> >> +            scale->scale_factor_y = 1 << 16;
> >> +    }
> >> +}
> >> +
> >> +static void update_fb(struct vs_plane *plane, u8 display_id,
> >> +                  struct dc_hw_fb *fb, struct drm_plane_state *state)
> >> +{
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
> >> +    struct drm_framebuffer *drm_fb = state->fb;
> >> +    struct drm_rect *src = &state->src;
> >> +
> >> +    fb->display_id = display_id;
> >> +    fb->y_address = plane_state->dma_addr[0];
> >> +    fb->y_stride = drm_fb->pitches[0];
> >> +    if (drm_fb->format->format == DRM_FORMAT_YVU420) {
> >> +            fb->u_address = plane_state->dma_addr[2];
> >> +            fb->v_address = plane_state->dma_addr[1];
> >> +            fb->u_stride = drm_fb->pitches[2];
> >> +            fb->v_stride = drm_fb->pitches[1];
> >> +    } else {
> >> +            fb->u_address = plane_state->dma_addr[1];
> >> +            fb->v_address = plane_state->dma_addr[2];
> >> +            fb->u_stride = drm_fb->pitches[1];
> >> +            fb->v_stride = drm_fb->pitches[2];
> >> +    }
> >> +    fb->width = drm_rect_width(src) >> 16;
> >> +    fb->height = drm_rect_height(src) >> 16;
> >> +    fb->tile_mode = to_vs_tile_mode(drm_fb->modifier);
> >> +    fb->rotation = to_vs_rotation(state->rotation);
> >> +    fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding);
> >> +    fb->zpos = state->zpos;
> >> +    fb->enable = state->visible;
> >> +    update_format(drm_fb->format->format, drm_fb->modifier, fb);
> >> +    update_swizzle(drm_fb->format->format, fb);
> >> +    update_watermark(plane_state->watermark, fb);
> >> +    plane_state->status.tile_mode = fb->tile_mode;
> >> +}
> >> +
> >> +static void update_degamma(struct vs_dc *dc, struct vs_plane *plane,
> >> +                       struct vs_plane_state *plane_state)
> >> +{
> >> +    dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
> >> +    plane_state->degamma_changed = false;
> >> +}
> >> +
> >> +static void update_roi(struct vs_dc *dc, u8 id,
> >> +                   struct vs_plane_state *plane_state,
> >> +                   struct dc_hw_roi *roi,
> >> +                   struct drm_plane_state *state)
> >> +{
> >> +    struct drm_vs_roi *data;
> >> +    struct drm_rect *src = &state->src;
> >> +    u16 src_w = drm_rect_width(src) >> 16;
> >> +    u16 src_h = drm_rect_height(src) >> 16;
> >> +
> >> +    if (plane_state->roi) {
> >> +            data = plane_state->roi->data;
> >> +
> >> +            if (data->enable) {
> >> +                    roi->x = data->roi_x;
> >> +                    roi->y = data->roi_y;
> >> +                    roi->width = (data->roi_x + data->roi_w > src_w) ?
> >> +                                             (src_w - data->roi_x) : data->roi_w;
> >> +                    roi->height = (data->roi_y + data->roi_h > src_h) ?
> >> +                                              (src_h - data->roi_y) : data->roi_h;
> >> +                    roi->enable = true;
> >> +            } else {
> >> +                    roi->enable = false;
> >> +            }
> >> +
> >> +            dc_hw_update_roi(&dc->hw, id, roi);
> >> +    } else {
> >> +            roi->enable = false;
> >> +    }
> >> +}
> >> +
> >> +static void update_color_mgmt(struct vs_dc *dc, u8 id,
> >> +                          struct dc_hw_fb *fb,
> >> +                          struct vs_plane_state *plane_state)
> >> +{
> >> +    struct drm_vs_color_mgmt *data;
> >> +    struct dc_hw_colorkey colorkey;
> >> +
> >> +    if (plane_state->color_mgmt) {
> >> +            data = plane_state->color_mgmt->data;
> >> +
> >> +            fb->clear_enable = data->clear_enable;
> >> +            fb->clear_value = data->clear_value;
> >> +
> >> +            if (data->colorkey > data->colorkey_high)
> >> +                    data->colorkey = data->colorkey_high;
> >> +
> >> +            colorkey.colorkey = data->colorkey;
> >> +            colorkey.colorkey_high = data->colorkey_high;
> >> +            colorkey.transparency = (data->transparency) ?
> >> +                            DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE;
> >> +            dc_hw_update_colorkey(&dc->hw, id, &colorkey);
> >> +    }
> >> +}
> >> +
> >> +static void update_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                     struct drm_plane *drm_plane,
> >> +                     struct drm_atomic_state *drm_state)
> >> +{
> >> +    struct dc_hw_fb fb = {0};
> >> +    struct dc_hw_scale scale;
> >> +    struct dc_hw_position pos;
> >> +    struct dc_hw_blend blend;
> >> +    struct dc_hw_roi roi;
> >> +    struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> >> +                                                                       drm_plane);
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
> >> +    struct drm_rect *dest = &state->dst;
> >> +    bool dec_enable = false;
> >> +    u8 display_id = 0;
> >> +
> >> +    display_id = to_vs_display_id(dc, state->crtc);
> >> +    update_fb(plane, display_id, &fb, state);
> >> +    fb.dec_enable = dec_enable;
> >> +
> >> +    update_roi(dc, plane->id, plane_state, &roi, state);
> >> +
> >> +    update_scale(state, &roi, &scale);
> >> +
> >> +    if (plane_state->degamma_changed)
> >> +            update_degamma(dc, plane, plane_state);
> >> +
> >> +    pos.start_x = dest->x1;
> >> +    pos.start_y = dest->y1;
> >> +    pos.end_x = dest->x2;
> >> +    pos.end_y = dest->y2;
> >> +
> >> +    blend.alpha = (u8)(state->alpha >> 8);
> >> +    blend.blend_mode = (u8)(state->pixel_blend_mode);
> >> +
> >> +    update_color_mgmt(dc, plane->id, &fb, plane_state);
> >> +
> >> +    dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
> >> +}
> >> +
> >> +static void update_qos(struct vs_dc *dc, struct vs_plane *plane,
> >> +                   struct drm_plane *drm_plane,
> >> +                   struct drm_atomic_state *drm_state)
> >> +{
> >> +    struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> >> +                                                                       drm_plane);
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
> >> +    struct drm_vs_watermark *data;
> >> +    struct dc_hw_qos qos;
> >> +
> >> +    if (plane_state->watermark) {
> >> +            data = plane_state->watermark->data;
> >> +
> >> +            if (data->qos_high) {
> >> +                    if (data->qos_low > data->qos_high)
> >> +                            data->qos_low = data->qos_high;
> >> +
> >> +                    qos.low_value = data->qos_low & 0x0F;
> >> +                    qos.high_value = data->qos_high & 0x0F;
> >> +                    dc_hw_update_qos(&dc->hw, &qos);
> >> +            }
> >> +    }
> >> +}
> >> +
> >> +static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor)
> >> +{
> >> +    u8 size_type;
> >> +
> >> +    switch (state->crtc_w) {
> >> +    case 32:
> >> +            size_type = CURSOR_SIZE_32X32;
> >> +            break;
> >> +    case 64:
> >> +            size_type = CURSOR_SIZE_64X64;
> >> +            break;
> >> +    default:
> >> +            size_type = CURSOR_SIZE_32X32;
> >> +            break;
> >> +    }
> >> +
> >> +    cursor->size = size_type;
> >> +}
> >> +
> >> +static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                            struct drm_plane *drm_plane,
> >> +                            struct drm_atomic_state *drm_state)
> >> +{
> >> +    struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> >> +                                                                   drm_plane);
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
> >> +    struct drm_framebuffer *drm_fb = state->fb;
> >> +    struct dc_hw_cursor cursor;
> >> +
> >> +    cursor.address = plane_state->dma_addr[0];
> >> +    cursor.x = state->crtc_x;
> >> +    cursor.y = state->crtc_y;
> >> +    cursor.hot_x = drm_fb->hot_x;
> >> +    cursor.hot_y = drm_fb->hot_y;
> >> +    cursor.display_id = to_vs_display_id(dc, state->crtc);
> >> +    update_cursor_size(state, &cursor);
> >> +    cursor.enable = true;
> >> +
> >> +    dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
> >> +}
> >> +
> >> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                    struct drm_plane *drm_plane,
> >> +                    struct drm_atomic_state *drm_state)
> >> +{
> >> +    update_plane(dc, plane, drm_plane, drm_state);
> >> +    update_qos(dc, plane, drm_plane, drm_state);
> >> +}
> >> +
> >> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                           struct drm_plane *drm_plane,
> >> +                           struct drm_atomic_state *drm_state)
> >> +{
> >> +    update_cursor_plane(dc, plane, drm_plane, drm_state);
> >> +}
> >> +
> >> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                     struct drm_plane_state *old_state)
> >> +{
> >> +    struct dc_hw_fb fb = {0};
> >> +
> >> +    fb.enable = false;
> >> +    dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL, NULL, NULL);
> >> +}
> >> +
> >> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                            struct drm_plane_state *old_state)
> >> +{
> >> +    struct dc_hw_cursor cursor = {0};
> >> +
> >> +    cursor.enable = false;
> >> +    cursor.display_id = to_vs_display_id(dc, old_state->crtc);
> >> +    dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
> >> +}
> >> +
> >> +static bool vs_dc_mod_supported(const struct vs_plane_info *plane_info,
> >> +                            u64 modifier)
> >> +{
> >> +    const u64 *mods;
> >> +
> >> +    if (!plane_info->modifiers)
> >> +            return false;
> >> +
> >> +    for (mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID; mods++) {
> >> +            if (*mods == modifier)
> >> +                    return true;
> >> +    }
> >> +
> >> +    return false;
> >> +}
> >> +
> >> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
> >> +                  struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> >> +                                                                             plane);
> >> +
> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
> >> +    const struct vs_plane_info *plane_info;
> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
> >> +    struct drm_crtc_state *crtc_state;
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +
> >> +    plane_info = &dc->hw.info->planes[vs_plane->id];
> >> +
> >> +    if (fb->width < plane_info->min_width ||
> >> +        fb->width > plane_info->max_width ||
> >> +        fb->height < plane_info->min_height ||
> >> +        fb->height > plane_info->max_height)
> >> +            drm_err_once(plane->dev, "buffer size may not support on plane%d.\n",
> >> +                         vs_plane->id);
> >> +
> >> +    if (!vs_dc_mod_supported(plane_info, fb->modifier)) {
> >> +            drm_err(plane->dev, "unsupported modifier on plane%d.\n", vs_plane->id);
> >> +            return -EINVAL;
> >> +    }
> >> +
> >> +    crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
> >> +    return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
> >> +                                              plane_info->min_scale,
> >> +                                              plane_info->max_scale,
> >> +                                              true, true);
> >> +}
> >> +
> >> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
> >> +                         struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> >> +                                                                       plane);
> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
> >> +    const struct vs_plane_info *plane_info;
> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
> >> +    struct drm_crtc_state *crtc_state;
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +
> >> +    plane_info = &dc->hw.info->planes[vs_plane->id];
> >> +
> >> +    if (fb->width < plane_info->min_width ||
> >> +        fb->width > plane_info->max_width ||
> >> +        fb->height < plane_info->min_height ||
> >> +        fb->height > plane_info->max_height)
> >> +            drm_err_once(plane->dev, "buffer size may not support on plane%d.\n", vs_plane->id);
> >> +
> >> +    crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
> >> +    if (IS_ERR(crtc_state))
> >> +            return -EINVAL;
> >> +
> >> +    return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
> >> +                                            plane_info->min_scale,
> >> +                                            plane_info->max_scale,
> >> +                                            true, true);
> >> +}
> >> +
> >> +static void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
> >> +{
> >> +    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
> >> +
> >> +    drm_crtc_handle_vblank(crtc);
> >> +
> >> +    vs_crtc_state->underflow = underflow;
> >> +}
> >> +
> >> +static irqreturn_t dc_isr(int irq, void *data)
> >> +{
> >> +    struct vs_dc *dc = data;
> >> +    struct vs_dc_info *dc_info = dc->hw.info;
> >> +    u32 i, ret;
> >> +
> >> +    ret = dc_hw_get_interrupt(&dc->hw);
> >> +
> >> +    for (i = 0; i < dc_info->panel_num; i++)
> >> +            vs_crtc_handle_vblank(&dc->crtc[i]->base, dc_hw_check_underflow(&dc->hw));
> >> +
> >> +    return IRQ_HANDLED;
> >> +}
> >> +
> >> +void vs_dc_commit(struct vs_dc *dc)
> >> +{
> >> +    dc_hw_enable_shadow_register(&dc->hw, false);
> >> +
> >> +    dc_hw_commit(&dc->hw);
> >> +
> >> +    if (dc->first_frame)
> >> +            dc->first_frame = false;
> >> +
> >> +    dc_hw_enable_shadow_register(&dc->hw, true);
> >> +}
> >> +
> >> +static int dc_bind(struct device *dev, struct device *master, void *data)
> >> +{
> >> +    struct drm_device *drm_dev = data;
> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
> >> +    struct device_node *port;
> >> +    struct vs_crtc *crtc;
> >> +    struct vs_dc_info *dc_info;
> >> +    struct vs_plane *plane;
> >> +    struct vs_plane_info *plane_info;
> >> +    int i, ret;
> >> +    u32 ctrc_mask = 0;
> >> +
> >> +    if (!drm_dev || !dc) {
> >> +            dev_err(dev, "devices are not created.\n");
> >> +            return -ENODEV;
> >> +    }
> >> +
> >> +    ret = dc_init(dev);
> >> +    if (ret < 0) {
> >> +            drm_err(drm_dev, "Failed to initialize DC hardware.\n");
> >> +            return ret;
> >> +    }
> >> +
> >> +    port = of_get_child_by_name(dev->of_node, "port");
> >> +    if (!port) {
> >> +            drm_err(drm_dev, "no port node found\n");
> >> +            return -ENODEV;
> >> +    }
> >> +    of_node_put(port);
> >> +
> >> +    dc_info = dc->hw.info;
> >> +
> >> +    for (i = 0; i < dc_info->panel_num; i++) {
> >> +            crtc = vs_crtc_create(drm_dev, dc_info);
> >> +            if (!crtc) {
> >> +                    drm_err(drm_dev, "Failed to create CRTC.\n");
> >> +                    ret = -ENOMEM;
> >> +                    return ret;
> >> +            }
> >> +
> >> +            crtc->base.port = port;
> >> +            crtc->dev = dev;
> >> +            dc->crtc[i] = crtc;
> >> +            ctrc_mask |= drm_crtc_mask(&crtc->base);
> >> +    }
> >> +
> >> +    for (i = 0; i < dc_info->plane_num; i++) {
> >> +            plane_info = (struct vs_plane_info *)&dc_info->planes[i];
> >> +
> >> +            if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
> >> +                    plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
> >> +                                            drm_crtc_mask(&dc->crtc[0]->base));
> >> +            } else if (!strcmp(plane_info->name, "Primary_1") ||
> >> +                               !strcmp(plane_info->name, "Cursor_1")) {
> >> +                    plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
> >> +                                            drm_crtc_mask(&dc->crtc[1]->base));
> >
> > I'd say, it's not typical to use name to distinguish objects. Maybe you
> > can add indices to the info structure and drop the names instead?
> >
> >
> >> +            } else {
> >> +                    plane = vs_plane_create(drm_dev, plane_info,
> >> +                                            dc_info->layer_num, ctrc_mask);
> >> +            }
> >> +
> >> +            if (IS_ERR(plane)) {
> >> +                    dev_err(dev, "failed to construct plane\n");
> >> +                    return PTR_ERR(plane);
> >> +            }
> >> +
> >> +            plane->id = i;
> >> +            dc->planes[i].id = plane_info->id;
> >> +
> >> +            if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
> >> +                    if (!strcmp(plane_info->name, "Primary"))
> >> +                            dc->crtc[0]->base.primary = &plane->base;
> >> +                    else
> >> +                            dc->crtc[1]->base.primary = &plane->base;
> >
> > This definitely urges for an index in the plane info / plane_state.
> >
> >> +                    drm_dev->mode_config.min_width = plane_info->min_width;
> >> +                    drm_dev->mode_config.min_height =
> >> +                                                    plane_info->min_height;
> >> +                    drm_dev->mode_config.max_width = plane_info->max_width;
> >> +                    drm_dev->mode_config.max_height =
> >> +                                                    plane_info->max_height;
> >> +            }
> >> +
> >> +            if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
> >> +                    if (!strcmp(plane_info->name, "Cursor"))
> >> +                            dc->crtc[0]->base.cursor = &plane->base;
> >> +                    else
> >> +                            dc->crtc[1]->base.cursor = &plane->base;
> >> +                    drm_dev->mode_config.cursor_width =
> >> +                                                    plane_info->max_width;
> >> +                    drm_dev->mode_config.cursor_height =
> >> +                                                    plane_info->max_height;
> >> +            }
> >> +    }
> >> +
> >> +    vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
> >> +    return 0;
> >> +}
> >> +
> >> +static void dc_unbind(struct device *dev, struct device *master, void *data)
> >> +{
> >> +    dc_deinit(dev);
> >> +}
> >> +
> >> +const struct component_ops dc_component_ops = {
> >> +    .bind = dc_bind,
> >> +    .unbind = dc_unbind,
> >> +};
> >> +
> >> +static const struct of_device_id dc_driver_dt_match[] = {
> >> +    { .compatible = "starfive,jh7110-dc8200", },
> >> +    {},
> >> +};
> >> +MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
> >> +
> >> +static int dc_probe(struct platform_device *pdev)
> >> +{
> >> +    struct device *dev = &pdev->dev;
> >> +    struct vs_dc *dc;
> >> +    int irq, ret, i;
> >> +
> >> +    dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
> >> +    if (!dc)
> >> +            return -ENOMEM;
> >> +
> >> +    dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
> >> +    if (IS_ERR(dc->hw.hi_base))
> >> +            return PTR_ERR(dc->hw.hi_base);
> >> +
> >> +    dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
> >> +    if (IS_ERR(dc->hw.reg_base))
> >> +            return PTR_ERR(dc->hw.reg_base);
> >> +
> >> +    dc->nclks = ARRAY_SIZE(dc->clk_vout);
> >> +    for (i = 0; i < dc->nclks; ++i)
> >> +            dc->clk_vout[i].id = vout_clocks[i];
> >> +    ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
> >> +    if (ret) {
> >> +            dev_err(dev, "Failed to get clk controls\n");
> >> +            return ret;
> >> +    }
> >> +
> >> +    dc->nrsts = ARRAY_SIZE(dc->rst_vout);
> >> +    for (i = 0; i < dc->nrsts; ++i)
> >> +            dc->rst_vout[i].id = vout_resets[i];
> >> +    ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
> >> +                                             dc->rst_vout);
> >> +    if (ret) {
> >> +            dev_err(dev, "Failed to get reset controls\n");
> >> +            return ret;
> >> +    }
> >> +
> >> +    irq = platform_get_irq(pdev, 0);
> >> +
> >> +    ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
> >
> > Are you ready to handle the IRQ at this point?
> >
> >> +    if (ret < 0) {
> >> +            dev_err(dev, "Failed to install irq:%u.\n", irq);
> >> +            return ret;
> >> +    }
> >> +
> >> +    dev_set_drvdata(dev, dc);
> >> +
> >> +    return component_add(dev, &dc_component_ops);
> >> +}
> >> +
> >> +static int dc_remove(struct platform_device *pdev)
> >> +{
> >> +    struct device *dev = &pdev->dev;
> >> +
> >> +    component_del(dev, &dc_component_ops);
> >> +
> >> +    dev_set_drvdata(dev, NULL);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +struct platform_driver dc_platform_driver = {
> >> +    .probe = dc_probe,
> >> +    .remove = dc_remove,
> >> +    .driver = {
> >> +            .name = "vs-dc",
> >> +            .of_match_table = of_match_ptr(dc_driver_dt_match),
> >> +    },
> >> +};
> >> +
> >> +MODULE_AUTHOR("StarFive Corporation");
> >> +MODULE_DESCRIPTION("VeriSilicon DC Driver");
> >> +MODULE_LICENSE("GPL");
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
> >> new file mode 100644
> >> index 000000000..8e96fd32c
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc.h
> >> @@ -0,0 +1,80 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#ifndef __VS_DC_H__
> >> +#define __VS_DC_H__
> >> +
> >> +#include <linux/clk.h>
> >> +#include <linux/mm_types.h>
> >> +#include <linux/reset.h>
> >> +
> >> +#include <drm/drm_fourcc.h>
> >> +#include <drm/drm_modes.h>
> >> +
> >> +#include "vs_crtc.h"
> >> +#include "vs_dc_hw.h"
> >> +#include "vs_plane.h"
> >> +
> >> +struct vs_dc_plane {
> >> +    enum dc_hw_plane_id id;
> >> +};
> >> +
> >> +enum vout_clk {
> >> +    CLK_VOUT_NOC_DISP = 0,
> >> +    CLK_VOUT_PIX0,
> >> +    CLK_VOUT_PIX1,
> >> +    CLK_VOUT_CORE,
> >> +    CLK_VOUT_AXI,
> >> +    CLK_VOUT_AHB,
> >> +    CLK_VOUT_HDMI_PIX,
> >> +    CLK_VOUT_SOC_PIX,
> >> +    CLK_VOUT_NUM
> >> +};
> >> +
> >> +enum rst_vout {
> >> +    RST_VOUT_AXI = 0,
> >> +    RST_VOUT_AHB,
> >> +    RST_VOUT_CORE,
> >> +    RST_VOUT_NUM
> >> +};
> >> +
> >> +struct vs_dc {
> >> +    struct vs_crtc          *crtc[DC_DISPLAY_NUM];
> >> +    struct dc_hw            hw;
> >> +    bool                    first_frame;
> >> +
> >> +    struct vs_dc_plane      planes[PLANE_NUM];
> >> +    struct clk_bulk_data    clk_vout[CLK_VOUT_NUM];
> >> +    int                     nclks;
> >> +    struct reset_control_bulk_data rst_vout[RST_VOUT_NUM];
> >> +    int                     nrsts;
> >> +};
> >> +
> >> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc);
> >> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc);
> >> +
> >> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> >> +                 struct drm_color_lut *lut, unsigned int size);
> >> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc, bool enable);
> >> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable);
> >> +void vs_dc_commit(struct vs_dc *dc);
> >> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                    struct drm_plane *drm_plane,
> >> +                    struct drm_atomic_state *drm_state);
> >> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                     struct drm_plane_state *old_state);
> >> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
> >> +                  struct drm_atomic_state *state);
> >> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                           struct drm_plane *drm_plane,
> >> +                           struct drm_atomic_state *drm_state);
> >> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                            struct drm_plane_state *old_state);
> >> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
> >> +                         struct drm_atomic_state *state);
> >> +
> >> +extern struct platform_driver dc_platform_driver;
> >> +
> >> +#endif /* __VS_DC_H__ */
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
> >> new file mode 100644
> >> index 000000000..e5acb7eb4
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
> >> @@ -0,0 +1,1959 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#include <linux/bits.h>
> >> +#include <linux/io.h>
> >> +#include <linux/media-bus-format.h>
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_blend.h>
> >> +#include <drm/drm_fourcc.h>
> >> +#include <drm/vs_drm.h>
> >> +
> >> +#include "vs_dc_hw.h"
> >> +#include "vs_type.h"
> >> +
> >> +static const u32 horkernel[] = {
> >> +    0x00000000, 0x20000000, 0x00002000, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x2b981468,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x10f00000,
> >> +    0x00002f10, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x3781087f, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x06660000, 0x0000399a, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x01470000,
> >> +    0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x00004000, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x20002000, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x27e1181f,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x14680000,
> >> +    0x00002b98, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x2f1010f0, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x0dc70000, 0x00003239, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x087f0000, 0x00003781, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x399a0666, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x3cc4033c,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x021f0000,
> >> +};
> >> +
> >> +#define H_COEF_SIZE (sizeof(horkernel) / sizeof(u32))
> >> +
> >> +static const u32 verkernel[] = {
> >> +    0x00000000, 0x20000000, 0x00002000, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x2b981468,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x10f00000,
> >> +    0x00002f10, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x3781087f, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x06660000, 0x0000399a, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x01470000,
> >> +    0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x00004000, 0x00000000,
> >> +    0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab,
> >> +    0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
> >> +    0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +};
> >> +
> >> +#define V_COEF_SIZE (sizeof(verkernel) / sizeof(u32))
> >> +
> >> +/*
> >> + * RGB 709->2020 conversion parameters
> >> + */
> >> +static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = {
> >> +    10279,  5395,   709,
> >> +    1132,   15065,  187,
> >> +    269,    1442,   14674
> >> +};
> >> +
> >> +/*
> >> + * YUV601 to RGB conversion parameters
> >> + * YUV2RGB[0]  - [8] : C0 - C8;
> >> + * YUV2RGB[9]  - [11]: D0 - D2;
> >> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> >> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> >> + */
> >> +static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> >> +    1196,   0,                      1640,   1196,
> >> +    -404,   -836,           1196,   2076,
> >> +    0,              -916224,        558336, -1202944,
> >> +    64,              940,           64,             960
> >> +};
> >> +
> >> +/*
> >> + * YUV709 to RGB conversion parameters
> >> + * YUV2RGB[0]  - [8] : C0 - C8;
> >> + * YUV2RGB[9]  - [11]: D0 - D2;
> >> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> >> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> >> + */
> >> +static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> >> +    1196,           0,              1844,   1196,
> >> +    -220,           -548,   1196,   2172,
> >> +    0,                      -1020672, 316672,  -1188608,
> >> +    64,                     940,            64,             960
> >> +};
> >> +
> >> +/*
> >> + * YUV2020 to RGB conversion parameters
> >> + * YUV2RGB[0]  - [8] : C0 - C8;
> >> + * YUV2RGB[9]  - [11]: D0 - D2;
> >> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> >> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> >> + */
> >> +static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> >> +    1196, 0, 1724, 1196,
> >> +    -192, -668, 1196, 2200,
> >> +    0, -959232, 363776, -1202944,
> >> +    64, 940, 64, 960
> >> +};
> >> +
> >> +/*
> >> + * RGB to YUV2020 conversion parameters
> >> + * RGB2YUV[0] - [8] : C0 - C8;
> >> + * RGB2YUV[9] - [11]: D0 - D2;
> >> + */
> >> +static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = {
> >> +    230,    594,    52,
> >> +    -125,  -323,    448,
> >> +    448,   -412,   -36,
> >> +    64,             512,    512
> >> +};
> >> +
> >> +/*
> >> + * Degamma table for 709 color space data.
> >> + */
> >> +static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005,
> >> +    0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024,
> >> +    0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064,
> >> +    0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce,
> >> +    0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166,
> >> +    0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230,
> >> +    0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331,
> >> +    0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c,
> >> +    0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
> >> +    0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d,
> >> +    0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999,
> >> +    0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb,
> >> +    0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65,
> >> +    0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a,
> >> +    0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b,
> >> +    0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc,
> >> +    0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e,
> >> +    0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
> >> +    0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e,
> >> +    0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf,
> >> +    0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea,
> >> +    0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f,
> >> +    0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930,
> >> +    0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f,
> >> +    0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee,
> >> +    0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde,
> >> +    0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
> >> +    0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9,
> >> +    0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406,
> >> +    0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a,
> >> +    0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576,
> >> +    0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd,
> >> +    0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
> >> +};
> >> +
> >> +/*
> >> + * Degamma table for 2020 color space data.
> >> + */
> >> +static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> >> +    0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
> >> +    0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
> >> +    0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006,
> >> +    0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a,
> >> +    0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f,
> >> +    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
> >> +    0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024,
> >> +    0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035,
> >> +    0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d,
> >> +    0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d,
> >> +    0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098,
> >> +    0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3,
> >> +    0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123,
> >> +    0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d,
> >> +    0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
> >> +    0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6,
> >> +    0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1,
> >> +    0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d,
> >> +    0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6,
> >> +    0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920,
> >> +    0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a,
> >> +    0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033,
> >> +    0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593,
> >> +    0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
> >> +    0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656,
> >> +    0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332,
> >> +    0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480,
> >> +    0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1,
> >> +    0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99,
> >> +    0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
> >> +};
> >> +
> >> +/* one is for primary plane and the other is for all overlay planes */
> >> +static const struct dc_hw_plane_reg dc_plane_reg[] = {
> >> +    {
> >> +    .y_address              = DC_FRAMEBUFFER_ADDRESS,
> >> +    .u_address              = DC_FRAMEBUFFER_U_ADDRESS,
> >> +    .v_address              = DC_FRAMEBUFFER_V_ADDRESS,
> >> +    .y_stride               = DC_FRAMEBUFFER_STRIDE,
> >> +    .u_stride               = DC_FRAMEBUFFER_U_STRIDE,
> >> +    .v_stride               = DC_FRAMEBUFFER_V_STRIDE,
> >> +    .size                   = DC_FRAMEBUFFER_SIZE,
> >> +    .top_left               = DC_FRAMEBUFFER_TOP_LEFT,
> >> +    .bottom_right   = DC_FRAMEBUFFER_BOTTOM_RIGHT,
> >> +    .scale_factor_x                 = DC_FRAMEBUFFER_SCALE_FACTOR_X,
> >> +    .scale_factor_y                 = DC_FRAMEBUFFER_SCALE_FACTOR_Y,
> >> +    .h_filter_coef_index    = DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
> >> +    .h_filter_coef_data             = DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
> >> +    .v_filter_coef_index    = DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
> >> +    .v_filter_coef_data             = DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
> >> +    .init_offset                    = DC_FRAMEBUFFER_INIT_OFFSET,
> >> +    .color_key                              = DC_FRAMEBUFFER_COLOR_KEY,
> >> +    .color_key_high                 = DC_FRAMEBUFFER_COLOR_KEY_HIGH,
> >> +    .clear_value                    = DC_FRAMEBUFFER_CLEAR_VALUE,
> >> +    .color_table_index              = DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
> >> +    .color_table_data               = DC_FRAMEBUFFER_COLOR_TABLE_DATA,
> >> +    .scale_config                   = DC_FRAMEBUFFER_SCALE_CONFIG,
> >> +    .water_mark                             = DC_FRAMEBUFFER_WATER_MARK,
> >> +    .degamma_index                  = DC_FRAMEBUFFER_DEGAMMA_INDEX,
> >> +    .degamma_data                   = DC_FRAMEBUFFER_DEGAMMA_DATA,
> >> +    .degamma_ex_data                = DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
> >> +    .src_global_color               = DC_FRAMEBUFFER_SRC_GLOBAL_COLOR,
> >> +    .dst_global_color               = DC_FRAMEBUFFER_DST_GLOBAL_COLOR,
> >> +    .blend_config                   = DC_FRAMEBUFFER_BLEND_CONFIG,
> >> +    .roi_origin                             = DC_FRAMEBUFFER_ROI_ORIGIN,
> >> +    .roi_size                               = DC_FRAMEBUFFER_ROI_SIZE,
> >> +    .yuv_to_rgb_coef0                       = DC_FRAMEBUFFER_YUVTORGB_COEF0,
> >> +    .yuv_to_rgb_coef1                       = DC_FRAMEBUFFER_YUVTORGB_COEF1,
> >> +    .yuv_to_rgb_coef2                       = DC_FRAMEBUFFER_YUVTORGB_COEF2,
> >> +    .yuv_to_rgb_coef3                       = DC_FRAMEBUFFER_YUVTORGB_COEF3,
> >> +    .yuv_to_rgb_coef4                       = DC_FRAMEBUFFER_YUVTORGB_COEF4,
> >> +    .yuv_to_rgb_coefd0                      = DC_FRAMEBUFFER_YUVTORGB_COEFD0,
> >> +    .yuv_to_rgb_coefd1                      = DC_FRAMEBUFFER_YUVTORGB_COEFD1,
> >> +    .yuv_to_rgb_coefd2                      = DC_FRAMEBUFFER_YUVTORGB_COEFD2,
> >> +    .y_clamp_bound                          = DC_FRAMEBUFFER_Y_CLAMP_BOUND,
> >> +    .uv_clamp_bound                         = DC_FRAMEBUFFER_UV_CLAMP_BOUND,
> >> +    .rgb_to_rgb_coef0                       = DC_FRAMEBUFFER_RGBTORGB_COEF0,
> >> +    .rgb_to_rgb_coef1                       = DC_FRAMEBUFFER_RGBTORGB_COEF1,
> >> +    .rgb_to_rgb_coef2                       = DC_FRAMEBUFFER_RGBTORGB_COEF2,
> >> +    .rgb_to_rgb_coef3                       = DC_FRAMEBUFFER_RGBTORGB_COEF3,
> >> +    .rgb_to_rgb_coef4                       = DC_FRAMEBUFFER_RGBTORGB_COEF4,
> >> +    },
> >> +    {
> >> +    .y_address              = DC_OVERLAY_ADDRESS,
> >> +    .u_address              = DC_OVERLAY_U_ADDRESS,
> >> +    .v_address              = DC_OVERLAY_V_ADDRESS,
> >> +    .y_stride               = DC_OVERLAY_STRIDE,
> >> +    .u_stride               = DC_OVERLAY_U_STRIDE,
> >> +    .v_stride               = DC_OVERLAY_V_STRIDE,
> >> +    .size                   = DC_OVERLAY_SIZE,
> >> +    .top_left               = DC_OVERLAY_TOP_LEFT,
> >> +    .bottom_right   = DC_OVERLAY_BOTTOM_RIGHT,
> >> +    .scale_factor_x = DC_OVERLAY_SCALE_FACTOR_X,
> >> +    .scale_factor_y = DC_OVERLAY_SCALE_FACTOR_Y,
> >> +    .h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
> >> +    .h_filter_coef_data  = DC_OVERLAY_H_FILTER_COEF_DATA,
> >> +    .v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
> >> +    .v_filter_coef_data  = DC_OVERLAY_V_FILTER_COEF_DATA,
> >> +    .init_offset             = DC_OVERLAY_INIT_OFFSET,
> >> +    .color_key                       = DC_OVERLAY_COLOR_KEY,
> >> +    .color_key_high                 = DC_OVERLAY_COLOR_KEY_HIGH,
> >> +    .clear_value             = DC_OVERLAY_CLEAR_VALUE,
> >> +    .color_table_index       = DC_OVERLAY_COLOR_TABLE_INDEX,
> >> +    .color_table_data        = DC_OVERLAY_COLOR_TABLE_DATA,
> >> +    .scale_config            = DC_OVERLAY_SCALE_CONFIG,
> >> +    .water_mark                             = DC_OVERLAY_WATER_MARK,
> >> +    .degamma_index           = DC_OVERLAY_DEGAMMA_INDEX,
> >> +    .degamma_data            = DC_OVERLAY_DEGAMMA_DATA,
> >> +    .degamma_ex_data         = DC_OVERLAY_DEGAMMA_EX_DATA,
> >> +    .src_global_color        = DC_OVERLAY_SRC_GLOBAL_COLOR,
> >> +    .dst_global_color        = DC_OVERLAY_DST_GLOBAL_COLOR,
> >> +    .blend_config            = DC_OVERLAY_BLEND_CONFIG,
> >> +    .roi_origin                             = DC_OVERLAY_ROI_ORIGIN,
> >> +    .roi_size                               = DC_OVERLAY_ROI_SIZE,
> >> +    .yuv_to_rgb_coef0                = DC_OVERLAY_YUVTORGB_COEF0,
> >> +    .yuv_to_rgb_coef1                = DC_OVERLAY_YUVTORGB_COEF1,
> >> +    .yuv_to_rgb_coef2                = DC_OVERLAY_YUVTORGB_COEF2,
> >> +    .yuv_to_rgb_coef3                = DC_OVERLAY_YUVTORGB_COEF3,
> >> +    .yuv_to_rgb_coef4                       = DC_OVERLAY_YUVTORGB_COEF4,
> >> +    .yuv_to_rgb_coefd0                      = DC_OVERLAY_YUVTORGB_COEFD0,
> >> +    .yuv_to_rgb_coefd1                      = DC_OVERLAY_YUVTORGB_COEFD1,
> >> +    .yuv_to_rgb_coefd2                      = DC_OVERLAY_YUVTORGB_COEFD2,
> >> +    .y_clamp_bound           = DC_OVERLAY_Y_CLAMP_BOUND,
> >> +    .uv_clamp_bound          = DC_OVERLAY_UV_CLAMP_BOUND,
> >> +    .rgb_to_rgb_coef0                = DC_OVERLAY_RGBTORGB_COEF0,
> >> +    .rgb_to_rgb_coef1                = DC_OVERLAY_RGBTORGB_COEF1,
> >> +    .rgb_to_rgb_coef2                = DC_OVERLAY_RGBTORGB_COEF2,
> >> +    .rgb_to_rgb_coef3                = DC_OVERLAY_RGBTORGB_COEF3,
> >> +    .rgb_to_rgb_coef4                = DC_OVERLAY_RGBTORGB_COEF4,
> >> +    },
> >> +};
> >> +
> >> +static const u32 primary_overlay_format0[] = {
> >> +    DRM_FORMAT_XRGB4444,
> >> +    DRM_FORMAT_XBGR4444,
> >> +    DRM_FORMAT_RGBX4444,
> >> +    DRM_FORMAT_BGRX4444,
> >> +    DRM_FORMAT_ARGB4444,
> >> +    DRM_FORMAT_ABGR4444,
> >> +    DRM_FORMAT_RGBA4444,
> >> +    DRM_FORMAT_BGRA4444,
> >> +    DRM_FORMAT_XRGB1555,
> >> +    DRM_FORMAT_XBGR1555,
> >> +    DRM_FORMAT_RGBX5551,
> >> +    DRM_FORMAT_BGRX5551,
> >> +    DRM_FORMAT_ARGB1555,
> >> +    DRM_FORMAT_ABGR1555,
> >> +    DRM_FORMAT_RGBA5551,
> >> +    DRM_FORMAT_BGRA5551,
> >> +    DRM_FORMAT_RGB565,
> >> +    DRM_FORMAT_BGR565,
> >> +    DRM_FORMAT_XRGB8888,
> >> +    DRM_FORMAT_XBGR8888,
> >> +    DRM_FORMAT_RGBX8888,
> >> +    DRM_FORMAT_BGRX8888,
> >> +    DRM_FORMAT_ARGB8888,
> >> +    DRM_FORMAT_ABGR8888,
> >> +    DRM_FORMAT_RGBA8888,
> >> +    DRM_FORMAT_BGRA8888,
> >> +    DRM_FORMAT_ARGB2101010,
> >> +    DRM_FORMAT_ABGR2101010,
> >> +    DRM_FORMAT_RGBA1010102,
> >> +    DRM_FORMAT_BGRA1010102,
> >> +    DRM_FORMAT_YUYV,
> >> +    DRM_FORMAT_YVYU,
> >> +    DRM_FORMAT_UYVY,
> >> +    DRM_FORMAT_VYUY,
> >> +    DRM_FORMAT_YVU420,
> >> +    DRM_FORMAT_YUV420,
> >> +    DRM_FORMAT_NV12,
> >> +    DRM_FORMAT_NV21,
> >> +    DRM_FORMAT_NV16,
> >> +    DRM_FORMAT_NV61,
> >> +    DRM_FORMAT_P010,
> >> +};
> >> +
> >> +static const u32 primary_overlay_format1[] = {
> >> +    DRM_FORMAT_XRGB8888,
> >> +    DRM_FORMAT_XBGR8888,
> >> +    DRM_FORMAT_RGBX8888,
> >> +    DRM_FORMAT_BGRX8888,
> >> +    DRM_FORMAT_ARGB8888,
> >> +    DRM_FORMAT_ABGR8888,
> >> +    DRM_FORMAT_RGBA8888,
> >> +    DRM_FORMAT_BGRA8888,
> >> +    DRM_FORMAT_ARGB2101010,
> >> +    DRM_FORMAT_ABGR2101010,
> >> +    DRM_FORMAT_RGBA1010102,
> >> +    DRM_FORMAT_BGRA1010102,
> >> +    DRM_FORMAT_NV12,
> >> +    DRM_FORMAT_NV21,
> >> +    DRM_FORMAT_YUV444,
> >> +};
> >> +
> >> +static const u32 cursor_formats[] = {
> >> +    DRM_FORMAT_ARGB8888
> >> +};
> >> +
> >> +static const u64 format_modifier0[] = {
> >> +    DRM_FORMAT_MOD_LINEAR,
> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8),
> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8),
> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X8),
> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X4),
> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4),
> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8),
> >> +    DRM_FORMAT_MOD_INVALID
> >> +};
> >> +
> >> +static const u64 secondary_format_modifiers[] = {
> >> +    DRM_FORMAT_MOD_LINEAR,
> >> +    DRM_FORMAT_MOD_INVALID
> >> +};
> >> +
> >> +#define FRAC_16_16(mult, div)        (((mult) << 16) / (div))
> >> +
> >> +static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = {
> >> +    {
> >> +            /* DC_REV_0 */
> >> +            {
> >> +            .name                   = "Primary",
> >
> > I'd say that the indentation is strange here.
> > It might be more scalable to split a single array into per-generation
> > data structures and reference them directly from the info struct.
> >
> >> +            .id                             = PRIMARY_PLANE_0,
> >> +            .type                   = DRM_PLANE_TYPE_PRIMARY,
> >> +            .num_formats    = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats                = primary_overlay_format0,
> >> +            .num_modifiers  = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers              = format_modifier0,
> >> +            .min_width              = 0,
> >> +            .min_height             = 0,
> >> +            .max_width              = 4096,
> >> +            .max_height             = 4096,
> >> +            .rotation               = DRM_MODE_ROTATE_0 |
> >> +                                                    DRM_MODE_ROTATE_90 |
> >> +                                                    DRM_MODE_ROTATE_180 |
> >> +                                                    DRM_MODE_ROTATE_270 |
> >> +                                                    DRM_MODE_REFLECT_X |
> >> +                                                    DRM_MODE_REFLECT_Y,
> >> +            .blend_mode             = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                                    BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                                    BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 0,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name                   = "Overlay",
> >> +            .id                             = OVERLAY_PLANE_0,
> >> +            .type                   = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats    = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats                = primary_overlay_format0,
> >> +            .num_modifiers  = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers              = format_modifier0,
> >> +            .min_width              = 0,
> >> +            .min_height             = 0,
> >> +            .max_width              = 4096,
> >> +            .max_height             = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 1,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name                   = "Overlay_1",
> >> +            .id                             = OVERLAY_PLANE_1,
> >> +            .type                   = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats    = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats                = primary_overlay_format0,
> >> +            .num_modifiers  = ARRAY_SIZE(secondary_format_modifiers),
> >> +            .modifiers              = secondary_format_modifiers,
> >> +            .min_width              = 0,
> >> +            .min_height             = 0,
> >> +            .max_width              = 4096,
> >> +            .max_height             = 4096,
> >> +            .rotation               = 0,
> >> +            .blend_mode             = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                                    BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                                    BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 2,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Primary_1",
> >> +            .id                             = PRIMARY_PLANE_1,
> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats         = primary_overlay_format0,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 3,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay_2",
> >> +            .id                             = OVERLAY_PLANE_2,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats         = primary_overlay_format0,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 4,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay_3",
> >> +            .id                     = OVERLAY_PLANE_3,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats         = primary_overlay_format0,
> >> +            .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
> >> +            .modifiers       = secondary_format_modifiers,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = 0,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 5,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Cursor",
> >> +            .id                             = CURSOR_PLANE_0,
> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
> >> +            .formats         = cursor_formats,
> >> +            .num_modifiers = 0,
> >> +            .modifiers       = NULL,
> >> +            .min_width       = 32,
> >> +            .min_height  = 32,
> >> +            .max_width       = 64,
> >> +            .max_height  = 64,
> >> +            .rotation        = 0,
> >> +            .degamma_size = 0,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 255,
> >> +            .watermark       = false,
> >> +            .color_mgmt  = false,
> >> +            .roi             = false,
> >> +            },
> >> +            {
> >> +            .name            = "Cursor_1",
> >> +            .id                             = CURSOR_PLANE_1,
> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
> >> +            .formats         = cursor_formats,
> >> +            .num_modifiers = 0,
> >> +            .modifiers       = NULL,
> >> +            .min_width       = 32,
> >> +            .min_height  = 32,
> >> +            .max_width       = 64,
> >> +            .max_height  = 64,
> >> +            .rotation        = 0,
> >> +            .degamma_size = 0,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 255,
> >> +            .watermark       = false,
> >> +            .color_mgmt  = false,
> >> +            .roi             = false,
> >> +            },
> >> +    },
> >> +    {
> >> +            /* DC_REV_1 */
> >> +            {
> >> +            .name            = "Primary",
> >> +            .id                             = PRIMARY_PLANE_0,
> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats         = primary_overlay_format0,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 0,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay",
> >> +            .id                             = OVERLAY_PLANE_0,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats         = primary_overlay_format0,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 1,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Primary_1",
> >> +            .id                             = PRIMARY_PLANE_1,
> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats         = primary_overlay_format0,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 2,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay_2",
> >> +            .id                             = OVERLAY_PLANE_2,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats         = primary_overlay_format0,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 3,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Cursor",
> >> +            .id                             = CURSOR_PLANE_0,
> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
> >> +            .formats         = cursor_formats,
> >> +            .num_modifiers = 0,
> >> +            .modifiers       = NULL,
> >> +            .min_width       = 32,
> >> +            .min_height  = 32,
> >> +            .max_width       = 64,
> >> +            .max_height  = 64,
> >> +            .rotation        = 0,
> >> +            .degamma_size = 0,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 255,
> >> +            .watermark       = false,
> >> +            .color_mgmt  = false,
> >> +            .roi             = false,
> >> +            },
> >> +            {
> >> +            .name            = "Cursor_1",
> >> +            .id                             = CURSOR_PLANE_1,
> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
> >> +            .formats         = cursor_formats,
> >> +            .num_modifiers = 0,
> >> +            .modifiers       = NULL,
> >> +            .min_width       = 32,
> >> +            .min_height  = 32,
> >> +            .max_width       = 64,
> >> +            .max_height  = 64,
> >> +            .rotation        = 0,
> >> +            .degamma_size = 0,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 255,
> >> +            .watermark       = false,
> >> +            .color_mgmt  = false,
> >> +            .roi             = false,
> >> +            },
> >> +    },
> >> +    {
> >> +            /* DC_REV_2 */
> >> +            {
> >> +            .name            = "Primary",
> >> +            .id                             = PRIMARY_PLANE_0,
> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
> >> +            .formats         = primary_overlay_format1,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 0,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay",
> >> +            .id                             = OVERLAY_PLANE_0,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
> >> +            .formats         = primary_overlay_format1,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 1,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay_1",
> >> +            .id                             = OVERLAY_PLANE_1,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
> >> +            .formats         = primary_overlay_format1,
> >> +            .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
> >> +            .modifiers       = secondary_format_modifiers,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = 0,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 2,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Primary_1",
> >> +            .id                             = PRIMARY_PLANE_1,
> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
> >> +            .formats         = primary_overlay_format1,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 3,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay_2",
> >> +            .id                             = OVERLAY_PLANE_2,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
> >> +            .formats         = primary_overlay_format1,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 4,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay_3",
> >> +            .id                             = OVERLAY_PLANE_3,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
> >> +            .formats         = primary_overlay_format1,
> >> +            .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
> >> +            .modifiers       = secondary_format_modifiers,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = 0,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 5,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Cursor",
> >> +            .id                             = CURSOR_PLANE_0,
> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
> >> +            .formats         = cursor_formats,
> >> +            .num_modifiers = 0,
> >> +            .modifiers       = NULL,
> >> +            .min_width       = 32,
> >> +            .min_height  = 32,
> >> +            .max_width       = 64,
> >> +            .max_height  = 64,
> >> +            .rotation        = 0,
> >> +            .degamma_size = 0,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 255,
> >> +            .watermark       = false,
> >> +            .color_mgmt  = false,
> >> +            .roi             = false,
> >> +            },
> >> +            {
> >> +            .name            = "Cursor_1",
> >> +            .id                             = CURSOR_PLANE_1,
> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
> >> +            .formats         = cursor_formats,
> >> +            .num_modifiers = 0,
> >> +            .modifiers       = NULL,
> >> +            .min_width       = 32,
> >> +            .min_height  = 32,
> >> +            .max_width       = 64,
> >> +            .max_height  = 64,
> >> +            .rotation        = 0,
> >> +            .degamma_size = 0,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 255,
> >> +            .watermark       = false,
> >> +            .color_mgmt  = false,
> >> +            .roi             = false,
> >> +            },
> >> +    },
> >> +};
> >> +
> >> +static const struct vs_dc_info dc_info[] = {
> >
> > Again, please split into per-generation instances.
> >
> >> +    {
> >> +            /* DC_REV_0 */
> >> +            .name                   = "DC8200",
> >> +            .panel_num              = 2,
> >> +            .plane_num              = 8,
> >> +            .planes                 = dc_hw_planes[DC_REV_0],
> >> +            .layer_num              = 6,
> >> +            .max_bpc                = 10,
> >> +            .color_formats  = DRM_COLOR_FORMAT_RGB444 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR444 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR422 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR420,
> >> +            .gamma_size             = GAMMA_EX_SIZE,
> >> +            .gamma_bits             = 12,
> >> +            .pitch_alignment        = 128,
> >> +            .pipe_sync              = false,
> >> +            .background             = true,
> >> +            .panel_sync             = true,
> >> +            .cap_dec                = true,
> >> +    },
> >> +    {
> >> +            /* DC_REV_1 */
> >> +            .name                   = "DC8200",
> >> +            .panel_num              = 2,
> >> +            .plane_num              = 6,
> >> +            .planes                 = dc_hw_planes[DC_REV_1],
> >> +            .layer_num              = 4,
> >> +            .max_bpc                = 10,
> >> +            .color_formats  = DRM_COLOR_FORMAT_RGB444 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR444 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR422 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR420,
> >> +            .gamma_size             = GAMMA_EX_SIZE,
> >> +            .gamma_bits             = 12,
> >> +            .pitch_alignment        = 128,
> >> +            .pipe_sync              = false,
> >> +            .background             = true,
> >> +            .panel_sync             = true,
> >> +            .cap_dec                = true,
> >> +    },
> >> +    {
> >> +            /* DC_REV_2 */
> >> +            .name                   = "DC8200",
> >> +            .panel_num              = 2,
> >> +            .plane_num              = 8,
> >> +            .planes                 = dc_hw_planes[DC_REV_2],
> >> +            .layer_num              = 6,
> >> +            .max_bpc                = 10,
> >> +            .color_formats  = DRM_COLOR_FORMAT_RGB444 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR444 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR422 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR420,
> >> +            .gamma_size             = GAMMA_EX_SIZE,
> >> +            .gamma_bits             = 12,
> >> +            .pitch_alignment        = 128,
> >> +            .pipe_sync              = false,
> >> +            .background             = true,
> >> +            .panel_sync             = true,
> >> +            .cap_dec                = false,
> >> +    },
> >> +};
> >> +
> >> +static const struct dc_hw_funcs hw_func;
> >> +
> >> +static inline u32 hi_read(struct dc_hw *hw, u32 reg)
> >> +{
> >> +    return readl(hw->hi_base + reg);
> >> +}
> >> +
> >> +static inline void hi_write(struct dc_hw *hw, u32 reg, u32 value)
> >> +{
> >> +    writel(value, hw->hi_base + reg);
> >> +}
> >> +
> >> +static inline void dc_write(struct dc_hw *hw, u32 reg, u32 value)
> >> +{
> >> +    writel(value, hw->reg_base + reg - DC_REG_BASE);
> >> +}
> >> +
> >> +static inline u32 dc_read(struct dc_hw *hw, u32 reg)
> >> +{
> >> +    u32 value = readl(hw->reg_base + reg - DC_REG_BASE);
> >> +
> >> +    return value;
> >> +}
> >> +
> >> +static inline void dc_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear)
> >> +{
> >> +    u32 value = dc_read(hw, reg);
> >> +
> >> +    value &= ~clear;
> >> +    value |= set;
> >> +    dc_write(hw, reg, value);
> >> +}
> >> +
> >> +static void load_default_filter(struct dc_hw *hw,
> >> +                            const struct dc_hw_plane_reg *reg, u32 offset)
> >> +{
> >> +    u8 i;
> >> +
> >> +    dc_write(hw, reg->scale_config + offset, 0x33);
> >> +    dc_write(hw, reg->init_offset + offset, 0x80008000);
> >> +    dc_write(hw, reg->h_filter_coef_index + offset, 0x00);
> >> +    for (i = 0; i < H_COEF_SIZE; i++)
> >> +            dc_write(hw, reg->h_filter_coef_data + offset, horkernel[i]);
> >> +
> >> +    dc_write(hw, reg->v_filter_coef_index + offset, 0x00);
> >> +    for (i = 0; i < V_COEF_SIZE; i++)
> >> +            dc_write(hw, reg->v_filter_coef_data + offset, verkernel[i]);
> >> +}
> >> +
> >> +static void load_rgb_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
> >> +                        u32 offset, u16 *table)
> >> +{
> >> +    dc_write(hw, reg->rgb_to_rgb_coef0 + offset, table[0] | (table[1] << 16));
> >> +    dc_write(hw, reg->rgb_to_rgb_coef1 + offset, table[2] | (table[3] << 16));
> >> +    dc_write(hw, reg->rgb_to_rgb_coef2 + offset, table[4] | (table[5] << 16));
> >> +    dc_write(hw, reg->rgb_to_rgb_coef3 + offset, table[6] | (table[7] << 16));
> >> +    dc_write(hw, reg->rgb_to_rgb_coef4 + offset, table[8]);
> >> +}
> >> +
> >> +static void load_yuv_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
> >> +                        u32 offset, s32 *table)
> >> +{
> >> +    dc_write(hw, reg->yuv_to_rgb_coef0 + offset,
> >> +             (0xFFFF & table[0]) | (table[1] << 16));
> >> +    dc_write(hw, reg->yuv_to_rgb_coef1 + offset,
> >> +             (0xFFFF & table[2]) | (table[3] << 16));
> >> +    dc_write(hw, reg->yuv_to_rgb_coef2 + offset,
> >> +             (0xFFFF & table[4]) | (table[5] << 16));
> >> +    dc_write(hw, reg->yuv_to_rgb_coef3 + offset,
> >> +             (0xFFFF & table[6]) | (table[7] << 16));
> >> +    dc_write(hw, reg->yuv_to_rgb_coef4 + offset, table[8]);
> >> +    dc_write(hw, reg->yuv_to_rgb_coefd0 + offset, table[9]);
> >> +    dc_write(hw, reg->yuv_to_rgb_coefd1 + offset, table[10]);
> >> +    dc_write(hw, reg->yuv_to_rgb_coefd2 + offset, table[11]);
> >> +    dc_write(hw, reg->y_clamp_bound + offset, table[12] | (table[13] << 16));
> >> +    dc_write(hw, reg->uv_clamp_bound + offset, table[14] | (table[15] << 16));
> >> +}
> >> +
> >> +static void load_rgb_to_yuv(struct dc_hw *hw, u32 offset, s16 *table)
> >> +{
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF0 + offset,
> >> +             table[0] | (table[1] << 16));
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF1 + offset,
> >> +             table[2] | (table[3] << 16));
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF2 + offset,
> >> +             table[4] | (table[5] << 16));
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF3 + offset,
> >> +             table[6] | (table[7] << 16));
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF4 + offset, table[8]);
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD0 + offset, table[9]);
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD1 + offset, table[10]);
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD2 + offset, table[11]);
> >> +}
> >> +
> >> +static bool is_rgb(enum dc_hw_color_format format)
> >> +{
> >> +    switch (format) {
> >> +    case FORMAT_X4R4G4B4:
> >> +    case FORMAT_A4R4G4B4:
> >> +    case FORMAT_X1R5G5B5:
> >> +    case FORMAT_A1R5G5B5:
> >> +    case FORMAT_R5G6B5:
> >> +    case FORMAT_X8R8G8B8:
> >> +    case FORMAT_A8R8G8B8:
> >> +    case FORMAT_A2R10G10B10:
> >> +            return true;
> >> +    default:
> >> +            return false;
> >> +    }
> >> +}
> >> +
> >> +static void load_degamma_table(struct dc_hw *hw,
> >> +                           const struct dc_hw_plane_reg *reg,
> >> +                           u32 offset, u16 *table)
> >> +{
> >> +    u16 i;
> >> +    u32 value;
> >> +
> >> +    dc_write(hw, reg->degamma_index + offset, 0);
> >> +
> >> +    for (i = 0; i < DEGAMMA_SIZE; i++) {
> >> +            value = table[i] | (table[i] << 16);
> >> +            dc_write(hw, reg->degamma_data + offset, value);
> >> +            dc_write(hw, reg->degamma_ex_data + offset, table[i]);
> >> +    }
> >> +}
> >> +
> >> +static u32 get_addr_offset(u32 id)
> >> +{
> >> +    u32 offset = 0;
> >> +
> >> +    switch (id) {
> >> +    case PRIMARY_PLANE_1:
> >> +    case OVERLAY_PLANE_1:
> >> +            offset = 0x04;
> >> +            break;
> >> +    case OVERLAY_PLANE_2:
> >> +            offset = 0x08;
> >> +            break;
> >> +    case OVERLAY_PLANE_3:
> >> +            offset = 0x0C;
> >> +            break;
> >> +    default:
> >> +            break;
> >> +    }
> >> +
> >> +    return offset;
> >> +}
> >> +
> >> +int dc_hw_init(struct dc_hw *hw)
> >> +{
> >> +    u8 i, id, panel_num, layer_num;
> >> +    u32 offset;
> >> +    u32 revision = hi_read(hw, DC_HW_REVISION);
> >> +    u32 cid = hi_read(hw, DC_HW_CHIP_CID);
> >> +    const struct dc_hw_plane_reg *reg;
> >> +
> >> +    switch (revision) {
> >> +    case 0x5720:
> >> +            hw->rev = DC_REV_0;
> >> +            break;
> >> +    case 0x5721:
> >> +            switch (cid) {
> >> +            case 0x30B:
> >> +                    hw->rev = DC_REV_1;
> >> +                    break;
> >> +            case 0x310:
> >> +                    hw->rev = DC_REV_2;
> >> +                    break;
> >> +            default:
> >> +                    hw->rev = DC_REV_0;
> >> +                    break;
> >> +            }
> >> +            break;
> >> +    default:
> >> +            return -ENXIO;
> >> +    }
> >
> > We (drm/msm) had code like this for a while (and autodetection of the HW
> > ID). And nowadays we are migrating to of_device_get_match_data and
> > per-SoC compat names. It is just easier to handle, it allows one to
> > specify device more precisely and also it simplifies DT schema.
> >
> >> +
> >> +    hw->info = (struct vs_dc_info *)&dc_info[hw->rev];
> >> +    hw->func = (struct dc_hw_funcs *)&hw_func;
> >> +
> >> +    layer_num = hw->info->layer_num;
> >> +    for (i = 0; i < layer_num; i++) {
> >> +            id = hw->info->planes[i].id;
> >> +            offset = get_addr_offset(id);
> >> +            if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
> >> +                    reg = &dc_plane_reg[0];
> >> +            else
> >> +                    reg = &dc_plane_reg[1];
> >> +
> >> +            load_default_filter(hw, reg, offset);
> >> +            load_rgb_to_rgb(hw, reg, offset, RGB2RGB);
> >> +    }
> >> +
> >> +    panel_num = hw->info->panel_num;
> >> +    for (i = 0; i < panel_num; i++) {
> >> +            offset = i << 2;
> >> +
> >> +            load_rgb_to_yuv(hw, offset, RGB2YUV);
> >> +            dc_write(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0x111);
> >> +
> >> +            offset = i ? DC_CURSOR_OFFSET : 0;
> >> +            dc_write(hw, DC_CURSOR_BACKGROUND + offset, 0x00FFFFFF);
> >> +            dc_write(hw, DC_CURSOR_FOREGROUND + offset, 0x00AAAAAA);
> >> +    }
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +void dc_hw_deinit(struct dc_hw *hw)
> >> +{
> >> +    /* Nothing to do */
> >> +}
> >> +
> >> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
> >> +                    struct dc_hw_fb *fb, struct dc_hw_scale *scale,
> >> +                    struct dc_hw_position *pos, struct dc_hw_blend *blend)
> >> +{
> >> +    struct dc_hw_plane *plane = &hw->plane[id];
> >> +
> >> +    if (plane) {
> >> +            if (fb) {
> >> +                    if (!fb->enable)
> >> +                            plane->fb.enable = false;
> >> +                    else
> >> +                            memcpy(&plane->fb, fb,
> >> +                                   sizeof(*fb) - sizeof(fb->dirty));
> >> +                    plane->fb.dirty = true;
> >> +            }
> >> +            if (scale) {
> >> +                    memcpy(&plane->scale, scale,
> >> +                           sizeof(*scale) - sizeof(scale->dirty));
> >> +                    plane->scale.dirty = true;
> >> +            }
> >> +            if (pos) {
> >> +                    memcpy(&plane->pos, pos,
> >> +                           sizeof(*pos) - sizeof(pos->dirty));
> >> +                    plane->pos.dirty = true;
> >> +            }
> >> +            if (blend) {
> >> +                    memcpy(&plane->blend, blend,
> >> +                           sizeof(*blend) - sizeof(blend->dirty));
> >> +                    plane->blend.dirty = true;
> >> +            }
> >> +    }
> >> +}
> >> +
> >> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode)
> >> +{
> >> +    struct dc_hw_plane *plane = &hw->plane[id];
> >> +
> >> +    if (plane) {
> >> +            if (hw->info->planes[id].degamma_size) {
> >> +                    plane->degamma.mode = mode;
> >> +                    plane->degamma.dirty = true;
> >> +            } else {
> >> +                    plane->degamma.dirty = false;
> >> +            }
> >> +    }
> >> +}
> >> +
> >> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi)
> >> +{
> >> +    struct dc_hw_plane *plane = &hw->plane[id];
> >> +
> >> +    if (plane) {
> >> +            memcpy(&plane->roi, roi, sizeof(*roi) - sizeof(roi->dirty));
> >> +            plane->roi.dirty = true;
> >> +    }
> >> +}
> >> +
> >> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
> >> +                       struct dc_hw_colorkey *colorkey)
> >> +{
> >> +    struct dc_hw_plane *plane = &hw->plane[id];
> >> +
> >> +    if (plane) {
> >> +            memcpy(&plane->colorkey, colorkey,
> >> +                   sizeof(*colorkey) - sizeof(colorkey->dirty));
> >> +            plane->colorkey.dirty = true;
> >> +    }
> >> +}
> >> +
> >> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos)
> >> +{
> >> +    memcpy(&hw->qos, qos, sizeof(*qos) - sizeof(qos->dirty));
> >> +    hw->qos.dirty = true;
> >> +}
> >> +
> >> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor)
> >> +{
> >> +    memcpy(&hw->cursor[id], cursor, sizeof(*cursor) - sizeof(cursor->dirty));
> >> +    hw->cursor[id].dirty = true;
> >> +}
> >> +
> >> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
> >> +                    u16 r, u16 g, u16 b)
> >> +{
> >> +    if (index >= hw->info->gamma_size)
> >> +            return;
> >> +
> >> +    hw->gamma[id].gamma[index][0] = r;
> >> +    hw->gamma[id].gamma[index][1] = g;
> >> +    hw->gamma[id].gamma[index][2] = b;
> >> +    hw->gamma[id].dirty = true;
> >> +}
> >> +
> >> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable)
> >> +{
> >> +    hw->gamma[id].enable = enable;
> >> +    hw->gamma[id].dirty = true;
> >> +}
> >> +
> >> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display)
> >> +{
> >> +    u8 id = display->id;
> >> +
> >> +    memcpy(&hw->display[id], display, sizeof(*display));
> >> +
> >> +    hw->func->display(hw, display);
> >> +}
> >> +
> >> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable)
> >> +{
> >> +    if (enable)
> >> +            hi_write(hw, AQ_INTR_ENBL, 0xFFFFFFFF);
> >> +    else
> >> +            hi_write(hw, AQ_INTR_ENBL, 0);
> >> +}
> >> +
> >> +u32 dc_hw_get_interrupt(struct dc_hw *hw)
> >> +{
> >> +    return hi_read(hw, AQ_INTR_ACKNOWLEDGE);
> >> +}
> >> +
> >> +bool dc_hw_check_underflow(struct dc_hw *hw)
> >> +{
> >> +    return dc_read(hw, DC_FRAMEBUFFER_CONFIG) & BIT(5);
> >> +}
> >> +
> >> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable)
> >> +{
> >> +    u32 i, offset;
> >> +    u8 id, layer_num = hw->info->layer_num;
> >> +    u8 panel_num = hw->info->panel_num;
> >> +
> >> +    for (i = 0; i < layer_num; i++) {
> >> +            id = hw->info->planes[i].id;
> >> +            offset = get_addr_offset(id);
> >> +            if (enable) {
> >> +                    if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, BIT(12), 0);
> >> +                    else
> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, BIT(31), 0);
> >> +            } else {
> >> +                    if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, 0, BIT(12));
> >> +                    else
> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, 0, BIT(31));
> >> +            }
> >> +    }
> >> +
> >> +    for (i = 0; i < panel_num; i++) {
> >> +            offset = i << 2;
> >> +            if (enable)
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, BIT(0));
> >> +            else
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, BIT(0), 0);
> >> +    }
> >> +}
> >> +
> >> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id)
> >> +{
> >> +    if (out <= OUT_DP)
> >> +            hw->out[id] = out;
> >> +}
> >> +
> >> +static void gamma_ex_commit(struct dc_hw *hw)
> >> +{
> >> +    u8 panel_num = hw->info->panel_num;
> >> +    u16 i, j;
> >> +    u32 value;
> >> +
> >> +    for (j = 0; j < panel_num; j++) {
> >> +            if (hw->gamma[j].dirty) {
> >> +                    if (hw->gamma[j].enable) {
> >> +                            dc_write(hw, DC_DISPLAY_GAMMA_EX_INDEX + (j << 2), 0x00);
> >> +                            for (i = 0; i < GAMMA_EX_SIZE; i++) {
> >> +                                    value = hw->gamma[j].gamma[i][2] |
> >> +                                            (hw->gamma[j].gamma[i][1] << 12);
> >> +                                    dc_write(hw, DC_DISPLAY_GAMMA_EX_DATA + (j << 2), value);
> >> +                                    dc_write(hw, DC_DISPLAY_GAMMA_EX_ONE_DATA + (j << 2),
> >> +                                             hw->gamma[j].gamma[i][0]);
> >> +                            }
> >> +                            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
> >> +                                         BIT(13), 0);
> >> +                    } else {
> >> +                            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
> >> +                                         0, BIT(13));
> >> +                    }
> >> +                    hw->gamma[j].dirty = false;
> >> +            }
> >> +    }
> >> +}
> >> +
> >> +static void plane_commit(struct dc_hw *hw)
> >> +{
> >> +    struct dc_hw_plane *plane;
> >> +    const struct dc_hw_plane_reg *reg;
> >> +    bool primary = false;
> >> +    u8 id, layer_num = hw->info->layer_num;
> >> +    u32 i, offset;
> >> +
> >> +    for (i = 0; i < layer_num; i++) {
> >> +            plane = &hw->plane[i];
> >> +            id = hw->info->planes[i].id;
> >> +            offset = get_addr_offset(id);
> >> +            if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
> >> +                    reg = &dc_plane_reg[0];
> >> +                    primary = true;
> >> +            } else {
> >> +                    reg = &dc_plane_reg[1];
> >> +                    primary = false;
> >> +            }
> >
> > Can you please move register defines to some internal hw_plane
> > structure. These details are known at the time of creation of the plane.
> > There is no need to determine them again and again.
> >
> >> +
> >> +            if (plane->fb.dirty) {
> >> +                    if (plane->fb.enable) {
> >
> > if (plane->fb.dirty && plane->fb.enable) will save you one level of
> > indentation.
> >
> > However it might be better to use existing DRM flags instead of
> > inventing your own flags.
> >
> >> +                            dc_write(hw, reg->y_address + offset,
> >> +                                     plane->fb.y_address);
> >> +                            dc_write(hw, reg->u_address + offset,
> >> +                                     plane->fb.u_address);
> >> +                            dc_write(hw, reg->v_address + offset,
> >> +                                     plane->fb.v_address);
> >> +                            dc_write(hw, reg->y_stride + offset,
> >> +                                     plane->fb.y_stride);
> >> +                            dc_write(hw, reg->u_stride + offset,
> >> +                                     plane->fb.u_stride);
> >> +                            dc_write(hw, reg->v_stride + offset,
> >> +                                     plane->fb.v_stride);
> >> +                            dc_write(hw, reg->size + offset,
> >> +                                     plane->fb.width |
> >> +                                     (plane->fb.height << 15));
> >> +                            dc_write(hw, reg->water_mark + offset,
> >> +                                     plane->fb.water_mark);
> >> +
> >> +                            if (plane->fb.clear_enable)
> >> +                                    dc_write(hw, reg->clear_value + offset,
> >> +                                             plane->fb.clear_value);
> >> +                    }
> >> +
> >> +                    if (primary) {
> >
> > You have two kinds of planes. Primary and overlays. Could you please
> > split this code into two separate functions and define two vtables, one
> > for primary planes and one for overlays. There is no need to stick
> > everything into a single funcion that handles everything.
> >
> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
> >> +                                         (plane->fb.format << 26) |
> >> +                                         (plane->fb.uv_swizzle << 25) |
> >> +                                         (plane->fb.swizzle << 23) |
> >> +                                         (plane->fb.tile_mode << 17) |
> >> +                                         (plane->fb.yuv_color_space << 14) |
> >> +                                         (plane->fb.rotation << 11) |
> >> +                                         (plane->fb.clear_enable << 8),
> >> +                                         (0x1F << 26) |
> >> +                                         BIT(25) |
> >> +                                         (0x03 << 23) |
> >> +                                         (0x1F << 17) |
> >> +                                         (0x07 << 14) |
> >> +                                         (0x07 << 11) |
> >> +                                         BIT(8));
> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                         (plane->fb.dec_enable << 1) |
> >> +                                         (plane->fb.enable << 13) |
> >> +                                         (plane->fb.zpos << 16) |
> >> +                                         (plane->fb.display_id << 19),
> >> +                                         BIT(1) | BIT(13) | (0x07 << 16) | BIT(19));
> >
> > It looks like this code can enjoy defining names for all the magic
> > values. Otherwise it is impossible to support it.
> >
> >> +                    } else {
> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> >> +                                         (plane->fb.dec_enable << 27) |
> >> +                                         (plane->fb.clear_enable << 25) |
> >> +                                         (plane->fb.enable << 24) |
> >> +                                         (plane->fb.format << 16) |
> >> +                                         (plane->fb.uv_swizzle << 15) |
> >> +                                         (plane->fb.swizzle << 13) |
> >> +                                         (plane->fb.tile_mode << 8) |
> >> +                                         (plane->fb.yuv_color_space << 5) |
> >> +                                         (plane->fb.rotation << 2),
> >> +                                         BIT(27) |
> >> +                                         BIT(25) |
> >> +                                         BIT(24) |
> >> +                                         (0x1F << 16) |
> >> +                                         BIT(15) |
> >> +                                         (0x03 << 13) |
> >> +                                         (0x1F << 8) |
> >> +                                         (0x07 << 5) |
> >> +                                         (0x07 << 2));
> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset,
> >> +                                         plane->fb.zpos | (plane->fb.display_id << 3),
> >> +                                         0x07 | BIT(3));
> >> +                    }
> >> +                    plane->fb.dirty = false;
> >> +            }
> >> +
> >> +            if (plane->scale.dirty) {
> >> +                    if (plane->scale.enable) {
> >> +                            dc_write(hw, reg->scale_factor_x + offset,
> >> +                                     plane->scale.scale_factor_x);
> >> +                            dc_write(hw, reg->scale_factor_y + offset,
> >> +                                     plane->scale.scale_factor_y);
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_FRAMEBUFFER_CONFIG + offset,
> >> +                                                 BIT(22), 0);
> >> +                            else
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_OVERLAY_SCALE_CONFIG + offset,
> >> +                                                 BIT(8), 0);
> >> +                    } else {
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_FRAMEBUFFER_CONFIG + offset,
> >> +                                                 0, BIT(22));
> >> +                            else
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_OVERLAY_SCALE_CONFIG + offset,
> >> +                                                 0, BIT(8));
> >> +                    }
> >> +                    plane->scale.dirty = false;
> >> +            }
> >> +
> >> +            if (plane->pos.dirty) {
> >> +                    dc_write(hw, reg->top_left + offset,
> >> +                             plane->pos.start_x |
> >> +                             (plane->pos.start_y << 15));
> >> +                    dc_write(hw, reg->bottom_right + offset,
> >> +                             plane->pos.end_x |
> >> +                             (plane->pos.end_y << 15));
> >> +                    plane->pos.dirty = false;
> >> +            }
> >> +
> >> +            if (plane->blend.dirty) {
> >> +                    dc_write(hw, reg->src_global_color + offset,
> >> +                             plane->blend.alpha << 24);
> >> +                    dc_write(hw, reg->dst_global_color + offset,
> >> +                             plane->blend.alpha << 24);
> >> +                    switch (plane->blend.blend_mode) {
> >> +                    case BLEND_PREMULTI:
> >> +                            dc_write(hw, reg->blend_config + offset, 0x3450);
> >> +                            break;
> >> +                    case BLEND_COVERAGE:
> >> +                            dc_write(hw, reg->blend_config + offset, 0x3950);
> >> +                            break;
> >> +                    case BLEND_PIXEL_NONE:
> >> +                            dc_write(hw, reg->blend_config + offset, 0x3548);
> >> +                            break;
> >> +                    default:
> >> +                            break;
> >> +                    }
> >> +                    plane->blend.dirty = false;
> >> +            }
> >> +
> >> +            if (plane->colorkey.dirty) {
> >> +                    dc_write(hw, reg->color_key + offset, plane->colorkey.colorkey);
> >> +                    dc_write(hw, reg->color_key_high + offset,
> >> +                             plane->colorkey.colorkey_high);
> >> +
> >> +                    if (primary)
> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
> >> +                                         plane->colorkey.transparency << 9, 0x03 << 9);
> >> +                    else
> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> >> +                                         plane->colorkey.transparency, 0x03);
> >> +
> >> +                    plane->colorkey.dirty = false;
> >> +            }
> >> +
> >> +            if (plane->roi.dirty) {
> >> +                    if (plane->roi.enable) {
> >> +                            dc_write(hw, reg->roi_origin + offset,
> >> +                                     plane->roi.x | (plane->roi.y << 16));
> >> +                            dc_write(hw, reg->roi_size + offset,
> >> +                                     plane->roi.width | (plane->roi.height << 16));
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                                 BIT(0), 0);
> >> +                            else
> >> +                                    dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> >> +                                                 BIT(22), 0);
> >> +                    } else {
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                                 0, BIT(0));
> >> +                            else
> >> +                                    dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> >> +                                                 0, BIT(22));
> >> +                    }
> >> +                    plane->roi.dirty = false;
> >> +            }
> >> +    }
> >> +}
> >> +
> >> +static void plane_ex_commit(struct dc_hw *hw)
> >> +{
> >> +    struct dc_hw_plane *plane;
> >> +    const struct dc_hw_plane_reg *reg;
> >> +    bool primary = false;
> >> +    u8 id, layer_num = hw->info->layer_num;
> >> +    u32 i, offset;
> >> +
> >> +    for (i = 0; i < layer_num; i++) {
> >> +            plane = &hw->plane[i];
> >> +            id = hw->info->planes[i].id;
> >> +            offset = get_addr_offset(id);
> >> +            if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
> >> +                    reg = &dc_plane_reg[0];
> >> +                    primary = true;
> >> +            } else {
> >> +                    reg = &dc_plane_reg[1];
> >> +                    primary = false;
> >> +            }
> >> +
> >> +            if (plane->fb.dirty) {
> >> +                    if (is_rgb(plane->fb.format)) {
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                                 BIT(6), BIT(8));
> >> +                            else
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_OVERLAY_CONFIG + offset,
> >> +                                                 BIT(29), BIT(30));
> >> +                    } else {
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                                 BIT(8), BIT(6));
> >> +                            else
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_OVERLAY_CONFIG + offset,
> >> +                                                 BIT(30), BIT(29));
> >> +                            switch (plane->fb.yuv_color_space) {
> >> +                            case COLOR_SPACE_601:
> >> +                                    load_yuv_to_rgb(hw, reg, offset, YUV601_2RGB);
> >> +                                    break;
> >> +                            case COLOR_SPACE_709:
> >> +                                    load_yuv_to_rgb(hw, reg, offset, YUV709_2RGB);
> >> +                                    break;
> >> +                            case COLOR_SPACE_2020:
> >> +                                    load_yuv_to_rgb(hw, reg, offset, YUV2020_2RGB);
> >> +                                    break;
> >> +                            default:
> >> +                                    break;
> >> +                            }
> >> +                    }
> >> +            }
> >> +            if (plane->degamma.dirty) {
> >> +                    switch (plane->degamma.mode) {
> >> +                    case VS_DEGAMMA_DISABLE:
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                                 0, BIT(5));
> >> +                            else
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_OVERLAY_CONFIG + offset,
> >> +                                                 0, BIT(28));
> >> +                            break;
> >> +                    case VS_DEGAMMA_BT709:
> >> +                            load_degamma_table(hw, reg, offset, DEGAMMA_709);
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                                 BIT(5), 0);
> >> +                            else
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_OVERLAY_CONFIG + offset,
> >> +                                                 BIT(28), 0);
> >> +                            break;
> >> +                    case VS_DEGAMMA_BT2020:
> >> +                            load_degamma_table(hw, reg, offset, DEGAMMA_2020);
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                                 BIT(5), 0);
> >> +                            else
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_OVERLAY_CONFIG + offset,
> >> +                                                 BIT(28), 0);
> >> +                            break;
> >> +                    default:
> >> +                            break;
> >> +                    }
> >> +                    plane->degamma.dirty = false;
> >> +            }
> >> +    }
> >> +    plane_commit(hw);
> >> +}
> >> +
> >> +static void setup_display(struct dc_hw *hw, struct dc_hw_display *display)
> >> +{
> >> +    u8 id = display->id;
> >> +    u32 dpi_cfg, offset = id << 2;
> >> +
> >> +    if (hw->display[id].enable) {
> >> +            switch (display->bus_format) {
> >> +            case MEDIA_BUS_FMT_RGB565_1X16:
> >> +                    dpi_cfg = 0;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_RGB666_1X18:
> >> +                    dpi_cfg = 3;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> >> +                    dpi_cfg = 4;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_RGB888_1X24:
> >> +                    dpi_cfg = 5;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_RGB101010_1X30:
> >> +                    dpi_cfg = 6;
> >> +                    break;
> >> +            default:
> >> +                    dpi_cfg = 5;
> >> +                    break;
> >> +            }
> >> +            dc_write(hw, DC_DISPLAY_DPI_CONFIG + offset, dpi_cfg);
> >> +
> >> +            if (id == 0)
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
> >> +            else
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
> >> +
> >> +            dc_write(hw, DC_DISPLAY_H + offset, hw->display[id].h_active |
> >> +                     (hw->display[id].h_total << 16));
> >> +            dc_write(hw, DC_DISPLAY_H_SYNC + offset,
> >> +                     hw->display[id].h_sync_start |
> >> +                     (hw->display[id].h_sync_end << 15) |
> >> +                     (hw->display[id].h_sync_polarity ? 0 : BIT(31)) |
> >> +                     BIT(30));
> >> +            dc_write(hw, DC_DISPLAY_V + offset, hw->display[id].v_active |
> >> +                            (hw->display[id].v_total << 16));
> >> +            dc_write(hw, DC_DISPLAY_V_SYNC + offset,
> >> +                     hw->display[id].v_sync_start |
> >> +                     (hw->display[id].v_sync_end << 15) |
> >> +                     (hw->display[id].v_sync_polarity ? 0 : BIT(31)) |
> >> +                     BIT(30));
> >> +
> >> +            if (hw->info->pipe_sync)
> >> +                    dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
> >> +                                 0, BIT(3) | BIT(4));
> >> +
> >> +            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(12), 0);
> >> +            if (id == 0)
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(0), BIT(3));
> >> +            else
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(1), BIT(3));
> >> +    } else {
> >> +            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(12));
> >> +            if (id == 0)
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
> >> +            else
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
> >> +    }
> >> +}
> >> +
> >> +static void setup_display_ex(struct dc_hw *hw, struct dc_hw_display *display)
> >> +{
> >> +    u8 id = display->id;
> >> +    u32 dp_cfg, offset = id << 2;
> >> +    bool is_yuv = false;
> >> +
> >> +    if (hw->display[id].enable && hw->out[id] == OUT_DP) {
> >> +            switch (display->bus_format) {
> >> +            case MEDIA_BUS_FMT_RGB565_1X16:
> >> +                    dp_cfg = 0;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_RGB666_1X18:
> >> +                    dp_cfg = 1;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_RGB888_1X24:
> >> +                    dp_cfg = 2;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_RGB101010_1X30:
> >> +                    dp_cfg = 3;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_UYVY8_1X16:
> >> +                    dp_cfg = 2 << 4;
> >> +                    is_yuv = true;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_YUV8_1X24:
> >> +                    dp_cfg = 4 << 4;
> >> +                    is_yuv = true;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_UYVY10_1X20:
> >> +                    dp_cfg = 8 << 4;
> >> +                    is_yuv = true;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_YUV10_1X30:
> >> +                    dp_cfg = 10 << 4;
> >> +                    is_yuv = true;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> >> +                    dp_cfg = 12 << 4;
> >> +                    is_yuv = true;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> >> +                    dp_cfg = 13 << 4;
> >> +                    is_yuv = true;
> >> +                    break;
> >> +            default:
> >> +                    dp_cfg = 2;
> >> +                    break;
> >> +            }
> >> +            if (is_yuv)
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(16), 0);
> >> +            else
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(16));
> >> +            dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | BIT(3));
> >> +    }
> >> +
> >> +    if (hw->out[id] == OUT_DPI)
> >> +            dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, BIT(3));
> >> +
> >> +    setup_display(hw, display);
> >> +}
> >> +
> >> +static const struct dc_hw_funcs hw_func = {
> >> +    .gamma = &gamma_ex_commit,
> >> +    .plane = &plane_ex_commit,
> >> +    .display = setup_display_ex,
> >> +};
> >> +
> >> +void dc_hw_commit(struct dc_hw *hw)
> >> +{
> >> +    u32 i, offset = 0;
> >> +    u8 plane_num = hw->info->plane_num;
> >> +    u8 layer_num = hw->info->layer_num;
> >> +    u8 cursor_num = plane_num - layer_num;
> >> +
> >> +    hw->func->gamma(hw);
> >> +    hw->func->plane(hw);
> >> +
> >> +    for (i = 0; i < cursor_num; i++) {
> >> +            if (hw->cursor[i].dirty) {
> >> +                    offset = hw->cursor[i].display_id ? DC_CURSOR_OFFSET : 0;
> >> +                    if (hw->cursor[i].enable) {
> >> +                            dc_write(hw, DC_CURSOR_ADDRESS + offset,
> >> +                                     hw->cursor[i].address);
> >> +                            dc_write(hw, DC_CURSOR_LOCATION + offset, hw->cursor[i].x |
> >> +                                     (hw->cursor[i].y << 16));
> >> +                            dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
> >> +                                         (hw->cursor[i].hot_x << 16) |
> >> +                                         (hw->cursor[i].hot_y << 8) |
> >> +                                         (hw->cursor[i].size << 5) |
> >> +                                         BIT(3) | BIT(2) | 0x02,
> >> +                                         (0xFF << 16) |
> >> +                                         (0xFF << 8) |
> >> +                                         (0x07 << 5) | 0x1F);
> >> +                    } else {
> >> +                            dc_set_clear(hw, DC_CURSOR_CONFIG + offset, BIT(3), 0x03);
> >> +                    }
> >> +                    hw->cursor[i].dirty = false;
> >> +            }
> >> +    }
> >> +
> >> +    if (hw->qos.dirty) {
> >> +            dc_set_clear(hw, DC_QOS_CONFIG, (hw->qos.high_value << 4) |
> >> +                                     hw->qos.low_value, 0xFF);
> >> +            hw->qos.dirty = false;
> >> +    }
> >> +}
> >> +
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.h b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
> >> new file mode 100644
> >> index 000000000..9a1d767ae
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
> >> @@ -0,0 +1,492 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#ifndef __VS_DC_HW_H__
> >> +#define __VS_DC_HW_H__
> >> +
> >> +#define AQ_INTR_ACKNOWLEDGE                         0x0010
> >> +#define AQ_INTR_ENBL                                        0x0014
> >
> > Please take care to align all the defines.
> >
> >> +#define DC_HW_REVISION                                      0x0024
> >> +#define DC_HW_CHIP_CID                                      0x0030
> >> +
> >> +#define DC_REG_BASE                                         0x0800
> >> +#define DC_REG_RANGE                                        0x2000
> >> +#define DC_SEC_REG_OFFSET                           0x100000
> >> +
> >> +#define DC_FRAMEBUFFER_CONFIG                       0x1518
> >> +#define DC_FRAMEBUFFER_CONFIG_EX            0x1CC0
> >> +#define DC_FRAMEBUFFER_SCALE_CONFIG         0x1520
> >> +#define DC_FRAMEBUFFER_TOP_LEFT                     0x24D8
> >> +#define DC_FRAMEBUFFER_BOTTOM_RIGHT         0x24E0
> >> +#define DC_FRAMEBUFFER_ADDRESS                      0x1400
> >> +#define DC_FRAMEBUFFER_U_ADDRESS            0x1530
> >> +#define DC_FRAMEBUFFER_V_ADDRESS            0x1538
> >> +#define DC_FRAMEBUFFER_STRIDE                       0x1408
> >> +#define DC_FRAMEBUFFER_U_STRIDE                     0x1800
> >> +#define DC_FRAMEBUFFER_V_STRIDE                     0x1808
> >> +#define DC_FRAMEBUFFER_SIZE                         0x1810
> >> +#define DC_FRAMEBUFFER_SCALE_FACTOR_X               0x1828
> >> +#define DC_FRAMEBUFFER_SCALE_FACTOR_Y               0x1830
> >> +#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX  0x1838
> >> +#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA   0x1A00
> >> +#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX  0x1A08
> >> +#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA   0x1A10
> >> +#define DC_FRAMEBUFFER_INIT_OFFSET                  0x1A20
> >> +#define DC_FRAMEBUFFER_COLOR_KEY                    0x1508
> >> +#define DC_FRAMEBUFFER_COLOR_KEY_HIGH               0x1510
> >> +#define DC_FRAMEBUFFER_CLEAR_VALUE                  0x1A18
> >> +#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX    0x1818
> >> +#define DC_FRAMEBUFFER_COLOR_TABLE_DATA             0x1820
> >> +#define DC_FRAMEBUFFER_BG_COLOR                             0x1528
> >> +#define DC_FRAMEBUFFER_ROI_ORIGIN                   0x1CB0
> >> +#define DC_FRAMEBUFFER_ROI_SIZE                             0x1CB8
> >> +#define DC_FRAMEBUFFER_WATER_MARK                   0x1CE8
> >> +#define DC_FRAMEBUFFER_DEGAMMA_INDEX                0x1D88
> >> +#define DC_FRAMEBUFFER_DEGAMMA_DATA                 0x1D90
> >> +#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA              0x1D98
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF0               0x1DA0
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF1               0x1DA8
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF2               0x1DB0
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF3               0x1DB8
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF4               0x1E00
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD0              0x1E08
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD1              0x1E10
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD2              0x1E18
> >> +#define DC_FRAMEBUFFER_Y_CLAMP_BOUND                0x1E88
> >> +#define DC_FRAMEBUFFER_UV_CLAMP_BOUND               0x1E90
> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF0               0x1E20
> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF1               0x1E28
> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF2               0x1E30
> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF3               0x1E38
> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF4               0x1E40
> >> +#define DC_FRAMEBUFFER_BLEND_CONFIG                 0x2510
> >> +#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR             0x2500
> >> +#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR             0x2508
> >> +
> >> +#define DC_OVERLAY_CONFIG                                   0x1540
> >> +#define DC_OVERLAY_CONFIG_EX                                0x2540
> >> +#define DC_OVERLAY_SCALE_CONFIG                             0x1C00
> >> +#define DC_OVERLAY_BLEND_CONFIG                             0x1580
> >> +#define DC_OVERLAY_TOP_LEFT                                 0x1640
> >> +#define DC_OVERLAY_BOTTOM_RIGHT                             0x1680
> >> +#define DC_OVERLAY_ADDRESS                                  0x15C0
> >> +#define DC_OVERLAY_U_ADDRESS                                0x1840
> >> +#define DC_OVERLAY_V_ADDRESS                                0x1880
> >> +#define DC_OVERLAY_STRIDE                                   0x1600
> >> +#define DC_OVERLAY_U_STRIDE                                 0x18C0
> >> +#define DC_OVERLAY_V_STRIDE                                 0x1900
> >> +#define DC_OVERLAY_SIZE                                             0x17C0
> >> +#define DC_OVERLAY_SCALE_FACTOR_X                   0x1A40
> >> +#define DC_OVERLAY_SCALE_FACTOR_Y                   0x1A80
> >> +#define DC_OVERLAY_H_FILTER_COEF_INDEX              0x1AC0
> >> +#define DC_OVERLAY_H_FILTER_COEF_DATA               0x1B00
> >> +#define DC_OVERLAY_V_FILTER_COEF_INDEX              0x1B40
> >> +#define DC_OVERLAY_V_FILTER_COEF_DATA               0x1B80
> >> +#define DC_OVERLAY_INIT_OFFSET                              0x1BC0
> >> +#define DC_OVERLAY_COLOR_KEY                                0x1740
> >> +#define DC_OVERLAY_COLOR_KEY_HIGH                   0x1780
> >> +#define DC_OVERLAY_CLEAR_VALUE                              0x1940
> >> +#define DC_OVERLAY_COLOR_TABLE_INDEX                0x1980
> >> +#define DC_OVERLAY_COLOR_TABLE_DATA                 0x19C0
> >> +#define DC_OVERLAY_SRC_GLOBAL_COLOR                 0x16C0
> >> +#define DC_OVERLAY_DST_GLOBAL_COLOR                 0x1700
> >> +#define DC_OVERLAY_ROI_ORIGIN                               0x1D00
> >> +#define DC_OVERLAY_ROI_SIZE                                 0x1D40
> >> +#define DC_OVERLAY_WATER_MARK                               0x1DC0
> >> +#define DC_OVERLAY_DEGAMMA_INDEX                    0x2200
> >> +#define DC_OVERLAY_DEGAMMA_DATA                             0x2240
> >> +#define DC_OVERLAY_DEGAMMA_EX_DATA                  0x2280
> >> +#define DC_OVERLAY_YUVTORGB_COEF0                   0x1EC0
> >> +#define DC_OVERLAY_YUVTORGB_COEF1                   0x1F00
> >> +#define DC_OVERLAY_YUVTORGB_COEF2                   0x1F40
> >> +#define DC_OVERLAY_YUVTORGB_COEF3                   0x1F80
> >> +#define DC_OVERLAY_YUVTORGB_COEF4                   0x1FC0
> >> +#define DC_OVERLAY_YUVTORGB_COEFD0                  0x2000
> >> +#define DC_OVERLAY_YUVTORGB_COEFD1                  0x2040
> >> +#define DC_OVERLAY_YUVTORGB_COEFD2                  0x2080
> >> +#define DC_OVERLAY_Y_CLAMP_BOUND                    0x22C0
> >> +#define DC_OVERLAY_UV_CLAMP_BOUND                   0x2300
> >> +#define DC_OVERLAY_RGBTORGB_COEF0                   0x20C0
> >> +#define DC_OVERLAY_RGBTORGB_COEF1                   0x2100
> >> +#define DC_OVERLAY_RGBTORGB_COEF2                   0x2140
> >> +#define DC_OVERLAY_RGBTORGB_COEF3                   0x2180
> >> +#define DC_OVERLAY_RGBTORGB_COEF4                   0x21C0
> >> +
> >> +#define DC_CURSOR_CONFIG                                    0x1468
> >> +#define DC_CURSOR_ADDRESS                                   0x146C
> >> +#define DC_CURSOR_LOCATION                                  0x1470
> >> +#define DC_CURSOR_BACKGROUND                                0x1474
> >> +#define DC_CURSOR_FOREGROUND                                0x1478
> >> +#define DC_CURSOR_CLK_GATING                                0x1484
> >> +#define DC_CURSOR_CONFIG_EX                                 0x24E8
> >> +#define DC_CURSOR_OFFSET                                    0x1080
> >> +
> >> +#define DC_DISPLAY_DITHER_CONFIG            0x1410
> >> +#define DC_DISPLAY_PANEL_CONFIG                     0x1418
> >> +#define DC_DISPLAY_PANEL_CONFIG_EX          0x2518
> >> +#define DC_DISPLAY_DITHER_TABLE_LOW         0x1420
> >> +#define DC_DISPLAY_DITHER_TABLE_HIGH        0x1428
> >> +#define DC_DISPLAY_H                                        0x1430
> >> +#define DC_DISPLAY_H_SYNC                           0x1438
> >> +#define DC_DISPLAY_V                                        0x1440
> >> +#define DC_DISPLAY_V_SYNC                           0x1448
> >> +#define DC_DISPLAY_CURRENT_LOCATION         0x1450
> >> +#define DC_DISPLAY_GAMMA_INDEX                      0x1458
> >> +#define DC_DISPLAY_GAMMA_DATA                       0x1460
> >> +#define DC_DISPLAY_INT                                      0x147C
> >> +#define DC_DISPLAY_INT_ENABLE                       0x1480
> >> +#define DC_DISPLAY_DBI_CONFIG                       0x1488
> >> +#define DC_DISPLAY_GENERAL_CONFIG           0x14B0
> >> +#define DC_DISPLAY_DPI_CONFIG                       0x14B8
> >> +#define DC_DISPLAY_PANEL_START                      0x1CCC
> >> +#define DC_DISPLAY_DEBUG_COUNTER_SELECT             0x14D0
> >> +#define DC_DISPLAY_DEBUG_COUNTER_VALUE              0x14D8
> >> +#define DC_DISPLAY_DP_CONFIG                                0x1CD0
> >> +#define DC_DISPLAY_GAMMA_EX_INDEX                   0x1CF0
> >> +#define DC_DISPLAY_GAMMA_EX_DATA                    0x1CF8
> >> +#define DC_DISPLAY_GAMMA_EX_ONE_DATA                0x1D80
> >> +#define DC_DISPLAY_RGBTOYUV_COEF0                   0x1E48
> >> +#define DC_DISPLAY_RGBTOYUV_COEF1                   0x1E50
> >> +#define DC_DISPLAY_RGBTOYUV_COEF2                   0x1E58
> >> +#define DC_DISPLAY_RGBTOYUV_COEF3                   0x1E60
> >> +#define DC_DISPLAY_RGBTOYUV_COEF4                   0x1E68
> >> +#define DC_DISPLAY_RGBTOYUV_COEFD0                  0x1E70
> >> +#define DC_DISPLAY_RGBTOYUV_COEFD1                  0x1E78
> >> +#define DC_DISPLAY_RGBTOYUV_COEFD2                  0x1E80
> >> +
> >> +#define DC_CLK_GATTING                                      0x1A28
> >> +#define DC_QOS_CONFIG                                       0x1A38
> >> +
> >> +#define DC_TRANSPARENCY_OPAQUE                      0x00
> >> +#define DC_TRANSPARENCY_KEY                         0x02
> >> +#define DC_DISPLAY_DITHERTABLE_LOW          0x7B48F3C0
> >> +#define DC_DISPLAY_DITHERTABLE_HIGH         0x596AD1E2
> >> +
> >> +#define GAMMA_SIZE                                          256
> >> +#define GAMMA_EX_SIZE                                       300
> >> +#define DEGAMMA_SIZE                                        260
> >> +
> >> +#define RGB_TO_RGB_TABLE_SIZE                       9
> >> +#define YUV_TO_RGB_TABLE_SIZE                       16
> >> +#define RGB_TO_YUV_TABLE_SIZE                       12
> >> +
> >> +#define DC_LAYER_NUM        6
> >> +#define DC_DISPLAY_NUM      2
> >> +#define DC_CURSOR_NUM       2
> >> +
> >> +#define DC_TILE_MODE4X4     0x15
> >> +
> >> +enum dc_chip_rev {
> >> +    DC_REV_0,/* For HW_REV_5720,HW_REV_5721_311 */
> >> +    DC_REV_1,/* For HW_REV_5721_30B */
> >> +    DC_REV_2,/* For HW_REV_5721_310 */
> >> +};
> >> +
> >> +enum dc_hw_plane_id {
> >> +    PRIMARY_PLANE_0,
> >> +    OVERLAY_PLANE_0,
> >> +    OVERLAY_PLANE_1,
> >> +    PRIMARY_PLANE_1,
> >> +    OVERLAY_PLANE_2,
> >> +    OVERLAY_PLANE_3,
> >> +    CURSOR_PLANE_0,
> >> +    CURSOR_PLANE_1,
> >> +    PLANE_NUM
> >> +};
> >> +
> >> +enum dc_hw_color_format {
> >> +    FORMAT_X4R4G4B4,//0
> >
> > Space after the comma will make it more readable. Or just define the
> > values explicitly, it will also be more readable.
> >
> >> +    FORMAT_A4R4G4B4,//1
> >> +    FORMAT_X1R5G5B5,//2
> >> +    FORMAT_A1R5G5B5,//3
> >> +    FORMAT_R5G6B5,//4
> >> +    FORMAT_X8R8G8B8,//5
> >> +    FORMAT_A8R8G8B8,//6
> >> +    FORMAT_YUY2,//7
> >> +    FORMAT_UYVY,//8
> >> +    FORMAT_INDEX8,//9
> >> +    FORMAT_MONOCHROME,//10
> >> +    FORMAT_YV12 = 0xf,
> >> +    FORMAT_A8,//16
> >> +    FORMAT_NV12,//17
> >> +    FORMAT_NV16,//18
> >> +    FORMAT_RG16,//19
> >> +    FORMAT_R8,//20
> >> +    FORMAT_NV12_10BIT,//21
> >> +    FORMAT_A2R10G10B10,//22
> >> +    FORMAT_NV16_10BIT,//23
> >> +    FORMAT_INDEX1,//24
> >> +    FORMAT_INDEX2,//25
> >> +    FORMAT_INDEX4,//26
> >> +    FORMAT_P010,//27
> >> +    FORMAT_YUV444,//28
> >> +    FORMAT_YUV444_10BIT,//29
> >> +};
> >> +
> >> +enum dc_hw_yuv_color_space {
> >> +    COLOR_SPACE_601 = 0,
> >> +    COLOR_SPACE_709 = 1,
> >> +    COLOR_SPACE_2020 = 3,
> >> +};
> >> +
> >> +enum dc_hw_rotation {
> >> +    ROT_0 = 0,
> >> +    ROT_90 = 4,
> >> +    ROT_180 = 5,
> >> +    ROT_270 = 6,
> >> +    FLIP_X = 1,
> >> +    FLIP_Y = 2,
> >> +    FLIP_XY = 3,
> >> +};
> >> +
> >> +enum dc_hw_swizzle {
> >> +    SWIZZLE_ARGB = 0,
> >> +    SWIZZLE_RGBA,
> >> +    SWIZZLE_ABGR,
> >> +    SWIZZLE_BGRA,
> >> +};
> >> +
> >> +enum dc_hw_out {
> >> +    OUT_DPI,
> >> +    OUT_DP,
> >> +};
> >> +
> >> +enum dc_hw_cursor_size {
> >> +    CURSOR_SIZE_32X32 = 0,
> >> +    CURSOR_SIZE_64X64,
> >> +};
> >> +
> >> +enum dc_hw_blend_mode {
> >> +    /* out.rgb = plane_alpha * fg.rgb +
> >> +     *              (1 - (plane_alpha * fg.alpha)) * bg.rgb
> >> +     */
> >> +    BLEND_PREMULTI,
> >> +    /* out.rgb = plane_alpha * fg.alpha * fg.rgb +
> >> +     *              (1 - (plane_alpha * fg.alpha)) * bg.rgb
> >> +     */
> >> +    BLEND_COVERAGE,
> >> +    /* out.rgb = plane_alpha * fg.rgb +
> >> +     *              (1 - plane_alpha) * bg.rgb
> >> +     */
> >> +    BLEND_PIXEL_NONE,
> >
> > Can you use defines from <drm/drm_blend.h>?
> >
> >> +};
> >> +
> >> +struct dc_hw_plane_reg {
> >> +    u32 y_address;
> >> +    u32 u_address;
> >> +    u32 v_address;
> >> +    u32 y_stride;
> >> +    u32 u_stride;
> >> +    u32 v_stride;
> >> +    u32 size;
> >> +    u32 top_left;
> >> +    u32 bottom_right;
> >> +    u32 scale_factor_x;
> >> +    u32 scale_factor_y;
> >> +    u32 h_filter_coef_index;
> >> +    u32 h_filter_coef_data;
> >> +    u32 v_filter_coef_index;
> >> +    u32 v_filter_coef_data;
> >> +    u32 init_offset;
> >> +    u32 color_key;
> >> +    u32 color_key_high;
> >> +    u32 clear_value;
> >> +    u32 color_table_index;
> >> +    u32 color_table_data;
> >> +    u32 scale_config;
> >> +    u32 water_mark;
> >> +    u32 degamma_index;
> >> +    u32 degamma_data;
> >> +    u32 degamma_ex_data;
> >> +    u32 src_global_color;
> >> +    u32 dst_global_color;
> >> +    u32 blend_config;
> >> +    u32 roi_origin;
> >> +    u32 roi_size;
> >> +    u32 yuv_to_rgb_coef0;
> >> +    u32 yuv_to_rgb_coef1;
> >> +    u32 yuv_to_rgb_coef2;
> >> +    u32 yuv_to_rgb_coef3;
> >> +    u32 yuv_to_rgb_coef4;
> >> +    u32 yuv_to_rgb_coefd0;
> >> +    u32 yuv_to_rgb_coefd1;
> >> +    u32 yuv_to_rgb_coefd2;
> >> +    u32 y_clamp_bound;
> >> +    u32 uv_clamp_bound;
> >> +    u32 rgb_to_rgb_coef0;
> >> +    u32 rgb_to_rgb_coef1;
> >> +    u32 rgb_to_rgb_coef2;
> >> +    u32 rgb_to_rgb_coef3;
> >> +    u32 rgb_to_rgb_coef4;
> >> +};
> >> +
> >> +struct dc_hw_fb {
> >> +    u32 y_address;
> >> +    u32 u_address;
> >> +    u32 v_address;
> >> +    u32 clear_value;
> >> +    u32 water_mark;
> >> +    u16 y_stride;
> >> +    u16 u_stride;
> >> +    u16 v_stride;
> >> +    u16 width;
> >> +    u16 height;
> >> +    u8      format;
> >> +    u8      tile_mode;
> >> +    u8      rotation;
> >> +    u8      yuv_color_space;
> >> +    u8      swizzle;
> >> +    u8      uv_swizzle;
> >> +    u8      zpos;
> >> +    u8      display_id;
> >> +    bool    clear_enable;
> >> +    bool    dec_enable;
> >> +    bool    enable;
> >> +    bool    dirty;
> >> +};
> >> +
> >> +struct dc_hw_scale {
> >> +    u32 scale_factor_x;
> >> +    u32 scale_factor_y;
> >> +    bool    enable;
> >> +    bool    dirty;
> >> +};
> >> +
> >> +struct dc_hw_position {
> >> +    u16 start_x;
> >> +    u16 start_y;
> >> +    u16 end_x;
> >> +    u16 end_y;
> >> +    bool    dirty;
> >> +};
> >> +
> >> +struct dc_hw_blend {
> >> +    u8      alpha;
> >> +    u8      blend_mode;
> >> +    bool    dirty;
> >> +};
> >> +
> >> +struct dc_hw_colorkey {
> >> +    u32 colorkey;
> >> +    u32 colorkey_high;
> >> +    u8      transparency;
> >> +    bool dirty;
> >> +};
> >> +
> >> +struct dc_hw_roi {
> >> +    u16 x;
> >> +    u16 y;
> >> +    u16 width;
> >> +    u16 height;
> >> +    bool enable;
> >> +    bool dirty;
> >> +};
> >> +
> >> +struct dc_hw_cursor {
> >> +    u32 address;
> >> +    u16 x;
> >> +    u16 y;
> >> +    u16 hot_x;
> >> +    u16 hot_y;
> >> +    u8      size;
> >> +    u8      display_id;
> >> +    bool    enable;
> >> +    bool    dirty;
> >> +};
> >> +
> >> +struct dc_hw_display {
> >> +    u32 bus_format;
> >> +    u16 h_active;
> >> +    u16 h_total;
> >> +    u16 h_sync_start;
> >> +    u16 h_sync_end;
> >> +    u16 v_active;
> >> +    u16 v_total;
> >> +    u16 v_sync_start;
> >> +    u16 v_sync_end;
> >> +    u8      id;
> >> +    bool    h_sync_polarity;
> >> +    bool    v_sync_polarity;
> >> +    bool    enable;
> >> +};
> >> +
> >> +struct dc_hw_gamma {
> >> +    u16 gamma[GAMMA_EX_SIZE][3];
> >> +    bool    enable;
> >> +    bool    dirty;
> >> +};
> >> +
> >> +struct dc_hw_degamma {
> >> +    u16 degamma[DEGAMMA_SIZE][3];
> >> +    u32 mode;
> >> +    bool    dirty;
> >> +};
> >> +
> >> +struct dc_hw_plane {
> >> +    struct dc_hw_fb                 fb;
> >> +    struct dc_hw_position   pos;
> >> +    struct dc_hw_scale              scale;
> >> +    struct dc_hw_blend              blend;
> >> +    struct dc_hw_roi                roi;
> >> +    struct dc_hw_colorkey   colorkey;
> >> +    struct dc_hw_degamma    degamma;
> >> +};
> >> +
> >> +struct dc_hw_qos {
> >> +    u8        low_value;
> >> +    u8        high_value;
> >> +    bool  dirty;
> >> +};
> >> +
> >> +struct dc_hw_read {
> >> +    u32                     reg;
> >> +    u32                     value;
> >> +};
> >> +
> >> +struct dc_hw;
> >> +struct dc_hw_funcs {
> >> +    void (*gamma)(struct dc_hw *hw);
> >> +    void (*plane)(struct dc_hw *hw);
> >> +    void (*display)(struct dc_hw *hw, struct dc_hw_display *display);
> >> +};
> >> +
> >> +struct dc_hw {
> >> +    enum dc_chip_rev rev;
> >> +    enum dc_hw_out          out[DC_DISPLAY_NUM];
> >> +    void                    *hi_base;
> >> +    void                    *reg_base;
> >> +
> >> +    struct dc_hw_display display[DC_DISPLAY_NUM];
> >> +    struct dc_hw_gamma       gamma[DC_DISPLAY_NUM];
> >> +    struct dc_hw_plane       plane[DC_LAYER_NUM];
> >> +    struct dc_hw_cursor  cursor[DC_CURSOR_NUM];
> >> +    struct dc_hw_qos         qos;
> >> +    struct dc_hw_funcs       *func;
> >> +    struct vs_dc_info        *info;
> >> +};
> >> +
> >> +int dc_hw_init(struct dc_hw *hw);
> >> +void dc_hw_deinit(struct dc_hw *hw);
> >> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
> >> +                    struct dc_hw_fb *fb, struct dc_hw_scale *scale,
> >> +                    struct dc_hw_position *pos, struct dc_hw_blend *blend);
> >> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode);
> >> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi);
> >> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
> >> +                       struct dc_hw_colorkey *colorkey);
> >> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos);
> >> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor);
> >> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
> >> +                    u16 r, u16 g, u16 b);
> >> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable);
> >> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display);
> >> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable);
> >> +u32 dc_hw_get_interrupt(struct dc_hw *hw);
> >> +bool dc_hw_check_underflow(struct dc_hw *hw);
> >> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable);
> >> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id);
> >> +void dc_hw_commit(struct dc_hw *hw);
> >> +
> >> +#endif /* __VS_DC_HW_H__ */
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
> >> index da7698c3d..3cd533cfa 100644
> >> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
> >> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> >> @@ -21,6 +21,7 @@
> >>
> >>   #include "vs_drv.h"
> >>   #include "vs_modeset.h"
> >> +#include "vs_dc.h"
> >>
> >>   #define DRV_NAME   "verisilicon"
> >>   #define DRV_DESC   "Verisilicon DRM driver"
> >> @@ -123,6 +124,7 @@ static const struct component_master_ops vs_drm_ops = {
> >>   };
> >>
> >>   static struct platform_driver *drm_sub_drivers[] = {
> >> +    &dc_platform_driver,
> >>
> >>   };
> >>
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c
> >> new file mode 100644
> >> index 000000000..9bd066015
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_plane.c
> >> @@ -0,0 +1,526 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#include <drm/drm_atomic.h>
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_blend.h>
> >> +#include <drm/drm_gem_dma_helper.h>
> >> +#include <drm/drm_fb_dma_helper.h>
> >> +#include <drm/drm_framebuffer.h>
> >> +#include <drm/drm_plane.h>
> >> +#include <drm/drm_plane_helper.h>
> >> +
> >> +#include <drm/vs_drm.h>
> >> +
> >> +#include "vs_plane.h"
> >> +#include "vs_drv.h"
> >> +#include "vs_dc.h"
> >> +
> >> +static void vs_plane_reset(struct drm_plane *plane)
> >> +{
> >> +    struct vs_plane_state *state;
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +
> >> +    if (plane->state) {
> >> +            __drm_atomic_helper_plane_destroy_state(plane->state);
> >> +
> >> +            state = to_vs_plane_state(plane->state);
> >> +            kfree(state);
> >> +            plane->state = NULL;
> >> +    }
> >> +
> >
> > Use vs_plane_destroy_state directly. This way you won't miss a refcount
> > on blobs.
> >
> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
> >> +    if (!state)
> >> +            return;
> >> +
> >> +    state->degamma = VS_DEGAMMA_DISABLE;
> >> +    state->degamma_changed = false;
> >> +    state->base.zpos = vs_plane->id;
> >> +    memset(&state->status, 0, sizeof(state->status));
> >> +
> >> +    __drm_atomic_helper_plane_reset(plane, &state->base);
> >> +}
> >> +
> >> +static void _vs_plane_duplicate_blob(struct vs_plane_state *state,
> >> +                                 struct vs_plane_state *ori_state)
> >> +{
> >> +    state->watermark = ori_state->watermark;
> >> +    state->color_mgmt = ori_state->color_mgmt;
> >> +    state->roi = ori_state->roi;
> >> +
> >> +    if (state->watermark)
> >> +            drm_property_blob_get(state->watermark);
> >> +    if (state->color_mgmt)
> >> +            drm_property_blob_get(state->color_mgmt);
> >> +    if (state->roi)
> >> +            drm_property_blob_get(state->roi);
> >> +}
> >> +
> >> +static int
> >> +_vs_plane_set_property_blob_from_id(struct drm_device *dev,
> >> +                                struct drm_property_blob **blob,
> >> +                                u64 blob_id,
> >> +                                size_t expected_size)
> >> +{
> >> +    struct drm_property_blob *new_blob = NULL;
> >> +
> >> +    if (blob_id) {
> >> +            new_blob = drm_property_lookup_blob(dev, blob_id);
> >> +            if (!new_blob)
> >> +                    return -EINVAL;
> >> +
> >> +            if (new_blob->length != expected_size) {
> >> +                    drm_property_blob_put(new_blob);
> >> +                    return -EINVAL;
> >> +            }
> >> +    }
> >> +
> >> +    drm_property_replace_blob(blob, new_blob);
> >> +    drm_property_blob_put(new_blob);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static struct drm_plane_state *
> >> +vs_plane_atomic_duplicate_state(struct drm_plane *plane)
> >> +{
> >> +    struct vs_plane_state *ori_state;
> >> +    struct vs_plane_state *state;
> >> +
> >> +    if (WARN_ON(!plane->state))
> >> +            return NULL;
> >> +
> >> +    ori_state = to_vs_plane_state(plane->state);
> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
> >> +    if (!state)
> >> +            return NULL;
> >> +
> >> +    __drm_atomic_helper_plane_duplicate_state(plane, &state->base);
> >> +
> >> +    state->degamma = ori_state->degamma;
> >> +    state->degamma_changed = ori_state->degamma_changed;
> >> +
> >> +    _vs_plane_duplicate_blob(state, ori_state);
> >> +    memcpy(&state->status, &ori_state->status, sizeof(ori_state->status));
> >> +
> >> +    return &state->base;
> >> +}
> >> +
> >> +static void vs_plane_atomic_destroy_state(struct drm_plane *plane,
> >> +                                      struct drm_plane_state *state)
> >> +{
> >> +    struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
> >> +
> >> +    __drm_atomic_helper_plane_destroy_state(state);
> >> +
> >> +    drm_property_blob_put(vs_plane_state->watermark);
> >> +    drm_property_blob_put(vs_plane_state->color_mgmt);
> >> +    drm_property_blob_put(vs_plane_state->roi);
> >> +    kfree(vs_plane_state);
> >> +}
> >> +
> >> +static int vs_plane_atomic_set_property(struct drm_plane *plane,
> >> +                                    struct drm_plane_state *state,
> >> +                                    struct drm_property *property,
> >> +                                    uint64_t val)
> >> +{
> >> +    struct drm_device *dev = plane->dev;
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +    struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
> >> +    int ret = 0;
> >> +
> >> +    if (property == vs_plane->degamma_mode) {
> >> +            if (vs_plane_state->degamma != val) {
> >> +                    vs_plane_state->degamma = val;
> >> +                    vs_plane_state->degamma_changed = true;
> >> +            } else {
> >> +                    vs_plane_state->degamma_changed = false;
> >> +            }
> >> +    } else if (property == vs_plane->watermark_prop) {
> >> +            ret = _vs_plane_set_property_blob_from_id(dev,
> >> +                                                      &vs_plane_state->watermark,
> >> +                                                      val,
> >> +                                                      sizeof(struct drm_vs_watermark));
> >> +            return ret;
> >> +    } else if (property == vs_plane->color_mgmt_prop) {
> >> +            ret = _vs_plane_set_property_blob_from_id(dev,
> >> +                                                      &vs_plane_state->color_mgmt,
> >> +                                                      val,
> >> +                                                      sizeof(struct drm_vs_color_mgmt));
> >> +            return ret;
> >> +    } else if (property == vs_plane->roi_prop) {
> >> +            ret = _vs_plane_set_property_blob_from_id(dev,
> >> +                                                      &vs_plane_state->roi,
> >> +                                                      val,
> >> +                                                      sizeof(struct drm_vs_roi));
> >> +            return ret;
> >> +    } else {
> >> +            return -EINVAL;
> >> +    }
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int vs_plane_atomic_get_property(struct drm_plane *plane,
> >> +                                    const struct drm_plane_state *state,
> >> +                                    struct drm_property *property,
> >> +                                    uint64_t *val)
> >> +{
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +    const struct vs_plane_state *vs_plane_state =
> >> +            container_of(state, const struct vs_plane_state, base);
> >> +
> >> +    if (property == vs_plane->degamma_mode)
> >> +            *val = vs_plane_state->degamma;
> >> +    else if (property == vs_plane->watermark_prop)
> >> +            *val = (vs_plane_state->watermark) ?
> >> +                                    vs_plane_state->watermark->base.id : 0;
> >> +    else if (property == vs_plane->color_mgmt_prop)
> >> +            *val = (vs_plane_state->color_mgmt) ?
> >> +                                    vs_plane_state->color_mgmt->base.id : 0;
> >
> > degamma and color management should use standard properties.
> >
> >> +    else if (property == vs_plane->roi_prop)
> >> +            *val = (vs_plane_state->roi) ?
> >> +                                    vs_plane_state->roi->base.id : 0;
> >> +    else
> >> +            return -EINVAL;
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static bool vs_format_mod_supported(struct drm_plane *plane,
> >> +                                u32 format,
> >> +                                u64 modifier)
> >> +{
> >> +    int i;
> >> +
> >> +    /* We always have to allow these modifiers:
> >> +     * 1. Core DRM checks for LINEAR support if userspace does not provide modifiers.
> >> +     * 2. Not passing any modifiers is the same as explicitly passing INVALID.
> >> +     */
> >> +    if (modifier == DRM_FORMAT_MOD_LINEAR)
> >> +            return true;
> >> +
> >> +    /* Check that the modifier is on the list of the plane's supported modifiers. */
> >> +    for (i = 0; i < plane->modifier_count; i++) {
> >> +            if (modifier == plane->modifiers[i])
> >> +                    break;
> >> +    }
> >> +
> >> +    if (i == plane->modifier_count)
> >> +            return false;
> >> +
> >> +    return true;
> >> +}
> >> +
> >> +const struct drm_plane_funcs vs_plane_funcs = {
> >> +    .update_plane           = drm_atomic_helper_update_plane,
> >> +    .disable_plane          = drm_atomic_helper_disable_plane,
> >> +    .reset                  = vs_plane_reset,
> >> +    .atomic_duplicate_state = vs_plane_atomic_duplicate_state,
> >> +    .atomic_destroy_state   = vs_plane_atomic_destroy_state,
> >> +    .atomic_set_property    = vs_plane_atomic_set_property,
> >> +    .atomic_get_property    = vs_plane_atomic_get_property,
> >> +    .format_mod_supported   = vs_format_mod_supported,
> >> +};
> >> +
> >> +static unsigned char vs_get_plane_number(struct drm_framebuffer *fb)
> >> +{
> >> +    const struct drm_format_info *info;
> >> +
> >> +    if (!fb)
> >> +            return 0;
> >> +
> >> +    info = drm_format_info(fb->format->format);
> >> +    if (!info || info->num_planes > DRM_FORMAT_MAX_PLANES)
> >> +            return 0;
> >> +
> >> +    return info->num_planes;
> >> +}
> >> +
> >> +static int vs_plane_atomic_check(struct drm_plane *plane,
> >> +                             struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> >> +                                                                             plane);
> >> +    unsigned char i, num_planes;
> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
> >> +
> >> +    if (!crtc || !fb)
> >> +            return 0;
> >> +
> >> +    num_planes = vs_get_plane_number(fb);
> >> +
> >> +    for (i = 0; i < num_planes; i++) {
> >> +            dma_addr_t dma_addr;
> >> +
> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
> >> +            plane_state->dma_addr[i] = dma_addr;
> >> +    }
> >> +
> >> +    return vs_dc_check_plane(dc, plane, state);
> >> +}
> >> +
> >> +static int vs_cursor_plane_atomic_check(struct drm_plane *plane,
> >> +                                    struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> >> +                                                                      plane);
> >> +    unsigned char i, num_planes;
> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
> >> +
> >> +    if (!crtc || !fb)
> >> +            return 0;
> >> +
> >> +    num_planes = vs_get_plane_number(fb);
> >> +
> >> +    for (i = 0; i < num_planes; i++) {
> >> +            dma_addr_t dma_addr;
> >> +
> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
> >> +            plane_state->dma_addr[i] = dma_addr;
> >> +    }
> >> +
> >> +    return vs_dc_check_cursor_plane(dc, plane, state);
> >> +}
> >> +
> >> +static void vs_plane_atomic_update(struct drm_plane *plane,
> >> +                               struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
> >> +                                                                      plane);
> >
> > New line after the equal sign will be better.
> >
> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> >> +                                                                      plane);
> >> +
> >> +    unsigned char i, num_planes;
> >> +    struct drm_framebuffer *fb;
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    if (!new_state->fb || !new_state->crtc)
> >> +            return;
> >
> > if (!new_state->visible) ?
> >
> >> +
> >> +    fb = new_state->fb;
> >> +
> >> +    drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
> >> +
> >> +    num_planes = vs_get_plane_number(fb);
> >> +
> >> +    for (i = 0; i < num_planes; i++) {
> >> +            dma_addr_t dma_addr;
> >> +
> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
> >> +            plane_state->dma_addr[i] = dma_addr;
> >> +    }
> >> +
> >> +    plane_state->status.src = drm_plane_state_src(new_state);
> >> +    plane_state->status.dest = drm_plane_state_dest(new_state);
> >> +
> >> +    vs_dc_update_plane(dc, vs_plane, plane, state);
> >> +}
> >> +
> >> +static void vs_cursor_plane_atomic_update(struct drm_plane *plane,
> >> +                                      struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
> >> +                                                                       plane);
> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> >> +                                                                       plane);
> >> +    unsigned char i, num_planes;
> >> +    struct drm_framebuffer *fb;
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    if (!new_state->fb || !new_state->crtc)
> >
> > and here.
> >
> >> +            return;
> >> +
> >> +    fb = new_state->fb;
> >> +    drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
> >> +
> >> +    num_planes = vs_get_plane_number(fb);
> >> +
> >> +    for (i = 0; i < num_planes; i++) {
> >> +            dma_addr_t dma_addr;
> >> +
> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
> >> +            plane_state->dma_addr[i] = dma_addr;
> >> +    }
> >> +
> >> +    plane_state->status.src = drm_plane_state_src(new_state);
> >> +    plane_state->status.dest = drm_plane_state_dest(new_state);
> >> +
> >> +    vs_dc_update_cursor_plane(dc, vs_plane, plane, state);
> >> +}
> >> +
> >> +static void vs_plane_atomic_disable(struct drm_plane *plane,
> >> +                                struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> >> +                                                                       plane);
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    vs_dc_disable_plane(dc, vs_plane, old_state);
> >> +}
> >> +
> >> +static void vs_cursor_plane_atomic_disable(struct drm_plane *plane,
> >> +                                       struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> >> +                                                                       plane);
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    vs_dc_disable_cursor_plane(dc, vs_plane, old_state);
> >> +}
> >> +
> >> +const struct drm_plane_helper_funcs primary_plane_helpers = {
> >> +    .atomic_check   = vs_plane_atomic_check,
> >> +    .atomic_update  = vs_plane_atomic_update,
> >> +    .atomic_disable = vs_plane_atomic_disable,
> >> +};
> >> +
> >> +const struct drm_plane_helper_funcs overlay_plane_helpers = {
> >> +    .atomic_check   = vs_plane_atomic_check,
> >> +    .atomic_update  = vs_plane_atomic_update,
> >> +    .atomic_disable = vs_plane_atomic_disable,
> >> +};
> >> +
> >> +const struct drm_plane_helper_funcs cursor_plane_helpers = {
> >> +    .atomic_check   = vs_cursor_plane_atomic_check,
> >> +    .atomic_update  = vs_cursor_plane_atomic_update,
> >> +    .atomic_disable = vs_cursor_plane_atomic_disable,
> >> +};
> >> +
> >> +static const struct drm_prop_enum_list vs_degamma_mode_enum_list[] = {
> >> +    { VS_DEGAMMA_DISABLE,   "disabled" },
> >> +    { VS_DEGAMMA_BT709,     "preset degamma for BT709" },
> >> +    { VS_DEGAMMA_BT2020,    "preset degamma for BT2020" },
> >> +};
> >> +
> >> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
> >> +                             struct vs_plane_info *info,
> >> +                             unsigned int layer_num,
> >> +                             unsigned int possible_crtcs)
> >> +{
> >> +    struct vs_plane *plane;
> >> +    int ret;
> >> +
> >> +    if (!info)
> >> +            return NULL;
> >> +
> >> +    plane = drmm_universal_plane_alloc(drm_dev, struct vs_plane, base,
> >> +                                       possible_crtcs,
> >> +                                       &vs_plane_funcs,
> >> +                                       info->formats, info->num_formats,
> >> +                                       info->modifiers, info->type,
> >> +                                       info->name ? info->name : NULL);
> >> +    if (IS_ERR(plane))
> >> +            return ERR_CAST(plane);
> >> +
> >> +    if (info->type == DRM_PLANE_TYPE_PRIMARY)
> >> +            drm_plane_helper_add(&plane->base, &primary_plane_helpers);
> >> +    else if (info->type == DRM_PLANE_TYPE_CURSOR)
> >> +            drm_plane_helper_add(&plane->base, &cursor_plane_helpers);
> >> +    else
> >> +            drm_plane_helper_add(&plane->base, &overlay_plane_helpers);
> >> +
> >> +    /* Set up the plane properties */
> >> +    if (info->degamma_size) {
> >> +            plane->degamma_mode =
> >> +            drm_property_create_enum(drm_dev, 0,
> >> +                                     "DEGAMMA_MODE",
> >> +                                     vs_degamma_mode_enum_list,
> >> +                                     ARRAY_SIZE(vs_degamma_mode_enum_list));
> >> +
> >> +            if (!plane->degamma_mode)
> >> +                    return NULL;
> >> +
> >> +            drm_object_attach_property(&plane->base.base,
> >> +                                       plane->degamma_mode,
> >> +                                       VS_DEGAMMA_DISABLE);
> >> +    }
> >> +
> >> +    if (info->rotation) {
> >> +            ret = drm_plane_create_rotation_property(&plane->base,
> >> +                                                     DRM_MODE_ROTATE_0,
> >> +                                                     info->rotation);
> >> +            if (ret)
> >> +                    return NULL;
> >> +    }
> >> +
> >> +    if (info->blend_mode) {
> >> +            ret = drm_plane_create_blend_mode_property(&plane->base,
> >> +                                                       info->blend_mode);
> >> +            if (ret)
> >> +                    return NULL;
> >> +            ret = drm_plane_create_alpha_property(&plane->base);
> >> +            if (ret)
> >> +                    return NULL;
> >> +    }
> >> +
> >> +    if (info->color_encoding) {
> >> +            ret = drm_plane_create_color_properties(&plane->base,
> >> +                                                    info->color_encoding,
> >> +                                                    BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
> >> +                                                    DRM_COLOR_YCBCR_BT709,
> >> +                                                    DRM_COLOR_YCBCR_LIMITED_RANGE);
> >> +            if (ret)
> >> +                    return NULL;
> >> +    }
> >> +
> >> +    if (info->zpos != 255) {
> >> +            ret = drm_plane_create_zpos_property(&plane->base, info->zpos, 0,
> >> +                                                 layer_num - 1);
> >> +            if (ret)
> >> +                    return NULL;
> >> +    } else {
> >> +            ret = drm_plane_create_zpos_immutable_property(&plane->base,
> >> +                                                           info->zpos);
> >> +            if (ret)
> >> +                    return NULL;
> >> +    }
> >> +
> >> +    if (info->watermark) {
> >> +            plane->watermark_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
> >> +                                                        "WATERMARK", 0);
> >> +            if (!plane->watermark_prop)
> >> +                    return NULL;
> >> +            drm_object_attach_property(&plane->base.base, plane->watermark_prop, 0);
> >> +    }
> >> +
> >> +    if (info->color_mgmt) {
> >> +            plane->color_mgmt_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
> >> +                                                         "COLOR_CONFIG", 0);
> >> +            if (!plane->color_mgmt_prop)
> >> +                    return NULL;
> >> +
> >> +            drm_object_attach_property(&plane->base.base, plane->color_mgmt_prop, 0);
> >> +    }
> >> +
> >> +    if (info->roi) {
> >> +            plane->roi_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
> >> +                                                  "ROI", 0);
> >> +            if (!plane->roi_prop)
> >> +                    return NULL;
> >> +
> >> +            drm_object_attach_property(&plane->base.base, plane->roi_prop, 0);
> >> +    }
> >> +
> >> +    return plane;
> >> +}
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h
> >> new file mode 100644
> >> index 000000000..554b74e96
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_plane.h
> >> @@ -0,0 +1,58 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#ifndef __VS_PLANE_H__
> >> +#define __VS_PLANE_H__
> >> +
> >> +#include <drm/drm_fourcc.h>
> >> +#include <drm/drm_plane_helper.h>
> >> +
> >> +#include "vs_type.h"
> >> +
> >> +struct vs_plane_status {
> >> +    u32 tile_mode;
> >> +    struct drm_rect src;
> >> +    struct drm_rect dest;
> >> +};
> >> +
> >> +struct vs_plane_state {
> >> +    struct drm_plane_state base;
> >> +    struct vs_plane_status status; /* for debugfs */
> >> +
> >> +    struct drm_property_blob *watermark;
> >> +    struct drm_property_blob *color_mgmt;
> >> +    struct drm_property_blob *roi;
> >> +    dma_addr_t dma_addr[DRM_FORMAT_MAX_PLANES];
> >> +
> >> +    u32 degamma;
> >> +    bool degamma_changed;
> >> +};
> >> +
> >> +struct vs_plane {
> >> +    struct drm_plane base;
> >> +    u8 id;
> >> +
> >> +    struct drm_property *degamma_mode;
> >> +    struct drm_property *watermark_prop;
> >> +    struct drm_property *color_mgmt_prop;
> >> +    struct drm_property *roi_prop;
> >> +};
> >> +
> >> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
> >> +                             struct vs_plane_info *info,
> >> +                             unsigned int layer_num,
> >> +                             unsigned int possible_crtcs);
> >> +
> >> +static inline struct vs_plane *to_vs_plane(struct drm_plane *plane)
> >> +{
> >> +    return container_of(plane, struct vs_plane, base);
> >> +}
> >> +
> >> +static inline struct vs_plane_state *
> >> +to_vs_plane_state(struct drm_plane_state *state)
> >> +{
> >> +    return container_of(state, struct vs_plane_state, base);
> >> +}
> >> +#endif /* __VS_PLANE_H__ */
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_type.h b/drivers/gpu/drm/verisilicon/vs_type.h
> >> new file mode 100644
> >> index 000000000..7d3378e29
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_type.h
> >> @@ -0,0 +1,69 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#ifndef __VS_TYPE_H__
> >> +#define __VS_TYPE_H__
> >> +
> >> +#include <drm/drm_plane.h>
> >> +#include <drm/drm_plane_helper.h>
> >> +
> >> +struct vs_plane_info {
> >> +    const char *name;
> >> +    u8 id;
> >> +    enum drm_plane_type type;
> >> +    unsigned int num_formats;
> >> +    const u32 *formats;
> >> +    u8 num_modifiers;
> >> +    const u64 *modifiers;
> >> +    unsigned int min_width;
> >> +    unsigned int min_height;
> >> +    unsigned int max_width;
> >> +    unsigned int max_height;
> >> +    unsigned int rotation;
> >> +    unsigned int blend_mode;
> >> +    unsigned int color_encoding;
> >> +
> >> +    /* 0 means no de-gamma LUT */
> >> +    unsigned int degamma_size;
> >> +
> >> +    int min_scale; /* 16.16 fixed point */
> >> +    int max_scale; /* 16.16 fixed point */
> >> +
> >> +    /* default zorder value,
> >> +     * and 255 means unsupported zorder capability
> >> +     */
> >> +    u8       zpos;
> >> +
> >> +    bool watermark;
> >> +    bool color_mgmt;
> >> +    bool roi;
> >> +};
> >> +
> >> +struct vs_dc_info {
> >> +    const char *name;
> >> +
> >> +    u8 panel_num;
> >> +
> >> +    /* planes */
> >> +    u8 plane_num;
> >> +    const struct vs_plane_info *planes;
> >> +
> >> +    u8 layer_num;
> >> +    unsigned int max_bpc;
> >> +    unsigned int color_formats;
> >> +
> >> +    /* 0 means no gamma LUT */
> >> +    u16 gamma_size;
> >> +    u8 gamma_bits;
> >> +
> >> +    u16 pitch_alignment;
> >> +
> >> +    bool pipe_sync;
> >> +    bool background;
> >> +    bool panel_sync;
> >> +    bool cap_dec;
> >> +};
> >> +
> >> +#endif /* __VS_TYPE_H__ */
> >



-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-11-14 10:59         ` Dmitry Baryshkov
  0 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-11-14 10:59 UTC (permalink / raw)
  To: Keith Zhao
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Maxime Ripard, Jagan Teki, Rob Herring,
	Chris Morgan, Paul Walmsley, Bjorn Andersson, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Shawn Guo, christian.koenig

On Tue, 14 Nov 2023 at 12:42, Keith Zhao <keith.zhao@starfivetech.com> wrote:
>
>
>
> On 2023/10/26 3:28, Dmitry Baryshkov wrote:
> > On 25/10/2023 13:39, Keith Zhao wrote:
> >> add 2 crtcs and 8 planes in vs-drm
> >>
> >> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> >> ---
> >>   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
> >>   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
> >>   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
> >>   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
> >>   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
> >>   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
> >>   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
> >>   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
> >>   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
> >>   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
> >>   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
> >>   11 files changed, 4494 insertions(+), 2 deletions(-)
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
> >>
> >> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> >> index 7d3be305b..1d48016ca 100644
> >> --- a/drivers/gpu/drm/verisilicon/Makefile
> >> +++ b/drivers/gpu/drm/verisilicon/Makefile
> >> @@ -1,7 +1,11 @@
> >>   # SPDX-License-Identifier: GPL-2.0
> >>
> >> -vs_drm-objs := vs_drv.o \
> >> -            vs_modeset.o
> >> +vs_drm-objs := vs_dc_hw.o \
> >> +            vs_dc.o \
> >> +            vs_crtc.o \
> >> +            vs_drv.o \
> >> +            vs_modeset.o \
> >> +            vs_plane.o
> >>
> >>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
> >>
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
> >> new file mode 100644
> >> index 000000000..8a658ea77
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
> >> @@ -0,0 +1,257 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + *
> >> + */
> >> +
> >> +#include <linux/clk.h>
> >> +#include <linux/debugfs.h>
> >> +#include <linux/media-bus-format.h>
> >> +
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_atomic.h>
> >> +#include <drm/drm_crtc.h>
> >> +#include <drm/drm_gem_atomic_helper.h>
> >> +#include <drm/drm_vblank.h>
> >> +#include <drm/vs_drm.h>
> >> +
> >> +#include "vs_crtc.h"
> >> +#include "vs_dc.h"
> >> +#include "vs_drv.h"
> >> +
> >> +static void vs_crtc_reset(struct drm_crtc *crtc)
> >> +{
> >> +    struct vs_crtc_state *state;
> >> +
> >> +    if (crtc->state) {
> >> +            __drm_atomic_helper_crtc_destroy_state(crtc->state);
> >> +
> >> +            state = to_vs_crtc_state(crtc->state);
> >> +            kfree(state);
> >> +            crtc->state = NULL;
> >> +    }
> >
> > You can call your crtc_destroy_state function directly here.
> >
> >> +
> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
> >> +    if (!state)
> >> +            return;
> >> +
> >> +    __drm_atomic_helper_crtc_reset(crtc, &state->base);
> >> +}
> >> +
> >> +static struct drm_crtc_state *
> >> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
> >> +{
> >> +    struct vs_crtc_state *ori_state;
> >
> > It might be a matter of taste, but it is usually old_state.
> >
> >> +    struct vs_crtc_state *state;
> >> +
> >> +    if (!crtc->state)
> >> +            return NULL;
> >> +
> >> +    ori_state = to_vs_crtc_state(crtc->state);
> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
> >> +    if (!state)
> >> +            return NULL;
> >> +
> >> +    __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
> >> +
> >> +    state->output_fmt = ori_state->output_fmt;
> >> +    state->encoder_type = ori_state->encoder_type;
> >> +    state->bpp = ori_state->bpp;
> >> +    state->underflow = ori_state->underflow;
> >
> > Can you use kmemdup instead?
> >
> >> +
> >> +    return &state->base;
> >> +}
> >> +
> >> +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
> >> +                                     struct drm_crtc_state *state)
> >> +{
> >> +    __drm_atomic_helper_crtc_destroy_state(state);
> >> +    kfree(to_vs_crtc_state(state));
> >> +}
> >> +
> >> +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
> >> +{
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    vs_dc_enable_vblank(dc, true);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
> >> +{
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    vs_dc_enable_vblank(dc, false);
> >> +}
> >> +
> >> +static const struct drm_crtc_funcs vs_crtc_funcs = {
> >> +    .set_config             = drm_atomic_helper_set_config,
> >> +    .page_flip              = drm_atomic_helper_page_flip,
> >
> > destroy is required, see drm_mode_config_cleanup()
>
> hi Dmitry:
> if define destroy in drm_crtc_funcs,
> it will make __drmm_crtc_init_with_planes unhappy

Ack, I missed that you have been using drmm_crtc_init. BTW, I checked
your code, you should be able to switch drm
drmm_crtc_alloc_with_planes().

>
> see:
> __printf(6, 0)
> static int __drmm_crtc_init_with_planes(struct drm_device *dev,
>                                         struct drm_crtc *crtc,
>                                         struct drm_plane *primary,
>                                         struct drm_plane *cursor,
>                                         const struct drm_crtc_funcs *funcs,
>                                         const char *name,
>                                         va_list args)
> {
>         int ret;
>
>         drm_WARN_ON(dev, funcs && funcs->destroy);
>
> ........
> }
>
> It should not need to be defined here, I think
>
> >
> >> +    .reset                  = vs_crtc_reset,
> >> +    .atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
> >> +    .atomic_destroy_state   = vs_crtc_atomic_destroy_state,
> >
> > please consider adding atomic_print_state to output driver-specific bits.
> >
> >> +    .enable_vblank          = vs_crtc_enable_vblank,
> >> +    .disable_vblank         = vs_crtc_disable_vblank,
> >> +};
> >> +
> >> +static u8 cal_pixel_bits(u32 bus_format)
> >
> > This looks like a generic helper code, which can go to a common place.
> >
> >> +{
> >> +    u8 bpp;
> >> +
> >> +    switch (bus_format) {
> >> +    case MEDIA_BUS_FMT_RGB565_1X16:
> >> +    case MEDIA_BUS_FMT_UYVY8_1X16:
> >> +            bpp = 16;
> >> +            break;
> >> +    case MEDIA_BUS_FMT_RGB666_1X18:
> >> +    case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> >> +            bpp = 18;
> >> +            break;
> >> +    case MEDIA_BUS_FMT_UYVY10_1X20:
> >> +            bpp = 20;
> >> +            break;
> >> +    case MEDIA_BUS_FMT_BGR888_1X24:
> >> +    case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> >> +    case MEDIA_BUS_FMT_YUV8_1X24:
> >> +            bpp = 24;
> >> +            break;
> >> +    case MEDIA_BUS_FMT_RGB101010_1X30:
> >> +    case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> >> +    case MEDIA_BUS_FMT_YUV10_1X30:
> >> +            bpp = 30;
> >> +            break;
> >> +    default:
> >> +            bpp = 24;
> >> +            break;
> >> +    }
> >> +
> >> +    return bpp;
> >> +}
> >> +
> >> +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
> >> +                              struct drm_atomic_state *state)
> >> +{
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
> >> +
> >> +    vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
> >> +
> >> +    vs_dc_enable(dc, crtc);
> >> +    drm_crtc_vblank_on(crtc);
> >> +}
> >> +
> >> +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
> >> +                               struct drm_atomic_state *state)
> >> +{
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    drm_crtc_vblank_off(crtc);
> >> +
> >> +    vs_dc_disable(dc, crtc);
> >> +
> >> +    if (crtc->state->event && !crtc->state->active) {
> >> +            spin_lock_irq(&crtc->dev->event_lock);
> >> +            drm_crtc_send_vblank_event(crtc, crtc->state->event);
> >> +            spin_unlock_irq(&crtc->dev->event_lock);
> >> +
> >> +            crtc->state->event = NULL;
> >
> > I think even should be cleared within the lock.
> >
> >> +    }
> >> +}
> >> +
> >> +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
> >> +                             struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
> >> +                                                                      crtc);
> >> +
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct device *dev = vs_crtc->dev;
> >> +    struct drm_property_blob *blob = crtc->state->gamma_lut;
> >> +    struct drm_color_lut *lut;
> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
> >> +
> >> +    if (crtc_state->color_mgmt_changed) {
> >> +            if (blob && blob->length) {
> >> +                    lut = blob->data;
> >> +                    vs_dc_set_gamma(dc, crtc, lut,
> >> +                                    blob->length / sizeof(*lut));
> >> +                    vs_dc_enable_gamma(dc, crtc, true);
> >> +            } else {
> >> +                    vs_dc_enable_gamma(dc, crtc, false);
> >> +            }
> >> +    }
> >> +}
> >> +
> >> +static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
> >> +                             struct drm_atomic_state *state)
> >> +{
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct drm_pending_vblank_event *event = crtc->state->event;
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    vs_dc_commit(dc);
> >> +
> >> +    if (event) {
> >> +            WARN_ON(drm_crtc_vblank_get(crtc) != 0);
> >> +
> >> +            spin_lock_irq(&crtc->dev->event_lock);
> >> +            drm_crtc_arm_vblank_event(crtc, event);
> >> +            spin_unlock_irq(&crtc->dev->event_lock);
> >> +            crtc->state->event = NULL;
> >
> > I think even should be cleared within the lock.
> >
> >> +    }
> >> +}
> >> +
> >> +static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
> >> +    .atomic_check = drm_crtc_helper_atomic_check,
> >> +    .atomic_enable  = vs_crtc_atomic_enable,
> >> +    .atomic_disable = vs_crtc_atomic_disable,
> >> +    .atomic_begin   = vs_crtc_atomic_begin,
> >> +    .atomic_flush   = vs_crtc_atomic_flush,
> >> +};
> >> +
> >> +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
> >> +    { VS_SINGLE_DC,                         "single dc mode" },
> >> +    { VS_MULTI_DC_PRIMARY,          "primary dc for multi dc mode" },
> >> +    { VS_MULTI_DC_SECONDARY,        "secondary dc for multi dc mode" },
> >> +};
> >> +
> >> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
> >> +                           struct vs_dc_info *info)
> >> +{
> >> +    struct vs_crtc *crtc;
> >> +    int ret;
> >> +
> >> +    if (!info)
> >> +            return NULL;
> >> +
> >> +    crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
> >> +    if (!crtc)
> >> +            return NULL;
> >> +
> >> +    ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
> >> +                                     NULL, NULL, &vs_crtc_funcs,
> >> +                                     info->name ? info->name : NULL);
> >
> > It might be better to add drmm_crtc_init() helper.
> >
> >> +    if (ret)
> >> +            return NULL;
> >> +
> >> +    drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
> >> +
> >> +    if (info->gamma_size) {
> >> +            ret = drm_mode_crtc_set_gamma_size(&crtc->base,
> >> +                                               info->gamma_size);
> >> +            if (ret)
> >> +                    return NULL;
> >> +
> >> +            drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
> >> +                                       info->gamma_size);
> >> +    }
> >> +
> >> +    crtc->max_bpc = info->max_bpc;
> >> +    crtc->color_formats = info->color_formats;
> >> +    return crtc;
> >> +}
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h
> >> new file mode 100644
> >> index 000000000..526dd63e5
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.h
> >> @@ -0,0 +1,43 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#ifndef __VS_CRTC_H__
> >> +#define __VS_CRTC_H__
> >> +
> >> +#include <drm/drm_crtc.h>
> >> +#include <drm/drm_crtc_helper.h>
> >> +
> >> +#include "vs_type.h"
> >> +
> >> +struct vs_crtc_state {
> >> +    struct drm_crtc_state base;
> >> +
> >> +    u32 output_fmt;
> >> +    u8 encoder_type;
> >> +    u8 bpp;
> >> +    bool underflow;
> >> +};
> >> +
> >> +struct vs_crtc {
> >> +    struct drm_crtc base;
> >> +    struct device *dev;
> >> +    unsigned int max_bpc;
> >> +    unsigned int color_formats;
> >> +};
> >> +
> >> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
> >> +                           struct vs_dc_info *info);
> >> +
> >> +static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc)
> >> +{
> >> +    return container_of(crtc, struct vs_crtc, base);
> >> +}
> >> +
> >> +static inline struct vs_crtc_state *
> >> +to_vs_crtc_state(struct drm_crtc_state *state)
> >> +{
> >> +    return container_of(state, struct vs_crtc_state, base);
> >> +}
> >> +#endif /* __VS_CRTC_H__ */
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
> >> new file mode 100644
> >> index 000000000..b5ab92d98
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc.c
> >> @@ -0,0 +1,1002 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#include <linux/component.h>
> >> +#include <linux/clk.h>
> >> +#include <linux/of.h>
> >> +#include <linux/of_device.h>
> >> +#include <linux/pm_runtime.h>
> >> +#include <linux/reset.h>
> >> +
> >> +#include <drm/drm_atomic.h>
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_blend.h>
> >> +#include <drm/drm_framebuffer.h>
> >> +#include <drm/drm_vblank.h>
> >> +#include <drm/vs_drm.h>
> >> +
> >> +#include "vs_dc_hw.h"
> >> +#include "vs_dc.h"
> >> +#include "vs_drv.h"
> >> +
> >> +static const char * const vout_clocks[] = {
> >> +    "noc_bus",
> >> +    "channel0",
> >> +    "channel1",
> >> +    "dc_core",
> >> +    "axi_core",
> >> +    "ahb",
> >> +    "hdmi_tx",
> >> +    "dc_parent",
> >> +
> >> +};
> >> +
> >> +static const char * const vout_resets[] = {
> >> +    "axi",
> >> +    "ahb",
> >> +    "core",
> >> +};
> >> +
> >> +static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb)
> >> +{
> >> +    u8 f = FORMAT_A8R8G8B8;
> >> +
> >> +    switch (format) {
> >> +    case DRM_FORMAT_XRGB4444:
> >> +    case DRM_FORMAT_RGBX4444:
> >> +    case DRM_FORMAT_XBGR4444:
> >> +    case DRM_FORMAT_BGRX4444:
> >> +            f = FORMAT_X4R4G4B4;
> >> +            break;
> >> +    case DRM_FORMAT_ARGB4444:
> >> +    case DRM_FORMAT_RGBA4444:
> >> +    case DRM_FORMAT_ABGR4444:
> >> +    case DRM_FORMAT_BGRA4444:
> >> +            f = FORMAT_A4R4G4B4;
> >> +            break;
> >> +    case DRM_FORMAT_XRGB1555:
> >> +    case DRM_FORMAT_RGBX5551:
> >> +    case DRM_FORMAT_XBGR1555:
> >> +    case DRM_FORMAT_BGRX5551:
> >> +            f = FORMAT_X1R5G5B5;
> >> +            break;
> >> +    case DRM_FORMAT_ARGB1555:
> >> +    case DRM_FORMAT_RGBA5551:
> >> +    case DRM_FORMAT_ABGR1555:
> >> +    case DRM_FORMAT_BGRA5551:
> >> +            f = FORMAT_A1R5G5B5;
> >> +            break;
> >> +    case DRM_FORMAT_RGB565:
> >> +    case DRM_FORMAT_BGR565:
> >> +            f = FORMAT_R5G6B5;
> >> +            break;
> >> +    case DRM_FORMAT_XRGB8888:
> >> +    case DRM_FORMAT_RGBX8888:
> >> +    case DRM_FORMAT_XBGR8888:
> >> +    case DRM_FORMAT_BGRX8888:
> >> +            f = FORMAT_X8R8G8B8;
> >> +            break;
> >> +    case DRM_FORMAT_ARGB8888:
> >> +    case DRM_FORMAT_RGBA8888:
> >> +    case DRM_FORMAT_ABGR8888:
> >> +    case DRM_FORMAT_BGRA8888:
> >> +            f = FORMAT_A8R8G8B8;
> >> +            break;
> >> +    case DRM_FORMAT_YUYV:
> >> +    case DRM_FORMAT_YVYU:
> >> +            f = FORMAT_YUY2;
> >> +            break;
> >> +    case DRM_FORMAT_UYVY:
> >> +    case DRM_FORMAT_VYUY:
> >> +            f = FORMAT_UYVY;
> >> +            break;
> >> +    case DRM_FORMAT_YUV420:
> >> +    case DRM_FORMAT_YVU420:
> >> +            f = FORMAT_YV12;
> >> +            break;
> >> +    case DRM_FORMAT_NV21:
> >> +            f = FORMAT_NV12;
> >> +            break;
> >> +    case DRM_FORMAT_NV16:
> >> +    case DRM_FORMAT_NV61:
> >> +            f = FORMAT_NV16;
> >> +            break;
> >> +    case DRM_FORMAT_P010:
> >> +            f = FORMAT_P010;
> >> +            break;
> >> +    case DRM_FORMAT_ARGB2101010:
> >> +    case DRM_FORMAT_RGBA1010102:
> >> +    case DRM_FORMAT_ABGR2101010:
> >> +    case DRM_FORMAT_BGRA1010102:
> >> +            f = FORMAT_A2R10G10B10;
> >> +            break;
> >> +    case DRM_FORMAT_NV12:
> >> +            f = FORMAT_NV12;
> >> +            break;
> >> +    case DRM_FORMAT_YUV444:
> >> +            f = FORMAT_YUV444;
> >> +            break;
> >> +    default:
> >> +            break;
> >> +    }
> >> +
> >> +    fb->format = f;
> >> +}
> >> +
> >> +static inline void update_swizzle(u32 format, struct dc_hw_fb *fb)
> >> +{
> >> +    fb->swizzle = SWIZZLE_ARGB;
> >> +    fb->uv_swizzle = 0;
> >> +
> >> +    switch (format) {
> >> +    case DRM_FORMAT_RGBX4444:
> >> +    case DRM_FORMAT_RGBA4444:
> >> +    case DRM_FORMAT_RGBX5551:
> >> +    case DRM_FORMAT_RGBA5551:
> >> +    case DRM_FORMAT_RGBX8888:
> >> +    case DRM_FORMAT_RGBA8888:
> >> +    case DRM_FORMAT_RGBA1010102:
> >> +            fb->swizzle = SWIZZLE_RGBA;
> >> +            break;
> >> +    case DRM_FORMAT_XBGR4444:
> >> +    case DRM_FORMAT_ABGR4444:
> >> +    case DRM_FORMAT_XBGR1555:
> >> +    case DRM_FORMAT_ABGR1555:
> >> +    case DRM_FORMAT_BGR565:
> >> +    case DRM_FORMAT_XBGR8888:
> >> +    case DRM_FORMAT_ABGR8888:
> >> +    case DRM_FORMAT_ABGR2101010:
> >> +            fb->swizzle = SWIZZLE_ABGR;
> >> +            break;
> >> +    case DRM_FORMAT_BGRX4444:
> >> +    case DRM_FORMAT_BGRA4444:
> >> +    case DRM_FORMAT_BGRX5551:
> >> +    case DRM_FORMAT_BGRA5551:
> >> +    case DRM_FORMAT_BGRX8888:
> >> +    case DRM_FORMAT_BGRA8888:
> >> +    case DRM_FORMAT_BGRA1010102:
> >> +            fb->swizzle = SWIZZLE_BGRA;
> >> +            break;
> >> +    case DRM_FORMAT_YVYU:
> >> +    case DRM_FORMAT_VYUY:
> >> +    case DRM_FORMAT_NV21:
> >> +    case DRM_FORMAT_NV61:
> >> +            fb->uv_swizzle = 1;
> >> +            break;
> >> +    default:
> >> +            break;
> >> +    }
> >> +}
> >> +
> >> +static inline void update_watermark(struct drm_property_blob *watermark,
> >> +                                struct dc_hw_fb *fb)
> >> +{
> >> +    struct drm_vs_watermark *data;
> >> +
> >> +    fb->water_mark = 0;
> >> +
> >> +    if (watermark) {
> >> +            data = watermark->data;
> >> +            fb->water_mark = data->watermark & 0xFFFFF;
> >> +    }
> >> +}
> >> +
> >> +static inline u8 to_vs_rotation(unsigned int rotation)
> >> +{
> >> +    u8 rot;
> >> +
> >> +    switch (rotation & DRM_MODE_REFLECT_MASK) {
> >> +    case DRM_MODE_REFLECT_X:
> >> +            rot = FLIP_X;
> >> +            return rot;
> >> +    case DRM_MODE_REFLECT_Y:
> >> +            rot = FLIP_Y;
> >> +            return rot;
> >> +    case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
> >> +            rot = FLIP_XY;
> >> +            return rot;
> >> +    default:
> >> +            break;
> >> +    }
> >> +
> >> +    switch (rotation & DRM_MODE_ROTATE_MASK) {
> >> +    case DRM_MODE_ROTATE_0:
> >> +            rot = ROT_0;
> >> +            break;
> >> +    case DRM_MODE_ROTATE_90:
> >> +            rot = ROT_90;
> >> +            break;
> >> +    case DRM_MODE_ROTATE_180:
> >> +            rot = ROT_180;
> >> +            break;
> >> +    case DRM_MODE_ROTATE_270:
> >> +            rot = ROT_270;
> >> +            break;
> >> +    default:
> >> +            rot = ROT_0;
> >> +            break;
> >> +    }
> >> +
> >> +    return rot;
> >> +}
> >> +
> >> +static inline u8 to_vs_yuv_color_space(u32 color_space)
> >> +{
> >> +    u8 cs;
> >> +
> >> +    switch (color_space) {
> >> +    case DRM_COLOR_YCBCR_BT601:
> >> +            cs = COLOR_SPACE_601;
> >> +            break;
> >> +    case DRM_COLOR_YCBCR_BT709:
> >> +            cs = COLOR_SPACE_709;
> >> +            break;
> >> +    case DRM_COLOR_YCBCR_BT2020:
> >> +            cs = COLOR_SPACE_2020;
> >> +            break;
> >> +    default:
> >> +            cs = COLOR_SPACE_601;
> >> +            break;
> >> +    }
> >> +
> >> +    return cs;
> >> +}
> >> +
> >> +static inline u8 to_vs_tile_mode(u64 modifier)
> >> +{
> >> +    if (modifier == DRM_FORMAT_MOD_VIVANTE_TILED)
> >> +            return DC_TILE_MODE4X4;
> >> +
> >> +    return (u8)(modifier & DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK);
> >> +}
> >> +
> >> +static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc)
> >> +{
> >> +    u8 panel_num = dc->hw.info->panel_num;
> >> +    u32 index = drm_crtc_index(crtc);
> >> +    int i;
> >> +
> >> +    for (i = 0; i < panel_num; i++) {
> >> +            if (index == dc->crtc[i]->base.index)
> >> +                    return i;
> >> +    }
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
> >> +                                      unsigned int alignment)
> >> +{
> >> +    struct vs_drm_device *priv = to_vs_drm_private(drm_dev);
> >> +
> >> +    if (alignment > priv->pitch_alignment)
> >> +            priv->pitch_alignment = alignment;
> >> +}
> >> +
> >> +static int plda_clk_rst_init(struct device *dev)
> >> +{
> >> +    int ret = 0;
> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
> >> +
> >> +    ret = clk_bulk_prepare_enable(dc->nclks, dc->clk_vout);
> >> +    if (ret) {
> >> +            dev_err(dev, "failed to enable clocks\n");
> >> +            return ret;
> >> +    }
> >> +
> >> +    ret = reset_control_bulk_deassert(dc->nrsts, dc->rst_vout);
> >> +    return ret;
> >> +}
> >> +
> >> +static void plda_clk_rst_deinit(struct device *dev)
> >> +{
> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
> >> +
> >> +    reset_control_bulk_assert(dc->nrsts, dc->rst_vout);
> >> +    clk_bulk_disable_unprepare(dc->nclks, dc->clk_vout);
> >> +}
> >> +
> >> +static void dc_deinit(struct device *dev)
> >> +{
> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
> >> +
> >> +    dc_hw_enable_interrupt(&dc->hw, 0);
> >> +    dc_hw_deinit(&dc->hw);
> >> +    plda_clk_rst_deinit(dev);
> >> +}
> >> +
> >> +static int dc_init(struct device *dev)
> >> +{
> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
> >> +    int ret;
> >> +
> >> +    dc->first_frame = true;
> >> +
> >> +    ret = plda_clk_rst_init(dev);
> >> +    if (ret < 0) {
> >> +            dev_err(dev, "failed to init dc clk reset: %d\n", ret);
> >> +            return ret;
> >> +    }
> >> +
> >> +    ret = dc_hw_init(&dc->hw);
> >> +    if (ret) {
> >> +            dev_err(dev, "failed to init DC HW\n");
> >> +            return ret;
> >> +    }
> >> +    return 0;
> >> +}
> >> +
> >> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
> >> +{
> >> +    struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
> >> +    struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> >> +    struct dc_hw_display display;
> >> +
> >> +    display.bus_format = crtc_state->output_fmt;
> >> +    display.h_active = mode->hdisplay;
> >> +    display.h_total = mode->htotal;
> >> +    display.h_sync_start = mode->hsync_start;
> >> +    display.h_sync_end = mode->hsync_end;
> >> +    if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> >> +            display.h_sync_polarity = true;
> >> +    else
> >> +            display.h_sync_polarity = false;
> >> +
> >> +    display.v_active = mode->vdisplay;
> >> +    display.v_total = mode->vtotal;
> >> +    display.v_sync_start = mode->vsync_start;
> >> +    display.v_sync_end = mode->vsync_end;
> >> +
> >> +    if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> >> +            display.v_sync_polarity = true;
> >> +    else
> >> +            display.v_sync_polarity = false;
> >> +
> >> +    display.id = to_vs_display_id(dc, crtc);
> >> +
> >> +    display.enable = true;
> >> +
> >> +    if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) {
> >> +            dc_hw_set_out(&dc->hw, OUT_DPI, display.id);
> >> +            clk_set_rate(dc->clk_vout[CLK_VOUT_SOC_PIX].clk, mode->clock * 1000);
> >> +            clk_set_parent(dc->clk_vout[CLK_VOUT_PIX1].clk,
> >> +                           dc->clk_vout[CLK_VOUT_SOC_PIX].clk);
> >> +    } else {
> >> +            dc_hw_set_out(&dc->hw, OUT_DP, display.id);
> >> +            clk_set_parent(dc->clk_vout[CLK_VOUT_PIX0].clk,
> >> +                           dc->clk_vout[CLK_VOUT_HDMI_PIX].clk);
> >> +    }
> >> +
> >> +    dc_hw_setup_display(&dc->hw, &display);
> >> +}
> >> +
> >> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc)
> >> +{
> >> +    struct dc_hw_display display;
> >> +
> >> +    display.id = to_vs_display_id(dc, crtc);
> >> +    display.enable = false;
> >> +
> >> +    dc_hw_setup_display(&dc->hw, &display);
> >> +}
> >> +
> >> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> >> +                 struct drm_color_lut *lut, unsigned int size)
> >> +{
> >> +    u16 i, r, g, b;
> >> +    u8 bits, id;
> >> +
> >> +    if (size != dc->hw.info->gamma_size) {
> >> +            drm_err(crtc->dev, "gamma size does not match!\n");
> >> +            return;
> >> +    }
> >> +
> >> +    id = to_vs_display_id(dc, crtc);
> >> +
> >> +    bits = dc->hw.info->gamma_bits;
> >> +    for (i = 0; i < size; i++) {
> >> +            r = drm_color_lut_extract(lut[i].red, bits);
> >> +            g = drm_color_lut_extract(lut[i].green, bits);
> >> +            b = drm_color_lut_extract(lut[i].blue, bits);
> >> +            dc_hw_update_gamma(&dc->hw, id, i, r, g, b);
> >> +    }
> >> +}
> >> +
> >> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> >> +                    bool enable)
> >> +{
> >> +    u8 id;
> >> +
> >> +    id = to_vs_display_id(dc, crtc);
> >> +    dc_hw_enable_gamma(&dc->hw, id, enable);
> >> +}
> >> +
> >> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable)
> >> +{
> >> +    dc_hw_enable_interrupt(&dc->hw, enable);
> >> +}
> >> +
> >> +static u32 calc_factor(u32 src, u32 dest)
> >> +{
> >> +    u32 factor = 1 << 16;
> >> +
> >> +    if (src > 1 && dest > 1)
> >> +            factor = ((src - 1) << 16) / (dest - 1);
> >> +
> >> +    return factor;
> >> +}
> >> +
> >> +static void update_scale(struct drm_plane_state *state, struct dc_hw_roi *roi,
> >> +                     struct dc_hw_scale *scale)
> >> +{
> >> +    int dst_w = drm_rect_width(&state->dst);
> >> +    int dst_h = drm_rect_height(&state->dst);
> >> +    int src_w, src_h, temp;
> >> +
> >> +    scale->enable = false;
> >> +
> >> +    if (roi->enable) {
> >> +            src_w = roi->width;
> >> +            src_h = roi->height;
> >> +    } else {
> >> +            src_w = drm_rect_width(&state->src) >> 16;
> >> +            src_h = drm_rect_height(&state->src) >> 16;
> >> +    }
> >> +
> >> +    if (drm_rotation_90_or_270(state->rotation)) {
> >> +            temp = src_w;
> >> +            src_w = src_h;
> >> +            src_h = temp;
> >> +    }
> >> +
> >> +    if (src_w != dst_w) {
> >> +            scale->scale_factor_x = calc_factor(src_w, dst_w);
> >> +            scale->enable = true;
> >> +    } else {
> >> +            scale->scale_factor_x = 1 << 16;
> >> +    }
> >> +    if (src_h != dst_h) {
> >> +            scale->scale_factor_y = calc_factor(src_h, dst_h);
> >> +            scale->enable = true;
> >> +    } else {
> >> +            scale->scale_factor_y = 1 << 16;
> >> +    }
> >> +}
> >> +
> >> +static void update_fb(struct vs_plane *plane, u8 display_id,
> >> +                  struct dc_hw_fb *fb, struct drm_plane_state *state)
> >> +{
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
> >> +    struct drm_framebuffer *drm_fb = state->fb;
> >> +    struct drm_rect *src = &state->src;
> >> +
> >> +    fb->display_id = display_id;
> >> +    fb->y_address = plane_state->dma_addr[0];
> >> +    fb->y_stride = drm_fb->pitches[0];
> >> +    if (drm_fb->format->format == DRM_FORMAT_YVU420) {
> >> +            fb->u_address = plane_state->dma_addr[2];
> >> +            fb->v_address = plane_state->dma_addr[1];
> >> +            fb->u_stride = drm_fb->pitches[2];
> >> +            fb->v_stride = drm_fb->pitches[1];
> >> +    } else {
> >> +            fb->u_address = plane_state->dma_addr[1];
> >> +            fb->v_address = plane_state->dma_addr[2];
> >> +            fb->u_stride = drm_fb->pitches[1];
> >> +            fb->v_stride = drm_fb->pitches[2];
> >> +    }
> >> +    fb->width = drm_rect_width(src) >> 16;
> >> +    fb->height = drm_rect_height(src) >> 16;
> >> +    fb->tile_mode = to_vs_tile_mode(drm_fb->modifier);
> >> +    fb->rotation = to_vs_rotation(state->rotation);
> >> +    fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding);
> >> +    fb->zpos = state->zpos;
> >> +    fb->enable = state->visible;
> >> +    update_format(drm_fb->format->format, drm_fb->modifier, fb);
> >> +    update_swizzle(drm_fb->format->format, fb);
> >> +    update_watermark(plane_state->watermark, fb);
> >> +    plane_state->status.tile_mode = fb->tile_mode;
> >> +}
> >> +
> >> +static void update_degamma(struct vs_dc *dc, struct vs_plane *plane,
> >> +                       struct vs_plane_state *plane_state)
> >> +{
> >> +    dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
> >> +    plane_state->degamma_changed = false;
> >> +}
> >> +
> >> +static void update_roi(struct vs_dc *dc, u8 id,
> >> +                   struct vs_plane_state *plane_state,
> >> +                   struct dc_hw_roi *roi,
> >> +                   struct drm_plane_state *state)
> >> +{
> >> +    struct drm_vs_roi *data;
> >> +    struct drm_rect *src = &state->src;
> >> +    u16 src_w = drm_rect_width(src) >> 16;
> >> +    u16 src_h = drm_rect_height(src) >> 16;
> >> +
> >> +    if (plane_state->roi) {
> >> +            data = plane_state->roi->data;
> >> +
> >> +            if (data->enable) {
> >> +                    roi->x = data->roi_x;
> >> +                    roi->y = data->roi_y;
> >> +                    roi->width = (data->roi_x + data->roi_w > src_w) ?
> >> +                                             (src_w - data->roi_x) : data->roi_w;
> >> +                    roi->height = (data->roi_y + data->roi_h > src_h) ?
> >> +                                              (src_h - data->roi_y) : data->roi_h;
> >> +                    roi->enable = true;
> >> +            } else {
> >> +                    roi->enable = false;
> >> +            }
> >> +
> >> +            dc_hw_update_roi(&dc->hw, id, roi);
> >> +    } else {
> >> +            roi->enable = false;
> >> +    }
> >> +}
> >> +
> >> +static void update_color_mgmt(struct vs_dc *dc, u8 id,
> >> +                          struct dc_hw_fb *fb,
> >> +                          struct vs_plane_state *plane_state)
> >> +{
> >> +    struct drm_vs_color_mgmt *data;
> >> +    struct dc_hw_colorkey colorkey;
> >> +
> >> +    if (plane_state->color_mgmt) {
> >> +            data = plane_state->color_mgmt->data;
> >> +
> >> +            fb->clear_enable = data->clear_enable;
> >> +            fb->clear_value = data->clear_value;
> >> +
> >> +            if (data->colorkey > data->colorkey_high)
> >> +                    data->colorkey = data->colorkey_high;
> >> +
> >> +            colorkey.colorkey = data->colorkey;
> >> +            colorkey.colorkey_high = data->colorkey_high;
> >> +            colorkey.transparency = (data->transparency) ?
> >> +                            DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE;
> >> +            dc_hw_update_colorkey(&dc->hw, id, &colorkey);
> >> +    }
> >> +}
> >> +
> >> +static void update_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                     struct drm_plane *drm_plane,
> >> +                     struct drm_atomic_state *drm_state)
> >> +{
> >> +    struct dc_hw_fb fb = {0};
> >> +    struct dc_hw_scale scale;
> >> +    struct dc_hw_position pos;
> >> +    struct dc_hw_blend blend;
> >> +    struct dc_hw_roi roi;
> >> +    struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> >> +                                                                       drm_plane);
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
> >> +    struct drm_rect *dest = &state->dst;
> >> +    bool dec_enable = false;
> >> +    u8 display_id = 0;
> >> +
> >> +    display_id = to_vs_display_id(dc, state->crtc);
> >> +    update_fb(plane, display_id, &fb, state);
> >> +    fb.dec_enable = dec_enable;
> >> +
> >> +    update_roi(dc, plane->id, plane_state, &roi, state);
> >> +
> >> +    update_scale(state, &roi, &scale);
> >> +
> >> +    if (plane_state->degamma_changed)
> >> +            update_degamma(dc, plane, plane_state);
> >> +
> >> +    pos.start_x = dest->x1;
> >> +    pos.start_y = dest->y1;
> >> +    pos.end_x = dest->x2;
> >> +    pos.end_y = dest->y2;
> >> +
> >> +    blend.alpha = (u8)(state->alpha >> 8);
> >> +    blend.blend_mode = (u8)(state->pixel_blend_mode);
> >> +
> >> +    update_color_mgmt(dc, plane->id, &fb, plane_state);
> >> +
> >> +    dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
> >> +}
> >> +
> >> +static void update_qos(struct vs_dc *dc, struct vs_plane *plane,
> >> +                   struct drm_plane *drm_plane,
> >> +                   struct drm_atomic_state *drm_state)
> >> +{
> >> +    struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> >> +                                                                       drm_plane);
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
> >> +    struct drm_vs_watermark *data;
> >> +    struct dc_hw_qos qos;
> >> +
> >> +    if (plane_state->watermark) {
> >> +            data = plane_state->watermark->data;
> >> +
> >> +            if (data->qos_high) {
> >> +                    if (data->qos_low > data->qos_high)
> >> +                            data->qos_low = data->qos_high;
> >> +
> >> +                    qos.low_value = data->qos_low & 0x0F;
> >> +                    qos.high_value = data->qos_high & 0x0F;
> >> +                    dc_hw_update_qos(&dc->hw, &qos);
> >> +            }
> >> +    }
> >> +}
> >> +
> >> +static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor)
> >> +{
> >> +    u8 size_type;
> >> +
> >> +    switch (state->crtc_w) {
> >> +    case 32:
> >> +            size_type = CURSOR_SIZE_32X32;
> >> +            break;
> >> +    case 64:
> >> +            size_type = CURSOR_SIZE_64X64;
> >> +            break;
> >> +    default:
> >> +            size_type = CURSOR_SIZE_32X32;
> >> +            break;
> >> +    }
> >> +
> >> +    cursor->size = size_type;
> >> +}
> >> +
> >> +static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                            struct drm_plane *drm_plane,
> >> +                            struct drm_atomic_state *drm_state)
> >> +{
> >> +    struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> >> +                                                                   drm_plane);
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
> >> +    struct drm_framebuffer *drm_fb = state->fb;
> >> +    struct dc_hw_cursor cursor;
> >> +
> >> +    cursor.address = plane_state->dma_addr[0];
> >> +    cursor.x = state->crtc_x;
> >> +    cursor.y = state->crtc_y;
> >> +    cursor.hot_x = drm_fb->hot_x;
> >> +    cursor.hot_y = drm_fb->hot_y;
> >> +    cursor.display_id = to_vs_display_id(dc, state->crtc);
> >> +    update_cursor_size(state, &cursor);
> >> +    cursor.enable = true;
> >> +
> >> +    dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
> >> +}
> >> +
> >> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                    struct drm_plane *drm_plane,
> >> +                    struct drm_atomic_state *drm_state)
> >> +{
> >> +    update_plane(dc, plane, drm_plane, drm_state);
> >> +    update_qos(dc, plane, drm_plane, drm_state);
> >> +}
> >> +
> >> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                           struct drm_plane *drm_plane,
> >> +                           struct drm_atomic_state *drm_state)
> >> +{
> >> +    update_cursor_plane(dc, plane, drm_plane, drm_state);
> >> +}
> >> +
> >> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                     struct drm_plane_state *old_state)
> >> +{
> >> +    struct dc_hw_fb fb = {0};
> >> +
> >> +    fb.enable = false;
> >> +    dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL, NULL, NULL);
> >> +}
> >> +
> >> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                            struct drm_plane_state *old_state)
> >> +{
> >> +    struct dc_hw_cursor cursor = {0};
> >> +
> >> +    cursor.enable = false;
> >> +    cursor.display_id = to_vs_display_id(dc, old_state->crtc);
> >> +    dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
> >> +}
> >> +
> >> +static bool vs_dc_mod_supported(const struct vs_plane_info *plane_info,
> >> +                            u64 modifier)
> >> +{
> >> +    const u64 *mods;
> >> +
> >> +    if (!plane_info->modifiers)
> >> +            return false;
> >> +
> >> +    for (mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID; mods++) {
> >> +            if (*mods == modifier)
> >> +                    return true;
> >> +    }
> >> +
> >> +    return false;
> >> +}
> >> +
> >> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
> >> +                  struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> >> +                                                                             plane);
> >> +
> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
> >> +    const struct vs_plane_info *plane_info;
> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
> >> +    struct drm_crtc_state *crtc_state;
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +
> >> +    plane_info = &dc->hw.info->planes[vs_plane->id];
> >> +
> >> +    if (fb->width < plane_info->min_width ||
> >> +        fb->width > plane_info->max_width ||
> >> +        fb->height < plane_info->min_height ||
> >> +        fb->height > plane_info->max_height)
> >> +            drm_err_once(plane->dev, "buffer size may not support on plane%d.\n",
> >> +                         vs_plane->id);
> >> +
> >> +    if (!vs_dc_mod_supported(plane_info, fb->modifier)) {
> >> +            drm_err(plane->dev, "unsupported modifier on plane%d.\n", vs_plane->id);
> >> +            return -EINVAL;
> >> +    }
> >> +
> >> +    crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
> >> +    return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
> >> +                                              plane_info->min_scale,
> >> +                                              plane_info->max_scale,
> >> +                                              true, true);
> >> +}
> >> +
> >> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
> >> +                         struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> >> +                                                                       plane);
> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
> >> +    const struct vs_plane_info *plane_info;
> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
> >> +    struct drm_crtc_state *crtc_state;
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +
> >> +    plane_info = &dc->hw.info->planes[vs_plane->id];
> >> +
> >> +    if (fb->width < plane_info->min_width ||
> >> +        fb->width > plane_info->max_width ||
> >> +        fb->height < plane_info->min_height ||
> >> +        fb->height > plane_info->max_height)
> >> +            drm_err_once(plane->dev, "buffer size may not support on plane%d.\n", vs_plane->id);
> >> +
> >> +    crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
> >> +    if (IS_ERR(crtc_state))
> >> +            return -EINVAL;
> >> +
> >> +    return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
> >> +                                            plane_info->min_scale,
> >> +                                            plane_info->max_scale,
> >> +                                            true, true);
> >> +}
> >> +
> >> +static void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
> >> +{
> >> +    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
> >> +
> >> +    drm_crtc_handle_vblank(crtc);
> >> +
> >> +    vs_crtc_state->underflow = underflow;
> >> +}
> >> +
> >> +static irqreturn_t dc_isr(int irq, void *data)
> >> +{
> >> +    struct vs_dc *dc = data;
> >> +    struct vs_dc_info *dc_info = dc->hw.info;
> >> +    u32 i, ret;
> >> +
> >> +    ret = dc_hw_get_interrupt(&dc->hw);
> >> +
> >> +    for (i = 0; i < dc_info->panel_num; i++)
> >> +            vs_crtc_handle_vblank(&dc->crtc[i]->base, dc_hw_check_underflow(&dc->hw));
> >> +
> >> +    return IRQ_HANDLED;
> >> +}
> >> +
> >> +void vs_dc_commit(struct vs_dc *dc)
> >> +{
> >> +    dc_hw_enable_shadow_register(&dc->hw, false);
> >> +
> >> +    dc_hw_commit(&dc->hw);
> >> +
> >> +    if (dc->first_frame)
> >> +            dc->first_frame = false;
> >> +
> >> +    dc_hw_enable_shadow_register(&dc->hw, true);
> >> +}
> >> +
> >> +static int dc_bind(struct device *dev, struct device *master, void *data)
> >> +{
> >> +    struct drm_device *drm_dev = data;
> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
> >> +    struct device_node *port;
> >> +    struct vs_crtc *crtc;
> >> +    struct vs_dc_info *dc_info;
> >> +    struct vs_plane *plane;
> >> +    struct vs_plane_info *plane_info;
> >> +    int i, ret;
> >> +    u32 ctrc_mask = 0;
> >> +
> >> +    if (!drm_dev || !dc) {
> >> +            dev_err(dev, "devices are not created.\n");
> >> +            return -ENODEV;
> >> +    }
> >> +
> >> +    ret = dc_init(dev);
> >> +    if (ret < 0) {
> >> +            drm_err(drm_dev, "Failed to initialize DC hardware.\n");
> >> +            return ret;
> >> +    }
> >> +
> >> +    port = of_get_child_by_name(dev->of_node, "port");
> >> +    if (!port) {
> >> +            drm_err(drm_dev, "no port node found\n");
> >> +            return -ENODEV;
> >> +    }
> >> +    of_node_put(port);
> >> +
> >> +    dc_info = dc->hw.info;
> >> +
> >> +    for (i = 0; i < dc_info->panel_num; i++) {
> >> +            crtc = vs_crtc_create(drm_dev, dc_info);
> >> +            if (!crtc) {
> >> +                    drm_err(drm_dev, "Failed to create CRTC.\n");
> >> +                    ret = -ENOMEM;
> >> +                    return ret;
> >> +            }
> >> +
> >> +            crtc->base.port = port;
> >> +            crtc->dev = dev;
> >> +            dc->crtc[i] = crtc;
> >> +            ctrc_mask |= drm_crtc_mask(&crtc->base);
> >> +    }
> >> +
> >> +    for (i = 0; i < dc_info->plane_num; i++) {
> >> +            plane_info = (struct vs_plane_info *)&dc_info->planes[i];
> >> +
> >> +            if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
> >> +                    plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
> >> +                                            drm_crtc_mask(&dc->crtc[0]->base));
> >> +            } else if (!strcmp(plane_info->name, "Primary_1") ||
> >> +                               !strcmp(plane_info->name, "Cursor_1")) {
> >> +                    plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
> >> +                                            drm_crtc_mask(&dc->crtc[1]->base));
> >
> > I'd say, it's not typical to use name to distinguish objects. Maybe you
> > can add indices to the info structure and drop the names instead?
> >
> >
> >> +            } else {
> >> +                    plane = vs_plane_create(drm_dev, plane_info,
> >> +                                            dc_info->layer_num, ctrc_mask);
> >> +            }
> >> +
> >> +            if (IS_ERR(plane)) {
> >> +                    dev_err(dev, "failed to construct plane\n");
> >> +                    return PTR_ERR(plane);
> >> +            }
> >> +
> >> +            plane->id = i;
> >> +            dc->planes[i].id = plane_info->id;
> >> +
> >> +            if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
> >> +                    if (!strcmp(plane_info->name, "Primary"))
> >> +                            dc->crtc[0]->base.primary = &plane->base;
> >> +                    else
> >> +                            dc->crtc[1]->base.primary = &plane->base;
> >
> > This definitely urges for an index in the plane info / plane_state.
> >
> >> +                    drm_dev->mode_config.min_width = plane_info->min_width;
> >> +                    drm_dev->mode_config.min_height =
> >> +                                                    plane_info->min_height;
> >> +                    drm_dev->mode_config.max_width = plane_info->max_width;
> >> +                    drm_dev->mode_config.max_height =
> >> +                                                    plane_info->max_height;
> >> +            }
> >> +
> >> +            if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
> >> +                    if (!strcmp(plane_info->name, "Cursor"))
> >> +                            dc->crtc[0]->base.cursor = &plane->base;
> >> +                    else
> >> +                            dc->crtc[1]->base.cursor = &plane->base;
> >> +                    drm_dev->mode_config.cursor_width =
> >> +                                                    plane_info->max_width;
> >> +                    drm_dev->mode_config.cursor_height =
> >> +                                                    plane_info->max_height;
> >> +            }
> >> +    }
> >> +
> >> +    vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
> >> +    return 0;
> >> +}
> >> +
> >> +static void dc_unbind(struct device *dev, struct device *master, void *data)
> >> +{
> >> +    dc_deinit(dev);
> >> +}
> >> +
> >> +const struct component_ops dc_component_ops = {
> >> +    .bind = dc_bind,
> >> +    .unbind = dc_unbind,
> >> +};
> >> +
> >> +static const struct of_device_id dc_driver_dt_match[] = {
> >> +    { .compatible = "starfive,jh7110-dc8200", },
> >> +    {},
> >> +};
> >> +MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
> >> +
> >> +static int dc_probe(struct platform_device *pdev)
> >> +{
> >> +    struct device *dev = &pdev->dev;
> >> +    struct vs_dc *dc;
> >> +    int irq, ret, i;
> >> +
> >> +    dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
> >> +    if (!dc)
> >> +            return -ENOMEM;
> >> +
> >> +    dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
> >> +    if (IS_ERR(dc->hw.hi_base))
> >> +            return PTR_ERR(dc->hw.hi_base);
> >> +
> >> +    dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
> >> +    if (IS_ERR(dc->hw.reg_base))
> >> +            return PTR_ERR(dc->hw.reg_base);
> >> +
> >> +    dc->nclks = ARRAY_SIZE(dc->clk_vout);
> >> +    for (i = 0; i < dc->nclks; ++i)
> >> +            dc->clk_vout[i].id = vout_clocks[i];
> >> +    ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
> >> +    if (ret) {
> >> +            dev_err(dev, "Failed to get clk controls\n");
> >> +            return ret;
> >> +    }
> >> +
> >> +    dc->nrsts = ARRAY_SIZE(dc->rst_vout);
> >> +    for (i = 0; i < dc->nrsts; ++i)
> >> +            dc->rst_vout[i].id = vout_resets[i];
> >> +    ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
> >> +                                             dc->rst_vout);
> >> +    if (ret) {
> >> +            dev_err(dev, "Failed to get reset controls\n");
> >> +            return ret;
> >> +    }
> >> +
> >> +    irq = platform_get_irq(pdev, 0);
> >> +
> >> +    ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
> >
> > Are you ready to handle the IRQ at this point?
> >
> >> +    if (ret < 0) {
> >> +            dev_err(dev, "Failed to install irq:%u.\n", irq);
> >> +            return ret;
> >> +    }
> >> +
> >> +    dev_set_drvdata(dev, dc);
> >> +
> >> +    return component_add(dev, &dc_component_ops);
> >> +}
> >> +
> >> +static int dc_remove(struct platform_device *pdev)
> >> +{
> >> +    struct device *dev = &pdev->dev;
> >> +
> >> +    component_del(dev, &dc_component_ops);
> >> +
> >> +    dev_set_drvdata(dev, NULL);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +struct platform_driver dc_platform_driver = {
> >> +    .probe = dc_probe,
> >> +    .remove = dc_remove,
> >> +    .driver = {
> >> +            .name = "vs-dc",
> >> +            .of_match_table = of_match_ptr(dc_driver_dt_match),
> >> +    },
> >> +};
> >> +
> >> +MODULE_AUTHOR("StarFive Corporation");
> >> +MODULE_DESCRIPTION("VeriSilicon DC Driver");
> >> +MODULE_LICENSE("GPL");
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
> >> new file mode 100644
> >> index 000000000..8e96fd32c
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc.h
> >> @@ -0,0 +1,80 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#ifndef __VS_DC_H__
> >> +#define __VS_DC_H__
> >> +
> >> +#include <linux/clk.h>
> >> +#include <linux/mm_types.h>
> >> +#include <linux/reset.h>
> >> +
> >> +#include <drm/drm_fourcc.h>
> >> +#include <drm/drm_modes.h>
> >> +
> >> +#include "vs_crtc.h"
> >> +#include "vs_dc_hw.h"
> >> +#include "vs_plane.h"
> >> +
> >> +struct vs_dc_plane {
> >> +    enum dc_hw_plane_id id;
> >> +};
> >> +
> >> +enum vout_clk {
> >> +    CLK_VOUT_NOC_DISP = 0,
> >> +    CLK_VOUT_PIX0,
> >> +    CLK_VOUT_PIX1,
> >> +    CLK_VOUT_CORE,
> >> +    CLK_VOUT_AXI,
> >> +    CLK_VOUT_AHB,
> >> +    CLK_VOUT_HDMI_PIX,
> >> +    CLK_VOUT_SOC_PIX,
> >> +    CLK_VOUT_NUM
> >> +};
> >> +
> >> +enum rst_vout {
> >> +    RST_VOUT_AXI = 0,
> >> +    RST_VOUT_AHB,
> >> +    RST_VOUT_CORE,
> >> +    RST_VOUT_NUM
> >> +};
> >> +
> >> +struct vs_dc {
> >> +    struct vs_crtc          *crtc[DC_DISPLAY_NUM];
> >> +    struct dc_hw            hw;
> >> +    bool                    first_frame;
> >> +
> >> +    struct vs_dc_plane      planes[PLANE_NUM];
> >> +    struct clk_bulk_data    clk_vout[CLK_VOUT_NUM];
> >> +    int                     nclks;
> >> +    struct reset_control_bulk_data rst_vout[RST_VOUT_NUM];
> >> +    int                     nrsts;
> >> +};
> >> +
> >> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc);
> >> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc);
> >> +
> >> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> >> +                 struct drm_color_lut *lut, unsigned int size);
> >> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc, bool enable);
> >> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable);
> >> +void vs_dc_commit(struct vs_dc *dc);
> >> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                    struct drm_plane *drm_plane,
> >> +                    struct drm_atomic_state *drm_state);
> >> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                     struct drm_plane_state *old_state);
> >> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
> >> +                  struct drm_atomic_state *state);
> >> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                           struct drm_plane *drm_plane,
> >> +                           struct drm_atomic_state *drm_state);
> >> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> >> +                            struct drm_plane_state *old_state);
> >> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
> >> +                         struct drm_atomic_state *state);
> >> +
> >> +extern struct platform_driver dc_platform_driver;
> >> +
> >> +#endif /* __VS_DC_H__ */
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
> >> new file mode 100644
> >> index 000000000..e5acb7eb4
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
> >> @@ -0,0 +1,1959 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#include <linux/bits.h>
> >> +#include <linux/io.h>
> >> +#include <linux/media-bus-format.h>
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_blend.h>
> >> +#include <drm/drm_fourcc.h>
> >> +#include <drm/vs_drm.h>
> >> +
> >> +#include "vs_dc_hw.h"
> >> +#include "vs_type.h"
> >> +
> >> +static const u32 horkernel[] = {
> >> +    0x00000000, 0x20000000, 0x00002000, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x2b981468,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x10f00000,
> >> +    0x00002f10, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x3781087f, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x06660000, 0x0000399a, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x01470000,
> >> +    0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x00004000, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x20002000, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x27e1181f,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x14680000,
> >> +    0x00002b98, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x2f1010f0, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x0dc70000, 0x00003239, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x087f0000, 0x00003781, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x399a0666, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x3cc4033c,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x021f0000,
> >> +};
> >> +
> >> +#define H_COEF_SIZE (sizeof(horkernel) / sizeof(u32))
> >> +
> >> +static const u32 verkernel[] = {
> >> +    0x00000000, 0x20000000, 0x00002000, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x2b981468,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x10f00000,
> >> +    0x00002f10, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x3781087f, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x06660000, 0x0000399a, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
> >> +    0x00000000, 0x00000000, 0x00000000, 0x01470000,
> >> +    0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
> >> +    0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
> >> +    0x00000000, 0x00000000, 0x00004000, 0x00000000,
> >> +    0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab,
> >> +    0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
> >> +    0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> >> +};
> >> +
> >> +#define V_COEF_SIZE (sizeof(verkernel) / sizeof(u32))
> >> +
> >> +/*
> >> + * RGB 709->2020 conversion parameters
> >> + */
> >> +static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = {
> >> +    10279,  5395,   709,
> >> +    1132,   15065,  187,
> >> +    269,    1442,   14674
> >> +};
> >> +
> >> +/*
> >> + * YUV601 to RGB conversion parameters
> >> + * YUV2RGB[0]  - [8] : C0 - C8;
> >> + * YUV2RGB[9]  - [11]: D0 - D2;
> >> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> >> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> >> + */
> >> +static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> >> +    1196,   0,                      1640,   1196,
> >> +    -404,   -836,           1196,   2076,
> >> +    0,              -916224,        558336, -1202944,
> >> +    64,              940,           64,             960
> >> +};
> >> +
> >> +/*
> >> + * YUV709 to RGB conversion parameters
> >> + * YUV2RGB[0]  - [8] : C0 - C8;
> >> + * YUV2RGB[9]  - [11]: D0 - D2;
> >> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> >> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> >> + */
> >> +static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> >> +    1196,           0,              1844,   1196,
> >> +    -220,           -548,   1196,   2172,
> >> +    0,                      -1020672, 316672,  -1188608,
> >> +    64,                     940,            64,             960
> >> +};
> >> +
> >> +/*
> >> + * YUV2020 to RGB conversion parameters
> >> + * YUV2RGB[0]  - [8] : C0 - C8;
> >> + * YUV2RGB[9]  - [11]: D0 - D2;
> >> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> >> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> >> + */
> >> +static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> >> +    1196, 0, 1724, 1196,
> >> +    -192, -668, 1196, 2200,
> >> +    0, -959232, 363776, -1202944,
> >> +    64, 940, 64, 960
> >> +};
> >> +
> >> +/*
> >> + * RGB to YUV2020 conversion parameters
> >> + * RGB2YUV[0] - [8] : C0 - C8;
> >> + * RGB2YUV[9] - [11]: D0 - D2;
> >> + */
> >> +static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = {
> >> +    230,    594,    52,
> >> +    -125,  -323,    448,
> >> +    448,   -412,   -36,
> >> +    64,             512,    512
> >> +};
> >> +
> >> +/*
> >> + * Degamma table for 709 color space data.
> >> + */
> >> +static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005,
> >> +    0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024,
> >> +    0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064,
> >> +    0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce,
> >> +    0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166,
> >> +    0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230,
> >> +    0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331,
> >> +    0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c,
> >> +    0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
> >> +    0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d,
> >> +    0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999,
> >> +    0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb,
> >> +    0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65,
> >> +    0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a,
> >> +    0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b,
> >> +    0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc,
> >> +    0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e,
> >> +    0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
> >> +    0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e,
> >> +    0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf,
> >> +    0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea,
> >> +    0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f,
> >> +    0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930,
> >> +    0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f,
> >> +    0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee,
> >> +    0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde,
> >> +    0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
> >> +    0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9,
> >> +    0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406,
> >> +    0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a,
> >> +    0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576,
> >> +    0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd,
> >> +    0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
> >> +};
> >> +
> >> +/*
> >> + * Degamma table for 2020 color space data.
> >> + */
> >> +static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> >> +    0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
> >> +    0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
> >> +    0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006,
> >> +    0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a,
> >> +    0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f,
> >> +    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
> >> +    0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024,
> >> +    0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035,
> >> +    0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d,
> >> +    0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d,
> >> +    0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098,
> >> +    0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3,
> >> +    0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123,
> >> +    0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d,
> >> +    0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
> >> +    0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6,
> >> +    0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1,
> >> +    0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d,
> >> +    0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6,
> >> +    0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920,
> >> +    0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a,
> >> +    0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033,
> >> +    0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593,
> >> +    0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
> >> +    0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656,
> >> +    0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332,
> >> +    0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480,
> >> +    0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1,
> >> +    0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99,
> >> +    0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
> >> +};
> >> +
> >> +/* one is for primary plane and the other is for all overlay planes */
> >> +static const struct dc_hw_plane_reg dc_plane_reg[] = {
> >> +    {
> >> +    .y_address              = DC_FRAMEBUFFER_ADDRESS,
> >> +    .u_address              = DC_FRAMEBUFFER_U_ADDRESS,
> >> +    .v_address              = DC_FRAMEBUFFER_V_ADDRESS,
> >> +    .y_stride               = DC_FRAMEBUFFER_STRIDE,
> >> +    .u_stride               = DC_FRAMEBUFFER_U_STRIDE,
> >> +    .v_stride               = DC_FRAMEBUFFER_V_STRIDE,
> >> +    .size                   = DC_FRAMEBUFFER_SIZE,
> >> +    .top_left               = DC_FRAMEBUFFER_TOP_LEFT,
> >> +    .bottom_right   = DC_FRAMEBUFFER_BOTTOM_RIGHT,
> >> +    .scale_factor_x                 = DC_FRAMEBUFFER_SCALE_FACTOR_X,
> >> +    .scale_factor_y                 = DC_FRAMEBUFFER_SCALE_FACTOR_Y,
> >> +    .h_filter_coef_index    = DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
> >> +    .h_filter_coef_data             = DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
> >> +    .v_filter_coef_index    = DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
> >> +    .v_filter_coef_data             = DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
> >> +    .init_offset                    = DC_FRAMEBUFFER_INIT_OFFSET,
> >> +    .color_key                              = DC_FRAMEBUFFER_COLOR_KEY,
> >> +    .color_key_high                 = DC_FRAMEBUFFER_COLOR_KEY_HIGH,
> >> +    .clear_value                    = DC_FRAMEBUFFER_CLEAR_VALUE,
> >> +    .color_table_index              = DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
> >> +    .color_table_data               = DC_FRAMEBUFFER_COLOR_TABLE_DATA,
> >> +    .scale_config                   = DC_FRAMEBUFFER_SCALE_CONFIG,
> >> +    .water_mark                             = DC_FRAMEBUFFER_WATER_MARK,
> >> +    .degamma_index                  = DC_FRAMEBUFFER_DEGAMMA_INDEX,
> >> +    .degamma_data                   = DC_FRAMEBUFFER_DEGAMMA_DATA,
> >> +    .degamma_ex_data                = DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
> >> +    .src_global_color               = DC_FRAMEBUFFER_SRC_GLOBAL_COLOR,
> >> +    .dst_global_color               = DC_FRAMEBUFFER_DST_GLOBAL_COLOR,
> >> +    .blend_config                   = DC_FRAMEBUFFER_BLEND_CONFIG,
> >> +    .roi_origin                             = DC_FRAMEBUFFER_ROI_ORIGIN,
> >> +    .roi_size                               = DC_FRAMEBUFFER_ROI_SIZE,
> >> +    .yuv_to_rgb_coef0                       = DC_FRAMEBUFFER_YUVTORGB_COEF0,
> >> +    .yuv_to_rgb_coef1                       = DC_FRAMEBUFFER_YUVTORGB_COEF1,
> >> +    .yuv_to_rgb_coef2                       = DC_FRAMEBUFFER_YUVTORGB_COEF2,
> >> +    .yuv_to_rgb_coef3                       = DC_FRAMEBUFFER_YUVTORGB_COEF3,
> >> +    .yuv_to_rgb_coef4                       = DC_FRAMEBUFFER_YUVTORGB_COEF4,
> >> +    .yuv_to_rgb_coefd0                      = DC_FRAMEBUFFER_YUVTORGB_COEFD0,
> >> +    .yuv_to_rgb_coefd1                      = DC_FRAMEBUFFER_YUVTORGB_COEFD1,
> >> +    .yuv_to_rgb_coefd2                      = DC_FRAMEBUFFER_YUVTORGB_COEFD2,
> >> +    .y_clamp_bound                          = DC_FRAMEBUFFER_Y_CLAMP_BOUND,
> >> +    .uv_clamp_bound                         = DC_FRAMEBUFFER_UV_CLAMP_BOUND,
> >> +    .rgb_to_rgb_coef0                       = DC_FRAMEBUFFER_RGBTORGB_COEF0,
> >> +    .rgb_to_rgb_coef1                       = DC_FRAMEBUFFER_RGBTORGB_COEF1,
> >> +    .rgb_to_rgb_coef2                       = DC_FRAMEBUFFER_RGBTORGB_COEF2,
> >> +    .rgb_to_rgb_coef3                       = DC_FRAMEBUFFER_RGBTORGB_COEF3,
> >> +    .rgb_to_rgb_coef4                       = DC_FRAMEBUFFER_RGBTORGB_COEF4,
> >> +    },
> >> +    {
> >> +    .y_address              = DC_OVERLAY_ADDRESS,
> >> +    .u_address              = DC_OVERLAY_U_ADDRESS,
> >> +    .v_address              = DC_OVERLAY_V_ADDRESS,
> >> +    .y_stride               = DC_OVERLAY_STRIDE,
> >> +    .u_stride               = DC_OVERLAY_U_STRIDE,
> >> +    .v_stride               = DC_OVERLAY_V_STRIDE,
> >> +    .size                   = DC_OVERLAY_SIZE,
> >> +    .top_left               = DC_OVERLAY_TOP_LEFT,
> >> +    .bottom_right   = DC_OVERLAY_BOTTOM_RIGHT,
> >> +    .scale_factor_x = DC_OVERLAY_SCALE_FACTOR_X,
> >> +    .scale_factor_y = DC_OVERLAY_SCALE_FACTOR_Y,
> >> +    .h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
> >> +    .h_filter_coef_data  = DC_OVERLAY_H_FILTER_COEF_DATA,
> >> +    .v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
> >> +    .v_filter_coef_data  = DC_OVERLAY_V_FILTER_COEF_DATA,
> >> +    .init_offset             = DC_OVERLAY_INIT_OFFSET,
> >> +    .color_key                       = DC_OVERLAY_COLOR_KEY,
> >> +    .color_key_high                 = DC_OVERLAY_COLOR_KEY_HIGH,
> >> +    .clear_value             = DC_OVERLAY_CLEAR_VALUE,
> >> +    .color_table_index       = DC_OVERLAY_COLOR_TABLE_INDEX,
> >> +    .color_table_data        = DC_OVERLAY_COLOR_TABLE_DATA,
> >> +    .scale_config            = DC_OVERLAY_SCALE_CONFIG,
> >> +    .water_mark                             = DC_OVERLAY_WATER_MARK,
> >> +    .degamma_index           = DC_OVERLAY_DEGAMMA_INDEX,
> >> +    .degamma_data            = DC_OVERLAY_DEGAMMA_DATA,
> >> +    .degamma_ex_data         = DC_OVERLAY_DEGAMMA_EX_DATA,
> >> +    .src_global_color        = DC_OVERLAY_SRC_GLOBAL_COLOR,
> >> +    .dst_global_color        = DC_OVERLAY_DST_GLOBAL_COLOR,
> >> +    .blend_config            = DC_OVERLAY_BLEND_CONFIG,
> >> +    .roi_origin                             = DC_OVERLAY_ROI_ORIGIN,
> >> +    .roi_size                               = DC_OVERLAY_ROI_SIZE,
> >> +    .yuv_to_rgb_coef0                = DC_OVERLAY_YUVTORGB_COEF0,
> >> +    .yuv_to_rgb_coef1                = DC_OVERLAY_YUVTORGB_COEF1,
> >> +    .yuv_to_rgb_coef2                = DC_OVERLAY_YUVTORGB_COEF2,
> >> +    .yuv_to_rgb_coef3                = DC_OVERLAY_YUVTORGB_COEF3,
> >> +    .yuv_to_rgb_coef4                       = DC_OVERLAY_YUVTORGB_COEF4,
> >> +    .yuv_to_rgb_coefd0                      = DC_OVERLAY_YUVTORGB_COEFD0,
> >> +    .yuv_to_rgb_coefd1                      = DC_OVERLAY_YUVTORGB_COEFD1,
> >> +    .yuv_to_rgb_coefd2                      = DC_OVERLAY_YUVTORGB_COEFD2,
> >> +    .y_clamp_bound           = DC_OVERLAY_Y_CLAMP_BOUND,
> >> +    .uv_clamp_bound          = DC_OVERLAY_UV_CLAMP_BOUND,
> >> +    .rgb_to_rgb_coef0                = DC_OVERLAY_RGBTORGB_COEF0,
> >> +    .rgb_to_rgb_coef1                = DC_OVERLAY_RGBTORGB_COEF1,
> >> +    .rgb_to_rgb_coef2                = DC_OVERLAY_RGBTORGB_COEF2,
> >> +    .rgb_to_rgb_coef3                = DC_OVERLAY_RGBTORGB_COEF3,
> >> +    .rgb_to_rgb_coef4                = DC_OVERLAY_RGBTORGB_COEF4,
> >> +    },
> >> +};
> >> +
> >> +static const u32 primary_overlay_format0[] = {
> >> +    DRM_FORMAT_XRGB4444,
> >> +    DRM_FORMAT_XBGR4444,
> >> +    DRM_FORMAT_RGBX4444,
> >> +    DRM_FORMAT_BGRX4444,
> >> +    DRM_FORMAT_ARGB4444,
> >> +    DRM_FORMAT_ABGR4444,
> >> +    DRM_FORMAT_RGBA4444,
> >> +    DRM_FORMAT_BGRA4444,
> >> +    DRM_FORMAT_XRGB1555,
> >> +    DRM_FORMAT_XBGR1555,
> >> +    DRM_FORMAT_RGBX5551,
> >> +    DRM_FORMAT_BGRX5551,
> >> +    DRM_FORMAT_ARGB1555,
> >> +    DRM_FORMAT_ABGR1555,
> >> +    DRM_FORMAT_RGBA5551,
> >> +    DRM_FORMAT_BGRA5551,
> >> +    DRM_FORMAT_RGB565,
> >> +    DRM_FORMAT_BGR565,
> >> +    DRM_FORMAT_XRGB8888,
> >> +    DRM_FORMAT_XBGR8888,
> >> +    DRM_FORMAT_RGBX8888,
> >> +    DRM_FORMAT_BGRX8888,
> >> +    DRM_FORMAT_ARGB8888,
> >> +    DRM_FORMAT_ABGR8888,
> >> +    DRM_FORMAT_RGBA8888,
> >> +    DRM_FORMAT_BGRA8888,
> >> +    DRM_FORMAT_ARGB2101010,
> >> +    DRM_FORMAT_ABGR2101010,
> >> +    DRM_FORMAT_RGBA1010102,
> >> +    DRM_FORMAT_BGRA1010102,
> >> +    DRM_FORMAT_YUYV,
> >> +    DRM_FORMAT_YVYU,
> >> +    DRM_FORMAT_UYVY,
> >> +    DRM_FORMAT_VYUY,
> >> +    DRM_FORMAT_YVU420,
> >> +    DRM_FORMAT_YUV420,
> >> +    DRM_FORMAT_NV12,
> >> +    DRM_FORMAT_NV21,
> >> +    DRM_FORMAT_NV16,
> >> +    DRM_FORMAT_NV61,
> >> +    DRM_FORMAT_P010,
> >> +};
> >> +
> >> +static const u32 primary_overlay_format1[] = {
> >> +    DRM_FORMAT_XRGB8888,
> >> +    DRM_FORMAT_XBGR8888,
> >> +    DRM_FORMAT_RGBX8888,
> >> +    DRM_FORMAT_BGRX8888,
> >> +    DRM_FORMAT_ARGB8888,
> >> +    DRM_FORMAT_ABGR8888,
> >> +    DRM_FORMAT_RGBA8888,
> >> +    DRM_FORMAT_BGRA8888,
> >> +    DRM_FORMAT_ARGB2101010,
> >> +    DRM_FORMAT_ABGR2101010,
> >> +    DRM_FORMAT_RGBA1010102,
> >> +    DRM_FORMAT_BGRA1010102,
> >> +    DRM_FORMAT_NV12,
> >> +    DRM_FORMAT_NV21,
> >> +    DRM_FORMAT_YUV444,
> >> +};
> >> +
> >> +static const u32 cursor_formats[] = {
> >> +    DRM_FORMAT_ARGB8888
> >> +};
> >> +
> >> +static const u64 format_modifier0[] = {
> >> +    DRM_FORMAT_MOD_LINEAR,
> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8),
> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8),
> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X8),
> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X4),
> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4),
> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8),
> >> +    DRM_FORMAT_MOD_INVALID
> >> +};
> >> +
> >> +static const u64 secondary_format_modifiers[] = {
> >> +    DRM_FORMAT_MOD_LINEAR,
> >> +    DRM_FORMAT_MOD_INVALID
> >> +};
> >> +
> >> +#define FRAC_16_16(mult, div)        (((mult) << 16) / (div))
> >> +
> >> +static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = {
> >> +    {
> >> +            /* DC_REV_0 */
> >> +            {
> >> +            .name                   = "Primary",
> >
> > I'd say that the indentation is strange here.
> > It might be more scalable to split a single array into per-generation
> > data structures and reference them directly from the info struct.
> >
> >> +            .id                             = PRIMARY_PLANE_0,
> >> +            .type                   = DRM_PLANE_TYPE_PRIMARY,
> >> +            .num_formats    = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats                = primary_overlay_format0,
> >> +            .num_modifiers  = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers              = format_modifier0,
> >> +            .min_width              = 0,
> >> +            .min_height             = 0,
> >> +            .max_width              = 4096,
> >> +            .max_height             = 4096,
> >> +            .rotation               = DRM_MODE_ROTATE_0 |
> >> +                                                    DRM_MODE_ROTATE_90 |
> >> +                                                    DRM_MODE_ROTATE_180 |
> >> +                                                    DRM_MODE_ROTATE_270 |
> >> +                                                    DRM_MODE_REFLECT_X |
> >> +                                                    DRM_MODE_REFLECT_Y,
> >> +            .blend_mode             = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                                    BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                                    BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 0,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name                   = "Overlay",
> >> +            .id                             = OVERLAY_PLANE_0,
> >> +            .type                   = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats    = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats                = primary_overlay_format0,
> >> +            .num_modifiers  = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers              = format_modifier0,
> >> +            .min_width              = 0,
> >> +            .min_height             = 0,
> >> +            .max_width              = 4096,
> >> +            .max_height             = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 1,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name                   = "Overlay_1",
> >> +            .id                             = OVERLAY_PLANE_1,
> >> +            .type                   = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats    = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats                = primary_overlay_format0,
> >> +            .num_modifiers  = ARRAY_SIZE(secondary_format_modifiers),
> >> +            .modifiers              = secondary_format_modifiers,
> >> +            .min_width              = 0,
> >> +            .min_height             = 0,
> >> +            .max_width              = 4096,
> >> +            .max_height             = 4096,
> >> +            .rotation               = 0,
> >> +            .blend_mode             = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                                    BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                                    BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 2,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Primary_1",
> >> +            .id                             = PRIMARY_PLANE_1,
> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats         = primary_overlay_format0,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 3,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay_2",
> >> +            .id                             = OVERLAY_PLANE_2,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats         = primary_overlay_format0,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 4,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay_3",
> >> +            .id                     = OVERLAY_PLANE_3,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats         = primary_overlay_format0,
> >> +            .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
> >> +            .modifiers       = secondary_format_modifiers,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = 0,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 5,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Cursor",
> >> +            .id                             = CURSOR_PLANE_0,
> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
> >> +            .formats         = cursor_formats,
> >> +            .num_modifiers = 0,
> >> +            .modifiers       = NULL,
> >> +            .min_width       = 32,
> >> +            .min_height  = 32,
> >> +            .max_width       = 64,
> >> +            .max_height  = 64,
> >> +            .rotation        = 0,
> >> +            .degamma_size = 0,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 255,
> >> +            .watermark       = false,
> >> +            .color_mgmt  = false,
> >> +            .roi             = false,
> >> +            },
> >> +            {
> >> +            .name            = "Cursor_1",
> >> +            .id                             = CURSOR_PLANE_1,
> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
> >> +            .formats         = cursor_formats,
> >> +            .num_modifiers = 0,
> >> +            .modifiers       = NULL,
> >> +            .min_width       = 32,
> >> +            .min_height  = 32,
> >> +            .max_width       = 64,
> >> +            .max_height  = 64,
> >> +            .rotation        = 0,
> >> +            .degamma_size = 0,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 255,
> >> +            .watermark       = false,
> >> +            .color_mgmt  = false,
> >> +            .roi             = false,
> >> +            },
> >> +    },
> >> +    {
> >> +            /* DC_REV_1 */
> >> +            {
> >> +            .name            = "Primary",
> >> +            .id                             = PRIMARY_PLANE_0,
> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats         = primary_overlay_format0,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 0,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay",
> >> +            .id                             = OVERLAY_PLANE_0,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats         = primary_overlay_format0,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 1,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Primary_1",
> >> +            .id                             = PRIMARY_PLANE_1,
> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats         = primary_overlay_format0,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 2,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay_2",
> >> +            .id                             = OVERLAY_PLANE_2,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
> >> +            .formats         = primary_overlay_format0,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 3,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Cursor",
> >> +            .id                             = CURSOR_PLANE_0,
> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
> >> +            .formats         = cursor_formats,
> >> +            .num_modifiers = 0,
> >> +            .modifiers       = NULL,
> >> +            .min_width       = 32,
> >> +            .min_height  = 32,
> >> +            .max_width       = 64,
> >> +            .max_height  = 64,
> >> +            .rotation        = 0,
> >> +            .degamma_size = 0,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 255,
> >> +            .watermark       = false,
> >> +            .color_mgmt  = false,
> >> +            .roi             = false,
> >> +            },
> >> +            {
> >> +            .name            = "Cursor_1",
> >> +            .id                             = CURSOR_PLANE_1,
> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
> >> +            .formats         = cursor_formats,
> >> +            .num_modifiers = 0,
> >> +            .modifiers       = NULL,
> >> +            .min_width       = 32,
> >> +            .min_height  = 32,
> >> +            .max_width       = 64,
> >> +            .max_height  = 64,
> >> +            .rotation        = 0,
> >> +            .degamma_size = 0,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 255,
> >> +            .watermark       = false,
> >> +            .color_mgmt  = false,
> >> +            .roi             = false,
> >> +            },
> >> +    },
> >> +    {
> >> +            /* DC_REV_2 */
> >> +            {
> >> +            .name            = "Primary",
> >> +            .id                             = PRIMARY_PLANE_0,
> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
> >> +            .formats         = primary_overlay_format1,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 0,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay",
> >> +            .id                             = OVERLAY_PLANE_0,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
> >> +            .formats         = primary_overlay_format1,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 1,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay_1",
> >> +            .id                             = OVERLAY_PLANE_1,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
> >> +            .formats         = primary_overlay_format1,
> >> +            .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
> >> +            .modifiers       = secondary_format_modifiers,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = 0,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 2,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Primary_1",
> >> +            .id                             = PRIMARY_PLANE_1,
> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
> >> +            .formats         = primary_overlay_format1,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 3,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay_2",
> >> +            .id                             = OVERLAY_PLANE_2,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
> >> +            .formats         = primary_overlay_format1,
> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
> >> +            .modifiers       = format_modifier0,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = DRM_MODE_ROTATE_0 |
> >> +                                       DRM_MODE_ROTATE_90 |
> >> +                                       DRM_MODE_ROTATE_180 |
> >> +                                       DRM_MODE_ROTATE_270 |
> >> +                                       DRM_MODE_REFLECT_X |
> >> +                                       DRM_MODE_REFLECT_Y,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = FRAC_16_16(1, 3),
> >> +            .max_scale       = FRAC_16_16(10, 1),
> >> +            .zpos            = 4,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Overlay_3",
> >> +            .id                             = OVERLAY_PLANE_3,
> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
> >> +            .formats         = primary_overlay_format1,
> >> +            .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
> >> +            .modifiers       = secondary_format_modifiers,
> >> +            .min_width       = 0,
> >> +            .min_height  = 0,
> >> +            .max_width       = 4096,
> >> +            .max_height  = 4096,
> >> +            .rotation        = 0,
> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
> >> +            .degamma_size   = DEGAMMA_SIZE,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 5,
> >> +            .watermark       = true,
> >> +            .color_mgmt  = true,
> >> +            .roi             = true,
> >> +            },
> >> +            {
> >> +            .name            = "Cursor",
> >> +            .id                             = CURSOR_PLANE_0,
> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
> >> +            .formats         = cursor_formats,
> >> +            .num_modifiers = 0,
> >> +            .modifiers       = NULL,
> >> +            .min_width       = 32,
> >> +            .min_height  = 32,
> >> +            .max_width       = 64,
> >> +            .max_height  = 64,
> >> +            .rotation        = 0,
> >> +            .degamma_size = 0,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 255,
> >> +            .watermark       = false,
> >> +            .color_mgmt  = false,
> >> +            .roi             = false,
> >> +            },
> >> +            {
> >> +            .name            = "Cursor_1",
> >> +            .id                             = CURSOR_PLANE_1,
> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
> >> +            .formats         = cursor_formats,
> >> +            .num_modifiers = 0,
> >> +            .modifiers       = NULL,
> >> +            .min_width       = 32,
> >> +            .min_height  = 32,
> >> +            .max_width       = 64,
> >> +            .max_height  = 64,
> >> +            .rotation        = 0,
> >> +            .degamma_size = 0,
> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
> >> +            .zpos            = 255,
> >> +            .watermark       = false,
> >> +            .color_mgmt  = false,
> >> +            .roi             = false,
> >> +            },
> >> +    },
> >> +};
> >> +
> >> +static const struct vs_dc_info dc_info[] = {
> >
> > Again, please split into per-generation instances.
> >
> >> +    {
> >> +            /* DC_REV_0 */
> >> +            .name                   = "DC8200",
> >> +            .panel_num              = 2,
> >> +            .plane_num              = 8,
> >> +            .planes                 = dc_hw_planes[DC_REV_0],
> >> +            .layer_num              = 6,
> >> +            .max_bpc                = 10,
> >> +            .color_formats  = DRM_COLOR_FORMAT_RGB444 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR444 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR422 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR420,
> >> +            .gamma_size             = GAMMA_EX_SIZE,
> >> +            .gamma_bits             = 12,
> >> +            .pitch_alignment        = 128,
> >> +            .pipe_sync              = false,
> >> +            .background             = true,
> >> +            .panel_sync             = true,
> >> +            .cap_dec                = true,
> >> +    },
> >> +    {
> >> +            /* DC_REV_1 */
> >> +            .name                   = "DC8200",
> >> +            .panel_num              = 2,
> >> +            .plane_num              = 6,
> >> +            .planes                 = dc_hw_planes[DC_REV_1],
> >> +            .layer_num              = 4,
> >> +            .max_bpc                = 10,
> >> +            .color_formats  = DRM_COLOR_FORMAT_RGB444 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR444 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR422 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR420,
> >> +            .gamma_size             = GAMMA_EX_SIZE,
> >> +            .gamma_bits             = 12,
> >> +            .pitch_alignment        = 128,
> >> +            .pipe_sync              = false,
> >> +            .background             = true,
> >> +            .panel_sync             = true,
> >> +            .cap_dec                = true,
> >> +    },
> >> +    {
> >> +            /* DC_REV_2 */
> >> +            .name                   = "DC8200",
> >> +            .panel_num              = 2,
> >> +            .plane_num              = 8,
> >> +            .planes                 = dc_hw_planes[DC_REV_2],
> >> +            .layer_num              = 6,
> >> +            .max_bpc                = 10,
> >> +            .color_formats  = DRM_COLOR_FORMAT_RGB444 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR444 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR422 |
> >> +                                              DRM_COLOR_FORMAT_YCBCR420,
> >> +            .gamma_size             = GAMMA_EX_SIZE,
> >> +            .gamma_bits             = 12,
> >> +            .pitch_alignment        = 128,
> >> +            .pipe_sync              = false,
> >> +            .background             = true,
> >> +            .panel_sync             = true,
> >> +            .cap_dec                = false,
> >> +    },
> >> +};
> >> +
> >> +static const struct dc_hw_funcs hw_func;
> >> +
> >> +static inline u32 hi_read(struct dc_hw *hw, u32 reg)
> >> +{
> >> +    return readl(hw->hi_base + reg);
> >> +}
> >> +
> >> +static inline void hi_write(struct dc_hw *hw, u32 reg, u32 value)
> >> +{
> >> +    writel(value, hw->hi_base + reg);
> >> +}
> >> +
> >> +static inline void dc_write(struct dc_hw *hw, u32 reg, u32 value)
> >> +{
> >> +    writel(value, hw->reg_base + reg - DC_REG_BASE);
> >> +}
> >> +
> >> +static inline u32 dc_read(struct dc_hw *hw, u32 reg)
> >> +{
> >> +    u32 value = readl(hw->reg_base + reg - DC_REG_BASE);
> >> +
> >> +    return value;
> >> +}
> >> +
> >> +static inline void dc_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear)
> >> +{
> >> +    u32 value = dc_read(hw, reg);
> >> +
> >> +    value &= ~clear;
> >> +    value |= set;
> >> +    dc_write(hw, reg, value);
> >> +}
> >> +
> >> +static void load_default_filter(struct dc_hw *hw,
> >> +                            const struct dc_hw_plane_reg *reg, u32 offset)
> >> +{
> >> +    u8 i;
> >> +
> >> +    dc_write(hw, reg->scale_config + offset, 0x33);
> >> +    dc_write(hw, reg->init_offset + offset, 0x80008000);
> >> +    dc_write(hw, reg->h_filter_coef_index + offset, 0x00);
> >> +    for (i = 0; i < H_COEF_SIZE; i++)
> >> +            dc_write(hw, reg->h_filter_coef_data + offset, horkernel[i]);
> >> +
> >> +    dc_write(hw, reg->v_filter_coef_index + offset, 0x00);
> >> +    for (i = 0; i < V_COEF_SIZE; i++)
> >> +            dc_write(hw, reg->v_filter_coef_data + offset, verkernel[i]);
> >> +}
> >> +
> >> +static void load_rgb_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
> >> +                        u32 offset, u16 *table)
> >> +{
> >> +    dc_write(hw, reg->rgb_to_rgb_coef0 + offset, table[0] | (table[1] << 16));
> >> +    dc_write(hw, reg->rgb_to_rgb_coef1 + offset, table[2] | (table[3] << 16));
> >> +    dc_write(hw, reg->rgb_to_rgb_coef2 + offset, table[4] | (table[5] << 16));
> >> +    dc_write(hw, reg->rgb_to_rgb_coef3 + offset, table[6] | (table[7] << 16));
> >> +    dc_write(hw, reg->rgb_to_rgb_coef4 + offset, table[8]);
> >> +}
> >> +
> >> +static void load_yuv_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
> >> +                        u32 offset, s32 *table)
> >> +{
> >> +    dc_write(hw, reg->yuv_to_rgb_coef0 + offset,
> >> +             (0xFFFF & table[0]) | (table[1] << 16));
> >> +    dc_write(hw, reg->yuv_to_rgb_coef1 + offset,
> >> +             (0xFFFF & table[2]) | (table[3] << 16));
> >> +    dc_write(hw, reg->yuv_to_rgb_coef2 + offset,
> >> +             (0xFFFF & table[4]) | (table[5] << 16));
> >> +    dc_write(hw, reg->yuv_to_rgb_coef3 + offset,
> >> +             (0xFFFF & table[6]) | (table[7] << 16));
> >> +    dc_write(hw, reg->yuv_to_rgb_coef4 + offset, table[8]);
> >> +    dc_write(hw, reg->yuv_to_rgb_coefd0 + offset, table[9]);
> >> +    dc_write(hw, reg->yuv_to_rgb_coefd1 + offset, table[10]);
> >> +    dc_write(hw, reg->yuv_to_rgb_coefd2 + offset, table[11]);
> >> +    dc_write(hw, reg->y_clamp_bound + offset, table[12] | (table[13] << 16));
> >> +    dc_write(hw, reg->uv_clamp_bound + offset, table[14] | (table[15] << 16));
> >> +}
> >> +
> >> +static void load_rgb_to_yuv(struct dc_hw *hw, u32 offset, s16 *table)
> >> +{
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF0 + offset,
> >> +             table[0] | (table[1] << 16));
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF1 + offset,
> >> +             table[2] | (table[3] << 16));
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF2 + offset,
> >> +             table[4] | (table[5] << 16));
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF3 + offset,
> >> +             table[6] | (table[7] << 16));
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF4 + offset, table[8]);
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD0 + offset, table[9]);
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD1 + offset, table[10]);
> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD2 + offset, table[11]);
> >> +}
> >> +
> >> +static bool is_rgb(enum dc_hw_color_format format)
> >> +{
> >> +    switch (format) {
> >> +    case FORMAT_X4R4G4B4:
> >> +    case FORMAT_A4R4G4B4:
> >> +    case FORMAT_X1R5G5B5:
> >> +    case FORMAT_A1R5G5B5:
> >> +    case FORMAT_R5G6B5:
> >> +    case FORMAT_X8R8G8B8:
> >> +    case FORMAT_A8R8G8B8:
> >> +    case FORMAT_A2R10G10B10:
> >> +            return true;
> >> +    default:
> >> +            return false;
> >> +    }
> >> +}
> >> +
> >> +static void load_degamma_table(struct dc_hw *hw,
> >> +                           const struct dc_hw_plane_reg *reg,
> >> +                           u32 offset, u16 *table)
> >> +{
> >> +    u16 i;
> >> +    u32 value;
> >> +
> >> +    dc_write(hw, reg->degamma_index + offset, 0);
> >> +
> >> +    for (i = 0; i < DEGAMMA_SIZE; i++) {
> >> +            value = table[i] | (table[i] << 16);
> >> +            dc_write(hw, reg->degamma_data + offset, value);
> >> +            dc_write(hw, reg->degamma_ex_data + offset, table[i]);
> >> +    }
> >> +}
> >> +
> >> +static u32 get_addr_offset(u32 id)
> >> +{
> >> +    u32 offset = 0;
> >> +
> >> +    switch (id) {
> >> +    case PRIMARY_PLANE_1:
> >> +    case OVERLAY_PLANE_1:
> >> +            offset = 0x04;
> >> +            break;
> >> +    case OVERLAY_PLANE_2:
> >> +            offset = 0x08;
> >> +            break;
> >> +    case OVERLAY_PLANE_3:
> >> +            offset = 0x0C;
> >> +            break;
> >> +    default:
> >> +            break;
> >> +    }
> >> +
> >> +    return offset;
> >> +}
> >> +
> >> +int dc_hw_init(struct dc_hw *hw)
> >> +{
> >> +    u8 i, id, panel_num, layer_num;
> >> +    u32 offset;
> >> +    u32 revision = hi_read(hw, DC_HW_REVISION);
> >> +    u32 cid = hi_read(hw, DC_HW_CHIP_CID);
> >> +    const struct dc_hw_plane_reg *reg;
> >> +
> >> +    switch (revision) {
> >> +    case 0x5720:
> >> +            hw->rev = DC_REV_0;
> >> +            break;
> >> +    case 0x5721:
> >> +            switch (cid) {
> >> +            case 0x30B:
> >> +                    hw->rev = DC_REV_1;
> >> +                    break;
> >> +            case 0x310:
> >> +                    hw->rev = DC_REV_2;
> >> +                    break;
> >> +            default:
> >> +                    hw->rev = DC_REV_0;
> >> +                    break;
> >> +            }
> >> +            break;
> >> +    default:
> >> +            return -ENXIO;
> >> +    }
> >
> > We (drm/msm) had code like this for a while (and autodetection of the HW
> > ID). And nowadays we are migrating to of_device_get_match_data and
> > per-SoC compat names. It is just easier to handle, it allows one to
> > specify device more precisely and also it simplifies DT schema.
> >
> >> +
> >> +    hw->info = (struct vs_dc_info *)&dc_info[hw->rev];
> >> +    hw->func = (struct dc_hw_funcs *)&hw_func;
> >> +
> >> +    layer_num = hw->info->layer_num;
> >> +    for (i = 0; i < layer_num; i++) {
> >> +            id = hw->info->planes[i].id;
> >> +            offset = get_addr_offset(id);
> >> +            if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
> >> +                    reg = &dc_plane_reg[0];
> >> +            else
> >> +                    reg = &dc_plane_reg[1];
> >> +
> >> +            load_default_filter(hw, reg, offset);
> >> +            load_rgb_to_rgb(hw, reg, offset, RGB2RGB);
> >> +    }
> >> +
> >> +    panel_num = hw->info->panel_num;
> >> +    for (i = 0; i < panel_num; i++) {
> >> +            offset = i << 2;
> >> +
> >> +            load_rgb_to_yuv(hw, offset, RGB2YUV);
> >> +            dc_write(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0x111);
> >> +
> >> +            offset = i ? DC_CURSOR_OFFSET : 0;
> >> +            dc_write(hw, DC_CURSOR_BACKGROUND + offset, 0x00FFFFFF);
> >> +            dc_write(hw, DC_CURSOR_FOREGROUND + offset, 0x00AAAAAA);
> >> +    }
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +void dc_hw_deinit(struct dc_hw *hw)
> >> +{
> >> +    /* Nothing to do */
> >> +}
> >> +
> >> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
> >> +                    struct dc_hw_fb *fb, struct dc_hw_scale *scale,
> >> +                    struct dc_hw_position *pos, struct dc_hw_blend *blend)
> >> +{
> >> +    struct dc_hw_plane *plane = &hw->plane[id];
> >> +
> >> +    if (plane) {
> >> +            if (fb) {
> >> +                    if (!fb->enable)
> >> +                            plane->fb.enable = false;
> >> +                    else
> >> +                            memcpy(&plane->fb, fb,
> >> +                                   sizeof(*fb) - sizeof(fb->dirty));
> >> +                    plane->fb.dirty = true;
> >> +            }
> >> +            if (scale) {
> >> +                    memcpy(&plane->scale, scale,
> >> +                           sizeof(*scale) - sizeof(scale->dirty));
> >> +                    plane->scale.dirty = true;
> >> +            }
> >> +            if (pos) {
> >> +                    memcpy(&plane->pos, pos,
> >> +                           sizeof(*pos) - sizeof(pos->dirty));
> >> +                    plane->pos.dirty = true;
> >> +            }
> >> +            if (blend) {
> >> +                    memcpy(&plane->blend, blend,
> >> +                           sizeof(*blend) - sizeof(blend->dirty));
> >> +                    plane->blend.dirty = true;
> >> +            }
> >> +    }
> >> +}
> >> +
> >> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode)
> >> +{
> >> +    struct dc_hw_plane *plane = &hw->plane[id];
> >> +
> >> +    if (plane) {
> >> +            if (hw->info->planes[id].degamma_size) {
> >> +                    plane->degamma.mode = mode;
> >> +                    plane->degamma.dirty = true;
> >> +            } else {
> >> +                    plane->degamma.dirty = false;
> >> +            }
> >> +    }
> >> +}
> >> +
> >> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi)
> >> +{
> >> +    struct dc_hw_plane *plane = &hw->plane[id];
> >> +
> >> +    if (plane) {
> >> +            memcpy(&plane->roi, roi, sizeof(*roi) - sizeof(roi->dirty));
> >> +            plane->roi.dirty = true;
> >> +    }
> >> +}
> >> +
> >> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
> >> +                       struct dc_hw_colorkey *colorkey)
> >> +{
> >> +    struct dc_hw_plane *plane = &hw->plane[id];
> >> +
> >> +    if (plane) {
> >> +            memcpy(&plane->colorkey, colorkey,
> >> +                   sizeof(*colorkey) - sizeof(colorkey->dirty));
> >> +            plane->colorkey.dirty = true;
> >> +    }
> >> +}
> >> +
> >> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos)
> >> +{
> >> +    memcpy(&hw->qos, qos, sizeof(*qos) - sizeof(qos->dirty));
> >> +    hw->qos.dirty = true;
> >> +}
> >> +
> >> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor)
> >> +{
> >> +    memcpy(&hw->cursor[id], cursor, sizeof(*cursor) - sizeof(cursor->dirty));
> >> +    hw->cursor[id].dirty = true;
> >> +}
> >> +
> >> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
> >> +                    u16 r, u16 g, u16 b)
> >> +{
> >> +    if (index >= hw->info->gamma_size)
> >> +            return;
> >> +
> >> +    hw->gamma[id].gamma[index][0] = r;
> >> +    hw->gamma[id].gamma[index][1] = g;
> >> +    hw->gamma[id].gamma[index][2] = b;
> >> +    hw->gamma[id].dirty = true;
> >> +}
> >> +
> >> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable)
> >> +{
> >> +    hw->gamma[id].enable = enable;
> >> +    hw->gamma[id].dirty = true;
> >> +}
> >> +
> >> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display)
> >> +{
> >> +    u8 id = display->id;
> >> +
> >> +    memcpy(&hw->display[id], display, sizeof(*display));
> >> +
> >> +    hw->func->display(hw, display);
> >> +}
> >> +
> >> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable)
> >> +{
> >> +    if (enable)
> >> +            hi_write(hw, AQ_INTR_ENBL, 0xFFFFFFFF);
> >> +    else
> >> +            hi_write(hw, AQ_INTR_ENBL, 0);
> >> +}
> >> +
> >> +u32 dc_hw_get_interrupt(struct dc_hw *hw)
> >> +{
> >> +    return hi_read(hw, AQ_INTR_ACKNOWLEDGE);
> >> +}
> >> +
> >> +bool dc_hw_check_underflow(struct dc_hw *hw)
> >> +{
> >> +    return dc_read(hw, DC_FRAMEBUFFER_CONFIG) & BIT(5);
> >> +}
> >> +
> >> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable)
> >> +{
> >> +    u32 i, offset;
> >> +    u8 id, layer_num = hw->info->layer_num;
> >> +    u8 panel_num = hw->info->panel_num;
> >> +
> >> +    for (i = 0; i < layer_num; i++) {
> >> +            id = hw->info->planes[i].id;
> >> +            offset = get_addr_offset(id);
> >> +            if (enable) {
> >> +                    if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, BIT(12), 0);
> >> +                    else
> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, BIT(31), 0);
> >> +            } else {
> >> +                    if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, 0, BIT(12));
> >> +                    else
> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, 0, BIT(31));
> >> +            }
> >> +    }
> >> +
> >> +    for (i = 0; i < panel_num; i++) {
> >> +            offset = i << 2;
> >> +            if (enable)
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, BIT(0));
> >> +            else
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, BIT(0), 0);
> >> +    }
> >> +}
> >> +
> >> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id)
> >> +{
> >> +    if (out <= OUT_DP)
> >> +            hw->out[id] = out;
> >> +}
> >> +
> >> +static void gamma_ex_commit(struct dc_hw *hw)
> >> +{
> >> +    u8 panel_num = hw->info->panel_num;
> >> +    u16 i, j;
> >> +    u32 value;
> >> +
> >> +    for (j = 0; j < panel_num; j++) {
> >> +            if (hw->gamma[j].dirty) {
> >> +                    if (hw->gamma[j].enable) {
> >> +                            dc_write(hw, DC_DISPLAY_GAMMA_EX_INDEX + (j << 2), 0x00);
> >> +                            for (i = 0; i < GAMMA_EX_SIZE; i++) {
> >> +                                    value = hw->gamma[j].gamma[i][2] |
> >> +                                            (hw->gamma[j].gamma[i][1] << 12);
> >> +                                    dc_write(hw, DC_DISPLAY_GAMMA_EX_DATA + (j << 2), value);
> >> +                                    dc_write(hw, DC_DISPLAY_GAMMA_EX_ONE_DATA + (j << 2),
> >> +                                             hw->gamma[j].gamma[i][0]);
> >> +                            }
> >> +                            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
> >> +                                         BIT(13), 0);
> >> +                    } else {
> >> +                            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
> >> +                                         0, BIT(13));
> >> +                    }
> >> +                    hw->gamma[j].dirty = false;
> >> +            }
> >> +    }
> >> +}
> >> +
> >> +static void plane_commit(struct dc_hw *hw)
> >> +{
> >> +    struct dc_hw_plane *plane;
> >> +    const struct dc_hw_plane_reg *reg;
> >> +    bool primary = false;
> >> +    u8 id, layer_num = hw->info->layer_num;
> >> +    u32 i, offset;
> >> +
> >> +    for (i = 0; i < layer_num; i++) {
> >> +            plane = &hw->plane[i];
> >> +            id = hw->info->planes[i].id;
> >> +            offset = get_addr_offset(id);
> >> +            if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
> >> +                    reg = &dc_plane_reg[0];
> >> +                    primary = true;
> >> +            } else {
> >> +                    reg = &dc_plane_reg[1];
> >> +                    primary = false;
> >> +            }
> >
> > Can you please move register defines to some internal hw_plane
> > structure. These details are known at the time of creation of the plane.
> > There is no need to determine them again and again.
> >
> >> +
> >> +            if (plane->fb.dirty) {
> >> +                    if (plane->fb.enable) {
> >
> > if (plane->fb.dirty && plane->fb.enable) will save you one level of
> > indentation.
> >
> > However it might be better to use existing DRM flags instead of
> > inventing your own flags.
> >
> >> +                            dc_write(hw, reg->y_address + offset,
> >> +                                     plane->fb.y_address);
> >> +                            dc_write(hw, reg->u_address + offset,
> >> +                                     plane->fb.u_address);
> >> +                            dc_write(hw, reg->v_address + offset,
> >> +                                     plane->fb.v_address);
> >> +                            dc_write(hw, reg->y_stride + offset,
> >> +                                     plane->fb.y_stride);
> >> +                            dc_write(hw, reg->u_stride + offset,
> >> +                                     plane->fb.u_stride);
> >> +                            dc_write(hw, reg->v_stride + offset,
> >> +                                     plane->fb.v_stride);
> >> +                            dc_write(hw, reg->size + offset,
> >> +                                     plane->fb.width |
> >> +                                     (plane->fb.height << 15));
> >> +                            dc_write(hw, reg->water_mark + offset,
> >> +                                     plane->fb.water_mark);
> >> +
> >> +                            if (plane->fb.clear_enable)
> >> +                                    dc_write(hw, reg->clear_value + offset,
> >> +                                             plane->fb.clear_value);
> >> +                    }
> >> +
> >> +                    if (primary) {
> >
> > You have two kinds of planes. Primary and overlays. Could you please
> > split this code into two separate functions and define two vtables, one
> > for primary planes and one for overlays. There is no need to stick
> > everything into a single funcion that handles everything.
> >
> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
> >> +                                         (plane->fb.format << 26) |
> >> +                                         (plane->fb.uv_swizzle << 25) |
> >> +                                         (plane->fb.swizzle << 23) |
> >> +                                         (plane->fb.tile_mode << 17) |
> >> +                                         (plane->fb.yuv_color_space << 14) |
> >> +                                         (plane->fb.rotation << 11) |
> >> +                                         (plane->fb.clear_enable << 8),
> >> +                                         (0x1F << 26) |
> >> +                                         BIT(25) |
> >> +                                         (0x03 << 23) |
> >> +                                         (0x1F << 17) |
> >> +                                         (0x07 << 14) |
> >> +                                         (0x07 << 11) |
> >> +                                         BIT(8));
> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                         (plane->fb.dec_enable << 1) |
> >> +                                         (plane->fb.enable << 13) |
> >> +                                         (plane->fb.zpos << 16) |
> >> +                                         (plane->fb.display_id << 19),
> >> +                                         BIT(1) | BIT(13) | (0x07 << 16) | BIT(19));
> >
> > It looks like this code can enjoy defining names for all the magic
> > values. Otherwise it is impossible to support it.
> >
> >> +                    } else {
> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> >> +                                         (plane->fb.dec_enable << 27) |
> >> +                                         (plane->fb.clear_enable << 25) |
> >> +                                         (plane->fb.enable << 24) |
> >> +                                         (plane->fb.format << 16) |
> >> +                                         (plane->fb.uv_swizzle << 15) |
> >> +                                         (plane->fb.swizzle << 13) |
> >> +                                         (plane->fb.tile_mode << 8) |
> >> +                                         (plane->fb.yuv_color_space << 5) |
> >> +                                         (plane->fb.rotation << 2),
> >> +                                         BIT(27) |
> >> +                                         BIT(25) |
> >> +                                         BIT(24) |
> >> +                                         (0x1F << 16) |
> >> +                                         BIT(15) |
> >> +                                         (0x03 << 13) |
> >> +                                         (0x1F << 8) |
> >> +                                         (0x07 << 5) |
> >> +                                         (0x07 << 2));
> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset,
> >> +                                         plane->fb.zpos | (plane->fb.display_id << 3),
> >> +                                         0x07 | BIT(3));
> >> +                    }
> >> +                    plane->fb.dirty = false;
> >> +            }
> >> +
> >> +            if (plane->scale.dirty) {
> >> +                    if (plane->scale.enable) {
> >> +                            dc_write(hw, reg->scale_factor_x + offset,
> >> +                                     plane->scale.scale_factor_x);
> >> +                            dc_write(hw, reg->scale_factor_y + offset,
> >> +                                     plane->scale.scale_factor_y);
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_FRAMEBUFFER_CONFIG + offset,
> >> +                                                 BIT(22), 0);
> >> +                            else
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_OVERLAY_SCALE_CONFIG + offset,
> >> +                                                 BIT(8), 0);
> >> +                    } else {
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_FRAMEBUFFER_CONFIG + offset,
> >> +                                                 0, BIT(22));
> >> +                            else
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_OVERLAY_SCALE_CONFIG + offset,
> >> +                                                 0, BIT(8));
> >> +                    }
> >> +                    plane->scale.dirty = false;
> >> +            }
> >> +
> >> +            if (plane->pos.dirty) {
> >> +                    dc_write(hw, reg->top_left + offset,
> >> +                             plane->pos.start_x |
> >> +                             (plane->pos.start_y << 15));
> >> +                    dc_write(hw, reg->bottom_right + offset,
> >> +                             plane->pos.end_x |
> >> +                             (plane->pos.end_y << 15));
> >> +                    plane->pos.dirty = false;
> >> +            }
> >> +
> >> +            if (plane->blend.dirty) {
> >> +                    dc_write(hw, reg->src_global_color + offset,
> >> +                             plane->blend.alpha << 24);
> >> +                    dc_write(hw, reg->dst_global_color + offset,
> >> +                             plane->blend.alpha << 24);
> >> +                    switch (plane->blend.blend_mode) {
> >> +                    case BLEND_PREMULTI:
> >> +                            dc_write(hw, reg->blend_config + offset, 0x3450);
> >> +                            break;
> >> +                    case BLEND_COVERAGE:
> >> +                            dc_write(hw, reg->blend_config + offset, 0x3950);
> >> +                            break;
> >> +                    case BLEND_PIXEL_NONE:
> >> +                            dc_write(hw, reg->blend_config + offset, 0x3548);
> >> +                            break;
> >> +                    default:
> >> +                            break;
> >> +                    }
> >> +                    plane->blend.dirty = false;
> >> +            }
> >> +
> >> +            if (plane->colorkey.dirty) {
> >> +                    dc_write(hw, reg->color_key + offset, plane->colorkey.colorkey);
> >> +                    dc_write(hw, reg->color_key_high + offset,
> >> +                             plane->colorkey.colorkey_high);
> >> +
> >> +                    if (primary)
> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
> >> +                                         plane->colorkey.transparency << 9, 0x03 << 9);
> >> +                    else
> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> >> +                                         plane->colorkey.transparency, 0x03);
> >> +
> >> +                    plane->colorkey.dirty = false;
> >> +            }
> >> +
> >> +            if (plane->roi.dirty) {
> >> +                    if (plane->roi.enable) {
> >> +                            dc_write(hw, reg->roi_origin + offset,
> >> +                                     plane->roi.x | (plane->roi.y << 16));
> >> +                            dc_write(hw, reg->roi_size + offset,
> >> +                                     plane->roi.width | (plane->roi.height << 16));
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                                 BIT(0), 0);
> >> +                            else
> >> +                                    dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> >> +                                                 BIT(22), 0);
> >> +                    } else {
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                                 0, BIT(0));
> >> +                            else
> >> +                                    dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> >> +                                                 0, BIT(22));
> >> +                    }
> >> +                    plane->roi.dirty = false;
> >> +            }
> >> +    }
> >> +}
> >> +
> >> +static void plane_ex_commit(struct dc_hw *hw)
> >> +{
> >> +    struct dc_hw_plane *plane;
> >> +    const struct dc_hw_plane_reg *reg;
> >> +    bool primary = false;
> >> +    u8 id, layer_num = hw->info->layer_num;
> >> +    u32 i, offset;
> >> +
> >> +    for (i = 0; i < layer_num; i++) {
> >> +            plane = &hw->plane[i];
> >> +            id = hw->info->planes[i].id;
> >> +            offset = get_addr_offset(id);
> >> +            if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
> >> +                    reg = &dc_plane_reg[0];
> >> +                    primary = true;
> >> +            } else {
> >> +                    reg = &dc_plane_reg[1];
> >> +                    primary = false;
> >> +            }
> >> +
> >> +            if (plane->fb.dirty) {
> >> +                    if (is_rgb(plane->fb.format)) {
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                                 BIT(6), BIT(8));
> >> +                            else
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_OVERLAY_CONFIG + offset,
> >> +                                                 BIT(29), BIT(30));
> >> +                    } else {
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                                 BIT(8), BIT(6));
> >> +                            else
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_OVERLAY_CONFIG + offset,
> >> +                                                 BIT(30), BIT(29));
> >> +                            switch (plane->fb.yuv_color_space) {
> >> +                            case COLOR_SPACE_601:
> >> +                                    load_yuv_to_rgb(hw, reg, offset, YUV601_2RGB);
> >> +                                    break;
> >> +                            case COLOR_SPACE_709:
> >> +                                    load_yuv_to_rgb(hw, reg, offset, YUV709_2RGB);
> >> +                                    break;
> >> +                            case COLOR_SPACE_2020:
> >> +                                    load_yuv_to_rgb(hw, reg, offset, YUV2020_2RGB);
> >> +                                    break;
> >> +                            default:
> >> +                                    break;
> >> +                            }
> >> +                    }
> >> +            }
> >> +            if (plane->degamma.dirty) {
> >> +                    switch (plane->degamma.mode) {
> >> +                    case VS_DEGAMMA_DISABLE:
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                                 0, BIT(5));
> >> +                            else
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_OVERLAY_CONFIG + offset,
> >> +                                                 0, BIT(28));
> >> +                            break;
> >> +                    case VS_DEGAMMA_BT709:
> >> +                            load_degamma_table(hw, reg, offset, DEGAMMA_709);
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                                 BIT(5), 0);
> >> +                            else
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_OVERLAY_CONFIG + offset,
> >> +                                                 BIT(28), 0);
> >> +                            break;
> >> +                    case VS_DEGAMMA_BT2020:
> >> +                            load_degamma_table(hw, reg, offset, DEGAMMA_2020);
> >> +                            if (primary)
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
> >> +                                                 BIT(5), 0);
> >> +                            else
> >> +                                    dc_set_clear(hw,
> >> +                                                 DC_OVERLAY_CONFIG + offset,
> >> +                                                 BIT(28), 0);
> >> +                            break;
> >> +                    default:
> >> +                            break;
> >> +                    }
> >> +                    plane->degamma.dirty = false;
> >> +            }
> >> +    }
> >> +    plane_commit(hw);
> >> +}
> >> +
> >> +static void setup_display(struct dc_hw *hw, struct dc_hw_display *display)
> >> +{
> >> +    u8 id = display->id;
> >> +    u32 dpi_cfg, offset = id << 2;
> >> +
> >> +    if (hw->display[id].enable) {
> >> +            switch (display->bus_format) {
> >> +            case MEDIA_BUS_FMT_RGB565_1X16:
> >> +                    dpi_cfg = 0;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_RGB666_1X18:
> >> +                    dpi_cfg = 3;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> >> +                    dpi_cfg = 4;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_RGB888_1X24:
> >> +                    dpi_cfg = 5;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_RGB101010_1X30:
> >> +                    dpi_cfg = 6;
> >> +                    break;
> >> +            default:
> >> +                    dpi_cfg = 5;
> >> +                    break;
> >> +            }
> >> +            dc_write(hw, DC_DISPLAY_DPI_CONFIG + offset, dpi_cfg);
> >> +
> >> +            if (id == 0)
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
> >> +            else
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
> >> +
> >> +            dc_write(hw, DC_DISPLAY_H + offset, hw->display[id].h_active |
> >> +                     (hw->display[id].h_total << 16));
> >> +            dc_write(hw, DC_DISPLAY_H_SYNC + offset,
> >> +                     hw->display[id].h_sync_start |
> >> +                     (hw->display[id].h_sync_end << 15) |
> >> +                     (hw->display[id].h_sync_polarity ? 0 : BIT(31)) |
> >> +                     BIT(30));
> >> +            dc_write(hw, DC_DISPLAY_V + offset, hw->display[id].v_active |
> >> +                            (hw->display[id].v_total << 16));
> >> +            dc_write(hw, DC_DISPLAY_V_SYNC + offset,
> >> +                     hw->display[id].v_sync_start |
> >> +                     (hw->display[id].v_sync_end << 15) |
> >> +                     (hw->display[id].v_sync_polarity ? 0 : BIT(31)) |
> >> +                     BIT(30));
> >> +
> >> +            if (hw->info->pipe_sync)
> >> +                    dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
> >> +                                 0, BIT(3) | BIT(4));
> >> +
> >> +            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(12), 0);
> >> +            if (id == 0)
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(0), BIT(3));
> >> +            else
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(1), BIT(3));
> >> +    } else {
> >> +            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(12));
> >> +            if (id == 0)
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
> >> +            else
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
> >> +    }
> >> +}
> >> +
> >> +static void setup_display_ex(struct dc_hw *hw, struct dc_hw_display *display)
> >> +{
> >> +    u8 id = display->id;
> >> +    u32 dp_cfg, offset = id << 2;
> >> +    bool is_yuv = false;
> >> +
> >> +    if (hw->display[id].enable && hw->out[id] == OUT_DP) {
> >> +            switch (display->bus_format) {
> >> +            case MEDIA_BUS_FMT_RGB565_1X16:
> >> +                    dp_cfg = 0;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_RGB666_1X18:
> >> +                    dp_cfg = 1;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_RGB888_1X24:
> >> +                    dp_cfg = 2;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_RGB101010_1X30:
> >> +                    dp_cfg = 3;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_UYVY8_1X16:
> >> +                    dp_cfg = 2 << 4;
> >> +                    is_yuv = true;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_YUV8_1X24:
> >> +                    dp_cfg = 4 << 4;
> >> +                    is_yuv = true;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_UYVY10_1X20:
> >> +                    dp_cfg = 8 << 4;
> >> +                    is_yuv = true;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_YUV10_1X30:
> >> +                    dp_cfg = 10 << 4;
> >> +                    is_yuv = true;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> >> +                    dp_cfg = 12 << 4;
> >> +                    is_yuv = true;
> >> +                    break;
> >> +            case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> >> +                    dp_cfg = 13 << 4;
> >> +                    is_yuv = true;
> >> +                    break;
> >> +            default:
> >> +                    dp_cfg = 2;
> >> +                    break;
> >> +            }
> >> +            if (is_yuv)
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(16), 0);
> >> +            else
> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(16));
> >> +            dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | BIT(3));
> >> +    }
> >> +
> >> +    if (hw->out[id] == OUT_DPI)
> >> +            dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, BIT(3));
> >> +
> >> +    setup_display(hw, display);
> >> +}
> >> +
> >> +static const struct dc_hw_funcs hw_func = {
> >> +    .gamma = &gamma_ex_commit,
> >> +    .plane = &plane_ex_commit,
> >> +    .display = setup_display_ex,
> >> +};
> >> +
> >> +void dc_hw_commit(struct dc_hw *hw)
> >> +{
> >> +    u32 i, offset = 0;
> >> +    u8 plane_num = hw->info->plane_num;
> >> +    u8 layer_num = hw->info->layer_num;
> >> +    u8 cursor_num = plane_num - layer_num;
> >> +
> >> +    hw->func->gamma(hw);
> >> +    hw->func->plane(hw);
> >> +
> >> +    for (i = 0; i < cursor_num; i++) {
> >> +            if (hw->cursor[i].dirty) {
> >> +                    offset = hw->cursor[i].display_id ? DC_CURSOR_OFFSET : 0;
> >> +                    if (hw->cursor[i].enable) {
> >> +                            dc_write(hw, DC_CURSOR_ADDRESS + offset,
> >> +                                     hw->cursor[i].address);
> >> +                            dc_write(hw, DC_CURSOR_LOCATION + offset, hw->cursor[i].x |
> >> +                                     (hw->cursor[i].y << 16));
> >> +                            dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
> >> +                                         (hw->cursor[i].hot_x << 16) |
> >> +                                         (hw->cursor[i].hot_y << 8) |
> >> +                                         (hw->cursor[i].size << 5) |
> >> +                                         BIT(3) | BIT(2) | 0x02,
> >> +                                         (0xFF << 16) |
> >> +                                         (0xFF << 8) |
> >> +                                         (0x07 << 5) | 0x1F);
> >> +                    } else {
> >> +                            dc_set_clear(hw, DC_CURSOR_CONFIG + offset, BIT(3), 0x03);
> >> +                    }
> >> +                    hw->cursor[i].dirty = false;
> >> +            }
> >> +    }
> >> +
> >> +    if (hw->qos.dirty) {
> >> +            dc_set_clear(hw, DC_QOS_CONFIG, (hw->qos.high_value << 4) |
> >> +                                     hw->qos.low_value, 0xFF);
> >> +            hw->qos.dirty = false;
> >> +    }
> >> +}
> >> +
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.h b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
> >> new file mode 100644
> >> index 000000000..9a1d767ae
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
> >> @@ -0,0 +1,492 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#ifndef __VS_DC_HW_H__
> >> +#define __VS_DC_HW_H__
> >> +
> >> +#define AQ_INTR_ACKNOWLEDGE                         0x0010
> >> +#define AQ_INTR_ENBL                                        0x0014
> >
> > Please take care to align all the defines.
> >
> >> +#define DC_HW_REVISION                                      0x0024
> >> +#define DC_HW_CHIP_CID                                      0x0030
> >> +
> >> +#define DC_REG_BASE                                         0x0800
> >> +#define DC_REG_RANGE                                        0x2000
> >> +#define DC_SEC_REG_OFFSET                           0x100000
> >> +
> >> +#define DC_FRAMEBUFFER_CONFIG                       0x1518
> >> +#define DC_FRAMEBUFFER_CONFIG_EX            0x1CC0
> >> +#define DC_FRAMEBUFFER_SCALE_CONFIG         0x1520
> >> +#define DC_FRAMEBUFFER_TOP_LEFT                     0x24D8
> >> +#define DC_FRAMEBUFFER_BOTTOM_RIGHT         0x24E0
> >> +#define DC_FRAMEBUFFER_ADDRESS                      0x1400
> >> +#define DC_FRAMEBUFFER_U_ADDRESS            0x1530
> >> +#define DC_FRAMEBUFFER_V_ADDRESS            0x1538
> >> +#define DC_FRAMEBUFFER_STRIDE                       0x1408
> >> +#define DC_FRAMEBUFFER_U_STRIDE                     0x1800
> >> +#define DC_FRAMEBUFFER_V_STRIDE                     0x1808
> >> +#define DC_FRAMEBUFFER_SIZE                         0x1810
> >> +#define DC_FRAMEBUFFER_SCALE_FACTOR_X               0x1828
> >> +#define DC_FRAMEBUFFER_SCALE_FACTOR_Y               0x1830
> >> +#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX  0x1838
> >> +#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA   0x1A00
> >> +#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX  0x1A08
> >> +#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA   0x1A10
> >> +#define DC_FRAMEBUFFER_INIT_OFFSET                  0x1A20
> >> +#define DC_FRAMEBUFFER_COLOR_KEY                    0x1508
> >> +#define DC_FRAMEBUFFER_COLOR_KEY_HIGH               0x1510
> >> +#define DC_FRAMEBUFFER_CLEAR_VALUE                  0x1A18
> >> +#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX    0x1818
> >> +#define DC_FRAMEBUFFER_COLOR_TABLE_DATA             0x1820
> >> +#define DC_FRAMEBUFFER_BG_COLOR                             0x1528
> >> +#define DC_FRAMEBUFFER_ROI_ORIGIN                   0x1CB0
> >> +#define DC_FRAMEBUFFER_ROI_SIZE                             0x1CB8
> >> +#define DC_FRAMEBUFFER_WATER_MARK                   0x1CE8
> >> +#define DC_FRAMEBUFFER_DEGAMMA_INDEX                0x1D88
> >> +#define DC_FRAMEBUFFER_DEGAMMA_DATA                 0x1D90
> >> +#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA              0x1D98
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF0               0x1DA0
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF1               0x1DA8
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF2               0x1DB0
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF3               0x1DB8
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF4               0x1E00
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD0              0x1E08
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD1              0x1E10
> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD2              0x1E18
> >> +#define DC_FRAMEBUFFER_Y_CLAMP_BOUND                0x1E88
> >> +#define DC_FRAMEBUFFER_UV_CLAMP_BOUND               0x1E90
> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF0               0x1E20
> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF1               0x1E28
> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF2               0x1E30
> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF3               0x1E38
> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF4               0x1E40
> >> +#define DC_FRAMEBUFFER_BLEND_CONFIG                 0x2510
> >> +#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR             0x2500
> >> +#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR             0x2508
> >> +
> >> +#define DC_OVERLAY_CONFIG                                   0x1540
> >> +#define DC_OVERLAY_CONFIG_EX                                0x2540
> >> +#define DC_OVERLAY_SCALE_CONFIG                             0x1C00
> >> +#define DC_OVERLAY_BLEND_CONFIG                             0x1580
> >> +#define DC_OVERLAY_TOP_LEFT                                 0x1640
> >> +#define DC_OVERLAY_BOTTOM_RIGHT                             0x1680
> >> +#define DC_OVERLAY_ADDRESS                                  0x15C0
> >> +#define DC_OVERLAY_U_ADDRESS                                0x1840
> >> +#define DC_OVERLAY_V_ADDRESS                                0x1880
> >> +#define DC_OVERLAY_STRIDE                                   0x1600
> >> +#define DC_OVERLAY_U_STRIDE                                 0x18C0
> >> +#define DC_OVERLAY_V_STRIDE                                 0x1900
> >> +#define DC_OVERLAY_SIZE                                             0x17C0
> >> +#define DC_OVERLAY_SCALE_FACTOR_X                   0x1A40
> >> +#define DC_OVERLAY_SCALE_FACTOR_Y                   0x1A80
> >> +#define DC_OVERLAY_H_FILTER_COEF_INDEX              0x1AC0
> >> +#define DC_OVERLAY_H_FILTER_COEF_DATA               0x1B00
> >> +#define DC_OVERLAY_V_FILTER_COEF_INDEX              0x1B40
> >> +#define DC_OVERLAY_V_FILTER_COEF_DATA               0x1B80
> >> +#define DC_OVERLAY_INIT_OFFSET                              0x1BC0
> >> +#define DC_OVERLAY_COLOR_KEY                                0x1740
> >> +#define DC_OVERLAY_COLOR_KEY_HIGH                   0x1780
> >> +#define DC_OVERLAY_CLEAR_VALUE                              0x1940
> >> +#define DC_OVERLAY_COLOR_TABLE_INDEX                0x1980
> >> +#define DC_OVERLAY_COLOR_TABLE_DATA                 0x19C0
> >> +#define DC_OVERLAY_SRC_GLOBAL_COLOR                 0x16C0
> >> +#define DC_OVERLAY_DST_GLOBAL_COLOR                 0x1700
> >> +#define DC_OVERLAY_ROI_ORIGIN                               0x1D00
> >> +#define DC_OVERLAY_ROI_SIZE                                 0x1D40
> >> +#define DC_OVERLAY_WATER_MARK                               0x1DC0
> >> +#define DC_OVERLAY_DEGAMMA_INDEX                    0x2200
> >> +#define DC_OVERLAY_DEGAMMA_DATA                             0x2240
> >> +#define DC_OVERLAY_DEGAMMA_EX_DATA                  0x2280
> >> +#define DC_OVERLAY_YUVTORGB_COEF0                   0x1EC0
> >> +#define DC_OVERLAY_YUVTORGB_COEF1                   0x1F00
> >> +#define DC_OVERLAY_YUVTORGB_COEF2                   0x1F40
> >> +#define DC_OVERLAY_YUVTORGB_COEF3                   0x1F80
> >> +#define DC_OVERLAY_YUVTORGB_COEF4                   0x1FC0
> >> +#define DC_OVERLAY_YUVTORGB_COEFD0                  0x2000
> >> +#define DC_OVERLAY_YUVTORGB_COEFD1                  0x2040
> >> +#define DC_OVERLAY_YUVTORGB_COEFD2                  0x2080
> >> +#define DC_OVERLAY_Y_CLAMP_BOUND                    0x22C0
> >> +#define DC_OVERLAY_UV_CLAMP_BOUND                   0x2300
> >> +#define DC_OVERLAY_RGBTORGB_COEF0                   0x20C0
> >> +#define DC_OVERLAY_RGBTORGB_COEF1                   0x2100
> >> +#define DC_OVERLAY_RGBTORGB_COEF2                   0x2140
> >> +#define DC_OVERLAY_RGBTORGB_COEF3                   0x2180
> >> +#define DC_OVERLAY_RGBTORGB_COEF4                   0x21C0
> >> +
> >> +#define DC_CURSOR_CONFIG                                    0x1468
> >> +#define DC_CURSOR_ADDRESS                                   0x146C
> >> +#define DC_CURSOR_LOCATION                                  0x1470
> >> +#define DC_CURSOR_BACKGROUND                                0x1474
> >> +#define DC_CURSOR_FOREGROUND                                0x1478
> >> +#define DC_CURSOR_CLK_GATING                                0x1484
> >> +#define DC_CURSOR_CONFIG_EX                                 0x24E8
> >> +#define DC_CURSOR_OFFSET                                    0x1080
> >> +
> >> +#define DC_DISPLAY_DITHER_CONFIG            0x1410
> >> +#define DC_DISPLAY_PANEL_CONFIG                     0x1418
> >> +#define DC_DISPLAY_PANEL_CONFIG_EX          0x2518
> >> +#define DC_DISPLAY_DITHER_TABLE_LOW         0x1420
> >> +#define DC_DISPLAY_DITHER_TABLE_HIGH        0x1428
> >> +#define DC_DISPLAY_H                                        0x1430
> >> +#define DC_DISPLAY_H_SYNC                           0x1438
> >> +#define DC_DISPLAY_V                                        0x1440
> >> +#define DC_DISPLAY_V_SYNC                           0x1448
> >> +#define DC_DISPLAY_CURRENT_LOCATION         0x1450
> >> +#define DC_DISPLAY_GAMMA_INDEX                      0x1458
> >> +#define DC_DISPLAY_GAMMA_DATA                       0x1460
> >> +#define DC_DISPLAY_INT                                      0x147C
> >> +#define DC_DISPLAY_INT_ENABLE                       0x1480
> >> +#define DC_DISPLAY_DBI_CONFIG                       0x1488
> >> +#define DC_DISPLAY_GENERAL_CONFIG           0x14B0
> >> +#define DC_DISPLAY_DPI_CONFIG                       0x14B8
> >> +#define DC_DISPLAY_PANEL_START                      0x1CCC
> >> +#define DC_DISPLAY_DEBUG_COUNTER_SELECT             0x14D0
> >> +#define DC_DISPLAY_DEBUG_COUNTER_VALUE              0x14D8
> >> +#define DC_DISPLAY_DP_CONFIG                                0x1CD0
> >> +#define DC_DISPLAY_GAMMA_EX_INDEX                   0x1CF0
> >> +#define DC_DISPLAY_GAMMA_EX_DATA                    0x1CF8
> >> +#define DC_DISPLAY_GAMMA_EX_ONE_DATA                0x1D80
> >> +#define DC_DISPLAY_RGBTOYUV_COEF0                   0x1E48
> >> +#define DC_DISPLAY_RGBTOYUV_COEF1                   0x1E50
> >> +#define DC_DISPLAY_RGBTOYUV_COEF2                   0x1E58
> >> +#define DC_DISPLAY_RGBTOYUV_COEF3                   0x1E60
> >> +#define DC_DISPLAY_RGBTOYUV_COEF4                   0x1E68
> >> +#define DC_DISPLAY_RGBTOYUV_COEFD0                  0x1E70
> >> +#define DC_DISPLAY_RGBTOYUV_COEFD1                  0x1E78
> >> +#define DC_DISPLAY_RGBTOYUV_COEFD2                  0x1E80
> >> +
> >> +#define DC_CLK_GATTING                                      0x1A28
> >> +#define DC_QOS_CONFIG                                       0x1A38
> >> +
> >> +#define DC_TRANSPARENCY_OPAQUE                      0x00
> >> +#define DC_TRANSPARENCY_KEY                         0x02
> >> +#define DC_DISPLAY_DITHERTABLE_LOW          0x7B48F3C0
> >> +#define DC_DISPLAY_DITHERTABLE_HIGH         0x596AD1E2
> >> +
> >> +#define GAMMA_SIZE                                          256
> >> +#define GAMMA_EX_SIZE                                       300
> >> +#define DEGAMMA_SIZE                                        260
> >> +
> >> +#define RGB_TO_RGB_TABLE_SIZE                       9
> >> +#define YUV_TO_RGB_TABLE_SIZE                       16
> >> +#define RGB_TO_YUV_TABLE_SIZE                       12
> >> +
> >> +#define DC_LAYER_NUM        6
> >> +#define DC_DISPLAY_NUM      2
> >> +#define DC_CURSOR_NUM       2
> >> +
> >> +#define DC_TILE_MODE4X4     0x15
> >> +
> >> +enum dc_chip_rev {
> >> +    DC_REV_0,/* For HW_REV_5720,HW_REV_5721_311 */
> >> +    DC_REV_1,/* For HW_REV_5721_30B */
> >> +    DC_REV_2,/* For HW_REV_5721_310 */
> >> +};
> >> +
> >> +enum dc_hw_plane_id {
> >> +    PRIMARY_PLANE_0,
> >> +    OVERLAY_PLANE_0,
> >> +    OVERLAY_PLANE_1,
> >> +    PRIMARY_PLANE_1,
> >> +    OVERLAY_PLANE_2,
> >> +    OVERLAY_PLANE_3,
> >> +    CURSOR_PLANE_0,
> >> +    CURSOR_PLANE_1,
> >> +    PLANE_NUM
> >> +};
> >> +
> >> +enum dc_hw_color_format {
> >> +    FORMAT_X4R4G4B4,//0
> >
> > Space after the comma will make it more readable. Or just define the
> > values explicitly, it will also be more readable.
> >
> >> +    FORMAT_A4R4G4B4,//1
> >> +    FORMAT_X1R5G5B5,//2
> >> +    FORMAT_A1R5G5B5,//3
> >> +    FORMAT_R5G6B5,//4
> >> +    FORMAT_X8R8G8B8,//5
> >> +    FORMAT_A8R8G8B8,//6
> >> +    FORMAT_YUY2,//7
> >> +    FORMAT_UYVY,//8
> >> +    FORMAT_INDEX8,//9
> >> +    FORMAT_MONOCHROME,//10
> >> +    FORMAT_YV12 = 0xf,
> >> +    FORMAT_A8,//16
> >> +    FORMAT_NV12,//17
> >> +    FORMAT_NV16,//18
> >> +    FORMAT_RG16,//19
> >> +    FORMAT_R8,//20
> >> +    FORMAT_NV12_10BIT,//21
> >> +    FORMAT_A2R10G10B10,//22
> >> +    FORMAT_NV16_10BIT,//23
> >> +    FORMAT_INDEX1,//24
> >> +    FORMAT_INDEX2,//25
> >> +    FORMAT_INDEX4,//26
> >> +    FORMAT_P010,//27
> >> +    FORMAT_YUV444,//28
> >> +    FORMAT_YUV444_10BIT,//29
> >> +};
> >> +
> >> +enum dc_hw_yuv_color_space {
> >> +    COLOR_SPACE_601 = 0,
> >> +    COLOR_SPACE_709 = 1,
> >> +    COLOR_SPACE_2020 = 3,
> >> +};
> >> +
> >> +enum dc_hw_rotation {
> >> +    ROT_0 = 0,
> >> +    ROT_90 = 4,
> >> +    ROT_180 = 5,
> >> +    ROT_270 = 6,
> >> +    FLIP_X = 1,
> >> +    FLIP_Y = 2,
> >> +    FLIP_XY = 3,
> >> +};
> >> +
> >> +enum dc_hw_swizzle {
> >> +    SWIZZLE_ARGB = 0,
> >> +    SWIZZLE_RGBA,
> >> +    SWIZZLE_ABGR,
> >> +    SWIZZLE_BGRA,
> >> +};
> >> +
> >> +enum dc_hw_out {
> >> +    OUT_DPI,
> >> +    OUT_DP,
> >> +};
> >> +
> >> +enum dc_hw_cursor_size {
> >> +    CURSOR_SIZE_32X32 = 0,
> >> +    CURSOR_SIZE_64X64,
> >> +};
> >> +
> >> +enum dc_hw_blend_mode {
> >> +    /* out.rgb = plane_alpha * fg.rgb +
> >> +     *              (1 - (plane_alpha * fg.alpha)) * bg.rgb
> >> +     */
> >> +    BLEND_PREMULTI,
> >> +    /* out.rgb = plane_alpha * fg.alpha * fg.rgb +
> >> +     *              (1 - (plane_alpha * fg.alpha)) * bg.rgb
> >> +     */
> >> +    BLEND_COVERAGE,
> >> +    /* out.rgb = plane_alpha * fg.rgb +
> >> +     *              (1 - plane_alpha) * bg.rgb
> >> +     */
> >> +    BLEND_PIXEL_NONE,
> >
> > Can you use defines from <drm/drm_blend.h>?
> >
> >> +};
> >> +
> >> +struct dc_hw_plane_reg {
> >> +    u32 y_address;
> >> +    u32 u_address;
> >> +    u32 v_address;
> >> +    u32 y_stride;
> >> +    u32 u_stride;
> >> +    u32 v_stride;
> >> +    u32 size;
> >> +    u32 top_left;
> >> +    u32 bottom_right;
> >> +    u32 scale_factor_x;
> >> +    u32 scale_factor_y;
> >> +    u32 h_filter_coef_index;
> >> +    u32 h_filter_coef_data;
> >> +    u32 v_filter_coef_index;
> >> +    u32 v_filter_coef_data;
> >> +    u32 init_offset;
> >> +    u32 color_key;
> >> +    u32 color_key_high;
> >> +    u32 clear_value;
> >> +    u32 color_table_index;
> >> +    u32 color_table_data;
> >> +    u32 scale_config;
> >> +    u32 water_mark;
> >> +    u32 degamma_index;
> >> +    u32 degamma_data;
> >> +    u32 degamma_ex_data;
> >> +    u32 src_global_color;
> >> +    u32 dst_global_color;
> >> +    u32 blend_config;
> >> +    u32 roi_origin;
> >> +    u32 roi_size;
> >> +    u32 yuv_to_rgb_coef0;
> >> +    u32 yuv_to_rgb_coef1;
> >> +    u32 yuv_to_rgb_coef2;
> >> +    u32 yuv_to_rgb_coef3;
> >> +    u32 yuv_to_rgb_coef4;
> >> +    u32 yuv_to_rgb_coefd0;
> >> +    u32 yuv_to_rgb_coefd1;
> >> +    u32 yuv_to_rgb_coefd2;
> >> +    u32 y_clamp_bound;
> >> +    u32 uv_clamp_bound;
> >> +    u32 rgb_to_rgb_coef0;
> >> +    u32 rgb_to_rgb_coef1;
> >> +    u32 rgb_to_rgb_coef2;
> >> +    u32 rgb_to_rgb_coef3;
> >> +    u32 rgb_to_rgb_coef4;
> >> +};
> >> +
> >> +struct dc_hw_fb {
> >> +    u32 y_address;
> >> +    u32 u_address;
> >> +    u32 v_address;
> >> +    u32 clear_value;
> >> +    u32 water_mark;
> >> +    u16 y_stride;
> >> +    u16 u_stride;
> >> +    u16 v_stride;
> >> +    u16 width;
> >> +    u16 height;
> >> +    u8      format;
> >> +    u8      tile_mode;
> >> +    u8      rotation;
> >> +    u8      yuv_color_space;
> >> +    u8      swizzle;
> >> +    u8      uv_swizzle;
> >> +    u8      zpos;
> >> +    u8      display_id;
> >> +    bool    clear_enable;
> >> +    bool    dec_enable;
> >> +    bool    enable;
> >> +    bool    dirty;
> >> +};
> >> +
> >> +struct dc_hw_scale {
> >> +    u32 scale_factor_x;
> >> +    u32 scale_factor_y;
> >> +    bool    enable;
> >> +    bool    dirty;
> >> +};
> >> +
> >> +struct dc_hw_position {
> >> +    u16 start_x;
> >> +    u16 start_y;
> >> +    u16 end_x;
> >> +    u16 end_y;
> >> +    bool    dirty;
> >> +};
> >> +
> >> +struct dc_hw_blend {
> >> +    u8      alpha;
> >> +    u8      blend_mode;
> >> +    bool    dirty;
> >> +};
> >> +
> >> +struct dc_hw_colorkey {
> >> +    u32 colorkey;
> >> +    u32 colorkey_high;
> >> +    u8      transparency;
> >> +    bool dirty;
> >> +};
> >> +
> >> +struct dc_hw_roi {
> >> +    u16 x;
> >> +    u16 y;
> >> +    u16 width;
> >> +    u16 height;
> >> +    bool enable;
> >> +    bool dirty;
> >> +};
> >> +
> >> +struct dc_hw_cursor {
> >> +    u32 address;
> >> +    u16 x;
> >> +    u16 y;
> >> +    u16 hot_x;
> >> +    u16 hot_y;
> >> +    u8      size;
> >> +    u8      display_id;
> >> +    bool    enable;
> >> +    bool    dirty;
> >> +};
> >> +
> >> +struct dc_hw_display {
> >> +    u32 bus_format;
> >> +    u16 h_active;
> >> +    u16 h_total;
> >> +    u16 h_sync_start;
> >> +    u16 h_sync_end;
> >> +    u16 v_active;
> >> +    u16 v_total;
> >> +    u16 v_sync_start;
> >> +    u16 v_sync_end;
> >> +    u8      id;
> >> +    bool    h_sync_polarity;
> >> +    bool    v_sync_polarity;
> >> +    bool    enable;
> >> +};
> >> +
> >> +struct dc_hw_gamma {
> >> +    u16 gamma[GAMMA_EX_SIZE][3];
> >> +    bool    enable;
> >> +    bool    dirty;
> >> +};
> >> +
> >> +struct dc_hw_degamma {
> >> +    u16 degamma[DEGAMMA_SIZE][3];
> >> +    u32 mode;
> >> +    bool    dirty;
> >> +};
> >> +
> >> +struct dc_hw_plane {
> >> +    struct dc_hw_fb                 fb;
> >> +    struct dc_hw_position   pos;
> >> +    struct dc_hw_scale              scale;
> >> +    struct dc_hw_blend              blend;
> >> +    struct dc_hw_roi                roi;
> >> +    struct dc_hw_colorkey   colorkey;
> >> +    struct dc_hw_degamma    degamma;
> >> +};
> >> +
> >> +struct dc_hw_qos {
> >> +    u8        low_value;
> >> +    u8        high_value;
> >> +    bool  dirty;
> >> +};
> >> +
> >> +struct dc_hw_read {
> >> +    u32                     reg;
> >> +    u32                     value;
> >> +};
> >> +
> >> +struct dc_hw;
> >> +struct dc_hw_funcs {
> >> +    void (*gamma)(struct dc_hw *hw);
> >> +    void (*plane)(struct dc_hw *hw);
> >> +    void (*display)(struct dc_hw *hw, struct dc_hw_display *display);
> >> +};
> >> +
> >> +struct dc_hw {
> >> +    enum dc_chip_rev rev;
> >> +    enum dc_hw_out          out[DC_DISPLAY_NUM];
> >> +    void                    *hi_base;
> >> +    void                    *reg_base;
> >> +
> >> +    struct dc_hw_display display[DC_DISPLAY_NUM];
> >> +    struct dc_hw_gamma       gamma[DC_DISPLAY_NUM];
> >> +    struct dc_hw_plane       plane[DC_LAYER_NUM];
> >> +    struct dc_hw_cursor  cursor[DC_CURSOR_NUM];
> >> +    struct dc_hw_qos         qos;
> >> +    struct dc_hw_funcs       *func;
> >> +    struct vs_dc_info        *info;
> >> +};
> >> +
> >> +int dc_hw_init(struct dc_hw *hw);
> >> +void dc_hw_deinit(struct dc_hw *hw);
> >> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
> >> +                    struct dc_hw_fb *fb, struct dc_hw_scale *scale,
> >> +                    struct dc_hw_position *pos, struct dc_hw_blend *blend);
> >> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode);
> >> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi);
> >> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
> >> +                       struct dc_hw_colorkey *colorkey);
> >> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos);
> >> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor);
> >> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
> >> +                    u16 r, u16 g, u16 b);
> >> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable);
> >> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display);
> >> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable);
> >> +u32 dc_hw_get_interrupt(struct dc_hw *hw);
> >> +bool dc_hw_check_underflow(struct dc_hw *hw);
> >> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable);
> >> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id);
> >> +void dc_hw_commit(struct dc_hw *hw);
> >> +
> >> +#endif /* __VS_DC_HW_H__ */
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
> >> index da7698c3d..3cd533cfa 100644
> >> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
> >> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> >> @@ -21,6 +21,7 @@
> >>
> >>   #include "vs_drv.h"
> >>   #include "vs_modeset.h"
> >> +#include "vs_dc.h"
> >>
> >>   #define DRV_NAME   "verisilicon"
> >>   #define DRV_DESC   "Verisilicon DRM driver"
> >> @@ -123,6 +124,7 @@ static const struct component_master_ops vs_drm_ops = {
> >>   };
> >>
> >>   static struct platform_driver *drm_sub_drivers[] = {
> >> +    &dc_platform_driver,
> >>
> >>   };
> >>
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c
> >> new file mode 100644
> >> index 000000000..9bd066015
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_plane.c
> >> @@ -0,0 +1,526 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#include <drm/drm_atomic.h>
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_blend.h>
> >> +#include <drm/drm_gem_dma_helper.h>
> >> +#include <drm/drm_fb_dma_helper.h>
> >> +#include <drm/drm_framebuffer.h>
> >> +#include <drm/drm_plane.h>
> >> +#include <drm/drm_plane_helper.h>
> >> +
> >> +#include <drm/vs_drm.h>
> >> +
> >> +#include "vs_plane.h"
> >> +#include "vs_drv.h"
> >> +#include "vs_dc.h"
> >> +
> >> +static void vs_plane_reset(struct drm_plane *plane)
> >> +{
> >> +    struct vs_plane_state *state;
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +
> >> +    if (plane->state) {
> >> +            __drm_atomic_helper_plane_destroy_state(plane->state);
> >> +
> >> +            state = to_vs_plane_state(plane->state);
> >> +            kfree(state);
> >> +            plane->state = NULL;
> >> +    }
> >> +
> >
> > Use vs_plane_destroy_state directly. This way you won't miss a refcount
> > on blobs.
> >
> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
> >> +    if (!state)
> >> +            return;
> >> +
> >> +    state->degamma = VS_DEGAMMA_DISABLE;
> >> +    state->degamma_changed = false;
> >> +    state->base.zpos = vs_plane->id;
> >> +    memset(&state->status, 0, sizeof(state->status));
> >> +
> >> +    __drm_atomic_helper_plane_reset(plane, &state->base);
> >> +}
> >> +
> >> +static void _vs_plane_duplicate_blob(struct vs_plane_state *state,
> >> +                                 struct vs_plane_state *ori_state)
> >> +{
> >> +    state->watermark = ori_state->watermark;
> >> +    state->color_mgmt = ori_state->color_mgmt;
> >> +    state->roi = ori_state->roi;
> >> +
> >> +    if (state->watermark)
> >> +            drm_property_blob_get(state->watermark);
> >> +    if (state->color_mgmt)
> >> +            drm_property_blob_get(state->color_mgmt);
> >> +    if (state->roi)
> >> +            drm_property_blob_get(state->roi);
> >> +}
> >> +
> >> +static int
> >> +_vs_plane_set_property_blob_from_id(struct drm_device *dev,
> >> +                                struct drm_property_blob **blob,
> >> +                                u64 blob_id,
> >> +                                size_t expected_size)
> >> +{
> >> +    struct drm_property_blob *new_blob = NULL;
> >> +
> >> +    if (blob_id) {
> >> +            new_blob = drm_property_lookup_blob(dev, blob_id);
> >> +            if (!new_blob)
> >> +                    return -EINVAL;
> >> +
> >> +            if (new_blob->length != expected_size) {
> >> +                    drm_property_blob_put(new_blob);
> >> +                    return -EINVAL;
> >> +            }
> >> +    }
> >> +
> >> +    drm_property_replace_blob(blob, new_blob);
> >> +    drm_property_blob_put(new_blob);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static struct drm_plane_state *
> >> +vs_plane_atomic_duplicate_state(struct drm_plane *plane)
> >> +{
> >> +    struct vs_plane_state *ori_state;
> >> +    struct vs_plane_state *state;
> >> +
> >> +    if (WARN_ON(!plane->state))
> >> +            return NULL;
> >> +
> >> +    ori_state = to_vs_plane_state(plane->state);
> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
> >> +    if (!state)
> >> +            return NULL;
> >> +
> >> +    __drm_atomic_helper_plane_duplicate_state(plane, &state->base);
> >> +
> >> +    state->degamma = ori_state->degamma;
> >> +    state->degamma_changed = ori_state->degamma_changed;
> >> +
> >> +    _vs_plane_duplicate_blob(state, ori_state);
> >> +    memcpy(&state->status, &ori_state->status, sizeof(ori_state->status));
> >> +
> >> +    return &state->base;
> >> +}
> >> +
> >> +static void vs_plane_atomic_destroy_state(struct drm_plane *plane,
> >> +                                      struct drm_plane_state *state)
> >> +{
> >> +    struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
> >> +
> >> +    __drm_atomic_helper_plane_destroy_state(state);
> >> +
> >> +    drm_property_blob_put(vs_plane_state->watermark);
> >> +    drm_property_blob_put(vs_plane_state->color_mgmt);
> >> +    drm_property_blob_put(vs_plane_state->roi);
> >> +    kfree(vs_plane_state);
> >> +}
> >> +
> >> +static int vs_plane_atomic_set_property(struct drm_plane *plane,
> >> +                                    struct drm_plane_state *state,
> >> +                                    struct drm_property *property,
> >> +                                    uint64_t val)
> >> +{
> >> +    struct drm_device *dev = plane->dev;
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +    struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
> >> +    int ret = 0;
> >> +
> >> +    if (property == vs_plane->degamma_mode) {
> >> +            if (vs_plane_state->degamma != val) {
> >> +                    vs_plane_state->degamma = val;
> >> +                    vs_plane_state->degamma_changed = true;
> >> +            } else {
> >> +                    vs_plane_state->degamma_changed = false;
> >> +            }
> >> +    } else if (property == vs_plane->watermark_prop) {
> >> +            ret = _vs_plane_set_property_blob_from_id(dev,
> >> +                                                      &vs_plane_state->watermark,
> >> +                                                      val,
> >> +                                                      sizeof(struct drm_vs_watermark));
> >> +            return ret;
> >> +    } else if (property == vs_plane->color_mgmt_prop) {
> >> +            ret = _vs_plane_set_property_blob_from_id(dev,
> >> +                                                      &vs_plane_state->color_mgmt,
> >> +                                                      val,
> >> +                                                      sizeof(struct drm_vs_color_mgmt));
> >> +            return ret;
> >> +    } else if (property == vs_plane->roi_prop) {
> >> +            ret = _vs_plane_set_property_blob_from_id(dev,
> >> +                                                      &vs_plane_state->roi,
> >> +                                                      val,
> >> +                                                      sizeof(struct drm_vs_roi));
> >> +            return ret;
> >> +    } else {
> >> +            return -EINVAL;
> >> +    }
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int vs_plane_atomic_get_property(struct drm_plane *plane,
> >> +                                    const struct drm_plane_state *state,
> >> +                                    struct drm_property *property,
> >> +                                    uint64_t *val)
> >> +{
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +    const struct vs_plane_state *vs_plane_state =
> >> +            container_of(state, const struct vs_plane_state, base);
> >> +
> >> +    if (property == vs_plane->degamma_mode)
> >> +            *val = vs_plane_state->degamma;
> >> +    else if (property == vs_plane->watermark_prop)
> >> +            *val = (vs_plane_state->watermark) ?
> >> +                                    vs_plane_state->watermark->base.id : 0;
> >> +    else if (property == vs_plane->color_mgmt_prop)
> >> +            *val = (vs_plane_state->color_mgmt) ?
> >> +                                    vs_plane_state->color_mgmt->base.id : 0;
> >
> > degamma and color management should use standard properties.
> >
> >> +    else if (property == vs_plane->roi_prop)
> >> +            *val = (vs_plane_state->roi) ?
> >> +                                    vs_plane_state->roi->base.id : 0;
> >> +    else
> >> +            return -EINVAL;
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static bool vs_format_mod_supported(struct drm_plane *plane,
> >> +                                u32 format,
> >> +                                u64 modifier)
> >> +{
> >> +    int i;
> >> +
> >> +    /* We always have to allow these modifiers:
> >> +     * 1. Core DRM checks for LINEAR support if userspace does not provide modifiers.
> >> +     * 2. Not passing any modifiers is the same as explicitly passing INVALID.
> >> +     */
> >> +    if (modifier == DRM_FORMAT_MOD_LINEAR)
> >> +            return true;
> >> +
> >> +    /* Check that the modifier is on the list of the plane's supported modifiers. */
> >> +    for (i = 0; i < plane->modifier_count; i++) {
> >> +            if (modifier == plane->modifiers[i])
> >> +                    break;
> >> +    }
> >> +
> >> +    if (i == plane->modifier_count)
> >> +            return false;
> >> +
> >> +    return true;
> >> +}
> >> +
> >> +const struct drm_plane_funcs vs_plane_funcs = {
> >> +    .update_plane           = drm_atomic_helper_update_plane,
> >> +    .disable_plane          = drm_atomic_helper_disable_plane,
> >> +    .reset                  = vs_plane_reset,
> >> +    .atomic_duplicate_state = vs_plane_atomic_duplicate_state,
> >> +    .atomic_destroy_state   = vs_plane_atomic_destroy_state,
> >> +    .atomic_set_property    = vs_plane_atomic_set_property,
> >> +    .atomic_get_property    = vs_plane_atomic_get_property,
> >> +    .format_mod_supported   = vs_format_mod_supported,
> >> +};
> >> +
> >> +static unsigned char vs_get_plane_number(struct drm_framebuffer *fb)
> >> +{
> >> +    const struct drm_format_info *info;
> >> +
> >> +    if (!fb)
> >> +            return 0;
> >> +
> >> +    info = drm_format_info(fb->format->format);
> >> +    if (!info || info->num_planes > DRM_FORMAT_MAX_PLANES)
> >> +            return 0;
> >> +
> >> +    return info->num_planes;
> >> +}
> >> +
> >> +static int vs_plane_atomic_check(struct drm_plane *plane,
> >> +                             struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> >> +                                                                             plane);
> >> +    unsigned char i, num_planes;
> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
> >> +
> >> +    if (!crtc || !fb)
> >> +            return 0;
> >> +
> >> +    num_planes = vs_get_plane_number(fb);
> >> +
> >> +    for (i = 0; i < num_planes; i++) {
> >> +            dma_addr_t dma_addr;
> >> +
> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
> >> +            plane_state->dma_addr[i] = dma_addr;
> >> +    }
> >> +
> >> +    return vs_dc_check_plane(dc, plane, state);
> >> +}
> >> +
> >> +static int vs_cursor_plane_atomic_check(struct drm_plane *plane,
> >> +                                    struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> >> +                                                                      plane);
> >> +    unsigned char i, num_planes;
> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
> >> +
> >> +    if (!crtc || !fb)
> >> +            return 0;
> >> +
> >> +    num_planes = vs_get_plane_number(fb);
> >> +
> >> +    for (i = 0; i < num_planes; i++) {
> >> +            dma_addr_t dma_addr;
> >> +
> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
> >> +            plane_state->dma_addr[i] = dma_addr;
> >> +    }
> >> +
> >> +    return vs_dc_check_cursor_plane(dc, plane, state);
> >> +}
> >> +
> >> +static void vs_plane_atomic_update(struct drm_plane *plane,
> >> +                               struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
> >> +                                                                      plane);
> >
> > New line after the equal sign will be better.
> >
> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> >> +                                                                      plane);
> >> +
> >> +    unsigned char i, num_planes;
> >> +    struct drm_framebuffer *fb;
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    if (!new_state->fb || !new_state->crtc)
> >> +            return;
> >
> > if (!new_state->visible) ?
> >
> >> +
> >> +    fb = new_state->fb;
> >> +
> >> +    drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
> >> +
> >> +    num_planes = vs_get_plane_number(fb);
> >> +
> >> +    for (i = 0; i < num_planes; i++) {
> >> +            dma_addr_t dma_addr;
> >> +
> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
> >> +            plane_state->dma_addr[i] = dma_addr;
> >> +    }
> >> +
> >> +    plane_state->status.src = drm_plane_state_src(new_state);
> >> +    plane_state->status.dest = drm_plane_state_dest(new_state);
> >> +
> >> +    vs_dc_update_plane(dc, vs_plane, plane, state);
> >> +}
> >> +
> >> +static void vs_cursor_plane_atomic_update(struct drm_plane *plane,
> >> +                                      struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
> >> +                                                                       plane);
> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> >> +                                                                       plane);
> >> +    unsigned char i, num_planes;
> >> +    struct drm_framebuffer *fb;
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    if (!new_state->fb || !new_state->crtc)
> >
> > and here.
> >
> >> +            return;
> >> +
> >> +    fb = new_state->fb;
> >> +    drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
> >> +
> >> +    num_planes = vs_get_plane_number(fb);
> >> +
> >> +    for (i = 0; i < num_planes; i++) {
> >> +            dma_addr_t dma_addr;
> >> +
> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
> >> +            plane_state->dma_addr[i] = dma_addr;
> >> +    }
> >> +
> >> +    plane_state->status.src = drm_plane_state_src(new_state);
> >> +    plane_state->status.dest = drm_plane_state_dest(new_state);
> >> +
> >> +    vs_dc_update_cursor_plane(dc, vs_plane, plane, state);
> >> +}
> >> +
> >> +static void vs_plane_atomic_disable(struct drm_plane *plane,
> >> +                                struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> >> +                                                                       plane);
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    vs_dc_disable_plane(dc, vs_plane, old_state);
> >> +}
> >> +
> >> +static void vs_cursor_plane_atomic_disable(struct drm_plane *plane,
> >> +                                       struct drm_atomic_state *state)
> >> +{
> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> >> +                                                                       plane);
> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> +
> >> +    vs_dc_disable_cursor_plane(dc, vs_plane, old_state);
> >> +}
> >> +
> >> +const struct drm_plane_helper_funcs primary_plane_helpers = {
> >> +    .atomic_check   = vs_plane_atomic_check,
> >> +    .atomic_update  = vs_plane_atomic_update,
> >> +    .atomic_disable = vs_plane_atomic_disable,
> >> +};
> >> +
> >> +const struct drm_plane_helper_funcs overlay_plane_helpers = {
> >> +    .atomic_check   = vs_plane_atomic_check,
> >> +    .atomic_update  = vs_plane_atomic_update,
> >> +    .atomic_disable = vs_plane_atomic_disable,
> >> +};
> >> +
> >> +const struct drm_plane_helper_funcs cursor_plane_helpers = {
> >> +    .atomic_check   = vs_cursor_plane_atomic_check,
> >> +    .atomic_update  = vs_cursor_plane_atomic_update,
> >> +    .atomic_disable = vs_cursor_plane_atomic_disable,
> >> +};
> >> +
> >> +static const struct drm_prop_enum_list vs_degamma_mode_enum_list[] = {
> >> +    { VS_DEGAMMA_DISABLE,   "disabled" },
> >> +    { VS_DEGAMMA_BT709,     "preset degamma for BT709" },
> >> +    { VS_DEGAMMA_BT2020,    "preset degamma for BT2020" },
> >> +};
> >> +
> >> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
> >> +                             struct vs_plane_info *info,
> >> +                             unsigned int layer_num,
> >> +                             unsigned int possible_crtcs)
> >> +{
> >> +    struct vs_plane *plane;
> >> +    int ret;
> >> +
> >> +    if (!info)
> >> +            return NULL;
> >> +
> >> +    plane = drmm_universal_plane_alloc(drm_dev, struct vs_plane, base,
> >> +                                       possible_crtcs,
> >> +                                       &vs_plane_funcs,
> >> +                                       info->formats, info->num_formats,
> >> +                                       info->modifiers, info->type,
> >> +                                       info->name ? info->name : NULL);
> >> +    if (IS_ERR(plane))
> >> +            return ERR_CAST(plane);
> >> +
> >> +    if (info->type == DRM_PLANE_TYPE_PRIMARY)
> >> +            drm_plane_helper_add(&plane->base, &primary_plane_helpers);
> >> +    else if (info->type == DRM_PLANE_TYPE_CURSOR)
> >> +            drm_plane_helper_add(&plane->base, &cursor_plane_helpers);
> >> +    else
> >> +            drm_plane_helper_add(&plane->base, &overlay_plane_helpers);
> >> +
> >> +    /* Set up the plane properties */
> >> +    if (info->degamma_size) {
> >> +            plane->degamma_mode =
> >> +            drm_property_create_enum(drm_dev, 0,
> >> +                                     "DEGAMMA_MODE",
> >> +                                     vs_degamma_mode_enum_list,
> >> +                                     ARRAY_SIZE(vs_degamma_mode_enum_list));
> >> +
> >> +            if (!plane->degamma_mode)
> >> +                    return NULL;
> >> +
> >> +            drm_object_attach_property(&plane->base.base,
> >> +                                       plane->degamma_mode,
> >> +                                       VS_DEGAMMA_DISABLE);
> >> +    }
> >> +
> >> +    if (info->rotation) {
> >> +            ret = drm_plane_create_rotation_property(&plane->base,
> >> +                                                     DRM_MODE_ROTATE_0,
> >> +                                                     info->rotation);
> >> +            if (ret)
> >> +                    return NULL;
> >> +    }
> >> +
> >> +    if (info->blend_mode) {
> >> +            ret = drm_plane_create_blend_mode_property(&plane->base,
> >> +                                                       info->blend_mode);
> >> +            if (ret)
> >> +                    return NULL;
> >> +            ret = drm_plane_create_alpha_property(&plane->base);
> >> +            if (ret)
> >> +                    return NULL;
> >> +    }
> >> +
> >> +    if (info->color_encoding) {
> >> +            ret = drm_plane_create_color_properties(&plane->base,
> >> +                                                    info->color_encoding,
> >> +                                                    BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
> >> +                                                    DRM_COLOR_YCBCR_BT709,
> >> +                                                    DRM_COLOR_YCBCR_LIMITED_RANGE);
> >> +            if (ret)
> >> +                    return NULL;
> >> +    }
> >> +
> >> +    if (info->zpos != 255) {
> >> +            ret = drm_plane_create_zpos_property(&plane->base, info->zpos, 0,
> >> +                                                 layer_num - 1);
> >> +            if (ret)
> >> +                    return NULL;
> >> +    } else {
> >> +            ret = drm_plane_create_zpos_immutable_property(&plane->base,
> >> +                                                           info->zpos);
> >> +            if (ret)
> >> +                    return NULL;
> >> +    }
> >> +
> >> +    if (info->watermark) {
> >> +            plane->watermark_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
> >> +                                                        "WATERMARK", 0);
> >> +            if (!plane->watermark_prop)
> >> +                    return NULL;
> >> +            drm_object_attach_property(&plane->base.base, plane->watermark_prop, 0);
> >> +    }
> >> +
> >> +    if (info->color_mgmt) {
> >> +            plane->color_mgmt_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
> >> +                                                         "COLOR_CONFIG", 0);
> >> +            if (!plane->color_mgmt_prop)
> >> +                    return NULL;
> >> +
> >> +            drm_object_attach_property(&plane->base.base, plane->color_mgmt_prop, 0);
> >> +    }
> >> +
> >> +    if (info->roi) {
> >> +            plane->roi_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
> >> +                                                  "ROI", 0);
> >> +            if (!plane->roi_prop)
> >> +                    return NULL;
> >> +
> >> +            drm_object_attach_property(&plane->base.base, plane->roi_prop, 0);
> >> +    }
> >> +
> >> +    return plane;
> >> +}
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h
> >> new file mode 100644
> >> index 000000000..554b74e96
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_plane.h
> >> @@ -0,0 +1,58 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#ifndef __VS_PLANE_H__
> >> +#define __VS_PLANE_H__
> >> +
> >> +#include <drm/drm_fourcc.h>
> >> +#include <drm/drm_plane_helper.h>
> >> +
> >> +#include "vs_type.h"
> >> +
> >> +struct vs_plane_status {
> >> +    u32 tile_mode;
> >> +    struct drm_rect src;
> >> +    struct drm_rect dest;
> >> +};
> >> +
> >> +struct vs_plane_state {
> >> +    struct drm_plane_state base;
> >> +    struct vs_plane_status status; /* for debugfs */
> >> +
> >> +    struct drm_property_blob *watermark;
> >> +    struct drm_property_blob *color_mgmt;
> >> +    struct drm_property_blob *roi;
> >> +    dma_addr_t dma_addr[DRM_FORMAT_MAX_PLANES];
> >> +
> >> +    u32 degamma;
> >> +    bool degamma_changed;
> >> +};
> >> +
> >> +struct vs_plane {
> >> +    struct drm_plane base;
> >> +    u8 id;
> >> +
> >> +    struct drm_property *degamma_mode;
> >> +    struct drm_property *watermark_prop;
> >> +    struct drm_property *color_mgmt_prop;
> >> +    struct drm_property *roi_prop;
> >> +};
> >> +
> >> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
> >> +                             struct vs_plane_info *info,
> >> +                             unsigned int layer_num,
> >> +                             unsigned int possible_crtcs);
> >> +
> >> +static inline struct vs_plane *to_vs_plane(struct drm_plane *plane)
> >> +{
> >> +    return container_of(plane, struct vs_plane, base);
> >> +}
> >> +
> >> +static inline struct vs_plane_state *
> >> +to_vs_plane_state(struct drm_plane_state *state)
> >> +{
> >> +    return container_of(state, struct vs_plane_state, base);
> >> +}
> >> +#endif /* __VS_PLANE_H__ */
> >> diff --git a/drivers/gpu/drm/verisilicon/vs_type.h b/drivers/gpu/drm/verisilicon/vs_type.h
> >> new file mode 100644
> >> index 000000000..7d3378e29
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/verisilicon/vs_type.h
> >> @@ -0,0 +1,69 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> + */
> >> +
> >> +#ifndef __VS_TYPE_H__
> >> +#define __VS_TYPE_H__
> >> +
> >> +#include <drm/drm_plane.h>
> >> +#include <drm/drm_plane_helper.h>
> >> +
> >> +struct vs_plane_info {
> >> +    const char *name;
> >> +    u8 id;
> >> +    enum drm_plane_type type;
> >> +    unsigned int num_formats;
> >> +    const u32 *formats;
> >> +    u8 num_modifiers;
> >> +    const u64 *modifiers;
> >> +    unsigned int min_width;
> >> +    unsigned int min_height;
> >> +    unsigned int max_width;
> >> +    unsigned int max_height;
> >> +    unsigned int rotation;
> >> +    unsigned int blend_mode;
> >> +    unsigned int color_encoding;
> >> +
> >> +    /* 0 means no de-gamma LUT */
> >> +    unsigned int degamma_size;
> >> +
> >> +    int min_scale; /* 16.16 fixed point */
> >> +    int max_scale; /* 16.16 fixed point */
> >> +
> >> +    /* default zorder value,
> >> +     * and 255 means unsupported zorder capability
> >> +     */
> >> +    u8       zpos;
> >> +
> >> +    bool watermark;
> >> +    bool color_mgmt;
> >> +    bool roi;
> >> +};
> >> +
> >> +struct vs_dc_info {
> >> +    const char *name;
> >> +
> >> +    u8 panel_num;
> >> +
> >> +    /* planes */
> >> +    u8 plane_num;
> >> +    const struct vs_plane_info *planes;
> >> +
> >> +    u8 layer_num;
> >> +    unsigned int max_bpc;
> >> +    unsigned int color_formats;
> >> +
> >> +    /* 0 means no gamma LUT */
> >> +    u16 gamma_size;
> >> +    u8 gamma_bits;
> >> +
> >> +    u16 pitch_alignment;
> >> +
> >> +    bool pipe_sync;
> >> +    bool background;
> >> +    bool panel_sync;
> >> +    bool cap_dec;
> >> +};
> >> +
> >> +#endif /* __VS_TYPE_H__ */
> >



-- 
With best wishes
Dmitry

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
  2023-11-14 10:59         ` Dmitry Baryshkov
  (?)
@ 2023-11-15 13:30           ` Keith Zhao
  -1 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-11-15 13:30 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Maxime Ripard, Jagan Teki, Rob Herring,
	Chris Morgan, Paul Walmsley, Bjorn Andersson, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Shawn Guo, christian.koenig



On 2023/11/14 18:59, Dmitry Baryshkov wrote:
> On Tue, 14 Nov 2023 at 12:42, Keith Zhao <keith.zhao@starfivetech.com> wrote:
>>
>>
>>
>> On 2023/10/26 3:28, Dmitry Baryshkov wrote:
>> > On 25/10/2023 13:39, Keith Zhao wrote:
>> >> add 2 crtcs and 8 planes in vs-drm
>> >>
>> >> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>> >> ---
>> >>   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
>> >>   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
>> >>   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
>> >>   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
>> >>   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
>> >>   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
>> >>   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
>> >>   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
>> >>   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
>> >>   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
>> >>   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
>> >>   11 files changed, 4494 insertions(+), 2 deletions(-)
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
>> >>
>> >> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
>> >> index 7d3be305b..1d48016ca 100644
>> >> --- a/drivers/gpu/drm/verisilicon/Makefile
>> >> +++ b/drivers/gpu/drm/verisilicon/Makefile
>> >> @@ -1,7 +1,11 @@
>> >>   # SPDX-License-Identifier: GPL-2.0
>> >>
>> >> -vs_drm-objs := vs_drv.o \
>> >> -            vs_modeset.o
>> >> +vs_drm-objs := vs_dc_hw.o \
>> >> +            vs_dc.o \
>> >> +            vs_crtc.o \
>> >> +            vs_drv.o \
>> >> +            vs_modeset.o \
>> >> +            vs_plane.o
>> >>
>> >>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>> >>
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> >> new file mode 100644
>> >> index 000000000..8a658ea77
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> >> @@ -0,0 +1,257 @@
>> >> +// SPDX-License-Identifier: GPL-2.0
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + *
>> >> + */
>> >> +
>> >> +#include <linux/clk.h>
>> >> +#include <linux/debugfs.h>
>> >> +#include <linux/media-bus-format.h>
>> >> +
>> >> +#include <drm/drm_atomic_helper.h>
>> >> +#include <drm/drm_atomic.h>
>> >> +#include <drm/drm_crtc.h>
>> >> +#include <drm/drm_gem_atomic_helper.h>
>> >> +#include <drm/drm_vblank.h>
>> >> +#include <drm/vs_drm.h>
>> >> +
>> >> +#include "vs_crtc.h"
>> >> +#include "vs_dc.h"
>> >> +#include "vs_drv.h"
>> >> +
>> >> +static void vs_crtc_reset(struct drm_crtc *crtc)
>> >> +{
>> >> +    struct vs_crtc_state *state;
>> >> +
>> >> +    if (crtc->state) {
>> >> +            __drm_atomic_helper_crtc_destroy_state(crtc->state);
>> >> +
>> >> +            state = to_vs_crtc_state(crtc->state);
>> >> +            kfree(state);
>> >> +            crtc->state = NULL;
>> >> +    }
>> >
>> > You can call your crtc_destroy_state function directly here.
>> >
>> >> +
>> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
>> >> +    if (!state)
>> >> +            return;
>> >> +
>> >> +    __drm_atomic_helper_crtc_reset(crtc, &state->base);
>> >> +}
>> >> +
>> >> +static struct drm_crtc_state *
>> >> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
>> >> +{
>> >> +    struct vs_crtc_state *ori_state;
>> >
>> > It might be a matter of taste, but it is usually old_state.
>> >
>> >> +    struct vs_crtc_state *state;
>> >> +
>> >> +    if (!crtc->state)
>> >> +            return NULL;
>> >> +
>> >> +    ori_state = to_vs_crtc_state(crtc->state);
>> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
>> >> +    if (!state)
>> >> +            return NULL;
>> >> +
>> >> +    __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
>> >> +
>> >> +    state->output_fmt = ori_state->output_fmt;
>> >> +    state->encoder_type = ori_state->encoder_type;
>> >> +    state->bpp = ori_state->bpp;
>> >> +    state->underflow = ori_state->underflow;
>> >
>> > Can you use kmemdup instead?
>> >
>> >> +
>> >> +    return &state->base;
>> >> +}
>> >> +
>> >> +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
>> >> +                                     struct drm_crtc_state *state)
>> >> +{
>> >> +    __drm_atomic_helper_crtc_destroy_state(state);
>> >> +    kfree(to_vs_crtc_state(state));
>> >> +}
>> >> +
>> >> +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
>> >> +{
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    vs_dc_enable_vblank(dc, true);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
>> >> +{
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    vs_dc_enable_vblank(dc, false);
>> >> +}
>> >> +
>> >> +static const struct drm_crtc_funcs vs_crtc_funcs = {
>> >> +    .set_config             = drm_atomic_helper_set_config,
>> >> +    .page_flip              = drm_atomic_helper_page_flip,
>> >
>> > destroy is required, see drm_mode_config_cleanup()
>>
>> hi Dmitry:
>> if define destroy in drm_crtc_funcs,
>> it will make __drmm_crtc_init_with_planes unhappy
> 
> Ack, I missed that you have been using drmm_crtc_init. BTW, I checked
> your code, you should be able to switch drm
> drmm_crtc_alloc_with_planes().
> 
yes 
I done the replace and it can work well.

>>
>> see:
>> __printf(6, 0)
>> static int __drmm_crtc_init_with_planes(struct drm_device *dev,
>>                                         struct drm_crtc *crtc,
>>                                         struct drm_plane *primary,
>>                                         struct drm_plane *cursor,
>>                                         const struct drm_crtc_funcs *funcs,
>>                                         const char *name,
>>                                         va_list args)
>> {
>>         int ret;
>>
>>         drm_WARN_ON(dev, funcs && funcs->destroy);
>>
>> ........
>> }
>>
>> It should not need to be defined here, I think
>>
>> >
>> >> +    .reset                  = vs_crtc_reset,
>> >> +    .atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
>> >> +    .atomic_destroy_state   = vs_crtc_atomic_destroy_state,
>> >
>> > please consider adding atomic_print_state to output driver-specific bits.
>> >
>> >> +    .enable_vblank          = vs_crtc_enable_vblank,
>> >> +    .disable_vblank         = vs_crtc_disable_vblank,
>> >> +};
>> >> +
>> >> +static u8 cal_pixel_bits(u32 bus_format)
>> >
>> > This looks like a generic helper code, which can go to a common place.
I don't know if I understand correctly
Here I remove static
Add 2 lines in vs_drv.h

/* vs_crtc.c */
u8 cal_pixel_bits(u32 bus_format);

to make it common 

>> >
>> >> +{
>> >> +    u8 bpp;
>> >> +
>> >> +    switch (bus_format) {
>> >> +    case MEDIA_BUS_FMT_RGB565_1X16:
>> >> +    case MEDIA_BUS_FMT_UYVY8_1X16:
>> >> +            bpp = 16;
>> >> +            break;
>> >> +    case MEDIA_BUS_FMT_RGB666_1X18:
>> >> +    case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
>> >> +            bpp = 18;
>> >> +            break;
>> >> +    case MEDIA_BUS_FMT_UYVY10_1X20:
>> >> +            bpp = 20;
>> >> +            break;
>> >> +    case MEDIA_BUS_FMT_BGR888_1X24:
>> >> +    case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
>> >> +    case MEDIA_BUS_FMT_YUV8_1X24:
>> >> +            bpp = 24;
>> >> +            break;
>> >> +    case MEDIA_BUS_FMT_RGB101010_1X30:
>> >> +    case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
>> >> +    case MEDIA_BUS_FMT_YUV10_1X30:
>> >> +            bpp = 30;
>> >> +            break;
>> >> +    default:
>> >> +            bpp = 24;
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    return bpp;
>> >> +}
>> >> +
>> >> +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
>> >> +                              struct drm_atomic_state *state)
>> >> +{
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
>> >> +
>> >> +    vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
>> >> +
>> >> +    vs_dc_enable(dc, crtc);
>> >> +    drm_crtc_vblank_on(crtc);
>> >> +}
>> >> +
>> >> +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
>> >> +                               struct drm_atomic_state *state)
>> >> +{
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    drm_crtc_vblank_off(crtc);
>> >> +
>> >> +    vs_dc_disable(dc, crtc);
>> >> +
>> >> +    if (crtc->state->event && !crtc->state->active) {
>> >> +            spin_lock_irq(&crtc->dev->event_lock);
>> >> +            drm_crtc_send_vblank_event(crtc, crtc->state->event);
>> >> +            spin_unlock_irq(&crtc->dev->event_lock);
>> >> +
>> >> +            crtc->state->event = NULL;
>> >
>> > I think even should be cleared within the lock.
>> >
>> >> +    }
>> >> +}
>> >> +
>> >> +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
>> >> +                             struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
>> >> +                                                                      crtc);
>> >> +
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct device *dev = vs_crtc->dev;
>> >> +    struct drm_property_blob *blob = crtc->state->gamma_lut;
>> >> +    struct drm_color_lut *lut;
>> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
>> >> +
>> >> +    if (crtc_state->color_mgmt_changed) {
>> >> +            if (blob && blob->length) {
>> >> +                    lut = blob->data;
>> >> +                    vs_dc_set_gamma(dc, crtc, lut,
>> >> +                                    blob->length / sizeof(*lut));
>> >> +                    vs_dc_enable_gamma(dc, crtc, true);
>> >> +            } else {
>> >> +                    vs_dc_enable_gamma(dc, crtc, false);
>> >> +            }
>> >> +    }
>> >> +}
>> >> +
>> >> +static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
>> >> +                             struct drm_atomic_state *state)
>> >> +{
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct drm_pending_vblank_event *event = crtc->state->event;
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    vs_dc_commit(dc);
>> >> +
>> >> +    if (event) {
>> >> +            WARN_ON(drm_crtc_vblank_get(crtc) != 0);
>> >> +
>> >> +            spin_lock_irq(&crtc->dev->event_lock);
>> >> +            drm_crtc_arm_vblank_event(crtc, event);
>> >> +            spin_unlock_irq(&crtc->dev->event_lock);
>> >> +            crtc->state->event = NULL;
>> >
>> > I think even should be cleared within the lock.
>> >
>> >> +    }
>> >> +}
>> >> +
>> >> +static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
>> >> +    .atomic_check = drm_crtc_helper_atomic_check,
>> >> +    .atomic_enable  = vs_crtc_atomic_enable,
>> >> +    .atomic_disable = vs_crtc_atomic_disable,
>> >> +    .atomic_begin   = vs_crtc_atomic_begin,
>> >> +    .atomic_flush   = vs_crtc_atomic_flush,
>> >> +};
>> >> +
>> >> +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
>> >> +    { VS_SINGLE_DC,                         "single dc mode" },
>> >> +    { VS_MULTI_DC_PRIMARY,          "primary dc for multi dc mode" },
>> >> +    { VS_MULTI_DC_SECONDARY,        "secondary dc for multi dc mode" },
>> >> +};
>> >> +
>> >> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
>> >> +                           struct vs_dc_info *info)
>> >> +{
>> >> +    struct vs_crtc *crtc;
>> >> +    int ret;
>> >> +
>> >> +    if (!info)
>> >> +            return NULL;
>> >> +
>> >> +    crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
>> >> +    if (!crtc)
>> >> +            return NULL;
>> >> +
>> >> +    ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
>> >> +                                     NULL, NULL, &vs_crtc_funcs,
>> >> +                                     info->name ? info->name : NULL);
>> >
>> > It might be better to add drmm_crtc_init() helper.
	drmm_crtc_alloc_with_planes used:
	...
	struct vs_crtc *crtc;
	int ret;

	if (!info)
		return NULL;

	crtc = drmm_crtc_alloc_with_planes(drm_dev, struct vs_crtc, base,
					       NULL, NULL,
					       &vs_crtc_funcs, info->name ? info->name : NULL);
	...

>> >
>> >> +    if (ret)
>> >> +            return NULL;
>> >> +
>> >> +    drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
>> >> +
>> >> +    if (info->gamma_size) {
>> >> +            ret = drm_mode_crtc_set_gamma_size(&crtc->base,
>> >> +                                               info->gamma_size);
>> >> +            if (ret)
>> >> +                    return NULL;
>> >> +
>> >> +            drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
>> >> +                                       info->gamma_size);
>> >> +    }
>> >> +
>> >> +    crtc->max_bpc = info->max_bpc;
>> >> +    crtc->color_formats = info->color_formats;
>> >> +    return crtc;
>> >> +}
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h
>> >> new file mode 100644
>> >> index 000000000..526dd63e5
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.h
>> >> @@ -0,0 +1,43 @@
>> >> +/* SPDX-License-Identifier: GPL-2.0 */
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#ifndef __VS_CRTC_H__
>> >> +#define __VS_CRTC_H__
>> >> +
>> >> +#include <drm/drm_crtc.h>
>> >> +#include <drm/drm_crtc_helper.h>
>> >> +
>> >> +#include "vs_type.h"
>> >> +
>> >> +struct vs_crtc_state {
>> >> +    struct drm_crtc_state base;
>> >> +
>> >> +    u32 output_fmt;
>> >> +    u8 encoder_type;
>> >> +    u8 bpp;
>> >> +    bool underflow;
>> >> +};
>> >> +
>> >> +struct vs_crtc {
>> >> +    struct drm_crtc base;
>> >> +    struct device *dev;
>> >> +    unsigned int max_bpc;
>> >> +    unsigned int color_formats;
>> >> +};
>> >> +
>> >> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
>> >> +                           struct vs_dc_info *info);
>> >> +
>> >> +static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc)
>> >> +{
>> >> +    return container_of(crtc, struct vs_crtc, base);
>> >> +}
>> >> +
>> >> +static inline struct vs_crtc_state *
>> >> +to_vs_crtc_state(struct drm_crtc_state *state)
>> >> +{
>> >> +    return container_of(state, struct vs_crtc_state, base);
>> >> +}
>> >> +#endif /* __VS_CRTC_H__ */
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
>> >> new file mode 100644
>> >> index 000000000..b5ab92d98
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc.c
>> >> @@ -0,0 +1,1002 @@
>> >> +// SPDX-License-Identifier: GPL-2.0
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#include <linux/component.h>
>> >> +#include <linux/clk.h>
>> >> +#include <linux/of.h>
>> >> +#include <linux/of_device.h>
>> >> +#include <linux/pm_runtime.h>
>> >> +#include <linux/reset.h>
>> >> +
>> >> +#include <drm/drm_atomic.h>
>> >> +#include <drm/drm_atomic_helper.h>
>> >> +#include <drm/drm_blend.h>
>> >> +#include <drm/drm_framebuffer.h>
>> >> +#include <drm/drm_vblank.h>
>> >> +#include <drm/vs_drm.h>
>> >> +
>> >> +#include "vs_dc_hw.h"
>> >> +#include "vs_dc.h"
>> >> +#include "vs_drv.h"
>> >> +
>> >> +static const char * const vout_clocks[] = {
>> >> +    "noc_bus",
>> >> +    "channel0",
>> >> +    "channel1",
>> >> +    "dc_core",
>> >> +    "axi_core",
>> >> +    "ahb",
>> >> +    "hdmi_tx",
>> >> +    "dc_parent",
>> >> +
>> >> +};
>> >> +
>> >> +static const char * const vout_resets[] = {
>> >> +    "axi",
>> >> +    "ahb",
>> >> +    "core",
>> >> +};
>> >> +
>> >> +static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb)
>> >> +{
>> >> +    u8 f = FORMAT_A8R8G8B8;
>> >> +
>> >> +    switch (format) {
>> >> +    case DRM_FORMAT_XRGB4444:
>> >> +    case DRM_FORMAT_RGBX4444:
>> >> +    case DRM_FORMAT_XBGR4444:
>> >> +    case DRM_FORMAT_BGRX4444:
>> >> +            f = FORMAT_X4R4G4B4;
>> >> +            break;
>> >> +    case DRM_FORMAT_ARGB4444:
>> >> +    case DRM_FORMAT_RGBA4444:
>> >> +    case DRM_FORMAT_ABGR4444:
>> >> +    case DRM_FORMAT_BGRA4444:
>> >> +            f = FORMAT_A4R4G4B4;
>> >> +            break;
>> >> +    case DRM_FORMAT_XRGB1555:
>> >> +    case DRM_FORMAT_RGBX5551:
>> >> +    case DRM_FORMAT_XBGR1555:
>> >> +    case DRM_FORMAT_BGRX5551:
>> >> +            f = FORMAT_X1R5G5B5;
>> >> +            break;
>> >> +    case DRM_FORMAT_ARGB1555:
>> >> +    case DRM_FORMAT_RGBA5551:
>> >> +    case DRM_FORMAT_ABGR1555:
>> >> +    case DRM_FORMAT_BGRA5551:
>> >> +            f = FORMAT_A1R5G5B5;
>> >> +            break;
>> >> +    case DRM_FORMAT_RGB565:
>> >> +    case DRM_FORMAT_BGR565:
>> >> +            f = FORMAT_R5G6B5;
>> >> +            break;
>> >> +    case DRM_FORMAT_XRGB8888:
>> >> +    case DRM_FORMAT_RGBX8888:
>> >> +    case DRM_FORMAT_XBGR8888:
>> >> +    case DRM_FORMAT_BGRX8888:
>> >> +            f = FORMAT_X8R8G8B8;
>> >> +            break;
>> >> +    case DRM_FORMAT_ARGB8888:
>> >> +    case DRM_FORMAT_RGBA8888:
>> >> +    case DRM_FORMAT_ABGR8888:
>> >> +    case DRM_FORMAT_BGRA8888:
>> >> +            f = FORMAT_A8R8G8B8;
>> >> +            break;
>> >> +    case DRM_FORMAT_YUYV:
>> >> +    case DRM_FORMAT_YVYU:
>> >> +            f = FORMAT_YUY2;
>> >> +            break;
>> >> +    case DRM_FORMAT_UYVY:
>> >> +    case DRM_FORMAT_VYUY:
>> >> +            f = FORMAT_UYVY;
>> >> +            break;
>> >> +    case DRM_FORMAT_YUV420:
>> >> +    case DRM_FORMAT_YVU420:
>> >> +            f = FORMAT_YV12;
>> >> +            break;
>> >> +    case DRM_FORMAT_NV21:
>> >> +            f = FORMAT_NV12;
>> >> +            break;
>> >> +    case DRM_FORMAT_NV16:
>> >> +    case DRM_FORMAT_NV61:
>> >> +            f = FORMAT_NV16;
>> >> +            break;
>> >> +    case DRM_FORMAT_P010:
>> >> +            f = FORMAT_P010;
>> >> +            break;
>> >> +    case DRM_FORMAT_ARGB2101010:
>> >> +    case DRM_FORMAT_RGBA1010102:
>> >> +    case DRM_FORMAT_ABGR2101010:
>> >> +    case DRM_FORMAT_BGRA1010102:
>> >> +            f = FORMAT_A2R10G10B10;
>> >> +            break;
>> >> +    case DRM_FORMAT_NV12:
>> >> +            f = FORMAT_NV12;
>> >> +            break;
>> >> +    case DRM_FORMAT_YUV444:
>> >> +            f = FORMAT_YUV444;
>> >> +            break;
>> >> +    default:
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    fb->format = f;
>> >> +}
>> >> +
>> >> +static inline void update_swizzle(u32 format, struct dc_hw_fb *fb)
>> >> +{
>> >> +    fb->swizzle = SWIZZLE_ARGB;
>> >> +    fb->uv_swizzle = 0;
>> >> +
>> >> +    switch (format) {
>> >> +    case DRM_FORMAT_RGBX4444:
>> >> +    case DRM_FORMAT_RGBA4444:
>> >> +    case DRM_FORMAT_RGBX5551:
>> >> +    case DRM_FORMAT_RGBA5551:
>> >> +    case DRM_FORMAT_RGBX8888:
>> >> +    case DRM_FORMAT_RGBA8888:
>> >> +    case DRM_FORMAT_RGBA1010102:
>> >> +            fb->swizzle = SWIZZLE_RGBA;
>> >> +            break;
>> >> +    case DRM_FORMAT_XBGR4444:
>> >> +    case DRM_FORMAT_ABGR4444:
>> >> +    case DRM_FORMAT_XBGR1555:
>> >> +    case DRM_FORMAT_ABGR1555:
>> >> +    case DRM_FORMAT_BGR565:
>> >> +    case DRM_FORMAT_XBGR8888:
>> >> +    case DRM_FORMAT_ABGR8888:
>> >> +    case DRM_FORMAT_ABGR2101010:
>> >> +            fb->swizzle = SWIZZLE_ABGR;
>> >> +            break;
>> >> +    case DRM_FORMAT_BGRX4444:
>> >> +    case DRM_FORMAT_BGRA4444:
>> >> +    case DRM_FORMAT_BGRX5551:
>> >> +    case DRM_FORMAT_BGRA5551:
>> >> +    case DRM_FORMAT_BGRX8888:
>> >> +    case DRM_FORMAT_BGRA8888:
>> >> +    case DRM_FORMAT_BGRA1010102:
>> >> +            fb->swizzle = SWIZZLE_BGRA;
>> >> +            break;
>> >> +    case DRM_FORMAT_YVYU:
>> >> +    case DRM_FORMAT_VYUY:
>> >> +    case DRM_FORMAT_NV21:
>> >> +    case DRM_FORMAT_NV61:
>> >> +            fb->uv_swizzle = 1;
>> >> +            break;
>> >> +    default:
>> >> +            break;
>> >> +    }
>> >> +}
>> >> +
>> >> +static inline void update_watermark(struct drm_property_blob *watermark,
>> >> +                                struct dc_hw_fb *fb)
>> >> +{
>> >> +    struct drm_vs_watermark *data;
>> >> +
>> >> +    fb->water_mark = 0;
>> >> +
>> >> +    if (watermark) {
>> >> +            data = watermark->data;
>> >> +            fb->water_mark = data->watermark & 0xFFFFF;
>> >> +    }
>> >> +}
>> >> +
>> >> +static inline u8 to_vs_rotation(unsigned int rotation)
>> >> +{
>> >> +    u8 rot;
>> >> +
>> >> +    switch (rotation & DRM_MODE_REFLECT_MASK) {
>> >> +    case DRM_MODE_REFLECT_X:
>> >> +            rot = FLIP_X;
>> >> +            return rot;
>> >> +    case DRM_MODE_REFLECT_Y:
>> >> +            rot = FLIP_Y;
>> >> +            return rot;
>> >> +    case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
>> >> +            rot = FLIP_XY;
>> >> +            return rot;
>> >> +    default:
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    switch (rotation & DRM_MODE_ROTATE_MASK) {
>> >> +    case DRM_MODE_ROTATE_0:
>> >> +            rot = ROT_0;
>> >> +            break;
>> >> +    case DRM_MODE_ROTATE_90:
>> >> +            rot = ROT_90;
>> >> +            break;
>> >> +    case DRM_MODE_ROTATE_180:
>> >> +            rot = ROT_180;
>> >> +            break;
>> >> +    case DRM_MODE_ROTATE_270:
>> >> +            rot = ROT_270;
>> >> +            break;
>> >> +    default:
>> >> +            rot = ROT_0;
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    return rot;
>> >> +}
>> >> +
>> >> +static inline u8 to_vs_yuv_color_space(u32 color_space)
>> >> +{
>> >> +    u8 cs;
>> >> +
>> >> +    switch (color_space) {
>> >> +    case DRM_COLOR_YCBCR_BT601:
>> >> +            cs = COLOR_SPACE_601;
>> >> +            break;
>> >> +    case DRM_COLOR_YCBCR_BT709:
>> >> +            cs = COLOR_SPACE_709;
>> >> +            break;
>> >> +    case DRM_COLOR_YCBCR_BT2020:
>> >> +            cs = COLOR_SPACE_2020;
>> >> +            break;
>> >> +    default:
>> >> +            cs = COLOR_SPACE_601;
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    return cs;
>> >> +}
>> >> +
>> >> +static inline u8 to_vs_tile_mode(u64 modifier)
>> >> +{
>> >> +    if (modifier == DRM_FORMAT_MOD_VIVANTE_TILED)
>> >> +            return DC_TILE_MODE4X4;
>> >> +
>> >> +    return (u8)(modifier & DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK);
>> >> +}
>> >> +
>> >> +static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc)
>> >> +{
>> >> +    u8 panel_num = dc->hw.info->panel_num;
>> >> +    u32 index = drm_crtc_index(crtc);
>> >> +    int i;
>> >> +
>> >> +    for (i = 0; i < panel_num; i++) {
>> >> +            if (index == dc->crtc[i]->base.index)
>> >> +                    return i;
>> >> +    }
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
>> >> +                                      unsigned int alignment)
>> >> +{
>> >> +    struct vs_drm_device *priv = to_vs_drm_private(drm_dev);
>> >> +
>> >> +    if (alignment > priv->pitch_alignment)
>> >> +            priv->pitch_alignment = alignment;
>> >> +}
>> >> +
>> >> +static int plda_clk_rst_init(struct device *dev)
>> >> +{
>> >> +    int ret = 0;
>> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
>> >> +
>> >> +    ret = clk_bulk_prepare_enable(dc->nclks, dc->clk_vout);
>> >> +    if (ret) {
>> >> +            dev_err(dev, "failed to enable clocks\n");
>> >> +            return ret;
>> >> +    }
>> >> +
>> >> +    ret = reset_control_bulk_deassert(dc->nrsts, dc->rst_vout);
>> >> +    return ret;
>> >> +}
>> >> +
>> >> +static void plda_clk_rst_deinit(struct device *dev)
>> >> +{
>> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
>> >> +
>> >> +    reset_control_bulk_assert(dc->nrsts, dc->rst_vout);
>> >> +    clk_bulk_disable_unprepare(dc->nclks, dc->clk_vout);
>> >> +}
>> >> +
>> >> +static void dc_deinit(struct device *dev)
>> >> +{
>> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
>> >> +
>> >> +    dc_hw_enable_interrupt(&dc->hw, 0);
>> >> +    dc_hw_deinit(&dc->hw);
>> >> +    plda_clk_rst_deinit(dev);
>> >> +}
>> >> +
>> >> +static int dc_init(struct device *dev)
>> >> +{
>> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
>> >> +    int ret;
>> >> +
>> >> +    dc->first_frame = true;
>> >> +
>> >> +    ret = plda_clk_rst_init(dev);
>> >> +    if (ret < 0) {
>> >> +            dev_err(dev, "failed to init dc clk reset: %d\n", ret);
>> >> +            return ret;
>> >> +    }
>> >> +
>> >> +    ret = dc_hw_init(&dc->hw);
>> >> +    if (ret) {
>> >> +            dev_err(dev, "failed to init DC HW\n");
>> >> +            return ret;
>> >> +    }
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
>> >> +{
>> >> +    struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
>> >> +    struct drm_display_mode *mode = &crtc->state->adjusted_mode;
>> >> +    struct dc_hw_display display;
>> >> +
>> >> +    display.bus_format = crtc_state->output_fmt;
>> >> +    display.h_active = mode->hdisplay;
>> >> +    display.h_total = mode->htotal;
>> >> +    display.h_sync_start = mode->hsync_start;
>> >> +    display.h_sync_end = mode->hsync_end;
>> >> +    if (mode->flags & DRM_MODE_FLAG_PHSYNC)
>> >> +            display.h_sync_polarity = true;
>> >> +    else
>> >> +            display.h_sync_polarity = false;
>> >> +
>> >> +    display.v_active = mode->vdisplay;
>> >> +    display.v_total = mode->vtotal;
>> >> +    display.v_sync_start = mode->vsync_start;
>> >> +    display.v_sync_end = mode->vsync_end;
>> >> +
>> >> +    if (mode->flags & DRM_MODE_FLAG_PVSYNC)
>> >> +            display.v_sync_polarity = true;
>> >> +    else
>> >> +            display.v_sync_polarity = false;
>> >> +
>> >> +    display.id = to_vs_display_id(dc, crtc);
>> >> +
>> >> +    display.enable = true;
>> >> +
>> >> +    if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) {
>> >> +            dc_hw_set_out(&dc->hw, OUT_DPI, display.id);
>> >> +            clk_set_rate(dc->clk_vout[CLK_VOUT_SOC_PIX].clk, mode->clock * 1000);
>> >> +            clk_set_parent(dc->clk_vout[CLK_VOUT_PIX1].clk,
>> >> +                           dc->clk_vout[CLK_VOUT_SOC_PIX].clk);
>> >> +    } else {
>> >> +            dc_hw_set_out(&dc->hw, OUT_DP, display.id);
>> >> +            clk_set_parent(dc->clk_vout[CLK_VOUT_PIX0].clk,
>> >> +                           dc->clk_vout[CLK_VOUT_HDMI_PIX].clk);
>> >> +    }
>> >> +
>> >> +    dc_hw_setup_display(&dc->hw, &display);
>> >> +}
>> >> +
>> >> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc)
>> >> +{
>> >> +    struct dc_hw_display display;
>> >> +
>> >> +    display.id = to_vs_display_id(dc, crtc);
>> >> +    display.enable = false;
>> >> +
>> >> +    dc_hw_setup_display(&dc->hw, &display);
>> >> +}
>> >> +
>> >> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
>> >> +                 struct drm_color_lut *lut, unsigned int size)
>> >> +{
>> >> +    u16 i, r, g, b;
>> >> +    u8 bits, id;
>> >> +
>> >> +    if (size != dc->hw.info->gamma_size) {
>> >> +            drm_err(crtc->dev, "gamma size does not match!\n");
>> >> +            return;
>> >> +    }
>> >> +
>> >> +    id = to_vs_display_id(dc, crtc);
>> >> +
>> >> +    bits = dc->hw.info->gamma_bits;
>> >> +    for (i = 0; i < size; i++) {
>> >> +            r = drm_color_lut_extract(lut[i].red, bits);
>> >> +            g = drm_color_lut_extract(lut[i].green, bits);
>> >> +            b = drm_color_lut_extract(lut[i].blue, bits);
>> >> +            dc_hw_update_gamma(&dc->hw, id, i, r, g, b);
>> >> +    }
>> >> +}
>> >> +
>> >> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
>> >> +                    bool enable)
>> >> +{
>> >> +    u8 id;
>> >> +
>> >> +    id = to_vs_display_id(dc, crtc);
>> >> +    dc_hw_enable_gamma(&dc->hw, id, enable);
>> >> +}
>> >> +
>> >> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable)
>> >> +{
>> >> +    dc_hw_enable_interrupt(&dc->hw, enable);
>> >> +}
>> >> +
>> >> +static u32 calc_factor(u32 src, u32 dest)
>> >> +{
>> >> +    u32 factor = 1 << 16;
>> >> +
>> >> +    if (src > 1 && dest > 1)
>> >> +            factor = ((src - 1) << 16) / (dest - 1);
>> >> +
>> >> +    return factor;
>> >> +}
>> >> +
>> >> +static void update_scale(struct drm_plane_state *state, struct dc_hw_roi *roi,
>> >> +                     struct dc_hw_scale *scale)
>> >> +{
>> >> +    int dst_w = drm_rect_width(&state->dst);
>> >> +    int dst_h = drm_rect_height(&state->dst);
>> >> +    int src_w, src_h, temp;
>> >> +
>> >> +    scale->enable = false;
>> >> +
>> >> +    if (roi->enable) {
>> >> +            src_w = roi->width;
>> >> +            src_h = roi->height;
>> >> +    } else {
>> >> +            src_w = drm_rect_width(&state->src) >> 16;
>> >> +            src_h = drm_rect_height(&state->src) >> 16;
>> >> +    }
>> >> +
>> >> +    if (drm_rotation_90_or_270(state->rotation)) {
>> >> +            temp = src_w;
>> >> +            src_w = src_h;
>> >> +            src_h = temp;
>> >> +    }
>> >> +
>> >> +    if (src_w != dst_w) {
>> >> +            scale->scale_factor_x = calc_factor(src_w, dst_w);
>> >> +            scale->enable = true;
>> >> +    } else {
>> >> +            scale->scale_factor_x = 1 << 16;
>> >> +    }
>> >> +    if (src_h != dst_h) {
>> >> +            scale->scale_factor_y = calc_factor(src_h, dst_h);
>> >> +            scale->enable = true;
>> >> +    } else {
>> >> +            scale->scale_factor_y = 1 << 16;
>> >> +    }
>> >> +}
>> >> +
>> >> +static void update_fb(struct vs_plane *plane, u8 display_id,
>> >> +                  struct dc_hw_fb *fb, struct drm_plane_state *state)
>> >> +{
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> >> +    struct drm_framebuffer *drm_fb = state->fb;
>> >> +    struct drm_rect *src = &state->src;
>> >> +
>> >> +    fb->display_id = display_id;
>> >> +    fb->y_address = plane_state->dma_addr[0];
>> >> +    fb->y_stride = drm_fb->pitches[0];
>> >> +    if (drm_fb->format->format == DRM_FORMAT_YVU420) {
>> >> +            fb->u_address = plane_state->dma_addr[2];
>> >> +            fb->v_address = plane_state->dma_addr[1];
>> >> +            fb->u_stride = drm_fb->pitches[2];
>> >> +            fb->v_stride = drm_fb->pitches[1];
>> >> +    } else {
>> >> +            fb->u_address = plane_state->dma_addr[1];
>> >> +            fb->v_address = plane_state->dma_addr[2];
>> >> +            fb->u_stride = drm_fb->pitches[1];
>> >> +            fb->v_stride = drm_fb->pitches[2];
>> >> +    }
>> >> +    fb->width = drm_rect_width(src) >> 16;
>> >> +    fb->height = drm_rect_height(src) >> 16;
>> >> +    fb->tile_mode = to_vs_tile_mode(drm_fb->modifier);
>> >> +    fb->rotation = to_vs_rotation(state->rotation);
>> >> +    fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding);
>> >> +    fb->zpos = state->zpos;
>> >> +    fb->enable = state->visible;
>> >> +    update_format(drm_fb->format->format, drm_fb->modifier, fb);
>> >> +    update_swizzle(drm_fb->format->format, fb);
>> >> +    update_watermark(plane_state->watermark, fb);
>> >> +    plane_state->status.tile_mode = fb->tile_mode;
>> >> +}
>> >> +
>> >> +static void update_degamma(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                       struct vs_plane_state *plane_state)
>> >> +{
>> >> +    dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
>> >> +    plane_state->degamma_changed = false;
>> >> +}
>> >> +
>> >> +static void update_roi(struct vs_dc *dc, u8 id,
>> >> +                   struct vs_plane_state *plane_state,
>> >> +                   struct dc_hw_roi *roi,
>> >> +                   struct drm_plane_state *state)
>> >> +{
>> >> +    struct drm_vs_roi *data;
>> >> +    struct drm_rect *src = &state->src;
>> >> +    u16 src_w = drm_rect_width(src) >> 16;
>> >> +    u16 src_h = drm_rect_height(src) >> 16;
>> >> +
>> >> +    if (plane_state->roi) {
>> >> +            data = plane_state->roi->data;
>> >> +
>> >> +            if (data->enable) {
>> >> +                    roi->x = data->roi_x;
>> >> +                    roi->y = data->roi_y;
>> >> +                    roi->width = (data->roi_x + data->roi_w > src_w) ?
>> >> +                                             (src_w - data->roi_x) : data->roi_w;
>> >> +                    roi->height = (data->roi_y + data->roi_h > src_h) ?
>> >> +                                              (src_h - data->roi_y) : data->roi_h;
>> >> +                    roi->enable = true;
>> >> +            } else {
>> >> +                    roi->enable = false;
>> >> +            }
>> >> +
>> >> +            dc_hw_update_roi(&dc->hw, id, roi);
>> >> +    } else {
>> >> +            roi->enable = false;
>> >> +    }
>> >> +}
>> >> +
>> >> +static void update_color_mgmt(struct vs_dc *dc, u8 id,
>> >> +                          struct dc_hw_fb *fb,
>> >> +                          struct vs_plane_state *plane_state)
>> >> +{
>> >> +    struct drm_vs_color_mgmt *data;
>> >> +    struct dc_hw_colorkey colorkey;
>> >> +
>> >> +    if (plane_state->color_mgmt) {
>> >> +            data = plane_state->color_mgmt->data;
>> >> +
>> >> +            fb->clear_enable = data->clear_enable;
>> >> +            fb->clear_value = data->clear_value;
>> >> +
>> >> +            if (data->colorkey > data->colorkey_high)
>> >> +                    data->colorkey = data->colorkey_high;
>> >> +
>> >> +            colorkey.colorkey = data->colorkey;
>> >> +            colorkey.colorkey_high = data->colorkey_high;
>> >> +            colorkey.transparency = (data->transparency) ?
>> >> +                            DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE;
>> >> +            dc_hw_update_colorkey(&dc->hw, id, &colorkey);
>> >> +    }
>> >> +}
>> >> +
>> >> +static void update_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                     struct drm_plane *drm_plane,
>> >> +                     struct drm_atomic_state *drm_state)
>> >> +{
>> >> +    struct dc_hw_fb fb = {0};
>> >> +    struct dc_hw_scale scale;
>> >> +    struct dc_hw_position pos;
>> >> +    struct dc_hw_blend blend;
>> >> +    struct dc_hw_roi roi;
>> >> +    struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
>> >> +                                                                       drm_plane);
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> >> +    struct drm_rect *dest = &state->dst;
>> >> +    bool dec_enable = false;
>> >> +    u8 display_id = 0;
>> >> +
>> >> +    display_id = to_vs_display_id(dc, state->crtc);
>> >> +    update_fb(plane, display_id, &fb, state);
>> >> +    fb.dec_enable = dec_enable;
>> >> +
>> >> +    update_roi(dc, plane->id, plane_state, &roi, state);
>> >> +
>> >> +    update_scale(state, &roi, &scale);
>> >> +
>> >> +    if (plane_state->degamma_changed)
>> >> +            update_degamma(dc, plane, plane_state);
>> >> +
>> >> +    pos.start_x = dest->x1;
>> >> +    pos.start_y = dest->y1;
>> >> +    pos.end_x = dest->x2;
>> >> +    pos.end_y = dest->y2;
>> >> +
>> >> +    blend.alpha = (u8)(state->alpha >> 8);
>> >> +    blend.blend_mode = (u8)(state->pixel_blend_mode);
>> >> +
>> >> +    update_color_mgmt(dc, plane->id, &fb, plane_state);
>> >> +
>> >> +    dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
>> >> +}
>> >> +
>> >> +static void update_qos(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                   struct drm_plane *drm_plane,
>> >> +                   struct drm_atomic_state *drm_state)
>> >> +{
>> >> +    struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
>> >> +                                                                       drm_plane);
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> >> +    struct drm_vs_watermark *data;
>> >> +    struct dc_hw_qos qos;
>> >> +
>> >> +    if (plane_state->watermark) {
>> >> +            data = plane_state->watermark->data;
>> >> +
>> >> +            if (data->qos_high) {
>> >> +                    if (data->qos_low > data->qos_high)
>> >> +                            data->qos_low = data->qos_high;
>> >> +
>> >> +                    qos.low_value = data->qos_low & 0x0F;
>> >> +                    qos.high_value = data->qos_high & 0x0F;
>> >> +                    dc_hw_update_qos(&dc->hw, &qos);
>> >> +            }
>> >> +    }
>> >> +}
>> >> +
>> >> +static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor)
>> >> +{
>> >> +    u8 size_type;
>> >> +
>> >> +    switch (state->crtc_w) {
>> >> +    case 32:
>> >> +            size_type = CURSOR_SIZE_32X32;
>> >> +            break;
>> >> +    case 64:
>> >> +            size_type = CURSOR_SIZE_64X64;
>> >> +            break;
>> >> +    default:
>> >> +            size_type = CURSOR_SIZE_32X32;
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    cursor->size = size_type;
>> >> +}
>> >> +
>> >> +static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                            struct drm_plane *drm_plane,
>> >> +                            struct drm_atomic_state *drm_state)
>> >> +{
>> >> +    struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
>> >> +                                                                   drm_plane);
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> >> +    struct drm_framebuffer *drm_fb = state->fb;
>> >> +    struct dc_hw_cursor cursor;
>> >> +
>> >> +    cursor.address = plane_state->dma_addr[0];
>> >> +    cursor.x = state->crtc_x;
>> >> +    cursor.y = state->crtc_y;
>> >> +    cursor.hot_x = drm_fb->hot_x;
>> >> +    cursor.hot_y = drm_fb->hot_y;
>> >> +    cursor.display_id = to_vs_display_id(dc, state->crtc);
>> >> +    update_cursor_size(state, &cursor);
>> >> +    cursor.enable = true;
>> >> +
>> >> +    dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
>> >> +}
>> >> +
>> >> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                    struct drm_plane *drm_plane,
>> >> +                    struct drm_atomic_state *drm_state)
>> >> +{
>> >> +    update_plane(dc, plane, drm_plane, drm_state);
>> >> +    update_qos(dc, plane, drm_plane, drm_state);
>> >> +}
>> >> +
>> >> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                           struct drm_plane *drm_plane,
>> >> +                           struct drm_atomic_state *drm_state)
>> >> +{
>> >> +    update_cursor_plane(dc, plane, drm_plane, drm_state);
>> >> +}
>> >> +
>> >> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                     struct drm_plane_state *old_state)
>> >> +{
>> >> +    struct dc_hw_fb fb = {0};
>> >> +
>> >> +    fb.enable = false;
>> >> +    dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL, NULL, NULL);
>> >> +}
>> >> +
>> >> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                            struct drm_plane_state *old_state)
>> >> +{
>> >> +    struct dc_hw_cursor cursor = {0};
>> >> +
>> >> +    cursor.enable = false;
>> >> +    cursor.display_id = to_vs_display_id(dc, old_state->crtc);
>> >> +    dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
>> >> +}
>> >> +
>> >> +static bool vs_dc_mod_supported(const struct vs_plane_info *plane_info,
>> >> +                            u64 modifier)
>> >> +{
>> >> +    const u64 *mods;
>> >> +
>> >> +    if (!plane_info->modifiers)
>> >> +            return false;
>> >> +
>> >> +    for (mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID; mods++) {
>> >> +            if (*mods == modifier)
>> >> +                    return true;
>> >> +    }
>> >> +
>> >> +    return false;
>> >> +}
>> >> +
>> >> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
>> >> +                  struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> >> +                                                                             plane);
>> >> +
>> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
>> >> +    const struct vs_plane_info *plane_info;
>> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
>> >> +    struct drm_crtc_state *crtc_state;
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +
>> >> +    plane_info = &dc->hw.info->planes[vs_plane->id];
>> >> +
>> >> +    if (fb->width < plane_info->min_width ||
>> >> +        fb->width > plane_info->max_width ||
>> >> +        fb->height < plane_info->min_height ||
>> >> +        fb->height > plane_info->max_height)
>> >> +            drm_err_once(plane->dev, "buffer size may not support on plane%d.\n",
>> >> +                         vs_plane->id);
>> >> +
>> >> +    if (!vs_dc_mod_supported(plane_info, fb->modifier)) {
>> >> +            drm_err(plane->dev, "unsupported modifier on plane%d.\n", vs_plane->id);
>> >> +            return -EINVAL;
>> >> +    }
>> >> +
>> >> +    crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
>> >> +    return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
>> >> +                                              plane_info->min_scale,
>> >> +                                              plane_info->max_scale,
>> >> +                                              true, true);
>> >> +}
>> >> +
>> >> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
>> >> +                         struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> >> +                                                                       plane);
>> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
>> >> +    const struct vs_plane_info *plane_info;
>> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
>> >> +    struct drm_crtc_state *crtc_state;
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +
>> >> +    plane_info = &dc->hw.info->planes[vs_plane->id];
>> >> +
>> >> +    if (fb->width < plane_info->min_width ||
>> >> +        fb->width > plane_info->max_width ||
>> >> +        fb->height < plane_info->min_height ||
>> >> +        fb->height > plane_info->max_height)
>> >> +            drm_err_once(plane->dev, "buffer size may not support on plane%d.\n", vs_plane->id);
>> >> +
>> >> +    crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
>> >> +    if (IS_ERR(crtc_state))
>> >> +            return -EINVAL;
>> >> +
>> >> +    return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
>> >> +                                            plane_info->min_scale,
>> >> +                                            plane_info->max_scale,
>> >> +                                            true, true);
>> >> +}
>> >> +
>> >> +static void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
>> >> +{
>> >> +    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
>> >> +
>> >> +    drm_crtc_handle_vblank(crtc);
>> >> +
>> >> +    vs_crtc_state->underflow = underflow;
>> >> +}
>> >> +
>> >> +static irqreturn_t dc_isr(int irq, void *data)
>> >> +{
>> >> +    struct vs_dc *dc = data;
>> >> +    struct vs_dc_info *dc_info = dc->hw.info;
>> >> +    u32 i, ret;
>> >> +
>> >> +    ret = dc_hw_get_interrupt(&dc->hw);
>> >> +
>> >> +    for (i = 0; i < dc_info->panel_num; i++)
>> >> +            vs_crtc_handle_vblank(&dc->crtc[i]->base, dc_hw_check_underflow(&dc->hw));
>> >> +
>> >> +    return IRQ_HANDLED;
>> >> +}
>> >> +
>> >> +void vs_dc_commit(struct vs_dc *dc)
>> >> +{
>> >> +    dc_hw_enable_shadow_register(&dc->hw, false);
>> >> +
>> >> +    dc_hw_commit(&dc->hw);
>> >> +
>> >> +    if (dc->first_frame)
>> >> +            dc->first_frame = false;
>> >> +
>> >> +    dc_hw_enable_shadow_register(&dc->hw, true);
>> >> +}
>> >> +
>> >> +static int dc_bind(struct device *dev, struct device *master, void *data)
>> >> +{
>> >> +    struct drm_device *drm_dev = data;
>> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
>> >> +    struct device_node *port;
>> >> +    struct vs_crtc *crtc;
>> >> +    struct vs_dc_info *dc_info;
>> >> +    struct vs_plane *plane;
>> >> +    struct vs_plane_info *plane_info;
>> >> +    int i, ret;
>> >> +    u32 ctrc_mask = 0;
>> >> +
>> >> +    if (!drm_dev || !dc) {
>> >> +            dev_err(dev, "devices are not created.\n");
>> >> +            return -ENODEV;
>> >> +    }
>> >> +
>> >> +    ret = dc_init(dev);
>> >> +    if (ret < 0) {
>> >> +            drm_err(drm_dev, "Failed to initialize DC hardware.\n");
>> >> +            return ret;
>> >> +    }
>> >> +
>> >> +    port = of_get_child_by_name(dev->of_node, "port");
>> >> +    if (!port) {
>> >> +            drm_err(drm_dev, "no port node found\n");
>> >> +            return -ENODEV;
>> >> +    }
>> >> +    of_node_put(port);
>> >> +
>> >> +    dc_info = dc->hw.info;
>> >> +
>> >> +    for (i = 0; i < dc_info->panel_num; i++) {
>> >> +            crtc = vs_crtc_create(drm_dev, dc_info);
>> >> +            if (!crtc) {
>> >> +                    drm_err(drm_dev, "Failed to create CRTC.\n");
>> >> +                    ret = -ENOMEM;
>> >> +                    return ret;
>> >> +            }
>> >> +
>> >> +            crtc->base.port = port;
>> >> +            crtc->dev = dev;
>> >> +            dc->crtc[i] = crtc;
>> >> +            ctrc_mask |= drm_crtc_mask(&crtc->base);
>> >> +    }
>> >> +
>> >> +    for (i = 0; i < dc_info->plane_num; i++) {
>> >> +            plane_info = (struct vs_plane_info *)&dc_info->planes[i];
>> >> +
>> >> +            if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
>> >> +                    plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
>> >> +                                            drm_crtc_mask(&dc->crtc[0]->base));
>> >> +            } else if (!strcmp(plane_info->name, "Primary_1") ||
>> >> +                               !strcmp(plane_info->name, "Cursor_1")) {
>> >> +                    plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
>> >> +                                            drm_crtc_mask(&dc->crtc[1]->base));
>> >
>> > I'd say, it's not typical to use name to distinguish objects. Maybe you
>> > can add indices to the info structure and drop the names instead?
>> >
>> >
>> >> +            } else {
>> >> +                    plane = vs_plane_create(drm_dev, plane_info,
>> >> +                                            dc_info->layer_num, ctrc_mask);
>> >> +            }
>> >> +
>> >> +            if (IS_ERR(plane)) {
>> >> +                    dev_err(dev, "failed to construct plane\n");
>> >> +                    return PTR_ERR(plane);
>> >> +            }
>> >> +
>> >> +            plane->id = i;
>> >> +            dc->planes[i].id = plane_info->id;
>> >> +
>> >> +            if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
>> >> +                    if (!strcmp(plane_info->name, "Primary"))
>> >> +                            dc->crtc[0]->base.primary = &plane->base;
>> >> +                    else
>> >> +                            dc->crtc[1]->base.primary = &plane->base;
>> >
>> > This definitely urges for an index in the plane info / plane_state.
>> >
>> >> +                    drm_dev->mode_config.min_width = plane_info->min_width;
>> >> +                    drm_dev->mode_config.min_height =
>> >> +                                                    plane_info->min_height;
>> >> +                    drm_dev->mode_config.max_width = plane_info->max_width;
>> >> +                    drm_dev->mode_config.max_height =
>> >> +                                                    plane_info->max_height;
>> >> +            }
>> >> +
>> >> +            if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
>> >> +                    if (!strcmp(plane_info->name, "Cursor"))
>> >> +                            dc->crtc[0]->base.cursor = &plane->base;
>> >> +                    else
>> >> +                            dc->crtc[1]->base.cursor = &plane->base;
>> >> +                    drm_dev->mode_config.cursor_width =
>> >> +                                                    plane_info->max_width;
>> >> +                    drm_dev->mode_config.cursor_height =
>> >> +                                                    plane_info->max_height;
>> >> +            }
>> >> +    }
>> >> +
>> >> +    vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static void dc_unbind(struct device *dev, struct device *master, void *data)
>> >> +{
>> >> +    dc_deinit(dev);
>> >> +}
>> >> +
>> >> +const struct component_ops dc_component_ops = {
>> >> +    .bind = dc_bind,
>> >> +    .unbind = dc_unbind,
>> >> +};
>> >> +
>> >> +static const struct of_device_id dc_driver_dt_match[] = {
>> >> +    { .compatible = "starfive,jh7110-dc8200", },
>> >> +    {},
>> >> +};
>> >> +MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
>> >> +
>> >> +static int dc_probe(struct platform_device *pdev)
>> >> +{
>> >> +    struct device *dev = &pdev->dev;
>> >> +    struct vs_dc *dc;
>> >> +    int irq, ret, i;
>> >> +
>> >> +    dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
>> >> +    if (!dc)
>> >> +            return -ENOMEM;
>> >> +
>> >> +    dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
>> >> +    if (IS_ERR(dc->hw.hi_base))
>> >> +            return PTR_ERR(dc->hw.hi_base);
>> >> +
>> >> +    dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
>> >> +    if (IS_ERR(dc->hw.reg_base))
>> >> +            return PTR_ERR(dc->hw.reg_base);
>> >> +
>> >> +    dc->nclks = ARRAY_SIZE(dc->clk_vout);
>> >> +    for (i = 0; i < dc->nclks; ++i)
>> >> +            dc->clk_vout[i].id = vout_clocks[i];
>> >> +    ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
>> >> +    if (ret) {
>> >> +            dev_err(dev, "Failed to get clk controls\n");
>> >> +            return ret;
>> >> +    }
>> >> +
>> >> +    dc->nrsts = ARRAY_SIZE(dc->rst_vout);
>> >> +    for (i = 0; i < dc->nrsts; ++i)
>> >> +            dc->rst_vout[i].id = vout_resets[i];
>> >> +    ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
>> >> +                                             dc->rst_vout);
>> >> +    if (ret) {
>> >> +            dev_err(dev, "Failed to get reset controls\n");
>> >> +            return ret;
>> >> +    }
>> >> +
>> >> +    irq = platform_get_irq(pdev, 0);
>> >> +
>> >> +    ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
>> >
>> > Are you ready to handle the IRQ at this point?
Do you have some good suggestions?
Are there any hidden dangers in doing so?
Hope to get good opinions 

>> >
>> >> +    if (ret < 0) {
>> >> +            dev_err(dev, "Failed to install irq:%u.\n", irq);
>> >> +            return ret;
>> >> +    }
>> >> +
>> >> +    dev_set_drvdata(dev, dc);
>> >> +
>> >> +    return component_add(dev, &dc_component_ops);
>> >> +}
>> >> +
>> >> +static int dc_remove(struct platform_device *pdev)
>> >> +{
>> >> +    struct device *dev = &pdev->dev;
>> >> +
>> >> +    component_del(dev, &dc_component_ops);
>> >> +
>> >> +    dev_set_drvdata(dev, NULL);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +struct platform_driver dc_platform_driver = {
>> >> +    .probe = dc_probe,
>> >> +    .remove = dc_remove,
>> >> +    .driver = {
>> >> +            .name = "vs-dc",
>> >> +            .of_match_table = of_match_ptr(dc_driver_dt_match),
>> >> +    },
>> >> +};
>> >> +
>> >> +MODULE_AUTHOR("StarFive Corporation");
>> >> +MODULE_DESCRIPTION("VeriSilicon DC Driver");
>> >> +MODULE_LICENSE("GPL");
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
>> >> new file mode 100644
>> >> index 000000000..8e96fd32c
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc.h
>> >> @@ -0,0 +1,80 @@
>> >> +/* SPDX-License-Identifier: GPL-2.0 */
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#ifndef __VS_DC_H__
>> >> +#define __VS_DC_H__
>> >> +
>> >> +#include <linux/clk.h>
>> >> +#include <linux/mm_types.h>
>> >> +#include <linux/reset.h>
>> >> +
>> >> +#include <drm/drm_fourcc.h>
>> >> +#include <drm/drm_modes.h>
>> >> +
>> >> +#include "vs_crtc.h"
>> >> +#include "vs_dc_hw.h"
>> >> +#include "vs_plane.h"
>> >> +
>> >> +struct vs_dc_plane {
>> >> +    enum dc_hw_plane_id id;
>> >> +};
>> >> +
>> >> +enum vout_clk {
>> >> +    CLK_VOUT_NOC_DISP = 0,
>> >> +    CLK_VOUT_PIX0,
>> >> +    CLK_VOUT_PIX1,
>> >> +    CLK_VOUT_CORE,
>> >> +    CLK_VOUT_AXI,
>> >> +    CLK_VOUT_AHB,
>> >> +    CLK_VOUT_HDMI_PIX,
>> >> +    CLK_VOUT_SOC_PIX,
>> >> +    CLK_VOUT_NUM
>> >> +};
>> >> +
>> >> +enum rst_vout {
>> >> +    RST_VOUT_AXI = 0,
>> >> +    RST_VOUT_AHB,
>> >> +    RST_VOUT_CORE,
>> >> +    RST_VOUT_NUM
>> >> +};
>> >> +
>> >> +struct vs_dc {
>> >> +    struct vs_crtc          *crtc[DC_DISPLAY_NUM];
>> >> +    struct dc_hw            hw;
>> >> +    bool                    first_frame;
>> >> +
>> >> +    struct vs_dc_plane      planes[PLANE_NUM];
>> >> +    struct clk_bulk_data    clk_vout[CLK_VOUT_NUM];
>> >> +    int                     nclks;
>> >> +    struct reset_control_bulk_data rst_vout[RST_VOUT_NUM];
>> >> +    int                     nrsts;
>> >> +};
>> >> +
>> >> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc);
>> >> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc);
>> >> +
>> >> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
>> >> +                 struct drm_color_lut *lut, unsigned int size);
>> >> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc, bool enable);
>> >> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable);
>> >> +void vs_dc_commit(struct vs_dc *dc);
>> >> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                    struct drm_plane *drm_plane,
>> >> +                    struct drm_atomic_state *drm_state);
>> >> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                     struct drm_plane_state *old_state);
>> >> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
>> >> +                  struct drm_atomic_state *state);
>> >> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                           struct drm_plane *drm_plane,
>> >> +                           struct drm_atomic_state *drm_state);
>> >> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                            struct drm_plane_state *old_state);
>> >> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
>> >> +                         struct drm_atomic_state *state);
>> >> +
>> >> +extern struct platform_driver dc_platform_driver;
>> >> +
>> >> +#endif /* __VS_DC_H__ */
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
>> >> new file mode 100644
>> >> index 000000000..e5acb7eb4
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
>> >> @@ -0,0 +1,1959 @@
>> >> +// SPDX-License-Identifier: GPL-2.0
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#include <linux/bits.h>
>> >> +#include <linux/io.h>
>> >> +#include <linux/media-bus-format.h>
>> >> +#include <drm/drm_atomic_helper.h>
>> >> +#include <drm/drm_blend.h>
>> >> +#include <drm/drm_fourcc.h>
>> >> +#include <drm/vs_drm.h>
>> >> +
>> >> +#include "vs_dc_hw.h"
>> >> +#include "vs_type.h"
>> >> +
>> >> +static const u32 horkernel[] = {
>> >> +    0x00000000, 0x20000000, 0x00002000, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x2b981468,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x10f00000,
>> >> +    0x00002f10, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x3781087f, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x06660000, 0x0000399a, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x01470000,
>> >> +    0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x00004000, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x20002000, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x27e1181f,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x14680000,
>> >> +    0x00002b98, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x2f1010f0, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x0dc70000, 0x00003239, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x087f0000, 0x00003781, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x399a0666, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x3cc4033c,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x021f0000,
>> >> +};
>> >> +
>> >> +#define H_COEF_SIZE (sizeof(horkernel) / sizeof(u32))
>> >> +
>> >> +static const u32 verkernel[] = {
>> >> +    0x00000000, 0x20000000, 0x00002000, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x2b981468,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x10f00000,
>> >> +    0x00002f10, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x3781087f, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x06660000, 0x0000399a, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x01470000,
>> >> +    0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x00004000, 0x00000000,
>> >> +    0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab,
>> >> +    0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
>> >> +    0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +};
>> >> +
>> >> +#define V_COEF_SIZE (sizeof(verkernel) / sizeof(u32))
>> >> +
>> >> +/*
>> >> + * RGB 709->2020 conversion parameters
>> >> + */
>> >> +static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = {
>> >> +    10279,  5395,   709,
>> >> +    1132,   15065,  187,
>> >> +    269,    1442,   14674
>> >> +};
>> >> +
>> >> +/*
>> >> + * YUV601 to RGB conversion parameters
>> >> + * YUV2RGB[0]  - [8] : C0 - C8;
>> >> + * YUV2RGB[9]  - [11]: D0 - D2;
>> >> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
>> >> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
>> >> + */
>> >> +static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
>> >> +    1196,   0,                      1640,   1196,
>> >> +    -404,   -836,           1196,   2076,
>> >> +    0,              -916224,        558336, -1202944,
>> >> +    64,              940,           64,             960
>> >> +};
>> >> +
>> >> +/*
>> >> + * YUV709 to RGB conversion parameters
>> >> + * YUV2RGB[0]  - [8] : C0 - C8;
>> >> + * YUV2RGB[9]  - [11]: D0 - D2;
>> >> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
>> >> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
>> >> + */
>> >> +static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
>> >> +    1196,           0,              1844,   1196,
>> >> +    -220,           -548,   1196,   2172,
>> >> +    0,                      -1020672, 316672,  -1188608,
>> >> +    64,                     940,            64,             960
>> >> +};
>> >> +
>> >> +/*
>> >> + * YUV2020 to RGB conversion parameters
>> >> + * YUV2RGB[0]  - [8] : C0 - C8;
>> >> + * YUV2RGB[9]  - [11]: D0 - D2;
>> >> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
>> >> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
>> >> + */
>> >> +static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
>> >> +    1196, 0, 1724, 1196,
>> >> +    -192, -668, 1196, 2200,
>> >> +    0, -959232, 363776, -1202944,
>> >> +    64, 940, 64, 960
>> >> +};
>> >> +
>> >> +/*
>> >> + * RGB to YUV2020 conversion parameters
>> >> + * RGB2YUV[0] - [8] : C0 - C8;
>> >> + * RGB2YUV[9] - [11]: D0 - D2;
>> >> + */
>> >> +static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = {
>> >> +    230,    594,    52,
>> >> +    -125,  -323,    448,
>> >> +    448,   -412,   -36,
>> >> +    64,             512,    512
>> >> +};
>> >> +
>> >> +/*
>> >> + * Degamma table for 709 color space data.
>> >> + */
>> >> +static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
>> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005,
>> >> +    0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024,
>> >> +    0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064,
>> >> +    0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce,
>> >> +    0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166,
>> >> +    0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230,
>> >> +    0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331,
>> >> +    0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c,
>> >> +    0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
>> >> +    0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d,
>> >> +    0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999,
>> >> +    0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb,
>> >> +    0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65,
>> >> +    0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a,
>> >> +    0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b,
>> >> +    0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc,
>> >> +    0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e,
>> >> +    0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
>> >> +    0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e,
>> >> +    0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf,
>> >> +    0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea,
>> >> +    0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f,
>> >> +    0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930,
>> >> +    0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f,
>> >> +    0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee,
>> >> +    0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde,
>> >> +    0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
>> >> +    0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9,
>> >> +    0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406,
>> >> +    0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a,
>> >> +    0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576,
>> >> +    0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd,
>> >> +    0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
>> >> +};
>> >> +
>> >> +/*
>> >> + * Degamma table for 2020 color space data.
>> >> + */
>> >> +static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
>> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
>> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
>> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
>> >> +    0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
>> >> +    0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
>> >> +    0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006,
>> >> +    0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a,
>> >> +    0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f,
>> >> +    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
>> >> +    0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024,
>> >> +    0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035,
>> >> +    0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d,
>> >> +    0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d,
>> >> +    0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098,
>> >> +    0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3,
>> >> +    0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123,
>> >> +    0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d,
>> >> +    0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
>> >> +    0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6,
>> >> +    0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1,
>> >> +    0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d,
>> >> +    0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6,
>> >> +    0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920,
>> >> +    0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a,
>> >> +    0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033,
>> >> +    0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593,
>> >> +    0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
>> >> +    0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656,
>> >> +    0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332,
>> >> +    0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480,
>> >> +    0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1,
>> >> +    0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99,
>> >> +    0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
>> >> +};
>> >> +
>> >> +/* one is for primary plane and the other is for all overlay planes */
>> >> +static const struct dc_hw_plane_reg dc_plane_reg[] = {
>> >> +    {
>> >> +    .y_address              = DC_FRAMEBUFFER_ADDRESS,
>> >> +    .u_address              = DC_FRAMEBUFFER_U_ADDRESS,
>> >> +    .v_address              = DC_FRAMEBUFFER_V_ADDRESS,
>> >> +    .y_stride               = DC_FRAMEBUFFER_STRIDE,
>> >> +    .u_stride               = DC_FRAMEBUFFER_U_STRIDE,
>> >> +    .v_stride               = DC_FRAMEBUFFER_V_STRIDE,
>> >> +    .size                   = DC_FRAMEBUFFER_SIZE,
>> >> +    .top_left               = DC_FRAMEBUFFER_TOP_LEFT,
>> >> +    .bottom_right   = DC_FRAMEBUFFER_BOTTOM_RIGHT,
>> >> +    .scale_factor_x                 = DC_FRAMEBUFFER_SCALE_FACTOR_X,
>> >> +    .scale_factor_y                 = DC_FRAMEBUFFER_SCALE_FACTOR_Y,
>> >> +    .h_filter_coef_index    = DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
>> >> +    .h_filter_coef_data             = DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
>> >> +    .v_filter_coef_index    = DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
>> >> +    .v_filter_coef_data             = DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
>> >> +    .init_offset                    = DC_FRAMEBUFFER_INIT_OFFSET,
>> >> +    .color_key                              = DC_FRAMEBUFFER_COLOR_KEY,
>> >> +    .color_key_high                 = DC_FRAMEBUFFER_COLOR_KEY_HIGH,
>> >> +    .clear_value                    = DC_FRAMEBUFFER_CLEAR_VALUE,
>> >> +    .color_table_index              = DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
>> >> +    .color_table_data               = DC_FRAMEBUFFER_COLOR_TABLE_DATA,
>> >> +    .scale_config                   = DC_FRAMEBUFFER_SCALE_CONFIG,
>> >> +    .water_mark                             = DC_FRAMEBUFFER_WATER_MARK,
>> >> +    .degamma_index                  = DC_FRAMEBUFFER_DEGAMMA_INDEX,
>> >> +    .degamma_data                   = DC_FRAMEBUFFER_DEGAMMA_DATA,
>> >> +    .degamma_ex_data                = DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
>> >> +    .src_global_color               = DC_FRAMEBUFFER_SRC_GLOBAL_COLOR,
>> >> +    .dst_global_color               = DC_FRAMEBUFFER_DST_GLOBAL_COLOR,
>> >> +    .blend_config                   = DC_FRAMEBUFFER_BLEND_CONFIG,
>> >> +    .roi_origin                             = DC_FRAMEBUFFER_ROI_ORIGIN,
>> >> +    .roi_size                               = DC_FRAMEBUFFER_ROI_SIZE,
>> >> +    .yuv_to_rgb_coef0                       = DC_FRAMEBUFFER_YUVTORGB_COEF0,
>> >> +    .yuv_to_rgb_coef1                       = DC_FRAMEBUFFER_YUVTORGB_COEF1,
>> >> +    .yuv_to_rgb_coef2                       = DC_FRAMEBUFFER_YUVTORGB_COEF2,
>> >> +    .yuv_to_rgb_coef3                       = DC_FRAMEBUFFER_YUVTORGB_COEF3,
>> >> +    .yuv_to_rgb_coef4                       = DC_FRAMEBUFFER_YUVTORGB_COEF4,
>> >> +    .yuv_to_rgb_coefd0                      = DC_FRAMEBUFFER_YUVTORGB_COEFD0,
>> >> +    .yuv_to_rgb_coefd1                      = DC_FRAMEBUFFER_YUVTORGB_COEFD1,
>> >> +    .yuv_to_rgb_coefd2                      = DC_FRAMEBUFFER_YUVTORGB_COEFD2,
>> >> +    .y_clamp_bound                          = DC_FRAMEBUFFER_Y_CLAMP_BOUND,
>> >> +    .uv_clamp_bound                         = DC_FRAMEBUFFER_UV_CLAMP_BOUND,
>> >> +    .rgb_to_rgb_coef0                       = DC_FRAMEBUFFER_RGBTORGB_COEF0,
>> >> +    .rgb_to_rgb_coef1                       = DC_FRAMEBUFFER_RGBTORGB_COEF1,
>> >> +    .rgb_to_rgb_coef2                       = DC_FRAMEBUFFER_RGBTORGB_COEF2,
>> >> +    .rgb_to_rgb_coef3                       = DC_FRAMEBUFFER_RGBTORGB_COEF3,
>> >> +    .rgb_to_rgb_coef4                       = DC_FRAMEBUFFER_RGBTORGB_COEF4,
>> >> +    },
>> >> +    {
>> >> +    .y_address              = DC_OVERLAY_ADDRESS,
>> >> +    .u_address              = DC_OVERLAY_U_ADDRESS,
>> >> +    .v_address              = DC_OVERLAY_V_ADDRESS,
>> >> +    .y_stride               = DC_OVERLAY_STRIDE,
>> >> +    .u_stride               = DC_OVERLAY_U_STRIDE,
>> >> +    .v_stride               = DC_OVERLAY_V_STRIDE,
>> >> +    .size                   = DC_OVERLAY_SIZE,
>> >> +    .top_left               = DC_OVERLAY_TOP_LEFT,
>> >> +    .bottom_right   = DC_OVERLAY_BOTTOM_RIGHT,
>> >> +    .scale_factor_x = DC_OVERLAY_SCALE_FACTOR_X,
>> >> +    .scale_factor_y = DC_OVERLAY_SCALE_FACTOR_Y,
>> >> +    .h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
>> >> +    .h_filter_coef_data  = DC_OVERLAY_H_FILTER_COEF_DATA,
>> >> +    .v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
>> >> +    .v_filter_coef_data  = DC_OVERLAY_V_FILTER_COEF_DATA,
>> >> +    .init_offset             = DC_OVERLAY_INIT_OFFSET,
>> >> +    .color_key                       = DC_OVERLAY_COLOR_KEY,
>> >> +    .color_key_high                 = DC_OVERLAY_COLOR_KEY_HIGH,
>> >> +    .clear_value             = DC_OVERLAY_CLEAR_VALUE,
>> >> +    .color_table_index       = DC_OVERLAY_COLOR_TABLE_INDEX,
>> >> +    .color_table_data        = DC_OVERLAY_COLOR_TABLE_DATA,
>> >> +    .scale_config            = DC_OVERLAY_SCALE_CONFIG,
>> >> +    .water_mark                             = DC_OVERLAY_WATER_MARK,
>> >> +    .degamma_index           = DC_OVERLAY_DEGAMMA_INDEX,
>> >> +    .degamma_data            = DC_OVERLAY_DEGAMMA_DATA,
>> >> +    .degamma_ex_data         = DC_OVERLAY_DEGAMMA_EX_DATA,
>> >> +    .src_global_color        = DC_OVERLAY_SRC_GLOBAL_COLOR,
>> >> +    .dst_global_color        = DC_OVERLAY_DST_GLOBAL_COLOR,
>> >> +    .blend_config            = DC_OVERLAY_BLEND_CONFIG,
>> >> +    .roi_origin                             = DC_OVERLAY_ROI_ORIGIN,
>> >> +    .roi_size                               = DC_OVERLAY_ROI_SIZE,
>> >> +    .yuv_to_rgb_coef0                = DC_OVERLAY_YUVTORGB_COEF0,
>> >> +    .yuv_to_rgb_coef1                = DC_OVERLAY_YUVTORGB_COEF1,
>> >> +    .yuv_to_rgb_coef2                = DC_OVERLAY_YUVTORGB_COEF2,
>> >> +    .yuv_to_rgb_coef3                = DC_OVERLAY_YUVTORGB_COEF3,
>> >> +    .yuv_to_rgb_coef4                       = DC_OVERLAY_YUVTORGB_COEF4,
>> >> +    .yuv_to_rgb_coefd0                      = DC_OVERLAY_YUVTORGB_COEFD0,
>> >> +    .yuv_to_rgb_coefd1                      = DC_OVERLAY_YUVTORGB_COEFD1,
>> >> +    .yuv_to_rgb_coefd2                      = DC_OVERLAY_YUVTORGB_COEFD2,
>> >> +    .y_clamp_bound           = DC_OVERLAY_Y_CLAMP_BOUND,
>> >> +    .uv_clamp_bound          = DC_OVERLAY_UV_CLAMP_BOUND,
>> >> +    .rgb_to_rgb_coef0                = DC_OVERLAY_RGBTORGB_COEF0,
>> >> +    .rgb_to_rgb_coef1                = DC_OVERLAY_RGBTORGB_COEF1,
>> >> +    .rgb_to_rgb_coef2                = DC_OVERLAY_RGBTORGB_COEF2,
>> >> +    .rgb_to_rgb_coef3                = DC_OVERLAY_RGBTORGB_COEF3,
>> >> +    .rgb_to_rgb_coef4                = DC_OVERLAY_RGBTORGB_COEF4,
>> >> +    },
>> >> +};
>> >> +
>> >> +static const u32 primary_overlay_format0[] = {
>> >> +    DRM_FORMAT_XRGB4444,
>> >> +    DRM_FORMAT_XBGR4444,
>> >> +    DRM_FORMAT_RGBX4444,
>> >> +    DRM_FORMAT_BGRX4444,
>> >> +    DRM_FORMAT_ARGB4444,
>> >> +    DRM_FORMAT_ABGR4444,
>> >> +    DRM_FORMAT_RGBA4444,
>> >> +    DRM_FORMAT_BGRA4444,
>> >> +    DRM_FORMAT_XRGB1555,
>> >> +    DRM_FORMAT_XBGR1555,
>> >> +    DRM_FORMAT_RGBX5551,
>> >> +    DRM_FORMAT_BGRX5551,
>> >> +    DRM_FORMAT_ARGB1555,
>> >> +    DRM_FORMAT_ABGR1555,
>> >> +    DRM_FORMAT_RGBA5551,
>> >> +    DRM_FORMAT_BGRA5551,
>> >> +    DRM_FORMAT_RGB565,
>> >> +    DRM_FORMAT_BGR565,
>> >> +    DRM_FORMAT_XRGB8888,
>> >> +    DRM_FORMAT_XBGR8888,
>> >> +    DRM_FORMAT_RGBX8888,
>> >> +    DRM_FORMAT_BGRX8888,
>> >> +    DRM_FORMAT_ARGB8888,
>> >> +    DRM_FORMAT_ABGR8888,
>> >> +    DRM_FORMAT_RGBA8888,
>> >> +    DRM_FORMAT_BGRA8888,
>> >> +    DRM_FORMAT_ARGB2101010,
>> >> +    DRM_FORMAT_ABGR2101010,
>> >> +    DRM_FORMAT_RGBA1010102,
>> >> +    DRM_FORMAT_BGRA1010102,
>> >> +    DRM_FORMAT_YUYV,
>> >> +    DRM_FORMAT_YVYU,
>> >> +    DRM_FORMAT_UYVY,
>> >> +    DRM_FORMAT_VYUY,
>> >> +    DRM_FORMAT_YVU420,
>> >> +    DRM_FORMAT_YUV420,
>> >> +    DRM_FORMAT_NV12,
>> >> +    DRM_FORMAT_NV21,
>> >> +    DRM_FORMAT_NV16,
>> >> +    DRM_FORMAT_NV61,
>> >> +    DRM_FORMAT_P010,
>> >> +};
>> >> +
>> >> +static const u32 primary_overlay_format1[] = {
>> >> +    DRM_FORMAT_XRGB8888,
>> >> +    DRM_FORMAT_XBGR8888,
>> >> +    DRM_FORMAT_RGBX8888,
>> >> +    DRM_FORMAT_BGRX8888,
>> >> +    DRM_FORMAT_ARGB8888,
>> >> +    DRM_FORMAT_ABGR8888,
>> >> +    DRM_FORMAT_RGBA8888,
>> >> +    DRM_FORMAT_BGRA8888,
>> >> +    DRM_FORMAT_ARGB2101010,
>> >> +    DRM_FORMAT_ABGR2101010,
>> >> +    DRM_FORMAT_RGBA1010102,
>> >> +    DRM_FORMAT_BGRA1010102,
>> >> +    DRM_FORMAT_NV12,
>> >> +    DRM_FORMAT_NV21,
>> >> +    DRM_FORMAT_YUV444,
>> >> +};
>> >> +
>> >> +static const u32 cursor_formats[] = {
>> >> +    DRM_FORMAT_ARGB8888
>> >> +};
>> >> +
>> >> +static const u64 format_modifier0[] = {
>> >> +    DRM_FORMAT_MOD_LINEAR,
>> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8),
>> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8),
>> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X8),
>> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X4),
>> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4),
>> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8),
>> >> +    DRM_FORMAT_MOD_INVALID
>> >> +};
>> >> +
>> >> +static const u64 secondary_format_modifiers[] = {
>> >> +    DRM_FORMAT_MOD_LINEAR,
>> >> +    DRM_FORMAT_MOD_INVALID
>> >> +};
>> >> +
>> >> +#define FRAC_16_16(mult, div)        (((mult) << 16) / (div))
>> >> +
>> >> +static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = {
>> >> +    {
>> >> +            /* DC_REV_0 */
>> >> +            {
>> >> +            .name                   = "Primary",
>> >
>> > I'd say that the indentation is strange here.
>> > It might be more scalable to split a single array into per-generation
>> > data structures and reference them directly from the info struct.
yes 
Use plat_data to reference
>> >
>> >> +            .id                             = PRIMARY_PLANE_0,
>> >> +            .type                   = DRM_PLANE_TYPE_PRIMARY,
>> >> +            .num_formats    = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats                = primary_overlay_format0,
>> >> +            .num_modifiers  = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers              = format_modifier0,
>> >> +            .min_width              = 0,
>> >> +            .min_height             = 0,
>> >> +            .max_width              = 4096,
>> >> +            .max_height             = 4096,
>> >> +            .rotation               = DRM_MODE_ROTATE_0 |
>> >> +                                                    DRM_MODE_ROTATE_90 |
>> >> +                                                    DRM_MODE_ROTATE_180 |
>> >> +                                                    DRM_MODE_ROTATE_270 |
>> >> +                                                    DRM_MODE_REFLECT_X |
>> >> +                                                    DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode             = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                                    BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                                    BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 0,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name                   = "Overlay",
>> >> +            .id                             = OVERLAY_PLANE_0,
>> >> +            .type                   = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats    = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats                = primary_overlay_format0,
>> >> +            .num_modifiers  = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers              = format_modifier0,
>> >> +            .min_width              = 0,
>> >> +            .min_height             = 0,
>> >> +            .max_width              = 4096,
>> >> +            .max_height             = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 1,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name                   = "Overlay_1",
>> >> +            .id                             = OVERLAY_PLANE_1,
>> >> +            .type                   = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats    = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats                = primary_overlay_format0,
>> >> +            .num_modifiers  = ARRAY_SIZE(secondary_format_modifiers),
>> >> +            .modifiers              = secondary_format_modifiers,
>> >> +            .min_width              = 0,
>> >> +            .min_height             = 0,
>> >> +            .max_width              = 4096,
>> >> +            .max_height             = 4096,
>> >> +            .rotation               = 0,
>> >> +            .blend_mode             = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                                    BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                                    BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 2,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Primary_1",
>> >> +            .id                             = PRIMARY_PLANE_1,
>> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats         = primary_overlay_format0,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 3,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay_2",
>> >> +            .id                             = OVERLAY_PLANE_2,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats         = primary_overlay_format0,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 4,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay_3",
>> >> +            .id                     = OVERLAY_PLANE_3,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats         = primary_overlay_format0,
>> >> +            .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
>> >> +            .modifiers       = secondary_format_modifiers,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = 0,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 5,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Cursor",
>> >> +            .id                             = CURSOR_PLANE_0,
>> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
>> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
>> >> +            .formats         = cursor_formats,
>> >> +            .num_modifiers = 0,
>> >> +            .modifiers       = NULL,
>> >> +            .min_width       = 32,
>> >> +            .min_height  = 32,
>> >> +            .max_width       = 64,
>> >> +            .max_height  = 64,
>> >> +            .rotation        = 0,
>> >> +            .degamma_size = 0,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 255,
>> >> +            .watermark       = false,
>> >> +            .color_mgmt  = false,
>> >> +            .roi             = false,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Cursor_1",
>> >> +            .id                             = CURSOR_PLANE_1,
>> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
>> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
>> >> +            .formats         = cursor_formats,
>> >> +            .num_modifiers = 0,
>> >> +            .modifiers       = NULL,
>> >> +            .min_width       = 32,
>> >> +            .min_height  = 32,
>> >> +            .max_width       = 64,
>> >> +            .max_height  = 64,
>> >> +            .rotation        = 0,
>> >> +            .degamma_size = 0,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 255,
>> >> +            .watermark       = false,
>> >> +            .color_mgmt  = false,
>> >> +            .roi             = false,
>> >> +            },
>> >> +    },
>> >> +    {
>> >> +            /* DC_REV_1 */
>> >> +            {
>> >> +            .name            = "Primary",
>> >> +            .id                             = PRIMARY_PLANE_0,
>> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats         = primary_overlay_format0,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 0,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay",
>> >> +            .id                             = OVERLAY_PLANE_0,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats         = primary_overlay_format0,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 1,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Primary_1",
>> >> +            .id                             = PRIMARY_PLANE_1,
>> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats         = primary_overlay_format0,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 2,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay_2",
>> >> +            .id                             = OVERLAY_PLANE_2,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats         = primary_overlay_format0,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 3,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Cursor",
>> >> +            .id                             = CURSOR_PLANE_0,
>> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
>> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
>> >> +            .formats         = cursor_formats,
>> >> +            .num_modifiers = 0,
>> >> +            .modifiers       = NULL,
>> >> +            .min_width       = 32,
>> >> +            .min_height  = 32,
>> >> +            .max_width       = 64,
>> >> +            .max_height  = 64,
>> >> +            .rotation        = 0,
>> >> +            .degamma_size = 0,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 255,
>> >> +            .watermark       = false,
>> >> +            .color_mgmt  = false,
>> >> +            .roi             = false,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Cursor_1",
>> >> +            .id                             = CURSOR_PLANE_1,
>> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
>> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
>> >> +            .formats         = cursor_formats,
>> >> +            .num_modifiers = 0,
>> >> +            .modifiers       = NULL,
>> >> +            .min_width       = 32,
>> >> +            .min_height  = 32,
>> >> +            .max_width       = 64,
>> >> +            .max_height  = 64,
>> >> +            .rotation        = 0,
>> >> +            .degamma_size = 0,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 255,
>> >> +            .watermark       = false,
>> >> +            .color_mgmt  = false,
>> >> +            .roi             = false,
>> >> +            },
>> >> +    },
>> >> +    {
>> >> +            /* DC_REV_2 */
>> >> +            {
>> >> +            .name            = "Primary",
>> >> +            .id                             = PRIMARY_PLANE_0,
>> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
>> >> +            .formats         = primary_overlay_format1,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 0,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay",
>> >> +            .id                             = OVERLAY_PLANE_0,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
>> >> +            .formats         = primary_overlay_format1,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 1,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay_1",
>> >> +            .id                             = OVERLAY_PLANE_1,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
>> >> +            .formats         = primary_overlay_format1,
>> >> +            .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
>> >> +            .modifiers       = secondary_format_modifiers,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = 0,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 2,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Primary_1",
>> >> +            .id                             = PRIMARY_PLANE_1,
>> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
>> >> +            .formats         = primary_overlay_format1,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 3,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay_2",
>> >> +            .id                             = OVERLAY_PLANE_2,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
>> >> +            .formats         = primary_overlay_format1,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 4,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay_3",
>> >> +            .id                             = OVERLAY_PLANE_3,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
>> >> +            .formats         = primary_overlay_format1,
>> >> +            .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
>> >> +            .modifiers       = secondary_format_modifiers,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = 0,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 5,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Cursor",
>> >> +            .id                             = CURSOR_PLANE_0,
>> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
>> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
>> >> +            .formats         = cursor_formats,
>> >> +            .num_modifiers = 0,
>> >> +            .modifiers       = NULL,
>> >> +            .min_width       = 32,
>> >> +            .min_height  = 32,
>> >> +            .max_width       = 64,
>> >> +            .max_height  = 64,
>> >> +            .rotation        = 0,
>> >> +            .degamma_size = 0,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 255,
>> >> +            .watermark       = false,
>> >> +            .color_mgmt  = false,
>> >> +            .roi             = false,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Cursor_1",
>> >> +            .id                             = CURSOR_PLANE_1,
>> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
>> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
>> >> +            .formats         = cursor_formats,
>> >> +            .num_modifiers = 0,
>> >> +            .modifiers       = NULL,
>> >> +            .min_width       = 32,
>> >> +            .min_height  = 32,
>> >> +            .max_width       = 64,
>> >> +            .max_height  = 64,
>> >> +            .rotation        = 0,
>> >> +            .degamma_size = 0,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 255,
>> >> +            .watermark       = false,
>> >> +            .color_mgmt  = false,
>> >> +            .roi             = false,
>> >> +            },
>> >> +    },
>> >> +};
>> >> +
>> >> +static const struct vs_dc_info dc_info[] = {
>> >
>> > Again, please split into per-generation instances.
will add  per-generation instances data
and I will only keep DC_REV_0 data struct
DC_REV_1 DC_REV_2 will delete ,because only DC_REV_0 used
>> >
>> >> +    {
>> >> +            /* DC_REV_0 */
>> >> +            .name                   = "DC8200",
>> >> +            .panel_num              = 2,
>> >> +            .plane_num              = 8,
>> >> +            .planes                 = dc_hw_planes[DC_REV_0],
>> >> +            .layer_num              = 6,
>> >> +            .max_bpc                = 10,
>> >> +            .color_formats  = DRM_COLOR_FORMAT_RGB444 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR444 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR422 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR420,
>> >> +            .gamma_size             = GAMMA_EX_SIZE,
>> >> +            .gamma_bits             = 12,
>> >> +            .pitch_alignment        = 128,
>> >> +            .pipe_sync              = false,
>> >> +            .background             = true,
>> >> +            .panel_sync             = true,
>> >> +            .cap_dec                = true,
>> >> +    },
>> >> +    {
>> >> +            /* DC_REV_1 */
>> >> +            .name                   = "DC8200",
>> >> +            .panel_num              = 2,
>> >> +            .plane_num              = 6,
>> >> +            .planes                 = dc_hw_planes[DC_REV_1],
>> >> +            .layer_num              = 4,
>> >> +            .max_bpc                = 10,
>> >> +            .color_formats  = DRM_COLOR_FORMAT_RGB444 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR444 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR422 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR420,
>> >> +            .gamma_size             = GAMMA_EX_SIZE,
>> >> +            .gamma_bits             = 12,
>> >> +            .pitch_alignment        = 128,
>> >> +            .pipe_sync              = false,
>> >> +            .background             = true,
>> >> +            .panel_sync             = true,
>> >> +            .cap_dec                = true,
>> >> +    },
>> >> +    {
>> >> +            /* DC_REV_2 */
>> >> +            .name                   = "DC8200",
>> >> +            .panel_num              = 2,
>> >> +            .plane_num              = 8,
>> >> +            .planes                 = dc_hw_planes[DC_REV_2],
>> >> +            .layer_num              = 6,
>> >> +            .max_bpc                = 10,
>> >> +            .color_formats  = DRM_COLOR_FORMAT_RGB444 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR444 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR422 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR420,
>> >> +            .gamma_size             = GAMMA_EX_SIZE,
>> >> +            .gamma_bits             = 12,
>> >> +            .pitch_alignment        = 128,
>> >> +            .pipe_sync              = false,
>> >> +            .background             = true,
>> >> +            .panel_sync             = true,
>> >> +            .cap_dec                = false,
>> >> +    },
>> >> +};
>> >> +
>> >> +static const struct dc_hw_funcs hw_func;
>> >> +
>> >> +static inline u32 hi_read(struct dc_hw *hw, u32 reg)
>> >> +{
>> >> +    return readl(hw->hi_base + reg);
>> >> +}
>> >> +
>> >> +static inline void hi_write(struct dc_hw *hw, u32 reg, u32 value)
>> >> +{
>> >> +    writel(value, hw->hi_base + reg);
>> >> +}
>> >> +
>> >> +static inline void dc_write(struct dc_hw *hw, u32 reg, u32 value)
>> >> +{
>> >> +    writel(value, hw->reg_base + reg - DC_REG_BASE);
>> >> +}
>> >> +
>> >> +static inline u32 dc_read(struct dc_hw *hw, u32 reg)
>> >> +{
>> >> +    u32 value = readl(hw->reg_base + reg - DC_REG_BASE);
>> >> +
>> >> +    return value;
>> >> +}
>> >> +
>> >> +static inline void dc_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear)
>> >> +{
>> >> +    u32 value = dc_read(hw, reg);
>> >> +
>> >> +    value &= ~clear;
>> >> +    value |= set;
>> >> +    dc_write(hw, reg, value);
>> >> +}
>> >> +
>> >> +static void load_default_filter(struct dc_hw *hw,
>> >> +                            const struct dc_hw_plane_reg *reg, u32 offset)
>> >> +{
>> >> +    u8 i;
>> >> +
>> >> +    dc_write(hw, reg->scale_config + offset, 0x33);
>> >> +    dc_write(hw, reg->init_offset + offset, 0x80008000);
>> >> +    dc_write(hw, reg->h_filter_coef_index + offset, 0x00);
>> >> +    for (i = 0; i < H_COEF_SIZE; i++)
>> >> +            dc_write(hw, reg->h_filter_coef_data + offset, horkernel[i]);
>> >> +
>> >> +    dc_write(hw, reg->v_filter_coef_index + offset, 0x00);
>> >> +    for (i = 0; i < V_COEF_SIZE; i++)
>> >> +            dc_write(hw, reg->v_filter_coef_data + offset, verkernel[i]);
>> >> +}
>> >> +
>> >> +static void load_rgb_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
>> >> +                        u32 offset, u16 *table)
>> >> +{
>> >> +    dc_write(hw, reg->rgb_to_rgb_coef0 + offset, table[0] | (table[1] << 16));
>> >> +    dc_write(hw, reg->rgb_to_rgb_coef1 + offset, table[2] | (table[3] << 16));
>> >> +    dc_write(hw, reg->rgb_to_rgb_coef2 + offset, table[4] | (table[5] << 16));
>> >> +    dc_write(hw, reg->rgb_to_rgb_coef3 + offset, table[6] | (table[7] << 16));
>> >> +    dc_write(hw, reg->rgb_to_rgb_coef4 + offset, table[8]);
>> >> +}
>> >> +
>> >> +static void load_yuv_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
>> >> +                        u32 offset, s32 *table)
>> >> +{
>> >> +    dc_write(hw, reg->yuv_to_rgb_coef0 + offset,
>> >> +             (0xFFFF & table[0]) | (table[1] << 16));
>> >> +    dc_write(hw, reg->yuv_to_rgb_coef1 + offset,
>> >> +             (0xFFFF & table[2]) | (table[3] << 16));
>> >> +    dc_write(hw, reg->yuv_to_rgb_coef2 + offset,
>> >> +             (0xFFFF & table[4]) | (table[5] << 16));
>> >> +    dc_write(hw, reg->yuv_to_rgb_coef3 + offset,
>> >> +             (0xFFFF & table[6]) | (table[7] << 16));
>> >> +    dc_write(hw, reg->yuv_to_rgb_coef4 + offset, table[8]);
>> >> +    dc_write(hw, reg->yuv_to_rgb_coefd0 + offset, table[9]);
>> >> +    dc_write(hw, reg->yuv_to_rgb_coefd1 + offset, table[10]);
>> >> +    dc_write(hw, reg->yuv_to_rgb_coefd2 + offset, table[11]);
>> >> +    dc_write(hw, reg->y_clamp_bound + offset, table[12] | (table[13] << 16));
>> >> +    dc_write(hw, reg->uv_clamp_bound + offset, table[14] | (table[15] << 16));
>> >> +}
>> >> +
>> >> +static void load_rgb_to_yuv(struct dc_hw *hw, u32 offset, s16 *table)
>> >> +{
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF0 + offset,
>> >> +             table[0] | (table[1] << 16));
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF1 + offset,
>> >> +             table[2] | (table[3] << 16));
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF2 + offset,
>> >> +             table[4] | (table[5] << 16));
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF3 + offset,
>> >> +             table[6] | (table[7] << 16));
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF4 + offset, table[8]);
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD0 + offset, table[9]);
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD1 + offset, table[10]);
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD2 + offset, table[11]);
>> >> +}
>> >> +
>> >> +static bool is_rgb(enum dc_hw_color_format format)
>> >> +{
>> >> +    switch (format) {
>> >> +    case FORMAT_X4R4G4B4:
>> >> +    case FORMAT_A4R4G4B4:
>> >> +    case FORMAT_X1R5G5B5:
>> >> +    case FORMAT_A1R5G5B5:
>> >> +    case FORMAT_R5G6B5:
>> >> +    case FORMAT_X8R8G8B8:
>> >> +    case FORMAT_A8R8G8B8:
>> >> +    case FORMAT_A2R10G10B10:
>> >> +            return true;
>> >> +    default:
>> >> +            return false;
>> >> +    }
>> >> +}
>> >> +
>> >> +static void load_degamma_table(struct dc_hw *hw,
>> >> +                           const struct dc_hw_plane_reg *reg,
>> >> +                           u32 offset, u16 *table)
>> >> +{
>> >> +    u16 i;
>> >> +    u32 value;
>> >> +
>> >> +    dc_write(hw, reg->degamma_index + offset, 0);
>> >> +
>> >> +    for (i = 0; i < DEGAMMA_SIZE; i++) {
>> >> +            value = table[i] | (table[i] << 16);
>> >> +            dc_write(hw, reg->degamma_data + offset, value);
>> >> +            dc_write(hw, reg->degamma_ex_data + offset, table[i]);
>> >> +    }
>> >> +}
>> >> +
>> >> +static u32 get_addr_offset(u32 id)
>> >> +{
>> >> +    u32 offset = 0;
>> >> +
>> >> +    switch (id) {
>> >> +    case PRIMARY_PLANE_1:
>> >> +    case OVERLAY_PLANE_1:
>> >> +            offset = 0x04;
>> >> +            break;
>> >> +    case OVERLAY_PLANE_2:
>> >> +            offset = 0x08;
>> >> +            break;
>> >> +    case OVERLAY_PLANE_3:
>> >> +            offset = 0x0C;
>> >> +            break;
>> >> +    default:
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    return offset;
>> >> +}
>> >> +
>> >> +int dc_hw_init(struct dc_hw *hw)
>> >> +{
>> >> +    u8 i, id, panel_num, layer_num;
>> >> +    u32 offset;
>> >> +    u32 revision = hi_read(hw, DC_HW_REVISION);
>> >> +    u32 cid = hi_read(hw, DC_HW_CHIP_CID);
>> >> +    const struct dc_hw_plane_reg *reg;
>> >> +
>> >> +    switch (revision) {
>> >> +    case 0x5720:
>> >> +            hw->rev = DC_REV_0;
>> >> +            break;
>> >> +    case 0x5721:
>> >> +            switch (cid) {
>> >> +            case 0x30B:
>> >> +                    hw->rev = DC_REV_1;
>> >> +                    break;
>> >> +            case 0x310:
>> >> +                    hw->rev = DC_REV_2;
>> >> +                    break;
>> >> +            default:
>> >> +                    hw->rev = DC_REV_0;
>> >> +                    break;
>> >> +            }
>> >> +            break;
>> >> +    default:
>> >> +            return -ENXIO;
>> >> +    }
>> >
>> > We (drm/msm) had code like this for a while (and autodetection of the HW
>> > ID). And nowadays we are migrating to of_device_get_match_data and
>> > per-SoC compat names. It is just easier to handle, it allows one to
>> > specify device more precisely and also it simplifies DT schema.
of_device_get_match_data is a good choice

>> >
>> >> +
>> >> +    hw->info = (struct vs_dc_info *)&dc_info[hw->rev];
>> >> +    hw->func = (struct dc_hw_funcs *)&hw_func;
>> >> +
>> >> +    layer_num = hw->info->layer_num;
>> >> +    for (i = 0; i < layer_num; i++) {
>> >> +            id = hw->info->planes[i].id;
>> >> +            offset = get_addr_offset(id);
>> >> +            if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
>> >> +                    reg = &dc_plane_reg[0];
>> >> +            else
>> >> +                    reg = &dc_plane_reg[1];
>> >> +
>> >> +            load_default_filter(hw, reg, offset);
>> >> +            load_rgb_to_rgb(hw, reg, offset, RGB2RGB);
>> >> +    }
>> >> +
>> >> +    panel_num = hw->info->panel_num;
>> >> +    for (i = 0; i < panel_num; i++) {
>> >> +            offset = i << 2;
>> >> +
>> >> +            load_rgb_to_yuv(hw, offset, RGB2YUV);
>> >> +            dc_write(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0x111);
>> >> +
>> >> +            offset = i ? DC_CURSOR_OFFSET : 0;
>> >> +            dc_write(hw, DC_CURSOR_BACKGROUND + offset, 0x00FFFFFF);
>> >> +            dc_write(hw, DC_CURSOR_FOREGROUND + offset, 0x00AAAAAA);
>> >> +    }
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +void dc_hw_deinit(struct dc_hw *hw)
>> >> +{
>> >> +    /* Nothing to do */
>> >> +}
>> >> +
>> >> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
>> >> +                    struct dc_hw_fb *fb, struct dc_hw_scale *scale,
>> >> +                    struct dc_hw_position *pos, struct dc_hw_blend *blend)
>> >> +{
>> >> +    struct dc_hw_plane *plane = &hw->plane[id];
>> >> +
>> >> +    if (plane) {
>> >> +            if (fb) {
>> >> +                    if (!fb->enable)
>> >> +                            plane->fb.enable = false;
>> >> +                    else
>> >> +                            memcpy(&plane->fb, fb,
>> >> +                                   sizeof(*fb) - sizeof(fb->dirty));
>> >> +                    plane->fb.dirty = true;
>> >> +            }
>> >> +            if (scale) {
>> >> +                    memcpy(&plane->scale, scale,
>> >> +                           sizeof(*scale) - sizeof(scale->dirty));
>> >> +                    plane->scale.dirty = true;
>> >> +            }
>> >> +            if (pos) {
>> >> +                    memcpy(&plane->pos, pos,
>> >> +                           sizeof(*pos) - sizeof(pos->dirty));
>> >> +                    plane->pos.dirty = true;
>> >> +            }
>> >> +            if (blend) {
>> >> +                    memcpy(&plane->blend, blend,
>> >> +                           sizeof(*blend) - sizeof(blend->dirty));
>> >> +                    plane->blend.dirty = true;
>> >> +            }
>> >> +    }
>> >> +}
>> >> +
>> >> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode)
>> >> +{
>> >> +    struct dc_hw_plane *plane = &hw->plane[id];
>> >> +
>> >> +    if (plane) {
>> >> +            if (hw->info->planes[id].degamma_size) {
>> >> +                    plane->degamma.mode = mode;
>> >> +                    plane->degamma.dirty = true;
>> >> +            } else {
>> >> +                    plane->degamma.dirty = false;
>> >> +            }
>> >> +    }
>> >> +}
>> >> +
>> >> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi)
>> >> +{
>> >> +    struct dc_hw_plane *plane = &hw->plane[id];
>> >> +
>> >> +    if (plane) {
>> >> +            memcpy(&plane->roi, roi, sizeof(*roi) - sizeof(roi->dirty));
>> >> +            plane->roi.dirty = true;
>> >> +    }
>> >> +}
>> >> +
>> >> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
>> >> +                       struct dc_hw_colorkey *colorkey)
>> >> +{
>> >> +    struct dc_hw_plane *plane = &hw->plane[id];
>> >> +
>> >> +    if (plane) {
>> >> +            memcpy(&plane->colorkey, colorkey,
>> >> +                   sizeof(*colorkey) - sizeof(colorkey->dirty));
>> >> +            plane->colorkey.dirty = true;
>> >> +    }
>> >> +}
>> >> +
>> >> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos)
>> >> +{
>> >> +    memcpy(&hw->qos, qos, sizeof(*qos) - sizeof(qos->dirty));
>> >> +    hw->qos.dirty = true;
>> >> +}
>> >> +
>> >> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor)
>> >> +{
>> >> +    memcpy(&hw->cursor[id], cursor, sizeof(*cursor) - sizeof(cursor->dirty));
>> >> +    hw->cursor[id].dirty = true;
>> >> +}
>> >> +
>> >> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
>> >> +                    u16 r, u16 g, u16 b)
>> >> +{
>> >> +    if (index >= hw->info->gamma_size)
>> >> +            return;
>> >> +
>> >> +    hw->gamma[id].gamma[index][0] = r;
>> >> +    hw->gamma[id].gamma[index][1] = g;
>> >> +    hw->gamma[id].gamma[index][2] = b;
>> >> +    hw->gamma[id].dirty = true;
>> >> +}
>> >> +
>> >> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable)
>> >> +{
>> >> +    hw->gamma[id].enable = enable;
>> >> +    hw->gamma[id].dirty = true;
>> >> +}
>> >> +
>> >> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display)
>> >> +{
>> >> +    u8 id = display->id;
>> >> +
>> >> +    memcpy(&hw->display[id], display, sizeof(*display));
>> >> +
>> >> +    hw->func->display(hw, display);
>> >> +}
>> >> +
>> >> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable)
>> >> +{
>> >> +    if (enable)
>> >> +            hi_write(hw, AQ_INTR_ENBL, 0xFFFFFFFF);
>> >> +    else
>> >> +            hi_write(hw, AQ_INTR_ENBL, 0);
>> >> +}
>> >> +
>> >> +u32 dc_hw_get_interrupt(struct dc_hw *hw)
>> >> +{
>> >> +    return hi_read(hw, AQ_INTR_ACKNOWLEDGE);
>> >> +}
>> >> +
>> >> +bool dc_hw_check_underflow(struct dc_hw *hw)
>> >> +{
>> >> +    return dc_read(hw, DC_FRAMEBUFFER_CONFIG) & BIT(5);
>> >> +}
>> >> +
>> >> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable)
>> >> +{
>> >> +    u32 i, offset;
>> >> +    u8 id, layer_num = hw->info->layer_num;
>> >> +    u8 panel_num = hw->info->panel_num;
>> >> +
>> >> +    for (i = 0; i < layer_num; i++) {
>> >> +            id = hw->info->planes[i].id;
>> >> +            offset = get_addr_offset(id);
>> >> +            if (enable) {
>> >> +                    if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
>> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, BIT(12), 0);
>> >> +                    else
>> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, BIT(31), 0);
>> >> +            } else {
>> >> +                    if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
>> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, 0, BIT(12));
>> >> +                    else
>> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, 0, BIT(31));
>> >> +            }
>> >> +    }
>> >> +
>> >> +    for (i = 0; i < panel_num; i++) {
>> >> +            offset = i << 2;
>> >> +            if (enable)
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, BIT(0));
>> >> +            else
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, BIT(0), 0);
>> >> +    }
>> >> +}
>> >> +
>> >> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id)
>> >> +{
>> >> +    if (out <= OUT_DP)
>> >> +            hw->out[id] = out;
>> >> +}
>> >> +
>> >> +static void gamma_ex_commit(struct dc_hw *hw)
>> >> +{
>> >> +    u8 panel_num = hw->info->panel_num;
>> >> +    u16 i, j;
>> >> +    u32 value;
>> >> +
>> >> +    for (j = 0; j < panel_num; j++) {
>> >> +            if (hw->gamma[j].dirty) {
>> >> +                    if (hw->gamma[j].enable) {
>> >> +                            dc_write(hw, DC_DISPLAY_GAMMA_EX_INDEX + (j << 2), 0x00);
>> >> +                            for (i = 0; i < GAMMA_EX_SIZE; i++) {
>> >> +                                    value = hw->gamma[j].gamma[i][2] |
>> >> +                                            (hw->gamma[j].gamma[i][1] << 12);
>> >> +                                    dc_write(hw, DC_DISPLAY_GAMMA_EX_DATA + (j << 2), value);
>> >> +                                    dc_write(hw, DC_DISPLAY_GAMMA_EX_ONE_DATA + (j << 2),
>> >> +                                             hw->gamma[j].gamma[i][0]);
>> >> +                            }
>> >> +                            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
>> >> +                                         BIT(13), 0);
>> >> +                    } else {
>> >> +                            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
>> >> +                                         0, BIT(13));
>> >> +                    }
>> >> +                    hw->gamma[j].dirty = false;
>> >> +            }
>> >> +    }
>> >> +}
>> >> +
>> >> +static void plane_commit(struct dc_hw *hw)
>> >> +{
>> >> +    struct dc_hw_plane *plane;
>> >> +    const struct dc_hw_plane_reg *reg;
>> >> +    bool primary = false;
>> >> +    u8 id, layer_num = hw->info->layer_num;
>> >> +    u32 i, offset;
>> >> +
>> >> +    for (i = 0; i < layer_num; i++) {
>> >> +            plane = &hw->plane[i];
>> >> +            id = hw->info->planes[i].id;
>> >> +            offset = get_addr_offset(id);
>> >> +            if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
>> >> +                    reg = &dc_plane_reg[0];
>> >> +                    primary = true;
>> >> +            } else {
>> >> +                    reg = &dc_plane_reg[1];
>> >> +                    primary = false;
>> >> +            }
>> >
>> > Can you please move register defines to some internal hw_plane
>> > structure. These details are known at the time of creation of the plane.
>> > There is no need to determine them again and again.
>> >
>> >> +
>> >> +            if (plane->fb.dirty) {
>> >> +                    if (plane->fb.enable) {
>> >
>> > if (plane->fb.dirty && plane->fb.enable) will save you one level of
>> > indentation.
>> >
>> > However it might be better to use existing DRM flags instead of
>> > inventing your own flags.
>> >
>> >> +                            dc_write(hw, reg->y_address + offset,
>> >> +                                     plane->fb.y_address);
>> >> +                            dc_write(hw, reg->u_address + offset,
>> >> +                                     plane->fb.u_address);
>> >> +                            dc_write(hw, reg->v_address + offset,
>> >> +                                     plane->fb.v_address);
>> >> +                            dc_write(hw, reg->y_stride + offset,
>> >> +                                     plane->fb.y_stride);
>> >> +                            dc_write(hw, reg->u_stride + offset,
>> >> +                                     plane->fb.u_stride);
>> >> +                            dc_write(hw, reg->v_stride + offset,
>> >> +                                     plane->fb.v_stride);
>> >> +                            dc_write(hw, reg->size + offset,
>> >> +                                     plane->fb.width |
>> >> +                                     (plane->fb.height << 15));
>> >> +                            dc_write(hw, reg->water_mark + offset,
>> >> +                                     plane->fb.water_mark);
>> >> +
>> >> +                            if (plane->fb.clear_enable)
>> >> +                                    dc_write(hw, reg->clear_value + offset,
>> >> +                                             plane->fb.clear_value);
>> >> +                    }
>> >> +
>> >> +                    if (primary) {
>> >
>> > You have two kinds of planes. Primary and overlays. Could you please
>> > split this code into two separate functions and define two vtables, one
>> > for primary planes and one for overlays. There is no need to stick
>> > everything into a single funcion that handles everything.
>> >
>> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
>> >> +                                         (plane->fb.format << 26) |
>> >> +                                         (plane->fb.uv_swizzle << 25) |
>> >> +                                         (plane->fb.swizzle << 23) |
>> >> +                                         (plane->fb.tile_mode << 17) |
>> >> +                                         (plane->fb.yuv_color_space << 14) |
>> >> +                                         (plane->fb.rotation << 11) |
>> >> +                                         (plane->fb.clear_enable << 8),
>> >> +                                         (0x1F << 26) |
>> >> +                                         BIT(25) |
>> >> +                                         (0x03 << 23) |
>> >> +                                         (0x1F << 17) |
>> >> +                                         (0x07 << 14) |
>> >> +                                         (0x07 << 11) |
>> >> +                                         BIT(8));
>> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                         (plane->fb.dec_enable << 1) |
>> >> +                                         (plane->fb.enable << 13) |
>> >> +                                         (plane->fb.zpos << 16) |
>> >> +                                         (plane->fb.display_id << 19),
>> >> +                                         BIT(1) | BIT(13) | (0x07 << 16) | BIT(19));
>> >
>> > It looks like this code can enjoy defining names for all the magic
>> > values. Otherwise it is impossible to support it.
>> >
>> >> +                    } else {
>> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> >> +                                         (plane->fb.dec_enable << 27) |
>> >> +                                         (plane->fb.clear_enable << 25) |
>> >> +                                         (plane->fb.enable << 24) |
>> >> +                                         (plane->fb.format << 16) |
>> >> +                                         (plane->fb.uv_swizzle << 15) |
>> >> +                                         (plane->fb.swizzle << 13) |
>> >> +                                         (plane->fb.tile_mode << 8) |
>> >> +                                         (plane->fb.yuv_color_space << 5) |
>> >> +                                         (plane->fb.rotation << 2),
>> >> +                                         BIT(27) |
>> >> +                                         BIT(25) |
>> >> +                                         BIT(24) |
>> >> +                                         (0x1F << 16) |
>> >> +                                         BIT(15) |
>> >> +                                         (0x03 << 13) |
>> >> +                                         (0x1F << 8) |
>> >> +                                         (0x07 << 5) |
>> >> +                                         (0x07 << 2));
>> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset,
>> >> +                                         plane->fb.zpos | (plane->fb.display_id << 3),
>> >> +                                         0x07 | BIT(3));
>> >> +                    }
>> >> +                    plane->fb.dirty = false;
>> >> +            }
>> >> +
>> >> +            if (plane->scale.dirty) {
>> >> +                    if (plane->scale.enable) {
>> >> +                            dc_write(hw, reg->scale_factor_x + offset,
>> >> +                                     plane->scale.scale_factor_x);
>> >> +                            dc_write(hw, reg->scale_factor_y + offset,
>> >> +                                     plane->scale.scale_factor_y);
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_FRAMEBUFFER_CONFIG + offset,
>> >> +                                                 BIT(22), 0);
>> >> +                            else
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_OVERLAY_SCALE_CONFIG + offset,
>> >> +                                                 BIT(8), 0);
>> >> +                    } else {
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_FRAMEBUFFER_CONFIG + offset,
>> >> +                                                 0, BIT(22));
>> >> +                            else
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_OVERLAY_SCALE_CONFIG + offset,
>> >> +                                                 0, BIT(8));
>> >> +                    }
>> >> +                    plane->scale.dirty = false;
>> >> +            }
>> >> +
>> >> +            if (plane->pos.dirty) {
>> >> +                    dc_write(hw, reg->top_left + offset,
>> >> +                             plane->pos.start_x |
>> >> +                             (plane->pos.start_y << 15));
>> >> +                    dc_write(hw, reg->bottom_right + offset,
>> >> +                             plane->pos.end_x |
>> >> +                             (plane->pos.end_y << 15));
>> >> +                    plane->pos.dirty = false;
>> >> +            }
>> >> +
>> >> +            if (plane->blend.dirty) {
>> >> +                    dc_write(hw, reg->src_global_color + offset,
>> >> +                             plane->blend.alpha << 24);
>> >> +                    dc_write(hw, reg->dst_global_color + offset,
>> >> +                             plane->blend.alpha << 24);
>> >> +                    switch (plane->blend.blend_mode) {
>> >> +                    case BLEND_PREMULTI:
>> >> +                            dc_write(hw, reg->blend_config + offset, 0x3450);
>> >> +                            break;
>> >> +                    case BLEND_COVERAGE:
>> >> +                            dc_write(hw, reg->blend_config + offset, 0x3950);
>> >> +                            break;
>> >> +                    case BLEND_PIXEL_NONE:
>> >> +                            dc_write(hw, reg->blend_config + offset, 0x3548);
>> >> +                            break;
>> >> +                    default:
>> >> +                            break;
>> >> +                    }
>> >> +                    plane->blend.dirty = false;
>> >> +            }
>> >> +
>> >> +            if (plane->colorkey.dirty) {
>> >> +                    dc_write(hw, reg->color_key + offset, plane->colorkey.colorkey);
>> >> +                    dc_write(hw, reg->color_key_high + offset,
>> >> +                             plane->colorkey.colorkey_high);
>> >> +
>> >> +                    if (primary)
>> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
>> >> +                                         plane->colorkey.transparency << 9, 0x03 << 9);
>> >> +                    else
>> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> >> +                                         plane->colorkey.transparency, 0x03);
>> >> +
>> >> +                    plane->colorkey.dirty = false;
>> >> +            }
>> >> +
>> >> +            if (plane->roi.dirty) {
>> >> +                    if (plane->roi.enable) {
>> >> +                            dc_write(hw, reg->roi_origin + offset,
>> >> +                                     plane->roi.x | (plane->roi.y << 16));
>> >> +                            dc_write(hw, reg->roi_size + offset,
>> >> +                                     plane->roi.width | (plane->roi.height << 16));
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                                 BIT(0), 0);
>> >> +                            else
>> >> +                                    dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> >> +                                                 BIT(22), 0);
>> >> +                    } else {
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                                 0, BIT(0));
>> >> +                            else
>> >> +                                    dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> >> +                                                 0, BIT(22));
>> >> +                    }
>> >> +                    plane->roi.dirty = false;
>> >> +            }
>> >> +    }
>> >> +}
>> >> +
>> >> +static void plane_ex_commit(struct dc_hw *hw)
>> >> +{
>> >> +    struct dc_hw_plane *plane;
>> >> +    const struct dc_hw_plane_reg *reg;
>> >> +    bool primary = false;
>> >> +    u8 id, layer_num = hw->info->layer_num;
>> >> +    u32 i, offset;
>> >> +
>> >> +    for (i = 0; i < layer_num; i++) {
>> >> +            plane = &hw->plane[i];
>> >> +            id = hw->info->planes[i].id;
>> >> +            offset = get_addr_offset(id);
>> >> +            if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
>> >> +                    reg = &dc_plane_reg[0];
>> >> +                    primary = true;
>> >> +            } else {
>> >> +                    reg = &dc_plane_reg[1];
>> >> +                    primary = false;
>> >> +            }
>> >> +
>> >> +            if (plane->fb.dirty) {
>> >> +                    if (is_rgb(plane->fb.format)) {
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                                 BIT(6), BIT(8));
>> >> +                            else
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_OVERLAY_CONFIG + offset,
>> >> +                                                 BIT(29), BIT(30));
>> >> +                    } else {
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                                 BIT(8), BIT(6));
>> >> +                            else
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_OVERLAY_CONFIG + offset,
>> >> +                                                 BIT(30), BIT(29));
>> >> +                            switch (plane->fb.yuv_color_space) {
>> >> +                            case COLOR_SPACE_601:
>> >> +                                    load_yuv_to_rgb(hw, reg, offset, YUV601_2RGB);
>> >> +                                    break;
>> >> +                            case COLOR_SPACE_709:
>> >> +                                    load_yuv_to_rgb(hw, reg, offset, YUV709_2RGB);
>> >> +                                    break;
>> >> +                            case COLOR_SPACE_2020:
>> >> +                                    load_yuv_to_rgb(hw, reg, offset, YUV2020_2RGB);
>> >> +                                    break;
>> >> +                            default:
>> >> +                                    break;
>> >> +                            }
>> >> +                    }
>> >> +            }
>> >> +            if (plane->degamma.dirty) {
>> >> +                    switch (plane->degamma.mode) {
>> >> +                    case VS_DEGAMMA_DISABLE:
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                                 0, BIT(5));
>> >> +                            else
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_OVERLAY_CONFIG + offset,
>> >> +                                                 0, BIT(28));
>> >> +                            break;
>> >> +                    case VS_DEGAMMA_BT709:
>> >> +                            load_degamma_table(hw, reg, offset, DEGAMMA_709);
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                                 BIT(5), 0);
>> >> +                            else
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_OVERLAY_CONFIG + offset,
>> >> +                                                 BIT(28), 0);
>> >> +                            break;
>> >> +                    case VS_DEGAMMA_BT2020:
>> >> +                            load_degamma_table(hw, reg, offset, DEGAMMA_2020);
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                                 BIT(5), 0);
>> >> +                            else
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_OVERLAY_CONFIG + offset,
>> >> +                                                 BIT(28), 0);
>> >> +                            break;
>> >> +                    default:
>> >> +                            break;
>> >> +                    }
>> >> +                    plane->degamma.dirty = false;
>> >> +            }
>> >> +    }
>> >> +    plane_commit(hw);
>> >> +}
>> >> +
>> >> +static void setup_display(struct dc_hw *hw, struct dc_hw_display *display)
>> >> +{
>> >> +    u8 id = display->id;
>> >> +    u32 dpi_cfg, offset = id << 2;
>> >> +
>> >> +    if (hw->display[id].enable) {
>> >> +            switch (display->bus_format) {
>> >> +            case MEDIA_BUS_FMT_RGB565_1X16:
>> >> +                    dpi_cfg = 0;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_RGB666_1X18:
>> >> +                    dpi_cfg = 3;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
>> >> +                    dpi_cfg = 4;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_RGB888_1X24:
>> >> +                    dpi_cfg = 5;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_RGB101010_1X30:
>> >> +                    dpi_cfg = 6;
>> >> +                    break;
>> >> +            default:
>> >> +                    dpi_cfg = 5;
>> >> +                    break;
>> >> +            }
>> >> +            dc_write(hw, DC_DISPLAY_DPI_CONFIG + offset, dpi_cfg);
>> >> +
>> >> +            if (id == 0)
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
>> >> +            else
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
>> >> +
>> >> +            dc_write(hw, DC_DISPLAY_H + offset, hw->display[id].h_active |
>> >> +                     (hw->display[id].h_total << 16));
>> >> +            dc_write(hw, DC_DISPLAY_H_SYNC + offset,
>> >> +                     hw->display[id].h_sync_start |
>> >> +                     (hw->display[id].h_sync_end << 15) |
>> >> +                     (hw->display[id].h_sync_polarity ? 0 : BIT(31)) |
>> >> +                     BIT(30));
>> >> +            dc_write(hw, DC_DISPLAY_V + offset, hw->display[id].v_active |
>> >> +                            (hw->display[id].v_total << 16));
>> >> +            dc_write(hw, DC_DISPLAY_V_SYNC + offset,
>> >> +                     hw->display[id].v_sync_start |
>> >> +                     (hw->display[id].v_sync_end << 15) |
>> >> +                     (hw->display[id].v_sync_polarity ? 0 : BIT(31)) |
>> >> +                     BIT(30));
>> >> +
>> >> +            if (hw->info->pipe_sync)
>> >> +                    dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
>> >> +                                 0, BIT(3) | BIT(4));
>> >> +
>> >> +            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(12), 0);
>> >> +            if (id == 0)
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(0), BIT(3));
>> >> +            else
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(1), BIT(3));
>> >> +    } else {
>> >> +            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(12));
>> >> +            if (id == 0)
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
>> >> +            else
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
>> >> +    }
>> >> +}
>> >> +
>> >> +static void setup_display_ex(struct dc_hw *hw, struct dc_hw_display *display)
>> >> +{
>> >> +    u8 id = display->id;
>> >> +    u32 dp_cfg, offset = id << 2;
>> >> +    bool is_yuv = false;
>> >> +
>> >> +    if (hw->display[id].enable && hw->out[id] == OUT_DP) {
>> >> +            switch (display->bus_format) {
>> >> +            case MEDIA_BUS_FMT_RGB565_1X16:
>> >> +                    dp_cfg = 0;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_RGB666_1X18:
>> >> +                    dp_cfg = 1;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_RGB888_1X24:
>> >> +                    dp_cfg = 2;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_RGB101010_1X30:
>> >> +                    dp_cfg = 3;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_UYVY8_1X16:
>> >> +                    dp_cfg = 2 << 4;
>> >> +                    is_yuv = true;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_YUV8_1X24:
>> >> +                    dp_cfg = 4 << 4;
>> >> +                    is_yuv = true;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_UYVY10_1X20:
>> >> +                    dp_cfg = 8 << 4;
>> >> +                    is_yuv = true;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_YUV10_1X30:
>> >> +                    dp_cfg = 10 << 4;
>> >> +                    is_yuv = true;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
>> >> +                    dp_cfg = 12 << 4;
>> >> +                    is_yuv = true;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
>> >> +                    dp_cfg = 13 << 4;
>> >> +                    is_yuv = true;
>> >> +                    break;
>> >> +            default:
>> >> +                    dp_cfg = 2;
>> >> +                    break;
>> >> +            }
>> >> +            if (is_yuv)
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(16), 0);
>> >> +            else
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(16));
>> >> +            dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | BIT(3));
>> >> +    }
>> >> +
>> >> +    if (hw->out[id] == OUT_DPI)
>> >> +            dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, BIT(3));
>> >> +
>> >> +    setup_display(hw, display);
>> >> +}
>> >> +
>> >> +static const struct dc_hw_funcs hw_func = {
>> >> +    .gamma = &gamma_ex_commit,
>> >> +    .plane = &plane_ex_commit,
>> >> +    .display = setup_display_ex,
>> >> +};
>> >> +
>> >> +void dc_hw_commit(struct dc_hw *hw)
>> >> +{
>> >> +    u32 i, offset = 0;
>> >> +    u8 plane_num = hw->info->plane_num;
>> >> +    u8 layer_num = hw->info->layer_num;
>> >> +    u8 cursor_num = plane_num - layer_num;
>> >> +
>> >> +    hw->func->gamma(hw);
>> >> +    hw->func->plane(hw);
>> >> +
>> >> +    for (i = 0; i < cursor_num; i++) {
>> >> +            if (hw->cursor[i].dirty) {
>> >> +                    offset = hw->cursor[i].display_id ? DC_CURSOR_OFFSET : 0;
>> >> +                    if (hw->cursor[i].enable) {
>> >> +                            dc_write(hw, DC_CURSOR_ADDRESS + offset,
>> >> +                                     hw->cursor[i].address);
>> >> +                            dc_write(hw, DC_CURSOR_LOCATION + offset, hw->cursor[i].x |
>> >> +                                     (hw->cursor[i].y << 16));
>> >> +                            dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
>> >> +                                         (hw->cursor[i].hot_x << 16) |
>> >> +                                         (hw->cursor[i].hot_y << 8) |
>> >> +                                         (hw->cursor[i].size << 5) |
>> >> +                                         BIT(3) | BIT(2) | 0x02,
>> >> +                                         (0xFF << 16) |
>> >> +                                         (0xFF << 8) |
>> >> +                                         (0x07 << 5) | 0x1F);
>> >> +                    } else {
>> >> +                            dc_set_clear(hw, DC_CURSOR_CONFIG + offset, BIT(3), 0x03);
>> >> +                    }
>> >> +                    hw->cursor[i].dirty = false;
>> >> +            }
>> >> +    }
>> >> +
>> >> +    if (hw->qos.dirty) {
>> >> +            dc_set_clear(hw, DC_QOS_CONFIG, (hw->qos.high_value << 4) |
>> >> +                                     hw->qos.low_value, 0xFF);
>> >> +            hw->qos.dirty = false;
>> >> +    }
>> >> +}
>> >> +
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.h b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
>> >> new file mode 100644
>> >> index 000000000..9a1d767ae
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
>> >> @@ -0,0 +1,492 @@
>> >> +/* SPDX-License-Identifier: GPL-2.0 */
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#ifndef __VS_DC_HW_H__
>> >> +#define __VS_DC_HW_H__
>> >> +
>> >> +#define AQ_INTR_ACKNOWLEDGE                         0x0010
>> >> +#define AQ_INTR_ENBL                                        0x0014
>> >
>> > Please take care to align all the defines.
>> >
>> >> +#define DC_HW_REVISION                                      0x0024
>> >> +#define DC_HW_CHIP_CID                                      0x0030
>> >> +
>> >> +#define DC_REG_BASE                                         0x0800
>> >> +#define DC_REG_RANGE                                        0x2000
>> >> +#define DC_SEC_REG_OFFSET                           0x100000
>> >> +
>> >> +#define DC_FRAMEBUFFER_CONFIG                       0x1518
>> >> +#define DC_FRAMEBUFFER_CONFIG_EX            0x1CC0
>> >> +#define DC_FRAMEBUFFER_SCALE_CONFIG         0x1520
>> >> +#define DC_FRAMEBUFFER_TOP_LEFT                     0x24D8
>> >> +#define DC_FRAMEBUFFER_BOTTOM_RIGHT         0x24E0
>> >> +#define DC_FRAMEBUFFER_ADDRESS                      0x1400
>> >> +#define DC_FRAMEBUFFER_U_ADDRESS            0x1530
>> >> +#define DC_FRAMEBUFFER_V_ADDRESS            0x1538
>> >> +#define DC_FRAMEBUFFER_STRIDE                       0x1408
>> >> +#define DC_FRAMEBUFFER_U_STRIDE                     0x1800
>> >> +#define DC_FRAMEBUFFER_V_STRIDE                     0x1808
>> >> +#define DC_FRAMEBUFFER_SIZE                         0x1810
>> >> +#define DC_FRAMEBUFFER_SCALE_FACTOR_X               0x1828
>> >> +#define DC_FRAMEBUFFER_SCALE_FACTOR_Y               0x1830
>> >> +#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX  0x1838
>> >> +#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA   0x1A00
>> >> +#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX  0x1A08
>> >> +#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA   0x1A10
>> >> +#define DC_FRAMEBUFFER_INIT_OFFSET                  0x1A20
>> >> +#define DC_FRAMEBUFFER_COLOR_KEY                    0x1508
>> >> +#define DC_FRAMEBUFFER_COLOR_KEY_HIGH               0x1510
>> >> +#define DC_FRAMEBUFFER_CLEAR_VALUE                  0x1A18
>> >> +#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX    0x1818
>> >> +#define DC_FRAMEBUFFER_COLOR_TABLE_DATA             0x1820
>> >> +#define DC_FRAMEBUFFER_BG_COLOR                             0x1528
>> >> +#define DC_FRAMEBUFFER_ROI_ORIGIN                   0x1CB0
>> >> +#define DC_FRAMEBUFFER_ROI_SIZE                             0x1CB8
>> >> +#define DC_FRAMEBUFFER_WATER_MARK                   0x1CE8
>> >> +#define DC_FRAMEBUFFER_DEGAMMA_INDEX                0x1D88
>> >> +#define DC_FRAMEBUFFER_DEGAMMA_DATA                 0x1D90
>> >> +#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA              0x1D98
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF0               0x1DA0
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF1               0x1DA8
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF2               0x1DB0
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF3               0x1DB8
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF4               0x1E00
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD0              0x1E08
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD1              0x1E10
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD2              0x1E18
>> >> +#define DC_FRAMEBUFFER_Y_CLAMP_BOUND                0x1E88
>> >> +#define DC_FRAMEBUFFER_UV_CLAMP_BOUND               0x1E90
>> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF0               0x1E20
>> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF1               0x1E28
>> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF2               0x1E30
>> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF3               0x1E38
>> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF4               0x1E40
>> >> +#define DC_FRAMEBUFFER_BLEND_CONFIG                 0x2510
>> >> +#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR             0x2500
>> >> +#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR             0x2508
>> >> +
>> >> +#define DC_OVERLAY_CONFIG                                   0x1540
>> >> +#define DC_OVERLAY_CONFIG_EX                                0x2540
>> >> +#define DC_OVERLAY_SCALE_CONFIG                             0x1C00
>> >> +#define DC_OVERLAY_BLEND_CONFIG                             0x1580
>> >> +#define DC_OVERLAY_TOP_LEFT                                 0x1640
>> >> +#define DC_OVERLAY_BOTTOM_RIGHT                             0x1680
>> >> +#define DC_OVERLAY_ADDRESS                                  0x15C0
>> >> +#define DC_OVERLAY_U_ADDRESS                                0x1840
>> >> +#define DC_OVERLAY_V_ADDRESS                                0x1880
>> >> +#define DC_OVERLAY_STRIDE                                   0x1600
>> >> +#define DC_OVERLAY_U_STRIDE                                 0x18C0
>> >> +#define DC_OVERLAY_V_STRIDE                                 0x1900
>> >> +#define DC_OVERLAY_SIZE                                             0x17C0
>> >> +#define DC_OVERLAY_SCALE_FACTOR_X                   0x1A40
>> >> +#define DC_OVERLAY_SCALE_FACTOR_Y                   0x1A80
>> >> +#define DC_OVERLAY_H_FILTER_COEF_INDEX              0x1AC0
>> >> +#define DC_OVERLAY_H_FILTER_COEF_DATA               0x1B00
>> >> +#define DC_OVERLAY_V_FILTER_COEF_INDEX              0x1B40
>> >> +#define DC_OVERLAY_V_FILTER_COEF_DATA               0x1B80
>> >> +#define DC_OVERLAY_INIT_OFFSET                              0x1BC0
>> >> +#define DC_OVERLAY_COLOR_KEY                                0x1740
>> >> +#define DC_OVERLAY_COLOR_KEY_HIGH                   0x1780
>> >> +#define DC_OVERLAY_CLEAR_VALUE                              0x1940
>> >> +#define DC_OVERLAY_COLOR_TABLE_INDEX                0x1980
>> >> +#define DC_OVERLAY_COLOR_TABLE_DATA                 0x19C0
>> >> +#define DC_OVERLAY_SRC_GLOBAL_COLOR                 0x16C0
>> >> +#define DC_OVERLAY_DST_GLOBAL_COLOR                 0x1700
>> >> +#define DC_OVERLAY_ROI_ORIGIN                               0x1D00
>> >> +#define DC_OVERLAY_ROI_SIZE                                 0x1D40
>> >> +#define DC_OVERLAY_WATER_MARK                               0x1DC0
>> >> +#define DC_OVERLAY_DEGAMMA_INDEX                    0x2200
>> >> +#define DC_OVERLAY_DEGAMMA_DATA                             0x2240
>> >> +#define DC_OVERLAY_DEGAMMA_EX_DATA                  0x2280
>> >> +#define DC_OVERLAY_YUVTORGB_COEF0                   0x1EC0
>> >> +#define DC_OVERLAY_YUVTORGB_COEF1                   0x1F00
>> >> +#define DC_OVERLAY_YUVTORGB_COEF2                   0x1F40
>> >> +#define DC_OVERLAY_YUVTORGB_COEF3                   0x1F80
>> >> +#define DC_OVERLAY_YUVTORGB_COEF4                   0x1FC0
>> >> +#define DC_OVERLAY_YUVTORGB_COEFD0                  0x2000
>> >> +#define DC_OVERLAY_YUVTORGB_COEFD1                  0x2040
>> >> +#define DC_OVERLAY_YUVTORGB_COEFD2                  0x2080
>> >> +#define DC_OVERLAY_Y_CLAMP_BOUND                    0x22C0
>> >> +#define DC_OVERLAY_UV_CLAMP_BOUND                   0x2300
>> >> +#define DC_OVERLAY_RGBTORGB_COEF0                   0x20C0
>> >> +#define DC_OVERLAY_RGBTORGB_COEF1                   0x2100
>> >> +#define DC_OVERLAY_RGBTORGB_COEF2                   0x2140
>> >> +#define DC_OVERLAY_RGBTORGB_COEF3                   0x2180
>> >> +#define DC_OVERLAY_RGBTORGB_COEF4                   0x21C0
>> >> +
>> >> +#define DC_CURSOR_CONFIG                                    0x1468
>> >> +#define DC_CURSOR_ADDRESS                                   0x146C
>> >> +#define DC_CURSOR_LOCATION                                  0x1470
>> >> +#define DC_CURSOR_BACKGROUND                                0x1474
>> >> +#define DC_CURSOR_FOREGROUND                                0x1478
>> >> +#define DC_CURSOR_CLK_GATING                                0x1484
>> >> +#define DC_CURSOR_CONFIG_EX                                 0x24E8
>> >> +#define DC_CURSOR_OFFSET                                    0x1080
>> >> +
>> >> +#define DC_DISPLAY_DITHER_CONFIG            0x1410
>> >> +#define DC_DISPLAY_PANEL_CONFIG                     0x1418
>> >> +#define DC_DISPLAY_PANEL_CONFIG_EX          0x2518
>> >> +#define DC_DISPLAY_DITHER_TABLE_LOW         0x1420
>> >> +#define DC_DISPLAY_DITHER_TABLE_HIGH        0x1428
>> >> +#define DC_DISPLAY_H                                        0x1430
>> >> +#define DC_DISPLAY_H_SYNC                           0x1438
>> >> +#define DC_DISPLAY_V                                        0x1440
>> >> +#define DC_DISPLAY_V_SYNC                           0x1448
>> >> +#define DC_DISPLAY_CURRENT_LOCATION         0x1450
>> >> +#define DC_DISPLAY_GAMMA_INDEX                      0x1458
>> >> +#define DC_DISPLAY_GAMMA_DATA                       0x1460
>> >> +#define DC_DISPLAY_INT                                      0x147C
>> >> +#define DC_DISPLAY_INT_ENABLE                       0x1480
>> >> +#define DC_DISPLAY_DBI_CONFIG                       0x1488
>> >> +#define DC_DISPLAY_GENERAL_CONFIG           0x14B0
>> >> +#define DC_DISPLAY_DPI_CONFIG                       0x14B8
>> >> +#define DC_DISPLAY_PANEL_START                      0x1CCC
>> >> +#define DC_DISPLAY_DEBUG_COUNTER_SELECT             0x14D0
>> >> +#define DC_DISPLAY_DEBUG_COUNTER_VALUE              0x14D8
>> >> +#define DC_DISPLAY_DP_CONFIG                                0x1CD0
>> >> +#define DC_DISPLAY_GAMMA_EX_INDEX                   0x1CF0
>> >> +#define DC_DISPLAY_GAMMA_EX_DATA                    0x1CF8
>> >> +#define DC_DISPLAY_GAMMA_EX_ONE_DATA                0x1D80
>> >> +#define DC_DISPLAY_RGBTOYUV_COEF0                   0x1E48
>> >> +#define DC_DISPLAY_RGBTOYUV_COEF1                   0x1E50
>> >> +#define DC_DISPLAY_RGBTOYUV_COEF2                   0x1E58
>> >> +#define DC_DISPLAY_RGBTOYUV_COEF3                   0x1E60
>> >> +#define DC_DISPLAY_RGBTOYUV_COEF4                   0x1E68
>> >> +#define DC_DISPLAY_RGBTOYUV_COEFD0                  0x1E70
>> >> +#define DC_DISPLAY_RGBTOYUV_COEFD1                  0x1E78
>> >> +#define DC_DISPLAY_RGBTOYUV_COEFD2                  0x1E80
>> >> +
>> >> +#define DC_CLK_GATTING                                      0x1A28
>> >> +#define DC_QOS_CONFIG                                       0x1A38
>> >> +
>> >> +#define DC_TRANSPARENCY_OPAQUE                      0x00
>> >> +#define DC_TRANSPARENCY_KEY                         0x02
>> >> +#define DC_DISPLAY_DITHERTABLE_LOW          0x7B48F3C0
>> >> +#define DC_DISPLAY_DITHERTABLE_HIGH         0x596AD1E2
>> >> +
>> >> +#define GAMMA_SIZE                                          256
>> >> +#define GAMMA_EX_SIZE                                       300
>> >> +#define DEGAMMA_SIZE                                        260
>> >> +
>> >> +#define RGB_TO_RGB_TABLE_SIZE                       9
>> >> +#define YUV_TO_RGB_TABLE_SIZE                       16
>> >> +#define RGB_TO_YUV_TABLE_SIZE                       12
>> >> +
>> >> +#define DC_LAYER_NUM        6
>> >> +#define DC_DISPLAY_NUM      2
>> >> +#define DC_CURSOR_NUM       2
>> >> +
>> >> +#define DC_TILE_MODE4X4     0x15
>> >> +
>> >> +enum dc_chip_rev {
>> >> +    DC_REV_0,/* For HW_REV_5720,HW_REV_5721_311 */
>> >> +    DC_REV_1,/* For HW_REV_5721_30B */
>> >> +    DC_REV_2,/* For HW_REV_5721_310 */
>> >> +};
>> >> +
>> >> +enum dc_hw_plane_id {
>> >> +    PRIMARY_PLANE_0,
>> >> +    OVERLAY_PLANE_0,
>> >> +    OVERLAY_PLANE_1,
>> >> +    PRIMARY_PLANE_1,
>> >> +    OVERLAY_PLANE_2,
>> >> +    OVERLAY_PLANE_3,
>> >> +    CURSOR_PLANE_0,
>> >> +    CURSOR_PLANE_1,
>> >> +    PLANE_NUM
>> >> +};
>> >> +
>> >> +enum dc_hw_color_format {
>> >> +    FORMAT_X4R4G4B4,//0
>> >
>> > Space after the comma will make it more readable. Or just define the
>> > values explicitly, it will also be more readable.
>> >
>> >> +    FORMAT_A4R4G4B4,//1
>> >> +    FORMAT_X1R5G5B5,//2
>> >> +    FORMAT_A1R5G5B5,//3
>> >> +    FORMAT_R5G6B5,//4
>> >> +    FORMAT_X8R8G8B8,//5
>> >> +    FORMAT_A8R8G8B8,//6
>> >> +    FORMAT_YUY2,//7
>> >> +    FORMAT_UYVY,//8
>> >> +    FORMAT_INDEX8,//9
>> >> +    FORMAT_MONOCHROME,//10
>> >> +    FORMAT_YV12 = 0xf,
>> >> +    FORMAT_A8,//16
>> >> +    FORMAT_NV12,//17
>> >> +    FORMAT_NV16,//18
>> >> +    FORMAT_RG16,//19
>> >> +    FORMAT_R8,//20
>> >> +    FORMAT_NV12_10BIT,//21
>> >> +    FORMAT_A2R10G10B10,//22
>> >> +    FORMAT_NV16_10BIT,//23
>> >> +    FORMAT_INDEX1,//24
>> >> +    FORMAT_INDEX2,//25
>> >> +    FORMAT_INDEX4,//26
>> >> +    FORMAT_P010,//27
>> >> +    FORMAT_YUV444,//28
>> >> +    FORMAT_YUV444_10BIT,//29
>> >> +};
>> >> +
>> >> +enum dc_hw_yuv_color_space {
>> >> +    COLOR_SPACE_601 = 0,
>> >> +    COLOR_SPACE_709 = 1,
>> >> +    COLOR_SPACE_2020 = 3,
>> >> +};
>> >> +
>> >> +enum dc_hw_rotation {
>> >> +    ROT_0 = 0,
>> >> +    ROT_90 = 4,
>> >> +    ROT_180 = 5,
>> >> +    ROT_270 = 6,
>> >> +    FLIP_X = 1,
>> >> +    FLIP_Y = 2,
>> >> +    FLIP_XY = 3,
>> >> +};
>> >> +
>> >> +enum dc_hw_swizzle {
>> >> +    SWIZZLE_ARGB = 0,
>> >> +    SWIZZLE_RGBA,
>> >> +    SWIZZLE_ABGR,
>> >> +    SWIZZLE_BGRA,
>> >> +};
>> >> +
>> >> +enum dc_hw_out {
>> >> +    OUT_DPI,
>> >> +    OUT_DP,
>> >> +};
>> >> +
>> >> +enum dc_hw_cursor_size {
>> >> +    CURSOR_SIZE_32X32 = 0,
>> >> +    CURSOR_SIZE_64X64,
>> >> +};
>> >> +
>> >> +enum dc_hw_blend_mode {
>> >> +    /* out.rgb = plane_alpha * fg.rgb +
>> >> +     *              (1 - (plane_alpha * fg.alpha)) * bg.rgb
>> >> +     */
>> >> +    BLEND_PREMULTI,
>> >> +    /* out.rgb = plane_alpha * fg.alpha * fg.rgb +
>> >> +     *              (1 - (plane_alpha * fg.alpha)) * bg.rgb
>> >> +     */
>> >> +    BLEND_COVERAGE,
>> >> +    /* out.rgb = plane_alpha * fg.rgb +
>> >> +     *              (1 - plane_alpha) * bg.rgb
>> >> +     */
>> >> +    BLEND_PIXEL_NONE,
>> >
>> > Can you use defines from <drm/drm_blend.h>?
>> >
>> >> +};
>> >> +
>> >> +struct dc_hw_plane_reg {
>> >> +    u32 y_address;
>> >> +    u32 u_address;
>> >> +    u32 v_address;
>> >> +    u32 y_stride;
>> >> +    u32 u_stride;
>> >> +    u32 v_stride;
>> >> +    u32 size;
>> >> +    u32 top_left;
>> >> +    u32 bottom_right;
>> >> +    u32 scale_factor_x;
>> >> +    u32 scale_factor_y;
>> >> +    u32 h_filter_coef_index;
>> >> +    u32 h_filter_coef_data;
>> >> +    u32 v_filter_coef_index;
>> >> +    u32 v_filter_coef_data;
>> >> +    u32 init_offset;
>> >> +    u32 color_key;
>> >> +    u32 color_key_high;
>> >> +    u32 clear_value;
>> >> +    u32 color_table_index;
>> >> +    u32 color_table_data;
>> >> +    u32 scale_config;
>> >> +    u32 water_mark;
>> >> +    u32 degamma_index;
>> >> +    u32 degamma_data;
>> >> +    u32 degamma_ex_data;
>> >> +    u32 src_global_color;
>> >> +    u32 dst_global_color;
>> >> +    u32 blend_config;
>> >> +    u32 roi_origin;
>> >> +    u32 roi_size;
>> >> +    u32 yuv_to_rgb_coef0;
>> >> +    u32 yuv_to_rgb_coef1;
>> >> +    u32 yuv_to_rgb_coef2;
>> >> +    u32 yuv_to_rgb_coef3;
>> >> +    u32 yuv_to_rgb_coef4;
>> >> +    u32 yuv_to_rgb_coefd0;
>> >> +    u32 yuv_to_rgb_coefd1;
>> >> +    u32 yuv_to_rgb_coefd2;
>> >> +    u32 y_clamp_bound;
>> >> +    u32 uv_clamp_bound;
>> >> +    u32 rgb_to_rgb_coef0;
>> >> +    u32 rgb_to_rgb_coef1;
>> >> +    u32 rgb_to_rgb_coef2;
>> >> +    u32 rgb_to_rgb_coef3;
>> >> +    u32 rgb_to_rgb_coef4;
>> >> +};
>> >> +
>> >> +struct dc_hw_fb {
>> >> +    u32 y_address;
>> >> +    u32 u_address;
>> >> +    u32 v_address;
>> >> +    u32 clear_value;
>> >> +    u32 water_mark;
>> >> +    u16 y_stride;
>> >> +    u16 u_stride;
>> >> +    u16 v_stride;
>> >> +    u16 width;
>> >> +    u16 height;
>> >> +    u8      format;
>> >> +    u8      tile_mode;
>> >> +    u8      rotation;
>> >> +    u8      yuv_color_space;
>> >> +    u8      swizzle;
>> >> +    u8      uv_swizzle;
>> >> +    u8      zpos;
>> >> +    u8      display_id;
>> >> +    bool    clear_enable;
>> >> +    bool    dec_enable;
>> >> +    bool    enable;
>> >> +    bool    dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_scale {
>> >> +    u32 scale_factor_x;
>> >> +    u32 scale_factor_y;
>> >> +    bool    enable;
>> >> +    bool    dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_position {
>> >> +    u16 start_x;
>> >> +    u16 start_y;
>> >> +    u16 end_x;
>> >> +    u16 end_y;
>> >> +    bool    dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_blend {
>> >> +    u8      alpha;
>> >> +    u8      blend_mode;
>> >> +    bool    dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_colorkey {
>> >> +    u32 colorkey;
>> >> +    u32 colorkey_high;
>> >> +    u8      transparency;
>> >> +    bool dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_roi {
>> >> +    u16 x;
>> >> +    u16 y;
>> >> +    u16 width;
>> >> +    u16 height;
>> >> +    bool enable;
>> >> +    bool dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_cursor {
>> >> +    u32 address;
>> >> +    u16 x;
>> >> +    u16 y;
>> >> +    u16 hot_x;
>> >> +    u16 hot_y;
>> >> +    u8      size;
>> >> +    u8      display_id;
>> >> +    bool    enable;
>> >> +    bool    dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_display {
>> >> +    u32 bus_format;
>> >> +    u16 h_active;
>> >> +    u16 h_total;
>> >> +    u16 h_sync_start;
>> >> +    u16 h_sync_end;
>> >> +    u16 v_active;
>> >> +    u16 v_total;
>> >> +    u16 v_sync_start;
>> >> +    u16 v_sync_end;
>> >> +    u8      id;
>> >> +    bool    h_sync_polarity;
>> >> +    bool    v_sync_polarity;
>> >> +    bool    enable;
>> >> +};
>> >> +
>> >> +struct dc_hw_gamma {
>> >> +    u16 gamma[GAMMA_EX_SIZE][3];
>> >> +    bool    enable;
>> >> +    bool    dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_degamma {
>> >> +    u16 degamma[DEGAMMA_SIZE][3];
>> >> +    u32 mode;
>> >> +    bool    dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_plane {
>> >> +    struct dc_hw_fb                 fb;
>> >> +    struct dc_hw_position   pos;
>> >> +    struct dc_hw_scale              scale;
>> >> +    struct dc_hw_blend              blend;
>> >> +    struct dc_hw_roi                roi;
>> >> +    struct dc_hw_colorkey   colorkey;
>> >> +    struct dc_hw_degamma    degamma;
>> >> +};
>> >> +
>> >> +struct dc_hw_qos {
>> >> +    u8        low_value;
>> >> +    u8        high_value;
>> >> +    bool  dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_read {
>> >> +    u32                     reg;
>> >> +    u32                     value;
>> >> +};
>> >> +
>> >> +struct dc_hw;
>> >> +struct dc_hw_funcs {
>> >> +    void (*gamma)(struct dc_hw *hw);
>> >> +    void (*plane)(struct dc_hw *hw);
>> >> +    void (*display)(struct dc_hw *hw, struct dc_hw_display *display);
>> >> +};
>> >> +
>> >> +struct dc_hw {
>> >> +    enum dc_chip_rev rev;
>> >> +    enum dc_hw_out          out[DC_DISPLAY_NUM];
>> >> +    void                    *hi_base;
>> >> +    void                    *reg_base;
>> >> +
>> >> +    struct dc_hw_display display[DC_DISPLAY_NUM];
>> >> +    struct dc_hw_gamma       gamma[DC_DISPLAY_NUM];
>> >> +    struct dc_hw_plane       plane[DC_LAYER_NUM];
>> >> +    struct dc_hw_cursor  cursor[DC_CURSOR_NUM];
>> >> +    struct dc_hw_qos         qos;
>> >> +    struct dc_hw_funcs       *func;
>> >> +    struct vs_dc_info        *info;
>> >> +};
>> >> +
>> >> +int dc_hw_init(struct dc_hw *hw);
>> >> +void dc_hw_deinit(struct dc_hw *hw);
>> >> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
>> >> +                    struct dc_hw_fb *fb, struct dc_hw_scale *scale,
>> >> +                    struct dc_hw_position *pos, struct dc_hw_blend *blend);
>> >> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode);
>> >> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi);
>> >> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
>> >> +                       struct dc_hw_colorkey *colorkey);
>> >> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos);
>> >> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor);
>> >> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
>> >> +                    u16 r, u16 g, u16 b);
>> >> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable);
>> >> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display);
>> >> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable);
>> >> +u32 dc_hw_get_interrupt(struct dc_hw *hw);
>> >> +bool dc_hw_check_underflow(struct dc_hw *hw);
>> >> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable);
>> >> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id);
>> >> +void dc_hw_commit(struct dc_hw *hw);
>> >> +
>> >> +#endif /* __VS_DC_HW_H__ */
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
>> >> index da7698c3d..3cd533cfa 100644
>> >> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
>> >> @@ -21,6 +21,7 @@
>> >>
>> >>   #include "vs_drv.h"
>> >>   #include "vs_modeset.h"
>> >> +#include "vs_dc.h"
>> >>
>> >>   #define DRV_NAME   "verisilicon"
>> >>   #define DRV_DESC   "Verisilicon DRM driver"
>> >> @@ -123,6 +124,7 @@ static const struct component_master_ops vs_drm_ops = {
>> >>   };
>> >>
>> >>   static struct platform_driver *drm_sub_drivers[] = {
>> >> +    &dc_platform_driver,
>> >>
>> >>   };
>> >>
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c
>> >> new file mode 100644
>> >> index 000000000..9bd066015
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_plane.c
>> >> @@ -0,0 +1,526 @@
>> >> +// SPDX-License-Identifier: GPL-2.0
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#include <drm/drm_atomic.h>
>> >> +#include <drm/drm_atomic_helper.h>
>> >> +#include <drm/drm_blend.h>
>> >> +#include <drm/drm_gem_dma_helper.h>
>> >> +#include <drm/drm_fb_dma_helper.h>
>> >> +#include <drm/drm_framebuffer.h>
>> >> +#include <drm/drm_plane.h>
>> >> +#include <drm/drm_plane_helper.h>
>> >> +
>> >> +#include <drm/vs_drm.h>
>> >> +
>> >> +#include "vs_plane.h"
>> >> +#include "vs_drv.h"
>> >> +#include "vs_dc.h"
>> >> +
>> >> +static void vs_plane_reset(struct drm_plane *plane)
>> >> +{
>> >> +    struct vs_plane_state *state;
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +
>> >> +    if (plane->state) {
>> >> +            __drm_atomic_helper_plane_destroy_state(plane->state);
>> >> +
>> >> +            state = to_vs_plane_state(plane->state);
>> >> +            kfree(state);
>> >> +            plane->state = NULL;
>> >> +    }
>> >> +
>> >
>> > Use vs_plane_destroy_state directly. This way you won't miss a refcount
>> > on blobs.
>> >
>> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
>> >> +    if (!state)
>> >> +            return;
>> >> +
>> >> +    state->degamma = VS_DEGAMMA_DISABLE;
>> >> +    state->degamma_changed = false;
>> >> +    state->base.zpos = vs_plane->id;
>> >> +    memset(&state->status, 0, sizeof(state->status));
>> >> +
>> >> +    __drm_atomic_helper_plane_reset(plane, &state->base);
>> >> +}
>> >> +
>> >> +static void _vs_plane_duplicate_blob(struct vs_plane_state *state,
>> >> +                                 struct vs_plane_state *ori_state)
>> >> +{
>> >> +    state->watermark = ori_state->watermark;
>> >> +    state->color_mgmt = ori_state->color_mgmt;
>> >> +    state->roi = ori_state->roi;
>> >> +
>> >> +    if (state->watermark)
>> >> +            drm_property_blob_get(state->watermark);
>> >> +    if (state->color_mgmt)
>> >> +            drm_property_blob_get(state->color_mgmt);
>> >> +    if (state->roi)
>> >> +            drm_property_blob_get(state->roi);
>> >> +}
>> >> +
>> >> +static int
>> >> +_vs_plane_set_property_blob_from_id(struct drm_device *dev,
>> >> +                                struct drm_property_blob **blob,
>> >> +                                u64 blob_id,
>> >> +                                size_t expected_size)
>> >> +{
>> >> +    struct drm_property_blob *new_blob = NULL;
>> >> +
>> >> +    if (blob_id) {
>> >> +            new_blob = drm_property_lookup_blob(dev, blob_id);
>> >> +            if (!new_blob)
>> >> +                    return -EINVAL;
>> >> +
>> >> +            if (new_blob->length != expected_size) {
>> >> +                    drm_property_blob_put(new_blob);
>> >> +                    return -EINVAL;
>> >> +            }
>> >> +    }
>> >> +
>> >> +    drm_property_replace_blob(blob, new_blob);
>> >> +    drm_property_blob_put(new_blob);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static struct drm_plane_state *
>> >> +vs_plane_atomic_duplicate_state(struct drm_plane *plane)
>> >> +{
>> >> +    struct vs_plane_state *ori_state;
>> >> +    struct vs_plane_state *state;
>> >> +
>> >> +    if (WARN_ON(!plane->state))
>> >> +            return NULL;
>> >> +
>> >> +    ori_state = to_vs_plane_state(plane->state);
>> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
>> >> +    if (!state)
>> >> +            return NULL;
>> >> +
>> >> +    __drm_atomic_helper_plane_duplicate_state(plane, &state->base);
>> >> +
>> >> +    state->degamma = ori_state->degamma;
>> >> +    state->degamma_changed = ori_state->degamma_changed;
>> >> +
>> >> +    _vs_plane_duplicate_blob(state, ori_state);
>> >> +    memcpy(&state->status, &ori_state->status, sizeof(ori_state->status));
>> >> +
>> >> +    return &state->base;
>> >> +}
>> >> +
>> >> +static void vs_plane_atomic_destroy_state(struct drm_plane *plane,
>> >> +                                      struct drm_plane_state *state)
>> >> +{
>> >> +    struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
>> >> +
>> >> +    __drm_atomic_helper_plane_destroy_state(state);
>> >> +
>> >> +    drm_property_blob_put(vs_plane_state->watermark);
>> >> +    drm_property_blob_put(vs_plane_state->color_mgmt);
>> >> +    drm_property_blob_put(vs_plane_state->roi);
>> >> +    kfree(vs_plane_state);
>> >> +}
>> >> +
>> >> +static int vs_plane_atomic_set_property(struct drm_plane *plane,
>> >> +                                    struct drm_plane_state *state,
>> >> +                                    struct drm_property *property,
>> >> +                                    uint64_t val)
>> >> +{
>> >> +    struct drm_device *dev = plane->dev;
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +    struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
>> >> +    int ret = 0;
>> >> +
>> >> +    if (property == vs_plane->degamma_mode) {
>> >> +            if (vs_plane_state->degamma != val) {
>> >> +                    vs_plane_state->degamma = val;
>> >> +                    vs_plane_state->degamma_changed = true;
>> >> +            } else {
>> >> +                    vs_plane_state->degamma_changed = false;
>> >> +            }
>> >> +    } else if (property == vs_plane->watermark_prop) {
>> >> +            ret = _vs_plane_set_property_blob_from_id(dev,
>> >> +                                                      &vs_plane_state->watermark,
>> >> +                                                      val,
>> >> +                                                      sizeof(struct drm_vs_watermark));
>> >> +            return ret;
>> >> +    } else if (property == vs_plane->color_mgmt_prop) {
>> >> +            ret = _vs_plane_set_property_blob_from_id(dev,
>> >> +                                                      &vs_plane_state->color_mgmt,
>> >> +                                                      val,
>> >> +                                                      sizeof(struct drm_vs_color_mgmt));
>> >> +            return ret;
>> >> +    } else if (property == vs_plane->roi_prop) {
>> >> +            ret = _vs_plane_set_property_blob_from_id(dev,
>> >> +                                                      &vs_plane_state->roi,
>> >> +                                                      val,
>> >> +                                                      sizeof(struct drm_vs_roi));
>> >> +            return ret;
>> >> +    } else {
>> >> +            return -EINVAL;
>> >> +    }
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int vs_plane_atomic_get_property(struct drm_plane *plane,
>> >> +                                    const struct drm_plane_state *state,
>> >> +                                    struct drm_property *property,
>> >> +                                    uint64_t *val)
>> >> +{
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +    const struct vs_plane_state *vs_plane_state =
>> >> +            container_of(state, const struct vs_plane_state, base);
>> >> +
>> >> +    if (property == vs_plane->degamma_mode)
>> >> +            *val = vs_plane_state->degamma;
>> >> +    else if (property == vs_plane->watermark_prop)
>> >> +            *val = (vs_plane_state->watermark) ?
>> >> +                                    vs_plane_state->watermark->base.id : 0;
>> >> +    else if (property == vs_plane->color_mgmt_prop)
>> >> +            *val = (vs_plane_state->color_mgmt) ?
>> >> +                                    vs_plane_state->color_mgmt->base.id : 0;
>> >
>> > degamma and color management should use standard properties.

hello Dmitry:
There is a question that troubles me

drm_plane include such standard properties.
{
	struct drm_property *alpha_property;
	struct drm_property *zpos_property;
	struct drm_property *rotation_property;
	struct drm_property *blend_mode_property;
	struct drm_property *color_encoding_property;
	struct drm_property *color_range_property;
	struct drm_property *scaling_filter_property;
}

it doesn't include degamma and color management properties

>> >
>> >> +    else if (property == vs_plane->roi_prop)
>> >> +            *val = (vs_plane_state->roi) ?
>> >> +                                    vs_plane_state->roi->base.id : 0;
>> >> +    else
>> >> +            return -EINVAL;
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static bool vs_format_mod_supported(struct drm_plane *plane,
>> >> +                                u32 format,
>> >> +                                u64 modifier)
>> >> +{
>> >> +    int i;
>> >> +
>> >> +    /* We always have to allow these modifiers:
>> >> +     * 1. Core DRM checks for LINEAR support if userspace does not provide modifiers.
>> >> +     * 2. Not passing any modifiers is the same as explicitly passing INVALID.
>> >> +     */
>> >> +    if (modifier == DRM_FORMAT_MOD_LINEAR)
>> >> +            return true;
>> >> +
>> >> +    /* Check that the modifier is on the list of the plane's supported modifiers. */
>> >> +    for (i = 0; i < plane->modifier_count; i++) {
>> >> +            if (modifier == plane->modifiers[i])
>> >> +                    break;
>> >> +    }
>> >> +
>> >> +    if (i == plane->modifier_count)
>> >> +            return false;
>> >> +
>> >> +    return true;
>> >> +}
>> >> +
>> >> +const struct drm_plane_funcs vs_plane_funcs = {
>> >> +    .update_plane           = drm_atomic_helper_update_plane,
>> >> +    .disable_plane          = drm_atomic_helper_disable_plane,
>> >> +    .reset                  = vs_plane_reset,
>> >> +    .atomic_duplicate_state = vs_plane_atomic_duplicate_state,
>> >> +    .atomic_destroy_state   = vs_plane_atomic_destroy_state,
>> >> +    .atomic_set_property    = vs_plane_atomic_set_property,
>> >> +    .atomic_get_property    = vs_plane_atomic_get_property,
>> >> +    .format_mod_supported   = vs_format_mod_supported,
>> >> +};
>> >> +
>> >> +static unsigned char vs_get_plane_number(struct drm_framebuffer *fb)
>> >> +{
>> >> +    const struct drm_format_info *info;
>> >> +
>> >> +    if (!fb)
>> >> +            return 0;
>> >> +
>> >> +    info = drm_format_info(fb->format->format);
>> >> +    if (!info || info->num_planes > DRM_FORMAT_MAX_PLANES)
>> >> +            return 0;
>> >> +
>> >> +    return info->num_planes;
>> >> +}
>> >> +
>> >> +static int vs_plane_atomic_check(struct drm_plane *plane,
>> >> +                             struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> >> +                                                                             plane);
>> >> +    unsigned char i, num_planes;
>> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
>> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
>> >> +
>> >> +    if (!crtc || !fb)
>> >> +            return 0;
>> >> +
>> >> +    num_planes = vs_get_plane_number(fb);
>> >> +
>> >> +    for (i = 0; i < num_planes; i++) {
>> >> +            dma_addr_t dma_addr;
>> >> +
>> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
>> >> +            plane_state->dma_addr[i] = dma_addr;
>> >> +    }
>> >> +
>> >> +    return vs_dc_check_plane(dc, plane, state);
>> >> +}
>> >> +
>> >> +static int vs_cursor_plane_atomic_check(struct drm_plane *plane,
>> >> +                                    struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> >> +                                                                      plane);
>> >> +    unsigned char i, num_planes;
>> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
>> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
>> >> +
>> >> +    if (!crtc || !fb)
>> >> +            return 0;
>> >> +
>> >> +    num_planes = vs_get_plane_number(fb);
>> >> +
>> >> +    for (i = 0; i < num_planes; i++) {
>> >> +            dma_addr_t dma_addr;
>> >> +
>> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
>> >> +            plane_state->dma_addr[i] = dma_addr;
>> >> +    }
>> >> +
>> >> +    return vs_dc_check_cursor_plane(dc, plane, state);
>> >> +}
>> >> +
>> >> +static void vs_plane_atomic_update(struct drm_plane *plane,
>> >> +                               struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
>> >> +                                                                      plane);
>> >
>> > New line after the equal sign will be better.
>> >
>> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> >> +                                                                      plane);
>> >> +
>> >> +    unsigned char i, num_planes;
>> >> +    struct drm_framebuffer *fb;
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    if (!new_state->fb || !new_state->crtc)
>> >> +            return;
>> >
>> > if (!new_state->visible) ?

no matter what value it is ? the driver will handle it
in func 

static void update_fb(struct vs_plane *plane, u8 display_id,
		      struct dc_hw_fb *fb, struct drm_plane_state *state)
{
	struct vs_plane_state *plane_state = to_vs_plane_state(state);
	struct drm_framebuffer *drm_fb = state->fb;
	struct drm_rect *src = &state->src;

.......
	fb->enable = state->visible;
.......
}

so no need add "if (!new_state->visible)" i think

>> >
>> >> +
>> >> +    fb = new_state->fb;
>> >> +
>> >> +    drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
>> >> +
>> >> +    num_planes = vs_get_plane_number(fb);
>> >> +
>> >> +    for (i = 0; i < num_planes; i++) {
>> >> +            dma_addr_t dma_addr;
>> >> +
>> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
>> >> +            plane_state->dma_addr[i] = dma_addr;
>> >> +    }
>> >> +
>> >> +    plane_state->status.src = drm_plane_state_src(new_state);
>> >> +    plane_state->status.dest = drm_plane_state_dest(new_state);
>> >> +
>> >> +    vs_dc_update_plane(dc, vs_plane, plane, state);
>> >> +}
>> >> +
>> >> +static void vs_cursor_plane_atomic_update(struct drm_plane *plane,
>> >> +                                      struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
>> >> +                                                                       plane);
>> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> >> +                                                                       plane);
>> >> +    unsigned char i, num_planes;
>> >> +    struct drm_framebuffer *fb;
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    if (!new_state->fb || !new_state->crtc)
>> >
>> > and here.
>> >
>> >> +            return;
>> >> +
>> >> +    fb = new_state->fb;
>> >> +    drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
>> >> +
>> >> +    num_planes = vs_get_plane_number(fb);
>> >> +
>> >> +    for (i = 0; i < num_planes; i++) {
>> >> +            dma_addr_t dma_addr;
>> >> +
>> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
>> >> +            plane_state->dma_addr[i] = dma_addr;
>> >> +    }
>> >> +
>> >> +    plane_state->status.src = drm_plane_state_src(new_state);
>> >> +    plane_state->status.dest = drm_plane_state_dest(new_state);
>> >> +
>> >> +    vs_dc_update_cursor_plane(dc, vs_plane, plane, state);
>> >> +}
>> >> +
>> >> +static void vs_plane_atomic_disable(struct drm_plane *plane,
>> >> +                                struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> >> +                                                                       plane);
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    vs_dc_disable_plane(dc, vs_plane, old_state);
>> >> +}
>> >> +
>> >> +static void vs_cursor_plane_atomic_disable(struct drm_plane *plane,
>> >> +                                       struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> >> +                                                                       plane);
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    vs_dc_disable_cursor_plane(dc, vs_plane, old_state);
>> >> +}
>> >> +
>> >> +const struct drm_plane_helper_funcs primary_plane_helpers = {
>> >> +    .atomic_check   = vs_plane_atomic_check,
>> >> +    .atomic_update  = vs_plane_atomic_update,
>> >> +    .atomic_disable = vs_plane_atomic_disable,
>> >> +};
>> >> +
>> >> +const struct drm_plane_helper_funcs overlay_plane_helpers = {
>> >> +    .atomic_check   = vs_plane_atomic_check,
>> >> +    .atomic_update  = vs_plane_atomic_update,
>> >> +    .atomic_disable = vs_plane_atomic_disable,
>> >> +};
>> >> +
>> >> +const struct drm_plane_helper_funcs cursor_plane_helpers = {
>> >> +    .atomic_check   = vs_cursor_plane_atomic_check,
>> >> +    .atomic_update  = vs_cursor_plane_atomic_update,
>> >> +    .atomic_disable = vs_cursor_plane_atomic_disable,
>> >> +};
>> >> +
>> >> +static const struct drm_prop_enum_list vs_degamma_mode_enum_list[] = {
>> >> +    { VS_DEGAMMA_DISABLE,   "disabled" },
>> >> +    { VS_DEGAMMA_BT709,     "preset degamma for BT709" },
>> >> +    { VS_DEGAMMA_BT2020,    "preset degamma for BT2020" },
>> >> +};
>> >> +
>> >> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
>> >> +                             struct vs_plane_info *info,
>> >> +                             unsigned int layer_num,
>> >> +                             unsigned int possible_crtcs)
>> >> +{
>> >> +    struct vs_plane *plane;
>> >> +    int ret;
>> >> +
>> >> +    if (!info)
>> >> +            return NULL;
>> >> +
>> >> +    plane = drmm_universal_plane_alloc(drm_dev, struct vs_plane, base,
>> >> +                                       possible_crtcs,
>> >> +                                       &vs_plane_funcs,
>> >> +                                       info->formats, info->num_formats,
>> >> +                                       info->modifiers, info->type,
>> >> +                                       info->name ? info->name : NULL);
>> >> +    if (IS_ERR(plane))
>> >> +            return ERR_CAST(plane);
>> >> +
>> >> +    if (info->type == DRM_PLANE_TYPE_PRIMARY)
>> >> +            drm_plane_helper_add(&plane->base, &primary_plane_helpers);
>> >> +    else if (info->type == DRM_PLANE_TYPE_CURSOR)
>> >> +            drm_plane_helper_add(&plane->base, &cursor_plane_helpers);
>> >> +    else
>> >> +            drm_plane_helper_add(&plane->base, &overlay_plane_helpers);
>> >> +
>> >> +    /* Set up the plane properties */
>> >> +    if (info->degamma_size) {
>> >> +            plane->degamma_mode =
>> >> +            drm_property_create_enum(drm_dev, 0,
>> >> +                                     "DEGAMMA_MODE",
>> >> +                                     vs_degamma_mode_enum_list,
>> >> +                                     ARRAY_SIZE(vs_degamma_mode_enum_list));
>> >> +
>> >> +            if (!plane->degamma_mode)
>> >> +                    return NULL;
>> >> +
>> >> +            drm_object_attach_property(&plane->base.base,
>> >> +                                       plane->degamma_mode,
>> >> +                                       VS_DEGAMMA_DISABLE);
>> >> +    }
>> >> +
>> >> +    if (info->rotation) {
>> >> +            ret = drm_plane_create_rotation_property(&plane->base,
>> >> +                                                     DRM_MODE_ROTATE_0,
>> >> +                                                     info->rotation);
>> >> +            if (ret)
>> >> +                    return NULL;
>> >> +    }
>> >> +
>> >> +    if (info->blend_mode) {
>> >> +            ret = drm_plane_create_blend_mode_property(&plane->base,
>> >> +                                                       info->blend_mode);
>> >> +            if (ret)
>> >> +                    return NULL;
>> >> +            ret = drm_plane_create_alpha_property(&plane->base);
>> >> +            if (ret)
>> >> +                    return NULL;
>> >> +    }
>> >> +
>> >> +    if (info->color_encoding) {
>> >> +            ret = drm_plane_create_color_properties(&plane->base,
>> >> +                                                    info->color_encoding,
>> >> +                                                    BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
>> >> +                                                    DRM_COLOR_YCBCR_BT709,
>> >> +                                                    DRM_COLOR_YCBCR_LIMITED_RANGE);
>> >> +            if (ret)
>> >> +                    return NULL;
>> >> +    }
>> >> +
>> >> +    if (info->zpos != 255) {
>> >> +            ret = drm_plane_create_zpos_property(&plane->base, info->zpos, 0,
>> >> +                                                 layer_num - 1);
>> >> +            if (ret)
>> >> +                    return NULL;
>> >> +    } else {
>> >> +            ret = drm_plane_create_zpos_immutable_property(&plane->base,
>> >> +                                                           info->zpos);
>> >> +            if (ret)
>> >> +                    return NULL;
>> >> +    }
>> >> +
>> >> +    if (info->watermark) {
>> >> +            plane->watermark_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
>> >> +                                                        "WATERMARK", 0);
>> >> +            if (!plane->watermark_prop)
>> >> +                    return NULL;
>> >> +            drm_object_attach_property(&plane->base.base, plane->watermark_prop, 0);
>> >> +    }
>> >> +
>> >> +    if (info->color_mgmt) {
>> >> +            plane->color_mgmt_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
>> >> +                                                         "COLOR_CONFIG", 0);
>> >> +            if (!plane->color_mgmt_prop)
>> >> +                    return NULL;
>> >> +
>> >> +            drm_object_attach_property(&plane->base.base, plane->color_mgmt_prop, 0);
>> >> +    }
>> >> +
>> >> +    if (info->roi) {
>> >> +            plane->roi_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
>> >> +                                                  "ROI", 0);
>> >> +            if (!plane->roi_prop)
>> >> +                    return NULL;
>> >> +
>> >> +            drm_object_attach_property(&plane->base.base, plane->roi_prop, 0);
>> >> +    }
>> >> +
>> >> +    return plane;
>> >> +}
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h
>> >> new file mode 100644
>> >> index 000000000..554b74e96
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_plane.h
>> >> @@ -0,0 +1,58 @@
>> >> +/* SPDX-License-Identifier: GPL-2.0 */
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#ifndef __VS_PLANE_H__
>> >> +#define __VS_PLANE_H__
>> >> +
>> >> +#include <drm/drm_fourcc.h>
>> >> +#include <drm/drm_plane_helper.h>
>> >> +
>> >> +#include "vs_type.h"
>> >> +
>> >> +struct vs_plane_status {
>> >> +    u32 tile_mode;
>> >> +    struct drm_rect src;
>> >> +    struct drm_rect dest;
>> >> +};
>> >> +
>> >> +struct vs_plane_state {
>> >> +    struct drm_plane_state base;
>> >> +    struct vs_plane_status status; /* for debugfs */
>> >> +
>> >> +    struct drm_property_blob *watermark;
>> >> +    struct drm_property_blob *color_mgmt;
>> >> +    struct drm_property_blob *roi;
>> >> +    dma_addr_t dma_addr[DRM_FORMAT_MAX_PLANES];
>> >> +
>> >> +    u32 degamma;
>> >> +    bool degamma_changed;
>> >> +};
>> >> +
>> >> +struct vs_plane {
>> >> +    struct drm_plane base;
>> >> +    u8 id;
>> >> +
>> >> +    struct drm_property *degamma_mode;
>> >> +    struct drm_property *watermark_prop;
>> >> +    struct drm_property *color_mgmt_prop;
>> >> +    struct drm_property *roi_prop;
>> >> +};
>> >> +
>> >> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
>> >> +                             struct vs_plane_info *info,
>> >> +                             unsigned int layer_num,
>> >> +                             unsigned int possible_crtcs);
>> >> +
>> >> +static inline struct vs_plane *to_vs_plane(struct drm_plane *plane)
>> >> +{
>> >> +    return container_of(plane, struct vs_plane, base);
>> >> +}
>> >> +
>> >> +static inline struct vs_plane_state *
>> >> +to_vs_plane_state(struct drm_plane_state *state)
>> >> +{
>> >> +    return container_of(state, struct vs_plane_state, base);
>> >> +}
>> >> +#endif /* __VS_PLANE_H__ */
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_type.h b/drivers/gpu/drm/verisilicon/vs_type.h
>> >> new file mode 100644
>> >> index 000000000..7d3378e29
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_type.h
>> >> @@ -0,0 +1,69 @@
>> >> +/* SPDX-License-Identifier: GPL-2.0 */
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#ifndef __VS_TYPE_H__
>> >> +#define __VS_TYPE_H__
>> >> +
>> >> +#include <drm/drm_plane.h>
>> >> +#include <drm/drm_plane_helper.h>
>> >> +
>> >> +struct vs_plane_info {
>> >> +    const char *name;
>> >> +    u8 id;
>> >> +    enum drm_plane_type type;
>> >> +    unsigned int num_formats;
>> >> +    const u32 *formats;
>> >> +    u8 num_modifiers;
>> >> +    const u64 *modifiers;
>> >> +    unsigned int min_width;
>> >> +    unsigned int min_height;
>> >> +    unsigned int max_width;
>> >> +    unsigned int max_height;
>> >> +    unsigned int rotation;
>> >> +    unsigned int blend_mode;
>> >> +    unsigned int color_encoding;
>> >> +
>> >> +    /* 0 means no de-gamma LUT */
>> >> +    unsigned int degamma_size;
>> >> +
>> >> +    int min_scale; /* 16.16 fixed point */
>> >> +    int max_scale; /* 16.16 fixed point */
>> >> +
>> >> +    /* default zorder value,
>> >> +     * and 255 means unsupported zorder capability
>> >> +     */
>> >> +    u8       zpos;
>> >> +
>> >> +    bool watermark;
>> >> +    bool color_mgmt;
>> >> +    bool roi;
>> >> +};
>> >> +
>> >> +struct vs_dc_info {
>> >> +    const char *name;
>> >> +
>> >> +    u8 panel_num;
>> >> +
>> >> +    /* planes */
>> >> +    u8 plane_num;
>> >> +    const struct vs_plane_info *planes;
>> >> +
>> >> +    u8 layer_num;
>> >> +    unsigned int max_bpc;
>> >> +    unsigned int color_formats;
>> >> +
>> >> +    /* 0 means no gamma LUT */
>> >> +    u16 gamma_size;
>> >> +    u8 gamma_bits;
>> >> +
>> >> +    u16 pitch_alignment;
>> >> +
>> >> +    bool pipe_sync;
>> >> +    bool background;
>> >> +    bool panel_sync;
>> >> +    bool cap_dec;
>> >> +};
>> >> +
>> >> +#endif /* __VS_TYPE_H__ */
>> >
> 
> 
> 

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-11-15 13:30           ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-11-15 13:30 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Emil Renner Berthing, dri-devel, Krzysztof Kozlowski,
	linux-riscv, Sumit Semwal, Shengyang Chen, linux-media,
	devicetree, Conor Dooley, Albert Ou, Maxime Ripard, Jagan Teki,
	linaro-mm-sig, Rob Herring, Chris Morgan, Paul Walmsley,
	Bjorn Andersson, linux-kernel, christian.koenig, Jack Zhu,
	Palmer Dabbelt, Thomas Zimmermann, Shawn Guo, Changhuang Liang



On 2023/11/14 18:59, Dmitry Baryshkov wrote:
> On Tue, 14 Nov 2023 at 12:42, Keith Zhao <keith.zhao@starfivetech.com> wrote:
>>
>>
>>
>> On 2023/10/26 3:28, Dmitry Baryshkov wrote:
>> > On 25/10/2023 13:39, Keith Zhao wrote:
>> >> add 2 crtcs and 8 planes in vs-drm
>> >>
>> >> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>> >> ---
>> >>   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
>> >>   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
>> >>   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
>> >>   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
>> >>   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
>> >>   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
>> >>   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
>> >>   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
>> >>   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
>> >>   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
>> >>   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
>> >>   11 files changed, 4494 insertions(+), 2 deletions(-)
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
>> >>
>> >> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
>> >> index 7d3be305b..1d48016ca 100644
>> >> --- a/drivers/gpu/drm/verisilicon/Makefile
>> >> +++ b/drivers/gpu/drm/verisilicon/Makefile
>> >> @@ -1,7 +1,11 @@
>> >>   # SPDX-License-Identifier: GPL-2.0
>> >>
>> >> -vs_drm-objs := vs_drv.o \
>> >> -            vs_modeset.o
>> >> +vs_drm-objs := vs_dc_hw.o \
>> >> +            vs_dc.o \
>> >> +            vs_crtc.o \
>> >> +            vs_drv.o \
>> >> +            vs_modeset.o \
>> >> +            vs_plane.o
>> >>
>> >>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>> >>
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> >> new file mode 100644
>> >> index 000000000..8a658ea77
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> >> @@ -0,0 +1,257 @@
>> >> +// SPDX-License-Identifier: GPL-2.0
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + *
>> >> + */
>> >> +
>> >> +#include <linux/clk.h>
>> >> +#include <linux/debugfs.h>
>> >> +#include <linux/media-bus-format.h>
>> >> +
>> >> +#include <drm/drm_atomic_helper.h>
>> >> +#include <drm/drm_atomic.h>
>> >> +#include <drm/drm_crtc.h>
>> >> +#include <drm/drm_gem_atomic_helper.h>
>> >> +#include <drm/drm_vblank.h>
>> >> +#include <drm/vs_drm.h>
>> >> +
>> >> +#include "vs_crtc.h"
>> >> +#include "vs_dc.h"
>> >> +#include "vs_drv.h"
>> >> +
>> >> +static void vs_crtc_reset(struct drm_crtc *crtc)
>> >> +{
>> >> +    struct vs_crtc_state *state;
>> >> +
>> >> +    if (crtc->state) {
>> >> +            __drm_atomic_helper_crtc_destroy_state(crtc->state);
>> >> +
>> >> +            state = to_vs_crtc_state(crtc->state);
>> >> +            kfree(state);
>> >> +            crtc->state = NULL;
>> >> +    }
>> >
>> > You can call your crtc_destroy_state function directly here.
>> >
>> >> +
>> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
>> >> +    if (!state)
>> >> +            return;
>> >> +
>> >> +    __drm_atomic_helper_crtc_reset(crtc, &state->base);
>> >> +}
>> >> +
>> >> +static struct drm_crtc_state *
>> >> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
>> >> +{
>> >> +    struct vs_crtc_state *ori_state;
>> >
>> > It might be a matter of taste, but it is usually old_state.
>> >
>> >> +    struct vs_crtc_state *state;
>> >> +
>> >> +    if (!crtc->state)
>> >> +            return NULL;
>> >> +
>> >> +    ori_state = to_vs_crtc_state(crtc->state);
>> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
>> >> +    if (!state)
>> >> +            return NULL;
>> >> +
>> >> +    __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
>> >> +
>> >> +    state->output_fmt = ori_state->output_fmt;
>> >> +    state->encoder_type = ori_state->encoder_type;
>> >> +    state->bpp = ori_state->bpp;
>> >> +    state->underflow = ori_state->underflow;
>> >
>> > Can you use kmemdup instead?
>> >
>> >> +
>> >> +    return &state->base;
>> >> +}
>> >> +
>> >> +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
>> >> +                                     struct drm_crtc_state *state)
>> >> +{
>> >> +    __drm_atomic_helper_crtc_destroy_state(state);
>> >> +    kfree(to_vs_crtc_state(state));
>> >> +}
>> >> +
>> >> +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
>> >> +{
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    vs_dc_enable_vblank(dc, true);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
>> >> +{
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    vs_dc_enable_vblank(dc, false);
>> >> +}
>> >> +
>> >> +static const struct drm_crtc_funcs vs_crtc_funcs = {
>> >> +    .set_config             = drm_atomic_helper_set_config,
>> >> +    .page_flip              = drm_atomic_helper_page_flip,
>> >
>> > destroy is required, see drm_mode_config_cleanup()
>>
>> hi Dmitry:
>> if define destroy in drm_crtc_funcs,
>> it will make __drmm_crtc_init_with_planes unhappy
> 
> Ack, I missed that you have been using drmm_crtc_init. BTW, I checked
> your code, you should be able to switch drm
> drmm_crtc_alloc_with_planes().
> 
yes 
I done the replace and it can work well.

>>
>> see:
>> __printf(6, 0)
>> static int __drmm_crtc_init_with_planes(struct drm_device *dev,
>>                                         struct drm_crtc *crtc,
>>                                         struct drm_plane *primary,
>>                                         struct drm_plane *cursor,
>>                                         const struct drm_crtc_funcs *funcs,
>>                                         const char *name,
>>                                         va_list args)
>> {
>>         int ret;
>>
>>         drm_WARN_ON(dev, funcs && funcs->destroy);
>>
>> ........
>> }
>>
>> It should not need to be defined here, I think
>>
>> >
>> >> +    .reset                  = vs_crtc_reset,
>> >> +    .atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
>> >> +    .atomic_destroy_state   = vs_crtc_atomic_destroy_state,
>> >
>> > please consider adding atomic_print_state to output driver-specific bits.
>> >
>> >> +    .enable_vblank          = vs_crtc_enable_vblank,
>> >> +    .disable_vblank         = vs_crtc_disable_vblank,
>> >> +};
>> >> +
>> >> +static u8 cal_pixel_bits(u32 bus_format)
>> >
>> > This looks like a generic helper code, which can go to a common place.
I don't know if I understand correctly
Here I remove static
Add 2 lines in vs_drv.h

/* vs_crtc.c */
u8 cal_pixel_bits(u32 bus_format);

to make it common 

>> >
>> >> +{
>> >> +    u8 bpp;
>> >> +
>> >> +    switch (bus_format) {
>> >> +    case MEDIA_BUS_FMT_RGB565_1X16:
>> >> +    case MEDIA_BUS_FMT_UYVY8_1X16:
>> >> +            bpp = 16;
>> >> +            break;
>> >> +    case MEDIA_BUS_FMT_RGB666_1X18:
>> >> +    case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
>> >> +            bpp = 18;
>> >> +            break;
>> >> +    case MEDIA_BUS_FMT_UYVY10_1X20:
>> >> +            bpp = 20;
>> >> +            break;
>> >> +    case MEDIA_BUS_FMT_BGR888_1X24:
>> >> +    case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
>> >> +    case MEDIA_BUS_FMT_YUV8_1X24:
>> >> +            bpp = 24;
>> >> +            break;
>> >> +    case MEDIA_BUS_FMT_RGB101010_1X30:
>> >> +    case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
>> >> +    case MEDIA_BUS_FMT_YUV10_1X30:
>> >> +            bpp = 30;
>> >> +            break;
>> >> +    default:
>> >> +            bpp = 24;
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    return bpp;
>> >> +}
>> >> +
>> >> +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
>> >> +                              struct drm_atomic_state *state)
>> >> +{
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
>> >> +
>> >> +    vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
>> >> +
>> >> +    vs_dc_enable(dc, crtc);
>> >> +    drm_crtc_vblank_on(crtc);
>> >> +}
>> >> +
>> >> +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
>> >> +                               struct drm_atomic_state *state)
>> >> +{
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    drm_crtc_vblank_off(crtc);
>> >> +
>> >> +    vs_dc_disable(dc, crtc);
>> >> +
>> >> +    if (crtc->state->event && !crtc->state->active) {
>> >> +            spin_lock_irq(&crtc->dev->event_lock);
>> >> +            drm_crtc_send_vblank_event(crtc, crtc->state->event);
>> >> +            spin_unlock_irq(&crtc->dev->event_lock);
>> >> +
>> >> +            crtc->state->event = NULL;
>> >
>> > I think even should be cleared within the lock.
>> >
>> >> +    }
>> >> +}
>> >> +
>> >> +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
>> >> +                             struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
>> >> +                                                                      crtc);
>> >> +
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct device *dev = vs_crtc->dev;
>> >> +    struct drm_property_blob *blob = crtc->state->gamma_lut;
>> >> +    struct drm_color_lut *lut;
>> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
>> >> +
>> >> +    if (crtc_state->color_mgmt_changed) {
>> >> +            if (blob && blob->length) {
>> >> +                    lut = blob->data;
>> >> +                    vs_dc_set_gamma(dc, crtc, lut,
>> >> +                                    blob->length / sizeof(*lut));
>> >> +                    vs_dc_enable_gamma(dc, crtc, true);
>> >> +            } else {
>> >> +                    vs_dc_enable_gamma(dc, crtc, false);
>> >> +            }
>> >> +    }
>> >> +}
>> >> +
>> >> +static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
>> >> +                             struct drm_atomic_state *state)
>> >> +{
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct drm_pending_vblank_event *event = crtc->state->event;
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    vs_dc_commit(dc);
>> >> +
>> >> +    if (event) {
>> >> +            WARN_ON(drm_crtc_vblank_get(crtc) != 0);
>> >> +
>> >> +            spin_lock_irq(&crtc->dev->event_lock);
>> >> +            drm_crtc_arm_vblank_event(crtc, event);
>> >> +            spin_unlock_irq(&crtc->dev->event_lock);
>> >> +            crtc->state->event = NULL;
>> >
>> > I think even should be cleared within the lock.
>> >
>> >> +    }
>> >> +}
>> >> +
>> >> +static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
>> >> +    .atomic_check = drm_crtc_helper_atomic_check,
>> >> +    .atomic_enable  = vs_crtc_atomic_enable,
>> >> +    .atomic_disable = vs_crtc_atomic_disable,
>> >> +    .atomic_begin   = vs_crtc_atomic_begin,
>> >> +    .atomic_flush   = vs_crtc_atomic_flush,
>> >> +};
>> >> +
>> >> +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
>> >> +    { VS_SINGLE_DC,                         "single dc mode" },
>> >> +    { VS_MULTI_DC_PRIMARY,          "primary dc for multi dc mode" },
>> >> +    { VS_MULTI_DC_SECONDARY,        "secondary dc for multi dc mode" },
>> >> +};
>> >> +
>> >> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
>> >> +                           struct vs_dc_info *info)
>> >> +{
>> >> +    struct vs_crtc *crtc;
>> >> +    int ret;
>> >> +
>> >> +    if (!info)
>> >> +            return NULL;
>> >> +
>> >> +    crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
>> >> +    if (!crtc)
>> >> +            return NULL;
>> >> +
>> >> +    ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
>> >> +                                     NULL, NULL, &vs_crtc_funcs,
>> >> +                                     info->name ? info->name : NULL);
>> >
>> > It might be better to add drmm_crtc_init() helper.
	drmm_crtc_alloc_with_planes used:
	...
	struct vs_crtc *crtc;
	int ret;

	if (!info)
		return NULL;

	crtc = drmm_crtc_alloc_with_planes(drm_dev, struct vs_crtc, base,
					       NULL, NULL,
					       &vs_crtc_funcs, info->name ? info->name : NULL);
	...

>> >
>> >> +    if (ret)
>> >> +            return NULL;
>> >> +
>> >> +    drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
>> >> +
>> >> +    if (info->gamma_size) {
>> >> +            ret = drm_mode_crtc_set_gamma_size(&crtc->base,
>> >> +                                               info->gamma_size);
>> >> +            if (ret)
>> >> +                    return NULL;
>> >> +
>> >> +            drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
>> >> +                                       info->gamma_size);
>> >> +    }
>> >> +
>> >> +    crtc->max_bpc = info->max_bpc;
>> >> +    crtc->color_formats = info->color_formats;
>> >> +    return crtc;
>> >> +}
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h
>> >> new file mode 100644
>> >> index 000000000..526dd63e5
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.h
>> >> @@ -0,0 +1,43 @@
>> >> +/* SPDX-License-Identifier: GPL-2.0 */
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#ifndef __VS_CRTC_H__
>> >> +#define __VS_CRTC_H__
>> >> +
>> >> +#include <drm/drm_crtc.h>
>> >> +#include <drm/drm_crtc_helper.h>
>> >> +
>> >> +#include "vs_type.h"
>> >> +
>> >> +struct vs_crtc_state {
>> >> +    struct drm_crtc_state base;
>> >> +
>> >> +    u32 output_fmt;
>> >> +    u8 encoder_type;
>> >> +    u8 bpp;
>> >> +    bool underflow;
>> >> +};
>> >> +
>> >> +struct vs_crtc {
>> >> +    struct drm_crtc base;
>> >> +    struct device *dev;
>> >> +    unsigned int max_bpc;
>> >> +    unsigned int color_formats;
>> >> +};
>> >> +
>> >> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
>> >> +                           struct vs_dc_info *info);
>> >> +
>> >> +static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc)
>> >> +{
>> >> +    return container_of(crtc, struct vs_crtc, base);
>> >> +}
>> >> +
>> >> +static inline struct vs_crtc_state *
>> >> +to_vs_crtc_state(struct drm_crtc_state *state)
>> >> +{
>> >> +    return container_of(state, struct vs_crtc_state, base);
>> >> +}
>> >> +#endif /* __VS_CRTC_H__ */
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
>> >> new file mode 100644
>> >> index 000000000..b5ab92d98
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc.c
>> >> @@ -0,0 +1,1002 @@
>> >> +// SPDX-License-Identifier: GPL-2.0
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#include <linux/component.h>
>> >> +#include <linux/clk.h>
>> >> +#include <linux/of.h>
>> >> +#include <linux/of_device.h>
>> >> +#include <linux/pm_runtime.h>
>> >> +#include <linux/reset.h>
>> >> +
>> >> +#include <drm/drm_atomic.h>
>> >> +#include <drm/drm_atomic_helper.h>
>> >> +#include <drm/drm_blend.h>
>> >> +#include <drm/drm_framebuffer.h>
>> >> +#include <drm/drm_vblank.h>
>> >> +#include <drm/vs_drm.h>
>> >> +
>> >> +#include "vs_dc_hw.h"
>> >> +#include "vs_dc.h"
>> >> +#include "vs_drv.h"
>> >> +
>> >> +static const char * const vout_clocks[] = {
>> >> +    "noc_bus",
>> >> +    "channel0",
>> >> +    "channel1",
>> >> +    "dc_core",
>> >> +    "axi_core",
>> >> +    "ahb",
>> >> +    "hdmi_tx",
>> >> +    "dc_parent",
>> >> +
>> >> +};
>> >> +
>> >> +static const char * const vout_resets[] = {
>> >> +    "axi",
>> >> +    "ahb",
>> >> +    "core",
>> >> +};
>> >> +
>> >> +static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb)
>> >> +{
>> >> +    u8 f = FORMAT_A8R8G8B8;
>> >> +
>> >> +    switch (format) {
>> >> +    case DRM_FORMAT_XRGB4444:
>> >> +    case DRM_FORMAT_RGBX4444:
>> >> +    case DRM_FORMAT_XBGR4444:
>> >> +    case DRM_FORMAT_BGRX4444:
>> >> +            f = FORMAT_X4R4G4B4;
>> >> +            break;
>> >> +    case DRM_FORMAT_ARGB4444:
>> >> +    case DRM_FORMAT_RGBA4444:
>> >> +    case DRM_FORMAT_ABGR4444:
>> >> +    case DRM_FORMAT_BGRA4444:
>> >> +            f = FORMAT_A4R4G4B4;
>> >> +            break;
>> >> +    case DRM_FORMAT_XRGB1555:
>> >> +    case DRM_FORMAT_RGBX5551:
>> >> +    case DRM_FORMAT_XBGR1555:
>> >> +    case DRM_FORMAT_BGRX5551:
>> >> +            f = FORMAT_X1R5G5B5;
>> >> +            break;
>> >> +    case DRM_FORMAT_ARGB1555:
>> >> +    case DRM_FORMAT_RGBA5551:
>> >> +    case DRM_FORMAT_ABGR1555:
>> >> +    case DRM_FORMAT_BGRA5551:
>> >> +            f = FORMAT_A1R5G5B5;
>> >> +            break;
>> >> +    case DRM_FORMAT_RGB565:
>> >> +    case DRM_FORMAT_BGR565:
>> >> +            f = FORMAT_R5G6B5;
>> >> +            break;
>> >> +    case DRM_FORMAT_XRGB8888:
>> >> +    case DRM_FORMAT_RGBX8888:
>> >> +    case DRM_FORMAT_XBGR8888:
>> >> +    case DRM_FORMAT_BGRX8888:
>> >> +            f = FORMAT_X8R8G8B8;
>> >> +            break;
>> >> +    case DRM_FORMAT_ARGB8888:
>> >> +    case DRM_FORMAT_RGBA8888:
>> >> +    case DRM_FORMAT_ABGR8888:
>> >> +    case DRM_FORMAT_BGRA8888:
>> >> +            f = FORMAT_A8R8G8B8;
>> >> +            break;
>> >> +    case DRM_FORMAT_YUYV:
>> >> +    case DRM_FORMAT_YVYU:
>> >> +            f = FORMAT_YUY2;
>> >> +            break;
>> >> +    case DRM_FORMAT_UYVY:
>> >> +    case DRM_FORMAT_VYUY:
>> >> +            f = FORMAT_UYVY;
>> >> +            break;
>> >> +    case DRM_FORMAT_YUV420:
>> >> +    case DRM_FORMAT_YVU420:
>> >> +            f = FORMAT_YV12;
>> >> +            break;
>> >> +    case DRM_FORMAT_NV21:
>> >> +            f = FORMAT_NV12;
>> >> +            break;
>> >> +    case DRM_FORMAT_NV16:
>> >> +    case DRM_FORMAT_NV61:
>> >> +            f = FORMAT_NV16;
>> >> +            break;
>> >> +    case DRM_FORMAT_P010:
>> >> +            f = FORMAT_P010;
>> >> +            break;
>> >> +    case DRM_FORMAT_ARGB2101010:
>> >> +    case DRM_FORMAT_RGBA1010102:
>> >> +    case DRM_FORMAT_ABGR2101010:
>> >> +    case DRM_FORMAT_BGRA1010102:
>> >> +            f = FORMAT_A2R10G10B10;
>> >> +            break;
>> >> +    case DRM_FORMAT_NV12:
>> >> +            f = FORMAT_NV12;
>> >> +            break;
>> >> +    case DRM_FORMAT_YUV444:
>> >> +            f = FORMAT_YUV444;
>> >> +            break;
>> >> +    default:
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    fb->format = f;
>> >> +}
>> >> +
>> >> +static inline void update_swizzle(u32 format, struct dc_hw_fb *fb)
>> >> +{
>> >> +    fb->swizzle = SWIZZLE_ARGB;
>> >> +    fb->uv_swizzle = 0;
>> >> +
>> >> +    switch (format) {
>> >> +    case DRM_FORMAT_RGBX4444:
>> >> +    case DRM_FORMAT_RGBA4444:
>> >> +    case DRM_FORMAT_RGBX5551:
>> >> +    case DRM_FORMAT_RGBA5551:
>> >> +    case DRM_FORMAT_RGBX8888:
>> >> +    case DRM_FORMAT_RGBA8888:
>> >> +    case DRM_FORMAT_RGBA1010102:
>> >> +            fb->swizzle = SWIZZLE_RGBA;
>> >> +            break;
>> >> +    case DRM_FORMAT_XBGR4444:
>> >> +    case DRM_FORMAT_ABGR4444:
>> >> +    case DRM_FORMAT_XBGR1555:
>> >> +    case DRM_FORMAT_ABGR1555:
>> >> +    case DRM_FORMAT_BGR565:
>> >> +    case DRM_FORMAT_XBGR8888:
>> >> +    case DRM_FORMAT_ABGR8888:
>> >> +    case DRM_FORMAT_ABGR2101010:
>> >> +            fb->swizzle = SWIZZLE_ABGR;
>> >> +            break;
>> >> +    case DRM_FORMAT_BGRX4444:
>> >> +    case DRM_FORMAT_BGRA4444:
>> >> +    case DRM_FORMAT_BGRX5551:
>> >> +    case DRM_FORMAT_BGRA5551:
>> >> +    case DRM_FORMAT_BGRX8888:
>> >> +    case DRM_FORMAT_BGRA8888:
>> >> +    case DRM_FORMAT_BGRA1010102:
>> >> +            fb->swizzle = SWIZZLE_BGRA;
>> >> +            break;
>> >> +    case DRM_FORMAT_YVYU:
>> >> +    case DRM_FORMAT_VYUY:
>> >> +    case DRM_FORMAT_NV21:
>> >> +    case DRM_FORMAT_NV61:
>> >> +            fb->uv_swizzle = 1;
>> >> +            break;
>> >> +    default:
>> >> +            break;
>> >> +    }
>> >> +}
>> >> +
>> >> +static inline void update_watermark(struct drm_property_blob *watermark,
>> >> +                                struct dc_hw_fb *fb)
>> >> +{
>> >> +    struct drm_vs_watermark *data;
>> >> +
>> >> +    fb->water_mark = 0;
>> >> +
>> >> +    if (watermark) {
>> >> +            data = watermark->data;
>> >> +            fb->water_mark = data->watermark & 0xFFFFF;
>> >> +    }
>> >> +}
>> >> +
>> >> +static inline u8 to_vs_rotation(unsigned int rotation)
>> >> +{
>> >> +    u8 rot;
>> >> +
>> >> +    switch (rotation & DRM_MODE_REFLECT_MASK) {
>> >> +    case DRM_MODE_REFLECT_X:
>> >> +            rot = FLIP_X;
>> >> +            return rot;
>> >> +    case DRM_MODE_REFLECT_Y:
>> >> +            rot = FLIP_Y;
>> >> +            return rot;
>> >> +    case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
>> >> +            rot = FLIP_XY;
>> >> +            return rot;
>> >> +    default:
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    switch (rotation & DRM_MODE_ROTATE_MASK) {
>> >> +    case DRM_MODE_ROTATE_0:
>> >> +            rot = ROT_0;
>> >> +            break;
>> >> +    case DRM_MODE_ROTATE_90:
>> >> +            rot = ROT_90;
>> >> +            break;
>> >> +    case DRM_MODE_ROTATE_180:
>> >> +            rot = ROT_180;
>> >> +            break;
>> >> +    case DRM_MODE_ROTATE_270:
>> >> +            rot = ROT_270;
>> >> +            break;
>> >> +    default:
>> >> +            rot = ROT_0;
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    return rot;
>> >> +}
>> >> +
>> >> +static inline u8 to_vs_yuv_color_space(u32 color_space)
>> >> +{
>> >> +    u8 cs;
>> >> +
>> >> +    switch (color_space) {
>> >> +    case DRM_COLOR_YCBCR_BT601:
>> >> +            cs = COLOR_SPACE_601;
>> >> +            break;
>> >> +    case DRM_COLOR_YCBCR_BT709:
>> >> +            cs = COLOR_SPACE_709;
>> >> +            break;
>> >> +    case DRM_COLOR_YCBCR_BT2020:
>> >> +            cs = COLOR_SPACE_2020;
>> >> +            break;
>> >> +    default:
>> >> +            cs = COLOR_SPACE_601;
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    return cs;
>> >> +}
>> >> +
>> >> +static inline u8 to_vs_tile_mode(u64 modifier)
>> >> +{
>> >> +    if (modifier == DRM_FORMAT_MOD_VIVANTE_TILED)
>> >> +            return DC_TILE_MODE4X4;
>> >> +
>> >> +    return (u8)(modifier & DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK);
>> >> +}
>> >> +
>> >> +static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc)
>> >> +{
>> >> +    u8 panel_num = dc->hw.info->panel_num;
>> >> +    u32 index = drm_crtc_index(crtc);
>> >> +    int i;
>> >> +
>> >> +    for (i = 0; i < panel_num; i++) {
>> >> +            if (index == dc->crtc[i]->base.index)
>> >> +                    return i;
>> >> +    }
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
>> >> +                                      unsigned int alignment)
>> >> +{
>> >> +    struct vs_drm_device *priv = to_vs_drm_private(drm_dev);
>> >> +
>> >> +    if (alignment > priv->pitch_alignment)
>> >> +            priv->pitch_alignment = alignment;
>> >> +}
>> >> +
>> >> +static int plda_clk_rst_init(struct device *dev)
>> >> +{
>> >> +    int ret = 0;
>> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
>> >> +
>> >> +    ret = clk_bulk_prepare_enable(dc->nclks, dc->clk_vout);
>> >> +    if (ret) {
>> >> +            dev_err(dev, "failed to enable clocks\n");
>> >> +            return ret;
>> >> +    }
>> >> +
>> >> +    ret = reset_control_bulk_deassert(dc->nrsts, dc->rst_vout);
>> >> +    return ret;
>> >> +}
>> >> +
>> >> +static void plda_clk_rst_deinit(struct device *dev)
>> >> +{
>> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
>> >> +
>> >> +    reset_control_bulk_assert(dc->nrsts, dc->rst_vout);
>> >> +    clk_bulk_disable_unprepare(dc->nclks, dc->clk_vout);
>> >> +}
>> >> +
>> >> +static void dc_deinit(struct device *dev)
>> >> +{
>> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
>> >> +
>> >> +    dc_hw_enable_interrupt(&dc->hw, 0);
>> >> +    dc_hw_deinit(&dc->hw);
>> >> +    plda_clk_rst_deinit(dev);
>> >> +}
>> >> +
>> >> +static int dc_init(struct device *dev)
>> >> +{
>> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
>> >> +    int ret;
>> >> +
>> >> +    dc->first_frame = true;
>> >> +
>> >> +    ret = plda_clk_rst_init(dev);
>> >> +    if (ret < 0) {
>> >> +            dev_err(dev, "failed to init dc clk reset: %d\n", ret);
>> >> +            return ret;
>> >> +    }
>> >> +
>> >> +    ret = dc_hw_init(&dc->hw);
>> >> +    if (ret) {
>> >> +            dev_err(dev, "failed to init DC HW\n");
>> >> +            return ret;
>> >> +    }
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
>> >> +{
>> >> +    struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
>> >> +    struct drm_display_mode *mode = &crtc->state->adjusted_mode;
>> >> +    struct dc_hw_display display;
>> >> +
>> >> +    display.bus_format = crtc_state->output_fmt;
>> >> +    display.h_active = mode->hdisplay;
>> >> +    display.h_total = mode->htotal;
>> >> +    display.h_sync_start = mode->hsync_start;
>> >> +    display.h_sync_end = mode->hsync_end;
>> >> +    if (mode->flags & DRM_MODE_FLAG_PHSYNC)
>> >> +            display.h_sync_polarity = true;
>> >> +    else
>> >> +            display.h_sync_polarity = false;
>> >> +
>> >> +    display.v_active = mode->vdisplay;
>> >> +    display.v_total = mode->vtotal;
>> >> +    display.v_sync_start = mode->vsync_start;
>> >> +    display.v_sync_end = mode->vsync_end;
>> >> +
>> >> +    if (mode->flags & DRM_MODE_FLAG_PVSYNC)
>> >> +            display.v_sync_polarity = true;
>> >> +    else
>> >> +            display.v_sync_polarity = false;
>> >> +
>> >> +    display.id = to_vs_display_id(dc, crtc);
>> >> +
>> >> +    display.enable = true;
>> >> +
>> >> +    if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) {
>> >> +            dc_hw_set_out(&dc->hw, OUT_DPI, display.id);
>> >> +            clk_set_rate(dc->clk_vout[CLK_VOUT_SOC_PIX].clk, mode->clock * 1000);
>> >> +            clk_set_parent(dc->clk_vout[CLK_VOUT_PIX1].clk,
>> >> +                           dc->clk_vout[CLK_VOUT_SOC_PIX].clk);
>> >> +    } else {
>> >> +            dc_hw_set_out(&dc->hw, OUT_DP, display.id);
>> >> +            clk_set_parent(dc->clk_vout[CLK_VOUT_PIX0].clk,
>> >> +                           dc->clk_vout[CLK_VOUT_HDMI_PIX].clk);
>> >> +    }
>> >> +
>> >> +    dc_hw_setup_display(&dc->hw, &display);
>> >> +}
>> >> +
>> >> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc)
>> >> +{
>> >> +    struct dc_hw_display display;
>> >> +
>> >> +    display.id = to_vs_display_id(dc, crtc);
>> >> +    display.enable = false;
>> >> +
>> >> +    dc_hw_setup_display(&dc->hw, &display);
>> >> +}
>> >> +
>> >> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
>> >> +                 struct drm_color_lut *lut, unsigned int size)
>> >> +{
>> >> +    u16 i, r, g, b;
>> >> +    u8 bits, id;
>> >> +
>> >> +    if (size != dc->hw.info->gamma_size) {
>> >> +            drm_err(crtc->dev, "gamma size does not match!\n");
>> >> +            return;
>> >> +    }
>> >> +
>> >> +    id = to_vs_display_id(dc, crtc);
>> >> +
>> >> +    bits = dc->hw.info->gamma_bits;
>> >> +    for (i = 0; i < size; i++) {
>> >> +            r = drm_color_lut_extract(lut[i].red, bits);
>> >> +            g = drm_color_lut_extract(lut[i].green, bits);
>> >> +            b = drm_color_lut_extract(lut[i].blue, bits);
>> >> +            dc_hw_update_gamma(&dc->hw, id, i, r, g, b);
>> >> +    }
>> >> +}
>> >> +
>> >> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
>> >> +                    bool enable)
>> >> +{
>> >> +    u8 id;
>> >> +
>> >> +    id = to_vs_display_id(dc, crtc);
>> >> +    dc_hw_enable_gamma(&dc->hw, id, enable);
>> >> +}
>> >> +
>> >> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable)
>> >> +{
>> >> +    dc_hw_enable_interrupt(&dc->hw, enable);
>> >> +}
>> >> +
>> >> +static u32 calc_factor(u32 src, u32 dest)
>> >> +{
>> >> +    u32 factor = 1 << 16;
>> >> +
>> >> +    if (src > 1 && dest > 1)
>> >> +            factor = ((src - 1) << 16) / (dest - 1);
>> >> +
>> >> +    return factor;
>> >> +}
>> >> +
>> >> +static void update_scale(struct drm_plane_state *state, struct dc_hw_roi *roi,
>> >> +                     struct dc_hw_scale *scale)
>> >> +{
>> >> +    int dst_w = drm_rect_width(&state->dst);
>> >> +    int dst_h = drm_rect_height(&state->dst);
>> >> +    int src_w, src_h, temp;
>> >> +
>> >> +    scale->enable = false;
>> >> +
>> >> +    if (roi->enable) {
>> >> +            src_w = roi->width;
>> >> +            src_h = roi->height;
>> >> +    } else {
>> >> +            src_w = drm_rect_width(&state->src) >> 16;
>> >> +            src_h = drm_rect_height(&state->src) >> 16;
>> >> +    }
>> >> +
>> >> +    if (drm_rotation_90_or_270(state->rotation)) {
>> >> +            temp = src_w;
>> >> +            src_w = src_h;
>> >> +            src_h = temp;
>> >> +    }
>> >> +
>> >> +    if (src_w != dst_w) {
>> >> +            scale->scale_factor_x = calc_factor(src_w, dst_w);
>> >> +            scale->enable = true;
>> >> +    } else {
>> >> +            scale->scale_factor_x = 1 << 16;
>> >> +    }
>> >> +    if (src_h != dst_h) {
>> >> +            scale->scale_factor_y = calc_factor(src_h, dst_h);
>> >> +            scale->enable = true;
>> >> +    } else {
>> >> +            scale->scale_factor_y = 1 << 16;
>> >> +    }
>> >> +}
>> >> +
>> >> +static void update_fb(struct vs_plane *plane, u8 display_id,
>> >> +                  struct dc_hw_fb *fb, struct drm_plane_state *state)
>> >> +{
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> >> +    struct drm_framebuffer *drm_fb = state->fb;
>> >> +    struct drm_rect *src = &state->src;
>> >> +
>> >> +    fb->display_id = display_id;
>> >> +    fb->y_address = plane_state->dma_addr[0];
>> >> +    fb->y_stride = drm_fb->pitches[0];
>> >> +    if (drm_fb->format->format == DRM_FORMAT_YVU420) {
>> >> +            fb->u_address = plane_state->dma_addr[2];
>> >> +            fb->v_address = plane_state->dma_addr[1];
>> >> +            fb->u_stride = drm_fb->pitches[2];
>> >> +            fb->v_stride = drm_fb->pitches[1];
>> >> +    } else {
>> >> +            fb->u_address = plane_state->dma_addr[1];
>> >> +            fb->v_address = plane_state->dma_addr[2];
>> >> +            fb->u_stride = drm_fb->pitches[1];
>> >> +            fb->v_stride = drm_fb->pitches[2];
>> >> +    }
>> >> +    fb->width = drm_rect_width(src) >> 16;
>> >> +    fb->height = drm_rect_height(src) >> 16;
>> >> +    fb->tile_mode = to_vs_tile_mode(drm_fb->modifier);
>> >> +    fb->rotation = to_vs_rotation(state->rotation);
>> >> +    fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding);
>> >> +    fb->zpos = state->zpos;
>> >> +    fb->enable = state->visible;
>> >> +    update_format(drm_fb->format->format, drm_fb->modifier, fb);
>> >> +    update_swizzle(drm_fb->format->format, fb);
>> >> +    update_watermark(plane_state->watermark, fb);
>> >> +    plane_state->status.tile_mode = fb->tile_mode;
>> >> +}
>> >> +
>> >> +static void update_degamma(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                       struct vs_plane_state *plane_state)
>> >> +{
>> >> +    dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
>> >> +    plane_state->degamma_changed = false;
>> >> +}
>> >> +
>> >> +static void update_roi(struct vs_dc *dc, u8 id,
>> >> +                   struct vs_plane_state *plane_state,
>> >> +                   struct dc_hw_roi *roi,
>> >> +                   struct drm_plane_state *state)
>> >> +{
>> >> +    struct drm_vs_roi *data;
>> >> +    struct drm_rect *src = &state->src;
>> >> +    u16 src_w = drm_rect_width(src) >> 16;
>> >> +    u16 src_h = drm_rect_height(src) >> 16;
>> >> +
>> >> +    if (plane_state->roi) {
>> >> +            data = plane_state->roi->data;
>> >> +
>> >> +            if (data->enable) {
>> >> +                    roi->x = data->roi_x;
>> >> +                    roi->y = data->roi_y;
>> >> +                    roi->width = (data->roi_x + data->roi_w > src_w) ?
>> >> +                                             (src_w - data->roi_x) : data->roi_w;
>> >> +                    roi->height = (data->roi_y + data->roi_h > src_h) ?
>> >> +                                              (src_h - data->roi_y) : data->roi_h;
>> >> +                    roi->enable = true;
>> >> +            } else {
>> >> +                    roi->enable = false;
>> >> +            }
>> >> +
>> >> +            dc_hw_update_roi(&dc->hw, id, roi);
>> >> +    } else {
>> >> +            roi->enable = false;
>> >> +    }
>> >> +}
>> >> +
>> >> +static void update_color_mgmt(struct vs_dc *dc, u8 id,
>> >> +                          struct dc_hw_fb *fb,
>> >> +                          struct vs_plane_state *plane_state)
>> >> +{
>> >> +    struct drm_vs_color_mgmt *data;
>> >> +    struct dc_hw_colorkey colorkey;
>> >> +
>> >> +    if (plane_state->color_mgmt) {
>> >> +            data = plane_state->color_mgmt->data;
>> >> +
>> >> +            fb->clear_enable = data->clear_enable;
>> >> +            fb->clear_value = data->clear_value;
>> >> +
>> >> +            if (data->colorkey > data->colorkey_high)
>> >> +                    data->colorkey = data->colorkey_high;
>> >> +
>> >> +            colorkey.colorkey = data->colorkey;
>> >> +            colorkey.colorkey_high = data->colorkey_high;
>> >> +            colorkey.transparency = (data->transparency) ?
>> >> +                            DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE;
>> >> +            dc_hw_update_colorkey(&dc->hw, id, &colorkey);
>> >> +    }
>> >> +}
>> >> +
>> >> +static void update_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                     struct drm_plane *drm_plane,
>> >> +                     struct drm_atomic_state *drm_state)
>> >> +{
>> >> +    struct dc_hw_fb fb = {0};
>> >> +    struct dc_hw_scale scale;
>> >> +    struct dc_hw_position pos;
>> >> +    struct dc_hw_blend blend;
>> >> +    struct dc_hw_roi roi;
>> >> +    struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
>> >> +                                                                       drm_plane);
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> >> +    struct drm_rect *dest = &state->dst;
>> >> +    bool dec_enable = false;
>> >> +    u8 display_id = 0;
>> >> +
>> >> +    display_id = to_vs_display_id(dc, state->crtc);
>> >> +    update_fb(plane, display_id, &fb, state);
>> >> +    fb.dec_enable = dec_enable;
>> >> +
>> >> +    update_roi(dc, plane->id, plane_state, &roi, state);
>> >> +
>> >> +    update_scale(state, &roi, &scale);
>> >> +
>> >> +    if (plane_state->degamma_changed)
>> >> +            update_degamma(dc, plane, plane_state);
>> >> +
>> >> +    pos.start_x = dest->x1;
>> >> +    pos.start_y = dest->y1;
>> >> +    pos.end_x = dest->x2;
>> >> +    pos.end_y = dest->y2;
>> >> +
>> >> +    blend.alpha = (u8)(state->alpha >> 8);
>> >> +    blend.blend_mode = (u8)(state->pixel_blend_mode);
>> >> +
>> >> +    update_color_mgmt(dc, plane->id, &fb, plane_state);
>> >> +
>> >> +    dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
>> >> +}
>> >> +
>> >> +static void update_qos(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                   struct drm_plane *drm_plane,
>> >> +                   struct drm_atomic_state *drm_state)
>> >> +{
>> >> +    struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
>> >> +                                                                       drm_plane);
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> >> +    struct drm_vs_watermark *data;
>> >> +    struct dc_hw_qos qos;
>> >> +
>> >> +    if (plane_state->watermark) {
>> >> +            data = plane_state->watermark->data;
>> >> +
>> >> +            if (data->qos_high) {
>> >> +                    if (data->qos_low > data->qos_high)
>> >> +                            data->qos_low = data->qos_high;
>> >> +
>> >> +                    qos.low_value = data->qos_low & 0x0F;
>> >> +                    qos.high_value = data->qos_high & 0x0F;
>> >> +                    dc_hw_update_qos(&dc->hw, &qos);
>> >> +            }
>> >> +    }
>> >> +}
>> >> +
>> >> +static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor)
>> >> +{
>> >> +    u8 size_type;
>> >> +
>> >> +    switch (state->crtc_w) {
>> >> +    case 32:
>> >> +            size_type = CURSOR_SIZE_32X32;
>> >> +            break;
>> >> +    case 64:
>> >> +            size_type = CURSOR_SIZE_64X64;
>> >> +            break;
>> >> +    default:
>> >> +            size_type = CURSOR_SIZE_32X32;
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    cursor->size = size_type;
>> >> +}
>> >> +
>> >> +static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                            struct drm_plane *drm_plane,
>> >> +                            struct drm_atomic_state *drm_state)
>> >> +{
>> >> +    struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
>> >> +                                                                   drm_plane);
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> >> +    struct drm_framebuffer *drm_fb = state->fb;
>> >> +    struct dc_hw_cursor cursor;
>> >> +
>> >> +    cursor.address = plane_state->dma_addr[0];
>> >> +    cursor.x = state->crtc_x;
>> >> +    cursor.y = state->crtc_y;
>> >> +    cursor.hot_x = drm_fb->hot_x;
>> >> +    cursor.hot_y = drm_fb->hot_y;
>> >> +    cursor.display_id = to_vs_display_id(dc, state->crtc);
>> >> +    update_cursor_size(state, &cursor);
>> >> +    cursor.enable = true;
>> >> +
>> >> +    dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
>> >> +}
>> >> +
>> >> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                    struct drm_plane *drm_plane,
>> >> +                    struct drm_atomic_state *drm_state)
>> >> +{
>> >> +    update_plane(dc, plane, drm_plane, drm_state);
>> >> +    update_qos(dc, plane, drm_plane, drm_state);
>> >> +}
>> >> +
>> >> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                           struct drm_plane *drm_plane,
>> >> +                           struct drm_atomic_state *drm_state)
>> >> +{
>> >> +    update_cursor_plane(dc, plane, drm_plane, drm_state);
>> >> +}
>> >> +
>> >> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                     struct drm_plane_state *old_state)
>> >> +{
>> >> +    struct dc_hw_fb fb = {0};
>> >> +
>> >> +    fb.enable = false;
>> >> +    dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL, NULL, NULL);
>> >> +}
>> >> +
>> >> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                            struct drm_plane_state *old_state)
>> >> +{
>> >> +    struct dc_hw_cursor cursor = {0};
>> >> +
>> >> +    cursor.enable = false;
>> >> +    cursor.display_id = to_vs_display_id(dc, old_state->crtc);
>> >> +    dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
>> >> +}
>> >> +
>> >> +static bool vs_dc_mod_supported(const struct vs_plane_info *plane_info,
>> >> +                            u64 modifier)
>> >> +{
>> >> +    const u64 *mods;
>> >> +
>> >> +    if (!plane_info->modifiers)
>> >> +            return false;
>> >> +
>> >> +    for (mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID; mods++) {
>> >> +            if (*mods == modifier)
>> >> +                    return true;
>> >> +    }
>> >> +
>> >> +    return false;
>> >> +}
>> >> +
>> >> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
>> >> +                  struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> >> +                                                                             plane);
>> >> +
>> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
>> >> +    const struct vs_plane_info *plane_info;
>> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
>> >> +    struct drm_crtc_state *crtc_state;
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +
>> >> +    plane_info = &dc->hw.info->planes[vs_plane->id];
>> >> +
>> >> +    if (fb->width < plane_info->min_width ||
>> >> +        fb->width > plane_info->max_width ||
>> >> +        fb->height < plane_info->min_height ||
>> >> +        fb->height > plane_info->max_height)
>> >> +            drm_err_once(plane->dev, "buffer size may not support on plane%d.\n",
>> >> +                         vs_plane->id);
>> >> +
>> >> +    if (!vs_dc_mod_supported(plane_info, fb->modifier)) {
>> >> +            drm_err(plane->dev, "unsupported modifier on plane%d.\n", vs_plane->id);
>> >> +            return -EINVAL;
>> >> +    }
>> >> +
>> >> +    crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
>> >> +    return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
>> >> +                                              plane_info->min_scale,
>> >> +                                              plane_info->max_scale,
>> >> +                                              true, true);
>> >> +}
>> >> +
>> >> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
>> >> +                         struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> >> +                                                                       plane);
>> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
>> >> +    const struct vs_plane_info *plane_info;
>> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
>> >> +    struct drm_crtc_state *crtc_state;
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +
>> >> +    plane_info = &dc->hw.info->planes[vs_plane->id];
>> >> +
>> >> +    if (fb->width < plane_info->min_width ||
>> >> +        fb->width > plane_info->max_width ||
>> >> +        fb->height < plane_info->min_height ||
>> >> +        fb->height > plane_info->max_height)
>> >> +            drm_err_once(plane->dev, "buffer size may not support on plane%d.\n", vs_plane->id);
>> >> +
>> >> +    crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
>> >> +    if (IS_ERR(crtc_state))
>> >> +            return -EINVAL;
>> >> +
>> >> +    return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
>> >> +                                            plane_info->min_scale,
>> >> +                                            plane_info->max_scale,
>> >> +                                            true, true);
>> >> +}
>> >> +
>> >> +static void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
>> >> +{
>> >> +    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
>> >> +
>> >> +    drm_crtc_handle_vblank(crtc);
>> >> +
>> >> +    vs_crtc_state->underflow = underflow;
>> >> +}
>> >> +
>> >> +static irqreturn_t dc_isr(int irq, void *data)
>> >> +{
>> >> +    struct vs_dc *dc = data;
>> >> +    struct vs_dc_info *dc_info = dc->hw.info;
>> >> +    u32 i, ret;
>> >> +
>> >> +    ret = dc_hw_get_interrupt(&dc->hw);
>> >> +
>> >> +    for (i = 0; i < dc_info->panel_num; i++)
>> >> +            vs_crtc_handle_vblank(&dc->crtc[i]->base, dc_hw_check_underflow(&dc->hw));
>> >> +
>> >> +    return IRQ_HANDLED;
>> >> +}
>> >> +
>> >> +void vs_dc_commit(struct vs_dc *dc)
>> >> +{
>> >> +    dc_hw_enable_shadow_register(&dc->hw, false);
>> >> +
>> >> +    dc_hw_commit(&dc->hw);
>> >> +
>> >> +    if (dc->first_frame)
>> >> +            dc->first_frame = false;
>> >> +
>> >> +    dc_hw_enable_shadow_register(&dc->hw, true);
>> >> +}
>> >> +
>> >> +static int dc_bind(struct device *dev, struct device *master, void *data)
>> >> +{
>> >> +    struct drm_device *drm_dev = data;
>> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
>> >> +    struct device_node *port;
>> >> +    struct vs_crtc *crtc;
>> >> +    struct vs_dc_info *dc_info;
>> >> +    struct vs_plane *plane;
>> >> +    struct vs_plane_info *plane_info;
>> >> +    int i, ret;
>> >> +    u32 ctrc_mask = 0;
>> >> +
>> >> +    if (!drm_dev || !dc) {
>> >> +            dev_err(dev, "devices are not created.\n");
>> >> +            return -ENODEV;
>> >> +    }
>> >> +
>> >> +    ret = dc_init(dev);
>> >> +    if (ret < 0) {
>> >> +            drm_err(drm_dev, "Failed to initialize DC hardware.\n");
>> >> +            return ret;
>> >> +    }
>> >> +
>> >> +    port = of_get_child_by_name(dev->of_node, "port");
>> >> +    if (!port) {
>> >> +            drm_err(drm_dev, "no port node found\n");
>> >> +            return -ENODEV;
>> >> +    }
>> >> +    of_node_put(port);
>> >> +
>> >> +    dc_info = dc->hw.info;
>> >> +
>> >> +    for (i = 0; i < dc_info->panel_num; i++) {
>> >> +            crtc = vs_crtc_create(drm_dev, dc_info);
>> >> +            if (!crtc) {
>> >> +                    drm_err(drm_dev, "Failed to create CRTC.\n");
>> >> +                    ret = -ENOMEM;
>> >> +                    return ret;
>> >> +            }
>> >> +
>> >> +            crtc->base.port = port;
>> >> +            crtc->dev = dev;
>> >> +            dc->crtc[i] = crtc;
>> >> +            ctrc_mask |= drm_crtc_mask(&crtc->base);
>> >> +    }
>> >> +
>> >> +    for (i = 0; i < dc_info->plane_num; i++) {
>> >> +            plane_info = (struct vs_plane_info *)&dc_info->planes[i];
>> >> +
>> >> +            if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
>> >> +                    plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
>> >> +                                            drm_crtc_mask(&dc->crtc[0]->base));
>> >> +            } else if (!strcmp(plane_info->name, "Primary_1") ||
>> >> +                               !strcmp(plane_info->name, "Cursor_1")) {
>> >> +                    plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
>> >> +                                            drm_crtc_mask(&dc->crtc[1]->base));
>> >
>> > I'd say, it's not typical to use name to distinguish objects. Maybe you
>> > can add indices to the info structure and drop the names instead?
>> >
>> >
>> >> +            } else {
>> >> +                    plane = vs_plane_create(drm_dev, plane_info,
>> >> +                                            dc_info->layer_num, ctrc_mask);
>> >> +            }
>> >> +
>> >> +            if (IS_ERR(plane)) {
>> >> +                    dev_err(dev, "failed to construct plane\n");
>> >> +                    return PTR_ERR(plane);
>> >> +            }
>> >> +
>> >> +            plane->id = i;
>> >> +            dc->planes[i].id = plane_info->id;
>> >> +
>> >> +            if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
>> >> +                    if (!strcmp(plane_info->name, "Primary"))
>> >> +                            dc->crtc[0]->base.primary = &plane->base;
>> >> +                    else
>> >> +                            dc->crtc[1]->base.primary = &plane->base;
>> >
>> > This definitely urges for an index in the plane info / plane_state.
>> >
>> >> +                    drm_dev->mode_config.min_width = plane_info->min_width;
>> >> +                    drm_dev->mode_config.min_height =
>> >> +                                                    plane_info->min_height;
>> >> +                    drm_dev->mode_config.max_width = plane_info->max_width;
>> >> +                    drm_dev->mode_config.max_height =
>> >> +                                                    plane_info->max_height;
>> >> +            }
>> >> +
>> >> +            if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
>> >> +                    if (!strcmp(plane_info->name, "Cursor"))
>> >> +                            dc->crtc[0]->base.cursor = &plane->base;
>> >> +                    else
>> >> +                            dc->crtc[1]->base.cursor = &plane->base;
>> >> +                    drm_dev->mode_config.cursor_width =
>> >> +                                                    plane_info->max_width;
>> >> +                    drm_dev->mode_config.cursor_height =
>> >> +                                                    plane_info->max_height;
>> >> +            }
>> >> +    }
>> >> +
>> >> +    vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static void dc_unbind(struct device *dev, struct device *master, void *data)
>> >> +{
>> >> +    dc_deinit(dev);
>> >> +}
>> >> +
>> >> +const struct component_ops dc_component_ops = {
>> >> +    .bind = dc_bind,
>> >> +    .unbind = dc_unbind,
>> >> +};
>> >> +
>> >> +static const struct of_device_id dc_driver_dt_match[] = {
>> >> +    { .compatible = "starfive,jh7110-dc8200", },
>> >> +    {},
>> >> +};
>> >> +MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
>> >> +
>> >> +static int dc_probe(struct platform_device *pdev)
>> >> +{
>> >> +    struct device *dev = &pdev->dev;
>> >> +    struct vs_dc *dc;
>> >> +    int irq, ret, i;
>> >> +
>> >> +    dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
>> >> +    if (!dc)
>> >> +            return -ENOMEM;
>> >> +
>> >> +    dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
>> >> +    if (IS_ERR(dc->hw.hi_base))
>> >> +            return PTR_ERR(dc->hw.hi_base);
>> >> +
>> >> +    dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
>> >> +    if (IS_ERR(dc->hw.reg_base))
>> >> +            return PTR_ERR(dc->hw.reg_base);
>> >> +
>> >> +    dc->nclks = ARRAY_SIZE(dc->clk_vout);
>> >> +    for (i = 0; i < dc->nclks; ++i)
>> >> +            dc->clk_vout[i].id = vout_clocks[i];
>> >> +    ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
>> >> +    if (ret) {
>> >> +            dev_err(dev, "Failed to get clk controls\n");
>> >> +            return ret;
>> >> +    }
>> >> +
>> >> +    dc->nrsts = ARRAY_SIZE(dc->rst_vout);
>> >> +    for (i = 0; i < dc->nrsts; ++i)
>> >> +            dc->rst_vout[i].id = vout_resets[i];
>> >> +    ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
>> >> +                                             dc->rst_vout);
>> >> +    if (ret) {
>> >> +            dev_err(dev, "Failed to get reset controls\n");
>> >> +            return ret;
>> >> +    }
>> >> +
>> >> +    irq = platform_get_irq(pdev, 0);
>> >> +
>> >> +    ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
>> >
>> > Are you ready to handle the IRQ at this point?
Do you have some good suggestions?
Are there any hidden dangers in doing so?
Hope to get good opinions 

>> >
>> >> +    if (ret < 0) {
>> >> +            dev_err(dev, "Failed to install irq:%u.\n", irq);
>> >> +            return ret;
>> >> +    }
>> >> +
>> >> +    dev_set_drvdata(dev, dc);
>> >> +
>> >> +    return component_add(dev, &dc_component_ops);
>> >> +}
>> >> +
>> >> +static int dc_remove(struct platform_device *pdev)
>> >> +{
>> >> +    struct device *dev = &pdev->dev;
>> >> +
>> >> +    component_del(dev, &dc_component_ops);
>> >> +
>> >> +    dev_set_drvdata(dev, NULL);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +struct platform_driver dc_platform_driver = {
>> >> +    .probe = dc_probe,
>> >> +    .remove = dc_remove,
>> >> +    .driver = {
>> >> +            .name = "vs-dc",
>> >> +            .of_match_table = of_match_ptr(dc_driver_dt_match),
>> >> +    },
>> >> +};
>> >> +
>> >> +MODULE_AUTHOR("StarFive Corporation");
>> >> +MODULE_DESCRIPTION("VeriSilicon DC Driver");
>> >> +MODULE_LICENSE("GPL");
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
>> >> new file mode 100644
>> >> index 000000000..8e96fd32c
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc.h
>> >> @@ -0,0 +1,80 @@
>> >> +/* SPDX-License-Identifier: GPL-2.0 */
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#ifndef __VS_DC_H__
>> >> +#define __VS_DC_H__
>> >> +
>> >> +#include <linux/clk.h>
>> >> +#include <linux/mm_types.h>
>> >> +#include <linux/reset.h>
>> >> +
>> >> +#include <drm/drm_fourcc.h>
>> >> +#include <drm/drm_modes.h>
>> >> +
>> >> +#include "vs_crtc.h"
>> >> +#include "vs_dc_hw.h"
>> >> +#include "vs_plane.h"
>> >> +
>> >> +struct vs_dc_plane {
>> >> +    enum dc_hw_plane_id id;
>> >> +};
>> >> +
>> >> +enum vout_clk {
>> >> +    CLK_VOUT_NOC_DISP = 0,
>> >> +    CLK_VOUT_PIX0,
>> >> +    CLK_VOUT_PIX1,
>> >> +    CLK_VOUT_CORE,
>> >> +    CLK_VOUT_AXI,
>> >> +    CLK_VOUT_AHB,
>> >> +    CLK_VOUT_HDMI_PIX,
>> >> +    CLK_VOUT_SOC_PIX,
>> >> +    CLK_VOUT_NUM
>> >> +};
>> >> +
>> >> +enum rst_vout {
>> >> +    RST_VOUT_AXI = 0,
>> >> +    RST_VOUT_AHB,
>> >> +    RST_VOUT_CORE,
>> >> +    RST_VOUT_NUM
>> >> +};
>> >> +
>> >> +struct vs_dc {
>> >> +    struct vs_crtc          *crtc[DC_DISPLAY_NUM];
>> >> +    struct dc_hw            hw;
>> >> +    bool                    first_frame;
>> >> +
>> >> +    struct vs_dc_plane      planes[PLANE_NUM];
>> >> +    struct clk_bulk_data    clk_vout[CLK_VOUT_NUM];
>> >> +    int                     nclks;
>> >> +    struct reset_control_bulk_data rst_vout[RST_VOUT_NUM];
>> >> +    int                     nrsts;
>> >> +};
>> >> +
>> >> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc);
>> >> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc);
>> >> +
>> >> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
>> >> +                 struct drm_color_lut *lut, unsigned int size);
>> >> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc, bool enable);
>> >> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable);
>> >> +void vs_dc_commit(struct vs_dc *dc);
>> >> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                    struct drm_plane *drm_plane,
>> >> +                    struct drm_atomic_state *drm_state);
>> >> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                     struct drm_plane_state *old_state);
>> >> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
>> >> +                  struct drm_atomic_state *state);
>> >> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                           struct drm_plane *drm_plane,
>> >> +                           struct drm_atomic_state *drm_state);
>> >> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                            struct drm_plane_state *old_state);
>> >> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
>> >> +                         struct drm_atomic_state *state);
>> >> +
>> >> +extern struct platform_driver dc_platform_driver;
>> >> +
>> >> +#endif /* __VS_DC_H__ */
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
>> >> new file mode 100644
>> >> index 000000000..e5acb7eb4
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
>> >> @@ -0,0 +1,1959 @@
>> >> +// SPDX-License-Identifier: GPL-2.0
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#include <linux/bits.h>
>> >> +#include <linux/io.h>
>> >> +#include <linux/media-bus-format.h>
>> >> +#include <drm/drm_atomic_helper.h>
>> >> +#include <drm/drm_blend.h>
>> >> +#include <drm/drm_fourcc.h>
>> >> +#include <drm/vs_drm.h>
>> >> +
>> >> +#include "vs_dc_hw.h"
>> >> +#include "vs_type.h"
>> >> +
>> >> +static const u32 horkernel[] = {
>> >> +    0x00000000, 0x20000000, 0x00002000, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x2b981468,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x10f00000,
>> >> +    0x00002f10, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x3781087f, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x06660000, 0x0000399a, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x01470000,
>> >> +    0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x00004000, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x20002000, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x27e1181f,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x14680000,
>> >> +    0x00002b98, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x2f1010f0, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x0dc70000, 0x00003239, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x087f0000, 0x00003781, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x399a0666, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x3cc4033c,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x021f0000,
>> >> +};
>> >> +
>> >> +#define H_COEF_SIZE (sizeof(horkernel) / sizeof(u32))
>> >> +
>> >> +static const u32 verkernel[] = {
>> >> +    0x00000000, 0x20000000, 0x00002000, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x2b981468,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x10f00000,
>> >> +    0x00002f10, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x3781087f, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x06660000, 0x0000399a, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x01470000,
>> >> +    0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x00004000, 0x00000000,
>> >> +    0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab,
>> >> +    0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
>> >> +    0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +};
>> >> +
>> >> +#define V_COEF_SIZE (sizeof(verkernel) / sizeof(u32))
>> >> +
>> >> +/*
>> >> + * RGB 709->2020 conversion parameters
>> >> + */
>> >> +static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = {
>> >> +    10279,  5395,   709,
>> >> +    1132,   15065,  187,
>> >> +    269,    1442,   14674
>> >> +};
>> >> +
>> >> +/*
>> >> + * YUV601 to RGB conversion parameters
>> >> + * YUV2RGB[0]  - [8] : C0 - C8;
>> >> + * YUV2RGB[9]  - [11]: D0 - D2;
>> >> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
>> >> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
>> >> + */
>> >> +static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
>> >> +    1196,   0,                      1640,   1196,
>> >> +    -404,   -836,           1196,   2076,
>> >> +    0,              -916224,        558336, -1202944,
>> >> +    64,              940,           64,             960
>> >> +};
>> >> +
>> >> +/*
>> >> + * YUV709 to RGB conversion parameters
>> >> + * YUV2RGB[0]  - [8] : C0 - C8;
>> >> + * YUV2RGB[9]  - [11]: D0 - D2;
>> >> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
>> >> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
>> >> + */
>> >> +static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
>> >> +    1196,           0,              1844,   1196,
>> >> +    -220,           -548,   1196,   2172,
>> >> +    0,                      -1020672, 316672,  -1188608,
>> >> +    64,                     940,            64,             960
>> >> +};
>> >> +
>> >> +/*
>> >> + * YUV2020 to RGB conversion parameters
>> >> + * YUV2RGB[0]  - [8] : C0 - C8;
>> >> + * YUV2RGB[9]  - [11]: D0 - D2;
>> >> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
>> >> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
>> >> + */
>> >> +static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
>> >> +    1196, 0, 1724, 1196,
>> >> +    -192, -668, 1196, 2200,
>> >> +    0, -959232, 363776, -1202944,
>> >> +    64, 940, 64, 960
>> >> +};
>> >> +
>> >> +/*
>> >> + * RGB to YUV2020 conversion parameters
>> >> + * RGB2YUV[0] - [8] : C0 - C8;
>> >> + * RGB2YUV[9] - [11]: D0 - D2;
>> >> + */
>> >> +static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = {
>> >> +    230,    594,    52,
>> >> +    -125,  -323,    448,
>> >> +    448,   -412,   -36,
>> >> +    64,             512,    512
>> >> +};
>> >> +
>> >> +/*
>> >> + * Degamma table for 709 color space data.
>> >> + */
>> >> +static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
>> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005,
>> >> +    0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024,
>> >> +    0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064,
>> >> +    0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce,
>> >> +    0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166,
>> >> +    0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230,
>> >> +    0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331,
>> >> +    0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c,
>> >> +    0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
>> >> +    0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d,
>> >> +    0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999,
>> >> +    0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb,
>> >> +    0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65,
>> >> +    0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a,
>> >> +    0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b,
>> >> +    0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc,
>> >> +    0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e,
>> >> +    0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
>> >> +    0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e,
>> >> +    0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf,
>> >> +    0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea,
>> >> +    0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f,
>> >> +    0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930,
>> >> +    0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f,
>> >> +    0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee,
>> >> +    0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde,
>> >> +    0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
>> >> +    0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9,
>> >> +    0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406,
>> >> +    0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a,
>> >> +    0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576,
>> >> +    0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd,
>> >> +    0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
>> >> +};
>> >> +
>> >> +/*
>> >> + * Degamma table for 2020 color space data.
>> >> + */
>> >> +static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
>> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
>> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
>> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
>> >> +    0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
>> >> +    0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
>> >> +    0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006,
>> >> +    0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a,
>> >> +    0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f,
>> >> +    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
>> >> +    0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024,
>> >> +    0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035,
>> >> +    0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d,
>> >> +    0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d,
>> >> +    0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098,
>> >> +    0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3,
>> >> +    0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123,
>> >> +    0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d,
>> >> +    0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
>> >> +    0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6,
>> >> +    0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1,
>> >> +    0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d,
>> >> +    0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6,
>> >> +    0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920,
>> >> +    0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a,
>> >> +    0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033,
>> >> +    0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593,
>> >> +    0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
>> >> +    0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656,
>> >> +    0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332,
>> >> +    0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480,
>> >> +    0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1,
>> >> +    0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99,
>> >> +    0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
>> >> +};
>> >> +
>> >> +/* one is for primary plane and the other is for all overlay planes */
>> >> +static const struct dc_hw_plane_reg dc_plane_reg[] = {
>> >> +    {
>> >> +    .y_address              = DC_FRAMEBUFFER_ADDRESS,
>> >> +    .u_address              = DC_FRAMEBUFFER_U_ADDRESS,
>> >> +    .v_address              = DC_FRAMEBUFFER_V_ADDRESS,
>> >> +    .y_stride               = DC_FRAMEBUFFER_STRIDE,
>> >> +    .u_stride               = DC_FRAMEBUFFER_U_STRIDE,
>> >> +    .v_stride               = DC_FRAMEBUFFER_V_STRIDE,
>> >> +    .size                   = DC_FRAMEBUFFER_SIZE,
>> >> +    .top_left               = DC_FRAMEBUFFER_TOP_LEFT,
>> >> +    .bottom_right   = DC_FRAMEBUFFER_BOTTOM_RIGHT,
>> >> +    .scale_factor_x                 = DC_FRAMEBUFFER_SCALE_FACTOR_X,
>> >> +    .scale_factor_y                 = DC_FRAMEBUFFER_SCALE_FACTOR_Y,
>> >> +    .h_filter_coef_index    = DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
>> >> +    .h_filter_coef_data             = DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
>> >> +    .v_filter_coef_index    = DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
>> >> +    .v_filter_coef_data             = DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
>> >> +    .init_offset                    = DC_FRAMEBUFFER_INIT_OFFSET,
>> >> +    .color_key                              = DC_FRAMEBUFFER_COLOR_KEY,
>> >> +    .color_key_high                 = DC_FRAMEBUFFER_COLOR_KEY_HIGH,
>> >> +    .clear_value                    = DC_FRAMEBUFFER_CLEAR_VALUE,
>> >> +    .color_table_index              = DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
>> >> +    .color_table_data               = DC_FRAMEBUFFER_COLOR_TABLE_DATA,
>> >> +    .scale_config                   = DC_FRAMEBUFFER_SCALE_CONFIG,
>> >> +    .water_mark                             = DC_FRAMEBUFFER_WATER_MARK,
>> >> +    .degamma_index                  = DC_FRAMEBUFFER_DEGAMMA_INDEX,
>> >> +    .degamma_data                   = DC_FRAMEBUFFER_DEGAMMA_DATA,
>> >> +    .degamma_ex_data                = DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
>> >> +    .src_global_color               = DC_FRAMEBUFFER_SRC_GLOBAL_COLOR,
>> >> +    .dst_global_color               = DC_FRAMEBUFFER_DST_GLOBAL_COLOR,
>> >> +    .blend_config                   = DC_FRAMEBUFFER_BLEND_CONFIG,
>> >> +    .roi_origin                             = DC_FRAMEBUFFER_ROI_ORIGIN,
>> >> +    .roi_size                               = DC_FRAMEBUFFER_ROI_SIZE,
>> >> +    .yuv_to_rgb_coef0                       = DC_FRAMEBUFFER_YUVTORGB_COEF0,
>> >> +    .yuv_to_rgb_coef1                       = DC_FRAMEBUFFER_YUVTORGB_COEF1,
>> >> +    .yuv_to_rgb_coef2                       = DC_FRAMEBUFFER_YUVTORGB_COEF2,
>> >> +    .yuv_to_rgb_coef3                       = DC_FRAMEBUFFER_YUVTORGB_COEF3,
>> >> +    .yuv_to_rgb_coef4                       = DC_FRAMEBUFFER_YUVTORGB_COEF4,
>> >> +    .yuv_to_rgb_coefd0                      = DC_FRAMEBUFFER_YUVTORGB_COEFD0,
>> >> +    .yuv_to_rgb_coefd1                      = DC_FRAMEBUFFER_YUVTORGB_COEFD1,
>> >> +    .yuv_to_rgb_coefd2                      = DC_FRAMEBUFFER_YUVTORGB_COEFD2,
>> >> +    .y_clamp_bound                          = DC_FRAMEBUFFER_Y_CLAMP_BOUND,
>> >> +    .uv_clamp_bound                         = DC_FRAMEBUFFER_UV_CLAMP_BOUND,
>> >> +    .rgb_to_rgb_coef0                       = DC_FRAMEBUFFER_RGBTORGB_COEF0,
>> >> +    .rgb_to_rgb_coef1                       = DC_FRAMEBUFFER_RGBTORGB_COEF1,
>> >> +    .rgb_to_rgb_coef2                       = DC_FRAMEBUFFER_RGBTORGB_COEF2,
>> >> +    .rgb_to_rgb_coef3                       = DC_FRAMEBUFFER_RGBTORGB_COEF3,
>> >> +    .rgb_to_rgb_coef4                       = DC_FRAMEBUFFER_RGBTORGB_COEF4,
>> >> +    },
>> >> +    {
>> >> +    .y_address              = DC_OVERLAY_ADDRESS,
>> >> +    .u_address              = DC_OVERLAY_U_ADDRESS,
>> >> +    .v_address              = DC_OVERLAY_V_ADDRESS,
>> >> +    .y_stride               = DC_OVERLAY_STRIDE,
>> >> +    .u_stride               = DC_OVERLAY_U_STRIDE,
>> >> +    .v_stride               = DC_OVERLAY_V_STRIDE,
>> >> +    .size                   = DC_OVERLAY_SIZE,
>> >> +    .top_left               = DC_OVERLAY_TOP_LEFT,
>> >> +    .bottom_right   = DC_OVERLAY_BOTTOM_RIGHT,
>> >> +    .scale_factor_x = DC_OVERLAY_SCALE_FACTOR_X,
>> >> +    .scale_factor_y = DC_OVERLAY_SCALE_FACTOR_Y,
>> >> +    .h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
>> >> +    .h_filter_coef_data  = DC_OVERLAY_H_FILTER_COEF_DATA,
>> >> +    .v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
>> >> +    .v_filter_coef_data  = DC_OVERLAY_V_FILTER_COEF_DATA,
>> >> +    .init_offset             = DC_OVERLAY_INIT_OFFSET,
>> >> +    .color_key                       = DC_OVERLAY_COLOR_KEY,
>> >> +    .color_key_high                 = DC_OVERLAY_COLOR_KEY_HIGH,
>> >> +    .clear_value             = DC_OVERLAY_CLEAR_VALUE,
>> >> +    .color_table_index       = DC_OVERLAY_COLOR_TABLE_INDEX,
>> >> +    .color_table_data        = DC_OVERLAY_COLOR_TABLE_DATA,
>> >> +    .scale_config            = DC_OVERLAY_SCALE_CONFIG,
>> >> +    .water_mark                             = DC_OVERLAY_WATER_MARK,
>> >> +    .degamma_index           = DC_OVERLAY_DEGAMMA_INDEX,
>> >> +    .degamma_data            = DC_OVERLAY_DEGAMMA_DATA,
>> >> +    .degamma_ex_data         = DC_OVERLAY_DEGAMMA_EX_DATA,
>> >> +    .src_global_color        = DC_OVERLAY_SRC_GLOBAL_COLOR,
>> >> +    .dst_global_color        = DC_OVERLAY_DST_GLOBAL_COLOR,
>> >> +    .blend_config            = DC_OVERLAY_BLEND_CONFIG,
>> >> +    .roi_origin                             = DC_OVERLAY_ROI_ORIGIN,
>> >> +    .roi_size                               = DC_OVERLAY_ROI_SIZE,
>> >> +    .yuv_to_rgb_coef0                = DC_OVERLAY_YUVTORGB_COEF0,
>> >> +    .yuv_to_rgb_coef1                = DC_OVERLAY_YUVTORGB_COEF1,
>> >> +    .yuv_to_rgb_coef2                = DC_OVERLAY_YUVTORGB_COEF2,
>> >> +    .yuv_to_rgb_coef3                = DC_OVERLAY_YUVTORGB_COEF3,
>> >> +    .yuv_to_rgb_coef4                       = DC_OVERLAY_YUVTORGB_COEF4,
>> >> +    .yuv_to_rgb_coefd0                      = DC_OVERLAY_YUVTORGB_COEFD0,
>> >> +    .yuv_to_rgb_coefd1                      = DC_OVERLAY_YUVTORGB_COEFD1,
>> >> +    .yuv_to_rgb_coefd2                      = DC_OVERLAY_YUVTORGB_COEFD2,
>> >> +    .y_clamp_bound           = DC_OVERLAY_Y_CLAMP_BOUND,
>> >> +    .uv_clamp_bound          = DC_OVERLAY_UV_CLAMP_BOUND,
>> >> +    .rgb_to_rgb_coef0                = DC_OVERLAY_RGBTORGB_COEF0,
>> >> +    .rgb_to_rgb_coef1                = DC_OVERLAY_RGBTORGB_COEF1,
>> >> +    .rgb_to_rgb_coef2                = DC_OVERLAY_RGBTORGB_COEF2,
>> >> +    .rgb_to_rgb_coef3                = DC_OVERLAY_RGBTORGB_COEF3,
>> >> +    .rgb_to_rgb_coef4                = DC_OVERLAY_RGBTORGB_COEF4,
>> >> +    },
>> >> +};
>> >> +
>> >> +static const u32 primary_overlay_format0[] = {
>> >> +    DRM_FORMAT_XRGB4444,
>> >> +    DRM_FORMAT_XBGR4444,
>> >> +    DRM_FORMAT_RGBX4444,
>> >> +    DRM_FORMAT_BGRX4444,
>> >> +    DRM_FORMAT_ARGB4444,
>> >> +    DRM_FORMAT_ABGR4444,
>> >> +    DRM_FORMAT_RGBA4444,
>> >> +    DRM_FORMAT_BGRA4444,
>> >> +    DRM_FORMAT_XRGB1555,
>> >> +    DRM_FORMAT_XBGR1555,
>> >> +    DRM_FORMAT_RGBX5551,
>> >> +    DRM_FORMAT_BGRX5551,
>> >> +    DRM_FORMAT_ARGB1555,
>> >> +    DRM_FORMAT_ABGR1555,
>> >> +    DRM_FORMAT_RGBA5551,
>> >> +    DRM_FORMAT_BGRA5551,
>> >> +    DRM_FORMAT_RGB565,
>> >> +    DRM_FORMAT_BGR565,
>> >> +    DRM_FORMAT_XRGB8888,
>> >> +    DRM_FORMAT_XBGR8888,
>> >> +    DRM_FORMAT_RGBX8888,
>> >> +    DRM_FORMAT_BGRX8888,
>> >> +    DRM_FORMAT_ARGB8888,
>> >> +    DRM_FORMAT_ABGR8888,
>> >> +    DRM_FORMAT_RGBA8888,
>> >> +    DRM_FORMAT_BGRA8888,
>> >> +    DRM_FORMAT_ARGB2101010,
>> >> +    DRM_FORMAT_ABGR2101010,
>> >> +    DRM_FORMAT_RGBA1010102,
>> >> +    DRM_FORMAT_BGRA1010102,
>> >> +    DRM_FORMAT_YUYV,
>> >> +    DRM_FORMAT_YVYU,
>> >> +    DRM_FORMAT_UYVY,
>> >> +    DRM_FORMAT_VYUY,
>> >> +    DRM_FORMAT_YVU420,
>> >> +    DRM_FORMAT_YUV420,
>> >> +    DRM_FORMAT_NV12,
>> >> +    DRM_FORMAT_NV21,
>> >> +    DRM_FORMAT_NV16,
>> >> +    DRM_FORMAT_NV61,
>> >> +    DRM_FORMAT_P010,
>> >> +};
>> >> +
>> >> +static const u32 primary_overlay_format1[] = {
>> >> +    DRM_FORMAT_XRGB8888,
>> >> +    DRM_FORMAT_XBGR8888,
>> >> +    DRM_FORMAT_RGBX8888,
>> >> +    DRM_FORMAT_BGRX8888,
>> >> +    DRM_FORMAT_ARGB8888,
>> >> +    DRM_FORMAT_ABGR8888,
>> >> +    DRM_FORMAT_RGBA8888,
>> >> +    DRM_FORMAT_BGRA8888,
>> >> +    DRM_FORMAT_ARGB2101010,
>> >> +    DRM_FORMAT_ABGR2101010,
>> >> +    DRM_FORMAT_RGBA1010102,
>> >> +    DRM_FORMAT_BGRA1010102,
>> >> +    DRM_FORMAT_NV12,
>> >> +    DRM_FORMAT_NV21,
>> >> +    DRM_FORMAT_YUV444,
>> >> +};
>> >> +
>> >> +static const u32 cursor_formats[] = {
>> >> +    DRM_FORMAT_ARGB8888
>> >> +};
>> >> +
>> >> +static const u64 format_modifier0[] = {
>> >> +    DRM_FORMAT_MOD_LINEAR,
>> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8),
>> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8),
>> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X8),
>> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X4),
>> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4),
>> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8),
>> >> +    DRM_FORMAT_MOD_INVALID
>> >> +};
>> >> +
>> >> +static const u64 secondary_format_modifiers[] = {
>> >> +    DRM_FORMAT_MOD_LINEAR,
>> >> +    DRM_FORMAT_MOD_INVALID
>> >> +};
>> >> +
>> >> +#define FRAC_16_16(mult, div)        (((mult) << 16) / (div))
>> >> +
>> >> +static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = {
>> >> +    {
>> >> +            /* DC_REV_0 */
>> >> +            {
>> >> +            .name                   = "Primary",
>> >
>> > I'd say that the indentation is strange here.
>> > It might be more scalable to split a single array into per-generation
>> > data structures and reference them directly from the info struct.
yes 
Use plat_data to reference
>> >
>> >> +            .id                             = PRIMARY_PLANE_0,
>> >> +            .type                   = DRM_PLANE_TYPE_PRIMARY,
>> >> +            .num_formats    = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats                = primary_overlay_format0,
>> >> +            .num_modifiers  = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers              = format_modifier0,
>> >> +            .min_width              = 0,
>> >> +            .min_height             = 0,
>> >> +            .max_width              = 4096,
>> >> +            .max_height             = 4096,
>> >> +            .rotation               = DRM_MODE_ROTATE_0 |
>> >> +                                                    DRM_MODE_ROTATE_90 |
>> >> +                                                    DRM_MODE_ROTATE_180 |
>> >> +                                                    DRM_MODE_ROTATE_270 |
>> >> +                                                    DRM_MODE_REFLECT_X |
>> >> +                                                    DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode             = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                                    BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                                    BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 0,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name                   = "Overlay",
>> >> +            .id                             = OVERLAY_PLANE_0,
>> >> +            .type                   = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats    = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats                = primary_overlay_format0,
>> >> +            .num_modifiers  = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers              = format_modifier0,
>> >> +            .min_width              = 0,
>> >> +            .min_height             = 0,
>> >> +            .max_width              = 4096,
>> >> +            .max_height             = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 1,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name                   = "Overlay_1",
>> >> +            .id                             = OVERLAY_PLANE_1,
>> >> +            .type                   = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats    = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats                = primary_overlay_format0,
>> >> +            .num_modifiers  = ARRAY_SIZE(secondary_format_modifiers),
>> >> +            .modifiers              = secondary_format_modifiers,
>> >> +            .min_width              = 0,
>> >> +            .min_height             = 0,
>> >> +            .max_width              = 4096,
>> >> +            .max_height             = 4096,
>> >> +            .rotation               = 0,
>> >> +            .blend_mode             = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                                    BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                                    BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 2,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Primary_1",
>> >> +            .id                             = PRIMARY_PLANE_1,
>> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats         = primary_overlay_format0,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 3,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay_2",
>> >> +            .id                             = OVERLAY_PLANE_2,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats         = primary_overlay_format0,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 4,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay_3",
>> >> +            .id                     = OVERLAY_PLANE_3,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats         = primary_overlay_format0,
>> >> +            .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
>> >> +            .modifiers       = secondary_format_modifiers,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = 0,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 5,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Cursor",
>> >> +            .id                             = CURSOR_PLANE_0,
>> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
>> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
>> >> +            .formats         = cursor_formats,
>> >> +            .num_modifiers = 0,
>> >> +            .modifiers       = NULL,
>> >> +            .min_width       = 32,
>> >> +            .min_height  = 32,
>> >> +            .max_width       = 64,
>> >> +            .max_height  = 64,
>> >> +            .rotation        = 0,
>> >> +            .degamma_size = 0,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 255,
>> >> +            .watermark       = false,
>> >> +            .color_mgmt  = false,
>> >> +            .roi             = false,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Cursor_1",
>> >> +            .id                             = CURSOR_PLANE_1,
>> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
>> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
>> >> +            .formats         = cursor_formats,
>> >> +            .num_modifiers = 0,
>> >> +            .modifiers       = NULL,
>> >> +            .min_width       = 32,
>> >> +            .min_height  = 32,
>> >> +            .max_width       = 64,
>> >> +            .max_height  = 64,
>> >> +            .rotation        = 0,
>> >> +            .degamma_size = 0,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 255,
>> >> +            .watermark       = false,
>> >> +            .color_mgmt  = false,
>> >> +            .roi             = false,
>> >> +            },
>> >> +    },
>> >> +    {
>> >> +            /* DC_REV_1 */
>> >> +            {
>> >> +            .name            = "Primary",
>> >> +            .id                             = PRIMARY_PLANE_0,
>> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats         = primary_overlay_format0,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 0,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay",
>> >> +            .id                             = OVERLAY_PLANE_0,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats         = primary_overlay_format0,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 1,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Primary_1",
>> >> +            .id                             = PRIMARY_PLANE_1,
>> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats         = primary_overlay_format0,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 2,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay_2",
>> >> +            .id                             = OVERLAY_PLANE_2,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats         = primary_overlay_format0,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 3,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Cursor",
>> >> +            .id                             = CURSOR_PLANE_0,
>> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
>> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
>> >> +            .formats         = cursor_formats,
>> >> +            .num_modifiers = 0,
>> >> +            .modifiers       = NULL,
>> >> +            .min_width       = 32,
>> >> +            .min_height  = 32,
>> >> +            .max_width       = 64,
>> >> +            .max_height  = 64,
>> >> +            .rotation        = 0,
>> >> +            .degamma_size = 0,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 255,
>> >> +            .watermark       = false,
>> >> +            .color_mgmt  = false,
>> >> +            .roi             = false,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Cursor_1",
>> >> +            .id                             = CURSOR_PLANE_1,
>> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
>> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
>> >> +            .formats         = cursor_formats,
>> >> +            .num_modifiers = 0,
>> >> +            .modifiers       = NULL,
>> >> +            .min_width       = 32,
>> >> +            .min_height  = 32,
>> >> +            .max_width       = 64,
>> >> +            .max_height  = 64,
>> >> +            .rotation        = 0,
>> >> +            .degamma_size = 0,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 255,
>> >> +            .watermark       = false,
>> >> +            .color_mgmt  = false,
>> >> +            .roi             = false,
>> >> +            },
>> >> +    },
>> >> +    {
>> >> +            /* DC_REV_2 */
>> >> +            {
>> >> +            .name            = "Primary",
>> >> +            .id                             = PRIMARY_PLANE_0,
>> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
>> >> +            .formats         = primary_overlay_format1,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 0,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay",
>> >> +            .id                             = OVERLAY_PLANE_0,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
>> >> +            .formats         = primary_overlay_format1,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 1,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay_1",
>> >> +            .id                             = OVERLAY_PLANE_1,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
>> >> +            .formats         = primary_overlay_format1,
>> >> +            .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
>> >> +            .modifiers       = secondary_format_modifiers,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = 0,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 2,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Primary_1",
>> >> +            .id                             = PRIMARY_PLANE_1,
>> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
>> >> +            .formats         = primary_overlay_format1,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 3,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay_2",
>> >> +            .id                             = OVERLAY_PLANE_2,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
>> >> +            .formats         = primary_overlay_format1,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 4,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay_3",
>> >> +            .id                             = OVERLAY_PLANE_3,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
>> >> +            .formats         = primary_overlay_format1,
>> >> +            .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
>> >> +            .modifiers       = secondary_format_modifiers,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = 0,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 5,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Cursor",
>> >> +            .id                             = CURSOR_PLANE_0,
>> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
>> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
>> >> +            .formats         = cursor_formats,
>> >> +            .num_modifiers = 0,
>> >> +            .modifiers       = NULL,
>> >> +            .min_width       = 32,
>> >> +            .min_height  = 32,
>> >> +            .max_width       = 64,
>> >> +            .max_height  = 64,
>> >> +            .rotation        = 0,
>> >> +            .degamma_size = 0,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 255,
>> >> +            .watermark       = false,
>> >> +            .color_mgmt  = false,
>> >> +            .roi             = false,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Cursor_1",
>> >> +            .id                             = CURSOR_PLANE_1,
>> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
>> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
>> >> +            .formats         = cursor_formats,
>> >> +            .num_modifiers = 0,
>> >> +            .modifiers       = NULL,
>> >> +            .min_width       = 32,
>> >> +            .min_height  = 32,
>> >> +            .max_width       = 64,
>> >> +            .max_height  = 64,
>> >> +            .rotation        = 0,
>> >> +            .degamma_size = 0,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 255,
>> >> +            .watermark       = false,
>> >> +            .color_mgmt  = false,
>> >> +            .roi             = false,
>> >> +            },
>> >> +    },
>> >> +};
>> >> +
>> >> +static const struct vs_dc_info dc_info[] = {
>> >
>> > Again, please split into per-generation instances.
will add  per-generation instances data
and I will only keep DC_REV_0 data struct
DC_REV_1 DC_REV_2 will delete ,because only DC_REV_0 used
>> >
>> >> +    {
>> >> +            /* DC_REV_0 */
>> >> +            .name                   = "DC8200",
>> >> +            .panel_num              = 2,
>> >> +            .plane_num              = 8,
>> >> +            .planes                 = dc_hw_planes[DC_REV_0],
>> >> +            .layer_num              = 6,
>> >> +            .max_bpc                = 10,
>> >> +            .color_formats  = DRM_COLOR_FORMAT_RGB444 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR444 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR422 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR420,
>> >> +            .gamma_size             = GAMMA_EX_SIZE,
>> >> +            .gamma_bits             = 12,
>> >> +            .pitch_alignment        = 128,
>> >> +            .pipe_sync              = false,
>> >> +            .background             = true,
>> >> +            .panel_sync             = true,
>> >> +            .cap_dec                = true,
>> >> +    },
>> >> +    {
>> >> +            /* DC_REV_1 */
>> >> +            .name                   = "DC8200",
>> >> +            .panel_num              = 2,
>> >> +            .plane_num              = 6,
>> >> +            .planes                 = dc_hw_planes[DC_REV_1],
>> >> +            .layer_num              = 4,
>> >> +            .max_bpc                = 10,
>> >> +            .color_formats  = DRM_COLOR_FORMAT_RGB444 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR444 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR422 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR420,
>> >> +            .gamma_size             = GAMMA_EX_SIZE,
>> >> +            .gamma_bits             = 12,
>> >> +            .pitch_alignment        = 128,
>> >> +            .pipe_sync              = false,
>> >> +            .background             = true,
>> >> +            .panel_sync             = true,
>> >> +            .cap_dec                = true,
>> >> +    },
>> >> +    {
>> >> +            /* DC_REV_2 */
>> >> +            .name                   = "DC8200",
>> >> +            .panel_num              = 2,
>> >> +            .plane_num              = 8,
>> >> +            .planes                 = dc_hw_planes[DC_REV_2],
>> >> +            .layer_num              = 6,
>> >> +            .max_bpc                = 10,
>> >> +            .color_formats  = DRM_COLOR_FORMAT_RGB444 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR444 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR422 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR420,
>> >> +            .gamma_size             = GAMMA_EX_SIZE,
>> >> +            .gamma_bits             = 12,
>> >> +            .pitch_alignment        = 128,
>> >> +            .pipe_sync              = false,
>> >> +            .background             = true,
>> >> +            .panel_sync             = true,
>> >> +            .cap_dec                = false,
>> >> +    },
>> >> +};
>> >> +
>> >> +static const struct dc_hw_funcs hw_func;
>> >> +
>> >> +static inline u32 hi_read(struct dc_hw *hw, u32 reg)
>> >> +{
>> >> +    return readl(hw->hi_base + reg);
>> >> +}
>> >> +
>> >> +static inline void hi_write(struct dc_hw *hw, u32 reg, u32 value)
>> >> +{
>> >> +    writel(value, hw->hi_base + reg);
>> >> +}
>> >> +
>> >> +static inline void dc_write(struct dc_hw *hw, u32 reg, u32 value)
>> >> +{
>> >> +    writel(value, hw->reg_base + reg - DC_REG_BASE);
>> >> +}
>> >> +
>> >> +static inline u32 dc_read(struct dc_hw *hw, u32 reg)
>> >> +{
>> >> +    u32 value = readl(hw->reg_base + reg - DC_REG_BASE);
>> >> +
>> >> +    return value;
>> >> +}
>> >> +
>> >> +static inline void dc_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear)
>> >> +{
>> >> +    u32 value = dc_read(hw, reg);
>> >> +
>> >> +    value &= ~clear;
>> >> +    value |= set;
>> >> +    dc_write(hw, reg, value);
>> >> +}
>> >> +
>> >> +static void load_default_filter(struct dc_hw *hw,
>> >> +                            const struct dc_hw_plane_reg *reg, u32 offset)
>> >> +{
>> >> +    u8 i;
>> >> +
>> >> +    dc_write(hw, reg->scale_config + offset, 0x33);
>> >> +    dc_write(hw, reg->init_offset + offset, 0x80008000);
>> >> +    dc_write(hw, reg->h_filter_coef_index + offset, 0x00);
>> >> +    for (i = 0; i < H_COEF_SIZE; i++)
>> >> +            dc_write(hw, reg->h_filter_coef_data + offset, horkernel[i]);
>> >> +
>> >> +    dc_write(hw, reg->v_filter_coef_index + offset, 0x00);
>> >> +    for (i = 0; i < V_COEF_SIZE; i++)
>> >> +            dc_write(hw, reg->v_filter_coef_data + offset, verkernel[i]);
>> >> +}
>> >> +
>> >> +static void load_rgb_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
>> >> +                        u32 offset, u16 *table)
>> >> +{
>> >> +    dc_write(hw, reg->rgb_to_rgb_coef0 + offset, table[0] | (table[1] << 16));
>> >> +    dc_write(hw, reg->rgb_to_rgb_coef1 + offset, table[2] | (table[3] << 16));
>> >> +    dc_write(hw, reg->rgb_to_rgb_coef2 + offset, table[4] | (table[5] << 16));
>> >> +    dc_write(hw, reg->rgb_to_rgb_coef3 + offset, table[6] | (table[7] << 16));
>> >> +    dc_write(hw, reg->rgb_to_rgb_coef4 + offset, table[8]);
>> >> +}
>> >> +
>> >> +static void load_yuv_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
>> >> +                        u32 offset, s32 *table)
>> >> +{
>> >> +    dc_write(hw, reg->yuv_to_rgb_coef0 + offset,
>> >> +             (0xFFFF & table[0]) | (table[1] << 16));
>> >> +    dc_write(hw, reg->yuv_to_rgb_coef1 + offset,
>> >> +             (0xFFFF & table[2]) | (table[3] << 16));
>> >> +    dc_write(hw, reg->yuv_to_rgb_coef2 + offset,
>> >> +             (0xFFFF & table[4]) | (table[5] << 16));
>> >> +    dc_write(hw, reg->yuv_to_rgb_coef3 + offset,
>> >> +             (0xFFFF & table[6]) | (table[7] << 16));
>> >> +    dc_write(hw, reg->yuv_to_rgb_coef4 + offset, table[8]);
>> >> +    dc_write(hw, reg->yuv_to_rgb_coefd0 + offset, table[9]);
>> >> +    dc_write(hw, reg->yuv_to_rgb_coefd1 + offset, table[10]);
>> >> +    dc_write(hw, reg->yuv_to_rgb_coefd2 + offset, table[11]);
>> >> +    dc_write(hw, reg->y_clamp_bound + offset, table[12] | (table[13] << 16));
>> >> +    dc_write(hw, reg->uv_clamp_bound + offset, table[14] | (table[15] << 16));
>> >> +}
>> >> +
>> >> +static void load_rgb_to_yuv(struct dc_hw *hw, u32 offset, s16 *table)
>> >> +{
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF0 + offset,
>> >> +             table[0] | (table[1] << 16));
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF1 + offset,
>> >> +             table[2] | (table[3] << 16));
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF2 + offset,
>> >> +             table[4] | (table[5] << 16));
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF3 + offset,
>> >> +             table[6] | (table[7] << 16));
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF4 + offset, table[8]);
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD0 + offset, table[9]);
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD1 + offset, table[10]);
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD2 + offset, table[11]);
>> >> +}
>> >> +
>> >> +static bool is_rgb(enum dc_hw_color_format format)
>> >> +{
>> >> +    switch (format) {
>> >> +    case FORMAT_X4R4G4B4:
>> >> +    case FORMAT_A4R4G4B4:
>> >> +    case FORMAT_X1R5G5B5:
>> >> +    case FORMAT_A1R5G5B5:
>> >> +    case FORMAT_R5G6B5:
>> >> +    case FORMAT_X8R8G8B8:
>> >> +    case FORMAT_A8R8G8B8:
>> >> +    case FORMAT_A2R10G10B10:
>> >> +            return true;
>> >> +    default:
>> >> +            return false;
>> >> +    }
>> >> +}
>> >> +
>> >> +static void load_degamma_table(struct dc_hw *hw,
>> >> +                           const struct dc_hw_plane_reg *reg,
>> >> +                           u32 offset, u16 *table)
>> >> +{
>> >> +    u16 i;
>> >> +    u32 value;
>> >> +
>> >> +    dc_write(hw, reg->degamma_index + offset, 0);
>> >> +
>> >> +    for (i = 0; i < DEGAMMA_SIZE; i++) {
>> >> +            value = table[i] | (table[i] << 16);
>> >> +            dc_write(hw, reg->degamma_data + offset, value);
>> >> +            dc_write(hw, reg->degamma_ex_data + offset, table[i]);
>> >> +    }
>> >> +}
>> >> +
>> >> +static u32 get_addr_offset(u32 id)
>> >> +{
>> >> +    u32 offset = 0;
>> >> +
>> >> +    switch (id) {
>> >> +    case PRIMARY_PLANE_1:
>> >> +    case OVERLAY_PLANE_1:
>> >> +            offset = 0x04;
>> >> +            break;
>> >> +    case OVERLAY_PLANE_2:
>> >> +            offset = 0x08;
>> >> +            break;
>> >> +    case OVERLAY_PLANE_3:
>> >> +            offset = 0x0C;
>> >> +            break;
>> >> +    default:
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    return offset;
>> >> +}
>> >> +
>> >> +int dc_hw_init(struct dc_hw *hw)
>> >> +{
>> >> +    u8 i, id, panel_num, layer_num;
>> >> +    u32 offset;
>> >> +    u32 revision = hi_read(hw, DC_HW_REVISION);
>> >> +    u32 cid = hi_read(hw, DC_HW_CHIP_CID);
>> >> +    const struct dc_hw_plane_reg *reg;
>> >> +
>> >> +    switch (revision) {
>> >> +    case 0x5720:
>> >> +            hw->rev = DC_REV_0;
>> >> +            break;
>> >> +    case 0x5721:
>> >> +            switch (cid) {
>> >> +            case 0x30B:
>> >> +                    hw->rev = DC_REV_1;
>> >> +                    break;
>> >> +            case 0x310:
>> >> +                    hw->rev = DC_REV_2;
>> >> +                    break;
>> >> +            default:
>> >> +                    hw->rev = DC_REV_0;
>> >> +                    break;
>> >> +            }
>> >> +            break;
>> >> +    default:
>> >> +            return -ENXIO;
>> >> +    }
>> >
>> > We (drm/msm) had code like this for a while (and autodetection of the HW
>> > ID). And nowadays we are migrating to of_device_get_match_data and
>> > per-SoC compat names. It is just easier to handle, it allows one to
>> > specify device more precisely and also it simplifies DT schema.
of_device_get_match_data is a good choice

>> >
>> >> +
>> >> +    hw->info = (struct vs_dc_info *)&dc_info[hw->rev];
>> >> +    hw->func = (struct dc_hw_funcs *)&hw_func;
>> >> +
>> >> +    layer_num = hw->info->layer_num;
>> >> +    for (i = 0; i < layer_num; i++) {
>> >> +            id = hw->info->planes[i].id;
>> >> +            offset = get_addr_offset(id);
>> >> +            if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
>> >> +                    reg = &dc_plane_reg[0];
>> >> +            else
>> >> +                    reg = &dc_plane_reg[1];
>> >> +
>> >> +            load_default_filter(hw, reg, offset);
>> >> +            load_rgb_to_rgb(hw, reg, offset, RGB2RGB);
>> >> +    }
>> >> +
>> >> +    panel_num = hw->info->panel_num;
>> >> +    for (i = 0; i < panel_num; i++) {
>> >> +            offset = i << 2;
>> >> +
>> >> +            load_rgb_to_yuv(hw, offset, RGB2YUV);
>> >> +            dc_write(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0x111);
>> >> +
>> >> +            offset = i ? DC_CURSOR_OFFSET : 0;
>> >> +            dc_write(hw, DC_CURSOR_BACKGROUND + offset, 0x00FFFFFF);
>> >> +            dc_write(hw, DC_CURSOR_FOREGROUND + offset, 0x00AAAAAA);
>> >> +    }
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +void dc_hw_deinit(struct dc_hw *hw)
>> >> +{
>> >> +    /* Nothing to do */
>> >> +}
>> >> +
>> >> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
>> >> +                    struct dc_hw_fb *fb, struct dc_hw_scale *scale,
>> >> +                    struct dc_hw_position *pos, struct dc_hw_blend *blend)
>> >> +{
>> >> +    struct dc_hw_plane *plane = &hw->plane[id];
>> >> +
>> >> +    if (plane) {
>> >> +            if (fb) {
>> >> +                    if (!fb->enable)
>> >> +                            plane->fb.enable = false;
>> >> +                    else
>> >> +                            memcpy(&plane->fb, fb,
>> >> +                                   sizeof(*fb) - sizeof(fb->dirty));
>> >> +                    plane->fb.dirty = true;
>> >> +            }
>> >> +            if (scale) {
>> >> +                    memcpy(&plane->scale, scale,
>> >> +                           sizeof(*scale) - sizeof(scale->dirty));
>> >> +                    plane->scale.dirty = true;
>> >> +            }
>> >> +            if (pos) {
>> >> +                    memcpy(&plane->pos, pos,
>> >> +                           sizeof(*pos) - sizeof(pos->dirty));
>> >> +                    plane->pos.dirty = true;
>> >> +            }
>> >> +            if (blend) {
>> >> +                    memcpy(&plane->blend, blend,
>> >> +                           sizeof(*blend) - sizeof(blend->dirty));
>> >> +                    plane->blend.dirty = true;
>> >> +            }
>> >> +    }
>> >> +}
>> >> +
>> >> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode)
>> >> +{
>> >> +    struct dc_hw_plane *plane = &hw->plane[id];
>> >> +
>> >> +    if (plane) {
>> >> +            if (hw->info->planes[id].degamma_size) {
>> >> +                    plane->degamma.mode = mode;
>> >> +                    plane->degamma.dirty = true;
>> >> +            } else {
>> >> +                    plane->degamma.dirty = false;
>> >> +            }
>> >> +    }
>> >> +}
>> >> +
>> >> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi)
>> >> +{
>> >> +    struct dc_hw_plane *plane = &hw->plane[id];
>> >> +
>> >> +    if (plane) {
>> >> +            memcpy(&plane->roi, roi, sizeof(*roi) - sizeof(roi->dirty));
>> >> +            plane->roi.dirty = true;
>> >> +    }
>> >> +}
>> >> +
>> >> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
>> >> +                       struct dc_hw_colorkey *colorkey)
>> >> +{
>> >> +    struct dc_hw_plane *plane = &hw->plane[id];
>> >> +
>> >> +    if (plane) {
>> >> +            memcpy(&plane->colorkey, colorkey,
>> >> +                   sizeof(*colorkey) - sizeof(colorkey->dirty));
>> >> +            plane->colorkey.dirty = true;
>> >> +    }
>> >> +}
>> >> +
>> >> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos)
>> >> +{
>> >> +    memcpy(&hw->qos, qos, sizeof(*qos) - sizeof(qos->dirty));
>> >> +    hw->qos.dirty = true;
>> >> +}
>> >> +
>> >> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor)
>> >> +{
>> >> +    memcpy(&hw->cursor[id], cursor, sizeof(*cursor) - sizeof(cursor->dirty));
>> >> +    hw->cursor[id].dirty = true;
>> >> +}
>> >> +
>> >> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
>> >> +                    u16 r, u16 g, u16 b)
>> >> +{
>> >> +    if (index >= hw->info->gamma_size)
>> >> +            return;
>> >> +
>> >> +    hw->gamma[id].gamma[index][0] = r;
>> >> +    hw->gamma[id].gamma[index][1] = g;
>> >> +    hw->gamma[id].gamma[index][2] = b;
>> >> +    hw->gamma[id].dirty = true;
>> >> +}
>> >> +
>> >> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable)
>> >> +{
>> >> +    hw->gamma[id].enable = enable;
>> >> +    hw->gamma[id].dirty = true;
>> >> +}
>> >> +
>> >> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display)
>> >> +{
>> >> +    u8 id = display->id;
>> >> +
>> >> +    memcpy(&hw->display[id], display, sizeof(*display));
>> >> +
>> >> +    hw->func->display(hw, display);
>> >> +}
>> >> +
>> >> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable)
>> >> +{
>> >> +    if (enable)
>> >> +            hi_write(hw, AQ_INTR_ENBL, 0xFFFFFFFF);
>> >> +    else
>> >> +            hi_write(hw, AQ_INTR_ENBL, 0);
>> >> +}
>> >> +
>> >> +u32 dc_hw_get_interrupt(struct dc_hw *hw)
>> >> +{
>> >> +    return hi_read(hw, AQ_INTR_ACKNOWLEDGE);
>> >> +}
>> >> +
>> >> +bool dc_hw_check_underflow(struct dc_hw *hw)
>> >> +{
>> >> +    return dc_read(hw, DC_FRAMEBUFFER_CONFIG) & BIT(5);
>> >> +}
>> >> +
>> >> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable)
>> >> +{
>> >> +    u32 i, offset;
>> >> +    u8 id, layer_num = hw->info->layer_num;
>> >> +    u8 panel_num = hw->info->panel_num;
>> >> +
>> >> +    for (i = 0; i < layer_num; i++) {
>> >> +            id = hw->info->planes[i].id;
>> >> +            offset = get_addr_offset(id);
>> >> +            if (enable) {
>> >> +                    if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
>> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, BIT(12), 0);
>> >> +                    else
>> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, BIT(31), 0);
>> >> +            } else {
>> >> +                    if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
>> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, 0, BIT(12));
>> >> +                    else
>> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, 0, BIT(31));
>> >> +            }
>> >> +    }
>> >> +
>> >> +    for (i = 0; i < panel_num; i++) {
>> >> +            offset = i << 2;
>> >> +            if (enable)
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, BIT(0));
>> >> +            else
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, BIT(0), 0);
>> >> +    }
>> >> +}
>> >> +
>> >> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id)
>> >> +{
>> >> +    if (out <= OUT_DP)
>> >> +            hw->out[id] = out;
>> >> +}
>> >> +
>> >> +static void gamma_ex_commit(struct dc_hw *hw)
>> >> +{
>> >> +    u8 panel_num = hw->info->panel_num;
>> >> +    u16 i, j;
>> >> +    u32 value;
>> >> +
>> >> +    for (j = 0; j < panel_num; j++) {
>> >> +            if (hw->gamma[j].dirty) {
>> >> +                    if (hw->gamma[j].enable) {
>> >> +                            dc_write(hw, DC_DISPLAY_GAMMA_EX_INDEX + (j << 2), 0x00);
>> >> +                            for (i = 0; i < GAMMA_EX_SIZE; i++) {
>> >> +                                    value = hw->gamma[j].gamma[i][2] |
>> >> +                                            (hw->gamma[j].gamma[i][1] << 12);
>> >> +                                    dc_write(hw, DC_DISPLAY_GAMMA_EX_DATA + (j << 2), value);
>> >> +                                    dc_write(hw, DC_DISPLAY_GAMMA_EX_ONE_DATA + (j << 2),
>> >> +                                             hw->gamma[j].gamma[i][0]);
>> >> +                            }
>> >> +                            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
>> >> +                                         BIT(13), 0);
>> >> +                    } else {
>> >> +                            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
>> >> +                                         0, BIT(13));
>> >> +                    }
>> >> +                    hw->gamma[j].dirty = false;
>> >> +            }
>> >> +    }
>> >> +}
>> >> +
>> >> +static void plane_commit(struct dc_hw *hw)
>> >> +{
>> >> +    struct dc_hw_plane *plane;
>> >> +    const struct dc_hw_plane_reg *reg;
>> >> +    bool primary = false;
>> >> +    u8 id, layer_num = hw->info->layer_num;
>> >> +    u32 i, offset;
>> >> +
>> >> +    for (i = 0; i < layer_num; i++) {
>> >> +            plane = &hw->plane[i];
>> >> +            id = hw->info->planes[i].id;
>> >> +            offset = get_addr_offset(id);
>> >> +            if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
>> >> +                    reg = &dc_plane_reg[0];
>> >> +                    primary = true;
>> >> +            } else {
>> >> +                    reg = &dc_plane_reg[1];
>> >> +                    primary = false;
>> >> +            }
>> >
>> > Can you please move register defines to some internal hw_plane
>> > structure. These details are known at the time of creation of the plane.
>> > There is no need to determine them again and again.
>> >
>> >> +
>> >> +            if (plane->fb.dirty) {
>> >> +                    if (plane->fb.enable) {
>> >
>> > if (plane->fb.dirty && plane->fb.enable) will save you one level of
>> > indentation.
>> >
>> > However it might be better to use existing DRM flags instead of
>> > inventing your own flags.
>> >
>> >> +                            dc_write(hw, reg->y_address + offset,
>> >> +                                     plane->fb.y_address);
>> >> +                            dc_write(hw, reg->u_address + offset,
>> >> +                                     plane->fb.u_address);
>> >> +                            dc_write(hw, reg->v_address + offset,
>> >> +                                     plane->fb.v_address);
>> >> +                            dc_write(hw, reg->y_stride + offset,
>> >> +                                     plane->fb.y_stride);
>> >> +                            dc_write(hw, reg->u_stride + offset,
>> >> +                                     plane->fb.u_stride);
>> >> +                            dc_write(hw, reg->v_stride + offset,
>> >> +                                     plane->fb.v_stride);
>> >> +                            dc_write(hw, reg->size + offset,
>> >> +                                     plane->fb.width |
>> >> +                                     (plane->fb.height << 15));
>> >> +                            dc_write(hw, reg->water_mark + offset,
>> >> +                                     plane->fb.water_mark);
>> >> +
>> >> +                            if (plane->fb.clear_enable)
>> >> +                                    dc_write(hw, reg->clear_value + offset,
>> >> +                                             plane->fb.clear_value);
>> >> +                    }
>> >> +
>> >> +                    if (primary) {
>> >
>> > You have two kinds of planes. Primary and overlays. Could you please
>> > split this code into two separate functions and define two vtables, one
>> > for primary planes and one for overlays. There is no need to stick
>> > everything into a single funcion that handles everything.
>> >
>> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
>> >> +                                         (plane->fb.format << 26) |
>> >> +                                         (plane->fb.uv_swizzle << 25) |
>> >> +                                         (plane->fb.swizzle << 23) |
>> >> +                                         (plane->fb.tile_mode << 17) |
>> >> +                                         (plane->fb.yuv_color_space << 14) |
>> >> +                                         (plane->fb.rotation << 11) |
>> >> +                                         (plane->fb.clear_enable << 8),
>> >> +                                         (0x1F << 26) |
>> >> +                                         BIT(25) |
>> >> +                                         (0x03 << 23) |
>> >> +                                         (0x1F << 17) |
>> >> +                                         (0x07 << 14) |
>> >> +                                         (0x07 << 11) |
>> >> +                                         BIT(8));
>> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                         (plane->fb.dec_enable << 1) |
>> >> +                                         (plane->fb.enable << 13) |
>> >> +                                         (plane->fb.zpos << 16) |
>> >> +                                         (plane->fb.display_id << 19),
>> >> +                                         BIT(1) | BIT(13) | (0x07 << 16) | BIT(19));
>> >
>> > It looks like this code can enjoy defining names for all the magic
>> > values. Otherwise it is impossible to support it.
>> >
>> >> +                    } else {
>> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> >> +                                         (plane->fb.dec_enable << 27) |
>> >> +                                         (plane->fb.clear_enable << 25) |
>> >> +                                         (plane->fb.enable << 24) |
>> >> +                                         (plane->fb.format << 16) |
>> >> +                                         (plane->fb.uv_swizzle << 15) |
>> >> +                                         (plane->fb.swizzle << 13) |
>> >> +                                         (plane->fb.tile_mode << 8) |
>> >> +                                         (plane->fb.yuv_color_space << 5) |
>> >> +                                         (plane->fb.rotation << 2),
>> >> +                                         BIT(27) |
>> >> +                                         BIT(25) |
>> >> +                                         BIT(24) |
>> >> +                                         (0x1F << 16) |
>> >> +                                         BIT(15) |
>> >> +                                         (0x03 << 13) |
>> >> +                                         (0x1F << 8) |
>> >> +                                         (0x07 << 5) |
>> >> +                                         (0x07 << 2));
>> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset,
>> >> +                                         plane->fb.zpos | (plane->fb.display_id << 3),
>> >> +                                         0x07 | BIT(3));
>> >> +                    }
>> >> +                    plane->fb.dirty = false;
>> >> +            }
>> >> +
>> >> +            if (plane->scale.dirty) {
>> >> +                    if (plane->scale.enable) {
>> >> +                            dc_write(hw, reg->scale_factor_x + offset,
>> >> +                                     plane->scale.scale_factor_x);
>> >> +                            dc_write(hw, reg->scale_factor_y + offset,
>> >> +                                     plane->scale.scale_factor_y);
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_FRAMEBUFFER_CONFIG + offset,
>> >> +                                                 BIT(22), 0);
>> >> +                            else
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_OVERLAY_SCALE_CONFIG + offset,
>> >> +                                                 BIT(8), 0);
>> >> +                    } else {
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_FRAMEBUFFER_CONFIG + offset,
>> >> +                                                 0, BIT(22));
>> >> +                            else
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_OVERLAY_SCALE_CONFIG + offset,
>> >> +                                                 0, BIT(8));
>> >> +                    }
>> >> +                    plane->scale.dirty = false;
>> >> +            }
>> >> +
>> >> +            if (plane->pos.dirty) {
>> >> +                    dc_write(hw, reg->top_left + offset,
>> >> +                             plane->pos.start_x |
>> >> +                             (plane->pos.start_y << 15));
>> >> +                    dc_write(hw, reg->bottom_right + offset,
>> >> +                             plane->pos.end_x |
>> >> +                             (plane->pos.end_y << 15));
>> >> +                    plane->pos.dirty = false;
>> >> +            }
>> >> +
>> >> +            if (plane->blend.dirty) {
>> >> +                    dc_write(hw, reg->src_global_color + offset,
>> >> +                             plane->blend.alpha << 24);
>> >> +                    dc_write(hw, reg->dst_global_color + offset,
>> >> +                             plane->blend.alpha << 24);
>> >> +                    switch (plane->blend.blend_mode) {
>> >> +                    case BLEND_PREMULTI:
>> >> +                            dc_write(hw, reg->blend_config + offset, 0x3450);
>> >> +                            break;
>> >> +                    case BLEND_COVERAGE:
>> >> +                            dc_write(hw, reg->blend_config + offset, 0x3950);
>> >> +                            break;
>> >> +                    case BLEND_PIXEL_NONE:
>> >> +                            dc_write(hw, reg->blend_config + offset, 0x3548);
>> >> +                            break;
>> >> +                    default:
>> >> +                            break;
>> >> +                    }
>> >> +                    plane->blend.dirty = false;
>> >> +            }
>> >> +
>> >> +            if (plane->colorkey.dirty) {
>> >> +                    dc_write(hw, reg->color_key + offset, plane->colorkey.colorkey);
>> >> +                    dc_write(hw, reg->color_key_high + offset,
>> >> +                             plane->colorkey.colorkey_high);
>> >> +
>> >> +                    if (primary)
>> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
>> >> +                                         plane->colorkey.transparency << 9, 0x03 << 9);
>> >> +                    else
>> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> >> +                                         plane->colorkey.transparency, 0x03);
>> >> +
>> >> +                    plane->colorkey.dirty = false;
>> >> +            }
>> >> +
>> >> +            if (plane->roi.dirty) {
>> >> +                    if (plane->roi.enable) {
>> >> +                            dc_write(hw, reg->roi_origin + offset,
>> >> +                                     plane->roi.x | (plane->roi.y << 16));
>> >> +                            dc_write(hw, reg->roi_size + offset,
>> >> +                                     plane->roi.width | (plane->roi.height << 16));
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                                 BIT(0), 0);
>> >> +                            else
>> >> +                                    dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> >> +                                                 BIT(22), 0);
>> >> +                    } else {
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                                 0, BIT(0));
>> >> +                            else
>> >> +                                    dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> >> +                                                 0, BIT(22));
>> >> +                    }
>> >> +                    plane->roi.dirty = false;
>> >> +            }
>> >> +    }
>> >> +}
>> >> +
>> >> +static void plane_ex_commit(struct dc_hw *hw)
>> >> +{
>> >> +    struct dc_hw_plane *plane;
>> >> +    const struct dc_hw_plane_reg *reg;
>> >> +    bool primary = false;
>> >> +    u8 id, layer_num = hw->info->layer_num;
>> >> +    u32 i, offset;
>> >> +
>> >> +    for (i = 0; i < layer_num; i++) {
>> >> +            plane = &hw->plane[i];
>> >> +            id = hw->info->planes[i].id;
>> >> +            offset = get_addr_offset(id);
>> >> +            if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
>> >> +                    reg = &dc_plane_reg[0];
>> >> +                    primary = true;
>> >> +            } else {
>> >> +                    reg = &dc_plane_reg[1];
>> >> +                    primary = false;
>> >> +            }
>> >> +
>> >> +            if (plane->fb.dirty) {
>> >> +                    if (is_rgb(plane->fb.format)) {
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                                 BIT(6), BIT(8));
>> >> +                            else
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_OVERLAY_CONFIG + offset,
>> >> +                                                 BIT(29), BIT(30));
>> >> +                    } else {
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                                 BIT(8), BIT(6));
>> >> +                            else
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_OVERLAY_CONFIG + offset,
>> >> +                                                 BIT(30), BIT(29));
>> >> +                            switch (plane->fb.yuv_color_space) {
>> >> +                            case COLOR_SPACE_601:
>> >> +                                    load_yuv_to_rgb(hw, reg, offset, YUV601_2RGB);
>> >> +                                    break;
>> >> +                            case COLOR_SPACE_709:
>> >> +                                    load_yuv_to_rgb(hw, reg, offset, YUV709_2RGB);
>> >> +                                    break;
>> >> +                            case COLOR_SPACE_2020:
>> >> +                                    load_yuv_to_rgb(hw, reg, offset, YUV2020_2RGB);
>> >> +                                    break;
>> >> +                            default:
>> >> +                                    break;
>> >> +                            }
>> >> +                    }
>> >> +            }
>> >> +            if (plane->degamma.dirty) {
>> >> +                    switch (plane->degamma.mode) {
>> >> +                    case VS_DEGAMMA_DISABLE:
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                                 0, BIT(5));
>> >> +                            else
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_OVERLAY_CONFIG + offset,
>> >> +                                                 0, BIT(28));
>> >> +                            break;
>> >> +                    case VS_DEGAMMA_BT709:
>> >> +                            load_degamma_table(hw, reg, offset, DEGAMMA_709);
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                                 BIT(5), 0);
>> >> +                            else
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_OVERLAY_CONFIG + offset,
>> >> +                                                 BIT(28), 0);
>> >> +                            break;
>> >> +                    case VS_DEGAMMA_BT2020:
>> >> +                            load_degamma_table(hw, reg, offset, DEGAMMA_2020);
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                                 BIT(5), 0);
>> >> +                            else
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_OVERLAY_CONFIG + offset,
>> >> +                                                 BIT(28), 0);
>> >> +                            break;
>> >> +                    default:
>> >> +                            break;
>> >> +                    }
>> >> +                    plane->degamma.dirty = false;
>> >> +            }
>> >> +    }
>> >> +    plane_commit(hw);
>> >> +}
>> >> +
>> >> +static void setup_display(struct dc_hw *hw, struct dc_hw_display *display)
>> >> +{
>> >> +    u8 id = display->id;
>> >> +    u32 dpi_cfg, offset = id << 2;
>> >> +
>> >> +    if (hw->display[id].enable) {
>> >> +            switch (display->bus_format) {
>> >> +            case MEDIA_BUS_FMT_RGB565_1X16:
>> >> +                    dpi_cfg = 0;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_RGB666_1X18:
>> >> +                    dpi_cfg = 3;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
>> >> +                    dpi_cfg = 4;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_RGB888_1X24:
>> >> +                    dpi_cfg = 5;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_RGB101010_1X30:
>> >> +                    dpi_cfg = 6;
>> >> +                    break;
>> >> +            default:
>> >> +                    dpi_cfg = 5;
>> >> +                    break;
>> >> +            }
>> >> +            dc_write(hw, DC_DISPLAY_DPI_CONFIG + offset, dpi_cfg);
>> >> +
>> >> +            if (id == 0)
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
>> >> +            else
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
>> >> +
>> >> +            dc_write(hw, DC_DISPLAY_H + offset, hw->display[id].h_active |
>> >> +                     (hw->display[id].h_total << 16));
>> >> +            dc_write(hw, DC_DISPLAY_H_SYNC + offset,
>> >> +                     hw->display[id].h_sync_start |
>> >> +                     (hw->display[id].h_sync_end << 15) |
>> >> +                     (hw->display[id].h_sync_polarity ? 0 : BIT(31)) |
>> >> +                     BIT(30));
>> >> +            dc_write(hw, DC_DISPLAY_V + offset, hw->display[id].v_active |
>> >> +                            (hw->display[id].v_total << 16));
>> >> +            dc_write(hw, DC_DISPLAY_V_SYNC + offset,
>> >> +                     hw->display[id].v_sync_start |
>> >> +                     (hw->display[id].v_sync_end << 15) |
>> >> +                     (hw->display[id].v_sync_polarity ? 0 : BIT(31)) |
>> >> +                     BIT(30));
>> >> +
>> >> +            if (hw->info->pipe_sync)
>> >> +                    dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
>> >> +                                 0, BIT(3) | BIT(4));
>> >> +
>> >> +            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(12), 0);
>> >> +            if (id == 0)
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(0), BIT(3));
>> >> +            else
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(1), BIT(3));
>> >> +    } else {
>> >> +            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(12));
>> >> +            if (id == 0)
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
>> >> +            else
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
>> >> +    }
>> >> +}
>> >> +
>> >> +static void setup_display_ex(struct dc_hw *hw, struct dc_hw_display *display)
>> >> +{
>> >> +    u8 id = display->id;
>> >> +    u32 dp_cfg, offset = id << 2;
>> >> +    bool is_yuv = false;
>> >> +
>> >> +    if (hw->display[id].enable && hw->out[id] == OUT_DP) {
>> >> +            switch (display->bus_format) {
>> >> +            case MEDIA_BUS_FMT_RGB565_1X16:
>> >> +                    dp_cfg = 0;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_RGB666_1X18:
>> >> +                    dp_cfg = 1;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_RGB888_1X24:
>> >> +                    dp_cfg = 2;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_RGB101010_1X30:
>> >> +                    dp_cfg = 3;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_UYVY8_1X16:
>> >> +                    dp_cfg = 2 << 4;
>> >> +                    is_yuv = true;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_YUV8_1X24:
>> >> +                    dp_cfg = 4 << 4;
>> >> +                    is_yuv = true;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_UYVY10_1X20:
>> >> +                    dp_cfg = 8 << 4;
>> >> +                    is_yuv = true;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_YUV10_1X30:
>> >> +                    dp_cfg = 10 << 4;
>> >> +                    is_yuv = true;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
>> >> +                    dp_cfg = 12 << 4;
>> >> +                    is_yuv = true;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
>> >> +                    dp_cfg = 13 << 4;
>> >> +                    is_yuv = true;
>> >> +                    break;
>> >> +            default:
>> >> +                    dp_cfg = 2;
>> >> +                    break;
>> >> +            }
>> >> +            if (is_yuv)
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(16), 0);
>> >> +            else
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(16));
>> >> +            dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | BIT(3));
>> >> +    }
>> >> +
>> >> +    if (hw->out[id] == OUT_DPI)
>> >> +            dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, BIT(3));
>> >> +
>> >> +    setup_display(hw, display);
>> >> +}
>> >> +
>> >> +static const struct dc_hw_funcs hw_func = {
>> >> +    .gamma = &gamma_ex_commit,
>> >> +    .plane = &plane_ex_commit,
>> >> +    .display = setup_display_ex,
>> >> +};
>> >> +
>> >> +void dc_hw_commit(struct dc_hw *hw)
>> >> +{
>> >> +    u32 i, offset = 0;
>> >> +    u8 plane_num = hw->info->plane_num;
>> >> +    u8 layer_num = hw->info->layer_num;
>> >> +    u8 cursor_num = plane_num - layer_num;
>> >> +
>> >> +    hw->func->gamma(hw);
>> >> +    hw->func->plane(hw);
>> >> +
>> >> +    for (i = 0; i < cursor_num; i++) {
>> >> +            if (hw->cursor[i].dirty) {
>> >> +                    offset = hw->cursor[i].display_id ? DC_CURSOR_OFFSET : 0;
>> >> +                    if (hw->cursor[i].enable) {
>> >> +                            dc_write(hw, DC_CURSOR_ADDRESS + offset,
>> >> +                                     hw->cursor[i].address);
>> >> +                            dc_write(hw, DC_CURSOR_LOCATION + offset, hw->cursor[i].x |
>> >> +                                     (hw->cursor[i].y << 16));
>> >> +                            dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
>> >> +                                         (hw->cursor[i].hot_x << 16) |
>> >> +                                         (hw->cursor[i].hot_y << 8) |
>> >> +                                         (hw->cursor[i].size << 5) |
>> >> +                                         BIT(3) | BIT(2) | 0x02,
>> >> +                                         (0xFF << 16) |
>> >> +                                         (0xFF << 8) |
>> >> +                                         (0x07 << 5) | 0x1F);
>> >> +                    } else {
>> >> +                            dc_set_clear(hw, DC_CURSOR_CONFIG + offset, BIT(3), 0x03);
>> >> +                    }
>> >> +                    hw->cursor[i].dirty = false;
>> >> +            }
>> >> +    }
>> >> +
>> >> +    if (hw->qos.dirty) {
>> >> +            dc_set_clear(hw, DC_QOS_CONFIG, (hw->qos.high_value << 4) |
>> >> +                                     hw->qos.low_value, 0xFF);
>> >> +            hw->qos.dirty = false;
>> >> +    }
>> >> +}
>> >> +
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.h b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
>> >> new file mode 100644
>> >> index 000000000..9a1d767ae
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
>> >> @@ -0,0 +1,492 @@
>> >> +/* SPDX-License-Identifier: GPL-2.0 */
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#ifndef __VS_DC_HW_H__
>> >> +#define __VS_DC_HW_H__
>> >> +
>> >> +#define AQ_INTR_ACKNOWLEDGE                         0x0010
>> >> +#define AQ_INTR_ENBL                                        0x0014
>> >
>> > Please take care to align all the defines.
>> >
>> >> +#define DC_HW_REVISION                                      0x0024
>> >> +#define DC_HW_CHIP_CID                                      0x0030
>> >> +
>> >> +#define DC_REG_BASE                                         0x0800
>> >> +#define DC_REG_RANGE                                        0x2000
>> >> +#define DC_SEC_REG_OFFSET                           0x100000
>> >> +
>> >> +#define DC_FRAMEBUFFER_CONFIG                       0x1518
>> >> +#define DC_FRAMEBUFFER_CONFIG_EX            0x1CC0
>> >> +#define DC_FRAMEBUFFER_SCALE_CONFIG         0x1520
>> >> +#define DC_FRAMEBUFFER_TOP_LEFT                     0x24D8
>> >> +#define DC_FRAMEBUFFER_BOTTOM_RIGHT         0x24E0
>> >> +#define DC_FRAMEBUFFER_ADDRESS                      0x1400
>> >> +#define DC_FRAMEBUFFER_U_ADDRESS            0x1530
>> >> +#define DC_FRAMEBUFFER_V_ADDRESS            0x1538
>> >> +#define DC_FRAMEBUFFER_STRIDE                       0x1408
>> >> +#define DC_FRAMEBUFFER_U_STRIDE                     0x1800
>> >> +#define DC_FRAMEBUFFER_V_STRIDE                     0x1808
>> >> +#define DC_FRAMEBUFFER_SIZE                         0x1810
>> >> +#define DC_FRAMEBUFFER_SCALE_FACTOR_X               0x1828
>> >> +#define DC_FRAMEBUFFER_SCALE_FACTOR_Y               0x1830
>> >> +#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX  0x1838
>> >> +#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA   0x1A00
>> >> +#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX  0x1A08
>> >> +#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA   0x1A10
>> >> +#define DC_FRAMEBUFFER_INIT_OFFSET                  0x1A20
>> >> +#define DC_FRAMEBUFFER_COLOR_KEY                    0x1508
>> >> +#define DC_FRAMEBUFFER_COLOR_KEY_HIGH               0x1510
>> >> +#define DC_FRAMEBUFFER_CLEAR_VALUE                  0x1A18
>> >> +#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX    0x1818
>> >> +#define DC_FRAMEBUFFER_COLOR_TABLE_DATA             0x1820
>> >> +#define DC_FRAMEBUFFER_BG_COLOR                             0x1528
>> >> +#define DC_FRAMEBUFFER_ROI_ORIGIN                   0x1CB0
>> >> +#define DC_FRAMEBUFFER_ROI_SIZE                             0x1CB8
>> >> +#define DC_FRAMEBUFFER_WATER_MARK                   0x1CE8
>> >> +#define DC_FRAMEBUFFER_DEGAMMA_INDEX                0x1D88
>> >> +#define DC_FRAMEBUFFER_DEGAMMA_DATA                 0x1D90
>> >> +#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA              0x1D98
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF0               0x1DA0
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF1               0x1DA8
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF2               0x1DB0
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF3               0x1DB8
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF4               0x1E00
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD0              0x1E08
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD1              0x1E10
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD2              0x1E18
>> >> +#define DC_FRAMEBUFFER_Y_CLAMP_BOUND                0x1E88
>> >> +#define DC_FRAMEBUFFER_UV_CLAMP_BOUND               0x1E90
>> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF0               0x1E20
>> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF1               0x1E28
>> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF2               0x1E30
>> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF3               0x1E38
>> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF4               0x1E40
>> >> +#define DC_FRAMEBUFFER_BLEND_CONFIG                 0x2510
>> >> +#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR             0x2500
>> >> +#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR             0x2508
>> >> +
>> >> +#define DC_OVERLAY_CONFIG                                   0x1540
>> >> +#define DC_OVERLAY_CONFIG_EX                                0x2540
>> >> +#define DC_OVERLAY_SCALE_CONFIG                             0x1C00
>> >> +#define DC_OVERLAY_BLEND_CONFIG                             0x1580
>> >> +#define DC_OVERLAY_TOP_LEFT                                 0x1640
>> >> +#define DC_OVERLAY_BOTTOM_RIGHT                             0x1680
>> >> +#define DC_OVERLAY_ADDRESS                                  0x15C0
>> >> +#define DC_OVERLAY_U_ADDRESS                                0x1840
>> >> +#define DC_OVERLAY_V_ADDRESS                                0x1880
>> >> +#define DC_OVERLAY_STRIDE                                   0x1600
>> >> +#define DC_OVERLAY_U_STRIDE                                 0x18C0
>> >> +#define DC_OVERLAY_V_STRIDE                                 0x1900
>> >> +#define DC_OVERLAY_SIZE                                             0x17C0
>> >> +#define DC_OVERLAY_SCALE_FACTOR_X                   0x1A40
>> >> +#define DC_OVERLAY_SCALE_FACTOR_Y                   0x1A80
>> >> +#define DC_OVERLAY_H_FILTER_COEF_INDEX              0x1AC0
>> >> +#define DC_OVERLAY_H_FILTER_COEF_DATA               0x1B00
>> >> +#define DC_OVERLAY_V_FILTER_COEF_INDEX              0x1B40
>> >> +#define DC_OVERLAY_V_FILTER_COEF_DATA               0x1B80
>> >> +#define DC_OVERLAY_INIT_OFFSET                              0x1BC0
>> >> +#define DC_OVERLAY_COLOR_KEY                                0x1740
>> >> +#define DC_OVERLAY_COLOR_KEY_HIGH                   0x1780
>> >> +#define DC_OVERLAY_CLEAR_VALUE                              0x1940
>> >> +#define DC_OVERLAY_COLOR_TABLE_INDEX                0x1980
>> >> +#define DC_OVERLAY_COLOR_TABLE_DATA                 0x19C0
>> >> +#define DC_OVERLAY_SRC_GLOBAL_COLOR                 0x16C0
>> >> +#define DC_OVERLAY_DST_GLOBAL_COLOR                 0x1700
>> >> +#define DC_OVERLAY_ROI_ORIGIN                               0x1D00
>> >> +#define DC_OVERLAY_ROI_SIZE                                 0x1D40
>> >> +#define DC_OVERLAY_WATER_MARK                               0x1DC0
>> >> +#define DC_OVERLAY_DEGAMMA_INDEX                    0x2200
>> >> +#define DC_OVERLAY_DEGAMMA_DATA                             0x2240
>> >> +#define DC_OVERLAY_DEGAMMA_EX_DATA                  0x2280
>> >> +#define DC_OVERLAY_YUVTORGB_COEF0                   0x1EC0
>> >> +#define DC_OVERLAY_YUVTORGB_COEF1                   0x1F00
>> >> +#define DC_OVERLAY_YUVTORGB_COEF2                   0x1F40
>> >> +#define DC_OVERLAY_YUVTORGB_COEF3                   0x1F80
>> >> +#define DC_OVERLAY_YUVTORGB_COEF4                   0x1FC0
>> >> +#define DC_OVERLAY_YUVTORGB_COEFD0                  0x2000
>> >> +#define DC_OVERLAY_YUVTORGB_COEFD1                  0x2040
>> >> +#define DC_OVERLAY_YUVTORGB_COEFD2                  0x2080
>> >> +#define DC_OVERLAY_Y_CLAMP_BOUND                    0x22C0
>> >> +#define DC_OVERLAY_UV_CLAMP_BOUND                   0x2300
>> >> +#define DC_OVERLAY_RGBTORGB_COEF0                   0x20C0
>> >> +#define DC_OVERLAY_RGBTORGB_COEF1                   0x2100
>> >> +#define DC_OVERLAY_RGBTORGB_COEF2                   0x2140
>> >> +#define DC_OVERLAY_RGBTORGB_COEF3                   0x2180
>> >> +#define DC_OVERLAY_RGBTORGB_COEF4                   0x21C0
>> >> +
>> >> +#define DC_CURSOR_CONFIG                                    0x1468
>> >> +#define DC_CURSOR_ADDRESS                                   0x146C
>> >> +#define DC_CURSOR_LOCATION                                  0x1470
>> >> +#define DC_CURSOR_BACKGROUND                                0x1474
>> >> +#define DC_CURSOR_FOREGROUND                                0x1478
>> >> +#define DC_CURSOR_CLK_GATING                                0x1484
>> >> +#define DC_CURSOR_CONFIG_EX                                 0x24E8
>> >> +#define DC_CURSOR_OFFSET                                    0x1080
>> >> +
>> >> +#define DC_DISPLAY_DITHER_CONFIG            0x1410
>> >> +#define DC_DISPLAY_PANEL_CONFIG                     0x1418
>> >> +#define DC_DISPLAY_PANEL_CONFIG_EX          0x2518
>> >> +#define DC_DISPLAY_DITHER_TABLE_LOW         0x1420
>> >> +#define DC_DISPLAY_DITHER_TABLE_HIGH        0x1428
>> >> +#define DC_DISPLAY_H                                        0x1430
>> >> +#define DC_DISPLAY_H_SYNC                           0x1438
>> >> +#define DC_DISPLAY_V                                        0x1440
>> >> +#define DC_DISPLAY_V_SYNC                           0x1448
>> >> +#define DC_DISPLAY_CURRENT_LOCATION         0x1450
>> >> +#define DC_DISPLAY_GAMMA_INDEX                      0x1458
>> >> +#define DC_DISPLAY_GAMMA_DATA                       0x1460
>> >> +#define DC_DISPLAY_INT                                      0x147C
>> >> +#define DC_DISPLAY_INT_ENABLE                       0x1480
>> >> +#define DC_DISPLAY_DBI_CONFIG                       0x1488
>> >> +#define DC_DISPLAY_GENERAL_CONFIG           0x14B0
>> >> +#define DC_DISPLAY_DPI_CONFIG                       0x14B8
>> >> +#define DC_DISPLAY_PANEL_START                      0x1CCC
>> >> +#define DC_DISPLAY_DEBUG_COUNTER_SELECT             0x14D0
>> >> +#define DC_DISPLAY_DEBUG_COUNTER_VALUE              0x14D8
>> >> +#define DC_DISPLAY_DP_CONFIG                                0x1CD0
>> >> +#define DC_DISPLAY_GAMMA_EX_INDEX                   0x1CF0
>> >> +#define DC_DISPLAY_GAMMA_EX_DATA                    0x1CF8
>> >> +#define DC_DISPLAY_GAMMA_EX_ONE_DATA                0x1D80
>> >> +#define DC_DISPLAY_RGBTOYUV_COEF0                   0x1E48
>> >> +#define DC_DISPLAY_RGBTOYUV_COEF1                   0x1E50
>> >> +#define DC_DISPLAY_RGBTOYUV_COEF2                   0x1E58
>> >> +#define DC_DISPLAY_RGBTOYUV_COEF3                   0x1E60
>> >> +#define DC_DISPLAY_RGBTOYUV_COEF4                   0x1E68
>> >> +#define DC_DISPLAY_RGBTOYUV_COEFD0                  0x1E70
>> >> +#define DC_DISPLAY_RGBTOYUV_COEFD1                  0x1E78
>> >> +#define DC_DISPLAY_RGBTOYUV_COEFD2                  0x1E80
>> >> +
>> >> +#define DC_CLK_GATTING                                      0x1A28
>> >> +#define DC_QOS_CONFIG                                       0x1A38
>> >> +
>> >> +#define DC_TRANSPARENCY_OPAQUE                      0x00
>> >> +#define DC_TRANSPARENCY_KEY                         0x02
>> >> +#define DC_DISPLAY_DITHERTABLE_LOW          0x7B48F3C0
>> >> +#define DC_DISPLAY_DITHERTABLE_HIGH         0x596AD1E2
>> >> +
>> >> +#define GAMMA_SIZE                                          256
>> >> +#define GAMMA_EX_SIZE                                       300
>> >> +#define DEGAMMA_SIZE                                        260
>> >> +
>> >> +#define RGB_TO_RGB_TABLE_SIZE                       9
>> >> +#define YUV_TO_RGB_TABLE_SIZE                       16
>> >> +#define RGB_TO_YUV_TABLE_SIZE                       12
>> >> +
>> >> +#define DC_LAYER_NUM        6
>> >> +#define DC_DISPLAY_NUM      2
>> >> +#define DC_CURSOR_NUM       2
>> >> +
>> >> +#define DC_TILE_MODE4X4     0x15
>> >> +
>> >> +enum dc_chip_rev {
>> >> +    DC_REV_0,/* For HW_REV_5720,HW_REV_5721_311 */
>> >> +    DC_REV_1,/* For HW_REV_5721_30B */
>> >> +    DC_REV_2,/* For HW_REV_5721_310 */
>> >> +};
>> >> +
>> >> +enum dc_hw_plane_id {
>> >> +    PRIMARY_PLANE_0,
>> >> +    OVERLAY_PLANE_0,
>> >> +    OVERLAY_PLANE_1,
>> >> +    PRIMARY_PLANE_1,
>> >> +    OVERLAY_PLANE_2,
>> >> +    OVERLAY_PLANE_3,
>> >> +    CURSOR_PLANE_0,
>> >> +    CURSOR_PLANE_1,
>> >> +    PLANE_NUM
>> >> +};
>> >> +
>> >> +enum dc_hw_color_format {
>> >> +    FORMAT_X4R4G4B4,//0
>> >
>> > Space after the comma will make it more readable. Or just define the
>> > values explicitly, it will also be more readable.
>> >
>> >> +    FORMAT_A4R4G4B4,//1
>> >> +    FORMAT_X1R5G5B5,//2
>> >> +    FORMAT_A1R5G5B5,//3
>> >> +    FORMAT_R5G6B5,//4
>> >> +    FORMAT_X8R8G8B8,//5
>> >> +    FORMAT_A8R8G8B8,//6
>> >> +    FORMAT_YUY2,//7
>> >> +    FORMAT_UYVY,//8
>> >> +    FORMAT_INDEX8,//9
>> >> +    FORMAT_MONOCHROME,//10
>> >> +    FORMAT_YV12 = 0xf,
>> >> +    FORMAT_A8,//16
>> >> +    FORMAT_NV12,//17
>> >> +    FORMAT_NV16,//18
>> >> +    FORMAT_RG16,//19
>> >> +    FORMAT_R8,//20
>> >> +    FORMAT_NV12_10BIT,//21
>> >> +    FORMAT_A2R10G10B10,//22
>> >> +    FORMAT_NV16_10BIT,//23
>> >> +    FORMAT_INDEX1,//24
>> >> +    FORMAT_INDEX2,//25
>> >> +    FORMAT_INDEX4,//26
>> >> +    FORMAT_P010,//27
>> >> +    FORMAT_YUV444,//28
>> >> +    FORMAT_YUV444_10BIT,//29
>> >> +};
>> >> +
>> >> +enum dc_hw_yuv_color_space {
>> >> +    COLOR_SPACE_601 = 0,
>> >> +    COLOR_SPACE_709 = 1,
>> >> +    COLOR_SPACE_2020 = 3,
>> >> +};
>> >> +
>> >> +enum dc_hw_rotation {
>> >> +    ROT_0 = 0,
>> >> +    ROT_90 = 4,
>> >> +    ROT_180 = 5,
>> >> +    ROT_270 = 6,
>> >> +    FLIP_X = 1,
>> >> +    FLIP_Y = 2,
>> >> +    FLIP_XY = 3,
>> >> +};
>> >> +
>> >> +enum dc_hw_swizzle {
>> >> +    SWIZZLE_ARGB = 0,
>> >> +    SWIZZLE_RGBA,
>> >> +    SWIZZLE_ABGR,
>> >> +    SWIZZLE_BGRA,
>> >> +};
>> >> +
>> >> +enum dc_hw_out {
>> >> +    OUT_DPI,
>> >> +    OUT_DP,
>> >> +};
>> >> +
>> >> +enum dc_hw_cursor_size {
>> >> +    CURSOR_SIZE_32X32 = 0,
>> >> +    CURSOR_SIZE_64X64,
>> >> +};
>> >> +
>> >> +enum dc_hw_blend_mode {
>> >> +    /* out.rgb = plane_alpha * fg.rgb +
>> >> +     *              (1 - (plane_alpha * fg.alpha)) * bg.rgb
>> >> +     */
>> >> +    BLEND_PREMULTI,
>> >> +    /* out.rgb = plane_alpha * fg.alpha * fg.rgb +
>> >> +     *              (1 - (plane_alpha * fg.alpha)) * bg.rgb
>> >> +     */
>> >> +    BLEND_COVERAGE,
>> >> +    /* out.rgb = plane_alpha * fg.rgb +
>> >> +     *              (1 - plane_alpha) * bg.rgb
>> >> +     */
>> >> +    BLEND_PIXEL_NONE,
>> >
>> > Can you use defines from <drm/drm_blend.h>?
>> >
>> >> +};
>> >> +
>> >> +struct dc_hw_plane_reg {
>> >> +    u32 y_address;
>> >> +    u32 u_address;
>> >> +    u32 v_address;
>> >> +    u32 y_stride;
>> >> +    u32 u_stride;
>> >> +    u32 v_stride;
>> >> +    u32 size;
>> >> +    u32 top_left;
>> >> +    u32 bottom_right;
>> >> +    u32 scale_factor_x;
>> >> +    u32 scale_factor_y;
>> >> +    u32 h_filter_coef_index;
>> >> +    u32 h_filter_coef_data;
>> >> +    u32 v_filter_coef_index;
>> >> +    u32 v_filter_coef_data;
>> >> +    u32 init_offset;
>> >> +    u32 color_key;
>> >> +    u32 color_key_high;
>> >> +    u32 clear_value;
>> >> +    u32 color_table_index;
>> >> +    u32 color_table_data;
>> >> +    u32 scale_config;
>> >> +    u32 water_mark;
>> >> +    u32 degamma_index;
>> >> +    u32 degamma_data;
>> >> +    u32 degamma_ex_data;
>> >> +    u32 src_global_color;
>> >> +    u32 dst_global_color;
>> >> +    u32 blend_config;
>> >> +    u32 roi_origin;
>> >> +    u32 roi_size;
>> >> +    u32 yuv_to_rgb_coef0;
>> >> +    u32 yuv_to_rgb_coef1;
>> >> +    u32 yuv_to_rgb_coef2;
>> >> +    u32 yuv_to_rgb_coef3;
>> >> +    u32 yuv_to_rgb_coef4;
>> >> +    u32 yuv_to_rgb_coefd0;
>> >> +    u32 yuv_to_rgb_coefd1;
>> >> +    u32 yuv_to_rgb_coefd2;
>> >> +    u32 y_clamp_bound;
>> >> +    u32 uv_clamp_bound;
>> >> +    u32 rgb_to_rgb_coef0;
>> >> +    u32 rgb_to_rgb_coef1;
>> >> +    u32 rgb_to_rgb_coef2;
>> >> +    u32 rgb_to_rgb_coef3;
>> >> +    u32 rgb_to_rgb_coef4;
>> >> +};
>> >> +
>> >> +struct dc_hw_fb {
>> >> +    u32 y_address;
>> >> +    u32 u_address;
>> >> +    u32 v_address;
>> >> +    u32 clear_value;
>> >> +    u32 water_mark;
>> >> +    u16 y_stride;
>> >> +    u16 u_stride;
>> >> +    u16 v_stride;
>> >> +    u16 width;
>> >> +    u16 height;
>> >> +    u8      format;
>> >> +    u8      tile_mode;
>> >> +    u8      rotation;
>> >> +    u8      yuv_color_space;
>> >> +    u8      swizzle;
>> >> +    u8      uv_swizzle;
>> >> +    u8      zpos;
>> >> +    u8      display_id;
>> >> +    bool    clear_enable;
>> >> +    bool    dec_enable;
>> >> +    bool    enable;
>> >> +    bool    dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_scale {
>> >> +    u32 scale_factor_x;
>> >> +    u32 scale_factor_y;
>> >> +    bool    enable;
>> >> +    bool    dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_position {
>> >> +    u16 start_x;
>> >> +    u16 start_y;
>> >> +    u16 end_x;
>> >> +    u16 end_y;
>> >> +    bool    dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_blend {
>> >> +    u8      alpha;
>> >> +    u8      blend_mode;
>> >> +    bool    dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_colorkey {
>> >> +    u32 colorkey;
>> >> +    u32 colorkey_high;
>> >> +    u8      transparency;
>> >> +    bool dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_roi {
>> >> +    u16 x;
>> >> +    u16 y;
>> >> +    u16 width;
>> >> +    u16 height;
>> >> +    bool enable;
>> >> +    bool dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_cursor {
>> >> +    u32 address;
>> >> +    u16 x;
>> >> +    u16 y;
>> >> +    u16 hot_x;
>> >> +    u16 hot_y;
>> >> +    u8      size;
>> >> +    u8      display_id;
>> >> +    bool    enable;
>> >> +    bool    dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_display {
>> >> +    u32 bus_format;
>> >> +    u16 h_active;
>> >> +    u16 h_total;
>> >> +    u16 h_sync_start;
>> >> +    u16 h_sync_end;
>> >> +    u16 v_active;
>> >> +    u16 v_total;
>> >> +    u16 v_sync_start;
>> >> +    u16 v_sync_end;
>> >> +    u8      id;
>> >> +    bool    h_sync_polarity;
>> >> +    bool    v_sync_polarity;
>> >> +    bool    enable;
>> >> +};
>> >> +
>> >> +struct dc_hw_gamma {
>> >> +    u16 gamma[GAMMA_EX_SIZE][3];
>> >> +    bool    enable;
>> >> +    bool    dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_degamma {
>> >> +    u16 degamma[DEGAMMA_SIZE][3];
>> >> +    u32 mode;
>> >> +    bool    dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_plane {
>> >> +    struct dc_hw_fb                 fb;
>> >> +    struct dc_hw_position   pos;
>> >> +    struct dc_hw_scale              scale;
>> >> +    struct dc_hw_blend              blend;
>> >> +    struct dc_hw_roi                roi;
>> >> +    struct dc_hw_colorkey   colorkey;
>> >> +    struct dc_hw_degamma    degamma;
>> >> +};
>> >> +
>> >> +struct dc_hw_qos {
>> >> +    u8        low_value;
>> >> +    u8        high_value;
>> >> +    bool  dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_read {
>> >> +    u32                     reg;
>> >> +    u32                     value;
>> >> +};
>> >> +
>> >> +struct dc_hw;
>> >> +struct dc_hw_funcs {
>> >> +    void (*gamma)(struct dc_hw *hw);
>> >> +    void (*plane)(struct dc_hw *hw);
>> >> +    void (*display)(struct dc_hw *hw, struct dc_hw_display *display);
>> >> +};
>> >> +
>> >> +struct dc_hw {
>> >> +    enum dc_chip_rev rev;
>> >> +    enum dc_hw_out          out[DC_DISPLAY_NUM];
>> >> +    void                    *hi_base;
>> >> +    void                    *reg_base;
>> >> +
>> >> +    struct dc_hw_display display[DC_DISPLAY_NUM];
>> >> +    struct dc_hw_gamma       gamma[DC_DISPLAY_NUM];
>> >> +    struct dc_hw_plane       plane[DC_LAYER_NUM];
>> >> +    struct dc_hw_cursor  cursor[DC_CURSOR_NUM];
>> >> +    struct dc_hw_qos         qos;
>> >> +    struct dc_hw_funcs       *func;
>> >> +    struct vs_dc_info        *info;
>> >> +};
>> >> +
>> >> +int dc_hw_init(struct dc_hw *hw);
>> >> +void dc_hw_deinit(struct dc_hw *hw);
>> >> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
>> >> +                    struct dc_hw_fb *fb, struct dc_hw_scale *scale,
>> >> +                    struct dc_hw_position *pos, struct dc_hw_blend *blend);
>> >> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode);
>> >> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi);
>> >> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
>> >> +                       struct dc_hw_colorkey *colorkey);
>> >> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos);
>> >> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor);
>> >> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
>> >> +                    u16 r, u16 g, u16 b);
>> >> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable);
>> >> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display);
>> >> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable);
>> >> +u32 dc_hw_get_interrupt(struct dc_hw *hw);
>> >> +bool dc_hw_check_underflow(struct dc_hw *hw);
>> >> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable);
>> >> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id);
>> >> +void dc_hw_commit(struct dc_hw *hw);
>> >> +
>> >> +#endif /* __VS_DC_HW_H__ */
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
>> >> index da7698c3d..3cd533cfa 100644
>> >> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
>> >> @@ -21,6 +21,7 @@
>> >>
>> >>   #include "vs_drv.h"
>> >>   #include "vs_modeset.h"
>> >> +#include "vs_dc.h"
>> >>
>> >>   #define DRV_NAME   "verisilicon"
>> >>   #define DRV_DESC   "Verisilicon DRM driver"
>> >> @@ -123,6 +124,7 @@ static const struct component_master_ops vs_drm_ops = {
>> >>   };
>> >>
>> >>   static struct platform_driver *drm_sub_drivers[] = {
>> >> +    &dc_platform_driver,
>> >>
>> >>   };
>> >>
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c
>> >> new file mode 100644
>> >> index 000000000..9bd066015
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_plane.c
>> >> @@ -0,0 +1,526 @@
>> >> +// SPDX-License-Identifier: GPL-2.0
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#include <drm/drm_atomic.h>
>> >> +#include <drm/drm_atomic_helper.h>
>> >> +#include <drm/drm_blend.h>
>> >> +#include <drm/drm_gem_dma_helper.h>
>> >> +#include <drm/drm_fb_dma_helper.h>
>> >> +#include <drm/drm_framebuffer.h>
>> >> +#include <drm/drm_plane.h>
>> >> +#include <drm/drm_plane_helper.h>
>> >> +
>> >> +#include <drm/vs_drm.h>
>> >> +
>> >> +#include "vs_plane.h"
>> >> +#include "vs_drv.h"
>> >> +#include "vs_dc.h"
>> >> +
>> >> +static void vs_plane_reset(struct drm_plane *plane)
>> >> +{
>> >> +    struct vs_plane_state *state;
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +
>> >> +    if (plane->state) {
>> >> +            __drm_atomic_helper_plane_destroy_state(plane->state);
>> >> +
>> >> +            state = to_vs_plane_state(plane->state);
>> >> +            kfree(state);
>> >> +            plane->state = NULL;
>> >> +    }
>> >> +
>> >
>> > Use vs_plane_destroy_state directly. This way you won't miss a refcount
>> > on blobs.
>> >
>> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
>> >> +    if (!state)
>> >> +            return;
>> >> +
>> >> +    state->degamma = VS_DEGAMMA_DISABLE;
>> >> +    state->degamma_changed = false;
>> >> +    state->base.zpos = vs_plane->id;
>> >> +    memset(&state->status, 0, sizeof(state->status));
>> >> +
>> >> +    __drm_atomic_helper_plane_reset(plane, &state->base);
>> >> +}
>> >> +
>> >> +static void _vs_plane_duplicate_blob(struct vs_plane_state *state,
>> >> +                                 struct vs_plane_state *ori_state)
>> >> +{
>> >> +    state->watermark = ori_state->watermark;
>> >> +    state->color_mgmt = ori_state->color_mgmt;
>> >> +    state->roi = ori_state->roi;
>> >> +
>> >> +    if (state->watermark)
>> >> +            drm_property_blob_get(state->watermark);
>> >> +    if (state->color_mgmt)
>> >> +            drm_property_blob_get(state->color_mgmt);
>> >> +    if (state->roi)
>> >> +            drm_property_blob_get(state->roi);
>> >> +}
>> >> +
>> >> +static int
>> >> +_vs_plane_set_property_blob_from_id(struct drm_device *dev,
>> >> +                                struct drm_property_blob **blob,
>> >> +                                u64 blob_id,
>> >> +                                size_t expected_size)
>> >> +{
>> >> +    struct drm_property_blob *new_blob = NULL;
>> >> +
>> >> +    if (blob_id) {
>> >> +            new_blob = drm_property_lookup_blob(dev, blob_id);
>> >> +            if (!new_blob)
>> >> +                    return -EINVAL;
>> >> +
>> >> +            if (new_blob->length != expected_size) {
>> >> +                    drm_property_blob_put(new_blob);
>> >> +                    return -EINVAL;
>> >> +            }
>> >> +    }
>> >> +
>> >> +    drm_property_replace_blob(blob, new_blob);
>> >> +    drm_property_blob_put(new_blob);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static struct drm_plane_state *
>> >> +vs_plane_atomic_duplicate_state(struct drm_plane *plane)
>> >> +{
>> >> +    struct vs_plane_state *ori_state;
>> >> +    struct vs_plane_state *state;
>> >> +
>> >> +    if (WARN_ON(!plane->state))
>> >> +            return NULL;
>> >> +
>> >> +    ori_state = to_vs_plane_state(plane->state);
>> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
>> >> +    if (!state)
>> >> +            return NULL;
>> >> +
>> >> +    __drm_atomic_helper_plane_duplicate_state(plane, &state->base);
>> >> +
>> >> +    state->degamma = ori_state->degamma;
>> >> +    state->degamma_changed = ori_state->degamma_changed;
>> >> +
>> >> +    _vs_plane_duplicate_blob(state, ori_state);
>> >> +    memcpy(&state->status, &ori_state->status, sizeof(ori_state->status));
>> >> +
>> >> +    return &state->base;
>> >> +}
>> >> +
>> >> +static void vs_plane_atomic_destroy_state(struct drm_plane *plane,
>> >> +                                      struct drm_plane_state *state)
>> >> +{
>> >> +    struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
>> >> +
>> >> +    __drm_atomic_helper_plane_destroy_state(state);
>> >> +
>> >> +    drm_property_blob_put(vs_plane_state->watermark);
>> >> +    drm_property_blob_put(vs_plane_state->color_mgmt);
>> >> +    drm_property_blob_put(vs_plane_state->roi);
>> >> +    kfree(vs_plane_state);
>> >> +}
>> >> +
>> >> +static int vs_plane_atomic_set_property(struct drm_plane *plane,
>> >> +                                    struct drm_plane_state *state,
>> >> +                                    struct drm_property *property,
>> >> +                                    uint64_t val)
>> >> +{
>> >> +    struct drm_device *dev = plane->dev;
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +    struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
>> >> +    int ret = 0;
>> >> +
>> >> +    if (property == vs_plane->degamma_mode) {
>> >> +            if (vs_plane_state->degamma != val) {
>> >> +                    vs_plane_state->degamma = val;
>> >> +                    vs_plane_state->degamma_changed = true;
>> >> +            } else {
>> >> +                    vs_plane_state->degamma_changed = false;
>> >> +            }
>> >> +    } else if (property == vs_plane->watermark_prop) {
>> >> +            ret = _vs_plane_set_property_blob_from_id(dev,
>> >> +                                                      &vs_plane_state->watermark,
>> >> +                                                      val,
>> >> +                                                      sizeof(struct drm_vs_watermark));
>> >> +            return ret;
>> >> +    } else if (property == vs_plane->color_mgmt_prop) {
>> >> +            ret = _vs_plane_set_property_blob_from_id(dev,
>> >> +                                                      &vs_plane_state->color_mgmt,
>> >> +                                                      val,
>> >> +                                                      sizeof(struct drm_vs_color_mgmt));
>> >> +            return ret;
>> >> +    } else if (property == vs_plane->roi_prop) {
>> >> +            ret = _vs_plane_set_property_blob_from_id(dev,
>> >> +                                                      &vs_plane_state->roi,
>> >> +                                                      val,
>> >> +                                                      sizeof(struct drm_vs_roi));
>> >> +            return ret;
>> >> +    } else {
>> >> +            return -EINVAL;
>> >> +    }
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int vs_plane_atomic_get_property(struct drm_plane *plane,
>> >> +                                    const struct drm_plane_state *state,
>> >> +                                    struct drm_property *property,
>> >> +                                    uint64_t *val)
>> >> +{
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +    const struct vs_plane_state *vs_plane_state =
>> >> +            container_of(state, const struct vs_plane_state, base);
>> >> +
>> >> +    if (property == vs_plane->degamma_mode)
>> >> +            *val = vs_plane_state->degamma;
>> >> +    else if (property == vs_plane->watermark_prop)
>> >> +            *val = (vs_plane_state->watermark) ?
>> >> +                                    vs_plane_state->watermark->base.id : 0;
>> >> +    else if (property == vs_plane->color_mgmt_prop)
>> >> +            *val = (vs_plane_state->color_mgmt) ?
>> >> +                                    vs_plane_state->color_mgmt->base.id : 0;
>> >
>> > degamma and color management should use standard properties.

hello Dmitry:
There is a question that troubles me

drm_plane include such standard properties.
{
	struct drm_property *alpha_property;
	struct drm_property *zpos_property;
	struct drm_property *rotation_property;
	struct drm_property *blend_mode_property;
	struct drm_property *color_encoding_property;
	struct drm_property *color_range_property;
	struct drm_property *scaling_filter_property;
}

it doesn't include degamma and color management properties

>> >
>> >> +    else if (property == vs_plane->roi_prop)
>> >> +            *val = (vs_plane_state->roi) ?
>> >> +                                    vs_plane_state->roi->base.id : 0;
>> >> +    else
>> >> +            return -EINVAL;
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static bool vs_format_mod_supported(struct drm_plane *plane,
>> >> +                                u32 format,
>> >> +                                u64 modifier)
>> >> +{
>> >> +    int i;
>> >> +
>> >> +    /* We always have to allow these modifiers:
>> >> +     * 1. Core DRM checks for LINEAR support if userspace does not provide modifiers.
>> >> +     * 2. Not passing any modifiers is the same as explicitly passing INVALID.
>> >> +     */
>> >> +    if (modifier == DRM_FORMAT_MOD_LINEAR)
>> >> +            return true;
>> >> +
>> >> +    /* Check that the modifier is on the list of the plane's supported modifiers. */
>> >> +    for (i = 0; i < plane->modifier_count; i++) {
>> >> +            if (modifier == plane->modifiers[i])
>> >> +                    break;
>> >> +    }
>> >> +
>> >> +    if (i == plane->modifier_count)
>> >> +            return false;
>> >> +
>> >> +    return true;
>> >> +}
>> >> +
>> >> +const struct drm_plane_funcs vs_plane_funcs = {
>> >> +    .update_plane           = drm_atomic_helper_update_plane,
>> >> +    .disable_plane          = drm_atomic_helper_disable_plane,
>> >> +    .reset                  = vs_plane_reset,
>> >> +    .atomic_duplicate_state = vs_plane_atomic_duplicate_state,
>> >> +    .atomic_destroy_state   = vs_plane_atomic_destroy_state,
>> >> +    .atomic_set_property    = vs_plane_atomic_set_property,
>> >> +    .atomic_get_property    = vs_plane_atomic_get_property,
>> >> +    .format_mod_supported   = vs_format_mod_supported,
>> >> +};
>> >> +
>> >> +static unsigned char vs_get_plane_number(struct drm_framebuffer *fb)
>> >> +{
>> >> +    const struct drm_format_info *info;
>> >> +
>> >> +    if (!fb)
>> >> +            return 0;
>> >> +
>> >> +    info = drm_format_info(fb->format->format);
>> >> +    if (!info || info->num_planes > DRM_FORMAT_MAX_PLANES)
>> >> +            return 0;
>> >> +
>> >> +    return info->num_planes;
>> >> +}
>> >> +
>> >> +static int vs_plane_atomic_check(struct drm_plane *plane,
>> >> +                             struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> >> +                                                                             plane);
>> >> +    unsigned char i, num_planes;
>> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
>> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
>> >> +
>> >> +    if (!crtc || !fb)
>> >> +            return 0;
>> >> +
>> >> +    num_planes = vs_get_plane_number(fb);
>> >> +
>> >> +    for (i = 0; i < num_planes; i++) {
>> >> +            dma_addr_t dma_addr;
>> >> +
>> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
>> >> +            plane_state->dma_addr[i] = dma_addr;
>> >> +    }
>> >> +
>> >> +    return vs_dc_check_plane(dc, plane, state);
>> >> +}
>> >> +
>> >> +static int vs_cursor_plane_atomic_check(struct drm_plane *plane,
>> >> +                                    struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> >> +                                                                      plane);
>> >> +    unsigned char i, num_planes;
>> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
>> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
>> >> +
>> >> +    if (!crtc || !fb)
>> >> +            return 0;
>> >> +
>> >> +    num_planes = vs_get_plane_number(fb);
>> >> +
>> >> +    for (i = 0; i < num_planes; i++) {
>> >> +            dma_addr_t dma_addr;
>> >> +
>> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
>> >> +            plane_state->dma_addr[i] = dma_addr;
>> >> +    }
>> >> +
>> >> +    return vs_dc_check_cursor_plane(dc, plane, state);
>> >> +}
>> >> +
>> >> +static void vs_plane_atomic_update(struct drm_plane *plane,
>> >> +                               struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
>> >> +                                                                      plane);
>> >
>> > New line after the equal sign will be better.
>> >
>> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> >> +                                                                      plane);
>> >> +
>> >> +    unsigned char i, num_planes;
>> >> +    struct drm_framebuffer *fb;
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    if (!new_state->fb || !new_state->crtc)
>> >> +            return;
>> >
>> > if (!new_state->visible) ?

no matter what value it is ? the driver will handle it
in func 

static void update_fb(struct vs_plane *plane, u8 display_id,
		      struct dc_hw_fb *fb, struct drm_plane_state *state)
{
	struct vs_plane_state *plane_state = to_vs_plane_state(state);
	struct drm_framebuffer *drm_fb = state->fb;
	struct drm_rect *src = &state->src;

.......
	fb->enable = state->visible;
.......
}

so no need add "if (!new_state->visible)" i think

>> >
>> >> +
>> >> +    fb = new_state->fb;
>> >> +
>> >> +    drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
>> >> +
>> >> +    num_planes = vs_get_plane_number(fb);
>> >> +
>> >> +    for (i = 0; i < num_planes; i++) {
>> >> +            dma_addr_t dma_addr;
>> >> +
>> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
>> >> +            plane_state->dma_addr[i] = dma_addr;
>> >> +    }
>> >> +
>> >> +    plane_state->status.src = drm_plane_state_src(new_state);
>> >> +    plane_state->status.dest = drm_plane_state_dest(new_state);
>> >> +
>> >> +    vs_dc_update_plane(dc, vs_plane, plane, state);
>> >> +}
>> >> +
>> >> +static void vs_cursor_plane_atomic_update(struct drm_plane *plane,
>> >> +                                      struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
>> >> +                                                                       plane);
>> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> >> +                                                                       plane);
>> >> +    unsigned char i, num_planes;
>> >> +    struct drm_framebuffer *fb;
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    if (!new_state->fb || !new_state->crtc)
>> >
>> > and here.
>> >
>> >> +            return;
>> >> +
>> >> +    fb = new_state->fb;
>> >> +    drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
>> >> +
>> >> +    num_planes = vs_get_plane_number(fb);
>> >> +
>> >> +    for (i = 0; i < num_planes; i++) {
>> >> +            dma_addr_t dma_addr;
>> >> +
>> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
>> >> +            plane_state->dma_addr[i] = dma_addr;
>> >> +    }
>> >> +
>> >> +    plane_state->status.src = drm_plane_state_src(new_state);
>> >> +    plane_state->status.dest = drm_plane_state_dest(new_state);
>> >> +
>> >> +    vs_dc_update_cursor_plane(dc, vs_plane, plane, state);
>> >> +}
>> >> +
>> >> +static void vs_plane_atomic_disable(struct drm_plane *plane,
>> >> +                                struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> >> +                                                                       plane);
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    vs_dc_disable_plane(dc, vs_plane, old_state);
>> >> +}
>> >> +
>> >> +static void vs_cursor_plane_atomic_disable(struct drm_plane *plane,
>> >> +                                       struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> >> +                                                                       plane);
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    vs_dc_disable_cursor_plane(dc, vs_plane, old_state);
>> >> +}
>> >> +
>> >> +const struct drm_plane_helper_funcs primary_plane_helpers = {
>> >> +    .atomic_check   = vs_plane_atomic_check,
>> >> +    .atomic_update  = vs_plane_atomic_update,
>> >> +    .atomic_disable = vs_plane_atomic_disable,
>> >> +};
>> >> +
>> >> +const struct drm_plane_helper_funcs overlay_plane_helpers = {
>> >> +    .atomic_check   = vs_plane_atomic_check,
>> >> +    .atomic_update  = vs_plane_atomic_update,
>> >> +    .atomic_disable = vs_plane_atomic_disable,
>> >> +};
>> >> +
>> >> +const struct drm_plane_helper_funcs cursor_plane_helpers = {
>> >> +    .atomic_check   = vs_cursor_plane_atomic_check,
>> >> +    .atomic_update  = vs_cursor_plane_atomic_update,
>> >> +    .atomic_disable = vs_cursor_plane_atomic_disable,
>> >> +};
>> >> +
>> >> +static const struct drm_prop_enum_list vs_degamma_mode_enum_list[] = {
>> >> +    { VS_DEGAMMA_DISABLE,   "disabled" },
>> >> +    { VS_DEGAMMA_BT709,     "preset degamma for BT709" },
>> >> +    { VS_DEGAMMA_BT2020,    "preset degamma for BT2020" },
>> >> +};
>> >> +
>> >> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
>> >> +                             struct vs_plane_info *info,
>> >> +                             unsigned int layer_num,
>> >> +                             unsigned int possible_crtcs)
>> >> +{
>> >> +    struct vs_plane *plane;
>> >> +    int ret;
>> >> +
>> >> +    if (!info)
>> >> +            return NULL;
>> >> +
>> >> +    plane = drmm_universal_plane_alloc(drm_dev, struct vs_plane, base,
>> >> +                                       possible_crtcs,
>> >> +                                       &vs_plane_funcs,
>> >> +                                       info->formats, info->num_formats,
>> >> +                                       info->modifiers, info->type,
>> >> +                                       info->name ? info->name : NULL);
>> >> +    if (IS_ERR(plane))
>> >> +            return ERR_CAST(plane);
>> >> +
>> >> +    if (info->type == DRM_PLANE_TYPE_PRIMARY)
>> >> +            drm_plane_helper_add(&plane->base, &primary_plane_helpers);
>> >> +    else if (info->type == DRM_PLANE_TYPE_CURSOR)
>> >> +            drm_plane_helper_add(&plane->base, &cursor_plane_helpers);
>> >> +    else
>> >> +            drm_plane_helper_add(&plane->base, &overlay_plane_helpers);
>> >> +
>> >> +    /* Set up the plane properties */
>> >> +    if (info->degamma_size) {
>> >> +            plane->degamma_mode =
>> >> +            drm_property_create_enum(drm_dev, 0,
>> >> +                                     "DEGAMMA_MODE",
>> >> +                                     vs_degamma_mode_enum_list,
>> >> +                                     ARRAY_SIZE(vs_degamma_mode_enum_list));
>> >> +
>> >> +            if (!plane->degamma_mode)
>> >> +                    return NULL;
>> >> +
>> >> +            drm_object_attach_property(&plane->base.base,
>> >> +                                       plane->degamma_mode,
>> >> +                                       VS_DEGAMMA_DISABLE);
>> >> +    }
>> >> +
>> >> +    if (info->rotation) {
>> >> +            ret = drm_plane_create_rotation_property(&plane->base,
>> >> +                                                     DRM_MODE_ROTATE_0,
>> >> +                                                     info->rotation);
>> >> +            if (ret)
>> >> +                    return NULL;
>> >> +    }
>> >> +
>> >> +    if (info->blend_mode) {
>> >> +            ret = drm_plane_create_blend_mode_property(&plane->base,
>> >> +                                                       info->blend_mode);
>> >> +            if (ret)
>> >> +                    return NULL;
>> >> +            ret = drm_plane_create_alpha_property(&plane->base);
>> >> +            if (ret)
>> >> +                    return NULL;
>> >> +    }
>> >> +
>> >> +    if (info->color_encoding) {
>> >> +            ret = drm_plane_create_color_properties(&plane->base,
>> >> +                                                    info->color_encoding,
>> >> +                                                    BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
>> >> +                                                    DRM_COLOR_YCBCR_BT709,
>> >> +                                                    DRM_COLOR_YCBCR_LIMITED_RANGE);
>> >> +            if (ret)
>> >> +                    return NULL;
>> >> +    }
>> >> +
>> >> +    if (info->zpos != 255) {
>> >> +            ret = drm_plane_create_zpos_property(&plane->base, info->zpos, 0,
>> >> +                                                 layer_num - 1);
>> >> +            if (ret)
>> >> +                    return NULL;
>> >> +    } else {
>> >> +            ret = drm_plane_create_zpos_immutable_property(&plane->base,
>> >> +                                                           info->zpos);
>> >> +            if (ret)
>> >> +                    return NULL;
>> >> +    }
>> >> +
>> >> +    if (info->watermark) {
>> >> +            plane->watermark_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
>> >> +                                                        "WATERMARK", 0);
>> >> +            if (!plane->watermark_prop)
>> >> +                    return NULL;
>> >> +            drm_object_attach_property(&plane->base.base, plane->watermark_prop, 0);
>> >> +    }
>> >> +
>> >> +    if (info->color_mgmt) {
>> >> +            plane->color_mgmt_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
>> >> +                                                         "COLOR_CONFIG", 0);
>> >> +            if (!plane->color_mgmt_prop)
>> >> +                    return NULL;
>> >> +
>> >> +            drm_object_attach_property(&plane->base.base, plane->color_mgmt_prop, 0);
>> >> +    }
>> >> +
>> >> +    if (info->roi) {
>> >> +            plane->roi_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
>> >> +                                                  "ROI", 0);
>> >> +            if (!plane->roi_prop)
>> >> +                    return NULL;
>> >> +
>> >> +            drm_object_attach_property(&plane->base.base, plane->roi_prop, 0);
>> >> +    }
>> >> +
>> >> +    return plane;
>> >> +}
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h
>> >> new file mode 100644
>> >> index 000000000..554b74e96
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_plane.h
>> >> @@ -0,0 +1,58 @@
>> >> +/* SPDX-License-Identifier: GPL-2.0 */
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#ifndef __VS_PLANE_H__
>> >> +#define __VS_PLANE_H__
>> >> +
>> >> +#include <drm/drm_fourcc.h>
>> >> +#include <drm/drm_plane_helper.h>
>> >> +
>> >> +#include "vs_type.h"
>> >> +
>> >> +struct vs_plane_status {
>> >> +    u32 tile_mode;
>> >> +    struct drm_rect src;
>> >> +    struct drm_rect dest;
>> >> +};
>> >> +
>> >> +struct vs_plane_state {
>> >> +    struct drm_plane_state base;
>> >> +    struct vs_plane_status status; /* for debugfs */
>> >> +
>> >> +    struct drm_property_blob *watermark;
>> >> +    struct drm_property_blob *color_mgmt;
>> >> +    struct drm_property_blob *roi;
>> >> +    dma_addr_t dma_addr[DRM_FORMAT_MAX_PLANES];
>> >> +
>> >> +    u32 degamma;
>> >> +    bool degamma_changed;
>> >> +};
>> >> +
>> >> +struct vs_plane {
>> >> +    struct drm_plane base;
>> >> +    u8 id;
>> >> +
>> >> +    struct drm_property *degamma_mode;
>> >> +    struct drm_property *watermark_prop;
>> >> +    struct drm_property *color_mgmt_prop;
>> >> +    struct drm_property *roi_prop;
>> >> +};
>> >> +
>> >> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
>> >> +                             struct vs_plane_info *info,
>> >> +                             unsigned int layer_num,
>> >> +                             unsigned int possible_crtcs);
>> >> +
>> >> +static inline struct vs_plane *to_vs_plane(struct drm_plane *plane)
>> >> +{
>> >> +    return container_of(plane, struct vs_plane, base);
>> >> +}
>> >> +
>> >> +static inline struct vs_plane_state *
>> >> +to_vs_plane_state(struct drm_plane_state *state)
>> >> +{
>> >> +    return container_of(state, struct vs_plane_state, base);
>> >> +}
>> >> +#endif /* __VS_PLANE_H__ */
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_type.h b/drivers/gpu/drm/verisilicon/vs_type.h
>> >> new file mode 100644
>> >> index 000000000..7d3378e29
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_type.h
>> >> @@ -0,0 +1,69 @@
>> >> +/* SPDX-License-Identifier: GPL-2.0 */
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#ifndef __VS_TYPE_H__
>> >> +#define __VS_TYPE_H__
>> >> +
>> >> +#include <drm/drm_plane.h>
>> >> +#include <drm/drm_plane_helper.h>
>> >> +
>> >> +struct vs_plane_info {
>> >> +    const char *name;
>> >> +    u8 id;
>> >> +    enum drm_plane_type type;
>> >> +    unsigned int num_formats;
>> >> +    const u32 *formats;
>> >> +    u8 num_modifiers;
>> >> +    const u64 *modifiers;
>> >> +    unsigned int min_width;
>> >> +    unsigned int min_height;
>> >> +    unsigned int max_width;
>> >> +    unsigned int max_height;
>> >> +    unsigned int rotation;
>> >> +    unsigned int blend_mode;
>> >> +    unsigned int color_encoding;
>> >> +
>> >> +    /* 0 means no de-gamma LUT */
>> >> +    unsigned int degamma_size;
>> >> +
>> >> +    int min_scale; /* 16.16 fixed point */
>> >> +    int max_scale; /* 16.16 fixed point */
>> >> +
>> >> +    /* default zorder value,
>> >> +     * and 255 means unsupported zorder capability
>> >> +     */
>> >> +    u8       zpos;
>> >> +
>> >> +    bool watermark;
>> >> +    bool color_mgmt;
>> >> +    bool roi;
>> >> +};
>> >> +
>> >> +struct vs_dc_info {
>> >> +    const char *name;
>> >> +
>> >> +    u8 panel_num;
>> >> +
>> >> +    /* planes */
>> >> +    u8 plane_num;
>> >> +    const struct vs_plane_info *planes;
>> >> +
>> >> +    u8 layer_num;
>> >> +    unsigned int max_bpc;
>> >> +    unsigned int color_formats;
>> >> +
>> >> +    /* 0 means no gamma LUT */
>> >> +    u16 gamma_size;
>> >> +    u8 gamma_bits;
>> >> +
>> >> +    u16 pitch_alignment;
>> >> +
>> >> +    bool pipe_sync;
>> >> +    bool background;
>> >> +    bool panel_sync;
>> >> +    bool cap_dec;
>> >> +};
>> >> +
>> >> +#endif /* __VS_TYPE_H__ */
>> >
> 
> 
> 

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-11-15 13:30           ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-11-15 13:30 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Maxime Ripard, Jagan Teki, Rob Herring,
	Chris Morgan, Paul Walmsley, Bjorn Andersson, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Shawn Guo, christian.koenig



On 2023/11/14 18:59, Dmitry Baryshkov wrote:
> On Tue, 14 Nov 2023 at 12:42, Keith Zhao <keith.zhao@starfivetech.com> wrote:
>>
>>
>>
>> On 2023/10/26 3:28, Dmitry Baryshkov wrote:
>> > On 25/10/2023 13:39, Keith Zhao wrote:
>> >> add 2 crtcs and 8 planes in vs-drm
>> >>
>> >> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>> >> ---
>> >>   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
>> >>   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
>> >>   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
>> >>   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
>> >>   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
>> >>   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
>> >>   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
>> >>   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
>> >>   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
>> >>   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
>> >>   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
>> >>   11 files changed, 4494 insertions(+), 2 deletions(-)
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
>> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
>> >>
>> >> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
>> >> index 7d3be305b..1d48016ca 100644
>> >> --- a/drivers/gpu/drm/verisilicon/Makefile
>> >> +++ b/drivers/gpu/drm/verisilicon/Makefile
>> >> @@ -1,7 +1,11 @@
>> >>   # SPDX-License-Identifier: GPL-2.0
>> >>
>> >> -vs_drm-objs := vs_drv.o \
>> >> -            vs_modeset.o
>> >> +vs_drm-objs := vs_dc_hw.o \
>> >> +            vs_dc.o \
>> >> +            vs_crtc.o \
>> >> +            vs_drv.o \
>> >> +            vs_modeset.o \
>> >> +            vs_plane.o
>> >>
>> >>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
>> >>
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> >> new file mode 100644
>> >> index 000000000..8a658ea77
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> >> @@ -0,0 +1,257 @@
>> >> +// SPDX-License-Identifier: GPL-2.0
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + *
>> >> + */
>> >> +
>> >> +#include <linux/clk.h>
>> >> +#include <linux/debugfs.h>
>> >> +#include <linux/media-bus-format.h>
>> >> +
>> >> +#include <drm/drm_atomic_helper.h>
>> >> +#include <drm/drm_atomic.h>
>> >> +#include <drm/drm_crtc.h>
>> >> +#include <drm/drm_gem_atomic_helper.h>
>> >> +#include <drm/drm_vblank.h>
>> >> +#include <drm/vs_drm.h>
>> >> +
>> >> +#include "vs_crtc.h"
>> >> +#include "vs_dc.h"
>> >> +#include "vs_drv.h"
>> >> +
>> >> +static void vs_crtc_reset(struct drm_crtc *crtc)
>> >> +{
>> >> +    struct vs_crtc_state *state;
>> >> +
>> >> +    if (crtc->state) {
>> >> +            __drm_atomic_helper_crtc_destroy_state(crtc->state);
>> >> +
>> >> +            state = to_vs_crtc_state(crtc->state);
>> >> +            kfree(state);
>> >> +            crtc->state = NULL;
>> >> +    }
>> >
>> > You can call your crtc_destroy_state function directly here.
>> >
>> >> +
>> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
>> >> +    if (!state)
>> >> +            return;
>> >> +
>> >> +    __drm_atomic_helper_crtc_reset(crtc, &state->base);
>> >> +}
>> >> +
>> >> +static struct drm_crtc_state *
>> >> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
>> >> +{
>> >> +    struct vs_crtc_state *ori_state;
>> >
>> > It might be a matter of taste, but it is usually old_state.
>> >
>> >> +    struct vs_crtc_state *state;
>> >> +
>> >> +    if (!crtc->state)
>> >> +            return NULL;
>> >> +
>> >> +    ori_state = to_vs_crtc_state(crtc->state);
>> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
>> >> +    if (!state)
>> >> +            return NULL;
>> >> +
>> >> +    __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
>> >> +
>> >> +    state->output_fmt = ori_state->output_fmt;
>> >> +    state->encoder_type = ori_state->encoder_type;
>> >> +    state->bpp = ori_state->bpp;
>> >> +    state->underflow = ori_state->underflow;
>> >
>> > Can you use kmemdup instead?
>> >
>> >> +
>> >> +    return &state->base;
>> >> +}
>> >> +
>> >> +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
>> >> +                                     struct drm_crtc_state *state)
>> >> +{
>> >> +    __drm_atomic_helper_crtc_destroy_state(state);
>> >> +    kfree(to_vs_crtc_state(state));
>> >> +}
>> >> +
>> >> +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
>> >> +{
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    vs_dc_enable_vblank(dc, true);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
>> >> +{
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    vs_dc_enable_vblank(dc, false);
>> >> +}
>> >> +
>> >> +static const struct drm_crtc_funcs vs_crtc_funcs = {
>> >> +    .set_config             = drm_atomic_helper_set_config,
>> >> +    .page_flip              = drm_atomic_helper_page_flip,
>> >
>> > destroy is required, see drm_mode_config_cleanup()
>>
>> hi Dmitry:
>> if define destroy in drm_crtc_funcs,
>> it will make __drmm_crtc_init_with_planes unhappy
> 
> Ack, I missed that you have been using drmm_crtc_init. BTW, I checked
> your code, you should be able to switch drm
> drmm_crtc_alloc_with_planes().
> 
yes 
I done the replace and it can work well.

>>
>> see:
>> __printf(6, 0)
>> static int __drmm_crtc_init_with_planes(struct drm_device *dev,
>>                                         struct drm_crtc *crtc,
>>                                         struct drm_plane *primary,
>>                                         struct drm_plane *cursor,
>>                                         const struct drm_crtc_funcs *funcs,
>>                                         const char *name,
>>                                         va_list args)
>> {
>>         int ret;
>>
>>         drm_WARN_ON(dev, funcs && funcs->destroy);
>>
>> ........
>> }
>>
>> It should not need to be defined here, I think
>>
>> >
>> >> +    .reset                  = vs_crtc_reset,
>> >> +    .atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
>> >> +    .atomic_destroy_state   = vs_crtc_atomic_destroy_state,
>> >
>> > please consider adding atomic_print_state to output driver-specific bits.
>> >
>> >> +    .enable_vblank          = vs_crtc_enable_vblank,
>> >> +    .disable_vblank         = vs_crtc_disable_vblank,
>> >> +};
>> >> +
>> >> +static u8 cal_pixel_bits(u32 bus_format)
>> >
>> > This looks like a generic helper code, which can go to a common place.
I don't know if I understand correctly
Here I remove static
Add 2 lines in vs_drv.h

/* vs_crtc.c */
u8 cal_pixel_bits(u32 bus_format);

to make it common 

>> >
>> >> +{
>> >> +    u8 bpp;
>> >> +
>> >> +    switch (bus_format) {
>> >> +    case MEDIA_BUS_FMT_RGB565_1X16:
>> >> +    case MEDIA_BUS_FMT_UYVY8_1X16:
>> >> +            bpp = 16;
>> >> +            break;
>> >> +    case MEDIA_BUS_FMT_RGB666_1X18:
>> >> +    case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
>> >> +            bpp = 18;
>> >> +            break;
>> >> +    case MEDIA_BUS_FMT_UYVY10_1X20:
>> >> +            bpp = 20;
>> >> +            break;
>> >> +    case MEDIA_BUS_FMT_BGR888_1X24:
>> >> +    case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
>> >> +    case MEDIA_BUS_FMT_YUV8_1X24:
>> >> +            bpp = 24;
>> >> +            break;
>> >> +    case MEDIA_BUS_FMT_RGB101010_1X30:
>> >> +    case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
>> >> +    case MEDIA_BUS_FMT_YUV10_1X30:
>> >> +            bpp = 30;
>> >> +            break;
>> >> +    default:
>> >> +            bpp = 24;
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    return bpp;
>> >> +}
>> >> +
>> >> +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
>> >> +                              struct drm_atomic_state *state)
>> >> +{
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
>> >> +
>> >> +    vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
>> >> +
>> >> +    vs_dc_enable(dc, crtc);
>> >> +    drm_crtc_vblank_on(crtc);
>> >> +}
>> >> +
>> >> +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
>> >> +                               struct drm_atomic_state *state)
>> >> +{
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    drm_crtc_vblank_off(crtc);
>> >> +
>> >> +    vs_dc_disable(dc, crtc);
>> >> +
>> >> +    if (crtc->state->event && !crtc->state->active) {
>> >> +            spin_lock_irq(&crtc->dev->event_lock);
>> >> +            drm_crtc_send_vblank_event(crtc, crtc->state->event);
>> >> +            spin_unlock_irq(&crtc->dev->event_lock);
>> >> +
>> >> +            crtc->state->event = NULL;
>> >
>> > I think even should be cleared within the lock.
>> >
>> >> +    }
>> >> +}
>> >> +
>> >> +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
>> >> +                             struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
>> >> +                                                                      crtc);
>> >> +
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct device *dev = vs_crtc->dev;
>> >> +    struct drm_property_blob *blob = crtc->state->gamma_lut;
>> >> +    struct drm_color_lut *lut;
>> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
>> >> +
>> >> +    if (crtc_state->color_mgmt_changed) {
>> >> +            if (blob && blob->length) {
>> >> +                    lut = blob->data;
>> >> +                    vs_dc_set_gamma(dc, crtc, lut,
>> >> +                                    blob->length / sizeof(*lut));
>> >> +                    vs_dc_enable_gamma(dc, crtc, true);
>> >> +            } else {
>> >> +                    vs_dc_enable_gamma(dc, crtc, false);
>> >> +            }
>> >> +    }
>> >> +}
>> >> +
>> >> +static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
>> >> +                             struct drm_atomic_state *state)
>> >> +{
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct drm_pending_vblank_event *event = crtc->state->event;
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    vs_dc_commit(dc);
>> >> +
>> >> +    if (event) {
>> >> +            WARN_ON(drm_crtc_vblank_get(crtc) != 0);
>> >> +
>> >> +            spin_lock_irq(&crtc->dev->event_lock);
>> >> +            drm_crtc_arm_vblank_event(crtc, event);
>> >> +            spin_unlock_irq(&crtc->dev->event_lock);
>> >> +            crtc->state->event = NULL;
>> >
>> > I think even should be cleared within the lock.
>> >
>> >> +    }
>> >> +}
>> >> +
>> >> +static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
>> >> +    .atomic_check = drm_crtc_helper_atomic_check,
>> >> +    .atomic_enable  = vs_crtc_atomic_enable,
>> >> +    .atomic_disable = vs_crtc_atomic_disable,
>> >> +    .atomic_begin   = vs_crtc_atomic_begin,
>> >> +    .atomic_flush   = vs_crtc_atomic_flush,
>> >> +};
>> >> +
>> >> +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
>> >> +    { VS_SINGLE_DC,                         "single dc mode" },
>> >> +    { VS_MULTI_DC_PRIMARY,          "primary dc for multi dc mode" },
>> >> +    { VS_MULTI_DC_SECONDARY,        "secondary dc for multi dc mode" },
>> >> +};
>> >> +
>> >> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
>> >> +                           struct vs_dc_info *info)
>> >> +{
>> >> +    struct vs_crtc *crtc;
>> >> +    int ret;
>> >> +
>> >> +    if (!info)
>> >> +            return NULL;
>> >> +
>> >> +    crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
>> >> +    if (!crtc)
>> >> +            return NULL;
>> >> +
>> >> +    ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
>> >> +                                     NULL, NULL, &vs_crtc_funcs,
>> >> +                                     info->name ? info->name : NULL);
>> >
>> > It might be better to add drmm_crtc_init() helper.
	drmm_crtc_alloc_with_planes used:
	...
	struct vs_crtc *crtc;
	int ret;

	if (!info)
		return NULL;

	crtc = drmm_crtc_alloc_with_planes(drm_dev, struct vs_crtc, base,
					       NULL, NULL,
					       &vs_crtc_funcs, info->name ? info->name : NULL);
	...

>> >
>> >> +    if (ret)
>> >> +            return NULL;
>> >> +
>> >> +    drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
>> >> +
>> >> +    if (info->gamma_size) {
>> >> +            ret = drm_mode_crtc_set_gamma_size(&crtc->base,
>> >> +                                               info->gamma_size);
>> >> +            if (ret)
>> >> +                    return NULL;
>> >> +
>> >> +            drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
>> >> +                                       info->gamma_size);
>> >> +    }
>> >> +
>> >> +    crtc->max_bpc = info->max_bpc;
>> >> +    crtc->color_formats = info->color_formats;
>> >> +    return crtc;
>> >> +}
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h
>> >> new file mode 100644
>> >> index 000000000..526dd63e5
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.h
>> >> @@ -0,0 +1,43 @@
>> >> +/* SPDX-License-Identifier: GPL-2.0 */
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#ifndef __VS_CRTC_H__
>> >> +#define __VS_CRTC_H__
>> >> +
>> >> +#include <drm/drm_crtc.h>
>> >> +#include <drm/drm_crtc_helper.h>
>> >> +
>> >> +#include "vs_type.h"
>> >> +
>> >> +struct vs_crtc_state {
>> >> +    struct drm_crtc_state base;
>> >> +
>> >> +    u32 output_fmt;
>> >> +    u8 encoder_type;
>> >> +    u8 bpp;
>> >> +    bool underflow;
>> >> +};
>> >> +
>> >> +struct vs_crtc {
>> >> +    struct drm_crtc base;
>> >> +    struct device *dev;
>> >> +    unsigned int max_bpc;
>> >> +    unsigned int color_formats;
>> >> +};
>> >> +
>> >> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
>> >> +                           struct vs_dc_info *info);
>> >> +
>> >> +static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc)
>> >> +{
>> >> +    return container_of(crtc, struct vs_crtc, base);
>> >> +}
>> >> +
>> >> +static inline struct vs_crtc_state *
>> >> +to_vs_crtc_state(struct drm_crtc_state *state)
>> >> +{
>> >> +    return container_of(state, struct vs_crtc_state, base);
>> >> +}
>> >> +#endif /* __VS_CRTC_H__ */
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
>> >> new file mode 100644
>> >> index 000000000..b5ab92d98
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc.c
>> >> @@ -0,0 +1,1002 @@
>> >> +// SPDX-License-Identifier: GPL-2.0
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#include <linux/component.h>
>> >> +#include <linux/clk.h>
>> >> +#include <linux/of.h>
>> >> +#include <linux/of_device.h>
>> >> +#include <linux/pm_runtime.h>
>> >> +#include <linux/reset.h>
>> >> +
>> >> +#include <drm/drm_atomic.h>
>> >> +#include <drm/drm_atomic_helper.h>
>> >> +#include <drm/drm_blend.h>
>> >> +#include <drm/drm_framebuffer.h>
>> >> +#include <drm/drm_vblank.h>
>> >> +#include <drm/vs_drm.h>
>> >> +
>> >> +#include "vs_dc_hw.h"
>> >> +#include "vs_dc.h"
>> >> +#include "vs_drv.h"
>> >> +
>> >> +static const char * const vout_clocks[] = {
>> >> +    "noc_bus",
>> >> +    "channel0",
>> >> +    "channel1",
>> >> +    "dc_core",
>> >> +    "axi_core",
>> >> +    "ahb",
>> >> +    "hdmi_tx",
>> >> +    "dc_parent",
>> >> +
>> >> +};
>> >> +
>> >> +static const char * const vout_resets[] = {
>> >> +    "axi",
>> >> +    "ahb",
>> >> +    "core",
>> >> +};
>> >> +
>> >> +static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb)
>> >> +{
>> >> +    u8 f = FORMAT_A8R8G8B8;
>> >> +
>> >> +    switch (format) {
>> >> +    case DRM_FORMAT_XRGB4444:
>> >> +    case DRM_FORMAT_RGBX4444:
>> >> +    case DRM_FORMAT_XBGR4444:
>> >> +    case DRM_FORMAT_BGRX4444:
>> >> +            f = FORMAT_X4R4G4B4;
>> >> +            break;
>> >> +    case DRM_FORMAT_ARGB4444:
>> >> +    case DRM_FORMAT_RGBA4444:
>> >> +    case DRM_FORMAT_ABGR4444:
>> >> +    case DRM_FORMAT_BGRA4444:
>> >> +            f = FORMAT_A4R4G4B4;
>> >> +            break;
>> >> +    case DRM_FORMAT_XRGB1555:
>> >> +    case DRM_FORMAT_RGBX5551:
>> >> +    case DRM_FORMAT_XBGR1555:
>> >> +    case DRM_FORMAT_BGRX5551:
>> >> +            f = FORMAT_X1R5G5B5;
>> >> +            break;
>> >> +    case DRM_FORMAT_ARGB1555:
>> >> +    case DRM_FORMAT_RGBA5551:
>> >> +    case DRM_FORMAT_ABGR1555:
>> >> +    case DRM_FORMAT_BGRA5551:
>> >> +            f = FORMAT_A1R5G5B5;
>> >> +            break;
>> >> +    case DRM_FORMAT_RGB565:
>> >> +    case DRM_FORMAT_BGR565:
>> >> +            f = FORMAT_R5G6B5;
>> >> +            break;
>> >> +    case DRM_FORMAT_XRGB8888:
>> >> +    case DRM_FORMAT_RGBX8888:
>> >> +    case DRM_FORMAT_XBGR8888:
>> >> +    case DRM_FORMAT_BGRX8888:
>> >> +            f = FORMAT_X8R8G8B8;
>> >> +            break;
>> >> +    case DRM_FORMAT_ARGB8888:
>> >> +    case DRM_FORMAT_RGBA8888:
>> >> +    case DRM_FORMAT_ABGR8888:
>> >> +    case DRM_FORMAT_BGRA8888:
>> >> +            f = FORMAT_A8R8G8B8;
>> >> +            break;
>> >> +    case DRM_FORMAT_YUYV:
>> >> +    case DRM_FORMAT_YVYU:
>> >> +            f = FORMAT_YUY2;
>> >> +            break;
>> >> +    case DRM_FORMAT_UYVY:
>> >> +    case DRM_FORMAT_VYUY:
>> >> +            f = FORMAT_UYVY;
>> >> +            break;
>> >> +    case DRM_FORMAT_YUV420:
>> >> +    case DRM_FORMAT_YVU420:
>> >> +            f = FORMAT_YV12;
>> >> +            break;
>> >> +    case DRM_FORMAT_NV21:
>> >> +            f = FORMAT_NV12;
>> >> +            break;
>> >> +    case DRM_FORMAT_NV16:
>> >> +    case DRM_FORMAT_NV61:
>> >> +            f = FORMAT_NV16;
>> >> +            break;
>> >> +    case DRM_FORMAT_P010:
>> >> +            f = FORMAT_P010;
>> >> +            break;
>> >> +    case DRM_FORMAT_ARGB2101010:
>> >> +    case DRM_FORMAT_RGBA1010102:
>> >> +    case DRM_FORMAT_ABGR2101010:
>> >> +    case DRM_FORMAT_BGRA1010102:
>> >> +            f = FORMAT_A2R10G10B10;
>> >> +            break;
>> >> +    case DRM_FORMAT_NV12:
>> >> +            f = FORMAT_NV12;
>> >> +            break;
>> >> +    case DRM_FORMAT_YUV444:
>> >> +            f = FORMAT_YUV444;
>> >> +            break;
>> >> +    default:
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    fb->format = f;
>> >> +}
>> >> +
>> >> +static inline void update_swizzle(u32 format, struct dc_hw_fb *fb)
>> >> +{
>> >> +    fb->swizzle = SWIZZLE_ARGB;
>> >> +    fb->uv_swizzle = 0;
>> >> +
>> >> +    switch (format) {
>> >> +    case DRM_FORMAT_RGBX4444:
>> >> +    case DRM_FORMAT_RGBA4444:
>> >> +    case DRM_FORMAT_RGBX5551:
>> >> +    case DRM_FORMAT_RGBA5551:
>> >> +    case DRM_FORMAT_RGBX8888:
>> >> +    case DRM_FORMAT_RGBA8888:
>> >> +    case DRM_FORMAT_RGBA1010102:
>> >> +            fb->swizzle = SWIZZLE_RGBA;
>> >> +            break;
>> >> +    case DRM_FORMAT_XBGR4444:
>> >> +    case DRM_FORMAT_ABGR4444:
>> >> +    case DRM_FORMAT_XBGR1555:
>> >> +    case DRM_FORMAT_ABGR1555:
>> >> +    case DRM_FORMAT_BGR565:
>> >> +    case DRM_FORMAT_XBGR8888:
>> >> +    case DRM_FORMAT_ABGR8888:
>> >> +    case DRM_FORMAT_ABGR2101010:
>> >> +            fb->swizzle = SWIZZLE_ABGR;
>> >> +            break;
>> >> +    case DRM_FORMAT_BGRX4444:
>> >> +    case DRM_FORMAT_BGRA4444:
>> >> +    case DRM_FORMAT_BGRX5551:
>> >> +    case DRM_FORMAT_BGRA5551:
>> >> +    case DRM_FORMAT_BGRX8888:
>> >> +    case DRM_FORMAT_BGRA8888:
>> >> +    case DRM_FORMAT_BGRA1010102:
>> >> +            fb->swizzle = SWIZZLE_BGRA;
>> >> +            break;
>> >> +    case DRM_FORMAT_YVYU:
>> >> +    case DRM_FORMAT_VYUY:
>> >> +    case DRM_FORMAT_NV21:
>> >> +    case DRM_FORMAT_NV61:
>> >> +            fb->uv_swizzle = 1;
>> >> +            break;
>> >> +    default:
>> >> +            break;
>> >> +    }
>> >> +}
>> >> +
>> >> +static inline void update_watermark(struct drm_property_blob *watermark,
>> >> +                                struct dc_hw_fb *fb)
>> >> +{
>> >> +    struct drm_vs_watermark *data;
>> >> +
>> >> +    fb->water_mark = 0;
>> >> +
>> >> +    if (watermark) {
>> >> +            data = watermark->data;
>> >> +            fb->water_mark = data->watermark & 0xFFFFF;
>> >> +    }
>> >> +}
>> >> +
>> >> +static inline u8 to_vs_rotation(unsigned int rotation)
>> >> +{
>> >> +    u8 rot;
>> >> +
>> >> +    switch (rotation & DRM_MODE_REFLECT_MASK) {
>> >> +    case DRM_MODE_REFLECT_X:
>> >> +            rot = FLIP_X;
>> >> +            return rot;
>> >> +    case DRM_MODE_REFLECT_Y:
>> >> +            rot = FLIP_Y;
>> >> +            return rot;
>> >> +    case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
>> >> +            rot = FLIP_XY;
>> >> +            return rot;
>> >> +    default:
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    switch (rotation & DRM_MODE_ROTATE_MASK) {
>> >> +    case DRM_MODE_ROTATE_0:
>> >> +            rot = ROT_0;
>> >> +            break;
>> >> +    case DRM_MODE_ROTATE_90:
>> >> +            rot = ROT_90;
>> >> +            break;
>> >> +    case DRM_MODE_ROTATE_180:
>> >> +            rot = ROT_180;
>> >> +            break;
>> >> +    case DRM_MODE_ROTATE_270:
>> >> +            rot = ROT_270;
>> >> +            break;
>> >> +    default:
>> >> +            rot = ROT_0;
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    return rot;
>> >> +}
>> >> +
>> >> +static inline u8 to_vs_yuv_color_space(u32 color_space)
>> >> +{
>> >> +    u8 cs;
>> >> +
>> >> +    switch (color_space) {
>> >> +    case DRM_COLOR_YCBCR_BT601:
>> >> +            cs = COLOR_SPACE_601;
>> >> +            break;
>> >> +    case DRM_COLOR_YCBCR_BT709:
>> >> +            cs = COLOR_SPACE_709;
>> >> +            break;
>> >> +    case DRM_COLOR_YCBCR_BT2020:
>> >> +            cs = COLOR_SPACE_2020;
>> >> +            break;
>> >> +    default:
>> >> +            cs = COLOR_SPACE_601;
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    return cs;
>> >> +}
>> >> +
>> >> +static inline u8 to_vs_tile_mode(u64 modifier)
>> >> +{
>> >> +    if (modifier == DRM_FORMAT_MOD_VIVANTE_TILED)
>> >> +            return DC_TILE_MODE4X4;
>> >> +
>> >> +    return (u8)(modifier & DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK);
>> >> +}
>> >> +
>> >> +static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc)
>> >> +{
>> >> +    u8 panel_num = dc->hw.info->panel_num;
>> >> +    u32 index = drm_crtc_index(crtc);
>> >> +    int i;
>> >> +
>> >> +    for (i = 0; i < panel_num; i++) {
>> >> +            if (index == dc->crtc[i]->base.index)
>> >> +                    return i;
>> >> +    }
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
>> >> +                                      unsigned int alignment)
>> >> +{
>> >> +    struct vs_drm_device *priv = to_vs_drm_private(drm_dev);
>> >> +
>> >> +    if (alignment > priv->pitch_alignment)
>> >> +            priv->pitch_alignment = alignment;
>> >> +}
>> >> +
>> >> +static int plda_clk_rst_init(struct device *dev)
>> >> +{
>> >> +    int ret = 0;
>> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
>> >> +
>> >> +    ret = clk_bulk_prepare_enable(dc->nclks, dc->clk_vout);
>> >> +    if (ret) {
>> >> +            dev_err(dev, "failed to enable clocks\n");
>> >> +            return ret;
>> >> +    }
>> >> +
>> >> +    ret = reset_control_bulk_deassert(dc->nrsts, dc->rst_vout);
>> >> +    return ret;
>> >> +}
>> >> +
>> >> +static void plda_clk_rst_deinit(struct device *dev)
>> >> +{
>> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
>> >> +
>> >> +    reset_control_bulk_assert(dc->nrsts, dc->rst_vout);
>> >> +    clk_bulk_disable_unprepare(dc->nclks, dc->clk_vout);
>> >> +}
>> >> +
>> >> +static void dc_deinit(struct device *dev)
>> >> +{
>> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
>> >> +
>> >> +    dc_hw_enable_interrupt(&dc->hw, 0);
>> >> +    dc_hw_deinit(&dc->hw);
>> >> +    plda_clk_rst_deinit(dev);
>> >> +}
>> >> +
>> >> +static int dc_init(struct device *dev)
>> >> +{
>> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
>> >> +    int ret;
>> >> +
>> >> +    dc->first_frame = true;
>> >> +
>> >> +    ret = plda_clk_rst_init(dev);
>> >> +    if (ret < 0) {
>> >> +            dev_err(dev, "failed to init dc clk reset: %d\n", ret);
>> >> +            return ret;
>> >> +    }
>> >> +
>> >> +    ret = dc_hw_init(&dc->hw);
>> >> +    if (ret) {
>> >> +            dev_err(dev, "failed to init DC HW\n");
>> >> +            return ret;
>> >> +    }
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
>> >> +{
>> >> +    struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
>> >> +    struct drm_display_mode *mode = &crtc->state->adjusted_mode;
>> >> +    struct dc_hw_display display;
>> >> +
>> >> +    display.bus_format = crtc_state->output_fmt;
>> >> +    display.h_active = mode->hdisplay;
>> >> +    display.h_total = mode->htotal;
>> >> +    display.h_sync_start = mode->hsync_start;
>> >> +    display.h_sync_end = mode->hsync_end;
>> >> +    if (mode->flags & DRM_MODE_FLAG_PHSYNC)
>> >> +            display.h_sync_polarity = true;
>> >> +    else
>> >> +            display.h_sync_polarity = false;
>> >> +
>> >> +    display.v_active = mode->vdisplay;
>> >> +    display.v_total = mode->vtotal;
>> >> +    display.v_sync_start = mode->vsync_start;
>> >> +    display.v_sync_end = mode->vsync_end;
>> >> +
>> >> +    if (mode->flags & DRM_MODE_FLAG_PVSYNC)
>> >> +            display.v_sync_polarity = true;
>> >> +    else
>> >> +            display.v_sync_polarity = false;
>> >> +
>> >> +    display.id = to_vs_display_id(dc, crtc);
>> >> +
>> >> +    display.enable = true;
>> >> +
>> >> +    if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) {
>> >> +            dc_hw_set_out(&dc->hw, OUT_DPI, display.id);
>> >> +            clk_set_rate(dc->clk_vout[CLK_VOUT_SOC_PIX].clk, mode->clock * 1000);
>> >> +            clk_set_parent(dc->clk_vout[CLK_VOUT_PIX1].clk,
>> >> +                           dc->clk_vout[CLK_VOUT_SOC_PIX].clk);
>> >> +    } else {
>> >> +            dc_hw_set_out(&dc->hw, OUT_DP, display.id);
>> >> +            clk_set_parent(dc->clk_vout[CLK_VOUT_PIX0].clk,
>> >> +                           dc->clk_vout[CLK_VOUT_HDMI_PIX].clk);
>> >> +    }
>> >> +
>> >> +    dc_hw_setup_display(&dc->hw, &display);
>> >> +}
>> >> +
>> >> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc)
>> >> +{
>> >> +    struct dc_hw_display display;
>> >> +
>> >> +    display.id = to_vs_display_id(dc, crtc);
>> >> +    display.enable = false;
>> >> +
>> >> +    dc_hw_setup_display(&dc->hw, &display);
>> >> +}
>> >> +
>> >> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
>> >> +                 struct drm_color_lut *lut, unsigned int size)
>> >> +{
>> >> +    u16 i, r, g, b;
>> >> +    u8 bits, id;
>> >> +
>> >> +    if (size != dc->hw.info->gamma_size) {
>> >> +            drm_err(crtc->dev, "gamma size does not match!\n");
>> >> +            return;
>> >> +    }
>> >> +
>> >> +    id = to_vs_display_id(dc, crtc);
>> >> +
>> >> +    bits = dc->hw.info->gamma_bits;
>> >> +    for (i = 0; i < size; i++) {
>> >> +            r = drm_color_lut_extract(lut[i].red, bits);
>> >> +            g = drm_color_lut_extract(lut[i].green, bits);
>> >> +            b = drm_color_lut_extract(lut[i].blue, bits);
>> >> +            dc_hw_update_gamma(&dc->hw, id, i, r, g, b);
>> >> +    }
>> >> +}
>> >> +
>> >> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
>> >> +                    bool enable)
>> >> +{
>> >> +    u8 id;
>> >> +
>> >> +    id = to_vs_display_id(dc, crtc);
>> >> +    dc_hw_enable_gamma(&dc->hw, id, enable);
>> >> +}
>> >> +
>> >> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable)
>> >> +{
>> >> +    dc_hw_enable_interrupt(&dc->hw, enable);
>> >> +}
>> >> +
>> >> +static u32 calc_factor(u32 src, u32 dest)
>> >> +{
>> >> +    u32 factor = 1 << 16;
>> >> +
>> >> +    if (src > 1 && dest > 1)
>> >> +            factor = ((src - 1) << 16) / (dest - 1);
>> >> +
>> >> +    return factor;
>> >> +}
>> >> +
>> >> +static void update_scale(struct drm_plane_state *state, struct dc_hw_roi *roi,
>> >> +                     struct dc_hw_scale *scale)
>> >> +{
>> >> +    int dst_w = drm_rect_width(&state->dst);
>> >> +    int dst_h = drm_rect_height(&state->dst);
>> >> +    int src_w, src_h, temp;
>> >> +
>> >> +    scale->enable = false;
>> >> +
>> >> +    if (roi->enable) {
>> >> +            src_w = roi->width;
>> >> +            src_h = roi->height;
>> >> +    } else {
>> >> +            src_w = drm_rect_width(&state->src) >> 16;
>> >> +            src_h = drm_rect_height(&state->src) >> 16;
>> >> +    }
>> >> +
>> >> +    if (drm_rotation_90_or_270(state->rotation)) {
>> >> +            temp = src_w;
>> >> +            src_w = src_h;
>> >> +            src_h = temp;
>> >> +    }
>> >> +
>> >> +    if (src_w != dst_w) {
>> >> +            scale->scale_factor_x = calc_factor(src_w, dst_w);
>> >> +            scale->enable = true;
>> >> +    } else {
>> >> +            scale->scale_factor_x = 1 << 16;
>> >> +    }
>> >> +    if (src_h != dst_h) {
>> >> +            scale->scale_factor_y = calc_factor(src_h, dst_h);
>> >> +            scale->enable = true;
>> >> +    } else {
>> >> +            scale->scale_factor_y = 1 << 16;
>> >> +    }
>> >> +}
>> >> +
>> >> +static void update_fb(struct vs_plane *plane, u8 display_id,
>> >> +                  struct dc_hw_fb *fb, struct drm_plane_state *state)
>> >> +{
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> >> +    struct drm_framebuffer *drm_fb = state->fb;
>> >> +    struct drm_rect *src = &state->src;
>> >> +
>> >> +    fb->display_id = display_id;
>> >> +    fb->y_address = plane_state->dma_addr[0];
>> >> +    fb->y_stride = drm_fb->pitches[0];
>> >> +    if (drm_fb->format->format == DRM_FORMAT_YVU420) {
>> >> +            fb->u_address = plane_state->dma_addr[2];
>> >> +            fb->v_address = plane_state->dma_addr[1];
>> >> +            fb->u_stride = drm_fb->pitches[2];
>> >> +            fb->v_stride = drm_fb->pitches[1];
>> >> +    } else {
>> >> +            fb->u_address = plane_state->dma_addr[1];
>> >> +            fb->v_address = plane_state->dma_addr[2];
>> >> +            fb->u_stride = drm_fb->pitches[1];
>> >> +            fb->v_stride = drm_fb->pitches[2];
>> >> +    }
>> >> +    fb->width = drm_rect_width(src) >> 16;
>> >> +    fb->height = drm_rect_height(src) >> 16;
>> >> +    fb->tile_mode = to_vs_tile_mode(drm_fb->modifier);
>> >> +    fb->rotation = to_vs_rotation(state->rotation);
>> >> +    fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding);
>> >> +    fb->zpos = state->zpos;
>> >> +    fb->enable = state->visible;
>> >> +    update_format(drm_fb->format->format, drm_fb->modifier, fb);
>> >> +    update_swizzle(drm_fb->format->format, fb);
>> >> +    update_watermark(plane_state->watermark, fb);
>> >> +    plane_state->status.tile_mode = fb->tile_mode;
>> >> +}
>> >> +
>> >> +static void update_degamma(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                       struct vs_plane_state *plane_state)
>> >> +{
>> >> +    dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
>> >> +    plane_state->degamma_changed = false;
>> >> +}
>> >> +
>> >> +static void update_roi(struct vs_dc *dc, u8 id,
>> >> +                   struct vs_plane_state *plane_state,
>> >> +                   struct dc_hw_roi *roi,
>> >> +                   struct drm_plane_state *state)
>> >> +{
>> >> +    struct drm_vs_roi *data;
>> >> +    struct drm_rect *src = &state->src;
>> >> +    u16 src_w = drm_rect_width(src) >> 16;
>> >> +    u16 src_h = drm_rect_height(src) >> 16;
>> >> +
>> >> +    if (plane_state->roi) {
>> >> +            data = plane_state->roi->data;
>> >> +
>> >> +            if (data->enable) {
>> >> +                    roi->x = data->roi_x;
>> >> +                    roi->y = data->roi_y;
>> >> +                    roi->width = (data->roi_x + data->roi_w > src_w) ?
>> >> +                                             (src_w - data->roi_x) : data->roi_w;
>> >> +                    roi->height = (data->roi_y + data->roi_h > src_h) ?
>> >> +                                              (src_h - data->roi_y) : data->roi_h;
>> >> +                    roi->enable = true;
>> >> +            } else {
>> >> +                    roi->enable = false;
>> >> +            }
>> >> +
>> >> +            dc_hw_update_roi(&dc->hw, id, roi);
>> >> +    } else {
>> >> +            roi->enable = false;
>> >> +    }
>> >> +}
>> >> +
>> >> +static void update_color_mgmt(struct vs_dc *dc, u8 id,
>> >> +                          struct dc_hw_fb *fb,
>> >> +                          struct vs_plane_state *plane_state)
>> >> +{
>> >> +    struct drm_vs_color_mgmt *data;
>> >> +    struct dc_hw_colorkey colorkey;
>> >> +
>> >> +    if (plane_state->color_mgmt) {
>> >> +            data = plane_state->color_mgmt->data;
>> >> +
>> >> +            fb->clear_enable = data->clear_enable;
>> >> +            fb->clear_value = data->clear_value;
>> >> +
>> >> +            if (data->colorkey > data->colorkey_high)
>> >> +                    data->colorkey = data->colorkey_high;
>> >> +
>> >> +            colorkey.colorkey = data->colorkey;
>> >> +            colorkey.colorkey_high = data->colorkey_high;
>> >> +            colorkey.transparency = (data->transparency) ?
>> >> +                            DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE;
>> >> +            dc_hw_update_colorkey(&dc->hw, id, &colorkey);
>> >> +    }
>> >> +}
>> >> +
>> >> +static void update_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                     struct drm_plane *drm_plane,
>> >> +                     struct drm_atomic_state *drm_state)
>> >> +{
>> >> +    struct dc_hw_fb fb = {0};
>> >> +    struct dc_hw_scale scale;
>> >> +    struct dc_hw_position pos;
>> >> +    struct dc_hw_blend blend;
>> >> +    struct dc_hw_roi roi;
>> >> +    struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
>> >> +                                                                       drm_plane);
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> >> +    struct drm_rect *dest = &state->dst;
>> >> +    bool dec_enable = false;
>> >> +    u8 display_id = 0;
>> >> +
>> >> +    display_id = to_vs_display_id(dc, state->crtc);
>> >> +    update_fb(plane, display_id, &fb, state);
>> >> +    fb.dec_enable = dec_enable;
>> >> +
>> >> +    update_roi(dc, plane->id, plane_state, &roi, state);
>> >> +
>> >> +    update_scale(state, &roi, &scale);
>> >> +
>> >> +    if (plane_state->degamma_changed)
>> >> +            update_degamma(dc, plane, plane_state);
>> >> +
>> >> +    pos.start_x = dest->x1;
>> >> +    pos.start_y = dest->y1;
>> >> +    pos.end_x = dest->x2;
>> >> +    pos.end_y = dest->y2;
>> >> +
>> >> +    blend.alpha = (u8)(state->alpha >> 8);
>> >> +    blend.blend_mode = (u8)(state->pixel_blend_mode);
>> >> +
>> >> +    update_color_mgmt(dc, plane->id, &fb, plane_state);
>> >> +
>> >> +    dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
>> >> +}
>> >> +
>> >> +static void update_qos(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                   struct drm_plane *drm_plane,
>> >> +                   struct drm_atomic_state *drm_state)
>> >> +{
>> >> +    struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
>> >> +                                                                       drm_plane);
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> >> +    struct drm_vs_watermark *data;
>> >> +    struct dc_hw_qos qos;
>> >> +
>> >> +    if (plane_state->watermark) {
>> >> +            data = plane_state->watermark->data;
>> >> +
>> >> +            if (data->qos_high) {
>> >> +                    if (data->qos_low > data->qos_high)
>> >> +                            data->qos_low = data->qos_high;
>> >> +
>> >> +                    qos.low_value = data->qos_low & 0x0F;
>> >> +                    qos.high_value = data->qos_high & 0x0F;
>> >> +                    dc_hw_update_qos(&dc->hw, &qos);
>> >> +            }
>> >> +    }
>> >> +}
>> >> +
>> >> +static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor)
>> >> +{
>> >> +    u8 size_type;
>> >> +
>> >> +    switch (state->crtc_w) {
>> >> +    case 32:
>> >> +            size_type = CURSOR_SIZE_32X32;
>> >> +            break;
>> >> +    case 64:
>> >> +            size_type = CURSOR_SIZE_64X64;
>> >> +            break;
>> >> +    default:
>> >> +            size_type = CURSOR_SIZE_32X32;
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    cursor->size = size_type;
>> >> +}
>> >> +
>> >> +static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                            struct drm_plane *drm_plane,
>> >> +                            struct drm_atomic_state *drm_state)
>> >> +{
>> >> +    struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
>> >> +                                                                   drm_plane);
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(state);
>> >> +    struct drm_framebuffer *drm_fb = state->fb;
>> >> +    struct dc_hw_cursor cursor;
>> >> +
>> >> +    cursor.address = plane_state->dma_addr[0];
>> >> +    cursor.x = state->crtc_x;
>> >> +    cursor.y = state->crtc_y;
>> >> +    cursor.hot_x = drm_fb->hot_x;
>> >> +    cursor.hot_y = drm_fb->hot_y;
>> >> +    cursor.display_id = to_vs_display_id(dc, state->crtc);
>> >> +    update_cursor_size(state, &cursor);
>> >> +    cursor.enable = true;
>> >> +
>> >> +    dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
>> >> +}
>> >> +
>> >> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                    struct drm_plane *drm_plane,
>> >> +                    struct drm_atomic_state *drm_state)
>> >> +{
>> >> +    update_plane(dc, plane, drm_plane, drm_state);
>> >> +    update_qos(dc, plane, drm_plane, drm_state);
>> >> +}
>> >> +
>> >> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                           struct drm_plane *drm_plane,
>> >> +                           struct drm_atomic_state *drm_state)
>> >> +{
>> >> +    update_cursor_plane(dc, plane, drm_plane, drm_state);
>> >> +}
>> >> +
>> >> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                     struct drm_plane_state *old_state)
>> >> +{
>> >> +    struct dc_hw_fb fb = {0};
>> >> +
>> >> +    fb.enable = false;
>> >> +    dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL, NULL, NULL);
>> >> +}
>> >> +
>> >> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                            struct drm_plane_state *old_state)
>> >> +{
>> >> +    struct dc_hw_cursor cursor = {0};
>> >> +
>> >> +    cursor.enable = false;
>> >> +    cursor.display_id = to_vs_display_id(dc, old_state->crtc);
>> >> +    dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
>> >> +}
>> >> +
>> >> +static bool vs_dc_mod_supported(const struct vs_plane_info *plane_info,
>> >> +                            u64 modifier)
>> >> +{
>> >> +    const u64 *mods;
>> >> +
>> >> +    if (!plane_info->modifiers)
>> >> +            return false;
>> >> +
>> >> +    for (mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID; mods++) {
>> >> +            if (*mods == modifier)
>> >> +                    return true;
>> >> +    }
>> >> +
>> >> +    return false;
>> >> +}
>> >> +
>> >> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
>> >> +                  struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> >> +                                                                             plane);
>> >> +
>> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
>> >> +    const struct vs_plane_info *plane_info;
>> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
>> >> +    struct drm_crtc_state *crtc_state;
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +
>> >> +    plane_info = &dc->hw.info->planes[vs_plane->id];
>> >> +
>> >> +    if (fb->width < plane_info->min_width ||
>> >> +        fb->width > plane_info->max_width ||
>> >> +        fb->height < plane_info->min_height ||
>> >> +        fb->height > plane_info->max_height)
>> >> +            drm_err_once(plane->dev, "buffer size may not support on plane%d.\n",
>> >> +                         vs_plane->id);
>> >> +
>> >> +    if (!vs_dc_mod_supported(plane_info, fb->modifier)) {
>> >> +            drm_err(plane->dev, "unsupported modifier on plane%d.\n", vs_plane->id);
>> >> +            return -EINVAL;
>> >> +    }
>> >> +
>> >> +    crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
>> >> +    return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
>> >> +                                              plane_info->min_scale,
>> >> +                                              plane_info->max_scale,
>> >> +                                              true, true);
>> >> +}
>> >> +
>> >> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
>> >> +                         struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> >> +                                                                       plane);
>> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
>> >> +    const struct vs_plane_info *plane_info;
>> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
>> >> +    struct drm_crtc_state *crtc_state;
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +
>> >> +    plane_info = &dc->hw.info->planes[vs_plane->id];
>> >> +
>> >> +    if (fb->width < plane_info->min_width ||
>> >> +        fb->width > plane_info->max_width ||
>> >> +        fb->height < plane_info->min_height ||
>> >> +        fb->height > plane_info->max_height)
>> >> +            drm_err_once(plane->dev, "buffer size may not support on plane%d.\n", vs_plane->id);
>> >> +
>> >> +    crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
>> >> +    if (IS_ERR(crtc_state))
>> >> +            return -EINVAL;
>> >> +
>> >> +    return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
>> >> +                                            plane_info->min_scale,
>> >> +                                            plane_info->max_scale,
>> >> +                                            true, true);
>> >> +}
>> >> +
>> >> +static void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
>> >> +{
>> >> +    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
>> >> +
>> >> +    drm_crtc_handle_vblank(crtc);
>> >> +
>> >> +    vs_crtc_state->underflow = underflow;
>> >> +}
>> >> +
>> >> +static irqreturn_t dc_isr(int irq, void *data)
>> >> +{
>> >> +    struct vs_dc *dc = data;
>> >> +    struct vs_dc_info *dc_info = dc->hw.info;
>> >> +    u32 i, ret;
>> >> +
>> >> +    ret = dc_hw_get_interrupt(&dc->hw);
>> >> +
>> >> +    for (i = 0; i < dc_info->panel_num; i++)
>> >> +            vs_crtc_handle_vblank(&dc->crtc[i]->base, dc_hw_check_underflow(&dc->hw));
>> >> +
>> >> +    return IRQ_HANDLED;
>> >> +}
>> >> +
>> >> +void vs_dc_commit(struct vs_dc *dc)
>> >> +{
>> >> +    dc_hw_enable_shadow_register(&dc->hw, false);
>> >> +
>> >> +    dc_hw_commit(&dc->hw);
>> >> +
>> >> +    if (dc->first_frame)
>> >> +            dc->first_frame = false;
>> >> +
>> >> +    dc_hw_enable_shadow_register(&dc->hw, true);
>> >> +}
>> >> +
>> >> +static int dc_bind(struct device *dev, struct device *master, void *data)
>> >> +{
>> >> +    struct drm_device *drm_dev = data;
>> >> +    struct vs_dc *dc = dev_get_drvdata(dev);
>> >> +    struct device_node *port;
>> >> +    struct vs_crtc *crtc;
>> >> +    struct vs_dc_info *dc_info;
>> >> +    struct vs_plane *plane;
>> >> +    struct vs_plane_info *plane_info;
>> >> +    int i, ret;
>> >> +    u32 ctrc_mask = 0;
>> >> +
>> >> +    if (!drm_dev || !dc) {
>> >> +            dev_err(dev, "devices are not created.\n");
>> >> +            return -ENODEV;
>> >> +    }
>> >> +
>> >> +    ret = dc_init(dev);
>> >> +    if (ret < 0) {
>> >> +            drm_err(drm_dev, "Failed to initialize DC hardware.\n");
>> >> +            return ret;
>> >> +    }
>> >> +
>> >> +    port = of_get_child_by_name(dev->of_node, "port");
>> >> +    if (!port) {
>> >> +            drm_err(drm_dev, "no port node found\n");
>> >> +            return -ENODEV;
>> >> +    }
>> >> +    of_node_put(port);
>> >> +
>> >> +    dc_info = dc->hw.info;
>> >> +
>> >> +    for (i = 0; i < dc_info->panel_num; i++) {
>> >> +            crtc = vs_crtc_create(drm_dev, dc_info);
>> >> +            if (!crtc) {
>> >> +                    drm_err(drm_dev, "Failed to create CRTC.\n");
>> >> +                    ret = -ENOMEM;
>> >> +                    return ret;
>> >> +            }
>> >> +
>> >> +            crtc->base.port = port;
>> >> +            crtc->dev = dev;
>> >> +            dc->crtc[i] = crtc;
>> >> +            ctrc_mask |= drm_crtc_mask(&crtc->base);
>> >> +    }
>> >> +
>> >> +    for (i = 0; i < dc_info->plane_num; i++) {
>> >> +            plane_info = (struct vs_plane_info *)&dc_info->planes[i];
>> >> +
>> >> +            if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
>> >> +                    plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
>> >> +                                            drm_crtc_mask(&dc->crtc[0]->base));
>> >> +            } else if (!strcmp(plane_info->name, "Primary_1") ||
>> >> +                               !strcmp(plane_info->name, "Cursor_1")) {
>> >> +                    plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
>> >> +                                            drm_crtc_mask(&dc->crtc[1]->base));
>> >
>> > I'd say, it's not typical to use name to distinguish objects. Maybe you
>> > can add indices to the info structure and drop the names instead?
>> >
>> >
>> >> +            } else {
>> >> +                    plane = vs_plane_create(drm_dev, plane_info,
>> >> +                                            dc_info->layer_num, ctrc_mask);
>> >> +            }
>> >> +
>> >> +            if (IS_ERR(plane)) {
>> >> +                    dev_err(dev, "failed to construct plane\n");
>> >> +                    return PTR_ERR(plane);
>> >> +            }
>> >> +
>> >> +            plane->id = i;
>> >> +            dc->planes[i].id = plane_info->id;
>> >> +
>> >> +            if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
>> >> +                    if (!strcmp(plane_info->name, "Primary"))
>> >> +                            dc->crtc[0]->base.primary = &plane->base;
>> >> +                    else
>> >> +                            dc->crtc[1]->base.primary = &plane->base;
>> >
>> > This definitely urges for an index in the plane info / plane_state.
>> >
>> >> +                    drm_dev->mode_config.min_width = plane_info->min_width;
>> >> +                    drm_dev->mode_config.min_height =
>> >> +                                                    plane_info->min_height;
>> >> +                    drm_dev->mode_config.max_width = plane_info->max_width;
>> >> +                    drm_dev->mode_config.max_height =
>> >> +                                                    plane_info->max_height;
>> >> +            }
>> >> +
>> >> +            if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
>> >> +                    if (!strcmp(plane_info->name, "Cursor"))
>> >> +                            dc->crtc[0]->base.cursor = &plane->base;
>> >> +                    else
>> >> +                            dc->crtc[1]->base.cursor = &plane->base;
>> >> +                    drm_dev->mode_config.cursor_width =
>> >> +                                                    plane_info->max_width;
>> >> +                    drm_dev->mode_config.cursor_height =
>> >> +                                                    plane_info->max_height;
>> >> +            }
>> >> +    }
>> >> +
>> >> +    vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static void dc_unbind(struct device *dev, struct device *master, void *data)
>> >> +{
>> >> +    dc_deinit(dev);
>> >> +}
>> >> +
>> >> +const struct component_ops dc_component_ops = {
>> >> +    .bind = dc_bind,
>> >> +    .unbind = dc_unbind,
>> >> +};
>> >> +
>> >> +static const struct of_device_id dc_driver_dt_match[] = {
>> >> +    { .compatible = "starfive,jh7110-dc8200", },
>> >> +    {},
>> >> +};
>> >> +MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
>> >> +
>> >> +static int dc_probe(struct platform_device *pdev)
>> >> +{
>> >> +    struct device *dev = &pdev->dev;
>> >> +    struct vs_dc *dc;
>> >> +    int irq, ret, i;
>> >> +
>> >> +    dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
>> >> +    if (!dc)
>> >> +            return -ENOMEM;
>> >> +
>> >> +    dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
>> >> +    if (IS_ERR(dc->hw.hi_base))
>> >> +            return PTR_ERR(dc->hw.hi_base);
>> >> +
>> >> +    dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
>> >> +    if (IS_ERR(dc->hw.reg_base))
>> >> +            return PTR_ERR(dc->hw.reg_base);
>> >> +
>> >> +    dc->nclks = ARRAY_SIZE(dc->clk_vout);
>> >> +    for (i = 0; i < dc->nclks; ++i)
>> >> +            dc->clk_vout[i].id = vout_clocks[i];
>> >> +    ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
>> >> +    if (ret) {
>> >> +            dev_err(dev, "Failed to get clk controls\n");
>> >> +            return ret;
>> >> +    }
>> >> +
>> >> +    dc->nrsts = ARRAY_SIZE(dc->rst_vout);
>> >> +    for (i = 0; i < dc->nrsts; ++i)
>> >> +            dc->rst_vout[i].id = vout_resets[i];
>> >> +    ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
>> >> +                                             dc->rst_vout);
>> >> +    if (ret) {
>> >> +            dev_err(dev, "Failed to get reset controls\n");
>> >> +            return ret;
>> >> +    }
>> >> +
>> >> +    irq = platform_get_irq(pdev, 0);
>> >> +
>> >> +    ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
>> >
>> > Are you ready to handle the IRQ at this point?
Do you have some good suggestions?
Are there any hidden dangers in doing so?
Hope to get good opinions 

>> >
>> >> +    if (ret < 0) {
>> >> +            dev_err(dev, "Failed to install irq:%u.\n", irq);
>> >> +            return ret;
>> >> +    }
>> >> +
>> >> +    dev_set_drvdata(dev, dc);
>> >> +
>> >> +    return component_add(dev, &dc_component_ops);
>> >> +}
>> >> +
>> >> +static int dc_remove(struct platform_device *pdev)
>> >> +{
>> >> +    struct device *dev = &pdev->dev;
>> >> +
>> >> +    component_del(dev, &dc_component_ops);
>> >> +
>> >> +    dev_set_drvdata(dev, NULL);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +struct platform_driver dc_platform_driver = {
>> >> +    .probe = dc_probe,
>> >> +    .remove = dc_remove,
>> >> +    .driver = {
>> >> +            .name = "vs-dc",
>> >> +            .of_match_table = of_match_ptr(dc_driver_dt_match),
>> >> +    },
>> >> +};
>> >> +
>> >> +MODULE_AUTHOR("StarFive Corporation");
>> >> +MODULE_DESCRIPTION("VeriSilicon DC Driver");
>> >> +MODULE_LICENSE("GPL");
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
>> >> new file mode 100644
>> >> index 000000000..8e96fd32c
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc.h
>> >> @@ -0,0 +1,80 @@
>> >> +/* SPDX-License-Identifier: GPL-2.0 */
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#ifndef __VS_DC_H__
>> >> +#define __VS_DC_H__
>> >> +
>> >> +#include <linux/clk.h>
>> >> +#include <linux/mm_types.h>
>> >> +#include <linux/reset.h>
>> >> +
>> >> +#include <drm/drm_fourcc.h>
>> >> +#include <drm/drm_modes.h>
>> >> +
>> >> +#include "vs_crtc.h"
>> >> +#include "vs_dc_hw.h"
>> >> +#include "vs_plane.h"
>> >> +
>> >> +struct vs_dc_plane {
>> >> +    enum dc_hw_plane_id id;
>> >> +};
>> >> +
>> >> +enum vout_clk {
>> >> +    CLK_VOUT_NOC_DISP = 0,
>> >> +    CLK_VOUT_PIX0,
>> >> +    CLK_VOUT_PIX1,
>> >> +    CLK_VOUT_CORE,
>> >> +    CLK_VOUT_AXI,
>> >> +    CLK_VOUT_AHB,
>> >> +    CLK_VOUT_HDMI_PIX,
>> >> +    CLK_VOUT_SOC_PIX,
>> >> +    CLK_VOUT_NUM
>> >> +};
>> >> +
>> >> +enum rst_vout {
>> >> +    RST_VOUT_AXI = 0,
>> >> +    RST_VOUT_AHB,
>> >> +    RST_VOUT_CORE,
>> >> +    RST_VOUT_NUM
>> >> +};
>> >> +
>> >> +struct vs_dc {
>> >> +    struct vs_crtc          *crtc[DC_DISPLAY_NUM];
>> >> +    struct dc_hw            hw;
>> >> +    bool                    first_frame;
>> >> +
>> >> +    struct vs_dc_plane      planes[PLANE_NUM];
>> >> +    struct clk_bulk_data    clk_vout[CLK_VOUT_NUM];
>> >> +    int                     nclks;
>> >> +    struct reset_control_bulk_data rst_vout[RST_VOUT_NUM];
>> >> +    int                     nrsts;
>> >> +};
>> >> +
>> >> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc);
>> >> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc);
>> >> +
>> >> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
>> >> +                 struct drm_color_lut *lut, unsigned int size);
>> >> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc, bool enable);
>> >> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable);
>> >> +void vs_dc_commit(struct vs_dc *dc);
>> >> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                    struct drm_plane *drm_plane,
>> >> +                    struct drm_atomic_state *drm_state);
>> >> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                     struct drm_plane_state *old_state);
>> >> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
>> >> +                  struct drm_atomic_state *state);
>> >> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                           struct drm_plane *drm_plane,
>> >> +                           struct drm_atomic_state *drm_state);
>> >> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
>> >> +                            struct drm_plane_state *old_state);
>> >> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
>> >> +                         struct drm_atomic_state *state);
>> >> +
>> >> +extern struct platform_driver dc_platform_driver;
>> >> +
>> >> +#endif /* __VS_DC_H__ */
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
>> >> new file mode 100644
>> >> index 000000000..e5acb7eb4
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
>> >> @@ -0,0 +1,1959 @@
>> >> +// SPDX-License-Identifier: GPL-2.0
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#include <linux/bits.h>
>> >> +#include <linux/io.h>
>> >> +#include <linux/media-bus-format.h>
>> >> +#include <drm/drm_atomic_helper.h>
>> >> +#include <drm/drm_blend.h>
>> >> +#include <drm/drm_fourcc.h>
>> >> +#include <drm/vs_drm.h>
>> >> +
>> >> +#include "vs_dc_hw.h"
>> >> +#include "vs_type.h"
>> >> +
>> >> +static const u32 horkernel[] = {
>> >> +    0x00000000, 0x20000000, 0x00002000, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x2b981468,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x10f00000,
>> >> +    0x00002f10, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x3781087f, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x06660000, 0x0000399a, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x01470000,
>> >> +    0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x00004000, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x20002000, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x27e1181f,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x14680000,
>> >> +    0x00002b98, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x2f1010f0, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x0dc70000, 0x00003239, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x087f0000, 0x00003781, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x399a0666, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x3cc4033c,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x021f0000,
>> >> +};
>> >> +
>> >> +#define H_COEF_SIZE (sizeof(horkernel) / sizeof(u32))
>> >> +
>> >> +static const u32 verkernel[] = {
>> >> +    0x00000000, 0x20000000, 0x00002000, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x2b981468,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x10f00000,
>> >> +    0x00002f10, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x3781087f, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x06660000, 0x0000399a, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
>> >> +    0x00000000, 0x00000000, 0x00000000, 0x01470000,
>> >> +    0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
>> >> +    0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
>> >> +    0x00000000, 0x00000000, 0x00004000, 0x00000000,
>> >> +    0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab,
>> >> +    0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
>> >> +    0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
>> >> +};
>> >> +
>> >> +#define V_COEF_SIZE (sizeof(verkernel) / sizeof(u32))
>> >> +
>> >> +/*
>> >> + * RGB 709->2020 conversion parameters
>> >> + */
>> >> +static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = {
>> >> +    10279,  5395,   709,
>> >> +    1132,   15065,  187,
>> >> +    269,    1442,   14674
>> >> +};
>> >> +
>> >> +/*
>> >> + * YUV601 to RGB conversion parameters
>> >> + * YUV2RGB[0]  - [8] : C0 - C8;
>> >> + * YUV2RGB[9]  - [11]: D0 - D2;
>> >> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
>> >> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
>> >> + */
>> >> +static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
>> >> +    1196,   0,                      1640,   1196,
>> >> +    -404,   -836,           1196,   2076,
>> >> +    0,              -916224,        558336, -1202944,
>> >> +    64,              940,           64,             960
>> >> +};
>> >> +
>> >> +/*
>> >> + * YUV709 to RGB conversion parameters
>> >> + * YUV2RGB[0]  - [8] : C0 - C8;
>> >> + * YUV2RGB[9]  - [11]: D0 - D2;
>> >> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
>> >> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
>> >> + */
>> >> +static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
>> >> +    1196,           0,              1844,   1196,
>> >> +    -220,           -548,   1196,   2172,
>> >> +    0,                      -1020672, 316672,  -1188608,
>> >> +    64,                     940,            64,             960
>> >> +};
>> >> +
>> >> +/*
>> >> + * YUV2020 to RGB conversion parameters
>> >> + * YUV2RGB[0]  - [8] : C0 - C8;
>> >> + * YUV2RGB[9]  - [11]: D0 - D2;
>> >> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
>> >> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
>> >> + */
>> >> +static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
>> >> +    1196, 0, 1724, 1196,
>> >> +    -192, -668, 1196, 2200,
>> >> +    0, -959232, 363776, -1202944,
>> >> +    64, 940, 64, 960
>> >> +};
>> >> +
>> >> +/*
>> >> + * RGB to YUV2020 conversion parameters
>> >> + * RGB2YUV[0] - [8] : C0 - C8;
>> >> + * RGB2YUV[9] - [11]: D0 - D2;
>> >> + */
>> >> +static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = {
>> >> +    230,    594,    52,
>> >> +    -125,  -323,    448,
>> >> +    448,   -412,   -36,
>> >> +    64,             512,    512
>> >> +};
>> >> +
>> >> +/*
>> >> + * Degamma table for 709 color space data.
>> >> + */
>> >> +static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
>> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005,
>> >> +    0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024,
>> >> +    0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064,
>> >> +    0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce,
>> >> +    0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166,
>> >> +    0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230,
>> >> +    0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331,
>> >> +    0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c,
>> >> +    0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
>> >> +    0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d,
>> >> +    0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999,
>> >> +    0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb,
>> >> +    0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65,
>> >> +    0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a,
>> >> +    0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b,
>> >> +    0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc,
>> >> +    0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e,
>> >> +    0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
>> >> +    0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e,
>> >> +    0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf,
>> >> +    0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea,
>> >> +    0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f,
>> >> +    0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930,
>> >> +    0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f,
>> >> +    0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee,
>> >> +    0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde,
>> >> +    0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
>> >> +    0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9,
>> >> +    0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406,
>> >> +    0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a,
>> >> +    0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576,
>> >> +    0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd,
>> >> +    0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
>> >> +};
>> >> +
>> >> +/*
>> >> + * Degamma table for 2020 color space data.
>> >> + */
>> >> +static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
>> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
>> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
>> >> +    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
>> >> +    0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
>> >> +    0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
>> >> +    0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006,
>> >> +    0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a,
>> >> +    0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f,
>> >> +    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
>> >> +    0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024,
>> >> +    0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035,
>> >> +    0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d,
>> >> +    0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d,
>> >> +    0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098,
>> >> +    0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3,
>> >> +    0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123,
>> >> +    0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d,
>> >> +    0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
>> >> +    0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6,
>> >> +    0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1,
>> >> +    0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d,
>> >> +    0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6,
>> >> +    0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920,
>> >> +    0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a,
>> >> +    0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033,
>> >> +    0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593,
>> >> +    0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
>> >> +    0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656,
>> >> +    0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332,
>> >> +    0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480,
>> >> +    0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1,
>> >> +    0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99,
>> >> +    0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
>> >> +};
>> >> +
>> >> +/* one is for primary plane and the other is for all overlay planes */
>> >> +static const struct dc_hw_plane_reg dc_plane_reg[] = {
>> >> +    {
>> >> +    .y_address              = DC_FRAMEBUFFER_ADDRESS,
>> >> +    .u_address              = DC_FRAMEBUFFER_U_ADDRESS,
>> >> +    .v_address              = DC_FRAMEBUFFER_V_ADDRESS,
>> >> +    .y_stride               = DC_FRAMEBUFFER_STRIDE,
>> >> +    .u_stride               = DC_FRAMEBUFFER_U_STRIDE,
>> >> +    .v_stride               = DC_FRAMEBUFFER_V_STRIDE,
>> >> +    .size                   = DC_FRAMEBUFFER_SIZE,
>> >> +    .top_left               = DC_FRAMEBUFFER_TOP_LEFT,
>> >> +    .bottom_right   = DC_FRAMEBUFFER_BOTTOM_RIGHT,
>> >> +    .scale_factor_x                 = DC_FRAMEBUFFER_SCALE_FACTOR_X,
>> >> +    .scale_factor_y                 = DC_FRAMEBUFFER_SCALE_FACTOR_Y,
>> >> +    .h_filter_coef_index    = DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
>> >> +    .h_filter_coef_data             = DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
>> >> +    .v_filter_coef_index    = DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
>> >> +    .v_filter_coef_data             = DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
>> >> +    .init_offset                    = DC_FRAMEBUFFER_INIT_OFFSET,
>> >> +    .color_key                              = DC_FRAMEBUFFER_COLOR_KEY,
>> >> +    .color_key_high                 = DC_FRAMEBUFFER_COLOR_KEY_HIGH,
>> >> +    .clear_value                    = DC_FRAMEBUFFER_CLEAR_VALUE,
>> >> +    .color_table_index              = DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
>> >> +    .color_table_data               = DC_FRAMEBUFFER_COLOR_TABLE_DATA,
>> >> +    .scale_config                   = DC_FRAMEBUFFER_SCALE_CONFIG,
>> >> +    .water_mark                             = DC_FRAMEBUFFER_WATER_MARK,
>> >> +    .degamma_index                  = DC_FRAMEBUFFER_DEGAMMA_INDEX,
>> >> +    .degamma_data                   = DC_FRAMEBUFFER_DEGAMMA_DATA,
>> >> +    .degamma_ex_data                = DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
>> >> +    .src_global_color               = DC_FRAMEBUFFER_SRC_GLOBAL_COLOR,
>> >> +    .dst_global_color               = DC_FRAMEBUFFER_DST_GLOBAL_COLOR,
>> >> +    .blend_config                   = DC_FRAMEBUFFER_BLEND_CONFIG,
>> >> +    .roi_origin                             = DC_FRAMEBUFFER_ROI_ORIGIN,
>> >> +    .roi_size                               = DC_FRAMEBUFFER_ROI_SIZE,
>> >> +    .yuv_to_rgb_coef0                       = DC_FRAMEBUFFER_YUVTORGB_COEF0,
>> >> +    .yuv_to_rgb_coef1                       = DC_FRAMEBUFFER_YUVTORGB_COEF1,
>> >> +    .yuv_to_rgb_coef2                       = DC_FRAMEBUFFER_YUVTORGB_COEF2,
>> >> +    .yuv_to_rgb_coef3                       = DC_FRAMEBUFFER_YUVTORGB_COEF3,
>> >> +    .yuv_to_rgb_coef4                       = DC_FRAMEBUFFER_YUVTORGB_COEF4,
>> >> +    .yuv_to_rgb_coefd0                      = DC_FRAMEBUFFER_YUVTORGB_COEFD0,
>> >> +    .yuv_to_rgb_coefd1                      = DC_FRAMEBUFFER_YUVTORGB_COEFD1,
>> >> +    .yuv_to_rgb_coefd2                      = DC_FRAMEBUFFER_YUVTORGB_COEFD2,
>> >> +    .y_clamp_bound                          = DC_FRAMEBUFFER_Y_CLAMP_BOUND,
>> >> +    .uv_clamp_bound                         = DC_FRAMEBUFFER_UV_CLAMP_BOUND,
>> >> +    .rgb_to_rgb_coef0                       = DC_FRAMEBUFFER_RGBTORGB_COEF0,
>> >> +    .rgb_to_rgb_coef1                       = DC_FRAMEBUFFER_RGBTORGB_COEF1,
>> >> +    .rgb_to_rgb_coef2                       = DC_FRAMEBUFFER_RGBTORGB_COEF2,
>> >> +    .rgb_to_rgb_coef3                       = DC_FRAMEBUFFER_RGBTORGB_COEF3,
>> >> +    .rgb_to_rgb_coef4                       = DC_FRAMEBUFFER_RGBTORGB_COEF4,
>> >> +    },
>> >> +    {
>> >> +    .y_address              = DC_OVERLAY_ADDRESS,
>> >> +    .u_address              = DC_OVERLAY_U_ADDRESS,
>> >> +    .v_address              = DC_OVERLAY_V_ADDRESS,
>> >> +    .y_stride               = DC_OVERLAY_STRIDE,
>> >> +    .u_stride               = DC_OVERLAY_U_STRIDE,
>> >> +    .v_stride               = DC_OVERLAY_V_STRIDE,
>> >> +    .size                   = DC_OVERLAY_SIZE,
>> >> +    .top_left               = DC_OVERLAY_TOP_LEFT,
>> >> +    .bottom_right   = DC_OVERLAY_BOTTOM_RIGHT,
>> >> +    .scale_factor_x = DC_OVERLAY_SCALE_FACTOR_X,
>> >> +    .scale_factor_y = DC_OVERLAY_SCALE_FACTOR_Y,
>> >> +    .h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
>> >> +    .h_filter_coef_data  = DC_OVERLAY_H_FILTER_COEF_DATA,
>> >> +    .v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
>> >> +    .v_filter_coef_data  = DC_OVERLAY_V_FILTER_COEF_DATA,
>> >> +    .init_offset             = DC_OVERLAY_INIT_OFFSET,
>> >> +    .color_key                       = DC_OVERLAY_COLOR_KEY,
>> >> +    .color_key_high                 = DC_OVERLAY_COLOR_KEY_HIGH,
>> >> +    .clear_value             = DC_OVERLAY_CLEAR_VALUE,
>> >> +    .color_table_index       = DC_OVERLAY_COLOR_TABLE_INDEX,
>> >> +    .color_table_data        = DC_OVERLAY_COLOR_TABLE_DATA,
>> >> +    .scale_config            = DC_OVERLAY_SCALE_CONFIG,
>> >> +    .water_mark                             = DC_OVERLAY_WATER_MARK,
>> >> +    .degamma_index           = DC_OVERLAY_DEGAMMA_INDEX,
>> >> +    .degamma_data            = DC_OVERLAY_DEGAMMA_DATA,
>> >> +    .degamma_ex_data         = DC_OVERLAY_DEGAMMA_EX_DATA,
>> >> +    .src_global_color        = DC_OVERLAY_SRC_GLOBAL_COLOR,
>> >> +    .dst_global_color        = DC_OVERLAY_DST_GLOBAL_COLOR,
>> >> +    .blend_config            = DC_OVERLAY_BLEND_CONFIG,
>> >> +    .roi_origin                             = DC_OVERLAY_ROI_ORIGIN,
>> >> +    .roi_size                               = DC_OVERLAY_ROI_SIZE,
>> >> +    .yuv_to_rgb_coef0                = DC_OVERLAY_YUVTORGB_COEF0,
>> >> +    .yuv_to_rgb_coef1                = DC_OVERLAY_YUVTORGB_COEF1,
>> >> +    .yuv_to_rgb_coef2                = DC_OVERLAY_YUVTORGB_COEF2,
>> >> +    .yuv_to_rgb_coef3                = DC_OVERLAY_YUVTORGB_COEF3,
>> >> +    .yuv_to_rgb_coef4                       = DC_OVERLAY_YUVTORGB_COEF4,
>> >> +    .yuv_to_rgb_coefd0                      = DC_OVERLAY_YUVTORGB_COEFD0,
>> >> +    .yuv_to_rgb_coefd1                      = DC_OVERLAY_YUVTORGB_COEFD1,
>> >> +    .yuv_to_rgb_coefd2                      = DC_OVERLAY_YUVTORGB_COEFD2,
>> >> +    .y_clamp_bound           = DC_OVERLAY_Y_CLAMP_BOUND,
>> >> +    .uv_clamp_bound          = DC_OVERLAY_UV_CLAMP_BOUND,
>> >> +    .rgb_to_rgb_coef0                = DC_OVERLAY_RGBTORGB_COEF0,
>> >> +    .rgb_to_rgb_coef1                = DC_OVERLAY_RGBTORGB_COEF1,
>> >> +    .rgb_to_rgb_coef2                = DC_OVERLAY_RGBTORGB_COEF2,
>> >> +    .rgb_to_rgb_coef3                = DC_OVERLAY_RGBTORGB_COEF3,
>> >> +    .rgb_to_rgb_coef4                = DC_OVERLAY_RGBTORGB_COEF4,
>> >> +    },
>> >> +};
>> >> +
>> >> +static const u32 primary_overlay_format0[] = {
>> >> +    DRM_FORMAT_XRGB4444,
>> >> +    DRM_FORMAT_XBGR4444,
>> >> +    DRM_FORMAT_RGBX4444,
>> >> +    DRM_FORMAT_BGRX4444,
>> >> +    DRM_FORMAT_ARGB4444,
>> >> +    DRM_FORMAT_ABGR4444,
>> >> +    DRM_FORMAT_RGBA4444,
>> >> +    DRM_FORMAT_BGRA4444,
>> >> +    DRM_FORMAT_XRGB1555,
>> >> +    DRM_FORMAT_XBGR1555,
>> >> +    DRM_FORMAT_RGBX5551,
>> >> +    DRM_FORMAT_BGRX5551,
>> >> +    DRM_FORMAT_ARGB1555,
>> >> +    DRM_FORMAT_ABGR1555,
>> >> +    DRM_FORMAT_RGBA5551,
>> >> +    DRM_FORMAT_BGRA5551,
>> >> +    DRM_FORMAT_RGB565,
>> >> +    DRM_FORMAT_BGR565,
>> >> +    DRM_FORMAT_XRGB8888,
>> >> +    DRM_FORMAT_XBGR8888,
>> >> +    DRM_FORMAT_RGBX8888,
>> >> +    DRM_FORMAT_BGRX8888,
>> >> +    DRM_FORMAT_ARGB8888,
>> >> +    DRM_FORMAT_ABGR8888,
>> >> +    DRM_FORMAT_RGBA8888,
>> >> +    DRM_FORMAT_BGRA8888,
>> >> +    DRM_FORMAT_ARGB2101010,
>> >> +    DRM_FORMAT_ABGR2101010,
>> >> +    DRM_FORMAT_RGBA1010102,
>> >> +    DRM_FORMAT_BGRA1010102,
>> >> +    DRM_FORMAT_YUYV,
>> >> +    DRM_FORMAT_YVYU,
>> >> +    DRM_FORMAT_UYVY,
>> >> +    DRM_FORMAT_VYUY,
>> >> +    DRM_FORMAT_YVU420,
>> >> +    DRM_FORMAT_YUV420,
>> >> +    DRM_FORMAT_NV12,
>> >> +    DRM_FORMAT_NV21,
>> >> +    DRM_FORMAT_NV16,
>> >> +    DRM_FORMAT_NV61,
>> >> +    DRM_FORMAT_P010,
>> >> +};
>> >> +
>> >> +static const u32 primary_overlay_format1[] = {
>> >> +    DRM_FORMAT_XRGB8888,
>> >> +    DRM_FORMAT_XBGR8888,
>> >> +    DRM_FORMAT_RGBX8888,
>> >> +    DRM_FORMAT_BGRX8888,
>> >> +    DRM_FORMAT_ARGB8888,
>> >> +    DRM_FORMAT_ABGR8888,
>> >> +    DRM_FORMAT_RGBA8888,
>> >> +    DRM_FORMAT_BGRA8888,
>> >> +    DRM_FORMAT_ARGB2101010,
>> >> +    DRM_FORMAT_ABGR2101010,
>> >> +    DRM_FORMAT_RGBA1010102,
>> >> +    DRM_FORMAT_BGRA1010102,
>> >> +    DRM_FORMAT_NV12,
>> >> +    DRM_FORMAT_NV21,
>> >> +    DRM_FORMAT_YUV444,
>> >> +};
>> >> +
>> >> +static const u32 cursor_formats[] = {
>> >> +    DRM_FORMAT_ARGB8888
>> >> +};
>> >> +
>> >> +static const u64 format_modifier0[] = {
>> >> +    DRM_FORMAT_MOD_LINEAR,
>> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8),
>> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8),
>> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X8),
>> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X4),
>> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4),
>> >> +    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8),
>> >> +    DRM_FORMAT_MOD_INVALID
>> >> +};
>> >> +
>> >> +static const u64 secondary_format_modifiers[] = {
>> >> +    DRM_FORMAT_MOD_LINEAR,
>> >> +    DRM_FORMAT_MOD_INVALID
>> >> +};
>> >> +
>> >> +#define FRAC_16_16(mult, div)        (((mult) << 16) / (div))
>> >> +
>> >> +static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = {
>> >> +    {
>> >> +            /* DC_REV_0 */
>> >> +            {
>> >> +            .name                   = "Primary",
>> >
>> > I'd say that the indentation is strange here.
>> > It might be more scalable to split a single array into per-generation
>> > data structures and reference them directly from the info struct.
yes 
Use plat_data to reference
>> >
>> >> +            .id                             = PRIMARY_PLANE_0,
>> >> +            .type                   = DRM_PLANE_TYPE_PRIMARY,
>> >> +            .num_formats    = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats                = primary_overlay_format0,
>> >> +            .num_modifiers  = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers              = format_modifier0,
>> >> +            .min_width              = 0,
>> >> +            .min_height             = 0,
>> >> +            .max_width              = 4096,
>> >> +            .max_height             = 4096,
>> >> +            .rotation               = DRM_MODE_ROTATE_0 |
>> >> +                                                    DRM_MODE_ROTATE_90 |
>> >> +                                                    DRM_MODE_ROTATE_180 |
>> >> +                                                    DRM_MODE_ROTATE_270 |
>> >> +                                                    DRM_MODE_REFLECT_X |
>> >> +                                                    DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode             = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                                    BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                                    BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 0,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name                   = "Overlay",
>> >> +            .id                             = OVERLAY_PLANE_0,
>> >> +            .type                   = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats    = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats                = primary_overlay_format0,
>> >> +            .num_modifiers  = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers              = format_modifier0,
>> >> +            .min_width              = 0,
>> >> +            .min_height             = 0,
>> >> +            .max_width              = 4096,
>> >> +            .max_height             = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 1,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name                   = "Overlay_1",
>> >> +            .id                             = OVERLAY_PLANE_1,
>> >> +            .type                   = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats    = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats                = primary_overlay_format0,
>> >> +            .num_modifiers  = ARRAY_SIZE(secondary_format_modifiers),
>> >> +            .modifiers              = secondary_format_modifiers,
>> >> +            .min_width              = 0,
>> >> +            .min_height             = 0,
>> >> +            .max_width              = 4096,
>> >> +            .max_height             = 4096,
>> >> +            .rotation               = 0,
>> >> +            .blend_mode             = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                                    BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                                    BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 2,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Primary_1",
>> >> +            .id                             = PRIMARY_PLANE_1,
>> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats         = primary_overlay_format0,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 3,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay_2",
>> >> +            .id                             = OVERLAY_PLANE_2,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats         = primary_overlay_format0,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 4,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay_3",
>> >> +            .id                     = OVERLAY_PLANE_3,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats         = primary_overlay_format0,
>> >> +            .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
>> >> +            .modifiers       = secondary_format_modifiers,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = 0,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 5,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Cursor",
>> >> +            .id                             = CURSOR_PLANE_0,
>> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
>> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
>> >> +            .formats         = cursor_formats,
>> >> +            .num_modifiers = 0,
>> >> +            .modifiers       = NULL,
>> >> +            .min_width       = 32,
>> >> +            .min_height  = 32,
>> >> +            .max_width       = 64,
>> >> +            .max_height  = 64,
>> >> +            .rotation        = 0,
>> >> +            .degamma_size = 0,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 255,
>> >> +            .watermark       = false,
>> >> +            .color_mgmt  = false,
>> >> +            .roi             = false,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Cursor_1",
>> >> +            .id                             = CURSOR_PLANE_1,
>> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
>> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
>> >> +            .formats         = cursor_formats,
>> >> +            .num_modifiers = 0,
>> >> +            .modifiers       = NULL,
>> >> +            .min_width       = 32,
>> >> +            .min_height  = 32,
>> >> +            .max_width       = 64,
>> >> +            .max_height  = 64,
>> >> +            .rotation        = 0,
>> >> +            .degamma_size = 0,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 255,
>> >> +            .watermark       = false,
>> >> +            .color_mgmt  = false,
>> >> +            .roi             = false,
>> >> +            },
>> >> +    },
>> >> +    {
>> >> +            /* DC_REV_1 */
>> >> +            {
>> >> +            .name            = "Primary",
>> >> +            .id                             = PRIMARY_PLANE_0,
>> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats         = primary_overlay_format0,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 0,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay",
>> >> +            .id                             = OVERLAY_PLANE_0,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats         = primary_overlay_format0,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 1,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Primary_1",
>> >> +            .id                             = PRIMARY_PLANE_1,
>> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats         = primary_overlay_format0,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 2,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay_2",
>> >> +            .id                             = OVERLAY_PLANE_2,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format0),
>> >> +            .formats         = primary_overlay_format0,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 3,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Cursor",
>> >> +            .id                             = CURSOR_PLANE_0,
>> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
>> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
>> >> +            .formats         = cursor_formats,
>> >> +            .num_modifiers = 0,
>> >> +            .modifiers       = NULL,
>> >> +            .min_width       = 32,
>> >> +            .min_height  = 32,
>> >> +            .max_width       = 64,
>> >> +            .max_height  = 64,
>> >> +            .rotation        = 0,
>> >> +            .degamma_size = 0,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 255,
>> >> +            .watermark       = false,
>> >> +            .color_mgmt  = false,
>> >> +            .roi             = false,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Cursor_1",
>> >> +            .id                             = CURSOR_PLANE_1,
>> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
>> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
>> >> +            .formats         = cursor_formats,
>> >> +            .num_modifiers = 0,
>> >> +            .modifiers       = NULL,
>> >> +            .min_width       = 32,
>> >> +            .min_height  = 32,
>> >> +            .max_width       = 64,
>> >> +            .max_height  = 64,
>> >> +            .rotation        = 0,
>> >> +            .degamma_size = 0,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 255,
>> >> +            .watermark       = false,
>> >> +            .color_mgmt  = false,
>> >> +            .roi             = false,
>> >> +            },
>> >> +    },
>> >> +    {
>> >> +            /* DC_REV_2 */
>> >> +            {
>> >> +            .name            = "Primary",
>> >> +            .id                             = PRIMARY_PLANE_0,
>> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
>> >> +            .formats         = primary_overlay_format1,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 0,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay",
>> >> +            .id                             = OVERLAY_PLANE_0,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
>> >> +            .formats         = primary_overlay_format1,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 1,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay_1",
>> >> +            .id                             = OVERLAY_PLANE_1,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
>> >> +            .formats         = primary_overlay_format1,
>> >> +            .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
>> >> +            .modifiers       = secondary_format_modifiers,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = 0,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 2,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Primary_1",
>> >> +            .id                             = PRIMARY_PLANE_1,
>> >> +            .type            = DRM_PLANE_TYPE_PRIMARY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
>> >> +            .formats         = primary_overlay_format1,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 3,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay_2",
>> >> +            .id                             = OVERLAY_PLANE_2,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
>> >> +            .formats         = primary_overlay_format1,
>> >> +            .num_modifiers = ARRAY_SIZE(format_modifier0),
>> >> +            .modifiers       = format_modifier0,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = DRM_MODE_ROTATE_0 |
>> >> +                                       DRM_MODE_ROTATE_90 |
>> >> +                                       DRM_MODE_ROTATE_180 |
>> >> +                                       DRM_MODE_ROTATE_270 |
>> >> +                                       DRM_MODE_REFLECT_X |
>> >> +                                       DRM_MODE_REFLECT_Y,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = FRAC_16_16(1, 3),
>> >> +            .max_scale       = FRAC_16_16(10, 1),
>> >> +            .zpos            = 4,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Overlay_3",
>> >> +            .id                             = OVERLAY_PLANE_3,
>> >> +            .type            = DRM_PLANE_TYPE_OVERLAY,
>> >> +            .num_formats = ARRAY_SIZE(primary_overlay_format1),
>> >> +            .formats         = primary_overlay_format1,
>> >> +            .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
>> >> +            .modifiers       = secondary_format_modifiers,
>> >> +            .min_width       = 0,
>> >> +            .min_height  = 0,
>> >> +            .max_width       = 4096,
>> >> +            .max_height  = 4096,
>> >> +            .rotation        = 0,
>> >> +            .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
>> >> +                                       BIT(DRM_MODE_BLEND_PREMULTI) |
>> >> +                                       BIT(DRM_MODE_BLEND_COVERAGE),
>> >> +            .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
>> >> +                                              BIT(DRM_COLOR_YCBCR_BT2020),
>> >> +            .degamma_size   = DEGAMMA_SIZE,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 5,
>> >> +            .watermark       = true,
>> >> +            .color_mgmt  = true,
>> >> +            .roi             = true,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Cursor",
>> >> +            .id                             = CURSOR_PLANE_0,
>> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
>> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
>> >> +            .formats         = cursor_formats,
>> >> +            .num_modifiers = 0,
>> >> +            .modifiers       = NULL,
>> >> +            .min_width       = 32,
>> >> +            .min_height  = 32,
>> >> +            .max_width       = 64,
>> >> +            .max_height  = 64,
>> >> +            .rotation        = 0,
>> >> +            .degamma_size = 0,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 255,
>> >> +            .watermark       = false,
>> >> +            .color_mgmt  = false,
>> >> +            .roi             = false,
>> >> +            },
>> >> +            {
>> >> +            .name            = "Cursor_1",
>> >> +            .id                             = CURSOR_PLANE_1,
>> >> +            .type            = DRM_PLANE_TYPE_CURSOR,
>> >> +            .num_formats = ARRAY_SIZE(cursor_formats),
>> >> +            .formats         = cursor_formats,
>> >> +            .num_modifiers = 0,
>> >> +            .modifiers       = NULL,
>> >> +            .min_width       = 32,
>> >> +            .min_height  = 32,
>> >> +            .max_width       = 64,
>> >> +            .max_height  = 64,
>> >> +            .rotation        = 0,
>> >> +            .degamma_size = 0,
>> >> +            .min_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .max_scale       = DRM_PLANE_NO_SCALING,
>> >> +            .zpos            = 255,
>> >> +            .watermark       = false,
>> >> +            .color_mgmt  = false,
>> >> +            .roi             = false,
>> >> +            },
>> >> +    },
>> >> +};
>> >> +
>> >> +static const struct vs_dc_info dc_info[] = {
>> >
>> > Again, please split into per-generation instances.
will add  per-generation instances data
and I will only keep DC_REV_0 data struct
DC_REV_1 DC_REV_2 will delete ,because only DC_REV_0 used
>> >
>> >> +    {
>> >> +            /* DC_REV_0 */
>> >> +            .name                   = "DC8200",
>> >> +            .panel_num              = 2,
>> >> +            .plane_num              = 8,
>> >> +            .planes                 = dc_hw_planes[DC_REV_0],
>> >> +            .layer_num              = 6,
>> >> +            .max_bpc                = 10,
>> >> +            .color_formats  = DRM_COLOR_FORMAT_RGB444 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR444 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR422 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR420,
>> >> +            .gamma_size             = GAMMA_EX_SIZE,
>> >> +            .gamma_bits             = 12,
>> >> +            .pitch_alignment        = 128,
>> >> +            .pipe_sync              = false,
>> >> +            .background             = true,
>> >> +            .panel_sync             = true,
>> >> +            .cap_dec                = true,
>> >> +    },
>> >> +    {
>> >> +            /* DC_REV_1 */
>> >> +            .name                   = "DC8200",
>> >> +            .panel_num              = 2,
>> >> +            .plane_num              = 6,
>> >> +            .planes                 = dc_hw_planes[DC_REV_1],
>> >> +            .layer_num              = 4,
>> >> +            .max_bpc                = 10,
>> >> +            .color_formats  = DRM_COLOR_FORMAT_RGB444 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR444 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR422 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR420,
>> >> +            .gamma_size             = GAMMA_EX_SIZE,
>> >> +            .gamma_bits             = 12,
>> >> +            .pitch_alignment        = 128,
>> >> +            .pipe_sync              = false,
>> >> +            .background             = true,
>> >> +            .panel_sync             = true,
>> >> +            .cap_dec                = true,
>> >> +    },
>> >> +    {
>> >> +            /* DC_REV_2 */
>> >> +            .name                   = "DC8200",
>> >> +            .panel_num              = 2,
>> >> +            .plane_num              = 8,
>> >> +            .planes                 = dc_hw_planes[DC_REV_2],
>> >> +            .layer_num              = 6,
>> >> +            .max_bpc                = 10,
>> >> +            .color_formats  = DRM_COLOR_FORMAT_RGB444 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR444 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR422 |
>> >> +                                              DRM_COLOR_FORMAT_YCBCR420,
>> >> +            .gamma_size             = GAMMA_EX_SIZE,
>> >> +            .gamma_bits             = 12,
>> >> +            .pitch_alignment        = 128,
>> >> +            .pipe_sync              = false,
>> >> +            .background             = true,
>> >> +            .panel_sync             = true,
>> >> +            .cap_dec                = false,
>> >> +    },
>> >> +};
>> >> +
>> >> +static const struct dc_hw_funcs hw_func;
>> >> +
>> >> +static inline u32 hi_read(struct dc_hw *hw, u32 reg)
>> >> +{
>> >> +    return readl(hw->hi_base + reg);
>> >> +}
>> >> +
>> >> +static inline void hi_write(struct dc_hw *hw, u32 reg, u32 value)
>> >> +{
>> >> +    writel(value, hw->hi_base + reg);
>> >> +}
>> >> +
>> >> +static inline void dc_write(struct dc_hw *hw, u32 reg, u32 value)
>> >> +{
>> >> +    writel(value, hw->reg_base + reg - DC_REG_BASE);
>> >> +}
>> >> +
>> >> +static inline u32 dc_read(struct dc_hw *hw, u32 reg)
>> >> +{
>> >> +    u32 value = readl(hw->reg_base + reg - DC_REG_BASE);
>> >> +
>> >> +    return value;
>> >> +}
>> >> +
>> >> +static inline void dc_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear)
>> >> +{
>> >> +    u32 value = dc_read(hw, reg);
>> >> +
>> >> +    value &= ~clear;
>> >> +    value |= set;
>> >> +    dc_write(hw, reg, value);
>> >> +}
>> >> +
>> >> +static void load_default_filter(struct dc_hw *hw,
>> >> +                            const struct dc_hw_plane_reg *reg, u32 offset)
>> >> +{
>> >> +    u8 i;
>> >> +
>> >> +    dc_write(hw, reg->scale_config + offset, 0x33);
>> >> +    dc_write(hw, reg->init_offset + offset, 0x80008000);
>> >> +    dc_write(hw, reg->h_filter_coef_index + offset, 0x00);
>> >> +    for (i = 0; i < H_COEF_SIZE; i++)
>> >> +            dc_write(hw, reg->h_filter_coef_data + offset, horkernel[i]);
>> >> +
>> >> +    dc_write(hw, reg->v_filter_coef_index + offset, 0x00);
>> >> +    for (i = 0; i < V_COEF_SIZE; i++)
>> >> +            dc_write(hw, reg->v_filter_coef_data + offset, verkernel[i]);
>> >> +}
>> >> +
>> >> +static void load_rgb_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
>> >> +                        u32 offset, u16 *table)
>> >> +{
>> >> +    dc_write(hw, reg->rgb_to_rgb_coef0 + offset, table[0] | (table[1] << 16));
>> >> +    dc_write(hw, reg->rgb_to_rgb_coef1 + offset, table[2] | (table[3] << 16));
>> >> +    dc_write(hw, reg->rgb_to_rgb_coef2 + offset, table[4] | (table[5] << 16));
>> >> +    dc_write(hw, reg->rgb_to_rgb_coef3 + offset, table[6] | (table[7] << 16));
>> >> +    dc_write(hw, reg->rgb_to_rgb_coef4 + offset, table[8]);
>> >> +}
>> >> +
>> >> +static void load_yuv_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
>> >> +                        u32 offset, s32 *table)
>> >> +{
>> >> +    dc_write(hw, reg->yuv_to_rgb_coef0 + offset,
>> >> +             (0xFFFF & table[0]) | (table[1] << 16));
>> >> +    dc_write(hw, reg->yuv_to_rgb_coef1 + offset,
>> >> +             (0xFFFF & table[2]) | (table[3] << 16));
>> >> +    dc_write(hw, reg->yuv_to_rgb_coef2 + offset,
>> >> +             (0xFFFF & table[4]) | (table[5] << 16));
>> >> +    dc_write(hw, reg->yuv_to_rgb_coef3 + offset,
>> >> +             (0xFFFF & table[6]) | (table[7] << 16));
>> >> +    dc_write(hw, reg->yuv_to_rgb_coef4 + offset, table[8]);
>> >> +    dc_write(hw, reg->yuv_to_rgb_coefd0 + offset, table[9]);
>> >> +    dc_write(hw, reg->yuv_to_rgb_coefd1 + offset, table[10]);
>> >> +    dc_write(hw, reg->yuv_to_rgb_coefd2 + offset, table[11]);
>> >> +    dc_write(hw, reg->y_clamp_bound + offset, table[12] | (table[13] << 16));
>> >> +    dc_write(hw, reg->uv_clamp_bound + offset, table[14] | (table[15] << 16));
>> >> +}
>> >> +
>> >> +static void load_rgb_to_yuv(struct dc_hw *hw, u32 offset, s16 *table)
>> >> +{
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF0 + offset,
>> >> +             table[0] | (table[1] << 16));
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF1 + offset,
>> >> +             table[2] | (table[3] << 16));
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF2 + offset,
>> >> +             table[4] | (table[5] << 16));
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF3 + offset,
>> >> +             table[6] | (table[7] << 16));
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF4 + offset, table[8]);
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD0 + offset, table[9]);
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD1 + offset, table[10]);
>> >> +    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD2 + offset, table[11]);
>> >> +}
>> >> +
>> >> +static bool is_rgb(enum dc_hw_color_format format)
>> >> +{
>> >> +    switch (format) {
>> >> +    case FORMAT_X4R4G4B4:
>> >> +    case FORMAT_A4R4G4B4:
>> >> +    case FORMAT_X1R5G5B5:
>> >> +    case FORMAT_A1R5G5B5:
>> >> +    case FORMAT_R5G6B5:
>> >> +    case FORMAT_X8R8G8B8:
>> >> +    case FORMAT_A8R8G8B8:
>> >> +    case FORMAT_A2R10G10B10:
>> >> +            return true;
>> >> +    default:
>> >> +            return false;
>> >> +    }
>> >> +}
>> >> +
>> >> +static void load_degamma_table(struct dc_hw *hw,
>> >> +                           const struct dc_hw_plane_reg *reg,
>> >> +                           u32 offset, u16 *table)
>> >> +{
>> >> +    u16 i;
>> >> +    u32 value;
>> >> +
>> >> +    dc_write(hw, reg->degamma_index + offset, 0);
>> >> +
>> >> +    for (i = 0; i < DEGAMMA_SIZE; i++) {
>> >> +            value = table[i] | (table[i] << 16);
>> >> +            dc_write(hw, reg->degamma_data + offset, value);
>> >> +            dc_write(hw, reg->degamma_ex_data + offset, table[i]);
>> >> +    }
>> >> +}
>> >> +
>> >> +static u32 get_addr_offset(u32 id)
>> >> +{
>> >> +    u32 offset = 0;
>> >> +
>> >> +    switch (id) {
>> >> +    case PRIMARY_PLANE_1:
>> >> +    case OVERLAY_PLANE_1:
>> >> +            offset = 0x04;
>> >> +            break;
>> >> +    case OVERLAY_PLANE_2:
>> >> +            offset = 0x08;
>> >> +            break;
>> >> +    case OVERLAY_PLANE_3:
>> >> +            offset = 0x0C;
>> >> +            break;
>> >> +    default:
>> >> +            break;
>> >> +    }
>> >> +
>> >> +    return offset;
>> >> +}
>> >> +
>> >> +int dc_hw_init(struct dc_hw *hw)
>> >> +{
>> >> +    u8 i, id, panel_num, layer_num;
>> >> +    u32 offset;
>> >> +    u32 revision = hi_read(hw, DC_HW_REVISION);
>> >> +    u32 cid = hi_read(hw, DC_HW_CHIP_CID);
>> >> +    const struct dc_hw_plane_reg *reg;
>> >> +
>> >> +    switch (revision) {
>> >> +    case 0x5720:
>> >> +            hw->rev = DC_REV_0;
>> >> +            break;
>> >> +    case 0x5721:
>> >> +            switch (cid) {
>> >> +            case 0x30B:
>> >> +                    hw->rev = DC_REV_1;
>> >> +                    break;
>> >> +            case 0x310:
>> >> +                    hw->rev = DC_REV_2;
>> >> +                    break;
>> >> +            default:
>> >> +                    hw->rev = DC_REV_0;
>> >> +                    break;
>> >> +            }
>> >> +            break;
>> >> +    default:
>> >> +            return -ENXIO;
>> >> +    }
>> >
>> > We (drm/msm) had code like this for a while (and autodetection of the HW
>> > ID). And nowadays we are migrating to of_device_get_match_data and
>> > per-SoC compat names. It is just easier to handle, it allows one to
>> > specify device more precisely and also it simplifies DT schema.
of_device_get_match_data is a good choice

>> >
>> >> +
>> >> +    hw->info = (struct vs_dc_info *)&dc_info[hw->rev];
>> >> +    hw->func = (struct dc_hw_funcs *)&hw_func;
>> >> +
>> >> +    layer_num = hw->info->layer_num;
>> >> +    for (i = 0; i < layer_num; i++) {
>> >> +            id = hw->info->planes[i].id;
>> >> +            offset = get_addr_offset(id);
>> >> +            if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
>> >> +                    reg = &dc_plane_reg[0];
>> >> +            else
>> >> +                    reg = &dc_plane_reg[1];
>> >> +
>> >> +            load_default_filter(hw, reg, offset);
>> >> +            load_rgb_to_rgb(hw, reg, offset, RGB2RGB);
>> >> +    }
>> >> +
>> >> +    panel_num = hw->info->panel_num;
>> >> +    for (i = 0; i < panel_num; i++) {
>> >> +            offset = i << 2;
>> >> +
>> >> +            load_rgb_to_yuv(hw, offset, RGB2YUV);
>> >> +            dc_write(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0x111);
>> >> +
>> >> +            offset = i ? DC_CURSOR_OFFSET : 0;
>> >> +            dc_write(hw, DC_CURSOR_BACKGROUND + offset, 0x00FFFFFF);
>> >> +            dc_write(hw, DC_CURSOR_FOREGROUND + offset, 0x00AAAAAA);
>> >> +    }
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +void dc_hw_deinit(struct dc_hw *hw)
>> >> +{
>> >> +    /* Nothing to do */
>> >> +}
>> >> +
>> >> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
>> >> +                    struct dc_hw_fb *fb, struct dc_hw_scale *scale,
>> >> +                    struct dc_hw_position *pos, struct dc_hw_blend *blend)
>> >> +{
>> >> +    struct dc_hw_plane *plane = &hw->plane[id];
>> >> +
>> >> +    if (plane) {
>> >> +            if (fb) {
>> >> +                    if (!fb->enable)
>> >> +                            plane->fb.enable = false;
>> >> +                    else
>> >> +                            memcpy(&plane->fb, fb,
>> >> +                                   sizeof(*fb) - sizeof(fb->dirty));
>> >> +                    plane->fb.dirty = true;
>> >> +            }
>> >> +            if (scale) {
>> >> +                    memcpy(&plane->scale, scale,
>> >> +                           sizeof(*scale) - sizeof(scale->dirty));
>> >> +                    plane->scale.dirty = true;
>> >> +            }
>> >> +            if (pos) {
>> >> +                    memcpy(&plane->pos, pos,
>> >> +                           sizeof(*pos) - sizeof(pos->dirty));
>> >> +                    plane->pos.dirty = true;
>> >> +            }
>> >> +            if (blend) {
>> >> +                    memcpy(&plane->blend, blend,
>> >> +                           sizeof(*blend) - sizeof(blend->dirty));
>> >> +                    plane->blend.dirty = true;
>> >> +            }
>> >> +    }
>> >> +}
>> >> +
>> >> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode)
>> >> +{
>> >> +    struct dc_hw_plane *plane = &hw->plane[id];
>> >> +
>> >> +    if (plane) {
>> >> +            if (hw->info->planes[id].degamma_size) {
>> >> +                    plane->degamma.mode = mode;
>> >> +                    plane->degamma.dirty = true;
>> >> +            } else {
>> >> +                    plane->degamma.dirty = false;
>> >> +            }
>> >> +    }
>> >> +}
>> >> +
>> >> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi)
>> >> +{
>> >> +    struct dc_hw_plane *plane = &hw->plane[id];
>> >> +
>> >> +    if (plane) {
>> >> +            memcpy(&plane->roi, roi, sizeof(*roi) - sizeof(roi->dirty));
>> >> +            plane->roi.dirty = true;
>> >> +    }
>> >> +}
>> >> +
>> >> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
>> >> +                       struct dc_hw_colorkey *colorkey)
>> >> +{
>> >> +    struct dc_hw_plane *plane = &hw->plane[id];
>> >> +
>> >> +    if (plane) {
>> >> +            memcpy(&plane->colorkey, colorkey,
>> >> +                   sizeof(*colorkey) - sizeof(colorkey->dirty));
>> >> +            plane->colorkey.dirty = true;
>> >> +    }
>> >> +}
>> >> +
>> >> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos)
>> >> +{
>> >> +    memcpy(&hw->qos, qos, sizeof(*qos) - sizeof(qos->dirty));
>> >> +    hw->qos.dirty = true;
>> >> +}
>> >> +
>> >> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor)
>> >> +{
>> >> +    memcpy(&hw->cursor[id], cursor, sizeof(*cursor) - sizeof(cursor->dirty));
>> >> +    hw->cursor[id].dirty = true;
>> >> +}
>> >> +
>> >> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
>> >> +                    u16 r, u16 g, u16 b)
>> >> +{
>> >> +    if (index >= hw->info->gamma_size)
>> >> +            return;
>> >> +
>> >> +    hw->gamma[id].gamma[index][0] = r;
>> >> +    hw->gamma[id].gamma[index][1] = g;
>> >> +    hw->gamma[id].gamma[index][2] = b;
>> >> +    hw->gamma[id].dirty = true;
>> >> +}
>> >> +
>> >> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable)
>> >> +{
>> >> +    hw->gamma[id].enable = enable;
>> >> +    hw->gamma[id].dirty = true;
>> >> +}
>> >> +
>> >> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display)
>> >> +{
>> >> +    u8 id = display->id;
>> >> +
>> >> +    memcpy(&hw->display[id], display, sizeof(*display));
>> >> +
>> >> +    hw->func->display(hw, display);
>> >> +}
>> >> +
>> >> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable)
>> >> +{
>> >> +    if (enable)
>> >> +            hi_write(hw, AQ_INTR_ENBL, 0xFFFFFFFF);
>> >> +    else
>> >> +            hi_write(hw, AQ_INTR_ENBL, 0);
>> >> +}
>> >> +
>> >> +u32 dc_hw_get_interrupt(struct dc_hw *hw)
>> >> +{
>> >> +    return hi_read(hw, AQ_INTR_ACKNOWLEDGE);
>> >> +}
>> >> +
>> >> +bool dc_hw_check_underflow(struct dc_hw *hw)
>> >> +{
>> >> +    return dc_read(hw, DC_FRAMEBUFFER_CONFIG) & BIT(5);
>> >> +}
>> >> +
>> >> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable)
>> >> +{
>> >> +    u32 i, offset;
>> >> +    u8 id, layer_num = hw->info->layer_num;
>> >> +    u8 panel_num = hw->info->panel_num;
>> >> +
>> >> +    for (i = 0; i < layer_num; i++) {
>> >> +            id = hw->info->planes[i].id;
>> >> +            offset = get_addr_offset(id);
>> >> +            if (enable) {
>> >> +                    if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
>> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, BIT(12), 0);
>> >> +                    else
>> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, BIT(31), 0);
>> >> +            } else {
>> >> +                    if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
>> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, 0, BIT(12));
>> >> +                    else
>> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, 0, BIT(31));
>> >> +            }
>> >> +    }
>> >> +
>> >> +    for (i = 0; i < panel_num; i++) {
>> >> +            offset = i << 2;
>> >> +            if (enable)
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, BIT(0));
>> >> +            else
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, BIT(0), 0);
>> >> +    }
>> >> +}
>> >> +
>> >> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id)
>> >> +{
>> >> +    if (out <= OUT_DP)
>> >> +            hw->out[id] = out;
>> >> +}
>> >> +
>> >> +static void gamma_ex_commit(struct dc_hw *hw)
>> >> +{
>> >> +    u8 panel_num = hw->info->panel_num;
>> >> +    u16 i, j;
>> >> +    u32 value;
>> >> +
>> >> +    for (j = 0; j < panel_num; j++) {
>> >> +            if (hw->gamma[j].dirty) {
>> >> +                    if (hw->gamma[j].enable) {
>> >> +                            dc_write(hw, DC_DISPLAY_GAMMA_EX_INDEX + (j << 2), 0x00);
>> >> +                            for (i = 0; i < GAMMA_EX_SIZE; i++) {
>> >> +                                    value = hw->gamma[j].gamma[i][2] |
>> >> +                                            (hw->gamma[j].gamma[i][1] << 12);
>> >> +                                    dc_write(hw, DC_DISPLAY_GAMMA_EX_DATA + (j << 2), value);
>> >> +                                    dc_write(hw, DC_DISPLAY_GAMMA_EX_ONE_DATA + (j << 2),
>> >> +                                             hw->gamma[j].gamma[i][0]);
>> >> +                            }
>> >> +                            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
>> >> +                                         BIT(13), 0);
>> >> +                    } else {
>> >> +                            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
>> >> +                                         0, BIT(13));
>> >> +                    }
>> >> +                    hw->gamma[j].dirty = false;
>> >> +            }
>> >> +    }
>> >> +}
>> >> +
>> >> +static void plane_commit(struct dc_hw *hw)
>> >> +{
>> >> +    struct dc_hw_plane *plane;
>> >> +    const struct dc_hw_plane_reg *reg;
>> >> +    bool primary = false;
>> >> +    u8 id, layer_num = hw->info->layer_num;
>> >> +    u32 i, offset;
>> >> +
>> >> +    for (i = 0; i < layer_num; i++) {
>> >> +            plane = &hw->plane[i];
>> >> +            id = hw->info->planes[i].id;
>> >> +            offset = get_addr_offset(id);
>> >> +            if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
>> >> +                    reg = &dc_plane_reg[0];
>> >> +                    primary = true;
>> >> +            } else {
>> >> +                    reg = &dc_plane_reg[1];
>> >> +                    primary = false;
>> >> +            }
>> >
>> > Can you please move register defines to some internal hw_plane
>> > structure. These details are known at the time of creation of the plane.
>> > There is no need to determine them again and again.
>> >
>> >> +
>> >> +            if (plane->fb.dirty) {
>> >> +                    if (plane->fb.enable) {
>> >
>> > if (plane->fb.dirty && plane->fb.enable) will save you one level of
>> > indentation.
>> >
>> > However it might be better to use existing DRM flags instead of
>> > inventing your own flags.
>> >
>> >> +                            dc_write(hw, reg->y_address + offset,
>> >> +                                     plane->fb.y_address);
>> >> +                            dc_write(hw, reg->u_address + offset,
>> >> +                                     plane->fb.u_address);
>> >> +                            dc_write(hw, reg->v_address + offset,
>> >> +                                     plane->fb.v_address);
>> >> +                            dc_write(hw, reg->y_stride + offset,
>> >> +                                     plane->fb.y_stride);
>> >> +                            dc_write(hw, reg->u_stride + offset,
>> >> +                                     plane->fb.u_stride);
>> >> +                            dc_write(hw, reg->v_stride + offset,
>> >> +                                     plane->fb.v_stride);
>> >> +                            dc_write(hw, reg->size + offset,
>> >> +                                     plane->fb.width |
>> >> +                                     (plane->fb.height << 15));
>> >> +                            dc_write(hw, reg->water_mark + offset,
>> >> +                                     plane->fb.water_mark);
>> >> +
>> >> +                            if (plane->fb.clear_enable)
>> >> +                                    dc_write(hw, reg->clear_value + offset,
>> >> +                                             plane->fb.clear_value);
>> >> +                    }
>> >> +
>> >> +                    if (primary) {
>> >
>> > You have two kinds of planes. Primary and overlays. Could you please
>> > split this code into two separate functions and define two vtables, one
>> > for primary planes and one for overlays. There is no need to stick
>> > everything into a single funcion that handles everything.
>> >
>> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
>> >> +                                         (plane->fb.format << 26) |
>> >> +                                         (plane->fb.uv_swizzle << 25) |
>> >> +                                         (plane->fb.swizzle << 23) |
>> >> +                                         (plane->fb.tile_mode << 17) |
>> >> +                                         (plane->fb.yuv_color_space << 14) |
>> >> +                                         (plane->fb.rotation << 11) |
>> >> +                                         (plane->fb.clear_enable << 8),
>> >> +                                         (0x1F << 26) |
>> >> +                                         BIT(25) |
>> >> +                                         (0x03 << 23) |
>> >> +                                         (0x1F << 17) |
>> >> +                                         (0x07 << 14) |
>> >> +                                         (0x07 << 11) |
>> >> +                                         BIT(8));
>> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                         (plane->fb.dec_enable << 1) |
>> >> +                                         (plane->fb.enable << 13) |
>> >> +                                         (plane->fb.zpos << 16) |
>> >> +                                         (plane->fb.display_id << 19),
>> >> +                                         BIT(1) | BIT(13) | (0x07 << 16) | BIT(19));
>> >
>> > It looks like this code can enjoy defining names for all the magic
>> > values. Otherwise it is impossible to support it.
>> >
>> >> +                    } else {
>> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> >> +                                         (plane->fb.dec_enable << 27) |
>> >> +                                         (plane->fb.clear_enable << 25) |
>> >> +                                         (plane->fb.enable << 24) |
>> >> +                                         (plane->fb.format << 16) |
>> >> +                                         (plane->fb.uv_swizzle << 15) |
>> >> +                                         (plane->fb.swizzle << 13) |
>> >> +                                         (plane->fb.tile_mode << 8) |
>> >> +                                         (plane->fb.yuv_color_space << 5) |
>> >> +                                         (plane->fb.rotation << 2),
>> >> +                                         BIT(27) |
>> >> +                                         BIT(25) |
>> >> +                                         BIT(24) |
>> >> +                                         (0x1F << 16) |
>> >> +                                         BIT(15) |
>> >> +                                         (0x03 << 13) |
>> >> +                                         (0x1F << 8) |
>> >> +                                         (0x07 << 5) |
>> >> +                                         (0x07 << 2));
>> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset,
>> >> +                                         plane->fb.zpos | (plane->fb.display_id << 3),
>> >> +                                         0x07 | BIT(3));
>> >> +                    }
>> >> +                    plane->fb.dirty = false;
>> >> +            }
>> >> +
>> >> +            if (plane->scale.dirty) {
>> >> +                    if (plane->scale.enable) {
>> >> +                            dc_write(hw, reg->scale_factor_x + offset,
>> >> +                                     plane->scale.scale_factor_x);
>> >> +                            dc_write(hw, reg->scale_factor_y + offset,
>> >> +                                     plane->scale.scale_factor_y);
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_FRAMEBUFFER_CONFIG + offset,
>> >> +                                                 BIT(22), 0);
>> >> +                            else
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_OVERLAY_SCALE_CONFIG + offset,
>> >> +                                                 BIT(8), 0);
>> >> +                    } else {
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_FRAMEBUFFER_CONFIG + offset,
>> >> +                                                 0, BIT(22));
>> >> +                            else
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_OVERLAY_SCALE_CONFIG + offset,
>> >> +                                                 0, BIT(8));
>> >> +                    }
>> >> +                    plane->scale.dirty = false;
>> >> +            }
>> >> +
>> >> +            if (plane->pos.dirty) {
>> >> +                    dc_write(hw, reg->top_left + offset,
>> >> +                             plane->pos.start_x |
>> >> +                             (plane->pos.start_y << 15));
>> >> +                    dc_write(hw, reg->bottom_right + offset,
>> >> +                             plane->pos.end_x |
>> >> +                             (plane->pos.end_y << 15));
>> >> +                    plane->pos.dirty = false;
>> >> +            }
>> >> +
>> >> +            if (plane->blend.dirty) {
>> >> +                    dc_write(hw, reg->src_global_color + offset,
>> >> +                             plane->blend.alpha << 24);
>> >> +                    dc_write(hw, reg->dst_global_color + offset,
>> >> +                             plane->blend.alpha << 24);
>> >> +                    switch (plane->blend.blend_mode) {
>> >> +                    case BLEND_PREMULTI:
>> >> +                            dc_write(hw, reg->blend_config + offset, 0x3450);
>> >> +                            break;
>> >> +                    case BLEND_COVERAGE:
>> >> +                            dc_write(hw, reg->blend_config + offset, 0x3950);
>> >> +                            break;
>> >> +                    case BLEND_PIXEL_NONE:
>> >> +                            dc_write(hw, reg->blend_config + offset, 0x3548);
>> >> +                            break;
>> >> +                    default:
>> >> +                            break;
>> >> +                    }
>> >> +                    plane->blend.dirty = false;
>> >> +            }
>> >> +
>> >> +            if (plane->colorkey.dirty) {
>> >> +                    dc_write(hw, reg->color_key + offset, plane->colorkey.colorkey);
>> >> +                    dc_write(hw, reg->color_key_high + offset,
>> >> +                             plane->colorkey.colorkey_high);
>> >> +
>> >> +                    if (primary)
>> >> +                            dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
>> >> +                                         plane->colorkey.transparency << 9, 0x03 << 9);
>> >> +                    else
>> >> +                            dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> >> +                                         plane->colorkey.transparency, 0x03);
>> >> +
>> >> +                    plane->colorkey.dirty = false;
>> >> +            }
>> >> +
>> >> +            if (plane->roi.dirty) {
>> >> +                    if (plane->roi.enable) {
>> >> +                            dc_write(hw, reg->roi_origin + offset,
>> >> +                                     plane->roi.x | (plane->roi.y << 16));
>> >> +                            dc_write(hw, reg->roi_size + offset,
>> >> +                                     plane->roi.width | (plane->roi.height << 16));
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                                 BIT(0), 0);
>> >> +                            else
>> >> +                                    dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> >> +                                                 BIT(22), 0);
>> >> +                    } else {
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                                 0, BIT(0));
>> >> +                            else
>> >> +                                    dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
>> >> +                                                 0, BIT(22));
>> >> +                    }
>> >> +                    plane->roi.dirty = false;
>> >> +            }
>> >> +    }
>> >> +}
>> >> +
>> >> +static void plane_ex_commit(struct dc_hw *hw)
>> >> +{
>> >> +    struct dc_hw_plane *plane;
>> >> +    const struct dc_hw_plane_reg *reg;
>> >> +    bool primary = false;
>> >> +    u8 id, layer_num = hw->info->layer_num;
>> >> +    u32 i, offset;
>> >> +
>> >> +    for (i = 0; i < layer_num; i++) {
>> >> +            plane = &hw->plane[i];
>> >> +            id = hw->info->planes[i].id;
>> >> +            offset = get_addr_offset(id);
>> >> +            if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
>> >> +                    reg = &dc_plane_reg[0];
>> >> +                    primary = true;
>> >> +            } else {
>> >> +                    reg = &dc_plane_reg[1];
>> >> +                    primary = false;
>> >> +            }
>> >> +
>> >> +            if (plane->fb.dirty) {
>> >> +                    if (is_rgb(plane->fb.format)) {
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                                 BIT(6), BIT(8));
>> >> +                            else
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_OVERLAY_CONFIG + offset,
>> >> +                                                 BIT(29), BIT(30));
>> >> +                    } else {
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                                 BIT(8), BIT(6));
>> >> +                            else
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_OVERLAY_CONFIG + offset,
>> >> +                                                 BIT(30), BIT(29));
>> >> +                            switch (plane->fb.yuv_color_space) {
>> >> +                            case COLOR_SPACE_601:
>> >> +                                    load_yuv_to_rgb(hw, reg, offset, YUV601_2RGB);
>> >> +                                    break;
>> >> +                            case COLOR_SPACE_709:
>> >> +                                    load_yuv_to_rgb(hw, reg, offset, YUV709_2RGB);
>> >> +                                    break;
>> >> +                            case COLOR_SPACE_2020:
>> >> +                                    load_yuv_to_rgb(hw, reg, offset, YUV2020_2RGB);
>> >> +                                    break;
>> >> +                            default:
>> >> +                                    break;
>> >> +                            }
>> >> +                    }
>> >> +            }
>> >> +            if (plane->degamma.dirty) {
>> >> +                    switch (plane->degamma.mode) {
>> >> +                    case VS_DEGAMMA_DISABLE:
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                                 0, BIT(5));
>> >> +                            else
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_OVERLAY_CONFIG + offset,
>> >> +                                                 0, BIT(28));
>> >> +                            break;
>> >> +                    case VS_DEGAMMA_BT709:
>> >> +                            load_degamma_table(hw, reg, offset, DEGAMMA_709);
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                                 BIT(5), 0);
>> >> +                            else
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_OVERLAY_CONFIG + offset,
>> >> +                                                 BIT(28), 0);
>> >> +                            break;
>> >> +                    case VS_DEGAMMA_BT2020:
>> >> +                            load_degamma_table(hw, reg, offset, DEGAMMA_2020);
>> >> +                            if (primary)
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_FRAMEBUFFER_CONFIG_EX + offset,
>> >> +                                                 BIT(5), 0);
>> >> +                            else
>> >> +                                    dc_set_clear(hw,
>> >> +                                                 DC_OVERLAY_CONFIG + offset,
>> >> +                                                 BIT(28), 0);
>> >> +                            break;
>> >> +                    default:
>> >> +                            break;
>> >> +                    }
>> >> +                    plane->degamma.dirty = false;
>> >> +            }
>> >> +    }
>> >> +    plane_commit(hw);
>> >> +}
>> >> +
>> >> +static void setup_display(struct dc_hw *hw, struct dc_hw_display *display)
>> >> +{
>> >> +    u8 id = display->id;
>> >> +    u32 dpi_cfg, offset = id << 2;
>> >> +
>> >> +    if (hw->display[id].enable) {
>> >> +            switch (display->bus_format) {
>> >> +            case MEDIA_BUS_FMT_RGB565_1X16:
>> >> +                    dpi_cfg = 0;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_RGB666_1X18:
>> >> +                    dpi_cfg = 3;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
>> >> +                    dpi_cfg = 4;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_RGB888_1X24:
>> >> +                    dpi_cfg = 5;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_RGB101010_1X30:
>> >> +                    dpi_cfg = 6;
>> >> +                    break;
>> >> +            default:
>> >> +                    dpi_cfg = 5;
>> >> +                    break;
>> >> +            }
>> >> +            dc_write(hw, DC_DISPLAY_DPI_CONFIG + offset, dpi_cfg);
>> >> +
>> >> +            if (id == 0)
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
>> >> +            else
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
>> >> +
>> >> +            dc_write(hw, DC_DISPLAY_H + offset, hw->display[id].h_active |
>> >> +                     (hw->display[id].h_total << 16));
>> >> +            dc_write(hw, DC_DISPLAY_H_SYNC + offset,
>> >> +                     hw->display[id].h_sync_start |
>> >> +                     (hw->display[id].h_sync_end << 15) |
>> >> +                     (hw->display[id].h_sync_polarity ? 0 : BIT(31)) |
>> >> +                     BIT(30));
>> >> +            dc_write(hw, DC_DISPLAY_V + offset, hw->display[id].v_active |
>> >> +                            (hw->display[id].v_total << 16));
>> >> +            dc_write(hw, DC_DISPLAY_V_SYNC + offset,
>> >> +                     hw->display[id].v_sync_start |
>> >> +                     (hw->display[id].v_sync_end << 15) |
>> >> +                     (hw->display[id].v_sync_polarity ? 0 : BIT(31)) |
>> >> +                     BIT(30));
>> >> +
>> >> +            if (hw->info->pipe_sync)
>> >> +                    dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
>> >> +                                 0, BIT(3) | BIT(4));
>> >> +
>> >> +            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(12), 0);
>> >> +            if (id == 0)
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(0), BIT(3));
>> >> +            else
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(1), BIT(3));
>> >> +    } else {
>> >> +            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(12));
>> >> +            if (id == 0)
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
>> >> +            else
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
>> >> +    }
>> >> +}
>> >> +
>> >> +static void setup_display_ex(struct dc_hw *hw, struct dc_hw_display *display)
>> >> +{
>> >> +    u8 id = display->id;
>> >> +    u32 dp_cfg, offset = id << 2;
>> >> +    bool is_yuv = false;
>> >> +
>> >> +    if (hw->display[id].enable && hw->out[id] == OUT_DP) {
>> >> +            switch (display->bus_format) {
>> >> +            case MEDIA_BUS_FMT_RGB565_1X16:
>> >> +                    dp_cfg = 0;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_RGB666_1X18:
>> >> +                    dp_cfg = 1;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_RGB888_1X24:
>> >> +                    dp_cfg = 2;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_RGB101010_1X30:
>> >> +                    dp_cfg = 3;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_UYVY8_1X16:
>> >> +                    dp_cfg = 2 << 4;
>> >> +                    is_yuv = true;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_YUV8_1X24:
>> >> +                    dp_cfg = 4 << 4;
>> >> +                    is_yuv = true;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_UYVY10_1X20:
>> >> +                    dp_cfg = 8 << 4;
>> >> +                    is_yuv = true;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_YUV10_1X30:
>> >> +                    dp_cfg = 10 << 4;
>> >> +                    is_yuv = true;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
>> >> +                    dp_cfg = 12 << 4;
>> >> +                    is_yuv = true;
>> >> +                    break;
>> >> +            case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
>> >> +                    dp_cfg = 13 << 4;
>> >> +                    is_yuv = true;
>> >> +                    break;
>> >> +            default:
>> >> +                    dp_cfg = 2;
>> >> +                    break;
>> >> +            }
>> >> +            if (is_yuv)
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(16), 0);
>> >> +            else
>> >> +                    dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(16));
>> >> +            dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | BIT(3));
>> >> +    }
>> >> +
>> >> +    if (hw->out[id] == OUT_DPI)
>> >> +            dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, BIT(3));
>> >> +
>> >> +    setup_display(hw, display);
>> >> +}
>> >> +
>> >> +static const struct dc_hw_funcs hw_func = {
>> >> +    .gamma = &gamma_ex_commit,
>> >> +    .plane = &plane_ex_commit,
>> >> +    .display = setup_display_ex,
>> >> +};
>> >> +
>> >> +void dc_hw_commit(struct dc_hw *hw)
>> >> +{
>> >> +    u32 i, offset = 0;
>> >> +    u8 plane_num = hw->info->plane_num;
>> >> +    u8 layer_num = hw->info->layer_num;
>> >> +    u8 cursor_num = plane_num - layer_num;
>> >> +
>> >> +    hw->func->gamma(hw);
>> >> +    hw->func->plane(hw);
>> >> +
>> >> +    for (i = 0; i < cursor_num; i++) {
>> >> +            if (hw->cursor[i].dirty) {
>> >> +                    offset = hw->cursor[i].display_id ? DC_CURSOR_OFFSET : 0;
>> >> +                    if (hw->cursor[i].enable) {
>> >> +                            dc_write(hw, DC_CURSOR_ADDRESS + offset,
>> >> +                                     hw->cursor[i].address);
>> >> +                            dc_write(hw, DC_CURSOR_LOCATION + offset, hw->cursor[i].x |
>> >> +                                     (hw->cursor[i].y << 16));
>> >> +                            dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
>> >> +                                         (hw->cursor[i].hot_x << 16) |
>> >> +                                         (hw->cursor[i].hot_y << 8) |
>> >> +                                         (hw->cursor[i].size << 5) |
>> >> +                                         BIT(3) | BIT(2) | 0x02,
>> >> +                                         (0xFF << 16) |
>> >> +                                         (0xFF << 8) |
>> >> +                                         (0x07 << 5) | 0x1F);
>> >> +                    } else {
>> >> +                            dc_set_clear(hw, DC_CURSOR_CONFIG + offset, BIT(3), 0x03);
>> >> +                    }
>> >> +                    hw->cursor[i].dirty = false;
>> >> +            }
>> >> +    }
>> >> +
>> >> +    if (hw->qos.dirty) {
>> >> +            dc_set_clear(hw, DC_QOS_CONFIG, (hw->qos.high_value << 4) |
>> >> +                                     hw->qos.low_value, 0xFF);
>> >> +            hw->qos.dirty = false;
>> >> +    }
>> >> +}
>> >> +
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.h b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
>> >> new file mode 100644
>> >> index 000000000..9a1d767ae
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
>> >> @@ -0,0 +1,492 @@
>> >> +/* SPDX-License-Identifier: GPL-2.0 */
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#ifndef __VS_DC_HW_H__
>> >> +#define __VS_DC_HW_H__
>> >> +
>> >> +#define AQ_INTR_ACKNOWLEDGE                         0x0010
>> >> +#define AQ_INTR_ENBL                                        0x0014
>> >
>> > Please take care to align all the defines.
>> >
>> >> +#define DC_HW_REVISION                                      0x0024
>> >> +#define DC_HW_CHIP_CID                                      0x0030
>> >> +
>> >> +#define DC_REG_BASE                                         0x0800
>> >> +#define DC_REG_RANGE                                        0x2000
>> >> +#define DC_SEC_REG_OFFSET                           0x100000
>> >> +
>> >> +#define DC_FRAMEBUFFER_CONFIG                       0x1518
>> >> +#define DC_FRAMEBUFFER_CONFIG_EX            0x1CC0
>> >> +#define DC_FRAMEBUFFER_SCALE_CONFIG         0x1520
>> >> +#define DC_FRAMEBUFFER_TOP_LEFT                     0x24D8
>> >> +#define DC_FRAMEBUFFER_BOTTOM_RIGHT         0x24E0
>> >> +#define DC_FRAMEBUFFER_ADDRESS                      0x1400
>> >> +#define DC_FRAMEBUFFER_U_ADDRESS            0x1530
>> >> +#define DC_FRAMEBUFFER_V_ADDRESS            0x1538
>> >> +#define DC_FRAMEBUFFER_STRIDE                       0x1408
>> >> +#define DC_FRAMEBUFFER_U_STRIDE                     0x1800
>> >> +#define DC_FRAMEBUFFER_V_STRIDE                     0x1808
>> >> +#define DC_FRAMEBUFFER_SIZE                         0x1810
>> >> +#define DC_FRAMEBUFFER_SCALE_FACTOR_X               0x1828
>> >> +#define DC_FRAMEBUFFER_SCALE_FACTOR_Y               0x1830
>> >> +#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX  0x1838
>> >> +#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA   0x1A00
>> >> +#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX  0x1A08
>> >> +#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA   0x1A10
>> >> +#define DC_FRAMEBUFFER_INIT_OFFSET                  0x1A20
>> >> +#define DC_FRAMEBUFFER_COLOR_KEY                    0x1508
>> >> +#define DC_FRAMEBUFFER_COLOR_KEY_HIGH               0x1510
>> >> +#define DC_FRAMEBUFFER_CLEAR_VALUE                  0x1A18
>> >> +#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX    0x1818
>> >> +#define DC_FRAMEBUFFER_COLOR_TABLE_DATA             0x1820
>> >> +#define DC_FRAMEBUFFER_BG_COLOR                             0x1528
>> >> +#define DC_FRAMEBUFFER_ROI_ORIGIN                   0x1CB0
>> >> +#define DC_FRAMEBUFFER_ROI_SIZE                             0x1CB8
>> >> +#define DC_FRAMEBUFFER_WATER_MARK                   0x1CE8
>> >> +#define DC_FRAMEBUFFER_DEGAMMA_INDEX                0x1D88
>> >> +#define DC_FRAMEBUFFER_DEGAMMA_DATA                 0x1D90
>> >> +#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA              0x1D98
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF0               0x1DA0
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF1               0x1DA8
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF2               0x1DB0
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF3               0x1DB8
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEF4               0x1E00
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD0              0x1E08
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD1              0x1E10
>> >> +#define DC_FRAMEBUFFER_YUVTORGB_COEFD2              0x1E18
>> >> +#define DC_FRAMEBUFFER_Y_CLAMP_BOUND                0x1E88
>> >> +#define DC_FRAMEBUFFER_UV_CLAMP_BOUND               0x1E90
>> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF0               0x1E20
>> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF1               0x1E28
>> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF2               0x1E30
>> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF3               0x1E38
>> >> +#define DC_FRAMEBUFFER_RGBTORGB_COEF4               0x1E40
>> >> +#define DC_FRAMEBUFFER_BLEND_CONFIG                 0x2510
>> >> +#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR             0x2500
>> >> +#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR             0x2508
>> >> +
>> >> +#define DC_OVERLAY_CONFIG                                   0x1540
>> >> +#define DC_OVERLAY_CONFIG_EX                                0x2540
>> >> +#define DC_OVERLAY_SCALE_CONFIG                             0x1C00
>> >> +#define DC_OVERLAY_BLEND_CONFIG                             0x1580
>> >> +#define DC_OVERLAY_TOP_LEFT                                 0x1640
>> >> +#define DC_OVERLAY_BOTTOM_RIGHT                             0x1680
>> >> +#define DC_OVERLAY_ADDRESS                                  0x15C0
>> >> +#define DC_OVERLAY_U_ADDRESS                                0x1840
>> >> +#define DC_OVERLAY_V_ADDRESS                                0x1880
>> >> +#define DC_OVERLAY_STRIDE                                   0x1600
>> >> +#define DC_OVERLAY_U_STRIDE                                 0x18C0
>> >> +#define DC_OVERLAY_V_STRIDE                                 0x1900
>> >> +#define DC_OVERLAY_SIZE                                             0x17C0
>> >> +#define DC_OVERLAY_SCALE_FACTOR_X                   0x1A40
>> >> +#define DC_OVERLAY_SCALE_FACTOR_Y                   0x1A80
>> >> +#define DC_OVERLAY_H_FILTER_COEF_INDEX              0x1AC0
>> >> +#define DC_OVERLAY_H_FILTER_COEF_DATA               0x1B00
>> >> +#define DC_OVERLAY_V_FILTER_COEF_INDEX              0x1B40
>> >> +#define DC_OVERLAY_V_FILTER_COEF_DATA               0x1B80
>> >> +#define DC_OVERLAY_INIT_OFFSET                              0x1BC0
>> >> +#define DC_OVERLAY_COLOR_KEY                                0x1740
>> >> +#define DC_OVERLAY_COLOR_KEY_HIGH                   0x1780
>> >> +#define DC_OVERLAY_CLEAR_VALUE                              0x1940
>> >> +#define DC_OVERLAY_COLOR_TABLE_INDEX                0x1980
>> >> +#define DC_OVERLAY_COLOR_TABLE_DATA                 0x19C0
>> >> +#define DC_OVERLAY_SRC_GLOBAL_COLOR                 0x16C0
>> >> +#define DC_OVERLAY_DST_GLOBAL_COLOR                 0x1700
>> >> +#define DC_OVERLAY_ROI_ORIGIN                               0x1D00
>> >> +#define DC_OVERLAY_ROI_SIZE                                 0x1D40
>> >> +#define DC_OVERLAY_WATER_MARK                               0x1DC0
>> >> +#define DC_OVERLAY_DEGAMMA_INDEX                    0x2200
>> >> +#define DC_OVERLAY_DEGAMMA_DATA                             0x2240
>> >> +#define DC_OVERLAY_DEGAMMA_EX_DATA                  0x2280
>> >> +#define DC_OVERLAY_YUVTORGB_COEF0                   0x1EC0
>> >> +#define DC_OVERLAY_YUVTORGB_COEF1                   0x1F00
>> >> +#define DC_OVERLAY_YUVTORGB_COEF2                   0x1F40
>> >> +#define DC_OVERLAY_YUVTORGB_COEF3                   0x1F80
>> >> +#define DC_OVERLAY_YUVTORGB_COEF4                   0x1FC0
>> >> +#define DC_OVERLAY_YUVTORGB_COEFD0                  0x2000
>> >> +#define DC_OVERLAY_YUVTORGB_COEFD1                  0x2040
>> >> +#define DC_OVERLAY_YUVTORGB_COEFD2                  0x2080
>> >> +#define DC_OVERLAY_Y_CLAMP_BOUND                    0x22C0
>> >> +#define DC_OVERLAY_UV_CLAMP_BOUND                   0x2300
>> >> +#define DC_OVERLAY_RGBTORGB_COEF0                   0x20C0
>> >> +#define DC_OVERLAY_RGBTORGB_COEF1                   0x2100
>> >> +#define DC_OVERLAY_RGBTORGB_COEF2                   0x2140
>> >> +#define DC_OVERLAY_RGBTORGB_COEF3                   0x2180
>> >> +#define DC_OVERLAY_RGBTORGB_COEF4                   0x21C0
>> >> +
>> >> +#define DC_CURSOR_CONFIG                                    0x1468
>> >> +#define DC_CURSOR_ADDRESS                                   0x146C
>> >> +#define DC_CURSOR_LOCATION                                  0x1470
>> >> +#define DC_CURSOR_BACKGROUND                                0x1474
>> >> +#define DC_CURSOR_FOREGROUND                                0x1478
>> >> +#define DC_CURSOR_CLK_GATING                                0x1484
>> >> +#define DC_CURSOR_CONFIG_EX                                 0x24E8
>> >> +#define DC_CURSOR_OFFSET                                    0x1080
>> >> +
>> >> +#define DC_DISPLAY_DITHER_CONFIG            0x1410
>> >> +#define DC_DISPLAY_PANEL_CONFIG                     0x1418
>> >> +#define DC_DISPLAY_PANEL_CONFIG_EX          0x2518
>> >> +#define DC_DISPLAY_DITHER_TABLE_LOW         0x1420
>> >> +#define DC_DISPLAY_DITHER_TABLE_HIGH        0x1428
>> >> +#define DC_DISPLAY_H                                        0x1430
>> >> +#define DC_DISPLAY_H_SYNC                           0x1438
>> >> +#define DC_DISPLAY_V                                        0x1440
>> >> +#define DC_DISPLAY_V_SYNC                           0x1448
>> >> +#define DC_DISPLAY_CURRENT_LOCATION         0x1450
>> >> +#define DC_DISPLAY_GAMMA_INDEX                      0x1458
>> >> +#define DC_DISPLAY_GAMMA_DATA                       0x1460
>> >> +#define DC_DISPLAY_INT                                      0x147C
>> >> +#define DC_DISPLAY_INT_ENABLE                       0x1480
>> >> +#define DC_DISPLAY_DBI_CONFIG                       0x1488
>> >> +#define DC_DISPLAY_GENERAL_CONFIG           0x14B0
>> >> +#define DC_DISPLAY_DPI_CONFIG                       0x14B8
>> >> +#define DC_DISPLAY_PANEL_START                      0x1CCC
>> >> +#define DC_DISPLAY_DEBUG_COUNTER_SELECT             0x14D0
>> >> +#define DC_DISPLAY_DEBUG_COUNTER_VALUE              0x14D8
>> >> +#define DC_DISPLAY_DP_CONFIG                                0x1CD0
>> >> +#define DC_DISPLAY_GAMMA_EX_INDEX                   0x1CF0
>> >> +#define DC_DISPLAY_GAMMA_EX_DATA                    0x1CF8
>> >> +#define DC_DISPLAY_GAMMA_EX_ONE_DATA                0x1D80
>> >> +#define DC_DISPLAY_RGBTOYUV_COEF0                   0x1E48
>> >> +#define DC_DISPLAY_RGBTOYUV_COEF1                   0x1E50
>> >> +#define DC_DISPLAY_RGBTOYUV_COEF2                   0x1E58
>> >> +#define DC_DISPLAY_RGBTOYUV_COEF3                   0x1E60
>> >> +#define DC_DISPLAY_RGBTOYUV_COEF4                   0x1E68
>> >> +#define DC_DISPLAY_RGBTOYUV_COEFD0                  0x1E70
>> >> +#define DC_DISPLAY_RGBTOYUV_COEFD1                  0x1E78
>> >> +#define DC_DISPLAY_RGBTOYUV_COEFD2                  0x1E80
>> >> +
>> >> +#define DC_CLK_GATTING                                      0x1A28
>> >> +#define DC_QOS_CONFIG                                       0x1A38
>> >> +
>> >> +#define DC_TRANSPARENCY_OPAQUE                      0x00
>> >> +#define DC_TRANSPARENCY_KEY                         0x02
>> >> +#define DC_DISPLAY_DITHERTABLE_LOW          0x7B48F3C0
>> >> +#define DC_DISPLAY_DITHERTABLE_HIGH         0x596AD1E2
>> >> +
>> >> +#define GAMMA_SIZE                                          256
>> >> +#define GAMMA_EX_SIZE                                       300
>> >> +#define DEGAMMA_SIZE                                        260
>> >> +
>> >> +#define RGB_TO_RGB_TABLE_SIZE                       9
>> >> +#define YUV_TO_RGB_TABLE_SIZE                       16
>> >> +#define RGB_TO_YUV_TABLE_SIZE                       12
>> >> +
>> >> +#define DC_LAYER_NUM        6
>> >> +#define DC_DISPLAY_NUM      2
>> >> +#define DC_CURSOR_NUM       2
>> >> +
>> >> +#define DC_TILE_MODE4X4     0x15
>> >> +
>> >> +enum dc_chip_rev {
>> >> +    DC_REV_0,/* For HW_REV_5720,HW_REV_5721_311 */
>> >> +    DC_REV_1,/* For HW_REV_5721_30B */
>> >> +    DC_REV_2,/* For HW_REV_5721_310 */
>> >> +};
>> >> +
>> >> +enum dc_hw_plane_id {
>> >> +    PRIMARY_PLANE_0,
>> >> +    OVERLAY_PLANE_0,
>> >> +    OVERLAY_PLANE_1,
>> >> +    PRIMARY_PLANE_1,
>> >> +    OVERLAY_PLANE_2,
>> >> +    OVERLAY_PLANE_3,
>> >> +    CURSOR_PLANE_0,
>> >> +    CURSOR_PLANE_1,
>> >> +    PLANE_NUM
>> >> +};
>> >> +
>> >> +enum dc_hw_color_format {
>> >> +    FORMAT_X4R4G4B4,//0
>> >
>> > Space after the comma will make it more readable. Or just define the
>> > values explicitly, it will also be more readable.
>> >
>> >> +    FORMAT_A4R4G4B4,//1
>> >> +    FORMAT_X1R5G5B5,//2
>> >> +    FORMAT_A1R5G5B5,//3
>> >> +    FORMAT_R5G6B5,//4
>> >> +    FORMAT_X8R8G8B8,//5
>> >> +    FORMAT_A8R8G8B8,//6
>> >> +    FORMAT_YUY2,//7
>> >> +    FORMAT_UYVY,//8
>> >> +    FORMAT_INDEX8,//9
>> >> +    FORMAT_MONOCHROME,//10
>> >> +    FORMAT_YV12 = 0xf,
>> >> +    FORMAT_A8,//16
>> >> +    FORMAT_NV12,//17
>> >> +    FORMAT_NV16,//18
>> >> +    FORMAT_RG16,//19
>> >> +    FORMAT_R8,//20
>> >> +    FORMAT_NV12_10BIT,//21
>> >> +    FORMAT_A2R10G10B10,//22
>> >> +    FORMAT_NV16_10BIT,//23
>> >> +    FORMAT_INDEX1,//24
>> >> +    FORMAT_INDEX2,//25
>> >> +    FORMAT_INDEX4,//26
>> >> +    FORMAT_P010,//27
>> >> +    FORMAT_YUV444,//28
>> >> +    FORMAT_YUV444_10BIT,//29
>> >> +};
>> >> +
>> >> +enum dc_hw_yuv_color_space {
>> >> +    COLOR_SPACE_601 = 0,
>> >> +    COLOR_SPACE_709 = 1,
>> >> +    COLOR_SPACE_2020 = 3,
>> >> +};
>> >> +
>> >> +enum dc_hw_rotation {
>> >> +    ROT_0 = 0,
>> >> +    ROT_90 = 4,
>> >> +    ROT_180 = 5,
>> >> +    ROT_270 = 6,
>> >> +    FLIP_X = 1,
>> >> +    FLIP_Y = 2,
>> >> +    FLIP_XY = 3,
>> >> +};
>> >> +
>> >> +enum dc_hw_swizzle {
>> >> +    SWIZZLE_ARGB = 0,
>> >> +    SWIZZLE_RGBA,
>> >> +    SWIZZLE_ABGR,
>> >> +    SWIZZLE_BGRA,
>> >> +};
>> >> +
>> >> +enum dc_hw_out {
>> >> +    OUT_DPI,
>> >> +    OUT_DP,
>> >> +};
>> >> +
>> >> +enum dc_hw_cursor_size {
>> >> +    CURSOR_SIZE_32X32 = 0,
>> >> +    CURSOR_SIZE_64X64,
>> >> +};
>> >> +
>> >> +enum dc_hw_blend_mode {
>> >> +    /* out.rgb = plane_alpha * fg.rgb +
>> >> +     *              (1 - (plane_alpha * fg.alpha)) * bg.rgb
>> >> +     */
>> >> +    BLEND_PREMULTI,
>> >> +    /* out.rgb = plane_alpha * fg.alpha * fg.rgb +
>> >> +     *              (1 - (plane_alpha * fg.alpha)) * bg.rgb
>> >> +     */
>> >> +    BLEND_COVERAGE,
>> >> +    /* out.rgb = plane_alpha * fg.rgb +
>> >> +     *              (1 - plane_alpha) * bg.rgb
>> >> +     */
>> >> +    BLEND_PIXEL_NONE,
>> >
>> > Can you use defines from <drm/drm_blend.h>?
>> >
>> >> +};
>> >> +
>> >> +struct dc_hw_plane_reg {
>> >> +    u32 y_address;
>> >> +    u32 u_address;
>> >> +    u32 v_address;
>> >> +    u32 y_stride;
>> >> +    u32 u_stride;
>> >> +    u32 v_stride;
>> >> +    u32 size;
>> >> +    u32 top_left;
>> >> +    u32 bottom_right;
>> >> +    u32 scale_factor_x;
>> >> +    u32 scale_factor_y;
>> >> +    u32 h_filter_coef_index;
>> >> +    u32 h_filter_coef_data;
>> >> +    u32 v_filter_coef_index;
>> >> +    u32 v_filter_coef_data;
>> >> +    u32 init_offset;
>> >> +    u32 color_key;
>> >> +    u32 color_key_high;
>> >> +    u32 clear_value;
>> >> +    u32 color_table_index;
>> >> +    u32 color_table_data;
>> >> +    u32 scale_config;
>> >> +    u32 water_mark;
>> >> +    u32 degamma_index;
>> >> +    u32 degamma_data;
>> >> +    u32 degamma_ex_data;
>> >> +    u32 src_global_color;
>> >> +    u32 dst_global_color;
>> >> +    u32 blend_config;
>> >> +    u32 roi_origin;
>> >> +    u32 roi_size;
>> >> +    u32 yuv_to_rgb_coef0;
>> >> +    u32 yuv_to_rgb_coef1;
>> >> +    u32 yuv_to_rgb_coef2;
>> >> +    u32 yuv_to_rgb_coef3;
>> >> +    u32 yuv_to_rgb_coef4;
>> >> +    u32 yuv_to_rgb_coefd0;
>> >> +    u32 yuv_to_rgb_coefd1;
>> >> +    u32 yuv_to_rgb_coefd2;
>> >> +    u32 y_clamp_bound;
>> >> +    u32 uv_clamp_bound;
>> >> +    u32 rgb_to_rgb_coef0;
>> >> +    u32 rgb_to_rgb_coef1;
>> >> +    u32 rgb_to_rgb_coef2;
>> >> +    u32 rgb_to_rgb_coef3;
>> >> +    u32 rgb_to_rgb_coef4;
>> >> +};
>> >> +
>> >> +struct dc_hw_fb {
>> >> +    u32 y_address;
>> >> +    u32 u_address;
>> >> +    u32 v_address;
>> >> +    u32 clear_value;
>> >> +    u32 water_mark;
>> >> +    u16 y_stride;
>> >> +    u16 u_stride;
>> >> +    u16 v_stride;
>> >> +    u16 width;
>> >> +    u16 height;
>> >> +    u8      format;
>> >> +    u8      tile_mode;
>> >> +    u8      rotation;
>> >> +    u8      yuv_color_space;
>> >> +    u8      swizzle;
>> >> +    u8      uv_swizzle;
>> >> +    u8      zpos;
>> >> +    u8      display_id;
>> >> +    bool    clear_enable;
>> >> +    bool    dec_enable;
>> >> +    bool    enable;
>> >> +    bool    dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_scale {
>> >> +    u32 scale_factor_x;
>> >> +    u32 scale_factor_y;
>> >> +    bool    enable;
>> >> +    bool    dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_position {
>> >> +    u16 start_x;
>> >> +    u16 start_y;
>> >> +    u16 end_x;
>> >> +    u16 end_y;
>> >> +    bool    dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_blend {
>> >> +    u8      alpha;
>> >> +    u8      blend_mode;
>> >> +    bool    dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_colorkey {
>> >> +    u32 colorkey;
>> >> +    u32 colorkey_high;
>> >> +    u8      transparency;
>> >> +    bool dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_roi {
>> >> +    u16 x;
>> >> +    u16 y;
>> >> +    u16 width;
>> >> +    u16 height;
>> >> +    bool enable;
>> >> +    bool dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_cursor {
>> >> +    u32 address;
>> >> +    u16 x;
>> >> +    u16 y;
>> >> +    u16 hot_x;
>> >> +    u16 hot_y;
>> >> +    u8      size;
>> >> +    u8      display_id;
>> >> +    bool    enable;
>> >> +    bool    dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_display {
>> >> +    u32 bus_format;
>> >> +    u16 h_active;
>> >> +    u16 h_total;
>> >> +    u16 h_sync_start;
>> >> +    u16 h_sync_end;
>> >> +    u16 v_active;
>> >> +    u16 v_total;
>> >> +    u16 v_sync_start;
>> >> +    u16 v_sync_end;
>> >> +    u8      id;
>> >> +    bool    h_sync_polarity;
>> >> +    bool    v_sync_polarity;
>> >> +    bool    enable;
>> >> +};
>> >> +
>> >> +struct dc_hw_gamma {
>> >> +    u16 gamma[GAMMA_EX_SIZE][3];
>> >> +    bool    enable;
>> >> +    bool    dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_degamma {
>> >> +    u16 degamma[DEGAMMA_SIZE][3];
>> >> +    u32 mode;
>> >> +    bool    dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_plane {
>> >> +    struct dc_hw_fb                 fb;
>> >> +    struct dc_hw_position   pos;
>> >> +    struct dc_hw_scale              scale;
>> >> +    struct dc_hw_blend              blend;
>> >> +    struct dc_hw_roi                roi;
>> >> +    struct dc_hw_colorkey   colorkey;
>> >> +    struct dc_hw_degamma    degamma;
>> >> +};
>> >> +
>> >> +struct dc_hw_qos {
>> >> +    u8        low_value;
>> >> +    u8        high_value;
>> >> +    bool  dirty;
>> >> +};
>> >> +
>> >> +struct dc_hw_read {
>> >> +    u32                     reg;
>> >> +    u32                     value;
>> >> +};
>> >> +
>> >> +struct dc_hw;
>> >> +struct dc_hw_funcs {
>> >> +    void (*gamma)(struct dc_hw *hw);
>> >> +    void (*plane)(struct dc_hw *hw);
>> >> +    void (*display)(struct dc_hw *hw, struct dc_hw_display *display);
>> >> +};
>> >> +
>> >> +struct dc_hw {
>> >> +    enum dc_chip_rev rev;
>> >> +    enum dc_hw_out          out[DC_DISPLAY_NUM];
>> >> +    void                    *hi_base;
>> >> +    void                    *reg_base;
>> >> +
>> >> +    struct dc_hw_display display[DC_DISPLAY_NUM];
>> >> +    struct dc_hw_gamma       gamma[DC_DISPLAY_NUM];
>> >> +    struct dc_hw_plane       plane[DC_LAYER_NUM];
>> >> +    struct dc_hw_cursor  cursor[DC_CURSOR_NUM];
>> >> +    struct dc_hw_qos         qos;
>> >> +    struct dc_hw_funcs       *func;
>> >> +    struct vs_dc_info        *info;
>> >> +};
>> >> +
>> >> +int dc_hw_init(struct dc_hw *hw);
>> >> +void dc_hw_deinit(struct dc_hw *hw);
>> >> +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
>> >> +                    struct dc_hw_fb *fb, struct dc_hw_scale *scale,
>> >> +                    struct dc_hw_position *pos, struct dc_hw_blend *blend);
>> >> +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode);
>> >> +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi);
>> >> +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
>> >> +                       struct dc_hw_colorkey *colorkey);
>> >> +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos);
>> >> +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor);
>> >> +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
>> >> +                    u16 r, u16 g, u16 b);
>> >> +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable);
>> >> +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display);
>> >> +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable);
>> >> +u32 dc_hw_get_interrupt(struct dc_hw *hw);
>> >> +bool dc_hw_check_underflow(struct dc_hw *hw);
>> >> +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable);
>> >> +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id);
>> >> +void dc_hw_commit(struct dc_hw *hw);
>> >> +
>> >> +#endif /* __VS_DC_HW_H__ */
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
>> >> index da7698c3d..3cd533cfa 100644
>> >> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
>> >> @@ -21,6 +21,7 @@
>> >>
>> >>   #include "vs_drv.h"
>> >>   #include "vs_modeset.h"
>> >> +#include "vs_dc.h"
>> >>
>> >>   #define DRV_NAME   "verisilicon"
>> >>   #define DRV_DESC   "Verisilicon DRM driver"
>> >> @@ -123,6 +124,7 @@ static const struct component_master_ops vs_drm_ops = {
>> >>   };
>> >>
>> >>   static struct platform_driver *drm_sub_drivers[] = {
>> >> +    &dc_platform_driver,
>> >>
>> >>   };
>> >>
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c
>> >> new file mode 100644
>> >> index 000000000..9bd066015
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_plane.c
>> >> @@ -0,0 +1,526 @@
>> >> +// SPDX-License-Identifier: GPL-2.0
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#include <drm/drm_atomic.h>
>> >> +#include <drm/drm_atomic_helper.h>
>> >> +#include <drm/drm_blend.h>
>> >> +#include <drm/drm_gem_dma_helper.h>
>> >> +#include <drm/drm_fb_dma_helper.h>
>> >> +#include <drm/drm_framebuffer.h>
>> >> +#include <drm/drm_plane.h>
>> >> +#include <drm/drm_plane_helper.h>
>> >> +
>> >> +#include <drm/vs_drm.h>
>> >> +
>> >> +#include "vs_plane.h"
>> >> +#include "vs_drv.h"
>> >> +#include "vs_dc.h"
>> >> +
>> >> +static void vs_plane_reset(struct drm_plane *plane)
>> >> +{
>> >> +    struct vs_plane_state *state;
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +
>> >> +    if (plane->state) {
>> >> +            __drm_atomic_helper_plane_destroy_state(plane->state);
>> >> +
>> >> +            state = to_vs_plane_state(plane->state);
>> >> +            kfree(state);
>> >> +            plane->state = NULL;
>> >> +    }
>> >> +
>> >
>> > Use vs_plane_destroy_state directly. This way you won't miss a refcount
>> > on blobs.
>> >
>> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
>> >> +    if (!state)
>> >> +            return;
>> >> +
>> >> +    state->degamma = VS_DEGAMMA_DISABLE;
>> >> +    state->degamma_changed = false;
>> >> +    state->base.zpos = vs_plane->id;
>> >> +    memset(&state->status, 0, sizeof(state->status));
>> >> +
>> >> +    __drm_atomic_helper_plane_reset(plane, &state->base);
>> >> +}
>> >> +
>> >> +static void _vs_plane_duplicate_blob(struct vs_plane_state *state,
>> >> +                                 struct vs_plane_state *ori_state)
>> >> +{
>> >> +    state->watermark = ori_state->watermark;
>> >> +    state->color_mgmt = ori_state->color_mgmt;
>> >> +    state->roi = ori_state->roi;
>> >> +
>> >> +    if (state->watermark)
>> >> +            drm_property_blob_get(state->watermark);
>> >> +    if (state->color_mgmt)
>> >> +            drm_property_blob_get(state->color_mgmt);
>> >> +    if (state->roi)
>> >> +            drm_property_blob_get(state->roi);
>> >> +}
>> >> +
>> >> +static int
>> >> +_vs_plane_set_property_blob_from_id(struct drm_device *dev,
>> >> +                                struct drm_property_blob **blob,
>> >> +                                u64 blob_id,
>> >> +                                size_t expected_size)
>> >> +{
>> >> +    struct drm_property_blob *new_blob = NULL;
>> >> +
>> >> +    if (blob_id) {
>> >> +            new_blob = drm_property_lookup_blob(dev, blob_id);
>> >> +            if (!new_blob)
>> >> +                    return -EINVAL;
>> >> +
>> >> +            if (new_blob->length != expected_size) {
>> >> +                    drm_property_blob_put(new_blob);
>> >> +                    return -EINVAL;
>> >> +            }
>> >> +    }
>> >> +
>> >> +    drm_property_replace_blob(blob, new_blob);
>> >> +    drm_property_blob_put(new_blob);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static struct drm_plane_state *
>> >> +vs_plane_atomic_duplicate_state(struct drm_plane *plane)
>> >> +{
>> >> +    struct vs_plane_state *ori_state;
>> >> +    struct vs_plane_state *state;
>> >> +
>> >> +    if (WARN_ON(!plane->state))
>> >> +            return NULL;
>> >> +
>> >> +    ori_state = to_vs_plane_state(plane->state);
>> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
>> >> +    if (!state)
>> >> +            return NULL;
>> >> +
>> >> +    __drm_atomic_helper_plane_duplicate_state(plane, &state->base);
>> >> +
>> >> +    state->degamma = ori_state->degamma;
>> >> +    state->degamma_changed = ori_state->degamma_changed;
>> >> +
>> >> +    _vs_plane_duplicate_blob(state, ori_state);
>> >> +    memcpy(&state->status, &ori_state->status, sizeof(ori_state->status));
>> >> +
>> >> +    return &state->base;
>> >> +}
>> >> +
>> >> +static void vs_plane_atomic_destroy_state(struct drm_plane *plane,
>> >> +                                      struct drm_plane_state *state)
>> >> +{
>> >> +    struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
>> >> +
>> >> +    __drm_atomic_helper_plane_destroy_state(state);
>> >> +
>> >> +    drm_property_blob_put(vs_plane_state->watermark);
>> >> +    drm_property_blob_put(vs_plane_state->color_mgmt);
>> >> +    drm_property_blob_put(vs_plane_state->roi);
>> >> +    kfree(vs_plane_state);
>> >> +}
>> >> +
>> >> +static int vs_plane_atomic_set_property(struct drm_plane *plane,
>> >> +                                    struct drm_plane_state *state,
>> >> +                                    struct drm_property *property,
>> >> +                                    uint64_t val)
>> >> +{
>> >> +    struct drm_device *dev = plane->dev;
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +    struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
>> >> +    int ret = 0;
>> >> +
>> >> +    if (property == vs_plane->degamma_mode) {
>> >> +            if (vs_plane_state->degamma != val) {
>> >> +                    vs_plane_state->degamma = val;
>> >> +                    vs_plane_state->degamma_changed = true;
>> >> +            } else {
>> >> +                    vs_plane_state->degamma_changed = false;
>> >> +            }
>> >> +    } else if (property == vs_plane->watermark_prop) {
>> >> +            ret = _vs_plane_set_property_blob_from_id(dev,
>> >> +                                                      &vs_plane_state->watermark,
>> >> +                                                      val,
>> >> +                                                      sizeof(struct drm_vs_watermark));
>> >> +            return ret;
>> >> +    } else if (property == vs_plane->color_mgmt_prop) {
>> >> +            ret = _vs_plane_set_property_blob_from_id(dev,
>> >> +                                                      &vs_plane_state->color_mgmt,
>> >> +                                                      val,
>> >> +                                                      sizeof(struct drm_vs_color_mgmt));
>> >> +            return ret;
>> >> +    } else if (property == vs_plane->roi_prop) {
>> >> +            ret = _vs_plane_set_property_blob_from_id(dev,
>> >> +                                                      &vs_plane_state->roi,
>> >> +                                                      val,
>> >> +                                                      sizeof(struct drm_vs_roi));
>> >> +            return ret;
>> >> +    } else {
>> >> +            return -EINVAL;
>> >> +    }
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int vs_plane_atomic_get_property(struct drm_plane *plane,
>> >> +                                    const struct drm_plane_state *state,
>> >> +                                    struct drm_property *property,
>> >> +                                    uint64_t *val)
>> >> +{
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +    const struct vs_plane_state *vs_plane_state =
>> >> +            container_of(state, const struct vs_plane_state, base);
>> >> +
>> >> +    if (property == vs_plane->degamma_mode)
>> >> +            *val = vs_plane_state->degamma;
>> >> +    else if (property == vs_plane->watermark_prop)
>> >> +            *val = (vs_plane_state->watermark) ?
>> >> +                                    vs_plane_state->watermark->base.id : 0;
>> >> +    else if (property == vs_plane->color_mgmt_prop)
>> >> +            *val = (vs_plane_state->color_mgmt) ?
>> >> +                                    vs_plane_state->color_mgmt->base.id : 0;
>> >
>> > degamma and color management should use standard properties.

hello Dmitry:
There is a question that troubles me

drm_plane include such standard properties.
{
	struct drm_property *alpha_property;
	struct drm_property *zpos_property;
	struct drm_property *rotation_property;
	struct drm_property *blend_mode_property;
	struct drm_property *color_encoding_property;
	struct drm_property *color_range_property;
	struct drm_property *scaling_filter_property;
}

it doesn't include degamma and color management properties

>> >
>> >> +    else if (property == vs_plane->roi_prop)
>> >> +            *val = (vs_plane_state->roi) ?
>> >> +                                    vs_plane_state->roi->base.id : 0;
>> >> +    else
>> >> +            return -EINVAL;
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static bool vs_format_mod_supported(struct drm_plane *plane,
>> >> +                                u32 format,
>> >> +                                u64 modifier)
>> >> +{
>> >> +    int i;
>> >> +
>> >> +    /* We always have to allow these modifiers:
>> >> +     * 1. Core DRM checks for LINEAR support if userspace does not provide modifiers.
>> >> +     * 2. Not passing any modifiers is the same as explicitly passing INVALID.
>> >> +     */
>> >> +    if (modifier == DRM_FORMAT_MOD_LINEAR)
>> >> +            return true;
>> >> +
>> >> +    /* Check that the modifier is on the list of the plane's supported modifiers. */
>> >> +    for (i = 0; i < plane->modifier_count; i++) {
>> >> +            if (modifier == plane->modifiers[i])
>> >> +                    break;
>> >> +    }
>> >> +
>> >> +    if (i == plane->modifier_count)
>> >> +            return false;
>> >> +
>> >> +    return true;
>> >> +}
>> >> +
>> >> +const struct drm_plane_funcs vs_plane_funcs = {
>> >> +    .update_plane           = drm_atomic_helper_update_plane,
>> >> +    .disable_plane          = drm_atomic_helper_disable_plane,
>> >> +    .reset                  = vs_plane_reset,
>> >> +    .atomic_duplicate_state = vs_plane_atomic_duplicate_state,
>> >> +    .atomic_destroy_state   = vs_plane_atomic_destroy_state,
>> >> +    .atomic_set_property    = vs_plane_atomic_set_property,
>> >> +    .atomic_get_property    = vs_plane_atomic_get_property,
>> >> +    .format_mod_supported   = vs_format_mod_supported,
>> >> +};
>> >> +
>> >> +static unsigned char vs_get_plane_number(struct drm_framebuffer *fb)
>> >> +{
>> >> +    const struct drm_format_info *info;
>> >> +
>> >> +    if (!fb)
>> >> +            return 0;
>> >> +
>> >> +    info = drm_format_info(fb->format->format);
>> >> +    if (!info || info->num_planes > DRM_FORMAT_MAX_PLANES)
>> >> +            return 0;
>> >> +
>> >> +    return info->num_planes;
>> >> +}
>> >> +
>> >> +static int vs_plane_atomic_check(struct drm_plane *plane,
>> >> +                             struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> >> +                                                                             plane);
>> >> +    unsigned char i, num_planes;
>> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
>> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
>> >> +
>> >> +    if (!crtc || !fb)
>> >> +            return 0;
>> >> +
>> >> +    num_planes = vs_get_plane_number(fb);
>> >> +
>> >> +    for (i = 0; i < num_planes; i++) {
>> >> +            dma_addr_t dma_addr;
>> >> +
>> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
>> >> +            plane_state->dma_addr[i] = dma_addr;
>> >> +    }
>> >> +
>> >> +    return vs_dc_check_plane(dc, plane, state);
>> >> +}
>> >> +
>> >> +static int vs_cursor_plane_atomic_check(struct drm_plane *plane,
>> >> +                                    struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
>> >> +                                                                      plane);
>> >> +    unsigned char i, num_planes;
>> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
>> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
>> >> +
>> >> +    if (!crtc || !fb)
>> >> +            return 0;
>> >> +
>> >> +    num_planes = vs_get_plane_number(fb);
>> >> +
>> >> +    for (i = 0; i < num_planes; i++) {
>> >> +            dma_addr_t dma_addr;
>> >> +
>> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
>> >> +            plane_state->dma_addr[i] = dma_addr;
>> >> +    }
>> >> +
>> >> +    return vs_dc_check_cursor_plane(dc, plane, state);
>> >> +}
>> >> +
>> >> +static void vs_plane_atomic_update(struct drm_plane *plane,
>> >> +                               struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
>> >> +                                                                      plane);
>> >
>> > New line after the equal sign will be better.
>> >
>> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> >> +                                                                      plane);
>> >> +
>> >> +    unsigned char i, num_planes;
>> >> +    struct drm_framebuffer *fb;
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    if (!new_state->fb || !new_state->crtc)
>> >> +            return;
>> >
>> > if (!new_state->visible) ?

no matter what value it is ? the driver will handle it
in func 

static void update_fb(struct vs_plane *plane, u8 display_id,
		      struct dc_hw_fb *fb, struct drm_plane_state *state)
{
	struct vs_plane_state *plane_state = to_vs_plane_state(state);
	struct drm_framebuffer *drm_fb = state->fb;
	struct drm_rect *src = &state->src;

.......
	fb->enable = state->visible;
.......
}

so no need add "if (!new_state->visible)" i think

>> >
>> >> +
>> >> +    fb = new_state->fb;
>> >> +
>> >> +    drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
>> >> +
>> >> +    num_planes = vs_get_plane_number(fb);
>> >> +
>> >> +    for (i = 0; i < num_planes; i++) {
>> >> +            dma_addr_t dma_addr;
>> >> +
>> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
>> >> +            plane_state->dma_addr[i] = dma_addr;
>> >> +    }
>> >> +
>> >> +    plane_state->status.src = drm_plane_state_src(new_state);
>> >> +    plane_state->status.dest = drm_plane_state_dest(new_state);
>> >> +
>> >> +    vs_dc_update_plane(dc, vs_plane, plane, state);
>> >> +}
>> >> +
>> >> +static void vs_cursor_plane_atomic_update(struct drm_plane *plane,
>> >> +                                      struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
>> >> +                                                                       plane);
>> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> >> +                                                                       plane);
>> >> +    unsigned char i, num_planes;
>> >> +    struct drm_framebuffer *fb;
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
>> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    if (!new_state->fb || !new_state->crtc)
>> >
>> > and here.
>> >
>> >> +            return;
>> >> +
>> >> +    fb = new_state->fb;
>> >> +    drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
>> >> +
>> >> +    num_planes = vs_get_plane_number(fb);
>> >> +
>> >> +    for (i = 0; i < num_planes; i++) {
>> >> +            dma_addr_t dma_addr;
>> >> +
>> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
>> >> +            plane_state->dma_addr[i] = dma_addr;
>> >> +    }
>> >> +
>> >> +    plane_state->status.src = drm_plane_state_src(new_state);
>> >> +    plane_state->status.dest = drm_plane_state_dest(new_state);
>> >> +
>> >> +    vs_dc_update_cursor_plane(dc, vs_plane, plane, state);
>> >> +}
>> >> +
>> >> +static void vs_plane_atomic_disable(struct drm_plane *plane,
>> >> +                                struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> >> +                                                                       plane);
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    vs_dc_disable_plane(dc, vs_plane, old_state);
>> >> +}
>> >> +
>> >> +static void vs_cursor_plane_atomic_disable(struct drm_plane *plane,
>> >> +                                       struct drm_atomic_state *state)
>> >> +{
>> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
>> >> +                                                                       plane);
>> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
>> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
>> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
>> >> +
>> >> +    vs_dc_disable_cursor_plane(dc, vs_plane, old_state);
>> >> +}
>> >> +
>> >> +const struct drm_plane_helper_funcs primary_plane_helpers = {
>> >> +    .atomic_check   = vs_plane_atomic_check,
>> >> +    .atomic_update  = vs_plane_atomic_update,
>> >> +    .atomic_disable = vs_plane_atomic_disable,
>> >> +};
>> >> +
>> >> +const struct drm_plane_helper_funcs overlay_plane_helpers = {
>> >> +    .atomic_check   = vs_plane_atomic_check,
>> >> +    .atomic_update  = vs_plane_atomic_update,
>> >> +    .atomic_disable = vs_plane_atomic_disable,
>> >> +};
>> >> +
>> >> +const struct drm_plane_helper_funcs cursor_plane_helpers = {
>> >> +    .atomic_check   = vs_cursor_plane_atomic_check,
>> >> +    .atomic_update  = vs_cursor_plane_atomic_update,
>> >> +    .atomic_disable = vs_cursor_plane_atomic_disable,
>> >> +};
>> >> +
>> >> +static const struct drm_prop_enum_list vs_degamma_mode_enum_list[] = {
>> >> +    { VS_DEGAMMA_DISABLE,   "disabled" },
>> >> +    { VS_DEGAMMA_BT709,     "preset degamma for BT709" },
>> >> +    { VS_DEGAMMA_BT2020,    "preset degamma for BT2020" },
>> >> +};
>> >> +
>> >> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
>> >> +                             struct vs_plane_info *info,
>> >> +                             unsigned int layer_num,
>> >> +                             unsigned int possible_crtcs)
>> >> +{
>> >> +    struct vs_plane *plane;
>> >> +    int ret;
>> >> +
>> >> +    if (!info)
>> >> +            return NULL;
>> >> +
>> >> +    plane = drmm_universal_plane_alloc(drm_dev, struct vs_plane, base,
>> >> +                                       possible_crtcs,
>> >> +                                       &vs_plane_funcs,
>> >> +                                       info->formats, info->num_formats,
>> >> +                                       info->modifiers, info->type,
>> >> +                                       info->name ? info->name : NULL);
>> >> +    if (IS_ERR(plane))
>> >> +            return ERR_CAST(plane);
>> >> +
>> >> +    if (info->type == DRM_PLANE_TYPE_PRIMARY)
>> >> +            drm_plane_helper_add(&plane->base, &primary_plane_helpers);
>> >> +    else if (info->type == DRM_PLANE_TYPE_CURSOR)
>> >> +            drm_plane_helper_add(&plane->base, &cursor_plane_helpers);
>> >> +    else
>> >> +            drm_plane_helper_add(&plane->base, &overlay_plane_helpers);
>> >> +
>> >> +    /* Set up the plane properties */
>> >> +    if (info->degamma_size) {
>> >> +            plane->degamma_mode =
>> >> +            drm_property_create_enum(drm_dev, 0,
>> >> +                                     "DEGAMMA_MODE",
>> >> +                                     vs_degamma_mode_enum_list,
>> >> +                                     ARRAY_SIZE(vs_degamma_mode_enum_list));
>> >> +
>> >> +            if (!plane->degamma_mode)
>> >> +                    return NULL;
>> >> +
>> >> +            drm_object_attach_property(&plane->base.base,
>> >> +                                       plane->degamma_mode,
>> >> +                                       VS_DEGAMMA_DISABLE);
>> >> +    }
>> >> +
>> >> +    if (info->rotation) {
>> >> +            ret = drm_plane_create_rotation_property(&plane->base,
>> >> +                                                     DRM_MODE_ROTATE_0,
>> >> +                                                     info->rotation);
>> >> +            if (ret)
>> >> +                    return NULL;
>> >> +    }
>> >> +
>> >> +    if (info->blend_mode) {
>> >> +            ret = drm_plane_create_blend_mode_property(&plane->base,
>> >> +                                                       info->blend_mode);
>> >> +            if (ret)
>> >> +                    return NULL;
>> >> +            ret = drm_plane_create_alpha_property(&plane->base);
>> >> +            if (ret)
>> >> +                    return NULL;
>> >> +    }
>> >> +
>> >> +    if (info->color_encoding) {
>> >> +            ret = drm_plane_create_color_properties(&plane->base,
>> >> +                                                    info->color_encoding,
>> >> +                                                    BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
>> >> +                                                    DRM_COLOR_YCBCR_BT709,
>> >> +                                                    DRM_COLOR_YCBCR_LIMITED_RANGE);
>> >> +            if (ret)
>> >> +                    return NULL;
>> >> +    }
>> >> +
>> >> +    if (info->zpos != 255) {
>> >> +            ret = drm_plane_create_zpos_property(&plane->base, info->zpos, 0,
>> >> +                                                 layer_num - 1);
>> >> +            if (ret)
>> >> +                    return NULL;
>> >> +    } else {
>> >> +            ret = drm_plane_create_zpos_immutable_property(&plane->base,
>> >> +                                                           info->zpos);
>> >> +            if (ret)
>> >> +                    return NULL;
>> >> +    }
>> >> +
>> >> +    if (info->watermark) {
>> >> +            plane->watermark_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
>> >> +                                                        "WATERMARK", 0);
>> >> +            if (!plane->watermark_prop)
>> >> +                    return NULL;
>> >> +            drm_object_attach_property(&plane->base.base, plane->watermark_prop, 0);
>> >> +    }
>> >> +
>> >> +    if (info->color_mgmt) {
>> >> +            plane->color_mgmt_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
>> >> +                                                         "COLOR_CONFIG", 0);
>> >> +            if (!plane->color_mgmt_prop)
>> >> +                    return NULL;
>> >> +
>> >> +            drm_object_attach_property(&plane->base.base, plane->color_mgmt_prop, 0);
>> >> +    }
>> >> +
>> >> +    if (info->roi) {
>> >> +            plane->roi_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
>> >> +                                                  "ROI", 0);
>> >> +            if (!plane->roi_prop)
>> >> +                    return NULL;
>> >> +
>> >> +            drm_object_attach_property(&plane->base.base, plane->roi_prop, 0);
>> >> +    }
>> >> +
>> >> +    return plane;
>> >> +}
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h
>> >> new file mode 100644
>> >> index 000000000..554b74e96
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_plane.h
>> >> @@ -0,0 +1,58 @@
>> >> +/* SPDX-License-Identifier: GPL-2.0 */
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#ifndef __VS_PLANE_H__
>> >> +#define __VS_PLANE_H__
>> >> +
>> >> +#include <drm/drm_fourcc.h>
>> >> +#include <drm/drm_plane_helper.h>
>> >> +
>> >> +#include "vs_type.h"
>> >> +
>> >> +struct vs_plane_status {
>> >> +    u32 tile_mode;
>> >> +    struct drm_rect src;
>> >> +    struct drm_rect dest;
>> >> +};
>> >> +
>> >> +struct vs_plane_state {
>> >> +    struct drm_plane_state base;
>> >> +    struct vs_plane_status status; /* for debugfs */
>> >> +
>> >> +    struct drm_property_blob *watermark;
>> >> +    struct drm_property_blob *color_mgmt;
>> >> +    struct drm_property_blob *roi;
>> >> +    dma_addr_t dma_addr[DRM_FORMAT_MAX_PLANES];
>> >> +
>> >> +    u32 degamma;
>> >> +    bool degamma_changed;
>> >> +};
>> >> +
>> >> +struct vs_plane {
>> >> +    struct drm_plane base;
>> >> +    u8 id;
>> >> +
>> >> +    struct drm_property *degamma_mode;
>> >> +    struct drm_property *watermark_prop;
>> >> +    struct drm_property *color_mgmt_prop;
>> >> +    struct drm_property *roi_prop;
>> >> +};
>> >> +
>> >> +struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
>> >> +                             struct vs_plane_info *info,
>> >> +                             unsigned int layer_num,
>> >> +                             unsigned int possible_crtcs);
>> >> +
>> >> +static inline struct vs_plane *to_vs_plane(struct drm_plane *plane)
>> >> +{
>> >> +    return container_of(plane, struct vs_plane, base);
>> >> +}
>> >> +
>> >> +static inline struct vs_plane_state *
>> >> +to_vs_plane_state(struct drm_plane_state *state)
>> >> +{
>> >> +    return container_of(state, struct vs_plane_state, base);
>> >> +}
>> >> +#endif /* __VS_PLANE_H__ */
>> >> diff --git a/drivers/gpu/drm/verisilicon/vs_type.h b/drivers/gpu/drm/verisilicon/vs_type.h
>> >> new file mode 100644
>> >> index 000000000..7d3378e29
>> >> --- /dev/null
>> >> +++ b/drivers/gpu/drm/verisilicon/vs_type.h
>> >> @@ -0,0 +1,69 @@
>> >> +/* SPDX-License-Identifier: GPL-2.0 */
>> >> +/*
>> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
>> >> + */
>> >> +
>> >> +#ifndef __VS_TYPE_H__
>> >> +#define __VS_TYPE_H__
>> >> +
>> >> +#include <drm/drm_plane.h>
>> >> +#include <drm/drm_plane_helper.h>
>> >> +
>> >> +struct vs_plane_info {
>> >> +    const char *name;
>> >> +    u8 id;
>> >> +    enum drm_plane_type type;
>> >> +    unsigned int num_formats;
>> >> +    const u32 *formats;
>> >> +    u8 num_modifiers;
>> >> +    const u64 *modifiers;
>> >> +    unsigned int min_width;
>> >> +    unsigned int min_height;
>> >> +    unsigned int max_width;
>> >> +    unsigned int max_height;
>> >> +    unsigned int rotation;
>> >> +    unsigned int blend_mode;
>> >> +    unsigned int color_encoding;
>> >> +
>> >> +    /* 0 means no de-gamma LUT */
>> >> +    unsigned int degamma_size;
>> >> +
>> >> +    int min_scale; /* 16.16 fixed point */
>> >> +    int max_scale; /* 16.16 fixed point */
>> >> +
>> >> +    /* default zorder value,
>> >> +     * and 255 means unsupported zorder capability
>> >> +     */
>> >> +    u8       zpos;
>> >> +
>> >> +    bool watermark;
>> >> +    bool color_mgmt;
>> >> +    bool roi;
>> >> +};
>> >> +
>> >> +struct vs_dc_info {
>> >> +    const char *name;
>> >> +
>> >> +    u8 panel_num;
>> >> +
>> >> +    /* planes */
>> >> +    u8 plane_num;
>> >> +    const struct vs_plane_info *planes;
>> >> +
>> >> +    u8 layer_num;
>> >> +    unsigned int max_bpc;
>> >> +    unsigned int color_formats;
>> >> +
>> >> +    /* 0 means no gamma LUT */
>> >> +    u16 gamma_size;
>> >> +    u8 gamma_bits;
>> >> +
>> >> +    u16 pitch_alignment;
>> >> +
>> >> +    bool pipe_sync;
>> >> +    bool background;
>> >> +    bool panel_sync;
>> >> +    bool cap_dec;
>> >> +};
>> >> +
>> >> +#endif /* __VS_TYPE_H__ */
>> >
> 
> 
> 

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 3/6] drm/fourcc: Add drm/vs tiled modifiers
  2023-10-25 15:44       ` Simon Ser
  (?)
@ 2023-11-15 14:12         ` Keith Zhao
  -1 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-11-15 14:12 UTC (permalink / raw)
  To: Simon Ser
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Maxime Ripard, Jagan Teki, Rob Herring,
	Chris Morgan, Paul Walmsley, Bjorn Andersson, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Shawn Guo, christian.koenig

ok, I will do this in my next patch
Thanks your advice!

On 2023/10/25 23:44, Simon Ser wrote:
>  it seems like you could start with just simple
> enumerated modifiers like Intel does, and then only switch to more
> complicated logic with macros and fields if there is an actual need in
> the future.

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 3/6] drm/fourcc: Add drm/vs tiled modifiers
@ 2023-11-15 14:12         ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-11-15 14:12 UTC (permalink / raw)
  To: Simon Ser
  Cc: Emil Renner Berthing, dri-devel, Krzysztof Kozlowski,
	linux-riscv, Sumit Semwal, Shengyang Chen, linux-media,
	devicetree, Conor Dooley, Albert Ou, Maxime Ripard, Jagan Teki,
	linaro-mm-sig, Rob Herring, Chris Morgan, Paul Walmsley,
	Bjorn Andersson, linux-kernel, christian.koenig, Jack Zhu,
	Palmer Dabbelt, Thomas Zimmermann, Shawn Guo, Changhuang Liang

ok, I will do this in my next patch
Thanks your advice!

On 2023/10/25 23:44, Simon Ser wrote:
>  it seems like you could start with just simple
> enumerated modifiers like Intel does, and then only switch to more
> complicated logic with macros and fields if there is an actual need in
> the future.

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 3/6] drm/fourcc: Add drm/vs tiled modifiers
@ 2023-11-15 14:12         ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-11-15 14:12 UTC (permalink / raw)
  To: Simon Ser
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, Krzysztof Kozlowski, Sumit Semwal,
	Emil Renner Berthing, Shengyang Chen, Conor Dooley, Albert Ou,
	Thomas Zimmermann, Maxime Ripard, Jagan Teki, Rob Herring,
	Chris Morgan, Paul Walmsley, Bjorn Andersson, Changhuang Liang,
	Jack Zhu, Palmer Dabbelt, Shawn Guo, christian.koenig

ok, I will do this in my next patch
Thanks your advice!

On 2023/10/25 23:44, Simon Ser wrote:
>  it seems like you could start with just simple
> enumerated modifiers like Intel does, and then only switch to more
> complicated logic with macros and fields if there is an actual need in
> the future.

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
  2023-10-25 13:57     ` Maxime Ripard
  (?)
@ 2023-11-15 14:52       ` Keith Zhao
  -1 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-11-15 14:52 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, David Airlie, Daniel Vetter, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Emil Renner Berthing,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Maarten Lankhorst,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Jack Zhu, Shengyang Chen, Changhuang Liang



On 2023/10/25 21:57, Maxime Ripard wrote:
> On Wed, Oct 25, 2023 at 06:39:56PM +0800, Keith Zhao wrote:
>> +static struct drm_crtc_state *
>> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
>> +{
>> +	struct vs_crtc_state *ori_state;
>> +	struct vs_crtc_state *state;
>> +
>> +	if (!crtc->state)
>> +		return NULL;
>> +
>> +	ori_state = to_vs_crtc_state(crtc->state);
>> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> +	if (!state)
>> +		return NULL;
>> +
>> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
>> +
>> +	state->output_fmt = ori_state->output_fmt;
> 
> That field is never set in your patch.
> 
>> +	state->encoder_type = ori_state->encoder_type;
> 
> That isn't either, and it's not clear why you would need the
> encoder_type stored in the CRTC?
> 
>> +	state->bpp = ori_state->bpp;
> 
> You seem to derive that from output_fmt, it doesn't need to be in the
> CRTC state.
> 
>> +	state->underflow = ori_state->underflow;
> 
> Assuming you're setting this from the interrupt handler, it's unsafe,
> you shouldn't do that. What are you using it for?
I am going to use the crtc_debugfs function for printing.
crtc_debugfs  will use it
But now I'd better delete it

> 
>> +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
>> +	{ VS_SINGLE_DC,				"single dc mode" },
>> +	{ VS_MULTI_DC_PRIMARY,		"primary dc for multi dc mode" },
>> +	{ VS_MULTI_DC_SECONDARY,	"secondary dc for multi dc mode" },
>> +};
> 
> Custom driver properties are a no-go:
> https://docs.kernel.org/gpu/drm-kms.html#requirements
> 
> And
> 
> https://docs.kernel.org/gpu/drm-uapi.html#open-source-userspace-requirements
> 
>> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
>> +{
>> +	struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
>> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
>> +	struct dc_hw_display display;
> 
> Why are you rolling your own structure here, if it's exactly equivalent
> to what drm_display_mode and the crtc_state provide?
My original intention was to make the hardware part purer. and 
want to decouple hardware from drm struct.

so I define the own structure  between drm and hardware.
Maybe doing this will make both the hardware and drm happy

> 
>> +void vs_dc_commit(struct vs_dc *dc)
>> +{
>> +	dc_hw_enable_shadow_register(&dc->hw, false);
>> +
>> +	dc_hw_commit(&dc->hw);
>> +
>> +	if (dc->first_frame)
>> +		dc->first_frame = false;
>> +
>> +	dc_hw_enable_shadow_register(&dc->hw, true);
>> +}
> 
> It's not clear to me what you're trying to do here, does the hardware
> have latched registers that are only updated during vblank?
> 
>> +static int dc_bind(struct device *dev, struct device *master, void *data)
>> +{
>> +	struct drm_device *drm_dev = data;
>> +	struct vs_dc *dc = dev_get_drvdata(dev);
>> +	struct device_node *port;
>> +	struct vs_crtc *crtc;
>> +	struct vs_dc_info *dc_info;
>> +	struct vs_plane *plane;
>> +	struct vs_plane_info *plane_info;
>> +	int i, ret;
>> +	u32 ctrc_mask = 0;
>> +
>> +	if (!drm_dev || !dc) {
>> +		dev_err(dev, "devices are not created.\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	ret = dc_init(dev);
>> +	if (ret < 0) {
>> +		drm_err(drm_dev, "Failed to initialize DC hardware.\n");
>> +		return ret;
>> +	}
>> +
>> +	port = of_get_child_by_name(dev->of_node, "port");
>> +	if (!port) {
>> +		drm_err(drm_dev, "no port node found\n");
>> +		return -ENODEV;
>> +	}
>> +	of_node_put(port);
>> +
>> +	dc_info = dc->hw.info;
>> +
>> +	for (i = 0; i < dc_info->panel_num; i++) {
>> +		crtc = vs_crtc_create(drm_dev, dc_info);
>> +		if (!crtc) {
>> +			drm_err(drm_dev, "Failed to create CRTC.\n");
>> +			ret = -ENOMEM;
>> +			return ret;
>> +		}
>> +
>> +		crtc->base.port = port;
>> +		crtc->dev = dev;
>> +		dc->crtc[i] = crtc;
>> +		ctrc_mask |= drm_crtc_mask(&crtc->base);
>> +	}
>> +
>> +	for (i = 0; i < dc_info->plane_num; i++) {
>> +		plane_info = (struct vs_plane_info *)&dc_info->planes[i];
>> +
>> +		if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
>> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
>> +						drm_crtc_mask(&dc->crtc[0]->base));
>> +		} else if (!strcmp(plane_info->name, "Primary_1") ||
>> +				   !strcmp(plane_info->name, "Cursor_1")) {
> 
> Please use an enum and an id there.
> 
>> +static int vs_plane_atomic_set_property(struct drm_plane *plane,
>> +					struct drm_plane_state *state,
>> +					struct drm_property *property,
>> +					uint64_t val)
>> +{
>> +	struct drm_device *dev = plane->dev;
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
>> +	int ret = 0;
>> +
>> +	if (property == vs_plane->degamma_mode) {
>> +		if (vs_plane_state->degamma != val) {
>> +			vs_plane_state->degamma = val;
>> +			vs_plane_state->degamma_changed = true;
>> +		} else {
>> +			vs_plane_state->degamma_changed = false;
>> +		}
>> +	} else if (property == vs_plane->watermark_prop) {
>> +		ret = _vs_plane_set_property_blob_from_id(dev,
>> +							  &vs_plane_state->watermark,
>> +							  val,
>> +							  sizeof(struct drm_vs_watermark));
>> +		return ret;
>> +	} else if (property == vs_plane->color_mgmt_prop) {
>> +		ret = _vs_plane_set_property_blob_from_id(dev,
>> +							  &vs_plane_state->color_mgmt,
>> +							  val,
>> +							  sizeof(struct drm_vs_color_mgmt));
>> +		return ret;
>> +	} else if (property == vs_plane->roi_prop) {
>> +		ret = _vs_plane_set_property_blob_from_id(dev,
>> +							  &vs_plane_state->roi,
>> +							  val,
>> +							  sizeof(struct drm_vs_roi));
>> +		return ret;
>> +	} else {
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
> 
> Same story than above for properties
> 
> 
> Honestly, that driver is pretty massive, and you should be simplifying
> it a lot of you want the initial contribution to be as smooth as
> possible.
> 
> Things like all the tiling formats, the underflowing handling, all those
> properties, etc can (and should) be added in a second step once the
> foundations are in.
> 
> Maxime

ok , Thanks for reminding me. I will clarify my next goal and be more likely to simplify features.

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-11-15 14:52       ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-11-15 14:52 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: dri-devel, devicetree, linux-kernel, linux-riscv, linux-media,
	linaro-mm-sig, David Airlie, Daniel Vetter, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Emil Renner Berthing,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Maarten Lankhorst,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Jack Zhu, Shengyang Chen, Changhuang Liang



On 2023/10/25 21:57, Maxime Ripard wrote:
> On Wed, Oct 25, 2023 at 06:39:56PM +0800, Keith Zhao wrote:
>> +static struct drm_crtc_state *
>> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
>> +{
>> +	struct vs_crtc_state *ori_state;
>> +	struct vs_crtc_state *state;
>> +
>> +	if (!crtc->state)
>> +		return NULL;
>> +
>> +	ori_state = to_vs_crtc_state(crtc->state);
>> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> +	if (!state)
>> +		return NULL;
>> +
>> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
>> +
>> +	state->output_fmt = ori_state->output_fmt;
> 
> That field is never set in your patch.
> 
>> +	state->encoder_type = ori_state->encoder_type;
> 
> That isn't either, and it's not clear why you would need the
> encoder_type stored in the CRTC?
> 
>> +	state->bpp = ori_state->bpp;
> 
> You seem to derive that from output_fmt, it doesn't need to be in the
> CRTC state.
> 
>> +	state->underflow = ori_state->underflow;
> 
> Assuming you're setting this from the interrupt handler, it's unsafe,
> you shouldn't do that. What are you using it for?
I am going to use the crtc_debugfs function for printing.
crtc_debugfs  will use it
But now I'd better delete it

> 
>> +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
>> +	{ VS_SINGLE_DC,				"single dc mode" },
>> +	{ VS_MULTI_DC_PRIMARY,		"primary dc for multi dc mode" },
>> +	{ VS_MULTI_DC_SECONDARY,	"secondary dc for multi dc mode" },
>> +};
> 
> Custom driver properties are a no-go:
> https://docs.kernel.org/gpu/drm-kms.html#requirements
> 
> And
> 
> https://docs.kernel.org/gpu/drm-uapi.html#open-source-userspace-requirements
> 
>> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
>> +{
>> +	struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
>> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
>> +	struct dc_hw_display display;
> 
> Why are you rolling your own structure here, if it's exactly equivalent
> to what drm_display_mode and the crtc_state provide?
My original intention was to make the hardware part purer. and 
want to decouple hardware from drm struct.

so I define the own structure  between drm and hardware.
Maybe doing this will make both the hardware and drm happy

> 
>> +void vs_dc_commit(struct vs_dc *dc)
>> +{
>> +	dc_hw_enable_shadow_register(&dc->hw, false);
>> +
>> +	dc_hw_commit(&dc->hw);
>> +
>> +	if (dc->first_frame)
>> +		dc->first_frame = false;
>> +
>> +	dc_hw_enable_shadow_register(&dc->hw, true);
>> +}
> 
> It's not clear to me what you're trying to do here, does the hardware
> have latched registers that are only updated during vblank?
> 
>> +static int dc_bind(struct device *dev, struct device *master, void *data)
>> +{
>> +	struct drm_device *drm_dev = data;
>> +	struct vs_dc *dc = dev_get_drvdata(dev);
>> +	struct device_node *port;
>> +	struct vs_crtc *crtc;
>> +	struct vs_dc_info *dc_info;
>> +	struct vs_plane *plane;
>> +	struct vs_plane_info *plane_info;
>> +	int i, ret;
>> +	u32 ctrc_mask = 0;
>> +
>> +	if (!drm_dev || !dc) {
>> +		dev_err(dev, "devices are not created.\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	ret = dc_init(dev);
>> +	if (ret < 0) {
>> +		drm_err(drm_dev, "Failed to initialize DC hardware.\n");
>> +		return ret;
>> +	}
>> +
>> +	port = of_get_child_by_name(dev->of_node, "port");
>> +	if (!port) {
>> +		drm_err(drm_dev, "no port node found\n");
>> +		return -ENODEV;
>> +	}
>> +	of_node_put(port);
>> +
>> +	dc_info = dc->hw.info;
>> +
>> +	for (i = 0; i < dc_info->panel_num; i++) {
>> +		crtc = vs_crtc_create(drm_dev, dc_info);
>> +		if (!crtc) {
>> +			drm_err(drm_dev, "Failed to create CRTC.\n");
>> +			ret = -ENOMEM;
>> +			return ret;
>> +		}
>> +
>> +		crtc->base.port = port;
>> +		crtc->dev = dev;
>> +		dc->crtc[i] = crtc;
>> +		ctrc_mask |= drm_crtc_mask(&crtc->base);
>> +	}
>> +
>> +	for (i = 0; i < dc_info->plane_num; i++) {
>> +		plane_info = (struct vs_plane_info *)&dc_info->planes[i];
>> +
>> +		if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
>> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
>> +						drm_crtc_mask(&dc->crtc[0]->base));
>> +		} else if (!strcmp(plane_info->name, "Primary_1") ||
>> +				   !strcmp(plane_info->name, "Cursor_1")) {
> 
> Please use an enum and an id there.
> 
>> +static int vs_plane_atomic_set_property(struct drm_plane *plane,
>> +					struct drm_plane_state *state,
>> +					struct drm_property *property,
>> +					uint64_t val)
>> +{
>> +	struct drm_device *dev = plane->dev;
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
>> +	int ret = 0;
>> +
>> +	if (property == vs_plane->degamma_mode) {
>> +		if (vs_plane_state->degamma != val) {
>> +			vs_plane_state->degamma = val;
>> +			vs_plane_state->degamma_changed = true;
>> +		} else {
>> +			vs_plane_state->degamma_changed = false;
>> +		}
>> +	} else if (property == vs_plane->watermark_prop) {
>> +		ret = _vs_plane_set_property_blob_from_id(dev,
>> +							  &vs_plane_state->watermark,
>> +							  val,
>> +							  sizeof(struct drm_vs_watermark));
>> +		return ret;
>> +	} else if (property == vs_plane->color_mgmt_prop) {
>> +		ret = _vs_plane_set_property_blob_from_id(dev,
>> +							  &vs_plane_state->color_mgmt,
>> +							  val,
>> +							  sizeof(struct drm_vs_color_mgmt));
>> +		return ret;
>> +	} else if (property == vs_plane->roi_prop) {
>> +		ret = _vs_plane_set_property_blob_from_id(dev,
>> +							  &vs_plane_state->roi,
>> +							  val,
>> +							  sizeof(struct drm_vs_roi));
>> +		return ret;
>> +	} else {
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
> 
> Same story than above for properties
> 
> 
> Honestly, that driver is pretty massive, and you should be simplifying
> it a lot of you want the initial contribution to be as smooth as
> possible.
> 
> Things like all the tiling formats, the underflowing handling, all those
> properties, etc can (and should) be added in a second step once the
> foundations are in.
> 
> Maxime

ok , Thanks for reminding me. I will clarify my next goal and be more likely to simplify features.

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-11-15 14:52       ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-11-15 14:52 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Emil Renner Berthing, dri-devel, Krzysztof Kozlowski,
	linux-riscv, Sumit Semwal, Shengyang Chen, linux-media,
	devicetree, Conor Dooley, Albert Ou, Jagan Teki, linaro-mm-sig,
	Rob Herring, Chris Morgan, Paul Walmsley, Bjorn Andersson,
	linux-kernel, Changhuang Liang, Jack Zhu, Palmer Dabbelt,
	Thomas Zimmermann, Shawn Guo, christian.koenig



On 2023/10/25 21:57, Maxime Ripard wrote:
> On Wed, Oct 25, 2023 at 06:39:56PM +0800, Keith Zhao wrote:
>> +static struct drm_crtc_state *
>> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
>> +{
>> +	struct vs_crtc_state *ori_state;
>> +	struct vs_crtc_state *state;
>> +
>> +	if (!crtc->state)
>> +		return NULL;
>> +
>> +	ori_state = to_vs_crtc_state(crtc->state);
>> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> +	if (!state)
>> +		return NULL;
>> +
>> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
>> +
>> +	state->output_fmt = ori_state->output_fmt;
> 
> That field is never set in your patch.
> 
>> +	state->encoder_type = ori_state->encoder_type;
> 
> That isn't either, and it's not clear why you would need the
> encoder_type stored in the CRTC?
> 
>> +	state->bpp = ori_state->bpp;
> 
> You seem to derive that from output_fmt, it doesn't need to be in the
> CRTC state.
> 
>> +	state->underflow = ori_state->underflow;
> 
> Assuming you're setting this from the interrupt handler, it's unsafe,
> you shouldn't do that. What are you using it for?
I am going to use the crtc_debugfs function for printing.
crtc_debugfs  will use it
But now I'd better delete it

> 
>> +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
>> +	{ VS_SINGLE_DC,				"single dc mode" },
>> +	{ VS_MULTI_DC_PRIMARY,		"primary dc for multi dc mode" },
>> +	{ VS_MULTI_DC_SECONDARY,	"secondary dc for multi dc mode" },
>> +};
> 
> Custom driver properties are a no-go:
> https://docs.kernel.org/gpu/drm-kms.html#requirements
> 
> And
> 
> https://docs.kernel.org/gpu/drm-uapi.html#open-source-userspace-requirements
> 
>> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
>> +{
>> +	struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
>> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
>> +	struct dc_hw_display display;
> 
> Why are you rolling your own structure here, if it's exactly equivalent
> to what drm_display_mode and the crtc_state provide?
My original intention was to make the hardware part purer. and 
want to decouple hardware from drm struct.

so I define the own structure  between drm and hardware.
Maybe doing this will make both the hardware and drm happy

> 
>> +void vs_dc_commit(struct vs_dc *dc)
>> +{
>> +	dc_hw_enable_shadow_register(&dc->hw, false);
>> +
>> +	dc_hw_commit(&dc->hw);
>> +
>> +	if (dc->first_frame)
>> +		dc->first_frame = false;
>> +
>> +	dc_hw_enable_shadow_register(&dc->hw, true);
>> +}
> 
> It's not clear to me what you're trying to do here, does the hardware
> have latched registers that are only updated during vblank?
> 
>> +static int dc_bind(struct device *dev, struct device *master, void *data)
>> +{
>> +	struct drm_device *drm_dev = data;
>> +	struct vs_dc *dc = dev_get_drvdata(dev);
>> +	struct device_node *port;
>> +	struct vs_crtc *crtc;
>> +	struct vs_dc_info *dc_info;
>> +	struct vs_plane *plane;
>> +	struct vs_plane_info *plane_info;
>> +	int i, ret;
>> +	u32 ctrc_mask = 0;
>> +
>> +	if (!drm_dev || !dc) {
>> +		dev_err(dev, "devices are not created.\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	ret = dc_init(dev);
>> +	if (ret < 0) {
>> +		drm_err(drm_dev, "Failed to initialize DC hardware.\n");
>> +		return ret;
>> +	}
>> +
>> +	port = of_get_child_by_name(dev->of_node, "port");
>> +	if (!port) {
>> +		drm_err(drm_dev, "no port node found\n");
>> +		return -ENODEV;
>> +	}
>> +	of_node_put(port);
>> +
>> +	dc_info = dc->hw.info;
>> +
>> +	for (i = 0; i < dc_info->panel_num; i++) {
>> +		crtc = vs_crtc_create(drm_dev, dc_info);
>> +		if (!crtc) {
>> +			drm_err(drm_dev, "Failed to create CRTC.\n");
>> +			ret = -ENOMEM;
>> +			return ret;
>> +		}
>> +
>> +		crtc->base.port = port;
>> +		crtc->dev = dev;
>> +		dc->crtc[i] = crtc;
>> +		ctrc_mask |= drm_crtc_mask(&crtc->base);
>> +	}
>> +
>> +	for (i = 0; i < dc_info->plane_num; i++) {
>> +		plane_info = (struct vs_plane_info *)&dc_info->planes[i];
>> +
>> +		if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
>> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
>> +						drm_crtc_mask(&dc->crtc[0]->base));
>> +		} else if (!strcmp(plane_info->name, "Primary_1") ||
>> +				   !strcmp(plane_info->name, "Cursor_1")) {
> 
> Please use an enum and an id there.
> 
>> +static int vs_plane_atomic_set_property(struct drm_plane *plane,
>> +					struct drm_plane_state *state,
>> +					struct drm_property *property,
>> +					uint64_t val)
>> +{
>> +	struct drm_device *dev = plane->dev;
>> +	struct vs_plane *vs_plane = to_vs_plane(plane);
>> +	struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
>> +	int ret = 0;
>> +
>> +	if (property == vs_plane->degamma_mode) {
>> +		if (vs_plane_state->degamma != val) {
>> +			vs_plane_state->degamma = val;
>> +			vs_plane_state->degamma_changed = true;
>> +		} else {
>> +			vs_plane_state->degamma_changed = false;
>> +		}
>> +	} else if (property == vs_plane->watermark_prop) {
>> +		ret = _vs_plane_set_property_blob_from_id(dev,
>> +							  &vs_plane_state->watermark,
>> +							  val,
>> +							  sizeof(struct drm_vs_watermark));
>> +		return ret;
>> +	} else if (property == vs_plane->color_mgmt_prop) {
>> +		ret = _vs_plane_set_property_blob_from_id(dev,
>> +							  &vs_plane_state->color_mgmt,
>> +							  val,
>> +							  sizeof(struct drm_vs_color_mgmt));
>> +		return ret;
>> +	} else if (property == vs_plane->roi_prop) {
>> +		ret = _vs_plane_set_property_blob_from_id(dev,
>> +							  &vs_plane_state->roi,
>> +							  val,
>> +							  sizeof(struct drm_vs_roi));
>> +		return ret;
>> +	} else {
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
> 
> Same story than above for properties
> 
> 
> Honestly, that driver is pretty massive, and you should be simplifying
> it a lot of you want the initial contribution to be as smooth as
> possible.
> 
> Things like all the tiling formats, the underflowing handling, all those
> properties, etc can (and should) be added in a second step once the
> foundations are in.
> 
> Maxime

ok , Thanks for reminding me. I will clarify my next goal and be more likely to simplify features.

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
  2023-11-15 13:30           ` Keith Zhao
  (?)
@ 2023-11-15 16:00             ` Dmitry Baryshkov
  -1 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-11-15 16:00 UTC (permalink / raw)
  To: Keith Zhao
  Cc: Emil Renner Berthing, dri-devel, Krzysztof Kozlowski,
	linux-riscv, Sumit Semwal, Shengyang Chen, linux-media,
	devicetree, Conor Dooley, Albert Ou, Maxime Ripard, Jagan Teki,
	linaro-mm-sig, Rob Herring, Chris Morgan, Paul Walmsley,
	Bjorn Andersson, linux-kernel, christian.koenig, Jack Zhu,
	Palmer Dabbelt, Thomas Zimmermann, Shawn Guo, Changhuang Liang

On Wed, 15 Nov 2023 at 15:30, Keith Zhao <keith.zhao@starfivetech.com> wrote:
>
>
>
> On 2023/11/14 18:59, Dmitry Baryshkov wrote:
> > On Tue, 14 Nov 2023 at 12:42, Keith Zhao <keith.zhao@starfivetech.com> wrote:
> >>
> >>
> >>
> >> On 2023/10/26 3:28, Dmitry Baryshkov wrote:
> >> > On 25/10/2023 13:39, Keith Zhao wrote:
> >> >> add 2 crtcs and 8 planes in vs-drm
> >> >>
> >> >> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> >> >> ---
> >> >>   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
> >> >>   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
> >> >>   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
> >> >>   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
> >> >>   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
> >> >>   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
> >> >>   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
> >> >>   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
> >> >>   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
> >> >>   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
> >> >>   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
> >> >>   11 files changed, 4494 insertions(+), 2 deletions(-)
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
> >> >>
> >> >> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> >> >> index 7d3be305b..1d48016ca 100644
> >> >> --- a/drivers/gpu/drm/verisilicon/Makefile
> >> >> +++ b/drivers/gpu/drm/verisilicon/Makefile
> >> >> @@ -1,7 +1,11 @@
> >> >>   # SPDX-License-Identifier: GPL-2.0
> >> >>
> >> >> -vs_drm-objs := vs_drv.o \
> >> >> -            vs_modeset.o
> >> >> +vs_drm-objs := vs_dc_hw.o \
> >> >> +            vs_dc.o \
> >> >> +            vs_crtc.o \
> >> >> +            vs_drv.o \
> >> >> +            vs_modeset.o \
> >> >> +            vs_plane.o
> >> >>
> >> >>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
> >> >>
> >> >> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
> >> >> new file mode 100644
> >> >> index 000000000..8a658ea77
> >> >> --- /dev/null
> >> >> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
> >> >> @@ -0,0 +1,257 @@
> >> >> +// SPDX-License-Identifier: GPL-2.0
> >> >> +/*
> >> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> >> + *
> >> >> + */
> >> >> +
> >> >> +#include <linux/clk.h>
> >> >> +#include <linux/debugfs.h>
> >> >> +#include <linux/media-bus-format.h>
> >> >> +
> >> >> +#include <drm/drm_atomic_helper.h>
> >> >> +#include <drm/drm_atomic.h>
> >> >> +#include <drm/drm_crtc.h>
> >> >> +#include <drm/drm_gem_atomic_helper.h>
> >> >> +#include <drm/drm_vblank.h>
> >> >> +#include <drm/vs_drm.h>
> >> >> +
> >> >> +#include "vs_crtc.h"
> >> >> +#include "vs_dc.h"
> >> >> +#include "vs_drv.h"
> >> >> +
> >> >> +static void vs_crtc_reset(struct drm_crtc *crtc)
> >> >> +{
> >> >> +    struct vs_crtc_state *state;
> >> >> +
> >> >> +    if (crtc->state) {
> >> >> +            __drm_atomic_helper_crtc_destroy_state(crtc->state);
> >> >> +
> >> >> +            state = to_vs_crtc_state(crtc->state);
> >> >> +            kfree(state);
> >> >> +            crtc->state = NULL;
> >> >> +    }
> >> >
> >> > You can call your crtc_destroy_state function directly here.
> >> >
> >> >> +
> >> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
> >> >> +    if (!state)
> >> >> +            return;
> >> >> +
> >> >> +    __drm_atomic_helper_crtc_reset(crtc, &state->base);
> >> >> +}
> >> >> +
> >> >> +static struct drm_crtc_state *
> >> >> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
> >> >> +{
> >> >> +    struct vs_crtc_state *ori_state;
> >> >
> >> > It might be a matter of taste, but it is usually old_state.
> >> >
> >> >> +    struct vs_crtc_state *state;
> >> >> +
> >> >> +    if (!crtc->state)
> >> >> +            return NULL;
> >> >> +
> >> >> +    ori_state = to_vs_crtc_state(crtc->state);
> >> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
> >> >> +    if (!state)
> >> >> +            return NULL;
> >> >> +
> >> >> +    __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
> >> >> +
> >> >> +    state->output_fmt = ori_state->output_fmt;
> >> >> +    state->encoder_type = ori_state->encoder_type;
> >> >> +    state->bpp = ori_state->bpp;
> >> >> +    state->underflow = ori_state->underflow;
> >> >
> >> > Can you use kmemdup instead?
> >> >
> >> >> +
> >> >> +    return &state->base;
> >> >> +}
> >> >> +
> >> >> +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
> >> >> +                                     struct drm_crtc_state *state)
> >> >> +{
> >> >> +    __drm_atomic_helper_crtc_destroy_state(state);
> >> >> +    kfree(to_vs_crtc_state(state));
> >> >> +}
> >> >> +
> >> >> +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
> >> >> +{
> >> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> >> +
> >> >> +    vs_dc_enable_vblank(dc, true);
> >> >> +
> >> >> +    return 0;
> >> >> +}
> >> >> +
> >> >> +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
> >> >> +{
> >> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> >> +
> >> >> +    vs_dc_enable_vblank(dc, false);
> >> >> +}
> >> >> +
> >> >> +static const struct drm_crtc_funcs vs_crtc_funcs = {
> >> >> +    .set_config             = drm_atomic_helper_set_config,
> >> >> +    .page_flip              = drm_atomic_helper_page_flip,
> >> >
> >> > destroy is required, see drm_mode_config_cleanup()
> >>
> >> hi Dmitry:
> >> if define destroy in drm_crtc_funcs,
> >> it will make __drmm_crtc_init_with_planes unhappy
> >
> > Ack, I missed that you have been using drmm_crtc_init. BTW, I checked
> > your code, you should be able to switch drm
> > drmm_crtc_alloc_with_planes().
> >
> yes
> I done the replace and it can work well.
>
> >>
> >> see:
> >> __printf(6, 0)
> >> static int __drmm_crtc_init_with_planes(struct drm_device *dev,
> >>                                         struct drm_crtc *crtc,
> >>                                         struct drm_plane *primary,
> >>                                         struct drm_plane *cursor,
> >>                                         const struct drm_crtc_funcs *funcs,
> >>                                         const char *name,
> >>                                         va_list args)
> >> {
> >>         int ret;
> >>
> >>         drm_WARN_ON(dev, funcs && funcs->destroy);
> >>
> >> ........
> >> }
> >>
> >> It should not need to be defined here, I think
> >>
> >> >
> >> >> +    .reset                  = vs_crtc_reset,
> >> >> +    .atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
> >> >> +    .atomic_destroy_state   = vs_crtc_atomic_destroy_state,
> >> >
> >> > please consider adding atomic_print_state to output driver-specific bits.
> >> >
> >> >> +    .enable_vblank          = vs_crtc_enable_vblank,
> >> >> +    .disable_vblank         = vs_crtc_disable_vblank,
> >> >> +};
> >> >> +
> >> >> +static u8 cal_pixel_bits(u32 bus_format)
> >> >
> >> > This looks like a generic helper code, which can go to a common place.
> I don't know if I understand correctly
> Here I remove static
> Add 2 lines in vs_drv.h
>
> /* vs_crtc.c */
> u8 cal_pixel_bits(u32 bus_format);
>
> to make it common

I mean, move it to a generic location, like include/media/

>
> >> >
> >> >> +{
> >> >> +    u8 bpp;
> >> >> +
> >> >> +    switch (bus_format) {
> >> >> +    case MEDIA_BUS_FMT_RGB565_1X16:
> >> >> +    case MEDIA_BUS_FMT_UYVY8_1X16:
> >> >> +            bpp = 16;
> >> >> +            break;
> >> >> +    case MEDIA_BUS_FMT_RGB666_1X18:
> >> >> +    case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> >> >> +            bpp = 18;
> >> >> +            break;
> >> >> +    case MEDIA_BUS_FMT_UYVY10_1X20:
> >> >> +            bpp = 20;
> >> >> +            break;
> >> >> +    case MEDIA_BUS_FMT_BGR888_1X24:
> >> >> +    case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> >> >> +    case MEDIA_BUS_FMT_YUV8_1X24:
> >> >> +            bpp = 24;
> >> >> +            break;
> >> >> +    case MEDIA_BUS_FMT_RGB101010_1X30:
> >> >> +    case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> >> >> +    case MEDIA_BUS_FMT_YUV10_1X30:
> >> >> +            bpp = 30;
> >> >> +            break;
> >> >> +    default:
> >> >> +            bpp = 24;
> >> >> +            break;
> >> >> +    }
> >> >> +
> >> >> +    return bpp;
> >> >> +}
> >> >> +

[skipped]

> >> >> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
> >> >> +                           struct vs_dc_info *info)
> >> >> +{
> >> >> +    struct vs_crtc *crtc;
> >> >> +    int ret;
> >> >> +
> >> >> +    if (!info)
> >> >> +            return NULL;
> >> >> +
> >> >> +    crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
> >> >> +    if (!crtc)
> >> >> +            return NULL;
> >> >> +
> >> >> +    ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
> >> >> +                                     NULL, NULL, &vs_crtc_funcs,
> >> >> +                                     info->name ? info->name : NULL);
> >> >
> >> > It might be better to add drmm_crtc_init() helper.
>         drmm_crtc_alloc_with_planes used:
>         ...
>         struct vs_crtc *crtc;
>         int ret;
>
>         if (!info)
>                 return NULL;
>
>         crtc = drmm_crtc_alloc_with_planes(drm_dev, struct vs_crtc, base,
>                                                NULL, NULL,
>                                                &vs_crtc_funcs, info->name ? info->name : NULL);
>         ...

Ack.

>
> >> >
> >> >> +    if (ret)
> >> >> +            return NULL;
> >> >> +
> >> >> +    drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
> >> >> +
> >> >> +    if (info->gamma_size) {
> >> >> +            ret = drm_mode_crtc_set_gamma_size(&crtc->base,
> >> >> +                                               info->gamma_size);
> >> >> +            if (ret)
> >> >> +                    return NULL;
> >> >> +
> >> >> +            drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
> >> >> +                                       info->gamma_size);
> >> >> +    }
> >> >> +
> >> >> +    crtc->max_bpc = info->max_bpc;
> >> >> +    crtc->color_formats = info->color_formats;
> >> >> +    return crtc;
> >> >> +}

> >> >> +const struct component_ops dc_component_ops = {
> >> >> +    .bind = dc_bind,
> >> >> +    .unbind = dc_unbind,
> >> >> +};
> >> >> +
> >> >> +static const struct of_device_id dc_driver_dt_match[] = {
> >> >> +    { .compatible = "starfive,jh7110-dc8200", },
> >> >> +    {},
> >> >> +};
> >> >> +MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
> >> >> +
> >> >> +static int dc_probe(struct platform_device *pdev)
> >> >> +{
> >> >> +    struct device *dev = &pdev->dev;
> >> >> +    struct vs_dc *dc;
> >> >> +    int irq, ret, i;
> >> >> +
> >> >> +    dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
> >> >> +    if (!dc)
> >> >> +            return -ENOMEM;
> >> >> +
> >> >> +    dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
> >> >> +    if (IS_ERR(dc->hw.hi_base))
> >> >> +            return PTR_ERR(dc->hw.hi_base);
> >> >> +
> >> >> +    dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
> >> >> +    if (IS_ERR(dc->hw.reg_base))
> >> >> +            return PTR_ERR(dc->hw.reg_base);
> >> >> +
> >> >> +    dc->nclks = ARRAY_SIZE(dc->clk_vout);
> >> >> +    for (i = 0; i < dc->nclks; ++i)
> >> >> +            dc->clk_vout[i].id = vout_clocks[i];
> >> >> +    ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
> >> >> +    if (ret) {
> >> >> +            dev_err(dev, "Failed to get clk controls\n");
> >> >> +            return ret;
> >> >> +    }
> >> >> +
> >> >> +    dc->nrsts = ARRAY_SIZE(dc->rst_vout);
> >> >> +    for (i = 0; i < dc->nrsts; ++i)
> >> >> +            dc->rst_vout[i].id = vout_resets[i];
> >> >> +    ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
> >> >> +                                             dc->rst_vout);
> >> >> +    if (ret) {
> >> >> +            dev_err(dev, "Failed to get reset controls\n");
> >> >> +            return ret;
> >> >> +    }
> >> >> +
> >> >> +    irq = platform_get_irq(pdev, 0);
> >> >> +
> >> >> +    ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
> >> >
> >> > Are you ready to handle the IRQ at this point?
> Do you have some good suggestions?
> Are there any hidden dangers in doing so?


If your driver is not ready, stray interrupt can crash your driver.
For pm_runtime-enabled devices it is even more important: the
interrupt handled might try accessing device which is powered off.

> Hope to get good opinions

The usual suggestion is: request the interrupt when your data is fully
set up. Or request_irq with IRQF_NO_AUTOEN and then enable_irq() /
disable_irq() as required.

>
> >> >
> >> >> +    if (ret < 0) {
> >> >> +            dev_err(dev, "Failed to install irq:%u.\n", irq);
> >> >> +            return ret;
> >> >> +    }
> >> >> +
> >> >> +    dev_set_drvdata(dev, dc);
> >> >> +
> >> >> +    return component_add(dev, &dc_component_ops);
> >> >> +}
> >> >> +

[skipped]

> >> >> +static int vs_plane_atomic_get_property(struct drm_plane *plane,
> >> >> +                                    const struct drm_plane_state *state,
> >> >> +                                    struct drm_property *property,
> >> >> +                                    uint64_t *val)
> >> >> +{
> >> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> >> +    const struct vs_plane_state *vs_plane_state =
> >> >> +            container_of(state, const struct vs_plane_state, base);
> >> >> +
> >> >> +    if (property == vs_plane->degamma_mode)
> >> >> +            *val = vs_plane_state->degamma;
> >> >> +    else if (property == vs_plane->watermark_prop)
> >> >> +            *val = (vs_plane_state->watermark) ?
> >> >> +                                    vs_plane_state->watermark->base.id : 0;
> >> >> +    else if (property == vs_plane->color_mgmt_prop)
> >> >> +            *val = (vs_plane_state->color_mgmt) ?
> >> >> +                                    vs_plane_state->color_mgmt->base.id : 0;
> >> >
> >> > degamma and color management should use standard properties.
>
> hello Dmitry:
> There is a question that troubles me
>
> drm_plane include such standard properties.
> {
>         struct drm_property *alpha_property;
>         struct drm_property *zpos_property;
>         struct drm_property *rotation_property;
>         struct drm_property *blend_mode_property;
>         struct drm_property *color_encoding_property;
>         struct drm_property *color_range_property;
>         struct drm_property *scaling_filter_property;
> }
>
> it doesn't include degamma and color management properties

Which is because they are not standardised yet.

>
> >> >
> >> >> +    else if (property == vs_plane->roi_prop)
> >> >> +            *val = (vs_plane_state->roi) ?
> >> >> +                                    vs_plane_state->roi->base.id : 0;
> >> >> +    else
> >> >> +            return -EINVAL;
> >> >> +
> >> >> +    return 0;
> >> >> +}
> >> >> +
> >> >> +static bool vs_format_mod_supported(struct drm_plane *plane,
> >> >> +                                u32 format,
> >> >> +                                u64 modifier)
> >> >> +{
> >> >> +    int i;
> >> >> +
> >> >> +    /* We always have to allow these modifiers:
> >> >> +     * 1. Core DRM checks for LINEAR support if userspace does not provide modifiers.
> >> >> +     * 2. Not passing any modifiers is the same as explicitly passing INVALID.
> >> >> +     */
> >> >> +    if (modifier == DRM_FORMAT_MOD_LINEAR)
> >> >> +            return true;
> >> >> +
> >> >> +    /* Check that the modifier is on the list of the plane's supported modifiers. */
> >> >> +    for (i = 0; i < plane->modifier_count; i++) {
> >> >> +            if (modifier == plane->modifiers[i])
> >> >> +                    break;
> >> >> +    }
> >> >> +
> >> >> +    if (i == plane->modifier_count)
> >> >> +            return false;
> >> >> +
> >> >> +    return true;
> >> >> +}
> >> >> +
> >> >> +const struct drm_plane_funcs vs_plane_funcs = {
> >> >> +    .update_plane           = drm_atomic_helper_update_plane,
> >> >> +    .disable_plane          = drm_atomic_helper_disable_plane,
> >> >> +    .reset                  = vs_plane_reset,
> >> >> +    .atomic_duplicate_state = vs_plane_atomic_duplicate_state,
> >> >> +    .atomic_destroy_state   = vs_plane_atomic_destroy_state,
> >> >> +    .atomic_set_property    = vs_plane_atomic_set_property,
> >> >> +    .atomic_get_property    = vs_plane_atomic_get_property,
> >> >> +    .format_mod_supported   = vs_format_mod_supported,
> >> >> +};
> >> >> +
> >> >> +static unsigned char vs_get_plane_number(struct drm_framebuffer *fb)
> >> >> +{
> >> >> +    const struct drm_format_info *info;
> >> >> +
> >> >> +    if (!fb)
> >> >> +            return 0;
> >> >> +
> >> >> +    info = drm_format_info(fb->format->format);
> >> >> +    if (!info || info->num_planes > DRM_FORMAT_MAX_PLANES)
> >> >> +            return 0;
> >> >> +
> >> >> +    return info->num_planes;
> >> >> +}
> >> >> +
> >> >> +static int vs_plane_atomic_check(struct drm_plane *plane,
> >> >> +                             struct drm_atomic_state *state)
> >> >> +{
> >> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> >> >> +                                                                             plane);
> >> >> +    unsigned char i, num_planes;
> >> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
> >> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
> >> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
> >> >> +
> >> >> +    if (!crtc || !fb)
> >> >> +            return 0;
> >> >> +
> >> >> +    num_planes = vs_get_plane_number(fb);
> >> >> +
> >> >> +    for (i = 0; i < num_planes; i++) {
> >> >> +            dma_addr_t dma_addr;
> >> >> +
> >> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
> >> >> +            plane_state->dma_addr[i] = dma_addr;
> >> >> +    }
> >> >> +
> >> >> +    return vs_dc_check_plane(dc, plane, state);
> >> >> +}
> >> >> +
> >> >> +static int vs_cursor_plane_atomic_check(struct drm_plane *plane,
> >> >> +                                    struct drm_atomic_state *state)
> >> >> +{
> >> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> >> >> +                                                                      plane);
> >> >> +    unsigned char i, num_planes;
> >> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
> >> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
> >> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
> >> >> +
> >> >> +    if (!crtc || !fb)
> >> >> +            return 0;
> >> >> +
> >> >> +    num_planes = vs_get_plane_number(fb);
> >> >> +
> >> >> +    for (i = 0; i < num_planes; i++) {
> >> >> +            dma_addr_t dma_addr;
> >> >> +
> >> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
> >> >> +            plane_state->dma_addr[i] = dma_addr;
> >> >> +    }
> >> >> +
> >> >> +    return vs_dc_check_cursor_plane(dc, plane, state);
> >> >> +}
> >> >> +
> >> >> +static void vs_plane_atomic_update(struct drm_plane *plane,
> >> >> +                               struct drm_atomic_state *state)
> >> >> +{
> >> >> +    struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
> >> >> +                                                                      plane);
> >> >
> >> > New line after the equal sign will be better.
> >> >
> >> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> >> >> +                                                                      plane);
> >> >> +
> >> >> +    unsigned char i, num_planes;
> >> >> +    struct drm_framebuffer *fb;
> >> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
> >> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
> >> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> >> +
> >> >> +    if (!new_state->fb || !new_state->crtc)
> >> >> +            return;
> >> >
> >> > if (!new_state->visible) ?
>
> no matter what value it is ? the driver will handle it
> in func
>
> static void update_fb(struct vs_plane *plane, u8 display_id,
>                       struct dc_hw_fb *fb, struct drm_plane_state *state)
> {
>         struct vs_plane_state *plane_state = to_vs_plane_state(state);
>         struct drm_framebuffer *drm_fb = state->fb;
>         struct drm_rect *src = &state->src;
>
> .......
>         fb->enable = state->visible;
> .......
> }
>
> so no need add "if (!new_state->visible)" i think

I mean instead of fb&&crtc check. Otherwise you are duplicating checks
in drm_atomic_helper_check_plane_state(). And with the pixel_source
being on their way this condition becomes legacy.

>
> >> >
> >> >> +
> >> >> +    fb = new_state->fb;
> >> >> +
> >> >> +    drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
> >> >> +
> >> >> +    num_planes = vs_get_plane_number(fb);
> >> >> +
> >> >> +    for (i = 0; i < num_planes; i++) {
> >> >> +            dma_addr_t dma_addr;
> >> >> +
> >> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
> >> >> +            plane_state->dma_addr[i] = dma_addr;
> >> >> +    }
> >> >> +
> >> >> +    plane_state->status.src = drm_plane_state_src(new_state);
> >> >> +    plane_state->status.dest = drm_plane_state_dest(new_state);
> >> >> +
> >> >> +    vs_dc_update_plane(dc, vs_plane, plane, state);
> >> >> +}
> >> >> +


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-11-15 16:00             ` Dmitry Baryshkov
  0 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-11-15 16:00 UTC (permalink / raw)
  To: Keith Zhao
  Cc: Emil Renner Berthing, dri-devel, Krzysztof Kozlowski,
	linux-riscv, Sumit Semwal, Shengyang Chen, linux-media,
	devicetree, Conor Dooley, Albert Ou, Maxime Ripard, Jagan Teki,
	linaro-mm-sig, Rob Herring, Chris Morgan, Paul Walmsley,
	Bjorn Andersson, linux-kernel, christian.koenig, Jack Zhu,
	Palmer Dabbelt, Thomas Zimmermann, Shawn Guo, Changhuang Liang

On Wed, 15 Nov 2023 at 15:30, Keith Zhao <keith.zhao@starfivetech.com> wrote:
>
>
>
> On 2023/11/14 18:59, Dmitry Baryshkov wrote:
> > On Tue, 14 Nov 2023 at 12:42, Keith Zhao <keith.zhao@starfivetech.com> wrote:
> >>
> >>
> >>
> >> On 2023/10/26 3:28, Dmitry Baryshkov wrote:
> >> > On 25/10/2023 13:39, Keith Zhao wrote:
> >> >> add 2 crtcs and 8 planes in vs-drm
> >> >>
> >> >> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> >> >> ---
> >> >>   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
> >> >>   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
> >> >>   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
> >> >>   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
> >> >>   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
> >> >>   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
> >> >>   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
> >> >>   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
> >> >>   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
> >> >>   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
> >> >>   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
> >> >>   11 files changed, 4494 insertions(+), 2 deletions(-)
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
> >> >>
> >> >> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> >> >> index 7d3be305b..1d48016ca 100644
> >> >> --- a/drivers/gpu/drm/verisilicon/Makefile
> >> >> +++ b/drivers/gpu/drm/verisilicon/Makefile
> >> >> @@ -1,7 +1,11 @@
> >> >>   # SPDX-License-Identifier: GPL-2.0
> >> >>
> >> >> -vs_drm-objs := vs_drv.o \
> >> >> -            vs_modeset.o
> >> >> +vs_drm-objs := vs_dc_hw.o \
> >> >> +            vs_dc.o \
> >> >> +            vs_crtc.o \
> >> >> +            vs_drv.o \
> >> >> +            vs_modeset.o \
> >> >> +            vs_plane.o
> >> >>
> >> >>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
> >> >>
> >> >> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
> >> >> new file mode 100644
> >> >> index 000000000..8a658ea77
> >> >> --- /dev/null
> >> >> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
> >> >> @@ -0,0 +1,257 @@
> >> >> +// SPDX-License-Identifier: GPL-2.0
> >> >> +/*
> >> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> >> + *
> >> >> + */
> >> >> +
> >> >> +#include <linux/clk.h>
> >> >> +#include <linux/debugfs.h>
> >> >> +#include <linux/media-bus-format.h>
> >> >> +
> >> >> +#include <drm/drm_atomic_helper.h>
> >> >> +#include <drm/drm_atomic.h>
> >> >> +#include <drm/drm_crtc.h>
> >> >> +#include <drm/drm_gem_atomic_helper.h>
> >> >> +#include <drm/drm_vblank.h>
> >> >> +#include <drm/vs_drm.h>
> >> >> +
> >> >> +#include "vs_crtc.h"
> >> >> +#include "vs_dc.h"
> >> >> +#include "vs_drv.h"
> >> >> +
> >> >> +static void vs_crtc_reset(struct drm_crtc *crtc)
> >> >> +{
> >> >> +    struct vs_crtc_state *state;
> >> >> +
> >> >> +    if (crtc->state) {
> >> >> +            __drm_atomic_helper_crtc_destroy_state(crtc->state);
> >> >> +
> >> >> +            state = to_vs_crtc_state(crtc->state);
> >> >> +            kfree(state);
> >> >> +            crtc->state = NULL;
> >> >> +    }
> >> >
> >> > You can call your crtc_destroy_state function directly here.
> >> >
> >> >> +
> >> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
> >> >> +    if (!state)
> >> >> +            return;
> >> >> +
> >> >> +    __drm_atomic_helper_crtc_reset(crtc, &state->base);
> >> >> +}
> >> >> +
> >> >> +static struct drm_crtc_state *
> >> >> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
> >> >> +{
> >> >> +    struct vs_crtc_state *ori_state;
> >> >
> >> > It might be a matter of taste, but it is usually old_state.
> >> >
> >> >> +    struct vs_crtc_state *state;
> >> >> +
> >> >> +    if (!crtc->state)
> >> >> +            return NULL;
> >> >> +
> >> >> +    ori_state = to_vs_crtc_state(crtc->state);
> >> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
> >> >> +    if (!state)
> >> >> +            return NULL;
> >> >> +
> >> >> +    __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
> >> >> +
> >> >> +    state->output_fmt = ori_state->output_fmt;
> >> >> +    state->encoder_type = ori_state->encoder_type;
> >> >> +    state->bpp = ori_state->bpp;
> >> >> +    state->underflow = ori_state->underflow;
> >> >
> >> > Can you use kmemdup instead?
> >> >
> >> >> +
> >> >> +    return &state->base;
> >> >> +}
> >> >> +
> >> >> +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
> >> >> +                                     struct drm_crtc_state *state)
> >> >> +{
> >> >> +    __drm_atomic_helper_crtc_destroy_state(state);
> >> >> +    kfree(to_vs_crtc_state(state));
> >> >> +}
> >> >> +
> >> >> +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
> >> >> +{
> >> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> >> +
> >> >> +    vs_dc_enable_vblank(dc, true);
> >> >> +
> >> >> +    return 0;
> >> >> +}
> >> >> +
> >> >> +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
> >> >> +{
> >> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> >> +
> >> >> +    vs_dc_enable_vblank(dc, false);
> >> >> +}
> >> >> +
> >> >> +static const struct drm_crtc_funcs vs_crtc_funcs = {
> >> >> +    .set_config             = drm_atomic_helper_set_config,
> >> >> +    .page_flip              = drm_atomic_helper_page_flip,
> >> >
> >> > destroy is required, see drm_mode_config_cleanup()
> >>
> >> hi Dmitry:
> >> if define destroy in drm_crtc_funcs,
> >> it will make __drmm_crtc_init_with_planes unhappy
> >
> > Ack, I missed that you have been using drmm_crtc_init. BTW, I checked
> > your code, you should be able to switch drm
> > drmm_crtc_alloc_with_planes().
> >
> yes
> I done the replace and it can work well.
>
> >>
> >> see:
> >> __printf(6, 0)
> >> static int __drmm_crtc_init_with_planes(struct drm_device *dev,
> >>                                         struct drm_crtc *crtc,
> >>                                         struct drm_plane *primary,
> >>                                         struct drm_plane *cursor,
> >>                                         const struct drm_crtc_funcs *funcs,
> >>                                         const char *name,
> >>                                         va_list args)
> >> {
> >>         int ret;
> >>
> >>         drm_WARN_ON(dev, funcs && funcs->destroy);
> >>
> >> ........
> >> }
> >>
> >> It should not need to be defined here, I think
> >>
> >> >
> >> >> +    .reset                  = vs_crtc_reset,
> >> >> +    .atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
> >> >> +    .atomic_destroy_state   = vs_crtc_atomic_destroy_state,
> >> >
> >> > please consider adding atomic_print_state to output driver-specific bits.
> >> >
> >> >> +    .enable_vblank          = vs_crtc_enable_vblank,
> >> >> +    .disable_vblank         = vs_crtc_disable_vblank,
> >> >> +};
> >> >> +
> >> >> +static u8 cal_pixel_bits(u32 bus_format)
> >> >
> >> > This looks like a generic helper code, which can go to a common place.
> I don't know if I understand correctly
> Here I remove static
> Add 2 lines in vs_drv.h
>
> /* vs_crtc.c */
> u8 cal_pixel_bits(u32 bus_format);
>
> to make it common

I mean, move it to a generic location, like include/media/

>
> >> >
> >> >> +{
> >> >> +    u8 bpp;
> >> >> +
> >> >> +    switch (bus_format) {
> >> >> +    case MEDIA_BUS_FMT_RGB565_1X16:
> >> >> +    case MEDIA_BUS_FMT_UYVY8_1X16:
> >> >> +            bpp = 16;
> >> >> +            break;
> >> >> +    case MEDIA_BUS_FMT_RGB666_1X18:
> >> >> +    case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> >> >> +            bpp = 18;
> >> >> +            break;
> >> >> +    case MEDIA_BUS_FMT_UYVY10_1X20:
> >> >> +            bpp = 20;
> >> >> +            break;
> >> >> +    case MEDIA_BUS_FMT_BGR888_1X24:
> >> >> +    case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> >> >> +    case MEDIA_BUS_FMT_YUV8_1X24:
> >> >> +            bpp = 24;
> >> >> +            break;
> >> >> +    case MEDIA_BUS_FMT_RGB101010_1X30:
> >> >> +    case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> >> >> +    case MEDIA_BUS_FMT_YUV10_1X30:
> >> >> +            bpp = 30;
> >> >> +            break;
> >> >> +    default:
> >> >> +            bpp = 24;
> >> >> +            break;
> >> >> +    }
> >> >> +
> >> >> +    return bpp;
> >> >> +}
> >> >> +

[skipped]

> >> >> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
> >> >> +                           struct vs_dc_info *info)
> >> >> +{
> >> >> +    struct vs_crtc *crtc;
> >> >> +    int ret;
> >> >> +
> >> >> +    if (!info)
> >> >> +            return NULL;
> >> >> +
> >> >> +    crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
> >> >> +    if (!crtc)
> >> >> +            return NULL;
> >> >> +
> >> >> +    ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
> >> >> +                                     NULL, NULL, &vs_crtc_funcs,
> >> >> +                                     info->name ? info->name : NULL);
> >> >
> >> > It might be better to add drmm_crtc_init() helper.
>         drmm_crtc_alloc_with_planes used:
>         ...
>         struct vs_crtc *crtc;
>         int ret;
>
>         if (!info)
>                 return NULL;
>
>         crtc = drmm_crtc_alloc_with_planes(drm_dev, struct vs_crtc, base,
>                                                NULL, NULL,
>                                                &vs_crtc_funcs, info->name ? info->name : NULL);
>         ...

Ack.

>
> >> >
> >> >> +    if (ret)
> >> >> +            return NULL;
> >> >> +
> >> >> +    drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
> >> >> +
> >> >> +    if (info->gamma_size) {
> >> >> +            ret = drm_mode_crtc_set_gamma_size(&crtc->base,
> >> >> +                                               info->gamma_size);
> >> >> +            if (ret)
> >> >> +                    return NULL;
> >> >> +
> >> >> +            drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
> >> >> +                                       info->gamma_size);
> >> >> +    }
> >> >> +
> >> >> +    crtc->max_bpc = info->max_bpc;
> >> >> +    crtc->color_formats = info->color_formats;
> >> >> +    return crtc;
> >> >> +}

> >> >> +const struct component_ops dc_component_ops = {
> >> >> +    .bind = dc_bind,
> >> >> +    .unbind = dc_unbind,
> >> >> +};
> >> >> +
> >> >> +static const struct of_device_id dc_driver_dt_match[] = {
> >> >> +    { .compatible = "starfive,jh7110-dc8200", },
> >> >> +    {},
> >> >> +};
> >> >> +MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
> >> >> +
> >> >> +static int dc_probe(struct platform_device *pdev)
> >> >> +{
> >> >> +    struct device *dev = &pdev->dev;
> >> >> +    struct vs_dc *dc;
> >> >> +    int irq, ret, i;
> >> >> +
> >> >> +    dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
> >> >> +    if (!dc)
> >> >> +            return -ENOMEM;
> >> >> +
> >> >> +    dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
> >> >> +    if (IS_ERR(dc->hw.hi_base))
> >> >> +            return PTR_ERR(dc->hw.hi_base);
> >> >> +
> >> >> +    dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
> >> >> +    if (IS_ERR(dc->hw.reg_base))
> >> >> +            return PTR_ERR(dc->hw.reg_base);
> >> >> +
> >> >> +    dc->nclks = ARRAY_SIZE(dc->clk_vout);
> >> >> +    for (i = 0; i < dc->nclks; ++i)
> >> >> +            dc->clk_vout[i].id = vout_clocks[i];
> >> >> +    ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
> >> >> +    if (ret) {
> >> >> +            dev_err(dev, "Failed to get clk controls\n");
> >> >> +            return ret;
> >> >> +    }
> >> >> +
> >> >> +    dc->nrsts = ARRAY_SIZE(dc->rst_vout);
> >> >> +    for (i = 0; i < dc->nrsts; ++i)
> >> >> +            dc->rst_vout[i].id = vout_resets[i];
> >> >> +    ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
> >> >> +                                             dc->rst_vout);
> >> >> +    if (ret) {
> >> >> +            dev_err(dev, "Failed to get reset controls\n");
> >> >> +            return ret;
> >> >> +    }
> >> >> +
> >> >> +    irq = platform_get_irq(pdev, 0);
> >> >> +
> >> >> +    ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
> >> >
> >> > Are you ready to handle the IRQ at this point?
> Do you have some good suggestions?
> Are there any hidden dangers in doing so?


If your driver is not ready, stray interrupt can crash your driver.
For pm_runtime-enabled devices it is even more important: the
interrupt handled might try accessing device which is powered off.

> Hope to get good opinions

The usual suggestion is: request the interrupt when your data is fully
set up. Or request_irq with IRQF_NO_AUTOEN and then enable_irq() /
disable_irq() as required.

>
> >> >
> >> >> +    if (ret < 0) {
> >> >> +            dev_err(dev, "Failed to install irq:%u.\n", irq);
> >> >> +            return ret;
> >> >> +    }
> >> >> +
> >> >> +    dev_set_drvdata(dev, dc);
> >> >> +
> >> >> +    return component_add(dev, &dc_component_ops);
> >> >> +}
> >> >> +

[skipped]

> >> >> +static int vs_plane_atomic_get_property(struct drm_plane *plane,
> >> >> +                                    const struct drm_plane_state *state,
> >> >> +                                    struct drm_property *property,
> >> >> +                                    uint64_t *val)
> >> >> +{
> >> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> >> +    const struct vs_plane_state *vs_plane_state =
> >> >> +            container_of(state, const struct vs_plane_state, base);
> >> >> +
> >> >> +    if (property == vs_plane->degamma_mode)
> >> >> +            *val = vs_plane_state->degamma;
> >> >> +    else if (property == vs_plane->watermark_prop)
> >> >> +            *val = (vs_plane_state->watermark) ?
> >> >> +                                    vs_plane_state->watermark->base.id : 0;
> >> >> +    else if (property == vs_plane->color_mgmt_prop)
> >> >> +            *val = (vs_plane_state->color_mgmt) ?
> >> >> +                                    vs_plane_state->color_mgmt->base.id : 0;
> >> >
> >> > degamma and color management should use standard properties.
>
> hello Dmitry:
> There is a question that troubles me
>
> drm_plane include such standard properties.
> {
>         struct drm_property *alpha_property;
>         struct drm_property *zpos_property;
>         struct drm_property *rotation_property;
>         struct drm_property *blend_mode_property;
>         struct drm_property *color_encoding_property;
>         struct drm_property *color_range_property;
>         struct drm_property *scaling_filter_property;
> }
>
> it doesn't include degamma and color management properties

Which is because they are not standardised yet.

>
> >> >
> >> >> +    else if (property == vs_plane->roi_prop)
> >> >> +            *val = (vs_plane_state->roi) ?
> >> >> +                                    vs_plane_state->roi->base.id : 0;
> >> >> +    else
> >> >> +            return -EINVAL;
> >> >> +
> >> >> +    return 0;
> >> >> +}
> >> >> +
> >> >> +static bool vs_format_mod_supported(struct drm_plane *plane,
> >> >> +                                u32 format,
> >> >> +                                u64 modifier)
> >> >> +{
> >> >> +    int i;
> >> >> +
> >> >> +    /* We always have to allow these modifiers:
> >> >> +     * 1. Core DRM checks for LINEAR support if userspace does not provide modifiers.
> >> >> +     * 2. Not passing any modifiers is the same as explicitly passing INVALID.
> >> >> +     */
> >> >> +    if (modifier == DRM_FORMAT_MOD_LINEAR)
> >> >> +            return true;
> >> >> +
> >> >> +    /* Check that the modifier is on the list of the plane's supported modifiers. */
> >> >> +    for (i = 0; i < plane->modifier_count; i++) {
> >> >> +            if (modifier == plane->modifiers[i])
> >> >> +                    break;
> >> >> +    }
> >> >> +
> >> >> +    if (i == plane->modifier_count)
> >> >> +            return false;
> >> >> +
> >> >> +    return true;
> >> >> +}
> >> >> +
> >> >> +const struct drm_plane_funcs vs_plane_funcs = {
> >> >> +    .update_plane           = drm_atomic_helper_update_plane,
> >> >> +    .disable_plane          = drm_atomic_helper_disable_plane,
> >> >> +    .reset                  = vs_plane_reset,
> >> >> +    .atomic_duplicate_state = vs_plane_atomic_duplicate_state,
> >> >> +    .atomic_destroy_state   = vs_plane_atomic_destroy_state,
> >> >> +    .atomic_set_property    = vs_plane_atomic_set_property,
> >> >> +    .atomic_get_property    = vs_plane_atomic_get_property,
> >> >> +    .format_mod_supported   = vs_format_mod_supported,
> >> >> +};
> >> >> +
> >> >> +static unsigned char vs_get_plane_number(struct drm_framebuffer *fb)
> >> >> +{
> >> >> +    const struct drm_format_info *info;
> >> >> +
> >> >> +    if (!fb)
> >> >> +            return 0;
> >> >> +
> >> >> +    info = drm_format_info(fb->format->format);
> >> >> +    if (!info || info->num_planes > DRM_FORMAT_MAX_PLANES)
> >> >> +            return 0;
> >> >> +
> >> >> +    return info->num_planes;
> >> >> +}
> >> >> +
> >> >> +static int vs_plane_atomic_check(struct drm_plane *plane,
> >> >> +                             struct drm_atomic_state *state)
> >> >> +{
> >> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> >> >> +                                                                             plane);
> >> >> +    unsigned char i, num_planes;
> >> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
> >> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
> >> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
> >> >> +
> >> >> +    if (!crtc || !fb)
> >> >> +            return 0;
> >> >> +
> >> >> +    num_planes = vs_get_plane_number(fb);
> >> >> +
> >> >> +    for (i = 0; i < num_planes; i++) {
> >> >> +            dma_addr_t dma_addr;
> >> >> +
> >> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
> >> >> +            plane_state->dma_addr[i] = dma_addr;
> >> >> +    }
> >> >> +
> >> >> +    return vs_dc_check_plane(dc, plane, state);
> >> >> +}
> >> >> +
> >> >> +static int vs_cursor_plane_atomic_check(struct drm_plane *plane,
> >> >> +                                    struct drm_atomic_state *state)
> >> >> +{
> >> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> >> >> +                                                                      plane);
> >> >> +    unsigned char i, num_planes;
> >> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
> >> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
> >> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
> >> >> +
> >> >> +    if (!crtc || !fb)
> >> >> +            return 0;
> >> >> +
> >> >> +    num_planes = vs_get_plane_number(fb);
> >> >> +
> >> >> +    for (i = 0; i < num_planes; i++) {
> >> >> +            dma_addr_t dma_addr;
> >> >> +
> >> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
> >> >> +            plane_state->dma_addr[i] = dma_addr;
> >> >> +    }
> >> >> +
> >> >> +    return vs_dc_check_cursor_plane(dc, plane, state);
> >> >> +}
> >> >> +
> >> >> +static void vs_plane_atomic_update(struct drm_plane *plane,
> >> >> +                               struct drm_atomic_state *state)
> >> >> +{
> >> >> +    struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
> >> >> +                                                                      plane);
> >> >
> >> > New line after the equal sign will be better.
> >> >
> >> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> >> >> +                                                                      plane);
> >> >> +
> >> >> +    unsigned char i, num_planes;
> >> >> +    struct drm_framebuffer *fb;
> >> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
> >> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
> >> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> >> +
> >> >> +    if (!new_state->fb || !new_state->crtc)
> >> >> +            return;
> >> >
> >> > if (!new_state->visible) ?
>
> no matter what value it is ? the driver will handle it
> in func
>
> static void update_fb(struct vs_plane *plane, u8 display_id,
>                       struct dc_hw_fb *fb, struct drm_plane_state *state)
> {
>         struct vs_plane_state *plane_state = to_vs_plane_state(state);
>         struct drm_framebuffer *drm_fb = state->fb;
>         struct drm_rect *src = &state->src;
>
> .......
>         fb->enable = state->visible;
> .......
> }
>
> so no need add "if (!new_state->visible)" i think

I mean instead of fb&&crtc check. Otherwise you are duplicating checks
in drm_atomic_helper_check_plane_state(). And with the pixel_source
being on their way this condition becomes legacy.

>
> >> >
> >> >> +
> >> >> +    fb = new_state->fb;
> >> >> +
> >> >> +    drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
> >> >> +
> >> >> +    num_planes = vs_get_plane_number(fb);
> >> >> +
> >> >> +    for (i = 0; i < num_planes; i++) {
> >> >> +            dma_addr_t dma_addr;
> >> >> +
> >> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
> >> >> +            plane_state->dma_addr[i] = dma_addr;
> >> >> +    }
> >> >> +
> >> >> +    plane_state->status.src = drm_plane_state_src(new_state);
> >> >> +    plane_state->status.dest = drm_plane_state_dest(new_state);
> >> >> +
> >> >> +    vs_dc_update_plane(dc, vs_plane, plane, state);
> >> >> +}
> >> >> +


-- 
With best wishes
Dmitry

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-11-15 16:00             ` Dmitry Baryshkov
  0 siblings, 0 replies; 117+ messages in thread
From: Dmitry Baryshkov @ 2023-11-15 16:00 UTC (permalink / raw)
  To: Keith Zhao
  Cc: Emil Renner Berthing, dri-devel, Krzysztof Kozlowski,
	linux-riscv, Sumit Semwal, Shengyang Chen, linux-media,
	devicetree, Conor Dooley, Albert Ou, Maxime Ripard, Jagan Teki,
	linaro-mm-sig, Rob Herring, Chris Morgan, Paul Walmsley,
	Bjorn Andersson, linux-kernel, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Thomas Zimmermann, Shawn Guo, christian.koenig

On Wed, 15 Nov 2023 at 15:30, Keith Zhao <keith.zhao@starfivetech.com> wrote:
>
>
>
> On 2023/11/14 18:59, Dmitry Baryshkov wrote:
> > On Tue, 14 Nov 2023 at 12:42, Keith Zhao <keith.zhao@starfivetech.com> wrote:
> >>
> >>
> >>
> >> On 2023/10/26 3:28, Dmitry Baryshkov wrote:
> >> > On 25/10/2023 13:39, Keith Zhao wrote:
> >> >> add 2 crtcs and 8 planes in vs-drm
> >> >>
> >> >> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> >> >> ---
> >> >>   drivers/gpu/drm/verisilicon/Makefile   |    8 +-
> >> >>   drivers/gpu/drm/verisilicon/vs_crtc.c  |  257 ++++
> >> >>   drivers/gpu/drm/verisilicon/vs_crtc.h  |   43 +
> >> >>   drivers/gpu/drm/verisilicon/vs_dc.c    | 1002 ++++++++++++
> >> >>   drivers/gpu/drm/verisilicon/vs_dc.h    |   80 +
> >> >>   drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1959 ++++++++++++++++++++++++
> >> >>   drivers/gpu/drm/verisilicon/vs_dc_hw.h |  492 ++++++
> >> >>   drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
> >> >>   drivers/gpu/drm/verisilicon/vs_plane.c |  526 +++++++
> >> >>   drivers/gpu/drm/verisilicon/vs_plane.h |   58 +
> >> >>   drivers/gpu/drm/verisilicon/vs_type.h  |   69 +
> >> >>   11 files changed, 4494 insertions(+), 2 deletions(-)
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h
> >> >>   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
> >> >>
> >> >> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> >> >> index 7d3be305b..1d48016ca 100644
> >> >> --- a/drivers/gpu/drm/verisilicon/Makefile
> >> >> +++ b/drivers/gpu/drm/verisilicon/Makefile
> >> >> @@ -1,7 +1,11 @@
> >> >>   # SPDX-License-Identifier: GPL-2.0
> >> >>
> >> >> -vs_drm-objs := vs_drv.o \
> >> >> -            vs_modeset.o
> >> >> +vs_drm-objs := vs_dc_hw.o \
> >> >> +            vs_dc.o \
> >> >> +            vs_crtc.o \
> >> >> +            vs_drv.o \
> >> >> +            vs_modeset.o \
> >> >> +            vs_plane.o
> >> >>
> >> >>   obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
> >> >>
> >> >> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
> >> >> new file mode 100644
> >> >> index 000000000..8a658ea77
> >> >> --- /dev/null
> >> >> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
> >> >> @@ -0,0 +1,257 @@
> >> >> +// SPDX-License-Identifier: GPL-2.0
> >> >> +/*
> >> >> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> >> >> + *
> >> >> + */
> >> >> +
> >> >> +#include <linux/clk.h>
> >> >> +#include <linux/debugfs.h>
> >> >> +#include <linux/media-bus-format.h>
> >> >> +
> >> >> +#include <drm/drm_atomic_helper.h>
> >> >> +#include <drm/drm_atomic.h>
> >> >> +#include <drm/drm_crtc.h>
> >> >> +#include <drm/drm_gem_atomic_helper.h>
> >> >> +#include <drm/drm_vblank.h>
> >> >> +#include <drm/vs_drm.h>
> >> >> +
> >> >> +#include "vs_crtc.h"
> >> >> +#include "vs_dc.h"
> >> >> +#include "vs_drv.h"
> >> >> +
> >> >> +static void vs_crtc_reset(struct drm_crtc *crtc)
> >> >> +{
> >> >> +    struct vs_crtc_state *state;
> >> >> +
> >> >> +    if (crtc->state) {
> >> >> +            __drm_atomic_helper_crtc_destroy_state(crtc->state);
> >> >> +
> >> >> +            state = to_vs_crtc_state(crtc->state);
> >> >> +            kfree(state);
> >> >> +            crtc->state = NULL;
> >> >> +    }
> >> >
> >> > You can call your crtc_destroy_state function directly here.
> >> >
> >> >> +
> >> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
> >> >> +    if (!state)
> >> >> +            return;
> >> >> +
> >> >> +    __drm_atomic_helper_crtc_reset(crtc, &state->base);
> >> >> +}
> >> >> +
> >> >> +static struct drm_crtc_state *
> >> >> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
> >> >> +{
> >> >> +    struct vs_crtc_state *ori_state;
> >> >
> >> > It might be a matter of taste, but it is usually old_state.
> >> >
> >> >> +    struct vs_crtc_state *state;
> >> >> +
> >> >> +    if (!crtc->state)
> >> >> +            return NULL;
> >> >> +
> >> >> +    ori_state = to_vs_crtc_state(crtc->state);
> >> >> +    state = kzalloc(sizeof(*state), GFP_KERNEL);
> >> >> +    if (!state)
> >> >> +            return NULL;
> >> >> +
> >> >> +    __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
> >> >> +
> >> >> +    state->output_fmt = ori_state->output_fmt;
> >> >> +    state->encoder_type = ori_state->encoder_type;
> >> >> +    state->bpp = ori_state->bpp;
> >> >> +    state->underflow = ori_state->underflow;
> >> >
> >> > Can you use kmemdup instead?
> >> >
> >> >> +
> >> >> +    return &state->base;
> >> >> +}
> >> >> +
> >> >> +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
> >> >> +                                     struct drm_crtc_state *state)
> >> >> +{
> >> >> +    __drm_atomic_helper_crtc_destroy_state(state);
> >> >> +    kfree(to_vs_crtc_state(state));
> >> >> +}
> >> >> +
> >> >> +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
> >> >> +{
> >> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> >> +
> >> >> +    vs_dc_enable_vblank(dc, true);
> >> >> +
> >> >> +    return 0;
> >> >> +}
> >> >> +
> >> >> +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
> >> >> +{
> >> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> >> +
> >> >> +    vs_dc_enable_vblank(dc, false);
> >> >> +}
> >> >> +
> >> >> +static const struct drm_crtc_funcs vs_crtc_funcs = {
> >> >> +    .set_config             = drm_atomic_helper_set_config,
> >> >> +    .page_flip              = drm_atomic_helper_page_flip,
> >> >
> >> > destroy is required, see drm_mode_config_cleanup()
> >>
> >> hi Dmitry:
> >> if define destroy in drm_crtc_funcs,
> >> it will make __drmm_crtc_init_with_planes unhappy
> >
> > Ack, I missed that you have been using drmm_crtc_init. BTW, I checked
> > your code, you should be able to switch drm
> > drmm_crtc_alloc_with_planes().
> >
> yes
> I done the replace and it can work well.
>
> >>
> >> see:
> >> __printf(6, 0)
> >> static int __drmm_crtc_init_with_planes(struct drm_device *dev,
> >>                                         struct drm_crtc *crtc,
> >>                                         struct drm_plane *primary,
> >>                                         struct drm_plane *cursor,
> >>                                         const struct drm_crtc_funcs *funcs,
> >>                                         const char *name,
> >>                                         va_list args)
> >> {
> >>         int ret;
> >>
> >>         drm_WARN_ON(dev, funcs && funcs->destroy);
> >>
> >> ........
> >> }
> >>
> >> It should not need to be defined here, I think
> >>
> >> >
> >> >> +    .reset                  = vs_crtc_reset,
> >> >> +    .atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
> >> >> +    .atomic_destroy_state   = vs_crtc_atomic_destroy_state,
> >> >
> >> > please consider adding atomic_print_state to output driver-specific bits.
> >> >
> >> >> +    .enable_vblank          = vs_crtc_enable_vblank,
> >> >> +    .disable_vblank         = vs_crtc_disable_vblank,
> >> >> +};
> >> >> +
> >> >> +static u8 cal_pixel_bits(u32 bus_format)
> >> >
> >> > This looks like a generic helper code, which can go to a common place.
> I don't know if I understand correctly
> Here I remove static
> Add 2 lines in vs_drv.h
>
> /* vs_crtc.c */
> u8 cal_pixel_bits(u32 bus_format);
>
> to make it common

I mean, move it to a generic location, like include/media/

>
> >> >
> >> >> +{
> >> >> +    u8 bpp;
> >> >> +
> >> >> +    switch (bus_format) {
> >> >> +    case MEDIA_BUS_FMT_RGB565_1X16:
> >> >> +    case MEDIA_BUS_FMT_UYVY8_1X16:
> >> >> +            bpp = 16;
> >> >> +            break;
> >> >> +    case MEDIA_BUS_FMT_RGB666_1X18:
> >> >> +    case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> >> >> +            bpp = 18;
> >> >> +            break;
> >> >> +    case MEDIA_BUS_FMT_UYVY10_1X20:
> >> >> +            bpp = 20;
> >> >> +            break;
> >> >> +    case MEDIA_BUS_FMT_BGR888_1X24:
> >> >> +    case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> >> >> +    case MEDIA_BUS_FMT_YUV8_1X24:
> >> >> +            bpp = 24;
> >> >> +            break;
> >> >> +    case MEDIA_BUS_FMT_RGB101010_1X30:
> >> >> +    case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> >> >> +    case MEDIA_BUS_FMT_YUV10_1X30:
> >> >> +            bpp = 30;
> >> >> +            break;
> >> >> +    default:
> >> >> +            bpp = 24;
> >> >> +            break;
> >> >> +    }
> >> >> +
> >> >> +    return bpp;
> >> >> +}
> >> >> +

[skipped]

> >> >> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
> >> >> +                           struct vs_dc_info *info)
> >> >> +{
> >> >> +    struct vs_crtc *crtc;
> >> >> +    int ret;
> >> >> +
> >> >> +    if (!info)
> >> >> +            return NULL;
> >> >> +
> >> >> +    crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
> >> >> +    if (!crtc)
> >> >> +            return NULL;
> >> >> +
> >> >> +    ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
> >> >> +                                     NULL, NULL, &vs_crtc_funcs,
> >> >> +                                     info->name ? info->name : NULL);
> >> >
> >> > It might be better to add drmm_crtc_init() helper.
>         drmm_crtc_alloc_with_planes used:
>         ...
>         struct vs_crtc *crtc;
>         int ret;
>
>         if (!info)
>                 return NULL;
>
>         crtc = drmm_crtc_alloc_with_planes(drm_dev, struct vs_crtc, base,
>                                                NULL, NULL,
>                                                &vs_crtc_funcs, info->name ? info->name : NULL);
>         ...

Ack.

>
> >> >
> >> >> +    if (ret)
> >> >> +            return NULL;
> >> >> +
> >> >> +    drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
> >> >> +
> >> >> +    if (info->gamma_size) {
> >> >> +            ret = drm_mode_crtc_set_gamma_size(&crtc->base,
> >> >> +                                               info->gamma_size);
> >> >> +            if (ret)
> >> >> +                    return NULL;
> >> >> +
> >> >> +            drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
> >> >> +                                       info->gamma_size);
> >> >> +    }
> >> >> +
> >> >> +    crtc->max_bpc = info->max_bpc;
> >> >> +    crtc->color_formats = info->color_formats;
> >> >> +    return crtc;
> >> >> +}

> >> >> +const struct component_ops dc_component_ops = {
> >> >> +    .bind = dc_bind,
> >> >> +    .unbind = dc_unbind,
> >> >> +};
> >> >> +
> >> >> +static const struct of_device_id dc_driver_dt_match[] = {
> >> >> +    { .compatible = "starfive,jh7110-dc8200", },
> >> >> +    {},
> >> >> +};
> >> >> +MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
> >> >> +
> >> >> +static int dc_probe(struct platform_device *pdev)
> >> >> +{
> >> >> +    struct device *dev = &pdev->dev;
> >> >> +    struct vs_dc *dc;
> >> >> +    int irq, ret, i;
> >> >> +
> >> >> +    dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
> >> >> +    if (!dc)
> >> >> +            return -ENOMEM;
> >> >> +
> >> >> +    dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
> >> >> +    if (IS_ERR(dc->hw.hi_base))
> >> >> +            return PTR_ERR(dc->hw.hi_base);
> >> >> +
> >> >> +    dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
> >> >> +    if (IS_ERR(dc->hw.reg_base))
> >> >> +            return PTR_ERR(dc->hw.reg_base);
> >> >> +
> >> >> +    dc->nclks = ARRAY_SIZE(dc->clk_vout);
> >> >> +    for (i = 0; i < dc->nclks; ++i)
> >> >> +            dc->clk_vout[i].id = vout_clocks[i];
> >> >> +    ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
> >> >> +    if (ret) {
> >> >> +            dev_err(dev, "Failed to get clk controls\n");
> >> >> +            return ret;
> >> >> +    }
> >> >> +
> >> >> +    dc->nrsts = ARRAY_SIZE(dc->rst_vout);
> >> >> +    for (i = 0; i < dc->nrsts; ++i)
> >> >> +            dc->rst_vout[i].id = vout_resets[i];
> >> >> +    ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
> >> >> +                                             dc->rst_vout);
> >> >> +    if (ret) {
> >> >> +            dev_err(dev, "Failed to get reset controls\n");
> >> >> +            return ret;
> >> >> +    }
> >> >> +
> >> >> +    irq = platform_get_irq(pdev, 0);
> >> >> +
> >> >> +    ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
> >> >
> >> > Are you ready to handle the IRQ at this point?
> Do you have some good suggestions?
> Are there any hidden dangers in doing so?


If your driver is not ready, stray interrupt can crash your driver.
For pm_runtime-enabled devices it is even more important: the
interrupt handled might try accessing device which is powered off.

> Hope to get good opinions

The usual suggestion is: request the interrupt when your data is fully
set up. Or request_irq with IRQF_NO_AUTOEN and then enable_irq() /
disable_irq() as required.

>
> >> >
> >> >> +    if (ret < 0) {
> >> >> +            dev_err(dev, "Failed to install irq:%u.\n", irq);
> >> >> +            return ret;
> >> >> +    }
> >> >> +
> >> >> +    dev_set_drvdata(dev, dc);
> >> >> +
> >> >> +    return component_add(dev, &dc_component_ops);
> >> >> +}
> >> >> +

[skipped]

> >> >> +static int vs_plane_atomic_get_property(struct drm_plane *plane,
> >> >> +                                    const struct drm_plane_state *state,
> >> >> +                                    struct drm_property *property,
> >> >> +                                    uint64_t *val)
> >> >> +{
> >> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> >> +    const struct vs_plane_state *vs_plane_state =
> >> >> +            container_of(state, const struct vs_plane_state, base);
> >> >> +
> >> >> +    if (property == vs_plane->degamma_mode)
> >> >> +            *val = vs_plane_state->degamma;
> >> >> +    else if (property == vs_plane->watermark_prop)
> >> >> +            *val = (vs_plane_state->watermark) ?
> >> >> +                                    vs_plane_state->watermark->base.id : 0;
> >> >> +    else if (property == vs_plane->color_mgmt_prop)
> >> >> +            *val = (vs_plane_state->color_mgmt) ?
> >> >> +                                    vs_plane_state->color_mgmt->base.id : 0;
> >> >
> >> > degamma and color management should use standard properties.
>
> hello Dmitry:
> There is a question that troubles me
>
> drm_plane include such standard properties.
> {
>         struct drm_property *alpha_property;
>         struct drm_property *zpos_property;
>         struct drm_property *rotation_property;
>         struct drm_property *blend_mode_property;
>         struct drm_property *color_encoding_property;
>         struct drm_property *color_range_property;
>         struct drm_property *scaling_filter_property;
> }
>
> it doesn't include degamma and color management properties

Which is because they are not standardised yet.

>
> >> >
> >> >> +    else if (property == vs_plane->roi_prop)
> >> >> +            *val = (vs_plane_state->roi) ?
> >> >> +                                    vs_plane_state->roi->base.id : 0;
> >> >> +    else
> >> >> +            return -EINVAL;
> >> >> +
> >> >> +    return 0;
> >> >> +}
> >> >> +
> >> >> +static bool vs_format_mod_supported(struct drm_plane *plane,
> >> >> +                                u32 format,
> >> >> +                                u64 modifier)
> >> >> +{
> >> >> +    int i;
> >> >> +
> >> >> +    /* We always have to allow these modifiers:
> >> >> +     * 1. Core DRM checks for LINEAR support if userspace does not provide modifiers.
> >> >> +     * 2. Not passing any modifiers is the same as explicitly passing INVALID.
> >> >> +     */
> >> >> +    if (modifier == DRM_FORMAT_MOD_LINEAR)
> >> >> +            return true;
> >> >> +
> >> >> +    /* Check that the modifier is on the list of the plane's supported modifiers. */
> >> >> +    for (i = 0; i < plane->modifier_count; i++) {
> >> >> +            if (modifier == plane->modifiers[i])
> >> >> +                    break;
> >> >> +    }
> >> >> +
> >> >> +    if (i == plane->modifier_count)
> >> >> +            return false;
> >> >> +
> >> >> +    return true;
> >> >> +}
> >> >> +
> >> >> +const struct drm_plane_funcs vs_plane_funcs = {
> >> >> +    .update_plane           = drm_atomic_helper_update_plane,
> >> >> +    .disable_plane          = drm_atomic_helper_disable_plane,
> >> >> +    .reset                  = vs_plane_reset,
> >> >> +    .atomic_duplicate_state = vs_plane_atomic_duplicate_state,
> >> >> +    .atomic_destroy_state   = vs_plane_atomic_destroy_state,
> >> >> +    .atomic_set_property    = vs_plane_atomic_set_property,
> >> >> +    .atomic_get_property    = vs_plane_atomic_get_property,
> >> >> +    .format_mod_supported   = vs_format_mod_supported,
> >> >> +};
> >> >> +
> >> >> +static unsigned char vs_get_plane_number(struct drm_framebuffer *fb)
> >> >> +{
> >> >> +    const struct drm_format_info *info;
> >> >> +
> >> >> +    if (!fb)
> >> >> +            return 0;
> >> >> +
> >> >> +    info = drm_format_info(fb->format->format);
> >> >> +    if (!info || info->num_planes > DRM_FORMAT_MAX_PLANES)
> >> >> +            return 0;
> >> >> +
> >> >> +    return info->num_planes;
> >> >> +}
> >> >> +
> >> >> +static int vs_plane_atomic_check(struct drm_plane *plane,
> >> >> +                             struct drm_atomic_state *state)
> >> >> +{
> >> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> >> >> +                                                                             plane);
> >> >> +    unsigned char i, num_planes;
> >> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
> >> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
> >> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
> >> >> +
> >> >> +    if (!crtc || !fb)
> >> >> +            return 0;
> >> >> +
> >> >> +    num_planes = vs_get_plane_number(fb);
> >> >> +
> >> >> +    for (i = 0; i < num_planes; i++) {
> >> >> +            dma_addr_t dma_addr;
> >> >> +
> >> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
> >> >> +            plane_state->dma_addr[i] = dma_addr;
> >> >> +    }
> >> >> +
> >> >> +    return vs_dc_check_plane(dc, plane, state);
> >> >> +}
> >> >> +
> >> >> +static int vs_cursor_plane_atomic_check(struct drm_plane *plane,
> >> >> +                                    struct drm_atomic_state *state)
> >> >> +{
> >> >> +    struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> >> >> +                                                                      plane);
> >> >> +    unsigned char i, num_planes;
> >> >> +    struct drm_framebuffer *fb = new_plane_state->fb;
> >> >> +    struct drm_crtc *crtc = new_plane_state->crtc;
> >> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> >> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
> >> >> +
> >> >> +    if (!crtc || !fb)
> >> >> +            return 0;
> >> >> +
> >> >> +    num_planes = vs_get_plane_number(fb);
> >> >> +
> >> >> +    for (i = 0; i < num_planes; i++) {
> >> >> +            dma_addr_t dma_addr;
> >> >> +
> >> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i);
> >> >> +            plane_state->dma_addr[i] = dma_addr;
> >> >> +    }
> >> >> +
> >> >> +    return vs_dc_check_cursor_plane(dc, plane, state);
> >> >> +}
> >> >> +
> >> >> +static void vs_plane_atomic_update(struct drm_plane *plane,
> >> >> +                               struct drm_atomic_state *state)
> >> >> +{
> >> >> +    struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
> >> >> +                                                                      plane);
> >> >
> >> > New line after the equal sign will be better.
> >> >
> >> >> +    struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
> >> >> +                                                                      plane);
> >> >> +
> >> >> +    unsigned char i, num_planes;
> >> >> +    struct drm_framebuffer *fb;
> >> >> +    struct vs_plane *vs_plane = to_vs_plane(plane);
> >> >> +    struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
> >> >> +    struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
> >> >> +    struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> >> >> +
> >> >> +    if (!new_state->fb || !new_state->crtc)
> >> >> +            return;
> >> >
> >> > if (!new_state->visible) ?
>
> no matter what value it is ? the driver will handle it
> in func
>
> static void update_fb(struct vs_plane *plane, u8 display_id,
>                       struct dc_hw_fb *fb, struct drm_plane_state *state)
> {
>         struct vs_plane_state *plane_state = to_vs_plane_state(state);
>         struct drm_framebuffer *drm_fb = state->fb;
>         struct drm_rect *src = &state->src;
>
> .......
>         fb->enable = state->visible;
> .......
> }
>
> so no need add "if (!new_state->visible)" i think

I mean instead of fb&&crtc check. Otherwise you are duplicating checks
in drm_atomic_helper_check_plane_state(). And with the pixel_source
being on their way this condition becomes legacy.

>
> >> >
> >> >> +
> >> >> +    fb = new_state->fb;
> >> >> +
> >> >> +    drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state);
> >> >> +
> >> >> +    num_planes = vs_get_plane_number(fb);
> >> >> +
> >> >> +    for (i = 0; i < num_planes; i++) {
> >> >> +            dma_addr_t dma_addr;
> >> >> +
> >> >> +            dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i);
> >> >> +            plane_state->dma_addr[i] = dma_addr;
> >> >> +    }
> >> >> +
> >> >> +    plane_state->status.src = drm_plane_state_src(new_state);
> >> >> +    plane_state->status.dest = drm_plane_state_dest(new_state);
> >> >> +
> >> >> +    vs_dc_update_plane(dc, vs_plane, plane, state);
> >> >> +}
> >> >> +


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
  2023-10-25 10:39   ` Keith Zhao
  (?)
@ 2023-11-24 13:40     ` Thomas Zimmermann
  -1 siblings, 0 replies; 117+ messages in thread
From: Thomas Zimmermann @ 2023-11-24 13:40 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Maxime Ripard,
	Jagan Teki, Rob Herring, Chris Morgan, Paul Walmsley,
	Bjorn Andersson, Changhuang Liang, Jack Zhu, Palmer Dabbelt,
	Shawn Guo, christian.koenig


[-- Attachment #1.1.1: Type: text/plain, Size: 58876 bytes --]

Hi

Am 25.10.23 um 12:39 schrieb Keith Zhao:
[...]
> +
> +static struct drm_crtc_state *
> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc_state *ori_state;

I have not yet seen 'ori_' being used anywhere. Typical names are 
'state' for the current state and 'new_state' for the newly created 
state.  Would be nice for consistency with other drivers.

> +	struct vs_crtc_state *state;
> +
> +	if (!crtc->state)
> +		return NULL;
> +
> +	ori_state = to_vs_crtc_state(crtc->state);
> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return NULL;
> +
> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
> +
> +	state->output_fmt = ori_state->output_fmt;
> +	state->encoder_type = ori_state->encoder_type;
> +	state->bpp = ori_state->bpp;
> +	state->underflow = ori_state->underflow;
> +
> +	return &state->base;
> +}
> +
> +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
> +					 struct drm_crtc_state *state)
> +{
> +	__drm_atomic_helper_crtc_destroy_state(state);
> +	kfree(to_vs_crtc_state(state));
> +}
> +
> +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_enable_vblank(dc, true);
> +
> +	return 0;
> +}
> +
> +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_enable_vblank(dc, false);
> +}
> +
> +static const struct drm_crtc_funcs vs_crtc_funcs = {
> +	.set_config		= drm_atomic_helper_set_config,
> +	.page_flip		= drm_atomic_helper_page_flip,
> +	.reset			= vs_crtc_reset,
> +	.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
> +	.atomic_destroy_state	= vs_crtc_atomic_destroy_state,
> +	.enable_vblank		= vs_crtc_enable_vblank,
> +	.disable_vblank		= vs_crtc_disable_vblank,
> +};
> +
> +static u8 cal_pixel_bits(u32 bus_format)
> +{
> +	u8 bpp;
> +
> +	switch (bus_format) {
> +	case MEDIA_BUS_FMT_RGB565_1X16:
> +	case MEDIA_BUS_FMT_UYVY8_1X16:
> +		bpp = 16;
> +		break;
> +	case MEDIA_BUS_FMT_RGB666_1X18:
> +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> +		bpp = 18;
> +		break;
> +	case MEDIA_BUS_FMT_UYVY10_1X20:
> +		bpp = 20;
> +		break;
> +	case MEDIA_BUS_FMT_BGR888_1X24:
> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +	case MEDIA_BUS_FMT_YUV8_1X24:
> +		bpp = 24;
> +		break;
> +	case MEDIA_BUS_FMT_RGB101010_1X30:
> +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> +	case MEDIA_BUS_FMT_YUV10_1X30:
> +		bpp = 30;
> +		break;
> +	default:
> +		bpp = 24;
> +		break;
> +	}
> +
> +	return bpp;
> +}
> +
> +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
> +				  struct drm_atomic_state *state)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
> +
> +	vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
> +
> +	vs_dc_enable(dc, crtc);
> +	drm_crtc_vblank_on(crtc);
> +}
> +
> +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
> +				   struct drm_atomic_state *state)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	drm_crtc_vblank_off(crtc);
> +
> +	vs_dc_disable(dc, crtc);
> +
> +	if (crtc->state->event && !crtc->state->active) {
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +
> +		crtc->state->event = NULL;
> +	}
> +}
> +
> +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
> +				 struct drm_atomic_state *state)
> +{
> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
> +									  crtc);
> +
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct device *dev = vs_crtc->dev;
> +	struct drm_property_blob *blob = crtc->state->gamma_lut;
> +	struct drm_color_lut *lut;
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	if (crtc_state->color_mgmt_changed) {
> +		if (blob && blob->length) {
> +			lut = blob->data;
> +			vs_dc_set_gamma(dc, crtc, lut,
> +					blob->length / sizeof(*lut));
> +			vs_dc_enable_gamma(dc, crtc, true);
> +		} else {
> +			vs_dc_enable_gamma(dc, crtc, false);
> +		}
> +	}
> +}
> +
> +static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
> +				 struct drm_atomic_state *state)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct drm_pending_vblank_event *event = crtc->state->event;
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_commit(dc);
> +
> +	if (event) {
> +		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
> +
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		drm_crtc_arm_vblank_event(crtc, event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +		crtc->state->event = NULL;
> +	}
> +}
> +
> +static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
> +	.atomic_check = drm_crtc_helper_atomic_check,
> +	.atomic_enable	= vs_crtc_atomic_enable,
> +	.atomic_disable = vs_crtc_atomic_disable,
> +	.atomic_begin	= vs_crtc_atomic_begin,
> +	.atomic_flush	= vs_crtc_atomic_flush,
> +};
> +
> +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
> +	{ VS_SINGLE_DC,				"single dc mode" },
> +	{ VS_MULTI_DC_PRIMARY,		"primary dc for multi dc mode" },
> +	{ VS_MULTI_DC_SECONDARY,	"secondary dc for multi dc mode" },
> +};
> +
> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
> +			       struct vs_dc_info *info)
> +{
> +	struct vs_crtc *crtc;
> +	int ret;
> +
> +	if (!info)
> +		return NULL;
> +
> +	crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
> +	if (!crtc)
> +		return NULL;
> +
> +	ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
> +					 NULL, NULL, &vs_crtc_funcs,
> +					 info->name ? info->name : NULL);
> +	if (ret)
> +		return NULL;
> +
> +	drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
> +
> +	if (info->gamma_size) {
> +		ret = drm_mode_crtc_set_gamma_size(&crtc->base,
> +						   info->gamma_size);
> +		if (ret)
> +			return NULL;
> +
> +		drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
> +					   info->gamma_size);
> +	}
> +
> +	crtc->max_bpc = info->max_bpc;
> +	crtc->color_formats = info->color_formats;
> +	return crtc;
> +}
> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h
> new file mode 100644
> index 000000000..526dd63e5
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.h
> @@ -0,0 +1,43 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_CRTC_H__
> +#define __VS_CRTC_H__
> +
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "vs_type.h"
> +
> +struct vs_crtc_state {
> +	struct drm_crtc_state base;
> +
> +	u32 output_fmt;
> +	u8 encoder_type;
> +	u8 bpp;
> +	bool underflow;
> +};
> +
> +struct vs_crtc {
> +	struct drm_crtc base;
> +	struct device *dev;
> +	unsigned int max_bpc;
> +	unsigned int color_formats;
> +};
> +
> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
> +			       struct vs_dc_info *info);
> +
> +static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc)
> +{
> +	return container_of(crtc, struct vs_crtc, base);
> +}
> +
> +static inline struct vs_crtc_state *
> +to_vs_crtc_state(struct drm_crtc_state *state)
> +{
> +	return container_of(state, struct vs_crtc_state, base);
> +}
> +#endif /* __VS_CRTC_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
> new file mode 100644
> index 000000000..b5ab92d98
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc.c
> @@ -0,0 +1,1002 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/component.h>
> +#include <linux/clk.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/reset.h>
> +
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_blend.h>
> +#include <drm/drm_framebuffer.h>
> +#include <drm/drm_vblank.h>
> +#include <drm/vs_drm.h>
> +
> +#include "vs_dc_hw.h"
> +#include "vs_dc.h"
> +#include "vs_drv.h"
> +
> +static const char * const vout_clocks[] = {
> +	"noc_bus",
> +	"channel0",
> +	"channel1",
> +	"dc_core",
> +	"axi_core",
> +	"ahb",
> +	"hdmi_tx",
> +	"dc_parent",
> +
> +};
> +
> +static const char * const vout_resets[] = {
> +	"axi",
> +	"ahb",
> +	"core",
> +};
> +
> +static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb)
> +{
> +	u8 f = FORMAT_A8R8G8B8;
> +
> +	switch (format) {
> +	case DRM_FORMAT_XRGB4444:
> +	case DRM_FORMAT_RGBX4444:
> +	case DRM_FORMAT_XBGR4444:
> +	case DRM_FORMAT_BGRX4444:
> +		f = FORMAT_X4R4G4B4;
> +		break;
> +	case DRM_FORMAT_ARGB4444:
> +	case DRM_FORMAT_RGBA4444:
> +	case DRM_FORMAT_ABGR4444:
> +	case DRM_FORMAT_BGRA4444:
> +		f = FORMAT_A4R4G4B4;
> +		break;
> +	case DRM_FORMAT_XRGB1555:
> +	case DRM_FORMAT_RGBX5551:
> +	case DRM_FORMAT_XBGR1555:
> +	case DRM_FORMAT_BGRX5551:
> +		f = FORMAT_X1R5G5B5;
> +		break;
> +	case DRM_FORMAT_ARGB1555:
> +	case DRM_FORMAT_RGBA5551:
> +	case DRM_FORMAT_ABGR1555:
> +	case DRM_FORMAT_BGRA5551:
> +		f = FORMAT_A1R5G5B5;
> +		break;
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		f = FORMAT_R5G6B5;
> +		break;
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_RGBX8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_BGRX8888:
> +		f = FORMAT_X8R8G8B8;
> +		break;
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_RGBA8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_BGRA8888:
> +		f = FORMAT_A8R8G8B8;
> +		break;
> +	case DRM_FORMAT_YUYV:
> +	case DRM_FORMAT_YVYU:
> +		f = FORMAT_YUY2;
> +		break;
> +	case DRM_FORMAT_UYVY:
> +	case DRM_FORMAT_VYUY:
> +		f = FORMAT_UYVY;
> +		break;
> +	case DRM_FORMAT_YUV420:
> +	case DRM_FORMAT_YVU420:
> +		f = FORMAT_YV12;
> +		break;
> +	case DRM_FORMAT_NV21:
> +		f = FORMAT_NV12;
> +		break;
> +	case DRM_FORMAT_NV16:
> +	case DRM_FORMAT_NV61:
> +		f = FORMAT_NV16;
> +		break;
> +	case DRM_FORMAT_P010:
> +		f = FORMAT_P010;
> +		break;
> +	case DRM_FORMAT_ARGB2101010:
> +	case DRM_FORMAT_RGBA1010102:
> +	case DRM_FORMAT_ABGR2101010:
> +	case DRM_FORMAT_BGRA1010102:
> +		f = FORMAT_A2R10G10B10;
> +		break;
> +	case DRM_FORMAT_NV12:
> +		f = FORMAT_NV12;
> +		break;
> +	case DRM_FORMAT_YUV444:
> +		f = FORMAT_YUV444;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	fb->format = f;
> +}
> +
> +static inline void update_swizzle(u32 format, struct dc_hw_fb *fb)
> +{
> +	fb->swizzle = SWIZZLE_ARGB;
> +	fb->uv_swizzle = 0;
> +
> +	switch (format) {
> +	case DRM_FORMAT_RGBX4444:
> +	case DRM_FORMAT_RGBA4444:
> +	case DRM_FORMAT_RGBX5551:
> +	case DRM_FORMAT_RGBA5551:
> +	case DRM_FORMAT_RGBX8888:
> +	case DRM_FORMAT_RGBA8888:
> +	case DRM_FORMAT_RGBA1010102:
> +		fb->swizzle = SWIZZLE_RGBA;
> +		break;
> +	case DRM_FORMAT_XBGR4444:
> +	case DRM_FORMAT_ABGR4444:
> +	case DRM_FORMAT_XBGR1555:
> +	case DRM_FORMAT_ABGR1555:
> +	case DRM_FORMAT_BGR565:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_ABGR2101010:
> +		fb->swizzle = SWIZZLE_ABGR;
> +		break;
> +	case DRM_FORMAT_BGRX4444:
> +	case DRM_FORMAT_BGRA4444:
> +	case DRM_FORMAT_BGRX5551:
> +	case DRM_FORMAT_BGRA5551:
> +	case DRM_FORMAT_BGRX8888:
> +	case DRM_FORMAT_BGRA8888:
> +	case DRM_FORMAT_BGRA1010102:
> +		fb->swizzle = SWIZZLE_BGRA;
> +		break;
> +	case DRM_FORMAT_YVYU:
> +	case DRM_FORMAT_VYUY:
> +	case DRM_FORMAT_NV21:
> +	case DRM_FORMAT_NV61:
> +		fb->uv_swizzle = 1;
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +static inline void update_watermark(struct drm_property_blob *watermark,
> +				    struct dc_hw_fb *fb)
> +{
> +	struct drm_vs_watermark *data;
> +
> +	fb->water_mark = 0;
> +
> +	if (watermark) {
> +		data = watermark->data;
> +		fb->water_mark = data->watermark & 0xFFFFF;
> +	}
> +}
> +
> +static inline u8 to_vs_rotation(unsigned int rotation)
> +{
> +	u8 rot;
> +
> +	switch (rotation & DRM_MODE_REFLECT_MASK) {
> +	case DRM_MODE_REFLECT_X:
> +		rot = FLIP_X;
> +		return rot;
> +	case DRM_MODE_REFLECT_Y:
> +		rot = FLIP_Y;
> +		return rot;
> +	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
> +		rot = FLIP_XY;
> +		return rot;
> +	default:
> +		break;
> +	}
> +
> +	switch (rotation & DRM_MODE_ROTATE_MASK) {
> +	case DRM_MODE_ROTATE_0:
> +		rot = ROT_0;
> +		break;
> +	case DRM_MODE_ROTATE_90:
> +		rot = ROT_90;
> +		break;
> +	case DRM_MODE_ROTATE_180:
> +		rot = ROT_180;
> +		break;
> +	case DRM_MODE_ROTATE_270:
> +		rot = ROT_270;
> +		break;
> +	default:
> +		rot = ROT_0;
> +		break;
> +	}
> +
> +	return rot;
> +}
> +
> +static inline u8 to_vs_yuv_color_space(u32 color_space)
> +{
> +	u8 cs;
> +
> +	switch (color_space) {
> +	case DRM_COLOR_YCBCR_BT601:
> +		cs = COLOR_SPACE_601;
> +		break;
> +	case DRM_COLOR_YCBCR_BT709:
> +		cs = COLOR_SPACE_709;
> +		break;
> +	case DRM_COLOR_YCBCR_BT2020:
> +		cs = COLOR_SPACE_2020;
> +		break;
> +	default:
> +		cs = COLOR_SPACE_601;
> +		break;
> +	}
> +
> +	return cs;
> +}
> +
> +static inline u8 to_vs_tile_mode(u64 modifier)
> +{
> +	if (modifier == DRM_FORMAT_MOD_VIVANTE_TILED)
> +		return DC_TILE_MODE4X4;
> +
> +	return (u8)(modifier & DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK);
> +}
> +
> +static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +	u8 panel_num = dc->hw.info->panel_num;
> +	u32 index = drm_crtc_index(crtc);
> +	int i;
> +
> +	for (i = 0; i < panel_num; i++) {
> +		if (index == dc->crtc[i]->base.index)
> +			return i;
> +	}
> +
> +	return 0;
> +}
> +
> +static void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
> +					  unsigned int alignment)
> +{
> +	struct vs_drm_device *priv = to_vs_drm_private(drm_dev);
> +
> +	if (alignment > priv->pitch_alignment)
> +		priv->pitch_alignment = alignment;
> +}
> +
> +static int plda_clk_rst_init(struct device *dev)
> +{
> +	int ret = 0;
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	ret = clk_bulk_prepare_enable(dc->nclks, dc->clk_vout);
> +	if (ret) {
> +		dev_err(dev, "failed to enable clocks\n");
> +		return ret;
> +	}
> +
> +	ret = reset_control_bulk_deassert(dc->nrsts, dc->rst_vout);
> +	return ret;
> +}
> +
> +static void plda_clk_rst_deinit(struct device *dev)
> +{
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	reset_control_bulk_assert(dc->nrsts, dc->rst_vout);
> +	clk_bulk_disable_unprepare(dc->nclks, dc->clk_vout);
> +}
> +
> +static void dc_deinit(struct device *dev)
> +{
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	dc_hw_enable_interrupt(&dc->hw, 0);
> +	dc_hw_deinit(&dc->hw);
> +	plda_clk_rst_deinit(dev);
> +}
> +
> +static int dc_init(struct device *dev)
> +{
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +	int ret;
> +
> +	dc->first_frame = true;
> +
> +	ret = plda_clk_rst_init(dev);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to init dc clk reset: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = dc_hw_init(&dc->hw);
> +	if (ret) {
> +		dev_err(dev, "failed to init DC HW\n");
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +	struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	struct dc_hw_display display;
> +
> +	display.bus_format = crtc_state->output_fmt;
> +	display.h_active = mode->hdisplay;
> +	display.h_total = mode->htotal;
> +	display.h_sync_start = mode->hsync_start;
> +	display.h_sync_end = mode->hsync_end;
> +	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> +		display.h_sync_polarity = true;
> +	else
> +		display.h_sync_polarity = false;
> +
> +	display.v_active = mode->vdisplay;
> +	display.v_total = mode->vtotal;
> +	display.v_sync_start = mode->vsync_start;
> +	display.v_sync_end = mode->vsync_end;
> +
> +	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> +		display.v_sync_polarity = true;
> +	else
> +		display.v_sync_polarity = false;
> +
> +	display.id = to_vs_display_id(dc, crtc);
> +
> +	display.enable = true;
> +
> +	if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) {
> +		dc_hw_set_out(&dc->hw, OUT_DPI, display.id);
> +		clk_set_rate(dc->clk_vout[CLK_VOUT_SOC_PIX].clk, mode->clock * 1000);
> +		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX1].clk,
> +			       dc->clk_vout[CLK_VOUT_SOC_PIX].clk);
> +	} else {
> +		dc_hw_set_out(&dc->hw, OUT_DP, display.id);
> +		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX0].clk,
> +			       dc->clk_vout[CLK_VOUT_HDMI_PIX].clk);
> +	}
> +
> +	dc_hw_setup_display(&dc->hw, &display);
> +}
> +
> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +	struct dc_hw_display display;
> +
> +	display.id = to_vs_display_id(dc, crtc);
> +	display.enable = false;
> +
> +	dc_hw_setup_display(&dc->hw, &display);
> +}
> +
> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> +		     struct drm_color_lut *lut, unsigned int size)
> +{
> +	u16 i, r, g, b;
> +	u8 bits, id;
> +
> +	if (size != dc->hw.info->gamma_size) {
> +		drm_err(crtc->dev, "gamma size does not match!\n");
> +		return;
> +	}
> +
> +	id = to_vs_display_id(dc, crtc);
> +
> +	bits = dc->hw.info->gamma_bits;
> +	for (i = 0; i < size; i++) {
> +		r = drm_color_lut_extract(lut[i].red, bits);
> +		g = drm_color_lut_extract(lut[i].green, bits);
> +		b = drm_color_lut_extract(lut[i].blue, bits);
> +		dc_hw_update_gamma(&dc->hw, id, i, r, g, b);
> +	}
> +}
> +
> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> +			bool enable)
> +{
> +	u8 id;
> +
> +	id = to_vs_display_id(dc, crtc);
> +	dc_hw_enable_gamma(&dc->hw, id, enable);
> +}
> +
> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable)
> +{
> +	dc_hw_enable_interrupt(&dc->hw, enable);
> +}
> +
> +static u32 calc_factor(u32 src, u32 dest)
> +{
> +	u32 factor = 1 << 16;
> +
> +	if (src > 1 && dest > 1)
> +		factor = ((src - 1) << 16) / (dest - 1);
> +
> +	return factor;
> +}
> +
> +static void update_scale(struct drm_plane_state *state, struct dc_hw_roi *roi,
> +			 struct dc_hw_scale *scale)
> +{
> +	int dst_w = drm_rect_width(&state->dst);
> +	int dst_h = drm_rect_height(&state->dst);
> +	int src_w, src_h, temp;
> +
> +	scale->enable = false;
> +
> +	if (roi->enable) {
> +		src_w = roi->width;
> +		src_h = roi->height;
> +	} else {
> +		src_w = drm_rect_width(&state->src) >> 16;
> +		src_h = drm_rect_height(&state->src) >> 16;
> +	}
> +
> +	if (drm_rotation_90_or_270(state->rotation)) {
> +		temp = src_w;
> +		src_w = src_h;
> +		src_h = temp;
> +	}
> +
> +	if (src_w != dst_w) {
> +		scale->scale_factor_x = calc_factor(src_w, dst_w);
> +		scale->enable = true;
> +	} else {
> +		scale->scale_factor_x = 1 << 16;
> +	}
> +	if (src_h != dst_h) {
> +		scale->scale_factor_y = calc_factor(src_h, dst_h);
> +		scale->enable = true;
> +	} else {
> +		scale->scale_factor_y = 1 << 16;
> +	}
> +}
> +
> +static void update_fb(struct vs_plane *plane, u8 display_id,
> +		      struct dc_hw_fb *fb, struct drm_plane_state *state)
> +{
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_framebuffer *drm_fb = state->fb;
> +	struct drm_rect *src = &state->src;
> +
> +	fb->display_id = display_id;
> +	fb->y_address = plane_state->dma_addr[0];
> +	fb->y_stride = drm_fb->pitches[0];
> +	if (drm_fb->format->format == DRM_FORMAT_YVU420) {
> +		fb->u_address = plane_state->dma_addr[2];
> +		fb->v_address = plane_state->dma_addr[1];
> +		fb->u_stride = drm_fb->pitches[2];
> +		fb->v_stride = drm_fb->pitches[1];
> +	} else {
> +		fb->u_address = plane_state->dma_addr[1];
> +		fb->v_address = plane_state->dma_addr[2];
> +		fb->u_stride = drm_fb->pitches[1];
> +		fb->v_stride = drm_fb->pitches[2];
> +	}
> +	fb->width = drm_rect_width(src) >> 16;
> +	fb->height = drm_rect_height(src) >> 16;
> +	fb->tile_mode = to_vs_tile_mode(drm_fb->modifier);
> +	fb->rotation = to_vs_rotation(state->rotation);
> +	fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding);
> +	fb->zpos = state->zpos;
> +	fb->enable = state->visible;
> +	update_format(drm_fb->format->format, drm_fb->modifier, fb);
> +	update_swizzle(drm_fb->format->format, fb);
> +	update_watermark(plane_state->watermark, fb);
> +	plane_state->status.tile_mode = fb->tile_mode;
> +}
> +
> +static void update_degamma(struct vs_dc *dc, struct vs_plane *plane,
> +			   struct vs_plane_state *plane_state)
> +{
> +	dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
> +	plane_state->degamma_changed = false;
> +}
> +
> +static void update_roi(struct vs_dc *dc, u8 id,
> +		       struct vs_plane_state *plane_state,
> +		       struct dc_hw_roi *roi,
> +		       struct drm_plane_state *state)
> +{
> +	struct drm_vs_roi *data;
> +	struct drm_rect *src = &state->src;
> +	u16 src_w = drm_rect_width(src) >> 16;
> +	u16 src_h = drm_rect_height(src) >> 16;
> +
> +	if (plane_state->roi) {
> +		data = plane_state->roi->data;
> +
> +		if (data->enable) {
> +			roi->x = data->roi_x;
> +			roi->y = data->roi_y;
> +			roi->width = (data->roi_x + data->roi_w > src_w) ?
> +						 (src_w - data->roi_x) : data->roi_w;
> +			roi->height = (data->roi_y + data->roi_h > src_h) ?
> +						  (src_h - data->roi_y) : data->roi_h;
> +			roi->enable = true;
> +		} else {
> +			roi->enable = false;
> +		}
> +
> +		dc_hw_update_roi(&dc->hw, id, roi);
> +	} else {
> +		roi->enable = false;
> +	}
> +}
> +
> +static void update_color_mgmt(struct vs_dc *dc, u8 id,
> +			      struct dc_hw_fb *fb,
> +			      struct vs_plane_state *plane_state)
> +{
> +	struct drm_vs_color_mgmt *data;
> +	struct dc_hw_colorkey colorkey;
> +
> +	if (plane_state->color_mgmt) {
> +		data = plane_state->color_mgmt->data;
> +
> +		fb->clear_enable = data->clear_enable;
> +		fb->clear_value = data->clear_value;
> +
> +		if (data->colorkey > data->colorkey_high)
> +			data->colorkey = data->colorkey_high;
> +
> +		colorkey.colorkey = data->colorkey;
> +		colorkey.colorkey_high = data->colorkey_high;
> +		colorkey.transparency = (data->transparency) ?
> +				DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE;
> +		dc_hw_update_colorkey(&dc->hw, id, &colorkey);
> +	}
> +}
> +
> +static void update_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			 struct drm_plane *drm_plane,
> +			 struct drm_atomic_state *drm_state)
> +{
> +	struct dc_hw_fb fb = {0};
> +	struct dc_hw_scale scale;
> +	struct dc_hw_position pos;
> +	struct dc_hw_blend blend;
> +	struct dc_hw_roi roi;
> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> +									   drm_plane);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_rect *dest = &state->dst;
> +	bool dec_enable = false;
> +	u8 display_id = 0;
> +
> +	display_id = to_vs_display_id(dc, state->crtc);
> +	update_fb(plane, display_id, &fb, state);
> +	fb.dec_enable = dec_enable;
> +
> +	update_roi(dc, plane->id, plane_state, &roi, state);
> +
> +	update_scale(state, &roi, &scale);
> +
> +	if (plane_state->degamma_changed)
> +		update_degamma(dc, plane, plane_state);
> +
> +	pos.start_x = dest->x1;
> +	pos.start_y = dest->y1;
> +	pos.end_x = dest->x2;
> +	pos.end_y = dest->y2;
> +
> +	blend.alpha = (u8)(state->alpha >> 8);
> +	blend.blend_mode = (u8)(state->pixel_blend_mode);
> +
> +	update_color_mgmt(dc, plane->id, &fb, plane_state);
> +
> +	dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
> +}
> +
> +static void update_qos(struct vs_dc *dc, struct vs_plane *plane,
> +		       struct drm_plane *drm_plane,
> +		       struct drm_atomic_state *drm_state)
> +{
> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> +									   drm_plane);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_vs_watermark *data;
> +	struct dc_hw_qos qos;
> +
> +	if (plane_state->watermark) {
> +		data = plane_state->watermark->data;
> +
> +		if (data->qos_high) {
> +			if (data->qos_low > data->qos_high)
> +				data->qos_low = data->qos_high;
> +
> +			qos.low_value = data->qos_low & 0x0F;
> +			qos.high_value = data->qos_high & 0x0F;
> +			dc_hw_update_qos(&dc->hw, &qos);
> +		}
> +	}
> +}
> +
> +static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor)
> +{
> +	u8 size_type;
> +
> +	switch (state->crtc_w) {
> +	case 32:
> +		size_type = CURSOR_SIZE_32X32;
> +		break;
> +	case 64:
> +		size_type = CURSOR_SIZE_64X64;
> +		break;
> +	default:
> +		size_type = CURSOR_SIZE_32X32;
> +		break;
> +	}
> +
> +	cursor->size = size_type;
> +}
> +
> +static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +				struct drm_plane *drm_plane,
> +				struct drm_atomic_state *drm_state)
> +{
> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> +								       drm_plane);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_framebuffer *drm_fb = state->fb;
> +	struct dc_hw_cursor cursor;
> +
> +	cursor.address = plane_state->dma_addr[0];
> +	cursor.x = state->crtc_x;
> +	cursor.y = state->crtc_y;
> +	cursor.hot_x = drm_fb->hot_x;
> +	cursor.hot_y = drm_fb->hot_y;
> +	cursor.display_id = to_vs_display_id(dc, state->crtc);
> +	update_cursor_size(state, &cursor);
> +	cursor.enable = true;
> +
> +	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
> +}
> +
> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			struct drm_plane *drm_plane,
> +			struct drm_atomic_state *drm_state)
> +{
> +	update_plane(dc, plane, drm_plane, drm_state);
> +	update_qos(dc, plane, drm_plane, drm_state);
> +}
> +
> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			       struct drm_plane *drm_plane,
> +			       struct drm_atomic_state *drm_state)
> +{
> +	update_cursor_plane(dc, plane, drm_plane, drm_state);
> +}
> +
> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			 struct drm_plane_state *old_state)
> +{
> +	struct dc_hw_fb fb = {0};
> +
> +	fb.enable = false;
> +	dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL, NULL, NULL);
> +}
> +
> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +				struct drm_plane_state *old_state)
> +{
> +	struct dc_hw_cursor cursor = {0};
> +
> +	cursor.enable = false;
> +	cursor.display_id = to_vs_display_id(dc, old_state->crtc);
> +	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
> +}
> +
> +static bool vs_dc_mod_supported(const struct vs_plane_info *plane_info,
> +				u64 modifier)
> +{
> +	const u64 *mods;
> +
> +	if (!plane_info->modifiers)
> +		return false;
> +
> +	for (mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID; mods++) {
> +		if (*mods == modifier)
> +			return true;
> +	}
> +
> +	return false;
> +}
> +
> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
> +		      struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> +										 plane);
> +
> +	struct drm_framebuffer *fb = new_plane_state->fb;
> +	const struct vs_plane_info *plane_info;
> +	struct drm_crtc *crtc = new_plane_state->crtc;
> +	struct drm_crtc_state *crtc_state;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +
> +	plane_info = &dc->hw.info->planes[vs_plane->id];
> +
> +	if (fb->width < plane_info->min_width ||
> +	    fb->width > plane_info->max_width ||
> +	    fb->height < plane_info->min_height ||
> +	    fb->height > plane_info->max_height)
> +		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n",
> +			     vs_plane->id);
> +
> +	if (!vs_dc_mod_supported(plane_info, fb->modifier)) {
> +		drm_err(plane->dev, "unsupported modifier on plane%d.\n", vs_plane->id);
> +		return -EINVAL;
> +	}
> +
> +	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
> +	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
> +						  plane_info->min_scale,
> +						  plane_info->max_scale,
> +						  true, true);
> +}
> +
> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
> +			     struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> +									   plane);
> +	struct drm_framebuffer *fb = new_plane_state->fb;
> +	const struct vs_plane_info *plane_info;
> +	struct drm_crtc *crtc = new_plane_state->crtc;
> +	struct drm_crtc_state *crtc_state;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +
> +	plane_info = &dc->hw.info->planes[vs_plane->id];
> +
> +	if (fb->width < plane_info->min_width ||
> +	    fb->width > plane_info->max_width ||
> +	    fb->height < plane_info->min_height ||
> +	    fb->height > plane_info->max_height)
> +		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n", vs_plane->id);
> +
> +	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
> +	if (IS_ERR(crtc_state))
> +		return -EINVAL;
> +
> +	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
> +						plane_info->min_scale,
> +						plane_info->max_scale,
> +						true, true);
> +}
> +
> +static void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
> +{
> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
> +
> +	drm_crtc_handle_vblank(crtc);
> +
> +	vs_crtc_state->underflow = underflow;
> +}
> +
> +static irqreturn_t dc_isr(int irq, void *data)
> +{
> +	struct vs_dc *dc = data;
> +	struct vs_dc_info *dc_info = dc->hw.info;
> +	u32 i, ret;
> +
> +	ret = dc_hw_get_interrupt(&dc->hw);
> +
> +	for (i = 0; i < dc_info->panel_num; i++)
> +		vs_crtc_handle_vblank(&dc->crtc[i]->base, dc_hw_check_underflow(&dc->hw));
> +
> +	return IRQ_HANDLED;
> +}
> +
> +void vs_dc_commit(struct vs_dc *dc)
> +{
> +	dc_hw_enable_shadow_register(&dc->hw, false);
> +
> +	dc_hw_commit(&dc->hw);
> +
> +	if (dc->first_frame)
> +		dc->first_frame = false;
> +
> +	dc_hw_enable_shadow_register(&dc->hw, true);
> +}
> +
> +static int dc_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct drm_device *drm_dev = data;
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +	struct device_node *port;
> +	struct vs_crtc *crtc;
> +	struct vs_dc_info *dc_info;
> +	struct vs_plane *plane;
> +	struct vs_plane_info *plane_info;
> +	int i, ret;
> +	u32 ctrc_mask = 0;
> +
> +	if (!drm_dev || !dc) {
> +		dev_err(dev, "devices are not created.\n");
> +		return -ENODEV;
> +	}
> +
> +	ret = dc_init(dev);
> +	if (ret < 0) {
> +		drm_err(drm_dev, "Failed to initialize DC hardware.\n");
> +		return ret;
> +	}
> +
> +	port = of_get_child_by_name(dev->of_node, "port");
> +	if (!port) {
> +		drm_err(drm_dev, "no port node found\n");
> +		return -ENODEV;
> +	}
> +	of_node_put(port);
> +
> +	dc_info = dc->hw.info;
> +
> +	for (i = 0; i < dc_info->panel_num; i++) {
> +		crtc = vs_crtc_create(drm_dev, dc_info);
> +		if (!crtc) {
> +			drm_err(drm_dev, "Failed to create CRTC.\n");
> +			ret = -ENOMEM;
> +			return ret;
> +		}
> +
> +		crtc->base.port = port;
> +		crtc->dev = dev;
> +		dc->crtc[i] = crtc;
> +		ctrc_mask |= drm_crtc_mask(&crtc->base);
> +	}
> +
> +	for (i = 0; i < dc_info->plane_num; i++) {
> +		plane_info = (struct vs_plane_info *)&dc_info->planes[i];
> +
> +		if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
> +						drm_crtc_mask(&dc->crtc[0]->base));
> +		} else if (!strcmp(plane_info->name, "Primary_1") ||
> +				   !strcmp(plane_info->name, "Cursor_1")) {
> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
> +						drm_crtc_mask(&dc->crtc[1]->base));

Do you somehow verify that dc->crtc[0] and dc->crtc[1] are not NULL here?

> +		} else {
> +			plane = vs_plane_create(drm_dev, plane_info,
> +						dc_info->layer_num, ctrc_mask);
> +		}

In addition to Maxime's comment, I think it would be easier to read if 
you only set the plane's crtc make in these if-else branches and then 
call vs_plane_create() with the result. Like this

   plane_crtc_mask = crtc_mask;

   if (primary || cursor)
     plane_crtc_mask = drm_crtc_mask(&dc->crtc[0]->base)
   else if (primary_1 || cursor_1)
     plane_crtc_mask = drm_crtc_mask(&dc->crtc[1]->base)

   vs_plane_create(..., plane_crtc_mask)

> +
> +		if (IS_ERR(plane)) {
> +			dev_err(dev, "failed to construct plane\n");
> +			return PTR_ERR(plane);
> +		}
> +
> +		plane->id = i;
> +		dc->planes[i].id = plane_info->id;
> +
> +		if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
> +			if (!strcmp(plane_info->name, "Primary"))
> +				dc->crtc[0]->base.primary = &plane->base;
> +			else
> +				dc->crtc[1]->base.primary = &plane->base;
> +			drm_dev->mode_config.min_width = plane_info->min_width;
> +			drm_dev->mode_config.min_height =
> +							plane_info->min_height;
> +			drm_dev->mode_config.max_width = plane_info->max_width;
> +			drm_dev->mode_config.max_height =
> +							plane_info->max_height;
> +		}

If you have multiple primary planes, the later planes overwrite the 
settings from the earlier ones. Is that intentional?

> +
> +		if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
> +			if (!strcmp(plane_info->name, "Cursor"))
> +				dc->crtc[0]->base.cursor = &plane->base;
> +			else
> +				dc->crtc[1]->base.cursor = &plane->base;
> +			drm_dev->mode_config.cursor_width =
> +							plane_info->max_width;
> +			drm_dev->mode_config.cursor_height =
> +							plane_info->max_height;
> +		}

Same question about overwriting as for the primary planes?

> +	}
> +
> +	vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
> +	return 0;
> +}
> +
> +static void dc_unbind(struct device *dev, struct device *master, void *data)
> +{
> +	dc_deinit(dev);
> +}
> +
> +const struct component_ops dc_component_ops = {
> +	.bind = dc_bind,
> +	.unbind = dc_unbind,
> +};
> +
> +static const struct of_device_id dc_driver_dt_match[] = {
> +	{ .compatible = "starfive,jh7110-dc8200", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
> +
> +static int dc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct vs_dc *dc;
> +	int irq, ret, i;
> +
> +	dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
> +	if (!dc)
> +		return -ENOMEM;
> +
> +	dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(dc->hw.hi_base))
> +		return PTR_ERR(dc->hw.hi_base);
> +
> +	dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
> +	if (IS_ERR(dc->hw.reg_base))
> +		return PTR_ERR(dc->hw.reg_base);
> +
> +	dc->nclks = ARRAY_SIZE(dc->clk_vout);
> +	for (i = 0; i < dc->nclks; ++i)
> +		dc->clk_vout[i].id = vout_clocks[i];
> +	ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
> +	if (ret) {
> +		dev_err(dev, "Failed to get clk controls\n");
> +		return ret;
> +	}
> +
> +	dc->nrsts = ARRAY_SIZE(dc->rst_vout);
> +	for (i = 0; i < dc->nrsts; ++i)
> +		dc->rst_vout[i].id = vout_resets[i];
> +	ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
> +						 dc->rst_vout);
> +	if (ret) {
> +		dev_err(dev, "Failed to get reset controls\n");
> +		return ret;
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +
> +	ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to install irq:%u.\n", irq);
> +		return ret;
> +	}
> +
> +	dev_set_drvdata(dev, dc);
> +
> +	return component_add(dev, &dc_component_ops);
> +}
> +
> +static int dc_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +
> +	component_del(dev, &dc_component_ops);
> +
> +	dev_set_drvdata(dev, NULL);
> +
> +	return 0;
> +}
> +
> +struct platform_driver dc_platform_driver = {
> +	.probe = dc_probe,
> +	.remove = dc_remove,
> +	.driver = {
> +		.name = "vs-dc",
> +		.of_match_table = of_match_ptr(dc_driver_dt_match),
> +	},
> +};
> +
> +MODULE_AUTHOR("StarFive Corporation");
> +MODULE_DESCRIPTION("VeriSilicon DC Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
> new file mode 100644
> index 000000000..8e96fd32c
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc.h
> @@ -0,0 +1,80 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DC_H__
> +#define __VS_DC_H__
> +
> +#include <linux/clk.h>
> +#include <linux/mm_types.h>
> +#include <linux/reset.h>
> +
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_modes.h>
> +
> +#include "vs_crtc.h"
> +#include "vs_dc_hw.h"
> +#include "vs_plane.h"
> +
> +struct vs_dc_plane {
> +	enum dc_hw_plane_id id;
> +};
> +
> +enum vout_clk {
> +	CLK_VOUT_NOC_DISP = 0,
> +	CLK_VOUT_PIX0,
> +	CLK_VOUT_PIX1,
> +	CLK_VOUT_CORE,
> +	CLK_VOUT_AXI,
> +	CLK_VOUT_AHB,
> +	CLK_VOUT_HDMI_PIX,
> +	CLK_VOUT_SOC_PIX,
> +	CLK_VOUT_NUM
> +};
> +
> +enum rst_vout {
> +	RST_VOUT_AXI = 0,
> +	RST_VOUT_AHB,
> +	RST_VOUT_CORE,
> +	RST_VOUT_NUM
> +};
> +
> +struct vs_dc {
> +	struct vs_crtc		*crtc[DC_DISPLAY_NUM];
> +	struct dc_hw		hw;
> +	bool			first_frame;
> +
> +	struct vs_dc_plane	planes[PLANE_NUM];
> +	struct clk_bulk_data	clk_vout[CLK_VOUT_NUM];
> +	int			nclks;
> +	struct reset_control_bulk_data rst_vout[RST_VOUT_NUM];
> +	int			nrsts;
> +};
> +
> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc);
> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc);
> +
> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> +		     struct drm_color_lut *lut, unsigned int size);
> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc, bool enable);
> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable);
> +void vs_dc_commit(struct vs_dc *dc);
> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			struct drm_plane *drm_plane,
> +			struct drm_atomic_state *drm_state);
> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			 struct drm_plane_state *old_state);
> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
> +		      struct drm_atomic_state *state);
> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			       struct drm_plane *drm_plane,
> +			       struct drm_atomic_state *drm_state);
> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +				struct drm_plane_state *old_state);
> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
> +			     struct drm_atomic_state *state);
> +
> +extern struct platform_driver dc_platform_driver;
> +
> +#endif /* __VS_DC_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
> new file mode 100644
> index 000000000..e5acb7eb4
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
> @@ -0,0 +1,1959 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/bits.h>
> +#include <linux/io.h>
> +#include <linux/media-bus-format.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_blend.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/vs_drm.h>
> +
> +#include "vs_dc_hw.h"
> +#include "vs_type.h"
> +
> +static const u32 horkernel[] = {
> +	0x00000000, 0x20000000, 0x00002000, 0x00000000,
> +	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
> +	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
> +	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
> +	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
> +	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
> +	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
> +	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
> +	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
> +	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
> +	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
> +	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
> +	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
> +	0x00000000, 0x00000000, 0x00000000, 0x01470000,
> +	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
> +	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
> +	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
> +	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
> +	0x00000000, 0x00000000, 0x00004000, 0x00000000,
> +	0x00000000, 0x00000000, 0x20002000, 0x00000000,
> +	0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
> +	0x00000000, 0x00000000, 0x00000000, 0x27e1181f,
> +	0x00000000, 0x00000000, 0x00000000, 0x14680000,
> +	0x00002b98, 0x00000000, 0x00000000, 0x00000000,
> +	0x2f1010f0, 0x00000000, 0x00000000, 0x00000000,
> +	0x0dc70000, 0x00003239, 0x00000000, 0x00000000,
> +	0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
> +	0x00000000, 0x087f0000, 0x00003781, 0x00000000,
> +	0x00000000, 0x00000000, 0x399a0666, 0x00000000,
> +	0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
> +	0x00000000, 0x00000000, 0x00000000, 0x3cc4033c,
> +	0x00000000, 0x00000000, 0x00000000, 0x021f0000,
> +};
> +
> +#define H_COEF_SIZE (sizeof(horkernel) / sizeof(u32))

Either drop this in favor of the regular ARRAY_SIZE() macro, or hardcode 
the size if it's a fixed hardware setting.

> +
> +static const u32 verkernel[] = {
> +	0x00000000, 0x20000000, 0x00002000, 0x00000000,
> +	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
> +	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
> +	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
> +	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
> +	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
> +	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
> +	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
> +	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
> +	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
> +	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
> +	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
> +	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
> +	0x00000000, 0x00000000, 0x00000000, 0x01470000,
> +	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
> +	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
> +	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
> +	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
> +	0x00000000, 0x00000000, 0x00004000, 0x00000000,
> +	0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab,
> +	0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
> +	0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +};
> +
> +#define V_COEF_SIZE (sizeof(verkernel) / sizeof(u32))

Same as H_COEF_SIZE;

> +
> +/*
> + * RGB 709->2020 conversion parameters
> + */
> +static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = {

'static const' here and for the arrays below.

> +	10279,	5395,	709,
> +	1132,	15065,	187,
> +	269,	1442,	14674
> +};
> +
> +/*
> + * YUV601 to RGB conversion parameters
> + * YUV2RGB[0]  - [8] : C0 - C8;
> + * YUV2RGB[9]  - [11]: D0 - D2;
> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> + */
> +static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> +	1196,	0,			1640,	1196,
> +	-404,	-836,		1196,	2076,
> +	0,		-916224,	558336,	-1202944,
> +	64,		 940,		64,		960

Might be my email reader, but indention looks off here.

> +};
> +
> +/*
> + * YUV709 to RGB conversion parameters
> + * YUV2RGB[0]  - [8] : C0 - C8;
> + * YUV2RGB[9]  - [11]: D0 - D2;
> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> + */
> +static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> +	1196,		0,		1844,	1196,
> +	-220,		-548,	1196,	2172,
> +	0,			-1020672, 316672,  -1188608,
> +	64,			940,		64,		960

Indention?

> +};
> +
> +/*
> + * YUV2020 to RGB conversion parameters
> + * YUV2RGB[0]  - [8] : C0 - C8;
> + * YUV2RGB[9]  - [11]: D0 - D2;
> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> + */
> +static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> +	1196, 0, 1724, 1196,
> +	-192, -668, 1196, 2200,
> +	0, -959232, 363776, -1202944,
> +	64, 940, 64, 960

Indention.

> +};
> +
> +/*
> + * RGB to YUV2020 conversion parameters
> + * RGB2YUV[0] - [8] : C0 - C8;
> + * RGB2YUV[9] - [11]: D0 - D2;
> + */
> +static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = {
> +	230,	594,	52,
> +	-125,  -323,	448,
> +	448,   -412,   -36,
> +	64,		512,	512
> +};
> +
> +/*
> + * Degamma table for 709 color space data.
> + */
> +static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005,
> +	0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024,
> +	0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064,
> +	0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce,
> +	0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166,
> +	0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230,
> +	0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331,
> +	0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c,
> +	0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
> +	0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d,
> +	0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999,
> +	0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb,
> +	0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65,
> +	0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a,
> +	0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b,
> +	0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc,
> +	0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e,
> +	0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
> +	0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e,
> +	0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf,
> +	0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea,
> +	0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f,
> +	0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930,
> +	0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f,
> +	0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee,
> +	0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde,
> +	0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
> +	0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9,
> +	0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406,
> +	0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a,
> +	0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576,
> +	0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd,
> +	0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
> +};
> +
> +/*
> + * Degamma table for 2020 color space data.
> + */
> +static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> +	0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
> +	0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
> +	0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006,
> +	0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a,
> +	0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f,
> +	0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
> +	0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024,
> +	0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035,
> +	0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d,
> +	0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d,
> +	0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098,
> +	0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3,
> +	0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123,
> +	0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d,
> +	0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
> +	0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6,
> +	0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1,
> +	0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d,
> +	0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6,
> +	0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920,
> +	0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a,
> +	0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033,
> +	0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593,
> +	0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
> +	0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656,
> +	0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332,
> +	0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480,
> +	0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1,
> +	0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99,
> +	0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
> +};
> +
> +/* one is for primary plane and the other is for all overlay planes */
> +static const struct dc_hw_plane_reg dc_plane_reg[] = {
> +	{
> +	.y_address		= DC_FRAMEBUFFER_ADDRESS,
> +	.u_address		= DC_FRAMEBUFFER_U_ADDRESS,
> +	.v_address		= DC_FRAMEBUFFER_V_ADDRESS,
> +	.y_stride		= DC_FRAMEBUFFER_STRIDE,
> +	.u_stride		= DC_FRAMEBUFFER_U_STRIDE,
> +	.v_stride		= DC_FRAMEBUFFER_V_STRIDE,
> +	.size			= DC_FRAMEBUFFER_SIZE,
> +	.top_left		= DC_FRAMEBUFFER_TOP_LEFT,
> +	.bottom_right	= DC_FRAMEBUFFER_BOTTOM_RIGHT,
> +	.scale_factor_x			= DC_FRAMEBUFFER_SCALE_FACTOR_X,
> +	.scale_factor_y			= DC_FRAMEBUFFER_SCALE_FACTOR_Y,
> +	.h_filter_coef_index	= DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
> +	.h_filter_coef_data		= DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
> +	.v_filter_coef_index	= DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
> +	.v_filter_coef_data		= DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
> +	.init_offset			= DC_FRAMEBUFFER_INIT_OFFSET,
> +	.color_key				= DC_FRAMEBUFFER_COLOR_KEY,
> +	.color_key_high			= DC_FRAMEBUFFER_COLOR_KEY_HIGH,
> +	.clear_value			= DC_FRAMEBUFFER_CLEAR_VALUE,
> +	.color_table_index		= DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
> +	.color_table_data		= DC_FRAMEBUFFER_COLOR_TABLE_DATA,
> +	.scale_config			= DC_FRAMEBUFFER_SCALE_CONFIG,
> +	.water_mark				= DC_FRAMEBUFFER_WATER_MARK,
> +	.degamma_index			= DC_FRAMEBUFFER_DEGAMMA_INDEX,
> +	.degamma_data			= DC_FRAMEBUFFER_DEGAMMA_DATA,
> +	.degamma_ex_data		= DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
> +	.src_global_color		= DC_FRAMEBUFFER_SRC_GLOBAL_COLOR,
> +	.dst_global_color		= DC_FRAMEBUFFER_DST_GLOBAL_COLOR,
> +	.blend_config			= DC_FRAMEBUFFER_BLEND_CONFIG,
> +	.roi_origin				= DC_FRAMEBUFFER_ROI_ORIGIN,
> +	.roi_size				= DC_FRAMEBUFFER_ROI_SIZE,
> +	.yuv_to_rgb_coef0			= DC_FRAMEBUFFER_YUVTORGB_COEF0,
> +	.yuv_to_rgb_coef1			= DC_FRAMEBUFFER_YUVTORGB_COEF1,
> +	.yuv_to_rgb_coef2			= DC_FRAMEBUFFER_YUVTORGB_COEF2,
> +	.yuv_to_rgb_coef3			= DC_FRAMEBUFFER_YUVTORGB_COEF3,
> +	.yuv_to_rgb_coef4			= DC_FRAMEBUFFER_YUVTORGB_COEF4,
> +	.yuv_to_rgb_coefd0			= DC_FRAMEBUFFER_YUVTORGB_COEFD0,
> +	.yuv_to_rgb_coefd1			= DC_FRAMEBUFFER_YUVTORGB_COEFD1,
> +	.yuv_to_rgb_coefd2			= DC_FRAMEBUFFER_YUVTORGB_COEFD2,
> +	.y_clamp_bound				= DC_FRAMEBUFFER_Y_CLAMP_BOUND,
> +	.uv_clamp_bound				= DC_FRAMEBUFFER_UV_CLAMP_BOUND,
> +	.rgb_to_rgb_coef0			= DC_FRAMEBUFFER_RGBTORGB_COEF0,
> +	.rgb_to_rgb_coef1			= DC_FRAMEBUFFER_RGBTORGB_COEF1,
> +	.rgb_to_rgb_coef2			= DC_FRAMEBUFFER_RGBTORGB_COEF2,
> +	.rgb_to_rgb_coef3			= DC_FRAMEBUFFER_RGBTORGB_COEF3,
> +	.rgb_to_rgb_coef4			= DC_FRAMEBUFFER_RGBTORGB_COEF4,
> +	},
> +	{
> +	.y_address		= DC_OVERLAY_ADDRESS,
> +	.u_address		= DC_OVERLAY_U_ADDRESS,
> +	.v_address		= DC_OVERLAY_V_ADDRESS,
> +	.y_stride		= DC_OVERLAY_STRIDE,
> +	.u_stride		= DC_OVERLAY_U_STRIDE,
> +	.v_stride		= DC_OVERLAY_V_STRIDE,
> +	.size			= DC_OVERLAY_SIZE,
> +	.top_left		= DC_OVERLAY_TOP_LEFT,
> +	.bottom_right	= DC_OVERLAY_BOTTOM_RIGHT,
> +	.scale_factor_x	= DC_OVERLAY_SCALE_FACTOR_X,
> +	.scale_factor_y	= DC_OVERLAY_SCALE_FACTOR_Y,
> +	.h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
> +	.h_filter_coef_data  = DC_OVERLAY_H_FILTER_COEF_DATA,
> +	.v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
> +	.v_filter_coef_data  = DC_OVERLAY_V_FILTER_COEF_DATA,
> +	.init_offset		 = DC_OVERLAY_INIT_OFFSET,
> +	.color_key			 = DC_OVERLAY_COLOR_KEY,
> +	.color_key_high			= DC_OVERLAY_COLOR_KEY_HIGH,
> +	.clear_value		 = DC_OVERLAY_CLEAR_VALUE,
> +	.color_table_index	 = DC_OVERLAY_COLOR_TABLE_INDEX,
> +	.color_table_data	 = DC_OVERLAY_COLOR_TABLE_DATA,
> +	.scale_config		 = DC_OVERLAY_SCALE_CONFIG,
> +	.water_mark				= DC_OVERLAY_WATER_MARK,
> +	.degamma_index		 = DC_OVERLAY_DEGAMMA_INDEX,
> +	.degamma_data		 = DC_OVERLAY_DEGAMMA_DATA,
> +	.degamma_ex_data	 = DC_OVERLAY_DEGAMMA_EX_DATA,
> +	.src_global_color	 = DC_OVERLAY_SRC_GLOBAL_COLOR,
> +	.dst_global_color	 = DC_OVERLAY_DST_GLOBAL_COLOR,
> +	.blend_config		 = DC_OVERLAY_BLEND_CONFIG,
> +	.roi_origin				= DC_OVERLAY_ROI_ORIGIN,
> +	.roi_size				= DC_OVERLAY_ROI_SIZE,
> +	.yuv_to_rgb_coef0		 = DC_OVERLAY_YUVTORGB_COEF0,
> +	.yuv_to_rgb_coef1		 = DC_OVERLAY_YUVTORGB_COEF1,
> +	.yuv_to_rgb_coef2		 = DC_OVERLAY_YUVTORGB_COEF2,
> +	.yuv_to_rgb_coef3		 = DC_OVERLAY_YUVTORGB_COEF3,
> +	.yuv_to_rgb_coef4			= DC_OVERLAY_YUVTORGB_COEF4,
> +	.yuv_to_rgb_coefd0			= DC_OVERLAY_YUVTORGB_COEFD0,
> +	.yuv_to_rgb_coefd1			= DC_OVERLAY_YUVTORGB_COEFD1,
> +	.yuv_to_rgb_coefd2			= DC_OVERLAY_YUVTORGB_COEFD2,
> +	.y_clamp_bound		 = DC_OVERLAY_Y_CLAMP_BOUND,
> +	.uv_clamp_bound		 = DC_OVERLAY_UV_CLAMP_BOUND,
> +	.rgb_to_rgb_coef0		 = DC_OVERLAY_RGBTORGB_COEF0,
> +	.rgb_to_rgb_coef1		 = DC_OVERLAY_RGBTORGB_COEF1,
> +	.rgb_to_rgb_coef2		 = DC_OVERLAY_RGBTORGB_COEF2,
> +	.rgb_to_rgb_coef3		 = DC_OVERLAY_RGBTORGB_COEF3,
> +	.rgb_to_rgb_coef4		 = DC_OVERLAY_RGBTORGB_COEF4,
> +	},
> +};
> +
> +static const u32 primary_overlay_format0[] = {

These format arrays are supposed to be roughly sorted by hardware 
preference. This gives user-space programs an idea of what the hardware 
wants. Are these 4444 and 1555 formats really preferable over their 8888 
and 2101010 variants?

> +	DRM_FORMAT_XRGB4444,
> +	DRM_FORMAT_XBGR4444,
> +	DRM_FORMAT_RGBX4444,
> +	DRM_FORMAT_BGRX4444,
> +	DRM_FORMAT_ARGB4444,
> +	DRM_FORMAT_ABGR4444,
> +	DRM_FORMAT_RGBA4444,
> +	DRM_FORMAT_BGRA4444,
> +	DRM_FORMAT_XRGB1555,
> +	DRM_FORMAT_XBGR1555,
> +	DRM_FORMAT_RGBX5551,
> +	DRM_FORMAT_BGRX5551,
> +	DRM_FORMAT_ARGB1555,
> +	DRM_FORMAT_ABGR1555,
> +	DRM_FORMAT_RGBA5551,
> +	DRM_FORMAT_BGRA5551,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_RGBX8888,
> +	DRM_FORMAT_BGRX8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGBA8888,
> +	DRM_FORMAT_BGRA8888,
> +	DRM_FORMAT_ARGB2101010,
> +	DRM_FORMAT_ABGR2101010,
> +	DRM_FORMAT_RGBA1010102,
> +	DRM_FORMAT_BGRA1010102,
> +	DRM_FORMAT_YUYV,
> +	DRM_FORMAT_YVYU,
> +	DRM_FORMAT_UYVY,
> +	DRM_FORMAT_VYUY,
> +	DRM_FORMAT_YVU420,
> +	DRM_FORMAT_YUV420,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV21,
> +	DRM_FORMAT_NV16,
> +	DRM_FORMAT_NV61,
> +	DRM_FORMAT_P010,
> +};
> +
> +static const u32 primary_overlay_format1[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_RGBX8888,
> +	DRM_FORMAT_BGRX8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGBA8888,
> +	DRM_FORMAT_BGRA8888,
> +	DRM_FORMAT_ARGB2101010,
> +	DRM_FORMAT_ABGR2101010,
> +	DRM_FORMAT_RGBA1010102,
> +	DRM_FORMAT_BGRA1010102,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV21,
> +	DRM_FORMAT_YUV444,
> +};
> +
> +static const u32 cursor_formats[] = {
> +	DRM_FORMAT_ARGB8888
> +};
> +
> +static const u64 format_modifier0[] = {
> +	DRM_FORMAT_MOD_LINEAR,
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X8),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X4),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8),
> +	DRM_FORMAT_MOD_INVALID
> +};
> +
> +static const u64 secondary_format_modifiers[] = {
> +	DRM_FORMAT_MOD_LINEAR,
> +	DRM_FORMAT_MOD_INVALID
> +};
> +
> +#define FRAC_16_16(mult, div)	 (((mult) << 16) / (div))
> +
> +static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = {

Please double-check the indention in this array.

Best regards
Thomas


-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstrasse 146, 90461 Nuernberg, Germany
GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman
HRB 36809 (AG Nuernberg)

[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

[-- Attachment #2: Type: text/plain, Size: 161 bytes --]

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-11-24 13:40     ` Thomas Zimmermann
  0 siblings, 0 replies; 117+ messages in thread
From: Thomas Zimmermann @ 2023-11-24 13:40 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: Conor Dooley, Albert Ou, Emil Renner Berthing, christian.koenig,
	Bjorn Andersson, Chris Morgan, Maxime Ripard, Jagan Teki,
	Jack Zhu, Rob Herring, Palmer Dabbelt, Krzysztof Kozlowski,
	Paul Walmsley, Shengyang Chen, Changhuang Liang, Shawn Guo,
	Sumit Semwal


[-- Attachment #1.1: Type: text/plain, Size: 58876 bytes --]

Hi

Am 25.10.23 um 12:39 schrieb Keith Zhao:
[...]
> +
> +static struct drm_crtc_state *
> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc_state *ori_state;

I have not yet seen 'ori_' being used anywhere. Typical names are 
'state' for the current state and 'new_state' for the newly created 
state.  Would be nice for consistency with other drivers.

> +	struct vs_crtc_state *state;
> +
> +	if (!crtc->state)
> +		return NULL;
> +
> +	ori_state = to_vs_crtc_state(crtc->state);
> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return NULL;
> +
> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
> +
> +	state->output_fmt = ori_state->output_fmt;
> +	state->encoder_type = ori_state->encoder_type;
> +	state->bpp = ori_state->bpp;
> +	state->underflow = ori_state->underflow;
> +
> +	return &state->base;
> +}
> +
> +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
> +					 struct drm_crtc_state *state)
> +{
> +	__drm_atomic_helper_crtc_destroy_state(state);
> +	kfree(to_vs_crtc_state(state));
> +}
> +
> +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_enable_vblank(dc, true);
> +
> +	return 0;
> +}
> +
> +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_enable_vblank(dc, false);
> +}
> +
> +static const struct drm_crtc_funcs vs_crtc_funcs = {
> +	.set_config		= drm_atomic_helper_set_config,
> +	.page_flip		= drm_atomic_helper_page_flip,
> +	.reset			= vs_crtc_reset,
> +	.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
> +	.atomic_destroy_state	= vs_crtc_atomic_destroy_state,
> +	.enable_vblank		= vs_crtc_enable_vblank,
> +	.disable_vblank		= vs_crtc_disable_vblank,
> +};
> +
> +static u8 cal_pixel_bits(u32 bus_format)
> +{
> +	u8 bpp;
> +
> +	switch (bus_format) {
> +	case MEDIA_BUS_FMT_RGB565_1X16:
> +	case MEDIA_BUS_FMT_UYVY8_1X16:
> +		bpp = 16;
> +		break;
> +	case MEDIA_BUS_FMT_RGB666_1X18:
> +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> +		bpp = 18;
> +		break;
> +	case MEDIA_BUS_FMT_UYVY10_1X20:
> +		bpp = 20;
> +		break;
> +	case MEDIA_BUS_FMT_BGR888_1X24:
> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +	case MEDIA_BUS_FMT_YUV8_1X24:
> +		bpp = 24;
> +		break;
> +	case MEDIA_BUS_FMT_RGB101010_1X30:
> +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> +	case MEDIA_BUS_FMT_YUV10_1X30:
> +		bpp = 30;
> +		break;
> +	default:
> +		bpp = 24;
> +		break;
> +	}
> +
> +	return bpp;
> +}
> +
> +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
> +				  struct drm_atomic_state *state)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
> +
> +	vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
> +
> +	vs_dc_enable(dc, crtc);
> +	drm_crtc_vblank_on(crtc);
> +}
> +
> +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
> +				   struct drm_atomic_state *state)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	drm_crtc_vblank_off(crtc);
> +
> +	vs_dc_disable(dc, crtc);
> +
> +	if (crtc->state->event && !crtc->state->active) {
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +
> +		crtc->state->event = NULL;
> +	}
> +}
> +
> +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
> +				 struct drm_atomic_state *state)
> +{
> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
> +									  crtc);
> +
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct device *dev = vs_crtc->dev;
> +	struct drm_property_blob *blob = crtc->state->gamma_lut;
> +	struct drm_color_lut *lut;
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	if (crtc_state->color_mgmt_changed) {
> +		if (blob && blob->length) {
> +			lut = blob->data;
> +			vs_dc_set_gamma(dc, crtc, lut,
> +					blob->length / sizeof(*lut));
> +			vs_dc_enable_gamma(dc, crtc, true);
> +		} else {
> +			vs_dc_enable_gamma(dc, crtc, false);
> +		}
> +	}
> +}
> +
> +static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
> +				 struct drm_atomic_state *state)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct drm_pending_vblank_event *event = crtc->state->event;
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_commit(dc);
> +
> +	if (event) {
> +		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
> +
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		drm_crtc_arm_vblank_event(crtc, event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +		crtc->state->event = NULL;
> +	}
> +}
> +
> +static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
> +	.atomic_check = drm_crtc_helper_atomic_check,
> +	.atomic_enable	= vs_crtc_atomic_enable,
> +	.atomic_disable = vs_crtc_atomic_disable,
> +	.atomic_begin	= vs_crtc_atomic_begin,
> +	.atomic_flush	= vs_crtc_atomic_flush,
> +};
> +
> +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
> +	{ VS_SINGLE_DC,				"single dc mode" },
> +	{ VS_MULTI_DC_PRIMARY,		"primary dc for multi dc mode" },
> +	{ VS_MULTI_DC_SECONDARY,	"secondary dc for multi dc mode" },
> +};
> +
> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
> +			       struct vs_dc_info *info)
> +{
> +	struct vs_crtc *crtc;
> +	int ret;
> +
> +	if (!info)
> +		return NULL;
> +
> +	crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
> +	if (!crtc)
> +		return NULL;
> +
> +	ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
> +					 NULL, NULL, &vs_crtc_funcs,
> +					 info->name ? info->name : NULL);
> +	if (ret)
> +		return NULL;
> +
> +	drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
> +
> +	if (info->gamma_size) {
> +		ret = drm_mode_crtc_set_gamma_size(&crtc->base,
> +						   info->gamma_size);
> +		if (ret)
> +			return NULL;
> +
> +		drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
> +					   info->gamma_size);
> +	}
> +
> +	crtc->max_bpc = info->max_bpc;
> +	crtc->color_formats = info->color_formats;
> +	return crtc;
> +}
> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h
> new file mode 100644
> index 000000000..526dd63e5
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.h
> @@ -0,0 +1,43 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_CRTC_H__
> +#define __VS_CRTC_H__
> +
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "vs_type.h"
> +
> +struct vs_crtc_state {
> +	struct drm_crtc_state base;
> +
> +	u32 output_fmt;
> +	u8 encoder_type;
> +	u8 bpp;
> +	bool underflow;
> +};
> +
> +struct vs_crtc {
> +	struct drm_crtc base;
> +	struct device *dev;
> +	unsigned int max_bpc;
> +	unsigned int color_formats;
> +};
> +
> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
> +			       struct vs_dc_info *info);
> +
> +static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc)
> +{
> +	return container_of(crtc, struct vs_crtc, base);
> +}
> +
> +static inline struct vs_crtc_state *
> +to_vs_crtc_state(struct drm_crtc_state *state)
> +{
> +	return container_of(state, struct vs_crtc_state, base);
> +}
> +#endif /* __VS_CRTC_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
> new file mode 100644
> index 000000000..b5ab92d98
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc.c
> @@ -0,0 +1,1002 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/component.h>
> +#include <linux/clk.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/reset.h>
> +
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_blend.h>
> +#include <drm/drm_framebuffer.h>
> +#include <drm/drm_vblank.h>
> +#include <drm/vs_drm.h>
> +
> +#include "vs_dc_hw.h"
> +#include "vs_dc.h"
> +#include "vs_drv.h"
> +
> +static const char * const vout_clocks[] = {
> +	"noc_bus",
> +	"channel0",
> +	"channel1",
> +	"dc_core",
> +	"axi_core",
> +	"ahb",
> +	"hdmi_tx",
> +	"dc_parent",
> +
> +};
> +
> +static const char * const vout_resets[] = {
> +	"axi",
> +	"ahb",
> +	"core",
> +};
> +
> +static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb)
> +{
> +	u8 f = FORMAT_A8R8G8B8;
> +
> +	switch (format) {
> +	case DRM_FORMAT_XRGB4444:
> +	case DRM_FORMAT_RGBX4444:
> +	case DRM_FORMAT_XBGR4444:
> +	case DRM_FORMAT_BGRX4444:
> +		f = FORMAT_X4R4G4B4;
> +		break;
> +	case DRM_FORMAT_ARGB4444:
> +	case DRM_FORMAT_RGBA4444:
> +	case DRM_FORMAT_ABGR4444:
> +	case DRM_FORMAT_BGRA4444:
> +		f = FORMAT_A4R4G4B4;
> +		break;
> +	case DRM_FORMAT_XRGB1555:
> +	case DRM_FORMAT_RGBX5551:
> +	case DRM_FORMAT_XBGR1555:
> +	case DRM_FORMAT_BGRX5551:
> +		f = FORMAT_X1R5G5B5;
> +		break;
> +	case DRM_FORMAT_ARGB1555:
> +	case DRM_FORMAT_RGBA5551:
> +	case DRM_FORMAT_ABGR1555:
> +	case DRM_FORMAT_BGRA5551:
> +		f = FORMAT_A1R5G5B5;
> +		break;
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		f = FORMAT_R5G6B5;
> +		break;
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_RGBX8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_BGRX8888:
> +		f = FORMAT_X8R8G8B8;
> +		break;
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_RGBA8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_BGRA8888:
> +		f = FORMAT_A8R8G8B8;
> +		break;
> +	case DRM_FORMAT_YUYV:
> +	case DRM_FORMAT_YVYU:
> +		f = FORMAT_YUY2;
> +		break;
> +	case DRM_FORMAT_UYVY:
> +	case DRM_FORMAT_VYUY:
> +		f = FORMAT_UYVY;
> +		break;
> +	case DRM_FORMAT_YUV420:
> +	case DRM_FORMAT_YVU420:
> +		f = FORMAT_YV12;
> +		break;
> +	case DRM_FORMAT_NV21:
> +		f = FORMAT_NV12;
> +		break;
> +	case DRM_FORMAT_NV16:
> +	case DRM_FORMAT_NV61:
> +		f = FORMAT_NV16;
> +		break;
> +	case DRM_FORMAT_P010:
> +		f = FORMAT_P010;
> +		break;
> +	case DRM_FORMAT_ARGB2101010:
> +	case DRM_FORMAT_RGBA1010102:
> +	case DRM_FORMAT_ABGR2101010:
> +	case DRM_FORMAT_BGRA1010102:
> +		f = FORMAT_A2R10G10B10;
> +		break;
> +	case DRM_FORMAT_NV12:
> +		f = FORMAT_NV12;
> +		break;
> +	case DRM_FORMAT_YUV444:
> +		f = FORMAT_YUV444;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	fb->format = f;
> +}
> +
> +static inline void update_swizzle(u32 format, struct dc_hw_fb *fb)
> +{
> +	fb->swizzle = SWIZZLE_ARGB;
> +	fb->uv_swizzle = 0;
> +
> +	switch (format) {
> +	case DRM_FORMAT_RGBX4444:
> +	case DRM_FORMAT_RGBA4444:
> +	case DRM_FORMAT_RGBX5551:
> +	case DRM_FORMAT_RGBA5551:
> +	case DRM_FORMAT_RGBX8888:
> +	case DRM_FORMAT_RGBA8888:
> +	case DRM_FORMAT_RGBA1010102:
> +		fb->swizzle = SWIZZLE_RGBA;
> +		break;
> +	case DRM_FORMAT_XBGR4444:
> +	case DRM_FORMAT_ABGR4444:
> +	case DRM_FORMAT_XBGR1555:
> +	case DRM_FORMAT_ABGR1555:
> +	case DRM_FORMAT_BGR565:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_ABGR2101010:
> +		fb->swizzle = SWIZZLE_ABGR;
> +		break;
> +	case DRM_FORMAT_BGRX4444:
> +	case DRM_FORMAT_BGRA4444:
> +	case DRM_FORMAT_BGRX5551:
> +	case DRM_FORMAT_BGRA5551:
> +	case DRM_FORMAT_BGRX8888:
> +	case DRM_FORMAT_BGRA8888:
> +	case DRM_FORMAT_BGRA1010102:
> +		fb->swizzle = SWIZZLE_BGRA;
> +		break;
> +	case DRM_FORMAT_YVYU:
> +	case DRM_FORMAT_VYUY:
> +	case DRM_FORMAT_NV21:
> +	case DRM_FORMAT_NV61:
> +		fb->uv_swizzle = 1;
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +static inline void update_watermark(struct drm_property_blob *watermark,
> +				    struct dc_hw_fb *fb)
> +{
> +	struct drm_vs_watermark *data;
> +
> +	fb->water_mark = 0;
> +
> +	if (watermark) {
> +		data = watermark->data;
> +		fb->water_mark = data->watermark & 0xFFFFF;
> +	}
> +}
> +
> +static inline u8 to_vs_rotation(unsigned int rotation)
> +{
> +	u8 rot;
> +
> +	switch (rotation & DRM_MODE_REFLECT_MASK) {
> +	case DRM_MODE_REFLECT_X:
> +		rot = FLIP_X;
> +		return rot;
> +	case DRM_MODE_REFLECT_Y:
> +		rot = FLIP_Y;
> +		return rot;
> +	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
> +		rot = FLIP_XY;
> +		return rot;
> +	default:
> +		break;
> +	}
> +
> +	switch (rotation & DRM_MODE_ROTATE_MASK) {
> +	case DRM_MODE_ROTATE_0:
> +		rot = ROT_0;
> +		break;
> +	case DRM_MODE_ROTATE_90:
> +		rot = ROT_90;
> +		break;
> +	case DRM_MODE_ROTATE_180:
> +		rot = ROT_180;
> +		break;
> +	case DRM_MODE_ROTATE_270:
> +		rot = ROT_270;
> +		break;
> +	default:
> +		rot = ROT_0;
> +		break;
> +	}
> +
> +	return rot;
> +}
> +
> +static inline u8 to_vs_yuv_color_space(u32 color_space)
> +{
> +	u8 cs;
> +
> +	switch (color_space) {
> +	case DRM_COLOR_YCBCR_BT601:
> +		cs = COLOR_SPACE_601;
> +		break;
> +	case DRM_COLOR_YCBCR_BT709:
> +		cs = COLOR_SPACE_709;
> +		break;
> +	case DRM_COLOR_YCBCR_BT2020:
> +		cs = COLOR_SPACE_2020;
> +		break;
> +	default:
> +		cs = COLOR_SPACE_601;
> +		break;
> +	}
> +
> +	return cs;
> +}
> +
> +static inline u8 to_vs_tile_mode(u64 modifier)
> +{
> +	if (modifier == DRM_FORMAT_MOD_VIVANTE_TILED)
> +		return DC_TILE_MODE4X4;
> +
> +	return (u8)(modifier & DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK);
> +}
> +
> +static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +	u8 panel_num = dc->hw.info->panel_num;
> +	u32 index = drm_crtc_index(crtc);
> +	int i;
> +
> +	for (i = 0; i < panel_num; i++) {
> +		if (index == dc->crtc[i]->base.index)
> +			return i;
> +	}
> +
> +	return 0;
> +}
> +
> +static void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
> +					  unsigned int alignment)
> +{
> +	struct vs_drm_device *priv = to_vs_drm_private(drm_dev);
> +
> +	if (alignment > priv->pitch_alignment)
> +		priv->pitch_alignment = alignment;
> +}
> +
> +static int plda_clk_rst_init(struct device *dev)
> +{
> +	int ret = 0;
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	ret = clk_bulk_prepare_enable(dc->nclks, dc->clk_vout);
> +	if (ret) {
> +		dev_err(dev, "failed to enable clocks\n");
> +		return ret;
> +	}
> +
> +	ret = reset_control_bulk_deassert(dc->nrsts, dc->rst_vout);
> +	return ret;
> +}
> +
> +static void plda_clk_rst_deinit(struct device *dev)
> +{
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	reset_control_bulk_assert(dc->nrsts, dc->rst_vout);
> +	clk_bulk_disable_unprepare(dc->nclks, dc->clk_vout);
> +}
> +
> +static void dc_deinit(struct device *dev)
> +{
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	dc_hw_enable_interrupt(&dc->hw, 0);
> +	dc_hw_deinit(&dc->hw);
> +	plda_clk_rst_deinit(dev);
> +}
> +
> +static int dc_init(struct device *dev)
> +{
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +	int ret;
> +
> +	dc->first_frame = true;
> +
> +	ret = plda_clk_rst_init(dev);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to init dc clk reset: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = dc_hw_init(&dc->hw);
> +	if (ret) {
> +		dev_err(dev, "failed to init DC HW\n");
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +	struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	struct dc_hw_display display;
> +
> +	display.bus_format = crtc_state->output_fmt;
> +	display.h_active = mode->hdisplay;
> +	display.h_total = mode->htotal;
> +	display.h_sync_start = mode->hsync_start;
> +	display.h_sync_end = mode->hsync_end;
> +	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> +		display.h_sync_polarity = true;
> +	else
> +		display.h_sync_polarity = false;
> +
> +	display.v_active = mode->vdisplay;
> +	display.v_total = mode->vtotal;
> +	display.v_sync_start = mode->vsync_start;
> +	display.v_sync_end = mode->vsync_end;
> +
> +	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> +		display.v_sync_polarity = true;
> +	else
> +		display.v_sync_polarity = false;
> +
> +	display.id = to_vs_display_id(dc, crtc);
> +
> +	display.enable = true;
> +
> +	if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) {
> +		dc_hw_set_out(&dc->hw, OUT_DPI, display.id);
> +		clk_set_rate(dc->clk_vout[CLK_VOUT_SOC_PIX].clk, mode->clock * 1000);
> +		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX1].clk,
> +			       dc->clk_vout[CLK_VOUT_SOC_PIX].clk);
> +	} else {
> +		dc_hw_set_out(&dc->hw, OUT_DP, display.id);
> +		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX0].clk,
> +			       dc->clk_vout[CLK_VOUT_HDMI_PIX].clk);
> +	}
> +
> +	dc_hw_setup_display(&dc->hw, &display);
> +}
> +
> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +	struct dc_hw_display display;
> +
> +	display.id = to_vs_display_id(dc, crtc);
> +	display.enable = false;
> +
> +	dc_hw_setup_display(&dc->hw, &display);
> +}
> +
> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> +		     struct drm_color_lut *lut, unsigned int size)
> +{
> +	u16 i, r, g, b;
> +	u8 bits, id;
> +
> +	if (size != dc->hw.info->gamma_size) {
> +		drm_err(crtc->dev, "gamma size does not match!\n");
> +		return;
> +	}
> +
> +	id = to_vs_display_id(dc, crtc);
> +
> +	bits = dc->hw.info->gamma_bits;
> +	for (i = 0; i < size; i++) {
> +		r = drm_color_lut_extract(lut[i].red, bits);
> +		g = drm_color_lut_extract(lut[i].green, bits);
> +		b = drm_color_lut_extract(lut[i].blue, bits);
> +		dc_hw_update_gamma(&dc->hw, id, i, r, g, b);
> +	}
> +}
> +
> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> +			bool enable)
> +{
> +	u8 id;
> +
> +	id = to_vs_display_id(dc, crtc);
> +	dc_hw_enable_gamma(&dc->hw, id, enable);
> +}
> +
> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable)
> +{
> +	dc_hw_enable_interrupt(&dc->hw, enable);
> +}
> +
> +static u32 calc_factor(u32 src, u32 dest)
> +{
> +	u32 factor = 1 << 16;
> +
> +	if (src > 1 && dest > 1)
> +		factor = ((src - 1) << 16) / (dest - 1);
> +
> +	return factor;
> +}
> +
> +static void update_scale(struct drm_plane_state *state, struct dc_hw_roi *roi,
> +			 struct dc_hw_scale *scale)
> +{
> +	int dst_w = drm_rect_width(&state->dst);
> +	int dst_h = drm_rect_height(&state->dst);
> +	int src_w, src_h, temp;
> +
> +	scale->enable = false;
> +
> +	if (roi->enable) {
> +		src_w = roi->width;
> +		src_h = roi->height;
> +	} else {
> +		src_w = drm_rect_width(&state->src) >> 16;
> +		src_h = drm_rect_height(&state->src) >> 16;
> +	}
> +
> +	if (drm_rotation_90_or_270(state->rotation)) {
> +		temp = src_w;
> +		src_w = src_h;
> +		src_h = temp;
> +	}
> +
> +	if (src_w != dst_w) {
> +		scale->scale_factor_x = calc_factor(src_w, dst_w);
> +		scale->enable = true;
> +	} else {
> +		scale->scale_factor_x = 1 << 16;
> +	}
> +	if (src_h != dst_h) {
> +		scale->scale_factor_y = calc_factor(src_h, dst_h);
> +		scale->enable = true;
> +	} else {
> +		scale->scale_factor_y = 1 << 16;
> +	}
> +}
> +
> +static void update_fb(struct vs_plane *plane, u8 display_id,
> +		      struct dc_hw_fb *fb, struct drm_plane_state *state)
> +{
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_framebuffer *drm_fb = state->fb;
> +	struct drm_rect *src = &state->src;
> +
> +	fb->display_id = display_id;
> +	fb->y_address = plane_state->dma_addr[0];
> +	fb->y_stride = drm_fb->pitches[0];
> +	if (drm_fb->format->format == DRM_FORMAT_YVU420) {
> +		fb->u_address = plane_state->dma_addr[2];
> +		fb->v_address = plane_state->dma_addr[1];
> +		fb->u_stride = drm_fb->pitches[2];
> +		fb->v_stride = drm_fb->pitches[1];
> +	} else {
> +		fb->u_address = plane_state->dma_addr[1];
> +		fb->v_address = plane_state->dma_addr[2];
> +		fb->u_stride = drm_fb->pitches[1];
> +		fb->v_stride = drm_fb->pitches[2];
> +	}
> +	fb->width = drm_rect_width(src) >> 16;
> +	fb->height = drm_rect_height(src) >> 16;
> +	fb->tile_mode = to_vs_tile_mode(drm_fb->modifier);
> +	fb->rotation = to_vs_rotation(state->rotation);
> +	fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding);
> +	fb->zpos = state->zpos;
> +	fb->enable = state->visible;
> +	update_format(drm_fb->format->format, drm_fb->modifier, fb);
> +	update_swizzle(drm_fb->format->format, fb);
> +	update_watermark(plane_state->watermark, fb);
> +	plane_state->status.tile_mode = fb->tile_mode;
> +}
> +
> +static void update_degamma(struct vs_dc *dc, struct vs_plane *plane,
> +			   struct vs_plane_state *plane_state)
> +{
> +	dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
> +	plane_state->degamma_changed = false;
> +}
> +
> +static void update_roi(struct vs_dc *dc, u8 id,
> +		       struct vs_plane_state *plane_state,
> +		       struct dc_hw_roi *roi,
> +		       struct drm_plane_state *state)
> +{
> +	struct drm_vs_roi *data;
> +	struct drm_rect *src = &state->src;
> +	u16 src_w = drm_rect_width(src) >> 16;
> +	u16 src_h = drm_rect_height(src) >> 16;
> +
> +	if (plane_state->roi) {
> +		data = plane_state->roi->data;
> +
> +		if (data->enable) {
> +			roi->x = data->roi_x;
> +			roi->y = data->roi_y;
> +			roi->width = (data->roi_x + data->roi_w > src_w) ?
> +						 (src_w - data->roi_x) : data->roi_w;
> +			roi->height = (data->roi_y + data->roi_h > src_h) ?
> +						  (src_h - data->roi_y) : data->roi_h;
> +			roi->enable = true;
> +		} else {
> +			roi->enable = false;
> +		}
> +
> +		dc_hw_update_roi(&dc->hw, id, roi);
> +	} else {
> +		roi->enable = false;
> +	}
> +}
> +
> +static void update_color_mgmt(struct vs_dc *dc, u8 id,
> +			      struct dc_hw_fb *fb,
> +			      struct vs_plane_state *plane_state)
> +{
> +	struct drm_vs_color_mgmt *data;
> +	struct dc_hw_colorkey colorkey;
> +
> +	if (plane_state->color_mgmt) {
> +		data = plane_state->color_mgmt->data;
> +
> +		fb->clear_enable = data->clear_enable;
> +		fb->clear_value = data->clear_value;
> +
> +		if (data->colorkey > data->colorkey_high)
> +			data->colorkey = data->colorkey_high;
> +
> +		colorkey.colorkey = data->colorkey;
> +		colorkey.colorkey_high = data->colorkey_high;
> +		colorkey.transparency = (data->transparency) ?
> +				DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE;
> +		dc_hw_update_colorkey(&dc->hw, id, &colorkey);
> +	}
> +}
> +
> +static void update_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			 struct drm_plane *drm_plane,
> +			 struct drm_atomic_state *drm_state)
> +{
> +	struct dc_hw_fb fb = {0};
> +	struct dc_hw_scale scale;
> +	struct dc_hw_position pos;
> +	struct dc_hw_blend blend;
> +	struct dc_hw_roi roi;
> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> +									   drm_plane);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_rect *dest = &state->dst;
> +	bool dec_enable = false;
> +	u8 display_id = 0;
> +
> +	display_id = to_vs_display_id(dc, state->crtc);
> +	update_fb(plane, display_id, &fb, state);
> +	fb.dec_enable = dec_enable;
> +
> +	update_roi(dc, plane->id, plane_state, &roi, state);
> +
> +	update_scale(state, &roi, &scale);
> +
> +	if (plane_state->degamma_changed)
> +		update_degamma(dc, plane, plane_state);
> +
> +	pos.start_x = dest->x1;
> +	pos.start_y = dest->y1;
> +	pos.end_x = dest->x2;
> +	pos.end_y = dest->y2;
> +
> +	blend.alpha = (u8)(state->alpha >> 8);
> +	blend.blend_mode = (u8)(state->pixel_blend_mode);
> +
> +	update_color_mgmt(dc, plane->id, &fb, plane_state);
> +
> +	dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
> +}
> +
> +static void update_qos(struct vs_dc *dc, struct vs_plane *plane,
> +		       struct drm_plane *drm_plane,
> +		       struct drm_atomic_state *drm_state)
> +{
> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> +									   drm_plane);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_vs_watermark *data;
> +	struct dc_hw_qos qos;
> +
> +	if (plane_state->watermark) {
> +		data = plane_state->watermark->data;
> +
> +		if (data->qos_high) {
> +			if (data->qos_low > data->qos_high)
> +				data->qos_low = data->qos_high;
> +
> +			qos.low_value = data->qos_low & 0x0F;
> +			qos.high_value = data->qos_high & 0x0F;
> +			dc_hw_update_qos(&dc->hw, &qos);
> +		}
> +	}
> +}
> +
> +static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor)
> +{
> +	u8 size_type;
> +
> +	switch (state->crtc_w) {
> +	case 32:
> +		size_type = CURSOR_SIZE_32X32;
> +		break;
> +	case 64:
> +		size_type = CURSOR_SIZE_64X64;
> +		break;
> +	default:
> +		size_type = CURSOR_SIZE_32X32;
> +		break;
> +	}
> +
> +	cursor->size = size_type;
> +}
> +
> +static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +				struct drm_plane *drm_plane,
> +				struct drm_atomic_state *drm_state)
> +{
> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> +								       drm_plane);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_framebuffer *drm_fb = state->fb;
> +	struct dc_hw_cursor cursor;
> +
> +	cursor.address = plane_state->dma_addr[0];
> +	cursor.x = state->crtc_x;
> +	cursor.y = state->crtc_y;
> +	cursor.hot_x = drm_fb->hot_x;
> +	cursor.hot_y = drm_fb->hot_y;
> +	cursor.display_id = to_vs_display_id(dc, state->crtc);
> +	update_cursor_size(state, &cursor);
> +	cursor.enable = true;
> +
> +	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
> +}
> +
> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			struct drm_plane *drm_plane,
> +			struct drm_atomic_state *drm_state)
> +{
> +	update_plane(dc, plane, drm_plane, drm_state);
> +	update_qos(dc, plane, drm_plane, drm_state);
> +}
> +
> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			       struct drm_plane *drm_plane,
> +			       struct drm_atomic_state *drm_state)
> +{
> +	update_cursor_plane(dc, plane, drm_plane, drm_state);
> +}
> +
> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			 struct drm_plane_state *old_state)
> +{
> +	struct dc_hw_fb fb = {0};
> +
> +	fb.enable = false;
> +	dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL, NULL, NULL);
> +}
> +
> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +				struct drm_plane_state *old_state)
> +{
> +	struct dc_hw_cursor cursor = {0};
> +
> +	cursor.enable = false;
> +	cursor.display_id = to_vs_display_id(dc, old_state->crtc);
> +	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
> +}
> +
> +static bool vs_dc_mod_supported(const struct vs_plane_info *plane_info,
> +				u64 modifier)
> +{
> +	const u64 *mods;
> +
> +	if (!plane_info->modifiers)
> +		return false;
> +
> +	for (mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID; mods++) {
> +		if (*mods == modifier)
> +			return true;
> +	}
> +
> +	return false;
> +}
> +
> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
> +		      struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> +										 plane);
> +
> +	struct drm_framebuffer *fb = new_plane_state->fb;
> +	const struct vs_plane_info *plane_info;
> +	struct drm_crtc *crtc = new_plane_state->crtc;
> +	struct drm_crtc_state *crtc_state;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +
> +	plane_info = &dc->hw.info->planes[vs_plane->id];
> +
> +	if (fb->width < plane_info->min_width ||
> +	    fb->width > plane_info->max_width ||
> +	    fb->height < plane_info->min_height ||
> +	    fb->height > plane_info->max_height)
> +		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n",
> +			     vs_plane->id);
> +
> +	if (!vs_dc_mod_supported(plane_info, fb->modifier)) {
> +		drm_err(plane->dev, "unsupported modifier on plane%d.\n", vs_plane->id);
> +		return -EINVAL;
> +	}
> +
> +	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
> +	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
> +						  plane_info->min_scale,
> +						  plane_info->max_scale,
> +						  true, true);
> +}
> +
> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
> +			     struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> +									   plane);
> +	struct drm_framebuffer *fb = new_plane_state->fb;
> +	const struct vs_plane_info *plane_info;
> +	struct drm_crtc *crtc = new_plane_state->crtc;
> +	struct drm_crtc_state *crtc_state;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +
> +	plane_info = &dc->hw.info->planes[vs_plane->id];
> +
> +	if (fb->width < plane_info->min_width ||
> +	    fb->width > plane_info->max_width ||
> +	    fb->height < plane_info->min_height ||
> +	    fb->height > plane_info->max_height)
> +		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n", vs_plane->id);
> +
> +	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
> +	if (IS_ERR(crtc_state))
> +		return -EINVAL;
> +
> +	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
> +						plane_info->min_scale,
> +						plane_info->max_scale,
> +						true, true);
> +}
> +
> +static void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
> +{
> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
> +
> +	drm_crtc_handle_vblank(crtc);
> +
> +	vs_crtc_state->underflow = underflow;
> +}
> +
> +static irqreturn_t dc_isr(int irq, void *data)
> +{
> +	struct vs_dc *dc = data;
> +	struct vs_dc_info *dc_info = dc->hw.info;
> +	u32 i, ret;
> +
> +	ret = dc_hw_get_interrupt(&dc->hw);
> +
> +	for (i = 0; i < dc_info->panel_num; i++)
> +		vs_crtc_handle_vblank(&dc->crtc[i]->base, dc_hw_check_underflow(&dc->hw));
> +
> +	return IRQ_HANDLED;
> +}
> +
> +void vs_dc_commit(struct vs_dc *dc)
> +{
> +	dc_hw_enable_shadow_register(&dc->hw, false);
> +
> +	dc_hw_commit(&dc->hw);
> +
> +	if (dc->first_frame)
> +		dc->first_frame = false;
> +
> +	dc_hw_enable_shadow_register(&dc->hw, true);
> +}
> +
> +static int dc_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct drm_device *drm_dev = data;
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +	struct device_node *port;
> +	struct vs_crtc *crtc;
> +	struct vs_dc_info *dc_info;
> +	struct vs_plane *plane;
> +	struct vs_plane_info *plane_info;
> +	int i, ret;
> +	u32 ctrc_mask = 0;
> +
> +	if (!drm_dev || !dc) {
> +		dev_err(dev, "devices are not created.\n");
> +		return -ENODEV;
> +	}
> +
> +	ret = dc_init(dev);
> +	if (ret < 0) {
> +		drm_err(drm_dev, "Failed to initialize DC hardware.\n");
> +		return ret;
> +	}
> +
> +	port = of_get_child_by_name(dev->of_node, "port");
> +	if (!port) {
> +		drm_err(drm_dev, "no port node found\n");
> +		return -ENODEV;
> +	}
> +	of_node_put(port);
> +
> +	dc_info = dc->hw.info;
> +
> +	for (i = 0; i < dc_info->panel_num; i++) {
> +		crtc = vs_crtc_create(drm_dev, dc_info);
> +		if (!crtc) {
> +			drm_err(drm_dev, "Failed to create CRTC.\n");
> +			ret = -ENOMEM;
> +			return ret;
> +		}
> +
> +		crtc->base.port = port;
> +		crtc->dev = dev;
> +		dc->crtc[i] = crtc;
> +		ctrc_mask |= drm_crtc_mask(&crtc->base);
> +	}
> +
> +	for (i = 0; i < dc_info->plane_num; i++) {
> +		plane_info = (struct vs_plane_info *)&dc_info->planes[i];
> +
> +		if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
> +						drm_crtc_mask(&dc->crtc[0]->base));
> +		} else if (!strcmp(plane_info->name, "Primary_1") ||
> +				   !strcmp(plane_info->name, "Cursor_1")) {
> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
> +						drm_crtc_mask(&dc->crtc[1]->base));

Do you somehow verify that dc->crtc[0] and dc->crtc[1] are not NULL here?

> +		} else {
> +			plane = vs_plane_create(drm_dev, plane_info,
> +						dc_info->layer_num, ctrc_mask);
> +		}

In addition to Maxime's comment, I think it would be easier to read if 
you only set the plane's crtc make in these if-else branches and then 
call vs_plane_create() with the result. Like this

   plane_crtc_mask = crtc_mask;

   if (primary || cursor)
     plane_crtc_mask = drm_crtc_mask(&dc->crtc[0]->base)
   else if (primary_1 || cursor_1)
     plane_crtc_mask = drm_crtc_mask(&dc->crtc[1]->base)

   vs_plane_create(..., plane_crtc_mask)

> +
> +		if (IS_ERR(plane)) {
> +			dev_err(dev, "failed to construct plane\n");
> +			return PTR_ERR(plane);
> +		}
> +
> +		plane->id = i;
> +		dc->planes[i].id = plane_info->id;
> +
> +		if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
> +			if (!strcmp(plane_info->name, "Primary"))
> +				dc->crtc[0]->base.primary = &plane->base;
> +			else
> +				dc->crtc[1]->base.primary = &plane->base;
> +			drm_dev->mode_config.min_width = plane_info->min_width;
> +			drm_dev->mode_config.min_height =
> +							plane_info->min_height;
> +			drm_dev->mode_config.max_width = plane_info->max_width;
> +			drm_dev->mode_config.max_height =
> +							plane_info->max_height;
> +		}

If you have multiple primary planes, the later planes overwrite the 
settings from the earlier ones. Is that intentional?

> +
> +		if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
> +			if (!strcmp(plane_info->name, "Cursor"))
> +				dc->crtc[0]->base.cursor = &plane->base;
> +			else
> +				dc->crtc[1]->base.cursor = &plane->base;
> +			drm_dev->mode_config.cursor_width =
> +							plane_info->max_width;
> +			drm_dev->mode_config.cursor_height =
> +							plane_info->max_height;
> +		}

Same question about overwriting as for the primary planes?

> +	}
> +
> +	vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
> +	return 0;
> +}
> +
> +static void dc_unbind(struct device *dev, struct device *master, void *data)
> +{
> +	dc_deinit(dev);
> +}
> +
> +const struct component_ops dc_component_ops = {
> +	.bind = dc_bind,
> +	.unbind = dc_unbind,
> +};
> +
> +static const struct of_device_id dc_driver_dt_match[] = {
> +	{ .compatible = "starfive,jh7110-dc8200", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
> +
> +static int dc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct vs_dc *dc;
> +	int irq, ret, i;
> +
> +	dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
> +	if (!dc)
> +		return -ENOMEM;
> +
> +	dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(dc->hw.hi_base))
> +		return PTR_ERR(dc->hw.hi_base);
> +
> +	dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
> +	if (IS_ERR(dc->hw.reg_base))
> +		return PTR_ERR(dc->hw.reg_base);
> +
> +	dc->nclks = ARRAY_SIZE(dc->clk_vout);
> +	for (i = 0; i < dc->nclks; ++i)
> +		dc->clk_vout[i].id = vout_clocks[i];
> +	ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
> +	if (ret) {
> +		dev_err(dev, "Failed to get clk controls\n");
> +		return ret;
> +	}
> +
> +	dc->nrsts = ARRAY_SIZE(dc->rst_vout);
> +	for (i = 0; i < dc->nrsts; ++i)
> +		dc->rst_vout[i].id = vout_resets[i];
> +	ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
> +						 dc->rst_vout);
> +	if (ret) {
> +		dev_err(dev, "Failed to get reset controls\n");
> +		return ret;
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +
> +	ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to install irq:%u.\n", irq);
> +		return ret;
> +	}
> +
> +	dev_set_drvdata(dev, dc);
> +
> +	return component_add(dev, &dc_component_ops);
> +}
> +
> +static int dc_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +
> +	component_del(dev, &dc_component_ops);
> +
> +	dev_set_drvdata(dev, NULL);
> +
> +	return 0;
> +}
> +
> +struct platform_driver dc_platform_driver = {
> +	.probe = dc_probe,
> +	.remove = dc_remove,
> +	.driver = {
> +		.name = "vs-dc",
> +		.of_match_table = of_match_ptr(dc_driver_dt_match),
> +	},
> +};
> +
> +MODULE_AUTHOR("StarFive Corporation");
> +MODULE_DESCRIPTION("VeriSilicon DC Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
> new file mode 100644
> index 000000000..8e96fd32c
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc.h
> @@ -0,0 +1,80 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DC_H__
> +#define __VS_DC_H__
> +
> +#include <linux/clk.h>
> +#include <linux/mm_types.h>
> +#include <linux/reset.h>
> +
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_modes.h>
> +
> +#include "vs_crtc.h"
> +#include "vs_dc_hw.h"
> +#include "vs_plane.h"
> +
> +struct vs_dc_plane {
> +	enum dc_hw_plane_id id;
> +};
> +
> +enum vout_clk {
> +	CLK_VOUT_NOC_DISP = 0,
> +	CLK_VOUT_PIX0,
> +	CLK_VOUT_PIX1,
> +	CLK_VOUT_CORE,
> +	CLK_VOUT_AXI,
> +	CLK_VOUT_AHB,
> +	CLK_VOUT_HDMI_PIX,
> +	CLK_VOUT_SOC_PIX,
> +	CLK_VOUT_NUM
> +};
> +
> +enum rst_vout {
> +	RST_VOUT_AXI = 0,
> +	RST_VOUT_AHB,
> +	RST_VOUT_CORE,
> +	RST_VOUT_NUM
> +};
> +
> +struct vs_dc {
> +	struct vs_crtc		*crtc[DC_DISPLAY_NUM];
> +	struct dc_hw		hw;
> +	bool			first_frame;
> +
> +	struct vs_dc_plane	planes[PLANE_NUM];
> +	struct clk_bulk_data	clk_vout[CLK_VOUT_NUM];
> +	int			nclks;
> +	struct reset_control_bulk_data rst_vout[RST_VOUT_NUM];
> +	int			nrsts;
> +};
> +
> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc);
> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc);
> +
> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> +		     struct drm_color_lut *lut, unsigned int size);
> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc, bool enable);
> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable);
> +void vs_dc_commit(struct vs_dc *dc);
> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			struct drm_plane *drm_plane,
> +			struct drm_atomic_state *drm_state);
> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			 struct drm_plane_state *old_state);
> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
> +		      struct drm_atomic_state *state);
> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			       struct drm_plane *drm_plane,
> +			       struct drm_atomic_state *drm_state);
> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +				struct drm_plane_state *old_state);
> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
> +			     struct drm_atomic_state *state);
> +
> +extern struct platform_driver dc_platform_driver;
> +
> +#endif /* __VS_DC_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
> new file mode 100644
> index 000000000..e5acb7eb4
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
> @@ -0,0 +1,1959 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/bits.h>
> +#include <linux/io.h>
> +#include <linux/media-bus-format.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_blend.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/vs_drm.h>
> +
> +#include "vs_dc_hw.h"
> +#include "vs_type.h"
> +
> +static const u32 horkernel[] = {
> +	0x00000000, 0x20000000, 0x00002000, 0x00000000,
> +	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
> +	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
> +	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
> +	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
> +	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
> +	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
> +	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
> +	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
> +	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
> +	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
> +	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
> +	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
> +	0x00000000, 0x00000000, 0x00000000, 0x01470000,
> +	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
> +	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
> +	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
> +	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
> +	0x00000000, 0x00000000, 0x00004000, 0x00000000,
> +	0x00000000, 0x00000000, 0x20002000, 0x00000000,
> +	0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
> +	0x00000000, 0x00000000, 0x00000000, 0x27e1181f,
> +	0x00000000, 0x00000000, 0x00000000, 0x14680000,
> +	0x00002b98, 0x00000000, 0x00000000, 0x00000000,
> +	0x2f1010f0, 0x00000000, 0x00000000, 0x00000000,
> +	0x0dc70000, 0x00003239, 0x00000000, 0x00000000,
> +	0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
> +	0x00000000, 0x087f0000, 0x00003781, 0x00000000,
> +	0x00000000, 0x00000000, 0x399a0666, 0x00000000,
> +	0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
> +	0x00000000, 0x00000000, 0x00000000, 0x3cc4033c,
> +	0x00000000, 0x00000000, 0x00000000, 0x021f0000,
> +};
> +
> +#define H_COEF_SIZE (sizeof(horkernel) / sizeof(u32))

Either drop this in favor of the regular ARRAY_SIZE() macro, or hardcode 
the size if it's a fixed hardware setting.

> +
> +static const u32 verkernel[] = {
> +	0x00000000, 0x20000000, 0x00002000, 0x00000000,
> +	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
> +	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
> +	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
> +	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
> +	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
> +	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
> +	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
> +	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
> +	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
> +	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
> +	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
> +	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
> +	0x00000000, 0x00000000, 0x00000000, 0x01470000,
> +	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
> +	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
> +	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
> +	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
> +	0x00000000, 0x00000000, 0x00004000, 0x00000000,
> +	0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab,
> +	0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
> +	0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +};
> +
> +#define V_COEF_SIZE (sizeof(verkernel) / sizeof(u32))

Same as H_COEF_SIZE;

> +
> +/*
> + * RGB 709->2020 conversion parameters
> + */
> +static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = {

'static const' here and for the arrays below.

> +	10279,	5395,	709,
> +	1132,	15065,	187,
> +	269,	1442,	14674
> +};
> +
> +/*
> + * YUV601 to RGB conversion parameters
> + * YUV2RGB[0]  - [8] : C0 - C8;
> + * YUV2RGB[9]  - [11]: D0 - D2;
> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> + */
> +static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> +	1196,	0,			1640,	1196,
> +	-404,	-836,		1196,	2076,
> +	0,		-916224,	558336,	-1202944,
> +	64,		 940,		64,		960

Might be my email reader, but indention looks off here.

> +};
> +
> +/*
> + * YUV709 to RGB conversion parameters
> + * YUV2RGB[0]  - [8] : C0 - C8;
> + * YUV2RGB[9]  - [11]: D0 - D2;
> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> + */
> +static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> +	1196,		0,		1844,	1196,
> +	-220,		-548,	1196,	2172,
> +	0,			-1020672, 316672,  -1188608,
> +	64,			940,		64,		960

Indention?

> +};
> +
> +/*
> + * YUV2020 to RGB conversion parameters
> + * YUV2RGB[0]  - [8] : C0 - C8;
> + * YUV2RGB[9]  - [11]: D0 - D2;
> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> + */
> +static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> +	1196, 0, 1724, 1196,
> +	-192, -668, 1196, 2200,
> +	0, -959232, 363776, -1202944,
> +	64, 940, 64, 960

Indention.

> +};
> +
> +/*
> + * RGB to YUV2020 conversion parameters
> + * RGB2YUV[0] - [8] : C0 - C8;
> + * RGB2YUV[9] - [11]: D0 - D2;
> + */
> +static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = {
> +	230,	594,	52,
> +	-125,  -323,	448,
> +	448,   -412,   -36,
> +	64,		512,	512
> +};
> +
> +/*
> + * Degamma table for 709 color space data.
> + */
> +static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005,
> +	0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024,
> +	0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064,
> +	0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce,
> +	0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166,
> +	0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230,
> +	0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331,
> +	0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c,
> +	0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
> +	0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d,
> +	0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999,
> +	0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb,
> +	0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65,
> +	0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a,
> +	0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b,
> +	0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc,
> +	0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e,
> +	0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
> +	0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e,
> +	0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf,
> +	0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea,
> +	0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f,
> +	0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930,
> +	0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f,
> +	0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee,
> +	0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde,
> +	0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
> +	0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9,
> +	0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406,
> +	0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a,
> +	0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576,
> +	0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd,
> +	0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
> +};
> +
> +/*
> + * Degamma table for 2020 color space data.
> + */
> +static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> +	0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
> +	0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
> +	0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006,
> +	0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a,
> +	0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f,
> +	0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
> +	0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024,
> +	0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035,
> +	0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d,
> +	0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d,
> +	0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098,
> +	0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3,
> +	0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123,
> +	0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d,
> +	0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
> +	0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6,
> +	0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1,
> +	0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d,
> +	0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6,
> +	0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920,
> +	0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a,
> +	0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033,
> +	0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593,
> +	0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
> +	0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656,
> +	0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332,
> +	0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480,
> +	0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1,
> +	0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99,
> +	0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
> +};
> +
> +/* one is for primary plane and the other is for all overlay planes */
> +static const struct dc_hw_plane_reg dc_plane_reg[] = {
> +	{
> +	.y_address		= DC_FRAMEBUFFER_ADDRESS,
> +	.u_address		= DC_FRAMEBUFFER_U_ADDRESS,
> +	.v_address		= DC_FRAMEBUFFER_V_ADDRESS,
> +	.y_stride		= DC_FRAMEBUFFER_STRIDE,
> +	.u_stride		= DC_FRAMEBUFFER_U_STRIDE,
> +	.v_stride		= DC_FRAMEBUFFER_V_STRIDE,
> +	.size			= DC_FRAMEBUFFER_SIZE,
> +	.top_left		= DC_FRAMEBUFFER_TOP_LEFT,
> +	.bottom_right	= DC_FRAMEBUFFER_BOTTOM_RIGHT,
> +	.scale_factor_x			= DC_FRAMEBUFFER_SCALE_FACTOR_X,
> +	.scale_factor_y			= DC_FRAMEBUFFER_SCALE_FACTOR_Y,
> +	.h_filter_coef_index	= DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
> +	.h_filter_coef_data		= DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
> +	.v_filter_coef_index	= DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
> +	.v_filter_coef_data		= DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
> +	.init_offset			= DC_FRAMEBUFFER_INIT_OFFSET,
> +	.color_key				= DC_FRAMEBUFFER_COLOR_KEY,
> +	.color_key_high			= DC_FRAMEBUFFER_COLOR_KEY_HIGH,
> +	.clear_value			= DC_FRAMEBUFFER_CLEAR_VALUE,
> +	.color_table_index		= DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
> +	.color_table_data		= DC_FRAMEBUFFER_COLOR_TABLE_DATA,
> +	.scale_config			= DC_FRAMEBUFFER_SCALE_CONFIG,
> +	.water_mark				= DC_FRAMEBUFFER_WATER_MARK,
> +	.degamma_index			= DC_FRAMEBUFFER_DEGAMMA_INDEX,
> +	.degamma_data			= DC_FRAMEBUFFER_DEGAMMA_DATA,
> +	.degamma_ex_data		= DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
> +	.src_global_color		= DC_FRAMEBUFFER_SRC_GLOBAL_COLOR,
> +	.dst_global_color		= DC_FRAMEBUFFER_DST_GLOBAL_COLOR,
> +	.blend_config			= DC_FRAMEBUFFER_BLEND_CONFIG,
> +	.roi_origin				= DC_FRAMEBUFFER_ROI_ORIGIN,
> +	.roi_size				= DC_FRAMEBUFFER_ROI_SIZE,
> +	.yuv_to_rgb_coef0			= DC_FRAMEBUFFER_YUVTORGB_COEF0,
> +	.yuv_to_rgb_coef1			= DC_FRAMEBUFFER_YUVTORGB_COEF1,
> +	.yuv_to_rgb_coef2			= DC_FRAMEBUFFER_YUVTORGB_COEF2,
> +	.yuv_to_rgb_coef3			= DC_FRAMEBUFFER_YUVTORGB_COEF3,
> +	.yuv_to_rgb_coef4			= DC_FRAMEBUFFER_YUVTORGB_COEF4,
> +	.yuv_to_rgb_coefd0			= DC_FRAMEBUFFER_YUVTORGB_COEFD0,
> +	.yuv_to_rgb_coefd1			= DC_FRAMEBUFFER_YUVTORGB_COEFD1,
> +	.yuv_to_rgb_coefd2			= DC_FRAMEBUFFER_YUVTORGB_COEFD2,
> +	.y_clamp_bound				= DC_FRAMEBUFFER_Y_CLAMP_BOUND,
> +	.uv_clamp_bound				= DC_FRAMEBUFFER_UV_CLAMP_BOUND,
> +	.rgb_to_rgb_coef0			= DC_FRAMEBUFFER_RGBTORGB_COEF0,
> +	.rgb_to_rgb_coef1			= DC_FRAMEBUFFER_RGBTORGB_COEF1,
> +	.rgb_to_rgb_coef2			= DC_FRAMEBUFFER_RGBTORGB_COEF2,
> +	.rgb_to_rgb_coef3			= DC_FRAMEBUFFER_RGBTORGB_COEF3,
> +	.rgb_to_rgb_coef4			= DC_FRAMEBUFFER_RGBTORGB_COEF4,
> +	},
> +	{
> +	.y_address		= DC_OVERLAY_ADDRESS,
> +	.u_address		= DC_OVERLAY_U_ADDRESS,
> +	.v_address		= DC_OVERLAY_V_ADDRESS,
> +	.y_stride		= DC_OVERLAY_STRIDE,
> +	.u_stride		= DC_OVERLAY_U_STRIDE,
> +	.v_stride		= DC_OVERLAY_V_STRIDE,
> +	.size			= DC_OVERLAY_SIZE,
> +	.top_left		= DC_OVERLAY_TOP_LEFT,
> +	.bottom_right	= DC_OVERLAY_BOTTOM_RIGHT,
> +	.scale_factor_x	= DC_OVERLAY_SCALE_FACTOR_X,
> +	.scale_factor_y	= DC_OVERLAY_SCALE_FACTOR_Y,
> +	.h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
> +	.h_filter_coef_data  = DC_OVERLAY_H_FILTER_COEF_DATA,
> +	.v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
> +	.v_filter_coef_data  = DC_OVERLAY_V_FILTER_COEF_DATA,
> +	.init_offset		 = DC_OVERLAY_INIT_OFFSET,
> +	.color_key			 = DC_OVERLAY_COLOR_KEY,
> +	.color_key_high			= DC_OVERLAY_COLOR_KEY_HIGH,
> +	.clear_value		 = DC_OVERLAY_CLEAR_VALUE,
> +	.color_table_index	 = DC_OVERLAY_COLOR_TABLE_INDEX,
> +	.color_table_data	 = DC_OVERLAY_COLOR_TABLE_DATA,
> +	.scale_config		 = DC_OVERLAY_SCALE_CONFIG,
> +	.water_mark				= DC_OVERLAY_WATER_MARK,
> +	.degamma_index		 = DC_OVERLAY_DEGAMMA_INDEX,
> +	.degamma_data		 = DC_OVERLAY_DEGAMMA_DATA,
> +	.degamma_ex_data	 = DC_OVERLAY_DEGAMMA_EX_DATA,
> +	.src_global_color	 = DC_OVERLAY_SRC_GLOBAL_COLOR,
> +	.dst_global_color	 = DC_OVERLAY_DST_GLOBAL_COLOR,
> +	.blend_config		 = DC_OVERLAY_BLEND_CONFIG,
> +	.roi_origin				= DC_OVERLAY_ROI_ORIGIN,
> +	.roi_size				= DC_OVERLAY_ROI_SIZE,
> +	.yuv_to_rgb_coef0		 = DC_OVERLAY_YUVTORGB_COEF0,
> +	.yuv_to_rgb_coef1		 = DC_OVERLAY_YUVTORGB_COEF1,
> +	.yuv_to_rgb_coef2		 = DC_OVERLAY_YUVTORGB_COEF2,
> +	.yuv_to_rgb_coef3		 = DC_OVERLAY_YUVTORGB_COEF3,
> +	.yuv_to_rgb_coef4			= DC_OVERLAY_YUVTORGB_COEF4,
> +	.yuv_to_rgb_coefd0			= DC_OVERLAY_YUVTORGB_COEFD0,
> +	.yuv_to_rgb_coefd1			= DC_OVERLAY_YUVTORGB_COEFD1,
> +	.yuv_to_rgb_coefd2			= DC_OVERLAY_YUVTORGB_COEFD2,
> +	.y_clamp_bound		 = DC_OVERLAY_Y_CLAMP_BOUND,
> +	.uv_clamp_bound		 = DC_OVERLAY_UV_CLAMP_BOUND,
> +	.rgb_to_rgb_coef0		 = DC_OVERLAY_RGBTORGB_COEF0,
> +	.rgb_to_rgb_coef1		 = DC_OVERLAY_RGBTORGB_COEF1,
> +	.rgb_to_rgb_coef2		 = DC_OVERLAY_RGBTORGB_COEF2,
> +	.rgb_to_rgb_coef3		 = DC_OVERLAY_RGBTORGB_COEF3,
> +	.rgb_to_rgb_coef4		 = DC_OVERLAY_RGBTORGB_COEF4,
> +	},
> +};
> +
> +static const u32 primary_overlay_format0[] = {

These format arrays are supposed to be roughly sorted by hardware 
preference. This gives user-space programs an idea of what the hardware 
wants. Are these 4444 and 1555 formats really preferable over their 8888 
and 2101010 variants?

> +	DRM_FORMAT_XRGB4444,
> +	DRM_FORMAT_XBGR4444,
> +	DRM_FORMAT_RGBX4444,
> +	DRM_FORMAT_BGRX4444,
> +	DRM_FORMAT_ARGB4444,
> +	DRM_FORMAT_ABGR4444,
> +	DRM_FORMAT_RGBA4444,
> +	DRM_FORMAT_BGRA4444,
> +	DRM_FORMAT_XRGB1555,
> +	DRM_FORMAT_XBGR1555,
> +	DRM_FORMAT_RGBX5551,
> +	DRM_FORMAT_BGRX5551,
> +	DRM_FORMAT_ARGB1555,
> +	DRM_FORMAT_ABGR1555,
> +	DRM_FORMAT_RGBA5551,
> +	DRM_FORMAT_BGRA5551,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_RGBX8888,
> +	DRM_FORMAT_BGRX8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGBA8888,
> +	DRM_FORMAT_BGRA8888,
> +	DRM_FORMAT_ARGB2101010,
> +	DRM_FORMAT_ABGR2101010,
> +	DRM_FORMAT_RGBA1010102,
> +	DRM_FORMAT_BGRA1010102,
> +	DRM_FORMAT_YUYV,
> +	DRM_FORMAT_YVYU,
> +	DRM_FORMAT_UYVY,
> +	DRM_FORMAT_VYUY,
> +	DRM_FORMAT_YVU420,
> +	DRM_FORMAT_YUV420,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV21,
> +	DRM_FORMAT_NV16,
> +	DRM_FORMAT_NV61,
> +	DRM_FORMAT_P010,
> +};
> +
> +static const u32 primary_overlay_format1[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_RGBX8888,
> +	DRM_FORMAT_BGRX8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGBA8888,
> +	DRM_FORMAT_BGRA8888,
> +	DRM_FORMAT_ARGB2101010,
> +	DRM_FORMAT_ABGR2101010,
> +	DRM_FORMAT_RGBA1010102,
> +	DRM_FORMAT_BGRA1010102,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV21,
> +	DRM_FORMAT_YUV444,
> +};
> +
> +static const u32 cursor_formats[] = {
> +	DRM_FORMAT_ARGB8888
> +};
> +
> +static const u64 format_modifier0[] = {
> +	DRM_FORMAT_MOD_LINEAR,
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X8),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X4),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8),
> +	DRM_FORMAT_MOD_INVALID
> +};
> +
> +static const u64 secondary_format_modifiers[] = {
> +	DRM_FORMAT_MOD_LINEAR,
> +	DRM_FORMAT_MOD_INVALID
> +};
> +
> +#define FRAC_16_16(mult, div)	 (((mult) << 16) / (div))
> +
> +static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = {

Please double-check the indention in this array.

Best regards
Thomas


-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstrasse 146, 90461 Nuernberg, Germany
GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman
HRB 36809 (AG Nuernberg)

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 5/6] drm/vs: Add KMS crtc&plane
@ 2023-11-24 13:40     ` Thomas Zimmermann
  0 siblings, 0 replies; 117+ messages in thread
From: Thomas Zimmermann @ 2023-11-24 13:40 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Maxime Ripard,
	Jagan Teki, Rob Herring, Chris Morgan, Paul Walmsley,
	Bjorn Andersson, Changhuang Liang, Jack Zhu, Palmer Dabbelt,
	Shawn Guo, christian.koenig


[-- Attachment #1.1: Type: text/plain, Size: 58876 bytes --]

Hi

Am 25.10.23 um 12:39 schrieb Keith Zhao:
[...]
> +
> +static struct drm_crtc_state *
> +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc_state *ori_state;

I have not yet seen 'ori_' being used anywhere. Typical names are 
'state' for the current state and 'new_state' for the newly created 
state.  Would be nice for consistency with other drivers.

> +	struct vs_crtc_state *state;
> +
> +	if (!crtc->state)
> +		return NULL;
> +
> +	ori_state = to_vs_crtc_state(crtc->state);
> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return NULL;
> +
> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
> +
> +	state->output_fmt = ori_state->output_fmt;
> +	state->encoder_type = ori_state->encoder_type;
> +	state->bpp = ori_state->bpp;
> +	state->underflow = ori_state->underflow;
> +
> +	return &state->base;
> +}
> +
> +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
> +					 struct drm_crtc_state *state)
> +{
> +	__drm_atomic_helper_crtc_destroy_state(state);
> +	kfree(to_vs_crtc_state(state));
> +}
> +
> +static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_enable_vblank(dc, true);
> +
> +	return 0;
> +}
> +
> +static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_enable_vblank(dc, false);
> +}
> +
> +static const struct drm_crtc_funcs vs_crtc_funcs = {
> +	.set_config		= drm_atomic_helper_set_config,
> +	.page_flip		= drm_atomic_helper_page_flip,
> +	.reset			= vs_crtc_reset,
> +	.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
> +	.atomic_destroy_state	= vs_crtc_atomic_destroy_state,
> +	.enable_vblank		= vs_crtc_enable_vblank,
> +	.disable_vblank		= vs_crtc_disable_vblank,
> +};
> +
> +static u8 cal_pixel_bits(u32 bus_format)
> +{
> +	u8 bpp;
> +
> +	switch (bus_format) {
> +	case MEDIA_BUS_FMT_RGB565_1X16:
> +	case MEDIA_BUS_FMT_UYVY8_1X16:
> +		bpp = 16;
> +		break;
> +	case MEDIA_BUS_FMT_RGB666_1X18:
> +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> +		bpp = 18;
> +		break;
> +	case MEDIA_BUS_FMT_UYVY10_1X20:
> +		bpp = 20;
> +		break;
> +	case MEDIA_BUS_FMT_BGR888_1X24:
> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +	case MEDIA_BUS_FMT_YUV8_1X24:
> +		bpp = 24;
> +		break;
> +	case MEDIA_BUS_FMT_RGB101010_1X30:
> +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> +	case MEDIA_BUS_FMT_YUV10_1X30:
> +		bpp = 30;
> +		break;
> +	default:
> +		bpp = 24;
> +		break;
> +	}
> +
> +	return bpp;
> +}
> +
> +static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
> +				  struct drm_atomic_state *state)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
> +
> +	vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
> +
> +	vs_dc_enable(dc, crtc);
> +	drm_crtc_vblank_on(crtc);
> +}
> +
> +static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
> +				   struct drm_atomic_state *state)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	drm_crtc_vblank_off(crtc);
> +
> +	vs_dc_disable(dc, crtc);
> +
> +	if (crtc->state->event && !crtc->state->active) {
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +
> +		crtc->state->event = NULL;
> +	}
> +}
> +
> +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
> +				 struct drm_atomic_state *state)
> +{
> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
> +									  crtc);
> +
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct device *dev = vs_crtc->dev;
> +	struct drm_property_blob *blob = crtc->state->gamma_lut;
> +	struct drm_color_lut *lut;
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	if (crtc_state->color_mgmt_changed) {
> +		if (blob && blob->length) {
> +			lut = blob->data;
> +			vs_dc_set_gamma(dc, crtc, lut,
> +					blob->length / sizeof(*lut));
> +			vs_dc_enable_gamma(dc, crtc, true);
> +		} else {
> +			vs_dc_enable_gamma(dc, crtc, false);
> +		}
> +	}
> +}
> +
> +static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
> +				 struct drm_atomic_state *state)
> +{
> +	struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
> +	struct drm_pending_vblank_event *event = crtc->state->event;
> +	struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
> +
> +	vs_dc_commit(dc);
> +
> +	if (event) {
> +		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
> +
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		drm_crtc_arm_vblank_event(crtc, event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +		crtc->state->event = NULL;
> +	}
> +}
> +
> +static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
> +	.atomic_check = drm_crtc_helper_atomic_check,
> +	.atomic_enable	= vs_crtc_atomic_enable,
> +	.atomic_disable = vs_crtc_atomic_disable,
> +	.atomic_begin	= vs_crtc_atomic_begin,
> +	.atomic_flush	= vs_crtc_atomic_flush,
> +};
> +
> +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
> +	{ VS_SINGLE_DC,				"single dc mode" },
> +	{ VS_MULTI_DC_PRIMARY,		"primary dc for multi dc mode" },
> +	{ VS_MULTI_DC_SECONDARY,	"secondary dc for multi dc mode" },
> +};
> +
> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
> +			       struct vs_dc_info *info)
> +{
> +	struct vs_crtc *crtc;
> +	int ret;
> +
> +	if (!info)
> +		return NULL;
> +
> +	crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
> +	if (!crtc)
> +		return NULL;
> +
> +	ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
> +					 NULL, NULL, &vs_crtc_funcs,
> +					 info->name ? info->name : NULL);
> +	if (ret)
> +		return NULL;
> +
> +	drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
> +
> +	if (info->gamma_size) {
> +		ret = drm_mode_crtc_set_gamma_size(&crtc->base,
> +						   info->gamma_size);
> +		if (ret)
> +			return NULL;
> +
> +		drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
> +					   info->gamma_size);
> +	}
> +
> +	crtc->max_bpc = info->max_bpc;
> +	crtc->color_formats = info->color_formats;
> +	return crtc;
> +}
> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h
> new file mode 100644
> index 000000000..526dd63e5
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.h
> @@ -0,0 +1,43 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_CRTC_H__
> +#define __VS_CRTC_H__
> +
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "vs_type.h"
> +
> +struct vs_crtc_state {
> +	struct drm_crtc_state base;
> +
> +	u32 output_fmt;
> +	u8 encoder_type;
> +	u8 bpp;
> +	bool underflow;
> +};
> +
> +struct vs_crtc {
> +	struct drm_crtc base;
> +	struct device *dev;
> +	unsigned int max_bpc;
> +	unsigned int color_formats;
> +};
> +
> +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
> +			       struct vs_dc_info *info);
> +
> +static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc)
> +{
> +	return container_of(crtc, struct vs_crtc, base);
> +}
> +
> +static inline struct vs_crtc_state *
> +to_vs_crtc_state(struct drm_crtc_state *state)
> +{
> +	return container_of(state, struct vs_crtc_state, base);
> +}
> +#endif /* __VS_CRTC_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
> new file mode 100644
> index 000000000..b5ab92d98
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc.c
> @@ -0,0 +1,1002 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/component.h>
> +#include <linux/clk.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/reset.h>
> +
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_blend.h>
> +#include <drm/drm_framebuffer.h>
> +#include <drm/drm_vblank.h>
> +#include <drm/vs_drm.h>
> +
> +#include "vs_dc_hw.h"
> +#include "vs_dc.h"
> +#include "vs_drv.h"
> +
> +static const char * const vout_clocks[] = {
> +	"noc_bus",
> +	"channel0",
> +	"channel1",
> +	"dc_core",
> +	"axi_core",
> +	"ahb",
> +	"hdmi_tx",
> +	"dc_parent",
> +
> +};
> +
> +static const char * const vout_resets[] = {
> +	"axi",
> +	"ahb",
> +	"core",
> +};
> +
> +static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb)
> +{
> +	u8 f = FORMAT_A8R8G8B8;
> +
> +	switch (format) {
> +	case DRM_FORMAT_XRGB4444:
> +	case DRM_FORMAT_RGBX4444:
> +	case DRM_FORMAT_XBGR4444:
> +	case DRM_FORMAT_BGRX4444:
> +		f = FORMAT_X4R4G4B4;
> +		break;
> +	case DRM_FORMAT_ARGB4444:
> +	case DRM_FORMAT_RGBA4444:
> +	case DRM_FORMAT_ABGR4444:
> +	case DRM_FORMAT_BGRA4444:
> +		f = FORMAT_A4R4G4B4;
> +		break;
> +	case DRM_FORMAT_XRGB1555:
> +	case DRM_FORMAT_RGBX5551:
> +	case DRM_FORMAT_XBGR1555:
> +	case DRM_FORMAT_BGRX5551:
> +		f = FORMAT_X1R5G5B5;
> +		break;
> +	case DRM_FORMAT_ARGB1555:
> +	case DRM_FORMAT_RGBA5551:
> +	case DRM_FORMAT_ABGR1555:
> +	case DRM_FORMAT_BGRA5551:
> +		f = FORMAT_A1R5G5B5;
> +		break;
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		f = FORMAT_R5G6B5;
> +		break;
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_RGBX8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_BGRX8888:
> +		f = FORMAT_X8R8G8B8;
> +		break;
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_RGBA8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_BGRA8888:
> +		f = FORMAT_A8R8G8B8;
> +		break;
> +	case DRM_FORMAT_YUYV:
> +	case DRM_FORMAT_YVYU:
> +		f = FORMAT_YUY2;
> +		break;
> +	case DRM_FORMAT_UYVY:
> +	case DRM_FORMAT_VYUY:
> +		f = FORMAT_UYVY;
> +		break;
> +	case DRM_FORMAT_YUV420:
> +	case DRM_FORMAT_YVU420:
> +		f = FORMAT_YV12;
> +		break;
> +	case DRM_FORMAT_NV21:
> +		f = FORMAT_NV12;
> +		break;
> +	case DRM_FORMAT_NV16:
> +	case DRM_FORMAT_NV61:
> +		f = FORMAT_NV16;
> +		break;
> +	case DRM_FORMAT_P010:
> +		f = FORMAT_P010;
> +		break;
> +	case DRM_FORMAT_ARGB2101010:
> +	case DRM_FORMAT_RGBA1010102:
> +	case DRM_FORMAT_ABGR2101010:
> +	case DRM_FORMAT_BGRA1010102:
> +		f = FORMAT_A2R10G10B10;
> +		break;
> +	case DRM_FORMAT_NV12:
> +		f = FORMAT_NV12;
> +		break;
> +	case DRM_FORMAT_YUV444:
> +		f = FORMAT_YUV444;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	fb->format = f;
> +}
> +
> +static inline void update_swizzle(u32 format, struct dc_hw_fb *fb)
> +{
> +	fb->swizzle = SWIZZLE_ARGB;
> +	fb->uv_swizzle = 0;
> +
> +	switch (format) {
> +	case DRM_FORMAT_RGBX4444:
> +	case DRM_FORMAT_RGBA4444:
> +	case DRM_FORMAT_RGBX5551:
> +	case DRM_FORMAT_RGBA5551:
> +	case DRM_FORMAT_RGBX8888:
> +	case DRM_FORMAT_RGBA8888:
> +	case DRM_FORMAT_RGBA1010102:
> +		fb->swizzle = SWIZZLE_RGBA;
> +		break;
> +	case DRM_FORMAT_XBGR4444:
> +	case DRM_FORMAT_ABGR4444:
> +	case DRM_FORMAT_XBGR1555:
> +	case DRM_FORMAT_ABGR1555:
> +	case DRM_FORMAT_BGR565:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_ABGR2101010:
> +		fb->swizzle = SWIZZLE_ABGR;
> +		break;
> +	case DRM_FORMAT_BGRX4444:
> +	case DRM_FORMAT_BGRA4444:
> +	case DRM_FORMAT_BGRX5551:
> +	case DRM_FORMAT_BGRA5551:
> +	case DRM_FORMAT_BGRX8888:
> +	case DRM_FORMAT_BGRA8888:
> +	case DRM_FORMAT_BGRA1010102:
> +		fb->swizzle = SWIZZLE_BGRA;
> +		break;
> +	case DRM_FORMAT_YVYU:
> +	case DRM_FORMAT_VYUY:
> +	case DRM_FORMAT_NV21:
> +	case DRM_FORMAT_NV61:
> +		fb->uv_swizzle = 1;
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +static inline void update_watermark(struct drm_property_blob *watermark,
> +				    struct dc_hw_fb *fb)
> +{
> +	struct drm_vs_watermark *data;
> +
> +	fb->water_mark = 0;
> +
> +	if (watermark) {
> +		data = watermark->data;
> +		fb->water_mark = data->watermark & 0xFFFFF;
> +	}
> +}
> +
> +static inline u8 to_vs_rotation(unsigned int rotation)
> +{
> +	u8 rot;
> +
> +	switch (rotation & DRM_MODE_REFLECT_MASK) {
> +	case DRM_MODE_REFLECT_X:
> +		rot = FLIP_X;
> +		return rot;
> +	case DRM_MODE_REFLECT_Y:
> +		rot = FLIP_Y;
> +		return rot;
> +	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
> +		rot = FLIP_XY;
> +		return rot;
> +	default:
> +		break;
> +	}
> +
> +	switch (rotation & DRM_MODE_ROTATE_MASK) {
> +	case DRM_MODE_ROTATE_0:
> +		rot = ROT_0;
> +		break;
> +	case DRM_MODE_ROTATE_90:
> +		rot = ROT_90;
> +		break;
> +	case DRM_MODE_ROTATE_180:
> +		rot = ROT_180;
> +		break;
> +	case DRM_MODE_ROTATE_270:
> +		rot = ROT_270;
> +		break;
> +	default:
> +		rot = ROT_0;
> +		break;
> +	}
> +
> +	return rot;
> +}
> +
> +static inline u8 to_vs_yuv_color_space(u32 color_space)
> +{
> +	u8 cs;
> +
> +	switch (color_space) {
> +	case DRM_COLOR_YCBCR_BT601:
> +		cs = COLOR_SPACE_601;
> +		break;
> +	case DRM_COLOR_YCBCR_BT709:
> +		cs = COLOR_SPACE_709;
> +		break;
> +	case DRM_COLOR_YCBCR_BT2020:
> +		cs = COLOR_SPACE_2020;
> +		break;
> +	default:
> +		cs = COLOR_SPACE_601;
> +		break;
> +	}
> +
> +	return cs;
> +}
> +
> +static inline u8 to_vs_tile_mode(u64 modifier)
> +{
> +	if (modifier == DRM_FORMAT_MOD_VIVANTE_TILED)
> +		return DC_TILE_MODE4X4;
> +
> +	return (u8)(modifier & DRM_FORMAT_MOD_VERISILICON_NORM_MODE_MASK);
> +}
> +
> +static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +	u8 panel_num = dc->hw.info->panel_num;
> +	u32 index = drm_crtc_index(crtc);
> +	int i;
> +
> +	for (i = 0; i < panel_num; i++) {
> +		if (index == dc->crtc[i]->base.index)
> +			return i;
> +	}
> +
> +	return 0;
> +}
> +
> +static void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
> +					  unsigned int alignment)
> +{
> +	struct vs_drm_device *priv = to_vs_drm_private(drm_dev);
> +
> +	if (alignment > priv->pitch_alignment)
> +		priv->pitch_alignment = alignment;
> +}
> +
> +static int plda_clk_rst_init(struct device *dev)
> +{
> +	int ret = 0;
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	ret = clk_bulk_prepare_enable(dc->nclks, dc->clk_vout);
> +	if (ret) {
> +		dev_err(dev, "failed to enable clocks\n");
> +		return ret;
> +	}
> +
> +	ret = reset_control_bulk_deassert(dc->nrsts, dc->rst_vout);
> +	return ret;
> +}
> +
> +static void plda_clk_rst_deinit(struct device *dev)
> +{
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	reset_control_bulk_assert(dc->nrsts, dc->rst_vout);
> +	clk_bulk_disable_unprepare(dc->nclks, dc->clk_vout);
> +}
> +
> +static void dc_deinit(struct device *dev)
> +{
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +	dc_hw_enable_interrupt(&dc->hw, 0);
> +	dc_hw_deinit(&dc->hw);
> +	plda_clk_rst_deinit(dev);
> +}
> +
> +static int dc_init(struct device *dev)
> +{
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +	int ret;
> +
> +	dc->first_frame = true;
> +
> +	ret = plda_clk_rst_init(dev);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to init dc clk reset: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = dc_hw_init(&dc->hw);
> +	if (ret) {
> +		dev_err(dev, "failed to init DC HW\n");
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +	struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	struct dc_hw_display display;
> +
> +	display.bus_format = crtc_state->output_fmt;
> +	display.h_active = mode->hdisplay;
> +	display.h_total = mode->htotal;
> +	display.h_sync_start = mode->hsync_start;
> +	display.h_sync_end = mode->hsync_end;
> +	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> +		display.h_sync_polarity = true;
> +	else
> +		display.h_sync_polarity = false;
> +
> +	display.v_active = mode->vdisplay;
> +	display.v_total = mode->vtotal;
> +	display.v_sync_start = mode->vsync_start;
> +	display.v_sync_end = mode->vsync_end;
> +
> +	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> +		display.v_sync_polarity = true;
> +	else
> +		display.v_sync_polarity = false;
> +
> +	display.id = to_vs_display_id(dc, crtc);
> +
> +	display.enable = true;
> +
> +	if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) {
> +		dc_hw_set_out(&dc->hw, OUT_DPI, display.id);
> +		clk_set_rate(dc->clk_vout[CLK_VOUT_SOC_PIX].clk, mode->clock * 1000);
> +		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX1].clk,
> +			       dc->clk_vout[CLK_VOUT_SOC_PIX].clk);
> +	} else {
> +		dc_hw_set_out(&dc->hw, OUT_DP, display.id);
> +		clk_set_parent(dc->clk_vout[CLK_VOUT_PIX0].clk,
> +			       dc->clk_vout[CLK_VOUT_HDMI_PIX].clk);
> +	}
> +
> +	dc_hw_setup_display(&dc->hw, &display);
> +}
> +
> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +	struct dc_hw_display display;
> +
> +	display.id = to_vs_display_id(dc, crtc);
> +	display.enable = false;
> +
> +	dc_hw_setup_display(&dc->hw, &display);
> +}
> +
> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> +		     struct drm_color_lut *lut, unsigned int size)
> +{
> +	u16 i, r, g, b;
> +	u8 bits, id;
> +
> +	if (size != dc->hw.info->gamma_size) {
> +		drm_err(crtc->dev, "gamma size does not match!\n");
> +		return;
> +	}
> +
> +	id = to_vs_display_id(dc, crtc);
> +
> +	bits = dc->hw.info->gamma_bits;
> +	for (i = 0; i < size; i++) {
> +		r = drm_color_lut_extract(lut[i].red, bits);
> +		g = drm_color_lut_extract(lut[i].green, bits);
> +		b = drm_color_lut_extract(lut[i].blue, bits);
> +		dc_hw_update_gamma(&dc->hw, id, i, r, g, b);
> +	}
> +}
> +
> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> +			bool enable)
> +{
> +	u8 id;
> +
> +	id = to_vs_display_id(dc, crtc);
> +	dc_hw_enable_gamma(&dc->hw, id, enable);
> +}
> +
> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable)
> +{
> +	dc_hw_enable_interrupt(&dc->hw, enable);
> +}
> +
> +static u32 calc_factor(u32 src, u32 dest)
> +{
> +	u32 factor = 1 << 16;
> +
> +	if (src > 1 && dest > 1)
> +		factor = ((src - 1) << 16) / (dest - 1);
> +
> +	return factor;
> +}
> +
> +static void update_scale(struct drm_plane_state *state, struct dc_hw_roi *roi,
> +			 struct dc_hw_scale *scale)
> +{
> +	int dst_w = drm_rect_width(&state->dst);
> +	int dst_h = drm_rect_height(&state->dst);
> +	int src_w, src_h, temp;
> +
> +	scale->enable = false;
> +
> +	if (roi->enable) {
> +		src_w = roi->width;
> +		src_h = roi->height;
> +	} else {
> +		src_w = drm_rect_width(&state->src) >> 16;
> +		src_h = drm_rect_height(&state->src) >> 16;
> +	}
> +
> +	if (drm_rotation_90_or_270(state->rotation)) {
> +		temp = src_w;
> +		src_w = src_h;
> +		src_h = temp;
> +	}
> +
> +	if (src_w != dst_w) {
> +		scale->scale_factor_x = calc_factor(src_w, dst_w);
> +		scale->enable = true;
> +	} else {
> +		scale->scale_factor_x = 1 << 16;
> +	}
> +	if (src_h != dst_h) {
> +		scale->scale_factor_y = calc_factor(src_h, dst_h);
> +		scale->enable = true;
> +	} else {
> +		scale->scale_factor_y = 1 << 16;
> +	}
> +}
> +
> +static void update_fb(struct vs_plane *plane, u8 display_id,
> +		      struct dc_hw_fb *fb, struct drm_plane_state *state)
> +{
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_framebuffer *drm_fb = state->fb;
> +	struct drm_rect *src = &state->src;
> +
> +	fb->display_id = display_id;
> +	fb->y_address = plane_state->dma_addr[0];
> +	fb->y_stride = drm_fb->pitches[0];
> +	if (drm_fb->format->format == DRM_FORMAT_YVU420) {
> +		fb->u_address = plane_state->dma_addr[2];
> +		fb->v_address = plane_state->dma_addr[1];
> +		fb->u_stride = drm_fb->pitches[2];
> +		fb->v_stride = drm_fb->pitches[1];
> +	} else {
> +		fb->u_address = plane_state->dma_addr[1];
> +		fb->v_address = plane_state->dma_addr[2];
> +		fb->u_stride = drm_fb->pitches[1];
> +		fb->v_stride = drm_fb->pitches[2];
> +	}
> +	fb->width = drm_rect_width(src) >> 16;
> +	fb->height = drm_rect_height(src) >> 16;
> +	fb->tile_mode = to_vs_tile_mode(drm_fb->modifier);
> +	fb->rotation = to_vs_rotation(state->rotation);
> +	fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding);
> +	fb->zpos = state->zpos;
> +	fb->enable = state->visible;
> +	update_format(drm_fb->format->format, drm_fb->modifier, fb);
> +	update_swizzle(drm_fb->format->format, fb);
> +	update_watermark(plane_state->watermark, fb);
> +	plane_state->status.tile_mode = fb->tile_mode;
> +}
> +
> +static void update_degamma(struct vs_dc *dc, struct vs_plane *plane,
> +			   struct vs_plane_state *plane_state)
> +{
> +	dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
> +	plane_state->degamma_changed = false;
> +}
> +
> +static void update_roi(struct vs_dc *dc, u8 id,
> +		       struct vs_plane_state *plane_state,
> +		       struct dc_hw_roi *roi,
> +		       struct drm_plane_state *state)
> +{
> +	struct drm_vs_roi *data;
> +	struct drm_rect *src = &state->src;
> +	u16 src_w = drm_rect_width(src) >> 16;
> +	u16 src_h = drm_rect_height(src) >> 16;
> +
> +	if (plane_state->roi) {
> +		data = plane_state->roi->data;
> +
> +		if (data->enable) {
> +			roi->x = data->roi_x;
> +			roi->y = data->roi_y;
> +			roi->width = (data->roi_x + data->roi_w > src_w) ?
> +						 (src_w - data->roi_x) : data->roi_w;
> +			roi->height = (data->roi_y + data->roi_h > src_h) ?
> +						  (src_h - data->roi_y) : data->roi_h;
> +			roi->enable = true;
> +		} else {
> +			roi->enable = false;
> +		}
> +
> +		dc_hw_update_roi(&dc->hw, id, roi);
> +	} else {
> +		roi->enable = false;
> +	}
> +}
> +
> +static void update_color_mgmt(struct vs_dc *dc, u8 id,
> +			      struct dc_hw_fb *fb,
> +			      struct vs_plane_state *plane_state)
> +{
> +	struct drm_vs_color_mgmt *data;
> +	struct dc_hw_colorkey colorkey;
> +
> +	if (plane_state->color_mgmt) {
> +		data = plane_state->color_mgmt->data;
> +
> +		fb->clear_enable = data->clear_enable;
> +		fb->clear_value = data->clear_value;
> +
> +		if (data->colorkey > data->colorkey_high)
> +			data->colorkey = data->colorkey_high;
> +
> +		colorkey.colorkey = data->colorkey;
> +		colorkey.colorkey_high = data->colorkey_high;
> +		colorkey.transparency = (data->transparency) ?
> +				DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE;
> +		dc_hw_update_colorkey(&dc->hw, id, &colorkey);
> +	}
> +}
> +
> +static void update_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			 struct drm_plane *drm_plane,
> +			 struct drm_atomic_state *drm_state)
> +{
> +	struct dc_hw_fb fb = {0};
> +	struct dc_hw_scale scale;
> +	struct dc_hw_position pos;
> +	struct dc_hw_blend blend;
> +	struct dc_hw_roi roi;
> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> +									   drm_plane);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_rect *dest = &state->dst;
> +	bool dec_enable = false;
> +	u8 display_id = 0;
> +
> +	display_id = to_vs_display_id(dc, state->crtc);
> +	update_fb(plane, display_id, &fb, state);
> +	fb.dec_enable = dec_enable;
> +
> +	update_roi(dc, plane->id, plane_state, &roi, state);
> +
> +	update_scale(state, &roi, &scale);
> +
> +	if (plane_state->degamma_changed)
> +		update_degamma(dc, plane, plane_state);
> +
> +	pos.start_x = dest->x1;
> +	pos.start_y = dest->y1;
> +	pos.end_x = dest->x2;
> +	pos.end_y = dest->y2;
> +
> +	blend.alpha = (u8)(state->alpha >> 8);
> +	blend.blend_mode = (u8)(state->pixel_blend_mode);
> +
> +	update_color_mgmt(dc, plane->id, &fb, plane_state);
> +
> +	dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
> +}
> +
> +static void update_qos(struct vs_dc *dc, struct vs_plane *plane,
> +		       struct drm_plane *drm_plane,
> +		       struct drm_atomic_state *drm_state)
> +{
> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> +									   drm_plane);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_vs_watermark *data;
> +	struct dc_hw_qos qos;
> +
> +	if (plane_state->watermark) {
> +		data = plane_state->watermark->data;
> +
> +		if (data->qos_high) {
> +			if (data->qos_low > data->qos_high)
> +				data->qos_low = data->qos_high;
> +
> +			qos.low_value = data->qos_low & 0x0F;
> +			qos.high_value = data->qos_high & 0x0F;
> +			dc_hw_update_qos(&dc->hw, &qos);
> +		}
> +	}
> +}
> +
> +static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor)
> +{
> +	u8 size_type;
> +
> +	switch (state->crtc_w) {
> +	case 32:
> +		size_type = CURSOR_SIZE_32X32;
> +		break;
> +	case 64:
> +		size_type = CURSOR_SIZE_64X64;
> +		break;
> +	default:
> +		size_type = CURSOR_SIZE_32X32;
> +		break;
> +	}
> +
> +	cursor->size = size_type;
> +}
> +
> +static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +				struct drm_plane *drm_plane,
> +				struct drm_atomic_state *drm_state)
> +{
> +	struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
> +								       drm_plane);
> +	struct vs_plane_state *plane_state = to_vs_plane_state(state);
> +	struct drm_framebuffer *drm_fb = state->fb;
> +	struct dc_hw_cursor cursor;
> +
> +	cursor.address = plane_state->dma_addr[0];
> +	cursor.x = state->crtc_x;
> +	cursor.y = state->crtc_y;
> +	cursor.hot_x = drm_fb->hot_x;
> +	cursor.hot_y = drm_fb->hot_y;
> +	cursor.display_id = to_vs_display_id(dc, state->crtc);
> +	update_cursor_size(state, &cursor);
> +	cursor.enable = true;
> +
> +	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
> +}
> +
> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			struct drm_plane *drm_plane,
> +			struct drm_atomic_state *drm_state)
> +{
> +	update_plane(dc, plane, drm_plane, drm_state);
> +	update_qos(dc, plane, drm_plane, drm_state);
> +}
> +
> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			       struct drm_plane *drm_plane,
> +			       struct drm_atomic_state *drm_state)
> +{
> +	update_cursor_plane(dc, plane, drm_plane, drm_state);
> +}
> +
> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			 struct drm_plane_state *old_state)
> +{
> +	struct dc_hw_fb fb = {0};
> +
> +	fb.enable = false;
> +	dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL, NULL, NULL);
> +}
> +
> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +				struct drm_plane_state *old_state)
> +{
> +	struct dc_hw_cursor cursor = {0};
> +
> +	cursor.enable = false;
> +	cursor.display_id = to_vs_display_id(dc, old_state->crtc);
> +	dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
> +}
> +
> +static bool vs_dc_mod_supported(const struct vs_plane_info *plane_info,
> +				u64 modifier)
> +{
> +	const u64 *mods;
> +
> +	if (!plane_info->modifiers)
> +		return false;
> +
> +	for (mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID; mods++) {
> +		if (*mods == modifier)
> +			return true;
> +	}
> +
> +	return false;
> +}
> +
> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
> +		      struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> +										 plane);
> +
> +	struct drm_framebuffer *fb = new_plane_state->fb;
> +	const struct vs_plane_info *plane_info;
> +	struct drm_crtc *crtc = new_plane_state->crtc;
> +	struct drm_crtc_state *crtc_state;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +
> +	plane_info = &dc->hw.info->planes[vs_plane->id];
> +
> +	if (fb->width < plane_info->min_width ||
> +	    fb->width > plane_info->max_width ||
> +	    fb->height < plane_info->min_height ||
> +	    fb->height > plane_info->max_height)
> +		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n",
> +			     vs_plane->id);
> +
> +	if (!vs_dc_mod_supported(plane_info, fb->modifier)) {
> +		drm_err(plane->dev, "unsupported modifier on plane%d.\n", vs_plane->id);
> +		return -EINVAL;
> +	}
> +
> +	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
> +	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
> +						  plane_info->min_scale,
> +						  plane_info->max_scale,
> +						  true, true);
> +}
> +
> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
> +			     struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
> +									   plane);
> +	struct drm_framebuffer *fb = new_plane_state->fb;
> +	const struct vs_plane_info *plane_info;
> +	struct drm_crtc *crtc = new_plane_state->crtc;
> +	struct drm_crtc_state *crtc_state;
> +	struct vs_plane *vs_plane = to_vs_plane(plane);
> +
> +	plane_info = &dc->hw.info->planes[vs_plane->id];
> +
> +	if (fb->width < plane_info->min_width ||
> +	    fb->width > plane_info->max_width ||
> +	    fb->height < plane_info->min_height ||
> +	    fb->height > plane_info->max_height)
> +		drm_err_once(plane->dev, "buffer size may not support on plane%d.\n", vs_plane->id);
> +
> +	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
> +	if (IS_ERR(crtc_state))
> +		return -EINVAL;
> +
> +	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
> +						plane_info->min_scale,
> +						plane_info->max_scale,
> +						true, true);
> +}
> +
> +static void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
> +{
> +	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
> +
> +	drm_crtc_handle_vblank(crtc);
> +
> +	vs_crtc_state->underflow = underflow;
> +}
> +
> +static irqreturn_t dc_isr(int irq, void *data)
> +{
> +	struct vs_dc *dc = data;
> +	struct vs_dc_info *dc_info = dc->hw.info;
> +	u32 i, ret;
> +
> +	ret = dc_hw_get_interrupt(&dc->hw);
> +
> +	for (i = 0; i < dc_info->panel_num; i++)
> +		vs_crtc_handle_vblank(&dc->crtc[i]->base, dc_hw_check_underflow(&dc->hw));
> +
> +	return IRQ_HANDLED;
> +}
> +
> +void vs_dc_commit(struct vs_dc *dc)
> +{
> +	dc_hw_enable_shadow_register(&dc->hw, false);
> +
> +	dc_hw_commit(&dc->hw);
> +
> +	if (dc->first_frame)
> +		dc->first_frame = false;
> +
> +	dc_hw_enable_shadow_register(&dc->hw, true);
> +}
> +
> +static int dc_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct drm_device *drm_dev = data;
> +	struct vs_dc *dc = dev_get_drvdata(dev);
> +	struct device_node *port;
> +	struct vs_crtc *crtc;
> +	struct vs_dc_info *dc_info;
> +	struct vs_plane *plane;
> +	struct vs_plane_info *plane_info;
> +	int i, ret;
> +	u32 ctrc_mask = 0;
> +
> +	if (!drm_dev || !dc) {
> +		dev_err(dev, "devices are not created.\n");
> +		return -ENODEV;
> +	}
> +
> +	ret = dc_init(dev);
> +	if (ret < 0) {
> +		drm_err(drm_dev, "Failed to initialize DC hardware.\n");
> +		return ret;
> +	}
> +
> +	port = of_get_child_by_name(dev->of_node, "port");
> +	if (!port) {
> +		drm_err(drm_dev, "no port node found\n");
> +		return -ENODEV;
> +	}
> +	of_node_put(port);
> +
> +	dc_info = dc->hw.info;
> +
> +	for (i = 0; i < dc_info->panel_num; i++) {
> +		crtc = vs_crtc_create(drm_dev, dc_info);
> +		if (!crtc) {
> +			drm_err(drm_dev, "Failed to create CRTC.\n");
> +			ret = -ENOMEM;
> +			return ret;
> +		}
> +
> +		crtc->base.port = port;
> +		crtc->dev = dev;
> +		dc->crtc[i] = crtc;
> +		ctrc_mask |= drm_crtc_mask(&crtc->base);
> +	}
> +
> +	for (i = 0; i < dc_info->plane_num; i++) {
> +		plane_info = (struct vs_plane_info *)&dc_info->planes[i];
> +
> +		if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
> +						drm_crtc_mask(&dc->crtc[0]->base));
> +		} else if (!strcmp(plane_info->name, "Primary_1") ||
> +				   !strcmp(plane_info->name, "Cursor_1")) {
> +			plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
> +						drm_crtc_mask(&dc->crtc[1]->base));

Do you somehow verify that dc->crtc[0] and dc->crtc[1] are not NULL here?

> +		} else {
> +			plane = vs_plane_create(drm_dev, plane_info,
> +						dc_info->layer_num, ctrc_mask);
> +		}

In addition to Maxime's comment, I think it would be easier to read if 
you only set the plane's crtc make in these if-else branches and then 
call vs_plane_create() with the result. Like this

   plane_crtc_mask = crtc_mask;

   if (primary || cursor)
     plane_crtc_mask = drm_crtc_mask(&dc->crtc[0]->base)
   else if (primary_1 || cursor_1)
     plane_crtc_mask = drm_crtc_mask(&dc->crtc[1]->base)

   vs_plane_create(..., plane_crtc_mask)

> +
> +		if (IS_ERR(plane)) {
> +			dev_err(dev, "failed to construct plane\n");
> +			return PTR_ERR(plane);
> +		}
> +
> +		plane->id = i;
> +		dc->planes[i].id = plane_info->id;
> +
> +		if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
> +			if (!strcmp(plane_info->name, "Primary"))
> +				dc->crtc[0]->base.primary = &plane->base;
> +			else
> +				dc->crtc[1]->base.primary = &plane->base;
> +			drm_dev->mode_config.min_width = plane_info->min_width;
> +			drm_dev->mode_config.min_height =
> +							plane_info->min_height;
> +			drm_dev->mode_config.max_width = plane_info->max_width;
> +			drm_dev->mode_config.max_height =
> +							plane_info->max_height;
> +		}

If you have multiple primary planes, the later planes overwrite the 
settings from the earlier ones. Is that intentional?

> +
> +		if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
> +			if (!strcmp(plane_info->name, "Cursor"))
> +				dc->crtc[0]->base.cursor = &plane->base;
> +			else
> +				dc->crtc[1]->base.cursor = &plane->base;
> +			drm_dev->mode_config.cursor_width =
> +							plane_info->max_width;
> +			drm_dev->mode_config.cursor_height =
> +							plane_info->max_height;
> +		}

Same question about overwriting as for the primary planes?

> +	}
> +
> +	vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
> +	return 0;
> +}
> +
> +static void dc_unbind(struct device *dev, struct device *master, void *data)
> +{
> +	dc_deinit(dev);
> +}
> +
> +const struct component_ops dc_component_ops = {
> +	.bind = dc_bind,
> +	.unbind = dc_unbind,
> +};
> +
> +static const struct of_device_id dc_driver_dt_match[] = {
> +	{ .compatible = "starfive,jh7110-dc8200", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
> +
> +static int dc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct vs_dc *dc;
> +	int irq, ret, i;
> +
> +	dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
> +	if (!dc)
> +		return -ENOMEM;
> +
> +	dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(dc->hw.hi_base))
> +		return PTR_ERR(dc->hw.hi_base);
> +
> +	dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
> +	if (IS_ERR(dc->hw.reg_base))
> +		return PTR_ERR(dc->hw.reg_base);
> +
> +	dc->nclks = ARRAY_SIZE(dc->clk_vout);
> +	for (i = 0; i < dc->nclks; ++i)
> +		dc->clk_vout[i].id = vout_clocks[i];
> +	ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
> +	if (ret) {
> +		dev_err(dev, "Failed to get clk controls\n");
> +		return ret;
> +	}
> +
> +	dc->nrsts = ARRAY_SIZE(dc->rst_vout);
> +	for (i = 0; i < dc->nrsts; ++i)
> +		dc->rst_vout[i].id = vout_resets[i];
> +	ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
> +						 dc->rst_vout);
> +	if (ret) {
> +		dev_err(dev, "Failed to get reset controls\n");
> +		return ret;
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +
> +	ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to install irq:%u.\n", irq);
> +		return ret;
> +	}
> +
> +	dev_set_drvdata(dev, dc);
> +
> +	return component_add(dev, &dc_component_ops);
> +}
> +
> +static int dc_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +
> +	component_del(dev, &dc_component_ops);
> +
> +	dev_set_drvdata(dev, NULL);
> +
> +	return 0;
> +}
> +
> +struct platform_driver dc_platform_driver = {
> +	.probe = dc_probe,
> +	.remove = dc_remove,
> +	.driver = {
> +		.name = "vs-dc",
> +		.of_match_table = of_match_ptr(dc_driver_dt_match),
> +	},
> +};
> +
> +MODULE_AUTHOR("StarFive Corporation");
> +MODULE_DESCRIPTION("VeriSilicon DC Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
> new file mode 100644
> index 000000000..8e96fd32c
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc.h
> @@ -0,0 +1,80 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DC_H__
> +#define __VS_DC_H__
> +
> +#include <linux/clk.h>
> +#include <linux/mm_types.h>
> +#include <linux/reset.h>
> +
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_modes.h>
> +
> +#include "vs_crtc.h"
> +#include "vs_dc_hw.h"
> +#include "vs_plane.h"
> +
> +struct vs_dc_plane {
> +	enum dc_hw_plane_id id;
> +};
> +
> +enum vout_clk {
> +	CLK_VOUT_NOC_DISP = 0,
> +	CLK_VOUT_PIX0,
> +	CLK_VOUT_PIX1,
> +	CLK_VOUT_CORE,
> +	CLK_VOUT_AXI,
> +	CLK_VOUT_AHB,
> +	CLK_VOUT_HDMI_PIX,
> +	CLK_VOUT_SOC_PIX,
> +	CLK_VOUT_NUM
> +};
> +
> +enum rst_vout {
> +	RST_VOUT_AXI = 0,
> +	RST_VOUT_AHB,
> +	RST_VOUT_CORE,
> +	RST_VOUT_NUM
> +};
> +
> +struct vs_dc {
> +	struct vs_crtc		*crtc[DC_DISPLAY_NUM];
> +	struct dc_hw		hw;
> +	bool			first_frame;
> +
> +	struct vs_dc_plane	planes[PLANE_NUM];
> +	struct clk_bulk_data	clk_vout[CLK_VOUT_NUM];
> +	int			nclks;
> +	struct reset_control_bulk_data rst_vout[RST_VOUT_NUM];
> +	int			nrsts;
> +};
> +
> +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc);
> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc);
> +
> +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc,
> +		     struct drm_color_lut *lut, unsigned int size);
> +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc, bool enable);
> +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable);
> +void vs_dc_commit(struct vs_dc *dc);
> +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			struct drm_plane *drm_plane,
> +			struct drm_atomic_state *drm_state);
> +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			 struct drm_plane_state *old_state);
> +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane,
> +		      struct drm_atomic_state *state);
> +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +			       struct drm_plane *drm_plane,
> +			       struct drm_atomic_state *drm_state);
> +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
> +				struct drm_plane_state *old_state);
> +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
> +			     struct drm_atomic_state *state);
> +
> +extern struct platform_driver dc_platform_driver;
> +
> +#endif /* __VS_DC_H__ */
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
> new file mode 100644
> index 000000000..e5acb7eb4
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
> @@ -0,0 +1,1959 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#include <linux/bits.h>
> +#include <linux/io.h>
> +#include <linux/media-bus-format.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_blend.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/vs_drm.h>
> +
> +#include "vs_dc_hw.h"
> +#include "vs_type.h"
> +
> +static const u32 horkernel[] = {
> +	0x00000000, 0x20000000, 0x00002000, 0x00000000,
> +	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
> +	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
> +	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
> +	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
> +	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
> +	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
> +	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
> +	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
> +	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
> +	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
> +	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
> +	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
> +	0x00000000, 0x00000000, 0x00000000, 0x01470000,
> +	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
> +	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
> +	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
> +	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
> +	0x00000000, 0x00000000, 0x00004000, 0x00000000,
> +	0x00000000, 0x00000000, 0x20002000, 0x00000000,
> +	0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
> +	0x00000000, 0x00000000, 0x00000000, 0x27e1181f,
> +	0x00000000, 0x00000000, 0x00000000, 0x14680000,
> +	0x00002b98, 0x00000000, 0x00000000, 0x00000000,
> +	0x2f1010f0, 0x00000000, 0x00000000, 0x00000000,
> +	0x0dc70000, 0x00003239, 0x00000000, 0x00000000,
> +	0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
> +	0x00000000, 0x087f0000, 0x00003781, 0x00000000,
> +	0x00000000, 0x00000000, 0x399a0666, 0x00000000,
> +	0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
> +	0x00000000, 0x00000000, 0x00000000, 0x3cc4033c,
> +	0x00000000, 0x00000000, 0x00000000, 0x021f0000,
> +};
> +
> +#define H_COEF_SIZE (sizeof(horkernel) / sizeof(u32))

Either drop this in favor of the regular ARRAY_SIZE() macro, or hardcode 
the size if it's a fixed hardware setting.

> +
> +static const u32 verkernel[] = {
> +	0x00000000, 0x20000000, 0x00002000, 0x00000000,
> +	0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
> +	0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
> +	0x00000000, 0x00000000, 0x00000000, 0x2b981468,
> +	0x00000000, 0x00000000, 0x00000000, 0x10f00000,
> +	0x00002f10, 0x00000000, 0x00000000, 0x00000000,
> +	0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
> +	0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
> +	0x00000000, 0x3781087f, 0x00000000, 0x00000000,
> +	0x00000000, 0x06660000, 0x0000399a, 0x00000000,
> +	0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
> +	0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
> +	0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
> +	0x00000000, 0x00000000, 0x00000000, 0x01470000,
> +	0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
> +	0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
> +	0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
> +	0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
> +	0x00000000, 0x00000000, 0x00004000, 0x00000000,
> +	0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab,
> +	0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
> +	0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +	0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
> +};
> +
> +#define V_COEF_SIZE (sizeof(verkernel) / sizeof(u32))

Same as H_COEF_SIZE;

> +
> +/*
> + * RGB 709->2020 conversion parameters
> + */
> +static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = {

'static const' here and for the arrays below.

> +	10279,	5395,	709,
> +	1132,	15065,	187,
> +	269,	1442,	14674
> +};
> +
> +/*
> + * YUV601 to RGB conversion parameters
> + * YUV2RGB[0]  - [8] : C0 - C8;
> + * YUV2RGB[9]  - [11]: D0 - D2;
> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> + */
> +static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> +	1196,	0,			1640,	1196,
> +	-404,	-836,		1196,	2076,
> +	0,		-916224,	558336,	-1202944,
> +	64,		 940,		64,		960

Might be my email reader, but indention looks off here.

> +};
> +
> +/*
> + * YUV709 to RGB conversion parameters
> + * YUV2RGB[0]  - [8] : C0 - C8;
> + * YUV2RGB[9]  - [11]: D0 - D2;
> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> + */
> +static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> +	1196,		0,		1844,	1196,
> +	-220,		-548,	1196,	2172,
> +	0,			-1020672, 316672,  -1188608,
> +	64,			940,		64,		960

Indention?

> +};
> +
> +/*
> + * YUV2020 to RGB conversion parameters
> + * YUV2RGB[0]  - [8] : C0 - C8;
> + * YUV2RGB[9]  - [11]: D0 - D2;
> + * YUV2RGB[12] - [13]: Y clamp min & max calue;
> + * YUV2RGB[14] - [15]: UV clamp min & max calue;
> + */
> +static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
> +	1196, 0, 1724, 1196,
> +	-192, -668, 1196, 2200,
> +	0, -959232, 363776, -1202944,
> +	64, 940, 64, 960

Indention.

> +};
> +
> +/*
> + * RGB to YUV2020 conversion parameters
> + * RGB2YUV[0] - [8] : C0 - C8;
> + * RGB2YUV[9] - [11]: D0 - D2;
> + */
> +static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = {
> +	230,	594,	52,
> +	-125,  -323,	448,
> +	448,   -412,   -36,
> +	64,		512,	512
> +};
> +
> +/*
> + * Degamma table for 709 color space data.
> + */
> +static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005,
> +	0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024,
> +	0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064,
> +	0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce,
> +	0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166,
> +	0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230,
> +	0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331,
> +	0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c,
> +	0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
> +	0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d,
> +	0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999,
> +	0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb,
> +	0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65,
> +	0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a,
> +	0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b,
> +	0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc,
> +	0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e,
> +	0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
> +	0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e,
> +	0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf,
> +	0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea,
> +	0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f,
> +	0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930,
> +	0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f,
> +	0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee,
> +	0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde,
> +	0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
> +	0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9,
> +	0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406,
> +	0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a,
> +	0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576,
> +	0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd,
> +	0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
> +};
> +
> +/*
> + * Degamma table for 2020 color space data.
> + */
> +static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
> +	0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
> +	0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
> +	0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006,
> +	0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a,
> +	0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f,
> +	0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
> +	0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024,
> +	0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035,
> +	0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d,
> +	0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d,
> +	0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098,
> +	0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3,
> +	0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123,
> +	0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d,
> +	0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
> +	0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6,
> +	0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1,
> +	0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d,
> +	0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6,
> +	0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920,
> +	0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a,
> +	0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033,
> +	0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593,
> +	0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
> +	0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656,
> +	0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332,
> +	0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480,
> +	0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1,
> +	0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99,
> +	0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
> +};
> +
> +/* one is for primary plane and the other is for all overlay planes */
> +static const struct dc_hw_plane_reg dc_plane_reg[] = {
> +	{
> +	.y_address		= DC_FRAMEBUFFER_ADDRESS,
> +	.u_address		= DC_FRAMEBUFFER_U_ADDRESS,
> +	.v_address		= DC_FRAMEBUFFER_V_ADDRESS,
> +	.y_stride		= DC_FRAMEBUFFER_STRIDE,
> +	.u_stride		= DC_FRAMEBUFFER_U_STRIDE,
> +	.v_stride		= DC_FRAMEBUFFER_V_STRIDE,
> +	.size			= DC_FRAMEBUFFER_SIZE,
> +	.top_left		= DC_FRAMEBUFFER_TOP_LEFT,
> +	.bottom_right	= DC_FRAMEBUFFER_BOTTOM_RIGHT,
> +	.scale_factor_x			= DC_FRAMEBUFFER_SCALE_FACTOR_X,
> +	.scale_factor_y			= DC_FRAMEBUFFER_SCALE_FACTOR_Y,
> +	.h_filter_coef_index	= DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
> +	.h_filter_coef_data		= DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
> +	.v_filter_coef_index	= DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
> +	.v_filter_coef_data		= DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
> +	.init_offset			= DC_FRAMEBUFFER_INIT_OFFSET,
> +	.color_key				= DC_FRAMEBUFFER_COLOR_KEY,
> +	.color_key_high			= DC_FRAMEBUFFER_COLOR_KEY_HIGH,
> +	.clear_value			= DC_FRAMEBUFFER_CLEAR_VALUE,
> +	.color_table_index		= DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
> +	.color_table_data		= DC_FRAMEBUFFER_COLOR_TABLE_DATA,
> +	.scale_config			= DC_FRAMEBUFFER_SCALE_CONFIG,
> +	.water_mark				= DC_FRAMEBUFFER_WATER_MARK,
> +	.degamma_index			= DC_FRAMEBUFFER_DEGAMMA_INDEX,
> +	.degamma_data			= DC_FRAMEBUFFER_DEGAMMA_DATA,
> +	.degamma_ex_data		= DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
> +	.src_global_color		= DC_FRAMEBUFFER_SRC_GLOBAL_COLOR,
> +	.dst_global_color		= DC_FRAMEBUFFER_DST_GLOBAL_COLOR,
> +	.blend_config			= DC_FRAMEBUFFER_BLEND_CONFIG,
> +	.roi_origin				= DC_FRAMEBUFFER_ROI_ORIGIN,
> +	.roi_size				= DC_FRAMEBUFFER_ROI_SIZE,
> +	.yuv_to_rgb_coef0			= DC_FRAMEBUFFER_YUVTORGB_COEF0,
> +	.yuv_to_rgb_coef1			= DC_FRAMEBUFFER_YUVTORGB_COEF1,
> +	.yuv_to_rgb_coef2			= DC_FRAMEBUFFER_YUVTORGB_COEF2,
> +	.yuv_to_rgb_coef3			= DC_FRAMEBUFFER_YUVTORGB_COEF3,
> +	.yuv_to_rgb_coef4			= DC_FRAMEBUFFER_YUVTORGB_COEF4,
> +	.yuv_to_rgb_coefd0			= DC_FRAMEBUFFER_YUVTORGB_COEFD0,
> +	.yuv_to_rgb_coefd1			= DC_FRAMEBUFFER_YUVTORGB_COEFD1,
> +	.yuv_to_rgb_coefd2			= DC_FRAMEBUFFER_YUVTORGB_COEFD2,
> +	.y_clamp_bound				= DC_FRAMEBUFFER_Y_CLAMP_BOUND,
> +	.uv_clamp_bound				= DC_FRAMEBUFFER_UV_CLAMP_BOUND,
> +	.rgb_to_rgb_coef0			= DC_FRAMEBUFFER_RGBTORGB_COEF0,
> +	.rgb_to_rgb_coef1			= DC_FRAMEBUFFER_RGBTORGB_COEF1,
> +	.rgb_to_rgb_coef2			= DC_FRAMEBUFFER_RGBTORGB_COEF2,
> +	.rgb_to_rgb_coef3			= DC_FRAMEBUFFER_RGBTORGB_COEF3,
> +	.rgb_to_rgb_coef4			= DC_FRAMEBUFFER_RGBTORGB_COEF4,
> +	},
> +	{
> +	.y_address		= DC_OVERLAY_ADDRESS,
> +	.u_address		= DC_OVERLAY_U_ADDRESS,
> +	.v_address		= DC_OVERLAY_V_ADDRESS,
> +	.y_stride		= DC_OVERLAY_STRIDE,
> +	.u_stride		= DC_OVERLAY_U_STRIDE,
> +	.v_stride		= DC_OVERLAY_V_STRIDE,
> +	.size			= DC_OVERLAY_SIZE,
> +	.top_left		= DC_OVERLAY_TOP_LEFT,
> +	.bottom_right	= DC_OVERLAY_BOTTOM_RIGHT,
> +	.scale_factor_x	= DC_OVERLAY_SCALE_FACTOR_X,
> +	.scale_factor_y	= DC_OVERLAY_SCALE_FACTOR_Y,
> +	.h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
> +	.h_filter_coef_data  = DC_OVERLAY_H_FILTER_COEF_DATA,
> +	.v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
> +	.v_filter_coef_data  = DC_OVERLAY_V_FILTER_COEF_DATA,
> +	.init_offset		 = DC_OVERLAY_INIT_OFFSET,
> +	.color_key			 = DC_OVERLAY_COLOR_KEY,
> +	.color_key_high			= DC_OVERLAY_COLOR_KEY_HIGH,
> +	.clear_value		 = DC_OVERLAY_CLEAR_VALUE,
> +	.color_table_index	 = DC_OVERLAY_COLOR_TABLE_INDEX,
> +	.color_table_data	 = DC_OVERLAY_COLOR_TABLE_DATA,
> +	.scale_config		 = DC_OVERLAY_SCALE_CONFIG,
> +	.water_mark				= DC_OVERLAY_WATER_MARK,
> +	.degamma_index		 = DC_OVERLAY_DEGAMMA_INDEX,
> +	.degamma_data		 = DC_OVERLAY_DEGAMMA_DATA,
> +	.degamma_ex_data	 = DC_OVERLAY_DEGAMMA_EX_DATA,
> +	.src_global_color	 = DC_OVERLAY_SRC_GLOBAL_COLOR,
> +	.dst_global_color	 = DC_OVERLAY_DST_GLOBAL_COLOR,
> +	.blend_config		 = DC_OVERLAY_BLEND_CONFIG,
> +	.roi_origin				= DC_OVERLAY_ROI_ORIGIN,
> +	.roi_size				= DC_OVERLAY_ROI_SIZE,
> +	.yuv_to_rgb_coef0		 = DC_OVERLAY_YUVTORGB_COEF0,
> +	.yuv_to_rgb_coef1		 = DC_OVERLAY_YUVTORGB_COEF1,
> +	.yuv_to_rgb_coef2		 = DC_OVERLAY_YUVTORGB_COEF2,
> +	.yuv_to_rgb_coef3		 = DC_OVERLAY_YUVTORGB_COEF3,
> +	.yuv_to_rgb_coef4			= DC_OVERLAY_YUVTORGB_COEF4,
> +	.yuv_to_rgb_coefd0			= DC_OVERLAY_YUVTORGB_COEFD0,
> +	.yuv_to_rgb_coefd1			= DC_OVERLAY_YUVTORGB_COEFD1,
> +	.yuv_to_rgb_coefd2			= DC_OVERLAY_YUVTORGB_COEFD2,
> +	.y_clamp_bound		 = DC_OVERLAY_Y_CLAMP_BOUND,
> +	.uv_clamp_bound		 = DC_OVERLAY_UV_CLAMP_BOUND,
> +	.rgb_to_rgb_coef0		 = DC_OVERLAY_RGBTORGB_COEF0,
> +	.rgb_to_rgb_coef1		 = DC_OVERLAY_RGBTORGB_COEF1,
> +	.rgb_to_rgb_coef2		 = DC_OVERLAY_RGBTORGB_COEF2,
> +	.rgb_to_rgb_coef3		 = DC_OVERLAY_RGBTORGB_COEF3,
> +	.rgb_to_rgb_coef4		 = DC_OVERLAY_RGBTORGB_COEF4,
> +	},
> +};
> +
> +static const u32 primary_overlay_format0[] = {

These format arrays are supposed to be roughly sorted by hardware 
preference. This gives user-space programs an idea of what the hardware 
wants. Are these 4444 and 1555 formats really preferable over their 8888 
and 2101010 variants?

> +	DRM_FORMAT_XRGB4444,
> +	DRM_FORMAT_XBGR4444,
> +	DRM_FORMAT_RGBX4444,
> +	DRM_FORMAT_BGRX4444,
> +	DRM_FORMAT_ARGB4444,
> +	DRM_FORMAT_ABGR4444,
> +	DRM_FORMAT_RGBA4444,
> +	DRM_FORMAT_BGRA4444,
> +	DRM_FORMAT_XRGB1555,
> +	DRM_FORMAT_XBGR1555,
> +	DRM_FORMAT_RGBX5551,
> +	DRM_FORMAT_BGRX5551,
> +	DRM_FORMAT_ARGB1555,
> +	DRM_FORMAT_ABGR1555,
> +	DRM_FORMAT_RGBA5551,
> +	DRM_FORMAT_BGRA5551,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_RGBX8888,
> +	DRM_FORMAT_BGRX8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGBA8888,
> +	DRM_FORMAT_BGRA8888,
> +	DRM_FORMAT_ARGB2101010,
> +	DRM_FORMAT_ABGR2101010,
> +	DRM_FORMAT_RGBA1010102,
> +	DRM_FORMAT_BGRA1010102,
> +	DRM_FORMAT_YUYV,
> +	DRM_FORMAT_YVYU,
> +	DRM_FORMAT_UYVY,
> +	DRM_FORMAT_VYUY,
> +	DRM_FORMAT_YVU420,
> +	DRM_FORMAT_YUV420,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV21,
> +	DRM_FORMAT_NV16,
> +	DRM_FORMAT_NV61,
> +	DRM_FORMAT_P010,
> +};
> +
> +static const u32 primary_overlay_format1[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_RGBX8888,
> +	DRM_FORMAT_BGRX8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGBA8888,
> +	DRM_FORMAT_BGRA8888,
> +	DRM_FORMAT_ARGB2101010,
> +	DRM_FORMAT_ABGR2101010,
> +	DRM_FORMAT_RGBA1010102,
> +	DRM_FORMAT_BGRA1010102,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV21,
> +	DRM_FORMAT_YUV444,
> +};
> +
> +static const u32 cursor_formats[] = {
> +	DRM_FORMAT_ARGB8888
> +};
> +
> +static const u64 format_modifier0[] = {
> +	DRM_FORMAT_MOD_LINEAR,
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X8),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_8X8),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X8),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_TILE_8X4),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_XMAJOR_8X4),
> +	fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VERISILICON_SUPER_TILED_YMAJOR_4X8),
> +	DRM_FORMAT_MOD_INVALID
> +};
> +
> +static const u64 secondary_format_modifiers[] = {
> +	DRM_FORMAT_MOD_LINEAR,
> +	DRM_FORMAT_MOD_INVALID
> +};
> +
> +#define FRAC_16_16(mult, div)	 (((mult) << 16) / (div))
> +
> +static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = {

Please double-check the indention in this array.

Best regards
Thomas


-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstrasse 146, 90461 Nuernberg, Germany
GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman
HRB 36809 (AG Nuernberg)

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 1/6] dt-bindings: display: Add yamls for JH7110 display system
  2023-10-25 12:50     ` Krzysztof Kozlowski
  (?)
@ 2023-11-29  3:13       ` Keith Zhao
  -1 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-11-29  3:13 UTC (permalink / raw)
  To: Krzysztof Kozlowski, dri-devel, devicetree, linux-kernel,
	linux-riscv, linux-media, linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Jack Zhu, Shengyang Chen, Changhuang Liang



On 2023/10/25 20:50, Krzysztof Kozlowski wrote:
> On 25/10/2023 12:39, Keith Zhao wrote:
>> StarFive SoCs JH7110 display system:
> 
> A nit, subject: drop second/last, redundant "yamls for". The
> "dt-bindings" prefix is already stating that these are bindings, so
> format is fixed.
> 
>> lcd-controller bases verisilicon dc8200 IP,
>> and hdmi bases Innosilicon IP. Add bindings for them.
> 
> Please make it a proper sentences, with proper wrapping.
> 
>> 
>> also update MAINTAINERS for dt-bindings
> 
> Not a sentence, but also not really needed.ok I see.
> 
>> 
>> about this patch, I tested the dtbs_check and dt_binding_check
>> with the result pass.
>> Based on the feedback of the previous version, the corresponding arrangement is made
> 
> Not relevant, so not really suitable for commit msg.
> 
>> 
>> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>> ---
>>  .../starfive/starfive,display-subsystem.yaml  |  41 +++++++
>>  .../starfive/starfive,jh7110-dc8200.yaml      | 109 ++++++++++++++++++
>>  .../starfive/starfive,jh7110-inno-hdmi.yaml   |  85 ++++++++++++++
>>  MAINTAINERS                                   |   7 ++
>>  4 files changed, 242 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
>>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
>> 
>> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>> new file mode 100644
>> index 000000000..f45b97b08
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>> @@ -0,0 +1,41 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/display/starfive/starfive,display-subsystem.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Starfive DRM master device
> 
> What is DRM in hardware? I know Digital Rights Management, but then
> subsystem seems wrong. If you mean Linux DRM, then Linux is not a
> hardware, so drop all Linuxisms and describe hardware.
ok , will only keep hardware describe in my next version
> 
> 
>> +
>> +maintainers:
>> +  - Keith Zhao <keith.zhao@starfivetech.com>
>> +  - ShengYang Chen <shengyang.chen@starfivetech.com>
>> +
>> +description:
>> +  The Starfive DRM master device is a virtual device needed to list all
> 
> Virtual device? Then not suitable for bindings, sorry.
> 
>> +  display controller or other display interface nodes that comprise the
>> +  graphics subsystem.
>> +
>> +properties:
>> +  compatible:
>> +    const: starfive,display-subsystem
>> +
>> +  ports:
>> +    $ref: /schemas/types.yaml#/definitions/phandle-array
> 
> No, ports is not phandle-array. ports is object, always.
> 
>> +    description:
>> +      Should contain a list of phandles pointing to display interface ports
>> +      of display controller devices. Display controller definitions as defined
>> +      in Documentation/devicetree/bindings/display/starfive/
>> +      starfive,jh7110-dc8200.yaml
> 
> Use standard graph ports, not some own, custom property.
> 
> Anyway, entire binding should be dropped. You do not need it even.
Hi Krzysztof:
Virtual device is not suitable for bindings, matbe I need associate it with the real hardware.
such as the top clocks & reset , irq , etc.
Currently I configure them in another yaml file. Logically speaking, this is more suitable.

Can adding the corresponding hardware description change its fate of being deleted?
 
> 
>> +
>> +required:
>> +  - compatible
>> +  - ports
>> +
>> +additionalProperties: false
>> +
>> +examples:
>> +  - |
>> +    display-subsystem {
>> +        compatible = "starfive,display-subsystem";
>> +        ports = <&dc_out>;
>> +    };
>> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
>> new file mode 100644
>> index 000000000..87051cddf
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
>> @@ -0,0 +1,109 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/display/starfive/starfive,jh7110-dc8200.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: StarFive display controller
>> +
>> +description:
>> +  The StarFive SoC uses the display controller based on Verisilicon IP
>> +  to transfer the image data from a video memory buffer to an external
>> +  LCD interface.
>> +
>> +maintainers:
>> +  - Keith Zhao <keith.zhao@starfivetech.com>
>> +
>> +properties:
>> +  compatible:
>> +    const: starfive,jh7110-dc8200
>> +
>> +  reg:
>> +    minItems: 1
>> +    items:
>> +      - description:
>> +          host interface
> 
> In one line.
>  - description: host interface
> 
>> +      - description:
>> +          display physical base address and length.
> 
> Drop redundant parts: base address and length. Everything in reg for
> MMIO would have it...
> 
>> +
>> +  interrupts:
>> +    items:
>> +      - description: The interrupt will be generated when DC finish one frame
>> +
>> +  clocks:
>> +    items:
>> +      - description: Clock for display system noc bus.
>> +      - description: Pixel clock for display channel 0.
>> +      - description: Pixel clock for display channel 1.
>> +      - description: Core clock for display controller.
>> +      - description: Clock for axi bus to access ddr.
>> +      - description: Clock for ahb bus to R/W the phy regs.
>> +      - description: External HDMI pixel clock.
>> +      - description: Parent clock for pixel clock
>> +
>> +  clock-names:
>> +    items:
>> +      - const: noc_bus
>> +      - const: channel0
>> +      - const: channel1
>> +      - const: dc_core
>> +      - const: axi_core
>> +      - const: ahb
>> +      - const: hdmi_tx
>> +      - const: dc_parent
>> +
>> +  resets:
>> +    items:
>> +      - description: Reset for axi bus.
>> +      - description: Reset for ahb bus.
>> +      - description: Core reset of display controller.
>> +
>> +  reset-names:
>> +    items:
>> +      - const: axi
>> +      - const: ahb
>> +      - const: core
>> +
>> +  port:
>> +    $ref: /schemas/graph.yaml#/properties/port
>> +
>> +required:
>> +  - compatible
>> +  - reg
>> +  - interrupts
>> +  - clocks
>> +  - clock-names
>> +  - resets
>> +  - reset-names
>> +  - port
>> +
>> +additionalProperties: false
>> +
>> +examples:
>> +  - |
>> +    dc8200: lcd-controller@29400000 {
>> +        compatible = "starfive,jh7110-dc8200";
>> +        reg = <0x29400000 0x100>, <0x29400800 0x2000>;
>> +        interrupts = <95>;
>> +        clocks = <&syscrg 60>,
>> +               <&voutcrg 7>,
>> +               <&voutcrg 8>,
>> +               <&voutcrg 4>,
>> +               <&voutcrg 5>,
>> +               <&voutcrg 6>,
>> +               <&hdmitx0_pixelclk>,
>> +               <&voutcrg 1>;
>> +        clock-names = "noc_bus", "channel0", "channel1",
>> +                      "dc_core", "axi_core", "ahb",
>> +                      "hdmi_tx","dc_parent";
>> +        resets = <&voutcrg 0>, <&voutcrg 1>, <&voutcrg 2>;
>> +        reset-names = "axi", "ahb", "core";
>> +        dc_out: port {
>> +            #address-cells = <1>;
>> +            #size-cells = <0>;
>> +            dc_out_hdmi: endpoint@0 {
>> +                reg = <0>;
>> +                remote-endpoint = <&hdmi_in_dc>;
>> +            };
>> +        };
>> +    };
>> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
>> new file mode 100644
>> index 000000000..f6c473a10
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
>> @@ -0,0 +1,85 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/display/starfive/starfive,jh7110-inno-hdmi.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Starfive JH7110 HDMI controller
>> +
>> +description:
>> +  The StarFive JH7110 SoC uses the HDMI signal transmiter based on innosilicon IP
>> +  to generate HDMI signal from its input and transmit the signal to the screen.
>> +
>> +maintainers:
>> +  - Keith Zhao <keith.zhao@starfivetech.com>
>> +
>> +properties:
>> +  compatible:
>> +    const: "starfive,jh7110-inno-hdmi"
> 
> It does not look like you tested the bindings, at least after quick
> look. Please run `make dt_binding_check` (see
> Documentation/devicetree/bindings/writing-schema.rst for instructions).
> Maybe you need to update your dtschema and yamllint.
> 
> Best regards,
> Krzysztof
> 

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 1/6] dt-bindings: display: Add yamls for JH7110 display system
@ 2023-11-29  3:13       ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-11-29  3:13 UTC (permalink / raw)
  To: Krzysztof Kozlowski, dri-devel, devicetree, linux-kernel,
	linux-riscv, linux-media, linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig



On 2023/10/25 20:50, Krzysztof Kozlowski wrote:
> On 25/10/2023 12:39, Keith Zhao wrote:
>> StarFive SoCs JH7110 display system:
> 
> A nit, subject: drop second/last, redundant "yamls for". The
> "dt-bindings" prefix is already stating that these are bindings, so
> format is fixed.
> 
>> lcd-controller bases verisilicon dc8200 IP,
>> and hdmi bases Innosilicon IP. Add bindings for them.
> 
> Please make it a proper sentences, with proper wrapping.
> 
>> 
>> also update MAINTAINERS for dt-bindings
> 
> Not a sentence, but also not really needed.ok I see.
> 
>> 
>> about this patch, I tested the dtbs_check and dt_binding_check
>> with the result pass.
>> Based on the feedback of the previous version, the corresponding arrangement is made
> 
> Not relevant, so not really suitable for commit msg.
> 
>> 
>> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>> ---
>>  .../starfive/starfive,display-subsystem.yaml  |  41 +++++++
>>  .../starfive/starfive,jh7110-dc8200.yaml      | 109 ++++++++++++++++++
>>  .../starfive/starfive,jh7110-inno-hdmi.yaml   |  85 ++++++++++++++
>>  MAINTAINERS                                   |   7 ++
>>  4 files changed, 242 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
>>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
>> 
>> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>> new file mode 100644
>> index 000000000..f45b97b08
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>> @@ -0,0 +1,41 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/display/starfive/starfive,display-subsystem.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Starfive DRM master device
> 
> What is DRM in hardware? I know Digital Rights Management, but then
> subsystem seems wrong. If you mean Linux DRM, then Linux is not a
> hardware, so drop all Linuxisms and describe hardware.
ok , will only keep hardware describe in my next version
> 
> 
>> +
>> +maintainers:
>> +  - Keith Zhao <keith.zhao@starfivetech.com>
>> +  - ShengYang Chen <shengyang.chen@starfivetech.com>
>> +
>> +description:
>> +  The Starfive DRM master device is a virtual device needed to list all
> 
> Virtual device? Then not suitable for bindings, sorry.
> 
>> +  display controller or other display interface nodes that comprise the
>> +  graphics subsystem.
>> +
>> +properties:
>> +  compatible:
>> +    const: starfive,display-subsystem
>> +
>> +  ports:
>> +    $ref: /schemas/types.yaml#/definitions/phandle-array
> 
> No, ports is not phandle-array. ports is object, always.
> 
>> +    description:
>> +      Should contain a list of phandles pointing to display interface ports
>> +      of display controller devices. Display controller definitions as defined
>> +      in Documentation/devicetree/bindings/display/starfive/
>> +      starfive,jh7110-dc8200.yaml
> 
> Use standard graph ports, not some own, custom property.
> 
> Anyway, entire binding should be dropped. You do not need it even.
Hi Krzysztof:
Virtual device is not suitable for bindings, matbe I need associate it with the real hardware.
such as the top clocks & reset , irq , etc.
Currently I configure them in another yaml file. Logically speaking, this is more suitable.

Can adding the corresponding hardware description change its fate of being deleted?
 
> 
>> +
>> +required:
>> +  - compatible
>> +  - ports
>> +
>> +additionalProperties: false
>> +
>> +examples:
>> +  - |
>> +    display-subsystem {
>> +        compatible = "starfive,display-subsystem";
>> +        ports = <&dc_out>;
>> +    };
>> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
>> new file mode 100644
>> index 000000000..87051cddf
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
>> @@ -0,0 +1,109 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/display/starfive/starfive,jh7110-dc8200.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: StarFive display controller
>> +
>> +description:
>> +  The StarFive SoC uses the display controller based on Verisilicon IP
>> +  to transfer the image data from a video memory buffer to an external
>> +  LCD interface.
>> +
>> +maintainers:
>> +  - Keith Zhao <keith.zhao@starfivetech.com>
>> +
>> +properties:
>> +  compatible:
>> +    const: starfive,jh7110-dc8200
>> +
>> +  reg:
>> +    minItems: 1
>> +    items:
>> +      - description:
>> +          host interface
> 
> In one line.
>  - description: host interface
> 
>> +      - description:
>> +          display physical base address and length.
> 
> Drop redundant parts: base address and length. Everything in reg for
> MMIO would have it...
> 
>> +
>> +  interrupts:
>> +    items:
>> +      - description: The interrupt will be generated when DC finish one frame
>> +
>> +  clocks:
>> +    items:
>> +      - description: Clock for display system noc bus.
>> +      - description: Pixel clock for display channel 0.
>> +      - description: Pixel clock for display channel 1.
>> +      - description: Core clock for display controller.
>> +      - description: Clock for axi bus to access ddr.
>> +      - description: Clock for ahb bus to R/W the phy regs.
>> +      - description: External HDMI pixel clock.
>> +      - description: Parent clock for pixel clock
>> +
>> +  clock-names:
>> +    items:
>> +      - const: noc_bus
>> +      - const: channel0
>> +      - const: channel1
>> +      - const: dc_core
>> +      - const: axi_core
>> +      - const: ahb
>> +      - const: hdmi_tx
>> +      - const: dc_parent
>> +
>> +  resets:
>> +    items:
>> +      - description: Reset for axi bus.
>> +      - description: Reset for ahb bus.
>> +      - description: Core reset of display controller.
>> +
>> +  reset-names:
>> +    items:
>> +      - const: axi
>> +      - const: ahb
>> +      - const: core
>> +
>> +  port:
>> +    $ref: /schemas/graph.yaml#/properties/port
>> +
>> +required:
>> +  - compatible
>> +  - reg
>> +  - interrupts
>> +  - clocks
>> +  - clock-names
>> +  - resets
>> +  - reset-names
>> +  - port
>> +
>> +additionalProperties: false
>> +
>> +examples:
>> +  - |
>> +    dc8200: lcd-controller@29400000 {
>> +        compatible = "starfive,jh7110-dc8200";
>> +        reg = <0x29400000 0x100>, <0x29400800 0x2000>;
>> +        interrupts = <95>;
>> +        clocks = <&syscrg 60>,
>> +               <&voutcrg 7>,
>> +               <&voutcrg 8>,
>> +               <&voutcrg 4>,
>> +               <&voutcrg 5>,
>> +               <&voutcrg 6>,
>> +               <&hdmitx0_pixelclk>,
>> +               <&voutcrg 1>;
>> +        clock-names = "noc_bus", "channel0", "channel1",
>> +                      "dc_core", "axi_core", "ahb",
>> +                      "hdmi_tx","dc_parent";
>> +        resets = <&voutcrg 0>, <&voutcrg 1>, <&voutcrg 2>;
>> +        reset-names = "axi", "ahb", "core";
>> +        dc_out: port {
>> +            #address-cells = <1>;
>> +            #size-cells = <0>;
>> +            dc_out_hdmi: endpoint@0 {
>> +                reg = <0>;
>> +                remote-endpoint = <&hdmi_in_dc>;
>> +            };
>> +        };
>> +    };
>> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
>> new file mode 100644
>> index 000000000..f6c473a10
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
>> @@ -0,0 +1,85 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/display/starfive/starfive,jh7110-inno-hdmi.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Starfive JH7110 HDMI controller
>> +
>> +description:
>> +  The StarFive JH7110 SoC uses the HDMI signal transmiter based on innosilicon IP
>> +  to generate HDMI signal from its input and transmit the signal to the screen.
>> +
>> +maintainers:
>> +  - Keith Zhao <keith.zhao@starfivetech.com>
>> +
>> +properties:
>> +  compatible:
>> +    const: "starfive,jh7110-inno-hdmi"
> 
> It does not look like you tested the bindings, at least after quick
> look. Please run `make dt_binding_check` (see
> Documentation/devicetree/bindings/writing-schema.rst for instructions).
> Maybe you need to update your dtschema and yamllint.
> 
> Best regards,
> Krzysztof
> 

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 1/6] dt-bindings: display: Add yamls for JH7110 display system
@ 2023-11-29  3:13       ` Keith Zhao
  0 siblings, 0 replies; 117+ messages in thread
From: Keith Zhao @ 2023-11-29  3:13 UTC (permalink / raw)
  To: Krzysztof Kozlowski, dri-devel, devicetree, linux-kernel,
	linux-riscv, linux-media, linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Jack Zhu, Shengyang Chen, Changhuang Liang



On 2023/10/25 20:50, Krzysztof Kozlowski wrote:
> On 25/10/2023 12:39, Keith Zhao wrote:
>> StarFive SoCs JH7110 display system:
> 
> A nit, subject: drop second/last, redundant "yamls for". The
> "dt-bindings" prefix is already stating that these are bindings, so
> format is fixed.
> 
>> lcd-controller bases verisilicon dc8200 IP,
>> and hdmi bases Innosilicon IP. Add bindings for them.
> 
> Please make it a proper sentences, with proper wrapping.
> 
>> 
>> also update MAINTAINERS for dt-bindings
> 
> Not a sentence, but also not really needed.ok I see.
> 
>> 
>> about this patch, I tested the dtbs_check and dt_binding_check
>> with the result pass.
>> Based on the feedback of the previous version, the corresponding arrangement is made
> 
> Not relevant, so not really suitable for commit msg.
> 
>> 
>> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>> ---
>>  .../starfive/starfive,display-subsystem.yaml  |  41 +++++++
>>  .../starfive/starfive,jh7110-dc8200.yaml      | 109 ++++++++++++++++++
>>  .../starfive/starfive,jh7110-inno-hdmi.yaml   |  85 ++++++++++++++
>>  MAINTAINERS                                   |   7 ++
>>  4 files changed, 242 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
>>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
>> 
>> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>> new file mode 100644
>> index 000000000..f45b97b08
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>> @@ -0,0 +1,41 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/display/starfive/starfive,display-subsystem.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Starfive DRM master device
> 
> What is DRM in hardware? I know Digital Rights Management, but then
> subsystem seems wrong. If you mean Linux DRM, then Linux is not a
> hardware, so drop all Linuxisms and describe hardware.
ok , will only keep hardware describe in my next version
> 
> 
>> +
>> +maintainers:
>> +  - Keith Zhao <keith.zhao@starfivetech.com>
>> +  - ShengYang Chen <shengyang.chen@starfivetech.com>
>> +
>> +description:
>> +  The Starfive DRM master device is a virtual device needed to list all
> 
> Virtual device? Then not suitable for bindings, sorry.
> 
>> +  display controller or other display interface nodes that comprise the
>> +  graphics subsystem.
>> +
>> +properties:
>> +  compatible:
>> +    const: starfive,display-subsystem
>> +
>> +  ports:
>> +    $ref: /schemas/types.yaml#/definitions/phandle-array
> 
> No, ports is not phandle-array. ports is object, always.
> 
>> +    description:
>> +      Should contain a list of phandles pointing to display interface ports
>> +      of display controller devices. Display controller definitions as defined
>> +      in Documentation/devicetree/bindings/display/starfive/
>> +      starfive,jh7110-dc8200.yaml
> 
> Use standard graph ports, not some own, custom property.
> 
> Anyway, entire binding should be dropped. You do not need it even.
Hi Krzysztof:
Virtual device is not suitable for bindings, matbe I need associate it with the real hardware.
such as the top clocks & reset , irq , etc.
Currently I configure them in another yaml file. Logically speaking, this is more suitable.

Can adding the corresponding hardware description change its fate of being deleted?
 
> 
>> +
>> +required:
>> +  - compatible
>> +  - ports
>> +
>> +additionalProperties: false
>> +
>> +examples:
>> +  - |
>> +    display-subsystem {
>> +        compatible = "starfive,display-subsystem";
>> +        ports = <&dc_out>;
>> +    };
>> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
>> new file mode 100644
>> index 000000000..87051cddf
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
>> @@ -0,0 +1,109 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/display/starfive/starfive,jh7110-dc8200.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: StarFive display controller
>> +
>> +description:
>> +  The StarFive SoC uses the display controller based on Verisilicon IP
>> +  to transfer the image data from a video memory buffer to an external
>> +  LCD interface.
>> +
>> +maintainers:
>> +  - Keith Zhao <keith.zhao@starfivetech.com>
>> +
>> +properties:
>> +  compatible:
>> +    const: starfive,jh7110-dc8200
>> +
>> +  reg:
>> +    minItems: 1
>> +    items:
>> +      - description:
>> +          host interface
> 
> In one line.
>  - description: host interface
> 
>> +      - description:
>> +          display physical base address and length.
> 
> Drop redundant parts: base address and length. Everything in reg for
> MMIO would have it...
> 
>> +
>> +  interrupts:
>> +    items:
>> +      - description: The interrupt will be generated when DC finish one frame
>> +
>> +  clocks:
>> +    items:
>> +      - description: Clock for display system noc bus.
>> +      - description: Pixel clock for display channel 0.
>> +      - description: Pixel clock for display channel 1.
>> +      - description: Core clock for display controller.
>> +      - description: Clock for axi bus to access ddr.
>> +      - description: Clock for ahb bus to R/W the phy regs.
>> +      - description: External HDMI pixel clock.
>> +      - description: Parent clock for pixel clock
>> +
>> +  clock-names:
>> +    items:
>> +      - const: noc_bus
>> +      - const: channel0
>> +      - const: channel1
>> +      - const: dc_core
>> +      - const: axi_core
>> +      - const: ahb
>> +      - const: hdmi_tx
>> +      - const: dc_parent
>> +
>> +  resets:
>> +    items:
>> +      - description: Reset for axi bus.
>> +      - description: Reset for ahb bus.
>> +      - description: Core reset of display controller.
>> +
>> +  reset-names:
>> +    items:
>> +      - const: axi
>> +      - const: ahb
>> +      - const: core
>> +
>> +  port:
>> +    $ref: /schemas/graph.yaml#/properties/port
>> +
>> +required:
>> +  - compatible
>> +  - reg
>> +  - interrupts
>> +  - clocks
>> +  - clock-names
>> +  - resets
>> +  - reset-names
>> +  - port
>> +
>> +additionalProperties: false
>> +
>> +examples:
>> +  - |
>> +    dc8200: lcd-controller@29400000 {
>> +        compatible = "starfive,jh7110-dc8200";
>> +        reg = <0x29400000 0x100>, <0x29400800 0x2000>;
>> +        interrupts = <95>;
>> +        clocks = <&syscrg 60>,
>> +               <&voutcrg 7>,
>> +               <&voutcrg 8>,
>> +               <&voutcrg 4>,
>> +               <&voutcrg 5>,
>> +               <&voutcrg 6>,
>> +               <&hdmitx0_pixelclk>,
>> +               <&voutcrg 1>;
>> +        clock-names = "noc_bus", "channel0", "channel1",
>> +                      "dc_core", "axi_core", "ahb",
>> +                      "hdmi_tx","dc_parent";
>> +        resets = <&voutcrg 0>, <&voutcrg 1>, <&voutcrg 2>;
>> +        reset-names = "axi", "ahb", "core";
>> +        dc_out: port {
>> +            #address-cells = <1>;
>> +            #size-cells = <0>;
>> +            dc_out_hdmi: endpoint@0 {
>> +                reg = <0>;
>> +                remote-endpoint = <&hdmi_in_dc>;
>> +            };
>> +        };
>> +    };
>> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
>> new file mode 100644
>> index 000000000..f6c473a10
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
>> @@ -0,0 +1,85 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/display/starfive/starfive,jh7110-inno-hdmi.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Starfive JH7110 HDMI controller
>> +
>> +description:
>> +  The StarFive JH7110 SoC uses the HDMI signal transmiter based on innosilicon IP
>> +  to generate HDMI signal from its input and transmit the signal to the screen.
>> +
>> +maintainers:
>> +  - Keith Zhao <keith.zhao@starfivetech.com>
>> +
>> +properties:
>> +  compatible:
>> +    const: "starfive,jh7110-inno-hdmi"
> 
> It does not look like you tested the bindings, at least after quick
> look. Please run `make dt_binding_check` (see
> Documentation/devicetree/bindings/writing-schema.rst for instructions).
> Maybe you need to update your dtschema and yamllint.
> 
> Best regards,
> Krzysztof
> 

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 1/6] dt-bindings: display: Add yamls for JH7110 display system
  2023-11-29  3:13       ` Keith Zhao
  (?)
@ 2023-11-29  8:14         ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 117+ messages in thread
From: Krzysztof Kozlowski @ 2023-11-29  8:14 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Jack Zhu, Shengyang Chen, Changhuang Liang

On 29/11/2023 04:13, Keith Zhao wrote:
> 
> 
> On 2023/10/25 20:50, Krzysztof Kozlowski wrote:
>> On 25/10/2023 12:39, Keith Zhao wrote:
>>> StarFive SoCs JH7110 display system:
>>
>> A nit, subject: drop second/last, redundant "yamls for". The
>> "dt-bindings" prefix is already stating that these are bindings, so
>> format is fixed.
>>
>>> lcd-controller bases verisilicon dc8200 IP,
>>> and hdmi bases Innosilicon IP. Add bindings for them.
>>
>> Please make it a proper sentences, with proper wrapping.
>>
>>>
>>> also update MAINTAINERS for dt-bindings
>>
>> Not a sentence, but also not really needed.ok I see.
>>
>>>
>>> about this patch, I tested the dtbs_check and dt_binding_check
>>> with the result pass.
>>> Based on the feedback of the previous version, the corresponding arrangement is made
>>
>> Not relevant, so not really suitable for commit msg.
>>
>>>
>>> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>>> ---
>>>  .../starfive/starfive,display-subsystem.yaml  |  41 +++++++
>>>  .../starfive/starfive,jh7110-dc8200.yaml      | 109 ++++++++++++++++++
>>>  .../starfive/starfive,jh7110-inno-hdmi.yaml   |  85 ++++++++++++++
>>>  MAINTAINERS                                   |   7 ++
>>>  4 files changed, 242 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>>>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
>>>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
>>>
>>> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>>> new file mode 100644
>>> index 000000000..f45b97b08
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>>> @@ -0,0 +1,41 @@
>>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>>> +%YAML 1.2
>>> +---
>>> +$id: http://devicetree.org/schemas/display/starfive/starfive,display-subsystem.yaml#
>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>>> +
>>> +title: Starfive DRM master device
>>
>> What is DRM in hardware? I know Digital Rights Management, but then
>> subsystem seems wrong. If you mean Linux DRM, then Linux is not a
>> hardware, so drop all Linuxisms and describe hardware.
> ok , will only keep hardware describe in my next version
>>
>>
>>> +
>>> +maintainers:
>>> +  - Keith Zhao <keith.zhao@starfivetech.com>
>>> +  - ShengYang Chen <shengyang.chen@starfivetech.com>
>>> +
>>> +description:
>>> +  The Starfive DRM master device is a virtual device needed to list all
>>
>> Virtual device? Then not suitable for bindings, sorry.
>>
>>> +  display controller or other display interface nodes that comprise the
>>> +  graphics subsystem.
>>> +
>>> +properties:
>>> +  compatible:
>>> +    const: starfive,display-subsystem
>>> +
>>> +  ports:
>>> +    $ref: /schemas/types.yaml#/definitions/phandle-array
>>
>> No, ports is not phandle-array. ports is object, always.
>>
>>> +    description:
>>> +      Should contain a list of phandles pointing to display interface ports
>>> +      of display controller devices. Display controller definitions as defined
>>> +      in Documentation/devicetree/bindings/display/starfive/
>>> +      starfive,jh7110-dc8200.yaml
>>
>> Use standard graph ports, not some own, custom property.
>>
>> Anyway, entire binding should be dropped. You do not need it even.
> Hi Krzysztof:
> Virtual device is not suitable for bindings, matbe I need associate it with the real hardware.
> such as the top clocks & reset , irq , etc.
> Currently I configure them in another yaml file. Logically speaking, this is more suitable.
> 
> Can adding the corresponding hardware description change its fate of being deleted?

I am not sure if I follow. Bindings and DTS describe the hardware, so if
you configure device A clocks in a device B node, then it is not
correct. If you add binding for something not being a real device, it is
not correct.

Feel free to bring proper hardware description, not Linux. This entire
binding was written to describe Linux driver, which is not correct.

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 1/6] dt-bindings: display: Add yamls for JH7110 display system
@ 2023-11-29  8:14         ` Krzysztof Kozlowski
  0 siblings, 0 replies; 117+ messages in thread
From: Krzysztof Kozlowski @ 2023-11-29  8:14 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: David Airlie, Daniel Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Emil Renner Berthing, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Philipp Zabel, Sumit Semwal, christian.koenig,
	Bjorn Andersson, Heiko Stuebner, Shawn Guo, Jagan Teki,
	Chris Morgan, Jack Zhu, Shengyang Chen, Changhuang Liang

On 29/11/2023 04:13, Keith Zhao wrote:
> 
> 
> On 2023/10/25 20:50, Krzysztof Kozlowski wrote:
>> On 25/10/2023 12:39, Keith Zhao wrote:
>>> StarFive SoCs JH7110 display system:
>>
>> A nit, subject: drop second/last, redundant "yamls for". The
>> "dt-bindings" prefix is already stating that these are bindings, so
>> format is fixed.
>>
>>> lcd-controller bases verisilicon dc8200 IP,
>>> and hdmi bases Innosilicon IP. Add bindings for them.
>>
>> Please make it a proper sentences, with proper wrapping.
>>
>>>
>>> also update MAINTAINERS for dt-bindings
>>
>> Not a sentence, but also not really needed.ok I see.
>>
>>>
>>> about this patch, I tested the dtbs_check and dt_binding_check
>>> with the result pass.
>>> Based on the feedback of the previous version, the corresponding arrangement is made
>>
>> Not relevant, so not really suitable for commit msg.
>>
>>>
>>> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>>> ---
>>>  .../starfive/starfive,display-subsystem.yaml  |  41 +++++++
>>>  .../starfive/starfive,jh7110-dc8200.yaml      | 109 ++++++++++++++++++
>>>  .../starfive/starfive,jh7110-inno-hdmi.yaml   |  85 ++++++++++++++
>>>  MAINTAINERS                                   |   7 ++
>>>  4 files changed, 242 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>>>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
>>>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
>>>
>>> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>>> new file mode 100644
>>> index 000000000..f45b97b08
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>>> @@ -0,0 +1,41 @@
>>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>>> +%YAML 1.2
>>> +---
>>> +$id: http://devicetree.org/schemas/display/starfive/starfive,display-subsystem.yaml#
>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>>> +
>>> +title: Starfive DRM master device
>>
>> What is DRM in hardware? I know Digital Rights Management, but then
>> subsystem seems wrong. If you mean Linux DRM, then Linux is not a
>> hardware, so drop all Linuxisms and describe hardware.
> ok , will only keep hardware describe in my next version
>>
>>
>>> +
>>> +maintainers:
>>> +  - Keith Zhao <keith.zhao@starfivetech.com>
>>> +  - ShengYang Chen <shengyang.chen@starfivetech.com>
>>> +
>>> +description:
>>> +  The Starfive DRM master device is a virtual device needed to list all
>>
>> Virtual device? Then not suitable for bindings, sorry.
>>
>>> +  display controller or other display interface nodes that comprise the
>>> +  graphics subsystem.
>>> +
>>> +properties:
>>> +  compatible:
>>> +    const: starfive,display-subsystem
>>> +
>>> +  ports:
>>> +    $ref: /schemas/types.yaml#/definitions/phandle-array
>>
>> No, ports is not phandle-array. ports is object, always.
>>
>>> +    description:
>>> +      Should contain a list of phandles pointing to display interface ports
>>> +      of display controller devices. Display controller definitions as defined
>>> +      in Documentation/devicetree/bindings/display/starfive/
>>> +      starfive,jh7110-dc8200.yaml
>>
>> Use standard graph ports, not some own, custom property.
>>
>> Anyway, entire binding should be dropped. You do not need it even.
> Hi Krzysztof:
> Virtual device is not suitable for bindings, matbe I need associate it with the real hardware.
> such as the top clocks & reset , irq , etc.
> Currently I configure them in another yaml file. Logically speaking, this is more suitable.
> 
> Can adding the corresponding hardware description change its fate of being deleted?

I am not sure if I follow. Bindings and DTS describe the hardware, so if
you configure device A clocks in a device B node, then it is not
correct. If you add binding for something not being a real device, it is
not correct.

Feel free to bring proper hardware description, not Linux. This entire
binding was written to describe Linux driver, which is not correct.

Best regards,
Krzysztof


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 117+ messages in thread

* Re: [PATCH v2 1/6] dt-bindings: display: Add yamls for JH7110 display system
@ 2023-11-29  8:14         ` Krzysztof Kozlowski
  0 siblings, 0 replies; 117+ messages in thread
From: Krzysztof Kozlowski @ 2023-11-29  8:14 UTC (permalink / raw)
  To: Keith Zhao, dri-devel, devicetree, linux-kernel, linux-riscv,
	linux-media, linaro-mm-sig
  Cc: Krzysztof Kozlowski, Sumit Semwal, Emil Renner Berthing,
	Shengyang Chen, Conor Dooley, Albert Ou, Thomas Zimmermann,
	Maxime Ripard, Jagan Teki, Rob Herring, Chris Morgan,
	Paul Walmsley, Bjorn Andersson, Changhuang Liang, Jack Zhu,
	Palmer Dabbelt, Shawn Guo, christian.koenig

On 29/11/2023 04:13, Keith Zhao wrote:
> 
> 
> On 2023/10/25 20:50, Krzysztof Kozlowski wrote:
>> On 25/10/2023 12:39, Keith Zhao wrote:
>>> StarFive SoCs JH7110 display system:
>>
>> A nit, subject: drop second/last, redundant "yamls for". The
>> "dt-bindings" prefix is already stating that these are bindings, so
>> format is fixed.
>>
>>> lcd-controller bases verisilicon dc8200 IP,
>>> and hdmi bases Innosilicon IP. Add bindings for them.
>>
>> Please make it a proper sentences, with proper wrapping.
>>
>>>
>>> also update MAINTAINERS for dt-bindings
>>
>> Not a sentence, but also not really needed.ok I see.
>>
>>>
>>> about this patch, I tested the dtbs_check and dt_binding_check
>>> with the result pass.
>>> Based on the feedback of the previous version, the corresponding arrangement is made
>>
>> Not relevant, so not really suitable for commit msg.
>>
>>>
>>> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>>> ---
>>>  .../starfive/starfive,display-subsystem.yaml  |  41 +++++++
>>>  .../starfive/starfive,jh7110-dc8200.yaml      | 109 ++++++++++++++++++
>>>  .../starfive/starfive,jh7110-inno-hdmi.yaml   |  85 ++++++++++++++
>>>  MAINTAINERS                                   |   7 ++
>>>  4 files changed, 242 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>>>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
>>>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
>>>
>>> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>>> new file mode 100644
>>> index 000000000..f45b97b08
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>>> @@ -0,0 +1,41 @@
>>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>>> +%YAML 1.2
>>> +---
>>> +$id: http://devicetree.org/schemas/display/starfive/starfive,display-subsystem.yaml#
>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>>> +
>>> +title: Starfive DRM master device
>>
>> What is DRM in hardware? I know Digital Rights Management, but then
>> subsystem seems wrong. If you mean Linux DRM, then Linux is not a
>> hardware, so drop all Linuxisms and describe hardware.
> ok , will only keep hardware describe in my next version
>>
>>
>>> +
>>> +maintainers:
>>> +  - Keith Zhao <keith.zhao@starfivetech.com>
>>> +  - ShengYang Chen <shengyang.chen@starfivetech.com>
>>> +
>>> +description:
>>> +  The Starfive DRM master device is a virtual device needed to list all
>>
>> Virtual device? Then not suitable for bindings, sorry.
>>
>>> +  display controller or other display interface nodes that comprise the
>>> +  graphics subsystem.
>>> +
>>> +properties:
>>> +  compatible:
>>> +    const: starfive,display-subsystem
>>> +
>>> +  ports:
>>> +    $ref: /schemas/types.yaml#/definitions/phandle-array
>>
>> No, ports is not phandle-array. ports is object, always.
>>
>>> +    description:
>>> +      Should contain a list of phandles pointing to display interface ports
>>> +      of display controller devices. Display controller definitions as defined
>>> +      in Documentation/devicetree/bindings/display/starfive/
>>> +      starfive,jh7110-dc8200.yaml
>>
>> Use standard graph ports, not some own, custom property.
>>
>> Anyway, entire binding should be dropped. You do not need it even.
> Hi Krzysztof:
> Virtual device is not suitable for bindings, matbe I need associate it with the real hardware.
> such as the top clocks & reset , irq , etc.
> Currently I configure them in another yaml file. Logically speaking, this is more suitable.
> 
> Can adding the corresponding hardware description change its fate of being deleted?

I am not sure if I follow. Bindings and DTS describe the hardware, so if
you configure device A clocks in a device B node, then it is not
correct. If you add binding for something not being a real device, it is
not correct.

Feel free to bring proper hardware description, not Linux. This entire
binding was written to describe Linux driver, which is not correct.

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 117+ messages in thread

end of thread, other threads:[~2023-11-29  8:14 UTC | newest]

Thread overview: 117+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-10-25 10:39 [PATCH v2 0/6] DRM driver for verisilicon Keith Zhao
2023-10-25 10:39 ` Keith Zhao
2023-10-25 10:39 ` Keith Zhao
2023-10-25 10:39 ` [PATCH v2 1/6] dt-bindings: display: Add yamls for JH7110 display system Keith Zhao
2023-10-25 10:39   ` Keith Zhao
2023-10-25 10:39   ` Keith Zhao
2023-10-25 12:50   ` Krzysztof Kozlowski
2023-10-25 12:50     ` Krzysztof Kozlowski
2023-10-25 12:50     ` Krzysztof Kozlowski
2023-11-29  3:13     ` Keith Zhao
2023-11-29  3:13       ` Keith Zhao
2023-11-29  3:13       ` Keith Zhao
2023-11-29  8:14       ` Krzysztof Kozlowski
2023-11-29  8:14         ` Krzysztof Kozlowski
2023-11-29  8:14         ` Krzysztof Kozlowski
2023-10-25 10:39 ` [PATCH v2 2/6] riscv: dts: starfive: jh7110: add dc controller and hdmi node Keith Zhao
2023-10-25 10:39   ` Keith Zhao
2023-10-25 10:39   ` Keith Zhao
2023-10-25 13:38   ` Emil Renner Berthing
2023-10-25 13:38     ` Emil Renner Berthing
2023-10-25 13:38     ` Emil Renner Berthing
2023-10-25 10:39 ` [PATCH v2 3/6] drm/fourcc: Add drm/vs tiled modifiers Keith Zhao
2023-10-25 10:39   ` Keith Zhao
2023-10-25 10:39   ` Keith Zhao
2023-10-25 13:50   ` Emil Renner Berthing
2023-10-25 13:50     ` Emil Renner Berthing
2023-10-25 13:50     ` Emil Renner Berthing
2023-10-25 15:28   ` Dmitry Baryshkov
2023-10-25 15:28     ` Dmitry Baryshkov
2023-10-25 15:28     ` Dmitry Baryshkov
2023-10-25 15:38   ` Simon Ser
2023-10-25 15:38     ` Simon Ser
2023-10-25 15:38     ` Simon Ser
2023-10-25 15:44     ` Simon Ser
2023-10-25 15:44       ` Simon Ser
2023-10-25 15:44       ` Simon Ser
2023-11-15 14:12       ` Keith Zhao
2023-11-15 14:12         ` Keith Zhao
2023-11-15 14:12         ` Keith Zhao
2023-10-25 10:39 ` [PATCH v2 4/6] drm/vs: Register DRM device Keith Zhao
2023-10-25 10:39   ` Keith Zhao
2023-10-25 10:39   ` Keith Zhao
2023-10-25 13:55   ` Emil Renner Berthing
2023-10-25 13:55     ` Emil Renner Berthing
2023-10-25 13:55     ` Emil Renner Berthing
2023-10-25 15:50   ` Dmitry Baryshkov
2023-10-25 15:50     ` Dmitry Baryshkov
2023-10-25 15:50     ` Dmitry Baryshkov
2023-10-25 10:39 ` [PATCH v2 5/6] drm/vs: Add KMS crtc&plane Keith Zhao
2023-10-25 10:39   ` Keith Zhao
2023-10-25 10:39   ` Keith Zhao
2023-10-25 13:57   ` Maxime Ripard
2023-10-25 13:57     ` Maxime Ripard
2023-10-25 13:57     ` Maxime Ripard
2023-11-15 14:52     ` Keith Zhao
2023-11-15 14:52       ` Keith Zhao
2023-11-15 14:52       ` Keith Zhao
2023-10-25 19:28   ` Dmitry Baryshkov
2023-10-25 19:28     ` Dmitry Baryshkov
2023-10-25 19:28     ` Dmitry Baryshkov
2023-10-25 19:49     ` Ville Syrjälä
2023-10-25 19:49       ` Ville Syrjälä
2023-10-25 19:49       ` Ville Syrjälä
2023-10-26  9:42       ` Keith Zhao
2023-10-26  9:42         ` Keith Zhao
2023-10-26  9:42         ` Keith Zhao
2023-10-26 10:09         ` Keith Zhao
2023-10-26 10:09           ` Keith Zhao
2023-10-26 10:09           ` Keith Zhao
2023-11-14 10:42     ` Keith Zhao
2023-11-14 10:42       ` Keith Zhao
2023-11-14 10:42       ` Keith Zhao
2023-11-14 10:59       ` Dmitry Baryshkov
2023-11-14 10:59         ` Dmitry Baryshkov
2023-11-14 10:59         ` Dmitry Baryshkov
2023-11-15 13:30         ` Keith Zhao
2023-11-15 13:30           ` Keith Zhao
2023-11-15 13:30           ` Keith Zhao
2023-11-15 16:00           ` Dmitry Baryshkov
2023-11-15 16:00             ` Dmitry Baryshkov
2023-11-15 16:00             ` Dmitry Baryshkov
2023-11-24 13:40   ` Thomas Zimmermann
2023-11-24 13:40     ` Thomas Zimmermann
2023-11-24 13:40     ` Thomas Zimmermann
2023-10-25 10:39 ` [PATCH v2 6/6] drm/vs: Add hdmi driver Keith Zhao
2023-10-25 10:39   ` Keith Zhao
2023-10-25 10:39   ` Keith Zhao
2023-10-25 14:07   ` Maxime Ripard
2023-10-25 14:07     ` Maxime Ripard
2023-10-25 14:07     ` Maxime Ripard
2023-10-25 22:23   ` Dmitry Baryshkov
2023-10-25 22:23     ` Dmitry Baryshkov
2023-10-25 22:23     ` Dmitry Baryshkov
2023-10-26  8:07     ` Maxime Ripard
2023-10-26  8:07       ` Maxime Ripard
2023-10-26  8:07       ` Maxime Ripard
2023-10-26  8:57       ` Dmitry Baryshkov
2023-10-26  8:57         ` Dmitry Baryshkov
2023-10-26  8:57         ` Dmitry Baryshkov
2023-10-26 11:53         ` Maxime Ripard
2023-10-26 11:53           ` Maxime Ripard
2023-10-26 11:53           ` Maxime Ripard
2023-10-29 16:52           ` Dmitry Baryshkov
2023-10-29 16:52             ` Dmitry Baryshkov
2023-10-29 16:52             ` Dmitry Baryshkov
2023-11-07  9:19             ` Maxime Ripard
2023-11-07  9:19               ` Maxime Ripard
2023-11-07  9:19               ` Maxime Ripard
2023-11-13 12:11     ` Keith Zhao
2023-11-13 12:11       ` Keith Zhao
2023-11-13 12:11       ` Keith Zhao
2023-11-13 13:31       ` Dmitry Baryshkov
2023-11-13 13:31         ` Dmitry Baryshkov
2023-11-13 13:31         ` Dmitry Baryshkov
2023-11-14  8:27         ` Keith Zhao
2023-11-14  8:27           ` Keith Zhao
2023-11-14  8:27           ` Keith Zhao

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.