diff options
29 files changed, 3186 insertions, 528 deletions
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.txt b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.txt new file mode 100644 index 000000000000..e00732dac939 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.txt | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | * Qualcomm Technologies LPASS CPU DAI | ||
| 2 | |||
| 3 | This node models the Qualcomm Technologies Low-Power Audio SubSystem (LPASS). | ||
| 4 | |||
| 5 | Required properties: | ||
| 6 | |||
| 7 | - compatible : "qcom,lpass-cpu" | ||
| 8 | - clocks : Must contain an entry for each entry in clock-names. | ||
| 9 | - clock-names : A list which must include the following entries: | ||
| 10 | * "ahbix-clk" | ||
| 11 | * "mi2s-osr-clk" | ||
| 12 | * "mi2s-bit-clk" | ||
| 13 | - interrupts : Must contain an entry for each entry in | ||
| 14 | interrupt-names. | ||
| 15 | - interrupt-names : A list which must include the following entries: | ||
| 16 | * "lpass-irq-lpaif" | ||
| 17 | - pinctrl-N : One property must exist for each entry in | ||
| 18 | pinctrl-names. See ../pinctrl/pinctrl-bindings.txt | ||
| 19 | for details of the property values. | ||
| 20 | - pinctrl-names : Must contain a "default" entry. | ||
| 21 | - reg : Must contain an address for each entry in reg-names. | ||
| 22 | - reg-names : A list which must include the following entries: | ||
| 23 | * "lpass-lpaif" | ||
| 24 | |||
| 25 | Optional properties: | ||
| 26 | |||
| 27 | - qcom,adsp : Phandle for the audio DSP node | ||
| 28 | |||
| 29 | Example: | ||
| 30 | |||
| 31 | lpass@28100000 { | ||
| 32 | compatible = "qcom,lpass-cpu"; | ||
| 33 | clocks = <&lcc AHBIX_CLK>, <&lcc MI2S_OSR_CLK>, <&lcc MI2S_BIT_CLK>; | ||
| 34 | clock-names = "ahbix-clk", "mi2s-osr-clk", "mi2s-bit-clk"; | ||
| 35 | interrupts = <0 85 1>; | ||
| 36 | interrupt-names = "lpass-irq-lpaif"; | ||
| 37 | pinctrl-names = "default", "idle"; | ||
| 38 | pinctrl-0 = <&mi2s_default>; | ||
| 39 | pinctrl-1 = <&mi2s_idle>; | ||
| 40 | reg = <0x28100000 0x10000>; | ||
| 41 | reg-names = "lpass-lpaif"; | ||
| 42 | qcom,adsp = <&adsp>; | ||
| 43 | }; | ||
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt index 2dd690bc19cc..f316ce1f214a 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt | |||
| @@ -29,9 +29,17 @@ SSI subnode properties: | |||
| 29 | - shared-pin : if shared clock pin | 29 | - shared-pin : if shared clock pin |
| 30 | - pio-transfer : use PIO transfer mode | 30 | - pio-transfer : use PIO transfer mode |
| 31 | - no-busif : BUSIF is not ussed when [mem -> SSI] via DMA case | 31 | - no-busif : BUSIF is not ussed when [mem -> SSI] via DMA case |
| 32 | - dma : Should contain Audio DMAC entry | ||
| 33 | - dma-names : SSI case "rx" (=playback), "tx" (=capture) | ||
| 34 | SSIU case "rxu" (=playback), "txu" (=capture) | ||
| 32 | 35 | ||
| 33 | SRC subnode properties: | 36 | SRC subnode properties: |
| 34 | no properties at this point | 37 | - dma : Should contain Audio DMAC entry |
| 38 | - dma-names : "rx" (=playback), "tx" (=capture) | ||
| 39 | |||
| 40 | DVC subnode properties: | ||
| 41 | - dma : Should contain Audio DMAC entry | ||
| 42 | - dma-names : "tx" (=playback/capture) | ||
| 35 | 43 | ||
| 36 | DAI subnode properties: | 44 | DAI subnode properties: |
| 37 | - playback : list of playback modules | 45 | - playback : list of playback modules |
| @@ -45,56 +53,145 @@ rcar_sound: rcar_sound@ec500000 { | |||
| 45 | reg = <0 0xec500000 0 0x1000>, /* SCU */ | 53 | reg = <0 0xec500000 0 0x1000>, /* SCU */ |
| 46 | <0 0xec5a0000 0 0x100>, /* ADG */ | 54 | <0 0xec5a0000 0 0x100>, /* ADG */ |
| 47 | <0 0xec540000 0 0x1000>, /* SSIU */ | 55 | <0 0xec540000 0 0x1000>, /* SSIU */ |
| 48 | <0 0xec541000 0 0x1280>; /* SSI */ | 56 | <0 0xec541000 0 0x1280>, /* SSI */ |
| 57 | <0 0xec740000 0 0x200>; /* Audio DMAC peri peri*/ | ||
| 58 | reg-names = "scu", "adg", "ssiu", "ssi", "audmapp"; | ||
| 59 | |||
| 60 | clocks = <&mstp10_clks R8A7790_CLK_SSI_ALL>, | ||
| 61 | <&mstp10_clks R8A7790_CLK_SSI9>, <&mstp10_clks R8A7790_CLK_SSI8>, | ||
| 62 | <&mstp10_clks R8A7790_CLK_SSI7>, <&mstp10_clks R8A7790_CLK_SSI6>, | ||
| 63 | <&mstp10_clks R8A7790_CLK_SSI5>, <&mstp10_clks R8A7790_CLK_SSI4>, | ||
| 64 | <&mstp10_clks R8A7790_CLK_SSI3>, <&mstp10_clks R8A7790_CLK_SSI2>, | ||
| 65 | <&mstp10_clks R8A7790_CLK_SSI1>, <&mstp10_clks R8A7790_CLK_SSI0>, | ||
| 66 | <&mstp10_clks R8A7790_CLK_SCU_SRC9>, <&mstp10_clks R8A7790_CLK_SCU_SRC8>, | ||
| 67 | <&mstp10_clks R8A7790_CLK_SCU_SRC7>, <&mstp10_clks R8A7790_CLK_SCU_SRC6>, | ||
| 68 | <&mstp10_clks R8A7790_CLK_SCU_SRC5>, <&mstp10_clks R8A7790_CLK_SCU_SRC4>, | ||
| 69 | <&mstp10_clks R8A7790_CLK_SCU_SRC3>, <&mstp10_clks R8A7790_CLK_SCU_SRC2>, | ||
| 70 | <&mstp10_clks R8A7790_CLK_SCU_SRC1>, <&mstp10_clks R8A7790_CLK_SCU_SRC0>, | ||
| 71 | <&mstp10_clks R8A7790_CLK_SCU_DVC0>, <&mstp10_clks R8A7790_CLK_SCU_DVC1>, | ||
| 72 | <&audio_clk_a>, <&audio_clk_b>, <&audio_clk_c>, <&m2_clk>; | ||
| 73 | clock-names = "ssi-all", | ||
| 74 | "ssi.9", "ssi.8", "ssi.7", "ssi.6", "ssi.5", | ||
| 75 | "ssi.4", "ssi.3", "ssi.2", "ssi.1", "ssi.0", | ||
| 76 | "src.9", "src.8", "src.7", "src.6", "src.5", | ||
| 77 | "src.4", "src.3", "src.2", "src.1", "src.0", | ||
| 78 | "dvc.0", "dvc.1", | ||
| 79 | "clk_a", "clk_b", "clk_c", "clk_i"; | ||
| 49 | 80 | ||
| 50 | rcar_sound,dvc { | 81 | rcar_sound,dvc { |
| 51 | dvc0: dvc@0 { }; | 82 | dvc0: dvc@0 { |
| 52 | dvc1: dvc@1 { }; | 83 | dmas = <&audma0 0xbc>; |
| 84 | dma-names = "tx"; | ||
| 85 | }; | ||
| 86 | dvc1: dvc@1 { | ||
| 87 | dmas = <&audma0 0xbe>; | ||
| 88 | dma-names = "tx"; | ||
| 89 | }; | ||
| 53 | }; | 90 | }; |
| 54 | 91 | ||
| 55 | rcar_sound,src { | 92 | rcar_sound,src { |
| 56 | src0: src@0 { }; | 93 | src0: src@0 { |
| 57 | src1: src@1 { }; | 94 | interrupts = <0 352 IRQ_TYPE_LEVEL_HIGH>; |
| 58 | src2: src@2 { }; | 95 | dmas = <&audma0 0x85>, <&audma1 0x9a>; |
| 59 | src3: src@3 { }; | 96 | dma-names = "rx", "tx"; |
| 60 | src4: src@4 { }; | 97 | }; |
| 61 | src5: src@5 { }; | 98 | src1: src@1 { |
| 62 | src6: src@6 { }; | 99 | interrupts = <0 353 IRQ_TYPE_LEVEL_HIGH>; |
| 63 | src7: src@7 { }; | 100 | dmas = <&audma0 0x87>, <&audma1 0x9c>; |
| 64 | src8: src@8 { }; | 101 | dma-names = "rx", "tx"; |
| 65 | src9: src@9 { }; | 102 | }; |
| 103 | src2: src@2 { | ||
| 104 | interrupts = <0 354 IRQ_TYPE_LEVEL_HIGH>; | ||
| 105 | dmas = <&audma0 0x89>, <&audma1 0x9e>; | ||
| 106 | dma-names = "rx", "tx"; | ||
| 107 | }; | ||
| 108 | src3: src@3 { | ||
| 109 | interrupts = <0 355 IRQ_TYPE_LEVEL_HIGH>; | ||
| 110 | dmas = <&audma0 0x8b>, <&audma1 0xa0>; | ||
| 111 | dma-names = "rx", "tx"; | ||
| 112 | }; | ||
| 113 | src4: src@4 { | ||
| 114 | interrupts = <0 356 IRQ_TYPE_LEVEL_HIGH>; | ||
| 115 | dmas = <&audma0 0x8d>, <&audma1 0xb0>; | ||
| 116 | dma-names = "rx", "tx"; | ||
| 117 | }; | ||
| 118 | src5: src@5 { | ||
| 119 | interrupts = <0 357 IRQ_TYPE_LEVEL_HIGH>; | ||
| 120 | dmas = <&audma0 0x8f>, <&audma1 0xb2>; | ||
| 121 | dma-names = "rx", "tx"; | ||
| 122 | }; | ||
| 123 | src6: src@6 { | ||
| 124 | interrupts = <0 358 IRQ_TYPE_LEVEL_HIGH>; | ||
| 125 | dmas = <&audma0 0x91>, <&audma1 0xb4>; | ||
| 126 | dma-names = "rx", "tx"; | ||
| 127 | }; | ||
| 128 | src7: src@7 { | ||
| 129 | interrupts = <0 359 IRQ_TYPE_LEVEL_HIGH>; | ||
| 130 | dmas = <&audma0 0x93>, <&audma1 0xb6>; | ||
| 131 | dma-names = "rx", "tx"; | ||
| 132 | }; | ||
| 133 | src8: src@8 { | ||
| 134 | interrupts = <0 360 IRQ_TYPE_LEVEL_HIGH>; | ||
| 135 | dmas = <&audma0 0x95>, <&audma1 0xb8>; | ||
| 136 | dma-names = "rx", "tx"; | ||
| 137 | }; | ||
| 138 | src9: src@9 { | ||
| 139 | interrupts = <0 361 IRQ_TYPE_LEVEL_HIGH>; | ||
| 140 | dmas = <&audma0 0x97>, <&audma1 0xba>; | ||
| 141 | dma-names = "rx", "tx"; | ||
| 142 | }; | ||
| 66 | }; | 143 | }; |
| 67 | 144 | ||
| 68 | rcar_sound,ssi { | 145 | rcar_sound,ssi { |
| 69 | ssi0: ssi@0 { | 146 | ssi0: ssi@0 { |
| 70 | interrupts = <0 370 IRQ_TYPE_LEVEL_HIGH>; | 147 | interrupts = <0 370 IRQ_TYPE_LEVEL_HIGH>; |
| 148 | dmas = <&audma0 0x01>, <&audma1 0x02>, <&audma0 0x15>, <&audma1 0x16>; | ||
| 149 | dma-names = "rx", "tx", "rxu", "txu"; | ||
| 71 | }; | 150 | }; |
| 72 | ssi1: ssi@1 { | 151 | ssi1: ssi@1 { |
| 73 | interrupts = <0 371 IRQ_TYPE_LEVEL_HIGH>; | 152 | interrupts = <0 371 IRQ_TYPE_LEVEL_HIGH>; |
| 153 | dmas = <&audma0 0x03>, <&audma1 0x04>, <&audma0 0x49>, <&audma1 0x4a>; | ||
| 154 | dma-names = "rx", "tx", "rxu", "txu"; | ||
| 74 | }; | 155 | }; |
| 75 | ssi2: ssi@2 { | 156 | ssi2: ssi@2 { |
| 76 | interrupts = <0 372 IRQ_TYPE_LEVEL_HIGH>; | 157 | interrupts = <0 372 IRQ_TYPE_LEVEL_HIGH>; |
| 158 | dmas = <&audma0 0x05>, <&audma1 0x06>, <&audma0 0x63>, <&audma1 0x64>; | ||
| 159 | dma-names = "rx", "tx", "rxu", "txu"; | ||
| 77 | }; | 160 | }; |
| 78 | ssi3: ssi@3 { | 161 | ssi3: ssi@3 { |
| 79 | interrupts = <0 373 IRQ_TYPE_LEVEL_HIGH>; | 162 | interrupts = <0 373 IRQ_TYPE_LEVEL_HIGH>; |
| 163 | dmas = <&audma0 0x07>, <&audma1 0x08>, <&audma0 0x6f>, <&audma1 0x70>; | ||
| 164 | dma-names = "rx", "tx", "rxu", "txu"; | ||
| 80 | }; | 165 | }; |
| 81 | ssi4: ssi@4 { | 166 | ssi4: ssi@4 { |
| 82 | interrupts = <0 374 IRQ_TYPE_LEVEL_HIGH>; | 167 | interrupts = <0 374 IRQ_TYPE_LEVEL_HIGH>; |
| 168 | dmas = <&audma0 0x09>, <&audma1 0x0a>, <&audma0 0x71>, <&audma1 0x72>; | ||
| 169 | dma-names = "rx", "tx", "rxu", "txu"; | ||
| 83 | }; | 170 | }; |
| 84 | ssi5: ssi@5 { | 171 | ssi5: ssi@5 { |
| 85 | interrupts = <0 375 IRQ_TYPE_LEVEL_HIGH>; | 172 | interrupts = <0 375 IRQ_TYPE_LEVEL_HIGH>; |
| 173 | dmas = <&audma0 0x0b>, <&audma1 0x0c>, <&audma0 0x73>, <&audma1 0x74>; | ||
| 174 | dma-names = "rx", "tx", "rxu", "txu"; | ||
| 86 | }; | 175 | }; |
| 87 | ssi6: ssi@6 { | 176 | ssi6: ssi@6 { |
| 88 | interrupts = <0 376 IRQ_TYPE_LEVEL_HIGH>; | 177 | interrupts = <0 376 IRQ_TYPE_LEVEL_HIGH>; |
| 178 | dmas = <&audma0 0x0d>, <&audma1 0x0e>, <&audma0 0x75>, <&audma1 0x76>; | ||
| 179 | dma-names = "rx", "tx", "rxu", "txu"; | ||
| 89 | }; | 180 | }; |
| 90 | ssi7: ssi@7 { | 181 | ssi7: ssi@7 { |
| 91 | interrupts = <0 377 IRQ_TYPE_LEVEL_HIGH>; | 182 | interrupts = <0 377 IRQ_TYPE_LEVEL_HIGH>; |
| 183 | dmas = <&audma0 0x0f>, <&audma1 0x10>, <&audma0 0x79>, <&audma1 0x7a>; | ||
| 184 | dma-names = "rx", "tx", "rxu", "txu"; | ||
| 92 | }; | 185 | }; |
| 93 | ssi8: ssi@8 { | 186 | ssi8: ssi@8 { |
| 94 | interrupts = <0 378 IRQ_TYPE_LEVEL_HIGH>; | 187 | interrupts = <0 378 IRQ_TYPE_LEVEL_HIGH>; |
| 188 | dmas = <&audma0 0x11>, <&audma1 0x12>, <&audma0 0x7b>, <&audma1 0x7c>; | ||
| 189 | dma-names = "rx", "tx", "rxu", "txu"; | ||
| 95 | }; | 190 | }; |
| 96 | ssi9: ssi@9 { | 191 | ssi9: ssi@9 { |
| 97 | interrupts = <0 379 IRQ_TYPE_LEVEL_HIGH>; | 192 | interrupts = <0 379 IRQ_TYPE_LEVEL_HIGH>; |
| 193 | dmas = <&audma0 0x13>, <&audma1 0x14>, <&audma0 0x7d>, <&audma1 0x7e>; | ||
| 194 | dma-names = "rx", "tx", "rxu", "txu"; | ||
| 98 | }; | 195 | }; |
| 99 | }; | 196 | }; |
| 100 | 197 | ||
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt b/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt new file mode 100644 index 000000000000..c64155027288 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | Renesas Sampling Rate Convert Sound Card: | ||
| 2 | |||
| 3 | Renesas Sampling Rate Convert Sound Card specifies audio DAI connections of SoC <-> codec. | ||
| 4 | |||
| 5 | Required properties: | ||
| 6 | |||
| 7 | - compatible : "renesas,rsrc-card,<board>" | ||
| 8 | Examples with soctypes are: | ||
| 9 | - "renesas,rsrc-card,lager" | ||
| 10 | - "renesas,rsrc-card,koelsch" | ||
| 11 | Optional properties: | ||
| 12 | |||
| 13 | - card_name : User specified audio sound card name, one string | ||
| 14 | property. | ||
| 15 | - cpu : CPU sub-node | ||
| 16 | - codec : CODEC sub-node | ||
| 17 | |||
| 18 | Optional subnode properties: | ||
| 19 | |||
| 20 | - format : CPU/CODEC common audio format. | ||
| 21 | "i2s", "right_j", "left_j" , "dsp_a" | ||
| 22 | "dsp_b", "ac97", "pdm", "msb", "lsb" | ||
| 23 | - frame-master : Indicates dai-link frame master. | ||
| 24 | phandle to a cpu or codec subnode. | ||
| 25 | - bitclock-master : Indicates dai-link bit clock master. | ||
| 26 | phandle to a cpu or codec subnode. | ||
| 27 | - bitclock-inversion : bool property. Add this if the | ||
| 28 | dai-link uses bit clock inversion. | ||
| 29 | - frame-inversion : bool property. Add this if the | ||
| 30 | dai-link uses frame clock inversion. | ||
| 31 | - convert-rate : platform specified sampling rate convert | ||
| 32 | |||
| 33 | Required CPU/CODEC subnodes properties: | ||
| 34 | |||
| 35 | - sound-dai : phandle and port of CPU/CODEC | ||
| 36 | |||
| 37 | Optional CPU/CODEC subnodes properties: | ||
| 38 | |||
| 39 | - clocks / system-clock-frequency : specify subnode's clock if needed. | ||
| 40 | it can be specified via "clocks" if system has | ||
| 41 | clock node (= common clock), or "system-clock-frequency" | ||
| 42 | (if system doens't support common clock) | ||
| 43 | If a clock is specified, it is | ||
| 44 | enabled with clk_prepare_enable() | ||
| 45 | in dai startup() and disabled with | ||
| 46 | clk_disable_unprepare() in dai | ||
| 47 | shutdown(). | ||
| 48 | |||
| 49 | Example | ||
| 50 | |||
| 51 | sound { | ||
| 52 | compatible = "renesas,rsrc-card,lager"; | ||
| 53 | |||
| 54 | card-name = "rsnd-ak4643"; | ||
| 55 | format = "left_j"; | ||
| 56 | bitclock-master = <&sndcodec>; | ||
| 57 | frame-master = <&sndcodec>; | ||
| 58 | |||
| 59 | sndcpu: cpu { | ||
| 60 | sound-dai = <&rcar_sound>; | ||
| 61 | }; | ||
| 62 | |||
| 63 | sndcodec: codec { | ||
| 64 | sound-dai = <&ak4643>; | ||
| 65 | system-clock-frequency = <11289600>; | ||
| 66 | }; | ||
| 67 | }; | ||
diff --git a/Documentation/devicetree/bindings/sound/storm.txt b/Documentation/devicetree/bindings/sound/storm.txt new file mode 100644 index 000000000000..062a4c185fa9 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/storm.txt | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | * Sound complex for Storm boards | ||
| 2 | |||
| 3 | Models a soundcard for Storm boards with the Qualcomm Technologies IPQ806x SOC | ||
| 4 | connected to a MAX98357A DAC via I2S. | ||
| 5 | |||
| 6 | Required properties: | ||
| 7 | |||
| 8 | - compatible : "google,storm-audio" | ||
| 9 | - cpu : Phandle of the CPU DAI | ||
| 10 | - codec : Phandle of the codec DAI | ||
| 11 | |||
| 12 | Optional properties: | ||
| 13 | |||
| 14 | - qcom,model : The user-visible name of this sound card. | ||
| 15 | |||
| 16 | Example: | ||
| 17 | |||
| 18 | sound { | ||
| 19 | compatible = "google,storm-audio"; | ||
| 20 | qcom,model = "ipq806x-storm"; | ||
| 21 | cpu = <&lpass_cpu>; | ||
| 22 | codec = <&max98357a>; | ||
| 23 | }; | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 3a3c461e0787..080be6d19f78 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
| @@ -5271,6 +5271,13 @@ F: drivers/char/ipmi/ | |||
| 5271 | F: include/linux/ipmi* | 5271 | F: include/linux/ipmi* |
| 5272 | F: include/uapi/linux/ipmi* | 5272 | F: include/uapi/linux/ipmi* |
| 5273 | 5273 | ||
| 5274 | QCOM AUDIO (ASoC) DRIVERS | ||
| 5275 | M: Patrick Lai <plai@codeaurora.org> | ||
| 5276 | M: Banajit Goswami <bgoswami@codeaurora.org> | ||
| 5277 | L: alsa-devel@alsa-project.org (moderated for non-subscribers) | ||
| 5278 | S: Supported | ||
| 5279 | F: sound/soc/qcom/ | ||
| 5280 | |||
| 5274 | IPS SCSI RAID DRIVER | 5281 | IPS SCSI RAID DRIVER |
| 5275 | M: Adaptec OEM Raid Solutions <aacraid@adaptec.com> | 5282 | M: Adaptec OEM Raid Solutions <aacraid@adaptec.com> |
| 5276 | L: linux-scsi@vger.kernel.org | 5283 | L: linux-scsi@vger.kernel.org |
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index dcc79aa0236b..3ba52da18bc6 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig | |||
| @@ -47,6 +47,7 @@ source "sound/soc/kirkwood/Kconfig" | |||
| 47 | source "sound/soc/intel/Kconfig" | 47 | source "sound/soc/intel/Kconfig" |
| 48 | source "sound/soc/mxs/Kconfig" | 48 | source "sound/soc/mxs/Kconfig" |
| 49 | source "sound/soc/pxa/Kconfig" | 49 | source "sound/soc/pxa/Kconfig" |
| 50 | source "sound/soc/qcom/Kconfig" | ||
| 50 | source "sound/soc/rockchip/Kconfig" | 51 | source "sound/soc/rockchip/Kconfig" |
| 51 | source "sound/soc/samsung/Kconfig" | 52 | source "sound/soc/samsung/Kconfig" |
| 52 | source "sound/soc/sh/Kconfig" | 53 | source "sound/soc/sh/Kconfig" |
diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 5b3c8f67c8db..974ba708b482 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile | |||
| @@ -28,6 +28,7 @@ obj-$(CONFIG_SND_SOC) += nuc900/ | |||
| 28 | obj-$(CONFIG_SND_SOC) += omap/ | 28 | obj-$(CONFIG_SND_SOC) += omap/ |
| 29 | obj-$(CONFIG_SND_SOC) += kirkwood/ | 29 | obj-$(CONFIG_SND_SOC) += kirkwood/ |
| 30 | obj-$(CONFIG_SND_SOC) += pxa/ | 30 | obj-$(CONFIG_SND_SOC) += pxa/ |
| 31 | obj-$(CONFIG_SND_SOC) += qcom/ | ||
| 31 | obj-$(CONFIG_SND_SOC) += rockchip/ | 32 | obj-$(CONFIG_SND_SOC) += rockchip/ |
| 32 | obj-$(CONFIG_SND_SOC) += samsung/ | 33 | obj-$(CONFIG_SND_SOC) += samsung/ |
| 33 | obj-$(CONFIG_SND_SOC) += sh/ | 34 | obj-$(CONFIG_SND_SOC) += sh/ |
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index 826037090c83..0fcda35a3a93 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c | |||
| @@ -397,7 +397,7 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) | |||
| 397 | 397 | ||
| 398 | if (jack) { | 398 | if (jack) { |
| 399 | /* enable IRQ */ | 399 | /* enable IRQ */ |
| 400 | if (rt286->jack->status | SND_JACK_HEADPHONE) | 400 | if (rt286->jack->status & SND_JACK_HEADPHONE) |
| 401 | snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO1"); | 401 | snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO1"); |
| 402 | regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x2); | 402 | regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x2); |
| 403 | /* Send an initial empty report */ | 403 | /* Send an initial empty report */ |
| @@ -1048,7 +1048,6 @@ static int rt286_probe(struct snd_soc_codec *codec) | |||
| 1048 | struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec); | 1048 | struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec); |
| 1049 | 1049 | ||
| 1050 | rt286->codec = codec; | 1050 | rt286->codec = codec; |
| 1051 | codec->dapm.bias_level = SND_SOC_BIAS_OFF; | ||
| 1052 | 1051 | ||
| 1053 | if (rt286->i2c->irq) { | 1052 | if (rt286->i2c->irq) { |
| 1054 | regmap_update_bits(rt286->regmap, | 1053 | regmap_update_bits(rt286->regmap, |
| @@ -1220,7 +1219,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c, | |||
| 1220 | { | 1219 | { |
| 1221 | struct rt286_platform_data *pdata = dev_get_platdata(&i2c->dev); | 1220 | struct rt286_platform_data *pdata = dev_get_platdata(&i2c->dev); |
| 1222 | struct rt286_priv *rt286; | 1221 | struct rt286_priv *rt286; |
| 1223 | int i, ret; | 1222 | int i, ret, val; |
| 1224 | 1223 | ||
| 1225 | rt286 = devm_kzalloc(&i2c->dev, sizeof(*rt286), | 1224 | rt286 = devm_kzalloc(&i2c->dev, sizeof(*rt286), |
| 1226 | GFP_KERNEL); | 1225 | GFP_KERNEL); |
| @@ -1235,11 +1234,15 @@ static int rt286_i2c_probe(struct i2c_client *i2c, | |||
| 1235 | return ret; | 1234 | return ret; |
| 1236 | } | 1235 | } |
| 1237 | 1236 | ||
| 1238 | regmap_read(rt286->regmap, | 1237 | ret = regmap_read(rt286->regmap, |
| 1239 | RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &ret); | 1238 | RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &val); |
| 1240 | if (ret != RT286_VENDOR_ID && ret != RT288_VENDOR_ID) { | 1239 | if (ret != 0) { |
| 1240 | dev_err(&i2c->dev, "I2C error %d\n", ret); | ||
| 1241 | return ret; | ||
| 1242 | } | ||
| 1243 | if (val != RT286_VENDOR_ID && val != RT288_VENDOR_ID) { | ||
| 1241 | dev_err(&i2c->dev, | 1244 | dev_err(&i2c->dev, |
| 1242 | "Device with ID register %x is not rt286\n", ret); | 1245 | "Device with ID register %x is not rt286\n", val); |
| 1243 | return -ENODEV; | 1246 | return -ENODEV; |
| 1244 | } | 1247 | } |
| 1245 | 1248 | ||
| @@ -1247,6 +1250,14 @@ static int rt286_i2c_probe(struct i2c_client *i2c, | |||
| 1247 | rt286->i2c = i2c; | 1250 | rt286->i2c = i2c; |
| 1248 | i2c_set_clientdata(i2c, rt286); | 1251 | i2c_set_clientdata(i2c, rt286); |
| 1249 | 1252 | ||
| 1253 | /* restore codec default */ | ||
| 1254 | for (i = 0; i < INDEX_CACHE_SIZE; i++) | ||
| 1255 | regmap_write(rt286->regmap, rt286->index_cache[i].reg, | ||
| 1256 | rt286->index_cache[i].def); | ||
| 1257 | for (i = 0; i < ARRAY_SIZE(rt286_reg); i++) | ||
| 1258 | regmap_write(rt286->regmap, rt286_reg[i].reg, | ||
| 1259 | rt286_reg[i].def); | ||
| 1260 | |||
| 1250 | if (pdata) | 1261 | if (pdata) |
| 1251 | rt286->pdata = *pdata; | 1262 | rt286->pdata = *pdata; |
| 1252 | 1263 | ||
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index e7c78b0406b5..6768e4f7d7d0 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig | |||
| @@ -105,7 +105,7 @@ config SND_OMAP_SOC_OMAP_ABE_TWL6040 | |||
| 105 | select SND_OMAP_SOC_MCPDM | 105 | select SND_OMAP_SOC_MCPDM |
| 106 | select SND_SOC_TWL6040 | 106 | select SND_SOC_TWL6040 |
| 107 | select SND_SOC_DMIC | 107 | select SND_SOC_DMIC |
| 108 | select COMMON_CLK_PALMAS if SOC_OMAP5 | 108 | select COMMON_CLK_PALMAS if MFD_PALMAS |
| 109 | help | 109 | help |
| 110 | Say Y if you want to add support for SoC audio on OMAP boards using | 110 | Say Y if you want to add support for SoC audio on OMAP boards using |
| 111 | ABE and twl6040 codec. This driver currently supports: | 111 | ABE and twl6040 codec. This driver currently supports: |
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c index 5d7f9cebe041..dcb5336b5698 100644 --- a/sound/soc/omap/n810.c +++ b/sound/soc/omap/n810.c | |||
| @@ -98,12 +98,11 @@ static int n810_startup(struct snd_pcm_substream *substream) | |||
| 98 | { | 98 | { |
| 99 | struct snd_pcm_runtime *runtime = substream->runtime; | 99 | struct snd_pcm_runtime *runtime = substream->runtime; |
| 100 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 100 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| 101 | struct snd_soc_codec *codec = rtd->codec; | ||
| 102 | 101 | ||
| 103 | snd_pcm_hw_constraint_minmax(runtime, | 102 | snd_pcm_hw_constraint_minmax(runtime, |
| 104 | SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2); | 103 | SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2); |
| 105 | 104 | ||
| 106 | n810_ext_control(&codec->dapm); | 105 | n810_ext_control(&rtd->card->dapm); |
| 107 | return clk_prepare_enable(sys_clkout2); | 106 | return clk_prepare_enable(sys_clkout2); |
| 108 | } | 107 | } |
| 109 | 108 | ||
| @@ -255,24 +254,6 @@ static const struct snd_kcontrol_new aic33_n810_controls[] = { | |||
| 255 | n810_get_input, n810_set_input), | 254 | n810_get_input, n810_set_input), |
| 256 | }; | 255 | }; |
| 257 | 256 | ||
| 258 | static int n810_aic33_init(struct snd_soc_pcm_runtime *rtd) | ||
| 259 | { | ||
| 260 | struct snd_soc_codec *codec = rtd->codec; | ||
| 261 | struct snd_soc_dapm_context *dapm = &codec->dapm; | ||
| 262 | |||
| 263 | /* Not connected */ | ||
| 264 | snd_soc_dapm_nc_pin(dapm, "MONO_LOUT"); | ||
| 265 | snd_soc_dapm_nc_pin(dapm, "HPLCOM"); | ||
| 266 | snd_soc_dapm_nc_pin(dapm, "HPRCOM"); | ||
| 267 | snd_soc_dapm_nc_pin(dapm, "MIC3L"); | ||
| 268 | snd_soc_dapm_nc_pin(dapm, "MIC3R"); | ||
| 269 | snd_soc_dapm_nc_pin(dapm, "LINE1R"); | ||
| 270 | snd_soc_dapm_nc_pin(dapm, "LINE2L"); | ||
| 271 | snd_soc_dapm_nc_pin(dapm, "LINE2R"); | ||
| 272 | |||
| 273 | return 0; | ||
| 274 | } | ||
| 275 | |||
| 276 | /* Digital audio interface glue - connects codec <--> CPU */ | 257 | /* Digital audio interface glue - connects codec <--> CPU */ |
| 277 | static struct snd_soc_dai_link n810_dai = { | 258 | static struct snd_soc_dai_link n810_dai = { |
| 278 | .name = "TLV320AIC33", | 259 | .name = "TLV320AIC33", |
| @@ -283,7 +264,6 @@ static struct snd_soc_dai_link n810_dai = { | |||
| 283 | .codec_dai_name = "tlv320aic3x-hifi", | 264 | .codec_dai_name = "tlv320aic3x-hifi", |
| 284 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | 265 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | |
| 285 | SND_SOC_DAIFMT_CBM_CFM, | 266 | SND_SOC_DAIFMT_CBM_CFM, |
| 286 | .init = n810_aic33_init, | ||
| 287 | .ops = &n810_ops, | 267 | .ops = &n810_ops, |
| 288 | }; | 268 | }; |
| 289 | 269 | ||
| @@ -300,6 +280,7 @@ static struct snd_soc_card snd_soc_n810 = { | |||
| 300 | .num_dapm_widgets = ARRAY_SIZE(aic33_dapm_widgets), | 280 | .num_dapm_widgets = ARRAY_SIZE(aic33_dapm_widgets), |
| 301 | .dapm_routes = audio_map, | 281 | .dapm_routes = audio_map, |
| 302 | .num_dapm_routes = ARRAY_SIZE(audio_map), | 282 | .num_dapm_routes = ARRAY_SIZE(audio_map), |
| 283 | .fully_routed = true, | ||
| 303 | }; | 284 | }; |
| 304 | 285 | ||
| 305 | static struct platform_device *n810_snd_device; | 286 | static struct platform_device *n810_snd_device; |
diff --git a/sound/soc/omap/omap-hdmi-audio.c b/sound/soc/omap/omap-hdmi-audio.c index f7eb42aa3f38..4775da4c4db5 100644 --- a/sound/soc/omap/omap-hdmi-audio.c +++ b/sound/soc/omap/omap-hdmi-audio.c | |||
| @@ -142,8 +142,6 @@ static int hdmi_dai_hw_params(struct snd_pcm_substream *substream, | |||
| 142 | 142 | ||
| 143 | iec->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE; | 143 | iec->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE; |
| 144 | 144 | ||
| 145 | iec->status[0] |= IEC958_AES1_PRO_MODE_NOTID; | ||
| 146 | |||
| 147 | iec->status[1] = IEC958_AES1_CON_GENERAL; | 145 | iec->status[1] = IEC958_AES1_CON_GENERAL; |
| 148 | 146 | ||
| 149 | iec->status[2] |= IEC958_AES2_CON_SOURCE_UNSPEC; | 147 | iec->status[2] |= IEC958_AES2_CON_SOURCE_UNSPEC; |
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig new file mode 100644 index 000000000000..5f58e4f1bca9 --- /dev/null +++ b/sound/soc/qcom/Kconfig | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | config SND_SOC_QCOM | ||
| 2 | tristate "ASoC support for QCOM platforms" | ||
| 3 | help | ||
| 4 | Say Y or M if you want to add support to use audio devices | ||
| 5 | in Qualcomm Technologies SOC-based platforms. | ||
| 6 | |||
| 7 | config SND_SOC_LPASS_CPU | ||
| 8 | tristate | ||
| 9 | depends on SND_SOC_QCOM | ||
| 10 | select REGMAP_MMIO | ||
| 11 | |||
| 12 | config SND_SOC_LPASS_PLATFORM | ||
| 13 | tristate | ||
| 14 | depends on SND_SOC_QCOM | ||
| 15 | select REGMAP_MMIO | ||
| 16 | |||
| 17 | config SND_SOC_STORM | ||
| 18 | tristate "ASoC I2S support for Storm boards" | ||
| 19 | depends on (ARCH_QCOM && SND_SOC_QCOM) || COMPILE_TEST | ||
| 20 | select SND_SOC_LPASS_CPU | ||
| 21 | select SND_SOC_LPASS_PLATFORM | ||
| 22 | select SND_SOC_MAX98357A | ||
| 23 | help | ||
| 24 | Say Y or M if you want add support for SoC audio on the | ||
| 25 | Qualcomm Technologies IPQ806X-based Storm board. | ||
diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile new file mode 100644 index 000000000000..c5ce96c761c4 --- /dev/null +++ b/sound/soc/qcom/Makefile | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # Platform | ||
| 2 | snd-soc-lpass-cpu-objs := lpass-cpu.o | ||
| 3 | snd-soc-lpass-platform-objs := lpass-platform.o | ||
| 4 | |||
| 5 | obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o | ||
| 6 | obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o | ||
| 7 | |||
| 8 | # Machine | ||
| 9 | snd-soc-storm-objs := storm.o | ||
| 10 | |||
| 11 | obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o | ||
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c new file mode 100644 index 000000000000..6698d058de29 --- /dev/null +++ b/sound/soc/qcom/lpass-cpu.c | |||
| @@ -0,0 +1,491 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 and | ||
| 6 | * only version 2 as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | * | ||
| 13 | * lpass-cpu.c -- ALSA SoC CPU DAI driver for QTi LPASS | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/clk.h> | ||
| 17 | #include <linux/compiler.h> | ||
| 18 | #include <linux/device.h> | ||
| 19 | #include <linux/err.h> | ||
| 20 | #include <linux/ioport.h> | ||
| 21 | #include <linux/kernel.h> | ||
| 22 | #include <linux/mod_devicetable.h> | ||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/of.h> | ||
| 25 | #include <linux/platform_device.h> | ||
| 26 | #include <sound/pcm.h> | ||
| 27 | #include <sound/pcm_params.h> | ||
| 28 | #include <linux/regmap.h> | ||
| 29 | #include <sound/soc.h> | ||
| 30 | #include <sound/soc-dai.h> | ||
| 31 | #include "lpass-lpaif-ipq806x.h" | ||
| 32 | #include "lpass.h" | ||
| 33 | |||
| 34 | static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id, | ||
| 35 | unsigned int freq, int dir) | ||
| 36 | { | ||
| 37 | struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); | ||
| 38 | int ret; | ||
| 39 | |||
| 40 | ret = clk_set_rate(drvdata->mi2s_osr_clk, freq); | ||
| 41 | if (ret) | ||
| 42 | dev_err(dai->dev, "%s() error setting mi2s osrclk to %u: %d\n", | ||
| 43 | __func__, freq, ret); | ||
| 44 | |||
| 45 | return ret; | ||
| 46 | } | ||
| 47 | |||
| 48 | static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream, | ||
| 49 | struct snd_soc_dai *dai) | ||
| 50 | { | ||
| 51 | struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); | ||
| 52 | int ret; | ||
| 53 | |||
| 54 | ret = clk_prepare_enable(drvdata->mi2s_osr_clk); | ||
| 55 | if (ret) { | ||
| 56 | dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n", | ||
| 57 | __func__, ret); | ||
| 58 | return ret; | ||
| 59 | } | ||
| 60 | |||
| 61 | ret = clk_prepare_enable(drvdata->mi2s_bit_clk); | ||
| 62 | if (ret) { | ||
| 63 | dev_err(dai->dev, "%s() error in enabling mi2s bit clk: %d\n", | ||
| 64 | __func__, ret); | ||
| 65 | clk_disable_unprepare(drvdata->mi2s_osr_clk); | ||
| 66 | return ret; | ||
| 67 | } | ||
| 68 | |||
| 69 | return 0; | ||
| 70 | } | ||
| 71 | |||
| 72 | static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream, | ||
| 73 | struct snd_soc_dai *dai) | ||
| 74 | { | ||
| 75 | struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); | ||
| 76 | |||
| 77 | clk_disable_unprepare(drvdata->mi2s_bit_clk); | ||
| 78 | clk_disable_unprepare(drvdata->mi2s_osr_clk); | ||
| 79 | } | ||
| 80 | |||
| 81 | static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, | ||
| 82 | struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) | ||
| 83 | { | ||
| 84 | struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); | ||
| 85 | snd_pcm_format_t format = params_format(params); | ||
| 86 | unsigned int channels = params_channels(params); | ||
| 87 | unsigned int rate = params_rate(params); | ||
| 88 | unsigned int regval; | ||
| 89 | int bitwidth, ret; | ||
| 90 | |||
| 91 | bitwidth = snd_pcm_format_width(format); | ||
| 92 | if (bitwidth < 0) { | ||
| 93 | dev_err(dai->dev, "%s() invalid bit width given: %d\n", | ||
| 94 | __func__, bitwidth); | ||
| 95 | return bitwidth; | ||
| 96 | } | ||
| 97 | |||
| 98 | regval = LPAIF_I2SCTL_LOOPBACK_DISABLE | | ||
| 99 | LPAIF_I2SCTL_WSSRC_INTERNAL; | ||
| 100 | |||
| 101 | switch (bitwidth) { | ||
| 102 | case 16: | ||
| 103 | regval |= LPAIF_I2SCTL_BITWIDTH_16; | ||
| 104 | break; | ||
| 105 | case 24: | ||
| 106 | regval |= LPAIF_I2SCTL_BITWIDTH_24; | ||
| 107 | break; | ||
| 108 | case 32: | ||
| 109 | regval |= LPAIF_I2SCTL_BITWIDTH_32; | ||
| 110 | break; | ||
| 111 | default: | ||
| 112 | dev_err(dai->dev, "%s() invalid bitwidth given: %d\n", | ||
| 113 | __func__, bitwidth); | ||
| 114 | return -EINVAL; | ||
| 115 | } | ||
| 116 | |||
| 117 | switch (channels) { | ||
| 118 | case 1: | ||
| 119 | regval |= LPAIF_I2SCTL_SPKMODE_SD0; | ||
| 120 | regval |= LPAIF_I2SCTL_SPKMONO_MONO; | ||
| 121 | break; | ||
| 122 | case 2: | ||
| 123 | regval |= LPAIF_I2SCTL_SPKMODE_SD0; | ||
| 124 | regval |= LPAIF_I2SCTL_SPKMONO_STEREO; | ||
| 125 | break; | ||
| 126 | case 4: | ||
| 127 | regval |= LPAIF_I2SCTL_SPKMODE_QUAD01; | ||
| 128 | regval |= LPAIF_I2SCTL_SPKMONO_STEREO; | ||
| 129 | break; | ||
| 130 | case 6: | ||
| 131 | regval |= LPAIF_I2SCTL_SPKMODE_6CH; | ||
| 132 | regval |= LPAIF_I2SCTL_SPKMONO_STEREO; | ||
| 133 | break; | ||
| 134 | case 8: | ||
| 135 | regval |= LPAIF_I2SCTL_SPKMODE_8CH; | ||
| 136 | regval |= LPAIF_I2SCTL_SPKMONO_STEREO; | ||
| 137 | break; | ||
| 138 | default: | ||
| 139 | dev_err(dai->dev, "%s() invalid channels given: %u\n", | ||
| 140 | __func__, channels); | ||
| 141 | return -EINVAL; | ||
| 142 | } | ||
| 143 | |||
| 144 | ret = regmap_write(drvdata->lpaif_map, | ||
| 145 | LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), regval); | ||
| 146 | if (ret) { | ||
| 147 | dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", | ||
| 148 | __func__, ret); | ||
| 149 | return ret; | ||
| 150 | } | ||
| 151 | |||
| 152 | ret = clk_set_rate(drvdata->mi2s_bit_clk, rate * bitwidth * 2); | ||
| 153 | if (ret) { | ||
| 154 | dev_err(dai->dev, "%s() error setting mi2s bitclk to %u: %d\n", | ||
| 155 | __func__, rate * bitwidth * 2, ret); | ||
| 156 | return ret; | ||
| 157 | } | ||
| 158 | |||
| 159 | return 0; | ||
| 160 | } | ||
| 161 | |||
| 162 | static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream, | ||
| 163 | struct snd_soc_dai *dai) | ||
| 164 | { | ||
| 165 | struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); | ||
| 166 | int ret; | ||
| 167 | |||
| 168 | ret = regmap_write(drvdata->lpaif_map, | ||
| 169 | LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0); | ||
| 170 | if (ret) | ||
| 171 | dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", | ||
| 172 | __func__, ret); | ||
| 173 | |||
| 174 | return ret; | ||
| 175 | } | ||
| 176 | |||
| 177 | static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream, | ||
| 178 | struct snd_soc_dai *dai) | ||
| 179 | { | ||
| 180 | struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); | ||
| 181 | int ret; | ||
| 182 | |||
| 183 | ret = regmap_update_bits(drvdata->lpaif_map, | ||
| 184 | LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), | ||
| 185 | LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE); | ||
| 186 | if (ret) | ||
| 187 | dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", | ||
| 188 | __func__, ret); | ||
| 189 | |||
| 190 | return ret; | ||
| 191 | } | ||
| 192 | |||
| 193 | static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, | ||
| 194 | int cmd, struct snd_soc_dai *dai) | ||
| 195 | { | ||
| 196 | struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); | ||
| 197 | int ret; | ||
| 198 | |||
| 199 | switch (cmd) { | ||
| 200 | case SNDRV_PCM_TRIGGER_START: | ||
| 201 | case SNDRV_PCM_TRIGGER_RESUME: | ||
| 202 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
| 203 | ret = regmap_update_bits(drvdata->lpaif_map, | ||
| 204 | LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), | ||
| 205 | LPAIF_I2SCTL_SPKEN_MASK, | ||
| 206 | LPAIF_I2SCTL_SPKEN_ENABLE); | ||
| 207 | if (ret) | ||
| 208 | dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", | ||
| 209 | __func__, ret); | ||
| 210 | break; | ||
| 211 | case SNDRV_PCM_TRIGGER_STOP: | ||
| 212 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
| 213 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
| 214 | ret = regmap_update_bits(drvdata->lpaif_map, | ||
| 215 | LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), | ||
| 216 | LPAIF_I2SCTL_SPKEN_MASK, | ||
| 217 | LPAIF_I2SCTL_SPKEN_DISABLE); | ||
| 218 | if (ret) | ||
| 219 | dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", | ||
| 220 | __func__, ret); | ||
| 221 | break; | ||
| 222 | } | ||
| 223 | |||
| 224 | return ret; | ||
| 225 | } | ||
| 226 | |||
| 227 | static struct snd_soc_dai_ops lpass_cpu_dai_ops = { | ||
| 228 | .set_sysclk = lpass_cpu_daiops_set_sysclk, | ||
| 229 | .startup = lpass_cpu_daiops_startup, | ||
| 230 | .shutdown = lpass_cpu_daiops_shutdown, | ||
| 231 | .hw_params = lpass_cpu_daiops_hw_params, | ||
| 232 | .hw_free = lpass_cpu_daiops_hw_free, | ||
| 233 | .prepare = lpass_cpu_daiops_prepare, | ||
| 234 | .trigger = lpass_cpu_daiops_trigger, | ||
| 235 | }; | ||
| 236 | |||
| 237 | static int lpass_cpu_dai_probe(struct snd_soc_dai *dai) | ||
| 238 | { | ||
| 239 | struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); | ||
| 240 | int ret; | ||
| 241 | |||
| 242 | /* ensure audio hardware is disabled */ | ||
| 243 | ret = regmap_write(drvdata->lpaif_map, | ||
| 244 | LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0); | ||
| 245 | if (ret) | ||
| 246 | dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", | ||
| 247 | __func__, ret); | ||
| 248 | |||
| 249 | return ret; | ||
| 250 | } | ||
| 251 | |||
| 252 | static struct snd_soc_dai_driver lpass_cpu_dai_driver = { | ||
| 253 | .playback = { | ||
| 254 | .stream_name = "lpass-cpu-playback", | ||
| 255 | .formats = SNDRV_PCM_FMTBIT_S16 | | ||
| 256 | SNDRV_PCM_FMTBIT_S24 | | ||
| 257 | SNDRV_PCM_FMTBIT_S32, | ||
| 258 | .rates = SNDRV_PCM_RATE_8000 | | ||
| 259 | SNDRV_PCM_RATE_16000 | | ||
| 260 | SNDRV_PCM_RATE_32000 | | ||
| 261 | SNDRV_PCM_RATE_48000 | | ||
| 262 | SNDRV_PCM_RATE_96000, | ||
| 263 | .rate_min = 8000, | ||
| 264 | .rate_max = 96000, | ||
| 265 | .channels_min = 1, | ||
| 266 | .channels_max = 8, | ||
| 267 | }, | ||
| 268 | .probe = &lpass_cpu_dai_probe, | ||
| 269 | .ops = &lpass_cpu_dai_ops, | ||
| 270 | }; | ||
| 271 | |||
| 272 | static const struct snd_soc_component_driver lpass_cpu_comp_driver = { | ||
| 273 | .name = "lpass-cpu", | ||
| 274 | }; | ||
| 275 | |||
| 276 | static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg) | ||
| 277 | { | ||
| 278 | int i; | ||
| 279 | |||
| 280 | for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i) | ||
| 281 | if (reg == LPAIF_I2SCTL_REG(i)) | ||
| 282 | return true; | ||
| 283 | |||
| 284 | for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) { | ||
| 285 | if (reg == LPAIF_IRQEN_REG(i)) | ||
| 286 | return true; | ||
| 287 | if (reg == LPAIF_IRQCLEAR_REG(i)) | ||
| 288 | return true; | ||
| 289 | } | ||
| 290 | |||
| 291 | for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) { | ||
| 292 | if (reg == LPAIF_RDMACTL_REG(i)) | ||
| 293 | return true; | ||
| 294 | if (reg == LPAIF_RDMABASE_REG(i)) | ||
| 295 | return true; | ||
| 296 | if (reg == LPAIF_RDMABUFF_REG(i)) | ||
| 297 | return true; | ||
| 298 | if (reg == LPAIF_RDMAPER_REG(i)) | ||
| 299 | return true; | ||
| 300 | } | ||
| 301 | |||
| 302 | return false; | ||
| 303 | } | ||
| 304 | |||
| 305 | static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg) | ||
| 306 | { | ||
| 307 | int i; | ||
| 308 | |||
| 309 | for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i) | ||
| 310 | if (reg == LPAIF_I2SCTL_REG(i)) | ||
| 311 | return true; | ||
| 312 | |||
| 313 | for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) { | ||
| 314 | if (reg == LPAIF_IRQEN_REG(i)) | ||
| 315 | return true; | ||
| 316 | if (reg == LPAIF_IRQSTAT_REG(i)) | ||
| 317 | return true; | ||
| 318 | } | ||
| 319 | |||
| 320 | for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) { | ||
| 321 | if (reg == LPAIF_RDMACTL_REG(i)) | ||
| 322 | return true; | ||
| 323 | if (reg == LPAIF_RDMABASE_REG(i)) | ||
| 324 | return true; | ||
| 325 | if (reg == LPAIF_RDMABUFF_REG(i)) | ||
| 326 | return true; | ||
| 327 | if (reg == LPAIF_RDMACURR_REG(i)) | ||
| 328 | return true; | ||
| 329 | if (reg == LPAIF_RDMAPER_REG(i)) | ||
| 330 | return true; | ||
| 331 | } | ||
| 332 | |||
| 333 | return false; | ||
| 334 | } | ||
| 335 | |||
| 336 | static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg) | ||
| 337 | { | ||
| 338 | int i; | ||
| 339 | |||
| 340 | for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) | ||
| 341 | if (reg == LPAIF_IRQSTAT_REG(i)) | ||
| 342 | return true; | ||
| 343 | |||
| 344 | for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) | ||
| 345 | if (reg == LPAIF_RDMACURR_REG(i)) | ||
| 346 | return true; | ||
| 347 | |||
| 348 | return false; | ||
| 349 | } | ||
| 350 | |||
| 351 | static const struct regmap_config lpass_cpu_regmap_config = { | ||
| 352 | .reg_bits = 32, | ||
| 353 | .reg_stride = 4, | ||
| 354 | .val_bits = 32, | ||
| 355 | .max_register = LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MAX), | ||
| 356 | .writeable_reg = lpass_cpu_regmap_writeable, | ||
| 357 | .readable_reg = lpass_cpu_regmap_readable, | ||
| 358 | .volatile_reg = lpass_cpu_regmap_volatile, | ||
| 359 | .cache_type = REGCACHE_FLAT, | ||
| 360 | }; | ||
| 361 | |||
| 362 | static int lpass_cpu_platform_probe(struct platform_device *pdev) | ||
| 363 | { | ||
| 364 | struct lpass_data *drvdata; | ||
| 365 | struct device_node *dsp_of_node; | ||
| 366 | struct resource *res; | ||
| 367 | int ret; | ||
| 368 | |||
| 369 | dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0); | ||
| 370 | if (dsp_of_node) { | ||
| 371 | dev_err(&pdev->dev, "%s() DSP exists and holds audio resources\n", | ||
| 372 | __func__); | ||
| 373 | return -EBUSY; | ||
| 374 | } | ||
| 375 | |||
| 376 | drvdata = devm_kzalloc(&pdev->dev, sizeof(struct lpass_data), | ||
| 377 | GFP_KERNEL); | ||
| 378 | if (!drvdata) | ||
| 379 | return -ENOMEM; | ||
| 380 | platform_set_drvdata(pdev, drvdata); | ||
| 381 | |||
| 382 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif"); | ||
| 383 | if (!res) { | ||
| 384 | dev_err(&pdev->dev, "%s() error getting resource\n", __func__); | ||
| 385 | return -ENODEV; | ||
| 386 | } | ||
| 387 | |||
| 388 | drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res); | ||
| 389 | if (IS_ERR((void const __force *)drvdata->lpaif)) { | ||
| 390 | dev_err(&pdev->dev, "%s() error mapping reg resource: %ld\n", | ||
| 391 | __func__, | ||
| 392 | PTR_ERR((void const __force *)drvdata->lpaif)); | ||
| 393 | return PTR_ERR((void const __force *)drvdata->lpaif); | ||
| 394 | } | ||
| 395 | |||
| 396 | drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif, | ||
| 397 | &lpass_cpu_regmap_config); | ||
| 398 | if (IS_ERR(drvdata->lpaif_map)) { | ||
| 399 | dev_err(&pdev->dev, "%s() error initializing regmap: %ld\n", | ||
| 400 | __func__, PTR_ERR(drvdata->lpaif_map)); | ||
| 401 | return PTR_ERR(drvdata->lpaif_map); | ||
| 402 | } | ||
| 403 | |||
| 404 | drvdata->mi2s_osr_clk = devm_clk_get(&pdev->dev, "mi2s-osr-clk"); | ||
| 405 | if (IS_ERR(drvdata->mi2s_osr_clk)) { | ||
| 406 | dev_err(&pdev->dev, "%s() error getting mi2s-osr-clk: %ld\n", | ||
| 407 | __func__, PTR_ERR(drvdata->mi2s_osr_clk)); | ||
| 408 | return PTR_ERR(drvdata->mi2s_osr_clk); | ||
| 409 | } | ||
| 410 | |||
| 411 | drvdata->mi2s_bit_clk = devm_clk_get(&pdev->dev, "mi2s-bit-clk"); | ||
| 412 | if (IS_ERR(drvdata->mi2s_bit_clk)) { | ||
| 413 | dev_err(&pdev->dev, "%s() error getting mi2s-bit-clk: %ld\n", | ||
| 414 | __func__, PTR_ERR(drvdata->mi2s_bit_clk)); | ||
| 415 | return PTR_ERR(drvdata->mi2s_bit_clk); | ||
| 416 | } | ||
| 417 | |||
| 418 | drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk"); | ||
| 419 | if (IS_ERR(drvdata->ahbix_clk)) { | ||
| 420 | dev_err(&pdev->dev, "%s() error getting ahbix-clk: %ld\n", | ||
| 421 | __func__, PTR_ERR(drvdata->ahbix_clk)); | ||
| 422 | return PTR_ERR(drvdata->ahbix_clk); | ||
| 423 | } | ||
| 424 | |||
| 425 | ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY); | ||
| 426 | if (ret) { | ||
| 427 | dev_err(&pdev->dev, "%s() error setting rate on ahbix_clk: %d\n", | ||
| 428 | __func__, ret); | ||
| 429 | return ret; | ||
| 430 | } | ||
| 431 | dev_dbg(&pdev->dev, "%s() set ahbix_clk rate to %lu\n", __func__, | ||
| 432 | clk_get_rate(drvdata->ahbix_clk)); | ||
| 433 | |||
| 434 | ret = clk_prepare_enable(drvdata->ahbix_clk); | ||
| 435 | if (ret) { | ||
| 436 | dev_err(&pdev->dev, "%s() error enabling ahbix_clk: %d\n", | ||
| 437 | __func__, ret); | ||
| 438 | return ret; | ||
| 439 | } | ||
| 440 | |||
| 441 | ret = devm_snd_soc_register_component(&pdev->dev, | ||
| 442 | &lpass_cpu_comp_driver, &lpass_cpu_dai_driver, 1); | ||
| 443 | if (ret) { | ||
| 444 | dev_err(&pdev->dev, "%s() error registering cpu driver: %d\n", | ||
| 445 | __func__, ret); | ||
| 446 | goto err_clk; | ||
| 447 | } | ||
| 448 | |||
| 449 | ret = asoc_qcom_lpass_platform_register(pdev); | ||
| 450 | if (ret) { | ||
| 451 | dev_err(&pdev->dev, "%s() error registering platform driver: %d\n", | ||
| 452 | __func__, ret); | ||
| 453 | goto err_clk; | ||
| 454 | } | ||
| 455 | |||
| 456 | return 0; | ||
| 457 | |||
| 458 | err_clk: | ||
| 459 | clk_disable_unprepare(drvdata->ahbix_clk); | ||
| 460 | return ret; | ||
| 461 | } | ||
| 462 | |||
| 463 | static int lpass_cpu_platform_remove(struct platform_device *pdev) | ||
| 464 | { | ||
| 465 | struct lpass_data *drvdata = platform_get_drvdata(pdev); | ||
| 466 | |||
| 467 | clk_disable_unprepare(drvdata->ahbix_clk); | ||
| 468 | |||
| 469 | return 0; | ||
| 470 | } | ||
| 471 | |||
| 472 | #ifdef CONFIG_OF | ||
| 473 | static const struct of_device_id lpass_cpu_device_id[] = { | ||
| 474 | { .compatible = "qcom,lpass-cpu" }, | ||
| 475 | {} | ||
| 476 | }; | ||
| 477 | MODULE_DEVICE_TABLE(of, lpass_cpu_device_id); | ||
| 478 | #endif | ||
| 479 | |||
| 480 | static struct platform_driver lpass_cpu_platform_driver = { | ||
| 481 | .driver = { | ||
| 482 | .name = "lpass-cpu", | ||
| 483 | .of_match_table = of_match_ptr(lpass_cpu_device_id), | ||
| 484 | }, | ||
| 485 | .probe = lpass_cpu_platform_probe, | ||
| 486 | .remove = lpass_cpu_platform_remove, | ||
| 487 | }; | ||
| 488 | module_platform_driver(lpass_cpu_platform_driver); | ||
| 489 | |||
| 490 | MODULE_DESCRIPTION("QTi LPASS CPU Driver"); | ||
| 491 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/qcom/lpass-lpaif-ipq806x.h b/sound/soc/qcom/lpass-lpaif-ipq806x.h new file mode 100644 index 000000000000..dc423b888842 --- /dev/null +++ b/sound/soc/qcom/lpass-lpaif-ipq806x.h | |||
| @@ -0,0 +1,172 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 and | ||
| 6 | * only version 2 as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | * | ||
| 13 | * lpass-lpaif-ipq806x.h -- Definitions for the QTi LPAIF in the ipq806x LPASS | ||
| 14 | */ | ||
| 15 | |||
| 16 | #ifndef __LPASS_LPAIF_H__ | ||
| 17 | #define __LPASS_LPAIF_H__ | ||
| 18 | |||
| 19 | #define LPAIF_BANK_OFFSET 0x1000 | ||
| 20 | |||
| 21 | /* LPAIF I2S */ | ||
| 22 | |||
| 23 | #define LPAIF_I2SCTL_REG_BASE 0x0010 | ||
| 24 | #define LPAIF_I2SCTL_REG_STRIDE 0x4 | ||
| 25 | #define LPAIF_I2SCTL_REG_ADDR(addr, port) \ | ||
| 26 | (LPAIF_I2SCTL_REG_BASE + (addr) + (LPAIF_I2SCTL_REG_STRIDE * (port))) | ||
| 27 | |||
| 28 | enum lpaif_i2s_ports { | ||
| 29 | LPAIF_I2S_PORT_MIN = 0, | ||
| 30 | |||
| 31 | LPAIF_I2S_PORT_CODEC_SPK = 0, | ||
| 32 | LPAIF_I2S_PORT_CODEC_MIC = 1, | ||
| 33 | LPAIF_I2S_PORT_SEC_SPK = 2, | ||
| 34 | LPAIF_I2S_PORT_SEC_MIC = 3, | ||
| 35 | LPAIF_I2S_PORT_MI2S = 4, | ||
| 36 | |||
| 37 | LPAIF_I2S_PORT_MAX = 4, | ||
| 38 | LPAIF_I2S_PORT_NUM = 5, | ||
| 39 | }; | ||
| 40 | |||
| 41 | #define LPAIF_I2SCTL_REG(port) LPAIF_I2SCTL_REG_ADDR(0x0, (port)) | ||
| 42 | |||
| 43 | #define LPAIF_I2SCTL_LOOPBACK_MASK 0x8000 | ||
| 44 | #define LPAIF_I2SCTL_LOOPBACK_SHIFT 15 | ||
| 45 | #define LPAIF_I2SCTL_LOOPBACK_DISABLE (0 << LPAIF_I2SCTL_LOOPBACK_SHIFT) | ||
| 46 | #define LPAIF_I2SCTL_LOOPBACK_ENABLE (1 << LPAIF_I2SCTL_LOOPBACK_SHIFT) | ||
| 47 | |||
| 48 | #define LPAIF_I2SCTL_SPKEN_MASK 0x4000 | ||
| 49 | #define LPAIF_I2SCTL_SPKEN_SHIFT 14 | ||
| 50 | #define LPAIF_I2SCTL_SPKEN_DISABLE (0 << LPAIF_I2SCTL_SPKEN_SHIFT) | ||
| 51 | #define LPAIF_I2SCTL_SPKEN_ENABLE (1 << LPAIF_I2SCTL_SPKEN_SHIFT) | ||
| 52 | |||
| 53 | #define LPAIF_I2SCTL_SPKMODE_MASK 0x3C00 | ||
| 54 | #define LPAIF_I2SCTL_SPKMODE_SHIFT 10 | ||
| 55 | #define LPAIF_I2SCTL_SPKMODE_NONE (0 << LPAIF_I2SCTL_SPKMODE_SHIFT) | ||
| 56 | #define LPAIF_I2SCTL_SPKMODE_SD0 (1 << LPAIF_I2SCTL_SPKMODE_SHIFT) | ||
| 57 | #define LPAIF_I2SCTL_SPKMODE_SD1 (2 << LPAIF_I2SCTL_SPKMODE_SHIFT) | ||
| 58 | #define LPAIF_I2SCTL_SPKMODE_SD2 (3 << LPAIF_I2SCTL_SPKMODE_SHIFT) | ||
| 59 | #define LPAIF_I2SCTL_SPKMODE_SD3 (4 << LPAIF_I2SCTL_SPKMODE_SHIFT) | ||
| 60 | #define LPAIF_I2SCTL_SPKMODE_QUAD01 (5 << LPAIF_I2SCTL_SPKMODE_SHIFT) | ||
| 61 | #define LPAIF_I2SCTL_SPKMODE_QUAD23 (6 << LPAIF_I2SCTL_SPKMODE_SHIFT) | ||
| 62 | #define LPAIF_I2SCTL_SPKMODE_6CH (7 << LPAIF_I2SCTL_SPKMODE_SHIFT) | ||
| 63 | #define LPAIF_I2SCTL_SPKMODE_8CH (8 << LPAIF_I2SCTL_SPKMODE_SHIFT) | ||
| 64 | |||
| 65 | #define LPAIF_I2SCTL_SPKMONO_MASK 0x0200 | ||
| 66 | #define LPAIF_I2SCTL_SPKMONO_SHIFT 9 | ||
| 67 | #define LPAIF_I2SCTL_SPKMONO_STEREO (0 << LPAIF_I2SCTL_SPKMONO_SHIFT) | ||
| 68 | #define LPAIF_I2SCTL_SPKMONO_MONO (1 << LPAIF_I2SCTL_SPKMONO_SHIFT) | ||
| 69 | |||
| 70 | #define LPAIF_I2SCTL_WSSRC_MASK 0x0004 | ||
| 71 | #define LPAIF_I2SCTL_WSSRC_SHIFT 2 | ||
| 72 | #define LPAIF_I2SCTL_WSSRC_INTERNAL (0 << LPAIF_I2SCTL_WSSRC_SHIFT) | ||
| 73 | #define LPAIF_I2SCTL_WSSRC_EXTERNAL (1 << LPAIF_I2SCTL_WSSRC_SHIFT) | ||
| 74 | |||
| 75 | #define LPAIF_I2SCTL_BITWIDTH_MASK 0x0003 | ||
| 76 | #define LPAIF_I2SCTL_BITWIDTH_SHIFT 0 | ||
| 77 | #define LPAIF_I2SCTL_BITWIDTH_16 (0 << LPAIF_I2SCTL_BITWIDTH_SHIFT) | ||
| 78 | #define LPAIF_I2SCTL_BITWIDTH_24 (1 << LPAIF_I2SCTL_BITWIDTH_SHIFT) | ||
| 79 | #define LPAIF_I2SCTL_BITWIDTH_32 (2 << LPAIF_I2SCTL_BITWIDTH_SHIFT) | ||
| 80 | |||
| 81 | /* LPAIF IRQ */ | ||
| 82 | |||
| 83 | #define LPAIF_IRQ_REG_BASE 0x3000 | ||
| 84 | #define LPAIF_IRQ_REG_STRIDE 0x1000 | ||
| 85 | #define LPAIF_IRQ_REG_ADDR(addr, port) \ | ||
| 86 | (LPAIF_IRQ_REG_BASE + (addr) + (LPAIF_IRQ_REG_STRIDE * (port))) | ||
| 87 | |||
| 88 | enum lpaif_irq_ports { | ||
| 89 | LPAIF_IRQ_PORT_MIN = 0, | ||
| 90 | |||
| 91 | LPAIF_IRQ_PORT_HOST = 0, | ||
| 92 | LPAIF_IRQ_PORT_ADSP = 1, | ||
| 93 | |||
| 94 | LPAIF_IRQ_PORT_MAX = 2, | ||
| 95 | LPAIF_IRQ_PORT_NUM = 3, | ||
| 96 | }; | ||
| 97 | |||
| 98 | #define LPAIF_IRQEN_REG(port) LPAIF_IRQ_REG_ADDR(0x0, (port)) | ||
| 99 | #define LPAIF_IRQSTAT_REG(port) LPAIF_IRQ_REG_ADDR(0x4, (port)) | ||
| 100 | #define LPAIF_IRQCLEAR_REG(port) LPAIF_IRQ_REG_ADDR(0xC, (port)) | ||
| 101 | |||
| 102 | #define LPAIF_IRQ_BITSTRIDE 3 | ||
| 103 | #define LPAIF_IRQ_PER(chan) (1 << (LPAIF_IRQ_BITSTRIDE * (chan))) | ||
| 104 | #define LPAIF_IRQ_XRUN(chan) (2 << (LPAIF_IRQ_BITSTRIDE * (chan))) | ||
| 105 | #define LPAIF_IRQ_ERR(chan) (4 << (LPAIF_IRQ_BITSTRIDE * (chan))) | ||
| 106 | #define LPAIF_IRQ_ALL(chan) (7 << (LPAIF_IRQ_BITSTRIDE * (chan))) | ||
| 107 | |||
| 108 | /* LPAIF DMA */ | ||
| 109 | |||
| 110 | #define LPAIF_RDMA_REG_BASE 0x6000 | ||
| 111 | #define LPAIF_RDMA_REG_STRIDE 0x1000 | ||
| 112 | #define LPAIF_RDMA_REG_ADDR(addr, chan) \ | ||
| 113 | (LPAIF_RDMA_REG_BASE + (addr) + (LPAIF_RDMA_REG_STRIDE * (chan))) | ||
| 114 | |||
| 115 | enum lpaif_dma_channels { | ||
| 116 | LPAIF_RDMA_CHAN_MIN = 0, | ||
| 117 | |||
| 118 | LPAIF_RDMA_CHAN_MI2S = 0, | ||
| 119 | LPAIF_RDMA_CHAN_PCM0 = 1, | ||
| 120 | LPAIF_RDMA_CHAN_PCM1 = 2, | ||
| 121 | |||
| 122 | LPAIF_RDMA_CHAN_MAX = 4, | ||
| 123 | LPAIF_RDMA_CHAN_NUM = 5, | ||
| 124 | }; | ||
| 125 | |||
| 126 | #define LPAIF_RDMACTL_REG(chan) LPAIF_RDMA_REG_ADDR(0x00, (chan)) | ||
| 127 | #define LPAIF_RDMABASE_REG(chan) LPAIF_RDMA_REG_ADDR(0x04, (chan)) | ||
| 128 | #define LPAIF_RDMABUFF_REG(chan) LPAIF_RDMA_REG_ADDR(0x08, (chan)) | ||
| 129 | #define LPAIF_RDMACURR_REG(chan) LPAIF_RDMA_REG_ADDR(0x0C, (chan)) | ||
| 130 | #define LPAIF_RDMAPER_REG(chan) LPAIF_RDMA_REG_ADDR(0x10, (chan)) | ||
| 131 | |||
| 132 | #define LPAIF_RDMACTL_BURSTEN_MASK 0x800 | ||
| 133 | #define LPAIF_RDMACTL_BURSTEN_SHIFT 11 | ||
| 134 | #define LPAIF_RDMACTL_BURSTEN_SINGLE (0 << LPAIF_RDMACTL_BURSTEN_SHIFT) | ||
| 135 | #define LPAIF_RDMACTL_BURSTEN_INCR4 (1 << LPAIF_RDMACTL_BURSTEN_SHIFT) | ||
| 136 | |||
| 137 | #define LPAIF_RDMACTL_WPSCNT_MASK 0x700 | ||
| 138 | #define LPAIF_RDMACTL_WPSCNT_SHIFT 8 | ||
| 139 | #define LPAIF_RDMACTL_WPSCNT_ONE (0 << LPAIF_RDMACTL_WPSCNT_SHIFT) | ||
| 140 | #define LPAIF_RDMACTL_WPSCNT_TWO (1 << LPAIF_RDMACTL_WPSCNT_SHIFT) | ||
| 141 | #define LPAIF_RDMACTL_WPSCNT_THREE (2 << LPAIF_RDMACTL_WPSCNT_SHIFT) | ||
| 142 | #define LPAIF_RDMACTL_WPSCNT_FOUR (3 << LPAIF_RDMACTL_WPSCNT_SHIFT) | ||
| 143 | #define LPAIF_RDMACTL_WPSCNT_SIX (5 << LPAIF_RDMACTL_WPSCNT_SHIFT) | ||
| 144 | #define LPAIF_RDMACTL_WPSCNT_EIGHT (7 << LPAIF_RDMACTL_WPSCNT_SHIFT) | ||
| 145 | |||
| 146 | #define LPAIF_RDMACTL_AUDINTF_MASK 0x0F0 | ||
| 147 | #define LPAIF_RDMACTL_AUDINTF_SHIFT 4 | ||
| 148 | #define LPAIF_RDMACTL_AUDINTF_NONE (0 << LPAIF_RDMACTL_AUDINTF_SHIFT) | ||
| 149 | #define LPAIF_RDMACTL_AUDINTF_CODEC (1 << LPAIF_RDMACTL_AUDINTF_SHIFT) | ||
| 150 | #define LPAIF_RDMACTL_AUDINTF_PCM (2 << LPAIF_RDMACTL_AUDINTF_SHIFT) | ||
| 151 | #define LPAIF_RDMACTL_AUDINTF_SEC_I2S (3 << LPAIF_RDMACTL_AUDINTF_SHIFT) | ||
| 152 | #define LPAIF_RDMACTL_AUDINTF_MI2S (4 << LPAIF_RDMACTL_AUDINTF_SHIFT) | ||
| 153 | #define LPAIF_RDMACTL_AUDINTF_HDMI (5 << LPAIF_RDMACTL_AUDINTF_SHIFT) | ||
| 154 | #define LPAIF_RDMACTL_AUDINTF_SEC_PCM (7 << LPAIF_RDMACTL_AUDINTF_SHIFT) | ||
| 155 | |||
| 156 | #define LPAIF_RDMACTL_FIFOWM_MASK 0x00E | ||
| 157 | #define LPAIF_RDMACTL_FIFOWM_SHIFT 1 | ||
| 158 | #define LPAIF_RDMACTL_FIFOWM_1 (0 << LPAIF_RDMACTL_FIFOWM_SHIFT) | ||
| 159 | #define LPAIF_RDMACTL_FIFOWM_2 (1 << LPAIF_RDMACTL_FIFOWM_SHIFT) | ||
| 160 | #define LPAIF_RDMACTL_FIFOWM_3 (2 << LPAIF_RDMACTL_FIFOWM_SHIFT) | ||
| 161 | #define LPAIF_RDMACTL_FIFOWM_4 (3 << LPAIF_RDMACTL_FIFOWM_SHIFT) | ||
| 162 | #define LPAIF_RDMACTL_FIFOWM_5 (4 << LPAIF_RDMACTL_FIFOWM_SHIFT) | ||
| 163 | #define LPAIF_RDMACTL_FIFOWM_6 (5 << LPAIF_RDMACTL_FIFOWM_SHIFT) | ||
| 164 | #define LPAIF_RDMACTL_FIFOWM_7 (6 << LPAIF_RDMACTL_FIFOWM_SHIFT) | ||
| 165 | #define LPAIF_RDMACTL_FIFOWM_8 (7 << LPAIF_RDMACTL_FIFOWM_SHIFT) | ||
| 166 | |||
| 167 | #define LPAIF_RDMACTL_ENABLE_MASK 0x1 | ||
| 168 | #define LPAIF_RDMACTL_ENABLE_SHIFT 0 | ||
| 169 | #define LPAIF_RDMACTL_ENABLE_OFF (0 << LPAIF_RDMACTL_ENABLE_SHIFT) | ||
| 170 | #define LPAIF_RDMACTL_ENABLE_ON (1 << LPAIF_RDMACTL_ENABLE_SHIFT) | ||
| 171 | |||
| 172 | #endif /* __LPASS_LPAIF_H__ */ | ||
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c new file mode 100644 index 000000000000..2fa6280dfb23 --- /dev/null +++ b/sound/soc/qcom/lpass-platform.c | |||
| @@ -0,0 +1,526 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 and | ||
| 6 | * only version 2 as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | * | ||
| 13 | * lpass-platform.c -- ALSA SoC platform driver for QTi LPASS | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/compiler.h> | ||
| 17 | #include <linux/device.h> | ||
| 18 | #include <linux/dma-mapping.h> | ||
| 19 | #include <linux/err.h> | ||
| 20 | #include <linux/export.h> | ||
| 21 | #include <linux/kernel.h> | ||
| 22 | #include <linux/module.h> | ||
| 23 | #include <linux/io.h> | ||
| 24 | #include <linux/platform_device.h> | ||
| 25 | #include <sound/memalloc.h> | ||
| 26 | #include <sound/pcm.h> | ||
| 27 | #include <sound/pcm_params.h> | ||
| 28 | #include <linux/regmap.h> | ||
| 29 | #include <sound/soc.h> | ||
| 30 | #include "lpass-lpaif-ipq806x.h" | ||
| 31 | #include "lpass.h" | ||
| 32 | |||
| 33 | #define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024) | ||
| 34 | #define LPASS_PLATFORM_PERIODS 2 | ||
| 35 | |||
| 36 | static struct snd_pcm_hardware lpass_platform_pcm_hardware = { | ||
| 37 | .info = SNDRV_PCM_INFO_MMAP | | ||
| 38 | SNDRV_PCM_INFO_MMAP_VALID | | ||
| 39 | SNDRV_PCM_INFO_INTERLEAVED | | ||
| 40 | SNDRV_PCM_INFO_PAUSE | | ||
| 41 | SNDRV_PCM_INFO_RESUME, | ||
| 42 | .formats = SNDRV_PCM_FMTBIT_S16 | | ||
| 43 | SNDRV_PCM_FMTBIT_S24 | | ||
| 44 | SNDRV_PCM_FMTBIT_S32, | ||
| 45 | .rates = SNDRV_PCM_RATE_8000_192000, | ||
| 46 | .rate_min = 8000, | ||
| 47 | .rate_max = 192000, | ||
| 48 | .channels_min = 1, | ||
| 49 | .channels_max = 8, | ||
| 50 | .buffer_bytes_max = LPASS_PLATFORM_BUFFER_SIZE, | ||
| 51 | .period_bytes_max = LPASS_PLATFORM_BUFFER_SIZE / | ||
| 52 | LPASS_PLATFORM_PERIODS, | ||
| 53 | .period_bytes_min = LPASS_PLATFORM_BUFFER_SIZE / | ||
| 54 | LPASS_PLATFORM_PERIODS, | ||
| 55 | .periods_min = LPASS_PLATFORM_PERIODS, | ||
| 56 | .periods_max = LPASS_PLATFORM_PERIODS, | ||
| 57 | .fifo_size = 0, | ||
| 58 | }; | ||
| 59 | |||
| 60 | static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream) | ||
| 61 | { | ||
| 62 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 63 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
| 64 | int ret; | ||
| 65 | |||
| 66 | snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware); | ||
| 67 | |||
| 68 | runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max; | ||
| 69 | |||
| 70 | ret = snd_pcm_hw_constraint_integer(runtime, | ||
| 71 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
| 72 | if (ret < 0) { | ||
| 73 | dev_err(soc_runtime->dev, "%s() setting constraints failed: %d\n", | ||
| 74 | __func__, ret); | ||
| 75 | return -EINVAL; | ||
| 76 | } | ||
| 77 | |||
| 78 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | ||
| 79 | |||
| 80 | return 0; | ||
| 81 | } | ||
| 82 | |||
| 83 | static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, | ||
| 84 | struct snd_pcm_hw_params *params) | ||
| 85 | { | ||
| 86 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
| 87 | struct lpass_data *drvdata = | ||
| 88 | snd_soc_platform_get_drvdata(soc_runtime->platform); | ||
| 89 | snd_pcm_format_t format = params_format(params); | ||
| 90 | unsigned int channels = params_channels(params); | ||
| 91 | unsigned int regval; | ||
| 92 | int bitwidth; | ||
| 93 | int ret; | ||
| 94 | |||
| 95 | bitwidth = snd_pcm_format_width(format); | ||
| 96 | if (bitwidth < 0) { | ||
| 97 | dev_err(soc_runtime->dev, "%s() invalid bit width given: %d\n", | ||
| 98 | __func__, bitwidth); | ||
| 99 | return bitwidth; | ||
| 100 | } | ||
| 101 | |||
| 102 | regval = LPAIF_RDMACTL_BURSTEN_INCR4 | | ||
| 103 | LPAIF_RDMACTL_AUDINTF_MI2S | | ||
| 104 | LPAIF_RDMACTL_FIFOWM_8; | ||
| 105 | |||
| 106 | switch (bitwidth) { | ||
| 107 | case 16: | ||
| 108 | switch (channels) { | ||
| 109 | case 1: | ||
| 110 | case 2: | ||
| 111 | regval |= LPAIF_RDMACTL_WPSCNT_ONE; | ||
| 112 | break; | ||
| 113 | case 4: | ||
| 114 | regval |= LPAIF_RDMACTL_WPSCNT_TWO; | ||
| 115 | break; | ||
| 116 | case 6: | ||
| 117 | regval |= LPAIF_RDMACTL_WPSCNT_THREE; | ||
| 118 | break; | ||
| 119 | case 8: | ||
| 120 | regval |= LPAIF_RDMACTL_WPSCNT_FOUR; | ||
| 121 | break; | ||
| 122 | default: | ||
| 123 | dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", | ||
| 124 | __func__, bitwidth, channels); | ||
| 125 | return -EINVAL; | ||
| 126 | } | ||
| 127 | break; | ||
| 128 | case 24: | ||
| 129 | case 32: | ||
| 130 | switch (channels) { | ||
| 131 | case 1: | ||
| 132 | regval |= LPAIF_RDMACTL_WPSCNT_ONE; | ||
| 133 | break; | ||
| 134 | case 2: | ||
| 135 | regval |= LPAIF_RDMACTL_WPSCNT_TWO; | ||
| 136 | break; | ||
| 137 | case 4: | ||
| 138 | regval |= LPAIF_RDMACTL_WPSCNT_FOUR; | ||
| 139 | break; | ||
| 140 | case 6: | ||
| 141 | regval |= LPAIF_RDMACTL_WPSCNT_SIX; | ||
| 142 | break; | ||
| 143 | case 8: | ||
| 144 | regval |= LPAIF_RDMACTL_WPSCNT_EIGHT; | ||
| 145 | break; | ||
| 146 | default: | ||
| 147 | dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", | ||
| 148 | __func__, bitwidth, channels); | ||
| 149 | return -EINVAL; | ||
| 150 | } | ||
| 151 | break; | ||
| 152 | default: | ||
| 153 | dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", | ||
| 154 | __func__, bitwidth, channels); | ||
| 155 | return -EINVAL; | ||
| 156 | } | ||
| 157 | |||
| 158 | ret = regmap_write(drvdata->lpaif_map, | ||
| 159 | LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), regval); | ||
| 160 | if (ret) { | ||
| 161 | dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", | ||
| 162 | __func__, ret); | ||
| 163 | return ret; | ||
| 164 | } | ||
| 165 | |||
| 166 | return 0; | ||
| 167 | } | ||
| 168 | |||
| 169 | static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream) | ||
| 170 | { | ||
| 171 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
| 172 | struct lpass_data *drvdata = | ||
| 173 | snd_soc_platform_get_drvdata(soc_runtime->platform); | ||
| 174 | int ret; | ||
| 175 | |||
| 176 | ret = regmap_write(drvdata->lpaif_map, | ||
| 177 | LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0); | ||
| 178 | if (ret) | ||
| 179 | dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", | ||
| 180 | __func__, ret); | ||
| 181 | |||
| 182 | return ret; | ||
| 183 | } | ||
| 184 | |||
| 185 | static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) | ||
| 186 | { | ||
| 187 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 188 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
| 189 | struct lpass_data *drvdata = | ||
| 190 | snd_soc_platform_get_drvdata(soc_runtime->platform); | ||
| 191 | int ret; | ||
| 192 | |||
| 193 | ret = regmap_write(drvdata->lpaif_map, | ||
| 194 | LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S), | ||
| 195 | runtime->dma_addr); | ||
| 196 | if (ret) { | ||
| 197 | dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n", | ||
| 198 | __func__, ret); | ||
| 199 | return ret; | ||
| 200 | } | ||
| 201 | |||
| 202 | ret = regmap_write(drvdata->lpaif_map, | ||
| 203 | LPAIF_RDMABUFF_REG(LPAIF_RDMA_CHAN_MI2S), | ||
| 204 | (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1); | ||
| 205 | if (ret) { | ||
| 206 | dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n", | ||
| 207 | __func__, ret); | ||
| 208 | return ret; | ||
| 209 | } | ||
| 210 | |||
| 211 | ret = regmap_write(drvdata->lpaif_map, | ||
| 212 | LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MI2S), | ||
| 213 | (snd_pcm_lib_period_bytes(substream) >> 2) - 1); | ||
| 214 | if (ret) { | ||
| 215 | dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n", | ||
| 216 | __func__, ret); | ||
| 217 | return ret; | ||
| 218 | } | ||
| 219 | |||
| 220 | ret = regmap_update_bits(drvdata->lpaif_map, | ||
| 221 | LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), | ||
| 222 | LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON); | ||
| 223 | if (ret) { | ||
| 224 | dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", | ||
| 225 | __func__, ret); | ||
| 226 | return ret; | ||
| 227 | } | ||
| 228 | |||
| 229 | return 0; | ||
| 230 | } | ||
| 231 | |||
| 232 | static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, | ||
| 233 | int cmd) | ||
| 234 | { | ||
| 235 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
| 236 | struct lpass_data *drvdata = | ||
| 237 | snd_soc_platform_get_drvdata(soc_runtime->platform); | ||
| 238 | int ret; | ||
| 239 | |||
| 240 | switch (cmd) { | ||
| 241 | case SNDRV_PCM_TRIGGER_START: | ||
| 242 | case SNDRV_PCM_TRIGGER_RESUME: | ||
| 243 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
| 244 | /* clear status before enabling interrupts */ | ||
| 245 | ret = regmap_write(drvdata->lpaif_map, | ||
| 246 | LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST), | ||
| 247 | LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S)); | ||
| 248 | if (ret) { | ||
| 249 | dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", | ||
| 250 | __func__, ret); | ||
| 251 | return ret; | ||
| 252 | } | ||
| 253 | |||
| 254 | ret = regmap_update_bits(drvdata->lpaif_map, | ||
| 255 | LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), | ||
| 256 | LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S), | ||
| 257 | LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S)); | ||
| 258 | if (ret) { | ||
| 259 | dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n", | ||
| 260 | __func__, ret); | ||
| 261 | return ret; | ||
| 262 | } | ||
| 263 | |||
| 264 | ret = regmap_update_bits(drvdata->lpaif_map, | ||
| 265 | LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), | ||
| 266 | LPAIF_RDMACTL_ENABLE_MASK, | ||
| 267 | LPAIF_RDMACTL_ENABLE_ON); | ||
| 268 | if (ret) { | ||
| 269 | dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", | ||
| 270 | __func__, ret); | ||
| 271 | return ret; | ||
| 272 | } | ||
| 273 | break; | ||
| 274 | case SNDRV_PCM_TRIGGER_STOP: | ||
| 275 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
| 276 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
| 277 | ret = regmap_update_bits(drvdata->lpaif_map, | ||
| 278 | LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), | ||
| 279 | LPAIF_RDMACTL_ENABLE_MASK, | ||
| 280 | LPAIF_RDMACTL_ENABLE_OFF); | ||
| 281 | if (ret) { | ||
| 282 | dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", | ||
| 283 | __func__, ret); | ||
| 284 | return ret; | ||
| 285 | } | ||
| 286 | |||
| 287 | ret = regmap_update_bits(drvdata->lpaif_map, | ||
| 288 | LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), | ||
| 289 | LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S), 0); | ||
| 290 | if (ret) { | ||
| 291 | dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n", | ||
| 292 | __func__, ret); | ||
| 293 | return ret; | ||
| 294 | } | ||
| 295 | break; | ||
| 296 | } | ||
| 297 | |||
| 298 | return 0; | ||
| 299 | } | ||
| 300 | |||
| 301 | static snd_pcm_uframes_t lpass_platform_pcmops_pointer( | ||
| 302 | struct snd_pcm_substream *substream) | ||
| 303 | { | ||
| 304 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
| 305 | struct lpass_data *drvdata = | ||
| 306 | snd_soc_platform_get_drvdata(soc_runtime->platform); | ||
| 307 | unsigned int base_addr, curr_addr; | ||
| 308 | int ret; | ||
| 309 | |||
| 310 | ret = regmap_read(drvdata->lpaif_map, | ||
| 311 | LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S), &base_addr); | ||
| 312 | if (ret) { | ||
| 313 | dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n", | ||
| 314 | __func__, ret); | ||
| 315 | return ret; | ||
| 316 | } | ||
| 317 | |||
| 318 | ret = regmap_read(drvdata->lpaif_map, | ||
| 319 | LPAIF_RDMACURR_REG(LPAIF_RDMA_CHAN_MI2S), &curr_addr); | ||
| 320 | if (ret) { | ||
| 321 | dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n", | ||
| 322 | __func__, ret); | ||
| 323 | return ret; | ||
| 324 | } | ||
| 325 | |||
| 326 | return bytes_to_frames(substream->runtime, curr_addr - base_addr); | ||
| 327 | } | ||
| 328 | |||
| 329 | static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream, | ||
| 330 | struct vm_area_struct *vma) | ||
| 331 | { | ||
| 332 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 333 | |||
| 334 | return dma_mmap_coherent(substream->pcm->card->dev, vma, | ||
| 335 | runtime->dma_area, runtime->dma_addr, | ||
| 336 | runtime->dma_bytes); | ||
| 337 | } | ||
| 338 | |||
| 339 | static struct snd_pcm_ops lpass_platform_pcm_ops = { | ||
| 340 | .open = lpass_platform_pcmops_open, | ||
| 341 | .ioctl = snd_pcm_lib_ioctl, | ||
| 342 | .hw_params = lpass_platform_pcmops_hw_params, | ||
| 343 | .hw_free = lpass_platform_pcmops_hw_free, | ||
| 344 | .prepare = lpass_platform_pcmops_prepare, | ||
| 345 | .trigger = lpass_platform_pcmops_trigger, | ||
| 346 | .pointer = lpass_platform_pcmops_pointer, | ||
| 347 | .mmap = lpass_platform_pcmops_mmap, | ||
| 348 | }; | ||
| 349 | |||
| 350 | static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data) | ||
| 351 | { | ||
| 352 | struct snd_pcm_substream *substream = data; | ||
| 353 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
| 354 | struct lpass_data *drvdata = | ||
| 355 | snd_soc_platform_get_drvdata(soc_runtime->platform); | ||
| 356 | unsigned int interrupts; | ||
| 357 | irqreturn_t ret = IRQ_NONE; | ||
| 358 | int rv; | ||
| 359 | |||
| 360 | rv = regmap_read(drvdata->lpaif_map, | ||
| 361 | LPAIF_IRQSTAT_REG(LPAIF_IRQ_PORT_HOST), &interrupts); | ||
| 362 | if (rv) { | ||
| 363 | dev_err(soc_runtime->dev, "%s() error reading from irqstat reg: %d\n", | ||
| 364 | __func__, rv); | ||
| 365 | return IRQ_NONE; | ||
| 366 | } | ||
| 367 | interrupts &= LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S); | ||
| 368 | |||
| 369 | if (interrupts & LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S)) { | ||
| 370 | rv = regmap_write(drvdata->lpaif_map, | ||
| 371 | LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST), | ||
| 372 | LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S)); | ||
| 373 | if (rv) { | ||
| 374 | dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", | ||
| 375 | __func__, rv); | ||
| 376 | return IRQ_NONE; | ||
| 377 | } | ||
| 378 | snd_pcm_period_elapsed(substream); | ||
| 379 | ret = IRQ_HANDLED; | ||
| 380 | } | ||
| 381 | |||
| 382 | if (interrupts & LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S)) { | ||
| 383 | rv = regmap_write(drvdata->lpaif_map, | ||
| 384 | LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST), | ||
| 385 | LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S)); | ||
| 386 | if (rv) { | ||
| 387 | dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", | ||
| 388 | __func__, rv); | ||
| 389 | return IRQ_NONE; | ||
| 390 | } | ||
| 391 | dev_warn(soc_runtime->dev, "%s() xrun warning\n", __func__); | ||
| 392 | snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); | ||
| 393 | ret = IRQ_HANDLED; | ||
| 394 | } | ||
| 395 | |||
| 396 | if (interrupts & LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S)) { | ||
| 397 | rv = regmap_write(drvdata->lpaif_map, | ||
| 398 | LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST), | ||
| 399 | LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S)); | ||
| 400 | if (rv) { | ||
| 401 | dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", | ||
| 402 | __func__, rv); | ||
| 403 | return IRQ_NONE; | ||
| 404 | } | ||
| 405 | dev_err(soc_runtime->dev, "%s() bus access error\n", __func__); | ||
| 406 | snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); | ||
| 407 | ret = IRQ_HANDLED; | ||
| 408 | } | ||
| 409 | |||
| 410 | return ret; | ||
| 411 | } | ||
| 412 | |||
| 413 | static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream, | ||
| 414 | struct snd_soc_pcm_runtime *soc_runtime) | ||
| 415 | { | ||
| 416 | struct snd_dma_buffer *buf = &substream->dma_buffer; | ||
| 417 | size_t size = lpass_platform_pcm_hardware.buffer_bytes_max; | ||
| 418 | |||
| 419 | buf->dev.type = SNDRV_DMA_TYPE_DEV; | ||
| 420 | buf->dev.dev = soc_runtime->dev; | ||
| 421 | buf->private_data = NULL; | ||
| 422 | buf->area = dma_alloc_coherent(soc_runtime->dev, size, &buf->addr, | ||
| 423 | GFP_KERNEL); | ||
| 424 | if (!buf->area) { | ||
| 425 | dev_err(soc_runtime->dev, "%s: Could not allocate DMA buffer\n", | ||
| 426 | __func__); | ||
| 427 | return -ENOMEM; | ||
| 428 | } | ||
| 429 | buf->bytes = size; | ||
| 430 | |||
| 431 | return 0; | ||
| 432 | } | ||
| 433 | |||
| 434 | static void lpass_platform_free_buffer(struct snd_pcm_substream *substream, | ||
| 435 | struct snd_soc_pcm_runtime *soc_runtime) | ||
| 436 | { | ||
| 437 | struct snd_dma_buffer *buf = &substream->dma_buffer; | ||
| 438 | |||
| 439 | if (buf->area) { | ||
| 440 | dma_free_coherent(soc_runtime->dev, buf->bytes, buf->area, | ||
| 441 | buf->addr); | ||
| 442 | } | ||
| 443 | buf->area = NULL; | ||
| 444 | } | ||
| 445 | |||
| 446 | static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) | ||
| 447 | { | ||
| 448 | struct snd_pcm *pcm = soc_runtime->pcm; | ||
| 449 | struct snd_pcm_substream *substream = | ||
| 450 | pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; | ||
| 451 | struct lpass_data *drvdata = | ||
| 452 | snd_soc_platform_get_drvdata(soc_runtime->platform); | ||
| 453 | int ret; | ||
| 454 | |||
| 455 | soc_runtime->dev->coherent_dma_mask = DMA_BIT_MASK(32); | ||
| 456 | soc_runtime->dev->dma_mask = &soc_runtime->dev->coherent_dma_mask; | ||
| 457 | |||
| 458 | ret = lpass_platform_alloc_buffer(substream, soc_runtime); | ||
| 459 | if (ret) | ||
| 460 | return ret; | ||
| 461 | |||
| 462 | ret = devm_request_irq(soc_runtime->dev, drvdata->lpaif_irq, | ||
| 463 | lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING, | ||
| 464 | "lpass-irq-lpaif", substream); | ||
| 465 | if (ret) { | ||
| 466 | dev_err(soc_runtime->dev, "%s() irq request failed: %d\n", | ||
| 467 | __func__, ret); | ||
| 468 | goto err_buf; | ||
| 469 | } | ||
| 470 | |||
| 471 | /* ensure audio hardware is disabled */ | ||
| 472 | ret = regmap_write(drvdata->lpaif_map, | ||
| 473 | LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), 0); | ||
| 474 | if (ret) { | ||
| 475 | dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n", | ||
| 476 | __func__, ret); | ||
| 477 | return ret; | ||
| 478 | } | ||
| 479 | ret = regmap_write(drvdata->lpaif_map, | ||
| 480 | LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0); | ||
| 481 | if (ret) { | ||
| 482 | dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", | ||
| 483 | __func__, ret); | ||
| 484 | return ret; | ||
| 485 | } | ||
| 486 | |||
| 487 | return 0; | ||
| 488 | |||
| 489 | err_buf: | ||
| 490 | lpass_platform_free_buffer(substream, soc_runtime); | ||
| 491 | return ret; | ||
| 492 | } | ||
| 493 | |||
| 494 | static void lpass_platform_pcm_free(struct snd_pcm *pcm) | ||
| 495 | { | ||
| 496 | struct snd_pcm_substream *substream = | ||
| 497 | pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; | ||
| 498 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
| 499 | |||
| 500 | lpass_platform_free_buffer(substream, soc_runtime); | ||
| 501 | } | ||
| 502 | |||
| 503 | static struct snd_soc_platform_driver lpass_platform_driver = { | ||
| 504 | .pcm_new = lpass_platform_pcm_new, | ||
| 505 | .pcm_free = lpass_platform_pcm_free, | ||
| 506 | .ops = &lpass_platform_pcm_ops, | ||
| 507 | }; | ||
| 508 | |||
| 509 | int asoc_qcom_lpass_platform_register(struct platform_device *pdev) | ||
| 510 | { | ||
| 511 | struct lpass_data *drvdata = platform_get_drvdata(pdev); | ||
| 512 | |||
| 513 | drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif"); | ||
| 514 | if (drvdata->lpaif_irq < 0) { | ||
| 515 | dev_err(&pdev->dev, "%s() error getting irq handle: %d\n", | ||
| 516 | __func__, drvdata->lpaif_irq); | ||
| 517 | return -ENODEV; | ||
| 518 | } | ||
| 519 | |||
| 520 | return devm_snd_soc_register_platform(&pdev->dev, | ||
| 521 | &lpass_platform_driver); | ||
| 522 | } | ||
| 523 | EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register); | ||
| 524 | |||
| 525 | MODULE_DESCRIPTION("QTi LPASS Platform Driver"); | ||
| 526 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h new file mode 100644 index 000000000000..5c99b3dace86 --- /dev/null +++ b/sound/soc/qcom/lpass.h | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 and | ||
| 6 | * only version 2 as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | * | ||
| 13 | * lpass.h - Definitions for the QTi LPASS | ||
| 14 | */ | ||
| 15 | |||
| 16 | #ifndef __LPASS_H__ | ||
| 17 | #define __LPASS_H__ | ||
| 18 | |||
| 19 | #include <linux/clk.h> | ||
| 20 | #include <linux/compiler.h> | ||
| 21 | #include <linux/platform_device.h> | ||
| 22 | #include <linux/regmap.h> | ||
| 23 | |||
| 24 | #define LPASS_AHBIX_CLOCK_FREQUENCY 131072000 | ||
| 25 | |||
| 26 | /* Both the CPU DAI and platform drivers will access this data */ | ||
| 27 | struct lpass_data { | ||
| 28 | |||
| 29 | /* AHB-I/X bus clocks inside the low-power audio subsystem (LPASS) */ | ||
| 30 | struct clk *ahbix_clk; | ||
| 31 | |||
| 32 | /* MI2S system clock */ | ||
| 33 | struct clk *mi2s_osr_clk; | ||
| 34 | |||
| 35 | /* MI2S bit clock (derived from system clock by a divider */ | ||
| 36 | struct clk *mi2s_bit_clk; | ||
| 37 | |||
| 38 | /* low-power audio interface (LPAIF) registers */ | ||
| 39 | void __iomem *lpaif; | ||
| 40 | |||
| 41 | /* regmap backed by the low-power audio interface (LPAIF) registers */ | ||
| 42 | struct regmap *lpaif_map; | ||
| 43 | |||
| 44 | /* interrupts from the low-power audio interface (LPAIF) */ | ||
| 45 | int lpaif_irq; | ||
| 46 | }; | ||
| 47 | |||
| 48 | /* register the platform driver from the CPU DAI driver */ | ||
| 49 | int asoc_qcom_lpass_platform_register(struct platform_device *); | ||
| 50 | |||
| 51 | #endif /* __LPASS_H__ */ | ||
diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c new file mode 100644 index 000000000000..b8bd296190ad --- /dev/null +++ b/sound/soc/qcom/storm.c | |||
| @@ -0,0 +1,162 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 and | ||
| 6 | * only version 2 as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | * | ||
| 13 | * storm.c -- ALSA SoC machine driver for QTi ipq806x-based Storm board | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/device.h> | ||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/of.h> | ||
| 19 | #include <linux/mod_devicetable.h> | ||
| 20 | #include <linux/platform_device.h> | ||
| 21 | #include <sound/pcm.h> | ||
| 22 | #include <sound/pcm_params.h> | ||
| 23 | #include <sound/soc.h> | ||
| 24 | |||
| 25 | #define STORM_SYSCLK_MULT 4 | ||
| 26 | |||
| 27 | static int storm_ops_hw_params(struct snd_pcm_substream *substream, | ||
| 28 | struct snd_pcm_hw_params *params) | ||
| 29 | { | ||
| 30 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
| 31 | struct snd_soc_card *card = soc_runtime->card; | ||
| 32 | snd_pcm_format_t format = params_format(params); | ||
| 33 | unsigned int rate = params_rate(params); | ||
| 34 | unsigned int sysclk_freq; | ||
| 35 | int bitwidth, ret; | ||
| 36 | |||
| 37 | bitwidth = snd_pcm_format_width(format); | ||
| 38 | if (bitwidth < 0) { | ||
| 39 | dev_err(card->dev, "%s() invalid bit width given: %d\n", | ||
| 40 | __func__, bitwidth); | ||
| 41 | return bitwidth; | ||
| 42 | } | ||
| 43 | |||
| 44 | /* | ||
| 45 | * as the CPU DAI is the I2S bus master and no system clock is needed by | ||
| 46 | * the MAX98357a DAC, simply set the system clock to be a constant | ||
| 47 | * multiple of the bit clock for the clock divider | ||
| 48 | */ | ||
| 49 | sysclk_freq = rate * bitwidth * 2 * STORM_SYSCLK_MULT; | ||
| 50 | |||
| 51 | ret = snd_soc_dai_set_sysclk(soc_runtime->cpu_dai, 0, sysclk_freq, 0); | ||
| 52 | if (ret) { | ||
| 53 | dev_err(card->dev, "%s() error setting sysclk to %u: %d\n", | ||
| 54 | __func__, sysclk_freq, ret); | ||
| 55 | return ret; | ||
| 56 | } | ||
| 57 | |||
| 58 | return 0; | ||
| 59 | } | ||
| 60 | |||
| 61 | static struct snd_soc_ops storm_soc_ops = { | ||
| 62 | .hw_params = storm_ops_hw_params, | ||
| 63 | }; | ||
| 64 | |||
| 65 | static struct snd_soc_dai_link storm_dai_link = { | ||
| 66 | .name = "Primary", | ||
| 67 | .stream_name = "Primary", | ||
| 68 | .codec_dai_name = "HiFi", | ||
| 69 | .ops = &storm_soc_ops, | ||
| 70 | }; | ||
| 71 | |||
| 72 | static struct snd_soc_card storm_soc_card = { | ||
| 73 | .name = "ipq806x-storm", | ||
| 74 | .dev = NULL, | ||
| 75 | }; | ||
| 76 | |||
| 77 | static int storm_parse_of(struct snd_soc_card *card) | ||
| 78 | { | ||
| 79 | struct snd_soc_dai_link *dai_link = card->dai_link; | ||
| 80 | struct device_node *np = card->dev->of_node; | ||
| 81 | |||
| 82 | dai_link->cpu_of_node = of_parse_phandle(np, "cpu", 0); | ||
| 83 | if (!dai_link->cpu_of_node) { | ||
| 84 | dev_err(card->dev, "%s() error getting cpu phandle\n", | ||
| 85 | __func__); | ||
| 86 | return -EINVAL; | ||
| 87 | } | ||
| 88 | dai_link->platform_of_node = dai_link->cpu_of_node; | ||
| 89 | |||
| 90 | dai_link->codec_of_node = of_parse_phandle(np, "codec", 0); | ||
| 91 | if (!dai_link->codec_of_node) { | ||
| 92 | dev_err(card->dev, "%s() error getting codec phandle\n", | ||
| 93 | __func__); | ||
| 94 | return -EINVAL; | ||
| 95 | } | ||
| 96 | |||
| 97 | return 0; | ||
| 98 | } | ||
| 99 | |||
| 100 | static int storm_platform_probe(struct platform_device *pdev) | ||
| 101 | { | ||
| 102 | struct snd_soc_card *card = &storm_soc_card; | ||
| 103 | int ret; | ||
| 104 | |||
| 105 | if (card->dev) { | ||
| 106 | dev_err(&pdev->dev, "%s() error, existing soundcard\n", | ||
| 107 | __func__); | ||
| 108 | return -ENODEV; | ||
| 109 | } | ||
| 110 | card->dev = &pdev->dev; | ||
| 111 | platform_set_drvdata(pdev, card); | ||
| 112 | |||
| 113 | ret = snd_soc_of_parse_card_name(card, "qcom,model"); | ||
| 114 | if (ret) { | ||
| 115 | dev_err(&pdev->dev, "%s() error parsing card name: %d\n", | ||
| 116 | __func__, ret); | ||
| 117 | return ret; | ||
| 118 | } | ||
| 119 | |||
| 120 | card->dai_link = &storm_dai_link; | ||
| 121 | card->num_links = 1; | ||
| 122 | |||
| 123 | ret = storm_parse_of(card); | ||
| 124 | if (ret) { | ||
| 125 | dev_err(&pdev->dev, "%s() error resolving dai links: %d\n", | ||
| 126 | __func__, ret); | ||
| 127 | return ret; | ||
| 128 | } | ||
| 129 | |||
| 130 | ret = devm_snd_soc_register_card(&pdev->dev, card); | ||
| 131 | if (ret == -EPROBE_DEFER) { | ||
| 132 | card->dev = NULL; | ||
| 133 | return ret; | ||
| 134 | } else if (ret) { | ||
| 135 | dev_err(&pdev->dev, "%s() error registering soundcard: %d\n", | ||
| 136 | __func__, ret); | ||
| 137 | return ret; | ||
| 138 | } | ||
| 139 | |||
| 140 | return 0; | ||
| 141 | } | ||
| 142 | |||
| 143 | #ifdef CONFIG_OF | ||
| 144 | static const struct of_device_id storm_device_id[] = { | ||
| 145 | { .compatible = "google,storm-audio" }, | ||
| 146 | {}, | ||
| 147 | }; | ||
| 148 | MODULE_DEVICE_TABLE(of, storm_device_id); | ||
| 149 | #endif | ||
| 150 | |||
| 151 | static struct platform_driver storm_platform_driver = { | ||
| 152 | .driver = { | ||
| 153 | .name = "storm-audio", | ||
| 154 | .of_match_table = | ||
| 155 | of_match_ptr(storm_device_id), | ||
| 156 | }, | ||
| 157 | .probe = storm_platform_probe, | ||
| 158 | }; | ||
| 159 | module_platform_driver(storm_platform_driver); | ||
| 160 | |||
| 161 | MODULE_DESCRIPTION("QTi IPQ806x-based Storm Machine Driver"); | ||
| 162 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 80245b6eebd6..07114b0b0dc1 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig | |||
| @@ -36,11 +36,17 @@ config SND_SOC_SH4_SIU | |||
| 36 | 36 | ||
| 37 | config SND_SOC_RCAR | 37 | config SND_SOC_RCAR |
| 38 | tristate "R-Car series SRU/SCU/SSIU/SSI support" | 38 | tristate "R-Car series SRU/SCU/SSIU/SSI support" |
| 39 | depends on DMA_OF | ||
| 39 | select SND_SIMPLE_CARD | 40 | select SND_SIMPLE_CARD |
| 40 | select REGMAP_MMIO | 41 | select REGMAP_MMIO |
| 41 | help | 42 | help |
| 42 | This option enables R-Car SUR/SCU/SSIU/SSI sound support | 43 | This option enables R-Car SUR/SCU/SSIU/SSI sound support |
| 43 | 44 | ||
| 45 | config SND_SOC_RSRC_CARD | ||
| 46 | tristate "Renesas Sampling Rate Convert Sound Card" | ||
| 47 | help | ||
| 48 | This option enables simple sound if you need sampling rate convert | ||
| 49 | |||
| 44 | ## | 50 | ## |
| 45 | ## Boards | 51 | ## Boards |
| 46 | ## | 52 | ## |
diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index 9ac536429800..f1b445173fba 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile | |||
| @@ -1,2 +1,5 @@ | |||
| 1 | snd-soc-rcar-objs := core.o gen.o src.o adg.o ssi.o dvc.o | 1 | snd-soc-rcar-objs := core.o gen.o dma.o src.o adg.o ssi.o dvc.o |
| 2 | obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o \ No newline at end of file | 2 | obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o |
| 3 | |||
| 4 | snd-soc-rsrc-card-objs := rsrc-card.o | ||
| 5 | obj-$(CONFIG_SND_SOC_RSRC_CARD) += snd-soc-rsrc-card.o | ||
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 7ac35c9d1cb8..fefc881dbac2 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c | |||
| @@ -183,6 +183,8 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, | |||
| 183 | 183 | ||
| 184 | rsnd_mod_bset(mod, DIV_EN, en, en); | 184 | rsnd_mod_bset(mod, DIV_EN, en, en); |
| 185 | 185 | ||
| 186 | dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate); | ||
| 187 | |||
| 186 | return 0; | 188 | return 0; |
| 187 | } | 189 | } |
| 188 | 190 | ||
| @@ -432,7 +434,5 @@ int rsnd_adg_probe(struct platform_device *pdev, | |||
| 432 | 434 | ||
| 433 | priv->adg = adg; | 435 | priv->adg = adg; |
| 434 | 436 | ||
| 435 | dev_dbg(dev, "adg probed\n"); | ||
| 436 | |||
| 437 | return 0; | 437 | return 0; |
| 438 | } | 438 | } |
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 31202e95be1e..9f48d75fa992 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c | |||
| @@ -94,21 +94,20 @@ | |||
| 94 | * | 94 | * |
| 95 | */ | 95 | */ |
| 96 | #include <linux/pm_runtime.h> | 96 | #include <linux/pm_runtime.h> |
| 97 | #include <linux/shdma-base.h> | ||
| 98 | #include "rsnd.h" | 97 | #include "rsnd.h" |
| 99 | 98 | ||
| 100 | #define RSND_RATES SNDRV_PCM_RATE_8000_96000 | 99 | #define RSND_RATES SNDRV_PCM_RATE_8000_96000 |
| 101 | #define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) | 100 | #define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) |
| 102 | 101 | ||
| 103 | static struct rsnd_of_data rsnd_of_data_gen1 = { | 102 | static const struct rsnd_of_data rsnd_of_data_gen1 = { |
| 104 | .flags = RSND_GEN1, | 103 | .flags = RSND_GEN1, |
| 105 | }; | 104 | }; |
| 106 | 105 | ||
| 107 | static struct rsnd_of_data rsnd_of_data_gen2 = { | 106 | static const struct rsnd_of_data rsnd_of_data_gen2 = { |
| 108 | .flags = RSND_GEN2, | 107 | .flags = RSND_GEN2, |
| 109 | }; | 108 | }; |
| 110 | 109 | ||
| 111 | static struct of_device_id rsnd_of_match[] = { | 110 | static const struct of_device_id rsnd_of_match[] = { |
| 112 | { .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 }, | 111 | { .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 }, |
| 113 | { .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 }, | 112 | { .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 }, |
| 114 | {}, | 113 | {}, |
| @@ -138,15 +137,12 @@ char *rsnd_mod_name(struct rsnd_mod *mod) | |||
| 138 | return mod->ops->name; | 137 | return mod->ops->name; |
| 139 | } | 138 | } |
| 140 | 139 | ||
| 141 | char *rsnd_mod_dma_name(struct rsnd_mod *mod) | 140 | struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod) |
| 142 | { | 141 | { |
| 143 | if (!mod || !mod->ops) | 142 | if (!mod || !mod->ops || !mod->ops->dma_req) |
| 144 | return "unknown"; | 143 | return NULL; |
| 145 | |||
| 146 | if (!mod->ops->dma_name) | ||
| 147 | return mod->ops->name; | ||
| 148 | 144 | ||
| 149 | return mod->ops->dma_name(mod); | 145 | return mod->ops->dma_req(mod); |
| 150 | } | 146 | } |
| 151 | 147 | ||
| 152 | int rsnd_mod_init(struct rsnd_mod *mod, | 148 | int rsnd_mod_init(struct rsnd_mod *mod, |
| @@ -175,228 +171,6 @@ void rsnd_mod_quit(struct rsnd_mod *mod) | |||
| 175 | } | 171 | } |
| 176 | 172 | ||
| 177 | /* | 173 | /* |
| 178 | * rsnd_dma functions | ||
| 179 | */ | ||
| 180 | void rsnd_dma_stop(struct rsnd_dma *dma) | ||
| 181 | { | ||
| 182 | dmaengine_terminate_all(dma->chan); | ||
| 183 | } | ||
| 184 | |||
| 185 | static void rsnd_dma_complete(void *data) | ||
| 186 | { | ||
| 187 | struct rsnd_dma *dma = (struct rsnd_dma *)data; | ||
| 188 | struct rsnd_mod *mod = rsnd_dma_to_mod(dma); | ||
| 189 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
| 190 | |||
| 191 | /* | ||
| 192 | * Renesas sound Gen1 needs 1 DMAC, | ||
| 193 | * Gen2 needs 2 DMAC. | ||
| 194 | * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri. | ||
| 195 | * But, Audio-DMAC-peri-peri doesn't have interrupt, | ||
| 196 | * and this driver is assuming that here. | ||
| 197 | * | ||
| 198 | * If Audio-DMAC-peri-peri has interrpt, | ||
| 199 | * rsnd_dai_pointer_update() will be called twice, | ||
| 200 | * ant it will breaks io->byte_pos | ||
| 201 | */ | ||
| 202 | |||
| 203 | rsnd_dai_pointer_update(io, io->byte_per_period); | ||
| 204 | } | ||
| 205 | |||
| 206 | void rsnd_dma_start(struct rsnd_dma *dma) | ||
| 207 | { | ||
| 208 | struct rsnd_mod *mod = rsnd_dma_to_mod(dma); | ||
| 209 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
| 210 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
| 211 | struct snd_pcm_substream *substream = io->substream; | ||
| 212 | struct device *dev = rsnd_priv_to_dev(priv); | ||
| 213 | struct dma_async_tx_descriptor *desc; | ||
| 214 | |||
| 215 | desc = dmaengine_prep_dma_cyclic(dma->chan, | ||
| 216 | (dma->addr) ? dma->addr : | ||
| 217 | substream->runtime->dma_addr, | ||
| 218 | snd_pcm_lib_buffer_bytes(substream), | ||
| 219 | snd_pcm_lib_period_bytes(substream), | ||
| 220 | dma->dir, | ||
| 221 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | ||
| 222 | |||
| 223 | if (!desc) { | ||
| 224 | dev_err(dev, "dmaengine_prep_slave_sg() fail\n"); | ||
| 225 | return; | ||
| 226 | } | ||
| 227 | |||
| 228 | desc->callback = rsnd_dma_complete; | ||
| 229 | desc->callback_param = dma; | ||
| 230 | |||
| 231 | if (dmaengine_submit(desc) < 0) { | ||
| 232 | dev_err(dev, "dmaengine_submit() fail\n"); | ||
| 233 | return; | ||
| 234 | } | ||
| 235 | |||
| 236 | dma_async_issue_pending(dma->chan); | ||
| 237 | } | ||
| 238 | |||
| 239 | int rsnd_dma_available(struct rsnd_dma *dma) | ||
| 240 | { | ||
| 241 | return !!dma->chan; | ||
| 242 | } | ||
| 243 | |||
| 244 | #define DMA_NAME_SIZE 16 | ||
| 245 | #define MOD_MAX 4 /* MEM/SSI/SRC/DVC */ | ||
| 246 | static int _rsnd_dma_of_name(char *dma_name, struct rsnd_mod *mod) | ||
| 247 | { | ||
| 248 | if (mod) | ||
| 249 | return snprintf(dma_name, DMA_NAME_SIZE / 2, "%s%d", | ||
| 250 | rsnd_mod_dma_name(mod), rsnd_mod_id(mod)); | ||
| 251 | else | ||
| 252 | return snprintf(dma_name, DMA_NAME_SIZE / 2, "mem"); | ||
| 253 | |||
| 254 | } | ||
| 255 | |||
| 256 | static void rsnd_dma_of_name(struct rsnd_mod *mod_from, | ||
| 257 | struct rsnd_mod *mod_to, | ||
| 258 | char *dma_name) | ||
| 259 | { | ||
| 260 | int index = 0; | ||
| 261 | |||
| 262 | index = _rsnd_dma_of_name(dma_name + index, mod_from); | ||
| 263 | *(dma_name + index++) = '_'; | ||
| 264 | index = _rsnd_dma_of_name(dma_name + index, mod_to); | ||
| 265 | } | ||
| 266 | |||
| 267 | static void rsnd_dma_of_path(struct rsnd_dma *dma, | ||
| 268 | int is_play, | ||
| 269 | struct rsnd_mod **mod_from, | ||
| 270 | struct rsnd_mod **mod_to) | ||
| 271 | { | ||
| 272 | struct rsnd_mod *this = rsnd_dma_to_mod(dma); | ||
| 273 | struct rsnd_dai_stream *io = rsnd_mod_to_io(this); | ||
| 274 | struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); | ||
| 275 | struct rsnd_mod *src = rsnd_io_to_mod_src(io); | ||
| 276 | struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); | ||
| 277 | struct rsnd_mod *mod[MOD_MAX]; | ||
| 278 | int i, index; | ||
| 279 | |||
| 280 | |||
| 281 | for (i = 0; i < MOD_MAX; i++) | ||
| 282 | mod[i] = NULL; | ||
| 283 | |||
| 284 | /* | ||
| 285 | * in play case... | ||
| 286 | * | ||
| 287 | * src -> dst | ||
| 288 | * | ||
| 289 | * mem -> SSI | ||
| 290 | * mem -> SRC -> SSI | ||
| 291 | * mem -> SRC -> DVC -> SSI | ||
| 292 | */ | ||
| 293 | mod[0] = NULL; /* for "mem" */ | ||
| 294 | index = 1; | ||
| 295 | for (i = 1; i < MOD_MAX; i++) { | ||
| 296 | if (!src) { | ||
| 297 | mod[i] = ssi; | ||
| 298 | } else if (!dvc) { | ||
| 299 | mod[i] = src; | ||
| 300 | src = NULL; | ||
| 301 | } else { | ||
| 302 | if ((!is_play) && (this == src)) | ||
| 303 | this = dvc; | ||
| 304 | |||
| 305 | mod[i] = (is_play) ? src : dvc; | ||
| 306 | i++; | ||
| 307 | mod[i] = (is_play) ? dvc : src; | ||
| 308 | src = NULL; | ||
| 309 | dvc = NULL; | ||
| 310 | } | ||
| 311 | |||
| 312 | if (mod[i] == this) | ||
| 313 | index = i; | ||
| 314 | |||
| 315 | if (mod[i] == ssi) | ||
| 316 | break; | ||
| 317 | } | ||
| 318 | |||
| 319 | if (is_play) { | ||
| 320 | *mod_from = mod[index - 1]; | ||
| 321 | *mod_to = mod[index]; | ||
| 322 | } else { | ||
| 323 | *mod_from = mod[index]; | ||
| 324 | *mod_to = mod[index - 1]; | ||
| 325 | } | ||
| 326 | } | ||
| 327 | |||
| 328 | int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, | ||
| 329 | int is_play, int id) | ||
| 330 | { | ||
| 331 | struct device *dev = rsnd_priv_to_dev(priv); | ||
| 332 | struct dma_slave_config cfg; | ||
| 333 | struct rsnd_mod *mod_from; | ||
| 334 | struct rsnd_mod *mod_to; | ||
| 335 | char dma_name[DMA_NAME_SIZE]; | ||
| 336 | dma_cap_mask_t mask; | ||
| 337 | int ret; | ||
| 338 | |||
| 339 | if (dma->chan) { | ||
| 340 | dev_err(dev, "it already has dma channel\n"); | ||
| 341 | return -EIO; | ||
| 342 | } | ||
| 343 | |||
| 344 | dma_cap_zero(mask); | ||
| 345 | dma_cap_set(DMA_SLAVE, mask); | ||
| 346 | |||
| 347 | rsnd_dma_of_path(dma, is_play, &mod_from, &mod_to); | ||
| 348 | rsnd_dma_of_name(mod_from, mod_to, dma_name); | ||
| 349 | |||
| 350 | cfg.slave_id = id; | ||
| 351 | cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; | ||
| 352 | cfg.src_addr = rsnd_gen_dma_addr(priv, mod_from, is_play, 1); | ||
| 353 | cfg.dst_addr = rsnd_gen_dma_addr(priv, mod_to, is_play, 0); | ||
| 354 | cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; | ||
| 355 | cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; | ||
| 356 | |||
| 357 | dev_dbg(dev, "dma : %s %pad -> %pad\n", | ||
| 358 | dma_name, &cfg.src_addr, &cfg.dst_addr); | ||
| 359 | |||
| 360 | dma->chan = dma_request_slave_channel_compat(mask, shdma_chan_filter, | ||
| 361 | (void *)id, dev, | ||
| 362 | dma_name); | ||
| 363 | if (!dma->chan) { | ||
| 364 | dev_err(dev, "can't get dma channel\n"); | ||
| 365 | goto rsnd_dma_channel_err; | ||
| 366 | } | ||
| 367 | |||
| 368 | ret = dmaengine_slave_config(dma->chan, &cfg); | ||
| 369 | if (ret < 0) | ||
| 370 | goto rsnd_dma_init_err; | ||
| 371 | |||
| 372 | dma->addr = is_play ? cfg.src_addr : cfg.dst_addr; | ||
| 373 | dma->dir = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; | ||
| 374 | |||
| 375 | return 0; | ||
| 376 | |||
| 377 | rsnd_dma_init_err: | ||
| 378 | rsnd_dma_quit(priv, dma); | ||
| 379 | rsnd_dma_channel_err: | ||
| 380 | |||
| 381 | /* | ||
| 382 | * DMA failed. try to PIO mode | ||
| 383 | * see | ||
| 384 | * rsnd_ssi_fallback() | ||
| 385 | * rsnd_rdai_continuance_probe() | ||
| 386 | */ | ||
| 387 | return -EAGAIN; | ||
| 388 | } | ||
| 389 | |||
| 390 | void rsnd_dma_quit(struct rsnd_priv *priv, | ||
| 391 | struct rsnd_dma *dma) | ||
| 392 | { | ||
| 393 | if (dma->chan) | ||
| 394 | dma_release_channel(dma->chan); | ||
| 395 | |||
| 396 | dma->chan = NULL; | ||
| 397 | } | ||
| 398 | |||
| 399 | /* | ||
| 400 | * settting function | 174 | * settting function |
| 401 | */ | 175 | */ |
| 402 | u32 rsnd_get_adinr(struct rsnd_mod *mod) | 176 | u32 rsnd_get_adinr(struct rsnd_mod *mod) |
| @@ -429,7 +203,7 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod) | |||
| 429 | ({ \ | 203 | ({ \ |
| 430 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ | 204 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ |
| 431 | struct device *dev = rsnd_priv_to_dev(priv); \ | 205 | struct device *dev = rsnd_priv_to_dev(priv); \ |
| 432 | u32 mask = 1 << __rsnd_mod_shift_##func; \ | 206 | u32 mask = (1 << __rsnd_mod_shift_##func) & ~(1 << 31); \ |
| 433 | u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func; \ | 207 | u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func; \ |
| 434 | int ret = 0; \ | 208 | int ret = 0; \ |
| 435 | if ((mod->status & mask) == call) { \ | 209 | if ((mod->status & mask) == call) { \ |
| @@ -471,7 +245,7 @@ static int rsnd_dai_connect(struct rsnd_mod *mod, | |||
| 471 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | 245 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
| 472 | struct device *dev = rsnd_priv_to_dev(priv); | 246 | struct device *dev = rsnd_priv_to_dev(priv); |
| 473 | 247 | ||
| 474 | dev_err(dev, "%s%d is not empty\n", | 248 | dev_err(dev, "%s[%d] is not empty\n", |
| 475 | rsnd_mod_name(mod), | 249 | rsnd_mod_name(mod), |
| 476 | rsnd_mod_id(mod)); | 250 | rsnd_mod_id(mod)); |
| 477 | return -EIO; | 251 | return -EIO; |
| @@ -887,20 +661,28 @@ static int rsnd_dai_probe(struct platform_device *pdev, | |||
| 887 | drv[i].name = rdai[i].name; | 661 | drv[i].name = rdai[i].name; |
| 888 | drv[i].ops = &rsnd_soc_dai_ops; | 662 | drv[i].ops = &rsnd_soc_dai_ops; |
| 889 | if (pmod) { | 663 | if (pmod) { |
| 664 | snprintf(rdai[i].playback.name, RSND_DAI_NAME_SIZE, | ||
| 665 | "DAI%d Playback", i); | ||
| 666 | |||
| 890 | drv[i].playback.rates = RSND_RATES; | 667 | drv[i].playback.rates = RSND_RATES; |
| 891 | drv[i].playback.formats = RSND_FMTS; | 668 | drv[i].playback.formats = RSND_FMTS; |
| 892 | drv[i].playback.channels_min = 2; | 669 | drv[i].playback.channels_min = 2; |
| 893 | drv[i].playback.channels_max = 2; | 670 | drv[i].playback.channels_max = 2; |
| 671 | drv[i].playback.stream_name = rdai[i].playback.name; | ||
| 894 | 672 | ||
| 895 | rdai[i].playback.info = &info->dai_info[i].playback; | 673 | rdai[i].playback.info = &info->dai_info[i].playback; |
| 896 | rdai[i].playback.rdai = rdai + i; | 674 | rdai[i].playback.rdai = rdai + i; |
| 897 | rsnd_path_init(priv, &rdai[i], &rdai[i].playback); | 675 | rsnd_path_init(priv, &rdai[i], &rdai[i].playback); |
| 898 | } | 676 | } |
| 899 | if (cmod) { | 677 | if (cmod) { |
| 678 | snprintf(rdai[i].capture.name, RSND_DAI_NAME_SIZE, | ||
| 679 | "DAI%d Capture", i); | ||
| 680 | |||
| 900 | drv[i].capture.rates = RSND_RATES; | 681 | drv[i].capture.rates = RSND_RATES; |
| 901 | drv[i].capture.formats = RSND_FMTS; | 682 | drv[i].capture.formats = RSND_FMTS; |
| 902 | drv[i].capture.channels_min = 2; | 683 | drv[i].capture.channels_min = 2; |
| 903 | drv[i].capture.channels_max = 2; | 684 | drv[i].capture.channels_max = 2; |
| 685 | drv[i].capture.stream_name = rdai[i].capture.name; | ||
| 904 | 686 | ||
| 905 | rdai[i].capture.info = &info->dai_info[i].capture; | 687 | rdai[i].capture.info = &info->dai_info[i].capture; |
| 906 | rdai[i].capture.rdai = rdai + i; | 688 | rdai[i].capture.rdai = rdai + i; |
| @@ -946,6 +728,15 @@ static int rsnd_pcm_open(struct snd_pcm_substream *substream) | |||
| 946 | static int rsnd_hw_params(struct snd_pcm_substream *substream, | 728 | static int rsnd_hw_params(struct snd_pcm_substream *substream, |
| 947 | struct snd_pcm_hw_params *hw_params) | 729 | struct snd_pcm_hw_params *hw_params) |
| 948 | { | 730 | { |
| 731 | struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); | ||
| 732 | struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); | ||
| 733 | struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); | ||
| 734 | int ret; | ||
| 735 | |||
| 736 | ret = rsnd_dai_call(hw_params, io, substream, hw_params); | ||
| 737 | if (ret) | ||
| 738 | return ret; | ||
| 739 | |||
| 949 | return snd_pcm_lib_malloc_pages(substream, | 740 | return snd_pcm_lib_malloc_pages(substream, |
| 950 | params_buffer_bytes(hw_params)); | 741 | params_buffer_bytes(hw_params)); |
| 951 | } | 742 | } |
| @@ -1210,6 +1001,7 @@ static int rsnd_probe(struct platform_device *pdev) | |||
| 1210 | const struct rsnd_of_data *of_data, | 1001 | const struct rsnd_of_data *of_data, |
| 1211 | struct rsnd_priv *priv) = { | 1002 | struct rsnd_priv *priv) = { |
| 1212 | rsnd_gen_probe, | 1003 | rsnd_gen_probe, |
| 1004 | rsnd_dma_probe, | ||
| 1213 | rsnd_ssi_probe, | 1005 | rsnd_ssi_probe, |
| 1214 | rsnd_src_probe, | 1006 | rsnd_src_probe, |
| 1215 | rsnd_dvc_probe, | 1007 | rsnd_dvc_probe, |
diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c new file mode 100644 index 000000000000..ac3756f6af60 --- /dev/null +++ b/sound/soc/sh/rcar/dma.c | |||
| @@ -0,0 +1,616 @@ | |||
| 1 | /* | ||
| 2 | * Renesas R-Car Audio DMAC support | ||
| 3 | * | ||
| 4 | * Copyright (C) 2015 Renesas Electronics Corp. | ||
| 5 | * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | */ | ||
| 11 | #include <linux/delay.h> | ||
| 12 | #include <linux/of_dma.h> | ||
| 13 | #include "rsnd.h" | ||
| 14 | |||
| 15 | /* | ||
| 16 | * Audio DMAC peri peri register | ||
| 17 | */ | ||
| 18 | #define PDMASAR 0x00 | ||
| 19 | #define PDMADAR 0x04 | ||
| 20 | #define PDMACHCR 0x0c | ||
| 21 | |||
| 22 | /* PDMACHCR */ | ||
| 23 | #define PDMACHCR_DE (1 << 0) | ||
| 24 | |||
| 25 | struct rsnd_dma_ctrl { | ||
| 26 | void __iomem *base; | ||
| 27 | int dmapp_num; | ||
| 28 | }; | ||
| 29 | |||
| 30 | #define rsnd_priv_to_dmac(p) ((struct rsnd_dma_ctrl *)(p)->dma) | ||
| 31 | |||
| 32 | /* | ||
| 33 | * Audio DMAC | ||
| 34 | */ | ||
| 35 | static void rsnd_dmaen_complete(void *data) | ||
| 36 | { | ||
| 37 | struct rsnd_dma *dma = (struct rsnd_dma *)data; | ||
| 38 | struct rsnd_mod *mod = rsnd_dma_to_mod(dma); | ||
| 39 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
| 40 | |||
| 41 | /* | ||
| 42 | * Renesas sound Gen1 needs 1 DMAC, | ||
| 43 | * Gen2 needs 2 DMAC. | ||
| 44 | * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri. | ||
| 45 | * But, Audio-DMAC-peri-peri doesn't have interrupt, | ||
| 46 | * and this driver is assuming that here. | ||
| 47 | * | ||
| 48 | * If Audio-DMAC-peri-peri has interrpt, | ||
| 49 | * rsnd_dai_pointer_update() will be called twice, | ||
| 50 | * ant it will breaks io->byte_pos | ||
| 51 | */ | ||
| 52 | |||
| 53 | rsnd_dai_pointer_update(io, io->byte_per_period); | ||
| 54 | } | ||
| 55 | |||
| 56 | static void rsnd_dmaen_stop(struct rsnd_dma *dma) | ||
| 57 | { | ||
| 58 | struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); | ||
| 59 | |||
| 60 | dmaengine_terminate_all(dmaen->chan); | ||
| 61 | } | ||
| 62 | |||
| 63 | static void rsnd_dmaen_start(struct rsnd_dma *dma) | ||
| 64 | { | ||
| 65 | struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); | ||
| 66 | struct rsnd_mod *mod = rsnd_dma_to_mod(dma); | ||
| 67 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
| 68 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
| 69 | struct snd_pcm_substream *substream = io->substream; | ||
| 70 | struct device *dev = rsnd_priv_to_dev(priv); | ||
| 71 | struct dma_async_tx_descriptor *desc; | ||
| 72 | int is_play = rsnd_io_is_play(io); | ||
| 73 | |||
| 74 | desc = dmaengine_prep_dma_cyclic(dmaen->chan, | ||
| 75 | substream->runtime->dma_addr, | ||
| 76 | snd_pcm_lib_buffer_bytes(substream), | ||
| 77 | snd_pcm_lib_period_bytes(substream), | ||
| 78 | is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, | ||
| 79 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | ||
| 80 | |||
| 81 | if (!desc) { | ||
| 82 | dev_err(dev, "dmaengine_prep_slave_sg() fail\n"); | ||
| 83 | return; | ||
| 84 | } | ||
| 85 | |||
| 86 | desc->callback = rsnd_dmaen_complete; | ||
| 87 | desc->callback_param = dma; | ||
| 88 | |||
| 89 | if (dmaengine_submit(desc) < 0) { | ||
| 90 | dev_err(dev, "dmaengine_submit() fail\n"); | ||
| 91 | return; | ||
| 92 | } | ||
| 93 | |||
| 94 | dma_async_issue_pending(dmaen->chan); | ||
| 95 | } | ||
| 96 | |||
| 97 | struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, | ||
| 98 | struct rsnd_mod *mod, char *name) | ||
| 99 | { | ||
| 100 | struct dma_chan *chan; | ||
| 101 | struct device_node *np; | ||
| 102 | int i = 0; | ||
| 103 | |||
| 104 | for_each_child_of_node(of_node, np) { | ||
| 105 | if (i == rsnd_mod_id(mod)) | ||
| 106 | break; | ||
| 107 | i++; | ||
| 108 | } | ||
| 109 | |||
| 110 | chan = of_dma_request_slave_channel(np, name); | ||
| 111 | |||
| 112 | of_node_put(np); | ||
| 113 | of_node_put(of_node); | ||
| 114 | |||
| 115 | return chan; | ||
| 116 | } | ||
| 117 | |||
| 118 | static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_mod *mod_from, | ||
| 119 | struct rsnd_mod *mod_to) | ||
| 120 | { | ||
| 121 | if ((!mod_from && !mod_to) || | ||
| 122 | (mod_from && mod_to)) | ||
| 123 | return NULL; | ||
| 124 | |||
| 125 | if (mod_from) | ||
| 126 | return rsnd_mod_dma_req(mod_from); | ||
| 127 | else | ||
| 128 | return rsnd_mod_dma_req(mod_to); | ||
| 129 | } | ||
| 130 | |||
| 131 | static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, | ||
| 132 | struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) | ||
| 133 | { | ||
| 134 | struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); | ||
| 135 | struct device *dev = rsnd_priv_to_dev(priv); | ||
| 136 | struct dma_slave_config cfg = {}; | ||
| 137 | struct rsnd_mod *mod = rsnd_dma_to_mod(dma); | ||
| 138 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
| 139 | int is_play = rsnd_io_is_play(io); | ||
| 140 | int ret; | ||
| 141 | |||
| 142 | if (dmaen->chan) { | ||
| 143 | dev_err(dev, "it already has dma channel\n"); | ||
| 144 | return -EIO; | ||
| 145 | } | ||
| 146 | |||
| 147 | if (dev->of_node) { | ||
| 148 | dmaen->chan = rsnd_dmaen_request_channel(mod_from, mod_to); | ||
| 149 | } else { | ||
| 150 | dma_cap_mask_t mask; | ||
| 151 | |||
| 152 | dma_cap_zero(mask); | ||
| 153 | dma_cap_set(DMA_SLAVE, mask); | ||
| 154 | |||
| 155 | dmaen->chan = dma_request_channel(mask, shdma_chan_filter, | ||
| 156 | (void *)id); | ||
| 157 | } | ||
| 158 | if (IS_ERR_OR_NULL(dmaen->chan)) { | ||
| 159 | dev_err(dev, "can't get dma channel\n"); | ||
| 160 | goto rsnd_dma_channel_err; | ||
| 161 | } | ||
| 162 | |||
| 163 | cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; | ||
| 164 | cfg.src_addr = dma->src_addr; | ||
| 165 | cfg.dst_addr = dma->dst_addr; | ||
| 166 | cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; | ||
| 167 | cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; | ||
| 168 | |||
| 169 | dev_dbg(dev, "dma : %pad -> %pad\n", | ||
| 170 | &cfg.src_addr, &cfg.dst_addr); | ||
| 171 | |||
| 172 | ret = dmaengine_slave_config(dmaen->chan, &cfg); | ||
| 173 | if (ret < 0) | ||
| 174 | goto rsnd_dma_init_err; | ||
| 175 | |||
| 176 | return 0; | ||
| 177 | |||
| 178 | rsnd_dma_init_err: | ||
| 179 | rsnd_dma_quit(dma); | ||
| 180 | rsnd_dma_channel_err: | ||
| 181 | |||
| 182 | /* | ||
| 183 | * DMA failed. try to PIO mode | ||
| 184 | * see | ||
| 185 | * rsnd_ssi_fallback() | ||
| 186 | * rsnd_rdai_continuance_probe() | ||
| 187 | */ | ||
| 188 | return -EAGAIN; | ||
| 189 | } | ||
| 190 | |||
| 191 | static void rsnd_dmaen_quit(struct rsnd_dma *dma) | ||
| 192 | { | ||
| 193 | struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); | ||
| 194 | |||
| 195 | if (dmaen->chan) | ||
| 196 | dma_release_channel(dmaen->chan); | ||
| 197 | |||
| 198 | dmaen->chan = NULL; | ||
| 199 | } | ||
| 200 | |||
| 201 | static struct rsnd_dma_ops rsnd_dmaen_ops = { | ||
| 202 | .start = rsnd_dmaen_start, | ||
| 203 | .stop = rsnd_dmaen_stop, | ||
| 204 | .init = rsnd_dmaen_init, | ||
| 205 | .quit = rsnd_dmaen_quit, | ||
| 206 | }; | ||
| 207 | |||
| 208 | /* | ||
| 209 | * Audio DMAC peri peri | ||
| 210 | */ | ||
| 211 | static const u8 gen2_id_table_ssiu[] = { | ||
| 212 | 0x00, /* SSI00 */ | ||
| 213 | 0x04, /* SSI10 */ | ||
| 214 | 0x08, /* SSI20 */ | ||
| 215 | 0x0c, /* SSI3 */ | ||
| 216 | 0x0d, /* SSI4 */ | ||
| 217 | 0x0e, /* SSI5 */ | ||
| 218 | 0x0f, /* SSI6 */ | ||
| 219 | 0x10, /* SSI7 */ | ||
| 220 | 0x11, /* SSI8 */ | ||
| 221 | 0x12, /* SSI90 */ | ||
| 222 | }; | ||
| 223 | static const u8 gen2_id_table_scu[] = { | ||
| 224 | 0x2d, /* SCU_SRCI0 */ | ||
| 225 | 0x2e, /* SCU_SRCI1 */ | ||
| 226 | 0x2f, /* SCU_SRCI2 */ | ||
| 227 | 0x30, /* SCU_SRCI3 */ | ||
| 228 | 0x31, /* SCU_SRCI4 */ | ||
| 229 | 0x32, /* SCU_SRCI5 */ | ||
| 230 | 0x33, /* SCU_SRCI6 */ | ||
| 231 | 0x34, /* SCU_SRCI7 */ | ||
| 232 | 0x35, /* SCU_SRCI8 */ | ||
| 233 | 0x36, /* SCU_SRCI9 */ | ||
| 234 | }; | ||
| 235 | static const u8 gen2_id_table_cmd[] = { | ||
| 236 | 0x37, /* SCU_CMD0 */ | ||
| 237 | 0x38, /* SCU_CMD1 */ | ||
| 238 | }; | ||
| 239 | |||
| 240 | static u32 rsnd_dmapp_get_id(struct rsnd_mod *mod) | ||
| 241 | { | ||
| 242 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
| 243 | struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); | ||
| 244 | struct rsnd_mod *src = rsnd_io_to_mod_src(io); | ||
| 245 | struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); | ||
| 246 | const u8 *entry = NULL; | ||
| 247 | int id = rsnd_mod_id(mod); | ||
| 248 | int size = 0; | ||
| 249 | |||
| 250 | if (mod == ssi) { | ||
| 251 | entry = gen2_id_table_ssiu; | ||
| 252 | size = ARRAY_SIZE(gen2_id_table_ssiu); | ||
| 253 | } else if (mod == src) { | ||
| 254 | entry = gen2_id_table_scu; | ||
| 255 | size = ARRAY_SIZE(gen2_id_table_scu); | ||
| 256 | } else if (mod == dvc) { | ||
| 257 | entry = gen2_id_table_cmd; | ||
| 258 | size = ARRAY_SIZE(gen2_id_table_cmd); | ||
| 259 | } | ||
| 260 | |||
| 261 | if (!entry) | ||
| 262 | return 0xFF; | ||
| 263 | |||
| 264 | if (size <= id) | ||
| 265 | return 0xFF; | ||
| 266 | |||
| 267 | return entry[id]; | ||
| 268 | } | ||
| 269 | |||
| 270 | static u32 rsnd_dmapp_get_chcr(struct rsnd_mod *mod_from, | ||
| 271 | struct rsnd_mod *mod_to) | ||
| 272 | { | ||
| 273 | return (rsnd_dmapp_get_id(mod_from) << 24) + | ||
| 274 | (rsnd_dmapp_get_id(mod_to) << 16); | ||
| 275 | } | ||
| 276 | |||
| 277 | #define rsnd_dmapp_addr(dmac, dma, reg) \ | ||
| 278 | (dmac->base + 0x20 + reg + \ | ||
| 279 | (0x10 * rsnd_dma_to_dmapp(dma)->dmapp_id)) | ||
| 280 | static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg) | ||
| 281 | { | ||
| 282 | struct rsnd_mod *mod = rsnd_dma_to_mod(dma); | ||
| 283 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
| 284 | struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); | ||
| 285 | struct device *dev = rsnd_priv_to_dev(priv); | ||
| 286 | |||
| 287 | dev_dbg(dev, "w %p : %08x\n", rsnd_dmapp_addr(dmac, dma, reg), data); | ||
| 288 | |||
| 289 | iowrite32(data, rsnd_dmapp_addr(dmac, dma, reg)); | ||
| 290 | } | ||
| 291 | |||
| 292 | static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg) | ||
| 293 | { | ||
| 294 | struct rsnd_mod *mod = rsnd_dma_to_mod(dma); | ||
| 295 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
| 296 | struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); | ||
| 297 | |||
| 298 | return ioread32(rsnd_dmapp_addr(dmac, dma, reg)); | ||
| 299 | } | ||
| 300 | |||
| 301 | static void rsnd_dmapp_stop(struct rsnd_dma *dma) | ||
| 302 | { | ||
| 303 | int i; | ||
| 304 | |||
| 305 | rsnd_dmapp_write(dma, 0, PDMACHCR); | ||
| 306 | |||
| 307 | for (i = 0; i < 1024; i++) { | ||
| 308 | if (0 == rsnd_dmapp_read(dma, PDMACHCR)) | ||
| 309 | return; | ||
| 310 | udelay(1); | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | static void rsnd_dmapp_start(struct rsnd_dma *dma) | ||
| 315 | { | ||
| 316 | struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); | ||
| 317 | |||
| 318 | rsnd_dmapp_write(dma, dma->src_addr, PDMASAR); | ||
| 319 | rsnd_dmapp_write(dma, dma->dst_addr, PDMADAR); | ||
| 320 | rsnd_dmapp_write(dma, dmapp->chcr, PDMACHCR); | ||
| 321 | } | ||
| 322 | |||
| 323 | static int rsnd_dmapp_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, | ||
| 324 | struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) | ||
| 325 | { | ||
| 326 | struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); | ||
| 327 | struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); | ||
| 328 | struct device *dev = rsnd_priv_to_dev(priv); | ||
| 329 | |||
| 330 | dmapp->dmapp_id = dmac->dmapp_num; | ||
| 331 | dmapp->chcr = rsnd_dmapp_get_chcr(mod_from, mod_to) | PDMACHCR_DE; | ||
| 332 | |||
| 333 | dmac->dmapp_num++; | ||
| 334 | |||
| 335 | rsnd_dmapp_stop(dma); | ||
| 336 | |||
| 337 | dev_dbg(dev, "id/src/dst/chcr = %d/%pad/%pad/%08x\n", | ||
| 338 | dmapp->dmapp_id, &dma->src_addr, &dma->dst_addr, dmapp->chcr); | ||
| 339 | |||
| 340 | return 0; | ||
| 341 | } | ||
| 342 | |||
| 343 | static struct rsnd_dma_ops rsnd_dmapp_ops = { | ||
| 344 | .start = rsnd_dmapp_start, | ||
| 345 | .stop = rsnd_dmapp_stop, | ||
| 346 | .init = rsnd_dmapp_init, | ||
| 347 | .quit = rsnd_dmapp_stop, | ||
| 348 | }; | ||
| 349 | |||
| 350 | /* | ||
| 351 | * Common DMAC Interface | ||
| 352 | */ | ||
| 353 | |||
| 354 | /* | ||
| 355 | * DMA read/write register offset | ||
| 356 | * | ||
| 357 | * RSND_xxx_I_N for Audio DMAC input | ||
| 358 | * RSND_xxx_O_N for Audio DMAC output | ||
| 359 | * RSND_xxx_I_P for Audio DMAC peri peri input | ||
| 360 | * RSND_xxx_O_P for Audio DMAC peri peri output | ||
| 361 | * | ||
| 362 | * ex) R-Car H2 case | ||
| 363 | * mod / DMAC in / DMAC out / DMAC PP in / DMAC pp out | ||
| 364 | * SSI : 0xec541000 / 0xec241008 / 0xec24100c | ||
| 365 | * SSIU: 0xec541000 / 0xec100000 / 0xec100000 / 0xec400000 / 0xec400000 | ||
| 366 | * SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000 | ||
| 367 | * CMD : 0xec500000 / / 0xec008000 0xec308000 | ||
| 368 | */ | ||
| 369 | #define RDMA_SSI_I_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0x8) | ||
| 370 | #define RDMA_SSI_O_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0xc) | ||
| 371 | |||
| 372 | #define RDMA_SSIU_I_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i)) | ||
| 373 | #define RDMA_SSIU_O_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i)) | ||
| 374 | |||
| 375 | #define RDMA_SSIU_I_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) | ||
| 376 | #define RDMA_SSIU_O_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) | ||
| 377 | |||
| 378 | #define RDMA_SRC_I_N(addr, i) (addr ##_reg - 0x00500000 + (0x400 * i)) | ||
| 379 | #define RDMA_SRC_O_N(addr, i) (addr ##_reg - 0x004fc000 + (0x400 * i)) | ||
| 380 | |||
| 381 | #define RDMA_SRC_I_P(addr, i) (addr ##_reg - 0x00200000 + (0x400 * i)) | ||
| 382 | #define RDMA_SRC_O_P(addr, i) (addr ##_reg - 0x001fc000 + (0x400 * i)) | ||
| 383 | |||
| 384 | #define RDMA_CMD_O_N(addr, i) (addr ##_reg - 0x004f8000 + (0x400 * i)) | ||
| 385 | #define RDMA_CMD_O_P(addr, i) (addr ##_reg - 0x001f8000 + (0x400 * i)) | ||
| 386 | |||
| 387 | static dma_addr_t | ||
| 388 | rsnd_gen2_dma_addr(struct rsnd_priv *priv, | ||
| 389 | struct rsnd_mod *mod, | ||
| 390 | int is_play, int is_from) | ||
| 391 | { | ||
| 392 | struct device *dev = rsnd_priv_to_dev(priv); | ||
| 393 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
| 394 | phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SSI); | ||
| 395 | phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SCU); | ||
| 396 | int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod); | ||
| 397 | int use_src = !!rsnd_io_to_mod_src(io); | ||
| 398 | int use_dvc = !!rsnd_io_to_mod_dvc(io); | ||
| 399 | int id = rsnd_mod_id(mod); | ||
| 400 | struct dma_addr { | ||
| 401 | dma_addr_t out_addr; | ||
| 402 | dma_addr_t in_addr; | ||
| 403 | } dma_addrs[3][2][3] = { | ||
| 404 | /* SRC */ | ||
| 405 | {{{ 0, 0 }, | ||
| 406 | /* Capture */ | ||
| 407 | { RDMA_SRC_O_N(src, id), RDMA_SRC_I_P(src, id) }, | ||
| 408 | { RDMA_CMD_O_N(src, id), RDMA_SRC_I_P(src, id) } }, | ||
| 409 | /* Playback */ | ||
| 410 | {{ 0, 0, }, | ||
| 411 | { RDMA_SRC_O_P(src, id), RDMA_SRC_I_N(src, id) }, | ||
| 412 | { RDMA_CMD_O_P(src, id), RDMA_SRC_I_N(src, id) } } | ||
| 413 | }, | ||
| 414 | /* SSI */ | ||
| 415 | /* Capture */ | ||
| 416 | {{{ RDMA_SSI_O_N(ssi, id), 0 }, | ||
| 417 | { RDMA_SSIU_O_P(ssi, id), 0 }, | ||
| 418 | { RDMA_SSIU_O_P(ssi, id), 0 } }, | ||
| 419 | /* Playback */ | ||
| 420 | {{ 0, RDMA_SSI_I_N(ssi, id) }, | ||
| 421 | { 0, RDMA_SSIU_I_P(ssi, id) }, | ||
| 422 | { 0, RDMA_SSIU_I_P(ssi, id) } } | ||
| 423 | }, | ||
| 424 | /* SSIU */ | ||
| 425 | /* Capture */ | ||
| 426 | {{{ RDMA_SSIU_O_N(ssi, id), 0 }, | ||
| 427 | { RDMA_SSIU_O_P(ssi, id), 0 }, | ||
| 428 | { RDMA_SSIU_O_P(ssi, id), 0 } }, | ||
| 429 | /* Playback */ | ||
| 430 | {{ 0, RDMA_SSIU_I_N(ssi, id) }, | ||
| 431 | { 0, RDMA_SSIU_I_P(ssi, id) }, | ||
| 432 | { 0, RDMA_SSIU_I_P(ssi, id) } } }, | ||
| 433 | }; | ||
| 434 | |||
| 435 | /* it shouldn't happen */ | ||
| 436 | if (use_dvc && !use_src) | ||
| 437 | dev_err(dev, "DVC is selected without SRC\n"); | ||
| 438 | |||
| 439 | /* use SSIU or SSI ? */ | ||
| 440 | if (is_ssi && rsnd_ssi_use_busif(mod)) | ||
| 441 | is_ssi++; | ||
| 442 | |||
| 443 | return (is_from) ? | ||
| 444 | dma_addrs[is_ssi][is_play][use_src + use_dvc].out_addr : | ||
| 445 | dma_addrs[is_ssi][is_play][use_src + use_dvc].in_addr; | ||
| 446 | } | ||
| 447 | |||
| 448 | static dma_addr_t rsnd_dma_addr(struct rsnd_priv *priv, | ||
| 449 | struct rsnd_mod *mod, | ||
| 450 | int is_play, int is_from) | ||
| 451 | { | ||
| 452 | /* | ||
| 453 | * gen1 uses default DMA addr | ||
| 454 | */ | ||
| 455 | if (rsnd_is_gen1(priv)) | ||
| 456 | return 0; | ||
| 457 | |||
| 458 | if (!mod) | ||
| 459 | return 0; | ||
| 460 | |||
| 461 | return rsnd_gen2_dma_addr(priv, mod, is_play, is_from); | ||
| 462 | } | ||
| 463 | |||
| 464 | #define MOD_MAX 4 /* MEM/SSI/SRC/DVC */ | ||
| 465 | static void rsnd_dma_of_path(struct rsnd_dma *dma, | ||
| 466 | int is_play, | ||
| 467 | struct rsnd_mod **mod_from, | ||
| 468 | struct rsnd_mod **mod_to) | ||
| 469 | { | ||
| 470 | struct rsnd_mod *this = rsnd_dma_to_mod(dma); | ||
| 471 | struct rsnd_dai_stream *io = rsnd_mod_to_io(this); | ||
| 472 | struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); | ||
| 473 | struct rsnd_mod *src = rsnd_io_to_mod_src(io); | ||
| 474 | struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); | ||
| 475 | struct rsnd_mod *mod[MOD_MAX]; | ||
| 476 | int i, index; | ||
| 477 | |||
| 478 | |||
| 479 | for (i = 0; i < MOD_MAX; i++) | ||
| 480 | mod[i] = NULL; | ||
| 481 | |||
| 482 | /* | ||
| 483 | * in play case... | ||
| 484 | * | ||
| 485 | * src -> dst | ||
| 486 | * | ||
| 487 | * mem -> SSI | ||
| 488 | * mem -> SRC -> SSI | ||
| 489 | * mem -> SRC -> DVC -> SSI | ||
| 490 | */ | ||
| 491 | mod[0] = NULL; /* for "mem" */ | ||
| 492 | index = 1; | ||
| 493 | for (i = 1; i < MOD_MAX; i++) { | ||
| 494 | if (!src) { | ||
| 495 | mod[i] = ssi; | ||
| 496 | } else if (!dvc) { | ||
| 497 | mod[i] = src; | ||
| 498 | src = NULL; | ||
| 499 | } else { | ||
| 500 | if ((!is_play) && (this == src)) | ||
| 501 | this = dvc; | ||
| 502 | |||
| 503 | mod[i] = (is_play) ? src : dvc; | ||
| 504 | i++; | ||
| 505 | mod[i] = (is_play) ? dvc : src; | ||
| 506 | src = NULL; | ||
| 507 | dvc = NULL; | ||
| 508 | } | ||
| 509 | |||
| 510 | if (mod[i] == this) | ||
| 511 | index = i; | ||
| 512 | |||
| 513 | if (mod[i] == ssi) | ||
| 514 | break; | ||
| 515 | } | ||
| 516 | |||
| 517 | if (is_play) { | ||
| 518 | *mod_from = mod[index - 1]; | ||
| 519 | *mod_to = mod[index]; | ||
| 520 | } else { | ||
| 521 | *mod_from = mod[index]; | ||
| 522 | *mod_to = mod[index - 1]; | ||
| 523 | } | ||
| 524 | } | ||
| 525 | |||
| 526 | void rsnd_dma_stop(struct rsnd_dma *dma) | ||
| 527 | { | ||
| 528 | dma->ops->stop(dma); | ||
| 529 | } | ||
| 530 | |||
| 531 | void rsnd_dma_start(struct rsnd_dma *dma) | ||
| 532 | { | ||
| 533 | dma->ops->start(dma); | ||
| 534 | } | ||
| 535 | |||
| 536 | void rsnd_dma_quit(struct rsnd_dma *dma) | ||
| 537 | { | ||
| 538 | struct rsnd_mod *mod = rsnd_dma_to_mod(dma); | ||
| 539 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
| 540 | struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); | ||
| 541 | |||
| 542 | if (!dmac) | ||
| 543 | return; | ||
| 544 | |||
| 545 | dma->ops->quit(dma); | ||
| 546 | } | ||
| 547 | |||
| 548 | int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id) | ||
| 549 | { | ||
| 550 | struct rsnd_mod *mod = rsnd_dma_to_mod(dma); | ||
| 551 | struct rsnd_mod *mod_from; | ||
| 552 | struct rsnd_mod *mod_to; | ||
| 553 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
| 554 | struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); | ||
| 555 | int is_play = rsnd_io_is_play(io); | ||
| 556 | |||
| 557 | /* | ||
| 558 | * DMA failed. try to PIO mode | ||
| 559 | * see | ||
| 560 | * rsnd_ssi_fallback() | ||
| 561 | * rsnd_rdai_continuance_probe() | ||
| 562 | */ | ||
| 563 | if (!dmac) | ||
| 564 | return -EAGAIN; | ||
| 565 | |||
| 566 | rsnd_dma_of_path(dma, is_play, &mod_from, &mod_to); | ||
| 567 | |||
| 568 | dma->src_addr = rsnd_dma_addr(priv, mod_from, is_play, 1); | ||
| 569 | dma->dst_addr = rsnd_dma_addr(priv, mod_to, is_play, 0); | ||
| 570 | |||
| 571 | /* for Gen2 */ | ||
| 572 | if (mod_from && mod_to) | ||
| 573 | dma->ops = &rsnd_dmapp_ops; | ||
| 574 | else | ||
| 575 | dma->ops = &rsnd_dmaen_ops; | ||
| 576 | |||
| 577 | /* for Gen1, overwrite */ | ||
| 578 | if (rsnd_is_gen1(priv)) | ||
| 579 | dma->ops = &rsnd_dmaen_ops; | ||
| 580 | |||
| 581 | return dma->ops->init(priv, dma, id, mod_from, mod_to); | ||
| 582 | } | ||
| 583 | |||
| 584 | int rsnd_dma_probe(struct platform_device *pdev, | ||
| 585 | const struct rsnd_of_data *of_data, | ||
| 586 | struct rsnd_priv *priv) | ||
| 587 | { | ||
| 588 | struct device *dev = rsnd_priv_to_dev(priv); | ||
| 589 | struct rsnd_dma_ctrl *dmac; | ||
| 590 | struct resource *res; | ||
| 591 | |||
| 592 | /* | ||
| 593 | * for Gen1 | ||
| 594 | */ | ||
| 595 | if (rsnd_is_gen1(priv)) | ||
| 596 | return 0; | ||
| 597 | |||
| 598 | /* | ||
| 599 | * for Gen2 | ||
| 600 | */ | ||
| 601 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audmapp"); | ||
| 602 | dmac = devm_kzalloc(dev, sizeof(*dmac), GFP_KERNEL); | ||
| 603 | if (!dmac || !res) { | ||
| 604 | dev_err(dev, "dma allocate failed\n"); | ||
| 605 | return 0; /* it will be PIO mode */ | ||
| 606 | } | ||
| 607 | |||
| 608 | dmac->dmapp_num = 0; | ||
| 609 | dmac->base = devm_ioremap_resource(dev, res); | ||
| 610 | if (IS_ERR(dmac->base)) | ||
| 611 | return PTR_ERR(dmac->base); | ||
| 612 | |||
| 613 | priv->dma = dmac; | ||
| 614 | |||
| 615 | return 0; | ||
| 616 | } | ||
diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 261997a3f589..e5fcb062ad77 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c | |||
| @@ -24,6 +24,9 @@ struct rsnd_dvc { | |||
| 24 | struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */ | 24 | struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */ |
| 25 | }; | 25 | }; |
| 26 | 26 | ||
| 27 | #define rsnd_dvc_of_node(priv) \ | ||
| 28 | of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc") | ||
| 29 | |||
| 27 | #define rsnd_mod_to_dvc(_mod) \ | 30 | #define rsnd_mod_to_dvc(_mod) \ |
| 28 | container_of((_mod), struct rsnd_dvc, mod) | 31 | container_of((_mod), struct rsnd_dvc, mod) |
| 29 | 32 | ||
| @@ -33,7 +36,7 @@ struct rsnd_dvc { | |||
| 33 | ((pos) = (struct rsnd_dvc *)(priv)->dvc + i); \ | 36 | ((pos) = (struct rsnd_dvc *)(priv)->dvc + i); \ |
| 34 | i++) | 37 | i++) |
| 35 | 38 | ||
| 36 | static const char const *dvc_ramp_rate[] = { | 39 | static const char * const dvc_ramp_rate[] = { |
| 37 | "128 dB/1 step", /* 00000 */ | 40 | "128 dB/1 step", /* 00000 */ |
| 38 | "64 dB/1 step", /* 00001 */ | 41 | "64 dB/1 step", /* 00001 */ |
| 39 | "32 dB/1 step", /* 00010 */ | 42 | "32 dB/1 step", /* 00010 */ |
| @@ -116,17 +119,6 @@ static void rsnd_dvc_volume_update(struct rsnd_mod *mod) | |||
| 116 | rsnd_mod_write(mod, DVC_DVUER, 1); | 119 | rsnd_mod_write(mod, DVC_DVUER, 1); |
| 117 | } | 120 | } |
| 118 | 121 | ||
| 119 | static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod, | ||
| 120 | struct rsnd_priv *priv) | ||
| 121 | { | ||
| 122 | struct device *dev = rsnd_priv_to_dev(priv); | ||
| 123 | |||
| 124 | dev_dbg(dev, "%s[%d] (Gen2) is probed\n", | ||
| 125 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
| 126 | |||
| 127 | return 0; | ||
| 128 | } | ||
| 129 | |||
| 130 | static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod, | 122 | static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod, |
| 131 | struct rsnd_priv *priv) | 123 | struct rsnd_priv *priv) |
| 132 | { | 124 | { |
| @@ -269,9 +261,17 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, | |||
| 269 | return 0; | 261 | return 0; |
| 270 | } | 262 | } |
| 271 | 263 | ||
| 264 | static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_mod *mod) | ||
| 265 | { | ||
| 266 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
| 267 | |||
| 268 | return rsnd_dma_request_channel(rsnd_dvc_of_node(priv), | ||
| 269 | mod, "tx"); | ||
| 270 | } | ||
| 271 | |||
| 272 | static struct rsnd_mod_ops rsnd_dvc_ops = { | 272 | static struct rsnd_mod_ops rsnd_dvc_ops = { |
| 273 | .name = DVC_NAME, | 273 | .name = DVC_NAME, |
| 274 | .probe = rsnd_dvc_probe_gen2, | 274 | .dma_req = rsnd_dvc_dma_req, |
| 275 | .remove = rsnd_dvc_remove_gen2, | 275 | .remove = rsnd_dvc_remove_gen2, |
| 276 | .init = rsnd_dvc_init, | 276 | .init = rsnd_dvc_init, |
| 277 | .quit = rsnd_dvc_quit, | 277 | .quit = rsnd_dvc_quit, |
| @@ -370,8 +370,6 @@ int rsnd_dvc_probe(struct platform_device *pdev, | |||
| 370 | clk, RSND_MOD_DVC, i); | 370 | clk, RSND_MOD_DVC, i); |
| 371 | if (ret) | 371 | if (ret) |
| 372 | return ret; | 372 | return ret; |
| 373 | |||
| 374 | dev_dbg(dev, "CMD%d probed\n", i); | ||
| 375 | } | 373 | } |
| 376 | 374 | ||
| 377 | return 0; | 375 | return 0; |
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index de0685f2abae..8c7dc51b1c4f 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c | |||
| @@ -28,6 +28,7 @@ struct rsnd_gen { | |||
| 28 | 28 | ||
| 29 | struct regmap *regmap[RSND_BASE_MAX]; | 29 | struct regmap *regmap[RSND_BASE_MAX]; |
| 30 | struct regmap_field *regs[RSND_REG_MAX]; | 30 | struct regmap_field *regs[RSND_REG_MAX]; |
| 31 | phys_addr_t res[RSND_REG_MAX]; | ||
| 31 | }; | 32 | }; |
| 32 | 33 | ||
| 33 | #define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) | 34 | #define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) |
| @@ -118,11 +119,19 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, | |||
| 118 | mask, data); | 119 | mask, data); |
| 119 | } | 120 | } |
| 120 | 121 | ||
| 121 | #define rsnd_gen_regmap_init(priv, id_size, reg_id, conf) \ | 122 | phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id) |
| 122 | _rsnd_gen_regmap_init(priv, id_size, reg_id, conf, ARRAY_SIZE(conf)) | 123 | { |
| 124 | struct rsnd_gen *gen = rsnd_priv_to_gen(priv); | ||
| 125 | |||
| 126 | return gen->res[reg_id]; | ||
| 127 | } | ||
| 128 | |||
| 129 | #define rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf) \ | ||
| 130 | _rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf, ARRAY_SIZE(conf)) | ||
| 123 | static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, | 131 | static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, |
| 124 | int id_size, | 132 | int id_size, |
| 125 | int reg_id, | 133 | int reg_id, |
| 134 | const char *name, | ||
| 126 | struct rsnd_regmap_field_conf *conf, | 135 | struct rsnd_regmap_field_conf *conf, |
| 127 | int conf_size) | 136 | int conf_size) |
| 128 | { | 137 | { |
| @@ -141,8 +150,11 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, | |||
| 141 | regc.reg_bits = 32; | 150 | regc.reg_bits = 32; |
| 142 | regc.val_bits = 32; | 151 | regc.val_bits = 32; |
| 143 | regc.reg_stride = 4; | 152 | regc.reg_stride = 4; |
| 153 | regc.name = name; | ||
| 144 | 154 | ||
| 145 | res = platform_get_resource(pdev, IORESOURCE_MEM, reg_id); | 155 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); |
| 156 | if (!res) | ||
| 157 | res = platform_get_resource(pdev, IORESOURCE_MEM, reg_id); | ||
| 146 | if (!res) | 158 | if (!res) |
| 147 | return -ENODEV; | 159 | return -ENODEV; |
| 148 | 160 | ||
| @@ -156,6 +168,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, | |||
| 156 | 168 | ||
| 157 | gen->base[reg_id] = base; | 169 | gen->base[reg_id] = base; |
| 158 | gen->regmap[reg_id] = regmap; | 170 | gen->regmap[reg_id] = regmap; |
| 171 | gen->res[reg_id] = res->start; | ||
| 159 | 172 | ||
| 160 | for (i = 0; i < conf_size; i++) { | 173 | for (i = 0; i < conf_size; i++) { |
| 161 | 174 | ||
| @@ -176,125 +189,11 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, | |||
| 176 | } | 189 | } |
| 177 | 190 | ||
| 178 | /* | 191 | /* |
| 179 | * DMA read/write register offset | ||
| 180 | * | ||
| 181 | * RSND_xxx_I_N for Audio DMAC input | ||
| 182 | * RSND_xxx_O_N for Audio DMAC output | ||
| 183 | * RSND_xxx_I_P for Audio DMAC peri peri input | ||
| 184 | * RSND_xxx_O_P for Audio DMAC peri peri output | ||
| 185 | * | ||
| 186 | * ex) R-Car H2 case | ||
| 187 | * mod / DMAC in / DMAC out / DMAC PP in / DMAC pp out | ||
| 188 | * SSI : 0xec541000 / 0xec241008 / 0xec24100c | ||
| 189 | * SSIU: 0xec541000 / 0xec100000 / 0xec100000 / 0xec400000 / 0xec400000 | ||
| 190 | * SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000 | ||
| 191 | * CMD : 0xec500000 / / 0xec008000 0xec308000 | ||
| 192 | */ | ||
| 193 | #define RDMA_SSI_I_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0x8) | ||
| 194 | #define RDMA_SSI_O_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0xc) | ||
| 195 | |||
| 196 | #define RDMA_SSIU_I_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i)) | ||
| 197 | #define RDMA_SSIU_O_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i)) | ||
| 198 | |||
| 199 | #define RDMA_SSIU_I_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) | ||
| 200 | #define RDMA_SSIU_O_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) | ||
| 201 | |||
| 202 | #define RDMA_SRC_I_N(addr, i) (addr ##_reg - 0x00500000 + (0x400 * i)) | ||
| 203 | #define RDMA_SRC_O_N(addr, i) (addr ##_reg - 0x004fc000 + (0x400 * i)) | ||
| 204 | |||
| 205 | #define RDMA_SRC_I_P(addr, i) (addr ##_reg - 0x00200000 + (0x400 * i)) | ||
| 206 | #define RDMA_SRC_O_P(addr, i) (addr ##_reg - 0x001fc000 + (0x400 * i)) | ||
| 207 | |||
| 208 | #define RDMA_CMD_O_N(addr, i) (addr ##_reg - 0x004f8000 + (0x400 * i)) | ||
| 209 | #define RDMA_CMD_O_P(addr, i) (addr ##_reg - 0x001f8000 + (0x400 * i)) | ||
| 210 | |||
| 211 | static dma_addr_t | ||
| 212 | rsnd_gen2_dma_addr(struct rsnd_priv *priv, | ||
| 213 | struct rsnd_mod *mod, | ||
| 214 | int is_play, int is_from) | ||
| 215 | { | ||
| 216 | struct platform_device *pdev = rsnd_priv_to_pdev(priv); | ||
| 217 | struct device *dev = rsnd_priv_to_dev(priv); | ||
| 218 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
| 219 | dma_addr_t ssi_reg = platform_get_resource(pdev, | ||
| 220 | IORESOURCE_MEM, RSND_GEN2_SSI)->start; | ||
| 221 | dma_addr_t src_reg = platform_get_resource(pdev, | ||
| 222 | IORESOURCE_MEM, RSND_GEN2_SCU)->start; | ||
| 223 | int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod); | ||
| 224 | int use_src = !!rsnd_io_to_mod_src(io); | ||
| 225 | int use_dvc = !!rsnd_io_to_mod_dvc(io); | ||
| 226 | int id = rsnd_mod_id(mod); | ||
| 227 | struct dma_addr { | ||
| 228 | dma_addr_t out_addr; | ||
| 229 | dma_addr_t in_addr; | ||
| 230 | } dma_addrs[3][2][3] = { | ||
| 231 | /* SRC */ | ||
| 232 | {{{ 0, 0 }, | ||
| 233 | /* Capture */ | ||
| 234 | { RDMA_SRC_O_N(src, id), RDMA_SRC_I_P(src, id) }, | ||
| 235 | { RDMA_CMD_O_N(src, id), RDMA_SRC_I_P(src, id) } }, | ||
| 236 | /* Playback */ | ||
| 237 | {{ 0, 0, }, | ||
| 238 | { RDMA_SRC_O_P(src, id), RDMA_SRC_I_N(src, id) }, | ||
| 239 | { RDMA_CMD_O_P(src, id), RDMA_SRC_I_N(src, id) } } | ||
| 240 | }, | ||
| 241 | /* SSI */ | ||
| 242 | /* Capture */ | ||
| 243 | {{{ RDMA_SSI_O_N(ssi, id), 0 }, | ||
| 244 | { RDMA_SSIU_O_P(ssi, id), 0 }, | ||
| 245 | { RDMA_SSIU_O_P(ssi, id), 0 } }, | ||
| 246 | /* Playback */ | ||
| 247 | {{ 0, RDMA_SSI_I_N(ssi, id) }, | ||
| 248 | { 0, RDMA_SSIU_I_P(ssi, id) }, | ||
| 249 | { 0, RDMA_SSIU_I_P(ssi, id) } } | ||
| 250 | }, | ||
| 251 | /* SSIU */ | ||
| 252 | /* Capture */ | ||
| 253 | {{{ RDMA_SSIU_O_N(ssi, id), 0 }, | ||
| 254 | { RDMA_SSIU_O_P(ssi, id), 0 }, | ||
| 255 | { RDMA_SSIU_O_P(ssi, id), 0 } }, | ||
| 256 | /* Playback */ | ||
| 257 | {{ 0, RDMA_SSIU_I_N(ssi, id) }, | ||
| 258 | { 0, RDMA_SSIU_I_P(ssi, id) }, | ||
| 259 | { 0, RDMA_SSIU_I_P(ssi, id) } } }, | ||
| 260 | }; | ||
| 261 | |||
| 262 | /* it shouldn't happen */ | ||
| 263 | if (use_dvc && !use_src) | ||
| 264 | dev_err(dev, "DVC is selected without SRC\n"); | ||
| 265 | |||
| 266 | /* use SSIU or SSI ? */ | ||
| 267 | if (is_ssi && (0 == strcmp(rsnd_mod_dma_name(mod), "ssiu"))) | ||
| 268 | is_ssi++; | ||
| 269 | |||
| 270 | return (is_from) ? | ||
| 271 | dma_addrs[is_ssi][is_play][use_src + use_dvc].out_addr : | ||
| 272 | dma_addrs[is_ssi][is_play][use_src + use_dvc].in_addr; | ||
| 273 | } | ||
| 274 | |||
| 275 | dma_addr_t rsnd_gen_dma_addr(struct rsnd_priv *priv, | ||
| 276 | struct rsnd_mod *mod, | ||
| 277 | int is_play, int is_from) | ||
| 278 | { | ||
| 279 | /* | ||
| 280 | * gen1 uses default DMA addr | ||
| 281 | */ | ||
| 282 | if (rsnd_is_gen1(priv)) | ||
| 283 | return 0; | ||
| 284 | |||
| 285 | if (!mod) | ||
| 286 | return 0; | ||
| 287 | |||
| 288 | return rsnd_gen2_dma_addr(priv, mod, is_play, is_from); | ||
| 289 | } | ||
| 290 | |||
| 291 | /* | ||
| 292 | * Gen2 | 192 | * Gen2 |
| 293 | */ | 193 | */ |
| 294 | static int rsnd_gen2_probe(struct platform_device *pdev, | 194 | static int rsnd_gen2_probe(struct platform_device *pdev, |
| 295 | struct rsnd_priv *priv) | 195 | struct rsnd_priv *priv) |
| 296 | { | 196 | { |
| 297 | struct device *dev = rsnd_priv_to_dev(priv); | ||
| 298 | struct rsnd_regmap_field_conf conf_ssiu[] = { | 197 | struct rsnd_regmap_field_conf conf_ssiu[] = { |
| 299 | RSND_GEN_S_REG(SSI_MODE0, 0x800), | 198 | RSND_GEN_S_REG(SSI_MODE0, 0x800), |
| 300 | RSND_GEN_S_REG(SSI_MODE1, 0x804), | 199 | RSND_GEN_S_REG(SSI_MODE1, 0x804), |
| @@ -368,18 +267,16 @@ static int rsnd_gen2_probe(struct platform_device *pdev, | |||
| 368 | int ret_adg; | 267 | int ret_adg; |
| 369 | int ret_ssi; | 268 | int ret_ssi; |
| 370 | 269 | ||
| 371 | ret_ssiu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSIU, conf_ssiu); | 270 | ret_ssiu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSIU, "ssiu", conf_ssiu); |
| 372 | ret_scu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SCU, conf_scu); | 271 | ret_scu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SCU, "scu", conf_scu); |
| 373 | ret_adg = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_ADG, conf_adg); | 272 | ret_adg = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_ADG, "adg", conf_adg); |
| 374 | ret_ssi = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSI, conf_ssi); | 273 | ret_ssi = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSI, "ssi", conf_ssi); |
| 375 | if (ret_ssiu < 0 || | 274 | if (ret_ssiu < 0 || |
| 376 | ret_scu < 0 || | 275 | ret_scu < 0 || |
| 377 | ret_adg < 0 || | 276 | ret_adg < 0 || |
| 378 | ret_ssi < 0) | 277 | ret_ssi < 0) |
| 379 | return ret_ssiu | ret_scu | ret_adg | ret_ssi; | 278 | return ret_ssiu | ret_scu | ret_adg | ret_ssi; |
| 380 | 279 | ||
| 381 | dev_dbg(dev, "Gen2 is probed\n"); | ||
| 382 | |||
| 383 | return 0; | 280 | return 0; |
| 384 | } | 281 | } |
| 385 | 282 | ||
| @@ -390,7 +287,6 @@ static int rsnd_gen2_probe(struct platform_device *pdev, | |||
| 390 | static int rsnd_gen1_probe(struct platform_device *pdev, | 287 | static int rsnd_gen1_probe(struct platform_device *pdev, |
| 391 | struct rsnd_priv *priv) | 288 | struct rsnd_priv *priv) |
| 392 | { | 289 | { |
| 393 | struct device *dev = rsnd_priv_to_dev(priv); | ||
| 394 | struct rsnd_regmap_field_conf conf_sru[] = { | 290 | struct rsnd_regmap_field_conf conf_sru[] = { |
| 395 | RSND_GEN_S_REG(SRC_ROUTE_SEL, 0x00), | 291 | RSND_GEN_S_REG(SRC_ROUTE_SEL, 0x00), |
| 396 | RSND_GEN_S_REG(SRC_TMG_SEL0, 0x08), | 292 | RSND_GEN_S_REG(SRC_TMG_SEL0, 0x08), |
| @@ -440,16 +336,14 @@ static int rsnd_gen1_probe(struct platform_device *pdev, | |||
| 440 | int ret_adg; | 336 | int ret_adg; |
| 441 | int ret_ssi; | 337 | int ret_ssi; |
| 442 | 338 | ||
| 443 | ret_sru = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SRU, conf_sru); | 339 | ret_sru = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SRU, "sru", conf_sru); |
| 444 | ret_adg = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_ADG, conf_adg); | 340 | ret_adg = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_ADG, "adg", conf_adg); |
| 445 | ret_ssi = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SSI, conf_ssi); | 341 | ret_ssi = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SSI, "ssi", conf_ssi); |
| 446 | if (ret_sru < 0 || | 342 | if (ret_sru < 0 || |
| 447 | ret_adg < 0 || | 343 | ret_adg < 0 || |
| 448 | ret_ssi < 0) | 344 | ret_ssi < 0) |
| 449 | return ret_sru | ret_adg | ret_ssi; | 345 | return ret_sru | ret_adg | ret_ssi; |
| 450 | 346 | ||
| 451 | dev_dbg(dev, "Gen1 is probed\n"); | ||
| 452 | |||
| 453 | return 0; | 347 | return 0; |
| 454 | } | 348 | } |
| 455 | 349 | ||
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 1bccc5515b5a..4e6de6804cfb 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h | |||
| @@ -170,21 +170,47 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod); | |||
| 170 | /* | 170 | /* |
| 171 | * R-Car DMA | 171 | * R-Car DMA |
| 172 | */ | 172 | */ |
| 173 | struct rsnd_dma { | 173 | struct rsnd_dma; |
| 174 | struct sh_dmae_slave slave; | 174 | struct rsnd_dma_ops { |
| 175 | void (*start)(struct rsnd_dma *dma); | ||
| 176 | void (*stop)(struct rsnd_dma *dma); | ||
| 177 | int (*init)(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, | ||
| 178 | struct rsnd_mod *mod_from, struct rsnd_mod *mod_to); | ||
| 179 | void (*quit)(struct rsnd_dma *dma); | ||
| 180 | }; | ||
| 181 | |||
| 182 | struct rsnd_dmaen { | ||
| 175 | struct dma_chan *chan; | 183 | struct dma_chan *chan; |
| 176 | enum dma_transfer_direction dir; | ||
| 177 | dma_addr_t addr; | ||
| 178 | }; | 184 | }; |
| 179 | 185 | ||
| 186 | struct rsnd_dmapp { | ||
| 187 | int dmapp_id; | ||
| 188 | u32 chcr; | ||
| 189 | }; | ||
| 190 | |||
| 191 | struct rsnd_dma { | ||
| 192 | struct rsnd_dma_ops *ops; | ||
| 193 | dma_addr_t src_addr; | ||
| 194 | dma_addr_t dst_addr; | ||
| 195 | union { | ||
| 196 | struct rsnd_dmaen en; | ||
| 197 | struct rsnd_dmapp pp; | ||
| 198 | } dma; | ||
| 199 | }; | ||
| 200 | #define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en) | ||
| 201 | #define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp) | ||
| 202 | |||
| 180 | void rsnd_dma_start(struct rsnd_dma *dma); | 203 | void rsnd_dma_start(struct rsnd_dma *dma); |
| 181 | void rsnd_dma_stop(struct rsnd_dma *dma); | 204 | void rsnd_dma_stop(struct rsnd_dma *dma); |
| 182 | int rsnd_dma_available(struct rsnd_dma *dma); | 205 | int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id); |
| 183 | int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, | 206 | void rsnd_dma_quit(struct rsnd_dma *dma); |
| 184 | int is_play, int id); | 207 | int rsnd_dma_probe(struct platform_device *pdev, |
| 185 | void rsnd_dma_quit(struct rsnd_priv *priv, | 208 | const struct rsnd_of_data *of_data, |
| 186 | struct rsnd_dma *dma); | 209 | struct rsnd_priv *priv); |
| 210 | struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, | ||
| 211 | struct rsnd_mod *mod, char *name); | ||
| 187 | 212 | ||
| 213 | #define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) | ||
| 188 | 214 | ||
| 189 | /* | 215 | /* |
| 190 | * R-Car sound mod | 216 | * R-Car sound mod |
| @@ -198,7 +224,7 @@ enum rsnd_mod_type { | |||
| 198 | 224 | ||
| 199 | struct rsnd_mod_ops { | 225 | struct rsnd_mod_ops { |
| 200 | char *name; | 226 | char *name; |
| 201 | char* (*dma_name)(struct rsnd_mod *mod); | 227 | struct dma_chan* (*dma_req)(struct rsnd_mod *mod); |
| 202 | int (*probe)(struct rsnd_mod *mod, | 228 | int (*probe)(struct rsnd_mod *mod, |
| 203 | struct rsnd_priv *priv); | 229 | struct rsnd_priv *priv); |
| 204 | int (*remove)(struct rsnd_mod *mod, | 230 | int (*remove)(struct rsnd_mod *mod, |
| @@ -213,6 +239,9 @@ struct rsnd_mod_ops { | |||
| 213 | struct rsnd_priv *priv); | 239 | struct rsnd_priv *priv); |
| 214 | int (*pcm_new)(struct rsnd_mod *mod, | 240 | int (*pcm_new)(struct rsnd_mod *mod, |
| 215 | struct snd_soc_pcm_runtime *rtd); | 241 | struct snd_soc_pcm_runtime *rtd); |
| 242 | int (*hw_params)(struct rsnd_mod *mod, | ||
| 243 | struct snd_pcm_substream *substream, | ||
| 244 | struct snd_pcm_hw_params *hw_params); | ||
| 216 | int (*fallback)(struct rsnd_mod *mod, | 245 | int (*fallback)(struct rsnd_mod *mod, |
| 217 | struct rsnd_priv *priv); | 246 | struct rsnd_priv *priv); |
| 218 | }; | 247 | }; |
| @@ -236,6 +265,9 @@ struct rsnd_mod { | |||
| 236 | * 2 0: start 1: stop | 265 | * 2 0: start 1: stop |
| 237 | * 3 0: pcm_new | 266 | * 3 0: pcm_new |
| 238 | * 4 0: fallback | 267 | * 4 0: fallback |
| 268 | * | ||
| 269 | * 31 bit is always called (see __rsnd_mod_call) | ||
| 270 | * 31 0: hw_params | ||
| 239 | */ | 271 | */ |
| 240 | #define __rsnd_mod_shift_probe 0 | 272 | #define __rsnd_mod_shift_probe 0 |
| 241 | #define __rsnd_mod_shift_remove 0 | 273 | #define __rsnd_mod_shift_remove 0 |
| @@ -245,6 +277,7 @@ struct rsnd_mod { | |||
| 245 | #define __rsnd_mod_shift_stop 2 | 277 | #define __rsnd_mod_shift_stop 2 |
| 246 | #define __rsnd_mod_shift_pcm_new 3 | 278 | #define __rsnd_mod_shift_pcm_new 3 |
| 247 | #define __rsnd_mod_shift_fallback 4 | 279 | #define __rsnd_mod_shift_fallback 4 |
| 280 | #define __rsnd_mod_shift_hw_params 31 /* always called */ | ||
| 248 | 281 | ||
| 249 | #define __rsnd_mod_call_probe 0 | 282 | #define __rsnd_mod_call_probe 0 |
| 250 | #define __rsnd_mod_call_remove 1 | 283 | #define __rsnd_mod_call_remove 1 |
| @@ -254,10 +287,10 @@ struct rsnd_mod { | |||
| 254 | #define __rsnd_mod_call_stop 1 | 287 | #define __rsnd_mod_call_stop 1 |
| 255 | #define __rsnd_mod_call_pcm_new 0 | 288 | #define __rsnd_mod_call_pcm_new 0 |
| 256 | #define __rsnd_mod_call_fallback 0 | 289 | #define __rsnd_mod_call_fallback 0 |
| 290 | #define __rsnd_mod_call_hw_params 0 | ||
| 257 | 291 | ||
| 258 | #define rsnd_mod_to_priv(mod) (rsnd_io_to_priv(rsnd_mod_to_io(mod))) | 292 | #define rsnd_mod_to_priv(mod) (rsnd_io_to_priv(rsnd_mod_to_io(mod))) |
| 259 | #define rsnd_mod_to_dma(mod) (&(mod)->dma) | 293 | #define rsnd_mod_to_dma(mod) (&(mod)->dma) |
| 260 | #define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) | ||
| 261 | #define rsnd_mod_to_io(mod) ((mod)->io) | 294 | #define rsnd_mod_to_io(mod) ((mod)->io) |
| 262 | #define rsnd_mod_id(mod) ((mod)->id) | 295 | #define rsnd_mod_id(mod) ((mod)->id) |
| 263 | #define rsnd_mod_hw_start(mod) clk_enable((mod)->clk) | 296 | #define rsnd_mod_hw_start(mod) clk_enable((mod)->clk) |
| @@ -270,13 +303,14 @@ int rsnd_mod_init(struct rsnd_mod *mod, | |||
| 270 | int id); | 303 | int id); |
| 271 | void rsnd_mod_quit(struct rsnd_mod *mod); | 304 | void rsnd_mod_quit(struct rsnd_mod *mod); |
| 272 | char *rsnd_mod_name(struct rsnd_mod *mod); | 305 | char *rsnd_mod_name(struct rsnd_mod *mod); |
| 273 | char *rsnd_mod_dma_name(struct rsnd_mod *mod); | 306 | struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod); |
| 274 | 307 | ||
| 275 | /* | 308 | /* |
| 276 | * R-Car sound DAI | 309 | * R-Car sound DAI |
| 277 | */ | 310 | */ |
| 278 | #define RSND_DAI_NAME_SIZE 16 | 311 | #define RSND_DAI_NAME_SIZE 16 |
| 279 | struct rsnd_dai_stream { | 312 | struct rsnd_dai_stream { |
| 313 | char name[RSND_DAI_NAME_SIZE]; | ||
| 280 | struct snd_pcm_substream *substream; | 314 | struct snd_pcm_substream *substream; |
| 281 | struct rsnd_mod *mod[RSND_MOD_MAX]; | 315 | struct rsnd_mod *mod[RSND_MOD_MAX]; |
| 282 | struct rsnd_dai_path_info *info; /* rcar_snd.h */ | 316 | struct rsnd_dai_path_info *info; /* rcar_snd.h */ |
| @@ -332,9 +366,7 @@ int rsnd_gen_probe(struct platform_device *pdev, | |||
| 332 | void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, | 366 | void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, |
| 333 | struct rsnd_mod *mod, | 367 | struct rsnd_mod *mod, |
| 334 | enum rsnd_reg reg); | 368 | enum rsnd_reg reg); |
| 335 | dma_addr_t rsnd_gen_dma_addr(struct rsnd_priv *priv, | 369 | phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id); |
| 336 | struct rsnd_mod *mod, | ||
| 337 | int is_play, int is_from); | ||
| 338 | 370 | ||
| 339 | #define rsnd_is_gen1(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN1) | 371 | #define rsnd_is_gen1(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN1) |
| 340 | #define rsnd_is_gen2(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN2) | 372 | #define rsnd_is_gen2(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN2) |
| @@ -390,6 +422,11 @@ struct rsnd_priv { | |||
| 390 | void *adg; | 422 | void *adg; |
| 391 | 423 | ||
| 392 | /* | 424 | /* |
| 425 | * below value will be filled on rsnd_dma_probe() | ||
| 426 | */ | ||
| 427 | void *dma; | ||
| 428 | |||
| 429 | /* | ||
| 393 | * below value will be filled on rsnd_ssi_probe() | 430 | * below value will be filled on rsnd_ssi_probe() |
| 394 | */ | 431 | */ |
| 395 | void *ssi; | 432 | void *ssi; |
| @@ -415,19 +452,6 @@ struct rsnd_priv { | |||
| 415 | #define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) | 452 | #define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) |
| 416 | #define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) | 453 | #define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) |
| 417 | 454 | ||
| 418 | #define rsnd_info_is_playback(priv, type) \ | ||
| 419 | ({ \ | ||
| 420 | struct rcar_snd_info *info = rsnd_priv_to_info(priv); \ | ||
| 421 | int i, is_play = 0; \ | ||
| 422 | for (i = 0; i < info->dai_info_nr; i++) { \ | ||
| 423 | if (info->dai_info[i].playback.type == (type)->info) { \ | ||
| 424 | is_play = 1; \ | ||
| 425 | break; \ | ||
| 426 | } \ | ||
| 427 | } \ | ||
| 428 | is_play; \ | ||
| 429 | }) | ||
| 430 | |||
| 431 | /* | 455 | /* |
| 432 | * rsnd_kctrl | 456 | * rsnd_kctrl |
| 433 | */ | 457 | */ |
| @@ -506,6 +530,7 @@ void rsnd_ssi_remove(struct platform_device *pdev, | |||
| 506 | struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); | 530 | struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); |
| 507 | int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); | 531 | int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); |
| 508 | int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); | 532 | int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); |
| 533 | int rsnd_ssi_use_busif(struct rsnd_mod *mod); | ||
| 509 | 534 | ||
| 510 | /* | 535 | /* |
| 511 | * R-Car DVC | 536 | * R-Car DVC |
diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c new file mode 100644 index 000000000000..a68517afe615 --- /dev/null +++ b/sound/soc/sh/rcar/rsrc-card.c | |||
| @@ -0,0 +1,512 @@ | |||
| 1 | /* | ||
| 2 | * Renesas Sampling Rate Convert Sound Card for DPCM | ||
| 3 | * | ||
| 4 | * Copyright (C) 2015 Renesas Solutions Corp. | ||
| 5 | * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | ||
| 6 | * | ||
| 7 | * based on ${LINUX}/sound/soc/generic/simple-card.c | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License version 2 as | ||
| 11 | * published by the Free Software Foundation. | ||
| 12 | */ | ||
| 13 | #include <linux/clk.h> | ||
| 14 | #include <linux/device.h> | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/of.h> | ||
| 17 | #include <linux/of_device.h> | ||
| 18 | #include <linux/platform_device.h> | ||
| 19 | #include <linux/string.h> | ||
| 20 | #include <sound/jack.h> | ||
| 21 | #include <sound/soc.h> | ||
| 22 | #include <sound/soc-dai.h> | ||
| 23 | |||
| 24 | struct rsrc_card_of_data { | ||
| 25 | const char *prefix; | ||
| 26 | const struct snd_soc_dapm_route *routes; | ||
| 27 | int num_routes; | ||
| 28 | }; | ||
| 29 | |||
| 30 | static const struct snd_soc_dapm_route routes_ssi0_ak4642[] = { | ||
| 31 | {"ak4642 Playback", NULL, "DAI0 Playback"}, | ||
| 32 | {"DAI0 Capture", NULL, "ak4642 Capture"}, | ||
| 33 | }; | ||
| 34 | |||
| 35 | static const struct rsrc_card_of_data routes_of_ssi0_ak4642 = { | ||
| 36 | .prefix = "ak4642", | ||
| 37 | .routes = routes_ssi0_ak4642, | ||
| 38 | .num_routes = ARRAY_SIZE(routes_ssi0_ak4642), | ||
| 39 | }; | ||
| 40 | |||
| 41 | static const struct of_device_id rsrc_card_of_match[] = { | ||
| 42 | { .compatible = "renesas,rsrc-card,lager", .data = &routes_of_ssi0_ak4642 }, | ||
| 43 | { .compatible = "renesas,rsrc-card,koelsch", .data = &routes_of_ssi0_ak4642 }, | ||
| 44 | {}, | ||
| 45 | }; | ||
| 46 | MODULE_DEVICE_TABLE(of, rsrc_card_of_match); | ||
| 47 | |||
| 48 | struct rsrc_card_dai { | ||
| 49 | const char *name; | ||
| 50 | unsigned int fmt; | ||
| 51 | unsigned int sysclk; | ||
| 52 | struct clk *clk; | ||
| 53 | }; | ||
| 54 | |||
| 55 | #define RSRC_FB_NUM 2 /* FE/BE */ | ||
| 56 | #define IDX_CPU 0 | ||
| 57 | #define IDX_CODEC 1 | ||
| 58 | struct rsrc_card_priv { | ||
| 59 | struct snd_soc_card snd_card; | ||
| 60 | struct rsrc_card_dai_props { | ||
| 61 | struct rsrc_card_dai cpu_dai; | ||
| 62 | struct rsrc_card_dai codec_dai; | ||
| 63 | } dai_props[RSRC_FB_NUM]; | ||
| 64 | struct snd_soc_codec_conf codec_conf; | ||
| 65 | struct snd_soc_dai_link dai_link[RSRC_FB_NUM]; | ||
| 66 | u32 convert_rate; | ||
| 67 | }; | ||
| 68 | |||
| 69 | #define rsrc_priv_to_dev(priv) ((priv)->snd_card.dev) | ||
| 70 | #define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i) | ||
| 71 | #define rsrc_priv_to_props(priv, i) ((priv)->dai_props + i) | ||
| 72 | #define rsrc_dev_to_of_data(dev) (of_match_device(rsrc_card_of_match, (dev))->data) | ||
| 73 | |||
| 74 | static int rsrc_card_startup(struct snd_pcm_substream *substream) | ||
| 75 | { | ||
| 76 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
| 77 | struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); | ||
| 78 | struct rsrc_card_dai_props *dai_props = | ||
| 79 | &priv->dai_props[rtd - rtd->card->rtd]; | ||
| 80 | int ret; | ||
| 81 | |||
| 82 | ret = clk_prepare_enable(dai_props->cpu_dai.clk); | ||
| 83 | if (ret) | ||
| 84 | return ret; | ||
| 85 | |||
| 86 | ret = clk_prepare_enable(dai_props->codec_dai.clk); | ||
| 87 | if (ret) | ||
| 88 | clk_disable_unprepare(dai_props->cpu_dai.clk); | ||
| 89 | |||
| 90 | return ret; | ||
| 91 | } | ||
| 92 | |||
| 93 | static void rsrc_card_shutdown(struct snd_pcm_substream *substream) | ||
| 94 | { | ||
| 95 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
| 96 | struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); | ||
| 97 | struct rsrc_card_dai_props *dai_props = | ||
| 98 | &priv->dai_props[rtd - rtd->card->rtd]; | ||
| 99 | |||
| 100 | clk_disable_unprepare(dai_props->cpu_dai.clk); | ||
| 101 | |||
| 102 | clk_disable_unprepare(dai_props->codec_dai.clk); | ||
| 103 | } | ||
| 104 | |||
| 105 | static struct snd_soc_ops rsrc_card_ops = { | ||
| 106 | .startup = rsrc_card_startup, | ||
| 107 | .shutdown = rsrc_card_shutdown, | ||
| 108 | }; | ||
| 109 | |||
| 110 | static int __rsrc_card_dai_init(struct snd_soc_dai *dai, | ||
| 111 | struct rsrc_card_dai *set) | ||
| 112 | { | ||
| 113 | int ret; | ||
| 114 | |||
| 115 | if (set->fmt) { | ||
| 116 | ret = snd_soc_dai_set_fmt(dai, set->fmt); | ||
| 117 | if (ret && ret != -ENOTSUPP) { | ||
| 118 | dev_err(dai->dev, "set_fmt error\n"); | ||
| 119 | goto err; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | if (set->sysclk) { | ||
| 124 | ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0); | ||
| 125 | if (ret && ret != -ENOTSUPP) { | ||
| 126 | dev_err(dai->dev, "set_sysclk error\n"); | ||
| 127 | goto err; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | ret = 0; | ||
| 132 | |||
| 133 | err: | ||
| 134 | return ret; | ||
| 135 | } | ||
| 136 | |||
| 137 | static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd) | ||
| 138 | { | ||
| 139 | struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); | ||
| 140 | struct snd_soc_dai *codec = rtd->codec_dai; | ||
| 141 | struct snd_soc_dai *cpu = rtd->cpu_dai; | ||
| 142 | struct rsrc_card_dai_props *dai_props; | ||
| 143 | int num, ret; | ||
| 144 | |||
| 145 | num = rtd - rtd->card->rtd; | ||
| 146 | dai_props = &priv->dai_props[num]; | ||
| 147 | ret = __rsrc_card_dai_init(codec, &dai_props->codec_dai); | ||
| 148 | if (ret < 0) | ||
| 149 | return ret; | ||
| 150 | |||
| 151 | ret = __rsrc_card_dai_init(cpu, &dai_props->cpu_dai); | ||
| 152 | if (ret < 0) | ||
| 153 | return ret; | ||
| 154 | |||
| 155 | return 0; | ||
| 156 | } | ||
| 157 | |||
| 158 | static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, | ||
| 159 | struct snd_pcm_hw_params *params) | ||
| 160 | { | ||
| 161 | struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); | ||
| 162 | struct snd_interval *rate = hw_param_interval(params, | ||
| 163 | SNDRV_PCM_HW_PARAM_RATE); | ||
| 164 | |||
| 165 | if (!priv->convert_rate) | ||
| 166 | return 0; | ||
| 167 | |||
| 168 | rate->min = rate->max = priv->convert_rate; | ||
| 169 | |||
| 170 | return 0; | ||
| 171 | } | ||
| 172 | |||
| 173 | static int | ||
| 174 | rsrc_card_sub_parse_of(struct rsrc_card_priv *priv, | ||
| 175 | struct device_node *np, | ||
| 176 | struct rsrc_card_dai *dai, | ||
| 177 | struct snd_soc_dai_link *dai_link, | ||
| 178 | int *args_count) | ||
| 179 | { | ||
| 180 | struct device *dev = rsrc_priv_to_dev(priv); | ||
| 181 | const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev); | ||
| 182 | struct of_phandle_args args; | ||
| 183 | struct device_node **p_node; | ||
| 184 | struct clk *clk; | ||
| 185 | const char **dai_name; | ||
| 186 | const char **name; | ||
| 187 | u32 val; | ||
| 188 | int ret; | ||
| 189 | |||
| 190 | if (args_count) { | ||
| 191 | p_node = &dai_link->cpu_of_node; | ||
| 192 | dai_name = &dai_link->cpu_dai_name; | ||
| 193 | name = &dai_link->cpu_name; | ||
| 194 | } else { | ||
| 195 | p_node = &dai_link->codec_of_node; | ||
| 196 | dai_name = &dai_link->codec_dai_name; | ||
| 197 | name = &dai_link->codec_name; | ||
| 198 | } | ||
| 199 | |||
| 200 | if (!np) { | ||
| 201 | /* use snd-soc-dummy */ | ||
| 202 | *p_node = NULL; | ||
| 203 | *dai_name = "snd-soc-dummy-dai"; | ||
| 204 | *name = "snd-soc-dummy"; | ||
| 205 | return 0; | ||
| 206 | } | ||
| 207 | |||
| 208 | /* | ||
| 209 | * Get node via "sound-dai = <&phandle port>" | ||
| 210 | * it will be used as xxx_of_node on soc_bind_dai_link() | ||
| 211 | */ | ||
| 212 | ret = of_parse_phandle_with_args(np, "sound-dai", | ||
| 213 | "#sound-dai-cells", 0, &args); | ||
| 214 | if (ret) | ||
| 215 | return ret; | ||
| 216 | |||
| 217 | *p_node = args.np; | ||
| 218 | |||
| 219 | /* Get dai->name */ | ||
| 220 | ret = snd_soc_of_get_dai_name(np, dai_name); | ||
| 221 | if (ret < 0) | ||
| 222 | return ret; | ||
| 223 | |||
| 224 | /* | ||
| 225 | * FIXME | ||
| 226 | * | ||
| 227 | * rsrc assumes DPCM playback/capture | ||
| 228 | */ | ||
| 229 | dai_link->dpcm_playback = 1; | ||
| 230 | dai_link->dpcm_capture = 1; | ||
| 231 | |||
| 232 | if (args_count) { | ||
| 233 | *args_count = args.args_count; | ||
| 234 | dai_link->dynamic = 1; | ||
| 235 | } else { | ||
| 236 | dai_link->no_pcm = 1; | ||
| 237 | priv->codec_conf.of_node = (*p_node); | ||
| 238 | priv->codec_conf.name_prefix = of_data->prefix; | ||
| 239 | } | ||
| 240 | |||
| 241 | /* | ||
| 242 | * Parse dai->sysclk come from "clocks = <&xxx>" | ||
| 243 | * (if system has common clock) | ||
| 244 | * or "system-clock-frequency = <xxx>" | ||
| 245 | * or device's module clock. | ||
| 246 | */ | ||
| 247 | if (of_property_read_bool(np, "clocks")) { | ||
| 248 | clk = of_clk_get(np, 0); | ||
| 249 | if (IS_ERR(clk)) { | ||
| 250 | ret = PTR_ERR(clk); | ||
| 251 | return ret; | ||
| 252 | } | ||
| 253 | |||
| 254 | dai->sysclk = clk_get_rate(clk); | ||
| 255 | dai->clk = clk; | ||
| 256 | } else if (!of_property_read_u32(np, "system-clock-frequency", &val)) { | ||
| 257 | dai->sysclk = val; | ||
| 258 | } else { | ||
| 259 | clk = of_clk_get(args.np, 0); | ||
| 260 | if (!IS_ERR(clk)) | ||
| 261 | dai->sysclk = clk_get_rate(clk); | ||
| 262 | } | ||
| 263 | |||
| 264 | return 0; | ||
| 265 | } | ||
| 266 | |||
| 267 | static int rsrc_card_parse_daifmt(struct device_node *node, | ||
| 268 | struct rsrc_card_priv *priv, | ||
| 269 | struct device_node *codec, | ||
| 270 | int idx) | ||
| 271 | { | ||
| 272 | struct device_node *bitclkmaster = NULL; | ||
| 273 | struct device_node *framemaster = NULL; | ||
| 274 | struct rsrc_card_dai_props *dai_props = rsrc_priv_to_props(priv, idx); | ||
| 275 | struct rsrc_card_dai *cpu_dai = &dai_props->cpu_dai; | ||
| 276 | struct rsrc_card_dai *codec_dai = &dai_props->codec_dai; | ||
| 277 | unsigned int daifmt; | ||
| 278 | |||
| 279 | daifmt = snd_soc_of_parse_daifmt(node, NULL, | ||
| 280 | &bitclkmaster, &framemaster); | ||
| 281 | daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; | ||
| 282 | |||
| 283 | if (!bitclkmaster && !framemaster) | ||
| 284 | return -EINVAL; | ||
| 285 | |||
| 286 | if (codec == bitclkmaster) | ||
| 287 | daifmt |= (codec == framemaster) ? | ||
| 288 | SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS; | ||
| 289 | else | ||
| 290 | daifmt |= (codec == framemaster) ? | ||
| 291 | SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS; | ||
| 292 | |||
| 293 | cpu_dai->fmt = daifmt; | ||
| 294 | codec_dai->fmt = daifmt; | ||
| 295 | |||
| 296 | of_node_put(bitclkmaster); | ||
| 297 | of_node_put(framemaster); | ||
| 298 | |||
| 299 | return 0; | ||
| 300 | } | ||
| 301 | |||
| 302 | static int rsrc_card_dai_link_of(struct device_node *node, | ||
| 303 | struct rsrc_card_priv *priv, | ||
| 304 | int idx) | ||
| 305 | { | ||
| 306 | struct device *dev = rsrc_priv_to_dev(priv); | ||
| 307 | struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx); | ||
| 308 | struct rsrc_card_dai_props *dai_props = rsrc_priv_to_props(priv, idx); | ||
| 309 | struct device_node *cpu = NULL; | ||
| 310 | struct device_node *codec = NULL; | ||
| 311 | char *name; | ||
| 312 | char prop[128]; | ||
| 313 | int ret, cpu_args; | ||
| 314 | |||
| 315 | cpu = of_get_child_by_name(node, "cpu"); | ||
| 316 | codec = of_get_child_by_name(node, "codec"); | ||
| 317 | |||
| 318 | if (!cpu || !codec) { | ||
| 319 | ret = -EINVAL; | ||
| 320 | dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); | ||
| 321 | goto dai_link_of_err; | ||
| 322 | } | ||
| 323 | |||
| 324 | ret = rsrc_card_parse_daifmt(node, priv, codec, idx); | ||
| 325 | if (ret < 0) | ||
| 326 | goto dai_link_of_err; | ||
| 327 | |||
| 328 | ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CPU) ? cpu : NULL, | ||
| 329 | &dai_props->cpu_dai, | ||
| 330 | dai_link, | ||
| 331 | &cpu_args); | ||
| 332 | if (ret < 0) | ||
| 333 | goto dai_link_of_err; | ||
| 334 | |||
| 335 | ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CODEC) ? codec : NULL, | ||
| 336 | &dai_props->codec_dai, | ||
| 337 | dai_link, | ||
| 338 | NULL); | ||
| 339 | if (ret < 0) | ||
| 340 | goto dai_link_of_err; | ||
| 341 | |||
| 342 | if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) { | ||
| 343 | ret = -EINVAL; | ||
| 344 | goto dai_link_of_err; | ||
| 345 | } | ||
| 346 | |||
| 347 | /* Simple Card assumes platform == cpu */ | ||
| 348 | dai_link->platform_of_node = dai_link->cpu_of_node; | ||
| 349 | |||
| 350 | /* DAI link name is created from CPU/CODEC dai name */ | ||
| 351 | name = devm_kzalloc(dev, | ||
| 352 | strlen(dai_link->cpu_dai_name) + | ||
| 353 | strlen(dai_link->codec_dai_name) + 2, | ||
| 354 | GFP_KERNEL); | ||
| 355 | if (!name) { | ||
| 356 | ret = -ENOMEM; | ||
| 357 | goto dai_link_of_err; | ||
| 358 | } | ||
| 359 | |||
| 360 | sprintf(name, "%s-%s", dai_link->cpu_dai_name, | ||
| 361 | dai_link->codec_dai_name); | ||
| 362 | dai_link->name = dai_link->stream_name = name; | ||
| 363 | dai_link->ops = &rsrc_card_ops; | ||
| 364 | dai_link->init = rsrc_card_dai_init; | ||
| 365 | |||
| 366 | if (idx == IDX_CODEC) | ||
| 367 | dai_link->be_hw_params_fixup = rsrc_card_be_hw_params_fixup; | ||
| 368 | |||
| 369 | dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); | ||
| 370 | dev_dbg(dev, "\tcpu : %s / %04x / %d\n", | ||
| 371 | dai_link->cpu_dai_name, | ||
| 372 | dai_props->cpu_dai.fmt, | ||
| 373 | dai_props->cpu_dai.sysclk); | ||
| 374 | dev_dbg(dev, "\tcodec : %s / %04x / %d\n", | ||
| 375 | dai_link->codec_dai_name, | ||
| 376 | dai_props->codec_dai.fmt, | ||
| 377 | dai_props->codec_dai.sysclk); | ||
| 378 | |||
| 379 | /* | ||
| 380 | * In soc_bind_dai_link() will check cpu name after | ||
| 381 | * of_node matching if dai_link has cpu_dai_name. | ||
| 382 | * but, it will never match if name was created by | ||
| 383 | * fmt_single_name() remove cpu_dai_name if cpu_args | ||
| 384 | * was 0. See: | ||
| 385 | * fmt_single_name() | ||
| 386 | * fmt_multiple_name() | ||
| 387 | */ | ||
| 388 | if (!cpu_args) | ||
| 389 | dai_link->cpu_dai_name = NULL; | ||
| 390 | |||
| 391 | dai_link_of_err: | ||
| 392 | of_node_put(cpu); | ||
| 393 | of_node_put(codec); | ||
| 394 | |||
| 395 | return ret; | ||
| 396 | } | ||
| 397 | |||
| 398 | static int rsrc_card_parse_of(struct device_node *node, | ||
| 399 | struct rsrc_card_priv *priv) | ||
| 400 | { | ||
| 401 | struct device *dev = rsrc_priv_to_dev(priv); | ||
| 402 | const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev); | ||
| 403 | int ret; | ||
| 404 | int i; | ||
| 405 | |||
| 406 | if (!node) | ||
| 407 | return -EINVAL; | ||
| 408 | |||
| 409 | /* Parse the card name from DT */ | ||
| 410 | snd_soc_of_parse_card_name(&priv->snd_card, "card-name"); | ||
| 411 | |||
| 412 | /* DAPM routes */ | ||
| 413 | priv->snd_card.of_dapm_routes = of_data->routes; | ||
| 414 | priv->snd_card.num_of_dapm_routes = of_data->num_routes; | ||
| 415 | |||
| 416 | /* sampling rate convert */ | ||
| 417 | of_property_read_u32(node, "convert-rate", &priv->convert_rate); | ||
| 418 | |||
| 419 | dev_dbg(dev, "New rsrc-audio-card: %s (%d)\n", | ||
| 420 | priv->snd_card.name ? priv->snd_card.name : "", | ||
| 421 | priv->convert_rate); | ||
| 422 | |||
| 423 | /* FE/BE */ | ||
| 424 | for (i = 0; i < RSRC_FB_NUM; i++) { | ||
| 425 | ret = rsrc_card_dai_link_of(node, priv, i); | ||
| 426 | if (ret < 0) | ||
| 427 | return ret; | ||
| 428 | } | ||
| 429 | |||
| 430 | if (!priv->snd_card.name) | ||
| 431 | priv->snd_card.name = priv->snd_card.dai_link->name; | ||
| 432 | |||
| 433 | return 0; | ||
| 434 | } | ||
| 435 | |||
| 436 | /* Decrease the reference count of the device nodes */ | ||
| 437 | static int rsrc_card_unref(struct snd_soc_card *card) | ||
| 438 | { | ||
| 439 | struct snd_soc_dai_link *dai_link; | ||
| 440 | int num_links; | ||
| 441 | |||
| 442 | for (num_links = 0, dai_link = card->dai_link; | ||
| 443 | num_links < card->num_links; | ||
| 444 | num_links++, dai_link++) { | ||
| 445 | of_node_put(dai_link->cpu_of_node); | ||
| 446 | of_node_put(dai_link->codec_of_node); | ||
| 447 | } | ||
| 448 | return 0; | ||
| 449 | } | ||
| 450 | |||
| 451 | static int rsrc_card_probe(struct platform_device *pdev) | ||
| 452 | { | ||
| 453 | struct rsrc_card_priv *priv; | ||
| 454 | struct snd_soc_dai_link *dai_link; | ||
| 455 | struct device_node *np = pdev->dev.of_node; | ||
| 456 | struct device *dev = &pdev->dev; | ||
| 457 | int ret; | ||
| 458 | |||
| 459 | /* Allocate the private data */ | ||
| 460 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
| 461 | if (!priv) | ||
| 462 | return -ENOMEM; | ||
| 463 | |||
| 464 | /* Init snd_soc_card */ | ||
| 465 | priv->snd_card.owner = THIS_MODULE; | ||
| 466 | priv->snd_card.dev = dev; | ||
| 467 | dai_link = priv->dai_link; | ||
| 468 | priv->snd_card.dai_link = dai_link; | ||
| 469 | priv->snd_card.num_links = RSRC_FB_NUM; | ||
| 470 | priv->snd_card.codec_conf = &priv->codec_conf; | ||
| 471 | priv->snd_card.num_configs = 1; | ||
| 472 | |||
| 473 | ret = rsrc_card_parse_of(np, priv); | ||
| 474 | if (ret < 0) { | ||
| 475 | if (ret != -EPROBE_DEFER) | ||
| 476 | dev_err(dev, "parse error %d\n", ret); | ||
| 477 | goto err; | ||
| 478 | } | ||
| 479 | |||
| 480 | snd_soc_card_set_drvdata(&priv->snd_card, priv); | ||
| 481 | |||
| 482 | ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card); | ||
| 483 | if (ret >= 0) | ||
| 484 | return ret; | ||
| 485 | err: | ||
| 486 | rsrc_card_unref(&priv->snd_card); | ||
| 487 | |||
| 488 | return ret; | ||
| 489 | } | ||
| 490 | |||
| 491 | static int rsrc_card_remove(struct platform_device *pdev) | ||
| 492 | { | ||
| 493 | struct snd_soc_card *card = platform_get_drvdata(pdev); | ||
| 494 | |||
| 495 | return rsrc_card_unref(card); | ||
| 496 | } | ||
| 497 | |||
| 498 | static struct platform_driver rsrc_card = { | ||
| 499 | .driver = { | ||
| 500 | .name = "renesas-src-audio-card", | ||
| 501 | .of_match_table = rsrc_card_of_match, | ||
| 502 | }, | ||
| 503 | .probe = rsrc_card_probe, | ||
| 504 | .remove = rsrc_card_remove, | ||
| 505 | }; | ||
| 506 | |||
| 507 | module_platform_driver(rsrc_card); | ||
| 508 | |||
| 509 | MODULE_ALIAS("platform:renesas-src-audio-card"); | ||
| 510 | MODULE_LICENSE("GPL"); | ||
| 511 | MODULE_DESCRIPTION("Renesas Sampling Rate Convert Sound Card"); | ||
| 512 | MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); | ||
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index c77d059edc84..3beb32eb412a 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c | |||
| @@ -22,16 +22,20 @@ | |||
| 22 | struct rsnd_src { | 22 | struct rsnd_src { |
| 23 | struct rsnd_src_platform_info *info; /* rcar_snd.h */ | 23 | struct rsnd_src_platform_info *info; /* rcar_snd.h */ |
| 24 | struct rsnd_mod mod; | 24 | struct rsnd_mod mod; |
| 25 | struct rsnd_kctrl_cfg_s sen; /* sync convert enable */ | ||
| 26 | struct rsnd_kctrl_cfg_s sync; /* sync convert */ | ||
| 27 | u32 convert_rate; /* sampling rate convert */ | ||
| 25 | int err; | 28 | int err; |
| 26 | }; | 29 | }; |
| 27 | 30 | ||
| 28 | #define RSND_SRC_NAME_SIZE 16 | 31 | #define RSND_SRC_NAME_SIZE 16 |
| 29 | 32 | ||
| 30 | #define rsnd_src_convert_rate(p) ((p)->info->convert_rate) | 33 | #define rsnd_enable_sync_convert(src) ((src)->sen.val) |
| 34 | #define rsnd_src_of_node(priv) \ | ||
| 35 | of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src") | ||
| 36 | |||
| 31 | #define rsnd_mod_to_src(_mod) \ | 37 | #define rsnd_mod_to_src(_mod) \ |
| 32 | container_of((_mod), struct rsnd_src, mod) | 38 | container_of((_mod), struct rsnd_src, mod) |
| 33 | #define rsnd_src_dma_available(src) \ | ||
| 34 | rsnd_dma_available(rsnd_mod_to_dma(&(src)->mod)) | ||
| 35 | 39 | ||
| 36 | #define for_each_rsnd_src(pos, priv, i) \ | 40 | #define for_each_rsnd_src(pos, priv, i) \ |
| 37 | for ((i) = 0; \ | 41 | for ((i) = 0; \ |
| @@ -113,6 +117,17 @@ struct rsnd_src { | |||
| 113 | /* | 117 | /* |
| 114 | * Gen1/Gen2 common functions | 118 | * Gen1/Gen2 common functions |
| 115 | */ | 119 | */ |
| 120 | static struct dma_chan *rsnd_src_dma_req(struct rsnd_mod *mod) | ||
| 121 | { | ||
| 122 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
| 123 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
| 124 | int is_play = rsnd_io_is_play(io); | ||
| 125 | |||
| 126 | return rsnd_dma_request_channel(rsnd_src_of_node(priv), | ||
| 127 | mod, | ||
| 128 | is_play ? "rx" : "tx"); | ||
| 129 | } | ||
| 130 | |||
| 116 | int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, | 131 | int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, |
| 117 | int use_busif) | 132 | int use_busif) |
| 118 | { | 133 | { |
| @@ -220,6 +235,30 @@ int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod) | |||
| 220 | return 0; | 235 | return 0; |
| 221 | } | 236 | } |
| 222 | 237 | ||
| 238 | static u32 rsnd_src_convert_rate(struct rsnd_src *src) | ||
| 239 | { | ||
| 240 | struct rsnd_mod *mod = &src->mod; | ||
| 241 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
| 242 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); | ||
| 243 | u32 convert_rate; | ||
| 244 | |||
| 245 | if (!runtime) | ||
| 246 | return 0; | ||
| 247 | |||
| 248 | if (!rsnd_enable_sync_convert(src)) | ||
| 249 | return src->convert_rate; | ||
| 250 | |||
| 251 | convert_rate = src->sync.val; | ||
| 252 | |||
| 253 | if (!convert_rate) | ||
| 254 | convert_rate = src->convert_rate; | ||
| 255 | |||
| 256 | if (!convert_rate) | ||
| 257 | convert_rate = runtime->rate; | ||
| 258 | |||
| 259 | return convert_rate; | ||
| 260 | } | ||
| 261 | |||
| 223 | unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, | 262 | unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, |
| 224 | struct rsnd_dai_stream *io, | 263 | struct rsnd_dai_stream *io, |
| 225 | struct snd_pcm_runtime *runtime) | 264 | struct snd_pcm_runtime *runtime) |
| @@ -276,7 +315,43 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod) | |||
| 276 | return 0; | 315 | return 0; |
| 277 | } | 316 | } |
| 278 | 317 | ||
| 279 | static int rsnd_src_init(struct rsnd_mod *mod) | 318 | static int rsnd_src_hw_params(struct rsnd_mod *mod, |
| 319 | struct snd_pcm_substream *substream, | ||
| 320 | struct snd_pcm_hw_params *fe_params) | ||
| 321 | { | ||
| 322 | struct rsnd_src *src = rsnd_mod_to_src(mod); | ||
| 323 | struct snd_soc_pcm_runtime *fe = substream->private_data; | ||
| 324 | |||
| 325 | /* default value (mainly for non-DT) */ | ||
| 326 | src->convert_rate = src->info->convert_rate; | ||
| 327 | |||
| 328 | /* | ||
| 329 | * SRC assumes that it is used under DPCM if user want to use | ||
| 330 | * sampling rate convert. Then, SRC should be FE. | ||
| 331 | * And then, this function will be called *after* BE settings. | ||
| 332 | * this means, each BE already has fixuped hw_params. | ||
| 333 | * see | ||
| 334 | * dpcm_fe_dai_hw_params() | ||
| 335 | * dpcm_be_dai_hw_params() | ||
| 336 | */ | ||
| 337 | if (fe->dai_link->dynamic) { | ||
| 338 | int stream = substream->stream; | ||
| 339 | struct snd_soc_dpcm *dpcm; | ||
| 340 | struct snd_pcm_hw_params *be_params; | ||
| 341 | |||
| 342 | list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { | ||
| 343 | be_params = &dpcm->hw_params; | ||
| 344 | |||
| 345 | if (params_rate(fe_params) != params_rate(be_params)) | ||
| 346 | src->convert_rate = params_rate(be_params); | ||
| 347 | } | ||
| 348 | } | ||
| 349 | |||
| 350 | return 0; | ||
| 351 | } | ||
| 352 | |||
| 353 | static int rsnd_src_init(struct rsnd_mod *mod, | ||
| 354 | struct rsnd_priv *priv) | ||
| 280 | { | 355 | { |
| 281 | struct rsnd_src *src = rsnd_mod_to_src(mod); | 356 | struct rsnd_src *src = rsnd_mod_to_src(mod); |
| 282 | 357 | ||
| @@ -284,6 +359,9 @@ static int rsnd_src_init(struct rsnd_mod *mod) | |||
| 284 | 359 | ||
| 285 | src->err = 0; | 360 | src->err = 0; |
| 286 | 361 | ||
| 362 | /* reset sync convert_rate */ | ||
| 363 | src->sync.val = 0; | ||
| 364 | |||
| 287 | /* | 365 | /* |
| 288 | * Initialize the operation of the SRC internal circuits | 366 | * Initialize the operation of the SRC internal circuits |
| 289 | * see rsnd_src_start() | 367 | * see rsnd_src_start() |
| @@ -305,6 +383,11 @@ static int rsnd_src_quit(struct rsnd_mod *mod, | |||
| 305 | dev_warn(dev, "%s[%d] under/over flow err = %d\n", | 383 | dev_warn(dev, "%s[%d] under/over flow err = %d\n", |
| 306 | rsnd_mod_name(mod), rsnd_mod_id(mod), src->err); | 384 | rsnd_mod_name(mod), rsnd_mod_id(mod), src->err); |
| 307 | 385 | ||
| 386 | src->convert_rate = 0; | ||
| 387 | |||
| 388 | /* reset sync convert_rate */ | ||
| 389 | src->sync.val = 0; | ||
| 390 | |||
| 308 | return 0; | 391 | return 0; |
| 309 | } | 392 | } |
| 310 | 393 | ||
| @@ -448,23 +531,12 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod) | |||
| 448 | return 0; | 531 | return 0; |
| 449 | } | 532 | } |
| 450 | 533 | ||
| 451 | static int rsnd_src_probe_gen1(struct rsnd_mod *mod, | ||
| 452 | struct rsnd_priv *priv) | ||
| 453 | { | ||
| 454 | struct device *dev = rsnd_priv_to_dev(priv); | ||
| 455 | |||
| 456 | dev_dbg(dev, "%s[%d] (Gen1) is probed\n", | ||
| 457 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
| 458 | |||
| 459 | return 0; | ||
| 460 | } | ||
| 461 | |||
| 462 | static int rsnd_src_init_gen1(struct rsnd_mod *mod, | 534 | static int rsnd_src_init_gen1(struct rsnd_mod *mod, |
| 463 | struct rsnd_priv *priv) | 535 | struct rsnd_priv *priv) |
| 464 | { | 536 | { |
| 465 | int ret; | 537 | int ret; |
| 466 | 538 | ||
| 467 | ret = rsnd_src_init(mod); | 539 | ret = rsnd_src_init(mod, priv); |
| 468 | if (ret < 0) | 540 | if (ret < 0) |
| 469 | return ret; | 541 | return ret; |
| 470 | 542 | ||
| @@ -505,11 +577,12 @@ static int rsnd_src_stop_gen1(struct rsnd_mod *mod, | |||
| 505 | 577 | ||
| 506 | static struct rsnd_mod_ops rsnd_src_gen1_ops = { | 578 | static struct rsnd_mod_ops rsnd_src_gen1_ops = { |
| 507 | .name = SRC_NAME, | 579 | .name = SRC_NAME, |
| 508 | .probe = rsnd_src_probe_gen1, | 580 | .dma_req = rsnd_src_dma_req, |
| 509 | .init = rsnd_src_init_gen1, | 581 | .init = rsnd_src_init_gen1, |
| 510 | .quit = rsnd_src_quit, | 582 | .quit = rsnd_src_quit, |
| 511 | .start = rsnd_src_start_gen1, | 583 | .start = rsnd_src_start_gen1, |
| 512 | .stop = rsnd_src_stop_gen1, | 584 | .stop = rsnd_src_stop_gen1, |
| 585 | .hw_params = rsnd_src_hw_params, | ||
| 513 | }; | 586 | }; |
| 514 | 587 | ||
| 515 | /* | 588 | /* |
| @@ -607,13 +680,17 @@ static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data) | |||
| 607 | 680 | ||
| 608 | if (rsnd_src_error_record_gen2(mod)) { | 681 | if (rsnd_src_error_record_gen2(mod)) { |
| 609 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | 682 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
| 683 | struct rsnd_src *src = rsnd_mod_to_src(mod); | ||
| 610 | struct device *dev = rsnd_priv_to_dev(priv); | 684 | struct device *dev = rsnd_priv_to_dev(priv); |
| 611 | 685 | ||
| 612 | _rsnd_src_stop_gen2(mod); | ||
| 613 | _rsnd_src_start_gen2(mod); | ||
| 614 | |||
| 615 | dev_dbg(dev, "%s[%d] restart\n", | 686 | dev_dbg(dev, "%s[%d] restart\n", |
| 616 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | 687 | rsnd_mod_name(mod), rsnd_mod_id(mod)); |
| 688 | |||
| 689 | _rsnd_src_stop_gen2(mod); | ||
| 690 | if (src->err < 1024) | ||
| 691 | _rsnd_src_start_gen2(mod); | ||
| 692 | else | ||
| 693 | dev_warn(dev, "no more SRC restart\n"); | ||
| 617 | } | 694 | } |
| 618 | 695 | ||
| 619 | return IRQ_HANDLED; | 696 | return IRQ_HANDLED; |
| @@ -627,6 +704,7 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod) | |||
| 627 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); | 704 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); |
| 628 | struct rsnd_src *src = rsnd_mod_to_src(mod); | 705 | struct rsnd_src *src = rsnd_mod_to_src(mod); |
| 629 | u32 convert_rate = rsnd_src_convert_rate(src); | 706 | u32 convert_rate = rsnd_src_convert_rate(src); |
| 707 | u32 cr, route; | ||
| 630 | uint ratio; | 708 | uint ratio; |
| 631 | int ret; | 709 | int ret; |
| 632 | 710 | ||
| @@ -647,13 +725,21 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod) | |||
| 647 | if (ret < 0) | 725 | if (ret < 0) |
| 648 | return ret; | 726 | return ret; |
| 649 | 727 | ||
| 650 | rsnd_mod_write(mod, SRC_SRCCR, 0x00011110); | 728 | cr = 0x00011110; |
| 651 | 729 | route = 0x0; | |
| 652 | if (convert_rate) { | 730 | if (convert_rate) { |
| 653 | /* Gen1/Gen2 are not compatible */ | 731 | route = 0x1; |
| 654 | rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); | 732 | |
| 733 | if (rsnd_enable_sync_convert(src)) { | ||
| 734 | cr |= 0x1; | ||
| 735 | route |= rsnd_io_is_play(io) ? | ||
| 736 | (0x1 << 24) : (0x1 << 25); | ||
| 737 | } | ||
| 655 | } | 738 | } |
| 656 | 739 | ||
| 740 | rsnd_mod_write(mod, SRC_SRCCR, cr); | ||
| 741 | rsnd_mod_write(mod, SRC_ROUTE_MODE0, route); | ||
| 742 | |||
| 657 | switch (rsnd_mod_id(mod)) { | 743 | switch (rsnd_mod_id(mod)) { |
| 658 | case 5: | 744 | case 5: |
| 659 | case 6: | 745 | case 6: |
| @@ -708,24 +794,12 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod, | |||
| 708 | IRQF_SHARED, | 794 | IRQF_SHARED, |
| 709 | dev_name(dev), mod); | 795 | dev_name(dev), mod); |
| 710 | if (ret) | 796 | if (ret) |
| 711 | goto rsnd_src_probe_gen2_fail; | 797 | return ret; |
| 712 | } | 798 | } |
| 713 | 799 | ||
| 714 | ret = rsnd_dma_init(priv, | 800 | ret = rsnd_dma_init(priv, |
| 715 | rsnd_mod_to_dma(mod), | 801 | rsnd_mod_to_dma(mod), |
| 716 | rsnd_info_is_playback(priv, src), | ||
| 717 | src->info->dma_id); | 802 | src->info->dma_id); |
| 718 | if (ret) | ||
| 719 | goto rsnd_src_probe_gen2_fail; | ||
| 720 | |||
| 721 | dev_dbg(dev, "%s[%d] (Gen2) is probed\n", | ||
| 722 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
| 723 | |||
| 724 | return ret; | ||
| 725 | |||
| 726 | rsnd_src_probe_gen2_fail: | ||
| 727 | dev_err(dev, "%s[%d] (Gen2) failed\n", | ||
| 728 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
| 729 | 803 | ||
| 730 | return ret; | 804 | return ret; |
| 731 | } | 805 | } |
| @@ -733,7 +807,7 @@ rsnd_src_probe_gen2_fail: | |||
| 733 | static int rsnd_src_remove_gen2(struct rsnd_mod *mod, | 807 | static int rsnd_src_remove_gen2(struct rsnd_mod *mod, |
| 734 | struct rsnd_priv *priv) | 808 | struct rsnd_priv *priv) |
| 735 | { | 809 | { |
| 736 | rsnd_dma_quit(priv, rsnd_mod_to_dma(mod)); | 810 | rsnd_dma_quit(rsnd_mod_to_dma(mod)); |
| 737 | 811 | ||
| 738 | return 0; | 812 | return 0; |
| 739 | } | 813 | } |
| @@ -743,7 +817,7 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod, | |||
| 743 | { | 817 | { |
| 744 | int ret; | 818 | int ret; |
| 745 | 819 | ||
| 746 | ret = rsnd_src_init(mod); | 820 | ret = rsnd_src_init(mod, priv); |
| 747 | if (ret < 0) | 821 | if (ret < 0) |
| 748 | return ret; | 822 | return ret; |
| 749 | 823 | ||
| @@ -778,14 +852,91 @@ static int rsnd_src_stop_gen2(struct rsnd_mod *mod, | |||
| 778 | return ret; | 852 | return ret; |
| 779 | } | 853 | } |
| 780 | 854 | ||
| 855 | static void rsnd_src_reconvert_update(struct rsnd_mod *mod) | ||
| 856 | { | ||
| 857 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
| 858 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); | ||
| 859 | struct rsnd_src *src = rsnd_mod_to_src(mod); | ||
| 860 | u32 convert_rate = rsnd_src_convert_rate(src); | ||
| 861 | u32 fsrate; | ||
| 862 | |||
| 863 | if (!runtime) | ||
| 864 | return; | ||
| 865 | |||
| 866 | if (!convert_rate) | ||
| 867 | convert_rate = runtime->rate; | ||
| 868 | |||
| 869 | fsrate = 0x0400000 / convert_rate * runtime->rate; | ||
| 870 | |||
| 871 | /* update IFS */ | ||
| 872 | rsnd_mod_write(mod, SRC_IFSVR, fsrate); | ||
| 873 | } | ||
| 874 | |||
| 875 | static int rsnd_src_pcm_new(struct rsnd_mod *mod, | ||
| 876 | struct snd_soc_pcm_runtime *rtd) | ||
| 877 | { | ||
| 878 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
| 879 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
| 880 | struct rsnd_dai *rdai = rsnd_io_to_rdai(io); | ||
| 881 | struct rsnd_src *src = rsnd_mod_to_src(mod); | ||
| 882 | int ret; | ||
| 883 | |||
| 884 | /* | ||
| 885 | * enable SRC sync convert if possible | ||
| 886 | */ | ||
| 887 | |||
| 888 | /* | ||
| 889 | * Gen1 is not supported | ||
| 890 | */ | ||
| 891 | if (rsnd_is_gen1(priv)) | ||
| 892 | return 0; | ||
| 893 | |||
| 894 | /* | ||
| 895 | * SRC sync convert needs clock master | ||
| 896 | */ | ||
| 897 | if (!rsnd_rdai_is_clk_master(rdai)) | ||
| 898 | return 0; | ||
| 899 | |||
| 900 | /* | ||
| 901 | * We can't use SRC sync convert | ||
| 902 | * if it has DVC | ||
| 903 | */ | ||
| 904 | if (rsnd_io_to_mod_dvc(io)) | ||
| 905 | return 0; | ||
| 906 | |||
| 907 | /* | ||
| 908 | * enable sync convert | ||
| 909 | */ | ||
| 910 | ret = rsnd_kctrl_new_s(mod, rtd, | ||
| 911 | rsnd_io_is_play(io) ? | ||
| 912 | "SRC Out Rate Switch" : | ||
| 913 | "SRC In Rate Switch", | ||
| 914 | rsnd_src_reconvert_update, | ||
| 915 | &src->sen, 1); | ||
| 916 | if (ret < 0) | ||
| 917 | return ret; | ||
| 918 | |||
| 919 | ret = rsnd_kctrl_new_s(mod, rtd, | ||
| 920 | rsnd_io_is_play(io) ? | ||
| 921 | "SRC Out Rate" : | ||
| 922 | "SRC In Rate", | ||
| 923 | rsnd_src_reconvert_update, | ||
| 924 | &src->sync, 192000); | ||
| 925 | |||
| 926 | return ret; | ||
| 927 | } | ||
| 928 | |||
| 781 | static struct rsnd_mod_ops rsnd_src_gen2_ops = { | 929 | static struct rsnd_mod_ops rsnd_src_gen2_ops = { |
| 782 | .name = SRC_NAME, | 930 | .name = SRC_NAME, |
| 931 | .dma_req = rsnd_src_dma_req, | ||
| 783 | .probe = rsnd_src_probe_gen2, | 932 | .probe = rsnd_src_probe_gen2, |
| 784 | .remove = rsnd_src_remove_gen2, | 933 | .remove = rsnd_src_remove_gen2, |
| 785 | .init = rsnd_src_init_gen2, | 934 | .init = rsnd_src_init_gen2, |
| 786 | .quit = rsnd_src_quit, | 935 | .quit = rsnd_src_quit, |
| 787 | .start = rsnd_src_start_gen2, | 936 | .start = rsnd_src_start_gen2, |
| 788 | .stop = rsnd_src_stop_gen2, | 937 | .stop = rsnd_src_stop_gen2, |
| 938 | .hw_params = rsnd_src_hw_params, | ||
| 939 | .pcm_new = rsnd_src_pcm_new, | ||
| 789 | }; | 940 | }; |
| 790 | 941 | ||
| 791 | struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) | 942 | struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) |
| @@ -810,7 +961,7 @@ static void rsnd_of_parse_src(struct platform_device *pdev, | |||
| 810 | if (!of_data) | 961 | if (!of_data) |
| 811 | return; | 962 | return; |
| 812 | 963 | ||
| 813 | src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src"); | 964 | src_node = rsnd_src_of_node(priv); |
| 814 | if (!src_node) | 965 | if (!src_node) |
| 815 | return; | 966 | return; |
| 816 | 967 | ||
| @@ -893,8 +1044,6 @@ int rsnd_src_probe(struct platform_device *pdev, | |||
| 893 | ret = rsnd_mod_init(&src->mod, ops, clk, RSND_MOD_SRC, i); | 1044 | ret = rsnd_mod_init(&src->mod, ops, clk, RSND_MOD_SRC, i); |
| 894 | if (ret) | 1045 | if (ret) |
| 895 | return ret; | 1046 | return ret; |
| 896 | |||
| 897 | dev_dbg(dev, "SRC%d probed\n", i); | ||
| 898 | } | 1047 | } |
| 899 | 1048 | ||
| 900 | return 0; | 1049 | return 0; |
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index f7cb1fd635a0..7bb9c087f3dc 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c | |||
| @@ -80,13 +80,13 @@ struct rsnd_ssi { | |||
| 80 | #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) | 80 | #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) |
| 81 | #define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma)) | 81 | #define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma)) |
| 82 | #define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0) | 82 | #define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0) |
| 83 | #define rsnd_ssi_dma_available(ssi) \ | ||
| 84 | rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod)) | ||
| 85 | #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) | 83 | #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) |
| 86 | #define rsnd_ssi_mode_flags(p) ((p)->info->flags) | 84 | #define rsnd_ssi_mode_flags(p) ((p)->info->flags) |
| 87 | #define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id) | 85 | #define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id) |
| 86 | #define rsnd_ssi_of_node(priv) \ | ||
| 87 | of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi") | ||
| 88 | 88 | ||
| 89 | static int rsnd_ssi_use_busif(struct rsnd_mod *mod) | 89 | int rsnd_ssi_use_busif(struct rsnd_mod *mod) |
| 90 | { | 90 | { |
| 91 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); | 91 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); |
| 92 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | 92 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); |
| @@ -416,11 +416,14 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) | |||
| 416 | /* | 416 | /* |
| 417 | * restart SSI | 417 | * restart SSI |
| 418 | */ | 418 | */ |
| 419 | rsnd_ssi_stop(mod, priv); | ||
| 420 | rsnd_ssi_start(mod, priv); | ||
| 421 | |||
| 422 | dev_dbg(dev, "%s[%d] restart\n", | 419 | dev_dbg(dev, "%s[%d] restart\n", |
| 423 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | 420 | rsnd_mod_name(mod), rsnd_mod_id(mod)); |
| 421 | |||
| 422 | rsnd_ssi_stop(mod, priv); | ||
| 423 | if (ssi->err < 1024) | ||
| 424 | rsnd_ssi_start(mod, priv); | ||
| 425 | else | ||
| 426 | dev_warn(dev, "no more SSI restart\n"); | ||
| 424 | } | 427 | } |
| 425 | 428 | ||
| 426 | rsnd_ssi_record_error(ssi, status); | 429 | rsnd_ssi_record_error(ssi, status); |
| @@ -442,12 +445,6 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, | |||
| 442 | rsnd_ssi_interrupt, | 445 | rsnd_ssi_interrupt, |
| 443 | IRQF_SHARED, | 446 | IRQF_SHARED, |
| 444 | dev_name(dev), ssi); | 447 | dev_name(dev), ssi); |
| 445 | if (ret) | ||
| 446 | dev_err(dev, "%s[%d] (PIO) request interrupt failed\n", | ||
| 447 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
| 448 | else | ||
| 449 | dev_dbg(dev, "%s[%d] (PIO) is probed\n", | ||
| 450 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
| 451 | 448 | ||
| 452 | return ret; | 449 | return ret; |
| 453 | } | 450 | } |
| @@ -474,23 +471,11 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, | |||
| 474 | IRQF_SHARED, | 471 | IRQF_SHARED, |
| 475 | dev_name(dev), ssi); | 472 | dev_name(dev), ssi); |
| 476 | if (ret) | 473 | if (ret) |
| 477 | goto rsnd_ssi_dma_probe_fail; | 474 | return ret; |
| 478 | 475 | ||
| 479 | ret = rsnd_dma_init( | 476 | ret = rsnd_dma_init( |
| 480 | priv, rsnd_mod_to_dma(mod), | 477 | priv, rsnd_mod_to_dma(mod), |
| 481 | rsnd_info_is_playback(priv, ssi), | ||
| 482 | dma_id); | 478 | dma_id); |
| 483 | if (ret) | ||
| 484 | goto rsnd_ssi_dma_probe_fail; | ||
| 485 | |||
| 486 | dev_dbg(dev, "%s[%d] (DMA) is probed\n", | ||
| 487 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
| 488 | |||
| 489 | return ret; | ||
| 490 | |||
| 491 | rsnd_ssi_dma_probe_fail: | ||
| 492 | dev_err(dev, "%s[%d] (DMA) is failed\n", | ||
| 493 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
| 494 | 479 | ||
| 495 | return ret; | 480 | return ret; |
| 496 | } | 481 | } |
| @@ -502,7 +487,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, | |||
| 502 | struct device *dev = rsnd_priv_to_dev(priv); | 487 | struct device *dev = rsnd_priv_to_dev(priv); |
| 503 | int irq = ssi->info->irq; | 488 | int irq = ssi->info->irq; |
| 504 | 489 | ||
| 505 | rsnd_dma_quit(priv, rsnd_mod_to_dma(mod)); | 490 | rsnd_dma_quit(rsnd_mod_to_dma(mod)); |
| 506 | 491 | ||
| 507 | /* PIO will request IRQ again */ | 492 | /* PIO will request IRQ again */ |
| 508 | devm_free_irq(dev, irq, ssi); | 493 | devm_free_irq(dev, irq, ssi); |
| @@ -554,14 +539,25 @@ static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, | |||
| 554 | return 0; | 539 | return 0; |
| 555 | } | 540 | } |
| 556 | 541 | ||
| 557 | static char *rsnd_ssi_dma_name(struct rsnd_mod *mod) | 542 | static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_mod *mod) |
| 558 | { | 543 | { |
| 559 | return rsnd_ssi_use_busif(mod) ? "ssiu" : SSI_NAME; | 544 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
| 545 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
| 546 | int is_play = rsnd_io_is_play(io); | ||
| 547 | char *name; | ||
| 548 | |||
| 549 | if (rsnd_ssi_use_busif(mod)) | ||
| 550 | name = is_play ? "rxu" : "txu"; | ||
| 551 | else | ||
| 552 | name = is_play ? "rx" : "tx"; | ||
| 553 | |||
| 554 | return rsnd_dma_request_channel(rsnd_ssi_of_node(priv), | ||
| 555 | mod, name); | ||
| 560 | } | 556 | } |
| 561 | 557 | ||
| 562 | static struct rsnd_mod_ops rsnd_ssi_dma_ops = { | 558 | static struct rsnd_mod_ops rsnd_ssi_dma_ops = { |
| 563 | .name = SSI_NAME, | 559 | .name = SSI_NAME, |
| 564 | .dma_name = rsnd_ssi_dma_name, | 560 | .dma_req = rsnd_ssi_dma_req, |
| 565 | .probe = rsnd_ssi_dma_probe, | 561 | .probe = rsnd_ssi_dma_probe, |
| 566 | .remove = rsnd_ssi_dma_remove, | 562 | .remove = rsnd_ssi_dma_remove, |
| 567 | .init = rsnd_ssi_init, | 563 | .init = rsnd_ssi_init, |
| @@ -636,7 +632,7 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev, | |||
| 636 | if (!of_data) | 632 | if (!of_data) |
| 637 | return; | 633 | return; |
| 638 | 634 | ||
| 639 | node = of_get_child_by_name(dev->of_node, "rcar_sound,ssi"); | 635 | node = rsnd_ssi_of_node(priv); |
| 640 | if (!node) | 636 | if (!node) |
| 641 | return; | 637 | return; |
| 642 | 638 | ||
