diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-13 14:51:23 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-13 14:51:23 -0500 |
commit | 046e7d685bc370fd4c879ab6635ad3f69e6673d1 (patch) | |
tree | 36b981f8d1f2bfd348c1479acbe3a9426d35c377 /sound/soc/atmel | |
parent | fe504c5c745aeb767d978fbedeb94775fd4cb69c (diff) | |
parent | 6eb827d23577a4efec2b10a9c4cc9ded268a1d1c (diff) |
Merge tag 'sound-3.8' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"This update contains a fairly wide range of changes all over in sound
subdirectory, mainly because of UAPI header moves by David and __dev*
annotation removals by Bill. Other highlights are:
- Introduced the support for wallclock timestamps in ALSA PCM core
- Add the poll loop implementation for HD-audio jack detection
- Yet more VGA-switcheroo fixes for HD-audio
- New VIA HD-audio codec support
- More fixes on resource management in USB audio and MIDI drivers
- More quirks for USB-audio ASUS Xonar U3, Reloop Play, Focusrite,
Roland VG-99, etc
- Add support for FastTrack C400 usb-audio
- Clean ups in many drivers regarding firmware loading
- Add PSC724 Ultiimate Edge support to ice1712
- A few hdspm driver updates
- New Stanton SCS.1d/1m FireWire driver
- Standardisation of the logging in ASoC codes
- DT and dmaengine support for ASoC Atmel
- Support for Wolfson ADSP cores
- New drivers for Freescale/iVeia P1022 and Maxim MAX98090
- Lots of other ASoC driver fixes and developments"
Fix up trivial conflicts. And go out on a limb and assume the dts file
'status' field of one of the conflicting things was supposed to be
"disabled", not "disable" like in pretty much all other cases.
* tag 'sound-3.8' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (341 commits)
ALSA: hda - Move runtime PM check to runtime_idle callback
ALSA: hda - Add stereo-dmic fixup for Acer Aspire One 522
ALSA: hda - Avoid doubly suspend after vga switcheroo
ALSA: usb-audio: Enable S/PDIF on the ASUS Xonar U3
ALSA: hda - Check validity of CORB/RIRB WP reads
ALSA: hda - use usleep_range in link reset and change timeout check
ALSA: HDA: VIA: Add support for codec VT1808.
ALSA: HDA: VIA Add support for codec VT1705CF.
ASoC: codecs: remove __dev* attributes
ASoC: utils: remove __dev* attributes
ASoC: ux500: remove __dev* attributes
ASoC: txx9: remove __dev* attributes
ASoC: tegra: remove __dev* attributes
ASoC: spear: remove __dev* attributes
ASoC: sh: remove __dev* attributes
ASoC: s6000: remove __dev* attributes
ASoC: OMAP: remove __dev* attributes
ASoC: nuc900: remove __dev* attributes
ASoC: mxs: remove __dev* attributes
ASoC: kirkwood: remove __dev* attributes
...
Diffstat (limited to 'sound/soc/atmel')
-rw-r--r-- | sound/soc/atmel/Kconfig | 13 | ||||
-rw-r--r-- | sound/soc/atmel/Makefile | 4 | ||||
-rw-r--r-- | sound/soc/atmel/atmel-pcm-dma.c | 240 | ||||
-rw-r--r-- | sound/soc/atmel/atmel-pcm-pdc.c | 401 | ||||
-rw-r--r-- | sound/soc/atmel/atmel-pcm.c | 401 | ||||
-rw-r--r-- | sound/soc/atmel/atmel-pcm.h | 34 | ||||
-rw-r--r-- | sound/soc/atmel/atmel_ssc_dai.c | 168 | ||||
-rw-r--r-- | sound/soc/atmel/atmel_ssc_dai.h | 3 | ||||
-rw-r--r-- | sound/soc/atmel/sam9g20_wm8731.c | 116 |
9 files changed, 843 insertions, 537 deletions
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..da976291da9e 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 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 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 = 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"); |