From d141aafe54b5e678977ba48d28fff0fe79e8dc61 Mon Sep 17 00:00:00 2001 From: Hal Feng Date: Tue, 5 Sep 2023 20:21:04 +0800 Subject: [PATCH 01/92] pinctrl: starfive: jh7110: Fix failure to set irq after CONFIG_PM is enabled The issue was found when we enabled CONFIG_PM and tested edge events using libgpiod. > # gpiomon -r gpiochip0 55 > gpiomon: error waiting for events: Permission denied `gpiomon` will call irq_chip_pm_get() and then call pm_runtime_resume_and_get() if (IS_ENABLED(CONFIG_PM) && sfp->gc.irq.domain->pm_dev). pm_runtime_resume_and_get() will fail if the runtime pm of pinctrl device is disabled. As we expect the pinctrl driver can be always working and never suspend during runtime, unset sfp->gc.irq.domain->pm_dev to make sure pm_runtime_resume_and_get() won't be called when setting irq. Fixes: 447976ab62c5 ("pinctrl: starfive: Add StarFive JH7110 sys controller driver") Signed-off-by: Hal Feng Link: https://lore.kernel.org/r/20230905122105.117000-2-hal.feng@starfivetech.com Signed-off-by: Linus Walleij --- drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c index 5fe729b4a03de..72747ad497b5e 100644 --- a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c +++ b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c @@ -968,8 +968,6 @@ int jh7110_pinctrl_probe(struct platform_device *pdev) if (ret) return dev_err_probe(dev, ret, "could not register gpiochip\n"); - irq_domain_set_pm_device(sfp->gc.irq.domain, dev); - dev_info(dev, "StarFive GPIO chip registered %d GPIOs\n", sfp->gc.ngpio); return pinctrl_enable(sfp->pctl); From f691a9c29b9a6c982ca5a4b1b483697c1edd8f2c Mon Sep 17 00:00:00 2001 From: Hal Feng Date: Tue, 5 Sep 2023 20:21:05 +0800 Subject: [PATCH 02/92] pinctrl: starfive: jh7110: Add system pm ops to save and restore context Add system pm ops to save and restore pinctrl registers when suspending and resuming the driver, respectively. Signed-off-by: Hal Feng Link: https://lore.kernel.org/r/20230905122105.117000-3-hal.feng@starfivetech.com Signed-off-by: Linus Walleij --- .../starfive/pinctrl-starfive-jh7110-aon.c | 4 ++ .../starfive/pinctrl-starfive-jh7110-sys.c | 4 ++ .../starfive/pinctrl-starfive-jh7110.c | 40 +++++++++++++++++++ .../starfive/pinctrl-starfive-jh7110.h | 4 ++ 4 files changed, 52 insertions(+) diff --git a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110-aon.c b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110-aon.c index 8cf28aaed2542..8691fd55594a7 100644 --- a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110-aon.c +++ b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110-aon.c @@ -34,6 +34,8 @@ #define JH7110_AON_NGPIO 4 #define JH7110_AON_GC_BASE 64 +#define JH7110_AON_REGS_NUM 37 + /* registers */ #define JH7110_AON_DOEN 0x0 #define JH7110_AON_DOUT 0x4 @@ -148,6 +150,7 @@ static const struct jh7110_pinctrl_soc_info jh7110_aon_pinctrl_info = { .gpi_mask = GENMASK(3, 0), .gpioin_reg_base = JH7110_AON_GPIOIN, .irq_reg = &jh7110_aon_irq_reg, + .nsaved_regs = JH7110_AON_REGS_NUM, .jh7110_set_one_pin_mux = jh7110_aon_set_one_pin_mux, .jh7110_get_padcfg_base = jh7110_aon_get_padcfg_base, .jh7110_gpio_irq_handler = jh7110_aon_irq_handler, @@ -168,6 +171,7 @@ static struct platform_driver jh7110_aon_pinctrl_driver = { .driver = { .name = "starfive-jh7110-aon-pinctrl", .of_match_table = jh7110_aon_pinctrl_of_match, + .pm = pm_sleep_ptr(&jh7110_pinctrl_pm_ops), }, }; module_platform_driver(jh7110_aon_pinctrl_driver); diff --git a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110-sys.c b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110-sys.c index bc279a39613fc..0654731d5c27f 100644 --- a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110-sys.c +++ b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110-sys.c @@ -33,6 +33,8 @@ #define JH7110_SYS_NGPIO 64 #define JH7110_SYS_GC_BASE 0 +#define JH7110_SYS_REGS_NUM 174 + /* registers */ #define JH7110_SYS_DOEN 0x000 #define JH7110_SYS_DOUT 0x040 @@ -419,6 +421,7 @@ static const struct jh7110_pinctrl_soc_info jh7110_sys_pinctrl_info = { .gpi_mask = GENMASK(6, 0), .gpioin_reg_base = JH7110_SYS_GPIOIN, .irq_reg = &jh7110_sys_irq_reg, + .nsaved_regs = JH7110_SYS_REGS_NUM, .jh7110_set_one_pin_mux = jh7110_sys_set_one_pin_mux, .jh7110_get_padcfg_base = jh7110_sys_get_padcfg_base, .jh7110_gpio_irq_handler = jh7110_sys_irq_handler, @@ -439,6 +442,7 @@ static struct platform_driver jh7110_sys_pinctrl_driver = { .driver = { .name = "starfive-jh7110-sys-pinctrl", .of_match_table = jh7110_sys_pinctrl_of_match, + .pm = pm_sleep_ptr(&jh7110_pinctrl_pm_ops), }, }; module_platform_driver(jh7110_sys_pinctrl_driver); diff --git a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c index 72747ad497b5e..813b5c2f11b6c 100644 --- a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c +++ b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c @@ -873,6 +873,13 @@ int jh7110_pinctrl_probe(struct platform_device *pdev) if (!sfp) return -ENOMEM; +#if IS_ENABLED(CONFIG_PM_SLEEP) + sfp->saved_regs = devm_kcalloc(dev, info->nsaved_regs, + sizeof(*sfp->saved_regs), GFP_KERNEL); + if (!sfp->saved_regs) + return -ENOMEM; +#endif + sfp->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sfp->base)) return PTR_ERR(sfp->base); @@ -974,6 +981,39 @@ int jh7110_pinctrl_probe(struct platform_device *pdev) } EXPORT_SYMBOL_GPL(jh7110_pinctrl_probe); +static int jh7110_pinctrl_suspend(struct device *dev) +{ + struct jh7110_pinctrl *sfp = dev_get_drvdata(dev); + unsigned long flags; + unsigned int i; + + raw_spin_lock_irqsave(&sfp->lock, flags); + for (i = 0 ; i < sfp->info->nsaved_regs ; i++) + sfp->saved_regs[i] = readl_relaxed(sfp->base + 4 * i); + + raw_spin_unlock_irqrestore(&sfp->lock, flags); + return 0; +} + +static int jh7110_pinctrl_resume(struct device *dev) +{ + struct jh7110_pinctrl *sfp = dev_get_drvdata(dev); + unsigned long flags; + unsigned int i; + + raw_spin_lock_irqsave(&sfp->lock, flags); + for (i = 0 ; i < sfp->info->nsaved_regs ; i++) + writel_relaxed(sfp->saved_regs[i], sfp->base + 4 * i); + + raw_spin_unlock_irqrestore(&sfp->lock, flags); + return 0; +} + +const struct dev_pm_ops jh7110_pinctrl_pm_ops = { + LATE_SYSTEM_SLEEP_PM_OPS(jh7110_pinctrl_suspend, jh7110_pinctrl_resume) +}; +EXPORT_SYMBOL_GPL(jh7110_pinctrl_pm_ops); + MODULE_DESCRIPTION("Pinctrl driver for the StarFive JH7110 SoC"); MODULE_AUTHOR("Emil Renner Berthing "); MODULE_AUTHOR("Jianlong Huang "); diff --git a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.h b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.h index 3f20b7ff96dd8..a33d0d4e13820 100644 --- a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.h +++ b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.h @@ -21,6 +21,7 @@ struct jh7110_pinctrl { /* register read/write mutex */ struct mutex mutex; const struct jh7110_pinctrl_soc_info *info; + u32 *saved_regs; }; struct jh7110_gpio_irq_reg { @@ -50,6 +51,8 @@ struct jh7110_pinctrl_soc_info { const struct jh7110_gpio_irq_reg *irq_reg; + unsigned int nsaved_regs; + /* generic pinmux */ int (*jh7110_set_one_pin_mux)(struct jh7110_pinctrl *sfp, unsigned int pin, @@ -66,5 +69,6 @@ void jh7110_set_gpiomux(struct jh7110_pinctrl *sfp, unsigned int pin, unsigned int din, u32 dout, u32 doen); int jh7110_pinctrl_probe(struct platform_device *pdev); struct jh7110_pinctrl *jh7110_from_irq_desc(struct irq_desc *desc); +extern const struct dev_pm_ops jh7110_pinctrl_pm_ops; #endif /* __PINCTRL_STARFIVE_JH7110_H__ */ From aa9843ebbb973d1cae56dc0e93304c3b9f081531 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Mon, 17 Jul 2023 10:30:34 +0800 Subject: [PATCH 03/92] dt-bindings: clock: Add StarFive JH7110 PLL clock generator Add bindings for the PLL clock generator on the JH7110 RISC-V SoC. Reviewed-by: Conor Dooley Reviewed-by: Krzysztof Kozlowski Signed-off-by: Xingyu Wu Reviewed-by: Emil Renner Berthing Signed-off-by: Conor Dooley --- .../bindings/clock/starfive,jh7110-pll.yaml | 46 +++++++++++++++++++ .../dt-bindings/clock/starfive,jh7110-crg.h | 6 +++ 2 files changed, 52 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/starfive,jh7110-pll.yaml diff --git a/Documentation/devicetree/bindings/clock/starfive,jh7110-pll.yaml b/Documentation/devicetree/bindings/clock/starfive,jh7110-pll.yaml new file mode 100644 index 0000000000000..be8300ce86d0f --- /dev/null +++ b/Documentation/devicetree/bindings/clock/starfive,jh7110-pll.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/starfive,jh7110-pll.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 PLL Clock Generator + +description: + These PLLs are high speed, low jitter frequency synthesizers in the JH7110. + Each PLL works in integer mode or fraction mode, with configuration + registers in the sys syscon. So the PLLs node should be a child of + SYS-SYSCON node. + The formula for calculating frequency is + Fvco = Fref * (NI + NF) / M / Q1 + +maintainers: + - Xingyu Wu + +properties: + compatible: + const: starfive,jh7110-pll + + clocks: + maxItems: 1 + description: Main Oscillator (24 MHz) + + '#clock-cells': + const: 1 + description: + See for valid indices. + +required: + - compatible + - clocks + - '#clock-cells' + +additionalProperties: false + +examples: + - | + clock-controller { + compatible = "starfive,jh7110-pll"; + clocks = <&osc>; + #clock-cells = <1>; + }; diff --git a/include/dt-bindings/clock/starfive,jh7110-crg.h b/include/dt-bindings/clock/starfive,jh7110-crg.h index 06257bfd9ac1e..3fb5e31c3be4b 100644 --- a/include/dt-bindings/clock/starfive,jh7110-crg.h +++ b/include/dt-bindings/clock/starfive,jh7110-crg.h @@ -6,6 +6,12 @@ #ifndef __DT_BINDINGS_CLOCK_STARFIVE_JH7110_CRG_H__ #define __DT_BINDINGS_CLOCK_STARFIVE_JH7110_CRG_H__ +/* PLL clocks */ +#define JH7110_PLLCLK_PLL0_OUT 0 +#define JH7110_PLLCLK_PLL1_OUT 1 +#define JH7110_PLLCLK_PLL2_OUT 2 +#define JH7110_PLLCLK_END 3 + /* SYSCRG clocks */ #define JH7110_SYSCLK_CPU_ROOT 0 #define JH7110_SYSCLK_CPU_CORE 1 From 46854f126a89b4b4b91db5818d917e8c47cbb1a1 Mon Sep 17 00:00:00 2001 From: William Qiu Date: Mon, 17 Jul 2023 10:30:35 +0800 Subject: [PATCH 04/92] dt-bindings: soc: starfive: Add StarFive syscon module Add documentation to describe StarFive System Controller Registers. Reviewed-by: Emil Renner Berthing Reviewed-by: Conor Dooley Co-developed-by: Xingyu Wu Signed-off-by: Xingyu Wu Signed-off-by: William Qiu Signed-off-by: Conor Dooley --- .../soc/starfive/starfive,jh7110-syscon.yaml | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml diff --git a/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml b/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml new file mode 100644 index 0000000000000..0039319e91fe6 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/starfive/starfive,jh7110-syscon.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 SoC system controller + +maintainers: + - William Qiu + +description: + The StarFive JH7110 SoC system controller provides register information such + as offset, mask and shift to configure related modules such as MMC and PCIe. + +properties: + compatible: + oneOf: + - items: + - const: starfive,jh7110-sys-syscon + - const: syscon + - const: simple-mfd + - items: + - enum: + - starfive,jh7110-aon-syscon + - starfive,jh7110-stg-syscon + - const: syscon + + reg: + maxItems: 1 + + clock-controller: + $ref: /schemas/clock/starfive,jh7110-pll.yaml# + type: object + + "#power-domain-cells": + const: 1 + +required: + - compatible + - reg + +allOf: + - if: + properties: + compatible: + contains: + const: starfive,jh7110-sys-syscon + then: + required: + - clock-controller + else: + properties: + clock-controller: false + - if: + properties: + compatible: + contains: + const: starfive,jh7110-aon-syscon + then: + required: + - "#power-domain-cells" + else: + properties: + "#power-domain-cells": false + +additionalProperties: false + +examples: + - | + syscon@10240000 { + compatible = "starfive,jh7110-stg-syscon", "syscon"; + reg = <0x10240000 0x1000>; + }; + + syscon@13030000 { + compatible = "starfive,jh7110-sys-syscon", "syscon", "simple-mfd"; + reg = <0x13030000 0x1000>; + + clock-controller { + compatible = "starfive,jh7110-pll"; + clocks = <&osc>; + #clock-cells = <1>; + }; + }; + + syscon@17010000 { + compatible = "starfive,jh7110-aon-syscon", "syscon"; + reg = <0x17010000 0x1000>; + #power-domain-cells = <1>; + }; + +... From f724caca5698f5c88032566a6882f3a75528fff3 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Mon, 17 Jul 2023 10:30:36 +0800 Subject: [PATCH 05/92] dt-bindings: clock: jh7110-syscrg: Add PLL clock inputs Add PLL clock inputs from PLL clock generator. Reviewed-by: Emil Renner Berthing Reviewed-by: Conor Dooley Signed-off-by: Xingyu Wu Signed-off-by: Conor Dooley --- .../bindings/clock/starfive,jh7110-syscrg.yaml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/starfive,jh7110-syscrg.yaml b/Documentation/devicetree/bindings/clock/starfive,jh7110-syscrg.yaml index 84373ae31644e..5ba0a885aa809 100644 --- a/Documentation/devicetree/bindings/clock/starfive,jh7110-syscrg.yaml +++ b/Documentation/devicetree/bindings/clock/starfive,jh7110-syscrg.yaml @@ -27,6 +27,9 @@ properties: - description: External I2S RX left/right channel clock - description: External TDM clock - description: External audio master clock + - description: PLL0 + - description: PLL1 + - description: PLL2 - items: - description: Main Oscillator (24 MHz) @@ -38,6 +41,9 @@ properties: - description: External I2S RX left/right channel clock - description: External TDM clock - description: External audio master clock + - description: PLL0 + - description: PLL1 + - description: PLL2 clock-names: oneOf: @@ -52,6 +58,9 @@ properties: - const: i2srx_lrck_ext - const: tdm_ext - const: mclk_ext + - const: pll0_out + - const: pll1_out + - const: pll2_out - items: - const: osc @@ -63,6 +72,9 @@ properties: - const: i2srx_lrck_ext - const: tdm_ext - const: mclk_ext + - const: pll0_out + - const: pll1_out + - const: pll2_out '#clock-cells': const: 1 @@ -93,12 +105,14 @@ examples: <&gmac1_rgmii_rxin>, <&i2stx_bclk_ext>, <&i2stx_lrck_ext>, <&i2srx_bclk_ext>, <&i2srx_lrck_ext>, - <&tdm_ext>, <&mclk_ext>; + <&tdm_ext>, <&mclk_ext>, + <&pllclk 0>, <&pllclk 1>, <&pllclk 2>; clock-names = "osc", "gmac1_rmii_refin", "gmac1_rgmii_rxin", "i2stx_bclk_ext", "i2stx_lrck_ext", "i2srx_bclk_ext", "i2srx_lrck_ext", - "tdm_ext", "mclk_ext"; + "tdm_ext", "mclk_ext", + "pll0_out", "pll1_out", "pll2_out"; #clock-cells = <1>; #reset-cells = <1>; }; From f4767c58bcdfe23f877ede41cc83c8caa3d6e407 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Thu, 13 Jul 2023 19:38:54 +0800 Subject: [PATCH 06/92] dt-bindings: clock: Add StarFive JH7110 System-Top-Group clock and reset generator Add bindings for the System-Top-Group clock and reset generator (STGCRG) on the JH7110 RISC-V SoC by StarFive Ltd. Acked-by: Palmer Dabbelt Reviewed-by: Emil Renner Berthing Reviewed-by: Krzysztof Kozlowski Signed-off-by: Xingyu Wu Signed-off-by: Conor Dooley --- .../clock/starfive,jh7110-stgcrg.yaml | 82 +++++++++++++++++++ .../dt-bindings/clock/starfive,jh7110-crg.h | 34 ++++++++ .../dt-bindings/reset/starfive,jh7110-crg.h | 28 +++++++ 3 files changed, 144 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/starfive,jh7110-stgcrg.yaml diff --git a/Documentation/devicetree/bindings/clock/starfive,jh7110-stgcrg.yaml b/Documentation/devicetree/bindings/clock/starfive,jh7110-stgcrg.yaml new file mode 100644 index 0000000000000..b64ccd84200ac --- /dev/null +++ b/Documentation/devicetree/bindings/clock/starfive,jh7110-stgcrg.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/starfive,jh7110-stgcrg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 System-Top-Group Clock and Reset Generator + +maintainers: + - Xingyu Wu + +properties: + compatible: + const: starfive,jh7110-stgcrg + + reg: + maxItems: 1 + + clocks: + items: + - description: Main Oscillator (24 MHz) + - description: HIFI4 core + - description: STG AXI/AHB + - description: USB (125 MHz) + - description: CPU Bus + - description: HIFI4 Axi + - description: NOC STG Bus + - description: APB Bus + + clock-names: + items: + - const: osc + - const: hifi4_core + - const: stg_axiahb + - const: usb_125m + - const: cpu_bus + - const: hifi4_axi + - const: nocstg_bus + - const: apb_bus + + '#clock-cells': + const: 1 + description: + See for valid indices. + + '#reset-cells': + const: 1 + description: + See for valid indices. + +required: + - compatible + - reg + - clocks + - clock-names + - '#clock-cells' + - '#reset-cells' + +additionalProperties: false + +examples: + - | + #include + + stgcrg: clock-controller@10230000 { + compatible = "starfive,jh7110-stgcrg"; + reg = <0x10230000 0x10000>; + clocks = <&osc>, + <&syscrg JH7110_SYSCLK_HIFI4_CORE>, + <&syscrg JH7110_SYSCLK_STG_AXIAHB>, + <&syscrg JH7110_SYSCLK_USB_125M>, + <&syscrg JH7110_SYSCLK_CPU_BUS>, + <&syscrg JH7110_SYSCLK_HIFI4_AXI>, + <&syscrg JH7110_SYSCLK_NOCSTG_BUS>, + <&syscrg JH7110_SYSCLK_APB_BUS>; + clock-names = "osc", "hifi4_core", + "stg_axiahb", "usb_125m", + "cpu_bus", "hifi4_axi", + "nocstg_bus", "apb_bus"; + #clock-cells = <1>; + #reset-cells = <1>; + }; diff --git a/include/dt-bindings/clock/starfive,jh7110-crg.h b/include/dt-bindings/clock/starfive,jh7110-crg.h index 3fb5e31c3be4b..4e229a5d7dc72 100644 --- a/include/dt-bindings/clock/starfive,jh7110-crg.h +++ b/include/dt-bindings/clock/starfive,jh7110-crg.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 OR MIT */ /* * Copyright 2022 Emil Renner Berthing + * Copyright 2022 StarFive Technology Co., Ltd. */ #ifndef __DT_BINDINGS_CLOCK_STARFIVE_JH7110_CRG_H__ @@ -224,4 +225,37 @@ #define JH7110_AONCLK_END 14 +/* STGCRG clocks */ +#define JH7110_STGCLK_HIFI4_CLK_CORE 0 +#define JH7110_STGCLK_USB0_APB 1 +#define JH7110_STGCLK_USB0_UTMI_APB 2 +#define JH7110_STGCLK_USB0_AXI 3 +#define JH7110_STGCLK_USB0_LPM 4 +#define JH7110_STGCLK_USB0_STB 5 +#define JH7110_STGCLK_USB0_APP_125 6 +#define JH7110_STGCLK_USB0_REFCLK 7 +#define JH7110_STGCLK_PCIE0_AXI_MST0 8 +#define JH7110_STGCLK_PCIE0_APB 9 +#define JH7110_STGCLK_PCIE0_TL 10 +#define JH7110_STGCLK_PCIE1_AXI_MST0 11 +#define JH7110_STGCLK_PCIE1_APB 12 +#define JH7110_STGCLK_PCIE1_TL 13 +#define JH7110_STGCLK_PCIE_SLV_MAIN 14 +#define JH7110_STGCLK_SEC_AHB 15 +#define JH7110_STGCLK_SEC_MISC_AHB 16 +#define JH7110_STGCLK_GRP0_MAIN 17 +#define JH7110_STGCLK_GRP0_BUS 18 +#define JH7110_STGCLK_GRP0_STG 19 +#define JH7110_STGCLK_GRP1_MAIN 20 +#define JH7110_STGCLK_GRP1_BUS 21 +#define JH7110_STGCLK_GRP1_STG 22 +#define JH7110_STGCLK_GRP1_HIFI 23 +#define JH7110_STGCLK_E2_RTC 24 +#define JH7110_STGCLK_E2_CORE 25 +#define JH7110_STGCLK_E2_DBG 26 +#define JH7110_STGCLK_DMA1P_AXI 27 +#define JH7110_STGCLK_DMA1P_AHB 28 + +#define JH7110_STGCLK_END 29 + #endif /* __DT_BINDINGS_CLOCK_STARFIVE_JH7110_CRG_H__ */ diff --git a/include/dt-bindings/reset/starfive,jh7110-crg.h b/include/dt-bindings/reset/starfive,jh7110-crg.h index d78e38690ceb3..4e96ab81dd8ec 100644 --- a/include/dt-bindings/reset/starfive,jh7110-crg.h +++ b/include/dt-bindings/reset/starfive,jh7110-crg.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 OR MIT */ /* * Copyright (C) 2022 Emil Renner Berthing + * Copyright (C) 2022 StarFive Technology Co., Ltd. */ #ifndef __DT_BINDINGS_RESET_STARFIVE_JH7110_CRG_H__ @@ -151,4 +152,31 @@ #define JH7110_AONRST_END 8 +/* STGCRG resets */ +#define JH7110_STGRST_SYSCON 0 +#define JH7110_STGRST_HIFI4_CORE 1 +#define JH7110_STGRST_HIFI4_AXI 2 +#define JH7110_STGRST_SEC_AHB 3 +#define JH7110_STGRST_E24_CORE 4 +#define JH7110_STGRST_DMA1P_AXI 5 +#define JH7110_STGRST_DMA1P_AHB 6 +#define JH7110_STGRST_USB0_AXI 7 +#define JH7110_STGRST_USB0_APB 8 +#define JH7110_STGRST_USB0_UTMI_APB 9 +#define JH7110_STGRST_USB0_PWRUP 10 +#define JH7110_STGRST_PCIE0_AXI_MST0 11 +#define JH7110_STGRST_PCIE0_AXI_SLV0 12 +#define JH7110_STGRST_PCIE0_AXI_SLV 13 +#define JH7110_STGRST_PCIE0_BRG 14 +#define JH7110_STGRST_PCIE0_CORE 15 +#define JH7110_STGRST_PCIE0_APB 16 +#define JH7110_STGRST_PCIE1_AXI_MST0 17 +#define JH7110_STGRST_PCIE1_AXI_SLV0 18 +#define JH7110_STGRST_PCIE1_AXI_SLV 19 +#define JH7110_STGRST_PCIE1_BRG 20 +#define JH7110_STGRST_PCIE1_CORE 21 +#define JH7110_STGRST_PCIE1_APB 22 + +#define JH7110_STGRST_END 23 + #endif /* __DT_BINDINGS_RESET_STARFIVE_JH7110_CRG_H__ */ From 77a0a40d43d620cea9d5d5c69ea1fb09a1767281 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Thu, 13 Jul 2023 19:38:56 +0800 Subject: [PATCH 07/92] dt-bindings: clock: Add StarFive JH7110 Image-Signal-Process clock and reset generator Add bindings for the Image-Signal-Process clock and reset generator (ISPCRG) on the JH7110 RISC-V SoC by StarFive Ltd. Acked-by: Palmer Dabbelt Reviewed-by: Emil Renner Berthing Reviewed-by: Krzysztof Kozlowski Signed-off-by: Xingyu Wu Signed-off-by: Conor Dooley --- .../clock/starfive,jh7110-ispcrg.yaml | 87 +++++++++++++++++++ .../dt-bindings/clock/starfive,jh7110-crg.h | 18 ++++ .../dt-bindings/reset/starfive,jh7110-crg.h | 16 ++++ 3 files changed, 121 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/starfive,jh7110-ispcrg.yaml diff --git a/Documentation/devicetree/bindings/clock/starfive,jh7110-ispcrg.yaml b/Documentation/devicetree/bindings/clock/starfive,jh7110-ispcrg.yaml new file mode 100644 index 0000000000000..3b8b85be5cd05 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/starfive,jh7110-ispcrg.yaml @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/starfive,jh7110-ispcrg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 Image-Signal-Process Clock and Reset Generator + +maintainers: + - Xingyu Wu + +properties: + compatible: + const: starfive,jh7110-ispcrg + + reg: + maxItems: 1 + + clocks: + items: + - description: ISP Top core + - description: ISP Top Axi + - description: NOC ISP Bus + - description: external DVP + + clock-names: + items: + - const: isp_top_core + - const: isp_top_axi + - const: noc_bus_isp_axi + - const: dvp_clk + + resets: + items: + - description: ISP Top core + - description: ISP Top Axi + - description: NOC ISP Bus + + '#clock-cells': + const: 1 + description: + See for valid indices. + + '#reset-cells': + const: 1 + description: + See for valid indices. + + power-domains: + maxItems: 1 + description: + ISP domain power + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - '#clock-cells' + - '#reset-cells' + - power-domains + +additionalProperties: false + +examples: + - | + #include + #include + #include + + ispcrg: clock-controller@19810000 { + compatible = "starfive,jh7110-ispcrg"; + reg = <0x19810000 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_ISP_TOP_CORE>, + <&syscrg JH7110_SYSCLK_ISP_TOP_AXI>, + <&syscrg JH7110_SYSCLK_NOC_BUS_ISP_AXI>, + <&dvp_clk>; + clock-names = "isp_top_core", "isp_top_axi", + "noc_bus_isp_axi", "dvp_clk"; + resets = <&syscrg JH7110_SYSRST_ISP_TOP>, + <&syscrg JH7110_SYSRST_ISP_TOP_AXI>, + <&syscrg JH7110_SYSRST_NOC_BUS_ISP_AXI>; + #clock-cells = <1>; + #reset-cells = <1>; + power-domains = <&pwrc JH7110_PD_ISP>; + }; diff --git a/include/dt-bindings/clock/starfive,jh7110-crg.h b/include/dt-bindings/clock/starfive,jh7110-crg.h index 4e229a5d7dc72..c322f44998946 100644 --- a/include/dt-bindings/clock/starfive,jh7110-crg.h +++ b/include/dt-bindings/clock/starfive,jh7110-crg.h @@ -258,4 +258,22 @@ #define JH7110_STGCLK_END 29 +/* ISPCRG clocks */ +#define JH7110_ISPCLK_DOM4_APB_FUNC 0 +#define JH7110_ISPCLK_MIPI_RX0_PXL 1 +#define JH7110_ISPCLK_DVP_INV 2 +#define JH7110_ISPCLK_M31DPHY_CFG_IN 3 +#define JH7110_ISPCLK_M31DPHY_REF_IN 4 +#define JH7110_ISPCLK_M31DPHY_TX_ESC_LAN0 5 +#define JH7110_ISPCLK_VIN_APB 6 +#define JH7110_ISPCLK_VIN_SYS 7 +#define JH7110_ISPCLK_VIN_PIXEL_IF0 8 +#define JH7110_ISPCLK_VIN_PIXEL_IF1 9 +#define JH7110_ISPCLK_VIN_PIXEL_IF2 10 +#define JH7110_ISPCLK_VIN_PIXEL_IF3 11 +#define JH7110_ISPCLK_VIN_P_AXI_WR 12 +#define JH7110_ISPCLK_ISPV2_TOP_WRAPPER_C 13 + +#define JH7110_ISPCLK_END 14 + #endif /* __DT_BINDINGS_CLOCK_STARFIVE_JH7110_CRG_H__ */ diff --git a/include/dt-bindings/reset/starfive,jh7110-crg.h b/include/dt-bindings/reset/starfive,jh7110-crg.h index 4e96ab81dd8ec..2c5d9dcefffa4 100644 --- a/include/dt-bindings/reset/starfive,jh7110-crg.h +++ b/include/dt-bindings/reset/starfive,jh7110-crg.h @@ -179,4 +179,20 @@ #define JH7110_STGRST_END 23 +/* ISPCRG resets */ +#define JH7110_ISPRST_ISPV2_TOP_WRAPPER_P 0 +#define JH7110_ISPRST_ISPV2_TOP_WRAPPER_C 1 +#define JH7110_ISPRST_M31DPHY_HW 2 +#define JH7110_ISPRST_M31DPHY_B09_AON 3 +#define JH7110_ISPRST_VIN_APB 4 +#define JH7110_ISPRST_VIN_PIXEL_IF0 5 +#define JH7110_ISPRST_VIN_PIXEL_IF1 6 +#define JH7110_ISPRST_VIN_PIXEL_IF2 7 +#define JH7110_ISPRST_VIN_PIXEL_IF3 8 +#define JH7110_ISPRST_VIN_SYS 9 +#define JH7110_ISPRST_VIN_P_AXI_RD 10 +#define JH7110_ISPRST_VIN_P_AXI_WR 11 + +#define JH7110_ISPRST_END 12 + #endif /* __DT_BINDINGS_RESET_STARFIVE_JH7110_CRG_H__ */ From 54dac13dd529190f164cfbf0dc7540af9c47c4c4 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Thu, 13 Jul 2023 19:38:58 +0800 Subject: [PATCH 08/92] dt-bindings: clock: Add StarFive JH7110 Video-Output clock and reset generator Add bindings for the Video-Output clock and reset generator (VOUTCRG) on the JH7110 RISC-V SoC by StarFive Ltd. Acked-by: Palmer Dabbelt Reviewed-by: Emil Renner Berthing Reviewed-by: Krzysztof Kozlowski Signed-off-by: Xingyu Wu Signed-off-by: Conor Dooley --- .../clock/starfive,jh7110-voutcrg.yaml | 90 +++++++++++++++++++ .../dt-bindings/clock/starfive,jh7110-crg.h | 22 +++++ .../dt-bindings/reset/starfive,jh7110-crg.h | 16 ++++ 3 files changed, 128 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/starfive,jh7110-voutcrg.yaml diff --git a/Documentation/devicetree/bindings/clock/starfive,jh7110-voutcrg.yaml b/Documentation/devicetree/bindings/clock/starfive,jh7110-voutcrg.yaml new file mode 100644 index 0000000000000..af77bd8c86b12 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/starfive,jh7110-voutcrg.yaml @@ -0,0 +1,90 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/starfive,jh7110-voutcrg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 Video-Output Clock and Reset Generator + +maintainers: + - Xingyu Wu + +properties: + compatible: + const: starfive,jh7110-voutcrg + + reg: + maxItems: 1 + + clocks: + items: + - description: Vout Top core + - description: Vout Top Ahb + - description: Vout Top Axi + - description: Vout Top HDMI MCLK + - description: I2STX0 BCLK + - description: external HDMI pixel + + clock-names: + items: + - const: vout_src + - const: vout_top_ahb + - const: vout_top_axi + - const: vout_top_hdmitx0_mclk + - const: i2stx0_bclk + - const: hdmitx0_pixelclk + + resets: + maxItems: 1 + description: Vout Top core + + '#clock-cells': + const: 1 + description: + See for valid indices. + + '#reset-cells': + const: 1 + description: + See for valid indices. + + power-domains: + maxItems: 1 + description: + Vout domain power + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - '#clock-cells' + - '#reset-cells' + - power-domains + +additionalProperties: false + +examples: + - | + #include + #include + #include + + voutcrg: clock-controller@295C0000 { + compatible = "starfive,jh7110-voutcrg"; + reg = <0x295C0000 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_VOUT_SRC>, + <&syscrg JH7110_SYSCLK_VOUT_TOP_AHB>, + <&syscrg JH7110_SYSCLK_VOUT_TOP_AXI>, + <&syscrg JH7110_SYSCLK_VOUT_TOP_HDMITX0_MCLK>, + <&syscrg JH7110_SYSCLK_I2STX0_BCLK>, + <&hdmitx0_pixelclk>; + clock-names = "vout_src", "vout_top_ahb", + "vout_top_axi", "vout_top_hdmitx0_mclk", + "i2stx0_bclk", "hdmitx0_pixelclk"; + resets = <&syscrg JH7110_SYSRST_VOUT_TOP_SRC>; + #clock-cells = <1>; + #reset-cells = <1>; + power-domains = <&pwrc JH7110_PD_VOUT>; + }; diff --git a/include/dt-bindings/clock/starfive,jh7110-crg.h b/include/dt-bindings/clock/starfive,jh7110-crg.h index c322f44998946..467ccab3bfaaa 100644 --- a/include/dt-bindings/clock/starfive,jh7110-crg.h +++ b/include/dt-bindings/clock/starfive,jh7110-crg.h @@ -276,4 +276,26 @@ #define JH7110_ISPCLK_END 14 +/* VOUTCRG clocks */ +#define JH7110_VOUTCLK_APB 0 +#define JH7110_VOUTCLK_DC8200_PIX 1 +#define JH7110_VOUTCLK_DSI_SYS 2 +#define JH7110_VOUTCLK_TX_ESC 3 +#define JH7110_VOUTCLK_DC8200_AXI 4 +#define JH7110_VOUTCLK_DC8200_CORE 5 +#define JH7110_VOUTCLK_DC8200_AHB 6 +#define JH7110_VOUTCLK_DC8200_PIX0 7 +#define JH7110_VOUTCLK_DC8200_PIX1 8 +#define JH7110_VOUTCLK_DOM_VOUT_TOP_LCD 9 +#define JH7110_VOUTCLK_DSITX_APB 10 +#define JH7110_VOUTCLK_DSITX_SYS 11 +#define JH7110_VOUTCLK_DSITX_DPI 12 +#define JH7110_VOUTCLK_DSITX_TXESC 13 +#define JH7110_VOUTCLK_MIPITX_DPHY_TXESC 14 +#define JH7110_VOUTCLK_HDMI_TX_MCLK 15 +#define JH7110_VOUTCLK_HDMI_TX_BCLK 16 +#define JH7110_VOUTCLK_HDMI_TX_SYS 17 + +#define JH7110_VOUTCLK_END 18 + #endif /* __DT_BINDINGS_CLOCK_STARFIVE_JH7110_CRG_H__ */ diff --git a/include/dt-bindings/reset/starfive,jh7110-crg.h b/include/dt-bindings/reset/starfive,jh7110-crg.h index 2c5d9dcefffa4..eaf4a0d84f6a9 100644 --- a/include/dt-bindings/reset/starfive,jh7110-crg.h +++ b/include/dt-bindings/reset/starfive,jh7110-crg.h @@ -195,4 +195,20 @@ #define JH7110_ISPRST_END 12 +/* VOUTCRG resets */ +#define JH7110_VOUTRST_DC8200_AXI 0 +#define JH7110_VOUTRST_DC8200_AHB 1 +#define JH7110_VOUTRST_DC8200_CORE 2 +#define JH7110_VOUTRST_DSITX_DPI 3 +#define JH7110_VOUTRST_DSITX_APB 4 +#define JH7110_VOUTRST_DSITX_RXESC 5 +#define JH7110_VOUTRST_DSITX_SYS 6 +#define JH7110_VOUTRST_DSITX_TXBYTEHS 7 +#define JH7110_VOUTRST_DSITX_TXESC 8 +#define JH7110_VOUTRST_HDMI_TX_HDMI 9 +#define JH7110_VOUTRST_MIPITX_DPHY_SYS 10 +#define JH7110_VOUTRST_MIPITX_DPHY_TXBYTEHS 11 + +#define JH7110_VOUTRST_END 12 + #endif /* __DT_BINDINGS_RESET_STARFIVE_JH7110_CRG_H__ */ From 1c25b88442820fdde929def1458542748392b7c6 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Mon, 17 Jul 2023 10:30:37 +0800 Subject: [PATCH 09/92] clk: starfive: Add StarFive JH7110 PLL clock driver Add driver for the StarFive JH7110 PLL clock controller and they work by reading and setting syscon registers. Co-developed-by: Emil Renner Berthing Signed-off-by: Emil Renner Berthing Signed-off-by: Xingyu Wu Signed-off-by: Conor Dooley --- drivers/clk/starfive/Kconfig | 8 + drivers/clk/starfive/Makefile | 1 + .../clk/starfive/clk-starfive-jh7110-pll.c | 507 ++++++++++++++++++ 3 files changed, 516 insertions(+) create mode 100644 drivers/clk/starfive/clk-starfive-jh7110-pll.c diff --git a/drivers/clk/starfive/Kconfig b/drivers/clk/starfive/Kconfig index 5d2333106f134..5195f7be5213c 100644 --- a/drivers/clk/starfive/Kconfig +++ b/drivers/clk/starfive/Kconfig @@ -21,6 +21,14 @@ config CLK_STARFIVE_JH7100_AUDIO Say Y or M here to support the audio clocks on the StarFive JH7100 SoC. +config CLK_STARFIVE_JH7110_PLL + bool "StarFive JH7110 PLL clock support" + depends on ARCH_STARFIVE || COMPILE_TEST + default ARCH_STARFIVE + help + Say yes here to support the PLL clock controller on the + StarFive JH7110 SoC. + config CLK_STARFIVE_JH7110_SYS bool "StarFive JH7110 system clock support" depends on ARCH_STARFIVE || COMPILE_TEST diff --git a/drivers/clk/starfive/Makefile b/drivers/clk/starfive/Makefile index f3df7d957b1e5..b48e539e52b01 100644 --- a/drivers/clk/starfive/Makefile +++ b/drivers/clk/starfive/Makefile @@ -4,5 +4,6 @@ obj-$(CONFIG_CLK_STARFIVE_JH71X0) += clk-starfive-jh71x0.o obj-$(CONFIG_CLK_STARFIVE_JH7100) += clk-starfive-jh7100.o obj-$(CONFIG_CLK_STARFIVE_JH7100_AUDIO) += clk-starfive-jh7100-audio.o +obj-$(CONFIG_CLK_STARFIVE_JH7110_PLL) += clk-starfive-jh7110-pll.o obj-$(CONFIG_CLK_STARFIVE_JH7110_SYS) += clk-starfive-jh7110-sys.o obj-$(CONFIG_CLK_STARFIVE_JH7110_AON) += clk-starfive-jh7110-aon.o diff --git a/drivers/clk/starfive/clk-starfive-jh7110-pll.c b/drivers/clk/starfive/clk-starfive-jh7110-pll.c new file mode 100644 index 0000000000000..3598390e8fd08 --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7110-pll.c @@ -0,0 +1,507 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7110 PLL Clock Generator Driver + * + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * Copyright (C) 2023 Emil Renner Berthing + * + * This driver is about to register JH7110 PLL clock generator and support ops. + * The JH7110 have three PLL clock, PLL0, PLL1 and PLL2. + * Each PLL clocks work in integer mode or fraction mode by some dividers, + * and the configuration registers and dividers are set in several syscon registers. + * The formula for calculating frequency is: + * Fvco = Fref * (NI + NF) / M / Q1 + * Fref: OSC source clock rate + * NI: integer frequency dividing ratio of feedback divider, set by fbdiv[11:0]. + * NF: fractional frequency dividing ratio, set by frac[23:0]. NF = frac[23:0] / 2^24 = 0 ~ 0.999. + * M: frequency dividing ratio of pre-divider, set by prediv[5:0]. + * Q1: frequency dividing ratio of post divider, set by 2^postdiv1[1:0], eg. 1, 2, 4 or 8. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* this driver expects a 24MHz input frequency from the oscillator */ +#define JH7110_PLL_OSC_RATE 24000000UL + +#define JH7110_PLL0_PD_OFFSET 0x18 +#define JH7110_PLL0_DACPD_SHIFT 24 +#define JH7110_PLL0_DACPD_MASK BIT(24) +#define JH7110_PLL0_DSMPD_SHIFT 25 +#define JH7110_PLL0_DSMPD_MASK BIT(25) +#define JH7110_PLL0_FBDIV_OFFSET 0x1c +#define JH7110_PLL0_FBDIV_SHIFT 0 +#define JH7110_PLL0_FBDIV_MASK GENMASK(11, 0) +#define JH7110_PLL0_FRAC_OFFSET 0x20 +#define JH7110_PLL0_PREDIV_OFFSET 0x24 + +#define JH7110_PLL1_PD_OFFSET 0x24 +#define JH7110_PLL1_DACPD_SHIFT 15 +#define JH7110_PLL1_DACPD_MASK BIT(15) +#define JH7110_PLL1_DSMPD_SHIFT 16 +#define JH7110_PLL1_DSMPD_MASK BIT(16) +#define JH7110_PLL1_FBDIV_OFFSET 0x24 +#define JH7110_PLL1_FBDIV_SHIFT 17 +#define JH7110_PLL1_FBDIV_MASK GENMASK(28, 17) +#define JH7110_PLL1_FRAC_OFFSET 0x28 +#define JH7110_PLL1_PREDIV_OFFSET 0x2c + +#define JH7110_PLL2_PD_OFFSET 0x2c +#define JH7110_PLL2_DACPD_SHIFT 15 +#define JH7110_PLL2_DACPD_MASK BIT(15) +#define JH7110_PLL2_DSMPD_SHIFT 16 +#define JH7110_PLL2_DSMPD_MASK BIT(16) +#define JH7110_PLL2_FBDIV_OFFSET 0x2c +#define JH7110_PLL2_FBDIV_SHIFT 17 +#define JH7110_PLL2_FBDIV_MASK GENMASK(28, 17) +#define JH7110_PLL2_FRAC_OFFSET 0x30 +#define JH7110_PLL2_PREDIV_OFFSET 0x34 + +#define JH7110_PLL_FRAC_SHIFT 0 +#define JH7110_PLL_FRAC_MASK GENMASK(23, 0) +#define JH7110_PLL_POSTDIV1_SHIFT 28 +#define JH7110_PLL_POSTDIV1_MASK GENMASK(29, 28) +#define JH7110_PLL_PREDIV_SHIFT 0 +#define JH7110_PLL_PREDIV_MASK GENMASK(5, 0) + +enum jh7110_pll_mode { + JH7110_PLL_MODE_FRACTION, + JH7110_PLL_MODE_INTEGER, +}; + +struct jh7110_pll_preset { + unsigned long freq; + u32 frac; /* frac value should be decimals multiplied by 2^24 */ + unsigned fbdiv : 12; /* fbdiv value should be 8 to 4095 */ + unsigned prediv : 6; + unsigned postdiv1 : 2; + unsigned mode : 1; +}; + +struct jh7110_pll_info { + char *name; + const struct jh7110_pll_preset *presets; + unsigned int npresets; + struct { + unsigned int pd; + unsigned int fbdiv; + unsigned int frac; + unsigned int prediv; + } offsets; + struct { + u32 dacpd; + u32 dsmpd; + u32 fbdiv; + } masks; + struct { + char dacpd; + char dsmpd; + char fbdiv; + } shifts; +}; + +#define _JH7110_PLL(_idx, _name, _presets) \ + [_idx] = { \ + .name = _name, \ + .presets = _presets, \ + .npresets = ARRAY_SIZE(_presets), \ + .offsets = { \ + .pd = JH7110_PLL##_idx##_PD_OFFSET, \ + .fbdiv = JH7110_PLL##_idx##_FBDIV_OFFSET, \ + .frac = JH7110_PLL##_idx##_FRAC_OFFSET, \ + .prediv = JH7110_PLL##_idx##_PREDIV_OFFSET, \ + }, \ + .masks = { \ + .dacpd = JH7110_PLL##_idx##_DACPD_MASK, \ + .dsmpd = JH7110_PLL##_idx##_DSMPD_MASK, \ + .fbdiv = JH7110_PLL##_idx##_FBDIV_MASK, \ + }, \ + .shifts = { \ + .dacpd = JH7110_PLL##_idx##_DACPD_SHIFT, \ + .dsmpd = JH7110_PLL##_idx##_DSMPD_SHIFT, \ + .fbdiv = JH7110_PLL##_idx##_FBDIV_SHIFT, \ + }, \ + } +#define JH7110_PLL(idx, name, presets) _JH7110_PLL(idx, name, presets) + +struct jh7110_pll_data { + struct clk_hw hw; + unsigned int idx; +}; + +struct jh7110_pll_priv { + struct device *dev; + struct regmap *regmap; + struct jh7110_pll_data pll[JH7110_PLLCLK_END]; +}; + +struct jh7110_pll_regvals { + u32 dacpd; + u32 dsmpd; + u32 fbdiv; + u32 frac; + u32 postdiv1; + u32 prediv; +}; + +/* + * Because the pll frequency is relatively fixed, + * it cannot be set arbitrarily, so it needs a specific configuration. + * PLL0 frequency should be multiple of 125MHz (USB frequency). + */ +static const struct jh7110_pll_preset jh7110_pll0_presets[] = { + { + .freq = 375000000, + .fbdiv = 125, + .prediv = 8, + .postdiv1 = 0, + .mode = JH7110_PLL_MODE_INTEGER, + }, { + .freq = 500000000, + .fbdiv = 125, + .prediv = 6, + .postdiv1 = 0, + .mode = JH7110_PLL_MODE_INTEGER, + }, { + .freq = 625000000, + .fbdiv = 625, + .prediv = 24, + .postdiv1 = 0, + .mode = JH7110_PLL_MODE_INTEGER, + }, { + .freq = 750000000, + .fbdiv = 125, + .prediv = 4, + .postdiv1 = 0, + .mode = JH7110_PLL_MODE_INTEGER, + }, { + .freq = 875000000, + .fbdiv = 875, + .prediv = 24, + .postdiv1 = 0, + .mode = JH7110_PLL_MODE_INTEGER, + }, { + .freq = 1000000000, + .fbdiv = 125, + .prediv = 3, + .postdiv1 = 0, + .mode = JH7110_PLL_MODE_INTEGER, + }, { + .freq = 1250000000, + .fbdiv = 625, + .prediv = 12, + .postdiv1 = 0, + .mode = JH7110_PLL_MODE_INTEGER, + }, { + .freq = 1375000000, + .fbdiv = 1375, + .prediv = 24, + .postdiv1 = 0, + .mode = JH7110_PLL_MODE_INTEGER, + }, { + .freq = 1500000000, + .fbdiv = 125, + .prediv = 2, + .postdiv1 = 0, + .mode = JH7110_PLL_MODE_INTEGER, + }, +}; + +static const struct jh7110_pll_preset jh7110_pll1_presets[] = { + { + .freq = 1066000000, + .fbdiv = 533, + .prediv = 12, + .postdiv1 = 0, + .mode = JH7110_PLL_MODE_INTEGER, + }, { + .freq = 1200000000, + .fbdiv = 50, + .prediv = 1, + .postdiv1 = 0, + .mode = JH7110_PLL_MODE_INTEGER, + }, { + .freq = 1400000000, + .fbdiv = 350, + .prediv = 6, + .postdiv1 = 0, + .mode = JH7110_PLL_MODE_INTEGER, + }, { + .freq = 1600000000, + .fbdiv = 200, + .prediv = 3, + .postdiv1 = 0, + .mode = JH7110_PLL_MODE_INTEGER, + }, +}; + +static const struct jh7110_pll_preset jh7110_pll2_presets[] = { + { + .freq = 1188000000, + .fbdiv = 99, + .prediv = 2, + .postdiv1 = 0, + .mode = JH7110_PLL_MODE_INTEGER, + }, { + .freq = 1228800000, + .fbdiv = 256, + .prediv = 5, + .postdiv1 = 0, + .mode = JH7110_PLL_MODE_INTEGER, + }, +}; + +static const struct jh7110_pll_info jh7110_plls[JH7110_PLLCLK_END] = { + JH7110_PLL(JH7110_PLLCLK_PLL0_OUT, "pll0_out", jh7110_pll0_presets), + JH7110_PLL(JH7110_PLLCLK_PLL1_OUT, "pll1_out", jh7110_pll1_presets), + JH7110_PLL(JH7110_PLLCLK_PLL2_OUT, "pll2_out", jh7110_pll2_presets), +}; + +static struct jh7110_pll_data *jh7110_pll_data_from(struct clk_hw *hw) +{ + return container_of(hw, struct jh7110_pll_data, hw); +} + +static struct jh7110_pll_priv *jh7110_pll_priv_from(struct jh7110_pll_data *pll) +{ + return container_of(pll, struct jh7110_pll_priv, pll[pll->idx]); +} + +static void jh7110_pll_regvals_get(struct regmap *regmap, + const struct jh7110_pll_info *info, + struct jh7110_pll_regvals *ret) +{ + u32 val; + + regmap_read(regmap, info->offsets.pd, &val); + ret->dacpd = (val & info->masks.dacpd) >> info->shifts.dacpd; + ret->dsmpd = (val & info->masks.dsmpd) >> info->shifts.dsmpd; + + regmap_read(regmap, info->offsets.fbdiv, &val); + ret->fbdiv = (val & info->masks.fbdiv) >> info->shifts.fbdiv; + + regmap_read(regmap, info->offsets.frac, &val); + ret->frac = (val & JH7110_PLL_FRAC_MASK) >> JH7110_PLL_FRAC_SHIFT; + ret->postdiv1 = (val & JH7110_PLL_POSTDIV1_MASK) >> JH7110_PLL_POSTDIV1_SHIFT; + + regmap_read(regmap, info->offsets.prediv, &val); + ret->prediv = (val & JH7110_PLL_PREDIV_MASK) >> JH7110_PLL_PREDIV_SHIFT; +} + +static unsigned long jh7110_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct jh7110_pll_data *pll = jh7110_pll_data_from(hw); + struct jh7110_pll_priv *priv = jh7110_pll_priv_from(pll); + struct jh7110_pll_regvals val; + unsigned long rate; + + jh7110_pll_regvals_get(priv->regmap, &jh7110_plls[pll->idx], &val); + + /* + * dacpd = dsmpd = 0: fraction mode + * dacpd = dsmpd = 1: integer mode, frac value ignored + * + * rate = parent * (fbdiv + frac/2^24) / prediv / 2^postdiv1 + * = (parent * fbdiv + parent * frac / 2^24) / (prediv * 2^postdiv1) + */ + if (val.dacpd == 0 && val.dsmpd == 0) + rate = parent_rate * val.frac / (1UL << 24); + else if (val.dacpd == 1 && val.dsmpd == 1) + rate = 0; + else + return 0; + + rate += parent_rate * val.fbdiv; + rate /= val.prediv << val.postdiv1; + + return rate; +} + +static int jh7110_pll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) +{ + struct jh7110_pll_data *pll = jh7110_pll_data_from(hw); + const struct jh7110_pll_info *info = &jh7110_plls[pll->idx]; + const struct jh7110_pll_preset *selected = &info->presets[0]; + unsigned int idx; + + /* if the parent rate doesn't match our expectations the presets won't work */ + if (req->best_parent_rate != JH7110_PLL_OSC_RATE) { + req->rate = jh7110_pll_recalc_rate(hw, req->best_parent_rate); + return 0; + } + + /* find highest rate lower or equal to the requested rate */ + for (idx = 1; idx < info->npresets; idx++) { + const struct jh7110_pll_preset *val = &info->presets[idx]; + + if (req->rate < val->freq) + break; + + selected = val; + } + + req->rate = selected->freq; + return 0; +} + +static int jh7110_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct jh7110_pll_data *pll = jh7110_pll_data_from(hw); + struct jh7110_pll_priv *priv = jh7110_pll_priv_from(pll); + const struct jh7110_pll_info *info = &jh7110_plls[pll->idx]; + const struct jh7110_pll_preset *val; + unsigned int idx; + + /* if the parent rate doesn't match our expectations the presets won't work */ + if (parent_rate != JH7110_PLL_OSC_RATE) + return -EINVAL; + + for (idx = 0, val = &info->presets[0]; idx < info->npresets; idx++, val++) { + if (val->freq == rate) + goto found; + } + return -EINVAL; + +found: + if (val->mode == JH7110_PLL_MODE_FRACTION) + regmap_update_bits(priv->regmap, info->offsets.frac, JH7110_PLL_FRAC_MASK, + val->frac << JH7110_PLL_FRAC_SHIFT); + + regmap_update_bits(priv->regmap, info->offsets.pd, info->masks.dacpd, + (u32)val->mode << info->shifts.dacpd); + regmap_update_bits(priv->regmap, info->offsets.pd, info->masks.dsmpd, + (u32)val->mode << info->shifts.dsmpd); + regmap_update_bits(priv->regmap, info->offsets.prediv, JH7110_PLL_PREDIV_MASK, + (u32)val->prediv << JH7110_PLL_PREDIV_SHIFT); + regmap_update_bits(priv->regmap, info->offsets.fbdiv, info->masks.fbdiv, + val->fbdiv << info->shifts.fbdiv); + regmap_update_bits(priv->regmap, info->offsets.frac, JH7110_PLL_POSTDIV1_MASK, + (u32)val->postdiv1 << JH7110_PLL_POSTDIV1_SHIFT); + + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static int jh7110_pll_registers_read(struct seq_file *s, void *unused) +{ + struct jh7110_pll_data *pll = s->private; + struct jh7110_pll_priv *priv = jh7110_pll_priv_from(pll); + struct jh7110_pll_regvals val; + + jh7110_pll_regvals_get(priv->regmap, &jh7110_plls[pll->idx], &val); + + seq_printf(s, "fbdiv=%u\n" + "frac=%u\n" + "prediv=%u\n" + "postdiv1=%u\n" + "dacpd=%u\n" + "dsmpd=%u\n", + val.fbdiv, val.frac, val.prediv, val.postdiv1, + val.dacpd, val.dsmpd); + + return 0; +} + +static int jh7110_pll_registers_open(struct inode *inode, struct file *f) +{ + return single_open(f, jh7110_pll_registers_read, inode->i_private); +} + +static const struct file_operations jh7110_pll_registers_ops = { + .owner = THIS_MODULE, + .open = jh7110_pll_registers_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek +}; + +static void jh7110_pll_debug_init(struct clk_hw *hw, struct dentry *dentry) +{ + struct jh7110_pll_data *pll = jh7110_pll_data_from(hw); + + debugfs_create_file("registers", 0400, dentry, pll, + &jh7110_pll_registers_ops); +} +#else +#define jh7110_pll_debug_init NULL +#endif + +static const struct clk_ops jh7110_pll_ops = { + .recalc_rate = jh7110_pll_recalc_rate, + .determine_rate = jh7110_pll_determine_rate, + .set_rate = jh7110_pll_set_rate, + .debug_init = jh7110_pll_debug_init, +}; + +static struct clk_hw *jh7110_pll_get(struct of_phandle_args *clkspec, void *data) +{ + struct jh7110_pll_priv *priv = data; + unsigned int idx = clkspec->args[0]; + + if (idx < JH7110_PLLCLK_END) + return &priv->pll[idx].hw; + + return ERR_PTR(-EINVAL); +} + +static int jh7110_pll_probe(struct platform_device *pdev) +{ + struct jh7110_pll_priv *priv; + unsigned int idx; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + priv->regmap = syscon_node_to_regmap(priv->dev->of_node->parent); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + for (idx = 0; idx < JH7110_PLLCLK_END; idx++) { + struct clk_parent_data parents = { + .index = 0, + }; + struct clk_init_data init = { + .name = jh7110_plls[idx].name, + .ops = &jh7110_pll_ops, + .parent_data = &parents, + .num_parents = 1, + .flags = 0, + }; + struct jh7110_pll_data *pll = &priv->pll[idx]; + + pll->hw.init = &init; + pll->idx = idx; + + ret = devm_clk_hw_register(&pdev->dev, &pll->hw); + if (ret) + return ret; + } + + return devm_of_clk_add_hw_provider(&pdev->dev, jh7110_pll_get, priv); +} + +static const struct of_device_id jh7110_pll_match[] = { + { .compatible = "starfive,jh7110-pll" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jh7110_pll_match); + +static struct platform_driver jh7110_pll_driver = { + .driver = { + .name = "clk-starfive-jh7110-pll", + .of_match_table = jh7110_pll_match, + }, +}; +builtin_platform_driver_probe(jh7110_pll_driver, jh7110_pll_probe); From c671f4f0b714a5d2826ec0267fe1ca8862ccc1d0 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Mon, 17 Jul 2023 10:30:38 +0800 Subject: [PATCH 10/92] clk: starfive: jh7110-sys: Add PLL clocks source from DTS Modify PLL clocks source to be got from DTS or the fixed factor clocks. Signed-off-by: Xingyu Wu Reviewed-by: Emil Renner Berthing Signed-off-by: Conor Dooley --- drivers/clk/starfive/Kconfig | 1 + .../clk/starfive/clk-starfive-jh7110-sys.c | 66 ++++++++++++------- 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/drivers/clk/starfive/Kconfig b/drivers/clk/starfive/Kconfig index 5195f7be5213c..978b78ec08b1b 100644 --- a/drivers/clk/starfive/Kconfig +++ b/drivers/clk/starfive/Kconfig @@ -35,6 +35,7 @@ config CLK_STARFIVE_JH7110_SYS select AUXILIARY_BUS select CLK_STARFIVE_JH71X0 select RESET_STARFIVE_JH7110 if RESET_CONTROLLER + select CLK_STARFIVE_JH7110_PLL default ARCH_STARFIVE help Say yes here to support the system clock controller on the diff --git a/drivers/clk/starfive/clk-starfive-jh7110-sys.c b/drivers/clk/starfive/clk-starfive-jh7110-sys.c index e6031345ef05d..3884eff9fe931 100644 --- a/drivers/clk/starfive/clk-starfive-jh7110-sys.c +++ b/drivers/clk/starfive/clk-starfive-jh7110-sys.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -389,6 +390,7 @@ static int __init jh7110_syscrg_probe(struct platform_device *pdev) struct jh71x0_clk_priv *priv; unsigned int idx; int ret; + struct clk *pllclk; priv = devm_kzalloc(&pdev->dev, struct_size(priv, reg, JH7110_SYSCLK_END), @@ -402,28 +404,42 @@ static int __init jh7110_syscrg_probe(struct platform_device *pdev) if (IS_ERR(priv->base)) return PTR_ERR(priv->base); - /* - * These PLL clocks are not actually fixed factor clocks and can be - * controlled by the syscon registers of JH7110. They will be dropped - * and registered in the PLL clock driver instead. - */ - /* 24MHz -> 1000.0MHz */ - priv->pll[0] = devm_clk_hw_register_fixed_factor(priv->dev, "pll0_out", - "osc", 0, 125, 3); - if (IS_ERR(priv->pll[0])) - return PTR_ERR(priv->pll[0]); - - /* 24MHz -> 1066.0MHz */ - priv->pll[1] = devm_clk_hw_register_fixed_factor(priv->dev, "pll1_out", - "osc", 0, 533, 12); - if (IS_ERR(priv->pll[1])) - return PTR_ERR(priv->pll[1]); - - /* 24MHz -> 1188.0MHz */ - priv->pll[2] = devm_clk_hw_register_fixed_factor(priv->dev, "pll2_out", - "osc", 0, 99, 2); - if (IS_ERR(priv->pll[2])) - return PTR_ERR(priv->pll[2]); + /* Use fixed factor clocks if can not get the PLL clocks from DTS */ + pllclk = clk_get(priv->dev, "pll0_out"); + if (IS_ERR(pllclk)) { + /* 24MHz -> 1000.0MHz */ + priv->pll[0] = devm_clk_hw_register_fixed_factor(priv->dev, "pll0_out", + "osc", 0, 125, 3); + if (IS_ERR(priv->pll[0])) + return PTR_ERR(priv->pll[0]); + } else { + clk_put(pllclk); + priv->pll[0] = NULL; + } + + pllclk = clk_get(priv->dev, "pll1_out"); + if (IS_ERR(pllclk)) { + /* 24MHz -> 1066.0MHz */ + priv->pll[1] = devm_clk_hw_register_fixed_factor(priv->dev, "pll1_out", + "osc", 0, 533, 12); + if (IS_ERR(priv->pll[1])) + return PTR_ERR(priv->pll[1]); + } else { + clk_put(pllclk); + priv->pll[1] = NULL; + } + + pllclk = clk_get(priv->dev, "pll2_out"); + if (IS_ERR(pllclk)) { + /* 24MHz -> 1188.0MHz */ + priv->pll[2] = devm_clk_hw_register_fixed_factor(priv->dev, "pll2_out", + "osc", 0, 99, 2); + if (IS_ERR(priv->pll[2])) + return PTR_ERR(priv->pll[2]); + } else { + clk_put(pllclk); + priv->pll[2] = NULL; + } for (idx = 0; idx < JH7110_SYSCLK_END; idx++) { u32 max = jh7110_sysclk_data[idx].max; @@ -462,6 +478,12 @@ static int __init jh7110_syscrg_probe(struct platform_device *pdev) parents[i].fw_name = "tdm_ext"; else if (pidx == JH7110_SYSCLK_MCLK_EXT) parents[i].fw_name = "mclk_ext"; + else if (pidx == JH7110_SYSCLK_PLL0_OUT && !priv->pll[0]) + parents[i].fw_name = "pll0_out"; + else if (pidx == JH7110_SYSCLK_PLL1_OUT && !priv->pll[1]) + parents[i].fw_name = "pll1_out"; + else if (pidx == JH7110_SYSCLK_PLL2_OUT && !priv->pll[2]) + parents[i].fw_name = "pll2_out"; else parents[i].hw = priv->pll[pidx - JH7110_SYSCLK_PLL0_OUT]; } From 8c8dc30d1d9c2a0b5caa197604995d03b6cc5962 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Thu, 13 Jul 2023 19:38:55 +0800 Subject: [PATCH 11/92] clk: starfive: Add StarFive JH7110 System-Top-Group clock driver Add driver for the StarFive JH7110 System-Top-Group clock controller. Acked-by: Palmer Dabbelt Reviewed-by: Hal Feng Co-developed-by: Xingyu Wu Signed-off-by: Xingyu Wu Signed-off-by: Emil Renner Berthing Signed-off-by: Conor Dooley --- drivers/clk/starfive/Kconfig | 8 + drivers/clk/starfive/Makefile | 1 + .../clk/starfive/clk-starfive-jh7110-stg.c | 173 ++++++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 drivers/clk/starfive/clk-starfive-jh7110-stg.c diff --git a/drivers/clk/starfive/Kconfig b/drivers/clk/starfive/Kconfig index 978b78ec08b1b..f5a460b75460b 100644 --- a/drivers/clk/starfive/Kconfig +++ b/drivers/clk/starfive/Kconfig @@ -48,3 +48,11 @@ config CLK_STARFIVE_JH7110_AON help Say yes here to support the always-on clock controller on the StarFive JH7110 SoC. + +config CLK_STARFIVE_JH7110_STG + tristate "StarFive JH7110 System-Top-Group clock support" + depends on CLK_STARFIVE_JH7110_SYS + default m if ARCH_STARFIVE + help + Say yes here to support the System-Top-Group clock controller + on the StarFive JH7110 SoC. diff --git a/drivers/clk/starfive/Makefile b/drivers/clk/starfive/Makefile index b48e539e52b01..8652d1f90ae9b 100644 --- a/drivers/clk/starfive/Makefile +++ b/drivers/clk/starfive/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_CLK_STARFIVE_JH7100_AUDIO) += clk-starfive-jh7100-audio.o obj-$(CONFIG_CLK_STARFIVE_JH7110_PLL) += clk-starfive-jh7110-pll.o obj-$(CONFIG_CLK_STARFIVE_JH7110_SYS) += clk-starfive-jh7110-sys.o obj-$(CONFIG_CLK_STARFIVE_JH7110_AON) += clk-starfive-jh7110-aon.o +obj-$(CONFIG_CLK_STARFIVE_JH7110_STG) += clk-starfive-jh7110-stg.o diff --git a/drivers/clk/starfive/clk-starfive-jh7110-stg.c b/drivers/clk/starfive/clk-starfive-jh7110-stg.c new file mode 100644 index 0000000000000..dafcb71905922 --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7110-stg.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7110 System-Top-Group Clock Driver + * + * Copyright (C) 2022 Emil Renner Berthing + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#include +#include +#include + +#include + +#include "clk-starfive-jh7110.h" + +/* external clocks */ +#define JH7110_STGCLK_OSC (JH7110_STGCLK_END + 0) +#define JH7110_STGCLK_HIFI4_CORE (JH7110_STGCLK_END + 1) +#define JH7110_STGCLK_STG_AXIAHB (JH7110_STGCLK_END + 2) +#define JH7110_STGCLK_USB_125M (JH7110_STGCLK_END + 3) +#define JH7110_STGCLK_CPU_BUS (JH7110_STGCLK_END + 4) +#define JH7110_STGCLK_HIFI4_AXI (JH7110_STGCLK_END + 5) +#define JH7110_STGCLK_NOCSTG_BUS (JH7110_STGCLK_END + 6) +#define JH7110_STGCLK_APB_BUS (JH7110_STGCLK_END + 7) +#define JH7110_STGCLK_EXT_END (JH7110_STGCLK_END + 8) + +static const struct jh71x0_clk_data jh7110_stgclk_data[] = { + /* hifi4 */ + JH71X0_GATE(JH7110_STGCLK_HIFI4_CLK_CORE, "hifi4_clk_core", 0, + JH7110_STGCLK_HIFI4_CORE), + /* usb */ + JH71X0_GATE(JH7110_STGCLK_USB0_APB, "usb0_apb", 0, JH7110_STGCLK_APB_BUS), + JH71X0_GATE(JH7110_STGCLK_USB0_UTMI_APB, "usb0_utmi_apb", 0, JH7110_STGCLK_APB_BUS), + JH71X0_GATE(JH7110_STGCLK_USB0_AXI, "usb0_axi", 0, JH7110_STGCLK_STG_AXIAHB), + JH71X0_GDIV(JH7110_STGCLK_USB0_LPM, "usb0_lpm", 0, 2, JH7110_STGCLK_OSC), + JH71X0_GDIV(JH7110_STGCLK_USB0_STB, "usb0_stb", 0, 4, JH7110_STGCLK_OSC), + JH71X0_GATE(JH7110_STGCLK_USB0_APP_125, "usb0_app_125", 0, JH7110_STGCLK_USB_125M), + JH71X0__DIV(JH7110_STGCLK_USB0_REFCLK, "usb0_refclk", 2, JH7110_STGCLK_OSC), + /* pci-e */ + JH71X0_GATE(JH7110_STGCLK_PCIE0_AXI_MST0, "pcie0_axi_mst0", 0, + JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_PCIE0_APB, "pcie0_apb", 0, JH7110_STGCLK_APB_BUS), + JH71X0_GATE(JH7110_STGCLK_PCIE0_TL, "pcie0_tl", 0, JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_PCIE1_AXI_MST0, "pcie1_axi_mst0", 0, + JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_PCIE1_APB, "pcie1_apb", 0, JH7110_STGCLK_APB_BUS), + JH71X0_GATE(JH7110_STGCLK_PCIE1_TL, "pcie1_tl", 0, JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_PCIE_SLV_MAIN, "pcie_slv_main", CLK_IS_CRITICAL, + JH7110_STGCLK_STG_AXIAHB), + /* security */ + JH71X0_GATE(JH7110_STGCLK_SEC_AHB, "sec_ahb", 0, JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_SEC_MISC_AHB, "sec_misc_ahb", 0, JH7110_STGCLK_STG_AXIAHB), + /* stg mtrx */ + JH71X0_GATE(JH7110_STGCLK_GRP0_MAIN, "mtrx_grp0_main", CLK_IS_CRITICAL, + JH7110_STGCLK_CPU_BUS), + JH71X0_GATE(JH7110_STGCLK_GRP0_BUS, "mtrx_grp0_bus", CLK_IS_CRITICAL, + JH7110_STGCLK_NOCSTG_BUS), + JH71X0_GATE(JH7110_STGCLK_GRP0_STG, "mtrx_grp0_stg", CLK_IS_CRITICAL, + JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_GRP1_MAIN, "mtrx_grp1_main", CLK_IS_CRITICAL, + JH7110_STGCLK_CPU_BUS), + JH71X0_GATE(JH7110_STGCLK_GRP1_BUS, "mtrx_grp1_bus", CLK_IS_CRITICAL, + JH7110_STGCLK_NOCSTG_BUS), + JH71X0_GATE(JH7110_STGCLK_GRP1_STG, "mtrx_grp1_stg", CLK_IS_CRITICAL, + JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_GRP1_HIFI, "mtrx_grp1_hifi", CLK_IS_CRITICAL, + JH7110_STGCLK_HIFI4_AXI), + /* e24_rvpi */ + JH71X0_GDIV(JH7110_STGCLK_E2_RTC, "e2_rtc", 0, 24, JH7110_STGCLK_OSC), + JH71X0_GATE(JH7110_STGCLK_E2_CORE, "e2_core", 0, JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_E2_DBG, "e2_dbg", 0, JH7110_STGCLK_STG_AXIAHB), + /* dw_sgdma1p */ + JH71X0_GATE(JH7110_STGCLK_DMA1P_AXI, "dma1p_axi", 0, JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_DMA1P_AHB, "dma1p_ahb", 0, JH7110_STGCLK_STG_AXIAHB), +}; + +static struct clk_hw *jh7110_stgclk_get(struct of_phandle_args *clkspec, void *data) +{ + struct jh71x0_clk_priv *priv = data; + unsigned int idx = clkspec->args[0]; + + if (idx < JH7110_STGCLK_END) + return &priv->reg[idx].hw; + + return ERR_PTR(-EINVAL); +} + +static int jh7110_stgcrg_probe(struct platform_device *pdev) +{ + struct jh71x0_clk_priv *priv; + unsigned int idx; + int ret; + + priv = devm_kzalloc(&pdev->dev, struct_size(priv, reg, JH7110_STGCLK_END), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->rmw_lock); + priv->dev = &pdev->dev; + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + for (idx = 0; idx < JH7110_STGCLK_END; idx++) { + u32 max = jh7110_stgclk_data[idx].max; + struct clk_parent_data parents[4] = {}; + struct clk_init_data init = { + .name = jh7110_stgclk_data[idx].name, + .ops = starfive_jh71x0_clk_ops(max), + .parent_data = parents, + .num_parents = + ((max & JH71X0_CLK_MUX_MASK) >> JH71X0_CLK_MUX_SHIFT) + 1, + .flags = jh7110_stgclk_data[idx].flags, + }; + struct jh71x0_clk *clk = &priv->reg[idx]; + const char *fw_name[JH7110_STGCLK_EXT_END - JH7110_STGCLK_END] = { + "osc", + "hifi4_core", + "stg_axiahb", + "usb_125m", + "cpu_bus", + "hifi4_axi", + "nocstg_bus", + "apb_bus" + }; + unsigned int i; + + for (i = 0; i < init.num_parents; i++) { + unsigned int pidx = jh7110_stgclk_data[idx].parents[i]; + + if (pidx < JH7110_STGCLK_END) + parents[i].hw = &priv->reg[pidx].hw; + else if (pidx < JH7110_STGCLK_EXT_END) + parents[i].fw_name = fw_name[pidx - JH7110_STGCLK_END]; + } + + clk->hw.init = &init; + clk->idx = idx; + clk->max_div = max & JH71X0_CLK_DIV_MASK; + + ret = devm_clk_hw_register(&pdev->dev, &clk->hw); + if (ret) + return ret; + } + + ret = devm_of_clk_add_hw_provider(&pdev->dev, jh7110_stgclk_get, priv); + if (ret) + return ret; + + return jh7110_reset_controller_register(priv, "rst-stg", 2); +} + +static const struct of_device_id jh7110_stgcrg_match[] = { + { .compatible = "starfive,jh7110-stgcrg" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jh7110_stgcrg_match); + +static struct platform_driver jh7110_stgcrg_driver = { + .probe = jh7110_stgcrg_probe, + .driver = { + .name = "clk-starfive-jh7110-stg", + .of_match_table = jh7110_stgcrg_match, + }, +}; +module_platform_driver(jh7110_stgcrg_driver); + +MODULE_AUTHOR("Xingyu Wu "); +MODULE_AUTHOR("Emil Renner Berthing "); +MODULE_DESCRIPTION("StarFive JH7110 System-Top-Group clock driver"); +MODULE_LICENSE("GPL"); From a9689d911a231adbf8313727c71300c74e8d05cc Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Thu, 13 Jul 2023 19:38:57 +0800 Subject: [PATCH 12/92] clk: starfive: Add StarFive JH7110 Image-Signal-Process clock driver Add driver for the StarFive JH7110 Image-Signal-Process clock controller. And these clock controllers should power on and enable the clocks from SYSCRG before registering. Acked-by: Palmer Dabbelt Reviewed-by: Emil Renner Berthing Reviewed-by: Hal Feng Signed-off-by: Xingyu Wu Signed-off-by: Conor Dooley --- drivers/clk/starfive/Kconfig | 8 + drivers/clk/starfive/Makefile | 1 + .../clk/starfive/clk-starfive-jh7110-isp.c | 232 ++++++++++++++++++ drivers/clk/starfive/clk-starfive-jh7110.h | 6 + 4 files changed, 247 insertions(+) create mode 100644 drivers/clk/starfive/clk-starfive-jh7110-isp.c diff --git a/drivers/clk/starfive/Kconfig b/drivers/clk/starfive/Kconfig index f5a460b75460b..68bdcedad1907 100644 --- a/drivers/clk/starfive/Kconfig +++ b/drivers/clk/starfive/Kconfig @@ -56,3 +56,11 @@ config CLK_STARFIVE_JH7110_STG help Say yes here to support the System-Top-Group clock controller on the StarFive JH7110 SoC. + +config CLK_STARFIVE_JH7110_ISP + tristate "StarFive JH7110 Image-Signal-Process clock support" + depends on CLK_STARFIVE_JH7110_SYS && JH71XX_PMU + default m if ARCH_STARFIVE + help + Say yes here to support the Image-Signal-Process clock controller + on the StarFive JH7110 SoC. diff --git a/drivers/clk/starfive/Makefile b/drivers/clk/starfive/Makefile index 8652d1f90ae9b..3063bcab60476 100644 --- a/drivers/clk/starfive/Makefile +++ b/drivers/clk/starfive/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_CLK_STARFIVE_JH7110_PLL) += clk-starfive-jh7110-pll.o obj-$(CONFIG_CLK_STARFIVE_JH7110_SYS) += clk-starfive-jh7110-sys.o obj-$(CONFIG_CLK_STARFIVE_JH7110_AON) += clk-starfive-jh7110-aon.o obj-$(CONFIG_CLK_STARFIVE_JH7110_STG) += clk-starfive-jh7110-stg.o +obj-$(CONFIG_CLK_STARFIVE_JH7110_ISP) += clk-starfive-jh7110-isp.o diff --git a/drivers/clk/starfive/clk-starfive-jh7110-isp.c b/drivers/clk/starfive/clk-starfive-jh7110-isp.c new file mode 100644 index 0000000000000..ce034ed285321 --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7110-isp.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7110 Image-Signal-Process Clock Driver + * + * Copyright (C) 2022-2023 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "clk-starfive-jh7110.h" + +/* external clocks */ +#define JH7110_ISPCLK_ISP_TOP_CORE (JH7110_ISPCLK_END + 0) +#define JH7110_ISPCLK_ISP_TOP_AXI (JH7110_ISPCLK_END + 1) +#define JH7110_ISPCLK_NOC_BUS_ISP_AXI (JH7110_ISPCLK_END + 2) +#define JH7110_ISPCLK_DVP_CLK (JH7110_ISPCLK_END + 3) +#define JH7110_ISPCLK_EXT_END (JH7110_ISPCLK_END + 4) + +static struct clk_bulk_data jh7110_isp_top_clks[] = { + { .id = "isp_top_core" }, + { .id = "isp_top_axi" } +}; + +static const struct jh71x0_clk_data jh7110_ispclk_data[] = { + /* syscon */ + JH71X0__DIV(JH7110_ISPCLK_DOM4_APB_FUNC, "dom4_apb_func", 15, + JH7110_ISPCLK_ISP_TOP_AXI), + JH71X0__DIV(JH7110_ISPCLK_MIPI_RX0_PXL, "mipi_rx0_pxl", 8, + JH7110_ISPCLK_ISP_TOP_CORE), + JH71X0__INV(JH7110_ISPCLK_DVP_INV, "dvp_inv", JH7110_ISPCLK_DVP_CLK), + /* vin */ + JH71X0__DIV(JH7110_ISPCLK_M31DPHY_CFG_IN, "m31dphy_cfg_in", 16, + JH7110_ISPCLK_ISP_TOP_CORE), + JH71X0__DIV(JH7110_ISPCLK_M31DPHY_REF_IN, "m31dphy_ref_in", 16, + JH7110_ISPCLK_ISP_TOP_CORE), + JH71X0__DIV(JH7110_ISPCLK_M31DPHY_TX_ESC_LAN0, "m31dphy_tx_esc_lan0", 60, + JH7110_ISPCLK_ISP_TOP_CORE), + JH71X0_GATE(JH7110_ISPCLK_VIN_APB, "vin_apb", 0, + JH7110_ISPCLK_DOM4_APB_FUNC), + JH71X0__DIV(JH7110_ISPCLK_VIN_SYS, "vin_sys", 8, JH7110_ISPCLK_ISP_TOP_CORE), + JH71X0_GATE(JH7110_ISPCLK_VIN_PIXEL_IF0, "vin_pixel_if0", 0, + JH7110_ISPCLK_MIPI_RX0_PXL), + JH71X0_GATE(JH7110_ISPCLK_VIN_PIXEL_IF1, "vin_pixel_if1", 0, + JH7110_ISPCLK_MIPI_RX0_PXL), + JH71X0_GATE(JH7110_ISPCLK_VIN_PIXEL_IF2, "vin_pixel_if2", 0, + JH7110_ISPCLK_MIPI_RX0_PXL), + JH71X0_GATE(JH7110_ISPCLK_VIN_PIXEL_IF3, "vin_pixel_if3", 0, + JH7110_ISPCLK_MIPI_RX0_PXL), + JH71X0__MUX(JH7110_ISPCLK_VIN_P_AXI_WR, "vin_p_axi_wr", 2, + JH7110_ISPCLK_MIPI_RX0_PXL, + JH7110_ISPCLK_DVP_INV), + /* ispv2_top_wrapper */ + JH71X0_GMUX(JH7110_ISPCLK_ISPV2_TOP_WRAPPER_C, "ispv2_top_wrapper_c", 0, 2, + JH7110_ISPCLK_MIPI_RX0_PXL, + JH7110_ISPCLK_DVP_INV), +}; + +static inline int jh7110_isp_top_rst_init(struct jh71x0_clk_priv *priv) +{ + struct reset_control *top_rsts; + + /* The resets should be shared and other ISP modules will use its. */ + top_rsts = devm_reset_control_array_get_shared(priv->dev); + if (IS_ERR(top_rsts)) + return dev_err_probe(priv->dev, PTR_ERR(top_rsts), + "failed to get top resets\n"); + + return reset_control_deassert(top_rsts); +} + +static struct clk_hw *jh7110_ispclk_get(struct of_phandle_args *clkspec, void *data) +{ + struct jh71x0_clk_priv *priv = data; + unsigned int idx = clkspec->args[0]; + + if (idx < JH7110_ISPCLK_END) + return &priv->reg[idx].hw; + + return ERR_PTR(-EINVAL); +} + +#ifdef CONFIG_PM +static int jh7110_ispcrg_suspend(struct device *dev) +{ + struct jh7110_top_sysclk *top = dev_get_drvdata(dev); + + clk_bulk_disable_unprepare(top->top_clks_num, top->top_clks); + + return 0; +} + +static int jh7110_ispcrg_resume(struct device *dev) +{ + struct jh7110_top_sysclk *top = dev_get_drvdata(dev); + + return clk_bulk_prepare_enable(top->top_clks_num, top->top_clks); +} + +static const struct dev_pm_ops jh7110_ispcrg_pm_ops = { + RUNTIME_PM_OPS(jh7110_ispcrg_suspend, jh7110_ispcrg_resume, NULL) +}; +#endif + +static int jh7110_ispcrg_probe(struct platform_device *pdev) +{ + struct jh71x0_clk_priv *priv; + struct jh7110_top_sysclk *top; + unsigned int idx; + int ret; + + priv = devm_kzalloc(&pdev->dev, + struct_size(priv, reg, JH7110_ISPCLK_END), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + top = devm_kzalloc(&pdev->dev, sizeof(*top), GFP_KERNEL); + if (!top) + return -ENOMEM; + + spin_lock_init(&priv->rmw_lock); + priv->dev = &pdev->dev; + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + top->top_clks = jh7110_isp_top_clks; + top->top_clks_num = ARRAY_SIZE(jh7110_isp_top_clks); + ret = devm_clk_bulk_get(priv->dev, top->top_clks_num, top->top_clks); + if (ret) + return dev_err_probe(priv->dev, ret, "failed to get main clocks\n"); + dev_set_drvdata(priv->dev, top); + + /* enable power domain and clocks */ + pm_runtime_enable(priv->dev); + ret = pm_runtime_get_sync(priv->dev); + if (ret < 0) + return dev_err_probe(priv->dev, ret, "failed to turn on power\n"); + + ret = jh7110_isp_top_rst_init(priv); + if (ret) + goto err_exit; + + for (idx = 0; idx < JH7110_ISPCLK_END; idx++) { + u32 max = jh7110_ispclk_data[idx].max; + struct clk_parent_data parents[4] = {}; + struct clk_init_data init = { + .name = jh7110_ispclk_data[idx].name, + .ops = starfive_jh71x0_clk_ops(max), + .parent_data = parents, + .num_parents = + ((max & JH71X0_CLK_MUX_MASK) >> JH71X0_CLK_MUX_SHIFT) + 1, + .flags = jh7110_ispclk_data[idx].flags, + }; + struct jh71x0_clk *clk = &priv->reg[idx]; + unsigned int i; + const char *fw_name[JH7110_ISPCLK_EXT_END - JH7110_ISPCLK_END] = { + "isp_top_core", + "isp_top_axi", + "noc_bus_isp_axi", + "dvp_clk" + }; + + for (i = 0; i < init.num_parents; i++) { + unsigned int pidx = jh7110_ispclk_data[idx].parents[i]; + + if (pidx < JH7110_ISPCLK_END) + parents[i].hw = &priv->reg[pidx].hw; + else + parents[i].fw_name = fw_name[pidx - JH7110_ISPCLK_END]; + } + + clk->hw.init = &init; + clk->idx = idx; + clk->max_div = max & JH71X0_CLK_DIV_MASK; + + ret = devm_clk_hw_register(&pdev->dev, &clk->hw); + if (ret) + goto err_exit; + } + + ret = devm_of_clk_add_hw_provider(&pdev->dev, jh7110_ispclk_get, priv); + if (ret) + goto err_exit; + + ret = jh7110_reset_controller_register(priv, "rst-isp", 3); + if (ret) + goto err_exit; + + return 0; + +err_exit: + pm_runtime_put_sync(priv->dev); + pm_runtime_disable(priv->dev); + return ret; +} + +static int jh7110_ispcrg_remove(struct platform_device *pdev) +{ + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id jh7110_ispcrg_match[] = { + { .compatible = "starfive,jh7110-ispcrg" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jh7110_ispcrg_match); + +static struct platform_driver jh7110_ispcrg_driver = { + .probe = jh7110_ispcrg_probe, + .remove = jh7110_ispcrg_remove, + .driver = { + .name = "clk-starfive-jh7110-isp", + .of_match_table = jh7110_ispcrg_match, + .pm = pm_ptr(&jh7110_ispcrg_pm_ops), + }, +}; +module_platform_driver(jh7110_ispcrg_driver); + +MODULE_AUTHOR("Xingyu Wu "); +MODULE_DESCRIPTION("StarFive JH7110 Image-Signal-Process clock driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/starfive/clk-starfive-jh7110.h b/drivers/clk/starfive/clk-starfive-jh7110.h index f29682b8d4003..0659adae4d76d 100644 --- a/drivers/clk/starfive/clk-starfive-jh7110.h +++ b/drivers/clk/starfive/clk-starfive-jh7110.h @@ -4,6 +4,12 @@ #include "clk-starfive-jh71x0.h" +/* top clocks of ISP/VOUT domain from JH7110 SYSCRG */ +struct jh7110_top_sysclk { + struct clk_bulk_data *top_clks; + int top_clks_num; +}; + int jh7110_reset_controller_register(struct jh71x0_clk_priv *priv, const char *adev_name, u32 adev_id); From 52f4ed09d156ef393df579845b5172ac5ba51f0c Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Thu, 13 Jul 2023 19:38:59 +0800 Subject: [PATCH 13/92] clk: starfive: Add StarFive JH7110 Video-Output clock driver Add driver for the StarFive JH7110 Video-Output clock controller. And these clock controllers should power on and enable the clocks from SYSCRG first before registering. Acked-by: Palmer Dabbelt Reviewed-by: Emil Renner Berthing Reviewed-by: Hal Feng Signed-off-by: Xingyu Wu Signed-off-by: Conor Dooley --- drivers/clk/starfive/Kconfig | 8 + drivers/clk/starfive/Makefile | 1 + .../clk/starfive/clk-starfive-jh7110-vout.c | 239 ++++++++++++++++++ 3 files changed, 248 insertions(+) create mode 100644 drivers/clk/starfive/clk-starfive-jh7110-vout.c diff --git a/drivers/clk/starfive/Kconfig b/drivers/clk/starfive/Kconfig index 68bdcedad1907..bd29358ffeec1 100644 --- a/drivers/clk/starfive/Kconfig +++ b/drivers/clk/starfive/Kconfig @@ -64,3 +64,11 @@ config CLK_STARFIVE_JH7110_ISP help Say yes here to support the Image-Signal-Process clock controller on the StarFive JH7110 SoC. + +config CLK_STARFIVE_JH7110_VOUT + tristate "StarFive JH7110 Video-Output clock support" + depends on CLK_STARFIVE_JH7110_SYS && JH71XX_PMU + default m if ARCH_STARFIVE + help + Say yes here to support the Video-Output clock controller + on the StarFive JH7110 SoC. diff --git a/drivers/clk/starfive/Makefile b/drivers/clk/starfive/Makefile index 3063bcab60476..199ac0f37a2f6 100644 --- a/drivers/clk/starfive/Makefile +++ b/drivers/clk/starfive/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_CLK_STARFIVE_JH7110_SYS) += clk-starfive-jh7110-sys.o obj-$(CONFIG_CLK_STARFIVE_JH7110_AON) += clk-starfive-jh7110-aon.o obj-$(CONFIG_CLK_STARFIVE_JH7110_STG) += clk-starfive-jh7110-stg.o obj-$(CONFIG_CLK_STARFIVE_JH7110_ISP) += clk-starfive-jh7110-isp.o +obj-$(CONFIG_CLK_STARFIVE_JH7110_VOUT) += clk-starfive-jh7110-vout.o diff --git a/drivers/clk/starfive/clk-starfive-jh7110-vout.c b/drivers/clk/starfive/clk-starfive-jh7110-vout.c new file mode 100644 index 0000000000000..10cc1ec439251 --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7110-vout.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7110 Video-Output Clock Driver + * + * Copyright (C) 2022-2023 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "clk-starfive-jh7110.h" + +/* external clocks */ +#define JH7110_VOUTCLK_VOUT_SRC (JH7110_VOUTCLK_END + 0) +#define JH7110_VOUTCLK_VOUT_TOP_AHB (JH7110_VOUTCLK_END + 1) +#define JH7110_VOUTCLK_VOUT_TOP_AXI (JH7110_VOUTCLK_END + 2) +#define JH7110_VOUTCLK_VOUT_TOP_HDMITX0_MCLK (JH7110_VOUTCLK_END + 3) +#define JH7110_VOUTCLK_I2STX0_BCLK (JH7110_VOUTCLK_END + 4) +#define JH7110_VOUTCLK_HDMITX0_PIXELCLK (JH7110_VOUTCLK_END + 5) +#define JH7110_VOUTCLK_EXT_END (JH7110_VOUTCLK_END + 6) + +static struct clk_bulk_data jh7110_vout_top_clks[] = { + { .id = "vout_src" }, + { .id = "vout_top_ahb" } +}; + +static const struct jh71x0_clk_data jh7110_voutclk_data[] = { + /* divider */ + JH71X0__DIV(JH7110_VOUTCLK_APB, "apb", 8, JH7110_VOUTCLK_VOUT_TOP_AHB), + JH71X0__DIV(JH7110_VOUTCLK_DC8200_PIX, "dc8200_pix", 63, JH7110_VOUTCLK_VOUT_SRC), + JH71X0__DIV(JH7110_VOUTCLK_DSI_SYS, "dsi_sys", 31, JH7110_VOUTCLK_VOUT_SRC), + JH71X0__DIV(JH7110_VOUTCLK_TX_ESC, "tx_esc", 31, JH7110_VOUTCLK_VOUT_TOP_AHB), + /* dc8200 */ + JH71X0_GATE(JH7110_VOUTCLK_DC8200_AXI, "dc8200_axi", 0, JH7110_VOUTCLK_VOUT_TOP_AXI), + JH71X0_GATE(JH7110_VOUTCLK_DC8200_CORE, "dc8200_core", 0, JH7110_VOUTCLK_VOUT_TOP_AXI), + JH71X0_GATE(JH7110_VOUTCLK_DC8200_AHB, "dc8200_ahb", 0, JH7110_VOUTCLK_VOUT_TOP_AHB), + JH71X0_GMUX(JH7110_VOUTCLK_DC8200_PIX0, "dc8200_pix0", 0, 2, + JH7110_VOUTCLK_DC8200_PIX, + JH7110_VOUTCLK_HDMITX0_PIXELCLK), + JH71X0_GMUX(JH7110_VOUTCLK_DC8200_PIX1, "dc8200_pix1", 0, 2, + JH7110_VOUTCLK_DC8200_PIX, + JH7110_VOUTCLK_HDMITX0_PIXELCLK), + /* LCD */ + JH71X0_GMUX(JH7110_VOUTCLK_DOM_VOUT_TOP_LCD, "dom_vout_top_lcd", 0, 2, + JH7110_VOUTCLK_DC8200_PIX0, + JH7110_VOUTCLK_DC8200_PIX1), + /* dsiTx */ + JH71X0_GATE(JH7110_VOUTCLK_DSITX_APB, "dsiTx_apb", 0, JH7110_VOUTCLK_DSI_SYS), + JH71X0_GATE(JH7110_VOUTCLK_DSITX_SYS, "dsiTx_sys", 0, JH7110_VOUTCLK_DSI_SYS), + JH71X0_GMUX(JH7110_VOUTCLK_DSITX_DPI, "dsiTx_dpi", 0, 2, + JH7110_VOUTCLK_DC8200_PIX, + JH7110_VOUTCLK_HDMITX0_PIXELCLK), + JH71X0_GATE(JH7110_VOUTCLK_DSITX_TXESC, "dsiTx_txesc", 0, JH7110_VOUTCLK_TX_ESC), + /* mipitx DPHY */ + JH71X0_GATE(JH7110_VOUTCLK_MIPITX_DPHY_TXESC, "mipitx_dphy_txesc", 0, + JH7110_VOUTCLK_TX_ESC), + /* hdmi */ + JH71X0_GATE(JH7110_VOUTCLK_HDMI_TX_MCLK, "hdmi_tx_mclk", 0, + JH7110_VOUTCLK_VOUT_TOP_HDMITX0_MCLK), + JH71X0_GATE(JH7110_VOUTCLK_HDMI_TX_BCLK, "hdmi_tx_bclk", 0, + JH7110_VOUTCLK_I2STX0_BCLK), + JH71X0_GATE(JH7110_VOUTCLK_HDMI_TX_SYS, "hdmi_tx_sys", 0, JH7110_VOUTCLK_APB), +}; + +static int jh7110_vout_top_rst_init(struct jh71x0_clk_priv *priv) +{ + struct reset_control *top_rst; + + /* The reset should be shared and other Vout modules will use its. */ + top_rst = devm_reset_control_get_shared(priv->dev, NULL); + if (IS_ERR(top_rst)) + return dev_err_probe(priv->dev, PTR_ERR(top_rst), "failed to get top reset\n"); + + return reset_control_deassert(top_rst); +} + +static struct clk_hw *jh7110_voutclk_get(struct of_phandle_args *clkspec, void *data) +{ + struct jh71x0_clk_priv *priv = data; + unsigned int idx = clkspec->args[0]; + + if (idx < JH7110_VOUTCLK_END) + return &priv->reg[idx].hw; + + return ERR_PTR(-EINVAL); +} + +#ifdef CONFIG_PM +static int jh7110_voutcrg_suspend(struct device *dev) +{ + struct jh7110_top_sysclk *top = dev_get_drvdata(dev); + + clk_bulk_disable_unprepare(top->top_clks_num, top->top_clks); + + return 0; +} + +static int jh7110_voutcrg_resume(struct device *dev) +{ + struct jh7110_top_sysclk *top = dev_get_drvdata(dev); + + return clk_bulk_prepare_enable(top->top_clks_num, top->top_clks); +} + +static const struct dev_pm_ops jh7110_voutcrg_pm_ops = { + RUNTIME_PM_OPS(jh7110_voutcrg_suspend, jh7110_voutcrg_resume, NULL) +}; +#endif + +static int jh7110_voutcrg_probe(struct platform_device *pdev) +{ + struct jh71x0_clk_priv *priv; + struct jh7110_top_sysclk *top; + unsigned int idx; + int ret; + + priv = devm_kzalloc(&pdev->dev, + struct_size(priv, reg, JH7110_VOUTCLK_END), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + top = devm_kzalloc(&pdev->dev, sizeof(*top), GFP_KERNEL); + if (!top) + return -ENOMEM; + + spin_lock_init(&priv->rmw_lock); + priv->dev = &pdev->dev; + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + top->top_clks = jh7110_vout_top_clks; + top->top_clks_num = ARRAY_SIZE(jh7110_vout_top_clks); + ret = devm_clk_bulk_get(priv->dev, top->top_clks_num, top->top_clks); + if (ret) + return dev_err_probe(priv->dev, ret, "failed to get top clocks\n"); + dev_set_drvdata(priv->dev, top); + + /* enable power domain and clocks */ + pm_runtime_enable(priv->dev); + ret = pm_runtime_get_sync(priv->dev); + if (ret < 0) + return dev_err_probe(priv->dev, ret, "failed to turn on power\n"); + + ret = jh7110_vout_top_rst_init(priv); + if (ret) + goto err_exit; + + for (idx = 0; idx < JH7110_VOUTCLK_END; idx++) { + u32 max = jh7110_voutclk_data[idx].max; + struct clk_parent_data parents[4] = {}; + struct clk_init_data init = { + .name = jh7110_voutclk_data[idx].name, + .ops = starfive_jh71x0_clk_ops(max), + .parent_data = parents, + .num_parents = + ((max & JH71X0_CLK_MUX_MASK) >> JH71X0_CLK_MUX_SHIFT) + 1, + .flags = jh7110_voutclk_data[idx].flags, + }; + struct jh71x0_clk *clk = &priv->reg[idx]; + unsigned int i; + const char *fw_name[JH7110_VOUTCLK_EXT_END - JH7110_VOUTCLK_END] = { + "vout_src", + "vout_top_ahb", + "vout_top_axi", + "vout_top_hdmitx0_mclk", + "i2stx0_bclk", + "hdmitx0_pixelclk" + }; + + for (i = 0; i < init.num_parents; i++) { + unsigned int pidx = jh7110_voutclk_data[idx].parents[i]; + + if (pidx < JH7110_VOUTCLK_END) + parents[i].hw = &priv->reg[pidx].hw; + else if (pidx < JH7110_VOUTCLK_EXT_END) + parents[i].fw_name = fw_name[pidx - JH7110_VOUTCLK_END]; + } + + clk->hw.init = &init; + clk->idx = idx; + clk->max_div = max & JH71X0_CLK_DIV_MASK; + + ret = devm_clk_hw_register(&pdev->dev, &clk->hw); + if (ret) + goto err_exit; + } + + ret = devm_of_clk_add_hw_provider(&pdev->dev, jh7110_voutclk_get, priv); + if (ret) + goto err_exit; + + ret = jh7110_reset_controller_register(priv, "rst-vo", 4); + if (ret) + goto err_exit; + + return 0; + +err_exit: + pm_runtime_put_sync(priv->dev); + pm_runtime_disable(priv->dev); + return ret; +} + +static int jh7110_voutcrg_remove(struct platform_device *pdev) +{ + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id jh7110_voutcrg_match[] = { + { .compatible = "starfive,jh7110-voutcrg" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jh7110_voutcrg_match); + +static struct platform_driver jh7110_voutcrg_driver = { + .probe = jh7110_voutcrg_probe, + .remove = jh7110_voutcrg_remove, + .driver = { + .name = "clk-starfive-jh7110-vout", + .of_match_table = jh7110_voutcrg_match, + .pm = pm_ptr(&jh7110_voutcrg_pm_ops), + }, +}; +module_platform_driver(jh7110_voutcrg_driver); + +MODULE_AUTHOR("Xingyu Wu "); +MODULE_DESCRIPTION("StarFive JH7110 Video-Output clock driver"); +MODULE_LICENSE("GPL"); From 108c23ba43117a052b21b01d6d9c5e49e972751f Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Mon, 24 Jul 2023 13:54:40 +0800 Subject: [PATCH 14/92] reset: starfive: jh7110: Add StarFive STG/ISP/VOUT resets support Add new struct members and auxiliary_device_id of resets to support System-Top-Group, Image-Signal-Process and Video-Output on the StarFive JH7110 SoC. Acked-by: Palmer Dabbelt Reviewed-by: Emil Renner Berthing Reviewed-by: Hal Feng Signed-off-by: Xingyu Wu Link: https://lore.kernel.org/r/20230724055440.100947-1-xingyu.wu@starfivetech.com Signed-off-by: Stephen Boyd --- .../reset/starfive/reset-starfive-jh7110.c | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/reset/starfive/reset-starfive-jh7110.c b/drivers/reset/starfive/reset-starfive-jh7110.c index 2d26ae95c8cc7..29a43f0f2ad6f 100644 --- a/drivers/reset/starfive/reset-starfive-jh7110.c +++ b/drivers/reset/starfive/reset-starfive-jh7110.c @@ -31,6 +31,24 @@ static const struct jh7110_reset_info jh7110_aon_info = { .status_offset = 0x3C, }; +static const struct jh7110_reset_info jh7110_stg_info = { + .nr_resets = JH7110_STGRST_END, + .assert_offset = 0x74, + .status_offset = 0x78, +}; + +static const struct jh7110_reset_info jh7110_isp_info = { + .nr_resets = JH7110_ISPRST_END, + .assert_offset = 0x38, + .status_offset = 0x3C, +}; + +static const struct jh7110_reset_info jh7110_vout_info = { + .nr_resets = JH7110_VOUTRST_END, + .assert_offset = 0x48, + .status_offset = 0x4C, +}; + static int jh7110_reset_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id) { @@ -58,6 +76,18 @@ static const struct auxiliary_device_id jh7110_reset_ids[] = { .name = "clk_starfive_jh7110_sys.rst-aon", .driver_data = (kernel_ulong_t)&jh7110_aon_info, }, + { + .name = "clk_starfive_jh7110_sys.rst-stg", + .driver_data = (kernel_ulong_t)&jh7110_stg_info, + }, + { + .name = "clk_starfive_jh7110_sys.rst-isp", + .driver_data = (kernel_ulong_t)&jh7110_isp_info, + }, + { + .name = "clk_starfive_jh7110_sys.rst-vo", + .driver_data = (kernel_ulong_t)&jh7110_vout_info, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(auxiliary, jh7110_reset_ids); From 88a95b7d1d7a94a27aa91537f656bb2ed12dff21 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Mon, 21 Aug 2023 23:29:15 +0800 Subject: [PATCH 15/92] clk: starfive: jh7110-sys: Fix lower rate of CPUfreq by setting PLL0 rate to 1.5GHz CPUfreq supports 4 cpu frequency loads on 375/500/750/1500MHz. But now PLL0 rate is 1GHz and the cpu frequency loads become 333/500/500/1000MHz in fact. So PLL0 rate should be set to 1.5GHz. Change the parent of cpu_root clock and the divider of cpu_core before the setting. Reviewed-by: Hal Feng Fixes: e2c510d6d630 ("riscv: dts: starfive: Add cpu scaling for JH7110 SoC") Signed-off-by: Xingyu Wu --- .../clk/starfive/clk-starfive-jh7110-sys.c | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/drivers/clk/starfive/clk-starfive-jh7110-sys.c b/drivers/clk/starfive/clk-starfive-jh7110-sys.c index 3884eff9fe931..b6b9e967dfc7e 100644 --- a/drivers/clk/starfive/clk-starfive-jh7110-sys.c +++ b/drivers/clk/starfive/clk-starfive-jh7110-sys.c @@ -501,7 +501,52 @@ static int __init jh7110_syscrg_probe(struct platform_device *pdev) if (ret) return ret; - return jh7110_reset_controller_register(priv, "rst-sys", 0); + ret = jh7110_reset_controller_register(priv, "rst-sys", 0); + if (ret) + return ret; + + /* + * Set PLL0 rate to 1.5GHz + * In order to not affect the cpu when the PLL0 rate is changing, + * we need to switch the parent of cpu_root clock to osc clock first, + * and then switch back after setting the PLL0 rate. + */ + pllclk = clk_get(priv->dev, "pll0_out"); + if (!IS_ERR(pllclk)) { + struct clk *osc = clk_get(&pdev->dev, "osc"); + struct clk *cpu_root = priv->reg[JH7110_SYSCLK_CPU_ROOT].hw.clk; + struct clk *cpu_core = priv->reg[JH7110_SYSCLK_CPU_CORE].hw.clk; + + if (IS_ERR(osc)) { + clk_put(pllclk); + return PTR_ERR(osc); + } + + /* + * CPU need voltage regulation by CPUfreq if set 1.5GHz. + * So in this driver, cpu_core need to be set the divider to be 2 first + * and will be 750M after setting parent. + */ + ret = clk_set_rate(cpu_core, clk_get_rate(cpu_core) / 2); + if (ret) + goto failed_set; + + ret = clk_set_parent(cpu_root, osc); + if (ret) + goto failed_set; + + ret = clk_set_rate(pllclk, 1500000000); + if (ret) + goto failed_set; + + ret = clk_set_parent(cpu_root, pllclk); + +failed_set: + clk_put(pllclk); + clk_put(osc); + } + + return ret; } static const struct of_device_id jh7110_syscrg_match[] = { From 1848389b496604dc35e8f6cf5e8a0f09fb098a63 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 6 Jul 2023 10:29:28 +0200 Subject: [PATCH 16/92] watchdog: starfive: Remove #ifdef guards for PM related functions Use the new PM macros for the suspend and resume functions to be automatically dropped by the compiler when CONFIG_PM or CONFIG_PM_SLEEP are disabled, without having to use #ifdef guards. This has the advantage of always compiling these functions in, independently of any Kconfig option. Thanks to that, bugs and other regressions are subsequently easier to catch. Signed-off-by: Paul Cercueil Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20230706082928.10869-1-paul@crapouillou.net Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/starfive-wdt.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/watchdog/starfive-wdt.c b/drivers/watchdog/starfive-wdt.c index 8058fca4d05d3..7c8a1c5e75be6 100644 --- a/drivers/watchdog/starfive-wdt.c +++ b/drivers/watchdog/starfive-wdt.c @@ -526,7 +526,6 @@ static void starfive_wdt_shutdown(struct platform_device *pdev) starfive_wdt_pm_stop(&wdt->wdd); } -#ifdef CONFIG_PM_SLEEP static int starfive_wdt_suspend(struct device *dev) { struct starfive_wdt *wdt = dev_get_drvdata(dev); @@ -556,9 +555,7 @@ static int starfive_wdt_resume(struct device *dev) return starfive_wdt_start(wdt); } -#endif /* CONFIG_PM_SLEEP */ -#ifdef CONFIG_PM static int starfive_wdt_runtime_suspend(struct device *dev) { struct starfive_wdt *wdt = dev_get_drvdata(dev); @@ -574,11 +571,10 @@ static int starfive_wdt_runtime_resume(struct device *dev) return starfive_wdt_enable_clock(wdt); } -#endif /* CONFIG_PM */ static const struct dev_pm_ops starfive_wdt_pm_ops = { - SET_RUNTIME_PM_OPS(starfive_wdt_runtime_suspend, starfive_wdt_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(starfive_wdt_suspend, starfive_wdt_resume) + RUNTIME_PM_OPS(starfive_wdt_runtime_suspend, starfive_wdt_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(starfive_wdt_suspend, starfive_wdt_resume) }; static const struct of_device_id starfive_wdt_match[] = { @@ -594,7 +590,7 @@ static struct platform_driver starfive_wdt_driver = { .shutdown = starfive_wdt_shutdown, .driver = { .name = "starfive-wdt", - .pm = &starfive_wdt_pm_ops, + .pm = pm_ptr(&starfive_wdt_pm_ops), .of_match_table = starfive_wdt_match, }, }; From 3db9a11106d7fffd1d2561b1c1d9acedee0afc56 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Mon, 14 Aug 2023 18:16:01 +0800 Subject: [PATCH 17/92] dt-bindings: timer: Add timer for StarFive JH7110 SoC Add bindings for the timer on the JH7110 RISC-V SoC by StarFive Technology Ltd. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Xingyu Wu --- .../bindings/timer/starfive,jh7110-timer.yaml | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml diff --git a/Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml b/Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml new file mode 100644 index 0000000000000..9a2dac11eb06d --- /dev/null +++ b/Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/timer/starfive,jh7110-timer.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 Timer + +maintainers: + - Xingyu Wu + - Samin Guo + +description: + This timer has four free-running 32 bit counters in StarFive JH7110 SoC. + And each channel(counter) triggers an interrupt when timeout. They support + one-shot mode and continuous-run mode. + +properties: + compatible: + const: starfive,jh7110-timer + + reg: + maxItems: 1 + + interrupts: + items: + - description: channel 0 + - description: channel 1 + - description: channel 2 + - description: channel 3 + + clocks: + items: + - description: timer APB + - description: channel 0 + - description: channel 1 + - description: channel 2 + - description: channel 3 + + clock-names: + items: + - const: apb + - const: ch0 + - const: ch1 + - const: ch2 + - const: ch3 + + resets: + items: + - description: timer APB + - description: channel 0 + - description: channel 1 + - description: channel 2 + - description: channel 3 + + reset-names: + items: + - const: apb + - const: ch0 + - const: ch1 + - const: ch2 + - const: ch3 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - resets + - reset-names + +additionalProperties: false + +examples: + - | + timer@13050000 { + compatible = "starfive,jh7110-timer"; + reg = <0x13050000 0x10000>; + interrupts = <69>, <70>, <71> ,<72>; + clocks = <&clk 124>, + <&clk 125>, + <&clk 126>, + <&clk 127>, + <&clk 128>; + clock-names = "apb", "ch0", "ch1", + "ch2", "ch3"; + resets = <&rst 117>, + <&rst 118>, + <&rst 119>, + <&rst 120>, + <&rst 121>; + reset-names = "apb", "ch0", "ch1", + "ch2", "ch3"; + }; + From 895975dc73d371fd9a7b83c7cb8fa4bf69cc9d20 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Mon, 14 Aug 2023 18:16:02 +0800 Subject: [PATCH 18/92] clocksource: Add JH7110 timer driver Add timer driver for the StarFive JH7110 SoC. Signed-off-by: Xingyu Wu --- drivers/clocksource/Kconfig | 11 + drivers/clocksource/Makefile | 1 + drivers/clocksource/timer-jh7110.c | 415 +++++++++++++++++++++++++++++ 3 files changed, 427 insertions(+) create mode 100644 drivers/clocksource/timer-jh7110.c diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index c4d671a5a13d9..171b9b54a4996 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -648,6 +648,17 @@ config RISCV_TIMER is accessed via both the SBI and the rdcycle instruction. This is required for all RISC-V systems. +config STARFIVE_JH7110_TIMER + bool "Timer for the STARFIVE JH7110 SoC" + depends on ARCH_STARFIVE || COMPILE_TEST + select TIMER_OF + select CLKSRC_MMIO + default ARCH_STARFIVE + help + This enables the timer for StarFive JH7110 SoCs. On RISC-V platform, + the system has started RISCV_TIMER. But you can also use this timer + which can provides four channels to do a lot more on JH7110 SoC. + config CLINT_TIMER bool "CLINT Timer for the RISC-V platform" if COMPILE_TEST depends on GENERIC_SCHED_CLOCK && RISCV diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 5d93c9e3fc558..56bd6d9ddd87a 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -81,6 +81,7 @@ obj-$(CONFIG_INGENIC_TIMER) += ingenic-timer.o obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o obj-$(CONFIG_X86_NUMACHIP) += numachip.o obj-$(CONFIG_RISCV_TIMER) += timer-riscv.o +obj-$(CONFIG_STARFIVE_JH7110_TIMER) += timer-jh7110.o obj-$(CONFIG_CLINT_TIMER) += timer-clint.o obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o obj-$(CONFIG_GX6605S_TIMER) += timer-gx6605s.o diff --git a/drivers/clocksource/timer-jh7110.c b/drivers/clocksource/timer-jh7110.c new file mode 100644 index 0000000000000..fe3cfed55df7d --- /dev/null +++ b/drivers/clocksource/timer-jh7110.c @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Starfive JH7110 Timer driver + * + * Copyright (C) 2022-2023 StarFive Technology Co., Ltd. + * + * Author: + * Xingyu Wu + * Samin Guo + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Bias: Ch0-0x0, Ch1-0x40, Ch2-0x80, and so on. */ +#define JH7110_TIMER_CH_LEN 0x40 +#define JH7110_TIMER_CH_BASE(x) ((x) * JH7110_TIMER_CH_LEN) +#define JH7110_TIMER_CH_MAX 4 + +#define JH7110_CLOCK_SOURCE_RATING 200 +#define JH7110_VALID_BITS 32 +#define JH7110_DELAY_US 0 +#define JH7110_TIMEOUT_US 10000 +#define JH7110_CLOCKEVENT_RATING 300 +#define JH7110_TIMER_MAX_TICKS 0xffffffff +#define JH7110_TIMER_MIN_TICKS 0xf +#define JH7110_TIMER_NAME_NUM 20 + +#define JH7110_TIMER_INT_STATUS 0x00 /* RO[0:4]: Interrupt Status for channel0~4 */ +#define JH7110_TIMER_CTL 0x04 /* RW[0]: 0-continuous run, 1-single run */ +#define JH7110_TIMER_LOAD 0x08 /* RW: load value to counter */ +#define JH7110_TIMER_ENABLE 0x10 /* RW[0]: timer enable register */ +#define JH7110_TIMER_RELOAD 0x14 /* RW: write 1 or 0 both reload counter */ +#define JH7110_TIMER_VALUE 0x18 /* RO: timer value register */ +#define JH7110_TIMER_INT_CLR 0x20 /* RW: timer interrupt clear register */ +#define JH7110_TIMER_INT_MASK 0x24 /* RW[0]: timer interrupt mask register */ +#define JH7110_TIMER_INT_CLR_AVA_MASK BIT(1) + +enum JH7110_TIMER_INTMASK { + JH7110_TIMER_INTMASK_DIS = 0, + JH7110_TIMER_INTMASK_ENA = 1 +}; + +enum JH7110_TIMER_MOD { + JH7110_TIMER_MOD_CONTIN = 0, + JH7110_TIMER_MOD_SINGLE = 1 +}; + +enum JH7110_TIMER_CTL_EN { + JH7110_TIMER_DIS = 0, + JH7110_TIMER_ENA = 1 +}; + +struct jh7110_clkevt { + struct clock_event_device evt; + struct clocksource cs; + struct clk *clk; + char name[JH7110_TIMER_NAME_NUM]; + int irq; + u32 periodic; + u32 rate; + u32 reload_val; + void __iomem *base; +}; + +struct jh7110_timer_priv { + struct device *dev; + void __iomem *base; + struct jh7110_clkevt clkevt[JH7110_TIMER_CH_MAX]; +}; + +static inline struct jh7110_clkevt *to_jh7110_clkevt(struct clock_event_device *evt) +{ + return container_of(evt, struct jh7110_clkevt, evt); +} + +/* 0:continuous-run mode, 1:single-run mode */ +static inline void jh7110_timer_set_continuous_mod(struct jh7110_clkevt *clkevt) +{ + writel(JH7110_TIMER_MOD_CONTIN, clkevt->base + JH7110_TIMER_CTL); +} + +static inline void jh7110_timer_set_single_mod(struct jh7110_clkevt *clkevt) +{ + writel(JH7110_TIMER_MOD_SINGLE, clkevt->base + JH7110_TIMER_CTL); +} + +/* Interrupt Mask Register, 0:Unmask, 1:Mask */ +static inline void jh7110_timer_int_enable(struct jh7110_clkevt *clkevt) +{ + writel(JH7110_TIMER_INTMASK_DIS, clkevt->base + JH7110_TIMER_INT_MASK); +} + +static inline void jh7110_timer_int_disable(struct jh7110_clkevt *clkevt) +{ + writel(JH7110_TIMER_INTMASK_ENA, clkevt->base + JH7110_TIMER_INT_MASK); +} + +/* + * BIT(0): Read value represent channel intr status. + * Write 1 to this bit to clear interrupt. Write 0 has no effects. + * BIT(1): "1" means that it is clearing interrupt. BIT(0) can not be written. + */ +static inline int jh7110_timer_int_clear(struct jh7110_clkevt *clkevt) +{ + u32 value; + int ret; + + /* waiting interrupt can be to clearing */ + ret = readl_poll_timeout_atomic(clkevt->base + JH7110_TIMER_INT_CLR, value, + !(value & JH7110_TIMER_INT_CLR_AVA_MASK), + JH7110_DELAY_US, JH7110_TIMEOUT_US); + if (!ret) + writel(0x1, clkevt->base + JH7110_TIMER_INT_CLR); + + return ret; +} + +/* + * The initial value to be loaded into the + * counter and is also used as the reload value. + * val = clock rate --> 1s + */ +static inline void jh7110_timer_set_load(struct jh7110_clkevt *clkevt, u32 val) +{ + writel(val, clkevt->base + JH7110_TIMER_LOAD); +} + +static inline u32 jh7110_timer_get_val(struct jh7110_clkevt *clkevt) +{ + return readl(clkevt->base + JH7110_TIMER_VALUE); +} + +/* + * Write RELOAD register to reload preset value to counter. + * Write 0 and write 1 are both ok. + */ +static inline void jh7110_timer_set_reload(struct jh7110_clkevt *clkevt) +{ + writel(0, clkevt->base + JH7110_TIMER_RELOAD); +} + +static inline void jh7110_timer_enable(struct jh7110_clkevt *clkevt) +{ + writel(JH7110_TIMER_ENA, clkevt->base + JH7110_TIMER_ENABLE); +} + +static inline void jh7110_timer_disable(struct jh7110_clkevt *clkevt) +{ + writel(JH7110_TIMER_DIS, clkevt->base + JH7110_TIMER_ENABLE); +} + +static int jh7110_timer_int_init_enable(struct jh7110_clkevt *clkevt) +{ + int ret; + + jh7110_timer_int_disable(clkevt); + ret = jh7110_timer_int_clear(clkevt); + if (ret) + return ret; + + jh7110_timer_int_enable(clkevt); + jh7110_timer_enable(clkevt); + + return 0; +} + +static int jh7110_timer_shutdown(struct clock_event_device *evt) +{ + struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt); + + jh7110_timer_disable(clkevt); + return jh7110_timer_int_clear(clkevt); +} + +static void jh7110_timer_suspend(struct clock_event_device *evt) +{ + struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt); + + clkevt->reload_val = jh7110_timer_get_val(clkevt); + jh7110_timer_shutdown(evt); +} + +static void jh7110_timer_resume(struct clock_event_device *evt) +{ + struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt); + + jh7110_timer_set_load(clkevt, clkevt->reload_val); + jh7110_timer_set_reload(clkevt); + jh7110_timer_int_enable(clkevt); + jh7110_timer_enable(clkevt); +} + +static int jh7110_timer_tick_resume(struct clock_event_device *evt) +{ + jh7110_timer_resume(evt); + + return 0; +} + +/* IRQ handler for the timer */ +static irqreturn_t jh7110_timer_interrupt(int irq, void *priv) +{ + struct clock_event_device *evt = (struct clock_event_device *)priv; + struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt); + + if (jh7110_timer_int_clear(clkevt)) + return IRQ_NONE; + + if (evt->event_handler) + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static int jh7110_timer_set_periodic(struct clock_event_device *evt) +{ + struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt); + + clkevt->periodic = DIV_ROUND_CLOSEST(clkevt->rate, HZ); + jh7110_timer_disable(clkevt); + jh7110_timer_set_continuous_mod(clkevt); + jh7110_timer_set_load(clkevt, clkevt->periodic); + + return jh7110_timer_int_init_enable(clkevt); +} + +static int jh7110_timer_set_oneshot(struct clock_event_device *evt) +{ + struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt); + + jh7110_timer_disable(clkevt); + jh7110_timer_set_single_mod(clkevt); + jh7110_timer_set_load(clkevt, JH7110_TIMER_MAX_TICKS); + + return jh7110_timer_int_init_enable(clkevt); +} + +static int jh7110_timer_set_next_event(unsigned long next, + struct clock_event_device *evt) +{ + struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt); + + jh7110_timer_disable(clkevt); + jh7110_timer_set_single_mod(clkevt); + jh7110_timer_set_load(clkevt, next); + jh7110_timer_enable(clkevt); + + return 0; +} + +static void jh7110_set_clockevent(struct clock_event_device *evt) +{ + evt->features = CLOCK_EVT_FEAT_PERIODIC | + CLOCK_EVT_FEAT_ONESHOT | + CLOCK_EVT_FEAT_DYNIRQ; + evt->set_state_shutdown = jh7110_timer_shutdown; + evt->set_state_periodic = jh7110_timer_set_periodic; + evt->set_state_oneshot = jh7110_timer_set_oneshot; + evt->set_state_oneshot_stopped = jh7110_timer_shutdown; + evt->tick_resume = jh7110_timer_tick_resume; + evt->set_next_event = jh7110_timer_set_next_event; + evt->suspend = jh7110_timer_suspend; + evt->resume = jh7110_timer_resume; + evt->rating = JH7110_CLOCKEVENT_RATING; +} + +static u64 jh7110_timer_clocksource_read(struct clocksource *cs) +{ + struct jh7110_clkevt *clkevt = container_of(cs, struct jh7110_clkevt, cs); + + return (u64)jh7110_timer_get_val(clkevt); +} + +static int jh7110_clocksource_init(struct jh7110_clkevt *clkevt) +{ + int ret; + + jh7110_timer_set_continuous_mod(clkevt); + jh7110_timer_set_load(clkevt, JH7110_TIMER_MAX_TICKS); + + ret = jh7110_timer_int_init_enable(clkevt); + if (ret) + return ret; + + clkevt->cs.name = clkevt->name; + clkevt->cs.rating = JH7110_CLOCK_SOURCE_RATING; + clkevt->cs.read = jh7110_timer_clocksource_read; + clkevt->cs.mask = CLOCKSOURCE_MASK(JH7110_VALID_BITS); + clkevt->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; + + return clocksource_register_hz(&clkevt->cs, clkevt->rate); +} + +static void jh7110_clockevents_register(struct jh7110_clkevt *clkevt) +{ + clkevt->rate = clk_get_rate(clkevt->clk); + + jh7110_set_clockevent(&clkevt->evt); + clkevt->evt.name = clkevt->name; + clkevt->evt.irq = clkevt->irq; + clkevt->evt.cpumask = cpu_possible_mask; + + clockevents_config_and_register(&clkevt->evt, clkevt->rate, + JH7110_TIMER_MIN_TICKS, JH7110_TIMER_MAX_TICKS); +} + +static int jh7110_timer_probe(struct platform_device *pdev) +{ + char name[JH7110_TIMER_NAME_NUM]; + struct jh7110_timer_priv *priv; + struct jh7110_clkevt *clkevt; + struct clk *pclk; + struct reset_control *rst; + int ch; + int ret; + + priv = devm_kzalloc(&pdev->dev, struct_size(priv, clkevt, JH7110_TIMER_CH_MAX), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->base), + "failed to map registers\n"); + + rst = devm_reset_control_get_exclusive(&pdev->dev, "apb"); + if (IS_ERR(rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(rst), "failed to get apb reset\n"); + + pclk = devm_clk_get_enabled(&pdev->dev, "apb"); + if (IS_ERR(pclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(pclk), + "failed to get & enable apb clock\n"); + + ret = reset_control_deassert(rst); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to deassert apb reset\n"); + + priv->dev = &pdev->dev; + platform_set_drvdata(pdev, priv); + + for (ch = 0; ch < JH7110_TIMER_CH_MAX; ch++) { + clkevt = &priv->clkevt[ch]; + snprintf(name, sizeof(name), "ch%d", ch); + + clkevt->base = priv->base + JH7110_TIMER_CH_BASE(ch); + /* Ensure timers are disabled */ + jh7110_timer_disable(clkevt); + + rst = devm_reset_control_get_exclusive(&pdev->dev, name); + if (IS_ERR(rst)) + return PTR_ERR(rst); + + clkevt->clk = devm_clk_get_enabled(&pdev->dev, name); + if (IS_ERR(clkevt->clk)) + return PTR_ERR(clkevt->clk); + + ret = reset_control_deassert(rst); + if (ret) + return ret; + + clkevt->irq = platform_get_irq(pdev, ch); + if (clkevt->irq < 0) + return clkevt->irq; + + snprintf(clkevt->name, sizeof(clkevt->name), "%s.ch%d", pdev->name, ch); + jh7110_clockevents_register(clkevt); + + ret = devm_request_irq(&pdev->dev, clkevt->irq, jh7110_timer_interrupt, + IRQF_TIMER | IRQF_IRQPOLL, + clkevt->name, &clkevt->evt); + if (ret) + return ret; + + ret = jh7110_clocksource_init(clkevt); + if (ret) + return ret; + } + + return 0; +} + +static const struct of_device_id jh7110_timer_match[] = { + { .compatible = "starfive,jh7110-timer", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jh7110_timer_match); + +static struct platform_driver jh7110_timer_driver = { + .probe = jh7110_timer_probe, + .driver = { + .name = "jh7110-timer", + .of_match_table = jh7110_timer_match, + }, +}; +module_platform_driver(jh7110_timer_driver); + +MODULE_AUTHOR("Xingyu Wu "); +MODULE_DESCRIPTION("StarFive JH7110 timer driver"); +MODULE_LICENSE("GPL"); From 4c1d138bf62d68cc2d108761a2299e526779a6e5 Mon Sep 17 00:00:00 2001 From: Samin Guo Date: Thu, 20 Jul 2023 19:15:08 +0800 Subject: [PATCH 19/92] dt-bindings: net: motorcomm: Add pad driver strength cfg The motorcomm phy (YT8531) supports the ability to adjust the drive strength of the rx_clk/rx_data. The YT8531 RGMII LDO voltage supports 1.8V/3.3V, and the LDO voltage can be configured with hardware pull-up resistors to match the SOC voltage (usually 1.8V). The software can read the registers 0xA001 obtain the current LDO voltage value. Reviewed-by: Hal Feng Signed-off-by: Samin Guo Reviewed-by: Andrew Lunn Reviewed-by: Conor Dooley Signed-off-by: David S. Miller --- .../bindings/net/motorcomm,yt8xxx.yaml | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Documentation/devicetree/bindings/net/motorcomm,yt8xxx.yaml b/Documentation/devicetree/bindings/net/motorcomm,yt8xxx.yaml index 157e3bbcaf6f7..26688e2302eaa 100644 --- a/Documentation/devicetree/bindings/net/motorcomm,yt8xxx.yaml +++ b/Documentation/devicetree/bindings/net/motorcomm,yt8xxx.yaml @@ -52,6 +52,40 @@ properties: for a timer. type: boolean + motorcomm,rx-clk-drv-microamp: + description: | + drive strength of rx_clk rgmii pad. + The YT8531 RGMII LDO voltage supports 1.8V/3.3V, and the LDO voltage can + be configured with hardware pull-up resistors to match the SOC voltage + (usually 1.8V). + The software can read the registers to obtain the LDO voltage and configure + the legal drive strength(curren). + ===================================================== + | voltage | current Available (uA) | + | 1.8v | 1200 2100 2700 2910 3110 3600 3970 4350 | + | 3.3v | 3070 4080 4370 4680 5020 5450 5740 6140 | + ===================================================== + enum: [ 1200, 2100, 2700, 2910, 3070, 3110, 3600, 3970, + 4080, 4350, 4370, 4680, 5020, 5450, 5740, 6140 ] + default: 2910 + + motorcomm,rx-data-drv-microamp: + description: | + drive strength of rx_data/rx_ctl rgmii pad. + The YT8531 RGMII LDO voltage supports 1.8V/3.3V, and the LDO voltage can + be configured with hardware pull-up resistors to match the SOC voltage + (usually 1.8V). + The software can read the registers to obtain the LDO voltage and configure + the legal drive strength(curren). + ===================================================== + | voltage | current Available (uA) | + | 1.8v | 1200 2100 2700 2910 3110 3600 3970 4350 | + | 3.3v | 3070 4080 4370 4680 5020 5450 5740 6140 | + ===================================================== + enum: [ 1200, 2100, 2700, 2910, 3070, 3110, 3600, 3970, + 4080, 4350, 4370, 4680, 5020, 5450, 5740, 6140 ] + default: 2910 + motorcomm,tx-clk-adj-enabled: description: | This configuration is mainly to adapt to VF2 with JH7110 SoC. From 17db06ce8e9d85145a6f0ba86f2651a5806e26aa Mon Sep 17 00:00:00 2001 From: Samin Guo Date: Thu, 20 Jul 2023 19:15:09 +0800 Subject: [PATCH 20/92] net: phy: motorcomm: Add pad drive strength cfg support The motorcomm phy (YT8531) supports the ability to adjust the drive strength of the rx_clk/rx_data, and the default strength may not be suitable for all boards. So add configurable options to better match the boards.(e.g. StarFive VisionFive 2) When we configure the drive strength, we need to read the current LDO voltage value to ensure that it is a legal value at that LDO voltage. Reviewed-by: Hal Feng Signed-off-by: Samin Guo Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/motorcomm.c | 118 ++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c index 2fa5a90e073bd..7a11fdb687cc4 100644 --- a/drivers/net/phy/motorcomm.c +++ b/drivers/net/phy/motorcomm.c @@ -163,6 +163,10 @@ #define YT8521_CHIP_CONFIG_REG 0xA001 #define YT8521_CCR_SW_RST BIT(15) +#define YT8531_RGMII_LDO_VOL_MASK GENMASK(5, 4) +#define YT8531_LDO_VOL_3V3 0x0 +#define YT8531_LDO_VOL_1V8 0x2 + /* 1b0 disable 1.9ns rxc clock delay *default* * 1b1 enable 1.9ns rxc clock delay */ @@ -236,6 +240,12 @@ */ #define YTPHY_WCR_TYPE_PULSE BIT(0) +#define YTPHY_PAD_DRIVE_STRENGTH_REG 0xA010 +#define YT8531_RGMII_RXC_DS_MASK GENMASK(15, 13) +#define YT8531_RGMII_RXD_DS_HI_MASK BIT(12) /* Bit 2 of rxd_ds */ +#define YT8531_RGMII_RXD_DS_LOW_MASK GENMASK(5, 4) /* Bit 1/0 of rxd_ds */ +#define YT8531_RGMII_RX_DS_DEFAULT 0x3 + #define YTPHY_SYNCE_CFG_REG 0xA012 #define YT8521_SCR_SYNCE_ENABLE BIT(5) /* 1b0 output 25m clock @@ -834,6 +844,110 @@ static int ytphy_rgmii_clk_delay_config_with_lock(struct phy_device *phydev) return ret; } +/** + * struct ytphy_ldo_vol_map - map a current value to a register value + * @vol: ldo voltage + * @ds: value in the register + * @cur: value in device configuration + */ +struct ytphy_ldo_vol_map { + u32 vol; + u32 ds; + u32 cur; +}; + +static const struct ytphy_ldo_vol_map yt8531_ldo_vol[] = { + {.vol = YT8531_LDO_VOL_1V8, .ds = 0, .cur = 1200}, + {.vol = YT8531_LDO_VOL_1V8, .ds = 1, .cur = 2100}, + {.vol = YT8531_LDO_VOL_1V8, .ds = 2, .cur = 2700}, + {.vol = YT8531_LDO_VOL_1V8, .ds = 3, .cur = 2910}, + {.vol = YT8531_LDO_VOL_1V8, .ds = 4, .cur = 3110}, + {.vol = YT8531_LDO_VOL_1V8, .ds = 5, .cur = 3600}, + {.vol = YT8531_LDO_VOL_1V8, .ds = 6, .cur = 3970}, + {.vol = YT8531_LDO_VOL_1V8, .ds = 7, .cur = 4350}, + {.vol = YT8531_LDO_VOL_3V3, .ds = 0, .cur = 3070}, + {.vol = YT8531_LDO_VOL_3V3, .ds = 1, .cur = 4080}, + {.vol = YT8531_LDO_VOL_3V3, .ds = 2, .cur = 4370}, + {.vol = YT8531_LDO_VOL_3V3, .ds = 3, .cur = 4680}, + {.vol = YT8531_LDO_VOL_3V3, .ds = 4, .cur = 5020}, + {.vol = YT8531_LDO_VOL_3V3, .ds = 5, .cur = 5450}, + {.vol = YT8531_LDO_VOL_3V3, .ds = 6, .cur = 5740}, + {.vol = YT8531_LDO_VOL_3V3, .ds = 7, .cur = 6140}, +}; + +static u32 yt8531_get_ldo_vol(struct phy_device *phydev) +{ + u32 val; + + val = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG); + val = FIELD_GET(YT8531_RGMII_LDO_VOL_MASK, val); + + return val <= YT8531_LDO_VOL_1V8 ? val : YT8531_LDO_VOL_1V8; +} + +static int yt8531_get_ds_map(struct phy_device *phydev, u32 cur) +{ + u32 vol; + int i; + + vol = yt8531_get_ldo_vol(phydev); + for (i = 0; i < ARRAY_SIZE(yt8531_ldo_vol); i++) { + if (yt8531_ldo_vol[i].vol == vol && yt8531_ldo_vol[i].cur == cur) + return yt8531_ldo_vol[i].ds; + } + + return -EINVAL; +} + +static int yt8531_set_ds(struct phy_device *phydev) +{ + struct device_node *node = phydev->mdio.dev.of_node; + u32 ds_field_low, ds_field_hi, val; + int ret, ds; + + /* set rgmii rx clk driver strength */ + if (!of_property_read_u32(node, "motorcomm,rx-clk-drv-microamp", &val)) { + ds = yt8531_get_ds_map(phydev, val); + if (ds < 0) + return dev_err_probe(&phydev->mdio.dev, ds, + "No matching current value was found.\n"); + } else { + ds = YT8531_RGMII_RX_DS_DEFAULT; + } + + ret = ytphy_modify_ext_with_lock(phydev, + YTPHY_PAD_DRIVE_STRENGTH_REG, + YT8531_RGMII_RXC_DS_MASK, + FIELD_PREP(YT8531_RGMII_RXC_DS_MASK, ds)); + if (ret < 0) + return ret; + + /* set rgmii rx data driver strength */ + if (!of_property_read_u32(node, "motorcomm,rx-data-drv-microamp", &val)) { + ds = yt8531_get_ds_map(phydev, val); + if (ds < 0) + return dev_err_probe(&phydev->mdio.dev, ds, + "No matching current value was found.\n"); + } else { + ds = YT8531_RGMII_RX_DS_DEFAULT; + } + + ds_field_hi = FIELD_GET(BIT(2), ds); + ds_field_hi = FIELD_PREP(YT8531_RGMII_RXD_DS_HI_MASK, ds_field_hi); + + ds_field_low = FIELD_GET(GENMASK(1, 0), ds); + ds_field_low = FIELD_PREP(YT8531_RGMII_RXD_DS_LOW_MASK, ds_field_low); + + ret = ytphy_modify_ext_with_lock(phydev, + YTPHY_PAD_DRIVE_STRENGTH_REG, + YT8531_RGMII_RXD_DS_LOW_MASK | YT8531_RGMII_RXD_DS_HI_MASK, + ds_field_low | ds_field_hi); + if (ret < 0) + return ret; + + return 0; +} + /** * yt8521_probe() - read chip config then set suitable polling_mode * @phydev: a pointer to a &struct phy_device @@ -1518,6 +1632,10 @@ static int yt8531_config_init(struct phy_device *phydev) return ret; } + ret = yt8531_set_ds(phydev); + if (ret < 0) + return ret; + return 0; } From 77fbacfb91dcc02f75d52df6d235ee2a5723879b Mon Sep 17 00:00:00 2001 From: William Qiu Date: Fri, 4 Aug 2023 10:02:52 +0800 Subject: [PATCH 21/92] dt-bindings: qspi: cdns,qspi-nor: Add clocks for StarFive JH7110 SoC The QSPI controller needs three clock items to work properly on StarFive JH7110 SoC, so there is need to change the maxItems's value to 3. Signed-off-by: William Qiu Reviewed-by: Hal Feng Reviewed-by: Conor Dooley Link: https://lore.kernel.org/r/20230804020254.291239-2-william.qiu@starfivetech.com Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/cdns,qspi-nor.yaml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/spi/cdns,qspi-nor.yaml b/Documentation/devicetree/bindings/spi/cdns,qspi-nor.yaml index 4f15f9a0cc34a..cca81f89e252d 100644 --- a/Documentation/devicetree/bindings/spi/cdns,qspi-nor.yaml +++ b/Documentation/devicetree/bindings/spi/cdns,qspi-nor.yaml @@ -86,7 +86,17 @@ properties: maxItems: 1 clocks: - maxItems: 1 + minItems: 1 + maxItems: 3 + + clock-names: + oneOf: + - items: + - const: ref + - items: + - const: ref + - const: ahb + - const: apb cdns,fifo-depth: description: From 46e228b66d4523fa20272d067ac2e1b243df626f Mon Sep 17 00:00:00 2001 From: William Qiu Date: Fri, 4 Aug 2023 10:02:53 +0800 Subject: [PATCH 22/92] spi: cadence-quadspi: Add clock configuration for StarFive JH7110 QSPI Add JH7110's clock initialization code to the driver. Signed-off-by: William Qiu Reviewed-by: Hal Feng Link: https://lore.kernel.org/r/20230804020254.291239-3-william.qiu@starfivetech.com Signed-off-by: Mark Brown --- drivers/spi/spi-cadence-quadspi.c | 67 +++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index abf10f92415dc..16985e1fe6109 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -47,6 +47,12 @@ #define CQSPI_OP_WIDTH(part) ((part).nbytes ? ilog2((part).buswidth) : 0) +enum { + CLK_QSPI_APB = 0, + CLK_QSPI_AHB, + CLK_QSPI_NUM, +}; + struct cqspi_st; struct cqspi_flash_pdata { @@ -64,6 +70,7 @@ struct cqspi_st { struct platform_device *pdev; struct spi_master *master; struct clk *clk; + struct clk *clks[CLK_QSPI_NUM]; unsigned int sclk; void __iomem *iobase; @@ -92,6 +99,8 @@ struct cqspi_st { bool wr_completion; bool slow_sram; bool apb_ahb_hazard; + + bool is_jh7110; /* Flag for StarFive JH7110 SoC */ }; struct cqspi_driver_platdata { @@ -100,6 +109,8 @@ struct cqspi_driver_platdata { int (*indirect_read_dma)(struct cqspi_flash_pdata *f_pdata, u_char *rxbuf, loff_t from_addr, size_t n_rx); u32 (*get_dma_status)(struct cqspi_st *cqspi); + int (*jh7110_clk_init)(struct platform_device *pdev, + struct cqspi_st *cqspi); }; /* Operation timeout value */ @@ -1630,6 +1641,51 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi) return 0; } +static int cqspi_jh7110_clk_init(struct platform_device *pdev, struct cqspi_st *cqspi) +{ + static struct clk_bulk_data qspiclk[] = { + { .id = "apb" }, + { .id = "ahb" }, + }; + + int ret = 0; + + ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(qspiclk), qspiclk); + if (ret) { + dev_err(&pdev->dev, "%s: failed to get qspi clocks\n", __func__); + return ret; + } + + cqspi->clks[CLK_QSPI_APB] = qspiclk[0].clk; + cqspi->clks[CLK_QSPI_AHB] = qspiclk[1].clk; + + ret = clk_prepare_enable(cqspi->clks[CLK_QSPI_APB]); + if (ret) { + dev_err(&pdev->dev, "%s: failed to enable CLK_QSPI_APB\n", __func__); + return ret; + } + + ret = clk_prepare_enable(cqspi->clks[CLK_QSPI_AHB]); + if (ret) { + dev_err(&pdev->dev, "%s: failed to enable CLK_QSPI_AHB\n", __func__); + goto disable_apb_clk; + } + + cqspi->is_jh7110 = true; + + return 0; + +disable_apb_clk: + clk_disable_unprepare(cqspi->clks[CLK_QSPI_APB]); + + return ret; +} + +static void cqspi_jh7110_disable_clk(struct platform_device *pdev, struct cqspi_st *cqspi) +{ + clk_disable_unprepare(cqspi->clks[CLK_QSPI_AHB]); + clk_disable_unprepare(cqspi->clks[CLK_QSPI_APB]); +} static int cqspi_probe(struct platform_device *pdev) { const struct cqspi_driver_platdata *ddata; @@ -1655,6 +1711,7 @@ static int cqspi_probe(struct platform_device *pdev) cqspi->pdev = pdev; cqspi->master = master; + cqspi->is_jh7110 = false; platform_set_drvdata(pdev, cqspi); /* Obtain configuration from OF. */ @@ -1766,6 +1823,12 @@ static int cqspi_probe(struct platform_device *pdev) if (ddata->quirks & CQSPI_NEEDS_APB_AHB_HAZARD_WAR) cqspi->apb_ahb_hazard = true; + if (ddata->jh7110_clk_init) { + ret = cqspi_jh7110_clk_init(pdev, cqspi); + if (ret) + goto probe_clk_failed; + } + if (of_device_is_compatible(pdev->dev.of_node, "xlnx,versal-ospi-1.0")) { ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); @@ -1830,6 +1893,9 @@ static void cqspi_remove(struct platform_device *pdev) clk_disable_unprepare(cqspi->clk); + if (cqspi->is_jh7110) + cqspi_jh7110_disable_clk(pdev, cqspi); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); } @@ -1897,6 +1963,7 @@ static const struct cqspi_driver_platdata versal_ospi = { static const struct cqspi_driver_platdata jh7110_qspi = { .quirks = CQSPI_DISABLE_DAC_MODE, + .jh7110_clk_init = cqspi_jh7110_clk_init, }; static const struct cqspi_driver_platdata pensando_cdns_qspi = { From 0c2db63ba3483a60de62748d3ce98a7dbd2ac0e5 Mon Sep 17 00:00:00 2001 From: William Qiu Date: Thu, 1 Jun 2023 16:51:51 +0800 Subject: [PATCH 23/92] dt-bindings: pwm: Add StarFive PWM module Add documentation to describe StarFive Pulse Width Modulation controller driver. Signed-off-by: William Qiu Reviewed-by: Krzysztof Kozlowski Reviewed-by: Hal Feng --- .../bindings/pwm/starfive,jh7100-pwm.yaml | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/starfive,jh7100-pwm.yaml diff --git a/Documentation/devicetree/bindings/pwm/starfive,jh7100-pwm.yaml b/Documentation/devicetree/bindings/pwm/starfive,jh7100-pwm.yaml new file mode 100644 index 0000000000000..6f1937beb962b --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/starfive,jh7100-pwm.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/starfive,jh7100-pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7100 and JH7110 PWM controller + +maintainers: + - William Qiu + +description: + StarFive SoCs contain PWM and when operating in PWM mode, the PTC core generates + binary signal with user-programmable low and high periods. Clock source for the + PWM can be either system clock or external clock. Each PWM timer block provides 8 + PWM channels. + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + enum: + - starfive,jh7100-pwm + - starfive,jh7110-pwm + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + "#pwm-cells": + const: 3 + +required: + - compatible + - reg + - clocks + - resets + +additionalProperties: false + +examples: + - | + pwm@12490000 { + compatible = "starfive,jh7100-pwm"; + reg = <0x12490000 0x10000>; + clocks = <&clkgen 181>; + resets = <&rstgen 109>; + #pwm-cells = <3>; + }; From fb841ea5a6c2187ddd73dc60e86e4759c4d6b926 Mon Sep 17 00:00:00 2001 From: William Qiu Date: Thu, 1 Jun 2023 16:51:52 +0800 Subject: [PATCH 24/92] pwm: starfive: Add PWM driver support Add Pulse Width Modulation driver support for StarFive JH7100 and JH7110 SoC. Co-developed-by: Hal Feng Signed-off-by: Hal Feng Signed-off-by: William Qiu --- drivers/pwm/Kconfig | 9 ++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-starfive-ptc.c | 192 +++++++++++++++++++++++++++++++++ 3 files changed, 202 insertions(+) create mode 100644 drivers/pwm/pwm-starfive-ptc.c diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 6210babb0741a..48800f33b5c1b 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -569,6 +569,15 @@ config PWM_SPRD To compile this driver as a module, choose M here: the module will be called pwm-sprd. +config PWM_STARFIVE_PTC + tristate "StarFive PWM PTC support" + depends on ARCH_STARFIVE || COMPILE_TEST + help + Generic PWM framework driver for StarFive SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-starfive-ptc. + config PWM_STI tristate "STiH4xx PWM support" depends on ARCH_STI || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index c822389c2a24c..d2b2a3aeea220 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o obj-$(CONFIG_PWM_SL28CPLD) += pwm-sl28cpld.o obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o obj-$(CONFIG_PWM_SPRD) += pwm-sprd.o +obj-$(CONFIG_PWM_STARFIVE_PTC) += pwm-starfive-ptc.o obj-$(CONFIG_PWM_STI) += pwm-sti.o obj-$(CONFIG_PWM_STM32) += pwm-stm32.o obj-$(CONFIG_PWM_STM32_LP) += pwm-stm32-lp.o diff --git a/drivers/pwm/pwm-starfive-ptc.c b/drivers/pwm/pwm-starfive-ptc.c new file mode 100644 index 0000000000000..57b5736f6732f --- /dev/null +++ b/drivers/pwm/pwm-starfive-ptc.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PWM driver for the StarFive JH71x0 SoC + * + * Copyright (C) 2018-2023 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Access PTC register (CNTR, HRC, LRC and CTRL) */ +#define REG_PTC_BASE_ADDR_SUB(base, N) \ +((base) + (((N) > 3) ? (((N) % 4) * 0x10 + (1 << 15)) : ((N) * 0x10))) +#define REG_PTC_RPTC_CNTR(base, N) (REG_PTC_BASE_ADDR_SUB(base, N)) +#define REG_PTC_RPTC_HRC(base, N) (REG_PTC_BASE_ADDR_SUB(base, N) + 0x4) +#define REG_PTC_RPTC_LRC(base, N) (REG_PTC_BASE_ADDR_SUB(base, N) + 0x8) +#define REG_PTC_RPTC_CTRL(base, N) (REG_PTC_BASE_ADDR_SUB(base, N) + 0xC) + +/* PTC_RPTC_CTRL register bits*/ +#define PTC_EN BIT(0) +#define PTC_ECLK BIT(1) +#define PTC_NEC BIT(2) +#define PTC_OE BIT(3) +#define PTC_SIGNLE BIT(4) +#define PTC_INTE BIT(5) +#define PTC_INT BIT(6) +#define PTC_CNTRRST BIT(7) +#define PTC_CAPTE BIT(8) + +struct starfive_pwm_ptc_device { + struct pwm_chip chip; + struct clk *clk; + struct reset_control *rst; + void __iomem *regs; + u32 clk_rate; /* PWM APB clock frequency */ +}; + +static inline +struct starfive_pwm_ptc_device *chip_to_starfive_ptc(struct pwm_chip *c) +{ + return container_of(c, struct starfive_pwm_ptc_device, chip); +} + +static int starfive_pwm_ptc_get_state(struct pwm_chip *chip, + struct pwm_device *dev, + struct pwm_state *state) +{ + struct starfive_pwm_ptc_device *pwm = chip_to_starfive_ptc(chip); + u32 period_data, duty_data, ctrl_data; + + period_data = readl(REG_PTC_RPTC_LRC(pwm->regs, dev->hwpwm)); + duty_data = readl(REG_PTC_RPTC_HRC(pwm->regs, dev->hwpwm)); + ctrl_data = readl(REG_PTC_RPTC_CTRL(pwm->regs, dev->hwpwm)); + + state->period = DIV_ROUND_CLOSEST_ULL((u64)period_data * NSEC_PER_SEC, pwm->clk_rate); + state->duty_cycle = DIV_ROUND_CLOSEST_ULL((u64)duty_data * NSEC_PER_SEC, pwm->clk_rate); + state->polarity = PWM_POLARITY_INVERSED; + state->enabled = (ctrl_data & PTC_EN) ? true : false; + + return 0; +} + +static int starfive_pwm_ptc_apply(struct pwm_chip *chip, + struct pwm_device *dev, + const struct pwm_state *state) +{ + struct starfive_pwm_ptc_device *pwm = chip_to_starfive_ptc(chip); + u32 period_data, duty_data, ctrl_data = 0; + + if (state->polarity != PWM_POLARITY_INVERSED) + return -EINVAL; + + period_data = DIV_ROUND_CLOSEST_ULL(state->period * pwm->clk_rate, + NSEC_PER_SEC); + duty_data = DIV_ROUND_CLOSEST_ULL(state->duty_cycle * pwm->clk_rate, + NSEC_PER_SEC); + + writel(period_data, REG_PTC_RPTC_LRC(pwm->regs, dev->hwpwm)); + writel(duty_data, REG_PTC_RPTC_HRC(pwm->regs, dev->hwpwm)); + writel(0, REG_PTC_RPTC_CNTR(pwm->regs, dev->hwpwm)); + + ctrl_data = readl(REG_PTC_RPTC_CTRL(pwm->regs, dev->hwpwm)); + if (state->enabled) + writel(ctrl_data | PTC_EN | PTC_OE, REG_PTC_RPTC_CTRL(pwm->regs, dev->hwpwm)); + else + writel(ctrl_data & ~(PTC_EN | PTC_OE), REG_PTC_RPTC_CTRL(pwm->regs, dev->hwpwm)); + + return 0; +} + +static const struct pwm_ops starfive_pwm_ptc_ops = { + .get_state = starfive_pwm_ptc_get_state, + .apply = starfive_pwm_ptc_apply, + .owner = THIS_MODULE, +}; + +static int starfive_pwm_ptc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct starfive_pwm_ptc_device *pwm; + struct pwm_chip *chip; + int ret; + + pwm = devm_kzalloc(dev, sizeof(*pwm), GFP_KERNEL); + if (!pwm) + return -ENOMEM; + + chip = &pwm->chip; + chip->dev = dev; + chip->ops = &starfive_pwm_ptc_ops; + chip->npwm = 8; + chip->of_pwm_n_cells = 3; + + pwm->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pwm->regs)) + return dev_err_probe(dev, PTR_ERR(pwm->regs), + "Unable to map IO resources\n"); + + pwm->clk = devm_clk_get(dev, NULL); + if (IS_ERR(pwm->clk)) + return dev_err_probe(dev, PTR_ERR(pwm->clk), + "Unable to get pwm's clock\n"); + + pwm->rst = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(pwm->rst)) + return dev_err_probe(dev, PTR_ERR(pwm->rst), + "Unable to get pwm's reset\n"); + + ret = clk_prepare_enable(pwm->clk); + if (ret) { + dev_err(dev, + "Failed to enable clock for pwm: %d\n", ret); + return ret; + } + + reset_control_deassert(pwm->rst); + + pwm->clk_rate = clk_get_rate(pwm->clk); + if (pwm->clk_rate <= 0) { + dev_warn(dev, "Failed to get APB clock rate\n"); + return -EINVAL; + } + + ret = devm_pwmchip_add(dev, chip); + if (ret < 0) { + dev_err(dev, "Cannot register PTC: %d\n", ret); + clk_disable_unprepare(pwm->clk); + reset_control_assert(pwm->rst); + return ret; + } + + platform_set_drvdata(pdev, pwm); + + return 0; +} + +static int starfive_pwm_ptc_remove(struct platform_device *dev) +{ + struct starfive_pwm_ptc_device *pwm = platform_get_drvdata(dev); + + reset_control_assert(pwm->rst); + clk_disable_unprepare(pwm->clk); + + return 0; +} + +static const struct of_device_id starfive_pwm_ptc_of_match[] = { + { .compatible = "starfive,jh7100-pwm" }, + { .compatible = "starfive,jh7110-pwm" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, starfive_pwm_ptc_of_match); + +static struct platform_driver starfive_pwm_ptc_driver = { + .probe = starfive_pwm_ptc_probe, + .remove = starfive_pwm_ptc_remove, + .driver = { + .name = "pwm-starfive-ptc", + .of_match_table = starfive_pwm_ptc_of_match, + }, +}; +module_platform_driver(starfive_pwm_ptc_driver); + +MODULE_AUTHOR("Jieqin Chen"); +MODULE_AUTHOR("Hal Feng "); +MODULE_DESCRIPTION("StarFive PWM PTC driver"); +MODULE_LICENSE("GPL"); From 09cd5482aec923becf9818a89b779b5d29382305 Mon Sep 17 00:00:00 2001 From: William Qiu Date: Mon, 24 Jul 2023 18:10:53 +0800 Subject: [PATCH 25/92] dt-bindings: spi: add reference file to YAML In JH7110 SoC, the spi module needs to use "arm,primecell-periphid" to override the h/w ID value to correctly init "spi_dev", so add "primecell.yaml" as the reference file for YAML. Signed-off-by: William Qiu Link: https://lore.kernel.org/r/20230724101054.25268-2-william.qiu@starfivetech.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-pl022.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/spi/spi-pl022.yaml b/Documentation/devicetree/bindings/spi/spi-pl022.yaml index 91e540a92fafe..5e5a704a766e5 100644 --- a/Documentation/devicetree/bindings/spi/spi-pl022.yaml +++ b/Documentation/devicetree/bindings/spi/spi-pl022.yaml @@ -11,6 +11,7 @@ maintainers: allOf: - $ref: spi-controller.yaml# + - $ref: /schemas/arm/primecell.yaml# # We need a select here so we don't match all nodes with 'arm,primecell' select: From 5856d8395e578b918e2282e18f00300790abca9d Mon Sep 17 00:00:00 2001 From: Jia Jie Ho Date: Thu, 25 May 2023 14:18:36 +0800 Subject: [PATCH 26/92] riscv: Kconfig: Add select ARM_AMBA to SOC_STARFIVE Selects ARM_AMBA platform support for StarFive SoCs required by spi and crypto dma engine. Signed-off-by: Jia Jie Ho Acked-by: Palmer Dabbelt Signed-off-by: Conor Dooley --- arch/riscv/Kconfig.socs | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs index 6833d01e2e707..30fd6a5128285 100644 --- a/arch/riscv/Kconfig.socs +++ b/arch/riscv/Kconfig.socs @@ -29,6 +29,7 @@ config SOC_STARFIVE bool "StarFive SoCs" select PINCTRL select RESET_CONTROLLER + select ARM_AMBA help This enables support for StarFive SoC platform hardware. From 7bcf8eddc40e13cb4dd677e5b5e137589a58e147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 7 Jul 2023 09:07:53 +0200 Subject: [PATCH 27/92] crypto: starfive - Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Signed-off-by: Herbert Xu --- drivers/crypto/starfive/jh7110-cryp.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/crypto/starfive/jh7110-cryp.c b/drivers/crypto/starfive/jh7110-cryp.c index cc43556b6c805..e573c097a4a05 100644 --- a/drivers/crypto/starfive/jh7110-cryp.c +++ b/drivers/crypto/starfive/jh7110-cryp.c @@ -212,7 +212,7 @@ static int starfive_cryp_probe(struct platform_device *pdev) return ret; } -static int starfive_cryp_remove(struct platform_device *pdev) +static void starfive_cryp_remove(struct platform_device *pdev) { struct starfive_cryp_dev *cryp = platform_get_drvdata(pdev); @@ -233,8 +233,6 @@ static int starfive_cryp_remove(struct platform_device *pdev) clk_disable_unprepare(cryp->hclk); clk_disable_unprepare(cryp->ahb); reset_control_assert(cryp->rst); - - return 0; } static const struct of_device_id starfive_dt_ids[] __maybe_unused = { @@ -245,7 +243,7 @@ MODULE_DEVICE_TABLE(of, starfive_dt_ids); static struct platform_driver starfive_cryp_driver = { .probe = starfive_cryp_probe, - .remove = starfive_cryp_remove, + .remove_new = starfive_cryp_remove, .driver = { .name = DRIVER_NAME, .of_match_table = starfive_dt_ids, From c30d68694e77e153286cbbd616b7d11ba3e5ad7b Mon Sep 17 00:00:00 2001 From: Jia Jie Ho Date: Mon, 17 Jul 2023 12:03:02 +0800 Subject: [PATCH 28/92] crypto: starfive - Add AES skcipher and aead support Adding AES skcipher and aead support to Starfive crypto module. Skcipher modes of operation include ecb, cbc, ctr, ofb, cfb. Aead modes include ccm and gcm. v1->v2: - Add include interrupt.h to fix compile error. (Herbert) Co-developed-by: Huan Feng Signed-off-by: Huan Feng Signed-off-by: Jia Jie Ho Signed-off-by: Herbert Xu --- drivers/crypto/starfive/Kconfig | 2 + drivers/crypto/starfive/Makefile | 2 +- drivers/crypto/starfive/jh7110-aes.c | 1034 +++++++++++++++++++++++++ drivers/crypto/starfive/jh7110-cryp.c | 36 +- drivers/crypto/starfive/jh7110-cryp.h | 65 ++ 5 files changed, 1132 insertions(+), 7 deletions(-) create mode 100644 drivers/crypto/starfive/jh7110-aes.c diff --git a/drivers/crypto/starfive/Kconfig b/drivers/crypto/starfive/Kconfig index df745fcb09dfc..2cb192502c1b7 100644 --- a/drivers/crypto/starfive/Kconfig +++ b/drivers/crypto/starfive/Kconfig @@ -12,6 +12,8 @@ config CRYPTO_DEV_JH7110 select CRYPTO_SHA512 select CRYPTO_SM3_GENERIC select CRYPTO_RSA + select CRYPTO_AES + select CRYPTO_CCM help Support for StarFive JH7110 crypto hardware acceleration engine. This module provides acceleration for public key algo, diff --git a/drivers/crypto/starfive/Makefile b/drivers/crypto/starfive/Makefile index 98b01d2f1ccf3..8c137afe58adf 100644 --- a/drivers/crypto/starfive/Makefile +++ b/drivers/crypto/starfive/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_CRYPTO_DEV_JH7110) += jh7110-crypto.o -jh7110-crypto-objs := jh7110-cryp.o jh7110-hash.o jh7110-rsa.o +jh7110-crypto-objs := jh7110-cryp.o jh7110-hash.o jh7110-rsa.o jh7110-aes.o diff --git a/drivers/crypto/starfive/jh7110-aes.c b/drivers/crypto/starfive/jh7110-aes.c new file mode 100644 index 0000000000000..04dd7958054f8 --- /dev/null +++ b/drivers/crypto/starfive/jh7110-aes.c @@ -0,0 +1,1034 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive AES acceleration driver + * + * Copyright (c) 2022 StarFive Technology + */ + +#include +#include +#include +#include +#include +#include "jh7110-cryp.h" + +#define STARFIVE_AES_REGS_OFFSET 0x100 +#define STARFIVE_AES_AESDIO0R (STARFIVE_AES_REGS_OFFSET + 0x0) +#define STARFIVE_AES_KEY0 (STARFIVE_AES_REGS_OFFSET + 0x4) +#define STARFIVE_AES_KEY1 (STARFIVE_AES_REGS_OFFSET + 0x8) +#define STARFIVE_AES_KEY2 (STARFIVE_AES_REGS_OFFSET + 0xC) +#define STARFIVE_AES_KEY3 (STARFIVE_AES_REGS_OFFSET + 0x10) +#define STARFIVE_AES_KEY4 (STARFIVE_AES_REGS_OFFSET + 0x14) +#define STARFIVE_AES_KEY5 (STARFIVE_AES_REGS_OFFSET + 0x18) +#define STARFIVE_AES_KEY6 (STARFIVE_AES_REGS_OFFSET + 0x1C) +#define STARFIVE_AES_KEY7 (STARFIVE_AES_REGS_OFFSET + 0x20) +#define STARFIVE_AES_CSR (STARFIVE_AES_REGS_OFFSET + 0x24) +#define STARFIVE_AES_IV0 (STARFIVE_AES_REGS_OFFSET + 0x28) +#define STARFIVE_AES_IV1 (STARFIVE_AES_REGS_OFFSET + 0x2C) +#define STARFIVE_AES_IV2 (STARFIVE_AES_REGS_OFFSET + 0x30) +#define STARFIVE_AES_IV3 (STARFIVE_AES_REGS_OFFSET + 0x34) +#define STARFIVE_AES_NONCE0 (STARFIVE_AES_REGS_OFFSET + 0x3C) +#define STARFIVE_AES_NONCE1 (STARFIVE_AES_REGS_OFFSET + 0x40) +#define STARFIVE_AES_NONCE2 (STARFIVE_AES_REGS_OFFSET + 0x44) +#define STARFIVE_AES_NONCE3 (STARFIVE_AES_REGS_OFFSET + 0x48) +#define STARFIVE_AES_ALEN0 (STARFIVE_AES_REGS_OFFSET + 0x4C) +#define STARFIVE_AES_ALEN1 (STARFIVE_AES_REGS_OFFSET + 0x50) +#define STARFIVE_AES_MLEN0 (STARFIVE_AES_REGS_OFFSET + 0x54) +#define STARFIVE_AES_MLEN1 (STARFIVE_AES_REGS_OFFSET + 0x58) +#define STARFIVE_AES_IVLEN (STARFIVE_AES_REGS_OFFSET + 0x5C) + +#define FLG_MODE_MASK GENMASK(2, 0) +#define FLG_ENCRYPT BIT(4) + +/* Misc */ +#define CCM_B0_ADATA 0x40 +#define AES_BLOCK_32 (AES_BLOCK_SIZE / sizeof(u32)) + +static inline int starfive_aes_wait_busy(struct starfive_cryp_dev *cryp) +{ + u32 status; + + return readl_relaxed_poll_timeout(cryp->base + STARFIVE_AES_CSR, status, + !(status & STARFIVE_AES_BUSY), 10, 100000); +} + +static inline int starfive_aes_wait_keydone(struct starfive_cryp_dev *cryp) +{ + u32 status; + + return readl_relaxed_poll_timeout(cryp->base + STARFIVE_AES_CSR, status, + (status & STARFIVE_AES_KEY_DONE), 10, 100000); +} + +static inline int starfive_aes_wait_gcmdone(struct starfive_cryp_dev *cryp) +{ + u32 status; + + return readl_relaxed_poll_timeout(cryp->base + STARFIVE_AES_CSR, status, + (status & STARFIVE_AES_GCM_DONE), 10, 100000); +} + +static inline int is_gcm(struct starfive_cryp_dev *cryp) +{ + return (cryp->flags & FLG_MODE_MASK) == STARFIVE_AES_MODE_GCM; +} + +static inline int is_encrypt(struct starfive_cryp_dev *cryp) +{ + return cryp->flags & FLG_ENCRYPT; +} + +static void starfive_aes_aead_hw_start(struct starfive_cryp_ctx *ctx, u32 hw_mode) +{ + struct starfive_cryp_dev *cryp = ctx->cryp; + unsigned int value; + + switch (hw_mode) { + case STARFIVE_AES_MODE_GCM: + value = readl(ctx->cryp->base + STARFIVE_AES_CSR); + value |= STARFIVE_AES_GCM_START; + writel(value, cryp->base + STARFIVE_AES_CSR); + starfive_aes_wait_gcmdone(cryp); + break; + case STARFIVE_AES_MODE_CCM: + value = readl(ctx->cryp->base + STARFIVE_AES_CSR); + value |= STARFIVE_AES_CCM_START; + writel(value, cryp->base + STARFIVE_AES_CSR); + break; + } +} + +static inline void starfive_aes_set_ivlen(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_dev *cryp = ctx->cryp; + + if (is_gcm(cryp)) + writel(GCM_AES_IV_SIZE, cryp->base + STARFIVE_AES_IVLEN); + else + writel(AES_BLOCK_SIZE, cryp->base + STARFIVE_AES_IVLEN); +} + +static inline void starfive_aes_set_alen(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_dev *cryp = ctx->cryp; + + writel(upper_32_bits(cryp->assoclen), cryp->base + STARFIVE_AES_ALEN0); + writel(lower_32_bits(cryp->assoclen), cryp->base + STARFIVE_AES_ALEN1); +} + +static inline void starfive_aes_set_mlen(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_dev *cryp = ctx->cryp; + + writel(upper_32_bits(cryp->total_in), cryp->base + STARFIVE_AES_MLEN0); + writel(lower_32_bits(cryp->total_in), cryp->base + STARFIVE_AES_MLEN1); +} + +static inline int starfive_aes_ccm_check_iv(const u8 *iv) +{ + /* 2 <= L <= 8, so 1 <= L' <= 7. */ + if (iv[0] < 1 || iv[0] > 7) + return -EINVAL; + + return 0; +} + +static int starfive_aes_write_iv(struct starfive_cryp_ctx *ctx, u32 *iv) +{ + struct starfive_cryp_dev *cryp = ctx->cryp; + + writel(iv[0], cryp->base + STARFIVE_AES_IV0); + writel(iv[1], cryp->base + STARFIVE_AES_IV1); + writel(iv[2], cryp->base + STARFIVE_AES_IV2); + + if (is_gcm(cryp)) { + if (starfive_aes_wait_gcmdone(cryp)) + return -ETIMEDOUT; + + return 0; + } + + writel(iv[3], cryp->base + STARFIVE_AES_IV3); + + return 0; +} + +static inline void starfive_aes_get_iv(struct starfive_cryp_dev *cryp, u32 *iv) +{ + iv[0] = readl(cryp->base + STARFIVE_AES_IV0); + iv[1] = readl(cryp->base + STARFIVE_AES_IV1); + iv[2] = readl(cryp->base + STARFIVE_AES_IV2); + iv[3] = readl(cryp->base + STARFIVE_AES_IV3); +} + +static inline void starfive_aes_write_nonce(struct starfive_cryp_ctx *ctx, u32 *nonce) +{ + struct starfive_cryp_dev *cryp = ctx->cryp; + + writel(nonce[0], cryp->base + STARFIVE_AES_NONCE0); + writel(nonce[1], cryp->base + STARFIVE_AES_NONCE1); + writel(nonce[2], cryp->base + STARFIVE_AES_NONCE2); + writel(nonce[3], cryp->base + STARFIVE_AES_NONCE3); +} + +static int starfive_aes_write_key(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_dev *cryp = ctx->cryp; + u32 *key = (u32 *)ctx->key; + + if (ctx->keylen >= AES_KEYSIZE_128) { + writel(key[0], cryp->base + STARFIVE_AES_KEY0); + writel(key[1], cryp->base + STARFIVE_AES_KEY1); + writel(key[2], cryp->base + STARFIVE_AES_KEY2); + writel(key[3], cryp->base + STARFIVE_AES_KEY3); + } + + if (ctx->keylen >= AES_KEYSIZE_192) { + writel(key[4], cryp->base + STARFIVE_AES_KEY4); + writel(key[5], cryp->base + STARFIVE_AES_KEY5); + } + + if (ctx->keylen >= AES_KEYSIZE_256) { + writel(key[6], cryp->base + STARFIVE_AES_KEY6); + writel(key[7], cryp->base + STARFIVE_AES_KEY7); + } + + if (starfive_aes_wait_keydone(cryp)) + return -ETIMEDOUT; + + return 0; +} + +static int starfive_aes_ccm_init(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_dev *cryp = ctx->cryp; + u8 iv[AES_BLOCK_SIZE], b0[AES_BLOCK_SIZE]; + unsigned int textlen; + + memcpy(iv, cryp->req.areq->iv, AES_BLOCK_SIZE); + memset(iv + AES_BLOCK_SIZE - 1 - iv[0], 0, iv[0] + 1); + + /* Build B0 */ + memcpy(b0, iv, AES_BLOCK_SIZE); + + b0[0] |= (8 * ((cryp->authsize - 2) / 2)); + + if (cryp->assoclen) + b0[0] |= CCM_B0_ADATA; + + textlen = cryp->total_in; + + b0[AES_BLOCK_SIZE - 2] = textlen >> 8; + b0[AES_BLOCK_SIZE - 1] = textlen & 0xFF; + + starfive_aes_write_nonce(ctx, (u32 *)b0); + + return 0; +} + +static int starfive_aes_hw_init(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_request_ctx *rctx = ctx->rctx; + struct starfive_cryp_dev *cryp = ctx->cryp; + u32 hw_mode; + + /* reset */ + rctx->csr.aes.v = 0; + rctx->csr.aes.aesrst = 1; + writel(rctx->csr.aes.v, cryp->base + STARFIVE_AES_CSR); + + /* csr setup */ + hw_mode = cryp->flags & FLG_MODE_MASK; + + rctx->csr.aes.v = 0; + + switch (ctx->keylen) { + case AES_KEYSIZE_128: + rctx->csr.aes.keymode = STARFIVE_AES_KEYMODE_128; + break; + case AES_KEYSIZE_192: + rctx->csr.aes.keymode = STARFIVE_AES_KEYMODE_192; + break; + case AES_KEYSIZE_256: + rctx->csr.aes.keymode = STARFIVE_AES_KEYMODE_256; + break; + } + + rctx->csr.aes.mode = hw_mode; + rctx->csr.aes.cmode = !is_encrypt(cryp); + rctx->csr.aes.ie = 1; + + if (hw_mode == STARFIVE_AES_MODE_CFB || + hw_mode == STARFIVE_AES_MODE_OFB) + rctx->csr.aes.stmode = STARFIVE_AES_MODE_XFB_128; + else + rctx->csr.aes.stmode = STARFIVE_AES_MODE_XFB_1; + + if (cryp->side_chan) { + rctx->csr.aes.delay_aes = 1; + rctx->csr.aes.vaes_start = 1; + } + + writel(rctx->csr.aes.v, cryp->base + STARFIVE_AES_CSR); + + cryp->err = starfive_aes_write_key(ctx); + if (cryp->err) + return cryp->err; + + switch (hw_mode) { + case STARFIVE_AES_MODE_GCM: + starfive_aes_set_alen(ctx); + starfive_aes_set_mlen(ctx); + starfive_aes_set_ivlen(ctx); + starfive_aes_aead_hw_start(ctx, hw_mode); + starfive_aes_write_iv(ctx, (void *)cryp->req.areq->iv); + break; + case STARFIVE_AES_MODE_CCM: + starfive_aes_set_alen(ctx); + starfive_aes_set_mlen(ctx); + starfive_aes_ccm_init(ctx); + starfive_aes_aead_hw_start(ctx, hw_mode); + break; + case STARFIVE_AES_MODE_OFB: + case STARFIVE_AES_MODE_CFB: + case STARFIVE_AES_MODE_CBC: + case STARFIVE_AES_MODE_CTR: + starfive_aes_write_iv(ctx, (void *)cryp->req.sreq->iv); + break; + default: + break; + } + + return cryp->err; +} + +static int starfive_aes_read_authtag(struct starfive_cryp_dev *cryp) +{ + int i, start_addr; + + if (starfive_aes_wait_busy(cryp)) + return dev_err_probe(cryp->dev, -ETIMEDOUT, + "Timeout waiting for tag generation."); + + start_addr = STARFIVE_AES_NONCE0; + + if (is_gcm(cryp)) + for (i = 0; i < AES_BLOCK_32; i++, start_addr += 4) + cryp->tag_out[i] = readl(cryp->base + start_addr); + else + for (i = 0; i < AES_BLOCK_32; i++) + cryp->tag_out[i] = readl(cryp->base + STARFIVE_AES_AESDIO0R); + + if (is_encrypt(cryp)) { + scatterwalk_copychunks(cryp->tag_out, &cryp->out_walk, cryp->authsize, 1); + } else { + scatterwalk_copychunks(cryp->tag_in, &cryp->in_walk, cryp->authsize, 0); + + if (crypto_memneq(cryp->tag_in, cryp->tag_out, cryp->authsize)) + return dev_err_probe(cryp->dev, -EBADMSG, "Failed tag verification\n"); + } + + return 0; +} + +static void starfive_aes_finish_req(struct starfive_cryp_dev *cryp) +{ + union starfive_aes_csr csr; + int err = cryp->err; + + if (!err && cryp->authsize) + err = starfive_aes_read_authtag(cryp); + + if (!err && ((cryp->flags & FLG_MODE_MASK) == STARFIVE_AES_MODE_CBC || + (cryp->flags & FLG_MODE_MASK) == STARFIVE_AES_MODE_CTR)) + starfive_aes_get_iv(cryp, (void *)cryp->req.sreq->iv); + + /* reset irq flags*/ + csr.v = 0; + csr.aesrst = 1; + writel(csr.v, cryp->base + STARFIVE_AES_CSR); + + if (cryp->authsize) + crypto_finalize_aead_request(cryp->engine, cryp->req.areq, err); + else + crypto_finalize_skcipher_request(cryp->engine, cryp->req.sreq, + err); +} + +void starfive_aes_done_task(unsigned long param) +{ + struct starfive_cryp_dev *cryp = (struct starfive_cryp_dev *)param; + u32 block[AES_BLOCK_32]; + u32 stat; + int i; + + for (i = 0; i < AES_BLOCK_32; i++) + block[i] = readl(cryp->base + STARFIVE_AES_AESDIO0R); + + scatterwalk_copychunks(block, &cryp->out_walk, min_t(size_t, AES_BLOCK_SIZE, + cryp->total_out), 1); + + cryp->total_out -= min_t(size_t, AES_BLOCK_SIZE, cryp->total_out); + + if (!cryp->total_out) { + starfive_aes_finish_req(cryp); + return; + } + + memset(block, 0, AES_BLOCK_SIZE); + scatterwalk_copychunks(block, &cryp->in_walk, min_t(size_t, AES_BLOCK_SIZE, + cryp->total_in), 0); + cryp->total_in -= min_t(size_t, AES_BLOCK_SIZE, cryp->total_in); + + for (i = 0; i < AES_BLOCK_32; i++) + writel(block[i], cryp->base + STARFIVE_AES_AESDIO0R); + + stat = readl(cryp->base + STARFIVE_IE_MASK_OFFSET); + stat &= ~STARFIVE_IE_MASK_AES_DONE; + writel(stat, cryp->base + STARFIVE_IE_MASK_OFFSET); +} + +static int starfive_aes_gcm_write_adata(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_dev *cryp = ctx->cryp; + struct starfive_cryp_request_ctx *rctx = ctx->rctx; + u32 *buffer; + int total_len, loop; + + total_len = ALIGN(cryp->assoclen, AES_BLOCK_SIZE) / sizeof(unsigned int); + buffer = (u32 *)rctx->adata; + + for (loop = 0; loop < total_len; loop += 4) { + writel(*buffer, cryp->base + STARFIVE_AES_NONCE0); + buffer++; + writel(*buffer, cryp->base + STARFIVE_AES_NONCE1); + buffer++; + writel(*buffer, cryp->base + STARFIVE_AES_NONCE2); + buffer++; + writel(*buffer, cryp->base + STARFIVE_AES_NONCE3); + buffer++; + } + + if (starfive_aes_wait_gcmdone(cryp)) + return dev_err_probe(cryp->dev, -ETIMEDOUT, + "Timeout processing gcm aad block"); + + return 0; +} + +static int starfive_aes_ccm_write_adata(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_dev *cryp = ctx->cryp; + struct starfive_cryp_request_ctx *rctx = ctx->rctx; + u32 *buffer; + u8 *ci; + int total_len, loop; + + total_len = cryp->assoclen; + + ci = rctx->adata; + writeb(*ci, cryp->base + STARFIVE_AES_AESDIO0R); + ci++; + writeb(*ci, cryp->base + STARFIVE_AES_AESDIO0R); + ci++; + total_len -= 2; + buffer = (u32 *)ci; + + for (loop = 0; loop < 3; loop++, buffer++) + writel(*buffer, cryp->base + STARFIVE_AES_AESDIO0R); + + total_len -= 12; + + while (total_len > 0) { + for (loop = 0; loop < AES_BLOCK_32; loop++, buffer++) + writel(*buffer, cryp->base + STARFIVE_AES_AESDIO0R); + + total_len -= AES_BLOCK_SIZE; + } + + if (starfive_aes_wait_busy(cryp)) + return dev_err_probe(cryp->dev, -ETIMEDOUT, + "Timeout processing ccm aad block"); + + return 0; +} + +static int starfive_aes_prepare_req(struct skcipher_request *req, + struct aead_request *areq) +{ + struct starfive_cryp_ctx *ctx; + struct starfive_cryp_request_ctx *rctx; + struct starfive_cryp_dev *cryp; + + if (!req && !areq) + return -EINVAL; + + ctx = req ? crypto_skcipher_ctx(crypto_skcipher_reqtfm(req)) : + crypto_aead_ctx(crypto_aead_reqtfm(areq)); + + cryp = ctx->cryp; + rctx = req ? skcipher_request_ctx(req) : aead_request_ctx(areq); + + if (req) { + cryp->req.sreq = req; + cryp->total_in = req->cryptlen; + cryp->total_out = req->cryptlen; + cryp->assoclen = 0; + cryp->authsize = 0; + } else { + cryp->req.areq = areq; + cryp->assoclen = areq->assoclen; + cryp->authsize = crypto_aead_authsize(crypto_aead_reqtfm(areq)); + if (is_encrypt(cryp)) { + cryp->total_in = areq->cryptlen; + cryp->total_out = areq->cryptlen; + } else { + cryp->total_in = areq->cryptlen - cryp->authsize; + cryp->total_out = cryp->total_in; + } + } + + rctx->in_sg = req ? req->src : areq->src; + scatterwalk_start(&cryp->in_walk, rctx->in_sg); + + rctx->out_sg = req ? req->dst : areq->dst; + scatterwalk_start(&cryp->out_walk, rctx->out_sg); + + if (cryp->assoclen) { + rctx->adata = kzalloc(ALIGN(cryp->assoclen, AES_BLOCK_SIZE), GFP_KERNEL); + if (IS_ERR(rctx->adata)) + return dev_err_probe(cryp->dev, PTR_ERR(rctx->adata), + "Failed to alloc memory for adata"); + + scatterwalk_copychunks(rctx->adata, &cryp->in_walk, cryp->assoclen, 0); + scatterwalk_copychunks(NULL, &cryp->out_walk, cryp->assoclen, 2); + } + + ctx->rctx = rctx; + + return starfive_aes_hw_init(ctx); +} + +static int starfive_aes_do_one_req(struct crypto_engine *engine, void *areq) +{ + struct skcipher_request *req = + container_of(areq, struct skcipher_request, base); + struct starfive_cryp_ctx *ctx = + crypto_skcipher_ctx(crypto_skcipher_reqtfm(req)); + struct starfive_cryp_dev *cryp = ctx->cryp; + u32 block[AES_BLOCK_32]; + u32 stat; + int i; + + /* + * Write first plain/ciphertext block to start the module + * then let irq tasklet handle the rest of the data blocks. + */ + scatterwalk_copychunks(block, &cryp->in_walk, min_t(size_t, AES_BLOCK_SIZE, + cryp->total_in), 0); + cryp->total_in -= min_t(size_t, AES_BLOCK_SIZE, cryp->total_in); + + for (i = 0; i < AES_BLOCK_32; i++) + writel(block[i], cryp->base + STARFIVE_AES_AESDIO0R); + + stat = readl(cryp->base + STARFIVE_IE_MASK_OFFSET); + stat &= ~STARFIVE_IE_MASK_AES_DONE; + writel(stat, cryp->base + STARFIVE_IE_MASK_OFFSET); + + return 0; +} + +static int starfive_aes_skcipher_prepare_req(struct crypto_engine *engine, + void *areq) +{ + struct skcipher_request *req = + container_of(areq, struct skcipher_request, base); + + return starfive_aes_prepare_req(req, NULL); +} + +static int starfive_aes_init_tfm(struct crypto_skcipher *tfm) +{ + struct starfive_cryp_ctx *ctx = crypto_skcipher_ctx(tfm); + + ctx->cryp = starfive_cryp_find_dev(ctx); + if (!ctx->cryp) + return -ENODEV; + + crypto_skcipher_set_reqsize(tfm, sizeof(struct starfive_cryp_request_ctx) + + sizeof(struct skcipher_request)); + + ctx->enginectx.op.do_one_request = starfive_aes_do_one_req; + ctx->enginectx.op.prepare_request = starfive_aes_skcipher_prepare_req; + ctx->enginectx.op.unprepare_request = NULL; + + return 0; +} + +static void starfive_aes_exit_tfm(struct crypto_skcipher *tfm) +{ + struct starfive_cryp_ctx *ctx = crypto_skcipher_ctx(tfm); + + ctx->enginectx.op.do_one_request = NULL; + ctx->enginectx.op.prepare_request = NULL; + ctx->enginectx.op.unprepare_request = NULL; +} + +static int starfive_aes_aead_do_one_req(struct crypto_engine *engine, void *areq) +{ + struct aead_request *req = + container_of(areq, struct aead_request, base); + struct starfive_cryp_ctx *ctx = + crypto_aead_ctx(crypto_aead_reqtfm(req)); + struct starfive_cryp_dev *cryp = ctx->cryp; + struct starfive_cryp_request_ctx *rctx = ctx->rctx; + u32 block[AES_BLOCK_32]; + u32 stat; + int i; + + if (!cryp->assoclen) + goto write_text; + + if ((cryp->flags & FLG_MODE_MASK) == STARFIVE_AES_MODE_CCM) + cryp->err = starfive_aes_ccm_write_adata(ctx); + else + cryp->err = starfive_aes_gcm_write_adata(ctx); + + kfree(rctx->adata); + + if (cryp->err) + return cryp->err; + +write_text: + if (!cryp->total_in) + goto finish_req; + + /* + * Write first plain/ciphertext block to start the module + * then let irq tasklet handle the rest of the data blocks. + */ + scatterwalk_copychunks(block, &cryp->in_walk, min_t(size_t, AES_BLOCK_SIZE, + cryp->total_in), 0); + cryp->total_in -= min_t(size_t, AES_BLOCK_SIZE, cryp->total_in); + + for (i = 0; i < AES_BLOCK_32; i++) + writel(block[i], cryp->base + STARFIVE_AES_AESDIO0R); + + stat = readl(cryp->base + STARFIVE_IE_MASK_OFFSET); + stat &= ~STARFIVE_IE_MASK_AES_DONE; + writel(stat, cryp->base + STARFIVE_IE_MASK_OFFSET); + + return 0; + +finish_req: + starfive_aes_finish_req(cryp); + return 0; +} + +static int starfive_aes_aead_prepare_req(struct crypto_engine *engine, void *areq) +{ + struct aead_request *req = + container_of(areq, struct aead_request, base); + + return starfive_aes_prepare_req(NULL, req); +} + +static int starfive_aes_aead_init_tfm(struct crypto_aead *tfm) +{ + struct starfive_cryp_ctx *ctx = crypto_aead_ctx(tfm); + struct starfive_cryp_dev *cryp = ctx->cryp; + struct crypto_tfm *aead = crypto_aead_tfm(tfm); + struct crypto_alg *alg = aead->__crt_alg; + + ctx->cryp = starfive_cryp_find_dev(ctx); + if (!ctx->cryp) + return -ENODEV; + + if (alg->cra_flags & CRYPTO_ALG_NEED_FALLBACK) { + ctx->aead_fbk = crypto_alloc_aead(alg->cra_name, 0, + CRYPTO_ALG_NEED_FALLBACK); + if (IS_ERR(ctx->aead_fbk)) + return dev_err_probe(cryp->dev, PTR_ERR(ctx->aead_fbk), + "%s() failed to allocate fallback for %s\n", + __func__, alg->cra_name); + } + + crypto_aead_set_reqsize(tfm, sizeof(struct starfive_cryp_ctx) + + sizeof(struct aead_request)); + + ctx->enginectx.op.do_one_request = starfive_aes_aead_do_one_req; + ctx->enginectx.op.prepare_request = starfive_aes_aead_prepare_req; + ctx->enginectx.op.unprepare_request = NULL; + + return 0; +} + +static void starfive_aes_aead_exit_tfm(struct crypto_aead *tfm) +{ + struct starfive_cryp_ctx *ctx = crypto_aead_ctx(tfm); + + if (ctx->aead_fbk) { + crypto_free_aead(ctx->aead_fbk); + ctx->aead_fbk = NULL; + } + + ctx->enginectx.op.do_one_request = NULL; + ctx->enginectx.op.prepare_request = NULL; + ctx->enginectx.op.unprepare_request = NULL; +} + +static int starfive_aes_crypt(struct skcipher_request *req, unsigned long flags) +{ + struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); + struct starfive_cryp_ctx *ctx = crypto_skcipher_ctx(tfm); + struct starfive_cryp_dev *cryp = ctx->cryp; + unsigned int blocksize_align = crypto_skcipher_blocksize(tfm) - 1; + + cryp->flags = flags; + + if ((cryp->flags & FLG_MODE_MASK) == STARFIVE_AES_MODE_ECB || + (cryp->flags & FLG_MODE_MASK) == STARFIVE_AES_MODE_CBC) + if (req->cryptlen & blocksize_align) + return -EINVAL; + + return crypto_transfer_skcipher_request_to_engine(cryp->engine, req); +} + +static int starfive_aes_aead_crypt(struct aead_request *req, unsigned long flags) +{ + struct starfive_cryp_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req)); + struct starfive_cryp_dev *cryp = ctx->cryp; + + cryp->flags = flags; + + /* + * HW engine could not perform CCM tag verification on + * non-blocksize aligned text, use fallback algo instead + */ + if (ctx->aead_fbk && !is_encrypt(cryp)) { + struct aead_request *subreq = aead_request_ctx(req); + + aead_request_set_tfm(subreq, ctx->aead_fbk); + aead_request_set_callback(subreq, req->base.flags, + req->base.complete, req->base.data); + aead_request_set_crypt(subreq, req->src, + req->dst, req->cryptlen, req->iv); + aead_request_set_ad(subreq, req->assoclen); + + return crypto_aead_decrypt(subreq); + } + + return crypto_transfer_aead_request_to_engine(cryp->engine, req); +} + +static int starfive_aes_setkey(struct crypto_skcipher *tfm, const u8 *key, + unsigned int keylen) +{ + struct starfive_cryp_ctx *ctx = crypto_skcipher_ctx(tfm); + + if (!key || !keylen) + return -EINVAL; + + if (keylen != AES_KEYSIZE_128 && + keylen != AES_KEYSIZE_192 && + keylen != AES_KEYSIZE_256) + return -EINVAL; + + memcpy(ctx->key, key, keylen); + ctx->keylen = keylen; + + return 0; +} + +static int starfive_aes_aead_setkey(struct crypto_aead *tfm, const u8 *key, + unsigned int keylen) +{ + struct starfive_cryp_ctx *ctx = crypto_aead_ctx(tfm); + + if (!key || !keylen) + return -EINVAL; + + if (keylen != AES_KEYSIZE_128 && + keylen != AES_KEYSIZE_192 && + keylen != AES_KEYSIZE_256) + return -EINVAL; + + memcpy(ctx->key, key, keylen); + ctx->keylen = keylen; + + if (ctx->aead_fbk) + return crypto_aead_setkey(ctx->aead_fbk, key, keylen); + + return 0; +} + +static int starfive_aes_gcm_setauthsize(struct crypto_aead *tfm, + unsigned int authsize) +{ + return crypto_gcm_check_authsize(authsize); +} + +static int starfive_aes_ccm_setauthsize(struct crypto_aead *tfm, + unsigned int authsize) +{ + struct starfive_cryp_ctx *ctx = crypto_aead_ctx(tfm); + + switch (authsize) { + case 4: + case 6: + case 8: + case 10: + case 12: + case 14: + case 16: + break; + default: + return -EINVAL; + } + + return crypto_aead_setauthsize(ctx->aead_fbk, authsize); +} + +static int starfive_aes_ecb_encrypt(struct skcipher_request *req) +{ + return starfive_aes_crypt(req, STARFIVE_AES_MODE_ECB | FLG_ENCRYPT); +} + +static int starfive_aes_ecb_decrypt(struct skcipher_request *req) +{ + return starfive_aes_crypt(req, STARFIVE_AES_MODE_ECB); +} + +static int starfive_aes_cbc_encrypt(struct skcipher_request *req) +{ + return starfive_aes_crypt(req, STARFIVE_AES_MODE_CBC | FLG_ENCRYPT); +} + +static int starfive_aes_cbc_decrypt(struct skcipher_request *req) +{ + return starfive_aes_crypt(req, STARFIVE_AES_MODE_CBC); +} + +static int starfive_aes_cfb_encrypt(struct skcipher_request *req) +{ + return starfive_aes_crypt(req, STARFIVE_AES_MODE_CFB | FLG_ENCRYPT); +} + +static int starfive_aes_cfb_decrypt(struct skcipher_request *req) +{ + return starfive_aes_crypt(req, STARFIVE_AES_MODE_CFB); +} + +static int starfive_aes_ofb_encrypt(struct skcipher_request *req) +{ + return starfive_aes_crypt(req, STARFIVE_AES_MODE_OFB | FLG_ENCRYPT); +} + +static int starfive_aes_ofb_decrypt(struct skcipher_request *req) +{ + return starfive_aes_crypt(req, STARFIVE_AES_MODE_OFB); +} + +static int starfive_aes_ctr_encrypt(struct skcipher_request *req) +{ + return starfive_aes_crypt(req, STARFIVE_AES_MODE_CTR | FLG_ENCRYPT); +} + +static int starfive_aes_ctr_decrypt(struct skcipher_request *req) +{ + return starfive_aes_crypt(req, STARFIVE_AES_MODE_CTR); +} + +static int starfive_aes_gcm_encrypt(struct aead_request *req) +{ + return starfive_aes_aead_crypt(req, STARFIVE_AES_MODE_GCM | FLG_ENCRYPT); +} + +static int starfive_aes_gcm_decrypt(struct aead_request *req) +{ + return starfive_aes_aead_crypt(req, STARFIVE_AES_MODE_GCM); +} + +static int starfive_aes_ccm_encrypt(struct aead_request *req) +{ + int ret; + + ret = starfive_aes_ccm_check_iv(req->iv); + if (ret) + return ret; + + return starfive_aes_aead_crypt(req, STARFIVE_AES_MODE_CCM | FLG_ENCRYPT); +} + +static int starfive_aes_ccm_decrypt(struct aead_request *req) +{ + int ret; + + ret = starfive_aes_ccm_check_iv(req->iv); + if (ret) + return ret; + + return starfive_aes_aead_crypt(req, STARFIVE_AES_MODE_CCM); +} + +static struct skcipher_alg skcipher_algs[] = { +{ + .init = starfive_aes_init_tfm, + .exit = starfive_aes_exit_tfm, + .setkey = starfive_aes_setkey, + .encrypt = starfive_aes_ecb_encrypt, + .decrypt = starfive_aes_ecb_decrypt, + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .base = { + .cra_name = "ecb(aes)", + .cra_driver_name = "starfive-ecb-aes", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 0xf, + .cra_module = THIS_MODULE, + }, +}, { + .init = starfive_aes_init_tfm, + .exit = starfive_aes_exit_tfm, + .setkey = starfive_aes_setkey, + .encrypt = starfive_aes_cbc_encrypt, + .decrypt = starfive_aes_cbc_decrypt, + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .base = { + .cra_name = "cbc(aes)", + .cra_driver_name = "starfive-cbc-aes", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 0xf, + .cra_module = THIS_MODULE, + }, +}, { + .init = starfive_aes_init_tfm, + .exit = starfive_aes_exit_tfm, + .setkey = starfive_aes_setkey, + .encrypt = starfive_aes_ctr_encrypt, + .decrypt = starfive_aes_ctr_decrypt, + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .base = { + .cra_name = "ctr(aes)", + .cra_driver_name = "starfive-ctr-aes", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = 1, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 0xf, + .cra_module = THIS_MODULE, + }, +}, { + .init = starfive_aes_init_tfm, + .exit = starfive_aes_exit_tfm, + .setkey = starfive_aes_setkey, + .encrypt = starfive_aes_cfb_encrypt, + .decrypt = starfive_aes_cfb_decrypt, + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .base = { + .cra_name = "cfb(aes)", + .cra_driver_name = "starfive-cfb-aes", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = 1, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 0xf, + .cra_module = THIS_MODULE, + }, +}, { + .init = starfive_aes_init_tfm, + .exit = starfive_aes_exit_tfm, + .setkey = starfive_aes_setkey, + .encrypt = starfive_aes_ofb_encrypt, + .decrypt = starfive_aes_ofb_decrypt, + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .base = { + .cra_name = "ofb(aes)", + .cra_driver_name = "starfive-ofb-aes", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = 1, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 0xf, + .cra_module = THIS_MODULE, + }, +}, +}; + +static struct aead_alg aead_algs[] = { +{ + .setkey = starfive_aes_aead_setkey, + .setauthsize = starfive_aes_gcm_setauthsize, + .encrypt = starfive_aes_gcm_encrypt, + .decrypt = starfive_aes_gcm_decrypt, + .init = starfive_aes_aead_init_tfm, + .exit = starfive_aes_aead_exit_tfm, + .ivsize = GCM_AES_IV_SIZE, + .maxauthsize = AES_BLOCK_SIZE, + .base = { + .cra_name = "gcm(aes)", + .cra_driver_name = "starfive-gcm-aes", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = 1, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 0xf, + .cra_module = THIS_MODULE, + }, +}, { + .setkey = starfive_aes_aead_setkey, + .setauthsize = starfive_aes_ccm_setauthsize, + .encrypt = starfive_aes_ccm_encrypt, + .decrypt = starfive_aes_ccm_decrypt, + .init = starfive_aes_aead_init_tfm, + .exit = starfive_aes_aead_exit_tfm, + .ivsize = AES_BLOCK_SIZE, + .maxauthsize = AES_BLOCK_SIZE, + .base = { + .cra_name = "ccm(aes)", + .cra_driver_name = "starfive-ccm-aes", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = 1, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 0xf, + .cra_module = THIS_MODULE, + }, +}, +}; + +int starfive_aes_register_algs(void) +{ + int ret; + + ret = crypto_register_skciphers(skcipher_algs, ARRAY_SIZE(skcipher_algs)); + if (ret) + return ret; + + ret = crypto_register_aeads(aead_algs, ARRAY_SIZE(aead_algs)); + if (ret) + crypto_unregister_skciphers(skcipher_algs, ARRAY_SIZE(skcipher_algs)); + + return ret; +} + +void starfive_aes_unregister_algs(void) +{ + crypto_unregister_aeads(aead_algs, ARRAY_SIZE(aead_algs)); + crypto_unregister_skciphers(skcipher_algs, ARRAY_SIZE(skcipher_algs)); +} diff --git a/drivers/crypto/starfive/jh7110-cryp.c b/drivers/crypto/starfive/jh7110-cryp.c index e573c097a4a05..ab37010ceb88f 100644 --- a/drivers/crypto/starfive/jh7110-cryp.c +++ b/drivers/crypto/starfive/jh7110-cryp.c @@ -51,6 +51,13 @@ struct starfive_cryp_dev *starfive_cryp_find_dev(struct starfive_cryp_ctx *ctx) return cryp; } +static u16 side_chan; +module_param(side_chan, ushort, 0); +MODULE_PARM_DESC(side_chan, "Enable side channel mitigation for AES module.\n" + "Enabling this feature will reduce speed performance.\n" + " 0 - Disabled\n" + " other - Enabled"); + static int starfive_dma_init(struct starfive_cryp_dev *cryp) { dma_cap_mask_t mask; @@ -82,20 +89,26 @@ static void starfive_dma_cleanup(struct starfive_cryp_dev *cryp) static irqreturn_t starfive_cryp_irq(int irq, void *priv) { u32 status; + u32 mask; struct starfive_cryp_dev *cryp = (struct starfive_cryp_dev *)priv; + mask = readl(cryp->base + STARFIVE_IE_MASK_OFFSET); status = readl(cryp->base + STARFIVE_IE_FLAG_OFFSET); + if (status & STARFIVE_IE_FLAG_AES_DONE) { + mask |= STARFIVE_IE_MASK_AES_DONE; + writel(mask, cryp->base + STARFIVE_IE_MASK_OFFSET); + tasklet_schedule(&cryp->aes_done); + } + if (status & STARFIVE_IE_FLAG_HASH_DONE) { - status = readl(cryp->base + STARFIVE_IE_MASK_OFFSET); - status |= STARFIVE_IE_MASK_HASH_DONE; - writel(status, cryp->base + STARFIVE_IE_MASK_OFFSET); + mask |= STARFIVE_IE_MASK_HASH_DONE; + writel(mask, cryp->base + STARFIVE_IE_MASK_OFFSET); tasklet_schedule(&cryp->hash_done); } if (status & STARFIVE_IE_FLAG_PKA_DONE) { - status = readl(cryp->base + STARFIVE_IE_MASK_OFFSET); - status |= STARFIVE_IE_MASK_PKA_DONE; - writel(status, cryp->base + STARFIVE_IE_MASK_OFFSET); + mask |= STARFIVE_IE_MASK_PKA_DONE; + writel(mask, cryp->base + STARFIVE_IE_MASK_OFFSET); complete(&cryp->pka_done); } @@ -121,10 +134,12 @@ static int starfive_cryp_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, PTR_ERR(cryp->base), "Error remapping memory for platform device\n"); + tasklet_init(&cryp->aes_done, starfive_aes_done_task, (unsigned long)cryp); tasklet_init(&cryp->hash_done, starfive_hash_done_task, (unsigned long)cryp); cryp->phys_base = res->start; cryp->dma_maxburst = 32; + cryp->side_chan = side_chan; cryp->hclk = devm_clk_get(&pdev->dev, "hclk"); if (IS_ERR(cryp->hclk)) @@ -180,6 +195,10 @@ static int starfive_cryp_probe(struct platform_device *pdev) if (ret) goto err_engine_start; + ret = starfive_aes_register_algs(); + if (ret) + goto err_algs_aes; + ret = starfive_hash_register_algs(); if (ret) goto err_algs_hash; @@ -193,6 +212,8 @@ static int starfive_cryp_probe(struct platform_device *pdev) err_algs_rsa: starfive_hash_unregister_algs(); err_algs_hash: + starfive_aes_unregister_algs(); +err_algs_aes: crypto_engine_stop(cryp->engine); err_engine_start: crypto_engine_exit(cryp->engine); @@ -207,6 +228,7 @@ static int starfive_cryp_probe(struct platform_device *pdev) clk_disable_unprepare(cryp->ahb); reset_control_assert(cryp->rst); + tasklet_kill(&cryp->aes_done); tasklet_kill(&cryp->hash_done); err_probe_defer: return ret; @@ -216,9 +238,11 @@ static void starfive_cryp_remove(struct platform_device *pdev) { struct starfive_cryp_dev *cryp = platform_get_drvdata(pdev); + starfive_aes_unregister_algs(); starfive_hash_unregister_algs(); starfive_rsa_unregister_algs(); + tasklet_kill(&cryp->aes_done); tasklet_kill(&cryp->hash_done); crypto_engine_stop(cryp->engine); diff --git a/drivers/crypto/starfive/jh7110-cryp.h b/drivers/crypto/starfive/jh7110-cryp.h index 0cdcffc0d7d42..b6d809e8fe452 100644 --- a/drivers/crypto/starfive/jh7110-cryp.h +++ b/drivers/crypto/starfive/jh7110-cryp.h @@ -5,7 +5,9 @@ #include #include #include +#include +#include #include #include #include @@ -17,13 +19,56 @@ #define STARFIVE_DMA_IN_LEN_OFFSET 0x10 #define STARFIVE_DMA_OUT_LEN_OFFSET 0x14 +#define STARFIVE_IE_MASK_AES_DONE 0x1 #define STARFIVE_IE_MASK_HASH_DONE 0x4 #define STARFIVE_IE_MASK_PKA_DONE 0x8 +#define STARFIVE_IE_FLAG_AES_DONE 0x1 #define STARFIVE_IE_FLAG_HASH_DONE 0x4 #define STARFIVE_IE_FLAG_PKA_DONE 0x8 #define STARFIVE_MSG_BUFFER_SIZE SZ_16K #define MAX_KEY_SIZE SHA512_BLOCK_SIZE +#define STARFIVE_AES_IV_LEN AES_BLOCK_SIZE +#define STARFIVE_AES_CTR_LEN AES_BLOCK_SIZE + +union starfive_aes_csr { + u32 v; + struct { + u32 cmode :1; +#define STARFIVE_AES_KEYMODE_128 0x0 +#define STARFIVE_AES_KEYMODE_192 0x1 +#define STARFIVE_AES_KEYMODE_256 0x2 + u32 keymode :2; +#define STARFIVE_AES_BUSY BIT(3) + u32 busy :1; + u32 done :1; +#define STARFIVE_AES_KEY_DONE BIT(5) + u32 krdy :1; + u32 aesrst :1; + u32 ie :1; +#define STARFIVE_AES_CCM_START BIT(8) + u32 ccm_start :1; +#define STARFIVE_AES_MODE_ECB 0x0 +#define STARFIVE_AES_MODE_CBC 0x1 +#define STARFIVE_AES_MODE_CFB 0x2 +#define STARFIVE_AES_MODE_OFB 0x3 +#define STARFIVE_AES_MODE_CTR 0x4 +#define STARFIVE_AES_MODE_CCM 0x5 +#define STARFIVE_AES_MODE_GCM 0x6 + u32 mode :3; +#define STARFIVE_AES_GCM_START BIT(12) + u32 gcm_start :1; +#define STARFIVE_AES_GCM_DONE BIT(13) + u32 gcm_done :1; + u32 delay_aes :1; + u32 vaes_start :1; + u32 rsvd_0 :8; +#define STARFIVE_AES_MODE_XFB_1 0x0 +#define STARFIVE_AES_MODE_XFB_128 0x5 + u32 stmode :3; + u32 rsvd_1 :5; + }; +}; union starfive_hash_csr { u32 v; @@ -116,6 +161,7 @@ struct starfive_cryp_ctx { struct starfive_rsa_key rsa_key; struct crypto_akcipher *akcipher_fbk; struct crypto_ahash *ahash_fbk; + struct crypto_aead *aead_fbk; }; struct starfive_cryp_dev { @@ -133,13 +179,26 @@ struct starfive_cryp_dev { struct dma_chan *rx; struct dma_slave_config cfg_in; struct dma_slave_config cfg_out; + struct scatter_walk in_walk; + struct scatter_walk out_walk; struct crypto_engine *engine; + struct tasklet_struct aes_done; struct tasklet_struct hash_done; struct completion pka_done; + size_t assoclen; + size_t total_in; + size_t total_out; + u32 tag_in[4]; + u32 tag_out[4]; + unsigned int authsize; + unsigned long flags; int err; + bool side_chan; union starfive_alg_cr alg_cr; union { struct ahash_request *hreq; + struct aead_request *areq; + struct skcipher_request *sreq; } req; }; @@ -147,6 +206,7 @@ struct starfive_cryp_request_ctx { union { union starfive_hash_csr hash; union starfive_pka_cacr pka; + union starfive_aes_csr aes; } csr; struct scatterlist *in_sg; @@ -157,6 +217,7 @@ struct starfive_cryp_request_ctx { unsigned int blksize; unsigned int digsize; unsigned long in_sg_len; + unsigned char *adata; u8 rsa_data[] __aligned(sizeof(u32)); }; @@ -168,5 +229,9 @@ void starfive_hash_unregister_algs(void); int starfive_rsa_register_algs(void); void starfive_rsa_unregister_algs(void); +int starfive_aes_register_algs(void); +void starfive_aes_unregister_algs(void); + void starfive_hash_done_task(unsigned long param); +void starfive_aes_done_task(unsigned long param); #endif From e8427a197553ae4e900f6d3382fcdc599e32f0c3 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Mon, 31 Jul 2023 22:02:49 +0800 Subject: [PATCH 29/92] crypto: starfive - fix return value check in starfive_aes_prepare_req() kzalloc() returns NULL pointer not PTR_ERR() when it fails, so replace the IS_ERR() check with NULL pointer check. Fixes: e22471c2331c ("crypto: starfive - Add AES skcipher and aead support") Signed-off-by: Yang Yingliang Signed-off-by: Herbert Xu --- drivers/crypto/starfive/jh7110-aes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/crypto/starfive/jh7110-aes.c b/drivers/crypto/starfive/jh7110-aes.c index 04dd7958054f8..278dfa4aa743f 100644 --- a/drivers/crypto/starfive/jh7110-aes.c +++ b/drivers/crypto/starfive/jh7110-aes.c @@ -496,8 +496,8 @@ static int starfive_aes_prepare_req(struct skcipher_request *req, if (cryp->assoclen) { rctx->adata = kzalloc(ALIGN(cryp->assoclen, AES_BLOCK_SIZE), GFP_KERNEL); - if (IS_ERR(rctx->adata)) - return dev_err_probe(cryp->dev, PTR_ERR(rctx->adata), + if (!rctx->adata) + return dev_err_probe(cryp->dev, -ENOMEM, "Failed to alloc memory for adata"); scatterwalk_copychunks(rctx->adata, &cryp->in_walk, cryp->assoclen, 0); From fa2a5103e47f1c0e0c2fc9949157db49ff803e3f Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Thu, 29 Jun 2023 15:51:11 +0800 Subject: [PATCH 30/92] dt-bindings: phy: Add StarFive JH7110 USB PHY Add StarFive JH7110 SoC USB 2.0 PHY dt-binding. Signed-off-by: Minda Chen Reviewed-by: Hal Feng Reviewed-by: Rob Herring Reviewed-by: Roger Quadros Link: https://lore.kernel.org/r/20230629075115.11934-2-minda.chen@starfivetech.com Signed-off-by: Vinod Koul --- .../bindings/phy/starfive,jh7110-usb-phy.yaml | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/starfive,jh7110-usb-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/starfive,jh7110-usb-phy.yaml b/Documentation/devicetree/bindings/phy/starfive,jh7110-usb-phy.yaml new file mode 100644 index 0000000000000..269e9f9f12b62 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/starfive,jh7110-usb-phy.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/starfive,jh7110-usb-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 USB 2.0 PHY + +maintainers: + - Minda Chen + +properties: + compatible: + const: starfive,jh7110-usb-phy + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + + clocks: + items: + - description: PHY 125m + - description: app 125m + + clock-names: + items: + - const: 125m + - const: app_125m + +required: + - compatible + - reg + - clocks + - clock-names + - "#phy-cells" + +additionalProperties: false + +examples: + - | + phy@10200000 { + compatible = "starfive,jh7110-usb-phy"; + reg = <0x10200000 0x10000>; + clocks = <&syscrg 95>, + <&stgcrg 6>; + clock-names = "125m", "app_125m"; + #phy-cells = <0>; + }; From 224e54f4b6108200dd6ba2c5e52458f08b9c3ebe Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Thu, 29 Jun 2023 15:51:12 +0800 Subject: [PATCH 31/92] dt-bindings: phy: Add StarFive JH7110 PCIe PHY Add StarFive JH7110 SoC PCIe 2.0 PHY dt-binding. PCIe PHY0 (phy@10210000) can be used as USB 3.0 PHY. Signed-off-by: Minda Chen Reviewed-by: Hal Feng Reviewed-by: Rob Herring Reviewed-by: Roger Quadros Link: https://lore.kernel.org/r/20230629075115.11934-3-minda.chen@starfivetech.com Signed-off-by: Vinod Koul --- .../phy/starfive,jh7110-pcie-phy.yaml | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/starfive,jh7110-pcie-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/starfive,jh7110-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/starfive,jh7110-pcie-phy.yaml new file mode 100644 index 0000000000000..2e83a6164cd1d --- /dev/null +++ b/Documentation/devicetree/bindings/phy/starfive,jh7110-pcie-phy.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/starfive,jh7110-pcie-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 PCIe 2.0 PHY + +maintainers: + - Minda Chen + +properties: + compatible: + const: starfive,jh7110-pcie-phy + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + + starfive,sys-syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to System Register Controller sys_syscon node. + - description: PHY connect offset of SYS_SYSCONSAIF__SYSCFG register for USB PHY. + description: + The phandle to System Register Controller syscon node and the PHY connect offset + of SYS_SYSCONSAIF__SYSCFG register. Connect PHY to USB3 controller. + + starfive,stg-syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to System Register Controller stg_syscon node. + - description: PHY mode offset of STG_SYSCONSAIF__SYSCFG register. + - description: PHY enable for USB offset of STG_SYSCONSAIF__SYSCFG register. + description: + The phandle to System Register Controller syscon node and the offset + of STG_SYSCONSAIF__SYSCFG register for PCIe PHY. Total 2 regsisters offset. + +required: + - compatible + - reg + - "#phy-cells" + +additionalProperties: false + +examples: + - | + phy@10210000 { + compatible = "starfive,jh7110-pcie-phy"; + reg = <0x10210000 0x10000>; + #phy-cells = <0>; + starfive,sys-syscon = <&sys_syscon 0x18>; + starfive,stg-syscon = <&stg_syscon 0x148 0x1f4>; + }; From fb46b52e035b556569cecd1d3c00bd57e1eb7c78 Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Thu, 29 Jun 2023 15:51:13 +0800 Subject: [PATCH 32/92] phy: starfive: Add JH7110 USB 2.0 PHY driver Add Starfive JH7110 SoC USB 2.0 PHY driver support. USB 2.0 PHY default connect to Cadence USB controller. Signed-off-by: Minda Chen Reviewed-by: Roger Quadros Link: https://lore.kernel.org/r/20230629075115.11934-4-minda.chen@starfivetech.com Signed-off-by: Vinod Koul --- drivers/phy/Kconfig | 1 + drivers/phy/Makefile | 1 + drivers/phy/starfive/Kconfig | 14 +++ drivers/phy/starfive/Makefile | 2 + drivers/phy/starfive/phy-jh7110-usb.c | 152 ++++++++++++++++++++++++++ 5 files changed, 170 insertions(+) create mode 100644 drivers/phy/starfive/Kconfig create mode 100644 drivers/phy/starfive/Makefile create mode 100644 drivers/phy/starfive/phy-jh7110-usb.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 8dba9596408f2..e4502958fd62d 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -92,6 +92,7 @@ source "drivers/phy/rockchip/Kconfig" source "drivers/phy/samsung/Kconfig" source "drivers/phy/socionext/Kconfig" source "drivers/phy/st/Kconfig" +source "drivers/phy/starfive/Kconfig" source "drivers/phy/sunplus/Kconfig" source "drivers/phy/tegra/Kconfig" source "drivers/phy/ti/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 54f312c10a408..fb3dc9de61115 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -31,6 +31,7 @@ obj-y += allwinner/ \ samsung/ \ socionext/ \ st/ \ + starfive/ \ sunplus/ \ tegra/ \ ti/ \ diff --git a/drivers/phy/starfive/Kconfig b/drivers/phy/starfive/Kconfig new file mode 100644 index 0000000000000..65b5f879bbf9f --- /dev/null +++ b/drivers/phy/starfive/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Phy drivers for StarFive platforms +# + +config PHY_STARFIVE_JH7110_USB + tristate "Starfive JH7110 USB 2.0 PHY support" + depends on USB_SUPPORT + select GENERIC_PHY + help + Enable this to support the StarFive USB 2.0 PHY, + used with the Cadence USB controller. + If M is selected, the module will be called + phy-jh7110-usb.ko. diff --git a/drivers/phy/starfive/Makefile b/drivers/phy/starfive/Makefile new file mode 100644 index 0000000000000..52e9a09cc6193 --- /dev/null +++ b/drivers/phy/starfive/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PHY_STARFIVE_JH7110_USB) += phy-jh7110-usb.o diff --git a/drivers/phy/starfive/phy-jh7110-usb.c b/drivers/phy/starfive/phy-jh7110-usb.c new file mode 100644 index 0000000000000..633912f8a05d0 --- /dev/null +++ b/drivers/phy/starfive/phy-jh7110-usb.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * StarFive JH7110 USB 2.0 PHY driver + * + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * Author: Minda Chen + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define USB_125M_CLK_RATE 125000000 +#define USB_LS_KEEPALIVE_OFF 0x4 +#define USB_LS_KEEPALIVE_ENABLE BIT(4) + +struct jh7110_usb2_phy { + struct phy *phy; + void __iomem *regs; + struct clk *usb_125m_clk; + struct clk *app_125m; + enum phy_mode mode; +}; + +static void usb2_set_ls_keepalive(struct jh7110_usb2_phy *phy, bool set) +{ + unsigned int val; + + /* Host mode enable the LS speed keep-alive signal */ + val = readl(phy->regs + USB_LS_KEEPALIVE_OFF); + if (set) + val |= USB_LS_KEEPALIVE_ENABLE; + else + val &= ~USB_LS_KEEPALIVE_ENABLE; + + writel(val, phy->regs + USB_LS_KEEPALIVE_OFF); +} + +static int usb2_phy_set_mode(struct phy *_phy, + enum phy_mode mode, int submode) +{ + struct jh7110_usb2_phy *phy = phy_get_drvdata(_phy); + + switch (mode) { + case PHY_MODE_USB_HOST: + case PHY_MODE_USB_DEVICE: + case PHY_MODE_USB_OTG: + break; + default: + return -EINVAL; + } + + if (mode != phy->mode) { + dev_dbg(&_phy->dev, "Changing phy to %d\n", mode); + phy->mode = mode; + usb2_set_ls_keepalive(phy, (mode != PHY_MODE_USB_DEVICE)); + } + + return 0; +} + +static int jh7110_usb2_phy_init(struct phy *_phy) +{ + struct jh7110_usb2_phy *phy = phy_get_drvdata(_phy); + int ret; + + ret = clk_set_rate(phy->usb_125m_clk, USB_125M_CLK_RATE); + if (ret) + return ret; + + ret = clk_prepare_enable(phy->app_125m); + if (ret) + return ret; + + return 0; +} + +static int jh7110_usb2_phy_exit(struct phy *_phy) +{ + struct jh7110_usb2_phy *phy = phy_get_drvdata(_phy); + + clk_disable_unprepare(phy->app_125m); + + return 0; +} + +static const struct phy_ops jh7110_usb2_phy_ops = { + .init = jh7110_usb2_phy_init, + .exit = jh7110_usb2_phy_exit, + .set_mode = usb2_phy_set_mode, + .owner = THIS_MODULE, +}; + +static int jh7110_usb_phy_probe(struct platform_device *pdev) +{ + struct jh7110_usb2_phy *phy; + struct device *dev = &pdev->dev; + struct phy_provider *phy_provider; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->usb_125m_clk = devm_clk_get(dev, "125m"); + if (IS_ERR(phy->usb_125m_clk)) + return dev_err_probe(dev, PTR_ERR(phy->usb_125m_clk), + "Failed to get 125m clock\n"); + + phy->app_125m = devm_clk_get(dev, "app_125m"); + if (IS_ERR(phy->app_125m)) + return dev_err_probe(dev, PTR_ERR(phy->app_125m), + "Failed to get app 125m clock\n"); + + phy->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(phy->regs)) + return dev_err_probe(dev, PTR_ERR(phy->regs), + "Failed to map phy base\n"); + + phy->phy = devm_phy_create(dev, NULL, &jh7110_usb2_phy_ops); + if (IS_ERR(phy->phy)) + return dev_err_probe(dev, PTR_ERR(phy->phy), + "Failed to create phy\n"); + + phy_set_drvdata(phy->phy, phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id jh7110_usb_phy_of_match[] = { + { .compatible = "starfive,jh7110-usb-phy" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, jh7110_usb_phy_of_match); + +static struct platform_driver jh7110_usb_phy_driver = { + .probe = jh7110_usb_phy_probe, + .driver = { + .of_match_table = jh7110_usb_phy_of_match, + .name = "jh7110-usb-phy", + } +}; +module_platform_driver(jh7110_usb_phy_driver); + +MODULE_DESCRIPTION("StarFive JH7110 USB 2.0 PHY driver"); +MODULE_AUTHOR("Minda Chen "); +MODULE_LICENSE("GPL"); From 41d07a6ae92ce70c344f0b6a85b4cb9011c8f563 Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Thu, 29 Jun 2023 15:51:14 +0800 Subject: [PATCH 33/92] phy: starfive: Add JH7110 PCIE 2.0 PHY driver Add Starfive JH7110 SoC PCIe 2.0 PHY driver support. PCIe 2.0 PHY default connect to PCIe controller. PCIe PHY can connect to USB 3.0 controller. Signed-off-by: Minda Chen Reviewed-by: Roger Quadros Link: https://lore.kernel.org/r/20230629075115.11934-5-minda.chen@starfivetech.com Signed-off-by: Vinod Koul --- drivers/phy/starfive/Kconfig | 10 ++ drivers/phy/starfive/Makefile | 1 + drivers/phy/starfive/phy-jh7110-pcie.c | 204 +++++++++++++++++++++++++ 3 files changed, 215 insertions(+) create mode 100644 drivers/phy/starfive/phy-jh7110-pcie.c diff --git a/drivers/phy/starfive/Kconfig b/drivers/phy/starfive/Kconfig index 65b5f879bbf9f..da9a98cdf7e39 100644 --- a/drivers/phy/starfive/Kconfig +++ b/drivers/phy/starfive/Kconfig @@ -3,6 +3,16 @@ # Phy drivers for StarFive platforms # +config PHY_STARFIVE_JH7110_PCIE + tristate "Starfive JH7110 PCIE 2.0/USB 3.0 PHY support" + depends on HAS_IOMEM + select GENERIC_PHY + help + Enable this to support the StarFive PCIe 2.0 PHY, + or used as USB 3.0 PHY. + If M is selected, the module will be called + phy-jh7110-pcie.ko. + config PHY_STARFIVE_JH7110_USB tristate "Starfive JH7110 USB 2.0 PHY support" depends on USB_SUPPORT diff --git a/drivers/phy/starfive/Makefile b/drivers/phy/starfive/Makefile index 52e9a09cc6193..1c62d93e32805 100644 --- a/drivers/phy/starfive/Makefile +++ b/drivers/phy/starfive/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PHY_STARFIVE_JH7110_PCIE) += phy-jh7110-pcie.o obj-$(CONFIG_PHY_STARFIVE_JH7110_USB) += phy-jh7110-usb.o diff --git a/drivers/phy/starfive/phy-jh7110-pcie.c b/drivers/phy/starfive/phy-jh7110-pcie.c new file mode 100644 index 0000000000000..cbe79c1f59d33 --- /dev/null +++ b/drivers/phy/starfive/phy-jh7110-pcie.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * StarFive JH7110 PCIe 2.0 PHY driver + * + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * Author: Minda Chen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCIE_KVCO_LEVEL_OFF 0x28 +#define PCIE_USB3_PHY_PLL_CTL_OFF 0x7c +#define PCIE_KVCO_TUNE_SIGNAL_OFF 0x80 +#define PCIE_USB3_PHY_ENABLE BIT(4) +#define PHY_KVCO_FINE_TUNE_LEVEL 0x91 +#define PHY_KVCO_FINE_TUNE_SIGNALS 0xc + +#define USB_PDRSTN_SPLIT BIT(17) + +#define PCIE_PHY_MODE BIT(20) +#define PCIE_PHY_MODE_MASK GENMASK(21, 20) +#define PCIE_USB3_BUS_WIDTH_MASK GENMASK(3, 2) +#define PCIE_USB3_BUS_WIDTH BIT(3) +#define PCIE_USB3_RATE_MASK GENMASK(6, 5) +#define PCIE_USB3_RX_STANDBY_MASK BIT(7) +#define PCIE_USB3_PHY_ENABLE BIT(4) + +struct jh7110_pcie_phy { + struct phy *phy; + struct regmap *stg_syscon; + struct regmap *sys_syscon; + void __iomem *regs; + u32 sys_phy_connect; + u32 stg_pcie_mode; + u32 stg_pcie_usb; + enum phy_mode mode; +}; + +static int phy_usb3_mode_set(struct jh7110_pcie_phy *data) +{ + if (!data->stg_syscon || !data->sys_syscon) { + dev_err(&data->phy->dev, "doesn't support usb3 mode\n"); + return -EINVAL; + } + + regmap_update_bits(data->stg_syscon, data->stg_pcie_mode, + PCIE_PHY_MODE_MASK, PCIE_PHY_MODE); + regmap_update_bits(data->stg_syscon, data->stg_pcie_usb, + PCIE_USB3_BUS_WIDTH_MASK, 0); + regmap_update_bits(data->stg_syscon, data->stg_pcie_usb, + PCIE_USB3_PHY_ENABLE, PCIE_USB3_PHY_ENABLE); + + /* Connect usb 3.0 phy mode */ + regmap_update_bits(data->sys_syscon, data->sys_phy_connect, + USB_PDRSTN_SPLIT, 0); + + /* Configuare spread-spectrum mode: down-spread-spectrum */ + writel(PCIE_USB3_PHY_ENABLE, data->regs + PCIE_USB3_PHY_PLL_CTL_OFF); + + return 0; +} + +static void phy_pcie_mode_set(struct jh7110_pcie_phy *data) +{ + u32 val; + + /* default is PCIe mode */ + if (!data->stg_syscon || !data->sys_syscon) + return; + + regmap_update_bits(data->stg_syscon, data->stg_pcie_mode, + PCIE_PHY_MODE_MASK, 0); + regmap_update_bits(data->stg_syscon, data->stg_pcie_usb, + PCIE_USB3_BUS_WIDTH_MASK, + PCIE_USB3_BUS_WIDTH); + regmap_update_bits(data->stg_syscon, data->stg_pcie_usb, + PCIE_USB3_PHY_ENABLE, 0); + + regmap_update_bits(data->sys_syscon, data->sys_phy_connect, + USB_PDRSTN_SPLIT, 0); + + val = readl(data->regs + PCIE_USB3_PHY_PLL_CTL_OFF); + val &= ~PCIE_USB3_PHY_ENABLE; + writel(val, data->regs + PCIE_USB3_PHY_PLL_CTL_OFF); +} + +static void phy_kvco_gain_set(struct jh7110_pcie_phy *phy) +{ + /* PCIe Multi-PHY PLL KVCO Gain fine tune settings: */ + writel(PHY_KVCO_FINE_TUNE_LEVEL, phy->regs + PCIE_KVCO_LEVEL_OFF); + writel(PHY_KVCO_FINE_TUNE_SIGNALS, phy->regs + PCIE_KVCO_TUNE_SIGNAL_OFF); +} + +static int jh7110_pcie_phy_set_mode(struct phy *_phy, + enum phy_mode mode, int submode) +{ + struct jh7110_pcie_phy *phy = phy_get_drvdata(_phy); + int ret; + + if (mode == phy->mode) + return 0; + + switch (mode) { + case PHY_MODE_USB_HOST: + case PHY_MODE_USB_DEVICE: + case PHY_MODE_USB_OTG: + ret = phy_usb3_mode_set(phy); + if (ret) + return ret; + break; + case PHY_MODE_PCIE: + phy_pcie_mode_set(phy); + break; + default: + return -EINVAL; + } + + dev_dbg(&_phy->dev, "Changing phy mode to %d\n", mode); + phy->mode = mode; + + return 0; +} + +static const struct phy_ops jh7110_pcie_phy_ops = { + .set_mode = jh7110_pcie_phy_set_mode, + .owner = THIS_MODULE, +}; + +static int jh7110_pcie_phy_probe(struct platform_device *pdev) +{ + struct jh7110_pcie_phy *phy; + struct device *dev = &pdev->dev; + struct phy_provider *phy_provider; + u32 args[2]; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(phy->regs)) + return PTR_ERR(phy->regs); + + phy->phy = devm_phy_create(dev, NULL, &jh7110_pcie_phy_ops); + if (IS_ERR(phy->phy)) + return dev_err_probe(dev, PTR_ERR(phy->regs), + "Failed to map phy base\n"); + + phy->sys_syscon = + syscon_regmap_lookup_by_phandle_args(pdev->dev.of_node, + "starfive,sys-syscon", + 1, args); + + if (!IS_ERR_OR_NULL(phy->sys_syscon)) + phy->sys_phy_connect = args[0]; + else + phy->sys_syscon = NULL; + + phy->stg_syscon = + syscon_regmap_lookup_by_phandle_args(pdev->dev.of_node, + "starfive,stg-syscon", + 2, args); + + if (!IS_ERR_OR_NULL(phy->stg_syscon)) { + phy->stg_pcie_mode = args[0]; + phy->stg_pcie_usb = args[1]; + } else { + phy->stg_syscon = NULL; + } + + phy_kvco_gain_set(phy); + + phy_set_drvdata(phy->phy, phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id jh7110_pcie_phy_of_match[] = { + { .compatible = "starfive,jh7110-pcie-phy" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, jh7110_pcie_phy_of_match); + +static struct platform_driver jh7110_pcie_phy_driver = { + .probe = jh7110_pcie_phy_probe, + .driver = { + .of_match_table = jh7110_pcie_phy_of_match, + .name = "jh7110-pcie-phy", + } +}; +module_platform_driver(jh7110_pcie_phy_driver); + +MODULE_DESCRIPTION("StarFive JH7110 PCIe 2.0 PHY driver"); +MODULE_AUTHOR("Minda Chen "); +MODULE_LICENSE("GPL"); From a702f9ac66bab6223312c211e58b4946e87a2135 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 18 Jul 2023 10:02:49 +0300 Subject: [PATCH 34/92] phy: starfive: fix error code in probe This is using the wrong pointer, "phy->regs" vs "phy->phy". Fixes: fd097f48eea9 ("phy: starfive: Add JH7110 PCIE 2.0 PHY driver") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/3cc81f2a-efd7-4ef7-ae6b-e38c91efe153@moroto.mountain Signed-off-by: Vinod Koul --- drivers/phy/starfive/phy-jh7110-pcie.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/starfive/phy-jh7110-pcie.c b/drivers/phy/starfive/phy-jh7110-pcie.c index cbe79c1f59d33..734c8e0077277 100644 --- a/drivers/phy/starfive/phy-jh7110-pcie.c +++ b/drivers/phy/starfive/phy-jh7110-pcie.c @@ -151,7 +151,7 @@ static int jh7110_pcie_phy_probe(struct platform_device *pdev) phy->phy = devm_phy_create(dev, NULL, &jh7110_pcie_phy_ops); if (IS_ERR(phy->phy)) - return dev_err_probe(dev, PTR_ERR(phy->regs), + return dev_err_probe(dev, PTR_ERR(phy->phy), "Failed to map phy base\n"); phy->sys_syscon = From fb858bf8f51a4c434b0d6cdb7da91e1cba76282e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 7 Jul 2023 09:36:53 +0200 Subject: [PATCH 35/92] usb: cdns3: starfive: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Roger Quadros Link: https://lore.kernel.org/r/20230707073653.3396988-1-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-starfive.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/cdns3/cdns3-starfive.c b/drivers/usb/cdns3/cdns3-starfive.c index fc1f003b145dc..a7265b86e4277 100644 --- a/drivers/usb/cdns3/cdns3-starfive.c +++ b/drivers/usb/cdns3/cdns3-starfive.c @@ -166,7 +166,7 @@ static int cdns_starfive_remove_core(struct device *dev, void *c) return 0; } -static int cdns_starfive_remove(struct platform_device *pdev) +static void cdns_starfive_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct cdns_starfive *data = dev_get_drvdata(dev); @@ -178,8 +178,6 @@ static int cdns_starfive_remove(struct platform_device *pdev) pm_runtime_put_noidle(dev); cdns_clk_rst_deinit(data); platform_set_drvdata(pdev, NULL); - - return 0; } #ifdef CONFIG_PM @@ -232,7 +230,7 @@ MODULE_DEVICE_TABLE(of, cdns_starfive_of_match); static struct platform_driver cdns_starfive_driver = { .probe = cdns_starfive_probe, - .remove = cdns_starfive_remove, + .remove_new = cdns_starfive_remove, .driver = { .name = "cdns3-starfive", .of_match_table = cdns_starfive_of_match, From 74fbbe0f51d9f7f3b107f0ccbec607c41205cc5c Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Fri, 21 Jul 2023 17:59:23 +0800 Subject: [PATCH 36/92] usb: cdns3: Add PHY mode switch to usb2 PHY cdns3 just set PHY mode switch for USB3.0 PHY. If USB 2.0 PHY contains PHY mode switch setting, USB 2.0 PHY mode function can't be called. So add PHY mode switch function for USB 2.0 PHY. Signed-off-by: Minda Chen Reviewed-by: Roger Quadros Link: https://lore.kernel.org/r/20230721095923.20445-1-minda.chen@starfivetech.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/drd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c index d00ff98dffabf..04b6d12f2b9a3 100644 --- a/drivers/usb/cdns3/drd.c +++ b/drivers/usb/cdns3/drd.c @@ -196,6 +196,7 @@ int cdns_drd_host_on(struct cdns *cdns) if (ret) dev_err(cdns->dev, "timeout waiting for xhci_ready\n"); + phy_set_mode(cdns->usb2_phy, PHY_MODE_USB_HOST); phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_HOST); return ret; } @@ -216,6 +217,7 @@ void cdns_drd_host_off(struct cdns *cdns) readl_poll_timeout_atomic(&cdns->otg_regs->state, val, !(val & OTGSTATE_HOST_STATE_MASK), 1, 2000000); + phy_set_mode(cdns->usb2_phy, PHY_MODE_INVALID); phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID); } @@ -248,6 +250,7 @@ int cdns_drd_gadget_on(struct cdns *cdns) return ret; } + phy_set_mode(cdns->usb2_phy, PHY_MODE_USB_DEVICE); phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_DEVICE); return 0; } @@ -273,6 +276,7 @@ void cdns_drd_gadget_off(struct cdns *cdns) readl_poll_timeout_atomic(&cdns->otg_regs->state, val, !(val & OTGSTATE_DEV_STATE_MASK), 1, 2000000); + phy_set_mode(cdns->usb2_phy, PHY_MODE_INVALID); phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID); } EXPORT_SYMBOL_GPL(cdns_drd_gadget_off); From 6b804a6fd10f323d25ec91f3a0965172f740420f Mon Sep 17 00:00:00 2001 From: Daire McNamara Date: Fri, 28 Jul 2023 14:13:55 +0100 Subject: [PATCH 37/92] PCI: microchip: Correct the DED and SEC interrupt bit offsets The SEC and DED interrupt bits were the wrong way round so the SEC interrupt handler attempted to mask, unmask, and clear the DED interrupt and vice versa. Correct the bit offsets so each interrupt handler operates properly. Fixes: 6f15a9c9f941 ("PCI: microchip: Add Microchip PolarFire PCIe controller driver") Signed-off-by: Daire McNamara Reviewed-by: Conor Dooley --- drivers/pci/controller/pcie-microchip-host.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pcie-microchip-host.c b/drivers/pci/controller/pcie-microchip-host.c index 5e710e4854646..dd5245904c874 100644 --- a/drivers/pci/controller/pcie-microchip-host.c +++ b/drivers/pci/controller/pcie-microchip-host.c @@ -167,12 +167,12 @@ #define EVENT_PCIE_DLUP_EXIT 2 #define EVENT_SEC_TX_RAM_SEC_ERR 3 #define EVENT_SEC_RX_RAM_SEC_ERR 4 -#define EVENT_SEC_AXI2PCIE_RAM_SEC_ERR 5 -#define EVENT_SEC_PCIE2AXI_RAM_SEC_ERR 6 +#define EVENT_SEC_PCIE2AXI_RAM_SEC_ERR 5 +#define EVENT_SEC_AXI2PCIE_RAM_SEC_ERR 6 #define EVENT_DED_TX_RAM_DED_ERR 7 #define EVENT_DED_RX_RAM_DED_ERR 8 -#define EVENT_DED_AXI2PCIE_RAM_DED_ERR 9 -#define EVENT_DED_PCIE2AXI_RAM_DED_ERR 10 +#define EVENT_DED_PCIE2AXI_RAM_DED_ERR 9 +#define EVENT_DED_AXI2PCIE_RAM_DED_ERR 10 #define EVENT_LOCAL_DMA_END_ENGINE_0 11 #define EVENT_LOCAL_DMA_END_ENGINE_1 12 #define EVENT_LOCAL_DMA_ERROR_ENGINE_0 13 From 73f357428a47f13892fc7895de73bd02b47f302f Mon Sep 17 00:00:00 2001 From: Daire McNamara Date: Fri, 28 Jul 2023 14:13:56 +0100 Subject: [PATCH 38/92] PCI: microchip: Enable building driver as a module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable building driver as a module. The expected use case is the driver is built as a module, is installed when needed, and cannot be removed once installed. The driver has .suppress_bind_attrs set to true. Link: https://lore.kernel.org/linux-pci/87y1wgbah8.wl-maz@kernel.org/ Suggested-by: Uwe Kleine-König Signed-off-by: Daire McNamara Acked-by: Conor Dooley --- drivers/pci/controller/Kconfig | 2 +- drivers/pci/controller/pcie-microchip-host.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 0859be86e7183..c0c3f28249907 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -216,7 +216,7 @@ config PCIE_MT7621 This selects a driver for the MediaTek MT7621 PCIe Controller. config PCIE_MICROCHIP_HOST - bool "Microchip AXI PCIe controller" + tristate "Microchip AXI PCIe controller" depends on PCI_MSI && OF select PCI_HOST_COMMON help diff --git a/drivers/pci/controller/pcie-microchip-host.c b/drivers/pci/controller/pcie-microchip-host.c index dd5245904c874..5c89caaab8c92 100644 --- a/drivers/pci/controller/pcie-microchip-host.c +++ b/drivers/pci/controller/pcie-microchip-host.c @@ -1135,5 +1135,6 @@ static struct platform_driver mc_pcie_driver = { }; builtin_platform_driver(mc_pcie_driver); +MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Microchip PCIe host controller driver"); MODULE_AUTHOR("Daire McNamara "); From 216d50b620e2905fc6039b93b6cec999fbee83f3 Mon Sep 17 00:00:00 2001 From: Daire McNamara Date: Fri, 28 Jul 2023 14:13:57 +0100 Subject: [PATCH 39/92] PCI: microchip: Align register, offset, and mask names with hw docs Minor re-organisation so that macros representing registers ascend in numerical order and use the same names as their hardware documentation. Removed registers not used by the driver. Signed-off-by: Daire McNamara Reviewed-by: Conor Dooley --- drivers/pci/controller/pcie-microchip-host.c | 122 +++++++++---------- 1 file changed, 60 insertions(+), 62 deletions(-) diff --git a/drivers/pci/controller/pcie-microchip-host.c b/drivers/pci/controller/pcie-microchip-host.c index 5c89caaab8c92..30153fd1a2b3e 100644 --- a/drivers/pci/controller/pcie-microchip-host.c +++ b/drivers/pci/controller/pcie-microchip-host.c @@ -30,66 +30,7 @@ #define MC_PCIE_BRIDGE_ADDR (MC_PCIE1_BRIDGE_ADDR) #define MC_PCIE_CTRL_ADDR (MC_PCIE1_CTRL_ADDR) -/* PCIe Controller Phy Regs */ -#define SEC_ERROR_CNT 0x20 -#define DED_ERROR_CNT 0x24 -#define SEC_ERROR_INT 0x28 -#define SEC_ERROR_INT_TX_RAM_SEC_ERR_INT GENMASK(3, 0) -#define SEC_ERROR_INT_RX_RAM_SEC_ERR_INT GENMASK(7, 4) -#define SEC_ERROR_INT_PCIE2AXI_RAM_SEC_ERR_INT GENMASK(11, 8) -#define SEC_ERROR_INT_AXI2PCIE_RAM_SEC_ERR_INT GENMASK(15, 12) -#define NUM_SEC_ERROR_INTS (4) -#define SEC_ERROR_INT_MASK 0x2c -#define DED_ERROR_INT 0x30 -#define DED_ERROR_INT_TX_RAM_DED_ERR_INT GENMASK(3, 0) -#define DED_ERROR_INT_RX_RAM_DED_ERR_INT GENMASK(7, 4) -#define DED_ERROR_INT_PCIE2AXI_RAM_DED_ERR_INT GENMASK(11, 8) -#define DED_ERROR_INT_AXI2PCIE_RAM_DED_ERR_INT GENMASK(15, 12) -#define NUM_DED_ERROR_INTS (4) -#define DED_ERROR_INT_MASK 0x34 -#define ECC_CONTROL 0x38 -#define ECC_CONTROL_TX_RAM_INJ_ERROR_0 BIT(0) -#define ECC_CONTROL_TX_RAM_INJ_ERROR_1 BIT(1) -#define ECC_CONTROL_TX_RAM_INJ_ERROR_2 BIT(2) -#define ECC_CONTROL_TX_RAM_INJ_ERROR_3 BIT(3) -#define ECC_CONTROL_RX_RAM_INJ_ERROR_0 BIT(4) -#define ECC_CONTROL_RX_RAM_INJ_ERROR_1 BIT(5) -#define ECC_CONTROL_RX_RAM_INJ_ERROR_2 BIT(6) -#define ECC_CONTROL_RX_RAM_INJ_ERROR_3 BIT(7) -#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_0 BIT(8) -#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_1 BIT(9) -#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_2 BIT(10) -#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_3 BIT(11) -#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_0 BIT(12) -#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_1 BIT(13) -#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_2 BIT(14) -#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_3 BIT(15) -#define ECC_CONTROL_TX_RAM_ECC_BYPASS BIT(24) -#define ECC_CONTROL_RX_RAM_ECC_BYPASS BIT(25) -#define ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS BIT(26) -#define ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS BIT(27) -#define LTSSM_STATE 0x5c -#define LTSSM_L0_STATE 0x10 -#define PCIE_EVENT_INT 0x14c -#define PCIE_EVENT_INT_L2_EXIT_INT BIT(0) -#define PCIE_EVENT_INT_HOTRST_EXIT_INT BIT(1) -#define PCIE_EVENT_INT_DLUP_EXIT_INT BIT(2) -#define PCIE_EVENT_INT_MASK GENMASK(2, 0) -#define PCIE_EVENT_INT_L2_EXIT_INT_MASK BIT(16) -#define PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK BIT(17) -#define PCIE_EVENT_INT_DLUP_EXIT_INT_MASK BIT(18) -#define PCIE_EVENT_INT_ENB_MASK GENMASK(18, 16) -#define PCIE_EVENT_INT_ENB_SHIFT 16 -#define NUM_PCIE_EVENTS (3) - /* PCIe Bridge Phy Regs */ -#define PCIE_PCI_IDS_DW1 0x9c - -/* PCIe Config space MSI capability structure */ -#define MC_MSI_CAP_CTRL_OFFSET 0xe0u -#define MC_MSI_MAX_Q_AVAIL (MC_NUM_MSI_IRQS_CODED << 1) -#define MC_MSI_Q_SIZE (MC_NUM_MSI_IRQS_CODED << 4) - #define IMASK_LOCAL 0x180 #define DMA_END_ENGINE_0_MASK 0x00000000u #define DMA_END_ENGINE_0_SHIFT 0 @@ -137,7 +78,8 @@ #define ISTATUS_LOCAL 0x184 #define IMASK_HOST 0x188 #define ISTATUS_HOST 0x18c -#define MSI_ADDR 0x190 +#define IMSI_ADDR 0x190 +#define MSI_ADDR 0x190 #define ISTATUS_MSI 0x194 /* PCIe Master table init defines */ @@ -162,6 +104,62 @@ #define ATR_ENTRY_SIZE 32 +/* PCIe Controller Phy Regs */ +#define SEC_ERROR_EVENT_CNT 0x20 +#define DED_ERROR_EVENT_CNT 0x24 +#define SEC_ERROR_INT 0x28 +#define SEC_ERROR_INT_TX_RAM_SEC_ERR_INT GENMASK(3, 0) +#define SEC_ERROR_INT_RX_RAM_SEC_ERR_INT GENMASK(7, 4) +#define SEC_ERROR_INT_PCIE2AXI_RAM_SEC_ERR_INT GENMASK(11, 8) +#define SEC_ERROR_INT_AXI2PCIE_RAM_SEC_ERR_INT GENMASK(15, 12) +#define NUM_SEC_ERROR_INTS (4) +#define SEC_ERROR_INT_MASK 0x2c +#define DED_ERROR_INT 0x30 +#define DED_ERROR_INT_TX_RAM_DED_ERR_INT GENMASK(3, 0) +#define DED_ERROR_INT_RX_RAM_DED_ERR_INT GENMASK(7, 4) +#define DED_ERROR_INT_PCIE2AXI_RAM_DED_ERR_INT GENMASK(11, 8) +#define DED_ERROR_INT_AXI2PCIE_RAM_DED_ERR_INT GENMASK(15, 12) +#define NUM_DED_ERROR_INTS (4) +#define DED_ERROR_INT_MASK 0x34 +#define ECC_CONTROL 0x38 +#define ECC_CONTROL_TX_RAM_INJ_ERROR_0 BIT(0) +#define ECC_CONTROL_TX_RAM_INJ_ERROR_1 BIT(1) +#define ECC_CONTROL_TX_RAM_INJ_ERROR_2 BIT(2) +#define ECC_CONTROL_TX_RAM_INJ_ERROR_3 BIT(3) +#define ECC_CONTROL_RX_RAM_INJ_ERROR_0 BIT(4) +#define ECC_CONTROL_RX_RAM_INJ_ERROR_1 BIT(5) +#define ECC_CONTROL_RX_RAM_INJ_ERROR_2 BIT(6) +#define ECC_CONTROL_RX_RAM_INJ_ERROR_3 BIT(7) +#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_0 BIT(8) +#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_1 BIT(9) +#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_2 BIT(10) +#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_3 BIT(11) +#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_0 BIT(12) +#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_1 BIT(13) +#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_2 BIT(14) +#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_3 BIT(15) +#define ECC_CONTROL_TX_RAM_ECC_BYPASS BIT(24) +#define ECC_CONTROL_RX_RAM_ECC_BYPASS BIT(25) +#define ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS BIT(26) +#define ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS BIT(27) +#define PCIE_EVENT_INT 0x14c +#define PCIE_EVENT_INT_L2_EXIT_INT BIT(0) +#define PCIE_EVENT_INT_HOTRST_EXIT_INT BIT(1) +#define PCIE_EVENT_INT_DLUP_EXIT_INT BIT(2) +#define PCIE_EVENT_INT_MASK GENMASK(2, 0) +#define PCIE_EVENT_INT_L2_EXIT_INT_MASK BIT(16) +#define PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK BIT(17) +#define PCIE_EVENT_INT_DLUP_EXIT_INT_MASK BIT(18) +#define PCIE_EVENT_INT_ENB_MASK GENMASK(18, 16) +#define PCIE_EVENT_INT_ENB_SHIFT 16 +#define NUM_PCIE_EVENTS (3) + +/* PCIe Config space MSI capability structure */ +#define MC_MSI_CAP_CTRL_OFFSET 0xe0u +#define MC_MSI_MAX_Q_AVAIL (MC_NUM_MSI_IRQS_CODED << 1) +#define MC_MSI_Q_SIZE (MC_NUM_MSI_IRQS_CODED << 4) + +/* Events */ #define EVENT_PCIE_L2_EXIT 0 #define EVENT_PCIE_HOTRST_EXIT 1 #define EVENT_PCIE_DLUP_EXIT 2 @@ -1086,7 +1084,7 @@ static int mc_platform_init(struct pci_config_window *cfg) SEC_ERROR_INT_AXI2PCIE_RAM_SEC_ERR_INT; writel_relaxed(val, ctrl_base_addr + SEC_ERROR_INT); writel_relaxed(0, ctrl_base_addr + SEC_ERROR_INT_MASK); - writel_relaxed(0, ctrl_base_addr + SEC_ERROR_CNT); + writel_relaxed(0, ctrl_base_addr + SEC_ERROR_EVENT_CNT); val = DED_ERROR_INT_TX_RAM_DED_ERR_INT | DED_ERROR_INT_RX_RAM_DED_ERR_INT | @@ -1094,7 +1092,7 @@ static int mc_platform_init(struct pci_config_window *cfg) DED_ERROR_INT_AXI2PCIE_RAM_DED_ERR_INT; writel_relaxed(val, ctrl_base_addr + DED_ERROR_INT); writel_relaxed(0, ctrl_base_addr + DED_ERROR_INT_MASK); - writel_relaxed(0, ctrl_base_addr + DED_ERROR_CNT); + writel_relaxed(0, ctrl_base_addr + DED_ERROR_EVENT_CNT); writel_relaxed(0, bridge_base_addr + IMASK_HOST); writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST); From 047b6dbb66b37db4c89cbebfd29a6bd0cc904505 Mon Sep 17 00:00:00 2001 From: Daire McNamara Date: Fri, 28 Jul 2023 14:13:58 +0100 Subject: [PATCH 40/92] PCI: microchip: Enable event handlers to access bridge and ctrl ptrs Minor re-organisation so that event handlers can access both a pointer to the bridge area of the PCIe Root Port and the ctrl area of the PCIe Root Port. Signed-off-by: Daire McNamara Reviewed-by: Conor Dooley --- drivers/pci/controller/pcie-microchip-host.c | 31 ++++++++++---------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/pci/controller/pcie-microchip-host.c b/drivers/pci/controller/pcie-microchip-host.c index 30153fd1a2b3e..a81e6d25e347e 100644 --- a/drivers/pci/controller/pcie-microchip-host.c +++ b/drivers/pci/controller/pcie-microchip-host.c @@ -654,9 +654,10 @@ static inline u32 reg_to_event(u32 reg, struct event_map field) return (reg & field.reg_mask) ? BIT(field.event_bit) : 0; } -static u32 pcie_events(void __iomem *addr) +static u32 pcie_events(struct mc_pcie *port) { - u32 reg = readl_relaxed(addr); + void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; + u32 reg = readl_relaxed(ctrl_base_addr + PCIE_EVENT_INT); u32 val = 0; int i; @@ -666,9 +667,10 @@ static u32 pcie_events(void __iomem *addr) return val; } -static u32 sec_errors(void __iomem *addr) +static u32 sec_errors(struct mc_pcie *port) { - u32 reg = readl_relaxed(addr); + void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; + u32 reg = readl_relaxed(ctrl_base_addr + SEC_ERROR_INT); u32 val = 0; int i; @@ -678,9 +680,10 @@ static u32 sec_errors(void __iomem *addr) return val; } -static u32 ded_errors(void __iomem *addr) +static u32 ded_errors(struct mc_pcie *port) { - u32 reg = readl_relaxed(addr); + void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; + u32 reg = readl_relaxed(ctrl_base_addr + DED_ERROR_INT); u32 val = 0; int i; @@ -690,9 +693,10 @@ static u32 ded_errors(void __iomem *addr) return val; } -static u32 local_events(void __iomem *addr) +static u32 local_events(struct mc_pcie *port) { - u32 reg = readl_relaxed(addr); + void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; + u32 reg = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); u32 val = 0; int i; @@ -704,15 +708,12 @@ static u32 local_events(void __iomem *addr) static u32 get_events(struct mc_pcie *port) { - void __iomem *bridge_base_addr = - port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; - void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; u32 events = 0; - events |= pcie_events(ctrl_base_addr + PCIE_EVENT_INT); - events |= sec_errors(ctrl_base_addr + SEC_ERROR_INT); - events |= ded_errors(ctrl_base_addr + DED_ERROR_INT); - events |= local_events(bridge_base_addr + ISTATUS_LOCAL); + events |= pcie_events(port); + events |= sec_errors(port); + events |= ded_errors(port); + events |= local_events(port); return events; } From 453badfc6dc9cea336d1cadfbe2118cb3531141b Mon Sep 17 00:00:00 2001 From: Daire McNamara Date: Fri, 28 Jul 2023 14:13:59 +0100 Subject: [PATCH 41/92] PCI: microchip: Clean up initialisation of interrupts Refactor interrupt handling in _init() function into disable_interrupts(), init_interrupts(), clear_sec_errors() and clear ded_errors(). It was unwieldy and prone to bugs. Then clearly disable interrupts as soon as possible and only enable interrupts after address translation is setup to prevent spurious axi2pcie and pcie2axi translation errors being reported Signed-off-by: Daire McNamara Reviewed-by: Conor Dooley --- drivers/pci/controller/pcie-microchip-host.c | 156 ++++++++++++------- 1 file changed, 100 insertions(+), 56 deletions(-) diff --git a/drivers/pci/controller/pcie-microchip-host.c b/drivers/pci/controller/pcie-microchip-host.c index a81e6d25e347e..7b212eea42d21 100644 --- a/drivers/pci/controller/pcie-microchip-host.c +++ b/drivers/pci/controller/pcie-microchip-host.c @@ -112,6 +112,7 @@ #define SEC_ERROR_INT_RX_RAM_SEC_ERR_INT GENMASK(7, 4) #define SEC_ERROR_INT_PCIE2AXI_RAM_SEC_ERR_INT GENMASK(11, 8) #define SEC_ERROR_INT_AXI2PCIE_RAM_SEC_ERR_INT GENMASK(15, 12) +#define SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT GENMASK(15, 0) #define NUM_SEC_ERROR_INTS (4) #define SEC_ERROR_INT_MASK 0x2c #define DED_ERROR_INT 0x30 @@ -119,6 +120,7 @@ #define DED_ERROR_INT_RX_RAM_DED_ERR_INT GENMASK(7, 4) #define DED_ERROR_INT_PCIE2AXI_RAM_DED_ERR_INT GENMASK(11, 8) #define DED_ERROR_INT_AXI2PCIE_RAM_DED_ERR_INT GENMASK(15, 12) +#define DED_ERROR_INT_ALL_RAM_DED_ERR_INT GENMASK(15, 0) #define NUM_DED_ERROR_INTS (4) #define DED_ERROR_INT_MASK 0x34 #define ECC_CONTROL 0x38 @@ -986,39 +988,73 @@ static int mc_pcie_setup_windows(struct platform_device *pdev, return 0; } -static int mc_platform_init(struct pci_config_window *cfg) +static inline void mc_clear_secs(struct mc_pcie *port) { - struct device *dev = cfg->parent; - struct platform_device *pdev = to_platform_device(dev); - struct mc_pcie *port; - void __iomem *bridge_base_addr; - void __iomem *ctrl_base_addr; - int ret; - int irq; - int i, intx_irq, msi_irq, event_irq; + void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; + + writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr + + SEC_ERROR_INT); + writel_relaxed(0, ctrl_base_addr + SEC_ERROR_EVENT_CNT); +} + +static inline void mc_clear_deds(struct mc_pcie *port) +{ + void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; + + writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr + + DED_ERROR_INT); + writel_relaxed(0, ctrl_base_addr + DED_ERROR_EVENT_CNT); +} + +static void mc_disable_interrupts(struct mc_pcie *port) +{ + void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; + void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; u32 val; - int err; - port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); - if (!port) - return -ENOMEM; - port->dev = dev; + /* Ensure ECC bypass is enabled */ + val = ECC_CONTROL_TX_RAM_ECC_BYPASS | + ECC_CONTROL_RX_RAM_ECC_BYPASS | + ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS | + ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS; + writel_relaxed(val, ctrl_base_addr + ECC_CONTROL); - ret = mc_pcie_init_clks(dev); - if (ret) { - dev_err(dev, "failed to get clock resources, error %d\n", ret); - return -ENODEV; - } + /* Disable SEC errors and clear any outstanding */ + writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr + + SEC_ERROR_INT_MASK); + mc_clear_secs(port); - port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1); - if (IS_ERR(port->axi_base_addr)) - return PTR_ERR(port->axi_base_addr); + /* Disable DED errors and clear any outstanding */ + writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr + + DED_ERROR_INT_MASK); + mc_clear_deds(port); - bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; - ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; + /* Disable local interrupts and clear any outstanding */ + writel_relaxed(0, bridge_base_addr + IMASK_LOCAL); + writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_LOCAL); + writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_MSI); + + /* Disable PCIe events and clear any outstanding */ + val = PCIE_EVENT_INT_L2_EXIT_INT | + PCIE_EVENT_INT_HOTRST_EXIT_INT | + PCIE_EVENT_INT_DLUP_EXIT_INT | + PCIE_EVENT_INT_L2_EXIT_INT_MASK | + PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK | + PCIE_EVENT_INT_DLUP_EXIT_INT_MASK; + writel_relaxed(val, ctrl_base_addr + PCIE_EVENT_INT); + + /* Disable host interrupts and clear any outstanding */ + writel_relaxed(0, bridge_base_addr + IMASK_HOST); + writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST); +} + +static int mc_init_interrupts(struct platform_device *pdev, struct mc_pcie *port) +{ + struct device *dev = &pdev->dev; + int irq; + int i, intx_irq, msi_irq, event_irq; + int ret; - port->msi.vector_phy = MSI_ADDR; - port->msi.num_vectors = MC_NUM_MSI_IRQS; ret = mc_pcie_init_irq_domains(port); if (ret) { dev_err(dev, "failed creating IRQ domains\n"); @@ -1036,11 +1072,11 @@ static int mc_platform_init(struct pci_config_window *cfg) return -ENXIO; } - err = devm_request_irq(dev, event_irq, mc_event_handler, + ret = devm_request_irq(dev, event_irq, mc_event_handler, 0, event_cause[i].sym, port); - if (err) { + if (ret) { dev_err(dev, "failed to request IRQ %d\n", event_irq); - return err; + return ret; } } @@ -1065,44 +1101,52 @@ static int mc_platform_init(struct pci_config_window *cfg) /* Plug the main event chained handler */ irq_set_chained_handler_and_data(irq, mc_handle_event, port); - /* Hardware doesn't setup MSI by default */ - mc_pcie_enable_msi(port, cfg->win); + return 0; +} - val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); - val |= PM_MSI_INT_INTX_MASK; - writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); +static int mc_platform_init(struct pci_config_window *cfg) +{ + struct device *dev = cfg->parent; + struct platform_device *pdev = to_platform_device(dev); + struct mc_pcie *port; + void __iomem *bridge_base_addr; + int ret; - writel_relaxed(val, ctrl_base_addr + ECC_CONTROL); + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + port->dev = dev; - val = PCIE_EVENT_INT_L2_EXIT_INT | - PCIE_EVENT_INT_HOTRST_EXIT_INT | - PCIE_EVENT_INT_DLUP_EXIT_INT; - writel_relaxed(val, ctrl_base_addr + PCIE_EVENT_INT); + ret = mc_pcie_init_clks(dev); + if (ret) { + dev_err(dev, "failed to get clock resources, error %d\n", ret); + return -ENODEV; + } - val = SEC_ERROR_INT_TX_RAM_SEC_ERR_INT | - SEC_ERROR_INT_RX_RAM_SEC_ERR_INT | - SEC_ERROR_INT_PCIE2AXI_RAM_SEC_ERR_INT | - SEC_ERROR_INT_AXI2PCIE_RAM_SEC_ERR_INT; - writel_relaxed(val, ctrl_base_addr + SEC_ERROR_INT); - writel_relaxed(0, ctrl_base_addr + SEC_ERROR_INT_MASK); - writel_relaxed(0, ctrl_base_addr + SEC_ERROR_EVENT_CNT); + port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(port->axi_base_addr)) + return PTR_ERR(port->axi_base_addr); - val = DED_ERROR_INT_TX_RAM_DED_ERR_INT | - DED_ERROR_INT_RX_RAM_DED_ERR_INT | - DED_ERROR_INT_PCIE2AXI_RAM_DED_ERR_INT | - DED_ERROR_INT_AXI2PCIE_RAM_DED_ERR_INT; - writel_relaxed(val, ctrl_base_addr + DED_ERROR_INT); - writel_relaxed(0, ctrl_base_addr + DED_ERROR_INT_MASK); - writel_relaxed(0, ctrl_base_addr + DED_ERROR_EVENT_CNT); + mc_disable_interrupts(port); - writel_relaxed(0, bridge_base_addr + IMASK_HOST); - writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST); + bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; + + port->msi.vector_phy = MSI_ADDR; + port->msi.num_vectors = MC_NUM_MSI_IRQS; + + /* Hardware doesn't setup MSI by default */ + mc_pcie_enable_msi(port, cfg->win); /* Configure Address Translation Table 0 for PCIe config space */ mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start & 0xffffffff, cfg->res.start, resource_size(&cfg->res)); - return mc_pcie_setup_windows(pdev, port); + ret = mc_pcie_setup_windows(pdev, port); + if (ret) + return ret; + + /* Address translation is up; safe to enable interrupts */ + return mc_init_interrupts(pdev, port); } static const struct pci_ecam_ops mc_ecam_ops = { From b16800705706cac071b0adad114023cee6fcd859 Mon Sep 17 00:00:00 2001 From: Daire McNamara Date: Fri, 28 Jul 2023 14:14:00 +0100 Subject: [PATCH 42/92] PCI: microchip: Gather MSI information from hardware config registers The PCIe Root Complex on PolarFire SoC is configured at bitstream creation time using Libero. Key MSI-related parameters include the number of MSIs (1/2/4/8/16/32) and the MSI address. In the device driver, extract this information from hw registers at init time, and use it to configure MSI system, including configuring MSI capability structure correctly in configuration space. Signed-off-by: Daire McNamara Reviewed-by: Conor Dooley --- drivers/pci/controller/pcie-microchip-host.c | 65 +++++++++++--------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/drivers/pci/controller/pcie-microchip-host.c b/drivers/pci/controller/pcie-microchip-host.c index 7b212eea42d21..ca13fd56a0d99 100644 --- a/drivers/pci/controller/pcie-microchip-host.c +++ b/drivers/pci/controller/pcie-microchip-host.c @@ -7,6 +7,7 @@ * Author: Daire McNamara */ +#include #include #include #include @@ -20,8 +21,7 @@ #include "../pci.h" /* Number of MSI IRQs */ -#define MC_NUM_MSI_IRQS 32 -#define MC_NUM_MSI_IRQS_CODED 5 +#define MC_MAX_NUM_MSI_IRQS 32 /* PCIe Bridge Phy and Controller Phy offsets */ #define MC_PCIE1_BRIDGE_ADDR 0x00008000u @@ -31,6 +31,11 @@ #define MC_PCIE_CTRL_ADDR (MC_PCIE1_CTRL_ADDR) /* PCIe Bridge Phy Regs */ +#define PCIE_PCI_IRQ_DW0 0xa8 +#define MSIX_CAP_MASK BIT(31) +#define NUM_MSI_MSGS_MASK GENMASK(6, 4) +#define NUM_MSI_MSGS_SHIFT 4 + #define IMASK_LOCAL 0x180 #define DMA_END_ENGINE_0_MASK 0x00000000u #define DMA_END_ENGINE_0_SHIFT 0 @@ -79,7 +84,6 @@ #define IMASK_HOST 0x188 #define ISTATUS_HOST 0x18c #define IMSI_ADDR 0x190 -#define MSI_ADDR 0x190 #define ISTATUS_MSI 0x194 /* PCIe Master table init defines */ @@ -158,8 +162,6 @@ /* PCIe Config space MSI capability structure */ #define MC_MSI_CAP_CTRL_OFFSET 0xe0u -#define MC_MSI_MAX_Q_AVAIL (MC_NUM_MSI_IRQS_CODED << 1) -#define MC_MSI_Q_SIZE (MC_NUM_MSI_IRQS_CODED << 4) /* Events */ #define EVENT_PCIE_L2_EXIT 0 @@ -259,7 +261,7 @@ struct mc_msi { struct irq_domain *dev_domain; u32 num_vectors; u64 vector_phy; - DECLARE_BITMAP(used, MC_NUM_MSI_IRQS); + DECLARE_BITMAP(used, MC_MAX_NUM_MSI_IRQS); }; struct mc_pcie { @@ -382,25 +384,27 @@ static struct { static char poss_clks[][5] = { "fic0", "fic1", "fic2", "fic3" }; -static void mc_pcie_enable_msi(struct mc_pcie *port, void __iomem *base) +static void mc_pcie_enable_msi(struct mc_pcie *port, void __iomem *ecam) { struct mc_msi *msi = &port->msi; - u32 cap_offset = MC_MSI_CAP_CTRL_OFFSET; - u16 msg_ctrl = readw_relaxed(base + cap_offset + PCI_MSI_FLAGS); + u16 reg; + u8 queue_size; - msg_ctrl |= PCI_MSI_FLAGS_ENABLE; - msg_ctrl &= ~PCI_MSI_FLAGS_QMASK; - msg_ctrl |= MC_MSI_MAX_Q_AVAIL; - msg_ctrl &= ~PCI_MSI_FLAGS_QSIZE; - msg_ctrl |= MC_MSI_Q_SIZE; - msg_ctrl |= PCI_MSI_FLAGS_64BIT; + /* Fixup MSI enable flag */ + reg = readw_relaxed(ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS); + reg |= PCI_MSI_FLAGS_ENABLE; + writew_relaxed(reg, ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS); - writew_relaxed(msg_ctrl, base + cap_offset + PCI_MSI_FLAGS); + /* Fixup PCI MSI queue flags */ + queue_size = FIELD_GET(PCI_MSI_FLAGS_QMASK, reg); + reg |= FIELD_PREP(PCI_MSI_FLAGS_QSIZE, queue_size); + writew_relaxed(reg, ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS); + /* Fixup MSI addr fields */ writel_relaxed(lower_32_bits(msi->vector_phy), - base + cap_offset + PCI_MSI_ADDRESS_LO); + ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_LO); writel_relaxed(upper_32_bits(msi->vector_phy), - base + cap_offset + PCI_MSI_ADDRESS_HI); + ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_HI); } static void mc_handle_msi(struct irq_desc *desc) @@ -473,10 +477,7 @@ static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, { struct mc_pcie *port = domain->host_data; struct mc_msi *msi = &port->msi; - void __iomem *bridge_base_addr = - port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; unsigned long bit; - u32 val; mutex_lock(&msi->lock); bit = find_first_zero_bit(msi->used, msi->num_vectors); @@ -490,11 +491,6 @@ static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, irq_domain_set_info(domain, virq, bit, &mc_msi_bottom_irq_chip, domain->host_data, handle_edge_irq, NULL, NULL); - /* Enable MSI interrupts */ - val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); - val |= PM_MSI_INT_MSI_MASK; - writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); - mutex_unlock(&msi->lock); return 0; @@ -1111,6 +1107,7 @@ static int mc_platform_init(struct pci_config_window *cfg) struct mc_pcie *port; void __iomem *bridge_base_addr; int ret; + u32 val; port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); if (!port) @@ -1131,12 +1128,24 @@ static int mc_platform_init(struct pci_config_window *cfg) bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; - port->msi.vector_phy = MSI_ADDR; - port->msi.num_vectors = MC_NUM_MSI_IRQS; + /* Allow enabling MSI by disabling MSI-X */ + val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); + val &= ~MSIX_CAP_MASK; + writel(val, bridge_base_addr + PCIE_PCI_IRQ_DW0); /* Hardware doesn't setup MSI by default */ mc_pcie_enable_msi(port, cfg->win); + /* Pick num vectors from bitfile programmed onto FPGA fabric */ + val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); + val &= NUM_MSI_MSGS_MASK; + val >>= NUM_MSI_MSGS_SHIFT; + + port->msi.num_vectors = 1 << val; + + /* Pick vector address from design */ + port->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR); + /* Configure Address Translation Table 0 for PCIe config space */ mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start & 0xffffffff, cfg->res.start, resource_size(&cfg->res)); From 214fe2d4f483fe8fd1a60e226c1492ca76398f15 Mon Sep 17 00:00:00 2001 From: Daire McNamara Date: Fri, 28 Jul 2023 14:14:01 +0100 Subject: [PATCH 43/92] PCI: microchip: Re-partition code between probe() and init() Continuing to use pci_host_common_probe() for the PCIe Root Complex on PolarFire SoC was leading to an extremely large _init() function and some unnatural code flow. Re-partition so some tasks are done in a _probe() routine, which calls pci_host_common_probe() and then use a much smaller _init() function, mainly to enable interrupts after address translation tables are set up. Signed-off-by: Daire McNamara Reviewed-by: Conor Dooley --- drivers/pci/controller/pcie-microchip-host.c | 58 +++++++++++++------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/drivers/pci/controller/pcie-microchip-host.c b/drivers/pci/controller/pcie-microchip-host.c index ca13fd56a0d99..252aff180ca24 100644 --- a/drivers/pci/controller/pcie-microchip-host.c +++ b/drivers/pci/controller/pcie-microchip-host.c @@ -384,6 +384,8 @@ static struct { static char poss_clks[][5] = { "fic0", "fic1", "fic2", "fic3" }; +static struct mc_pcie *port; + static void mc_pcie_enable_msi(struct mc_pcie *port, void __iomem *ecam) { struct mc_msi *msi = &port->msi; @@ -1104,7 +1106,34 @@ static int mc_platform_init(struct pci_config_window *cfg) { struct device *dev = cfg->parent; struct platform_device *pdev = to_platform_device(dev); - struct mc_pcie *port; + void __iomem *bridge_base_addr = + port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; + int ret; + + /* Configure address translation table 0 for PCIe config space */ + mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start, + cfg->res.start, + resource_size(&cfg->res)); + + /* Need some fixups in config space */ + mc_pcie_enable_msi(port, cfg->win); + + /* Configure non-config space outbound ranges */ + ret = mc_pcie_setup_windows(pdev, port); + if (ret) + return ret; + + /* Address translation is up; safe to enable interrupts */ + ret = mc_init_interrupts(pdev, port); + if (ret) + return ret; + + return 0; +} + +static int mc_host_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; void __iomem *bridge_base_addr; int ret; u32 val; @@ -1112,13 +1141,8 @@ static int mc_platform_init(struct pci_config_window *cfg) port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); if (!port) return -ENOMEM; - port->dev = dev; - ret = mc_pcie_init_clks(dev); - if (ret) { - dev_err(dev, "failed to get clock resources, error %d\n", ret); - return -ENODEV; - } + port->dev = dev; port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(port->axi_base_addr)) @@ -1133,9 +1157,6 @@ static int mc_platform_init(struct pci_config_window *cfg) val &= ~MSIX_CAP_MASK; writel(val, bridge_base_addr + PCIE_PCI_IRQ_DW0); - /* Hardware doesn't setup MSI by default */ - mc_pcie_enable_msi(port, cfg->win); - /* Pick num vectors from bitfile programmed onto FPGA fabric */ val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); val &= NUM_MSI_MSGS_MASK; @@ -1146,16 +1167,13 @@ static int mc_platform_init(struct pci_config_window *cfg) /* Pick vector address from design */ port->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR); - /* Configure Address Translation Table 0 for PCIe config space */ - mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start & 0xffffffff, - cfg->res.start, resource_size(&cfg->res)); - - ret = mc_pcie_setup_windows(pdev, port); - if (ret) - return ret; + ret = mc_pcie_init_clks(dev); + if (ret) { + dev_err(dev, "failed to get clock resources, error %d\n", ret); + return -ENODEV; + } - /* Address translation is up; safe to enable interrupts */ - return mc_init_interrupts(pdev, port); + return pci_host_common_probe(pdev); } static const struct pci_ecam_ops mc_ecam_ops = { @@ -1178,7 +1196,7 @@ static const struct of_device_id mc_pcie_of_match[] = { MODULE_DEVICE_TABLE(of, mc_pcie_of_match); static struct platform_driver mc_pcie_driver = { - .probe = pci_host_common_probe, + .probe = mc_host_probe, .driver = { .name = "microchip-pcie", .of_match_table = mc_pcie_of_match, From 8a054fbd700d75224148d4e9000e6b00e88207a5 Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Thu, 7 Sep 2023 17:10:48 +0800 Subject: [PATCH 44/92] dt-bindings: PCI: Add PLDA XpressRICH PCIe host common properties Add PLDA XpressRICH PCIe host common properties dt-binding doc. Microchip PolarFire PCIe host using PLDA IP. Move common properties from Microchip PolarFire PCIe host to PLDA files. Signed-off-by: Minda Chen Reviewed-by: Hal Feng Reviewed-by: Conor Dooley Reviewed-by: Rob Herring --- .../bindings/pci/microchip,pcie-host.yaml | 55 +------------- .../pci/plda,xpressrich3-axi-common.yaml | 75 +++++++++++++++++++ 2 files changed, 76 insertions(+), 54 deletions(-) create mode 100644 Documentation/devicetree/bindings/pci/plda,xpressrich3-axi-common.yaml diff --git a/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml b/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml index f7a3c26363556..7c2d51221f657 100644 --- a/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml +++ b/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml @@ -10,21 +10,13 @@ maintainers: - Daire McNamara allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: plda,xpressrich3-axi-common.yaml# - $ref: /schemas/interrupt-controller/msi-controller.yaml# properties: compatible: const: microchip,pcie-host-1.0 # PolarFire - reg: - maxItems: 2 - - reg-names: - items: - - const: cfg - - const: apb - clocks: description: Fabric Interface Controllers, FICs, are the interface between the FPGA @@ -52,18 +44,6 @@ properties: items: pattern: '^fic[0-3]$' - interrupts: - minItems: 1 - items: - - description: PCIe host controller - - description: builtin MSI controller - - interrupt-names: - minItems: 1 - items: - - const: pcie - - const: msi - ranges: maxItems: 1 @@ -71,39 +51,6 @@ properties: minItems: 1 maxItems: 6 - msi-controller: - description: Identifies the node as an MSI controller. - - msi-parent: - description: MSI controller the device is capable of using. - - interrupt-controller: - type: object - properties: - '#address-cells': - const: 0 - - '#interrupt-cells': - const: 1 - - interrupt-controller: true - - required: - - '#address-cells' - - '#interrupt-cells' - - interrupt-controller - - additionalProperties: false - -required: - - reg - - reg-names - - "#interrupt-cells" - - interrupts - - interrupt-map-mask - - interrupt-map - - msi-controller - unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/pci/plda,xpressrich3-axi-common.yaml b/Documentation/devicetree/bindings/pci/plda,xpressrich3-axi-common.yaml new file mode 100644 index 0000000000000..31bb17b11e585 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/plda,xpressrich3-axi-common.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/plda,xpressrich3-axi-common.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: PLDA XpressRICH PCIe host common properties + +maintainers: + - Daire McNamara + - Kevin Xie + +description: + Generic PLDA XpressRICH PCIe host common properties. + +allOf: + - $ref: /schemas/pci/pci-bus.yaml# + +properties: + reg: + maxItems: 2 + + reg-names: + items: + - const: cfg + - const: apb + + interrupts: + minItems: 1 + items: + - description: PCIe host controller + - description: builtin MSI controller + + interrupt-names: + minItems: 1 + items: + - const: pcie + - const: msi + + msi-controller: + description: Identifies the node as an MSI controller. + + msi-parent: + description: MSI controller the device is capable of using. + + interrupt-controller: + type: object + properties: + '#address-cells': + const: 0 + + '#interrupt-cells': + const: 1 + + interrupt-controller: true + + required: + - '#address-cells' + - '#interrupt-cells' + - interrupt-controller + + additionalProperties: false + +required: + - reg + - reg-names + - interrupts + - msi-controller + - "#interrupt-cells" + - interrupt-map-mask + - interrupt-map + +additionalProperties: true + +... From 528969a72eaedd6f7a7e87462294ed162a9343fb Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Thu, 7 Sep 2023 17:10:49 +0800 Subject: [PATCH 45/92] PCI: microchip: Move pcie-microchip-host.c to plda directory For Microchip Polarfire PCIe host is PLDA XpressRich IP, move to plda directory. Prepare for refactor the codes. Signed-off-by: Minda Chen Reviewed-by: Conor Dooley --- drivers/pci/controller/Kconfig | 9 +-------- drivers/pci/controller/Makefile | 2 +- drivers/pci/controller/plda/Kconfig | 14 ++++++++++++++ drivers/pci/controller/plda/Makefile | 2 ++ .../controller/{ => plda}/pcie-microchip-host.c | 2 +- 5 files changed, 19 insertions(+), 10 deletions(-) create mode 100644 drivers/pci/controller/plda/Kconfig create mode 100644 drivers/pci/controller/plda/Makefile rename drivers/pci/controller/{ => plda}/pcie-microchip-host.c (99%) diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index c0c3f28249907..80e23013c229a 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -215,14 +215,6 @@ config PCIE_MT7621 help This selects a driver for the MediaTek MT7621 PCIe Controller. -config PCIE_MICROCHIP_HOST - tristate "Microchip AXI PCIe controller" - depends on PCI_MSI && OF - select PCI_HOST_COMMON - help - Say Y here if you want kernel to support the Microchip AXI PCIe - Host Bridge driver. - config PCI_HYPERV_INTERFACE tristate "Microsoft Hyper-V PCI Interface" depends on ((X86 && X86_64) || ARM64) && HYPERV && PCI_MSI @@ -345,4 +337,5 @@ config PCIE_XILINX_CPM source "drivers/pci/controller/cadence/Kconfig" source "drivers/pci/controller/dwc/Kconfig" source "drivers/pci/controller/mobiveil/Kconfig" +source "drivers/pci/controller/plda/Kconfig" endmenu diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile index 37c8663de7fe1..93236dc97b215 100644 --- a/drivers/pci/controller/Makefile +++ b/drivers/pci/controller/Makefile @@ -32,7 +32,6 @@ obj-$(CONFIG_PCIE_ROCKCHIP_EP) += pcie-rockchip-ep.o obj-$(CONFIG_PCIE_ROCKCHIP_HOST) += pcie-rockchip-host.o obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o obj-$(CONFIG_PCIE_MEDIATEK_GEN3) += pcie-mediatek-gen3.o -obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o obj-$(CONFIG_VMD) += vmd.o obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o @@ -43,6 +42,7 @@ obj-$(CONFIG_PCIE_MT7621) += pcie-mt7621.o # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW obj-y += dwc/ obj-y += mobiveil/ +obj-y += plda/ # The following drivers are for devices that use the generic ACPI diff --git a/drivers/pci/controller/plda/Kconfig b/drivers/pci/controller/plda/Kconfig new file mode 100644 index 0000000000000..5cb3be4fc98cc --- /dev/null +++ b/drivers/pci/controller/plda/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 + +menu "PLDA-based PCIe controllers" + depends on PCI + +config PCIE_MICROCHIP_HOST + tristate "Microchip AXI PCIe controller" + depends on PCI_MSI && OF + select PCI_HOST_COMMON + help + Say Y here if you want kernel to support the Microchip AXI PCIe + Host Bridge driver. + +endmenu diff --git a/drivers/pci/controller/plda/Makefile b/drivers/pci/controller/plda/Makefile new file mode 100644 index 0000000000000..e1a265cbf91c2 --- /dev/null +++ b/drivers/pci/controller/plda/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o diff --git a/drivers/pci/controller/pcie-microchip-host.c b/drivers/pci/controller/plda/pcie-microchip-host.c similarity index 99% rename from drivers/pci/controller/pcie-microchip-host.c rename to drivers/pci/controller/plda/pcie-microchip-host.c index 252aff180ca24..a953ba1c9b0f9 100644 --- a/drivers/pci/controller/pcie-microchip-host.c +++ b/drivers/pci/controller/plda/pcie-microchip-host.c @@ -18,7 +18,7 @@ #include #include -#include "../pci.h" +#include "../../pci.h" /* Number of MSI IRQs */ #define MC_MAX_NUM_MSI_IRQS 32 From 172448ab20e689bcf8e2166f82fe6e6432c063af Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Thu, 7 Sep 2023 17:10:50 +0800 Subject: [PATCH 46/92] PCI: microchip: Move PLDA IP register macros to pcie-plda.h Move PLDA PCIe host controller IP registers macros to pcie-plda.h, Including bridge and configuration space registers. Signed-off-by: Minda Chen Reviewed-by: Conor Dooley --- .../pci/controller/plda/pcie-microchip-host.c | 108 +++--------------- drivers/pci/controller/plda/pcie-plda.h | 102 +++++++++++++++++ 2 files changed, 118 insertions(+), 92 deletions(-) create mode 100644 drivers/pci/controller/plda/pcie-plda.h diff --git a/drivers/pci/controller/plda/pcie-microchip-host.c b/drivers/pci/controller/plda/pcie-microchip-host.c index a953ba1c9b0f9..465215c5effa3 100644 --- a/drivers/pci/controller/plda/pcie-microchip-host.c +++ b/drivers/pci/controller/plda/pcie-microchip-host.c @@ -19,6 +19,7 @@ #include #include "../../pci.h" +#include "pcie-plda.h" /* Number of MSI IRQs */ #define MC_MAX_NUM_MSI_IRQS 32 @@ -30,84 +31,6 @@ #define MC_PCIE_BRIDGE_ADDR (MC_PCIE1_BRIDGE_ADDR) #define MC_PCIE_CTRL_ADDR (MC_PCIE1_CTRL_ADDR) -/* PCIe Bridge Phy Regs */ -#define PCIE_PCI_IRQ_DW0 0xa8 -#define MSIX_CAP_MASK BIT(31) -#define NUM_MSI_MSGS_MASK GENMASK(6, 4) -#define NUM_MSI_MSGS_SHIFT 4 - -#define IMASK_LOCAL 0x180 -#define DMA_END_ENGINE_0_MASK 0x00000000u -#define DMA_END_ENGINE_0_SHIFT 0 -#define DMA_END_ENGINE_1_MASK 0x00000000u -#define DMA_END_ENGINE_1_SHIFT 1 -#define DMA_ERROR_ENGINE_0_MASK 0x00000100u -#define DMA_ERROR_ENGINE_0_SHIFT 8 -#define DMA_ERROR_ENGINE_1_MASK 0x00000200u -#define DMA_ERROR_ENGINE_1_SHIFT 9 -#define A_ATR_EVT_POST_ERR_MASK 0x00010000u -#define A_ATR_EVT_POST_ERR_SHIFT 16 -#define A_ATR_EVT_FETCH_ERR_MASK 0x00020000u -#define A_ATR_EVT_FETCH_ERR_SHIFT 17 -#define A_ATR_EVT_DISCARD_ERR_MASK 0x00040000u -#define A_ATR_EVT_DISCARD_ERR_SHIFT 18 -#define A_ATR_EVT_DOORBELL_MASK 0x00000000u -#define A_ATR_EVT_DOORBELL_SHIFT 19 -#define P_ATR_EVT_POST_ERR_MASK 0x00100000u -#define P_ATR_EVT_POST_ERR_SHIFT 20 -#define P_ATR_EVT_FETCH_ERR_MASK 0x00200000u -#define P_ATR_EVT_FETCH_ERR_SHIFT 21 -#define P_ATR_EVT_DISCARD_ERR_MASK 0x00400000u -#define P_ATR_EVT_DISCARD_ERR_SHIFT 22 -#define P_ATR_EVT_DOORBELL_MASK 0x00000000u -#define P_ATR_EVT_DOORBELL_SHIFT 23 -#define PM_MSI_INT_INTA_MASK 0x01000000u -#define PM_MSI_INT_INTA_SHIFT 24 -#define PM_MSI_INT_INTB_MASK 0x02000000u -#define PM_MSI_INT_INTB_SHIFT 25 -#define PM_MSI_INT_INTC_MASK 0x04000000u -#define PM_MSI_INT_INTC_SHIFT 26 -#define PM_MSI_INT_INTD_MASK 0x08000000u -#define PM_MSI_INT_INTD_SHIFT 27 -#define PM_MSI_INT_INTX_MASK 0x0f000000u -#define PM_MSI_INT_INTX_SHIFT 24 -#define PM_MSI_INT_MSI_MASK 0x10000000u -#define PM_MSI_INT_MSI_SHIFT 28 -#define PM_MSI_INT_AER_EVT_MASK 0x20000000u -#define PM_MSI_INT_AER_EVT_SHIFT 29 -#define PM_MSI_INT_EVENTS_MASK 0x40000000u -#define PM_MSI_INT_EVENTS_SHIFT 30 -#define PM_MSI_INT_SYS_ERR_MASK 0x80000000u -#define PM_MSI_INT_SYS_ERR_SHIFT 31 -#define NUM_LOCAL_EVENTS 15 -#define ISTATUS_LOCAL 0x184 -#define IMASK_HOST 0x188 -#define ISTATUS_HOST 0x18c -#define IMSI_ADDR 0x190 -#define ISTATUS_MSI 0x194 - -/* PCIe Master table init defines */ -#define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600u -#define ATR0_PCIE_ATR_SIZE 0x25 -#define ATR0_PCIE_ATR_SIZE_SHIFT 1 -#define ATR0_PCIE_WIN0_SRC_ADDR 0x604u -#define ATR0_PCIE_WIN0_TRSL_ADDR_LSB 0x608u -#define ATR0_PCIE_WIN0_TRSL_ADDR_UDW 0x60cu -#define ATR0_PCIE_WIN0_TRSL_PARAM 0x610u - -/* PCIe AXI slave table init defines */ -#define ATR0_AXI4_SLV0_SRCADDR_PARAM 0x800u -#define ATR_SIZE_SHIFT 1 -#define ATR_IMPL_ENABLE 1 -#define ATR0_AXI4_SLV0_SRC_ADDR 0x804u -#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB 0x808u -#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW 0x80cu -#define ATR0_AXI4_SLV0_TRSL_PARAM 0x810u -#define PCIE_TX_RX_INTERFACE 0x00000000u -#define PCIE_CONFIG_INTERFACE 0x00000001u - -#define ATR_ENTRY_SIZE 32 - /* PCIe Controller Phy Regs */ #define SEC_ERROR_EVENT_CNT 0x20 #define DED_ERROR_EVENT_CNT 0x24 @@ -179,20 +102,21 @@ #define EVENT_LOCAL_DMA_END_ENGINE_1 12 #define EVENT_LOCAL_DMA_ERROR_ENGINE_0 13 #define EVENT_LOCAL_DMA_ERROR_ENGINE_1 14 -#define EVENT_LOCAL_A_ATR_EVT_POST_ERR 15 -#define EVENT_LOCAL_A_ATR_EVT_FETCH_ERR 16 -#define EVENT_LOCAL_A_ATR_EVT_DISCARD_ERR 17 -#define EVENT_LOCAL_A_ATR_EVT_DOORBELL 18 -#define EVENT_LOCAL_P_ATR_EVT_POST_ERR 19 -#define EVENT_LOCAL_P_ATR_EVT_FETCH_ERR 20 -#define EVENT_LOCAL_P_ATR_EVT_DISCARD_ERR 21 -#define EVENT_LOCAL_P_ATR_EVT_DOORBELL 22 -#define EVENT_LOCAL_PM_MSI_INT_INTX 23 -#define EVENT_LOCAL_PM_MSI_INT_MSI 24 -#define EVENT_LOCAL_PM_MSI_INT_AER_EVT 25 -#define EVENT_LOCAL_PM_MSI_INT_EVENTS 26 -#define EVENT_LOCAL_PM_MSI_INT_SYS_ERR 27 -#define NUM_EVENTS 28 +#define NUM_MC_EVENTS 15 +#define EVENT_LOCAL_A_ATR_EVT_POST_ERR (NUM_MC_EVENTS + EVENT_A_ATR_EVT_POST_ERR) +#define EVENT_LOCAL_A_ATR_EVT_FETCH_ERR (NUM_MC_EVENTS + EVENT_A_ATR_EVT_FETCH_ERR) +#define EVENT_LOCAL_A_ATR_EVT_DISCARD_ERR (NUM_MC_EVENTS + EVENT_A_ATR_EVT_DISCARD_ERR) +#define EVENT_LOCAL_A_ATR_EVT_DOORBELL (NUM_MC_EVENTS + EVENT_A_ATR_EVT_DOORBELL) +#define EVENT_LOCAL_P_ATR_EVT_POST_ERR (NUM_MC_EVENTS + EVENT_P_ATR_EVT_POST_ERR) +#define EVENT_LOCAL_P_ATR_EVT_FETCH_ERR (NUM_MC_EVENTS + EVENT_P_ATR_EVT_FETCH_ERR) +#define EVENT_LOCAL_P_ATR_EVT_DISCARD_ERR (NUM_MC_EVENTS + EVENT_P_ATR_EVT_DISCARD_ERR) +#define EVENT_LOCAL_P_ATR_EVT_DOORBELL (NUM_MC_EVENTS + EVENT_P_ATR_EVT_DOORBELL) +#define EVENT_LOCAL_PM_MSI_INT_INTX (NUM_MC_EVENTS + EVENT_PM_MSI_INT_INTX) +#define EVENT_LOCAL_PM_MSI_INT_MSI (NUM_MC_EVENTS + EVENT_PM_MSI_INT_MSI) +#define EVENT_LOCAL_PM_MSI_INT_AER_EVT (NUM_MC_EVENTS + EVENT_PM_MSI_INT_AER_EVT) +#define EVENT_LOCAL_PM_MSI_INT_EVENTS (NUM_MC_EVENTS + EVENT_PM_MSI_INT_EVENTS) +#define EVENT_LOCAL_PM_MSI_INT_SYS_ERR (NUM_MC_EVENTS + EVENT_PM_MSI_INT_SYS_ERR) +#define NUM_EVENTS (NUM_MC_EVENTS + NUM_PLDA_EVENTS) #define PCIE_EVENT_CAUSE(x, s) \ [EVENT_PCIE_ ## x] = { __stringify(x), s } diff --git a/drivers/pci/controller/plda/pcie-plda.h b/drivers/pci/controller/plda/pcie-plda.h new file mode 100644 index 0000000000000..727fc54312c9c --- /dev/null +++ b/drivers/pci/controller/plda/pcie-plda.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * PLDA PCIe host controller driver + */ + +#ifndef _PCIE_PLDA_H +#define _PCIE_PLDA_H + +/* PCIe Bridge Phy Regs */ +#define PCIE_PCI_IRQ_DW0 0xa8 +#define MSIX_CAP_MASK BIT(31) +#define NUM_MSI_MSGS_MASK GENMASK(6, 4) +#define NUM_MSI_MSGS_SHIFT 4 + +#define IMASK_LOCAL 0x180 +#define DMA_END_ENGINE_0_MASK 0x00000000u +#define DMA_END_ENGINE_0_SHIFT 0 +#define DMA_END_ENGINE_1_MASK 0x00000000u +#define DMA_END_ENGINE_1_SHIFT 1 +#define DMA_ERROR_ENGINE_0_MASK 0x00000100u +#define DMA_ERROR_ENGINE_0_SHIFT 8 +#define DMA_ERROR_ENGINE_1_MASK 0x00000200u +#define DMA_ERROR_ENGINE_1_SHIFT 9 +#define A_ATR_EVT_POST_ERR_MASK 0x00010000u +#define A_ATR_EVT_POST_ERR_SHIFT 16 +#define A_ATR_EVT_FETCH_ERR_MASK 0x00020000u +#define A_ATR_EVT_FETCH_ERR_SHIFT 17 +#define A_ATR_EVT_DISCARD_ERR_MASK 0x00040000u +#define A_ATR_EVT_DISCARD_ERR_SHIFT 18 +#define A_ATR_EVT_DOORBELL_MASK 0x00000000u +#define A_ATR_EVT_DOORBELL_SHIFT 19 +#define P_ATR_EVT_POST_ERR_MASK 0x00100000u +#define P_ATR_EVT_POST_ERR_SHIFT 20 +#define P_ATR_EVT_FETCH_ERR_MASK 0x00200000u +#define P_ATR_EVT_FETCH_ERR_SHIFT 21 +#define P_ATR_EVT_DISCARD_ERR_MASK 0x00400000u +#define P_ATR_EVT_DISCARD_ERR_SHIFT 22 +#define P_ATR_EVT_DOORBELL_MASK 0x00000000u +#define P_ATR_EVT_DOORBELL_SHIFT 23 +#define PM_MSI_INT_INTA_MASK 0x01000000u +#define PM_MSI_INT_INTA_SHIFT 24 +#define PM_MSI_INT_INTB_MASK 0x02000000u +#define PM_MSI_INT_INTB_SHIFT 25 +#define PM_MSI_INT_INTC_MASK 0x04000000u +#define PM_MSI_INT_INTC_SHIFT 26 +#define PM_MSI_INT_INTD_MASK 0x08000000u +#define PM_MSI_INT_INTD_SHIFT 27 +#define PM_MSI_INT_INTX_MASK 0x0f000000u +#define PM_MSI_INT_INTX_SHIFT 24 +#define PM_MSI_INT_MSI_MASK 0x10000000u +#define PM_MSI_INT_MSI_SHIFT 28 +#define PM_MSI_INT_AER_EVT_MASK 0x20000000u +#define PM_MSI_INT_AER_EVT_SHIFT 29 +#define PM_MSI_INT_EVENTS_MASK 0x40000000u +#define PM_MSI_INT_EVENTS_SHIFT 30 +#define PM_MSI_INT_SYS_ERR_MASK 0x80000000u +#define PM_MSI_INT_SYS_ERR_SHIFT 31 +#define NUM_LOCAL_EVENTS 15 +#define ISTATUS_LOCAL 0x184 +#define IMASK_HOST 0x188 +#define ISTATUS_HOST 0x18c +#define IMSI_ADDR 0x190 +#define ISTATUS_MSI 0x194 + +/* PCIe Master table init defines */ +#define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600u +#define ATR0_PCIE_ATR_SIZE 0x25 +#define ATR0_PCIE_ATR_SIZE_SHIFT 1 +#define ATR0_PCIE_WIN0_SRC_ADDR 0x604u +#define ATR0_PCIE_WIN0_TRSL_ADDR_LSB 0x608u +#define ATR0_PCIE_WIN0_TRSL_ADDR_UDW 0x60cu +#define ATR0_PCIE_WIN0_TRSL_PARAM 0x610u + +/* PCIe AXI slave table init defines */ +#define ATR0_AXI4_SLV0_SRCADDR_PARAM 0x800u +#define ATR_SIZE_SHIFT 1 +#define ATR_IMPL_ENABLE 1 +#define ATR0_AXI4_SLV0_SRC_ADDR 0x804u +#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB 0x808u +#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW 0x80cu +#define ATR0_AXI4_SLV0_TRSL_PARAM 0x810u +#define PCIE_TX_RX_INTERFACE 0x00000000u +#define PCIE_CONFIG_INTERFACE 0x00000001u + +#define ATR_ENTRY_SIZE 32 + +#define EVENT_A_ATR_EVT_POST_ERR 0 +#define EVENT_A_ATR_EVT_FETCH_ERR 1 +#define EVENT_A_ATR_EVT_DISCARD_ERR 2 +#define EVENT_A_ATR_EVT_DOORBELL 3 +#define EVENT_P_ATR_EVT_POST_ERR 4 +#define EVENT_P_ATR_EVT_FETCH_ERR 5 +#define EVENT_P_ATR_EVT_DISCARD_ERR 6 +#define EVENT_P_ATR_EVT_DOORBELL 7 +#define EVENT_PM_MSI_INT_INTX 8 +#define EVENT_PM_MSI_INT_MSI 9 +#define EVENT_PM_MSI_INT_AER_EVT 10 +#define EVENT_PM_MSI_INT_EVENTS 11 +#define EVENT_PM_MSI_INT_SYS_ERR 12 +#define NUM_PLDA_EVENTS 13 + +#endif From 82e30575acef74ffbd20f7012d0ea55c2f19188a Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Thu, 7 Sep 2023 17:10:51 +0800 Subject: [PATCH 47/92] PCI: microchip: Rename data structure and functions Rename mc_* to plda_* for common data structures, setup and IRQ functions. The modification includes: - Add related data structures of PCIe host instance. mc_pcie --> plda_pcie_rp (Get most of data members) mc_msi --> plda_msi - function rename list: mc_pcie_setup_window() --> plda_pcie_setup_window() mc_pcie_setup_windows() --> plda_pcie_setup_iomems() mc_allocate_msi_domains() --> plda_allocate_msi_domains() MSI interrupts related functions and IRQ domain (primary function is mc_handle_msi()): mc_handle_msi() --> plda_handle_msi() INTx interrupts related functions and IRQ domain (primary function is mc_handle_intx()): mc_handle_intx() --> plda_handle_intx() Signed-off-by: Minda Chen Reviewed-by: Conor Dooley --- .../pci/controller/plda/pcie-microchip-host.c | 216 ++++++++---------- drivers/pci/controller/plda/pcie-plda.h | 20 ++ 2 files changed, 120 insertions(+), 116 deletions(-) diff --git a/drivers/pci/controller/plda/pcie-microchip-host.c b/drivers/pci/controller/plda/pcie-microchip-host.c index 465215c5effa3..928f125cb6957 100644 --- a/drivers/pci/controller/plda/pcie-microchip-host.c +++ b/drivers/pci/controller/plda/pcie-microchip-host.c @@ -21,9 +21,6 @@ #include "../../pci.h" #include "pcie-plda.h" -/* Number of MSI IRQs */ -#define MC_MAX_NUM_MSI_IRQS 32 - /* PCIe Bridge Phy and Controller Phy offsets */ #define MC_PCIE1_BRIDGE_ADDR 0x00008000u #define MC_PCIE1_CTRL_ADDR 0x0000a000u @@ -179,22 +176,9 @@ struct event_map { u32 event_bit; }; -struct mc_msi { - struct mutex lock; /* Protect used bitmap */ - struct irq_domain *msi_domain; - struct irq_domain *dev_domain; - u32 num_vectors; - u64 vector_phy; - DECLARE_BITMAP(used, MC_MAX_NUM_MSI_IRQS); -}; - struct mc_pcie { + struct plda_pcie_rp plda; void __iomem *axi_base_addr; - struct device *dev; - struct irq_domain *intx_domain; - struct irq_domain *event_domain; - raw_spinlock_t lock; - struct mc_msi msi; }; struct cause { @@ -312,7 +296,7 @@ static struct mc_pcie *port; static void mc_pcie_enable_msi(struct mc_pcie *port, void __iomem *ecam) { - struct mc_msi *msi = &port->msi; + struct plda_msi *msi = &port->plda.msi; u16 reg; u8 queue_size; @@ -333,14 +317,13 @@ static void mc_pcie_enable_msi(struct mc_pcie *port, void __iomem *ecam) ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_HI); } -static void mc_handle_msi(struct irq_desc *desc) +static void plda_handle_msi(struct irq_desc *desc) { - struct mc_pcie *port = irq_desc_get_handler_data(desc); + struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); struct device *dev = port->dev; - struct mc_msi *msi = &port->msi; - void __iomem *bridge_base_addr = - port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; + struct plda_msi *msi = &port->msi; + void __iomem *bridge_base_addr = port->bridge_addr; unsigned long status; u32 bit; int ret; @@ -362,19 +345,18 @@ static void mc_handle_msi(struct irq_desc *desc) chained_irq_exit(chip, desc); } -static void mc_msi_bottom_irq_ack(struct irq_data *data) +static void plda_msi_bottom_irq_ack(struct irq_data *data) { - struct mc_pcie *port = irq_data_get_irq_chip_data(data); - void __iomem *bridge_base_addr = - port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; u32 bitpos = data->hwirq; writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI); } -static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +static void plda_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) { - struct mc_pcie *port = irq_data_get_irq_chip_data(data); + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); phys_addr_t addr = port->msi.vector_phy; msg->address_lo = lower_32_bits(addr); @@ -385,24 +367,24 @@ static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) (int)data->hwirq, msg->address_hi, msg->address_lo); } -static int mc_msi_set_affinity(struct irq_data *irq_data, - const struct cpumask *mask, bool force) +static int plda_msi_set_affinity(struct irq_data *irq_data, + const struct cpumask *mask, bool force) { return -EINVAL; } -static struct irq_chip mc_msi_bottom_irq_chip = { - .name = "Microchip MSI", - .irq_ack = mc_msi_bottom_irq_ack, - .irq_compose_msi_msg = mc_compose_msi_msg, - .irq_set_affinity = mc_msi_set_affinity, +static struct irq_chip plda_msi_bottom_irq_chip = { + .name = "PLDA MSI", + .irq_ack = plda_msi_bottom_irq_ack, + .irq_compose_msi_msg = plda_compose_msi_msg, + .irq_set_affinity = plda_msi_set_affinity, }; -static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs, void *args) +static int plda_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *args) { - struct mc_pcie *port = domain->host_data; - struct mc_msi *msi = &port->msi; + struct plda_pcie_rp *port = domain->host_data; + struct plda_msi *msi = &port->msi; unsigned long bit; mutex_lock(&msi->lock); @@ -414,7 +396,7 @@ static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, set_bit(bit, msi->used); - irq_domain_set_info(domain, virq, bit, &mc_msi_bottom_irq_chip, + irq_domain_set_info(domain, virq, bit, &plda_msi_bottom_irq_chip, domain->host_data, handle_edge_irq, NULL, NULL); mutex_unlock(&msi->lock); @@ -422,12 +404,12 @@ static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, return 0; } -static void mc_irq_msi_domain_free(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs) +static void plda_irq_msi_domain_free(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) { struct irq_data *d = irq_domain_get_irq_data(domain, virq); - struct mc_pcie *port = irq_data_get_irq_chip_data(d); - struct mc_msi *msi = &port->msi; + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(d); + struct plda_msi *msi = &port->msi; mutex_lock(&msi->lock); @@ -440,28 +422,28 @@ static void mc_irq_msi_domain_free(struct irq_domain *domain, unsigned int virq, } static const struct irq_domain_ops msi_domain_ops = { - .alloc = mc_irq_msi_domain_alloc, - .free = mc_irq_msi_domain_free, + .alloc = plda_irq_msi_domain_alloc, + .free = plda_irq_msi_domain_free, }; -static struct irq_chip mc_msi_irq_chip = { - .name = "Microchip PCIe MSI", +static struct irq_chip plda_msi_irq_chip = { + .name = "PLDA PCIe MSI", .irq_ack = irq_chip_ack_parent, .irq_mask = pci_msi_mask_irq, .irq_unmask = pci_msi_unmask_irq, }; -static struct msi_domain_info mc_msi_domain_info = { +static struct msi_domain_info plda_msi_domain_info = { .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | MSI_FLAG_PCI_MSIX), - .chip = &mc_msi_irq_chip, + .chip = &plda_msi_irq_chip, }; -static int mc_allocate_msi_domains(struct mc_pcie *port) +static int plda_allocate_msi_domains(struct plda_pcie_rp *port) { struct device *dev = port->dev; struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); - struct mc_msi *msi = &port->msi; + struct plda_msi *msi = &port->msi; mutex_init(&port->msi.lock); @@ -472,7 +454,7 @@ static int mc_allocate_msi_domains(struct mc_pcie *port) return -ENOMEM; } - msi->msi_domain = pci_msi_create_irq_domain(fwnode, &mc_msi_domain_info, + msi->msi_domain = pci_msi_create_irq_domain(fwnode, &plda_msi_domain_info, msi->dev_domain); if (!msi->msi_domain) { dev_err(dev, "failed to create MSI domain\n"); @@ -483,13 +465,12 @@ static int mc_allocate_msi_domains(struct mc_pcie *port) return 0; } -static void mc_handle_intx(struct irq_desc *desc) +static void plda_handle_intx(struct irq_desc *desc) { - struct mc_pcie *port = irq_desc_get_handler_data(desc); + struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); struct device *dev = port->dev; - void __iomem *bridge_base_addr = - port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; + void __iomem *bridge_base_addr = port->bridge_addr; unsigned long status; u32 bit; int ret; @@ -511,21 +492,19 @@ static void mc_handle_intx(struct irq_desc *desc) chained_irq_exit(chip, desc); } -static void mc_ack_intx_irq(struct irq_data *data) +static void plda_ack_intx_irq(struct irq_data *data) { - struct mc_pcie *port = irq_data_get_irq_chip_data(data); - void __iomem *bridge_base_addr = - port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL); } -static void mc_mask_intx_irq(struct irq_data *data) +static void plda_mask_intx_irq(struct irq_data *data) { - struct mc_pcie *port = irq_data_get_irq_chip_data(data); - void __iomem *bridge_base_addr = - port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; unsigned long flags; u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); u32 val; @@ -537,11 +516,10 @@ static void mc_mask_intx_irq(struct irq_data *data) raw_spin_unlock_irqrestore(&port->lock, flags); } -static void mc_unmask_intx_irq(struct irq_data *data) +static void plda_unmask_intx_irq(struct irq_data *data) { - struct mc_pcie *port = irq_data_get_irq_chip_data(data); - void __iomem *bridge_base_addr = - port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; unsigned long flags; u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); u32 val; @@ -553,24 +531,24 @@ static void mc_unmask_intx_irq(struct irq_data *data) raw_spin_unlock_irqrestore(&port->lock, flags); } -static struct irq_chip mc_intx_irq_chip = { - .name = "Microchip PCIe INTx", - .irq_ack = mc_ack_intx_irq, - .irq_mask = mc_mask_intx_irq, - .irq_unmask = mc_unmask_intx_irq, +static struct irq_chip plda_intx_irq_chip = { + .name = "PLDA PCIe INTx", + .irq_ack = plda_ack_intx_irq, + .irq_mask = plda_mask_intx_irq, + .irq_unmask = plda_unmask_intx_irq, }; -static int mc_pcie_intx_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) +static int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) { - irq_set_chip_and_handler(irq, &mc_intx_irq_chip, handle_level_irq); + irq_set_chip_and_handler(irq, &plda_intx_irq_chip, handle_level_irq); irq_set_chip_data(irq, domain->host_data); return 0; } static const struct irq_domain_ops intx_domain_ops = { - .map = mc_pcie_intx_map, + .map = plda_pcie_intx_map, }; static inline u32 reg_to_event(u32 reg, struct event_map field) @@ -630,21 +608,22 @@ static u32 local_events(struct mc_pcie *port) return val; } -static u32 get_events(struct mc_pcie *port) +static u32 get_events(struct plda_pcie_rp *port) { + struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda); u32 events = 0; - events |= pcie_events(port); - events |= sec_errors(port); - events |= ded_errors(port); - events |= local_events(port); + events |= pcie_events(mc_port); + events |= sec_errors(mc_port); + events |= ded_errors(mc_port); + events |= local_events(mc_port); return events; } static irqreturn_t mc_event_handler(int irq, void *dev_id) { - struct mc_pcie *port = dev_id; + struct plda_pcie_rp *port = dev_id; struct device *dev = port->dev; struct irq_data *data; @@ -660,7 +639,7 @@ static irqreturn_t mc_event_handler(int irq, void *dev_id) static void mc_handle_event(struct irq_desc *desc) { - struct mc_pcie *port = irq_desc_get_handler_data(desc); + struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); unsigned long events; u32 bit; struct irq_chip *chip = irq_desc_get_chip(desc); @@ -677,12 +656,13 @@ static void mc_handle_event(struct irq_desc *desc) static void mc_ack_event_irq(struct irq_data *data) { - struct mc_pcie *port = irq_data_get_irq_chip_data(data); + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda); u32 event = data->hwirq; void __iomem *addr; u32 mask; - addr = port->axi_base_addr + event_descs[event].base + + addr = mc_port->axi_base_addr + event_descs[event].base + event_descs[event].offset; mask = event_descs[event].mask; mask |= event_descs[event].enb_mask; @@ -692,13 +672,14 @@ static void mc_ack_event_irq(struct irq_data *data) static void mc_mask_event_irq(struct irq_data *data) { - struct mc_pcie *port = irq_data_get_irq_chip_data(data); + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda); u32 event = data->hwirq; void __iomem *addr; u32 mask; u32 val; - addr = port->axi_base_addr + event_descs[event].base + + addr = mc_port->axi_base_addr + event_descs[event].base + event_descs[event].mask_offset; mask = event_descs[event].mask; if (event_descs[event].enb_mask) { @@ -722,13 +703,14 @@ static void mc_mask_event_irq(struct irq_data *data) static void mc_unmask_event_irq(struct irq_data *data) { - struct mc_pcie *port = irq_data_get_irq_chip_data(data); + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda); u32 event = data->hwirq; void __iomem *addr; u32 mask; u32 val; - addr = port->axi_base_addr + event_descs[event].base + + addr = mc_port->axi_base_addr + event_descs[event].base + event_descs[event].mask_offset; mask = event_descs[event].mask; @@ -810,7 +792,7 @@ static int mc_pcie_init_clks(struct device *dev) return 0; } -static int mc_pcie_init_irq_domains(struct mc_pcie *port) +static int mc_pcie_init_irq_domains(struct plda_pcie_rp *port) { struct device *dev = port->dev; struct device_node *node = dev->of_node; @@ -846,12 +828,12 @@ static int mc_pcie_init_irq_domains(struct mc_pcie *port) of_node_put(pcie_intc_node); raw_spin_lock_init(&port->lock); - return mc_allocate_msi_domains(port); + return plda_allocate_msi_domains(port); } -static void mc_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, - phys_addr_t axi_addr, phys_addr_t pci_addr, - size_t size) +static void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, + phys_addr_t axi_addr, phys_addr_t pci_addr, + size_t size) { u32 atr_sz = ilog2(size) - 1; u32 val; @@ -887,12 +869,10 @@ static void mc_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR); } -static int mc_pcie_setup_windows(struct platform_device *pdev, - struct mc_pcie *port) +static int plda_pcie_setup_iomems(struct pci_host_bridge *bridge, + struct plda_pcie_rp *port) { - void __iomem *bridge_base_addr = - port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; - struct pci_host_bridge *bridge = platform_get_drvdata(pdev); + void __iomem *bridge_base_addr = port->bridge_addr; struct resource_entry *entry; u64 pci_addr; u32 index = 1; @@ -900,9 +880,9 @@ static int mc_pcie_setup_windows(struct platform_device *pdev, resource_list_for_each_entry(entry, &bridge->windows) { if (resource_type(entry->res) == IORESOURCE_MEM) { pci_addr = entry->res->start - entry->offset; - mc_pcie_setup_window(bridge_base_addr, index, - entry->res->start, pci_addr, - resource_size(entry->res)); + plda_pcie_setup_window(bridge_base_addr, index, + entry->res->start, pci_addr, + resource_size(entry->res)); index++; } } @@ -970,7 +950,7 @@ static void mc_disable_interrupts(struct mc_pcie *port) writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST); } -static int mc_init_interrupts(struct platform_device *pdev, struct mc_pcie *port) +static int mc_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp *port) { struct device *dev = &pdev->dev; int irq; @@ -1010,7 +990,7 @@ static int mc_init_interrupts(struct platform_device *pdev, struct mc_pcie *port } /* Plug the INTx chained handler */ - irq_set_chained_handler_and_data(intx_irq, mc_handle_intx, port); + irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port); msi_irq = irq_create_mapping(port->event_domain, EVENT_LOCAL_PM_MSI_INT_MSI); @@ -1018,7 +998,7 @@ static int mc_init_interrupts(struct platform_device *pdev, struct mc_pcie *port return -ENXIO; /* Plug the MSI chained handler */ - irq_set_chained_handler_and_data(msi_irq, mc_handle_msi, port); + irq_set_chained_handler_and_data(msi_irq, plda_handle_msi, port); /* Plug the main event chained handler */ irq_set_chained_handler_and_data(irq, mc_handle_event, port); @@ -1032,23 +1012,24 @@ static int mc_platform_init(struct pci_config_window *cfg) struct platform_device *pdev = to_platform_device(dev); void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; + struct pci_host_bridge *bridge = platform_get_drvdata(pdev); int ret; /* Configure address translation table 0 for PCIe config space */ - mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start, - cfg->res.start, - resource_size(&cfg->res)); + plda_pcie_setup_window(bridge_base_addr, 0, cfg->res.start, + cfg->res.start, + resource_size(&cfg->res)); /* Need some fixups in config space */ mc_pcie_enable_msi(port, cfg->win); /* Configure non-config space outbound ranges */ - ret = mc_pcie_setup_windows(pdev, port); + ret = plda_pcie_setup_iomems(bridge, &port->plda); if (ret) return ret; /* Address translation is up; safe to enable interrupts */ - ret = mc_init_interrupts(pdev, port); + ret = mc_init_interrupts(pdev, &port->plda); if (ret) return ret; @@ -1059,6 +1040,7 @@ static int mc_host_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; void __iomem *bridge_base_addr; + struct plda_pcie_rp *plda; int ret; u32 val; @@ -1066,7 +1048,8 @@ static int mc_host_probe(struct platform_device *pdev) if (!port) return -ENOMEM; - port->dev = dev; + plda = &port->plda; + plda->dev = dev; port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(port->axi_base_addr)) @@ -1075,6 +1058,7 @@ static int mc_host_probe(struct platform_device *pdev) mc_disable_interrupts(port); bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; + plda->bridge_addr = bridge_base_addr; /* Allow enabling MSI by disabling MSI-X */ val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); @@ -1086,10 +1070,10 @@ static int mc_host_probe(struct platform_device *pdev) val &= NUM_MSI_MSGS_MASK; val >>= NUM_MSI_MSGS_SHIFT; - port->msi.num_vectors = 1 << val; + plda->msi.num_vectors = 1 << val; /* Pick vector address from design */ - port->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR); + plda->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR); ret = mc_pcie_init_clks(dev); if (ret) { diff --git a/drivers/pci/controller/plda/pcie-plda.h b/drivers/pci/controller/plda/pcie-plda.h index 727fc54312c9c..d04a571404b92 100644 --- a/drivers/pci/controller/plda/pcie-plda.h +++ b/drivers/pci/controller/plda/pcie-plda.h @@ -6,6 +6,9 @@ #ifndef _PCIE_PLDA_H #define _PCIE_PLDA_H +/* Number of MSI IRQs */ +#define PLDA_MAX_NUM_MSI_IRQS 32 + /* PCIe Bridge Phy Regs */ #define PCIE_PCI_IRQ_DW0 0xa8 #define MSIX_CAP_MASK BIT(31) @@ -99,4 +102,21 @@ #define EVENT_PM_MSI_INT_SYS_ERR 12 #define NUM_PLDA_EVENTS 13 +struct plda_msi { + struct mutex lock; /* Protect used bitmap */ + struct irq_domain *msi_domain; + struct irq_domain *dev_domain; + u32 num_vectors; + u64 vector_phy; + DECLARE_BITMAP(used, PLDA_MAX_NUM_MSI_IRQS); +}; + +struct plda_pcie_rp { + struct device *dev; + struct irq_domain *intx_domain; + struct irq_domain *event_domain; + raw_spinlock_t lock; + struct plda_msi msi; + void __iomem *bridge_addr; +}; #endif From fc472b96d8040bef6e03af60261617e2f1d62867 Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Thu, 7 Sep 2023 17:10:52 +0800 Subject: [PATCH 48/92] PCI: plda: Move the common functions to pcie-plda-host.c Move MSI IRQ, INTx IRQ and setup functions to common pcie-plda-host.c. Signed-off-by: Minda Chen Reviewed-by: Conor Dooley --- drivers/pci/controller/plda/Kconfig | 4 + drivers/pci/controller/plda/Makefile | 1 + .../pci/controller/plda/pcie-microchip-host.c | 289 ---------------- drivers/pci/controller/plda/pcie-plda-host.c | 312 ++++++++++++++++++ drivers/pci/controller/plda/pcie-plda.h | 11 + 5 files changed, 328 insertions(+), 289 deletions(-) create mode 100644 drivers/pci/controller/plda/pcie-plda-host.c diff --git a/drivers/pci/controller/plda/Kconfig b/drivers/pci/controller/plda/Kconfig index 5cb3be4fc98cc..e54a82ee94f59 100644 --- a/drivers/pci/controller/plda/Kconfig +++ b/drivers/pci/controller/plda/Kconfig @@ -3,10 +3,14 @@ menu "PLDA-based PCIe controllers" depends on PCI +config PCIE_PLDA_HOST + bool + config PCIE_MICROCHIP_HOST tristate "Microchip AXI PCIe controller" depends on PCI_MSI && OF select PCI_HOST_COMMON + select PCIE_PLDA_HOST help Say Y here if you want kernel to support the Microchip AXI PCIe Host Bridge driver. diff --git a/drivers/pci/controller/plda/Makefile b/drivers/pci/controller/plda/Makefile index e1a265cbf91c2..4340ab007f440 100644 --- a/drivers/pci/controller/plda/Makefile +++ b/drivers/pci/controller/plda/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PCIE_PLDA_HOST) += pcie-plda-host.o obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o diff --git a/drivers/pci/controller/plda/pcie-microchip-host.c b/drivers/pci/controller/plda/pcie-microchip-host.c index 928f125cb6957..b1d5b5b3cee50 100644 --- a/drivers/pci/controller/plda/pcie-microchip-host.c +++ b/drivers/pci/controller/plda/pcie-microchip-host.c @@ -317,236 +317,6 @@ static void mc_pcie_enable_msi(struct mc_pcie *port, void __iomem *ecam) ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_HI); } -static void plda_handle_msi(struct irq_desc *desc) -{ - struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); - struct irq_chip *chip = irq_desc_get_chip(desc); - struct device *dev = port->dev; - struct plda_msi *msi = &port->msi; - void __iomem *bridge_base_addr = port->bridge_addr; - unsigned long status; - u32 bit; - int ret; - - chained_irq_enter(chip, desc); - - status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); - if (status & PM_MSI_INT_MSI_MASK) { - writel_relaxed(status & PM_MSI_INT_MSI_MASK, bridge_base_addr + ISTATUS_LOCAL); - status = readl_relaxed(bridge_base_addr + ISTATUS_MSI); - for_each_set_bit(bit, &status, msi->num_vectors) { - ret = generic_handle_domain_irq(msi->dev_domain, bit); - if (ret) - dev_err_ratelimited(dev, "bad MSI IRQ %d\n", - bit); - } - } - - chained_irq_exit(chip, desc); -} - -static void plda_msi_bottom_irq_ack(struct irq_data *data) -{ - struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); - void __iomem *bridge_base_addr = port->bridge_addr; - u32 bitpos = data->hwirq; - - writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI); -} - -static void plda_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) -{ - struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); - phys_addr_t addr = port->msi.vector_phy; - - msg->address_lo = lower_32_bits(addr); - msg->address_hi = upper_32_bits(addr); - msg->data = data->hwirq; - - dev_dbg(port->dev, "msi#%x address_hi %#x address_lo %#x\n", - (int)data->hwirq, msg->address_hi, msg->address_lo); -} - -static int plda_msi_set_affinity(struct irq_data *irq_data, - const struct cpumask *mask, bool force) -{ - return -EINVAL; -} - -static struct irq_chip plda_msi_bottom_irq_chip = { - .name = "PLDA MSI", - .irq_ack = plda_msi_bottom_irq_ack, - .irq_compose_msi_msg = plda_compose_msi_msg, - .irq_set_affinity = plda_msi_set_affinity, -}; - -static int plda_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs, void *args) -{ - struct plda_pcie_rp *port = domain->host_data; - struct plda_msi *msi = &port->msi; - unsigned long bit; - - mutex_lock(&msi->lock); - bit = find_first_zero_bit(msi->used, msi->num_vectors); - if (bit >= msi->num_vectors) { - mutex_unlock(&msi->lock); - return -ENOSPC; - } - - set_bit(bit, msi->used); - - irq_domain_set_info(domain, virq, bit, &plda_msi_bottom_irq_chip, - domain->host_data, handle_edge_irq, NULL, NULL); - - mutex_unlock(&msi->lock); - - return 0; -} - -static void plda_irq_msi_domain_free(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs) -{ - struct irq_data *d = irq_domain_get_irq_data(domain, virq); - struct plda_pcie_rp *port = irq_data_get_irq_chip_data(d); - struct plda_msi *msi = &port->msi; - - mutex_lock(&msi->lock); - - if (test_bit(d->hwirq, msi->used)) - __clear_bit(d->hwirq, msi->used); - else - dev_err(port->dev, "trying to free unused MSI%lu\n", d->hwirq); - - mutex_unlock(&msi->lock); -} - -static const struct irq_domain_ops msi_domain_ops = { - .alloc = plda_irq_msi_domain_alloc, - .free = plda_irq_msi_domain_free, -}; - -static struct irq_chip plda_msi_irq_chip = { - .name = "PLDA PCIe MSI", - .irq_ack = irq_chip_ack_parent, - .irq_mask = pci_msi_mask_irq, - .irq_unmask = pci_msi_unmask_irq, -}; - -static struct msi_domain_info plda_msi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_PCI_MSIX), - .chip = &plda_msi_irq_chip, -}; - -static int plda_allocate_msi_domains(struct plda_pcie_rp *port) -{ - struct device *dev = port->dev; - struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); - struct plda_msi *msi = &port->msi; - - mutex_init(&port->msi.lock); - - msi->dev_domain = irq_domain_add_linear(NULL, msi->num_vectors, - &msi_domain_ops, port); - if (!msi->dev_domain) { - dev_err(dev, "failed to create IRQ domain\n"); - return -ENOMEM; - } - - msi->msi_domain = pci_msi_create_irq_domain(fwnode, &plda_msi_domain_info, - msi->dev_domain); - if (!msi->msi_domain) { - dev_err(dev, "failed to create MSI domain\n"); - irq_domain_remove(msi->dev_domain); - return -ENOMEM; - } - - return 0; -} - -static void plda_handle_intx(struct irq_desc *desc) -{ - struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); - struct irq_chip *chip = irq_desc_get_chip(desc); - struct device *dev = port->dev; - void __iomem *bridge_base_addr = port->bridge_addr; - unsigned long status; - u32 bit; - int ret; - - chained_irq_enter(chip, desc); - - status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); - if (status & PM_MSI_INT_INTX_MASK) { - status &= PM_MSI_INT_INTX_MASK; - status >>= PM_MSI_INT_INTX_SHIFT; - for_each_set_bit(bit, &status, PCI_NUM_INTX) { - ret = generic_handle_domain_irq(port->intx_domain, bit); - if (ret) - dev_err_ratelimited(dev, "bad INTx IRQ %d\n", - bit); - } - } - - chained_irq_exit(chip, desc); -} - -static void plda_ack_intx_irq(struct irq_data *data) -{ - struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); - void __iomem *bridge_base_addr = port->bridge_addr; - u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); - - writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL); -} - -static void plda_mask_intx_irq(struct irq_data *data) -{ - struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); - void __iomem *bridge_base_addr = port->bridge_addr; - unsigned long flags; - u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); - u32 val; - - raw_spin_lock_irqsave(&port->lock, flags); - val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); - val &= ~mask; - writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); - raw_spin_unlock_irqrestore(&port->lock, flags); -} - -static void plda_unmask_intx_irq(struct irq_data *data) -{ - struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); - void __iomem *bridge_base_addr = port->bridge_addr; - unsigned long flags; - u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); - u32 val; - - raw_spin_lock_irqsave(&port->lock, flags); - val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); - val |= mask; - writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); - raw_spin_unlock_irqrestore(&port->lock, flags); -} - -static struct irq_chip plda_intx_irq_chip = { - .name = "PLDA PCIe INTx", - .irq_ack = plda_ack_intx_irq, - .irq_mask = plda_mask_intx_irq, - .irq_unmask = plda_unmask_intx_irq, -}; - -static int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) -{ - irq_set_chip_and_handler(irq, &plda_intx_irq_chip, handle_level_irq); - irq_set_chip_data(irq, domain->host_data); - - return 0; -} - static const struct irq_domain_ops intx_domain_ops = { .map = plda_pcie_intx_map, }; @@ -831,65 +601,6 @@ static int mc_pcie_init_irq_domains(struct plda_pcie_rp *port) return plda_allocate_msi_domains(port); } -static void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, - phys_addr_t axi_addr, phys_addr_t pci_addr, - size_t size) -{ - u32 atr_sz = ilog2(size) - 1; - u32 val; - - if (index == 0) - val = PCIE_CONFIG_INTERFACE; - else - val = PCIE_TX_RX_INTERFACE; - - writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + - ATR0_AXI4_SLV0_TRSL_PARAM); - - val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) | - ATR_IMPL_ENABLE; - writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + - ATR0_AXI4_SLV0_SRCADDR_PARAM); - - val = upper_32_bits(axi_addr); - writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + - ATR0_AXI4_SLV0_SRC_ADDR); - - val = lower_32_bits(pci_addr); - writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + - ATR0_AXI4_SLV0_TRSL_ADDR_LSB); - - val = upper_32_bits(pci_addr); - writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + - ATR0_AXI4_SLV0_TRSL_ADDR_UDW); - - val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); - val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT); - writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); - writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR); -} - -static int plda_pcie_setup_iomems(struct pci_host_bridge *bridge, - struct plda_pcie_rp *port) -{ - void __iomem *bridge_base_addr = port->bridge_addr; - struct resource_entry *entry; - u64 pci_addr; - u32 index = 1; - - resource_list_for_each_entry(entry, &bridge->windows) { - if (resource_type(entry->res) == IORESOURCE_MEM) { - pci_addr = entry->res->start - entry->offset; - plda_pcie_setup_window(bridge_base_addr, index, - entry->res->start, pci_addr, - resource_size(entry->res)); - index++; - } - } - - return 0; -} - static inline void mc_clear_secs(struct mc_pcie *port) { void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; diff --git a/drivers/pci/controller/plda/pcie-plda-host.c b/drivers/pci/controller/plda/pcie-plda-host.c new file mode 100644 index 0000000000000..ce02cfe6b5fa5 --- /dev/null +++ b/drivers/pci/controller/plda/pcie-plda-host.c @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PLDA PCIe XpressRich host controller driver + * + * Copyright (C) 2023 Microchip Co. Ltd + * StarFive Co. Ltd. + * + * Author: Daire McNamara + * Author: Minda Chen + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-plda.h" + +void plda_handle_msi(struct irq_desc *desc) +{ + struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + struct device *dev = port->dev; + struct plda_msi *msi = &port->msi; + void __iomem *bridge_base_addr = port->bridge_addr; + unsigned long status; + u32 bit; + int ret; + + chained_irq_enter(chip, desc); + + status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); + if (status & PM_MSI_INT_MSI_MASK) { + writel_relaxed(status & PM_MSI_INT_MSI_MASK, bridge_base_addr + ISTATUS_LOCAL); + status = readl_relaxed(bridge_base_addr + ISTATUS_MSI); + for_each_set_bit(bit, &status, msi->num_vectors) { + ret = generic_handle_domain_irq(msi->dev_domain, bit); + if (ret) + dev_err_ratelimited(dev, "bad MSI IRQ %d\n", + bit); + } + } + + chained_irq_exit(chip, desc); +} + +static void plda_msi_bottom_irq_ack(struct irq_data *data) +{ + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; + u32 bitpos = data->hwirq; + + writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI); +} + +static void plda_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + phys_addr_t addr = port->msi.vector_phy; + + msg->address_lo = lower_32_bits(addr); + msg->address_hi = upper_32_bits(addr); + msg->data = data->hwirq; + + dev_dbg(port->dev, "msi#%x address_hi %#x address_lo %#x\n", + (int)data->hwirq, msg->address_hi, msg->address_lo); +} + +static int plda_msi_set_affinity(struct irq_data *irq_data, + const struct cpumask *mask, bool force) +{ + return -EINVAL; +} + +static struct irq_chip plda_msi_bottom_irq_chip = { + .name = "PLDA MSI", + .irq_ack = plda_msi_bottom_irq_ack, + .irq_compose_msi_msg = plda_compose_msi_msg, + .irq_set_affinity = plda_msi_set_affinity, +}; + +int plda_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *args) +{ + struct plda_pcie_rp *port = domain->host_data; + struct plda_msi *msi = &port->msi; + unsigned long bit; + + mutex_lock(&msi->lock); + bit = find_first_zero_bit(msi->used, msi->num_vectors); + if (bit >= msi->num_vectors) { + mutex_unlock(&msi->lock); + return -ENOSPC; + } + + set_bit(bit, msi->used); + + irq_domain_set_info(domain, virq, bit, &plda_msi_bottom_irq_chip, + domain->host_data, handle_edge_irq, NULL, NULL); + + mutex_unlock(&msi->lock); + + return 0; +} + +static void plda_irq_msi_domain_free(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) +{ + struct irq_data *d = irq_domain_get_irq_data(domain, virq); + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(d); + struct plda_msi *msi = &port->msi; + + mutex_lock(&msi->lock); + + if (test_bit(d->hwirq, msi->used)) + __clear_bit(d->hwirq, msi->used); + else + dev_err(port->dev, "trying to free unused MSI%lu\n", d->hwirq); + + mutex_unlock(&msi->lock); +} + +static const struct irq_domain_ops msi_domain_ops = { + .alloc = plda_irq_msi_domain_alloc, + .free = plda_irq_msi_domain_free, +}; + +static struct irq_chip plda_msi_irq_chip = { + .name = "PLDA PCIe MSI", + .irq_ack = irq_chip_ack_parent, + .irq_mask = pci_msi_mask_irq, + .irq_unmask = pci_msi_unmask_irq, +}; + +static struct msi_domain_info plda_msi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_PCI_MSIX), + .chip = &plda_msi_irq_chip, +}; + +int plda_allocate_msi_domains(struct plda_pcie_rp *port) +{ + struct device *dev = port->dev; + struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); + struct plda_msi *msi = &port->msi; + + mutex_init(&port->msi.lock); + + msi->dev_domain = irq_domain_add_linear(NULL, msi->num_vectors, + &msi_domain_ops, port); + if (!msi->dev_domain) { + dev_err(dev, "failed to create IRQ domain\n"); + return -ENOMEM; + } + + msi->msi_domain = pci_msi_create_irq_domain(fwnode, &plda_msi_domain_info, + msi->dev_domain); + if (!msi->msi_domain) { + dev_err(dev, "failed to create MSI domain\n"); + irq_domain_remove(msi->dev_domain); + return -ENOMEM; + } + + return 0; +} + +void plda_handle_intx(struct irq_desc *desc) +{ + struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + struct device *dev = port->dev; + void __iomem *bridge_base_addr = port->bridge_addr; + unsigned long status; + u32 bit; + int ret; + + chained_irq_enter(chip, desc); + + status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); + if (status & PM_MSI_INT_INTX_MASK) { + status &= PM_MSI_INT_INTX_MASK; + status >>= PM_MSI_INT_INTX_SHIFT; + for_each_set_bit(bit, &status, PCI_NUM_INTX) { + ret = generic_handle_domain_irq(port->intx_domain, bit); + if (ret) + dev_err_ratelimited(dev, "bad INTx IRQ %d\n", + bit); + } + } + + chained_irq_exit(chip, desc); +} + +static void plda_ack_intx_irq(struct irq_data *data) +{ + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; + u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); + + writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL); +} + +static void plda_mask_intx_irq(struct irq_data *data) +{ + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; + unsigned long flags; + u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); + u32 val; + + raw_spin_lock_irqsave(&port->lock, flags); + val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); + val &= ~mask; + writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); + raw_spin_unlock_irqrestore(&port->lock, flags); +} + +static void plda_unmask_intx_irq(struct irq_data *data) +{ + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; + unsigned long flags; + u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); + u32 val; + + raw_spin_lock_irqsave(&port->lock, flags); + val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); + val |= mask; + writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); + raw_spin_unlock_irqrestore(&port->lock, flags); +} + +static struct irq_chip plda_intx_irq_chip = { + .name = "PLDA PCIe INTx", + .irq_ack = plda_ack_intx_irq, + .irq_mask = plda_mask_intx_irq, + .irq_unmask = plda_unmask_intx_irq, +}; + +int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &plda_intx_irq_chip, handle_level_irq); + irq_set_chip_data(irq, domain->host_data); + + return 0; +} + +void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, + phys_addr_t axi_addr, phys_addr_t pci_addr, + size_t size) +{ + u32 atr_sz = ilog2(size) - 1; + u32 val; + + if (index == 0) + val = PCIE_CONFIG_INTERFACE; + else + val = PCIE_TX_RX_INTERFACE; + + writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + + ATR0_AXI4_SLV0_TRSL_PARAM); + + val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) | + ATR_IMPL_ENABLE; + writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + + ATR0_AXI4_SLV0_SRCADDR_PARAM); + + val = upper_32_bits(axi_addr); + writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + + ATR0_AXI4_SLV0_SRC_ADDR); + + val = lower_32_bits(pci_addr); + writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + + ATR0_AXI4_SLV0_TRSL_ADDR_LSB); + + val = upper_32_bits(pci_addr); + writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + + ATR0_AXI4_SLV0_TRSL_ADDR_UDW); + + val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); + val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT); + writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); + writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR); +} +EXPORT_SYMBOL_GPL(plda_pcie_setup_window); + +int plda_pcie_setup_iomems(struct pci_host_bridge *bridge, + struct plda_pcie_rp *port) +{ + void __iomem *bridge_base_addr = port->bridge_addr; + struct resource_entry *entry; + u64 pci_addr; + u32 index = 1; + + resource_list_for_each_entry(entry, &bridge->windows) { + if (resource_type(entry->res) == IORESOURCE_MEM) { + pci_addr = entry->res->start - entry->offset; + plda_pcie_setup_window(bridge_base_addr, index, + entry->res->start, pci_addr, + resource_size(entry->res)); + index++; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(plda_pcie_setup_iomems); diff --git a/drivers/pci/controller/plda/pcie-plda.h b/drivers/pci/controller/plda/pcie-plda.h index d04a571404b92..a5eb02ec6171c 100644 --- a/drivers/pci/controller/plda/pcie-plda.h +++ b/drivers/pci/controller/plda/pcie-plda.h @@ -119,4 +119,15 @@ struct plda_pcie_rp { struct plda_msi msi; void __iomem *bridge_addr; }; + +void plda_handle_msi(struct irq_desc *desc); +int plda_allocate_msi_domains(struct plda_pcie_rp *port); +void plda_handle_intx(struct irq_desc *desc); +int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq); +void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, + phys_addr_t axi_addr, phys_addr_t pci_addr, + size_t size); +int plda_pcie_setup_iomems(struct pci_host_bridge *bridge, + struct plda_pcie_rp *port); #endif From 0034acc59e4a043aa5a7f2795d7f61ee456dab32 Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Thu, 7 Sep 2023 17:10:53 +0800 Subject: [PATCH 49/92] PCI: plda: Add event interrupt codes and IRQ domain ops For PolarFire implements non-PLDA local interrupt events, most of event interrupt process codes can not be re-used. PLDA implements new codes and IRQ domain ops like PolarFire. plda_get_events() adds a new IRQ num to event num mapping codes for PLDA local event except DMA engine interrupt events. The DMA engine interrupt events are implemented by vendors. Signed-off-by: Minda Chen --- drivers/pci/controller/plda/pcie-plda-host.c | 115 +++++++++++++++++++ drivers/pci/controller/plda/pcie-plda.h | 10 ++ 2 files changed, 125 insertions(+) diff --git a/drivers/pci/controller/plda/pcie-plda-host.c b/drivers/pci/controller/plda/pcie-plda-host.c index ce02cfe6b5fa5..8a8b463281cbb 100644 --- a/drivers/pci/controller/plda/pcie-plda-host.c +++ b/drivers/pci/controller/plda/pcie-plda-host.c @@ -250,6 +250,121 @@ int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq, return 0; } +irqreturn_t plda_event_handler(int irq, void *dev_id) +{ + return IRQ_HANDLED; +} + +static u32 plda_get_events(struct plda_pcie_rp *port) +{ + u32 events, val, origin; + + origin = readl_relaxed(port->bridge_addr + ISTATUS_LOCAL); + + /* Error events and doorbell events */ + events = (origin >> A_ATR_EVT_POST_ERR_SHIFT) & 0xff; + + /* INTx events */ + if (origin & PM_MSI_INT_INTX_MASK) + events |= BIT(EVENT_PM_MSI_INT_INTX); + + /* MSI event and sys events */ + val = (origin >> PM_MSI_INT_MSI_SHIFT) & 0xf; + events |= val << EVENT_PM_MSI_INT_MSI; + + return events; +} + +void plda_handle_event(struct irq_desc *desc) +{ + struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + u32 bit; + unsigned long events; + + chained_irq_enter(chip, desc); + + events = plda_get_events(port); + for_each_set_bit(bit, &events, port->num_events) + generic_handle_domain_irq(port->event_domain, bit); + + chained_irq_exit(chip, desc); +} + +static u32 plda_hwirq_to_mask(int hwirq) +{ + u32 mask; + + if (hwirq < EVENT_PM_MSI_INT_INTX) + mask = BIT(hwirq + A_ATR_EVT_POST_ERR_SHIFT); + else if (hwirq == EVENT_PM_MSI_INT_INTX) + mask = PM_MSI_INT_INTX_MASK; + else + mask = BIT(hwirq + PM_MSI_TO_MASK_OFFSET); + + return mask; +} + +static void plda_ack_event_irq(struct irq_data *data) +{ + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + + writel_relaxed(plda_hwirq_to_mask(data->hwirq), + port->bridge_addr + ISTATUS_LOCAL); +} + +static void plda_mask_event_irq(struct irq_data *data) +{ + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + u32 mask, val; + + mask = plda_hwirq_to_mask(data->hwirq); + + raw_spin_lock(&port->lock); + val = readl_relaxed(port->bridge_addr + IMASK_LOCAL); + val &= ~mask; + writel_relaxed(val, port->bridge_addr + IMASK_LOCAL); + raw_spin_unlock(&port->lock); +} + +static void plda_unmask_event_irq(struct irq_data *data) +{ + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + u32 mask, val; + + mask = plda_hwirq_to_mask(data->hwirq); + + raw_spin_lock(&port->lock); + val = readl_relaxed(port->bridge_addr + IMASK_LOCAL); + val |= mask; + writel_relaxed(val, port->bridge_addr + IMASK_LOCAL); + raw_spin_unlock(&port->lock); +} + +static struct irq_chip plda_event_irq_chip = { + .name = "PLDA PCIe EVENT", + .irq_ack = plda_ack_event_irq, + .irq_mask = plda_mask_event_irq, + .irq_unmask = plda_unmask_event_irq, +}; + +static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &plda_event_irq_chip, handle_level_irq); + irq_set_chip_data(irq, domain->host_data); + + return 0; +} + +static const struct irq_domain_ops plda_evt_dom_ops = { + .map = plda_pcie_event_map, +}; + +static const struct plda_event_ops plda_event_ops = { + .get_events = plda_get_events, +}; + void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, phys_addr_t axi_addr, phys_addr_t pci_addr, size_t size) diff --git a/drivers/pci/controller/plda/pcie-plda.h b/drivers/pci/controller/plda/pcie-plda.h index a5eb02ec6171c..cc8fc8a2da91b 100644 --- a/drivers/pci/controller/plda/pcie-plda.h +++ b/drivers/pci/controller/plda/pcie-plda.h @@ -102,6 +102,14 @@ #define EVENT_PM_MSI_INT_SYS_ERR 12 #define NUM_PLDA_EVENTS 13 +#define PM_MSI_TO_MASK_OFFSET 19 + +struct plda_pcie_rp; + +struct plda_event_ops { + u32 (*get_events)(struct plda_pcie_rp *pcie); +}; + struct plda_msi { struct mutex lock; /* Protect used bitmap */ struct irq_domain *msi_domain; @@ -118,10 +126,12 @@ struct plda_pcie_rp { raw_spinlock_t lock; struct plda_msi msi; void __iomem *bridge_addr; + int num_events; }; void plda_handle_msi(struct irq_desc *desc); int plda_allocate_msi_domains(struct plda_pcie_rp *port); +irqreturn_t plda_event_handler(int irq, void *dev_id); void plda_handle_intx(struct irq_desc *desc); int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq, irq_hw_number_t hwirq); From 3e3818b5132296783b94bc1a4835d6f1d68c58b3 Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Thu, 7 Sep 2023 17:10:54 +0800 Subject: [PATCH 50/92] PCI: microchip: Rename IRQ init function Rename IRQ init function and prepare for re-use IRQ init function. Add plda_event structure for IRQ init register. rename list: mc_init_interrupts() -> plda_init_interrupts() mc_pcie_init_irq_domain()-> plda_pcie_init_irq_domains() mc_handle_event() -> plda_handle_event() get_events() -> mc_get_events() Signed-off-by: Minda Chen --- .../pci/controller/plda/pcie-microchip-host.c | 63 ++++++++++++++----- drivers/pci/controller/plda/pcie-plda.h | 10 +++ 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/drivers/pci/controller/plda/pcie-microchip-host.c b/drivers/pci/controller/plda/pcie-microchip-host.c index b1d5b5b3cee50..626302dbcfe53 100644 --- a/drivers/pci/controller/plda/pcie-microchip-host.c +++ b/drivers/pci/controller/plda/pcie-microchip-host.c @@ -378,7 +378,7 @@ static u32 local_events(struct mc_pcie *port) return val; } -static u32 get_events(struct plda_pcie_rp *port) +static u32 mc_get_events(struct plda_pcie_rp *port) { struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda); u32 events = 0; @@ -416,7 +416,7 @@ static void mc_handle_event(struct irq_desc *desc) chained_irq_enter(chip, desc); - events = get_events(port); + events = port->event_ops->get_events(port); for_each_set_bit(bit, &events, NUM_EVENTS) generic_handle_domain_irq(port->event_domain, bit); @@ -519,7 +519,7 @@ static int mc_pcie_event_map(struct irq_domain *domain, unsigned int irq, return 0; } -static const struct irq_domain_ops event_domain_ops = { +static const struct irq_domain_ops mc_event_domain_ops = { .map = mc_pcie_event_map, }; @@ -562,11 +562,13 @@ static int mc_pcie_init_clks(struct device *dev) return 0; } -static int mc_pcie_init_irq_domains(struct plda_pcie_rp *port) +static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port, + const struct plda_event *event) { struct device *dev = port->dev; struct device_node *node = dev->of_node; struct device_node *pcie_intc_node; + const struct irq_domain_ops *ops; /* Setup INTx */ pcie_intc_node = of_get_next_child(node, NULL); @@ -575,8 +577,9 @@ static int mc_pcie_init_irq_domains(struct plda_pcie_rp *port) return -EINVAL; } - port->event_domain = irq_domain_add_linear(pcie_intc_node, NUM_EVENTS, - &event_domain_ops, port); + ops = event->domain_ops ? event->domain_ops : &mc_event_domain_ops; + port->event_domain = irq_domain_add_linear(pcie_intc_node, port->num_events, + ops, port); if (!port->event_domain) { dev_err(dev, "failed to get event domain\n"); of_node_put(pcie_intc_node); @@ -661,14 +664,40 @@ static void mc_disable_interrupts(struct mc_pcie *port) writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST); } -static int mc_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp *port) +static int mc_request_event_irq(struct plda_pcie_rp *plda, int event_irq, + int event) +{ + return devm_request_irq(plda->dev, event_irq, mc_event_handler, + 0, event_cause[event].sym, plda); +} + +static const struct plda_event_ops mc_event_ops = { + .get_events = mc_get_events, +}; + +static const struct plda_event mc_event = { + .domain_ops = &mc_event_domain_ops, + .event_ops = &mc_event_ops, + .request_event_irq = mc_request_event_irq, + .intx_event = EVENT_LOCAL_PM_MSI_INT_INTX, + .msi_event = EVENT_LOCAL_PM_MSI_INT_MSI, +}; + +static int plda_init_interrupts(struct platform_device *pdev, + struct plda_pcie_rp *port, + const struct plda_event *event) { struct device *dev = &pdev->dev; int irq; int i, intx_irq, msi_irq, event_irq; int ret; - ret = mc_pcie_init_irq_domains(port); + if (!event->event_ops || !event->event_ops->get_events) { + dev_err(dev, "no get events ops\n"); + return -EINVAL; + } + + ret = plda_pcie_init_irq_domains(port, event); if (ret) { dev_err(dev, "failed creating IRQ domains\n"); return ret; @@ -678,15 +707,20 @@ static int mc_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp if (irq < 0) return -ENODEV; - for (i = 0; i < NUM_EVENTS; i++) { + port->event_ops = event->event_ops; + + for (i = 0; i < port->num_events; i++) { event_irq = irq_create_mapping(port->event_domain, i); if (!event_irq) { dev_err(dev, "failed to map hwirq %d\n", i); return -ENXIO; } - ret = devm_request_irq(dev, event_irq, mc_event_handler, - 0, event_cause[i].sym, port); + if (event->request_event_irq) + ret = event->request_event_irq(port, event_irq, i); + else + ret = devm_request_irq(dev, event_irq, plda_event_handler, + 0, NULL, port); if (ret) { dev_err(dev, "failed to request IRQ %d\n", event_irq); return ret; @@ -694,7 +728,7 @@ static int mc_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp } intx_irq = irq_create_mapping(port->event_domain, - EVENT_LOCAL_PM_MSI_INT_INTX); + event->intx_event); if (!intx_irq) { dev_err(dev, "failed to map INTx interrupt\n"); return -ENXIO; @@ -704,7 +738,7 @@ static int mc_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port); msi_irq = irq_create_mapping(port->event_domain, - EVENT_LOCAL_PM_MSI_INT_MSI); + event->msi_event); if (!msi_irq) return -ENXIO; @@ -740,7 +774,7 @@ static int mc_platform_init(struct pci_config_window *cfg) return ret; /* Address translation is up; safe to enable interrupts */ - ret = mc_init_interrupts(pdev, &port->plda); + ret = plda_init_interrupts(pdev, &port->plda, &mc_event); if (ret) return ret; @@ -761,6 +795,7 @@ static int mc_host_probe(struct platform_device *pdev) plda = &port->plda; plda->dev = dev; + plda->num_events = NUM_EVENTS; port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(port->axi_base_addr)) diff --git a/drivers/pci/controller/plda/pcie-plda.h b/drivers/pci/controller/plda/pcie-plda.h index cc8fc8a2da91b..fa0dc6bcaa2ea 100644 --- a/drivers/pci/controller/plda/pcie-plda.h +++ b/drivers/pci/controller/plda/pcie-plda.h @@ -125,10 +125,20 @@ struct plda_pcie_rp { struct irq_domain *event_domain; raw_spinlock_t lock; struct plda_msi msi; + const struct plda_event_ops *event_ops; void __iomem *bridge_addr; int num_events; }; +struct plda_event { + const struct irq_domain_ops *domain_ops; + const struct plda_event_ops *event_ops; + int (*request_event_irq)(struct plda_pcie_rp *pcie, + int event_irq, int event); + int intx_event; + int msi_event; +}; + void plda_handle_msi(struct irq_desc *desc); int plda_allocate_msi_domains(struct plda_pcie_rp *port); irqreturn_t plda_event_handler(int irq, void *dev_id); From c1a2e8ee6c98d57af95f601ba0e09f8d7dabe26a Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Thu, 7 Sep 2023 17:10:55 +0800 Subject: [PATCH 51/92] PCI: microchip: Move IRQ init functions to pcie-plda-host.c Move IRQ init functions to pcie-plda-host.c. mc_handle_event() is merged to plda_handle_event(). Set most of the IRQ functions to static in pcie-plda-host.c Signed-off-by: Minda Chen --- .../pci/controller/plda/pcie-microchip-host.c | 131 ------------------ drivers/pci/controller/plda/pcie-plda-host.c | 130 ++++++++++++++++- drivers/pci/controller/plda/pcie-plda.h | 9 +- 3 files changed, 126 insertions(+), 144 deletions(-) diff --git a/drivers/pci/controller/plda/pcie-microchip-host.c b/drivers/pci/controller/plda/pcie-microchip-host.c index 626302dbcfe53..194ac702f917d 100644 --- a/drivers/pci/controller/plda/pcie-microchip-host.c +++ b/drivers/pci/controller/plda/pcie-microchip-host.c @@ -317,10 +317,6 @@ static void mc_pcie_enable_msi(struct mc_pcie *port, void __iomem *ecam) ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_HI); } -static const struct irq_domain_ops intx_domain_ops = { - .map = plda_pcie_intx_map, -}; - static inline u32 reg_to_event(u32 reg, struct event_map field) { return (reg & field.reg_mask) ? BIT(field.event_bit) : 0; @@ -407,23 +403,6 @@ static irqreturn_t mc_event_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static void mc_handle_event(struct irq_desc *desc) -{ - struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); - unsigned long events; - u32 bit; - struct irq_chip *chip = irq_desc_get_chip(desc); - - chained_irq_enter(chip, desc); - - events = port->event_ops->get_events(port); - - for_each_set_bit(bit, &events, NUM_EVENTS) - generic_handle_domain_irq(port->event_domain, bit); - - chained_irq_exit(chip, desc); -} - static void mc_ack_event_irq(struct irq_data *data) { struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); @@ -562,48 +541,6 @@ static int mc_pcie_init_clks(struct device *dev) return 0; } -static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port, - const struct plda_event *event) -{ - struct device *dev = port->dev; - struct device_node *node = dev->of_node; - struct device_node *pcie_intc_node; - const struct irq_domain_ops *ops; - - /* Setup INTx */ - pcie_intc_node = of_get_next_child(node, NULL); - if (!pcie_intc_node) { - dev_err(dev, "failed to find PCIe Intc node\n"); - return -EINVAL; - } - - ops = event->domain_ops ? event->domain_ops : &mc_event_domain_ops; - port->event_domain = irq_domain_add_linear(pcie_intc_node, port->num_events, - ops, port); - if (!port->event_domain) { - dev_err(dev, "failed to get event domain\n"); - of_node_put(pcie_intc_node); - return -ENOMEM; - } - - irq_domain_update_bus_token(port->event_domain, DOMAIN_BUS_NEXUS); - - port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, - &intx_domain_ops, port); - if (!port->intx_domain) { - dev_err(dev, "failed to get an INTx IRQ domain\n"); - of_node_put(pcie_intc_node); - return -ENOMEM; - } - - irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED); - - of_node_put(pcie_intc_node); - raw_spin_lock_init(&port->lock); - - return plda_allocate_msi_domains(port); -} - static inline void mc_clear_secs(struct mc_pcie *port) { void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; @@ -683,74 +620,6 @@ static const struct plda_event mc_event = { .msi_event = EVENT_LOCAL_PM_MSI_INT_MSI, }; -static int plda_init_interrupts(struct platform_device *pdev, - struct plda_pcie_rp *port, - const struct plda_event *event) -{ - struct device *dev = &pdev->dev; - int irq; - int i, intx_irq, msi_irq, event_irq; - int ret; - - if (!event->event_ops || !event->event_ops->get_events) { - dev_err(dev, "no get events ops\n"); - return -EINVAL; - } - - ret = plda_pcie_init_irq_domains(port, event); - if (ret) { - dev_err(dev, "failed creating IRQ domains\n"); - return ret; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return -ENODEV; - - port->event_ops = event->event_ops; - - for (i = 0; i < port->num_events; i++) { - event_irq = irq_create_mapping(port->event_domain, i); - if (!event_irq) { - dev_err(dev, "failed to map hwirq %d\n", i); - return -ENXIO; - } - - if (event->request_event_irq) - ret = event->request_event_irq(port, event_irq, i); - else - ret = devm_request_irq(dev, event_irq, plda_event_handler, - 0, NULL, port); - if (ret) { - dev_err(dev, "failed to request IRQ %d\n", event_irq); - return ret; - } - } - - intx_irq = irq_create_mapping(port->event_domain, - event->intx_event); - if (!intx_irq) { - dev_err(dev, "failed to map INTx interrupt\n"); - return -ENXIO; - } - - /* Plug the INTx chained handler */ - irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port); - - msi_irq = irq_create_mapping(port->event_domain, - event->msi_event); - if (!msi_irq) - return -ENXIO; - - /* Plug the MSI chained handler */ - irq_set_chained_handler_and_data(msi_irq, plda_handle_msi, port); - - /* Plug the main event chained handler */ - irq_set_chained_handler_and_data(irq, mc_handle_event, port); - - return 0; -} - static int mc_platform_init(struct pci_config_window *cfg) { struct device *dev = cfg->parent; diff --git a/drivers/pci/controller/plda/pcie-plda-host.c b/drivers/pci/controller/plda/pcie-plda-host.c index 8a8b463281cbb..a188f608a0cf8 100644 --- a/drivers/pci/controller/plda/pcie-plda-host.c +++ b/drivers/pci/controller/plda/pcie-plda-host.c @@ -20,7 +20,7 @@ #include "pcie-plda.h" -void plda_handle_msi(struct irq_desc *desc) +static void plda_handle_msi(struct irq_desc *desc) { struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); @@ -83,7 +83,7 @@ static struct irq_chip plda_msi_bottom_irq_chip = { .irq_set_affinity = plda_msi_set_affinity, }; -int plda_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, +static int plda_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *args) { struct plda_pcie_rp *port = domain->host_data; @@ -142,7 +142,7 @@ static struct msi_domain_info plda_msi_domain_info = { .chip = &plda_msi_irq_chip, }; -int plda_allocate_msi_domains(struct plda_pcie_rp *port) +static int plda_allocate_msi_domains(struct plda_pcie_rp *port) { struct device *dev = port->dev; struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); @@ -168,7 +168,7 @@ int plda_allocate_msi_domains(struct plda_pcie_rp *port) return 0; } -void plda_handle_intx(struct irq_desc *desc) +static void plda_handle_intx(struct irq_desc *desc) { struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); @@ -241,7 +241,7 @@ static struct irq_chip plda_intx_irq_chip = { .irq_unmask = plda_unmask_intx_irq, }; -int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq, +static int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq, irq_hw_number_t hwirq) { irq_set_chip_and_handler(irq, &plda_intx_irq_chip, handle_level_irq); @@ -250,7 +250,11 @@ int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq, return 0; } -irqreturn_t plda_event_handler(int irq, void *dev_id) +static const struct irq_domain_ops intx_domain_ops = { + .map = plda_pcie_intx_map, +}; + +static irqreturn_t plda_event_handler(int irq, void *dev_id) { return IRQ_HANDLED; } @@ -284,7 +288,8 @@ void plda_handle_event(struct irq_desc *desc) chained_irq_enter(chip, desc); - events = plda_get_events(port); + events = port->event_ops->get_events(port); + for_each_set_bit(bit, &events, port->num_events) generic_handle_domain_irq(port->event_domain, bit); @@ -365,6 +370,117 @@ static const struct plda_event_ops plda_event_ops = { .get_events = plda_get_events, }; +static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port, + const struct plda_event *event) +{ + struct device *dev = port->dev; + struct device_node *node = dev->of_node; + struct device_node *pcie_intc_node; + const struct irq_domain_ops *ops; + + /* Setup INTx */ + pcie_intc_node = of_get_next_child(node, NULL); + if (!pcie_intc_node) { + dev_err(dev, "failed to find PCIe Intc node\n"); + return -EINVAL; + } + + ops = event->domain_ops ? event->domain_ops : &plda_evt_dom_ops; + port->event_domain = irq_domain_add_linear(pcie_intc_node, port->num_events, + ops, port); + if (!port->event_domain) { + dev_err(dev, "failed to get event domain\n"); + of_node_put(pcie_intc_node); + return -ENOMEM; + } + + irq_domain_update_bus_token(port->event_domain, DOMAIN_BUS_NEXUS); + + port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, + &intx_domain_ops, port); + if (!port->intx_domain) { + dev_err(dev, "failed to get an INTx IRQ domain\n"); + of_node_put(pcie_intc_node); + return -ENOMEM; + } + + irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED); + + of_node_put(pcie_intc_node); + raw_spin_lock_init(&port->lock); + + return plda_allocate_msi_domains(port); +} + +int plda_init_interrupts(struct platform_device *pdev, + struct plda_pcie_rp *port, + const struct plda_event *event) +{ + struct device *dev = &pdev->dev; + int irq; + int i, intx_irq, msi_irq, event_irq; + int ret; + + if (!event->event_ops || !event->event_ops->get_events) { + dev_err(dev, "no get events ops\n"); + return -EINVAL; + } + + ret = plda_pcie_init_irq_domains(port, event); + if (ret) { + dev_err(dev, "failed creating IRQ domains\n"); + return ret; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -ENODEV; + + port->event_ops = event->event_ops; + + for (i = 0; i < port->num_events; i++) { + event_irq = irq_create_mapping(port->event_domain, i); + if (!event_irq) { + dev_err(dev, "failed to map hwirq %d\n", i); + return -ENXIO; + } + + if (event->request_event_irq) + ret = event->request_event_irq(port, event_irq, i); + else + ret = devm_request_irq(dev, event_irq, plda_event_handler, + 0, NULL, port); + if (ret) { + dev_err(dev, "failed to request IRQ %d\n", event_irq); + return ret; + } + } + + intx_irq = irq_create_mapping(port->event_domain, + event->intx_event); + if (!intx_irq) { + dev_err(dev, "failed to map INTx interrupt\n"); + return -ENXIO; + } + + /* Plug the INTx chained handler */ + irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port); + + msi_irq = irq_create_mapping(port->event_domain, + event->msi_event); + if (!msi_irq) + return -ENXIO; + + /* Plug the MSI chained handler */ + irq_set_chained_handler_and_data(msi_irq, plda_handle_msi, port); + + /* Plug the main event chained handler */ + irq_set_chained_handler_and_data(irq, plda_handle_event, port); + + return 0; +} +EXPORT_SYMBOL_GPL(plda_init_interrupts); + void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, phys_addr_t axi_addr, phys_addr_t pci_addr, size_t size) diff --git a/drivers/pci/controller/plda/pcie-plda.h b/drivers/pci/controller/plda/pcie-plda.h index fa0dc6bcaa2ea..d86a18d92fcd1 100644 --- a/drivers/pci/controller/plda/pcie-plda.h +++ b/drivers/pci/controller/plda/pcie-plda.h @@ -139,12 +139,9 @@ struct plda_event { int msi_event; }; -void plda_handle_msi(struct irq_desc *desc); -int plda_allocate_msi_domains(struct plda_pcie_rp *port); -irqreturn_t plda_event_handler(int irq, void *dev_id); -void plda_handle_intx(struct irq_desc *desc); -int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq); +int plda_init_interrupts(struct platform_device *pdev, + struct plda_pcie_rp *port, + const struct plda_event *event); void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, phys_addr_t axi_addr, phys_addr_t pci_addr, size_t size); From 8f6f6b27af3c9bd27c60e7183ba013e77ec815ec Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Thu, 7 Sep 2023 17:10:56 +0800 Subject: [PATCH 52/92] dt-bindings: PCI: Add StarFive JH7110 PCIe controller Add StarFive JH7110 SoC PCIe controller dt-bindings. JH7110 using PLDA XpressRICH PCIe host controller IP. Signed-off-by: Minda Chen Reviewed-by: Hal Feng Reviewed-by: Conor Dooley Reviewed-by: Rob Herring --- .../bindings/pci/starfive,jh7110-pcie.yaml | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml diff --git a/Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml b/Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml new file mode 100644 index 0000000000000..67151aaa39480 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml @@ -0,0 +1,120 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/starfive,jh7110-pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 PCIe host controller + +maintainers: + - Kevin Xie + +allOf: + - $ref: plda,xpressrich3-axi-common.yaml# + +properties: + compatible: + const: starfive,jh7110-pcie + + clocks: + items: + - description: NOC bus clock + - description: Transport layer clock + - description: AXI MST0 clock + - description: APB clock + + clock-names: + items: + - const: noc + - const: tl + - const: axi_mst0 + - const: apb + + resets: + items: + - description: AXI MST0 reset + - description: AXI SLAVE0 reset + - description: AXI SLAVE reset + - description: PCIE BRIDGE reset + - description: PCIE CORE reset + - description: PCIE APB reset + + reset-names: + items: + - const: mst0 + - const: slv0 + - const: slv + - const: brg + - const: core + - const: apb + + starfive,stg-syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: + The phandle to System Register Controller syscon node. + + perst-gpios: + description: GPIO controlled connection to PERST# signal + maxItems: 1 + + phys: + description: + Specified PHY is attached to PCIe controller. + maxItems: 1 + +required: + - clocks + - resets + - starfive,stg-syscon + +unevaluatedProperties: false + +examples: + - | + #include + soc { + #address-cells = <2>; + #size-cells = <2>; + + pcie@940000000 { + compatible = "starfive,jh7110-pcie"; + reg = <0x9 0x40000000 0x0 0x10000000>, + <0x0 0x2b000000 0x0 0x1000000>; + reg-names = "cfg", "apb"; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + device_type = "pci"; + ranges = <0x82000000 0x0 0x30000000 0x0 0x30000000 0x0 0x08000000>, + <0xc3000000 0x9 0x00000000 0x9 0x00000000 0x0 0x40000000>; + starfive,stg-syscon = <&stg_syscon>; + bus-range = <0x0 0xff>; + interrupt-parent = <&plic>; + interrupts = <56>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc0 0x1>, + <0x0 0x0 0x0 0x2 &pcie_intc0 0x2>, + <0x0 0x0 0x0 0x3 &pcie_intc0 0x3>, + <0x0 0x0 0x0 0x4 &pcie_intc0 0x4>; + msi-controller; + clocks = <&syscrg 86>, + <&stgcrg 10>, + <&stgcrg 8>, + <&stgcrg 9>; + clock-names = "noc", "tl", "axi_mst0", "apb"; + resets = <&stgcrg 11>, + <&stgcrg 12>, + <&stgcrg 13>, + <&stgcrg 14>, + <&stgcrg 15>, + <&stgcrg 16>; + perst-gpios = <&gpios 26 GPIO_ACTIVE_LOW>; + phys = <&pciephy0>; + + pcie_intc0: interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + }; + }; + }; From 43f48d1e3c115fa09466786e6cb2c9bf6f5aa20a Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Thu, 7 Sep 2023 17:10:57 +0800 Subject: [PATCH 53/92] PCI: starfive: Add JH7110 PCIe controller Add StarFive JH7110 SoC PCIe controller platform driver codes, and add host init/deinit to pcie-plda-host.c Signed-off-by: Minda Chen Co-developed-by: Kevin Xie Reviewed-by: Mason Huo --- drivers/pci/controller/plda/Kconfig | 12 + drivers/pci/controller/plda/Makefile | 1 + drivers/pci/controller/plda/pcie-plda-host.c | 121 ++++- drivers/pci/controller/plda/pcie-plda.h | 91 +++- drivers/pci/controller/plda/pcie-starfive.c | 460 +++++++++++++++++++ 5 files changed, 680 insertions(+), 5 deletions(-) create mode 100644 drivers/pci/controller/plda/pcie-starfive.c diff --git a/drivers/pci/controller/plda/Kconfig b/drivers/pci/controller/plda/Kconfig index e54a82ee94f59..8de661730aa58 100644 --- a/drivers/pci/controller/plda/Kconfig +++ b/drivers/pci/controller/plda/Kconfig @@ -15,4 +15,16 @@ config PCIE_MICROCHIP_HOST Say Y here if you want kernel to support the Microchip AXI PCIe Host Bridge driver. +config PCIE_STARFIVE_HOST + tristate "StarFive PCIe host controller" + depends on OF && PCI_MSI + select PCIE_PLDA_HOST + help + Say Y here if you want to support the StarFive PCIe controller + in host mode. StarFive PCIe controller uses PLDA PCIe + core. + If you choose to build this driver as module it will + be dynamically linked and module will be called + pcie-starfive.ko + endmenu diff --git a/drivers/pci/controller/plda/Makefile b/drivers/pci/controller/plda/Makefile index 4340ab007f440..0ac6851bed48e 100644 --- a/drivers/pci/controller/plda/Makefile +++ b/drivers/pci/controller/plda/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_PCIE_PLDA_HOST) += pcie-plda-host.o obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o +obj-$(CONFIG_PCIE_STARFIVE_HOST) += pcie-starfive.o diff --git a/drivers/pci/controller/plda/pcie-plda-host.c b/drivers/pci/controller/plda/pcie-plda-host.c index a188f608a0cf8..f34a1e9e59eb2 100644 --- a/drivers/pci/controller/plda/pcie-plda-host.c +++ b/drivers/pci/controller/plda/pcie-plda-host.c @@ -20,6 +20,15 @@ #include "pcie-plda.h" +void __iomem *plda_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, + int where) +{ + struct plda_pcie_rp *pcie = bus->sysdata; + + return pcie->config_base + PCIE_ECAM_OFFSET(bus->number, devfn, where); +} +EXPORT_SYMBOL_GPL(plda_pcie_map_bus); + static void plda_handle_msi(struct irq_desc *desc) { struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); @@ -370,6 +379,12 @@ static const struct plda_event_ops plda_event_ops = { .get_events = plda_get_events, }; +static const struct plda_event plda_default_event = { + .event_ops = &plda_event_ops, + .intx_event = EVENT_PM_MSI_INT_INTX, + .msi_event = EVENT_PM_MSI_INT_MSI +}; + static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port, const struct plda_event *event) { @@ -417,7 +432,6 @@ int plda_init_interrupts(struct platform_device *pdev, const struct plda_event *event) { struct device *dev = &pdev->dev; - int irq; int i, intx_irq, msi_irq, event_irq; int ret; @@ -432,8 +446,8 @@ int plda_init_interrupts(struct platform_device *pdev, return ret; } - irq = platform_get_irq(pdev, 0); - if (irq < 0) + port->irq = platform_get_irq(pdev, 0); + if (port->irq < 0) return -ENODEV; port->event_ops = event->event_ops; @@ -462,6 +476,7 @@ int plda_init_interrupts(struct platform_device *pdev, dev_err(dev, "failed to map INTx interrupt\n"); return -ENXIO; } + port->intx_irq = intx_irq; /* Plug the INTx chained handler */ irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port); @@ -470,12 +485,13 @@ int plda_init_interrupts(struct platform_device *pdev, event->msi_event); if (!msi_irq) return -ENXIO; + port->msi_irq = msi_irq; /* Plug the MSI chained handler */ irq_set_chained_handler_and_data(msi_irq, plda_handle_msi, port); /* Plug the main event chained handler */ - irq_set_chained_handler_and_data(irq, plda_handle_event, port); + irq_set_chained_handler_and_data(port->irq, plda_handle_event, port); return 0; } @@ -541,3 +557,100 @@ int plda_pcie_setup_iomems(struct pci_host_bridge *bridge, return 0; } EXPORT_SYMBOL_GPL(plda_pcie_setup_iomems); + +static void plda_pcie_irq_domain_deinit(struct plda_pcie_rp *pcie) +{ + irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); + irq_set_chained_handler_and_data(pcie->msi_irq, NULL, NULL); + irq_set_chained_handler_and_data(pcie->intx_irq, NULL, NULL); + + irq_domain_remove(pcie->msi.msi_domain); + irq_domain_remove(pcie->msi.dev_domain); + + irq_domain_remove(pcie->intx_domain); + irq_domain_remove(pcie->event_domain); +} + +int plda_pcie_host_init(struct plda_pcie_rp *port, struct pci_ops *ops) +{ + struct resource *cfg_res; + struct device *dev = port->dev; + int ret; + struct pci_host_bridge *bridge; + struct platform_device *pdev = to_platform_device(dev); + + port->bridge_addr = + devm_platform_ioremap_resource_byname(pdev, "apb"); + + if (IS_ERR(port->bridge_addr)) + return dev_err_probe(dev, PTR_ERR(port->bridge_addr), + "failed to map reg memory\n"); + + cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); + if (!cfg_res) + return dev_err_probe(dev, -ENODEV, + "failed to get config memory\n"); + + port->config_base = devm_ioremap_resource(dev, cfg_res); + if (IS_ERR(port->config_base)) + return dev_err_probe(dev, PTR_ERR(port->config_base), + "failed to map config memory\n"); + + port->phy = devm_phy_optional_get(dev, NULL); + if (IS_ERR(port->phy)) + return dev_err_probe(dev, PTR_ERR(port->phy), + "failed to get pcie phy\n"); + + bridge = devm_pci_alloc_host_bridge(dev, 0); + if (!bridge) + return dev_err_probe(dev, -ENOMEM, + "failed to alloc bridge\n"); + + if (port->host_ops->host_init) { + ret = port->host_ops->host_init(port); + if (ret) + return ret; + } + + port->bridge = bridge; + plda_pcie_setup_window(port->bridge_addr, 0, cfg_res->start, 0, + resource_size(cfg_res)); + plda_pcie_setup_iomems(bridge, port); + plda_set_default_msi(&port->msi); + ret = plda_init_interrupts(pdev, port, &plda_default_event); + if (ret) + goto err_host; + + /* Set default bus ops */ + bridge->ops = ops; + bridge->sysdata = port; + + ret = pci_host_probe(bridge); + if (ret < 0) { + dev_err(dev, "failed to pci host probe: %d\n", ret); + goto err_probe; + } + + return ret; + +err_probe: + plda_pcie_irq_domain_deinit(port); +err_host: + if (port->host_ops->host_deinit) + port->host_ops->host_deinit(port); + + return ret; +} +EXPORT_SYMBOL_GPL(plda_pcie_host_init); + +void plda_pcie_host_deinit(struct plda_pcie_rp *port) +{ + pci_stop_root_bus(port->bridge->bus); + pci_remove_root_bus(port->bridge->bus); + + plda_pcie_irq_domain_deinit(port); + + if (port->host_ops->host_deinit) + port->host_ops->host_deinit(port); +} +EXPORT_SYMBOL_GPL(plda_pcie_host_deinit); diff --git a/drivers/pci/controller/plda/pcie-plda.h b/drivers/pci/controller/plda/pcie-plda.h index d86a18d92fcd1..f208f70d7be4d 100644 --- a/drivers/pci/controller/plda/pcie-plda.h +++ b/drivers/pci/controller/plda/pcie-plda.h @@ -6,14 +6,24 @@ #ifndef _PCIE_PLDA_H #define _PCIE_PLDA_H +#include + /* Number of MSI IRQs */ #define PLDA_MAX_NUM_MSI_IRQS 32 /* PCIe Bridge Phy Regs */ +#define GEN_SETTINGS 0x80 +#define RP_ENABLE 1 +#define PCIE_PCI_IDS_DW1 0x9c +#define IDS_CLASS_CODE_SHIFT 16 #define PCIE_PCI_IRQ_DW0 0xa8 #define MSIX_CAP_MASK BIT(31) #define NUM_MSI_MSGS_MASK GENMASK(6, 4) #define NUM_MSI_MSGS_SHIFT 4 +#define PCI_MISC 0xb4 +#define PHY_FUNCTION_DIS BIT(15) +#define PCIE_WINROM 0xfc +#define PREF_MEM_WIN_64_SUPPORT BIT(3) #define IMASK_LOCAL 0x180 #define DMA_END_ENGINE_0_MASK 0x00000000u @@ -64,6 +74,8 @@ #define ISTATUS_HOST 0x18c #define IMSI_ADDR 0x190 #define ISTATUS_MSI 0x194 +#define PMSG_SUPPORT_RX 0x3f0 +#define PMSG_LTR_SUPPORT BIT(2) /* PCIe Master table init defines */ #define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600u @@ -85,6 +97,8 @@ #define PCIE_TX_RX_INTERFACE 0x00000000u #define PCIE_CONFIG_INTERFACE 0x00000001u +#define CONFIG_SPACE_ADDR 0x1000u + #define ATR_ENTRY_SIZE 32 #define EVENT_A_ATR_EVT_POST_ERR 0 @@ -110,6 +124,11 @@ struct plda_event_ops { u32 (*get_events)(struct plda_pcie_rp *pcie); }; +struct plda_pcie_host_ops { + int (*host_init)(struct plda_pcie_rp *pcie); + void (*host_deinit)(struct plda_pcie_rp *pcie); +}; + struct plda_msi { struct mutex lock; /* Protect used bitmap */ struct irq_domain *msi_domain; @@ -121,12 +140,19 @@ struct plda_msi { struct plda_pcie_rp { struct device *dev; + struct pci_host_bridge *bridge; struct irq_domain *intx_domain; struct irq_domain *event_domain; raw_spinlock_t lock; struct plda_msi msi; const struct plda_event_ops *event_ops; + const struct plda_pcie_host_ops *host_ops; + struct phy *phy; void __iomem *bridge_addr; + void __iomem *config_base; + int irq; + int msi_irq; + int intx_irq; int num_events; }; @@ -139,6 +165,7 @@ struct plda_event { int msi_event; }; +void __iomem *plda_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, int where); int plda_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp *port, const struct plda_event *event); @@ -147,4 +174,66 @@ void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, size_t size); int plda_pcie_setup_iomems(struct pci_host_bridge *bridge, struct plda_pcie_rp *port); -#endif +int plda_pcie_host_init(struct plda_pcie_rp *pcie, struct pci_ops *ops); +void plda_pcie_host_deinit(struct plda_pcie_rp *pcie); + +static inline void plda_set_default_msi(struct plda_msi *msi) +{ + msi->vector_phy = IMSI_ADDR; + msi->num_vectors = PLDA_MAX_NUM_MSI_IRQS; +} + +static inline void plda_pcie_enable_root_port(struct plda_pcie_rp *plda) +{ + u32 value; + + value = readl_relaxed(plda->bridge_addr + GEN_SETTINGS); + value |= RP_ENABLE; + writel_relaxed(value, plda->bridge_addr + GEN_SETTINGS); +} + +static inline void plda_pcie_set_standard_class(struct plda_pcie_rp *plda) +{ + u32 value; + + value = readl_relaxed(plda->bridge_addr + PCIE_PCI_IDS_DW1); + value &= 0xff; + value |= (PCI_CLASS_BRIDGE_PCI << IDS_CLASS_CODE_SHIFT); + writel_relaxed(value, plda->bridge_addr + PCIE_PCI_IDS_DW1); +} + +static inline void plda_pcie_set_pref_win_64bit(struct plda_pcie_rp *plda) +{ + u32 value; + + value = readl_relaxed(plda->bridge_addr + PCIE_WINROM); + value |= PREF_MEM_WIN_64_SUPPORT; + writel_relaxed(value, plda->bridge_addr + PCIE_WINROM); +} + +static inline void plda_pcie_disable_ltr(struct plda_pcie_rp *plda) +{ + u32 value; + + value = readl_relaxed(plda->bridge_addr + PMSG_SUPPORT_RX); + value &= ~PMSG_LTR_SUPPORT; + writel_relaxed(value, plda->bridge_addr + PMSG_SUPPORT_RX); +} + +static inline void plda_pcie_disable_func(struct plda_pcie_rp *plda) +{ + u32 value; + + value = readl_relaxed(plda->bridge_addr + PCI_MISC); + value |= PHY_FUNCTION_DIS; + writel_relaxed(value, plda->bridge_addr + PCI_MISC); +} + +static inline void plda_pcie_write_rc_bar(struct plda_pcie_rp *plda, u64 val) +{ + void __iomem *addr = plda->bridge_addr + CONFIG_SPACE_ADDR; + + writel_relaxed(val & 0xffffffff, addr + PCI_BASE_ADDRESS_0); + writel_relaxed(val >> 32, addr + PCI_BASE_ADDRESS_1); +} +#endif /* _PCIE_PLDA_H */ diff --git a/drivers/pci/controller/plda/pcie-starfive.c b/drivers/pci/controller/plda/pcie-starfive.c new file mode 100644 index 0000000000000..1eb80041b7c4d --- /dev/null +++ b/drivers/pci/controller/plda/pcie-starfive.c @@ -0,0 +1,460 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * PCIe host controller driver for StarFive JH7110 Soc. + * + * Copyright (C) 2023 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../pci.h" + +#include "pcie-plda.h" + +#define PCIE_FUNC_NUM 4 + +/* system control */ +#define STG_SYSCON_PCIE0_BASE 0x48 +#define STG_SYSCON_PCIE1_BASE 0x1f8 + +#define STG_SYSCON_ARFUNC_OFFSET 0x78 +#define STG_SYSCON_AXI4_SLVL_ARFUNC_MASK GENMASK(22, 8) +#define STG_SYSCON_AXI4_SLVL_PHY_ARFUNC(x) FIELD_PREP(GENMASK(20, 17), x) +#define STG_SYSCON_AWFUNC_OFFSET 0x7c +#define STG_SYSCON_AXI4_SLVL_AWFUNC_MASK GENMASK(14, 0) +#define STG_SYSCON_AXI4_SLVL_PHY_AWFUNC(x) FIELD_PREP(GENMASK(12, 9), x) +#define STG_SYSCON_CLKREQ BIT(22) +#define STG_SYSCON_CKREF_SRC_MASK GENMASK(19, 18) +#define STG_SYSCON_RP_NEP_OFFSET 0xe8 +#define STG_SYSCON_K_RP_NEP BIT(8) +#define STG_SYSCON_LNKSTA_OFFSET 0x170 +#define DATA_LINK_ACTIVE BIT(5) + +/* Parameters for the waiting for link up routine */ +#define LINK_WAIT_MAX_RETRIES 10 +#define LINK_WAIT_USLEEP_MIN 90000 +#define LINK_WAIT_USLEEP_MAX 100000 + +struct starfive_jh7110_pcie { + struct plda_pcie_rp plda; + struct reset_control *resets; + struct clk_bulk_data *clks; + struct regmap *reg_syscon; + struct gpio_desc *power_gpio; + struct gpio_desc *reset_gpio; + + u32 stg_arfun; + u32 stg_awfun; + u32 stg_rp_nep; + u32 stg_lnksta; + + int num_clks; +}; + +/* + * The BAR0/1 of bridge should be hidden during enumeration to + * avoid the sizing and resource allocation by PCIe core. + */ +static bool starfive_pcie_hide_rc_bar(struct pci_bus *bus, unsigned int devfn, + int offset) +{ + if (pci_is_root_bus(bus) && !devfn && + (offset == PCI_BASE_ADDRESS_0 || offset == PCI_BASE_ADDRESS_1)) + return true; + + return false; +} + +static int starfive_pcie_config_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 value) +{ + if (starfive_pcie_hide_rc_bar(bus, devfn, where)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + return pci_generic_config_write(bus, devfn, where, size, value); +} + +static int starfive_pcie_config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *value) +{ + if (starfive_pcie_hide_rc_bar(bus, devfn, where)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + return pci_generic_config_read(bus, devfn, where, size, value); +} + +static int starfive_pcie_parse_dt(struct starfive_jh7110_pcie *pcie, struct device *dev) +{ + int pcie_id, stg_pcie_base; + + pcie->num_clks = devm_clk_bulk_get_all(dev, &pcie->clks); + if (pcie->num_clks < 0) + return dev_err_probe(dev, -ENODEV, + "failed to get pcie clocks\n"); + + pcie->resets = devm_reset_control_array_get_exclusive(dev); + if (IS_ERR(pcie->resets)) + return dev_err_probe(dev, PTR_ERR(pcie->resets), + "failed to get pcie resets"); + + pcie->reg_syscon = + syscon_regmap_lookup_by_phandle(dev->of_node, + "starfive,stg-syscon"); + + if (IS_ERR(pcie->reg_syscon)) + return dev_err_probe(dev, PTR_ERR(pcie->reg_syscon), + "failed to parse starfive,stg-syscon\n"); + + pcie_id = of_alias_get_id(dev->of_node, "pcie"); + if (pcie_id < 0 || pcie_id > 1) + return dev_err_probe(dev, -ENODEV, + "failed to get valid pcie id\n"); + + if (pcie_id == 0) + stg_pcie_base = STG_SYSCON_PCIE0_BASE; + else + stg_pcie_base = STG_SYSCON_PCIE1_BASE; + + pcie->stg_arfun = stg_pcie_base + STG_SYSCON_ARFUNC_OFFSET; + pcie->stg_awfun = stg_pcie_base + STG_SYSCON_AWFUNC_OFFSET; + pcie->stg_rp_nep = stg_pcie_base + STG_SYSCON_RP_NEP_OFFSET; + pcie->stg_lnksta = stg_pcie_base + STG_SYSCON_LNKSTA_OFFSET; + + pcie->reset_gpio = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_HIGH); + if (IS_ERR(pcie->reset_gpio)) { + dev_warn(dev, "failed to get perst-gpio\n"); + return -EINVAL; + } + + pcie->power_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(pcie->power_gpio)) { + dev_warn(dev, "failed to get power-gpio\n"); + return -EINVAL; + } + + return 0; +} + +static struct pci_ops starfive_pcie_ops = { + .map_bus = plda_pcie_map_bus, + .read = starfive_pcie_config_read, + .write = starfive_pcie_config_write, +}; + +static int starfive_pcie_clk_rst_init(struct starfive_jh7110_pcie *pcie) +{ + int ret; + struct device *dev = pcie->plda.dev; + + ret = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks); + if (ret) { + dev_err(dev, "failed to enable clocks\n"); + return ret; + } + + ret = reset_control_deassert(pcie->resets); + if (ret) { + clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); + dev_err(dev, "failed to resets\n"); + } + + return ret; +} + +static void starfive_pcie_clk_rst_deinit(struct starfive_jh7110_pcie *pcie) +{ + reset_control_assert(pcie->resets); + clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); +} + +static bool starfive_pcie_link_up(struct plda_pcie_rp *plda) +{ + struct starfive_jh7110_pcie *pcie = + container_of(plda, struct starfive_jh7110_pcie, plda); + struct device *dev = pcie->plda.dev; + int ret; + u32 stg_reg_val; + + ret = regmap_read(pcie->reg_syscon, pcie->stg_lnksta, &stg_reg_val); + if (ret) { + dev_err(dev, "failed to read link status\n"); + return false; + } + + if (stg_reg_val & DATA_LINK_ACTIVE) + return true; + + return false; +} + +static int starfive_pcie_host_wait_for_link(struct starfive_jh7110_pcie *pcie) +{ + struct device *dev = pcie->plda.dev; + int retries; + + /* Check if the link is up or not */ + for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { + if (starfive_pcie_link_up(&pcie->plda)) { + dev_info(dev, "port link up\n"); + return 0; + } + usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); + } + + return -ETIMEDOUT; +} + +static int starfive_pcie_enable_phy(struct device *dev, struct plda_pcie_rp *pcie) +{ + int ret; + + if (!pcie->phy) + return 0; + + ret = phy_init(pcie->phy); + if (ret) + return dev_err_probe(dev, ret, + "failed to initialize pcie phy\n"); + + ret = phy_set_mode(pcie->phy, PHY_MODE_PCIE); + if (ret) { + dev_err(dev, "failed to set pcie mode\n"); + goto err_phy_on; + } + + ret = phy_power_on(pcie->phy); + if (ret) { + dev_err(dev, "failed to power on pcie phy\n"); + goto err_phy_on; + } + + return 0; + +err_phy_on: + phy_exit(pcie->phy); + return ret; +} + +static void starfive_pcie_disable_phy(struct plda_pcie_rp *pcie) +{ + phy_power_off(pcie->phy); + phy_exit(pcie->phy); +} + +static void starfive_pcie_host_deinit(struct plda_pcie_rp *plda) +{ + struct starfive_jh7110_pcie *pcie = + container_of(plda, struct starfive_jh7110_pcie, plda); + + starfive_pcie_clk_rst_deinit(pcie); + if (pcie->power_gpio) + gpiod_set_value_cansleep(pcie->power_gpio, 0); + starfive_pcie_disable_phy(plda); +} + +static int starfive_pcie_host_init(struct plda_pcie_rp *plda) +{ + int i; + struct starfive_jh7110_pcie *pcie = + container_of(plda, struct starfive_jh7110_pcie, plda); + struct device *dev = plda->dev; + int ret; + + ret = starfive_pcie_enable_phy(dev, plda); + if (ret) + return ret; + + regmap_update_bits(pcie->reg_syscon, pcie->stg_rp_nep, + STG_SYSCON_K_RP_NEP, STG_SYSCON_K_RP_NEP); + + regmap_update_bits(pcie->reg_syscon, pcie->stg_awfun, + STG_SYSCON_CKREF_SRC_MASK, + FIELD_PREP(STG_SYSCON_CKREF_SRC_MASK, 2)); + + regmap_update_bits(pcie->reg_syscon, pcie->stg_awfun, + STG_SYSCON_CLKREQ, STG_SYSCON_CLKREQ); + + ret = starfive_pcie_clk_rst_init(pcie); + if (ret) + return ret; + + if (pcie->power_gpio) + gpiod_set_value_cansleep(pcie->power_gpio, 1); + + if (pcie->reset_gpio) + gpiod_set_value_cansleep(pcie->reset_gpio, 1); + + /* Disable physical functions except #0 */ + for (i = 1; i < PCIE_FUNC_NUM; i++) { + regmap_update_bits(pcie->reg_syscon, + pcie->stg_arfun, + STG_SYSCON_AXI4_SLVL_ARFUNC_MASK, + STG_SYSCON_AXI4_SLVL_PHY_ARFUNC(i)); + + regmap_update_bits(pcie->reg_syscon, + pcie->stg_awfun, + STG_SYSCON_AXI4_SLVL_AWFUNC_MASK, + STG_SYSCON_AXI4_SLVL_PHY_AWFUNC(i)); + + plda_pcie_disable_func(plda); + } + + regmap_update_bits(pcie->reg_syscon, pcie->stg_arfun, + STG_SYSCON_AXI4_SLVL_ARFUNC_MASK, 0); + regmap_update_bits(pcie->reg_syscon, pcie->stg_awfun, + STG_SYSCON_AXI4_SLVL_AWFUNC_MASK, 0); + + plda_pcie_enable_root_port(plda); + plda_pcie_write_rc_bar(plda, 0); + + /* PCIe PCI Standard Configuration Identification Settings. */ + plda_pcie_set_standard_class(plda); + + /* + * The LTR message forwarding of PCIe Message Reception was set by core + * as default, but the forward id & addr are also need to be reset. + * If we do not disable LTR message forwarding here, or set a legal + * forwarding address, the kernel will get stuck after this driver probe. + * To workaround, disable the LTR message forwarding support on + * PCIe Message Reception. + */ + plda_pcie_disable_ltr(plda); + + /* Prefetchable memory window 64-bit addressing support */ + plda_pcie_set_pref_win_64bit(plda); + + /* Ensure that PERST has been asserted for at least 100 ms, + * the sleep value is T_PVPERL from PCIe CEM spec r2.0 (Table 2-4) + */ + msleep(100); + if (pcie->reset_gpio) + gpiod_set_value_cansleep(pcie->reset_gpio, 0); + + /* As the requirement in PCIe base spec r6.0, system (<=5GT/s) must + * wait a minimum of 100 ms following exit from a conventional reset + * before sending a configuration request to the device. + */ + msleep(100); + + if (starfive_pcie_host_wait_for_link(pcie)) + dev_info(dev, "port link down\n"); + + return ret; +} + +static const struct plda_pcie_host_ops sf_host_ops = { + .host_init = starfive_pcie_host_init, + .host_deinit = starfive_pcie_host_deinit, +}; + +static int starfive_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct starfive_jh7110_pcie *pcie; + struct plda_pcie_rp *plda; + int ret; + + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + plda = &pcie->plda; + plda->dev = dev; + + ret = starfive_pcie_parse_dt(pcie, dev); + if (ret) + return ret; + + plda->host_ops = &sf_host_ops; + plda->num_events = NUM_PLDA_EVENTS; + ret = plda_pcie_host_init(&pcie->plda, &starfive_pcie_ops); + if (ret) + return ret; + + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + platform_set_drvdata(pdev, pcie); + + return 0; +} + +static void starfive_pcie_remove(struct platform_device *pdev) +{ + struct starfive_jh7110_pcie *pcie = platform_get_drvdata(pdev); + + plda_pcie_host_deinit(&pcie->plda); + platform_set_drvdata(pdev, NULL); +} + +static int starfive_pcie_suspend_noirq(struct device *dev) +{ + struct starfive_jh7110_pcie *pcie = dev_get_drvdata(dev); + + if (!pcie) + return 0; + + clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); + starfive_pcie_disable_phy(&pcie->plda); + + return 0; +} + +static int starfive_pcie_resume_noirq(struct device *dev) +{ + struct starfive_jh7110_pcie *pcie = dev_get_drvdata(dev); + int ret; + + ret = starfive_pcie_enable_phy(dev, &pcie->plda); + if (ret) + return ret; + + ret = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks); + if (ret) { + dev_err(dev, "failed to enable clocks\n"); + starfive_pcie_disable_phy(&pcie->plda); + return ret; + } + + return ret; +} + +static const struct dev_pm_ops starfive_pcie_pm_ops = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(starfive_pcie_suspend_noirq, + starfive_pcie_resume_noirq) +}; + +static const struct of_device_id starfive_pcie_of_match[] = { + { .compatible = "starfive,jh7110-pcie"}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, starfive_pcie_of_match); + +static struct platform_driver starfive_pcie_driver = { + .driver = { + .name = "pcie-starfive", + .of_match_table = of_match_ptr(starfive_pcie_of_match), +#ifdef CONFIG_PM_SLEEP + .pm = &starfive_pcie_pm_ops, +#endif + }, + .probe = starfive_pcie_probe, + .remove_new = starfive_pcie_remove, +}; +module_platform_driver(starfive_pcie_driver); + +MODULE_DESCRIPTION("StarFive JH7110 PCIe host driver"); +MODULE_LICENSE("GPL v2"); From 05160af3ed8c19b213463d65dbcf38792e24ca16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 7 Jul 2023 09:28:30 +0200 Subject: [PATCH 54/92] ASoC: starfive: jh7110_tdm: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230707072830.3395789-3-u.kleine-koenig@pengutronix.de Signed-off-by: Mark Brown --- sound/soc/starfive/jh7110_tdm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/starfive/jh7110_tdm.c b/sound/soc/starfive/jh7110_tdm.c index 5f5a6ca7dbda6..705f1420097be 100644 --- a/sound/soc/starfive/jh7110_tdm.c +++ b/sound/soc/starfive/jh7110_tdm.c @@ -634,10 +634,9 @@ static int jh7110_tdm_probe(struct platform_device *pdev) return ret; } -static int jh7110_tdm_dev_remove(struct platform_device *pdev) +static void jh7110_tdm_dev_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); - return 0; } static const struct of_device_id jh7110_tdm_of_match[] = { @@ -661,7 +660,7 @@ static struct platform_driver jh7110_tdm_driver = { .pm = pm_ptr(&jh7110_tdm_pm_ops), }, .probe = jh7110_tdm_probe, - .remove = jh7110_tdm_dev_remove, + .remove_new = jh7110_tdm_dev_remove, }; module_platform_driver(jh7110_tdm_driver); From 3457d5fdfc9163fce97f7d103ee02815ea33f741 Mon Sep 17 00:00:00 2001 From: Hal Feng Date: Mon, 14 Aug 2023 16:06:16 +0800 Subject: [PATCH 55/92] ASoC: dt-bindings: Add StarFive JH7110 PWM-DAC controller Add bindings for the PWM-DAC controller on the JH7110 RISC-V SoC by StarFive Ltd. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Hal Feng Link: https://lore.kernel.org/r/20230814080618.10036-2-hal.feng@starfivetech.com Signed-off-by: Mark Brown --- .../sound/starfive,jh7110-pwmdac.yaml | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/starfive,jh7110-pwmdac.yaml diff --git a/Documentation/devicetree/bindings/sound/starfive,jh7110-pwmdac.yaml b/Documentation/devicetree/bindings/sound/starfive,jh7110-pwmdac.yaml new file mode 100644 index 0000000000000..e2b4db6aa2fb6 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/starfive,jh7110-pwmdac.yaml @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/starfive,jh7110-pwmdac.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 PWM-DAC Controller + +description: + The PWM-DAC Controller uses PWM square wave generators plus RC filters to + form a DAC for audio play in StarFive JH7110 SoC. This audio play controller + supports 16 bit audio format, up to 48K sampling frequency, up to left and + right dual channels. + +maintainers: + - Hal Feng + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: starfive,jh7110-pwmdac + + reg: + maxItems: 1 + + clocks: + items: + - description: PWMDAC APB + - description: PWMDAC CORE + + clock-names: + items: + - const: apb + - const: core + + resets: + maxItems: 1 + description: PWMDAC APB + + dmas: + maxItems: 1 + description: TX DMA Channel + + dma-names: + const: tx + + "#sound-dai-cells": + const: 0 + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - dmas + - dma-names + - "#sound-dai-cells" + +additionalProperties: false + +examples: + - | + pwmdac@100b0000 { + compatible = "starfive,jh7110-pwmdac"; + reg = <0x100b0000 0x1000>; + clocks = <&syscrg 157>, + <&syscrg 158>; + clock-names = "apb", "core"; + resets = <&syscrg 96>; + dmas = <&dma 22>; + dma-names = "tx"; + #sound-dai-cells = <0>; + }; From 395886d3b6471c30a8413fea9fdf298801c10043 Mon Sep 17 00:00:00 2001 From: Hal Feng Date: Mon, 14 Aug 2023 16:06:17 +0800 Subject: [PATCH 56/92] ASoC: starfive: Add JH7110 PWM-DAC driver Add PWM-DAC driver support for the StarFive JH7110 SoC. Reviewed-by: Walker Chen Signed-off-by: Hal Feng Link: https://lore.kernel.org/r/20230814080618.10036-3-hal.feng@starfivetech.com Signed-off-by: Mark Brown --- sound/soc/starfive/Kconfig | 9 + sound/soc/starfive/Makefile | 1 + sound/soc/starfive/jh7110_pwmdac.c | 529 +++++++++++++++++++++++++++++ 3 files changed, 539 insertions(+) create mode 100644 sound/soc/starfive/jh7110_pwmdac.c diff --git a/sound/soc/starfive/Kconfig b/sound/soc/starfive/Kconfig index fafb681f8c0a7..279ac5c1d309b 100644 --- a/sound/soc/starfive/Kconfig +++ b/sound/soc/starfive/Kconfig @@ -7,6 +7,15 @@ config SND_SOC_STARFIVE the Starfive SoCs' Audio interfaces. You will also need to select the audio interfaces to support below. +config SND_SOC_JH7110_PWMDAC + tristate "JH7110 PWM-DAC device driver" + depends on HAVE_CLK && SND_SOC_STARFIVE + select SND_SOC_GENERIC_DMAENGINE_PCM + select SND_SOC_SPDIF + help + Say Y or M if you want to add support for StarFive JH7110 + PWM-DAC driver. + config SND_SOC_JH7110_TDM tristate "JH7110 TDM device driver" depends on HAVE_CLK && SND_SOC_STARFIVE diff --git a/sound/soc/starfive/Makefile b/sound/soc/starfive/Makefile index f7d960211d727..9e958f70ef51e 100644 --- a/sound/soc/starfive/Makefile +++ b/sound/soc/starfive/Makefile @@ -1,2 +1,3 @@ # StarFive Platform Support +obj-$(CONFIG_SND_SOC_JH7110_PWMDAC) += jh7110_pwmdac.o obj-$(CONFIG_SND_SOC_JH7110_TDM) += jh7110_tdm.o diff --git a/sound/soc/starfive/jh7110_pwmdac.c b/sound/soc/starfive/jh7110_pwmdac.c new file mode 100644 index 0000000000000..033a9064f06b7 --- /dev/null +++ b/sound/soc/starfive/jh7110_pwmdac.c @@ -0,0 +1,529 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * jh7110_pwmdac.c -- StarFive JH7110 PWM-DAC driver + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + * + * Authors: Jenny Zhang + * Curry Zhang + * Xingyu Wu + * Hal Feng + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define JH7110_PWMDAC_WDATA 0x00 +#define JH7110_PWMDAC_CTRL 0x04 + #define JH7110_PWMDAC_ENABLE BIT(0) + #define JH7110_PWMDAC_SHIFT BIT(1) + #define JH7110_PWMDAC_DUTY_CYCLE_SHIFT 2 + #define JH7110_PWMDAC_DUTY_CYCLE_MASK GENMASK(3, 2) + #define JH7110_PWMDAC_CNT_N_SHIFT 4 + #define JH7110_PWMDAC_CNT_N_MASK GENMASK(12, 4) + #define JH7110_PWMDAC_DATA_CHANGE BIT(13) + #define JH7110_PWMDAC_DATA_MODE BIT(14) + #define JH7110_PWMDAC_DATA_SHIFT_SHIFT 15 + #define JH7110_PWMDAC_DATA_SHIFT_MASK GENMASK(17, 15) + +enum JH7110_PWMDAC_SHIFT_VAL { + PWMDAC_SHIFT_8 = 0, + PWMDAC_SHIFT_10, +}; + +enum JH7110_PWMDAC_DUTY_CYCLE_VAL { + PWMDAC_CYCLE_LEFT = 0, + PWMDAC_CYCLE_RIGHT, + PWMDAC_CYCLE_CENTER, +}; + +enum JH7110_PWMDAC_CNT_N_VAL { + PWMDAC_SAMPLE_CNT_1 = 1, + PWMDAC_SAMPLE_CNT_2, + PWMDAC_SAMPLE_CNT_3, + PWMDAC_SAMPLE_CNT_512 = 512, /* max */ +}; + +enum JH7110_PWMDAC_DATA_CHANGE_VAL { + NO_CHANGE = 0, + CHANGE, +}; + +enum JH7110_PWMDAC_DATA_MODE_VAL { + UNSIGNED_DATA = 0, + INVERTER_DATA_MSB, +}; + +enum JH7110_PWMDAC_DATA_SHIFT_VAL { + PWMDAC_DATA_LEFT_SHIFT_BIT_0 = 0, + PWMDAC_DATA_LEFT_SHIFT_BIT_1, + PWMDAC_DATA_LEFT_SHIFT_BIT_2, + PWMDAC_DATA_LEFT_SHIFT_BIT_3, + PWMDAC_DATA_LEFT_SHIFT_BIT_4, + PWMDAC_DATA_LEFT_SHIFT_BIT_5, + PWMDAC_DATA_LEFT_SHIFT_BIT_6, + PWMDAC_DATA_LEFT_SHIFT_BIT_7, +}; + +struct jh7110_pwmdac_cfg { + enum JH7110_PWMDAC_SHIFT_VAL shift; + enum JH7110_PWMDAC_DUTY_CYCLE_VAL duty_cycle; + u16 cnt_n; + enum JH7110_PWMDAC_DATA_CHANGE_VAL data_change; + enum JH7110_PWMDAC_DATA_MODE_VAL data_mode; + enum JH7110_PWMDAC_DATA_SHIFT_VAL data_shift; +}; + +struct jh7110_pwmdac_dev { + void __iomem *base; + resource_size_t mapbase; + struct jh7110_pwmdac_cfg cfg; + + struct clk_bulk_data clks[2]; + struct reset_control *rst_apb; + struct device *dev; + struct snd_dmaengine_dai_dma_data play_dma_data; + u32 saved_ctrl; +}; + +static inline void jh7110_pwmdac_write_reg(void __iomem *io_base, int reg, u32 val) +{ + writel(val, io_base + reg); +} + +static inline u32 jh7110_pwmdac_read_reg(void __iomem *io_base, int reg) +{ + return readl(io_base + reg); +} + +static void jh7110_pwmdac_set_enable(struct jh7110_pwmdac_dev *dev, bool enable) +{ + u32 value; + + value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); + if (enable) + value |= JH7110_PWMDAC_ENABLE; + else + value &= ~JH7110_PWMDAC_ENABLE; + + jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); +} + +static void jh7110_pwmdac_set_shift(struct jh7110_pwmdac_dev *dev) +{ + u32 value; + + value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); + if (dev->cfg.shift == PWMDAC_SHIFT_8) + value &= ~JH7110_PWMDAC_SHIFT; + else if (dev->cfg.shift == PWMDAC_SHIFT_10) + value |= JH7110_PWMDAC_SHIFT; + + jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); +} + +static void jh7110_pwmdac_set_duty_cycle(struct jh7110_pwmdac_dev *dev) +{ + u32 value; + + value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); + value &= ~JH7110_PWMDAC_DUTY_CYCLE_MASK; + value |= (dev->cfg.duty_cycle & 0x3) << JH7110_PWMDAC_DUTY_CYCLE_SHIFT; + + jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); +} + +static void jh7110_pwmdac_set_cnt_n(struct jh7110_pwmdac_dev *dev) +{ + u32 value; + + value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); + value &= ~JH7110_PWMDAC_CNT_N_MASK; + value |= ((dev->cfg.cnt_n - 1) & 0x1ff) << JH7110_PWMDAC_CNT_N_SHIFT; + + jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); +} + +static void jh7110_pwmdac_set_data_change(struct jh7110_pwmdac_dev *dev) +{ + u32 value; + + value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); + if (dev->cfg.data_change == NO_CHANGE) + value &= ~JH7110_PWMDAC_DATA_CHANGE; + else if (dev->cfg.data_change == CHANGE) + value |= JH7110_PWMDAC_DATA_CHANGE; + + jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); +} + +static void jh7110_pwmdac_set_data_mode(struct jh7110_pwmdac_dev *dev) +{ + u32 value; + + value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); + if (dev->cfg.data_mode == UNSIGNED_DATA) + value &= ~JH7110_PWMDAC_DATA_MODE; + else if (dev->cfg.data_mode == INVERTER_DATA_MSB) + value |= JH7110_PWMDAC_DATA_MODE; + + jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); +} + +static void jh7110_pwmdac_set_data_shift(struct jh7110_pwmdac_dev *dev) +{ + u32 value; + + value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); + value &= ~JH7110_PWMDAC_DATA_SHIFT_MASK; + value |= (dev->cfg.data_shift & 0x7) << JH7110_PWMDAC_DATA_SHIFT_SHIFT; + + jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); +} + +static void jh7110_pwmdac_set(struct jh7110_pwmdac_dev *dev) +{ + jh7110_pwmdac_set_shift(dev); + jh7110_pwmdac_set_duty_cycle(dev); + jh7110_pwmdac_set_cnt_n(dev); + jh7110_pwmdac_set_enable(dev, true); + + jh7110_pwmdac_set_data_change(dev); + jh7110_pwmdac_set_data_mode(dev); + jh7110_pwmdac_set_data_shift(dev); +} + +static void jh7110_pwmdac_stop(struct jh7110_pwmdac_dev *dev) +{ + jh7110_pwmdac_set_enable(dev, false); +} + +static int jh7110_pwmdac_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC; + + return 0; +} + +static int jh7110_pwmdac_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct jh7110_pwmdac_dev *dev = dev_get_drvdata(dai->dev); + unsigned long core_clk_rate; + int ret; + + switch (params_rate(params)) { + case 8000: + dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_3; + core_clk_rate = 6144000; + break; + case 11025: + dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_2; + core_clk_rate = 5644800; + break; + case 16000: + dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_3; + core_clk_rate = 12288000; + break; + case 22050: + dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1; + core_clk_rate = 5644800; + break; + case 32000: + dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1; + core_clk_rate = 8192000; + break; + case 44100: + dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1; + core_clk_rate = 11289600; + break; + case 48000: + dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1; + core_clk_rate = 12288000; + break; + default: + dev_err(dai->dev, "%d rate not supported\n", + params_rate(params)); + return -EINVAL; + } + + switch (params_channels(params)) { + case 1: + dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + case 2: + dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + default: + dev_err(dai->dev, "%d channels not supported\n", + params_channels(params)); + return -EINVAL; + } + + /* + * The clock rate always rounds down when using clk_set_rate() + * so increase the rate a bit + */ + core_clk_rate += 64; + jh7110_pwmdac_set(dev); + + ret = clk_set_rate(dev->clks[1].clk, core_clk_rate); + if (ret) + return dev_err_probe(dai->dev, ret, + "failed to set rate %lu for core clock\n", + core_clk_rate); + + return 0; +} + +static int jh7110_pwmdac_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct jh7110_pwmdac_dev *dev = snd_soc_dai_get_drvdata(dai); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + jh7110_pwmdac_set(dev); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + jh7110_pwmdac_stop(dev); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int jh7110_pwmdac_crg_enable(struct jh7110_pwmdac_dev *dev, bool enable) +{ + int ret; + + if (enable) { + ret = clk_bulk_prepare_enable(ARRAY_SIZE(dev->clks), dev->clks); + if (ret) + return dev_err_probe(dev->dev, ret, + "failed to enable pwmdac clocks\n"); + + ret = reset_control_deassert(dev->rst_apb); + if (ret) { + dev_err(dev->dev, "failed to deassert pwmdac apb reset\n"); + goto err_rst_apb; + } + } else { + clk_bulk_disable_unprepare(ARRAY_SIZE(dev->clks), dev->clks); + } + + return 0; + +err_rst_apb: + clk_bulk_disable_unprepare(ARRAY_SIZE(dev->clks), dev->clks); + + return ret; +} + +static int jh7110_pwmdac_dai_probe(struct snd_soc_dai *dai) +{ + struct jh7110_pwmdac_dev *dev = dev_get_drvdata(dai->dev); + + snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, NULL); + snd_soc_dai_set_drvdata(dai, dev); + + return 0; +} + +static const struct snd_soc_dai_ops jh7110_pwmdac_dai_ops = { + .startup = jh7110_pwmdac_startup, + .hw_params = jh7110_pwmdac_hw_params, + .trigger = jh7110_pwmdac_trigger, +}; + +static const struct snd_soc_component_driver jh7110_pwmdac_component = { + .name = "jh7110-pwmdac", +}; + +static struct snd_soc_dai_driver jh7110_pwmdac_dai = { + .name = "jh7110-pwmdac", + .id = 0, + .probe = jh7110_pwmdac_dai_probe, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &jh7110_pwmdac_dai_ops, +}; + +static int jh7110_pwmdac_runtime_suspend(struct device *dev) +{ + struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev); + + return jh7110_pwmdac_crg_enable(pwmdac, false); +} + +static int jh7110_pwmdac_runtime_resume(struct device *dev) +{ + struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev); + + return jh7110_pwmdac_crg_enable(pwmdac, true); +} + +static int jh7110_pwmdac_system_suspend(struct device *dev) +{ + struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev); + + /* save the CTRL register value */ + pwmdac->saved_ctrl = jh7110_pwmdac_read_reg(pwmdac->base, + JH7110_PWMDAC_CTRL); + return pm_runtime_force_suspend(dev); +} + +static int jh7110_pwmdac_system_resume(struct device *dev) +{ + struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + /* restore the CTRL register value */ + jh7110_pwmdac_write_reg(pwmdac->base, JH7110_PWMDAC_CTRL, + pwmdac->saved_ctrl); + return 0; +} + +static const struct dev_pm_ops jh7110_pwmdac_pm_ops = { + RUNTIME_PM_OPS(jh7110_pwmdac_runtime_suspend, + jh7110_pwmdac_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(jh7110_pwmdac_system_suspend, + jh7110_pwmdac_system_resume) +}; + +static void jh7110_pwmdac_init_params(struct jh7110_pwmdac_dev *dev) +{ + dev->cfg.shift = PWMDAC_SHIFT_8; + dev->cfg.duty_cycle = PWMDAC_CYCLE_CENTER; + dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1; + dev->cfg.data_change = NO_CHANGE; + dev->cfg.data_mode = INVERTER_DATA_MSB; + dev->cfg.data_shift = PWMDAC_DATA_LEFT_SHIFT_BIT_0; + + dev->play_dma_data.addr = dev->mapbase + JH7110_PWMDAC_WDATA; + dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dev->play_dma_data.fifo_size = 1; + dev->play_dma_data.maxburst = 16; +} + +static int jh7110_pwmdac_probe(struct platform_device *pdev) +{ + struct jh7110_pwmdac_dev *dev; + struct resource *res; + int ret; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(dev->base)) + return PTR_ERR(dev->base); + + dev->mapbase = res->start; + + dev->clks[0].id = "apb"; + dev->clks[1].id = "core"; + + ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(dev->clks), dev->clks); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to get pwmdac clocks\n"); + + dev->rst_apb = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(dev->rst_apb)) + return dev_err_probe(&pdev->dev, PTR_ERR(dev->rst_apb), + "failed to get pwmdac apb reset\n"); + + jh7110_pwmdac_init_params(dev); + + dev->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, dev); + ret = devm_snd_soc_register_component(&pdev->dev, + &jh7110_pwmdac_component, + &jh7110_pwmdac_dai, 1); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to register dai\n"); + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to register pcm\n"); + + pm_runtime_enable(dev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = jh7110_pwmdac_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + return 0; + +err_pm_disable: + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static int jh7110_pwmdac_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + return 0; +} + +static const struct of_device_id jh7110_pwmdac_of_match[] = { + { .compatible = "starfive,jh7110-pwmdac" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jh7110_pwmdac_of_match); + +static struct platform_driver jh7110_pwmdac_driver = { + .driver = { + .name = "jh7110-pwmdac", + .of_match_table = jh7110_pwmdac_of_match, + .pm = pm_ptr(&jh7110_pwmdac_pm_ops), + }, + .probe = jh7110_pwmdac_probe, + .remove = jh7110_pwmdac_remove, +}; +module_platform_driver(jh7110_pwmdac_driver); + +MODULE_AUTHOR("Jenny Zhang"); +MODULE_AUTHOR("Curry Zhang"); +MODULE_AUTHOR("Xingyu Wu "); +MODULE_AUTHOR("Hal Feng "); +MODULE_DESCRIPTION("StarFive JH7110 PWM-DAC driver"); +MODULE_LICENSE("GPL"); From 4d1f3391276fd880291591cc894e0617cef5a823 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Mon, 21 Aug 2023 22:41:47 +0800 Subject: [PATCH 57/92] ASoC: dt-bindings: snps,designware-i2s: Add StarFive JH7110 SoC support Add the StarFive JH7110 (TX0/TX1/RX channel) SoC support in the bindings of Designware I2S controller. The I2S controller needs two reset items to work properly on the JH7110 SoC. And TX0 channel as master mode needs 5 clock items and TX1/RX channels as slave mode need 9 clock items on the JH7110 SoC. The RX channel needs System Register Controller property to enable it and other platforms do not need it. Signed-off-by: Xingyu Wu Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230821144151.207339-2-xingyu.wu@starfivetech.com Signed-off-by: Mark Brown --- .../bindings/sound/snps,designware-i2s.yaml | 108 +++++++++++++++++- 1 file changed, 105 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/snps,designware-i2s.yaml b/Documentation/devicetree/bindings/sound/snps,designware-i2s.yaml index a970fd264b213..a48d040b0a4fb 100644 --- a/Documentation/devicetree/bindings/sound/snps,designware-i2s.yaml +++ b/Documentation/devicetree/bindings/sound/snps,designware-i2s.yaml @@ -17,6 +17,9 @@ properties: - const: snps,designware-i2s - enum: - snps,designware-i2s + - starfive,jh7110-i2stx0 + - starfive,jh7110-i2stx1 + - starfive,jh7110-i2srx reg: maxItems: 1 @@ -29,15 +32,36 @@ properties: maxItems: 1 clocks: - description: Sampling rate reference clock - maxItems: 1 + items: + - description: Sampling rate reference clock + - description: APB clock + - description: Audio master clock + - description: Inner audio master clock source + - description: External audio master clock source + - description: Bit clock + - description: Left/right channel clock + - description: External bit clock + - description: External left/right channel clock + minItems: 1 clock-names: - const: i2sclk + items: + - const: i2sclk + - const: apb + - const: mclk + - const: mclk_inner + - const: mclk_ext + - const: bclk + - const: lrck + - const: bclk_ext + - const: lrck_ext + minItems: 1 resets: items: - description: Optional controller resets + - description: controller reset of Sampling rate + minItems: 1 dmas: items: @@ -51,6 +75,17 @@ properties: - const: rx minItems: 1 + starfive,syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to System Register Controller sys_syscon node. + - description: I2S-rx enabled control offset of SYS_SYSCONSAIF__SYSCFG register. + - description: I2S-rx enabled control mask + description: + The phandle to System Register Controller syscon node and the I2S-rx(ADC) + enabled control offset and mask of SYS_SYSCONSAIF__SYSCFG register. + allOf: - $ref: dai-common.yaml# - if: @@ -66,6 +101,73 @@ allOf: properties: "#sound-dai-cells": const: 0 + - if: + properties: + compatible: + contains: + const: snps,designware-i2s + then: + properties: + clocks: + maxItems: 1 + clock-names: + maxItems: 1 + resets: + maxItems: 1 + else: + properties: + resets: + minItems: 2 + maxItems: 2 + - if: + properties: + compatible: + contains: + const: starfive,jh7110-i2stx0 + then: + properties: + clocks: + minItems: 5 + maxItems: 5 + clock-names: + minItems: 5 + maxItems: 5 + required: + - resets + - if: + properties: + compatible: + contains: + const: starfive,jh7110-i2stx1 + then: + properties: + clocks: + minItems: 9 + maxItems: 9 + clock-names: + minItems: 9 + maxItems: 9 + required: + - resets + - if: + properties: + compatible: + contains: + const: starfive,jh7110-i2srx + then: + properties: + clocks: + minItems: 9 + maxItems: 9 + clock-names: + minItems: 9 + maxItems: 9 + required: + - resets + - starfive,syscon + else: + properties: + starfive,syscon: false required: - compatible From 63b54b15a9040fe985874b4ea6746c1909cee474 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Mon, 21 Aug 2023 22:41:48 +0800 Subject: [PATCH 58/92] ASoC: dwc: Use ops to get platform data Use of_device_get_match_data() to get platform data. Signed-off-by: Xingyu Wu Link: https://lore.kernel.org/r/20230821144151.207339-3-xingyu.wu@starfivetech.com Signed-off-by: Mark Brown --- sound/soc/dwc/dwc-i2s.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c index 97d652f0e84d3..815ddebf97e85 100644 --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c @@ -633,7 +633,7 @@ static int dw_i2s_dai_probe(struct snd_soc_dai *dai) static int dw_i2s_probe(struct platform_device *pdev) { - const struct i2s_platform_data *pdata = pdev->dev.platform_data; + const struct i2s_platform_data *pdata = of_device_get_match_data(&pdev->dev); struct dw_i2s_dev *dev; struct resource *res; int ret, irq; From 05777a8a384f1f1042c6a0f977b4ce0e5b7625fb Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Mon, 21 Aug 2023 22:41:49 +0800 Subject: [PATCH 59/92] ASoC: dwc: i2s: Add StarFive JH7110 SoC support Add StarFive JH7110(TX0/TX1/RX channels) SoC support in the designware I2S driver and a flag to check if it is on the JH7110 SoC. These channels need to enable clocks, resets and syscon register on the JH7110 SoC. So add init ops in platform data for the JH7110 SoC to do this. Their resets should be deassert before changing the parent of clocks so these are done in the init ops of platform data. The I2S controllers use DMA controller by platform data on the JH7110 and their settings about snd_dmaengine_dai_dma_data() should be added in the dw_configure_dai_by_pd(). And use dmaengine PCM registration if these do not have IRQ on the JH7110 SoC. Signed-off-by: Xingyu Wu Link: https://lore.kernel.org/r/20230821144151.207339-4-xingyu.wu@starfivetech.com Signed-off-by: Mark Brown --- include/sound/designware_i2s.h | 3 + sound/soc/dwc/dwc-i2s.c | 304 ++++++++++++++++++++++++++++++--- sound/soc/dwc/local.h | 1 + 3 files changed, 287 insertions(+), 21 deletions(-) diff --git a/include/sound/designware_i2s.h b/include/sound/designware_i2s.h index 80d275b9ae0de..f6803205a9fb1 100644 --- a/include/sound/designware_i2s.h +++ b/include/sound/designware_i2s.h @@ -21,6 +21,8 @@ struct i2s_clk_config_data { u32 sample_rate; }; +struct dw_i2s_dev; + struct i2s_platform_data { #define DWC_I2S_PLAY (1 << 0) #define DWC_I2S_RECORD (1 << 1) @@ -42,6 +44,7 @@ struct i2s_platform_data { void *capture_dma_data; bool (*filter)(struct dma_chan *chan, void *slave); int (*i2s_clk_cfg)(struct i2s_clk_config_data *config); + int (*i2s_pd_init)(struct dw_i2s_dev *dev); }; struct i2s_dma_data { diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c index 815ddebf97e85..dabe23b58f493 100644 --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -190,7 +191,8 @@ static void i2s_start(struct dw_i2s_dev *dev, else i2s_write_reg(dev->i2s_base, IRER, 1); - if (dev->use_pio) + /* I2S needs to enable IRQ to make a handshake with DMAC on the JH7110 SoC */ + if (dev->use_pio || dev->is_jh7110) i2s_enable_irqs(dev, substream->stream, config->chan_nr); else i2s_enable_dma(dev, substream->stream); @@ -208,7 +210,7 @@ static void i2s_stop(struct dw_i2s_dev *dev, else i2s_write_reg(dev->i2s_base, IRER, 0); - if (dev->use_pio) + if (dev->use_pio || dev->is_jh7110) i2s_disable_irqs(dev, substream->stream, 8); else i2s_disable_dma(dev, substream->stream); @@ -219,6 +221,21 @@ static void i2s_stop(struct dw_i2s_dev *dev, } } +static int dw_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); + + if (dev->is_jh7110) { + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC; + } + + return 0; +} + static void dw_i2s_config(struct dw_i2s_dev *dev, int stream) { u32 ch_reg; @@ -388,6 +405,7 @@ static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) } static const struct snd_soc_dai_ops dw_i2s_dai_ops = { + .startup = dw_i2s_startup, .hw_params = dw_i2s_hw_params, .prepare = dw_i2s_prepare, .trigger = dw_i2s_trigger, @@ -571,17 +589,39 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE) idx = 1; - /* Set DMA slaves info */ - dev->play_dma_data.pd.data = pdata->play_dma_data; - dev->capture_dma_data.pd.data = pdata->capture_dma_data; - dev->play_dma_data.pd.addr = res->start + I2S_TXDMA; - dev->capture_dma_data.pd.addr = res->start + I2S_RXDMA; - dev->play_dma_data.pd.max_burst = 16; - dev->capture_dma_data.pd.max_burst = 16; - dev->play_dma_data.pd.addr_width = bus_widths[idx]; - dev->capture_dma_data.pd.addr_width = bus_widths[idx]; - dev->play_dma_data.pd.filter = pdata->filter; - dev->capture_dma_data.pd.filter = pdata->filter; + + if (dev->is_jh7110) { + /* Use platform data and snd_dmaengine_dai_dma_data struct at the same time */ + u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2); + u32 idx2; + + if (COMP1_TX_ENABLED(comp1)) { + idx2 = COMP1_TX_WORDSIZE_0(comp1); + dev->play_dma_data.dt.addr = res->start + I2S_TXDMA; + dev->play_dma_data.dt.fifo_size = dev->fifo_th * 2 * + (fifo_width[idx2]) >> 8; + dev->play_dma_data.dt.maxburst = 16; + } + if (COMP1_RX_ENABLED(comp1)) { + idx2 = COMP2_RX_WORDSIZE_0(comp2); + dev->capture_dma_data.dt.addr = res->start + I2S_RXDMA; + dev->capture_dma_data.dt.fifo_size = dev->fifo_th * 2 * + (fifo_width[idx2] >> 8); + dev->capture_dma_data.dt.maxburst = 16; + } + } else { + /* Set DMA slaves info */ + dev->play_dma_data.pd.data = pdata->play_dma_data; + dev->capture_dma_data.pd.data = pdata->capture_dma_data; + dev->play_dma_data.pd.addr = res->start + I2S_TXDMA; + dev->capture_dma_data.pd.addr = res->start + I2S_RXDMA; + dev->play_dma_data.pd.max_burst = 16; + dev->capture_dma_data.pd.max_burst = 16; + dev->play_dma_data.pd.addr_width = bus_widths[idx]; + dev->capture_dma_data.pd.addr_width = bus_widths[idx]; + dev->play_dma_data.pd.filter = pdata->filter; + dev->capture_dma_data.pd.filter = pdata->filter; + } return 0; } @@ -631,6 +671,190 @@ static int dw_i2s_dai_probe(struct snd_soc_dai *dai) return 0; } +/* clocks initialization with master mode on JH7110 SoC */ +static int jh7110_i2s_crg_master_init(struct dw_i2s_dev *dev) +{ + static struct clk_bulk_data clks[] = { + { .id = "mclk" }, + { .id = "mclk_ext" }, + { .id = "mclk_inner" }, + { .id = "apb" }, + { .id = "i2sclk" }, + }; + struct reset_control *resets = devm_reset_control_array_get_exclusive(dev->dev); + int ret; + struct clk *pclk; + struct clk *bclk_mst; + struct clk *mclk; + struct clk *mclk_ext; + struct clk *mclk_inner; + + if (IS_ERR(resets)) + return dev_err_probe(dev->dev, PTR_ERR(resets), "failed to get i2s resets\n"); + + ret = clk_bulk_get(dev->dev, ARRAY_SIZE(clks), clks); + if (ret) + return dev_err_probe(dev->dev, ret, "failed to get i2s clocks\n"); + + mclk = clks[0].clk; + mclk_ext = clks[1].clk; + mclk_inner = clks[2].clk; + pclk = clks[3].clk; + bclk_mst = clks[4].clk; + + ret = clk_prepare_enable(pclk); + if (ret) + goto exit; + + /* Use inner mclk first and avoid uninitialized gpio for external mclk */ + ret = clk_set_parent(mclk, mclk_inner); + if (ret) + goto err_dis_pclk; + + ret = clk_prepare_enable(bclk_mst); + if (ret) + goto err_dis_pclk; + + /* deassert resets before set clock parent */ + ret = reset_control_deassert(resets); + if (ret) + goto err_dis_all; + + /* external clock (12.288MHz) for Audio */ + ret = clk_set_parent(mclk, mclk_ext); + if (ret) + goto err_dis_all; + + /* i2sclk will be got and enabled repeatedly later and should be disabled now. */ + clk_disable_unprepare(bclk_mst); + clk_bulk_put(ARRAY_SIZE(clks), clks); + dev->is_jh7110 = true; + + return 0; + +err_dis_all: + clk_disable_unprepare(bclk_mst); +err_dis_pclk: + clk_disable_unprepare(pclk); +exit: + clk_bulk_put(ARRAY_SIZE(clks), clks); + return ret; +} + +/* clocks initialization with slave mode on JH7110 SoC */ +static int jh7110_i2s_crg_slave_init(struct dw_i2s_dev *dev) +{ + static struct clk_bulk_data clks[] = { + { .id = "mclk" }, + { .id = "mclk_ext" }, + { .id = "apb" }, + { .id = "bclk_ext" }, + { .id = "lrck_ext" }, + { .id = "bclk" }, + { .id = "lrck" }, + { .id = "mclk_inner" }, + { .id = "i2sclk" }, + }; + struct reset_control *resets = devm_reset_control_array_get_exclusive(dev->dev); + int ret; + struct clk *pclk; + struct clk *bclk_mst; + struct clk *bclk_ext; + struct clk *lrck_ext; + struct clk *bclk; + struct clk *lrck; + struct clk *mclk; + struct clk *mclk_ext; + struct clk *mclk_inner; + + if (IS_ERR(resets)) + return dev_err_probe(dev->dev, PTR_ERR(resets), "failed to get i2s resets\n"); + + ret = clk_bulk_get(dev->dev, ARRAY_SIZE(clks), clks); + if (ret) + return dev_err_probe(dev->dev, ret, "failed to get i2s clocks\n"); + + mclk = clks[0].clk; + mclk_ext = clks[1].clk; + pclk = clks[2].clk; + bclk_ext = clks[3].clk; + lrck_ext = clks[4].clk; + bclk = clks[5].clk; + lrck = clks[6].clk; + mclk_inner = clks[7].clk; + bclk_mst = clks[8].clk; + + ret = clk_prepare_enable(pclk); + if (ret) + goto exit; + + ret = clk_set_parent(mclk, mclk_inner); + if (ret) + goto err_dis_pclk; + + ret = clk_prepare_enable(bclk_mst); + if (ret) + goto err_dis_pclk; + + ret = reset_control_deassert(resets); + if (ret) + goto err_dis_all; + + /* The sources of BCLK and LRCK are the external codec. */ + ret = clk_set_parent(bclk, bclk_ext); + if (ret) + goto err_dis_all; + + ret = clk_set_parent(lrck, lrck_ext); + if (ret) + goto err_dis_all; + + ret = clk_set_parent(mclk, mclk_ext); + if (ret) + goto err_dis_all; + + /* The i2sclk will be got and enabled repeatedly later and should be disabled now. */ + clk_disable_unprepare(bclk_mst); + clk_bulk_put(ARRAY_SIZE(clks), clks); + dev->is_jh7110 = true; + + return 0; + +err_dis_all: + clk_disable_unprepare(bclk_mst); +err_dis_pclk: + clk_disable_unprepare(pclk); +exit: + clk_bulk_put(ARRAY_SIZE(clks), clks); + return ret; +} + +/* Special syscon initialization about RX channel with slave mode on JH7110 SoC */ +static int jh7110_i2srx_crg_init(struct dw_i2s_dev *dev) +{ + struct regmap *regmap; + unsigned int args[2]; + + regmap = syscon_regmap_lookup_by_phandle_args(dev->dev->of_node, + "starfive,syscon", + 2, args); + if (IS_ERR(regmap)) + return dev_err_probe(dev->dev, PTR_ERR(regmap), "getting the regmap failed\n"); + + /* Enable I2Srx with syscon register, args[0]: offset, args[1]: mask */ + regmap_update_bits(regmap, args[0], args[1], args[1]); + + return jh7110_i2s_crg_slave_init(dev); +} + +static int jh7110_i2stx0_clk_cfg(struct i2s_clk_config_data *config) +{ + struct dw_i2s_dev *dev = container_of(config, struct dw_i2s_dev, config); + u32 bclk_rate = config->sample_rate * 64; + + return clk_set_rate(dev->clk, bclk_rate); +} + static int dw_i2s_probe(struct platform_device *pdev) { const struct i2s_platform_data *pdata = of_device_get_match_data(&pdev->dev); @@ -655,15 +879,25 @@ static int dw_i2s_probe(struct platform_device *pdev) if (IS_ERR(dev->i2s_base)) return PTR_ERR(dev->i2s_base); - dev->reset = devm_reset_control_array_get_optional_shared(&pdev->dev); - if (IS_ERR(dev->reset)) - return PTR_ERR(dev->reset); + dev->dev = &pdev->dev; + dev->is_jh7110 = false; + if (pdata) { + if (pdata->i2s_pd_init) { + ret = pdata->i2s_pd_init(dev); + if (ret) + return ret; + } + } - ret = reset_control_deassert(dev->reset); - if (ret) - return ret; + if (!dev->is_jh7110) { + dev->reset = devm_reset_control_array_get_optional_shared(&pdev->dev); + if (IS_ERR(dev->reset)) + return PTR_ERR(dev->reset); - dev->dev = &pdev->dev; + ret = reset_control_deassert(dev->reset); + if (ret) + return ret; + } irq = platform_get_irq_optional(pdev, 0); if (irq >= 0) { @@ -722,7 +956,7 @@ static int dw_i2s_probe(struct platform_device *pdev) goto err_clk_disable; } - if (!pdata) { + if (!pdata || dev->is_jh7110) { if (irq >= 0) { ret = dw_pcm_register(pdev); dev->use_pio = true; @@ -762,8 +996,36 @@ static void dw_i2s_remove(struct platform_device *pdev) } #ifdef CONFIG_OF +static const struct i2s_platform_data jh7110_i2stx0_data = { + .cap = DWC_I2S_PLAY | DW_I2S_MASTER, + .channel = TWO_CHANNEL_SUPPORT, + .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, + .snd_rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .i2s_clk_cfg = jh7110_i2stx0_clk_cfg, + .i2s_pd_init = jh7110_i2s_crg_master_init, +}; + +static const struct i2s_platform_data jh7110_i2stx1_data = { + .cap = DWC_I2S_PLAY | DW_I2S_SLAVE, + .channel = TWO_CHANNEL_SUPPORT, + .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, + .snd_rates = SNDRV_PCM_RATE_8000_192000, + .i2s_pd_init = jh7110_i2s_crg_slave_init, +}; + +static const struct i2s_platform_data jh7110_i2srx_data = { + .cap = DWC_I2S_RECORD | DW_I2S_SLAVE, + .channel = TWO_CHANNEL_SUPPORT, + .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, + .snd_rates = SNDRV_PCM_RATE_8000_192000, + .i2s_pd_init = jh7110_i2srx_crg_init, +}; + static const struct of_device_id dw_i2s_of_match[] = { { .compatible = "snps,designware-i2s", }, + { .compatible = "starfive,jh7110-i2stx0", .data = &jh7110_i2stx0_data, }, + { .compatible = "starfive,jh7110-i2stx1", .data = &jh7110_i2stx1_data,}, + { .compatible = "starfive,jh7110-i2srx", .data = &jh7110_i2srx_data,}, {}, }; diff --git a/sound/soc/dwc/local.h b/sound/soc/dwc/local.h index ba4e397099be4..404ca78f3a7ed 100644 --- a/sound/soc/dwc/local.h +++ b/sound/soc/dwc/local.h @@ -105,6 +105,7 @@ struct dw_i2s_dev { u32 ccr; u32 xfer_resolution; u32 fifo_th; + bool is_jh7110; /* Flag for StarFive JH7110 SoC */ /* data related to DMA transfers b/w i2s and DMAC */ union dw_i2s_snd_dma_data play_dma_data; From dace2842a8dd451aa1fecedd608ebc024892ac1a Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Wed, 2 Aug 2023 16:43:00 +0800 Subject: [PATCH 60/92] riscv: dts: starfive: pinfunc: Fix the pins name of I2STX1 These pins are actually I2STX1 clock input, not I2STX0, so their names should be changed. Signed-off-by: Xingyu Wu --- arch/riscv/boot/dts/starfive/jh7110-pinfunc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/boot/dts/starfive/jh7110-pinfunc.h b/arch/riscv/boot/dts/starfive/jh7110-pinfunc.h index fb0139b56723a..256de17f52611 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-pinfunc.h +++ b/arch/riscv/boot/dts/starfive/jh7110-pinfunc.h @@ -240,8 +240,8 @@ #define GPI_SYS_MCLK_EXT 30 #define GPI_SYS_I2SRX_BCLK 31 #define GPI_SYS_I2SRX_LRCK 32 -#define GPI_SYS_I2STX0_BCLK 33 -#define GPI_SYS_I2STX0_LRCK 34 +#define GPI_SYS_I2STX1_BCLK 33 +#define GPI_SYS_I2STX1_LRCK 34 #define GPI_SYS_TDM_CLK 35 #define GPI_SYS_TDM_RXD 36 #define GPI_SYS_TDM_SYNC 37 From 1274e1dafa2e154f66f1c66b7f3034721ceffd39 Mon Sep 17 00:00:00 2001 From: Changhuang Liang Date: Thu, 18 May 2023 23:01:59 -0700 Subject: [PATCH 61/92] dt-bindings: power: Add power-domain header for JH7110 Add power-domain header for JH7110 SoC, it can use to operate dphy power. Signed-off-by: Changhuang Liang Signed-off-by: Conor Dooley --- include/dt-bindings/power/starfive,jh7110-pmu.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/dt-bindings/power/starfive,jh7110-pmu.h b/include/dt-bindings/power/starfive,jh7110-pmu.h index 132bfe401fc89..341e2a0676ba0 100644 --- a/include/dt-bindings/power/starfive,jh7110-pmu.h +++ b/include/dt-bindings/power/starfive,jh7110-pmu.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ /* - * Copyright (C) 2022 StarFive Technology Co., Ltd. + * Copyright (C) 2022-2023 StarFive Technology Co., Ltd. * Author: Walker Chen */ #ifndef __DT_BINDINGS_POWER_JH7110_POWER_H__ @@ -14,4 +14,7 @@ #define JH7110_PD_ISP 5 #define JH7110_PD_VENC 6 +#define JH7110_PD_DPHY_TX 0 +#define JH7110_PD_DPHY_RX 1 + #endif From 0299dffe1a392bc22150624729ab35aadfcc096c Mon Sep 17 00:00:00 2001 From: Changhuang Liang Date: Thu, 18 May 2023 23:02:00 -0700 Subject: [PATCH 62/92] soc: starfive: Replace SOC_STARFIVE with ARCH_STARFIVE Using ARCH_FOO symbol is preferred than SOC_FOO. Reviewed-by: Conor Dooley Reviewed-by: Walker Chen Signed-off-by: Changhuang Liang Signed-off-by: Conor Dooley --- drivers/soc/starfive/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soc/starfive/Kconfig b/drivers/soc/starfive/Kconfig index bdb96dc4c9891..1e9b0c414fec7 100644 --- a/drivers/soc/starfive/Kconfig +++ b/drivers/soc/starfive/Kconfig @@ -3,8 +3,8 @@ config JH71XX_PMU bool "Support PMU for StarFive JH71XX Soc" depends on PM - depends on SOC_STARFIVE || COMPILE_TEST - default SOC_STARFIVE + depends on ARCH_STARFIVE || COMPILE_TEST + default ARCH_STARFIVE select PM_GENERIC_DOMAINS help Say 'y' here to enable support power domain support. From 6fd2e3d22dc938fafc1c559adff887b457f77cb1 Mon Sep 17 00:00:00 2001 From: Changhuang Liang Date: Thu, 18 May 2023 23:02:01 -0700 Subject: [PATCH 63/92] soc: starfive: Extract JH7110 pmu private operations Move JH7110 private operation into private data of compatible. Convenient to add AON PMU which would not have interrupts property. Signed-off-by: Changhuang Liang Reviewed-by: Walker Chen Signed-off-by: Conor Dooley --- drivers/soc/starfive/jh71xx_pmu.c | 89 +++++++++++++++++++++---------- 1 file changed, 62 insertions(+), 27 deletions(-) diff --git a/drivers/soc/starfive/jh71xx_pmu.c b/drivers/soc/starfive/jh71xx_pmu.c index 7d5f50d71c0dd..0dbdcc0d2c91e 100644 --- a/drivers/soc/starfive/jh71xx_pmu.c +++ b/drivers/soc/starfive/jh71xx_pmu.c @@ -51,9 +51,17 @@ struct jh71xx_domain_info { u8 bit; }; +struct jh71xx_pmu; +struct jh71xx_pmu_dev; + struct jh71xx_pmu_match_data { const struct jh71xx_domain_info *domain_info; int num_domains; + unsigned int pmu_status; + int (*pmu_parse_irq)(struct platform_device *pdev, + struct jh71xx_pmu *pmu); + int (*pmu_set_state)(struct jh71xx_pmu_dev *pmd, + u32 mask, bool on); }; struct jh71xx_pmu { @@ -79,12 +87,12 @@ static int jh71xx_pmu_get_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool *is_o if (!mask) return -EINVAL; - *is_on = readl(pmu->base + JH71XX_PMU_CURR_POWER_MODE) & mask; + *is_on = readl(pmu->base + pmu->match_data->pmu_status) & mask; return 0; } -static int jh71xx_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) +static int jh7110_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) { struct jh71xx_pmu *pmu = pmd->pmu; unsigned long flags; @@ -92,22 +100,8 @@ static int jh71xx_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) u32 mode; u32 encourage_lo; u32 encourage_hi; - bool is_on; int ret; - ret = jh71xx_pmu_get_state(pmd, mask, &is_on); - if (ret) { - dev_dbg(pmu->dev, "unable to get current state for %s\n", - pmd->genpd.name); - return ret; - } - - if (is_on == on) { - dev_dbg(pmu->dev, "pm domain [%s] is already %sable status.\n", - pmd->genpd.name, on ? "en" : "dis"); - return 0; - } - spin_lock_irqsave(&pmu->lock, flags); /* @@ -166,6 +160,29 @@ static int jh71xx_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) return 0; } +static int jh71xx_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) +{ + struct jh71xx_pmu *pmu = pmd->pmu; + const struct jh71xx_pmu_match_data *match_data = pmu->match_data; + bool is_on; + int ret; + + ret = jh71xx_pmu_get_state(pmd, mask, &is_on); + if (ret) { + dev_dbg(pmu->dev, "unable to get current state for %s\n", + pmd->genpd.name); + return ret; + } + + if (is_on == on) { + dev_dbg(pmu->dev, "pm domain [%s] is already %sable status.\n", + pmd->genpd.name, on ? "en" : "dis"); + return 0; + } + + return match_data->pmu_set_state(pmd, mask, on); +} + static int jh71xx_pmu_on(struct generic_pm_domain *genpd) { struct jh71xx_pmu_dev *pmd = container_of(genpd, @@ -226,6 +243,25 @@ static irqreturn_t jh71xx_pmu_interrupt(int irq, void *data) return IRQ_HANDLED; } +static int jh7110_pmu_parse_irq(struct platform_device *pdev, struct jh71xx_pmu *pmu) +{ + struct device *dev = &pdev->dev; + int ret; + + pmu->irq = platform_get_irq(pdev, 0); + if (pmu->irq < 0) + return pmu->irq; + + ret = devm_request_irq(dev, pmu->irq, jh71xx_pmu_interrupt, + 0, pdev->name, pmu); + if (ret) + dev_err(dev, "failed to request irq\n"); + + jh71xx_pmu_int_enable(pmu, JH71XX_PMU_INT_ALL_MASK & ~JH71XX_PMU_INT_PCH_FAIL, true); + + return 0; +} + static int jh71xx_pmu_init_domain(struct jh71xx_pmu *pmu, int index) { struct jh71xx_pmu_dev *pmd; @@ -275,19 +311,18 @@ static int jh71xx_pmu_probe(struct platform_device *pdev) if (IS_ERR(pmu->base)) return PTR_ERR(pmu->base); - pmu->irq = platform_get_irq(pdev, 0); - if (pmu->irq < 0) - return pmu->irq; - - ret = devm_request_irq(dev, pmu->irq, jh71xx_pmu_interrupt, - 0, pdev->name, pmu); - if (ret) - dev_err(dev, "failed to request irq\n"); + spin_lock_init(&pmu->lock); match_data = of_device_get_match_data(dev); if (!match_data) return -EINVAL; + ret = match_data->pmu_parse_irq(pdev, pmu); + if (ret) { + dev_err(dev, "failed to parse irq\n"); + return ret; + } + pmu->genpd = devm_kcalloc(dev, match_data->num_domains, sizeof(struct generic_pm_domain *), GFP_KERNEL); @@ -307,9 +342,6 @@ static int jh71xx_pmu_probe(struct platform_device *pdev) } } - spin_lock_init(&pmu->lock); - jh71xx_pmu_int_enable(pmu, JH71XX_PMU_INT_ALL_MASK & ~JH71XX_PMU_INT_PCH_FAIL, true); - ret = of_genpd_add_provider_onecell(np, &pmu->genpd_data); if (ret) { dev_err(dev, "failed to register genpd driver: %d\n", ret); @@ -357,6 +389,9 @@ static const struct jh71xx_domain_info jh7110_power_domains[] = { static const struct jh71xx_pmu_match_data jh7110_pmu = { .num_domains = ARRAY_SIZE(jh7110_power_domains), .domain_info = jh7110_power_domains, + .pmu_status = JH71XX_PMU_CURR_POWER_MODE, + .pmu_parse_irq = jh7110_pmu_parse_irq, + .pmu_set_state = jh7110_pmu_set_state, }; static const struct of_device_id jh71xx_pmu_of_match[] = { From c17d77a198dc5dd32a4f83f5b37ea1d8b42a69ed Mon Sep 17 00:00:00 2001 From: Changhuang Liang Date: Thu, 18 May 2023 23:02:02 -0700 Subject: [PATCH 64/92] soc: starfive: Add JH7110 AON PMU support Add AON PMU for StarFive JH7110 SoC. It can be used to turn on/off the dphy rx/tx power switch. Reviewed-by: Walker Chen Signed-off-by: Changhuang Liang Signed-off-by: Conor Dooley --- drivers/soc/starfive/jh71xx_pmu.c | 57 ++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/drivers/soc/starfive/jh71xx_pmu.c b/drivers/soc/starfive/jh71xx_pmu.c index 0dbdcc0d2c91e..c7b474409cf74 100644 --- a/drivers/soc/starfive/jh71xx_pmu.c +++ b/drivers/soc/starfive/jh71xx_pmu.c @@ -2,7 +2,7 @@ /* * StarFive JH71XX PMU (Power Management Unit) Controller Driver * - * Copyright (C) 2022 StarFive Technology Co., Ltd. + * Copyright (C) 2022-2023 StarFive Technology Co., Ltd. */ #include @@ -24,6 +24,9 @@ #define JH71XX_PMU_EVENT_STATUS 0x88 #define JH71XX_PMU_INT_STATUS 0x8C +/* aon pmu register offset */ +#define JH71XX_AON_PMU_SWITCH 0x00 + /* sw encourage cfg */ #define JH71XX_PMU_SW_ENCOURAGE_EN_LO 0x05 #define JH71XX_PMU_SW_ENCOURAGE_EN_HI 0x50 @@ -160,6 +163,26 @@ static int jh7110_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) return 0; } +static int jh7110_aon_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) +{ + struct jh71xx_pmu *pmu = pmd->pmu; + unsigned long flags; + u32 val; + + spin_lock_irqsave(&pmu->lock, flags); + val = readl(pmu->base + JH71XX_AON_PMU_SWITCH); + + if (on) + val |= mask; + else + val &= ~mask; + + writel(val, pmu->base + JH71XX_AON_PMU_SWITCH); + spin_unlock_irqrestore(&pmu->lock, flags); + + return 0; +} + static int jh71xx_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) { struct jh71xx_pmu *pmu = pmd->pmu; @@ -317,10 +340,12 @@ static int jh71xx_pmu_probe(struct platform_device *pdev) if (!match_data) return -EINVAL; - ret = match_data->pmu_parse_irq(pdev, pmu); - if (ret) { - dev_err(dev, "failed to parse irq\n"); - return ret; + if (match_data->pmu_parse_irq) { + ret = match_data->pmu_parse_irq(pdev, pmu); + if (ret) { + dev_err(dev, "failed to parse irq\n"); + return ret; + } } pmu->genpd = devm_kcalloc(dev, match_data->num_domains, @@ -394,10 +419,31 @@ static const struct jh71xx_pmu_match_data jh7110_pmu = { .pmu_set_state = jh7110_pmu_set_state, }; +static const struct jh71xx_domain_info jh7110_aon_power_domains[] = { + [JH7110_PD_DPHY_TX] = { + .name = "DPHY-TX", + .bit = 30, + }, + [JH7110_PD_DPHY_RX] = { + .name = "DPHY-RX", + .bit = 31, + }, +}; + +static const struct jh71xx_pmu_match_data jh7110_aon_pmu = { + .num_domains = ARRAY_SIZE(jh7110_aon_power_domains), + .domain_info = jh7110_aon_power_domains, + .pmu_status = JH71XX_AON_PMU_SWITCH, + .pmu_set_state = jh7110_aon_pmu_set_state, +}; + static const struct of_device_id jh71xx_pmu_of_match[] = { { .compatible = "starfive,jh7110-pmu", .data = (void *)&jh7110_pmu, + }, { + .compatible = "starfive,jh7110-aon-syscon", + .data = (void *)&jh7110_aon_pmu, }, { /* sentinel */ } @@ -414,5 +460,6 @@ static struct platform_driver jh71xx_pmu_driver = { builtin_platform_driver(jh71xx_pmu_driver); MODULE_AUTHOR("Walker Chen "); +MODULE_AUTHOR("Changhuang Liang "); MODULE_DESCRIPTION("StarFive JH71XX PMU Driver"); MODULE_LICENSE("GPL"); From e1532896aeb2f7a6b041afdf5d7e7c603d5bb364 Mon Sep 17 00:00:00 2001 From: Changhuang Liang Date: Tue, 18 Jul 2023 00:08:02 -0700 Subject: [PATCH 65/92] dt-bindings: phy: Add starfive,jh7110-dphy-rx StarFive SoCs like the jh7110 use a MIPI D-PHY RX controller based on a M31 IP. Add a binding for it. Signed-off-by: Changhuang Liang Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20230718070803.16660-2-changhuang.liang@starfivetech.com Signed-off-by: Vinod Koul --- .../bindings/phy/starfive,jh7110-dphy-rx.yaml | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/starfive,jh7110-dphy-rx.yaml diff --git a/Documentation/devicetree/bindings/phy/starfive,jh7110-dphy-rx.yaml b/Documentation/devicetree/bindings/phy/starfive,jh7110-dphy-rx.yaml new file mode 100644 index 0000000000000..7224cde6fce01 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/starfive,jh7110-dphy-rx.yaml @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/starfive,jh7110-dphy-rx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive SoC JH7110 MIPI D-PHY Rx Controller + +maintainers: + - Jack Zhu + - Changhuang Liang + +description: + StarFive SoCs contain a MIPI CSI D-PHY based on M31 IP, used to + transfer CSI camera data. + +properties: + compatible: + const: starfive,jh7110-dphy-rx + + reg: + maxItems: 1 + + clocks: + items: + - description: config clock + - description: reference clock + - description: escape mode transmit clock + + clock-names: + items: + - const: cfg + - const: ref + - const: tx + + resets: + items: + - description: DPHY_HW reset + - description: DPHY_B09_ALWAYS_ON reset + + power-domains: + maxItems: 1 + + "#phy-cells": + const: 0 + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - power-domains + - "#phy-cells" + +additionalProperties: false + +examples: + - | + phy@19820000 { + compatible = "starfive,jh7110-dphy-rx"; + reg = <0x19820000 0x10000>; + clocks = <&ispcrg 3>, + <&ispcrg 4>, + <&ispcrg 5>; + clock-names = "cfg", "ref", "tx"; + resets = <&ispcrg 2>, + <&ispcrg 3>; + power-domains = <&aon_syscon 1>; + #phy-cells = <0>; + }; From 6353ced6c3e306b270aebdaa9c432aabfe0182de Mon Sep 17 00:00:00 2001 From: Changhuang Liang Date: Tue, 18 Jul 2023 00:08:03 -0700 Subject: [PATCH 66/92] phy: starfive: Add mipi dphy rx support Add mipi dphy rx support for the StarFive JH7110 SoC. It is used to transfer CSI camera data. Signed-off-by: Changhuang Liang Reviewed-by: Minda Chen Link: https://lore.kernel.org/r/20230718070803.16660-3-changhuang.liang@starfivetech.com Signed-off-by: Vinod Koul --- drivers/phy/starfive/Kconfig | 9 + drivers/phy/starfive/Makefile | 5 +- drivers/phy/starfive/phy-jh7110-dphy-rx.c | 232 ++++++++++++++++++++++ 3 files changed, 244 insertions(+), 2 deletions(-) create mode 100644 drivers/phy/starfive/phy-jh7110-dphy-rx.c diff --git a/drivers/phy/starfive/Kconfig b/drivers/phy/starfive/Kconfig index da9a98cdf7e39..a560533a674ee 100644 --- a/drivers/phy/starfive/Kconfig +++ b/drivers/phy/starfive/Kconfig @@ -3,6 +3,15 @@ # Phy drivers for StarFive platforms # +config PHY_STARFIVE_JH7110_DPHY_RX + tristate "StarFive JH7110 D-PHY RX support" + select GENERIC_PHY + select GENERIC_PHY_MIPI_DPHY + help + Choose this option if you have a StarFive D-PHY in your + system. If M is selected, the module will be called + phy-jh7110-dphy-rx.ko. + config PHY_STARFIVE_JH7110_PCIE tristate "Starfive JH7110 PCIE 2.0/USB 3.0 PHY support" depends on HAS_IOMEM diff --git a/drivers/phy/starfive/Makefile b/drivers/phy/starfive/Makefile index 1c62d93e32805..b391018b7c471 100644 --- a/drivers/phy/starfive/Makefile +++ b/drivers/phy/starfive/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_PHY_STARFIVE_JH7110_PCIE) += phy-jh7110-pcie.o -obj-$(CONFIG_PHY_STARFIVE_JH7110_USB) += phy-jh7110-usb.o +obj-$(CONFIG_PHY_STARFIVE_JH7110_DPHY_RX) += phy-jh7110-dphy-rx.o +obj-$(CONFIG_PHY_STARFIVE_JH7110_PCIE) += phy-jh7110-pcie.o +obj-$(CONFIG_PHY_STARFIVE_JH7110_USB) += phy-jh7110-usb.o diff --git a/drivers/phy/starfive/phy-jh7110-dphy-rx.c b/drivers/phy/starfive/phy-jh7110-dphy-rx.c new file mode 100644 index 0000000000000..037a9e0263cda --- /dev/null +++ b/drivers/phy/starfive/phy-jh7110-dphy-rx.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * StarFive JH7110 DPHY RX driver + * + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * Author: Jack Zhu + * Author: Changhuang Liang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STF_DPHY_APBCFGSAIF_SYSCFG(x) (x) + +#define STF_DPHY_ENABLE_CLK BIT(6) +#define STF_DPHY_ENABLE_CLK1 BIT(7) +#define STF_DPHY_ENABLE_LAN0 BIT(8) +#define STF_DPHY_ENABLE_LAN1 BIT(9) +#define STF_DPHY_ENABLE_LAN2 BIT(10) +#define STF_DPHY_ENABLE_LAN3 BIT(11) +#define STF_DPHY_LANE_SWAP_CLK GENMASK(22, 20) +#define STF_DPHY_LANE_SWAP_CLK1 GENMASK(25, 23) +#define STF_DPHY_LANE_SWAP_LAN0 GENMASK(28, 26) +#define STF_DPHY_LANE_SWAP_LAN1 GENMASK(31, 29) + +#define STF_DPHY_LANE_SWAP_LAN2 GENMASK(2, 0) +#define STF_DPHY_LANE_SWAP_LAN3 GENMASK(5, 3) +#define STF_DPHY_PLL_CLK_SEL GENMASK(21, 12) +#define STF_DPHY_PRECOUNTER_IN_CLK GENMASK(29, 22) + +#define STF_DPHY_PRECOUNTER_IN_CLK1 GENMASK(7, 0) +#define STF_DPHY_PRECOUNTER_IN_LAN0 GENMASK(15, 8) +#define STF_DPHY_PRECOUNTER_IN_LAN1 GENMASK(23, 16) +#define STF_DPHY_PRECOUNTER_IN_LAN2 GENMASK(31, 24) + +#define STF_DPHY_PRECOUNTER_IN_LAN3 GENMASK(7, 0) +#define STF_DPHY_RX_1C2C_SEL BIT(8) + +#define STF_MAP_LANES_NUM 6 + +struct regval { + u32 addr; + u32 val; +}; + +struct stf_dphy_info { + /** + * @maps: + * + * Physical lanes and logic lanes mapping table. + * + * The default order is: + * [clk lane0, data lane 0, data lane 1, data lane 2, date lane 3, clk lane 1] + */ + u8 maps[STF_MAP_LANES_NUM]; +}; + +struct stf_dphy { + struct device *dev; + void __iomem *regs; + struct clk *cfg_clk; + struct clk *ref_clk; + struct clk *tx_clk; + struct reset_control *rstc; + struct regulator *mipi_0p9; + struct phy *phy; + const struct stf_dphy_info *info; +}; + +static int stf_dphy_configure(struct phy *phy, union phy_configure_opts *opts) +{ + struct stf_dphy *dphy = phy_get_drvdata(phy); + const struct stf_dphy_info *info = dphy->info; + + writel(FIELD_PREP(STF_DPHY_ENABLE_CLK, 1) | + FIELD_PREP(STF_DPHY_ENABLE_CLK1, 1) | + FIELD_PREP(STF_DPHY_ENABLE_LAN0, 1) | + FIELD_PREP(STF_DPHY_ENABLE_LAN1, 1) | + FIELD_PREP(STF_DPHY_ENABLE_LAN2, 1) | + FIELD_PREP(STF_DPHY_ENABLE_LAN3, 1) | + FIELD_PREP(STF_DPHY_LANE_SWAP_CLK, info->maps[0]) | + FIELD_PREP(STF_DPHY_LANE_SWAP_CLK1, info->maps[5]) | + FIELD_PREP(STF_DPHY_LANE_SWAP_LAN0, info->maps[1]) | + FIELD_PREP(STF_DPHY_LANE_SWAP_LAN1, info->maps[2]), + dphy->regs + STF_DPHY_APBCFGSAIF_SYSCFG(188)); + + writel(FIELD_PREP(STF_DPHY_LANE_SWAP_LAN2, info->maps[3]) | + FIELD_PREP(STF_DPHY_LANE_SWAP_LAN3, info->maps[4]) | + FIELD_PREP(STF_DPHY_PRECOUNTER_IN_CLK, 8), + dphy->regs + STF_DPHY_APBCFGSAIF_SYSCFG(192)); + + writel(FIELD_PREP(STF_DPHY_PRECOUNTER_IN_CLK1, 8) | + FIELD_PREP(STF_DPHY_PRECOUNTER_IN_LAN0, 7) | + FIELD_PREP(STF_DPHY_PRECOUNTER_IN_LAN1, 7) | + FIELD_PREP(STF_DPHY_PRECOUNTER_IN_LAN2, 7), + dphy->regs + STF_DPHY_APBCFGSAIF_SYSCFG(196)); + + writel(FIELD_PREP(STF_DPHY_PRECOUNTER_IN_LAN3, 7), + dphy->regs + STF_DPHY_APBCFGSAIF_SYSCFG(200)); + + return 0; +} + +static int stf_dphy_power_on(struct phy *phy) +{ + struct stf_dphy *dphy = phy_get_drvdata(phy); + int ret; + + ret = pm_runtime_resume_and_get(dphy->dev); + if (ret < 0) + return ret; + + ret = regulator_enable(dphy->mipi_0p9); + if (ret) { + pm_runtime_put(dphy->dev); + return ret; + } + + clk_set_rate(dphy->cfg_clk, 99000000); + clk_set_rate(dphy->ref_clk, 49500000); + clk_set_rate(dphy->tx_clk, 19800000); + reset_control_deassert(dphy->rstc); + + return 0; +} + +static int stf_dphy_power_off(struct phy *phy) +{ + struct stf_dphy *dphy = phy_get_drvdata(phy); + + reset_control_assert(dphy->rstc); + + regulator_disable(dphy->mipi_0p9); + + pm_runtime_put_sync(dphy->dev); + + return 0; +} + +static const struct phy_ops stf_dphy_ops = { + .configure = stf_dphy_configure, + .power_on = stf_dphy_power_on, + .power_off = stf_dphy_power_off, +}; + +static int stf_dphy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct stf_dphy *dphy; + + dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL); + if (!dphy) + return -ENOMEM; + + dphy->info = of_device_get_match_data(&pdev->dev); + + dev_set_drvdata(&pdev->dev, dphy); + dphy->dev = &pdev->dev; + + dphy->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dphy->regs)) + return PTR_ERR(dphy->regs); + + dphy->cfg_clk = devm_clk_get(&pdev->dev, "cfg"); + if (IS_ERR(dphy->cfg_clk)) + return PTR_ERR(dphy->cfg_clk); + + dphy->ref_clk = devm_clk_get(&pdev->dev, "ref"); + if (IS_ERR(dphy->ref_clk)) + return PTR_ERR(dphy->ref_clk); + + dphy->tx_clk = devm_clk_get(&pdev->dev, "tx"); + if (IS_ERR(dphy->tx_clk)) + return PTR_ERR(dphy->tx_clk); + + dphy->rstc = devm_reset_control_array_get_exclusive(&pdev->dev); + if (IS_ERR(dphy->rstc)) + return PTR_ERR(dphy->rstc); + + dphy->mipi_0p9 = devm_regulator_get(&pdev->dev, "mipi_0p9"); + if (IS_ERR(dphy->mipi_0p9)) + return PTR_ERR(dphy->mipi_0p9); + + dphy->phy = devm_phy_create(&pdev->dev, NULL, &stf_dphy_ops); + if (IS_ERR(dphy->phy)) { + dev_err(&pdev->dev, "Failed to create PHY\n"); + return PTR_ERR(dphy->phy); + } + + pm_runtime_enable(&pdev->dev); + + phy_set_drvdata(dphy->phy, dphy); + phy_provider = devm_of_phy_provider_register(&pdev->dev, + of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct stf_dphy_info starfive_dphy_info = { + .maps = {4, 0, 1, 2, 3, 5}, +}; + +static const struct of_device_id stf_dphy_dt_ids[] = { + { + .compatible = "starfive,jh7110-dphy-rx", + .data = &starfive_dphy_info, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, stf_dphy_dt_ids); + +static struct platform_driver stf_dphy_driver = { + .probe = stf_dphy_probe, + .driver = { + .name = "starfive-dphy-rx", + .of_match_table = stf_dphy_dt_ids, + }, +}; +module_platform_driver(stf_dphy_driver); + +MODULE_AUTHOR("Jack Zhu "); +MODULE_AUTHOR("Changhuang Liang "); +MODULE_DESCRIPTION("StarFive JH7110 DPHY RX driver"); +MODULE_LICENSE("GPL"); From c5e5863a9d63b17d9389f69ee0836978c365dda2 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 25 Jul 2023 12:08:56 +0530 Subject: [PATCH 67/92] phy: starfive: make phys depend on HAS_IOMEM the startfive phy drivers use devm_platform_ioremap_resource() which on some archs (s390) is not present. So make the drivers depend on HAS_IOMEM Fixes: f8aa660841bc ("phy: starfive: Add mipi dphy rx support") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202307250509.oeudxG28-lkp@intel.com/ Reviewed-by: Changhuang Liang Signed-off-by: Vinod Koul Link: https://lore.kernel.org/r/20230725063856.482696-1-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/starfive/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/phy/starfive/Kconfig b/drivers/phy/starfive/Kconfig index a560533a674ee..0508f9b123e0a 100644 --- a/drivers/phy/starfive/Kconfig +++ b/drivers/phy/starfive/Kconfig @@ -5,6 +5,7 @@ config PHY_STARFIVE_JH7110_DPHY_RX tristate "StarFive JH7110 D-PHY RX support" + depends on HAS_IOMEM select GENERIC_PHY select GENERIC_PHY_MIPI_DPHY help From 2d909fb2b249f63742fbc59791ec945abbf827a5 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 25 Jul 2023 17:49:27 +0200 Subject: [PATCH 68/92] phy: starfive: StarFive PHYs should depend on ARCH_STARFIVE The various StarFive PHYs are only present on StarFive SoCs. Hence add a dependency on ARCH_STARFIVE, to prevent asking the user about these drivers when configuring a kernel without StarFive SoC support. Signed-off-by: Geert Uytterhoeven Acked-by: Changhuang Liang Link: https://lore.kernel.org/r/12097f6107a18e2f7cfb80f47ac7b27808e062c4.1690300076.git.geert+renesas@glider.be Signed-off-by: Vinod Koul --- drivers/phy/starfive/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/phy/starfive/Kconfig b/drivers/phy/starfive/Kconfig index 0508f9b123e0a..9508e21430110 100644 --- a/drivers/phy/starfive/Kconfig +++ b/drivers/phy/starfive/Kconfig @@ -3,6 +3,8 @@ # Phy drivers for StarFive platforms # +if ARCH_STARFIVE || COMPILE_TEST + config PHY_STARFIVE_JH7110_DPHY_RX tristate "StarFive JH7110 D-PHY RX support" depends on HAS_IOMEM @@ -32,3 +34,5 @@ config PHY_STARFIVE_JH7110_USB used with the Cadence USB controller. If M is selected, the module will be called phy-jh7110-usb.ko. + +endif # ARCH_STARFIVE || COMPILE_TEST From e2e44e4d4f541505e4654c79ddf43fd427931a81 Mon Sep 17 00:00:00 2001 From: Jack Zhu Date: Tue, 23 May 2023 10:56:22 +0200 Subject: [PATCH 69/92] media: dt-bindings: cadence-csi2rx: Convert to DT schema Convert DT bindings document for Cadence MIPI-CSI2 RX controller to DT schema format. For compatible, new compatibles should not be messed with conversion, but the original binding did not specify any SoC-specific compatible string, so add the StarFive compatible string. Reviewed-by: Krzysztof Kozlowski Reviewed-by: Laurent Pinchart Signed-off-by: Jack Zhu Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/cdns,csi2rx.txt | 100 ---------- .../bindings/media/cdns,csi2rx.yaml | 177 ++++++++++++++++++ 2 files changed, 177 insertions(+), 100 deletions(-) delete mode 100644 Documentation/devicetree/bindings/media/cdns,csi2rx.txt create mode 100644 Documentation/devicetree/bindings/media/cdns,csi2rx.yaml diff --git a/Documentation/devicetree/bindings/media/cdns,csi2rx.txt b/Documentation/devicetree/bindings/media/cdns,csi2rx.txt deleted file mode 100644 index 6b02a0657ad97..0000000000000 --- a/Documentation/devicetree/bindings/media/cdns,csi2rx.txt +++ /dev/null @@ -1,100 +0,0 @@ -Cadence MIPI-CSI2 RX controller -=============================== - -The Cadence MIPI-CSI2 RX controller is a CSI-2 bridge supporting up to 4 CSI -lanes in input, and 4 different pixel streams in output. - -Required properties: - - compatible: must be set to "cdns,csi2rx" and an SoC-specific compatible - - reg: base address and size of the memory mapped region - - clocks: phandles to the clocks driving the controller - - clock-names: must contain: - * sys_clk: main clock - * p_clk: register bank clock - * pixel_if[0-3]_clk: pixel stream output clock, one for each stream - implemented in hardware, between 0 and 3 - -Optional properties: - - phys: phandle to the external D-PHY, phy-names must be provided - - phy-names: must contain "dphy", if the implementation uses an - external D-PHY - -Required subnodes: - - ports: A ports node with one port child node per device input and output - port, in accordance with the video interface bindings defined in - Documentation/devicetree/bindings/media/video-interfaces.txt. The - port nodes are numbered as follows: - - Port Description - ----------------------------- - 0 CSI-2 input - 1 Stream 0 output - 2 Stream 1 output - 3 Stream 2 output - 4 Stream 3 output - - The stream output port nodes are optional if they are not - connected to anything at the hardware level or implemented - in the design.Since there is only one endpoint per port, - the endpoints are not numbered. - - -Example: - -csi2rx: csi-bridge@0d060000 { - compatible = "cdns,csi2rx"; - reg = <0x0d060000 0x1000>; - clocks = <&byteclock>, <&byteclock> - <&coreclock>, <&coreclock>, - <&coreclock>, <&coreclock>; - clock-names = "sys_clk", "p_clk", - "pixel_if0_clk", "pixel_if1_clk", - "pixel_if2_clk", "pixel_if3_clk"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - csi2rx_in_sensor: endpoint { - remote-endpoint = <&sensor_out_csi2rx>; - clock-lanes = <0>; - data-lanes = <1 2>; - }; - }; - - port@1 { - reg = <1>; - - csi2rx_out_grabber0: endpoint { - remote-endpoint = <&grabber0_in_csi2rx>; - }; - }; - - port@2 { - reg = <2>; - - csi2rx_out_grabber1: endpoint { - remote-endpoint = <&grabber1_in_csi2rx>; - }; - }; - - port@3 { - reg = <3>; - - csi2rx_out_grabber2: endpoint { - remote-endpoint = <&grabber2_in_csi2rx>; - }; - }; - - port@4 { - reg = <4>; - - csi2rx_out_grabber3: endpoint { - remote-endpoint = <&grabber3_in_csi2rx>; - }; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml b/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml new file mode 100644 index 0000000000000..aba1191b3e779 --- /dev/null +++ b/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml @@ -0,0 +1,177 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/cdns,csi2rx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cadence MIPI-CSI2 RX controller + +maintainers: + - Maxime Ripard + +description: + The Cadence MIPI-CSI2 RX controller is a CSI-2 bridge supporting up to 4 CSI + lanes in input, and 4 different pixel streams in output. + +properties: + compatible: + items: + - enum: + - starfive,jh7110-csi2rx + - const: cdns,csi2rx + + reg: + maxItems: 1 + + clocks: + items: + - description: CSI2Rx system clock + - description: Gated Register bank clock for APB interface + - description: pixel Clock for Stream interface 0 + - description: pixel Clock for Stream interface 1 + - description: pixel Clock for Stream interface 2 + - description: pixel Clock for Stream interface 3 + + clock-names: + items: + - const: sys_clk + - const: p_clk + - const: pixel_if0_clk + - const: pixel_if1_clk + - const: pixel_if2_clk + - const: pixel_if3_clk + + phys: + maxItems: 1 + description: MIPI D-PHY + + phy-names: + items: + - const: dphy + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: + Input port node, single endpoint describing the CSI-2 transmitter. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + properties: + bus-type: + const: 4 + + clock-lanes: + const: 0 + + data-lanes: + minItems: 1 + maxItems: 4 + items: + maximum: 4 + + required: + - data-lanes + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: + Stream 0 Output port node + + port@2: + $ref: /schemas/graph.yaml#/properties/port + description: + Stream 1 Output port node + + port@3: + $ref: /schemas/graph.yaml#/properties/port + description: + Stream 2 Output port node + + port@4: + $ref: /schemas/graph.yaml#/properties/port + description: + Stream 3 Output port node + + required: + - port@0 + +required: + - compatible + - reg + - clocks + - clock-names + - ports + +additionalProperties: false + +examples: + - | + csi@d060000 { + compatible = "starfive,jh7110-csi2rx", "cdns,csi2rx"; + reg = <0x0d060000 0x1000>; + clocks = <&byteclock 7>, <&byteclock 6>, + <&coreclock 8>, <&coreclock 9>, + <&coreclock 10>, <&coreclock 11>; + clock-names = "sys_clk", "p_clk", + "pixel_if0_clk", "pixel_if1_clk", + "pixel_if2_clk", "pixel_if3_clk"; + phys = <&csi_phy>; + phy-names = "dphy"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + csi2rx_in_sensor: endpoint { + remote-endpoint = <&sensor_out_csi2rx>; + clock-lanes = <0>; + data-lanes = <1 2>; + }; + }; + + port@1 { + reg = <1>; + + csi2rx_out_grabber0: endpoint { + remote-endpoint = <&grabber0_in_csi2rx>; + }; + }; + + port@2 { + reg = <2>; + + csi2rx_out_grabber1: endpoint { + remote-endpoint = <&grabber1_in_csi2rx>; + }; + }; + + port@3 { + reg = <3>; + + csi2rx_out_grabber2: endpoint { + remote-endpoint = <&grabber2_in_csi2rx>; + }; + }; + + port@4 { + reg = <4>; + + csi2rx_out_grabber3: endpoint { + remote-endpoint = <&grabber3_in_csi2rx>; + }; + }; + }; + }; + +... From b3693eb927a8a94956602a7522760249b8c87e04 Mon Sep 17 00:00:00 2001 From: Jack Zhu Date: Tue, 23 May 2023 10:56:23 +0200 Subject: [PATCH 70/92] media: dt-bindings: cadence-csi2rx: Add resets property Add resets property for Cadence MIPI-CSI2 RX controller Reviewed-by: Krzysztof Kozlowski Reviewed-by: Laurent Pinchart Signed-off-by: Jack Zhu Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/cdns,csi2rx.yaml | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml b/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml index aba1191b3e779..30a335b107621 100644 --- a/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml +++ b/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml @@ -41,6 +41,24 @@ properties: - const: pixel_if2_clk - const: pixel_if3_clk + resets: + items: + - description: CSI2Rx system reset + - description: Gated Register bank reset for APB interface + - description: pixel reset for Stream interface 0 + - description: pixel reset for Stream interface 1 + - description: pixel reset for Stream interface 2 + - description: pixel reset for Stream interface 3 + + reset-names: + items: + - const: sys + - const: reg_bank + - const: pixel_if0 + - const: pixel_if1 + - const: pixel_if2 + - const: pixel_if3 + phys: maxItems: 1 description: MIPI D-PHY @@ -123,6 +141,12 @@ examples: clock-names = "sys_clk", "p_clk", "pixel_if0_clk", "pixel_if1_clk", "pixel_if2_clk", "pixel_if3_clk"; + resets = <&bytereset 9>, <&bytereset 4>, + <&corereset 5>, <&corereset 6>, + <&corereset 7>, <&corereset 8>; + reset-names = "sys", "reg_bank", + "pixel_if0", "pixel_if1", + "pixel_if2", "pixel_if3"; phys = <&csi_phy>; phy-names = "dphy"; From 858ff5556aaa4b71533f40e9d3543bea36d2bb79 Mon Sep 17 00:00:00 2001 From: Jack Zhu Date: Tue, 23 May 2023 10:56:24 +0200 Subject: [PATCH 71/92] media: cadence: Add operation on reset Add operation on reset for Cadence MIPI-CSI2 RX Controller. Reviewed-by: Laurent Pinchart Signed-off-by: Jack Zhu Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/cadence/cdns-csi2rx.c | 40 +++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index 9755d1c8ceb9b..c9b80ac5cca5a 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -68,6 +69,9 @@ struct csi2rx_priv { struct clk *sys_clk; struct clk *p_clk; struct clk *pixel_clk[CSI2RX_STREAMS_MAX]; + struct reset_control *sys_rst; + struct reset_control *p_rst; + struct reset_control *pixel_rst[CSI2RX_STREAMS_MAX]; struct phy *dphy; u8 lanes[CSI2RX_LANES_MAX]; @@ -112,6 +116,7 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) if (ret) return ret; + reset_control_deassert(csi2rx->p_rst); csi2rx_reset(csi2rx); reg = csi2rx->num_lanes << 8; @@ -154,6 +159,8 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) if (ret) goto err_disable_pixclk; + reset_control_deassert(csi2rx->pixel_rst[i]); + writel(CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF, csi2rx->base + CSI2RX_STREAM_CFG_REG(i)); @@ -169,13 +176,16 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) if (ret) goto err_disable_pixclk; + reset_control_deassert(csi2rx->sys_rst); clk_disable_unprepare(csi2rx->p_clk); return 0; err_disable_pixclk: - for (; i > 0; i--) + for (; i > 0; i--) { + reset_control_assert(csi2rx->pixel_rst[i - 1]); clk_disable_unprepare(csi2rx->pixel_clk[i - 1]); + } err_disable_pclk: clk_disable_unprepare(csi2rx->p_clk); @@ -188,14 +198,17 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx) unsigned int i; clk_prepare_enable(csi2rx->p_clk); + reset_control_assert(csi2rx->sys_rst); clk_disable_unprepare(csi2rx->sys_clk); for (i = 0; i < csi2rx->max_streams; i++) { writel(0, csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); + reset_control_assert(csi2rx->pixel_rst[i]); clk_disable_unprepare(csi2rx->pixel_clk[i]); } + reset_control_assert(csi2rx->p_rst); clk_disable_unprepare(csi2rx->p_clk); if (v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, false)) @@ -299,6 +312,16 @@ static int csi2rx_get_resources(struct csi2rx_priv *csi2rx, return PTR_ERR(csi2rx->p_clk); } + csi2rx->sys_rst = devm_reset_control_get_optional_exclusive(&pdev->dev, + "sys"); + if (IS_ERR(csi2rx->sys_rst)) + return PTR_ERR(csi2rx->sys_rst); + + csi2rx->p_rst = devm_reset_control_get_optional_exclusive(&pdev->dev, + "reg_bank"); + if (IS_ERR(csi2rx->p_rst)) + return PTR_ERR(csi2rx->p_rst); + csi2rx->dphy = devm_phy_optional_get(&pdev->dev, "dphy"); if (IS_ERR(csi2rx->dphy)) { dev_err(&pdev->dev, "Couldn't get external D-PHY\n"); @@ -349,14 +372,21 @@ static int csi2rx_get_resources(struct csi2rx_priv *csi2rx, } for (i = 0; i < csi2rx->max_streams; i++) { - char clk_name[16]; + char name[16]; - snprintf(clk_name, sizeof(clk_name), "pixel_if%u_clk", i); - csi2rx->pixel_clk[i] = devm_clk_get(&pdev->dev, clk_name); + snprintf(name, sizeof(name), "pixel_if%u_clk", i); + csi2rx->pixel_clk[i] = devm_clk_get(&pdev->dev, name); if (IS_ERR(csi2rx->pixel_clk[i])) { - dev_err(&pdev->dev, "Couldn't get clock %s\n", clk_name); + dev_err(&pdev->dev, "Couldn't get clock %s\n", name); return PTR_ERR(csi2rx->pixel_clk[i]); } + + snprintf(name, sizeof(name), "pixel_if%u", i); + csi2rx->pixel_rst[i] = + devm_reset_control_get_optional_exclusive(&pdev->dev, + name); + if (IS_ERR(csi2rx->pixel_rst[i])) + return PTR_ERR(csi2rx->pixel_rst[i]); } return 0; From b4dc0dc1e15254d0000b638b5f78c04f3db8bf02 Mon Sep 17 00:00:00 2001 From: Jack Zhu Date: Tue, 23 May 2023 10:56:25 +0200 Subject: [PATCH 72/92] media: cadence: Add support for external dphy Add support for external MIPI D-PHY. Reviewed-by: Laurent Pinchart Signed-off-by: Jack Zhu Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/cadence/cdns-csi2rx.c | 66 +++++++++++++++++--- 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index c9b80ac5cca5a..a562c27906e15 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -31,6 +31,12 @@ #define CSI2RX_STATIC_CFG_DLANE_MAP(llane, plane) ((plane) << (16 + (llane) * 4)) #define CSI2RX_STATIC_CFG_LANES_MASK GENMASK(11, 8) +#define CSI2RX_DPHY_LANE_CTRL_REG 0x40 +#define CSI2RX_DPHY_CL_RST BIT(16) +#define CSI2RX_DPHY_DL_RST(i) BIT((i) + 12) +#define CSI2RX_DPHY_CL_EN BIT(4) +#define CSI2RX_DPHY_DL_EN(i) BIT(i) + #define CSI2RX_STREAM_BASE(n) (((n) + 1) * 0x100) #define CSI2RX_STREAM_CTRL_REG(n) (CSI2RX_STREAM_BASE(n) + 0x000) @@ -105,6 +111,24 @@ static void csi2rx_reset(struct csi2rx_priv *csi2rx) writel(0, csi2rx->base + CSI2RX_SOFT_RESET_REG); } +static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx) +{ + union phy_configure_opts opts = { }; + int ret; + + ret = phy_power_on(csi2rx->dphy); + if (ret) + return ret; + + ret = phy_configure(csi2rx->dphy, &opts); + if (ret) { + phy_power_off(csi2rx->dphy); + return ret; + } + + return 0; +} + static int csi2rx_start(struct csi2rx_priv *csi2rx) { unsigned int i; @@ -144,6 +168,17 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) if (ret) goto err_disable_pclk; + /* Enable DPHY clk and data lanes. */ + if (csi2rx->dphy) { + reg = CSI2RX_DPHY_CL_EN | CSI2RX_DPHY_CL_RST; + for (i = 0; i < csi2rx->num_lanes; i++) { + reg |= CSI2RX_DPHY_DL_EN(csi2rx->lanes[i] - 1); + reg |= CSI2RX_DPHY_DL_RST(csi2rx->lanes[i] - 1); + } + + writel(reg, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); + } + /* * Create a static mapping between the CSI virtual channels * and the output stream. @@ -177,10 +212,22 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) goto err_disable_pixclk; reset_control_deassert(csi2rx->sys_rst); + + if (csi2rx->dphy) { + ret = csi2rx_configure_ext_dphy(csi2rx); + if (ret) { + dev_err(csi2rx->dev, + "Failed to configure external DPHY: %d\n", ret); + goto err_disable_sysclk; + } + } + clk_disable_unprepare(csi2rx->p_clk); return 0; +err_disable_sysclk: + clk_disable_unprepare(csi2rx->sys_clk); err_disable_pixclk: for (; i > 0; i--) { reset_control_assert(csi2rx->pixel_rst[i - 1]); @@ -213,6 +260,13 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx) if (v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, false)) dev_warn(csi2rx->dev, "Couldn't disable our subdev\n"); + + if (csi2rx->dphy) { + writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); + + if (phy_power_off(csi2rx->dphy)) + dev_warn(csi2rx->dev, "Couldn't power off DPHY\n"); + } } static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable) @@ -328,15 +382,6 @@ static int csi2rx_get_resources(struct csi2rx_priv *csi2rx, return PTR_ERR(csi2rx->dphy); } - /* - * FIXME: Once we'll have external D-PHY support, the check - * will need to be removed. - */ - if (csi2rx->dphy) { - dev_err(&pdev->dev, "External D-PHY not supported yet\n"); - return -EINVAL; - } - ret = clk_prepare_enable(csi2rx->p_clk); if (ret) { dev_err(&pdev->dev, "Couldn't prepare and enable P clock\n"); @@ -366,7 +411,7 @@ static int csi2rx_get_resources(struct csi2rx_priv *csi2rx, * FIXME: Once we'll have internal D-PHY support, the check * will need to be removed. */ - if (csi2rx->has_internal_dphy) { + if (!csi2rx->dphy && csi2rx->has_internal_dphy) { dev_err(&pdev->dev, "Internal D-PHY not supported yet\n"); return -EINVAL; } @@ -492,6 +537,7 @@ static int csi2rx_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Probed CSI2RX with %u/%u lanes, %u streams, %s D-PHY\n", csi2rx->num_lanes, csi2rx->max_lanes, csi2rx->max_streams, + csi2rx->dphy ? "external" : csi2rx->has_internal_dphy ? "internal" : "no"); return 0; From e353ca793f99289ccee768959b3e3b2fbf7fbee8 Mon Sep 17 00:00:00 2001 From: Jack Zhu Date: Tue, 23 May 2023 10:56:26 +0200 Subject: [PATCH 73/92] media: cadence: Add support for JH7110 SoC Add support for Starfive JH7110 SoC which has the cadence csi2 receiver. Reviewed-by: Laurent Pinchart Signed-off-by: Jack Zhu Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/cadence/cdns-csi2rx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index a562c27906e15..f2b4574b8216a 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -558,6 +558,7 @@ static void csi2rx_remove(struct platform_device *pdev) } static const struct of_device_id csi2rx_of_table[] = { + { .compatible = "starfive,jh7110-csi2rx" }, { .compatible = "cdns,csi2rx" }, { }, }; From bb10c54ec4c299465e3ecef7c30ab99f9b85a6da Mon Sep 17 00:00:00 2001 From: Jack Zhu Date: Thu, 24 Aug 2023 16:01:02 +0800 Subject: [PATCH 74/92] media: dt-bindings: Add JH7110 Camera Subsystem Add the bindings documentation for Starfive JH7110 Camera Subsystem which is used for handing image sensor data. Reviewed-by: Krzysztof Kozlowski Reviewed-by: Laurent Pinchart Signed-off-by: Jack Zhu --- .../bindings/media/starfive,jh7110-camss.yaml | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml diff --git a/Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml b/Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml new file mode 100644 index 0000000000000..c66586d90fa2b --- /dev/null +++ b/Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml @@ -0,0 +1,180 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/starfive,jh7110-camss.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Starfive SoC CAMSS ISP + +maintainers: + - Jack Zhu + - Changhuang Liang + +description: + The Starfive CAMSS ISP is a Camera interface for Starfive JH7110 SoC. It + consists of a VIN controller (Video In Controller, a top-level control unit) + and an ISP. + +properties: + compatible: + const: starfive,jh7110-camss + + reg: + maxItems: 2 + + reg-names: + items: + - const: syscon + - const: isp + + clocks: + maxItems: 7 + + clock-names: + items: + - const: apb_func + - const: wrapper_clk_c + - const: dvp_inv + - const: axiwr + - const: mipi_rx0_pxl + - const: ispcore_2x + - const: isp_axi + + resets: + maxItems: 6 + + reset-names: + items: + - const: wrapper_p + - const: wrapper_c + - const: axird + - const: axiwr + - const: isp_top_n + - const: isp_top_axi + + power-domains: + items: + - description: JH7110 ISP Power Domain Switch Controller. + + interrupts: + maxItems: 4 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: Input port for receiving DVP data. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + properties: + bus-type: + enum: [5, 6] + + bus-width: + enum: [8, 10, 12] + + data-shift: + enum: [0, 2] + default: 0 + + hsync-active: + enum: [0, 1] + default: 1 + + vsync-active: + enum: [0, 1] + default: 1 + + required: + - bus-type + - bus-width + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: Input port for receiving CSI data. + + required: + - port@0 + - port@1 + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - resets + - reset-names + - power-domains + - interrupts + - ports + +additionalProperties: false + +examples: + - | + isp@19840000 { + compatible = "starfive,jh7110-camss"; + reg = <0x19840000 0x10000>, + <0x19870000 0x30000>; + reg-names = "syscon", "isp"; + clocks = <&ispcrg 0>, + <&ispcrg 13>, + <&ispcrg 2>, + <&ispcrg 12>, + <&ispcrg 1>, + <&syscrg 51>, + <&syscrg 52>; + clock-names = "apb_func", + "wrapper_clk_c", + "dvp_inv", + "axiwr", + "mipi_rx0_pxl", + "ispcore_2x", + "isp_axi"; + resets = <&ispcrg 0>, + <&ispcrg 1>, + <&ispcrg 10>, + <&ispcrg 11>, + <&syscrg 41>, + <&syscrg 42>; + reset-names = "wrapper_p", + "wrapper_c", + "axird", + "axiwr", + "isp_top_n", + "isp_top_axi"; + power-domains = <&pwrc 5>; + interrupts = <92>, <87>, <88>, <90>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + vin_from_sc2235: endpoint { + remote-endpoint = <&sc2235_to_vin>; + bus-type = <5>; + bus-width = <8>; + data-shift = <2>; + hsync-active = <1>; + vsync-active = <0>; + pclk-sample = <1>; + }; + }; + + port@1 { + reg = <1>; + vin_from_csi2rx: endpoint { + remote-endpoint = <&csi2rx_to_vin>; + }; + }; + }; + }; From b918d5f8e45ff24e6502842043156a839857236d Mon Sep 17 00:00:00 2001 From: Jack Zhu Date: Thu, 24 Aug 2023 16:01:03 +0800 Subject: [PATCH 75/92] media: admin-guide: Add starfive_camss.rst for Starfive Camera Subsystem Add starfive_camss.rst file that documents the Starfive Camera Subsystem driver which is used for handing image sensor data. Signed-off-by: Jack Zhu --- .../admin-guide/media/starfive_camss.rst | 72 +++++++++++++++++++ .../media/starfive_camss_graph.dot | 12 ++++ .../admin-guide/media/v4l-drivers.rst | 1 + 3 files changed, 85 insertions(+) create mode 100644 Documentation/admin-guide/media/starfive_camss.rst create mode 100644 Documentation/admin-guide/media/starfive_camss_graph.dot diff --git a/Documentation/admin-guide/media/starfive_camss.rst b/Documentation/admin-guide/media/starfive_camss.rst new file mode 100644 index 0000000000000..ca42e9447c47b --- /dev/null +++ b/Documentation/admin-guide/media/starfive_camss.rst @@ -0,0 +1,72 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: + +================================ +Starfive Camera Subsystem driver +================================ + +Introduction +------------ + +This file documents the driver for the Starfive Camera Subsystem found on +Starfive JH7110 SoC. The driver is located under drivers/staging/media/starfive/ +camss. + +The driver implements V4L2, Media controller and v4l2_subdev interfaces. Camera +sensor using V4L2 subdev interface in the kernel is supported. + +The driver has been successfully used on the Gstreamer 1.18.5 with v4l2src +plugin. + + +Starfive Camera Subsystem hardware +---------------------------------- + +The Starfive Camera Subsystem hardware consists of:: + + |\ +---------------+ +-----------+ + +----------+ | \ | | | | + | | | | | | | | + | MIPI |----->| |----->| ISP |----->| | + | | | | | | | | + +----------+ | | | | | Memory | + |MUX| +---------------+ | Interface | + +----------+ | | | | + | | | |---------------------------->| | + | Parallel |----->| | | | + | | | | | | + +----------+ | / | | + |/ +-----------+ + +- MIPI: The MIPI interface, receiving data from a MIPI CSI-2 camera sensor. + +- Parallel: The parallel interface, receiving data from a parallel sensor. + +- ISP: The ISP, processing raw Bayer data from an image sensor and producing + YUV frames. + + +Topology +-------- + +The media controller pipeline graph is as follows: + +.. _starfive_camss_graph: + +.. kernel-figure:: starfive_camss_graph.dot + :alt: starfive_camss_graph.dot + :align: center + +The driver has 2 video devices: + +- capture_raw: The capture device, capturing image data directly from a sensor. +- capture_yuv: The capture device, capturing YUV frame data processed by the + ISP module + +The driver has 3 subdevices: + +- stf_isp: is responsible for all the isp operations, outputs YUV frames. +- cdns_csi2rx: a CSI-2 bridge supporting up to 4 CSI lanes in input, and 4 + different pixel streams in output. +- imx219: an image sensor, image data is sent through MIPI CSI-2. diff --git a/Documentation/admin-guide/media/starfive_camss_graph.dot b/Documentation/admin-guide/media/starfive_camss_graph.dot new file mode 100644 index 0000000000000..8eff1f161ac7f --- /dev/null +++ b/Documentation/admin-guide/media/starfive_camss_graph.dot @@ -0,0 +1,12 @@ +digraph board { + rankdir=TB + n00000001 [label="{{ 0} | stf_isp\n/dev/v4l-subdev0 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000001:port1 -> n00000008 [style=dashed] + n00000004 [label="capture_raw\n/dev/video0", shape=box, style=filled, fillcolor=yellow] + n00000008 [label="capture_yuv\n/dev/video1", shape=box, style=filled, fillcolor=yellow] + n0000000e [label="{{ 0} | cdns_csi2rx.19800000.csi-bridge\n | { 1 | 2 | 3 | 4}}", shape=Mrecord, style=filled, fillcolor=green] + n0000000e:port1 -> n00000001:port0 [style=dashed] + n0000000e:port1 -> n00000004 [style=dashed] + n00000018 [label="{{} | imx219 6-0010\n/dev/v4l-subdev1 | { 0}}", shape=Mrecord, style=filled, fillcolor=green] + n00000018:port0 -> n0000000e:port0 [style=bold] +} diff --git a/Documentation/admin-guide/media/v4l-drivers.rst b/Documentation/admin-guide/media/v4l-drivers.rst index 1c41f87c39171..2274fd29c1d71 100644 --- a/Documentation/admin-guide/media/v4l-drivers.rst +++ b/Documentation/admin-guide/media/v4l-drivers.rst @@ -27,6 +27,7 @@ Video4Linux (V4L) driver-specific documentation si470x si4713 si476x + starfive_camss vimc visl vivid From 1ace0fffe0d0719e3cc00fff65b61dcab1c663e8 Mon Sep 17 00:00:00 2001 From: Jack Zhu Date: Thu, 24 Aug 2023 16:01:04 +0800 Subject: [PATCH 76/92] media: staging: media: starfive: camss: Add core driver Add core driver for StarFive Camera Subsystem. The code parses the device platform resources and registers related devices. Reviewed-by: Bryan O'Donoghue Signed-off-by: Jack Zhu --- drivers/staging/media/Kconfig | 2 + drivers/staging/media/Makefile | 1 + drivers/staging/media/starfive/Kconfig | 5 + drivers/staging/media/starfive/Makefile | 2 + drivers/staging/media/starfive/camss/Kconfig | 17 + drivers/staging/media/starfive/camss/Makefile | 9 + .../staging/media/starfive/camss/stf_camss.c | 316 ++++++++++++++++++ .../staging/media/starfive/camss/stf_camss.h | 129 +++++++ 8 files changed, 481 insertions(+) create mode 100644 drivers/staging/media/starfive/Kconfig create mode 100644 drivers/staging/media/starfive/Makefile create mode 100644 drivers/staging/media/starfive/camss/Kconfig create mode 100644 drivers/staging/media/starfive/camss/Makefile create mode 100644 drivers/staging/media/starfive/camss/stf_camss.c create mode 100644 drivers/staging/media/starfive/camss/stf_camss.h diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index bc6c7b248f862..554c2e475ce3b 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -36,6 +36,8 @@ source "drivers/staging/media/omap4iss/Kconfig" source "drivers/staging/media/rkvdec/Kconfig" +source "drivers/staging/media/starfive/Kconfig" + source "drivers/staging/media/sunxi/Kconfig" source "drivers/staging/media/tegra-video/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 1a4c3a062e3dc..dcaeeca0ee6d7 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_VIDEO_MAX96712) += max96712/ obj-$(CONFIG_VIDEO_MESON_VDEC) += meson/vdec/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_VIDEO_ROCKCHIP_VDEC) += rkvdec/ +obj-$(CONFIG_VIDEO_STARFIVE_CAMSS) += starfive/ obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/ obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ diff --git a/drivers/staging/media/starfive/Kconfig b/drivers/staging/media/starfive/Kconfig new file mode 100644 index 0000000000000..34727cf560722 --- /dev/null +++ b/drivers/staging/media/starfive/Kconfig @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "StarFive media platform drivers" + +source "drivers/staging/media/starfive/camss/Kconfig" diff --git a/drivers/staging/media/starfive/Makefile b/drivers/staging/media/starfive/Makefile new file mode 100644 index 0000000000000..4639fa1bca325 --- /dev/null +++ b/drivers/staging/media/starfive/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-y += camss/ diff --git a/drivers/staging/media/starfive/camss/Kconfig b/drivers/staging/media/starfive/camss/Kconfig new file mode 100644 index 0000000000000..8d20e2bd2559c --- /dev/null +++ b/drivers/staging/media/starfive/camss/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_STARFIVE_CAMSS + tristate "Starfive Camera Subsystem driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && OF + depends on HAS_DMA + depends on PM + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG + select V4L2_FWNODE + help + Enable this to support for the Starfive Camera subsystem + found on Starfive JH7110 SoC. + + To compile this driver as a module, choose M here: the + module will be called stf-camss. diff --git a/drivers/staging/media/starfive/camss/Makefile b/drivers/staging/media/starfive/camss/Makefile new file mode 100644 index 0000000000000..f53c5cbe958f6 --- /dev/null +++ b/drivers/staging/media/starfive/camss/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for StarFive Camera Subsystem driver +# + +starfive-camss-objs += \ + stf_camss.o + +obj-$(CONFIG_VIDEO_STARFIVE_CAMSS) += starfive-camss.o diff --git a/drivers/staging/media/starfive/camss/stf_camss.c b/drivers/staging/media/starfive/camss/stf_camss.c new file mode 100644 index 0000000000000..75ebc3a35218a --- /dev/null +++ b/drivers/staging/media/starfive/camss/stf_camss.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * stf_camss.c + * + * Starfive Camera Subsystem driver + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + * + * Author: Jack Zhu + * Author: Changhuang Liang + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "stf_camss.h" + +static const char * const stfcamss_clocks[] = { + "clk_wrapper_clk_c", + "clk_ispcore_2x", + "clk_isp_axi", +}; + +static const char * const stfcamss_resets[] = { + "rst_wrapper_p", + "rst_wrapper_c", + "rst_axiwr", + "rst_isp_top_n", + "rst_isp_top_axi", +}; + +static int stfcamss_get_mem_res(struct stfcamss *stfcamss) +{ + struct platform_device *pdev = to_platform_device(stfcamss->dev); + + stfcamss->syscon_base = + devm_platform_ioremap_resource_byname(pdev, "syscon"); + if (IS_ERR(stfcamss->syscon_base)) + return PTR_ERR(stfcamss->syscon_base); + + stfcamss->isp_base = devm_platform_ioremap_resource_byname(pdev, "isp"); + if (IS_ERR(stfcamss->isp_base)) + return PTR_ERR(stfcamss->isp_base); + + return 0; +} + +/* + * stfcamss_of_parse_endpoint_node - Parse port endpoint node + * @dev: Device + * @node: Device node to be parsed + * @csd: Parsed data from port endpoint node + * + * Return 0 on success or a negative error code on failure + */ +static int stfcamss_of_parse_endpoint_node(struct stfcamss *stfcamss, + struct device_node *node, + struct stfcamss_async_subdev *csd) +{ + struct v4l2_fwnode_endpoint vep = { { 0 } }; + int ret; + + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep); + if (ret) { + dev_err(stfcamss->dev, "endpoint not defined at %pOF\n", node); + return ret; + } + + csd->port = vep.base.port; + + return 0; +} + +/* + * stfcamss_of_parse_ports - Parse ports node + * @stfcamss: STFCAMSS device + * + * Return number of "port" nodes found in "ports" node + */ +static int stfcamss_of_parse_ports(struct stfcamss *stfcamss) +{ + struct device_node *node = NULL; + int ret, num_subdevs = 0; + + for_each_endpoint_of_node(stfcamss->dev->of_node, node) { + struct stfcamss_async_subdev *csd; + + if (!of_device_is_available(node)) + continue; + + csd = v4l2_async_nf_add_fwnode_remote(&stfcamss->notifier, + of_fwnode_handle(node), + struct stfcamss_async_subdev); + if (IS_ERR(csd)) { + ret = PTR_ERR(csd); + dev_err(stfcamss->dev, "failed to add async notifier\n"); + v4l2_async_nf_cleanup(&stfcamss->notifier); + return ret; + } + + ret = stfcamss_of_parse_endpoint_node(stfcamss, node, csd); + if (ret) + return ret; + + num_subdevs++; + } + + return num_subdevs; +} + +static int stfcamss_subdev_notifier_complete(struct v4l2_async_notifier *ntf) +{ + struct stfcamss *stfcamss = + container_of(ntf, struct stfcamss, notifier); + + return v4l2_device_register_subdev_nodes(&stfcamss->v4l2_dev); +} + +static const struct v4l2_async_notifier_operations +stfcamss_subdev_notifier_ops = { + .complete = stfcamss_subdev_notifier_complete, +}; + +static void stfcamss_mc_init(struct platform_device *pdev, + struct stfcamss *stfcamss) +{ + stfcamss->media_dev.dev = stfcamss->dev; + strscpy(stfcamss->media_dev.model, "Starfive Camera Subsystem", + sizeof(stfcamss->media_dev.model)); + media_device_init(&stfcamss->media_dev); + + stfcamss->v4l2_dev.mdev = &stfcamss->media_dev; +} + +/* + * stfcamss_probe - Probe STFCAMSS platform device + * @pdev: Pointer to STFCAMSS platform device + * + * Return 0 on success or a negative error code on failure + */ +static int stfcamss_probe(struct platform_device *pdev) +{ + struct stfcamss *stfcamss; + struct device *dev = &pdev->dev; + int ret, num_subdevs; + unsigned int i; + + stfcamss = devm_kzalloc(dev, sizeof(*stfcamss), GFP_KERNEL); + if (!stfcamss) + return -ENOMEM; + + stfcamss->dev = dev; + + stfcamss->nclks = ARRAY_SIZE(stfcamss->sys_clk); + for (i = 0; i < stfcamss->nclks; ++i) + stfcamss->sys_clk[i].id = stfcamss_clocks[i]; + ret = devm_clk_bulk_get(dev, stfcamss->nclks, stfcamss->sys_clk); + if (ret) { + dev_err(dev, "Failed to get clk controls\n"); + return ret; + } + + stfcamss->nrsts = ARRAY_SIZE(stfcamss->sys_rst); + for (i = 0; i < stfcamss->nrsts; ++i) + stfcamss->sys_rst[i].id = stfcamss_resets[i]; + ret = devm_reset_control_bulk_get_shared(dev, stfcamss->nrsts, + stfcamss->sys_rst); + if (ret) { + dev_err(dev, "Failed to get reset controls\n"); + return ret; + } + + ret = stfcamss_get_mem_res(stfcamss); + if (ret) { + dev_err(dev, "Could not map registers\n"); + return ret; + } + + platform_set_drvdata(pdev, stfcamss); + + v4l2_async_nf_init(&stfcamss->notifier); + + num_subdevs = stfcamss_of_parse_ports(stfcamss); + if (num_subdevs < 0) { + ret = -ENODEV; + dev_err(dev, "Failed to get sub devices: %d\n", ret); + goto err_cleanup_notifier; + } + + stfcamss_mc_init(pdev, stfcamss); + + ret = v4l2_device_register(stfcamss->dev, &stfcamss->v4l2_dev); + if (ret < 0) { + dev_err(dev, "Failed to register V4L2 device: %d\n", ret); + goto err_cleanup_media_device; + } + + ret = media_device_register(&stfcamss->media_dev); + if (ret) { + dev_err(dev, "Failed to register media device: %d\n", ret); + goto err_unregister_device; + } + + pm_runtime_enable(dev); + + stfcamss->notifier.ops = &stfcamss_subdev_notifier_ops; + ret = v4l2_async_nf_register(&stfcamss->v4l2_dev, &stfcamss->notifier); + if (ret) { + dev_err(dev, "Failed to register async subdev nodes: %d\n", + ret); + pm_runtime_disable(dev); + goto err_unregister_media_dev; + } + + return 0; + +err_unregister_media_dev: + media_device_unregister(&stfcamss->media_dev); +err_unregister_device: + v4l2_device_unregister(&stfcamss->v4l2_dev); +err_cleanup_media_device: + media_device_cleanup(&stfcamss->media_dev); +err_cleanup_notifier: + v4l2_async_nf_cleanup(&stfcamss->notifier); + return ret; +} + +/* + * stfcamss_remove - Remove STFCAMSS platform device + * @pdev: Pointer to STFCAMSS platform device + * + * Always returns 0. + */ +static int stfcamss_remove(struct platform_device *pdev) +{ + struct stfcamss *stfcamss = platform_get_drvdata(pdev); + + v4l2_device_unregister(&stfcamss->v4l2_dev); + media_device_cleanup(&stfcamss->media_dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id stfcamss_of_match[] = { + { .compatible = "starfive,jh7110-camss" }, + { /* sentinel */ }, +}; + +MODULE_DEVICE_TABLE(of, stfcamss_of_match); + +static int __maybe_unused stfcamss_runtime_suspend(struct device *dev) +{ + struct stfcamss *stfcamss = dev_get_drvdata(dev); + int ret; + + ret = reset_control_bulk_assert(stfcamss->nrsts, stfcamss->sys_rst); + if (ret) { + dev_err(dev, "reset assert failed\n"); + return ret; + } + + clk_bulk_disable_unprepare(stfcamss->nclks, stfcamss->sys_clk); + + return 0; +} + +static int __maybe_unused stfcamss_runtime_resume(struct device *dev) +{ + struct stfcamss *stfcamss = dev_get_drvdata(dev); + int ret; + + ret = clk_bulk_prepare_enable(stfcamss->nclks, stfcamss->sys_clk); + if (ret) { + dev_err(dev, "clock prepare enable failed\n"); + return ret; + } + + ret = reset_control_bulk_deassert(stfcamss->nrsts, stfcamss->sys_rst); + if (ret < 0) { + dev_err(dev, "cannot deassert resets\n"); + clk_bulk_disable_unprepare(stfcamss->nclks, stfcamss->sys_clk); + return ret; + } + + return 0; +} + +static const struct dev_pm_ops stfcamss_pm_ops = { + SET_RUNTIME_PM_OPS(stfcamss_runtime_suspend, + stfcamss_runtime_resume, + NULL) +}; + +static struct platform_driver stfcamss_driver = { + .probe = stfcamss_probe, + .remove = stfcamss_remove, + .driver = { + .name = "starfive-camss", + .pm = &stfcamss_pm_ops, + .of_match_table = stfcamss_of_match, + }, +}; + +module_platform_driver(stfcamss_driver); + +MODULE_AUTHOR("Jack Zhu "); +MODULE_AUTHOR("Changhuang Liang "); +MODULE_DESCRIPTION("StarFive Camera Subsystem driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/starfive/camss/stf_camss.h b/drivers/staging/media/starfive/camss/stf_camss.h new file mode 100644 index 0000000000000..6110433e59f72 --- /dev/null +++ b/drivers/staging/media/starfive/camss/stf_camss.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * stf_camss.h + * + * Starfive Camera Subsystem driver + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ + +#ifndef STF_CAMSS_H +#define STF_CAMSS_H + +#include +#include +#include +#include +#include +#include +#include + +enum stf_port_num { + STF_PORT_DVP = 0, + STF_PORT_CSI2RX +}; + +enum stf_clk { + STF_CLK_WRAPPER_CLK_C = 0, + STF_CLK_ISPCORE_2X, + STF_CLK_ISP_AXI, + STF_CLK_NUM +}; + +enum stf_rst { + STF_RST_WRAPPER_P = 0, + STF_RST_WRAPPER_C, + STF_RST_AXIWR, + STF_RST_ISP_TOP_N, + STF_RST_ISP_TOP_AXI, + STF_RST_NUM +}; + +struct stf_isr_data { + const char *name; + irqreturn_t (*isr)(int irq, void *priv); +}; + +struct stfcamss { + struct v4l2_device v4l2_dev; + struct media_device media_dev; + struct media_pipeline pipe; + struct device *dev; + struct v4l2_async_notifier notifier; + void __iomem *syscon_base; + void __iomem *isp_base; + struct clk_bulk_data sys_clk[STF_CLK_NUM]; + int nclks; + struct reset_control_bulk_data sys_rst[STF_RST_NUM]; + int nrsts; +}; + +struct stfcamss_async_subdev { + struct v4l2_async_subdev asd; /* must be first */ + enum stf_port_num port; +}; + +static inline u32 stf_isp_reg_read(struct stfcamss *stfcamss, u32 reg) +{ + return ioread32(stfcamss->isp_base + reg); +} + +static inline void stf_isp_reg_write(struct stfcamss *stfcamss, + u32 reg, u32 val) +{ + iowrite32(val, stfcamss->isp_base + reg); +} + +static inline void stf_isp_reg_write_delay(struct stfcamss *stfcamss, + u32 reg, u32 val, u32 delay) +{ + iowrite32(val, stfcamss->isp_base + reg); + usleep_range(1000 * delay, 1000 * delay + 100); +} + +static inline void stf_isp_reg_set_bit(struct stfcamss *stfcamss, + u32 reg, u32 mask, u32 val) +{ + u32 value; + + value = ioread32(stfcamss->isp_base + reg) & ~mask; + val &= mask; + val |= value; + iowrite32(val, stfcamss->isp_base + reg); +} + +static inline void stf_isp_reg_set(struct stfcamss *stfcamss, u32 reg, u32 mask) +{ + iowrite32(ioread32(stfcamss->isp_base + reg) | mask, + stfcamss->isp_base + reg); +} + +static inline u32 stf_syscon_reg_read(struct stfcamss *stfcamss, u32 reg) +{ + return ioread32(stfcamss->syscon_base + reg); +} + +static inline void stf_syscon_reg_write(struct stfcamss *stfcamss, + u32 reg, u32 val) +{ + iowrite32(val, stfcamss->syscon_base + reg); +} + +static inline void stf_syscon_reg_set_bit(struct stfcamss *stfcamss, + u32 reg, u32 bit_mask) +{ + u32 value; + + value = ioread32(stfcamss->syscon_base + reg); + iowrite32(value | bit_mask, stfcamss->syscon_base + reg); +} + +static inline void stf_syscon_reg_clear_bit(struct stfcamss *stfcamss, + u32 reg, u32 bit_mask) +{ + u32 value; + + value = ioread32(stfcamss->syscon_base + reg); + iowrite32(value & ~bit_mask, stfcamss->syscon_base + reg); +} +#endif /* STF_CAMSS_H */ From 6cca95ef9e24766e03ac8159e8581eff2e9c38fa Mon Sep 17 00:00:00 2001 From: Jack Zhu Date: Thu, 24 Aug 2023 16:01:05 +0800 Subject: [PATCH 77/92] media: staging: media: starfive: camss: Add video driver Add video driver for StarFive Camera Subsystem. Signed-off-by: Jack Zhu --- drivers/staging/media/starfive/camss/Makefile | 3 +- .../staging/media/starfive/camss/stf_video.c | 557 ++++++++++++++++++ .../staging/media/starfive/camss/stf_video.h | 100 ++++ 3 files changed, 659 insertions(+), 1 deletion(-) create mode 100644 drivers/staging/media/starfive/camss/stf_video.c create mode 100644 drivers/staging/media/starfive/camss/stf_video.h diff --git a/drivers/staging/media/starfive/camss/Makefile b/drivers/staging/media/starfive/camss/Makefile index f53c5cbe958f6..eb457917a9148 100644 --- a/drivers/staging/media/starfive/camss/Makefile +++ b/drivers/staging/media/starfive/camss/Makefile @@ -4,6 +4,7 @@ # starfive-camss-objs += \ - stf_camss.o + stf_camss.o \ + stf_video.o obj-$(CONFIG_VIDEO_STARFIVE_CAMSS) += starfive-camss.o diff --git a/drivers/staging/media/starfive/camss/stf_video.c b/drivers/staging/media/starfive/camss/stf_video.c new file mode 100644 index 0000000000000..bf00f6a1e611c --- /dev/null +++ b/drivers/staging/media/starfive/camss/stf_video.c @@ -0,0 +1,557 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * stf_video.c + * + * StarFive Camera Subsystem - V4L2 device node + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include + +#include "stf_camss.h" +#include "stf_video.h" + +/* ----------------------------------------------------------------------------- + * Helper functions + */ + +static inline struct stfcamss_buffer * +to_stfcamss_buffer(struct vb2_v4l2_buffer *vbuf) +{ + return container_of(vbuf, struct stfcamss_buffer, vb); +} + +static const struct stfcamss_format_info * +video_g_fi_by_index(struct stfcamss_video *video, int index) +{ + if (index >= video->nformats) + return NULL; + + return &video->formats[index]; +} + +static const struct stfcamss_format_info * +video_g_fi_by_mcode(struct stfcamss_video *video, u32 mcode) +{ + unsigned int i; + + for (i = 0; i < video->nformats; i++) { + if (video->formats[i].code == mcode) + return &video->formats[i]; + } + + return NULL; +} + +static const struct stfcamss_format_info * +video_g_fi_by_pfmt(struct stfcamss_video *video, u32 pixelformat) +{ + unsigned int i; + + for (i = 0; i < video->nformats; i++) { + if (video->formats[i].pixelformat == pixelformat) + return &video->formats[i]; + } + + return NULL; +} + +static int __video_try_fmt(struct stfcamss_video *video, struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + const struct stfcamss_format_info *fi; + u32 width, height; + u32 bpl; + unsigned int i; + + fi = video_g_fi_by_pfmt(video, pix->pixelformat); + if (!fi) + fi = &video->formats[0]; /* default format */ + + width = pix->width; + height = pix->height; + + memset(pix, 0, sizeof(*pix)); + + pix->pixelformat = fi->pixelformat; + pix->width = clamp_t(u32, width, STFCAMSS_FRAME_MIN_WIDTH, + STFCAMSS_FRAME_MAX_WIDTH); + pix->height = clamp_t(u32, height, STFCAMSS_FRAME_MIN_HEIGHT, + STFCAMSS_FRAME_MAX_HEIGHT); + bpl = pix->width * fi->bpp / 8; + bpl = ALIGN(bpl, video->bpl_alignment); + pix->bytesperline = bpl; + + for (i = 0; i < fi->planes; ++i) + pix->sizeimage += bpl * pix->height / fi->vsub[i]; + + pix->field = V4L2_FIELD_NONE; + pix->colorspace = V4L2_COLORSPACE_SRGB; + pix->flags = 0; + pix->ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace); + pix->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, + pix->colorspace, + pix->ycbcr_enc); + pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace); + + return 0; +} + +static int stf_video_init_format(struct stfcamss_video *video) +{ + int ret; + struct v4l2_format format = { + .type = video->type, + .fmt.pix = { + .width = 1920, + .height = 1080, + .pixelformat = V4L2_PIX_FMT_NV12, + }, + }; + + ret = __video_try_fmt(video, &format); + + if (ret < 0) + return ret; + + video->active_fmt = format; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Video queue operations + */ + +static int video_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct stfcamss_video *video = vb2_get_drv_priv(q); + const struct v4l2_pix_format *format = &video->active_fmt.fmt.pix; + + if (*num_planes) { + if (*num_planes != 1) + return -EINVAL; + + if (sizes[0] < format->sizeimage) + return -EINVAL; + } + + *num_planes = 1; + sizes[0] = format->sizeimage; + if (!sizes[0]) { + dev_dbg(video->stfcamss->dev, + "%s: error size is zero.\n", __func__); + return -EINVAL; + } + + dev_dbg(video->stfcamss->dev, "planes = %d, size = %d\n", + *num_planes, sizes[0]); + + return 0; +} + +static int video_buf_init(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue); + struct stfcamss_buffer *buffer = to_stfcamss_buffer(vbuf); + const struct v4l2_pix_format *fmt = &video->active_fmt.fmt.pix; + dma_addr_t *paddr; + + paddr = vb2_plane_cookie(vb, 0); + buffer->addr[0] = *paddr; + + if (fmt->pixelformat == V4L2_PIX_FMT_NV12) + buffer->addr[1] = + buffer->addr[0] + fmt->bytesperline * fmt->height; + + return 0; +} + +static int video_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue); + const struct v4l2_pix_format *fmt = &video->active_fmt.fmt.pix; + + if (fmt->sizeimage > vb2_plane_size(vb, 0)) { + dev_dbg(video->stfcamss->dev, + "sizeimage = %u, plane size = %u\n", + fmt->sizeimage, (unsigned int)vb2_plane_size(vb, 0)); + return -EINVAL; + } + vb2_set_plane_payload(vb, 0, fmt->sizeimage); + + vbuf->field = V4L2_FIELD_NONE; + + return 0; +} + +static void video_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue); + struct stfcamss_buffer *buffer = to_stfcamss_buffer(vbuf); + + video->ops->queue_buffer(video, buffer); +} + +static int video_get_subdev_format(struct stfcamss_video *video, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_subdev *subdev; + struct media_pad *pad; + struct media_entity *entity; + int ret; + + entity = &video->vdev.entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + fmt->pad = pad->index; + fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; + + ret = v4l2_subdev_call_state_active(subdev, pad, get_fmt, fmt); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + else if (!ret) + break; + } + + return 0; +} + +static int video_check_format(struct stfcamss_video *video) +{ + struct v4l2_pix_format *pix = &video->active_fmt.fmt.pix; + struct v4l2_subdev_format sd_fmt; + const struct stfcamss_format_info *fi; + int ret; + + ret = video_get_subdev_format(video, &sd_fmt); + if (ret < 0) + return ret; + + fi = video_g_fi_by_mcode(video, sd_fmt.format.code); + if (!fi) + return -EINVAL; + + if (pix->pixelformat != fi->pixelformat || + pix->height != sd_fmt.format.height || + pix->width != sd_fmt.format.width || + pix->field != sd_fmt.format.field) + return -EPIPE; + + return 0; +} + +static int video_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct stfcamss_video *video = vb2_get_drv_priv(q); + struct video_device *vdev = &video->vdev; + int ret; + + ret = video_device_pipeline_start(vdev, &video->stfcamss->pipe); + if (ret < 0) { + dev_err(video->stfcamss->dev, + "Failed to media_pipeline_start: %d\n", ret); + goto err_ret_buffers; + } + + ret = video_check_format(video); + if (ret < 0) + goto err_pipeline_stop; + + ret = pm_runtime_resume_and_get(video->stfcamss->dev); + if (ret < 0) { + dev_err(video->stfcamss->dev, "power up failed %d\n", ret); + goto err_pipeline_stop; + } + + video->ops->start_streaming(video); + + ret = v4l2_subdev_call(video->source_subdev, video, s_stream, true); + if (ret) { + dev_err(video->stfcamss->dev, "stream on failed\n"); + goto err_pm_put; + } + + return 0; + +err_pm_put: + pm_runtime_put(video->stfcamss->dev); +err_pipeline_stop: + video_device_pipeline_stop(vdev); +err_ret_buffers: + video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED); + return ret; +} + +static void video_stop_streaming(struct vb2_queue *q) +{ + struct stfcamss_video *video = vb2_get_drv_priv(q); + struct video_device *vdev = &video->vdev; + + video->ops->stop_streaming(video); + + v4l2_subdev_call(video->source_subdev, video, s_stream, false); + + pm_runtime_put(video->stfcamss->dev); + + video_device_pipeline_stop(vdev); + video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR); +} + +static const struct vb2_ops stf_video_vb2_q_ops = { + .queue_setup = video_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_init = video_buf_init, + .buf_prepare = video_buf_prepare, + .buf_queue = video_buf_queue, + .start_streaming = video_start_streaming, + .stop_streaming = video_stop_streaming, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 ioctls + */ + +static int video_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, "stf camss", sizeof(cap->driver)); + strscpy(cap->card, "Starfive Camera Subsystem", sizeof(cap->card)); + + return 0; +} + +static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct stfcamss_video *video = video_drvdata(file); + const struct stfcamss_format_info *fi; + + if (f->index >= video->nformats) + return -EINVAL; + + if (f->mbus_code) { + /* Each entry in formats[] table has unique mbus_code */ + if (f->index > 0) + return -EINVAL; + + fi = video_g_fi_by_mcode(video, f->mbus_code); + } else { + fi = video_g_fi_by_index(video, f->index); + } + + if (!fi) + return -EINVAL; + + f->pixelformat = fi->pixelformat; + + return 0; +} + +static int video_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct stfcamss_video *video = video_drvdata(file); + unsigned int i; + + if (fsize->index) + return -EINVAL; + + for (i = 0; i < video->nformats; i++) { + if (video->formats[i].pixelformat == fsize->pixel_format) + break; + } + + if (i == video->nformats) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = STFCAMSS_FRAME_MIN_WIDTH; + fsize->stepwise.max_width = STFCAMSS_FRAME_MAX_WIDTH; + fsize->stepwise.min_height = STFCAMSS_FRAME_MIN_HEIGHT; + fsize->stepwise.max_height = STFCAMSS_FRAME_MAX_HEIGHT; + fsize->stepwise.step_width = 1; + fsize->stepwise.step_height = 1; + + return 0; +} + +static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct stfcamss_video *video = video_drvdata(file); + + *f = video->active_fmt; + + return 0; +} + +static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct stfcamss_video *video = video_drvdata(file); + int ret; + + if (vb2_is_busy(&video->vb2_q)) + return -EBUSY; + + ret = __video_try_fmt(video, f); + if (ret < 0) + return ret; + + video->active_fmt = *f; + + return 0; +} + +static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct stfcamss_video *video = video_drvdata(file); + + return __video_try_fmt(video, f); +} + +static const struct v4l2_ioctl_ops stf_vid_ioctl_ops = { + .vidioc_querycap = video_querycap, + .vidioc_enum_fmt_vid_cap = video_enum_fmt, + .vidioc_enum_framesizes = video_enum_framesizes, + .vidioc_g_fmt_vid_cap = video_g_fmt, + .vidioc_s_fmt_vid_cap = video_s_fmt, + .vidioc_try_fmt_vid_cap = video_try_fmt, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 file operations + */ + +static const struct v4l2_file_operations stf_vid_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .read = vb2_fop_read, +}; + +/* ----------------------------------------------------------------------------- + * STFCAMSS video core + */ + +static void stf_video_release(struct video_device *vdev) +{ + struct stfcamss_video *video = video_get_drvdata(vdev); + + media_entity_cleanup(&vdev->entity); + + mutex_destroy(&video->q_lock); + mutex_destroy(&video->lock); +} + +int stf_video_register(struct stfcamss_video *video, + struct v4l2_device *v4l2_dev, const char *name) +{ + struct video_device *vdev = &video->vdev; + struct vb2_queue *q; + struct media_pad *pad = &video->pad; + int ret; + + mutex_init(&video->q_lock); + mutex_init(&video->lock); + + q = &video->vb2_q; + q->drv_priv = video; + q->mem_ops = &vb2_dma_contig_memops; + q->ops = &stf_video_vb2_q_ops; + q->type = video->type; + q->io_modes = VB2_DMABUF | VB2_MMAP; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->buf_struct_size = sizeof(struct stfcamss_buffer); + q->dev = video->stfcamss->dev; + q->lock = &video->q_lock; + q->min_buffers_needed = STFCAMSS_MIN_BUFFERS; + ret = vb2_queue_init(q); + if (ret < 0) { + dev_err(video->stfcamss->dev, + "Failed to init vb2 queue: %d\n", ret); + goto err_mutex_destroy; + } + + pad->flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&vdev->entity, 1, pad); + if (ret < 0) { + dev_err(video->stfcamss->dev, + "Failed to init video entity: %d\n", ret); + goto err_mutex_destroy; + } + + ret = stf_video_init_format(video); + if (ret < 0) { + dev_err(video->stfcamss->dev, + "Failed to init format: %d\n", ret); + goto err_media_cleanup; + } + + vdev->fops = &stf_vid_fops; + vdev->ioctl_ops = &stf_vid_ioctl_ops; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + vdev->vfl_dir = VFL_DIR_RX; + vdev->release = stf_video_release; + vdev->v4l2_dev = v4l2_dev; + vdev->queue = &video->vb2_q; + vdev->lock = &video->lock; + strscpy(vdev->name, name, sizeof(vdev->name)); + + video_set_drvdata(vdev, video); + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret < 0) { + dev_err(video->stfcamss->dev, + "Failed to register video device: %d\n", ret); + goto err_media_cleanup; + } + + return 0; + +err_media_cleanup: + media_entity_cleanup(&vdev->entity); +err_mutex_destroy: + mutex_destroy(&video->lock); + mutex_destroy(&video->q_lock); + return ret; +} + +void stf_video_unregister(struct stfcamss_video *video) +{ + vb2_video_unregister_device(&video->vdev); +} diff --git a/drivers/staging/media/starfive/camss/stf_video.h b/drivers/staging/media/starfive/camss/stf_video.h new file mode 100644 index 0000000000000..8052b77e3ad83 --- /dev/null +++ b/drivers/staging/media/starfive/camss/stf_video.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * stf_video.h + * + * StarFive Camera Subsystem - V4L2 device node + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ + +#ifndef STF_VIDEO_H +#define STF_VIDEO_H + +#include +#include +#include +#include +#include +#include +#include + +#define STFCAMSS_FRAME_MIN_WIDTH 64 +#define STFCAMSS_FRAME_MAX_WIDTH 1920 +#define STFCAMSS_FRAME_MIN_HEIGHT 64 +#define STFCAMSS_FRAME_MAX_HEIGHT 1080 +#define STFCAMSS_FRAME_WIDTH_ALIGN_8 8 +#define STFCAMSS_FRAME_WIDTH_ALIGN_128 128 +#define STFCAMSS_MIN_BUFFERS 2 + +#define STFCAMSS_MAX_ENTITY_NAME_LEN 27 + +enum stf_v_line_id { + STF_V_LINE_WR = 0, + STF_V_LINE_ISP, + STF_V_LINE_MAX, +}; + +enum stf_capture_type { + STF_CAPTURE_RAW = 0, + STF_CAPTURE_YUV, + STF_CAPTURE_NUM, +}; + +struct stfcamss_buffer { + struct vb2_v4l2_buffer vb; + dma_addr_t addr[2]; + struct list_head queue; +}; + +struct fract { + u8 numerator; + u8 denominator; +}; + +/* + * struct stfcamss_format_info - ISP media bus format information + * @code: V4L2 media bus format code + * @pixelformat: V4L2 pixel format FCC identifier + * @planes: Number of planes + * @vsub: Vertical subsampling (for each plane) + * @bpp: Bits per pixel when stored in memory (for each plane) + */ +struct stfcamss_format_info { + u32 code; + u32 pixelformat; + u8 planes; + u8 vsub[3]; + u8 bpp; +}; + +struct stfcamss_video { + struct stfcamss *stfcamss; + struct vb2_queue vb2_q; + struct video_device vdev; + struct media_pad pad; + struct v4l2_format active_fmt; + enum v4l2_buf_type type; + const struct stfcamss_video_ops *ops; + struct mutex lock; /* serialize device access */ + struct mutex q_lock; /* protects the queue */ + unsigned int bpl_alignment; + const struct stfcamss_format_info *formats; + unsigned int nformats; + struct v4l2_subdev *source_subdev; +}; + +struct stfcamss_video_ops { + int (*queue_buffer)(struct stfcamss_video *video, + struct stfcamss_buffer *buf); + int (*flush_buffers)(struct stfcamss_video *video, + enum vb2_buffer_state state); + void (*start_streaming)(struct stfcamss_video *video); + void (*stop_streaming)(struct stfcamss_video *video); +}; + +int stf_video_register(struct stfcamss_video *video, + struct v4l2_device *v4l2_dev, const char *name); + +void stf_video_unregister(struct stfcamss_video *video); + +#endif /* STF_VIDEO_H */ From 6c9e2745c3641bd9a24b01fb4aa6759cd1c0d6ae Mon Sep 17 00:00:00 2001 From: Jack Zhu Date: Thu, 24 Aug 2023 16:01:06 +0800 Subject: [PATCH 78/92] media: staging: media: starfive: camss: Add ISP driver Add ISP driver for StarFive Camera Subsystem. Signed-off-by: Jack Zhu --- drivers/staging/media/starfive/camss/Makefile | 2 + .../staging/media/starfive/camss/stf_camss.h | 3 + .../staging/media/starfive/camss/stf_isp.c | 407 ++++++++++++++++ .../staging/media/starfive/camss/stf_isp.h | 428 +++++++++++++++++ .../media/starfive/camss/stf_isp_hw_ops.c | 445 ++++++++++++++++++ 5 files changed, 1285 insertions(+) create mode 100644 drivers/staging/media/starfive/camss/stf_isp.c create mode 100644 drivers/staging/media/starfive/camss/stf_isp.h create mode 100644 drivers/staging/media/starfive/camss/stf_isp_hw_ops.c diff --git a/drivers/staging/media/starfive/camss/Makefile b/drivers/staging/media/starfive/camss/Makefile index eb457917a9148..cdf57e8c95468 100644 --- a/drivers/staging/media/starfive/camss/Makefile +++ b/drivers/staging/media/starfive/camss/Makefile @@ -5,6 +5,8 @@ starfive-camss-objs += \ stf_camss.o \ + stf_isp.o \ + stf_isp_hw_ops.o \ stf_video.o obj-$(CONFIG_VIDEO_STARFIVE_CAMSS) += starfive-camss.o diff --git a/drivers/staging/media/starfive/camss/stf_camss.h b/drivers/staging/media/starfive/camss/stf_camss.h index 6110433e59f72..e0f7ee9f25834 100644 --- a/drivers/staging/media/starfive/camss/stf_camss.h +++ b/drivers/staging/media/starfive/camss/stf_camss.h @@ -18,6 +18,8 @@ #include #include +#include "stf_isp.h" + enum stf_port_num { STF_PORT_DVP = 0, STF_PORT_CSI2RX @@ -49,6 +51,7 @@ struct stfcamss { struct media_device media_dev; struct media_pipeline pipe; struct device *dev; + struct stf_isp_dev isp_dev; struct v4l2_async_notifier notifier; void __iomem *syscon_base; void __iomem *isp_base; diff --git a/drivers/staging/media/starfive/camss/stf_isp.c b/drivers/staging/media/starfive/camss/stf_isp.c new file mode 100644 index 0000000000000..7d9740965e7e9 --- /dev/null +++ b/drivers/staging/media/starfive/camss/stf_isp.c @@ -0,0 +1,407 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * stf_isp.c + * + * StarFive Camera Subsystem - ISP Module + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ +#include + +#include "stf_camss.h" + +#define SINK_FORMATS_INDEX 0 +#define SOURCE_FORMATS_INDEX 1 + +static int isp_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel); + +static const struct stf_isp_format isp_formats_sink[] = { + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, +}; + +static const struct stf_isp_format isp_formats_source[] = { + { MEDIA_BUS_FMT_YUYV8_1_5X8, 8 }, +}; + +static const struct stf_isp_format_table isp_formats_st7110[] = { + { isp_formats_sink, ARRAY_SIZE(isp_formats_sink) }, + { isp_formats_source, ARRAY_SIZE(isp_formats_source) }, +}; + +static const struct stf_isp_format * +stf_g_fmt_by_mcode(const struct stf_isp_format_table *fmt_table, u32 mcode) +{ + unsigned int i; + + for (i = 0; i < fmt_table->nfmts; i++) { + if (fmt_table->fmts[i].code == mcode) + return &fmt_table->fmts[i]; + } + + return NULL; +} + +int stf_isp_init(struct stfcamss *stfcamss) +{ + struct stf_isp_dev *isp_dev = &stfcamss->isp_dev; + + isp_dev->stfcamss = stfcamss; + isp_dev->formats = isp_formats_st7110; + isp_dev->nformats = ARRAY_SIZE(isp_formats_st7110); + isp_dev->current_fmt = &isp_formats_source[0]; + + return 0; +} + +static int isp_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct v4l2_subdev_state *sd_state; + struct v4l2_mbus_framefmt *fmt; + struct v4l2_rect *crop; + + sd_state = v4l2_subdev_lock_and_get_active_state(sd); + fmt = v4l2_subdev_get_pad_format(sd, sd_state, STF_ISP_PAD_SINK); + crop = v4l2_subdev_get_pad_crop(sd, sd_state, STF_ISP_PAD_SRC); + + if (enable) { + stf_isp_reset(isp_dev); + stf_isp_init_cfg(isp_dev); + stf_isp_settings(isp_dev, crop, fmt->code); + stf_isp_stream_set(isp_dev); + } + + v4l2_subdev_call(isp_dev->source_subdev, video, s_stream, enable); + + v4l2_subdev_unlock_state(sd_state); + return 0; +} + +static void isp_try_format(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + unsigned int pad, + struct v4l2_mbus_framefmt *fmt) +{ + const struct stf_isp_format_table *formats; + + if (pad >= STF_ISP_PAD_MAX) { + fmt->colorspace = V4L2_COLORSPACE_SRGB; + return; + } + + if (pad == STF_ISP_PAD_SINK) + formats = &isp_dev->formats[SINK_FORMATS_INDEX]; + else if (pad == STF_ISP_PAD_SRC) + formats = &isp_dev->formats[SOURCE_FORMATS_INDEX]; + + fmt->width = clamp_t(u32, fmt->width, STFCAMSS_FRAME_MIN_WIDTH, + STFCAMSS_FRAME_MAX_WIDTH); + fmt->height = clamp_t(u32, fmt->height, STFCAMSS_FRAME_MIN_HEIGHT, + STFCAMSS_FRAME_MAX_HEIGHT); + fmt->height &= ~0x1; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->flags = 0; + + if (!stf_g_fmt_by_mcode(formats, fmt->code)) + fmt->code = formats->fmts[0].code; +} + +static int isp_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + const struct stf_isp_format_table *formats; + + if (code->pad == STF_ISP_PAD_SINK) { + formats = &isp_dev->formats[SINK_FORMATS_INDEX]; + code->code = formats->fmts[code->index].code; + } else { + struct v4l2_mbus_framefmt *sink_fmt; + + sink_fmt = v4l2_subdev_get_pad_format(sd, state, + STF_ISP_PAD_SRC); + + code->code = sink_fmt->code; + if (!code->code) + return -EINVAL; + } + code->flags = 0; + + return 0; +} + +static int isp_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + isp_try_format(isp_dev, state, fse->pad, &format); + fse->min_width = STFCAMSS_FRAME_MIN_WIDTH; + fse->min_height = STFCAMSS_FRAME_MIN_HEIGHT; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + isp_try_format(isp_dev, state, fse->pad, &format); + fse->max_width = STFCAMSS_FRAME_MAX_WIDTH; + fse->max_height = STFCAMSS_FRAME_MAX_HEIGHT; + + return 0; +} + +static int isp_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_pad_format(sd, state, fmt->pad); + if (!format) + return -EINVAL; + + isp_try_format(isp_dev, state, fmt->pad, &fmt->format); + *format = fmt->format; + + isp_dev->current_fmt = stf_g_fmt_by_mcode(&isp_dev->formats[fmt->pad], + fmt->format.code); + + /* Propagate to in crop */ + if (fmt->pad == STF_ISP_PAD_SINK) { + struct v4l2_subdev_selection sel = { 0 }; + + /* Reset sink pad compose selection */ + sel.which = fmt->which; + sel.pad = STF_ISP_PAD_SINK; + sel.target = V4L2_SEL_TGT_CROP; + sel.r.width = fmt->format.width; + sel.r.height = fmt->format.height; + isp_set_selection(sd, state, &sel); + } + + return 0; +} + +static const struct v4l2_rect stf_frame_min_crop = { + .width = STFCAMSS_FRAME_MIN_WIDTH, + .height = STFCAMSS_FRAME_MIN_HEIGHT, + .top = 0, + .left = 0, +}; + +static void isp_try_crop(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + struct v4l2_rect *crop) +{ + struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_get_pad_format(&isp_dev->subdev, state, + STF_ISP_PAD_SINK); + + const struct v4l2_rect bounds = { + .width = fmt->width, + .height = fmt->height, + .left = 0, + .top = 0, + }; + + v4l2_rect_set_min_size(crop, &stf_frame_min_crop); + v4l2_rect_map_inside(crop, &bounds); +} + +static int isp_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct v4l2_subdev_format fmt = { 0 }; + struct v4l2_rect *rect; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + if (sel->pad == STF_ISP_PAD_SINK) { + fmt.format = *v4l2_subdev_get_pad_format(sd, state, + sel->pad); + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = fmt.format.width; + sel->r.height = fmt.format.height; + } else if (sel->pad == STF_ISP_PAD_SRC) { + rect = v4l2_subdev_get_pad_crop(sd, state, sel->pad); + sel->r = *rect; + } + break; + + case V4L2_SEL_TGT_CROP: + rect = v4l2_subdev_get_pad_crop(sd, state, sel->pad); + if (!rect) + return -EINVAL; + + sel->r = *rect; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int isp_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct v4l2_rect *rect; + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + if (sel->target == V4L2_SEL_TGT_CROP && + sel->pad == STF_ISP_PAD_SINK) { + struct v4l2_subdev_selection crop = { 0 }; + + rect = v4l2_subdev_get_pad_crop(sd, state, sel->pad); + if (!rect) + return -EINVAL; + + isp_try_crop(isp_dev, state, &sel->r); + *rect = sel->r; + + /* Reset source crop selection */ + crop.which = sel->which; + crop.pad = STF_ISP_PAD_SRC; + crop.target = V4L2_SEL_TGT_CROP; + crop.r = *rect; + isp_set_selection(sd, state, &crop); + } else if (sel->target == V4L2_SEL_TGT_CROP && + sel->pad == STF_ISP_PAD_SRC) { + struct v4l2_subdev_format fmt = { 0 }; + + rect = v4l2_subdev_get_pad_crop(sd, state, sel->pad); + if (!rect) + return -EINVAL; + + isp_try_crop(isp_dev, state, &sel->r); + *rect = sel->r; + + /* Reset source pad format width and height */ + fmt.which = sel->which; + fmt.pad = STF_ISP_PAD_SRC; + fmt.format.width = rect->width; + fmt.format.height = rect->height; + isp_set_format(sd, state, &fmt); + } + + dev_dbg(isp_dev->stfcamss->dev, "pad: %d sel(%d,%d)/%dx%d\n", + sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); + + return 0; +} + +static int isp_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_subdev_format format = { + .pad = STF_ISP_PAD_SINK, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .format = { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .width = 1920, + .height = 1080 + } + }; + + return isp_set_format(sd, sd_state, &format); +} + +static const struct v4l2_subdev_video_ops isp_video_ops = { + .s_stream = isp_set_stream, +}; + +static const struct v4l2_subdev_pad_ops isp_pad_ops = { + .init_cfg = isp_init_formats, + .enum_mbus_code = isp_enum_mbus_code, + .enum_frame_size = isp_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = isp_set_format, + .get_selection = isp_get_selection, + .set_selection = isp_set_selection, +}; + +static const struct v4l2_subdev_ops isp_v4l2_ops = { + .video = &isp_video_ops, + .pad = &isp_pad_ops, +}; + +static const struct media_entity_operations isp_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +int stf_isp_register(struct stf_isp_dev *isp_dev, struct v4l2_device *v4l2_dev) +{ + struct v4l2_subdev *sd = &isp_dev->subdev; + struct media_pad *pads = isp_dev->pads; + int ret; + + v4l2_subdev_init(sd, &isp_v4l2_ops); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(sd->name, ARRAY_SIZE(sd->name), "stf_isp"); + v4l2_set_subdevdata(sd, isp_dev); + + pads[STF_ISP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[STF_ISP_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; + + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP; + sd->entity.ops = &isp_media_ops; + ret = media_entity_pads_init(&sd->entity, STF_ISP_PAD_MAX, pads); + if (ret) { + dev_err(isp_dev->stfcamss->dev, + "Failed to init media entity: %d\n", ret); + return ret; + } + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto err_entity_cleanup; + + ret = v4l2_device_register_subdev(v4l2_dev, sd); + if (ret) { + dev_err(isp_dev->stfcamss->dev, + "Failed to register subdev: %d\n", ret); + goto err_subdev_cleanup; + } + + return 0; + +err_subdev_cleanup: + v4l2_subdev_cleanup(sd); +err_entity_cleanup: + media_entity_cleanup(&sd->entity); + return ret; +} + +int stf_isp_unregister(struct stf_isp_dev *isp_dev) +{ + v4l2_device_unregister_subdev(&isp_dev->subdev); + v4l2_subdev_cleanup(&isp_dev->subdev); + media_entity_cleanup(&isp_dev->subdev.entity); + + return 0; +} diff --git a/drivers/staging/media/starfive/camss/stf_isp.h b/drivers/staging/media/starfive/camss/stf_isp.h new file mode 100644 index 0000000000000..7d2ae369f050b --- /dev/null +++ b/drivers/staging/media/starfive/camss/stf_isp.h @@ -0,0 +1,428 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * stf_isp.h + * + * StarFive Camera Subsystem - ISP Module + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ + +#ifndef STF_ISP_H +#define STF_ISP_H + +#include + +#include "stf_video.h" + +#define ISP_RAW_DATA_BITS 12 +#define SCALER_RATIO_MAX 1 +#define STF_ISP_REG_OFFSET_MAX 0x0fff +#define STF_ISP_REG_DELAY_MAX 100 + +/* isp registers */ +#define ISP_REG_CSI_INPUT_EN_AND_STATUS 0x000 +#define CSI_SCD_ERR BIT(6) +#define CSI_ITU656_ERR BIT(4) +#define CSI_ITU656_F BIT(3) +#define CSI_SCD_DONE BIT(2) +#define CSI_BUSY_S BIT(1) +#define CSI_EN_S BIT(0) + +#define ISP_REG_CSIINTS 0x008 +#define CSI_INTS(n) ((n) << 16) +#define CSI_SHA_M(n) ((n) << 0) +#define CSI_INTS_MASK GENMASK(17, 16) + +#define ISP_REG_CSI_MODULE_CFG 0x010 +#define CSI_DUMP_EN BIT(19) +#define CSI_VS_EN BIT(18) +#define CSI_SC_EN BIT(17) +#define CSI_OBA_EN BIT(16) +#define CSI_AWB_EN BIT(7) +#define CSI_LCCF_EN BIT(6) +#define CSI_OECFHM_EN BIT(5) +#define CSI_OECF_EN BIT(4) +#define CSI_LCBQ_EN BIT(3) +#define CSI_OBC_EN BIT(2) +#define CSI_DEC_EN BIT(1) +#define CSI_DC_EN BIT(0) + +#define ISP_REG_SENSOR 0x014 +#define DVP_SYNC_POL(n) ((n) << 2) +#define ITU656_EN(n) ((n) << 1) +#define IMAGER_SEL(n) ((n) << 0) + +#define ISP_REG_RAW_FORMAT_CFG 0x018 +#define SMY13(n) ((n) << 14) +#define SMY12(n) ((n) << 12) +#define SMY11(n) ((n) << 10) +#define SMY10(n) ((n) << 8) +#define SMY3(n) ((n) << 6) +#define SMY2(n) ((n) << 4) +#define SMY1(n) ((n) << 2) +#define SMY0(n) ((n) << 0) + +#define ISP_REG_PIC_CAPTURE_START_CFG 0x01c +#define VSTART_CAP(n) ((n) << 16) +#define HSTART_CAP(n) ((n) << 0) + +#define ISP_REG_PIC_CAPTURE_END_CFG 0x020 +#define VEND_CAP(n) ((n) << 16) +#define HEND_CAP(n) ((n) << 0) + +#define ISP_REG_DUMP_CFG_0 0x024 +#define ISP_REG_DUMP_CFG_1 0x028 +#define DUMP_ID(n) ((n) << 24) +#define DUMP_SHT(n) ((n) << 20) +#define DUMP_BURST_LEN(n) ((n) << 16) +#define DUMP_SD(n) ((n) << 0) +#define DUMP_BURST_LEN_MASK GENMASK(17, 16) +#define DUMP_SD_MASK GENMASK(15, 0) + +#define ISP_REG_DEC_CFG 0x030 +#define DEC_V_KEEP(n) ((n) << 24) +#define DEC_V_PERIOD(n) ((n) << 16) +#define DEC_H_KEEP(n) ((n) << 8) +#define DEC_H_PERIOD(n) ((n) << 0) + +#define ISP_REG_OBC_CFG 0x034 +#define OBC_W_H(y) ((y) << 4) +#define OBC_W_W(x) ((x) << 0) + +#define ISP_REG_DC_CFG_1 0x044 +#define DC_AXI_ID(n) ((n) << 0) + +#define ISP_REG_LCCF_CFG_0 0x050 +#define Y_DISTANCE(y) ((y) << 16) +#define X_DISTANCE(x) ((x) << 0) + +#define ISP_REG_LCCF_CFG_1 0x058 +#define LCCF_MAX_DIS(n) ((n) << 0) + +#define ISP_REG_LCBQ_CFG_0 0x074 +#define H_LCBQ(y) ((y) << 12) +#define W_LCBQ(x) ((x) << 8) + +#define ISP_REG_LCBQ_CFG_1 0x07c +#define Y_COOR(y) ((y) << 16) +#define X_COOR(x) ((x) << 0) + +#define ISP_REG_LCCF_CFG_2 0x0e0 +#define ISP_REG_LCCF_CFG_3 0x0e4 +#define ISP_REG_LCCF_CFG_4 0x0e8 +#define ISP_REG_LCCF_CFG_5 0x0ec +#define LCCF_F2_PAR(n) ((n) << 16) +#define LCCF_F1_PAR(n) ((n) << 0) + +#define ISP_REG_OECF_X0_CFG0 0x100 +#define ISP_REG_OECF_X0_CFG1 0x104 +#define ISP_REG_OECF_X0_CFG2 0x108 +#define ISP_REG_OECF_X0_CFG3 0x10c +#define ISP_REG_OECF_X0_CFG4 0x110 +#define ISP_REG_OECF_X0_CFG5 0x114 +#define ISP_REG_OECF_X0_CFG6 0x118 +#define ISP_REG_OECF_X0_CFG7 0x11c + +#define ISP_REG_OECF_Y3_CFG0 0x1e0 +#define ISP_REG_OECF_Y3_CFG1 0x1e4 +#define ISP_REG_OECF_Y3_CFG2 0x1e8 +#define ISP_REG_OECF_Y3_CFG3 0x1ec +#define ISP_REG_OECF_Y3_CFG4 0x1f0 +#define ISP_REG_OECF_Y3_CFG5 0x1f4 +#define ISP_REG_OECF_Y3_CFG6 0x1f8 +#define ISP_REG_OECF_Y3_CFG7 0x1fc + +#define ISP_REG_OECF_S0_CFG0 0x200 +#define ISP_REG_OECF_S3_CFG7 0x27c +#define OCEF_PAR_H(n) ((n) << 16) +#define OCEF_PAR_L(n) ((n) << 0) + +#define ISP_REG_AWB_X0_CFG_0 0x280 +#define ISP_REG_AWB_X0_CFG_1 0x284 +#define ISP_REG_AWB_X1_CFG_0 0x288 +#define ISP_REG_AWB_X1_CFG_1 0x28c +#define ISP_REG_AWB_X2_CFG_0 0x290 +#define ISP_REG_AWB_X2_CFG_1 0x294 +#define ISP_REG_AWB_X3_CFG_0 0x298 +#define ISP_REG_AWB_X3_CFG_1 0x29c +#define AWB_X_SYMBOL_H(n) ((n) << 16) +#define AWB_X_SYMBOL_L(n) ((n) << 0) + +#define ISP_REG_AWB_Y0_CFG_0 0x2a0 +#define ISP_REG_AWB_Y0_CFG_1 0x2a4 +#define ISP_REG_AWB_Y1_CFG_0 0x2a8 +#define ISP_REG_AWB_Y1_CFG_1 0x2ac +#define ISP_REG_AWB_Y2_CFG_0 0x2b0 +#define ISP_REG_AWB_Y2_CFG_1 0x2b4 +#define ISP_REG_AWB_Y3_CFG_0 0x2b8 +#define ISP_REG_AWB_Y3_CFG_1 0x2bc +#define AWB_Y_SYMBOL_H(n) ((n) << 16) +#define AWB_Y_SYMBOL_L(n) ((n) << 0) + +#define ISP_REG_AWB_S0_CFG_0 0x2c0 +#define ISP_REG_AWB_S0_CFG_1 0x2c4 +#define ISP_REG_AWB_S1_CFG_0 0x2c8 +#define ISP_REG_AWB_S1_CFG_1 0x2cc +#define ISP_REG_AWB_S2_CFG_0 0x2d0 +#define ISP_REG_AWB_S2_CFG_1 0x2d4 +#define ISP_REG_AWB_S3_CFG_0 0x2d8 +#define ISP_REG_AWB_S3_CFG_1 0x2dc +#define AWB_S_SYMBOL_H(n) ((n) << 16) +#define AWB_S_SYMBOL_L(n) ((n) << 0) + +#define ISP_REG_OBCG_CFG_0 0x2e0 +#define ISP_REG_OBCG_CFG_1 0x2e4 +#define ISP_REG_OBCG_CFG_2 0x2e8 +#define ISP_REG_OBCG_CFG_3 0x2ec +#define ISP_REG_OBCO_CFG_0 0x2f0 +#define ISP_REG_OBCO_CFG_1 0x2f4 +#define ISP_REG_OBCO_CFG_2 0x2f8 +#define ISP_REG_OBCO_CFG_3 0x2fc +#define GAIN_D_POINT(x) ((x) << 24) +#define GAIN_C_POINT(x) ((x) << 16) +#define GAIN_B_POINT(x) ((x) << 8) +#define GAIN_A_POINT(x) ((x) << 0) +#define OFFSET_D_POINT(x) ((x) << 24) +#define OFFSET_C_POINT(x) ((x) << 16) +#define OFFSET_B_POINT(x) ((x) << 8) +#define OFFSET_A_POINT(x) ((x) << 0) + +#define ISP_REG_ISP_CTRL_0 0xa00 +#define ISPC_LINE BIT(27) +#define ISPC_SC BIT(26) +#define ISPC_CSI BIT(25) +#define ISPC_ISP BIT(24) +#define ISPC_ENUO BIT(20) +#define ISPC_ENLS BIT(17) +#define ISPC_ENSS1 BIT(12) +#define ISPC_ENSS0 BIT(11) +#define ISPC_RST BIT(1) +#define ISPC_EN BIT(0) +#define ISPC_RST_MASK BIT(1) +#define ISPC_INT_ALL_MASK GENMASK(27, 24) + +#define ISP_REG_ISP_CTRL_1 0xa08 +#define CTRL_SAT(n) ((n) << 28) +#define CTRL_DBC BIT(22) +#define CTRL_CTC BIT(21) +#define CTRL_YHIST BIT(20) +#define CTRL_YCURVE BIT(19) +#define CTRL_CTM BIT(18) +#define CTRL_BIYUV BIT(17) +#define CTRL_SCE BIT(8) +#define CTRL_EE BIT(7) +#define CTRL_CCE BIT(5) +#define CTRL_RGE BIT(4) +#define CTRL_CME BIT(3) +#define CTRL_AE BIT(2) +#define CTRL_CE BIT(1) +#define CTRL_SAT_MASK GENMASK(31, 28) + +#define ISP_REG_PIPELINE_XY_SIZE 0xa0c +#define H_ACT_CAP(n) ((n) << 16) +#define W_ACT_CAP(n) ((n) << 0) + +#define ISP_REG_ICTC 0xa10 +#define GF_MODE(n) ((n) << 30) +#define MAXGT(n) ((n) << 16) +#define MINGT(n) ((n) << 0) + +#define ISP_REG_IDBC 0xa14 +#define BADGT(n) ((n) << 16) +#define BADXT(n) ((n) << 0) + +#define ISP_REG_ICFAM 0xa1c +#define CROSS_COV(n) ((n) << 4) +#define HV_W(n) ((n) << 0) + +#define ISP_REG_CS_GAIN 0xa30 +#define CMAD(n) ((n) << 16) +#define CMAB(n) ((n) << 0) + +#define ISP_REG_CS_THRESHOLD 0xa34 +#define CMD(n) ((n) << 16) +#define CMB(n) ((n) << 0) + +#define ISP_REG_CS_OFFSET 0xa38 +#define VOFF(n) ((n) << 16) +#define UOFF(n) ((n) << 0) + +#define ISP_REG_CS_HUE_F 0xa3c +#define SIN(n) ((n) << 16) +#define COS(n) ((n) << 0) + +#define ISP_REG_CS_SCALE 0xa40 + +#define ISP_REG_IESHD 0xa50 +#define SHAD_UP_M BIT(1) +#define SHAD_UP_EN BIT(0) + +#define ISP_REG_YADJ0 0xa54 +#define YOIR(n) ((n) << 16) +#define YIMIN(n) ((n) << 0) + +#define ISP_REG_YADJ1 0xa58 +#define YOMAX(n) ((n) << 16) +#define YOMIN(n) ((n) << 0) + +#define ISP_REG_Y_PLANE_START_ADDR 0xa80 +#define ISP_REG_UV_PLANE_START_ADDR 0xa84 +#define ISP_REG_STRIDE 0xa88 + +#define ISP_REG_ITIIWSR 0xb20 +#define ITI_HSIZE(n) ((n) << 16) +#define ITI_WSIZE(n) ((n) << 0) + +#define ISP_REG_ITIDWLSR 0xb24 +#define ISP_REG_ITIPDFR 0xb38 +#define ISP_REG_ITIDRLSR 0xb3C + +#define ISP_REG_DNYUV_YSWR0 0xc00 +#define ISP_REG_DNYUV_YSWR1 0xc04 +#define ISP_REG_DNYUV_CSWR0 0xc08 +#define ISP_REG_DNYUV_CSWR1 0xc0c +#define YUVSW5(n) ((n) << 20) +#define YUVSW4(n) ((n) << 16) +#define YUVSW3(n) ((n) << 12) +#define YUVSW2(n) ((n) << 8) +#define YUVSW1(n) ((n) << 4) +#define YUVSW0(n) ((n) << 0) + +#define ISP_REG_DNYUV_YDR0 0xc10 +#define ISP_REG_DNYUV_YDR1 0xc14 +#define ISP_REG_DNYUV_YDR2 0xc18 +#define ISP_REG_DNYUV_CDR0 0xc1c +#define ISP_REG_DNYUV_CDR1 0xc20 +#define ISP_REG_DNYUV_CDR2 0xc24 +#define CURVE_D_H(n) ((n) << 16) +#define CURVE_D_L(n) ((n) << 0) + +#define ISP_REG_ICAMD_0 0xc40 +#define ISP_REG_ICAMD_12 0xc70 +#define ISP_REG_ICAMD_20 0xc90 +#define ISP_REG_ICAMD_24 0xca0 +#define ISP_REG_ICAMD_25 0xca4 +#define DNRM_F(n) ((n) << 16) +#define CCM_M_DAT(n) ((n) << 0) + +#define ISP_REG_GAMMA_VAL0 0xe00 +#define ISP_REG_GAMMA_VAL1 0xe04 +#define ISP_REG_GAMMA_VAL2 0xe08 +#define ISP_REG_GAMMA_VAL3 0xe0c +#define ISP_REG_GAMMA_VAL4 0xe10 +#define ISP_REG_GAMMA_VAL5 0xe14 +#define ISP_REG_GAMMA_VAL6 0xe18 +#define ISP_REG_GAMMA_VAL7 0xe1c +#define ISP_REG_GAMMA_VAL8 0xe20 +#define ISP_REG_GAMMA_VAL9 0xe24 +#define ISP_REG_GAMMA_VAL10 0xe28 +#define ISP_REG_GAMMA_VAL11 0xe2c +#define ISP_REG_GAMMA_VAL12 0xe30 +#define ISP_REG_GAMMA_VAL13 0xe34 +#define ISP_REG_GAMMA_VAL14 0xe38 +#define GAMMA_S_VAL(n) ((n) << 16) +#define GAMMA_VAL(n) ((n) << 0) + +#define ISP_REG_R2Y_0 0xe40 +#define ISP_REG_R2Y_1 0xe44 +#define ISP_REG_R2Y_2 0xe48 +#define ISP_REG_R2Y_3 0xe4c +#define ISP_REG_R2Y_4 0xe50 +#define ISP_REG_R2Y_5 0xe54 +#define ISP_REG_R2Y_6 0xe58 +#define ISP_REG_R2Y_7 0xe5c +#define ISP_REG_R2Y_8 0xe60 + +#define ISP_REG_SHARPEN0 0xe80 +#define ISP_REG_SHARPEN1 0xe84 +#define ISP_REG_SHARPEN2 0xe88 +#define ISP_REG_SHARPEN3 0xe8c +#define ISP_REG_SHARPEN4 0xe90 +#define ISP_REG_SHARPEN5 0xe94 +#define ISP_REG_SHARPEN6 0xe98 +#define ISP_REG_SHARPEN7 0xe9c +#define ISP_REG_SHARPEN8 0xea0 +#define ISP_REG_SHARPEN9 0xea4 +#define ISP_REG_SHARPEN10 0xea8 +#define ISP_REG_SHARPEN11 0xeac +#define ISP_REG_SHARPEN12 0xeb0 +#define ISP_REG_SHARPEN13 0xeb4 +#define ISP_REG_SHARPEN14 0xeb8 +#define S_DELTA(n) ((n) << 16) +#define S_WEIGHT(n) ((n) << 8) + +#define ISP_REG_SHARPEN_FS0 0xebc +#define ISP_REG_SHARPEN_FS1 0xec0 +#define ISP_REG_SHARPEN_FS2 0xec4 +#define ISP_REG_SHARPEN_FS3 0xec8 +#define ISP_REG_SHARPEN_FS4 0xecc +#define ISP_REG_SHARPEN_FS5 0xed0 +#define S_FACTOR(n) ((n) << 24) +#define S_SLOPE(n) ((n) << 0) + +#define ISP_REG_SHARPEN_WN 0xed4 +#define PDIRF(n) ((n) << 28) +#define NDIRF(n) ((n) << 24) +#define WSUM(n) ((n) << 0) + +#define ISP_REG_IUVS1 0xed8 +#define UVDIFF2(n) ((n) << 16) +#define UVDIFF1(n) ((n) << 0) + +#define ISP_REG_IUVS2 0xedc +#define UVF(n) ((n) << 24) +#define UVSLOPE(n) ((n) << 0) + +#define ISP_REG_IUVCKS1 0xee0 +#define UVCKDIFF2(n) ((n) << 16) +#define UVCKDIFF1(n) ((n) << 0) + +#define ISP_REG_IUVCKS2 0xee4 + +#define ISP_REG_ISHRPET 0xee8 +#define TH(n) ((n) << 8) +#define EN(n) ((n) << 0) + +#define ISP_REG_YCURVE_0 0xf00 +#define ISP_REG_YCURVE_63 0xffc + +#define IMAGE_MAX_WIDTH 1920 +#define IMAGE_MAX_HEIGH 1080 + +/* pad id for media framework */ +enum stf_isp_pad_id { + STF_ISP_PAD_SINK = 0, + STF_ISP_PAD_SRC, + STF_ISP_PAD_MAX +}; + +struct stf_isp_format { + u32 code; + u8 bpp; +}; + +struct stf_isp_format_table { + const struct stf_isp_format *fmts; + int nfmts; +}; + +struct stf_isp_dev { + struct stfcamss *stfcamss; + struct v4l2_subdev subdev; + struct media_pad pads[STF_ISP_PAD_MAX]; + const struct stf_isp_format_table *formats; + unsigned int nformats; + struct v4l2_subdev *source_subdev; + const struct stf_isp_format *current_fmt; +}; + +int stf_isp_reset(struct stf_isp_dev *isp_dev); +void stf_isp_init_cfg(struct stf_isp_dev *isp_dev); +void stf_isp_settings(struct stf_isp_dev *isp_dev, + struct v4l2_rect *crop, u32 mcode); +void stf_isp_stream_set(struct stf_isp_dev *isp_dev); +int stf_isp_init(struct stfcamss *stfcamss); +int stf_isp_register(struct stf_isp_dev *isp_dev, struct v4l2_device *v4l2_dev); +int stf_isp_unregister(struct stf_isp_dev *isp_dev); + +#endif /* STF_ISP_H */ diff --git a/drivers/staging/media/starfive/camss/stf_isp_hw_ops.c b/drivers/staging/media/starfive/camss/stf_isp_hw_ops.c new file mode 100644 index 0000000000000..a5d6e928714e3 --- /dev/null +++ b/drivers/staging/media/starfive/camss/stf_isp_hw_ops.c @@ -0,0 +1,445 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * stf_isp_hw_ops.c + * + * Register interface file for StarFive ISP driver + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + * + */ + +#include "stf_camss.h" + +static void stf_isp_config_obc(struct stfcamss *stfcamss) +{ + u32 reg_val, reg_add; + + stf_isp_reg_write(stfcamss, ISP_REG_OBC_CFG, OBC_W_H(11) | OBC_W_W(11)); + + reg_val = GAIN_D_POINT(0x40) | GAIN_C_POINT(0x40) | + GAIN_B_POINT(0x40) | GAIN_A_POINT(0x40); + for (reg_add = ISP_REG_OBCG_CFG_0; reg_add <= ISP_REG_OBCG_CFG_3;) { + stf_isp_reg_write(stfcamss, reg_add, reg_val); + reg_add += 4; + } + + reg_val = OFFSET_D_POINT(0) | OFFSET_C_POINT(0) | + OFFSET_B_POINT(0) | OFFSET_A_POINT(0); + for (reg_add = ISP_REG_OBCO_CFG_0; reg_add <= ISP_REG_OBCO_CFG_3;) { + stf_isp_reg_write(stfcamss, reg_add, reg_val); + reg_add += 4; + } +} + +static void stf_isp_config_oecf(struct stfcamss *stfcamss) +{ + u32 reg_add, par_val; + u16 par_h, par_l; + + par_h = 0x10; par_l = 0; + par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l); + for (reg_add = ISP_REG_OECF_X0_CFG0; reg_add <= ISP_REG_OECF_Y3_CFG0;) { + stf_isp_reg_write(stfcamss, reg_add, par_val); + reg_add += 0x20; + } + + par_h = 0x40; par_l = 0x20; + par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l); + for (reg_add = ISP_REG_OECF_X0_CFG1; reg_add <= ISP_REG_OECF_Y3_CFG1;) { + stf_isp_reg_write(stfcamss, reg_add, par_val); + reg_add += 0x20; + } + + par_h = 0x80; par_l = 0x60; + par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l); + for (reg_add = ISP_REG_OECF_X0_CFG2; reg_add <= ISP_REG_OECF_Y3_CFG2;) { + stf_isp_reg_write(stfcamss, reg_add, par_val); + reg_add += 0x20; + } + + par_h = 0xc0; par_l = 0xa0; + par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l); + for (reg_add = ISP_REG_OECF_X0_CFG3; reg_add <= ISP_REG_OECF_Y3_CFG3;) { + stf_isp_reg_write(stfcamss, reg_add, par_val); + reg_add += 0x20; + } + + par_h = 0x100; par_l = 0xe0; + par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l); + for (reg_add = ISP_REG_OECF_X0_CFG4; reg_add <= ISP_REG_OECF_Y3_CFG4;) { + stf_isp_reg_write(stfcamss, reg_add, par_val); + reg_add += 0x20; + } + + par_h = 0x200; par_l = 0x180; + par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l); + for (reg_add = ISP_REG_OECF_X0_CFG5; reg_add <= ISP_REG_OECF_Y3_CFG5;) { + stf_isp_reg_write(stfcamss, reg_add, par_val); + reg_add += 0x20; + } + + par_h = 0x300; par_l = 0x280; + par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l); + for (reg_add = ISP_REG_OECF_X0_CFG6; reg_add <= ISP_REG_OECF_Y3_CFG6;) { + stf_isp_reg_write(stfcamss, reg_add, par_val); + reg_add += 0x20; + } + + par_h = 0x3fe; par_l = 0x380; + par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l); + for (reg_add = ISP_REG_OECF_X0_CFG7; reg_add <= ISP_REG_OECF_Y3_CFG7;) { + stf_isp_reg_write(stfcamss, reg_add, par_val); + reg_add += 0x20; + } + + par_h = 0x80; par_l = 0x80; + par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l); + for (reg_add = ISP_REG_OECF_S0_CFG0; reg_add <= ISP_REG_OECF_S3_CFG7;) { + stf_isp_reg_write(stfcamss, reg_add, par_val); + reg_add += 4; + } +} + +static void stf_isp_config_lccf(struct stfcamss *stfcamss) +{ + u32 reg_add; + + stf_isp_reg_write(stfcamss, ISP_REG_LCCF_CFG_0, + Y_DISTANCE(0x21C) | X_DISTANCE(0x3C0)); + stf_isp_reg_write(stfcamss, ISP_REG_LCCF_CFG_1, LCCF_MAX_DIS(0xb)); + + for (reg_add = ISP_REG_LCCF_CFG_2; reg_add <= ISP_REG_LCCF_CFG_5;) { + stf_isp_reg_write(stfcamss, reg_add, + LCCF_F2_PAR(0x0) | LCCF_F1_PAR(0x0)); + reg_add += 4; + } +} + +static void stf_isp_config_awb(struct stfcamss *stfcamss) +{ + u32 reg_val, reg_add; + u16 symbol_h, symbol_l; + + symbol_h = 0x0; symbol_l = 0x0; + reg_val = AWB_X_SYMBOL_H(symbol_h) | AWB_X_SYMBOL_L(symbol_l); + + for (reg_add = ISP_REG_AWB_X0_CFG_0; reg_add <= ISP_REG_AWB_X3_CFG_1;) { + stf_isp_reg_write(stfcamss, reg_add, reg_val); + reg_add += 4; + } + + symbol_h = 0x0, symbol_l = 0x0; + reg_val = AWB_Y_SYMBOL_H(symbol_h) | AWB_Y_SYMBOL_L(symbol_l); + + for (reg_add = ISP_REG_AWB_Y0_CFG_0; reg_add <= ISP_REG_AWB_Y3_CFG_1;) { + stf_isp_reg_write(stfcamss, reg_add, reg_val); + reg_add += 4; + } + + symbol_h = 0x80, symbol_l = 0x80; + reg_val = AWB_S_SYMBOL_H(symbol_h) | AWB_S_SYMBOL_L(symbol_l); + + for (reg_add = ISP_REG_AWB_S0_CFG_0; reg_add <= ISP_REG_AWB_S3_CFG_1;) { + stf_isp_reg_write(stfcamss, reg_add, reg_val); + reg_add += 4; + } +} + +static void stf_isp_config_grgb(struct stfcamss *stfcamss) +{ + stf_isp_reg_write(stfcamss, ISP_REG_ICTC, + GF_MODE(1) | MAXGT(0x140) | MINGT(0x40)); + stf_isp_reg_write(stfcamss, ISP_REG_IDBC, BADGT(0x200) | BADXT(0x200)); +} + +static void stf_isp_config_cfa(struct stfcamss *stfcamss) +{ + stf_isp_reg_write(stfcamss, ISP_REG_RAW_FORMAT_CFG, + SMY13(0) | SMY12(1) | SMY11(0) | SMY10(1) | SMY3(2) | + SMY2(3) | SMY1(2) | SMY0(3)); + stf_isp_reg_write(stfcamss, ISP_REG_ICFAM, CROSS_COV(3) | HV_W(2)); +} + +static void stf_isp_config_ccm(struct stfcamss *stfcamss) +{ + u32 reg_add; + + stf_isp_reg_write(stfcamss, ISP_REG_ICAMD_0, DNRM_F(6) | CCM_M_DAT(0)); + + for (reg_add = ISP_REG_ICAMD_12; reg_add <= ISP_REG_ICAMD_20;) { + stf_isp_reg_write(stfcamss, reg_add, CCM_M_DAT(0x80)); + reg_add += 0x10; + } + + stf_isp_reg_write(stfcamss, ISP_REG_ICAMD_24, CCM_M_DAT(0x700)); + stf_isp_reg_write(stfcamss, ISP_REG_ICAMD_25, CCM_M_DAT(0x200)); +} + +static void stf_isp_config_gamma(struct stfcamss *stfcamss) +{ + u32 reg_val, reg_add; + u16 gamma_slope_v, gamma_v; + + gamma_slope_v = 0x2400; gamma_v = 0x0; + reg_val = GAMMA_S_VAL(gamma_slope_v) | GAMMA_VAL(gamma_v); + stf_isp_reg_write(stfcamss, ISP_REG_GAMMA_VAL0, reg_val); + + gamma_slope_v = 0x800; gamma_v = 0x20; + for (reg_add = ISP_REG_GAMMA_VAL1; reg_add <= ISP_REG_GAMMA_VAL7;) { + reg_val = GAMMA_S_VAL(gamma_slope_v) | GAMMA_VAL(gamma_v); + stf_isp_reg_write(stfcamss, reg_add, reg_val); + reg_add += 4; + gamma_v += 0x20; + } + + gamma_v = 0x100; + for (reg_add = ISP_REG_GAMMA_VAL8; reg_add <= ISP_REG_GAMMA_VAL13;) { + reg_val = GAMMA_S_VAL(gamma_slope_v) | GAMMA_VAL(gamma_v); + stf_isp_reg_write(stfcamss, reg_add, reg_val); + reg_add += 4; + gamma_v += 0x80; + } + + gamma_v = 0x3fe; + reg_val = GAMMA_S_VAL(gamma_slope_v) | GAMMA_VAL(gamma_v); + stf_isp_reg_write(stfcamss, ISP_REG_GAMMA_VAL14, reg_val); +} + +static void stf_isp_config_r2y(struct stfcamss *stfcamss) +{ + stf_isp_reg_write(stfcamss, ISP_REG_R2Y_0, 0x4C); + stf_isp_reg_write(stfcamss, ISP_REG_R2Y_1, 0x97); + stf_isp_reg_write(stfcamss, ISP_REG_R2Y_2, 0x1d); + stf_isp_reg_write(stfcamss, ISP_REG_R2Y_3, 0x1d5); + stf_isp_reg_write(stfcamss, ISP_REG_R2Y_4, 0x1ac); + stf_isp_reg_write(stfcamss, ISP_REG_R2Y_5, 0x80); + stf_isp_reg_write(stfcamss, ISP_REG_R2Y_6, 0x80); + stf_isp_reg_write(stfcamss, ISP_REG_R2Y_7, 0x194); + stf_isp_reg_write(stfcamss, ISP_REG_R2Y_8, 0x1ec); +} + +static void stf_isp_config_y_curve(struct stfcamss *stfcamss) +{ + u32 reg_add; + u16 y_curve; + + y_curve = 0x0; + for (reg_add = ISP_REG_YCURVE_0; reg_add <= ISP_REG_YCURVE_63;) { + stf_isp_reg_write(stfcamss, reg_add, y_curve); + reg_add += 4; + y_curve += 0x10; + } +} + +static void stf_isp_config_sharpen(struct stfcamss *sc) +{ + u32 reg_add; + + stf_isp_reg_write(sc, ISP_REG_SHARPEN0, S_DELTA(0x7) | S_WEIGHT(0xf)); + stf_isp_reg_write(sc, ISP_REG_SHARPEN1, S_DELTA(0x18) | S_WEIGHT(0xf)); + stf_isp_reg_write(sc, ISP_REG_SHARPEN2, S_DELTA(0x80) | S_WEIGHT(0xf)); + stf_isp_reg_write(sc, ISP_REG_SHARPEN3, S_DELTA(0x100) | S_WEIGHT(0xf)); + stf_isp_reg_write(sc, ISP_REG_SHARPEN4, S_DELTA(0x10) | S_WEIGHT(0xf)); + stf_isp_reg_write(sc, ISP_REG_SHARPEN5, S_DELTA(0x60) | S_WEIGHT(0xf)); + stf_isp_reg_write(sc, ISP_REG_SHARPEN6, S_DELTA(0x100) | S_WEIGHT(0xf)); + stf_isp_reg_write(sc, ISP_REG_SHARPEN7, S_DELTA(0x190) | S_WEIGHT(0xf)); + stf_isp_reg_write(sc, ISP_REG_SHARPEN8, S_DELTA(0x0) | S_WEIGHT(0xf)); + + for (reg_add = ISP_REG_SHARPEN9; reg_add <= ISP_REG_SHARPEN14;) { + stf_isp_reg_write(sc, reg_add, S_WEIGHT(0xf)); + reg_add += 4; + } + + for (reg_add = ISP_REG_SHARPEN_FS0; reg_add <= ISP_REG_SHARPEN_FS5;) { + stf_isp_reg_write(sc, reg_add, S_FACTOR(0x10) | S_SLOPE(0x0)); + reg_add += 4; + } + + stf_isp_reg_write(sc, ISP_REG_SHARPEN_WN, + PDIRF(0x8) | NDIRF(0x8) | WSUM(0xd7c)); + stf_isp_reg_write(sc, ISP_REG_IUVS1, UVDIFF2(0xC0) | UVDIFF1(0x40)); + stf_isp_reg_write(sc, ISP_REG_IUVS2, UVF(0xff) | UVSLOPE(0x0)); + stf_isp_reg_write(sc, ISP_REG_IUVCKS1, + UVCKDIFF2(0xa0) | UVCKDIFF1(0x40)); +} + +static void stf_isp_config_dnyuv(struct stfcamss *stfcamss) +{ + u32 reg_val; + + reg_val = YUVSW5(7) | YUVSW4(7) | YUVSW3(7) | YUVSW2(7) | + YUVSW1(7) | YUVSW0(7); + stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_YSWR0, reg_val); + stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_CSWR0, reg_val); + + reg_val = YUVSW3(7) | YUVSW2(7) | YUVSW1(7) | YUVSW0(7); + stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_YSWR1, reg_val); + stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_CSWR1, reg_val); + + reg_val = CURVE_D_H(0x60) | CURVE_D_L(0x40); + stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_YDR0, reg_val); + stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_CDR0, reg_val); + + reg_val = CURVE_D_H(0xd8) | CURVE_D_L(0x90); + stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_YDR1, reg_val); + stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_CDR1, reg_val); + + reg_val = CURVE_D_H(0x1e6) | CURVE_D_L(0x144); + stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_YDR2, reg_val); + stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_CDR2, reg_val); +} + +static void stf_isp_config_sat(struct stfcamss *stfcamss) +{ + stf_isp_reg_write(stfcamss, ISP_REG_CS_GAIN, CMAD(0x0) | CMAB(0x100)); + stf_isp_reg_write(stfcamss, ISP_REG_CS_THRESHOLD, CMD(0x1f) | CMB(0x1)); + stf_isp_reg_write(stfcamss, ISP_REG_CS_OFFSET, VOFF(0x0) | UOFF(0x0)); + stf_isp_reg_write(stfcamss, ISP_REG_CS_HUE_F, SIN(0x0) | COS(0x100)); + stf_isp_reg_write(stfcamss, ISP_REG_CS_SCALE, 0x8); + stf_isp_reg_write(stfcamss, ISP_REG_YADJ0, YOIR(0x401) | YIMIN(0x1)); + stf_isp_reg_write(stfcamss, ISP_REG_YADJ1, YOMAX(0x3ff) | YOMIN(0x1)); +} + +int stf_isp_reset(struct stf_isp_dev *isp_dev) +{ + stf_isp_reg_set_bit(isp_dev->stfcamss, ISP_REG_ISP_CTRL_0, + ISPC_RST_MASK, ISPC_RST); + stf_isp_reg_set_bit(isp_dev->stfcamss, ISP_REG_ISP_CTRL_0, + ISPC_RST_MASK, 0); + + return 0; +} + +void stf_isp_init_cfg(struct stf_isp_dev *isp_dev) +{ + stf_isp_reg_write(isp_dev->stfcamss, ISP_REG_DC_CFG_1, DC_AXI_ID(0x0)); + stf_isp_reg_write(isp_dev->stfcamss, ISP_REG_DEC_CFG, + DEC_V_KEEP(0x0) | + DEC_V_PERIOD(0x0) | + DEC_H_KEEP(0x0) | + DEC_H_PERIOD(0x0)); + + stf_isp_config_obc(isp_dev->stfcamss); + stf_isp_config_oecf(isp_dev->stfcamss); + stf_isp_config_lccf(isp_dev->stfcamss); + stf_isp_config_awb(isp_dev->stfcamss); + stf_isp_config_grgb(isp_dev->stfcamss); + stf_isp_config_cfa(isp_dev->stfcamss); + stf_isp_config_ccm(isp_dev->stfcamss); + stf_isp_config_gamma(isp_dev->stfcamss); + stf_isp_config_r2y(isp_dev->stfcamss); + stf_isp_config_y_curve(isp_dev->stfcamss); + stf_isp_config_sharpen(isp_dev->stfcamss); + stf_isp_config_dnyuv(isp_dev->stfcamss); + stf_isp_config_sat(isp_dev->stfcamss); + + stf_isp_reg_write(isp_dev->stfcamss, ISP_REG_CSI_MODULE_CFG, + CSI_DUMP_EN | CSI_SC_EN | CSI_AWB_EN | + CSI_LCCF_EN | CSI_OECF_EN | CSI_OBC_EN | CSI_DEC_EN); + stf_isp_reg_write(isp_dev->stfcamss, ISP_REG_ISP_CTRL_1, + CTRL_SAT(1) | CTRL_DBC | CTRL_CTC | CTRL_YHIST | + CTRL_YCURVE | CTRL_BIYUV | CTRL_SCE | CTRL_EE | + CTRL_CCE | CTRL_RGE | CTRL_CME | CTRL_AE | CTRL_CE); +} + +static void stf_isp_config_crop(struct stfcamss *stfcamss, + struct v4l2_rect *crop) +{ + u32 bpp = stfcamss->isp_dev.current_fmt->bpp; + u32 val; + + val = VSTART_CAP(crop->top) | HSTART_CAP(crop->left); + stf_isp_reg_write(stfcamss, ISP_REG_PIC_CAPTURE_START_CFG, val); + + val = VEND_CAP(crop->height + crop->top - 1) | + HEND_CAP(crop->width + crop->left - 1); + stf_isp_reg_write(stfcamss, ISP_REG_PIC_CAPTURE_END_CFG, val); + + val = H_ACT_CAP(crop->height) | W_ACT_CAP(crop->width); + stf_isp_reg_write(stfcamss, ISP_REG_PIPELINE_XY_SIZE, val); + + val = ALIGN(crop->width * bpp / 8, STFCAMSS_FRAME_WIDTH_ALIGN_8); + stf_isp_reg_write(stfcamss, ISP_REG_STRIDE, val); +} + +static void stf_isp_config_raw_fmt(struct stfcamss *stfcamss, u32 mcode) +{ + u32 val, val1; + + switch (mcode) { + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SRGGB8_1X8: + /* 3 2 3 2 1 0 1 0 B Gb B Gb Gr R Gr R */ + val = SMY13(3) | SMY12(2) | SMY11(3) | SMY10(2) | + SMY3(1) | SMY2(0) | SMY1(1) | SMY0(0); + val1 = CTRL_SAT(0x0); + break; + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGRBG8_1X8: + /* 2 3 2 3 0 1 0 1, Gb B Gb B R Gr R Gr */ + val = SMY13(2) | SMY12(3) | SMY11(2) | SMY10(3) | + SMY3(0) | SMY2(1) | SMY1(0) | SMY0(1); + val1 = CTRL_SAT(0x2); + break; + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGBRG8_1X8: + /* 1 0 1 0 3 2 3 2, Gr R Gr R B Gb B Gb */ + val = SMY13(1) | SMY12(0) | SMY11(1) | SMY10(0) | + SMY3(3) | SMY2(2) | SMY1(3) | SMY0(2); + val1 = CTRL_SAT(0x3); + break; + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SBGGR8_1X8: + /* 0 1 0 1 2 3 2 3 R Gr R Gr Gb B Gb B */ + val = SMY13(0) | SMY12(1) | SMY11(0) | SMY10(1) | + SMY3(2) | SMY2(3) | SMY1(2) | SMY0(3); + val1 = CTRL_SAT(0x1); + break; + default: + val = SMY13(0) | SMY12(1) | SMY11(0) | SMY10(1) | + SMY3(2) | SMY2(3) | SMY1(2) | SMY0(3); + val1 = CTRL_SAT(0x1); + break; + } + stf_isp_reg_write(stfcamss, ISP_REG_RAW_FORMAT_CFG, val); + stf_isp_reg_set_bit(stfcamss, ISP_REG_ISP_CTRL_1, CTRL_SAT_MASK, val1); +} + +void stf_isp_settings(struct stf_isp_dev *isp_dev, + struct v4l2_rect *crop, u32 mcode) +{ + struct stfcamss *stfcamss = isp_dev->stfcamss; + + stf_isp_config_crop(stfcamss, crop); + stf_isp_config_raw_fmt(stfcamss, mcode); + + stf_isp_reg_set_bit(stfcamss, ISP_REG_DUMP_CFG_1, + DUMP_BURST_LEN_MASK | DUMP_SD_MASK, + DUMP_BURST_LEN(3)); + + stf_isp_reg_write(stfcamss, ISP_REG_ITIIWSR, + ITI_HSIZE(IMAGE_MAX_HEIGH) | + ITI_WSIZE(IMAGE_MAX_WIDTH)); + stf_isp_reg_write(stfcamss, ISP_REG_ITIDWLSR, 0x960); + stf_isp_reg_write(stfcamss, ISP_REG_ITIDRLSR, 0x960); + stf_isp_reg_write(stfcamss, ISP_REG_SENSOR, IMAGER_SEL(1)); +} + +void stf_isp_stream_set(struct stf_isp_dev *isp_dev) +{ + struct stfcamss *stfcamss = isp_dev->stfcamss; + + stf_isp_reg_write_delay(stfcamss, ISP_REG_ISP_CTRL_0, + ISPC_ENUO | ISPC_ENLS | ISPC_RST, 10); + stf_isp_reg_write_delay(stfcamss, ISP_REG_ISP_CTRL_0, + ISPC_ENUO | ISPC_ENLS, 10); + stf_isp_reg_write(stfcamss, ISP_REG_IESHD, SHAD_UP_M); + stf_isp_reg_write_delay(stfcamss, ISP_REG_ISP_CTRL_0, + ISPC_ENUO | ISPC_ENLS | ISPC_EN, 10); + stf_isp_reg_write_delay(stfcamss, ISP_REG_CSIINTS, + CSI_INTS(1) | CSI_SHA_M(4), 10); + stf_isp_reg_write_delay(stfcamss, ISP_REG_CSIINTS, + CSI_INTS(2) | CSI_SHA_M(4), 10); + stf_isp_reg_write_delay(stfcamss, ISP_REG_CSI_INPUT_EN_AND_STATUS, + CSI_EN_S, 10); +} From 72abbe91cf5b6cfc0a40b6f5ba54115cdd7df102 Mon Sep 17 00:00:00 2001 From: Jack Zhu Date: Thu, 24 Aug 2023 16:01:07 +0800 Subject: [PATCH 79/92] media: staging: media: starfive: camss: Add capture driver Add capture driver for StarFive Camera Subsystem. It contains two video devices: capture_yuv and capture_raw. Signed-off-by: Jack Zhu --- drivers/staging/media/starfive/camss/Makefile | 1 + .../staging/media/starfive/camss/stf_camss.h | 2 + .../media/starfive/camss/stf_capture.c | 603 ++++++++++++++++++ .../media/starfive/camss/stf_capture.h | 87 +++ 4 files changed, 693 insertions(+) create mode 100644 drivers/staging/media/starfive/camss/stf_capture.c create mode 100644 drivers/staging/media/starfive/camss/stf_capture.h diff --git a/drivers/staging/media/starfive/camss/Makefile b/drivers/staging/media/starfive/camss/Makefile index cdf57e8c95468..685f9f16acba3 100644 --- a/drivers/staging/media/starfive/camss/Makefile +++ b/drivers/staging/media/starfive/camss/Makefile @@ -5,6 +5,7 @@ starfive-camss-objs += \ stf_camss.o \ + stf_capture.o \ stf_isp.o \ stf_isp_hw_ops.o \ stf_video.o diff --git a/drivers/staging/media/starfive/camss/stf_camss.h b/drivers/staging/media/starfive/camss/stf_camss.h index e0f7ee9f25834..5e026a4c0528f 100644 --- a/drivers/staging/media/starfive/camss/stf_camss.h +++ b/drivers/staging/media/starfive/camss/stf_camss.h @@ -19,6 +19,7 @@ #include #include "stf_isp.h" +#include "stf_capture.h" enum stf_port_num { STF_PORT_DVP = 0, @@ -52,6 +53,7 @@ struct stfcamss { struct media_pipeline pipe; struct device *dev; struct stf_isp_dev isp_dev; + struct stf_capture captures[STF_CAPTURE_NUM]; struct v4l2_async_notifier notifier; void __iomem *syscon_base; void __iomem *isp_base; diff --git a/drivers/staging/media/starfive/camss/stf_capture.c b/drivers/staging/media/starfive/camss/stf_capture.c new file mode 100644 index 0000000000000..ebdb2550174e9 --- /dev/null +++ b/drivers/staging/media/starfive/camss/stf_capture.c @@ -0,0 +1,603 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * stf_capture.c + * + * StarFive Camera Subsystem - capture device + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ + +#include "stf_camss.h" + +static const char * const stf_cap_names[] = { + "capture_raw", + "capture_yuv", +}; + +static const struct stfcamss_format_info stf_wr_fmts[] = { + { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .pixelformat = V4L2_PIX_FMT_SRGGB10, + .planes = 1, + .vsub = { 1 }, + .bpp = 10, + }, + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pixelformat = V4L2_PIX_FMT_SGRBG10, + .planes = 1, + .vsub = { 1 }, + .bpp = 10, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .pixelformat = V4L2_PIX_FMT_SGBRG10, + .planes = 1, + .vsub = { 1 }, + .bpp = 10, + }, + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pixelformat = V4L2_PIX_FMT_SBGGR10, + .planes = 1, + .vsub = { 1 }, + .bpp = 10, + }, +}; + +static const struct stfcamss_format_info stf_isp_fmts[] = { + { + .code = MEDIA_BUS_FMT_YUYV8_1_5X8, + .pixelformat = V4L2_PIX_FMT_NV12, + .planes = 2, + .vsub = { 1, 2 }, + .bpp = 8, + }, +}; + +static inline struct stf_capture *to_stf_capture(struct stfcamss_video *video) +{ + return container_of(video, struct stf_capture, video); +} + +static void stf_set_raw_addr(struct stfcamss *stfcamss, dma_addr_t addr) +{ + stf_syscon_reg_write(stfcamss, VIN_START_ADDR_O, (long)addr); + stf_syscon_reg_write(stfcamss, VIN_START_ADDR_N, (long)addr); +} + +static void stf_set_yuv_addr(struct stfcamss *stfcamss, + dma_addr_t y_addr, dma_addr_t uv_addr) +{ + stf_isp_reg_write(stfcamss, ISP_REG_Y_PLANE_START_ADDR, y_addr); + stf_isp_reg_write(stfcamss, ISP_REG_UV_PLANE_START_ADDR, uv_addr); +} + +static void stf_init_addrs(struct stfcamss_video *video) +{ + struct stf_capture *cap = to_stf_capture(video); + struct stf_v_buf *output = &cap->buffers; + dma_addr_t addr0, addr1; + + output->active_buf = 0; + + if (!output->buf[0]) + return; + + addr0 = output->buf[0]->addr[0]; + addr1 = output->buf[0]->addr[1]; + + if (cap->type == STF_CAPTURE_RAW) + stf_set_raw_addr(video->stfcamss, addr0); + else if (cap->type == STF_CAPTURE_YUV) + stf_set_yuv_addr(video->stfcamss, addr0, addr1); +} + +static struct stfcamss_buffer *stf_buf_get_pending(struct stf_v_buf *output) +{ + struct stfcamss_buffer *buffer = NULL; + + if (!list_empty(&output->pending_bufs)) { + buffer = list_first_entry(&output->pending_bufs, + struct stfcamss_buffer, + queue); + list_del(&buffer->queue); + } + + return buffer; +} + +static void stf_cap_s_cfg(struct stfcamss_video *video) +{ + struct stf_capture *cap = to_stf_capture(video); + struct stf_v_buf *output = &cap->buffers; + unsigned long flags; + + spin_lock_irqsave(&output->lock, flags); + + output->state = STF_OUTPUT_IDLE; + output->buf[0] = stf_buf_get_pending(output); + + if (!output->buf[0] && output->buf[1]) { + output->buf[0] = output->buf[1]; + output->buf[1] = NULL; + } + + if (output->buf[0]) + output->state = STF_OUTPUT_SINGLE; + + output->sequence = 0; + stf_init_addrs(video); + + spin_unlock_irqrestore(&output->lock, flags); +} + +static int stf_cap_s_cleanup(struct stfcamss_video *video) +{ + struct stf_capture *cap = to_stf_capture(video); + struct stf_v_buf *output = &cap->buffers; + unsigned long flags; + + spin_lock_irqsave(&output->lock, flags); + + output->state = STF_OUTPUT_OFF; + + spin_unlock_irqrestore(&output->lock, flags); + + return 0; +} + +static void stf_wr_data_en(struct stfcamss_video *video) +{ + struct stf_capture *cap = to_stf_capture(video); + struct stfcamss *stfcamss = cap->video.stfcamss; + + stf_syscon_reg_set_bit(stfcamss, VIN_CHANNEL_SEL_EN, U0_VIN_AXIWR0_EN); +} + +static void stf_wr_irq_enable(struct stfcamss_video *video) +{ + struct stf_capture *cap = to_stf_capture(video); + struct stfcamss *stfcamss = cap->video.stfcamss; + + stf_syscon_reg_clear_bit(stfcamss, VIN_INRT_PIX_CFG, U0_VIN_INTR_M); +} + +static void stf_wr_irq_disable(struct stfcamss_video *video) +{ + struct stf_capture *cap = to_stf_capture(video); + struct stfcamss *stfcamss = cap->video.stfcamss; + + stf_syscon_reg_set_bit(stfcamss, VIN_INRT_PIX_CFG, U0_VIN_INTR_CLEAN); + stf_syscon_reg_clear_bit(stfcamss, VIN_INRT_PIX_CFG, U0_VIN_INTR_CLEAN); + stf_syscon_reg_set_bit(stfcamss, VIN_INRT_PIX_CFG, U0_VIN_INTR_M); +} + +static void stf_channel_set(struct stfcamss_video *video) +{ + struct stf_capture *cap = to_stf_capture(video); + struct stfcamss *stfcamss = cap->video.stfcamss; + u32 val; + + if (cap->type == STF_CAPTURE_RAW) { + val = stf_syscon_reg_read(stfcamss, VIN_CHANNEL_SEL_EN); + val &= ~U0_VIN_CHANNEL_SEL_MASK; + val |= CHANNEL(0); + stf_syscon_reg_write(stfcamss, VIN_CHANNEL_SEL_EN, val); + + val = stf_syscon_reg_read(stfcamss, VIN_INRT_PIX_CFG); + val &= ~U0_VIN_PIX_CT_MASK; + val |= PIX_CT(1); + + val &= ~U0_VIN_PIXEL_HEIGH_BIT_SEL_MAKS; + val |= PIXEL_HEIGH_BIT_SEL(0); + + val &= ~U0_VIN_PIX_CNT_END_MASK; + val |= PIX_CNT_END(IMAGE_MAX_WIDTH / 4 - 1); + + stf_syscon_reg_write(stfcamss, VIN_INRT_PIX_CFG, val); + } else if (cap->type == STF_CAPTURE_YUV) { + val = stf_syscon_reg_read(stfcamss, VIN_CFG_REG); + val &= ~U0_VIN_MIPI_BYTE_EN_ISP0_MASK; + val |= U0_VIN_MIPI_BYTE_EN_ISP0(0); + + val &= ~U0_VIN_MIPI_CHANNEL_SEL0_MASK; + val |= U0_VIN_MIPI_CHANNEL_SEL0(0); + + val &= ~U0_VIN_PIX_NUM_MASK; + val |= U0_VIN_PIX_NUM(0); + + val &= ~U0_VIN_P_I_MIPI_HAEDER_EN0_MASK; + val |= U0_VIN_P_I_MIPI_HAEDER_EN0(1); + + stf_syscon_reg_write(stfcamss, VIN_CFG_REG, val); + } +} + +static void stf_capture_start(struct stfcamss_video *video) +{ + struct stf_capture *cap = to_stf_capture(video); + + stf_channel_set(video); + if (cap->type == STF_CAPTURE_RAW) { + stf_wr_irq_enable(video); + stf_wr_data_en(video); + } + + stf_cap_s_cfg(video); +} + +static void stf_capture_stop(struct stfcamss_video *video) +{ + struct stf_capture *cap = to_stf_capture(video); + + if (cap->type == STF_CAPTURE_RAW) + stf_wr_irq_disable(video); + + stf_cap_s_cleanup(video); +} + +static void stf_capture_init(struct stfcamss *stfcamss, struct stf_capture *cap) +{ + cap->buffers.state = STF_OUTPUT_OFF; + cap->buffers.buf[0] = NULL; + cap->buffers.buf[1] = NULL; + cap->buffers.active_buf = 0; + atomic_set(&cap->buffers.frame_skip, 4); + INIT_LIST_HEAD(&cap->buffers.pending_bufs); + INIT_LIST_HEAD(&cap->buffers.ready_bufs); + spin_lock_init(&cap->buffers.lock); + + cap->video.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cap->video.stfcamss = stfcamss; + cap->video.bpl_alignment = 16 * 8; + + if (cap->type == STF_CAPTURE_RAW) { + cap->video.formats = stf_wr_fmts; + cap->video.nformats = ARRAY_SIZE(stf_wr_fmts); + cap->video.bpl_alignment = 8; + } else if (cap->type == STF_CAPTURE_YUV) { + cap->video.formats = stf_isp_fmts; + cap->video.nformats = ARRAY_SIZE(stf_isp_fmts); + cap->video.bpl_alignment = 1; + } +} + +static void stf_buf_add_ready(struct stf_v_buf *output, + struct stfcamss_buffer *buffer) +{ + INIT_LIST_HEAD(&buffer->queue); + list_add_tail(&buffer->queue, &output->ready_bufs); +} + +static struct stfcamss_buffer *stf_buf_get_ready(struct stf_v_buf *output) +{ + struct stfcamss_buffer *buffer = NULL; + + if (!list_empty(&output->ready_bufs)) { + buffer = list_first_entry(&output->ready_bufs, + struct stfcamss_buffer, + queue); + list_del(&buffer->queue); + } + + return buffer; +} + +static void stf_buf_add_pending(struct stf_v_buf *output, + struct stfcamss_buffer *buffer) +{ + INIT_LIST_HEAD(&buffer->queue); + list_add_tail(&buffer->queue, &output->pending_bufs); +} + +static void stf_buf_update_on_last(struct stf_v_buf *output) +{ + switch (output->state) { + case STF_OUTPUT_CONTINUOUS: + output->state = STF_OUTPUT_SINGLE; + output->active_buf = !output->active_buf; + break; + case STF_OUTPUT_SINGLE: + output->state = STF_OUTPUT_STOPPING; + break; + default: + break; + } +} + +static void stf_buf_update_on_next(struct stf_v_buf *output) +{ + switch (output->state) { + case STF_OUTPUT_CONTINUOUS: + output->active_buf = !output->active_buf; + break; + case STF_OUTPUT_SINGLE: + default: + break; + } +} + +static void stf_buf_update_on_new(struct stfcamss_video *video, + struct stfcamss_buffer *new_buf) +{ + struct stf_capture *cap = to_stf_capture(video); + struct stf_v_buf *output = &cap->buffers; + + switch (output->state) { + case STF_OUTPUT_SINGLE: + stf_buf_add_pending(output, new_buf); + break; + case STF_OUTPUT_IDLE: + if (!output->buf[0]) { + output->buf[0] = new_buf; + stf_init_addrs(video); + output->state = STF_OUTPUT_SINGLE; + } else { + stf_buf_add_pending(output, new_buf); + } + break; + case STF_OUTPUT_STOPPING: + if (output->last_buffer) { + output->buf[output->active_buf] = output->last_buffer; + output->last_buffer = NULL; + } + + output->state = STF_OUTPUT_SINGLE; + stf_buf_add_pending(output, new_buf); + break; + case STF_OUTPUT_CONTINUOUS: + default: + stf_buf_add_pending(output, new_buf); + break; + } +} + +static void stf_buf_flush(struct stf_v_buf *output, enum vb2_buffer_state state) +{ + struct stfcamss_buffer *buf; + struct stfcamss_buffer *t; + + list_for_each_entry_safe(buf, t, &output->pending_bufs, queue) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->queue); + } + list_for_each_entry_safe(buf, t, &output->ready_bufs, queue) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->queue); + } +} + +static void stf_buf_done(struct stf_v_buf *output) +{ + struct stfcamss_buffer *ready_buf; + u64 ts = ktime_get_ns(); + unsigned long flags; + + if (output->state == STF_OUTPUT_OFF || + output->state == STF_OUTPUT_RESERVED) + return; + + spin_lock_irqsave(&output->lock, flags); + + while ((ready_buf = stf_buf_get_ready(output))) { + ready_buf->vb.vb2_buf.timestamp = ts; + ready_buf->vb.sequence = output->sequence++; + + vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + } + + spin_unlock_irqrestore(&output->lock, flags); +} + +static void stf_change_buffer(struct stf_v_buf *output) +{ + struct stf_capture *cap = container_of(output, struct stf_capture, + buffers); + struct stfcamss *stfcamss = cap->video.stfcamss; + struct stfcamss_buffer *ready_buf; + dma_addr_t *new_addr; + unsigned long flags; + u32 active_index; + + if (output->state == STF_OUTPUT_OFF || + output->state == STF_OUTPUT_STOPPING || + output->state == STF_OUTPUT_RESERVED || + output->state == STF_OUTPUT_IDLE) + return; + + spin_lock_irqsave(&output->lock, flags); + + active_index = output->active_buf; + + ready_buf = output->buf[active_index]; + if (!ready_buf) { + dev_dbg(stfcamss->dev, "missing ready buf %d %d.\n", + active_index, output->state); + active_index = !active_index; + ready_buf = output->buf[active_index]; + if (!ready_buf) { + dev_dbg(stfcamss->dev, + "missing ready buf2 %d %d.\n", + active_index, output->state); + goto out_unlock; + } + } + + /* Get next buffer */ + output->buf[active_index] = stf_buf_get_pending(output); + if (!output->buf[active_index]) { + new_addr = ready_buf->addr; + stf_buf_update_on_last(output); + } else { + new_addr = output->buf[active_index]->addr; + stf_buf_update_on_next(output); + } + + if (output->state == STF_OUTPUT_STOPPING) { + output->last_buffer = ready_buf; + } else { + if (cap->type == STF_CAPTURE_RAW) + stf_set_raw_addr(stfcamss, new_addr[0]); + else if (cap->type == STF_CAPTURE_YUV) + stf_set_yuv_addr(stfcamss, new_addr[0], new_addr[1]); + + stf_buf_add_ready(output, ready_buf); + } + +out_unlock: + spin_unlock_irqrestore(&output->lock, flags); +} + +irqreturn_t stf_wr_irq_handler(int irq, void *priv) +{ + struct stfcamss *stfcamss = priv; + struct stf_capture *cap = &stfcamss->captures[STF_CAPTURE_RAW]; + + if (atomic_dec_if_positive(&cap->buffers.frame_skip) < 0) { + stf_change_buffer(&cap->buffers); + stf_buf_done(&cap->buffers); + } + + stf_syscon_reg_set_bit(stfcamss, VIN_INRT_PIX_CFG, U0_VIN_INTR_CLEAN); + stf_syscon_reg_clear_bit(stfcamss, VIN_INRT_PIX_CFG, U0_VIN_INTR_CLEAN); + + return IRQ_HANDLED; +} + +irqreturn_t stf_isp_irq_handler(int irq, void *priv) +{ + struct stfcamss *stfcamss = priv; + struct stf_capture *cap = &stfcamss->captures[STF_CAPTURE_YUV]; + u32 status; + + status = stf_isp_reg_read(stfcamss, ISP_REG_ISP_CTRL_0); + if (status & ISPC_ISP) { + if (status & ISPC_ENUO) + stf_buf_done(&cap->buffers); + + stf_isp_reg_write(stfcamss, ISP_REG_ISP_CTRL_0, + (status & ~ISPC_INT_ALL_MASK) | + ISPC_ISP | ISPC_CSI | ISPC_SC); + } + + return IRQ_HANDLED; +} + +irqreturn_t stf_line_irq_handler(int irq, void *priv) +{ + struct stfcamss *stfcamss = priv; + struct stf_capture *cap = &stfcamss->captures[STF_CAPTURE_YUV]; + u32 status; + + status = stf_isp_reg_read(stfcamss, ISP_REG_ISP_CTRL_0); + if (status & ISPC_LINE) { + if (atomic_dec_if_positive(&cap->buffers.frame_skip) < 0) { + if ((status & ISPC_ENUO)) + stf_change_buffer(&cap->buffers); + } + + stf_isp_reg_set_bit(stfcamss, ISP_REG_CSIINTS, + CSI_INTS_MASK, CSI_INTS(0x3)); + stf_isp_reg_set_bit(stfcamss, ISP_REG_IESHD, + SHAD_UP_M | SHAD_UP_EN, 0x3); + + stf_isp_reg_write(stfcamss, ISP_REG_ISP_CTRL_0, + (status & ~ISPC_INT_ALL_MASK) | ISPC_LINE); + } + + return IRQ_HANDLED; +} + +static int stf_queue_buffer(struct stfcamss_video *video, + struct stfcamss_buffer *buf) +{ + struct stf_capture *cap = to_stf_capture(video); + struct stf_v_buf *v_bufs = &cap->buffers; + unsigned long flags; + + spin_lock_irqsave(&v_bufs->lock, flags); + stf_buf_update_on_new(video, buf); + spin_unlock_irqrestore(&v_bufs->lock, flags); + + return 0; +} + +static int stf_flush_buffers(struct stfcamss_video *video, + enum vb2_buffer_state state) +{ + struct stf_capture *cap = to_stf_capture(video); + struct stf_v_buf *v_bufs = &cap->buffers; + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&v_bufs->lock, flags); + + stf_buf_flush(v_bufs, state); + + for (i = 0; i < ARRAY_SIZE(v_bufs->buf); i++) { + if (v_bufs->buf[i]) + vb2_buffer_done(&v_bufs->buf[i]->vb.vb2_buf, state); + + v_bufs->buf[i] = NULL; + } + + if (v_bufs->last_buffer) { + vb2_buffer_done(&v_bufs->last_buffer->vb.vb2_buf, state); + v_bufs->last_buffer = NULL; + } + + spin_unlock_irqrestore(&v_bufs->lock, flags); + return 0; +} + +static const struct stfcamss_video_ops stf_capture_ops = { + .queue_buffer = stf_queue_buffer, + .flush_buffers = stf_flush_buffers, + .start_streaming = stf_capture_start, + .stop_streaming = stf_capture_stop, +}; + +static void stf_capture_unregister_one(struct stf_capture *cap) +{ + if (!video_is_registered(&cap->video.vdev)) + return; + + media_entity_cleanup(&cap->video.vdev.entity); + vb2_video_unregister_device(&cap->video.vdev); +} + +void stf_capture_unregister(struct stfcamss *stfcamss) +{ + struct stf_capture *cap_raw = &stfcamss->captures[STF_CAPTURE_RAW]; + struct stf_capture *cap_yuv = &stfcamss->captures[STF_CAPTURE_YUV]; + + stf_capture_unregister_one(cap_raw); + stf_capture_unregister_one(cap_yuv); +} + +int stf_capture_register(struct stfcamss *stfcamss, + struct v4l2_device *v4l2_dev) +{ + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(stfcamss->captures); i++) { + struct stf_capture *capture = &stfcamss->captures[i]; + + capture->type = i; + capture->video.ops = &stf_capture_ops; + stf_capture_init(stfcamss, capture); + + ret = stf_video_register(&capture->video, v4l2_dev, + stf_cap_names[i]); + if (ret < 0) { + dev_err(stfcamss->dev, + "Failed to register video node: %d\n", ret); + stf_capture_unregister(stfcamss); + return ret; + } + } + + return 0; +} diff --git a/drivers/staging/media/starfive/camss/stf_capture.h b/drivers/staging/media/starfive/camss/stf_capture.h new file mode 100644 index 0000000000000..a01782eb50192 --- /dev/null +++ b/drivers/staging/media/starfive/camss/stf_capture.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * stf_capture.h + * + * Starfive Camera Subsystem driver + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ + +#ifndef STF_CAPTURE_H +#define STF_CAPTURE_H + +#include "stf_video.h" + +#define VIN_CHANNEL_SEL_EN 0x14 +#define VIN_START_ADDR_N 0x18 +#define VIN_INRT_PIX_CFG 0x1c +#define VIN_START_ADDR_O 0x20 +#define VIN_CFG_REG 0x24 + +#define U0_VIN_CNFG_AXI_DVP_EN BIT(2) + +#define U0_VIN_CHANNEL_SEL_MASK GENMASK(3, 0) +#define U0_VIN_AXIWR0_EN BIT(4) +#define CHANNEL(x) ((x) << 0) + +#define U0_VIN_INTR_CLEAN BIT(0) +#define U0_VIN_INTR_M BIT(1) +#define U0_VIN_PIX_CNT_END_MASK GENMASK(12, 2) +#define U0_VIN_PIX_CT_MASK GENMASK(14, 13) +#define U0_VIN_PIXEL_HEIGH_BIT_SEL_MAKS GENMASK(16, 15) + +#define PIX_CNT_END(x) ((x) << 2) +#define PIX_CT(x) ((x) << 13) +#define PIXEL_HEIGH_BIT_SEL(x) ((x) << 15) + +#define U0_VIN_CNFG_DVP_HS_POS BIT(1) +#define U0_VIN_CNFG_DVP_SWAP_EN BIT(2) +#define U0_VIN_CNFG_DVP_VS_POS BIT(3) +#define U0_VIN_CNFG_GEN_EN_AXIRD BIT(4) +#define U0_VIN_CNFG_ISP_DVP_EN0 BIT(5) +#define U0_VIN_MIPI_BYTE_EN_ISP0(n) ((n) << 6) +#define U0_VIN_MIPI_CHANNEL_SEL0(n) ((n) << 8) +#define U0_VIN_P_I_MIPI_HAEDER_EN0(n) ((n) << 12) +#define U0_VIN_PIX_NUM(n) ((n) << 13) +#define U0_VIN_MIPI_BYTE_EN_ISP0_MASK GENMASK(7, 6) +#define U0_VIN_MIPI_CHANNEL_SEL0_MASK GENMASK(11, 8) +#define U0_VIN_P_I_MIPI_HAEDER_EN0_MASK BIT(12) +#define U0_VIN_PIX_NUM_MASK GENMASK(16, 13) + +enum stf_v_state { + STF_OUTPUT_OFF, + STF_OUTPUT_RESERVED, + STF_OUTPUT_SINGLE, + STF_OUTPUT_CONTINUOUS, + STF_OUTPUT_IDLE, + STF_OUTPUT_STOPPING +}; + +struct stf_v_buf { + int active_buf; + struct stfcamss_buffer *buf[2]; + struct stfcamss_buffer *last_buffer; + struct list_head pending_bufs; + struct list_head ready_bufs; + enum stf_v_state state; + unsigned int sequence; + /* protects the above member variables */ + spinlock_t lock; + atomic_t frame_skip; +}; + +struct stf_capture { + struct stfcamss_video video; + struct stf_v_buf buffers; + enum stf_capture_type type; +}; + +irqreturn_t stf_wr_irq_handler(int irq, void *priv); +irqreturn_t stf_isp_irq_handler(int irq, void *priv); +irqreturn_t stf_line_irq_handler(int irq, void *priv); +int stf_capture_register(struct stfcamss *stfcamss, + struct v4l2_device *v4l2_dev); +void stf_capture_unregister(struct stfcamss *stfcamss); + +#endif + From 3a3921335cc6f1d8e02c4e04ec2195f9f171e4f7 Mon Sep 17 00:00:00 2001 From: Jack Zhu Date: Thu, 24 Aug 2023 16:01:08 +0800 Subject: [PATCH 80/92] media: staging: media: starfive: camss: Add interrupt handling Parse interrupt resources and register interrupt handlers. Signed-off-by: Jack Zhu --- .../staging/media/starfive/camss/stf_camss.c | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/staging/media/starfive/camss/stf_camss.c b/drivers/staging/media/starfive/camss/stf_camss.c index 75ebc3a35218a..918b8b2a77132 100644 --- a/drivers/staging/media/starfive/camss/stf_camss.c +++ b/drivers/staging/media/starfive/camss/stf_camss.c @@ -35,6 +35,12 @@ static const char * const stfcamss_resets[] = { "rst_isp_top_axi", }; +static const struct stf_isr_data stf_isrs[] = { + {"wr_irq", stf_wr_irq_handler}, + {"isp_irq", stf_isp_irq_handler}, + {"line_irq", stf_line_irq_handler}, +}; + static int stfcamss_get_mem_res(struct stfcamss *stfcamss) { struct platform_device *pdev = to_platform_device(stfcamss->dev); @@ -157,6 +163,21 @@ static int stfcamss_probe(struct platform_device *pdev) stfcamss->dev = dev; + for (i = 0; i < ARRAY_SIZE(stf_isrs); ++i) { + int irq; + + irq = platform_get_irq(pdev, i); + if (irq < 0) + return irq; + + ret = devm_request_irq(stfcamss->dev, irq, stf_isrs[i].isr, 0, + stf_isrs[i].name, stfcamss); + if (ret) { + dev_err(dev, "request irq failed: %d\n", ret); + return ret; + } + } + stfcamss->nclks = ARRAY_SIZE(stfcamss->sys_clk); for (i = 0; i < stfcamss->nclks; ++i) stfcamss->sys_clk[i].id = stfcamss_clocks[i]; From e223b1ed50e23796f3049ae0db0921b6d230883c Mon Sep 17 00:00:00 2001 From: Jack Zhu Date: Thu, 24 Aug 2023 16:01:09 +0800 Subject: [PATCH 81/92] media: staging: media: starfive: camss: Register devices Register ISP sub-device and video devices for StarFive Camera Subsystem. Signed-off-by: Jack Zhu --- .../staging/media/starfive/camss/stf_camss.c | 97 ++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/drivers/staging/media/starfive/camss/stf_camss.c b/drivers/staging/media/starfive/camss/stf_camss.c index 918b8b2a77132..ab8d700d30165 100644 --- a/drivers/staging/media/starfive/camss/stf_camss.c +++ b/drivers/staging/media/starfive/camss/stf_camss.c @@ -120,6 +120,85 @@ static int stfcamss_of_parse_ports(struct stfcamss *stfcamss) return num_subdevs; } +static int stfcamss_register_devs(struct stfcamss *stfcamss) +{ + struct stf_capture *cap_yuv = &stfcamss->captures[STF_CAPTURE_YUV]; + struct stf_isp_dev *isp_dev = &stfcamss->isp_dev; + int ret; + + ret = stf_isp_register(isp_dev, &stfcamss->v4l2_dev); + if (ret < 0) { + dev_err(stfcamss->dev, + "failed to register stf isp%d entity: %d\n", 0, ret); + return ret; + } + + ret = stf_capture_register(stfcamss, &stfcamss->v4l2_dev); + if (ret < 0) { + dev_err(stfcamss->dev, + "failed to register capture: %d\n", ret); + goto err_isp_unregister; + } + + ret = media_create_pad_link(&isp_dev->subdev.entity, STF_ISP_PAD_SRC, + &cap_yuv->video.vdev.entity, 0, 0); + if (ret) + goto err_cap_unregister; + + cap_yuv->video.source_subdev = &isp_dev->subdev; + + return ret; + +err_cap_unregister: + stf_capture_unregister(stfcamss); +err_isp_unregister: + stf_isp_unregister(&stfcamss->isp_dev); + + return ret; +} + +static void stfcamss_unregister_devs(struct stfcamss *stfcamss) +{ + stf_isp_unregister(&stfcamss->isp_dev); + stf_capture_unregister(stfcamss); +} + +static int stfcamss_subdev_notifier_bound(struct v4l2_async_notifier *async, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct stfcamss *stfcamss = + container_of(async, struct stfcamss, notifier); + struct stfcamss_async_subdev *csd = + container_of(asd, struct stfcamss_async_subdev, asd); + enum stf_port_num port = csd->port; + struct stf_isp_dev *isp_dev = &stfcamss->isp_dev; + struct stf_capture *cap_raw = &stfcamss->captures[STF_CAPTURE_RAW]; + struct media_pad *pad; + int ret; + + if (port == STF_PORT_CSI2RX) { + pad = &isp_dev->pads[STF_ISP_PAD_SINK]; + } else { + dev_err(stfcamss->dev, "not support port %d\n", port); + return -EPERM; + } + + ret = v4l2_create_fwnode_links_to_pad(subdev, pad, 0); + if (ret) + return ret; + + ret = media_create_pad_link(&subdev->entity, 1, + &cap_raw->video.vdev.entity, 0, 0); + if (ret) + return ret; + + isp_dev->source_subdev = subdev; + cap_raw->video.source_subdev = subdev; + + return 0; +} + static int stfcamss_subdev_notifier_complete(struct v4l2_async_notifier *ntf) { struct stfcamss *stfcamss = @@ -130,6 +209,7 @@ static int stfcamss_subdev_notifier_complete(struct v4l2_async_notifier *ntf) static const struct v4l2_async_notifier_operations stfcamss_subdev_notifier_ops = { + .bound = stfcamss_subdev_notifier_bound, .complete = stfcamss_subdev_notifier_complete, }; @@ -214,6 +294,12 @@ static int stfcamss_probe(struct platform_device *pdev) goto err_cleanup_notifier; } + ret = stf_isp_init(stfcamss); + if (ret < 0) { + dev_err(dev, "Failed to init isp: %d\n", ret); + goto err_cleanup_notifier; + } + stfcamss_mc_init(pdev, stfcamss); ret = v4l2_device_register(stfcamss->dev, &stfcamss->v4l2_dev); @@ -228,6 +314,12 @@ static int stfcamss_probe(struct platform_device *pdev) goto err_unregister_device; } + ret = stfcamss_register_devs(stfcamss); + if (ret < 0) { + dev_err(dev, "Failed to register subdevice: %d\n", ret); + goto err_unregister_media_dev; + } + pm_runtime_enable(dev); stfcamss->notifier.ops = &stfcamss_subdev_notifier_ops; @@ -236,11 +328,13 @@ static int stfcamss_probe(struct platform_device *pdev) dev_err(dev, "Failed to register async subdev nodes: %d\n", ret); pm_runtime_disable(dev); - goto err_unregister_media_dev; + goto err_unregister_subdevs; } return 0; +err_unregister_subdevs: + stfcamss_unregister_devs(stfcamss); err_unregister_media_dev: media_device_unregister(&stfcamss->media_dev); err_unregister_device: @@ -262,6 +356,7 @@ static int stfcamss_remove(struct platform_device *pdev) { struct stfcamss *stfcamss = platform_get_drvdata(pdev); + stfcamss_unregister_devs(stfcamss); v4l2_device_unregister(&stfcamss->v4l2_dev); media_device_cleanup(&stfcamss->media_dev); pm_runtime_disable(&pdev->dev); From d883b40f1460f6b55d17d936e9893799deeb7209 Mon Sep 17 00:00:00 2001 From: Keith Zhao Date: Tue, 1 Aug 2023 18:10:25 +0800 Subject: [PATCH 82/92] dt-bindings: display: Add yamls for JH7110 display system 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 0000000000000..86018a8e68494 --- /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 0000000000000..bebe2050cff62 --- /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 0000000000000..f6927acf6655d --- /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 9f257a72e9a96e1aebfd5bebc6de7732ea3ba54d Mon Sep 17 00:00:00 2001 From: Keith Zhao Date: Tue, 1 Aug 2023 18:10:27 +0800 Subject: [PATCH 83/92] drm/fourcc: Add drm/vs tiled modifiers 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 8db7fd3f743e4..0b884cf503d8f 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 4af2e5f732e68114a6b729eff9b2ad5025d92c98 Mon Sep 17 00:00:00 2001 From: Keith Zhao Date: Tue, 1 Aug 2023 18:10:28 +0800 Subject: [PATCH 84/92] drm/vs: Register DRM device 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 afb3b2f5f4253..9ede80ef911f9 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 7a09a89b493be..6db3bc397b65e 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 0000000000000..fcc39dded0702 --- /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 0000000000000..26cc7e21b724e --- /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 0000000000000..69591e6404168 --- /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 0000000000000..6ddc99dcfc5d6 --- /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 0000000000000..a7d5a5c7b5d49 --- /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 0000000000000..d9ff6d23b035b --- /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 0000000000000..cd0bdf530964e --- /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 0000000000000..2e1f04a8a56ca --- /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 0000000000000..96b7fc95d6588 --- /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 4dd0e97598260ad104aa3274c92847359df5efbe Mon Sep 17 00:00:00 2001 From: Keith Zhao Date: Tue, 1 Aug 2023 18:10:29 +0800 Subject: [PATCH 85/92] drm/vs: Add KMS crtc&plane 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 0000000000000..6a5af8d8c06cb --- /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 0000000000000..92fcbcfc1b03b --- /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 0000000000000..44d9f0c180124 --- /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 0000000000000..4d6b1d045fc0e --- /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 0000000000000..d370dd4010849 --- /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 0000000000000..81e522ab76098 --- /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 69591e6404168..ddec92910670a 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 0000000000000..61648d7b1ee5c --- /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 0000000000000..d84b1abd9edf5 --- /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 0000000000000..80408cdb87cf7 --- /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 bafa58039fc9d6784243209e82662a91b9e39f26 Mon Sep 17 00:00:00 2001 From: Keith Zhao Date: Tue, 1 Aug 2023 18:10:30 +0800 Subject: [PATCH 86/92] drm/vs: Add hdmi 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 0000000000000..c02f4f749bb2d --- /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 0000000000000..91c5ca30a5217 --- /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__ */ From 003a1eee61e8466ad3cee9c1cd9304bdeba7370b Mon Sep 17 00:00:00 2001 From: Felix Moessbauer Date: Tue, 27 Jun 2023 16:06:20 +0800 Subject: [PATCH 87/92] riscv: dts: Enable device-tree overlay support for starfive devices Add the '-@' DTC option for the starfive devices. This option populates the '__symbols__' node that contains all the necessary symbols for supporting device-tree overlays (for instance from the firmware or the bootloader) on these devices. The starfive devices allow various modules to be connected and this enables users to create out-of-tree device-tree overlays for these modules. Please note that this change does increase the size of the resulting DTB by ~20%. For example, with v6.4 increase in size is as follows: jh7100-beaglev-starlight.dtb 6192 -> 7339 jh7100-starfive-visionfive-v1.dtb 6281 -> 7428 jh7110-starfive-visionfive-2-v1.2a.dtb 11101 -> 13447 jh7110-starfive-visionfive-2-v1.3b.dtb 11101 -> 13447 Signed-off-by: Felix Moessbauer Acked-by: Emil Renner Berthing Acked-by: Palmer Dabbelt Signed-off-by: Conor Dooley --- arch/riscv/boot/dts/starfive/Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/Makefile b/arch/riscv/boot/dts/starfive/Makefile index 170956846d494..0141504c0f5ca 100644 --- a/arch/riscv/boot/dts/starfive/Makefile +++ b/arch/riscv/boot/dts/starfive/Makefile @@ -1,4 +1,10 @@ # SPDX-License-Identifier: GPL-2.0 +# Enables support for device-tree overlays +DTC_FLAGS_jh7100-beaglev-starlight := -@ +DTC_FLAGS_jh7100-starfive-visionfive-v1 := -@ +DTC_FLAGS_jh7110-starfive-visionfive-2-v1.2a := -@ +DTC_FLAGS_jh7110-starfive-visionfive-2-v1.3b := -@ + dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-beaglev-starlight.dtb dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-starfive-visionfive-v1.dtb From 48aa08bac2db3705ee7037513a2d72c908315bc5 Mon Sep 17 00:00:00 2001 From: Hal Feng Date: Tue, 11 Apr 2023 16:31:15 +0800 Subject: [PATCH 88/92] riscv: dts: starfive: Add full support for JH7110 and VisionFive 2 board Merge all StarFive dts patches together. Signed-off-by: Hal Feng --- .../jh7110-starfive-visionfive-2-v1.2a.dts | 13 + .../jh7110-starfive-visionfive-2-v1.3b.dts | 31 + .../jh7110-starfive-visionfive-2.dtsi | 645 ++++++++++++++ arch/riscv/boot/dts/starfive/jh7110.dtsi | 805 +++++++++++++++++- 4 files changed, 1492 insertions(+), 2 deletions(-) diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.2a.dts b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.2a.dts index 4af3300f3cf31..205a13d8c8b14 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.2a.dts +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.2a.dts @@ -11,3 +11,16 @@ model = "StarFive VisionFive 2 v1.2A"; compatible = "starfive,visionfive-2-v1.2a", "starfive,jh7110"; }; + +&gmac1 { + phy-mode = "rmii"; + assigned-clocks = <&syscrg JH7110_SYSCLK_GMAC1_TX>, + <&syscrg JH7110_SYSCLK_GMAC1_RX>; + assigned-clock-parents = <&syscrg JH7110_SYSCLK_GMAC1_RMII_RTX>, + <&syscrg JH7110_SYSCLK_GMAC1_RMII_RTX>; +}; + +&phy0 { + rx-internal-delay-ps = <1900>; + tx-internal-delay-ps = <1350>; +}; diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.3b.dts b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.3b.dts index 9230cc3d89466..d4ea4a2c0b9bb 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.3b.dts +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.3b.dts @@ -11,3 +11,34 @@ model = "StarFive VisionFive 2 v1.3B"; compatible = "starfive,visionfive-2-v1.3b", "starfive,jh7110"; }; + +&gmac0 { + starfive,tx-use-rgmii-clk; + assigned-clocks = <&aoncrg JH7110_AONCLK_GMAC0_TX>; + assigned-clock-parents = <&aoncrg JH7110_AONCLK_GMAC0_RMII_RTX>; +}; + +&gmac1 { + starfive,tx-use-rgmii-clk; + assigned-clocks = <&syscrg JH7110_SYSCLK_GMAC1_TX>; + assigned-clock-parents = <&syscrg JH7110_SYSCLK_GMAC1_RMII_RTX>; +}; + +&phy0 { + motorcomm,tx-clk-adj-enabled; + motorcomm,tx-clk-100-inverted; + motorcomm,tx-clk-1000-inverted; + motorcomm,rx-clk-drv-microamp = <3970>; + motorcomm,rx-data-drv-microamp = <2910>; + rx-internal-delay-ps = <1500>; + tx-internal-delay-ps = <1500>; +}; + +&phy1 { + motorcomm,tx-clk-adj-enabled; + motorcomm,tx-clk-100-inverted; + motorcomm,rx-clk-drv-microamp = <3970>; + motorcomm,rx-data-drv-microamp = <2910>; + rx-internal-delay-ps = <300>; + tx-internal-delay-ps = <0>; +}; 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 fa0061eb33a75..390965ad5404e 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi @@ -11,10 +11,16 @@ / { aliases { + ethernet0 = &gmac0; + ethernet1 = &gmac1; i2c0 = &i2c0; i2c2 = &i2c2; i2c5 = &i2c5; i2c6 = &i2c6; + mmc0 = &mmc0; + mmc1 = &mmc1; + pcie0 = &pcie0; + pcie1 = &pcie1; serial0 = &uart0; }; @@ -31,11 +37,63 @@ 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>; priority = <224>; }; + + pwmdac_codec: pwmdac-codec { + compatible = "linux,spdif-dit"; + #sound-dai-cells = <0>; + }; + + sound-pwmdac { + compatible = "simple-audio-card"; + simple-audio-card,name = "StarFive-PWMDAC-Sound-Card"; + #address-cells = <1>; + #size-cells = <0>; + + simple-audio-card,dai-link@0 { + reg = <0>; + format = "left_j"; + bitclock-master = <&sndcpu0>; + frame-master = <&sndcpu0>; + + sndcpu0: cpu { + sound-dai = <&pwmdac>; + }; + + codec { + sound-dai = <&pwmdac_codec>; + }; + }; + }; + + clk_ext_camera: clk_ext_camera { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24000000>; + }; +}; + +&dvp_clk { + clock-frequency = <74250000>; }; &gmac0_rgmii_rxin { @@ -54,6 +112,10 @@ clock-frequency = <50000000>; }; +&hdmitx0_pixelclk { + clock-frequency = <297000000>; +}; + &i2srx_bclk_ext { clock-frequency = <12288000>; }; @@ -86,6 +148,104 @@ clock-frequency = <49152000>; }; +&csi2rx { + status = "okay"; + + assigned-clocks = <&ispcrg JH7110_ISPCLK_VIN_SYS>; + assigned-clock-rates = <297000000>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + csi2rx_from_imx219: endpoint { + remote-endpoint = <&imx219_to_csi2rx>; + bus-type = <4>; + clock-lanes = <0>; + data-lanes = <1 2>; + status = "okay"; + }; + }; + + port@1 { + reg = <1>; + + csi2rx_to_vin: endpoint { + remote-endpoint = <&vin_from_csi2rx>; + status = "okay"; + }; + }; + }; +}; + +&dc8200 { + status = "okay"; + + dc_out: port { + #address-cells = <1>; + #size-cells = <0>; + dc_out_hdmi: endpoint@0 { + reg = <0>; + remote-endpoint = <&hdmi_in_dc>; + }; + + }; +}; + +&display { + status = "okay"; +}; + +&gmac0 { + phy-handle = <&phy0>; + phy-mode = "rgmii-id"; + status = "okay"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + compatible = "snps,dwmac-mdio"; + + phy0: ethernet-phy@0 { + reg = <0>; + }; + }; +}; + +&gmac1 { + phy-handle = <&phy1>; + phy-mode = "rgmii-id"; + status = "okay"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + compatible = "snps,dwmac-mdio"; + + phy1: ethernet-phy@1 { + reg = <0>; + }; + }; +}; + +&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>; + }; + }; +}; + &i2c0 { clock-frequency = <100000>; i2c-sda-hold-time-ns = <300>; @@ -123,12 +283,28 @@ #interrupt-cells = <1>; regulators { + vcc_3v3: dcdc1 { + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc_3v3"; + }; + vdd_cpu: dcdc2 { regulator-always-on; regulator-min-microvolt = <500000>; regulator-max-microvolt = <1540000>; regulator-name = "vdd-cpu"; }; + + emmc_vdd: aldo4 { + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "emmc_vdd"; + }; }; }; }; @@ -141,9 +317,210 @@ pinctrl-names = "default"; pinctrl-0 = <&i2c6_pins>; status = "okay"; + + imx219: imx219@10 { + compatible = "sony,imx219"; + reg = <0x10>; + clocks = <&clk_ext_camera>; + reset-gpio = <&sysgpio 18 0>; + rotation = <0>; + orientation = <1>; + + port { + imx219_to_csi2rx: endpoint { + remote-endpoint = <&csi2rx_from_imx219>; + bus-type = <4>; + clock-lanes = <0>; + data-lanes = <1 2>; + link-frequencies = /bits/ 64 <456000000>; + }; + }; + }; +}; + +&i2srx { + pinctrl-names = "default"; + pinctrl-0 = <&i2srx_pins>; + status = "okay"; +}; + +&i2stx0 { + pinctrl-names = "default"; + pinctrl-0 = <&mclk_ext_pins>; + status = "okay"; +}; + +&i2stx1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2stx1_pins>; + status = "okay"; +}; + +&mmc0 { + max-frequency = <100000000>; + bus-width = <8>; + cap-mmc-highspeed; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + non-removable; + cap-mmc-hw-reset; + post-power-on-delay-ms = <200>; + pinctrl-names = "default"; + pinctrl-0 = <&mmc0_pins>; + vmmc-supply = <&vcc_3v3>; + vqmmc-supply = <&emmc_vdd>; + status = "okay"; +}; + +&mmc1 { + max-frequency = <100000000>; + bus-width = <4>; + no-sdio; + no-mmc; + broken-cd; + cap-sd-highspeed; + post-power-on-delay-ms = <200>; + pinctrl-names = "default"; + pinctrl-0 = <&mmc1_pins>; + status = "okay"; +}; + +&pcie0 { + pinctrl-names = "default"; + perst-gpios = <&sysgpio 26 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + perst-gpios = <&sysgpio 28 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pcie1_pins>; + status = "okay"; +}; + +&ptc { + pinctrl-names = "default"; + pinctrl-0 = <&pwm_pins>; + status = "okay"; +}; + +&pwmdac { + pinctrl-names = "default"; + pinctrl-0 = <&pwmdac_pins>; + status = "okay"; +}; + +&qspi { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + nor_flash: flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + cdns,read-delay = <5>; + spi-max-frequency = <12000000>; + cdns,tshsl-ns = <1>; + cdns,tsd2d-ns = <1>; + cdns,tchsh-ns = <1>; + cdns,tslch-ns = <1>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + spl@0 { + reg = <0x0 0x80000>; + }; + uboot-env@f0000 { + reg = <0xf0000 0x10000>; + }; + uboot@100000 { + reg = <0x100000 0x400000>; + }; + reserved-data@600000 { + reg = <0x600000 0xa00000>; + }; + }; + }; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_pins>; + status = "okay"; + + spi_dev0: spi@0 { + compatible = "rohm,dh2228fv"; + reg = <0>; + spi-max-frequency = <10000000>; + }; +}; + +&stfcamss { + status = "okay"; + + assigned-clocks = <&ispcrg JH7110_ISPCLK_DOM4_APB_FUNC>; + assigned-clock-rates = <49500000>; + + assigned-clocks = <&ispcrg JH7110_ISPCLK_MIPI_RX0_PXL>; + assigned-clock-rates = <198000000>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + vin_from_csi2rx: endpoint@1 { + reg = <1>; + remote-endpoint = <&csi2rx_to_vin>; + status = "okay"; + }; + }; + }; }; &sysgpio { + 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 */ + }; + }; + i2c0_pins: i2c0-0 { i2c-pins { pinmux = , + , + , + , + ; + input-enable; + }; + }; + + i2stx1_pins: i2stx1-0 { + sd-pins { + pinmux = ; + bias-disable; + input-disable; + }; + }; + + mclk_ext_pins: mclk-ext-0 { + mclk-ext-pins { + pinmux = ; + input-enable; + }; + }; + + mmc0_pins: mmc0-0 { + rst-pins { + pinmux = ; + bias-pull-up; + drive-strength = <12>; + input-disable; + input-schmitt-disable; + slew-rate = <0>; + }; + + mmc-pins { + pinmux = , + , + , + , + , + , + , + , + , + ; + bias-pull-up; + drive-strength = <12>; + input-enable; + }; + }; + + mmc1_pins: mmc1-0 { + clk-pins { + pinmux = ; + bias-pull-up; + drive-strength = <12>; + input-disable; + input-schmitt-disable; + slew-rate = <0>; + }; + + mmc-pins { + pinmux = , + , + , + , + ; + bias-pull-up; + drive-strength = <12>; + input-enable; + input-schmitt-enable; + slew-rate = <0>; + }; + }; + + pcie0_pins: pcie0-0 { + wake-pins { + pinmux = ; + bias-pull-up; + drive-strength = <2>; + input-enable; + input-schmitt-disable; + slew-rate = <0>; + }; + + clkreq-pins { + pinmux = ; + bias-pull-down; + drive-strength = <2>; + input-enable; + input-schmitt-disable; + slew-rate = <0>; + }; + }; + + pcie1_pins: pcie1-0 { + wake-pins { + pinmux = ; + bias-pull-up; + drive-strength = <2>; + input-enable; + input-schmitt-disable; + slew-rate = <0>; + }; + + clkreq-pins { + pinmux = ; + bias-pull-down; + drive-strength = <2>; + input-enable; + input-schmitt-disable; + slew-rate = <0>; + }; + }; + + pwm_pins: pwm-0 { + pwm-pins { + pinmux = , + ; + bias-disable; + drive-strength = <12>; + input-disable; + input-schmitt-disable; + slew-rate = <0>; + }; + }; + + pwmdac_pins: pwmdac-0 { + pwmdac-pins { + pinmux = , + ; + bias-disable; + drive-strength = <2>; + input-disable; + input-schmitt-disable; + slew-rate = <0>; + }; + }; + + spi0_pins: spi0-0 { + mosi-pins { + pinmux = ; + bias-disable; + input-disable; + input-schmitt-disable; + }; + + miso-pins { + pinmux = ; + bias-pull-up; + input-enable; + input-schmitt-enable; + }; + + sck-pins { + pinmux = ; + bias-disable; + input-disable; + input-schmitt-disable; + }; + + ss-pins { + pinmux = ; + bias-disable; + input-disable; + input-schmitt-disable; + }; + }; + + tdm_pins: tdm-0 { + tx-pins { + pinmux = ; + bias-pull-up; + drive-strength = <2>; + input-disable; + input-schmitt-disable; + slew-rate = <0>; + }; + + rx-pins { + pinmux = ; + input-enable; + }; + + sync-pins { + pinmux = ; + input-enable; + }; + + pcmclk-pins { + pinmux = ; + input-enable; + }; + }; + uart0_pins: uart0-0 { tx-pins { pinmux = ; + status = "okay"; +}; + &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_pins>; status = "okay"; }; +&usb0 { + dr_mode = "peripheral"; + status = "okay"; +}; + &U74_1 { cpu-supply = <&vdd_cpu>; }; diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index ec2e70011a736..504fb96d3f1cc 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -6,7 +6,9 @@ /dts-v1/; #include +#include #include +#include / { compatible = "starfive,jh7110"; @@ -56,6 +58,7 @@ operating-points-v2 = <&cpu_opp>; clocks = <&syscrg JH7110_SYSCLK_CPU_CORE>; clock-names = "cpu"; + #cooling-cells = <2>; cpu1_intc: interrupt-controller { compatible = "riscv,cpu-intc"; @@ -85,6 +88,7 @@ operating-points-v2 = <&cpu_opp>; clocks = <&syscrg JH7110_SYSCLK_CPU_CORE>; clock-names = "cpu"; + #cooling-cells = <2>; cpu2_intc: interrupt-controller { compatible = "riscv,cpu-intc"; @@ -114,6 +118,7 @@ operating-points-v2 = <&cpu_opp>; clocks = <&syscrg JH7110_SYSCLK_CPU_CORE>; clock-names = "cpu"; + #cooling-cells = <2>; cpu3_intc: interrupt-controller { compatible = "riscv,cpu-intc"; @@ -143,6 +148,7 @@ operating-points-v2 = <&cpu_opp>; clocks = <&syscrg JH7110_SYSCLK_CPU_CORE>; clock-names = "cpu"; + #cooling-cells = <2>; cpu4_intc: interrupt-controller { compatible = "riscv,cpu-intc"; @@ -197,6 +203,60 @@ }; }; + display: display-subsystem { + compatible = "starfive,display-subsystem"; + ports = <&dc_out>; + }; + + stmmac_axi_setup: stmmac-axi-config { + snps,lpi_en; + snps,wr_osr_lmt = <15>; + snps,rd_osr_lmt = <15>; + snps,blen = <256 128 64 32 0 0 0>; + }; + + thermal-zones { + cpu-thermal { + polling-delay-passive = <250>; + polling-delay = <15000>; + + thermal-sensors = <&sfctemp>; + + cooling-maps { + map0 { + trip = <&cpu_alert0>; + cooling-device = + <&U74_1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&U74_2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&U74_3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&U74_4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + + trips { + cpu_alert0: cpu_alert0 { + /* milliCelsius */ + temperature = <85000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu_crit { + /* milliCelsius */ + temperature = <100000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + }; + }; + + dvp_clk: dvp-clock { + compatible = "fixed-clock"; + clock-output-names = "dvp_clk"; + #clock-cells = <0>; + }; + gmac0_rgmii_rxin: gmac0-rgmii-rxin-clock { compatible = "fixed-clock"; clock-output-names = "gmac0_rgmii_rxin"; @@ -221,6 +281,12 @@ #clock-cells = <0>; }; + hdmitx0_pixelclk: hdmitx0-pixel-clock { + compatible = "fixed-clock"; + clock-output-names = "hdmitx0_pixelclk"; + #clock-cells = <0>; + }; + i2srx_bclk_ext: i2srx-bclk-ext-clock { compatible = "fixed-clock"; clock-output-names = "i2srx_bclk_ext"; @@ -386,6 +452,186 @@ status = "disabled"; }; + spi0: spi@10060000 { + compatible = "arm,pl022", "arm,primecell"; + reg = <0x0 0x10060000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_SPI0_APB>, + <&syscrg JH7110_SYSCLK_SPI0_APB>; + clock-names = "sspclk", "apb_pclk"; + resets = <&syscrg JH7110_SYSRST_SPI0_APB>; + interrupts = <38>; + arm,primecell-periphid = <0x00041022>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + spi1: spi@10070000 { + compatible = "arm,pl022", "arm,primecell"; + reg = <0x0 0x10070000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_SPI1_APB>, + <&syscrg JH7110_SYSCLK_SPI1_APB>; + clock-names = "sspclk", "apb_pclk"; + resets = <&syscrg JH7110_SYSRST_SPI1_APB>; + interrupts = <39>; + arm,primecell-periphid = <0x00041022>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + spi2: spi@10080000 { + compatible = "arm,pl022", "arm,primecell"; + reg = <0x0 0x10080000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_SPI2_APB>, + <&syscrg JH7110_SYSCLK_SPI2_APB>; + clock-names = "sspclk", "apb_pclk"; + resets = <&syscrg JH7110_SYSRST_SPI2_APB>; + interrupts = <40>; + arm,primecell-periphid = <0x00041022>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + tdm: tdm@10090000 { + compatible = "starfive,jh7110-tdm"; + reg = <0x0 0x10090000 0x0 0x1000>; + clocks = <&syscrg JH7110_SYSCLK_TDM_AHB>, + <&syscrg JH7110_SYSCLK_TDM_APB>, + <&syscrg JH7110_SYSCLK_TDM_INTERNAL>, + <&syscrg JH7110_SYSCLK_TDM_TDM>, + <&syscrg JH7110_SYSCLK_MCLK_INNER>, + <&tdm_ext>; + clock-names = "tdm_ahb", "tdm_apb", + "tdm_internal", "tdm", + "mclk_inner", "tdm_ext"; + resets = <&syscrg JH7110_SYSRST_TDM_AHB>, + <&syscrg JH7110_SYSRST_TDM_APB>, + <&syscrg JH7110_SYSRST_TDM_CORE>; + dmas = <&dma 20>, <&dma 21>; + dma-names = "rx","tx"; + #sound-dai-cells = <0>; + status = "disabled"; + }; + + pwmdac: pwmdac@100b0000 { + compatible = "starfive,jh7110-pwmdac"; + reg = <0x0 0x100b0000 0x0 0x1000>; + clocks = <&syscrg JH7110_SYSCLK_PWMDAC_APB>, + <&syscrg JH7110_SYSCLK_PWMDAC_CORE>; + clock-names = "apb", "core"; + resets = <&syscrg JH7110_SYSRST_PWMDAC_APB>; + dmas = <&dma 22>; + dma-names = "tx"; + #sound-dai-cells = <0>; + status = "disabled"; + }; + + i2srx: i2s@100e0000 { + compatible = "starfive,jh7110-i2srx"; + reg = <0x0 0x100e0000 0x0 0x1000>; + clocks = <&syscrg JH7110_SYSCLK_I2SRX_BCLK_MST>, + <&syscrg JH7110_SYSCLK_I2SRX_APB>, + <&syscrg JH7110_SYSCLK_MCLK>, + <&syscrg JH7110_SYSCLK_MCLK_INNER>, + <&mclk_ext>, + <&syscrg JH7110_SYSCLK_I2SRX_BCLK>, + <&syscrg JH7110_SYSCLK_I2SRX_LRCK>, + <&i2srx_bclk_ext>, + <&i2srx_lrck_ext>; + clock-names = "i2sclk", "apb", "mclk", + "mclk_inner", "mclk_ext", "bclk", + "lrck", "bclk_ext", "lrck_ext"; + resets = <&syscrg JH7110_SYSRST_I2SRX_APB>, + <&syscrg JH7110_SYSRST_I2SRX_BCLK>; + dmas = <0>, <&dma 24>; + dma-names = "tx", "rx"; + starfive,syscon = <&sys_syscon 0x18 0x2>; + #sound-dai-cells = <0>; + status = "disabled"; + }; + + usb0: usb@10100000 { + compatible = "starfive,jh7110-usb"; + ranges = <0x0 0x0 0x10100000 0x100000>; + #address-cells = <1>; + #size-cells = <1>; + starfive,stg-syscon = <&stg_syscon 0x4>; + clocks = <&stgcrg JH7110_STGCLK_USB0_LPM>, + <&stgcrg JH7110_STGCLK_USB0_STB>, + <&stgcrg JH7110_STGCLK_USB0_APB>, + <&stgcrg JH7110_STGCLK_USB0_AXI>, + <&stgcrg JH7110_STGCLK_USB0_UTMI_APB>; + clock-names = "lpm", "stb", "apb", "axi", "utmi_apb"; + resets = <&stgcrg JH7110_STGRST_USB0_PWRUP>, + <&stgcrg JH7110_STGRST_USB0_APB>, + <&stgcrg JH7110_STGRST_USB0_AXI>, + <&stgcrg JH7110_STGRST_USB0_UTMI_APB>; + reset-names = "pwrup", "apb", "axi", "utmi_apb"; + status = "disabled"; + + usb_cdns3: usb@0 { + compatible = "cdns,usb3"; + reg = <0x0 0x10000>, + <0x10000 0x10000>, + <0x20000 0x10000>; + reg-names = "otg", "xhci", "dev"; + interrupts = <100>, <108>, <110>; + interrupt-names = "host", "peripheral", "otg"; + phys = <&usbphy0>; + phy-names = "cdns3,usb2-phy"; + }; + }; + + usbphy0: phy@10200000 { + compatible = "starfive,jh7110-usb-phy"; + reg = <0x0 0x10200000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_USB_125M>, + <&stgcrg JH7110_STGCLK_USB0_APP_125>; + clock-names = "125m", "app_125m"; + #phy-cells = <0>; + }; + + pciephy0: phy@10210000 { + compatible = "starfive,jh7110-pcie-phy"; + reg = <0x0 0x10210000 0x0 0x10000>; + #phy-cells = <0>; + }; + + pciephy1: phy@10220000 { + compatible = "starfive,jh7110-pcie-phy"; + reg = <0x0 0x10220000 0x0 0x10000>; + #phy-cells = <0>; + }; + + stgcrg: clock-controller@10230000 { + compatible = "starfive,jh7110-stgcrg"; + reg = <0x0 0x10230000 0x0 0x10000>; + clocks = <&osc>, + <&syscrg JH7110_SYSCLK_HIFI4_CORE>, + <&syscrg JH7110_SYSCLK_STG_AXIAHB>, + <&syscrg JH7110_SYSCLK_USB_125M>, + <&syscrg JH7110_SYSCLK_CPU_BUS>, + <&syscrg JH7110_SYSCLK_HIFI4_AXI>, + <&syscrg JH7110_SYSCLK_NOCSTG_BUS>, + <&syscrg JH7110_SYSCLK_APB_BUS>; + clock-names = "osc", "hifi4_core", + "stg_axiahb", "usb_125m", + "cpu_bus", "hifi4_axi", + "nocstg_bus", "apb_bus"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + stg_syscon: syscon@10240000 { + compatible = "starfive,jh7110-stg-syscon", "syscon"; + reg = <0x0 0x10240000 0x0 0x1000>; + }; + uart3: serial@12000000 { compatible = "snps,dw-apb-uart"; reg = <0x0 0x12000000 0x0 0x10000>; @@ -473,6 +719,147 @@ status = "disabled"; }; + spi3: spi@12070000 { + compatible = "arm,pl022", "arm,primecell"; + reg = <0x0 0x12070000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_SPI3_APB>, + <&syscrg JH7110_SYSCLK_SPI3_APB>; + clock-names = "sspclk", "apb_pclk"; + resets = <&syscrg JH7110_SYSRST_SPI3_APB>; + interrupts = <52>; + arm,primecell-periphid = <0x00041022>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + spi4: spi@12080000 { + compatible = "arm,pl022", "arm,primecell"; + reg = <0x0 0x12080000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_SPI4_APB>, + <&syscrg JH7110_SYSCLK_SPI4_APB>; + clock-names = "sspclk", "apb_pclk"; + resets = <&syscrg JH7110_SYSRST_SPI4_APB>; + interrupts = <53>; + arm,primecell-periphid = <0x00041022>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + spi5: spi@12090000 { + compatible = "arm,pl022", "arm,primecell"; + reg = <0x0 0x12090000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_SPI5_APB>, + <&syscrg JH7110_SYSCLK_SPI5_APB>; + clock-names = "sspclk", "apb_pclk"; + resets = <&syscrg JH7110_SYSRST_SPI5_APB>; + interrupts = <54>; + arm,primecell-periphid = <0x00041022>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + spi6: spi@120a0000 { + compatible = "arm,pl022", "arm,primecell"; + reg = <0x0 0x120A0000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_SPI6_APB>, + <&syscrg JH7110_SYSCLK_SPI6_APB>; + clock-names = "sspclk", "apb_pclk"; + resets = <&syscrg JH7110_SYSRST_SPI6_APB>; + interrupts = <55>; + arm,primecell-periphid = <0x00041022>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2stx0: i2s@120b0000 { + compatible = "starfive,jh7110-i2stx0"; + reg = <0x0 0x120b0000 0x0 0x1000>; + clocks = <&syscrg JH7110_SYSCLK_I2STX0_BCLK_MST>, + <&syscrg JH7110_SYSCLK_I2STX0_APB>, + <&syscrg JH7110_SYSCLK_MCLK>, + <&syscrg JH7110_SYSCLK_MCLK_INNER>, + <&mclk_ext>; + clock-names = "i2sclk", "apb", "mclk", + "mclk_inner","mclk_ext"; + resets = <&syscrg JH7110_SYSRST_I2STX0_APB>, + <&syscrg JH7110_SYSRST_I2STX0_BCLK>; + dmas = <&dma 47>; + dma-names = "tx"; + #sound-dai-cells = <0>; + status = "disabled"; + }; + + i2stx1: i2s@120c0000 { + compatible = "starfive,jh7110-i2stx1"; + reg = <0x0 0x120c0000 0x0 0x1000>; + clocks = <&syscrg JH7110_SYSCLK_I2STX1_BCLK_MST>, + <&syscrg JH7110_SYSCLK_I2STX1_APB>, + <&syscrg JH7110_SYSCLK_MCLK>, + <&syscrg JH7110_SYSCLK_MCLK_INNER>, + <&mclk_ext>, + <&syscrg JH7110_SYSCLK_I2STX1_BCLK>, + <&syscrg JH7110_SYSCLK_I2STX1_LRCK>, + <&i2stx_bclk_ext>, + <&i2stx_lrck_ext>; + clock-names = "i2sclk", "apb", "mclk", + "mclk_inner", "mclk_ext", "bclk", + "lrck", "bclk_ext", "lrck_ext"; + resets = <&syscrg JH7110_SYSRST_I2STX1_APB>, + <&syscrg JH7110_SYSRST_I2STX1_BCLK>; + dmas = <&dma 48>; + dma-names = "tx"; + #sound-dai-cells = <0>; + status = "disabled"; + }; + + ptc: pwm@120d0000 { + compatible = "starfive,jh7110-pwm"; + reg = <0x0 0x120d0000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_PWM_APB>; + resets = <&syscrg JH7110_SYSRST_PWM_APB>; + #pwm-cells = <3>; + status = "disabled"; + }; + + sfctemp: temperature-sensor@120e0000 { + compatible = "starfive,jh7110-temp"; + reg = <0x0 0x120e0000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_TEMP_CORE>, + <&syscrg JH7110_SYSCLK_TEMP_APB>; + clock-names = "sense", "bus"; + resets = <&syscrg JH7110_SYSRST_TEMP_CORE>, + <&syscrg JH7110_SYSRST_TEMP_APB>; + reset-names = "sense", "bus"; + #thermal-sensor-cells = <0>; + }; + + qspi: spi@13010000 { + compatible = "starfive,jh7110-qspi", "cdns,qspi-nor"; + reg = <0x0 0x13010000 0x0 0x10000>, + <0x0 0x21000000 0x0 0x400000>; + interrupts = <25>; + clocks = <&syscrg JH7110_SYSCLK_QSPI_REF>, + <&syscrg JH7110_SYSCLK_QSPI_AHB>, + <&syscrg JH7110_SYSCLK_QSPI_APB>; + clock-names = "ref", "ahb", "apb"; + resets = <&syscrg JH7110_SYSRST_QSPI_APB>, + <&syscrg JH7110_SYSRST_QSPI_AHB>, + <&syscrg JH7110_SYSRST_QSPI_REF>; + reset-names = "qspi", "qspi-ocp", "rstc_ref"; + cdns,fifo-depth = <256>; + cdns,fifo-width = <4>; + cdns,trigger-address = <0x0>; + status = "disabled"; + }; + syscrg: clock-controller@13020000 { compatible = "starfive,jh7110-syscrg"; reg = <0x0 0x13020000 0x0 0x10000>; @@ -480,16 +867,31 @@ <&gmac1_rgmii_rxin>, <&i2stx_bclk_ext>, <&i2stx_lrck_ext>, <&i2srx_bclk_ext>, <&i2srx_lrck_ext>, - <&tdm_ext>, <&mclk_ext>; + <&tdm_ext>, <&mclk_ext>, + <&pllclk JH7110_PLLCLK_PLL0_OUT>, + <&pllclk JH7110_PLLCLK_PLL1_OUT>, + <&pllclk JH7110_PLLCLK_PLL2_OUT>; clock-names = "osc", "gmac1_rmii_refin", "gmac1_rgmii_rxin", "i2stx_bclk_ext", "i2stx_lrck_ext", "i2srx_bclk_ext", "i2srx_lrck_ext", - "tdm_ext", "mclk_ext"; + "tdm_ext", "mclk_ext", + "pll0_out", "pll1_out", "pll2_out"; #clock-cells = <1>; #reset-cells = <1>; }; + sys_syscon: syscon@13030000 { + compatible = "starfive,jh7110-sys-syscon", "syscon", "simple-mfd"; + reg = <0x0 0x13030000 0x0 0x1000>; + + pllclk: clock-controller { + compatible = "starfive,jh7110-pll"; + clocks = <&osc>; + #clock-cells = <1>; + }; + }; + sysgpio: pinctrl@13040000 { compatible = "starfive,jh7110-sys-pinctrl"; reg = <0x0 0x13040000 0x0 0x10000>; @@ -502,6 +904,26 @@ #gpio-cells = <2>; }; + timer@13050000 { + compatible = "starfive,jh7110-timer"; + reg = <0x0 0x13050000 0x0 0x10000>; + interrupts = <69>, <70>, <71> ,<72>; + clocks = <&syscrg JH7110_SYSCLK_TIMER_APB>, + <&syscrg JH7110_SYSCLK_TIMER0>, + <&syscrg JH7110_SYSCLK_TIMER1>, + <&syscrg JH7110_SYSCLK_TIMER2>, + <&syscrg JH7110_SYSCLK_TIMER3>; + clock-names = "apb", "ch0", "ch1", + "ch2", "ch3"; + resets = <&syscrg JH7110_SYSRST_TIMER_APB>, + <&syscrg JH7110_SYSRST_TIMER0>, + <&syscrg JH7110_SYSRST_TIMER1>, + <&syscrg JH7110_SYSRST_TIMER2>, + <&syscrg JH7110_SYSRST_TIMER3>; + reset-names = "apb", "ch0", "ch1", + "ch2", "ch3"; + }; + watchdog@13070000 { compatible = "starfive,jh7110-wdt"; reg = <0x0 0x13070000 0x0 0x10000>; @@ -512,6 +934,155 @@ <&syscrg JH7110_SYSRST_WDT_CORE>; }; + crypto: crypto@16000000 { + compatible = "starfive,jh7110-crypto"; + reg = <0x0 0x16000000 0x0 0x4000>; + clocks = <&stgcrg JH7110_STGCLK_SEC_AHB>, + <&stgcrg JH7110_STGCLK_SEC_MISC_AHB>; + clock-names = "hclk", "ahb"; + interrupts = <28>; + resets = <&stgcrg JH7110_STGRST_SEC_AHB>; + dmas = <&sdma 1 2>, <&sdma 0 2>; + dma-names = "tx", "rx"; + }; + + sdma: dma-controller@16008000 { + compatible = "arm,pl080", "arm,primecell"; + arm,primecell-periphid = <0x00041080>; + reg = <0x0 0x16008000 0x0 0x4000>; + interrupts = <29>; + clocks = <&stgcrg JH7110_STGCLK_SEC_AHB>; + clock-names = "apb_pclk"; + resets = <&stgcrg JH7110_STGRST_SEC_AHB>; + lli-bus-interface-ahb1; + mem-bus-interface-ahb1; + memcpy-burst-size = <256>; + memcpy-bus-width = <32>; + #dma-cells = <2>; + }; + + rng: rng@1600c000 { + compatible = "starfive,jh7110-trng"; + reg = <0x0 0x1600C000 0x0 0x4000>; + clocks = <&stgcrg JH7110_STGCLK_SEC_AHB>, + <&stgcrg JH7110_STGCLK_SEC_MISC_AHB>; + clock-names = "hclk", "ahb"; + resets = <&stgcrg JH7110_STGRST_SEC_AHB>; + interrupts = <30>; + }; + + mmc0: mmc@16010000 { + compatible = "starfive,jh7110-mmc"; + reg = <0x0 0x16010000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_SDIO0_AHB>, + <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; + clock-names = "biu","ciu"; + resets = <&syscrg JH7110_SYSRST_SDIO0_AHB>; + reset-names = "reset"; + interrupts = <74>; + fifo-depth = <32>; + fifo-watermark-aligned; + data-addr = <0>; + starfive,sysreg = <&sys_syscon 0x14 0x1a 0x7c000000>; + status = "disabled"; + }; + + mmc1: mmc@16020000 { + compatible = "starfive,jh7110-mmc"; + reg = <0x0 0x16020000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_SDIO1_AHB>, + <&syscrg JH7110_SYSCLK_SDIO1_SDCARD>; + clock-names = "biu","ciu"; + resets = <&syscrg JH7110_SYSRST_SDIO1_AHB>; + reset-names = "reset"; + interrupts = <75>; + fifo-depth = <32>; + fifo-watermark-aligned; + data-addr = <0>; + starfive,sysreg = <&sys_syscon 0x9c 0x1 0x3e>; + status = "disabled"; + }; + + gmac0: ethernet@16030000 { + compatible = "starfive,jh7110-dwmac", "snps,dwmac-5.20"; + reg = <0x0 0x16030000 0x0 0x10000>; + clocks = <&aoncrg JH7110_AONCLK_GMAC0_AXI>, + <&aoncrg JH7110_AONCLK_GMAC0_AHB>, + <&syscrg JH7110_SYSCLK_GMAC0_PTP>, + <&aoncrg JH7110_AONCLK_GMAC0_TX_INV>, + <&syscrg JH7110_SYSCLK_GMAC0_GTXC>; + clock-names = "stmmaceth", "pclk", "ptp_ref", + "tx", "gtx"; + resets = <&aoncrg JH7110_AONRST_GMAC0_AXI>, + <&aoncrg JH7110_AONRST_GMAC0_AHB>; + reset-names = "stmmaceth", "ahb"; + interrupts = <7>, <6>, <5>; + interrupt-names = "macirq", "eth_wake_irq", "eth_lpi"; + rx-fifo-depth = <2048>; + tx-fifo-depth = <2048>; + snps,multicast-filter-bins = <64>; + snps,perfect-filter-entries = <256>; + snps,fixed-burst; + snps,no-pbl-x8; + snps,force_thresh_dma_mode; + snps,axi-config = <&stmmac_axi_setup>; + snps,tso; + snps,en-tx-lpi-clockgating; + snps,txpbl = <16>; + snps,rxpbl = <16>; + starfive,syscon = <&aon_syscon 0xc 0x12>; + status = "disabled"; + }; + + gmac1: ethernet@16040000 { + compatible = "starfive,jh7110-dwmac", "snps,dwmac-5.20"; + reg = <0x0 0x16040000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_GMAC1_AXI>, + <&syscrg JH7110_SYSCLK_GMAC1_AHB>, + <&syscrg JH7110_SYSCLK_GMAC1_PTP>, + <&syscrg JH7110_SYSCLK_GMAC1_TX_INV>, + <&syscrg JH7110_SYSCLK_GMAC1_GTXC>; + clock-names = "stmmaceth", "pclk", "ptp_ref", + "tx", "gtx"; + resets = <&syscrg JH7110_SYSRST_GMAC1_AXI>, + <&syscrg JH7110_SYSRST_GMAC1_AHB>; + reset-names = "stmmaceth", "ahb"; + interrupts = <78>, <77>, <76>; + interrupt-names = "macirq", "eth_wake_irq", "eth_lpi"; + rx-fifo-depth = <2048>; + tx-fifo-depth = <2048>; + snps,multicast-filter-bins = <64>; + snps,perfect-filter-entries = <256>; + snps,fixed-burst; + snps,no-pbl-x8; + snps,force_thresh_dma_mode; + snps,axi-config = <&stmmac_axi_setup>; + snps,tso; + snps,en-tx-lpi-clockgating; + snps,txpbl = <16>; + snps,rxpbl = <16>; + starfive,syscon = <&sys_syscon 0x90 0x2>; + status = "disabled"; + }; + + dma: dma-controller@16050000 { + compatible = "starfive,jh7110-axi-dma"; + reg = <0x0 0x16050000 0x0 0x10000>; + clocks = <&stgcrg JH7110_STGCLK_DMA1P_AXI>, + <&stgcrg JH7110_STGCLK_DMA1P_AHB>; + clock-names = "core-clk", "cfgr-clk"; + resets = <&stgcrg JH7110_STGRST_DMA1P_AXI>, + <&stgcrg JH7110_STGRST_DMA1P_AHB>; + interrupts = <73>; + #dma-cells = <1>; + dma-channels = <4>; + snps,dma-masters = <1>; + snps,data-width = <3>; + snps,block-size = <65536 65536 65536 65536>; + snps,priority = <0 1 2 3>; + snps,axi-max-burst-len = <16>; + }; + aoncrg: clock-controller@17000000 { compatible = "starfive,jh7110-aoncrg"; reg = <0x0 0x17000000 0x0 0x10000>; @@ -529,6 +1100,12 @@ #reset-cells = <1>; }; + aon_syscon: syscon@17010000 { + compatible = "starfive,jh7110-aon-syscon", "syscon"; + reg = <0x0 0x17010000 0x0 0x1000>; + #power-domain-cells = <1>; + }; + aongpio: pinctrl@17020000 { compatible = "starfive,jh7110-aon-pinctrl"; reg = <0x0 0x17020000 0x0 0x10000>; @@ -546,5 +1123,229 @@ interrupts = <111>; #power-domain-cells = <1>; }; + + csi2rx: csi-bridge@19800000 { + compatible = "starfive,jh7110-csi2rx"; + reg = <0x0 0x19800000 0x0 0x10000>; + clocks = <&ispcrg JH7110_ISPCLK_VIN_SYS>, + <&ispcrg JH7110_ISPCLK_VIN_APB>, + <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF0>, + <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF1>, + <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF2>, + <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF3>; + clock-names = "sys_clk", "p_clk", + "pixel_if0_clk", "pixel_if1_clk", + "pixel_if2_clk", "pixel_if3_clk"; + resets = <&ispcrg JH7110_ISPRST_VIN_SYS>, + <&ispcrg JH7110_ISPRST_VIN_APB>, + <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF0>, + <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF1>, + <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF2>, + <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF3>; + reset-names = "sys", "reg_bank", + "pixel_if0", "pixel_if1", + "pixel_if2", "pixel_if3"; + phys = <&csi_phy>; + phy-names = "dphy"; + status = "disabled"; + }; + + ispcrg: clock-controller@19810000 { + compatible = "starfive,jh7110-ispcrg"; + reg = <0x0 0x19810000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_ISP_TOP_CORE>, + <&syscrg JH7110_SYSCLK_ISP_TOP_AXI>, + <&syscrg JH7110_SYSCLK_NOC_BUS_ISP_AXI>, + <&dvp_clk>; + clock-names = "isp_top_core", "isp_top_axi", + "noc_bus_isp_axi", "dvp_clk"; + resets = <&syscrg JH7110_SYSRST_ISP_TOP>, + <&syscrg JH7110_SYSRST_ISP_TOP_AXI>, + <&syscrg JH7110_SYSRST_NOC_BUS_ISP_AXI>; + #clock-cells = <1>; + #reset-cells = <1>; + power-domains = <&pwrc JH7110_PD_ISP>; + }; + + csi_phy: phy@19820000 { + compatible = "starfive,jh7110-dphy-rx"; + reg = <0x0 0x19820000 0x0 0x10000>; + clocks = <&ispcrg JH7110_ISPCLK_M31DPHY_CFG_IN>, + <&ispcrg JH7110_ISPCLK_M31DPHY_REF_IN>, + <&ispcrg JH7110_ISPCLK_M31DPHY_TX_ESC_LAN0>; + clock-names = "cfg", "ref", "tx"; + resets = <&ispcrg JH7110_ISPRST_M31DPHY_HW>, + <&ispcrg JH7110_ISPRST_M31DPHY_B09_AON>; + power-domains = <&aon_syscon JH7110_PD_DPHY_RX>; + #phy-cells = <0>; + }; + + stfcamss: camss@19840000 { + compatible = "starfive,jh7110-camss"; + reg = <0x0 0x19840000 0x0 0x10000>, + <0x0 0x19870000 0x0 0x30000>; + reg-names = "syscon", "isp"; + clocks = <&ispcrg JH7110_ISPCLK_ISPV2_TOP_WRAPPER_C>, + <&syscrg JH7110_SYSCLK_ISP_TOP_CORE>, + <&syscrg JH7110_SYSCLK_ISP_TOP_AXI>; + clock-names = "clk_wrapper_clk_c", + "clk_ispcore_2x", + "clk_isp_axi"; + resets = <&ispcrg JH7110_ISPRST_ISPV2_TOP_WRAPPER_P>, + <&ispcrg JH7110_ISPRST_ISPV2_TOP_WRAPPER_C>, + <&ispcrg JH7110_ISPRST_VIN_P_AXI_WR>, + <&syscrg JH7110_SYSRST_ISP_TOP>, + <&syscrg JH7110_SYSRST_ISP_TOP_AXI>; + reset-names = "rst_wrapper_p", + "rst_wrapper_c", + "rst_axiwr", + "rst_isp_top_n", + "rst_isp_top_axi"; + power-domains = <&pwrc JH7110_PD_ISP>; + /* irq nr: vin, isp, isp_csi, isp_csiline */ + interrupts = <92>, <87>, <90>; + status = "disabled"; + }; + + 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>; + }; + + voutcrg: clock-controller@295c0000 { + compatible = "starfive,jh7110-voutcrg"; + reg = <0x0 0x295c0000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_VOUT_SRC>, + <&syscrg JH7110_SYSCLK_VOUT_TOP_AHB>, + <&syscrg JH7110_SYSCLK_VOUT_TOP_AXI>, + <&syscrg JH7110_SYSCLK_VOUT_TOP_HDMITX0_MCLK>, + <&syscrg JH7110_SYSCLK_I2STX0_BCLK>, + <&hdmitx0_pixelclk>; + clock-names = "vout_src", "vout_top_ahb", + "vout_top_axi", "vout_top_hdmitx0_mclk", + "i2stx0_bclk", "hdmitx0_pixelclk"; + resets = <&syscrg JH7110_SYSRST_VOUT_TOP_SRC>; + #clock-cells = <1>; + #reset-cells = <1>; + power-domains = <&pwrc JH7110_PD_VOUT>; + }; + + pcie0: pcie@940000000 { + compatible = "starfive,jh7110-pcie"; + reg = <0x9 0x40000000 0x0 0x1000000>, + <0x0 0x2b000000 0x0 0x100000>; + reg-names = "cfg", "apb"; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges = <0x82000000 0x0 0x30000000 0x0 0x30000000 0x0 0x08000000>, + <0xc3000000 0x9 0x00000000 0x9 0x00000000 0x0 0x40000000>; + interrupts = <56>; + interrupt-parent = <&plic>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc0 0x1>, + <0x0 0x0 0x0 0x2 &pcie_intc0 0x2>, + <0x0 0x0 0x0 0x3 &pcie_intc0 0x3>, + <0x0 0x0 0x0 0x4 &pcie_intc0 0x4>; + msi-controller; + device_type = "pci"; + starfive,stg-syscon = <&stg_syscon>; + bus-range = <0x0 0xff>; + clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_STG_AXI>, + <&stgcrg JH7110_STGCLK_PCIE0_TL>, + <&stgcrg JH7110_STGCLK_PCIE0_AXI_MST0>, + <&stgcrg JH7110_STGCLK_PCIE0_APB>; + clock-names = "noc", "tl", "axi_mst0", "apb"; + resets = <&stgcrg JH7110_STGRST_PCIE0_AXI_MST0>, + <&stgcrg JH7110_STGRST_PCIE0_AXI_SLV0>, + <&stgcrg JH7110_STGRST_PCIE0_AXI_SLV>, + <&stgcrg JH7110_STGRST_PCIE0_BRG>, + <&stgcrg JH7110_STGRST_PCIE0_CORE>, + <&stgcrg JH7110_STGRST_PCIE0_APB>; + reset-names = "mst0", "slv0", "slv", "brg", + "core", "apb"; + status = "disabled"; + + pcie_intc0: interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + }; + }; + + pcie1: pcie@9c0000000 { + compatible = "starfive,jh7110-pcie"; + reg = <0x9 0xc0000000 0x0 0x1000000>, + <0x0 0x2c000000 0x0 0x100000>; + reg-names = "cfg", "apb"; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges = <0x82000000 0x0 0x38000000 0x0 0x38000000 0x0 0x08000000>, + <0xc3000000 0x9 0x80000000 0x9 0x80000000 0x0 0x40000000>; + interrupts = <57>; + interrupt-parent = <&plic>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc1 0x1>, + <0x0 0x0 0x0 0x2 &pcie_intc1 0x2>, + <0x0 0x0 0x0 0x3 &pcie_intc1 0x3>, + <0x0 0x0 0x0 0x4 &pcie_intc1 0x4>; + msi-controller; + device_type = "pci"; + starfive,stg-syscon = <&stg_syscon>; + bus-range = <0x0 0xff>; + clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_STG_AXI>, + <&stgcrg JH7110_STGCLK_PCIE1_TL>, + <&stgcrg JH7110_STGCLK_PCIE1_AXI_MST0>, + <&stgcrg JH7110_STGCLK_PCIE1_APB>; + clock-names = "noc", "tl", "axi_mst0", "apb"; + resets = <&stgcrg JH7110_STGRST_PCIE1_AXI_MST0>, + <&stgcrg JH7110_STGRST_PCIE1_AXI_SLV0>, + <&stgcrg JH7110_STGRST_PCIE1_AXI_SLV>, + <&stgcrg JH7110_STGRST_PCIE1_BRG>, + <&stgcrg JH7110_STGRST_PCIE1_CORE>, + <&stgcrg JH7110_STGRST_PCIE1_APB>; + reset-names = "mst0", "slv0", "slv", "brg", + "core", "apb"; + status = "disabled"; + + pcie_intc1: interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + }; + }; }; }; From 596ab2686a790290badea5bd0c1173021ea8d31c Mon Sep 17 00:00:00 2001 From: Hal Feng Date: Tue, 11 Apr 2023 16:25:57 +0800 Subject: [PATCH 89/92] MAINTAINERS: Update all StarFive entries Merge all StarFive maintainers changes together. Signed-off-by: Hal Feng --- MAINTAINERS | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 4cc6bf79fdd83..d1f7bcf506174 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4450,6 +4450,7 @@ M: Maxime Ripard L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/cdns,*.txt +F: Documentation/devicetree/bindings/media/cdns,csi2rx.yaml F: drivers/media/platform/cadence/cdns-csi2* CADENCE NAND DRIVER @@ -20260,6 +20261,15 @@ M: Ion Badulescu S: Odd Fixes F: drivers/net/ethernet/adaptec/starfire* +STARFIVE CAMERA SUBSYSTEM DRIVER +M: Jack Zhu +M: Changhuang Liang +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/admin-guide/media/starfive_camss.rst +F: Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml +F: drivers/media/platform/starfive/ + STARFIVE CRYPTO DRIVER M: Jia Jie Ho M: William Qiu @@ -20279,12 +20289,39 @@ S: Maintained F: Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml F: drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c +STARFIVE JH7110 DPHY RX DRIVER +M: Jack Zhu +M: Changhuang Liang +S: Supported +F: Documentation/devicetree/bindings/phy/starfive,jh7110-dphy-rx.yaml +F: drivers/phy/starfive/phy-starfive-dphy-rx.c + STARFIVE JH7110 MMC/SD/SDIO DRIVER M: William Qiu S: Supported F: Documentation/devicetree/bindings/mmc/starfive* F: drivers/mmc/host/dw_mmc-starfive.c +STARFIVE JH7110 PLL CLOCK DRIVER +M: Xingyu Wu +S: Supported +F: Documentation/devicetree/bindings/clock/starfive,jh7110-pll.yaml +F: drivers/clk/starfive/clk-starfive-jh7110-pll.* + +STARFIVE JH7110 PWMDAC DRIVER +M: Hal Feng +M: Xingyu Wu +S: Supported +F: Documentation/devicetree/bindings/sound/starfive,jh7110-pwmdac* +F: sound/soc/codecs/jh7110_pwmdac_transmitter.c +F: sound/soc/starfive/jh7110_pwmdac.c + +STARFIVE JH7110 SYSCON +M: William Qiu +M: Xingyu Wu +S: Supported +F: Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml + STARFIVE JH7110 TDM DRIVER M: Walker Chen S: Maintained @@ -20294,11 +20331,27 @@ F: sound/soc/starfive/jh7110_tdm.c STARFIVE JH71X0 CLOCK DRIVERS M: Emil Renner Berthing M: Hal Feng +M: Xingyu Wu S: Maintained F: Documentation/devicetree/bindings/clock/starfive,jh71*.yaml F: drivers/clk/starfive/clk-starfive-jh71* F: include/dt-bindings/clock/starfive?jh71*.h +STARFIVE JH71X0 PCIE AND USB PHY DRIVER +M: Emil Renner Berthing +M: Minda Chen +S: Supported +F: Documentation/devicetree/bindings/phy/starfive,jh7110-pcie-phy.yaml +F: Documentation/devicetree/bindings/phy/starfive,jh7110-usb-phy.yaml +F: drivers/phy/starfive/phy-jh7110-pcie.c +F: drivers/phy/starfive/phy-jh7110-usb.c + +STARFIVE JH71X0 PCIE DRIVERS +M: Minda Chen +S: Maintained +F: Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml +F: drivers/pci/controller/pcie-starfive.c + STARFIVE JH71X0 PINCTRL DRIVERS M: Emil Renner Berthing M: Jianlong Huang @@ -20309,6 +20362,13 @@ F: drivers/pinctrl/starfive/pinctrl-starfive-jh71* F: include/dt-bindings/pinctrl/pinctrl-starfive-jh7100.h F: include/dt-bindings/pinctrl/starfive,jh7110-pinctrl.h +STARFIVE JH71X0 PWM DRIVERS +M: William Qiu +M: Hal Feng +S: Supported +F: Documentation/devicetree/bindings/pwm/starfive,jh7110-pwm.yaml +F: drivers/pwm/pwm-starfive-ptc.c + STARFIVE JH71X0 RESET CONTROLLER DRIVERS M: Emil Renner Berthing M: Hal Feng @@ -20325,6 +20385,7 @@ F: drivers/usb/cdns3/cdns3-starfive.c STARFIVE JH71XX PMU CONTROLLER DRIVER M: Walker Chen +M: Changhuang Liang S: Supported F: Documentation/devicetree/bindings/power/starfive* F: drivers/soc/starfive/jh71xx_pmu.c @@ -20334,8 +20395,16 @@ STARFIVE SOC DRIVERS M: Conor Dooley S: Maintained T: git https://git.kernel.org/pub/scm/linux/kernel/git/conor/linux.git/ +F: Documentation/devicetree/bindings/soc/starfive/ F: drivers/soc/starfive/ +STARFIVE TIMER DRIVER +M: Samin Guo +M: Xingyu Wu +S: Supported +F: Documentation/devicetree/bindings/timer/starfive* +F: drivers/clocksource/timer-starfive* + STARFIVE TRNG DRIVER M: Jia Jie Ho S: Supported From 20ab944ad9abab7b022d312e4d6f8755e44ae74e Mon Sep 17 00:00:00 2001 From: Hal Feng Date: Wed, 26 Apr 2023 22:59:08 +0800 Subject: [PATCH 90/92] [NOT-FOR-UPSTREAM] Add starfive_visionfive2_defconfig for test Signed-off-by: Hal Feng --- .../configs/starfive_visionfive2_defconfig | 229 ++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 arch/riscv/configs/starfive_visionfive2_defconfig diff --git a/arch/riscv/configs/starfive_visionfive2_defconfig b/arch/riscv/configs/starfive_visionfive2_defconfig new file mode 100644 index 0000000000000..06447654fa1b5 --- /dev/null +++ b/arch/riscv/configs/starfive_visionfive2_defconfig @@ -0,0 +1,229 @@ +CONFIG_WERROR=y +CONFIG_SYSVIPC=y +# CONFIG_CROSS_MEMORY_ATTACH is not set +CONFIG_NO_HZ_IDLE=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PSI=y +# CONFIG_CPU_ISOLATION is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_SCHED=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_NAMESPACES=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set +CONFIG_EXPERT=y +# CONFIG_SYSFS_SYSCALL is not set +CONFIG_PERF_EVENTS=y +CONFIG_SOC_STARFIVE=y +CONFIG_ERRATA_SIFIVE=y +CONFIG_NONPORTABLE=y +CONFIG_SMP=y +# CONFIG_RISCV_ISA_SVPBMT is not set +# CONFIG_COMPAT is not set +CONFIG_HIBERNATION=y +CONFIG_CPU_IDLE=y +CONFIG_RISCV_SBI_CPUIDLE=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_CPUFREQ_DT=y +CONFIG_JUMP_LABEL=y +# CONFIG_STACKPROTECTOR is not set +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_COMPRESS_ZSTD=y +# CONFIG_BLOCK_LEGACY_AUTOLOAD is not set +CONFIG_BLK_WBT=y +# CONFIG_BLK_DEBUG_FS is not set +CONFIG_PARTITION_ADVANCED=y +# CONFIG_MQ_IOSCHED_DEADLINE is not set +# CONFIG_MQ_IOSCHED_KYBER is not set +CONFIG_KSM=y +# CONFIG_VM_EVENT_COUNTERS is not set +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_PNP=y +CONFIG_INET_DIAG=m +# CONFIG_IPV6_SIT is not set +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_WIRELESS is not set +# CONFIG_ETHTOOL_NETLINK is not set +CONFIG_PCI=y +CONFIG_PCIEPORTBUS=y +CONFIG_PCI_HOST_GENERIC=y +CONFIG_PCIE_STARFIVE_HOST=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +# CONFIG_STANDALONE is not set +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +CONFIG_FW_LOADER=m +CONFIG_EFI_DISABLE_RUNTIME=y +CONFIG_MTD=y +CONFIG_MTD_SPI_NOR=y +CONFIG_ZRAM=y +CONFIG_ZRAM_MEMORY_TRACKING=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=1 +CONFIG_BLK_DEV_NVME=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_DEV_SR=y +CONFIG_NETDEVICES=y +CONFIG_STMMAC_ETH=y +CONFIG_DWMAC_DWC_QOS_ETH=y +# CONFIG_DWMAC_GENERIC is not set +CONFIG_DWMAC_STARFIVE=y +CONFIG_MICROCHIP_PHY=y +CONFIG_MOTORCOMM_PHY=y +# CONFIG_WLAN is not set +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_SERIO is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_LDISC_AUTOLOAD is not set +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set +# CONFIG_SERIAL_8250_16550A_VARIANTS is not set +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_DW=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_JH7110=y +# CONFIG_I2C_COMPAT is not set +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_DESIGNWARE_PLATFORM=y +CONFIG_SPI=y +CONFIG_SPI_CADENCE_QUADSPI=y +CONFIG_SPI_PL022=y +# CONFIG_PTP_1588_CLOCK is not set +CONFIG_GPIOLIB_FASTPATH_LIMIT=128 +CONFIG_GPIO_SYSFS=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_GPIO_RESTART=y +CONFIG_SENSORS_SFCTEMP=y +CONFIG_THERMAL=y +CONFIG_CPU_THERMAL=y +CONFIG_THERMAL_EMULATION=y +CONFIG_WATCHDOG=y +CONFIG_MFD_AXP20X_I2C=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_AXP20X=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_CADENCE_CSI2RX=y +CONFIG_VIDEO_IMX219=y +CONFIG_DRM=y +CONFIG_DRM_VERISILICON=y +CONFIG_STARFIVE_HDMI=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_DESIGNWARE_I2S=y +CONFIG_SND_SOC_STARFIVE=y +CONFIG_SND_SOC_JH7110_PWMDAC=y +CONFIG_SND_SOC_JH7110_TDM=y +CONFIG_SND_SOC_WM8960=y +CONFIG_SND_SIMPLE_CARD=y +CONFIG_USB=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_UAS=y +CONFIG_USB_CDNS_SUPPORT=y +CONFIG_USB_CDNS3=y +CONFIG_USB_CDNS3_GADGET=y +CONFIG_USB_CDNS3_HOST=y +CONFIG_USB_CDNS3_STARFIVE=y +CONFIG_USB_GADGET=y +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_MMC=y +# CONFIG_PWRSEQ_EMMC is not set +# CONFIG_PWRSEQ_SIMPLE is not set +CONFIG_MMC_DW=y +CONFIG_MMC_DW_STARFIVE=y +CONFIG_DMADEVICES=y +CONFIG_AMBA_PL08X=y +CONFIG_DW_AXI_DMAC=y +CONFIG_DMATEST=y +# CONFIG_VIRTIO_MENU is not set +# CONFIG_VHOST_MENU is not set +CONFIG_STAGING=y +CONFIG_STAGING_MEDIA=y +CONFIG_VIDEO_STARFIVE_CAMSS=y +CONFIG_CLK_STARFIVE_JH7110_AON=y +CONFIG_CLK_STARFIVE_JH7110_STG=y +CONFIG_CLK_STARFIVE_JH7110_ISP=y +CONFIG_CLK_STARFIVE_JH7110_VOUT=y +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_SIFIVE_CCACHE=y +CONFIG_PWM=y +CONFIG_PWM_STARFIVE_PTC=y +CONFIG_PHY_STARFIVE_JH7110_DPHY_RX=y +CONFIG_PHY_STARFIVE_JH7110_PCIE=y +CONFIG_PHY_STARFIVE_JH7110_USB=y +CONFIG_EXT4_FS=y +CONFIG_BTRFS_FS=y +CONFIG_BTRFS_FS_POSIX_ACL=y +# CONFIG_DNOTIFY is not set +CONFIG_FANOTIFY=y +CONFIG_AUTOFS_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_UTF8=y +CONFIG_EXFAT_FS=y +CONFIG_NTFS_FS=y +CONFIG_NTFS_RW=y +CONFIG_PROC_KCORE=y +CONFIG_PROC_CHILDREN=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_EFIVAR_FS=y +CONFIG_JFFS2_FS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V4=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_DEFAULT="iso8859-15" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_15=y +CONFIG_LSM="" +CONFIG_CRYPTO_ZSTD=y +# CONFIG_RAID6_PQ_BENCHMARK is not set +# CONFIG_DEBUG_MISC is not set +CONFIG_STRIP_ASM_SYMS=y +CONFIG_DEBUG_SECTION_MISMATCH=y +# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set +CONFIG_DEBUG_FS=y +# CONFIG_SLUB_DEBUG is not set +CONFIG_DEBUG_RODATA_TEST=y +CONFIG_DEBUG_WX=y +CONFIG_SOFTLOCKUP_DETECTOR=y +CONFIG_WQ_WATCHDOG=y +# CONFIG_SCHED_DEBUG is not set +CONFIG_STACKTRACE=y +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +# CONFIG_RCU_TRACE is not set +# CONFIG_FTRACE is not set +# CONFIG_RUNTIME_TESTING_MENU is not set From 73a14d34ba66921dde50c31769915c3fdeebe04f Mon Sep 17 00:00:00 2001 From: Hal Feng <87058983+hal-feng@users.noreply.github.com> Date: Tue, 20 Dec 2022 16:18:35 +0800 Subject: [PATCH 91/92] [NOT-FOR-UPSTREAM] Add readme Signed-off-by: Hal Feng --- README.md | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000000000..0b6919bbe9ee9 --- /dev/null +++ b/README.md @@ -0,0 +1,95 @@ +## JH7110 Upstream Status ## + +To get the latest status of each upstreaming patch series, please visit +our RVspace. + +https://rvspace.org/en/project/JH7110_Upstream_Plan + +## Test Status ## + +| Component | Test Result | Note | +| :---------| :-----------| :----| +| Clock tree / Reset | Pass | | +| Pinctrl | Pass | | +| Watchdog | Pass | | +| Timer | Pass | | +| PLL clock | Pass | | +| Temperature sensor | Pass | | +| DMA | Pass | | +| PWM | Pass | | +| GMAC | Pass | | +| SDIO / EMMC | Pass | | +| I2C | Pass | | +| SPI | Pass | | +| QSPI | Pass | | +| USB | Pass | | +| PCIe | Pass | Please use a power supply with strong driving capability | +| PMU | Pass | | +| Hibernation | Pass | | +| PMIC | Pass | | +| CPU freq scaling | Pass | | +| Crypto | Fail | Report Segmentation faults when booting | +| TRNG | Pass | | +| PWMDAC | Pass | | +| I2S | Pass | | +| TDM | Pass | | +| HDMI / DC8200 | Fail | You need to boot Linux with the U-Boot in the mainline | +| MIPI CSI | Pass | | +| ISP | Fail | Some issues need to be fixed | +| MIPI CSI PHY | Pass | | + +## Build Instructions ## + +1. Configure Kconfig options + +```shell +# Use the provided starfive_visionfive2_defconfig +make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- starfive_visionfive2_defconfig +``` + +or + +```shell +# Select options manually +make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- nconfig +``` + +To boot up the VisionFive 2 board, please make sure **SOC_STARFIVE**, +**CLK_STARFIVE_JH7110_SYS**, **PINCTRL_STARFIVE_JH7110_SYS**, +**SERIAL_8250_DW** are selected. +> If you need MMC and GMAC drivers, you should also select +**MMC_DW_STARFIVE** and **DWMAC_STARFIVE**. + +2. Build +```shell +make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- +``` + +## How to Run on VisionFive 2 Board via Network ## + +1. Power on, enter u-boot and set enviroment parameters +``` +setenv fdt_high 0xffffffffffffffff; setenv initrd_high 0xffffffffffffffff; +setenv scriptaddr 0x88100000; setenv script_offset_f 0x1fff000; setenv script_size_f 0x1000; +setenv kernel_addr_r 0x84000000; setenv kernel_comp_addr_r 0x90000000; setenv kernel_comp_size 0x10000000; +setenv fdt_addr_r 0x88000000; setenv ramdisk_addr_r 0x88300000; +``` +2. Set IP addresses for the board and your tftp server +``` +setenv serverip 192.168.w.x; setenv gatewayip 192.168.w.y; setenv ipaddr 192.168.w.z; setenv hostname starfive; setenv netdev eth0; +``` +3. Upload dtb, image and file system to DDR from your tftp server +``` +tftpboot ${fdt_addr_r} jh7110-starfive-visionfive-2-v1.3b.dtb; tftpboot ${kernel_addr_r} Image.gz; tftpboot ${ramdisk_addr_r} initramfs.cpio.gz; +``` +> If your VisionFive 2 is v1.2A, you should upload jh7110-starfive-visionfive-2-v1.2a.dtb instead. +4. Load and boot the kernel +``` +booti ${kernel_addr_r} ${ramdisk_addr_r}:${filesize} ${fdt_addr_r}; +``` +When you see the message "buildroot login:", the launch was successful. +You can just input the following accout and password, then continue. +``` +buildroot login: root +Password: starfive +``` From bdb039e123f619f86f3981fb6178902d3126fd55 Mon Sep 17 00:00:00 2001 From: John Clark Date: Sat, 11 Nov 2023 06:22:56 +0000 Subject: [PATCH 92/92] Assign fixed PCI domain numbers to the host bridge. Without fixed domain numbers, Linux fails to initialize PCI: pcie-starfive 940000000.pcie: error -ENODEV: failed to get valid pcie domain pcie-starfive 9c0000000.pcie: error -ENODEV: failed to get valid pcie domain Signed-off-by: John Clark --- arch/riscv/boot/dts/starfive/jh7110.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index 504fb96d3f1cc..6a50af4c0dc80 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -1267,6 +1267,7 @@ reg = <0x9 0x40000000 0x0 0x1000000>, <0x0 0x2b000000 0x0 0x100000>; reg-names = "cfg", "apb"; + linux,pci-domain = <0>; #address-cells = <3>; #size-cells = <2>; #interrupt-cells = <1>; @@ -1310,6 +1311,7 @@ reg = <0x9 0xc0000000 0x0 0x1000000>, <0x0 0x2c000000 0x0 0x100000>; reg-names = "cfg", "apb"; + linux,pci-domain = <1>; #address-cells = <3>; #size-cells = <2>; #interrupt-cells = <1>;