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
This commit is contained in:
Mark Brown 2026-06-01 15:30:29 +01:00
commit debea6d6bc
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
13 changed files with 1857 additions and 126 deletions

View File

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

View File

@ -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);
@ -417,6 +494,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
@ -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);
}

View File

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

View File

@ -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 = {

View File

@ -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);
}

View File

@ -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
*/
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;
/* 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)))
#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 @@ int rsnd_dma_probe(struct rsnd_priv *priv)
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);
}
}

View File

@ -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);
}

View File

@ -464,6 +464,184 @@ static int rsnd_gen1_probe(struct rsnd_priv *priv)
return ret_adg | ret_ssi;
}
/*
* 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
*/
@ -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");

View File

@ -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);
}

View File

@ -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,
@ -473,11 +483,30 @@ int rsnd_runtime_is_multi_ssi(struct rsnd_dai_stream *io);
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) \

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}