dri-devel.lists.freedesktop.org archive mirror
 help / color / mirror / Atom feed
* [v3 0/6] DRM driver for verisilicon
@ 2023-12-04 12:33 Keith Zhao
  2023-12-04 12:33 ` [v3 1/6] dt-bindings: display: Add yamls for JH7110 display system Keith Zhao
                   ` (8 more replies)
  0 siblings, 9 replies; 52+ messages in thread
From: Keith Zhao @ 2023-12-04 12:33 UTC (permalink / raw)
  To: Keith Zhao, devicetree, dri-devel, linux-kernel, linux-riscv
  Cc: aou, suijingfeng, tzimmermann, paul.walmsley, mripard, xingyu.wu,
	jack.zhu, palmer, krzysztof.kozlowski+dt, william.qiu,
	shengyang.chen, changhuang.liang

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 on next branch.

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 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 

V3:
Changes since v3:
- Delete the custom plane property.
- Delete the custom fourcc modifiers.
- Adjust the calculation mode of hdmi pixclock.
- Add match data for dc8200 driver.
- Adjust some magic values.
- Add a simple encoder for dsi output.

Keith Zhao (6):
  dt-bindings: display: Add yamls for JH7110 display system
  riscv: dts: starfive: jh7110: display subsystem
  drm/vs: Register DRM device
  drm/vs: Add KMS crtc&plane
  drm/vs: Add hdmi driver
  drm/vs: simple encoder

 .../starfive/starfive,display-subsystem.yaml  |  104 ++
 .../starfive/starfive,dsi-encoder.yaml        |   92 ++
 .../starfive/starfive,jh7110-dc8200.yaml      |  113 ++
 .../starfive/starfive,jh7110-inno-hdmi.yaml   |   82 ++
 .../soc/starfive/starfive,jh7110-syscon.yaml  |    1 +
 MAINTAINERS                                   |    8 +
 .../jh7110-starfive-visionfive-2.dtsi         |  134 ++
 arch/riscv/boot/dts/starfive/jh7110.dtsi      |   49 +
 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   |  849 ++++++++++++
 drivers/gpu/drm/verisilicon/starfive_hdmi.h   |  304 +++++
 drivers/gpu/drm/verisilicon/vs_crtc.c         |  208 +++
 drivers/gpu/drm/verisilicon/vs_crtc.h         |   42 +
 drivers/gpu/drm/verisilicon/vs_dc.c           | 1192 +++++++++++++++++
 drivers/gpu/drm/verisilicon/vs_dc.h           |   67 +
 drivers/gpu/drm/verisilicon/vs_dc_hw.c        | 1022 ++++++++++++++
 drivers/gpu/drm/verisilicon/vs_dc_hw.h        |  580 ++++++++
 drivers/gpu/drm/verisilicon/vs_drv.c          |  323 +++++
 drivers/gpu/drm/verisilicon/vs_drv.h          |   46 +
 drivers/gpu/drm/verisilicon/vs_modeset.c      |   39 +
 drivers/gpu/drm/verisilicon/vs_modeset.h      |   10 +
 drivers/gpu/drm/verisilicon/vs_plane.c        |  301 +++++
 drivers/gpu/drm/verisilicon/vs_plane.h        |   39 +
 drivers/gpu/drm/verisilicon/vs_simple_enc.c   |  195 +++
 drivers/gpu/drm/verisilicon/vs_simple_enc.h   |   23 +
 drivers/gpu/drm/verisilicon/vs_type.h         |   69 +
 29 files changed, 5928 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,dsi-encoder.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_simple_enc.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_simple_enc.h
 create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h

-- 
2.34.1


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

* [v3 1/6] dt-bindings: display: Add yamls for JH7110 display system
  2023-12-04 12:33 [v3 0/6] DRM driver for verisilicon Keith Zhao
@ 2023-12-04 12:33 ` Keith Zhao
  2023-12-05  6:59   ` Krzysztof Kozlowski
                     ` (2 more replies)
  2023-12-04 12:33 ` [v3 2/6] riscv: dts: starfive: jh7110: display subsystem Keith Zhao
                   ` (7 subsequent siblings)
  8 siblings, 3 replies; 52+ messages in thread
From: Keith Zhao @ 2023-12-04 12:33 UTC (permalink / raw)
  To: Keith Zhao, devicetree, dri-devel, linux-kernel, linux-riscv
  Cc: aou, suijingfeng, tzimmermann, paul.walmsley, mripard, xingyu.wu,
	jack.zhu, palmer, krzysztof.kozlowski+dt, william.qiu,
	shengyang.chen, changhuang.liang

StarFive SoCs JH7110 display system:
dc controller, hdmi controller,
encoder, vout syscon.

add the path of yaml file in MAINTAINERS

Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
---
 .../starfive/starfive,display-subsystem.yaml  | 104 ++++++++++++++++
 .../starfive/starfive,dsi-encoder.yaml        |  92 ++++++++++++++
 .../starfive/starfive,jh7110-dc8200.yaml      | 113 ++++++++++++++++++
 .../starfive/starfive,jh7110-inno-hdmi.yaml   |  82 +++++++++++++
 .../soc/starfive/starfive,jh7110-syscon.yaml  |   1 +
 MAINTAINERS                                   |   7 ++
 6 files changed, 399 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
 create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,dsi-encoder.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 000000000000..d5ebdba3fb36
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
@@ -0,0 +1,104 @@
+# 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 JH7110 Soc Display SubSystem
+
+maintainers:
+  - Keith Zhao <keith.zhao@starfivetech.com>
+  - ShengYang Chen <shengyang.chen@starfivetech.com>
+
+description:
+  This is the bindings documentation for the JH7110 Soc Display Subsystem that
+  includes front-end video data capture, display controller and display
+  interface. such as HDMI and MIPI.
+
+  JH7110 display pipeline have several components as below description,
+  multi display controllers and corresponding physical interfaces.
+  For different display scenarios, pipe0 and pipe1 maybe binding to different
+  encoder. for example,
+
+  pipe0 binding to HDMI for primary display,
+  pipe1 binding to DSI for external display.
+
+          +------------------------------+
+          |                              |
+          |                              |
+  +----+  |   +-------------------+      |   +-------+   +------+   +------+
+  |    +----->+  dc controller 0  +--->----->+HDMICtl| ->+ PHY  +-->+PANEL0+
+  |AXI |  |   +-------------------+      |   +-------+   +------+   +------+
+  |    |  |                              |
+  |    |  |                              |
+  |    |  |                              |
+  |    |  |                              |
+  |APB |  |   +-------------------+         +---------+    +------+  +-------+
+  |    +----->+  dc controller 1  +--->---->+ dsiTx   +--->+DPHY  +->+ PANEL1+
+  |    |  |   +-------------------+         +---------+    +------+  +-------+
+  +----+  |                              |
+          +------------------------------+
+
+
+properties:
+  compatible:
+    const: starfive,display-subsystem
+
+  clocks:
+    items:
+      - description: Clock for display system noc bus.
+      - 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.
+
+  clock-names:
+    items:
+      - const: noc_bus
+      - const: dc_core
+      - const: axi_core
+      - const: ahb
+
+  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
+
+  ports:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    items:
+      maxItems: 1
+    description:
+      Should contain a list of phandles pointing to display interface port
+      of dc-controller devices.
+
+required:
+  - compatible
+  - clocks
+  - clock-names
+  - resets
+  - reset-names
+  - ports
+
+additionalProperties: false
+
+examples:
+  - |
+    display-subsystem {
+        compatible = "starfive,display-subsystem";
+        ports = <&dc_out>;
+
+        clocks = <&syscrg 60>,
+               <&voutcrg 4>,
+               <&voutcrg 5>,
+               <&voutcrg 6>;
+        clock-names = "noc_bus", "dc_core", "axi_core", "ahb";
+        resets = <&voutcrg 0>, <&voutcrg 1>, <&voutcrg 2>;
+        reset-names = "axi", "ahb", "core";
+    };
diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,dsi-encoder.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,dsi-encoder.yaml
new file mode 100644
index 000000000000..2cc0ad8e65ba
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/starfive/starfive,dsi-encoder.yaml
@@ -0,0 +1,92 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/starfive/starfive,dsi-encoder.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: starfive jh7110 soc Encoder
+
+maintainers:
+  - Keith Zhao <keith.zhao@starfivetech.com>
+
+description:
+  Device-Tree bindings for simple encoder.
+  Simple encoder driver only has basic functionality.
+  the hardware of dc8200 has 2 output interface, use
+  syscon to select which one to be used.
+
+properties:
+  compatible:
+    const: starfive,dsi-encoder
+
+  starfive,syscon:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    items:
+      - items:
+          - description: phandle to syscon that select crtc output.
+          - description: Offset of crtc selection
+          - description: Shift of crtc selection
+    description:
+      A phandle to syscon with two arguments that configure select output.
+      The argument one is the offset of crtc selection, the
+      argument two is the shift of crtc selection.
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description:
+          The first port should be the input coming from the associated dc channel.
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description:
+          The second port should be the output coming from the associated bridge.
+
+    required:
+      - port@0
+      - port@1
+
+required:
+  - compatible
+  - ports
+
+additionalProperties: false
+
+examples:
+  - |
+        dssctrl: dssctrl@295b0000 {
+            compatible = "starfive,jh7110-vout-syscon", "syscon";
+            reg = <0x295b0000 0x90>;
+        };
+
+        dsi_encoder: dsi_encoder {
+            compatible = "starfive,dsi-encoder";
+            starfive,syscon = <&dssctrl 0x8 0x12>;
+
+            ports {
+                #address-cells = <1>;
+                #size-cells = <0>;
+                /* input */
+                port@0 {
+                    #address-cells = <1>;
+                    #size-cells = <0>;
+                    reg = <0>;
+                    dsi_input0:endpoint@0 {
+                        reg = <0>;
+                        remote-endpoint = <&dc_out_dpi1>;
+                    };
+                };
+                /* output */
+                port@1 {
+                    reg = <1>;
+                    #address-cells = <1>;
+                    #size-cells = <0>;
+                    dsi_out:endpoint {
+                        remote-endpoint = <&mipi_in>;
+                    };
+                };
+            };
+        };
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 000000000000..0b083effec02
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
@@ -0,0 +1,113 @@
+# 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: Pixel clock for display channel 0.
+      - description: Pixel clock for display channel 1.
+      - description: Pixel clock from hdmi.
+      - description: Pixel clock for soc .
+  clock-names:
+    items:
+      - const: channel0
+      - const: channel1
+      - const: hdmi_tx
+      - const: dc_parent
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description:
+          channel 0 output
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description:
+          channel 1 output
+
+    required:
+      - port@0
+      - port@1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - ports
+
+additionalProperties: false
+
+examples:
+  - |
+    dc8200: lcd-controller@29400000 {
+        compatible = "starfive,jh7110-dc8200";
+        reg = <0x29400000 0x100>, <0x29400800 0x2000>;
+        interrupts = <95>;
+        clocks = <&voutcrg 7>,
+               <&voutcrg 8>,
+               <&voutcrg 9>,
+               <&voutcrg 10>;
+        clock-names = "channel0", "channel1","hdmi_tx", "dc_parent";
+
+        crtc_out: ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            dc_out0: port@0 {
+                reg = <0>;
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                dc_out_dpi0: endpoint@0 {
+                    reg = <0>;
+                    remote-endpoint = <&hdmi_in_dc>;
+                };
+
+            };
+
+            dc_out1: port@1 {
+                reg = <1>;
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                dc_out_dpi1: endpoint@1 {
+                    reg = <1>;
+                    remote-endpoint = <&dsi_input0>;
+                };
+
+            };
+          };
+    };
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 000000000000..3640d97ab789
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
@@ -0,0 +1,82 @@
+# 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.
+
+  clock-names:
+    items:
+      - const: sysclk
+      - const: mclk
+      - const: bclk
+
+  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>;
+        clock-names = "sysclk", "mclk","bclk";
+        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/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml b/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml
index 0039319e91fe..cf9b657d0e8a 100644
--- a/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml
+++ b/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml
@@ -24,6 +24,7 @@ properties:
           - enum:
               - starfive,jh7110-aon-syscon
               - starfive,jh7110-stg-syscon
+              - starfive,jh7110-vout-syscon
           - const: syscon
 
   reg:
diff --git a/MAINTAINERS b/MAINTAINERS
index 7b151710e8c5..7caaadb83f3f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6881,6 +6881,13 @@ T:	git git://anongit.freedesktop.org/drm/drm-misc
 F:	Documentation/devicetree/bindings/display/ste,mcde.yaml
 F:	drivers/gpu/drm/mcde/
 
+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 TI DLPC3433 MIPI DSI TO DMD BRIDGE
 M:	Jagan Teki <jagan@amarulasolutions.com>
 S:	Maintained
-- 
2.34.1


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

* [v3 2/6] riscv: dts: starfive: jh7110: display subsystem
  2023-12-04 12:33 [v3 0/6] DRM driver for verisilicon Keith Zhao
  2023-12-04 12:33 ` [v3 1/6] dt-bindings: display: Add yamls for JH7110 display system Keith Zhao
@ 2023-12-04 12:33 ` Keith Zhao
  2023-12-04 12:33 ` [v3 3/6] drm/vs: Register DRM device Keith Zhao
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 52+ messages in thread
From: Keith Zhao @ 2023-12-04 12:33 UTC (permalink / raw)
  To: Keith Zhao, devicetree, dri-devel, linux-kernel, linux-riscv
  Cc: aou, suijingfeng, tzimmermann, paul.walmsley, mripard, xingyu.wu,
	jack.zhu, palmer, krzysztof.kozlowski+dt, william.qiu,
	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         | 134 ++++++++++++++++++
 arch/riscv/boot/dts/starfive/jh7110.dtsi      |  49 +++++++
 2 files changed, 183 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 b89e9791efa7..6e387e0138c0 100644
--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
+++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
@@ -35,6 +35,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>;
@@ -69,6 +88,68 @@ codec {
 	};
 };
 
+&dc8200 {
+	status = "okay";
+
+	crtc_out: ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		dc_out0: port@0 {
+			reg = <0>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			dc_out_dpi0: endpoint@0 {
+				reg = <0>;
+				remote-endpoint = <&hdmi_enc>;
+			};
+
+		};
+
+		dc_out1: port@1 {
+			reg = <1>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			dc_out_dpi1: endpoint@1 {
+				reg = <1>;
+				remote-endpoint = <&dsi_enc>;
+			};
+
+		};
+	};
+};
+
+&display {
+	status = "okay";
+	ports = <&crtc_out>;
+};
+
+&dsi_encoder {
+	status = "okay";
+
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		/* input */
+		enc_in: port@0 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0>;
+			dsi_enc:endpoint@0 {
+				reg = <0>;
+				remote-endpoint = <&dc_out_dpi1>;
+			};
+		};
+		/* output */
+		enc_out: port@1 {
+			reg = <1>;
+			/*need add a remote-endpoint to dsi bridge*/
+		};
+	};
+};
+
 &dvp_clk {
 	clock-frequency = <74250000>;
 };
@@ -89,6 +170,21 @@ &gmac1_rmii_refin {
 	clock-frequency = <50000000>;
 };
 
+&hdmi {
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&hdmi_pins>;
+
+	hdmi_in: port {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		hdmi_enc: endpoint@0 {
+			reg = <0>;
+			remote-endpoint = <&dc_out_dpi0>;
+		};
+	};
+};
+
 &hdmitx0_pixelclk {
 	clock-frequency = <297000000>;
 };
@@ -336,6 +432,40 @@ spi_dev0: spi@0 {
 };
 
 &sysgpio {
+	hdmi_pins: hdmi-0 {
+		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 */
+		};
+
+		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;
+		};
+	};
+
 	i2c0_pins: i2c0-0 {
 		i2c-pins {
 			pinmux = <GPIOMUX(57, GPOUT_LOW,
@@ -642,3 +772,7 @@ &U74_3 {
 &U74_4 {
 	cpu-supply = <&vdd_cpu>;
 };
+
+&voutcrg {
+	status = "okay";
+};
diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi
index 45213cdf50dc..df51b9407328 100644
--- a/arch/riscv/boot/dts/starfive/jh7110.dtsi
+++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi
@@ -344,6 +344,24 @@ tdm_ext: tdm-ext-clock {
 		#clock-cells = <0>;
 	};
 
+	display: display-subsystem {
+		compatible = "starfive,display-subsystem";
+
+		clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_DISP_AXI>,
+			<&voutcrg JH7110_VOUTCLK_DC8200_CORE>,
+			<&voutcrg JH7110_VOUTCLK_DC8200_AXI>,
+			<&voutcrg JH7110_VOUTCLK_DC8200_AHB>;
+		clock-names = "noc_bus", "dc_core", "axi_core", "ahb";
+		resets = <&voutcrg JH7110_VOUTRST_DC8200_AXI>,
+			 <&voutcrg JH7110_VOUTRST_DC8200_AHB>,
+			 <&voutcrg JH7110_VOUTRST_DC8200_CORE>;
+		reset-names = "axi","ahb", "core";
+	};
+
+	dsi_encoder: dsi_encoder {
+		compatible = "starfive,dsi-encoder";
+		starfive,syscon = <&vout_syscon 0x8 0x08>;
+	};
 	soc {
 		compatible = "simple-bus";
 		interrupt-parent = <&plic>;
@@ -1121,6 +1139,37 @@ ispcrg: clock-controller@19810000 {
 			power-domains = <&pwrc JH7110_PD_ISP>;
 		};
 
+		dc8200: lcd-controller@29400000 {
+			compatible = "starfive,jh7110-dc8200";
+			reg = <0x0 0x29400000 0x0 0x100>,
+			      <0x0 0x29400800 0x0 0x2000>;
+			interrupts = <95>;
+			clocks = <&voutcrg JH7110_VOUTCLK_DC8200_PIX0>,
+				<&voutcrg JH7110_VOUTCLK_DC8200_PIX1>,
+				<&hdmitx0_pixelclk>,
+				<&voutcrg JH7110_VOUTCLK_DC8200_PIX>;
+			clock-names = "channel0", "channel1",
+				      "hdmi_tx", "dc_parent";
+		};
+
+		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>;
+			clock-names = "sysclk", "mclk", "bclk";
+			resets = <&voutcrg JH7110_VOUTRST_HDMI_TX_HDMI>;
+			#sound-dai-cells = <0>;
+		};
+
+		vout_syscon: syscon@295b0000 {
+			compatible = "starfive,jh7110-vout-syscon", "syscon";
+			reg = <0 0x295b0000 0 0x90>;
+		};
+
 		voutcrg: clock-controller@295c0000 {
 			compatible = "starfive,jh7110-voutcrg";
 			reg = <0x0 0x295c0000 0x0 0x10000>;
-- 
2.34.1


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

* [v3 3/6] drm/vs: Register DRM device
  2023-12-04 12:33 [v3 0/6] DRM driver for verisilicon Keith Zhao
  2023-12-04 12:33 ` [v3 1/6] dt-bindings: display: Add yamls for JH7110 display system Keith Zhao
  2023-12-04 12:33 ` [v3 2/6] riscv: dts: starfive: jh7110: display subsystem Keith Zhao
@ 2023-12-04 12:33 ` Keith Zhao
  2023-12-04 13:30   ` Philipp Zabel
  2023-12-05 11:33   ` Dmitry Baryshkov
  2023-12-04 12:33 ` [v3 4/6] drm/vs: Add KMS crtc&plane Keith Zhao
                   ` (5 subsequent siblings)
  8 siblings, 2 replies; 52+ messages in thread
From: Keith Zhao @ 2023-12-04 12:33 UTC (permalink / raw)
  To: Keith Zhao, devicetree, dri-devel, linux-kernel, linux-riscv
  Cc: aou, suijingfeng, tzimmermann, paul.walmsley, mripard, xingyu.wu,
	jack.zhu, palmer, krzysztof.kozlowski+dt, william.qiu,
	shengyang.chen, changhuang.liang

Implement drm device registration interface

Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
---
 MAINTAINERS                              |   1 +
 drivers/gpu/drm/Kconfig                  |   2 +
 drivers/gpu/drm/Makefile                 |   1 +
 drivers/gpu/drm/verisilicon/Kconfig      |  13 +
 drivers/gpu/drm/verisilicon/Makefile     |   6 +
 drivers/gpu/drm/verisilicon/vs_drv.c     | 316 +++++++++++++++++++++++
 drivers/gpu/drm/verisilicon/vs_drv.h     |  42 +++
 drivers/gpu/drm/verisilicon/vs_modeset.c |  39 +++
 drivers/gpu/drm/verisilicon/vs_modeset.h |  10 +
 9 files changed, 430 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

diff --git a/MAINTAINERS b/MAINTAINERS
index 7caaadb83f3f..8dc9ebfe4605 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6887,6 +6887,7 @@ L:	dri-devel@lists.freedesktop.org
 S:	Maintained
 T:	git git://anongit.freedesktop.org/drm/drm-misc
 F:	Documentation/devicetree/bindings/display/starfive/
+F:	drivers/gpu/drm/verisilicon/
 
 DRM DRIVER FOR TI DLPC3433 MIPI DSI TO DMD BRIDGE
 M:	Jagan Teki <jagan@amarulasolutions.com>
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 3eee8636f847..e8d53c2e7c86 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -394,6 +394,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 8e1bde059170..29e04acded06 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -198,3 +198,4 @@ obj-$(CONFIG_DRM_HYPERV) += hyperv/
 obj-y			+= solomon/
 obj-$(CONFIG_DRM_SPRD) += sprd/
 obj-$(CONFIG_DRM_LOONGSON) += loongson/
+obj-$(CONFIG_DRM_VERISILICON) += verisilicon/
diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
new file mode 100644
index 000000000000..e10fa97635aa
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/Kconfig
@@ -0,0 +1,13 @@
+# 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 000000000000..d785a1dfaa7f
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/Makefile
@@ -0,0 +1,6 @@
+# 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 000000000000..4fb1f29ef84b
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_drv.c
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of_clk.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);
+
+	args->pitch = ALIGN(pitch, priv->pitch_alignment);
+	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 void vs_drm_device_release_clocks(void *res)
+{
+	struct vs_drm_device *priv = res;
+	unsigned int i;
+
+	reset_control_bulk_assert(priv->nrsts, priv->rst_vout);
+
+	for (i = 0; i < priv->clk_count; ++i) {
+		if (priv->clks[i]) {
+			clk_disable_unprepare(priv->clks[i]);
+			clk_put(priv->clks[i]);
+		}
+	}
+}
+
+static const char * const vout_resets[] = {
+	"axi",
+	"ahb",
+	"core",
+};
+
+static int vs_drm_device_init_clocks(struct vs_drm_device *priv)
+{
+	struct drm_device *dev = &priv->base;
+	struct platform_device *pdev = to_platform_device(dev->dev);
+	struct device_node *of_node = pdev->dev.of_node;
+	struct clk *clock;
+	unsigned int i;
+	int ret;
+
+	if (dev_get_platdata(&pdev->dev) || !of_node)
+		return 0;
+
+	priv->nrsts = ARRAY_SIZE(priv->rst_vout);
+	for (int i = 0; i < priv->nrsts; ++i)
+		priv->rst_vout[i].id = vout_resets[i];
+	ret = devm_reset_control_bulk_get_shared(dev->dev, priv->nrsts,
+						 priv->rst_vout);
+	if (ret) {
+		drm_err(dev, "Failed to get reset controls\n");
+		return ret;
+	}
+
+	priv->clk_count = of_clk_get_parent_count(of_node);
+	if (!priv->clk_count)
+		return 0;
+
+	priv->clks = drmm_kzalloc(dev, priv->clk_count * sizeof(priv->clks[0]),
+				  GFP_KERNEL);
+	if (!priv->clks)
+		return -ENOMEM;
+
+	for (i = 0; i < priv->clk_count; ++i) {
+		clock = of_clk_get(of_node, i);
+		if (IS_ERR(clock)) {
+			ret = PTR_ERR(clock);
+			if (ret == -EPROBE_DEFER)
+				goto err;
+			drm_err(dev, "clock %u not found: %d\n", i, ret);
+			continue;
+		}
+		ret = clk_prepare_enable(clock);
+		if (ret) {
+			drm_err(dev, "failed to enable clock %u: %d\n",
+				i, ret);
+			clk_put(clock);
+			continue;
+		}
+		priv->clks[i] = clock;
+	}
+
+	ret = reset_control_bulk_deassert(priv->nrsts, priv->rst_vout);
+	if (ret)
+		return ret;
+
+	return devm_add_action_or_reset(&pdev->dev,
+					vs_drm_device_release_clocks,
+					priv);
+
+err:
+	while (i) {
+		--i;
+		if (priv->clks[i]) {
+			clk_disable_unprepare(priv->clks[i]);
+			clk_put(priv->clks[i]);
+		}
+	}
+	return ret;
+}
+
+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);
+
+	ret = vs_drm_device_init_clocks(priv);
+	if (ret)
+		return ret;
+
+	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);
+
+			drm_of_component_match_add(dev, &match, component_compare_of,
+						   d->of_node);
+			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));
+}
+
+late_initcall_sync(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 000000000000..ea2189772980
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_drv.h
@@ -0,0 +1,42 @@
+/* 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>
+#include <linux/clk.h>
+#include <linux/reset.h>
+
+enum rst_vout {
+	RST_VOUT_AXI = 0,
+	RST_VOUT_AHB,
+	RST_VOUT_CORE,
+	RST_VOUT_NUM
+};
+
+/*@pitch_alignment: buffer pitch alignment required by sub-devices.*/
+struct vs_drm_device {
+	struct drm_device base;
+	unsigned int pitch_alignment;
+	/* clocks */
+	unsigned int clk_count;
+	struct clk **clks;
+
+	struct reset_control_bulk_data rst_vout[RST_VOUT_NUM];
+	int	nrsts;
+};
+
+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 000000000000..eaf406c1b7c7
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_modeset.c
@@ -0,0 +1,39 @@
+// 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)
+{
+	int ret;
+
+	ret = drmm_mode_config_init(dev);
+	if (ret)
+		return;
+
+	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 000000000000..bd04f81d2ad2
--- /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_MODESET_H__
+#define __VS_MODESET_H__
+
+void vs_mode_config_init(struct drm_device *dev);
+#endif /* __VS_FB_H__ */
-- 
2.34.1


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

* [v3 4/6] drm/vs: Add KMS crtc&plane
  2023-12-04 12:33 [v3 0/6] DRM driver for verisilicon Keith Zhao
                   ` (2 preceding siblings ...)
  2023-12-04 12:33 ` [v3 3/6] drm/vs: Register DRM device Keith Zhao
@ 2023-12-04 12:33 ` Keith Zhao
  2023-12-05 12:48   ` Dmitry Baryshkov
                     ` (2 more replies)
  2023-12-04 12:33 ` [v3 5/6] drm/vs: Add hdmi driver Keith Zhao
                   ` (4 subsequent siblings)
  8 siblings, 3 replies; 52+ messages in thread
From: Keith Zhao @ 2023-12-04 12:33 UTC (permalink / raw)
  To: Keith Zhao, devicetree, dri-devel, linux-kernel, linux-riscv
  Cc: aou, suijingfeng, tzimmermann, paul.walmsley, mripard, xingyu.wu,
	jack.zhu, palmer, krzysztof.kozlowski+dt, william.qiu,
	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   |    9 +-
 drivers/gpu/drm/verisilicon/vs_crtc.c  |  208 +++++
 drivers/gpu/drm/verisilicon/vs_crtc.h  |   42 +
 drivers/gpu/drm/verisilicon/vs_dc.c    | 1192 ++++++++++++++++++++++++
 drivers/gpu/drm/verisilicon/vs_dc.h    |   67 ++
 drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1022 ++++++++++++++++++++
 drivers/gpu/drm/verisilicon/vs_dc_hw.h |  580 ++++++++++++
 drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
 drivers/gpu/drm/verisilicon/vs_plane.c |  301 ++++++
 drivers/gpu/drm/verisilicon/vs_plane.h |   39 +
 drivers/gpu/drm/verisilicon/vs_type.h  |   69 ++
 11 files changed, 3528 insertions(+), 3 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 d785a1dfaa7f..bf6f2b7ee480 100644
--- a/drivers/gpu/drm/verisilicon/Makefile
+++ b/drivers/gpu/drm/verisilicon/Makefile
@@ -1,6 +1,9 @@
 # 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 000000000000..5581219b1230
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
@@ -0,0 +1,208 @@
+// 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 "vs_crtc.h"
+#include "vs_dc.h"
+#include "vs_drv.h"
+
+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 void vs_crtc_reset(struct drm_crtc *crtc)
+{
+	struct vs_crtc_state *state;
+
+	if (crtc->state)
+		vs_crtc_atomic_destroy_state(crtc, crtc->state);
+
+	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 *old_state;
+	struct vs_crtc_state *state;
+
+	if (!crtc->state)
+		return NULL;
+
+	old_state = to_vs_crtc_state(crtc->state);
+
+	state = kmemdup(old_state, sizeof(*old_state), GFP_KERNEL);
+		if (!state)
+			return NULL;
+
+	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
+
+	return &state->base;
+}
+
+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 void vs_crtc_atomic_print_state(struct drm_printer *p,
+				       const struct drm_crtc_state *state)
+{
+	struct vs_crtc *vs_crtc = to_vs_crtc(state->crtc);
+
+	drm_printf(p, "vs crtc State\n");
+	drm_printf(p, "\tcolor_formats: %d\n", vs_crtc->color_formats);
+	drm_printf(p, "\tmax_bpc: %d\n", vs_crtc->max_bpc);
+}
+
+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,
+	.atomic_print_state = vs_crtc_atomic_print_state,
+};
+
+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);
+
+	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);
+		crtc->state->event = NULL;
+		spin_unlock_irq(&crtc->dev->event_lock);
+	}
+}
+
+static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
+				 struct drm_atomic_state *state)
+{
+	struct drm_crtc_state *new_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 = new_state->gamma_lut;
+	struct drm_color_lut *lut;
+	struct vs_dc *dc = dev_get_drvdata(dev);
+
+	if (new_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);
+		crtc->state->event = NULL;
+		spin_unlock_irq(&crtc->dev->event_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,
+};
+
+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_crtc_alloc_with_planes(drm_dev, struct vs_crtc, base, NULL,
+					   NULL, &vs_crtc_funcs,
+					   info->name ? info->name : 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 000000000000..8f5b5718283f
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_crtc.h
@@ -0,0 +1,42 @@
+/* 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;
+};
+
+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 000000000000..3f1e70ba4261
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc.c
@@ -0,0 +1,1192 @@
+// 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/of.h>
+#include <linux/of_graph.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 "vs_dc_hw.h"
+#include "vs_dc.h"
+#include "vs_drv.h"
+
+static const char * const vout_clocks[] = {
+	"channel0",
+	"channel1",
+	"hdmi_tx",
+	"dc_parent",
+};
+
+#define FRAC_16_16(mult, div)	 (((mult) << 16) / (div))
+
+static const u32 primary_overlay_format0[] = {
+	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_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_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,
+	DRM_FORMAT_MOD_INVALID
+};
+
+static const u64 secondary_format_modifiers[] = {
+	DRM_FORMAT_MOD_LINEAR,
+	DRM_FORMAT_MOD_INVALID
+};
+
+static const struct vs_plane_info dc_hw_planes_rev0[PLANE_NUM] = {
+	{
+		.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,
+	},
+};
+
+static const struct vs_dc_info dc8200_info = {
+	.name			= "DC8200",
+	.panel_num		= 2,
+	.plane_num		= 8,
+	.planes			= dc_hw_planes_rev0,
+	.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,
+};
+
+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 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_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)
+{
+	struct vs_dc *dc = dev_get_drvdata(dev);
+
+	return clk_bulk_prepare_enable(dc->nclks, dc->clk_vout);
+}
+
+static void plda_clk_rst_deinit(struct device *dev)
+{
+	struct vs_dc *dc = dev_get_drvdata(dev);
+
+	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;
+
+	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);
+
+	enable_irq(dc->irq);
+}
+
+void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc)
+{
+	struct dc_hw_display display;
+
+	disable_irq(dc->irq);
+
+	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 = DRM_FORMAT_MOD_LINEAR;
+	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);
+}
+
+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 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;
+	roi.enable = false;
+
+	update_scale(state, &roi, &scale);
+
+	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);
+
+	dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
+}
+
+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);
+}
+
+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 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;
+
+	if (!dc_info)
+		return IRQ_HANDLED;
+
+	ret = dc_hw_get_interrupt(&dc->hw);
+
+	for (i = 0; i < dc_info->panel_num; i++)
+		drm_crtc_handle_vblank(&dc->crtc[i]->base);
+
+	return IRQ_HANDLED;
+}
+
+void vs_dc_commit(struct vs_dc *dc)
+{
+	dc_hw_enable_shadow_register(&dc->hw, false);
+
+	dc_hw_commit(&dc->hw);
+
+	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 max_width = 0, max_height = 0;
+	u32 min_width = 0xffff, min_heigth = 0xffff;
+	u32 possible_crtc = 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;
+	}
+
+	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;
+		}
+
+		port = of_graph_get_port_by_id(dev->of_node, i);
+		if (!port) {
+			drm_err(drm_dev, "no port node found for crtc_port%d\n", i);
+			return -ENOENT;
+		}
+
+		crtc->base.port = port;
+		crtc->dev = dev;
+		dc->crtc[i] = crtc;
+	}
+
+	for (i = 0; i < dc_info->plane_num; i++) {
+		plane_info = (struct vs_plane_info *)&dc_info->planes[i];
+
+		if (!dc->crtc[0]->base.port || !dc->crtc[1]->base.port) {
+			drm_err(drm_dev, "no port no crtc mask, fail to create plane\n");
+			return -ENOENT;
+		}
+
+		if (plane_info->id == PRIMARY_PLANE_0 ||
+		    plane_info->id == CURSOR_PLANE_0) {
+			possible_crtc = drm_crtc_mask(&dc->crtc[0]->base);
+		} else if (plane_info->id == PRIMARY_PLANE_1 ||
+			plane_info->id == CURSOR_PLANE_1) {
+			possible_crtc = drm_crtc_mask(&dc->crtc[1]->base);
+		} else {
+			possible_crtc = drm_crtc_mask(&dc->crtc[0]->base) |
+					drm_crtc_mask(&dc->crtc[1]->base);
+		}
+
+		plane = vs_plane_create(drm_dev, plane_info,
+					dc_info->layer_num, possible_crtc);
+		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 (plane_info->id == PRIMARY_PLANE_0)
+				dc->crtc[0]->base.primary = &plane->base;
+			else
+				dc->crtc[1]->base.primary = &plane->base;
+
+			min_width = min_t(u32, min_width, plane_info->min_width);
+			min_heigth = min_t(u32, min_heigth, plane_info->min_height);
+			/*
+			 * Note: these values are used for multiple independent things:
+			 * hw display mode filtering, plane buffer sizes ...
+			 * Use the combined maximum values here to cover all use cases,
+			 * and do more specific checking in the respective code paths.
+			 */
+			max_width = max_t(u32, max_width, plane_info->max_width);
+			max_height = max_t(u32, max_height, plane_info->max_height);
+		}
+
+		if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
+			if (plane_info->id == CURSOR_PLANE_0)
+				dc->crtc[0]->base.cursor = &plane->base;
+			else
+				dc->crtc[1]->base.cursor = &plane->base;
+			drm_dev->mode_config.cursor_width =
+				max_t(u32, drm_dev->mode_config.cursor_width,
+				      plane_info->max_width);
+			drm_dev->mode_config.cursor_height =
+				max_t(u32, drm_dev->mode_config.cursor_height,
+				      plane_info->max_height);
+		}
+	}
+
+	drm_dev->mode_config.min_width = min_width;
+	drm_dev->mode_config.min_height = min_heigth;
+	drm_dev->mode_config.max_width = max_width;
+	drm_dev->mode_config.max_height = 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",
+		.data = &dc8200_info,
+	},
+	{},
+};
+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 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->irq = platform_get_irq(pdev, 0);
+
+	/* do not autoenable, will be enabled later */
+	ret = devm_request_irq(dev, dc->irq, dc_isr, IRQF_NO_AUTOEN, dev_name(dev), dc);
+	if (ret < 0) {
+		dev_err(dev, "Failed to install irq:%u.\n", dc->irq);
+		return ret;
+	}
+
+	dc->hw.info = (struct vs_dc_info *)of_device_get_match_data(dev);
+
+	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 000000000000..eeb5be0499ea
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc.h
@@ -0,0 +1,67 @@
+/* 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_PIX0,
+	CLK_VOUT_PIX1,
+	CLK_VOUT_HDMI_PIX,
+	CLK_VOUT_SOC_PIX,
+	CLK_VOUT_NUM
+};
+
+struct vs_dc {
+	struct vs_crtc		*crtc[DC_DISPLAY_NUM];
+	struct dc_hw		hw;
+
+	struct vs_dc_plane	planes[PLANE_NUM];
+	struct clk_bulk_data	clk_vout[CLK_VOUT_NUM];
+	int	nclks;
+	int irq;
+};
+
+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 000000000000..d05ad3f00881
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
@@ -0,0 +1,1022 @@
+// 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 "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 ARRAY_SIZE(horkernel)
+
+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 ARRAY_SIZE(verkernel)
+
+/*
+ * RGB 709->2020 conversion parameters
+ */
+static const 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 const 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
+};
+
+/* 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 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, const 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, const 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 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;
+
+	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)
+			hw->reg[i] = dc_plane_reg[0];
+		else
+			hw->reg[i] = dc_plane_reg[1];
+
+		load_default_filter(hw, &hw->reg[i], offset);
+		load_rgb_to_rgb(hw, &hw->reg[i], 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_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);
+}
+
+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,
+					     PRIMARY_SHADOW_EN, 0);
+			else
+				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+					     OVERLAY_SHADOW_EN, 0);
+		} else {
+			if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
+				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
+					     0, PRIMARY_SHADOW_EN);
+			else
+				dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+					     0, OVERLAY_SHADOW_EN);
+		}
+	}
+
+	for (i = 0; i < panel_num; i++) {
+		offset = i << 2;
+		if (enable)
+			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, PANEL_SHADOW_EN);
+		else
+			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, PANEL_SHADOW_EN, 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),
+					     PANEL_GAMMA_EN, 0);
+			} else {
+				dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
+					     0, PANEL_GAMMA_EN);
+			}
+			hw->gamma[j].dirty = false;
+		}
+	}
+}
+
+static void plane_ex_commit_primary(struct dc_hw *hw, struct dc_hw_plane *plane, u32 i, u32 offset)
+{
+	if (plane->fb.dirty) {
+		if (is_rgb(plane->fb.format)) {
+			dc_set_clear(hw,
+				     DC_FRAMEBUFFER_CONFIG_EX + offset,
+				     PRIMARY_RGB2RGB_EN, PRIMARY_YUVCLAMP_EN);
+		} else {
+			dc_set_clear(hw,
+				     DC_FRAMEBUFFER_CONFIG_EX + offset,
+				     PRIMARY_YUVCLAMP_EN, PRIMARY_RGB2RGB_EN);
+
+			switch (plane->fb.yuv_color_space) {
+			case COLOR_SPACE_601:
+				load_yuv_to_rgb(hw, &hw->reg[i], offset, YUV601_2RGB);
+				break;
+			case COLOR_SPACE_709:
+				load_yuv_to_rgb(hw, &hw->reg[i], offset, YUV709_2RGB);
+				break;
+			case COLOR_SPACE_2020:
+				load_yuv_to_rgb(hw, &hw->reg[i], offset, YUV2020_2RGB);
+				break;
+			default:
+				break;
+			}
+		}
+
+		if (plane->fb.enable) {
+			dc_write(hw, hw->reg[i].y_address + offset,
+				 plane->fb.y_address);
+			dc_write(hw, hw->reg[i].u_address + offset,
+				 plane->fb.u_address);
+			dc_write(hw, hw->reg[i].v_address + offset,
+				 plane->fb.v_address);
+			dc_write(hw, hw->reg[i].y_stride + offset,
+				 plane->fb.y_stride);
+			dc_write(hw, hw->reg[i].u_stride + offset,
+				 plane->fb.u_stride);
+			dc_write(hw, hw->reg[i].v_stride + offset,
+				 plane->fb.v_stride);
+			dc_write(hw, hw->reg[i].size + offset,
+				 FB_SIZE(plane->fb.width, plane->fb.height));
+			dc_write(hw, hw->reg[i].water_mark + offset,
+				 plane->fb.water_mark);
+
+			if (plane->fb.clear_enable)
+				dc_write(hw, hw->reg[i].clear_value + offset,
+					 plane->fb.clear_value);
+		}
+
+		dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
+			     PRIMARY_FORMAT(plane->fb.format) |
+			     PRIMARY_UV_SWIZ(plane->fb.uv_swizzle) |
+			     PRIMARY_SWIZ(plane->fb.swizzle) |
+			     PRIMARY_TILE(plane->fb.tile_mode) |
+			     PRIMARY_YUV_COLOR(plane->fb.yuv_color_space) |
+			     PRIMARY_ROTATION(plane->fb.rotation) |
+			     PRIMARY_CLEAR_EN(plane->fb.clear_enable),
+			     PRIMARY_FORMAT_MASK |
+			     PRIMARY_UV_SWIZ_MASK |
+			     PRIMARY_SWIZ_MASK |
+			     PRIMARY_TILE_MASK |
+			     PRIMARY_YUV_COLOR_MASK |
+			     PRIMARY_ROTATION_MASK |
+			     PRIMARY_CLEAR_EN_MASK);
+		dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
+			     PRIMARY_DECODER_EN(plane->fb.dec_enable) |
+			     PRIMARY_EN(plane->fb.enable) |
+			     PRIMARY_ZPOS(plane->fb.zpos) |
+			     PRIMARY_CHANNEL(plane->fb.display_id),
+			     PRIMARY_DECODER_EN_EN_MASK |
+			     PRIMARY_EN_MASK |
+			     PRIMARY_ZPOS_MASK |
+			     PRIMARY_CHANNEL_MASK);
+
+		plane->fb.dirty = false;
+	}
+
+	if (plane->scale.dirty) {
+		if (plane->scale.enable) {
+			dc_write(hw, hw->reg[i].scale_factor_x + offset,
+				 plane->scale.scale_factor_x);
+			dc_write(hw, hw->reg[i].scale_factor_y + offset,
+				 plane->scale.scale_factor_y);
+				dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
+					     PRIMARY_SCALE_EN, 0);
+		} else {
+			dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
+				     0, PRIMARY_SCALE_EN);
+		}
+		plane->scale.dirty = false;
+	}
+
+	if (plane->pos.dirty) {
+		dc_write(hw, hw->reg[i].top_left + offset,
+			 X_POS(plane->pos.start_x) |
+			 Y_POS(plane->pos.start_y));
+		dc_write(hw, hw->reg[i].bottom_right + offset,
+			 X_POS(plane->pos.end_x) |
+			 Y_POS(plane->pos.end_y));
+		plane->pos.dirty = false;
+	}
+
+	if (plane->blend.dirty) {
+		dc_write(hw, hw->reg[i].src_global_color + offset,
+			 PRIMARY_ALPHA_LEN(plane->blend.alpha));
+		dc_write(hw, hw->reg[i].dst_global_color + offset,
+			 PRIMARY_ALPHA_LEN(plane->blend.alpha));
+		switch (plane->blend.blend_mode) {
+		case DRM_MODE_BLEND_PREMULTI:
+			dc_write(hw, hw->reg[i].blend_config + offset, BLEND_PREMULTI);
+			break;
+		case DRM_MODE_BLEND_COVERAGE:
+			dc_write(hw, hw->reg[i].blend_config + offset, BLEND_COVERAGE);
+			break;
+		case DRM_MODE_BLEND_PIXEL_NONE:
+			dc_write(hw, hw->reg[i].blend_config + offset, BLEND_PIXEL_NONE);
+			break;
+		default:
+			break;
+		}
+		plane->blend.dirty = false;
+	}
+}
+
+static void plane_ex_commit_overlay(struct dc_hw *hw, struct dc_hw_plane *plane,
+				    u32 i, u32 offset)
+{
+	if (plane->fb.dirty) {
+		if (is_rgb(plane->fb.format)) {
+			dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+				     OVERLAY_RGB2RGB_EN, OVERLAY_CLAMP_EN);
+		} else {
+			dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+				     OVERLAY_CLAMP_EN, OVERLAY_RGB2RGB_EN);
+
+			switch (plane->fb.yuv_color_space) {
+			case COLOR_SPACE_601:
+				load_yuv_to_rgb(hw, &hw->reg[i], offset, YUV601_2RGB);
+				break;
+			case COLOR_SPACE_709:
+				load_yuv_to_rgb(hw, &hw->reg[i], offset, YUV709_2RGB);
+				break;
+			case COLOR_SPACE_2020:
+				load_yuv_to_rgb(hw, &hw->reg[i], offset, YUV2020_2RGB);
+				break;
+			default:
+				break;
+			}
+		}
+
+		if (plane->fb.enable) {
+			dc_write(hw, hw->reg[i].y_address + offset,
+				 plane->fb.y_address);
+			dc_write(hw, hw->reg[i].u_address + offset,
+				 plane->fb.u_address);
+			dc_write(hw, hw->reg[i].v_address + offset,
+				 plane->fb.v_address);
+			dc_write(hw, hw->reg[i].y_stride + offset,
+				 plane->fb.y_stride);
+			dc_write(hw, hw->reg[i].u_stride + offset,
+				 plane->fb.u_stride);
+			dc_write(hw, hw->reg[i].v_stride + offset,
+				 plane->fb.v_stride);
+			dc_write(hw, hw->reg[i].size + offset,
+				 FB_SIZE(plane->fb.width, plane->fb.height));
+			dc_write(hw, hw->reg[i].water_mark + offset,
+				 plane->fb.water_mark);
+
+			if (plane->fb.clear_enable)
+				dc_write(hw, hw->reg[i].clear_value + offset,
+					 plane->fb.clear_value);
+		}
+
+		dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+			     OVERLAY_DEC_EN(plane->fb.dec_enable) |
+			     OVERLAY_CLEAR_EN(plane->fb.clear_enable) |
+			     OVERLAY_FB_EN(plane->fb.enable) |
+			     OVERLAY_FORMAT(plane->fb.format) |
+			     OVERLAY_UV_SWIZ(plane->fb.uv_swizzle) |
+			     OVERLAY_SWIZ(plane->fb.swizzle) |
+			     OVERLAY_TILE(plane->fb.tile_mode) |
+			     OVERLAY_YUV_COLOR(plane->fb.yuv_color_space) |
+			     OVERLAY_ROTATION(plane->fb.rotation),
+			     OVERLAY_DEC_EN_MASK |
+			     OVERLAY_CLEAR_EN_MASK |
+			     OVERLAY_FB_EN_MASK |
+			     OVERLAY_FORMAT_MASK |
+			     OVERLAY_UV_SWIZ_MASK |
+			     OVERLAY_SWIZ_MASK |
+			     OVERLAY_TILE_MASK |
+			     OVERLAY_YUV_COLOR_MASK |
+			     OVERLAY_ROTATION_MASK);
+
+		dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset,
+			     OVERLAY_LAYER_SEL(plane->fb.zpos) |
+			     OVERLAY_PANEL_SEL(plane->fb.display_id),
+			     OVERLAY_LAYER_SEL_MASK |
+			     OVERLAY_PANEL_SEL_MASK);
+
+		plane->fb.dirty = false;
+	}
+
+	if (plane->scale.dirty) {
+		if (plane->scale.enable) {
+			dc_write(hw, hw->reg[i].scale_factor_x + offset,
+				 plane->scale.scale_factor_x);
+			dc_write(hw, hw->reg[i].scale_factor_y + offset,
+				 plane->scale.scale_factor_y);
+			dc_set_clear(hw, DC_OVERLAY_SCALE_CONFIG + offset,
+				     OVERLAY_SCALE_EN, 0);
+		} else {
+			dc_set_clear(hw, DC_OVERLAY_SCALE_CONFIG + offset,
+				     0, OVERLAY_SCALE_EN);
+		}
+		plane->scale.dirty = false;
+	}
+
+	if (plane->pos.dirty) {
+		dc_write(hw, hw->reg[i].top_left + offset,
+			 X_POS(plane->pos.start_x) |
+			 Y_POS(plane->pos.start_y));
+		dc_write(hw, hw->reg[i].bottom_right + offset,
+			 X_POS(plane->pos.end_x) |
+			 Y_POS(plane->pos.end_y));
+		plane->pos.dirty = false;
+	}
+
+	if (plane->blend.dirty) {
+		dc_write(hw, hw->reg[i].src_global_color + offset,
+			 OVERLAY_ALPHA_LEN(plane->blend.alpha));
+		dc_write(hw, hw->reg[i].dst_global_color + offset,
+			 OVERLAY_ALPHA_LEN(plane->blend.alpha));
+		switch (plane->blend.blend_mode) {
+		case DRM_MODE_BLEND_PREMULTI:
+			dc_write(hw, hw->reg[i].blend_config + offset, BLEND_PREMULTI);
+			break;
+		case DRM_MODE_BLEND_COVERAGE:
+			dc_write(hw, hw->reg[i].blend_config + offset, BLEND_COVERAGE);
+			break;
+		case DRM_MODE_BLEND_PIXEL_NONE:
+			dc_write(hw, hw->reg[i].blend_config + offset, BLEND_PIXEL_NONE);
+			break;
+		default:
+			break;
+		}
+		plane->blend.dirty = false;
+	}
+}
+
+static void plane_ex_commit(struct dc_hw *hw)
+{
+	struct dc_hw_plane *plane;
+	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)
+			plane_ex_commit_primary(hw, plane, i, offset);
+		else
+			plane_ex_commit_overlay(hw, plane, i, offset);
+	}
+}
+
+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, PANEL0_EN | TWO_PANEL_EN);
+		else
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, PANEL1_EN | TWO_PANEL_EN);
+
+		dc_write(hw, DC_DISPLAY_H + offset,
+			 H_ACTIVE_LEN(hw->display[id].h_active) |
+			 H_TOTAL_LEN(hw->display[id].h_total));
+
+		dc_write(hw, DC_DISPLAY_H_SYNC + offset,
+			 H_SYNC_START_LEN(hw->display[id].h_sync_start) |
+			 H_SYNC_END_LEN(hw->display[id].h_sync_end) |
+			 H_POLARITY_LEN(hw->display[id].h_sync_polarity ? 0 : 1) |
+			 H_PLUS_LEN(1));
+
+		dc_write(hw, DC_DISPLAY_V + offset,
+			 V_ACTIVE_LEN(hw->display[id].v_active) |
+			 V_TOTAL_LEN(hw->display[id].v_total));
+
+		dc_write(hw, DC_DISPLAY_V_SYNC + offset,
+			 V_SYNC_START_LEN(hw->display[id].v_sync_start) |
+			 V_SYNC_END_LEN(hw->display[id].v_sync_end) |
+			 V_POLARITY_LEN(hw->display[id].v_sync_polarity ? 0 : 1) |
+			 V_PLUS_LEN(1));
+
+		if (hw->info->pipe_sync)
+			dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
+				     0, PRIMARY_SYNC0_EN | PRIMARY_SYNC1_EN);
+
+		dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, PANEL_OUTPUT_EN, 0);
+		if (id == 0)
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, PANEL0_EN, SYNC_EN);
+		else
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, PANEL1_EN, SYNC_EN);
+	} else {
+		dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, PANEL_OUTPUT_EN);
+
+		if (id == 0)
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, PANEL0_EN | TWO_PANEL_EN);
+		else
+			dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, PANEL1_EN | TWO_PANEL_EN);
+	}
+}
+
+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,
+				     PANEL_RGB2YUV_EN, 0);
+		else
+			dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset,
+				     0, PANEL_RGB2YUV_EN);
+		dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | DP_SELECT);
+	}
+
+	if (hw->out[id] == OUT_DPI)
+		dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, DP_SELECT);
+
+	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,
+					 X_LCOTION(hw->cursor[i].x) |
+					 Y_LCOTION(hw->cursor[i].y));
+				dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
+					     CURSOR_HOT_X(hw->cursor[i].hot_x) |
+					     CURSOR_HOT_y(hw->cursor[i].hot_y) |
+					     CURSOR_SIZE(hw->cursor[i].size) |
+					     CURSOR_VALID(1) |
+					     CURSOR_TRIG_FETCH(1) |
+					     CURSOR_FORMAT(CURSOR_FORMAT_A8R8G8B8),
+					     CURSOR_HOT_X_MASK |
+					     CURSOR_HOT_y_MASK |
+					     CURSOR_SIZE_MASK |
+					     CURSOR_VALID_MASK |
+					     CURSOR_TRIG_FETCH_MASK |
+					     CURSOR_FORMAT_MASK);
+			} else {
+				dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
+					     CURSOR_VALID(1),
+					     CURSOR_FORMAT_MASK);
+			}
+			hw->cursor[i].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 000000000000..fcf51f00adaa
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
@@ -0,0 +1,580 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_DC_HW_H__
+#define __VS_DC_HW_H__
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+
+#define UPDATE(x, h, l)				FIELD_PREP(GENMASK(h, l), x)
+
+#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 PRIMARY_FORMAT(x)			((x) << 26)
+# define PRIMARY_FORMAT_MASK			GENMASK(31, 26)
+# define PRIMARY_UV_SWIZ(x)			((x) << 25)
+# define PRIMARY_UV_SWIZ_MASK			GENMASK(25, 25)
+# define PRIMARY_SWIZ(x)			((x) << 23)
+# define PRIMARY_SWIZ_MASK			GENMASK(24, 23)
+# define PRIMARY_SCALE_EN			BIT(12)
+# define PRIMARY_TILE(x)			((x) << 17)
+# define PRIMARY_TILE_MASK			GENMASK(21, 17)
+# define PRIMARY_YUV_COLOR(x)			((x) << 14)
+# define PRIMARY_YUV_COLOR_MASK			GENMASK(16, 14)
+# define PRIMARY_ROTATION(x)			((x) << 11)
+# define PRIMARY_ROTATION_MASK			GENMASK(13, 11)
+# define PRIMARY_CLEAR_EN(x)			((x) << 8)
+# define PRIMARY_CLEAR_EN_MASK			GENMASK(8, 8)
+
+#define DC_FRAMEBUFFER_CONFIG_EX		0x1CC0
+# define PRIMARY_CHANNEL(x)			((x) << 19)
+# define PRIMARY_CHANNEL_MASK			GENMASK(19, 19)
+# define PRIMARY_ZPOS(x)			((x) << 16)
+# define PRIMARY_ZPOS_MASK			GENMASK(18, 16)
+# define PRIMARY_EN(x)				((x) << 13)
+# define PRIMARY_EN_MASK			GENMASK(13, 13)
+# define PRIMARY_SHADOW_EN			BIT(12)
+# define PRIMARY_YUVCLAMP_EN			BIT(8)
+# define PRIMARY_RGB2RGB_EN			BIT(6)
+# define PRIMARY_SYNC1_EN			BIT(4)
+# define PRIMARY_SYNC0_EN			BIT(3)
+# define PRIMARY_DECODER_EN(x)			((x) << 1)
+# define PRIMARY_DECODER_EN_EN_MASK		GENMASK(1, 1)
+
+#define DC_FRAMEBUFFER_SCALE_CONFIG		0x1520
+#define DC_FRAMEBUFFER_TOP_LEFT			0x24D8
+#define X_POS(x)				(x)
+#define Y_POS(x)				((x) << 15)
+
+#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 FB_SIZE(w, h)				((w) | ((h) << 15))
+
+#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 BLEND_PREMULTI				0x3450
+# define BLEND_COVERAGE				0x3950
+# define BLEND_PIXEL_NONE			0x3548
+
+#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR		0x2500
+# define PRIMARY_ALPHA_LEN(x)			((x) << 24)
+
+#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR		0x2508
+
+#define DC_OVERLAY_CONFIG			0x1540
+# define OVERLAY_SHADOW_EN			BIT(31)
+# define OVERLAY_CLAMP_EN			BIT(30)
+# define OVERLAY_RGB2RGB_EN			BIT(29)
+# define OVERLAY_DEC_EN(x)			((x) << 27)
+# define OVERLAY_DEC_EN_MASK			GENMASK(27, 27)
+# define OVERLAY_CLEAR_EN(x)			((x) << 25)
+# define OVERLAY_CLEAR_EN_MASK			GENMASK(25, 25)
+# define OVERLAY_FB_EN(x)			((x) << 24)
+# define OVERLAY_FB_EN_MASK			GENMASK(24, 24)
+# define OVERLAY_FORMAT(x)			((x) << 16)
+# define OVERLAY_FORMAT_MASK			GENMASK(21, 16)
+# define OVERLAY_UV_SWIZ(x)			((x) << 15)
+# define OVERLAY_UV_SWIZ_MASK			GENMASK(15, 15)
+# define OVERLAY_SWIZ(x)			((x) << 13)
+# define OVERLAY_SWIZ_MASK			GENMASK(14, 13)
+# define OVERLAY_TILE(x)			((x) << 8)
+# define OVERLAY_TILE_MASK			GENMASK(12, 8)
+# define OVERLAY_YUV_COLOR(x)			((x) << 5)
+# define OVERLAY_YUV_COLOR_MASK			GENMASK(7, 5)
+# define OVERLAY_ROTATION(x)			((x) << 2)
+# define OVERLAY_ROTATION_MASK			GENMASK(4, 2)
+
+#define DC_OVERLAY_CONFIG_EX			0x2540
+# define OVERLAY_LAYER_SEL(x)			((x) << 0)
+# define OVERLAY_LAYER_SEL_MASK			GENMASK(2, 0)
+# define OVERLAY_PANEL_SEL(x)			((x) << 3)
+# define OVERLAY_PANEL_SEL_MASK			GENMASK(3, 3)
+
+#define DC_OVERLAY_SCALE_CONFIG			0x1C00
+# define OVERLAY_SCALE_EN			BIT(8)
+
+#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 OVERLAY_ALPHA_LEN(x)			((x) << 24)
+
+#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 CURSOR_HOT_X(x)			((x) << 16)
+# define CURSOR_HOT_X_MASK			GENMASK(23, 16)
+# define CURSOR_HOT_y(x)			((x) << 8)
+# define CURSOR_HOT_y_MASK			GENMASK(15, 8)
+# define CURSOR_SIZE(x)				((x) << 5)
+# define CURSOR_SIZE_MASK			GENMASK(7, 5)
+# define CURSOR_VALID(x)			((x) << 3)
+# define CURSOR_VALID_MASK			GENMASK(3, 3)
+# define CURSOR_TRIG_FETCH(x)			((x) << 2)
+# define CURSOR_TRIG_FETCH_MASK			GENMASK(2, 2)
+# define CURSOR_FORMAT(x)			((x) << 0)
+# define CURSOR_FORMAT_MASK			GENMASK(1, 0)
+# define CURSOR_FORMAT_DISABLE			0
+# define CURSOR_FORMAT_MARK			1
+# define CURSOR_FORMAT_A8R8G8B8			2
+
+#define DC_CURSOR_ADDRESS			0x146C
+#define DC_CURSOR_LOCATION			0x1470
+# define X_LCOTION(x)				(x)
+# define Y_LCOTION(x)				((x) << 16)
+
+#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 PANEL_RGB2YUV_EN			BIT(16)
+# define PANEL_GAMMA_EN				BIT(13)
+# define PANEL_OUTPUT_EN			BIT(12)
+
+#define DC_DISPLAY_PANEL_CONFIG_EX		0x2518
+# define PANEL_SHADOW_EN			BIT(0)
+
+#define DC_DISPLAY_DITHER_TABLE_LOW		0x1420
+#define DC_DISPLAY_DITHER_TABLE_HIGH		0x1428
+#define DC_DISPLAY_H				0x1430
+# define H_ACTIVE_LEN(x)			(x)
+# define H_TOTAL_LEN(x)				((x) << 16)
+
+#define DC_DISPLAY_H_SYNC			0x1438
+# define H_SYNC_START_LEN(x)			(x)
+# define H_SYNC_END_LEN(x)			((x) << 15)
+# define H_PLUS_LEN(x)				((x) << 30)
+# define H_POLARITY_LEN(x)			((x) << 31)
+
+#define DC_DISPLAY_V				0x1440
+# define V_ACTIVE_LEN(x)			(x)
+# define V_TOTAL_LEN(x)				((x) << 16)
+
+#define DC_DISPLAY_V_SYNC			0x1448
+# define V_SYNC_START_LEN(x)			(x)
+# define V_SYNC_END_LEN(x)			((x) << 15)
+# define V_PLUS_LEN(x)				((x) << 30)
+# define V_POLARITY_LEN(x)			((x) << 31)
+
+#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 PANEL0_EN				BIT(0)
+# define PANEL1_EN				BIT(1)
+# define TWO_PANEL_EN				BIT(2)
+# define SYNC_EN				BIT(3)
+
+#define DC_DISPLAY_DEBUG_COUNTER_SELECT		0x14D0
+#define DC_DISPLAY_DEBUG_COUNTER_VALUE		0x14D8
+#define DC_DISPLAY_DP_CONFIG			0x1CD0
+# define DP_SELECT				BIT(3)
+
+#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_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,
+	FORMAT_A4R4G4B4,
+	FORMAT_X1R5G5B5,
+	FORMAT_A1R5G5B5,
+	FORMAT_R5G6B5,
+	FORMAT_X8R8G8B8,
+	FORMAT_A8R8G8B8,
+	FORMAT_YUY2,
+	FORMAT_UYVY,
+	FORMAT_INDEX8,
+	FORMAT_MONOCHROME,
+	FORMAT_YV12 = 0xf,
+	FORMAT_A8,
+	FORMAT_NV12,
+	FORMAT_NV16,
+	FORMAT_RG16,
+	FORMAT_R8,
+	FORMAT_NV12_10BIT,
+	FORMAT_A2R10G10B10,
+	FORMAT_NV16_10BIT,
+	FORMAT_INDEX1,
+	FORMAT_INDEX2,
+	FORMAT_INDEX4,
+	FORMAT_P010,
+	FORMAT_YUV444,
+	FORMAT_YUV444_10BIT,
+};
+
+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,
+};
+
+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_plane {
+	struct dc_hw_fb			fb;
+	struct dc_hw_position	pos;
+	struct dc_hw_scale		scale;
+	struct dc_hw_blend		blend;
+};
+
+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_hw_out		out[DC_DISPLAY_NUM];
+	void			*hi_base;
+	void			*reg_base;
+	struct dc_hw_plane_reg	reg[DC_LAYER_NUM];
+
+	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_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_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);
+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 4fb1f29ef84b..3ef90c8238a0 100644
--- a/drivers/gpu/drm/verisilicon/vs_drv.c
+++ b/drivers/gpu/drm/verisilicon/vs_drv.c
@@ -22,6 +22,7 @@
 
 #include "vs_drv.h"
 #include "vs_modeset.h"
+#include "vs_dc.h"
 
 #define DRV_NAME	"verisilicon"
 #define DRV_DESC	"Verisilicon DRM driver"
@@ -212,6 +213,7 @@ static const struct component_master_ops vs_drm_ops = {
 };
 
 static struct platform_driver *drm_sub_drivers[] = {
+	&dc_platform_driver,
 };
 
 static struct component_match *vs_drm_match_add(struct device *dev)
diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c
new file mode 100644
index 000000000000..53580fbe05b1
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_plane.c
@@ -0,0 +1,301 @@
+// 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 "vs_plane.h"
+#include "vs_drv.h"
+#include "vs_dc.h"
+
+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);
+
+	kfree(vs_plane_state);
+}
+
+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)
+		vs_plane_atomic_destroy_state(plane, plane->state);
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return;
+
+	state->base.zpos = vs_plane->id;
+	__drm_atomic_helper_plane_reset(plane, &state->base);
+}
+
+static struct drm_plane_state *
+vs_plane_atomic_duplicate_state(struct drm_plane *plane)
+{
+	struct vs_plane_state *old_state;
+	struct vs_plane_state *state;
+
+	if (WARN_ON(!plane->state))
+		return NULL;
+
+	old_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);
+
+	return &state->base;
+}
+
+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,
+	.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;
+	}
+
+	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;
+	}
+
+	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,
+};
+
+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;
+
+	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);
+
+	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 000000000000..7174ac298ffd
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_plane.h
@@ -0,0 +1,39 @@
+/* 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_state {
+	struct drm_plane_state base;
+	dma_addr_t dma_addr[DRM_FORMAT_MAX_PLANES];
+};
+
+struct vs_plane {
+	struct drm_plane base;
+	u8 id;
+};
+
+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 000000000000..7d3378e29c87
--- /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] 52+ messages in thread

* [v3 5/6] drm/vs: Add hdmi driver
  2023-12-04 12:33 [v3 0/6] DRM driver for verisilicon Keith Zhao
                   ` (3 preceding siblings ...)
  2023-12-04 12:33 ` [v3 4/6] drm/vs: Add KMS crtc&plane Keith Zhao
@ 2023-12-04 12:33 ` Keith Zhao
  2023-12-05 13:02   ` Dmitry Baryshkov
                     ` (2 more replies)
  2023-12-04 12:33 ` [v3 6/6] drm/vs: simple encoder Keith Zhao
                   ` (3 subsequent siblings)
  8 siblings, 3 replies; 52+ messages in thread
From: Keith Zhao @ 2023-12-04 12:33 UTC (permalink / raw)
  To: Keith Zhao, devicetree, dri-devel, linux-kernel, linux-riscv
  Cc: aou, suijingfeng, tzimmermann, paul.walmsley, mripard, xingyu.wu,
	jack.zhu, palmer, krzysztof.kozlowski+dt, william.qiu,
	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 | 849 ++++++++++++++++++++
 drivers/gpu/drm/verisilicon/starfive_hdmi.h | 304 +++++++
 drivers/gpu/drm/verisilicon/vs_drv.c        |   3 +
 drivers/gpu/drm/verisilicon/vs_drv.h        |   4 +
 6 files changed, 1169 insertions(+)
 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 e10fa97635aa..122c786e3948 100644
--- a/drivers/gpu/drm/verisilicon/Kconfig
+++ b/drivers/gpu/drm/verisilicon/Kconfig
@@ -11,3 +11,11 @@ config DRM_VERISILICON
 	  This driver provides VeriSilicon kernel mode
 	  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 bf6f2b7ee480..71fadafcee13 100644
--- a/drivers/gpu/drm/verisilicon/Makefile
+++ b/drivers/gpu/drm/verisilicon/Makefile
@@ -6,4 +6,5 @@ vs_drm-objs := vs_dc_hw.o \
 		vs_drv.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 000000000000..aa621db0dee0
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
@@ -0,0 +1,849 @@
+// 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 const char * const hdmi_clocks[] = {
+	"sysclk",
+	"mclk",
+	"bclk"
+};
+
+static struct starfive_hdmi_encoder *encoder_to_hdmi(struct drm_encoder *encoder)
+{
+	return container_of(encoder, struct starfive_hdmi_encoder, encoder);
+}
+
+static struct starfive_hdmi *connector_to_hdmi(struct drm_connector *connector)
+{
+	return container_of(connector, struct starfive_hdmi, connector);
+}
+
+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_writew(struct starfive_hdmi *hdmi, u16 offset, u32 val)
+{
+	writew_relaxed(val & 0xFF, hdmi->regs + (offset) * 0x04);
+	writew_relaxed((val >> 8) & 0xFF, hdmi->regs + (offset + 1) * 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_bulk_prepare_enable(hdmi->nclks, hdmi->clk_hdmi);
+	if (ret) {
+		dev_err(dev, "failed to enable clocks\n");
+		return ret;
+	}
+
+	ret = reset_control_deassert(hdmi->tx_rst);
+	if (ret < 0) {
+		dev_err(dev, "failed to deassert tx_rst\n");
+		return ret;
+	}
+	return 0;
+}
+
+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_bulk_disable_unprepare(hdmi->nclks, hdmi->clk_hdmi);
+}
+
+#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->clk_hdmi[CLK_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 void starfive_hdmi_phy_get_pre_pll_cfg(struct starfive_hdmi *hdmi)
+{
+	if (hdmi->tmds_rate > 30000000) {
+		hdmi->pre_cfg.pixclock = hdmi->tmds_rate;
+		hdmi->pre_cfg.tmdsclock = hdmi->tmds_rate;
+		hdmi->pre_cfg.prediv = 1;
+		hdmi->pre_cfg.fbdiv = hdmi->tmds_rate / 3000000;
+		hdmi->pre_cfg.tmds_div_a = 0;
+		hdmi->pre_cfg.tmds_div_b = 1;
+		hdmi->pre_cfg.tmds_div_c = 1;
+		hdmi->pre_cfg.pclk_div_a = 1;
+		hdmi->pre_cfg.pclk_div_b = 0;
+		hdmi->pre_cfg.pclk_div_c = 2;
+		hdmi->pre_cfg.pclk_div_d = 2;
+		hdmi->pre_cfg.vco_div_5_en = hdmi->tmds_rate % 3000000 ? 1 : 0;
+
+		if (hdmi->pre_cfg.vco_div_5_en) {
+			hdmi->pre_cfg.fracdiv = (hdmi->tmds_rate % 3000000) *
+						 0xffffff / 1000000;
+		}
+	} else {
+		hdmi->pre_cfg.pixclock = hdmi->tmds_rate;
+		hdmi->pre_cfg.tmdsclock = hdmi->tmds_rate;
+		hdmi->pre_cfg.prediv = 1;
+		hdmi->pre_cfg.fbdiv = hdmi->tmds_rate / 1000000;
+		hdmi->pre_cfg.tmds_div_a = 2;
+		hdmi->pre_cfg.tmds_div_b = 1;
+		hdmi->pre_cfg.tmds_div_c = 1;
+		hdmi->pre_cfg.pclk_div_a = 3;
+		hdmi->pre_cfg.pclk_div_b = 0;
+		hdmi->pre_cfg.pclk_div_c = 3;
+		hdmi->pre_cfg.pclk_div_d = 4;
+		hdmi->pre_cfg.vco_div_5_en = hdmi->tmds_rate % 1000000 ? 1 : 0;
+
+		if (hdmi->pre_cfg.vco_div_5_en) {
+			hdmi->pre_cfg.fracdiv = (hdmi->tmds_rate % 1000000) *
+						 0xffffff / 1000000;
+		}
+	}
+}
+
+static int starfive_hdmi_phy_clk_set_rate(struct starfive_hdmi *hdmi)
+{
+	hdmi->post_cfg = post_pll_cfg_table;
+
+	starfive_hdmi_phy_get_pre_pll_cfg(hdmi);
+
+	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_writew(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value);
+
+	value = mode->htotal - mode->hdisplay;
+	hdmi_writew(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value);
+
+	value = mode->htotal - mode->hsync_start;
+	hdmi_writew(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value);
+
+	value = mode->hsync_end - mode->hsync_start;
+	hdmi_writew(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value);
+
+	value = mode->vtotal;
+	hdmi_writew(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value);
+
+	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)
+{
+	int ret;
+	u32 val;
+
+	hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
+	hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
+
+	hdmi->tmds_rate = mode->clock * 1000;
+	starfive_hdmi_phy_clk_set_rate(hdmi);
+
+	ret = readx_poll_timeout(readl_relaxed,
+				 hdmi->regs + (STARFIVE_PRE_PLL_LOCK_STATUS) * 0x04,
+				 val, val & 0x1, 1000, 100000);
+	if (ret < 0) {
+		dev_err(hdmi->dev, "failed to wait pre-pll lock\n");
+		return ret;
+	}
+
+	ret = readx_poll_timeout(readl_relaxed,
+				 hdmi->regs + (STARFIVE_POST_PLL_LOCK_STATUS) * 0x04,
+				 val, val & 0x1, 1000, 100000);
+	if (ret < 0) {
+		dev_err(hdmi->dev, "failed to wait post-pll lock\n");
+		return ret;
+	}
+
+	/*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_enable(struct drm_encoder *encoder)
+{
+	struct starfive_hdmi_encoder *hdmi_encoder = encoder_to_hdmi(encoder);
+	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
+	int ret, idx;
+	struct drm_device *drm = hdmi_encoder->hdmi->connector.dev;
+
+	if (drm && !drm_dev_enter(drm, &idx))
+		return;
+
+	ret = pm_runtime_get_sync(hdmi_encoder->hdmi->dev);
+	if (ret < 0)
+		return;
+	starfive_hdmi_setup(hdmi_encoder->hdmi, mode);
+
+	if (drm)
+		drm_dev_exit(idx);
+}
+
+static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct starfive_hdmi_encoder *hdmi_encoder = encoder_to_hdmi(encoder);
+
+	pm_runtime_put(hdmi_encoder->hdmi->dev);
+}
+
+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;
+
+	int pclk = mode->clock * 1000;
+
+	if (pclk <= PIXCLOCK_4K_30FPS)
+		valid = true;
+
+	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,
+	.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);
+	int ret = 0;
+
+	if (!hdmi->ddc)
+		return 0;
+	ret = pm_runtime_get_sync(hdmi->dev);
+	if (ret < 0)
+		return ret;
+
+	ret = drm_connector_helper_get_modes_from_ddc(connector);
+	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)
+{
+	int pclk = mode->clock * 1000;
+	bool valid = false;
+
+	if (pclk <= PIXCLOCK_4K_30FPS)
+		valid = true;
+
+	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_encoder *hdmi_encoder)
+{
+	struct drm_encoder *encoder = &hdmi_encoder->encoder;
+	struct device *dev = hdmi_encoder->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_encoder->hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+	drm_connector_helper_add(&hdmi_encoder->hdmi->connector,
+				 &starfive_hdmi_connector_helper_funcs);
+	drmm_connector_init(drm, &hdmi_encoder->hdmi->connector,
+			    &starfive_hdmi_connector_funcs,
+			    DRM_MODE_CONNECTOR_HDMIA,
+			    hdmi_encoder->hdmi->ddc);
+
+	drm_connector_attach_encoder(&hdmi_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 = devm_kzalloc(hdmi->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) {
+		dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
+		devm_kfree(hdmi->dev, i2c);
+		return ERR_PTR(ret);
+	}
+
+	hdmi->i2c = i2c;
+
+	DRM_DEV_INFO(hdmi->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)
+{
+	int ret;
+
+	hdmi->nclks = ARRAY_SIZE(hdmi->clk_hdmi);
+	for (int i = 0; i < hdmi->nclks; ++i)
+		hdmi->clk_hdmi[i].id = hdmi_clocks[i];
+
+	ret = devm_clk_bulk_get(dev, hdmi->nclks, hdmi->clk_hdmi);
+	if (ret) {
+		dev_err(dev, "Failed to get clk controls\n");
+		return ret;
+	}
+
+	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 drm_device *drm = dev_get_drvdata(master);
+	struct starfive_hdmi_encoder *hdmi_encoder;
+	int ret;
+
+	hdmi_encoder = drmm_simple_encoder_alloc(drm, struct starfive_hdmi_encoder,
+						 encoder, DRM_MODE_ENCODER_TMDS);
+	if (IS_ERR(hdmi_encoder))
+		return PTR_ERR(hdmi_encoder);
+
+	hdmi_encoder->hdmi = dev_get_drvdata(dev);
+	hdmi_encoder->hdmi->drm_dev = drm;
+
+	ret = pm_runtime_resume_and_get(dev);
+	if (ret)
+		return ret;
+
+	starfive_hdmi_i2c_init(hdmi_encoder->hdmi);
+
+	ret = starfive_hdmi_register(drm, hdmi_encoder);
+	if (ret)
+		goto err_put_adapter;
+
+	/* Unmute hotplug interrupt */
+	hdmi_modb(hdmi_encoder->hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
+
+	ret = devm_request_threaded_irq(dev, hdmi_encoder->hdmi->irq, starfive_hdmi_hardirq,
+					starfive_hdmi_irq, IRQF_SHARED,
+					dev_name(dev), hdmi_encoder->hdmi);
+	if (ret < 0)
+		goto err_put_adapter;
+
+	pm_runtime_put_sync(dev);
+
+	return 0;
+
+err_put_adapter:
+	i2c_put_adapter(hdmi_encoder->hdmi->ddc);
+	return ret;
+}
+
+static const struct component_ops starfive_hdmi_ops = {
+	.bind	= starfive_hdmi_bind,
+};
+
+static int starfive_hdmi_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct starfive_hdmi *hdmi;
+	struct resource *iores;
+
+	hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return -ENOMEM;
+
+	dev_set_drvdata(&pdev->dev, hdmi);
+	hdmi->dev = &pdev->dev;
+
+	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	hdmi->regs = devm_ioremap_resource(hdmi->dev, iores);
+	if (IS_ERR(hdmi->regs))
+		return PTR_ERR(hdmi->regs);
+
+	ret = starfive_hdmi_get_clk_rst(hdmi->dev, hdmi);
+	if (ret < 0)
+		return ret;
+
+	ret = devm_pm_runtime_enable(hdmi->dev);
+	if (ret)
+		return ret;
+
+	hdmi->irq = platform_get_irq(pdev, 0);
+	if (hdmi->irq < 0) {
+		ret = hdmi->irq;
+		return ret;
+	}
+
+	hdmi->ddc = starfive_hdmi_i2c_adapter(hdmi);
+	if (IS_ERR(hdmi->ddc)) {
+		ret = PTR_ERR(hdmi->ddc);
+		hdmi->ddc = NULL;
+		return ret;
+	}
+
+	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 000000000000..ca5f40be0796
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
@@ -0,0 +1,304 @@
+/* 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)
+
+#define PIXCLOCK_4K_30FPS					297000000
+
+enum hdmi_clk {
+	CLK_SYS = 0,
+	CLK_M,
+	CLK_B,
+	CLK_HDMI_NUM
+};
+
+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 starfive_hdmi_encoder {
+	struct drm_encoder encoder;
+	struct starfive_hdmi *hdmi;
+};
+
+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;
+};
+
+struct starfive_hdmi {
+	struct device *dev;
+	struct drm_device *drm_dev;
+	struct drm_connector	connector;
+	void __iomem *regs;
+
+	int irq;
+	struct clk_bulk_data	clk_hdmi[CLK_HDMI_NUM];
+	struct reset_control *tx_rst;
+	int	nclks;
+
+	struct i2c_adapter *ddc;
+	struct starfive_hdmi_i2c *i2c;
+
+	unsigned long tmds_rate;
+	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 3ef90c8238a0..d7e5199fe293 100644
--- a/drivers/gpu/drm/verisilicon/vs_drv.c
+++ b/drivers/gpu/drm/verisilicon/vs_drv.c
@@ -214,6 +214,9 @@ static const struct component_master_ops vs_drm_ops = {
 
 static struct platform_driver *drm_sub_drivers[] = {
 	&dc_platform_driver,
+#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 ea2189772980..9a88cf9a7362 100644
--- a/drivers/gpu/drm/verisilicon/vs_drv.h
+++ b/drivers/gpu/drm/verisilicon/vs_drv.h
@@ -39,4 +39,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] 52+ messages in thread

* [v3 6/6] drm/vs: simple encoder
  2023-12-04 12:33 [v3 0/6] DRM driver for verisilicon Keith Zhao
                   ` (4 preceding siblings ...)
  2023-12-04 12:33 ` [v3 5/6] drm/vs: Add hdmi driver Keith Zhao
@ 2023-12-04 12:33 ` Keith Zhao
  2023-12-05 13:14   ` Dmitry Baryshkov
  2023-12-05  6:55 ` [v3 0/6] DRM driver for verisilicon Krzysztof Kozlowski
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 52+ messages in thread
From: Keith Zhao @ 2023-12-04 12:33 UTC (permalink / raw)
  To: Keith Zhao, devicetree, dri-devel, linux-kernel, linux-riscv
  Cc: aou, suijingfeng, tzimmermann, paul.walmsley, mripard, xingyu.wu,
	jack.zhu, palmer, krzysztof.kozlowski+dt, william.qiu,
	shengyang.chen, changhuang.liang

add simple encoder for dsi bridge

Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
---
 drivers/gpu/drm/verisilicon/Makefile        |   4 +-
 drivers/gpu/drm/verisilicon/vs_drv.c        |   2 +
 drivers/gpu/drm/verisilicon/vs_simple_enc.c | 195 ++++++++++++++++++++
 drivers/gpu/drm/verisilicon/vs_simple_enc.h |  23 +++
 4 files changed, 223 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/verisilicon/vs_simple_enc.c
 create mode 100644 drivers/gpu/drm/verisilicon/vs_simple_enc.h

diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
index 71fadafcee13..cd5d0a90bcfe 100644
--- a/drivers/gpu/drm/verisilicon/Makefile
+++ b/drivers/gpu/drm/verisilicon/Makefile
@@ -5,6 +5,8 @@ vs_drm-objs := vs_dc_hw.o \
 		vs_crtc.o \
 		vs_drv.o \
 		vs_modeset.o \
-		vs_plane.o
+		vs_plane.o \
+		vs_simple_enc.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/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
index d7e5199fe293..946f137ab124 100644
--- a/drivers/gpu/drm/verisilicon/vs_drv.c
+++ b/drivers/gpu/drm/verisilicon/vs_drv.c
@@ -23,6 +23,7 @@
 #include "vs_drv.h"
 #include "vs_modeset.h"
 #include "vs_dc.h"
+#include "vs_simple_enc.h"
 
 #define DRV_NAME	"verisilicon"
 #define DRV_DESC	"Verisilicon DRM driver"
@@ -217,6 +218,7 @@ static struct platform_driver *drm_sub_drivers[] = {
 #ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
 	&starfive_hdmi_driver,
 #endif
+	&simple_encoder_driver,
 };
 
 static struct component_match *vs_drm_match_add(struct device *dev)
diff --git a/drivers/gpu/drm/verisilicon/vs_simple_enc.c b/drivers/gpu/drm/verisilicon/vs_simple_enc.c
new file mode 100644
index 000000000000..c5a8d82bc469
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_simple_enc.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+#include <linux/component.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_of.h>
+#include <linux/regmap.h>
+#include <linux/media-bus-format.h>
+#include <linux/mfd/syscon.h>
+
+#include "vs_crtc.h"
+#include "vs_simple_enc.h"
+
+static const struct simple_encoder_priv dsi_priv = {
+	.encoder_type = DRM_MODE_ENCODER_DSI
+};
+
+static inline struct simple_encoder *to_simple_encoder(struct drm_encoder *enc)
+{
+	return container_of(enc, struct simple_encoder, encoder);
+}
+
+static int encoder_parse_dt(struct device *dev)
+{
+	struct simple_encoder *simple = dev_get_drvdata(dev);
+	unsigned int args[2];
+
+	simple->dss_regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node,
+								  "starfive,syscon",
+								  2, args);
+
+	if (IS_ERR(simple->dss_regmap)) {
+		return dev_err_probe(dev, PTR_ERR(simple->dss_regmap),
+				     "getting the regmap failed\n");
+	}
+
+	simple->offset = args[0];
+	simple->mask = args[1];
+
+	return 0;
+}
+
+void encoder_atomic_enable(struct drm_encoder *encoder,
+			   struct drm_atomic_state *state)
+{
+	struct simple_encoder *simple = to_simple_encoder(encoder);
+
+	regmap_update_bits(simple->dss_regmap, simple->offset, simple->mask,
+			   simple->mask);
+}
+
+int encoder_atomic_check(struct drm_encoder *encoder,
+			 struct drm_crtc_state *crtc_state,
+			 struct drm_connector_state *conn_state)
+{
+	struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
+	struct drm_connector *connector = conn_state->connector;
+	int ret = 0;
+
+	struct drm_bridge *first_bridge = drm_bridge_chain_get_first_bridge(encoder);
+	struct drm_bridge_state *bridge_state = ERR_PTR(-EINVAL);
+
+	vs_crtc_state->encoder_type = encoder->encoder_type;
+
+	if (first_bridge && first_bridge->funcs->atomic_duplicate_state)
+		bridge_state = drm_atomic_get_bridge_state(crtc_state->state, first_bridge);
+
+	if (IS_ERR(bridge_state)) {
+		if (connector->display_info.num_bus_formats)
+			vs_crtc_state->output_fmt = connector->display_info.bus_formats[0];
+		else
+			vs_crtc_state->output_fmt = MEDIA_BUS_FMT_FIXED;
+	} else {
+		vs_crtc_state->output_fmt = bridge_state->input_bus_cfg.format;
+	}
+
+	switch (vs_crtc_state->output_fmt) {
+	case MEDIA_BUS_FMT_FIXED:
+	case MEDIA_BUS_FMT_RGB565_1X16:
+	case MEDIA_BUS_FMT_RGB666_1X18:
+	case MEDIA_BUS_FMT_RGB888_1X24:
+	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+	case MEDIA_BUS_FMT_RGB101010_1X30:
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+	case MEDIA_BUS_FMT_UYVY8_1X16:
+	case MEDIA_BUS_FMT_YUV8_1X24:
+	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+	case MEDIA_BUS_FMT_UYVY10_1X20:
+	case MEDIA_BUS_FMT_YUV10_1X30:
+		ret = 0;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	/* If MEDIA_BUS_FMT_FIXED, set it to default value */
+	if (vs_crtc_state->output_fmt == MEDIA_BUS_FMT_FIXED)
+		vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
+
+	return ret;
+}
+
+static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
+	.atomic_check = encoder_atomic_check,
+	.atomic_enable = encoder_atomic_enable,
+};
+
+static int encoder_bind(struct device *dev, struct device *master, void *data)
+{
+	struct drm_device *drm_dev = data;
+	struct simple_encoder *simple = dev_get_drvdata(dev);
+	struct drm_encoder *encoder;
+	struct drm_bridge *bridge;
+	int ret;
+
+	encoder = &simple->encoder;
+
+	ret = drmm_encoder_init(drm_dev, encoder, NULL, simple->priv->encoder_type, NULL);
+	if (ret)
+		return ret;
+
+	drm_encoder_helper_add(encoder, &encoder_helper_funcs);
+
+	encoder->possible_crtcs =
+			drm_of_find_possible_crtcs(drm_dev, dev->of_node);
+
+	/* output port is port1*/
+	bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
+	if (IS_ERR(bridge))
+		return 0;
+
+	return drm_bridge_attach(encoder, bridge, NULL, 0);
+}
+
+static const struct component_ops encoder_component_ops = {
+	.bind = encoder_bind,
+};
+
+static const struct of_device_id simple_encoder_dt_match[] = {
+	{ .compatible = "starfive,dsi-encoder", .data = &dsi_priv},
+	{},
+};
+MODULE_DEVICE_TABLE(of, simple_encoder_dt_match);
+
+static int encoder_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct simple_encoder *simple;
+	int ret;
+
+	simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
+	if (!simple)
+		return -ENOMEM;
+
+	simple->priv = of_device_get_match_data(dev);
+
+	simple->dev = dev;
+
+	dev_set_drvdata(dev, simple);
+
+	ret = encoder_parse_dt(dev);
+	if (ret)
+		return ret;
+
+	return component_add(dev, &encoder_component_ops);
+}
+
+static int encoder_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	component_del(dev, &encoder_component_ops);
+	dev_set_drvdata(dev, NULL);
+
+	return 0;
+}
+
+struct platform_driver simple_encoder_driver = {
+	.probe = encoder_probe,
+	.remove = encoder_remove,
+	.driver = {
+		.name = "vs-simple-encoder",
+		.of_match_table = of_match_ptr(simple_encoder_dt_match),
+	},
+};
+
+MODULE_DESCRIPTION("Simple Encoder Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/verisilicon/vs_simple_enc.h b/drivers/gpu/drm/verisilicon/vs_simple_enc.h
new file mode 100644
index 000000000000..fb33ca9e18d6
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_simple_enc.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_SIMPLE_ENC_H_
+#define __VS_SIMPLE_ENC_H_
+
+struct simple_encoder_priv {
+	unsigned char encoder_type;
+};
+
+struct simple_encoder {
+	struct drm_encoder encoder;
+	struct device *dev;
+	const struct simple_encoder_priv *priv;
+	struct regmap *dss_regmap;
+	unsigned int offset;
+	unsigned int mask;
+};
+
+extern struct platform_driver simple_encoder_driver;
+#endif /* __VS_SIMPLE_ENC_H_ */
-- 
2.34.1


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

* Re: [v3 3/6] drm/vs: Register DRM device
  2023-12-04 12:33 ` [v3 3/6] drm/vs: Register DRM device Keith Zhao
@ 2023-12-04 13:30   ` Philipp Zabel
  2023-12-11  9:00     ` Keith Zhao
  2023-12-05 11:33   ` Dmitry Baryshkov
  1 sibling, 1 reply; 52+ messages in thread
From: Philipp Zabel @ 2023-12-04 13:30 UTC (permalink / raw)
  To: Keith Zhao, devicetree, dri-devel, linux-kernel, linux-riscv
  Cc: aou, suijingfeng, tzimmermann, paul.walmsley, mripard, xingyu.wu,
	jack.zhu, palmer, krzysztof.kozlowski+dt, william.qiu,
	shengyang.chen, changhuang.liang

Hi Keith,

On Mo, 2023-12-04 at 20:33 +0800, Keith Zhao wrote:
> Implement drm device registration interface
> 
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
[...]
> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
> new file mode 100644
> index 000000000000..e10fa97635aa
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/Kconfig
> @@ -0,0 +1,13 @@
> +# 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 seems a bit generic. Doesn't the VeriSilicon display controller IP
used on JH7110 have a product name?

[...]
> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
> new file mode 100644
> index 000000000000..4fb1f29ef84b
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> @@ -0,0 +1,316 @@
> +// SPDX-License-Identifier: GPL-2.0
[...]
> +static void vs_drm_device_release_clocks(void *res)
> +{
> +	struct vs_drm_device *priv = res;
> +	unsigned int i;
> +
> +	reset_control_bulk_assert(priv->nrsts, priv->rst_vout);
> +
> +	for (i = 0; i < priv->clk_count; ++i) {
> +		if (priv->clks[i]) {
> +			clk_disable_unprepare(priv->clks[i]);
> +			clk_put(priv->clks[i]);
> +		}
> +	}

Why not use the bulk API for clk as well?

[...]
> +static int vs_drm_device_init_clocks(struct vs_drm_device *priv)
> +{
> +	struct drm_device *dev = &priv->base;
> +	struct platform_device *pdev = to_platform_device(dev->dev);
> +	struct device_node *of_node = pdev->dev.of_node;
> +	struct clk *clock;
> +	unsigned int i;
> +	int ret;
> +
> +	if (dev_get_platdata(&pdev->dev) || !of_node)
> +		return 0;
> +
> +	priv->nrsts = ARRAY_SIZE(priv->rst_vout);
> +	for (int i = 0; i < priv->nrsts; ++i)
> +		priv->rst_vout[i].id = vout_resets[i];
> +	ret = devm_reset_control_bulk_get_shared(dev->dev, priv->nrsts,
> +						 priv->rst_vout);

I would request resets and clocks in _probe().

If component_bind_all() returns -EPROBE_DEFER because of a still
missing DSI panel backlight or similar, this doesn't have to be done
multiple times.

> +	if (ret) {
> +		drm_err(dev, "Failed to get reset controls\n");
> +		return ret;
> +	}
> +
> +	priv->clk_count = of_clk_get_parent_count(of_node);
> +	if (!priv->clk_count)
> +		return 0;
> +
> +	priv->clks = drmm_kzalloc(dev, priv->clk_count * sizeof(priv->clks[0]),
> +				  GFP_KERNEL);
> +	if (!priv->clks)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < priv->clk_count; ++i) {
> +		clock = of_clk_get(of_node, i);
> +		if (IS_ERR(clock)) {
> +			ret = PTR_ERR(clock);
> +			if (ret == -EPROBE_DEFER)
> +				goto err;
> +			drm_err(dev, "clock %u not found: %d\n", i, ret);
> +			continue;
> +		}
> +		ret = clk_prepare_enable(clock);
> +		if (ret) {
> +			drm_err(dev, "failed to enable clock %u: %d\n",
> +				i, ret);
> +			clk_put(clock);
> +			continue;
> +		}
> +		priv->clks[i] = clock;
> +	}
> +
> +	ret = reset_control_bulk_deassert(priv->nrsts, priv->rst_vout);
> +	if (ret)
> +		return ret;

This should goto err, otherwise clocks are left enabled.

> +
> +	return devm_add_action_or_reset(&pdev->dev,
> +					vs_drm_device_release_clocks,
> +					priv);
> +
> +err:
> +	while (i) {
> +		--i;
> +		if (priv->clks[i]) {
> +			clk_disable_unprepare(priv->clks[i]);
> +			clk_put(priv->clks[i]);
> +		}
> +	}
> +	return ret;
> +}
> +
> +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;

Why is this a variable instead of a constant?

> +	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);
> +
> +	ret = vs_drm_device_init_clocks(priv);
> +	if (ret)
> +		return ret;
> +
> +	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;

Missing component_unbind_all(), see below.

> +
> +	drm_mode_config_reset(drm_dev);
> +
> +	drm_kms_helper_poll_init(drm_dev);
> +
> +	ret = drm_dev_register(drm_dev, 0);
> +	if (ret)
> +		return ret;

Missing drm_kms_helper_poll_fini(), see below.

> +
> +	drm_fbdev_generic_setup(drm_dev, 32);
> +
> +	return 0;

Here I'd expect an error path calling drm_kms_helper_poll_fini() and
component_unbind_all() as appropriate.


regards
Philipp

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

* Re: [v3 0/6] DRM driver for verisilicon
  2023-12-04 12:33 [v3 0/6] DRM driver for verisilicon Keith Zhao
                   ` (5 preceding siblings ...)
  2023-12-04 12:33 ` [v3 6/6] drm/vs: simple encoder Keith Zhao
@ 2023-12-05  6:55 ` Krzysztof Kozlowski
  2023-12-05 10:27 ` Sui Jingfeng
  2023-12-05 11:59 ` Sui Jingfeng
  8 siblings, 0 replies; 52+ messages in thread
From: Krzysztof Kozlowski @ 2023-12-05  6:55 UTC (permalink / raw)
  To: Keith Zhao, devicetree, dri-devel, linux-kernel, linux-riscv
  Cc: aou, suijingfeng, tzimmermann, paul.walmsley, mripard, xingyu.wu,
	jack.zhu, palmer, krzysztof.kozlowski+dt, william.qiu,
	shengyang.chen, changhuang.liang

On 04/12/2023 13:33, Keith Zhao wrote:
> 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 on next branch.

Please use standard email subjects, so with the PATCH keyword in the
title. `git format-patch` helps here to create proper versioned patches.
Another useful tool is b4. Skipping the PATCH keyword makes filtering of
emails more difficult thus making the review process less convenient.

Best regards,
Krzysztof


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

* Re: [v3 1/6] dt-bindings: display: Add yamls for JH7110 display system
  2023-12-04 12:33 ` [v3 1/6] dt-bindings: display: Add yamls for JH7110 display system Keith Zhao
@ 2023-12-05  6:59   ` Krzysztof Kozlowski
  2023-12-06 12:50   ` Sui Jingfeng
  2023-12-08 16:31   ` Rob Herring
  2 siblings, 0 replies; 52+ messages in thread
From: Krzysztof Kozlowski @ 2023-12-05  6:59 UTC (permalink / raw)
  To: Keith Zhao, devicetree, dri-devel, linux-kernel, linux-riscv
  Cc: aou, suijingfeng, tzimmermann, paul.walmsley, mripard, xingyu.wu,
	jack.zhu, palmer, krzysztof.kozlowski+dt, william.qiu,
	shengyang.chen, changhuang.liang

On 04/12/2023 13:33, Keith Zhao wrote:
> StarFive SoCs JH7110 display system:
> dc controller, hdmi controller,
> encoder, vout syscon.

Nothing improved here.

> 
> add the path of yaml file in MAINTAINERS

Neither here - still these are not proper sentences and wrapping is wrong.

Please wrap commit message according to Linux coding style / submission
process (neither too early nor over the limit):
https://elixir.bootlin.com/linux/v6.4-rc1/source/Documentation/process/submitting-patches.rst#L597

> 
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>  .../starfive/starfive,display-subsystem.yaml  | 104 ++++++++++++++++
>  .../starfive/starfive,dsi-encoder.yaml        |  92 ++++++++++++++
>  .../starfive/starfive,jh7110-dc8200.yaml      | 113 ++++++++++++++++++
>  .../starfive/starfive,jh7110-inno-hdmi.yaml   |  82 +++++++++++++
>  .../soc/starfive/starfive,jh7110-syscon.yaml  |   1 +
>  MAINTAINERS                                   |   7 ++
>  6 files changed, 399 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>  create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,dsi-encoder.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 000000000000..d5ebdba3fb36
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
> @@ -0,0 +1,104 @@
> +# 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 JH7110 Soc Display SubSystem
> +
> +maintainers:
> +  - Keith Zhao <keith.zhao@starfivetech.com>
> +  - ShengYang Chen <shengyang.chen@starfivetech.com>
> +
> +description:
> +  This is the bindings documentation for the JH7110 Soc Display Subsystem that

Drop "This is the bindings documentation for " because it is obvious. It
cannot be anything else.

> +  includes front-end video data capture, display controller and display
> +  interface. such as HDMI and MIPI.
> +
> +  JH7110 display pipeline have several components as below description,
> +  multi display controllers and corresponding physical interfaces.
> +  For different display scenarios, pipe0 and pipe1 maybe binding to different
> +  encoder. for example,
> +
> +  pipe0 binding to HDMI for primary display,
> +  pipe1 binding to DSI for external display.
> +
> +          +------------------------------+
> +          |                              |
> +          |                              |
> +  +----+  |   +-------------------+      |   +-------+   +------+   +------+
> +  |    +----->+  dc controller 0  +--->----->+HDMICtl| ->+ PHY  +-->+PANEL0+
> +  |AXI |  |   +-------------------+      |   +-------+   +------+   +------+
> +  |    |  |                              |
> +  |    |  |                              |
> +  |    |  |                              |
> +  |    |  |                              |
> +  |APB |  |   +-------------------+         +---------+    +------+  +-------+
> +  |    +----->+  dc controller 1  +--->---->+ dsiTx   +--->+DPHY  +->+ PANEL1+
> +  |    |  |   +-------------------+         +---------+    +------+  +-------+
> +  +----+  |                              |
> +          +------------------------------+
> +
> +
> +properties:
> +  compatible:
> +    const: starfive,display-subsystem
> +
> +  clocks:
> +    items:
> +      - description: Clock for display system noc bus.
> +      - 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.
> +
> +  clock-names:
> +    items:
> +      - const: noc_bus
> +      - const: dc_core
> +      - const: axi_core
> +      - const: ahb
> +
> +  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
> +
> +  ports:
> +    $ref: /schemas/types.yaml#/definitions/phandle-array

NAK, you just ignored my comments. More than once in this patchset.

I stop review. You must implement entire feedback or respond to it.
Especially when ignoring feedback results in buggy code.

Best regards,
Krzysztof


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

* Re: [v3 0/6] DRM driver for verisilicon
  2023-12-04 12:33 [v3 0/6] DRM driver for verisilicon Keith Zhao
                   ` (6 preceding siblings ...)
  2023-12-05  6:55 ` [v3 0/6] DRM driver for verisilicon Krzysztof Kozlowski
@ 2023-12-05 10:27 ` Sui Jingfeng
  2023-12-05 11:59 ` Sui Jingfeng
  8 siblings, 0 replies; 52+ messages in thread
From: Sui Jingfeng @ 2023-12-05 10:27 UTC (permalink / raw)
  To: Keith Zhao, devicetree, dri-devel, linux-kernel, linux-riscv
  Cc: aou, krzysztof.kozlowski+dt, william.qiu, mripard, xingyu.wu,
	jack.zhu, palmer, tzimmermann, paul.walmsley, shengyang.chen,
	changhuang.liang

HI,

This series are very interesting and nice!


On 2023/12/4 20:33, Keith Zhao wrote:
> This patch is a drm driver for Starfive Soc JH7110,

'SoC' : System on Chip, no more 'Soc' or 'soc' please.

> I am sending Drm driver part and HDMI driver part.


'DRM' or 'drm' nor Drm. DRM: Direct Rendering Manager.

Typically you should only *capitalize* the *first* letter of the first word
in a sentence, while this Drm appears in the middle of this sentence.

Please also improve the English written also, for example:

This series is a DRM driver for Starfive Soc JH7110, which contains (consists of)
a KMS driver for the vivante DC8200 display controller and a HDMI transmitter driver.


>
> 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

'servers' -> 'serve as', because server is a noun.

> 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 on next branch.
>
> 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 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
>
> V3:
> Changes since v3:
> - Delete the custom plane property.
> - Delete the custom fourcc modifiers.
> - Adjust the calculation mode of hdmi pixclock.
> - Add match data for dc8200 driver.
> - Adjust some magic values.
> - Add a simple encoder for dsi output.
>
> Keith Zhao (6):
>    dt-bindings: display: Add yamls for JH7110 display system
>    riscv: dts: starfive: jh7110: display subsystem
>    drm/vs: Register DRM device
>    drm/vs: Add KMS crtc&plane
>    drm/vs: Add hdmi driver
>    drm/vs: simple encoder
>
>   .../starfive/starfive,display-subsystem.yaml  |  104 ++
>   .../starfive/starfive,dsi-encoder.yaml        |   92 ++
>   .../starfive/starfive,jh7110-dc8200.yaml      |  113 ++
>   .../starfive/starfive,jh7110-inno-hdmi.yaml   |   82 ++
>   .../soc/starfive/starfive,jh7110-syscon.yaml  |    1 +
>   MAINTAINERS                                   |    8 +
>   .../jh7110-starfive-visionfive-2.dtsi         |  134 ++
>   arch/riscv/boot/dts/starfive/jh7110.dtsi      |   49 +
>   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   |  849 ++++++++++++
>   drivers/gpu/drm/verisilicon/starfive_hdmi.h   |  304 +++++
>   drivers/gpu/drm/verisilicon/vs_crtc.c         |  208 +++
>   drivers/gpu/drm/verisilicon/vs_crtc.h         |   42 +
>   drivers/gpu/drm/verisilicon/vs_dc.c           | 1192 +++++++++++++++++
>   drivers/gpu/drm/verisilicon/vs_dc.h           |   67 +
>   drivers/gpu/drm/verisilicon/vs_dc_hw.c        | 1022 ++++++++++++++
>   drivers/gpu/drm/verisilicon/vs_dc_hw.h        |  580 ++++++++
>   drivers/gpu/drm/verisilicon/vs_drv.c          |  323 +++++
>   drivers/gpu/drm/verisilicon/vs_drv.h          |   46 +
>   drivers/gpu/drm/verisilicon/vs_modeset.c      |   39 +
>   drivers/gpu/drm/verisilicon/vs_modeset.h      |   10 +
>   drivers/gpu/drm/verisilicon/vs_plane.c        |  301 +++++
>   drivers/gpu/drm/verisilicon/vs_plane.h        |   39 +
>   drivers/gpu/drm/verisilicon/vs_simple_enc.c   |  195 +++
>   drivers/gpu/drm/verisilicon/vs_simple_enc.h   |   23 +
>   drivers/gpu/drm/verisilicon/vs_type.h         |   69 +
>   29 files changed, 5928 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>   create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,dsi-encoder.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_simple_enc.c
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_simple_enc.h
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
>

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

* Re: [v3 3/6] drm/vs: Register DRM device
  2023-12-04 12:33 ` [v3 3/6] drm/vs: Register DRM device Keith Zhao
  2023-12-04 13:30   ` Philipp Zabel
@ 2023-12-05 11:33   ` Dmitry Baryshkov
  1 sibling, 0 replies; 52+ messages in thread
From: Dmitry Baryshkov @ 2023-12-05 11:33 UTC (permalink / raw)
  To: Keith Zhao
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	william.qiu, linux-kernel, dri-devel, xingyu.wu, jack.zhu,
	palmer, mripard, tzimmermann, paul.walmsley, shengyang.chen,
	linux-riscv, changhuang.liang

On Mon, 4 Dec 2023 at 14:33, Keith Zhao <keith.zhao@starfivetech.com> wrote:
>
> Implement drm device registration interface
>
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>  MAINTAINERS                              |   1 +
>  drivers/gpu/drm/Kconfig                  |   2 +
>  drivers/gpu/drm/Makefile                 |   1 +
>  drivers/gpu/drm/verisilicon/Kconfig      |  13 +
>  drivers/gpu/drm/verisilicon/Makefile     |   6 +
>  drivers/gpu/drm/verisilicon/vs_drv.c     | 316 +++++++++++++++++++++++
>  drivers/gpu/drm/verisilicon/vs_drv.h     |  42 +++
>  drivers/gpu/drm/verisilicon/vs_modeset.c |  39 +++
>  drivers/gpu/drm/verisilicon/vs_modeset.h |  10 +
>  9 files changed, 430 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
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 7caaadb83f3f..8dc9ebfe4605 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6887,6 +6887,7 @@ L:        dri-devel@lists.freedesktop.org
>  S:     Maintained
>  T:     git git://anongit.freedesktop.org/drm/drm-misc
>  F:     Documentation/devicetree/bindings/display/starfive/
> +F:     drivers/gpu/drm/verisilicon/
>
>  DRM DRIVER FOR TI DLPC3433 MIPI DSI TO DMD BRIDGE
>  M:     Jagan Teki <jagan@amarulasolutions.com>
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 3eee8636f847..e8d53c2e7c86 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -394,6 +394,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 8e1bde059170..29e04acded06 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -198,3 +198,4 @@ obj-$(CONFIG_DRM_HYPERV) += hyperv/
>  obj-y                  += solomon/
>  obj-$(CONFIG_DRM_SPRD) += sprd/
>  obj-$(CONFIG_DRM_LOONGSON) += loongson/
> +obj-$(CONFIG_DRM_VERISILICON) += verisilicon/
> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
> new file mode 100644
> index 000000000000..e10fa97635aa
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/Kconfig
> @@ -0,0 +1,13 @@
> +# 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 000000000000..d785a1dfaa7f
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/Makefile
> @@ -0,0 +1,6 @@
> +# 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 000000000000..4fb1f29ef84b
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> @@ -0,0 +1,316 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/of_clk.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);
> +
> +       args->pitch = ALIGN(pitch, priv->pitch_alignment);
> +       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 void vs_drm_device_release_clocks(void *res)
> +{
> +       struct vs_drm_device *priv = res;
> +       unsigned int i;
> +
> +       reset_control_bulk_assert(priv->nrsts, priv->rst_vout);
> +
> +       for (i = 0; i < priv->clk_count; ++i) {
> +               if (priv->clks[i]) {
> +                       clk_disable_unprepare(priv->clks[i]);
> +                       clk_put(priv->clks[i]);
> +               }
> +       }
> +}
> +
> +static const char * const vout_resets[] = {
> +       "axi",
> +       "ahb",
> +       "core",
> +};
> +
> +static int vs_drm_device_init_clocks(struct vs_drm_device *priv)
> +{
> +       struct drm_device *dev = &priv->base;
> +       struct platform_device *pdev = to_platform_device(dev->dev);
> +       struct device_node *of_node = pdev->dev.of_node;
> +       struct clk *clock;
> +       unsigned int i;
> +       int ret;
> +
> +       if (dev_get_platdata(&pdev->dev) || !of_node)
> +               return 0;

Drop dev_get_platdata(), you don't seem to use it.

> +
> +       priv->nrsts = ARRAY_SIZE(priv->rst_vout);
> +       for (int i = 0; i < priv->nrsts; ++i)
> +               priv->rst_vout[i].id = vout_resets[i];
> +       ret = devm_reset_control_bulk_get_shared(dev->dev, priv->nrsts,
> +                                                priv->rst_vout);
> +       if (ret) {
> +               drm_err(dev, "Failed to get reset controls\n");
> +               return ret;
> +       }
> +
> +       priv->clk_count = of_clk_get_parent_count(of_node);
> +       if (!priv->clk_count)
> +               return 0;
> +
> +       priv->clks = drmm_kzalloc(dev, priv->clk_count * sizeof(priv->clks[0]),
> +                                 GFP_KERNEL);
> +       if (!priv->clks)
> +               return -ENOMEM;
> +
> +       for (i = 0; i < priv->clk_count; ++i) {
> +               clock = of_clk_get(of_node, i);
> +               if (IS_ERR(clock)) {
> +                       ret = PTR_ERR(clock);
> +                       if (ret == -EPROBE_DEFER)
> +                               goto err;
> +                       drm_err(dev, "clock %u not found: %d\n", i, ret);
> +                       continue;
> +               }
> +               ret = clk_prepare_enable(clock);
> +               if (ret) {
> +                       drm_err(dev, "failed to enable clock %u: %d\n",
> +                               i, ret);
> +                       clk_put(clock);
> +                       continue;
> +               }
> +               priv->clks[i] = clock;
> +       }

This can be rewritten as devm_clk_bulk_get_all()

> +
> +       ret = reset_control_bulk_deassert(priv->nrsts, priv->rst_vout);
> +       if (ret)
> +               return ret;

It is a bad idea to mix get_resources kind of function with the actual
resource control. Please move reset deassertion upwards.

> +
> +       return devm_add_action_or_reset(&pdev->dev,
> +                                       vs_drm_device_release_clocks,
> +                                       priv);
> +
> +err:
> +       while (i) {
> +               --i;
> +               if (priv->clks[i]) {
> +                       clk_disable_unprepare(priv->clks[i]);
> +                       clk_put(priv->clks[i]);
> +               }
> +       }
> +       return ret;
> +}
> +
> +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;

If anything happens during the probe, your platform is left with no
display output. I think it might be better to call this when the
driver has acquired all the resources and is ready to start hw init.

> +
> +       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);
> +
> +       ret = vs_drm_device_init_clocks(priv);
> +       if (ret)
> +               return ret;
> +
> +       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;

Teardown path is missing.

> +
> +       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);
> +
> +                       drm_of_component_match_add(dev, &match, component_compare_of,
> +                                                  d->of_node);
> +                       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);

I wonder if you can use drm_of_component_probe() instead?

> +}
> +
> +static int vs_drm_platform_remove(struct platform_device *pdev)

I think this should be void vs_drm_platform_remove() and .remove_new

> +{
> +       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 drm_mode_config_helper_resume()

> +
> +       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));
> +}
> +
> +late_initcall_sync(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 000000000000..ea2189772980
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.h
> @@ -0,0 +1,42 @@
> +/* 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>
> +#include <linux/clk.h>
> +#include <linux/reset.h>
> +
> +enum rst_vout {
> +       RST_VOUT_AXI = 0,
> +       RST_VOUT_AHB,
> +       RST_VOUT_CORE,
> +       RST_VOUT_NUM
> +};

Do you need these values? They can easily get out of sync with vout_rsts.

> +
> +/*@pitch_alignment: buffer pitch alignment required by sub-devices.*/
> +struct vs_drm_device {
> +       struct drm_device base;
> +       unsigned int pitch_alignment;
> +       /* clocks */
> +       unsigned int clk_count;
> +       struct clk **clks;
> +
> +       struct reset_control_bulk_data rst_vout[RST_VOUT_NUM];
> +       int     nrsts;
> +};
> +
> +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 000000000000..eaf406c1b7c7
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_modeset.c
> @@ -0,0 +1,39 @@
> +// 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)
> +{
> +       int ret;
> +
> +       ret = drmm_mode_config_init(dev);
> +       if (ret)
> +               return;
> +
> +       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 000000000000..bd04f81d2ad2
> --- /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_MODESET_H__
> +#define __VS_MODESET_H__
> +
> +void vs_mode_config_init(struct drm_device *dev);
> +#endif /* __VS_FB_H__ */
> --
> 2.34.1
>


-- 
With best wishes
Dmitry

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

* Re: [v3 0/6] DRM driver for verisilicon
  2023-12-04 12:33 [v3 0/6] DRM driver for verisilicon Keith Zhao
                   ` (7 preceding siblings ...)
  2023-12-05 10:27 ` Sui Jingfeng
@ 2023-12-05 11:59 ` Sui Jingfeng
  8 siblings, 0 replies; 52+ messages in thread
From: Sui Jingfeng @ 2023-12-05 11:59 UTC (permalink / raw)
  To: Keith Zhao, devicetree, dri-devel, linux-kernel, linux-riscv
  Cc: aou, krzysztof.kozlowski+dt, william.qiu, mripard, xingyu.wu,
	jack.zhu, palmer, tzimmermann, paul.walmsley, shengyang.chen,
	changhuang.liang

Hi,

On 2023/12/4 20:33, Keith Zhao wrote:
> 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 on next branch.


Please study Thomas's patch[1][2] carefully and write a good cover letter.
Introduce what each single patch does, demonstrate how the whole driver is
divided and organized, and why. And probably keep contact with him if he
would like to curve your driver to a good shape. :-)


[1] https://lore.kernel.org/dri-devel/20200715145902.13122-1-tzimmermann@suse.de/
[2] https://lore.kernel.org/dri-devel/20231113091439.17181-1-tzimmermann@suse.de/


> 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 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
>
> V3:
> Changes since v3:
> - Delete the custom plane property.
> - Delete the custom fourcc modifiers.
> - Adjust the calculation mode of hdmi pixclock.
> - Add match data for dc8200 driver.
> - Adjust some magic values.
> - Add a simple encoder for dsi output.
>
> Keith Zhao (6):
>    dt-bindings: display: Add yamls for JH7110 display system
>    riscv: dts: starfive: jh7110: display subsystem
>    drm/vs: Register DRM device
>    drm/vs: Add KMS crtc&plane
>    drm/vs: Add hdmi driver
>    drm/vs: simple encoder
>
>   .../starfive/starfive,display-subsystem.yaml  |  104 ++
>   .../starfive/starfive,dsi-encoder.yaml        |   92 ++
>   .../starfive/starfive,jh7110-dc8200.yaml      |  113 ++
>   .../starfive/starfive,jh7110-inno-hdmi.yaml   |   82 ++
>   .../soc/starfive/starfive,jh7110-syscon.yaml  |    1 +
>   MAINTAINERS                                   |    8 +
>   .../jh7110-starfive-visionfive-2.dtsi         |  134 ++
>   arch/riscv/boot/dts/starfive/jh7110.dtsi      |   49 +
>   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   |  849 ++++++++++++
>   drivers/gpu/drm/verisilicon/starfive_hdmi.h   |  304 +++++
>   drivers/gpu/drm/verisilicon/vs_crtc.c         |  208 +++
>   drivers/gpu/drm/verisilicon/vs_crtc.h         |   42 +
>   drivers/gpu/drm/verisilicon/vs_dc.c           | 1192 +++++++++++++++++
>   drivers/gpu/drm/verisilicon/vs_dc.h           |   67 +
>   drivers/gpu/drm/verisilicon/vs_dc_hw.c        | 1022 ++++++++++++++
>   drivers/gpu/drm/verisilicon/vs_dc_hw.h        |  580 ++++++++
>   drivers/gpu/drm/verisilicon/vs_drv.c          |  323 +++++
>   drivers/gpu/drm/verisilicon/vs_drv.h          |   46 +
>   drivers/gpu/drm/verisilicon/vs_modeset.c      |   39 +
>   drivers/gpu/drm/verisilicon/vs_modeset.h      |   10 +
>   drivers/gpu/drm/verisilicon/vs_plane.c        |  301 +++++
>   drivers/gpu/drm/verisilicon/vs_plane.h        |   39 +
>   drivers/gpu/drm/verisilicon/vs_simple_enc.c   |  195 +++
>   drivers/gpu/drm/verisilicon/vs_simple_enc.h   |   23 +
>   drivers/gpu/drm/verisilicon/vs_type.h         |   69 +
>   29 files changed, 5928 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>   create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,dsi-encoder.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_simple_enc.c
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_simple_enc.h
>   create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h
>

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

* Re: [v3 4/6] drm/vs: Add KMS crtc&plane
  2023-12-04 12:33 ` [v3 4/6] drm/vs: Add KMS crtc&plane Keith Zhao
@ 2023-12-05 12:48   ` Dmitry Baryshkov
  2023-12-06  8:55   ` Maxime Ripard
  2023-12-07  8:41   ` Icenowy Zheng
  2 siblings, 0 replies; 52+ messages in thread
From: Dmitry Baryshkov @ 2023-12-05 12:48 UTC (permalink / raw)
  To: Keith Zhao
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	william.qiu, linux-kernel, dri-devel, xingyu.wu, jack.zhu,
	palmer, mripard, tzimmermann, paul.walmsley, shengyang.chen,
	linux-riscv, changhuang.liang

On Mon, 4 Dec 2023 at 14:33, Keith Zhao <keith.zhao@starfivetech.com> wrote:
>
> add 2 crtcs and 8 planes in vs-drm
>
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>  drivers/gpu/drm/verisilicon/Makefile   |    9 +-
>  drivers/gpu/drm/verisilicon/vs_crtc.c  |  208 +++++
>  drivers/gpu/drm/verisilicon/vs_crtc.h  |   42 +
>  drivers/gpu/drm/verisilicon/vs_dc.c    | 1192 ++++++++++++++++++++++++
>  drivers/gpu/drm/verisilicon/vs_dc.h    |   67 ++
>  drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1022 ++++++++++++++++++++
>  drivers/gpu/drm/verisilicon/vs_dc_hw.h |  580 ++++++++++++
>  drivers/gpu/drm/verisilicon/vs_drv.c   |    2 +
>  drivers/gpu/drm/verisilicon/vs_plane.c |  301 ++++++
>  drivers/gpu/drm/verisilicon/vs_plane.h |   39 +
>  drivers/gpu/drm/verisilicon/vs_type.h  |   69 ++
>  11 files changed, 3528 insertions(+), 3 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 d785a1dfaa7f..bf6f2b7ee480 100644
> --- a/drivers/gpu/drm/verisilicon/Makefile
> +++ b/drivers/gpu/drm/verisilicon/Makefile
> @@ -1,6 +1,9 @@
>  # SPDX-License-Identifier: GPL-2.0
>
> -vs_drm-objs := vs_drv.o \
> -              vs_modeset.o
> -
> +vs_drm-objs := vs_dc_hw.o \

Nit: if you move the first object to the next line, you won't have to
touch the first line.

> +               vs_dc.o \
> +               vs_crtc.o \
> +               vs_drv.o \
> +               vs_modeset.o \
> +               vs_plane.o

Keep the empty line, please.

>  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 000000000000..5581219b1230
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
> @@ -0,0 +1,208 @@
> +// 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 "vs_crtc.h"
> +#include "vs_dc.h"
> +#include "vs_drv.h"
> +
> +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 void vs_crtc_reset(struct drm_crtc *crtc)
> +{
> +       struct vs_crtc_state *state;
> +
> +       if (crtc->state)
> +               vs_crtc_atomic_destroy_state(crtc, crtc->state);
> +
> +       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 *old_state;
> +       struct vs_crtc_state *state;
> +
> +       if (!crtc->state)
> +               return NULL;
> +
> +       old_state = to_vs_crtc_state(crtc->state);
> +
> +       state = kmemdup(old_state, sizeof(*old_state), GFP_KERNEL);
> +               if (!state)
> +                       return NULL;
> +
> +       __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
> +
> +       return &state->base;
> +}
> +
> +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);

I always found foo_enable_something(false) to be strange. Could you
please split this into two functions?

> +}
> +
> +static void vs_crtc_atomic_print_state(struct drm_printer *p,
> +                                      const struct drm_crtc_state *state)
> +{
> +       struct vs_crtc *vs_crtc = to_vs_crtc(state->crtc);
> +
> +       drm_printf(p, "vs crtc State\n");

Please drop this line, it breaks the debugfs/state flow.

> +       drm_printf(p, "\tcolor_formats: %d\n", vs_crtc->color_formats);
> +       drm_printf(p, "\tmax_bpc: %d\n", vs_crtc->max_bpc);
> +}
> +
> +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,
> +       .atomic_print_state = vs_crtc_atomic_print_state,
> +};
> +
> +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);
> +
> +       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);
> +               crtc->state->event = NULL;
> +               spin_unlock_irq(&crtc->dev->event_lock);
> +       }
> +}
> +
> +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
> +                                struct drm_atomic_state *state)
> +{
> +       struct drm_crtc_state *new_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 = new_state->gamma_lut;
> +       struct drm_color_lut *lut;
> +       struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +       if (new_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);
> +               crtc->state->event = NULL;
> +               spin_unlock_irq(&crtc->dev->event_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,
> +};
> +
> +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_crtc_alloc_with_planes(drm_dev, struct vs_crtc, base, NULL,
> +                                          NULL, &vs_crtc_funcs,
> +                                          info->name ? info->name : 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 000000000000..8f5b5718283f
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.h
> @@ -0,0 +1,42 @@
> +/* 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;
> +};
> +
> +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 000000000000..3f1e70ba4261
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc.c
> @@ -0,0 +1,1192 @@
> +// 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/of.h>
> +#include <linux/of_graph.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 "vs_dc_hw.h"
> +#include "vs_dc.h"
> +#include "vs_drv.h"
> +
> +static const char * const vout_clocks[] = {
> +       "channel0",
> +       "channel1",
> +       "hdmi_tx",
> +       "dc_parent",
> +};
> +
> +#define FRAC_16_16(mult, div)   (((mult) << 16) / (div))
> +
> +static const u32 primary_overlay_format0[] = {
> +       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_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_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,
> +       DRM_FORMAT_MOD_INVALID
> +};
> +
> +static const u64 secondary_format_modifiers[] = {
> +       DRM_FORMAT_MOD_LINEAR,
> +       DRM_FORMAT_MOD_INVALID
> +};
> +
> +static const struct vs_plane_info dc_hw_planes_rev0[PLANE_NUM] = {

What is the difference between PRIMARY_PLANE_0 and PRIMARY_PLANE_1?
And between different overlay planes? I think you are mixing the
'plane kind' vs 'plane enumeration' here.
If you really wish to have 'plane kind' structures, please have one
for the primary plane, one for the overlay and one for the cursor.

> +       {
> +               .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,
> +       },
> +};
> +
> +static const struct vs_dc_info dc8200_info = {
> +       .name                   = "DC8200",
> +       .panel_num              = 2,
> +       .plane_num              = 8,
> +       .planes                 = dc_hw_planes_rev0,
> +       .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,
> +};
> +
> +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 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_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)
> +{
> +       struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +       return clk_bulk_prepare_enable(dc->nclks, dc->clk_vout);
> +}
> +
> +static void plda_clk_rst_deinit(struct device *dev)
> +{
> +       struct vs_dc *dc = dev_get_drvdata(dev);
> +
> +       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;
> +
> +       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);
> +
> +       enable_irq(dc->irq);
> +}
> +
> +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc)
> +{
> +       struct dc_hw_display display;
> +
> +       disable_irq(dc->irq);
> +
> +       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 = DRM_FORMAT_MOD_LINEAR;
> +       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;

Is there any reason to copy the plane state instead of using it directly?

> +       update_format(drm_fb->format->format, drm_fb->modifier, fb);
> +       update_swizzle(drm_fb->format->format, fb);
> +}
> +
> +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 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;
> +       roi.enable = false;
> +
> +       update_scale(state, &roi, &scale);
> +
> +       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);
> +
> +       dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
> +}
> +
> +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);
> +}
> +
> +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 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;
> +
> +       if (!dc_info)
> +               return IRQ_HANDLED;
> +
> +       ret = dc_hw_get_interrupt(&dc->hw);
> +
> +       for (i = 0; i < dc_info->panel_num; i++)
> +               drm_crtc_handle_vblank(&dc->crtc[i]->base);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +void vs_dc_commit(struct vs_dc *dc)
> +{
> +       dc_hw_enable_shadow_register(&dc->hw, false);
> +
> +       dc_hw_commit(&dc->hw);
> +
> +       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 max_width = 0, max_height = 0;
> +       u32 min_width = 0xffff, min_heigth = 0xffff;
> +       u32 possible_crtc = 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;
> +       }
> +
> +       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;
> +               }
> +
> +               port = of_graph_get_port_by_id(dev->of_node, i);
> +               if (!port) {
> +                       drm_err(drm_dev, "no port node found for crtc_port%d\n", i);
> +                       return -ENOENT;
> +               }
> +
> +               crtc->base.port = port;

When do you of_node_put() the node?

> +               crtc->dev = dev;
> +               dc->crtc[i] = crtc;
> +       }
> +
> +       for (i = 0; i < dc_info->plane_num; i++) {
> +               plane_info = (struct vs_plane_info *)&dc_info->planes[i];
> +
> +               if (!dc->crtc[0]->base.port || !dc->crtc[1]->base.port) {
> +                       drm_err(drm_dev, "no port no crtc mask, fail to create plane\n");
> +                       return -ENOENT;
> +               }
> +
> +               if (plane_info->id == PRIMARY_PLANE_0 ||
> +                   plane_info->id == CURSOR_PLANE_0) {
> +                       possible_crtc = drm_crtc_mask(&dc->crtc[0]->base);
> +               } else if (plane_info->id == PRIMARY_PLANE_1 ||
> +                       plane_info->id == CURSOR_PLANE_1) {
> +                       possible_crtc = drm_crtc_mask(&dc->crtc[1]->base);
> +               } else {
> +                       possible_crtc = drm_crtc_mask(&dc->crtc[0]->base) |
> +                                       drm_crtc_mask(&dc->crtc[1]->base);
> +               }
> +
> +               plane = vs_plane_create(drm_dev, plane_info,
> +                                       dc_info->layer_num, possible_crtc);
> +               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 (plane_info->id == PRIMARY_PLANE_0)
> +                               dc->crtc[0]->base.primary = &plane->base;
> +                       else
> +                               dc->crtc[1]->base.primary = &plane->base;
> +
> +                       min_width = min_t(u32, min_width, plane_info->min_width);
> +                       min_heigth = min_t(u32, min_heigth, plane_info->min_height);
> +                       /*
> +                        * Note: these values are used for multiple independent things:
> +                        * hw display mode filtering, plane buffer sizes ...
> +                        * Use the combined maximum values here to cover all use cases,
> +                        * and do more specific checking in the respective code paths.
> +                        */
> +                       max_width = max_t(u32, max_width, plane_info->max_width);
> +                       max_height = max_t(u32, max_height, plane_info->max_height);
> +               }
> +
> +               if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
> +                       if (plane_info->id == CURSOR_PLANE_0)
> +                               dc->crtc[0]->base.cursor = &plane->base;
> +                       else
> +                               dc->crtc[1]->base.cursor = &plane->base;
> +                       drm_dev->mode_config.cursor_width =
> +                               max_t(u32, drm_dev->mode_config.cursor_width,
> +                                     plane_info->max_width);
> +                       drm_dev->mode_config.cursor_height =
> +                               max_t(u32, drm_dev->mode_config.cursor_height,
> +                                     plane_info->max_height);
> +               }
> +       }
> +
> +       drm_dev->mode_config.min_width = min_width;
> +       drm_dev->mode_config.min_height = min_heigth;
> +       drm_dev->mode_config.max_width = max_width;
> +       drm_dev->mode_config.max_height = 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",
> +               .data = &dc8200_info,
> +       },
> +       {},
> +};
> +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 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);

devm_clk_bulk_get_all() ?

> +       if (ret) {
> +               dev_err(dev, "Failed to get clk controls\n");
> +               return ret;
> +       }
> +
> +       dc->irq = platform_get_irq(pdev, 0);
> +
> +       /* do not autoenable, will be enabled later */
> +       ret = devm_request_irq(dev, dc->irq, dc_isr, IRQF_NO_AUTOEN, dev_name(dev), dc);
> +       if (ret < 0) {
> +               dev_err(dev, "Failed to install irq:%u.\n", dc->irq);
> +               return ret;
> +       }
> +
> +       dc->hw.info = (struct vs_dc_info *)of_device_get_match_data(dev);
> +
> +       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 000000000000..eeb5be0499ea
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc.h
> @@ -0,0 +1,67 @@
> +/* 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_PIX0,
> +       CLK_VOUT_PIX1,
> +       CLK_VOUT_HDMI_PIX,
> +       CLK_VOUT_SOC_PIX,
> +       CLK_VOUT_NUM
> +};
> +
> +struct vs_dc {
> +       struct vs_crtc          *crtc[DC_DISPLAY_NUM];
> +       struct dc_hw            hw;
> +
> +       struct vs_dc_plane      planes[PLANE_NUM];
> +       struct clk_bulk_data    clk_vout[CLK_VOUT_NUM];
> +       int     nclks;
> +       int irq;
> +};
> +
> +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 000000000000..d05ad3f00881
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
> @@ -0,0 +1,1022 @@
> +// 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 "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 ARRAY_SIZE(horkernel)
> +
> +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 ARRAY_SIZE(verkernel)
> +
> +/*
> + * RGB 709->2020 conversion parameters
> + */
> +static const 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 const 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
> +};
> +
> +/* 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 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, const 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, const 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)

Can you use drm_format_info.is_yuv instead of manually coding it here?

> +{
> +       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 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;

Wouldn't it be better to store this value in the plane instance?

> +}
> +
> +int dc_hw_init(struct dc_hw *hw)
> +{
> +       u8 i, id, panel_num, layer_num;
> +       u32 offset;
> +
> +       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)
> +                       hw->reg[i] = dc_plane_reg[0];
> +               else
> +                       hw->reg[i] = dc_plane_reg[1];
> +
> +               load_default_filter(hw, &hw->reg[i], offset);
> +               load_rgb_to_rgb(hw, &hw->reg[i], 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_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);
> +}
> +
> +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,
> +                                            PRIMARY_SHADOW_EN, 0);
> +                       else
> +                               dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> +                                            OVERLAY_SHADOW_EN, 0);
> +               } else {
> +                       if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
> +                               dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
> +                                            0, PRIMARY_SHADOW_EN);
> +                       else
> +                               dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> +                                            0, OVERLAY_SHADOW_EN);
> +               }
> +       }
> +
> +       for (i = 0; i < panel_num; i++) {
> +               offset = i << 2;
> +               if (enable)
> +                       dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, PANEL_SHADOW_EN);
> +               else
> +                       dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, PANEL_SHADOW_EN, 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),
> +                                            PANEL_GAMMA_EN, 0);
> +                       } else {
> +                               dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
> +                                            0, PANEL_GAMMA_EN);
> +                       }
> +                       hw->gamma[j].dirty = false;
> +               }
> +       }
> +}
> +
> +static void plane_ex_commit_primary(struct dc_hw *hw, struct dc_hw_plane *plane, u32 i, u32 offset)
> +{
> +       if (plane->fb.dirty) {
> +               if (is_rgb(plane->fb.format)) {
> +                       dc_set_clear(hw,
> +                                    DC_FRAMEBUFFER_CONFIG_EX + offset,
> +                                    PRIMARY_RGB2RGB_EN, PRIMARY_YUVCLAMP_EN);
> +               } else {
> +                       dc_set_clear(hw,
> +                                    DC_FRAMEBUFFER_CONFIG_EX + offset,
> +                                    PRIMARY_YUVCLAMP_EN, PRIMARY_RGB2RGB_EN);
> +
> +                       switch (plane->fb.yuv_color_space) {
> +                       case COLOR_SPACE_601:
> +                               load_yuv_to_rgb(hw, &hw->reg[i], offset, YUV601_2RGB);
> +                               break;
> +                       case COLOR_SPACE_709:
> +                               load_yuv_to_rgb(hw, &hw->reg[i], offset, YUV709_2RGB);
> +                               break;
> +                       case COLOR_SPACE_2020:
> +                               load_yuv_to_rgb(hw, &hw->reg[i], offset, YUV2020_2RGB);
> +                               break;
> +                       default:
> +                               break;
> +                       }
> +               }
> +
> +               if (plane->fb.enable) {
> +                       dc_write(hw, hw->reg[i].y_address + offset,
> +                                plane->fb.y_address);
> +                       dc_write(hw, hw->reg[i].u_address + offset,
> +                                plane->fb.u_address);
> +                       dc_write(hw, hw->reg[i].v_address + offset,
> +                                plane->fb.v_address);
> +                       dc_write(hw, hw->reg[i].y_stride + offset,
> +                                plane->fb.y_stride);
> +                       dc_write(hw, hw->reg[i].u_stride + offset,
> +                                plane->fb.u_stride);
> +                       dc_write(hw, hw->reg[i].v_stride + offset,
> +                                plane->fb.v_stride);
> +                       dc_write(hw, hw->reg[i].size + offset,
> +                                FB_SIZE(plane->fb.width, plane->fb.height));
> +                       dc_write(hw, hw->reg[i].water_mark + offset,
> +                                plane->fb.water_mark);
> +
> +                       if (plane->fb.clear_enable)
> +                               dc_write(hw, hw->reg[i].clear_value + offset,
> +                                        plane->fb.clear_value);
> +               }
> +
> +               dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
> +                            PRIMARY_FORMAT(plane->fb.format) |
> +                            PRIMARY_UV_SWIZ(plane->fb.uv_swizzle) |
> +                            PRIMARY_SWIZ(plane->fb.swizzle) |
> +                            PRIMARY_TILE(plane->fb.tile_mode) |
> +                            PRIMARY_YUV_COLOR(plane->fb.yuv_color_space) |
> +                            PRIMARY_ROTATION(plane->fb.rotation) |
> +                            PRIMARY_CLEAR_EN(plane->fb.clear_enable),
> +                            PRIMARY_FORMAT_MASK |
> +                            PRIMARY_UV_SWIZ_MASK |
> +                            PRIMARY_SWIZ_MASK |
> +                            PRIMARY_TILE_MASK |
> +                            PRIMARY_YUV_COLOR_MASK |
> +                            PRIMARY_ROTATION_MASK |
> +                            PRIMARY_CLEAR_EN_MASK);
> +               dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
> +                            PRIMARY_DECODER_EN(plane->fb.dec_enable) |
> +                            PRIMARY_EN(plane->fb.enable) |
> +                            PRIMARY_ZPOS(plane->fb.zpos) |
> +                            PRIMARY_CHANNEL(plane->fb.display_id),
> +                            PRIMARY_DECODER_EN_EN_MASK |
> +                            PRIMARY_EN_MASK |
> +                            PRIMARY_ZPOS_MASK |
> +                            PRIMARY_CHANNEL_MASK);
> +
> +               plane->fb.dirty = false;
> +       }
> +
> +       if (plane->scale.dirty) {
> +               if (plane->scale.enable) {
> +                       dc_write(hw, hw->reg[i].scale_factor_x + offset,
> +                                plane->scale.scale_factor_x);
> +                       dc_write(hw, hw->reg[i].scale_factor_y + offset,
> +                                plane->scale.scale_factor_y);
> +                               dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
> +                                            PRIMARY_SCALE_EN, 0);
> +               } else {
> +                       dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
> +                                    0, PRIMARY_SCALE_EN);
> +               }
> +               plane->scale.dirty = false;
> +       }
> +
> +       if (plane->pos.dirty) {
> +               dc_write(hw, hw->reg[i].top_left + offset,
> +                        X_POS(plane->pos.start_x) |
> +                        Y_POS(plane->pos.start_y));
> +               dc_write(hw, hw->reg[i].bottom_right + offset,
> +                        X_POS(plane->pos.end_x) |
> +                        Y_POS(plane->pos.end_y));
> +               plane->pos.dirty = false;
> +       }
> +
> +       if (plane->blend.dirty) {
> +               dc_write(hw, hw->reg[i].src_global_color + offset,
> +                        PRIMARY_ALPHA_LEN(plane->blend.alpha));
> +               dc_write(hw, hw->reg[i].dst_global_color + offset,
> +                        PRIMARY_ALPHA_LEN(plane->blend.alpha));
> +               switch (plane->blend.blend_mode) {
> +               case DRM_MODE_BLEND_PREMULTI:
> +                       dc_write(hw, hw->reg[i].blend_config + offset, BLEND_PREMULTI);
> +                       break;
> +               case DRM_MODE_BLEND_COVERAGE:
> +                       dc_write(hw, hw->reg[i].blend_config + offset, BLEND_COVERAGE);
> +                       break;
> +               case DRM_MODE_BLEND_PIXEL_NONE:
> +                       dc_write(hw, hw->reg[i].blend_config + offset, BLEND_PIXEL_NONE);
> +                       break;
> +               default:
> +                       break;
> +               }
> +               plane->blend.dirty = false;
> +       }
> +}
> +
> +static void plane_ex_commit_overlay(struct dc_hw *hw, struct dc_hw_plane *plane,
> +                                   u32 i, u32 offset)
> +{
> +       if (plane->fb.dirty) {
> +               if (is_rgb(plane->fb.format)) {
> +                       dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> +                                    OVERLAY_RGB2RGB_EN, OVERLAY_CLAMP_EN);
> +               } else {
> +                       dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> +                                    OVERLAY_CLAMP_EN, OVERLAY_RGB2RGB_EN);
> +
> +                       switch (plane->fb.yuv_color_space) {
> +                       case COLOR_SPACE_601:
> +                               load_yuv_to_rgb(hw, &hw->reg[i], offset, YUV601_2RGB);
> +                               break;
> +                       case COLOR_SPACE_709:
> +                               load_yuv_to_rgb(hw, &hw->reg[i], offset, YUV709_2RGB);
> +                               break;
> +                       case COLOR_SPACE_2020:
> +                               load_yuv_to_rgb(hw, &hw->reg[i], offset, YUV2020_2RGB);
> +                               break;
> +                       default:
> +                               break;
> +                       }
> +               }
> +
> +               if (plane->fb.enable) {
> +                       dc_write(hw, hw->reg[i].y_address + offset,
> +                                plane->fb.y_address);
> +                       dc_write(hw, hw->reg[i].u_address + offset,
> +                                plane->fb.u_address);
> +                       dc_write(hw, hw->reg[i].v_address + offset,
> +                                plane->fb.v_address);
> +                       dc_write(hw, hw->reg[i].y_stride + offset,
> +                                plane->fb.y_stride);
> +                       dc_write(hw, hw->reg[i].u_stride + offset,
> +                                plane->fb.u_stride);
> +                       dc_write(hw, hw->reg[i].v_stride + offset,
> +                                plane->fb.v_stride);
> +                       dc_write(hw, hw->reg[i].size + offset,
> +                                FB_SIZE(plane->fb.width, plane->fb.height));
> +                       dc_write(hw, hw->reg[i].water_mark + offset,
> +                                plane->fb.water_mark);
> +
> +                       if (plane->fb.clear_enable)
> +                               dc_write(hw, hw->reg[i].clear_value + offset,
> +                                        plane->fb.clear_value);
> +               }
> +
> +               dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
> +                            OVERLAY_DEC_EN(plane->fb.dec_enable) |
> +                            OVERLAY_CLEAR_EN(plane->fb.clear_enable) |
> +                            OVERLAY_FB_EN(plane->fb.enable) |
> +                            OVERLAY_FORMAT(plane->fb.format) |
> +                            OVERLAY_UV_SWIZ(plane->fb.uv_swizzle) |
> +                            OVERLAY_SWIZ(plane->fb.swizzle) |
> +                            OVERLAY_TILE(plane->fb.tile_mode) |
> +                            OVERLAY_YUV_COLOR(plane->fb.yuv_color_space) |
> +                            OVERLAY_ROTATION(plane->fb.rotation),
> +                            OVERLAY_DEC_EN_MASK |
> +                            OVERLAY_CLEAR_EN_MASK |
> +                            OVERLAY_FB_EN_MASK |
> +                            OVERLAY_FORMAT_MASK |
> +                            OVERLAY_UV_SWIZ_MASK |
> +                            OVERLAY_SWIZ_MASK |
> +                            OVERLAY_TILE_MASK |
> +                            OVERLAY_YUV_COLOR_MASK |
> +                            OVERLAY_ROTATION_MASK);
> +
> +               dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset,
> +                            OVERLAY_LAYER_SEL(plane->fb.zpos) |
> +                            OVERLAY_PANEL_SEL(plane->fb.display_id),
> +                            OVERLAY_LAYER_SEL_MASK |
> +                            OVERLAY_PANEL_SEL_MASK);
> +
> +               plane->fb.dirty = false;
> +       }
> +
> +       if (plane->scale.dirty) {
> +               if (plane->scale.enable) {
> +                       dc_write(hw, hw->reg[i].scale_factor_x + offset,
> +                                plane->scale.scale_factor_x);
> +                       dc_write(hw, hw->reg[i].scale_factor_y + offset,
> +                                plane->scale.scale_factor_y);
> +                       dc_set_clear(hw, DC_OVERLAY_SCALE_CONFIG + offset,
> +                                    OVERLAY_SCALE_EN, 0);
> +               } else {
> +                       dc_set_clear(hw, DC_OVERLAY_SCALE_CONFIG + offset,
> +                                    0, OVERLAY_SCALE_EN);
> +               }
> +               plane->scale.dirty = false;
> +       }
> +
> +       if (plane->pos.dirty) {
> +               dc_write(hw, hw->reg[i].top_left + offset,
> +                        X_POS(plane->pos.start_x) |
> +                        Y_POS(plane->pos.start_y));
> +               dc_write(hw, hw->reg[i].bottom_right + offset,
> +                        X_POS(plane->pos.end_x) |
> +                        Y_POS(plane->pos.end_y));
> +               plane->pos.dirty = false;
> +       }
> +
> +       if (plane->blend.dirty) {
> +               dc_write(hw, hw->reg[i].src_global_color + offset,
> +                        OVERLAY_ALPHA_LEN(plane->blend.alpha));
> +               dc_write(hw, hw->reg[i].dst_global_color + offset,
> +                        OVERLAY_ALPHA_LEN(plane->blend.alpha));
> +               switch (plane->blend.blend_mode) {
> +               case DRM_MODE_BLEND_PREMULTI:
> +                       dc_write(hw, hw->reg[i].blend_config + offset, BLEND_PREMULTI);
> +                       break;
> +               case DRM_MODE_BLEND_COVERAGE:
> +                       dc_write(hw, hw->reg[i].blend_config + offset, BLEND_COVERAGE);
> +                       break;
> +               case DRM_MODE_BLEND_PIXEL_NONE:
> +                       dc_write(hw, hw->reg[i].blend_config + offset, BLEND_PIXEL_NONE);
> +                       break;
> +               default:
> +                       break;
> +               }
> +               plane->blend.dirty = false;
> +       }
> +}
> +
> +static void plane_ex_commit(struct dc_hw *hw)
> +{
> +       struct dc_hw_plane *plane;
> +       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)
> +                       plane_ex_commit_primary(hw, plane, i, offset);
> +               else
> +                       plane_ex_commit_overlay(hw, plane, i, offset);
> +       }
> +}
> +
> +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, PANEL0_EN | TWO_PANEL_EN);
> +               else
> +                       dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, PANEL1_EN | TWO_PANEL_EN);
> +
> +               dc_write(hw, DC_DISPLAY_H + offset,
> +                        H_ACTIVE_LEN(hw->display[id].h_active) |
> +                        H_TOTAL_LEN(hw->display[id].h_total));
> +
> +               dc_write(hw, DC_DISPLAY_H_SYNC + offset,
> +                        H_SYNC_START_LEN(hw->display[id].h_sync_start) |
> +                        H_SYNC_END_LEN(hw->display[id].h_sync_end) |
> +                        H_POLARITY_LEN(hw->display[id].h_sync_polarity ? 0 : 1) |
> +                        H_PLUS_LEN(1));
> +
> +               dc_write(hw, DC_DISPLAY_V + offset,
> +                        V_ACTIVE_LEN(hw->display[id].v_active) |
> +                        V_TOTAL_LEN(hw->display[id].v_total));
> +
> +               dc_write(hw, DC_DISPLAY_V_SYNC + offset,
> +                        V_SYNC_START_LEN(hw->display[id].v_sync_start) |
> +                        V_SYNC_END_LEN(hw->display[id].v_sync_end) |
> +                        V_POLARITY_LEN(hw->display[id].v_sync_polarity ? 0 : 1) |
> +                        V_PLUS_LEN(1));
> +
> +               if (hw->info->pipe_sync)
> +                       dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
> +                                    0, PRIMARY_SYNC0_EN | PRIMARY_SYNC1_EN);
> +
> +               dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, PANEL_OUTPUT_EN, 0);
> +               if (id == 0)
> +                       dc_set_clear(hw, DC_DISPLAY_PANEL_START, PANEL0_EN, SYNC_EN);
> +               else
> +                       dc_set_clear(hw, DC_DISPLAY_PANEL_START, PANEL1_EN, SYNC_EN);
> +       } else {
> +               dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, PANEL_OUTPUT_EN);
> +
> +               if (id == 0)
> +                       dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, PANEL0_EN | TWO_PANEL_EN);
> +               else
> +                       dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, PANEL1_EN | TWO_PANEL_EN);
> +       }
> +}
> +
> +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,
> +                                    PANEL_RGB2YUV_EN, 0);
> +               else
> +                       dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset,
> +                                    0, PANEL_RGB2YUV_EN);
> +               dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | DP_SELECT);
> +       }
> +
> +       if (hw->out[id] == OUT_DPI)
> +               dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, DP_SELECT);
> +
> +       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,
> +                                        X_LCOTION(hw->cursor[i].x) |
> +                                        Y_LCOTION(hw->cursor[i].y));
> +                               dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
> +                                            CURSOR_HOT_X(hw->cursor[i].hot_x) |
> +                                            CURSOR_HOT_y(hw->cursor[i].hot_y) |
> +                                            CURSOR_SIZE(hw->cursor[i].size) |
> +                                            CURSOR_VALID(1) |
> +                                            CURSOR_TRIG_FETCH(1) |
> +                                            CURSOR_FORMAT(CURSOR_FORMAT_A8R8G8B8),
> +                                            CURSOR_HOT_X_MASK |
> +                                            CURSOR_HOT_y_MASK |
> +                                            CURSOR_SIZE_MASK |
> +                                            CURSOR_VALID_MASK |
> +                                            CURSOR_TRIG_FETCH_MASK |
> +                                            CURSOR_FORMAT_MASK);
> +                       } else {
> +                               dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
> +                                            CURSOR_VALID(1),
> +                                            CURSOR_FORMAT_MASK);
> +                       }
> +                       hw->cursor[i].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 000000000000..fcf51f00adaa
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
> @@ -0,0 +1,580 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_DC_HW_H__
> +#define __VS_DC_HW_H__
> +
> +#include <linux/bitfield.h>
> +#include <linux/bits.h>
> +
> +#define UPDATE(x, h, l)                                FIELD_PREP(GENMASK(h, l), x)
> +
> +#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 PRIMARY_FORMAT(x)                     ((x) << 26)
> +# define PRIMARY_FORMAT_MASK                   GENMASK(31, 26)
> +# define PRIMARY_UV_SWIZ(x)                    ((x) << 25)
> +# define PRIMARY_UV_SWIZ_MASK                  GENMASK(25, 25)
> +# define PRIMARY_SWIZ(x)                       ((x) << 23)
> +# define PRIMARY_SWIZ_MASK                     GENMASK(24, 23)
> +# define PRIMARY_SCALE_EN                      BIT(12)
> +# define PRIMARY_TILE(x)                       ((x) << 17)
> +# define PRIMARY_TILE_MASK                     GENMASK(21, 17)
> +# define PRIMARY_YUV_COLOR(x)                  ((x) << 14)
> +# define PRIMARY_YUV_COLOR_MASK                        GENMASK(16, 14)
> +# define PRIMARY_ROTATION(x)                   ((x) << 11)
> +# define PRIMARY_ROTATION_MASK                 GENMASK(13, 11)
> +# define PRIMARY_CLEAR_EN(x)                   ((x) << 8)
> +# define PRIMARY_CLEAR_EN_MASK                 GENMASK(8, 8)
> +
> +#define DC_FRAMEBUFFER_CONFIG_EX               0x1CC0
> +# define PRIMARY_CHANNEL(x)                    ((x) << 19)
> +# define PRIMARY_CHANNEL_MASK                  GENMASK(19, 19)
> +# define PRIMARY_ZPOS(x)                       ((x) << 16)
> +# define PRIMARY_ZPOS_MASK                     GENMASK(18, 16)
> +# define PRIMARY_EN(x)                         ((x) << 13)
> +# define PRIMARY_EN_MASK                       GENMASK(13, 13)
> +# define PRIMARY_SHADOW_EN                     BIT(12)
> +# define PRIMARY_YUVCLAMP_EN                   BIT(8)
> +# define PRIMARY_RGB2RGB_EN                    BIT(6)
> +# define PRIMARY_SYNC1_EN                      BIT(4)
> +# define PRIMARY_SYNC0_EN                      BIT(3)
> +# define PRIMARY_DECODER_EN(x)                 ((x) << 1)
> +# define PRIMARY_DECODER_EN_EN_MASK            GENMASK(1, 1)
> +
> +#define DC_FRAMEBUFFER_SCALE_CONFIG            0x1520
> +#define DC_FRAMEBUFFER_TOP_LEFT                        0x24D8
> +#define X_POS(x)                               (x)
> +#define Y_POS(x)                               ((x) << 15)
> +
> +#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 FB_SIZE(w, h)                          ((w) | ((h) << 15))
> +
> +#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 BLEND_PREMULTI                                0x3450
> +# define BLEND_COVERAGE                                0x3950
> +# define BLEND_PIXEL_NONE                      0x3548
> +
> +#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR                0x2500
> +# define PRIMARY_ALPHA_LEN(x)                  ((x) << 24)
> +
> +#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR                0x2508
> +
> +#define DC_OVERLAY_CONFIG                      0x1540
> +# define OVERLAY_SHADOW_EN                     BIT(31)
> +# define OVERLAY_CLAMP_EN                      BIT(30)
> +# define OVERLAY_RGB2RGB_EN                    BIT(29)
> +# define OVERLAY_DEC_EN(x)                     ((x) << 27)
> +# define OVERLAY_DEC_EN_MASK                   GENMASK(27, 27)
> +# define OVERLAY_CLEAR_EN(x)                   ((x) << 25)
> +# define OVERLAY_CLEAR_EN_MASK                 GENMASK(25, 25)
> +# define OVERLAY_FB_EN(x)                      ((x) << 24)
> +# define OVERLAY_FB_EN_MASK                    GENMASK(24, 24)
> +# define OVERLAY_FORMAT(x)                     ((x) << 16)
> +# define OVERLAY_FORMAT_MASK                   GENMASK(21, 16)
> +# define OVERLAY_UV_SWIZ(x)                    ((x) << 15)
> +# define OVERLAY_UV_SWIZ_MASK                  GENMASK(15, 15)
> +# define OVERLAY_SWIZ(x)                       ((x) << 13)
> +# define OVERLAY_SWIZ_MASK                     GENMASK(14, 13)
> +# define OVERLAY_TILE(x)                       ((x) << 8)
> +# define OVERLAY_TILE_MASK                     GENMASK(12, 8)
> +# define OVERLAY_YUV_COLOR(x)                  ((x) << 5)
> +# define OVERLAY_YUV_COLOR_MASK                        GENMASK(7, 5)
> +# define OVERLAY_ROTATION(x)                   ((x) << 2)
> +# define OVERLAY_ROTATION_MASK                 GENMASK(4, 2)
> +
> +#define DC_OVERLAY_CONFIG_EX                   0x2540
> +# define OVERLAY_LAYER_SEL(x)                  ((x) << 0)
> +# define OVERLAY_LAYER_SEL_MASK                        GENMASK(2, 0)
> +# define OVERLAY_PANEL_SEL(x)                  ((x) << 3)
> +# define OVERLAY_PANEL_SEL_MASK                        GENMASK(3, 3)
> +
> +#define DC_OVERLAY_SCALE_CONFIG                        0x1C00
> +# define OVERLAY_SCALE_EN                      BIT(8)
> +
> +#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 OVERLAY_ALPHA_LEN(x)                  ((x) << 24)
> +
> +#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 CURSOR_HOT_X(x)                       ((x) << 16)
> +# define CURSOR_HOT_X_MASK                     GENMASK(23, 16)
> +# define CURSOR_HOT_y(x)                       ((x) << 8)
> +# define CURSOR_HOT_y_MASK                     GENMASK(15, 8)
> +# define CURSOR_SIZE(x)                                ((x) << 5)
> +# define CURSOR_SIZE_MASK                      GENMASK(7, 5)
> +# define CURSOR_VALID(x)                       ((x) << 3)
> +# define CURSOR_VALID_MASK                     GENMASK(3, 3)
> +# define CURSOR_TRIG_FETCH(x)                  ((x) << 2)
> +# define CURSOR_TRIG_FETCH_MASK                        GENMASK(2, 2)
> +# define CURSOR_FORMAT(x)                      ((x) << 0)
> +# define CURSOR_FORMAT_MASK                    GENMASK(1, 0)
> +# define CURSOR_FORMAT_DISABLE                 0
> +# define CURSOR_FORMAT_MARK                    1
> +# define CURSOR_FORMAT_A8R8G8B8                        2
> +
> +#define DC_CURSOR_ADDRESS                      0x146C
> +#define DC_CURSOR_LOCATION                     0x1470
> +# define X_LCOTION(x)                          (x)
> +# define Y_LCOTION(x)                          ((x) << 16)
> +
> +#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 PANEL_RGB2YUV_EN                      BIT(16)
> +# define PANEL_GAMMA_EN                                BIT(13)
> +# define PANEL_OUTPUT_EN                       BIT(12)
> +
> +#define DC_DISPLAY_PANEL_CONFIG_EX             0x2518
> +# define PANEL_SHADOW_EN                       BIT(0)
> +
> +#define DC_DISPLAY_DITHER_TABLE_LOW            0x1420
> +#define DC_DISPLAY_DITHER_TABLE_HIGH           0x1428
> +#define DC_DISPLAY_H                           0x1430
> +# define H_ACTIVE_LEN(x)                       (x)
> +# define H_TOTAL_LEN(x)                                ((x) << 16)
> +
> +#define DC_DISPLAY_H_SYNC                      0x1438
> +# define H_SYNC_START_LEN(x)                   (x)
> +# define H_SYNC_END_LEN(x)                     ((x) << 15)
> +# define H_PLUS_LEN(x)                         ((x) << 30)
> +# define H_POLARITY_LEN(x)                     ((x) << 31)
> +
> +#define DC_DISPLAY_V                           0x1440
> +# define V_ACTIVE_LEN(x)                       (x)
> +# define V_TOTAL_LEN(x)                                ((x) << 16)
> +
> +#define DC_DISPLAY_V_SYNC                      0x1448
> +# define V_SYNC_START_LEN(x)                   (x)
> +# define V_SYNC_END_LEN(x)                     ((x) << 15)
> +# define V_PLUS_LEN(x)                         ((x) << 30)
> +# define V_POLARITY_LEN(x)                     ((x) << 31)
> +
> +#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 PANEL0_EN                             BIT(0)
> +# define PANEL1_EN                             BIT(1)
> +# define TWO_PANEL_EN                          BIT(2)
> +# define SYNC_EN                               BIT(3)
> +
> +#define DC_DISPLAY_DEBUG_COUNTER_SELECT                0x14D0
> +#define DC_DISPLAY_DEBUG_COUNTER_VALUE         0x14D8
> +#define DC_DISPLAY_DP_CONFIG                   0x1CD0
> +# define DP_SELECT                             BIT(3)
> +
> +#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_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,
> +       FORMAT_A4R4G4B4,
> +       FORMAT_X1R5G5B5,
> +       FORMAT_A1R5G5B5,
> +       FORMAT_R5G6B5,
> +       FORMAT_X8R8G8B8,
> +       FORMAT_A8R8G8B8,
> +       FORMAT_YUY2,
> +       FORMAT_UYVY,
> +       FORMAT_INDEX8,
> +       FORMAT_MONOCHROME,
> +       FORMAT_YV12 = 0xf,
> +       FORMAT_A8,
> +       FORMAT_NV12,
> +       FORMAT_NV16,
> +       FORMAT_RG16,
> +       FORMAT_R8,
> +       FORMAT_NV12_10BIT,
> +       FORMAT_A2R10G10B10,
> +       FORMAT_NV16_10BIT,
> +       FORMAT_INDEX1,
> +       FORMAT_INDEX2,
> +       FORMAT_INDEX4,
> +       FORMAT_P010,
> +       FORMAT_YUV444,
> +       FORMAT_YUV444_10BIT,
> +};
> +
> +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,
> +};
> +
> +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_plane {
> +       struct dc_hw_fb                 fb;
> +       struct dc_hw_position   pos;
> +       struct dc_hw_scale              scale;
> +       struct dc_hw_blend              blend;
> +};
> +
> +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_hw_out          out[DC_DISPLAY_NUM];
> +       void                    *hi_base;
> +       void                    *reg_base;
> +       struct dc_hw_plane_reg  reg[DC_LAYER_NUM];
> +
> +       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_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_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);
> +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 4fb1f29ef84b..3ef90c8238a0 100644
> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> @@ -22,6 +22,7 @@
>
>  #include "vs_drv.h"
>  #include "vs_modeset.h"
> +#include "vs_dc.h"
>
>  #define DRV_NAME       "verisilicon"
>  #define DRV_DESC       "Verisilicon DRM driver"
> @@ -212,6 +213,7 @@ static const struct component_master_ops vs_drm_ops = {
>  };
>
>  static struct platform_driver *drm_sub_drivers[] = {
> +       &dc_platform_driver,
>  };
>
>  static struct component_match *vs_drm_match_add(struct device *dev)
> diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c
> new file mode 100644
> index 000000000000..53580fbe05b1
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_plane.c
> @@ -0,0 +1,301 @@
> +// 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 "vs_plane.h"
> +#include "vs_drv.h"
> +#include "vs_dc.h"
> +
> +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);
> +
> +       kfree(vs_plane_state);
> +}
> +
> +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)
> +               vs_plane_atomic_destroy_state(plane, plane->state);
> +
> +       state = kzalloc(sizeof(*state), GFP_KERNEL);
> +       if (!state)
> +               return;
> +
> +       state->base.zpos = vs_plane->id;
> +       __drm_atomic_helper_plane_reset(plane, &state->base);
> +}
> +
> +static struct drm_plane_state *
> +vs_plane_atomic_duplicate_state(struct drm_plane *plane)
> +{
> +       struct vs_plane_state *old_state;
> +       struct vs_plane_state *state;
> +
> +       if (WARN_ON(!plane->state))
> +               return NULL;
> +
> +       old_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);
> +
> +       return &state->base;
> +}
> +
> +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,
> +       .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;
> +       }
> +
> +       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;
> +       }
> +
> +       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,
> +};
> +
> +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;
> +
> +       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);
> +
> +       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 000000000000..7174ac298ffd
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_plane.h
> @@ -0,0 +1,39 @@
> +/* 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_state {
> +       struct drm_plane_state base;
> +       dma_addr_t dma_addr[DRM_FORMAT_MAX_PLANES];
> +};
> +
> +struct vs_plane {
> +       struct drm_plane base;
> +       u8 id;
> +};
> +
> +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 000000000000..7d3378e29c87
> --- /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;

I could not fine a call to either of drm_plane_create_zpos_property()
and drm_plane_create_zpos_immutable_property().
Please add them to document your zpos values.

> +
> +       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
>


--
With best wishes

Dmitry

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

* Re: [v3 5/6] drm/vs: Add hdmi driver
  2023-12-04 12:33 ` [v3 5/6] drm/vs: Add hdmi driver Keith Zhao
@ 2023-12-05 13:02   ` Dmitry Baryshkov
  2023-12-06  9:04   ` Maxime Ripard
  2023-12-11 17:34   ` Rob Herring
  2 siblings, 0 replies; 52+ messages in thread
From: Dmitry Baryshkov @ 2023-12-05 13:02 UTC (permalink / raw)
  To: Keith Zhao
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	william.qiu, linux-kernel, dri-devel, xingyu.wu, jack.zhu,
	palmer, mripard, tzimmermann, paul.walmsley, shengyang.chen,
	linux-riscv, changhuang.liang

On Mon, 4 Dec 2023 at 14:33, Keith Zhao <keith.zhao@starfivetech.com> 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 | 849 ++++++++++++++++++++
>  drivers/gpu/drm/verisilicon/starfive_hdmi.h | 304 +++++++
>  drivers/gpu/drm/verisilicon/vs_drv.c        |   3 +
>  drivers/gpu/drm/verisilicon/vs_drv.h        |   4 +
>  6 files changed, 1169 insertions(+)
>  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 e10fa97635aa..122c786e3948 100644
> --- a/drivers/gpu/drm/verisilicon/Kconfig
> +++ b/drivers/gpu/drm/verisilicon/Kconfig
> @@ -11,3 +11,11 @@ config DRM_VERISILICON
>           This driver provides VeriSilicon kernel mode
>           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 bf6f2b7ee480..71fadafcee13 100644
> --- a/drivers/gpu/drm/verisilicon/Makefile
> +++ b/drivers/gpu/drm/verisilicon/Makefile
> @@ -6,4 +6,5 @@ vs_drm-objs := vs_dc_hw.o \
>                 vs_drv.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 000000000000..aa621db0dee0
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
> @@ -0,0 +1,849 @@
> +// 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 const char * const hdmi_clocks[] = {
> +       "sysclk",
> +       "mclk",
> +       "bclk"
> +};
> +
> +static struct starfive_hdmi_encoder *encoder_to_hdmi(struct drm_encoder *encoder)
> +{
> +       return container_of(encoder, struct starfive_hdmi_encoder, encoder);
> +}
> +
> +static struct starfive_hdmi *connector_to_hdmi(struct drm_connector *connector)
> +{
> +       return container_of(connector, struct starfive_hdmi, connector);
> +}
> +
> +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_writew(struct starfive_hdmi *hdmi, u16 offset, u32 val)
> +{
> +       writew_relaxed(val & 0xFF, hdmi->regs + (offset) * 0x04);
> +       writew_relaxed((val >> 8) & 0xFF, hdmi->regs + (offset + 1) * 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_bulk_prepare_enable(hdmi->nclks, hdmi->clk_hdmi);
> +       if (ret) {
> +               dev_err(dev, "failed to enable clocks\n");
> +               return ret;
> +       }
> +
> +       ret = reset_control_deassert(hdmi->tx_rst);
> +       if (ret < 0) {
> +               dev_err(dev, "failed to deassert tx_rst\n");
> +               return ret;
> +       }
> +       return 0;
> +}
> +
> +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_bulk_disable_unprepare(hdmi->nclks, hdmi->clk_hdmi);
> +}
> +
> +#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->clk_hdmi[CLK_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 void starfive_hdmi_phy_get_pre_pll_cfg(struct starfive_hdmi *hdmi)
> +{
> +       if (hdmi->tmds_rate > 30000000) {
> +               hdmi->pre_cfg.pixclock = hdmi->tmds_rate;
> +               hdmi->pre_cfg.tmdsclock = hdmi->tmds_rate;
> +               hdmi->pre_cfg.prediv = 1;
> +               hdmi->pre_cfg.fbdiv = hdmi->tmds_rate / 3000000;
> +               hdmi->pre_cfg.tmds_div_a = 0;
> +               hdmi->pre_cfg.tmds_div_b = 1;
> +               hdmi->pre_cfg.tmds_div_c = 1;
> +               hdmi->pre_cfg.pclk_div_a = 1;
> +               hdmi->pre_cfg.pclk_div_b = 0;
> +               hdmi->pre_cfg.pclk_div_c = 2;
> +               hdmi->pre_cfg.pclk_div_d = 2;
> +               hdmi->pre_cfg.vco_div_5_en = hdmi->tmds_rate % 3000000 ? 1 : 0;
> +
> +               if (hdmi->pre_cfg.vco_div_5_en) {
> +                       hdmi->pre_cfg.fracdiv = (hdmi->tmds_rate % 3000000) *
> +                                                0xffffff / 1000000;
> +               }
> +       } else {
> +               hdmi->pre_cfg.pixclock = hdmi->tmds_rate;
> +               hdmi->pre_cfg.tmdsclock = hdmi->tmds_rate;
> +               hdmi->pre_cfg.prediv = 1;
> +               hdmi->pre_cfg.fbdiv = hdmi->tmds_rate / 1000000;
> +               hdmi->pre_cfg.tmds_div_a = 2;
> +               hdmi->pre_cfg.tmds_div_b = 1;
> +               hdmi->pre_cfg.tmds_div_c = 1;
> +               hdmi->pre_cfg.pclk_div_a = 3;
> +               hdmi->pre_cfg.pclk_div_b = 0;
> +               hdmi->pre_cfg.pclk_div_c = 3;
> +               hdmi->pre_cfg.pclk_div_d = 4;
> +               hdmi->pre_cfg.vco_div_5_en = hdmi->tmds_rate % 1000000 ? 1 : 0;
> +
> +               if (hdmi->pre_cfg.vco_div_5_en) {
> +                       hdmi->pre_cfg.fracdiv = (hdmi->tmds_rate % 1000000) *
> +                                                0xffffff / 1000000;
> +               }
> +       }
> +}
> +
> +static int starfive_hdmi_phy_clk_set_rate(struct starfive_hdmi *hdmi)
> +{
> +       hdmi->post_cfg = post_pll_cfg_table;
> +
> +       starfive_hdmi_phy_get_pre_pll_cfg(hdmi);
> +
> +       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_writew(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value);
> +
> +       value = mode->htotal - mode->hdisplay;
> +       hdmi_writew(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value);
> +
> +       value = mode->htotal - mode->hsync_start;
> +       hdmi_writew(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value);
> +
> +       value = mode->hsync_end - mode->hsync_start;
> +       hdmi_writew(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value);
> +
> +       value = mode->vtotal;
> +       hdmi_writew(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value);
> +
> +       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)
> +{
> +       int ret;
> +       u32 val;
> +
> +       hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
> +       hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
> +
> +       hdmi->tmds_rate = mode->clock * 1000;
> +       starfive_hdmi_phy_clk_set_rate(hdmi);
> +
> +       ret = readx_poll_timeout(readl_relaxed,
> +                                hdmi->regs + (STARFIVE_PRE_PLL_LOCK_STATUS) * 0x04,
> +                                val, val & 0x1, 1000, 100000);
> +       if (ret < 0) {
> +               dev_err(hdmi->dev, "failed to wait pre-pll lock\n");
> +               return ret;
> +       }
> +
> +       ret = readx_poll_timeout(readl_relaxed,
> +                                hdmi->regs + (STARFIVE_POST_PLL_LOCK_STATUS) * 0x04,
> +                                val, val & 0x1, 1000, 100000);
> +       if (ret < 0) {
> +               dev_err(hdmi->dev, "failed to wait post-pll lock\n");
> +               return ret;
> +       }
> +
> +       /*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_enable(struct drm_encoder *encoder)
> +{
> +       struct starfive_hdmi_encoder *hdmi_encoder = encoder_to_hdmi(encoder);
> +       struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
> +       int ret, idx;
> +       struct drm_device *drm = hdmi_encoder->hdmi->connector.dev;
> +
> +       if (drm && !drm_dev_enter(drm, &idx))
> +               return;
> +
> +       ret = pm_runtime_get_sync(hdmi_encoder->hdmi->dev);
> +       if (ret < 0)
> +               return;
> +       starfive_hdmi_setup(hdmi_encoder->hdmi, mode);
> +
> +       if (drm)
> +               drm_dev_exit(idx);
> +}
> +
> +static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
> +{
> +       struct starfive_hdmi_encoder *hdmi_encoder = encoder_to_hdmi(encoder);
> +
> +       pm_runtime_put(hdmi_encoder->hdmi->dev);
> +}
> +
> +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;
> +
> +       int pclk = mode->clock * 1000;
> +
> +       if (pclk <= PIXCLOCK_4K_30FPS)
> +               valid = true;
> +
> +       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,
> +       .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);
> +       int ret = 0;
> +
> +       if (!hdmi->ddc)
> +               return 0;
> +       ret = pm_runtime_get_sync(hdmi->dev);
> +       if (ret < 0)
> +               return ret;
> +
> +       ret = drm_connector_helper_get_modes_from_ddc(connector);
> +       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)
> +{
> +       int pclk = mode->clock * 1000;
> +       bool valid = false;
> +
> +       if (pclk <= PIXCLOCK_4K_30FPS)
> +               valid = true;
> +
> +       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_encoder *hdmi_encoder)
> +{
> +       struct drm_encoder *encoder = &hdmi_encoder->encoder;
> +       struct device *dev = hdmi_encoder->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_encoder->hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> +
> +       drm_connector_helper_add(&hdmi_encoder->hdmi->connector,
> +                                &starfive_hdmi_connector_helper_funcs);
> +       drmm_connector_init(drm, &hdmi_encoder->hdmi->connector,
> +                           &starfive_hdmi_connector_funcs,
> +                           DRM_MODE_CONNECTOR_HDMIA,
> +                           hdmi_encoder->hdmi->ddc);
> +
> +       drm_connector_attach_encoder(&hdmi_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);

What if the I2C transfer doesn't come in a write-read pair? Or if it
comes with more read transfers? You are heavily depending on the
particular message transfer structure.


> +       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 = devm_kzalloc(hdmi->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) {
> +               dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
> +               devm_kfree(hdmi->dev, i2c);
> +               return ERR_PTR(ret);
> +       }
> +
> +       hdmi->i2c = i2c;
> +
> +       DRM_DEV_INFO(hdmi->dev, "registered %s I2C bus driver success\n", adap->name);

this should be drm_dbg

> +
> +       return adap;
> +}
> +
> +static int starfive_hdmi_get_clk_rst(struct device *dev, struct starfive_hdmi *hdmi)
> +{
> +       int ret;
> +
> +       hdmi->nclks = ARRAY_SIZE(hdmi->clk_hdmi);
> +       for (int i = 0; i < hdmi->nclks; ++i)
> +               hdmi->clk_hdmi[i].id = hdmi_clocks[i];
> +
> +       ret = devm_clk_bulk_get(dev, hdmi->nclks, hdmi->clk_hdmi);
> +       if (ret) {
> +               dev_err(dev, "Failed to get clk controls\n");
> +               return ret;
> +       }
> +
> +       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 drm_device *drm = dev_get_drvdata(master);
> +       struct starfive_hdmi_encoder *hdmi_encoder;
> +       int ret;
> +
> +       hdmi_encoder = drmm_simple_encoder_alloc(drm, struct starfive_hdmi_encoder,
> +                                                encoder, DRM_MODE_ENCODER_TMDS);
> +       if (IS_ERR(hdmi_encoder))
> +               return PTR_ERR(hdmi_encoder);
> +
> +       hdmi_encoder->hdmi = dev_get_drvdata(dev);
> +       hdmi_encoder->hdmi->drm_dev = drm;
> +
> +       ret = pm_runtime_resume_and_get(dev);
> +       if (ret)
> +               return ret;
> +
> +       starfive_hdmi_i2c_init(hdmi_encoder->hdmi);
> +
> +       ret = starfive_hdmi_register(drm, hdmi_encoder);
> +       if (ret)
> +               goto err_put_adapter;
> +
> +       /* Unmute hotplug interrupt */
> +       hdmi_modb(hdmi_encoder->hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
> +
> +       ret = devm_request_threaded_irq(dev, hdmi_encoder->hdmi->irq, starfive_hdmi_hardirq,
> +                                       starfive_hdmi_irq, IRQF_SHARED,
> +                                       dev_name(dev), hdmi_encoder->hdmi);
> +       if (ret < 0)
> +               goto err_put_adapter;
> +
> +       pm_runtime_put_sync(dev);

What happens if the IRQ is triggered while the device is suspended?

> +
> +       return 0;
> +
> +err_put_adapter:
> +       i2c_put_adapter(hdmi_encoder->hdmi->ddc);

pm_runtime_put_sync is missing.

> +       return ret;
> +}
> +
> +static const struct component_ops starfive_hdmi_ops = {
> +       .bind   = starfive_hdmi_bind,
> +};
> +
> +static int starfive_hdmi_probe(struct platform_device *pdev)
> +{
> +       int ret;
> +       struct starfive_hdmi *hdmi;
> +       struct resource *iores;
> +
> +       hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
> +       if (!hdmi)
> +               return -ENOMEM;
> +
> +       dev_set_drvdata(&pdev->dev, hdmi);
> +       hdmi->dev = &pdev->dev;
> +
> +       iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       hdmi->regs = devm_ioremap_resource(hdmi->dev, iores);
> +       if (IS_ERR(hdmi->regs))
> +               return PTR_ERR(hdmi->regs);
> +
> +       ret = starfive_hdmi_get_clk_rst(hdmi->dev, hdmi);
> +       if (ret < 0)
> +               return ret;
> +
> +       ret = devm_pm_runtime_enable(hdmi->dev);
> +       if (ret)
> +               return ret;
> +
> +       hdmi->irq = platform_get_irq(pdev, 0);
> +       if (hdmi->irq < 0) {
> +               ret = hdmi->irq;
> +               return ret;
> +       }
> +
> +       hdmi->ddc = starfive_hdmi_i2c_adapter(hdmi);
> +       if (IS_ERR(hdmi->ddc)) {
> +               ret = PTR_ERR(hdmi->ddc);
> +               hdmi->ddc = NULL;
> +               return ret;
> +       }
> +
> +       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 000000000000..ca5f40be0796
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.h
> @@ -0,0 +1,304 @@
> +/* 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)
> +
> +#define PIXCLOCK_4K_30FPS                                      297000000
> +
> +enum hdmi_clk {
> +       CLK_SYS = 0,
> +       CLK_M,
> +       CLK_B,
> +       CLK_HDMI_NUM
> +};
> +
> +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 starfive_hdmi_encoder {
> +       struct drm_encoder encoder;
> +       struct starfive_hdmi *hdmi;
> +};
> +
> +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;
> +};
> +
> +struct starfive_hdmi {
> +       struct device *dev;
> +       struct drm_device *drm_dev;
> +       struct drm_connector    connector;
> +       void __iomem *regs;
> +
> +       int irq;
> +       struct clk_bulk_data    clk_hdmi[CLK_HDMI_NUM];
> +       struct reset_control *tx_rst;
> +       int     nclks;
> +
> +       struct i2c_adapter *ddc;
> +       struct starfive_hdmi_i2c *i2c;
> +
> +       unsigned long tmds_rate;
> +       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 3ef90c8238a0..d7e5199fe293 100644
> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> @@ -214,6 +214,9 @@ static const struct component_master_ops vs_drm_ops = {
>
>  static struct platform_driver *drm_sub_drivers[] = {
>         &dc_platform_driver,
> +#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 ea2189772980..9a88cf9a7362 100644
> --- a/drivers/gpu/drm/verisilicon/vs_drv.h
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.h
> @@ -39,4 +39,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
>


-- 
With best wishes
Dmitry

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

* Re: [v3 6/6] drm/vs: simple encoder
  2023-12-04 12:33 ` [v3 6/6] drm/vs: simple encoder Keith Zhao
@ 2023-12-05 13:14   ` Dmitry Baryshkov
  2023-12-05 13:18     ` Dmitry Baryshkov
  0 siblings, 1 reply; 52+ messages in thread
From: Dmitry Baryshkov @ 2023-12-05 13:14 UTC (permalink / raw)
  To: Keith Zhao
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	william.qiu, linux-kernel, dri-devel, xingyu.wu, jack.zhu,
	palmer, mripard, tzimmermann, paul.walmsley, shengyang.chen,
	linux-riscv, changhuang.liang

On Mon, 4 Dec 2023 at 14:33, Keith Zhao <keith.zhao@starfivetech.com> wrote:
>
> add simple encoder for dsi bridge

This doesn't look like a proper commit message.

>
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>  drivers/gpu/drm/verisilicon/Makefile        |   4 +-
>  drivers/gpu/drm/verisilicon/vs_drv.c        |   2 +
>  drivers/gpu/drm/verisilicon/vs_simple_enc.c | 195 ++++++++++++++++++++
>  drivers/gpu/drm/verisilicon/vs_simple_enc.h |  23 +++
>  4 files changed, 223 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/verisilicon/vs_simple_enc.c
>  create mode 100644 drivers/gpu/drm/verisilicon/vs_simple_enc.h
>
> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> index 71fadafcee13..cd5d0a90bcfe 100644
> --- a/drivers/gpu/drm/verisilicon/Makefile
> +++ b/drivers/gpu/drm/verisilicon/Makefile
> @@ -5,6 +5,8 @@ vs_drm-objs := vs_dc_hw.o \
>                 vs_crtc.o \
>                 vs_drv.o \
>                 vs_modeset.o \
> -               vs_plane.o
> +               vs_plane.o \
> +               vs_simple_enc.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/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
> index d7e5199fe293..946f137ab124 100644
> --- a/drivers/gpu/drm/verisilicon/vs_drv.c
> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> @@ -23,6 +23,7 @@
>  #include "vs_drv.h"
>  #include "vs_modeset.h"
>  #include "vs_dc.h"
> +#include "vs_simple_enc.h"
>
>  #define DRV_NAME       "verisilicon"
>  #define DRV_DESC       "Verisilicon DRM driver"
> @@ -217,6 +218,7 @@ static struct platform_driver *drm_sub_drivers[] = {
>  #ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
>         &starfive_hdmi_driver,
>  #endif
> +       &simple_encoder_driver,
>  };
>
>  static struct component_match *vs_drm_match_add(struct device *dev)
> diff --git a/drivers/gpu/drm/verisilicon/vs_simple_enc.c b/drivers/gpu/drm/verisilicon/vs_simple_enc.c
> new file mode 100644
> index 000000000000..c5a8d82bc469
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_simple_enc.c
> @@ -0,0 +1,195 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
> + */
> +#include <linux/component.h>
> +#include <linux/of_device.h>
> +#include <linux/module.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_of.h>
> +#include <linux/regmap.h>
> +#include <linux/media-bus-format.h>
> +#include <linux/mfd/syscon.h>
> +
> +#include "vs_crtc.h"
> +#include "vs_simple_enc.h"
> +
> +static const struct simple_encoder_priv dsi_priv = {

Please use proper prefix for all the struct and function names.
vs_simple_encoder sounds better. Or vs_dsi_encoder.

> +       .encoder_type = DRM_MODE_ENCODER_DSI
> +};
> +
> +static inline struct simple_encoder *to_simple_encoder(struct drm_encoder *enc)
> +{
> +       return container_of(enc, struct simple_encoder, encoder);
> +}
> +
> +static int encoder_parse_dt(struct device *dev)
> +{
> +       struct simple_encoder *simple = dev_get_drvdata(dev);
> +       unsigned int args[2];
> +
> +       simple->dss_regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node,
> +                                                                 "starfive,syscon",
> +                                                                 2, args);
> +
> +       if (IS_ERR(simple->dss_regmap)) {
> +               return dev_err_probe(dev, PTR_ERR(simple->dss_regmap),
> +                                    "getting the regmap failed\n");
> +       }
> +
> +       simple->offset = args[0];
> +       simple->mask = args[1];
> +
> +       return 0;
> +}
> +
> +void encoder_atomic_enable(struct drm_encoder *encoder,
> +                          struct drm_atomic_state *state)
> +{
> +       struct simple_encoder *simple = to_simple_encoder(encoder);
> +
> +       regmap_update_bits(simple->dss_regmap, simple->offset, simple->mask,
> +                          simple->mask);
> +}
> +
> +int encoder_atomic_check(struct drm_encoder *encoder,
> +                        struct drm_crtc_state *crtc_state,
> +                        struct drm_connector_state *conn_state)
> +{
> +       struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
> +       struct drm_connector *connector = conn_state->connector;
> +       int ret = 0;
> +
> +       struct drm_bridge *first_bridge = drm_bridge_chain_get_first_bridge(encoder);
> +       struct drm_bridge_state *bridge_state = ERR_PTR(-EINVAL);
> +
> +       vs_crtc_state->encoder_type = encoder->encoder_type;
> +
> +       if (first_bridge && first_bridge->funcs->atomic_duplicate_state)
> +               bridge_state = drm_atomic_get_bridge_state(crtc_state->state, first_bridge);

Please don't poke into others' playground. This should go into your
DSI bridge's atomic_check() instead.

> +
> +       if (IS_ERR(bridge_state)) {
> +               if (connector->display_info.num_bus_formats)
> +                       vs_crtc_state->output_fmt = connector->display_info.bus_formats[0];
> +               else
> +                       vs_crtc_state->output_fmt = MEDIA_BUS_FMT_FIXED;
> +       } else {
> +               vs_crtc_state->output_fmt = bridge_state->input_bus_cfg.format;
> +       }
> +
> +       switch (vs_crtc_state->output_fmt) {
> +       case MEDIA_BUS_FMT_FIXED:
> +       case MEDIA_BUS_FMT_RGB565_1X16:
> +       case MEDIA_BUS_FMT_RGB666_1X18:
> +       case MEDIA_BUS_FMT_RGB888_1X24:
> +       case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> +       case MEDIA_BUS_FMT_RGB101010_1X30:
> +       case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +       case MEDIA_BUS_FMT_UYVY8_1X16:
> +       case MEDIA_BUS_FMT_YUV8_1X24:
> +       case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> +       case MEDIA_BUS_FMT_UYVY10_1X20:
> +       case MEDIA_BUS_FMT_YUV10_1X30:
> +               ret = 0;
> +               break;
> +       default:
> +               ret = -EINVAL;
> +               break;
> +       }
> +
> +       /* If MEDIA_BUS_FMT_FIXED, set it to default value */
> +       if (vs_crtc_state->output_fmt == MEDIA_BUS_FMT_FIXED)
> +               vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
> +
> +       return ret;
> +}
> +
> +static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
> +       .atomic_check = encoder_atomic_check,
> +       .atomic_enable = encoder_atomic_enable,
> +};
> +
> +static int encoder_bind(struct device *dev, struct device *master, void *data)
> +{
> +       struct drm_device *drm_dev = data;
> +       struct simple_encoder *simple = dev_get_drvdata(dev);
> +       struct drm_encoder *encoder;
> +       struct drm_bridge *bridge;
> +       int ret;
> +
> +       encoder = &simple->encoder;
> +
> +       ret = drmm_encoder_init(drm_dev, encoder, NULL, simple->priv->encoder_type, NULL);
> +       if (ret)
> +               return ret;
> +
> +       drm_encoder_helper_add(encoder, &encoder_helper_funcs);
> +
> +       encoder->possible_crtcs =
> +                       drm_of_find_possible_crtcs(drm_dev, dev->of_node);
> +
> +       /* output port is port1*/
> +       bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
> +       if (IS_ERR(bridge))
> +               return 0;
> +
> +       return drm_bridge_attach(encoder, bridge, NULL, 0);
> +}
> +
> +static const struct component_ops encoder_component_ops = {
> +       .bind = encoder_bind,
> +};
> +
> +static const struct of_device_id simple_encoder_dt_match[] = {
> +       { .compatible = "starfive,dsi-encoder", .data = &dsi_priv},
> +       {},
> +};
> +MODULE_DEVICE_TABLE(of, simple_encoder_dt_match);
> +
> +static int encoder_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct simple_encoder *simple;
> +       int ret;
> +
> +       simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
> +       if (!simple)
> +               return -ENOMEM;
> +
> +       simple->priv = of_device_get_match_data(dev);
> +
> +       simple->dev = dev;
> +
> +       dev_set_drvdata(dev, simple);
> +
> +       ret = encoder_parse_dt(dev);
> +       if (ret)
> +               return ret;
> +
> +       return component_add(dev, &encoder_component_ops);
> +}
> +
> +static int encoder_remove(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +
> +       component_del(dev, &encoder_component_ops);
> +       dev_set_drvdata(dev, NULL);
> +
> +       return 0;
> +}
> +
> +struct platform_driver simple_encoder_driver = {
> +       .probe = encoder_probe,
> +       .remove = encoder_remove,
> +       .driver = {
> +               .name = "vs-simple-encoder",
> +               .of_match_table = of_match_ptr(simple_encoder_dt_match),
> +       },
> +};
> +
> +MODULE_DESCRIPTION("Simple Encoder Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/verisilicon/vs_simple_enc.h b/drivers/gpu/drm/verisilicon/vs_simple_enc.h
> new file mode 100644
> index 000000000000..fb33ca9e18d6
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/vs_simple_enc.h
> @@ -0,0 +1,23 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2022 VeriSilicon Holdings Co., Ltd.
> + */
> +
> +#ifndef __VS_SIMPLE_ENC_H_
> +#define __VS_SIMPLE_ENC_H_
> +
> +struct simple_encoder_priv {
> +       unsigned char encoder_type;
> +};
> +
> +struct simple_encoder {
> +       struct drm_encoder encoder;
> +       struct device *dev;
> +       const struct simple_encoder_priv *priv;
> +       struct regmap *dss_regmap;
> +       unsigned int offset;
> +       unsigned int mask;
> +};
> +
> +extern struct platform_driver simple_encoder_driver;
> +#endif /* __VS_SIMPLE_ENC_H_ */
> --
> 2.34.1
>


-- 
With best wishes
Dmitry

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

* Re: [v3 6/6] drm/vs: simple encoder
  2023-12-05 13:14   ` Dmitry Baryshkov
@ 2023-12-05 13:18     ` Dmitry Baryshkov
  2024-05-15 10:07       ` Keith Zhao
  0 siblings, 1 reply; 52+ messages in thread
From: Dmitry Baryshkov @ 2023-12-05 13:18 UTC (permalink / raw)
  To: Keith Zhao
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	william.qiu, linux-kernel, dri-devel, xingyu.wu, jack.zhu,
	palmer, mripard, tzimmermann, paul.walmsley, shengyang.chen,
	linux-riscv, changhuang.liang

On Tue, 5 Dec 2023 at 15:14, Dmitry Baryshkov
<dmitry.baryshkov@linaro.org> wrote:
>
> On Mon, 4 Dec 2023 at 14:33, Keith Zhao <keith.zhao@starfivetech.com> wrote:
> >
> > add simple encoder for dsi bridge
>
> This doesn't look like a proper commit message.
>
> >
> > Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> > ---
> >  drivers/gpu/drm/verisilicon/Makefile        |   4 +-
> >  drivers/gpu/drm/verisilicon/vs_drv.c        |   2 +
> >  drivers/gpu/drm/verisilicon/vs_simple_enc.c | 195 ++++++++++++++++++++
> >  drivers/gpu/drm/verisilicon/vs_simple_enc.h |  23 +++
> >  4 files changed, 223 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/gpu/drm/verisilicon/vs_simple_enc.c
> >  create mode 100644 drivers/gpu/drm/verisilicon/vs_simple_enc.h
> >
> > diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> > index 71fadafcee13..cd5d0a90bcfe 100644
> > --- a/drivers/gpu/drm/verisilicon/Makefile
> > +++ b/drivers/gpu/drm/verisilicon/Makefile
> > @@ -5,6 +5,8 @@ vs_drm-objs := vs_dc_hw.o \
> >                 vs_crtc.o \
> >                 vs_drv.o \
> >                 vs_modeset.o \
> > -               vs_plane.o
> > +               vs_plane.o \
> > +               vs_simple_enc.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/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
> > index d7e5199fe293..946f137ab124 100644
> > --- a/drivers/gpu/drm/verisilicon/vs_drv.c
> > +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> > @@ -23,6 +23,7 @@
> >  #include "vs_drv.h"
> >  #include "vs_modeset.h"
> >  #include "vs_dc.h"
> > +#include "vs_simple_enc.h"
> >
> >  #define DRV_NAME       "verisilicon"
> >  #define DRV_DESC       "Verisilicon DRM driver"
> > @@ -217,6 +218,7 @@ static struct platform_driver *drm_sub_drivers[] = {
> >  #ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
> >         &starfive_hdmi_driver,
> >  #endif
> > +       &simple_encoder_driver,
> >  };
> >
> >  static struct component_match *vs_drm_match_add(struct device *dev)
> > diff --git a/drivers/gpu/drm/verisilicon/vs_simple_enc.c b/drivers/gpu/drm/verisilicon/vs_simple_enc.c
> > new file mode 100644
> > index 000000000000..c5a8d82bc469
> > --- /dev/null
> > +++ b/drivers/gpu/drm/verisilicon/vs_simple_enc.c
> > @@ -0,0 +1,195 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
> > + */
> > +#include <linux/component.h>
> > +#include <linux/of_device.h>
> > +#include <linux/module.h>
> > +
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_bridge.h>
> > +#include <drm/drm_crtc_helper.h>
> > +#include <drm/drm_of.h>
> > +#include <linux/regmap.h>
> > +#include <linux/media-bus-format.h>
> > +#include <linux/mfd/syscon.h>
> > +
> > +#include "vs_crtc.h"
> > +#include "vs_simple_enc.h"
> > +
> > +static const struct simple_encoder_priv dsi_priv = {
>
> Please use proper prefix for all the struct and function names.
> vs_simple_encoder sounds better. Or vs_dsi_encoder.
>
> > +       .encoder_type = DRM_MODE_ENCODER_DSI
> > +};
> > +
> > +static inline struct simple_encoder *to_simple_encoder(struct drm_encoder *enc)
> > +{
> > +       return container_of(enc, struct simple_encoder, encoder);
> > +}
> > +
> > +static int encoder_parse_dt(struct device *dev)
> > +{
> > +       struct simple_encoder *simple = dev_get_drvdata(dev);
> > +       unsigned int args[2];
> > +
> > +       simple->dss_regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node,
> > +                                                                 "starfive,syscon",
> > +                                                                 2, args);
> > +
> > +       if (IS_ERR(simple->dss_regmap)) {
> > +               return dev_err_probe(dev, PTR_ERR(simple->dss_regmap),
> > +                                    "getting the regmap failed\n");
> > +       }
> > +
> > +       simple->offset = args[0];
> > +       simple->mask = args[1];
> > +
> > +       return 0;
> > +}
> > +
> > +void encoder_atomic_enable(struct drm_encoder *encoder,
> > +                          struct drm_atomic_state *state)
> > +{
> > +       struct simple_encoder *simple = to_simple_encoder(encoder);
> > +
> > +       regmap_update_bits(simple->dss_regmap, simple->offset, simple->mask,
> > +                          simple->mask);
> > +}
> > +
> > +int encoder_atomic_check(struct drm_encoder *encoder,
> > +                        struct drm_crtc_state *crtc_state,
> > +                        struct drm_connector_state *conn_state)
> > +{
> > +       struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
> > +       struct drm_connector *connector = conn_state->connector;
> > +       int ret = 0;
> > +
> > +       struct drm_bridge *first_bridge = drm_bridge_chain_get_first_bridge(encoder);
> > +       struct drm_bridge_state *bridge_state = ERR_PTR(-EINVAL);
> > +
> > +       vs_crtc_state->encoder_type = encoder->encoder_type;
> > +
> > +       if (first_bridge && first_bridge->funcs->atomic_duplicate_state)
> > +               bridge_state = drm_atomic_get_bridge_state(crtc_state->state, first_bridge);
>
> Please don't poke into others' playground. This should go into your
> DSI bridge's atomic_check() instead.

Hmm. And you can not use vs_crtc_state from your bridge. Actually this
design makes me wonder, how does your hardware work? Is it possible to
send the DSI commands to the panel?

>
> > +
> > +       if (IS_ERR(bridge_state)) {
> > +               if (connector->display_info.num_bus_formats)
> > +                       vs_crtc_state->output_fmt = connector->display_info.bus_formats[0];
> > +               else
> > +                       vs_crtc_state->output_fmt = MEDIA_BUS_FMT_FIXED;
> > +       } else {
> > +               vs_crtc_state->output_fmt = bridge_state->input_bus_cfg.format;
> > +       }
> > +
> > +       switch (vs_crtc_state->output_fmt) {
> > +       case MEDIA_BUS_FMT_FIXED:
> > +       case MEDIA_BUS_FMT_RGB565_1X16:
> > +       case MEDIA_BUS_FMT_RGB666_1X18:
> > +       case MEDIA_BUS_FMT_RGB888_1X24:
> > +       case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> > +       case MEDIA_BUS_FMT_RGB101010_1X30:
> > +       case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> > +       case MEDIA_BUS_FMT_UYVY8_1X16:
> > +       case MEDIA_BUS_FMT_YUV8_1X24:
> > +       case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> > +       case MEDIA_BUS_FMT_UYVY10_1X20:
> > +       case MEDIA_BUS_FMT_YUV10_1X30:
> > +               ret = 0;
> > +               break;
> > +       default:
> > +               ret = -EINVAL;
> > +               break;
> > +       }
> > +
> > +       /* If MEDIA_BUS_FMT_FIXED, set it to default value */
> > +       if (vs_crtc_state->output_fmt == MEDIA_BUS_FMT_FIXED)
> > +               vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
> > +
> > +       return ret;
> > +}
> > +
> > +static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
> > +       .atomic_check = encoder_atomic_check,
> > +       .atomic_enable = encoder_atomic_enable,
> > +};
> > +
> > +static int encoder_bind(struct device *dev, struct device *master, void *data)
> > +{
> > +       struct drm_device *drm_dev = data;
> > +       struct simple_encoder *simple = dev_get_drvdata(dev);
> > +       struct drm_encoder *encoder;
> > +       struct drm_bridge *bridge;
> > +       int ret;
> > +
> > +       encoder = &simple->encoder;
> > +
> > +       ret = drmm_encoder_init(drm_dev, encoder, NULL, simple->priv->encoder_type, NULL);
> > +       if (ret)
> > +               return ret;
> > +
> > +       drm_encoder_helper_add(encoder, &encoder_helper_funcs);
> > +
> > +       encoder->possible_crtcs =
> > +                       drm_of_find_possible_crtcs(drm_dev, dev->of_node);
> > +
> > +       /* output port is port1*/
> > +       bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
> > +       if (IS_ERR(bridge))
> > +               return 0;
> > +
> > +       return drm_bridge_attach(encoder, bridge, NULL, 0);
> > +}
> > +
> > +static const struct component_ops encoder_component_ops = {
> > +       .bind = encoder_bind,
> > +};
> > +
> > +static const struct of_device_id simple_encoder_dt_match[] = {
> > +       { .compatible = "starfive,dsi-encoder", .data = &dsi_priv},
> > +       {},
> > +};
> > +MODULE_DEVICE_TABLE(of, simple_encoder_dt_match);
> > +
> > +static int encoder_probe(struct platform_device *pdev)
> > +{
> > +       struct device *dev = &pdev->dev;
> > +       struct simple_encoder *simple;
> > +       int ret;
> > +
> > +       simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
> > +       if (!simple)
> > +               return -ENOMEM;
> > +
> > +       simple->priv = of_device_get_match_data(dev);
> > +
> > +       simple->dev = dev;
> > +
> > +       dev_set_drvdata(dev, simple);
> > +
> > +       ret = encoder_parse_dt(dev);
> > +       if (ret)
> > +               return ret;
> > +
> > +       return component_add(dev, &encoder_component_ops);
> > +}
> > +
> > +static int encoder_remove(struct platform_device *pdev)
> > +{
> > +       struct device *dev = &pdev->dev;
> > +
> > +       component_del(dev, &encoder_component_ops);
> > +       dev_set_drvdata(dev, NULL);
> > +
> > +       return 0;
> > +}
> > +
> > +struct platform_driver simple_encoder_driver = {
> > +       .probe = encoder_probe,
> > +       .remove = encoder_remove,
> > +       .driver = {
> > +               .name = "vs-simple-encoder",
> > +               .of_match_table = of_match_ptr(simple_encoder_dt_match),
> > +       },
> > +};
> > +
> > +MODULE_DESCRIPTION("Simple Encoder Driver");
> > +MODULE_LICENSE("GPL");
> > diff --git a/drivers/gpu/drm/verisilicon/vs_simple_enc.h b/drivers/gpu/drm/verisilicon/vs_simple_enc.h
> > new file mode 100644
> > index 000000000000..fb33ca9e18d6
> > --- /dev/null
> > +++ b/drivers/gpu/drm/verisilicon/vs_simple_enc.h
> > @@ -0,0 +1,23 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2022 VeriSilicon Holdings Co., Ltd.
> > + */
> > +
> > +#ifndef __VS_SIMPLE_ENC_H_
> > +#define __VS_SIMPLE_ENC_H_
> > +
> > +struct simple_encoder_priv {
> > +       unsigned char encoder_type;
> > +};
> > +
> > +struct simple_encoder {
> > +       struct drm_encoder encoder;
> > +       struct device *dev;
> > +       const struct simple_encoder_priv *priv;
> > +       struct regmap *dss_regmap;
> > +       unsigned int offset;
> > +       unsigned int mask;
> > +};
> > +
> > +extern struct platform_driver simple_encoder_driver;
> > +#endif /* __VS_SIMPLE_ENC_H_ */
> > --
> > 2.34.1
> >
>
>
> --
> With best wishes
> Dmitry



-- 
With best wishes
Dmitry

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

* Re: [v3 4/6] drm/vs: Add KMS crtc&plane
  2023-12-04 12:33 ` [v3 4/6] drm/vs: Add KMS crtc&plane Keith Zhao
  2023-12-05 12:48   ` Dmitry Baryshkov
@ 2023-12-06  8:55   ` Maxime Ripard
  2023-12-06 12:53     ` Keith Zhao
                       ` (2 more replies)
  2023-12-07  8:41   ` Icenowy Zheng
  2 siblings, 3 replies; 52+ messages in thread
From: Maxime Ripard @ 2023-12-06  8:55 UTC (permalink / raw)
  To: Keith Zhao
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	paul.walmsley, linux-kernel, dri-devel, xingyu.wu, jack.zhu,
	palmer, tzimmermann, william.qiu, shengyang.chen, linux-riscv,
	changhuang.liang

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

On Mon, Dec 04, 2023 at 08:33:13PM +0800, Keith Zhao wrote:
> +static const struct vs_plane_info dc_hw_planes_rev0[PLANE_NUM] = {
> +	{
> +		.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,
> +	},
> +};
> +
> +static const struct vs_dc_info dc8200_info = {
> +	.name			= "DC8200",
> +	.panel_num		= 2,
> +	.plane_num		= 8,
> +	.planes			= dc_hw_planes_rev0,
> +	.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,
> +};

I really think that entire thing is to workaround a suboptimal device
tree binding. You should have two CRTCs in the device tree, you'll probe
twice, and you won't get to do that whole dance.


> +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;
> +};

That's your plane state.

> +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;
> +};

Your framebuffer

> +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;
> +};

Your CRTC state.

> +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;
> +};

Your display mode.

Really, you have the huge majority of those informations already
available, there's no need to duplicate it. And chances are you'll
create bugs in the process.
Maxime

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

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

* Re: [v3 5/6] drm/vs: Add hdmi driver
  2023-12-04 12:33 ` [v3 5/6] drm/vs: Add hdmi driver Keith Zhao
  2023-12-05 13:02   ` Dmitry Baryshkov
@ 2023-12-06  9:04   ` Maxime Ripard
  2023-12-06 12:02     ` Keith Zhao
  2023-12-11 17:34   ` Rob Herring
  2 siblings, 1 reply; 52+ messages in thread
From: Maxime Ripard @ 2023-12-06  9:04 UTC (permalink / raw)
  To: Keith Zhao
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	paul.walmsley, linux-kernel, dri-devel, xingyu.wu, jack.zhu,
	palmer, tzimmermann, william.qiu, shengyang.chen, linux-riscv,
	changhuang.liang

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

On Mon, Dec 04, 2023 at 08:33:14PM +0800, 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 | 849 ++++++++++++++++++++
>  drivers/gpu/drm/verisilicon/starfive_hdmi.h | 304 +++++++
>  drivers/gpu/drm/verisilicon/vs_drv.c        |   3 +
>  drivers/gpu/drm/verisilicon/vs_drv.h        |   4 +
>  6 files changed, 1169 insertions(+)
>  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 e10fa97635aa..122c786e3948 100644
> --- a/drivers/gpu/drm/verisilicon/Kconfig
> +++ b/drivers/gpu/drm/verisilicon/Kconfig
> @@ -11,3 +11,11 @@ config DRM_VERISILICON
>  	  This driver provides VeriSilicon kernel mode
>  	  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.

I'm confused, is it a starfive or verisilicon IP?

> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
> index bf6f2b7ee480..71fadafcee13 100644
> --- a/drivers/gpu/drm/verisilicon/Makefile
> +++ b/drivers/gpu/drm/verisilicon/Makefile
> @@ -6,4 +6,5 @@ vs_drm-objs := vs_dc_hw.o \
>  		vs_drv.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 000000000000..aa621db0dee0
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
> @@ -0,0 +1,849 @@
> +// 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 const char * const hdmi_clocks[] = {
> +	"sysclk",
> +	"mclk",
> +	"bclk"
> +};
> +
> +static struct starfive_hdmi_encoder *encoder_to_hdmi(struct drm_encoder *encoder)
> +{
> +	return container_of(encoder, struct starfive_hdmi_encoder, encoder);
> +}
> +
> +static struct starfive_hdmi *connector_to_hdmi(struct drm_connector *connector)
> +{
> +	return container_of(connector, struct starfive_hdmi, connector);
> +}
> +
> +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},

If you don't support modes > 340MHz, then there's no point in listing 594MHz here

> +	{ /* 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_writew(struct starfive_hdmi *hdmi, u16 offset, u32 val)
> +{
> +	writew_relaxed(val & 0xFF, hdmi->regs + (offset) * 0x04);
> +	writew_relaxed((val >> 8) & 0xFF, hdmi->regs + (offset + 1) * 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_bulk_prepare_enable(hdmi->nclks, hdmi->clk_hdmi);
> +	if (ret) {
> +		dev_err(dev, "failed to enable clocks\n");
> +		return ret;
> +	}
> +
> +	ret = reset_control_deassert(hdmi->tx_rst);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to deassert tx_rst\n");
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +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_bulk_disable_unprepare(hdmi->nclks, hdmi->clk_hdmi);
> +}
> +
> +#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->clk_hdmi[CLK_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 void starfive_hdmi_phy_get_pre_pll_cfg(struct starfive_hdmi *hdmi)
> +{
> +	if (hdmi->tmds_rate > 30000000) {
> +		hdmi->pre_cfg.pixclock = hdmi->tmds_rate;
> +		hdmi->pre_cfg.tmdsclock = hdmi->tmds_rate;
> +		hdmi->pre_cfg.prediv = 1;
> +		hdmi->pre_cfg.fbdiv = hdmi->tmds_rate / 3000000;
> +		hdmi->pre_cfg.tmds_div_a = 0;
> +		hdmi->pre_cfg.tmds_div_b = 1;
> +		hdmi->pre_cfg.tmds_div_c = 1;
> +		hdmi->pre_cfg.pclk_div_a = 1;
> +		hdmi->pre_cfg.pclk_div_b = 0;
> +		hdmi->pre_cfg.pclk_div_c = 2;
> +		hdmi->pre_cfg.pclk_div_d = 2;
> +		hdmi->pre_cfg.vco_div_5_en = hdmi->tmds_rate % 3000000 ? 1 : 0;
> +
> +		if (hdmi->pre_cfg.vco_div_5_en) {
> +			hdmi->pre_cfg.fracdiv = (hdmi->tmds_rate % 3000000) *
> +						 0xffffff / 1000000;
> +		}
> +	} else {
> +		hdmi->pre_cfg.pixclock = hdmi->tmds_rate;
> +		hdmi->pre_cfg.tmdsclock = hdmi->tmds_rate;
> +		hdmi->pre_cfg.prediv = 1;
> +		hdmi->pre_cfg.fbdiv = hdmi->tmds_rate / 1000000;
> +		hdmi->pre_cfg.tmds_div_a = 2;
> +		hdmi->pre_cfg.tmds_div_b = 1;
> +		hdmi->pre_cfg.tmds_div_c = 1;
> +		hdmi->pre_cfg.pclk_div_a = 3;
> +		hdmi->pre_cfg.pclk_div_b = 0;
> +		hdmi->pre_cfg.pclk_div_c = 3;
> +		hdmi->pre_cfg.pclk_div_d = 4;
> +		hdmi->pre_cfg.vco_div_5_en = hdmi->tmds_rate % 1000000 ? 1 : 0;
> +
> +		if (hdmi->pre_cfg.vco_div_5_en) {
> +			hdmi->pre_cfg.fracdiv = (hdmi->tmds_rate % 1000000) *
> +						 0xffffff / 1000000;
> +		}
> +	}
> +}
> +
> +static int starfive_hdmi_phy_clk_set_rate(struct starfive_hdmi *hdmi)
> +{
> +	hdmi->post_cfg = post_pll_cfg_table;
> +
> +	starfive_hdmi_phy_get_pre_pll_cfg(hdmi);
> +
> +	for (; hdmi->post_cfg->tmdsclock != 0; hdmi->post_cfg++)
> +		if (hdmi->tmds_rate <= hdmi->post_cfg->tmdsclock)
> +			break;

What happens if you don't find a mode?

> +	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_writew(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value);
> +
> +	value = mode->htotal - mode->hdisplay;
> +	hdmi_writew(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value);
> +
> +	value = mode->htotal - mode->hsync_start;
> +	hdmi_writew(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value);
> +
> +	value = mode->hsync_end - mode->hsync_start;
> +	hdmi_writew(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value);
> +
> +	value = mode->vtotal;
> +	hdmi_writew(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value);
> +
> +	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)
> +{
> +	int ret;
> +	u32 val;
> +
> +	hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
> +	hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
> +
> +	hdmi->tmds_rate = mode->clock * 1000;
> +	starfive_hdmi_phy_clk_set_rate(hdmi);

The calculation is more complicated than that, see
https://lore.kernel.org/dri-devel/20231128-kms-hdmi-connector-state-v4-13-c7602158306e@kernel.org/

> +	ret = readx_poll_timeout(readl_relaxed,
> +				 hdmi->regs + (STARFIVE_PRE_PLL_LOCK_STATUS) * 0x04,
> +				 val, val & 0x1, 1000, 100000);
> +	if (ret < 0) {
> +		dev_err(hdmi->dev, "failed to wait pre-pll lock\n");
> +		return ret;
> +	}
> +
> +	ret = readx_poll_timeout(readl_relaxed,
> +				 hdmi->regs + (STARFIVE_POST_PLL_LOCK_STATUS) * 0x04,
> +				 val, val & 0x1, 1000, 100000);
> +	if (ret < 0) {
> +		dev_err(hdmi->dev, "failed to wait post-pll lock\n");
> +		return ret;
> +	}
> +
> +	/*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_enable(struct drm_encoder *encoder)
> +{
> +	struct starfive_hdmi_encoder *hdmi_encoder = encoder_to_hdmi(encoder);
> +	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
> +	int ret, idx;
> +	struct drm_device *drm = hdmi_encoder->hdmi->connector.dev;
> +
> +	if (drm && !drm_dev_enter(drm, &idx))
> +		return;
> +
> +	ret = pm_runtime_get_sync(hdmi_encoder->hdmi->dev);
> +	if (ret < 0)
> +		return;
> +	starfive_hdmi_setup(hdmi_encoder->hdmi, mode);
> +
> +	if (drm)
> +		drm_dev_exit(idx);
> +}

You're not sending any infoframes?

> +static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
> +{
> +	struct starfive_hdmi_encoder *hdmi_encoder = encoder_to_hdmi(encoder);
> +
> +	pm_runtime_put(hdmi_encoder->hdmi->dev);
> +}
> +
> +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;
> +
> +	int pclk = mode->clock * 1000;

That's a compiler warning.

> +	if (pclk <= PIXCLOCK_4K_30FPS)
> +		valid = true;

if (pclk > PIXCLOCK_4K_30FPS)
	return -EINVAL;

?

> +
> +	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,
> +	.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);
> +	int ret = 0;
> +
> +	if (!hdmi->ddc)
> +		return 0;
> +	ret = pm_runtime_get_sync(hdmi->dev);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = drm_connector_helper_get_modes_from_ddc(connector);
> +	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)
> +{
> +	int pclk = mode->clock * 1000;
> +	bool valid = false;
> +
> +	if (pclk <= PIXCLOCK_4K_30FPS)
> +		valid = true;
> +
> +	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_encoder *hdmi_encoder)
> +{
> +	struct drm_encoder *encoder = &hdmi_encoder->encoder;
> +	struct device *dev = hdmi_encoder->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_encoder->hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> +
> +	drm_connector_helper_add(&hdmi_encoder->hdmi->connector,
> +				 &starfive_hdmi_connector_helper_funcs);
> +	drmm_connector_init(drm, &hdmi_encoder->hdmi->connector,
> +			    &starfive_hdmi_connector_funcs,
> +			    DRM_MODE_CONNECTOR_HDMIA,
> +			    hdmi_encoder->hdmi->ddc);
> +
> +	drm_connector_attach_encoder(&hdmi_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 = devm_kzalloc(hdmi->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) {
> +		dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
> +		devm_kfree(hdmi->dev, i2c);
> +		return ERR_PTR(ret);
> +	}
> +
> +	hdmi->i2c = i2c;
> +
> +	DRM_DEV_INFO(hdmi->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)
> +{
> +	int ret;
> +
> +	hdmi->nclks = ARRAY_SIZE(hdmi->clk_hdmi);
> +	for (int i = 0; i < hdmi->nclks; ++i)
> +		hdmi->clk_hdmi[i].id = hdmi_clocks[i];
> +
> +	ret = devm_clk_bulk_get(dev, hdmi->nclks, hdmi->clk_hdmi);
> +	if (ret) {
> +		dev_err(dev, "Failed to get clk controls\n");
> +		return ret;
> +	}
> +
> +	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 drm_device *drm = dev_get_drvdata(master);
> +	struct starfive_hdmi_encoder *hdmi_encoder;
> +	int ret;
> +
> +	hdmi_encoder = drmm_simple_encoder_alloc(drm, struct starfive_hdmi_encoder,
> +						 encoder, DRM_MODE_ENCODER_TMDS);
> +	if (IS_ERR(hdmi_encoder))
> +		return PTR_ERR(hdmi_encoder);
> +
> +	hdmi_encoder->hdmi = dev_get_drvdata(dev);
> +	hdmi_encoder->hdmi->drm_dev = drm;
> +
> +	ret = pm_runtime_resume_and_get(dev);
> +	if (ret)
> +		return ret;
> +
> +	starfive_hdmi_i2c_init(hdmi_encoder->hdmi);
> +
> +	ret = starfive_hdmi_register(drm, hdmi_encoder);
> +	if (ret)
> +		goto err_put_adapter;
> +
> +	/* Unmute hotplug interrupt */
> +	hdmi_modb(hdmi_encoder->hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
> +
> +	ret = devm_request_threaded_irq(dev, hdmi_encoder->hdmi->irq, starfive_hdmi_hardirq,
> +					starfive_hdmi_irq, IRQF_SHARED,
> +					dev_name(dev), hdmi_encoder->hdmi);
> +	if (ret < 0)
> +		goto err_put_adapter;
> +
> +	pm_runtime_put_sync(dev);
> +
> +	return 0;
> +
> +err_put_adapter:
> +	i2c_put_adapter(hdmi_encoder->hdmi->ddc);
> +	return ret;
> +}
> +
> +static const struct component_ops starfive_hdmi_ops = {
> +	.bind	= starfive_hdmi_bind,
> +};
> +
> +static int starfive_hdmi_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +	struct starfive_hdmi *hdmi;
> +	struct resource *iores;
> +
> +	hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
> +	if (!hdmi)
> +		return -ENOMEM;
> +
> +	dev_set_drvdata(&pdev->dev, hdmi);
> +	hdmi->dev = &pdev->dev;
> +
> +	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	hdmi->regs = devm_ioremap_resource(hdmi->dev, iores);
> +	if (IS_ERR(hdmi->regs))
> +		return PTR_ERR(hdmi->regs);
> +
> +	ret = starfive_hdmi_get_clk_rst(hdmi->dev, hdmi);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = devm_pm_runtime_enable(hdmi->dev);
> +	if (ret)
> +		return ret;
> +
> +	hdmi->irq = platform_get_irq(pdev, 0);
> +	if (hdmi->irq < 0) {
> +		ret = hdmi->irq;
> +		return ret;
> +	}
> +
> +	hdmi->ddc = starfive_hdmi_i2c_adapter(hdmi);
> +	if (IS_ERR(hdmi->ddc)) {
> +		ret = PTR_ERR(hdmi->ddc);
> +		hdmi->ddc = NULL;
> +		return ret;
> +	}
> +
> +	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",},

So it's inno hdmi, just like Rockchip then?

This should be a common driver.
Maxime

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

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

* Re: [v3 5/6] drm/vs: Add hdmi driver
  2023-12-06  9:04   ` Maxime Ripard
@ 2023-12-06 12:02     ` Keith Zhao
  2023-12-06 12:56       ` Maxime Ripard
  0 siblings, 1 reply; 52+ messages in thread
From: Keith Zhao @ 2023-12-06 12:02 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	paul.walmsley, linux-kernel, dri-devel, Xingyu Wu, Jack Zhu,
	palmer, tzimmermann, William Qiu, Shengyang Chen, linux-riscv,
	Changhuang Liang



On 2023/12/6 17:04, Maxime Ripard wrote:
> On Mon, Dec 04, 2023 at 08:33:14PM +0800, 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 | 849 ++++++++++++++++++++
>>  drivers/gpu/drm/verisilicon/starfive_hdmi.h | 304 +++++++
>>  drivers/gpu/drm/verisilicon/vs_drv.c        |   3 +
>>  drivers/gpu/drm/verisilicon/vs_drv.h        |   4 +
>>  6 files changed, 1169 insertions(+)
>>  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 e10fa97635aa..122c786e3948 100644
>> --- a/drivers/gpu/drm/verisilicon/Kconfig
>> +++ b/drivers/gpu/drm/verisilicon/Kconfig
>> @@ -11,3 +11,11 @@ config DRM_VERISILICON
>>  	  This driver provides VeriSilicon kernel mode
>>  	  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.
> 
> I'm confused, is it a starfive or verisilicon IP?
it is Innosilicon ip ,I need describe it clearer in my next.
> 
>> diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
>> index bf6f2b7ee480..71fadafcee13 100644
>> --- a/drivers/gpu/drm/verisilicon/Makefile
>> +++ b/drivers/gpu/drm/verisilicon/Makefile
>> @@ -6,4 +6,5 @@ vs_drm-objs := vs_dc_hw.o \
>>  		vs_drv.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 000000000000..aa621db0dee0
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
>> @@ -0,0 +1,849 @@
>> +// 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 const char * const hdmi_clocks[] = {
>> +	"sysclk",
>> +	"mclk",
>> +	"bclk"
>> +};
>> +
>> +static struct starfive_hdmi_encoder *encoder_to_hdmi(struct drm_encoder *encoder)
>> +{
>> +	return container_of(encoder, struct starfive_hdmi_encoder, encoder);
>> +}
>> +
>> +static struct starfive_hdmi *connector_to_hdmi(struct drm_connector *connector)
>> +{
>> +	return container_of(connector, struct starfive_hdmi, connector);
>> +}
>> +
>> +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},
> 
> If you don't support modes > 340MHz, then there's no point in listing 594MHz here
sure . max should be 297M here.
> 
>> +	{ /* 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_writew(struct starfive_hdmi *hdmi, u16 offset, u32 val)
>> +{
>> +	writew_relaxed(val & 0xFF, hdmi->regs + (offset) * 0x04);
>> +	writew_relaxed((val >> 8) & 0xFF, hdmi->regs + (offset + 1) * 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_bulk_prepare_enable(hdmi->nclks, hdmi->clk_hdmi);
>> +	if (ret) {
>> +		dev_err(dev, "failed to enable clocks\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = reset_control_deassert(hdmi->tx_rst);
>> +	if (ret < 0) {
>> +		dev_err(dev, "failed to deassert tx_rst\n");
>> +		return ret;
>> +	}
>> +	return 0;
>> +}
>> +
>> +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_bulk_disable_unprepare(hdmi->nclks, hdmi->clk_hdmi);
>> +}
>> +
>> +#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->clk_hdmi[CLK_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 void starfive_hdmi_phy_get_pre_pll_cfg(struct starfive_hdmi *hdmi)
>> +{
>> +	if (hdmi->tmds_rate > 30000000) {
>> +		hdmi->pre_cfg.pixclock = hdmi->tmds_rate;
>> +		hdmi->pre_cfg.tmdsclock = hdmi->tmds_rate;
>> +		hdmi->pre_cfg.prediv = 1;
>> +		hdmi->pre_cfg.fbdiv = hdmi->tmds_rate / 3000000;
>> +		hdmi->pre_cfg.tmds_div_a = 0;
>> +		hdmi->pre_cfg.tmds_div_b = 1;
>> +		hdmi->pre_cfg.tmds_div_c = 1;
>> +		hdmi->pre_cfg.pclk_div_a = 1;
>> +		hdmi->pre_cfg.pclk_div_b = 0;
>> +		hdmi->pre_cfg.pclk_div_c = 2;
>> +		hdmi->pre_cfg.pclk_div_d = 2;
>> +		hdmi->pre_cfg.vco_div_5_en = hdmi->tmds_rate % 3000000 ? 1 : 0;
>> +
>> +		if (hdmi->pre_cfg.vco_div_5_en) {
>> +			hdmi->pre_cfg.fracdiv = (hdmi->tmds_rate % 3000000) *
>> +						 0xffffff / 1000000;
>> +		}
>> +	} else {
>> +		hdmi->pre_cfg.pixclock = hdmi->tmds_rate;
>> +		hdmi->pre_cfg.tmdsclock = hdmi->tmds_rate;
>> +		hdmi->pre_cfg.prediv = 1;
>> +		hdmi->pre_cfg.fbdiv = hdmi->tmds_rate / 1000000;
>> +		hdmi->pre_cfg.tmds_div_a = 2;
>> +		hdmi->pre_cfg.tmds_div_b = 1;
>> +		hdmi->pre_cfg.tmds_div_c = 1;
>> +		hdmi->pre_cfg.pclk_div_a = 3;
>> +		hdmi->pre_cfg.pclk_div_b = 0;
>> +		hdmi->pre_cfg.pclk_div_c = 3;
>> +		hdmi->pre_cfg.pclk_div_d = 4;
>> +		hdmi->pre_cfg.vco_div_5_en = hdmi->tmds_rate % 1000000 ? 1 : 0;
>> +
>> +		if (hdmi->pre_cfg.vco_div_5_en) {
>> +			hdmi->pre_cfg.fracdiv = (hdmi->tmds_rate % 1000000) *
>> +						 0xffffff / 1000000;
>> +		}
>> +	}
>> +}
>> +
>> +static int starfive_hdmi_phy_clk_set_rate(struct starfive_hdmi *hdmi)
>> +{
>> +	hdmi->post_cfg = post_pll_cfg_table;
>> +
>> +	starfive_hdmi_phy_get_pre_pll_cfg(hdmi);
>> +
>> +	for (; hdmi->post_cfg->tmdsclock != 0; hdmi->post_cfg++)
>> +		if (hdmi->tmds_rate <= hdmi->post_cfg->tmdsclock)
>> +			break;
> 
> What happens if you don't find a mode?
hdmi->tmds_rate  max value is 297M and hdmi->tmds_rate table also 297M (after delete 594M)
it can find a mode always.
> 
>> +	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_writew(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value);
>> +
>> +	value = mode->htotal - mode->hdisplay;
>> +	hdmi_writew(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value);
>> +
>> +	value = mode->htotal - mode->hsync_start;
>> +	hdmi_writew(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value);
>> +
>> +	value = mode->hsync_end - mode->hsync_start;
>> +	hdmi_writew(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value);
>> +
>> +	value = mode->vtotal;
>> +	hdmi_writew(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value);
>> +
>> +	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)
>> +{
>> +	int ret;
>> +	u32 val;
>> +
>> +	hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE);
>> +	hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE);
>> +
>> +	hdmi->tmds_rate = mode->clock * 1000;
>> +	starfive_hdmi_phy_clk_set_rate(hdmi);
> 
> The calculation is more complicated than that, see
> https://lore.kernel.org/dri-devel/20231128-kms-hdmi-connector-state-v4-13-c7602158306e@kernel.org/
drm_connector_hdmi_compute_mode_clock is a good helper to update my modeclock,
I will match it accordding used hdmi_colorspace
> 
>> +	ret = readx_poll_timeout(readl_relaxed,
>> +				 hdmi->regs + (STARFIVE_PRE_PLL_LOCK_STATUS) * 0x04,
>> +				 val, val & 0x1, 1000, 100000);
>> +	if (ret < 0) {
>> +		dev_err(hdmi->dev, "failed to wait pre-pll lock\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = readx_poll_timeout(readl_relaxed,
>> +				 hdmi->regs + (STARFIVE_POST_PLL_LOCK_STATUS) * 0x04,
>> +				 val, val & 0x1, 1000, 100000);
>> +	if (ret < 0) {
>> +		dev_err(hdmi->dev, "failed to wait post-pll lock\n");
>> +		return ret;
>> +	}
>> +
>> +	/*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_enable(struct drm_encoder *encoder)
>> +{
>> +	struct starfive_hdmi_encoder *hdmi_encoder = encoder_to_hdmi(encoder);
>> +	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
>> +	int ret, idx;
>> +	struct drm_device *drm = hdmi_encoder->hdmi->connector.dev;
>> +
>> +	if (drm && !drm_dev_enter(drm, &idx))
>> +		return;
>> +
>> +	ret = pm_runtime_get_sync(hdmi_encoder->hdmi->dev);
>> +	if (ret < 0)
>> +		return;
>> +	starfive_hdmi_setup(hdmi_encoder->hdmi, mode);
>> +
>> +	if (drm)
>> +		drm_dev_exit(idx);
>> +}
> 
> You're not sending any infoframes?
there is 2 way to modeset the hdmi driver
1、send infoframes to hdmi display , and config the vic to the hardware , 
   no need to care the mode timing parameters,
   but the hareware support vic list:
   01/02/04/05/06/16/17/19/31/32/93/94/95/96/97/98/100/101/102
   so final the plan was abandoned

2、get mode timing parameters and config them to hardware
   no need care the vic value, this will support most of resolutions,so no sending any infoframe.

Considering the integrity of the HDMI protocol, is this send infoframes necessary?
> 
>> +static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder)
>> +{
>> +	struct starfive_hdmi_encoder *hdmi_encoder = encoder_to_hdmi(encoder);
>> +
>> +	pm_runtime_put(hdmi_encoder->hdmi->dev);
>> +}
>> +
>> +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;
>> +
>> +	int pclk = mode->clock * 1000;
> 
> That's a compiler warning.
> 
>> +	if (pclk <= PIXCLOCK_4K_30FPS)
>> +		valid = true;
> 
> if (pclk > PIXCLOCK_4K_30FPS)
> 	return -EINVAL;
> 
> ?
en , it seems clearer.
> 
>> +
>> +	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,
>> +	.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);
>> +	int ret = 0;
>> +
>> +	if (!hdmi->ddc)
>> +		return 0;
>> +	ret = pm_runtime_get_sync(hdmi->dev);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = drm_connector_helper_get_modes_from_ddc(connector);
>> +	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)
>> +{
>> +	int pclk = mode->clock * 1000;
>> +	bool valid = false;
>> +
>> +	if (pclk <= PIXCLOCK_4K_30FPS)
>> +		valid = true;
>> +
>> +	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_encoder *hdmi_encoder)
>> +{
>> +	struct drm_encoder *encoder = &hdmi_encoder->encoder;
>> +	struct device *dev = hdmi_encoder->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_encoder->hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
>> +
>> +	drm_connector_helper_add(&hdmi_encoder->hdmi->connector,
>> +				 &starfive_hdmi_connector_helper_funcs);
>> +	drmm_connector_init(drm, &hdmi_encoder->hdmi->connector,
>> +			    &starfive_hdmi_connector_funcs,
>> +			    DRM_MODE_CONNECTOR_HDMIA,
>> +			    hdmi_encoder->hdmi->ddc);
>> +
>> +	drm_connector_attach_encoder(&hdmi_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 = devm_kzalloc(hdmi->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) {
>> +		dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
>> +		devm_kfree(hdmi->dev, i2c);
>> +		return ERR_PTR(ret);
>> +	}
>> +
>> +	hdmi->i2c = i2c;
>> +
>> +	DRM_DEV_INFO(hdmi->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)
>> +{
>> +	int ret;
>> +
>> +	hdmi->nclks = ARRAY_SIZE(hdmi->clk_hdmi);
>> +	for (int i = 0; i < hdmi->nclks; ++i)
>> +		hdmi->clk_hdmi[i].id = hdmi_clocks[i];
>> +
>> +	ret = devm_clk_bulk_get(dev, hdmi->nclks, hdmi->clk_hdmi);
>> +	if (ret) {
>> +		dev_err(dev, "Failed to get clk controls\n");
>> +		return ret;
>> +	}
>> +
>> +	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 drm_device *drm = dev_get_drvdata(master);
>> +	struct starfive_hdmi_encoder *hdmi_encoder;
>> +	int ret;
>> +
>> +	hdmi_encoder = drmm_simple_encoder_alloc(drm, struct starfive_hdmi_encoder,
>> +						 encoder, DRM_MODE_ENCODER_TMDS);
>> +	if (IS_ERR(hdmi_encoder))
>> +		return PTR_ERR(hdmi_encoder);
>> +
>> +	hdmi_encoder->hdmi = dev_get_drvdata(dev);
>> +	hdmi_encoder->hdmi->drm_dev = drm;
>> +
>> +	ret = pm_runtime_resume_and_get(dev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	starfive_hdmi_i2c_init(hdmi_encoder->hdmi);
>> +
>> +	ret = starfive_hdmi_register(drm, hdmi_encoder);
>> +	if (ret)
>> +		goto err_put_adapter;
>> +
>> +	/* Unmute hotplug interrupt */
>> +	hdmi_modb(hdmi_encoder->hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
>> +
>> +	ret = devm_request_threaded_irq(dev, hdmi_encoder->hdmi->irq, starfive_hdmi_hardirq,
>> +					starfive_hdmi_irq, IRQF_SHARED,
>> +					dev_name(dev), hdmi_encoder->hdmi);
>> +	if (ret < 0)
>> +		goto err_put_adapter;
>> +
>> +	pm_runtime_put_sync(dev);
>> +
>> +	return 0;
>> +
>> +err_put_adapter:
>> +	i2c_put_adapter(hdmi_encoder->hdmi->ddc);
>> +	return ret;
>> +}
>> +
>> +static const struct component_ops starfive_hdmi_ops = {
>> +	.bind	= starfive_hdmi_bind,
>> +};
>> +
>> +static int starfive_hdmi_probe(struct platform_device *pdev)
>> +{
>> +	int ret;
>> +	struct starfive_hdmi *hdmi;
>> +	struct resource *iores;
>> +
>> +	hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
>> +	if (!hdmi)
>> +		return -ENOMEM;
>> +
>> +	dev_set_drvdata(&pdev->dev, hdmi);
>> +	hdmi->dev = &pdev->dev;
>> +
>> +	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	hdmi->regs = devm_ioremap_resource(hdmi->dev, iores);
>> +	if (IS_ERR(hdmi->regs))
>> +		return PTR_ERR(hdmi->regs);
>> +
>> +	ret = starfive_hdmi_get_clk_rst(hdmi->dev, hdmi);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = devm_pm_runtime_enable(hdmi->dev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	hdmi->irq = platform_get_irq(pdev, 0);
>> +	if (hdmi->irq < 0) {
>> +		ret = hdmi->irq;
>> +		return ret;
>> +	}
>> +
>> +	hdmi->ddc = starfive_hdmi_i2c_adapter(hdmi);
>> +	if (IS_ERR(hdmi->ddc)) {
>> +		ret = PTR_ERR(hdmi->ddc);
>> +		hdmi->ddc = NULL;
>> +		return ret;
>> +	}
>> +
>> +	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",},
> 
> So it's inno hdmi, just like Rockchip then?
> 
> This should be a common driver.
Rockchip has a inno hdmi IP. and Starfive has a inno hdmi IP.
but the harewawre difference of them is big , it is not easy to use the common driver
maybe i need the inno hdmi version here to make a distinction

best regards
Keith
 
> Maxime

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

* Re: [v3 1/6] dt-bindings: display: Add yamls for JH7110 display system
  2023-12-04 12:33 ` [v3 1/6] dt-bindings: display: Add yamls for JH7110 display system Keith Zhao
  2023-12-05  6:59   ` Krzysztof Kozlowski
@ 2023-12-06 12:50   ` Sui Jingfeng
  2023-12-08 16:33     ` Rob Herring
  2023-12-08 16:31   ` Rob Herring
  2 siblings, 1 reply; 52+ messages in thread
From: Sui Jingfeng @ 2023-12-06 12:50 UTC (permalink / raw)
  To: Keith Zhao, devicetree, dri-devel, linux-kernel, linux-riscv
  Cc: aou, krzysztof.kozlowski+dt, william.qiu, mripard, xingyu.wu,
	jack.zhu, palmer, tzimmermann, paul.walmsley, shengyang.chen,
	changhuang.liang

Hi,


On 2023/12/4 20:33, Keith Zhao wrote:
> StarFive SoCs JH7110 display system:
> dc controller, hdmi controller,
> encoder, vout syscon.
>
> add the path of yaml file in MAINTAINERS
>
> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> ---
>   .../starfive/starfive,display-subsystem.yaml  | 104 ++++++++++++++++
>   .../starfive/starfive,dsi-encoder.yaml        |  92 ++++++++++++++
>   .../starfive/starfive,jh7110-dc8200.yaml      | 113 ++++++++++++++++++
>   .../starfive/starfive,jh7110-inno-hdmi.yaml   |  82 +++++++++++++
>   .../soc/starfive/starfive,jh7110-syscon.yaml  |   1 +
>   MAINTAINERS                                   |   7 ++
>   6 files changed, 399 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
>   create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,dsi-encoder.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 000000000000..d5ebdba3fb36
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml
> @@ -0,0 +1,104 @@
> +# 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 JH7110 Soc Display SubSystem
> +
> +maintainers:
> +  - Keith Zhao <keith.zhao@starfivetech.com>
> +  - ShengYang Chen <shengyang.chen@starfivetech.com>
> +
> +description:
> +  This is the bindings documentation for the JH7110 Soc Display Subsystem that
> +  includes front-end video data capture, display controller and display
> +  interface. such as HDMI and MIPI.
> +
> +  JH7110 display pipeline have several components as below description,
> +  multi display controllers and corresponding physical interfaces.
> +  For different display scenarios, pipe0 and pipe1 maybe binding to different
> +  encoder. for example,
> +
> +  pipe0 binding to HDMI for primary display,
> +  pipe1 binding to DSI for external display.
> +
> +          +------------------------------+
> +          |                              |
> +          |                              |
> +  +----+  |   +-------------------+      |   +-------+   +------+   +------+
> +  |    +----->+  dc controller 0  +--->----->+HDMICtl| ->+ PHY  +-->+PANEL0+
> +  |AXI |  |   +-------------------+      |   +-------+   +------+   +------+
> +  |    |  |                              |
> +  |    |  |                              |
> +  |    |  |                              |
> +  |    |  |                              |
> +  |APB |  |   +-------------------+         +---------+    +------+  +-------+
> +  |    +----->+  dc controller 1  +--->---->+ dsiTx   +--->+DPHY  +->+ PANEL1+
> +  |    |  |   +-------------------+         +---------+    +------+  +-------+
> +  +----+  |                              |
> +          +------------------------------+
> +
> +
> +properties:
> +  compatible:
> +    const: starfive,display-subsystem
> +
> +  clocks:
> +    items:
> +      - description: Clock for display system noc bus.
> +      - 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.
> +
> +  clock-names:
> +    items:
> +      - const: noc_bus
> +      - const: dc_core
> +      - const: axi_core
> +      - const: ahb
> +
> +  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
> +
> +  ports:
> +    $ref: /schemas/types.yaml#/definitions/phandle-array
> +    items:
> +      maxItems: 1
> +    description:
> +      Should contain a list of phandles pointing to display interface port
> +      of dc-controller devices.
> +
> +required:
> +  - compatible
> +  - clocks
> +  - clock-names
> +  - resets
> +  - reset-names
> +  - ports
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    display-subsystem {
> +        compatible = "starfive,display-subsystem";
> +        ports = <&dc_out>;
> +
> +        clocks = <&syscrg 60>,
> +               <&voutcrg 4>,
> +               <&voutcrg 5>,
> +               <&voutcrg 6>;
> +        clock-names = "noc_bus", "dc_core", "axi_core", "ahb";
> +        resets = <&voutcrg 0>, <&voutcrg 1>, <&voutcrg 2>;
> +        reset-names = "axi", "ahb", "core";
> +    };
> diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,dsi-encoder.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,dsi-encoder.yaml
> new file mode 100644
> index 000000000000..2cc0ad8e65ba
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,dsi-encoder.yaml
> @@ -0,0 +1,92 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/starfive/starfive,dsi-encoder.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: starfive jh7110 soc Encoder
> +
> +maintainers:
> +  - Keith Zhao <keith.zhao@starfivetech.com>
> +
> +description:
> +  Device-Tree bindings for simple encoder.
> +  Simple encoder driver only has basic functionality.
> +  the hardware of dc8200 has 2 output interface, use
> +  syscon to select which one to be used.
> +
> +properties:
> +  compatible:
> +    const: starfive,dsi-encoder
> +
> +  starfive,syscon:
> +    $ref: /schemas/types.yaml#/definitions/phandle-array
> +    items:
> +      - items:
> +          - description: phandle to syscon that select crtc output.
> +          - description: Offset of crtc selection
> +          - description: Shift of crtc selection
> +    description:
> +      A phandle to syscon with two arguments that configure select output.
> +      The argument one is the offset of crtc selection, the
> +      argument two is the shift of crtc selection.
> +
> +  ports:
> +    $ref: /schemas/graph.yaml#/properties/ports
> +
> +    properties:
> +      port@0:
> +        $ref: /schemas/graph.yaml#/properties/port
> +        description:
> +          The first port should be the input coming from the associated dc channel.
> +
> +      port@1:
> +        $ref: /schemas/graph.yaml#/properties/port
> +        description:
> +          The second port should be the output coming from the associated bridge.
> +
> +    required:
> +      - port@0
> +      - port@1
> +
> +required:
> +  - compatible
> +  - ports
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +        dssctrl: dssctrl@295b0000 {
> +            compatible = "starfive,jh7110-vout-syscon", "syscon";
> +            reg = <0x295b0000 0x90>;
> +        };
> +
> +        dsi_encoder: dsi_encoder {
> +            compatible = "starfive,dsi-encoder";
> +            starfive,syscon = <&dssctrl 0x8 0x12>;
> +
> +            ports {
> +                #address-cells = <1>;
> +                #size-cells = <0>;
> +                /* input */
> +                port@0 {
> +                    #address-cells = <1>;
> +                    #size-cells = <0>;
> +                    reg = <0>;
> +                    dsi_input0:endpoint@0 {
> +                        reg = <0>;
> +                        remote-endpoint = <&dc_out_dpi1>;
> +                    };
> +                };
> +                /* output */
> +                port@1 {
> +                    reg = <1>;
> +                    #address-cells = <1>;
> +                    #size-cells = <0>;
> +                    dsi_out:endpoint {
> +                        remote-endpoint = <&mipi_in>;
> +                    };
> +                };
> +            };
> +        };
> 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 000000000000..0b083effec02
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml
> @@ -0,0 +1,113 @@
> +# 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: Pixel clock for display channel 0.
> +      - description: Pixel clock for display channel 1.
> +      - description: Pixel clock from hdmi.
> +      - description: Pixel clock for soc .
> +  clock-names:
> +    items:
> +      - const: channel0
> +      - const: channel1
> +      - const: hdmi_tx
> +      - const: dc_parent
> +  ports:
> +    $ref: /schemas/graph.yaml#/properties/ports
> +
> +    properties:
> +      port@0:
> +        $ref: /schemas/graph.yaml#/properties/port
> +        description:
> +          channel 0 output
> +
> +      port@1:
> +        $ref: /schemas/graph.yaml#/properties/port
> +        description:
> +          channel 1 output
> +
> +    required:
> +      - port@0
> +      - port@1
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - clocks
> +  - clock-names
> +  - ports
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    dc8200: lcd-controller@29400000 {
> +        compatible = "starfive,jh7110-dc8200";
> +        reg = <0x29400000 0x100>, <0x29400800 0x2000>;
> +        interrupts = <95>;
> +        clocks = <&voutcrg 7>,
> +               <&voutcrg 8>,
> +               <&voutcrg 9>,
> +               <&voutcrg 10>;
> +        clock-names = "channel0", "channel1","hdmi_tx", "dc_parent";
> +
> +        crtc_out: ports {
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +
> +            dc_out0: port@0 {
> +                reg = <0>;
> +                #address-cells = <1>;
> +                #size-cells = <0>;
> +
> +                dc_out_dpi0: endpoint@0 {
> +                    reg = <0>;
> +                    remote-endpoint = <&hdmi_in_dc>;
> +                };
> +
> +            };
> +
> +            dc_out1: port@1 {
> +                reg = <1>;
> +                #address-cells = <1>;
> +                #size-cells = <0>;
> +
> +                dc_out_dpi1: endpoint@1 {
> +                    reg = <1>;
> +                    remote-endpoint = <&dsi_input0>;
> +                };
> +
> +            };
> +          };
> +    };
> 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 000000000000..3640d97ab789
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
> @@ -0,0 +1,82 @@
> +# 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

'transmiter' -> 'transmitter'


> +  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.
> +
> +  clock-names:
> +    items:
> +      - const: sysclk
> +      - const: mclk
> +      - const: bclk
> +
> +  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>;
> +        clock-names = "sysclk", "mclk","bclk";
> +        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/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml b/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml
> index 0039319e91fe..cf9b657d0e8a 100644
> --- a/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml
> +++ b/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml
> @@ -24,6 +24,7 @@ properties:
>             - enum:
>                 - starfive,jh7110-aon-syscon
>                 - starfive,jh7110-stg-syscon
> +              - starfive,jh7110-vout-syscon
>             - const: syscon
>   
>     reg:
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 7b151710e8c5..7caaadb83f3f 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6881,6 +6881,13 @@ T:	git git://anongit.freedesktop.org/drm/drm-misc
>   F:	Documentation/devicetree/bindings/display/ste,mcde.yaml
>   F:	drivers/gpu/drm/mcde/
>   
> +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 TI DLPC3433 MIPI DSI TO DMD BRIDGE
>   M:	Jagan Teki <jagan@amarulasolutions.com>
>   S:	Maintained

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

* Re: [v3 4/6] drm/vs: Add KMS crtc&plane
  2023-12-06  8:55   ` Maxime Ripard
@ 2023-12-06 12:53     ` Keith Zhao
  2024-01-31  8:57     ` 回复: " Keith Zhao
  2024-01-31  9:33     ` Keith Zhao
  2 siblings, 0 replies; 52+ messages in thread
From: Keith Zhao @ 2023-12-06 12:53 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	paul.walmsley, linux-kernel, dri-devel, Xingyu Wu, Jack Zhu,
	palmer, tzimmermann, William Qiu, Shengyang Chen, linux-riscv,
	Changhuang Liang



On 2023/12/6 16:55, Maxime Ripard wrote:
> On Mon, Dec 04, 2023 at 08:33:13PM +0800, Keith Zhao wrote:
>> +static const struct vs_plane_info dc_hw_planes_rev0[PLANE_NUM] = {
>> +	{
>> +		.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,
>> +	},
>> +};
>> +
>> +static const struct vs_dc_info dc8200_info = {
>> +	.name			= "DC8200",
>> +	.panel_num		= 2,
>> +	.plane_num		= 8,
>> +	.planes			= dc_hw_planes_rev0,
>> +	.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,
>> +};
> 
> I really think that entire thing is to workaround a suboptimal device
> tree binding. You should have two CRTCs in the device tree, you'll probe
> twice, and you won't get to do that whole dance.
> 

i got you means “two CRTCs in the device tree,probe twice"
maybe also I can split the common ctrc helper fun into 2 , 

current the driver probe once and map 2 port as ctrc node ,

> 
>> +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;
>> +};
> 
> That's your plane state.
> 
>> +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;
>> +};
> 
> Your framebuffer
> 
>> +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;
>> +};
> 
> Your CRTC state.
> 
>> +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;
>> +};
> 
> Your display mode.
> 
> Really, you have the huge majority of those informations already
> available, there's no need to duplicate it. And chances are you'll
> create bugs in the process.
> Maxime
ok let me try it, 
This will be another relatively big change.


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

* Re: [v3 5/6] drm/vs: Add hdmi driver
  2023-12-06 12:02     ` Keith Zhao
@ 2023-12-06 12:56       ` Maxime Ripard
  2023-12-06 14:11         ` Keith Zhao
  0 siblings, 1 reply; 52+ messages in thread
From: Maxime Ripard @ 2023-12-06 12:56 UTC (permalink / raw)
  To: Keith Zhao
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	paul.walmsley, linux-kernel, dri-devel, Xingyu Wu, Jack Zhu,
	palmer, tzimmermann, William Qiu, Shengyang Chen, linux-riscv,
	Changhuang Liang

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

On Wed, Dec 06, 2023 at 08:02:55PM +0800, Keith Zhao wrote:
> >> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
> >> +	{ .compatible = "starfive,jh7110-inno-hdmi",},
> > 
> > So it's inno hdmi, just like Rockchip then?
> > 
> > This should be a common driver.
>
> Rockchip has a inno hdmi IP. and Starfive has a inno hdmi IP.
> but the harewawre difference of them is big , it is not easy to use the common driver
> maybe i need the inno hdmi version here to make a distinction

I just had a look at the rockchip header file: all the registers but the
STARFIVE_* ones are identical.

There's no need to have two identical drivers then, please use the
rockchip driver instead.

Maxime

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

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

* Re: [v3 5/6] drm/vs: Add hdmi driver
  2023-12-06 12:56       ` Maxime Ripard
@ 2023-12-06 14:11         ` Keith Zhao
  2023-12-07  9:02           ` Andy Yan
  0 siblings, 1 reply; 52+ messages in thread
From: Keith Zhao @ 2023-12-06 14:11 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	paul.walmsley, linux-kernel, dri-devel, Xingyu Wu, Jack Zhu,
	palmer, tzimmermann, William Qiu, Shengyang Chen, linux-riscv,
	Changhuang Liang



On 2023/12/6 20:56, Maxime Ripard wrote:
> On Wed, Dec 06, 2023 at 08:02:55PM +0800, Keith Zhao wrote:
>> >> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
>> >> +	{ .compatible = "starfive,jh7110-inno-hdmi",},
>> > 
>> > So it's inno hdmi, just like Rockchip then?
>> > 
>> > This should be a common driver.
>>
>> Rockchip has a inno hdmi IP. and Starfive has a inno hdmi IP.
>> but the harewawre difference of them is big , it is not easy to use the common driver
>> maybe i need the inno hdmi version here to make a distinction
> 
> I just had a look at the rockchip header file: all the registers but the
> STARFIVE_* ones are identical.
> 
> There's no need to have two identical drivers then, please use the
> rockchip driver instead.
> 
> Maxime

ok, have a simple test , edid can get . i will continue 


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

* Re: [v3 4/6] drm/vs: Add KMS crtc&plane
  2023-12-04 12:33 ` [v3 4/6] drm/vs: Add KMS crtc&plane Keith Zhao
  2023-12-05 12:48   ` Dmitry Baryshkov
  2023-12-06  8:55   ` Maxime Ripard
@ 2023-12-07  8:41   ` Icenowy Zheng
  2023-12-07 11:31     ` Keith Zhao
  2 siblings, 1 reply; 52+ messages in thread
From: Icenowy Zheng @ 2023-12-07  8:41 UTC (permalink / raw)
  To: Keith Zhao, devicetree, dri-devel, linux-kernel, linux-riscv
  Cc: aou, suijingfeng, krzysztof.kozlowski+dt, william.qiu, mripard,
	xingyu.wu, jack.zhu, palmer, tzimmermann, paul.walmsley,
	shengyang.chen, changhuang.liang

在 2023-12-04星期一的 20:33 +0800,Keith Zhao写道:
*snip*

> +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;

From my experiments on poking with registers on T-Head TH1520 (also
uses DC8200 display controller and a similar driver), the DC8200
hardware have a different definition of cursor position X and Y with
the CRTC plane state.

For CRTC plane state, hot_x and hot_y are only provided as reference,
and the cursor should be displayed with its (0,0) drawn to (crtc_x,
crtc_y) ([XY]_crtc are values specified in CRTC state, the right part
of the assignments here), when the cursor is moved to (0,0) but the hot
point is not (0,0), it could be negative.

However, for DC8200 registers definition, cursor XY position could not
be negative -- the cursor will disappear then; because in its
definition, the cursor XY position should be where the cursor is
pointing to, instead of its (0,0). DC8200 will draw (0,0) of the cursor
to (x - hot_x, y - hot_y). So to met the expectation of the KMS plane
settings, the DC8200 position should be set to (crtc_x + hot_x, crtc_y
+ hot_y) instead. Thus these two lines of code should be:

```
        cursor.x = state->crtc_x + drm_fb->hot_x;
        cursor.y = state->crtc_y + drm_fb->hot_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);
> +}
*snip

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

* Re:Re: [v3 5/6] drm/vs: Add hdmi driver
  2023-12-06 14:11         ` Keith Zhao
@ 2023-12-07  9:02           ` Andy Yan
  2023-12-07 10:48             ` Keith Zhao
  0 siblings, 1 reply; 52+ messages in thread
From: Andy Yan @ 2023-12-07  9:02 UTC (permalink / raw)
  To: Keith Zhao
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	paul.walmsley, linux-kernel, dri-devel, Xingyu Wu, Jack Zhu,
	palmer, Maxime Ripard, tzimmermann, William Qiu, Shengyang Chen,
	linux-riscv, Changhuang Liang





Hi Keith:











At 2023-12-06 22:11:33, "Keith Zhao" <keith.zhao@starfivetech.com> wrote:
>
>
>On 2023/12/6 20:56, Maxime Ripard wrote:
>> On Wed, Dec 06, 2023 at 08:02:55PM +0800, Keith Zhao wrote:
>>> >> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
>>> >> +	{ .compatible = "starfive,jh7110-inno-hdmi",},
>>> > 
>>> > So it's inno hdmi, just like Rockchip then?
>>> > 
>>> > This should be a common driver.
>>>
>>> Rockchip has a inno hdmi IP. and Starfive has a inno hdmi IP.
>>> but the harewawre difference of them is big , it is not easy to use the common driver
>>> maybe i need the inno hdmi version here to make a distinction
>> 
>> I just had a look at the rockchip header file: all the registers but the
>> STARFIVE_* ones are identical.
>> 
>> There's no need to have two identical drivers then, please use the
>> rockchip driver instead.
>> 
>> Maxime
>
>ok, have a simple test , edid can get . i will continue 

Maybe you can take drivers/gpu/drm/bridge/synopsys/dw-hdmi as a reference, this
is also a hdmi ip used by rockchip/meson/sunxi/jz/imx。
We finally make it share one driver。
>
>
>_______________________________________________
>linux-riscv mailing list
>linux-riscv@lists.infradead.org
>http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [v3 5/6] drm/vs: Add hdmi driver
  2023-12-07  9:02           ` Andy Yan
@ 2023-12-07 10:48             ` Keith Zhao
  2023-12-08  0:37               ` Andy Yan
  0 siblings, 1 reply; 52+ messages in thread
From: Keith Zhao @ 2023-12-07 10:48 UTC (permalink / raw)
  To: Andy Yan
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	paul.walmsley, linux-kernel, dri-devel, Xingyu Wu, Jack Zhu,
	palmer, Maxime Ripard, tzimmermann, William Qiu, Shengyang Chen,
	linux-riscv, Changhuang Liang



On 2023/12/7 17:02, Andy Yan wrote:
> 
> 
> 
> 
> Hi Keith:
> 
> 
> 
> 
> 
> 
> 
> 
> 
> 
> 
> At 2023-12-06 22:11:33, "Keith Zhao" <keith.zhao@starfivetech.com> wrote:
>>
>>
>>On 2023/12/6 20:56, Maxime Ripard wrote:
>>> On Wed, Dec 06, 2023 at 08:02:55PM +0800, Keith Zhao wrote:
>>>> >> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
>>>> >> +	{ .compatible = "starfive,jh7110-inno-hdmi",},
>>>> > 
>>>> > So it's inno hdmi, just like Rockchip then?
>>>> > 
>>>> > This should be a common driver.
>>>>
>>>> Rockchip has a inno hdmi IP. and Starfive has a inno hdmi IP.
>>>> but the harewawre difference of them is big , it is not easy to use the common driver
>>>> maybe i need the inno hdmi version here to make a distinction
>>> 
>>> I just had a look at the rockchip header file: all the registers but the
>>> STARFIVE_* ones are identical.
>>> 
>>> There's no need to have two identical drivers then, please use the
>>> rockchip driver instead.
>>> 
>>> Maxime
>>
>>ok, have a simple test , edid can get . i will continue 
> 
> Maybe you can take drivers/gpu/drm/bridge/synopsys/dw-hdmi as a reference, this
> is also a hdmi ip used by rockchip/meson/sunxi/jz/imx。
> We finally make it share one driver。
>>
hi Andy:

dw_hdmi seems a good choice , it can handle inno hdmi hardware by define its dw_hdmi_plat_data.
does it means i can write own driver files such as(dw_hdmi-starfive.c) based on dw_hdmi instead of add plat_data in inno_hdmi.c

Thanks for pointing this out!!!

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

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

* Re: [v3 4/6] drm/vs: Add KMS crtc&plane
  2023-12-07  8:41   ` Icenowy Zheng
@ 2023-12-07 11:31     ` Keith Zhao
  2023-12-07 15:29       ` Icenowy Zheng
  2023-12-27  3:57       ` Icenowy Zheng
  0 siblings, 2 replies; 52+ messages in thread
From: Keith Zhao @ 2023-12-07 11:31 UTC (permalink / raw)
  To: Icenowy Zheng, devicetree, dri-devel, linux-kernel, linux-riscv
  Cc: aou, suijingfeng, krzysztof.kozlowski+dt, William Qiu, mripard,
	Xingyu Wu, Jack Zhu, palmer, tzimmermann, paul.walmsley,
	Shengyang Chen, Changhuang Liang



On 2023/12/7 16:41, Icenowy Zheng wrote:
> 在 2023-12-04星期一的 20:33 +0800,Keith Zhao写道:
> *snip*
> 
>> +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;
> 
> From my experiments on poking with registers on T-Head TH1520 (also
> uses DC8200 display controller and a similar driver), the DC8200
> hardware have a different definition of cursor position X and Y with
> the CRTC plane state.
> 
> For CRTC plane state, hot_x and hot_y are only provided as reference,
> and the cursor should be displayed with its (0,0) drawn to (crtc_x,
> crtc_y) ([XY]_crtc are values specified in CRTC state, the right part
> of the assignments here), when the cursor is moved to (0,0) but the hot
> point is not (0,0), it could be negative.
> 
> However, for DC8200 registers definition, cursor XY position could not
> be negative -- the cursor will disappear then; because in its
> definition, the cursor XY position should be where the cursor is
> pointing to, instead of its (0,0). DC8200 will draw (0,0) of the cursor
> to (x - hot_x, y - hot_y). So to met the expectation of the KMS plane
> settings, the DC8200 position should be set to (crtc_x + hot_x, crtc_y
> + hot_y) instead. Thus these two lines of code should be:
> 
> ```
>         cursor.x = state->crtc_x + drm_fb->hot_x;
>         cursor.y = state->crtc_y + drm_fb->hot_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);
>> +}
> *snip
hello Icenowy:
you are deep understanding on dc8200.
by the way of practice
I tested this change on the debian desktop, is there a way to compare the cursor behavior change?
Thanks




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

* Re: [v3 4/6] drm/vs: Add KMS crtc&plane
  2023-12-07 11:31     ` Keith Zhao
@ 2023-12-07 15:29       ` Icenowy Zheng
  2023-12-27  3:57       ` Icenowy Zheng
  1 sibling, 0 replies; 52+ messages in thread
From: Icenowy Zheng @ 2023-12-07 15:29 UTC (permalink / raw)
  To: Keith Zhao, devicetree, dri-devel, linux-kernel, linux-riscv
  Cc: aou, suijingfeng, tzimmermann, paul.walmsley, mripard, Xingyu Wu,
	Jack Zhu, palmer, krzysztof.kozlowski+dt, William Qiu,
	Shengyang Chen, Changhuang Liang

在 2023-12-07星期四的 19:31 +0800,Keith Zhao写道:
> 
> 
> On 2023/12/7 16:41, Icenowy Zheng wrote:
> > 在 2023-12-04星期一的 20:33 +0800,Keith Zhao写道:
> > *snip*
> > 
> > > +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;
> > 
> > From my experiments on poking with registers on T-Head TH1520 (also
> > uses DC8200 display controller and a similar driver), the DC8200
> > hardware have a different definition of cursor position X and Y
> > with
> > the CRTC plane state.
> > 
> > For CRTC plane state, hot_x and hot_y are only provided as
> > reference,
> > and the cursor should be displayed with its (0,0) drawn to (crtc_x,
> > crtc_y) ([XY]_crtc are values specified in CRTC state, the right
> > part
> > of the assignments here), when the cursor is moved to (0,0) but the
> > hot
> > point is not (0,0), it could be negative.
> > 
> > However, for DC8200 registers definition, cursor XY position could
> > not
> > be negative -- the cursor will disappear then; because in its
> > definition, the cursor XY position should be where the cursor is
> > pointing to, instead of its (0,0). DC8200 will draw (0,0) of the
> > cursor
> > to (x - hot_x, y - hot_y). So to met the expectation of the KMS
> > plane
> > settings, the DC8200 position should be set to (crtc_x + hot_x,
> > crtc_y
> > + hot_y) instead. Thus these two lines of code should be:
> > 
> > ```
> >         cursor.x = state->crtc_x + drm_fb->hot_x;
> >         cursor.y = state->crtc_y + drm_fb->hot_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);
> > > +}
> > *snip
> hello Icenowy:
> you are deep understanding on dc8200.
> by the way of practice
> I tested this change on the debian desktop, is there a way to compare
> the cursor behavior change?

Try selecting some small-sized text in a textbox?

> Thanks
> 
> 
> 


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

* Re:Re: [v3 5/6] drm/vs: Add hdmi driver
  2023-12-07 10:48             ` Keith Zhao
@ 2023-12-08  0:37               ` Andy Yan
  2023-12-08  3:00                 ` Keith Zhao
  0 siblings, 1 reply; 52+ messages in thread
From: Andy Yan @ 2023-12-08  0:37 UTC (permalink / raw)
  To: Keith Zhao
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	paul.walmsley, linux-kernel, dri-devel, Xingyu Wu, Jack Zhu,
	palmer, Maxime Ripard, tzimmermann, William Qiu, Shengyang Chen,
	linux-riscv, Changhuang Liang

Hi Keth:






在 2023-12-07 18:48:13,"Keith Zhao" <keith.zhao@starfivetech.com> 写道:
>
>
>On 2023/12/7 17:02, Andy Yan wrote:
>> 
>> 
>> 
>> 
>> Hi Keith:
>> 
>> 
>> 
>> 
>> 
>> 
>> 
>> 
>> 
>> 
>> 
>> At 2023-12-06 22:11:33, "Keith Zhao" <keith.zhao@starfivetech.com> wrote:
>>>
>>>
>>>On 2023/12/6 20:56, Maxime Ripard wrote:
>>>> On Wed, Dec 06, 2023 at 08:02:55PM +0800, Keith Zhao wrote:
>>>>> >> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
>>>>> >> +	{ .compatible = "starfive,jh7110-inno-hdmi",},
>>>>> > 
>>>>> > So it's inno hdmi, just like Rockchip then?
>>>>> > 
>>>>> > This should be a common driver.
>>>>>
>>>>> Rockchip has a inno hdmi IP. and Starfive has a inno hdmi IP.
>>>>> but the harewawre difference of them is big , it is not easy to use the common driver
>>>>> maybe i need the inno hdmi version here to make a distinction
>>>> 
>>>> I just had a look at the rockchip header file: all the registers but the
>>>> STARFIVE_* ones are identical.
>>>> 
>>>> There's no need to have two identical drivers then, please use the
>>>> rockchip driver instead.
>>>> 
>>>> Maxime
>>>
>>>ok, have a simple test , edid can get . i will continue 
>> 
>> Maybe you can take drivers/gpu/drm/bridge/synopsys/dw-hdmi as a reference, this
>> is also a hdmi ip used by rockchip/meson/sunxi/jz/imx。
>> We finally make it share one driver。
>>>
>hi Andy:
>
>dw_hdmi seems a good choice , it can handle inno hdmi hardware by define its dw_hdmi_plat_data.
>does it means i can write own driver files such as(dw_hdmi-starfive.c) based on dw_hdmi instead of add plat_data in inno_hdmi.c
>

I think the process maybe like this:

1. split the inno_hdmi.c under rockchip to  inno_hdmi.c(the common part), inno_hdmi-rockchip.c(the soc specific part)
2. move the common part inno_hdmi.c to drivers/gpu/drm/bridge/innosilicon/
3. add startfive specific part, inno_hdmi-startfive.c

bellow git log from kernel three show how we convert  dw_hdmi to a common driver: 



12b9f204e804 drm: bridge/dw_hdmi: add rockchip rk3288 support
74af9e4d03b8 dt-bindings: Add documentation for rockchip dw hdmi
d346c14eeea9 drm: bridge/dw_hdmi: add function dw_hdmi_phy_enable_spare
a4d3b8b050d5 drm: bridge/dw_hdmi: clear i2cmphy_stat0 reg in hdmi_phy_wait_i2c_done
632d035bace2 drm: bridge/dw_hdmi: add mode_valid support
0cd9d1428322 drm: bridge/dw_hdmi: add support for multi-byte register width access
cd152393967e dt-bindings: add document for dw_hdmi
b21f4b658df8 drm: imx: imx-hdmi: move imx-hdmi to bridge/dw_hdmi
aaa757a092c2 drm: imx: imx-hdmi: split phy configuration to platform driver
3d1b35a3d9f3 drm: imx: imx-hdmi: convert imx-hdmi to drm_bridge mode
c2c3848851a7 drm: imx: imx-hdmi: return defer if can't get ddc i2c adapter
b587833933de drm: imx: imx-hdmi: make checkpatch happy


>Thanks for pointing this out!!!
>
>>>
>>>_______________________________________________
>>>linux-riscv mailing list
>>>linux-riscv@lists.infradead.org
>>>http://lists.infradead.org/mailman/listinfo/linux-riscv
>
>_______________________________________________
>linux-riscv mailing list
>linux-riscv@lists.infradead.org
>http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [v3 5/6] drm/vs: Add hdmi driver
  2023-12-08  0:37               ` Andy Yan
@ 2023-12-08  3:00                 ` Keith Zhao
  2023-12-08  3:23                   ` Andy Yan
  0 siblings, 1 reply; 52+ messages in thread
From: Keith Zhao @ 2023-12-08  3:00 UTC (permalink / raw)
  To: Andy Yan
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	paul.walmsley, linux-kernel, dri-devel, Xingyu Wu, Jack Zhu,
	palmer, Maxime Ripard, tzimmermann, William Qiu, Shengyang Chen,
	linux-riscv, Changhuang Liang



On 2023/12/8 8:37, Andy Yan wrote:
> Hi Keth:
> 
> 
> 
> 
> 
> 
> 在 2023-12-07 18:48:13,"Keith Zhao" <keith.zhao@starfivetech.com> 写道:
>>
>>
>>On 2023/12/7 17:02, Andy Yan wrote:
>>> 
>>> 
>>> 
>>> 
>>> Hi Keith:
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> At 2023-12-06 22:11:33, "Keith Zhao" <keith.zhao@starfivetech.com> wrote:
>>>>
>>>>
>>>>On 2023/12/6 20:56, Maxime Ripard wrote:
>>>>> On Wed, Dec 06, 2023 at 08:02:55PM +0800, Keith Zhao wrote:
>>>>>> >> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
>>>>>> >> +	{ .compatible = "starfive,jh7110-inno-hdmi",},
>>>>>> > 
>>>>>> > So it's inno hdmi, just like Rockchip then?
>>>>>> > 
>>>>>> > This should be a common driver.
>>>>>>
>>>>>> Rockchip has a inno hdmi IP. and Starfive has a inno hdmi IP.
>>>>>> but the harewawre difference of them is big , it is not easy to use the common driver
>>>>>> maybe i need the inno hdmi version here to make a distinction
>>>>> 
>>>>> I just had a look at the rockchip header file: all the registers but the
>>>>> STARFIVE_* ones are identical.
>>>>> 
>>>>> There's no need to have two identical drivers then, please use the
>>>>> rockchip driver instead.
>>>>> 
>>>>> Maxime
>>>>
>>>>ok, have a simple test , edid can get . i will continue 
>>> 
>>> Maybe you can take drivers/gpu/drm/bridge/synopsys/dw-hdmi as a reference, this
>>> is also a hdmi ip used by rockchip/meson/sunxi/jz/imx。
>>> We finally make it share one driver。
>>>>
>>hi Andy:
>>
>>dw_hdmi seems a good choice , it can handle inno hdmi hardware by define its dw_hdmi_plat_data.
>>does it means i can write own driver files such as(dw_hdmi-starfive.c) based on dw_hdmi instead of add plat_data in inno_hdmi.c
>>
> 
> I think the process maybe like this:
> 
> 1. split the inno_hdmi.c under rockchip to  inno_hdmi.c(the common part), inno_hdmi-rockchip.c(the soc specific part)
> 2. move the common part inno_hdmi.c to drivers/gpu/drm/bridge/innosilicon/
> 3. add startfive specific part, inno_hdmi-startfive.c
> 
> bellow git log from kernel three show how we convert  dw_hdmi to a common driver: 
> 
> 
> 
> 12b9f204e804 drm: bridge/dw_hdmi: add rockchip rk3288 support
> 74af9e4d03b8 dt-bindings: Add documentation for rockchip dw hdmi
> d346c14eeea9 drm: bridge/dw_hdmi: add function dw_hdmi_phy_enable_spare
> a4d3b8b050d5 drm: bridge/dw_hdmi: clear i2cmphy_stat0 reg in hdmi_phy_wait_i2c_done
> 632d035bace2 drm: bridge/dw_hdmi: add mode_valid support
> 0cd9d1428322 drm: bridge/dw_hdmi: add support for multi-byte register width access
> cd152393967e dt-bindings: add document for dw_hdmi
> b21f4b658df8 drm: imx: imx-hdmi: move imx-hdmi to bridge/dw_hdmi
> aaa757a092c2 drm: imx: imx-hdmi: split phy configuration to platform driver
> 3d1b35a3d9f3 drm: imx: imx-hdmi: convert imx-hdmi to drm_bridge mode
> c2c3848851a7 drm: imx: imx-hdmi: return defer if can't get ddc i2c adapter
> b587833933de drm: imx: imx-hdmi: make checkpatch happy
> 
hi Andy:
I got you means, 
as I don't have a rockchip board on hand , to split the inno_hdmi.c can not be tested.

how adout this idea:
1、split the starfive_hdmi.c under verisilicion to  inno_hdmi.c(the common part), inno_hdmi-starfive.c(the soc specific part)
2. move the common part inno_hdmi.c to drivers/gpu/drm/bridge/innosilicon/
3. In the future, inno hdmi.c under rockchip will reuse the public driver.

> 
>>Thanks for pointing this out!!!
>>
>>>>
>>>>_______________________________________________
>>>>linux-riscv mailing list
>>>>linux-riscv@lists.infradead.org
>>>>http://lists.infradead.org/mailman/listinfo/linux-riscv
>>
>>_______________________________________________
>>linux-riscv mailing list
>>linux-riscv@lists.infradead.org
>>http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re:Re: [v3 5/6] drm/vs: Add hdmi driver
  2023-12-08  3:00                 ` Keith Zhao
@ 2023-12-08  3:23                   ` Andy Yan
  2023-12-08  9:14                     ` Maxime Ripard
  0 siblings, 1 reply; 52+ messages in thread
From: Andy Yan @ 2023-12-08  3:23 UTC (permalink / raw)
  To: Keith Zhao
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	paul.walmsley, linux-kernel, dri-devel, Xingyu Wu, Jack Zhu,
	palmer, Maxime Ripard, tzimmermann, William Qiu, Shengyang Chen,
	linux-riscv, Changhuang Liang



Hi Keith:

在 2023-12-08 11:00:31,"Keith Zhao" <keith.zhao@starfivetech.com> 写道:
>
>
>On 2023/12/8 8:37, Andy Yan wrote:
>> Hi Keth:
>> 
>> 
>> 
>> 
>> 
>> 
>> 在 2023-12-07 18:48:13,"Keith Zhao" <keith.zhao@starfivetech.com> 写道:
>>>
>>>
>>>On 2023/12/7 17:02, Andy Yan wrote:
>>>> 
>>>> 
>>>> 
>>>> 
>>>> Hi Keith:
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>> At 2023-12-06 22:11:33, "Keith Zhao" <keith.zhao@starfivetech.com> wrote:
>>>>>
>>>>>
>>>>>On 2023/12/6 20:56, Maxime Ripard wrote:
>>>>>> On Wed, Dec 06, 2023 at 08:02:55PM +0800, Keith Zhao wrote:
>>>>>>> >> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
>>>>>>> >> +	{ .compatible = "starfive,jh7110-inno-hdmi",},
>>>>>>> > 
>>>>>>> > So it's inno hdmi, just like Rockchip then?
>>>>>>> > 
>>>>>>> > This should be a common driver.
>>>>>>>
>>>>>>> Rockchip has a inno hdmi IP. and Starfive has a inno hdmi IP.
>>>>>>> but the harewawre difference of them is big , it is not easy to use the common driver
>>>>>>> maybe i need the inno hdmi version here to make a distinction
>>>>>> 
>>>>>> I just had a look at the rockchip header file: all the registers but the
>>>>>> STARFIVE_* ones are identical.
>>>>>> 
>>>>>> There's no need to have two identical drivers then, please use the
>>>>>> rockchip driver instead.
>>>>>> 
>>>>>> Maxime
>>>>>
>>>>>ok, have a simple test , edid can get . i will continue 
>>>> 
>>>> Maybe you can take drivers/gpu/drm/bridge/synopsys/dw-hdmi as a reference, this
>>>> is also a hdmi ip used by rockchip/meson/sunxi/jz/imx。
>>>> We finally make it share one driver。
>>>>>
>>>hi Andy:
>>>
>>>dw_hdmi seems a good choice , it can handle inno hdmi hardware by define its dw_hdmi_plat_data.
>>>does it means i can write own driver files such as(dw_hdmi-starfive.c) based on dw_hdmi instead of add plat_data in inno_hdmi.c
>>>
>> 
>> I think the process maybe like this:
>> 
>> 1. split the inno_hdmi.c under rockchip to  inno_hdmi.c(the common part), inno_hdmi-rockchip.c(the soc specific part)
>> 2. move the common part inno_hdmi.c to drivers/gpu/drm/bridge/innosilicon/
>> 3. add startfive specific part, inno_hdmi-startfive.c
>> 
>> bellow git log from kernel three show how we convert  dw_hdmi to a common driver: 
>> 
>> 
>> 
>> 12b9f204e804 drm: bridge/dw_hdmi: add rockchip rk3288 support
>> 74af9e4d03b8 dt-bindings: Add documentation for rockchip dw hdmi
>> d346c14eeea9 drm: bridge/dw_hdmi: add function dw_hdmi_phy_enable_spare
>> a4d3b8b050d5 drm: bridge/dw_hdmi: clear i2cmphy_stat0 reg in hdmi_phy_wait_i2c_done
>> 632d035bace2 drm: bridge/dw_hdmi: add mode_valid support
>> 0cd9d1428322 drm: bridge/dw_hdmi: add support for multi-byte register width access
>> cd152393967e dt-bindings: add document for dw_hdmi
>> b21f4b658df8 drm: imx: imx-hdmi: move imx-hdmi to bridge/dw_hdmi
>> aaa757a092c2 drm: imx: imx-hdmi: split phy configuration to platform driver
>> 3d1b35a3d9f3 drm: imx: imx-hdmi: convert imx-hdmi to drm_bridge mode
>> c2c3848851a7 drm: imx: imx-hdmi: return defer if can't get ddc i2c adapter
>> b587833933de drm: imx: imx-hdmi: make checkpatch happy
>> 
>hi Andy:
>I got you means, 
>as I don't have a rockchip board on hand , to split the inno_hdmi.c can not be tested.
>
>how adout this idea:
>1、split the starfive_hdmi.c under verisilicion to  inno_hdmi.c(the common part), inno_hdmi-starfive.c(the soc specific part)
>2. move the common part inno_hdmi.c to drivers/gpu/drm/bridge/innosilicon/
>3. In the future, inno hdmi.c under rockchip will reuse the public driver.

I am not sure if drm maintainers are happy with this。

To be honest, I also don't have a  i.mx board when I start convert dw_hdmi to a common driver,
some respectable people from the community help test and give me many valuable advice, this
is the power of open source。

I found a rk3036 based kylin board this week,but it can't  boot yet,I will go on try if
I can boot it this weekend。 I can do the test on rockchip side, if i can make this board work。

>
>> 
>>>Thanks for pointing this out!!!
>>>
>>>>>
>>>>>_______________________________________________
>>>>>linux-riscv mailing list
>>>>>linux-riscv@lists.infradead.org
>>>>>http://lists.infradead.org/mailman/listinfo/linux-riscv
>>>
>>>_______________________________________________
>>>linux-riscv mailing list
>>>linux-riscv@lists.infradead.org
>>>http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [v3 5/6] drm/vs: Add hdmi driver
  2023-12-08  3:23                   ` Andy Yan
@ 2023-12-08  9:14                     ` Maxime Ripard
  2023-12-11 10:24                       ` Keith Zhao
  0 siblings, 1 reply; 52+ messages in thread
From: Maxime Ripard @ 2023-12-08  9:14 UTC (permalink / raw)
  To: Andy Yan
  Cc: devicetree, Keith Zhao, suijingfeng, krzysztof.kozlowski+dt,
	paul.walmsley, linux-kernel, dri-devel, Xingyu Wu, Jack Zhu, aou,
	palmer, tzimmermann, William Qiu, Shengyang Chen, linux-riscv,
	Changhuang Liang

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

Hi,

On Fri, Dec 08, 2023 at 11:23:37AM +0800, Andy Yan wrote:
> 在 2023-12-08 11:00:31,"Keith Zhao" <keith.zhao@starfivetech.com> 写道:
> >
> >
> >On 2023/12/8 8:37, Andy Yan wrote:
> >> Hi Keth:
> >> 
> >> 
> >> 
> >> 
> >> 
> >> 
> >> 在 2023-12-07 18:48:13,"Keith Zhao" <keith.zhao@starfivetech.com> 写道:
> >>>
> >>>
> >>>On 2023/12/7 17:02, Andy Yan wrote:
> >>>> 
> >>>> 
> >>>> 
> >>>> 
> >>>> Hi Keith:
> >>>> 
> >>>> 
> >>>> 
> >>>> 
> >>>> 
> >>>> 
> >>>> 
> >>>> 
> >>>> 
> >>>> 
> >>>> 
> >>>> At 2023-12-06 22:11:33, "Keith Zhao" <keith.zhao@starfivetech.com> wrote:
> >>>>>
> >>>>>
> >>>>>On 2023/12/6 20:56, Maxime Ripard wrote:
> >>>>>> On Wed, Dec 06, 2023 at 08:02:55PM +0800, Keith Zhao wrote:
> >>>>>>> >> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
> >>>>>>> >> +	{ .compatible = "starfive,jh7110-inno-hdmi",},
> >>>>>>> > 
> >>>>>>> > So it's inno hdmi, just like Rockchip then?
> >>>>>>> > 
> >>>>>>> > This should be a common driver.
> >>>>>>>
> >>>>>>> Rockchip has a inno hdmi IP. and Starfive has a inno hdmi IP.
> >>>>>>> but the harewawre difference of them is big , it is not easy to use the common driver
> >>>>>>> maybe i need the inno hdmi version here to make a distinction
> >>>>>> 
> >>>>>> I just had a look at the rockchip header file: all the registers but the
> >>>>>> STARFIVE_* ones are identical.
> >>>>>> 
> >>>>>> There's no need to have two identical drivers then, please use the
> >>>>>> rockchip driver instead.
> >>>>>> 
> >>>>>> Maxime
> >>>>>
> >>>>>ok, have a simple test , edid can get . i will continue 
> >>>> 
> >>>> Maybe you can take drivers/gpu/drm/bridge/synopsys/dw-hdmi as a reference, this
> >>>> is also a hdmi ip used by rockchip/meson/sunxi/jz/imx。
> >>>> We finally make it share one driver。
> >>>>>
> >>>hi Andy:
> >>>
> >>>dw_hdmi seems a good choice , it can handle inno hdmi hardware by define its dw_hdmi_plat_data.
> >>>does it means i can write own driver files such as(dw_hdmi-starfive.c) based on dw_hdmi instead of add plat_data in inno_hdmi.c
> >>>
> >> 
> >> I think the process maybe like this:
> >> 
> >> 1. split the inno_hdmi.c under rockchip to  inno_hdmi.c(the common part), inno_hdmi-rockchip.c(the soc specific part)
> >> 2. move the common part inno_hdmi.c to drivers/gpu/drm/bridge/innosilicon/
> >> 3. add startfive specific part, inno_hdmi-startfive.c
> >> 
> >> bellow git log from kernel three show how we convert  dw_hdmi to a common driver: 
> >> 
> >> 
> >> 
> >> 12b9f204e804 drm: bridge/dw_hdmi: add rockchip rk3288 support
> >> 74af9e4d03b8 dt-bindings: Add documentation for rockchip dw hdmi
> >> d346c14eeea9 drm: bridge/dw_hdmi: add function dw_hdmi_phy_enable_spare
> >> a4d3b8b050d5 drm: bridge/dw_hdmi: clear i2cmphy_stat0 reg in hdmi_phy_wait_i2c_done
> >> 632d035bace2 drm: bridge/dw_hdmi: add mode_valid support
> >> 0cd9d1428322 drm: bridge/dw_hdmi: add support for multi-byte register width access
> >> cd152393967e dt-bindings: add document for dw_hdmi
> >> b21f4b658df8 drm: imx: imx-hdmi: move imx-hdmi to bridge/dw_hdmi
> >> aaa757a092c2 drm: imx: imx-hdmi: split phy configuration to platform driver
> >> 3d1b35a3d9f3 drm: imx: imx-hdmi: convert imx-hdmi to drm_bridge mode
> >> c2c3848851a7 drm: imx: imx-hdmi: return defer if can't get ddc i2c adapter
> >> b587833933de drm: imx: imx-hdmi: make checkpatch happy
> >> 
> >hi Andy:
> >I got you means, 
> >as I don't have a rockchip board on hand , to split the inno_hdmi.c can not be tested.
> >
> >how adout this idea:
> >1、split the starfive_hdmi.c under verisilicion to  inno_hdmi.c(the common part), inno_hdmi-starfive.c(the soc specific part)
> >2. move the common part inno_hdmi.c to drivers/gpu/drm/bridge/innosilicon/
> >3. In the future, inno hdmi.c under rockchip will reuse the public driver.
> 
> I am not sure if drm maintainers are happy with this。

Not really, no.

Because we would still have two drivers for the same controller, and a
common one that haven't really been tested on anything but a single
platform. So arguably a worse situation than what you were suggesting in
the first place.

The best solution would be to find someone with a Rockchip board to test
your changes, or to get one if it's doable so you can test yourself.

Maxime

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

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

* Re: [v3 1/6] dt-bindings: display: Add yamls for JH7110 display system
  2023-12-04 12:33 ` [v3 1/6] dt-bindings: display: Add yamls for JH7110 display system Keith Zhao
  2023-12-05  6:59   ` Krzysztof Kozlowski
  2023-12-06 12:50   ` Sui Jingfeng
@ 2023-12-08 16:31   ` Rob Herring
  2 siblings, 0 replies; 52+ messages in thread
From: Rob Herring @ 2023-12-08 16:31 UTC (permalink / raw)
  To: Keith Zhao
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	paul.walmsley, linux-kernel, mripard, xingyu.wu, jack.zhu,
	palmer, dri-devel, tzimmermann, william.qiu, shengyang.chen,
	linux-riscv, changhuang.liang

On Mon, Dec 04, 2023 at 08:33:10PM +0800, Keith Zhao wrote:
> StarFive SoCs JH7110 display system:
> dc controller, hdmi controller,
> encoder, vout syscon.

What are yamls?

I prefer not using 'yaml' as yaml is lots of things that are not DT 
schema.

Rob

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

* Re: [v3 1/6] dt-bindings: display: Add yamls for JH7110 display system
  2023-12-06 12:50   ` Sui Jingfeng
@ 2023-12-08 16:33     ` Rob Herring
  0 siblings, 0 replies; 52+ messages in thread
From: Rob Herring @ 2023-12-08 16:33 UTC (permalink / raw)
  To: Sui Jingfeng
  Cc: devicetree, Keith Zhao, krzysztof.kozlowski+dt, william.qiu,
	linux-kernel, dri-devel, xingyu.wu, jack.zhu, aou, palmer,
	mripard, tzimmermann, paul.walmsley, shengyang.chen, linux-riscv,
	changhuang.liang

On Wed, Dec 06, 2023 at 08:50:29PM +0800, Sui Jingfeng wrote:
> Hi,
> 
> 
> On 2023/12/4 20:33, Keith Zhao wrote:
> > StarFive SoCs JH7110 display system:
> > dc controller, hdmi controller,
> > encoder, vout syscon.

[...]

> > +description:
> > +  The StarFive JH7110 SoC uses the HDMI signal transmiter based on innosilicon IP
> 
> 'transmiter' -> 'transmitter'


Thank you for reviewing, but please trim your replies especially when it 
is only 1 line reply in the middle of 100s of lines.

Rob

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

* Re: [v3 3/6] drm/vs: Register DRM device
  2023-12-04 13:30   ` Philipp Zabel
@ 2023-12-11  9:00     ` Keith Zhao
  2023-12-11  9:17       ` mripard
  0 siblings, 1 reply; 52+ messages in thread
From: Keith Zhao @ 2023-12-11  9:00 UTC (permalink / raw)
  To: Philipp Zabel, devicetree, dri-devel, linux-kernel, linux-riscv
  Cc: aou, suijingfeng, tzimmermann, paul.walmsley, mripard, Xingyu Wu,
	Jack Zhu, palmer, krzysztof.kozlowski+dt, William Qiu,
	Shengyang Chen, Changhuang Liang

hi Philipp:

On 2023/12/4 21:30, Philipp Zabel wrote:
> Hi Keith,
> 
> On Mo, 2023-12-04 at 20:33 +0800, Keith Zhao wrote:
>> Implement drm device registration interface
>> 
>> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
>> ---
> [...]
>> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
>> new file mode 100644
>> index 000000000000..e10fa97635aa
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/Kconfig
>> @@ -0,0 +1,13 @@
>> +# 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 seems a bit generic. Doesn't the VeriSilicon display controller IP
> used on JH7110 have a product name?
yes , there is a product name "dc8200", I will match it.
> 
> [...]
>> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
>> new file mode 100644
>> index 000000000000..4fb1f29ef84b
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
>> @@ -0,0 +1,316 @@
>> +// SPDX-License-Identifier: GPL-2.0
> [...]
>> +static void vs_drm_device_release_clocks(void *res)
>> +{
>> +	struct vs_drm_device *priv = res;
>> +	unsigned int i;
>> +
>> +	reset_control_bulk_assert(priv->nrsts, priv->rst_vout);
>> +
>> +	for (i = 0; i < priv->clk_count; ++i) {
>> +		if (priv->clks[i]) {
>> +			clk_disable_unprepare(priv->clks[i]);
>> +			clk_put(priv->clks[i]);
>> +		}
>> +	}
> 
> Why not use the bulk API for clk as well?
ok , will do it next version
> 
> [...]
>> +static int vs_drm_device_init_clocks(struct vs_drm_device *priv)
>> +{
>> +	struct drm_device *dev = &priv->base;
>> +	struct platform_device *pdev = to_platform_device(dev->dev);
>> +	struct device_node *of_node = pdev->dev.of_node;
>> +	struct clk *clock;
>> +	unsigned int i;
>> +	int ret;
>> +
>> +	if (dev_get_platdata(&pdev->dev) || !of_node)
>> +		return 0;
>> +
>> +	priv->nrsts = ARRAY_SIZE(priv->rst_vout);
>> +	for (int i = 0; i < priv->nrsts; ++i)
>> +		priv->rst_vout[i].id = vout_resets[i];
>> +	ret = devm_reset_control_bulk_get_shared(dev->dev, priv->nrsts,
>> +						 priv->rst_vout);
> 
> I would request resets and clocks in _probe().

> 
> If component_bind_all() returns -EPROBE_DEFER because of a still
> missing DSI panel backlight or similar, this doesn't have to be done
> multiple times.
I got what you mean. component_bind_all should be done multiple times
 to prevent the dsi panel driver from lagging load.

in my drm subsystem , there are 2 pipeline 

          +------------------------------+
          |                              |
          |                              |
  +----+  |   +-------------------+      |   +-------+   +------+   +------+
  |    +----->+  dc controller 0  +--->----->+HDMICtl| ->+ PHY  +-->+PANEL0+
  |AXI |  |   +-------------------+      |   +-------+   +------+   +------+
  |    |  |                              |
  |    |  |                              |
  |    |  |                              |
  |    |  |                              |
  |APB |  |   +-------------------+         +---------+    +------+  +-------+
  |    +----->+  dc controller 1  +--->---->+ dsiTx   +--->+DPHY  +->+ PANEL1+
  |    |  |   +-------------------+         +---------+    +------+  +-------+
  +----+  |                              |
          +------------------------------+


component_bind_all will bind the hdmi encoder and dsi encoder .
binding the hdmi encoder will always return ok .

binging the dsi encoder has a question :
I used the panel-raspberrypi-touchscreen.c as panel driver , 
this driver is a i2c device and it use a i2c command to read reg ID
if read success , it will do drm_panel_add. 

if I disconnect the panel ,it will not do drm_panel_add.
dsiTx will fail to find panel , The consequence is that the inputbridge cannot be created , 
also outputbridge cannot be created.
for encoder bind , it will fail to find the input bridge of dsi.
Under this premise, although returning -EPROBE_DEFER allows bind to be executed multiple times, 
the final result is that the entire bind fails.

returning -EPROBE_DEFER can solve panel driver from lagging load , 
but for no panel case , it will destory all pipeline (include hdmi and dsi).

I did two things:
late_initcall_sync(vs_drm_init); to make sure the panel drive has been probed;
dsi encoder bind always return ok to make sure hdmi pipeline ok at lease.
component_bind_all do once . 




> 
>> +	if (ret) {
>> +		drm_err(dev, "Failed to get reset controls\n");
>> +		return ret;
>> +	}
>> +
>> +	priv->clk_count = of_clk_get_parent_count(of_node);
>> +	if (!priv->clk_count)
>> +		return 0;
>> +
>> +	priv->clks = drmm_kzalloc(dev, priv->clk_count * sizeof(priv->clks[0]),
>> +				  GFP_KERNEL);
>> +	if (!priv->clks)
>> +		return -ENOMEM;
>> +
>> +	for (i = 0; i < priv->clk_count; ++i) {
>> +		clock = of_clk_get(of_node, i);
>> +		if (IS_ERR(clock)) {
>> +			ret = PTR_ERR(clock);
>> +			if (ret == -EPROBE_DEFER)
>> +				goto err;
>> +			drm_err(dev, "clock %u not found: %d\n", i, ret);
>> +			continue;
>> +		}
>> +		ret = clk_prepare_enable(clock);
>> +		if (ret) {
>> +			drm_err(dev, "failed to enable clock %u: %d\n",
>> +				i, ret);
>> +			clk_put(clock);
>> +			continue;
>> +		}
>> +		priv->clks[i] = clock;
>> +	}
>> +
>> +	ret = reset_control_bulk_deassert(priv->nrsts, priv->rst_vout);
>> +	if (ret)
>> +		return ret;
> 
> This should goto err, otherwise clocks are left enabled.
> 
>> +
>> +	return devm_add_action_or_reset(&pdev->dev,
>> +					vs_drm_device_release_clocks,
>> +					priv);
>> +
>> +err:
>> +	while (i) {
>> +		--i;
>> +		if (priv->clks[i]) {
>> +			clk_disable_unprepare(priv->clks[i]);
>> +			clk_put(priv->clks[i]);
>> +		}
>> +	}
>> +	return ret;
>> +}
>> +
>> +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;
> 
> Why is this a variable instead of a constant?
the dc controllers of VeriSilicon have different series, like dc8200 , dc9200...
Their pitch values may vary from series to series, like 64 , 128

> 
>> +	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);
>> +
>> +	ret = vs_drm_device_init_clocks(priv);
>> +	if (ret)
>> +		return ret;
>> +
>> +	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;
> 
> Missing component_unbind_all(), see below.
> 
>> +
>> +	drm_mode_config_reset(drm_dev);
>> +
>> +	drm_kms_helper_poll_init(drm_dev);
>> +
>> +	ret = drm_dev_register(drm_dev, 0);
>> +	if (ret)
>> +		return ret;
> 
> Missing drm_kms_helper_poll_fini(), see below.
> 
>> +
>> +	drm_fbdev_generic_setup(drm_dev, 32);
>> +
>> +	return 0;
> 
> Here I'd expect an error path calling drm_kms_helper_poll_fini() and
> component_unbind_all() as appropriate.
> 
ok

> 
> regards
> Philipp

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

* Re: [v3 3/6] drm/vs: Register DRM device
  2023-12-11  9:00     ` Keith Zhao
@ 2023-12-11  9:17       ` mripard
  0 siblings, 0 replies; 52+ messages in thread
From: mripard @ 2023-12-11  9:17 UTC (permalink / raw)
  To: Keith Zhao
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	paul.walmsley, linux-kernel, dri-devel, Xingyu Wu, Jack Zhu,
	palmer, tzimmermann, William Qiu, Shengyang Chen, linux-riscv,
	Changhuang Liang

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

Hi,

On Mon, Dec 11, 2023 at 05:00:04PM +0800, Keith Zhao wrote:
> >> +static int vs_drm_device_init_clocks(struct vs_drm_device *priv)
> >> +{
> >> +	struct drm_device *dev = &priv->base;
> >> +	struct platform_device *pdev = to_platform_device(dev->dev);
> >> +	struct device_node *of_node = pdev->dev.of_node;
> >> +	struct clk *clock;
> >> +	unsigned int i;
> >> +	int ret;
> >> +
> >> +	if (dev_get_platdata(&pdev->dev) || !of_node)
> >> +		return 0;
> >> +
> >> +	priv->nrsts = ARRAY_SIZE(priv->rst_vout);
> >> +	for (int i = 0; i < priv->nrsts; ++i)
> >> +		priv->rst_vout[i].id = vout_resets[i];
> >> +	ret = devm_reset_control_bulk_get_shared(dev->dev, priv->nrsts,
> >> +						 priv->rst_vout);
> > 
> > I would request resets and clocks in _probe().
> 
> > 
> > If component_bind_all() returns -EPROBE_DEFER because of a still
> > missing DSI panel backlight or similar, this doesn't have to be done
> > multiple times.
> I got what you mean. component_bind_all should be done multiple times
>  to prevent the dsi panel driver from lagging load.

No. component_bind_all only needs to be called once.

> in my drm subsystem , there are 2 pipeline 
> 
>           +------------------------------+
>           |                              |
>           |                              |
>   +----+  |   +-------------------+      |   +-------+   +------+   +------+
>   |    +----->+  dc controller 0  +--->----->+HDMICtl| ->+ PHY  +-->+PANEL0+
>   |AXI |  |   +-------------------+      |   +-------+   +------+   +------+
>   |    |  |                              |
>   |    |  |                              |
>   |    |  |                              |
>   |    |  |                              |
>   |APB |  |   +-------------------+         +---------+    +------+  +-------+
>   |    +----->+  dc controller 1  +--->---->+ dsiTx   +--->+DPHY  +->+ PANEL1+
>   |    |  |   +-------------------+         +---------+    +------+  +-------+
>   +----+  |                              |
>           +------------------------------+
> 
> 
> component_bind_all will bind the hdmi encoder and dsi encoder .
> binding the hdmi encoder will always return ok .
> 
> binging the dsi encoder has a question :
> I used the panel-raspberrypi-touchscreen.c as panel driver , 
> this driver is a i2c device and it use a i2c command to read reg ID
> if read success , it will do drm_panel_add. 
> 
> if I disconnect the panel ,it will not do drm_panel_add.
> dsiTx will fail to find panel , The consequence is that the inputbridge cannot be created , 
> also outputbridge cannot be created.
> for encoder bind , it will fail to find the input bridge of dsi.
> Under this premise, although returning -EPROBE_DEFER allows bind to be executed multiple times, 
> the final result is that the entire bind fails.
> 
> returning -EPROBE_DEFER can solve panel driver from lagging load , 
> but for no panel case , it will destory all pipeline (include hdmi and dsi).

Yes, that's expected.

> I did two things:
> late_initcall_sync(vs_drm_init); to make sure the panel drive has been probed;
> dsi encoder bind always return ok to make sure hdmi pipeline ok at lease.
> component_bind_all do once . 

You should have a look at
https://www.kernel.org/doc/html/latest/gpu/drm-kms-helpers.html#special-care-with-mipi-dsi-bridges

Maxime

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

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

* Re: [v3 5/6] drm/vs: Add hdmi driver
  2023-12-08  9:14                     ` Maxime Ripard
@ 2023-12-11 10:24                       ` Keith Zhao
  2023-12-11 12:13                         ` Andy Yan
  0 siblings, 1 reply; 52+ messages in thread
From: Keith Zhao @ 2023-12-11 10:24 UTC (permalink / raw)
  To: Maxime Ripard, Andy Yan
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	paul.walmsley, linux-kernel, dri-devel, Xingyu Wu, Jack Zhu,
	palmer, tzimmermann, William Qiu, Shengyang Chen, linux-riscv,
	Changhuang Liang

hi Maxime:
hi Andy:

On 2023/12/8 17:14, Maxime Ripard wrote:
> Hi,
> 
> On Fri, Dec 08, 2023 at 11:23:37AM +0800, Andy Yan wrote:
>> 在 2023-12-08 11:00:31,"Keith Zhao" <keith.zhao@starfivetech.com> 写道:
>> >
>> >
>> >On 2023/12/8 8:37, Andy Yan wrote:
>> >> Hi Keth:
>> >> 
>> >> 
>> >> 
>> >> 
>> >> 
>> >> 
>> >> 在 2023-12-07 18:48:13,"Keith Zhao" <keith.zhao@starfivetech.com> 写道:
>> >>>
>> >>>
>> >>>On 2023/12/7 17:02, Andy Yan wrote:
>> >>>> 
>> >>>> 
>> >>>> 
>> >>>> 
>> >>>> Hi Keith:
>> >>>> 
>> >>>> 
>> >>>> 
>> >>>> 
>> >>>> 
>> >>>> 
>> >>>> 
>> >>>> 
>> >>>> 
>> >>>> 
>> >>>> 
>> >>>> At 2023-12-06 22:11:33, "Keith Zhao" <keith.zhao@starfivetech.com> wrote:
>> >>>>>
>> >>>>>
>> >>>>>On 2023/12/6 20:56, Maxime Ripard wrote:
>> >>>>>> On Wed, Dec 06, 2023 at 08:02:55PM +0800, Keith Zhao wrote:
>> >>>>>>> >> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
>> >>>>>>> >> +	{ .compatible = "starfive,jh7110-inno-hdmi",},
>> >>>>>>> > 
>> >>>>>>> > So it's inno hdmi, just like Rockchip then?
>> >>>>>>> > 
>> >>>>>>> > This should be a common driver.
>> >>>>>>>
>> >>>>>>> Rockchip has a inno hdmi IP. and Starfive has a inno hdmi IP.
>> >>>>>>> but the harewawre difference of them is big , it is not easy to use the common driver
>> >>>>>>> maybe i need the inno hdmi version here to make a distinction
>> >>>>>> 
>> >>>>>> I just had a look at the rockchip header file: all the registers but the
>> >>>>>> STARFIVE_* ones are identical.
>> >>>>>> 
>> >>>>>> There's no need to have two identical drivers then, please use the
>> >>>>>> rockchip driver instead.
>> >>>>>> 
>> >>>>>> Maxime
>> >>>>>
>> >>>>>ok, have a simple test , edid can get . i will continue 
>> >>>> 
>> >>>> Maybe you can take drivers/gpu/drm/bridge/synopsys/dw-hdmi as a reference, this
>> >>>> is also a hdmi ip used by rockchip/meson/sunxi/jz/imx。
>> >>>> We finally make it share one driver。
>> >>>>>
>> >>>hi Andy:
>> >>>
>> >>>dw_hdmi seems a good choice , it can handle inno hdmi hardware by define its dw_hdmi_plat_data.
>> >>>does it means i can write own driver files such as(dw_hdmi-starfive.c) based on dw_hdmi instead of add plat_data in inno_hdmi.c
>> >>>
>> >> 
>> >> I think the process maybe like this:
>> >> 
>> >> 1. split the inno_hdmi.c under rockchip to  inno_hdmi.c(the common part), inno_hdmi-rockchip.c(the soc specific part)
>> >> 2. move the common part inno_hdmi.c to drivers/gpu/drm/bridge/innosilicon/
>> >> 3. add startfive specific part, inno_hdmi-startfive.c
>> >> 
>> >> bellow git log from kernel three show how we convert  dw_hdmi to a common driver: 
>> >> 
>> >> 
>> >> 
>> >> 12b9f204e804 drm: bridge/dw_hdmi: add rockchip rk3288 support
>> >> 74af9e4d03b8 dt-bindings: Add documentation for rockchip dw hdmi
>> >> d346c14eeea9 drm: bridge/dw_hdmi: add function dw_hdmi_phy_enable_spare
>> >> a4d3b8b050d5 drm: bridge/dw_hdmi: clear i2cmphy_stat0 reg in hdmi_phy_wait_i2c_done
>> >> 632d035bace2 drm: bridge/dw_hdmi: add mode_valid support
>> >> 0cd9d1428322 drm: bridge/dw_hdmi: add support for multi-byte register width access
>> >> cd152393967e dt-bindings: add document for dw_hdmi
>> >> b21f4b658df8 drm: imx: imx-hdmi: move imx-hdmi to bridge/dw_hdmi
>> >> aaa757a092c2 drm: imx: imx-hdmi: split phy configuration to platform driver
>> >> 3d1b35a3d9f3 drm: imx: imx-hdmi: convert imx-hdmi to drm_bridge mode
>> >> c2c3848851a7 drm: imx: imx-hdmi: return defer if can't get ddc i2c adapter
>> >> b587833933de drm: imx: imx-hdmi: make checkpatch happy
>> >> 
>> >hi Andy:
>> >I got you means, 
>> >as I don't have a rockchip board on hand , to split the inno_hdmi.c can not be tested.
>> >
>> >how adout this idea:
>> >1、split the starfive_hdmi.c under verisilicion to  inno_hdmi.c(the common part), inno_hdmi-starfive.c(the soc specific part)
>> >2. move the common part inno_hdmi.c to drivers/gpu/drm/bridge/innosilicon/
>> >3. In the future, inno hdmi.c under rockchip will reuse the public driver.
>> 
>> I am not sure if drm maintainers are happy with this。
> 
> Not really, no.
> 
> Because we would still have two drivers for the same controller, and a
> common one that haven't really been tested on anything but a single
> platform. So arguably a worse situation than what you were suggesting in
> the first place.
> 
> The best solution would be to find someone with a Rockchip board to test
> your changes, or to get one if it's doable so you can test yourself.

ok I will also try to buy a Rockchip 3036 board for self-test.
According to the commit log idea provided by Andy before, make the inno_hdmi driver common module.

would the steps be ok? (if I tested rockchip and starifve pass)
1. split the inno_hdmi.c under rockchip to  inno_hdmi.c(the common part), inno_hdmi-rockchip.c(the soc specific part)
2. move the common part inno_hdmi.c to drivers/gpu/drm/bridge/innosilicon/
3. add startfive specific part, inno_hdmi-startfive.c

Thanks
> 
> Maxime

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

* Re:Re: [v3 5/6] drm/vs: Add hdmi driver
  2023-12-11 10:24                       ` Keith Zhao
@ 2023-12-11 12:13                         ` Andy Yan
  2023-12-13  1:40                           ` Keith Zhao
  0 siblings, 1 reply; 52+ messages in thread
From: Andy Yan @ 2023-12-11 12:13 UTC (permalink / raw)
  To: Keith Zhao
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	paul.walmsley, linux-kernel, dri-devel, Xingyu Wu, Jack Zhu,
	palmer, Maxime Ripard, tzimmermann, William Qiu, Shengyang Chen,
	linux-riscv, Changhuang Liang

Hi Keith:

在 2023-12-11 18:24:35,"Keith Zhao" <keith.zhao@starfivetech.com> 写道:
>hi Maxime:
>hi Andy:
>
>On 2023/12/8 17:14, Maxime Ripard wrote:
>> Hi,
>> 
>> On Fri, Dec 08, 2023 at 11:23:37AM +0800, Andy Yan wrote:
>>> 在 2023-12-08 11:00:31,"Keith Zhao" <keith.zhao@starfivetech.com> 写道:
>>> >
>>> >
>>> >On 2023/12/8 8:37, Andy Yan wrote:
>>> >> Hi Keth:
>>> >> 
>>> >> 
>>> >> 
>>> >> 
>>> >> 
>>> >> 
>>> >> 在 2023-12-07 18:48:13,"Keith Zhao" <keith.zhao@starfivetech.com> 写道:
>>> >>>
>>> >>>
>>> >>>On 2023/12/7 17:02, Andy Yan wrote:
>>> >>>> 
>>> >>>> 
>>> >>>> 
>>> >>>> 
>>> >>>> Hi Keith:
>>> >>>> 
>>> >>>> 
>>> >>>> 
>>> >>>> 
>>> >>>> 
>>> >>>> 
>>> >>>> 
>>> >>>> 
>>> >>>> 
>>> >>>> 
>>> >>>> 
>>> >>>> At 2023-12-06 22:11:33, "Keith Zhao" <keith.zhao@starfivetech.com> wrote:
>>> >>>>>
>>> >>>>>
>>> >>>>>On 2023/12/6 20:56, Maxime Ripard wrote:
>>> >>>>>> On Wed, Dec 06, 2023 at 08:02:55PM +0800, Keith Zhao wrote:
>>> >>>>>>> >> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
>>> >>>>>>> >> +	{ .compatible = "starfive,jh7110-inno-hdmi",},
>>> >>>>>>> > 
>>> >>>>>>> > So it's inno hdmi, just like Rockchip then?
>>> >>>>>>> > 
>>> >>>>>>> > This should be a common driver.
>>> >>>>>>>
>>> >>>>>>> Rockchip has a inno hdmi IP. and Starfive has a inno hdmi IP.
>>> >>>>>>> but the harewawre difference of them is big , it is not easy to use the common driver
>>> >>>>>>> maybe i need the inno hdmi version here to make a distinction
>>> >>>>>> 
>>> >>>>>> I just had a look at the rockchip header file: all the registers but the
>>> >>>>>> STARFIVE_* ones are identical.
>>> >>>>>> 
>>> >>>>>> There's no need to have two identical drivers then, please use the
>>> >>>>>> rockchip driver instead.
>>> >>>>>> 
>>> >>>>>> Maxime
>>> >>>>>
>>> >>>>>ok, have a simple test , edid can get . i will continue 
>>> >>>> 
>>> >>>> Maybe you can take drivers/gpu/drm/bridge/synopsys/dw-hdmi as a reference, this
>>> >>>> is also a hdmi ip used by rockchip/meson/sunxi/jz/imx。
>>> >>>> We finally make it share one driver。
>>> >>>>>
>>> >>>hi Andy:
>>> >>>
>>> >>>dw_hdmi seems a good choice , it can handle inno hdmi hardware by define its dw_hdmi_plat_data.
>>> >>>does it means i can write own driver files such as(dw_hdmi-starfive.c) based on dw_hdmi instead of add plat_data in inno_hdmi.c
>>> >>>
>>> >> 
>>> >> I think the process maybe like this:
>>> >> 
>>> >> 1. split the inno_hdmi.c under rockchip to  inno_hdmi.c(the common part), inno_hdmi-rockchip.c(the soc specific part)
>>> >> 2. move the common part inno_hdmi.c to drivers/gpu/drm/bridge/innosilicon/
>>> >> 3. add startfive specific part, inno_hdmi-startfive.c
>>> >> 
>>> >> bellow git log from kernel three show how we convert  dw_hdmi to a common driver: 
>>> >> 
>>> >> 
>>> >> 
>>> >> 12b9f204e804 drm: bridge/dw_hdmi: add rockchip rk3288 support
>>> >> 74af9e4d03b8 dt-bindings: Add documentation for rockchip dw hdmi
>>> >> d346c14eeea9 drm: bridge/dw_hdmi: add function dw_hdmi_phy_enable_spare
>>> >> a4d3b8b050d5 drm: bridge/dw_hdmi: clear i2cmphy_stat0 reg in hdmi_phy_wait_i2c_done
>>> >> 632d035bace2 drm: bridge/dw_hdmi: add mode_valid support
>>> >> 0cd9d1428322 drm: bridge/dw_hdmi: add support for multi-byte register width access
>>> >> cd152393967e dt-bindings: add document for dw_hdmi
>>> >> b21f4b658df8 drm: imx: imx-hdmi: move imx-hdmi to bridge/dw_hdmi
>>> >> aaa757a092c2 drm: imx: imx-hdmi: split phy configuration to platform driver
>>> >> 3d1b35a3d9f3 drm: imx: imx-hdmi: convert imx-hdmi to drm_bridge mode
>>> >> c2c3848851a7 drm: imx: imx-hdmi: return defer if can't get ddc i2c adapter
>>> >> b587833933de drm: imx: imx-hdmi: make checkpatch happy
>>> >> 
>>> >hi Andy:
>>> >I got you means, 
>>> >as I don't have a rockchip board on hand , to split the inno_hdmi.c can not be tested.
>>> >
>>> >how adout this idea:
>>> >1、split the starfive_hdmi.c under verisilicion to  inno_hdmi.c(the common part), inno_hdmi-starfive.c(the soc specific part)
>>> >2. move the common part inno_hdmi.c to drivers/gpu/drm/bridge/innosilicon/
>>> >3. In the future, inno hdmi.c under rockchip will reuse the public driver.
>>> 
>>> I am not sure if drm maintainers are happy with this。
>> 
>> Not really, no.
>> 
>> Because we would still have two drivers for the same controller, and a
>> common one that haven't really been tested on anything but a single
>> platform. So arguably a worse situation than what you were suggesting in
>> the first place.
>> 
>> The best solution would be to find someone with a Rockchip board to test
>> your changes, or to get one if it's doable so you can test yourself.
>
>ok I will also try to buy a Rockchip 3036 board for self-test.
>According to the commit log idea provided by Andy before, make the inno_hdmi driver common module.

I finally  make my rk3036 based kylin board bootup (use a linux 4.4 downstream bsp,I will find time to try boot
it with mainline)。 So I can help do the test for rockchip side。

It seems not that easy to buy a rk3036 based board from market now。

>
>would the steps be ok? (if I tested rockchip and starifve pass)
>1. split the inno_hdmi.c under rockchip to  inno_hdmi.c(the common part), inno_hdmi-rockchip.c(the soc specific part)
>2. move the common part inno_hdmi.c to drivers/gpu/drm/bridge/innosilicon/
>3. add startfive specific part, inno_hdmi-startfive.c
>
>Thanks
>> 
>> Maxime

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

* Re: [v3 5/6] drm/vs: Add hdmi driver
  2023-12-04 12:33 ` [v3 5/6] drm/vs: Add hdmi driver Keith Zhao
  2023-12-05 13:02   ` Dmitry Baryshkov
  2023-12-06  9:04   ` Maxime Ripard
@ 2023-12-11 17:34   ` Rob Herring
  2 siblings, 0 replies; 52+ messages in thread
From: Rob Herring @ 2023-12-11 17:34 UTC (permalink / raw)
  To: Keith Zhao
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	william.qiu, linux-kernel, dri-devel, xingyu.wu, jack.zhu,
	palmer, mripard, tzimmermann, paul.walmsley, shengyang.chen,
	linux-riscv, changhuang.liang

On Mon, Dec 4, 2023 at 6:33 AM Keith Zhao <keith.zhao@starfivetech.com> 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 | 849 ++++++++++++++++++++
>  drivers/gpu/drm/verisilicon/starfive_hdmi.h | 304 +++++++
>  drivers/gpu/drm/verisilicon/vs_drv.c        |   3 +
>  drivers/gpu/drm/verisilicon/vs_drv.h        |   4 +
>  6 files changed, 1169 insertions(+)
>  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 e10fa97635aa..122c786e3948 100644
> --- a/drivers/gpu/drm/verisilicon/Kconfig
> +++ b/drivers/gpu/drm/verisilicon/Kconfig
> @@ -11,3 +11,11 @@ config DRM_VERISILICON
>           This driver provides VeriSilicon kernel mode
>           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 bf6f2b7ee480..71fadafcee13 100644
> --- a/drivers/gpu/drm/verisilicon/Makefile
> +++ b/drivers/gpu/drm/verisilicon/Makefile
> @@ -6,4 +6,5 @@ vs_drm-objs := vs_dc_hw.o \
>                 vs_drv.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 000000000000..aa621db0dee0
> --- /dev/null
> +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.c
> @@ -0,0 +1,849 @@
> +// 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>

You probably don't need this header and the implicit includes it makes
are dropped now in linux-next. Please check what you actually need and
make them explicit.

Rob

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

* Re: [v3 5/6] drm/vs: Add hdmi driver
  2023-12-11 12:13                         ` Andy Yan
@ 2023-12-13  1:40                           ` Keith Zhao
  2023-12-14  2:51                             ` Andy Yan
  0 siblings, 1 reply; 52+ messages in thread
From: Keith Zhao @ 2023-12-13  1:40 UTC (permalink / raw)
  To: Andy Yan
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	paul.walmsley, linux-kernel, dri-devel, Xingyu Wu, Jack Zhu,
	palmer, Maxime Ripard, tzimmermann, William Qiu, Shengyang Chen,
	linux-riscv, Changhuang Liang



On 2023/12/11 20:13, Andy Yan wrote:
> Hi Keith:
> 
> 在 2023-12-11 18:24:35,"Keith Zhao" <keith.zhao@starfivetech.com> 写道:
>>hi Maxime:
>>hi Andy:
>>
>>On 2023/12/8 17:14, Maxime Ripard wrote:
>>> Hi,
>>> 
>>> On Fri, Dec 08, 2023 at 11:23:37AM +0800, Andy Yan wrote:
>>>> 在 2023-12-08 11:00:31,"Keith Zhao" <keith.zhao@starfivetech.com> 写道:
>>>> >
>>>> >
>>>> >On 2023/12/8 8:37, Andy Yan wrote:
>>>> >> Hi Keth:
>>>> >> 
>>>> >> 
>>>> >> 
>>>> >> 
>>>> >> 
>>>> >> 
>>>> >> 在 2023-12-07 18:48:13,"Keith Zhao" <keith.zhao@starfivetech.com> 写道:
>>>> >>>
>>>> >>>
>>>> >>>On 2023/12/7 17:02, Andy Yan wrote:
>>>> >>>> 
>>>> >>>> 
>>>> >>>> 
>>>> >>>> 
>>>> >>>> Hi Keith:
>>>> >>>> 
>>>> >>>> 
>>>> >>>> 
>>>> >>>> 
>>>> >>>> 
>>>> >>>> 
>>>> >>>> 
>>>> >>>> 
>>>> >>>> 
>>>> >>>> 
>>>> >>>> 
>>>> >>>> At 2023-12-06 22:11:33, "Keith Zhao" <keith.zhao@starfivetech.com> wrote:
>>>> >>>>>
>>>> >>>>>
>>>> >>>>>On 2023/12/6 20:56, Maxime Ripard wrote:
>>>> >>>>>> On Wed, Dec 06, 2023 at 08:02:55PM +0800, Keith Zhao wrote:
>>>> >>>>>>> >> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
>>>> >>>>>>> >> +	{ .compatible = "starfive,jh7110-inno-hdmi",},
>>>> >>>>>>> > 
>>>> >>>>>>> > So it's inno hdmi, just like Rockchip then?
>>>> >>>>>>> > 
>>>> >>>>>>> > This should be a common driver.
>>>> >>>>>>>
>>>> >>>>>>> Rockchip has a inno hdmi IP. and Starfive has a inno hdmi IP.
>>>> >>>>>>> but the harewawre difference of them is big , it is not easy to use the common driver
>>>> >>>>>>> maybe i need the inno hdmi version here to make a distinction
>>>> >>>>>> 
>>>> >>>>>> I just had a look at the rockchip header file: all the registers but the
>>>> >>>>>> STARFIVE_* ones are identical.
>>>> >>>>>> 
>>>> >>>>>> There's no need to have two identical drivers then, please use the
>>>> >>>>>> rockchip driver instead.
>>>> >>>>>> 
>>>> >>>>>> Maxime
>>>> >>>>>
>>>> >>>>>ok, have a simple test , edid can get . i will continue 
>>>> >>>> 
>>>> >>>> Maybe you can take drivers/gpu/drm/bridge/synopsys/dw-hdmi as a reference, this
>>>> >>>> is also a hdmi ip used by rockchip/meson/sunxi/jz/imx。
>>>> >>>> We finally make it share one driver。
>>>> >>>>>
>>>> >>>hi Andy:
>>>> >>>
>>>> >>>dw_hdmi seems a good choice , it can handle inno hdmi hardware by define its dw_hdmi_plat_data.
>>>> >>>does it means i can write own driver files such as(dw_hdmi-starfive.c) based on dw_hdmi instead of add plat_data in inno_hdmi.c
>>>> >>>
>>>> >> 
>>>> >> I think the process maybe like this:
>>>> >> 
>>>> >> 1. split the inno_hdmi.c under rockchip to  inno_hdmi.c(the common part), inno_hdmi-rockchip.c(the soc specific part)
>>>> >> 2. move the common part inno_hdmi.c to drivers/gpu/drm/bridge/innosilicon/
>>>> >> 3. add startfive specific part, inno_hdmi-startfive.c
>>>> >> 
>>>> >> bellow git log from kernel three show how we convert  dw_hdmi to a common driver: 
>>>> >> 
>>>> >> 
>>>> >> 
>>>> >> 12b9f204e804 drm: bridge/dw_hdmi: add rockchip rk3288 support
>>>> >> 74af9e4d03b8 dt-bindings: Add documentation for rockchip dw hdmi
>>>> >> d346c14eeea9 drm: bridge/dw_hdmi: add function dw_hdmi_phy_enable_spare
>>>> >> a4d3b8b050d5 drm: bridge/dw_hdmi: clear i2cmphy_stat0 reg in hdmi_phy_wait_i2c_done
>>>> >> 632d035bace2 drm: bridge/dw_hdmi: add mode_valid support
>>>> >> 0cd9d1428322 drm: bridge/dw_hdmi: add support for multi-byte register width access
>>>> >> cd152393967e dt-bindings: add document for dw_hdmi
>>>> >> b21f4b658df8 drm: imx: imx-hdmi: move imx-hdmi to bridge/dw_hdmi
>>>> >> aaa757a092c2 drm: imx: imx-hdmi: split phy configuration to platform driver
>>>> >> 3d1b35a3d9f3 drm: imx: imx-hdmi: convert imx-hdmi to drm_bridge mode
>>>> >> c2c3848851a7 drm: imx: imx-hdmi: return defer if can't get ddc i2c adapter
>>>> >> b587833933de drm: imx: imx-hdmi: make checkpatch happy
>>>> >> 
>>>> >hi Andy:
>>>> >I got you means, 
>>>> >as I don't have a rockchip board on hand , to split the inno_hdmi.c can not be tested.
>>>> >
>>>> >how adout this idea:
>>>> >1、split the starfive_hdmi.c under verisilicion to  inno_hdmi.c(the common part), inno_hdmi-starfive.c(the soc specific part)
>>>> >2. move the common part inno_hdmi.c to drivers/gpu/drm/bridge/innosilicon/
>>>> >3. In the future, inno hdmi.c under rockchip will reuse the public driver.
>>>> 
>>>> I am not sure if drm maintainers are happy with this。
>>> 
>>> Not really, no.
>>> 
>>> Because we would still have two drivers for the same controller, and a
>>> common one that haven't really been tested on anything but a single
>>> platform. So arguably a worse situation than what you were suggesting in
>>> the first place.
>>> 
>>> The best solution would be to find someone with a Rockchip board to test
>>> your changes, or to get one if it's doable so you can test yourself.
>>
>>ok I will also try to buy a Rockchip 3036 board for self-test.
>>According to the commit log idea provided by Andy before, make the inno_hdmi driver common module.
> 
> I finally  make my rk3036 based kylin board bootup (use a linux 4.4 downstream bsp,I will find time to try boot
> it with mainline)。 So I can help do the test for rockchip side。
> 
> It seems not that easy to buy a rk3036 based board from market now。
en, The online store seems to have stopped selling rk3036 
really not easy to buy one , I write the code first , need help testing rk3036 in the future.

thanks
> 
>>
>>would the steps be ok? (if I tested rockchip and starifve pass)
>>1. split the inno_hdmi.c under rockchip to  inno_hdmi.c(the common part), inno_hdmi-rockchip.c(the soc specific part)
>>2. move the common part inno_hdmi.c to drivers/gpu/drm/bridge/innosilicon/
>>3. add startfive specific part, inno_hdmi-startfive.c
>>
>>Thanks
>>> 
>>> Maxime

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

* Re:Re: [v3 5/6] drm/vs: Add hdmi driver
  2023-12-13  1:40                           ` Keith Zhao
@ 2023-12-14  2:51                             ` Andy Yan
  0 siblings, 0 replies; 52+ messages in thread
From: Andy Yan @ 2023-12-14  2:51 UTC (permalink / raw)
  To: Keith Zhao
  Cc: devicetree, aou, suijingfeng, krzysztof.kozlowski+dt,
	paul.walmsley, linux-kernel, dri-devel, Xingyu Wu, Jack Zhu,
	palmer, Maxime Ripard, tzimmermann, William Qiu, Shengyang Chen,
	linux-riscv, Changhuang Liang


Hi Keith:

在 2023-12-13 09:40:31,"Keith Zhao" <keith.zhao@starfivetech.com> 写道:
>
>
>On 2023/12/11 20:13, Andy Yan wrote:
>> Hi Keith:
>> 
>> 在 2023-12-11 18:24:35,"Keith Zhao" <keith.zhao@starfivetech.com> 写道:
>>>hi Maxime:
>>>hi Andy:
>>>
>>>On 2023/12/8 17:14, Maxime Ripard wrote:
>>>> Hi,
>>>> 
>>>> On Fri, Dec 08, 2023 at 11:23:37AM +0800, Andy Yan wrote:
>>>>> 在 2023-12-08 11:00:31,"Keith Zhao" <keith.zhao@starfivetech.com> 写道:
>>>>> >
>>>>> >
>>>>> >On 2023/12/8 8:37, Andy Yan wrote:
>>>>> >> Hi Keth:
>>>>> >> 
>>>>> >> 
>>>>> >> 
>>>>> >> 
>>>>> >> 
>>>>> >> 
>>>>> >> 在 2023-12-07 18:48:13,"Keith Zhao" <keith.zhao@starfivetech.com> 写道:
>>>>> >>>
>>>>> >>>
>>>>> >>>On 2023/12/7 17:02, Andy Yan wrote:
>>>>> >>>> 
>>>>> >>>> 
>>>>> >>>> 
>>>>> >>>> 
>>>>> >>>> Hi Keith:
>>>>> >>>> 
>>>>> >>>> 
>>>>> >>>> 
>>>>> >>>> 
>>>>> >>>> 
>>>>> >>>> 
>>>>> >>>> 
>>>>> >>>> 
>>>>> >>>> 
>>>>> >>>> 
>>>>> >>>> 
>>>>> >>>> At 2023-12-06 22:11:33, "Keith Zhao" <keith.zhao@starfivetech.com> wrote:
>>>>> >>>>>
>>>>> >>>>>
>>>>> >>>>>On 2023/12/6 20:56, Maxime Ripard wrote:
>>>>> >>>>>> On Wed, Dec 06, 2023 at 08:02:55PM +0800, Keith Zhao wrote:
>>>>> >>>>>>> >> +static const struct of_device_id starfive_hdmi_dt_ids[] = {
>>>>> >>>>>>> >> +	{ .compatible = "starfive,jh7110-inno-hdmi",},
>>>>> >>>>>>> > 
>>>>> >>>>>>> > So it's inno hdmi, just like Rockchip then?
>>>>> >>>>>>> > 
>>>>> >>>>>>> > This should be a common driver.
>>>>> >>>>>>>
>>>>> >>>>>>> Rockchip has a inno hdmi IP. and Starfive has a inno hdmi IP.
>>>>> >>>>>>> but the harewawre difference of them is big , it is not easy to use the common driver
>>>>> >>>>>>> maybe i need the inno hdmi version here to make a distinction
>>>>> >>>>>> 
>>>>> >>>>>> I just had a look at the rockchip header file: all the registers but the
>>>>> >>>>>> STARFIVE_* ones are identical.
>>>>> >>>>>> 
>>>>> >>>>>> There's no need to have two identical drivers then, please use the
>>>>> >>>>>> rockchip driver instead.
>>>>> >>>>>> 
>>>>> >>>>>> Maxime
>>>>> >>>>>
>>>>> >>>>>ok, have a simple test , edid can get . i will continue 
>>>>> >>>> 
>>>>> >>>> Maybe you can take drivers/gpu/drm/bridge/synopsys/dw-hdmi as a reference, this
>>>>> >>>> is also a hdmi ip used by rockchip/meson/sunxi/jz/imx。
>>>>> >>>> We finally make it share one driver。
>>>>> >>>>>
>>>>> >>>hi Andy:
>>>>> >>>
>>>>> >>>dw_hdmi seems a good choice , it can handle inno hdmi hardware by define its dw_hdmi_plat_data.
>>>>> >>>does it means i can write own driver files such as(dw_hdmi-starfive.c) based on dw_hdmi instead of add plat_data in inno_hdmi.c
>>>>> >>>
>>>>> >> 
>>>>> >> I think the process maybe like this:
>>>>> >> 
>>>>> >> 1. split the inno_hdmi.c under rockchip to  inno_hdmi.c(the common part), inno_hdmi-rockchip.c(the soc specific part)
>>>>> >> 2. move the common part inno_hdmi.c to drivers/gpu/drm/bridge/innosilicon/
>>>>> >> 3. add startfive specific part, inno_hdmi-startfive.c
>>>>> >> 
>>>>> >> bellow git log from kernel three show how we convert  dw_hdmi to a common driver: 
>>>>> >> 
>>>>> >> 
>>>>> >> 
>>>>> >> 12b9f204e804 drm: bridge/dw_hdmi: add rockchip rk3288 support
>>>>> >> 74af9e4d03b8 dt-bindings: Add documentation for rockchip dw hdmi
>>>>> >> d346c14eeea9 drm: bridge/dw_hdmi: add function dw_hdmi_phy_enable_spare
>>>>> >> a4d3b8b050d5 drm: bridge/dw_hdmi: clear i2cmphy_stat0 reg in hdmi_phy_wait_i2c_done
>>>>> >> 632d035bace2 drm: bridge/dw_hdmi: add mode_valid support
>>>>> >> 0cd9d1428322 drm: bridge/dw_hdmi: add support for multi-byte register width access
>>>>> >> cd152393967e dt-bindings: add document for dw_hdmi
>>>>> >> b21f4b658df8 drm: imx: imx-hdmi: move imx-hdmi to bridge/dw_hdmi
>>>>> >> aaa757a092c2 drm: imx: imx-hdmi: split phy configuration to platform driver
>>>>> >> 3d1b35a3d9f3 drm: imx: imx-hdmi: convert imx-hdmi to drm_bridge mode
>>>>> >> c2c3848851a7 drm: imx: imx-hdmi: return defer if can't get ddc i2c adapter
>>>>> >> b587833933de drm: imx: imx-hdmi: make checkpatch happy
>>>>> >> 
>>>>> >hi Andy:
>>>>> >I got you means, 
>>>>> >as I don't have a rockchip board on hand , to split the inno_hdmi.c can not be tested.
>>>>> >
>>>>> >how adout this idea:
>>>>> >1、split the starfive_hdmi.c under verisilicion to  inno_hdmi.c(the common part), inno_hdmi-starfive.c(the soc specific part)
>>>>> >2. move the common part inno_hdmi.c to drivers/gpu/drm/bridge/innosilicon/
>>>>> >3. In the future, inno hdmi.c under rockchip will reuse the public driver.
>>>>> 
>>>>> I am not sure if drm maintainers are happy with this。
>>>> 
>>>> Not really, no.
>>>> 
>>>> Because we would still have two drivers for the same controller, and a
>>>> common one that haven't really been tested on anything but a single
>>>> platform. So arguably a worse situation than what you were suggesting in
>>>> the first place.
>>>> 
>>>> The best solution would be to find someone with a Rockchip board to test
>>>> your changes, or to get one if it's doable so you can test yourself.
>>>
>>>ok I will also try to buy a Rockchip 3036 board for self-test.
>>>According to the commit log idea provided by Andy before, make the inno_hdmi driver common module.
>> 
>> I finally  make my rk3036 based kylin board bootup (use a linux 4.4 downstream bsp,I will find time to try boot
>> it with mainline)。 So I can help do the test for rockchip side。
>> 
>> It seems not that easy to buy a rk3036 based board from market now。
>en, The online store seems to have stopped selling rk3036 
>really not easy to buy one , I write the code first , need help testing rk3036 in the future.


I found A RK3128 based board XPI-3128 is for sale[0], which also has a inno-hdmi.
And Alx Bee is working on adding upstream support for it[1].

[0] https://www.geniatech.com/product/xpi-3128/

[1]https://patchwork.kernel.org/project/linux-arm-kernel/cover/20231213195125.212923-1-knaerzche@gmail.com/
>
>thanks
>> 
>>>
>>>would the steps be ok? (if I tested rockchip and starifve pass)
>>>1. split the inno_hdmi.c under rockchip to  inno_hdmi.c(the common part), inno_hdmi-rockchip.c(the soc specific part)
>>>2. move the common part inno_hdmi.c to drivers/gpu/drm/bridge/innosilicon/
>>>3. add startfive specific part, inno_hdmi-startfive.c
>>>
>>>Thanks
>>>> 
>>>> Maxime

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

* Re: [v3 4/6] drm/vs: Add KMS crtc&plane
  2023-12-07 11:31     ` Keith Zhao
  2023-12-07 15:29       ` Icenowy Zheng
@ 2023-12-27  3:57       ` Icenowy Zheng
  1 sibling, 0 replies; 52+ messages in thread
From: Icenowy Zheng @ 2023-12-27  3:57 UTC (permalink / raw)
  To: Keith Zhao, devicetree, dri-devel, linux-kernel, linux-riscv
  Cc: aou, suijingfeng, krzysztof.kozlowski+dt, William Qiu, mripard,
	Xingyu Wu, Jack Zhu, palmer, tzimmermann, paul.walmsley,
	Shengyang Chen, Changhuang Liang

在 2023-12-07星期四的 19:31 +0800,Keith Zhao写道:
> 
> 
> On 2023/12/7 16:41, Icenowy Zheng wrote:
> > 在 2023-12-04星期一的 20:33 +0800,Keith Zhao写道:
> > *snip*
> > 
> > > +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;
> > 
> > From my experiments on poking with registers on T-Head TH1520 (also
> > uses DC8200 display controller and a similar driver), the DC8200
> > hardware have a different definition of cursor position X and Y
> > with
> > the CRTC plane state.
> > 
> > For CRTC plane state, hot_x and hot_y are only provided as
> > reference,
> > and the cursor should be displayed with its (0,0) drawn to (crtc_x,
> > crtc_y) ([XY]_crtc are values specified in CRTC state, the right
> > part
> > of the assignments here), when the cursor is moved to (0,0) but the
> > hot
> > point is not (0,0), it could be negative.
> > 
> > However, for DC8200 registers definition, cursor XY position could
> > not
> > be negative -- the cursor will disappear then; because in its
> > definition, the cursor XY position should be where the cursor is
> > pointing to, instead of its (0,0). DC8200 will draw (0,0) of the
> > cursor
> > to (x - hot_x, y - hot_y). So to met the expectation of the KMS
> > plane
> > settings, the DC8200 position should be set to (crtc_x + hot_x,
> > crtc_y
> > + hot_y) instead. Thus these two lines of code should be:
> > 
> > ```
> >         cursor.x = state->crtc_x + drm_fb->hot_x;
> >         cursor.y = state->crtc_y + drm_fb->hot_y;
> > ```

Well I realized that this change is not correct too: when moving the
mouse cursor with the screen rotated, the mouse cursor will disappear
at some screen border.

My current idea is:

As the CRTC hot point is just a reference, we can just ignore it, and
use the HW hot point to implement negative cursor position.

The patch is like the follow:

-    cursor.x = state->crtc_x;
-    cursor.y = state->crtc_y;
-    cursor.hot_x = drm_fb->hot_x;
-    cursor.hot_y = drm_fb->hot_y;
+    if (state->crtc_x > 0) {
+        cursor.x = state->crtc_x;
+        cursor.hot_x = 0;
+    } else {
+        cursor.hot_x = -state->crtc_x;
+        cursor.x = 0;
+    }
+    if (state->crtc_y > 0) {
+        cursor.y = state->crtc_y;
+        cursor.hot_y = 0;
+    } else {
+        cursor.hot_y = -state->crtc_y;
+        cursor.y = 0;
+    }

drm_fb could just be removed in this function then because it's no
longer needed (it's used to get the cursor's hot point, which we
ignored now).

> > 
> > 
> > > +       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);
> > > +}
> > *snip
> hello Icenowy:
> you are deep understanding on dc8200.
> by the way of practice
> I tested this change on the debian desktop, is there a way to compare
> the cursor behavior change?
> Thanks
> 
> 
> 


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

* 回复: [v3 4/6] drm/vs: Add KMS crtc&plane
  2023-12-06  8:55   ` Maxime Ripard
  2023-12-06 12:53     ` Keith Zhao
@ 2024-01-31  8:57     ` Keith Zhao
  2024-01-31 16:30       ` Maxime Ripard
  2024-01-31  9:33     ` Keith Zhao
  2 siblings, 1 reply; 52+ messages in thread
From: Keith Zhao @ 2024-01-31  8:57 UTC (permalink / raw)
  To: Maxime Ripard, Keith Zhao
  Cc: devicetree, aou, krzysztof.kozlowski+dt, paul.walmsley,
	linux-kernel, dri-devel, Xingyu Wu, Jack Zhu, palmer,
	tzimmermann, William Qiu, Shengyang Chen, linux-riscv,
	Changhuang Liang



> -----邮件原件-----
> 发件人: Maxime Ripard <mripard@kernel.org>
> 发送时间: 2023年12月6日 16:56
> 收件人: Keith Zhao <keith.zhao@starfivetech.com>
> 抄送: devicetree@vger.kernel.org; dri-devel@lists.freedesktop.org;
> linux-kernel@vger.kernel.org; linux-riscv@lists.infradead.org;
> tzimmermann@suse.de; airlied@gmail.com; krzysztof.kozlowski+dt@linaro.org;
> William Qiu <william.qiu@starfivetech.com>; Xingyu Wu
> <xingyu.wu@starfivetech.com>; paul.walmsley@sifive.com;
> aou@eecs.berkeley.edu; palmer@dabbelt.com; p.zabel@pengutronix.de;
> Shengyang Chen <shengyang.chen@starfivetech.com>; Jack Zhu
> <jack.zhu@starfivetech.com>; Changhuang Liang
> <changhuang.liang@starfivetech.com>; maarten.lankhorst@linux.intel.com;
> suijingfeng@loongson.cn
> 主题: Re: [v3 4/6] drm/vs: Add KMS crtc&plane
> 
> On Mon, Dec 04, 2023 at 08:33:13PM +0800, Keith Zhao wrote:
> > +static const struct vs_plane_info dc_hw_planes_rev0[PLANE_NUM] = {
> > +	{
> > +		.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,
> > +	},
> > +};
> > +
> > +static const struct vs_dc_info dc8200_info = {
> > +	.name			= "DC8200",
> > +	.panel_num		= 2,
> > +	.plane_num		= 8,
> > +	.planes			= dc_hw_planes_rev0,
> > +	.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,
> > +};
> 
> I really think that entire thing is to workaround a suboptimal device tree binding.
> You should have two CRTCs in the device tree, you'll probe twice, and you won't
> get to do that whole dance.
> 
Hi Maxime:
I tried to modify it according to this idea Found it too difficult In terms of hardware, 
the two crtc designs are too close to separate, and they are even designed into the same reg with different bits representing crtc0 and crtc1.
It seems not easy to described the 2 ctrc hardware by 2 device nodes

The idea is to avoid a whole dance
I don't know if I understand correctly about whole dance.
Is it means I create 2 ctrc and 8 plane in the dc_bind?

Thanks
Keith
> 
> > +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;
> > +};
> 
> That's your plane state.
> 
> > +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;
> > +};
> 
> Your framebuffer
> 
> > +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;
> > +};
> 
> Your CRTC state.
> 
> > +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;
> > +};
> 
> Your display mode.
> 
> Really, you have the huge majority of those informations already available,
> there's no need to duplicate it. And chances are you'll create bugs in the
> process.
> Maxime

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

* [v3 4/6] drm/vs: Add KMS crtc&plane
  2023-12-06  8:55   ` Maxime Ripard
  2023-12-06 12:53     ` Keith Zhao
  2024-01-31  8:57     ` 回复: " Keith Zhao
@ 2024-01-31  9:33     ` Keith Zhao
  2024-01-31 13:23       ` Maxime Ripard
  2 siblings, 1 reply; 52+ messages in thread
From: Keith Zhao @ 2024-01-31  9:33 UTC (permalink / raw)
  To: Maxime Ripard, Keith Zhao
  Cc: devicetree, aou, krzysztof.kozlowski+dt, paul.walmsley,
	linux-kernel, dri-devel, Xingyu Wu, Jack Zhu, palmer,
	tzimmermann, William Qiu, Shengyang Chen, linux-riscv,
	Changhuang Liang



> -----邮件原件-----
> 发件人: Maxime Ripard <mripard@kernel.org>
> 发送时间: 2023年12月6日 16:56
> 收件人: Keith Zhao <keith.zhao@starfivetech.com>
> 抄送: devicetree@vger.kernel.org; dri-devel@lists.freedesktop.org;
> linux-kernel@vger.kernel.org; linux-riscv@lists.infradead.org;
> tzimmermann@suse.de; airlied@gmail.com; krzysztof.kozlowski+dt@linaro.org;
> William Qiu <william.qiu@starfivetech.com>; Xingyu Wu
> <xingyu.wu@starfivetech.com>; paul.walmsley@sifive.com;
> aou@eecs.berkeley.edu; palmer@dabbelt.com; p.zabel@pengutronix.de;
> Shengyang Chen <shengyang.chen@starfivetech.com>; Jack Zhu
> <jack.zhu@starfivetech.com>; Changhuang Liang
> <changhuang.liang@starfivetech.com>; maarten.lankhorst@linux.intel.com;
> suijingfeng@loongson.cn
> 主题: Re: [v3 4/6] drm/vs: Add KMS crtc&plane
> 
> On Mon, Dec 04, 2023 at 08:33:13PM +0800, Keith Zhao wrote:
> > +static const struct vs_plane_info dc_hw_planes_rev0[PLANE_NUM] = {
> > +	{
> > +		.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,
> > +	},
> > +};
> > +
> > +static const struct vs_dc_info dc8200_info = {
> > +	.name			= "DC8200",
> > +	.panel_num		= 2,
> > +	.plane_num		= 8,
> > +	.planes			= dc_hw_planes_rev0,
> > +	.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,
> > +};
> 
> I really think that entire thing is to workaround a suboptimal device tree binding.
> You should have two CRTCs in the device tree, you'll probe twice, and you won't
> get to do that whole dance.
> 
Hi Maxime:
I tried to modify it according to this idea Found it too difficult In terms of hardware, the two crtc designs are too close to separate, and they are even designed into the same reg with different bits representing crtc0 and crtc1.
It seems not easy to described the 2 ctrc hardware by 2 device nodes

The idea is to avoid a whole dance
I don't know if I understand correctly about whole dance.
Is it means I create 2 ctrc and 8 plane in the dc_bind?

Thanks
Keith
> 
> > +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;
> > +};
> 
> That's your plane state.
> 
> > +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;
> > +};
> 
> Your framebuffer
> 
> > +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;
> > +};
> 
> Your CRTC state.
> 
> > +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;
> > +};
> 
> Your display mode.
> 
> Really, you have the huge majority of those informations already available,
> there's no need to duplicate it. And chances are you'll create bugs in the
> process.
> Maxime

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

* Re: [v3 4/6] drm/vs: Add KMS crtc&plane
  2024-01-31  9:33     ` Keith Zhao
@ 2024-01-31 13:23       ` Maxime Ripard
  2024-02-01  2:22         ` Keith Zhao
  0 siblings, 1 reply; 52+ messages in thread
From: Maxime Ripard @ 2024-01-31 13:23 UTC (permalink / raw)
  To: Keith Zhao
  Cc: devicetree, aou, krzysztof.kozlowski+dt, paul.walmsley,
	linux-kernel, dri-devel, Xingyu Wu, Jack Zhu, palmer,
	tzimmermann, William Qiu, Shengyang Chen, linux-riscv,
	Changhuang Liang

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

On Wed, Jan 31, 2024 at 09:33:06AM +0000, Keith Zhao wrote:
> 
> 
> > -----邮件原件-----
> > 发件人: Maxime Ripard <mripard@kernel.org>
> > 发送时间: 2023年12月6日 16:56
> > 收件人: Keith Zhao <keith.zhao@starfivetech.com>
> > 抄送: devicetree@vger.kernel.org; dri-devel@lists.freedesktop.org;
> > linux-kernel@vger.kernel.org; linux-riscv@lists.infradead.org;
> > tzimmermann@suse.de; airlied@gmail.com; krzysztof.kozlowski+dt@linaro.org;
> > William Qiu <william.qiu@starfivetech.com>; Xingyu Wu
> > <xingyu.wu@starfivetech.com>; paul.walmsley@sifive.com;
> > aou@eecs.berkeley.edu; palmer@dabbelt.com; p.zabel@pengutronix.de;
> > Shengyang Chen <shengyang.chen@starfivetech.com>; Jack Zhu
> > <jack.zhu@starfivetech.com>; Changhuang Liang
> > <changhuang.liang@starfivetech.com>; maarten.lankhorst@linux.intel.com;
> > suijingfeng@loongson.cn
> > 主题: Re: [v3 4/6] drm/vs: Add KMS crtc&plane
> > 
> > On Mon, Dec 04, 2023 at 08:33:13PM +0800, Keith Zhao wrote:
> > > +static const struct vs_plane_info dc_hw_planes_rev0[PLANE_NUM] = {
> > > +	{
> > > +		.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,
> > > +	},
> > > +};
> > > +
> > > +static const struct vs_dc_info dc8200_info = {
> > > +	.name			= "DC8200",
> > > +	.panel_num		= 2,
> > > +	.plane_num		= 8,
> > > +	.planes			= dc_hw_planes_rev0,
> > > +	.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,
> > > +};
> > 
> > I really think that entire thing is to workaround a suboptimal device tree binding.
> > You should have two CRTCs in the device tree, you'll probe twice, and you won't
> > get to do that whole dance.
> > 

> I tried to modify it according to this idea Found it too difficult In
> terms of hardware, the two crtc designs are too close to separate, and
> they are even designed into the same reg with different bits
> representing crtc0 and crtc1. It seems not easy to described the 2
> ctrc hardware by 2 device nodes

What are these bits doing?

> The idea is to avoid a whole dance I don't know if I understand
> correctly about whole dance. Is it means I create 2 ctrc and 8 plane
> in the dc_bind?

Yeah, you should strive to make it two separate devices.

Maxime

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

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

* Re: 回复: [v3 4/6] drm/vs: Add KMS crtc&plane
  2024-01-31  8:57     ` 回复: " Keith Zhao
@ 2024-01-31 16:30       ` Maxime Ripard
  0 siblings, 0 replies; 52+ messages in thread
From: Maxime Ripard @ 2024-01-31 16:30 UTC (permalink / raw)
  To: Keith Zhao
  Cc: devicetree, aou, krzysztof.kozlowski+dt, paul.walmsley,
	linux-kernel, dri-devel, Xingyu Wu, Jack Zhu, palmer,
	tzimmermann, William Qiu, Shengyang Chen, linux-riscv,
	Changhuang Liang

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

On Wed, Jan 31, 2024 at 08:57:21AM +0000, Keith Zhao wrote:
> > > +static const struct vs_dc_info dc8200_info = {
> > > +	.name			= "DC8200",
> > > +	.panel_num		= 2,
> > > +	.plane_num		= 8,
> > > +	.planes			= dc_hw_planes_rev0,
> > > +	.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,
> > > +};
> > 
> > I really think that entire thing is to workaround a suboptimal device tree binding.
> > You should have two CRTCs in the device tree, you'll probe twice, and you won't
> > get to do that whole dance.
> > 
> Hi Maxime:
> I tried to modify it according to this idea Found it too difficult In terms of hardware, 
> the two crtc designs are too close to separate, and they are even designed into the same reg with different bits representing crtc0 and crtc1.
> It seems not easy to described the 2 ctrc hardware by 2 device nodes
> 
> The idea is to avoid a whole dance
> I don't know if I understand correctly about whole dance.
> Is it means I create 2 ctrc and 8 plane in the dc_bind?

It looks like you just sent the same mail?

Maxime

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

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

* RE: [v3 4/6] drm/vs: Add KMS crtc&plane
  2024-01-31 13:23       ` Maxime Ripard
@ 2024-02-01  2:22         ` Keith Zhao
  2024-02-09 15:37           ` Maxime Ripard
  0 siblings, 1 reply; 52+ messages in thread
From: Keith Zhao @ 2024-02-01  2:22 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: devicetree, aou, krzysztof.kozlowski+dt, paul.walmsley,
	linux-kernel, dri-devel, Xingyu Wu, Jack Zhu, palmer,
	tzimmermann, William Qiu, Shengyang Chen, linux-riscv,
	Changhuang Liang


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





> -----Original Message-----

> From: Maxime Ripard <mripard@kernel.org>

> Sent: 2024年1月31日 21:24

> To: Keith Zhao <keith.zhao@starfivetech.com>

> Cc: devicetree@vger.kernel.org; dri-devel@lists.freedesktop.org;

> linux-kernel@vger.kernel.org; linux-riscv@lists.infradead.org;

> tzimmermann@suse.de; airlied@gmail.com; krzysztof.kozlowski+dt@linaro.org;

> William Qiu <william.qiu@starfivetech.com>; Xingyu Wu

> <xingyu.wu@starfivetech.com>; paul.walmsley@sifive.com;

> aou@eecs.berkeley.edu; palmer@dabbelt.com; p.zabel@pengutronix.de;

> Shengyang Chen <shengyang.chen@starfivetech.com>; Jack Zhu

> <jack.zhu@starfivetech.com>; Changhuang Liang

> <changhuang.liang@starfivetech.com>; maarten.lankhorst@linux.intel.com

> Subject: Re: [v3 4/6] drm/vs: Add KMS crtc&plane

>

> On Wed, Jan 31, 2024 at 09:33:06AM +0000, Keith Zhao wrote:

> >

> >

> > > -----邮件原件-----

> > > 发件人: Maxime Ripard <mripard@kernel.org<mailto:mripard@kernel.org>>

> > > 发送时间: 2023年12月6日 16:56

> > > 收件人: Keith Zhao <keith.zhao@starfivetech.com<mailto:keith.zhao@starfivetech.com>>

> > > 抄送: devicetree@vger.kernel.org<mailto:devicetree@vger.kernel.org>; dri-devel@lists.freedesktop.org<mailto:dri-devel@lists.freedesktop.org>;

> > > linux-kernel@vger.kernel.org<mailto:linux-kernel@vger.kernel.org>; linux-riscv@lists.infradead.org<mailto:linux-riscv@lists.infradead.org>;

> > > tzimmermann@suse.de<mailto:tzimmermann@suse.de>; airlied@gmail.com<mailto:airlied@gmail.com>;

> > > krzysztof.kozlowski+dt@linaro.org<mailto:krzysztof.kozlowski+dt@linaro.org>;

> > > William Qiu <william.qiu@starfivetech.com<mailto:william.qiu@starfivetech.com>>; Xingyu Wu

> > > <xingyu.wu@starfivetech.com<mailto:xingyu.wu@starfivetech.com>>; paul.walmsley@sifive.com<mailto:paul.walmsley@sifive.com>;

> > > aou@eecs.berkeley.edu<mailto:aou@eecs.berkeley.edu>; palmer@dabbelt.com<mailto:palmer@dabbelt.com>; p.zabel@pengutronix.de<mailto:p.zabel@pengutronix.de>;

> > > Shengyang Chen <shengyang.chen@starfivetech.com<mailto:shengyang.chen@starfivetech.com>>; Jack Zhu

> > > <jack.zhu@starfivetech.com<mailto:jack.zhu@starfivetech.com>>; Changhuang Liang

> > > <changhuang.liang@starfivetech.com<mailto:changhuang.liang@starfivetech.com>>;

> > > maarten.lankhorst@linux.intel.com<mailto:maarten.lankhorst@linux.intel.com>;

> > > suijingfeng@loongson.cn<mailto:suijingfeng@loongson.cn>

> > > 主题: Re: [v3 4/6] drm/vs: Add KMS crtc&plane

> > >

> > > On Mon, Dec 04, 2023 at 08:33:13PM +0800, Keith Zhao wrote:

> > > > +static const struct vs_plane_info dc_hw_planes_rev0[PLANE_NUM] = {

> > > > +   {

> > > > +            .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,

> > > > +   },

> > > > +};

> > > > +

> > > > +static const struct vs_dc_info dc8200_info = {

> > > > +   .name                         = "DC8200",

> > > > +   .panel_num               = 2,

> > > > +   .plane_num               = 8,

> > > > +   .planes                        = dc_hw_planes_rev0,

> > > > +   .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,

> > > > +};

> > >

> > > I really think that entire thing is to workaround a suboptimal device tree

> binding.

> > > You should have two CRTCs in the device tree, you'll probe twice,

> > > and you won't get to do that whole dance.

> > >

>

> > I tried to modify it according to this idea Found it too difficult In

> > terms of hardware, the two crtc designs are too close to separate, and

> > they are even designed into the same reg with different bits

> > representing crtc0 and crtc1. It seems not easy to described the 2

> > ctrc hardware by 2 device nodes

>

> What are these bits doing?



[cid:image001.png@01DA54F5.ECEA9C10]

For example:

like this , bit0 and bit1 match ctrc0 and crtc1 , it is used to start crtc

A similar situation exists for other register definitions.



Another case is that ctrc0 and crtc1 have the same function and their offset is continuous,

instead of crtc0 being a continuous area, crtc1 being a continuous area

reg:

crtc0 X

crtc1 X+4

crtc0 X+8

crtc1 X+12

if to make it two separate devices, the device node dts reg attribute would be complex



>

> > The idea is to avoid a whole dance I don't know if I understand

> > correctly about whole dance. Is it means I create 2 ctrc and 8 plane

> > in the dc_bind?

>

> Yeah, you should strive to make it two separate devices.

>

> Maxime

[-- Attachment #1.2: Type: text/html, Size: 76101 bytes --]

[-- Attachment #2: image001.png --]
[-- Type: image/png, Size: 70280 bytes --]

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

* Re: RE: [v3 4/6] drm/vs: Add KMS crtc&plane
  2024-02-01  2:22         ` Keith Zhao
@ 2024-02-09 15:37           ` Maxime Ripard
  0 siblings, 0 replies; 52+ messages in thread
From: Maxime Ripard @ 2024-02-09 15:37 UTC (permalink / raw)
  To: Keith Zhao
  Cc: devicetree, dri-devel, linux-kernel, linux-riscv, tzimmermann,
	airlied, krzysztof.kozlowski+dt, William Qiu, Xingyu Wu,
	paul.walmsley, aou, palmer, p.zabel, Shengyang Chen, Jack Zhu,
	Changhuang Liang, maarten.lankhorst

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

On Thu, Feb 01, 2024 at 02:22:16AM +0000, Keith Zhao wrote:
> 
> 
> 
> 
> > -----Original Message-----
> 
> > From: Maxime Ripard <mripard@kernel.org>
> 
> > Sent: 2024年1月31日 21:24
> 
> > To: Keith Zhao <keith.zhao@starfivetech.com>
> 
> > Cc: devicetree@vger.kernel.org; dri-devel@lists.freedesktop.org;
> 
> > linux-kernel@vger.kernel.org; linux-riscv@lists.infradead.org;
> 
> > tzimmermann@suse.de; airlied@gmail.com; krzysztof.kozlowski+dt@linaro.org;
> 
> > William Qiu <william.qiu@starfivetech.com>; Xingyu Wu
> 
> > <xingyu.wu@starfivetech.com>; paul.walmsley@sifive.com;
> 
> > aou@eecs.berkeley.edu; palmer@dabbelt.com; p.zabel@pengutronix.de;
> 
> > Shengyang Chen <shengyang.chen@starfivetech.com>; Jack Zhu
> 
> > <jack.zhu@starfivetech.com>; Changhuang Liang
> 
> > <changhuang.liang@starfivetech.com>; maarten.lankhorst@linux.intel.com
> 
> > Subject: Re: [v3 4/6] drm/vs: Add KMS crtc&plane
> 
> >
> 
> > On Wed, Jan 31, 2024 at 09:33:06AM +0000, Keith Zhao wrote:
> 
> > >
> 
> > >
> 
> > > > -----邮件原件-----
> 
> > > > 发件人: Maxime Ripard <mripard@kernel.org<mailto:mripard@kernel.org>>
> 
> > > > 发送时间: 2023年12月6日 16:56
> 
> > > > 收件人: Keith Zhao <keith.zhao@starfivetech.com<mailto:keith.zhao@starfivetech.com>>
> 
> > > > 抄送: devicetree@vger.kernel.org<mailto:devicetree@vger.kernel.org>; dri-devel@lists.freedesktop.org<mailto:dri-devel@lists.freedesktop.org>;
> 
> > > > linux-kernel@vger.kernel.org<mailto:linux-kernel@vger.kernel.org>; linux-riscv@lists.infradead.org<mailto:linux-riscv@lists.infradead.org>;
> 
> > > > tzimmermann@suse.de<mailto:tzimmermann@suse.de>; airlied@gmail.com<mailto:airlied@gmail.com>;
> 
> > > > krzysztof.kozlowski+dt@linaro.org<mailto:krzysztof.kozlowski+dt@linaro.org>;
> 
> > > > William Qiu <william.qiu@starfivetech.com<mailto:william.qiu@starfivetech.com>>; Xingyu Wu
> 
> > > > <xingyu.wu@starfivetech.com<mailto:xingyu.wu@starfivetech.com>>; paul.walmsley@sifive.com<mailto:paul.walmsley@sifive.com>;
> 
> > > > aou@eecs.berkeley.edu<mailto:aou@eecs.berkeley.edu>; palmer@dabbelt.com<mailto:palmer@dabbelt.com>; p.zabel@pengutronix.de<mailto:p.zabel@pengutronix.de>;
> 
> > > > Shengyang Chen <shengyang.chen@starfivetech.com<mailto:shengyang.chen@starfivetech.com>>; Jack Zhu
> 
> > > > <jack.zhu@starfivetech.com<mailto:jack.zhu@starfivetech.com>>; Changhuang Liang
> 
> > > > <changhuang.liang@starfivetech.com<mailto:changhuang.liang@starfivetech.com>>;
> 
> > > > maarten.lankhorst@linux.intel.com<mailto:maarten.lankhorst@linux.intel.com>;
> 
> > > > suijingfeng@loongson.cn<mailto:suijingfeng@loongson.cn>
> 
> > > > 主题: Re: [v3 4/6] drm/vs: Add KMS crtc&plane
> 
> > > >
> 
> > > > On Mon, Dec 04, 2023 at 08:33:13PM +0800, Keith Zhao wrote:
> 
> > > > > +static const struct vs_plane_info dc_hw_planes_rev0[PLANE_NUM] = {
> 
> > > > > +   {
> 
> > > > > +            .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,
> 
> > > > > +   },
> 
> > > > > +};
> 
> > > > > +
> 
> > > > > +static const struct vs_dc_info dc8200_info = {
> 
> > > > > +   .name                         = "DC8200",
> 
> > > > > +   .panel_num               = 2,
> 
> > > > > +   .plane_num               = 8,
> 
> > > > > +   .planes                        = dc_hw_planes_rev0,
> 
> > > > > +   .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,
> 
> > > > > +};
> 
> > > >
> 
> > > > I really think that entire thing is to workaround a suboptimal device tree
> 
> > binding.
> 
> > > > You should have two CRTCs in the device tree, you'll probe twice,
> 
> > > > and you won't get to do that whole dance.
> 
> > > >
> 
> >
> 
> > > I tried to modify it according to this idea Found it too difficult In
> 
> > > terms of hardware, the two crtc designs are too close to separate, and
> 
> > > they are even designed into the same reg with different bits
> 
> > > representing crtc0 and crtc1. It seems not easy to described the 2
> 
> > > ctrc hardware by 2 device nodes
> 
> >
> 
> > What are these bits doing?
> 
> 
> 
> [cid:image001.png@01DA54F5.ECEA9C10]
> 
> For example:
> 
> like this , bit0 and bit1 match ctrc0 and crtc1 , it is used to start crtc
> 
> A similar situation exists for other register definitions.
> 
> 
> 
> Another case is that ctrc0 and crtc1 have the same function and their offset is continuous,
> 
> instead of crtc0 being a continuous area, crtc1 being a continuous area
> 
> reg:
> 
> crtc0 X
> 
> crtc1 X+4
> 
> crtc0 X+8
> 
> crtc1 X+12
> 
> if to make it two separate devices, the device node dts reg attribute would be complex

Yeah, ok, that makes sense then.

In which case, if we're going to have one big device in the DT with all
the planes and CRTCs, why do we need the display-subsystem device? Just
make your driver probe on the dc8200 device.

Maxime

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

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

* RE: [v3 6/6] drm/vs: simple encoder
  2023-12-05 13:18     ` Dmitry Baryshkov
@ 2024-05-15 10:07       ` Keith Zhao
  2024-05-15 15:17         ` Dmitry Baryshkov
  0 siblings, 1 reply; 52+ messages in thread
From: Keith Zhao @ 2024-05-15 10:07 UTC (permalink / raw)
  To: Dmitry Baryshkov, Keith Zhao
  Cc: devicetree, dri-devel, linux-kernel, linux-riscv, aou,
	suijingfeng, tzimmermann, paul.walmsley, mripard, Xingyu Wu,
	Jack Zhu, palmer, krzysztof.kozlowski+dt, William Qiu,
	Shengyang Chen, Changhuang Liang



> -----Original Message-----
> From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> Sent: 2023年12月5日 21:19
> To: Keith Zhao <keith.zhao@starfivetech.com>
> Cc: devicetree@vger.kernel.org; dri-devel@lists.freedesktop.org;
> linux-kernel@vger.kernel.org; linux-riscv@lists.infradead.org;
> aou@eecs.berkeley.edu; suijingfeng@loongson.cn; tzimmermann@suse.de;
> paul.walmsley@sifive.com; mripard@kernel.org; Xingyu Wu
> <xingyu.wu@starfivetech.com>; Jack Zhu <jack.zhu@starfivetech.com>;
> palmer@dabbelt.com; krzysztof.kozlowski+dt@linaro.org; William Qiu
> <william.qiu@starfivetech.com>; Shengyang Chen
> <shengyang.chen@starfivetech.com>; Changhuang Liang
> <changhuang.liang@starfivetech.com>
> Subject: Re: [v3 6/6] drm/vs: simple encoder
> 
> On Tue, 5 Dec 2023 at 15:14, Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> wrote:
> >
> > On Mon, 4 Dec 2023 at 14:33, Keith Zhao <keith.zhao@starfivetech.com>
> wrote:
> > >
> > > add simple encoder for dsi bridge
> >
> > This doesn't look like a proper commit message.
> >
> > >
> > > Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> > > ---
> > >  drivers/gpu/drm/verisilicon/Makefile        |   4 +-
> > >  drivers/gpu/drm/verisilicon/vs_drv.c        |   2 +
> > >  drivers/gpu/drm/verisilicon/vs_simple_enc.c | 195
> > > ++++++++++++++++++++  drivers/gpu/drm/verisilicon/vs_simple_enc.h |
> > > 23 +++
> > >  4 files changed, 223 insertions(+), 1 deletion(-)  create mode
> > > 100644 drivers/gpu/drm/verisilicon/vs_simple_enc.c
> > >  create mode 100644 drivers/gpu/drm/verisilicon/vs_simple_enc.h
> > >
> > > diff --git a/drivers/gpu/drm/verisilicon/Makefile
> > > b/drivers/gpu/drm/verisilicon/Makefile
> > > index 71fadafcee13..cd5d0a90bcfe 100644
> > > --- a/drivers/gpu/drm/verisilicon/Makefile
> > > +++ b/drivers/gpu/drm/verisilicon/Makefile
> > > @@ -5,6 +5,8 @@ vs_drm-objs := vs_dc_hw.o \
> > >                 vs_crtc.o \
> > >                 vs_drv.o \
> > >                 vs_modeset.o \
> > > -               vs_plane.o
> > > +               vs_plane.o \
> > > +               vs_simple_enc.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/vs_drv.c
> > > b/drivers/gpu/drm/verisilicon/vs_drv.c
> > > index d7e5199fe293..946f137ab124 100644
> > > --- a/drivers/gpu/drm/verisilicon/vs_drv.c
> > > +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> > > @@ -23,6 +23,7 @@
> > >  #include "vs_drv.h"
> > >  #include "vs_modeset.h"
> > >  #include "vs_dc.h"
> > > +#include "vs_simple_enc.h"
> > >
> > >  #define DRV_NAME       "verisilicon"
> > >  #define DRV_DESC       "Verisilicon DRM driver"
> > > @@ -217,6 +218,7 @@ static struct platform_driver *drm_sub_drivers[]
> > > = {  #ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
> > >         &starfive_hdmi_driver,
> > >  #endif
> > > +       &simple_encoder_driver,
> > >  };
> > >
> > >  static struct component_match *vs_drm_match_add(struct device *dev)
> > > diff --git a/drivers/gpu/drm/verisilicon/vs_simple_enc.c
> > > b/drivers/gpu/drm/verisilicon/vs_simple_enc.c
> > > new file mode 100644
> > > index 000000000000..c5a8d82bc469
> > > --- /dev/null
> > > +++ b/drivers/gpu/drm/verisilicon/vs_simple_enc.c
> > > @@ -0,0 +1,195 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
> > > + */
> > > +#include <linux/component.h>
> > > +#include <linux/of_device.h>
> > > +#include <linux/module.h>
> > > +
> > > +#include <drm/drm_atomic_helper.h>
> > > +#include <drm/drm_bridge.h>
> > > +#include <drm/drm_crtc_helper.h>
> > > +#include <drm/drm_of.h>
> > > +#include <linux/regmap.h>
> > > +#include <linux/media-bus-format.h> #include <linux/mfd/syscon.h>
> > > +
> > > +#include "vs_crtc.h"
> > > +#include "vs_simple_enc.h"
> > > +
> > > +static const struct simple_encoder_priv dsi_priv = {
> >
> > Please use proper prefix for all the struct and function names.
> > vs_simple_encoder sounds better. Or vs_dsi_encoder.
> >
> > > +       .encoder_type = DRM_MODE_ENCODER_DSI };
> > > +
> > > +static inline struct simple_encoder *to_simple_encoder(struct
> > > +drm_encoder *enc) {
> > > +       return container_of(enc, struct simple_encoder, encoder); }
> > > +
> > > +static int encoder_parse_dt(struct device *dev) {
> > > +       struct simple_encoder *simple = dev_get_drvdata(dev);
> > > +       unsigned int args[2];
> > > +
> > > +       simple->dss_regmap =
> syscon_regmap_lookup_by_phandle_args(dev->of_node,
> > > +
> "starfive,syscon",
> > > +
> 2,
> > > + args);
> > > +
> > > +       if (IS_ERR(simple->dss_regmap)) {
> > > +               return dev_err_probe(dev,
> PTR_ERR(simple->dss_regmap),
> > > +                                    "getting the regmap
> failed\n");
> > > +       }
> > > +
> > > +       simple->offset = args[0];
> > > +       simple->mask = args[1];
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +void encoder_atomic_enable(struct drm_encoder *encoder,
> > > +                          struct drm_atomic_state *state) {
> > > +       struct simple_encoder *simple = to_simple_encoder(encoder);
> > > +
> > > +       regmap_update_bits(simple->dss_regmap, simple->offset,
> simple->mask,
> > > +                          simple->mask); }
> > > +
> > > +int encoder_atomic_check(struct drm_encoder *encoder,
> > > +                        struct drm_crtc_state *crtc_state,
> > > +                        struct drm_connector_state *conn_state) {
> > > +       struct vs_crtc_state *vs_crtc_state =
> to_vs_crtc_state(crtc_state);
> > > +       struct drm_connector *connector = conn_state->connector;
> > > +       int ret = 0;
> > > +
> > > +       struct drm_bridge *first_bridge =
> drm_bridge_chain_get_first_bridge(encoder);
> > > +       struct drm_bridge_state *bridge_state = ERR_PTR(-EINVAL);
> > > +
> > > +       vs_crtc_state->encoder_type = encoder->encoder_type;
> > > +
> > > +       if (first_bridge && first_bridge->funcs->atomic_duplicate_state)
> > > +               bridge_state =
> > > + drm_atomic_get_bridge_state(crtc_state->state, first_bridge);
> >
> > Please don't poke into others' playground. This should go into your
> > DSI bridge's atomic_check() instead.
> 
> Hmm. And you can not use vs_crtc_state from your bridge. Actually this design
> makes me wonder, how does your hardware work? Is it possible to send the DSI
> commands to the panel?
> 
Hi Dmitry:
1、 This fun is used to check the media bus format whether in support list , if not , this mode will be invalid,
    I just used the bridge api to get the media bus format. (sync with the input bridge(connector) bus format)

2、 the bridge doesn't care the vs_crtc_state,  just do their own drm_bridge_funcs ->atomic_check, 
    or it will be impossible to make this dsi encoder to fit dsi bridge driver.
3、 hardware work :
    encoder_atomic_check get the media bus format and update it to vs_crtc_state-> output_fmt,
    during the crtc_enable , it will write the output_fmt to hardware .

4、	to send dsi command to panel ,  It is only relevant to dsi controllers and panel driver (bridge and panel ),
	It does not involve the logic associated with encoder and crtc

https://elixir.bootlin.com/linux/v6.6.30/source/drivers/gpu/drm/mxsfb/lcdif_kms.c#L457
Do the similar logic

Best wish
> >
> > > +
> > > +       if (IS_ERR(bridge_state)) {
> > > +               if (connector->display_info.num_bus_formats)
> > > +                       vs_crtc_state->output_fmt =
> connector->display_info.bus_formats[0];
> > > +               else
> > > +                       vs_crtc_state->output_fmt =
> MEDIA_BUS_FMT_FIXED;
> > > +       } else {
> > > +               vs_crtc_state->output_fmt =
> bridge_state->input_bus_cfg.format;
> > > +       }
> > > +
> > > +       switch (vs_crtc_state->output_fmt) {
> > > +       case MEDIA_BUS_FMT_FIXED:
> > > +       case MEDIA_BUS_FMT_RGB565_1X16:
> > > +       case MEDIA_BUS_FMT_RGB666_1X18:
> > > +       case MEDIA_BUS_FMT_RGB888_1X24:
> > > +       case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> > > +       case MEDIA_BUS_FMT_RGB101010_1X30:
> > > +       case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> > > +       case MEDIA_BUS_FMT_UYVY8_1X16:
> > > +       case MEDIA_BUS_FMT_YUV8_1X24:
> > > +       case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> > > +       case MEDIA_BUS_FMT_UYVY10_1X20:
> > > +       case MEDIA_BUS_FMT_YUV10_1X30:
> > > +               ret = 0;
> > > +               break;
> > > +       default:
> > > +               ret = -EINVAL;
> > > +               break;
> > > +       }
> > > +
> > > +       /* If MEDIA_BUS_FMT_FIXED, set it to default value */
> > > +       if (vs_crtc_state->output_fmt == MEDIA_BUS_FMT_FIXED)
> > > +               vs_crtc_state->output_fmt =
> > > + MEDIA_BUS_FMT_RGB888_1X24;
> > > +
> > > +       return ret;
> > > +}
> > > +
> > > +static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
> > > +       .atomic_check = encoder_atomic_check,
> > > +       .atomic_enable = encoder_atomic_enable, };
> > > +
> > > +static int encoder_bind(struct device *dev, struct device *master,
> > > +void *data) {
> > > +       struct drm_device *drm_dev = data;
> > > +       struct simple_encoder *simple = dev_get_drvdata(dev);
> > > +       struct drm_encoder *encoder;
> > > +       struct drm_bridge *bridge;
> > > +       int ret;
> > > +
> > > +       encoder = &simple->encoder;
> > > +
> > > +       ret = drmm_encoder_init(drm_dev, encoder, NULL,
> simple->priv->encoder_type, NULL);
> > > +       if (ret)
> > > +               return ret;
> > > +
> > > +       drm_encoder_helper_add(encoder, &encoder_helper_funcs);
> > > +
> > > +       encoder->possible_crtcs =
> > > +                       drm_of_find_possible_crtcs(drm_dev,
> > > + dev->of_node);
> > > +
> > > +       /* output port is port1*/
> > > +       bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
> > > +       if (IS_ERR(bridge))
> > > +               return 0;
> > > +
> > > +       return drm_bridge_attach(encoder, bridge, NULL, 0); }
> > > +
> > > +static const struct component_ops encoder_component_ops = {
> > > +       .bind = encoder_bind,
> > > +};
> > > +
> > > +static const struct of_device_id simple_encoder_dt_match[] = {
> > > +       { .compatible = "starfive,dsi-encoder", .data = &dsi_priv},
> > > +       {},
> > > +};
> > > +MODULE_DEVICE_TABLE(of, simple_encoder_dt_match);
> > > +
> > > +static int encoder_probe(struct platform_device *pdev) {
> > > +       struct device *dev = &pdev->dev;
> > > +       struct simple_encoder *simple;
> > > +       int ret;
> > > +
> > > +       simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
> > > +       if (!simple)
> > > +               return -ENOMEM;
> > > +
> > > +       simple->priv = of_device_get_match_data(dev);
> > > +
> > > +       simple->dev = dev;
> > > +
> > > +       dev_set_drvdata(dev, simple);
> > > +
> > > +       ret = encoder_parse_dt(dev);
> > > +       if (ret)
> > > +               return ret;
> > > +
> > > +       return component_add(dev, &encoder_component_ops); }
> > > +
> > > +static int encoder_remove(struct platform_device *pdev) {
> > > +       struct device *dev = &pdev->dev;
> > > +
> > > +       component_del(dev, &encoder_component_ops);
> > > +       dev_set_drvdata(dev, NULL);
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +struct platform_driver simple_encoder_driver = {
> > > +       .probe = encoder_probe,
> > > +       .remove = encoder_remove,
> > > +       .driver = {
> > > +               .name = "vs-simple-encoder",
> > > +               .of_match_table =
> of_match_ptr(simple_encoder_dt_match),
> > > +       },
> > > +};
> > > +
> > > +MODULE_DESCRIPTION("Simple Encoder Driver");
> MODULE_LICENSE("GPL");
> > > diff --git a/drivers/gpu/drm/verisilicon/vs_simple_enc.h
> > > b/drivers/gpu/drm/verisilicon/vs_simple_enc.h
> > > new file mode 100644
> > > index 000000000000..fb33ca9e18d6
> > > --- /dev/null
> > > +++ b/drivers/gpu/drm/verisilicon/vs_simple_enc.h
> > > @@ -0,0 +1,23 @@
> > > +/* SPDX-License-Identifier: GPL-2.0 */
> > > +/*
> > > + * Copyright (C) 2022 VeriSilicon Holdings Co., Ltd.
> > > + */
> > > +
> > > +#ifndef __VS_SIMPLE_ENC_H_
> > > +#define __VS_SIMPLE_ENC_H_
> > > +
> > > +struct simple_encoder_priv {
> > > +       unsigned char encoder_type;
> > > +};
> > > +
> > > +struct simple_encoder {
> > > +       struct drm_encoder encoder;
> > > +       struct device *dev;
> > > +       const struct simple_encoder_priv *priv;
> > > +       struct regmap *dss_regmap;
> > > +       unsigned int offset;
> > > +       unsigned int mask;
> > > +};
> > > +
> > > +extern struct platform_driver simple_encoder_driver; #endif /*
> > > +__VS_SIMPLE_ENC_H_ */
> > > --
> > > 2.34.1
> > >
> >
> >
> > --
> > With best wishes
> > Dmitry
> 
> 
> 
> --
> With best wishes
> Dmitry

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

* Re: [v3 6/6] drm/vs: simple encoder
  2024-05-15 10:07       ` Keith Zhao
@ 2024-05-15 15:17         ` Dmitry Baryshkov
  2024-05-16  2:57           ` Keith Zhao
  0 siblings, 1 reply; 52+ messages in thread
From: Dmitry Baryshkov @ 2024-05-15 15:17 UTC (permalink / raw)
  To: Keith Zhao
  Cc: devicetree, dri-devel, linux-kernel, linux-riscv, aou,
	suijingfeng, tzimmermann, paul.walmsley, mripard, Xingyu Wu,
	Jack Zhu, palmer, krzysztof.kozlowski+dt, William Qiu,
	Shengyang Chen, Changhuang Liang

On Wed, May 15, 2024 at 10:07:27AM +0000, Keith Zhao wrote:
> 
> 
> > -----Original Message-----
> > From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> > Sent: 2023年12月5日 21:19
> > To: Keith Zhao <keith.zhao@starfivetech.com>
> > Cc: devicetree@vger.kernel.org; dri-devel@lists.freedesktop.org;
> > linux-kernel@vger.kernel.org; linux-riscv@lists.infradead.org;
> > aou@eecs.berkeley.edu; suijingfeng@loongson.cn; tzimmermann@suse.de;
> > paul.walmsley@sifive.com; mripard@kernel.org; Xingyu Wu
> > <xingyu.wu@starfivetech.com>; Jack Zhu <jack.zhu@starfivetech.com>;
> > palmer@dabbelt.com; krzysztof.kozlowski+dt@linaro.org; William Qiu
> > <william.qiu@starfivetech.com>; Shengyang Chen
> > <shengyang.chen@starfivetech.com>; Changhuang Liang
> > <changhuang.liang@starfivetech.com>
> > Subject: Re: [v3 6/6] drm/vs: simple encoder
> > 
> > On Tue, 5 Dec 2023 at 15:14, Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> > wrote:
> > >
> > > On Mon, 4 Dec 2023 at 14:33, Keith Zhao <keith.zhao@starfivetech.com>
> > wrote:
> > > >
> > > > add simple encoder for dsi bridge
> > >
> > > This doesn't look like a proper commit message.
> > >
> > > >
> > > > Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> > > > ---
> > > >  drivers/gpu/drm/verisilicon/Makefile        |   4 +-
> > > >  drivers/gpu/drm/verisilicon/vs_drv.c        |   2 +
> > > >  drivers/gpu/drm/verisilicon/vs_simple_enc.c | 195
> > > > ++++++++++++++++++++  drivers/gpu/drm/verisilicon/vs_simple_enc.h |
> > > > 23 +++
> > > >  4 files changed, 223 insertions(+), 1 deletion(-)  create mode
> > > > 100644 drivers/gpu/drm/verisilicon/vs_simple_enc.c
> > > >  create mode 100644 drivers/gpu/drm/verisilicon/vs_simple_enc.h
> > > >
> > > > diff --git a/drivers/gpu/drm/verisilicon/Makefile
> > > > b/drivers/gpu/drm/verisilicon/Makefile
> > > > index 71fadafcee13..cd5d0a90bcfe 100644
> > > > --- a/drivers/gpu/drm/verisilicon/Makefile
> > > > +++ b/drivers/gpu/drm/verisilicon/Makefile
> > > > @@ -5,6 +5,8 @@ vs_drm-objs := vs_dc_hw.o \
> > > >                 vs_crtc.o \
> > > >                 vs_drv.o \
> > > >                 vs_modeset.o \
> > > > -               vs_plane.o
> > > > +               vs_plane.o \
> > > > +               vs_simple_enc.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/vs_drv.c
> > > > b/drivers/gpu/drm/verisilicon/vs_drv.c
> > > > index d7e5199fe293..946f137ab124 100644
> > > > --- a/drivers/gpu/drm/verisilicon/vs_drv.c
> > > > +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> > > > @@ -23,6 +23,7 @@
> > > >  #include "vs_drv.h"
> > > >  #include "vs_modeset.h"
> > > >  #include "vs_dc.h"
> > > > +#include "vs_simple_enc.h"
> > > >
> > > >  #define DRV_NAME       "verisilicon"
> > > >  #define DRV_DESC       "Verisilicon DRM driver"
> > > > @@ -217,6 +218,7 @@ static struct platform_driver *drm_sub_drivers[]
> > > > = {  #ifdef CONFIG_DRM_VERISILICON_STARFIVE_HDMI
> > > >         &starfive_hdmi_driver,
> > > >  #endif
> > > > +       &simple_encoder_driver,
> > > >  };
> > > >
> > > >  static struct component_match *vs_drm_match_add(struct device *dev)
> > > > diff --git a/drivers/gpu/drm/verisilicon/vs_simple_enc.c
> > > > b/drivers/gpu/drm/verisilicon/vs_simple_enc.c
> > > > new file mode 100644
> > > > index 000000000000..c5a8d82bc469
> > > > --- /dev/null
> > > > +++ b/drivers/gpu/drm/verisilicon/vs_simple_enc.c
> > > > @@ -0,0 +1,195 @@
> > > > +// SPDX-License-Identifier: GPL-2.0
> > > > +/*
> > > > + * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
> > > > + */
> > > > +#include <linux/component.h>
> > > > +#include <linux/of_device.h>
> > > > +#include <linux/module.h>
> > > > +
> > > > +#include <drm/drm_atomic_helper.h>
> > > > +#include <drm/drm_bridge.h>
> > > > +#include <drm/drm_crtc_helper.h>
> > > > +#include <drm/drm_of.h>
> > > > +#include <linux/regmap.h>
> > > > +#include <linux/media-bus-format.h> #include <linux/mfd/syscon.h>
> > > > +
> > > > +#include "vs_crtc.h"
> > > > +#include "vs_simple_enc.h"
> > > > +
> > > > +static const struct simple_encoder_priv dsi_priv = {
> > >
> > > Please use proper prefix for all the struct and function names.
> > > vs_simple_encoder sounds better. Or vs_dsi_encoder.
> > >
> > > > +       .encoder_type = DRM_MODE_ENCODER_DSI };
> > > > +
> > > > +static inline struct simple_encoder *to_simple_encoder(struct
> > > > +drm_encoder *enc) {
> > > > +       return container_of(enc, struct simple_encoder, encoder); }
> > > > +
> > > > +static int encoder_parse_dt(struct device *dev) {
> > > > +       struct simple_encoder *simple = dev_get_drvdata(dev);
> > > > +       unsigned int args[2];
> > > > +
> > > > +       simple->dss_regmap =
> > syscon_regmap_lookup_by_phandle_args(dev->of_node,
> > > > +
> > "starfive,syscon",
> > > > +
> > 2,
> > > > + args);
> > > > +
> > > > +       if (IS_ERR(simple->dss_regmap)) {
> > > > +               return dev_err_probe(dev,
> > PTR_ERR(simple->dss_regmap),
> > > > +                                    "getting the regmap
> > failed\n");
> > > > +       }
> > > > +
> > > > +       simple->offset = args[0];
> > > > +       simple->mask = args[1];
> > > > +
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +void encoder_atomic_enable(struct drm_encoder *encoder,
> > > > +                          struct drm_atomic_state *state) {
> > > > +       struct simple_encoder *simple = to_simple_encoder(encoder);
> > > > +
> > > > +       regmap_update_bits(simple->dss_regmap, simple->offset,
> > simple->mask,
> > > > +                          simple->mask); }
> > > > +
> > > > +int encoder_atomic_check(struct drm_encoder *encoder,
> > > > +                        struct drm_crtc_state *crtc_state,
> > > > +                        struct drm_connector_state *conn_state) {
> > > > +       struct vs_crtc_state *vs_crtc_state =
> > to_vs_crtc_state(crtc_state);
> > > > +       struct drm_connector *connector = conn_state->connector;
> > > > +       int ret = 0;
> > > > +
> > > > +       struct drm_bridge *first_bridge =
> > drm_bridge_chain_get_first_bridge(encoder);
> > > > +       struct drm_bridge_state *bridge_state = ERR_PTR(-EINVAL);
> > > > +
> > > > +       vs_crtc_state->encoder_type = encoder->encoder_type;
> > > > +
> > > > +       if (first_bridge && first_bridge->funcs->atomic_duplicate_state)
> > > > +               bridge_state =
> > > > + drm_atomic_get_bridge_state(crtc_state->state, first_bridge);
> > >
> > > Please don't poke into others' playground. This should go into your
> > > DSI bridge's atomic_check() instead.
> > 
> > Hmm. And you can not use vs_crtc_state from your bridge. Actually this design
> > makes me wonder, how does your hardware work? Is it possible to send the DSI
> > commands to the panel?
> > 
> Hi Dmitry:
> 1、 This fun is used to check the media bus format whether in support list , if not , this mode will be invalid,
>     I just used the bridge api to get the media bus format. (sync with the input bridge(connector) bus format)
> 2、 the bridge doesn't care the vs_crtc_state,  just do their own drm_bridge_funcs ->atomic_check, 
>     or it will be impossible to make this dsi encoder to fit dsi bridge driver.
> 3、 hardware work :
>     encoder_atomic_check get the media bus format and update it to vs_crtc_state-> output_fmt,
>     during the crtc_enable , it will write the output_fmt to hardware .
> 
> 4、	to send dsi command to panel ,  It is only relevant to dsi controllers and panel driver (bridge and panel ),
> 	It does not involve the logic associated with encoder and crtc

Do you have a pointer to your DSI bridge driver somewhere (a preliminary
version is ok, if it's not ready yet for upstream submission). From the
current design it seems that there is no need for a separate 'simple'
encoder. Instead it might be easier/better to transform your DSI bridge
driver into an encoder driver, especially as you are prety flexible in
the component connections.

> 
> https://elixir.bootlin.com/linux/v6.6.30/source/drivers/gpu/drm/mxsfb/lcdif_kms.c#L457
> Do the similar logic

-- 
With best wishes
Dmitry

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

* RE: [v3 6/6] drm/vs: simple encoder
  2024-05-15 15:17         ` Dmitry Baryshkov
@ 2024-05-16  2:57           ` Keith Zhao
  0 siblings, 0 replies; 52+ messages in thread
From: Keith Zhao @ 2024-05-16  2:57 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: devicetree, dri-devel, linux-kernel, linux-riscv, aou,
	suijingfeng, tzimmermann, paul.walmsley, mripard, Xingyu Wu,
	Jack Zhu, palmer, krzysztof.kozlowski+dt, William Qiu,
	Shengyang Chen, Changhuang Liang



> -----Original Message-----
> From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> Sent: 2024年5月15日 23:17
> To: Keith Zhao <keith.zhao@starfivetech.com>
> Cc: devicetree@vger.kernel.org; dri-devel@lists.freedesktop.org;
> linux-kernel@vger.kernel.org; linux-riscv@lists.infradead.org;
> aou@eecs.berkeley.edu; suijingfeng@loongson.cn; tzimmermann@suse.de;
> paul.walmsley@sifive.com; mripard@kernel.org; Xingyu Wu
> <xingyu.wu@starfivetech.com>; Jack Zhu <jack.zhu@starfivetech.com>;
> palmer@dabbelt.com; krzysztof.kozlowski+dt@linaro.org; William Qiu
> <william.qiu@starfivetech.com>; Shengyang Chen
> <shengyang.chen@starfivetech.com>; Changhuang Liang
> <changhuang.liang@starfivetech.com>
> Subject: Re: [v3 6/6] drm/vs: simple encoder
> 
> On Wed, May 15, 2024 at 10:07:27AM +0000, Keith Zhao wrote:
> >
> >
> > > -----Original Message-----
> > > From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> > > Sent: 2023年12月5日 21:19
> > > To: Keith Zhao <keith.zhao@starfivetech.com>
> > > Cc: devicetree@vger.kernel.org; dri-devel@lists.freedesktop.org;
> > > linux-kernel@vger.kernel.org; linux-riscv@lists.infradead.org;
> > > aou@eecs.berkeley.edu; suijingfeng@loongson.cn; tzimmermann@suse.de;
> > > paul.walmsley@sifive.com; mripard@kernel.org; Xingyu Wu
> > > <xingyu.wu@starfivetech.com>; Jack Zhu <jack.zhu@starfivetech.com>;
> > > palmer@dabbelt.com; krzysztof.kozlowski+dt@linaro.org; William Qiu
> > > <william.qiu@starfivetech.com>; Shengyang Chen
> > > <shengyang.chen@starfivetech.com>; Changhuang Liang
> > > <changhuang.liang@starfivetech.com>
> > > Subject: Re: [v3 6/6] drm/vs: simple encoder
> > >
> > > On Tue, 5 Dec 2023 at 15:14, Dmitry Baryshkov
> > > <dmitry.baryshkov@linaro.org>
> > > wrote:
> > > >
> > > > On Mon, 4 Dec 2023 at 14:33, Keith Zhao
> > > > <keith.zhao@starfivetech.com>
> > > wrote:
> > > > >
> > > > > add simple encoder for dsi bridge
> > > >
> > > > This doesn't look like a proper commit message.
> > > >
> > > > >
> > > > > Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com>
> > > > > ---
> > > > >  drivers/gpu/drm/verisilicon/Makefile        |   4 +-
> > > > >  drivers/gpu/drm/verisilicon/vs_drv.c        |   2 +
> > > > >  drivers/gpu/drm/verisilicon/vs_simple_enc.c | 195
> > > > > ++++++++++++++++++++
> > > > > ++++++++++++++++++++ drivers/gpu/drm/verisilicon/vs_simple_enc.h
> > > > > ++++++++++++++++++++ |
> > > > > 23 +++
> > > > >  4 files changed, 223 insertions(+), 1 deletion(-)  create mode
> > > > > 100644 drivers/gpu/drm/verisilicon/vs_simple_enc.c
> > > > >  create mode 100644 drivers/gpu/drm/verisilicon/vs_simple_enc.h
> > > > >
> > > > > diff --git a/drivers/gpu/drm/verisilicon/Makefile
> > > > > b/drivers/gpu/drm/verisilicon/Makefile
> > > > > index 71fadafcee13..cd5d0a90bcfe 100644
> > > > > --- a/drivers/gpu/drm/verisilicon/Makefile
> > > > > +++ b/drivers/gpu/drm/verisilicon/Makefile
> > > > > @@ -5,6 +5,8 @@ vs_drm-objs := vs_dc_hw.o \
> > > > >                 vs_crtc.o \
> > > > >                 vs_drv.o \
> > > > >                 vs_modeset.o \
> > > > > -               vs_plane.o
> > > > > +               vs_plane.o \
> > > > > +               vs_simple_enc.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/vs_drv.c
> > > > > b/drivers/gpu/drm/verisilicon/vs_drv.c
> > > > > index d7e5199fe293..946f137ab124 100644
> > > > > --- a/drivers/gpu/drm/verisilicon/vs_drv.c
> > > > > +++ b/drivers/gpu/drm/verisilicon/vs_drv.c
> > > > > @@ -23,6 +23,7 @@
> > > > >  #include "vs_drv.h"
> > > > >  #include "vs_modeset.h"
> > > > >  #include "vs_dc.h"
> > > > > +#include "vs_simple_enc.h"
> > > > >
> > > > >  #define DRV_NAME       "verisilicon"
> > > > >  #define DRV_DESC       "Verisilicon DRM driver"
> > > > > @@ -217,6 +218,7 @@ static struct platform_driver
> > > > > *drm_sub_drivers[] = {  #ifdef
> CONFIG_DRM_VERISILICON_STARFIVE_HDMI
> > > > >         &starfive_hdmi_driver,
> > > > >  #endif
> > > > > +       &simple_encoder_driver,
> > > > >  };
> > > > >
> > > > >  static struct component_match *vs_drm_match_add(struct device
> > > > > *dev) diff --git a/drivers/gpu/drm/verisilicon/vs_simple_enc.c
> > > > > b/drivers/gpu/drm/verisilicon/vs_simple_enc.c
> > > > > new file mode 100644
> > > > > index 000000000000..c5a8d82bc469
> > > > > --- /dev/null
> > > > > +++ b/drivers/gpu/drm/verisilicon/vs_simple_enc.c
> > > > > @@ -0,0 +1,195 @@
> > > > > +// SPDX-License-Identifier: GPL-2.0
> > > > > +/*
> > > > > + * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
> > > > > + */
> > > > > +#include <linux/component.h>
> > > > > +#include <linux/of_device.h>
> > > > > +#include <linux/module.h>
> > > > > +
> > > > > +#include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h>
> > > > > +#include <drm/drm_crtc_helper.h> #include <drm/drm_of.h>
> > > > > +#include <linux/regmap.h> #include <linux/media-bus-format.h>
> > > > > +#include <linux/mfd/syscon.h>
> > > > > +
> > > > > +#include "vs_crtc.h"
> > > > > +#include "vs_simple_enc.h"
> > > > > +
> > > > > +static const struct simple_encoder_priv dsi_priv = {
> > > >
> > > > Please use proper prefix for all the struct and function names.
> > > > vs_simple_encoder sounds better. Or vs_dsi_encoder.
> > > >
> > > > > +       .encoder_type = DRM_MODE_ENCODER_DSI };
> > > > > +
> > > > > +static inline struct simple_encoder *to_simple_encoder(struct
> > > > > +drm_encoder *enc) {
> > > > > +       return container_of(enc, struct simple_encoder,
> > > > > +encoder); }
> > > > > +
> > > > > +static int encoder_parse_dt(struct device *dev) {
> > > > > +       struct simple_encoder *simple = dev_get_drvdata(dev);
> > > > > +       unsigned int args[2];
> > > > > +
> > > > > +       simple->dss_regmap =
> > > syscon_regmap_lookup_by_phandle_args(dev->of_node,
> > > > > +
> > > "starfive,syscon",
> > > > > +
> > > 2,
> > > > > + args);
> > > > > +
> > > > > +       if (IS_ERR(simple->dss_regmap)) {
> > > > > +               return dev_err_probe(dev,
> > > PTR_ERR(simple->dss_regmap),
> > > > > +                                    "getting the regmap
> > > failed\n");
> > > > > +       }
> > > > > +
> > > > > +       simple->offset = args[0];
> > > > > +       simple->mask = args[1];
> > > > > +
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > > +void encoder_atomic_enable(struct drm_encoder *encoder,
> > > > > +                          struct drm_atomic_state *state) {
> > > > > +       struct simple_encoder *simple =
> > > > > +to_simple_encoder(encoder);
> > > > > +
> > > > > +       regmap_update_bits(simple->dss_regmap, simple->offset,
> > > simple->mask,
> > > > > +                          simple->mask); }
> > > > > +
> > > > > +int encoder_atomic_check(struct drm_encoder *encoder,
> > > > > +                        struct drm_crtc_state *crtc_state,
> > > > > +                        struct drm_connector_state *conn_state)
> {
> > > > > +       struct vs_crtc_state *vs_crtc_state =
> > > to_vs_crtc_state(crtc_state);
> > > > > +       struct drm_connector *connector = conn_state->connector;
> > > > > +       int ret = 0;
> > > > > +
> > > > > +       struct drm_bridge *first_bridge =
> > > drm_bridge_chain_get_first_bridge(encoder);
> > > > > +       struct drm_bridge_state *bridge_state =
> > > > > + ERR_PTR(-EINVAL);
> > > > > +
> > > > > +       vs_crtc_state->encoder_type = encoder->encoder_type;
> > > > > +
> > > > > +       if (first_bridge &&
> first_bridge->funcs->atomic_duplicate_state)
> > > > > +               bridge_state =
> > > > > + drm_atomic_get_bridge_state(crtc_state->state, first_bridge);
> > > >
> > > > Please don't poke into others' playground. This should go into
> > > > your DSI bridge's atomic_check() instead.
> > >
> > > Hmm. And you can not use vs_crtc_state from your bridge. Actually
> > > this design makes me wonder, how does your hardware work? Is it
> > > possible to send the DSI commands to the panel?
> > >
> > Hi Dmitry:
> > 1、 This fun is used to check the media bus format whether in support list , if
> not , this mode will be invalid,
> >     I just used the bridge api to get the media bus format. (sync with
> > the input bridge(connector) bus format)
> > 2、 the bridge doesn't care the vs_crtc_state,  just do their own
> drm_bridge_funcs ->atomic_check,
> >     or it will be impossible to make this dsi encoder to fit dsi bridge driver.
> > 3、 hardware work :
> >     encoder_atomic_check get the media bus format and update it to
> vs_crtc_state-> output_fmt,
> >     during the crtc_enable , it will write the output_fmt to hardware .
> >
> > 4、	to send dsi command to panel ,  It is only relevant to dsi controllers
> and panel driver (bridge and panel ),
> > 	It does not involve the logic associated with encoder and crtc
> 
> Do you have a pointer to your DSI bridge driver somewhere (a preliminary
> version is ok, if it's not ready yet for upstream submission). From the current
> design it seems that there is no need for a separate 'simple'
> encoder. Instead it might be easier/better to transform your DSI bridge driver
> into an encoder driver, especially as you are prety flexible in the component
> connections.
> 
Hi Dmitry:
My SoC DSI bridge driver is cdns dsi and cdns dsi is already in the path:
/linux/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c

Encoder_bind will attach the bridge to encoder
/* output port is port1*/
	bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
    ......
	return drm_bridge_attach(encoder, bridge, NULL, 0);


There are two types of bridges in the Linux mainline code:
Type 1: only contains bridge driver, for example
/linux/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c
/linux/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
Type 2: Contains encoder + bridge:
/linux/drivers/gpu\drm/i2c/tda998x_drv.c

This simple encoder driver is used to match the Type 1 bridge.
Of course, if i adapt to Type 2, i only need to care the encoder possible crtc. Skip this encoder

Thanks 
Keith
> >
> > https://elixir.bootlin.com/linux/v6.6.30/source/drivers/gpu/drm/mxsfb/
> > lcdif_kms.c#L457
> > Do the similar logic
> 
> --
> With best wishes
> Dmitry

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

end of thread, other threads:[~2024-05-16  2:57 UTC | newest]

Thread overview: 52+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-12-04 12:33 [v3 0/6] DRM driver for verisilicon Keith Zhao
2023-12-04 12:33 ` [v3 1/6] dt-bindings: display: Add yamls for JH7110 display system Keith Zhao
2023-12-05  6:59   ` Krzysztof Kozlowski
2023-12-06 12:50   ` Sui Jingfeng
2023-12-08 16:33     ` Rob Herring
2023-12-08 16:31   ` Rob Herring
2023-12-04 12:33 ` [v3 2/6] riscv: dts: starfive: jh7110: display subsystem Keith Zhao
2023-12-04 12:33 ` [v3 3/6] drm/vs: Register DRM device Keith Zhao
2023-12-04 13:30   ` Philipp Zabel
2023-12-11  9:00     ` Keith Zhao
2023-12-11  9:17       ` mripard
2023-12-05 11:33   ` Dmitry Baryshkov
2023-12-04 12:33 ` [v3 4/6] drm/vs: Add KMS crtc&plane Keith Zhao
2023-12-05 12:48   ` Dmitry Baryshkov
2023-12-06  8:55   ` Maxime Ripard
2023-12-06 12:53     ` Keith Zhao
2024-01-31  8:57     ` 回复: " Keith Zhao
2024-01-31 16:30       ` Maxime Ripard
2024-01-31  9:33     ` Keith Zhao
2024-01-31 13:23       ` Maxime Ripard
2024-02-01  2:22         ` Keith Zhao
2024-02-09 15:37           ` Maxime Ripard
2023-12-07  8:41   ` Icenowy Zheng
2023-12-07 11:31     ` Keith Zhao
2023-12-07 15:29       ` Icenowy Zheng
2023-12-27  3:57       ` Icenowy Zheng
2023-12-04 12:33 ` [v3 5/6] drm/vs: Add hdmi driver Keith Zhao
2023-12-05 13:02   ` Dmitry Baryshkov
2023-12-06  9:04   ` Maxime Ripard
2023-12-06 12:02     ` Keith Zhao
2023-12-06 12:56       ` Maxime Ripard
2023-12-06 14:11         ` Keith Zhao
2023-12-07  9:02           ` Andy Yan
2023-12-07 10:48             ` Keith Zhao
2023-12-08  0:37               ` Andy Yan
2023-12-08  3:00                 ` Keith Zhao
2023-12-08  3:23                   ` Andy Yan
2023-12-08  9:14                     ` Maxime Ripard
2023-12-11 10:24                       ` Keith Zhao
2023-12-11 12:13                         ` Andy Yan
2023-12-13  1:40                           ` Keith Zhao
2023-12-14  2:51                             ` Andy Yan
2023-12-11 17:34   ` Rob Herring
2023-12-04 12:33 ` [v3 6/6] drm/vs: simple encoder Keith Zhao
2023-12-05 13:14   ` Dmitry Baryshkov
2023-12-05 13:18     ` Dmitry Baryshkov
2024-05-15 10:07       ` Keith Zhao
2024-05-15 15:17         ` Dmitry Baryshkov
2024-05-16  2:57           ` Keith Zhao
2023-12-05  6:55 ` [v3 0/6] DRM driver for verisilicon Krzysztof Kozlowski
2023-12-05 10:27 ` Sui Jingfeng
2023-12-05 11:59 ` Sui Jingfeng

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).