aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/atmel
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-12-13 14:51:23 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2012-12-13 14:51:23 -0500
commit046e7d685bc370fd4c879ab6635ad3f69e6673d1 (patch)
tree36b981f8d1f2bfd348c1479acbe3a9426d35c377 /sound/soc/atmel
parentfe504c5c745aeb767d978fbedeb94775fd4cb69c (diff)
parent6eb827d23577a4efec2b10a9c4cc9ded268a1d1c (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/Kconfig13
-rw-r--r--sound/soc/atmel/Makefile4
-rw-r--r--sound/soc/atmel/atmel-pcm-dma.c240
-rw-r--r--sound/soc/atmel/atmel-pcm-pdc.c401
-rw-r--r--sound/soc/atmel/atmel-pcm.c401
-rw-r--r--sound/soc/atmel/atmel-pcm.h34
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c168
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.h3
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c116
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
9config SND_ATMEL_SOC_PDC
10 tristate
11 depends on SND_ATMEL_SOC
12
13config SND_ATMEL_SOC_DMA
14 tristate
15 depends on SND_ATMEL_SOC
16
9config SND_ATMEL_SOC_SSC 17config 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
17config SND_AT91_SOC_SAM9G20_WM8731 25config 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
27config SND_AT91_SOC_AFEB9260 35config 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
2snd-soc-atmel-pcm-objs := atmel-pcm.o 2snd-soc-atmel-pcm-objs := atmel-pcm.o
3snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o
4snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o
3snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o 5snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o
4 6
5obj-$(CONFIG_SND_ATMEL_SOC) += snd-soc-atmel-pcm.o 7obj-$(CONFIG_SND_ATMEL_SOC) += snd-soc-atmel-pcm.o
8obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o
9obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.o
6obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o 10obj-$(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\*--------------------------------------------------------------------------*/
47static 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 */
67static 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\*--------------------------------------------------------------------------*/
94static 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
106static 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
145static 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;
178err:
179 snd_dmaengine_pcm_close(substream);
180 return ret;
181}
182
183static 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
195static 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
202static int atmel_pcm_close(struct snd_pcm_substream *substream)
203{
204 snd_dmaengine_pcm_close(substream);
205
206 return 0;
207}
208
209static 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
220static 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
226int atmel_pcm_dma_platform_register(struct device *dev)
227{
228 return snd_soc_register_platform(dev, &atmel_soc_platform);
229}
230EXPORT_SYMBOL(atmel_pcm_dma_platform_register);
231
232void atmel_pcm_dma_platform_unregister(struct device *dev)
233{
234 snd_soc_unregister_platform(dev);
235}
236EXPORT_SYMBOL(atmel_pcm_dma_platform_unregister);
237
238MODULE_AUTHOR("Bo Shen <voice.shen@atmel.com>");
239MODULE_DESCRIPTION("Atmel DMA based PCM module");
240MODULE_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 */
56static 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\*--------------------------------------------------------------------------*/
73struct 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\*--------------------------------------------------------------------------*/
91static 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\*--------------------------------------------------------------------------*/
140static 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
169static 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
183static 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
195static 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
261static 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
279static 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
304static 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
312static 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
329static 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
353static 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
379static 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
387int atmel_pcm_pdc_platform_register(struct device *dev)
388{
389 return snd_soc_register_platform(dev, &atmel_soc_platform);
390}
391EXPORT_SYMBOL(atmel_pcm_pdc_platform_register);
392
393void atmel_pcm_pdc_platform_unregister(struct device *dev)
394{
395 snd_soc_unregister_platform(dev);
396}
397EXPORT_SYMBOL(atmel_pcm_pdc_platform_unregister);
398
399MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
400MODULE_DESCRIPTION("Atmel PCM module");
401MODULE_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 */
56static 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\*--------------------------------------------------------------------------*/
73struct 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\*--------------------------------------------------------------------------*/
92static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, 40static 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\*--------------------------------------------------------------------------*/
119static 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\*--------------------------------------------------------------------------*/
169static 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
198static 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
212static 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
224static 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; 62int atmel_pcm_mmap(struct snd_pcm_substream *substream,
288}
289
290static 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
308static 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
333static 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
341static 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}
69EXPORT_SYMBOL_GPL(atmel_pcm_mmap);
348 70
349static 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\*--------------------------------------------------------------------------*/
365static u64 atmel_pcm_dmamask = DMA_BIT_MASK(32); 71static u64 atmel_pcm_dmamask = DMA_BIT_MASK(32);
366 72
367static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd) 73int 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}
102EXPORT_SYMBOL_GPL(atmel_pcm_new);
396 103
397static void atmel_pcm_free_dma_buffers(struct snd_pcm *pcm) 104void 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}
123EXPORT_SYMBOL_GPL(atmel_pcm_free);
416 124
417#ifdef CONFIG_PM
418static 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
442static 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
468static 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
476static int __devinit atmel_soc_platform_probe(struct platform_device *pdev)
477{
478 return snd_soc_register_platform(&pdev->dev, &atmel_soc_platform);
479}
480
481static int __devexit atmel_soc_platform_remove(struct platform_device *pdev)
482{
483 snd_soc_unregister_platform(&pdev->dev);
484 return 0;
485}
486
487static 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
497module_platform_driver(atmel_pcm_driver);
498
499MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
500MODULE_DESCRIPTION("Atmel PCM module");
501MODULE_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 {
50struct atmel_ssc_mask { 52struct 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
86int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd);
87void atmel_pcm_free(struct snd_pcm *pcm);
88int atmel_pcm_mmap(struct snd_pcm_substream *substream,
89 struct vm_area_struct *vma);
90
91#ifdef CONFIG_SND_ATMEL_SOC_PDC
92int atmel_pcm_pdc_platform_register(struct device *dev);
93void atmel_pcm_pdc_platform_unregister(struct device *dev);
94#else
95static inline int atmel_pcm_pdc_platform_register(struct device *dev)
96{
97 return 0;
98}
99static inline void atmel_pcm_pdc_platform_unregister(struct device *dev)
100{
101}
102#endif
103
104#ifdef CONFIG_SND_ATMEL_SOC_DMA
105int atmel_pcm_dma_platform_register(struct device *dev);
106void atmel_pcm_dma_platform_unregister(struct device *dev);
107#else
108static inline int atmel_pcm_dma_platform_register(struct device *dev)
109{
110 return 0;
111}
112static 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)
690static int atmel_ssc_probe(struct snd_soc_dai *dai) 682static 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
709static 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
731static struct snd_soc_dai_driver atmel_ssc_dai[NUM_SSC_DEVICES] = { 705static 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
790static __devinit int asoc_ssc_probe(struct platform_device *pdev) 722static 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
797static int __devexit asoc_ssc_remove(struct platform_device *pdev)
798{
799 snd_soc_unregister_dai(&pdev->dev);
800 return 0; 744 return 0;
745
746err_unregister_dai:
747 snd_soc_unregister_dai(dev);
748err:
749 return ret;
801} 750}
802 751
803static struct platform_driver asoc_ssc_driver = { 752static 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 = {
816int atmel_ssc_set_audio(int ssc_id) 768int 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}
860EXPORT_SYMBOL_GPL(atmel_ssc_set_audio); 787EXPORT_SYMBOL_GPL(atmel_ssc_set_audio);
861 788
862module_platform_driver(asoc_ssc_driver); 789void 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}
796EXPORT_SYMBOL_GPL(atmel_ssc_put_audio);
863 797
864/* Module information */ 798/* Module information */
865MODULE_AUTHOR("Sedji Gaouaou, sedji.gaouaou@atmel.com, www.atmel.com"); 799MODULE_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
120int atmel_ssc_set_audio(int ssc); 120int atmel_ssc_set_audio(int ssc_id);
121void 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)
179static struct snd_soc_dai_link at91sam9g20ek_dai = { 181static 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
198static struct platform_device *at91sam9g20ek_snd_device; 200static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
199
200static 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
257err_device_add:
258 platform_device_put(at91sam9g20ek_snd_device);
259err_mclk: 296err_mclk:
260 clk_put(mclk); 297 clk_put(mclk);
261 mclk = NULL; 298 mclk = NULL;
262err: 299err:
300 atmel_ssc_put_audio(0);
263 return ret; 301 return ret;
264} 302}
265 303
266static void __exit at91sam9g20ek_exit(void) 304static 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
274module_init(at91sam9g20ek_init); 316#ifdef CONFIG_OF
275module_exit(at91sam9g20ek_exit); 317static const struct of_device_id at91sam9g20ek_wm8731_dt_ids[] = {
318 { .compatible = "atmel,at91sam9g20ek-wm8731-audio", },
319 { }
320};
321MODULE_DEVICE_TABLE(of, at91sam9g20ek_wm8731_dt_ids);
322#endif
323
324static 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
334module_platform_driver(at91sam9g20ek_audio_driver);
276 335
277/* Module information */ 336/* Module information */
278MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>"); 337MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
279MODULE_DESCRIPTION("ALSA SoC AT91SAM9G20EK_WM8731"); 338MODULE_DESCRIPTION("ALSA SoC AT91SAM9G20EK_WM8731");
339MODULE_ALIAS("platform:at91sam9g20ek-audio");
280MODULE_LICENSE("GPL"); 340MODULE_LICENSE("GPL");