diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-12-01 23:34:58 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-12-01 23:34:58 -0500 |
commit | 3bc324922663bda59af5cdc409e8df3ed217d296 (patch) | |
tree | 1c9c7d527c4fd3383350d54acd7d15b9c5c4f639 | |
parent | 9d3493e84def26bd9b0bab825629063bacc89383 (diff) | |
parent | 3951e4aae2ce7e4593e575e91cbb22f1ba153596 (diff) |
Merge remote-tracking branch 'asoc/topic/atmel' into asoc-next
32 files changed, 1108 insertions, 611 deletions
diff --git a/Documentation/devicetree/bindings/misc/atmel-ssc.txt b/Documentation/devicetree/bindings/misc/atmel-ssc.txt new file mode 100644 index 000000000000..38e51ad2e07e --- /dev/null +++ b/Documentation/devicetree/bindings/misc/atmel-ssc.txt | |||
@@ -0,0 +1,15 @@ | |||
1 | * Atmel SSC driver. | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: "atmel,at91rm9200-ssc" or "atmel,at91sam9g45-ssc" | ||
5 | - atmel,at91rm9200-ssc: support pdc transfer | ||
6 | - atmel,at91sam9g45-ssc: support dma transfer | ||
7 | - reg: Should contain SSC registers location and length | ||
8 | - interrupts: Should contain SSC interrupt | ||
9 | |||
10 | Example: | ||
11 | ssc0: ssc@fffbc000 { | ||
12 | compatible = "atmel,at91rm9200-ssc"; | ||
13 | reg = <0xfffbc000 0x4000>; | ||
14 | interrupts = <14 4 5>; | ||
15 | }; | ||
diff --git a/Documentation/devicetree/bindings/sound/atmel-at91sam9g20ek-wm8731-audio.txt b/Documentation/devicetree/bindings/sound/atmel-at91sam9g20ek-wm8731-audio.txt new file mode 100644 index 000000000000..9c5a9947b64d --- /dev/null +++ b/Documentation/devicetree/bindings/sound/atmel-at91sam9g20ek-wm8731-audio.txt | |||
@@ -0,0 +1,26 @@ | |||
1 | * Atmel at91sam9g20ek wm8731 audio complex | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: "atmel,at91sam9g20ek-wm8731-audio" | ||
5 | - atmel,model: The user-visible name of this sound complex. | ||
6 | - atmel,audio-routing: A list of the connections between audio components. | ||
7 | - atmel,ssc-controller: The phandle of the SSC controller | ||
8 | - atmel,audio-codec: The phandle of the WM8731 audio codec | ||
9 | Optional properties: | ||
10 | - pinctrl-names, pinctrl-0: Please refer to pinctrl-bindings.txt | ||
11 | |||
12 | Example: | ||
13 | sound { | ||
14 | compatible = "atmel,at91sam9g20ek-wm8731-audio"; | ||
15 | pinctrl-names = "default"; | ||
16 | pinctrl-0 = <&pinctrl_pck0_as_mck>; | ||
17 | |||
18 | atmel,model = "wm8731 @ AT91SAMG20EK"; | ||
19 | |||
20 | atmel,audio-routing = | ||
21 | "Ext Spk", "LHPOUT", | ||
22 | "Int MIC", "MICIN"; | ||
23 | |||
24 | atmel,ssc-controller = <&ssc0>; | ||
25 | atmel,audio-codec = <&wm8731>; | ||
26 | }; | ||
diff --git a/arch/arm/boot/dts/at91sam9260.dtsi b/arch/arm/boot/dts/at91sam9260.dtsi index d410581a5a85..aaa42d8d4f88 100644 --- a/arch/arm/boot/dts/at91sam9260.dtsi +++ b/arch/arm/boot/dts/at91sam9260.dtsi | |||
@@ -29,6 +29,7 @@ | |||
29 | tcb0 = &tcb0; | 29 | tcb0 = &tcb0; |
30 | tcb1 = &tcb1; | 30 | tcb1 = &tcb1; |
31 | i2c0 = &i2c0; | 31 | i2c0 = &i2c0; |
32 | ssc0 = &ssc0; | ||
32 | }; | 33 | }; |
33 | cpus { | 34 | cpus { |
34 | cpu@0 { | 35 | cpu@0 { |
@@ -212,6 +213,13 @@ | |||
212 | status = "disabled"; | 213 | status = "disabled"; |
213 | }; | 214 | }; |
214 | 215 | ||
216 | ssc0: ssc@fffbc000 { | ||
217 | compatible = "atmel,at91rm9200-ssc"; | ||
218 | reg = <0xfffbc000 0x4000>; | ||
219 | interrupts = <14 4 5>; | ||
220 | status = "disable"; | ||
221 | }; | ||
222 | |||
215 | adc0: adc@fffe0000 { | 223 | adc0: adc@fffe0000 { |
216 | compatible = "atmel,at91sam9260-adc"; | 224 | compatible = "atmel,at91sam9260-adc"; |
217 | reg = <0xfffe0000 0x100>; | 225 | reg = <0xfffe0000 0x100>; |
diff --git a/arch/arm/boot/dts/at91sam9263.dtsi b/arch/arm/boot/dts/at91sam9263.dtsi index 3e6e5c1abbf3..3b721ee59b10 100644 --- a/arch/arm/boot/dts/at91sam9263.dtsi +++ b/arch/arm/boot/dts/at91sam9263.dtsi | |||
@@ -25,6 +25,8 @@ | |||
25 | gpio4 = &pioE; | 25 | gpio4 = &pioE; |
26 | tcb0 = &tcb0; | 26 | tcb0 = &tcb0; |
27 | i2c0 = &i2c0; | 27 | i2c0 = &i2c0; |
28 | ssc0 = &ssc0; | ||
29 | ssc1 = &ssc1; | ||
28 | }; | 30 | }; |
29 | cpus { | 31 | cpus { |
30 | cpu@0 { | 32 | cpu@0 { |
@@ -173,6 +175,20 @@ | |||
173 | status = "disabled"; | 175 | status = "disabled"; |
174 | }; | 176 | }; |
175 | 177 | ||
178 | ssc0: ssc@fff98000 { | ||
179 | compatible = "atmel,at91rm9200-ssc"; | ||
180 | reg = <0xfff98000 0x4000>; | ||
181 | interrupts = <16 4 5>; | ||
182 | status = "disable"; | ||
183 | }; | ||
184 | |||
185 | ssc1: ssc@fff9c000 { | ||
186 | compatible = "atmel,at91rm9200-ssc"; | ||
187 | reg = <0xfff9c000 0x4000>; | ||
188 | interrupts = <17 4 5>; | ||
189 | status = "disable"; | ||
190 | }; | ||
191 | |||
176 | macb0: ethernet@fffbc000 { | 192 | macb0: ethernet@fffbc000 { |
177 | compatible = "cdns,at32ap7000-macb", "cdns,macb"; | 193 | compatible = "cdns,at32ap7000-macb", "cdns,macb"; |
178 | reg = <0xfffbc000 0x100>; | 194 | reg = <0xfffbc000 0x100>; |
diff --git a/arch/arm/boot/dts/at91sam9g20ek_common.dtsi b/arch/arm/boot/dts/at91sam9g20ek_common.dtsi index e6391a4e6649..2dcec8de759f 100644 --- a/arch/arm/boot/dts/at91sam9g20ek_common.dtsi +++ b/arch/arm/boot/dts/at91sam9g20ek_common.dtsi | |||
@@ -30,6 +30,16 @@ | |||
30 | 30 | ||
31 | ahb { | 31 | ahb { |
32 | apb { | 32 | apb { |
33 | pinctrl@fffff400 { | ||
34 | board { | ||
35 | pinctrl_pck0_as_mck: pck0_as_mck { | ||
36 | atmel,pins = | ||
37 | <2 1 0x2 0x0>; /* PC1 periph B */ | ||
38 | }; | ||
39 | |||
40 | }; | ||
41 | }; | ||
42 | |||
33 | dbgu: serial@fffff200 { | 43 | dbgu: serial@fffff200 { |
34 | status = "okay"; | 44 | status = "okay"; |
35 | }; | 45 | }; |
@@ -51,6 +61,11 @@ | |||
51 | atmel,vbus-gpio = <&pioC 5 0>; | 61 | atmel,vbus-gpio = <&pioC 5 0>; |
52 | status = "okay"; | 62 | status = "okay"; |
53 | }; | 63 | }; |
64 | |||
65 | ssc0: ssc@fffbc000 { | ||
66 | status = "okay"; | ||
67 | pinctrl-0 = <&pinctrl_ssc0_tx>; | ||
68 | }; | ||
54 | }; | 69 | }; |
55 | 70 | ||
56 | nand0: nand@40000000 { | 71 | nand0: nand@40000000 { |
@@ -114,7 +129,7 @@ | |||
114 | reg = <0x50>; | 129 | reg = <0x50>; |
115 | }; | 130 | }; |
116 | 131 | ||
117 | wm8731@1b { | 132 | wm8731: wm8731@1b { |
118 | compatible = "wm8731"; | 133 | compatible = "wm8731"; |
119 | reg = <0x1b>; | 134 | reg = <0x1b>; |
120 | }; | 135 | }; |
@@ -139,4 +154,19 @@ | |||
139 | gpio-key,wakeup; | 154 | gpio-key,wakeup; |
140 | }; | 155 | }; |
141 | }; | 156 | }; |
157 | |||
158 | sound { | ||
159 | compatible = "atmel,at91sam9g20ek-wm8731-audio"; | ||
160 | pinctrl-names = "default"; | ||
161 | pinctrl-0 = <&pinctrl_pck0_as_mck>; | ||
162 | |||
163 | atmel,model = "wm8731 @ AT91SAMG20EK"; | ||
164 | |||
165 | atmel,audio-routing = | ||
166 | "Ext Spk", "LHPOUT", | ||
167 | "Int Mic", "MICIN"; | ||
168 | |||
169 | atmel,ssc-controller = <&ssc0>; | ||
170 | atmel,audio-codec = <&wm8731>; | ||
171 | }; | ||
142 | }; | 172 | }; |
diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi index 3add030d61f8..acfa207162ff 100644 --- a/arch/arm/boot/dts/at91sam9g45.dtsi +++ b/arch/arm/boot/dts/at91sam9g45.dtsi | |||
@@ -31,6 +31,8 @@ | |||
31 | tcb1 = &tcb1; | 31 | tcb1 = &tcb1; |
32 | i2c0 = &i2c0; | 32 | i2c0 = &i2c0; |
33 | i2c1 = &i2c1; | 33 | i2c1 = &i2c1; |
34 | ssc0 = &ssc0; | ||
35 | ssc1 = &ssc1; | ||
34 | }; | 36 | }; |
35 | cpus { | 37 | cpus { |
36 | cpu@0 { | 38 | cpu@0 { |
@@ -226,6 +228,20 @@ | |||
226 | status = "disabled"; | 228 | status = "disabled"; |
227 | }; | 229 | }; |
228 | 230 | ||
231 | ssc0: ssc@fff9c000 { | ||
232 | compatible = "atmel,at91sam9g45-ssc"; | ||
233 | reg = <0xfff9c000 0x4000>; | ||
234 | interrupts = <16 4 5>; | ||
235 | status = "disable"; | ||
236 | }; | ||
237 | |||
238 | ssc1: ssc@fffa0000 { | ||
239 | compatible = "atmel,at91sam9g45-ssc"; | ||
240 | reg = <0xfffa0000 0x4000>; | ||
241 | interrupts = <17 4 5>; | ||
242 | status = "disable"; | ||
243 | }; | ||
244 | |||
229 | adc0: adc@fffb0000 { | 245 | adc0: adc@fffb0000 { |
230 | compatible = "atmel,at91sam9260-adc"; | 246 | compatible = "atmel,at91sam9260-adc"; |
231 | reg = <0xfffb0000 0x100>; | 247 | reg = <0xfffb0000 0x100>; |
diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi index 03fc136421c5..69667d0ac347 100644 --- a/arch/arm/boot/dts/at91sam9x5.dtsi +++ b/arch/arm/boot/dts/at91sam9x5.dtsi | |||
@@ -30,6 +30,7 @@ | |||
30 | i2c0 = &i2c0; | 30 | i2c0 = &i2c0; |
31 | i2c1 = &i2c1; | 31 | i2c1 = &i2c1; |
32 | i2c2 = &i2c2; | 32 | i2c2 = &i2c2; |
33 | ssc0 = &ssc0; | ||
33 | }; | 34 | }; |
34 | cpus { | 35 | cpus { |
35 | cpu@0 { | 36 | cpu@0 { |
@@ -87,6 +88,13 @@ | |||
87 | interrupts = <1 4 7>; | 88 | interrupts = <1 4 7>; |
88 | }; | 89 | }; |
89 | 90 | ||
91 | ssc0: ssc@f0010000 { | ||
92 | compatible = "atmel,at91sam9g45-ssc"; | ||
93 | reg = <0xf0010000 0x4000>; | ||
94 | interrupts = <28 4 5>; | ||
95 | status = "disable"; | ||
96 | }; | ||
97 | |||
90 | tcb0: timer@f8008000 { | 98 | tcb0: timer@f8008000 { |
91 | compatible = "atmel,at91sam9x5-tcb"; | 99 | compatible = "atmel,at91sam9x5-tcb"; |
92 | reg = <0xf8008000 0x100>; | 100 | reg = <0xf8008000 0x100>; |
diff --git a/arch/arm/mach-at91/at91rm9200.c b/arch/arm/mach-at91/at91rm9200.c index 5269825194a8..af47c75db513 100644 --- a/arch/arm/mach-at91/at91rm9200.c +++ b/arch/arm/mach-at91/at91rm9200.c | |||
@@ -184,9 +184,12 @@ static struct clk_lookup periph_clocks_lookups[] = { | |||
184 | CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.1", &tc3_clk), | 184 | CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.1", &tc3_clk), |
185 | CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.1", &tc4_clk), | 185 | CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.1", &tc4_clk), |
186 | CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.1", &tc5_clk), | 186 | CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.1", &tc5_clk), |
187 | CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk), | 187 | CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk), |
188 | CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk), | 188 | CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk), |
189 | CLKDEV_CON_DEV_ID("pclk", "ssc.2", &ssc2_clk), | 189 | CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.2", &ssc2_clk), |
190 | CLKDEV_CON_DEV_ID("pclk", "fffd0000.ssc", &ssc0_clk), | ||
191 | CLKDEV_CON_DEV_ID("pclk", "fffd4000.ssc", &ssc1_clk), | ||
192 | CLKDEV_CON_DEV_ID("pclk", "fffd8000.ssc", &ssc2_clk), | ||
190 | CLKDEV_CON_DEV_ID(NULL, "i2c-at91rm9200.0", &twi_clk), | 193 | CLKDEV_CON_DEV_ID(NULL, "i2c-at91rm9200.0", &twi_clk), |
191 | /* fake hclk clock */ | 194 | /* fake hclk clock */ |
192 | CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk), | 195 | CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk), |
diff --git a/arch/arm/mach-at91/at91rm9200_devices.c b/arch/arm/mach-at91/at91rm9200_devices.c index 3cee0e6ea7c3..9e76427aaec2 100644 --- a/arch/arm/mach-at91/at91rm9200_devices.c +++ b/arch/arm/mach-at91/at91rm9200_devices.c | |||
@@ -752,7 +752,7 @@ static struct resource ssc0_resources[] = { | |||
752 | }; | 752 | }; |
753 | 753 | ||
754 | static struct platform_device at91rm9200_ssc0_device = { | 754 | static struct platform_device at91rm9200_ssc0_device = { |
755 | .name = "ssc", | 755 | .name = "at91rm9200_ssc", |
756 | .id = 0, | 756 | .id = 0, |
757 | .dev = { | 757 | .dev = { |
758 | .dma_mask = &ssc0_dmamask, | 758 | .dma_mask = &ssc0_dmamask, |
@@ -794,7 +794,7 @@ static struct resource ssc1_resources[] = { | |||
794 | }; | 794 | }; |
795 | 795 | ||
796 | static struct platform_device at91rm9200_ssc1_device = { | 796 | static struct platform_device at91rm9200_ssc1_device = { |
797 | .name = "ssc", | 797 | .name = "at91rm9200_ssc", |
798 | .id = 1, | 798 | .id = 1, |
799 | .dev = { | 799 | .dev = { |
800 | .dma_mask = &ssc1_dmamask, | 800 | .dma_mask = &ssc1_dmamask, |
@@ -836,7 +836,7 @@ static struct resource ssc2_resources[] = { | |||
836 | }; | 836 | }; |
837 | 837 | ||
838 | static struct platform_device at91rm9200_ssc2_device = { | 838 | static struct platform_device at91rm9200_ssc2_device = { |
839 | .name = "ssc", | 839 | .name = "at91rm9200_ssc", |
840 | .id = 2, | 840 | .id = 2, |
841 | .dev = { | 841 | .dev = { |
842 | .dma_mask = &ssc2_dmamask, | 842 | .dma_mask = &ssc2_dmamask, |
diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c index f8202615f4a8..a41eb3d23f68 100644 --- a/arch/arm/mach-at91/at91sam9260.c +++ b/arch/arm/mach-at91/at91sam9260.c | |||
@@ -210,7 +210,8 @@ static struct clk_lookup periph_clocks_lookups[] = { | |||
210 | CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.1", &tc3_clk), | 210 | CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.1", &tc3_clk), |
211 | CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.1", &tc4_clk), | 211 | CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.1", &tc4_clk), |
212 | CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.1", &tc5_clk), | 212 | CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.1", &tc5_clk), |
213 | CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc_clk), | 213 | CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc_clk), |
214 | CLKDEV_CON_DEV_ID("pclk", "fffbc000.ssc", &ssc_clk), | ||
214 | CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9260.0", &twi_clk), | 215 | CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9260.0", &twi_clk), |
215 | CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.0", &twi_clk), | 216 | CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.0", &twi_clk), |
216 | /* more usart lookup table for DT entries */ | 217 | /* more usart lookup table for DT entries */ |
diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c index 414bd855fb0c..e67cfa2acbe0 100644 --- a/arch/arm/mach-at91/at91sam9260_devices.c +++ b/arch/arm/mach-at91/at91sam9260_devices.c | |||
@@ -742,7 +742,7 @@ static struct resource ssc_resources[] = { | |||
742 | }; | 742 | }; |
743 | 743 | ||
744 | static struct platform_device at91sam9260_ssc_device = { | 744 | static struct platform_device at91sam9260_ssc_device = { |
745 | .name = "ssc", | 745 | .name = "at91rm9200_ssc", |
746 | .id = 0, | 746 | .id = 0, |
747 | .dev = { | 747 | .dev = { |
748 | .dma_mask = &ssc_dmamask, | 748 | .dma_mask = &ssc_dmamask, |
diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c index 04295c04b3e0..7fcbe0583342 100644 --- a/arch/arm/mach-at91/at91sam9261.c +++ b/arch/arm/mach-at91/at91sam9261.c | |||
@@ -174,9 +174,12 @@ static struct clk_lookup periph_clocks_lookups[] = { | |||
174 | CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk), | 174 | CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk), |
175 | CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.0", &tc1_clk), | 175 | CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.0", &tc1_clk), |
176 | CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.0", &tc2_clk), | 176 | CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.0", &tc2_clk), |
177 | CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk), | 177 | CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk), |
178 | CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk), | 178 | CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk), |
179 | CLKDEV_CON_DEV_ID("pclk", "ssc.2", &ssc2_clk), | 179 | CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.2", &ssc2_clk), |
180 | CLKDEV_CON_DEV_ID("pclk", "fffbc000.ssc", &ssc0_clk), | ||
181 | CLKDEV_CON_DEV_ID("pclk", "fffc0000.ssc", &ssc1_clk), | ||
182 | CLKDEV_CON_DEV_ID("pclk", "fffc4000.ssc", &ssc2_clk), | ||
180 | CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &hck0), | 183 | CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &hck0), |
181 | CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9261.0", &twi_clk), | 184 | CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9261.0", &twi_clk), |
182 | CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.0", &twi_clk), | 185 | CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.0", &twi_clk), |
diff --git a/arch/arm/mach-at91/at91sam9261_devices.c b/arch/arm/mach-at91/at91sam9261_devices.c index cd604aad8e96..a27d9dd0faa4 100644 --- a/arch/arm/mach-at91/at91sam9261_devices.c +++ b/arch/arm/mach-at91/at91sam9261_devices.c | |||
@@ -706,7 +706,7 @@ static struct resource ssc0_resources[] = { | |||
706 | }; | 706 | }; |
707 | 707 | ||
708 | static struct platform_device at91sam9261_ssc0_device = { | 708 | static struct platform_device at91sam9261_ssc0_device = { |
709 | .name = "ssc", | 709 | .name = "at91rm9200_ssc", |
710 | .id = 0, | 710 | .id = 0, |
711 | .dev = { | 711 | .dev = { |
712 | .dma_mask = &ssc0_dmamask, | 712 | .dma_mask = &ssc0_dmamask, |
@@ -748,7 +748,7 @@ static struct resource ssc1_resources[] = { | |||
748 | }; | 748 | }; |
749 | 749 | ||
750 | static struct platform_device at91sam9261_ssc1_device = { | 750 | static struct platform_device at91sam9261_ssc1_device = { |
751 | .name = "ssc", | 751 | .name = "at91rm9200_ssc", |
752 | .id = 1, | 752 | .id = 1, |
753 | .dev = { | 753 | .dev = { |
754 | .dma_mask = &ssc1_dmamask, | 754 | .dma_mask = &ssc1_dmamask, |
@@ -790,7 +790,7 @@ static struct resource ssc2_resources[] = { | |||
790 | }; | 790 | }; |
791 | 791 | ||
792 | static struct platform_device at91sam9261_ssc2_device = { | 792 | static struct platform_device at91sam9261_ssc2_device = { |
793 | .name = "ssc", | 793 | .name = "at91rm9200_ssc", |
794 | .id = 2, | 794 | .id = 2, |
795 | .dev = { | 795 | .dev = { |
796 | .dma_mask = &ssc2_dmamask, | 796 | .dma_mask = &ssc2_dmamask, |
diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c index d6f9c23927c4..c0f4c8c1f4ed 100644 --- a/arch/arm/mach-at91/at91sam9263.c +++ b/arch/arm/mach-at91/at91sam9263.c | |||
@@ -186,8 +186,10 @@ static struct clk *periph_clocks[] __initdata = { | |||
186 | static struct clk_lookup periph_clocks_lookups[] = { | 186 | static struct clk_lookup periph_clocks_lookups[] = { |
187 | /* One additional fake clock for macb_hclk */ | 187 | /* One additional fake clock for macb_hclk */ |
188 | CLKDEV_CON_ID("hclk", &macb_clk), | 188 | CLKDEV_CON_ID("hclk", &macb_clk), |
189 | CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk), | 189 | CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk), |
190 | CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk), | 190 | CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk), |
191 | CLKDEV_CON_DEV_ID("pclk", "fff98000.ssc", &ssc0_clk), | ||
192 | CLKDEV_CON_DEV_ID("pclk", "fff9c000.ssc", &ssc1_clk), | ||
191 | CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.0", &mmc0_clk), | 193 | CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.0", &mmc0_clk), |
192 | CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.1", &mmc1_clk), | 194 | CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.1", &mmc1_clk), |
193 | CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk), | 195 | CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk), |
diff --git a/arch/arm/mach-at91/at91sam9263_devices.c b/arch/arm/mach-at91/at91sam9263_devices.c index 9c61e59a2104..8215839f2d54 100644 --- a/arch/arm/mach-at91/at91sam9263_devices.c +++ b/arch/arm/mach-at91/at91sam9263_devices.c | |||
@@ -1199,7 +1199,7 @@ static struct resource ssc0_resources[] = { | |||
1199 | }; | 1199 | }; |
1200 | 1200 | ||
1201 | static struct platform_device at91sam9263_ssc0_device = { | 1201 | static struct platform_device at91sam9263_ssc0_device = { |
1202 | .name = "ssc", | 1202 | .name = "at91rm9200_ssc", |
1203 | .id = 0, | 1203 | .id = 0, |
1204 | .dev = { | 1204 | .dev = { |
1205 | .dma_mask = &ssc0_dmamask, | 1205 | .dma_mask = &ssc0_dmamask, |
@@ -1241,7 +1241,7 @@ static struct resource ssc1_resources[] = { | |||
1241 | }; | 1241 | }; |
1242 | 1242 | ||
1243 | static struct platform_device at91sam9263_ssc1_device = { | 1243 | static struct platform_device at91sam9263_ssc1_device = { |
1244 | .name = "ssc", | 1244 | .name = "at91rm9200_ssc", |
1245 | .id = 1, | 1245 | .id = 1, |
1246 | .dev = { | 1246 | .dev = { |
1247 | .dma_mask = &ssc1_dmamask, | 1247 | .dma_mask = &ssc1_dmamask, |
diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c index 84af1b506d92..a4282d3742bf 100644 --- a/arch/arm/mach-at91/at91sam9g45.c +++ b/arch/arm/mach-at91/at91sam9g45.c | |||
@@ -239,8 +239,10 @@ static struct clk_lookup periph_clocks_lookups[] = { | |||
239 | CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.1", &tcb0_clk), | 239 | CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.1", &tcb0_clk), |
240 | CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.0", &twi0_clk), | 240 | CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.0", &twi0_clk), |
241 | CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.1", &twi1_clk), | 241 | CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.1", &twi1_clk), |
242 | CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk), | 242 | CLKDEV_CON_DEV_ID("pclk", "at91sam9g45_ssc.0", &ssc0_clk), |
243 | CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk), | 243 | CLKDEV_CON_DEV_ID("pclk", "at91sam9g45_ssc.1", &ssc1_clk), |
244 | CLKDEV_CON_DEV_ID("pclk", "fff9c000.ssc", &ssc0_clk), | ||
245 | CLKDEV_CON_DEV_ID("pclk", "fffa0000.ssc", &ssc1_clk), | ||
244 | CLKDEV_CON_DEV_ID(NULL, "atmel-trng", &trng_clk), | 246 | CLKDEV_CON_DEV_ID(NULL, "atmel-trng", &trng_clk), |
245 | CLKDEV_CON_DEV_ID(NULL, "atmel_sha", &aestdessha_clk), | 247 | CLKDEV_CON_DEV_ID(NULL, "atmel_sha", &aestdessha_clk), |
246 | CLKDEV_CON_DEV_ID(NULL, "atmel_tdes", &aestdessha_clk), | 248 | CLKDEV_CON_DEV_ID(NULL, "atmel_tdes", &aestdessha_clk), |
diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c index fcd233cb33d2..d26474a97fec 100644 --- a/arch/arm/mach-at91/at91sam9g45_devices.c +++ b/arch/arm/mach-at91/at91sam9g45_devices.c | |||
@@ -1459,7 +1459,7 @@ static struct resource ssc0_resources[] = { | |||
1459 | }; | 1459 | }; |
1460 | 1460 | ||
1461 | static struct platform_device at91sam9g45_ssc0_device = { | 1461 | static struct platform_device at91sam9g45_ssc0_device = { |
1462 | .name = "ssc", | 1462 | .name = "at91sam9g45_ssc", |
1463 | .id = 0, | 1463 | .id = 0, |
1464 | .dev = { | 1464 | .dev = { |
1465 | .dma_mask = &ssc0_dmamask, | 1465 | .dma_mask = &ssc0_dmamask, |
@@ -1501,7 +1501,7 @@ static struct resource ssc1_resources[] = { | |||
1501 | }; | 1501 | }; |
1502 | 1502 | ||
1503 | static struct platform_device at91sam9g45_ssc1_device = { | 1503 | static struct platform_device at91sam9g45_ssc1_device = { |
1504 | .name = "ssc", | 1504 | .name = "at91sam9g45_ssc", |
1505 | .id = 1, | 1505 | .id = 1, |
1506 | .dev = { | 1506 | .dev = { |
1507 | .dma_mask = &ssc1_dmamask, | 1507 | .dma_mask = &ssc1_dmamask, |
diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c index 72e908412222..b683fdc699f1 100644 --- a/arch/arm/mach-at91/at91sam9rl.c +++ b/arch/arm/mach-at91/at91sam9rl.c | |||
@@ -184,8 +184,10 @@ static struct clk_lookup periph_clocks_lookups[] = { | |||
184 | CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk), | 184 | CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk), |
185 | CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.0", &tc1_clk), | 185 | CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.0", &tc1_clk), |
186 | CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.0", &tc2_clk), | 186 | CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.0", &tc2_clk), |
187 | CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk), | 187 | CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk), |
188 | CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk), | 188 | CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk), |
189 | CLKDEV_CON_DEV_ID("pclk", "fffc0000.ssc", &ssc0_clk), | ||
190 | CLKDEV_CON_DEV_ID("pclk", "fffc4000.ssc", &ssc1_clk), | ||
189 | CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.0", &twi0_clk), | 191 | CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.0", &twi0_clk), |
190 | CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.1", &twi1_clk), | 192 | CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.1", &twi1_clk), |
191 | CLKDEV_CON_ID("pioA", &pioA_clk), | 193 | CLKDEV_CON_ID("pioA", &pioA_clk), |
diff --git a/arch/arm/mach-at91/at91sam9rl_devices.c b/arch/arm/mach-at91/at91sam9rl_devices.c index 5047bdc92adf..b656110e8afe 100644 --- a/arch/arm/mach-at91/at91sam9rl_devices.c +++ b/arch/arm/mach-at91/at91sam9rl_devices.c | |||
@@ -832,7 +832,7 @@ static struct resource ssc0_resources[] = { | |||
832 | }; | 832 | }; |
833 | 833 | ||
834 | static struct platform_device at91sam9rl_ssc0_device = { | 834 | static struct platform_device at91sam9rl_ssc0_device = { |
835 | .name = "ssc", | 835 | .name = "at91rm9200_ssc", |
836 | .id = 0, | 836 | .id = 0, |
837 | .dev = { | 837 | .dev = { |
838 | .dma_mask = &ssc0_dmamask, | 838 | .dma_mask = &ssc0_dmamask, |
@@ -874,7 +874,7 @@ static struct resource ssc1_resources[] = { | |||
874 | }; | 874 | }; |
875 | 875 | ||
876 | static struct platform_device at91sam9rl_ssc1_device = { | 876 | static struct platform_device at91sam9rl_ssc1_device = { |
877 | .name = "ssc", | 877 | .name = "at91rm9200_ssc", |
878 | .id = 1, | 878 | .id = 1, |
879 | .dev = { | 879 | .dev = { |
880 | .dma_mask = &ssc1_dmamask, | 880 | .dma_mask = &ssc1_dmamask, |
diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c index e5035380dcbc..18fbbb27f97f 100644 --- a/arch/arm/mach-at91/at91sam9x5.c +++ b/arch/arm/mach-at91/at91sam9x5.c | |||
@@ -231,6 +231,7 @@ static struct clk_lookup periph_clocks_lookups[] = { | |||
231 | CLKDEV_CON_DEV_ID("t0_clk", "f800c000.timer", &tcb0_clk), | 231 | CLKDEV_CON_DEV_ID("t0_clk", "f800c000.timer", &tcb0_clk), |
232 | CLKDEV_CON_DEV_ID("dma_clk", "ffffec00.dma-controller", &dma0_clk), | 232 | CLKDEV_CON_DEV_ID("dma_clk", "ffffec00.dma-controller", &dma0_clk), |
233 | CLKDEV_CON_DEV_ID("dma_clk", "ffffee00.dma-controller", &dma1_clk), | 233 | CLKDEV_CON_DEV_ID("dma_clk", "ffffee00.dma-controller", &dma1_clk), |
234 | CLKDEV_CON_DEV_ID("pclk", "f0010000.ssc", &ssc_clk), | ||
234 | CLKDEV_CON_DEV_ID(NULL, "f8010000.i2c", &twi0_clk), | 235 | CLKDEV_CON_DEV_ID(NULL, "f8010000.i2c", &twi0_clk), |
235 | CLKDEV_CON_DEV_ID(NULL, "f8014000.i2c", &twi1_clk), | 236 | CLKDEV_CON_DEV_ID(NULL, "f8014000.i2c", &twi1_clk), |
236 | CLKDEV_CON_DEV_ID(NULL, "f8018000.i2c", &twi2_clk), | 237 | CLKDEV_CON_DEV_ID(NULL, "f8018000.i2c", &twi2_clk), |
diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c index 3ab2b86a3762..ebdbf42c02c1 100644 --- a/arch/arm/mach-at91/board-sam9g20ek.c +++ b/arch/arm/mach-at91/board-sam9g20ek.c | |||
@@ -353,6 +353,16 @@ static struct i2c_board_info __initdata ek_i2c_devices[] = { | |||
353 | }, | 353 | }, |
354 | }; | 354 | }; |
355 | 355 | ||
356 | static struct platform_device sam9g20ek_audio_device = { | ||
357 | .name = "at91sam9g20ek-audio", | ||
358 | .id = -1, | ||
359 | }; | ||
360 | |||
361 | static void __init ek_add_device_audio(void) | ||
362 | { | ||
363 | platform_device_register(&sam9g20ek_audio_device); | ||
364 | } | ||
365 | |||
356 | 366 | ||
357 | static void __init ek_board_init(void) | 367 | static void __init ek_board_init(void) |
358 | { | 368 | { |
@@ -394,6 +404,7 @@ static void __init ek_board_init(void) | |||
394 | at91_set_B_periph(AT91_PIN_PC1, 0); | 404 | at91_set_B_periph(AT91_PIN_PC1, 0); |
395 | /* SSC (for WM8731) */ | 405 | /* SSC (for WM8731) */ |
396 | at91_add_device_ssc(AT91SAM9260_ID_SSC, ATMEL_SSC_TX); | 406 | at91_add_device_ssc(AT91SAM9260_ID_SSC, ATMEL_SSC_TX); |
407 | ek_add_device_audio(); | ||
397 | } | 408 | } |
398 | 409 | ||
399 | MACHINE_START(AT91SAM9G20EK, "Atmel AT91SAM9G20-EK") | 410 | MACHINE_START(AT91SAM9G20EK, "Atmel AT91SAM9G20-EK") |
diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index 5bb187781074..d07a9eda7fff 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c | |||
@@ -18,6 +18,8 @@ | |||
18 | #include <linux/slab.h> | 18 | #include <linux/slab.h> |
19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
20 | 20 | ||
21 | #include <linux/of.h> | ||
22 | |||
21 | /* Serialize access to ssc_list and user count */ | 23 | /* Serialize access to ssc_list and user count */ |
22 | static DEFINE_SPINLOCK(user_lock); | 24 | static DEFINE_SPINLOCK(user_lock); |
23 | static LIST_HEAD(ssc_list); | 25 | static LIST_HEAD(ssc_list); |
@@ -29,7 +31,13 @@ struct ssc_device *ssc_request(unsigned int ssc_num) | |||
29 | 31 | ||
30 | spin_lock(&user_lock); | 32 | spin_lock(&user_lock); |
31 | list_for_each_entry(ssc, &ssc_list, list) { | 33 | list_for_each_entry(ssc, &ssc_list, list) { |
32 | if (ssc->pdev->id == ssc_num) { | 34 | if (ssc->pdev->dev.of_node) { |
35 | if (of_alias_get_id(ssc->pdev->dev.of_node, "ssc") | ||
36 | == ssc_num) { | ||
37 | ssc_valid = 1; | ||
38 | break; | ||
39 | } | ||
40 | } else if (ssc->pdev->id == ssc_num) { | ||
33 | ssc_valid = 1; | 41 | ssc_valid = 1; |
34 | break; | 42 | break; |
35 | } | 43 | } |
@@ -68,39 +76,93 @@ void ssc_free(struct ssc_device *ssc) | |||
68 | } | 76 | } |
69 | EXPORT_SYMBOL(ssc_free); | 77 | EXPORT_SYMBOL(ssc_free); |
70 | 78 | ||
71 | static int __init ssc_probe(struct platform_device *pdev) | 79 | static struct atmel_ssc_platform_data at91rm9200_config = { |
80 | .use_dma = 0, | ||
81 | }; | ||
82 | |||
83 | static struct atmel_ssc_platform_data at91sam9g45_config = { | ||
84 | .use_dma = 1, | ||
85 | }; | ||
86 | |||
87 | static const struct platform_device_id atmel_ssc_devtypes[] = { | ||
88 | { | ||
89 | .name = "at91rm9200_ssc", | ||
90 | .driver_data = (unsigned long) &at91rm9200_config, | ||
91 | }, { | ||
92 | .name = "at91sam9g45_ssc", | ||
93 | .driver_data = (unsigned long) &at91sam9g45_config, | ||
94 | }, { | ||
95 | /* sentinel */ | ||
96 | } | ||
97 | }; | ||
98 | |||
99 | #ifdef CONFIG_OF | ||
100 | static const struct of_device_id atmel_ssc_dt_ids[] = { | ||
101 | { | ||
102 | .compatible = "atmel,at91rm9200-ssc", | ||
103 | .data = &at91rm9200_config, | ||
104 | }, { | ||
105 | .compatible = "atmel,at91sam9g45-ssc", | ||
106 | .data = &at91sam9g45_config, | ||
107 | }, { | ||
108 | /* sentinel */ | ||
109 | } | ||
110 | }; | ||
111 | MODULE_DEVICE_TABLE(of, atmel_ssc_dt_ids); | ||
112 | #endif | ||
113 | |||
114 | static inline const struct atmel_ssc_platform_data * __init | ||
115 | atmel_ssc_get_driver_data(struct platform_device *pdev) | ||
116 | { | ||
117 | if (pdev->dev.of_node) { | ||
118 | const struct of_device_id *match; | ||
119 | match = of_match_node(atmel_ssc_dt_ids, pdev->dev.of_node); | ||
120 | if (match == NULL) | ||
121 | return NULL; | ||
122 | return match->data; | ||
123 | } | ||
124 | |||
125 | return (struct atmel_ssc_platform_data *) | ||
126 | platform_get_device_id(pdev)->driver_data; | ||
127 | } | ||
128 | |||
129 | static int ssc_probe(struct platform_device *pdev) | ||
72 | { | 130 | { |
73 | int retval = 0; | ||
74 | struct resource *regs; | 131 | struct resource *regs; |
75 | struct ssc_device *ssc; | 132 | struct ssc_device *ssc; |
133 | const struct atmel_ssc_platform_data *plat_dat; | ||
76 | 134 | ||
77 | ssc = kzalloc(sizeof(struct ssc_device), GFP_KERNEL); | 135 | ssc = devm_kzalloc(&pdev->dev, sizeof(struct ssc_device), GFP_KERNEL); |
78 | if (!ssc) { | 136 | if (!ssc) { |
79 | dev_dbg(&pdev->dev, "out of memory\n"); | 137 | dev_dbg(&pdev->dev, "out of memory\n"); |
80 | retval = -ENOMEM; | 138 | return -ENOMEM; |
81 | goto out; | ||
82 | } | 139 | } |
83 | 140 | ||
141 | ssc->pdev = pdev; | ||
142 | |||
143 | plat_dat = atmel_ssc_get_driver_data(pdev); | ||
144 | if (!plat_dat) | ||
145 | return -ENODEV; | ||
146 | ssc->pdata = (struct atmel_ssc_platform_data *)plat_dat; | ||
147 | |||
84 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 148 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
85 | if (!regs) { | 149 | if (!regs) { |
86 | dev_dbg(&pdev->dev, "no mmio resource defined\n"); | 150 | dev_dbg(&pdev->dev, "no mmio resource defined\n"); |
87 | retval = -ENXIO; | 151 | return -ENXIO; |
88 | goto out_free; | ||
89 | } | 152 | } |
90 | 153 | ||
91 | ssc->clk = clk_get(&pdev->dev, "pclk"); | 154 | ssc->regs = devm_request_and_ioremap(&pdev->dev, regs); |
92 | if (IS_ERR(ssc->clk)) { | ||
93 | dev_dbg(&pdev->dev, "no pclk clock defined\n"); | ||
94 | retval = -ENXIO; | ||
95 | goto out_free; | ||
96 | } | ||
97 | |||
98 | ssc->pdev = pdev; | ||
99 | ssc->regs = ioremap(regs->start, resource_size(regs)); | ||
100 | if (!ssc->regs) { | 155 | if (!ssc->regs) { |
101 | dev_dbg(&pdev->dev, "ioremap failed\n"); | 156 | dev_dbg(&pdev->dev, "ioremap failed\n"); |
102 | retval = -EINVAL; | 157 | return -EINVAL; |
103 | goto out_clk; | 158 | } |
159 | |||
160 | ssc->phybase = regs->start; | ||
161 | |||
162 | ssc->clk = devm_clk_get(&pdev->dev, "pclk"); | ||
163 | if (IS_ERR(ssc->clk)) { | ||
164 | dev_dbg(&pdev->dev, "no pclk clock defined\n"); | ||
165 | return -ENXIO; | ||
104 | } | 166 | } |
105 | 167 | ||
106 | /* disable all interrupts */ | 168 | /* disable all interrupts */ |
@@ -112,8 +174,7 @@ static int __init ssc_probe(struct platform_device *pdev) | |||
112 | ssc->irq = platform_get_irq(pdev, 0); | 174 | ssc->irq = platform_get_irq(pdev, 0); |
113 | if (!ssc->irq) { | 175 | if (!ssc->irq) { |
114 | dev_dbg(&pdev->dev, "could not get irq\n"); | 176 | dev_dbg(&pdev->dev, "could not get irq\n"); |
115 | retval = -ENXIO; | 177 | return -ENXIO; |
116 | goto out_unmap; | ||
117 | } | 178 | } |
118 | 179 | ||
119 | spin_lock(&user_lock); | 180 | spin_lock(&user_lock); |
@@ -125,16 +186,7 @@ static int __init ssc_probe(struct platform_device *pdev) | |||
125 | dev_info(&pdev->dev, "Atmel SSC device at 0x%p (irq %d)\n", | 186 | dev_info(&pdev->dev, "Atmel SSC device at 0x%p (irq %d)\n", |
126 | ssc->regs, ssc->irq); | 187 | ssc->regs, ssc->irq); |
127 | 188 | ||
128 | goto out; | 189 | return 0; |
129 | |||
130 | out_unmap: | ||
131 | iounmap(ssc->regs); | ||
132 | out_clk: | ||
133 | clk_put(ssc->clk); | ||
134 | out_free: | ||
135 | kfree(ssc); | ||
136 | out: | ||
137 | return retval; | ||
138 | } | 190 | } |
139 | 191 | ||
140 | static int __devexit ssc_remove(struct platform_device *pdev) | 192 | static int __devexit ssc_remove(struct platform_device *pdev) |
@@ -142,34 +194,23 @@ static int __devexit ssc_remove(struct platform_device *pdev) | |||
142 | struct ssc_device *ssc = platform_get_drvdata(pdev); | 194 | struct ssc_device *ssc = platform_get_drvdata(pdev); |
143 | 195 | ||
144 | spin_lock(&user_lock); | 196 | spin_lock(&user_lock); |
145 | iounmap(ssc->regs); | ||
146 | clk_put(ssc->clk); | ||
147 | list_del(&ssc->list); | 197 | list_del(&ssc->list); |
148 | kfree(ssc); | ||
149 | spin_unlock(&user_lock); | 198 | spin_unlock(&user_lock); |
150 | 199 | ||
151 | return 0; | 200 | return 0; |
152 | } | 201 | } |
153 | 202 | ||
154 | static struct platform_driver ssc_driver = { | 203 | static struct platform_driver ssc_driver = { |
155 | .remove = __devexit_p(ssc_remove), | ||
156 | .driver = { | 204 | .driver = { |
157 | .name = "ssc", | 205 | .name = "ssc", |
158 | .owner = THIS_MODULE, | 206 | .owner = THIS_MODULE, |
207 | .of_match_table = of_match_ptr(atmel_ssc_dt_ids), | ||
159 | }, | 208 | }, |
209 | .id_table = atmel_ssc_devtypes, | ||
210 | .probe = ssc_probe, | ||
211 | .remove = __devexit_p(ssc_remove), | ||
160 | }; | 212 | }; |
161 | 213 | module_platform_driver(ssc_driver); | |
162 | static int __init ssc_init(void) | ||
163 | { | ||
164 | return platform_driver_probe(&ssc_driver, ssc_probe); | ||
165 | } | ||
166 | module_init(ssc_init); | ||
167 | |||
168 | static void __exit ssc_exit(void) | ||
169 | { | ||
170 | platform_driver_unregister(&ssc_driver); | ||
171 | } | ||
172 | module_exit(ssc_exit); | ||
173 | 214 | ||
174 | MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>"); | 215 | MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>"); |
175 | MODULE_DESCRIPTION("SSC driver for Atmel AVR32 and AT91"); | 216 | MODULE_DESCRIPTION("SSC driver for Atmel AVR32 and AT91"); |
diff --git a/include/linux/atmel-ssc.h b/include/linux/atmel-ssc.h index 4eb31752e2b7..deb0ae58b99b 100644 --- a/include/linux/atmel-ssc.h +++ b/include/linux/atmel-ssc.h | |||
@@ -5,10 +5,16 @@ | |||
5 | #include <linux/list.h> | 5 | #include <linux/list.h> |
6 | #include <linux/io.h> | 6 | #include <linux/io.h> |
7 | 7 | ||
8 | struct atmel_ssc_platform_data { | ||
9 | int use_dma; | ||
10 | }; | ||
11 | |||
8 | struct ssc_device { | 12 | struct ssc_device { |
9 | struct list_head list; | 13 | struct list_head list; |
14 | resource_size_t phybase; | ||
10 | void __iomem *regs; | 15 | void __iomem *regs; |
11 | struct platform_device *pdev; | 16 | struct platform_device *pdev; |
17 | struct atmel_ssc_platform_data *pdata; | ||
12 | struct clk *clk; | 18 | struct clk *clk; |
13 | int user; | 19 | int user; |
14 | int irq; | 20 | int irq; |
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 72b09cfd3dc3..d1b691bf8e2d 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig | |||
@@ -6,6 +6,14 @@ config SND_ATMEL_SOC | |||
6 | the ATMEL SSC interface. You will also need | 6 | the ATMEL SSC interface. You will also need |
7 | to select the audio interfaces to support below. | 7 | to select the audio interfaces to support below. |
8 | 8 | ||
9 | config SND_ATMEL_SOC_PDC | ||
10 | tristate | ||
11 | depends on SND_ATMEL_SOC | ||
12 | |||
13 | config SND_ATMEL_SOC_DMA | ||
14 | tristate | ||
15 | depends on SND_ATMEL_SOC | ||
16 | |||
9 | config SND_ATMEL_SOC_SSC | 17 | config SND_ATMEL_SOC_SSC |
10 | tristate | 18 | tristate |
11 | depends on SND_ATMEL_SOC | 19 | depends on SND_ATMEL_SOC |
@@ -16,8 +24,8 @@ config SND_ATMEL_SOC_SSC | |||
16 | 24 | ||
17 | config SND_AT91_SOC_SAM9G20_WM8731 | 25 | config SND_AT91_SOC_SAM9G20_WM8731 |
18 | tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board" | 26 | tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board" |
19 | depends on ATMEL_SSC && ARCH_AT91SAM9G20 && SND_ATMEL_SOC && \ | 27 | depends on ATMEL_SSC && SND_ATMEL_SOC && AT91_PROGRAMMABLE_CLOCKS |
20 | AT91_PROGRAMMABLE_CLOCKS | 28 | select SND_ATMEL_SOC_PDC |
21 | select SND_ATMEL_SOC_SSC | 29 | select SND_ATMEL_SOC_SSC |
22 | select SND_SOC_WM8731 | 30 | select SND_SOC_WM8731 |
23 | help | 31 | help |
@@ -27,6 +35,7 @@ config SND_AT91_SOC_SAM9G20_WM8731 | |||
27 | config SND_AT91_SOC_AFEB9260 | 35 | config SND_AT91_SOC_AFEB9260 |
28 | tristate "SoC Audio support for AFEB9260 board" | 36 | tristate "SoC Audio support for AFEB9260 board" |
29 | depends on ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC | 37 | depends on ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC |
38 | select SND_ATMEL_SOC_PDC | ||
30 | select SND_ATMEL_SOC_SSC | 39 | select SND_ATMEL_SOC_SSC |
31 | select SND_SOC_TLV320AIC23 | 40 | select SND_SOC_TLV320AIC23 |
32 | help | 41 | help |
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index a5c0bf19da78..41967ccb6f41 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile | |||
@@ -1,8 +1,12 @@ | |||
1 | # AT91 Platform Support | 1 | # AT91 Platform Support |
2 | snd-soc-atmel-pcm-objs := atmel-pcm.o | 2 | snd-soc-atmel-pcm-objs := atmel-pcm.o |
3 | snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o | ||
4 | snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o | ||
3 | snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o | 5 | snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o |
4 | 6 | ||
5 | obj-$(CONFIG_SND_ATMEL_SOC) += snd-soc-atmel-pcm.o | 7 | obj-$(CONFIG_SND_ATMEL_SOC) += snd-soc-atmel-pcm.o |
8 | obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o | ||
9 | obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.o | ||
6 | obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o | 10 | obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o |
7 | 11 | ||
8 | # AT91 Machine Support | 12 | # AT91 Machine Support |
diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c new file mode 100644 index 000000000000..30184a4a147a --- /dev/null +++ b/sound/soc/atmel/atmel-pcm-dma.c | |||
@@ -0,0 +1,240 @@ | |||
1 | /* | ||
2 | * atmel-pcm-dma.c -- ALSA PCM DMA support for the Atmel SoC. | ||
3 | * | ||
4 | * Copyright (C) 2012 Atmel | ||
5 | * | ||
6 | * Author: Bo Shen <voice.shen@atmel.com> | ||
7 | * | ||
8 | * Based on atmel-pcm by: | ||
9 | * Sedji Gaouaou <sedji.gaouaou@atmel.com> | ||
10 | * Copyright 2008 Atmel | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | * GNU General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
25 | */ | ||
26 | |||
27 | #include <linux/module.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/platform_device.h> | ||
30 | #include <linux/slab.h> | ||
31 | #include <linux/dma-mapping.h> | ||
32 | #include <linux/dmaengine.h> | ||
33 | #include <linux/atmel-ssc.h> | ||
34 | #include <linux/platform_data/dma-atmel.h> | ||
35 | |||
36 | #include <sound/core.h> | ||
37 | #include <sound/pcm.h> | ||
38 | #include <sound/pcm_params.h> | ||
39 | #include <sound/soc.h> | ||
40 | #include <sound/dmaengine_pcm.h> | ||
41 | |||
42 | #include "atmel-pcm.h" | ||
43 | |||
44 | /*--------------------------------------------------------------------------*\ | ||
45 | * Hardware definition | ||
46 | \*--------------------------------------------------------------------------*/ | ||
47 | static const struct snd_pcm_hardware atmel_pcm_dma_hardware = { | ||
48 | .info = SNDRV_PCM_INFO_MMAP | | ||
49 | SNDRV_PCM_INFO_MMAP_VALID | | ||
50 | SNDRV_PCM_INFO_INTERLEAVED | | ||
51 | SNDRV_PCM_INFO_RESUME | | ||
52 | SNDRV_PCM_INFO_PAUSE, | ||
53 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
54 | .period_bytes_min = 256, /* lighting DMA overhead */ | ||
55 | .period_bytes_max = 2 * 0xffff, /* if 2 bytes format */ | ||
56 | .periods_min = 8, | ||
57 | .periods_max = 1024, /* no limit */ | ||
58 | .buffer_bytes_max = ATMEL_SSC_DMABUF_SIZE, | ||
59 | }; | ||
60 | |||
61 | /** | ||
62 | * atmel_pcm_dma_irq: SSC interrupt handler for DMAENGINE enabled SSC | ||
63 | * | ||
64 | * We use DMAENGINE to send/receive data to/from SSC so this ISR is only to | ||
65 | * check if any overrun occured. | ||
66 | */ | ||
67 | static void atmel_pcm_dma_irq(u32 ssc_sr, | ||
68 | struct snd_pcm_substream *substream) | ||
69 | { | ||
70 | struct atmel_pcm_dma_params *prtd; | ||
71 | |||
72 | prtd = snd_dmaengine_pcm_get_data(substream); | ||
73 | |||
74 | if (ssc_sr & prtd->mask->ssc_error) { | ||
75 | if (snd_pcm_running(substream)) | ||
76 | pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x)\n", | ||
77 | substream->stream == SNDRV_PCM_STREAM_PLAYBACK | ||
78 | ? "underrun" : "overrun", prtd->name, | ||
79 | ssc_sr); | ||
80 | |||
81 | /* stop RX and capture: will be enabled again at restart */ | ||
82 | ssc_writex(prtd->ssc->regs, SSC_CR, prtd->mask->ssc_disable); | ||
83 | snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); | ||
84 | |||
85 | /* now drain RHR and read status to remove xrun condition */ | ||
86 | ssc_readx(prtd->ssc->regs, SSC_RHR); | ||
87 | ssc_readx(prtd->ssc->regs, SSC_SR); | ||
88 | } | ||
89 | } | ||
90 | |||
91 | /*--------------------------------------------------------------------------*\ | ||
92 | * DMAENGINE operations | ||
93 | \*--------------------------------------------------------------------------*/ | ||
94 | static bool filter(struct dma_chan *chan, void *slave) | ||
95 | { | ||
96 | struct at_dma_slave *sl = slave; | ||
97 | |||
98 | if (sl->dma_dev == chan->device->dev) { | ||
99 | chan->private = sl; | ||
100 | return true; | ||
101 | } else { | ||
102 | return false; | ||
103 | } | ||
104 | } | ||
105 | |||
106 | static int atmel_pcm_configure_dma(struct snd_pcm_substream *substream, | ||
107 | struct snd_pcm_hw_params *params) | ||
108 | { | ||
109 | struct atmel_pcm_dma_params *prtd; | ||
110 | struct ssc_device *ssc; | ||
111 | struct dma_chan *dma_chan; | ||
112 | struct dma_slave_config slave_config; | ||
113 | int ret; | ||
114 | |||
115 | prtd = snd_dmaengine_pcm_get_data(substream); | ||
116 | ssc = prtd->ssc; | ||
117 | |||
118 | ret = snd_hwparams_to_dma_slave_config(substream, params, | ||
119 | &slave_config); | ||
120 | if (ret) { | ||
121 | pr_err("atmel-pcm: hwparams to dma slave configure failed\n"); | ||
122 | return ret; | ||
123 | } | ||
124 | |||
125 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
126 | slave_config.dst_addr = (dma_addr_t)ssc->phybase + SSC_THR; | ||
127 | slave_config.dst_maxburst = 1; | ||
128 | } else { | ||
129 | slave_config.src_addr = (dma_addr_t)ssc->phybase + SSC_RHR; | ||
130 | slave_config.src_maxburst = 1; | ||
131 | } | ||
132 | |||
133 | slave_config.device_fc = false; | ||
134 | |||
135 | dma_chan = snd_dmaengine_pcm_get_chan(substream); | ||
136 | if (dmaengine_slave_config(dma_chan, &slave_config)) { | ||
137 | pr_err("atmel-pcm: failed to configure dma channel\n"); | ||
138 | ret = -EBUSY; | ||
139 | return ret; | ||
140 | } | ||
141 | |||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | static int atmel_pcm_hw_params(struct snd_pcm_substream *substream, | ||
146 | struct snd_pcm_hw_params *params) | ||
147 | { | ||
148 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
149 | struct atmel_pcm_dma_params *prtd; | ||
150 | struct ssc_device *ssc; | ||
151 | struct at_dma_slave *sdata = NULL; | ||
152 | int ret; | ||
153 | |||
154 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | ||
155 | |||
156 | prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | ||
157 | ssc = prtd->ssc; | ||
158 | if (ssc->pdev) | ||
159 | sdata = ssc->pdev->dev.platform_data; | ||
160 | |||
161 | ret = snd_dmaengine_pcm_open(substream, filter, sdata); | ||
162 | if (ret) { | ||
163 | pr_err("atmel-pcm: dmaengine pcm open failed\n"); | ||
164 | return -EINVAL; | ||
165 | } | ||
166 | |||
167 | snd_dmaengine_pcm_set_data(substream, prtd); | ||
168 | |||
169 | ret = atmel_pcm_configure_dma(substream, params); | ||
170 | if (ret) { | ||
171 | pr_err("atmel-pcm: failed to configure dmai\n"); | ||
172 | goto err; | ||
173 | } | ||
174 | |||
175 | prtd->dma_intr_handler = atmel_pcm_dma_irq; | ||
176 | |||
177 | return 0; | ||
178 | err: | ||
179 | snd_dmaengine_pcm_close(substream); | ||
180 | return ret; | ||
181 | } | ||
182 | |||
183 | static int atmel_pcm_dma_prepare(struct snd_pcm_substream *substream) | ||
184 | { | ||
185 | struct atmel_pcm_dma_params *prtd; | ||
186 | |||
187 | prtd = snd_dmaengine_pcm_get_data(substream); | ||
188 | |||
189 | ssc_writex(prtd->ssc->regs, SSC_IER, prtd->mask->ssc_error); | ||
190 | ssc_writex(prtd->ssc->regs, SSC_CR, prtd->mask->ssc_enable); | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static int atmel_pcm_open(struct snd_pcm_substream *substream) | ||
196 | { | ||
197 | snd_soc_set_runtime_hwparams(substream, &atmel_pcm_dma_hardware); | ||
198 | |||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | static int atmel_pcm_close(struct snd_pcm_substream *substream) | ||
203 | { | ||
204 | snd_dmaengine_pcm_close(substream); | ||
205 | |||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | static struct snd_pcm_ops atmel_pcm_ops = { | ||
210 | .open = atmel_pcm_open, | ||
211 | .close = atmel_pcm_close, | ||
212 | .ioctl = snd_pcm_lib_ioctl, | ||
213 | .hw_params = atmel_pcm_hw_params, | ||
214 | .prepare = atmel_pcm_dma_prepare, | ||
215 | .trigger = snd_dmaengine_pcm_trigger, | ||
216 | .pointer = snd_dmaengine_pcm_pointer_no_residue, | ||
217 | .mmap = atmel_pcm_mmap, | ||
218 | }; | ||
219 | |||
220 | static struct snd_soc_platform_driver atmel_soc_platform = { | ||
221 | .ops = &atmel_pcm_ops, | ||
222 | .pcm_new = atmel_pcm_new, | ||
223 | .pcm_free = atmel_pcm_free, | ||
224 | }; | ||
225 | |||
226 | int atmel_pcm_dma_platform_register(struct device *dev) | ||
227 | { | ||
228 | return snd_soc_register_platform(dev, &atmel_soc_platform); | ||
229 | } | ||
230 | EXPORT_SYMBOL(atmel_pcm_dma_platform_register); | ||
231 | |||
232 | void atmel_pcm_dma_platform_unregister(struct device *dev) | ||
233 | { | ||
234 | snd_soc_unregister_platform(dev); | ||
235 | } | ||
236 | EXPORT_SYMBOL(atmel_pcm_dma_platform_unregister); | ||
237 | |||
238 | MODULE_AUTHOR("Bo Shen <voice.shen@atmel.com>"); | ||
239 | MODULE_DESCRIPTION("Atmel DMA based PCM module"); | ||
240 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/atmel/atmel-pcm-pdc.c b/sound/soc/atmel/atmel-pcm-pdc.c new file mode 100644 index 000000000000..6a293c713a38 --- /dev/null +++ b/sound/soc/atmel/atmel-pcm-pdc.c | |||
@@ -0,0 +1,401 @@ | |||
1 | /* | ||
2 | * atmel-pcm.c -- ALSA PCM interface for the Atmel atmel SoC. | ||
3 | * | ||
4 | * Copyright (C) 2005 SAN People | ||
5 | * Copyright (C) 2008 Atmel | ||
6 | * | ||
7 | * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com> | ||
8 | * | ||
9 | * Based on at91-pcm. by: | ||
10 | * Frank Mandarino <fmandarino@endrelia.com> | ||
11 | * Copyright 2006 Endrelia Technologies Inc. | ||
12 | * | ||
13 | * Based on pxa2xx-pcm.c by: | ||
14 | * | ||
15 | * Author: Nicolas Pitre | ||
16 | * Created: Nov 30, 2004 | ||
17 | * Copyright: (C) 2004 MontaVista Software, Inc. | ||
18 | * | ||
19 | * This program is free software; you can redistribute it and/or modify | ||
20 | * it under the terms of the GNU General Public License as published by | ||
21 | * the Free Software Foundation; either version 2 of the License, or | ||
22 | * (at your option) any later version. | ||
23 | * | ||
24 | * This program is distributed in the hope that it will be useful, | ||
25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
27 | * GNU General Public License for more details. | ||
28 | * | ||
29 | * You should have received a copy of the GNU General Public License | ||
30 | * along with this program; if not, write to the Free Software | ||
31 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
32 | */ | ||
33 | |||
34 | #include <linux/module.h> | ||
35 | #include <linux/init.h> | ||
36 | #include <linux/platform_device.h> | ||
37 | #include <linux/slab.h> | ||
38 | #include <linux/dma-mapping.h> | ||
39 | #include <linux/atmel_pdc.h> | ||
40 | #include <linux/atmel-ssc.h> | ||
41 | |||
42 | #include <sound/core.h> | ||
43 | #include <sound/pcm.h> | ||
44 | #include <sound/pcm_params.h> | ||
45 | #include <sound/soc.h> | ||
46 | |||
47 | #include "atmel-pcm.h" | ||
48 | |||
49 | |||
50 | /*--------------------------------------------------------------------------*\ | ||
51 | * Hardware definition | ||
52 | \*--------------------------------------------------------------------------*/ | ||
53 | /* TODO: These values were taken from the AT91 platform driver, check | ||
54 | * them against real values for AT32 | ||
55 | */ | ||
56 | static const struct snd_pcm_hardware atmel_pcm_hardware = { | ||
57 | .info = SNDRV_PCM_INFO_MMAP | | ||
58 | SNDRV_PCM_INFO_MMAP_VALID | | ||
59 | SNDRV_PCM_INFO_INTERLEAVED | | ||
60 | SNDRV_PCM_INFO_PAUSE, | ||
61 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
62 | .period_bytes_min = 32, | ||
63 | .period_bytes_max = 8192, | ||
64 | .periods_min = 2, | ||
65 | .periods_max = 1024, | ||
66 | .buffer_bytes_max = ATMEL_SSC_DMABUF_SIZE, | ||
67 | }; | ||
68 | |||
69 | |||
70 | /*--------------------------------------------------------------------------*\ | ||
71 | * Data types | ||
72 | \*--------------------------------------------------------------------------*/ | ||
73 | struct atmel_runtime_data { | ||
74 | struct atmel_pcm_dma_params *params; | ||
75 | dma_addr_t dma_buffer; /* physical address of dma buffer */ | ||
76 | dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */ | ||
77 | size_t period_size; | ||
78 | |||
79 | dma_addr_t period_ptr; /* physical address of next period */ | ||
80 | |||
81 | /* PDC register save */ | ||
82 | u32 pdc_xpr_save; | ||
83 | u32 pdc_xcr_save; | ||
84 | u32 pdc_xnpr_save; | ||
85 | u32 pdc_xncr_save; | ||
86 | }; | ||
87 | |||
88 | /*--------------------------------------------------------------------------*\ | ||
89 | * ISR | ||
90 | \*--------------------------------------------------------------------------*/ | ||
91 | static void atmel_pcm_dma_irq(u32 ssc_sr, | ||
92 | struct snd_pcm_substream *substream) | ||
93 | { | ||
94 | struct atmel_runtime_data *prtd = substream->runtime->private_data; | ||
95 | struct atmel_pcm_dma_params *params = prtd->params; | ||
96 | static int count; | ||
97 | |||
98 | count++; | ||
99 | |||
100 | if (ssc_sr & params->mask->ssc_endbuf) { | ||
101 | pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n", | ||
102 | substream->stream == SNDRV_PCM_STREAM_PLAYBACK | ||
103 | ? "underrun" : "overrun", | ||
104 | params->name, ssc_sr, count); | ||
105 | |||
106 | /* re-start the PDC */ | ||
107 | ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, | ||
108 | params->mask->pdc_disable); | ||
109 | prtd->period_ptr += prtd->period_size; | ||
110 | if (prtd->period_ptr >= prtd->dma_buffer_end) | ||
111 | prtd->period_ptr = prtd->dma_buffer; | ||
112 | |||
113 | ssc_writex(params->ssc->regs, params->pdc->xpr, | ||
114 | prtd->period_ptr); | ||
115 | ssc_writex(params->ssc->regs, params->pdc->xcr, | ||
116 | prtd->period_size / params->pdc_xfer_size); | ||
117 | ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, | ||
118 | params->mask->pdc_enable); | ||
119 | } | ||
120 | |||
121 | if (ssc_sr & params->mask->ssc_endx) { | ||
122 | /* Load the PDC next pointer and counter registers */ | ||
123 | prtd->period_ptr += prtd->period_size; | ||
124 | if (prtd->period_ptr >= prtd->dma_buffer_end) | ||
125 | prtd->period_ptr = prtd->dma_buffer; | ||
126 | |||
127 | ssc_writex(params->ssc->regs, params->pdc->xnpr, | ||
128 | prtd->period_ptr); | ||
129 | ssc_writex(params->ssc->regs, params->pdc->xncr, | ||
130 | prtd->period_size / params->pdc_xfer_size); | ||
131 | } | ||
132 | |||
133 | snd_pcm_period_elapsed(substream); | ||
134 | } | ||
135 | |||
136 | |||
137 | /*--------------------------------------------------------------------------*\ | ||
138 | * PCM operations | ||
139 | \*--------------------------------------------------------------------------*/ | ||
140 | static int atmel_pcm_hw_params(struct snd_pcm_substream *substream, | ||
141 | struct snd_pcm_hw_params *params) | ||
142 | { | ||
143 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
144 | struct atmel_runtime_data *prtd = runtime->private_data; | ||
145 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
146 | |||
147 | /* this may get called several times by oss emulation | ||
148 | * with different params */ | ||
149 | |||
150 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | ||
151 | runtime->dma_bytes = params_buffer_bytes(params); | ||
152 | |||
153 | prtd->params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | ||
154 | prtd->params->dma_intr_handler = atmel_pcm_dma_irq; | ||
155 | |||
156 | prtd->dma_buffer = runtime->dma_addr; | ||
157 | prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes; | ||
158 | prtd->period_size = params_period_bytes(params); | ||
159 | |||
160 | pr_debug("atmel-pcm: " | ||
161 | "hw_params: DMA for %s initialized " | ||
162 | "(dma_bytes=%u, period_size=%u)\n", | ||
163 | prtd->params->name, | ||
164 | runtime->dma_bytes, | ||
165 | prtd->period_size); | ||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | static int atmel_pcm_hw_free(struct snd_pcm_substream *substream) | ||
170 | { | ||
171 | struct atmel_runtime_data *prtd = substream->runtime->private_data; | ||
172 | struct atmel_pcm_dma_params *params = prtd->params; | ||
173 | |||
174 | if (params != NULL) { | ||
175 | ssc_writex(params->ssc->regs, SSC_PDC_PTCR, | ||
176 | params->mask->pdc_disable); | ||
177 | prtd->params->dma_intr_handler = NULL; | ||
178 | } | ||
179 | |||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | static int atmel_pcm_prepare(struct snd_pcm_substream *substream) | ||
184 | { | ||
185 | struct atmel_runtime_data *prtd = substream->runtime->private_data; | ||
186 | struct atmel_pcm_dma_params *params = prtd->params; | ||
187 | |||
188 | ssc_writex(params->ssc->regs, SSC_IDR, | ||
189 | params->mask->ssc_endx | params->mask->ssc_endbuf); | ||
190 | ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, | ||
191 | params->mask->pdc_disable); | ||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static int atmel_pcm_trigger(struct snd_pcm_substream *substream, | ||
196 | int cmd) | ||
197 | { | ||
198 | struct snd_pcm_runtime *rtd = substream->runtime; | ||
199 | struct atmel_runtime_data *prtd = rtd->private_data; | ||
200 | struct atmel_pcm_dma_params *params = prtd->params; | ||
201 | int ret = 0; | ||
202 | |||
203 | pr_debug("atmel-pcm:buffer_size = %ld," | ||
204 | "dma_area = %p, dma_bytes = %u\n", | ||
205 | rtd->buffer_size, rtd->dma_area, rtd->dma_bytes); | ||
206 | |||
207 | switch (cmd) { | ||
208 | case SNDRV_PCM_TRIGGER_START: | ||
209 | prtd->period_ptr = prtd->dma_buffer; | ||
210 | |||
211 | ssc_writex(params->ssc->regs, params->pdc->xpr, | ||
212 | prtd->period_ptr); | ||
213 | ssc_writex(params->ssc->regs, params->pdc->xcr, | ||
214 | prtd->period_size / params->pdc_xfer_size); | ||
215 | |||
216 | prtd->period_ptr += prtd->period_size; | ||
217 | ssc_writex(params->ssc->regs, params->pdc->xnpr, | ||
218 | prtd->period_ptr); | ||
219 | ssc_writex(params->ssc->regs, params->pdc->xncr, | ||
220 | prtd->period_size / params->pdc_xfer_size); | ||
221 | |||
222 | pr_debug("atmel-pcm: trigger: " | ||
223 | "period_ptr=%lx, xpr=%u, " | ||
224 | "xcr=%u, xnpr=%u, xncr=%u\n", | ||
225 | (unsigned long)prtd->period_ptr, | ||
226 | ssc_readx(params->ssc->regs, params->pdc->xpr), | ||
227 | ssc_readx(params->ssc->regs, params->pdc->xcr), | ||
228 | ssc_readx(params->ssc->regs, params->pdc->xnpr), | ||
229 | ssc_readx(params->ssc->regs, params->pdc->xncr)); | ||
230 | |||
231 | ssc_writex(params->ssc->regs, SSC_IER, | ||
232 | params->mask->ssc_endx | params->mask->ssc_endbuf); | ||
233 | ssc_writex(params->ssc->regs, SSC_PDC_PTCR, | ||
234 | params->mask->pdc_enable); | ||
235 | |||
236 | pr_debug("sr=%u imr=%u\n", | ||
237 | ssc_readx(params->ssc->regs, SSC_SR), | ||
238 | ssc_readx(params->ssc->regs, SSC_IER)); | ||
239 | break; /* SNDRV_PCM_TRIGGER_START */ | ||
240 | |||
241 | case SNDRV_PCM_TRIGGER_STOP: | ||
242 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
243 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
244 | ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, | ||
245 | params->mask->pdc_disable); | ||
246 | break; | ||
247 | |||
248 | case SNDRV_PCM_TRIGGER_RESUME: | ||
249 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
250 | ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, | ||
251 | params->mask->pdc_enable); | ||
252 | break; | ||
253 | |||
254 | default: | ||
255 | ret = -EINVAL; | ||
256 | } | ||
257 | |||
258 | return ret; | ||
259 | } | ||
260 | |||
261 | static snd_pcm_uframes_t atmel_pcm_pointer( | ||
262 | struct snd_pcm_substream *substream) | ||
263 | { | ||
264 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
265 | struct atmel_runtime_data *prtd = runtime->private_data; | ||
266 | struct atmel_pcm_dma_params *params = prtd->params; | ||
267 | dma_addr_t ptr; | ||
268 | snd_pcm_uframes_t x; | ||
269 | |||
270 | ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr); | ||
271 | x = bytes_to_frames(runtime, ptr - prtd->dma_buffer); | ||
272 | |||
273 | if (x == runtime->buffer_size) | ||
274 | x = 0; | ||
275 | |||
276 | return x; | ||
277 | } | ||
278 | |||
279 | static int atmel_pcm_open(struct snd_pcm_substream *substream) | ||
280 | { | ||
281 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
282 | struct atmel_runtime_data *prtd; | ||
283 | int ret = 0; | ||
284 | |||
285 | snd_soc_set_runtime_hwparams(substream, &atmel_pcm_hardware); | ||
286 | |||
287 | /* ensure that buffer size is a multiple of period size */ | ||
288 | ret = snd_pcm_hw_constraint_integer(runtime, | ||
289 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
290 | if (ret < 0) | ||
291 | goto out; | ||
292 | |||
293 | prtd = kzalloc(sizeof(struct atmel_runtime_data), GFP_KERNEL); | ||
294 | if (prtd == NULL) { | ||
295 | ret = -ENOMEM; | ||
296 | goto out; | ||
297 | } | ||
298 | runtime->private_data = prtd; | ||
299 | |||
300 | out: | ||
301 | return ret; | ||
302 | } | ||
303 | |||
304 | static int atmel_pcm_close(struct snd_pcm_substream *substream) | ||
305 | { | ||
306 | struct atmel_runtime_data *prtd = substream->runtime->private_data; | ||
307 | |||
308 | kfree(prtd); | ||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | static struct snd_pcm_ops atmel_pcm_ops = { | ||
313 | .open = atmel_pcm_open, | ||
314 | .close = atmel_pcm_close, | ||
315 | .ioctl = snd_pcm_lib_ioctl, | ||
316 | .hw_params = atmel_pcm_hw_params, | ||
317 | .hw_free = atmel_pcm_hw_free, | ||
318 | .prepare = atmel_pcm_prepare, | ||
319 | .trigger = atmel_pcm_trigger, | ||
320 | .pointer = atmel_pcm_pointer, | ||
321 | .mmap = atmel_pcm_mmap, | ||
322 | }; | ||
323 | |||
324 | |||
325 | /*--------------------------------------------------------------------------*\ | ||
326 | * ASoC platform driver | ||
327 | \*--------------------------------------------------------------------------*/ | ||
328 | #ifdef CONFIG_PM | ||
329 | static int atmel_pcm_suspend(struct snd_soc_dai *dai) | ||
330 | { | ||
331 | struct snd_pcm_runtime *runtime = dai->runtime; | ||
332 | struct atmel_runtime_data *prtd; | ||
333 | struct atmel_pcm_dma_params *params; | ||
334 | |||
335 | if (!runtime) | ||
336 | return 0; | ||
337 | |||
338 | prtd = runtime->private_data; | ||
339 | params = prtd->params; | ||
340 | |||
341 | /* disable the PDC and save the PDC registers */ | ||
342 | |||
343 | ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_disable); | ||
344 | |||
345 | prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr); | ||
346 | prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr); | ||
347 | prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr); | ||
348 | prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr); | ||
349 | |||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | static int atmel_pcm_resume(struct snd_soc_dai *dai) | ||
354 | { | ||
355 | struct snd_pcm_runtime *runtime = dai->runtime; | ||
356 | struct atmel_runtime_data *prtd; | ||
357 | struct atmel_pcm_dma_params *params; | ||
358 | |||
359 | if (!runtime) | ||
360 | return 0; | ||
361 | |||
362 | prtd = runtime->private_data; | ||
363 | params = prtd->params; | ||
364 | |||
365 | /* restore the PDC registers and enable the PDC */ | ||
366 | ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save); | ||
367 | ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save); | ||
368 | ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save); | ||
369 | ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save); | ||
370 | |||
371 | ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable); | ||
372 | return 0; | ||
373 | } | ||
374 | #else | ||
375 | #define atmel_pcm_suspend NULL | ||
376 | #define atmel_pcm_resume NULL | ||
377 | #endif | ||
378 | |||
379 | static struct snd_soc_platform_driver atmel_soc_platform = { | ||
380 | .ops = &atmel_pcm_ops, | ||
381 | .pcm_new = atmel_pcm_new, | ||
382 | .pcm_free = atmel_pcm_free, | ||
383 | .suspend = atmel_pcm_suspend, | ||
384 | .resume = atmel_pcm_resume, | ||
385 | }; | ||
386 | |||
387 | int atmel_pcm_pdc_platform_register(struct device *dev) | ||
388 | { | ||
389 | return snd_soc_register_platform(dev, &atmel_soc_platform); | ||
390 | } | ||
391 | EXPORT_SYMBOL(atmel_pcm_pdc_platform_register); | ||
392 | |||
393 | void atmel_pcm_pdc_platform_unregister(struct device *dev) | ||
394 | { | ||
395 | snd_soc_unregister_platform(dev); | ||
396 | } | ||
397 | EXPORT_SYMBOL(atmel_pcm_pdc_platform_unregister); | ||
398 | |||
399 | MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>"); | ||
400 | MODULE_DESCRIPTION("Atmel PCM module"); | ||
401 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c index 9b84f985770e..e99f1811300a 100644 --- a/sound/soc/atmel/atmel-pcm.c +++ b/sound/soc/atmel/atmel-pcm.c | |||
@@ -32,80 +32,25 @@ | |||
32 | */ | 32 | */ |
33 | 33 | ||
34 | #include <linux/module.h> | 34 | #include <linux/module.h> |
35 | #include <linux/init.h> | ||
36 | #include <linux/platform_device.h> | ||
37 | #include <linux/slab.h> | ||
38 | #include <linux/dma-mapping.h> | 35 | #include <linux/dma-mapping.h> |
39 | #include <linux/atmel_pdc.h> | ||
40 | #include <linux/atmel-ssc.h> | ||
41 | |||
42 | #include <sound/core.h> | ||
43 | #include <sound/pcm.h> | 36 | #include <sound/pcm.h> |
44 | #include <sound/pcm_params.h> | ||
45 | #include <sound/soc.h> | 37 | #include <sound/soc.h> |
46 | |||
47 | #include "atmel-pcm.h" | 38 | #include "atmel-pcm.h" |
48 | 39 | ||
49 | |||
50 | /*--------------------------------------------------------------------------*\ | ||
51 | * Hardware definition | ||
52 | \*--------------------------------------------------------------------------*/ | ||
53 | /* TODO: These values were taken from the AT91 platform driver, check | ||
54 | * them against real values for AT32 | ||
55 | */ | ||
56 | static const struct snd_pcm_hardware atmel_pcm_hardware = { | ||
57 | .info = SNDRV_PCM_INFO_MMAP | | ||
58 | SNDRV_PCM_INFO_MMAP_VALID | | ||
59 | SNDRV_PCM_INFO_INTERLEAVED | | ||
60 | SNDRV_PCM_INFO_PAUSE, | ||
61 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
62 | .period_bytes_min = 32, | ||
63 | .period_bytes_max = 8192, | ||
64 | .periods_min = 2, | ||
65 | .periods_max = 1024, | ||
66 | .buffer_bytes_max = 32 * 1024, | ||
67 | }; | ||
68 | |||
69 | |||
70 | /*--------------------------------------------------------------------------*\ | ||
71 | * Data types | ||
72 | \*--------------------------------------------------------------------------*/ | ||
73 | struct atmel_runtime_data { | ||
74 | struct atmel_pcm_dma_params *params; | ||
75 | dma_addr_t dma_buffer; /* physical address of dma buffer */ | ||
76 | dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */ | ||
77 | size_t period_size; | ||
78 | |||
79 | dma_addr_t period_ptr; /* physical address of next period */ | ||
80 | |||
81 | /* PDC register save */ | ||
82 | u32 pdc_xpr_save; | ||
83 | u32 pdc_xcr_save; | ||
84 | u32 pdc_xnpr_save; | ||
85 | u32 pdc_xncr_save; | ||
86 | }; | ||
87 | |||
88 | |||
89 | /*--------------------------------------------------------------------------*\ | ||
90 | * Helper functions | ||
91 | \*--------------------------------------------------------------------------*/ | ||
92 | static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, | 40 | static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, |
93 | int stream) | 41 | int stream) |
94 | { | 42 | { |
95 | struct snd_pcm_substream *substream = pcm->streams[stream].substream; | 43 | struct snd_pcm_substream *substream = pcm->streams[stream].substream; |
96 | struct snd_dma_buffer *buf = &substream->dma_buffer; | 44 | struct snd_dma_buffer *buf = &substream->dma_buffer; |
97 | size_t size = atmel_pcm_hardware.buffer_bytes_max; | 45 | size_t size = ATMEL_SSC_DMABUF_SIZE; |
98 | 46 | ||
99 | buf->dev.type = SNDRV_DMA_TYPE_DEV; | 47 | buf->dev.type = SNDRV_DMA_TYPE_DEV; |
100 | buf->dev.dev = pcm->card->dev; | 48 | buf->dev.dev = pcm->card->dev; |
101 | buf->private_data = NULL; | 49 | buf->private_data = NULL; |
102 | buf->area = dma_alloc_coherent(pcm->card->dev, size, | 50 | buf->area = dma_alloc_coherent(pcm->card->dev, size, |
103 | &buf->addr, GFP_KERNEL); | 51 | &buf->addr, GFP_KERNEL); |
104 | pr_debug("atmel-pcm:" | 52 | pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%d\n", |
105 | "preallocate_dma_buffer: area=%p, addr=%p, size=%d\n", | 53 | (void *)buf->area, (void *)buf->addr, size); |
106 | (void *) buf->area, | ||
107 | (void *) buf->addr, | ||
108 | size); | ||
109 | 54 | ||
110 | if (!buf->area) | 55 | if (!buf->area) |
111 | return -ENOMEM; | 56 | return -ENOMEM; |
@@ -113,258 +58,19 @@ static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, | |||
113 | buf->bytes = size; | 58 | buf->bytes = size; |
114 | return 0; | 59 | return 0; |
115 | } | 60 | } |
116 | /*--------------------------------------------------------------------------*\ | ||
117 | * ISR | ||
118 | \*--------------------------------------------------------------------------*/ | ||
119 | static void atmel_pcm_dma_irq(u32 ssc_sr, | ||
120 | struct snd_pcm_substream *substream) | ||
121 | { | ||
122 | struct atmel_runtime_data *prtd = substream->runtime->private_data; | ||
123 | struct atmel_pcm_dma_params *params = prtd->params; | ||
124 | static int count; | ||
125 | |||
126 | count++; | ||
127 | |||
128 | if (ssc_sr & params->mask->ssc_endbuf) { | ||
129 | pr_warning("atmel-pcm: buffer %s on %s" | ||
130 | " (SSC_SR=%#x, count=%d)\n", | ||
131 | substream->stream == SNDRV_PCM_STREAM_PLAYBACK | ||
132 | ? "underrun" : "overrun", | ||
133 | params->name, ssc_sr, count); | ||
134 | |||
135 | /* re-start the PDC */ | ||
136 | ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, | ||
137 | params->mask->pdc_disable); | ||
138 | prtd->period_ptr += prtd->period_size; | ||
139 | if (prtd->period_ptr >= prtd->dma_buffer_end) | ||
140 | prtd->period_ptr = prtd->dma_buffer; | ||
141 | |||
142 | ssc_writex(params->ssc->regs, params->pdc->xpr, | ||
143 | prtd->period_ptr); | ||
144 | ssc_writex(params->ssc->regs, params->pdc->xcr, | ||
145 | prtd->period_size / params->pdc_xfer_size); | ||
146 | ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, | ||
147 | params->mask->pdc_enable); | ||
148 | } | ||
149 | |||
150 | if (ssc_sr & params->mask->ssc_endx) { | ||
151 | /* Load the PDC next pointer and counter registers */ | ||
152 | prtd->period_ptr += prtd->period_size; | ||
153 | if (prtd->period_ptr >= prtd->dma_buffer_end) | ||
154 | prtd->period_ptr = prtd->dma_buffer; | ||
155 | |||
156 | ssc_writex(params->ssc->regs, params->pdc->xnpr, | ||
157 | prtd->period_ptr); | ||
158 | ssc_writex(params->ssc->regs, params->pdc->xncr, | ||
159 | prtd->period_size / params->pdc_xfer_size); | ||
160 | } | ||
161 | |||
162 | snd_pcm_period_elapsed(substream); | ||
163 | } | ||
164 | |||
165 | |||
166 | /*--------------------------------------------------------------------------*\ | ||
167 | * PCM operations | ||
168 | \*--------------------------------------------------------------------------*/ | ||
169 | static int atmel_pcm_hw_params(struct snd_pcm_substream *substream, | ||
170 | struct snd_pcm_hw_params *params) | ||
171 | { | ||
172 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
173 | struct atmel_runtime_data *prtd = runtime->private_data; | ||
174 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
175 | |||
176 | /* this may get called several times by oss emulation | ||
177 | * with different params */ | ||
178 | |||
179 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | ||
180 | runtime->dma_bytes = params_buffer_bytes(params); | ||
181 | |||
182 | prtd->params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | ||
183 | prtd->params->dma_intr_handler = atmel_pcm_dma_irq; | ||
184 | |||
185 | prtd->dma_buffer = runtime->dma_addr; | ||
186 | prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes; | ||
187 | prtd->period_size = params_period_bytes(params); | ||
188 | |||
189 | pr_debug("atmel-pcm: " | ||
190 | "hw_params: DMA for %s initialized " | ||
191 | "(dma_bytes=%u, period_size=%u)\n", | ||
192 | prtd->params->name, | ||
193 | runtime->dma_bytes, | ||
194 | prtd->period_size); | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | static int atmel_pcm_hw_free(struct snd_pcm_substream *substream) | ||
199 | { | ||
200 | struct atmel_runtime_data *prtd = substream->runtime->private_data; | ||
201 | struct atmel_pcm_dma_params *params = prtd->params; | ||
202 | |||
203 | if (params != NULL) { | ||
204 | ssc_writex(params->ssc->regs, SSC_PDC_PTCR, | ||
205 | params->mask->pdc_disable); | ||
206 | prtd->params->dma_intr_handler = NULL; | ||
207 | } | ||
208 | |||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | static int atmel_pcm_prepare(struct snd_pcm_substream *substream) | ||
213 | { | ||
214 | struct atmel_runtime_data *prtd = substream->runtime->private_data; | ||
215 | struct atmel_pcm_dma_params *params = prtd->params; | ||
216 | |||
217 | ssc_writex(params->ssc->regs, SSC_IDR, | ||
218 | params->mask->ssc_endx | params->mask->ssc_endbuf); | ||
219 | ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, | ||
220 | params->mask->pdc_disable); | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static int atmel_pcm_trigger(struct snd_pcm_substream *substream, | ||
225 | int cmd) | ||
226 | { | ||
227 | struct snd_pcm_runtime *rtd = substream->runtime; | ||
228 | struct atmel_runtime_data *prtd = rtd->private_data; | ||
229 | struct atmel_pcm_dma_params *params = prtd->params; | ||
230 | int ret = 0; | ||
231 | |||
232 | pr_debug("atmel-pcm:buffer_size = %ld," | ||
233 | "dma_area = %p, dma_bytes = %u\n", | ||
234 | rtd->buffer_size, rtd->dma_area, rtd->dma_bytes); | ||
235 | |||
236 | switch (cmd) { | ||
237 | case SNDRV_PCM_TRIGGER_START: | ||
238 | prtd->period_ptr = prtd->dma_buffer; | ||
239 | |||
240 | ssc_writex(params->ssc->regs, params->pdc->xpr, | ||
241 | prtd->period_ptr); | ||
242 | ssc_writex(params->ssc->regs, params->pdc->xcr, | ||
243 | prtd->period_size / params->pdc_xfer_size); | ||
244 | |||
245 | prtd->period_ptr += prtd->period_size; | ||
246 | ssc_writex(params->ssc->regs, params->pdc->xnpr, | ||
247 | prtd->period_ptr); | ||
248 | ssc_writex(params->ssc->regs, params->pdc->xncr, | ||
249 | prtd->period_size / params->pdc_xfer_size); | ||
250 | |||
251 | pr_debug("atmel-pcm: trigger: " | ||
252 | "period_ptr=%lx, xpr=%u, " | ||
253 | "xcr=%u, xnpr=%u, xncr=%u\n", | ||
254 | (unsigned long)prtd->period_ptr, | ||
255 | ssc_readx(params->ssc->regs, params->pdc->xpr), | ||
256 | ssc_readx(params->ssc->regs, params->pdc->xcr), | ||
257 | ssc_readx(params->ssc->regs, params->pdc->xnpr), | ||
258 | ssc_readx(params->ssc->regs, params->pdc->xncr)); | ||
259 | |||
260 | ssc_writex(params->ssc->regs, SSC_IER, | ||
261 | params->mask->ssc_endx | params->mask->ssc_endbuf); | ||
262 | ssc_writex(params->ssc->regs, SSC_PDC_PTCR, | ||
263 | params->mask->pdc_enable); | ||
264 | |||
265 | pr_debug("sr=%u imr=%u\n", | ||
266 | ssc_readx(params->ssc->regs, SSC_SR), | ||
267 | ssc_readx(params->ssc->regs, SSC_IER)); | ||
268 | break; /* SNDRV_PCM_TRIGGER_START */ | ||
269 | |||
270 | case SNDRV_PCM_TRIGGER_STOP: | ||
271 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
272 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
273 | ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, | ||
274 | params->mask->pdc_disable); | ||
275 | break; | ||
276 | |||
277 | case SNDRV_PCM_TRIGGER_RESUME: | ||
278 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
279 | ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, | ||
280 | params->mask->pdc_enable); | ||
281 | break; | ||
282 | |||
283 | default: | ||
284 | ret = -EINVAL; | ||
285 | } | ||
286 | 61 | ||
287 | return ret; | 62 | int atmel_pcm_mmap(struct snd_pcm_substream *substream, |
288 | } | ||
289 | |||
290 | static snd_pcm_uframes_t atmel_pcm_pointer( | ||
291 | struct snd_pcm_substream *substream) | ||
292 | { | ||
293 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
294 | struct atmel_runtime_data *prtd = runtime->private_data; | ||
295 | struct atmel_pcm_dma_params *params = prtd->params; | ||
296 | dma_addr_t ptr; | ||
297 | snd_pcm_uframes_t x; | ||
298 | |||
299 | ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr); | ||
300 | x = bytes_to_frames(runtime, ptr - prtd->dma_buffer); | ||
301 | |||
302 | if (x == runtime->buffer_size) | ||
303 | x = 0; | ||
304 | |||
305 | return x; | ||
306 | } | ||
307 | |||
308 | static int atmel_pcm_open(struct snd_pcm_substream *substream) | ||
309 | { | ||
310 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
311 | struct atmel_runtime_data *prtd; | ||
312 | int ret = 0; | ||
313 | |||
314 | snd_soc_set_runtime_hwparams(substream, &atmel_pcm_hardware); | ||
315 | |||
316 | /* ensure that buffer size is a multiple of period size */ | ||
317 | ret = snd_pcm_hw_constraint_integer(runtime, | ||
318 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
319 | if (ret < 0) | ||
320 | goto out; | ||
321 | |||
322 | prtd = kzalloc(sizeof(struct atmel_runtime_data), GFP_KERNEL); | ||
323 | if (prtd == NULL) { | ||
324 | ret = -ENOMEM; | ||
325 | goto out; | ||
326 | } | ||
327 | runtime->private_data = prtd; | ||
328 | |||
329 | out: | ||
330 | return ret; | ||
331 | } | ||
332 | |||
333 | static int atmel_pcm_close(struct snd_pcm_substream *substream) | ||
334 | { | ||
335 | struct atmel_runtime_data *prtd = substream->runtime->private_data; | ||
336 | |||
337 | kfree(prtd); | ||
338 | return 0; | ||
339 | } | ||
340 | |||
341 | static int atmel_pcm_mmap(struct snd_pcm_substream *substream, | ||
342 | struct vm_area_struct *vma) | 63 | struct vm_area_struct *vma) |
343 | { | 64 | { |
344 | return remap_pfn_range(vma, vma->vm_start, | 65 | return remap_pfn_range(vma, vma->vm_start, |
345 | substream->dma_buffer.addr >> PAGE_SHIFT, | 66 | substream->dma_buffer.addr >> PAGE_SHIFT, |
346 | vma->vm_end - vma->vm_start, vma->vm_page_prot); | 67 | vma->vm_end - vma->vm_start, vma->vm_page_prot); |
347 | } | 68 | } |
69 | EXPORT_SYMBOL_GPL(atmel_pcm_mmap); | ||
348 | 70 | ||
349 | static struct snd_pcm_ops atmel_pcm_ops = { | ||
350 | .open = atmel_pcm_open, | ||
351 | .close = atmel_pcm_close, | ||
352 | .ioctl = snd_pcm_lib_ioctl, | ||
353 | .hw_params = atmel_pcm_hw_params, | ||
354 | .hw_free = atmel_pcm_hw_free, | ||
355 | .prepare = atmel_pcm_prepare, | ||
356 | .trigger = atmel_pcm_trigger, | ||
357 | .pointer = atmel_pcm_pointer, | ||
358 | .mmap = atmel_pcm_mmap, | ||
359 | }; | ||
360 | |||
361 | |||
362 | /*--------------------------------------------------------------------------*\ | ||
363 | * ASoC platform driver | ||
364 | \*--------------------------------------------------------------------------*/ | ||
365 | static u64 atmel_pcm_dmamask = DMA_BIT_MASK(32); | 71 | static u64 atmel_pcm_dmamask = DMA_BIT_MASK(32); |
366 | 72 | ||
367 | static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd) | 73 | int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd) |
368 | { | 74 | { |
369 | struct snd_card *card = rtd->card->snd_card; | 75 | struct snd_card *card = rtd->card->snd_card; |
370 | struct snd_pcm *pcm = rtd->pcm; | 76 | struct snd_pcm *pcm = rtd->pcm; |
@@ -376,6 +82,7 @@ static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd) | |||
376 | card->dev->coherent_dma_mask = DMA_BIT_MASK(32); | 82 | card->dev->coherent_dma_mask = DMA_BIT_MASK(32); |
377 | 83 | ||
378 | if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { | 84 | if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { |
85 | pr_debug("atmel-pcm: allocating PCM playback DMA buffer\n"); | ||
379 | ret = atmel_pcm_preallocate_dma_buffer(pcm, | 86 | ret = atmel_pcm_preallocate_dma_buffer(pcm, |
380 | SNDRV_PCM_STREAM_PLAYBACK); | 87 | SNDRV_PCM_STREAM_PLAYBACK); |
381 | if (ret) | 88 | if (ret) |
@@ -383,8 +90,7 @@ static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd) | |||
383 | } | 90 | } |
384 | 91 | ||
385 | if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { | 92 | if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { |
386 | pr_debug("atmel-pcm:" | 93 | pr_debug("atmel-pcm: allocating PCM capture DMA buffer\n"); |
387 | "Allocating PCM capture DMA buffer\n"); | ||
388 | ret = atmel_pcm_preallocate_dma_buffer(pcm, | 94 | ret = atmel_pcm_preallocate_dma_buffer(pcm, |
389 | SNDRV_PCM_STREAM_CAPTURE); | 95 | SNDRV_PCM_STREAM_CAPTURE); |
390 | if (ret) | 96 | if (ret) |
@@ -393,8 +99,9 @@ static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd) | |||
393 | out: | 99 | out: |
394 | return ret; | 100 | return ret; |
395 | } | 101 | } |
102 | EXPORT_SYMBOL_GPL(atmel_pcm_new); | ||
396 | 103 | ||
397 | static void atmel_pcm_free_dma_buffers(struct snd_pcm *pcm) | 104 | void atmel_pcm_free(struct snd_pcm *pcm) |
398 | { | 105 | { |
399 | struct snd_pcm_substream *substream; | 106 | struct snd_pcm_substream *substream; |
400 | struct snd_dma_buffer *buf; | 107 | struct snd_dma_buffer *buf; |
@@ -413,89 +120,5 @@ static void atmel_pcm_free_dma_buffers(struct snd_pcm *pcm) | |||
413 | buf->area = NULL; | 120 | buf->area = NULL; |
414 | } | 121 | } |
415 | } | 122 | } |
123 | EXPORT_SYMBOL_GPL(atmel_pcm_free); | ||
416 | 124 | ||
417 | #ifdef CONFIG_PM | ||
418 | static int atmel_pcm_suspend(struct snd_soc_dai *dai) | ||
419 | { | ||
420 | struct snd_pcm_runtime *runtime = dai->runtime; | ||
421 | struct atmel_runtime_data *prtd; | ||
422 | struct atmel_pcm_dma_params *params; | ||
423 | |||
424 | if (!runtime) | ||
425 | return 0; | ||
426 | |||
427 | prtd = runtime->private_data; | ||
428 | params = prtd->params; | ||
429 | |||
430 | /* disable the PDC and save the PDC registers */ | ||
431 | |||
432 | ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_disable); | ||
433 | |||
434 | prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr); | ||
435 | prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr); | ||
436 | prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr); | ||
437 | prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr); | ||
438 | |||
439 | return 0; | ||
440 | } | ||
441 | |||
442 | static int atmel_pcm_resume(struct snd_soc_dai *dai) | ||
443 | { | ||
444 | struct snd_pcm_runtime *runtime = dai->runtime; | ||
445 | struct atmel_runtime_data *prtd; | ||
446 | struct atmel_pcm_dma_params *params; | ||
447 | |||
448 | if (!runtime) | ||
449 | return 0; | ||
450 | |||
451 | prtd = runtime->private_data; | ||
452 | params = prtd->params; | ||
453 | |||
454 | /* restore the PDC registers and enable the PDC */ | ||
455 | ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save); | ||
456 | ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save); | ||
457 | ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save); | ||
458 | ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save); | ||
459 | |||
460 | ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable); | ||
461 | return 0; | ||
462 | } | ||
463 | #else | ||
464 | #define atmel_pcm_suspend NULL | ||
465 | #define atmel_pcm_resume NULL | ||
466 | #endif | ||
467 | |||
468 | static struct snd_soc_platform_driver atmel_soc_platform = { | ||
469 | .ops = &atmel_pcm_ops, | ||
470 | .pcm_new = atmel_pcm_new, | ||
471 | .pcm_free = atmel_pcm_free_dma_buffers, | ||
472 | .suspend = atmel_pcm_suspend, | ||
473 | .resume = atmel_pcm_resume, | ||
474 | }; | ||
475 | |||
476 | static int __devinit atmel_soc_platform_probe(struct platform_device *pdev) | ||
477 | { | ||
478 | return snd_soc_register_platform(&pdev->dev, &atmel_soc_platform); | ||
479 | } | ||
480 | |||
481 | static int __devexit atmel_soc_platform_remove(struct platform_device *pdev) | ||
482 | { | ||
483 | snd_soc_unregister_platform(&pdev->dev); | ||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | static struct platform_driver atmel_pcm_driver = { | ||
488 | .driver = { | ||
489 | .name = "atmel-pcm-audio", | ||
490 | .owner = THIS_MODULE, | ||
491 | }, | ||
492 | |||
493 | .probe = atmel_soc_platform_probe, | ||
494 | .remove = __devexit_p(atmel_soc_platform_remove), | ||
495 | }; | ||
496 | |||
497 | module_platform_driver(atmel_pcm_driver); | ||
498 | |||
499 | MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>"); | ||
500 | MODULE_DESCRIPTION("Atmel PCM module"); | ||
501 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h index 5e0a95e64329..bb45d20e7250 100644 --- a/sound/soc/atmel/atmel-pcm.h +++ b/sound/soc/atmel/atmel-pcm.h | |||
@@ -36,6 +36,8 @@ | |||
36 | 36 | ||
37 | #include <linux/atmel-ssc.h> | 37 | #include <linux/atmel-ssc.h> |
38 | 38 | ||
39 | #define ATMEL_SSC_DMABUF_SIZE (64 * 1024) | ||
40 | |||
39 | /* | 41 | /* |
40 | * Registers and status bits that are required by the PCM driver. | 42 | * Registers and status bits that are required by the PCM driver. |
41 | */ | 43 | */ |
@@ -50,6 +52,7 @@ struct atmel_pdc_regs { | |||
50 | struct atmel_ssc_mask { | 52 | struct atmel_ssc_mask { |
51 | u32 ssc_enable; /* SSC recv/trans enable */ | 53 | u32 ssc_enable; /* SSC recv/trans enable */ |
52 | u32 ssc_disable; /* SSC recv/trans disable */ | 54 | u32 ssc_disable; /* SSC recv/trans disable */ |
55 | u32 ssc_error; /* SSC error conditions */ | ||
53 | u32 ssc_endx; /* SSC ENDTX or ENDRX */ | 56 | u32 ssc_endx; /* SSC ENDTX or ENDRX */ |
54 | u32 ssc_endbuf; /* SSC TXBUFE or RXBUFF */ | 57 | u32 ssc_endbuf; /* SSC TXBUFE or RXBUFF */ |
55 | u32 pdc_enable; /* PDC recv/trans enable */ | 58 | u32 pdc_enable; /* PDC recv/trans enable */ |
@@ -80,4 +83,35 @@ struct atmel_pcm_dma_params { | |||
80 | #define ssc_readx(base, reg) (__raw_readl((base) + (reg))) | 83 | #define ssc_readx(base, reg) (__raw_readl((base) + (reg))) |
81 | #define ssc_writex(base, reg, value) __raw_writel((value), (base) + (reg)) | 84 | #define ssc_writex(base, reg, value) __raw_writel((value), (base) + (reg)) |
82 | 85 | ||
86 | int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd); | ||
87 | void atmel_pcm_free(struct snd_pcm *pcm); | ||
88 | int atmel_pcm_mmap(struct snd_pcm_substream *substream, | ||
89 | struct vm_area_struct *vma); | ||
90 | |||
91 | #ifdef CONFIG_SND_ATMEL_SOC_PDC | ||
92 | int atmel_pcm_pdc_platform_register(struct device *dev); | ||
93 | void atmel_pcm_pdc_platform_unregister(struct device *dev); | ||
94 | #else | ||
95 | static inline int atmel_pcm_pdc_platform_register(struct device *dev) | ||
96 | { | ||
97 | return 0; | ||
98 | } | ||
99 | static inline void atmel_pcm_pdc_platform_unregister(struct device *dev) | ||
100 | { | ||
101 | } | ||
102 | #endif | ||
103 | |||
104 | #ifdef CONFIG_SND_ATMEL_SOC_DMA | ||
105 | int atmel_pcm_dma_platform_register(struct device *dev); | ||
106 | void atmel_pcm_dma_platform_unregister(struct device *dev); | ||
107 | #else | ||
108 | static inline int atmel_pcm_dma_platform_register(struct device *dev) | ||
109 | { | ||
110 | return 0; | ||
111 | } | ||
112 | static inline void atmel_pcm_dma_platform_unregister(struct device *dev) | ||
113 | { | ||
114 | } | ||
115 | #endif | ||
116 | |||
83 | #endif /* _ATMEL_PCM_H */ | 117 | #endif /* _ATMEL_PCM_H */ |
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 354341ec0f42..1c7663422054 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c | |||
@@ -48,11 +48,7 @@ | |||
48 | #include "atmel_ssc_dai.h" | 48 | #include "atmel_ssc_dai.h" |
49 | 49 | ||
50 | 50 | ||
51 | #if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9G20) | ||
52 | #define NUM_SSC_DEVICES 1 | ||
53 | #else | ||
54 | #define NUM_SSC_DEVICES 3 | 51 | #define NUM_SSC_DEVICES 3 |
55 | #endif | ||
56 | 52 | ||
57 | /* | 53 | /* |
58 | * SSC PDC registers required by the PCM DMA engine. | 54 | * SSC PDC registers required by the PCM DMA engine. |
@@ -107,7 +103,6 @@ static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = { | |||
107 | .pdc = &pdc_rx_reg, | 103 | .pdc = &pdc_rx_reg, |
108 | .mask = &ssc_rx_mask, | 104 | .mask = &ssc_rx_mask, |
109 | } }, | 105 | } }, |
110 | #if NUM_SSC_DEVICES == 3 | ||
111 | {{ | 106 | {{ |
112 | .name = "SSC1 PCM out", | 107 | .name = "SSC1 PCM out", |
113 | .pdc = &pdc_tx_reg, | 108 | .pdc = &pdc_tx_reg, |
@@ -128,7 +123,6 @@ static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = { | |||
128 | .pdc = &pdc_rx_reg, | 123 | .pdc = &pdc_rx_reg, |
129 | .mask = &ssc_rx_mask, | 124 | .mask = &ssc_rx_mask, |
130 | } }, | 125 | } }, |
131 | #endif | ||
132 | }; | 126 | }; |
133 | 127 | ||
134 | 128 | ||
@@ -139,7 +133,6 @@ static struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = { | |||
139 | .dir_mask = SSC_DIR_MASK_UNUSED, | 133 | .dir_mask = SSC_DIR_MASK_UNUSED, |
140 | .initialized = 0, | 134 | .initialized = 0, |
141 | }, | 135 | }, |
142 | #if NUM_SSC_DEVICES == 3 | ||
143 | { | 136 | { |
144 | .name = "ssc1", | 137 | .name = "ssc1", |
145 | .lock = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock), | 138 | .lock = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock), |
@@ -152,7 +145,6 @@ static struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = { | |||
152 | .dir_mask = SSC_DIR_MASK_UNUSED, | 145 | .dir_mask = SSC_DIR_MASK_UNUSED, |
153 | .initialized = 0, | 146 | .initialized = 0, |
154 | }, | 147 | }, |
155 | #endif | ||
156 | }; | 148 | }; |
157 | 149 | ||
158 | 150 | ||
@@ -690,27 +682,9 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai) | |||
690 | static int atmel_ssc_probe(struct snd_soc_dai *dai) | 682 | static int atmel_ssc_probe(struct snd_soc_dai *dai) |
691 | { | 683 | { |
692 | struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; | 684 | struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; |
693 | int ret = 0; | ||
694 | 685 | ||
695 | snd_soc_dai_set_drvdata(dai, ssc_p); | 686 | snd_soc_dai_set_drvdata(dai, ssc_p); |
696 | 687 | ||
697 | /* | ||
698 | * Request SSC device | ||
699 | */ | ||
700 | ssc_p->ssc = ssc_request(dai->id); | ||
701 | if (IS_ERR(ssc_p->ssc)) { | ||
702 | printk(KERN_ERR "ASoC: Failed to request SSC %d\n", dai->id); | ||
703 | ret = PTR_ERR(ssc_p->ssc); | ||
704 | } | ||
705 | |||
706 | return ret; | ||
707 | } | ||
708 | |||
709 | static int atmel_ssc_remove(struct snd_soc_dai *dai) | ||
710 | { | ||
711 | struct atmel_ssc_info *ssc_p = snd_soc_dai_get_drvdata(dai); | ||
712 | |||
713 | ssc_free(ssc_p->ssc); | ||
714 | return 0; | 688 | return 0; |
715 | } | 689 | } |
716 | 690 | ||
@@ -728,30 +702,8 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { | |||
728 | .set_clkdiv = atmel_ssc_set_dai_clkdiv, | 702 | .set_clkdiv = atmel_ssc_set_dai_clkdiv, |
729 | }; | 703 | }; |
730 | 704 | ||
731 | static struct snd_soc_dai_driver atmel_ssc_dai[NUM_SSC_DEVICES] = { | 705 | static struct snd_soc_dai_driver atmel_ssc_dai = { |
732 | { | ||
733 | .name = "atmel-ssc-dai.0", | ||
734 | .probe = atmel_ssc_probe, | ||
735 | .remove = atmel_ssc_remove, | ||
736 | .suspend = atmel_ssc_suspend, | ||
737 | .resume = atmel_ssc_resume, | ||
738 | .playback = { | ||
739 | .channels_min = 1, | ||
740 | .channels_max = 2, | ||
741 | .rates = ATMEL_SSC_RATES, | ||
742 | .formats = ATMEL_SSC_FORMATS,}, | ||
743 | .capture = { | ||
744 | .channels_min = 1, | ||
745 | .channels_max = 2, | ||
746 | .rates = ATMEL_SSC_RATES, | ||
747 | .formats = ATMEL_SSC_FORMATS,}, | ||
748 | .ops = &atmel_ssc_dai_ops, | ||
749 | }, | ||
750 | #if NUM_SSC_DEVICES == 3 | ||
751 | { | ||
752 | .name = "atmel-ssc-dai.1", | ||
753 | .probe = atmel_ssc_probe, | 706 | .probe = atmel_ssc_probe, |
754 | .remove = atmel_ssc_remove, | ||
755 | .suspend = atmel_ssc_suspend, | 707 | .suspend = atmel_ssc_suspend, |
756 | .resume = atmel_ssc_resume, | 708 | .resume = atmel_ssc_resume, |
757 | .playback = { | 709 | .playback = { |
@@ -765,50 +717,50 @@ static struct snd_soc_dai_driver atmel_ssc_dai[NUM_SSC_DEVICES] = { | |||
765 | .rates = ATMEL_SSC_RATES, | 717 | .rates = ATMEL_SSC_RATES, |
766 | .formats = ATMEL_SSC_FORMATS,}, | 718 | .formats = ATMEL_SSC_FORMATS,}, |
767 | .ops = &atmel_ssc_dai_ops, | 719 | .ops = &atmel_ssc_dai_ops, |
768 | }, | ||
769 | { | ||
770 | .name = "atmel-ssc-dai.2", | ||
771 | .probe = atmel_ssc_probe, | ||
772 | .remove = atmel_ssc_remove, | ||
773 | .suspend = atmel_ssc_suspend, | ||
774 | .resume = atmel_ssc_resume, | ||
775 | .playback = { | ||
776 | .channels_min = 1, | ||
777 | .channels_max = 2, | ||
778 | .rates = ATMEL_SSC_RATES, | ||
779 | .formats = ATMEL_SSC_FORMATS,}, | ||
780 | .capture = { | ||
781 | .channels_min = 1, | ||
782 | .channels_max = 2, | ||
783 | .rates = ATMEL_SSC_RATES, | ||
784 | .formats = ATMEL_SSC_FORMATS,}, | ||
785 | .ops = &atmel_ssc_dai_ops, | ||
786 | }, | ||
787 | #endif | ||
788 | }; | 720 | }; |
789 | 721 | ||
790 | static __devinit int asoc_ssc_probe(struct platform_device *pdev) | 722 | static int asoc_ssc_init(struct device *dev) |
791 | { | 723 | { |
792 | BUG_ON(pdev->id < 0); | 724 | struct platform_device *pdev = to_platform_device(dev); |
793 | BUG_ON(pdev->id >= ARRAY_SIZE(atmel_ssc_dai)); | 725 | struct ssc_device *ssc = platform_get_drvdata(pdev); |
794 | return snd_soc_register_dai(&pdev->dev, &atmel_ssc_dai[pdev->id]); | 726 | int ret; |
795 | } | 727 | |
728 | ret = snd_soc_register_dai(dev, &atmel_ssc_dai); | ||
729 | if (ret) { | ||
730 | dev_err(dev, "Could not register DAI: %d\n", ret); | ||
731 | goto err; | ||
732 | } | ||
733 | |||
734 | if (ssc->pdata->use_dma) | ||
735 | ret = atmel_pcm_dma_platform_register(dev); | ||
736 | else | ||
737 | ret = atmel_pcm_pdc_platform_register(dev); | ||
738 | |||
739 | if (ret) { | ||
740 | dev_err(dev, "Could not register PCM: %d\n", ret); | ||
741 | goto err_unregister_dai; | ||
742 | }; | ||
796 | 743 | ||
797 | static int __devexit asoc_ssc_remove(struct platform_device *pdev) | ||
798 | { | ||
799 | snd_soc_unregister_dai(&pdev->dev); | ||
800 | return 0; | 744 | return 0; |
745 | |||
746 | err_unregister_dai: | ||
747 | snd_soc_unregister_dai(dev); | ||
748 | err: | ||
749 | return ret; | ||
801 | } | 750 | } |
802 | 751 | ||
803 | static struct platform_driver asoc_ssc_driver = { | 752 | static void asoc_ssc_exit(struct device *dev) |
804 | .driver = { | 753 | { |
805 | .name = "atmel-ssc-dai", | 754 | struct platform_device *pdev = to_platform_device(dev); |
806 | .owner = THIS_MODULE, | 755 | struct ssc_device *ssc = platform_get_drvdata(pdev); |
807 | }, | ||
808 | 756 | ||
809 | .probe = asoc_ssc_probe, | 757 | if (ssc->pdata->use_dma) |
810 | .remove = __devexit_p(asoc_ssc_remove), | 758 | atmel_pcm_dma_platform_unregister(dev); |
811 | }; | 759 | else |
760 | atmel_pcm_pdc_platform_unregister(dev); | ||
761 | |||
762 | snd_soc_unregister_dai(dev); | ||
763 | } | ||
812 | 764 | ||
813 | /** | 765 | /** |
814 | * atmel_ssc_set_audio - Allocate the specified SSC for audio use. | 766 | * atmel_ssc_set_audio - Allocate the specified SSC for audio use. |
@@ -816,50 +768,32 @@ static struct platform_driver asoc_ssc_driver = { | |||
816 | int atmel_ssc_set_audio(int ssc_id) | 768 | int atmel_ssc_set_audio(int ssc_id) |
817 | { | 769 | { |
818 | struct ssc_device *ssc; | 770 | struct ssc_device *ssc; |
819 | static struct platform_device *dma_pdev; | ||
820 | struct platform_device *ssc_pdev; | ||
821 | int ret; | 771 | int ret; |
822 | 772 | ||
823 | if (ssc_id < 0 || ssc_id >= ARRAY_SIZE(atmel_ssc_dai)) | ||
824 | return -EINVAL; | ||
825 | |||
826 | /* Allocate a dummy device for DMA if we don't have one already */ | ||
827 | if (!dma_pdev) { | ||
828 | dma_pdev = platform_device_alloc("atmel-pcm-audio", -1); | ||
829 | if (!dma_pdev) | ||
830 | return -ENOMEM; | ||
831 | |||
832 | ret = platform_device_add(dma_pdev); | ||
833 | if (ret < 0) { | ||
834 | platform_device_put(dma_pdev); | ||
835 | dma_pdev = NULL; | ||
836 | return ret; | ||
837 | } | ||
838 | } | ||
839 | |||
840 | ssc_pdev = platform_device_alloc("atmel-ssc-dai", ssc_id); | ||
841 | if (!ssc_pdev) | ||
842 | return -ENOMEM; | ||
843 | |||
844 | /* If we can grab the SSC briefly to parent the DAI device off it */ | 773 | /* If we can grab the SSC briefly to parent the DAI device off it */ |
845 | ssc = ssc_request(ssc_id); | 774 | ssc = ssc_request(ssc_id); |
846 | if (IS_ERR(ssc)) | 775 | if (IS_ERR(ssc)) { |
847 | pr_warn("Unable to parent ASoC SSC DAI on SSC: %ld\n", | 776 | pr_err("Unable to parent ASoC SSC DAI on SSC: %ld\n", |
848 | PTR_ERR(ssc)); | 777 | PTR_ERR(ssc)); |
849 | else { | 778 | return PTR_ERR(ssc); |
850 | ssc_pdev->dev.parent = &(ssc->pdev->dev); | 779 | } else { |
851 | ssc_free(ssc); | 780 | ssc_info[ssc_id].ssc = ssc; |
852 | } | 781 | } |
853 | 782 | ||
854 | ret = platform_device_add(ssc_pdev); | 783 | ret = asoc_ssc_init(&ssc->pdev->dev); |
855 | if (ret < 0) | ||
856 | platform_device_put(ssc_pdev); | ||
857 | 784 | ||
858 | return ret; | 785 | return ret; |
859 | } | 786 | } |
860 | EXPORT_SYMBOL_GPL(atmel_ssc_set_audio); | 787 | EXPORT_SYMBOL_GPL(atmel_ssc_set_audio); |
861 | 788 | ||
862 | module_platform_driver(asoc_ssc_driver); | 789 | void atmel_ssc_put_audio(int ssc_id) |
790 | { | ||
791 | struct ssc_device *ssc = ssc_info[ssc_id].ssc; | ||
792 | |||
793 | ssc_free(ssc); | ||
794 | asoc_ssc_exit(&ssc->pdev->dev); | ||
795 | } | ||
796 | EXPORT_SYMBOL_GPL(atmel_ssc_put_audio); | ||
863 | 797 | ||
864 | /* Module information */ | 798 | /* Module information */ |
865 | MODULE_AUTHOR("Sedji Gaouaou, sedji.gaouaou@atmel.com, www.atmel.com"); | 799 | MODULE_AUTHOR("Sedji Gaouaou, sedji.gaouaou@atmel.com, www.atmel.com"); |
diff --git a/sound/soc/atmel/atmel_ssc_dai.h b/sound/soc/atmel/atmel_ssc_dai.h index 5d4f0f9b4d9a..b1f08d511495 100644 --- a/sound/soc/atmel/atmel_ssc_dai.h +++ b/sound/soc/atmel/atmel_ssc_dai.h | |||
@@ -117,6 +117,7 @@ struct atmel_ssc_info { | |||
117 | struct atmel_ssc_state ssc_state; | 117 | struct atmel_ssc_state ssc_state; |
118 | }; | 118 | }; |
119 | 119 | ||
120 | int atmel_ssc_set_audio(int ssc); | 120 | int atmel_ssc_set_audio(int ssc_id); |
121 | void atmel_ssc_put_audio(int ssc_id); | ||
121 | 122 | ||
122 | #endif /* _AT91_SSC_DAI_H */ | 123 | #endif /* _AT91_SSC_DAI_H */ |
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index c88351488f45..0744610e53dd 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c | |||
@@ -38,6 +38,8 @@ | |||
38 | #include <linux/platform_device.h> | 38 | #include <linux/platform_device.h> |
39 | #include <linux/i2c.h> | 39 | #include <linux/i2c.h> |
40 | 40 | ||
41 | #include <linux/pinctrl/consumer.h> | ||
42 | |||
41 | #include <linux/atmel-ssc.h> | 43 | #include <linux/atmel-ssc.h> |
42 | 44 | ||
43 | #include <sound/core.h> | 45 | #include <sound/core.h> |
@@ -179,10 +181,10 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd) | |||
179 | static struct snd_soc_dai_link at91sam9g20ek_dai = { | 181 | static struct snd_soc_dai_link at91sam9g20ek_dai = { |
180 | .name = "WM8731", | 182 | .name = "WM8731", |
181 | .stream_name = "WM8731 PCM", | 183 | .stream_name = "WM8731 PCM", |
182 | .cpu_dai_name = "atmel-ssc-dai.0", | 184 | .cpu_dai_name = "at91rm9200_ssc.0", |
183 | .codec_dai_name = "wm8731-hifi", | 185 | .codec_dai_name = "wm8731-hifi", |
184 | .init = at91sam9g20ek_wm8731_init, | 186 | .init = at91sam9g20ek_wm8731_init, |
185 | .platform_name = "atmel-pcm-audio", | 187 | .platform_name = "at91rm9200_ssc.0", |
186 | .codec_name = "wm8731.0-001b", | 188 | .codec_name = "wm8731.0-001b", |
187 | .ops = &at91sam9g20ek_ops, | 189 | .ops = &at91sam9g20ek_ops, |
188 | }; | 190 | }; |
@@ -195,20 +197,31 @@ static struct snd_soc_card snd_soc_at91sam9g20ek = { | |||
195 | .set_bias_level = at91sam9g20ek_set_bias_level, | 197 | .set_bias_level = at91sam9g20ek_set_bias_level, |
196 | }; | 198 | }; |
197 | 199 | ||
198 | static struct platform_device *at91sam9g20ek_snd_device; | 200 | static int __devinit at91sam9g20ek_audio_probe(struct platform_device *pdev) |
199 | |||
200 | static int __init at91sam9g20ek_init(void) | ||
201 | { | 201 | { |
202 | struct device_node *np = pdev->dev.of_node; | ||
203 | struct device_node *codec_np, *cpu_np; | ||
202 | struct clk *pllb; | 204 | struct clk *pllb; |
205 | struct snd_soc_card *card = &snd_soc_at91sam9g20ek; | ||
206 | struct pinctrl *pinctrl; | ||
203 | int ret; | 207 | int ret; |
204 | 208 | ||
205 | if (!(machine_is_at91sam9g20ek() || machine_is_at91sam9g20ek_2mmc())) | 209 | pinctrl = devm_pinctrl_get_select_default(&pdev->dev); |
206 | return -ENODEV; | 210 | if (IS_ERR(pinctrl)) { |
211 | dev_err(&pdev->dev, "Failed to request pinctrl for mck\n"); | ||
212 | return PTR_ERR(pinctrl); | ||
213 | } | ||
214 | |||
215 | if (!np) { | ||
216 | if (!(machine_is_at91sam9g20ek() || | ||
217 | machine_is_at91sam9g20ek_2mmc())) | ||
218 | return -ENODEV; | ||
219 | } | ||
207 | 220 | ||
208 | ret = atmel_ssc_set_audio(0); | 221 | ret = atmel_ssc_set_audio(0); |
209 | if (ret != 0) { | 222 | if (ret) { |
210 | pr_err("Failed to set SSC 0 for audio: %d\n", ret); | 223 | dev_err(&pdev->dev, "ssc channel is not valid\n"); |
211 | return ret; | 224 | return -EINVAL; |
212 | } | 225 | } |
213 | 226 | ||
214 | /* | 227 | /* |
@@ -236,45 +249,92 @@ static int __init at91sam9g20ek_init(void) | |||
236 | 249 | ||
237 | clk_set_rate(mclk, MCLK_RATE); | 250 | clk_set_rate(mclk, MCLK_RATE); |
238 | 251 | ||
239 | at91sam9g20ek_snd_device = platform_device_alloc("soc-audio", -1); | 252 | card->dev = &pdev->dev; |
240 | if (!at91sam9g20ek_snd_device) { | 253 | |
241 | printk(KERN_ERR "ASoC: Platform device allocation failed\n"); | 254 | /* Parse device node info */ |
242 | ret = -ENOMEM; | 255 | if (np) { |
243 | goto err_mclk; | 256 | ret = snd_soc_of_parse_card_name(card, "atmel,model"); |
257 | if (ret) | ||
258 | goto err; | ||
259 | |||
260 | ret = snd_soc_of_parse_audio_routing(card, | ||
261 | "atmel,audio-routing"); | ||
262 | if (ret) | ||
263 | goto err; | ||
264 | |||
265 | /* Parse codec info */ | ||
266 | at91sam9g20ek_dai.codec_name = NULL; | ||
267 | codec_np = of_parse_phandle(np, "atmel,audio-codec", 0); | ||
268 | if (!codec_np) { | ||
269 | dev_err(&pdev->dev, "codec info missing\n"); | ||
270 | return -EINVAL; | ||
271 | } | ||
272 | at91sam9g20ek_dai.codec_of_node = codec_np; | ||
273 | |||
274 | /* Parse dai and platform info */ | ||
275 | at91sam9g20ek_dai.cpu_dai_name = NULL; | ||
276 | at91sam9g20ek_dai.platform_name = NULL; | ||
277 | cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0); | ||
278 | if (!cpu_np) { | ||
279 | dev_err(&pdev->dev, "dai and pcm info missing\n"); | ||
280 | return -EINVAL; | ||
281 | } | ||
282 | at91sam9g20ek_dai.cpu_of_node = cpu_np; | ||
283 | at91sam9g20ek_dai.platform_of_node = cpu_np; | ||
284 | |||
285 | of_node_put(codec_np); | ||
286 | of_node_put(cpu_np); | ||
244 | } | 287 | } |
245 | 288 | ||
246 | platform_set_drvdata(at91sam9g20ek_snd_device, | 289 | ret = snd_soc_register_card(card); |
247 | &snd_soc_at91sam9g20ek); | ||
248 | |||
249 | ret = platform_device_add(at91sam9g20ek_snd_device); | ||
250 | if (ret) { | 290 | if (ret) { |
251 | printk(KERN_ERR "ASoC: Platform device allocation failed\n"); | 291 | printk(KERN_ERR "ASoC: snd_soc_register_card() failed\n"); |
252 | goto err_device_add; | ||
253 | } | 292 | } |
254 | 293 | ||
255 | return ret; | 294 | return ret; |
256 | 295 | ||
257 | err_device_add: | ||
258 | platform_device_put(at91sam9g20ek_snd_device); | ||
259 | err_mclk: | 296 | err_mclk: |
260 | clk_put(mclk); | 297 | clk_put(mclk); |
261 | mclk = NULL; | 298 | mclk = NULL; |
262 | err: | 299 | err: |
300 | atmel_ssc_put_audio(0); | ||
263 | return ret; | 301 | return ret; |
264 | } | 302 | } |
265 | 303 | ||
266 | static void __exit at91sam9g20ek_exit(void) | 304 | static int __devexit at91sam9g20ek_audio_remove(struct platform_device *pdev) |
267 | { | 305 | { |
268 | platform_device_unregister(at91sam9g20ek_snd_device); | 306 | struct snd_soc_card *card = platform_get_drvdata(pdev); |
269 | at91sam9g20ek_snd_device = NULL; | 307 | |
308 | atmel_ssc_put_audio(0); | ||
309 | snd_soc_unregister_card(card); | ||
270 | clk_put(mclk); | 310 | clk_put(mclk); |
271 | mclk = NULL; | 311 | mclk = NULL; |
312 | |||
313 | return 0; | ||
272 | } | 314 | } |
273 | 315 | ||
274 | module_init(at91sam9g20ek_init); | 316 | #ifdef CONFIG_OF |
275 | module_exit(at91sam9g20ek_exit); | 317 | static const struct of_device_id at91sam9g20ek_wm8731_dt_ids[] = { |
318 | { .compatible = "atmel,at91sam9g20ek-wm8731-audio", }, | ||
319 | { } | ||
320 | }; | ||
321 | MODULE_DEVICE_TABLE(of, at91sam9g20ek_wm8731_dt_ids); | ||
322 | #endif | ||
323 | |||
324 | static struct platform_driver at91sam9g20ek_audio_driver = { | ||
325 | .driver = { | ||
326 | .name = "at91sam9g20ek-audio", | ||
327 | .owner = THIS_MODULE, | ||
328 | .of_match_table = of_match_ptr(at91sam9g20ek_wm8731_dt_ids), | ||
329 | }, | ||
330 | .probe = at91sam9g20ek_audio_probe, | ||
331 | .remove = __devexit_p(at91sam9g20ek_audio_remove), | ||
332 | }; | ||
333 | |||
334 | module_platform_driver(at91sam9g20ek_audio_driver); | ||
276 | 335 | ||
277 | /* Module information */ | 336 | /* Module information */ |
278 | MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>"); | 337 | MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>"); |
279 | MODULE_DESCRIPTION("ALSA SoC AT91SAM9G20EK_WM8731"); | 338 | MODULE_DESCRIPTION("ALSA SoC AT91SAM9G20EK_WM8731"); |
339 | MODULE_ALIAS("platform:at91sam9g20ek-audio"); | ||
280 | MODULE_LICENSE("GPL"); | 340 | MODULE_LICENSE("GPL"); |