diff options
| author | Mark Brown <broonie@kernel.org> | 2026-06-01 17:30:29 +0300 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-06-01 17:30:29 +0300 |
| commit | debea6d6bce8abdb6f0dac6755936f48ababc7bb (patch) | |
| tree | 978fa09f4f7445f58f7a48d7a465c5c4003d33f2 | |
| parent | a67c554dbc0fdd7e3c5909cb9f0fff41c51b2e9d (diff) | |
| parent | ef19ecf042b448a69ee3bd9b3e35689b0b7892ac (diff) | |
| download | linux-debea6d6bce8abdb6f0dac6755936f48ababc7bb.tar.xz | |
ASoC: rsnd: Add RZ/G3E audio driver support
John Madieu <john.madieu.xa@bp.renesas.com> says:
Add audio support for the Renesas RZ/G3E SoC to the R-Car Sound
driver. The RZ/G3E audio subsystem is based on R-Car Sound IP but
has several differences requiring dedicated handling:
- SSI operates exclusively in BUSIF mode (no PIO)
- 2-4 BUSIF channels per SSI (layout differs from R-Car)
- Separate register regions for SCU, ADG, SSIU, SSI accessed by name
- Per-SSI ADG and SSIF supply clocks
- Dedicated audmapp clock/reset for Audio DMAC peri-peri
- Per-SSI and per-module reset controllers via CPG
- Unprefixed DT sub-node names (ssi, ssiu, src, ...) instead of
rcar_sound,xxx
- Hyphenated indexed clock/reset names (ssi-0, src-0, adg-ssi-0,
audio-clka, ...) instead of the legacy dotted form
Link: https://patch.msgid.link/20260525110230.4014435-1-john.madieu.xa@bp.renesas.com
| -rw-r--r-- | Documentation/devicetree/bindings/sound/renesas,r9a09g047-sound.yaml | 800 | ||||
| -rw-r--r-- | sound/soc/renesas/rcar/adg.c | 168 | ||||
| -rw-r--r-- | sound/soc/renesas/rcar/cmd.c | 2 | ||||
| -rw-r--r-- | sound/soc/renesas/rcar/core.c | 161 | ||||
| -rw-r--r-- | sound/soc/renesas/rcar/ctu.c | 29 | ||||
| -rw-r--r-- | sound/soc/renesas/rcar/dma.c | 280 | ||||
| -rw-r--r-- | sound/soc/renesas/rcar/dvc.c | 29 | ||||
| -rw-r--r-- | sound/soc/renesas/rcar/gen.c | 180 | ||||
| -rw-r--r-- | sound/soc/renesas/rcar/mix.c | 29 | ||||
| -rw-r--r-- | sound/soc/renesas/rcar/rsnd.h | 73 | ||||
| -rw-r--r-- | sound/soc/renesas/rcar/src.c | 91 | ||||
| -rw-r--r-- | sound/soc/renesas/rcar/ssi.c | 49 | ||||
| -rw-r--r-- | sound/soc/renesas/rcar/ssiu.c | 92 |
13 files changed, 1857 insertions, 126 deletions
diff --git a/Documentation/devicetree/bindings/sound/renesas,r9a09g047-sound.yaml b/Documentation/devicetree/bindings/sound/renesas,r9a09g047-sound.yaml new file mode 100644 index 000000000000..d7fa16554698 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/renesas,r9a09g047-sound.yaml @@ -0,0 +1,800 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/renesas,r9a09g047-sound.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas RZ/G3E Sound Controller + +maintainers: + - Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + - John Madieu <john.madieu.xa@bp.renesas.com> + +description: + The RZ/G3E (R9A09G047) sound controller is based on R-Car Sound IP + with extended DMA channel support (up to 5 DMACs per direction), + additional clock domains (47 clocks including per-SSI ADG clocks), + and additional reset lines (14 including SCU, ADG and Audio DMAC + peri-peri resets). SSI operates exclusively in BUSIF mode with + 2-4 BUSIF channels per SSI. + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: renesas,r9a09g047-sound + + reg: + maxItems: 5 + + reg-names: + items: + - const: scu + - const: adg + - const: ssiu + - const: ssi + - const: audmapp + + "#sound-dai-cells": + const: 1 + + "#clock-cells": + const: 0 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + clocks: + maxItems: 47 + + clock-names: + items: + - const: ssi-all + - const: ssi-0 + - const: ssi-1 + - const: ssi-2 + - const: ssi-3 + - const: ssi-4 + - const: ssi-5 + - const: ssi-6 + - const: ssi-7 + - const: ssi-8 + - const: ssi-9 + - const: src-0 + - const: src-1 + - const: src-2 + - const: src-3 + - const: src-4 + - const: src-5 + - const: src-6 + - const: src-7 + - const: src-8 + - const: src-9 + - const: mix-0 + - const: mix-1 + - const: ctu-0 + - const: ctu-1 + - const: dvc-0 + - const: dvc-1 + - const: audio-clka + - const: audio-clkb + - const: audio-clkc + - const: audio-clki + - const: ssif_supply + - const: scu + - const: scu_x2 + - const: scu_supply + - const: adg-ssi-0 + - const: adg-ssi-1 + - const: adg-ssi-2 + - const: adg-ssi-3 + - const: adg-ssi-4 + - const: adg-ssi-5 + - const: adg-ssi-6 + - const: adg-ssi-7 + - const: adg-ssi-8 + - const: adg-ssi-9 + - const: audmapp + - const: adg + + power-domains: + maxItems: 1 + + resets: + maxItems: 14 + + reset-names: + items: + - const: ssi-all + - const: ssi-0 + - const: ssi-1 + - const: ssi-2 + - const: ssi-3 + - const: ssi-4 + - const: ssi-5 + - const: ssi-6 + - const: ssi-7 + - const: ssi-8 + - const: ssi-9 + - const: scu + - const: adg + - const: audmapp + + dvc: + type: object + additionalProperties: false + + patternProperties: + "^dvc-[0-1]$": + type: object + additionalProperties: false + + properties: + dmas: + maxItems: 5 + description: + List of references to DMA specifiers, one per DMA + controller, all for the transmission direction + (DVC is playback-only). The dma-engine core falls + through the list to find a free channel. + + dma-names: + maxItems: 5 + allOf: + - items: + enum: + - tx + + required: + - dmas + - dma-names + + mix: + type: object + additionalProperties: false + description: + Per-channel Mixer (MIX) sub-nodes. Each mix-N node has no + properties of its own. It exists so the driver can enumerate + the MIX instances and so that DT labels can be attached to it + for the dai/playback/capture phandle routing arrays. + + patternProperties: + "^mix-[0-1]$": + type: object + additionalProperties: false + + ctu: + type: object + additionalProperties: false + description: + Per-channel Channel Transfer Unit (CTU) sub-nodes. Each ctu-N + node has no properties of its own. It exists so the driver + can enumerate the CTU instances and so that DT labels can be + attached to it for the dai/playback/capture phandle routing arrays. + + patternProperties: + "^ctu-[0-7]$": + type: object + additionalProperties: false + + src: + type: object + additionalProperties: false + + patternProperties: + "^src-[0-9]$": + type: object + additionalProperties: false + + properties: + interrupts: + maxItems: 1 + + dmas: + maxItems: 10 + description: + Must contain a list of pairs of references to DMA + specifiers, one for transmission and one for reception, + repeated for each DMA controller. The dma-engine core + falls through the list to find a free channel. + + dma-names: + maxItems: 10 + allOf: + - items: + enum: + - tx + - rx + + ssiu: + type: object + additionalProperties: false + + patternProperties: + "^ssiu-[0-9]+$": + type: object + additionalProperties: false + + properties: + dmas: + maxItems: 10 + description: + Must contain a list of pairs of references to DMA + specifiers, one for transmission and one for reception, + repeated for each DMA controller. The dma-engine core + falls through the list to find a free channel. + + dma-names: + maxItems: 10 + allOf: + - items: + enum: + - tx + - rx + + required: + - dmas + - dma-names + + ssi: + type: object + additionalProperties: false + + patternProperties: + "^ssi-[0-9]$": + type: object + additionalProperties: false + + properties: + interrupts: + maxItems: 1 + + shared-pin: + description: Shared clock pin. + $ref: /schemas/types.yaml#/definitions/flag + + required: + - interrupts + + ports: + $ref: audio-graph-port.yaml#/definitions/port-base + unevaluatedProperties: false + patternProperties: + '^port@[0-9a-f]+$': + $ref: audio-graph-port.yaml#/definitions/port-base + unevaluatedProperties: false + properties: + reg: + maxItems: 1 + endpoint: + $ref: audio-graph-port.yaml#/definitions/endpoint-base + unevaluatedProperties: false + properties: + playback: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: + Ordered list of phandles to the in-SoC modules used + by this DAI in the playback direction. Each phandle + must reference one of the ssi-N, src-N, ctu-N, + mix-N or dvc-N sub-nodes of the parent sound + controller. The list order is the pipeline order + from CPU to off-SoC endpoint. + capture: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: + Ordered list of phandles to the in-SoC modules used + by this DAI in the capture direction. Each phandle + must reference one of the ssi-N, src-N, ctu-N, + mix-N or dvc-N sub-nodes of the parent sound + controller. The list order is the pipeline order + from off-SoC endpoint to CPU. + +required: + - compatible + - reg + - reg-names + - "#sound-dai-cells" + - "#clock-cells" + - clocks + - clock-names + - resets + - reset-names + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + + sound@13c00000 { + #sound-dai-cells = <1>; + #clock-cells = <0>; + compatible = "renesas,r9a09g047-sound"; + reg = <0x13c00000 0x10000>, + <0x13c20000 0x10000>, + <0x13c30000 0x1000>, + <0x13c31000 0x1f000>, + <0x13c50000 0x10000>; + reg-names = "scu", "adg", "ssiu", "ssi", "audmapp"; + clocks = <&cpg 245>, + <&cpg 385>, <&cpg 386>, + <&cpg 387>, <&cpg 388>, + <&cpg 389>, <&cpg 390>, + <&cpg 391>, <&cpg 392>, + <&cpg 393>, <&cpg 394>, + <&cpg 372>, <&cpg 373>, + <&cpg 374>, <&cpg 375>, + <&cpg 376>, <&cpg 377>, + <&cpg 378>, <&cpg 379>, + <&cpg 380>, <&cpg 381>, + <&cpg 370>, <&cpg 371>, + <&cpg 370>, <&cpg 371>, + <&cpg 368>, <&cpg 369>, + <&cpg 251>, <&cpg 252>, + <&cpg 253>, <&cpg 250>, + <&cpg 384>, + <&cpg 246>, <&cpg 247>, + <&cpg 382>, + <&cpg 352>, <&cpg 353>, + <&cpg 354>, <&cpg 355>, + <&cpg 356>, <&cpg 357>, + <&cpg 358>, <&cpg 359>, + <&cpg 360>, <&cpg 361>, + <&cpg 248>, <&cpg 249>; + clock-names = "ssi-all", + "ssi-0", "ssi-1", + "ssi-2", "ssi-3", + "ssi-4", "ssi-5", + "ssi-6", "ssi-7", + "ssi-8", "ssi-9", + "src-0", "src-1", + "src-2", "src-3", + "src-4", "src-5", + "src-6", "src-7", + "src-8", "src-9", + "mix-0", "mix-1", + "ctu-0", "ctu-1", + "dvc-0", "dvc-1", + "audio-clka", "audio-clkb", + "audio-clkc", "audio-clki", + "ssif_supply", + "scu", "scu_x2", + "scu_supply", + "adg-ssi-0", "adg-ssi-1", + "adg-ssi-2", "adg-ssi-3", + "adg-ssi-4", "adg-ssi-5", + "adg-ssi-6", "adg-ssi-7", + "adg-ssi-8", "adg-ssi-9", + "audmapp", "adg"; + power-domains = <&cpg>; + resets = <&cpg 225>, + <&cpg 226>, <&cpg 227>, + <&cpg 228>, <&cpg 229>, + <&cpg 230>, <&cpg 231>, + <&cpg 232>, <&cpg 233>, + <&cpg 234>, <&cpg 235>, + <&cpg 236>, <&cpg 238>, <&cpg 237>; + reset-names = "ssi-all", + "ssi-0", "ssi-1", + "ssi-2", "ssi-3", + "ssi-4", "ssi-5", + "ssi-6", "ssi-7", + "ssi-8", "ssi-9", + "scu", "adg", + "audmapp"; + + ctu { + ctu-0 { }; + ctu-1 { }; + ctu-2 { }; + ctu-3 { }; + ctu-4 { }; + ctu-5 { }; + ctu-6 { }; + ctu-7 { }; + }; + + dvc { + dvc0: dvc-0 { + dmas = <&dmac0 0x1db3>, <&dmac1 0x1db3>, + <&dmac2 0x1db3>, <&dmac3 0x1db3>, + <&dmac4 0x1db3>; + dma-names = "tx", "tx", "tx", "tx", "tx"; + }; + dvc1: dvc-1 { + dmas = <&dmac0 0x1db4>, <&dmac1 0x1db4>, + <&dmac2 0x1db4>, <&dmac3 0x1db4>, + <&dmac4 0x1db4>; + dma-names = "tx", "tx", "tx", "tx", "tx"; + }; + }; + + mix { + mix-0 { }; + mix-1 { }; + }; + + src { + src0: src-0 { + interrupts = <GIC_SPI 902 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&dmac0 0x1d9f>, <&dmac0 0x1da9>, + <&dmac1 0x1d9f>, <&dmac1 0x1da9>, + <&dmac2 0x1d9f>, <&dmac2 0x1da9>, + <&dmac3 0x1d9f>, <&dmac3 0x1da9>, + <&dmac4 0x1d9f>, <&dmac4 0x1da9>; + dma-names = "rx", "tx", "rx", "tx", "rx", "tx", + "rx", "tx", "rx", "tx"; + }; + src1: src-1 { + interrupts = <GIC_SPI 903 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&dmac0 0x1da0>, <&dmac0 0x1daa>, + <&dmac1 0x1da0>, <&dmac1 0x1daa>, + <&dmac2 0x1da0>, <&dmac2 0x1daa>, + <&dmac3 0x1da0>, <&dmac3 0x1daa>, + <&dmac4 0x1da0>, <&dmac4 0x1daa>; + dma-names = "rx", "tx", "rx", "tx", "rx", "tx", + "rx", "tx", "rx", "tx"; + }; + src-2 { + interrupts = <GIC_SPI 904 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&dmac0 0x1da1>, <&dmac0 0x1dab>, + <&dmac1 0x1da1>, <&dmac1 0x1dab>, + <&dmac2 0x1da1>, <&dmac2 0x1dab>, + <&dmac3 0x1da1>, <&dmac3 0x1dab>, + <&dmac4 0x1da1>, <&dmac4 0x1dab>; + dma-names = "rx", "tx", "rx", "tx", "rx", "tx", + "rx", "tx", "rx", "tx"; + }; + src-3 { + interrupts = <GIC_SPI 905 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&dmac0 0x1da2>, <&dmac0 0x1dac>, + <&dmac1 0x1da2>, <&dmac1 0x1dac>, + <&dmac2 0x1da2>, <&dmac2 0x1dac>, + <&dmac3 0x1da2>, <&dmac3 0x1dac>, + <&dmac4 0x1da2>, <&dmac4 0x1dac>; + dma-names = "rx", "tx", "rx", "tx", "rx", "tx", + "rx", "tx", "rx", "tx"; + }; + src-4 { + interrupts = <GIC_SPI 906 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&dmac0 0x1da3>, <&dmac0 0x1dad>, + <&dmac1 0x1da3>, <&dmac1 0x1dad>, + <&dmac2 0x1da3>, <&dmac2 0x1dad>, + <&dmac3 0x1da3>, <&dmac3 0x1dad>, + <&dmac4 0x1da3>, <&dmac4 0x1dad>; + dma-names = "rx", "tx", "rx", "tx", "rx", "tx", + "rx", "tx", "rx", "tx"; + }; + src-5 { + interrupts = <GIC_SPI 907 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&dmac0 0x1da4>, <&dmac0 0x1dae>, + <&dmac1 0x1da4>, <&dmac1 0x1dae>, + <&dmac2 0x1da4>, <&dmac2 0x1dae>, + <&dmac3 0x1da4>, <&dmac3 0x1dae>, + <&dmac4 0x1da4>, <&dmac4 0x1dae>; + dma-names = "rx", "tx", "rx", "tx", "rx", "tx", + "rx", "tx", "rx", "tx"; + }; + src-6 { + interrupts = <GIC_SPI 908 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&dmac0 0x1da5>, <&dmac0 0x1daf>, + <&dmac1 0x1da5>, <&dmac1 0x1daf>, + <&dmac2 0x1da5>, <&dmac2 0x1daf>, + <&dmac3 0x1da5>, <&dmac3 0x1daf>, + <&dmac4 0x1da5>, <&dmac4 0x1daf>; + dma-names = "rx", "tx", "rx", "tx", "rx", "tx", + "rx", "tx", "rx", "tx"; + }; + src-7 { + interrupts = <GIC_SPI 909 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&dmac0 0x1da6>, <&dmac0 0x1db0>, + <&dmac1 0x1da6>, <&dmac1 0x1db0>, + <&dmac2 0x1da6>, <&dmac2 0x1db0>, + <&dmac3 0x1da6>, <&dmac3 0x1db0>, + <&dmac4 0x1da6>, <&dmac4 0x1db0>; + dma-names = "rx", "tx", "rx", "tx", "rx", "tx", + "rx", "tx", "rx", "tx"; + }; + src-8 { + interrupts = <GIC_SPI 910 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&dmac0 0x1da7>, <&dmac0 0x1db1>, + <&dmac1 0x1da7>, <&dmac1 0x1db1>, + <&dmac2 0x1da7>, <&dmac2 0x1db1>, + <&dmac3 0x1da7>, <&dmac3 0x1db1>, + <&dmac4 0x1da7>, <&dmac4 0x1db1>; + dma-names = "rx", "tx", "rx", "tx", "rx", "tx", + "rx", "tx", "rx", "tx"; + }; + src-9 { + interrupts = <GIC_SPI 911 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&dmac0 0x1da8>, <&dmac0 0x1db2>, + <&dmac1 0x1da8>, <&dmac1 0x1db2>, + <&dmac2 0x1da8>, <&dmac2 0x1db2>, + <&dmac3 0x1da8>, <&dmac3 0x1db2>, + <&dmac4 0x1da8>, <&dmac4 0x1db2>; + dma-names = "rx", "tx", "rx", "tx", "rx", "tx", + "rx", "tx", "rx", "tx"; + }; + }; + + ssi { + ssi-0 { + interrupts = <GIC_SPI 889 IRQ_TYPE_LEVEL_HIGH>; + }; + ssi-1 { + interrupts = <GIC_SPI 890 IRQ_TYPE_LEVEL_HIGH>; + }; + ssi-2 { + interrupts = <GIC_SPI 891 IRQ_TYPE_LEVEL_HIGH>; + }; + ssi3: ssi-3 { + interrupts = <GIC_SPI 892 IRQ_TYPE_LEVEL_HIGH>; + }; + ssi4: ssi-4 { + interrupts = <GIC_SPI 893 IRQ_TYPE_LEVEL_HIGH>; + shared-pin; + }; + ssi-5 { + interrupts = <GIC_SPI 894 IRQ_TYPE_LEVEL_HIGH>; + }; + ssi-6 { + interrupts = <GIC_SPI 895 IRQ_TYPE_LEVEL_HIGH>; + }; + ssi-7 { + interrupts = <GIC_SPI 896 IRQ_TYPE_LEVEL_HIGH>; + }; + ssi-8 { + interrupts = <GIC_SPI 897 IRQ_TYPE_LEVEL_HIGH>; + }; + ssi-9 { + interrupts = <GIC_SPI 898 IRQ_TYPE_LEVEL_HIGH>; + }; + }; + + ssiu { + ssiu-0 { + dmas = <&dmac0 0x1d61>, <&dmac0 0x1d62>, + <&dmac1 0x1d61>, <&dmac1 0x1d62>, + <&dmac2 0x1d61>, <&dmac2 0x1d62>, + <&dmac3 0x1d61>, <&dmac3 0x1d62>, + <&dmac4 0x1d61>, <&dmac4 0x1d62>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-1 { + dmas = <&dmac0 0x1d63>, <&dmac0 0x1d64>, + <&dmac1 0x1d63>, <&dmac1 0x1d64>, + <&dmac2 0x1d63>, <&dmac2 0x1d64>, + <&dmac3 0x1d63>, <&dmac3 0x1d64>, + <&dmac4 0x1d63>, <&dmac4 0x1d64>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-2 { + dmas = <&dmac0 0x1d65>, <&dmac0 0x1d66>, + <&dmac1 0x1d65>, <&dmac1 0x1d66>, + <&dmac2 0x1d65>, <&dmac2 0x1d66>, + <&dmac3 0x1d65>, <&dmac3 0x1d66>, + <&dmac4 0x1d65>, <&dmac4 0x1d66>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-3 { + dmas = <&dmac0 0x1d67>, <&dmac0 0x1d68>, + <&dmac1 0x1d67>, <&dmac1 0x1d68>, + <&dmac2 0x1d67>, <&dmac2 0x1d68>, + <&dmac3 0x1d67>, <&dmac3 0x1d68>, + <&dmac4 0x1d67>, <&dmac4 0x1d68>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-4 { + dmas = <&dmac0 0x1d69>, <&dmac0 0x1d6a>, + <&dmac1 0x1d69>, <&dmac1 0x1d6a>, + <&dmac2 0x1d69>, <&dmac2 0x1d6a>, + <&dmac3 0x1d69>, <&dmac3 0x1d6a>, + <&dmac4 0x1d69>, <&dmac4 0x1d6a>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-5 { + dmas = <&dmac0 0x1d6b>, <&dmac0 0x1d6c>, + <&dmac1 0x1d6b>, <&dmac1 0x1d6c>, + <&dmac2 0x1d6b>, <&dmac2 0x1d6c>, + <&dmac3 0x1d6b>, <&dmac3 0x1d6c>, + <&dmac4 0x1d6b>, <&dmac4 0x1d6c>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-6 { + dmas = <&dmac0 0x1d6d>, <&dmac0 0x1d6e>, + <&dmac1 0x1d6d>, <&dmac1 0x1d6e>, + <&dmac2 0x1d6d>, <&dmac2 0x1d6e>, + <&dmac3 0x1d6d>, <&dmac3 0x1d6e>, + <&dmac4 0x1d6d>, <&dmac4 0x1d6e>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-7 { + dmas = <&dmac0 0x1d6f>, <&dmac0 0x1d70>, + <&dmac1 0x1d6f>, <&dmac1 0x1d70>, + <&dmac2 0x1d6f>, <&dmac2 0x1d70>, + <&dmac3 0x1d6f>, <&dmac3 0x1d70>, + <&dmac4 0x1d6f>, <&dmac4 0x1d70>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-8 { + dmas = <&dmac0 0x1d71>, <&dmac0 0x1d72>, + <&dmac1 0x1d71>, <&dmac1 0x1d72>, + <&dmac2 0x1d71>, <&dmac2 0x1d72>, + <&dmac3 0x1d71>, <&dmac3 0x1d72>, + <&dmac4 0x1d71>, <&dmac4 0x1d72>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-9 { + dmas = <&dmac0 0x1d73>, <&dmac0 0x1d74>, + <&dmac1 0x1d73>, <&dmac1 0x1d74>, + <&dmac2 0x1d73>, <&dmac2 0x1d74>, + <&dmac3 0x1d73>, <&dmac3 0x1d74>, + <&dmac4 0x1d73>, <&dmac4 0x1d74>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-10 { + dmas = <&dmac0 0x1d75>, <&dmac0 0x1d76>, + <&dmac1 0x1d75>, <&dmac1 0x1d76>, + <&dmac2 0x1d75>, <&dmac2 0x1d76>, + <&dmac3 0x1d75>, <&dmac3 0x1d76>, + <&dmac4 0x1d75>, <&dmac4 0x1d76>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-11 { + dmas = <&dmac0 0x1d77>, <&dmac0 0x1d78>, + <&dmac1 0x1d77>, <&dmac1 0x1d78>, + <&dmac2 0x1d77>, <&dmac2 0x1d78>, + <&dmac3 0x1d77>, <&dmac3 0x1d78>, + <&dmac4 0x1d77>, <&dmac4 0x1d78>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-12 { + dmas = <&dmac0 0x1d79>, <&dmac0 0x1d7a>, + <&dmac1 0x1d79>, <&dmac1 0x1d7a>, + <&dmac2 0x1d79>, <&dmac2 0x1d7a>, + <&dmac3 0x1d79>, <&dmac3 0x1d7a>, + <&dmac4 0x1d79>, <&dmac4 0x1d7a>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-13 { + dmas = <&dmac0 0x1d7b>, <&dmac0 0x1d7c>, + <&dmac1 0x1d7b>, <&dmac1 0x1d7c>, + <&dmac2 0x1d7b>, <&dmac2 0x1d7c>, + <&dmac3 0x1d7b>, <&dmac3 0x1d7c>, + <&dmac4 0x1d7b>, <&dmac4 0x1d7c>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-14 { + dmas = <&dmac0 0x1d7d>, <&dmac0 0x1d7e>, + <&dmac1 0x1d7d>, <&dmac1 0x1d7e>, + <&dmac2 0x1d7d>, <&dmac2 0x1d7e>, + <&dmac3 0x1d7d>, <&dmac3 0x1d7e>, + <&dmac4 0x1d7d>, <&dmac4 0x1d7e>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-15 { + dmas = <&dmac0 0x1d7f>, <&dmac0 0x1d80>, + <&dmac1 0x1d7f>, <&dmac1 0x1d80>, + <&dmac2 0x1d7f>, <&dmac2 0x1d80>, + <&dmac3 0x1d7f>, <&dmac3 0x1d80>, + <&dmac4 0x1d7f>, <&dmac4 0x1d80>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-16 { + dmas = <&dmac0 0x1d81>, <&dmac0 0x1d82>, + <&dmac1 0x1d81>, <&dmac1 0x1d82>, + <&dmac2 0x1d81>, <&dmac2 0x1d82>, + <&dmac3 0x1d81>, <&dmac3 0x1d82>, + <&dmac4 0x1d81>, <&dmac4 0x1d82>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-17 { + dmas = <&dmac0 0x1d83>, <&dmac0 0x1d84>, + <&dmac1 0x1d83>, <&dmac1 0x1d84>, + <&dmac2 0x1d83>, <&dmac2 0x1d84>, + <&dmac3 0x1d83>, <&dmac3 0x1d84>, + <&dmac4 0x1d83>, <&dmac4 0x1d84>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-18 { + dmas = <&dmac0 0x1d85>, <&dmac0 0x1d86>, + <&dmac1 0x1d85>, <&dmac1 0x1d86>, + <&dmac2 0x1d85>, <&dmac2 0x1d86>, + <&dmac3 0x1d85>, <&dmac3 0x1d86>, + <&dmac4 0x1d85>, <&dmac4 0x1d86>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-19 { + dmas = <&dmac0 0x1d87>, <&dmac0 0x1d88>, + <&dmac1 0x1d87>, <&dmac1 0x1d88>, + <&dmac2 0x1d87>, <&dmac2 0x1d88>, + <&dmac3 0x1d87>, <&dmac3 0x1d88>, + <&dmac4 0x1d87>, <&dmac4 0x1d88>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-20 { + dmas = <&dmac0 0x1d89>, <&dmac0 0x1d8a>, + <&dmac1 0x1d89>, <&dmac1 0x1d8a>, + <&dmac2 0x1d89>, <&dmac2 0x1d8a>, + <&dmac3 0x1d89>, <&dmac3 0x1d8a>, + <&dmac4 0x1d89>, <&dmac4 0x1d8a>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-21 { + dmas = <&dmac0 0x1d8b>, <&dmac0 0x1d8c>, + <&dmac1 0x1d8b>, <&dmac1 0x1d8c>, + <&dmac2 0x1d8b>, <&dmac2 0x1d8c>, + <&dmac3 0x1d8b>, <&dmac3 0x1d8c>, + <&dmac4 0x1d8b>, <&dmac4 0x1d8c>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-22 { + dmas = <&dmac0 0x1d8d>, <&dmac0 0x1d8e>, + <&dmac1 0x1d8d>, <&dmac1 0x1d8e>, + <&dmac2 0x1d8d>, <&dmac2 0x1d8e>, + <&dmac3 0x1d8d>, <&dmac3 0x1d8e>, + <&dmac4 0x1d8d>, <&dmac4 0x1d8e>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-23 { + dmas = <&dmac0 0x1d8f>, <&dmac0 0x1d90>, + <&dmac1 0x1d8f>, <&dmac1 0x1d90>, + <&dmac2 0x1d8f>, <&dmac2 0x1d90>, + <&dmac3 0x1d8f>, <&dmac3 0x1d90>, + <&dmac4 0x1d8f>, <&dmac4 0x1d90>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-24 { + dmas = <&dmac0 0x1d91>, <&dmac0 0x1d92>, + <&dmac1 0x1d91>, <&dmac1 0x1d92>, + <&dmac2 0x1d91>, <&dmac2 0x1d92>, + <&dmac3 0x1d91>, <&dmac3 0x1d92>, + <&dmac4 0x1d91>, <&dmac4 0x1d92>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-25 { + dmas = <&dmac0 0x1d93>, <&dmac0 0x1d94>, + <&dmac1 0x1d93>, <&dmac1 0x1d94>, + <&dmac2 0x1d93>, <&dmac2 0x1d94>, + <&dmac3 0x1d93>, <&dmac3 0x1d94>, + <&dmac4 0x1d93>, <&dmac4 0x1d94>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-26 { + dmas = <&dmac0 0x1d95>, <&dmac0 0x1d96>, + <&dmac1 0x1d95>, <&dmac1 0x1d96>, + <&dmac2 0x1d95>, <&dmac2 0x1d96>, + <&dmac3 0x1d95>, <&dmac3 0x1d96>, + <&dmac4 0x1d95>, <&dmac4 0x1d96>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + ssiu-27 { + dmas = <&dmac0 0x1d97>, <&dmac0 0x1d98>, + <&dmac1 0x1d97>, <&dmac1 0x1d98>, + <&dmac2 0x1d97>, <&dmac2 0x1d98>, + <&dmac3 0x1d97>, <&dmac3 0x1d98>, + <&dmac4 0x1d97>, <&dmac4 0x1d98>; + dma-names = "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx", "tx", "rx"; + }; + }; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + rsnd_endpoint0: endpoint { + remote-endpoint = <&codec_endpoint>; + dai-format = "i2s"; + bitclock-master = <&rsnd_endpoint0>; + frame-master = <&rsnd_endpoint0>; + playback = <&ssi3>, <&src1>, <&dvc1>; + capture = <&ssi4>, <&src0>, <&dvc0>; + }; + }; + }; + }; diff --git a/sound/soc/renesas/rcar/adg.c b/sound/soc/renesas/rcar/adg.c index 8641b73d1f77..5479cefb6dbe 100644 --- a/sound/soc/renesas/rcar/adg.c +++ b/sound/soc/renesas/rcar/adg.c @@ -19,6 +19,9 @@ #define CLKOUT3 3 #define CLKOUTMAX 4 +/* Maximum SSI count for per-SSI clocks */ +#define ADG_SSI_MAX 10 + #define BRGCKR_31 (1 << 31) #define BRRx_MASK(x) (0x3FF & x) @@ -34,10 +37,14 @@ struct rsnd_adg { struct clk *adg; struct clk *clkin[CLKINMAX]; struct clk *clkout[CLKOUTMAX]; + /* RZ/G3E: per-SSI ADG clocks (adg-ssi-0 through adg-ssi-9) */ + struct clk *clk_adg_ssi[ADG_SSI_MAX]; + struct clk *clk_ssif_supply; struct clk *null_clk; struct clk_onecell_data onecell; struct rsnd_mod mod; int clkin_rate[CLKINMAX]; + bool ssi_clk_prepared; int clkin_size; int clkout_size; u32 ckr; @@ -70,6 +77,13 @@ static const char * const clkin_name_gen2[] = { [CLKI] = "clk_i", }; +static const char * const clkin_name_rzg3e[] = { + [CLKA] = "audio-clka", + [CLKB] = "audio-clkb", + [CLKC] = "audio-clkc", + [CLKI] = "audio-clki", +}; + static const char * const clkout_name_gen2[] = { [CLKOUT] = "audio_clkout", [CLKOUT1] = "audio_clkout1", @@ -343,8 +357,16 @@ int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate) int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod) { + struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + int id = rsnd_mod_id(ssi_mod); + rsnd_adg_set_ssi_clk(ssi_mod, 0); + /* RZ/G3E: only disable here, unprepare is done in hw_free */ + clk_disable(adg->clk_adg_ssi[id]); + clk_disable(adg->clk_ssif_supply); + return 0; } @@ -354,7 +376,8 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - int data; + int id = rsnd_mod_id(ssi_mod); + int ret, data; u32 ckr = 0; data = rsnd_adg_clk_query(priv, rate); @@ -376,9 +399,63 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) (ckr) ? adg->brg_rate[ADG_HZ_48] : adg->brg_rate[ADG_HZ_441]); + /* + * RZ/G3E: enable per-SSI and supply clocks + */ + ret = clk_enable(adg->clk_adg_ssi[id]); + if (ret) { + dev_err(dev, "Cannot enable adg-ssi-%d ADG clock\n", id); + return ret; + } + + ret = clk_enable(adg->clk_ssif_supply); + if (ret) { + dev_err(dev, "Cannot enable SSIF supply clock\n"); + clk_disable(adg->clk_adg_ssi[id]); + return ret; + } + return 0; } +static int rsnd_adg_ssi_clk_prepare(struct rsnd_adg *adg) +{ + int i, ret; + + if (adg->ssi_clk_prepared) + return 0; + + for (i = 0; i < ADG_SSI_MAX; i++) { + ret = clk_prepare(adg->clk_adg_ssi[i]); + if (ret) + goto unwind; + } + ret = clk_prepare(adg->clk_ssif_supply); + if (ret) + goto unwind; + + adg->ssi_clk_prepared = true; + return 0; + +unwind: + while (i--) + clk_unprepare(adg->clk_adg_ssi[i]); + return ret; +} + +static void rsnd_adg_ssi_clk_unprepare(struct rsnd_adg *adg) +{ + int i; + + if (!adg->ssi_clk_prepared) + return; + adg->ssi_clk_prepared = false; + + clk_unprepare(adg->clk_ssif_supply); + for (i = 0; i < ADG_SSI_MAX; i++) + clk_unprepare(adg->clk_adg_ssi[i]); +} + int rsnd_adg_clk_control(struct rsnd_priv *priv, int enable) { struct rsnd_adg *adg = rsnd_priv_to_adg(priv); @@ -421,6 +498,28 @@ int rsnd_adg_clk_control(struct rsnd_priv *priv, int enable) * rsnd_adg_clk_enable() might return error (_disable() will not). * We need to rollback in such case */ + /* + * RZ/G3E per-SSI ADG and SSIF supply clocks. + * + * Follow the same style as for_each_rsnd_clkin() above: on enable, + * try to prepare every clock and accumulate the error. On disable, + * unprepare every clock. Absent optional clocks are NULL, for + * which clk_prepare() and clk_unprepare() are no-ops. + */ + if (enable) { + int sub_ret = rsnd_adg_ssi_clk_prepare(adg); + + /* Preserve the first error from the clkin loop above. */ + if (sub_ret && !ret) + ret = sub_ret; + } else { + rsnd_adg_ssi_clk_unprepare(adg); + } + + /* + * rsnd_adg_clk_enable() might return error (_disable() will not). + * We need to rollback in such case + */ if (ret < 0) rsnd_adg_clk_disable(priv); @@ -482,6 +581,9 @@ static int rsnd_adg_get_clkin(struct rsnd_priv *priv) if (rsnd_is_gen4(priv)) { clkin_name = clkin_name_gen4; clkin_size = ARRAY_SIZE(clkin_name_gen4); + } else if (rsnd_is_rzg3e(priv)) { + clkin_name = clkin_name_rzg3e; + clkin_size = ARRAY_SIZE(clkin_name_rzg3e); } /* @@ -769,8 +871,34 @@ void rsnd_adg_clk_dbg_info(struct rsnd_priv *priv, struct seq_file *m) #define rsnd_adg_clk_dbg_info(priv, m) #endif +static int rsnd_adg_get_ssi_clks(struct rsnd_priv *priv) +{ + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct device *dev = rsnd_priv_to_dev(priv); + char name[16]; + int i; + + /* SSIF supply clock */ + adg->clk_ssif_supply = devm_clk_get_optional(dev, "ssif_supply"); + if (IS_ERR(adg->clk_ssif_supply)) + return dev_err_probe(dev, PTR_ERR(adg->clk_ssif_supply), + "failed to get ssif_supply clock\n"); + + /* Per-SSI ADG clocks (RZ/G3E-only; no legacy dotted form exists) */ + for (i = 0; i < ADG_SSI_MAX; i++) { + snprintf(name, sizeof(name), "adg-ssi-%d", i); + adg->clk_adg_ssi[i] = devm_clk_get_optional(dev, name); + if (IS_ERR(adg->clk_adg_ssi[i])) + return dev_err_probe(dev, PTR_ERR(adg->clk_adg_ssi[i]), + "failed to get %s clock\n", name); + } + + return 0; +} + int rsnd_adg_probe(struct rsnd_priv *priv) { + struct reset_control *rstc; struct rsnd_adg *adg; struct device *dev = rsnd_priv_to_dev(priv); int ret; @@ -779,8 +907,11 @@ int rsnd_adg_probe(struct rsnd_priv *priv) if (!adg) return -ENOMEM; - ret = rsnd_mod_init(priv, &adg->mod, &adg_ops, - NULL, 0, 0); + rstc = devm_reset_control_get_optional_exclusive(dev, "adg"); + if (IS_ERR(rstc)) + return dev_err_probe(dev, PTR_ERR(rstc), "failed to get adg reset\n"); + + ret = rsnd_mod_init(priv, &adg->mod, &adg_ops, NULL, rstc, 0, 0); if (ret) return ret; @@ -794,6 +925,11 @@ int rsnd_adg_probe(struct rsnd_priv *priv) if (ret) return ret; + /* RZ/G3E-specific: per-SSI ADG and SSIF supply clocks */ + ret = rsnd_adg_get_ssi_clks(priv); + if (ret) + return ret; + ret = rsnd_adg_clk_enable(priv); if (ret) return ret; @@ -817,3 +953,29 @@ void rsnd_adg_remove(struct rsnd_priv *priv) /* It should be called after rsnd_adg_clk_disable() */ rsnd_adg_null_clk_clean(priv); } + +static struct rsnd_mod *rsnd_adg_mod_get(struct rsnd_priv *priv) +{ + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + + if (!adg) + return NULL; + + return rsnd_mod_get(adg); +} + +void rsnd_adg_suspend(struct rsnd_priv *priv) +{ + struct rsnd_mod *mod = rsnd_adg_mod_get(priv); + + if (mod) + rsnd_suspend_clk_reset(mod->clk, mod->rstc); +} + +void rsnd_adg_resume(struct rsnd_priv *priv) +{ + struct rsnd_mod *mod = rsnd_adg_mod_get(priv); + + if (mod) + rsnd_resume_clk_reset(mod->clk, mod->rstc); +} diff --git a/sound/soc/renesas/rcar/cmd.c b/sound/soc/renesas/rcar/cmd.c index 8d9a1e345a22..13beef389797 100644 --- a/sound/soc/renesas/rcar/cmd.c +++ b/sound/soc/renesas/rcar/cmd.c @@ -171,7 +171,7 @@ int rsnd_cmd_probe(struct rsnd_priv *priv) for_each_rsnd_cmd(cmd, priv, i) { int ret = rsnd_mod_init(priv, rsnd_mod_get(cmd), - &rsnd_cmd_ops, NULL, + &rsnd_cmd_ops, NULL, NULL, RSND_MOD_CMD, i); if (ret) return ret; diff --git a/sound/soc/renesas/rcar/core.c b/sound/soc/renesas/rcar/core.c index 2dc078358612..9ce56cd84f46 100644 --- a/sound/soc/renesas/rcar/core.c +++ b/sound/soc/renesas/rcar/core.c @@ -106,6 +106,8 @@ static const struct of_device_id rsnd_of_match[] = { { .compatible = "renesas,rcar_sound-gen4", .data = (void *)RSND_GEN4 }, /* Special Handling */ { .compatible = "renesas,rcar_sound-r8a77990", .data = (void *)(RSND_GEN3 | RSND_SOC_E) }, + { .compatible = "renesas,r9a09g047-sound", + .data = (void *)(RSND_RZ3 | RSND_RZG3E | RSND_SSIU_BUSIF_STATUS_COUNT_2) }, {}, }; MODULE_DEVICE_TABLE(of, rsnd_of_match); @@ -196,18 +198,29 @@ int rsnd_mod_init(struct rsnd_priv *priv, struct rsnd_mod *mod, struct rsnd_mod_ops *ops, struct clk *clk, + struct reset_control *rstc, enum rsnd_mod_type type, int id) { - int ret = clk_prepare(clk); + int ret; + ret = clk_prepare_enable(clk); if (ret) return ret; + ret = reset_control_deassert(rstc); + if (ret) { + clk_disable_unprepare(clk); + return ret; + } + + clk_disable(clk); + mod->id = id; mod->ops = ops; mod->type = type; mod->clk = clk; + mod->rstc = rstc; mod->priv = priv; return 0; @@ -215,6 +228,8 @@ int rsnd_mod_init(struct rsnd_priv *priv, void rsnd_mod_quit(struct rsnd_mod *mod) { + reset_control_assert(mod->rstc); + mod->rstc = NULL; clk_unprepare(mod->clk); mod->clk = NULL; } @@ -947,7 +962,8 @@ static int rsnd_soc_hw_rule_channels(struct snd_pcm_hw_params *params, static const struct snd_pcm_hardware rsnd_pcm_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID, + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_RESUME, .buffer_bytes_max = 64 * 1024, .period_bytes_min = 32, .period_bytes_max = 8192, @@ -1219,6 +1235,107 @@ int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name return i; } +/* + * Build "<base>-<index>" or "<base>.<index>" and try the hyphen form first, + * falling back to the dot form if the hyphen form is not present. This lets + * the driver accept both the new DT convention ("ssi-0", "src-0", ...) and + * the legacy R-Car convention ("ssi.0", "src.0", ...) transparently. + * + * @base: name prefix ("ssi", "src", "ctu", "mix", "dvc", "adg.ssi", ...) + * @index: integer suffix + * + * On -ENOENT from the hyphen form, the dot form is tried. All other errors + * (including -EPROBE_DEFER) are returned to the caller unchanged, so + * behaviour against the clock and reset frameworks is preserved. + */ +#define RSND_INDEXED_NAME_MAX 32 + +static void rsnd_format_indexed_name(char *buf, size_t buflen, char sep, + const char *base, int index) +{ + snprintf(buf, buflen, "%s%c%d", base, sep, index); +} + +struct clk *rsnd_devm_clk_get_indexed(struct device *dev, + const char *base, int index) +{ + char name[RSND_INDEXED_NAME_MAX]; + struct clk *clk; + + rsnd_format_indexed_name(name, sizeof(name), '-', base, index); + clk = devm_clk_get(dev, name); + if (!IS_ERR(clk) || PTR_ERR(clk) != -ENOENT) + return clk; + + rsnd_format_indexed_name(name, sizeof(name), '.', base, index); + return devm_clk_get(dev, name); +} + +struct clk *rsnd_devm_clk_get_optional_indexed(struct device *dev, + const char *base, int index) +{ + char name[RSND_INDEXED_NAME_MAX]; + struct clk *clk; + + rsnd_format_indexed_name(name, sizeof(name), '-', base, index); + clk = devm_clk_get_optional(dev, name); + if (IS_ERR(clk) || clk) + return clk; + + rsnd_format_indexed_name(name, sizeof(name), '.', base, index); + return devm_clk_get_optional(dev, name); +} + +struct reset_control * +rsnd_devm_reset_control_get_optional_indexed(struct device *dev, + const char *base, int index) +{ + char name[RSND_INDEXED_NAME_MAX]; + struct reset_control *rstc; + + rsnd_format_indexed_name(name, sizeof(name), '-', base, index); + rstc = devm_reset_control_get_optional(dev, name); + if (IS_ERR(rstc) || rstc) + return rstc; + + rsnd_format_indexed_name(name, sizeof(name), '.', base, index); + return devm_reset_control_get_optional(dev, name); +} + +/* + * Strip the "rcar_sound," prefix from a legacy node name. + * + * The RZ/G3E binding uses unprefixed sub-node names (e.g. "ssi", + * "ssiu") while earlier R-Car bindings use the legacy "rcar_sound,*" + * form. This helper returns the unprefixed portion (the part after + * the comma) or NULL if there is no prefix. + * + * Centralising the convention here keeps every call site consistent. + */ +static const char *rsnd_node_name_strip_prefix(const char *name) +{ + const char *comma = strchr(name, ','); + + return comma ? comma + 1 : NULL; +} + +struct device_node *rsnd_parse_of_node(struct rsnd_priv *priv, const char *name) +{ + struct device_node *np = rsnd_priv_to_dev(priv)->of_node; + struct device_node *node; + const char *unprefixed; + + node = of_get_child_by_name(np, name); + if (node) + return node; + + unprefixed = rsnd_node_name_strip_prefix(name); + if (unprefixed) + node = of_get_child_by_name(np, unprefixed); + + return node; +} + static struct device_node* rsnd_pick_endpoint_node_for_ports(struct device_node *e_ports, struct device_node *e_port) @@ -2043,11 +2160,35 @@ static void rsnd_remove(struct platform_device *pdev) remove_func[i](priv); } +void rsnd_suspend_clk_reset(struct clk *clk, struct reset_control *rstc) +{ + clk_unprepare(clk); + reset_control_assert(rstc); +} + +void rsnd_resume_clk_reset(struct clk *clk, struct reset_control *rstc) +{ + reset_control_deassert(rstc); + clk_prepare(clk); +} + static int rsnd_suspend(struct device *dev) { struct rsnd_priv *priv = dev_get_drvdata(dev); + /* + * Reverse order of probe: + * ADG -> DVC -> MIX -> CTU -> SRC -> SSIU -> SSI -> DMA + */ rsnd_adg_clk_disable(priv); + rsnd_adg_suspend(priv); + rsnd_dvc_suspend(priv); + rsnd_mix_suspend(priv); + rsnd_ctu_suspend(priv); + rsnd_src_suspend(priv); + rsnd_ssiu_suspend(priv); + rsnd_ssi_suspend(priv); + rsnd_dma_suspend(priv); return 0; } @@ -2056,7 +2197,21 @@ static int rsnd_resume(struct device *dev) { struct rsnd_priv *priv = dev_get_drvdata(dev); - return rsnd_adg_clk_enable(priv); + /* + * Same order as probe: + * DMA -> SSI -> SSIU -> SRC -> CTU -> MIX -> DVC -> ADG + */ + rsnd_dma_resume(priv); + rsnd_ssi_resume(priv); + rsnd_ssiu_resume(priv); + rsnd_src_resume(priv); + rsnd_ctu_resume(priv); + rsnd_mix_resume(priv); + rsnd_dvc_resume(priv); + rsnd_adg_resume(priv); + rsnd_adg_clk_enable(priv); + + return 0; } static const struct dev_pm_ops rsnd_pm_ops = { diff --git a/sound/soc/renesas/rcar/ctu.c b/sound/soc/renesas/rcar/ctu.c index bd4c61f9fb3c..7db0fb3612bc 100644 --- a/sound/soc/renesas/rcar/ctu.c +++ b/sound/soc/renesas/rcar/ctu.c @@ -6,7 +6,6 @@ #include "rsnd.h" -#define CTU_NAME_SIZE 16 #define CTU_NAME "ctu" /* @@ -319,7 +318,6 @@ int rsnd_ctu_probe(struct rsnd_priv *priv) struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_ctu *ctu; struct clk *clk; - char name[CTU_NAME_SIZE]; int i, nr, ret; node = rsnd_ctu_of_node(priv); @@ -350,17 +348,14 @@ int rsnd_ctu_probe(struct rsnd_priv *priv) * CTU00, CTU01, CTU02, CTU03 => CTU0 * CTU10, CTU11, CTU12, CTU13 => CTU1 */ - snprintf(name, CTU_NAME_SIZE, "%s.%d", - CTU_NAME, i / 4); - - clk = devm_clk_get(dev, name); + clk = rsnd_devm_clk_get_indexed(dev, CTU_NAME, i / 4); if (IS_ERR(clk)) { ret = PTR_ERR(clk); goto rsnd_ctu_probe_done; } ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops, - clk, RSND_MOD_CTU, i); + clk, NULL, RSND_MOD_CTU, i); if (ret) goto rsnd_ctu_probe_done; @@ -383,3 +378,23 @@ void rsnd_ctu_remove(struct rsnd_priv *priv) rsnd_mod_quit(rsnd_mod_get(ctu)); } } + +void rsnd_ctu_suspend(struct rsnd_priv *priv) +{ + struct rsnd_ctu *ctu; + int i; + + for_each_rsnd_ctu(ctu, priv, i) + rsnd_suspend_clk_reset(rsnd_mod_get(ctu)->clk, + rsnd_mod_get(ctu)->rstc); +} + +void rsnd_ctu_resume(struct rsnd_priv *priv) +{ + struct rsnd_ctu *ctu; + int i; + + for_each_rsnd_ctu(ctu, priv, i) + rsnd_resume_clk_reset(rsnd_mod_get(ctu)->clk, + rsnd_mod_get(ctu)->rstc); +} diff --git a/sound/soc/renesas/rcar/dma.c b/sound/soc/renesas/rcar/dma.c index 2035ce06fe4c..793dd4adbe5c 100644 --- a/sound/soc/renesas/rcar/dma.c +++ b/sound/soc/renesas/rcar/dma.c @@ -47,6 +47,9 @@ struct rsnd_dma_ctrl { phys_addr_t ppres; int dmaen_num; int dmapp_num; + /* RZ/G3E: Audio DMAC peri-peri clock and reset */ + struct clk *audmapp_clk; + struct reset_control *audmapp_rstc; }; #define rsnd_priv_to_dmac(p) ((struct rsnd_dma_ctrl *)(p)->dma) @@ -478,6 +481,69 @@ static struct rsnd_mod_ops rsnd_dmapp_ops = { DEBUG_INFO }; +struct rsnd_dma_addr { + dma_addr_t out_addr; + dma_addr_t in_addr; +}; + +struct rsnd_dma_addr_dir { + struct rsnd_dma_addr capture[3]; + struct rsnd_dma_addr playback[3]; +}; + +struct rsnd_dma_addr_map { + struct rsnd_dma_addr_dir src; + struct rsnd_dma_addr_dir ssi; + struct rsnd_dma_addr_dir ssiu; +}; + +static dma_addr_t +rsnd_dma_addr_lookup(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + struct rsnd_priv *priv, + const struct rsnd_dma_addr_map *map, + int is_play, int is_from) +{ + struct device *dev = rsnd_priv_to_dev(priv); + int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod) || + !!(rsnd_io_to_mod_ssiu(io) == mod); + int use_src = !!rsnd_io_to_mod_src(io); + int use_cmd = !!rsnd_io_to_mod_dvc(io) || + !!rsnd_io_to_mod_mix(io) || + !!rsnd_io_to_mod_ctu(io); + int id = rsnd_mod_id(mod); + const struct rsnd_dma_addr_dir *dir; + const struct rsnd_dma_addr *addr; + + /* it shouldn't happen */ + if (use_cmd && !use_src) + dev_err(dev, "DVC is selected without SRC\n"); + + /* use SSIU or SSI? */ + if (is_ssi && rsnd_ssi_use_busif(io)) + is_ssi++; + + dev_dbg(dev, "dma%d addr : is_ssi=%d use_src=%d use_cmd=%d\n", + id, is_ssi, use_src, use_cmd); + + switch (is_ssi) { + case 2: + dir = &map->ssiu; + break; + case 1: + dir = &map->ssi; + break; + default: + dir = &map->src; + break; + } + + addr = is_play ? &dir->playback[use_src + use_cmd] + : &dir->capture[use_src + use_cmd]; + + return is_from ? addr->out_addr : addr->in_addr; +} + /* * Common DMAC Interface */ @@ -524,47 +590,45 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io, struct device *dev = rsnd_priv_to_dev(priv); phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_BASE_SSI); phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_BASE_SCU); - int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod) || - !!(rsnd_io_to_mod_ssiu(io) == mod); - int use_src = !!rsnd_io_to_mod_src(io); - int use_cmd = !!rsnd_io_to_mod_dvc(io) || - !!rsnd_io_to_mod_mix(io) || - !!rsnd_io_to_mod_ctu(io); int id = rsnd_mod_id(mod); int busif = rsnd_mod_id_sub(rsnd_io_to_mod_ssiu(io)); - struct dma_addr { - dma_addr_t out_addr; - dma_addr_t in_addr; - } dma_addrs[3][2][3] = { - /* SRC */ - /* Capture */ - {{{ 0, 0 }, - { RDMA_SRC_O_N(src, id), RDMA_SRC_I_P(src, id) }, - { RDMA_CMD_O_N(src, id), RDMA_SRC_I_P(src, id) } }, - /* Playback */ - {{ 0, 0, }, - { RDMA_SRC_O_P(src, id), RDMA_SRC_I_N(src, id) }, - { RDMA_CMD_O_P(src, id), RDMA_SRC_I_N(src, id) } } + const struct rsnd_dma_addr_map map = { + .src = { + .capture = { + { 0, 0 }, + { RDMA_SRC_O_N(src, id), RDMA_SRC_I_P(src, id) }, + { RDMA_CMD_O_N(src, id), RDMA_SRC_I_P(src, id) }, + }, + .playback = { + { 0, 0 }, + { RDMA_SRC_O_P(src, id), RDMA_SRC_I_N(src, id) }, + { RDMA_CMD_O_P(src, id), RDMA_SRC_I_N(src, id) }, + }, }, - /* SSI */ - /* Capture */ - {{{ RDMA_SSI_O_N(ssi, id), 0 }, - { RDMA_SSIU_O_P(ssi, id, busif), 0 }, - { RDMA_SSIU_O_P(ssi, id, busif), 0 } }, - /* Playback */ - {{ 0, RDMA_SSI_I_N(ssi, id) }, - { 0, RDMA_SSIU_I_P(ssi, id, busif) }, - { 0, RDMA_SSIU_I_P(ssi, id, busif) } } + .ssi = { + .capture = { + { RDMA_SSI_O_N(ssi, id), 0 }, + { RDMA_SSIU_O_P(ssi, id, busif), 0 }, + { RDMA_SSIU_O_P(ssi, id, busif), 0 }, + }, + .playback = { + { 0, RDMA_SSI_I_N(ssi, id) }, + { 0, RDMA_SSIU_I_P(ssi, id, busif) }, + { 0, RDMA_SSIU_I_P(ssi, id, busif) }, + }, + }, + .ssiu = { + .capture = { + { RDMA_SSIU_O_N(ssi, id, busif), 0 }, + { RDMA_SSIU_O_P(ssi, id, busif), 0 }, + { RDMA_SSIU_O_P(ssi, id, busif), 0 }, + }, + .playback = { + { 0, RDMA_SSIU_I_N(ssi, id, busif) }, + { 0, RDMA_SSIU_I_P(ssi, id, busif) }, + { 0, RDMA_SSIU_I_P(ssi, id, busif) }, + }, }, - /* SSIU */ - /* Capture */ - {{{ RDMA_SSIU_O_N(ssi, id, busif), 0 }, - { RDMA_SSIU_O_P(ssi, id, busif), 0 }, - { RDMA_SSIU_O_P(ssi, id, busif), 0 } }, - /* Playback */ - {{ 0, RDMA_SSIU_I_N(ssi, id, busif) }, - { 0, RDMA_SSIU_I_P(ssi, id, busif) }, - { 0, RDMA_SSIU_I_P(ssi, id, busif) } } }, }; /* @@ -577,17 +641,86 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io, dev_err(dev, "This driver doesn't support SSI%d-%d, so far", id, busif); - /* it shouldn't happen */ - if (use_cmd && !use_src) - dev_err(dev, "DVC is selected without SRC\n"); + return rsnd_dma_addr_lookup(io, mod, priv, &map, is_play, is_from); +} - /* use SSIU or SSI ? */ - if (is_ssi && rsnd_ssi_use_busif(io)) - is_ssi++; +/* + * ex) G3E case + * mod / DMAC in / DMAC out / DMAC PP in / DMAC pp out + * SSI : 0x13C31000 / 0x13C40000 / 0x13C40000 + * SSIU: 0x13C31000 / 0x13C40000 / 0x13C40000 / 0xEC400000 / 0xEC400000 + * SCU : 0x13C00000 / 0x13C10000 / 0x13C14000 / 0xEC300000 / 0xEC304000 + * CMD : 0x13C00000 / / 0x13C18000 0xEC308000 + */ + +/* RZ/G3E DMA address macros */ +#define RDMA_SSI_I_N_G3E(addr, i) (addr ##_reg + 0x0000F000 + (0x1000 * (i))) +#define RDMA_SSI_O_N_G3E(addr, i) (addr ##_reg + 0x0000F000 + (0x1000 * (i))) + +#define RDMA_SSIU_I_N_G3E(addr, i, j) (addr ##_reg + 0x0000F000 + (0x1000 * (i)) + (((j) / 4) * 0xA000) + (((j) % 4) * 0x400) - (0x4000 * ((i) / 9) * ((j) / 4))) +#define RDMA_SSIU_O_N_G3E(addr, i, j) RDMA_SSIU_I_N_G3E(addr, i, j) + +#define RDMA_SSIU_I_P_G3E(addr, i, j) (addr ##_reg + 0xD87CF000 + (0x1000 * (i)) + (((j) / 4) * 0xA000) + (((j) % 4) * 0x400) - (0x4000 * ((i) / 9) * ((j) / 4))) +#define RDMA_SSIU_O_P_G3E(addr, i, j) RDMA_SSIU_I_P_G3E(addr, i, j) + +#define RDMA_SRC_I_N_G3E(addr, i) (addr ##_reg + 0x00010000 + (0x400 * (i))) +#define RDMA_SRC_O_N_G3E(addr, i) (addr ##_reg + 0x00014000 + (0x400 * (i))) + +#define RDMA_SRC_I_P_G3E(addr, i) (addr ##_reg + 0xD8700000 + (0x400 * (i))) +#define RDMA_SRC_O_P_G3E(addr, i) (addr ##_reg + 0xD8704000 + (0x400 * (i))) - return (is_from) ? - dma_addrs[is_ssi][is_play][use_src + use_cmd].out_addr : - dma_addrs[is_ssi][is_play][use_src + use_cmd].in_addr; +#define RDMA_CMD_O_N_G3E(addr, i) (addr ##_reg + 0x00018000 + (0x400 * (i))) +#define RDMA_CMD_O_P_G3E(addr, i) (addr ##_reg + 0xD8708000 + (0x400 * (i))) + +static dma_addr_t +rsnd_rzg3e_dma_addr(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, int is_play, int is_from) +{ + struct rsnd_priv *priv = rsnd_io_to_priv(io); + phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_BASE_SSI); + phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_BASE_SCU); + int id = rsnd_mod_id(mod); + int busif = rsnd_mod_id_sub(rsnd_io_to_mod_ssiu(io)); + const struct rsnd_dma_addr_map map = { + .src = { + .capture = { + { 0, 0 }, + { RDMA_SRC_O_N_G3E(src, id), RDMA_SRC_I_P_G3E(src, id) }, + { RDMA_CMD_O_N_G3E(src, id), RDMA_SRC_I_P_G3E(src, id) }, + }, + .playback = { + { 0, 0 }, + { RDMA_SRC_O_P_G3E(src, id), RDMA_SRC_I_N_G3E(src, id) }, + { RDMA_CMD_O_P_G3E(src, id), RDMA_SRC_I_N_G3E(src, id) }, + }, + }, + .ssi = { + .capture = { + { RDMA_SSI_O_N_G3E(ssi, id), 0 }, + { RDMA_SSIU_O_P_G3E(ssi, id, busif), 0 }, + { RDMA_SSIU_O_P_G3E(ssi, id, busif), 0 }, + }, + .playback = { + { 0, RDMA_SSI_I_N_G3E(ssi, id) }, + { 0, RDMA_SSIU_I_P_G3E(ssi, id, busif) }, + { 0, RDMA_SSIU_I_P_G3E(ssi, id, busif) }, + }, + }, + .ssiu = { + .capture = { + { RDMA_SSIU_O_N_G3E(ssi, id, busif), 0 }, + { RDMA_SSIU_O_P_G3E(ssi, id, busif), 0 }, + { RDMA_SSIU_O_P_G3E(ssi, id, busif), 0 }, + }, + .playback = { + { 0, RDMA_SSIU_I_N_G3E(ssi, id, busif) }, + { 0, RDMA_SSIU_I_P_G3E(ssi, id, busif) }, + { 0, RDMA_SSIU_I_P_G3E(ssi, id, busif) }, + }, + }, + }; + + return rsnd_dma_addr_lookup(io, mod, priv, &map, is_play, is_from); } /* @@ -636,6 +769,8 @@ static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io, return 0; else if (rsnd_is_gen4(priv)) return rsnd_gen4_dma_addr(io, mod, is_play, is_from); + else if (rsnd_is_rzg3e(priv)) + return rsnd_rzg3e_dma_addr(io, mod, is_play, is_from); else return rsnd_gen2_dma_addr(io, mod, is_play, is_from); } @@ -659,11 +794,11 @@ static void rsnd_dma_of_path(struct rsnd_mod *this, int nr, i, idx; /* - * It should use "rcar_sound,ssiu" on DT. - * But, we need to keep compatibility for old version. + * It should use "rcar_sound,ssiu" (R-Car) or "ssiu" (RZ/G3E) on DT. + * We need to keep compatibility for old version. * - * If it has "rcar_sound.ssiu", it will be used. - * If not, "rcar_sound.ssi" will be used. + * If it has "rcar_sound.ssiu" or "ssiu", it will be used. + * If not, "rcar_sound.ssi" or "ssi" will be used. * see * rsnd_ssiu_dma_req() * rsnd_ssi_dma_req() @@ -803,7 +938,7 @@ static int rsnd_dma_alloc(struct rsnd_dai_stream *io, struct rsnd_mod *mod, *dma_mod = rsnd_mod_get(dma); - ret = rsnd_mod_init(priv, *dma_mod, ops, NULL, + ret = rsnd_mod_init(priv, *dma_mod, ops, NULL, NULL, type, dma_id); if (ret < 0) return ret; @@ -870,6 +1005,25 @@ int rsnd_dma_probe(struct rsnd_priv *priv) return 0; /* it will be PIO mode */ } + /* + * Audio DMAC peri-peri clock and reset for RZ/G3E. + * These use optional APIs, so they gracefully return NULL + * (no error) on platforms whose DT does not provide them. + * + * Enable the clock first so the block sees a stable clock on + * the way out of reset, then deassert the reset line. + */ + dmac->audmapp_clk = devm_clk_get_optional_enabled(dev, "audmapp"); + if (IS_ERR(dmac->audmapp_clk)) + return dev_err_probe(dev, PTR_ERR(dmac->audmapp_clk), + "failed to get audmapp clock\n"); + + dmac->audmapp_rstc = + devm_reset_control_get_optional_exclusive_deasserted(dev, "audmapp"); + if (IS_ERR(dmac->audmapp_rstc)) + return dev_err_probe(dev, PTR_ERR(dmac->audmapp_rstc), + "failed to get audmapp reset\n"); + dmac->dmapp_num = 0; dmac->ppres = res->start; dmac->ppbase = devm_ioremap_resource(dev, res); @@ -879,5 +1033,27 @@ audmapp_end: priv->dma = dmac; /* dummy mem mod for debug */ - return rsnd_mod_init(NULL, &mem, &mem_ops, NULL, 0, 0); + return rsnd_mod_init(NULL, &mem, &mem_ops, NULL, NULL, 0, 0); +} + +void rsnd_dma_suspend(struct rsnd_priv *priv) +{ + struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + + if (dmac) { + /* Mirror probe (which enables clk before deasserting reset) */ + rsnd_suspend_clk_reset(NULL, dmac->audmapp_rstc); + clk_disable_unprepare(dmac->audmapp_clk); + } +} + +void rsnd_dma_resume(struct rsnd_priv *priv) +{ + struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + + if (dmac) { + /* Clock must be stable before reset is deasserted */ + clk_prepare_enable(dmac->audmapp_clk); + rsnd_resume_clk_reset(NULL, dmac->audmapp_rstc); + } } diff --git a/sound/soc/renesas/rcar/dvc.c b/sound/soc/renesas/rcar/dvc.c index 988cbddbc611..7601dfb0810a 100644 --- a/sound/soc/renesas/rcar/dvc.c +++ b/sound/soc/renesas/rcar/dvc.c @@ -29,7 +29,6 @@ #include "rsnd.h" -#define RSND_DVC_NAME_SIZE 16 #define DVC_NAME "dvc" @@ -327,7 +326,6 @@ int rsnd_dvc_probe(struct rsnd_priv *priv) struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_dvc *dvc; struct clk *clk; - char name[RSND_DVC_NAME_SIZE]; int i, nr, ret; node = rsnd_dvc_of_node(priv); @@ -354,17 +352,14 @@ int rsnd_dvc_probe(struct rsnd_priv *priv) for_each_child_of_node_scoped(node, np) { dvc = rsnd_dvc_get(priv, i); - snprintf(name, RSND_DVC_NAME_SIZE, "%s.%d", - DVC_NAME, i); - - clk = devm_clk_get(dev, name); + clk = rsnd_devm_clk_get_indexed(dev, DVC_NAME, i); if (IS_ERR(clk)) { ret = PTR_ERR(clk); goto rsnd_dvc_probe_done; } ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops, - clk, RSND_MOD_DVC, i); + clk, NULL, RSND_MOD_DVC, i); if (ret) goto rsnd_dvc_probe_done; @@ -386,3 +381,23 @@ void rsnd_dvc_remove(struct rsnd_priv *priv) rsnd_mod_quit(rsnd_mod_get(dvc)); } } + +void rsnd_dvc_suspend(struct rsnd_priv *priv) +{ + struct rsnd_dvc *dvc; + int i; + + for_each_rsnd_dvc(dvc, priv, i) + rsnd_suspend_clk_reset(rsnd_mod_get(dvc)->clk, + rsnd_mod_get(dvc)->rstc); +} + +void rsnd_dvc_resume(struct rsnd_priv *priv) +{ + struct rsnd_dvc *dvc; + int i; + + for_each_rsnd_dvc(dvc, priv, i) + rsnd_resume_clk_reset(rsnd_mod_get(dvc)->clk, + rsnd_mod_get(dvc)->rstc); +} diff --git a/sound/soc/renesas/rcar/gen.c b/sound/soc/renesas/rcar/gen.c index d1f20cde66be..05d5f656fb01 100644 --- a/sound/soc/renesas/rcar/gen.c +++ b/sound/soc/renesas/rcar/gen.c @@ -465,6 +465,184 @@ static int rsnd_gen1_probe(struct rsnd_priv *priv) } /* + * RZ/G3E Generation + */ +static int rsnd_rzg3e_probe(struct rsnd_priv *priv) +{ + static const struct rsnd_regmap_field_conf conf_ssiu[] = { + RSND_GEN_S_REG(SSI_MODE1, 0x804), + RSND_GEN_S_REG(SSI_MODE2, 0x808), + RSND_GEN_S_REG(SSI_MODE3, 0x80c), + RSND_GEN_S_REG(SSI_CONTROL, 0x810), + RSND_GEN_S_REG(SSI_CONTROL2, 0x814), + RSND_GEN_S_REG(SSI_SYS_STATUS0, 0x840), + RSND_GEN_S_REG(SSI_SYS_STATUS1, 0x844), + RSND_GEN_S_REG(SSI_SYS_STATUS2, 0x848), + RSND_GEN_S_REG(SSI_SYS_STATUS3, 0x84c), + RSND_GEN_S_REG(SSI_SYS_INT_ENABLE0, 0x850), + RSND_GEN_S_REG(SSI_SYS_INT_ENABLE1, 0x854), + RSND_GEN_S_REG(SSI_SYS_INT_ENABLE2, 0x858), + RSND_GEN_S_REG(SSI_SYS_INT_ENABLE3, 0x85c), + RSND_GEN_M_REG(SSI_BUSIF0_MODE, 0x0, 0x80), + RSND_GEN_M_REG(SSI_BUSIF0_ADINR, 0x4, 0x80), + RSND_GEN_M_REG(SSI_BUSIF0_DALIGN, 0x8, 0x80), + RSND_GEN_M_REG(SSI_BUSIF1_MODE, 0x20, 0x80), + RSND_GEN_M_REG(SSI_BUSIF1_ADINR, 0x24, 0x80), + RSND_GEN_M_REG(SSI_BUSIF1_DALIGN, 0x28, 0x80), + RSND_GEN_M_REG(SSI_BUSIF2_MODE, 0x40, 0x80), + RSND_GEN_M_REG(SSI_BUSIF2_ADINR, 0x44, 0x80), + RSND_GEN_M_REG(SSI_BUSIF2_DALIGN, 0x48, 0x80), + RSND_GEN_M_REG(SSI_BUSIF3_MODE, 0x60, 0x80), + RSND_GEN_M_REG(SSI_BUSIF3_ADINR, 0x64, 0x80), + RSND_GEN_M_REG(SSI_BUSIF3_DALIGN, 0x68, 0x80), + RSND_GEN_M_REG(SSI_MODE, 0xc, 0x80), + RSND_GEN_M_REG(SSI_CTRL, 0x10, 0x80), + RSND_GEN_M_REG(SSI_INT_ENABLE, 0x18, 0x80), + RSND_GEN_S_REG(SSI9_BUSIF0_MODE, 0x480), + RSND_GEN_S_REG(SSI9_BUSIF0_ADINR, 0x484), + RSND_GEN_S_REG(SSI9_BUSIF0_DALIGN, 0x488), + RSND_GEN_S_REG(SSI9_BUSIF1_MODE, 0x4a0), + RSND_GEN_S_REG(SSI9_BUSIF1_ADINR, 0x4a4), + RSND_GEN_S_REG(SSI9_BUSIF1_DALIGN, 0x4a8), + RSND_GEN_S_REG(SSI9_BUSIF2_MODE, 0x4c0), + RSND_GEN_S_REG(SSI9_BUSIF2_ADINR, 0x4c4), + RSND_GEN_S_REG(SSI9_BUSIF2_DALIGN, 0x4c8), + RSND_GEN_S_REG(SSI9_BUSIF3_MODE, 0x4e0), + RSND_GEN_S_REG(SSI9_BUSIF3_ADINR, 0x4e4), + RSND_GEN_S_REG(SSI9_BUSIF3_DALIGN, 0x4e8), + }; + static const struct rsnd_regmap_field_conf conf_scu[] = { + RSND_GEN_M_REG(SRC_I_BUSIF_MODE, 0x0, 0x20), + RSND_GEN_M_REG(SRC_O_BUSIF_MODE, 0x4, 0x20), + RSND_GEN_M_REG(SRC_BUSIF_DALIGN, 0x8, 0x20), + RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20), + RSND_GEN_M_REG(SRC_CTRL, 0x10, 0x20), + RSND_GEN_M_REG(SRC_INT_ENABLE0, 0x18, 0x20), + RSND_GEN_M_REG(CMD_BUSIF_MODE, 0x184, 0x20), + RSND_GEN_M_REG(CMD_BUSIF_DALIGN, 0x188, 0x20), + RSND_GEN_M_REG(CMD_ROUTE_SLCT, 0x18c, 0x20), + RSND_GEN_M_REG(CMD_CTRL, 0x190, 0x20), + RSND_GEN_S_REG(SCU_SYS_STATUS0, 0x1c8), + RSND_GEN_S_REG(SCU_SYS_INT_EN0, 0x1cc), + RSND_GEN_S_REG(SCU_SYS_STATUS1, 0x1d0), + RSND_GEN_S_REG(SCU_SYS_INT_EN1, 0x1d4), + RSND_GEN_M_REG(SRC_SWRSR, 0x200, 0x40), + RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40), + RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40), + RSND_GEN_M_REG(SRC_IFSCR, 0x21c, 0x40), + RSND_GEN_M_REG(SRC_IFSVR, 0x220, 0x40), + RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40), + RSND_GEN_M_REG(SRC_BSDSR, 0x22c, 0x40), + RSND_GEN_M_REG(SRC_BSISR, 0x238, 0x40), + RSND_GEN_M_REG(CTU_SWRSR, 0x500, 0x100), + RSND_GEN_M_REG(CTU_CTUIR, 0x504, 0x100), + RSND_GEN_M_REG(CTU_ADINR, 0x508, 0x100), + RSND_GEN_M_REG(CTU_CPMDR, 0x510, 0x100), + RSND_GEN_M_REG(CTU_SCMDR, 0x514, 0x100), + RSND_GEN_M_REG(CTU_SV00R, 0x518, 0x100), + RSND_GEN_M_REG(CTU_SV01R, 0x51c, 0x100), + RSND_GEN_M_REG(CTU_SV02R, 0x520, 0x100), + RSND_GEN_M_REG(CTU_SV03R, 0x524, 0x100), + RSND_GEN_M_REG(CTU_SV04R, 0x528, 0x100), + RSND_GEN_M_REG(CTU_SV05R, 0x52c, 0x100), + RSND_GEN_M_REG(CTU_SV06R, 0x530, 0x100), + RSND_GEN_M_REG(CTU_SV07R, 0x534, 0x100), + RSND_GEN_M_REG(CTU_SV10R, 0x538, 0x100), + RSND_GEN_M_REG(CTU_SV11R, 0x53c, 0x100), + RSND_GEN_M_REG(CTU_SV12R, 0x540, 0x100), + RSND_GEN_M_REG(CTU_SV13R, 0x544, 0x100), + RSND_GEN_M_REG(CTU_SV14R, 0x548, 0x100), + RSND_GEN_M_REG(CTU_SV15R, 0x54c, 0x100), + RSND_GEN_M_REG(CTU_SV16R, 0x550, 0x100), + RSND_GEN_M_REG(CTU_SV17R, 0x554, 0x100), + RSND_GEN_M_REG(CTU_SV20R, 0x558, 0x100), + RSND_GEN_M_REG(CTU_SV21R, 0x55c, 0x100), + RSND_GEN_M_REG(CTU_SV22R, 0x560, 0x100), + RSND_GEN_M_REG(CTU_SV23R, 0x564, 0x100), + RSND_GEN_M_REG(CTU_SV24R, 0x568, 0x100), + RSND_GEN_M_REG(CTU_SV25R, 0x56c, 0x100), + RSND_GEN_M_REG(CTU_SV26R, 0x570, 0x100), + RSND_GEN_M_REG(CTU_SV27R, 0x574, 0x100), + RSND_GEN_M_REG(CTU_SV30R, 0x578, 0x100), + RSND_GEN_M_REG(CTU_SV31R, 0x57c, 0x100), + RSND_GEN_M_REG(CTU_SV32R, 0x580, 0x100), + RSND_GEN_M_REG(CTU_SV33R, 0x584, 0x100), + RSND_GEN_M_REG(CTU_SV34R, 0x588, 0x100), + RSND_GEN_M_REG(CTU_SV35R, 0x58c, 0x100), + RSND_GEN_M_REG(CTU_SV36R, 0x590, 0x100), + RSND_GEN_M_REG(CTU_SV37R, 0x594, 0x100), + RSND_GEN_M_REG(MIX_SWRSR, 0xd00, 0x40), + RSND_GEN_M_REG(MIX_MIXIR, 0xd04, 0x40), + RSND_GEN_M_REG(MIX_ADINR, 0xd08, 0x40), + RSND_GEN_M_REG(MIX_MIXMR, 0xd10, 0x40), + RSND_GEN_M_REG(MIX_MVPDR, 0xd14, 0x40), + RSND_GEN_M_REG(MIX_MDBAR, 0xd18, 0x40), + RSND_GEN_M_REG(MIX_MDBBR, 0xd1c, 0x40), + RSND_GEN_M_REG(MIX_MDBCR, 0xd20, 0x40), + RSND_GEN_M_REG(MIX_MDBDR, 0xd24, 0x40), + RSND_GEN_M_REG(MIX_MDBER, 0xd28, 0x40), + RSND_GEN_M_REG(DVC_SWRSR, 0xe00, 0x100), + RSND_GEN_M_REG(DVC_DVUIR, 0xe04, 0x100), + RSND_GEN_M_REG(DVC_ADINR, 0xe08, 0x100), + RSND_GEN_M_REG(DVC_DVUCR, 0xe10, 0x100), + RSND_GEN_M_REG(DVC_ZCMCR, 0xe14, 0x100), + RSND_GEN_M_REG(DVC_VRCTR, 0xe18, 0x100), + RSND_GEN_M_REG(DVC_VRPDR, 0xe1c, 0x100), + RSND_GEN_M_REG(DVC_VRDBR, 0xe20, 0x100), + RSND_GEN_M_REG(DVC_VOL0R, 0xe28, 0x100), + RSND_GEN_M_REG(DVC_VOL1R, 0xe2c, 0x100), + RSND_GEN_M_REG(DVC_VOL2R, 0xe30, 0x100), + RSND_GEN_M_REG(DVC_VOL3R, 0xe34, 0x100), + RSND_GEN_M_REG(DVC_VOL4R, 0xe38, 0x100), + RSND_GEN_M_REG(DVC_VOL5R, 0xe3c, 0x100), + RSND_GEN_M_REG(DVC_VOL6R, 0xe40, 0x100), + RSND_GEN_M_REG(DVC_VOL7R, 0xe44, 0x100), + RSND_GEN_M_REG(DVC_DVUER, 0xe48, 0x100), + }; + static const struct rsnd_regmap_field_conf conf_adg[] = { + RSND_GEN_S_REG(BRRA, 0x00), + RSND_GEN_S_REG(BRRB, 0x04), + RSND_GEN_S_REG(BRGCKR, 0x08), + RSND_GEN_S_REG(AUDIO_CLK_SEL0, 0x0c), + RSND_GEN_S_REG(AUDIO_CLK_SEL1, 0x10), + RSND_GEN_S_REG(AUDIO_CLK_SEL2, 0x14), + RSND_GEN_S_REG(AUDIO_CLK_SEL3, 0x18), + RSND_GEN_S_REG(DIV_EN, 0x30), + RSND_GEN_S_REG(SRCIN_TIMSEL0, 0x34), + RSND_GEN_S_REG(SRCIN_TIMSEL1, 0x38), + RSND_GEN_S_REG(SRCIN_TIMSEL2, 0x3c), + RSND_GEN_S_REG(SRCIN_TIMSEL3, 0x40), + RSND_GEN_S_REG(SRCIN_TIMSEL4, 0x44), + RSND_GEN_S_REG(SRCOUT_TIMSEL0, 0x48), + RSND_GEN_S_REG(SRCOUT_TIMSEL1, 0x4c), + RSND_GEN_S_REG(SRCOUT_TIMSEL2, 0x50), + RSND_GEN_S_REG(SRCOUT_TIMSEL3, 0x54), + RSND_GEN_S_REG(SRCOUT_TIMSEL4, 0x58), + RSND_GEN_S_REG(CMDOUT_TIMSEL, 0x5c), + }; + static const struct rsnd_regmap_field_conf conf_ssi[] = { + RSND_GEN_M_REG(SSICR, 0x00, 0x40), + RSND_GEN_M_REG(SSISR, 0x04, 0x40), + RSND_GEN_M_REG(SSIWSR, 0x20, 0x40), + }; + int ret; + + ret = rsnd_gen_regmap_init(priv, 10, RSND_BASE_SCU, "scu", conf_scu); + if (ret < 0) + return ret; + + ret = rsnd_gen_regmap_init(priv, 1, RSND_BASE_ADG, "adg", conf_adg); + if (ret < 0) + return ret; + + ret = rsnd_gen_regmap_init(priv, 10, RSND_BASE_SSIU, "ssiu", conf_ssiu); + if (ret < 0) + return ret; + + return rsnd_gen_regmap_init(priv, 10, RSND_BASE_SSI, "ssi", conf_ssi); +} + +/* * Gen */ int rsnd_gen_probe(struct rsnd_priv *priv) @@ -487,6 +665,8 @@ int rsnd_gen_probe(struct rsnd_priv *priv) ret = rsnd_gen2_probe(priv); else if (rsnd_is_gen4(priv)) ret = rsnd_gen4_probe(priv); + else if (rsnd_is_rzg3e(priv)) + ret = rsnd_rzg3e_probe(priv); if (ret < 0) dev_err(dev, "unknown generation R-Car sound device\n"); diff --git a/sound/soc/renesas/rcar/mix.c b/sound/soc/renesas/rcar/mix.c index aea74e703305..c4da4c4bedb3 100644 --- a/sound/soc/renesas/rcar/mix.c +++ b/sound/soc/renesas/rcar/mix.c @@ -32,7 +32,6 @@ #include "rsnd.h" -#define MIX_NAME_SIZE 16 #define MIX_NAME "mix" struct rsnd_mix { @@ -291,7 +290,6 @@ int rsnd_mix_probe(struct rsnd_priv *priv) struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_mix *mix; struct clk *clk; - char name[MIX_NAME_SIZE]; int i, nr, ret; node = rsnd_mix_of_node(priv); @@ -318,17 +316,14 @@ int rsnd_mix_probe(struct rsnd_priv *priv) for_each_child_of_node_scoped(node, np) { mix = rsnd_mix_get(priv, i); - snprintf(name, MIX_NAME_SIZE, "%s.%d", - MIX_NAME, i); - - clk = devm_clk_get(dev, name); + clk = rsnd_devm_clk_get_indexed(dev, MIX_NAME, i); if (IS_ERR(clk)) { ret = PTR_ERR(clk); goto rsnd_mix_probe_done; } ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops, - clk, RSND_MOD_MIX, i); + clk, NULL, RSND_MOD_MIX, i); if (ret) goto rsnd_mix_probe_done; @@ -350,3 +345,23 @@ void rsnd_mix_remove(struct rsnd_priv *priv) rsnd_mod_quit(rsnd_mod_get(mix)); } } + +void rsnd_mix_suspend(struct rsnd_priv *priv) +{ + struct rsnd_mix *mix; + int i; + + for_each_rsnd_mix(mix, priv, i) + rsnd_suspend_clk_reset(rsnd_mod_get(mix)->clk, + rsnd_mod_get(mix)->rstc); +} + +void rsnd_mix_resume(struct rsnd_priv *priv) +{ + struct rsnd_mix *mix; + int i; + + for_each_rsnd_mix(mix, priv, i) + rsnd_resume_clk_reset(rsnd_mod_get(mix)->clk, + rsnd_mod_get(mix)->rstc); +} diff --git a/sound/soc/renesas/rcar/rsnd.h b/sound/soc/renesas/rcar/rsnd.h index 04c70690f7a2..b480085fb0e7 100644 --- a/sound/soc/renesas/rcar/rsnd.h +++ b/sound/soc/renesas/rcar/rsnd.h @@ -15,6 +15,7 @@ #include <linux/list.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/reset.h> #include <linux/sh_dma.h> #include <linux/workqueue.h> #include <sound/soc.h> @@ -142,13 +143,16 @@ enum rsnd_reg { AUDIO_CLK_SEL0, AUDIO_CLK_SEL1, AUDIO_CLK_SEL2, + AUDIO_CLK_SEL3, /* SSIU */ SSI_MODE, SSI_MODE0, SSI_MODE1, SSI_MODE2, + SSI_MODE3, SSI_CONTROL, + SSI_CONTROL2, SSI_CTRL, SSI_BUSIF0_MODE, SSI_BUSIF1_MODE, @@ -263,6 +267,8 @@ u32 rsnd_get_busif_shift(struct rsnd_dai_stream *io, struct rsnd_mod *mod); int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod, struct rsnd_mod **dma_mod); int rsnd_dma_probe(struct rsnd_priv *priv); +void rsnd_dma_suspend(struct rsnd_priv *priv); +void rsnd_dma_resume(struct rsnd_priv *priv); struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, char *name, struct rsnd_mod *mod, char *x); @@ -353,6 +359,7 @@ struct rsnd_mod { struct rsnd_mod_ops *ops; struct rsnd_priv *priv; struct clk *clk; + struct reset_control *rstc; u32 status; }; /* @@ -420,9 +427,12 @@ int rsnd_mod_init(struct rsnd_priv *priv, struct rsnd_mod *mod, struct rsnd_mod_ops *ops, struct clk *clk, + struct reset_control *rstc, enum rsnd_mod_type type, int id); void rsnd_mod_quit(struct rsnd_mod *mod); +void rsnd_suspend_clk_reset(struct clk *clk, struct reset_control *rstc); +void rsnd_resume_clk_reset(struct clk *clk, struct reset_control *rstc); struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, struct rsnd_mod *mod); void rsnd_mod_interrupt(struct rsnd_mod *mod, @@ -474,10 +484,29 @@ int rsnd_runtime_is_tdm(struct rsnd_dai_stream *io); int rsnd_runtime_is_tdm_split(struct rsnd_dai_stream *io); /* + * Indexed clock and reset name helpers. + * + * Historically the rsnd driver has looked up per-instance clocks and + * resets using dot-separated names (e.g. "ssi.0", "src.0", "adg.ssi.0"). + * Newer Renesas SoC bindings (RZ/G3E and later) use hyphen-separated + * names ("ssi-0", "src-0", ...) to follow the standard Device Tree + * naming convention. These helpers look up the hyphenated name first + * and transparently fall back to the dotted name, so a single driver + * build supports both conventions. + */ +struct clk *rsnd_devm_clk_get_indexed(struct device *dev, + const char *base, int index); +struct clk *rsnd_devm_clk_get_optional_indexed(struct device *dev, + const char *base, int index); +struct reset_control * +rsnd_devm_reset_control_get_optional_indexed(struct device *dev, + const char *base, int index); + +/* * DT */ -#define rsnd_parse_of_node(priv, node) \ - of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, node) +struct device_node *rsnd_parse_of_node(struct rsnd_priv *priv, const char *name); + #define RSND_NODE_DAI "rcar_sound,dai" #define RSND_NODE_SSI "rcar_sound,ssi" #define RSND_NODE_SSIU "rcar_sound,ssiu" @@ -600,6 +629,8 @@ int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod); int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate); int rsnd_adg_probe(struct rsnd_priv *priv); void rsnd_adg_remove(struct rsnd_priv *priv); +void rsnd_adg_suspend(struct rsnd_priv *priv); +void rsnd_adg_resume(struct rsnd_priv *priv); int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, struct rsnd_dai_stream *io, unsigned int in_rate, @@ -619,14 +650,29 @@ struct rsnd_priv { struct platform_device *pdev; spinlock_t lock; unsigned long flags; + + /* + * Flags layout: 0xDCBA + * + * A: R-Car generation (Gen1/Gen2/Gen3/Gen4) + * B: R-Car SoC variant (e.g. SOC_E for E1/E2/E3) + * C: RZ series generation + * D: RZ series SoC identifier (e.g. RZG3E) + * + * Bits 16+ are used for capability flags. + */ #define RSND_GEN_MASK (0xF << 0) #define RSND_GEN1 (1 << 0) #define RSND_GEN2 (2 << 0) #define RSND_GEN3 (3 << 0) #define RSND_GEN4 (4 << 0) -#define RSND_SOC_MASK (0xFF << 4) -#define RSND_SOC_E (1 << 4) /* E1/E2/E3 */ - +#define RSND_SOC_MASK (0xF << 4) /* nibble B */ +#define RSND_SOC_E (1 << 4) /* E1/E2/E3 */ +#define RSND_RZ_MASK (0xF << 8) /* nibble C */ +#define RSND_RZ3 (3 << 8) +#define RSND_RZ_ID_MASK (0xF << 12) /* nibble D */ +#define RSND_RZG3E (1 << 12) +#define RSND_SSIU_BUSIF_STATUS_COUNT_2 BIT(16) /* Only 2 BUSIF error-status register pairs */ /* * below value will be filled on rsnd_gen_probe() */ @@ -651,12 +697,14 @@ struct rsnd_priv { /* * below value will be filled on rsnd_ssiu_probe() */ + void *ssiu_ctrl; void *ssiu; int ssiu_nr; /* * below value will be filled on rsnd_src_probe() */ + void *src_ctrl; void *src; int src_nr; @@ -705,6 +753,9 @@ struct rsnd_priv { #define rsnd_is_gen3_e3(priv) (((priv)->flags & \ (RSND_GEN_MASK | RSND_SOC_MASK)) == \ (RSND_GEN3 | RSND_SOC_E)) +#define rsnd_is_rzg3e(priv) (((priv)->flags & \ + (RSND_RZ_MASK | RSND_RZ_ID_MASK)) == \ + (RSND_RZ3 | RSND_RZG3E)) #define rsnd_flags_has(p, f) ((p)->flags & (f)) #define rsnd_flags_set(p, f) ((p)->flags |= (f)) @@ -777,6 +828,8 @@ extern const char * const volume_ramp_rate[]; */ int rsnd_ssi_probe(struct rsnd_priv *priv); void rsnd_ssi_remove(struct rsnd_priv *priv); +void rsnd_ssi_suspend(struct rsnd_priv *priv); +void rsnd_ssi_resume(struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); u32 rsnd_ssi_multi_secondaries_runtime(struct rsnd_dai_stream *io); @@ -800,6 +853,8 @@ int rsnd_ssiu_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod); int rsnd_ssiu_probe(struct rsnd_priv *priv); void rsnd_ssiu_remove(struct rsnd_priv *priv); +void rsnd_ssiu_suspend(struct rsnd_priv *priv); +void rsnd_ssiu_resume(struct rsnd_priv *priv); void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai, struct device_node *playback, struct device_node *capture); @@ -811,6 +866,8 @@ bool rsnd_ssiu_busif_err_status_clear(struct rsnd_mod *mod); */ int rsnd_src_probe(struct rsnd_priv *priv); void rsnd_src_remove(struct rsnd_priv *priv); +void rsnd_src_suspend(struct rsnd_priv *priv); +void rsnd_src_resume(struct rsnd_priv *priv); struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id); #define rsnd_src_get_in_rate(priv, io) rsnd_src_get_rate(priv, io, 1) @@ -830,6 +887,8 @@ unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, */ int rsnd_ctu_probe(struct rsnd_priv *priv); void rsnd_ctu_remove(struct rsnd_priv *priv); +void rsnd_ctu_suspend(struct rsnd_priv *priv); +void rsnd_ctu_resume(struct rsnd_priv *priv); struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id); #define rsnd_ctu_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_CTU) #define rsnd_parse_connect_ctu(rdai, playback, capture) \ @@ -842,6 +901,8 @@ struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id); */ int rsnd_mix_probe(struct rsnd_priv *priv); void rsnd_mix_remove(struct rsnd_priv *priv); +void rsnd_mix_suspend(struct rsnd_priv *priv); +void rsnd_mix_resume(struct rsnd_priv *priv); struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id); #define rsnd_mix_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_MIX) #define rsnd_parse_connect_mix(rdai, playback, capture) \ @@ -854,6 +915,8 @@ struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id); */ int rsnd_dvc_probe(struct rsnd_priv *priv); void rsnd_dvc_remove(struct rsnd_priv *priv); +void rsnd_dvc_suspend(struct rsnd_priv *priv); +void rsnd_dvc_resume(struct rsnd_priv *priv); struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id); #define rsnd_dvc_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_DVC) #define rsnd_parse_connect_dvc(rdai, playback, capture) \ diff --git a/sound/soc/renesas/rcar/src.c b/sound/soc/renesas/rcar/src.c index 6a3dbc84f474..ac806bdc96d9 100644 --- a/sound/soc/renesas/rcar/src.c +++ b/sound/soc/renesas/rcar/src.c @@ -39,7 +39,6 @@ struct rsnd_src { int irq; }; -#define RSND_SRC_NAME_SIZE 16 #define rsnd_src_get(priv, id) ((struct rsnd_src *)(priv->src) + id) #define rsnd_src_nr(priv) ((priv)->src_nr) @@ -54,6 +53,14 @@ struct rsnd_src { ((pos) = (struct rsnd_src *)(priv)->src + i); \ i++) +struct rsnd_src_ctrl { + struct clk *scu; + struct clk *scu_x2; + struct clk *scu_supply; +}; + +#define rsnd_priv_to_src_ctrl(priv) \ + ((struct rsnd_src_ctrl *)(priv)->src_ctrl) /* * image of SRC (Sampling Rate Converter) @@ -713,9 +720,10 @@ int rsnd_src_probe(struct rsnd_priv *priv) { struct device_node *node; struct device *dev = rsnd_priv_to_dev(priv); + struct reset_control *rstc; + struct rsnd_src_ctrl *src_ctrl; struct rsnd_src *src; struct clk *clk; - char name[RSND_SRC_NAME_SIZE]; int i, nr, ret; node = rsnd_src_of_node(priv); @@ -728,6 +736,12 @@ int rsnd_src_probe(struct rsnd_priv *priv) goto rsnd_src_probe_done; } + src_ctrl = devm_kzalloc(dev, sizeof(*src_ctrl), GFP_KERNEL); + if (!src_ctrl) { + ret = -ENOMEM; + goto rsnd_src_probe_done; + } + src = devm_kcalloc(dev, nr, sizeof(*src), GFP_KERNEL); if (!src) { ret = -ENOMEM; @@ -736,6 +750,38 @@ int rsnd_src_probe(struct rsnd_priv *priv) priv->src_nr = nr; priv->src = src; + priv->src_ctrl = src_ctrl; + + src_ctrl->scu = devm_clk_get_optional_enabled(dev, "scu"); + if (IS_ERR(src_ctrl->scu)) { + ret = dev_err_probe(dev, PTR_ERR(src_ctrl->scu), + "failed to get scu clock\n"); + goto rsnd_src_probe_done; + } + + src_ctrl->scu_x2 = devm_clk_get_optional_enabled(dev, "scu_x2"); + if (IS_ERR(src_ctrl->scu_x2)) { + ret = dev_err_probe(dev, PTR_ERR(src_ctrl->scu_x2), + "failed to get scu_x2 clock\n"); + goto rsnd_src_probe_done; + } + + src_ctrl->scu_supply = devm_clk_get_optional_enabled(dev, "scu_supply"); + if (IS_ERR(src_ctrl->scu_supply)) { + ret = dev_err_probe(dev, PTR_ERR(src_ctrl->scu_supply), + "failed to get scu_supply clock\n"); + goto rsnd_src_probe_done; + } + + /* + * Shared SCU reset for every SRC module; acquire once. + * R-Car platforms typically don't have SRC reset controls. + */ + rstc = devm_reset_control_get_optional_shared(dev, "scu"); + if (IS_ERR(rstc)) { + ret = PTR_ERR(rstc); + goto rsnd_src_probe_done; + } i = 0; for_each_child_of_node_scoped(node, np) { @@ -750,23 +796,20 @@ int rsnd_src_probe(struct rsnd_priv *priv) src = rsnd_src_get(priv, i); - snprintf(name, RSND_SRC_NAME_SIZE, "%s.%d", - SRC_NAME, i); - src->irq = irq_of_parse_and_map(np, 0); if (!src->irq) { ret = -EINVAL; goto rsnd_src_probe_done; } - clk = devm_clk_get(dev, name); + clk = rsnd_devm_clk_get_indexed(dev, SRC_NAME, i); if (IS_ERR(clk)) { ret = PTR_ERR(clk); goto rsnd_src_probe_done; } ret = rsnd_mod_init(priv, rsnd_mod_get(src), - &rsnd_src_ops, clk, RSND_MOD_SRC, i); + &rsnd_src_ops, clk, rstc, RSND_MOD_SRC, i); if (ret) goto rsnd_src_probe_done; @@ -791,3 +834,37 @@ void rsnd_src_remove(struct rsnd_priv *priv) rsnd_mod_quit(rsnd_mod_get(src)); } } + +void rsnd_src_suspend(struct rsnd_priv *priv) +{ + struct rsnd_src_ctrl *src_ctrl = rsnd_priv_to_src_ctrl(priv); + struct rsnd_src *src; + int i; + + if (!src_ctrl) + return; + + for_each_rsnd_src(src, priv, i) + rsnd_suspend_clk_reset(rsnd_mod_get(src)->clk, + rsnd_mod_get(src)->rstc); + + clk_disable_unprepare(src_ctrl->scu_x2); + clk_disable_unprepare(src_ctrl->scu); +} + +void rsnd_src_resume(struct rsnd_priv *priv) +{ + struct rsnd_src_ctrl *src_ctrl = rsnd_priv_to_src_ctrl(priv); + struct rsnd_src *src; + int i; + + if (!src_ctrl) + return; + + clk_prepare_enable(src_ctrl->scu); + clk_prepare_enable(src_ctrl->scu_x2); + + for_each_rsnd_src(src, priv, i) + rsnd_resume_clk_reset(rsnd_mod_get(src)->clk, + rsnd_mod_get(src)->rstc); +} diff --git a/sound/soc/renesas/rcar/ssi.c b/sound/soc/renesas/rcar/ssi.c index 0420041e282c..2fa76a079982 100644 --- a/sound/soc/renesas/rcar/ssi.c +++ b/sound/soc/renesas/rcar/ssi.c @@ -21,7 +21,6 @@ #include <linux/of_irq.h> #include <linux/delay.h> #include "rsnd.h" -#define RSND_SSI_NAME_SIZE 16 /* * SSICR @@ -1010,11 +1009,11 @@ static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io, char *name; /* - * It should use "rcar_sound,ssiu" on DT. - * But, we need to keep compatibility for old version. + * It should use "rcar_sound,ssiu" (R-Car) or "ssiu" (RZ/G3E) on DT. + * We need to keep compatibility for old version. * - * If it has "rcar_sound.ssiu", it will be used. - * If not, "rcar_sound.ssi" will be used. + * If it has "rcar_sound.ssiu" or "ssiu", it will be used. + * If not, "rcar_sound.ssi" or "ssi" will be used. * see * rsnd_ssiu_dma_req() * rsnd_dma_of_path() @@ -1158,12 +1157,12 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) int rsnd_ssi_probe(struct rsnd_priv *priv) { + struct reset_control *rstc; struct device_node *node; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_mod_ops *ops; struct clk *clk; struct rsnd_ssi *ssi; - char name[RSND_SSI_NAME_SIZE]; int i, nr, ret; node = rsnd_ssi_of_node(priv); @@ -1198,15 +1197,23 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) ssi = rsnd_ssi_get(priv, i); - snprintf(name, RSND_SSI_NAME_SIZE, "%s.%d", - SSI_NAME, i); - - clk = devm_clk_get(dev, name); + clk = rsnd_devm_clk_get_indexed(dev, SSI_NAME, i); if (IS_ERR(clk)) { ret = PTR_ERR(clk); goto rsnd_ssi_probe_done; } + /* + * RZ/G3E uses per-SSI reset controllers. + * R-Car platforms typically don't have SSI reset controls. + */ + rstc = rsnd_devm_reset_control_get_optional_indexed(dev, + SSI_NAME, i); + if (IS_ERR(rstc)) { + ret = PTR_ERR(rstc); + goto rsnd_ssi_probe_done; + } + if (of_property_read_bool(np, "shared-pin")) rsnd_flags_set(ssi, RSND_SSI_CLK_PIN_SHARE); @@ -1225,7 +1232,7 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) ops = &rsnd_ssi_dma_ops; ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk, - RSND_MOD_SSI, i); + rstc, RSND_MOD_SSI, i); if (ret) goto rsnd_ssi_probe_done; @@ -1250,3 +1257,23 @@ void rsnd_ssi_remove(struct rsnd_priv *priv) rsnd_mod_quit(rsnd_mod_get(ssi)); } } + +void rsnd_ssi_suspend(struct rsnd_priv *priv) +{ + struct rsnd_ssi *ssi; + int i; + + for_each_rsnd_ssi(ssi, priv, i) + rsnd_suspend_clk_reset(rsnd_mod_get(ssi)->clk, + rsnd_mod_get(ssi)->rstc); +} + +void rsnd_ssi_resume(struct rsnd_priv *priv) +{ + struct rsnd_ssi *ssi; + int i; + + for_each_rsnd_ssi(ssi, priv, i) + rsnd_resume_clk_reset(rsnd_mod_get(ssi)->clk, + rsnd_mod_get(ssi)->rstc); +} diff --git a/sound/soc/renesas/rcar/ssiu.c b/sound/soc/renesas/rcar/ssiu.c index 244fb833292a..2a8593a5d4a6 100644 --- a/sound/soc/renesas/rcar/ssiu.c +++ b/sound/soc/renesas/rcar/ssiu.c @@ -29,31 +29,39 @@ struct rsnd_ssiu { i++) /* - * SSI Gen2 Gen3 Gen4 - * 0 BUSIF0-3 BUSIF0-7 BUSIF0-7 - * 1 BUSIF0-3 BUSIF0-7 - * 2 BUSIF0-3 BUSIF0-7 - * 3 BUSIF0 BUSIF0-7 - * 4 BUSIF0 BUSIF0-7 - * 5 BUSIF0 BUSIF0 - * 6 BUSIF0 BUSIF0 - * 7 BUSIF0 BUSIF0 - * 8 BUSIF0 BUSIF0 - * 9 BUSIF0-3 BUSIF0-7 - * total 22 52 8 + * SSI Gen2 Gen3 Gen4 RZ/G3E + * 0 BUSIF0-3 BUSIF0-7 BUSIF0-7 BUSIF0-3 + * 1 BUSIF0-3 BUSIF0-7 BUSIF0-3 + * 2 BUSIF0-3 BUSIF0-7 BUSIF0-3 + * 3 BUSIF0 BUSIF0-7 BUSIF0-3 + * 4 BUSIF0 BUSIF0-7 BUSIF0-3 + * 5 BUSIF0 BUSIF0 BUSIF0 + * 6 BUSIF0 BUSIF0 BUSIF0 + * 7 BUSIF0 BUSIF0 BUSIF0 + * 8 BUSIF0 BUSIF0 BUSIF0 + * 9 BUSIF0-3 BUSIF0-7 BUSIF0-3 + * total 22 52 8 28 */ static const int gen2_id[] = { 0, 4, 8, 12, 13, 14, 15, 16, 17, 18 }; static const int gen3_id[] = { 0, 8, 16, 24, 32, 40, 41, 42, 43, 44 }; static const int gen4_id[] = { 0 }; +static const int rzg3e_id[] = { 0, 4, 8, 12, 16, 20, 21, 22, 23, 24 }; + +struct rsnd_ssiu_ctrl { + unsigned int busif_status_count; +}; + +#define rsnd_priv_to_ssiu_ctrl(priv) \ + ((struct rsnd_ssiu_ctrl *)(priv)->ssiu_ctrl) /* enable busif buffer over/under run interrupt. */ #define rsnd_ssiu_busif_err_irq_enable(mod) rsnd_ssiu_busif_err_irq_ctrl(mod, 1) #define rsnd_ssiu_busif_err_irq_disable(mod) rsnd_ssiu_busif_err_irq_ctrl(mod, 0) static void rsnd_ssiu_busif_err_irq_ctrl(struct rsnd_mod *mod, int enable) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); int id = rsnd_mod_id(mod); int shift, offset; - int i; switch (id) { case 0: @@ -72,7 +80,7 @@ static void rsnd_ssiu_busif_err_irq_ctrl(struct rsnd_mod *mod, int enable) return; } - for (i = 0; i < 4; i++) { + for (unsigned int i = 0; i < rsnd_priv_to_ssiu_ctrl(priv)->busif_status_count; i++) { enum rsnd_reg reg = SSI_SYS_INT_ENABLE((i * 2) + offset); u32 val = 0xf << (shift * 4); u32 sys_int_enable = rsnd_mod_read(mod, reg); @@ -87,10 +95,10 @@ static void rsnd_ssiu_busif_err_irq_ctrl(struct rsnd_mod *mod, int enable) bool rsnd_ssiu_busif_err_status_clear(struct rsnd_mod *mod) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); bool error = false; int id = rsnd_mod_id(mod); int shift, offset; - int i; switch (id) { case 0: @@ -109,14 +117,13 @@ bool rsnd_ssiu_busif_err_status_clear(struct rsnd_mod *mod) goto out; } - for (i = 0; i < 4; i++) { + for (unsigned int i = 0; i < rsnd_priv_to_ssiu_ctrl(priv)->busif_status_count; i++) { u32 reg = SSI_SYS_STATUS(i * 2) + offset; u32 status = rsnd_mod_read(mod, reg); u32 val = 0xf << (shift * 4); status &= val; if (status) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); rsnd_print_irq_status(dev, "%s err status : 0x%08x\n", @@ -160,7 +167,8 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod, /* * SSI_MODE0 */ - rsnd_mod_bset(mod, SSI_MODE0, (1 << id), !use_busif << id); + if (!rsnd_is_rzg3e(priv)) + rsnd_mod_bset(mod, SSI_MODE0, (1 << id), !use_busif << id); /* * SSI_MODE1 / SSI_MODE2 @@ -392,11 +400,11 @@ static struct dma_chan *rsnd_ssiu_dma_req(struct rsnd_dai_stream *io, char *name; /* - * It should use "rcar_sound,ssiu" on DT. - * But, we need to keep compatibility for old version. + * It should use "rcar_sound,ssiu" (R-Car) or "ssiu" (RZ/G3E) on DT. + * We need to keep compatibility for old versions. * - * If it has "rcar_sound.ssiu", it will be used. - * If not, "rcar_sound.ssi" will be used. + * If it has "rcar_sound.ssiu" or "ssiu", it will be used. + * If not, "rcar_sound.ssi" or "ssi" will be used. * see * rsnd_ssi_dma_req() * rsnd_dma_of_path() @@ -510,6 +518,8 @@ int rsnd_ssiu_probe(struct rsnd_priv *priv) { struct device *dev = rsnd_priv_to_dev(priv); struct device_node *node __free(device_node) = rsnd_ssiu_of_node(priv); + struct reset_control *rstc; + struct rsnd_ssiu_ctrl *ctrl; struct rsnd_ssiu *ssiu; struct rsnd_mod_ops *ops; const int *list = NULL; @@ -534,8 +544,15 @@ int rsnd_ssiu_probe(struct rsnd_priv *priv) if (!ssiu) return -ENOMEM; + ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + ctrl->busif_status_count = rsnd_flags_has(priv, RSND_SSIU_BUSIF_STATUS_COUNT_2) ? 2 : 4; + priv->ssiu = ssiu; priv->ssiu_nr = nr; + priv->ssiu_ctrl = ctrl; if (rsnd_is_gen1(priv)) ops = &rsnd_ssiu_ops_gen1; @@ -558,12 +575,21 @@ int rsnd_ssiu_probe(struct rsnd_priv *priv) } else if (rsnd_is_gen4(priv)) { list = gen4_id; nr = ARRAY_SIZE(gen4_id); + } else if (rsnd_is_rzg3e(priv)) { + list = rzg3e_id; + nr = ARRAY_SIZE(rzg3e_id); } else { dev_err(dev, "unknown SSIU\n"); return -ENODEV; } } + /* Acquire shared reset once for all SSIU modules */ + rstc = devm_reset_control_get_optional_shared(dev, "ssi-all"); + if (IS_ERR(rstc)) + return dev_err_probe(dev, PTR_ERR(rstc), + "failed to get ssi-all reset\n"); + for_each_rsnd_ssiu(ssiu, priv, i) { int ret; @@ -586,7 +612,7 @@ int rsnd_ssiu_probe(struct rsnd_priv *priv) } ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu), - ops, NULL, RSND_MOD_SSIU, i); + ops, NULL, rstc, RSND_MOD_SSIU, i); if (ret) return ret; } @@ -603,3 +629,23 @@ void rsnd_ssiu_remove(struct rsnd_priv *priv) rsnd_mod_quit(rsnd_mod_get(ssiu)); } } + +void rsnd_ssiu_suspend(struct rsnd_priv *priv) +{ + struct rsnd_ssiu *ssiu; + int i; + + for_each_rsnd_ssiu(ssiu, priv, i) + rsnd_suspend_clk_reset(rsnd_mod_get(ssiu)->clk, + rsnd_mod_get(ssiu)->rstc); +} + +void rsnd_ssiu_resume(struct rsnd_priv *priv) +{ + struct rsnd_ssiu *ssiu; + int i; + + for_each_rsnd_ssiu(ssiu, priv, i) + rsnd_resume_clk_reset(rsnd_mod_get(ssiu)->clk, + rsnd_mod_get(ssiu)->rstc); +} |
