From patchwork Tue Aug 1 10:10:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Keith Zhao X-Patchwork-Id: 13336160 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 13595C001DF for ; Tue, 1 Aug 2023 10:11:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:CC:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=ENWdVnQIt8xyFUTQFpmVSvf5hA3Ub/RP0+f4G3LQZGE=; b=ekblawcU6IfM/B ZDU0NkYRD/9kf8q9F2o0XeAKzdI2ivCpjr7ii6Ki/0Y4IoWA9UiOrUTBiNI8VKilB52lwNLOeUm0f 12UQebv+OODWSJi152CzgBm30/Qmb8AI+giML6Jt5y07RaoqO/eKrYZ+Al9KxhbAo5KdCwgdcbTv6 9Gr17CHlEBmmZY9pGgMdSnJlkDwPgYNkTbGVLiKpNXmzQ3LGmRTEwm0d/vQNCSF7pN7rh8b217zWq 2KTVQtW+2YY8PveONJRgF1Dik2U11YoCv+92bFdVM5dfyGhhBzo4kFCqkuxzGIZf3H/+QoS88h7BZ jGlX06xGbLPEGYoCtiNg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1qQmLf-001jmF-1K; Tue, 01 Aug 2023 10:11:27 +0000 Received: from fd01.gateway.ufhost.com ([61.152.239.71]) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1qQmLc-001jZc-27 for linux-riscv@lists.infradead.org; Tue, 01 Aug 2023 10:11:26 +0000 Received: from EXMBX165.cuchost.com (unknown [175.102.18.54]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "EXMBX165", Issuer "EXMBX165" (not verified)) by fd01.gateway.ufhost.com (Postfix) with ESMTP id 545618080; Tue, 1 Aug 2023 18:10:33 +0800 (CST) Received: from EXMBX061.cuchost.com (172.16.6.61) by EXMBX165.cuchost.com (172.16.6.75) with Microsoft SMTP Server (TLS) id 15.0.1497.42; Tue, 1 Aug 2023 18:10:33 +0800 Received: from xiaofei.localdomain (180.164.60.184) by EXMBX061.cuchost.com (172.16.6.61) with Microsoft SMTP Server (TLS) id 15.0.1497.42; Tue, 1 Aug 2023 18:10:32 +0800 From: Keith Zhao To: , , , , , CC: David Airlie , Daniel Vetter , "Rob Herring" , Krzysztof Kozlowski , Conor Dooley , "Emil Renner Berthing" , Paul Walmsley , Palmer Dabbelt , Albert Ou , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , Philipp Zabel , Sumit Semwal , , Bjorn Andersson , "Heiko Stuebner" , Shawn Guo , Jagan Teki , Chris Morgan , Keith Zhao , Jack Zhu , Shengyang Chen , Changhuang Liang Subject: [PATCH v1 v1 1/7] MAINTAINERS: Update starfive maintainers Date: Tue, 1 Aug 2023 18:10:24 +0800 Message-ID: <20230801101030.2040-2-keith.zhao@starfivetech.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230801101030.2040-1-keith.zhao@starfivetech.com> References: <20230801101030.2040-1-keith.zhao@starfivetech.com> MIME-Version: 1.0 X-Originating-IP: [180.164.60.184] X-ClientProxiedBy: EXCAS064.cuchost.com (172.16.6.24) To EXMBX061.cuchost.com (172.16.6.61) X-YovoleRuleAgent: yovoleflag X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20230801_031124_875941_17053BE6 X-CRM114-Status: UNSURE ( 8.49 ) X-CRM114-Notice: Please train this message. X-BeenThere: linux-riscv@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-riscv" Errors-To: linux-riscv-bounces+linux-riscv=archiver.kernel.org@lists.infradead.org update starfive maintainers Signed-off-by: Keith Zhao --- 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 +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 M: Melissa Wen From patchwork Tue Aug 1 10:10:25 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Keith Zhao X-Patchwork-Id: 13336158 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 2314CC001E0 for ; Tue, 1 Aug 2023 10:11:32 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:CC:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=qXHE+q3x3DEem05+79IMFxHonrwZuXYtzIW1YDWKBNo=; b=ck/PNXlgKbXPp3 wWQ3dK2bZvcvG1qmPnAuiTBRCFsQUbruhbPztMGOjrX5WXdHQ4B/BUNieO2An3eAGxNaPtjrhKM91 dJy+9MW8qmAuKXORlfqh/rMpkDjbX4aXbb0hlH16dhbODrn4EO5PDeuv9y96+W15zmHHGN1CUN/CP sBXVrSwVTePy+YcR893jtQxJTSTziGaKkRXwVnImEAClX0mRzFKAfGzkxxiIZHz21ezGAf5vxVpfm J2W3yYH7MiZtkDe05gULrHjx7JDLWyrqEpnUTq+/zdKx/y6gXL7+d/3yv7KmK3iOF+7vaj6PS0/9i KvCpz1ywhiu9a03PAzVg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1qQmLb-001jk9-1v; Tue, 01 Aug 2023 10:11:23 +0000 Received: from fd01.gateway.ufhost.com ([61.152.239.71]) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1qQmLX-001jZb-22 for linux-riscv@lists.infradead.org; Tue, 01 Aug 2023 10:11:21 +0000 Received: from EXMBX166.cuchost.com (unknown [175.102.18.54]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "EXMBX166", Issuer "EXMBX166" (not verified)) by fd01.gateway.ufhost.com (Postfix) with ESMTP id BDD158086; Tue, 1 Aug 2023 18:10:33 +0800 (CST) Received: from EXMBX061.cuchost.com (172.16.6.61) by EXMBX166.cuchost.com (172.16.6.76) with Microsoft SMTP Server (TLS) id 15.0.1497.42; Tue, 1 Aug 2023 18:10:33 +0800 Received: from xiaofei.localdomain (180.164.60.184) by EXMBX061.cuchost.com (172.16.6.61) with Microsoft SMTP Server (TLS) id 15.0.1497.42; Tue, 1 Aug 2023 18:10:33 +0800 From: Keith Zhao To: , , , , , CC: David Airlie , Daniel Vetter , "Rob Herring" , Krzysztof Kozlowski , Conor Dooley , "Emil Renner Berthing" , Paul Walmsley , Palmer Dabbelt , Albert Ou , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , Philipp Zabel , Sumit Semwal , , Bjorn Andersson , "Heiko Stuebner" , Shawn Guo , Jagan Teki , Chris Morgan , Keith Zhao , Jack Zhu , Shengyang Chen , Changhuang Liang Subject: [PATCH v1 v1 2/7] dt-bindings: display: Add yamls for JH7110 display system Date: Tue, 1 Aug 2023 18:10:25 +0800 Message-ID: <20230801101030.2040-3-keith.zhao@starfivetech.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230801101030.2040-1-keith.zhao@starfivetech.com> References: <20230801101030.2040-1-keith.zhao@starfivetech.com> MIME-Version: 1.0 X-Originating-IP: [180.164.60.184] X-ClientProxiedBy: EXCAS064.cuchost.com (172.16.6.24) To EXMBX061.cuchost.com (172.16.6.61) X-YovoleRuleAgent: yovoleflag X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20230801_031120_012528_F5275065 X-CRM114-Status: GOOD ( 16.02 ) X-BeenThere: linux-riscv@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-riscv" Errors-To: linux-riscv-bounces+linux-riscv=archiver.kernel.org@lists.infradead.org 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 --- .../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.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Starfive DRM master device + +maintainers: + - Keith Zhao + - ShengYang Chen + +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 + +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.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Starfive JH7110 HDMI controller + +description: + The StarFive JH7110 SoC uses the HDMI signal transmiter based on innosilicon IP + to generate HDMI signal from its input and transmit the signal to the screen. + +maintainers: + - Keith Zhao + +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>; + }; + }; + }; From patchwork Tue Aug 1 10:10:26 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Keith Zhao X-Patchwork-Id: 13336155 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 80DBEEB64DD for ; Tue, 1 Aug 2023 10:11:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:CC:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=IWp3hCxYzbmzRS7pfHzLfsS0ldN0GQ9RupNk6ouon78=; b=cAOZrPpisiXsLa eblITDAdbJ2oaOP0DcNs1AdtoyNfiAPcA/Z6IQGJjiOzId+OV6iyeNUea1VPVUEf8s5mXmMccVxcA If0SH4eDhlZ8EfxTKmbFqm3gZKR31aFsJ/FxCfbrAq3C21Jogbns+U/Cy3roygxGi2r2hQEo9zElm 6TYxDTFScc2VSLf26WrzuYICQMqQuPwooj1RGtAys2miUr7MFi/T6YiF2y1zV9jAolCa8rqn3IDTT XSdL/+CF6b9KjvqLL7/t9EUUEsGhAbvNczKDfAVhU5zYyylJHNXOO395sAiiGPq5hUjHvRZSak7zP 8u1sUvkq9YwN08Cq5gBg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1qQmLc-001jkQ-0X; Tue, 01 Aug 2023 10:11:24 +0000 Received: from ex01.ufhost.com ([61.152.239.75]) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1qQmLY-001jZg-1f for linux-riscv@lists.infradead.org; Tue, 01 Aug 2023 10:11:23 +0000 Received: from EXMBX165.cuchost.com (unknown [175.102.18.54]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "EXMBX165", Issuer "EXMBX165" (not verified)) by ex01.ufhost.com (Postfix) with ESMTP id 2459C24E2AA; Tue, 1 Aug 2023 18:10:34 +0800 (CST) Received: from EXMBX061.cuchost.com (172.16.6.61) by EXMBX165.cuchost.com (172.16.6.75) with Microsoft SMTP Server (TLS) id 15.0.1497.42; Tue, 1 Aug 2023 18:10:34 +0800 Received: from xiaofei.localdomain (180.164.60.184) by EXMBX061.cuchost.com (172.16.6.61) with Microsoft SMTP Server (TLS) id 15.0.1497.42; Tue, 1 Aug 2023 18:10:33 +0800 From: Keith Zhao To: , , , , , CC: David Airlie , Daniel Vetter , "Rob Herring" , Krzysztof Kozlowski , Conor Dooley , "Emil Renner Berthing" , Paul Walmsley , Palmer Dabbelt , Albert Ou , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , Philipp Zabel , Sumit Semwal , , Bjorn Andersson , "Heiko Stuebner" , Shawn Guo , Jagan Teki , Chris Morgan , Keith Zhao , Jack Zhu , Shengyang Chen , Changhuang Liang Subject: [PATCH v1 v1 3/7] riscv: dts: starfive: jh7110: add dc controller and hdmi node Date: Tue, 1 Aug 2023 18:10:26 +0800 Message-ID: <20230801101030.2040-4-keith.zhao@starfivetech.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230801101030.2040-1-keith.zhao@starfivetech.com> References: <20230801101030.2040-1-keith.zhao@starfivetech.com> MIME-Version: 1.0 X-Originating-IP: [180.164.60.184] X-ClientProxiedBy: EXCAS064.cuchost.com (172.16.6.24) To EXMBX061.cuchost.com (172.16.6.61) X-YovoleRuleAgent: yovoleflag X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20230801_031120_838983_EC984FF8 X-CRM114-Status: UNSURE ( 9.96 ) X-CRM114-Notice: Please train this message. X-BeenThere: linux-riscv@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-riscv" Errors-To: linux-riscv-bounces+linux-riscv=archiver.kernel.org@lists.infradead.org Add the dc controller and hdmi node for the Starfive JH7110 SoC. Signed-off-by: Keith Zhao --- .../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 = ; + input-enable; + bias-pull-up; + }; + + hdmi-sda-pins { + pinmux = ; + input-enable; + bias-pull-up; + }; + + hdmi-cec-pins { + pinmux = ; + input-enable; + bias-pull-up; + }; + + hdmi-hpd-pins { + pinmux = ; + 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>; + }; }; }; From patchwork Tue Aug 1 10:10:27 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Keith Zhao X-Patchwork-Id: 13336154 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 17CDAC001E0 for ; Tue, 1 Aug 2023 10:11:17 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:CC:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=yKAaoafDfB4StHEnT1rv8pu+CMYdGcHC31scJeKDNC4=; b=pkB5Bdx7CgGo5D BNy4ZWT7n0rubjCDFrUHzQ/6jkEP+CmtuDjk+c1khsec5z80UBwmdOqKoMg31vfxAphpXECKDLBwO 1N2I6HFLd4RwNfhk7fC5r7mm+wzlf4O/U4tFWVmRv6+Xyg0TsBXcMTOrZu7x3yjji2ltX0s6AvXfI CxZPPQlukdnr/rk8U6xDl0ujCgovsXIX0yaDkoBzF6LleAt60xgu+NF/OWSm4mStIAVG+pV+RxbBd T+260Rf81H2pKA9KaRj00fcP0IkvsCkitri/OBIP+cQ1YwwbQEurccdnktWyr6/dtHAj3dlKQSOJJ zrmHmRNTfzf8nxEF1CcQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1qQmLQ-001jgG-2b; Tue, 01 Aug 2023 10:11:12 +0000 Received: from ex01.ufhost.com ([61.152.239.75]) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1qQmLN-001jZh-1x for linux-riscv@lists.infradead.org; Tue, 01 Aug 2023 10:11:11 +0000 Received: from EXMBX166.cuchost.com (unknown [175.102.18.54]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "EXMBX166", Issuer "EXMBX166" (not verified)) by ex01.ufhost.com (Postfix) with ESMTP id 530F024E2F2; Tue, 1 Aug 2023 18:10:35 +0800 (CST) Received: from EXMBX061.cuchost.com (172.16.6.61) by EXMBX166.cuchost.com (172.16.6.76) with Microsoft SMTP Server (TLS) id 15.0.1497.42; Tue, 1 Aug 2023 18:10:34 +0800 Received: from xiaofei.localdomain (180.164.60.184) by EXMBX061.cuchost.com (172.16.6.61) with Microsoft SMTP Server (TLS) id 15.0.1497.42; Tue, 1 Aug 2023 18:10:33 +0800 From: Keith Zhao To: , , , , , CC: David Airlie , Daniel Vetter , "Rob Herring" , Krzysztof Kozlowski , Conor Dooley , "Emil Renner Berthing" , Paul Walmsley , Palmer Dabbelt , Albert Ou , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , Philipp Zabel , Sumit Semwal , , Bjorn Andersson , "Heiko Stuebner" , Shawn Guo , Jagan Teki , Chris Morgan , Keith Zhao , Jack Zhu , Shengyang Chen , Changhuang Liang Subject: [PATCH v1 v1 4/7] drm/fourcc: Add drm/vs tiled modifiers Date: Tue, 1 Aug 2023 18:10:27 +0800 Message-ID: <20230801101030.2040-5-keith.zhao@starfivetech.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230801101030.2040-1-keith.zhao@starfivetech.com> References: <20230801101030.2040-1-keith.zhao@starfivetech.com> MIME-Version: 1.0 X-Originating-IP: [180.164.60.184] X-ClientProxiedBy: EXCAS064.cuchost.com (172.16.6.24) To EXMBX061.cuchost.com (172.16.6.61) X-YovoleRuleAgent: yovoleflag X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20230801_031109_952243_5D3A592D X-CRM114-Status: GOOD ( 12.61 ) X-BeenThere: linux-riscv@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-riscv" Errors-To: linux-riscv-bounces+linux-riscv=archiver.kernel.org@lists.infradead.org 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 --- 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 From patchwork Tue Aug 1 10:10:28 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Keith Zhao X-Patchwork-Id: 13336159 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 5E98CEB64DD for ; Tue, 1 Aug 2023 10:11:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:CC:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=RyyReG0rPY4IBzLEe3iIsdXi3aNi1ELfJgUoVCQd2lc=; b=R26GmdbcSDIiLq g93y/YR+idwKI1LikuvTRiA2+7slK4NzVPGv6RUOHJvcVfAkJE8c8H5b6aO+8nASVhdq67w1lpqry s2M5RjYuNWNSM01zUFrXPe/5ymVUxP9bohXYMCKYq0LhEWFEjI6BYOI/3lyk0L0JF3iR1IMUhqm96 oBf1OzykUUpBwiasPPEzEkcKz9HbenSp0zPMP25nymUGVs7f95BooQqBXEVqH4+U+yIt/6/43t4qP hP7kd7XhnDXlkwtlkGLJJdshVjMP3Noaw5lC0sB4R5smDc9qsTpRHqV3cApai1vRTNfN828YcC0xt SA7VsXavt5iQJBOeSQWg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1qQmLi-001jnV-0K; Tue, 01 Aug 2023 10:11:30 +0000 Received: from fd01.gateway.ufhost.com ([61.152.239.71]) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1qQmLd-001jZa-0n for linux-riscv@lists.infradead.org; Tue, 01 Aug 2023 10:11:28 +0000 Received: from EXMBX165.cuchost.com (unknown [175.102.18.54]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "EXMBX165", Issuer "EXMBX165" (not verified)) by fd01.gateway.ufhost.com (Postfix) with ESMTP id F05678016; Tue, 1 Aug 2023 18:10:34 +0800 (CST) Received: from EXMBX061.cuchost.com (172.16.6.61) by EXMBX165.cuchost.com (172.16.6.75) with Microsoft SMTP Server (TLS) id 15.0.1497.42; Tue, 1 Aug 2023 18:10:35 +0800 Received: from xiaofei.localdomain (180.164.60.184) by EXMBX061.cuchost.com (172.16.6.61) with Microsoft SMTP Server (TLS) id 15.0.1497.42; Tue, 1 Aug 2023 18:10:34 +0800 From: Keith Zhao To: , , , , , CC: David Airlie , Daniel Vetter , "Rob Herring" , Krzysztof Kozlowski , Conor Dooley , "Emil Renner Berthing" , Paul Walmsley , Palmer Dabbelt , Albert Ou , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , Philipp Zabel , Sumit Semwal , , Bjorn Andersson , "Heiko Stuebner" , Shawn Guo , Jagan Teki , Chris Morgan , Keith Zhao , Jack Zhu , Shengyang Chen , Changhuang Liang Subject: [PATCH v1 v1 5/7] drm/vs: Register DRM device Date: Tue, 1 Aug 2023 18:10:28 +0800 Message-ID: <20230801101030.2040-6-keith.zhao@starfivetech.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230801101030.2040-1-keith.zhao@starfivetech.com> References: <20230801101030.2040-1-keith.zhao@starfivetech.com> MIME-Version: 1.0 X-Originating-IP: [180.164.60.184] X-ClientProxiedBy: EXCAS064.cuchost.com (172.16.6.24) To EXMBX061.cuchost.com (172.16.6.61) X-YovoleRuleAgent: yovoleflag X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20230801_031125_785937_CF14389C X-CRM114-Status: GOOD ( 28.09 ) X-BeenThere: linux-riscv@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-riscv" Errors-To: linux-riscv-bounces+linux-riscv=archiver.kernel.org@lists.infradead.org Implement drm device registration interface Signed-off-by: Keith Zhao --- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include + +/* + * + * @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 +#include +#include + +#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 + +#include +#include +#include + +#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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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__ */ From patchwork Tue Aug 1 10:10:29 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Keith Zhao X-Patchwork-Id: 13337352 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 3AC45EB64DD for ; Tue, 1 Aug 2023 23:23:19 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:CC:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=JQIr+cGgZoI3wpXRagddxy9IU2XxEUwoK2vnsRnW77o=; b=VUQViMfPhQ85yG WsVuUQmH128/+PuQgFJ26ZTf7GfQp4d1yKRc5LJt9W3qNiFnuNZRoichLUZkXiSxpwNxGHWIOxZnz zovRJ+bzLP4mnI9JoqA1XwByp8h8kWvARwGje9apdKIAayM1z1xVf+KNmWkFXQccHiwOIYFWJv4JT P8YDta9CsIzU/LbVCEcQabPXv0u/vD1GQ6CsgHAWnTQYhFbzYI4oZHxkN6rDLAtfFVEPl1Yk9BOjP 4qnle40mFAk4em9HUDMweEy2w7c7gDgo/Gp0XHVBe1yA9ZFxZHNXUQaFYvs+/2iUAgJQJwhWvZokx kW/XDrANlIgAIoaOAu6A==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1qQyht-003VpP-19; Tue, 01 Aug 2023 23:23:13 +0000 Received: from fd01.gateway.ufhost.com ([61.152.239.71]) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1qQmLP-001jZZ-0Q for linux-riscv@lists.infradead.org; Tue, 01 Aug 2023 10:11:17 +0000 Received: from EXMBX166.cuchost.com (unknown [175.102.18.54]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "EXMBX166", Issuer "EXMBX166" (not verified)) by fd01.gateway.ufhost.com (Postfix) with ESMTP id 9E39D806C; Tue, 1 Aug 2023 18:10:35 +0800 (CST) Received: from EXMBX061.cuchost.com (172.16.6.61) by EXMBX166.cuchost.com (172.16.6.76) with Microsoft SMTP Server (TLS) id 15.0.1497.42; Tue, 1 Aug 2023 18:10:35 +0800 Received: from xiaofei.localdomain (180.164.60.184) by EXMBX061.cuchost.com (172.16.6.61) with Microsoft SMTP Server (TLS) id 15.0.1497.42; Tue, 1 Aug 2023 18:10:34 +0800 From: Keith Zhao To: , , , , , CC: David Airlie , Daniel Vetter , "Rob Herring" , Krzysztof Kozlowski , Conor Dooley , "Emil Renner Berthing" , Paul Walmsley , Palmer Dabbelt , Albert Ou , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , Philipp Zabel , Sumit Semwal , , Bjorn Andersson , "Heiko Stuebner" , Shawn Guo , Jagan Teki , Chris Morgan , Keith Zhao , Jack Zhu , Shengyang Chen , Changhuang Liang Subject: [PATCH v1 v1 6/7] drm/vs: Add KMS crtc&plane Date: Tue, 1 Aug 2023 18:10:29 +0800 Message-ID: <20230801101030.2040-7-keith.zhao@starfivetech.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230801101030.2040-1-keith.zhao@starfivetech.com> References: <20230801101030.2040-1-keith.zhao@starfivetech.com> MIME-Version: 1.0 X-Originating-IP: [180.164.60.184] X-ClientProxiedBy: EXCAS064.cuchost.com (172.16.6.24) To EXMBX061.cuchost.com (172.16.6.61) X-YovoleRuleAgent: yovoleflag X-Mailman-Approved-At: Tue, 01 Aug 2023 16:23:11 -0700 X-BeenThere: linux-riscv@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-riscv" Errors-To: linux-riscv-bounces+linux-riscv=archiver.kernel.org@lists.infradead.org add 2 crtcs and 8 planes in vs-drm Signed-off-by: Keith Zhao --- 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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include + +#include +#include + +#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 +#include +#include +#include +#include +#include +#include + +#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 + +#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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 +#include + +#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 +#include + +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__ */ From patchwork Tue Aug 1 10:10:30 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Keith Zhao X-Patchwork-Id: 13336157 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 5A2BFC001DF for ; Tue, 1 Aug 2023 10:11:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:CC:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=dyDNlVjv3c/lWkLplFBKAZZCkC59fikFkWe+nfPAP2w=; b=UDNOiyZOczPywz ARLI6PFaji1MnDQ6c6UqTAbcvJVERDCVccm9mIe/pYr2lt53osZMVdcTxhQB51DhbsNnk1ENBViNk 1Pt9BsKLmWhZfs1FXAuYJfdy+fnCU+yd55FjcgX2Wz68/4dlcS5UM3wH+3araHWTgAV+80X/48m8c 2pqNtoSO4MdA3814PJpzE1rxczn8Jov/N1JL6CLqma0cQ4XgOa2PB/xhDuPOgdQJ0LPO6U37ePSt6 iSU91n7yytNrc8UB5tDxZ19OlqfIBjhNx+dJl4leNnk++uK5bYNaOvVicCvfG26/t/+r5Q5lF0ELP VDK4gWKoRonc90Yr+tEw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1qQmLe-001jls-2a; Tue, 01 Aug 2023 10:11:26 +0000 Received: from ex01.ufhost.com ([61.152.239.75]) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1qQmLX-001jZf-39 for linux-riscv@lists.infradead.org; Tue, 01 Aug 2023 10:11:24 +0000 Received: from EXMBX166.cuchost.com (unknown [175.102.18.54]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "EXMBX166", Issuer "EXMBX166" (not verified)) by ex01.ufhost.com (Postfix) with ESMTP id BD3C724E2F7; Tue, 1 Aug 2023 18:10:35 +0800 (CST) Received: from EXMBX061.cuchost.com (172.16.6.61) by EXMBX166.cuchost.com (172.16.6.76) with Microsoft SMTP Server (TLS) id 15.0.1497.42; Tue, 1 Aug 2023 18:10:35 +0800 Received: from xiaofei.localdomain (180.164.60.184) by EXMBX061.cuchost.com (172.16.6.61) with Microsoft SMTP Server (TLS) id 15.0.1497.42; Tue, 1 Aug 2023 18:10:34 +0800 From: Keith Zhao To: , , , , , CC: David Airlie , Daniel Vetter , "Rob Herring" , Krzysztof Kozlowski , Conor Dooley , "Emil Renner Berthing" , Paul Walmsley , Palmer Dabbelt , Albert Ou , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , Philipp Zabel , Sumit Semwal , , Bjorn Andersson , "Heiko Stuebner" , Shawn Guo , Jagan Teki , Chris Morgan , Keith Zhao , Jack Zhu , Shengyang Chen , Changhuang Liang Subject: [PATCH v1 v1 7/7] drm/vs: Add hdmi Date: Tue, 1 Aug 2023 18:10:30 +0800 Message-ID: <20230801101030.2040-8-keith.zhao@starfivetech.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230801101030.2040-1-keith.zhao@starfivetech.com> References: <20230801101030.2040-1-keith.zhao@starfivetech.com> MIME-Version: 1.0 X-Originating-IP: [180.164.60.184] X-ClientProxiedBy: EXCAS064.cuchost.com (172.16.6.24) To EXMBX061.cuchost.com (172.16.6.61) X-YovoleRuleAgent: yovoleflag X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20230801_031120_657714_31C00F26 X-CRM114-Status: GOOD ( 20.77 ) X-BeenThere: linux-riscv@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-riscv" Errors-To: linux-riscv-bounces+linux-riscv=archiver.kernel.org@lists.infradead.org add hdmi driver as encoder and connect Signed-off-by: Keith Zhao --- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include + +#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__ */