This patch is a drm driver for Starfive Soc JH7110, I am sending Drm driver part and HDMI driver part.
We used GEM framework for buffer management , and for buffer allocation,we use DMA APIs.
the Starfive HDMI servers as interface between a LCD Controller and a HDMI bus. A HDMI TX consists of one HDMI transmitter controller and one HDMI transmitter PHY. (Sound support is not include in this patch)
This patchset should be applied after the patchset: https://patchwork.kernel.org/project/linux-clk/cover/20230713113902.56519-1-...
V1: Changes since v1: - Further standardize the yaml file. - Dts naming convention improved. - Fix the problem of compiling and loading ko files. - Use drm new api to automatically manage resources. - Drop struct vs_crtc_funcs&vs_plane_funcs,subdivide the plane's help interface - Reduce the modifiers unused. - Optimize the hdmi driver code
Keith Zhao (7): MAINTAINERS: Update starfive maintainers dt-bindings: display: Add yamls for JH7110 display system riscv: dts: starfive: jh7110: add dc controller and hdmi node drm/fourcc: Add drm/vs tiled modifiers drm/vs: Register DRM device drm/vs: Add KMS crtc&plane drm/vs: Add hdmi
.../starfive/starfive,display-subsystem.yaml | 41 + .../starfive/starfive,jh7110-dc8200.yaml | 107 + .../starfive/starfive,jh7110-inno-hdmi.yaml | 92 + MAINTAINERS | 7 + .../jh7110-starfive-visionfive-2.dtsi | 87 + arch/riscv/boot/dts/starfive/jh7110.dtsi | 43 + drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/verisilicon/Kconfig | 25 + drivers/gpu/drm/verisilicon/Makefile | 13 + drivers/gpu/drm/verisilicon/starfive_hdmi.c | 940 ++++++++ drivers/gpu/drm/verisilicon/starfive_hdmi.h | 295 +++ drivers/gpu/drm/verisilicon/vs_crtc.c | 365 +++ drivers/gpu/drm/verisilicon/vs_crtc.h | 54 + drivers/gpu/drm/verisilicon/vs_dc.c | 1036 +++++++++ drivers/gpu/drm/verisilicon/vs_dc.h | 87 + drivers/gpu/drm/verisilicon/vs_dc_hw.c | 2008 +++++++++++++++++ drivers/gpu/drm/verisilicon/vs_dc_hw.h | 496 ++++ drivers/gpu/drm/verisilicon/vs_drv.c | 274 +++ drivers/gpu/drm/verisilicon/vs_drv.h | 54 + drivers/gpu/drm/verisilicon/vs_gem.c | 298 +++ drivers/gpu/drm/verisilicon/vs_gem.h | 50 + drivers/gpu/drm/verisilicon/vs_modeset.c | 92 + drivers/gpu/drm/verisilicon/vs_modeset.h | 13 + drivers/gpu/drm/verisilicon/vs_plane.c | 502 +++++ drivers/gpu/drm/verisilicon/vs_plane.h | 65 + drivers/gpu/drm/verisilicon/vs_type.h | 70 + include/uapi/drm/drm_fourcc.h | 27 + include/uapi/drm/vs_drm.h | 50 + 29 files changed, 7194 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml create mode 100644 drivers/gpu/drm/verisilicon/Kconfig create mode 100644 drivers/gpu/drm/verisilicon/Makefile create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.c create mode 100644 drivers/gpu/drm/verisilicon/starfive_hdmi.h create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.c create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.h create mode 100644 drivers/gpu/drm/verisilicon/vs_gem.c create mode 100644 drivers/gpu/drm/verisilicon/vs_gem.h create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.c create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.h create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h create mode 100644 include/uapi/drm/vs_drm.h
update starfive maintainers
Signed-off-by: Keith Zhao keith.zhao@starfivetech.com --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS index 3be1bdfe8..daadd1707 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6760,6 +6760,13 @@ S: Supported T: git git://anongit.freedesktop.org/drm/drm-misc F: drivers/gpu/drm/udl/
+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 VIRTUAL KERNEL MODESETTING (VKMS) M: Rodrigo Siqueira rodrigosiqueiramelo@gmail.com M: Melissa Wen melissa.srw@gmail.com
On Tue, Aug 01, 2023 at 06:10:24PM +0800, Keith Zhao wrote:
update starfive maintainers
Signed-off-by: Keith Zhao keith.zhao@starfivetech.com
Why is this a standalone patch, before you've even added any of the files in question?
MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS index 3be1bdfe8..daadd1707 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6760,6 +6760,13 @@ S: Supported T: git git://anongit.freedesktop.org/drm/drm-misc F: drivers/gpu/drm/udl/ +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 VIRTUAL KERNEL MODESETTING (VKMS) M: Rodrigo Siqueira rodrigosiqueiramelo@gmail.com M: Melissa Wen melissa.srw@gmail.com -- 2.34.1
On 8/1/23 03:10, Keith Zhao wrote:
update starfive maintainers
Signed-off-by: Keith Zhao keith.zhao@starfivetech.com
MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS index 3be1bdfe8..daadd1707 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6760,6 +6760,13 @@ S: Supported T: git git://anongit.freedesktop.org/drm/drm-misc F: drivers/gpu/drm/udl/
This new entry should be in alphabetical order (hint: it's not).
+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 VIRTUAL KERNEL MODESETTING (VKMS) M: Rodrigo Siqueira rodrigosiqueiramelo@gmail.com M: Melissa Wen melissa.srw@gmail.com
StarFive SoCs JH7110 display system: lcd-controller bases verisilicon dc8200 IP, and hdmi bases Innosilicon IP. Add bindings for them.
Signed-off-by: Keith Zhao keith.zhao@starfivetech.com --- .../starfive/starfive,display-subsystem.yaml | 41 +++++++ .../starfive/starfive,jh7110-dc8200.yaml | 107 ++++++++++++++++++ .../starfive/starfive,jh7110-inno-hdmi.yaml | 92 +++++++++++++++ 3 files changed, 240 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml new file mode 100644 index 000000000..86018a8e6 --- /dev/null +++ b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/starfive/starfive,display-subsystem.ya... +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Starfive DRM master device + +maintainers: + - Keith Zhao keith.zhao@starfivetech.com + - ShengYang Chen shengyang.chen@starfivetech.com + +description: + The Starfive DRM master device is a virtual device needed to list all + display controller or other display interface nodes that comprise the + graphics subsystem. + +properties: + compatible: + const: starfive,display-subsystem + + ports: + $ref: /schemas/graph.yaml#/properties/ports + description: | + Should contain a list of phandles pointing to display interface ports + of display controller devices. Display controller definitions as defined + in Documentation/devicetree/bindings/display/starfive/ + starfive,jh7110-dc8200.yaml + +required: + - compatible + - ports + +additionalProperties: false + +examples: + - | + display-subsystem { + compatible = "starfive,display-subsystem"; + ports = <&dc_out>; + }; diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml new file mode 100644 index 000000000..bebe2050c --- /dev/null +++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml @@ -0,0 +1,107 @@ +# 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: + maxItems: 3 + + interrupts: + items: + - description: The interrupt will be generated when DC finish one frame + + clocks: + items: + - description: Clock for display system noc bus. + - description: Pixel clock for display channel 0. + - description: Pixel clock for display channel 1. + - description: Clock for axi interface of display controller. + - description: Core clock for display controller. + - description: Clock for ahb interface of display controller. + - description: External HDMI pixel clock. + - description: Parent clock for pixel clock + + clock-names: + items: + - const: vout_noc_disp + - const: vout_pix0 + - const: vout_pix1 + - const: vout_axi + - const: vout_core + - const: vout_vout_ahb + - const: hdmitx0_pixel + - const: vout_dc8200 + + resets: + items: + - description: Reset for axi interface of display controller. + - description: Reset for ahb interface of display controller. + - description: Core reset of display controller. + + reset-names: + items: + - const: vout_axi + - const: vout_ahb + - const: vout_core + + port: + $ref: /schemas/graph.yaml#/properties/port + description: + A port node with endpoint definitions as defined in + Documentation/devicetree/bindings/media/video-interfaces.txt. + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - resets + - reset-names + - port + +additionalProperties: false + +examples: + - | + dc8200: lcd-controller@29400000 { + compatible = "starfive,jh7110-dc8200"; + reg = <0x29400000 0x100>, <0x29400800 0x2000>, <0x295b0000 0x90>; + interrupts = <95>; + clocks = <&syscrg 60>, + <&voutcrg 7>, + <&voutcrg 8>, + <&voutcrg 4>, + <&voutcrg 5>, + <&voutcrg 6>, + <&hdmitx0_pixelclk>, + <&voutcrg 1>; + clock-names = "vout_noc_disp", "vout_pix0", "vout_pix1", + "vout_axi", "vout_core", "vout_vout_ahb", + "hdmitx0_pixel","vout_dc8200"; + resets = <&voutcrg 0>, <&voutcrg 1>, <&voutcrg 2>; + reset-names = "vout_axi", "vout_ahb", "vout_core"; + dc_out: port { + #address-cells = <1>; + #size-cells = <0>; + dc_out_hdmi: endpoint@0 { + reg = <0>; + remote-endpoint = <&hdmi_in_dc>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml new file mode 100644 index 000000000..f6927acf6 --- /dev/null +++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.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,jh7110-inno-hdmi.yam... +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Starfive JH7110 HDMI controller + +description: + The StarFive JH7110 SoC uses the HDMI signal transmiter based on innosilicon IP + to generate HDMI signal from its input and transmit the signal to the screen. + +maintainers: + - Keith Zhao keith.zhao@starfivetech.com + +properties: + compatible: + const: "starfive,jh7110-inno-hdmi" + + reg: + minItems: 1 + + interrupts: + items: + - description: The HDMI hot plug detection interrupt. + + clocks: + items: + - description: System clock of HDMI module. + - description: Mclk clock of HDMI audio. + - description: Bclk clock of HDMI audio. + - description: Pixel clock generated by HDMI module. + + clock-names: + items: + - const: sysclk + - const: mclk + - const: bclk + - const: pclk + + resets: + items: + - description: Reset for HDMI module. + + reset-names: + items: + - const: hdmi_tx + + '#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 + - reset-names + - '#sound-dai-cells' + - port + +additionalProperties: false + +examples: + - | + hdmi: hdmi@29590000 { + compatible = "starfive,jh7110-inno-hdmi"; + reg = <0x29590000 0x4000>; + interrupts = <99>; + clocks = <&voutcrg 17>, + <&voutcrg 15>, + <&voutcrg 16>, + <&hdmitx0_pixelclk>; + clock-names = "sysclk", "mclk","bclk","pclk"; + resets = <&voutcrg 9>; + reset-names = "hdmi_tx"; + #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>; + }; + }; + };
On Tue, 01 Aug 2023 18:10:25 +0800, Keith Zhao wrote:
StarFive SoCs JH7110 display system: lcd-controller bases verisilicon dc8200 IP, and hdmi bases Innosilicon IP. Add bindings for them.
Signed-off-by: Keith Zhao keith.zhao@starfivetech.com
.../starfive/starfive,display-subsystem.yaml | 41 +++++++ .../starfive/starfive,jh7110-dc8200.yaml | 107 ++++++++++++++++++ .../starfive/starfive,jh7110-inno-hdmi.yaml | 92 +++++++++++++++ 3 files changed, 240 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check' on your patch (DT_CHECKER_FLAGS is new in v5.13):
yamllint warnings/errors:
dtschema/dtc warnings/errors: /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.example.dtb: display-subsystem: ports: [[4294967295]] is not of type 'object' from schema $id: http://devicetree.org/schemas/display/starfive/starfive,display-subsystem.ya... /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.example.dtb: display-subsystem: ports: More than one condition true in oneOf schema: {'additionalProperties': False, 'oneOf': [{'required': ['port']}, {'required': ['#address-cells', '#size-cells']}], 'patternProperties': {'^port@[0-9a-f]+$': {'required': ['reg'], 'type': 'object'}, 'pinctrl-[0-9]+': True}, 'properties': {'#address-cells': {'items': [{'items': [{'const': 1}], 'maxItems': 1, 'minItems': 1, 'type': 'array'}], 'maxItems': 1, 'minItems': 1, 'type': 'array'}, '#size-cells': {'items': [{'items': [{'const': 0}], 'maxItems': 1, 'minItems': 1, 'type': 'array'}], 'maxItems': 1, 'minItems': 1, 'type': 'array'}, '$nodename': True, 'bootph-all': True, 'bootph-pre-ram': True, 'bootph-pre-sram': True, 'bootph-some-ram': True, 'bootph-verify': True, 'phandle': True, 'pinctrl-names': True, 'port': {'type': 'object'}, 'secure-status': True, 'status': True}, 'type': 'object'} from schema $id: http://devicetree.org/schemas/display/starfive/starfive,display-subsystem.ya...
doc reference errors (make refcheckdocs):
See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/2023080110103...
The base for the series is generally the latest rc1. A different dependency should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure 'yamllint' is installed and dt-schema is up to date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note that DT_SCHEMA_FILES can be set to your schema file to speed up checking your schema. However, it must be unset to test all examples with your schema.
Hi,
On Tue, Aug 01, 2023 at 06:10:25PM +0800, Keith Zhao wrote:
diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml new file mode 100644 index 000000000..bebe2050c --- /dev/null +++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml @@ -0,0 +1,107 @@ +# 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:
- maxItems: 3
What these registers are used for must be documented.
- interrupts:
- items:
- description: The interrupt will be generated when DC finish one frame
- clocks:
- items:
- description: Clock for display system noc bus.
- description: Pixel clock for display channel 0.
- description: Pixel clock for display channel 1.
- description: Clock for axi interface of display controller.
- description: Core clock for display controller.
- description: Clock for ahb interface of display controller.
- description: External HDMI pixel clock.
- description: Parent clock for pixel clock
- clock-names:
- items:
- const: vout_noc_disp
- const: vout_pix0
- const: vout_pix1
- const: vout_axi
- const: vout_core
- const: vout_vout_ahb
- const: hdmitx0_pixel
- const: vout_dc8200
The clock-names should reflect what they are used for on the device, not what their name is in the system. So it should rather be something like "noc-bus", "channel0", "channel1", etc.
vout, or the soc model, shouldn't appear there.
- resets:
- items:
- description: Reset for axi interface of display controller.
- description: Reset for ahb interface of display controller.
- description: Core reset of display controller.
- reset-names:
- items:
- const: vout_axi
- const: vout_ahb
- const: vout_core
Ditto.
Also, I'm a bit confused, how can a device be attached to both an AXI and AHB bus? That, plus the multiple registers spaces, make me think that this is multiple devices glued together in a single node, which isn't ok.
- port:
- $ref: /schemas/graph.yaml#/properties/port
- description:
A port node with endpoint definitions as defined in
Documentation/devicetree/bindings/media/video-interfaces.txt.
+required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- resets
- reset-names
- port
+additionalProperties: false
+examples:
- |
- dc8200: lcd-controller@29400000 {
compatible = "starfive,jh7110-dc8200";
reg = <0x29400000 0x100>, <0x29400800 0x2000>, <0x295b0000 0x90>;
interrupts = <95>;
clocks = <&syscrg 60>,
<&voutcrg 7>,
<&voutcrg 8>,
<&voutcrg 4>,
<&voutcrg 5>,
<&voutcrg 6>,
<&hdmitx0_pixelclk>,
<&voutcrg 1>;
clock-names = "vout_noc_disp", "vout_pix0", "vout_pix1",
"vout_axi", "vout_core", "vout_vout_ahb",
"hdmitx0_pixel","vout_dc8200";
resets = <&voutcrg 0>, <&voutcrg 1>, <&voutcrg 2>;
reset-names = "vout_axi", "vout_ahb", "vout_core";
dc_out: port {
#address-cells = <1>;
#size-cells = <0>;
dc_out_hdmi: endpoint@0 {
reg = <0>;
remote-endpoint = <&hdmi_in_dc>;
};
};
- };
diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml new file mode 100644 index 000000000..f6927acf6 --- /dev/null +++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.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,jh7110-inno-hdmi.yam... +$schema: http://devicetree.org/meta-schemas/core.yaml#
+title: Starfive JH7110 HDMI controller
+description:
- The StarFive JH7110 SoC uses the HDMI signal transmiter based on innosilicon IP
- to generate HDMI signal from its input and transmit the signal to the screen.
+maintainers:
- Keith Zhao keith.zhao@starfivetech.com
+properties:
- compatible:
- const: "starfive,jh7110-inno-hdmi"
- reg:
- minItems: 1
- interrupts:
- items:
- description: The HDMI hot plug detection interrupt.
- clocks:
- items:
- description: System clock of HDMI module.
- description: Mclk clock of HDMI audio.
- description: Bclk clock of HDMI audio.
- description: Pixel clock generated by HDMI module.
- clock-names:
- items:
- const: sysclk
- const: mclk
- const: bclk
- const: pclk
- resets:
- items:
- description: Reset for HDMI module.
- reset-names:
- items:
- const: hdmi_tx
If there's only one you don't need reset-names
Maxime
On Tue, Aug 01, 2023 at 06:10:25PM +0800, Keith Zhao wrote:
StarFive SoCs JH7110 display system: lcd-controller bases verisilicon dc8200 IP, and hdmi bases Innosilicon IP. Add bindings for them.
Please, you can use more than 46 characters in a line!
Also, "v1 v1" does not a v2 make.
Signed-off-by: Keith Zhao keith.zhao@starfivetech.com
.../starfive/starfive,display-subsystem.yaml | 41 +++++++ .../starfive/starfive,jh7110-dc8200.yaml | 107 ++++++++++++++++++ .../starfive/starfive,jh7110-inno-hdmi.yaml | 92 +++++++++++++++ 3 files changed, 240 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml create mode 100644 Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml
diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml new file mode 100644 index 000000000..86018a8e6 --- /dev/null +++ b/Documentation/devicetree/bindings/display/starfive/starfive,display-subsystem.yaml @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/starfive/starfive,display-subsystem.ya... +$schema: http://devicetree.org/meta-schemas/core.yaml#
+title: Starfive DRM master device
+maintainers:
- Keith Zhao keith.zhao@starfivetech.com
- ShengYang Chen shengyang.chen@starfivetech.com
+description:
- The Starfive DRM master device is a virtual device needed to list all
- display controller or other display interface nodes that comprise the
- graphics subsystem.
+properties:
- compatible:
- const: starfive,display-subsystem
- ports:
- $ref: /schemas/graph.yaml#/properties/ports
- description: |
A | is not needed when you do not have formatting to preserve.
Should contain a list of phandles pointing to display interface ports
of display controller devices. Display controller definitions as defined
in Documentation/devicetree/bindings/display/starfive/
starfive,jh7110-dc8200.yaml
+required:
- compatible
- ports
+additionalProperties: false
+examples:
- |
- display-subsystem {
compatible = "starfive,display-subsystem";
ports = <&dc_out>;
- };
Given Rob's bot complains, it looks like you never tested this.
diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml new file mode 100644 index 000000000..bebe2050c --- /dev/null +++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-dc8200.yaml @@ -0,0 +1,107 @@ +# 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:
- maxItems: 3
What do each of these represent?
- interrupts:
- items:
- description: The interrupt will be generated when DC finish one frame
- clocks:
- items:
- description: Clock for display system noc bus.
- description: Pixel clock for display channel 0.
- description: Pixel clock for display channel 1.
- description: Clock for axi interface of display controller.
- description: Core clock for display controller.
- description: Clock for ahb interface of display controller.
- description: External HDMI pixel clock.
- description: Parent clock for pixel clock
- clock-names:
- items:
- const: vout_noc_disp
- const: vout_pix0
- const: vout_pix1
- const: vout_axi
- const: vout_core
- const: vout_vout_ahb
- const: hdmitx0_pixel
- const: vout_dc8200
- resets:
- items:
- description: Reset for axi interface of display controller.
- description: Reset for ahb interface of display controller.
- description: Core reset of display controller.
- reset-names:
- items:
- const: vout_axi
- const: vout_ahb
- const: vout_core
Please trim all the vouts from here & the clocks - especially the one named "vout_vout_ahb".
- port:
- $ref: /schemas/graph.yaml#/properties/port
- description:
A port node with endpoint definitions as defined in
Documentation/devicetree/bindings/media/video-interfaces.txt.
This file is empty, it has been converted to yaml.
diff --git a/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.yaml new file mode 100644 index 000000000..f6927acf6 --- /dev/null +++ b/Documentation/devicetree/bindings/display/starfive/starfive,jh7110-inno-hdmi.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,jh7110-inno-hdmi.yam... +$schema: http://devicetree.org/meta-schemas/core.yaml#
+title: Starfive JH7110 HDMI controller
+description:
- The StarFive JH7110 SoC uses the HDMI signal transmiter based on innosilicon IP
- to generate HDMI signal from its input and transmit the signal to the screen.
+maintainers:
- Keith Zhao keith.zhao@starfivetech.com
+properties:
- compatible:
- const: "starfive,jh7110-inno-hdmi"
- reg:
- minItems: 1
- interrupts:
- items:
- description: The HDMI hot plug detection interrupt.
- clocks:
- items:
- description: System clock of HDMI module.
- description: Mclk clock of HDMI audio.
- description: Bclk clock of HDMI audio.
- description: Pixel clock generated by HDMI module.
- clock-names:
- items:
- const: sysclk
- const: mclk
- const: bclk
- const: pclk
- resets:
- items:
- description: Reset for HDMI module.
For this & resets, you don't have a list & don't need items:
Cheers, Conor.
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 | 87 +++++++++++++++++++ arch/riscv/boot/dts/starfive/jh7110.dtsi | 43 +++++++++ 2 files changed, 130 insertions(+)
diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi index de0f40a8b..32e5cc96c 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi @@ -31,6 +31,21 @@ memory@40000000 { reg = <0x0 0x40000000 0x1 0x0>; };
+ reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + linux,cma { + compatible = "shared-dma-pool"; + reusable; + size = <0x0 0x20000000>; + alignment = <0x0 0x1000>; + alloc-ranges = <0x0 0x80000000 0x0 0x20000000>; + linux,cma-default; + }; + }; + gpio-restart { compatible = "gpio-restart"; gpios = <&sysgpio 35 GPIO_ACTIVE_HIGH>; @@ -231,6 +246,41 @@ GPOEN_DISABLE, slew-rate = <0>; }; }; + + hdmi_pins: hdmi-0 { + hdmi-scl-pins { + pinmux = <GPIOMUX(0, GPOUT_SYS_HDMI_DDC_SCL, + GPOEN_SYS_HDMI_DDC_SCL, + GPI_SYS_HDMI_DDC_SCL)>; + input-enable; + bias-pull-up; + }; + + hdmi-sda-pins { + pinmux = <GPIOMUX(1, GPOUT_SYS_HDMI_DDC_SDA, + GPOEN_SYS_HDMI_DDC_SDA, + GPI_SYS_HDMI_DDC_SDA)>; + input-enable; + bias-pull-up; + }; + + hdmi-cec-pins { + pinmux = <GPIOMUX(14, GPOUT_SYS_HDMI_CEC_SDA, + GPOEN_SYS_HDMI_CEC_SDA, + GPI_SYS_HDMI_CEC_SDA)>; + input-enable; + bias-pull-up; + }; + + hdmi-hpd-pins { + pinmux = <GPIOMUX(15, GPOUT_HIGH, + GPOEN_ENABLE, + GPI_SYS_HDMI_HPD)>; + input-enable; + bias-disable; /* external pull-up */ + }; + }; + };
&uart0 { @@ -254,3 +304,40 @@ &U74_3 { &U74_4 { cpu-supply = <&vdd_cpu>; }; + +&voutcrg { + status = "okay"; +}; + +&display { + status = "okay"; +}; + +&hdmi { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&hdmi_pins>; + + hdmi_in: port { + #address-cells = <1>; + #size-cells = <0>; + hdmi_in_dc: endpoint@0 { + reg = <0>; + remote-endpoint = <&dc_out_hdmi>; + }; + }; +}; + +&dc8200 { + status = "okay"; + + dc_out: port { + #address-cells = <1>; + #size-cells = <0>; + dc_out_hdmi: endpoint@0 { + reg = <0>; + remote-endpoint = <&hdmi_in_dc>; + }; + + }; +}; diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index 0005fa163..b8c527d9f 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -282,6 +282,11 @@ tdm_ext: tdm-ext-clock { #clock-cells = <0>; };
+ display: display-subsystem { + compatible = "starfive,display-subsystem"; + ports = <&dc_out>; + }; + soc { compatible = "simple-bus"; interrupt-parent = <&plic>; @@ -613,5 +618,43 @@ voutcrg: clock-controller@295c0000 { #reset-cells = <1>; power-domains = <&pwrc JH7110_PD_VOUT>; }; + + dc8200: lcd-controller@29400000 { + compatible = "starfive,jh7110-dc8200"; + reg = <0x0 0x29400000 0x0 0x100>, + <0x0 0x29400800 0x0 0x2000>, + <0x0 0x295b0000 0x0 0x90>; + interrupts = <95>; + clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_DISP_AXI>, + <&voutcrg JH7110_VOUTCLK_DC8200_PIX0>, + <&voutcrg JH7110_VOUTCLK_DC8200_PIX1>, + <&voutcrg JH7110_VOUTCLK_DC8200_AXI>, + <&voutcrg JH7110_VOUTCLK_DC8200_CORE>, + <&voutcrg JH7110_VOUTCLK_DC8200_AHB>, + <&hdmitx0_pixelclk>, + <&voutcrg JH7110_VOUTCLK_DC8200_PIX>; + clock-names = "vout_noc_disp", "vout_pix0", "vout_pix1", + "vout_axi", "vout_core", "vout_vout_ahb", + "hdmitx0_pixel", "vout_dc8200"; + resets = <&voutcrg JH7110_VOUTRST_DC8200_AXI>, + <&voutcrg JH7110_VOUTRST_DC8200_AHB>, + <&voutcrg JH7110_VOUTRST_DC8200_CORE>; + reset-names = "vout_axi","vout_ahb", "vout_core"; + }; + + hdmi: hdmi@29590000 { + compatible = "starfive,jh7110-inno-hdmi"; + reg = <0x0 0x29590000 0x0 0x4000>; + interrupts = <99>; + + clocks = <&voutcrg JH7110_VOUTCLK_HDMI_TX_SYS>, + <&voutcrg JH7110_VOUTCLK_HDMI_TX_MCLK>, + <&voutcrg JH7110_VOUTCLK_HDMI_TX_BCLK>, + <&hdmitx0_pixelclk>; + clock-names = "sysclk", "mclk", "bclk", "pclk"; + resets = <&voutcrg JH7110_VOUTRST_HDMI_TX_HDMI>; + reset-names = "hdmi_tx"; + #sound-dai-cells = <0>; + }; }; };
On Tue, Aug 01, 2023 at 06:10:26PM +0800, Keith Zhao wrote:
Add the dc controller and hdmi node for the Starfive JH7110 SoC.
Signed-off-by: Keith Zhao keith.zhao@starfivetech.com
.../jh7110-starfive-visionfive-2.dtsi | 87 +++++++++++++++++++ arch/riscv/boot/dts/starfive/jh7110.dtsi | 43 +++++++++ 2 files changed, 130 insertions(+)
diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi index de0f40a8b..32e5cc96c 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi @@ -31,6 +31,21 @@ memory@40000000 { reg = <0x0 0x40000000 0x1 0x0>; };
- reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
linux,cma {
compatible = "shared-dma-pool";
reusable;
size = <0x0 0x20000000>;
alignment = <0x0 0x1000>;
alloc-ranges = <0x0 0x80000000 0x0 0x20000000>;
linux,cma-default;
};
- };
You should explain why you need this, either in a comment or in the commit log.
These are mainly used internally in vs-drm, I'm not sure if the new modifiers can be used with the existing ones. If there is a problem, I will improve it further.
Signed-off-by: Keith Zhao keith.zhao@starfivetech.com --- include/uapi/drm/drm_fourcc.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)
diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index 8db7fd3f7..0b884cf50 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -419,6 +419,7 @@ extern "C" { #define DRM_FORMAT_MOD_VENDOR_ARM 0x08 #define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09 #define DRM_FORMAT_MOD_VENDOR_AMLOGIC 0x0a +#define DRM_FORMAT_MOD_VENDOR_VS 0x0b
/* add more to the end as needed */
@@ -1562,6 +1563,32 @@ drm_fourcc_canonicalize_nvidia_format_mod(__u64 modifier) #define AMD_FMT_MOD_CLEAR(field) \ (~((__u64)AMD_FMT_MOD_##field##_MASK << AMD_FMT_MOD_##field##_SHIFT))
+#define DRM_FORMAT_MOD_VS_TYPE_NORMAL 0x00 +#define DRM_FORMAT_MOD_VS_TYPE_COMPRESSED 0x01 +#define DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT 0x02 +#define DRM_FORMAT_MOD_VS_TYPE_MASK ((__u64)0x3 << 54) + +#define fourcc_mod_vs_code(type, val) \ + fourcc_mod_code(VS, ((((__u64)type) << 54) | (val))) + +#define DRM_FORMAT_MOD_VS_NORM_MODE_MASK 0x1F +#define DRM_FORMAT_MOD_VS_LINEAR 0x00 +#define DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR 0x02 +#define DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR 0x03 +#define DRM_FORMAT_MOD_VS_TILE_8X8 0x04 +#define DRM_FORMAT_MOD_VS_TILE_8X4 0x07 +#define DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR_8X4 0x0B +#define DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR_4X8 0x0C +#define DRM_FORMAT_MOD_VS_TILE_MODE4X4 0x15 + +#define fourcc_mod_vs_norm_code(tile) \ + fourcc_mod_vs_code(DRM_FORMAT_MOD_VS_TYPE_NORMAL, \ + (tile)) + +#define fourcc_mod_vs_custom_code(tile) \ + fourcc_mod_vs_code(DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT, \ + (tile)) + #if defined(__cplusplus) } #endif
On Tue, Aug 01, 2023 at 06:10:27PM +0800, Keith Zhao wrote:
These are mainly used internally in vs-drm, I'm not sure if the new modifiers can be used with the existing ones. If there is a problem, I will improve it further.
Signed-off-by: Keith Zhao keith.zhao@starfivetech.com
include/uapi/drm/drm_fourcc.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)
diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index 8db7fd3f7..0b884cf50 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -419,6 +419,7 @@ extern "C" { #define DRM_FORMAT_MOD_VENDOR_ARM 0x08 #define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09 #define DRM_FORMAT_MOD_VENDOR_AMLOGIC 0x0a +#define DRM_FORMAT_MOD_VENDOR_VS 0x0b
Please don't use any acronym there.
Maxime
Hi
Am 01.08.23 um 12:10 schrieb Keith Zhao:
These are mainly used internally in vs-drm, I'm not sure if the new modifiers can be used with the existing ones. If there is a problem, I will improve it further.
Signed-off-by: Keith Zhao keith.zhao@starfivetech.com
include/uapi/drm/drm_fourcc.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)
diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index 8db7fd3f7..0b884cf50 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -419,6 +419,7 @@ extern "C" { #define DRM_FORMAT_MOD_VENDOR_ARM 0x08 #define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09 #define DRM_FORMAT_MOD_VENDOR_AMLOGIC 0x0a +#define DRM_FORMAT_MOD_VENDOR_VS 0x0b /* add more to the end as needed */ @@ -1562,6 +1563,32 @@ drm_fourcc_canonicalize_nvidia_format_mod(__u64 modifier) #define AMD_FMT_MOD_CLEAR(field) \ (~((__u64)AMD_FMT_MOD_##field##_MASK << AMD_FMT_MOD_##field##_SHIFT)) +#define DRM_FORMAT_MOD_VS_TYPE_NORMAL 0x00 +#define DRM_FORMAT_MOD_VS_TYPE_COMPRESSED 0x01 +#define DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT 0x02 +#define DRM_FORMAT_MOD_VS_TYPE_MASK ((__u64)0x3 << 54)
+#define fourcc_mod_vs_code(type, val) \
- fourcc_mod_code(VS, ((((__u64)type) << 54) | (val)))
+#define DRM_FORMAT_MOD_VS_NORM_MODE_MASK 0x1F
+#define DRM_FORMAT_MOD_VS_LINEAR 0x00
This define should be removed if it does the same as DRM_FORMAT_MODE_LINEAR.
+#define DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR 0x02 +#define DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR 0x03 +#define DRM_FORMAT_MOD_VS_TILE_8X8 0x04 +#define DRM_FORMAT_MOD_VS_TILE_8X4 0x07 +#define DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR_8X4 0x0B +#define DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR_4X8 0x0C +#define DRM_FORMAT_MOD_VS_TILE_MODE4X4 0x15
The existing formats in this file have documentation on their effects and meaning. You should include similar comments here.
Best regards Thomas
+#define fourcc_mod_vs_norm_code(tile) \
- fourcc_mod_vs_code(DRM_FORMAT_MOD_VS_TYPE_NORMAL, \
(tile))
+#define fourcc_mod_vs_custom_code(tile) \
- fourcc_mod_vs_code(DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT, \
(tile))
- #if defined(__cplusplus) } #endif
Implement drm device registration interface
Signed-off-by: Keith Zhao keith.zhao@starfivetech.com --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/verisilicon/Kconfig | 25 ++ drivers/gpu/drm/verisilicon/Makefile | 13 + drivers/gpu/drm/verisilicon/vs_drv.c | 273 +++++++++++++++++++++ drivers/gpu/drm/verisilicon/vs_drv.h | 54 ++++ drivers/gpu/drm/verisilicon/vs_gem.c | 298 +++++++++++++++++++++++ drivers/gpu/drm/verisilicon/vs_gem.h | 50 ++++ drivers/gpu/drm/verisilicon/vs_modeset.c | 92 +++++++ drivers/gpu/drm/verisilicon/vs_modeset.h | 13 + include/uapi/drm/vs_drm.h | 50 ++++ 11 files changed, 871 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_gem.c create mode 100644 drivers/gpu/drm/verisilicon/vs_gem.h create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.c create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.h create mode 100644 include/uapi/drm/vs_drm.h
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index afb3b2f5f..9ede80ef9 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -363,6 +363,8 @@ source "drivers/gpu/drm/solomon/Kconfig"
source "drivers/gpu/drm/sprd/Kconfig"
+source "drivers/gpu/drm/verisilicon/Kconfig" + config DRM_HYPERV tristate "DRM Support for Hyper-V synthetic video device" depends on DRM && PCI && MMU && HYPERV diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 7a09a89b4..6db3bc397 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -194,3 +194,4 @@ obj-y += gud/ obj-$(CONFIG_DRM_HYPERV) += hyperv/ obj-y += solomon/ obj-$(CONFIG_DRM_SPRD) += sprd/ +obj-$(CONFIG_DRM_VERISILICON) += verisilicon/ diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig new file mode 100644 index 000000000..fcc39dded --- /dev/null +++ b/drivers/gpu/drm/verisilicon/Kconfig @@ -0,0 +1,25 @@ +# 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. + +if DRM_VERISILICON + +config STARFIVE_HDMI + bool "Starfive specific extensions HDMI" + 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. + + To compile this driver as a module, choose M here. +endif diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile new file mode 100644 index 000000000..26cc7e21b --- /dev/null +++ b/drivers/gpu/drm/verisilicon/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 + +vs_drm-objs := vs_dc_hw.o \ + vs_dc.o \ + vs_crtc.o \ + vs_drv.o \ + vs_modeset.o \ + vs_gem.o \ + vs_plane.o + +vs_drm-$(CONFIG_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 new file mode 100644 index 000000000..69591e640 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_drv.c @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + */ + +#include <linux/clk.h> +#include <linux/component.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/iommu.h> +#include <linux/of_graph.h> +#include <linux/of_reserved_mem.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> +#include <linux/version.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_debugfs.h> +#include <drm/drm_drv.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_fbdev_generic.h> +#include <drm/drm_file.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_ioctl.h> +#include <drm/drm_of.h> +#include <drm/drm_prime.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_vblank.h> + +#include "vs_drv.h" +#include "vs_modeset.h" +#include "vs_gem.h" + +#define DRV_NAME "starfive" +#define DRV_DESC "Starfive DRM driver" +#define DRV_DATE "202305161" +#define DRV_MAJOR 1 +#define DRV_MINOR 0 + +static struct platform_driver vs_drm_platform_driver; + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .compat_ioctl = drm_compat_ioctl, + .poll = drm_poll, + .read = drm_read, + .mmap = drm_gem_mmap, +}; + +static struct drm_driver vs_drm_driver = { + .driver_features = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM, + .lastclose = drm_fb_helper_lastclose, + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_import_sg_table = vs_gem_prime_import_sg_table, + .gem_prime_mmap = drm_gem_prime_mmap, + .dumb_create = vs_gem_dumb_create, + .fops = &fops, + .name = DRV_NAME, + .desc = DRV_DESC, + .date = DRV_DATE, + .major = DRV_MAJOR, + .minor = DRV_MINOR, +}; + +void vs_drm_update_pitch_alignment(struct drm_device *drm_dev, + unsigned int alignment) +{ + struct vs_drm_private *priv = to_vs_dev(drm_dev); + + if (alignment > priv->pitch_alignment) + priv->pitch_alignment = alignment; +} + +static int vs_drm_bind(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct vs_drm_private *priv; + int ret; + static u64 dma_mask = DMA_BIT_MASK(40); + struct drm_device *drm_dev; + + /* Remove existing drivers that may own the framebuffer memory. */ + ret = drm_aperture_remove_framebuffers(&vs_drm_driver); + if (ret) { + dev_err(dev, + "Failed to remove existing framebuffers - %d.\n", + ret); + return ret; + } + + priv = devm_drm_dev_alloc(dev, &vs_drm_driver, struct vs_drm_private, base); + if (IS_ERR(priv)) + return PTR_ERR(priv); + + priv->pitch_alignment = 64; + priv->dma_dev = priv->base.dev; + priv->dma_dev->coherent_dma_mask = dma_mask; + drm_dev = &priv->base; + platform_set_drvdata(pdev, drm_dev); + + vs_mode_config_init(drm_dev); + + /* Now try and bind all our sub-components */ + ret = component_bind_all(dev, drm_dev); + if (ret) + goto err_mode; + + ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc); + if (ret) + goto err_bind; + + drm_mode_config_reset(drm_dev); + + drm_kms_helper_poll_init(drm_dev); + + ret = drm_dev_register(drm_dev, 0); + if (ret) + goto err_helper; + + drm_fbdev_generic_setup(drm_dev, 32); + + return 0; + +err_helper: + drm_kms_helper_poll_fini(drm_dev); +err_bind: + component_unbind_all(drm_dev->dev, drm_dev); +err_mode: + drm_mode_config_cleanup(drm_dev); + + return ret; +} + +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[] = { + + + /* connector + encoder*/ +#ifdef CONFIG_STARFIVE_HDMI + &starfive_hdmi_driver, +#endif + +}; + +#define NUM_DRM_DRIVERS \ + (sizeof(drm_sub_drivers) / sizeof(struct platform_driver *)) + +static int compare_dev(struct device *dev, void *data) +{ + return dev == (struct device *)data; +} + +static struct component_match *vs_drm_match_add(struct device *dev) +{ + struct component_match *match = NULL; + int i; + + for (i = 0; i < NUM_DRM_DRIVERS; ++i) { + struct platform_driver *drv = drm_sub_drivers[i]; + struct device *p = NULL, *d; + + while ((d = platform_find_device_by_driver(p, &drv->driver))) { + put_device(p); + + component_match_add(dev, &match, compare_dev, d); + p = d; + } + put_device(p); + } + + return 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, NUM_DRM_DRIVERS); + if (ret) + return ret; + + ret = platform_driver_register(&vs_drm_platform_driver); + if (ret) + platform_unregister_drivers(drm_sub_drivers, NUM_DRM_DRIVERS); + + return ret; +} + +static void __exit vs_drm_fini(void) +{ + platform_driver_unregister(&vs_drm_platform_driver); + platform_unregister_drivers(drm_sub_drivers, NUM_DRM_DRIVERS); +} + +module_init(vs_drm_init); +module_exit(vs_drm_fini); + +MODULE_DESCRIPTION("VeriSilicon DRM Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h new file mode 100644 index 000000000..6ddc99dcf --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_drv.h @@ -0,0 +1,54 @@ +/* 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 <linux/version.h> +#include <drm/drm_drv.h> +#include <drm/drm_gem.h> +#include <drm/drm_managed.h> + +/* + * + * @dma_dev: device for DMA API. + * - use the first attached device if support iommu + else use drm device (only contiguous buffer support) + * @domain: iommu domain for DRM. + * - all DC IOMMU share same domain to reduce mapping + * @pitch_alignment: buffer pitch alignment required by sub-devices. + * + */ +struct vs_drm_private { + struct drm_device base; + struct device *dma_dev; + struct iommu_domain *domain; + unsigned int pitch_alignment; +}; + +static inline struct vs_drm_private * +to_vs_dev(const struct drm_device *dev) +{ + return container_of(dev, struct vs_drm_private, base); +} + +void vs_drm_update_pitch_alignment(struct drm_device *drm_dev, + unsigned int alignment); + + +static inline bool is_iommu_enabled(struct drm_device *dev) +{ + struct vs_drm_private *priv = to_vs_dev(dev); + + return priv->domain ? true : false; +} + +#ifdef CONFIG_STARFIVE_HDMI +extern struct platform_driver starfive_hdmi_driver; +#endif + +#endif /* __VS_DRV_H__ */ diff --git a/drivers/gpu/drm/verisilicon/vs_gem.c b/drivers/gpu/drm/verisilicon/vs_gem.c new file mode 100644 index 000000000..a7d5a5c7b --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_gem.c @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + */ + +#include <linux/dma-buf.h> +#include <linux/of_reserved_mem.h> +#include <drm/drm_gem_dma_helper.h> + +#include "vs_drv.h" +#include "vs_gem.h" + +MODULE_IMPORT_NS(DMA_BUF); + +static const struct drm_gem_object_funcs vs_gem_default_funcs; + +static int vs_gem_alloc_buf(struct vs_gem_object *vs_obj) +{ + struct drm_device *dev = vs_obj->base.base.dev; + unsigned int nr_pages; + struct sg_table sgt; + int ret = -ENOMEM; + + if (vs_obj->base.dma_addr) { + drm_dbg_kms(dev, "already allocated.\n"); + return 0; + } + + vs_obj->base.dma_addr = DMA_ATTR_WRITE_COMBINE | DMA_ATTR_FORCE_CONTIGUOUS + | DMA_ATTR_NO_KERNEL_MAPPING; + + nr_pages = vs_obj->base.base.size >> PAGE_SHIFT; + + vs_obj->pages = kvmalloc_array(nr_pages, sizeof(struct page *), + GFP_KERNEL | __GFP_ZERO); + if (!vs_obj->pages) + return -ENOMEM; + + vs_obj->cookie = dma_alloc_attrs(dev->dev, vs_obj->base.base.size, + &vs_obj->base.dma_addr, GFP_KERNEL, + vs_obj->dma_attrs); + + if (!vs_obj->cookie) { + dev_err(dev->dev, "failed to allocate buffer.\n"); + goto err_free; + } + + vs_obj->iova = vs_obj->base.dma_addr; + + ret = dma_get_sgtable_attrs(dev->dev, &sgt, + vs_obj->cookie, vs_obj->base.dma_addr, + vs_obj->base.base.size, vs_obj->dma_attrs); + if (ret < 0) { + dev_err(dev->dev, "failed to get sgtable.\n"); + goto err_mem_free; + } + + if (drm_prime_sg_to_page_array(&sgt, vs_obj->pages, nr_pages)) { + dev_err(dev->dev, "invalid sgtable.\n"); + ret = -EINVAL; + goto err_sgt_free; + } + + sg_free_table(&sgt); + + return 0; + +err_sgt_free: + sg_free_table(&sgt); +err_mem_free: + dma_free_attrs(dev->dev, vs_obj->base.base.size, vs_obj->cookie, + vs_obj->base.dma_addr, vs_obj->dma_attrs); +err_free: + kvfree(vs_obj->pages); + + return ret; +} + +static void vs_gem_free_buf(struct vs_gem_object *vs_obj) +{ + struct drm_device *dev = vs_obj->base.base.dev; + + if (!vs_obj->base.dma_addr) { + drm_dbg_kms(dev, "dma_addr is invalid.\n"); + return; + } + + dma_free_attrs(dev->dev, vs_obj->base.base.size, vs_obj->cookie, + (dma_addr_t)vs_obj->base.dma_addr, + vs_obj->dma_attrs); + + kvfree(vs_obj->pages); +} + +static void vs_gem_free_object(struct drm_gem_object *obj) +{ + struct vs_gem_object *vs_obj = to_vs_gem_object(obj); + + if (obj->import_attach) + drm_prime_gem_destroy(obj, vs_obj->base.sgt); + else + vs_gem_free_buf(vs_obj); + + drm_gem_object_release(obj); + + kfree(vs_obj); +} + +static struct vs_gem_object *vs_gem_alloc_object(struct drm_device *dev, + size_t size) +{ + struct vs_gem_object *vs_obj; + struct drm_gem_object *obj; + int ret; + + vs_obj = kzalloc(sizeof(*vs_obj), GFP_KERNEL); + if (!vs_obj) + return ERR_PTR(-ENOMEM); + + vs_obj->base.base.size = size; + obj = &vs_obj->base.base; + + ret = drm_gem_object_init(dev, obj, size); + if (ret) + goto err_free; + + vs_obj->base.base.funcs = &vs_gem_default_funcs; + + ret = drm_gem_create_mmap_offset(obj); + if (ret) { + drm_gem_object_release(obj); + goto err_free; + } + + return vs_obj; + +err_free: + kfree(vs_obj); + return ERR_PTR(ret); +} + +static struct vs_gem_object *vs_gem_create_object(struct drm_device *dev, + size_t size) +{ + struct vs_gem_object *vs_obj; + int ret; + + size = PAGE_ALIGN(size); + + vs_obj = vs_gem_alloc_object(dev, size); + if (IS_ERR(vs_obj)) + return vs_obj; + + ret = vs_gem_alloc_buf(vs_obj); + if (ret) { + drm_gem_object_release(&vs_obj->base.base); + kfree(vs_obj); + return ERR_PTR(ret); + } + + return vs_obj; +} + +static struct vs_gem_object *vs_gem_create_with_handle(struct drm_device *dev, + struct drm_file *file, + size_t size, + unsigned int *handle) +{ + struct vs_gem_object *vs_obj; + struct drm_gem_object *obj; + int ret; + + vs_obj = vs_gem_create_object(dev, size); + if (IS_ERR(vs_obj)) + return vs_obj; + + obj = &vs_obj->base.base; + + ret = drm_gem_handle_create(file, obj, handle); + + drm_gem_object_put(obj); + + if (ret) + return ERR_PTR(ret); + + return vs_obj; +} + +static struct sg_table *vs_gem_prime_get_sg_table(struct drm_gem_object *obj) +{ + struct vs_gem_object *vs_obj = to_vs_gem_object(obj); + + return drm_prime_pages_to_sg(obj->dev, vs_obj->pages, + vs_obj->base.base.size >> PAGE_SHIFT); +} + +static int vs_gem_prime_vmap(struct drm_gem_object *obj, struct iosys_map *map) +{ + struct vs_gem_object *vs_obj = to_vs_gem_object(obj); + + vs_obj->base.vaddr = vs_obj->dma_attrs & DMA_ATTR_NO_KERNEL_MAPPING ? + page_address(vs_obj->cookie) : vs_obj->cookie; + + return drm_gem_dma_vmap(&vs_obj->base, map); +} + +static const struct vm_operations_struct vs_vm_ops = { + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, +}; + +static const struct drm_gem_object_funcs vs_gem_default_funcs = { + .free = vs_gem_free_object, + .get_sg_table = vs_gem_prime_get_sg_table, + .vmap = vs_gem_prime_vmap, + .mmap = drm_gem_dma_object_mmap, + .vm_ops = &vs_vm_ops, +}; + +int vs_gem_dumb_create(struct drm_file *file, + struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + struct vs_drm_private *priv = to_vs_dev(dev); + struct vs_gem_object *vs_obj; + unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8); + + if (args->bpp % 10) + args->pitch = ALIGN(pitch, priv->pitch_alignment); + else + /* for costum 10bit format with no bit gaps */ + args->pitch = pitch; + args->size = PAGE_ALIGN(args->pitch * args->height); + vs_obj = vs_gem_create_with_handle(dev, file, args->size, + &args->handle); + return PTR_ERR_OR_ZERO(vs_obj); +} + +struct drm_gem_object * +vs_gem_prime_import_sg_table(struct drm_device *dev, + struct dma_buf_attachment *attach, + struct sg_table *sgt) +{ + struct vs_gem_object *vs_obj; + int npages; + int ret; + struct scatterlist *s; + u32 i; + dma_addr_t expected; + size_t size = attach->dmabuf->size; + + size = PAGE_ALIGN(size); + + vs_obj = vs_gem_alloc_object(dev, size); + if (IS_ERR(vs_obj)) + return ERR_CAST(vs_obj); + + expected = sg_dma_address(sgt->sgl); + for_each_sg(sgt->sgl, s, sgt->nents, i) { + if (sg_dma_address(s) != expected) { + DRM_ERROR("sg_table is not contiguous"); + ret = -EINVAL; + goto err; + } + if (sg_dma_len(s) & (PAGE_SIZE - 1)) { + ret = -EINVAL; + goto err; + } + if (i == 0) + vs_obj->iova = sg_dma_address(s); + expected = sg_dma_address(s) + sg_dma_len(s); + } + + vs_obj->base.dma_addr = sg_dma_address(sgt->sgl); + + npages = vs_obj->base.base.size >> PAGE_SHIFT; + vs_obj->pages = kvmalloc_array(npages, sizeof(struct page *), + GFP_KERNEL); + if (!vs_obj->pages) { + ret = -ENOMEM; + goto err; + } + + ret = drm_prime_sg_to_page_array(sgt, vs_obj->pages, npages); + if (ret) + goto err_free_page; + + vs_obj->base.sgt = sgt; + + return &vs_obj->base.base; + +err_free_page: + kvfree(vs_obj->pages); +err: + vs_gem_free_object(&vs_obj->base.base); + + return ERR_PTR(ret); +} diff --git a/drivers/gpu/drm/verisilicon/vs_gem.h b/drivers/gpu/drm/verisilicon/vs_gem.h new file mode 100644 index 000000000..d9ff6d23b --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_gem.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + */ + +#ifndef __VS_GEM_H__ +#define __VS_GEM_H__ + +#include <linux/dma-buf.h> + +#include <drm/drm_gem.h> +#include <drm/drm_gem_dma_helper.h> +#include <drm/drm_prime.h> + +#include "vs_drv.h" +/* + * + * @base: drm_gem_dma_object. + * @cookie: cookie returned by dma_alloc_attrs + * - not kernel virtual address with DMA_ATTR_NO_KERNEL_MAPPING + * @dma_attrs: attribute for DMA API + * @get_pages: flag for manually applying for non-contiguous memory. + * @pages: Array of backing pages. + * + */ +struct vs_gem_object { + struct drm_gem_dma_object base; + void *cookie; + u32 iova; + unsigned long dma_attrs; + bool get_pages; + struct page **pages; +}; + +static inline struct vs_gem_object * +to_vs_gem_object(const struct drm_gem_object *bo) +{ + return container_of(to_drm_gem_dma_obj(bo), struct vs_gem_object, base); +} + +int vs_gem_dumb_create(struct drm_file *file_priv, + struct drm_device *drm, + struct drm_mode_create_dumb *args); + +struct drm_gem_object * +vs_gem_prime_import_sg_table(struct drm_device *dev, + struct dma_buf_attachment *attach, + struct sg_table *sgt); + +#endif /* __VS_GEM_H__ */ diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.c b/drivers/gpu/drm/verisilicon/vs_modeset.c new file mode 100644 index 000000000..cd0bdf530 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_modeset.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + */ + +#include <linux/module.h> +#include <linux/version.h> + +#include <drm/drm_damage_helper.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_gem.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_gem_dma_helper.h> + +#include "vs_modeset.h" +#include "vs_gem.h" + +#define fourcc_mod_vs_get_type(val) \ + (((val) & DRM_FORMAT_MOD_VS_TYPE_MASK) >> 54) + +struct vs_gem_object *vs_fb_get_gem_obj(struct drm_framebuffer *fb, + unsigned char plane) +{ + if (plane > DRM_FORMAT_MAX_PLANES) + return NULL; + + return to_vs_gem_object(fb->obj[plane]); +} + +static const struct drm_format_info vs_formats[] = { + {.format = DRM_FORMAT_NV12, .depth = 0, .num_planes = 2, .char_per_block = { 20, 40, 0 }, + .block_w = { 4, 4, 0 }, .block_h = { 4, 4, 0 }, .hsub = 2, .vsub = 2, .is_yuv = true}, + {.format = DRM_FORMAT_YUV444, .depth = 0, .num_planes = 3, .char_per_block = { 20, 20, 20 }, + .block_w = { 4, 4, 4 }, .block_h = { 4, 4, 4 }, .hsub = 1, .vsub = 1, .is_yuv = true}, +}; + +static const struct drm_format_info * +vs_lookup_format_info(const struct drm_format_info formats[], + int num_formats, u32 format) +{ + int i; + + for (i = 0; i < num_formats; i++) { + if (formats[i].format == format) + return &formats[i]; + } + + return NULL; +} + +static const struct drm_format_info * +vs_get_format_info(const struct drm_mode_fb_cmd2 *cmd) +{ + if (fourcc_mod_vs_get_type(cmd->modifier[0]) == + DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT) + return vs_lookup_format_info(vs_formats, ARRAY_SIZE(vs_formats), + cmd->pixel_format); + else + return NULL; +} + +static const struct drm_mode_config_funcs vs_mode_config_funcs = { + .fb_create = drm_gem_fb_create, + .get_format_info = vs_get_format_info, + .output_poll_changed = drm_fb_helper_output_poll_changed, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +static struct drm_mode_config_helper_funcs vs_mode_config_helpers = { + .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, +}; + +void vs_mode_config_init(struct drm_device *dev) +{ + drm_mode_config_init(dev); + dev->mode_config.fb_modifiers_not_supported = false; + + if (dev->mode_config.max_width == 0 || + dev->mode_config.max_height == 0) { + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + dev->mode_config.max_width = 4096; + dev->mode_config.max_height = 4096; + } + dev->mode_config.funcs = &vs_mode_config_funcs; + dev->mode_config.helper_private = &vs_mode_config_helpers; +} diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.h b/drivers/gpu/drm/verisilicon/vs_modeset.h new file mode 100644 index 000000000..2e1f04a8a --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_modeset.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd. + */ + +#ifndef __VS_FB_H__ +#define __VS_FB_H__ + +struct vs_gem_object *vs_fb_get_gem_obj(struct drm_framebuffer *fb, + unsigned char plane); + +void vs_mode_config_init(struct drm_device *dev); +#endif /* __VS_FB_H__ */ diff --git a/include/uapi/drm/vs_drm.h b/include/uapi/drm/vs_drm.h new file mode 100644 index 000000000..96b7fc95d --- /dev/null +++ b/include/uapi/drm/vs_drm.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd. + */ + +#ifndef __VS_DRM_H__ +#define __VS_DRM_H__ + +#include "drm.h" + +enum drm_vs_degamma_mode { + VS_DEGAMMA_DISABLE = 0, + VS_DEGAMMA_BT709 = 1, + VS_DEGAMMA_BT2020 = 2, +}; + +enum drm_vs_sync_dc_mode { + VS_SINGLE_DC = 0, + VS_MULTI_DC_PRIMARY = 1, + VS_MULTI_DC_SECONDARY = 2, +}; + +enum drm_vs_mmu_prefetch_mode { + VS_MMU_PREFETCH_DISABLE = 0, + VS_MMU_PREFETCH_ENABLE = 1, +}; + +struct drm_vs_watermark { + __u32 watermark; + __u8 qos_low; + __u8 qos_high; +}; + +struct drm_vs_color_mgmt { + __u32 colorkey; + __u32 colorkey_high; + __u32 clear_value; + bool clear_enable; + bool transparency; +}; + +struct drm_vs_roi { + bool enable; + __u16 roi_x; + __u16 roi_y; + __u16 roi_w; + __u16 roi_h; +}; + +#endif /* __VS_DRM_H__ */
On Tue, Aug 01, 2023 at 06:10:28PM +0800, Keith Zhao wrote:
+#define DRV_NAME "starfive" +#define DRV_DESC "Starfive DRM driver"
Shouldn't it be verisilicon?
+#define DRV_DATE "202305161" +#define DRV_MAJOR 1 +#define DRV_MINOR 0
+static struct platform_driver vs_drm_platform_driver;
You don't seem to need that forward declaration.
+static const struct file_operations fops = {
- .owner = THIS_MODULE,
- .open = drm_open,
- .release = drm_release,
- .unlocked_ioctl = drm_ioctl,
- .compat_ioctl = drm_compat_ioctl,
- .poll = drm_poll,
- .read = drm_read,
- .mmap = drm_gem_mmap,
+};
+static struct drm_driver vs_drm_driver = {
- .driver_features = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
- .lastclose = drm_fb_helper_lastclose,
- .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
- .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
- .gem_prime_import_sg_table = vs_gem_prime_import_sg_table,
- .gem_prime_mmap = drm_gem_prime_mmap,
- .dumb_create = vs_gem_dumb_create,
- .fops = &fops,
- .name = DRV_NAME,
- .desc = DRV_DESC,
- .date = DRV_DATE,
- .major = DRV_MAJOR,
- .minor = DRV_MINOR,
+};
+void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
unsigned int alignment)
+{
- struct vs_drm_private *priv = to_vs_dev(drm_dev);
- if (alignment > priv->pitch_alignment)
priv->pitch_alignment = alignment;
+}
+static int vs_drm_bind(struct device *dev) +{
- struct platform_device *pdev = to_platform_device(dev);
- struct vs_drm_private *priv;
- int ret;
- static u64 dma_mask = DMA_BIT_MASK(40);
- struct drm_device *drm_dev;
- /* Remove existing drivers that may own the framebuffer memory. */
- ret = drm_aperture_remove_framebuffers(&vs_drm_driver);
- if (ret) {
dev_err(dev,
"Failed to remove existing framebuffers - %d.\n",
ret);
return ret;
- }
- priv = devm_drm_dev_alloc(dev, &vs_drm_driver, struct vs_drm_private, base);
if (IS_ERR(priv))
return PTR_ERR(priv);
- priv->pitch_alignment = 64;
- priv->dma_dev = priv->base.dev;
- priv->dma_dev->coherent_dma_mask = dma_mask;
dma_set_coherent_mask()
- drm_dev = &priv->base;
- platform_set_drvdata(pdev, drm_dev);
- vs_mode_config_init(drm_dev);
- /* Now try and bind all our sub-components */
- ret = component_bind_all(dev, drm_dev);
- if (ret)
goto err_mode;
- ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
- if (ret)
goto err_bind;
- drm_mode_config_reset(drm_dev);
- drm_kms_helper_poll_init(drm_dev);
- ret = drm_dev_register(drm_dev, 0);
- if (ret)
goto err_helper;
- drm_fbdev_generic_setup(drm_dev, 32);
- return 0;
+err_helper:
- drm_kms_helper_poll_fini(drm_dev);
+err_bind:
- component_unbind_all(drm_dev->dev, drm_dev);
+err_mode:
- drm_mode_config_cleanup(drm_dev);
If you're using drmm_mode_config_init this can go away.
- return ret;
+}
+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[] = {
- /* connector + encoder*/
+#ifdef CONFIG_STARFIVE_HDMI
- &starfive_hdmi_driver,
+#endif
+};
+#define NUM_DRM_DRIVERS \
- (sizeof(drm_sub_drivers) / sizeof(struct platform_driver *))
+static int compare_dev(struct device *dev, void *data) +{
- return dev == (struct device *)data;
+}
+static struct component_match *vs_drm_match_add(struct device *dev) +{
- struct component_match *match = NULL;
- int i;
- for (i = 0; i < NUM_DRM_DRIVERS; ++i) {
struct platform_driver *drv = drm_sub_drivers[i];
struct device *p = NULL, *d;
while ((d = platform_find_device_by_driver(p, &drv->driver))) {
put_device(p);
component_match_add(dev, &match, compare_dev, d);
p = d;
}
put_device(p);
- }
- return 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, NUM_DRM_DRIVERS);
- if (ret)
return ret;
- ret = platform_driver_register(&vs_drm_platform_driver);
- if (ret)
platform_unregister_drivers(drm_sub_drivers, NUM_DRM_DRIVERS);
- return ret;
+}
+static void __exit vs_drm_fini(void) +{
- platform_driver_unregister(&vs_drm_platform_driver);
- platform_unregister_drivers(drm_sub_drivers, NUM_DRM_DRIVERS);
+}
+module_init(vs_drm_init); +module_exit(vs_drm_fini);
+MODULE_DESCRIPTION("VeriSilicon DRM Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h new file mode 100644 index 000000000..6ddc99dcf --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_drv.h @@ -0,0 +1,54 @@ +/* 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 <linux/version.h> +#include <drm/drm_drv.h> +#include <drm/drm_gem.h> +#include <drm/drm_managed.h>
+/*
- @dma_dev: device for DMA API.
- use the first attached device if support iommu
- else use drm device (only contiguous buffer support)
- @domain: iommu domain for DRM.
- all DC IOMMU share same domain to reduce mapping
- @pitch_alignment: buffer pitch alignment required by sub-devices.
- */
+struct vs_drm_private {
- struct drm_device base;
- struct device *dma_dev;
- struct iommu_domain *domain;
- unsigned int pitch_alignment;
+};
+static inline struct vs_drm_private * +to_vs_dev(const struct drm_device *dev) +{
- return container_of(dev, struct vs_drm_private, base);
+}
+void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
unsigned int alignment);
+static inline bool is_iommu_enabled(struct drm_device *dev) +{
- struct vs_drm_private *priv = to_vs_dev(dev);
- return priv->domain ? true : false;
+}
+#ifdef CONFIG_STARFIVE_HDMI +extern struct platform_driver starfive_hdmi_driver; +#endif
+#endif /* __VS_DRV_H__ */ diff --git a/drivers/gpu/drm/verisilicon/vs_gem.c b/drivers/gpu/drm/verisilicon/vs_gem.c new file mode 100644 index 000000000..a7d5a5c7b --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_gem.c @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
- */
+#include <linux/dma-buf.h> +#include <linux/of_reserved_mem.h> +#include <drm/drm_gem_dma_helper.h>
+#include "vs_drv.h" +#include "vs_gem.h"
+MODULE_IMPORT_NS(DMA_BUF);
+static const struct drm_gem_object_funcs vs_gem_default_funcs;
+static int vs_gem_alloc_buf(struct vs_gem_object *vs_obj) +{
- struct drm_device *dev = vs_obj->base.base.dev;
- unsigned int nr_pages;
- struct sg_table sgt;
- int ret = -ENOMEM;
- if (vs_obj->base.dma_addr) {
drm_dbg_kms(dev, "already allocated.\n");
return 0;
- }
- vs_obj->base.dma_addr = DMA_ATTR_WRITE_COMBINE | DMA_ATTR_FORCE_CONTIGUOUS
| DMA_ATTR_NO_KERNEL_MAPPING;
- nr_pages = vs_obj->base.base.size >> PAGE_SHIFT;
- vs_obj->pages = kvmalloc_array(nr_pages, sizeof(struct page *),
GFP_KERNEL | __GFP_ZERO);
- if (!vs_obj->pages)
return -ENOMEM;
- vs_obj->cookie = dma_alloc_attrs(dev->dev, vs_obj->base.base.size,
&vs_obj->base.dma_addr, GFP_KERNEL,
vs_obj->dma_attrs);
- if (!vs_obj->cookie) {
dev_err(dev->dev, "failed to allocate buffer.\n");
goto err_free;
- }
- vs_obj->iova = vs_obj->base.dma_addr;
- ret = dma_get_sgtable_attrs(dev->dev, &sgt,
vs_obj->cookie, vs_obj->base.dma_addr,
vs_obj->base.base.size, vs_obj->dma_attrs);
- if (ret < 0) {
dev_err(dev->dev, "failed to get sgtable.\n");
goto err_mem_free;
- }
- if (drm_prime_sg_to_page_array(&sgt, vs_obj->pages, nr_pages)) {
dev_err(dev->dev, "invalid sgtable.\n");
ret = -EINVAL;
goto err_sgt_free;
- }
- sg_free_table(&sgt);
- return 0;
+err_sgt_free:
- sg_free_table(&sgt);
+err_mem_free:
dma_free_attrs(dev->dev, vs_obj->base.base.size, vs_obj->cookie,
vs_obj->base.dma_addr, vs_obj->dma_attrs);
+err_free:
- kvfree(vs_obj->pages);
- return ret;
+}
+static void vs_gem_free_buf(struct vs_gem_object *vs_obj) +{
- struct drm_device *dev = vs_obj->base.base.dev;
- if (!vs_obj->base.dma_addr) {
drm_dbg_kms(dev, "dma_addr is invalid.\n");
return;
- }
- dma_free_attrs(dev->dev, vs_obj->base.base.size, vs_obj->cookie,
(dma_addr_t)vs_obj->base.dma_addr,
vs_obj->dma_attrs);
- kvfree(vs_obj->pages);
+}
+static void vs_gem_free_object(struct drm_gem_object *obj) +{
- struct vs_gem_object *vs_obj = to_vs_gem_object(obj);
- if (obj->import_attach)
drm_prime_gem_destroy(obj, vs_obj->base.sgt);
- else
vs_gem_free_buf(vs_obj);
- drm_gem_object_release(obj);
- kfree(vs_obj);
+}
+static struct vs_gem_object *vs_gem_alloc_object(struct drm_device *dev,
size_t size)
+{
- struct vs_gem_object *vs_obj;
- struct drm_gem_object *obj;
- int ret;
- vs_obj = kzalloc(sizeof(*vs_obj), GFP_KERNEL);
- if (!vs_obj)
return ERR_PTR(-ENOMEM);
- vs_obj->base.base.size = size;
- obj = &vs_obj->base.base;
- ret = drm_gem_object_init(dev, obj, size);
- if (ret)
goto err_free;
- vs_obj->base.base.funcs = &vs_gem_default_funcs;
- ret = drm_gem_create_mmap_offset(obj);
- if (ret) {
drm_gem_object_release(obj);
goto err_free;
- }
- return vs_obj;
+err_free:
- kfree(vs_obj);
- return ERR_PTR(ret);
+}
+static struct vs_gem_object *vs_gem_create_object(struct drm_device *dev,
size_t size)
+{
- struct vs_gem_object *vs_obj;
- int ret;
- size = PAGE_ALIGN(size);
- vs_obj = vs_gem_alloc_object(dev, size);
- if (IS_ERR(vs_obj))
return vs_obj;
- ret = vs_gem_alloc_buf(vs_obj);
- if (ret) {
drm_gem_object_release(&vs_obj->base.base);
kfree(vs_obj);
return ERR_PTR(ret);
- }
- return vs_obj;
+}
+static struct vs_gem_object *vs_gem_create_with_handle(struct drm_device *dev,
struct drm_file *file,
size_t size,
unsigned int *handle)
+{
- struct vs_gem_object *vs_obj;
- struct drm_gem_object *obj;
- int ret;
- vs_obj = vs_gem_create_object(dev, size);
- if (IS_ERR(vs_obj))
return vs_obj;
- obj = &vs_obj->base.base;
- ret = drm_gem_handle_create(file, obj, handle);
- drm_gem_object_put(obj);
- if (ret)
return ERR_PTR(ret);
- return vs_obj;
+}
+static struct sg_table *vs_gem_prime_get_sg_table(struct drm_gem_object *obj) +{
- struct vs_gem_object *vs_obj = to_vs_gem_object(obj);
- return drm_prime_pages_to_sg(obj->dev, vs_obj->pages,
vs_obj->base.base.size >> PAGE_SHIFT);
+}
+static int vs_gem_prime_vmap(struct drm_gem_object *obj, struct iosys_map *map) +{
- struct vs_gem_object *vs_obj = to_vs_gem_object(obj);
- vs_obj->base.vaddr = vs_obj->dma_attrs & DMA_ATTR_NO_KERNEL_MAPPING ?
page_address(vs_obj->cookie) : vs_obj->cookie;
- return drm_gem_dma_vmap(&vs_obj->base, map);
+}
This looks *very* similar to the GEM DMA ops. Why do you need to roll your own?
+static const struct vm_operations_struct vs_vm_ops = {
- .open = drm_gem_vm_open,
- .close = drm_gem_vm_close,
+};
+static const struct drm_gem_object_funcs vs_gem_default_funcs = {
- .free = vs_gem_free_object,
- .get_sg_table = vs_gem_prime_get_sg_table,
- .vmap = vs_gem_prime_vmap,
- .mmap = drm_gem_dma_object_mmap,
- .vm_ops = &vs_vm_ops,
+};
+int vs_gem_dumb_create(struct drm_file *file,
struct drm_device *dev,
struct drm_mode_create_dumb *args)
+{
- struct vs_drm_private *priv = to_vs_dev(dev);
- struct vs_gem_object *vs_obj;
- unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
- if (args->bpp % 10)
args->pitch = ALIGN(pitch, priv->pitch_alignment);
- else
/* for costum 10bit format with no bit gaps */
args->pitch = pitch;
- args->size = PAGE_ALIGN(args->pitch * args->height);
- vs_obj = vs_gem_create_with_handle(dev, file, args->size,
&args->handle);
- return PTR_ERR_OR_ZERO(vs_obj);
+}
+struct drm_gem_object * +vs_gem_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach,
struct sg_table *sgt)
+{
- struct vs_gem_object *vs_obj;
- int npages;
- int ret;
- struct scatterlist *s;
- u32 i;
- dma_addr_t expected;
- size_t size = attach->dmabuf->size;
- size = PAGE_ALIGN(size);
- vs_obj = vs_gem_alloc_object(dev, size);
- if (IS_ERR(vs_obj))
return ERR_CAST(vs_obj);
- expected = sg_dma_address(sgt->sgl);
- for_each_sg(sgt->sgl, s, sgt->nents, i) {
if (sg_dma_address(s) != expected) {
DRM_ERROR("sg_table is not contiguous");
ret = -EINVAL;
goto err;
}
if (sg_dma_len(s) & (PAGE_SIZE - 1)) {
ret = -EINVAL;
goto err;
}
if (i == 0)
vs_obj->iova = sg_dma_address(s);
expected = sg_dma_address(s) + sg_dma_len(s);
- }
- vs_obj->base.dma_addr = sg_dma_address(sgt->sgl);
- npages = vs_obj->base.base.size >> PAGE_SHIFT;
- vs_obj->pages = kvmalloc_array(npages, sizeof(struct page *),
GFP_KERNEL);
- if (!vs_obj->pages) {
ret = -ENOMEM;
goto err;
- }
- ret = drm_prime_sg_to_page_array(sgt, vs_obj->pages, npages);
- if (ret)
goto err_free_page;
- vs_obj->base.sgt = sgt;
- return &vs_obj->base.base;
+err_free_page:
- kvfree(vs_obj->pages);
+err:
- vs_gem_free_object(&vs_obj->base.base);
- return ERR_PTR(ret);
+}
Ditto
Maxime
Hi,
On 2023/8/1 18:10, Keith Zhao wrote:
Implement drm device registration interface
Signed-off-by: Keith Zhao keith.zhao@starfivetech.com
drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/verisilicon/Kconfig | 25 ++ drivers/gpu/drm/verisilicon/Makefile | 13 + drivers/gpu/drm/verisilicon/vs_drv.c | 273 +++++++++++++++++++++ drivers/gpu/drm/verisilicon/vs_drv.h | 54 ++++ drivers/gpu/drm/verisilicon/vs_gem.c | 298 +++++++++++++++++++++++ drivers/gpu/drm/verisilicon/vs_gem.h | 50 ++++ drivers/gpu/drm/verisilicon/vs_modeset.c | 92 +++++++ drivers/gpu/drm/verisilicon/vs_modeset.h | 13 + include/uapi/drm/vs_drm.h | 50 ++++ 11 files changed, 871 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_gem.c create mode 100644 drivers/gpu/drm/verisilicon/vs_gem.h create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.c create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.h create mode 100644 include/uapi/drm/vs_drm.h
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index afb3b2f5f..9ede80ef9 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -363,6 +363,8 @@ source "drivers/gpu/drm/solomon/Kconfig" source "drivers/gpu/drm/sprd/Kconfig" +source "drivers/gpu/drm/verisilicon/Kconfig"
- config DRM_HYPERV tristate "DRM Support for Hyper-V synthetic video device" depends on DRM && PCI && MMU && HYPERV
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 7a09a89b4..6db3bc397 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -194,3 +194,4 @@ obj-y += gud/ obj-$(CONFIG_DRM_HYPERV) += hyperv/ obj-y += solomon/ obj-$(CONFIG_DRM_SPRD) += sprd/ +obj-$(CONFIG_DRM_VERISILICON) += verisilicon/ diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig new file mode 100644 index 000000000..fcc39dded --- /dev/null +++ b/drivers/gpu/drm/verisilicon/Kconfig @@ -0,0 +1,25 @@ +# 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.
+if DRM_VERISILICON
+config STARFIVE_HDMI
- bool "Starfive specific extensions HDMI"
- 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.
To compile this driver as a module, choose M here.
+endif
Why not use
diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile new file mode 100644 index 000000000..26cc7e21b --- /dev/null +++ b/drivers/gpu/drm/verisilicon/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0
+vs_drm-objs := vs_dc_hw.o \
vs_dc.o \
vs_crtc.o \
vs_drv.o \
vs_modeset.o \
vs_gem.o \
vs_plane.o
+vs_drm-$(CONFIG_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 new file mode 100644 index 000000000..69591e640 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_drv.c @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
- */
+#include <linux/clk.h> +#include <linux/component.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/iommu.h> +#include <linux/of_graph.h> +#include <linux/of_reserved_mem.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> +#include <linux/version.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_debugfs.h> +#include <drm/drm_drv.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_fbdev_generic.h> +#include <drm/drm_file.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_ioctl.h> +#include <drm/drm_of.h> +#include <drm/drm_prime.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_vblank.h>
+#include "vs_drv.h" +#include "vs_modeset.h" +#include "vs_gem.h"
+#define DRV_NAME "starfive" +#define DRV_DESC "Starfive DRM driver" +#define DRV_DATE "202305161"
The date is not correct here, generally it should have 8 numbers,
while you have 9 digits, why you are so special ?
+#define DRV_MAJOR 1 +#define DRV_MINOR 0
+static struct platform_driver vs_drm_platform_driver;
+static const struct file_operations fops = {
- .owner = THIS_MODULE,
- .open = drm_open,
- .release = drm_release,
- .unlocked_ioctl = drm_ioctl,
- .compat_ioctl = drm_compat_ioctl,
- .poll = drm_poll,
- .read = drm_read,
- .mmap = drm_gem_mmap,
+};
+static struct drm_driver vs_drm_driver = {
- .driver_features = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
- .lastclose = drm_fb_helper_lastclose,
- .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
- .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
You are not using drm-tip branch, in normal case, new patch for drm should be compile against the drm-tip branch.
the drm_gem_prime_handle_to_fd() and drm_gem_prime_fd_to_handle() function is not be exported anymore.
see 71a7974ac7019 ('drm/prime: Unexport helpers for fd/handle conversion')
So, you patch will be pass the compile test, I guess.
- .gem_prime_import_sg_table = vs_gem_prime_import_sg_table,
By implement gem_prime_import_sg_table, while in curiosity,
which drivers you are import the shared buffer from ?
Is this vs_gem_prime_import_sg_table() function really has been used and tested ?
As far as I could understand, most embedded SoC using KMS-RO framework.
By using the KMS-RO framework, typically the GPU driver will import buffer from the Display controller.
So, why you need implement this function interface?
- .gem_prime_mmap = drm_gem_prime_mmap,
This hook is(or going to be) obsolete, see 0adec22702d4 ('drm: Remove struct drm_driver.gem_prime_mmap')
- .dumb_create = vs_gem_dumb_create,
- .fops = &fops,
- .name = DRV_NAME,
- .desc = DRV_DESC,
- .date = DRV_DATE,
- .major = DRV_MAJOR,
- .minor = DRV_MINOR,
+};
+void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
unsigned int alignment)
+{
- struct vs_drm_private *priv = to_vs_dev(drm_dev);
- if (alignment > priv->pitch_alignment)
priv->pitch_alignment = alignment;
+}
+static int vs_drm_bind(struct device *dev) +{
- struct platform_device *pdev = to_platform_device(dev);
- struct vs_drm_private *priv;
- int ret;
- static u64 dma_mask = DMA_BIT_MASK(40);
- struct drm_device *drm_dev;
- /* Remove existing drivers that may own the framebuffer memory. */
- ret = drm_aperture_remove_framebuffers(&vs_drm_driver);
- if (ret) {
dev_err(dev,
"Failed to remove existing framebuffers - %d.\n",
ret);
return ret;
- }
- priv = devm_drm_dev_alloc(dev, &vs_drm_driver, struct vs_drm_private, base);
if (IS_ERR(priv))
return PTR_ERR(priv);
- priv->pitch_alignment = 64;
- priv->dma_dev = priv->base.dev;
- priv->dma_dev->coherent_dma_mask = dma_mask;
- drm_dev = &priv->base;
- platform_set_drvdata(pdev, drm_dev);
- vs_mode_config_init(drm_dev);
- /* Now try and bind all our sub-components */
- ret = component_bind_all(dev, drm_dev);
- if (ret)
goto err_mode;
- ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
- if (ret)
goto err_bind;
- drm_mode_config_reset(drm_dev);
- drm_kms_helper_poll_init(drm_dev);
- ret = drm_dev_register(drm_dev, 0);
- if (ret)
goto err_helper;
- drm_fbdev_generic_setup(drm_dev, 32);
- return 0;
+err_helper:
- drm_kms_helper_poll_fini(drm_dev);
+err_bind:
- component_unbind_all(drm_dev->dev, drm_dev);
+err_mode:
- drm_mode_config_cleanup(drm_dev);
- return ret;
+}
+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[] = {
- /* connector + encoder*/
+#ifdef CONFIG_STARFIVE_HDMI
- &starfive_hdmi_driver,
+#endif
+};
+#define NUM_DRM_DRIVERS \
- (sizeof(drm_sub_drivers) / sizeof(struct platform_driver *))
I think, the macro is bad name,
you have only one drm drivers, all of the rest are sub platform drivers
which need to bind to the only one drm master.
+static int compare_dev(struct device *dev, void *data) +{
- return dev == (struct device *)data;
+}
There already has a 'component_compare_dev()' defined in component.c (see include/linux/component.h)
why you need want implement one yourself.
+static struct component_match *vs_drm_match_add(struct device *dev) +{
- struct component_match *match = NULL;
- int i;
- for (i = 0; i < NUM_DRM_DRIVERS; ++i) {
struct platform_driver *drv = drm_sub_drivers[i];
struct device *p = NULL, *d;
while ((d = platform_find_device_by_driver(p, &drv->driver))) {
put_device(p);
component_match_add(dev, &match, compare_dev, d);
p = d;
}
put_device(p);
- }
- return 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, NUM_DRM_DRIVERS);
- if (ret)
return ret;
- ret = platform_driver_register(&vs_drm_platform_driver);
- if (ret)
platform_unregister_drivers(drm_sub_drivers, NUM_DRM_DRIVERS);
- return ret;
+}
+static void __exit vs_drm_fini(void) +{
- platform_driver_unregister(&vs_drm_platform_driver);
- platform_unregister_drivers(drm_sub_drivers, NUM_DRM_DRIVERS);
+}
+module_init(vs_drm_init); +module_exit(vs_drm_fini);
+MODULE_DESCRIPTION("VeriSilicon DRM Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h new file mode 100644 index 000000000..6ddc99dcf --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_drv.h @@ -0,0 +1,54 @@ +/* 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 <linux/version.h> +#include <drm/drm_drv.h> +#include <drm/drm_gem.h> +#include <drm/drm_managed.h>
+/*
- @dma_dev: device for DMA API.
- use the first attached device if support iommu
- else use drm device (only contiguous buffer support)
- @domain: iommu domain for DRM.
- all DC IOMMU share same domain to reduce mapping
- @pitch_alignment: buffer pitch alignment required by sub-devices.
- */
+struct vs_drm_private {
- struct drm_device base;
- struct device *dma_dev;
- struct iommu_domain *domain;
- unsigned int pitch_alignment;
+};
+static inline struct vs_drm_private * +to_vs_dev(const struct drm_device *dev) +{
- return container_of(dev, struct vs_drm_private, base);
+}
+void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
unsigned int alignment);
+static inline bool is_iommu_enabled(struct drm_device *dev) +{
- struct vs_drm_private *priv = to_vs_dev(dev);
- return priv->domain ? true : false;
+}
+#ifdef CONFIG_STARFIVE_HDMI +extern struct platform_driver starfive_hdmi_driver; +#endif
+#endif /* __VS_DRV_H__ */ diff --git a/drivers/gpu/drm/verisilicon/vs_gem.c b/drivers/gpu/drm/verisilicon/vs_gem.c new file mode 100644 index 000000000..a7d5a5c7b --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_gem.c @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
- */
+#include <linux/dma-buf.h> +#include <linux/of_reserved_mem.h> +#include <drm/drm_gem_dma_helper.h>
+#include "vs_drv.h" +#include "vs_gem.h"
+MODULE_IMPORT_NS(DMA_BUF);
+static const struct drm_gem_object_funcs vs_gem_default_funcs;
+static int vs_gem_alloc_buf(struct vs_gem_object *vs_obj) +{
- struct drm_device *dev = vs_obj->base.base.dev;
- unsigned int nr_pages;
- struct sg_table sgt;
- int ret = -ENOMEM;
- if (vs_obj->base.dma_addr) {
drm_dbg_kms(dev, "already allocated.\n");
return 0;
- }
- vs_obj->base.dma_addr = DMA_ATTR_WRITE_COMBINE | DMA_ATTR_FORCE_CONTIGUOUS
| DMA_ATTR_NO_KERNEL_MAPPING;
- nr_pages = vs_obj->base.base.size >> PAGE_SHIFT;
- vs_obj->pages = kvmalloc_array(nr_pages, sizeof(struct page *),
GFP_KERNEL | __GFP_ZERO);
- if (!vs_obj->pages)
return -ENOMEM;
- vs_obj->cookie = dma_alloc_attrs(dev->dev, vs_obj->base.base.size,
&vs_obj->base.dma_addr, GFP_KERNEL,
vs_obj->dma_attrs);
- if (!vs_obj->cookie) {
dev_err(dev->dev, "failed to allocate buffer.\n");
goto err_free;
- }
- vs_obj->iova = vs_obj->base.dma_addr;
- ret = dma_get_sgtable_attrs(dev->dev, &sgt,
vs_obj->cookie, vs_obj->base.dma_addr,
vs_obj->base.base.size, vs_obj->dma_attrs);
- if (ret < 0) {
dev_err(dev->dev, "failed to get sgtable.\n");
goto err_mem_free;
- }
- if (drm_prime_sg_to_page_array(&sgt, vs_obj->pages, nr_pages)) {
dev_err(dev->dev, "invalid sgtable.\n");
ret = -EINVAL;
goto err_sgt_free;
- }
- sg_free_table(&sgt);
- return 0;
+err_sgt_free:
- sg_free_table(&sgt);
+err_mem_free:
dma_free_attrs(dev->dev, vs_obj->base.base.size, vs_obj->cookie,
vs_obj->base.dma_addr, vs_obj->dma_attrs);
+err_free:
- kvfree(vs_obj->pages);
- return ret;
+}
+static void vs_gem_free_buf(struct vs_gem_object *vs_obj) +{
- struct drm_device *dev = vs_obj->base.base.dev;
- if (!vs_obj->base.dma_addr) {
drm_dbg_kms(dev, "dma_addr is invalid.\n");
return;
- }
- dma_free_attrs(dev->dev, vs_obj->base.base.size, vs_obj->cookie,
(dma_addr_t)vs_obj->base.dma_addr,
vs_obj->dma_attrs);
- kvfree(vs_obj->pages);
+}
+static void vs_gem_free_object(struct drm_gem_object *obj) +{
- struct vs_gem_object *vs_obj = to_vs_gem_object(obj);
- if (obj->import_attach)
drm_prime_gem_destroy(obj, vs_obj->base.sgt);
- else
vs_gem_free_buf(vs_obj);
- drm_gem_object_release(obj);
- kfree(vs_obj);
+}
+static struct vs_gem_object *vs_gem_alloc_object(struct drm_device *dev,
size_t size)
+{
- struct vs_gem_object *vs_obj;
- struct drm_gem_object *obj;
- int ret;
- vs_obj = kzalloc(sizeof(*vs_obj), GFP_KERNEL);
- if (!vs_obj)
return ERR_PTR(-ENOMEM);
- vs_obj->base.base.size = size;
- obj = &vs_obj->base.base;
- ret = drm_gem_object_init(dev, obj, size);
- if (ret)
goto err_free;
- vs_obj->base.base.funcs = &vs_gem_default_funcs;
- ret = drm_gem_create_mmap_offset(obj);
- if (ret) {
drm_gem_object_release(obj);
goto err_free;
- }
- return vs_obj;
+err_free:
- kfree(vs_obj);
- return ERR_PTR(ret);
+}
+static struct vs_gem_object *vs_gem_create_object(struct drm_device *dev,
size_t size)
+{
- struct vs_gem_object *vs_obj;
- int ret;
- size = PAGE_ALIGN(size);
- vs_obj = vs_gem_alloc_object(dev, size);
- if (IS_ERR(vs_obj))
return vs_obj;
- ret = vs_gem_alloc_buf(vs_obj);
- if (ret) {
drm_gem_object_release(&vs_obj->base.base);
kfree(vs_obj);
return ERR_PTR(ret);
- }
- return vs_obj;
+}
+static struct vs_gem_object *vs_gem_create_with_handle(struct drm_device *dev,
struct drm_file *file,
size_t size,
unsigned int *handle)
+{
- struct vs_gem_object *vs_obj;
- struct drm_gem_object *obj;
- int ret;
- vs_obj = vs_gem_create_object(dev, size);
- if (IS_ERR(vs_obj))
return vs_obj;
- obj = &vs_obj->base.base;
- ret = drm_gem_handle_create(file, obj, handle);
- drm_gem_object_put(obj);
- if (ret)
return ERR_PTR(ret);
- return vs_obj;
+}
+static struct sg_table *vs_gem_prime_get_sg_table(struct drm_gem_object *obj) +{
- struct vs_gem_object *vs_obj = to_vs_gem_object(obj);
- return drm_prime_pages_to_sg(obj->dev, vs_obj->pages,
vs_obj->base.base.size >> PAGE_SHIFT);
+}
+static int vs_gem_prime_vmap(struct drm_gem_object *obj, struct iosys_map *map) +{
- struct vs_gem_object *vs_obj = to_vs_gem_object(obj);
- vs_obj->base.vaddr = vs_obj->dma_attrs & DMA_ATTR_NO_KERNEL_MAPPING ?
page_address(vs_obj->cookie) : vs_obj->cookie;
- return drm_gem_dma_vmap(&vs_obj->base, map);
+}
+static const struct vm_operations_struct vs_vm_ops = {
- .open = drm_gem_vm_open,
- .close = drm_gem_vm_close,
+};
+static const struct drm_gem_object_funcs vs_gem_default_funcs = {
- .free = vs_gem_free_object,
- .get_sg_table = vs_gem_prime_get_sg_table,
- .vmap = vs_gem_prime_vmap,
- .mmap = drm_gem_dma_object_mmap,
- .vm_ops = &vs_vm_ops,
+};
+int vs_gem_dumb_create(struct drm_file *file,
struct drm_device *dev,
struct drm_mode_create_dumb *args)
+{
- struct vs_drm_private *priv = to_vs_dev(dev);
- struct vs_gem_object *vs_obj;
- unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
- if (args->bpp % 10)
args->pitch = ALIGN(pitch, priv->pitch_alignment);
- else
/* for costum 10bit format with no bit gaps */
args->pitch = pitch;
- args->size = PAGE_ALIGN(args->pitch * args->height);
- vs_obj = vs_gem_create_with_handle(dev, file, args->size,
&args->handle);
- return PTR_ERR_OR_ZERO(vs_obj);
+}
+struct drm_gem_object * +vs_gem_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach,
struct sg_table *sgt)
+{
- struct vs_gem_object *vs_obj;
- int npages;
- int ret;
- struct scatterlist *s;
- u32 i;
- dma_addr_t expected;
- size_t size = attach->dmabuf->size;
- size = PAGE_ALIGN(size);
- vs_obj = vs_gem_alloc_object(dev, size);
- if (IS_ERR(vs_obj))
return ERR_CAST(vs_obj);
- expected = sg_dma_address(sgt->sgl);
- for_each_sg(sgt->sgl, s, sgt->nents, i) {
if (sg_dma_address(s) != expected) {
DRM_ERROR("sg_table is not contiguous");
ret = -EINVAL;
goto err;
}
if (sg_dma_len(s) & (PAGE_SIZE - 1)) {
ret = -EINVAL;
goto err;
}
if (i == 0)
vs_obj->iova = sg_dma_address(s);
expected = sg_dma_address(s) + sg_dma_len(s);
- }
- vs_obj->base.dma_addr = sg_dma_address(sgt->sgl);
- npages = vs_obj->base.base.size >> PAGE_SHIFT;
- vs_obj->pages = kvmalloc_array(npages, sizeof(struct page *),
GFP_KERNEL);
- if (!vs_obj->pages) {
ret = -ENOMEM;
goto err;
- }
- ret = drm_prime_sg_to_page_array(sgt, vs_obj->pages, npages);
- if (ret)
goto err_free_page;
- vs_obj->base.sgt = sgt;
- return &vs_obj->base.base;
+err_free_page:
- kvfree(vs_obj->pages);
+err:
- vs_gem_free_object(&vs_obj->base.base);
- return ERR_PTR(ret);
+} diff --git a/drivers/gpu/drm/verisilicon/vs_gem.h b/drivers/gpu/drm/verisilicon/vs_gem.h new file mode 100644 index 000000000..d9ff6d23b --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_gem.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
- */
+#ifndef __VS_GEM_H__ +#define __VS_GEM_H__
+#include <linux/dma-buf.h>
+#include <drm/drm_gem.h> +#include <drm/drm_gem_dma_helper.h> +#include <drm/drm_prime.h>
+#include "vs_drv.h" +/*
- @base: drm_gem_dma_object.
- @cookie: cookie returned by dma_alloc_attrs
- not kernel virtual address with DMA_ATTR_NO_KERNEL_MAPPING
- @dma_attrs: attribute for DMA API
- @get_pages: flag for manually applying for non-contiguous memory.
- @pages: Array of backing pages.
- */
+struct vs_gem_object {
- struct drm_gem_dma_object base;
- void *cookie;
- u32 iova;
- unsigned long dma_attrs;
- bool get_pages;
- struct page **pages;
+};
+static inline struct vs_gem_object * +to_vs_gem_object(const struct drm_gem_object *bo) +{
- return container_of(to_drm_gem_dma_obj(bo), struct vs_gem_object, base);
+}
+int vs_gem_dumb_create(struct drm_file *file_priv,
struct drm_device *drm,
struct drm_mode_create_dumb *args);
+struct drm_gem_object * +vs_gem_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach,
struct sg_table *sgt);
+#endif /* __VS_GEM_H__ */ diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.c b/drivers/gpu/drm/verisilicon/vs_modeset.c new file mode 100644 index 000000000..cd0bdf530 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_modeset.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
- */
+#include <linux/module.h> +#include <linux/version.h>
+#include <drm/drm_damage_helper.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_gem.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_gem_dma_helper.h>
+#include "vs_modeset.h" +#include "vs_gem.h"
+#define fourcc_mod_vs_get_type(val) \
- (((val) & DRM_FORMAT_MOD_VS_TYPE_MASK) >> 54)
+struct vs_gem_object *vs_fb_get_gem_obj(struct drm_framebuffer *fb,
unsigned char plane)
+{
- if (plane > DRM_FORMAT_MAX_PLANES)
return NULL;
- return to_vs_gem_object(fb->obj[plane]);
+}
+static const struct drm_format_info vs_formats[] = {
- {.format = DRM_FORMAT_NV12, .depth = 0, .num_planes = 2, .char_per_block = { 20, 40, 0 },
.block_w = { 4, 4, 0 }, .block_h = { 4, 4, 0 }, .hsub = 2, .vsub = 2, .is_yuv = true},
- {.format = DRM_FORMAT_YUV444, .depth = 0, .num_planes = 3, .char_per_block = { 20, 20, 20 },
.block_w = { 4, 4, 4 }, .block_h = { 4, 4, 4 }, .hsub = 1, .vsub = 1, .is_yuv = true},
+};
+static const struct drm_format_info * +vs_lookup_format_info(const struct drm_format_info formats[],
int num_formats, u32 format)
+{
- int i;
- for (i = 0; i < num_formats; i++) {
if (formats[i].format == format)
return &formats[i];
- }
- return NULL;
+}
+static const struct drm_format_info * +vs_get_format_info(const struct drm_mode_fb_cmd2 *cmd) +{
- if (fourcc_mod_vs_get_type(cmd->modifier[0]) ==
DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT)
return vs_lookup_format_info(vs_formats, ARRAY_SIZE(vs_formats),
cmd->pixel_format);
- else
return NULL;
+}
+static const struct drm_mode_config_funcs vs_mode_config_funcs = {
- .fb_create = drm_gem_fb_create,
- .get_format_info = vs_get_format_info,
- .output_poll_changed = drm_fb_helper_output_poll_changed,
- .atomic_check = drm_atomic_helper_check,
- .atomic_commit = drm_atomic_helper_commit,
+};
+static struct drm_mode_config_helper_funcs vs_mode_config_helpers = {
- .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
+};
+void vs_mode_config_init(struct drm_device *dev) +{
- drm_mode_config_init(dev);
- dev->mode_config.fb_modifiers_not_supported = false;
- if (dev->mode_config.max_width == 0 ||
dev->mode_config.max_height == 0) {
Not sure if the 'if' is really needed .
dev->mode_config.min_width = 0;
dev->mode_config.min_height = 0;
dev->mode_config.max_width = 4096;
dev->mode_config.max_height = 4096;
- }
- dev->mode_config.funcs = &vs_mode_config_funcs;
- dev->mode_config.helper_private = &vs_mode_config_helpers;
+} diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.h b/drivers/gpu/drm/verisilicon/vs_modeset.h new file mode 100644 index 000000000..2e1f04a8a --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_modeset.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
- */
+#ifndef __VS_FB_H__ +#define __VS_FB_H__
+struct vs_gem_object *vs_fb_get_gem_obj(struct drm_framebuffer *fb,
unsigned char plane);
+void vs_mode_config_init(struct drm_device *dev); +#endif /* __VS_FB_H__ */ diff --git a/include/uapi/drm/vs_drm.h b/include/uapi/drm/vs_drm.h new file mode 100644 index 000000000..96b7fc95d --- /dev/null +++ b/include/uapi/drm/vs_drm.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/*
- Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
- */
+#ifndef __VS_DRM_H__ +#define __VS_DRM_H__
+#include "drm.h"
+enum drm_vs_degamma_mode {
- VS_DEGAMMA_DISABLE = 0,
- VS_DEGAMMA_BT709 = 1,
- VS_DEGAMMA_BT2020 = 2,
+};
+enum drm_vs_sync_dc_mode {
- VS_SINGLE_DC = 0,
- VS_MULTI_DC_PRIMARY = 1,
- VS_MULTI_DC_SECONDARY = 2,
+};
+enum drm_vs_mmu_prefetch_mode {
- VS_MMU_PREFETCH_DISABLE = 0,
- VS_MMU_PREFETCH_ENABLE = 1,
+};
+struct drm_vs_watermark {
- __u32 watermark;
- __u8 qos_low;
- __u8 qos_high;
+};
+struct drm_vs_color_mgmt {
- __u32 colorkey;
- __u32 colorkey_high;
- __u32 clear_value;
- bool clear_enable;
- bool transparency;
+};
+struct drm_vs_roi {
- bool enable;
- __u16 roi_x;
- __u16 roi_y;
- __u16 roi_w;
- __u16 roi_h;
+};
+#endif /* __VS_DRM_H__ */
Hi,
On 2023/8/1 21:40, suijingfeng wrote:
+if DRM_VERISILICON
+config STARFIVE_HDMI + bool "Starfive specific extensions HDMI" + 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.
+ To compile this driver as a module, choose M here. +endif
Why not use
Why not use the 'depends on DRM_VERISILICON' here ?
```
config STARFIVE_HDMI
depends on DRM_VERISILICON bool "Starfive specific extensions HDMI"
help
```
I see the Kconfig of VC4 using the 'depends on', and most driver using the 'depends on'
Hi,
On 2023/8/1 21:40, suijingfeng wrote:
+#define DRV_DATE "202305161"
The date is not correct here, generally it should have 8 numbers,
while you have 9 digits, why you are so special ?
I'm also interesting in RISCV arch, I got attracted by your patch.
I just want to join into the discussion at here (at my spare time),
So when you see my comments, I hoping that you will not interpret it as hostility.
Welcome contributing. :-)
On 8/1/23 03:10, Keith Zhao wrote:
diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig new file mode 100644 index 000000000..fcc39dded --- /dev/null +++ b/drivers/gpu/drm/verisilicon/Kconfig @@ -0,0 +1,25 @@ +# 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.
s/soc/SoC/ as used below.
This driver provides VeriSilicon kernel mode
setting and buffer management. It does not
provide 2D or 3D acceleration.
+if DRM_VERISILICON
+config STARFIVE_HDMI
- bool "Starfive specific extensions HDMI"
- 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.
To compile this driver as a module, choose M here.
+endif
Hi
Am 01.08.23 um 12:10 schrieb Keith Zhao:
Implement drm device registration interface
Signed-off-by: Keith Zhao keith.zhao@starfivetech.com
drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/verisilicon/Kconfig | 25 ++ drivers/gpu/drm/verisilicon/Makefile | 13 + drivers/gpu/drm/verisilicon/vs_drv.c | 273 +++++++++++++++++++++ drivers/gpu/drm/verisilicon/vs_drv.h | 54 ++++ drivers/gpu/drm/verisilicon/vs_gem.c | 298 +++++++++++++++++++++++ drivers/gpu/drm/verisilicon/vs_gem.h | 50 ++++ drivers/gpu/drm/verisilicon/vs_modeset.c | 92 +++++++ drivers/gpu/drm/verisilicon/vs_modeset.h | 13 + include/uapi/drm/vs_drm.h | 50 ++++ 11 files changed, 871 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_gem.c create mode 100644 drivers/gpu/drm/verisilicon/vs_gem.h create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.c create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.h create mode 100644 include/uapi/drm/vs_drm.h
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index afb3b2f5f..9ede80ef9 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -363,6 +363,8 @@ source "drivers/gpu/drm/solomon/Kconfig" source "drivers/gpu/drm/sprd/Kconfig" +source "drivers/gpu/drm/verisilicon/Kconfig"
- config DRM_HYPERV tristate "DRM Support for Hyper-V synthetic video device" depends on DRM && PCI && MMU && HYPERV
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 7a09a89b4..6db3bc397 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -194,3 +194,4 @@ obj-y += gud/ obj-$(CONFIG_DRM_HYPERV) += hyperv/ obj-y += solomon/ obj-$(CONFIG_DRM_SPRD) += sprd/ +obj-$(CONFIG_DRM_VERISILICON) += verisilicon/ diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig new file mode 100644 index 000000000..fcc39dded --- /dev/null +++ b/drivers/gpu/drm/verisilicon/Kconfig @@ -0,0 +1,25 @@ +# 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.
+if DRM_VERISILICON
Maybe drop this if and make STARFIVE_HDMI depend on DRM_VERISILICON
+config STARFIVE_HDMI
That name is a bit generic. Better do some prefixing, like DRM_VERISILICON_STARFIVE_HDMI
- bool "Starfive specific extensions HDMI"
Maybe say "Starfive HDMI extensions"
- 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.
To compile this driver as a module, choose M here.
+endif diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile new file mode 100644 index 000000000..26cc7e21b --- /dev/null +++ b/drivers/gpu/drm/verisilicon/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0
+vs_drm-objs := vs_dc_hw.o \
vs_dc.o \
vs_crtc.o \
vs_drv.o \
vs_modeset.o \
vs_gem.o \
vs_plane.o
+vs_drm-$(CONFIG_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 new file mode 100644 index 000000000..69591e640 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_drv.c @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
- */
+#include <linux/clk.h> +#include <linux/component.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/iommu.h> +#include <linux/of_graph.h> +#include <linux/of_reserved_mem.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> +#include <linux/version.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_debugfs.h> +#include <drm/drm_drv.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_fbdev_generic.h> +#include <drm/drm_file.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_ioctl.h> +#include <drm/drm_of.h> +#include <drm/drm_prime.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_vblank.h>
+#include "vs_drv.h" +#include "vs_modeset.h" +#include "vs_gem.h"
+#define DRV_NAME "starfive" +#define DRV_DESC "Starfive DRM driver"
Better use Verisilicon consistently throughout your driver. Use Starfive only for Starfive-specific code (like this HDMI extention).
+#define DRV_DATE "202305161" +#define DRV_MAJOR 1 +#define DRV_MINOR 0
+static struct platform_driver vs_drm_platform_driver;
Apparently not needed. Please remove.
+static const struct file_operations fops = {
- .owner = THIS_MODULE,
- .open = drm_open,
- .release = drm_release,
- .unlocked_ioctl = drm_ioctl,
- .compat_ioctl = drm_compat_ioctl,
- .poll = drm_poll,
- .read = drm_read,
- .mmap = drm_gem_mmap,
+};
Please use DEFINE_DRM_GEM_FOPS() to create this instance.
https://elixir.bootlin.com/linux/v6.5/source/include/drm/drm_gem.h#L438
+static struct drm_driver vs_drm_driver = {
- .driver_features = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
- .lastclose = drm_fb_helper_lastclose,
This function pointer is to be removed soon. Please don't set it.
- .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
- .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
These call are the defaults. Please remove the assignments and let the PRIME code handle it by itself. The function pointer are only here for vmwgfx.
- .gem_prime_import_sg_table = vs_gem_prime_import_sg_table,
- .gem_prime_mmap = drm_gem_prime_mmap,
- .dumb_create = vs_gem_dumb_create,
- .fops = &fops,
Better call it vs_drm_fops
- .name = DRV_NAME,
- .desc = DRV_DESC,
- .date = DRV_DATE,
- .major = DRV_MAJOR,
- .minor = DRV_MINOR,
+};
+void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
unsigned int alignment)
+{
- struct vs_drm_private *priv = to_vs_dev(drm_dev);
- if (alignment > priv->pitch_alignment)
priv->pitch_alignment = alignment;
+}
Please remove. There's apparantly no caller for this helper.
+static int vs_drm_bind(struct device *dev) +{
- struct platform_device *pdev = to_platform_device(dev);
- struct vs_drm_private *priv;
- int ret;
- static u64 dma_mask = DMA_BIT_MASK(40);
- struct drm_device *drm_dev;
- /* Remove existing drivers that may own the framebuffer memory. */
- ret = drm_aperture_remove_framebuffers(&vs_drm_driver);
- if (ret) {
dev_err(dev,
"Failed to remove existing framebuffers - %d.\n",
ret);
return ret;
- }
- priv = devm_drm_dev_alloc(dev, &vs_drm_driver, struct vs_drm_private, base);
if (IS_ERR(priv))
return PTR_ERR(priv);
Is it my email program or is the alignment off?
- priv->pitch_alignment = 64;
- priv->dma_dev = priv->base.dev;
- priv->dma_dev->coherent_dma_mask = dma_mask;
- drm_dev = &priv->base;
- platform_set_drvdata(pdev, drm_dev);
- vs_mode_config_init(drm_dev);
- /* Now try and bind all our sub-components */
- ret = component_bind_all(dev, drm_dev);
- if (ret)
goto err_mode;
- ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
- if (ret)
goto err_bind;
- drm_mode_config_reset(drm_dev);
- drm_kms_helper_poll_init(drm_dev);
- ret = drm_dev_register(drm_dev, 0);
- if (ret)
goto err_helper;
- drm_fbdev_generic_setup(drm_dev, 32);
- return 0;
+err_helper:
- drm_kms_helper_poll_fini(drm_dev);
+err_bind:
- component_unbind_all(drm_dev->dev, drm_dev);
+err_mode:
- drm_mode_config_cleanup(drm_dev);
- return ret;
+}
+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[] = {
- /* connector + encoder*/
+#ifdef CONFIG_STARFIVE_HDMI
- &starfive_hdmi_driver,
+#endif
+};
+#define NUM_DRM_DRIVERS \
- (sizeof(drm_sub_drivers) / sizeof(struct platform_driver *))
Replace this define with ARRAY_SIZE() everywhere.
+static int compare_dev(struct device *dev, void *data) +{
- return dev == (struct device *)data;
+}
+static struct component_match *vs_drm_match_add(struct device *dev) +{
- struct component_match *match = NULL;
- int i;
- for (i = 0; i < NUM_DRM_DRIVERS; ++i) {
struct platform_driver *drv = drm_sub_drivers[i];
struct device *p = NULL, *d;
while ((d = platform_find_device_by_driver(p, &drv->driver))) {
put_device(p);
component_match_add(dev, &match, compare_dev, d);
p = d;
}
put_device(p);
- }
- return match ?: ERR_PTR(-ENODEV);
Is there someting between '?' and ':' ?
+}
+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, NUM_DRM_DRIVERS);
- if (ret)
return ret;
- ret = platform_driver_register(&vs_drm_platform_driver);
Please use drm_platform_driver_register() here.
https://elixir.bootlin.com/linux/v6.5/source/include/drm/drm_module.h#L101
Best regards Thomas
- if (ret)
platform_unregister_drivers(drm_sub_drivers, NUM_DRM_DRIVERS);
- return ret;
+}
+static void __exit vs_drm_fini(void) +{
- platform_driver_unregister(&vs_drm_platform_driver);
- platform_unregister_drivers(drm_sub_drivers, NUM_DRM_DRIVERS);
+}
+module_init(vs_drm_init); +module_exit(vs_drm_fini);
+MODULE_DESCRIPTION("VeriSilicon DRM Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h new file mode 100644 index 000000000..6ddc99dcf --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_drv.h @@ -0,0 +1,54 @@ +/* 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 <linux/version.h> +#include <drm/drm_drv.h> +#include <drm/drm_gem.h> +#include <drm/drm_managed.h>
+/*
- @dma_dev: device for DMA API.
- use the first attached device if support iommu
- else use drm device (only contiguous buffer support)
- @domain: iommu domain for DRM.
- all DC IOMMU share same domain to reduce mapping
- @pitch_alignment: buffer pitch alignment required by sub-devices.
- */
+struct vs_drm_private {
It's better called struct vs_drm_device, because it 'inherits' fom drm_device.
- struct drm_device base;
- struct device *dma_dev;
- struct iommu_domain *domain;
- unsigned int pitch_alignment;
+};
+static inline struct vs_drm_private * +to_vs_dev(const struct drm_device *dev)
to_vs_drm_private() (or to_vs_drm_device() if you rename the struct)
+{
- return container_of(dev, struct vs_drm_private, base);
+}
+void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
unsigned int alignment);
Please remove.
+static inline bool is_iommu_enabled(struct drm_device *dev) +{
- struct vs_drm_private *priv = to_vs_dev(dev);
- return priv->domain ? true : false;
+}
+#ifdef CONFIG_STARFIVE_HDMI +extern struct platform_driver starfive_hdmi_driver; +#endif
+#endif /* __VS_DRV_H__ */ diff --git a/drivers/gpu/drm/verisilicon/vs_gem.c b/drivers/gpu/drm/verisilicon/vs_gem.c new file mode 100644 index 000000000..a7d5a5c7b --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_gem.c @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
- */
+#include <linux/dma-buf.h> +#include <linux/of_reserved_mem.h> +#include <drm/drm_gem_dma_helper.h>
+#include "vs_drv.h" +#include "vs_gem.h"
+MODULE_IMPORT_NS(DMA_BUF);
+static const struct drm_gem_object_funcs vs_gem_default_funcs;
+static int vs_gem_alloc_buf(struct vs_gem_object *vs_obj) +{
- struct drm_device *dev = vs_obj->base.base.dev;
- unsigned int nr_pages;
- struct sg_table sgt;
- int ret = -ENOMEM;
- if (vs_obj->base.dma_addr) {
drm_dbg_kms(dev, "already allocated.\n");
Not drm_dbg_kms(). In your driver, please use drm_dbg().
return 0;
- }
- vs_obj->base.dma_addr = DMA_ATTR_WRITE_COMBINE | DMA_ATTR_FORCE_CONTIGUOUS
| DMA_ATTR_NO_KERNEL_MAPPING;
- nr_pages = vs_obj->base.base.size >> PAGE_SHIFT;
- vs_obj->pages = kvmalloc_array(nr_pages, sizeof(struct page *),
GFP_KERNEL | __GFP_ZERO);
- if (!vs_obj->pages)
return -ENOMEM;
- vs_obj->cookie = dma_alloc_attrs(dev->dev, vs_obj->base.base.size,
&vs_obj->base.dma_addr, GFP_KERNEL,
vs_obj->dma_attrs);
- if (!vs_obj->cookie) {
dev_err(dev->dev, "failed to allocate buffer.\n");
Please replace all calls to dev_err() by drm_err() in all of your patches. Same for dev_info(), dev_warn(), etc. The only exception is in code that does not have a DRM dvice, such as the device-probing code.
goto err_free;
- }
- vs_obj->iova = vs_obj->base.dma_addr;
- ret = dma_get_sgtable_attrs(dev->dev, &sgt,
vs_obj->cookie, vs_obj->base.dma_addr,
vs_obj->base.base.size, vs_obj->dma_attrs);
- if (ret < 0) {
dev_err(dev->dev, "failed to get sgtable.\n");
goto err_mem_free;
- }
- if (drm_prime_sg_to_page_array(&sgt, vs_obj->pages, nr_pages)) {
dev_err(dev->dev, "invalid sgtable.\n");
ret = -EINVAL;
goto err_sgt_free;
- }
- sg_free_table(&sgt);
- return 0;
+err_sgt_free:
- sg_free_table(&sgt);
+err_mem_free:
dma_free_attrs(dev->dev, vs_obj->base.base.size, vs_obj->cookie,
vs_obj->base.dma_addr, vs_obj->dma_attrs);
Indention
+err_free:
- kvfree(vs_obj->pages);
- return ret;
+}
+static void vs_gem_free_buf(struct vs_gem_object *vs_obj) +{
- struct drm_device *dev = vs_obj->base.base.dev;
- if (!vs_obj->base.dma_addr) {
drm_dbg_kms(dev, "dma_addr is invalid.\n");
return;
- }
- dma_free_attrs(dev->dev, vs_obj->base.base.size, vs_obj->cookie,
(dma_addr_t)vs_obj->base.dma_addr,
vs_obj->dma_attrs);
- kvfree(vs_obj->pages);
+}
+static void vs_gem_free_object(struct drm_gem_object *obj) +{
- struct vs_gem_object *vs_obj = to_vs_gem_object(obj);
- if (obj->import_attach)
drm_prime_gem_destroy(obj, vs_obj->base.sgt);
- else
vs_gem_free_buf(vs_obj);
- drm_gem_object_release(obj);
- kfree(vs_obj);
+}
+static struct vs_gem_object *vs_gem_alloc_object(struct drm_device *dev,
size_t size)
+{
- struct vs_gem_object *vs_obj;
- struct drm_gem_object *obj;
- int ret;
- vs_obj = kzalloc(sizeof(*vs_obj), GFP_KERNEL);
- if (!vs_obj)
return ERR_PTR(-ENOMEM);
- vs_obj->base.base.size = size;
- obj = &vs_obj->base.base;
- ret = drm_gem_object_init(dev, obj, size);
- if (ret)
goto err_free;
- vs_obj->base.base.funcs = &vs_gem_default_funcs;
- ret = drm_gem_create_mmap_offset(obj);
- if (ret) {
drm_gem_object_release(obj);
goto err_free;
- }
- return vs_obj;
+err_free:
- kfree(vs_obj);
- return ERR_PTR(ret);
+}
+static struct vs_gem_object *vs_gem_create_object(struct drm_device *dev,
size_t size)
+{
- struct vs_gem_object *vs_obj;
- int ret;
- size = PAGE_ALIGN(size);
- vs_obj = vs_gem_alloc_object(dev, size);
- if (IS_ERR(vs_obj))
return vs_obj;
- ret = vs_gem_alloc_buf(vs_obj);
- if (ret) {
drm_gem_object_release(&vs_obj->base.base);
kfree(vs_obj);
return ERR_PTR(ret);
- }
- return vs_obj;
+}
+static struct vs_gem_object *vs_gem_create_with_handle(struct drm_device *dev,
struct drm_file *file,
size_t size,
unsigned int *handle)
+{
- struct vs_gem_object *vs_obj;
- struct drm_gem_object *obj;
- int ret;
- vs_obj = vs_gem_create_object(dev, size);
- if (IS_ERR(vs_obj))
return vs_obj;
- obj = &vs_obj->base.base;
- ret = drm_gem_handle_create(file, obj, handle);
- drm_gem_object_put(obj);
- if (ret)
return ERR_PTR(ret);
- return vs_obj;
+}
+static struct sg_table *vs_gem_prime_get_sg_table(struct drm_gem_object *obj) +{
- struct vs_gem_object *vs_obj = to_vs_gem_object(obj);
- return drm_prime_pages_to_sg(obj->dev, vs_obj->pages,
vs_obj->base.base.size >> PAGE_SHIFT);
+}
+static int vs_gem_prime_vmap(struct drm_gem_object *obj, struct iosys_map *map) +{
- struct vs_gem_object *vs_obj = to_vs_gem_object(obj);
- vs_obj->base.vaddr = vs_obj->dma_attrs & DMA_ATTR_NO_KERNEL_MAPPING ?
page_address(vs_obj->cookie) : vs_obj->cookie;
- return drm_gem_dma_vmap(&vs_obj->base, map);
+}
+static const struct vm_operations_struct vs_vm_ops = {
- .open = drm_gem_vm_open,
- .close = drm_gem_vm_close,
+};
+static const struct drm_gem_object_funcs vs_gem_default_funcs = {
- .free = vs_gem_free_object,
- .get_sg_table = vs_gem_prime_get_sg_table,
- .vmap = vs_gem_prime_vmap,
- .mmap = drm_gem_dma_object_mmap,
- .vm_ops = &vs_vm_ops,
+};
+int vs_gem_dumb_create(struct drm_file *file,
struct drm_device *dev,
struct drm_mode_create_dumb *args)
+{
- struct vs_drm_private *priv = to_vs_dev(dev);
- struct vs_gem_object *vs_obj;
- unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
- if (args->bpp % 10)
args->pitch = ALIGN(pitch, priv->pitch_alignment);
- else
/* for costum 10bit format with no bit gaps */
args->pitch = pitch;
- args->size = PAGE_ALIGN(args->pitch * args->height);
- vs_obj = vs_gem_create_with_handle(dev, file, args->size,
&args->handle);
- return PTR_ERR_OR_ZERO(vs_obj);
+}
+struct drm_gem_object * +vs_gem_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach,
struct sg_table *sgt)
+{
- struct vs_gem_object *vs_obj;
- int npages;
- int ret;
- struct scatterlist *s;
- u32 i;
- dma_addr_t expected;
- size_t size = attach->dmabuf->size;
- size = PAGE_ALIGN(size);
- vs_obj = vs_gem_alloc_object(dev, size);
- if (IS_ERR(vs_obj))
return ERR_CAST(vs_obj);
- expected = sg_dma_address(sgt->sgl);
- for_each_sg(sgt->sgl, s, sgt->nents, i) {
if (sg_dma_address(s) != expected) {
DRM_ERROR("sg_table is not contiguous");
ret = -EINVAL;
goto err;
}
if (sg_dma_len(s) & (PAGE_SIZE - 1)) {
ret = -EINVAL;
goto err;
}
if (i == 0)
vs_obj->iova = sg_dma_address(s);
expected = sg_dma_address(s) + sg_dma_len(s);
- }
- vs_obj->base.dma_addr = sg_dma_address(sgt->sgl);
- npages = vs_obj->base.base.size >> PAGE_SHIFT;
- vs_obj->pages = kvmalloc_array(npages, sizeof(struct page *),
GFP_KERNEL);
- if (!vs_obj->pages) {
ret = -ENOMEM;
goto err;
- }
- ret = drm_prime_sg_to_page_array(sgt, vs_obj->pages, npages);
- if (ret)
goto err_free_page;
- vs_obj->base.sgt = sgt;
- return &vs_obj->base.base;
+err_free_page:
- kvfree(vs_obj->pages);
+err:
- vs_gem_free_object(&vs_obj->base.base);
- return ERR_PTR(ret);
+} diff --git a/drivers/gpu/drm/verisilicon/vs_gem.h b/drivers/gpu/drm/verisilicon/vs_gem.h new file mode 100644 index 000000000..d9ff6d23b --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_gem.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
- */
+#ifndef __VS_GEM_H__ +#define __VS_GEM_H__
+#include <linux/dma-buf.h>
+#include <drm/drm_gem.h> +#include <drm/drm_gem_dma_helper.h> +#include <drm/drm_prime.h>
+#include "vs_drv.h" +/*
- @base: drm_gem_dma_object.
- @cookie: cookie returned by dma_alloc_attrs
- not kernel virtual address with DMA_ATTR_NO_KERNEL_MAPPING
- @dma_attrs: attribute for DMA API
- @get_pages: flag for manually applying for non-contiguous memory.
- @pages: Array of backing pages.
- */
+struct vs_gem_object {
- struct drm_gem_dma_object base;
You are replacing almost all of the GEM DMA object's helper code.
Either inherit directly from drm_gem_object drop the dependency entirely, or you could try to fit your code into GEM DMA as well.
Best regards Thomas
- void *cookie;
- u32 iova;
- unsigned long dma_attrs;
- bool get_pages;
- struct page **pages;
+};
+static inline struct vs_gem_object * +to_vs_gem_object(const struct drm_gem_object *bo) +{
- return container_of(to_drm_gem_dma_obj(bo), struct vs_gem_object, base);
+}
+int vs_gem_dumb_create(struct drm_file *file_priv,
struct drm_device *drm,
struct drm_mode_create_dumb *args);
+struct drm_gem_object * +vs_gem_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach,
struct sg_table *sgt);
+#endif /* __VS_GEM_H__ */ diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.c b/drivers/gpu/drm/verisilicon/vs_modeset.c new file mode 100644 index 000000000..cd0bdf530 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_modeset.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
- */
+#include <linux/module.h> +#include <linux/version.h>
+#include <drm/drm_damage_helper.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_gem.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_gem_dma_helper.h>
+#include "vs_modeset.h" +#include "vs_gem.h"
+#define fourcc_mod_vs_get_type(val) \
- (((val) & DRM_FORMAT_MOD_VS_TYPE_MASK) >> 54)
+struct vs_gem_object *vs_fb_get_gem_obj(struct drm_framebuffer *fb,
unsigned char plane)
+{
- if (plane > DRM_FORMAT_MAX_PLANES)
return NULL;
- return to_vs_gem_object(fb->obj[plane]);
Please use
drm_gem_object *gem = drm_gem_fb_get_obj(fb,plane) if (!gem) return NULL return to_vs_gem_object(gem);
This will do the right thing, print the appropriate warnings and hide the details of the implementation.
+}
+static const struct drm_format_info vs_formats[] = {
- {.format = DRM_FORMAT_NV12, .depth = 0, .num_planes = 2, .char_per_block = { 20, 40, 0 },
.block_w = { 4, 4, 0 }, .block_h = { 4, 4, 0 }, .hsub = 2, .vsub = 2, .is_yuv = true},
- {.format = DRM_FORMAT_YUV444, .depth = 0, .num_planes = 3, .char_per_block = { 20, 20, 20 },
.block_w = { 4, 4, 4 }, .block_h = { 4, 4, 4 }, .hsub = 1, .vsub = 1, .is_yuv = true},
+};
+static const struct drm_format_info * +vs_lookup_format_info(const struct drm_format_info formats[],
int num_formats, u32 format)
+{
- int i;
- for (i = 0; i < num_formats; i++) {
if (formats[i].format == format)
return &formats[i];
- }
- return NULL;
+}
+static const struct drm_format_info * +vs_get_format_info(const struct drm_mode_fb_cmd2 *cmd) +{
- if (fourcc_mod_vs_get_type(cmd->modifier[0]) ==
DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT)
return vs_lookup_format_info(vs_formats, ARRAY_SIZE(vs_formats),
cmd->pixel_format);
- else
return NULL;
+}
+static const struct drm_mode_config_funcs vs_mode_config_funcs = {
- .fb_create = drm_gem_fb_create,
- .get_format_info = vs_get_format_info,
- .output_poll_changed = drm_fb_helper_output_poll_changed,
Another deprecated callback. Don't assign to it. Probe helpers will do the right thing.
- .atomic_check = drm_atomic_helper_check,
- .atomic_commit = drm_atomic_helper_commit,
+};
+static struct drm_mode_config_helper_funcs vs_mode_config_helpers = {
- .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
+};
+void vs_mode_config_init(struct drm_device *dev) +{
- drm_mode_config_init(dev);
- dev->mode_config.fb_modifiers_not_supported = false;
- if (dev->mode_config.max_width == 0 ||
dev->mode_config.max_height == 0) {
dev->mode_config.min_width = 0;
dev->mode_config.min_height = 0;
dev->mode_config.max_width = 4096;
dev->mode_config.max_height = 4096;
- }
- dev->mode_config.funcs = &vs_mode_config_funcs;
- dev->mode_config.helper_private = &vs_mode_config_helpers;
+} diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.h b/drivers/gpu/drm/verisilicon/vs_modeset.h new file mode 100644 index 000000000..2e1f04a8a --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_modeset.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
- */
+#ifndef __VS_FB_H__ +#define __VS_FB_H__
+struct vs_gem_object *vs_fb_get_gem_obj(struct drm_framebuffer *fb,
unsigned char plane);
+void vs_mode_config_init(struct drm_device *dev); +#endif /* __VS_FB_H__ */ diff --git a/include/uapi/drm/vs_drm.h b/include/uapi/drm/vs_drm.h new file mode 100644 index 000000000..96b7fc95d --- /dev/null +++ b/include/uapi/drm/vs_drm.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/*
- Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
- */
+#ifndef __VS_DRM_H__ +#define __VS_DRM_H__
+#include "drm.h"
+enum drm_vs_degamma_mode {
- VS_DEGAMMA_DISABLE = 0,
- VS_DEGAMMA_BT709 = 1,
- VS_DEGAMMA_BT2020 = 2,
+};
+enum drm_vs_sync_dc_mode {
- VS_SINGLE_DC = 0,
- VS_MULTI_DC_PRIMARY = 1,
- VS_MULTI_DC_SECONDARY = 2,
+};
+enum drm_vs_mmu_prefetch_mode {
- VS_MMU_PREFETCH_DISABLE = 0,
- VS_MMU_PREFETCH_ENABLE = 1,
+};
+struct drm_vs_watermark {
- __u32 watermark;
- __u8 qos_low;
- __u8 qos_high;
+};
+struct drm_vs_color_mgmt {
- __u32 colorkey;
- __u32 colorkey_high;
- __u32 clear_value;
- bool clear_enable;
- bool transparency;
+};
+struct drm_vs_roi {
- bool enable;
- __u16 roi_x;
- __u16 roi_y;
- __u16 roi_w;
- __u16 roi_h;
+};
+#endif /* __VS_DRM_H__ */
add 2 crtcs and 8 planes in vs-drm
Signed-off-by: Keith Zhao keith.zhao@starfivetech.com --- drivers/gpu/drm/verisilicon/vs_crtc.c | 365 +++++ drivers/gpu/drm/verisilicon/vs_crtc.h | 54 + drivers/gpu/drm/verisilicon/vs_dc.c | 1036 ++++++++++++ drivers/gpu/drm/verisilicon/vs_dc.h | 87 + drivers/gpu/drm/verisilicon/vs_dc_hw.c | 2008 ++++++++++++++++++++++++ drivers/gpu/drm/verisilicon/vs_dc_hw.h | 496 ++++++ drivers/gpu/drm/verisilicon/vs_drv.c | 3 +- drivers/gpu/drm/verisilicon/vs_plane.c | 502 ++++++ drivers/gpu/drm/verisilicon/vs_plane.h | 65 + drivers/gpu/drm/verisilicon/vs_type.h | 70 + 10 files changed, 4685 insertions(+), 1 deletion(-) 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/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c new file mode 100644 index 000000000..6a5af8d8c --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + * + */ + +#include <linux/clk.h> +#include <linux/debugfs.h> +#include <linux/media-bus-format.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_atomic.h> +#include <drm/drm_crtc.h> +#include <drm/drm_gem_atomic_helper.h> +#include <drm/drm_vblank.h> +#include <drm/vs_drm.h> + +#include "vs_crtc.h" +#include "vs_dc.h" +#include "vs_drv.h" + +static void vs_crtc_reset(struct drm_crtc *crtc) +{ + struct vs_crtc_state *state; + + if (crtc->state) { + __drm_atomic_helper_crtc_destroy_state(crtc->state); + + state = to_vs_crtc_state(crtc->state); + kfree(state); + crtc->state = NULL; + } + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return; + + __drm_atomic_helper_crtc_reset(crtc, &state->base); + + state->sync_mode = VS_SINGLE_DC; + state->output_fmt = MEDIA_BUS_FMT_RBG888_1X24; + state->encoder_type = DRM_MODE_ENCODER_NONE; +} + +static struct drm_crtc_state * +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc) +{ + struct vs_crtc_state *ori_state; + struct vs_crtc_state *state; + + if (!crtc->state) + return NULL; + + ori_state = to_vs_crtc_state(crtc->state); + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base); + + state->sync_mode = ori_state->sync_mode; + state->output_fmt = ori_state->output_fmt; + state->encoder_type = ori_state->encoder_type; + state->bg_color = ori_state->bg_color; + state->bpp = ori_state->bpp; + state->sync_enable = ori_state->sync_enable; + state->dither_enable = ori_state->dither_enable; + state->underflow = ori_state->underflow; + + return &state->base; +} + +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + __drm_atomic_helper_crtc_destroy_state(state); + kfree(to_vs_crtc_state(state)); +} + +static int vs_crtc_atomic_set_property(struct drm_crtc *crtc, + struct drm_crtc_state *state, + struct drm_property *property, + uint64_t val) +{ + struct vs_crtc *vs_crtc = to_vs_crtc(crtc); + struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(state); + + if (property == vs_crtc->sync_mode) + vs_crtc_state->sync_mode = val; + else if (property == vs_crtc->mmu_prefetch) + vs_crtc_state->mmu_prefetch = val; + else if (property == vs_crtc->bg_color) + vs_crtc_state->bg_color = val; + else if (property == vs_crtc->panel_sync) + vs_crtc_state->sync_enable = val; + else if (property == vs_crtc->dither) + vs_crtc_state->dither_enable = val; + else + return -EINVAL; + + return 0; +} + +static int vs_crtc_atomic_get_property(struct drm_crtc *crtc, + const struct drm_crtc_state *state, + struct drm_property *property, + uint64_t *val) +{ + struct vs_crtc *vs_crtc = to_vs_crtc(crtc); + const struct vs_crtc_state *vs_crtc_state = + container_of(state, const struct vs_crtc_state, base); + + if (property == vs_crtc->sync_mode) + *val = vs_crtc_state->sync_mode; + else if (property == vs_crtc->mmu_prefetch) + *val = vs_crtc_state->mmu_prefetch; + else if (property == vs_crtc->bg_color) + *val = vs_crtc_state->bg_color; + else if (property == vs_crtc->panel_sync) + *val = vs_crtc_state->sync_enable; + else if (property == vs_crtc->dither) + *val = vs_crtc_state->dither_enable; + else + return -EINVAL; + + return 0; +} + +static int vs_crtc_late_register(struct drm_crtc *crtc) +{ + return 0; +} + +static int vs_crtc_enable_vblank(struct drm_crtc *crtc) +{ + struct vs_crtc *vs_crtc = to_vs_crtc(crtc); + + vs_dc_enable_vblank(vs_crtc->dev, true); + + return 0; +} + +static void vs_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct vs_crtc *vs_crtc = to_vs_crtc(crtc); + + vs_dc_enable_vblank(vs_crtc->dev, false); +} + +static const struct drm_crtc_funcs vs_crtc_funcs = { + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .reset = vs_crtc_reset, + .atomic_duplicate_state = vs_crtc_atomic_duplicate_state, + .atomic_destroy_state = vs_crtc_atomic_destroy_state, + .atomic_set_property = vs_crtc_atomic_set_property, + .atomic_get_property = vs_crtc_atomic_get_property, + .late_register = vs_crtc_late_register, + .enable_vblank = vs_crtc_enable_vblank, + .disable_vblank = vs_crtc_disable_vblank, +}; + +static u8 cal_pixel_bits(u32 bus_format) +{ + u8 bpp; + + switch (bus_format) { + case MEDIA_BUS_FMT_RGB565_1X16: + case MEDIA_BUS_FMT_UYVY8_1X16: + bpp = 16; + break; + case MEDIA_BUS_FMT_RGB666_1X18: + case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: + bpp = 18; + break; + case MEDIA_BUS_FMT_UYVY10_1X20: + bpp = 20; + break; + case MEDIA_BUS_FMT_BGR888_1X24: + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + case MEDIA_BUS_FMT_YUV8_1X24: + bpp = 24; + break; + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + case MEDIA_BUS_FMT_YUV10_1X30: + bpp = 30; + break; + default: + bpp = 24; + break; + } + + return bpp; +} + +static bool vs_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct vs_crtc *vs_crtc = to_vs_crtc(crtc); + + return vs_dc_mode_fixup(vs_crtc->dev, mode, adjusted_mode); +} + +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_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state); + + vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt); + + vs_dc_enable(vs_crtc->dev, 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); + + drm_crtc_vblank_off(crtc); + + vs_dc_disable(vs_crtc->dev, crtc); + + if (crtc->state->event && !crtc->state->active) { + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + spin_unlock_irq(&crtc->dev->event_lock); + + crtc->state->event = NULL; + } +} + +static void vs_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, + crtc); + + struct vs_crtc *vs_crtc = to_vs_crtc(crtc); + struct device *dev = vs_crtc->dev; + struct drm_property_blob *blob = crtc->state->gamma_lut; + struct drm_color_lut *lut; + + if (crtc_state->color_mgmt_changed) { + if (blob && blob->length) { + lut = blob->data; + vs_dc_set_gamma(dev, crtc, lut, + blob->length / sizeof(*lut)); + vs_dc_enable_gamma(dev, crtc, true); + } else { + vs_dc_enable_gamma(dev, 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; + + vs_dc_commit(vs_crtc->dev); + + if (event) { + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_arm_vblank_event(crtc, event); + spin_unlock_irq(&crtc->dev->event_lock); + crtc->state->event = NULL; + } +} + +static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = { + .mode_fixup = vs_crtc_mode_fixup, + .atomic_enable = vs_crtc_atomic_enable, + .atomic_disable = vs_crtc_atomic_disable, + .atomic_begin = vs_crtc_atomic_begin, + .atomic_flush = vs_crtc_atomic_flush, +}; + +static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = { + { VS_SINGLE_DC, "single dc mode" }, + { VS_MULTI_DC_PRIMARY, "primary dc for multi dc mode" }, + { VS_MULTI_DC_SECONDARY, "secondary dc for multi dc mode" }, +}; + +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev, + struct vs_dc_info *info) +{ + struct vs_crtc *crtc; + int ret; + + if (!info) + return NULL; + + crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL); + if (!crtc) + return NULL; + + ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base, + NULL, NULL, &vs_crtc_funcs, + info->name ? info->name : NULL); + if (ret) + return NULL; + + drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs); + + /* Set up the crtc properties */ + if (info->pipe_sync) { + crtc->sync_mode = drm_property_create_enum(drm_dev, 0, + "SYNC_MODE", + vs_sync_mode_enum_list, + ARRAY_SIZE(vs_sync_mode_enum_list)); + + if (!crtc->sync_mode) + return NULL; + + drm_object_attach_property(&crtc->base.base, + crtc->sync_mode, + VS_SINGLE_DC); + } + + 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); + } + + if (info->background) { + crtc->bg_color = drm_property_create_range(drm_dev, 0, + "BG_COLOR", 0, 0xffffffff); + + if (!crtc->bg_color) + return NULL; + + drm_object_attach_property(&crtc->base.base, crtc->bg_color, 0); + } + + if (info->panel_sync) { + crtc->panel_sync = drm_property_create_bool(drm_dev, 0, "SYNC_ENABLED"); + + if (!crtc->panel_sync) + return NULL; + + drm_object_attach_property(&crtc->base.base, crtc->panel_sync, 0); + } + + crtc->dither = drm_property_create_bool(drm_dev, 0, "DITHER_ENABLED"); + if (!crtc->dither) + return NULL; + + drm_object_attach_property(&crtc->base.base, crtc->dither, 0); + + crtc->max_bpc = info->max_bpc; + crtc->color_formats = info->color_formats; + return crtc; +} diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h new file mode 100644 index 000000000..92fcbcfc1 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_crtc.h @@ -0,0 +1,54 @@ +/* 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 sync_mode; + u32 output_fmt; + u32 bg_color; + u8 encoder_type; + u8 mmu_prefetch; + u8 bpp; + bool sync_enable; + bool dither_enable; + bool underflow; +}; + +struct vs_crtc { + struct drm_crtc base; + struct device *dev; + unsigned int max_bpc; + unsigned int color_formats; + + struct drm_property *sync_mode; + struct drm_property *mmu_prefetch; + struct drm_property *bg_color; + struct drm_property *panel_sync; + struct drm_property *dither; +}; + +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev, + struct vs_dc_info *info); + +static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc) +{ + return container_of(crtc, struct vs_crtc, base); +} + +static inline struct vs_crtc_state * +to_vs_crtc_state(struct drm_crtc_state *state) +{ + return container_of(state, struct vs_crtc_state, base); +} +#endif /* __VS_CRTC_H__ */ diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c new file mode 100644 index 000000000..44d9f0c18 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_dc.c @@ -0,0 +1,1036 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + */ + +#include <linux/component.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/media-bus-format.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_blend.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_vblank.h> +#include <drm/vs_drm.h> + +#include "vs_crtc.h" +#include "vs_dc_hw.h" +#include "vs_dc.h" +#include "vs_drv.h" +#include "vs_type.h" + +static const char * const vout_clocks[] = { + "vout_noc_disp", + "vout_pix0", + "vout_pix1", + "vout_axi", + "vout_core", + "vout_vout_ahb", + "hdmitx0_pixel", + "vout_dc8200", + +}; + +static const char * const vout_resets[] = { + "vout_axi", + "vout_ahb", + "vout_core", +}; + +static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb) +{ + u8 f = FORMAT_A8R8G8B8; + + switch (format) { + case DRM_FORMAT_XRGB4444: + case DRM_FORMAT_RGBX4444: + case DRM_FORMAT_XBGR4444: + case DRM_FORMAT_BGRX4444: + f = FORMAT_X4R4G4B4; + break; + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_RGBA4444: + case DRM_FORMAT_ABGR4444: + case DRM_FORMAT_BGRA4444: + f = FORMAT_A4R4G4B4; + break; + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_RGBX5551: + case DRM_FORMAT_XBGR1555: + case DRM_FORMAT_BGRX5551: + f = FORMAT_X1R5G5B5; + break; + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_ABGR1555: + case DRM_FORMAT_BGRA5551: + f = FORMAT_A1R5G5B5; + break; + case DRM_FORMAT_RGB565: + case DRM_FORMAT_BGR565: + f = FORMAT_R5G6B5; + break; + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_BGRX8888: + f = FORMAT_X8R8G8B8; + break; + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_BGRA8888: + f = FORMAT_A8R8G8B8; + break; + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + f = FORMAT_YUY2; + break; + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + f = FORMAT_UYVY; + break; + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + f = FORMAT_YV12; + break; + case DRM_FORMAT_NV21: + f = FORMAT_NV12; + break; + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + f = FORMAT_NV16; + break; + case DRM_FORMAT_P010: + f = FORMAT_P010; + break; + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_RGBA1010102: + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_BGRA1010102: + f = FORMAT_A2R10G10B10; + break; + case DRM_FORMAT_NV12: + if (fourcc_mod_vs_get_type(mod) == + DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT) + f = FORMAT_NV12_10BIT; + else + f = FORMAT_NV12; + break; + case DRM_FORMAT_YUV444: + if (fourcc_mod_vs_get_type(mod) == + DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT) + f = FORMAT_YUV444_10BIT; + else + f = FORMAT_YUV444; + break; + default: + break; + } + + fb->format = f; +} + +static inline void update_swizzle(u32 format, struct dc_hw_fb *fb) +{ + fb->swizzle = SWIZZLE_ARGB; + fb->uv_swizzle = 0; + + switch (format) { + case DRM_FORMAT_RGBX4444: + case DRM_FORMAT_RGBA4444: + case DRM_FORMAT_RGBX5551: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_RGBA1010102: + fb->swizzle = SWIZZLE_RGBA; + break; + case DRM_FORMAT_XBGR4444: + case DRM_FORMAT_ABGR4444: + case DRM_FORMAT_XBGR1555: + case DRM_FORMAT_ABGR1555: + case DRM_FORMAT_BGR565: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_ABGR2101010: + fb->swizzle = SWIZZLE_ABGR; + break; + case DRM_FORMAT_BGRX4444: + case DRM_FORMAT_BGRA4444: + case DRM_FORMAT_BGRX5551: + case DRM_FORMAT_BGRA5551: + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_BGRA1010102: + fb->swizzle = SWIZZLE_BGRA; + break; + case DRM_FORMAT_YVYU: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV61: + fb->uv_swizzle = 1; + break; + default: + break; + } +} + +static inline void update_watermark(struct drm_property_blob *watermark, + struct dc_hw_fb *fb) +{ + struct drm_vs_watermark *data; + + fb->water_mark = 0; + + if (watermark) { + data = watermark->data; + fb->water_mark = data->watermark & 0xFFFFF; + } +} + +static inline u8 to_vs_rotation(unsigned int rotation) +{ + u8 rot; + + switch (rotation & DRM_MODE_REFLECT_MASK) { + case DRM_MODE_REFLECT_X: + rot = FLIP_X; + return rot; + case DRM_MODE_REFLECT_Y: + rot = FLIP_Y; + return rot; + case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y: + rot = FLIP_XY; + return rot; + default: + break; + } + + switch (rotation & DRM_MODE_ROTATE_MASK) { + case DRM_MODE_ROTATE_0: + rot = ROT_0; + break; + case DRM_MODE_ROTATE_90: + rot = ROT_90; + break; + case DRM_MODE_ROTATE_180: + rot = ROT_180; + break; + case DRM_MODE_ROTATE_270: + rot = ROT_270; + break; + default: + rot = ROT_0; + break; + } + + return rot; +} + +static inline u8 to_vs_yuv_color_space(u32 color_space) +{ + u8 cs; + + switch (color_space) { + case DRM_COLOR_YCBCR_BT601: + cs = COLOR_SPACE_601; + break; + case DRM_COLOR_YCBCR_BT709: + cs = COLOR_SPACE_709; + break; + case DRM_COLOR_YCBCR_BT2020: + cs = COLOR_SPACE_2020; + break; + default: + cs = COLOR_SPACE_601; + break; + } + + return cs; +} + +static inline u8 to_vs_tile_mode(u64 modifier) +{ + return (u8)(modifier & DRM_FORMAT_MOD_VS_NORM_MODE_MASK); +} + +static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc) +{ + u8 panel_num = dc->hw.info->panel_num; + u32 index = drm_crtc_index(crtc); + int i; + + for (i = 0; i < panel_num; i++) { + if (index == dc->crtc[i]->base.index) + return i; + } + + return 0; +} + +static int plda_clk_rst_init(struct device *dev) +{ + int ret = 0; + struct vs_dc *dc = dev_get_drvdata(dev); + + ret = clk_bulk_prepare_enable(dc->nclks, dc->clk_vout); + if (ret) { + dev_err(dev, "failed to enable clocks\n"); + return ret; + } + + ret = reset_control_bulk_deassert(dc->nrsts, dc->rst_vout); + return ret; +} + +static void plda_clk_rst_deinit(struct device *dev) +{ + struct vs_dc *dc = dev_get_drvdata(dev); + + reset_control_bulk_assert(dc->nrsts, dc->rst_vout); + clk_bulk_disable_unprepare(dc->nclks, dc->clk_vout); +} + +static void dc_deinit(struct device *dev) +{ + struct vs_dc *dc = dev_get_drvdata(dev); + + dc_hw_enable_interrupt(&dc->hw, 0); + dc_hw_deinit(&dc->hw); + plda_clk_rst_deinit(dev); +} + +static int dc_init(struct device *dev) +{ + struct vs_dc *dc = dev_get_drvdata(dev); + int ret; + + dc->first_frame = true; + + ret = plda_clk_rst_init(dev); + if (ret < 0) { + dev_err(dev, "failed to init dc clk reset: %d\n", ret); + return ret; + } + + ret = dc_hw_init(&dc->hw); + if (ret) { + dev_err(dev, "failed to init DC HW\n"); + return ret; + } + return 0; +} + +void vs_dc_enable(struct device *dev, struct drm_crtc *crtc) +{ + struct vs_dc *dc = dev_get_drvdata(dev); + 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.sync_mode = crtc_state->sync_mode; + display.bg_color = crtc_state->bg_color; + + display.id = to_vs_display_id(dc, crtc); + display.sync_enable = crtc_state->sync_enable; + display.dither_enable = crtc_state->dither_enable; + + display.enable = true; + + if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) { + dc_hw_set_out(&dc->hw, OUT_DPI, display.id); + clk_set_rate(dc->clk_vout[CLK_VOUT_SOC_PIX].clk, mode->clock * 1000); + clk_set_parent(dc->clk_vout[CLK_VOUT_PIX1].clk, + dc->clk_vout[CLK_VOUT_SOC_PIX].clk); + } else { + dc_hw_set_out(&dc->hw, OUT_DP, display.id); + clk_set_parent(dc->clk_vout[CLK_VOUT_PIX0].clk, + dc->clk_vout[CLK_VOUT_HDMI_PIX].clk); + } + + dc_hw_setup_display(&dc->hw, &display); +} + +void vs_dc_disable(struct device *dev, struct drm_crtc *crtc) +{ + struct vs_dc *dc = dev_get_drvdata(dev); + struct dc_hw_display display; + + display.id = to_vs_display_id(dc, crtc); + display.enable = false; + + dc_hw_setup_display(&dc->hw, &display); +} + +bool vs_dc_mode_fixup(struct device *dev, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +void vs_dc_set_gamma(struct device *dev, struct drm_crtc *crtc, + struct drm_color_lut *lut, unsigned int size) +{ + struct vs_dc *dc = dev_get_drvdata(dev); + u16 i, r, g, b; + u8 bits, id; + + if (size != dc->hw.info->gamma_size) { + dev_err(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 device *dev, struct drm_crtc *crtc, + bool enable) +{ + struct vs_dc *dc = dev_get_drvdata(dev); + u8 id; + + id = to_vs_display_id(dc, crtc); + dc_hw_enable_gamma(&dc->hw, id, enable); +} + +void vs_dc_enable_vblank(struct device *dev, bool enable) +{ + struct vs_dc *dc = dev_get_drvdata(dev); + + 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->dma_addr[0]; + fb->y_stride = drm_fb->pitches[0]; + if (drm_fb->format->format == DRM_FORMAT_YVU420) { + fb->u_address = plane->dma_addr[2]; + fb->v_address = plane->dma_addr[1]; + fb->u_stride = drm_fb->pitches[2]; + fb->v_stride = drm_fb->pitches[1]; + } else { + fb->u_address = plane->dma_addr[1]; + fb->v_address = plane->dma_addr[2]; + fb->u_stride = drm_fb->pitches[1]; + fb->v_stride = drm_fb->pitches[2]; + } + fb->width = drm_rect_width(src) >> 16; + fb->height = drm_rect_height(src) >> 16; + fb->tile_mode = to_vs_tile_mode(drm_fb->modifier); + fb->rotation = to_vs_rotation(state->rotation); + fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding); + fb->zpos = state->zpos; + fb->enable = state->visible; + update_format(drm_fb->format->format, drm_fb->modifier, fb); + update_swizzle(drm_fb->format->format, fb); + update_watermark(plane_state->watermark, fb); + plane_state->status.tile_mode = fb->tile_mode; +} + +static void update_degamma(struct vs_dc *dc, struct vs_plane *plane, + struct vs_plane_state *plane_state) +{ + dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma); + plane_state->degamma_changed = false; +} + +static void update_roi(struct vs_dc *dc, u8 id, + struct vs_plane_state *plane_state, + struct dc_hw_roi *roi, + struct drm_plane_state *state) +{ + struct drm_vs_roi *data; + struct drm_rect *src = &state->src; + u16 src_w = drm_rect_width(src) >> 16; + u16 src_h = drm_rect_height(src) >> 16; + + if (plane_state->roi) { + data = plane_state->roi->data; + + if (data->enable) { + roi->x = data->roi_x; + roi->y = data->roi_y; + roi->width = (data->roi_x + data->roi_w > src_w) ? + (src_w - data->roi_x) : data->roi_w; + roi->height = (data->roi_y + data->roi_h > src_h) ? + (src_h - data->roi_y) : data->roi_h; + roi->enable = true; + } else { + roi->enable = false; + } + + dc_hw_update_roi(&dc->hw, id, roi); + } else { + roi->enable = false; + } +} + +static void update_color_mgmt(struct vs_dc *dc, u8 id, + struct dc_hw_fb *fb, + struct vs_plane_state *plane_state) +{ + struct drm_vs_color_mgmt *data; + struct dc_hw_colorkey colorkey; + + if (plane_state->color_mgmt) { + data = plane_state->color_mgmt->data; + + fb->clear_enable = data->clear_enable; + fb->clear_value = data->clear_value; + + if (data->colorkey > data->colorkey_high) + data->colorkey = data->colorkey_high; + + colorkey.colorkey = data->colorkey; + colorkey.colorkey_high = data->colorkey_high; + colorkey.transparency = (data->transparency) ? + DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE; + dc_hw_update_colorkey(&dc->hw, id, &colorkey); + } +} + +static void update_plane(struct vs_dc *dc, struct vs_plane *plane, + struct drm_plane *drm_plane, + struct drm_atomic_state *drm_state) +{ + struct dc_hw_fb fb = {0}; + struct dc_hw_scale scale; + struct dc_hw_position pos; + struct dc_hw_blend blend; + struct dc_hw_roi roi; + struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state, + drm_plane); + struct vs_plane_state *plane_state = to_vs_plane_state(state); + struct drm_rect *dest = &state->dst; + bool dec_enable = false; + u8 display_id = 0; + + display_id = to_vs_display_id(dc, state->crtc); + update_fb(plane, display_id, &fb, state); + fb.dec_enable = dec_enable; + + update_roi(dc, plane->id, plane_state, &roi, state); + + update_scale(state, &roi, &scale); + + if (plane_state->degamma_changed) + update_degamma(dc, plane, plane_state); + + pos.start_x = dest->x1; + pos.start_y = dest->y1; + pos.end_x = dest->x2; + pos.end_y = dest->y2; + + blend.alpha = (u8)(state->alpha >> 8); + blend.blend_mode = (u8)(state->pixel_blend_mode); + + update_color_mgmt(dc, plane->id, &fb, plane_state); + + dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend); +} + +static void update_qos(struct vs_dc *dc, struct vs_plane *plane, + struct drm_plane *drm_plane, + struct drm_atomic_state *drm_state) +{ + struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state, + drm_plane); + struct vs_plane_state *plane_state = to_vs_plane_state(state); + struct drm_vs_watermark *data; + struct dc_hw_qos qos; + + if (plane_state->watermark) { + data = plane_state->watermark->data; + + if (data->qos_high) { + if (data->qos_low > data->qos_high) + data->qos_low = data->qos_high; + + qos.low_value = data->qos_low & 0x0F; + qos.high_value = data->qos_high & 0x0F; + dc_hw_update_qos(&dc->hw, &qos); + } + } +} + +static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor) +{ + u8 size_type; + + switch (state->crtc_w) { + case 32: + size_type = CURSOR_SIZE_32X32; + break; + case 64: + size_type = CURSOR_SIZE_64X64; + break; + default: + size_type = CURSOR_SIZE_32X32; + break; + } + + cursor->size = size_type; +} + +static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane, + struct drm_plane *drm_plane, + struct drm_atomic_state *drm_state) +{ + struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state, + drm_plane); + struct drm_framebuffer *drm_fb = state->fb; + struct dc_hw_cursor cursor; + + cursor.address = plane->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 device *dev, struct vs_plane *plane, + struct drm_plane *drm_plane, + struct drm_atomic_state *drm_state) +{ + struct vs_dc *dc = dev_get_drvdata(dev); + + update_plane(dc, plane, drm_plane, drm_state); + update_qos(dc, plane, drm_plane, drm_state); +} + +void vs_dc_update_cursor_plane(struct device *dev, struct vs_plane *plane, + struct drm_plane *drm_plane, + struct drm_atomic_state *drm_state) +{ + struct vs_dc *dc = dev_get_drvdata(dev); + + update_cursor_plane(dc, plane, drm_plane, drm_state); +} + +void vs_dc_disable_plane(struct device *dev, struct vs_plane *plane, + struct drm_plane_state *old_state) +{ + struct vs_dc *dc = dev_get_drvdata(dev); + 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 device *dev, struct vs_plane *plane, + struct drm_plane_state *old_state) +{ + struct vs_dc *dc = dev_get_drvdata(dev); + 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 device *dev, 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 vs_dc *dc = dev_get_drvdata(dev); + 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) + dev_err_once(dev, "buffer size may not support on plane%d.\n", + vs_plane->id); + + if (!vs_dc_mod_supported(plane_info, fb->modifier)) { + dev_err(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 device *dev, 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 vs_dc *dc = dev_get_drvdata(dev); + 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) + dev_err_once(dev, "buffer size may not support on plane%d.\n", vs_plane->id); + + crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) + return -EINVAL; + + return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, + plane_info->min_scale, + plane_info->max_scale, + true, true); +} + +static void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow) +{ + struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state); + + drm_crtc_handle_vblank(crtc); + + vs_crtc_state->underflow = underflow; +} + +static irqreturn_t dc_isr(int irq, void *data) +{ + struct vs_dc *dc = data; + struct vs_dc_info *dc_info = dc->hw.info; + u32 i, ret; + + ret = dc_hw_get_interrupt(&dc->hw); + + for (i = 0; i < dc_info->panel_num; i++) + vs_crtc_handle_vblank(&dc->crtc[i]->base, dc_hw_check_underflow(&dc->hw)); + + return IRQ_HANDLED; +} + +void vs_dc_commit(struct device *dev) +{ + struct vs_dc *dc = dev_get_drvdata(dev); + + dc_hw_enable_shadow_register(&dc->hw, false); + + dc_hw_commit(&dc->hw); + + if (dc->first_frame) + dc->first_frame = false; + + dc_hw_enable_shadow_register(&dc->hw, true); +} + +static int dc_bind(struct device *dev, struct device *master, void *data) +{ + struct drm_device *drm_dev = data; + struct vs_dc *dc = dev_get_drvdata(dev); + struct device_node *port; + struct vs_crtc *crtc; + struct vs_dc_info *dc_info; + struct vs_plane *plane; + struct vs_plane_info *plane_info; + int i, ret; + u32 ctrc_mask = 0; + + if (!drm_dev || !dc) { + dev_err(dev, "devices are not created.\n"); + return -ENODEV; + } + + ret = dc_init(dev); + if (ret < 0) { + dev_err(dev, "Failed to initialize DC hardware.\n"); + return ret; + } + + port = of_get_child_by_name(dev->of_node, "port"); + if (!port) { + dev_err(dev, "no port node found\n"); + return -ENODEV; + } + of_node_put(port); + + dc_info = dc->hw.info; + + for (i = 0; i < dc_info->panel_num; i++) { + crtc = vs_crtc_create(drm_dev, dc_info); + if (!crtc) { + dev_err(dev, "Failed to create CRTC.\n"); + ret = -ENOMEM; + return ret; + } + + crtc->base.port = port; + crtc->dev = dev; + dc->crtc[i] = crtc; + ctrc_mask |= drm_crtc_mask(&crtc->base); + } + + for (i = 0; i < dc_info->plane_num; i++) { + plane_info = (struct vs_plane_info *)&dc_info->planes[i]; + + if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) { + plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num, + drm_crtc_mask(&dc->crtc[0]->base)); + } else if (!strcmp(plane_info->name, "Primary_1") || + !strcmp(plane_info->name, "Cursor_1")) { + plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num, + drm_crtc_mask(&dc->crtc[1]->base)); + } else { + plane = vs_plane_create(drm_dev, plane_info, + dc_info->layer_num, ctrc_mask); + } + + if (IS_ERR(plane)) { + dev_err(dev, "failed to construct plane\n"); + return PTR_ERR(plane); + } + + plane->id = i; + dc->planes[i].id = plane_info->id; + + if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) { + if (!strcmp(plane_info->name, "Primary")) + dc->crtc[0]->base.primary = &plane->base; + else + dc->crtc[1]->base.primary = &plane->base; + drm_dev->mode_config.min_width = plane_info->min_width; + drm_dev->mode_config.min_height = + plane_info->min_height; + drm_dev->mode_config.max_width = plane_info->max_width; + drm_dev->mode_config.max_height = + plane_info->max_height; + } + + if (plane_info->type == DRM_PLANE_TYPE_CURSOR) { + if (!strcmp(plane_info->name, "Cursor")) + dc->crtc[0]->base.cursor = &plane->base; + else + dc->crtc[1]->base.cursor = &plane->base; + drm_dev->mode_config.cursor_width = + plane_info->max_width; + drm_dev->mode_config.cursor_height = + plane_info->max_height; + } + } + + vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment); + + return 0; +} + +static void dc_unbind(struct device *dev, struct device *master, void *data) +{ + dc_deinit(dev); +} + +const struct component_ops dc_component_ops = { + .bind = dc_bind, + .unbind = dc_unbind, +}; + +static const struct of_device_id dc_driver_dt_match[] = { + { .compatible = "starfive,jh7110-dc8200", }, + {}, +}; +MODULE_DEVICE_TABLE(of, dc_driver_dt_match); + +static int dc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct vs_dc *dc; + int irq, ret, i; + + dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL); + if (!dc) + return -ENOMEM; + + dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dc->hw.hi_base)) + return PTR_ERR(dc->hw.hi_base); + + dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(dc->hw.reg_base)) + return PTR_ERR(dc->hw.reg_base); + + dc->dss_reg = devm_platform_ioremap_resource(pdev, 2); + if (IS_ERR(dc->dss_reg)) + return PTR_ERR(dc->dss_reg); + + dc->nclks = ARRAY_SIZE(dc->clk_vout); + for (i = 0; i < dc->nclks; ++i) + dc->clk_vout[i].id = vout_clocks[i]; + ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout); + if (ret) { + dev_err(dev, "Failed to get clk controls\n"); + return ret; + } + + dc->nrsts = ARRAY_SIZE(dc->rst_vout); + for (i = 0; i < dc->nrsts; ++i) + dc->rst_vout[i].id = vout_resets[i]; + ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts, + dc->rst_vout); + if (ret) { + dev_err(dev, "Failed to get reset controls\n"); + return ret; + } + + irq = platform_get_irq(pdev, 0); + + ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc); + if (ret < 0) { + dev_err(dev, "Failed to install irq:%u.\n", irq); + return ret; + } + + dev_set_drvdata(dev, dc); + + return component_add(dev, &dc_component_ops); +} + +static int dc_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + component_del(dev, &dc_component_ops); + + dev_set_drvdata(dev, NULL); + + return 0; +} + +struct platform_driver dc_platform_driver = { + .probe = dc_probe, + .remove = dc_remove, + .driver = { + .name = "vs-dc", + .of_match_table = of_match_ptr(dc_driver_dt_match), + }, +}; + +MODULE_AUTHOR("StarFive Corporation"); +MODULE_DESCRIPTION("VeriSilicon DC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h new file mode 100644 index 000000000..4d6b1d045 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_dc.h @@ -0,0 +1,87 @@ +/* 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 <linux/version.h> + +#include <drm/drm_fourcc.h> +#include <drm/drm_modes.h> + +#include "vs_crtc.h" +#include "vs_dc_hw.h" +#include "vs_plane.h" + +#define fourcc_mod_vs_get_type(val) \ + (((val) & DRM_FORMAT_MOD_VS_TYPE_MASK) >> 54) + +struct vs_dc_plane { + enum dc_hw_plane_id id; +}; + +enum vout_clk { + CLK_VOUT_NOC_DISP = 0, + CLK_VOUT_PIX0, + CLK_VOUT_PIX1, + CLK_VOUT_AXI, + CLK_VOUT_CORE, + CLK_VOUT_AHB, + CLK_VOUT_HDMI_PIX, + CLK_VOUT_SOC_PIX, + CLK_VOUT_NUM +}; + +enum rst_vout { + RST_VOUT_AXI = 0, + RST_VOUT_AHB, + RST_VOUT_CORE, + RST_VOUT_NUM +}; + +struct vs_dc { + struct vs_crtc *crtc[DC_DISPLAY_NUM]; + struct dc_hw hw; + void __iomem *dss_reg; + bool first_frame; + + struct vs_dc_plane planes[PLANE_NUM]; + struct clk_bulk_data clk_vout[CLK_VOUT_NUM]; + int nclks; + struct reset_control_bulk_data rst_vout[RST_VOUT_NUM]; + int nrsts; +}; + +void vs_dc_enable(struct device *dev, struct drm_crtc *crtc); +void vs_dc_disable(struct device *dev, struct drm_crtc *crtc); +bool vs_dc_mode_fixup(struct device *dev, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +void vs_dc_set_gamma(struct device *dev, struct drm_crtc *crtc, + struct drm_color_lut *lut, unsigned int size); +void vs_dc_enable_gamma(struct device *dev, struct drm_crtc *crtc, bool enable); +void vs_dc_enable_vblank(struct device *dev, bool enable); +void vs_dc_commit(struct device *dev); +void vs_dc_update_plane(struct device *dev, struct vs_plane *plane, + struct drm_plane *drm_plane, + struct drm_atomic_state *drm_state); +void vs_dc_disable_plane(struct device *dev, struct vs_plane *plane, + struct drm_plane_state *old_state); +int vs_dc_check_plane(struct device *dev, struct drm_plane *plane, + struct drm_atomic_state *state); +void vs_dc_update_cursor_plane(struct device *dev, struct vs_plane *plane, + struct drm_plane *drm_plane, + struct drm_atomic_state *drm_state); +void vs_dc_disable_cursor_plane(struct device *dev, struct vs_plane *plane, + struct drm_plane_state *old_state); +int vs_dc_check_cursor_plane(struct device *dev, struct drm_plane *plane, + struct drm_atomic_state *state); + +extern struct platform_driver dc_platform_driver; + +#endif /* __VS_DC_H__ */ diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c new file mode 100644 index 000000000..d370dd401 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c @@ -0,0 +1,2008 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + */ + +#include <linux/bits.h> +#include <linux/io.h> +#include <linux/media-bus-format.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_blend.h> +#include <drm/drm_fourcc.h> +#include <drm/vs_drm.h> + +#include "vs_dc_hw.h" +#include "vs_type.h" + +static const u32 horkernel[] = { + 0x00000000, 0x20000000, 0x00002000, 0x00000000, + 0x00000000, 0x00000000, 0x23fd1c03, 0x00000000, + 0x00000000, 0x00000000, 0x181f0000, 0x000027e1, + 0x00000000, 0x00000000, 0x00000000, 0x2b981468, + 0x00000000, 0x00000000, 0x00000000, 0x10f00000, + 0x00002f10, 0x00000000, 0x00000000, 0x00000000, + 0x32390dc7, 0x00000000, 0x00000000, 0x00000000, + 0x0af50000, 0x0000350b, 0x00000000, 0x00000000, + 0x00000000, 0x3781087f, 0x00000000, 0x00000000, + 0x00000000, 0x06660000, 0x0000399a, 0x00000000, + 0x00000000, 0x00000000, 0x3b5904a7, 0x00000000, + 0x00000000, 0x00000000, 0x033c0000, 0x00003cc4, + 0x00000000, 0x00000000, 0x00000000, 0x3de1021f, + 0x00000000, 0x00000000, 0x00000000, 0x01470000, + 0x00003eb9, 0x00000000, 0x00000000, 0x00000000, + 0x3f5300ad, 0x00000000, 0x00000000, 0x00000000, + 0x00480000, 0x00003fb8, 0x00000000, 0x00000000, + 0x00000000, 0x3fef0011, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00004000, 0x00000000, + 0x00000000, 0x00000000, 0x20002000, 0x00000000, + 0x00000000, 0x00000000, 0x1c030000, 0x000023fd, + 0x00000000, 0x00000000, 0x00000000, 0x27e1181f, + 0x00000000, 0x00000000, 0x00000000, 0x14680000, + 0x00002b98, 0x00000000, 0x00000000, 0x00000000, + 0x2f1010f0, 0x00000000, 0x00000000, 0x00000000, + 0x0dc70000, 0x00003239, 0x00000000, 0x00000000, + 0x00000000, 0x350b0af5, 0x00000000, 0x00000000, + 0x00000000, 0x087f0000, 0x00003781, 0x00000000, + 0x00000000, 0x00000000, 0x399a0666, 0x00000000, + 0x00000000, 0x00000000, 0x04a70000, 0x00003b59, + 0x00000000, 0x00000000, 0x00000000, 0x3cc4033c, + 0x00000000, 0x00000000, 0x00000000, 0x021f0000, +}; + +#define H_COEF_SIZE (sizeof(horkernel) / sizeof(u32)) + +static const u32 verkernel[] = { + 0x00000000, 0x20000000, 0x00002000, 0x00000000, + 0x00000000, 0x00000000, 0x23fd1c03, 0x00000000, + 0x00000000, 0x00000000, 0x181f0000, 0x000027e1, + 0x00000000, 0x00000000, 0x00000000, 0x2b981468, + 0x00000000, 0x00000000, 0x00000000, 0x10f00000, + 0x00002f10, 0x00000000, 0x00000000, 0x00000000, + 0x32390dc7, 0x00000000, 0x00000000, 0x00000000, + 0x0af50000, 0x0000350b, 0x00000000, 0x00000000, + 0x00000000, 0x3781087f, 0x00000000, 0x00000000, + 0x00000000, 0x06660000, 0x0000399a, 0x00000000, + 0x00000000, 0x00000000, 0x3b5904a7, 0x00000000, + 0x00000000, 0x00000000, 0x033c0000, 0x00003cc4, + 0x00000000, 0x00000000, 0x00000000, 0x3de1021f, + 0x00000000, 0x00000000, 0x00000000, 0x01470000, + 0x00003eb9, 0x00000000, 0x00000000, 0x00000000, + 0x3f5300ad, 0x00000000, 0x00000000, 0x00000000, + 0x00480000, 0x00003fb8, 0x00000000, 0x00000000, + 0x00000000, 0x3fef0011, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00004000, 0x00000000, + 0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab, + 0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58, + 0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee, + 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, + 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, + 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, + 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, + 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, + 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, + 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, + 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, + 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, + 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, +}; + +#define V_COEF_SIZE (sizeof(verkernel) / sizeof(u32)) + +/* + * RGB 709->2020 conversion parameters + */ +static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = { + 10279, 5395, 709, + 1132, 15065, 187, + 269, 1442, 14674 +}; + +/* + * YUV601 to RGB conversion parameters + * YUV2RGB[0] - [8] : C0 - C8; + * YUV2RGB[9] - [11]: D0 - D2; + * YUV2RGB[12] - [13]: Y clamp min & max calue; + * YUV2RGB[14] - [15]: UV clamp min & max calue; + */ +static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = { + 1196, 0, 1640, 1196, + -404, -836, 1196, 2076, + 0, -916224, 558336, -1202944, + 64, 940, 64, 960 +}; + +/* + * YUV709 to RGB conversion parameters + * YUV2RGB[0] - [8] : C0 - C8; + * YUV2RGB[9] - [11]: D0 - D2; + * YUV2RGB[12] - [13]: Y clamp min & max calue; + * YUV2RGB[14] - [15]: UV clamp min & max calue; + */ +static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = { + 1196, 0, 1844, 1196, + -220, -548, 1196, 2172, + 0, -1020672, 316672, -1188608, + 64, 940, 64, 960 +}; + +/* + * YUV2020 to RGB conversion parameters + * YUV2RGB[0] - [8] : C0 - C8; + * YUV2RGB[9] - [11]: D0 - D2; + * YUV2RGB[12] - [13]: Y clamp min & max calue; + * YUV2RGB[14] - [15]: UV clamp min & max calue; + */ +static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = { + 1196, 0, 1724, 1196, + -192, -668, 1196, 2200, + 0, -959232, 363776, -1202944, + 64, 940, 64, 960 +}; + +/* + * RGB to YUV2020 conversion parameters + * RGB2YUV[0] - [8] : C0 - C8; + * RGB2YUV[9] - [11]: D0 - D2; + */ +static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = { + 230, 594, 52, + -125, -323, 448, + 448, -412, -36, + 64, 512, 512 +}; + +/* + * Degamma table for 709 color space data. + */ +static u16 DEGAMMA_709[DEGAMMA_SIZE] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005, + 0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024, + 0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064, + 0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce, + 0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166, + 0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230, + 0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331, + 0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c, + 0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5, + 0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d, + 0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999, + 0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb, + 0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65, + 0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a, + 0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b, + 0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc, + 0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e, + 0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3, + 0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e, + 0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf, + 0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea, + 0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f, + 0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930, + 0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f, + 0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee, + 0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde, + 0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431, + 0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9, + 0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406, + 0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a, + 0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576, + 0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd, + 0x3f65, 0x3f8c, 0x3fb2, 0x3fd8 +}; + +/* + * Degamma table for 2020 color space data. + */ +static u16 DEGAMMA_2020[DEGAMMA_SIZE] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003, + 0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006, + 0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a, + 0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018, + 0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024, + 0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035, + 0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d, + 0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d, + 0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098, + 0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3, + 0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123, + 0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d, + 0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a, + 0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6, + 0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1, + 0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d, + 0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6, + 0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920, + 0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a, + 0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033, + 0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593, + 0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf, + 0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656, + 0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332, + 0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480, + 0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1, + 0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99, + 0x3dcb, 0x3e60, 0x3ef5, 0x3f8c +}; + +/* one is for primary plane and the other is for all overlay planes */ +static const struct dc_hw_plane_reg dc_plane_reg[] = { + { + .y_address = DC_FRAMEBUFFER_ADDRESS, + .u_address = DC_FRAMEBUFFER_U_ADDRESS, + .v_address = DC_FRAMEBUFFER_V_ADDRESS, + .y_stride = DC_FRAMEBUFFER_STRIDE, + .u_stride = DC_FRAMEBUFFER_U_STRIDE, + .v_stride = DC_FRAMEBUFFER_V_STRIDE, + .size = DC_FRAMEBUFFER_SIZE, + .top_left = DC_FRAMEBUFFER_TOP_LEFT, + .bottom_right = DC_FRAMEBUFFER_BOTTOM_RIGHT, + .scale_factor_x = DC_FRAMEBUFFER_SCALE_FACTOR_X, + .scale_factor_y = DC_FRAMEBUFFER_SCALE_FACTOR_Y, + .h_filter_coef_index = DC_FRAMEBUFFER_H_FILTER_COEF_INDEX, + .h_filter_coef_data = DC_FRAMEBUFFER_H_FILTER_COEF_DATA, + .v_filter_coef_index = DC_FRAMEBUFFER_V_FILTER_COEF_INDEX, + .v_filter_coef_data = DC_FRAMEBUFFER_V_FILTER_COEF_DATA, + .init_offset = DC_FRAMEBUFFER_INIT_OFFSET, + .color_key = DC_FRAMEBUFFER_COLOR_KEY, + .color_key_high = DC_FRAMEBUFFER_COLOR_KEY_HIGH, + .clear_value = DC_FRAMEBUFFER_CLEAR_VALUE, + .color_table_index = DC_FRAMEBUFFER_COLOR_TABLE_INDEX, + .color_table_data = DC_FRAMEBUFFER_COLOR_TABLE_DATA, + .scale_config = DC_FRAMEBUFFER_SCALE_CONFIG, + .water_mark = DC_FRAMEBUFFER_WATER_MARK, + .degamma_index = DC_FRAMEBUFFER_DEGAMMA_INDEX, + .degamma_data = DC_FRAMEBUFFER_DEGAMMA_DATA, + .degamma_ex_data = DC_FRAMEBUFFER_DEGAMMA_EX_DATA, + .src_global_color = DC_FRAMEBUFFER_SRC_GLOBAL_COLOR, + .dst_global_color = DC_FRAMEBUFFER_DST_GLOBAL_COLOR, + .blend_config = DC_FRAMEBUFFER_BLEND_CONFIG, + .roi_origin = DC_FRAMEBUFFER_ROI_ORIGIN, + .roi_size = DC_FRAMEBUFFER_ROI_SIZE, + .yuv_to_rgb_coef0 = DC_FRAMEBUFFER_YUVTORGB_COEF0, + .yuv_to_rgb_coef1 = DC_FRAMEBUFFER_YUVTORGB_COEF1, + .yuv_to_rgb_coef2 = DC_FRAMEBUFFER_YUVTORGB_COEF2, + .yuv_to_rgb_coef3 = DC_FRAMEBUFFER_YUVTORGB_COEF3, + .yuv_to_rgb_coef4 = DC_FRAMEBUFFER_YUVTORGB_COEF4, + .yuv_to_rgb_coefd0 = DC_FRAMEBUFFER_YUVTORGB_COEFD0, + .yuv_to_rgb_coefd1 = DC_FRAMEBUFFER_YUVTORGB_COEFD1, + .yuv_to_rgb_coefd2 = DC_FRAMEBUFFER_YUVTORGB_COEFD2, + .y_clamp_bound = DC_FRAMEBUFFER_Y_CLAMP_BOUND, + .uv_clamp_bound = DC_FRAMEBUFFER_UV_CLAMP_BOUND, + .rgb_to_rgb_coef0 = DC_FRAMEBUFFER_RGBTORGB_COEF0, + .rgb_to_rgb_coef1 = DC_FRAMEBUFFER_RGBTORGB_COEF1, + .rgb_to_rgb_coef2 = DC_FRAMEBUFFER_RGBTORGB_COEF2, + .rgb_to_rgb_coef3 = DC_FRAMEBUFFER_RGBTORGB_COEF3, + .rgb_to_rgb_coef4 = DC_FRAMEBUFFER_RGBTORGB_COEF4, + }, + { + .y_address = DC_OVERLAY_ADDRESS, + .u_address = DC_OVERLAY_U_ADDRESS, + .v_address = DC_OVERLAY_V_ADDRESS, + .y_stride = DC_OVERLAY_STRIDE, + .u_stride = DC_OVERLAY_U_STRIDE, + .v_stride = DC_OVERLAY_V_STRIDE, + .size = DC_OVERLAY_SIZE, + .top_left = DC_OVERLAY_TOP_LEFT, + .bottom_right = DC_OVERLAY_BOTTOM_RIGHT, + .scale_factor_x = DC_OVERLAY_SCALE_FACTOR_X, + .scale_factor_y = DC_OVERLAY_SCALE_FACTOR_Y, + .h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX, + .h_filter_coef_data = DC_OVERLAY_H_FILTER_COEF_DATA, + .v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX, + .v_filter_coef_data = DC_OVERLAY_V_FILTER_COEF_DATA, + .init_offset = DC_OVERLAY_INIT_OFFSET, + .color_key = DC_OVERLAY_COLOR_KEY, + .color_key_high = DC_OVERLAY_COLOR_KEY_HIGH, + .clear_value = DC_OVERLAY_CLEAR_VALUE, + .color_table_index = DC_OVERLAY_COLOR_TABLE_INDEX, + .color_table_data = DC_OVERLAY_COLOR_TABLE_DATA, + .scale_config = DC_OVERLAY_SCALE_CONFIG, + .water_mark = DC_OVERLAY_WATER_MARK, + .degamma_index = DC_OVERLAY_DEGAMMA_INDEX, + .degamma_data = DC_OVERLAY_DEGAMMA_DATA, + .degamma_ex_data = DC_OVERLAY_DEGAMMA_EX_DATA, + .src_global_color = DC_OVERLAY_SRC_GLOBAL_COLOR, + .dst_global_color = DC_OVERLAY_DST_GLOBAL_COLOR, + .blend_config = DC_OVERLAY_BLEND_CONFIG, + .roi_origin = DC_OVERLAY_ROI_ORIGIN, + .roi_size = DC_OVERLAY_ROI_SIZE, + .yuv_to_rgb_coef0 = DC_OVERLAY_YUVTORGB_COEF0, + .yuv_to_rgb_coef1 = DC_OVERLAY_YUVTORGB_COEF1, + .yuv_to_rgb_coef2 = DC_OVERLAY_YUVTORGB_COEF2, + .yuv_to_rgb_coef3 = DC_OVERLAY_YUVTORGB_COEF3, + .yuv_to_rgb_coef4 = DC_OVERLAY_YUVTORGB_COEF4, + .yuv_to_rgb_coefd0 = DC_OVERLAY_YUVTORGB_COEFD0, + .yuv_to_rgb_coefd1 = DC_OVERLAY_YUVTORGB_COEFD1, + .yuv_to_rgb_coefd2 = DC_OVERLAY_YUVTORGB_COEFD2, + .y_clamp_bound = DC_OVERLAY_Y_CLAMP_BOUND, + .uv_clamp_bound = DC_OVERLAY_UV_CLAMP_BOUND, + .rgb_to_rgb_coef0 = DC_OVERLAY_RGBTORGB_COEF0, + .rgb_to_rgb_coef1 = DC_OVERLAY_RGBTORGB_COEF1, + .rgb_to_rgb_coef2 = DC_OVERLAY_RGBTORGB_COEF2, + .rgb_to_rgb_coef3 = DC_OVERLAY_RGBTORGB_COEF3, + .rgb_to_rgb_coef4 = DC_OVERLAY_RGBTORGB_COEF4, + }, +}; + +static const u32 primary_overlay_format0[] = { + DRM_FORMAT_XRGB4444, + DRM_FORMAT_XBGR4444, + DRM_FORMAT_RGBX4444, + DRM_FORMAT_BGRX4444, + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ABGR4444, + DRM_FORMAT_RGBA4444, + DRM_FORMAT_BGRA4444, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_BGRX5551, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_RGB565, + DRM_FORMAT_BGR565, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_BGRX8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_ARGB2101010, + DRM_FORMAT_ABGR2101010, + DRM_FORMAT_RGBA1010102, + DRM_FORMAT_BGRA1010102, + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, + DRM_FORMAT_YVU420, + DRM_FORMAT_YUV420, + DRM_FORMAT_NV12, + DRM_FORMAT_NV21, + DRM_FORMAT_NV16, + DRM_FORMAT_NV61, + DRM_FORMAT_P010, +}; + +static const u32 primary_overlay_format1[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_BGRX8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_ARGB2101010, + DRM_FORMAT_ABGR2101010, + DRM_FORMAT_RGBA1010102, + DRM_FORMAT_BGRA1010102, + DRM_FORMAT_NV12, + DRM_FORMAT_NV21, + DRM_FORMAT_YUV444, +}; + +static const u32 cursor_formats[] = { + DRM_FORMAT_ARGB8888 +}; + +static const u64 format_modifier0[] = { + DRM_FORMAT_MOD_LINEAR, + fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_LINEAR), + fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR), + fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR), + fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_TILE_8X8), + fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_TILE_8X4), + fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR_8X4), + fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR_4X8), + DRM_FORMAT_MOD_INVALID +}; + +static const u64 format_modifier1[] = { + DRM_FORMAT_MOD_LINEAR, + fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_LINEAR), + fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR), + fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR), + fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_TILE_8X8), + fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_TILE_8X4), + fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR_8X4), + fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR_4X8), + fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_TILE_MODE4X4), + fourcc_mod_vs_custom_code(DRM_FORMAT_MOD_VS_TILE_MODE4X4), + DRM_FORMAT_MOD_INVALID +}; + +static const u64 secondary_format_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +#define FRAC_16_16(mult, div) (((mult) << 16) / (div)) + +static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = { + { + /* DC_REV_0 */ + { + .name = "Primary", + .id = PRIMARY_PLANE_0, + .type = DRM_PLANE_TYPE_PRIMARY, + .num_formats = ARRAY_SIZE(primary_overlay_format0), + .formats = primary_overlay_format0, + .num_modifiers = ARRAY_SIZE(format_modifier0), + .modifiers = format_modifier0, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = DRM_MODE_ROTATE_0 | + DRM_MODE_ROTATE_90 | + DRM_MODE_ROTATE_180 | + DRM_MODE_ROTATE_270 | + DRM_MODE_REFLECT_X | + DRM_MODE_REFLECT_Y, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = FRAC_16_16(1, 3), + .max_scale = FRAC_16_16(10, 1), + .zpos = 0, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Overlay", + .id = OVERLAY_PLANE_0, + .type = DRM_PLANE_TYPE_OVERLAY, + .num_formats = ARRAY_SIZE(primary_overlay_format0), + .formats = primary_overlay_format0, + .num_modifiers = ARRAY_SIZE(format_modifier0), + .modifiers = format_modifier0, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = DRM_MODE_ROTATE_0 | + DRM_MODE_ROTATE_90 | + DRM_MODE_ROTATE_180 | + DRM_MODE_ROTATE_270 | + DRM_MODE_REFLECT_X | + DRM_MODE_REFLECT_Y, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = FRAC_16_16(1, 3), + .max_scale = FRAC_16_16(10, 1), + .zpos = 1, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Overlay_1", + .id = OVERLAY_PLANE_1, + .type = DRM_PLANE_TYPE_OVERLAY, + .num_formats = ARRAY_SIZE(primary_overlay_format0), + .formats = primary_overlay_format0, + .num_modifiers = ARRAY_SIZE(secondary_format_modifiers), + .modifiers = secondary_format_modifiers, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = 0, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = DRM_PLANE_NO_SCALING, + .max_scale = DRM_PLANE_NO_SCALING, + .zpos = 2, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Primary_1", + .id = PRIMARY_PLANE_1, + .type = DRM_PLANE_TYPE_PRIMARY, + .num_formats = ARRAY_SIZE(primary_overlay_format0), + .formats = primary_overlay_format0, + .num_modifiers = ARRAY_SIZE(format_modifier0), + .modifiers = format_modifier0, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = DRM_MODE_ROTATE_0 | + DRM_MODE_ROTATE_90 | + DRM_MODE_ROTATE_180 | + DRM_MODE_ROTATE_270 | + DRM_MODE_REFLECT_X | + DRM_MODE_REFLECT_Y, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = FRAC_16_16(1, 3), + .max_scale = FRAC_16_16(10, 1), + .zpos = 3, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Overlay_2", + .id = OVERLAY_PLANE_2, + .type = DRM_PLANE_TYPE_OVERLAY, + .num_formats = ARRAY_SIZE(primary_overlay_format0), + .formats = primary_overlay_format0, + .num_modifiers = ARRAY_SIZE(format_modifier0), + .modifiers = format_modifier0, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = DRM_MODE_ROTATE_0 | + DRM_MODE_ROTATE_90 | + DRM_MODE_ROTATE_180 | + DRM_MODE_ROTATE_270 | + DRM_MODE_REFLECT_X | + DRM_MODE_REFLECT_Y, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = FRAC_16_16(1, 3), + .max_scale = FRAC_16_16(10, 1), + .zpos = 4, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Overlay_3", + .id = OVERLAY_PLANE_3, + .type = DRM_PLANE_TYPE_OVERLAY, + .num_formats = ARRAY_SIZE(primary_overlay_format0), + .formats = primary_overlay_format0, + .num_modifiers = ARRAY_SIZE(secondary_format_modifiers), + .modifiers = secondary_format_modifiers, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = 0, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = DRM_PLANE_NO_SCALING, + .max_scale = DRM_PLANE_NO_SCALING, + .zpos = 5, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Cursor", + .id = CURSOR_PLANE_0, + .type = DRM_PLANE_TYPE_CURSOR, + .num_formats = ARRAY_SIZE(cursor_formats), + .formats = cursor_formats, + .num_modifiers = 0, + .modifiers = NULL, + .min_width = 32, + .min_height = 32, + .max_width = 64, + .max_height = 64, + .rotation = 0, + .degamma_size = 0, + .min_scale = DRM_PLANE_NO_SCALING, + .max_scale = DRM_PLANE_NO_SCALING, + .zpos = 255, + .watermark = false, + .color_mgmt = false, + .roi = false, + }, + { + .name = "Cursor_1", + .id = CURSOR_PLANE_1, + .type = DRM_PLANE_TYPE_CURSOR, + .num_formats = ARRAY_SIZE(cursor_formats), + .formats = cursor_formats, + .num_modifiers = 0, + .modifiers = NULL, + .min_width = 32, + .min_height = 32, + .max_width = 64, + .max_height = 64, + .rotation = 0, + .degamma_size = 0, + .min_scale = DRM_PLANE_NO_SCALING, + .max_scale = DRM_PLANE_NO_SCALING, + .zpos = 255, + .watermark = false, + .color_mgmt = false, + .roi = false, + }, + }, + { + /* DC_REV_1 */ + { + .name = "Primary", + .id = PRIMARY_PLANE_0, + .type = DRM_PLANE_TYPE_PRIMARY, + .num_formats = ARRAY_SIZE(primary_overlay_format0), + .formats = primary_overlay_format0, + .num_modifiers = ARRAY_SIZE(format_modifier0), + .modifiers = format_modifier0, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = DRM_MODE_ROTATE_0 | + DRM_MODE_ROTATE_90 | + DRM_MODE_ROTATE_180 | + DRM_MODE_ROTATE_270 | + DRM_MODE_REFLECT_X | + DRM_MODE_REFLECT_Y, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = FRAC_16_16(1, 3), + .max_scale = FRAC_16_16(10, 1), + .zpos = 0, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Overlay", + .id = OVERLAY_PLANE_0, + .type = DRM_PLANE_TYPE_OVERLAY, + .num_formats = ARRAY_SIZE(primary_overlay_format0), + .formats = primary_overlay_format0, + .num_modifiers = ARRAY_SIZE(format_modifier0), + .modifiers = format_modifier0, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = DRM_MODE_ROTATE_0 | + DRM_MODE_ROTATE_90 | + DRM_MODE_ROTATE_180 | + DRM_MODE_ROTATE_270 | + DRM_MODE_REFLECT_X | + DRM_MODE_REFLECT_Y, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = FRAC_16_16(1, 3), + .max_scale = FRAC_16_16(10, 1), + .zpos = 1, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Primary_1", + .id = PRIMARY_PLANE_1, + .type = DRM_PLANE_TYPE_PRIMARY, + .num_formats = ARRAY_SIZE(primary_overlay_format0), + .formats = primary_overlay_format0, + .num_modifiers = ARRAY_SIZE(format_modifier0), + .modifiers = format_modifier0, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = DRM_MODE_ROTATE_0 | + DRM_MODE_ROTATE_90 | + DRM_MODE_ROTATE_180 | + DRM_MODE_ROTATE_270 | + DRM_MODE_REFLECT_X | + DRM_MODE_REFLECT_Y, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = FRAC_16_16(1, 3), + .max_scale = FRAC_16_16(10, 1), + .zpos = 2, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Overlay_2", + .id = OVERLAY_PLANE_2, + .type = DRM_PLANE_TYPE_OVERLAY, + .num_formats = ARRAY_SIZE(primary_overlay_format0), + .formats = primary_overlay_format0, + .num_modifiers = ARRAY_SIZE(format_modifier0), + .modifiers = format_modifier0, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = DRM_MODE_ROTATE_0 | + DRM_MODE_ROTATE_90 | + DRM_MODE_ROTATE_180 | + DRM_MODE_ROTATE_270 | + DRM_MODE_REFLECT_X | + DRM_MODE_REFLECT_Y, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = FRAC_16_16(1, 3), + .max_scale = FRAC_16_16(10, 1), + .zpos = 3, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Cursor", + .id = CURSOR_PLANE_0, + .type = DRM_PLANE_TYPE_CURSOR, + .num_formats = ARRAY_SIZE(cursor_formats), + .formats = cursor_formats, + .num_modifiers = 0, + .modifiers = NULL, + .min_width = 32, + .min_height = 32, + .max_width = 64, + .max_height = 64, + .rotation = 0, + .degamma_size = 0, + .min_scale = DRM_PLANE_NO_SCALING, + .max_scale = DRM_PLANE_NO_SCALING, + .zpos = 255, + .watermark = false, + .color_mgmt = false, + .roi = false, + }, + { + .name = "Cursor_1", + .id = CURSOR_PLANE_1, + .type = DRM_PLANE_TYPE_CURSOR, + .num_formats = ARRAY_SIZE(cursor_formats), + .formats = cursor_formats, + .num_modifiers = 0, + .modifiers = NULL, + .min_width = 32, + .min_height = 32, + .max_width = 64, + .max_height = 64, + .rotation = 0, + .degamma_size = 0, + .min_scale = DRM_PLANE_NO_SCALING, + .max_scale = DRM_PLANE_NO_SCALING, + .zpos = 255, + .watermark = false, + .color_mgmt = false, + .roi = false, + }, + }, + { + /* DC_REV_2 */ + { + .name = "Primary", + .id = PRIMARY_PLANE_0, + .type = DRM_PLANE_TYPE_PRIMARY, + .num_formats = ARRAY_SIZE(primary_overlay_format1), + .formats = primary_overlay_format1, + .num_modifiers = ARRAY_SIZE(format_modifier1), + .modifiers = format_modifier1, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = DRM_MODE_ROTATE_0 | + DRM_MODE_ROTATE_90 | + DRM_MODE_ROTATE_180 | + DRM_MODE_ROTATE_270 | + DRM_MODE_REFLECT_X | + DRM_MODE_REFLECT_Y, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = FRAC_16_16(1, 3), + .max_scale = FRAC_16_16(10, 1), + .zpos = 0, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Overlay", + .id = OVERLAY_PLANE_0, + .type = DRM_PLANE_TYPE_OVERLAY, + .num_formats = ARRAY_SIZE(primary_overlay_format1), + .formats = primary_overlay_format1, + .num_modifiers = ARRAY_SIZE(format_modifier1), + .modifiers = format_modifier1, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = DRM_MODE_ROTATE_0 | + DRM_MODE_ROTATE_90 | + DRM_MODE_ROTATE_180 | + DRM_MODE_ROTATE_270 | + DRM_MODE_REFLECT_X | + DRM_MODE_REFLECT_Y, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = FRAC_16_16(1, 3), + .max_scale = FRAC_16_16(10, 1), + .zpos = 1, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Overlay_1", + .id = OVERLAY_PLANE_1, + .type = DRM_PLANE_TYPE_OVERLAY, + .num_formats = ARRAY_SIZE(primary_overlay_format1), + .formats = primary_overlay_format1, + .num_modifiers = ARRAY_SIZE(secondary_format_modifiers), + .modifiers = secondary_format_modifiers, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = 0, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = DRM_PLANE_NO_SCALING, + .max_scale = DRM_PLANE_NO_SCALING, + .zpos = 2, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Primary_1", + .id = PRIMARY_PLANE_1, + .type = DRM_PLANE_TYPE_PRIMARY, + .num_formats = ARRAY_SIZE(primary_overlay_format1), + .formats = primary_overlay_format1, + .num_modifiers = ARRAY_SIZE(format_modifier1), + .modifiers = format_modifier1, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = DRM_MODE_ROTATE_0 | + DRM_MODE_ROTATE_90 | + DRM_MODE_ROTATE_180 | + DRM_MODE_ROTATE_270 | + DRM_MODE_REFLECT_X | + DRM_MODE_REFLECT_Y, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = FRAC_16_16(1, 3), + .max_scale = FRAC_16_16(10, 1), + .zpos = 3, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Overlay_2", + .id = OVERLAY_PLANE_2, + .type = DRM_PLANE_TYPE_OVERLAY, + .num_formats = ARRAY_SIZE(primary_overlay_format1), + .formats = primary_overlay_format1, + .num_modifiers = ARRAY_SIZE(format_modifier1), + .modifiers = format_modifier1, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = DRM_MODE_ROTATE_0 | + DRM_MODE_ROTATE_90 | + DRM_MODE_ROTATE_180 | + DRM_MODE_ROTATE_270 | + DRM_MODE_REFLECT_X | + DRM_MODE_REFLECT_Y, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = FRAC_16_16(1, 3), + .max_scale = FRAC_16_16(10, 1), + .zpos = 4, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Overlay_3", + .id = OVERLAY_PLANE_3, + .type = DRM_PLANE_TYPE_OVERLAY, + .num_formats = ARRAY_SIZE(primary_overlay_format1), + .formats = primary_overlay_format1, + .num_modifiers = ARRAY_SIZE(secondary_format_modifiers), + .modifiers = secondary_format_modifiers, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = 0, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = DRM_PLANE_NO_SCALING, + .max_scale = DRM_PLANE_NO_SCALING, + .zpos = 5, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Cursor", + .id = CURSOR_PLANE_0, + .type = DRM_PLANE_TYPE_CURSOR, + .num_formats = ARRAY_SIZE(cursor_formats), + .formats = cursor_formats, + .num_modifiers = 0, + .modifiers = NULL, + .min_width = 32, + .min_height = 32, + .max_width = 64, + .max_height = 64, + .rotation = 0, + .degamma_size = 0, + .min_scale = DRM_PLANE_NO_SCALING, + .max_scale = DRM_PLANE_NO_SCALING, + .zpos = 255, + .watermark = false, + .color_mgmt = false, + .roi = false, + }, + { + .name = "Cursor_1", + .id = CURSOR_PLANE_1, + .type = DRM_PLANE_TYPE_CURSOR, + .num_formats = ARRAY_SIZE(cursor_formats), + .formats = cursor_formats, + .num_modifiers = 0, + .modifiers = NULL, + .min_width = 32, + .min_height = 32, + .max_width = 64, + .max_height = 64, + .rotation = 0, + .degamma_size = 0, + .min_scale = DRM_PLANE_NO_SCALING, + .max_scale = DRM_PLANE_NO_SCALING, + .zpos = 255, + .watermark = false, + .color_mgmt = false, + .roi = false, + }, + }, +}; + +static const struct vs_dc_info dc_info[] = { + { + /* DC_REV_0 */ + .name = "DC8200", + .panel_num = 2, + .plane_num = 8, + .planes = dc_hw_planes[DC_REV_0], + .layer_num = 6, + .max_bpc = 10, + .color_formats = DRM_COLOR_FORMAT_RGB444 | + DRM_COLOR_FORMAT_YCBCR444 | + DRM_COLOR_FORMAT_YCBCR422 | + DRM_COLOR_FORMAT_YCBCR420, + .gamma_size = GAMMA_EX_SIZE, + .gamma_bits = 12, + .pitch_alignment = 128, + .pipe_sync = false, + .mmu_prefetch = false, + .background = true, + .panel_sync = true, + .cap_dec = true, + }, + { + /* DC_REV_1 */ + .name = "DC8200", + .panel_num = 2, + .plane_num = 6, + .planes = dc_hw_planes[DC_REV_1], + .layer_num = 4, + .max_bpc = 10, + .color_formats = DRM_COLOR_FORMAT_RGB444 | + DRM_COLOR_FORMAT_YCBCR444 | + DRM_COLOR_FORMAT_YCBCR422 | + DRM_COLOR_FORMAT_YCBCR420, + .gamma_size = GAMMA_EX_SIZE, + .gamma_bits = 12, + .pitch_alignment = 128, + .pipe_sync = false, + .mmu_prefetch = false, + .background = true, + .panel_sync = true, + .cap_dec = true, + }, + { + /* DC_REV_2 */ + .name = "DC8200", + .panel_num = 2, + .plane_num = 8, + .planes = dc_hw_planes[DC_REV_2], + .layer_num = 6, + .max_bpc = 10, + .color_formats = DRM_COLOR_FORMAT_RGB444 | + DRM_COLOR_FORMAT_YCBCR444 | + DRM_COLOR_FORMAT_YCBCR422 | + DRM_COLOR_FORMAT_YCBCR420, + .gamma_size = GAMMA_EX_SIZE, + .gamma_bits = 12, + .pitch_alignment = 128, + .pipe_sync = false, + .mmu_prefetch = false, + .background = true, + .panel_sync = true, + .cap_dec = false, + }, +}; + +static const struct dc_hw_funcs hw_func; + +static inline u32 hi_read(struct dc_hw *hw, u32 reg) +{ + return readl(hw->hi_base + reg); +} + +static inline void hi_write(struct dc_hw *hw, u32 reg, u32 value) +{ + writel(value, hw->hi_base + reg); +} + +static inline void dc_write(struct dc_hw *hw, u32 reg, u32 value) +{ + writel(value, hw->reg_base + reg - DC_REG_BASE); +} + +static inline u32 dc_read(struct dc_hw *hw, u32 reg) +{ + u32 value = readl(hw->reg_base + reg - DC_REG_BASE); + + return value; +} + +static inline void dc_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear) +{ + u32 value = dc_read(hw, reg); + + value &= ~clear; + value |= set; + dc_write(hw, reg, value); +} + +static void load_default_filter(struct dc_hw *hw, + const struct dc_hw_plane_reg *reg, u32 offset) +{ + u8 i; + + dc_write(hw, reg->scale_config + offset, 0x33); + dc_write(hw, reg->init_offset + offset, 0x80008000); + dc_write(hw, reg->h_filter_coef_index + offset, 0x00); + for (i = 0; i < H_COEF_SIZE; i++) + dc_write(hw, reg->h_filter_coef_data + offset, horkernel[i]); + + dc_write(hw, reg->v_filter_coef_index + offset, 0x00); + for (i = 0; i < V_COEF_SIZE; i++) + dc_write(hw, reg->v_filter_coef_data + offset, verkernel[i]); +} + +static void load_rgb_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg, + u32 offset, u16 *table) +{ + dc_write(hw, reg->rgb_to_rgb_coef0 + offset, table[0] | (table[1] << 16)); + dc_write(hw, reg->rgb_to_rgb_coef1 + offset, table[2] | (table[3] << 16)); + dc_write(hw, reg->rgb_to_rgb_coef2 + offset, table[4] | (table[5] << 16)); + dc_write(hw, reg->rgb_to_rgb_coef3 + offset, table[6] | (table[7] << 16)); + dc_write(hw, reg->rgb_to_rgb_coef4 + offset, table[8]); +} + +static void load_yuv_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg, + u32 offset, s32 *table) +{ + dc_write(hw, reg->yuv_to_rgb_coef0 + offset, + (0xFFFF & table[0]) | (table[1] << 16)); + dc_write(hw, reg->yuv_to_rgb_coef1 + offset, + (0xFFFF & table[2]) | (table[3] << 16)); + dc_write(hw, reg->yuv_to_rgb_coef2 + offset, + (0xFFFF & table[4]) | (table[5] << 16)); + dc_write(hw, reg->yuv_to_rgb_coef3 + offset, + (0xFFFF & table[6]) | (table[7] << 16)); + dc_write(hw, reg->yuv_to_rgb_coef4 + offset, table[8]); + dc_write(hw, reg->yuv_to_rgb_coefd0 + offset, table[9]); + dc_write(hw, reg->yuv_to_rgb_coefd1 + offset, table[10]); + dc_write(hw, reg->yuv_to_rgb_coefd2 + offset, table[11]); + dc_write(hw, reg->y_clamp_bound + offset, table[12] | (table[13] << 16)); + dc_write(hw, reg->uv_clamp_bound + offset, table[14] | (table[15] << 16)); +} + +static void load_rgb_to_yuv(struct dc_hw *hw, u32 offset, s16 *table) +{ + dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF0 + offset, + table[0] | (table[1] << 16)); + dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF1 + offset, + table[2] | (table[3] << 16)); + dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF2 + offset, + table[4] | (table[5] << 16)); + dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF3 + offset, + table[6] | (table[7] << 16)); + dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF4 + offset, table[8]); + dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD0 + offset, table[9]); + dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD1 + offset, table[10]); + dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD2 + offset, table[11]); +} + +static bool is_rgb(enum dc_hw_color_format format) +{ + switch (format) { + case FORMAT_X4R4G4B4: + case FORMAT_A4R4G4B4: + case FORMAT_X1R5G5B5: + case FORMAT_A1R5G5B5: + case FORMAT_R5G6B5: + case FORMAT_X8R8G8B8: + case FORMAT_A8R8G8B8: + case FORMAT_A2R10G10B10: + return true; + default: + return false; + } +} + +static void load_degamma_table(struct dc_hw *hw, + const struct dc_hw_plane_reg *reg, + u32 offset, u16 *table) +{ + u16 i; + u32 value; + + dc_write(hw, reg->degamma_index + offset, 0); + + for (i = 0; i < DEGAMMA_SIZE; i++) { + value = table[i] | (table[i] << 16); + dc_write(hw, reg->degamma_data + offset, value); + dc_write(hw, reg->degamma_ex_data + offset, table[i]); + } +} + +static u32 get_addr_offset(u32 id) +{ + u32 offset = 0; + + switch (id) { + case PRIMARY_PLANE_1: + case OVERLAY_PLANE_1: + offset = 0x04; + break; + case OVERLAY_PLANE_2: + offset = 0x08; + break; + case OVERLAY_PLANE_3: + offset = 0x0C; + break; + default: + break; + } + + return offset; +} + +int dc_hw_init(struct dc_hw *hw) +{ + u8 i, id, panel_num, layer_num; + u32 offset; + u32 revision = hi_read(hw, DC_HW_REVISION); + u32 cid = hi_read(hw, DC_HW_CHIP_CID); + const struct dc_hw_plane_reg *reg; + + switch (revision) { + case 0x5720: + hw->rev = DC_REV_0; + break; + case 0x5721: + switch (cid) { + case 0x30B: + hw->rev = DC_REV_1; + break; + case 0x310: + hw->rev = DC_REV_2; + break; + default: + hw->rev = DC_REV_0; + break; + } + break; + default: + return -ENXIO; + } + + hw->info = (struct vs_dc_info *)&dc_info[hw->rev]; + hw->func = (struct dc_hw_funcs *)&hw_func; + + layer_num = hw->info->layer_num; + for (i = 0; i < layer_num; i++) { + id = hw->info->planes[i].id; + offset = get_addr_offset(id); + if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) + reg = &dc_plane_reg[0]; + else + reg = &dc_plane_reg[1]; + + load_default_filter(hw, reg, offset); + load_rgb_to_rgb(hw, reg, offset, RGB2RGB); + } + + panel_num = hw->info->panel_num; + for (i = 0; i < panel_num; i++) { + offset = i << 2; + + load_rgb_to_yuv(hw, offset, RGB2YUV); + dc_write(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0x111); + + offset = i ? DC_CURSOR_OFFSET : 0; + dc_write(hw, DC_CURSOR_BACKGROUND + offset, 0x00FFFFFF); + dc_write(hw, DC_CURSOR_FOREGROUND + offset, 0x00AAAAAA); + } + + return 0; +} + +void dc_hw_deinit(struct dc_hw *hw) +{ + /* Nothing to do */ +} + +void dc_hw_update_plane(struct dc_hw *hw, u8 id, + struct dc_hw_fb *fb, struct dc_hw_scale *scale, + struct dc_hw_position *pos, struct dc_hw_blend *blend) +{ + struct dc_hw_plane *plane = &hw->plane[id]; + + if (plane) { + if (fb) { + if (!fb->enable) + plane->fb.enable = false; + else + memcpy(&plane->fb, fb, + sizeof(*fb) - sizeof(fb->dirty)); + plane->fb.dirty = true; + } + if (scale) { + memcpy(&plane->scale, scale, + sizeof(*scale) - sizeof(scale->dirty)); + plane->scale.dirty = true; + } + if (pos) { + memcpy(&plane->pos, pos, + sizeof(*pos) - sizeof(pos->dirty)); + plane->pos.dirty = true; + } + if (blend) { + memcpy(&plane->blend, blend, + sizeof(*blend) - sizeof(blend->dirty)); + plane->blend.dirty = true; + } + } +} + +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode) +{ + struct dc_hw_plane *plane = &hw->plane[id]; + + if (plane) { + if (hw->info->planes[id].degamma_size) { + plane->degamma.mode = mode; + plane->degamma.dirty = true; + } else { + plane->degamma.dirty = false; + } + } +} + +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi) +{ + struct dc_hw_plane *plane = &hw->plane[id]; + + if (plane) { + memcpy(&plane->roi, roi, sizeof(*roi) - sizeof(roi->dirty)); + plane->roi.dirty = true; + } +} + +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id, + struct dc_hw_colorkey *colorkey) +{ + struct dc_hw_plane *plane = &hw->plane[id]; + + if (plane) { + memcpy(&plane->colorkey, colorkey, + sizeof(*colorkey) - sizeof(colorkey->dirty)); + plane->colorkey.dirty = true; + } +} + +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos) +{ + memcpy(&hw->qos, qos, sizeof(*qos) - sizeof(qos->dirty)); + hw->qos.dirty = true; +} + +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor) +{ + memcpy(&hw->cursor[id], cursor, sizeof(*cursor) - sizeof(cursor->dirty)); + hw->cursor[id].dirty = true; +} + +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index, + u16 r, u16 g, u16 b) +{ + if (index >= hw->info->gamma_size) + return; + + hw->gamma[id].gamma[index][0] = r; + hw->gamma[id].gamma[index][1] = g; + hw->gamma[id].gamma[index][2] = b; + hw->gamma[id].dirty = true; +} + +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable) +{ + hw->gamma[id].enable = enable; + hw->gamma[id].dirty = true; +} + +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display) +{ + u8 id = display->id; + + memcpy(&hw->display[id], display, sizeof(*display)); + + hw->func->display(hw, display); +} + +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable) +{ + if (enable) + hi_write(hw, AQ_INTR_ENBL, 0xFFFFFFFF); + else + hi_write(hw, AQ_INTR_ENBL, 0); +} + +u32 dc_hw_get_interrupt(struct dc_hw *hw) +{ + return hi_read(hw, AQ_INTR_ACKNOWLEDGE); +} + +bool dc_hw_check_underflow(struct dc_hw *hw) +{ + return dc_read(hw, DC_FRAMEBUFFER_CONFIG) & BIT(5); +} + +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable) +{ + u32 i, offset; + u8 id, layer_num = hw->info->layer_num; + u8 panel_num = hw->info->panel_num; + + for (i = 0; i < layer_num; i++) { + id = hw->info->planes[i].id; + offset = get_addr_offset(id); + if (enable) { + if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) + dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, BIT(12), 0); + else + dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, BIT(31), 0); + } else { + if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) + dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, 0, BIT(12)); + else + dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, 0, BIT(31)); + } + } + + for (i = 0; i < panel_num; i++) { + offset = i << 2; + if (enable) + dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, BIT(0)); + else + dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, BIT(0), 0); + } +} + +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id) +{ + if (out <= OUT_DP) + hw->out[id] = out; +} + +static void gamma_ex_commit(struct dc_hw *hw) +{ + u8 panel_num = hw->info->panel_num; + u16 i, j; + u32 value; + + for (j = 0; j < panel_num; j++) { + if (hw->gamma[j].dirty) { + if (hw->gamma[j].enable) { + dc_write(hw, DC_DISPLAY_GAMMA_EX_INDEX + (j << 2), 0x00); + for (i = 0; i < GAMMA_EX_SIZE; i++) { + value = hw->gamma[j].gamma[i][2] | + (hw->gamma[j].gamma[i][1] << 12); + dc_write(hw, DC_DISPLAY_GAMMA_EX_DATA + (j << 2), value); + dc_write(hw, DC_DISPLAY_GAMMA_EX_ONE_DATA + (j << 2), + hw->gamma[j].gamma[i][0]); + } + dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2), + BIT(13), 0); + } else { + dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2), + 0, BIT(13)); + } + hw->gamma[j].dirty = false; + } + } +} + +static void plane_commit(struct dc_hw *hw) +{ + struct dc_hw_plane *plane; + const struct dc_hw_plane_reg *reg; + bool primary = false; + u8 id, layer_num = hw->info->layer_num; + u32 i, offset; + + for (i = 0; i < layer_num; i++) { + plane = &hw->plane[i]; + id = hw->info->planes[i].id; + offset = get_addr_offset(id); + if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) { + reg = &dc_plane_reg[0]; + primary = true; + } else { + reg = &dc_plane_reg[1]; + primary = false; + } + + if (plane->fb.dirty) { + if (plane->fb.enable) { + dc_write(hw, reg->y_address + offset, + plane->fb.y_address); + dc_write(hw, reg->u_address + offset, + plane->fb.u_address); + dc_write(hw, reg->v_address + offset, + plane->fb.v_address); + dc_write(hw, reg->y_stride + offset, + plane->fb.y_stride); + dc_write(hw, reg->u_stride + offset, + plane->fb.u_stride); + dc_write(hw, reg->v_stride + offset, + plane->fb.v_stride); + dc_write(hw, reg->size + offset, + plane->fb.width | + (plane->fb.height << 15)); + dc_write(hw, reg->water_mark + offset, + plane->fb.water_mark); + + if (plane->fb.clear_enable) + dc_write(hw, reg->clear_value + offset, + plane->fb.clear_value); + } + + if (primary) { + dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset, + (plane->fb.format << 26) | + (plane->fb.uv_swizzle << 25) | + (plane->fb.swizzle << 23) | + (plane->fb.tile_mode << 17) | + (plane->fb.yuv_color_space << 14) | + (plane->fb.rotation << 11) | + (plane->fb.clear_enable << 8), + (0x1F << 26) | + BIT(25) | + (0x03 << 23) | + (0x1F << 17) | + (0x07 << 14) | + (0x07 << 11) | + BIT(8)); + dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, + (plane->fb.dec_enable << 1) | + (plane->fb.enable << 13) | + (plane->fb.zpos << 16) | + (plane->fb.display_id << 19), + BIT(1) | BIT(13) | (0x07 << 16) | BIT(19)); + } else { + dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, + (plane->fb.dec_enable << 27) | + (plane->fb.clear_enable << 25) | + (plane->fb.enable << 24) | + (plane->fb.format << 16) | + (plane->fb.uv_swizzle << 15) | + (plane->fb.swizzle << 13) | + (plane->fb.tile_mode << 8) | + (plane->fb.yuv_color_space << 5) | + (plane->fb.rotation << 2), + BIT(27) | + BIT(25) | + BIT(24) | + (0x1F << 16) | + BIT(15) | + (0x03 << 13) | + (0x1F << 8) | + (0x07 << 5) | + (0x07 << 2)); + dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset, + plane->fb.zpos | (plane->fb.display_id << 3), + 0x07 | BIT(3)); + } + plane->fb.dirty = false; + } + + if (plane->scale.dirty) { + if (plane->scale.enable) { + dc_write(hw, reg->scale_factor_x + offset, + plane->scale.scale_factor_x); + dc_write(hw, reg->scale_factor_y + offset, + plane->scale.scale_factor_y); + if (primary) + dc_set_clear(hw, + DC_FRAMEBUFFER_CONFIG + offset, + BIT(22), 0); + else + dc_set_clear(hw, + DC_OVERLAY_SCALE_CONFIG + offset, + BIT(8), 0); + } else { + if (primary) + dc_set_clear(hw, + DC_FRAMEBUFFER_CONFIG + offset, + 0, BIT(22)); + else + dc_set_clear(hw, + DC_OVERLAY_SCALE_CONFIG + offset, + 0, BIT(8)); + } + plane->scale.dirty = false; + } + + if (plane->pos.dirty) { + dc_write(hw, reg->top_left + offset, + plane->pos.start_x | + (plane->pos.start_y << 15)); + dc_write(hw, reg->bottom_right + offset, + plane->pos.end_x | + (plane->pos.end_y << 15)); + plane->pos.dirty = false; + } + + if (plane->blend.dirty) { + dc_write(hw, reg->src_global_color + offset, + plane->blend.alpha << 24); + dc_write(hw, reg->dst_global_color + offset, + plane->blend.alpha << 24); + switch (plane->blend.blend_mode) { + case BLEND_PREMULTI: + dc_write(hw, reg->blend_config + offset, 0x3450); + break; + case BLEND_COVERAGE: + dc_write(hw, reg->blend_config + offset, 0x3950); + break; + case BLEND_PIXEL_NONE: + dc_write(hw, reg->blend_config + offset, 0x3548); + break; + default: + break; + } + plane->blend.dirty = false; + } + + if (plane->colorkey.dirty) { + dc_write(hw, reg->color_key + offset, plane->colorkey.colorkey); + dc_write(hw, reg->color_key_high + offset, + plane->colorkey.colorkey_high); + + if (primary) + dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset, + plane->colorkey.transparency << 9, 0x03 << 9); + else + dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, + plane->colorkey.transparency, 0x03); + + plane->colorkey.dirty = false; + } + + if (plane->roi.dirty) { + if (plane->roi.enable) { + dc_write(hw, reg->roi_origin + offset, + plane->roi.x | (plane->roi.y << 16)); + dc_write(hw, reg->roi_size + offset, + plane->roi.width | (plane->roi.height << 16)); + if (primary) + dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, + BIT(0), 0); + else + dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, + BIT(22), 0); + } else { + if (primary) + dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, + 0, BIT(0)); + else + dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, + 0, BIT(22)); + } + plane->roi.dirty = false; + } + } +} + +static void plane_ex_commit(struct dc_hw *hw) +{ + struct dc_hw_plane *plane; + const struct dc_hw_plane_reg *reg; + bool primary = false; + u8 id, layer_num = hw->info->layer_num; + u32 i, offset; + + for (i = 0; i < layer_num; i++) { + plane = &hw->plane[i]; + id = hw->info->planes[i].id; + offset = get_addr_offset(id); + if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) { + reg = &dc_plane_reg[0]; + primary = true; + } else { + reg = &dc_plane_reg[1]; + primary = false; + } + + if (plane->fb.dirty) { + if (is_rgb(plane->fb.format)) { + if (primary) + dc_set_clear(hw, + DC_FRAMEBUFFER_CONFIG_EX + offset, + BIT(6), BIT(8)); + else + dc_set_clear(hw, + DC_OVERLAY_CONFIG + offset, + BIT(29), BIT(30)); + } else { + if (primary) + dc_set_clear(hw, + DC_FRAMEBUFFER_CONFIG_EX + offset, + BIT(8), BIT(6)); + else + dc_set_clear(hw, + DC_OVERLAY_CONFIG + offset, + BIT(30), BIT(29)); + switch (plane->fb.yuv_color_space) { + case COLOR_SPACE_601: + load_yuv_to_rgb(hw, reg, offset, YUV601_2RGB); + break; + case COLOR_SPACE_709: + load_yuv_to_rgb(hw, reg, offset, YUV709_2RGB); + break; + case COLOR_SPACE_2020: + load_yuv_to_rgb(hw, reg, offset, YUV2020_2RGB); + break; + default: + break; + } + } + } + if (plane->degamma.dirty) { + switch (plane->degamma.mode) { + case VS_DEGAMMA_DISABLE: + if (primary) + dc_set_clear(hw, + DC_FRAMEBUFFER_CONFIG_EX + offset, + 0, BIT(5)); + else + dc_set_clear(hw, + DC_OVERLAY_CONFIG + offset, + 0, BIT(28)); + break; + case VS_DEGAMMA_BT709: + load_degamma_table(hw, reg, offset, DEGAMMA_709); + if (primary) + dc_set_clear(hw, + DC_FRAMEBUFFER_CONFIG_EX + offset, + BIT(5), 0); + else + dc_set_clear(hw, + DC_OVERLAY_CONFIG + offset, + BIT(28), 0); + break; + case VS_DEGAMMA_BT2020: + load_degamma_table(hw, reg, offset, DEGAMMA_2020); + if (primary) + dc_set_clear(hw, + DC_FRAMEBUFFER_CONFIG_EX + offset, + BIT(5), 0); + else + dc_set_clear(hw, + DC_OVERLAY_CONFIG + offset, + BIT(28), 0); + break; + default: + break; + } + plane->degamma.dirty = false; + } + } + plane_commit(hw); +} + +static void setup_display(struct dc_hw *hw, struct dc_hw_display *display) +{ + u8 id = display->id; + u32 dpi_cfg, offset = id << 2; + + if (hw->display[id].enable) { + switch (display->bus_format) { + case MEDIA_BUS_FMT_RGB565_1X16: + dpi_cfg = 0; + break; + case MEDIA_BUS_FMT_RGB666_1X18: + dpi_cfg = 3; + break; + case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: + dpi_cfg = 4; + break; + case MEDIA_BUS_FMT_RGB888_1X24: + dpi_cfg = 5; + break; + case MEDIA_BUS_FMT_RGB101010_1X30: + dpi_cfg = 6; + break; + default: + dpi_cfg = 5; + break; + } + dc_write(hw, DC_DISPLAY_DPI_CONFIG + offset, dpi_cfg); + + if (id == 0) + dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2)); + else + dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2)); + + dc_write(hw, DC_DISPLAY_H + offset, hw->display[id].h_active | + (hw->display[id].h_total << 16)); + dc_write(hw, DC_DISPLAY_H_SYNC + offset, + hw->display[id].h_sync_start | + (hw->display[id].h_sync_end << 15) | + (hw->display[id].h_sync_polarity ? 0 : BIT(31)) | + BIT(30)); + dc_write(hw, DC_DISPLAY_V + offset, hw->display[id].v_active | + (hw->display[id].v_total << 16)); + dc_write(hw, DC_DISPLAY_V_SYNC + offset, + hw->display[id].v_sync_start | + (hw->display[id].v_sync_end << 15) | + (hw->display[id].v_sync_polarity ? 0 : BIT(31)) | + BIT(30)); + + if (hw->info->pipe_sync) { + switch (display->sync_mode) { + case VS_SINGLE_DC: + dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX, + 0, BIT(3) | BIT(4)); + break; + case VS_MULTI_DC_PRIMARY: + dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX, + BIT(3) | BIT(4), 0); + break; + case VS_MULTI_DC_SECONDARY: + dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX, + BIT(3), BIT(4)); + break; + default: + break; + } + } + + if (hw->info->background) + dc_write(hw, DC_FRAMEBUFFER_BG_COLOR + offset, + hw->display[id].bg_color); + + if (hw->display[id].dither_enable) { + dc_write(hw, DC_DISPLAY_DITHER_TABLE_LOW + offset, + DC_DISPLAY_DITHERTABLE_LOW); + dc_write(hw, DC_DISPLAY_DITHER_TABLE_HIGH + offset, + DC_DISPLAY_DITHERTABLE_HIGH); + dc_write(hw, DC_DISPLAY_DITHER_CONFIG + offset, BIT(31)); + } else { + dc_write(hw, DC_DISPLAY_DITHER_CONFIG + offset, 0); + } + + dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(12), 0); + if (hw->display[id].sync_enable) + dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(2) | BIT(3), 0); + else if (id == 0) + dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(0), BIT(3)); + else + dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(1), BIT(3)); + } else { + dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(12)); + if (id == 0) + dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2)); + else + dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2)); + } +} + +static void setup_display_ex(struct dc_hw *hw, struct dc_hw_display *display) +{ + u8 id = display->id; + u32 dp_cfg, offset = id << 2; + bool is_yuv = false; + + if (hw->display[id].enable && hw->out[id] == OUT_DP) { + switch (display->bus_format) { + case MEDIA_BUS_FMT_RGB565_1X16: + dp_cfg = 0; + break; + case MEDIA_BUS_FMT_RGB666_1X18: + dp_cfg = 1; + break; + case MEDIA_BUS_FMT_RGB888_1X24: + dp_cfg = 2; + break; + case MEDIA_BUS_FMT_RGB101010_1X30: + dp_cfg = 3; + break; + case MEDIA_BUS_FMT_UYVY8_1X16: + dp_cfg = 2 << 4; + is_yuv = true; + break; + case MEDIA_BUS_FMT_YUV8_1X24: + dp_cfg = 4 << 4; + is_yuv = true; + break; + case MEDIA_BUS_FMT_UYVY10_1X20: + dp_cfg = 8 << 4; + is_yuv = true; + break; + case MEDIA_BUS_FMT_YUV10_1X30: + dp_cfg = 10 << 4; + is_yuv = true; + break; + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + dp_cfg = 12 << 4; + is_yuv = true; + break; + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + dp_cfg = 13 << 4; + is_yuv = true; + break; + default: + dp_cfg = 2; + break; + } + if (is_yuv) + dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(16), 0); + else + dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(16)); + dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | BIT(3)); + } + + if (hw->out[id] == OUT_DPI) + dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, BIT(3)); + + setup_display(hw, display); +} + +static const struct dc_hw_funcs hw_func = { + .gamma = &gamma_ex_commit, + .plane = &plane_ex_commit, + .display = setup_display_ex, +}; + +void dc_hw_commit(struct dc_hw *hw) +{ + u32 i, offset = 0; + u8 plane_num = hw->info->plane_num; + u8 layer_num = hw->info->layer_num; + u8 cursor_num = plane_num - layer_num; + + hw->func->gamma(hw); + hw->func->plane(hw); + + for (i = 0; i < cursor_num; i++) { + if (hw->cursor[i].dirty) { + offset = hw->cursor[i].display_id ? DC_CURSOR_OFFSET : 0; + if (hw->cursor[i].enable) { + dc_write(hw, DC_CURSOR_ADDRESS + offset, + hw->cursor[i].address); + dc_write(hw, DC_CURSOR_LOCATION + offset, hw->cursor[i].x | + (hw->cursor[i].y << 16)); + dc_set_clear(hw, DC_CURSOR_CONFIG + offset, + (hw->cursor[i].hot_x << 16) | + (hw->cursor[i].hot_y << 8) | + (hw->cursor[i].size << 5) | + BIT(3) | BIT(2) | 0x02, + (0xFF << 16) | + (0xFF << 8) | + (0x07 << 5) | 0x1F); + } else { + dc_set_clear(hw, DC_CURSOR_CONFIG + offset, BIT(3), 0x03); + } + hw->cursor[i].dirty = false; + } + } + + if (hw->qos.dirty) { + dc_set_clear(hw, DC_QOS_CONFIG, (hw->qos.high_value << 4) | + hw->qos.low_value, 0xFF); + hw->qos.dirty = false; + } +} + diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.h b/drivers/gpu/drm/verisilicon/vs_dc_hw.h new file mode 100644 index 000000000..81e522ab7 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.h @@ -0,0 +1,496 @@ +/* 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/version.h> + +#define AQ_INTR_ACKNOWLEDGE 0x0010 +#define AQ_INTR_ENBL 0x0014 +#define DC_HW_REVISION 0x0024 +#define DC_HW_CHIP_CID 0x0030 + +#define DC_REG_BASE 0x0800 +#define DC_REG_RANGE 0x2000 +#define DC_SEC_REG_OFFSET 0x100000 + +#define DC_FRAMEBUFFER_CONFIG 0x1518 +#define DC_FRAMEBUFFER_CONFIG_EX 0x1CC0 +#define DC_FRAMEBUFFER_SCALE_CONFIG 0x1520 +#define DC_FRAMEBUFFER_TOP_LEFT 0x24D8 +#define DC_FRAMEBUFFER_BOTTOM_RIGHT 0x24E0 +#define DC_FRAMEBUFFER_ADDRESS 0x1400 +#define DC_FRAMEBUFFER_U_ADDRESS 0x1530 +#define DC_FRAMEBUFFER_V_ADDRESS 0x1538 +#define DC_FRAMEBUFFER_STRIDE 0x1408 +#define DC_FRAMEBUFFER_U_STRIDE 0x1800 +#define DC_FRAMEBUFFER_V_STRIDE 0x1808 +#define DC_FRAMEBUFFER_SIZE 0x1810 +#define DC_FRAMEBUFFER_SCALE_FACTOR_X 0x1828 +#define DC_FRAMEBUFFER_SCALE_FACTOR_Y 0x1830 +#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX 0x1838 +#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA 0x1A00 +#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX 0x1A08 +#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA 0x1A10 +#define DC_FRAMEBUFFER_INIT_OFFSET 0x1A20 +#define DC_FRAMEBUFFER_COLOR_KEY 0x1508 +#define DC_FRAMEBUFFER_COLOR_KEY_HIGH 0x1510 +#define DC_FRAMEBUFFER_CLEAR_VALUE 0x1A18 +#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX 0x1818 +#define DC_FRAMEBUFFER_COLOR_TABLE_DATA 0x1820 +#define DC_FRAMEBUFFER_BG_COLOR 0x1528 +#define DC_FRAMEBUFFER_ROI_ORIGIN 0x1CB0 +#define DC_FRAMEBUFFER_ROI_SIZE 0x1CB8 +#define DC_FRAMEBUFFER_WATER_MARK 0x1CE8 +#define DC_FRAMEBUFFER_DEGAMMA_INDEX 0x1D88 +#define DC_FRAMEBUFFER_DEGAMMA_DATA 0x1D90 +#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA 0x1D98 +#define DC_FRAMEBUFFER_YUVTORGB_COEF0 0x1DA0 +#define DC_FRAMEBUFFER_YUVTORGB_COEF1 0x1DA8 +#define DC_FRAMEBUFFER_YUVTORGB_COEF2 0x1DB0 +#define DC_FRAMEBUFFER_YUVTORGB_COEF3 0x1DB8 +#define DC_FRAMEBUFFER_YUVTORGB_COEF4 0x1E00 +#define DC_FRAMEBUFFER_YUVTORGB_COEFD0 0x1E08 +#define DC_FRAMEBUFFER_YUVTORGB_COEFD1 0x1E10 +#define DC_FRAMEBUFFER_YUVTORGB_COEFD2 0x1E18 +#define DC_FRAMEBUFFER_Y_CLAMP_BOUND 0x1E88 +#define DC_FRAMEBUFFER_UV_CLAMP_BOUND 0x1E90 +#define DC_FRAMEBUFFER_RGBTORGB_COEF0 0x1E20 +#define DC_FRAMEBUFFER_RGBTORGB_COEF1 0x1E28 +#define DC_FRAMEBUFFER_RGBTORGB_COEF2 0x1E30 +#define DC_FRAMEBUFFER_RGBTORGB_COEF3 0x1E38 +#define DC_FRAMEBUFFER_RGBTORGB_COEF4 0x1E40 +#define DC_FRAMEBUFFER_BLEND_CONFIG 0x2510 +#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR 0x2500 +#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR 0x2508 + +#define DC_OVERLAY_CONFIG 0x1540 +#define DC_OVERLAY_CONFIG_EX 0x2540 +#define DC_OVERLAY_SCALE_CONFIG 0x1C00 +#define DC_OVERLAY_BLEND_CONFIG 0x1580 +#define DC_OVERLAY_TOP_LEFT 0x1640 +#define DC_OVERLAY_BOTTOM_RIGHT 0x1680 +#define DC_OVERLAY_ADDRESS 0x15C0 +#define DC_OVERLAY_U_ADDRESS 0x1840 +#define DC_OVERLAY_V_ADDRESS 0x1880 +#define DC_OVERLAY_STRIDE 0x1600 +#define DC_OVERLAY_U_STRIDE 0x18C0 +#define DC_OVERLAY_V_STRIDE 0x1900 +#define DC_OVERLAY_SIZE 0x17C0 +#define DC_OVERLAY_SCALE_FACTOR_X 0x1A40 +#define DC_OVERLAY_SCALE_FACTOR_Y 0x1A80 +#define DC_OVERLAY_H_FILTER_COEF_INDEX 0x1AC0 +#define DC_OVERLAY_H_FILTER_COEF_DATA 0x1B00 +#define DC_OVERLAY_V_FILTER_COEF_INDEX 0x1B40 +#define DC_OVERLAY_V_FILTER_COEF_DATA 0x1B80 +#define DC_OVERLAY_INIT_OFFSET 0x1BC0 +#define DC_OVERLAY_COLOR_KEY 0x1740 +#define DC_OVERLAY_COLOR_KEY_HIGH 0x1780 +#define DC_OVERLAY_CLEAR_VALUE 0x1940 +#define DC_OVERLAY_COLOR_TABLE_INDEX 0x1980 +#define DC_OVERLAY_COLOR_TABLE_DATA 0x19C0 +#define DC_OVERLAY_SRC_GLOBAL_COLOR 0x16C0 +#define DC_OVERLAY_DST_GLOBAL_COLOR 0x1700 +#define DC_OVERLAY_ROI_ORIGIN 0x1D00 +#define DC_OVERLAY_ROI_SIZE 0x1D40 +#define DC_OVERLAY_WATER_MARK 0x1DC0 +#define DC_OVERLAY_DEGAMMA_INDEX 0x2200 +#define DC_OVERLAY_DEGAMMA_DATA 0x2240 +#define DC_OVERLAY_DEGAMMA_EX_DATA 0x2280 +#define DC_OVERLAY_YUVTORGB_COEF0 0x1EC0 +#define DC_OVERLAY_YUVTORGB_COEF1 0x1F00 +#define DC_OVERLAY_YUVTORGB_COEF2 0x1F40 +#define DC_OVERLAY_YUVTORGB_COEF3 0x1F80 +#define DC_OVERLAY_YUVTORGB_COEF4 0x1FC0 +#define DC_OVERLAY_YUVTORGB_COEFD0 0x2000 +#define DC_OVERLAY_YUVTORGB_COEFD1 0x2040 +#define DC_OVERLAY_YUVTORGB_COEFD2 0x2080 +#define DC_OVERLAY_Y_CLAMP_BOUND 0x22C0 +#define DC_OVERLAY_UV_CLAMP_BOUND 0x2300 +#define DC_OVERLAY_RGBTORGB_COEF0 0x20C0 +#define DC_OVERLAY_RGBTORGB_COEF1 0x2100 +#define DC_OVERLAY_RGBTORGB_COEF2 0x2140 +#define DC_OVERLAY_RGBTORGB_COEF3 0x2180 +#define DC_OVERLAY_RGBTORGB_COEF4 0x21C0 + +#define DC_CURSOR_CONFIG 0x1468 +#define DC_CURSOR_ADDRESS 0x146C +#define DC_CURSOR_LOCATION 0x1470 +#define DC_CURSOR_BACKGROUND 0x1474 +#define DC_CURSOR_FOREGROUND 0x1478 +#define DC_CURSOR_CLK_GATING 0x1484 +#define DC_CURSOR_CONFIG_EX 0x24E8 +#define DC_CURSOR_OFFSET 0x1080 + +#define DC_DISPLAY_DITHER_CONFIG 0x1410 +#define DC_DISPLAY_PANEL_CONFIG 0x1418 +#define DC_DISPLAY_PANEL_CONFIG_EX 0x2518 +#define DC_DISPLAY_DITHER_TABLE_LOW 0x1420 +#define DC_DISPLAY_DITHER_TABLE_HIGH 0x1428 +#define DC_DISPLAY_H 0x1430 +#define DC_DISPLAY_H_SYNC 0x1438 +#define DC_DISPLAY_V 0x1440 +#define DC_DISPLAY_V_SYNC 0x1448 +#define DC_DISPLAY_CURRENT_LOCATION 0x1450 +#define DC_DISPLAY_GAMMA_INDEX 0x1458 +#define DC_DISPLAY_GAMMA_DATA 0x1460 +#define DC_DISPLAY_INT 0x147C +#define DC_DISPLAY_INT_ENABLE 0x1480 +#define DC_DISPLAY_DBI_CONFIG 0x1488 +#define DC_DISPLAY_GENERAL_CONFIG 0x14B0 +#define DC_DISPLAY_DPI_CONFIG 0x14B8 +#define DC_DISPLAY_PANEL_START 0x1CCC +#define DC_DISPLAY_DEBUG_COUNTER_SELECT 0x14D0 +#define DC_DISPLAY_DEBUG_COUNTER_VALUE 0x14D8 +#define DC_DISPLAY_DP_CONFIG 0x1CD0 +#define DC_DISPLAY_GAMMA_EX_INDEX 0x1CF0 +#define DC_DISPLAY_GAMMA_EX_DATA 0x1CF8 +#define DC_DISPLAY_GAMMA_EX_ONE_DATA 0x1D80 +#define DC_DISPLAY_RGBTOYUV_COEF0 0x1E48 +#define DC_DISPLAY_RGBTOYUV_COEF1 0x1E50 +#define DC_DISPLAY_RGBTOYUV_COEF2 0x1E58 +#define DC_DISPLAY_RGBTOYUV_COEF3 0x1E60 +#define DC_DISPLAY_RGBTOYUV_COEF4 0x1E68 +#define DC_DISPLAY_RGBTOYUV_COEFD0 0x1E70 +#define DC_DISPLAY_RGBTOYUV_COEFD1 0x1E78 +#define DC_DISPLAY_RGBTOYUV_COEFD2 0x1E80 + +#define DC_CLK_GATTING 0x1A28 +#define DC_QOS_CONFIG 0x1A38 + +#define DC_TRANSPARENCY_OPAQUE 0x00 +#define DC_TRANSPARENCY_KEY 0x02 +#define DC_DISPLAY_DITHERTABLE_LOW 0x7B48F3C0 +#define DC_DISPLAY_DITHERTABLE_HIGH 0x596AD1E2 + +#define GAMMA_SIZE 256 +#define GAMMA_EX_SIZE 300 +#define DEGAMMA_SIZE 260 + +#define RGB_TO_RGB_TABLE_SIZE 9 +#define YUV_TO_RGB_TABLE_SIZE 16 +#define RGB_TO_YUV_TABLE_SIZE 12 + +#define DC_LAYER_NUM 6 +#define DC_DISPLAY_NUM 2 +#define DC_CURSOR_NUM 2 + +enum dc_chip_rev { + DC_REV_0,/* For HW_REV_5720,HW_REV_5721_311 */ + DC_REV_1,/* For HW_REV_5721_30B */ + DC_REV_2,/* For HW_REV_5721_310 */ +}; + +enum dc_hw_plane_id { + PRIMARY_PLANE_0, + OVERLAY_PLANE_0, + OVERLAY_PLANE_1, + PRIMARY_PLANE_1, + OVERLAY_PLANE_2, + OVERLAY_PLANE_3, + CURSOR_PLANE_0, + CURSOR_PLANE_1, + PLANE_NUM +}; + +enum dc_hw_color_format { + FORMAT_X4R4G4B4,//0 + FORMAT_A4R4G4B4,//1 + FORMAT_X1R5G5B5,//2 + FORMAT_A1R5G5B5,//3 + FORMAT_R5G6B5,//4 + FORMAT_X8R8G8B8,//5 + FORMAT_A8R8G8B8,//6 + FORMAT_YUY2,//7 + FORMAT_UYVY,//8 + FORMAT_INDEX8,//9 + FORMAT_MONOCHROME,//10 + FORMAT_YV12 = 0xf, + FORMAT_A8,//16 + FORMAT_NV12,//17 + FORMAT_NV16,//18 + FORMAT_RG16,//19 + FORMAT_R8,//20 + FORMAT_NV12_10BIT,//21 + FORMAT_A2R10G10B10,//22 + FORMAT_NV16_10BIT,//23 + FORMAT_INDEX1,//24 + FORMAT_INDEX2,//25 + FORMAT_INDEX4,//26 + FORMAT_P010,//27 + FORMAT_YUV444,//28 + FORMAT_YUV444_10BIT,//29 +}; + +enum dc_hw_yuv_color_space { + COLOR_SPACE_601 = 0, + COLOR_SPACE_709 = 1, + COLOR_SPACE_2020 = 3, +}; + +enum dc_hw_rotation { + ROT_0 = 0, + ROT_90 = 4, + ROT_180 = 5, + ROT_270 = 6, + FLIP_X = 1, + FLIP_Y = 2, + FLIP_XY = 3, +}; + +enum dc_hw_swizzle { + SWIZZLE_ARGB = 0, + SWIZZLE_RGBA, + SWIZZLE_ABGR, + SWIZZLE_BGRA, +}; + +enum dc_hw_out { + OUT_DPI, + OUT_DP, +}; + +enum dc_hw_cursor_size { + CURSOR_SIZE_32X32 = 0, + CURSOR_SIZE_64X64, +}; + +enum dc_hw_blend_mode { + /* out.rgb = plane_alpha * fg.rgb + + * (1 - (plane_alpha * fg.alpha)) * bg.rgb + */ + BLEND_PREMULTI, + /* out.rgb = plane_alpha * fg.alpha * fg.rgb + + * (1 - (plane_alpha * fg.alpha)) * bg.rgb + */ + BLEND_COVERAGE, + /* out.rgb = plane_alpha * fg.rgb + + * (1 - plane_alpha) * bg.rgb + */ + BLEND_PIXEL_NONE, +}; + +struct dc_hw_plane_reg { + u32 y_address; + u32 u_address; + u32 v_address; + u32 y_stride; + u32 u_stride; + u32 v_stride; + u32 size; + u32 top_left; + u32 bottom_right; + u32 scale_factor_x; + u32 scale_factor_y; + u32 h_filter_coef_index; + u32 h_filter_coef_data; + u32 v_filter_coef_index; + u32 v_filter_coef_data; + u32 init_offset; + u32 color_key; + u32 color_key_high; + u32 clear_value; + u32 color_table_index; + u32 color_table_data; + u32 scale_config; + u32 water_mark; + u32 degamma_index; + u32 degamma_data; + u32 degamma_ex_data; + u32 src_global_color; + u32 dst_global_color; + u32 blend_config; + u32 roi_origin; + u32 roi_size; + u32 yuv_to_rgb_coef0; + u32 yuv_to_rgb_coef1; + u32 yuv_to_rgb_coef2; + u32 yuv_to_rgb_coef3; + u32 yuv_to_rgb_coef4; + u32 yuv_to_rgb_coefd0; + u32 yuv_to_rgb_coefd1; + u32 yuv_to_rgb_coefd2; + u32 y_clamp_bound; + u32 uv_clamp_bound; + u32 rgb_to_rgb_coef0; + u32 rgb_to_rgb_coef1; + u32 rgb_to_rgb_coef2; + u32 rgb_to_rgb_coef3; + u32 rgb_to_rgb_coef4; +}; + +struct dc_hw_fb { + u32 y_address; + u32 u_address; + u32 v_address; + u32 clear_value; + u32 water_mark; + u16 y_stride; + u16 u_stride; + u16 v_stride; + u16 width; + u16 height; + u8 format; + u8 tile_mode; + u8 rotation; + u8 yuv_color_space; + u8 swizzle; + u8 uv_swizzle; + u8 zpos; + u8 display_id; + bool clear_enable; + bool dec_enable; + bool enable; + bool dirty; +}; + +struct dc_hw_scale { + u32 scale_factor_x; + u32 scale_factor_y; + bool enable; + bool dirty; +}; + +struct dc_hw_position { + u16 start_x; + u16 start_y; + u16 end_x; + u16 end_y; + bool dirty; +}; + +struct dc_hw_blend { + u8 alpha; + u8 blend_mode; + bool dirty; +}; + +struct dc_hw_colorkey { + u32 colorkey; + u32 colorkey_high; + u8 transparency; + bool dirty; +}; + +struct dc_hw_roi { + u16 x; + u16 y; + u16 width; + u16 height; + bool enable; + bool dirty; +}; + +struct dc_hw_cursor { + u32 address; + u16 x; + u16 y; + u16 hot_x; + u16 hot_y; + u8 size; + u8 display_id; + bool enable; + bool dirty; +}; + +struct dc_hw_display { + u32 bus_format; + u16 h_active; + u16 h_total; + u16 h_sync_start; + u16 h_sync_end; + u16 v_active; + u16 v_total; + u16 v_sync_start; + u16 v_sync_end; + u16 sync_mode; + u32 bg_color; + u8 id; + bool h_sync_polarity; + bool v_sync_polarity; + bool enable; + bool sync_enable; + bool dither_enable; +}; + +struct dc_hw_gamma { + u16 gamma[GAMMA_EX_SIZE][3]; + bool enable; + bool dirty; +}; + +struct dc_hw_degamma { + u16 degamma[DEGAMMA_SIZE][3]; + u32 mode; + bool dirty; +}; + +struct dc_hw_plane { + struct dc_hw_fb fb; + struct dc_hw_position pos; + struct dc_hw_scale scale; + struct dc_hw_blend blend; + struct dc_hw_roi roi; + struct dc_hw_colorkey colorkey; + struct dc_hw_degamma degamma; +}; + +struct dc_hw_qos { + u8 low_value; + u8 high_value; + bool dirty; +}; + +struct dc_hw_read { + u32 reg; + u32 value; +}; + +struct dc_hw; +struct dc_hw_funcs { + void (*gamma)(struct dc_hw *hw); + void (*plane)(struct dc_hw *hw); + void (*display)(struct dc_hw *hw, struct dc_hw_display *display); +}; + +struct dc_hw { + enum dc_chip_rev rev; + enum dc_hw_out out[DC_DISPLAY_NUM]; + void *hi_base; + void *reg_base; + + struct dc_hw_display display[DC_DISPLAY_NUM]; + struct dc_hw_gamma gamma[DC_DISPLAY_NUM]; + struct dc_hw_plane plane[DC_LAYER_NUM]; + struct dc_hw_cursor cursor[DC_CURSOR_NUM]; + struct dc_hw_qos qos; + struct dc_hw_funcs *func; + struct vs_dc_info *info; +}; + +int dc_hw_init(struct dc_hw *hw); +void dc_hw_deinit(struct dc_hw *hw); +void dc_hw_update_plane(struct dc_hw *hw, u8 id, + struct dc_hw_fb *fb, struct dc_hw_scale *scale, + struct dc_hw_position *pos, struct dc_hw_blend *blend); +void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode); +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi); +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id, + struct dc_hw_colorkey *colorkey); +void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos); +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor); +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index, + u16 r, u16 g, u16 b); +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable); +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display); +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable); +u32 dc_hw_get_interrupt(struct dc_hw *hw); +bool dc_hw_check_underflow(struct dc_hw *hw); +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable); +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id); +void dc_hw_commit(struct dc_hw *hw); + +#endif /* __VS_DC_HW_H__ */ diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c index 69591e640..ddec92910 100644 --- a/drivers/gpu/drm/verisilicon/vs_drv.c +++ b/drivers/gpu/drm/verisilicon/vs_drv.c @@ -33,6 +33,7 @@ #include "vs_drv.h" #include "vs_modeset.h" #include "vs_gem.h" +#include "vs_dc.h"
#define DRV_NAME "starfive" #define DRV_DESC "Starfive DRM driver" @@ -155,7 +156,7 @@ static const struct component_master_ops vs_drm_ops = { };
static struct platform_driver *drm_sub_drivers[] = { - + &dc_platform_driver,
/* connector + encoder*/ #ifdef CONFIG_STARFIVE_HDMI diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c new file mode 100644 index 000000000..61648d7b1 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_plane.c @@ -0,0 +1,502 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + */ + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_blend.h> +#include <drm/drm_gem_dma_helper.h> +#include <drm/drm_fb_dma_helper.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_plane.h> +#include <drm/drm_plane_helper.h> + +#include <drm/vs_drm.h> + +#include "vs_crtc.h" +#include "vs_modeset.h" +#include "vs_gem.h" +#include "vs_plane.h" +#include "vs_type.h" +#include "vs_drv.h" +#include "vs_dc.h" + +static void vs_plane_reset(struct drm_plane *plane) +{ + struct vs_plane_state *state; + struct vs_plane *vs_plane = to_vs_plane(plane); + + if (plane->state) { + __drm_atomic_helper_plane_destroy_state(plane->state); + + state = to_vs_plane_state(plane->state); + kfree(state); + plane->state = NULL; + } + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return; + + __drm_atomic_helper_plane_reset(plane, &state->base); + + state->degamma = VS_DEGAMMA_DISABLE; + state->degamma_changed = false; + state->base.zpos = vs_plane->id; + + memset(&state->status, 0, sizeof(state->status)); +} + +static void _vs_plane_duplicate_blob(struct vs_plane_state *state, + struct vs_plane_state *ori_state) +{ + state->watermark = ori_state->watermark; + state->color_mgmt = ori_state->color_mgmt; + state->roi = ori_state->roi; + + if (state->watermark) + drm_property_blob_get(state->watermark); + if (state->color_mgmt) + drm_property_blob_get(state->color_mgmt); + if (state->roi) + drm_property_blob_get(state->roi); +} + +static int +_vs_plane_set_property_blob_from_id(struct drm_device *dev, + struct drm_property_blob **blob, + u64 blob_id, + size_t expected_size) +{ + struct drm_property_blob *new_blob = NULL; + + if (blob_id) { + new_blob = drm_property_lookup_blob(dev, blob_id); + if (!new_blob) + return -EINVAL; + + if (new_blob->length != expected_size) { + drm_property_blob_put(new_blob); + return -EINVAL; + } + } + + drm_property_replace_blob(blob, new_blob); + drm_property_blob_put(new_blob); + + return 0; +} + +static struct drm_plane_state * +vs_plane_atomic_duplicate_state(struct drm_plane *plane) +{ + struct vs_plane_state *ori_state; + struct vs_plane_state *state; + + if (WARN_ON(!plane->state)) + return NULL; + + ori_state = to_vs_plane_state(plane->state); + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + __drm_atomic_helper_plane_duplicate_state(plane, &state->base); + + state->degamma = ori_state->degamma; + state->degamma_changed = ori_state->degamma_changed; + + _vs_plane_duplicate_blob(state, ori_state); + memcpy(&state->status, &ori_state->status, sizeof(ori_state->status)); + + return &state->base; +} + +static void vs_plane_atomic_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct vs_plane_state *vs_plane_state = to_vs_plane_state(state); + + __drm_atomic_helper_plane_destroy_state(state); + + drm_property_blob_put(vs_plane_state->watermark); + drm_property_blob_put(vs_plane_state->color_mgmt); + drm_property_blob_put(vs_plane_state->roi); + kfree(vs_plane_state); +} + +static int vs_plane_atomic_set_property(struct drm_plane *plane, + struct drm_plane_state *state, + struct drm_property *property, + uint64_t val) +{ + struct drm_device *dev = plane->dev; + struct vs_plane *vs_plane = to_vs_plane(plane); + struct vs_plane_state *vs_plane_state = to_vs_plane_state(state); + int ret = 0; + + if (property == vs_plane->degamma_mode) { + if (vs_plane_state->degamma != val) { + vs_plane_state->degamma = val; + vs_plane_state->degamma_changed = true; + } else { + vs_plane_state->degamma_changed = false; + } + } else if (property == vs_plane->watermark_prop) { + ret = _vs_plane_set_property_blob_from_id(dev, + &vs_plane_state->watermark, + val, + sizeof(struct drm_vs_watermark)); + return ret; + } else if (property == vs_plane->color_mgmt_prop) { + ret = _vs_plane_set_property_blob_from_id(dev, + &vs_plane_state->color_mgmt, + val, + sizeof(struct drm_vs_color_mgmt)); + return ret; + } else if (property == vs_plane->roi_prop) { + ret = _vs_plane_set_property_blob_from_id(dev, + &vs_plane_state->roi, + val, + sizeof(struct drm_vs_roi)); + return ret; + } else { + return -EINVAL; + } + + return 0; +} + +static int vs_plane_atomic_get_property(struct drm_plane *plane, + const struct drm_plane_state *state, + struct drm_property *property, + uint64_t *val) +{ + struct vs_plane *vs_plane = to_vs_plane(plane); + const struct vs_plane_state *vs_plane_state = + container_of(state, const struct vs_plane_state, base); + + if (property == vs_plane->degamma_mode) + *val = vs_plane_state->degamma; + else if (property == vs_plane->watermark_prop) + *val = (vs_plane_state->watermark) ? + vs_plane_state->watermark->base.id : 0; + else if (property == vs_plane->color_mgmt_prop) + *val = (vs_plane_state->color_mgmt) ? + vs_plane_state->color_mgmt->base.id : 0; + else if (property == vs_plane->roi_prop) + *val = (vs_plane_state->roi) ? + vs_plane_state->roi->base.id : 0; + else + return -EINVAL; + + return 0; +} + +static bool vs_format_mod_supported(struct drm_plane *plane, + u32 format, + u64 modifier) +{ + int i; + + /* We always have to allow these modifiers: + * 1. Core DRM checks for LINEAR support if userspace does not provide modifiers. + * 2. Not passing any modifiers is the same as explicitly passing INVALID. + */ + if (modifier == DRM_FORMAT_MOD_LINEAR) + return true; + + /* Check that the modifier is on the list of the plane's supported modifiers. */ + for (i = 0; i < plane->modifier_count; i++) { + if (modifier == plane->modifiers[i]) + break; + } + + if (i == plane->modifier_count) + return false; + + return true; +} + +const struct drm_plane_funcs vs_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .reset = vs_plane_reset, + .atomic_duplicate_state = vs_plane_atomic_duplicate_state, + .atomic_destroy_state = vs_plane_atomic_destroy_state, + .atomic_set_property = vs_plane_atomic_set_property, + .atomic_get_property = vs_plane_atomic_get_property, + .format_mod_supported = vs_format_mod_supported, +}; + +static unsigned char vs_get_plane_number(struct drm_framebuffer *fb) +{ + const struct drm_format_info *info; + + if (!fb) + return 0; + + info = drm_format_info(fb->format->format); + if (!info || info->num_planes > DRM_FORMAT_MAX_PLANES) + return 0; + + return info->num_planes; +} + +static int vs_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, + plane); + 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); + + if (!crtc || !fb) + return 0; + + return vs_dc_check_plane(vs_crtc->dev, 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); + 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); + + if (!crtc || !fb) + return 0; + + return vs_dc_check_cursor_plane(vs_crtc->dev, 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); + 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); + + if (!new_state->fb || !new_state->crtc) + return; + + fb = new_state->fb; + + num_planes = vs_get_plane_number(fb); + + for (i = 0; i < num_planes; i++) { + struct vs_gem_object *vs_obj; + + vs_obj = vs_fb_get_gem_obj(fb, i); + vs_plane->dma_addr[i] = vs_obj->iova + fb->offsets[i]; + } + + plane_state->status.src = drm_plane_state_src(new_state); + plane_state->status.dest = drm_plane_state_dest(new_state); + + vs_dc_update_plane(vs_crtc->dev, 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); + 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); + + if (!new_state->fb || !new_state->crtc) + return; + + fb = new_state->fb; + + num_planes = vs_get_plane_number(fb); + + for (i = 0; i < num_planes; i++) { + struct vs_gem_object *vs_obj; + + vs_obj = vs_fb_get_gem_obj(fb, i); + vs_plane->dma_addr[i] = vs_obj->iova + fb->offsets[i]; + } + + plane_state->status.src = drm_plane_state_src(new_state); + plane_state->status.dest = drm_plane_state_dest(new_state); + + vs_dc_update_cursor_plane(vs_crtc->dev, 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); + + vs_dc_disable_plane(vs_crtc->dev, 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); + + vs_dc_disable_cursor_plane(vs_crtc->dev, vs_plane, old_state); +} + +const struct drm_plane_helper_funcs primary_plane_helpers = { + .atomic_check = vs_plane_atomic_check, + .atomic_update = vs_plane_atomic_update, + .atomic_disable = vs_plane_atomic_disable, +}; + +const struct drm_plane_helper_funcs overlay_plane_helpers = { + .atomic_check = vs_plane_atomic_check, + .atomic_update = vs_plane_atomic_update, + .atomic_disable = vs_plane_atomic_disable, +}; + +const struct drm_plane_helper_funcs cursor_plane_helpers = { + .atomic_check = vs_cursor_plane_atomic_check, + .atomic_update = vs_cursor_plane_atomic_update, + .atomic_disable = vs_cursor_plane_atomic_disable, +}; + +static const struct drm_prop_enum_list vs_degamma_mode_enum_list[] = { + { VS_DEGAMMA_DISABLE, "disabled" }, + { VS_DEGAMMA_BT709, "preset degamma for BT709" }, + { VS_DEGAMMA_BT2020, "preset degamma for BT2020" }, +}; + +struct vs_plane *vs_plane_create(struct drm_device *drm_dev, + struct vs_plane_info *info, + unsigned int layer_num, + unsigned int possible_crtcs) +{ + struct vs_plane *plane; + int ret; + + if (!info) + return NULL; + + plane = drmm_kzalloc(drm_dev, sizeof(*plane), GFP_KERNEL); + if (!plane) + 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 (!strcmp(info->name, "Primary") || + !strcmp(info->name, "Primary_1")) { + drm_plane_helper_add(&plane->base, &primary_plane_helpers); + } else if (!strcmp(info->name, "Cursor_1") || + !strcmp(info->name, "Cursor")) { + drm_plane_helper_add(&plane->base, &cursor_plane_helpers); + } else { + drm_plane_helper_add(&plane->base, &overlay_plane_helpers); + } + + /* Set up the plane properties */ + if (info->degamma_size) { + plane->degamma_mode = + drm_property_create_enum(drm_dev, 0, + "DEGAMMA_MODE", + vs_degamma_mode_enum_list, + ARRAY_SIZE(vs_degamma_mode_enum_list)); + + if (!plane->degamma_mode) + return NULL; + + drm_object_attach_property(&plane->base.base, + plane->degamma_mode, + VS_DEGAMMA_DISABLE); + } + + if (info->rotation) { + ret = drm_plane_create_rotation_property(&plane->base, + DRM_MODE_ROTATE_0, + info->rotation); + if (ret) + return NULL; + } + + if (info->blend_mode) { + ret = drm_plane_create_blend_mode_property(&plane->base, + info->blend_mode); + if (ret) + return NULL; + ret = drm_plane_create_alpha_property(&plane->base); + if (ret) + return NULL; + } + + if (info->color_encoding) { + ret = drm_plane_create_color_properties(&plane->base, + info->color_encoding, + BIT(DRM_COLOR_YCBCR_LIMITED_RANGE), + DRM_COLOR_YCBCR_BT709, + DRM_COLOR_YCBCR_LIMITED_RANGE); + if (ret) + return NULL; + } + + if (info->zpos != 255) { + ret = drm_plane_create_zpos_property(&plane->base, info->zpos, 0, + layer_num - 1); + if (ret) + return NULL; + } else { + ret = drm_plane_create_zpos_immutable_property(&plane->base, + info->zpos); + if (ret) + return NULL; + } + + if (info->watermark) { + plane->watermark_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB, + "WATERMARK", 0); + if (!plane->watermark_prop) + return NULL; + drm_object_attach_property(&plane->base.base, plane->watermark_prop, 0); + } + + if (info->color_mgmt) { + plane->color_mgmt_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB, + "COLOR_CONFIG", 0); + if (!plane->color_mgmt_prop) + return NULL; + + drm_object_attach_property(&plane->base.base, plane->color_mgmt_prop, 0); + } + + if (info->roi) { + plane->roi_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB, + "ROI", 0); + if (!plane->roi_prop) + return NULL; + + drm_object_attach_property(&plane->base.base, plane->roi_prop, 0); + } + + return plane; +} diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h new file mode 100644 index 000000000..d84b1abd9 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_plane.h @@ -0,0 +1,65 @@ +/* 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_modeset.h" +#include "vs_type.h" + +struct vs_plane; + +struct vs_plane_status { + u32 tile_mode; + struct drm_rect src; + struct drm_rect dest; +}; + +struct vs_plane_state { + struct drm_plane_state base; + struct vs_plane_status status; /* for debugfs */ + + struct drm_property_blob *watermark; + struct drm_property_blob *color_mgmt; + struct drm_property_blob *roi; + + u32 degamma; + bool degamma_changed; +}; + +struct vs_plane { + struct drm_plane base; + u8 id; + dma_addr_t dma_addr[DRM_FORMAT_MAX_PLANES]; + + struct drm_property *degamma_mode; + struct drm_property *watermark_prop; + struct drm_property *color_mgmt_prop; + struct drm_property *roi_prop; + + const struct vs_plane_funcs *funcs; +}; + +void vs_plane_destory(struct drm_plane *plane); + +struct vs_plane *vs_plane_create(struct drm_device *drm_dev, + struct vs_plane_info *info, + unsigned int layer_num, + unsigned int possible_crtcs); + +static inline struct vs_plane *to_vs_plane(struct drm_plane *plane) +{ + return container_of(plane, struct vs_plane, base); +} + +static inline struct vs_plane_state * +to_vs_plane_state(struct drm_plane_state *state) +{ + return container_of(state, struct vs_plane_state, base); +} +#endif /* __VS_PLANE_H__ */ diff --git a/drivers/gpu/drm/verisilicon/vs_type.h b/drivers/gpu/drm/verisilicon/vs_type.h new file mode 100644 index 000000000..80408cdb8 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_type.h @@ -0,0 +1,70 @@ +/* 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 mmu_prefetch; + bool background; + bool panel_sync; + bool cap_dec; +}; + +#endif /* __VS_TYPE_H__ */
Hi,
On Tue, Aug 01, 2023 at 06:10:29PM +0800, Keith Zhao wrote:
+static int vs_crtc_atomic_set_property(struct drm_crtc *crtc,
struct drm_crtc_state *state,
struct drm_property *property,
uint64_t val)
+{
- struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
- struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(state);
- if (property == vs_crtc->sync_mode)
vs_crtc_state->sync_mode = val;
- else if (property == vs_crtc->mmu_prefetch)
vs_crtc_state->mmu_prefetch = val;
- else if (property == vs_crtc->bg_color)
vs_crtc_state->bg_color = val;
- else if (property == vs_crtc->panel_sync)
vs_crtc_state->sync_enable = val;
- else if (property == vs_crtc->dither)
vs_crtc_state->dither_enable = val;
- else
return -EINVAL;
- return 0;
+}
+static int vs_crtc_atomic_get_property(struct drm_crtc *crtc,
const struct drm_crtc_state *state,
struct drm_property *property,
uint64_t *val)
+{
- struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
- const struct vs_crtc_state *vs_crtc_state =
container_of(state, const struct vs_crtc_state, base);
- if (property == vs_crtc->sync_mode)
*val = vs_crtc_state->sync_mode;
- else if (property == vs_crtc->mmu_prefetch)
*val = vs_crtc_state->mmu_prefetch;
- else if (property == vs_crtc->bg_color)
*val = vs_crtc_state->bg_color;
- else if (property == vs_crtc->panel_sync)
*val = vs_crtc_state->sync_enable;
- else if (property == vs_crtc->dither)
*val = vs_crtc_state->dither_enable;
- else
return -EINVAL;
- return 0;
+}
Any new property needs to follow these requirements: https://docs.kernel.org/gpu/drm-kms.html#requirements https://docs.kernel.org/gpu/drm-uapi.html#open-source-userspace-requirements
Also, most of them are suspicious, like sync_mode, mmu_prefetch, panel_sync or dither_enable. Why would you want userspace to change those ?
+static int vs_crtc_late_register(struct drm_crtc *crtc) +{
- return 0;
+}
You can drop that.
+static int vs_crtc_enable_vblank(struct drm_crtc *crtc) +{
- struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
- vs_dc_enable_vblank(vs_crtc->dev, true);
- return 0;
+}
+static void vs_crtc_disable_vblank(struct drm_crtc *crtc) +{
- struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
- vs_dc_enable_vblank(vs_crtc->dev, false);
+}
+static const struct drm_crtc_funcs vs_crtc_funcs = {
- .set_config = drm_atomic_helper_set_config,
- .page_flip = drm_atomic_helper_page_flip,
- .reset = vs_crtc_reset,
- .atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
- .atomic_destroy_state = vs_crtc_atomic_destroy_state,
- .atomic_set_property = vs_crtc_atomic_set_property,
- .atomic_get_property = vs_crtc_atomic_get_property,
- .late_register = vs_crtc_late_register,
- .enable_vblank = vs_crtc_enable_vblank,
- .disable_vblank = vs_crtc_disable_vblank,
+};
+static u8 cal_pixel_bits(u32 bus_format) +{
- u8 bpp;
- switch (bus_format) {
- case MEDIA_BUS_FMT_RGB565_1X16:
- case MEDIA_BUS_FMT_UYVY8_1X16:
bpp = 16;
break;
- case MEDIA_BUS_FMT_RGB666_1X18:
- case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
bpp = 18;
break;
- case MEDIA_BUS_FMT_UYVY10_1X20:
bpp = 20;
break;
- case MEDIA_BUS_FMT_BGR888_1X24:
- case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
- case MEDIA_BUS_FMT_YUV8_1X24:
bpp = 24;
break;
- case MEDIA_BUS_FMT_RGB101010_1X30:
- case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
- case MEDIA_BUS_FMT_YUV10_1X30:
bpp = 30;
break;
- default:
bpp = 24;
break;
- }
- return bpp;
+}
+static bool vs_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
+{
- struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
- return vs_dc_mode_fixup(vs_crtc->dev, mode, adjusted_mode);
+}
You should be using atomic_check.
Maxime
Hi
Am 01.08.23 um 12:10 schrieb Keith Zhao:
add 2 crtcs and 8 planes in vs-drm
Signed-off-by: Keith Zhao keith.zhao@starfivetech.com
drivers/gpu/drm/verisilicon/vs_crtc.c | 365 +++++ drivers/gpu/drm/verisilicon/vs_crtc.h | 54 + drivers/gpu/drm/verisilicon/vs_dc.c | 1036 ++++++++++++ drivers/gpu/drm/verisilicon/vs_dc.h | 87 + drivers/gpu/drm/verisilicon/vs_dc_hw.c | 2008 ++++++++++++++++++++++++ drivers/gpu/drm/verisilicon/vs_dc_hw.h | 496 ++++++ drivers/gpu/drm/verisilicon/vs_drv.c | 3 +- drivers/gpu/drm/verisilicon/vs_plane.c | 502 ++++++ drivers/gpu/drm/verisilicon/vs_plane.h | 65 + drivers/gpu/drm/verisilicon/vs_type.h | 70 + 10 files changed, 4685 insertions(+), 1 deletion(-) 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/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c new file mode 100644 index 000000000..6a5af8d8c --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
- */
+#include <linux/clk.h> +#include <linux/debugfs.h> +#include <linux/media-bus-format.h>
+#include <drm/drm_atomic_helper.h> +#include <drm/drm_atomic.h> +#include <drm/drm_crtc.h> +#include <drm/drm_gem_atomic_helper.h> +#include <drm/drm_vblank.h> +#include <drm/vs_drm.h>
+#include "vs_crtc.h" +#include "vs_dc.h" +#include "vs_drv.h"
+static void vs_crtc_reset(struct drm_crtc *crtc) +{
- struct vs_crtc_state *state;
- if (crtc->state) {
__drm_atomic_helper_crtc_destroy_state(crtc->state);
state = to_vs_crtc_state(crtc->state);
kfree(state);
crtc->state = NULL;
- }
- state = kzalloc(sizeof(*state), GFP_KERNEL);
- if (!state)
return;
- __drm_atomic_helper_crtc_reset(crtc, &state->base);
This sets the CRTC's state. Do this after you've fully initialized state.
- state->sync_mode = VS_SINGLE_DC;
- state->output_fmt = MEDIA_BUS_FMT_RBG888_1X24;
- state->encoder_type = DRM_MODE_ENCODER_NONE;
What is this? The code below that sets some registers depends on this. You should instead look at the CRTC's encoders to pick the correct one.
https://elixir.bootlin.com/linux/v6.5/source/include/drm/drm_crtc.h#L212
+}
+static struct drm_crtc_state * +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc) +{
- struct vs_crtc_state *ori_state;
- struct vs_crtc_state *state;
- if (!crtc->state)
return NULL;
- ori_state = to_vs_crtc_state(crtc->state);
- state = kzalloc(sizeof(*state), GFP_KERNEL);
- if (!state)
return NULL;
- __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
- state->sync_mode = ori_state->sync_mode;
- state->output_fmt = ori_state->output_fmt;
- state->encoder_type = ori_state->encoder_type;
- state->bg_color = ori_state->bg_color;
- state->bpp = ori_state->bpp;
- state->sync_enable = ori_state->sync_enable;
- state->dither_enable = ori_state->dither_enable;
- state->underflow = ori_state->underflow;
- return &state->base;
+}
+static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
struct drm_crtc_state *state)
+{
- __drm_atomic_helper_crtc_destroy_state(state);
- kfree(to_vs_crtc_state(state));
+}
+static int vs_crtc_atomic_set_property(struct drm_crtc *crtc,
struct drm_crtc_state *state,
struct drm_property *property,
uint64_t val)
+{
- struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
- struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(state);
- if (property == vs_crtc->sync_mode)
vs_crtc_state->sync_mode = val;
- else if (property == vs_crtc->mmu_prefetch)
vs_crtc_state->mmu_prefetch = val;
- else if (property == vs_crtc->bg_color)
vs_crtc_state->bg_color = val;
- else if (property == vs_crtc->panel_sync)
vs_crtc_state->sync_enable = val;
- else if (property == vs_crtc->dither)
vs_crtc_state->dither_enable = val;
- else
return -EINVAL;
- return 0;
+}
+static int vs_crtc_atomic_get_property(struct drm_crtc *crtc,
const struct drm_crtc_state *state,
struct drm_property *property,
uint64_t *val)
+{
- struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
- const struct vs_crtc_state *vs_crtc_state =
container_of(state, const struct vs_crtc_state, base);
- if (property == vs_crtc->sync_mode)
*val = vs_crtc_state->sync_mode;
- else if (property == vs_crtc->mmu_prefetch)
*val = vs_crtc_state->mmu_prefetch;
- else if (property == vs_crtc->bg_color)
*val = vs_crtc_state->bg_color;
- else if (property == vs_crtc->panel_sync)
*val = vs_crtc_state->sync_enable;
- else if (property == vs_crtc->dither)
*val = vs_crtc_state->dither_enable;
- else
return -EINVAL;
- return 0;
+}
+static int vs_crtc_late_register(struct drm_crtc *crtc) +{
- return 0;
+}
+static int vs_crtc_enable_vblank(struct drm_crtc *crtc) +{
- struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
- vs_dc_enable_vblank(vs_crtc->dev, true);
- return 0;
+}
+static void vs_crtc_disable_vblank(struct drm_crtc *crtc) +{
- struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
- vs_dc_enable_vblank(vs_crtc->dev, false);
+}
+static const struct drm_crtc_funcs vs_crtc_funcs = {
- .set_config = drm_atomic_helper_set_config,
- .page_flip = drm_atomic_helper_page_flip,
- .reset = vs_crtc_reset,
- .atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
- .atomic_destroy_state = vs_crtc_atomic_destroy_state,
- .atomic_set_property = vs_crtc_atomic_set_property,
- .atomic_get_property = vs_crtc_atomic_get_property,
- .late_register = vs_crtc_late_register,
- .enable_vblank = vs_crtc_enable_vblank,
- .disable_vblank = vs_crtc_disable_vblank,
+};
+static u8 cal_pixel_bits(u32 bus_format) +{
- u8 bpp;
- switch (bus_format) {
- case MEDIA_BUS_FMT_RGB565_1X16:
- case MEDIA_BUS_FMT_UYVY8_1X16:
bpp = 16;
break;
- case MEDIA_BUS_FMT_RGB666_1X18:
- case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
bpp = 18;
break;
- case MEDIA_BUS_FMT_UYVY10_1X20:
bpp = 20;
break;
- case MEDIA_BUS_FMT_BGR888_1X24:
- case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
- case MEDIA_BUS_FMT_YUV8_1X24:
bpp = 24;
break;
- case MEDIA_BUS_FMT_RGB101010_1X30:
- case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
- case MEDIA_BUS_FMT_YUV10_1X30:
bpp = 30;
break;
- default:
bpp = 24;
break;
- }
- return bpp;
+}
+static bool vs_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
+{
- struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
- return vs_dc_mode_fixup(vs_crtc->dev, mode, adjusted_mode);
+}
+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_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
- vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
- vs_dc_enable(vs_crtc->dev, 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);
- drm_crtc_vblank_off(crtc);
- vs_dc_disable(vs_crtc->dev, crtc);
- if (crtc->state->event && !crtc->state->active) {
spin_lock_irq(&crtc->dev->event_lock);
drm_crtc_send_vblank_event(crtc, crtc->state->event);
spin_unlock_irq(&crtc->dev->event_lock);
crtc->state->event = NULL;
- }
+}
+static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_atomic_state *state)
+{
- struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
crtc);
- struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
- struct device *dev = vs_crtc->dev;
- struct drm_property_blob *blob = crtc->state->gamma_lut;
- struct drm_color_lut *lut;
- if (crtc_state->color_mgmt_changed) {
if (blob && blob->length) {
lut = blob->data;
vs_dc_set_gamma(dev, crtc, lut,
blob->length / sizeof(*lut));
vs_dc_enable_gamma(dev, crtc, true);
} else {
vs_dc_enable_gamma(dev, 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;
- vs_dc_commit(vs_crtc->dev);
- if (event) {
WARN_ON(drm_crtc_vblank_get(crtc) != 0);
spin_lock_irq(&crtc->dev->event_lock);
drm_crtc_arm_vblank_event(crtc, event);
spin_unlock_irq(&crtc->dev->event_lock);
crtc->state->event = NULL;
- }
+}
+static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
- .mode_fixup = vs_crtc_mode_fixup,
- .atomic_enable = vs_crtc_atomic_enable,
- .atomic_disable = vs_crtc_atomic_disable,
- .atomic_begin = vs_crtc_atomic_begin,
- .atomic_flush = vs_crtc_atomic_flush,
+};
+static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
- { VS_SINGLE_DC, "single dc mode" },
- { VS_MULTI_DC_PRIMARY, "primary dc for multi dc mode" },
- { VS_MULTI_DC_SECONDARY, "secondary dc for multi dc mode" },
+};
+struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
struct vs_dc_info *info)
+{
- struct vs_crtc *crtc;
- int ret;
- if (!info)
return NULL;
- crtc = drmm_kzalloc(drm_dev, sizeof(*crtc), GFP_KERNEL);
- if (!crtc)
return NULL;
- ret = drmm_crtc_init_with_planes(drm_dev, &crtc->base,
NULL, NULL, &vs_crtc_funcs,
info->name ? info->name : NULL);
- if (ret)
return NULL;
- drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
- /* Set up the crtc properties */
- if (info->pipe_sync) {
crtc->sync_mode = drm_property_create_enum(drm_dev, 0,
"SYNC_MODE",
vs_sync_mode_enum_list,
ARRAY_SIZE(vs_sync_mode_enum_list));
if (!crtc->sync_mode)
return NULL;
drm_object_attach_property(&crtc->base.base,
crtc->sync_mode,
VS_SINGLE_DC);
- }
- 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);
- }
- if (info->background) {
crtc->bg_color = drm_property_create_range(drm_dev, 0,
"BG_COLOR", 0, 0xffffffff);
if (!crtc->bg_color)
return NULL;
drm_object_attach_property(&crtc->base.base, crtc->bg_color, 0);
- }
- if (info->panel_sync) {
crtc->panel_sync = drm_property_create_bool(drm_dev, 0, "SYNC_ENABLED");
if (!crtc->panel_sync)
return NULL;
drm_object_attach_property(&crtc->base.base, crtc->panel_sync, 0);
- }
- crtc->dither = drm_property_create_bool(drm_dev, 0, "DITHER_ENABLED");
- if (!crtc->dither)
return NULL;
- drm_object_attach_property(&crtc->base.base, crtc->dither, 0);
- crtc->max_bpc = info->max_bpc;
- crtc->color_formats = info->color_formats;
- return crtc;
+} diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h new file mode 100644 index 000000000..92fcbcfc1 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_crtc.h @@ -0,0 +1,54 @@ +/* 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 sync_mode;
- u32 output_fmt;
- u32 bg_color;
- u8 encoder_type;
- u8 mmu_prefetch;
- u8 bpp;
- bool sync_enable;
- bool dither_enable;
- bool underflow;
+};
+struct vs_crtc {
- struct drm_crtc base;
- struct device *dev;
- unsigned int max_bpc;
- unsigned int color_formats;
- struct drm_property *sync_mode;
- struct drm_property *mmu_prefetch;
- struct drm_property *bg_color;
- struct drm_property *panel_sync;
- struct drm_property *dither;
+};
+struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
struct vs_dc_info *info);
+static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc) +{
- return container_of(crtc, struct vs_crtc, base);
+}
+static inline struct vs_crtc_state * +to_vs_crtc_state(struct drm_crtc_state *state) +{
- return container_of(state, struct vs_crtc_state, base);
+} +#endif /* __VS_CRTC_H__ */ diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c new file mode 100644 index 000000000..44d9f0c18 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_dc.c @@ -0,0 +1,1036 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
- */
+#include <linux/component.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/media-bus-format.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/reset.h>
+#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_blend.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_vblank.h> +#include <drm/vs_drm.h>
+#include "vs_crtc.h" +#include "vs_dc_hw.h" +#include "vs_dc.h" +#include "vs_drv.h" +#include "vs_type.h"
+static const char * const vout_clocks[] = {
- "vout_noc_disp",
- "vout_pix0",
- "vout_pix1",
- "vout_axi",
- "vout_core",
- "vout_vout_ahb",
- "hdmitx0_pixel",
- "vout_dc8200",
+};
+static const char * const vout_resets[] = {
- "vout_axi",
- "vout_ahb",
- "vout_core",
+};
+static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb) +{
- u8 f = FORMAT_A8R8G8B8;
- switch (format) {
- case DRM_FORMAT_XRGB4444:
- case DRM_FORMAT_RGBX4444:
- case DRM_FORMAT_XBGR4444:
- case DRM_FORMAT_BGRX4444:
f = FORMAT_X4R4G4B4;
break;
- case DRM_FORMAT_ARGB4444:
- case DRM_FORMAT_RGBA4444:
- case DRM_FORMAT_ABGR4444:
- case DRM_FORMAT_BGRA4444:
f = FORMAT_A4R4G4B4;
break;
- case DRM_FORMAT_XRGB1555:
- case DRM_FORMAT_RGBX5551:
- case DRM_FORMAT_XBGR1555:
- case DRM_FORMAT_BGRX5551:
f = FORMAT_X1R5G5B5;
break;
- case DRM_FORMAT_ARGB1555:
- case DRM_FORMAT_RGBA5551:
- case DRM_FORMAT_ABGR1555:
- case DRM_FORMAT_BGRA5551:
f = FORMAT_A1R5G5B5;
break;
- case DRM_FORMAT_RGB565:
- case DRM_FORMAT_BGR565:
f = FORMAT_R5G6B5;
break;
- case DRM_FORMAT_XRGB8888:
- case DRM_FORMAT_RGBX8888:
- case DRM_FORMAT_XBGR8888:
- case DRM_FORMAT_BGRX8888:
f = FORMAT_X8R8G8B8;
break;
- case DRM_FORMAT_ARGB8888:
- case DRM_FORMAT_RGBA8888:
- case DRM_FORMAT_ABGR8888:
- case DRM_FORMAT_BGRA8888:
f = FORMAT_A8R8G8B8;
break;
- case DRM_FORMAT_YUYV:
- case DRM_FORMAT_YVYU:
f = FORMAT_YUY2;
break;
- case DRM_FORMAT_UYVY:
- case DRM_FORMAT_VYUY:
f = FORMAT_UYVY;
break;
- case DRM_FORMAT_YUV420:
- case DRM_FORMAT_YVU420:
f = FORMAT_YV12;
break;
- case DRM_FORMAT_NV21:
f = FORMAT_NV12;
break;
- case DRM_FORMAT_NV16:
- case DRM_FORMAT_NV61:
f = FORMAT_NV16;
break;
- case DRM_FORMAT_P010:
f = FORMAT_P010;
break;
- case DRM_FORMAT_ARGB2101010:
- case DRM_FORMAT_RGBA1010102:
- case DRM_FORMAT_ABGR2101010:
- case DRM_FORMAT_BGRA1010102:
f = FORMAT_A2R10G10B10;
break;
- case DRM_FORMAT_NV12:
if (fourcc_mod_vs_get_type(mod) ==
DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT)
f = FORMAT_NV12_10BIT;
else
f = FORMAT_NV12;
break;
- case DRM_FORMAT_YUV444:
if (fourcc_mod_vs_get_type(mod) ==
DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT)
f = FORMAT_YUV444_10BIT;
else
f = FORMAT_YUV444;
break;
- default:
break;
- }
- fb->format = f;
+}
+static inline void update_swizzle(u32 format, struct dc_hw_fb *fb) +{
- fb->swizzle = SWIZZLE_ARGB;
- fb->uv_swizzle = 0;
- switch (format) {
- case DRM_FORMAT_RGBX4444:
- case DRM_FORMAT_RGBA4444:
- case DRM_FORMAT_RGBX5551:
- case DRM_FORMAT_RGBA5551:
- case DRM_FORMAT_RGBX8888:
- case DRM_FORMAT_RGBA8888:
- case DRM_FORMAT_RGBA1010102:
fb->swizzle = SWIZZLE_RGBA;
break;
- case DRM_FORMAT_XBGR4444:
- case DRM_FORMAT_ABGR4444:
- case DRM_FORMAT_XBGR1555:
- case DRM_FORMAT_ABGR1555:
- case DRM_FORMAT_BGR565:
- case DRM_FORMAT_XBGR8888:
- case DRM_FORMAT_ABGR8888:
- case DRM_FORMAT_ABGR2101010:
fb->swizzle = SWIZZLE_ABGR;
break;
- case DRM_FORMAT_BGRX4444:
- case DRM_FORMAT_BGRA4444:
- case DRM_FORMAT_BGRX5551:
- case DRM_FORMAT_BGRA5551:
- case DRM_FORMAT_BGRX8888:
- case DRM_FORMAT_BGRA8888:
- case DRM_FORMAT_BGRA1010102:
fb->swizzle = SWIZZLE_BGRA;
break;
- case DRM_FORMAT_YVYU:
- case DRM_FORMAT_VYUY:
- case DRM_FORMAT_NV21:
- case DRM_FORMAT_NV61:
fb->uv_swizzle = 1;
break;
- default:
break;
- }
+}
+static inline void update_watermark(struct drm_property_blob *watermark,
struct dc_hw_fb *fb)
+{
- struct drm_vs_watermark *data;
- fb->water_mark = 0;
- if (watermark) {
data = watermark->data;
fb->water_mark = data->watermark & 0xFFFFF;
- }
+}
+static inline u8 to_vs_rotation(unsigned int rotation) +{
- u8 rot;
- switch (rotation & DRM_MODE_REFLECT_MASK) {
- case DRM_MODE_REFLECT_X:
rot = FLIP_X;
return rot;
- case DRM_MODE_REFLECT_Y:
rot = FLIP_Y;
return rot;
- case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
rot = FLIP_XY;
return rot;
- default:
break;
- }
- switch (rotation & DRM_MODE_ROTATE_MASK) {
- case DRM_MODE_ROTATE_0:
rot = ROT_0;
break;
- case DRM_MODE_ROTATE_90:
rot = ROT_90;
break;
- case DRM_MODE_ROTATE_180:
rot = ROT_180;
break;
- case DRM_MODE_ROTATE_270:
rot = ROT_270;
break;
- default:
rot = ROT_0;
break;
- }
- return rot;
+}
+static inline u8 to_vs_yuv_color_space(u32 color_space) +{
- u8 cs;
- switch (color_space) {
- case DRM_COLOR_YCBCR_BT601:
cs = COLOR_SPACE_601;
break;
- case DRM_COLOR_YCBCR_BT709:
cs = COLOR_SPACE_709;
break;
- case DRM_COLOR_YCBCR_BT2020:
cs = COLOR_SPACE_2020;
break;
- default:
cs = COLOR_SPACE_601;
break;
- }
- return cs;
+}
+static inline u8 to_vs_tile_mode(u64 modifier) +{
- return (u8)(modifier & DRM_FORMAT_MOD_VS_NORM_MODE_MASK);
+}
+static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc) +{
- u8 panel_num = dc->hw.info->panel_num;
- u32 index = drm_crtc_index(crtc);
- int i;
- for (i = 0; i < panel_num; i++) {
if (index == dc->crtc[i]->base.index)
return i;
- }
- return 0;
+}
+static int plda_clk_rst_init(struct device *dev) +{
- int ret = 0;
- struct vs_dc *dc = dev_get_drvdata(dev);
- ret = clk_bulk_prepare_enable(dc->nclks, dc->clk_vout);
- if (ret) {
dev_err(dev, "failed to enable clocks\n");
return ret;
- }
- ret = reset_control_bulk_deassert(dc->nrsts, dc->rst_vout);
- return ret;
+}
+static void plda_clk_rst_deinit(struct device *dev) +{
- struct vs_dc *dc = dev_get_drvdata(dev);
- reset_control_bulk_assert(dc->nrsts, dc->rst_vout);
- clk_bulk_disable_unprepare(dc->nclks, dc->clk_vout);
+}
+static void dc_deinit(struct device *dev) +{
- struct vs_dc *dc = dev_get_drvdata(dev);
- dc_hw_enable_interrupt(&dc->hw, 0);
- dc_hw_deinit(&dc->hw);
- plda_clk_rst_deinit(dev);
+}
+static int dc_init(struct device *dev) +{
- struct vs_dc *dc = dev_get_drvdata(dev);
- int ret;
- dc->first_frame = true;
- ret = plda_clk_rst_init(dev);
- if (ret < 0) {
dev_err(dev, "failed to init dc clk reset: %d\n", ret);
return ret;
- }
- ret = dc_hw_init(&dc->hw);
- if (ret) {
dev_err(dev, "failed to init DC HW\n");
return ret;
- }
- return 0;
+}
+void vs_dc_enable(struct device *dev, struct drm_crtc *crtc) +{
- struct vs_dc *dc = dev_get_drvdata(dev);
- 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.sync_mode = crtc_state->sync_mode;
- display.bg_color = crtc_state->bg_color;
- display.id = to_vs_display_id(dc, crtc);
- display.sync_enable = crtc_state->sync_enable;
- display.dither_enable = crtc_state->dither_enable;
- display.enable = true;
- if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) {
dc_hw_set_out(&dc->hw, OUT_DPI, display.id);
clk_set_rate(dc->clk_vout[CLK_VOUT_SOC_PIX].clk, mode->clock * 1000);
clk_set_parent(dc->clk_vout[CLK_VOUT_PIX1].clk,
dc->clk_vout[CLK_VOUT_SOC_PIX].clk);
- } else {
dc_hw_set_out(&dc->hw, OUT_DP, display.id);
clk_set_parent(dc->clk_vout[CLK_VOUT_PIX0].clk,
dc->clk_vout[CLK_VOUT_HDMI_PIX].clk);
- }
- dc_hw_setup_display(&dc->hw, &display);
+}
+void vs_dc_disable(struct device *dev, struct drm_crtc *crtc) +{
- struct vs_dc *dc = dev_get_drvdata(dev);
- struct dc_hw_display display;
- display.id = to_vs_display_id(dc, crtc);
- display.enable = false;
- dc_hw_setup_display(&dc->hw, &display);
+}
+bool vs_dc_mode_fixup(struct device *dev,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
+{
- return true;
+}
+void vs_dc_set_gamma(struct device *dev, struct drm_crtc *crtc,
struct drm_color_lut *lut, unsigned int size)
+{
- struct vs_dc *dc = dev_get_drvdata(dev);
- u16 i, r, g, b;
- u8 bits, id;
- if (size != dc->hw.info->gamma_size) {
dev_err(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 device *dev, struct drm_crtc *crtc,
bool enable)
+{
- struct vs_dc *dc = dev_get_drvdata(dev);
- u8 id;
- id = to_vs_display_id(dc, crtc);
- dc_hw_enable_gamma(&dc->hw, id, enable);
+}
+void vs_dc_enable_vblank(struct device *dev, bool enable) +{
- struct vs_dc *dc = dev_get_drvdata(dev);
- 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->dma_addr[0];
- fb->y_stride = drm_fb->pitches[0];
- if (drm_fb->format->format == DRM_FORMAT_YVU420) {
fb->u_address = plane->dma_addr[2];
fb->v_address = plane->dma_addr[1];
fb->u_stride = drm_fb->pitches[2];
fb->v_stride = drm_fb->pitches[1];
- } else {
fb->u_address = plane->dma_addr[1];
fb->v_address = plane->dma_addr[2];
fb->u_stride = drm_fb->pitches[1];
fb->v_stride = drm_fb->pitches[2];
- }
- fb->width = drm_rect_width(src) >> 16;
- fb->height = drm_rect_height(src) >> 16;
- fb->tile_mode = to_vs_tile_mode(drm_fb->modifier);
- fb->rotation = to_vs_rotation(state->rotation);
- fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding);
- fb->zpos = state->zpos;
- fb->enable = state->visible;
- update_format(drm_fb->format->format, drm_fb->modifier, fb);
- update_swizzle(drm_fb->format->format, fb);
- update_watermark(plane_state->watermark, fb);
- plane_state->status.tile_mode = fb->tile_mode;
+}
+static void update_degamma(struct vs_dc *dc, struct vs_plane *plane,
struct vs_plane_state *plane_state)
+{
- dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
- plane_state->degamma_changed = false;
+}
+static void update_roi(struct vs_dc *dc, u8 id,
struct vs_plane_state *plane_state,
struct dc_hw_roi *roi,
struct drm_plane_state *state)
+{
- struct drm_vs_roi *data;
- struct drm_rect *src = &state->src;
- u16 src_w = drm_rect_width(src) >> 16;
- u16 src_h = drm_rect_height(src) >> 16;
- if (plane_state->roi) {
data = plane_state->roi->data;
if (data->enable) {
roi->x = data->roi_x;
roi->y = data->roi_y;
roi->width = (data->roi_x + data->roi_w > src_w) ?
(src_w - data->roi_x) : data->roi_w;
roi->height = (data->roi_y + data->roi_h > src_h) ?
(src_h - data->roi_y) : data->roi_h;
roi->enable = true;
} else {
roi->enable = false;
}
dc_hw_update_roi(&dc->hw, id, roi);
- } else {
roi->enable = false;
- }
+}
+static void update_color_mgmt(struct vs_dc *dc, u8 id,
struct dc_hw_fb *fb,
struct vs_plane_state *plane_state)
+{
- struct drm_vs_color_mgmt *data;
- struct dc_hw_colorkey colorkey;
- if (plane_state->color_mgmt) {
data = plane_state->color_mgmt->data;
fb->clear_enable = data->clear_enable;
fb->clear_value = data->clear_value;
if (data->colorkey > data->colorkey_high)
data->colorkey = data->colorkey_high;
colorkey.colorkey = data->colorkey;
colorkey.colorkey_high = data->colorkey_high;
colorkey.transparency = (data->transparency) ?
DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE;
dc_hw_update_colorkey(&dc->hw, id, &colorkey);
- }
+}
+static void update_plane(struct vs_dc *dc, struct vs_plane *plane,
struct drm_plane *drm_plane,
struct drm_atomic_state *drm_state)
+{
- struct dc_hw_fb fb = {0};
- struct dc_hw_scale scale;
- struct dc_hw_position pos;
- struct dc_hw_blend blend;
- struct dc_hw_roi roi;
- struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
drm_plane);
- struct vs_plane_state *plane_state = to_vs_plane_state(state);
- struct drm_rect *dest = &state->dst;
- bool dec_enable = false;
- u8 display_id = 0;
- display_id = to_vs_display_id(dc, state->crtc);
- update_fb(plane, display_id, &fb, state);
- fb.dec_enable = dec_enable;
- update_roi(dc, plane->id, plane_state, &roi, state);
- update_scale(state, &roi, &scale);
- if (plane_state->degamma_changed)
update_degamma(dc, plane, plane_state);
- pos.start_x = dest->x1;
- pos.start_y = dest->y1;
- pos.end_x = dest->x2;
- pos.end_y = dest->y2;
- blend.alpha = (u8)(state->alpha >> 8);
- blend.blend_mode = (u8)(state->pixel_blend_mode);
- update_color_mgmt(dc, plane->id, &fb, plane_state);
- dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
+}
+static void update_qos(struct vs_dc *dc, struct vs_plane *plane,
struct drm_plane *drm_plane,
struct drm_atomic_state *drm_state)
+{
- struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
drm_plane);
- struct vs_plane_state *plane_state = to_vs_plane_state(state);
- struct drm_vs_watermark *data;
- struct dc_hw_qos qos;
- if (plane_state->watermark) {
data = plane_state->watermark->data;
if (data->qos_high) {
if (data->qos_low > data->qos_high)
data->qos_low = data->qos_high;
qos.low_value = data->qos_low & 0x0F;
qos.high_value = data->qos_high & 0x0F;
dc_hw_update_qos(&dc->hw, &qos);
}
- }
+}
+static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor) +{
- u8 size_type;
- switch (state->crtc_w) {
- case 32:
size_type = CURSOR_SIZE_32X32;
break;
- case 64:
size_type = CURSOR_SIZE_64X64;
break;
- default:
size_type = CURSOR_SIZE_32X32;
break;
- }
- cursor->size = size_type;
+}
+static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
struct drm_plane *drm_plane,
struct drm_atomic_state *drm_state)
+{
- struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
drm_plane);
- struct drm_framebuffer *drm_fb = state->fb;
- struct dc_hw_cursor cursor;
- cursor.address = plane->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 device *dev, struct vs_plane *plane,
struct drm_plane *drm_plane,
struct drm_atomic_state *drm_state)
+{
- struct vs_dc *dc = dev_get_drvdata(dev);
- update_plane(dc, plane, drm_plane, drm_state);
- update_qos(dc, plane, drm_plane, drm_state);
+}
+void vs_dc_update_cursor_plane(struct device *dev, struct vs_plane *plane,
struct drm_plane *drm_plane,
struct drm_atomic_state *drm_state)
+{
- struct vs_dc *dc = dev_get_drvdata(dev);
- update_cursor_plane(dc, plane, drm_plane, drm_state);
+}
+void vs_dc_disable_plane(struct device *dev, struct vs_plane *plane,
struct drm_plane_state *old_state)
+{
- struct vs_dc *dc = dev_get_drvdata(dev);
- 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 device *dev, struct vs_plane *plane,
struct drm_plane_state *old_state)
+{
- struct vs_dc *dc = dev_get_drvdata(dev);
- 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 device *dev, 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 vs_dc *dc = dev_get_drvdata(dev);
- 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)
dev_err_once(dev, "buffer size may not support on plane%d.\n",
vs_plane->id);
- if (!vs_dc_mod_supported(plane_info, fb->modifier)) {
dev_err(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 device *dev, 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 vs_dc *dc = dev_get_drvdata(dev);
- 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)
dev_err_once(dev, "buffer size may not support on plane%d.\n", vs_plane->id);
- crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
- if (IS_ERR(crtc_state))
return -EINVAL;
- return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
plane_info->min_scale,
plane_info->max_scale,
true, true);
+}
+static void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow) +{
- struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
- drm_crtc_handle_vblank(crtc);
- vs_crtc_state->underflow = underflow;
+}
+static irqreturn_t dc_isr(int irq, void *data) +{
- struct vs_dc *dc = data;
- struct vs_dc_info *dc_info = dc->hw.info;
- u32 i, ret;
- ret = dc_hw_get_interrupt(&dc->hw);
- for (i = 0; i < dc_info->panel_num; i++)
vs_crtc_handle_vblank(&dc->crtc[i]->base, dc_hw_check_underflow(&dc->hw));
- return IRQ_HANDLED;
+}
+void vs_dc_commit(struct device *dev)
All these many functions that operate on struct vs_dc should receive the pointer to it instead of a struct device. struct device is an entirely incorrect level of abstraction here.
+{
- struct vs_dc *dc = dev_get_drvdata(dev);
- dc_hw_enable_shadow_register(&dc->hw, false);
- dc_hw_commit(&dc->hw);
- if (dc->first_frame)
dc->first_frame = false;
- dc_hw_enable_shadow_register(&dc->hw, true);
+}
+static int dc_bind(struct device *dev, struct device *master, void *data) +{
- struct drm_device *drm_dev = data;
- struct vs_dc *dc = dev_get_drvdata(dev);
- struct device_node *port;
- struct vs_crtc *crtc;
- struct vs_dc_info *dc_info;
- struct vs_plane *plane;
- struct vs_plane_info *plane_info;
- int i, ret;
- u32 ctrc_mask = 0;
- if (!drm_dev || !dc) {
dev_err(dev, "devices are not created.\n");
return -ENODEV;
- }
- ret = dc_init(dev);
- if (ret < 0) {
dev_err(dev, "Failed to initialize DC hardware.\n");
return ret;
- }
- port = of_get_child_by_name(dev->of_node, "port");
- if (!port) {
dev_err(dev, "no port node found\n");
return -ENODEV;
- }
- of_node_put(port);
- dc_info = dc->hw.info;
- for (i = 0; i < dc_info->panel_num; i++) {
crtc = vs_crtc_create(drm_dev, dc_info);
if (!crtc) {
dev_err(dev, "Failed to create CRTC.\n");
ret = -ENOMEM;
return ret;
}
crtc->base.port = port;
crtc->dev = dev;
dc->crtc[i] = crtc;
ctrc_mask |= drm_crtc_mask(&crtc->base);
- }
- for (i = 0; i < dc_info->plane_num; i++) {
plane_info = (struct vs_plane_info *)&dc_info->planes[i];
if (!strcmp(plane_info->name, "Primary") || !strcmp(plane_info->name, "Cursor")) {
plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
drm_crtc_mask(&dc->crtc[0]->base));
} else if (!strcmp(plane_info->name, "Primary_1") ||
!strcmp(plane_info->name, "Cursor_1")) {
plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
drm_crtc_mask(&dc->crtc[1]->base));
} else {
plane = vs_plane_create(drm_dev, plane_info,
dc_info->layer_num, ctrc_mask);
}
if (IS_ERR(plane)) {
dev_err(dev, "failed to construct plane\n");
return PTR_ERR(plane);
}
plane->id = i;
dc->planes[i].id = plane_info->id;
if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
if (!strcmp(plane_info->name, "Primary"))
dc->crtc[0]->base.primary = &plane->base;
else
dc->crtc[1]->base.primary = &plane->base;
drm_dev->mode_config.min_width = plane_info->min_width;
drm_dev->mode_config.min_height =
plane_info->min_height;
drm_dev->mode_config.max_width = plane_info->max_width;
drm_dev->mode_config.max_height =
plane_info->max_height;
}
if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
if (!strcmp(plane_info->name, "Cursor"))
dc->crtc[0]->base.cursor = &plane->base;
else
dc->crtc[1]->base.cursor = &plane->base;
drm_dev->mode_config.cursor_width =
plane_info->max_width;
drm_dev->mode_config.cursor_height =
plane_info->max_height;
}
- }
- vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
- return 0;
+}
+static void dc_unbind(struct device *dev, struct device *master, void *data) +{
- dc_deinit(dev);
+}
+const struct component_ops dc_component_ops = {
- .bind = dc_bind,
- .unbind = dc_unbind,
+};
+static const struct of_device_id dc_driver_dt_match[] = {
- { .compatible = "starfive,jh7110-dc8200", },
- {},
+}; +MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
+static int dc_probe(struct platform_device *pdev) +{
- struct device *dev = &pdev->dev;
- struct vs_dc *dc;
- int irq, ret, i;
- dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
- if (!dc)
return -ENOMEM;
- dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(dc->hw.hi_base))
return PTR_ERR(dc->hw.hi_base);
- dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
- if (IS_ERR(dc->hw.reg_base))
return PTR_ERR(dc->hw.reg_base);
- dc->dss_reg = devm_platform_ioremap_resource(pdev, 2);
- if (IS_ERR(dc->dss_reg))
return PTR_ERR(dc->dss_reg);
- dc->nclks = ARRAY_SIZE(dc->clk_vout);
- for (i = 0; i < dc->nclks; ++i)
dc->clk_vout[i].id = vout_clocks[i];
- ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout);
- if (ret) {
dev_err(dev, "Failed to get clk controls\n");
return ret;
- }
- dc->nrsts = ARRAY_SIZE(dc->rst_vout);
- for (i = 0; i < dc->nrsts; ++i)
dc->rst_vout[i].id = vout_resets[i];
- ret = devm_reset_control_bulk_get_shared(dev, dc->nrsts,
dc->rst_vout);
- if (ret) {
dev_err(dev, "Failed to get reset controls\n");
return ret;
- }
- irq = platform_get_irq(pdev, 0);
- ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
- if (ret < 0) {
dev_err(dev, "Failed to install irq:%u.\n", irq);
return ret;
- }
- dev_set_drvdata(dev, dc);
- return component_add(dev, &dc_component_ops);
+}
+static int dc_remove(struct platform_device *pdev) +{
- struct device *dev = &pdev->dev;
- component_del(dev, &dc_component_ops);
- dev_set_drvdata(dev, NULL);
- return 0;
+}
+struct platform_driver dc_platform_driver = {
- .probe = dc_probe,
- .remove = dc_remove,
- .driver = {
.name = "vs-dc",
.of_match_table = of_match_ptr(dc_driver_dt_match),
- },
+};
+MODULE_AUTHOR("StarFive Corporation"); +MODULE_DESCRIPTION("VeriSilicon DC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h new file mode 100644 index 000000000..4d6b1d045 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_dc.h @@ -0,0 +1,87 @@ +/* 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 <linux/version.h>
+#include <drm/drm_fourcc.h> +#include <drm/drm_modes.h>
+#include "vs_crtc.h" +#include "vs_dc_hw.h" +#include "vs_plane.h"
+#define fourcc_mod_vs_get_type(val) \
(((val) & DRM_FORMAT_MOD_VS_TYPE_MASK) >> 54)
+struct vs_dc_plane {
- enum dc_hw_plane_id id;
+};
+enum vout_clk {
- CLK_VOUT_NOC_DISP = 0,
- CLK_VOUT_PIX0,
- CLK_VOUT_PIX1,
- CLK_VOUT_AXI,
- CLK_VOUT_CORE,
- CLK_VOUT_AHB,
- CLK_VOUT_HDMI_PIX,
- CLK_VOUT_SOC_PIX,
- CLK_VOUT_NUM
+};
+enum rst_vout {
- RST_VOUT_AXI = 0,
- RST_VOUT_AHB,
- RST_VOUT_CORE,
- RST_VOUT_NUM
+};
+struct vs_dc {
- struct vs_crtc *crtc[DC_DISPLAY_NUM];
- struct dc_hw hw;
- void __iomem *dss_reg;
- bool first_frame;
- struct vs_dc_plane planes[PLANE_NUM];
- struct clk_bulk_data clk_vout[CLK_VOUT_NUM];
- int nclks;
- struct reset_control_bulk_data rst_vout[RST_VOUT_NUM];
- int nrsts;
+};
+void vs_dc_enable(struct device *dev, struct drm_crtc *crtc); +void vs_dc_disable(struct device *dev, struct drm_crtc *crtc); +bool vs_dc_mode_fixup(struct device *dev,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
+void vs_dc_set_gamma(struct device *dev, struct drm_crtc *crtc,
struct drm_color_lut *lut, unsigned int size);
+void vs_dc_enable_gamma(struct device *dev, struct drm_crtc *crtc, bool enable); +void vs_dc_enable_vblank(struct device *dev, bool enable); +void vs_dc_commit(struct device *dev); +void vs_dc_update_plane(struct device *dev, struct vs_plane *plane,
struct drm_plane *drm_plane,
struct drm_atomic_state *drm_state);
+void vs_dc_disable_plane(struct device *dev, struct vs_plane *plane,
struct drm_plane_state *old_state);
+int vs_dc_check_plane(struct device *dev, struct drm_plane *plane,
struct drm_atomic_state *state);
+void vs_dc_update_cursor_plane(struct device *dev, struct vs_plane *plane,
struct drm_plane *drm_plane,
struct drm_atomic_state *drm_state);
+void vs_dc_disable_cursor_plane(struct device *dev, struct vs_plane *plane,
struct drm_plane_state *old_state);
+int vs_dc_check_cursor_plane(struct device *dev, struct drm_plane *plane,
struct drm_atomic_state *state);
+extern struct platform_driver dc_platform_driver;
+#endif /* __VS_DC_H__ */ diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c new file mode 100644 index 000000000..d370dd401 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c @@ -0,0 +1,2008 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
- */
+#include <linux/bits.h> +#include <linux/io.h> +#include <linux/media-bus-format.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_blend.h> +#include <drm/drm_fourcc.h> +#include <drm/vs_drm.h>
+#include "vs_dc_hw.h" +#include "vs_type.h"
+static const u32 horkernel[] = {
- 0x00000000, 0x20000000, 0x00002000, 0x00000000,
- 0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
- 0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
- 0x00000000, 0x00000000, 0x00000000, 0x2b981468,
- 0x00000000, 0x00000000, 0x00000000, 0x10f00000,
- 0x00002f10, 0x00000000, 0x00000000, 0x00000000,
- 0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
- 0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
- 0x00000000, 0x3781087f, 0x00000000, 0x00000000,
- 0x00000000, 0x06660000, 0x0000399a, 0x00000000,
- 0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
- 0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
- 0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
- 0x00000000, 0x00000000, 0x00000000, 0x01470000,
- 0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
- 0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
- 0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
- 0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
- 0x00000000, 0x00000000, 0x00004000, 0x00000000,
- 0x00000000, 0x00000000, 0x20002000, 0x00000000,
- 0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
- 0x00000000, 0x00000000, 0x00000000, 0x27e1181f,
- 0x00000000, 0x00000000, 0x00000000, 0x14680000,
- 0x00002b98, 0x00000000, 0x00000000, 0x00000000,
- 0x2f1010f0, 0x00000000, 0x00000000, 0x00000000,
- 0x0dc70000, 0x00003239, 0x00000000, 0x00000000,
- 0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
- 0x00000000, 0x087f0000, 0x00003781, 0x00000000,
- 0x00000000, 0x00000000, 0x399a0666, 0x00000000,
- 0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
- 0x00000000, 0x00000000, 0x00000000, 0x3cc4033c,
- 0x00000000, 0x00000000, 0x00000000, 0x021f0000,
+};
+#define H_COEF_SIZE (sizeof(horkernel) / sizeof(u32))
+static const u32 verkernel[] = {
- 0x00000000, 0x20000000, 0x00002000, 0x00000000,
- 0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
- 0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
- 0x00000000, 0x00000000, 0x00000000, 0x2b981468,
- 0x00000000, 0x00000000, 0x00000000, 0x10f00000,
- 0x00002f10, 0x00000000, 0x00000000, 0x00000000,
- 0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
- 0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
- 0x00000000, 0x3781087f, 0x00000000, 0x00000000,
- 0x00000000, 0x06660000, 0x0000399a, 0x00000000,
- 0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
- 0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
- 0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
- 0x00000000, 0x00000000, 0x00000000, 0x01470000,
- 0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
- 0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
- 0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
- 0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
- 0x00000000, 0x00000000, 0x00004000, 0x00000000,
- 0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab,
- 0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
- 0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee,
- 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
- 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
- 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
- 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
- 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
- 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
- 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
- 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
- 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
- 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+};
+#define V_COEF_SIZE (sizeof(verkernel) / sizeof(u32))
+/*
- RGB 709->2020 conversion parameters
- */
+static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = {
- 10279, 5395, 709,
- 1132, 15065, 187,
- 269, 1442, 14674
+};
+/*
- YUV601 to RGB conversion parameters
- YUV2RGB[0] - [8] : C0 - C8;
- YUV2RGB[9] - [11]: D0 - D2;
- YUV2RGB[12] - [13]: Y clamp min & max calue;
- YUV2RGB[14] - [15]: UV clamp min & max calue;
- */
+static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
- 1196, 0, 1640, 1196,
- -404, -836, 1196, 2076,
- 0, -916224, 558336, -1202944,
- 64, 940, 64, 960
+};
+/*
- YUV709 to RGB conversion parameters
- YUV2RGB[0] - [8] : C0 - C8;
- YUV2RGB[9] - [11]: D0 - D2;
- YUV2RGB[12] - [13]: Y clamp min & max calue;
- YUV2RGB[14] - [15]: UV clamp min & max calue;
- */
+static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
- 1196, 0, 1844, 1196,
- -220, -548, 1196, 2172,
- 0, -1020672, 316672, -1188608,
- 64, 940, 64, 960
+};
+/*
- YUV2020 to RGB conversion parameters
- YUV2RGB[0] - [8] : C0 - C8;
- YUV2RGB[9] - [11]: D0 - D2;
- YUV2RGB[12] - [13]: Y clamp min & max calue;
- YUV2RGB[14] - [15]: UV clamp min & max calue;
- */
+static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
- 1196, 0, 1724, 1196,
- -192, -668, 1196, 2200,
- 0, -959232, 363776, -1202944,
- 64, 940, 64, 960
+};
+/*
- RGB to YUV2020 conversion parameters
- RGB2YUV[0] - [8] : C0 - C8;
- RGB2YUV[9] - [11]: D0 - D2;
- */
+static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = {
- 230, 594, 52,
- -125, -323, 448,
- 448, -412, -36,
- 64, 512, 512
+};
+/*
- Degamma table for 709 color space data.
- */
+static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005,
- 0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024,
- 0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064,
- 0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce,
- 0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166,
- 0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230,
- 0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331,
- 0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c,
- 0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
- 0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d,
- 0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999,
- 0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb,
- 0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65,
- 0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a,
- 0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b,
- 0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc,
- 0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e,
- 0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
- 0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e,
- 0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf,
- 0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea,
- 0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f,
- 0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930,
- 0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f,
- 0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee,
- 0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde,
- 0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
- 0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9,
- 0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406,
- 0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a,
- 0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576,
- 0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd,
- 0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
+};
+/*
- Degamma table for 2020 color space data.
- */
+static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
- 0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
- 0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006,
- 0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a,
- 0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
- 0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024,
- 0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035,
- 0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d,
- 0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d,
- 0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098,
- 0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3,
- 0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123,
- 0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d,
- 0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
- 0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6,
- 0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1,
- 0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d,
- 0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6,
- 0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920,
- 0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a,
- 0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033,
- 0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593,
- 0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
- 0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656,
- 0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332,
- 0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480,
- 0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1,
- 0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99,
- 0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
+};
+/* one is for primary plane and the other is for all overlay planes */ +static const struct dc_hw_plane_reg dc_plane_reg[] = {
- {
- .y_address = DC_FRAMEBUFFER_ADDRESS,
- .u_address = DC_FRAMEBUFFER_U_ADDRESS,
- .v_address = DC_FRAMEBUFFER_V_ADDRESS,
- .y_stride = DC_FRAMEBUFFER_STRIDE,
- .u_stride = DC_FRAMEBUFFER_U_STRIDE,
- .v_stride = DC_FRAMEBUFFER_V_STRIDE,
- .size = DC_FRAMEBUFFER_SIZE,
- .top_left = DC_FRAMEBUFFER_TOP_LEFT,
- .bottom_right = DC_FRAMEBUFFER_BOTTOM_RIGHT,
- .scale_factor_x = DC_FRAMEBUFFER_SCALE_FACTOR_X,
- .scale_factor_y = DC_FRAMEBUFFER_SCALE_FACTOR_Y,
- .h_filter_coef_index = DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
- .h_filter_coef_data = DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
- .v_filter_coef_index = DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
- .v_filter_coef_data = DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
- .init_offset = DC_FRAMEBUFFER_INIT_OFFSET,
- .color_key = DC_FRAMEBUFFER_COLOR_KEY,
- .color_key_high = DC_FRAMEBUFFER_COLOR_KEY_HIGH,
- .clear_value = DC_FRAMEBUFFER_CLEAR_VALUE,
- .color_table_index = DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
- .color_table_data = DC_FRAMEBUFFER_COLOR_TABLE_DATA,
- .scale_config = DC_FRAMEBUFFER_SCALE_CONFIG,
- .water_mark = DC_FRAMEBUFFER_WATER_MARK,
- .degamma_index = DC_FRAMEBUFFER_DEGAMMA_INDEX,
- .degamma_data = DC_FRAMEBUFFER_DEGAMMA_DATA,
- .degamma_ex_data = DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
- .src_global_color = DC_FRAMEBUFFER_SRC_GLOBAL_COLOR,
- .dst_global_color = DC_FRAMEBUFFER_DST_GLOBAL_COLOR,
- .blend_config = DC_FRAMEBUFFER_BLEND_CONFIG,
- .roi_origin = DC_FRAMEBUFFER_ROI_ORIGIN,
- .roi_size = DC_FRAMEBUFFER_ROI_SIZE,
- .yuv_to_rgb_coef0 = DC_FRAMEBUFFER_YUVTORGB_COEF0,
- .yuv_to_rgb_coef1 = DC_FRAMEBUFFER_YUVTORGB_COEF1,
- .yuv_to_rgb_coef2 = DC_FRAMEBUFFER_YUVTORGB_COEF2,
- .yuv_to_rgb_coef3 = DC_FRAMEBUFFER_YUVTORGB_COEF3,
- .yuv_to_rgb_coef4 = DC_FRAMEBUFFER_YUVTORGB_COEF4,
- .yuv_to_rgb_coefd0 = DC_FRAMEBUFFER_YUVTORGB_COEFD0,
- .yuv_to_rgb_coefd1 = DC_FRAMEBUFFER_YUVTORGB_COEFD1,
- .yuv_to_rgb_coefd2 = DC_FRAMEBUFFER_YUVTORGB_COEFD2,
- .y_clamp_bound = DC_FRAMEBUFFER_Y_CLAMP_BOUND,
- .uv_clamp_bound = DC_FRAMEBUFFER_UV_CLAMP_BOUND,
- .rgb_to_rgb_coef0 = DC_FRAMEBUFFER_RGBTORGB_COEF0,
- .rgb_to_rgb_coef1 = DC_FRAMEBUFFER_RGBTORGB_COEF1,
- .rgb_to_rgb_coef2 = DC_FRAMEBUFFER_RGBTORGB_COEF2,
- .rgb_to_rgb_coef3 = DC_FRAMEBUFFER_RGBTORGB_COEF3,
- .rgb_to_rgb_coef4 = DC_FRAMEBUFFER_RGBTORGB_COEF4,
- },
- {
- .y_address = DC_OVERLAY_ADDRESS,
- .u_address = DC_OVERLAY_U_ADDRESS,
- .v_address = DC_OVERLAY_V_ADDRESS,
- .y_stride = DC_OVERLAY_STRIDE,
- .u_stride = DC_OVERLAY_U_STRIDE,
- .v_stride = DC_OVERLAY_V_STRIDE,
- .size = DC_OVERLAY_SIZE,
- .top_left = DC_OVERLAY_TOP_LEFT,
- .bottom_right = DC_OVERLAY_BOTTOM_RIGHT,
- .scale_factor_x = DC_OVERLAY_SCALE_FACTOR_X,
- .scale_factor_y = DC_OVERLAY_SCALE_FACTOR_Y,
- .h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
- .h_filter_coef_data = DC_OVERLAY_H_FILTER_COEF_DATA,
- .v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
- .v_filter_coef_data = DC_OVERLAY_V_FILTER_COEF_DATA,
- .init_offset = DC_OVERLAY_INIT_OFFSET,
- .color_key = DC_OVERLAY_COLOR_KEY,
- .color_key_high = DC_OVERLAY_COLOR_KEY_HIGH,
- .clear_value = DC_OVERLAY_CLEAR_VALUE,
- .color_table_index = DC_OVERLAY_COLOR_TABLE_INDEX,
- .color_table_data = DC_OVERLAY_COLOR_TABLE_DATA,
- .scale_config = DC_OVERLAY_SCALE_CONFIG,
- .water_mark = DC_OVERLAY_WATER_MARK,
- .degamma_index = DC_OVERLAY_DEGAMMA_INDEX,
- .degamma_data = DC_OVERLAY_DEGAMMA_DATA,
- .degamma_ex_data = DC_OVERLAY_DEGAMMA_EX_DATA,
- .src_global_color = DC_OVERLAY_SRC_GLOBAL_COLOR,
- .dst_global_color = DC_OVERLAY_DST_GLOBAL_COLOR,
- .blend_config = DC_OVERLAY_BLEND_CONFIG,
- .roi_origin = DC_OVERLAY_ROI_ORIGIN,
- .roi_size = DC_OVERLAY_ROI_SIZE,
- .yuv_to_rgb_coef0 = DC_OVERLAY_YUVTORGB_COEF0,
- .yuv_to_rgb_coef1 = DC_OVERLAY_YUVTORGB_COEF1,
- .yuv_to_rgb_coef2 = DC_OVERLAY_YUVTORGB_COEF2,
- .yuv_to_rgb_coef3 = DC_OVERLAY_YUVTORGB_COEF3,
- .yuv_to_rgb_coef4 = DC_OVERLAY_YUVTORGB_COEF4,
- .yuv_to_rgb_coefd0 = DC_OVERLAY_YUVTORGB_COEFD0,
- .yuv_to_rgb_coefd1 = DC_OVERLAY_YUVTORGB_COEFD1,
- .yuv_to_rgb_coefd2 = DC_OVERLAY_YUVTORGB_COEFD2,
- .y_clamp_bound = DC_OVERLAY_Y_CLAMP_BOUND,
- .uv_clamp_bound = DC_OVERLAY_UV_CLAMP_BOUND,
- .rgb_to_rgb_coef0 = DC_OVERLAY_RGBTORGB_COEF0,
- .rgb_to_rgb_coef1 = DC_OVERLAY_RGBTORGB_COEF1,
- .rgb_to_rgb_coef2 = DC_OVERLAY_RGBTORGB_COEF2,
- .rgb_to_rgb_coef3 = DC_OVERLAY_RGBTORGB_COEF3,
- .rgb_to_rgb_coef4 = DC_OVERLAY_RGBTORGB_COEF4,
- },
+};
+static const u32 primary_overlay_format0[] = {
- DRM_FORMAT_XRGB4444,
- DRM_FORMAT_XBGR4444,
- DRM_FORMAT_RGBX4444,
- DRM_FORMAT_BGRX4444,
- DRM_FORMAT_ARGB4444,
- DRM_FORMAT_ABGR4444,
- DRM_FORMAT_RGBA4444,
- DRM_FORMAT_BGRA4444,
- DRM_FORMAT_XRGB1555,
- DRM_FORMAT_XBGR1555,
- DRM_FORMAT_RGBX5551,
- DRM_FORMAT_BGRX5551,
- DRM_FORMAT_ARGB1555,
- DRM_FORMAT_ABGR1555,
- DRM_FORMAT_RGBA5551,
- DRM_FORMAT_BGRA5551,
- DRM_FORMAT_RGB565,
- DRM_FORMAT_BGR565,
- DRM_FORMAT_XRGB8888,
- DRM_FORMAT_XBGR8888,
- DRM_FORMAT_RGBX8888,
- DRM_FORMAT_BGRX8888,
- DRM_FORMAT_ARGB8888,
- DRM_FORMAT_ABGR8888,
- DRM_FORMAT_RGBA8888,
- DRM_FORMAT_BGRA8888,
- DRM_FORMAT_ARGB2101010,
- DRM_FORMAT_ABGR2101010,
- DRM_FORMAT_RGBA1010102,
- DRM_FORMAT_BGRA1010102,
- DRM_FORMAT_YUYV,
- DRM_FORMAT_YVYU,
- DRM_FORMAT_UYVY,
- DRM_FORMAT_VYUY,
- DRM_FORMAT_YVU420,
- DRM_FORMAT_YUV420,
- DRM_FORMAT_NV12,
- DRM_FORMAT_NV21,
- DRM_FORMAT_NV16,
- DRM_FORMAT_NV61,
- DRM_FORMAT_P010,
+};
+static const u32 primary_overlay_format1[] = {
- DRM_FORMAT_XRGB8888,
- DRM_FORMAT_XBGR8888,
- DRM_FORMAT_RGBX8888,
- DRM_FORMAT_BGRX8888,
- DRM_FORMAT_ARGB8888,
- DRM_FORMAT_ABGR8888,
- DRM_FORMAT_RGBA8888,
- DRM_FORMAT_BGRA8888,
- DRM_FORMAT_ARGB2101010,
- DRM_FORMAT_ABGR2101010,
- DRM_FORMAT_RGBA1010102,
- DRM_FORMAT_BGRA1010102,
- DRM_FORMAT_NV12,
- DRM_FORMAT_NV21,
- DRM_FORMAT_YUV444,
+};
+static const u32 cursor_formats[] = {
- DRM_FORMAT_ARGB8888
+};
+static const u64 format_modifier0[] = {
- DRM_FORMAT_MOD_LINEAR,
- fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_LINEAR),
- fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR),
- fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR),
- fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_TILE_8X8),
- fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_TILE_8X4),
- fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR_8X4),
- fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR_4X8),
- DRM_FORMAT_MOD_INVALID
+};
+static const u64 format_modifier1[] = {
- DRM_FORMAT_MOD_LINEAR,
- fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_LINEAR),
- fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR),
- fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR),
- fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_TILE_8X8),
- fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_TILE_8X4),
- fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR_8X4),
- fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR_4X8),
- fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_TILE_MODE4X4),
- fourcc_mod_vs_custom_code(DRM_FORMAT_MOD_VS_TILE_MODE4X4),
- DRM_FORMAT_MOD_INVALID
+};
+static const u64 secondary_format_modifiers[] = {
- DRM_FORMAT_MOD_LINEAR,
- DRM_FORMAT_MOD_INVALID
+};
+#define FRAC_16_16(mult, div) (((mult) << 16) / (div))
+static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = {
- {
/* DC_REV_0 */
{
.name = "Primary",
.id = PRIMARY_PLANE_0,
.type = DRM_PLANE_TYPE_PRIMARY,
.num_formats = ARRAY_SIZE(primary_overlay_format0),
.formats = primary_overlay_format0,
.num_modifiers = ARRAY_SIZE(format_modifier0),
.modifiers = format_modifier0,
.min_width = 0,
.min_height = 0,
.max_width = 4096,
.max_height = 4096,
.rotation = DRM_MODE_ROTATE_0 |
DRM_MODE_ROTATE_90 |
DRM_MODE_ROTATE_180 |
DRM_MODE_ROTATE_270 |
DRM_MODE_REFLECT_X |
DRM_MODE_REFLECT_Y,
.blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE),
.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
BIT(DRM_COLOR_YCBCR_BT2020),
.degamma_size = DEGAMMA_SIZE,
.min_scale = FRAC_16_16(1, 3),
.max_scale = FRAC_16_16(10, 1),
.zpos = 0,
.watermark = true,
.color_mgmt = true,
.roi = true,
},
{
.name = "Overlay",
.id = OVERLAY_PLANE_0,
.type = DRM_PLANE_TYPE_OVERLAY,
.num_formats = ARRAY_SIZE(primary_overlay_format0),
.formats = primary_overlay_format0,
.num_modifiers = ARRAY_SIZE(format_modifier0),
.modifiers = format_modifier0,
.min_width = 0,
.min_height = 0,
.max_width = 4096,
.max_height = 4096,
.rotation = DRM_MODE_ROTATE_0 |
DRM_MODE_ROTATE_90 |
DRM_MODE_ROTATE_180 |
DRM_MODE_ROTATE_270 |
DRM_MODE_REFLECT_X |
DRM_MODE_REFLECT_Y,
.blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE),
.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
BIT(DRM_COLOR_YCBCR_BT2020),
.degamma_size = DEGAMMA_SIZE,
.min_scale = FRAC_16_16(1, 3),
.max_scale = FRAC_16_16(10, 1),
.zpos = 1,
.watermark = true,
.color_mgmt = true,
.roi = true,
},
{
.name = "Overlay_1",
.id = OVERLAY_PLANE_1,
.type = DRM_PLANE_TYPE_OVERLAY,
.num_formats = ARRAY_SIZE(primary_overlay_format0),
.formats = primary_overlay_format0,
.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
.modifiers = secondary_format_modifiers,
.min_width = 0,
.min_height = 0,
.max_width = 4096,
.max_height = 4096,
.rotation = 0,
.blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE),
.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
BIT(DRM_COLOR_YCBCR_BT2020),
.degamma_size = DEGAMMA_SIZE,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = DRM_PLANE_NO_SCALING,
.zpos = 2,
.watermark = true,
.color_mgmt = true,
.roi = true,
},
{
.name = "Primary_1",
.id = PRIMARY_PLANE_1,
.type = DRM_PLANE_TYPE_PRIMARY,
.num_formats = ARRAY_SIZE(primary_overlay_format0),
.formats = primary_overlay_format0,
.num_modifiers = ARRAY_SIZE(format_modifier0),
.modifiers = format_modifier0,
.min_width = 0,
.min_height = 0,
.max_width = 4096,
.max_height = 4096,
.rotation = DRM_MODE_ROTATE_0 |
DRM_MODE_ROTATE_90 |
DRM_MODE_ROTATE_180 |
DRM_MODE_ROTATE_270 |
DRM_MODE_REFLECT_X |
DRM_MODE_REFLECT_Y,
.blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE),
.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
BIT(DRM_COLOR_YCBCR_BT2020),
.degamma_size = DEGAMMA_SIZE,
.min_scale = FRAC_16_16(1, 3),
.max_scale = FRAC_16_16(10, 1),
.zpos = 3,
.watermark = true,
.color_mgmt = true,
.roi = true,
},
{
.name = "Overlay_2",
.id = OVERLAY_PLANE_2,
.type = DRM_PLANE_TYPE_OVERLAY,
.num_formats = ARRAY_SIZE(primary_overlay_format0),
.formats = primary_overlay_format0,
.num_modifiers = ARRAY_SIZE(format_modifier0),
.modifiers = format_modifier0,
.min_width = 0,
.min_height = 0,
.max_width = 4096,
.max_height = 4096,
.rotation = DRM_MODE_ROTATE_0 |
DRM_MODE_ROTATE_90 |
DRM_MODE_ROTATE_180 |
DRM_MODE_ROTATE_270 |
DRM_MODE_REFLECT_X |
DRM_MODE_REFLECT_Y,
.blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE),
.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
BIT(DRM_COLOR_YCBCR_BT2020),
.degamma_size = DEGAMMA_SIZE,
.min_scale = FRAC_16_16(1, 3),
.max_scale = FRAC_16_16(10, 1),
.zpos = 4,
.watermark = true,
.color_mgmt = true,
.roi = true,
},
{
.name = "Overlay_3",
.id = OVERLAY_PLANE_3,
.type = DRM_PLANE_TYPE_OVERLAY,
.num_formats = ARRAY_SIZE(primary_overlay_format0),
.formats = primary_overlay_format0,
.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
.modifiers = secondary_format_modifiers,
.min_width = 0,
.min_height = 0,
.max_width = 4096,
.max_height = 4096,
.rotation = 0,
.blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE),
.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
BIT(DRM_COLOR_YCBCR_BT2020),
.degamma_size = DEGAMMA_SIZE,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = DRM_PLANE_NO_SCALING,
.zpos = 5,
.watermark = true,
.color_mgmt = true,
.roi = true,
},
{
.name = "Cursor",
.id = CURSOR_PLANE_0,
.type = DRM_PLANE_TYPE_CURSOR,
.num_formats = ARRAY_SIZE(cursor_formats),
.formats = cursor_formats,
.num_modifiers = 0,
.modifiers = NULL,
.min_width = 32,
.min_height = 32,
.max_width = 64,
.max_height = 64,
.rotation = 0,
.degamma_size = 0,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = DRM_PLANE_NO_SCALING,
.zpos = 255,
.watermark = false,
.color_mgmt = false,
.roi = false,
},
{
.name = "Cursor_1",
.id = CURSOR_PLANE_1,
.type = DRM_PLANE_TYPE_CURSOR,
.num_formats = ARRAY_SIZE(cursor_formats),
.formats = cursor_formats,
.num_modifiers = 0,
.modifiers = NULL,
.min_width = 32,
.min_height = 32,
.max_width = 64,
.max_height = 64,
.rotation = 0,
.degamma_size = 0,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = DRM_PLANE_NO_SCALING,
.zpos = 255,
.watermark = false,
.color_mgmt = false,
.roi = false,
},
- },
- {
/* DC_REV_1 */
{
.name = "Primary",
.id = PRIMARY_PLANE_0,
.type = DRM_PLANE_TYPE_PRIMARY,
.num_formats = ARRAY_SIZE(primary_overlay_format0),
.formats = primary_overlay_format0,
.num_modifiers = ARRAY_SIZE(format_modifier0),
.modifiers = format_modifier0,
.min_width = 0,
.min_height = 0,
.max_width = 4096,
.max_height = 4096,
.rotation = DRM_MODE_ROTATE_0 |
DRM_MODE_ROTATE_90 |
DRM_MODE_ROTATE_180 |
DRM_MODE_ROTATE_270 |
DRM_MODE_REFLECT_X |
DRM_MODE_REFLECT_Y,
.blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE),
.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
BIT(DRM_COLOR_YCBCR_BT2020),
.degamma_size = DEGAMMA_SIZE,
.min_scale = FRAC_16_16(1, 3),
.max_scale = FRAC_16_16(10, 1),
.zpos = 0,
.watermark = true,
.color_mgmt = true,
.roi = true,
},
{
.name = "Overlay",
.id = OVERLAY_PLANE_0,
.type = DRM_PLANE_TYPE_OVERLAY,
.num_formats = ARRAY_SIZE(primary_overlay_format0),
.formats = primary_overlay_format0,
.num_modifiers = ARRAY_SIZE(format_modifier0),
.modifiers = format_modifier0,
.min_width = 0,
.min_height = 0,
.max_width = 4096,
.max_height = 4096,
.rotation = DRM_MODE_ROTATE_0 |
DRM_MODE_ROTATE_90 |
DRM_MODE_ROTATE_180 |
DRM_MODE_ROTATE_270 |
DRM_MODE_REFLECT_X |
DRM_MODE_REFLECT_Y,
.blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE),
.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
BIT(DRM_COLOR_YCBCR_BT2020),
.degamma_size = DEGAMMA_SIZE,
.min_scale = FRAC_16_16(1, 3),
.max_scale = FRAC_16_16(10, 1),
.zpos = 1,
.watermark = true,
.color_mgmt = true,
.roi = true,
},
{
.name = "Primary_1",
.id = PRIMARY_PLANE_1,
.type = DRM_PLANE_TYPE_PRIMARY,
.num_formats = ARRAY_SIZE(primary_overlay_format0),
.formats = primary_overlay_format0,
.num_modifiers = ARRAY_SIZE(format_modifier0),
.modifiers = format_modifier0,
.min_width = 0,
.min_height = 0,
.max_width = 4096,
.max_height = 4096,
.rotation = DRM_MODE_ROTATE_0 |
DRM_MODE_ROTATE_90 |
DRM_MODE_ROTATE_180 |
DRM_MODE_ROTATE_270 |
DRM_MODE_REFLECT_X |
DRM_MODE_REFLECT_Y,
.blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE),
.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
BIT(DRM_COLOR_YCBCR_BT2020),
.degamma_size = DEGAMMA_SIZE,
.min_scale = FRAC_16_16(1, 3),
.max_scale = FRAC_16_16(10, 1),
.zpos = 2,
.watermark = true,
.color_mgmt = true,
.roi = true,
},
{
.name = "Overlay_2",
.id = OVERLAY_PLANE_2,
.type = DRM_PLANE_TYPE_OVERLAY,
.num_formats = ARRAY_SIZE(primary_overlay_format0),
.formats = primary_overlay_format0,
.num_modifiers = ARRAY_SIZE(format_modifier0),
.modifiers = format_modifier0,
.min_width = 0,
.min_height = 0,
.max_width = 4096,
.max_height = 4096,
.rotation = DRM_MODE_ROTATE_0 |
DRM_MODE_ROTATE_90 |
DRM_MODE_ROTATE_180 |
DRM_MODE_ROTATE_270 |
DRM_MODE_REFLECT_X |
DRM_MODE_REFLECT_Y,
.blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE),
.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
BIT(DRM_COLOR_YCBCR_BT2020),
.degamma_size = DEGAMMA_SIZE,
.min_scale = FRAC_16_16(1, 3),
.max_scale = FRAC_16_16(10, 1),
.zpos = 3,
.watermark = true,
.color_mgmt = true,
.roi = true,
},
{
.name = "Cursor",
.id = CURSOR_PLANE_0,
.type = DRM_PLANE_TYPE_CURSOR,
.num_formats = ARRAY_SIZE(cursor_formats),
.formats = cursor_formats,
.num_modifiers = 0,
.modifiers = NULL,
.min_width = 32,
.min_height = 32,
.max_width = 64,
.max_height = 64,
.rotation = 0,
.degamma_size = 0,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = DRM_PLANE_NO_SCALING,
.zpos = 255,
.watermark = false,
.color_mgmt = false,
.roi = false,
},
{
.name = "Cursor_1",
.id = CURSOR_PLANE_1,
.type = DRM_PLANE_TYPE_CURSOR,
.num_formats = ARRAY_SIZE(cursor_formats),
.formats = cursor_formats,
.num_modifiers = 0,
.modifiers = NULL,
.min_width = 32,
.min_height = 32,
.max_width = 64,
.max_height = 64,
.rotation = 0,
.degamma_size = 0,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = DRM_PLANE_NO_SCALING,
.zpos = 255,
.watermark = false,
.color_mgmt = false,
.roi = false,
},
- },
- {
/* DC_REV_2 */
{
.name = "Primary",
.id = PRIMARY_PLANE_0,
.type = DRM_PLANE_TYPE_PRIMARY,
.num_formats = ARRAY_SIZE(primary_overlay_format1),
.formats = primary_overlay_format1,
.num_modifiers = ARRAY_SIZE(format_modifier1),
.modifiers = format_modifier1,
.min_width = 0,
.min_height = 0,
.max_width = 4096,
.max_height = 4096,
.rotation = DRM_MODE_ROTATE_0 |
DRM_MODE_ROTATE_90 |
DRM_MODE_ROTATE_180 |
DRM_MODE_ROTATE_270 |
DRM_MODE_REFLECT_X |
DRM_MODE_REFLECT_Y,
.blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE),
.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
BIT(DRM_COLOR_YCBCR_BT2020),
.degamma_size = DEGAMMA_SIZE,
.min_scale = FRAC_16_16(1, 3),
.max_scale = FRAC_16_16(10, 1),
.zpos = 0,
.watermark = true,
.color_mgmt = true,
.roi = true,
},
{
.name = "Overlay",
.id = OVERLAY_PLANE_0,
.type = DRM_PLANE_TYPE_OVERLAY,
.num_formats = ARRAY_SIZE(primary_overlay_format1),
.formats = primary_overlay_format1,
.num_modifiers = ARRAY_SIZE(format_modifier1),
.modifiers = format_modifier1,
.min_width = 0,
.min_height = 0,
.max_width = 4096,
.max_height = 4096,
.rotation = DRM_MODE_ROTATE_0 |
DRM_MODE_ROTATE_90 |
DRM_MODE_ROTATE_180 |
DRM_MODE_ROTATE_270 |
DRM_MODE_REFLECT_X |
DRM_MODE_REFLECT_Y,
.blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE),
.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
BIT(DRM_COLOR_YCBCR_BT2020),
.degamma_size = DEGAMMA_SIZE,
.min_scale = FRAC_16_16(1, 3),
.max_scale = FRAC_16_16(10, 1),
.zpos = 1,
.watermark = true,
.color_mgmt = true,
.roi = true,
},
{
.name = "Overlay_1",
.id = OVERLAY_PLANE_1,
.type = DRM_PLANE_TYPE_OVERLAY,
.num_formats = ARRAY_SIZE(primary_overlay_format1),
.formats = primary_overlay_format1,
.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
.modifiers = secondary_format_modifiers,
.min_width = 0,
.min_height = 0,
.max_width = 4096,
.max_height = 4096,
.rotation = 0,
.blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE),
.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
BIT(DRM_COLOR_YCBCR_BT2020),
.degamma_size = DEGAMMA_SIZE,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = DRM_PLANE_NO_SCALING,
.zpos = 2,
.watermark = true,
.color_mgmt = true,
.roi = true,
},
{
.name = "Primary_1",
.id = PRIMARY_PLANE_1,
.type = DRM_PLANE_TYPE_PRIMARY,
.num_formats = ARRAY_SIZE(primary_overlay_format1),
.formats = primary_overlay_format1,
.num_modifiers = ARRAY_SIZE(format_modifier1),
.modifiers = format_modifier1,
.min_width = 0,
.min_height = 0,
.max_width = 4096,
.max_height = 4096,
.rotation = DRM_MODE_ROTATE_0 |
DRM_MODE_ROTATE_90 |
DRM_MODE_ROTATE_180 |
DRM_MODE_ROTATE_270 |
DRM_MODE_REFLECT_X |
DRM_MODE_REFLECT_Y,
.blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE),
.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
BIT(DRM_COLOR_YCBCR_BT2020),
.degamma_size = DEGAMMA_SIZE,
.min_scale = FRAC_16_16(1, 3),
.max_scale = FRAC_16_16(10, 1),
.zpos = 3,
.watermark = true,
.color_mgmt = true,
.roi = true,
},
{
.name = "Overlay_2",
.id = OVERLAY_PLANE_2,
.type = DRM_PLANE_TYPE_OVERLAY,
.num_formats = ARRAY_SIZE(primary_overlay_format1),
.formats = primary_overlay_format1,
.num_modifiers = ARRAY_SIZE(format_modifier1),
.modifiers = format_modifier1,
.min_width = 0,
.min_height = 0,
.max_width = 4096,
.max_height = 4096,
.rotation = DRM_MODE_ROTATE_0 |
DRM_MODE_ROTATE_90 |
DRM_MODE_ROTATE_180 |
DRM_MODE_ROTATE_270 |
DRM_MODE_REFLECT_X |
DRM_MODE_REFLECT_Y,
.blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE),
.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
BIT(DRM_COLOR_YCBCR_BT2020),
.degamma_size = DEGAMMA_SIZE,
.min_scale = FRAC_16_16(1, 3),
.max_scale = FRAC_16_16(10, 1),
.zpos = 4,
.watermark = true,
.color_mgmt = true,
.roi = true,
},
{
.name = "Overlay_3",
.id = OVERLAY_PLANE_3,
.type = DRM_PLANE_TYPE_OVERLAY,
.num_formats = ARRAY_SIZE(primary_overlay_format1),
.formats = primary_overlay_format1,
.num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
.modifiers = secondary_format_modifiers,
.min_width = 0,
.min_height = 0,
.max_width = 4096,
.max_height = 4096,
.rotation = 0,
.blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE),
.color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
BIT(DRM_COLOR_YCBCR_BT2020),
.degamma_size = DEGAMMA_SIZE,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = DRM_PLANE_NO_SCALING,
.zpos = 5,
.watermark = true,
.color_mgmt = true,
.roi = true,
},
{
.name = "Cursor",
.id = CURSOR_PLANE_0,
.type = DRM_PLANE_TYPE_CURSOR,
.num_formats = ARRAY_SIZE(cursor_formats),
.formats = cursor_formats,
.num_modifiers = 0,
.modifiers = NULL,
.min_width = 32,
.min_height = 32,
.max_width = 64,
.max_height = 64,
.rotation = 0,
.degamma_size = 0,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = DRM_PLANE_NO_SCALING,
.zpos = 255,
.watermark = false,
.color_mgmt = false,
.roi = false,
},
{
.name = "Cursor_1",
.id = CURSOR_PLANE_1,
.type = DRM_PLANE_TYPE_CURSOR,
.num_formats = ARRAY_SIZE(cursor_formats),
.formats = cursor_formats,
.num_modifiers = 0,
.modifiers = NULL,
.min_width = 32,
.min_height = 32,
.max_width = 64,
.max_height = 64,
.rotation = 0,
.degamma_size = 0,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = DRM_PLANE_NO_SCALING,
.zpos = 255,
.watermark = false,
.color_mgmt = false,
.roi = false,
},
- },
+};
+static const struct vs_dc_info dc_info[] = {
- {
/* DC_REV_0 */
.name = "DC8200",
.panel_num = 2,
.plane_num = 8,
.planes = dc_hw_planes[DC_REV_0],
.layer_num = 6,
.max_bpc = 10,
.color_formats = DRM_COLOR_FORMAT_RGB444 |
DRM_COLOR_FORMAT_YCBCR444 |
DRM_COLOR_FORMAT_YCBCR422 |
DRM_COLOR_FORMAT_YCBCR420,
.gamma_size = GAMMA_EX_SIZE,
.gamma_bits = 12,
.pitch_alignment = 128,
.pipe_sync = false,
.mmu_prefetch = false,
.background = true,
.panel_sync = true,
.cap_dec = true,
- },
- {
/* DC_REV_1 */
.name = "DC8200",
.panel_num = 2,
.plane_num = 6,
.planes = dc_hw_planes[DC_REV_1],
.layer_num = 4,
.max_bpc = 10,
.color_formats = DRM_COLOR_FORMAT_RGB444 |
DRM_COLOR_FORMAT_YCBCR444 |
DRM_COLOR_FORMAT_YCBCR422 |
DRM_COLOR_FORMAT_YCBCR420,
.gamma_size = GAMMA_EX_SIZE,
.gamma_bits = 12,
.pitch_alignment = 128,
.pipe_sync = false,
.mmu_prefetch = false,
.background = true,
.panel_sync = true,
.cap_dec = true,
- },
- {
/* DC_REV_2 */
.name = "DC8200",
.panel_num = 2,
.plane_num = 8,
.planes = dc_hw_planes[DC_REV_2],
.layer_num = 6,
.max_bpc = 10,
.color_formats = DRM_COLOR_FORMAT_RGB444 |
DRM_COLOR_FORMAT_YCBCR444 |
DRM_COLOR_FORMAT_YCBCR422 |
DRM_COLOR_FORMAT_YCBCR420,
.gamma_size = GAMMA_EX_SIZE,
.gamma_bits = 12,
.pitch_alignment = 128,
.pipe_sync = false,
.mmu_prefetch = false,
.background = true,
.panel_sync = true,
.cap_dec = false,
- },
+};
+static const struct dc_hw_funcs hw_func;
+static inline u32 hi_read(struct dc_hw *hw, u32 reg) +{
- return readl(hw->hi_base + reg);
+}
+static inline void hi_write(struct dc_hw *hw, u32 reg, u32 value) +{
- writel(value, hw->hi_base + reg);
+}
+static inline void dc_write(struct dc_hw *hw, u32 reg, u32 value) +{
- writel(value, hw->reg_base + reg - DC_REG_BASE);
+}
+static inline u32 dc_read(struct dc_hw *hw, u32 reg) +{
- u32 value = readl(hw->reg_base + reg - DC_REG_BASE);
- return value;
+}
+static inline void dc_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear) +{
- u32 value = dc_read(hw, reg);
- value &= ~clear;
- value |= set;
- dc_write(hw, reg, value);
+}
+static void load_default_filter(struct dc_hw *hw,
const struct dc_hw_plane_reg *reg, u32 offset)
+{
- u8 i;
- dc_write(hw, reg->scale_config + offset, 0x33);
- dc_write(hw, reg->init_offset + offset, 0x80008000);
- dc_write(hw, reg->h_filter_coef_index + offset, 0x00);
- for (i = 0; i < H_COEF_SIZE; i++)
dc_write(hw, reg->h_filter_coef_data + offset, horkernel[i]);
- dc_write(hw, reg->v_filter_coef_index + offset, 0x00);
- for (i = 0; i < V_COEF_SIZE; i++)
dc_write(hw, reg->v_filter_coef_data + offset, verkernel[i]);
+}
+static void load_rgb_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
u32 offset, u16 *table)
+{
- dc_write(hw, reg->rgb_to_rgb_coef0 + offset, table[0] | (table[1] << 16));
- dc_write(hw, reg->rgb_to_rgb_coef1 + offset, table[2] | (table[3] << 16));
- dc_write(hw, reg->rgb_to_rgb_coef2 + offset, table[4] | (table[5] << 16));
- dc_write(hw, reg->rgb_to_rgb_coef3 + offset, table[6] | (table[7] << 16));
- dc_write(hw, reg->rgb_to_rgb_coef4 + offset, table[8]);
+}
+static void load_yuv_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
u32 offset, s32 *table)
+{
- dc_write(hw, reg->yuv_to_rgb_coef0 + offset,
(0xFFFF & table[0]) | (table[1] << 16));
- dc_write(hw, reg->yuv_to_rgb_coef1 + offset,
(0xFFFF & table[2]) | (table[3] << 16));
- dc_write(hw, reg->yuv_to_rgb_coef2 + offset,
(0xFFFF & table[4]) | (table[5] << 16));
- dc_write(hw, reg->yuv_to_rgb_coef3 + offset,
(0xFFFF & table[6]) | (table[7] << 16));
- dc_write(hw, reg->yuv_to_rgb_coef4 + offset, table[8]);
- dc_write(hw, reg->yuv_to_rgb_coefd0 + offset, table[9]);
- dc_write(hw, reg->yuv_to_rgb_coefd1 + offset, table[10]);
- dc_write(hw, reg->yuv_to_rgb_coefd2 + offset, table[11]);
- dc_write(hw, reg->y_clamp_bound + offset, table[12] | (table[13] << 16));
- dc_write(hw, reg->uv_clamp_bound + offset, table[14] | (table[15] << 16));
+}
+static void load_rgb_to_yuv(struct dc_hw *hw, u32 offset, s16 *table) +{
- dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF0 + offset,
table[0] | (table[1] << 16));
- dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF1 + offset,
table[2] | (table[3] << 16));
- dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF2 + offset,
table[4] | (table[5] << 16));
- dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF3 + offset,
table[6] | (table[7] << 16));
- dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF4 + offset, table[8]);
- dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD0 + offset, table[9]);
- dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD1 + offset, table[10]);
- dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD2 + offset, table[11]);
+}
+static bool is_rgb(enum dc_hw_color_format format) +{
- switch (format) {
- case FORMAT_X4R4G4B4:
- case FORMAT_A4R4G4B4:
- case FORMAT_X1R5G5B5:
- case FORMAT_A1R5G5B5:
- case FORMAT_R5G6B5:
- case FORMAT_X8R8G8B8:
- case FORMAT_A8R8G8B8:
- case FORMAT_A2R10G10B10:
return true;
- default:
return false;
- }
+}
+static void load_degamma_table(struct dc_hw *hw,
const struct dc_hw_plane_reg *reg,
u32 offset, u16 *table)
+{
- u16 i;
- u32 value;
- dc_write(hw, reg->degamma_index + offset, 0);
- for (i = 0; i < DEGAMMA_SIZE; i++) {
value = table[i] | (table[i] << 16);
dc_write(hw, reg->degamma_data + offset, value);
dc_write(hw, reg->degamma_ex_data + offset, table[i]);
- }
+}
+static u32 get_addr_offset(u32 id) +{
- u32 offset = 0;
- switch (id) {
- case PRIMARY_PLANE_1:
- case OVERLAY_PLANE_1:
offset = 0x04;
break;
- case OVERLAY_PLANE_2:
offset = 0x08;
break;
- case OVERLAY_PLANE_3:
offset = 0x0C;
break;
- default:
break;
- }
- return offset;
+}
+int dc_hw_init(struct dc_hw *hw) +{
- u8 i, id, panel_num, layer_num;
- u32 offset;
- u32 revision = hi_read(hw, DC_HW_REVISION);
- u32 cid = hi_read(hw, DC_HW_CHIP_CID);
- const struct dc_hw_plane_reg *reg;
- switch (revision) {
- case 0x5720:
hw->rev = DC_REV_0;
break;
- case 0x5721:
switch (cid) {
case 0x30B:
hw->rev = DC_REV_1;
break;
case 0x310:
hw->rev = DC_REV_2;
break;
default:
hw->rev = DC_REV_0;
break;
}
break;
- default:
return -ENXIO;
- }
- hw->info = (struct vs_dc_info *)&dc_info[hw->rev];
- hw->func = (struct dc_hw_funcs *)&hw_func;
- layer_num = hw->info->layer_num;
- for (i = 0; i < layer_num; i++) {
id = hw->info->planes[i].id;
offset = get_addr_offset(id);
if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
reg = &dc_plane_reg[0];
else
reg = &dc_plane_reg[1];
load_default_filter(hw, reg, offset);
load_rgb_to_rgb(hw, reg, offset, RGB2RGB);
- }
- panel_num = hw->info->panel_num;
- for (i = 0; i < panel_num; i++) {
offset = i << 2;
load_rgb_to_yuv(hw, offset, RGB2YUV);
dc_write(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0x111);
offset = i ? DC_CURSOR_OFFSET : 0;
dc_write(hw, DC_CURSOR_BACKGROUND + offset, 0x00FFFFFF);
dc_write(hw, DC_CURSOR_FOREGROUND + offset, 0x00AAAAAA);
- }
- return 0;
+}
+void dc_hw_deinit(struct dc_hw *hw) +{
- /* Nothing to do */
+}
+void dc_hw_update_plane(struct dc_hw *hw, u8 id,
struct dc_hw_fb *fb, struct dc_hw_scale *scale,
struct dc_hw_position *pos, struct dc_hw_blend *blend)
+{
- struct dc_hw_plane *plane = &hw->plane[id];
- if (plane) {
if (fb) {
if (!fb->enable)
plane->fb.enable = false;
else
memcpy(&plane->fb, fb,
sizeof(*fb) - sizeof(fb->dirty));
plane->fb.dirty = true;
}
if (scale) {
memcpy(&plane->scale, scale,
sizeof(*scale) - sizeof(scale->dirty));
plane->scale.dirty = true;
}
if (pos) {
memcpy(&plane->pos, pos,
sizeof(*pos) - sizeof(pos->dirty));
plane->pos.dirty = true;
}
if (blend) {
memcpy(&plane->blend, blend,
sizeof(*blend) - sizeof(blend->dirty));
plane->blend.dirty = true;
}
- }
+}
+void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode) +{
- struct dc_hw_plane *plane = &hw->plane[id];
- if (plane) {
if (hw->info->planes[id].degamma_size) {
plane->degamma.mode = mode;
plane->degamma.dirty = true;
} else {
plane->degamma.dirty = false;
}
- }
+}
+void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi) +{
- struct dc_hw_plane *plane = &hw->plane[id];
- if (plane) {
memcpy(&plane->roi, roi, sizeof(*roi) - sizeof(roi->dirty));
plane->roi.dirty = true;
- }
+}
+void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
struct dc_hw_colorkey *colorkey)
+{
- struct dc_hw_plane *plane = &hw->plane[id];
- if (plane) {
memcpy(&plane->colorkey, colorkey,
sizeof(*colorkey) - sizeof(colorkey->dirty));
plane->colorkey.dirty = true;
- }
+}
+void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos) +{
- memcpy(&hw->qos, qos, sizeof(*qos) - sizeof(qos->dirty));
- hw->qos.dirty = true;
+}
+void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor) +{
- memcpy(&hw->cursor[id], cursor, sizeof(*cursor) - sizeof(cursor->dirty));
- hw->cursor[id].dirty = true;
+}
+void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
u16 r, u16 g, u16 b)
+{
- if (index >= hw->info->gamma_size)
return;
- hw->gamma[id].gamma[index][0] = r;
- hw->gamma[id].gamma[index][1] = g;
- hw->gamma[id].gamma[index][2] = b;
- hw->gamma[id].dirty = true;
+}
+void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable) +{
- hw->gamma[id].enable = enable;
- hw->gamma[id].dirty = true;
+}
+void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display) +{
- u8 id = display->id;
- memcpy(&hw->display[id], display, sizeof(*display));
- hw->func->display(hw, display);
+}
+void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable) +{
- if (enable)
hi_write(hw, AQ_INTR_ENBL, 0xFFFFFFFF);
- else
hi_write(hw, AQ_INTR_ENBL, 0);
+}
+u32 dc_hw_get_interrupt(struct dc_hw *hw) +{
- return hi_read(hw, AQ_INTR_ACKNOWLEDGE);
+}
+bool dc_hw_check_underflow(struct dc_hw *hw) +{
- return dc_read(hw, DC_FRAMEBUFFER_CONFIG) & BIT(5);
+}
+void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable) +{
- u32 i, offset;
- u8 id, layer_num = hw->info->layer_num;
- u8 panel_num = hw->info->panel_num;
- for (i = 0; i < layer_num; i++) {
id = hw->info->planes[i].id;
offset = get_addr_offset(id);
if (enable) {
if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, BIT(12), 0);
else
dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, BIT(31), 0);
} else {
if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, 0, BIT(12));
else
dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, 0, BIT(31));
}
- }
- for (i = 0; i < panel_num; i++) {
offset = i << 2;
if (enable)
dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, BIT(0));
else
dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, BIT(0), 0);
- }
+}
+void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id) +{
- if (out <= OUT_DP)
hw->out[id] = out;
+}
+static void gamma_ex_commit(struct dc_hw *hw) +{
- u8 panel_num = hw->info->panel_num;
- u16 i, j;
- u32 value;
- for (j = 0; j < panel_num; j++) {
if (hw->gamma[j].dirty) {
if (hw->gamma[j].enable) {
dc_write(hw, DC_DISPLAY_GAMMA_EX_INDEX + (j << 2), 0x00);
for (i = 0; i < GAMMA_EX_SIZE; i++) {
value = hw->gamma[j].gamma[i][2] |
(hw->gamma[j].gamma[i][1] << 12);
dc_write(hw, DC_DISPLAY_GAMMA_EX_DATA + (j << 2), value);
dc_write(hw, DC_DISPLAY_GAMMA_EX_ONE_DATA + (j << 2),
hw->gamma[j].gamma[i][0]);
}
dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
BIT(13), 0);
} else {
dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
0, BIT(13));
}
hw->gamma[j].dirty = false;
}
- }
+}
+static void plane_commit(struct dc_hw *hw) +{
- struct dc_hw_plane *plane;
- const struct dc_hw_plane_reg *reg;
- bool primary = false;
- u8 id, layer_num = hw->info->layer_num;
- u32 i, offset;
- for (i = 0; i < layer_num; i++) {
plane = &hw->plane[i];
id = hw->info->planes[i].id;
offset = get_addr_offset(id);
if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
reg = &dc_plane_reg[0];
primary = true;
} else {
reg = &dc_plane_reg[1];
primary = false;
}
if (plane->fb.dirty) {
if (plane->fb.enable) {
dc_write(hw, reg->y_address + offset,
plane->fb.y_address);
dc_write(hw, reg->u_address + offset,
plane->fb.u_address);
dc_write(hw, reg->v_address + offset,
plane->fb.v_address);
dc_write(hw, reg->y_stride + offset,
plane->fb.y_stride);
dc_write(hw, reg->u_stride + offset,
plane->fb.u_stride);
dc_write(hw, reg->v_stride + offset,
plane->fb.v_stride);
dc_write(hw, reg->size + offset,
plane->fb.width |
(plane->fb.height << 15));
dc_write(hw, reg->water_mark + offset,
plane->fb.water_mark);
if (plane->fb.clear_enable)
dc_write(hw, reg->clear_value + offset,
plane->fb.clear_value);
}
if (primary) {
dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
(plane->fb.format << 26) |
(plane->fb.uv_swizzle << 25) |
(plane->fb.swizzle << 23) |
(plane->fb.tile_mode << 17) |
(plane->fb.yuv_color_space << 14) |
(plane->fb.rotation << 11) |
(plane->fb.clear_enable << 8),
(0x1F << 26) |
BIT(25) |
(0x03 << 23) |
(0x1F << 17) |
(0x07 << 14) |
(0x07 << 11) |
BIT(8));
dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
(plane->fb.dec_enable << 1) |
(plane->fb.enable << 13) |
(plane->fb.zpos << 16) |
(plane->fb.display_id << 19),
BIT(1) | BIT(13) | (0x07 << 16) | BIT(19));
} else {
dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
(plane->fb.dec_enable << 27) |
(plane->fb.clear_enable << 25) |
(plane->fb.enable << 24) |
(plane->fb.format << 16) |
(plane->fb.uv_swizzle << 15) |
(plane->fb.swizzle << 13) |
(plane->fb.tile_mode << 8) |
(plane->fb.yuv_color_space << 5) |
(plane->fb.rotation << 2),
BIT(27) |
BIT(25) |
BIT(24) |
(0x1F << 16) |
BIT(15) |
(0x03 << 13) |
(0x1F << 8) |
(0x07 << 5) |
(0x07 << 2));
dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset,
plane->fb.zpos | (plane->fb.display_id << 3),
0x07 | BIT(3));
}
plane->fb.dirty = false;
}
if (plane->scale.dirty) {
if (plane->scale.enable) {
dc_write(hw, reg->scale_factor_x + offset,
plane->scale.scale_factor_x);
dc_write(hw, reg->scale_factor_y + offset,
plane->scale.scale_factor_y);
if (primary)
dc_set_clear(hw,
DC_FRAMEBUFFER_CONFIG + offset,
BIT(22), 0);
else
dc_set_clear(hw,
DC_OVERLAY_SCALE_CONFIG + offset,
BIT(8), 0);
} else {
if (primary)
dc_set_clear(hw,
DC_FRAMEBUFFER_CONFIG + offset,
0, BIT(22));
else
dc_set_clear(hw,
DC_OVERLAY_SCALE_CONFIG + offset,
0, BIT(8));
}
plane->scale.dirty = false;
}
if (plane->pos.dirty) {
dc_write(hw, reg->top_left + offset,
plane->pos.start_x |
(plane->pos.start_y << 15));
dc_write(hw, reg->bottom_right + offset,
plane->pos.end_x |
(plane->pos.end_y << 15));
plane->pos.dirty = false;
}
if (plane->blend.dirty) {
dc_write(hw, reg->src_global_color + offset,
plane->blend.alpha << 24);
dc_write(hw, reg->dst_global_color + offset,
plane->blend.alpha << 24);
switch (plane->blend.blend_mode) {
case BLEND_PREMULTI:
dc_write(hw, reg->blend_config + offset, 0x3450);
break;
case BLEND_COVERAGE:
dc_write(hw, reg->blend_config + offset, 0x3950);
break;
case BLEND_PIXEL_NONE:
dc_write(hw, reg->blend_config + offset, 0x3548);
break;
default:
break;
}
plane->blend.dirty = false;
}
if (plane->colorkey.dirty) {
dc_write(hw, reg->color_key + offset, plane->colorkey.colorkey);
dc_write(hw, reg->color_key_high + offset,
plane->colorkey.colorkey_high);
if (primary)
dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
plane->colorkey.transparency << 9, 0x03 << 9);
else
dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
plane->colorkey.transparency, 0x03);
plane->colorkey.dirty = false;
}
if (plane->roi.dirty) {
if (plane->roi.enable) {
dc_write(hw, reg->roi_origin + offset,
plane->roi.x | (plane->roi.y << 16));
dc_write(hw, reg->roi_size + offset,
plane->roi.width | (plane->roi.height << 16));
if (primary)
dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
BIT(0), 0);
else
dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
BIT(22), 0);
} else {
if (primary)
dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
0, BIT(0));
else
dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
0, BIT(22));
}
plane->roi.dirty = false;
}
- }
+}
+static void plane_ex_commit(struct dc_hw *hw) +{
- struct dc_hw_plane *plane;
- const struct dc_hw_plane_reg *reg;
- bool primary = false;
- u8 id, layer_num = hw->info->layer_num;
- u32 i, offset;
- for (i = 0; i < layer_num; i++) {
plane = &hw->plane[i];
id = hw->info->planes[i].id;
offset = get_addr_offset(id);
if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
reg = &dc_plane_reg[0];
primary = true;
} else {
reg = &dc_plane_reg[1];
primary = false;
}
if (plane->fb.dirty) {
if (is_rgb(plane->fb.format)) {
if (primary)
dc_set_clear(hw,
DC_FRAMEBUFFER_CONFIG_EX + offset,
BIT(6), BIT(8));
else
dc_set_clear(hw,
DC_OVERLAY_CONFIG + offset,
BIT(29), BIT(30));
} else {
if (primary)
dc_set_clear(hw,
DC_FRAMEBUFFER_CONFIG_EX + offset,
BIT(8), BIT(6));
else
dc_set_clear(hw,
DC_OVERLAY_CONFIG + offset,
BIT(30), BIT(29));
switch (plane->fb.yuv_color_space) {
case COLOR_SPACE_601:
load_yuv_to_rgb(hw, reg, offset, YUV601_2RGB);
break;
case COLOR_SPACE_709:
load_yuv_to_rgb(hw, reg, offset, YUV709_2RGB);
break;
case COLOR_SPACE_2020:
load_yuv_to_rgb(hw, reg, offset, YUV2020_2RGB);
break;
default:
break;
}
}
}
if (plane->degamma.dirty) {
switch (plane->degamma.mode) {
case VS_DEGAMMA_DISABLE:
if (primary)
dc_set_clear(hw,
DC_FRAMEBUFFER_CONFIG_EX + offset,
0, BIT(5));
else
dc_set_clear(hw,
DC_OVERLAY_CONFIG + offset,
0, BIT(28));
break;
case VS_DEGAMMA_BT709:
load_degamma_table(hw, reg, offset, DEGAMMA_709);
if (primary)
dc_set_clear(hw,
DC_FRAMEBUFFER_CONFIG_EX + offset,
BIT(5), 0);
else
dc_set_clear(hw,
DC_OVERLAY_CONFIG + offset,
BIT(28), 0);
break;
case VS_DEGAMMA_BT2020:
load_degamma_table(hw, reg, offset, DEGAMMA_2020);
if (primary)
dc_set_clear(hw,
DC_FRAMEBUFFER_CONFIG_EX + offset,
BIT(5), 0);
else
dc_set_clear(hw,
DC_OVERLAY_CONFIG + offset,
BIT(28), 0);
break;
default:
break;
}
plane->degamma.dirty = false;
}
- }
- plane_commit(hw);
+}
+static void setup_display(struct dc_hw *hw, struct dc_hw_display *display) +{
- u8 id = display->id;
- u32 dpi_cfg, offset = id << 2;
- if (hw->display[id].enable) {
switch (display->bus_format) {
case MEDIA_BUS_FMT_RGB565_1X16:
dpi_cfg = 0;
break;
case MEDIA_BUS_FMT_RGB666_1X18:
dpi_cfg = 3;
break;
case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
dpi_cfg = 4;
break;
case MEDIA_BUS_FMT_RGB888_1X24:
dpi_cfg = 5;
break;
case MEDIA_BUS_FMT_RGB101010_1X30:
dpi_cfg = 6;
break;
default:
dpi_cfg = 5;
break;
}
dc_write(hw, DC_DISPLAY_DPI_CONFIG + offset, dpi_cfg);
if (id == 0)
dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
else
dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
dc_write(hw, DC_DISPLAY_H + offset, hw->display[id].h_active |
(hw->display[id].h_total << 16));
dc_write(hw, DC_DISPLAY_H_SYNC + offset,
hw->display[id].h_sync_start |
(hw->display[id].h_sync_end << 15) |
(hw->display[id].h_sync_polarity ? 0 : BIT(31)) |
BIT(30));
dc_write(hw, DC_DISPLAY_V + offset, hw->display[id].v_active |
(hw->display[id].v_total << 16));
dc_write(hw, DC_DISPLAY_V_SYNC + offset,
hw->display[id].v_sync_start |
(hw->display[id].v_sync_end << 15) |
(hw->display[id].v_sync_polarity ? 0 : BIT(31)) |
BIT(30));
if (hw->info->pipe_sync) {
switch (display->sync_mode) {
case VS_SINGLE_DC:
dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
0, BIT(3) | BIT(4));
break;
case VS_MULTI_DC_PRIMARY:
dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
BIT(3) | BIT(4), 0);
break;
case VS_MULTI_DC_SECONDARY:
dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
BIT(3), BIT(4));
break;
default:
break;
}
}
if (hw->info->background)
dc_write(hw, DC_FRAMEBUFFER_BG_COLOR + offset,
hw->display[id].bg_color);
if (hw->display[id].dither_enable) {
dc_write(hw, DC_DISPLAY_DITHER_TABLE_LOW + offset,
DC_DISPLAY_DITHERTABLE_LOW);
dc_write(hw, DC_DISPLAY_DITHER_TABLE_HIGH + offset,
DC_DISPLAY_DITHERTABLE_HIGH);
dc_write(hw, DC_DISPLAY_DITHER_CONFIG + offset, BIT(31));
} else {
dc_write(hw, DC_DISPLAY_DITHER_CONFIG + offset, 0);
}
dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(12), 0);
if (hw->display[id].sync_enable)
dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(2) | BIT(3), 0);
else if (id == 0)
dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(0), BIT(3));
else
dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(1), BIT(3));
- } else {
dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(12));
if (id == 0)
dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
else
dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
- }
+}
+static void setup_display_ex(struct dc_hw *hw, struct dc_hw_display *display) +{
- u8 id = display->id;
- u32 dp_cfg, offset = id << 2;
- bool is_yuv = false;
- if (hw->display[id].enable && hw->out[id] == OUT_DP) {
switch (display->bus_format) {
case MEDIA_BUS_FMT_RGB565_1X16:
dp_cfg = 0;
break;
case MEDIA_BUS_FMT_RGB666_1X18:
dp_cfg = 1;
break;
case MEDIA_BUS_FMT_RGB888_1X24:
dp_cfg = 2;
break;
case MEDIA_BUS_FMT_RGB101010_1X30:
dp_cfg = 3;
break;
case MEDIA_BUS_FMT_UYVY8_1X16:
dp_cfg = 2 << 4;
is_yuv = true;
break;
case MEDIA_BUS_FMT_YUV8_1X24:
dp_cfg = 4 << 4;
is_yuv = true;
break;
case MEDIA_BUS_FMT_UYVY10_1X20:
dp_cfg = 8 << 4;
is_yuv = true;
break;
case MEDIA_BUS_FMT_YUV10_1X30:
dp_cfg = 10 << 4;
is_yuv = true;
break;
case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
dp_cfg = 12 << 4;
is_yuv = true;
break;
case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
dp_cfg = 13 << 4;
is_yuv = true;
break;
default:
dp_cfg = 2;
break;
}
if (is_yuv)
dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(16), 0);
else
dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(16));
dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | BIT(3));
- }
- if (hw->out[id] == OUT_DPI)
dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, BIT(3));
- setup_display(hw, display);
+}
+static const struct dc_hw_funcs hw_func = {
- .gamma = &gamma_ex_commit,
- .plane = &plane_ex_commit,
- .display = setup_display_ex,
+};
+void dc_hw_commit(struct dc_hw *hw) +{
- u32 i, offset = 0;
- u8 plane_num = hw->info->plane_num;
- u8 layer_num = hw->info->layer_num;
- u8 cursor_num = plane_num - layer_num;
- hw->func->gamma(hw);
- hw->func->plane(hw);
- for (i = 0; i < cursor_num; i++) {
if (hw->cursor[i].dirty) {
offset = hw->cursor[i].display_id ? DC_CURSOR_OFFSET : 0;
if (hw->cursor[i].enable) {
dc_write(hw, DC_CURSOR_ADDRESS + offset,
hw->cursor[i].address);
dc_write(hw, DC_CURSOR_LOCATION + offset, hw->cursor[i].x |
(hw->cursor[i].y << 16));
dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
(hw->cursor[i].hot_x << 16) |
(hw->cursor[i].hot_y << 8) |
(hw->cursor[i].size << 5) |
BIT(3) | BIT(2) | 0x02,
(0xFF << 16) |
(0xFF << 8) |
(0x07 << 5) | 0x1F);
} else {
dc_set_clear(hw, DC_CURSOR_CONFIG + offset, BIT(3), 0x03);
}
hw->cursor[i].dirty = false;
}
- }
- if (hw->qos.dirty) {
dc_set_clear(hw, DC_QOS_CONFIG, (hw->qos.high_value << 4) |
hw->qos.low_value, 0xFF);
hw->qos.dirty = false;
- }
+}
diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.h b/drivers/gpu/drm/verisilicon/vs_dc_hw.h new file mode 100644 index 000000000..81e522ab7 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.h @@ -0,0 +1,496 @@ +/* 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/version.h>
+#define AQ_INTR_ACKNOWLEDGE 0x0010 +#define AQ_INTR_ENBL 0x0014 +#define DC_HW_REVISION 0x0024 +#define DC_HW_CHIP_CID 0x0030
+#define DC_REG_BASE 0x0800 +#define DC_REG_RANGE 0x2000 +#define DC_SEC_REG_OFFSET 0x100000
+#define DC_FRAMEBUFFER_CONFIG 0x1518 +#define DC_FRAMEBUFFER_CONFIG_EX 0x1CC0 +#define DC_FRAMEBUFFER_SCALE_CONFIG 0x1520 +#define DC_FRAMEBUFFER_TOP_LEFT 0x24D8 +#define DC_FRAMEBUFFER_BOTTOM_RIGHT 0x24E0 +#define DC_FRAMEBUFFER_ADDRESS 0x1400 +#define DC_FRAMEBUFFER_U_ADDRESS 0x1530 +#define DC_FRAMEBUFFER_V_ADDRESS 0x1538 +#define DC_FRAMEBUFFER_STRIDE 0x1408 +#define DC_FRAMEBUFFER_U_STRIDE 0x1800 +#define DC_FRAMEBUFFER_V_STRIDE 0x1808 +#define DC_FRAMEBUFFER_SIZE 0x1810 +#define DC_FRAMEBUFFER_SCALE_FACTOR_X 0x1828 +#define DC_FRAMEBUFFER_SCALE_FACTOR_Y 0x1830 +#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX 0x1838 +#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA 0x1A00 +#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX 0x1A08 +#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA 0x1A10 +#define DC_FRAMEBUFFER_INIT_OFFSET 0x1A20 +#define DC_FRAMEBUFFER_COLOR_KEY 0x1508 +#define DC_FRAMEBUFFER_COLOR_KEY_HIGH 0x1510 +#define DC_FRAMEBUFFER_CLEAR_VALUE 0x1A18 +#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX 0x1818 +#define DC_FRAMEBUFFER_COLOR_TABLE_DATA 0x1820 +#define DC_FRAMEBUFFER_BG_COLOR 0x1528 +#define DC_FRAMEBUFFER_ROI_ORIGIN 0x1CB0 +#define DC_FRAMEBUFFER_ROI_SIZE 0x1CB8 +#define DC_FRAMEBUFFER_WATER_MARK 0x1CE8 +#define DC_FRAMEBUFFER_DEGAMMA_INDEX 0x1D88 +#define DC_FRAMEBUFFER_DEGAMMA_DATA 0x1D90 +#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA 0x1D98 +#define DC_FRAMEBUFFER_YUVTORGB_COEF0 0x1DA0 +#define DC_FRAMEBUFFER_YUVTORGB_COEF1 0x1DA8 +#define DC_FRAMEBUFFER_YUVTORGB_COEF2 0x1DB0 +#define DC_FRAMEBUFFER_YUVTORGB_COEF3 0x1DB8 +#define DC_FRAMEBUFFER_YUVTORGB_COEF4 0x1E00 +#define DC_FRAMEBUFFER_YUVTORGB_COEFD0 0x1E08 +#define DC_FRAMEBUFFER_YUVTORGB_COEFD1 0x1E10 +#define DC_FRAMEBUFFER_YUVTORGB_COEFD2 0x1E18 +#define DC_FRAMEBUFFER_Y_CLAMP_BOUND 0x1E88 +#define DC_FRAMEBUFFER_UV_CLAMP_BOUND 0x1E90 +#define DC_FRAMEBUFFER_RGBTORGB_COEF0 0x1E20 +#define DC_FRAMEBUFFER_RGBTORGB_COEF1 0x1E28 +#define DC_FRAMEBUFFER_RGBTORGB_COEF2 0x1E30 +#define DC_FRAMEBUFFER_RGBTORGB_COEF3 0x1E38 +#define DC_FRAMEBUFFER_RGBTORGB_COEF4 0x1E40 +#define DC_FRAMEBUFFER_BLEND_CONFIG 0x2510 +#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR 0x2500 +#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR 0x2508
+#define DC_OVERLAY_CONFIG 0x1540 +#define DC_OVERLAY_CONFIG_EX 0x2540 +#define DC_OVERLAY_SCALE_CONFIG 0x1C00 +#define DC_OVERLAY_BLEND_CONFIG 0x1580 +#define DC_OVERLAY_TOP_LEFT 0x1640 +#define DC_OVERLAY_BOTTOM_RIGHT 0x1680 +#define DC_OVERLAY_ADDRESS 0x15C0 +#define DC_OVERLAY_U_ADDRESS 0x1840 +#define DC_OVERLAY_V_ADDRESS 0x1880 +#define DC_OVERLAY_STRIDE 0x1600 +#define DC_OVERLAY_U_STRIDE 0x18C0 +#define DC_OVERLAY_V_STRIDE 0x1900 +#define DC_OVERLAY_SIZE 0x17C0 +#define DC_OVERLAY_SCALE_FACTOR_X 0x1A40 +#define DC_OVERLAY_SCALE_FACTOR_Y 0x1A80 +#define DC_OVERLAY_H_FILTER_COEF_INDEX 0x1AC0 +#define DC_OVERLAY_H_FILTER_COEF_DATA 0x1B00 +#define DC_OVERLAY_V_FILTER_COEF_INDEX 0x1B40 +#define DC_OVERLAY_V_FILTER_COEF_DATA 0x1B80 +#define DC_OVERLAY_INIT_OFFSET 0x1BC0 +#define DC_OVERLAY_COLOR_KEY 0x1740 +#define DC_OVERLAY_COLOR_KEY_HIGH 0x1780 +#define DC_OVERLAY_CLEAR_VALUE 0x1940 +#define DC_OVERLAY_COLOR_TABLE_INDEX 0x1980 +#define DC_OVERLAY_COLOR_TABLE_DATA 0x19C0 +#define DC_OVERLAY_SRC_GLOBAL_COLOR 0x16C0 +#define DC_OVERLAY_DST_GLOBAL_COLOR 0x1700 +#define DC_OVERLAY_ROI_ORIGIN 0x1D00 +#define DC_OVERLAY_ROI_SIZE 0x1D40 +#define DC_OVERLAY_WATER_MARK 0x1DC0 +#define DC_OVERLAY_DEGAMMA_INDEX 0x2200 +#define DC_OVERLAY_DEGAMMA_DATA 0x2240 +#define DC_OVERLAY_DEGAMMA_EX_DATA 0x2280 +#define DC_OVERLAY_YUVTORGB_COEF0 0x1EC0 +#define DC_OVERLAY_YUVTORGB_COEF1 0x1F00 +#define DC_OVERLAY_YUVTORGB_COEF2 0x1F40 +#define DC_OVERLAY_YUVTORGB_COEF3 0x1F80 +#define DC_OVERLAY_YUVTORGB_COEF4 0x1FC0 +#define DC_OVERLAY_YUVTORGB_COEFD0 0x2000 +#define DC_OVERLAY_YUVTORGB_COEFD1 0x2040 +#define DC_OVERLAY_YUVTORGB_COEFD2 0x2080 +#define DC_OVERLAY_Y_CLAMP_BOUND 0x22C0 +#define DC_OVERLAY_UV_CLAMP_BOUND 0x2300 +#define DC_OVERLAY_RGBTORGB_COEF0 0x20C0 +#define DC_OVERLAY_RGBTORGB_COEF1 0x2100 +#define DC_OVERLAY_RGBTORGB_COEF2 0x2140 +#define DC_OVERLAY_RGBTORGB_COEF3 0x2180 +#define DC_OVERLAY_RGBTORGB_COEF4 0x21C0
+#define DC_CURSOR_CONFIG 0x1468 +#define DC_CURSOR_ADDRESS 0x146C +#define DC_CURSOR_LOCATION 0x1470 +#define DC_CURSOR_BACKGROUND 0x1474 +#define DC_CURSOR_FOREGROUND 0x1478 +#define DC_CURSOR_CLK_GATING 0x1484 +#define DC_CURSOR_CONFIG_EX 0x24E8 +#define DC_CURSOR_OFFSET 0x1080
+#define DC_DISPLAY_DITHER_CONFIG 0x1410 +#define DC_DISPLAY_PANEL_CONFIG 0x1418 +#define DC_DISPLAY_PANEL_CONFIG_EX 0x2518 +#define DC_DISPLAY_DITHER_TABLE_LOW 0x1420 +#define DC_DISPLAY_DITHER_TABLE_HIGH 0x1428 +#define DC_DISPLAY_H 0x1430 +#define DC_DISPLAY_H_SYNC 0x1438 +#define DC_DISPLAY_V 0x1440 +#define DC_DISPLAY_V_SYNC 0x1448 +#define DC_DISPLAY_CURRENT_LOCATION 0x1450 +#define DC_DISPLAY_GAMMA_INDEX 0x1458 +#define DC_DISPLAY_GAMMA_DATA 0x1460 +#define DC_DISPLAY_INT 0x147C +#define DC_DISPLAY_INT_ENABLE 0x1480 +#define DC_DISPLAY_DBI_CONFIG 0x1488 +#define DC_DISPLAY_GENERAL_CONFIG 0x14B0 +#define DC_DISPLAY_DPI_CONFIG 0x14B8 +#define DC_DISPLAY_PANEL_START 0x1CCC +#define DC_DISPLAY_DEBUG_COUNTER_SELECT 0x14D0 +#define DC_DISPLAY_DEBUG_COUNTER_VALUE 0x14D8 +#define DC_DISPLAY_DP_CONFIG 0x1CD0 +#define DC_DISPLAY_GAMMA_EX_INDEX 0x1CF0 +#define DC_DISPLAY_GAMMA_EX_DATA 0x1CF8 +#define DC_DISPLAY_GAMMA_EX_ONE_DATA 0x1D80 +#define DC_DISPLAY_RGBTOYUV_COEF0 0x1E48 +#define DC_DISPLAY_RGBTOYUV_COEF1 0x1E50 +#define DC_DISPLAY_RGBTOYUV_COEF2 0x1E58 +#define DC_DISPLAY_RGBTOYUV_COEF3 0x1E60 +#define DC_DISPLAY_RGBTOYUV_COEF4 0x1E68 +#define DC_DISPLAY_RGBTOYUV_COEFD0 0x1E70 +#define DC_DISPLAY_RGBTOYUV_COEFD1 0x1E78 +#define DC_DISPLAY_RGBTOYUV_COEFD2 0x1E80
+#define DC_CLK_GATTING 0x1A28 +#define DC_QOS_CONFIG 0x1A38
+#define DC_TRANSPARENCY_OPAQUE 0x00 +#define DC_TRANSPARENCY_KEY 0x02 +#define DC_DISPLAY_DITHERTABLE_LOW 0x7B48F3C0 +#define DC_DISPLAY_DITHERTABLE_HIGH 0x596AD1E2
+#define GAMMA_SIZE 256 +#define GAMMA_EX_SIZE 300 +#define DEGAMMA_SIZE 260
+#define RGB_TO_RGB_TABLE_SIZE 9 +#define YUV_TO_RGB_TABLE_SIZE 16 +#define RGB_TO_YUV_TABLE_SIZE 12
+#define DC_LAYER_NUM 6 +#define DC_DISPLAY_NUM 2 +#define DC_CURSOR_NUM 2
+enum dc_chip_rev {
- DC_REV_0,/* For HW_REV_5720,HW_REV_5721_311 */
- DC_REV_1,/* For HW_REV_5721_30B */
- DC_REV_2,/* For HW_REV_5721_310 */
+};
+enum dc_hw_plane_id {
- PRIMARY_PLANE_0,
- OVERLAY_PLANE_0,
- OVERLAY_PLANE_1,
- PRIMARY_PLANE_1,
- OVERLAY_PLANE_2,
- OVERLAY_PLANE_3,
- CURSOR_PLANE_0,
- CURSOR_PLANE_1,
- PLANE_NUM
+};
+enum dc_hw_color_format {
- FORMAT_X4R4G4B4,//0
- FORMAT_A4R4G4B4,//1
- FORMAT_X1R5G5B5,//2
- FORMAT_A1R5G5B5,//3
- FORMAT_R5G6B5,//4
- FORMAT_X8R8G8B8,//5
- FORMAT_A8R8G8B8,//6
- FORMAT_YUY2,//7
- FORMAT_UYVY,//8
- FORMAT_INDEX8,//9
- FORMAT_MONOCHROME,//10
- FORMAT_YV12 = 0xf,
- FORMAT_A8,//16
- FORMAT_NV12,//17
- FORMAT_NV16,//18
- FORMAT_RG16,//19
- FORMAT_R8,//20
- FORMAT_NV12_10BIT,//21
- FORMAT_A2R10G10B10,//22
- FORMAT_NV16_10BIT,//23
- FORMAT_INDEX1,//24
- FORMAT_INDEX2,//25
- FORMAT_INDEX4,//26
- FORMAT_P010,//27
- FORMAT_YUV444,//28
- FORMAT_YUV444_10BIT,//29
+};
+enum dc_hw_yuv_color_space {
- COLOR_SPACE_601 = 0,
- COLOR_SPACE_709 = 1,
- COLOR_SPACE_2020 = 3,
+};
+enum dc_hw_rotation {
- ROT_0 = 0,
- ROT_90 = 4,
- ROT_180 = 5,
- ROT_270 = 6,
- FLIP_X = 1,
- FLIP_Y = 2,
- FLIP_XY = 3,
+};
+enum dc_hw_swizzle {
- SWIZZLE_ARGB = 0,
- SWIZZLE_RGBA,
- SWIZZLE_ABGR,
- SWIZZLE_BGRA,
+};
+enum dc_hw_out {
- OUT_DPI,
- OUT_DP,
+};
+enum dc_hw_cursor_size {
- CURSOR_SIZE_32X32 = 0,
- CURSOR_SIZE_64X64,
+};
+enum dc_hw_blend_mode {
- /* out.rgb = plane_alpha * fg.rgb +
* (1 - (plane_alpha * fg.alpha)) * bg.rgb
*/
- BLEND_PREMULTI,
- /* out.rgb = plane_alpha * fg.alpha * fg.rgb +
* (1 - (plane_alpha * fg.alpha)) * bg.rgb
*/
- BLEND_COVERAGE,
- /* out.rgb = plane_alpha * fg.rgb +
* (1 - plane_alpha) * bg.rgb
*/
- BLEND_PIXEL_NONE,
+};
+struct dc_hw_plane_reg {
- u32 y_address;
- u32 u_address;
- u32 v_address;
- u32 y_stride;
- u32 u_stride;
- u32 v_stride;
- u32 size;
- u32 top_left;
- u32 bottom_right;
- u32 scale_factor_x;
- u32 scale_factor_y;
- u32 h_filter_coef_index;
- u32 h_filter_coef_data;
- u32 v_filter_coef_index;
- u32 v_filter_coef_data;
- u32 init_offset;
- u32 color_key;
- u32 color_key_high;
- u32 clear_value;
- u32 color_table_index;
- u32 color_table_data;
- u32 scale_config;
- u32 water_mark;
- u32 degamma_index;
- u32 degamma_data;
- u32 degamma_ex_data;
- u32 src_global_color;
- u32 dst_global_color;
- u32 blend_config;
- u32 roi_origin;
- u32 roi_size;
- u32 yuv_to_rgb_coef0;
- u32 yuv_to_rgb_coef1;
- u32 yuv_to_rgb_coef2;
- u32 yuv_to_rgb_coef3;
- u32 yuv_to_rgb_coef4;
- u32 yuv_to_rgb_coefd0;
- u32 yuv_to_rgb_coefd1;
- u32 yuv_to_rgb_coefd2;
- u32 y_clamp_bound;
- u32 uv_clamp_bound;
- u32 rgb_to_rgb_coef0;
- u32 rgb_to_rgb_coef1;
- u32 rgb_to_rgb_coef2;
- u32 rgb_to_rgb_coef3;
- u32 rgb_to_rgb_coef4;
+};
+struct dc_hw_fb {
- u32 y_address;
- u32 u_address;
- u32 v_address;
- u32 clear_value;
- u32 water_mark;
- u16 y_stride;
- u16 u_stride;
- u16 v_stride;
- u16 width;
- u16 height;
- u8 format;
- u8 tile_mode;
- u8 rotation;
- u8 yuv_color_space;
- u8 swizzle;
- u8 uv_swizzle;
- u8 zpos;
- u8 display_id;
- bool clear_enable;
- bool dec_enable;
- bool enable;
- bool dirty;
+};
+struct dc_hw_scale {
- u32 scale_factor_x;
- u32 scale_factor_y;
- bool enable;
- bool dirty;
+};
+struct dc_hw_position {
- u16 start_x;
- u16 start_y;
- u16 end_x;
- u16 end_y;
- bool dirty;
+};
+struct dc_hw_blend {
- u8 alpha;
- u8 blend_mode;
- bool dirty;
+};
+struct dc_hw_colorkey {
- u32 colorkey;
- u32 colorkey_high;
- u8 transparency;
- bool dirty;
+};
+struct dc_hw_roi {
- u16 x;
- u16 y;
- u16 width;
- u16 height;
- bool enable;
- bool dirty;
+};
+struct dc_hw_cursor {
- u32 address;
- u16 x;
- u16 y;
- u16 hot_x;
- u16 hot_y;
- u8 size;
- u8 display_id;
- bool enable;
- bool dirty;
+};
+struct dc_hw_display {
- u32 bus_format;
- u16 h_active;
- u16 h_total;
- u16 h_sync_start;
- u16 h_sync_end;
- u16 v_active;
- u16 v_total;
- u16 v_sync_start;
- u16 v_sync_end;
- u16 sync_mode;
- u32 bg_color;
- u8 id;
- bool h_sync_polarity;
- bool v_sync_polarity;
- bool enable;
- bool sync_enable;
- bool dither_enable;
+};
+struct dc_hw_gamma {
- u16 gamma[GAMMA_EX_SIZE][3];
- bool enable;
- bool dirty;
+};
+struct dc_hw_degamma {
- u16 degamma[DEGAMMA_SIZE][3];
- u32 mode;
- bool dirty;
+};
+struct dc_hw_plane {
- struct dc_hw_fb fb;
- struct dc_hw_position pos;
- struct dc_hw_scale scale;
- struct dc_hw_blend blend;
- struct dc_hw_roi roi;
- struct dc_hw_colorkey colorkey;
- struct dc_hw_degamma degamma;
+};
+struct dc_hw_qos {
- u8 low_value;
- u8 high_value;
- bool dirty;
+};
+struct dc_hw_read {
- u32 reg;
- u32 value;
+};
+struct dc_hw; +struct dc_hw_funcs {
- void (*gamma)(struct dc_hw *hw);
- void (*plane)(struct dc_hw *hw);
- void (*display)(struct dc_hw *hw, struct dc_hw_display *display);
+};
+struct dc_hw {
- enum dc_chip_rev rev;
- enum dc_hw_out out[DC_DISPLAY_NUM];
- void *hi_base;
- void *reg_base;
- struct dc_hw_display display[DC_DISPLAY_NUM];
- struct dc_hw_gamma gamma[DC_DISPLAY_NUM];
- struct dc_hw_plane plane[DC_LAYER_NUM];
- struct dc_hw_cursor cursor[DC_CURSOR_NUM];
- struct dc_hw_qos qos;
- struct dc_hw_funcs *func;
- struct vs_dc_info *info;
+};
+int dc_hw_init(struct dc_hw *hw); +void dc_hw_deinit(struct dc_hw *hw); +void dc_hw_update_plane(struct dc_hw *hw, u8 id,
struct dc_hw_fb *fb, struct dc_hw_scale *scale,
struct dc_hw_position *pos, struct dc_hw_blend *blend);
+void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode); +void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi); +void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
struct dc_hw_colorkey *colorkey);
+void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos); +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor); +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
u16 r, u16 g, u16 b);
+void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable); +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display); +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable); +u32 dc_hw_get_interrupt(struct dc_hw *hw); +bool dc_hw_check_underflow(struct dc_hw *hw); +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable); +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id); +void dc_hw_commit(struct dc_hw *hw);
+#endif /* __VS_DC_HW_H__ */ diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c index 69591e640..ddec92910 100644 --- a/drivers/gpu/drm/verisilicon/vs_drv.c +++ b/drivers/gpu/drm/verisilicon/vs_drv.c @@ -33,6 +33,7 @@ #include "vs_drv.h" #include "vs_modeset.h" #include "vs_gem.h" +#include "vs_dc.h" #define DRV_NAME "starfive" #define DRV_DESC "Starfive DRM driver" @@ -155,7 +156,7 @@ static const struct component_master_ops vs_drm_ops = { }; static struct platform_driver *drm_sub_drivers[] = {
- &dc_platform_driver,
/* connector + encoder*/ #ifdef CONFIG_STARFIVE_HDMI diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c new file mode 100644 index 000000000..61648d7b1 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_plane.c @@ -0,0 +1,502 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
- */
+#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_blend.h> +#include <drm/drm_gem_dma_helper.h> +#include <drm/drm_fb_dma_helper.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_plane.h> +#include <drm/drm_plane_helper.h>
+#include <drm/vs_drm.h>
+#include "vs_crtc.h" +#include "vs_modeset.h" +#include "vs_gem.h" +#include "vs_plane.h" +#include "vs_type.h" +#include "vs_drv.h" +#include "vs_dc.h"
+static void vs_plane_reset(struct drm_plane *plane) +{
- struct vs_plane_state *state;
- struct vs_plane *vs_plane = to_vs_plane(plane);
- if (plane->state) {
__drm_atomic_helper_plane_destroy_state(plane->state);
state = to_vs_plane_state(plane->state);
kfree(state);
plane->state = NULL;
- }
- state = kzalloc(sizeof(*state), GFP_KERNEL);
- if (!state)
return;
- __drm_atomic_helper_plane_reset(plane, &state->base);
Call this after fully initializing the state.
- state->degamma = VS_DEGAMMA_DISABLE;
- state->degamma_changed = false;
- state->base.zpos = vs_plane->id;
- memset(&state->status, 0, sizeof(state->status));
+}
+static void _vs_plane_duplicate_blob(struct vs_plane_state *state,
struct vs_plane_state *ori_state)
+{
- state->watermark = ori_state->watermark;
- state->color_mgmt = ori_state->color_mgmt;
- state->roi = ori_state->roi;
- if (state->watermark)
drm_property_blob_get(state->watermark);
- if (state->color_mgmt)
drm_property_blob_get(state->color_mgmt);
- if (state->roi)
drm_property_blob_get(state->roi);
+}
+static int +_vs_plane_set_property_blob_from_id(struct drm_device *dev,
struct drm_property_blob **blob,
u64 blob_id,
size_t expected_size)
+{
- struct drm_property_blob *new_blob = NULL;
- if (blob_id) {
new_blob = drm_property_lookup_blob(dev, blob_id);
if (!new_blob)
return -EINVAL;
if (new_blob->length != expected_size) {
drm_property_blob_put(new_blob);
return -EINVAL;
}
- }
- drm_property_replace_blob(blob, new_blob);
- drm_property_blob_put(new_blob);
- return 0;
+}
+static struct drm_plane_state * +vs_plane_atomic_duplicate_state(struct drm_plane *plane) +{
- struct vs_plane_state *ori_state;
- struct vs_plane_state *state;
- if (WARN_ON(!plane->state))
return NULL;
- ori_state = to_vs_plane_state(plane->state);
- state = kzalloc(sizeof(*state), GFP_KERNEL);
- if (!state)
return NULL;
- __drm_atomic_helper_plane_duplicate_state(plane, &state->base);
- state->degamma = ori_state->degamma;
- state->degamma_changed = ori_state->degamma_changed;
- _vs_plane_duplicate_blob(state, ori_state);
- memcpy(&state->status, &ori_state->status, sizeof(ori_state->status));
- return &state->base;
+}
+static void vs_plane_atomic_destroy_state(struct drm_plane *plane,
struct drm_plane_state *state)
+{
- struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
- __drm_atomic_helper_plane_destroy_state(state);
- drm_property_blob_put(vs_plane_state->watermark);
- drm_property_blob_put(vs_plane_state->color_mgmt);
- drm_property_blob_put(vs_plane_state->roi);
- kfree(vs_plane_state);
+}
+static int vs_plane_atomic_set_property(struct drm_plane *plane,
struct drm_plane_state *state,
struct drm_property *property,
uint64_t val)
+{
- struct drm_device *dev = plane->dev;
- struct vs_plane *vs_plane = to_vs_plane(plane);
- struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
- int ret = 0;
- if (property == vs_plane->degamma_mode) {
if (vs_plane_state->degamma != val) {
vs_plane_state->degamma = val;
vs_plane_state->degamma_changed = true;
} else {
vs_plane_state->degamma_changed = false;
}
- } else if (property == vs_plane->watermark_prop) {
ret = _vs_plane_set_property_blob_from_id(dev,
&vs_plane_state->watermark,
val,
sizeof(struct drm_vs_watermark));
return ret;
- } else if (property == vs_plane->color_mgmt_prop) {
ret = _vs_plane_set_property_blob_from_id(dev,
&vs_plane_state->color_mgmt,
val,
sizeof(struct drm_vs_color_mgmt));
return ret;
- } else if (property == vs_plane->roi_prop) {
ret = _vs_plane_set_property_blob_from_id(dev,
&vs_plane_state->roi,
val,
sizeof(struct drm_vs_roi));
return ret;
- } else {
return -EINVAL;
- }
- return 0;
+}
+static int vs_plane_atomic_get_property(struct drm_plane *plane,
const struct drm_plane_state *state,
struct drm_property *property,
uint64_t *val)
+{
- struct vs_plane *vs_plane = to_vs_plane(plane);
- const struct vs_plane_state *vs_plane_state =
container_of(state, const struct vs_plane_state, base);
- if (property == vs_plane->degamma_mode)
*val = vs_plane_state->degamma;
- else if (property == vs_plane->watermark_prop)
*val = (vs_plane_state->watermark) ?
vs_plane_state->watermark->base.id : 0;
- else if (property == vs_plane->color_mgmt_prop)
*val = (vs_plane_state->color_mgmt) ?
vs_plane_state->color_mgmt->base.id : 0;
- else if (property == vs_plane->roi_prop)
*val = (vs_plane_state->roi) ?
vs_plane_state->roi->base.id : 0;
- else
return -EINVAL;
- return 0;
+}
+static bool vs_format_mod_supported(struct drm_plane *plane,
u32 format,
u64 modifier)
+{
- int i;
- /* We always have to allow these modifiers:
* 1. Core DRM checks for LINEAR support if userspace does not provide modifiers.
* 2. Not passing any modifiers is the same as explicitly passing INVALID.
*/
- if (modifier == DRM_FORMAT_MOD_LINEAR)
return true;
- /* Check that the modifier is on the list of the plane's supported modifiers. */
- for (i = 0; i < plane->modifier_count; i++) {
if (modifier == plane->modifiers[i])
break;
- }
- if (i == plane->modifier_count)
return false;
- return true;
+}
+const struct drm_plane_funcs vs_plane_funcs = {
- .update_plane = drm_atomic_helper_update_plane,
- .disable_plane = drm_atomic_helper_disable_plane,
- .reset = vs_plane_reset,
- .atomic_duplicate_state = vs_plane_atomic_duplicate_state,
- .atomic_destroy_state = vs_plane_atomic_destroy_state,
- .atomic_set_property = vs_plane_atomic_set_property,
- .atomic_get_property = vs_plane_atomic_get_property,
- .format_mod_supported = vs_format_mod_supported,
+};
+static unsigned char vs_get_plane_number(struct drm_framebuffer *fb) +{
- const struct drm_format_info *info;
- if (!fb)
return 0;
- info = drm_format_info(fb->format->format);
- if (!info || info->num_planes > DRM_FORMAT_MAX_PLANES)
return 0;
- return info->num_planes;
+}
+static int vs_plane_atomic_check(struct drm_plane *plane,
struct drm_atomic_state *state)
+{
- struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
plane);
- 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);
- if (!crtc || !fb)
return 0;
- return vs_dc_check_plane(vs_crtc->dev, 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);
- 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);
- if (!crtc || !fb)
return 0;
- return vs_dc_check_cursor_plane(vs_crtc->dev, 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);
- 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);
- if (!new_state->fb || !new_state->crtc)
return;
- fb = new_state->fb;
- num_planes = vs_get_plane_number(fb);
- for (i = 0; i < num_planes; i++) {
struct vs_gem_object *vs_obj;
vs_obj = vs_fb_get_gem_obj(fb, i);
vs_plane->dma_addr[i] = vs_obj->iova + fb->offsets[i];
- }
- plane_state->status.src = drm_plane_state_src(new_state);
- plane_state->status.dest = drm_plane_state_dest(new_state);
- vs_dc_update_plane(vs_crtc->dev, 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);
- 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);
- if (!new_state->fb || !new_state->crtc)
return;
- fb = new_state->fb;
- num_planes = vs_get_plane_number(fb);
- for (i = 0; i < num_planes; i++) {
struct vs_gem_object *vs_obj;
vs_obj = vs_fb_get_gem_obj(fb, i);
vs_plane->dma_addr[i] = vs_obj->iova + fb->offsets[i];
- }
- plane_state->status.src = drm_plane_state_src(new_state);
- plane_state->status.dest = drm_plane_state_dest(new_state);
- vs_dc_update_cursor_plane(vs_crtc->dev, 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);
- vs_dc_disable_plane(vs_crtc->dev, 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);
- vs_dc_disable_cursor_plane(vs_crtc->dev, vs_plane, old_state);
+}
+const struct drm_plane_helper_funcs primary_plane_helpers = {
- .atomic_check = vs_plane_atomic_check,
- .atomic_update = vs_plane_atomic_update,
- .atomic_disable = vs_plane_atomic_disable,
+};
+const struct drm_plane_helper_funcs overlay_plane_helpers = {
- .atomic_check = vs_plane_atomic_check,
- .atomic_update = vs_plane_atomic_update,
- .atomic_disable = vs_plane_atomic_disable,
+};
+const struct drm_plane_helper_funcs cursor_plane_helpers = {
- .atomic_check = vs_cursor_plane_atomic_check,
- .atomic_update = vs_cursor_plane_atomic_update,
- .atomic_disable = vs_cursor_plane_atomic_disable,
+};
+static const struct drm_prop_enum_list vs_degamma_mode_enum_list[] = {
- { VS_DEGAMMA_DISABLE, "disabled" },
- { VS_DEGAMMA_BT709, "preset degamma for BT709" },
- { VS_DEGAMMA_BT2020, "preset degamma for BT2020" },
+};
+struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
struct vs_plane_info *info,
unsigned int layer_num,
unsigned int possible_crtcs)
+{
- struct vs_plane *plane;
- int ret;
- if (!info)
return NULL;
- plane = drmm_kzalloc(drm_dev, sizeof(*plane), GFP_KERNEL);
- if (!plane)
return NULL;
This allocation is not necessary.
- 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 (!strcmp(info->name, "Primary") ||
!strcmp(info->name, "Primary_1")) {
drm_plane_helper_add(&plane->base, &primary_plane_helpers);
- } else if (!strcmp(info->name, "Cursor_1") ||
!strcmp(info->name, "Cursor")) {
drm_plane_helper_add(&plane->base, &cursor_plane_helpers);
- } else {
drm_plane_helper_add(&plane->base, &overlay_plane_helpers);
- }
Rather look at the info->type here than at the name.
- /* Set up the plane properties */
- if (info->degamma_size) {
plane->degamma_mode =
drm_property_create_enum(drm_dev, 0,
"DEGAMMA_MODE",
vs_degamma_mode_enum_list,
ARRAY_SIZE(vs_degamma_mode_enum_list));
if (!plane->degamma_mode)
return NULL;
drm_object_attach_property(&plane->base.base,
plane->degamma_mode,
VS_DEGAMMA_DISABLE);
- }
- if (info->rotation) {
ret = drm_plane_create_rotation_property(&plane->base,
DRM_MODE_ROTATE_0,
info->rotation);
if (ret)
return NULL;
- }
- if (info->blend_mode) {
ret = drm_plane_create_blend_mode_property(&plane->base,
info->blend_mode);
if (ret)
return NULL;
ret = drm_plane_create_alpha_property(&plane->base);
if (ret)
return NULL;
- }
- if (info->color_encoding) {
ret = drm_plane_create_color_properties(&plane->base,
info->color_encoding,
BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
DRM_COLOR_YCBCR_BT709,
DRM_COLOR_YCBCR_LIMITED_RANGE);
if (ret)
return NULL;
- }
- if (info->zpos != 255) {
ret = drm_plane_create_zpos_property(&plane->base, info->zpos, 0,
layer_num - 1);
if (ret)
return NULL;
- } else {
ret = drm_plane_create_zpos_immutable_property(&plane->base,
info->zpos);
if (ret)
return NULL;
- }
- if (info->watermark) {
plane->watermark_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
"WATERMARK", 0);
if (!plane->watermark_prop)
return NULL;
drm_object_attach_property(&plane->base.base, plane->watermark_prop, 0);
- }
- if (info->color_mgmt) {
plane->color_mgmt_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
"COLOR_CONFIG", 0);
if (!plane->color_mgmt_prop)
return NULL;
drm_object_attach_property(&plane->base.base, plane->color_mgmt_prop, 0);
- }
- if (info->roi) {
plane->roi_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
"ROI", 0);
if (!plane->roi_prop)
return NULL;
drm_object_attach_property(&plane->base.base, plane->roi_prop, 0);
- }
- return plane;
+} diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h new file mode 100644 index 000000000..d84b1abd9 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_plane.h @@ -0,0 +1,65 @@ +/* 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_modeset.h" +#include "vs_type.h"
+struct vs_plane;
+struct vs_plane_status {
- u32 tile_mode;
- struct drm_rect src;
- struct drm_rect dest;
+};
+struct vs_plane_state {
- struct drm_plane_state base;
- struct vs_plane_status status; /* for debugfs */
- struct drm_property_blob *watermark;
- struct drm_property_blob *color_mgmt;
- struct drm_property_blob *roi;
- u32 degamma;
- bool degamma_changed;
+};
+struct vs_plane {
- struct drm_plane base;
- u8 id;
- dma_addr_t dma_addr[DRM_FORMAT_MAX_PLANES];
This changes with each plane update, so it belongs into the vs_plane_state. You set it in the plane's atomic_check and later use it in atomic_update.
- struct drm_property *degamma_mode;
- struct drm_property *watermark_prop;
- struct drm_property *color_mgmt_prop;
- struct drm_property *roi_prop;
- const struct vs_plane_funcs *funcs;
This type does not exist any longer.
+};
+void vs_plane_destory(struct drm_plane *plane);
'destory'
It appears to be unused.
Best regards Thomas
+struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
struct vs_plane_info *info,
unsigned int layer_num,
unsigned int possible_crtcs);
+static inline struct vs_plane *to_vs_plane(struct drm_plane *plane) +{
- return container_of(plane, struct vs_plane, base);
+}
+static inline struct vs_plane_state * +to_vs_plane_state(struct drm_plane_state *state) +{
- return container_of(state, struct vs_plane_state, base);
+} +#endif /* __VS_PLANE_H__ */ diff --git a/drivers/gpu/drm/verisilicon/vs_type.h b/drivers/gpu/drm/verisilicon/vs_type.h new file mode 100644 index 000000000..80408cdb8 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_type.h @@ -0,0 +1,70 @@ +/* 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 mmu_prefetch;
- bool background;
- bool panel_sync;
- bool cap_dec;
+};
+#endif /* __VS_TYPE_H__ */
add hdmi driver as encoder and connect
Signed-off-by: Keith Zhao keith.zhao@starfivetech.com --- drivers/gpu/drm/verisilicon/starfive_hdmi.c | 940 ++++++++++++++++++++ drivers/gpu/drm/verisilicon/starfive_hdmi.h | 295 ++++++ 2 files changed, 1235 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/starfive_hdmi.c b/drivers/gpu/drm/verisilicon/starfive_hdmi.c new file mode 100644 index 000000000..c02f4f749 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.c @@ -0,0 +1,940 @@ +// 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/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" + +static struct starfive_hdmi *encoder_to_hdmi(struct drm_encoder *encoder) +{ + return container_of(encoder, struct starfive_hdmi, encoder); +} + +static struct starfive_hdmi *connector_to_hdmi(struct drm_connector *connector) +{ + return container_of(connector, struct starfive_hdmi, connector); +} + +struct starfive_hdmi_i2c { + struct i2c_adapter adap; + + u8 ddc_addr; + u8 segment_addr; + /* protects the edid data when use i2c cmd to read edid */ + struct mutex lock; + struct completion cmp; +}; + +static const struct pre_pll_config pre_pll_cfg_table[] = { + { 25175000, 25175000, 1, 100, 2, 3, 3, 12, 3, 3, 4, 0, 0xf55555}, + { 25200000, 25200000, 1, 100, 2, 3, 3, 12, 3, 3, 4, 0, 0}, + { 27000000, 27000000, 1, 90, 3, 2, 2, 10, 3, 3, 4, 0, 0}, + { 27027000, 27027000, 1, 90, 3, 2, 2, 10, 3, 3, 4, 0, 0x170a3d}, + { 28320000, 28320000, 1, 28, 2, 1, 1, 3, 0, 3, 4, 0, 0x51eb85}, + { 30240000, 30240000, 1, 30, 2, 1, 1, 3, 0, 3, 4, 0, 0x3d70a3}, + { 31500000, 31500000, 1, 31, 2, 1, 1, 3, 0, 3, 4, 0, 0x7fffff}, + { 33750000, 33750000, 1, 33, 2, 1, 1, 3, 0, 3, 4, 0, 0xcfffff}, + { 36000000, 36000000, 1, 36, 2, 1, 1, 3, 0, 3, 4, 0, 0}, + { 40000000, 40000000, 1, 80, 2, 2, 2, 12, 2, 2, 2, 0, 0}, + { 46970000, 46970000, 1, 46, 2, 1, 1, 3, 0, 3, 4, 0, 0xf851eb}, + { 49500000, 49500000, 1, 49, 2, 1, 1, 3, 0, 3, 4, 0, 0x7fffff}, + { 49000000, 49000000, 1, 49, 2, 1, 1, 3, 0, 3, 4, 0, 0}, + { 50000000, 50000000, 1, 50, 2, 1, 1, 3, 0, 3, 4, 0, 0}, + { 54000000, 54000000, 1, 54, 2, 1, 1, 3, 0, 3, 4, 0, 0}, + { 54054000, 54054000, 1, 54, 2, 1, 1, 3, 0, 3, 4, 0, 0x0dd2f1}, + { 57284000, 57284000, 1, 57, 2, 1, 1, 3, 0, 3, 4, 0, 0x48b439}, + { 58230000, 58230000, 1, 58, 2, 1, 1, 3, 0, 3, 4, 0, 0x3ae147}, + { 59341000, 59341000, 1, 59, 2, 1, 1, 3, 0, 3, 4, 0, 0x574bc6}, + { 59400000, 59400000, 1, 99, 3, 1, 1, 1, 3, 3, 4, 0, 0}, + { 65000000, 65000000, 1, 130, 2, 2, 2, 12, 0, 2, 2, 0, 0}, + { 68250000, 68250000, 1, 68, 2, 1, 1, 3, 0, 3, 4, 0, 0x3fffff}, + { 71000000, 71000000, 1, 71, 2, 1, 1, 3, 0, 3, 4, 0, 0}, + { 74176000, 74176000, 1, 98, 1, 2, 2, 1, 2, 3, 4, 0, 0xe6ae6b}, + { 74250000, 74250000, 1, 99, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + { 75000000, 75000000, 1, 75, 2, 1, 1, 3, 0, 3, 4, 0, 0}, + { 78750000, 78750000, 1, 78, 2, 1, 1, 3, 0, 3, 4, 0, 0xcfffff}, + { 79500000, 79500000, 1, 79, 2, 1, 1, 3, 0, 3, 4, 0, 0x7fffff}, + { 83500000, 83500000, 2, 167, 2, 1, 1, 1, 0, 0, 6, 0, 0}, + { 83500000, 104375000, 1, 104, 2, 1, 1, 1, 1, 0, 5, 0, 0x600000}, + { 84858000, 84858000, 1, 85, 2, 1, 1, 3, 0, 3, 4, 0, 0xdba5e2}, + { 85500000, 85500000, 1, 85, 2, 1, 1, 3, 0, 3, 4, 0, 0x7fffff}, + { 85750000, 85750000, 1, 85, 2, 1, 1, 3, 0, 3, 4, 0, 0xcfffff}, + { 85800000, 85800000, 1, 85, 2, 1, 1, 3, 0, 3, 4, 0, 0xcccccc}, + { 88750000, 88750000, 1, 88, 2, 1, 1, 3, 0, 3, 4, 0, 0xcfffff}, + { 89910000, 89910000, 1, 89, 2, 1, 1, 3, 0, 3, 4, 0, 0xe8f5c1}, + { 90000000, 90000000, 1, 90, 2, 1, 1, 3, 0, 3, 4, 0, 0}, + {101000000, 101000000, 1, 101, 2, 1, 1, 3, 0, 3, 4, 0, 0}, + {102250000, 102250000, 1, 102, 2, 1, 1, 3, 0, 3, 4, 0, 0x3fffff}, + {106500000, 106500000, 1, 106, 2, 1, 1, 3, 0, 3, 4, 0, 0x7fffff}, + {108000000, 108000000, 1, 90, 3, 0, 0, 5, 0, 2, 2, 0, 0}, + {119000000, 119000000, 1, 119, 2, 1, 1, 3, 0, 3, 4, 0, 0}, + {131481000, 131481000, 1, 131, 2, 1, 1, 3, 0, 3, 4, 0, 0x7b22d1}, + {135000000, 135000000, 1, 135, 2, 1, 1, 3, 0, 3, 4, 0, 0}, + {136750000, 136750000, 1, 136, 2, 1, 1, 3, 0, 3, 4, 0, 0xcfffff}, + {147180000, 147180000, 1, 147, 2, 1, 1, 3, 0, 3, 4, 0, 0x2e147a}, + {148352000, 148352000, 1, 98, 1, 1, 1, 1, 2, 2, 2, 0, 0xe6ae6b}, + {148500000, 148500000, 1, 99, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + {154000000, 154000000, 1, 154, 2, 1, 1, 3, 0, 3, 4, 0, 0}, + {156000000, 156000000, 1, 156, 2, 1, 1, 3, 0, 3, 4, 0, 0}, + {157000000, 157000000, 1, 157, 2, 1, 1, 3, 0, 3, 4, 0, 0}, + {162000000, 162000000, 1, 162, 2, 1, 1, 3, 0, 3, 4, 0, 0}, + {174250000, 174250000, 1, 145, 3, 0, 0, 5, 0, 2, 2, 0, 0x355555}, + {174500000, 174500000, 1, 174, 2, 1, 1, 3, 0, 3, 4, 0, 0x7fffff}, + {174570000, 174570000, 1, 174, 2, 1, 1, 3, 0, 3, 4, 0, 0x91eb84}, + {175500000, 175500000, 1, 175, 2, 1, 1, 3, 0, 3, 4, 0, 0x7fffff}, + {185590000, 185590000, 1, 185, 2, 1, 1, 3, 0, 3, 4, 0, 0x970a3c}, + {187000000, 187000000, 1, 187, 2, 1, 1, 3, 0, 3, 4, 0, 0}, + {241500000, 241500000, 1, 161, 1, 1, 1, 4, 0, 2, 2, 0, 0}, + {241700000, 241700000, 1, 241, 2, 1, 1, 3, 0, 3, 4, 0, 0xb33332}, + {262750000, 262750000, 1, 262, 2, 1, 1, 3, 0, 3, 4, 0, 0xcfffff}, + {296500000, 296500000, 1, 296, 2, 1, 1, 3, 0, 3, 4, 0, 0x7fffff}, + {296703000, 296703000, 1, 98, 0, 1, 1, 1, 0, 2, 2, 0, 0xe6ae6b}, + {297000000, 297000000, 1, 99, 0, 1, 1, 1, 0, 2, 2, 0, 0}, + {594000000, 594000000, 1, 99, 0, 2, 0, 1, 0, 1, 1, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +}; + +static const struct post_pll_config post_pll_cfg_table[] = { + {25200000, 1, 80, 13, 3, 1}, + {27000000, 1, 40, 11, 3, 1}, + {33750000, 1, 40, 11, 3, 1}, + {49000000, 1, 20, 1, 3, 3}, + {241700000, 1, 20, 1, 3, 3}, + {297000000, 4, 20, 0, 0, 3}, + {594000000, 4, 20, 0, 0, 0}, + { /* sentinel */ } +}; + +inline u8 hdmi_readb(struct starfive_hdmi *hdmi, u16 offset) +{ + return readl_relaxed(hdmi->regs + (offset) * 0x04); +} + +inline void hdmi_writeb(struct starfive_hdmi *hdmi, u16 offset, u32 val) +{ + writel_relaxed(val, hdmi->regs + (offset) * 0x04); +} + +inline void hdmi_modb(struct starfive_hdmi *hdmi, u16 offset, + u32 msk, u32 val) +{ + u8 temp = hdmi_readb(hdmi, offset) & ~msk; + + temp |= val & msk; + hdmi_writeb(hdmi, offset, temp); +} + +static int starfive_hdmi_enable_clk_deassert_rst(struct device *dev, struct starfive_hdmi *hdmi) +{ + int ret; + + ret = clk_prepare_enable(hdmi->sys_clk); + if (ret) { + dev_err(dev, "Cannot enable HDMI sys clock: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(hdmi->mclk); + if (ret) { + dev_err(dev, "Cannot enable HDMI mclk clock: %d\n", ret); + goto err_mclk; + } + ret = clk_prepare_enable(hdmi->bclk); + if (ret) { + dev_err(dev, "Cannot enable HDMI bclk clock: %d\n", ret); + goto err_bclk; + } + ret = reset_control_deassert(hdmi->tx_rst); + if (ret < 0) { + dev_err(dev, "failed to deassert tx_rst\n"); + goto err_rst; + } + return 0; + +err_rst: + clk_disable_unprepare(hdmi->bclk); +err_bclk: + clk_disable_unprepare(hdmi->mclk); +err_mclk: + clk_disable_unprepare(hdmi->sys_clk); + return ret; +} + +static void starfive_hdmi_disable_clk_assert_rst(struct device *dev, struct starfive_hdmi *hdmi) +{ + int ret; + + ret = reset_control_assert(hdmi->tx_rst); + if (ret < 0) + dev_err(dev, "failed to assert tx_rst\n"); + + clk_disable_unprepare(hdmi->sys_clk); + clk_disable_unprepare(hdmi->mclk); + clk_disable_unprepare(hdmi->bclk); +} + +#ifdef CONFIG_PM_SLEEP +static int hdmi_system_pm_suspend(struct device *dev) +{ + return pm_runtime_force_suspend(dev); +} + +static int hdmi_system_pm_resume(struct device *dev) +{ + return pm_runtime_force_resume(dev); +} +#endif + +#ifdef CONFIG_PM +static int hdmi_runtime_suspend(struct device *dev) +{ + struct starfive_hdmi *hdmi = dev_get_drvdata(dev); + + starfive_hdmi_disable_clk_assert_rst(dev, hdmi); + + return 0; +} + +static int hdmi_runtime_resume(struct device *dev) +{ + struct starfive_hdmi *hdmi = dev_get_drvdata(dev); + + return starfive_hdmi_enable_clk_deassert_rst(dev, hdmi); +} +#endif + +static void starfive_hdmi_tx_phy_power_down(struct starfive_hdmi *hdmi) +{ + hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF); +} + +static void starfive_hdmi_tx_phy_power_on(struct starfive_hdmi *hdmi) +{ + hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON); +} + +static void starfive_hdmi_config_pll(struct starfive_hdmi *hdmi) +{ + u32 val; + u8 reg_1ad_value = hdmi->post_cfg->post_div_en ? + hdmi->post_cfg->postdiv : 0x00; + u8 reg_1aa_value = hdmi->post_cfg->post_div_en ? + 0x0e : 0x02; + + hdmi_writeb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN); + hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1, + STARFIVE_POST_PLL_POST_DIV_ENABLE | + STARFIVE_POST_PLL_REFCLK_SEL_TMDS | + STARFIVE_POST_PLL_POWER_DOWN); + hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_1, STARFIVE_PRE_PLL_PRE_DIV(hdmi->pre_cfg->prediv)); + + val = STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE | STARFIVE_SPREAD_SPECTRUM_MOD_DOWN; + if (!hdmi->pre_cfg->fracdiv) + val |= STARFIVE_PRE_PLL_FRAC_DIV_DISABLE; + hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_2, + STARFIVE_PRE_PLL_FB_DIV_11_8(hdmi->pre_cfg->fbdiv) | val); + hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_3, + STARFIVE_PRE_PLL_FB_DIV_7_0(hdmi->pre_cfg->fbdiv)); + hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_4, + STARFIVE_PRE_PLL_TMDSCLK_DIV_C(hdmi->pre_cfg->tmds_div_c) | + STARFIVE_PRE_PLL_TMDSCLK_DIV_A(hdmi->pre_cfg->tmds_div_a) | + STARFIVE_PRE_PLL_TMDSCLK_DIV_B(hdmi->pre_cfg->tmds_div_b)); + + if (hdmi->pre_cfg->fracdiv) { + hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_L, + STARFIVE_PRE_PLL_FRAC_DIV_7_0(hdmi->pre_cfg->fracdiv)); + hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_M, + STARFIVE_PRE_PLL_FRAC_DIV_15_8(hdmi->pre_cfg->fracdiv)); + hdmi_writeb(hdmi, STARFIVE_PRE_PLL_FRAC_DIV_H, + STARFIVE_PRE_PLL_FRAC_DIV_23_16(hdmi->pre_cfg->fracdiv)); + } + + hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_5, + STARFIVE_PRE_PLL_PCLK_DIV_A(hdmi->pre_cfg->pclk_div_a) | + STARFIVE_PRE_PLL_PCLK_DIV_B(hdmi->pre_cfg->pclk_div_b)); + hdmi_writeb(hdmi, STARFIVE_PRE_PLL_DIV_6, + STARFIVE_PRE_PLL_PCLK_DIV_C(hdmi->pre_cfg->pclk_div_c) | + STARFIVE_PRE_PLL_PCLK_DIV_D(hdmi->pre_cfg->pclk_div_d)); + + /*pre-pll power down*/ + hdmi_modb(hdmi, STARFIVE_PRE_PLL_CONTROL, STARFIVE_PRE_PLL_POWER_DOWN, 0); + + hdmi_modb(hdmi, STARFIVE_POST_PLL_DIV_2, STARFIVE_POST_PLL_Pre_DIV_MASK, + STARFIVE_POST_PLL_PRE_DIV(hdmi->post_cfg->prediv)); + hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_3, hdmi->post_cfg->fbdiv & 0xff); + hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_4, reg_1ad_value); + hdmi_writeb(hdmi, STARFIVE_POST_PLL_DIV_1, reg_1aa_value); +} + +static void starfive_hdmi_tmds_driver_on(struct starfive_hdmi *hdmi) +{ + hdmi_modb(hdmi, STARFIVE_TMDS_CONTROL, + STARFIVE_TMDS_DRIVER_ENABLE, STARFIVE_TMDS_DRIVER_ENABLE); +} + +static void starfive_hdmi_sync_tmds(struct starfive_hdmi *hdmi) +{ + /*first send 0 to this bit, then send 1 and keep 1 into this bit*/ + hdmi_writeb(hdmi, HDMI_SYNC, 0x0); + hdmi_writeb(hdmi, HDMI_SYNC, 0x1); +} + +static void starfive_hdmi_i2c_init(struct starfive_hdmi *hdmi) +{ + int ddc_bus_freq; + + ddc_bus_freq = (clk_get_rate(hdmi->sys_clk) >> 2) / HDMI_SCL_RATE; + + hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF); + hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF); + + /* Clear the EDID interrupt flag and mute the interrupt */ + hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0); + hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY); +} + +static const +struct pre_pll_config *starfive_hdmi_phy_get_pre_pll_cfg(struct starfive_hdmi *hdmi, + unsigned long rate) +{ + const struct pre_pll_config *cfg = pre_pll_cfg_table; + + rate = (rate / 1000) * 1000; + for (; cfg->pixclock != 0; cfg++) + if (cfg->tmdsclock == rate && cfg->pixclock == rate) + break; + + if (cfg->pixclock == 0) + return ERR_PTR(-EINVAL); + + return cfg; +} + +static int starfive_hdmi_phy_clk_set_rate(struct starfive_hdmi *hdmi) +{ + hdmi->post_cfg = post_pll_cfg_table; + + hdmi->pre_cfg = starfive_hdmi_phy_get_pre_pll_cfg(hdmi, hdmi->tmds_rate); + if (IS_ERR(hdmi->pre_cfg)) + return PTR_ERR(hdmi->pre_cfg); + + for (; hdmi->post_cfg->tmdsclock != 0; hdmi->post_cfg++) + if (hdmi->tmds_rate <= hdmi->post_cfg->tmdsclock) + break; + + starfive_hdmi_config_pll(hdmi); + + return 0; +} + +static int starfive_hdmi_config_video_timing(struct starfive_hdmi *hdmi, + struct drm_display_mode *mode) +{ + int value; + /* Set detail external video timing */ + value = mode->htotal; + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF); + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF); + + value = mode->htotal - mode->hdisplay; + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF); + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF); + + value = mode->htotal - mode->hsync_start; + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF); + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF); + + value = mode->hsync_end - mode->hsync_start; + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF); + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF); + + value = mode->vtotal; + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF); + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF); + + value = mode->vtotal - mode->vdisplay; + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF); + + value = mode->vtotal - mode->vsync_start; + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF); + + value = mode->vsync_end - mode->vsync_start; + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF); + + /* Set detail external video timing polarity and interlace mode */ + value = v_EXTERANL_VIDEO(1); + value |= mode->flags & DRM_MODE_FLAG_PHSYNC ? + v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0); + value |= mode->flags & DRM_MODE_FLAG_PVSYNC ? + v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0); + value |= mode->flags & DRM_MODE_FLAG_INTERLACE ? + v_INETLACE(1) : v_INETLACE(0); + + hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value); + return 0; +} + +static int starfive_hdmi_setup(struct starfive_hdmi *hdmi, + struct drm_display_mode *mode) +{ + hdmi_modb(hdmi, STARFIVE_BIAS_CONTROL, STARFIVE_BIAS_ENABLE, STARFIVE_BIAS_ENABLE); + hdmi_writeb(hdmi, STARFIVE_RX_CONTROL, STARFIVE_RX_ENABLE); + hdmi->hdmi_data.vic = drm_match_cea_mode(mode); + + hdmi->tmds_rate = mode->clock * 1000; + starfive_hdmi_phy_clk_set_rate(hdmi); + + while (!(hdmi_readb(hdmi, STARFIVE_PRE_PLL_LOCK_STATUS) & 0x1)) + continue; + while (!(hdmi_readb(hdmi, STARFIVE_POST_PLL_LOCK_STATUS) & 0x1)) + continue; + + /*turn on LDO*/ + hdmi_writeb(hdmi, STARFIVE_LDO_CONTROL, STARFIVE_LDO_ENABLE); + /*turn on serializer*/ + hdmi_writeb(hdmi, STARFIVE_SERIALIER_CONTROL, STARFIVE_SERIALIER_ENABLE); + + starfive_hdmi_tx_phy_power_down(hdmi); + starfive_hdmi_config_video_timing(hdmi, mode); + starfive_hdmi_tx_phy_power_on(hdmi); + + starfive_hdmi_tmds_driver_on(hdmi); + starfive_hdmi_sync_tmds(hdmi); + + return 0; +} + +static void starfive_hdmi_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ + struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder); + + memcpy(&hdmi->previous_mode, adj_mode, sizeof(hdmi->previous_mode)); +} + +static void starfive_hdmi_encoder_enable(struct drm_encoder *encoder) +{ + struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder); + int ret, idx; + struct drm_device *drm = hdmi->connector.dev; + + if (drm && !drm_dev_enter(drm, &idx)) + return; + + ret = pm_runtime_get_sync(hdmi->dev); + if (ret < 0) + return; + mdelay(10); + starfive_hdmi_setup(hdmi, &hdmi->previous_mode); + + if (drm) + drm_dev_exit(idx); +} + +static void starfive_hdmi_encoder_disable(struct drm_encoder *encoder) +{ + struct starfive_hdmi *hdmi = encoder_to_hdmi(encoder); + + int idx; + struct drm_device *drm = hdmi->connector.dev; + + if (drm && !drm_dev_enter(drm, &idx)) + return; + + pm_runtime_put(hdmi->dev); + + if (drm) + drm_dev_exit(idx); +} + +static int +starfive_hdmi_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct drm_display_mode *mode = &crtc_state->adjusted_mode; + + const struct pre_pll_config *cfg = pre_pll_cfg_table; + int pclk = mode->clock * 1000; + bool valid = false; + + for (; cfg->pixclock != 0; cfg++) { + if (pclk == cfg->pixclock) { + if (pclk > 297000000) + continue; + + valid = true; + break; + } + } + + return (valid) ? 0 : -EINVAL; +} + +static const struct drm_encoder_helper_funcs starfive_hdmi_encoder_helper_funcs = { + .enable = starfive_hdmi_encoder_enable, + .disable = starfive_hdmi_encoder_disable, + .mode_set = starfive_hdmi_encoder_mode_set, + .atomic_check = starfive_hdmi_encoder_atomic_check, +}; + +static enum drm_connector_status +starfive_hdmi_connector_detect(struct drm_connector *connector, bool force) +{ + struct starfive_hdmi *hdmi = connector_to_hdmi(connector); + struct drm_device *drm = hdmi->connector.dev; + int ret; + int idx; + + if (drm && !drm_dev_enter(drm, &idx)) + return connector_status_disconnected; + + ret = pm_runtime_get_sync(hdmi->dev); + if (ret < 0) + return ret; + + ret = (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ? + connector_status_connected : connector_status_disconnected; + pm_runtime_put(hdmi->dev); + + if (drm) + drm_dev_exit(idx); + + return ret; +} + +static int starfive_hdmi_connector_get_modes(struct drm_connector *connector) +{ + struct starfive_hdmi *hdmi = connector_to_hdmi(connector); + struct edid *edid; + int ret = 0; + + if (!hdmi->ddc) + return 0; + ret = pm_runtime_get_sync(hdmi->dev); + if (ret < 0) + return ret; + + edid = drm_get_edid(connector, hdmi->ddc); + if (edid) { + hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid); + hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid); + drm_connector_update_edid_property(connector, edid); + ret = drm_add_edid_modes(connector, edid); + kfree(edid); + } + pm_runtime_put(hdmi->dev); + + return ret; +} + +static enum drm_mode_status +starfive_hdmi_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + const struct pre_pll_config *cfg = pre_pll_cfg_table; + int pclk = mode->clock * 1000; + bool valid = false; + + for (; cfg->pixclock != 0; cfg++) { + if (pclk == cfg->pixclock) { + if (pclk > 297000000) + continue; + + valid = true; + break; + } + } + + return (valid) ? MODE_OK : MODE_BAD; +} + +static int +starfive_hdmi_probe_single_connector_modes(struct drm_connector *connector, + u32 maxX, u32 maxY) +{ + return drm_helper_probe_single_connector_modes(connector, 3840, 2160); +} + +static const struct drm_connector_funcs starfive_hdmi_connector_funcs = { + .fill_modes = starfive_hdmi_probe_single_connector_modes, + .detect = starfive_hdmi_connector_detect, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static struct drm_connector_helper_funcs starfive_hdmi_connector_helper_funcs = { + .get_modes = starfive_hdmi_connector_get_modes, + .mode_valid = starfive_hdmi_connector_mode_valid, +}; + +static int starfive_hdmi_register(struct drm_device *drm, struct starfive_hdmi *hdmi) +{ + struct drm_encoder *encoder = &hdmi->encoder; + struct device *dev = hdmi->dev; + + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); + + /* + * If we failed to find the CRTC(s) which this encoder is + * supposed to be connected to, it's because the CRTC has + * not been registered yet. Defer probing, and hope that + * the required CRTC is added later. + */ + if (encoder->possible_crtcs == 0) + return -EPROBE_DEFER; + + drm_encoder_helper_add(encoder, &starfive_hdmi_encoder_helper_funcs); + drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); + + hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD; + + drm_connector_helper_add(&hdmi->connector, + &starfive_hdmi_connector_helper_funcs); + drmm_connector_init(drm, &hdmi->connector, + &starfive_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA, + hdmi->ddc); + + drm_connector_attach_encoder(&hdmi->connector, encoder); + + return 0; +} + +static irqreturn_t starfive_hdmi_i2c_irq(struct starfive_hdmi *hdmi) +{ + struct starfive_hdmi_i2c *i2c = hdmi->i2c; + u8 stat; + + stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1); + if (!(stat & m_INT_EDID_READY)) + return IRQ_NONE; + + /* Clear HDMI EDID interrupt flag */ + hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY); + + complete(&i2c->cmp); + + return IRQ_HANDLED; +} + +static irqreturn_t starfive_hdmi_hardirq(int irq, void *dev_id) +{ + struct starfive_hdmi *hdmi = dev_id; + irqreturn_t ret = IRQ_NONE; + u8 interrupt; + + if (hdmi->i2c) + ret = starfive_hdmi_i2c_irq(hdmi); + + interrupt = hdmi_readb(hdmi, HDMI_STATUS); + if (interrupt & m_INT_HOTPLUG) { + hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG); + ret = IRQ_WAKE_THREAD; + } + + return ret; +} + +static irqreturn_t starfive_hdmi_irq(int irq, void *dev_id) +{ + struct starfive_hdmi *hdmi = dev_id; + + drm_connector_helper_hpd_irq_event(&hdmi->connector); + + return IRQ_HANDLED; +} + +static int starfive_hdmi_i2c_read(struct starfive_hdmi *hdmi, struct i2c_msg *msgs) +{ + int length = msgs->len; + u8 *buf = msgs->buf; + int ret; + + ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10); + if (!ret) + return -EAGAIN; + + while (length--) + *buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR); + + return 0; +} + +static int starfive_hdmi_i2c_write(struct starfive_hdmi *hdmi, struct i2c_msg *msgs) +{ + /* + * The DDC module only support read EDID message, so + * we assume that each word write to this i2c adapter + * should be the offset of EDID word address. + */ + if (msgs->len != 1 || + (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR)) + return -EINVAL; + + reinit_completion(&hdmi->i2c->cmp); + + if (msgs->addr == DDC_SEGMENT_ADDR) + hdmi->i2c->segment_addr = msgs->buf[0]; + if (msgs->addr == DDC_ADDR) + hdmi->i2c->ddc_addr = msgs->buf[0]; + + /* Set edid fifo first addr */ + hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00); + + /* Set edid word address 0x00/0x80 */ + hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr); + + /* Set edid segment pointer */ + hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr); + + return 0; +} + +static int starfive_hdmi_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct starfive_hdmi *hdmi = i2c_get_adapdata(adap); + struct starfive_hdmi_i2c *i2c = hdmi->i2c; + int i, ret = 0; + + mutex_lock(&i2c->lock); + + /* Clear the EDID interrupt flag and unmute the interrupt */ + hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY); + hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY); + + for (i = 0; i < num; i++) { + DRM_DEV_DEBUG(hdmi->dev, + "xfer: num: %d/%d, len: %d, flags: %#x\n", + i + 1, num, msgs[i].len, msgs[i].flags); + + if (msgs[i].flags & I2C_M_RD) + ret = starfive_hdmi_i2c_read(hdmi, &msgs[i]); + else + ret = starfive_hdmi_i2c_write(hdmi, &msgs[i]); + + if (ret < 0) + break; + } + + if (!ret) + ret = num; + + /* Mute HDMI EDID interrupt */ + hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0); + + mutex_unlock(&i2c->lock); + + return ret; +} + +static u32 starfive_hdmi_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm starfive_hdmi_algorithm = { + .master_xfer = starfive_hdmi_i2c_xfer, + .functionality = starfive_hdmi_i2c_func, +}; + +static struct i2c_adapter *starfive_hdmi_i2c_adapter(struct starfive_hdmi *hdmi) +{ + struct i2c_adapter *adap; + struct starfive_hdmi_i2c *i2c; + int ret; + + i2c = 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) +{ + hdmi->sys_clk = devm_clk_get(dev, "sysclk"); + if (IS_ERR(hdmi->sys_clk)) { + dev_err(dev, "Unable to get HDMI sysclk clk\n"); + return PTR_ERR(hdmi->sys_clk); + } + hdmi->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(hdmi->mclk)) { + dev_err(dev, "Unable to get HDMI mclk clk\n"); + return PTR_ERR(hdmi->mclk); + } + hdmi->bclk = devm_clk_get(dev, "bclk"); + if (IS_ERR(hdmi->bclk)) { + dev_err(dev, "Unable to get HDMI bclk clk\n"); + return PTR_ERR(hdmi->bclk); + } + hdmi->tx_rst = devm_reset_control_get_shared(dev, "hdmi_tx"); + if (IS_ERR(hdmi->tx_rst)) { + dev_err(dev, "Unable to get HDMI tx rst\n"); + return PTR_ERR(hdmi->tx_rst); + } + return 0; +} + +static int starfive_hdmi_bind(struct device *dev, struct device *master, + void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = dev_get_drvdata(master); + struct starfive_hdmi *hdmi; + struct resource *iores; + int irq; + int ret; + + hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL); + if (!hdmi) + return -ENOMEM; + + hdmi->dev = dev; + hdmi->drm_dev = drm; + dev_set_drvdata(dev, hdmi); + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hdmi->regs = devm_ioremap_resource(dev, iores); + if (IS_ERR(hdmi->regs)) + return PTR_ERR(hdmi->regs); + + ret = starfive_hdmi_get_clk_rst(dev, hdmi); + if (ret < 0) + return ret; + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + goto err_put_runtime_pm; + } + + hdmi->ddc = starfive_hdmi_i2c_adapter(hdmi); + if (IS_ERR(hdmi->ddc)) { + ret = PTR_ERR(hdmi->ddc); + hdmi->ddc = NULL; + goto err_put_runtime_pm; + } + + starfive_hdmi_i2c_init(hdmi); + + ret = starfive_hdmi_register(drm, hdmi); + if (ret) + goto err_put_adapter; + + /* Unmute hotplug interrupt */ + hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1)); + + ret = devm_request_threaded_irq(dev, irq, starfive_hdmi_hardirq, + starfive_hdmi_irq, IRQF_SHARED, + dev_name(dev), hdmi); + if (ret < 0) + goto err_put_adapter; + + pm_runtime_put_sync(dev); + + return 0; + +err_put_adapter: + i2c_put_adapter(hdmi->ddc); +err_put_runtime_pm: + pm_runtime_put_sync(dev); + + return ret; +} + +static const struct component_ops starfive_hdmi_ops = { + .bind = starfive_hdmi_bind, +}; + +static int starfive_hdmi_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &starfive_hdmi_ops); +} + +static int starfive_hdmi_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &starfive_hdmi_ops); + + return 0; +} + +static const struct dev_pm_ops hdmi_pm_ops = { + SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(hdmi_system_pm_suspend, hdmi_system_pm_resume) +}; + +static const struct of_device_id starfive_hdmi_dt_ids[] = { + { .compatible = "starfive,jh7110-inno-hdmi",}, + {}, +}; +MODULE_DEVICE_TABLE(of, starfive_hdmi_dt_ids); + +struct platform_driver starfive_hdmi_driver = { + .probe = starfive_hdmi_probe, + .remove = starfive_hdmi_remove, + .driver = { + .name = "starfive-hdmi", + .of_match_table = starfive_hdmi_dt_ids, + .pm = &hdmi_pm_ops, + }, +}; + +MODULE_AUTHOR("StarFive Corporation"); +MODULE_DESCRIPTION("Starfive HDMI Driver"); diff --git a/drivers/gpu/drm/verisilicon/starfive_hdmi.h b/drivers/gpu/drm/verisilicon/starfive_hdmi.h new file mode 100644 index 000000000..91c5ca30a --- /dev/null +++ b/drivers/gpu/drm/verisilicon/starfive_hdmi.h @@ -0,0 +1,295 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2023 StarFive Technology Co., Ltd. + */ + +#ifndef __STARFIVE_HDMI_H__ +#define __STARFIVE_HDMI_H__ + +#include <drm/bridge/dw_hdmi.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_of.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_simple_kms_helper.h> +#include <linux/bitfield.h> +#include <linux/bits.h> + +#define DDC_SEGMENT_ADDR 0x30 + +#define HDMI_SCL_RATE (100 * 1000) +#define DDC_BUS_FREQ_L 0x4b +#define DDC_BUS_FREQ_H 0x4c + +#define HDMI_SYS_CTRL 0x00 +#define m_RST_ANALOG BIT(6) +#define v_RST_ANALOG 0 +#define v_NOT_RST_ANALOG BIT(6) +#define m_RST_DIGITAL BIT(5) +#define v_RST_DIGITAL 0 +#define v_NOT_RST_DIGITAL BIT(5) +#define m_REG_CLK_INV BIT(4) +#define v_REG_CLK_NOT_INV 0 +#define v_REG_CLK_INV BIT(4) +#define m_VCLK_INV BIT(3) +#define v_VCLK_NOT_INV 0 +#define v_VCLK_INV BIT(3) +#define m_REG_CLK_SOURCE BIT(2) +#define v_REG_CLK_SOURCE_TMDS 0 +#define v_REG_CLK_SOURCE_SYS BIT(2) +#define m_POWER BIT(1) +#define v_PWR_ON 0 +#define v_PWR_OFF BIT(1) +#define m_INT_POL BIT(0) +#define v_INT_POL_HIGH 1 +#define v_INT_POL_LOW 0 + +#define HDMI_AV_MUTE 0x05 +#define m_AVMUTE_CLEAR BIT(7) +#define m_AVMUTE_ENABLE BIT(6) +#define m_AUDIO_MUTE BIT(1) +#define m_VIDEO_BLACK BIT(0) +#define v_AVMUTE_CLEAR(n) ((n) << 7) +#define v_AVMUTE_ENABLE(n) ((n) << 6) +#define v_AUDIO_MUTE(n) ((n) << 1) +#define v_VIDEO_MUTE(n) ((n) << 0) + +#define HDMI_VIDEO_TIMING_CTL 0x08 +#define v_VSYNC_POLARITY(n) ((n) << 3) +#define v_HSYNC_POLARITY(n) ((n) << 2) +#define v_INETLACE(n) ((n) << 1) +#define v_EXTERANL_VIDEO(n) ((n) << 0) + +#define HDMI_VIDEO_EXT_HTOTAL_L 0x09 +#define HDMI_VIDEO_EXT_HTOTAL_H 0x0a +#define HDMI_VIDEO_EXT_HBLANK_L 0x0b +#define HDMI_VIDEO_EXT_HBLANK_H 0x0c +#define HDMI_VIDEO_EXT_HDELAY_L 0x0d +#define HDMI_VIDEO_EXT_HDELAY_H 0x0e +#define HDMI_VIDEO_EXT_HDURATION_L 0x0f +#define HDMI_VIDEO_EXT_HDURATION_H 0x10 +#define HDMI_VIDEO_EXT_VTOTAL_L 0x11 +#define HDMI_VIDEO_EXT_VTOTAL_H 0x12 +#define HDMI_VIDEO_EXT_VBLANK 0x13 +#define HDMI_VIDEO_EXT_VDELAY 0x14 +#define HDMI_VIDEO_EXT_VDURATION 0x15 + +#define HDMI_EDID_SEGMENT_POINTER 0x4d +#define HDMI_EDID_WORD_ADDR 0x4e +#define HDMI_EDID_FIFO_OFFSET 0x4f +#define HDMI_EDID_FIFO_ADDR 0x50 + +#define HDMI_INTERRUPT_MASK1 0xc0 +#define HDMI_INTERRUPT_STATUS1 0xc1 +#define m_INT_ACTIVE_VSYNC BIT(5) +#define m_INT_EDID_READY BIT(2) + +#define HDMI_STATUS 0xc8 +#define m_HOTPLUG BIT(7) +#define m_MASK_INT_HOTPLUG BIT(5) +#define m_INT_HOTPLUG BIT(1) +#define v_MASK_INT_HOTPLUG(n) (((n) & 0x1) << 5) + +#define HDMI_SYNC 0xce + +#define UPDATE(x, h, l) FIELD_PREP(GENMASK(h, l), x) + +/* REG: 0x1a0 */ +#define STARFIVE_PRE_PLL_CONTROL 0x1a0 +#define STARFIVE_PCLK_VCO_DIV_5_MASK BIT(1) +#define STARFIVE_PCLK_VCO_DIV_5(x) UPDATE(x, 1, 1) +#define STARFIVE_PRE_PLL_POWER_DOWN BIT(0) + +/* REG: 0x1a1 */ +#define STARFIVE_PRE_PLL_DIV_1 0x1a1 +#define STARFIVE_PRE_PLL_PRE_DIV_MASK GENMASK(5, 0) +#define STARFIVE_PRE_PLL_PRE_DIV(x) UPDATE(x, 5, 0) + +/* REG: 0x1a2 */ +#define STARFIVE_PRE_PLL_DIV_2 0x1a2 +#define STARFIVE_SPREAD_SPECTRUM_MOD_DOWN BIT(7) +#define STARFIVE_SPREAD_SPECTRUM_MOD_DISABLE BIT(6) +#define STARFIVE_PRE_PLL_FRAC_DIV_DISABLE UPDATE(3, 5, 4) +#define STARFIVE_PRE_PLL_FB_DIV_11_8_MASK GENMASK(3, 0) +#define STARFIVE_PRE_PLL_FB_DIV_11_8(x) UPDATE((x) >> 8, 3, 0) + +/* REG: 0x1a3 */ +#define STARFIVE_PRE_PLL_DIV_3 0x1a3 +#define STARFIVE_PRE_PLL_FB_DIV_7_0(x) UPDATE(x, 7, 0) + +/* REG: 0x1a4*/ +#define STARFIVE_PRE_PLL_DIV_4 0x1a4 +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C_MASK GENMASK(1, 0) +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_C(x) UPDATE(x, 1, 0) +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B_MASK GENMASK(3, 2) +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_B(x) UPDATE(x, 3, 2) +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A_MASK GENMASK(5, 4) +#define STARFIVE_PRE_PLL_TMDSCLK_DIV_A(x) UPDATE(x, 5, 4) + +/* REG: 0x1a5 */ +#define STARFIVE_PRE_PLL_DIV_5 0x1a5 +#define STARFIVE_PRE_PLL_PCLK_DIV_B_SHIFT 5 +#define STARFIVE_PRE_PLL_PCLK_DIV_B_MASK GENMASK(6, 5) +#define STARFIVE_PRE_PLL_PCLK_DIV_B(x) UPDATE(x, 6, 5) +#define STARFIVE_PRE_PLL_PCLK_DIV_A_MASK GENMASK(4, 0) +#define STARFIVE_PRE_PLL_PCLK_DIV_A(x) UPDATE(x, 4, 0) + +/* REG: 0x1a6 */ +#define STARFIVE_PRE_PLL_DIV_6 0x1a6 +#define STARFIVE_PRE_PLL_PCLK_DIV_C_SHIFT 5 +#define STARFIVE_PRE_PLL_PCLK_DIV_C_MASK GENMASK(6, 5) +#define STARFIVE_PRE_PLL_PCLK_DIV_C(x) UPDATE(x, 6, 5) +#define STARFIVE_PRE_PLL_PCLK_DIV_D_MASK GENMASK(4, 0) +#define STARFIVE_PRE_PLL_PCLK_DIV_D(x) UPDATE(x, 4, 0) + +/* REG: 0x1a9 */ +#define STARFIVE_PRE_PLL_LOCK_STATUS 0x1a9 + +/* REG: 0x1aa */ +#define STARFIVE_POST_PLL_DIV_1 0x1aa +#define STARFIVE_POST_PLL_POST_DIV_ENABLE GENMASK(3, 2) +#define STARFIVE_POST_PLL_REFCLK_SEL_TMDS BIT(1) +#define STARFIVE_POST_PLL_POWER_DOWN BIT(0) +#define STARFIVE_POST_PLL_FB_DIV_8(x) UPDATE(((x) >> 8) << 4, 4, 4) + +/* REG:0x1ab */ +#define STARFIVE_POST_PLL_DIV_2 0x1ab +#define STARFIVE_POST_PLL_Pre_DIV_MASK GENMASK(5, 0) +#define STARFIVE_POST_PLL_PRE_DIV(x) UPDATE(x, 5, 0) + +/* REG: 0x1ac */ +#define STARFIVE_POST_PLL_DIV_3 0x1ac +#define STARFIVE_POST_PLL_FB_DIV_7_0(x) UPDATE(x, 7, 0) + +/* REG: 0x1ad */ +#define STARFIVE_POST_PLL_DIV_4 0x1ad +#define STARFIVE_POST_PLL_POST_DIV_MASK GENMASK(2, 0) +#define STARFIVE_POST_PLL_POST_DIV_2 0x0 +#define STARFIVE_POST_PLL_POST_DIV_4 0x1 +#define STARFIVE_POST_PLL_POST_DIV_8 0x3 + +/* REG: 0x1af */ +#define STARFIVE_POST_PLL_LOCK_STATUS 0x1af + +/* REG: 0x1b0 */ +#define STARFIVE_BIAS_CONTROL 0x1b0 +#define STARFIVE_BIAS_ENABLE BIT(2) + +/* REG: 0x1b2 */ +#define STARFIVE_TMDS_CONTROL 0x1b2 +#define STARFIVE_TMDS_CLK_DRIVER_EN BIT(3) +#define STARFIVE_TMDS_D2_DRIVER_EN BIT(2) +#define STARFIVE_TMDS_D1_DRIVER_EN BIT(1) +#define STARFIVE_TMDS_D0_DRIVER_EN BIT(0) +#define STARFIVE_TMDS_DRIVER_ENABLE (STARFIVE_TMDS_CLK_DRIVER_EN | \ + STARFIVE_TMDS_D2_DRIVER_EN | \ + STARFIVE_TMDS_D1_DRIVER_EN | \ + STARFIVE_TMDS_D0_DRIVER_EN) + +/* REG: 0x1b4 */ +#define STARFIVE_LDO_CONTROL 0x1b4 +#define STARFIVE_LDO_D2_EN BIT(2) +#define STARFIVE_LDO_D1_EN BIT(1) +#define STARFIVE_LDO_D0_EN BIT(0) +#define STARFIVE_LDO_ENABLE (STARFIVE_LDO_D2_EN | \ + STARFIVE_LDO_D1_EN | \ + STARFIVE_LDO_D0_EN) + +/* REG: 0x1be */ +#define STARFIVE_SERIALIER_CONTROL 0x1be +#define STARFIVE_SERIALIER_D2_EN BIT(6) +#define STARFIVE_SERIALIER_D1_EN BIT(5) +#define STARFIVE_SERIALIER_D0_EN BIT(4) +#define STARFIVE_SERIALIER_EN BIT(0) + +#define STARFIVE_SERIALIER_ENABLE (STARFIVE_SERIALIER_D2_EN | \ + STARFIVE_SERIALIER_D1_EN | \ + STARFIVE_SERIALIER_D0_EN | \ + STARFIVE_SERIALIER_EN) + +/* REG: 0x1cc */ +#define STARFIVE_RX_CONTROL 0x1cc +#define STARFIVE_RX_EN BIT(3) +#define STARFIVE_RX_CHANNEL_2_EN BIT(2) +#define STARFIVE_RX_CHANNEL_1_EN BIT(1) +#define STARFIVE_RX_CHANNEL_0_EN BIT(0) +#define STARFIVE_RX_ENABLE (STARFIVE_RX_EN | \ + STARFIVE_RX_CHANNEL_2_EN | \ + STARFIVE_RX_CHANNEL_1_EN | \ + STARFIVE_RX_CHANNEL_0_EN) + +/* REG: 0x1d1 */ +#define STARFIVE_PRE_PLL_FRAC_DIV_H 0x1d1 +#define STARFIVE_PRE_PLL_FRAC_DIV_23_16(x) UPDATE((x) >> 16, 7, 0) +/* REG: 0x1d2 */ +#define STARFIVE_PRE_PLL_FRAC_DIV_M 0x1d2 +#define STARFIVE_PRE_PLL_FRAC_DIV_15_8(x) UPDATE((x) >> 8, 7, 0) +/* REG: 0x1d3 */ +#define STARFIVE_PRE_PLL_FRAC_DIV_L 0x1d3 +#define STARFIVE_PRE_PLL_FRAC_DIV_7_0(x) UPDATE(x, 7, 0) + +struct pre_pll_config { + unsigned long pixclock; + unsigned long tmdsclock; + u8 prediv; + u16 fbdiv; + u8 tmds_div_a; + u8 tmds_div_b; + u8 tmds_div_c; + u8 pclk_div_a; + u8 pclk_div_b; + u8 pclk_div_c; + u8 pclk_div_d; + u8 vco_div_5_en; + u32 fracdiv; +}; + +struct post_pll_config { + unsigned long tmdsclock; + u8 prediv; + u16 fbdiv; + u8 postdiv; + u8 post_div_en; + u8 version; +}; + +struct phy_config { + unsigned long tmdsclock; + u8 regs[14]; +}; + +struct hdmi_data_info { + int vic; + bool sink_is_hdmi; + bool sink_has_audio; + unsigned int enc_in_format; + unsigned int enc_out_format; + unsigned int colorimetry; +}; + +struct starfive_hdmi { + struct device *dev; + struct drm_device *drm_dev; + + int irq; + struct clk *sys_clk; + struct clk *mclk; + struct clk *bclk; + struct reset_control *tx_rst; + void __iomem *regs; + + struct drm_connector connector; + struct drm_encoder encoder; + + struct starfive_hdmi_i2c *i2c; + struct i2c_adapter *ddc; + + unsigned long tmds_rate; + + struct hdmi_data_info hdmi_data; + struct drm_display_mode previous_mode; + const struct pre_pll_config *pre_cfg; + const struct post_pll_config *post_cfg; +}; + +#endif /* __STARFIVE_HDMI_H__ */
Hi,
On Tue, Aug 01, 2023 at 06:10:30PM +0800, Keith Zhao wrote:
add hdmi driver as encoder and connect
Signed-off-by: Keith Zhao keith.zhao@starfivetech.com
You've ignored most of the comments I made on the previous version, so please go back to that mail and fix everything you've ignored.
https://lore.kernel.org/dri-devel/ayygsdwzogu4ygkobs7zkroxicxtixtp5bxayn5vzk...
Maxime
linaro-mm-sig@lists.linaro.org