aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/ep93xx
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/ep93xx')
-rw-r--r--sound/soc/ep93xx/Kconfig41
-rw-r--r--sound/soc/ep93xx/Makefile17
-rw-r--r--sound/soc/ep93xx/edb93xx.c142
-rw-r--r--sound/soc/ep93xx/ep93xx-ac97.c467
-rw-r--r--sound/soc/ep93xx/ep93xx-i2s.c483
-rw-r--r--sound/soc/ep93xx/ep93xx-pcm.c357
-rw-r--r--sound/soc/ep93xx/ep93xx-pcm.h20
-rw-r--r--sound/soc/ep93xx/simone.c91
-rw-r--r--sound/soc/ep93xx/snappercl15.c146
9 files changed, 1764 insertions, 0 deletions
diff --git a/sound/soc/ep93xx/Kconfig b/sound/soc/ep93xx/Kconfig
new file mode 100644
index 00000000000..91a28de9410
--- /dev/null
+++ b/sound/soc/ep93xx/Kconfig
@@ -0,0 +1,41 @@
1config SND_EP93XX_SOC
2 tristate "SoC Audio support for the Cirrus Logic EP93xx series"
3 depends on ARCH_EP93XX && SND_SOC
4 help
5 Say Y or M if you want to add support for codecs attached to
6 the EP93xx I2S or AC97 interfaces.
7
8config SND_EP93XX_SOC_I2S
9 tristate
10
11config SND_EP93XX_SOC_AC97
12 tristate
13 select AC97_BUS
14 select SND_SOC_AC97_BUS
15
16config SND_EP93XX_SOC_SNAPPERCL15
17 tristate "SoC Audio support for Bluewater Systems Snapper CL15 module"
18 depends on SND_EP93XX_SOC && MACH_SNAPPER_CL15
19 select SND_EP93XX_SOC_I2S
20 select SND_SOC_TLV320AIC23
21 help
22 Say Y or M here if you want to add support for I2S audio on the
23 Bluewater Systems Snapper CL15 module.
24
25config SND_EP93XX_SOC_SIMONE
26 tristate "SoC Audio support for Simplemachines Sim.One board"
27 depends on SND_EP93XX_SOC && MACH_SIM_ONE
28 select SND_EP93XX_SOC_AC97
29 select SND_SOC_AC97_CODEC
30 help
31 Say Y or M here if you want to add support for AC97 audio on the
32 Simplemachines Sim.One board.
33
34config SND_EP93XX_SOC_EDB93XX
35 tristate "SoC Audio support for Cirrus Logic EDB93xx boards"
36 depends on SND_EP93XX_SOC && (MACH_EDB9301 || MACH_EDB9302 || MACH_EDB9302A || MACH_EDB9307A || MACH_EDB9315A)
37 select SND_EP93XX_SOC_I2S
38 select SND_SOC_CS4271
39 help
40 Say Y or M here if you want to add support for I2S audio on the
41 Cirrus Logic EDB93xx boards.
diff --git a/sound/soc/ep93xx/Makefile b/sound/soc/ep93xx/Makefile
new file mode 100644
index 00000000000..5514146cbdf
--- /dev/null
+++ b/sound/soc/ep93xx/Makefile
@@ -0,0 +1,17 @@
1# EP93xx Platform Support
2snd-soc-ep93xx-objs := ep93xx-pcm.o
3snd-soc-ep93xx-i2s-objs := ep93xx-i2s.o
4snd-soc-ep93xx-ac97-objs := ep93xx-ac97.o
5
6obj-$(CONFIG_SND_EP93XX_SOC) += snd-soc-ep93xx.o
7obj-$(CONFIG_SND_EP93XX_SOC_I2S) += snd-soc-ep93xx-i2s.o
8obj-$(CONFIG_SND_EP93XX_SOC_AC97) += snd-soc-ep93xx-ac97.o
9
10# EP93XX Machine Support
11snd-soc-snappercl15-objs := snappercl15.o
12snd-soc-simone-objs := simone.o
13snd-soc-edb93xx-objs := edb93xx.o
14
15obj-$(CONFIG_SND_EP93XX_SOC_SNAPPERCL15) += snd-soc-snappercl15.o
16obj-$(CONFIG_SND_EP93XX_SOC_SIMONE) += snd-soc-simone.o
17obj-$(CONFIG_SND_EP93XX_SOC_EDB93XX) += snd-soc-edb93xx.o
diff --git a/sound/soc/ep93xx/edb93xx.c b/sound/soc/ep93xx/edb93xx.c
new file mode 100644
index 00000000000..d3aa15119d2
--- /dev/null
+++ b/sound/soc/ep93xx/edb93xx.c
@@ -0,0 +1,142 @@
1/*
2 * SoC audio for EDB93xx
3 *
4 * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * This driver support CS4271 codec being master or slave, working
17 * in control port mode, connected either via SPI or I2C.
18 * The data format accepted is I2S or left-justified.
19 * DAPM support not implemented.
20 */
21
22#include <linux/platform_device.h>
23#include <linux/gpio.h>
24#include <sound/core.h>
25#include <sound/pcm.h>
26#include <sound/soc.h>
27#include <asm/mach-types.h>
28#include <mach/hardware.h>
29#include "ep93xx-pcm.h"
30
31#define edb93xx_has_audio() (machine_is_edb9301() || \
32 machine_is_edb9302() || \
33 machine_is_edb9302a() || \
34 machine_is_edb9307a() || \
35 machine_is_edb9315a())
36
37static int edb93xx_hw_params(struct snd_pcm_substream *substream,
38 struct snd_pcm_hw_params *params)
39{
40 struct snd_soc_pcm_runtime *rtd = substream->private_data;
41 struct snd_soc_dai *codec_dai = rtd->codec_dai;
42 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
43 int err;
44 unsigned int mclk_rate;
45 unsigned int rate = params_rate(params);
46
47 /*
48 * According to CS4271 datasheet we use MCLK/LRCK=256 for
49 * rates below 50kHz and 128 for higher sample rates
50 */
51 if (rate < 50000)
52 mclk_rate = rate * 64 * 4;
53 else
54 mclk_rate = rate * 64 * 2;
55
56 err = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
57 SND_SOC_DAIFMT_NB_IF |
58 SND_SOC_DAIFMT_CBS_CFS);
59 if (err)
60 return err;
61
62 err = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
63 SND_SOC_DAIFMT_NB_IF |
64 SND_SOC_DAIFMT_CBS_CFS);
65 if (err)
66 return err;
67
68 err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate,
69 SND_SOC_CLOCK_IN);
70 if (err)
71 return err;
72
73 return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate,
74 SND_SOC_CLOCK_OUT);
75}
76
77static struct snd_soc_ops edb93xx_ops = {
78 .hw_params = edb93xx_hw_params,
79};
80
81static struct snd_soc_dai_link edb93xx_dai = {
82 .name = "CS4271",
83 .stream_name = "CS4271 HiFi",
84 .platform_name = "ep93xx-pcm-audio",
85 .cpu_dai_name = "ep93xx-i2s",
86 .codec_name = "spi0.0",
87 .codec_dai_name = "cs4271-hifi",
88 .ops = &edb93xx_ops,
89};
90
91static struct snd_soc_card snd_soc_edb93xx = {
92 .name = "EDB93XX",
93 .dai_link = &edb93xx_dai,
94 .num_links = 1,
95};
96
97static struct platform_device *edb93xx_snd_device;
98
99static int __init edb93xx_init(void)
100{
101 int ret;
102
103 if (!edb93xx_has_audio())
104 return -ENODEV;
105
106 ret = ep93xx_i2s_acquire(EP93XX_SYSCON_DEVCFG_I2SONAC97,
107 EP93XX_SYSCON_I2SCLKDIV_ORIDE |
108 EP93XX_SYSCON_I2SCLKDIV_SPOL);
109 if (ret)
110 return ret;
111
112 edb93xx_snd_device = platform_device_alloc("soc-audio", -1);
113 if (!edb93xx_snd_device) {
114 ret = -ENOMEM;
115 goto free_i2s;
116 }
117
118 platform_set_drvdata(edb93xx_snd_device, &snd_soc_edb93xx);
119 ret = platform_device_add(edb93xx_snd_device);
120 if (ret)
121 goto device_put;
122
123 return 0;
124
125device_put:
126 platform_device_put(edb93xx_snd_device);
127free_i2s:
128 ep93xx_i2s_release();
129 return ret;
130}
131module_init(edb93xx_init);
132
133static void __exit edb93xx_exit(void)
134{
135 platform_device_unregister(edb93xx_snd_device);
136 ep93xx_i2s_release();
137}
138module_exit(edb93xx_exit);
139
140MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
141MODULE_DESCRIPTION("ALSA SoC EDB93xx");
142MODULE_LICENSE("GPL");
diff --git a/sound/soc/ep93xx/ep93xx-ac97.c b/sound/soc/ep93xx/ep93xx-ac97.c
new file mode 100644
index 00000000000..c7417c76552
--- /dev/null
+++ b/sound/soc/ep93xx/ep93xx-ac97.c
@@ -0,0 +1,467 @@
1/*
2 * ASoC driver for Cirrus Logic EP93xx AC97 controller.
3 *
4 * Copyright (c) 2010 Mika Westerberg
5 *
6 * Based on s3c-ac97 ASoC driver by Jaswinder Singh.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/delay.h>
14#include <linux/io.h>
15#include <linux/init.h>
16#include <linux/module.h>
17#include <linux/platform_device.h>
18#include <linux/slab.h>
19
20#include <sound/core.h>
21#include <sound/ac97_codec.h>
22#include <sound/soc.h>
23
24#include <mach/dma.h>
25#include "ep93xx-pcm.h"
26
27/*
28 * Per channel (1-4) registers.
29 */
30#define AC97CH(n) (((n) - 1) * 0x20)
31
32#define AC97DR(n) (AC97CH(n) + 0x0000)
33
34#define AC97RXCR(n) (AC97CH(n) + 0x0004)
35#define AC97RXCR_REN BIT(0)
36#define AC97RXCR_RX3 BIT(3)
37#define AC97RXCR_RX4 BIT(4)
38#define AC97RXCR_CM BIT(15)
39
40#define AC97TXCR(n) (AC97CH(n) + 0x0008)
41#define AC97TXCR_TEN BIT(0)
42#define AC97TXCR_TX3 BIT(3)
43#define AC97TXCR_TX4 BIT(4)
44#define AC97TXCR_CM BIT(15)
45
46#define AC97SR(n) (AC97CH(n) + 0x000c)
47#define AC97SR_TXFE BIT(1)
48#define AC97SR_TXUE BIT(6)
49
50#define AC97RISR(n) (AC97CH(n) + 0x0010)
51#define AC97ISR(n) (AC97CH(n) + 0x0014)
52#define AC97IE(n) (AC97CH(n) + 0x0018)
53
54/*
55 * Global AC97 controller registers.
56 */
57#define AC97S1DATA 0x0080
58#define AC97S2DATA 0x0084
59#define AC97S12DATA 0x0088
60
61#define AC97RGIS 0x008c
62#define AC97GIS 0x0090
63#define AC97IM 0x0094
64/*
65 * Common bits for RGIS, GIS and IM registers.
66 */
67#define AC97_SLOT2RXVALID BIT(1)
68#define AC97_CODECREADY BIT(5)
69#define AC97_SLOT2TXCOMPLETE BIT(6)
70
71#define AC97EOI 0x0098
72#define AC97EOI_WINT BIT(0)
73#define AC97EOI_CODECREADY BIT(1)
74
75#define AC97GCR 0x009c
76#define AC97GCR_AC97IFE BIT(0)
77
78#define AC97RESET 0x00a0
79#define AC97RESET_TIMEDRESET BIT(0)
80
81#define AC97SYNC 0x00a4
82#define AC97SYNC_TIMEDSYNC BIT(0)
83
84#define AC97_TIMEOUT msecs_to_jiffies(5)
85
86/**
87 * struct ep93xx_ac97_info - EP93xx AC97 controller info structure
88 * @lock: mutex serializing access to the bus (slot 1 & 2 ops)
89 * @dev: pointer to the platform device dev structure
90 * @mem: physical memory resource for the registers
91 * @regs: mapped AC97 controller registers
92 * @irq: AC97 interrupt number
93 * @done: bus ops wait here for an interrupt
94 */
95struct ep93xx_ac97_info {
96 struct mutex lock;
97 struct device *dev;
98 struct resource *mem;
99 void __iomem *regs;
100 int irq;
101 struct completion done;
102};
103
104/* currently ALSA only supports a single AC97 device */
105static struct ep93xx_ac97_info *ep93xx_ac97_info;
106
107static struct ep93xx_pcm_dma_params ep93xx_ac97_pcm_out = {
108 .name = "ac97-pcm-out",
109 .dma_port = EP93XX_DMA_AAC1,
110};
111
112static struct ep93xx_pcm_dma_params ep93xx_ac97_pcm_in = {
113 .name = "ac97-pcm-in",
114 .dma_port = EP93XX_DMA_AAC1,
115};
116
117static inline unsigned ep93xx_ac97_read_reg(struct ep93xx_ac97_info *info,
118 unsigned reg)
119{
120 return __raw_readl(info->regs + reg);
121}
122
123static inline void ep93xx_ac97_write_reg(struct ep93xx_ac97_info *info,
124 unsigned reg, unsigned val)
125{
126 __raw_writel(val, info->regs + reg);
127}
128
129static unsigned short ep93xx_ac97_read(struct snd_ac97 *ac97,
130 unsigned short reg)
131{
132 struct ep93xx_ac97_info *info = ep93xx_ac97_info;
133 unsigned short val;
134
135 mutex_lock(&info->lock);
136
137 ep93xx_ac97_write_reg(info, AC97S1DATA, reg);
138 ep93xx_ac97_write_reg(info, AC97IM, AC97_SLOT2RXVALID);
139 if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT)) {
140 dev_warn(info->dev, "timeout reading register %x\n", reg);
141 mutex_unlock(&info->lock);
142 return -ETIMEDOUT;
143 }
144 val = (unsigned short)ep93xx_ac97_read_reg(info, AC97S2DATA);
145
146 mutex_unlock(&info->lock);
147 return val;
148}
149
150static void ep93xx_ac97_write(struct snd_ac97 *ac97,
151 unsigned short reg,
152 unsigned short val)
153{
154 struct ep93xx_ac97_info *info = ep93xx_ac97_info;
155
156 mutex_lock(&info->lock);
157
158 /*
159 * Writes to the codec need to be done so that slot 2 is filled in
160 * before slot 1.
161 */
162 ep93xx_ac97_write_reg(info, AC97S2DATA, val);
163 ep93xx_ac97_write_reg(info, AC97S1DATA, reg);
164
165 ep93xx_ac97_write_reg(info, AC97IM, AC97_SLOT2TXCOMPLETE);
166 if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT))
167 dev_warn(info->dev, "timeout writing register %x\n", reg);
168
169 mutex_unlock(&info->lock);
170}
171
172static void ep93xx_ac97_warm_reset(struct snd_ac97 *ac97)
173{
174 struct ep93xx_ac97_info *info = ep93xx_ac97_info;
175
176 mutex_lock(&info->lock);
177
178 /*
179 * We are assuming that before this functions gets called, the codec
180 * BIT_CLK is stopped by forcing the codec into powerdown mode. We can
181 * control the SYNC signal directly via AC97SYNC register. Using
182 * TIMEDSYNC the controller will keep the SYNC high > 1us.
183 */
184 ep93xx_ac97_write_reg(info, AC97SYNC, AC97SYNC_TIMEDSYNC);
185 ep93xx_ac97_write_reg(info, AC97IM, AC97_CODECREADY);
186 if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT))
187 dev_warn(info->dev, "codec warm reset timeout\n");
188
189 mutex_unlock(&info->lock);
190}
191
192static void ep93xx_ac97_cold_reset(struct snd_ac97 *ac97)
193{
194 struct ep93xx_ac97_info *info = ep93xx_ac97_info;
195
196 mutex_lock(&info->lock);
197
198 /*
199 * For doing cold reset, we disable the AC97 controller interface, clear
200 * WINT and CODECREADY bits, and finally enable the interface again.
201 */
202 ep93xx_ac97_write_reg(info, AC97GCR, 0);
203 ep93xx_ac97_write_reg(info, AC97EOI, AC97EOI_CODECREADY | AC97EOI_WINT);
204 ep93xx_ac97_write_reg(info, AC97GCR, AC97GCR_AC97IFE);
205
206 /*
207 * Now, assert the reset and wait for the codec to become ready.
208 */
209 ep93xx_ac97_write_reg(info, AC97RESET, AC97RESET_TIMEDRESET);
210 ep93xx_ac97_write_reg(info, AC97IM, AC97_CODECREADY);
211 if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT))
212 dev_warn(info->dev, "codec cold reset timeout\n");
213
214 /*
215 * Give the codec some time to come fully out from the reset. This way
216 * we ensure that the subsequent reads/writes will work.
217 */
218 usleep_range(15000, 20000);
219
220 mutex_unlock(&info->lock);
221}
222
223static irqreturn_t ep93xx_ac97_interrupt(int irq, void *dev_id)
224{
225 struct ep93xx_ac97_info *info = dev_id;
226 unsigned status, mask;
227
228 /*
229 * Just mask out the interrupt and wake up the waiting thread.
230 * Interrupts are cleared via reading/writing to slot 1 & 2 registers by
231 * the waiting thread.
232 */
233 status = ep93xx_ac97_read_reg(info, AC97GIS);
234 mask = ep93xx_ac97_read_reg(info, AC97IM);
235 mask &= ~status;
236 ep93xx_ac97_write_reg(info, AC97IM, mask);
237
238 complete(&info->done);
239 return IRQ_HANDLED;
240}
241
242struct snd_ac97_bus_ops soc_ac97_ops = {
243 .read = ep93xx_ac97_read,
244 .write = ep93xx_ac97_write,
245 .reset = ep93xx_ac97_cold_reset,
246 .warm_reset = ep93xx_ac97_warm_reset,
247};
248EXPORT_SYMBOL_GPL(soc_ac97_ops);
249
250static int ep93xx_ac97_trigger(struct snd_pcm_substream *substream,
251 int cmd, struct snd_soc_dai *dai)
252{
253 struct ep93xx_ac97_info *info = snd_soc_dai_get_drvdata(dai);
254 unsigned v = 0;
255
256 switch (cmd) {
257 case SNDRV_PCM_TRIGGER_START:
258 case SNDRV_PCM_TRIGGER_RESUME:
259 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
260 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
261 /*
262 * Enable compact mode, TX slots 3 & 4, and the TX FIFO
263 * itself.
264 */
265 v |= AC97TXCR_CM;
266 v |= AC97TXCR_TX3 | AC97TXCR_TX4;
267 v |= AC97TXCR_TEN;
268 ep93xx_ac97_write_reg(info, AC97TXCR(1), v);
269 } else {
270 /*
271 * Enable compact mode, RX slots 3 & 4, and the RX FIFO
272 * itself.
273 */
274 v |= AC97RXCR_CM;
275 v |= AC97RXCR_RX3 | AC97RXCR_RX4;
276 v |= AC97RXCR_REN;
277 ep93xx_ac97_write_reg(info, AC97RXCR(1), v);
278 }
279 break;
280
281 case SNDRV_PCM_TRIGGER_STOP:
282 case SNDRV_PCM_TRIGGER_SUSPEND:
283 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
284 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
285 /*
286 * As per Cirrus EP93xx errata described below:
287 *
288 * http://www.cirrus.com/en/pubs/errata/ER667E2B.pdf
289 *
290 * we will wait for the TX FIFO to be empty before
291 * clearing the TEN bit.
292 */
293 unsigned long timeout = jiffies + AC97_TIMEOUT;
294
295 do {
296 v = ep93xx_ac97_read_reg(info, AC97SR(1));
297 if (time_after(jiffies, timeout)) {
298 dev_warn(info->dev, "TX timeout\n");
299 break;
300 }
301 } while (!(v & (AC97SR_TXFE | AC97SR_TXUE)));
302
303 /* disable the TX FIFO */
304 ep93xx_ac97_write_reg(info, AC97TXCR(1), 0);
305 } else {
306 /* disable the RX FIFO */
307 ep93xx_ac97_write_reg(info, AC97RXCR(1), 0);
308 }
309 break;
310
311 default:
312 dev_warn(info->dev, "unknown command %d\n", cmd);
313 return -EINVAL;
314 }
315
316 return 0;
317}
318
319static int ep93xx_ac97_startup(struct snd_pcm_substream *substream,
320 struct snd_soc_dai *dai)
321{
322 struct ep93xx_pcm_dma_params *dma_data;
323
324 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
325 dma_data = &ep93xx_ac97_pcm_out;
326 else
327 dma_data = &ep93xx_ac97_pcm_in;
328
329 snd_soc_dai_set_dma_data(dai, substream, dma_data);
330 return 0;
331}
332
333static struct snd_soc_dai_ops ep93xx_ac97_dai_ops = {
334 .startup = ep93xx_ac97_startup,
335 .trigger = ep93xx_ac97_trigger,
336};
337
338struct snd_soc_dai_driver ep93xx_ac97_dai = {
339 .name = "ep93xx-ac97",
340 .id = 0,
341 .ac97_control = 1,
342 .playback = {
343 .stream_name = "AC97 Playback",
344 .channels_min = 2,
345 .channels_max = 2,
346 .rates = SNDRV_PCM_RATE_8000_48000,
347 .formats = SNDRV_PCM_FMTBIT_S16_LE,
348 },
349 .capture = {
350 .stream_name = "AC97 Capture",
351 .channels_min = 2,
352 .channels_max = 2,
353 .rates = SNDRV_PCM_RATE_8000_48000,
354 .formats = SNDRV_PCM_FMTBIT_S16_LE,
355 },
356 .ops = &ep93xx_ac97_dai_ops,
357};
358
359static int __devinit ep93xx_ac97_probe(struct platform_device *pdev)
360{
361 struct ep93xx_ac97_info *info;
362 int ret;
363
364 info = kzalloc(sizeof(struct ep93xx_ac97_info), GFP_KERNEL);
365 if (!info)
366 return -ENOMEM;
367
368 dev_set_drvdata(&pdev->dev, info);
369
370 mutex_init(&info->lock);
371 init_completion(&info->done);
372 info->dev = &pdev->dev;
373
374 info->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
375 if (!info->mem) {
376 ret = -ENXIO;
377 goto fail_free_info;
378 }
379
380 info->irq = platform_get_irq(pdev, 0);
381 if (!info->irq) {
382 ret = -ENXIO;
383 goto fail_free_info;
384 }
385
386 if (!request_mem_region(info->mem->start, resource_size(info->mem),
387 pdev->name)) {
388 ret = -EBUSY;
389 goto fail_free_info;
390 }
391
392 info->regs = ioremap(info->mem->start, resource_size(info->mem));
393 if (!info->regs) {
394 ret = -ENOMEM;
395 goto fail_release_mem;
396 }
397
398 ret = request_irq(info->irq, ep93xx_ac97_interrupt, IRQF_TRIGGER_HIGH,
399 pdev->name, info);
400 if (ret)
401 goto fail_unmap_mem;
402
403 ep93xx_ac97_info = info;
404 platform_set_drvdata(pdev, info);
405
406 ret = snd_soc_register_dai(&pdev->dev, &ep93xx_ac97_dai);
407 if (ret)
408 goto fail_free_irq;
409
410 return 0;
411
412fail_free_irq:
413 platform_set_drvdata(pdev, NULL);
414 free_irq(info->irq, info);
415fail_unmap_mem:
416 iounmap(info->regs);
417fail_release_mem:
418 release_mem_region(info->mem->start, resource_size(info->mem));
419fail_free_info:
420 kfree(info);
421
422 return ret;
423}
424
425static int __devexit ep93xx_ac97_remove(struct platform_device *pdev)
426{
427 struct ep93xx_ac97_info *info = platform_get_drvdata(pdev);
428
429 snd_soc_unregister_dai(&pdev->dev);
430
431 /* disable the AC97 controller */
432 ep93xx_ac97_write_reg(info, AC97GCR, 0);
433
434 free_irq(info->irq, info);
435 iounmap(info->regs);
436 release_mem_region(info->mem->start, resource_size(info->mem));
437 platform_set_drvdata(pdev, NULL);
438 kfree(info);
439
440 return 0;
441}
442
443static struct platform_driver ep93xx_ac97_driver = {
444 .probe = ep93xx_ac97_probe,
445 .remove = __devexit_p(ep93xx_ac97_remove),
446 .driver = {
447 .name = "ep93xx-ac97",
448 .owner = THIS_MODULE,
449 },
450};
451
452static int __init ep93xx_ac97_init(void)
453{
454 return platform_driver_register(&ep93xx_ac97_driver);
455}
456module_init(ep93xx_ac97_init);
457
458static void __exit ep93xx_ac97_exit(void)
459{
460 platform_driver_unregister(&ep93xx_ac97_driver);
461}
462module_exit(ep93xx_ac97_exit);
463
464MODULE_DESCRIPTION("EP93xx AC97 ASoC Driver");
465MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
466MODULE_LICENSE("GPL");
467MODULE_ALIAS("platform:ep93xx-ac97");
diff --git a/sound/soc/ep93xx/ep93xx-i2s.c b/sound/soc/ep93xx/ep93xx-i2s.c
new file mode 100644
index 00000000000..099614e1665
--- /dev/null
+++ b/sound/soc/ep93xx/ep93xx-i2s.c
@@ -0,0 +1,483 @@
1/*
2 * linux/sound/soc/ep93xx-i2s.c
3 * EP93xx I2S driver
4 *
5 * Copyright (C) 2010 Ryan Mallon
6 *
7 * Based on the original driver by:
8 * Copyright (C) 2007 Chase Douglas <chasedouglas@gmail>
9 * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14 *
15 */
16
17#include <linux/module.h>
18#include <linux/init.h>
19#include <linux/slab.h>
20#include <linux/clk.h>
21#include <linux/io.h>
22
23#include <sound/core.h>
24#include <sound/pcm.h>
25#include <sound/pcm_params.h>
26#include <sound/initval.h>
27#include <sound/soc.h>
28
29#include <mach/hardware.h>
30#include <mach/ep93xx-regs.h>
31#include <mach/dma.h>
32
33#include "ep93xx-pcm.h"
34
35#define EP93XX_I2S_TXCLKCFG 0x00
36#define EP93XX_I2S_RXCLKCFG 0x04
37#define EP93XX_I2S_GLCTRL 0x0C
38
39#define EP93XX_I2S_TXLINCTRLDATA 0x28
40#define EP93XX_I2S_TXCTRL 0x2C
41#define EP93XX_I2S_TXWRDLEN 0x30
42#define EP93XX_I2S_TX0EN 0x34
43
44#define EP93XX_I2S_RXLINCTRLDATA 0x58
45#define EP93XX_I2S_RXCTRL 0x5C
46#define EP93XX_I2S_RXWRDLEN 0x60
47#define EP93XX_I2S_RX0EN 0x64
48
49#define EP93XX_I2S_WRDLEN_16 (0 << 0)
50#define EP93XX_I2S_WRDLEN_24 (1 << 0)
51#define EP93XX_I2S_WRDLEN_32 (2 << 0)
52
53#define EP93XX_I2S_LINCTRLDATA_R_JUST (1 << 2) /* Right justify */
54
55#define EP93XX_I2S_CLKCFG_LRS (1 << 0) /* lrclk polarity */
56#define EP93XX_I2S_CLKCFG_CKP (1 << 1) /* Bit clock polarity */
57#define EP93XX_I2S_CLKCFG_REL (1 << 2) /* First bit transition */
58#define EP93XX_I2S_CLKCFG_MASTER (1 << 3) /* Master mode */
59#define EP93XX_I2S_CLKCFG_NBCG (1 << 4) /* Not bit clock gating */
60
61struct ep93xx_i2s_info {
62 struct clk *mclk;
63 struct clk *sclk;
64 struct clk *lrclk;
65 struct ep93xx_pcm_dma_params *dma_params;
66 struct resource *mem;
67 void __iomem *regs;
68};
69
70struct ep93xx_pcm_dma_params ep93xx_i2s_dma_params[] = {
71 [SNDRV_PCM_STREAM_PLAYBACK] = {
72 .name = "i2s-pcm-out",
73 .dma_port = EP93XX_DMA_I2S1,
74 },
75 [SNDRV_PCM_STREAM_CAPTURE] = {
76 .name = "i2s-pcm-in",
77 .dma_port = EP93XX_DMA_I2S1,
78 },
79};
80
81static inline void ep93xx_i2s_write_reg(struct ep93xx_i2s_info *info,
82 unsigned reg, unsigned val)
83{
84 __raw_writel(val, info->regs + reg);
85}
86
87static inline unsigned ep93xx_i2s_read_reg(struct ep93xx_i2s_info *info,
88 unsigned reg)
89{
90 return __raw_readl(info->regs + reg);
91}
92
93static void ep93xx_i2s_enable(struct ep93xx_i2s_info *info, int stream)
94{
95 unsigned base_reg;
96 int i;
97
98 if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 &&
99 (ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) {
100 /* Enable clocks */
101 clk_enable(info->mclk);
102 clk_enable(info->sclk);
103 clk_enable(info->lrclk);
104
105 /* Enable i2s */
106 ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 1);
107 }
108
109 /* Enable fifos */
110 if (stream == SNDRV_PCM_STREAM_PLAYBACK)
111 base_reg = EP93XX_I2S_TX0EN;
112 else
113 base_reg = EP93XX_I2S_RX0EN;
114 for (i = 0; i < 3; i++)
115 ep93xx_i2s_write_reg(info, base_reg + (i * 4), 1);
116}
117
118static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream)
119{
120 unsigned base_reg;
121 int i;
122
123 /* Disable fifos */
124 if (stream == SNDRV_PCM_STREAM_PLAYBACK)
125 base_reg = EP93XX_I2S_TX0EN;
126 else
127 base_reg = EP93XX_I2S_RX0EN;
128 for (i = 0; i < 3; i++)
129 ep93xx_i2s_write_reg(info, base_reg + (i * 4), 0);
130
131 if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 &&
132 (ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) {
133 /* Disable i2s */
134 ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 0);
135
136 /* Disable clocks */
137 clk_disable(info->lrclk);
138 clk_disable(info->sclk);
139 clk_disable(info->mclk);
140 }
141}
142
143static int ep93xx_i2s_startup(struct snd_pcm_substream *substream,
144 struct snd_soc_dai *dai)
145{
146 struct snd_soc_pcm_runtime *rtd = substream->private_data;
147 struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
148 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
149
150 snd_soc_dai_set_dma_data(cpu_dai, substream,
151 &info->dma_params[substream->stream]);
152 return 0;
153}
154
155static void ep93xx_i2s_shutdown(struct snd_pcm_substream *substream,
156 struct snd_soc_dai *dai)
157{
158 struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
159
160 ep93xx_i2s_disable(info, substream->stream);
161}
162
163static int ep93xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
164 unsigned int fmt)
165{
166 struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(cpu_dai);
167 unsigned int clk_cfg, lin_ctrl;
168
169 clk_cfg = ep93xx_i2s_read_reg(info, EP93XX_I2S_RXCLKCFG);
170 lin_ctrl = ep93xx_i2s_read_reg(info, EP93XX_I2S_RXLINCTRLDATA);
171
172 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
173 case SND_SOC_DAIFMT_I2S:
174 clk_cfg |= EP93XX_I2S_CLKCFG_REL;
175 lin_ctrl &= ~EP93XX_I2S_LINCTRLDATA_R_JUST;
176 break;
177
178 case SND_SOC_DAIFMT_LEFT_J:
179 clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
180 lin_ctrl &= ~EP93XX_I2S_LINCTRLDATA_R_JUST;
181 break;
182
183 case SND_SOC_DAIFMT_RIGHT_J:
184 clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
185 lin_ctrl |= EP93XX_I2S_LINCTRLDATA_R_JUST;
186 break;
187
188 default:
189 return -EINVAL;
190 }
191
192 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
193 case SND_SOC_DAIFMT_CBS_CFS:
194 /* CPU is master */
195 clk_cfg |= EP93XX_I2S_CLKCFG_MASTER;
196 break;
197
198 case SND_SOC_DAIFMT_CBM_CFM:
199 /* Codec is master */
200 clk_cfg &= ~EP93XX_I2S_CLKCFG_MASTER;
201 break;
202
203 default:
204 return -EINVAL;
205 }
206
207 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
208 case SND_SOC_DAIFMT_NB_NF:
209 /* Negative bit clock, lrclk low on left word */
210 clk_cfg &= ~(EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_REL);
211 break;
212
213 case SND_SOC_DAIFMT_NB_IF:
214 /* Negative bit clock, lrclk low on right word */
215 clk_cfg &= ~EP93XX_I2S_CLKCFG_CKP;
216 clk_cfg |= EP93XX_I2S_CLKCFG_REL;
217 break;
218
219 case SND_SOC_DAIFMT_IB_NF:
220 /* Positive bit clock, lrclk low on left word */
221 clk_cfg |= EP93XX_I2S_CLKCFG_CKP;
222 clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
223 break;
224
225 case SND_SOC_DAIFMT_IB_IF:
226 /* Positive bit clock, lrclk low on right word */
227 clk_cfg |= EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_REL;
228 break;
229 }
230
231 /* Write new register values */
232 ep93xx_i2s_write_reg(info, EP93XX_I2S_RXCLKCFG, clk_cfg);
233 ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCLKCFG, clk_cfg);
234 ep93xx_i2s_write_reg(info, EP93XX_I2S_RXLINCTRLDATA, lin_ctrl);
235 ep93xx_i2s_write_reg(info, EP93XX_I2S_TXLINCTRLDATA, lin_ctrl);
236 return 0;
237}
238
239static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream,
240 struct snd_pcm_hw_params *params,
241 struct snd_soc_dai *dai)
242{
243 struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
244 unsigned word_len, div, sdiv, lrdiv;
245 int err;
246
247 switch (params_format(params)) {
248 case SNDRV_PCM_FORMAT_S16_LE:
249 word_len = EP93XX_I2S_WRDLEN_16;
250 break;
251
252 case SNDRV_PCM_FORMAT_S24_LE:
253 word_len = EP93XX_I2S_WRDLEN_24;
254 break;
255
256 case SNDRV_PCM_FORMAT_S32_LE:
257 word_len = EP93XX_I2S_WRDLEN_32;
258 break;
259
260 default:
261 return -EINVAL;
262 }
263
264 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
265 ep93xx_i2s_write_reg(info, EP93XX_I2S_TXWRDLEN, word_len);
266 else
267 ep93xx_i2s_write_reg(info, EP93XX_I2S_RXWRDLEN, word_len);
268
269 /*
270 * EP93xx I2S module can be setup so SCLK / LRCLK value can be
271 * 32, 64, 128. MCLK / SCLK value can be 2 and 4.
272 * We set LRCLK equal to `rate' and minimum SCLK / LRCLK
273 * value is 64, because our sample size is 32 bit * 2 channels.
274 * I2S standard permits us to transmit more bits than
275 * the codec uses.
276 */
277 div = clk_get_rate(info->mclk) / params_rate(params);
278 sdiv = 4;
279 if (div > (256 + 512) / 2) {
280 lrdiv = 128;
281 } else {
282 lrdiv = 64;
283 if (div < (128 + 256) / 2)
284 sdiv = 2;
285 }
286
287 err = clk_set_rate(info->sclk, clk_get_rate(info->mclk) / sdiv);
288 if (err)
289 return err;
290
291 err = clk_set_rate(info->lrclk, clk_get_rate(info->sclk) / lrdiv);
292 if (err)
293 return err;
294
295 ep93xx_i2s_enable(info, substream->stream);
296 return 0;
297}
298
299static int ep93xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
300 unsigned int freq, int dir)
301{
302 struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(cpu_dai);
303
304 if (dir == SND_SOC_CLOCK_IN || clk_id != 0)
305 return -EINVAL;
306
307 return clk_set_rate(info->mclk, freq);
308}
309
310#ifdef CONFIG_PM
311static int ep93xx_i2s_suspend(struct snd_soc_dai *dai)
312{
313 struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
314
315 if (!dai->active)
316 return 0;
317
318 ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_PLAYBACK);
319 ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_CAPTURE);
320
321 return 0;
322}
323
324static int ep93xx_i2s_resume(struct snd_soc_dai *dai)
325{
326 struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
327
328 if (!dai->active)
329 return 0;
330
331 ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK);
332 ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_CAPTURE);
333
334 return 0;
335}
336#else
337#define ep93xx_i2s_suspend NULL
338#define ep93xx_i2s_resume NULL
339#endif
340
341static struct snd_soc_dai_ops ep93xx_i2s_dai_ops = {
342 .startup = ep93xx_i2s_startup,
343 .shutdown = ep93xx_i2s_shutdown,
344 .hw_params = ep93xx_i2s_hw_params,
345 .set_sysclk = ep93xx_i2s_set_sysclk,
346 .set_fmt = ep93xx_i2s_set_dai_fmt,
347};
348
349#define EP93XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
350
351static struct snd_soc_dai_driver ep93xx_i2s_dai = {
352 .symmetric_rates= 1,
353 .suspend = ep93xx_i2s_suspend,
354 .resume = ep93xx_i2s_resume,
355 .playback = {
356 .channels_min = 2,
357 .channels_max = 2,
358 .rates = SNDRV_PCM_RATE_8000_192000,
359 .formats = EP93XX_I2S_FORMATS,
360 },
361 .capture = {
362 .channels_min = 2,
363 .channels_max = 2,
364 .rates = SNDRV_PCM_RATE_8000_192000,
365 .formats = EP93XX_I2S_FORMATS,
366 },
367 .ops = &ep93xx_i2s_dai_ops,
368};
369
370static int ep93xx_i2s_probe(struct platform_device *pdev)
371{
372 struct ep93xx_i2s_info *info;
373 struct resource *res;
374 int err;
375
376 info = kzalloc(sizeof(struct ep93xx_i2s_info), GFP_KERNEL);
377 if (!info) {
378 err = -ENOMEM;
379 goto fail;
380 }
381
382 dev_set_drvdata(&pdev->dev, info);
383 info->dma_params = ep93xx_i2s_dma_params;
384
385 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
386 if (!res) {
387 err = -ENODEV;
388 goto fail_free_info;
389 }
390
391 info->mem = request_mem_region(res->start, resource_size(res),
392 pdev->name);
393 if (!info->mem) {
394 err = -EBUSY;
395 goto fail_free_info;
396 }
397
398 info->regs = ioremap(info->mem->start, resource_size(info->mem));
399 if (!info->regs) {
400 err = -ENXIO;
401 goto fail_release_mem;
402 }
403
404 info->mclk = clk_get(&pdev->dev, "mclk");
405 if (IS_ERR(info->mclk)) {
406 err = PTR_ERR(info->mclk);
407 goto fail_unmap_mem;
408 }
409
410 info->sclk = clk_get(&pdev->dev, "sclk");
411 if (IS_ERR(info->sclk)) {
412 err = PTR_ERR(info->sclk);
413 goto fail_put_mclk;
414 }
415
416 info->lrclk = clk_get(&pdev->dev, "lrclk");
417 if (IS_ERR(info->lrclk)) {
418 err = PTR_ERR(info->lrclk);
419 goto fail_put_sclk;
420 }
421
422 err = snd_soc_register_dai(&pdev->dev, &ep93xx_i2s_dai);
423 if (err)
424 goto fail_put_lrclk;
425
426 return 0;
427
428fail_put_lrclk:
429 clk_put(info->lrclk);
430fail_put_sclk:
431 clk_put(info->sclk);
432fail_put_mclk:
433 clk_put(info->mclk);
434fail_unmap_mem:
435 iounmap(info->regs);
436fail_release_mem:
437 release_mem_region(info->mem->start, resource_size(info->mem));
438fail_free_info:
439 kfree(info);
440fail:
441 return err;
442}
443
444static int __devexit ep93xx_i2s_remove(struct platform_device *pdev)
445{
446 struct ep93xx_i2s_info *info = dev_get_drvdata(&pdev->dev);
447
448 snd_soc_unregister_dai(&pdev->dev);
449 clk_put(info->lrclk);
450 clk_put(info->sclk);
451 clk_put(info->mclk);
452 iounmap(info->regs);
453 release_mem_region(info->mem->start, resource_size(info->mem));
454 kfree(info);
455 return 0;
456}
457
458static struct platform_driver ep93xx_i2s_driver = {
459 .probe = ep93xx_i2s_probe,
460 .remove = __devexit_p(ep93xx_i2s_remove),
461 .driver = {
462 .name = "ep93xx-i2s",
463 .owner = THIS_MODULE,
464 },
465};
466
467static int __init ep93xx_i2s_init(void)
468{
469 return platform_driver_register(&ep93xx_i2s_driver);
470}
471
472static void __exit ep93xx_i2s_exit(void)
473{
474 platform_driver_unregister(&ep93xx_i2s_driver);
475}
476
477module_init(ep93xx_i2s_init);
478module_exit(ep93xx_i2s_exit);
479
480MODULE_ALIAS("platform:ep93xx-i2s");
481MODULE_AUTHOR("Ryan Mallon");
482MODULE_DESCRIPTION("EP93XX I2S driver");
483MODULE_LICENSE("GPL");
diff --git a/sound/soc/ep93xx/ep93xx-pcm.c b/sound/soc/ep93xx/ep93xx-pcm.c
new file mode 100644
index 00000000000..8dfd3ad84b1
--- /dev/null
+++ b/sound/soc/ep93xx/ep93xx-pcm.c
@@ -0,0 +1,357 @@
1/*
2 * linux/sound/arm/ep93xx-pcm.c - EP93xx ALSA PCM interface
3 *
4 * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
5 * Copyright (C) 2006 Applied Data Systems
6 *
7 * Rewritten for the SoC audio subsystem (Based on PXA2xx code):
8 * Copyright (c) 2008 Ryan Mallon
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 */
14
15#include <linux/module.h>
16#include <linux/init.h>
17#include <linux/device.h>
18#include <linux/slab.h>
19#include <linux/dmaengine.h>
20#include <linux/dma-mapping.h>
21
22#include <sound/core.h>
23#include <sound/pcm.h>
24#include <sound/pcm_params.h>
25#include <sound/soc.h>
26
27#include <mach/dma.h>
28#include <mach/hardware.h>
29#include <mach/ep93xx-regs.h>
30
31#include "ep93xx-pcm.h"
32
33static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
34 .info = (SNDRV_PCM_INFO_MMAP |
35 SNDRV_PCM_INFO_MMAP_VALID |
36 SNDRV_PCM_INFO_INTERLEAVED |
37 SNDRV_PCM_INFO_BLOCK_TRANSFER),
38
39 .rates = SNDRV_PCM_RATE_8000_192000,
40 .rate_min = SNDRV_PCM_RATE_8000,
41 .rate_max = SNDRV_PCM_RATE_192000,
42
43 .formats = (SNDRV_PCM_FMTBIT_S16_LE |
44 SNDRV_PCM_FMTBIT_S24_LE |
45 SNDRV_PCM_FMTBIT_S32_LE),
46
47 .buffer_bytes_max = 131072,
48 .period_bytes_min = 32,
49 .period_bytes_max = 32768,
50 .periods_min = 1,
51 .periods_max = 32,
52 .fifo_size = 32,
53};
54
55struct ep93xx_runtime_data
56{
57 int pointer_bytes;
58 int periods;
59 int period_bytes;
60 struct dma_chan *dma_chan;
61 struct ep93xx_dma_data dma_data;
62};
63
64static void ep93xx_pcm_dma_callback(void *data)
65{
66 struct snd_pcm_substream *substream = data;
67 struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
68
69 rtd->pointer_bytes += rtd->period_bytes;
70 rtd->pointer_bytes %= rtd->period_bytes * rtd->periods;
71
72 snd_pcm_period_elapsed(substream);
73}
74
75static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param)
76{
77 struct ep93xx_dma_data *data = filter_param;
78
79 if (data->direction == ep93xx_dma_chan_direction(chan)) {
80 chan->private = data;
81 return true;
82 }
83
84 return false;
85}
86
87static int ep93xx_pcm_open(struct snd_pcm_substream *substream)
88{
89 struct snd_soc_pcm_runtime *soc_rtd = substream->private_data;
90 struct snd_soc_dai *cpu_dai = soc_rtd->cpu_dai;
91 struct ep93xx_pcm_dma_params *dma_params;
92 struct ep93xx_runtime_data *rtd;
93 dma_cap_mask_t mask;
94 int ret;
95
96 ret = snd_pcm_hw_constraint_integer(substream->runtime,
97 SNDRV_PCM_HW_PARAM_PERIODS);
98 if (ret < 0)
99 return ret;
100
101 snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware);
102
103 rtd = kmalloc(sizeof(*rtd), GFP_KERNEL);
104 if (!rtd)
105 return -ENOMEM;
106
107 dma_cap_zero(mask);
108 dma_cap_set(DMA_SLAVE, mask);
109 dma_cap_set(DMA_CYCLIC, mask);
110
111 dma_params = snd_soc_dai_get_dma_data(cpu_dai, substream);
112 rtd->dma_data.port = dma_params->dma_port;
113 rtd->dma_data.name = dma_params->name;
114
115 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
116 rtd->dma_data.direction = DMA_TO_DEVICE;
117 else
118 rtd->dma_data.direction = DMA_FROM_DEVICE;
119
120 rtd->dma_chan = dma_request_channel(mask, ep93xx_pcm_dma_filter,
121 &rtd->dma_data);
122 if (!rtd->dma_chan) {
123 kfree(rtd);
124 return -EINVAL;
125 }
126
127 substream->runtime->private_data = rtd;
128 return 0;
129}
130
131static int ep93xx_pcm_close(struct snd_pcm_substream *substream)
132{
133 struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
134
135 dma_release_channel(rtd->dma_chan);
136 kfree(rtd);
137 return 0;
138}
139
140static int ep93xx_pcm_dma_submit(struct snd_pcm_substream *substream)
141{
142 struct snd_pcm_runtime *runtime = substream->runtime;
143 struct ep93xx_runtime_data *rtd = runtime->private_data;
144 struct dma_chan *chan = rtd->dma_chan;
145 struct dma_device *dma_dev = chan->device;
146 struct dma_async_tx_descriptor *desc;
147
148 rtd->pointer_bytes = 0;
149 desc = dma_dev->device_prep_dma_cyclic(chan, runtime->dma_addr,
150 rtd->period_bytes * rtd->periods,
151 rtd->period_bytes,
152 rtd->dma_data.direction);
153 if (!desc)
154 return -EINVAL;
155
156 desc->callback = ep93xx_pcm_dma_callback;
157 desc->callback_param = substream;
158
159 dmaengine_submit(desc);
160 return 0;
161}
162
163static void ep93xx_pcm_dma_flush(struct snd_pcm_substream *substream)
164{
165 struct snd_pcm_runtime *runtime = substream->runtime;
166 struct ep93xx_runtime_data *rtd = runtime->private_data;
167
168 dmaengine_terminate_all(rtd->dma_chan);
169}
170
171static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream,
172 struct snd_pcm_hw_params *params)
173{
174 struct snd_pcm_runtime *runtime = substream->runtime;
175 struct ep93xx_runtime_data *rtd = runtime->private_data;
176
177 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
178
179 rtd->periods = params_periods(params);
180 rtd->period_bytes = params_period_bytes(params);
181 return 0;
182}
183
184static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream)
185{
186 snd_pcm_set_runtime_buffer(substream, NULL);
187 return 0;
188}
189
190static int ep93xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
191{
192 int ret;
193
194 ret = 0;
195 switch (cmd) {
196 case SNDRV_PCM_TRIGGER_START:
197 case SNDRV_PCM_TRIGGER_RESUME:
198 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
199 ret = ep93xx_pcm_dma_submit(substream);
200 break;
201
202 case SNDRV_PCM_TRIGGER_STOP:
203 case SNDRV_PCM_TRIGGER_SUSPEND:
204 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
205 ep93xx_pcm_dma_flush(substream);
206 break;
207
208 default:
209 ret = -EINVAL;
210 break;
211 }
212
213 return ret;
214}
215
216static snd_pcm_uframes_t ep93xx_pcm_pointer(struct snd_pcm_substream *substream)
217{
218 struct snd_pcm_runtime *runtime = substream->runtime;
219 struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
220
221 /* FIXME: implement this with sub-period granularity */
222 return bytes_to_frames(runtime, rtd->pointer_bytes);
223}
224
225static int ep93xx_pcm_mmap(struct snd_pcm_substream *substream,
226 struct vm_area_struct *vma)
227{
228 struct snd_pcm_runtime *runtime = substream->runtime;
229
230 return dma_mmap_writecombine(substream->pcm->card->dev, vma,
231 runtime->dma_area,
232 runtime->dma_addr,
233 runtime->dma_bytes);
234}
235
236static struct snd_pcm_ops ep93xx_pcm_ops = {
237 .open = ep93xx_pcm_open,
238 .close = ep93xx_pcm_close,
239 .ioctl = snd_pcm_lib_ioctl,
240 .hw_params = ep93xx_pcm_hw_params,
241 .hw_free = ep93xx_pcm_hw_free,
242 .trigger = ep93xx_pcm_trigger,
243 .pointer = ep93xx_pcm_pointer,
244 .mmap = ep93xx_pcm_mmap,
245};
246
247static int ep93xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
248{
249 struct snd_pcm_substream *substream = pcm->streams[stream].substream;
250 struct snd_dma_buffer *buf = &substream->dma_buffer;
251 size_t size = ep93xx_pcm_hardware.buffer_bytes_max;
252
253 buf->dev.type = SNDRV_DMA_TYPE_DEV;
254 buf->dev.dev = pcm->card->dev;
255 buf->private_data = NULL;
256 buf->area = dma_alloc_writecombine(pcm->card->dev, size,
257 &buf->addr, GFP_KERNEL);
258 buf->bytes = size;
259
260 return (buf->area == NULL) ? -ENOMEM : 0;
261}
262
263static void ep93xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
264{
265 struct snd_pcm_substream *substream;
266 struct snd_dma_buffer *buf;
267 int stream;
268
269 for (stream = 0; stream < 2; stream++) {
270 substream = pcm->streams[stream].substream;
271 if (!substream)
272 continue;
273
274 buf = &substream->dma_buffer;
275 if (!buf->area)
276 continue;
277
278 dma_free_writecombine(pcm->card->dev, buf->bytes, buf->area,
279 buf->addr);
280 buf->area = NULL;
281 }
282}
283
284static u64 ep93xx_pcm_dmamask = 0xffffffff;
285
286static int ep93xx_pcm_new(struct snd_soc_pcm_runtime *rtd)
287{
288 struct snd_card *card = rtd->card->snd_card;
289 struct snd_soc_dai *dai = rtd->cpu_dai;
290 struct snd_pcm *pcm = rtd->pcm;
291 int ret = 0;
292
293 if (!card->dev->dma_mask)
294 card->dev->dma_mask = &ep93xx_pcm_dmamask;
295 if (!card->dev->coherent_dma_mask)
296 card->dev->coherent_dma_mask = 0xffffffff;
297
298 if (dai->driver->playback.channels_min) {
299 ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
300 SNDRV_PCM_STREAM_PLAYBACK);
301 if (ret)
302 return ret;
303 }
304
305 if (dai->driver->capture.channels_min) {
306 ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
307 SNDRV_PCM_STREAM_CAPTURE);
308 if (ret)
309 return ret;
310 }
311
312 return 0;
313}
314
315static struct snd_soc_platform_driver ep93xx_soc_platform = {
316 .ops = &ep93xx_pcm_ops,
317 .pcm_new = &ep93xx_pcm_new,
318 .pcm_free = &ep93xx_pcm_free_dma_buffers,
319};
320
321static int __devinit ep93xx_soc_platform_probe(struct platform_device *pdev)
322{
323 return snd_soc_register_platform(&pdev->dev, &ep93xx_soc_platform);
324}
325
326static int __devexit ep93xx_soc_platform_remove(struct platform_device *pdev)
327{
328 snd_soc_unregister_platform(&pdev->dev);
329 return 0;
330}
331
332static struct platform_driver ep93xx_pcm_driver = {
333 .driver = {
334 .name = "ep93xx-pcm-audio",
335 .owner = THIS_MODULE,
336 },
337
338 .probe = ep93xx_soc_platform_probe,
339 .remove = __devexit_p(ep93xx_soc_platform_remove),
340};
341
342static int __init ep93xx_soc_platform_init(void)
343{
344 return platform_driver_register(&ep93xx_pcm_driver);
345}
346
347static void __exit ep93xx_soc_platform_exit(void)
348{
349 platform_driver_unregister(&ep93xx_pcm_driver);
350}
351
352module_init(ep93xx_soc_platform_init);
353module_exit(ep93xx_soc_platform_exit);
354
355MODULE_AUTHOR("Ryan Mallon");
356MODULE_DESCRIPTION("EP93xx ALSA PCM interface");
357MODULE_LICENSE("GPL");
diff --git a/sound/soc/ep93xx/ep93xx-pcm.h b/sound/soc/ep93xx/ep93xx-pcm.h
new file mode 100644
index 00000000000..111e1121ecb
--- /dev/null
+++ b/sound/soc/ep93xx/ep93xx-pcm.h
@@ -0,0 +1,20 @@
1/*
2 * sound/soc/ep93xx/ep93xx-pcm.h - EP93xx ALSA PCM interface
3 *
4 * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
5 * Copyright (C) 2006 Applied Data Systems
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#ifndef _EP93XX_SND_SOC_PCM_H
13#define _EP93XX_SND_SOC_PCM_H
14
15struct ep93xx_pcm_dma_params {
16 char *name;
17 int dma_port;
18};
19
20#endif /* _EP93XX_SND_SOC_PCM_H */
diff --git a/sound/soc/ep93xx/simone.c b/sound/soc/ep93xx/simone.c
new file mode 100644
index 00000000000..286817946c5
--- /dev/null
+++ b/sound/soc/ep93xx/simone.c
@@ -0,0 +1,91 @@
1/*
2 * simone.c -- ASoC audio for Simplemachines Sim.One board
3 *
4 * Copyright (c) 2010 Mika Westerberg
5 *
6 * Based on snappercl15 machine driver by Ryan Mallon.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/init.h>
14#include <linux/module.h>
15#include <linux/platform_device.h>
16
17#include <sound/core.h>
18#include <sound/pcm.h>
19#include <sound/soc.h>
20
21#include <asm/mach-types.h>
22#include <mach/hardware.h>
23
24#include "ep93xx-pcm.h"
25
26static struct snd_soc_dai_link simone_dai = {
27 .name = "AC97",
28 .stream_name = "AC97 HiFi",
29 .cpu_dai_name = "ep93xx-ac97",
30 .codec_dai_name = "ac97-hifi",
31 .codec_name = "ac97-codec",
32 .platform_name = "ep93xx-pcm-audio",
33};
34
35static struct snd_soc_card snd_soc_simone = {
36 .name = "Sim.One",
37 .dai_link = &simone_dai,
38 .num_links = 1,
39};
40
41static struct platform_device *simone_snd_ac97_device;
42static struct platform_device *simone_snd_device;
43
44static int __init simone_init(void)
45{
46 int ret;
47
48 if (!machine_is_sim_one())
49 return -ENODEV;
50
51 simone_snd_ac97_device = platform_device_alloc("ac97-codec", -1);
52 if (!simone_snd_ac97_device)
53 return -ENOMEM;
54
55 ret = platform_device_add(simone_snd_ac97_device);
56 if (ret)
57 goto fail1;
58
59 simone_snd_device = platform_device_alloc("soc-audio", -1);
60 if (!simone_snd_device) {
61 ret = -ENOMEM;
62 goto fail2;
63 }
64
65 platform_set_drvdata(simone_snd_device, &snd_soc_simone);
66 ret = platform_device_add(simone_snd_device);
67 if (ret)
68 goto fail3;
69
70 return 0;
71
72fail3:
73 platform_device_put(simone_snd_device);
74fail2:
75 platform_device_del(simone_snd_ac97_device);
76fail1:
77 platform_device_put(simone_snd_ac97_device);
78 return ret;
79}
80module_init(simone_init);
81
82static void __exit simone_exit(void)
83{
84 platform_device_unregister(simone_snd_device);
85 platform_device_unregister(simone_snd_ac97_device);
86}
87module_exit(simone_exit);
88
89MODULE_DESCRIPTION("ALSA SoC Simplemachines Sim.One");
90MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
91MODULE_LICENSE("GPL");
diff --git a/sound/soc/ep93xx/snappercl15.c b/sound/soc/ep93xx/snappercl15.c
new file mode 100644
index 00000000000..c8aa8a5003c
--- /dev/null
+++ b/sound/soc/ep93xx/snappercl15.c
@@ -0,0 +1,146 @@
1/*
2 * snappercl15.c -- SoC audio for Bluewater Systems Snapper CL15 module
3 *
4 * Copyright (C) 2008 Bluewater Systems Ltd
5 * Author: Ryan Mallon
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version.
11 *
12 */
13
14#include <linux/platform_device.h>
15#include <sound/core.h>
16#include <sound/pcm.h>
17#include <sound/soc.h>
18
19#include <asm/mach-types.h>
20#include <mach/hardware.h>
21
22#include "../codecs/tlv320aic23.h"
23#include "ep93xx-pcm.h"
24
25#define CODEC_CLOCK 5644800
26
27static int snappercl15_hw_params(struct snd_pcm_substream *substream,
28 struct snd_pcm_hw_params *params)
29{
30 struct snd_soc_pcm_runtime *rtd = substream->private_data;
31 struct snd_soc_dai *codec_dai = rtd->codec_dai;
32 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
33 int err;
34
35 err = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
36 SND_SOC_DAIFMT_NB_IF |
37 SND_SOC_DAIFMT_CBS_CFS);
38
39 err = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
40 SND_SOC_DAIFMT_NB_IF |
41 SND_SOC_DAIFMT_CBS_CFS);
42 if (err)
43 return err;
44
45 err = snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK,
46 SND_SOC_CLOCK_IN);
47 if (err)
48 return err;
49
50 err = snd_soc_dai_set_sysclk(cpu_dai, 0, CODEC_CLOCK,
51 SND_SOC_CLOCK_OUT);
52 if (err)
53 return err;
54
55 return 0;
56}
57
58static struct snd_soc_ops snappercl15_ops = {
59 .hw_params = snappercl15_hw_params,
60};
61
62static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
63 SND_SOC_DAPM_HP("Headphone Jack", NULL),
64 SND_SOC_DAPM_LINE("Line In", NULL),
65 SND_SOC_DAPM_MIC("Mic Jack", NULL),
66};
67
68static const struct snd_soc_dapm_route audio_map[] = {
69 {"Headphone Jack", NULL, "LHPOUT"},
70 {"Headphone Jack", NULL, "RHPOUT"},
71
72 {"LLINEIN", NULL, "Line In"},
73 {"RLINEIN", NULL, "Line In"},
74
75 {"MICIN", NULL, "Mic Jack"},
76};
77
78static int snappercl15_tlv320aic23_init(struct snd_soc_pcm_runtime *rtd)
79{
80 struct snd_soc_codec *codec = rtd->codec;
81 struct snd_soc_dapm_context *dapm = &codec->dapm;
82
83 snd_soc_dapm_new_controls(dapm, tlv320aic23_dapm_widgets,
84 ARRAY_SIZE(tlv320aic23_dapm_widgets));
85
86 snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
87 return 0;
88}
89
90static struct snd_soc_dai_link snappercl15_dai = {
91 .name = "tlv320aic23",
92 .stream_name = "AIC23",
93 .cpu_dai_name = "ep93xx-i2s",
94 .codec_dai_name = "tlv320aic23-hifi",
95 .codec_name = "tlv320aic23-codec.0-001a",
96 .platform_name = "ep93xx-pcm-audio",
97 .init = snappercl15_tlv320aic23_init,
98 .ops = &snappercl15_ops,
99};
100
101static struct snd_soc_card snd_soc_snappercl15 = {
102 .name = "Snapper CL15",
103 .dai_link = &snappercl15_dai,
104 .num_links = 1,
105};
106
107static struct platform_device *snappercl15_snd_device;
108
109static int __init snappercl15_init(void)
110{
111 int ret;
112
113 if (!machine_is_snapper_cl15())
114 return -ENODEV;
115
116 ret = ep93xx_i2s_acquire(EP93XX_SYSCON_DEVCFG_I2SONAC97,
117 EP93XX_SYSCON_I2SCLKDIV_ORIDE |
118 EP93XX_SYSCON_I2SCLKDIV_SPOL);
119 if (ret)
120 return ret;
121
122 snappercl15_snd_device = platform_device_alloc("soc-audio", -1);
123 if (!snappercl15_snd_device)
124 return -ENOMEM;
125
126 platform_set_drvdata(snappercl15_snd_device, &snd_soc_snappercl15);
127 ret = platform_device_add(snappercl15_snd_device);
128 if (ret)
129 platform_device_put(snappercl15_snd_device);
130
131 return ret;
132}
133
134static void __exit snappercl15_exit(void)
135{
136 platform_device_unregister(snappercl15_snd_device);
137 ep93xx_i2s_release();
138}
139
140module_init(snappercl15_init);
141module_exit(snappercl15_exit);
142
143MODULE_AUTHOR("Ryan Mallon");
144MODULE_DESCRIPTION("ALSA SoC Snapper CL15");
145MODULE_LICENSE("GPL");
146