diff options
Diffstat (limited to 'sound/soc')
31 files changed, 2841 insertions, 255 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index f9054f7c1d52..1381db853ef0 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig | |||
@@ -53,6 +53,7 @@ source "sound/soc/nuc900/Kconfig" | |||
53 | source "sound/soc/omap/Kconfig" | 53 | source "sound/soc/omap/Kconfig" |
54 | source "sound/soc/kirkwood/Kconfig" | 54 | source "sound/soc/kirkwood/Kconfig" |
55 | source "sound/soc/mid-x86/Kconfig" | 55 | source "sound/soc/mid-x86/Kconfig" |
56 | source "sound/soc/mxs/Kconfig" | ||
56 | source "sound/soc/pxa/Kconfig" | 57 | source "sound/soc/pxa/Kconfig" |
57 | source "sound/soc/samsung/Kconfig" | 58 | source "sound/soc/samsung/Kconfig" |
58 | source "sound/soc/s6000/Kconfig" | 59 | source "sound/soc/s6000/Kconfig" |
diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 4f913876f332..9ea8ac827adc 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile | |||
@@ -12,6 +12,7 @@ obj-$(CONFIG_SND_SOC) += fsl/ | |||
12 | obj-$(CONFIG_SND_SOC) += imx/ | 12 | obj-$(CONFIG_SND_SOC) += imx/ |
13 | obj-$(CONFIG_SND_SOC) += jz4740/ | 13 | obj-$(CONFIG_SND_SOC) += jz4740/ |
14 | obj-$(CONFIG_SND_SOC) += mid-x86/ | 14 | obj-$(CONFIG_SND_SOC) += mid-x86/ |
15 | obj-$(CONFIG_SND_SOC) += mxs/ | ||
15 | obj-$(CONFIG_SND_SOC) += nuc900/ | 16 | obj-$(CONFIG_SND_SOC) += nuc900/ |
16 | obj-$(CONFIG_SND_SOC) += omap/ | 17 | obj-$(CONFIG_SND_SOC) += omap/ |
17 | obj-$(CONFIG_SND_SOC) += kirkwood/ | 18 | obj-$(CONFIG_SND_SOC) += kirkwood/ |
diff --git a/sound/soc/au1x/Kconfig b/sound/soc/au1x/Kconfig index 4b67140fdec3..6d592546e8fc 100644 --- a/sound/soc/au1x/Kconfig +++ b/sound/soc/au1x/Kconfig | |||
@@ -18,10 +18,38 @@ config SND_SOC_AU1XPSC_AC97 | |||
18 | select SND_AC97_CODEC | 18 | select SND_AC97_CODEC |
19 | select SND_SOC_AC97_BUS | 19 | select SND_SOC_AC97_BUS |
20 | 20 | ||
21 | ## | ||
22 | ## Au1000/1500/1100 DMA + AC97C/I2SC | ||
23 | ## | ||
24 | config SND_SOC_AU1XAUDIO | ||
25 | tristate "SoC Audio for Au1000/Au1500/Au1100" | ||
26 | depends on MIPS_ALCHEMY | ||
27 | help | ||
28 | This is a driver set for the AC97 unit and the | ||
29 | old DMA controller as found on the Au1000/Au1500/Au1100 chips. | ||
30 | |||
31 | config SND_SOC_AU1XAC97C | ||
32 | tristate | ||
33 | select AC97_BUS | ||
34 | select SND_AC97_CODEC | ||
35 | select SND_SOC_AC97_BUS | ||
36 | |||
37 | config SND_SOC_AU1XI2SC | ||
38 | tristate | ||
39 | |||
21 | 40 | ||
22 | ## | 41 | ## |
23 | ## Boards | 42 | ## Boards |
24 | ## | 43 | ## |
44 | config SND_SOC_DB1000 | ||
45 | tristate "DB1000 Audio support" | ||
46 | depends on SND_SOC_AU1XAUDIO | ||
47 | select SND_SOC_AU1XAC97C | ||
48 | select SND_SOC_AC97_CODEC | ||
49 | help | ||
50 | Select this option to enable AC97 audio on the early DB1x00 series | ||
51 | of boards (DB1000/DB1500/DB1100). | ||
52 | |||
25 | config SND_SOC_DB1200 | 53 | config SND_SOC_DB1200 |
26 | tristate "DB1200 AC97+I2S audio support" | 54 | tristate "DB1200 AC97+I2S audio support" |
27 | depends on SND_SOC_AU1XPSC | 55 | depends on SND_SOC_AU1XPSC |
diff --git a/sound/soc/au1x/Makefile b/sound/soc/au1x/Makefile index 16873076e8c4..920710514ea0 100644 --- a/sound/soc/au1x/Makefile +++ b/sound/soc/au1x/Makefile | |||
@@ -3,11 +3,21 @@ snd-soc-au1xpsc-dbdma-objs := dbdma2.o | |||
3 | snd-soc-au1xpsc-i2s-objs := psc-i2s.o | 3 | snd-soc-au1xpsc-i2s-objs := psc-i2s.o |
4 | snd-soc-au1xpsc-ac97-objs := psc-ac97.o | 4 | snd-soc-au1xpsc-ac97-objs := psc-ac97.o |
5 | 5 | ||
6 | # Au1000/1500/1100 Audio units | ||
7 | snd-soc-au1x-dma-objs := dma.o | ||
8 | snd-soc-au1x-ac97c-objs := ac97c.o | ||
9 | snd-soc-au1x-i2sc-objs := i2sc.o | ||
10 | |||
6 | obj-$(CONFIG_SND_SOC_AU1XPSC) += snd-soc-au1xpsc-dbdma.o | 11 | obj-$(CONFIG_SND_SOC_AU1XPSC) += snd-soc-au1xpsc-dbdma.o |
7 | obj-$(CONFIG_SND_SOC_AU1XPSC_I2S) += snd-soc-au1xpsc-i2s.o | 12 | obj-$(CONFIG_SND_SOC_AU1XPSC_I2S) += snd-soc-au1xpsc-i2s.o |
8 | obj-$(CONFIG_SND_SOC_AU1XPSC_AC97) += snd-soc-au1xpsc-ac97.o | 13 | obj-$(CONFIG_SND_SOC_AU1XPSC_AC97) += snd-soc-au1xpsc-ac97.o |
14 | obj-$(CONFIG_SND_SOC_AU1XAUDIO) += snd-soc-au1x-dma.o | ||
15 | obj-$(CONFIG_SND_SOC_AU1XAC97C) += snd-soc-au1x-ac97c.o | ||
16 | obj-$(CONFIG_SND_SOC_AU1XI2SC) += snd-soc-au1x-i2sc.o | ||
9 | 17 | ||
10 | # Boards | 18 | # Boards |
19 | snd-soc-db1000-objs := db1000.o | ||
11 | snd-soc-db1200-objs := db1200.o | 20 | snd-soc-db1200-objs := db1200.o |
12 | 21 | ||
22 | obj-$(CONFIG_SND_SOC_DB1000) += snd-soc-db1000.o | ||
13 | obj-$(CONFIG_SND_SOC_DB1200) += snd-soc-db1200.o | 23 | obj-$(CONFIG_SND_SOC_DB1200) += snd-soc-db1200.o |
diff --git a/sound/soc/au1x/ac97c.c b/sound/soc/au1x/ac97c.c new file mode 100644 index 000000000000..9c05f381d95e --- /dev/null +++ b/sound/soc/au1x/ac97c.c | |||
@@ -0,0 +1,365 @@ | |||
1 | /* | ||
2 | * Au1000/Au1500/Au1100 AC97C controller driver for ASoC | ||
3 | * | ||
4 | * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com> | ||
5 | * | ||
6 | * based on the old ALSA driver originally written by | ||
7 | * Charles Eidsness <charles@cooper-street.com> | ||
8 | */ | ||
9 | |||
10 | #include <linux/init.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/slab.h> | ||
13 | #include <linux/device.h> | ||
14 | #include <linux/delay.h> | ||
15 | #include <linux/mutex.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/suspend.h> | ||
18 | #include <sound/core.h> | ||
19 | #include <sound/pcm.h> | ||
20 | #include <sound/initval.h> | ||
21 | #include <sound/soc.h> | ||
22 | #include <asm/mach-au1x00/au1000.h> | ||
23 | |||
24 | #include "psc.h" | ||
25 | |||
26 | /* register offsets and bits */ | ||
27 | #define AC97_CONFIG 0x00 | ||
28 | #define AC97_STATUS 0x04 | ||
29 | #define AC97_DATA 0x08 | ||
30 | #define AC97_CMDRESP 0x0c | ||
31 | #define AC97_ENABLE 0x10 | ||
32 | |||
33 | #define CFG_RC(x) (((x) & 0x3ff) << 13) /* valid rx slots mask */ | ||
34 | #define CFG_XS(x) (((x) & 0x3ff) << 3) /* valid tx slots mask */ | ||
35 | #define CFG_SG (1 << 2) /* sync gate */ | ||
36 | #define CFG_SN (1 << 1) /* sync control */ | ||
37 | #define CFG_RS (1 << 0) /* acrst# control */ | ||
38 | #define STAT_XU (1 << 11) /* tx underflow */ | ||
39 | #define STAT_XO (1 << 10) /* tx overflow */ | ||
40 | #define STAT_RU (1 << 9) /* rx underflow */ | ||
41 | #define STAT_RO (1 << 8) /* rx overflow */ | ||
42 | #define STAT_RD (1 << 7) /* codec ready */ | ||
43 | #define STAT_CP (1 << 6) /* command pending */ | ||
44 | #define STAT_TE (1 << 4) /* tx fifo empty */ | ||
45 | #define STAT_TF (1 << 3) /* tx fifo full */ | ||
46 | #define STAT_RE (1 << 1) /* rx fifo empty */ | ||
47 | #define STAT_RF (1 << 0) /* rx fifo full */ | ||
48 | #define CMD_SET_DATA(x) (((x) & 0xffff) << 16) | ||
49 | #define CMD_GET_DATA(x) ((x) & 0xffff) | ||
50 | #define CMD_READ (1 << 7) | ||
51 | #define CMD_WRITE (0 << 7) | ||
52 | #define CMD_IDX(x) ((x) & 0x7f) | ||
53 | #define EN_D (1 << 1) /* DISable bit */ | ||
54 | #define EN_CE (1 << 0) /* clock enable bit */ | ||
55 | |||
56 | /* how often to retry failed codec register reads/writes */ | ||
57 | #define AC97_RW_RETRIES 5 | ||
58 | |||
59 | #define AC97_RATES \ | ||
60 | SNDRV_PCM_RATE_CONTINUOUS | ||
61 | |||
62 | #define AC97_FMTS \ | ||
63 | (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE) | ||
64 | |||
65 | /* instance data. There can be only one, MacLeod!!!!, fortunately there IS only | ||
66 | * once AC97C on early Alchemy chips. The newer ones aren't so lucky. | ||
67 | */ | ||
68 | static struct au1xpsc_audio_data *ac97c_workdata; | ||
69 | #define ac97_to_ctx(x) ac97c_workdata | ||
70 | |||
71 | static inline unsigned long RD(struct au1xpsc_audio_data *ctx, int reg) | ||
72 | { | ||
73 | return __raw_readl(ctx->mmio + reg); | ||
74 | } | ||
75 | |||
76 | static inline void WR(struct au1xpsc_audio_data *ctx, int reg, unsigned long v) | ||
77 | { | ||
78 | __raw_writel(v, ctx->mmio + reg); | ||
79 | wmb(); | ||
80 | } | ||
81 | |||
82 | static unsigned short au1xac97c_ac97_read(struct snd_ac97 *ac97, | ||
83 | unsigned short r) | ||
84 | { | ||
85 | struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97); | ||
86 | unsigned int tmo, retry; | ||
87 | unsigned long data; | ||
88 | |||
89 | data = ~0; | ||
90 | retry = AC97_RW_RETRIES; | ||
91 | do { | ||
92 | mutex_lock(&ctx->lock); | ||
93 | |||
94 | tmo = 5; | ||
95 | while ((RD(ctx, AC97_STATUS) & STAT_CP) && tmo--) | ||
96 | udelay(21); /* wait an ac97 frame time */ | ||
97 | if (!tmo) { | ||
98 | pr_debug("ac97rd timeout #1\n"); | ||
99 | goto next; | ||
100 | } | ||
101 | |||
102 | WR(ctx, AC97_CMDRESP, CMD_IDX(r) | CMD_READ); | ||
103 | |||
104 | /* stupid errata: data is only valid for 21us, so | ||
105 | * poll, Forrest, poll... | ||
106 | */ | ||
107 | tmo = 0x10000; | ||
108 | while ((RD(ctx, AC97_STATUS) & STAT_CP) && tmo--) | ||
109 | asm volatile ("nop"); | ||
110 | data = RD(ctx, AC97_CMDRESP); | ||
111 | |||
112 | if (!tmo) | ||
113 | pr_debug("ac97rd timeout #2\n"); | ||
114 | |||
115 | next: | ||
116 | mutex_unlock(&ctx->lock); | ||
117 | } while (--retry && !tmo); | ||
118 | |||
119 | pr_debug("AC97RD %04x %04lx %d\n", r, data, retry); | ||
120 | |||
121 | return retry ? data & 0xffff : 0xffff; | ||
122 | } | ||
123 | |||
124 | static void au1xac97c_ac97_write(struct snd_ac97 *ac97, unsigned short r, | ||
125 | unsigned short v) | ||
126 | { | ||
127 | struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97); | ||
128 | unsigned int tmo, retry; | ||
129 | |||
130 | retry = AC97_RW_RETRIES; | ||
131 | do { | ||
132 | mutex_lock(&ctx->lock); | ||
133 | |||
134 | for (tmo = 5; (RD(ctx, AC97_STATUS) & STAT_CP) && tmo; tmo--) | ||
135 | udelay(21); | ||
136 | if (!tmo) { | ||
137 | pr_debug("ac97wr timeout #1\n"); | ||
138 | goto next; | ||
139 | } | ||
140 | |||
141 | WR(ctx, AC97_CMDRESP, CMD_WRITE | CMD_IDX(r) | CMD_SET_DATA(v)); | ||
142 | |||
143 | for (tmo = 10; (RD(ctx, AC97_STATUS) & STAT_CP) && tmo; tmo--) | ||
144 | udelay(21); | ||
145 | if (!tmo) | ||
146 | pr_debug("ac97wr timeout #2\n"); | ||
147 | next: | ||
148 | mutex_unlock(&ctx->lock); | ||
149 | } while (--retry && !tmo); | ||
150 | |||
151 | pr_debug("AC97WR %04x %04x %d\n", r, v, retry); | ||
152 | } | ||
153 | |||
154 | static void au1xac97c_ac97_warm_reset(struct snd_ac97 *ac97) | ||
155 | { | ||
156 | struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97); | ||
157 | |||
158 | WR(ctx, AC97_CONFIG, ctx->cfg | CFG_SG | CFG_SN); | ||
159 | msleep(20); | ||
160 | WR(ctx, AC97_CONFIG, ctx->cfg | CFG_SG); | ||
161 | WR(ctx, AC97_CONFIG, ctx->cfg); | ||
162 | } | ||
163 | |||
164 | static void au1xac97c_ac97_cold_reset(struct snd_ac97 *ac97) | ||
165 | { | ||
166 | struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97); | ||
167 | int i; | ||
168 | |||
169 | WR(ctx, AC97_CONFIG, ctx->cfg | CFG_RS); | ||
170 | msleep(500); | ||
171 | WR(ctx, AC97_CONFIG, ctx->cfg); | ||
172 | |||
173 | /* wait for codec ready */ | ||
174 | i = 50; | ||
175 | while (((RD(ctx, AC97_STATUS) & STAT_RD) == 0) && --i) | ||
176 | msleep(20); | ||
177 | if (!i) | ||
178 | printk(KERN_ERR "ac97c: codec not ready after cold reset\n"); | ||
179 | } | ||
180 | |||
181 | /* AC97 controller operations */ | ||
182 | struct snd_ac97_bus_ops soc_ac97_ops = { | ||
183 | .read = au1xac97c_ac97_read, | ||
184 | .write = au1xac97c_ac97_write, | ||
185 | .reset = au1xac97c_ac97_cold_reset, | ||
186 | .warm_reset = au1xac97c_ac97_warm_reset, | ||
187 | }; | ||
188 | EXPORT_SYMBOL_GPL(soc_ac97_ops); /* globals be gone! */ | ||
189 | |||
190 | static int alchemy_ac97c_startup(struct snd_pcm_substream *substream, | ||
191 | struct snd_soc_dai *dai) | ||
192 | { | ||
193 | struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai); | ||
194 | snd_soc_dai_set_dma_data(dai, substream, &ctx->dmaids[0]); | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | static struct snd_soc_dai_ops alchemy_ac97c_ops = { | ||
199 | .startup = alchemy_ac97c_startup, | ||
200 | }; | ||
201 | |||
202 | static int au1xac97c_dai_probe(struct snd_soc_dai *dai) | ||
203 | { | ||
204 | return ac97c_workdata ? 0 : -ENODEV; | ||
205 | } | ||
206 | |||
207 | static struct snd_soc_dai_driver au1xac97c_dai_driver = { | ||
208 | .name = "alchemy-ac97c", | ||
209 | .ac97_control = 1, | ||
210 | .probe = au1xac97c_dai_probe, | ||
211 | .playback = { | ||
212 | .rates = AC97_RATES, | ||
213 | .formats = AC97_FMTS, | ||
214 | .channels_min = 2, | ||
215 | .channels_max = 2, | ||
216 | }, | ||
217 | .capture = { | ||
218 | .rates = AC97_RATES, | ||
219 | .formats = AC97_FMTS, | ||
220 | .channels_min = 2, | ||
221 | .channels_max = 2, | ||
222 | }, | ||
223 | .ops = &alchemy_ac97c_ops, | ||
224 | }; | ||
225 | |||
226 | static int __devinit au1xac97c_drvprobe(struct platform_device *pdev) | ||
227 | { | ||
228 | int ret; | ||
229 | struct resource *r; | ||
230 | struct au1xpsc_audio_data *ctx; | ||
231 | |||
232 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | ||
233 | if (!ctx) | ||
234 | return -ENOMEM; | ||
235 | |||
236 | mutex_init(&ctx->lock); | ||
237 | |||
238 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
239 | if (!r) { | ||
240 | ret = -ENODEV; | ||
241 | goto out0; | ||
242 | } | ||
243 | |||
244 | ret = -EBUSY; | ||
245 | if (!request_mem_region(r->start, resource_size(r), pdev->name)) | ||
246 | goto out0; | ||
247 | |||
248 | ctx->mmio = ioremap_nocache(r->start, resource_size(r)); | ||
249 | if (!ctx->mmio) | ||
250 | goto out1; | ||
251 | |||
252 | r = platform_get_resource(pdev, IORESOURCE_DMA, 0); | ||
253 | if (!r) | ||
254 | goto out1; | ||
255 | ctx->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = r->start; | ||
256 | |||
257 | r = platform_get_resource(pdev, IORESOURCE_DMA, 1); | ||
258 | if (!r) | ||
259 | goto out1; | ||
260 | ctx->dmaids[SNDRV_PCM_STREAM_CAPTURE] = r->start; | ||
261 | |||
262 | /* switch it on */ | ||
263 | WR(ctx, AC97_ENABLE, EN_D | EN_CE); | ||
264 | WR(ctx, AC97_ENABLE, EN_CE); | ||
265 | |||
266 | ctx->cfg = CFG_RC(3) | CFG_XS(3); | ||
267 | WR(ctx, AC97_CONFIG, ctx->cfg); | ||
268 | |||
269 | platform_set_drvdata(pdev, ctx); | ||
270 | |||
271 | ret = snd_soc_register_dai(&pdev->dev, &au1xac97c_dai_driver); | ||
272 | if (ret) | ||
273 | goto out1; | ||
274 | |||
275 | ac97c_workdata = ctx; | ||
276 | return 0; | ||
277 | |||
278 | |||
279 | snd_soc_unregister_dai(&pdev->dev); | ||
280 | out1: | ||
281 | release_mem_region(r->start, resource_size(r)); | ||
282 | out0: | ||
283 | kfree(ctx); | ||
284 | return ret; | ||
285 | } | ||
286 | |||
287 | static int __devexit au1xac97c_drvremove(struct platform_device *pdev) | ||
288 | { | ||
289 | struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev); | ||
290 | struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
291 | |||
292 | snd_soc_unregister_dai(&pdev->dev); | ||
293 | |||
294 | WR(ctx, AC97_ENABLE, EN_D); /* clock off, disable */ | ||
295 | |||
296 | iounmap(ctx->mmio); | ||
297 | release_mem_region(r->start, resource_size(r)); | ||
298 | kfree(ctx); | ||
299 | |||
300 | ac97c_workdata = NULL; /* MDEV */ | ||
301 | |||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | #ifdef CONFIG_PM | ||
306 | static int au1xac97c_drvsuspend(struct device *dev) | ||
307 | { | ||
308 | struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev); | ||
309 | |||
310 | WR(ctx, AC97_ENABLE, EN_D); /* clock off, disable */ | ||
311 | |||
312 | return 0; | ||
313 | } | ||
314 | |||
315 | static int au1xac97c_drvresume(struct device *dev) | ||
316 | { | ||
317 | struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev); | ||
318 | |||
319 | WR(ctx, AC97_ENABLE, EN_D | EN_CE); | ||
320 | WR(ctx, AC97_ENABLE, EN_CE); | ||
321 | WR(ctx, AC97_CONFIG, ctx->cfg); | ||
322 | |||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | static const struct dev_pm_ops au1xpscac97_pmops = { | ||
327 | .suspend = au1xac97c_drvsuspend, | ||
328 | .resume = au1xac97c_drvresume, | ||
329 | }; | ||
330 | |||
331 | #define AU1XPSCAC97_PMOPS (&au1xpscac97_pmops) | ||
332 | |||
333 | #else | ||
334 | |||
335 | #define AU1XPSCAC97_PMOPS NULL | ||
336 | |||
337 | #endif | ||
338 | |||
339 | static struct platform_driver au1xac97c_driver = { | ||
340 | .driver = { | ||
341 | .name = "alchemy-ac97c", | ||
342 | .owner = THIS_MODULE, | ||
343 | .pm = AU1XPSCAC97_PMOPS, | ||
344 | }, | ||
345 | .probe = au1xac97c_drvprobe, | ||
346 | .remove = __devexit_p(au1xac97c_drvremove), | ||
347 | }; | ||
348 | |||
349 | static int __init au1xac97c_load(void) | ||
350 | { | ||
351 | ac97c_workdata = NULL; | ||
352 | return platform_driver_register(&au1xac97c_driver); | ||
353 | } | ||
354 | |||
355 | static void __exit au1xac97c_unload(void) | ||
356 | { | ||
357 | platform_driver_unregister(&au1xac97c_driver); | ||
358 | } | ||
359 | |||
360 | module_init(au1xac97c_load); | ||
361 | module_exit(au1xac97c_unload); | ||
362 | |||
363 | MODULE_LICENSE("GPL"); | ||
364 | MODULE_DESCRIPTION("Au1000/1500/1100 AC97C ASoC driver"); | ||
365 | MODULE_AUTHOR("Manuel Lauss"); | ||
diff --git a/sound/soc/au1x/db1000.c b/sound/soc/au1x/db1000.c new file mode 100644 index 000000000000..127477a5e0c7 --- /dev/null +++ b/sound/soc/au1x/db1000.c | |||
@@ -0,0 +1,75 @@ | |||
1 | /* | ||
2 | * DB1000/DB1500/DB1100 ASoC audio fabric support code. | ||
3 | * | ||
4 | * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com> | ||
5 | * | ||
6 | */ | ||
7 | |||
8 | #include <linux/module.h> | ||
9 | #include <linux/moduleparam.h> | ||
10 | #include <linux/timer.h> | ||
11 | #include <linux/interrupt.h> | ||
12 | #include <linux/platform_device.h> | ||
13 | #include <sound/core.h> | ||
14 | #include <sound/pcm.h> | ||
15 | #include <sound/soc.h> | ||
16 | #include <asm/mach-au1x00/au1000.h> | ||
17 | #include <asm/mach-db1x00/bcsr.h> | ||
18 | |||
19 | #include "psc.h" | ||
20 | |||
21 | static struct snd_soc_dai_link db1000_ac97_dai = { | ||
22 | .name = "AC97", | ||
23 | .stream_name = "AC97 HiFi", | ||
24 | .codec_dai_name = "ac97-hifi", | ||
25 | .cpu_dai_name = "alchemy-ac97c", | ||
26 | .platform_name = "alchemy-pcm-dma.0", | ||
27 | .codec_name = "ac97-codec", | ||
28 | }; | ||
29 | |||
30 | static struct snd_soc_card db1000_ac97 = { | ||
31 | .name = "DB1000_AC97", | ||
32 | .dai_link = &db1000_ac97_dai, | ||
33 | .num_links = 1, | ||
34 | }; | ||
35 | |||
36 | static int __devinit db1000_audio_probe(struct platform_device *pdev) | ||
37 | { | ||
38 | struct snd_soc_card *card = &db1000_ac97; | ||
39 | card->dev = &pdev->dev; | ||
40 | return snd_soc_register_card(card); | ||
41 | } | ||
42 | |||
43 | static int __devexit db1000_audio_remove(struct platform_device *pdev) | ||
44 | { | ||
45 | struct snd_soc_card *card = platform_get_drvdata(pdev); | ||
46 | snd_soc_unregister_card(card); | ||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | static struct platform_driver db1000_audio_driver = { | ||
51 | .driver = { | ||
52 | .name = "db1000-audio", | ||
53 | .owner = THIS_MODULE, | ||
54 | .pm = &snd_soc_pm_ops, | ||
55 | }, | ||
56 | .probe = db1000_audio_probe, | ||
57 | .remove = __devexit_p(db1000_audio_remove), | ||
58 | }; | ||
59 | |||
60 | static int __init db1000_audio_load(void) | ||
61 | { | ||
62 | return platform_driver_register(&db1000_audio_driver); | ||
63 | } | ||
64 | |||
65 | static void __exit db1000_audio_unload(void) | ||
66 | { | ||
67 | platform_driver_unregister(&db1000_audio_driver); | ||
68 | } | ||
69 | |||
70 | module_init(db1000_audio_load); | ||
71 | module_exit(db1000_audio_unload); | ||
72 | |||
73 | MODULE_LICENSE("GPL"); | ||
74 | MODULE_DESCRIPTION("DB1000/DB1500/DB1100 ASoC audio"); | ||
75 | MODULE_AUTHOR("Manuel Lauss"); | ||
diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c index 1d3e258c9ea8..289312c14b99 100644 --- a/sound/soc/au1x/db1200.c +++ b/sound/soc/au1x/db1200.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * DB1200 ASoC audio fabric support code. | 2 | * DB1200 ASoC audio fabric support code. |
3 | * | 3 | * |
4 | * (c) 2008-9 Manuel Lauss <manuel.lauss@gmail.com> | 4 | * (c) 2008-2011 Manuel Lauss <manuel.lauss@googlemail.com> |
5 | * | 5 | * |
6 | */ | 6 | */ |
7 | 7 | ||
@@ -21,6 +21,17 @@ | |||
21 | #include "../codecs/wm8731.h" | 21 | #include "../codecs/wm8731.h" |
22 | #include "psc.h" | 22 | #include "psc.h" |
23 | 23 | ||
24 | static struct platform_device_id db1200_pids[] = { | ||
25 | { | ||
26 | .name = "db1200-ac97", | ||
27 | .driver_data = 0, | ||
28 | }, { | ||
29 | .name = "db1200-i2s", | ||
30 | .driver_data = 1, | ||
31 | }, | ||
32 | {}, | ||
33 | }; | ||
34 | |||
24 | /*------------------------- AC97 PART ---------------------------*/ | 35 | /*------------------------- AC97 PART ---------------------------*/ |
25 | 36 | ||
26 | static struct snd_soc_dai_link db1200_ac97_dai = { | 37 | static struct snd_soc_dai_link db1200_ac97_dai = { |
@@ -89,36 +100,47 @@ static struct snd_soc_card db1200_i2s_machine = { | |||
89 | 100 | ||
90 | /*------------------------- COMMON PART ---------------------------*/ | 101 | /*------------------------- COMMON PART ---------------------------*/ |
91 | 102 | ||
92 | static struct platform_device *db1200_asoc_dev; | 103 | static struct snd_soc_card *db1200_cards[] __devinitdata = { |
104 | &db1200_ac97_machine, | ||
105 | &db1200_i2s_machine, | ||
106 | }; | ||
93 | 107 | ||
94 | static int __init db1200_audio_load(void) | 108 | static int __devinit db1200_audio_probe(struct platform_device *pdev) |
95 | { | 109 | { |
96 | int ret; | 110 | const struct platform_device_id *pid = platform_get_device_id(pdev); |
111 | struct snd_soc_card *card; | ||
97 | 112 | ||
98 | ret = -ENOMEM; | 113 | card = db1200_cards[pid->driver_data]; |
99 | db1200_asoc_dev = platform_device_alloc("soc-audio", 1); /* PSC1 */ | 114 | card->dev = &pdev->dev; |
100 | if (!db1200_asoc_dev) | 115 | return snd_soc_register_card(card); |
101 | goto out; | 116 | } |
102 | 117 | ||
103 | /* DB1200 board setup set PSC1MUX to preferred audio device */ | 118 | static int __devexit db1200_audio_remove(struct platform_device *pdev) |
104 | if (bcsr_read(BCSR_RESETS) & BCSR_RESETS_PSC1MUX) | 119 | { |
105 | platform_set_drvdata(db1200_asoc_dev, &db1200_i2s_machine); | 120 | struct snd_soc_card *card = platform_get_drvdata(pdev); |
106 | else | 121 | snd_soc_unregister_card(card); |
107 | platform_set_drvdata(db1200_asoc_dev, &db1200_ac97_machine); | 122 | return 0; |
123 | } | ||
108 | 124 | ||
109 | ret = platform_device_add(db1200_asoc_dev); | 125 | static struct platform_driver db1200_audio_driver = { |
126 | .driver = { | ||
127 | .name = "db1200-ac97", | ||
128 | .owner = THIS_MODULE, | ||
129 | .pm = &snd_soc_pm_ops, | ||
130 | }, | ||
131 | .id_table = db1200_pids, | ||
132 | .probe = db1200_audio_probe, | ||
133 | .remove = __devexit_p(db1200_audio_remove), | ||
134 | }; | ||
110 | 135 | ||
111 | if (ret) { | 136 | static int __init db1200_audio_load(void) |
112 | platform_device_put(db1200_asoc_dev); | 137 | { |
113 | db1200_asoc_dev = NULL; | 138 | return platform_driver_register(&db1200_audio_driver); |
114 | } | ||
115 | out: | ||
116 | return ret; | ||
117 | } | 139 | } |
118 | 140 | ||
119 | static void __exit db1200_audio_unload(void) | 141 | static void __exit db1200_audio_unload(void) |
120 | { | 142 | { |
121 | platform_device_unregister(db1200_asoc_dev); | 143 | platform_driver_unregister(&db1200_audio_driver); |
122 | } | 144 | } |
123 | 145 | ||
124 | module_init(db1200_audio_load); | 146 | module_init(db1200_audio_load); |
diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c index 20bb53a837b1..d7d04e26eee5 100644 --- a/sound/soc/au1x/dbdma2.c +++ b/sound/soc/au1x/dbdma2.c | |||
@@ -169,7 +169,7 @@ static int au1x_pcm_dbdma_realloc(struct au1xpsc_audio_dmadata *pcd, | |||
169 | 169 | ||
170 | au1x_pcm_dbdma_free(pcd); | 170 | au1x_pcm_dbdma_free(pcd); |
171 | 171 | ||
172 | if (stype == PCM_RX) | 172 | if (stype == SNDRV_PCM_STREAM_CAPTURE) |
173 | pcd->ddma_chan = au1xxx_dbdma_chan_alloc(pcd->ddma_id, | 173 | pcd->ddma_chan = au1xxx_dbdma_chan_alloc(pcd->ddma_id, |
174 | DSCR_CMD0_ALWAYS, | 174 | DSCR_CMD0_ALWAYS, |
175 | au1x_pcm_dmarx_cb, (void *)pcd); | 175 | au1x_pcm_dmarx_cb, (void *)pcd); |
@@ -198,7 +198,7 @@ static inline struct au1xpsc_audio_dmadata *to_dmadata(struct snd_pcm_substream | |||
198 | struct snd_soc_pcm_runtime *rtd = ss->private_data; | 198 | struct snd_soc_pcm_runtime *rtd = ss->private_data; |
199 | struct au1xpsc_audio_dmadata *pcd = | 199 | struct au1xpsc_audio_dmadata *pcd = |
200 | snd_soc_platform_get_drvdata(rtd->platform); | 200 | snd_soc_platform_get_drvdata(rtd->platform); |
201 | return &pcd[SUBSTREAM_TYPE(ss)]; | 201 | return &pcd[ss->stream]; |
202 | } | 202 | } |
203 | 203 | ||
204 | static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream, | 204 | static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream, |
@@ -212,7 +212,7 @@ static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream, | |||
212 | if (ret < 0) | 212 | if (ret < 0) |
213 | goto out; | 213 | goto out; |
214 | 214 | ||
215 | stype = SUBSTREAM_TYPE(substream); | 215 | stype = substream->stream; |
216 | pcd = to_dmadata(substream); | 216 | pcd = to_dmadata(substream); |
217 | 217 | ||
218 | DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %d " | 218 | DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %d " |
@@ -255,7 +255,7 @@ static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream) | |||
255 | 255 | ||
256 | au1xxx_dbdma_reset(pcd->ddma_chan); | 256 | au1xxx_dbdma_reset(pcd->ddma_chan); |
257 | 257 | ||
258 | if (SUBSTREAM_TYPE(substream) == PCM_RX) { | 258 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { |
259 | au1x_pcm_queue_rx(pcd); | 259 | au1x_pcm_queue_rx(pcd); |
260 | au1x_pcm_queue_rx(pcd); | 260 | au1x_pcm_queue_rx(pcd); |
261 | } else { | 261 | } else { |
@@ -293,6 +293,16 @@ au1xpsc_pcm_pointer(struct snd_pcm_substream *substream) | |||
293 | 293 | ||
294 | static int au1xpsc_pcm_open(struct snd_pcm_substream *substream) | 294 | static int au1xpsc_pcm_open(struct snd_pcm_substream *substream) |
295 | { | 295 | { |
296 | struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream); | ||
297 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
298 | int stype = substream->stream, *dmaids; | ||
299 | |||
300 | dmaids = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | ||
301 | if (!dmaids) | ||
302 | return -ENODEV; /* whoa, has ordering changed? */ | ||
303 | |||
304 | pcd->ddma_id = dmaids[stype]; | ||
305 | |||
296 | snd_soc_set_runtime_hwparams(substream, &au1xpsc_pcm_hardware); | 306 | snd_soc_set_runtime_hwparams(substream, &au1xpsc_pcm_hardware); |
297 | return 0; | 307 | return 0; |
298 | } | 308 | } |
@@ -340,36 +350,18 @@ struct snd_soc_platform_driver au1xpsc_soc_platform = { | |||
340 | static int __devinit au1xpsc_pcm_drvprobe(struct platform_device *pdev) | 350 | static int __devinit au1xpsc_pcm_drvprobe(struct platform_device *pdev) |
341 | { | 351 | { |
342 | struct au1xpsc_audio_dmadata *dmadata; | 352 | struct au1xpsc_audio_dmadata *dmadata; |
343 | struct resource *r; | ||
344 | int ret; | 353 | int ret; |
345 | 354 | ||
346 | dmadata = kzalloc(2 * sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL); | 355 | dmadata = kzalloc(2 * sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL); |
347 | if (!dmadata) | 356 | if (!dmadata) |
348 | return -ENOMEM; | 357 | return -ENOMEM; |
349 | 358 | ||
350 | r = platform_get_resource(pdev, IORESOURCE_DMA, 0); | ||
351 | if (!r) { | ||
352 | ret = -ENODEV; | ||
353 | goto out1; | ||
354 | } | ||
355 | dmadata[PCM_TX].ddma_id = r->start; | ||
356 | |||
357 | /* RX DMA */ | ||
358 | r = platform_get_resource(pdev, IORESOURCE_DMA, 1); | ||
359 | if (!r) { | ||
360 | ret = -ENODEV; | ||
361 | goto out1; | ||
362 | } | ||
363 | dmadata[PCM_RX].ddma_id = r->start; | ||
364 | |||
365 | platform_set_drvdata(pdev, dmadata); | 359 | platform_set_drvdata(pdev, dmadata); |
366 | 360 | ||
367 | ret = snd_soc_register_platform(&pdev->dev, &au1xpsc_soc_platform); | 361 | ret = snd_soc_register_platform(&pdev->dev, &au1xpsc_soc_platform); |
368 | if (!ret) | 362 | if (ret) |
369 | return ret; | 363 | kfree(dmadata); |
370 | 364 | ||
371 | out1: | ||
372 | kfree(dmadata); | ||
373 | return ret; | 365 | return ret; |
374 | } | 366 | } |
375 | 367 | ||
@@ -405,57 +397,6 @@ static void __exit au1xpsc_audio_dbdma_unload(void) | |||
405 | module_init(au1xpsc_audio_dbdma_load); | 397 | module_init(au1xpsc_audio_dbdma_load); |
406 | module_exit(au1xpsc_audio_dbdma_unload); | 398 | module_exit(au1xpsc_audio_dbdma_unload); |
407 | 399 | ||
408 | |||
409 | struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev) | ||
410 | { | ||
411 | struct resource *res, *r; | ||
412 | struct platform_device *pd; | ||
413 | int id[2]; | ||
414 | int ret; | ||
415 | |||
416 | r = platform_get_resource(pdev, IORESOURCE_DMA, 0); | ||
417 | if (!r) | ||
418 | return NULL; | ||
419 | id[0] = r->start; | ||
420 | |||
421 | r = platform_get_resource(pdev, IORESOURCE_DMA, 1); | ||
422 | if (!r) | ||
423 | return NULL; | ||
424 | id[1] = r->start; | ||
425 | |||
426 | res = kzalloc(sizeof(struct resource) * 2, GFP_KERNEL); | ||
427 | if (!res) | ||
428 | return NULL; | ||
429 | |||
430 | res[0].start = res[0].end = id[0]; | ||
431 | res[1].start = res[1].end = id[1]; | ||
432 | res[0].flags = res[1].flags = IORESOURCE_DMA; | ||
433 | |||
434 | pd = platform_device_alloc("au1xpsc-pcm", pdev->id); | ||
435 | if (!pd) | ||
436 | goto out; | ||
437 | |||
438 | pd->resource = res; | ||
439 | pd->num_resources = 2; | ||
440 | |||
441 | ret = platform_device_add(pd); | ||
442 | if (!ret) | ||
443 | return pd; | ||
444 | |||
445 | platform_device_put(pd); | ||
446 | out: | ||
447 | kfree(res); | ||
448 | return NULL; | ||
449 | } | ||
450 | EXPORT_SYMBOL_GPL(au1xpsc_pcm_add); | ||
451 | |||
452 | void au1xpsc_pcm_destroy(struct platform_device *dmapd) | ||
453 | { | ||
454 | if (dmapd) | ||
455 | platform_device_unregister(dmapd); | ||
456 | } | ||
457 | EXPORT_SYMBOL_GPL(au1xpsc_pcm_destroy); | ||
458 | |||
459 | MODULE_LICENSE("GPL"); | 400 | MODULE_LICENSE("GPL"); |
460 | MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver"); | 401 | MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver"); |
461 | MODULE_AUTHOR("Manuel Lauss"); | 402 | MODULE_AUTHOR("Manuel Lauss"); |
diff --git a/sound/soc/au1x/dma.c b/sound/soc/au1x/dma.c new file mode 100644 index 000000000000..7aa5b7606777 --- /dev/null +++ b/sound/soc/au1x/dma.c | |||
@@ -0,0 +1,377 @@ | |||
1 | /* | ||
2 | * Au1000/Au1500/Au1100 Audio DMA support. | ||
3 | * | ||
4 | * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com> | ||
5 | * | ||
6 | * copied almost verbatim from the old ALSA driver, written by | ||
7 | * Charles Eidsness <charles@cooper-street.com> | ||
8 | */ | ||
9 | |||
10 | #include <linux/module.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/platform_device.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/dma-mapping.h> | ||
15 | #include <sound/core.h> | ||
16 | #include <sound/pcm.h> | ||
17 | #include <sound/pcm_params.h> | ||
18 | #include <sound/soc.h> | ||
19 | #include <asm/mach-au1x00/au1000.h> | ||
20 | #include <asm/mach-au1x00/au1000_dma.h> | ||
21 | |||
22 | #include "psc.h" | ||
23 | |||
24 | #define ALCHEMY_PCM_FMTS \ | ||
25 | (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \ | ||
26 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ | ||
27 | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE | \ | ||
28 | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | \ | ||
29 | SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE | \ | ||
30 | 0) | ||
31 | |||
32 | struct pcm_period { | ||
33 | u32 start; | ||
34 | u32 relative_end; /* relative to start of buffer */ | ||
35 | struct pcm_period *next; | ||
36 | }; | ||
37 | |||
38 | struct audio_stream { | ||
39 | struct snd_pcm_substream *substream; | ||
40 | int dma; | ||
41 | struct pcm_period *buffer; | ||
42 | unsigned int period_size; | ||
43 | unsigned int periods; | ||
44 | }; | ||
45 | |||
46 | struct alchemy_pcm_ctx { | ||
47 | struct audio_stream stream[2]; /* playback & capture */ | ||
48 | }; | ||
49 | |||
50 | static void au1000_release_dma_link(struct audio_stream *stream) | ||
51 | { | ||
52 | struct pcm_period *pointer; | ||
53 | struct pcm_period *pointer_next; | ||
54 | |||
55 | stream->period_size = 0; | ||
56 | stream->periods = 0; | ||
57 | pointer = stream->buffer; | ||
58 | if (!pointer) | ||
59 | return; | ||
60 | do { | ||
61 | pointer_next = pointer->next; | ||
62 | kfree(pointer); | ||
63 | pointer = pointer_next; | ||
64 | } while (pointer != stream->buffer); | ||
65 | stream->buffer = NULL; | ||
66 | } | ||
67 | |||
68 | static int au1000_setup_dma_link(struct audio_stream *stream, | ||
69 | unsigned int period_bytes, | ||
70 | unsigned int periods) | ||
71 | { | ||
72 | struct snd_pcm_substream *substream = stream->substream; | ||
73 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
74 | struct pcm_period *pointer; | ||
75 | unsigned long dma_start; | ||
76 | int i; | ||
77 | |||
78 | dma_start = virt_to_phys(runtime->dma_area); | ||
79 | |||
80 | if (stream->period_size == period_bytes && | ||
81 | stream->periods == periods) | ||
82 | return 0; /* not changed */ | ||
83 | |||
84 | au1000_release_dma_link(stream); | ||
85 | |||
86 | stream->period_size = period_bytes; | ||
87 | stream->periods = periods; | ||
88 | |||
89 | stream->buffer = kmalloc(sizeof(struct pcm_period), GFP_KERNEL); | ||
90 | if (!stream->buffer) | ||
91 | return -ENOMEM; | ||
92 | pointer = stream->buffer; | ||
93 | for (i = 0; i < periods; i++) { | ||
94 | pointer->start = (u32)(dma_start + (i * period_bytes)); | ||
95 | pointer->relative_end = (u32) (((i+1) * period_bytes) - 0x1); | ||
96 | if (i < periods - 1) { | ||
97 | pointer->next = kmalloc(sizeof(struct pcm_period), | ||
98 | GFP_KERNEL); | ||
99 | if (!pointer->next) { | ||
100 | au1000_release_dma_link(stream); | ||
101 | return -ENOMEM; | ||
102 | } | ||
103 | pointer = pointer->next; | ||
104 | } | ||
105 | } | ||
106 | pointer->next = stream->buffer; | ||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | static void au1000_dma_stop(struct audio_stream *stream) | ||
111 | { | ||
112 | if (stream->buffer) | ||
113 | disable_dma(stream->dma); | ||
114 | } | ||
115 | |||
116 | static void au1000_dma_start(struct audio_stream *stream) | ||
117 | { | ||
118 | if (!stream->buffer) | ||
119 | return; | ||
120 | |||
121 | init_dma(stream->dma); | ||
122 | if (get_dma_active_buffer(stream->dma) == 0) { | ||
123 | clear_dma_done0(stream->dma); | ||
124 | set_dma_addr0(stream->dma, stream->buffer->start); | ||
125 | set_dma_count0(stream->dma, stream->period_size >> 1); | ||
126 | set_dma_addr1(stream->dma, stream->buffer->next->start); | ||
127 | set_dma_count1(stream->dma, stream->period_size >> 1); | ||
128 | } else { | ||
129 | clear_dma_done1(stream->dma); | ||
130 | set_dma_addr1(stream->dma, stream->buffer->start); | ||
131 | set_dma_count1(stream->dma, stream->period_size >> 1); | ||
132 | set_dma_addr0(stream->dma, stream->buffer->next->start); | ||
133 | set_dma_count0(stream->dma, stream->period_size >> 1); | ||
134 | } | ||
135 | enable_dma_buffers(stream->dma); | ||
136 | start_dma(stream->dma); | ||
137 | } | ||
138 | |||
139 | static irqreturn_t au1000_dma_interrupt(int irq, void *ptr) | ||
140 | { | ||
141 | struct audio_stream *stream = (struct audio_stream *)ptr; | ||
142 | struct snd_pcm_substream *substream = stream->substream; | ||
143 | |||
144 | switch (get_dma_buffer_done(stream->dma)) { | ||
145 | case DMA_D0: | ||
146 | stream->buffer = stream->buffer->next; | ||
147 | clear_dma_done0(stream->dma); | ||
148 | set_dma_addr0(stream->dma, stream->buffer->next->start); | ||
149 | set_dma_count0(stream->dma, stream->period_size >> 1); | ||
150 | enable_dma_buffer0(stream->dma); | ||
151 | break; | ||
152 | case DMA_D1: | ||
153 | stream->buffer = stream->buffer->next; | ||
154 | clear_dma_done1(stream->dma); | ||
155 | set_dma_addr1(stream->dma, stream->buffer->next->start); | ||
156 | set_dma_count1(stream->dma, stream->period_size >> 1); | ||
157 | enable_dma_buffer1(stream->dma); | ||
158 | break; | ||
159 | case (DMA_D0 | DMA_D1): | ||
160 | pr_debug("DMA %d missed interrupt.\n", stream->dma); | ||
161 | au1000_dma_stop(stream); | ||
162 | au1000_dma_start(stream); | ||
163 | break; | ||
164 | case (~DMA_D0 & ~DMA_D1): | ||
165 | pr_debug("DMA %d empty irq.\n", stream->dma); | ||
166 | } | ||
167 | snd_pcm_period_elapsed(substream); | ||
168 | return IRQ_HANDLED; | ||
169 | } | ||
170 | |||
171 | static const struct snd_pcm_hardware alchemy_pcm_hardware = { | ||
172 | .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | | ||
173 | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BATCH, | ||
174 | .formats = ALCHEMY_PCM_FMTS, | ||
175 | .rates = SNDRV_PCM_RATE_8000_192000, | ||
176 | .rate_min = SNDRV_PCM_RATE_8000, | ||
177 | .rate_max = SNDRV_PCM_RATE_192000, | ||
178 | .channels_min = 2, | ||
179 | .channels_max = 2, | ||
180 | .period_bytes_min = 1024, | ||
181 | .period_bytes_max = 16 * 1024 - 1, | ||
182 | .periods_min = 4, | ||
183 | .periods_max = 255, | ||
184 | .buffer_bytes_max = 128 * 1024, | ||
185 | .fifo_size = 16, | ||
186 | }; | ||
187 | |||
188 | static inline struct alchemy_pcm_ctx *ss_to_ctx(struct snd_pcm_substream *ss) | ||
189 | { | ||
190 | struct snd_soc_pcm_runtime *rtd = ss->private_data; | ||
191 | return snd_soc_platform_get_drvdata(rtd->platform); | ||
192 | } | ||
193 | |||
194 | static inline struct audio_stream *ss_to_as(struct snd_pcm_substream *ss) | ||
195 | { | ||
196 | struct alchemy_pcm_ctx *ctx = ss_to_ctx(ss); | ||
197 | return &(ctx->stream[ss->stream]); | ||
198 | } | ||
199 | |||
200 | static int alchemy_pcm_open(struct snd_pcm_substream *substream) | ||
201 | { | ||
202 | struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream); | ||
203 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
204 | int *dmaids, s = substream->stream; | ||
205 | char *name; | ||
206 | |||
207 | dmaids = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | ||
208 | if (!dmaids) | ||
209 | return -ENODEV; /* whoa, has ordering changed? */ | ||
210 | |||
211 | /* DMA setup */ | ||
212 | name = (s == SNDRV_PCM_STREAM_PLAYBACK) ? "audio-tx" : "audio-rx"; | ||
213 | ctx->stream[s].dma = request_au1000_dma(dmaids[s], name, | ||
214 | au1000_dma_interrupt, IRQF_DISABLED, | ||
215 | &ctx->stream[s]); | ||
216 | set_dma_mode(ctx->stream[s].dma, | ||
217 | get_dma_mode(ctx->stream[s].dma) & ~DMA_NC); | ||
218 | |||
219 | ctx->stream[s].substream = substream; | ||
220 | ctx->stream[s].buffer = NULL; | ||
221 | snd_soc_set_runtime_hwparams(substream, &alchemy_pcm_hardware); | ||
222 | |||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | static int alchemy_pcm_close(struct snd_pcm_substream *substream) | ||
227 | { | ||
228 | struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream); | ||
229 | int stype = substream->stream; | ||
230 | |||
231 | ctx->stream[stype].substream = NULL; | ||
232 | free_au1000_dma(ctx->stream[stype].dma); | ||
233 | |||
234 | return 0; | ||
235 | } | ||
236 | |||
237 | static int alchemy_pcm_hw_params(struct snd_pcm_substream *substream, | ||
238 | struct snd_pcm_hw_params *hw_params) | ||
239 | { | ||
240 | struct audio_stream *stream = ss_to_as(substream); | ||
241 | int err; | ||
242 | |||
243 | err = snd_pcm_lib_malloc_pages(substream, | ||
244 | params_buffer_bytes(hw_params)); | ||
245 | if (err < 0) | ||
246 | return err; | ||
247 | err = au1000_setup_dma_link(stream, | ||
248 | params_period_bytes(hw_params), | ||
249 | params_periods(hw_params)); | ||
250 | if (err) | ||
251 | snd_pcm_lib_free_pages(substream); | ||
252 | |||
253 | return err; | ||
254 | } | ||
255 | |||
256 | static int alchemy_pcm_hw_free(struct snd_pcm_substream *substream) | ||
257 | { | ||
258 | struct audio_stream *stream = ss_to_as(substream); | ||
259 | au1000_release_dma_link(stream); | ||
260 | return snd_pcm_lib_free_pages(substream); | ||
261 | } | ||
262 | |||
263 | static int alchemy_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | ||
264 | { | ||
265 | struct audio_stream *stream = ss_to_as(substream); | ||
266 | int err = 0; | ||
267 | |||
268 | switch (cmd) { | ||
269 | case SNDRV_PCM_TRIGGER_START: | ||
270 | au1000_dma_start(stream); | ||
271 | break; | ||
272 | case SNDRV_PCM_TRIGGER_STOP: | ||
273 | au1000_dma_stop(stream); | ||
274 | break; | ||
275 | default: | ||
276 | err = -EINVAL; | ||
277 | break; | ||
278 | } | ||
279 | return err; | ||
280 | } | ||
281 | |||
282 | static snd_pcm_uframes_t alchemy_pcm_pointer(struct snd_pcm_substream *ss) | ||
283 | { | ||
284 | struct audio_stream *stream = ss_to_as(ss); | ||
285 | long location; | ||
286 | |||
287 | location = get_dma_residue(stream->dma); | ||
288 | location = stream->buffer->relative_end - location; | ||
289 | if (location == -1) | ||
290 | location = 0; | ||
291 | return bytes_to_frames(ss->runtime, location); | ||
292 | } | ||
293 | |||
294 | static struct snd_pcm_ops alchemy_pcm_ops = { | ||
295 | .open = alchemy_pcm_open, | ||
296 | .close = alchemy_pcm_close, | ||
297 | .ioctl = snd_pcm_lib_ioctl, | ||
298 | .hw_params = alchemy_pcm_hw_params, | ||
299 | .hw_free = alchemy_pcm_hw_free, | ||
300 | .trigger = alchemy_pcm_trigger, | ||
301 | .pointer = alchemy_pcm_pointer, | ||
302 | }; | ||
303 | |||
304 | static void alchemy_pcm_free_dma_buffers(struct snd_pcm *pcm) | ||
305 | { | ||
306 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
307 | } | ||
308 | |||
309 | static int alchemy_pcm_new(struct snd_soc_pcm_runtime *rtd) | ||
310 | { | ||
311 | struct snd_pcm *pcm = rtd->pcm; | ||
312 | |||
313 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, | ||
314 | snd_dma_continuous_data(GFP_KERNEL), 65536, (4096 * 1024) - 1); | ||
315 | |||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | struct snd_soc_platform_driver alchemy_pcm_soc_platform = { | ||
320 | .ops = &alchemy_pcm_ops, | ||
321 | .pcm_new = alchemy_pcm_new, | ||
322 | .pcm_free = alchemy_pcm_free_dma_buffers, | ||
323 | }; | ||
324 | |||
325 | static int __devinit alchemy_pcm_drvprobe(struct platform_device *pdev) | ||
326 | { | ||
327 | struct alchemy_pcm_ctx *ctx; | ||
328 | int ret; | ||
329 | |||
330 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | ||
331 | if (!ctx) | ||
332 | return -ENOMEM; | ||
333 | |||
334 | platform_set_drvdata(pdev, ctx); | ||
335 | |||
336 | ret = snd_soc_register_platform(&pdev->dev, &alchemy_pcm_soc_platform); | ||
337 | if (ret) | ||
338 | kfree(ctx); | ||
339 | |||
340 | return ret; | ||
341 | } | ||
342 | |||
343 | static int __devexit alchemy_pcm_drvremove(struct platform_device *pdev) | ||
344 | { | ||
345 | struct alchemy_pcm_ctx *ctx = platform_get_drvdata(pdev); | ||
346 | |||
347 | snd_soc_unregister_platform(&pdev->dev); | ||
348 | kfree(ctx); | ||
349 | |||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | static struct platform_driver alchemy_pcmdma_driver = { | ||
354 | .driver = { | ||
355 | .name = "alchemy-pcm-dma", | ||
356 | .owner = THIS_MODULE, | ||
357 | }, | ||
358 | .probe = alchemy_pcm_drvprobe, | ||
359 | .remove = __devexit_p(alchemy_pcm_drvremove), | ||
360 | }; | ||
361 | |||
362 | static int __init alchemy_pcmdma_load(void) | ||
363 | { | ||
364 | return platform_driver_register(&alchemy_pcmdma_driver); | ||
365 | } | ||
366 | |||
367 | static void __exit alchemy_pcmdma_unload(void) | ||
368 | { | ||
369 | platform_driver_unregister(&alchemy_pcmdma_driver); | ||
370 | } | ||
371 | |||
372 | module_init(alchemy_pcmdma_load); | ||
373 | module_exit(alchemy_pcmdma_unload); | ||
374 | |||
375 | MODULE_LICENSE("GPL"); | ||
376 | MODULE_DESCRIPTION("Au1000/Au1500/Au1100 Audio DMA driver"); | ||
377 | MODULE_AUTHOR("Manuel Lauss"); | ||
diff --git a/sound/soc/au1x/i2sc.c b/sound/soc/au1x/i2sc.c new file mode 100644 index 000000000000..b4172fdd2c48 --- /dev/null +++ b/sound/soc/au1x/i2sc.c | |||
@@ -0,0 +1,347 @@ | |||
1 | /* | ||
2 | * Au1000/Au1500/Au1100 I2S controller driver for ASoC | ||
3 | * | ||
4 | * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com> | ||
5 | * | ||
6 | * Note: clock supplied to the I2S controller must be 256x samplerate. | ||
7 | */ | ||
8 | |||
9 | #include <linux/init.h> | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/slab.h> | ||
12 | #include <linux/suspend.h> | ||
13 | #include <sound/core.h> | ||
14 | #include <sound/pcm.h> | ||
15 | #include <sound/initval.h> | ||
16 | #include <sound/soc.h> | ||
17 | #include <asm/mach-au1x00/au1000.h> | ||
18 | |||
19 | #include "psc.h" | ||
20 | |||
21 | #define I2S_RXTX 0x00 | ||
22 | #define I2S_CFG 0x04 | ||
23 | #define I2S_ENABLE 0x08 | ||
24 | |||
25 | #define CFG_XU (1 << 25) /* tx underflow */ | ||
26 | #define CFG_XO (1 << 24) | ||
27 | #define CFG_RU (1 << 23) | ||
28 | #define CFG_RO (1 << 22) | ||
29 | #define CFG_TR (1 << 21) | ||
30 | #define CFG_TE (1 << 20) | ||
31 | #define CFG_TF (1 << 19) | ||
32 | #define CFG_RR (1 << 18) | ||
33 | #define CFG_RF (1 << 17) | ||
34 | #define CFG_ICK (1 << 12) /* clock invert */ | ||
35 | #define CFG_PD (1 << 11) /* set to make I2SDIO INPUT */ | ||
36 | #define CFG_LB (1 << 10) /* loopback */ | ||
37 | #define CFG_IC (1 << 9) /* word select invert */ | ||
38 | #define CFG_FM_I2S (0 << 7) /* I2S format */ | ||
39 | #define CFG_FM_LJ (1 << 7) /* left-justified */ | ||
40 | #define CFG_FM_RJ (2 << 7) /* right-justified */ | ||
41 | #define CFG_FM_MASK (3 << 7) | ||
42 | #define CFG_TN (1 << 6) /* tx fifo en */ | ||
43 | #define CFG_RN (1 << 5) /* rx fifo en */ | ||
44 | #define CFG_SZ_8 (0x08) | ||
45 | #define CFG_SZ_16 (0x10) | ||
46 | #define CFG_SZ_18 (0x12) | ||
47 | #define CFG_SZ_20 (0x14) | ||
48 | #define CFG_SZ_24 (0x18) | ||
49 | #define CFG_SZ_MASK (0x1f) | ||
50 | #define EN_D (1 << 1) /* DISable */ | ||
51 | #define EN_CE (1 << 0) /* clock enable */ | ||
52 | |||
53 | /* only limited by clock generator and board design */ | ||
54 | #define AU1XI2SC_RATES \ | ||
55 | SNDRV_PCM_RATE_CONTINUOUS | ||
56 | |||
57 | #define AU1XI2SC_FMTS \ | ||
58 | (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \ | ||
59 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ | ||
60 | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE | \ | ||
61 | SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_U18_3LE | \ | ||
62 | SNDRV_PCM_FMTBIT_S18_3BE | SNDRV_PCM_FMTBIT_U18_3BE | \ | ||
63 | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \ | ||
64 | SNDRV_PCM_FMTBIT_S20_3BE | SNDRV_PCM_FMTBIT_U20_3BE | \ | ||
65 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \ | ||
66 | SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE | \ | ||
67 | 0) | ||
68 | |||
69 | static inline unsigned long RD(struct au1xpsc_audio_data *ctx, int reg) | ||
70 | { | ||
71 | return __raw_readl(ctx->mmio + reg); | ||
72 | } | ||
73 | |||
74 | static inline void WR(struct au1xpsc_audio_data *ctx, int reg, unsigned long v) | ||
75 | { | ||
76 | __raw_writel(v, ctx->mmio + reg); | ||
77 | wmb(); | ||
78 | } | ||
79 | |||
80 | static int au1xi2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) | ||
81 | { | ||
82 | struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(cpu_dai); | ||
83 | unsigned long c; | ||
84 | int ret; | ||
85 | |||
86 | ret = -EINVAL; | ||
87 | c = ctx->cfg; | ||
88 | |||
89 | c &= ~CFG_FM_MASK; | ||
90 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
91 | case SND_SOC_DAIFMT_I2S: | ||
92 | c |= CFG_FM_I2S; | ||
93 | break; | ||
94 | case SND_SOC_DAIFMT_MSB: | ||
95 | c |= CFG_FM_RJ; | ||
96 | break; | ||
97 | case SND_SOC_DAIFMT_LSB: | ||
98 | c |= CFG_FM_LJ; | ||
99 | break; | ||
100 | default: | ||
101 | goto out; | ||
102 | } | ||
103 | |||
104 | c &= ~(CFG_IC | CFG_ICK); /* IB-IF */ | ||
105 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
106 | case SND_SOC_DAIFMT_NB_NF: | ||
107 | c |= CFG_IC | CFG_ICK; | ||
108 | break; | ||
109 | case SND_SOC_DAIFMT_NB_IF: | ||
110 | c |= CFG_IC; | ||
111 | break; | ||
112 | case SND_SOC_DAIFMT_IB_NF: | ||
113 | c |= CFG_ICK; | ||
114 | break; | ||
115 | case SND_SOC_DAIFMT_IB_IF: | ||
116 | break; | ||
117 | default: | ||
118 | goto out; | ||
119 | } | ||
120 | |||
121 | /* I2S controller only supports master */ | ||
122 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
123 | case SND_SOC_DAIFMT_CBS_CFS: /* CODEC slave */ | ||
124 | break; | ||
125 | default: | ||
126 | goto out; | ||
127 | } | ||
128 | |||
129 | ret = 0; | ||
130 | ctx->cfg = c; | ||
131 | out: | ||
132 | return ret; | ||
133 | } | ||
134 | |||
135 | static int au1xi2s_trigger(struct snd_pcm_substream *substream, | ||
136 | int cmd, struct snd_soc_dai *dai) | ||
137 | { | ||
138 | struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai); | ||
139 | int stype = SUBSTREAM_TYPE(substream); | ||
140 | |||
141 | switch (cmd) { | ||
142 | case SNDRV_PCM_TRIGGER_START: | ||
143 | case SNDRV_PCM_TRIGGER_RESUME: | ||
144 | /* power up */ | ||
145 | WR(ctx, I2S_ENABLE, EN_D | EN_CE); | ||
146 | WR(ctx, I2S_ENABLE, EN_CE); | ||
147 | ctx->cfg |= (stype == PCM_TX) ? CFG_TN : CFG_RN; | ||
148 | WR(ctx, I2S_CFG, ctx->cfg); | ||
149 | break; | ||
150 | case SNDRV_PCM_TRIGGER_STOP: | ||
151 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
152 | ctx->cfg &= ~((stype == PCM_TX) ? CFG_TN : CFG_RN); | ||
153 | WR(ctx, I2S_CFG, ctx->cfg); | ||
154 | WR(ctx, I2S_ENABLE, EN_D); /* power off */ | ||
155 | break; | ||
156 | default: | ||
157 | return -EINVAL; | ||
158 | } | ||
159 | |||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | static unsigned long msbits_to_reg(int msbits) | ||
164 | { | ||
165 | switch (msbits) { | ||
166 | case 8: | ||
167 | return CFG_SZ_8; | ||
168 | case 16: | ||
169 | return CFG_SZ_16; | ||
170 | case 18: | ||
171 | return CFG_SZ_18; | ||
172 | case 20: | ||
173 | return CFG_SZ_20; | ||
174 | case 24: | ||
175 | return CFG_SZ_24; | ||
176 | } | ||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | static int au1xi2s_hw_params(struct snd_pcm_substream *substream, | ||
181 | struct snd_pcm_hw_params *params, | ||
182 | struct snd_soc_dai *dai) | ||
183 | { | ||
184 | struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai); | ||
185 | unsigned long v; | ||
186 | |||
187 | v = msbits_to_reg(params->msbits); | ||
188 | if (!v) | ||
189 | return -EINVAL; | ||
190 | |||
191 | ctx->cfg &= ~CFG_SZ_MASK; | ||
192 | ctx->cfg |= v; | ||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | static int au1xi2s_startup(struct snd_pcm_substream *substream, | ||
197 | struct snd_soc_dai *dai) | ||
198 | { | ||
199 | struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai); | ||
200 | snd_soc_dai_set_dma_data(dai, substream, &ctx->dmaids[0]); | ||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static const struct snd_soc_dai_ops au1xi2s_dai_ops = { | ||
205 | .startup = au1xi2s_startup, | ||
206 | .trigger = au1xi2s_trigger, | ||
207 | .hw_params = au1xi2s_hw_params, | ||
208 | .set_fmt = au1xi2s_set_fmt, | ||
209 | }; | ||
210 | |||
211 | static struct snd_soc_dai_driver au1xi2s_dai_driver = { | ||
212 | .symmetric_rates = 1, | ||
213 | .playback = { | ||
214 | .rates = AU1XI2SC_RATES, | ||
215 | .formats = AU1XI2SC_FMTS, | ||
216 | .channels_min = 2, | ||
217 | .channels_max = 2, | ||
218 | }, | ||
219 | .capture = { | ||
220 | .rates = AU1XI2SC_RATES, | ||
221 | .formats = AU1XI2SC_FMTS, | ||
222 | .channels_min = 2, | ||
223 | .channels_max = 2, | ||
224 | }, | ||
225 | .ops = &au1xi2s_dai_ops, | ||
226 | }; | ||
227 | |||
228 | static int __devinit au1xi2s_drvprobe(struct platform_device *pdev) | ||
229 | { | ||
230 | int ret; | ||
231 | struct resource *r; | ||
232 | struct au1xpsc_audio_data *ctx; | ||
233 | |||
234 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | ||
235 | if (!ctx) | ||
236 | return -ENOMEM; | ||
237 | |||
238 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
239 | if (!r) { | ||
240 | ret = -ENODEV; | ||
241 | goto out0; | ||
242 | } | ||
243 | |||
244 | ret = -EBUSY; | ||
245 | if (!request_mem_region(r->start, resource_size(r), pdev->name)) | ||
246 | goto out0; | ||
247 | |||
248 | ctx->mmio = ioremap_nocache(r->start, resource_size(r)); | ||
249 | if (!ctx->mmio) | ||
250 | goto out1; | ||
251 | |||
252 | r = platform_get_resource(pdev, IORESOURCE_DMA, 0); | ||
253 | if (!r) | ||
254 | goto out1; | ||
255 | ctx->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = r->start; | ||
256 | |||
257 | r = platform_get_resource(pdev, IORESOURCE_DMA, 1); | ||
258 | if (!r) | ||
259 | goto out1; | ||
260 | ctx->dmaids[SNDRV_PCM_STREAM_CAPTURE] = r->start; | ||
261 | |||
262 | platform_set_drvdata(pdev, ctx); | ||
263 | |||
264 | ret = snd_soc_register_dai(&pdev->dev, &au1xi2s_dai_driver); | ||
265 | if (ret) | ||
266 | goto out1; | ||
267 | |||
268 | return 0; | ||
269 | |||
270 | snd_soc_unregister_dai(&pdev->dev); | ||
271 | out1: | ||
272 | release_mem_region(r->start, resource_size(r)); | ||
273 | out0: | ||
274 | kfree(ctx); | ||
275 | return ret; | ||
276 | } | ||
277 | |||
278 | static int __devexit au1xi2s_drvremove(struct platform_device *pdev) | ||
279 | { | ||
280 | struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev); | ||
281 | struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
282 | |||
283 | snd_soc_unregister_dai(&pdev->dev); | ||
284 | |||
285 | WR(ctx, I2S_ENABLE, EN_D); /* clock off, disable */ | ||
286 | |||
287 | iounmap(ctx->mmio); | ||
288 | release_mem_region(r->start, resource_size(r)); | ||
289 | kfree(ctx); | ||
290 | |||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | #ifdef CONFIG_PM | ||
295 | static int au1xi2s_drvsuspend(struct device *dev) | ||
296 | { | ||
297 | struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev); | ||
298 | |||
299 | WR(ctx, I2S_ENABLE, EN_D); /* clock off, disable */ | ||
300 | |||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | static int au1xi2s_drvresume(struct device *dev) | ||
305 | { | ||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | static const struct dev_pm_ops au1xi2sc_pmops = { | ||
310 | .suspend = au1xi2s_drvsuspend, | ||
311 | .resume = au1xi2s_drvresume, | ||
312 | }; | ||
313 | |||
314 | #define AU1XI2SC_PMOPS (&au1xi2sc_pmops) | ||
315 | |||
316 | #else | ||
317 | |||
318 | #define AU1XI2SC_PMOPS NULL | ||
319 | |||
320 | #endif | ||
321 | |||
322 | static struct platform_driver au1xi2s_driver = { | ||
323 | .driver = { | ||
324 | .name = "alchemy-i2sc", | ||
325 | .owner = THIS_MODULE, | ||
326 | .pm = AU1XI2SC_PMOPS, | ||
327 | }, | ||
328 | .probe = au1xi2s_drvprobe, | ||
329 | .remove = __devexit_p(au1xi2s_drvremove), | ||
330 | }; | ||
331 | |||
332 | static int __init au1xi2s_load(void) | ||
333 | { | ||
334 | return platform_driver_register(&au1xi2s_driver); | ||
335 | } | ||
336 | |||
337 | static void __exit au1xi2s_unload(void) | ||
338 | { | ||
339 | platform_driver_unregister(&au1xi2s_driver); | ||
340 | } | ||
341 | |||
342 | module_init(au1xi2s_load); | ||
343 | module_exit(au1xi2s_unload); | ||
344 | |||
345 | MODULE_LICENSE("GPL"); | ||
346 | MODULE_DESCRIPTION("Au1000/1500/1100 I2S ASoC driver"); | ||
347 | MODULE_AUTHOR("Manuel Lauss"); | ||
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index d0db66f24a00..172eefd38b2d 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c | |||
@@ -41,14 +41,14 @@ | |||
41 | (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3BE) | 41 | (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3BE) |
42 | 42 | ||
43 | #define AC97PCR_START(stype) \ | 43 | #define AC97PCR_START(stype) \ |
44 | ((stype) == PCM_TX ? PSC_AC97PCR_TS : PSC_AC97PCR_RS) | 44 | ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97PCR_TS : PSC_AC97PCR_RS) |
45 | #define AC97PCR_STOP(stype) \ | 45 | #define AC97PCR_STOP(stype) \ |
46 | ((stype) == PCM_TX ? PSC_AC97PCR_TP : PSC_AC97PCR_RP) | 46 | ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97PCR_TP : PSC_AC97PCR_RP) |
47 | #define AC97PCR_CLRFIFO(stype) \ | 47 | #define AC97PCR_CLRFIFO(stype) \ |
48 | ((stype) == PCM_TX ? PSC_AC97PCR_TC : PSC_AC97PCR_RC) | 48 | ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97PCR_TC : PSC_AC97PCR_RC) |
49 | 49 | ||
50 | #define AC97STAT_BUSY(stype) \ | 50 | #define AC97STAT_BUSY(stype) \ |
51 | ((stype) == PCM_TX ? PSC_AC97STAT_TB : PSC_AC97STAT_RB) | 51 | ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97STAT_TB : PSC_AC97STAT_RB) |
52 | 52 | ||
53 | /* instance data. There can be only one, MacLeod!!!! */ | 53 | /* instance data. There can be only one, MacLeod!!!! */ |
54 | static struct au1xpsc_audio_data *au1xpsc_ac97_workdata; | 54 | static struct au1xpsc_audio_data *au1xpsc_ac97_workdata; |
@@ -215,7 +215,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, | |||
215 | { | 215 | { |
216 | struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai); | 216 | struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai); |
217 | unsigned long r, ro, stat; | 217 | unsigned long r, ro, stat; |
218 | int chans, t, stype = SUBSTREAM_TYPE(substream); | 218 | int chans, t, stype = substream->stream; |
219 | 219 | ||
220 | chans = params_channels(params); | 220 | chans = params_channels(params); |
221 | 221 | ||
@@ -235,7 +235,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, | |||
235 | r |= PSC_AC97CFG_SET_LEN(params->msbits); | 235 | r |= PSC_AC97CFG_SET_LEN(params->msbits); |
236 | 236 | ||
237 | /* channels: enable slots for front L/R channel */ | 237 | /* channels: enable slots for front L/R channel */ |
238 | if (stype == PCM_TX) { | 238 | if (stype == SNDRV_PCM_STREAM_PLAYBACK) { |
239 | r &= ~PSC_AC97CFG_TXSLOT_MASK; | 239 | r &= ~PSC_AC97CFG_TXSLOT_MASK; |
240 | r |= PSC_AC97CFG_TXSLOT_ENA(3); | 240 | r |= PSC_AC97CFG_TXSLOT_ENA(3); |
241 | r |= PSC_AC97CFG_TXSLOT_ENA(4); | 241 | r |= PSC_AC97CFG_TXSLOT_ENA(4); |
@@ -294,7 +294,7 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream, | |||
294 | int cmd, struct snd_soc_dai *dai) | 294 | int cmd, struct snd_soc_dai *dai) |
295 | { | 295 | { |
296 | struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai); | 296 | struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai); |
297 | int ret, stype = SUBSTREAM_TYPE(substream); | 297 | int ret, stype = substream->stream; |
298 | 298 | ||
299 | ret = 0; | 299 | ret = 0; |
300 | 300 | ||
@@ -324,12 +324,21 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream, | |||
324 | return ret; | 324 | return ret; |
325 | } | 325 | } |
326 | 326 | ||
327 | static int au1xpsc_ac97_startup(struct snd_pcm_substream *substream, | ||
328 | struct snd_soc_dai *dai) | ||
329 | { | ||
330 | struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai); | ||
331 | snd_soc_dai_set_dma_data(dai, substream, &pscdata->dmaids[0]); | ||
332 | return 0; | ||
333 | } | ||
334 | |||
327 | static int au1xpsc_ac97_probe(struct snd_soc_dai *dai) | 335 | static int au1xpsc_ac97_probe(struct snd_soc_dai *dai) |
328 | { | 336 | { |
329 | return au1xpsc_ac97_workdata ? 0 : -ENODEV; | 337 | return au1xpsc_ac97_workdata ? 0 : -ENODEV; |
330 | } | 338 | } |
331 | 339 | ||
332 | static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = { | 340 | static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = { |
341 | .startup = au1xpsc_ac97_startup, | ||
333 | .trigger = au1xpsc_ac97_trigger, | 342 | .trigger = au1xpsc_ac97_trigger, |
334 | .hw_params = au1xpsc_ac97_hw_params, | 343 | .hw_params = au1xpsc_ac97_hw_params, |
335 | }; | 344 | }; |
@@ -379,6 +388,16 @@ static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev) | |||
379 | if (!wd->mmio) | 388 | if (!wd->mmio) |
380 | goto out1; | 389 | goto out1; |
381 | 390 | ||
391 | r = platform_get_resource(pdev, IORESOURCE_DMA, 0); | ||
392 | if (!r) | ||
393 | goto out2; | ||
394 | wd->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = r->start; | ||
395 | |||
396 | r = platform_get_resource(pdev, IORESOURCE_DMA, 1); | ||
397 | if (!r) | ||
398 | goto out2; | ||
399 | wd->dmaids[SNDRV_PCM_STREAM_CAPTURE] = r->start; | ||
400 | |||
382 | /* configuration: max dma trigger threshold, enable ac97 */ | 401 | /* configuration: max dma trigger threshold, enable ac97 */ |
383 | wd->cfg = PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8 | | 402 | wd->cfg = PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8 | |
384 | PSC_AC97CFG_DE_ENABLE; | 403 | PSC_AC97CFG_DE_ENABLE; |
@@ -401,15 +420,13 @@ static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev) | |||
401 | 420 | ||
402 | ret = snd_soc_register_dai(&pdev->dev, &wd->dai_drv); | 421 | ret = snd_soc_register_dai(&pdev->dev, &wd->dai_drv); |
403 | if (ret) | 422 | if (ret) |
404 | goto out1; | 423 | goto out2; |
405 | 424 | ||
406 | wd->dmapd = au1xpsc_pcm_add(pdev); | 425 | au1xpsc_ac97_workdata = wd; |
407 | if (wd->dmapd) { | 426 | return 0; |
408 | au1xpsc_ac97_workdata = wd; | ||
409 | return 0; | ||
410 | } | ||
411 | 427 | ||
412 | snd_soc_unregister_dai(&pdev->dev); | 428 | out2: |
429 | iounmap(wd->mmio); | ||
413 | out1: | 430 | out1: |
414 | release_mem_region(r->start, resource_size(r)); | 431 | release_mem_region(r->start, resource_size(r)); |
415 | out0: | 432 | out0: |
@@ -422,9 +439,6 @@ static int __devexit au1xpsc_ac97_drvremove(struct platform_device *pdev) | |||
422 | struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev); | 439 | struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev); |
423 | struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 440 | struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
424 | 441 | ||
425 | if (wd->dmapd) | ||
426 | au1xpsc_pcm_destroy(wd->dmapd); | ||
427 | |||
428 | snd_soc_unregister_dai(&pdev->dev); | 442 | snd_soc_unregister_dai(&pdev->dev); |
429 | 443 | ||
430 | /* disable PSC completely */ | 444 | /* disable PSC completely */ |
diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c index fca091276320..7c5ae920544f 100644 --- a/sound/soc/au1x/psc-i2s.c +++ b/sound/soc/au1x/psc-i2s.c | |||
@@ -42,13 +42,13 @@ | |||
42 | (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) | 42 | (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) |
43 | 43 | ||
44 | #define I2SSTAT_BUSY(stype) \ | 44 | #define I2SSTAT_BUSY(stype) \ |
45 | ((stype) == PCM_TX ? PSC_I2SSTAT_TB : PSC_I2SSTAT_RB) | 45 | ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_I2SSTAT_TB : PSC_I2SSTAT_RB) |
46 | #define I2SPCR_START(stype) \ | 46 | #define I2SPCR_START(stype) \ |
47 | ((stype) == PCM_TX ? PSC_I2SPCR_TS : PSC_I2SPCR_RS) | 47 | ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_I2SPCR_TS : PSC_I2SPCR_RS) |
48 | #define I2SPCR_STOP(stype) \ | 48 | #define I2SPCR_STOP(stype) \ |
49 | ((stype) == PCM_TX ? PSC_I2SPCR_TP : PSC_I2SPCR_RP) | 49 | ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_I2SPCR_TP : PSC_I2SPCR_RP) |
50 | #define I2SPCR_CLRFIFO(stype) \ | 50 | #define I2SPCR_CLRFIFO(stype) \ |
51 | ((stype) == PCM_TX ? PSC_I2SPCR_TC : PSC_I2SPCR_RC) | 51 | ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_I2SPCR_TC : PSC_I2SPCR_RC) |
52 | 52 | ||
53 | 53 | ||
54 | static int au1xpsc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, | 54 | static int au1xpsc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, |
@@ -240,7 +240,7 @@ static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, | |||
240 | struct snd_soc_dai *dai) | 240 | struct snd_soc_dai *dai) |
241 | { | 241 | { |
242 | struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai); | 242 | struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai); |
243 | int ret, stype = SUBSTREAM_TYPE(substream); | 243 | int ret, stype = substream->stream; |
244 | 244 | ||
245 | switch (cmd) { | 245 | switch (cmd) { |
246 | case SNDRV_PCM_TRIGGER_START: | 246 | case SNDRV_PCM_TRIGGER_START: |
@@ -257,7 +257,16 @@ static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, | |||
257 | return ret; | 257 | return ret; |
258 | } | 258 | } |
259 | 259 | ||
260 | static int au1xpsc_i2s_startup(struct snd_pcm_substream *substream, | ||
261 | struct snd_soc_dai *dai) | ||
262 | { | ||
263 | struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai); | ||
264 | snd_soc_dai_set_dma_data(dai, substream, &pscdata->dmaids[0]); | ||
265 | return 0; | ||
266 | } | ||
267 | |||
260 | static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = { | 268 | static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = { |
269 | .startup = au1xpsc_i2s_startup, | ||
261 | .trigger = au1xpsc_i2s_trigger, | 270 | .trigger = au1xpsc_i2s_trigger, |
262 | .hw_params = au1xpsc_i2s_hw_params, | 271 | .hw_params = au1xpsc_i2s_hw_params, |
263 | .set_fmt = au1xpsc_i2s_set_fmt, | 272 | .set_fmt = au1xpsc_i2s_set_fmt, |
@@ -304,6 +313,16 @@ static int __devinit au1xpsc_i2s_drvprobe(struct platform_device *pdev) | |||
304 | if (!wd->mmio) | 313 | if (!wd->mmio) |
305 | goto out1; | 314 | goto out1; |
306 | 315 | ||
316 | r = platform_get_resource(pdev, IORESOURCE_DMA, 0); | ||
317 | if (!r) | ||
318 | goto out2; | ||
319 | wd->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = r->start; | ||
320 | |||
321 | r = platform_get_resource(pdev, IORESOURCE_DMA, 1); | ||
322 | if (!r) | ||
323 | goto out2; | ||
324 | wd->dmaids[SNDRV_PCM_STREAM_CAPTURE] = r->start; | ||
325 | |||
307 | /* preserve PSC clock source set up by platform (dev.platform_data | 326 | /* preserve PSC clock source set up by platform (dev.platform_data |
308 | * is already occupied by soc layer) | 327 | * is already occupied by soc layer) |
309 | */ | 328 | */ |
@@ -330,15 +349,11 @@ static int __devinit au1xpsc_i2s_drvprobe(struct platform_device *pdev) | |||
330 | platform_set_drvdata(pdev, wd); | 349 | platform_set_drvdata(pdev, wd); |
331 | 350 | ||
332 | ret = snd_soc_register_dai(&pdev->dev, &wd->dai_drv); | 351 | ret = snd_soc_register_dai(&pdev->dev, &wd->dai_drv); |
333 | if (ret) | 352 | if (!ret) |
334 | goto out1; | ||
335 | |||
336 | /* finally add the DMA device for this PSC */ | ||
337 | wd->dmapd = au1xpsc_pcm_add(pdev); | ||
338 | if (wd->dmapd) | ||
339 | return 0; | 353 | return 0; |
340 | 354 | ||
341 | snd_soc_unregister_dai(&pdev->dev); | 355 | out2: |
356 | iounmap(wd->mmio); | ||
342 | out1: | 357 | out1: |
343 | release_mem_region(r->start, resource_size(r)); | 358 | release_mem_region(r->start, resource_size(r)); |
344 | out0: | 359 | out0: |
@@ -351,9 +366,6 @@ static int __devexit au1xpsc_i2s_drvremove(struct platform_device *pdev) | |||
351 | struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev); | 366 | struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev); |
352 | struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 367 | struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
353 | 368 | ||
354 | if (wd->dmapd) | ||
355 | au1xpsc_pcm_destroy(wd->dmapd); | ||
356 | |||
357 | snd_soc_unregister_dai(&pdev->dev); | 369 | snd_soc_unregister_dai(&pdev->dev); |
358 | 370 | ||
359 | au_writel(0, I2S_CFG(wd)); | 371 | au_writel(0, I2S_CFG(wd)); |
diff --git a/sound/soc/au1x/psc.h b/sound/soc/au1x/psc.h index b30eadd422a7..b16b2e02e0c9 100644 --- a/sound/soc/au1x/psc.h +++ b/sound/soc/au1x/psc.h | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * Au12x0/Au1550 PSC ALSA ASoC audio support. | 2 | * Alchemy ALSA ASoC audio support. |
3 | * | 3 | * |
4 | * (c) 2007-2008 MSC Vertriebsges.m.b.H., | 4 | * (c) 2007-2011 MSC Vertriebsges.m.b.H., |
5 | * Manuel Lauss <manuel.lauss@gmail.com> | 5 | * Manuel Lauss <manuel.lauss@gmail.com> |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
@@ -13,10 +13,6 @@ | |||
13 | #ifndef _AU1X_PCM_H | 13 | #ifndef _AU1X_PCM_H |
14 | #define _AU1X_PCM_H | 14 | #define _AU1X_PCM_H |
15 | 15 | ||
16 | /* DBDMA helpers */ | ||
17 | extern struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev); | ||
18 | extern void au1xpsc_pcm_destroy(struct platform_device *dmapd); | ||
19 | |||
20 | struct au1xpsc_audio_data { | 16 | struct au1xpsc_audio_data { |
21 | void __iomem *mmio; | 17 | void __iomem *mmio; |
22 | 18 | ||
@@ -27,15 +23,9 @@ struct au1xpsc_audio_data { | |||
27 | 23 | ||
28 | unsigned long pm[2]; | 24 | unsigned long pm[2]; |
29 | struct mutex lock; | 25 | struct mutex lock; |
30 | struct platform_device *dmapd; | 26 | int dmaids[2]; |
31 | }; | 27 | }; |
32 | 28 | ||
33 | #define PCM_TX 0 | ||
34 | #define PCM_RX 1 | ||
35 | |||
36 | #define SUBSTREAM_TYPE(substream) \ | ||
37 | ((substream)->stream == SNDRV_PCM_STREAM_PLAYBACK ? PCM_TX : PCM_RX) | ||
38 | |||
39 | /* easy access macros */ | 29 | /* easy access macros */ |
40 | #define PSC_CTRL(x) ((unsigned long)((x)->mmio) + PSC_CTRL_OFFSET) | 30 | #define PSC_CTRL(x) ((unsigned long)((x)->mmio) + PSC_CTRL_OFFSET) |
41 | #define PSC_SEL(x) ((unsigned long)((x)->mmio) + PSC_SEL_OFFSET) | 31 | #define PSC_SEL(x) ((unsigned long)((x)->mmio) + PSC_SEL_OFFSET) |
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 76258f2a2ffb..666fae6e148d 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/regulator/driver.h> | 20 | #include <linux/regulator/driver.h> |
21 | #include <linux/regulator/machine.h> | 21 | #include <linux/regulator/machine.h> |
22 | #include <linux/regulator/consumer.h> | 22 | #include <linux/regulator/consumer.h> |
23 | #include <linux/of_device.h> | ||
23 | #include <sound/core.h> | 24 | #include <sound/core.h> |
24 | #include <sound/tlv.h> | 25 | #include <sound/tlv.h> |
25 | #include <sound/pcm.h> | 26 | #include <sound/pcm.h> |
@@ -33,73 +34,31 @@ | |||
33 | #define SGTL5000_DAP_REG_OFFSET 0x0100 | 34 | #define SGTL5000_DAP_REG_OFFSET 0x0100 |
34 | #define SGTL5000_MAX_REG_OFFSET 0x013A | 35 | #define SGTL5000_MAX_REG_OFFSET 0x013A |
35 | 36 | ||
36 | /* default value of sgtl5000 registers except DAP */ | 37 | /* default value of sgtl5000 registers */ |
37 | static const u16 sgtl5000_regs[SGTL5000_MAX_REG_OFFSET >> 1] = { | 38 | static const u16 sgtl5000_regs[SGTL5000_MAX_REG_OFFSET] = { |
38 | 0xa011, /* 0x0000, CHIP_ID. 11 stand for revison 17 */ | 39 | [SGTL5000_CHIP_CLK_CTRL] = 0x0008, |
39 | 0x0000, /* 0x0002, CHIP_DIG_POWER. */ | 40 | [SGTL5000_CHIP_I2S_CTRL] = 0x0010, |
40 | 0x0008, /* 0x0004, CHIP_CKL_CTRL */ | 41 | [SGTL5000_CHIP_SSS_CTRL] = 0x0008, |
41 | 0x0010, /* 0x0006, CHIP_I2S_CTRL */ | 42 | [SGTL5000_CHIP_DAC_VOL] = 0x3c3c, |
42 | 0x0000, /* 0x0008, reserved */ | 43 | [SGTL5000_CHIP_PAD_STRENGTH] = 0x015f, |
43 | 0x0008, /* 0x000A, CHIP_SSS_CTRL */ | 44 | [SGTL5000_CHIP_ANA_HP_CTRL] = 0x1818, |
44 | 0x0000, /* 0x000C, reserved */ | 45 | [SGTL5000_CHIP_ANA_CTRL] = 0x0111, |
45 | 0x020c, /* 0x000E, CHIP_ADCDAC_CTRL */ | 46 | [SGTL5000_CHIP_LINE_OUT_VOL] = 0x0404, |
46 | 0x3c3c, /* 0x0010, CHIP_DAC_VOL */ | 47 | [SGTL5000_CHIP_ANA_POWER] = 0x7060, |
47 | 0x0000, /* 0x0012, reserved */ | 48 | [SGTL5000_CHIP_PLL_CTRL] = 0x5000, |
48 | 0x015f, /* 0x0014, CHIP_PAD_STRENGTH */ | 49 | [SGTL5000_DAP_BASS_ENHANCE] = 0x0040, |
49 | 0x0000, /* 0x0016, reserved */ | 50 | [SGTL5000_DAP_BASS_ENHANCE_CTRL] = 0x051f, |
50 | 0x0000, /* 0x0018, reserved */ | 51 | [SGTL5000_DAP_SURROUND] = 0x0040, |
51 | 0x0000, /* 0x001A, reserved */ | 52 | [SGTL5000_DAP_EQ_BASS_BAND0] = 0x002f, |
52 | 0x0000, /* 0x001E, reserved */ | 53 | [SGTL5000_DAP_EQ_BASS_BAND1] = 0x002f, |
53 | 0x0000, /* 0x0020, CHIP_ANA_ADC_CTRL */ | 54 | [SGTL5000_DAP_EQ_BASS_BAND2] = 0x002f, |
54 | 0x1818, /* 0x0022, CHIP_ANA_HP_CTRL */ | 55 | [SGTL5000_DAP_EQ_BASS_BAND3] = 0x002f, |
55 | 0x0111, /* 0x0024, CHIP_ANN_CTRL */ | 56 | [SGTL5000_DAP_EQ_BASS_BAND4] = 0x002f, |
56 | 0x0000, /* 0x0026, CHIP_LINREG_CTRL */ | 57 | [SGTL5000_DAP_MAIN_CHAN] = 0x8000, |
57 | 0x0000, /* 0x0028, CHIP_REF_CTRL */ | 58 | [SGTL5000_DAP_AVC_CTRL] = 0x0510, |
58 | 0x0000, /* 0x002A, CHIP_MIC_CTRL */ | 59 | [SGTL5000_DAP_AVC_THRESHOLD] = 0x1473, |
59 | 0x0000, /* 0x002C, CHIP_LINE_OUT_CTRL */ | 60 | [SGTL5000_DAP_AVC_ATTACK] = 0x0028, |
60 | 0x0404, /* 0x002E, CHIP_LINE_OUT_VOL */ | 61 | [SGTL5000_DAP_AVC_DECAY] = 0x0050, |
61 | 0x7060, /* 0x0030, CHIP_ANA_POWER */ | ||
62 | 0x5000, /* 0x0032, CHIP_PLL_CTRL */ | ||
63 | 0x0000, /* 0x0034, CHIP_CLK_TOP_CTRL */ | ||
64 | 0x0000, /* 0x0036, CHIP_ANA_STATUS */ | ||
65 | 0x0000, /* 0x0038, reserved */ | ||
66 | 0x0000, /* 0x003A, CHIP_ANA_TEST2 */ | ||
67 | 0x0000, /* 0x003C, CHIP_SHORT_CTRL */ | ||
68 | 0x0000, /* reserved */ | ||
69 | }; | ||
70 | |||
71 | /* default value of dap registers */ | ||
72 | static const u16 sgtl5000_dap_regs[] = { | ||
73 | 0x0000, /* 0x0100, DAP_CONTROL */ | ||
74 | 0x0000, /* 0x0102, DAP_PEQ */ | ||
75 | 0x0040, /* 0x0104, DAP_BASS_ENHANCE */ | ||
76 | 0x051f, /* 0x0106, DAP_BASS_ENHANCE_CTRL */ | ||
77 | 0x0000, /* 0x0108, DAP_AUDIO_EQ */ | ||
78 | 0x0040, /* 0x010A, DAP_SGTL_SURROUND */ | ||
79 | 0x0000, /* 0x010C, DAP_FILTER_COEF_ACCESS */ | ||
80 | 0x0000, /* 0x010E, DAP_COEF_WR_B0_MSB */ | ||
81 | 0x0000, /* 0x0110, DAP_COEF_WR_B0_LSB */ | ||
82 | 0x0000, /* 0x0112, reserved */ | ||
83 | 0x0000, /* 0x0114, reserved */ | ||
84 | 0x002f, /* 0x0116, DAP_AUDIO_EQ_BASS_BAND0 */ | ||
85 | 0x002f, /* 0x0118, DAP_AUDIO_EQ_BAND0 */ | ||
86 | 0x002f, /* 0x011A, DAP_AUDIO_EQ_BAND2 */ | ||
87 | 0x002f, /* 0x011C, DAP_AUDIO_EQ_BAND3 */ | ||
88 | 0x002f, /* 0x011E, DAP_AUDIO_EQ_TREBLE_BAND4 */ | ||
89 | 0x8000, /* 0x0120, DAP_MAIN_CHAN */ | ||
90 | 0x0000, /* 0x0122, DAP_MIX_CHAN */ | ||
91 | 0x0510, /* 0x0124, DAP_AVC_CTRL */ | ||
92 | 0x1473, /* 0x0126, DAP_AVC_THRESHOLD */ | ||
93 | 0x0028, /* 0x0128, DAP_AVC_ATTACK */ | ||
94 | 0x0050, /* 0x012A, DAP_AVC_DECAY */ | ||
95 | 0x0000, /* 0x012C, DAP_COEF_WR_B1_MSB */ | ||
96 | 0x0000, /* 0x012E, DAP_COEF_WR_B1_LSB */ | ||
97 | 0x0000, /* 0x0130, DAP_COEF_WR_B2_MSB */ | ||
98 | 0x0000, /* 0x0132, DAP_COEF_WR_B2_LSB */ | ||
99 | 0x0000, /* 0x0134, DAP_COEF_WR_A1_MSB */ | ||
100 | 0x0000, /* 0x0136, DAP_COEF_WR_A1_LSB */ | ||
101 | 0x0000, /* 0x0138, DAP_COEF_WR_A2_MSB */ | ||
102 | 0x0000, /* 0x013A, DAP_COEF_WR_A2_LSB */ | ||
103 | }; | 62 | }; |
104 | 63 | ||
105 | /* regulator supplies for sgtl5000, VDDD is an optional external supply */ | 64 | /* regulator supplies for sgtl5000, VDDD is an optional external supply */ |
@@ -1023,12 +982,10 @@ static int sgtl5000_suspend(struct snd_soc_codec *codec, pm_message_t state) | |||
1023 | static int sgtl5000_restore_regs(struct snd_soc_codec *codec) | 982 | static int sgtl5000_restore_regs(struct snd_soc_codec *codec) |
1024 | { | 983 | { |
1025 | u16 *cache = codec->reg_cache; | 984 | u16 *cache = codec->reg_cache; |
1026 | int i; | 985 | u16 reg; |
1027 | int regular_regs = SGTL5000_CHIP_SHORT_CTRL >> 1; | ||
1028 | 986 | ||
1029 | /* restore regular registers */ | 987 | /* restore regular registers */ |
1030 | for (i = 0; i < regular_regs; i++) { | 988 | for (reg = 0; reg <= SGTL5000_CHIP_SHORT_CTRL; reg += 2) { |
1031 | int reg = i << 1; | ||
1032 | 989 | ||
1033 | /* this regs depends on the others */ | 990 | /* this regs depends on the others */ |
1034 | if (reg == SGTL5000_CHIP_ANA_POWER || | 991 | if (reg == SGTL5000_CHIP_ANA_POWER || |
@@ -1038,35 +995,31 @@ static int sgtl5000_restore_regs(struct snd_soc_codec *codec) | |||
1038 | reg == SGTL5000_CHIP_CLK_CTRL) | 995 | reg == SGTL5000_CHIP_CLK_CTRL) |
1039 | continue; | 996 | continue; |
1040 | 997 | ||
1041 | snd_soc_write(codec, reg, cache[i]); | 998 | snd_soc_write(codec, reg, cache[reg]); |
1042 | } | 999 | } |
1043 | 1000 | ||
1044 | /* restore dap registers */ | 1001 | /* restore dap registers */ |
1045 | for (i = SGTL5000_DAP_REG_OFFSET >> 1; | 1002 | for (reg = SGTL5000_DAP_REG_OFFSET; reg < SGTL5000_MAX_REG_OFFSET; reg += 2) |
1046 | i < SGTL5000_MAX_REG_OFFSET >> 1; i++) { | 1003 | snd_soc_write(codec, reg, cache[reg]); |
1047 | int reg = i << 1; | ||
1048 | |||
1049 | snd_soc_write(codec, reg, cache[i]); | ||
1050 | } | ||
1051 | 1004 | ||
1052 | /* | 1005 | /* |
1053 | * restore power and other regs according | 1006 | * restore power and other regs according |
1054 | * to set_power() and set_clock() | 1007 | * to set_power() and set_clock() |
1055 | */ | 1008 | */ |
1056 | snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL, | 1009 | snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL, |
1057 | cache[SGTL5000_CHIP_LINREG_CTRL >> 1]); | 1010 | cache[SGTL5000_CHIP_LINREG_CTRL]); |
1058 | 1011 | ||
1059 | snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, | 1012 | snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, |
1060 | cache[SGTL5000_CHIP_ANA_POWER >> 1]); | 1013 | cache[SGTL5000_CHIP_ANA_POWER]); |
1061 | 1014 | ||
1062 | snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, | 1015 | snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, |
1063 | cache[SGTL5000_CHIP_CLK_CTRL >> 1]); | 1016 | cache[SGTL5000_CHIP_CLK_CTRL]); |
1064 | 1017 | ||
1065 | snd_soc_write(codec, SGTL5000_CHIP_REF_CTRL, | 1018 | snd_soc_write(codec, SGTL5000_CHIP_REF_CTRL, |
1066 | cache[SGTL5000_CHIP_REF_CTRL >> 1]); | 1019 | cache[SGTL5000_CHIP_REF_CTRL]); |
1067 | 1020 | ||
1068 | snd_soc_write(codec, SGTL5000_CHIP_LINE_OUT_CTRL, | 1021 | snd_soc_write(codec, SGTL5000_CHIP_LINE_OUT_CTRL, |
1069 | cache[SGTL5000_CHIP_LINE_OUT_CTRL >> 1]); | 1022 | cache[SGTL5000_CHIP_LINE_OUT_CTRL]); |
1070 | return 0; | 1023 | return 0; |
1071 | } | 1024 | } |
1072 | 1025 | ||
@@ -1454,16 +1407,6 @@ static __devinit int sgtl5000_i2c_probe(struct i2c_client *client, | |||
1454 | if (!sgtl5000) | 1407 | if (!sgtl5000) |
1455 | return -ENOMEM; | 1408 | return -ENOMEM; |
1456 | 1409 | ||
1457 | /* | ||
1458 | * copy DAP default values to default value array. | ||
1459 | * sgtl5000 register space has a big hole, merge it | ||
1460 | * at init phase makes life easy. | ||
1461 | * FIXME: should we drop 'const' of sgtl5000_regs? | ||
1462 | */ | ||
1463 | memcpy((void *)(&sgtl5000_regs[0] + (SGTL5000_DAP_REG_OFFSET >> 1)), | ||
1464 | sgtl5000_dap_regs, | ||
1465 | SGTL5000_MAX_REG_OFFSET - SGTL5000_DAP_REG_OFFSET); | ||
1466 | |||
1467 | i2c_set_clientdata(client, sgtl5000); | 1410 | i2c_set_clientdata(client, sgtl5000); |
1468 | 1411 | ||
1469 | ret = snd_soc_register_codec(&client->dev, | 1412 | ret = snd_soc_register_codec(&client->dev, |
@@ -1494,10 +1437,17 @@ static const struct i2c_device_id sgtl5000_id[] = { | |||
1494 | 1437 | ||
1495 | MODULE_DEVICE_TABLE(i2c, sgtl5000_id); | 1438 | MODULE_DEVICE_TABLE(i2c, sgtl5000_id); |
1496 | 1439 | ||
1440 | static const struct of_device_id sgtl5000_dt_ids[] = { | ||
1441 | { .compatible = "fsl,sgtl5000", }, | ||
1442 | { /* sentinel */ } | ||
1443 | }; | ||
1444 | MODULE_DEVICE_TABLE(i2c, sgtl5000_dt_ids); | ||
1445 | |||
1497 | static struct i2c_driver sgtl5000_i2c_driver = { | 1446 | static struct i2c_driver sgtl5000_i2c_driver = { |
1498 | .driver = { | 1447 | .driver = { |
1499 | .name = "sgtl5000", | 1448 | .name = "sgtl5000", |
1500 | .owner = THIS_MODULE, | 1449 | .owner = THIS_MODULE, |
1450 | .of_match_table = sgtl5000_dt_ids, | ||
1501 | }, | 1451 | }, |
1502 | .probe = sgtl5000_i2c_probe, | 1452 | .probe = sgtl5000_i2c_probe, |
1503 | .remove = __devexit_p(sgtl5000_i2c_remove), | 1453 | .remove = __devexit_p(sgtl5000_i2c_remove), |
diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c index bcc208967917..bbcf9ec34759 100644 --- a/sound/soc/codecs/wm1250-ev1.c +++ b/sound/soc/codecs/wm1250-ev1.c | |||
@@ -56,8 +56,26 @@ static struct snd_soc_codec_driver soc_codec_dev_wm1250_ev1 = { | |||
56 | }; | 56 | }; |
57 | 57 | ||
58 | static int __devinit wm1250_ev1_probe(struct i2c_client *i2c, | 58 | static int __devinit wm1250_ev1_probe(struct i2c_client *i2c, |
59 | const struct i2c_device_id *id) | 59 | const struct i2c_device_id *i2c_id) |
60 | { | 60 | { |
61 | int ret, id, board, rev; | ||
62 | |||
63 | board = i2c_smbus_read_byte_data(i2c, 0); | ||
64 | if (board < 0) { | ||
65 | dev_err(&i2c->dev, "Failed to read ID: %d\n", ret); | ||
66 | return ret; | ||
67 | } | ||
68 | |||
69 | id = (board & 0xfe) >> 2; | ||
70 | rev = board & 0x3; | ||
71 | |||
72 | if (id != 1) { | ||
73 | dev_err(&i2c->dev, "Unknown board ID %d\n", id); | ||
74 | return -ENODEV; | ||
75 | } | ||
76 | |||
77 | dev_info(&i2c->dev, "revision %d\n", rev); | ||
78 | |||
61 | return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm1250_ev1, | 79 | return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm1250_ev1, |
62 | &wm1250_ev1_dai, 1); | 80 | &wm1250_ev1_dai, 1); |
63 | } | 81 | } |
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index 4fd4d8dca0fc..131200917c56 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c | |||
@@ -551,7 +551,7 @@ MODULE_DEVICE_TABLE(i2c, wm8523_i2c_id); | |||
551 | 551 | ||
552 | static struct i2c_driver wm8523_i2c_driver = { | 552 | static struct i2c_driver wm8523_i2c_driver = { |
553 | .driver = { | 553 | .driver = { |
554 | .name = "wm8523-codec", | 554 | .name = "wm8523", |
555 | .owner = THIS_MODULE, | 555 | .owner = THIS_MODULE, |
556 | }, | 556 | }, |
557 | .probe = wm8523_i2c_probe, | 557 | .probe = wm8523_i2c_probe, |
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index 4bbc0a79f01e..95ac6651094f 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c | |||
@@ -943,7 +943,7 @@ MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id); | |||
943 | 943 | ||
944 | static struct i2c_driver wm8580_i2c_driver = { | 944 | static struct i2c_driver wm8580_i2c_driver = { |
945 | .driver = { | 945 | .driver = { |
946 | .name = "wm8580-codec", | 946 | .name = "wm8580", |
947 | .owner = THIS_MODULE, | 947 | .owner = THIS_MODULE, |
948 | }, | 948 | }, |
949 | .probe = wm8580_i2c_probe, | 949 | .probe = wm8580_i2c_probe, |
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 76b4361e9b80..f76b6fc6766a 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/platform_device.h> | 22 | #include <linux/platform_device.h> |
23 | #include <linux/regulator/consumer.h> | 23 | #include <linux/regulator/consumer.h> |
24 | #include <linux/spi/spi.h> | 24 | #include <linux/spi/spi.h> |
25 | #include <linux/of_device.h> | ||
25 | #include <sound/core.h> | 26 | #include <sound/core.h> |
26 | #include <sound/pcm.h> | 27 | #include <sound/pcm.h> |
27 | #include <sound/pcm_params.h> | 28 | #include <sound/pcm_params.h> |
@@ -607,6 +608,13 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8731 = { | |||
607 | .num_dapm_routes = ARRAY_SIZE(wm8731_intercon), | 608 | .num_dapm_routes = ARRAY_SIZE(wm8731_intercon), |
608 | }; | 609 | }; |
609 | 610 | ||
611 | static const struct of_device_id wm8731_of_match[] = { | ||
612 | { .compatible = "wlf,wm8731", }, | ||
613 | { } | ||
614 | }; | ||
615 | |||
616 | MODULE_DEVICE_TABLE(of, wm8731_of_match); | ||
617 | |||
610 | #if defined(CONFIG_SPI_MASTER) | 618 | #if defined(CONFIG_SPI_MASTER) |
611 | static int __devinit wm8731_spi_probe(struct spi_device *spi) | 619 | static int __devinit wm8731_spi_probe(struct spi_device *spi) |
612 | { | 620 | { |
@@ -638,6 +646,7 @@ static struct spi_driver wm8731_spi_driver = { | |||
638 | .driver = { | 646 | .driver = { |
639 | .name = "wm8731", | 647 | .name = "wm8731", |
640 | .owner = THIS_MODULE, | 648 | .owner = THIS_MODULE, |
649 | .of_match_table = wm8731_of_match, | ||
641 | }, | 650 | }, |
642 | .probe = wm8731_spi_probe, | 651 | .probe = wm8731_spi_probe, |
643 | .remove = __devexit_p(wm8731_spi_remove), | 652 | .remove = __devexit_p(wm8731_spi_remove), |
@@ -682,6 +691,7 @@ static struct i2c_driver wm8731_i2c_driver = { | |||
682 | .driver = { | 691 | .driver = { |
683 | .name = "wm8731", | 692 | .name = "wm8731", |
684 | .owner = THIS_MODULE, | 693 | .owner = THIS_MODULE, |
694 | .of_match_table = wm8731_of_match, | ||
685 | }, | 695 | }, |
686 | .probe = wm8731_i2c_probe, | 696 | .probe = wm8731_i2c_probe, |
687 | .remove = __devexit_p(wm8731_i2c_remove), | 697 | .remove = __devexit_p(wm8731_i2c_remove), |
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 6e85b8869af7..f014e5676d20 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c | |||
@@ -1433,7 +1433,8 @@ static int wm8993_probe(struct snd_soc_codec *codec) | |||
1433 | int ret, i, val; | 1433 | int ret, i, val; |
1434 | 1434 | ||
1435 | wm8993->hubs_data.hp_startup_mode = 1; | 1435 | wm8993->hubs_data.hp_startup_mode = 1; |
1436 | wm8993->hubs_data.dcs_codes = -2; | 1436 | wm8993->hubs_data.dcs_codes_l = -2; |
1437 | wm8993->hubs_data.dcs_codes_r = -2; | ||
1437 | wm8993->hubs_data.series_startup = 1; | 1438 | wm8993->hubs_data.series_startup = 1; |
1438 | 1439 | ||
1439 | ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C); | 1440 | ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C); |
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 09e680ae88b2..fb5c96163610 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c | |||
@@ -107,6 +107,7 @@ static int wm8994_volatile(struct snd_soc_codec *codec, unsigned int reg) | |||
107 | case WM8994_LDO_2: | 107 | case WM8994_LDO_2: |
108 | case WM8958_DSP2_EXECCONTROL: | 108 | case WM8958_DSP2_EXECCONTROL: |
109 | case WM8958_MIC_DETECT_3: | 109 | case WM8958_MIC_DETECT_3: |
110 | case WM8994_DC_SERVO_4E: | ||
110 | return 1; | 111 | return 1; |
111 | default: | 112 | default: |
112 | return 0; | 113 | return 0; |
@@ -2972,13 +2973,14 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) | |||
2972 | switch (wm8994->revision) { | 2973 | switch (wm8994->revision) { |
2973 | case 2: | 2974 | case 2: |
2974 | case 3: | 2975 | case 3: |
2975 | wm8994->hubs.dcs_codes = -5; | 2976 | wm8994->hubs.dcs_codes_l = -5; |
2977 | wm8994->hubs.dcs_codes_r = -5; | ||
2976 | wm8994->hubs.hp_startup_mode = 1; | 2978 | wm8994->hubs.hp_startup_mode = 1; |
2977 | wm8994->hubs.dcs_readback_mode = 1; | 2979 | wm8994->hubs.dcs_readback_mode = 1; |
2978 | wm8994->hubs.series_startup = 1; | 2980 | wm8994->hubs.series_startup = 1; |
2979 | break; | 2981 | break; |
2980 | default: | 2982 | default: |
2981 | wm8994->hubs.dcs_readback_mode = 1; | 2983 | wm8994->hubs.dcs_readback_mode = 2; |
2982 | break; | 2984 | break; |
2983 | } | 2985 | } |
2984 | 2986 | ||
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 4cc2d567f22f..017522e7cef9 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/pm.h> | 18 | #include <linux/pm.h> |
19 | #include <linux/i2c.h> | 19 | #include <linux/i2c.h> |
20 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
21 | #include <linux/mfd/wm8994/registers.h> | ||
21 | #include <sound/core.h> | 22 | #include <sound/core.h> |
22 | #include <sound/pcm.h> | 23 | #include <sound/pcm.h> |
23 | #include <sound/pcm_params.h> | 24 | #include <sound/pcm_params.h> |
@@ -116,14 +117,23 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec) | |||
116 | { | 117 | { |
117 | struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); | 118 | struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); |
118 | s8 offset; | 119 | s8 offset; |
119 | u16 reg, reg_l, reg_r, dcs_cfg; | 120 | u16 reg, reg_l, reg_r, dcs_cfg, dcs_reg; |
121 | |||
122 | switch (hubs->dcs_readback_mode) { | ||
123 | case 2: | ||
124 | dcs_reg = WM8994_DC_SERVO_4E; | ||
125 | break; | ||
126 | default: | ||
127 | dcs_reg = WM8993_DC_SERVO_3; | ||
128 | break; | ||
129 | } | ||
120 | 130 | ||
121 | /* If we're using a digital only path and have a previously | 131 | /* If we're using a digital only path and have a previously |
122 | * callibrated DC servo offset stored then use that. */ | 132 | * callibrated DC servo offset stored then use that. */ |
123 | if (hubs->class_w && hubs->class_w_dcs) { | 133 | if (hubs->class_w && hubs->class_w_dcs) { |
124 | dev_dbg(codec->dev, "Using cached DC servo offset %x\n", | 134 | dev_dbg(codec->dev, "Using cached DC servo offset %x\n", |
125 | hubs->class_w_dcs); | 135 | hubs->class_w_dcs); |
126 | snd_soc_write(codec, WM8993_DC_SERVO_3, hubs->class_w_dcs); | 136 | snd_soc_write(codec, dcs_reg, hubs->class_w_dcs); |
127 | wait_for_dc_servo(codec, | 137 | wait_for_dc_servo(codec, |
128 | WM8993_DCS_TRIG_DAC_WR_0 | | 138 | WM8993_DCS_TRIG_DAC_WR_0 | |
129 | WM8993_DCS_TRIG_DAC_WR_1); | 139 | WM8993_DCS_TRIG_DAC_WR_1); |
@@ -154,8 +164,9 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec) | |||
154 | reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2) | 164 | reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2) |
155 | & WM8993_DCS_INTEG_CHAN_1_MASK; | 165 | & WM8993_DCS_INTEG_CHAN_1_MASK; |
156 | break; | 166 | break; |
167 | case 2: | ||
157 | case 1: | 168 | case 1: |
158 | reg = snd_soc_read(codec, WM8993_DC_SERVO_3); | 169 | reg = snd_soc_read(codec, dcs_reg); |
159 | reg_r = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK) | 170 | reg_r = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK) |
160 | >> WM8993_DCS_DAC_WR_VAL_1_SHIFT; | 171 | >> WM8993_DCS_DAC_WR_VAL_1_SHIFT; |
161 | reg_l = reg & WM8993_DCS_DAC_WR_VAL_0_MASK; | 172 | reg_l = reg & WM8993_DCS_DAC_WR_VAL_0_MASK; |
@@ -168,24 +179,25 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec) | |||
168 | dev_dbg(codec->dev, "DCS input: %x %x\n", reg_l, reg_r); | 179 | dev_dbg(codec->dev, "DCS input: %x %x\n", reg_l, reg_r); |
169 | 180 | ||
170 | /* Apply correction to DC servo result */ | 181 | /* Apply correction to DC servo result */ |
171 | if (hubs->dcs_codes) { | 182 | if (hubs->dcs_codes_l || hubs->dcs_codes_r) { |
172 | dev_dbg(codec->dev, "Applying %d code DC servo correction\n", | 183 | dev_dbg(codec->dev, |
173 | hubs->dcs_codes); | 184 | "Applying %d/%d code DC servo correction\n", |
185 | hubs->dcs_codes_l, hubs->dcs_codes_r); | ||
174 | 186 | ||
175 | /* HPOUT1R */ | 187 | /* HPOUT1R */ |
176 | offset = reg_r; | 188 | offset = reg_r; |
177 | offset += hubs->dcs_codes; | 189 | offset += hubs->dcs_codes_r; |
178 | dcs_cfg = (u8)offset << WM8993_DCS_DAC_WR_VAL_1_SHIFT; | 190 | dcs_cfg = (u8)offset << WM8993_DCS_DAC_WR_VAL_1_SHIFT; |
179 | 191 | ||
180 | /* HPOUT1L */ | 192 | /* HPOUT1L */ |
181 | offset = reg_l; | 193 | offset = reg_l; |
182 | offset += hubs->dcs_codes; | 194 | offset += hubs->dcs_codes_l; |
183 | dcs_cfg |= (u8)offset; | 195 | dcs_cfg |= (u8)offset; |
184 | 196 | ||
185 | dev_dbg(codec->dev, "DCS result: %x\n", dcs_cfg); | 197 | dev_dbg(codec->dev, "DCS result: %x\n", dcs_cfg); |
186 | 198 | ||
187 | /* Do it */ | 199 | /* Do it */ |
188 | snd_soc_write(codec, WM8993_DC_SERVO_3, dcs_cfg); | 200 | snd_soc_write(codec, dcs_reg, dcs_cfg); |
189 | wait_for_dc_servo(codec, | 201 | wait_for_dc_servo(codec, |
190 | WM8993_DCS_TRIG_DAC_WR_0 | | 202 | WM8993_DCS_TRIG_DAC_WR_0 | |
191 | WM8993_DCS_TRIG_DAC_WR_1); | 203 | WM8993_DCS_TRIG_DAC_WR_1); |
@@ -217,7 +229,7 @@ static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol, | |||
217 | 229 | ||
218 | /* If we're applying an offset correction then updating the | 230 | /* If we're applying an offset correction then updating the |
219 | * callibration would be likely to introduce further offsets. */ | 231 | * callibration would be likely to introduce further offsets. */ |
220 | if (hubs->dcs_codes || hubs->no_series_update) | 232 | if (hubs->dcs_codes_l || hubs->dcs_codes_r || hubs->no_series_update) |
221 | return ret; | 233 | return ret; |
222 | 234 | ||
223 | /* Only need to do this if the outputs are active */ | 235 | /* Only need to do this if the outputs are active */ |
@@ -440,9 +452,8 @@ static int hp_event(struct snd_soc_dapm_widget *w, | |||
440 | reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY; | 452 | reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY; |
441 | snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg); | 453 | snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg); |
442 | 454 | ||
443 | /* Smallest supported update interval */ | ||
444 | snd_soc_update_bits(codec, WM8993_DC_SERVO_1, | 455 | snd_soc_update_bits(codec, WM8993_DC_SERVO_1, |
445 | WM8993_DCS_TIMER_PERIOD_01_MASK, 1); | 456 | WM8993_DCS_TIMER_PERIOD_01_MASK, 0); |
446 | 457 | ||
447 | calibrate_dc_servo(codec); | 458 | calibrate_dc_servo(codec); |
448 | 459 | ||
diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h index 676b1252ab91..c674c7a502a6 100644 --- a/sound/soc/codecs/wm_hubs.h +++ b/sound/soc/codecs/wm_hubs.h | |||
@@ -23,7 +23,8 @@ extern const unsigned int wm_hubs_spkmix_tlv[]; | |||
23 | 23 | ||
24 | /* This *must* be the first element of the codec->private_data struct */ | 24 | /* This *must* be the first element of the codec->private_data struct */ |
25 | struct wm_hubs_data { | 25 | struct wm_hubs_data { |
26 | int dcs_codes; | 26 | int dcs_codes_l; |
27 | int dcs_codes_r; | ||
27 | int dcs_readback_mode; | 28 | int dcs_readback_mode; |
28 | int hp_startup_mode; | 29 | int hp_startup_mode; |
29 | int series_startup; | 30 | int series_startup; |
diff --git a/sound/soc/mxs/Kconfig b/sound/soc/mxs/Kconfig new file mode 100644 index 000000000000..e4ba8d5f25fa --- /dev/null +++ b/sound/soc/mxs/Kconfig | |||
@@ -0,0 +1,20 @@ | |||
1 | menuconfig SND_MXS_SOC | ||
2 | tristate "SoC Audio for Freescale MXS CPUs" | ||
3 | depends on ARCH_MXS | ||
4 | select SND_PCM | ||
5 | help | ||
6 | Say Y or M if you want to add support for codecs attached to | ||
7 | the MXS SAIF interface. | ||
8 | |||
9 | |||
10 | if SND_MXS_SOC | ||
11 | |||
12 | config SND_SOC_MXS_SGTL5000 | ||
13 | tristate "SoC Audio support for i.MX boards with sgtl5000" | ||
14 | depends on I2C | ||
15 | select SND_SOC_SGTL5000 | ||
16 | help | ||
17 | Say Y if you want to add support for SoC audio on an MXS board with | ||
18 | a sgtl5000 codec. | ||
19 | |||
20 | endif # SND_MXS_SOC | ||
diff --git a/sound/soc/mxs/Makefile b/sound/soc/mxs/Makefile new file mode 100644 index 000000000000..565b5b51e8b7 --- /dev/null +++ b/sound/soc/mxs/Makefile | |||
@@ -0,0 +1,10 @@ | |||
1 | # MXS Platform Support | ||
2 | snd-soc-mxs-objs := mxs-saif.o | ||
3 | snd-soc-mxs-pcm-objs := mxs-pcm.o | ||
4 | |||
5 | obj-$(CONFIG_SND_MXS_SOC) += snd-soc-mxs.o snd-soc-mxs-pcm.o | ||
6 | |||
7 | # i.MX Machine Support | ||
8 | snd-soc-mxs-sgtl5000-objs := mxs-sgtl5000.o | ||
9 | |||
10 | obj-$(CONFIG_SND_SOC_MXS_SGTL5000) += snd-soc-mxs-sgtl5000.o | ||
diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c new file mode 100644 index 000000000000..dea5aa4aa647 --- /dev/null +++ b/sound/soc/mxs/mxs-pcm.c | |||
@@ -0,0 +1,359 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * Based on sound/soc/imx/imx-pcm-dma-mx2.c | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (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 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | #include <linux/clk.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/device.h> | ||
24 | #include <linux/dma-mapping.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/platform_device.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/dmaengine.h> | ||
31 | |||
32 | #include <sound/core.h> | ||
33 | #include <sound/initval.h> | ||
34 | #include <sound/pcm.h> | ||
35 | #include <sound/pcm_params.h> | ||
36 | #include <sound/soc.h> | ||
37 | |||
38 | #include <mach/dma.h> | ||
39 | #include "mxs-pcm.h" | ||
40 | |||
41 | static struct snd_pcm_hardware snd_mxs_hardware = { | ||
42 | .info = SNDRV_PCM_INFO_MMAP | | ||
43 | SNDRV_PCM_INFO_MMAP_VALID | | ||
44 | SNDRV_PCM_INFO_PAUSE | | ||
45 | SNDRV_PCM_INFO_RESUME | | ||
46 | SNDRV_PCM_INFO_INTERLEAVED, | ||
47 | .formats = SNDRV_PCM_FMTBIT_S16_LE | | ||
48 | SNDRV_PCM_FMTBIT_S20_3LE | | ||
49 | SNDRV_PCM_FMTBIT_S24_LE, | ||
50 | .channels_min = 2, | ||
51 | .channels_max = 2, | ||
52 | .period_bytes_min = 32, | ||
53 | .period_bytes_max = 8192, | ||
54 | .periods_min = 1, | ||
55 | .periods_max = 52, | ||
56 | .buffer_bytes_max = 64 * 1024, | ||
57 | .fifo_size = 32, | ||
58 | |||
59 | }; | ||
60 | |||
61 | static void audio_dma_irq(void *data) | ||
62 | { | ||
63 | struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data; | ||
64 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
65 | struct mxs_pcm_runtime_data *iprtd = runtime->private_data; | ||
66 | |||
67 | iprtd->offset += iprtd->period_bytes; | ||
68 | iprtd->offset %= iprtd->period_bytes * iprtd->periods; | ||
69 | snd_pcm_period_elapsed(substream); | ||
70 | } | ||
71 | |||
72 | static bool filter(struct dma_chan *chan, void *param) | ||
73 | { | ||
74 | struct mxs_pcm_runtime_data *iprtd = param; | ||
75 | struct mxs_pcm_dma_params *dma_params = iprtd->dma_params; | ||
76 | |||
77 | if (!mxs_dma_is_apbx(chan)) | ||
78 | return false; | ||
79 | |||
80 | if (chan->chan_id != dma_params->chan_num) | ||
81 | return false; | ||
82 | |||
83 | chan->private = &iprtd->dma_data; | ||
84 | |||
85 | return true; | ||
86 | } | ||
87 | |||
88 | static int mxs_dma_alloc(struct snd_pcm_substream *substream, | ||
89 | struct snd_pcm_hw_params *params) | ||
90 | { | ||
91 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
92 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
93 | struct mxs_pcm_runtime_data *iprtd = runtime->private_data; | ||
94 | dma_cap_mask_t mask; | ||
95 | |||
96 | iprtd->dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | ||
97 | |||
98 | dma_cap_zero(mask); | ||
99 | dma_cap_set(DMA_SLAVE, mask); | ||
100 | iprtd->dma_data.chan_irq = iprtd->dma_params->chan_irq; | ||
101 | iprtd->dma_chan = dma_request_channel(mask, filter, iprtd); | ||
102 | if (!iprtd->dma_chan) | ||
103 | return -EINVAL; | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static int snd_mxs_pcm_hw_params(struct snd_pcm_substream *substream, | ||
109 | struct snd_pcm_hw_params *params) | ||
110 | { | ||
111 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
112 | struct mxs_pcm_runtime_data *iprtd = runtime->private_data; | ||
113 | unsigned long dma_addr; | ||
114 | struct dma_chan *chan; | ||
115 | int ret; | ||
116 | |||
117 | ret = mxs_dma_alloc(substream, params); | ||
118 | if (ret) | ||
119 | return ret; | ||
120 | chan = iprtd->dma_chan; | ||
121 | |||
122 | iprtd->size = params_buffer_bytes(params); | ||
123 | iprtd->periods = params_periods(params); | ||
124 | iprtd->period_bytes = params_period_bytes(params); | ||
125 | iprtd->offset = 0; | ||
126 | iprtd->period_time = HZ / (params_rate(params) / | ||
127 | params_period_size(params)); | ||
128 | |||
129 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | ||
130 | |||
131 | dma_addr = runtime->dma_addr; | ||
132 | |||
133 | iprtd->buf = substream->dma_buffer.area; | ||
134 | |||
135 | iprtd->desc = chan->device->device_prep_dma_cyclic(chan, dma_addr, | ||
136 | iprtd->period_bytes * iprtd->periods, | ||
137 | iprtd->period_bytes, | ||
138 | substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? | ||
139 | DMA_TO_DEVICE : DMA_FROM_DEVICE); | ||
140 | if (!iprtd->desc) { | ||
141 | dev_err(&chan->dev->device, "cannot prepare slave dma\n"); | ||
142 | return -EINVAL; | ||
143 | } | ||
144 | |||
145 | iprtd->desc->callback = audio_dma_irq; | ||
146 | iprtd->desc->callback_param = substream; | ||
147 | |||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | static int snd_mxs_pcm_hw_free(struct snd_pcm_substream *substream) | ||
152 | { | ||
153 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
154 | struct mxs_pcm_runtime_data *iprtd = runtime->private_data; | ||
155 | |||
156 | if (iprtd->dma_chan) { | ||
157 | dma_release_channel(iprtd->dma_chan); | ||
158 | iprtd->dma_chan = NULL; | ||
159 | } | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | static int snd_mxs_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | ||
165 | { | ||
166 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
167 | struct mxs_pcm_runtime_data *iprtd = runtime->private_data; | ||
168 | |||
169 | switch (cmd) { | ||
170 | case SNDRV_PCM_TRIGGER_START: | ||
171 | case SNDRV_PCM_TRIGGER_RESUME: | ||
172 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
173 | dmaengine_submit(iprtd->desc); | ||
174 | |||
175 | break; | ||
176 | case SNDRV_PCM_TRIGGER_STOP: | ||
177 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
178 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
179 | dmaengine_terminate_all(iprtd->dma_chan); | ||
180 | |||
181 | break; | ||
182 | default: | ||
183 | return -EINVAL; | ||
184 | } | ||
185 | |||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | static snd_pcm_uframes_t snd_mxs_pcm_pointer( | ||
190 | struct snd_pcm_substream *substream) | ||
191 | { | ||
192 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
193 | struct mxs_pcm_runtime_data *iprtd = runtime->private_data; | ||
194 | |||
195 | return bytes_to_frames(substream->runtime, iprtd->offset); | ||
196 | } | ||
197 | |||
198 | static int snd_mxs_open(struct snd_pcm_substream *substream) | ||
199 | { | ||
200 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
201 | struct mxs_pcm_runtime_data *iprtd; | ||
202 | int ret; | ||
203 | |||
204 | iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL); | ||
205 | if (iprtd == NULL) | ||
206 | return -ENOMEM; | ||
207 | runtime->private_data = iprtd; | ||
208 | |||
209 | ret = snd_pcm_hw_constraint_integer(substream->runtime, | ||
210 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
211 | if (ret < 0) { | ||
212 | kfree(iprtd); | ||
213 | return ret; | ||
214 | } | ||
215 | |||
216 | snd_soc_set_runtime_hwparams(substream, &snd_mxs_hardware); | ||
217 | |||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | static int snd_mxs_close(struct snd_pcm_substream *substream) | ||
222 | { | ||
223 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
224 | struct mxs_pcm_runtime_data *iprtd = runtime->private_data; | ||
225 | |||
226 | kfree(iprtd); | ||
227 | |||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | static int snd_mxs_pcm_mmap(struct snd_pcm_substream *substream, | ||
232 | struct vm_area_struct *vma) | ||
233 | { | ||
234 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
235 | |||
236 | return dma_mmap_writecombine(substream->pcm->card->dev, vma, | ||
237 | runtime->dma_area, | ||
238 | runtime->dma_addr, | ||
239 | runtime->dma_bytes); | ||
240 | } | ||
241 | |||
242 | static struct snd_pcm_ops mxs_pcm_ops = { | ||
243 | .open = snd_mxs_open, | ||
244 | .close = snd_mxs_close, | ||
245 | .ioctl = snd_pcm_lib_ioctl, | ||
246 | .hw_params = snd_mxs_pcm_hw_params, | ||
247 | .hw_free = snd_mxs_pcm_hw_free, | ||
248 | .trigger = snd_mxs_pcm_trigger, | ||
249 | .pointer = snd_mxs_pcm_pointer, | ||
250 | .mmap = snd_mxs_pcm_mmap, | ||
251 | }; | ||
252 | |||
253 | static int mxs_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) | ||
254 | { | ||
255 | struct snd_pcm_substream *substream = pcm->streams[stream].substream; | ||
256 | struct snd_dma_buffer *buf = &substream->dma_buffer; | ||
257 | size_t size = snd_mxs_hardware.buffer_bytes_max; | ||
258 | |||
259 | buf->dev.type = SNDRV_DMA_TYPE_DEV; | ||
260 | buf->dev.dev = pcm->card->dev; | ||
261 | buf->private_data = NULL; | ||
262 | buf->area = dma_alloc_writecombine(pcm->card->dev, size, | ||
263 | &buf->addr, GFP_KERNEL); | ||
264 | if (!buf->area) | ||
265 | return -ENOMEM; | ||
266 | buf->bytes = size; | ||
267 | |||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | static u64 mxs_pcm_dmamask = DMA_BIT_MASK(32); | ||
272 | static int mxs_pcm_new(struct snd_soc_pcm_runtime *rtd) | ||
273 | { | ||
274 | struct snd_card *card = rtd->card->snd_card; | ||
275 | struct snd_pcm *pcm = rtd->pcm; | ||
276 | int ret = 0; | ||
277 | |||
278 | if (!card->dev->dma_mask) | ||
279 | card->dev->dma_mask = &mxs_pcm_dmamask; | ||
280 | if (!card->dev->coherent_dma_mask) | ||
281 | card->dev->coherent_dma_mask = DMA_BIT_MASK(32); | ||
282 | |||
283 | if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { | ||
284 | ret = mxs_pcm_preallocate_dma_buffer(pcm, | ||
285 | SNDRV_PCM_STREAM_PLAYBACK); | ||
286 | if (ret) | ||
287 | goto out; | ||
288 | } | ||
289 | |||
290 | if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { | ||
291 | ret = mxs_pcm_preallocate_dma_buffer(pcm, | ||
292 | SNDRV_PCM_STREAM_CAPTURE); | ||
293 | if (ret) | ||
294 | goto out; | ||
295 | } | ||
296 | |||
297 | out: | ||
298 | return ret; | ||
299 | } | ||
300 | |||
301 | static void mxs_pcm_free(struct snd_pcm *pcm) | ||
302 | { | ||
303 | struct snd_pcm_substream *substream; | ||
304 | struct snd_dma_buffer *buf; | ||
305 | int stream; | ||
306 | |||
307 | for (stream = 0; stream < 2; stream++) { | ||
308 | substream = pcm->streams[stream].substream; | ||
309 | if (!substream) | ||
310 | continue; | ||
311 | |||
312 | buf = &substream->dma_buffer; | ||
313 | if (!buf->area) | ||
314 | continue; | ||
315 | |||
316 | dma_free_writecombine(pcm->card->dev, buf->bytes, | ||
317 | buf->area, buf->addr); | ||
318 | buf->area = NULL; | ||
319 | } | ||
320 | } | ||
321 | |||
322 | static struct snd_soc_platform_driver mxs_soc_platform = { | ||
323 | .ops = &mxs_pcm_ops, | ||
324 | .pcm_new = mxs_pcm_new, | ||
325 | .pcm_free = mxs_pcm_free, | ||
326 | }; | ||
327 | |||
328 | static int __devinit mxs_soc_platform_probe(struct platform_device *pdev) | ||
329 | { | ||
330 | return snd_soc_register_platform(&pdev->dev, &mxs_soc_platform); | ||
331 | } | ||
332 | |||
333 | static int __devexit mxs_soc_platform_remove(struct platform_device *pdev) | ||
334 | { | ||
335 | snd_soc_unregister_platform(&pdev->dev); | ||
336 | |||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | static struct platform_driver mxs_pcm_driver = { | ||
341 | .driver = { | ||
342 | .name = "mxs-pcm-audio", | ||
343 | .owner = THIS_MODULE, | ||
344 | }, | ||
345 | .probe = mxs_soc_platform_probe, | ||
346 | .remove = __devexit_p(mxs_soc_platform_remove), | ||
347 | }; | ||
348 | |||
349 | static int __init snd_mxs_pcm_init(void) | ||
350 | { | ||
351 | return platform_driver_register(&mxs_pcm_driver); | ||
352 | } | ||
353 | module_init(snd_mxs_pcm_init); | ||
354 | |||
355 | static void __exit snd_mxs_pcm_exit(void) | ||
356 | { | ||
357 | platform_driver_unregister(&mxs_pcm_driver); | ||
358 | } | ||
359 | module_exit(snd_mxs_pcm_exit); | ||
diff --git a/sound/soc/mxs/mxs-pcm.h b/sound/soc/mxs/mxs-pcm.h new file mode 100644 index 000000000000..f55ac4f7a76a --- /dev/null +++ b/sound/soc/mxs/mxs-pcm.h | |||
@@ -0,0 +1,43 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along | ||
15 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
17 | */ | ||
18 | |||
19 | #ifndef _MXS_PCM_H | ||
20 | #define _MXS_PCM_H | ||
21 | |||
22 | #include <mach/dma.h> | ||
23 | |||
24 | struct mxs_pcm_dma_params { | ||
25 | int chan_irq; | ||
26 | int chan_num; | ||
27 | }; | ||
28 | |||
29 | struct mxs_pcm_runtime_data { | ||
30 | int period_bytes; | ||
31 | int periods; | ||
32 | int dma; | ||
33 | unsigned long offset; | ||
34 | unsigned long size; | ||
35 | void *buf; | ||
36 | int period_time; | ||
37 | struct dma_async_tx_descriptor *desc; | ||
38 | struct dma_chan *dma_chan; | ||
39 | struct mxs_dma_data dma_data; | ||
40 | struct mxs_pcm_dma_params *dma_params; | ||
41 | }; | ||
42 | |||
43 | #endif | ||
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c new file mode 100644 index 000000000000..0b3adaec9f4c --- /dev/null +++ b/sound/soc/mxs/mxs-saif.c | |||
@@ -0,0 +1,677 @@ | |||
1 | /* | ||
2 | * Copyright 2011 Freescale Semiconductor, Inc. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along | ||
15 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
17 | */ | ||
18 | |||
19 | #include <linux/module.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/dma-mapping.h> | ||
24 | #include <linux/clk.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <sound/core.h> | ||
27 | #include <sound/pcm.h> | ||
28 | #include <sound/pcm_params.h> | ||
29 | #include <sound/soc.h> | ||
30 | #include <mach/dma.h> | ||
31 | #include <asm/mach-types.h> | ||
32 | #include <mach/hardware.h> | ||
33 | #include <mach/mxs.h> | ||
34 | |||
35 | #include "mxs-saif.h" | ||
36 | |||
37 | static struct mxs_saif *mxs_saif[2]; | ||
38 | |||
39 | static int mxs_saif_set_dai_sysclk(struct snd_soc_dai *cpu_dai, | ||
40 | int clk_id, unsigned int freq, int dir) | ||
41 | { | ||
42 | struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); | ||
43 | |||
44 | switch (clk_id) { | ||
45 | case MXS_SAIF_MCLK: | ||
46 | saif->mclk = freq; | ||
47 | break; | ||
48 | default: | ||
49 | return -EINVAL; | ||
50 | } | ||
51 | return 0; | ||
52 | } | ||
53 | |||
54 | /* | ||
55 | * Set SAIF clock and MCLK | ||
56 | */ | ||
57 | static int mxs_saif_set_clk(struct mxs_saif *saif, | ||
58 | unsigned int mclk, | ||
59 | unsigned int rate) | ||
60 | { | ||
61 | u32 scr; | ||
62 | int ret; | ||
63 | |||
64 | scr = __raw_readl(saif->base + SAIF_CTRL); | ||
65 | scr &= ~BM_SAIF_CTRL_BITCLK_MULT_RATE; | ||
66 | scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; | ||
67 | |||
68 | /* | ||
69 | * Set SAIF clock | ||
70 | * | ||
71 | * The SAIF clock should be either 384*fs or 512*fs. | ||
72 | * If MCLK is used, the SAIF clk ratio need to match mclk ratio. | ||
73 | * For 32x mclk, set saif clk as 512*fs. | ||
74 | * For 48x mclk, set saif clk as 384*fs. | ||
75 | * | ||
76 | * If MCLK is not used, we just set saif clk to 512*fs. | ||
77 | */ | ||
78 | if (saif->mclk_in_use) { | ||
79 | if (mclk % 32 == 0) { | ||
80 | scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; | ||
81 | ret = clk_set_rate(saif->clk, 512 * rate); | ||
82 | } else if (mclk % 48 == 0) { | ||
83 | scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE; | ||
84 | ret = clk_set_rate(saif->clk, 384 * rate); | ||
85 | } else { | ||
86 | /* SAIF MCLK should be either 32x or 48x */ | ||
87 | return -EINVAL; | ||
88 | } | ||
89 | } else { | ||
90 | ret = clk_set_rate(saif->clk, 512 * rate); | ||
91 | scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; | ||
92 | } | ||
93 | |||
94 | if (ret) | ||
95 | return ret; | ||
96 | |||
97 | if (!saif->mclk_in_use) { | ||
98 | __raw_writel(scr, saif->base + SAIF_CTRL); | ||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | /* | ||
103 | * Program the over-sample rate for MCLK output | ||
104 | * | ||
105 | * The available MCLK range is 32x, 48x... 512x. The rate | ||
106 | * could be from 8kHz to 192kH. | ||
107 | */ | ||
108 | switch (mclk / rate) { | ||
109 | case 32: | ||
110 | scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(4); | ||
111 | break; | ||
112 | case 64: | ||
113 | scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(3); | ||
114 | break; | ||
115 | case 128: | ||
116 | scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(2); | ||
117 | break; | ||
118 | case 256: | ||
119 | scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(1); | ||
120 | break; | ||
121 | case 512: | ||
122 | scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(0); | ||
123 | break; | ||
124 | case 48: | ||
125 | scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(3); | ||
126 | break; | ||
127 | case 96: | ||
128 | scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(2); | ||
129 | break; | ||
130 | case 192: | ||
131 | scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(1); | ||
132 | break; | ||
133 | case 384: | ||
134 | scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(0); | ||
135 | break; | ||
136 | default: | ||
137 | return -EINVAL; | ||
138 | } | ||
139 | |||
140 | __raw_writel(scr, saif->base + SAIF_CTRL); | ||
141 | |||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | /* | ||
146 | * Put and disable MCLK. | ||
147 | */ | ||
148 | int mxs_saif_put_mclk(unsigned int saif_id) | ||
149 | { | ||
150 | struct mxs_saif *saif = mxs_saif[saif_id]; | ||
151 | u32 stat; | ||
152 | |||
153 | if (!saif) | ||
154 | return -EINVAL; | ||
155 | |||
156 | stat = __raw_readl(saif->base + SAIF_STAT); | ||
157 | if (stat & BM_SAIF_STAT_BUSY) { | ||
158 | dev_err(saif->dev, "error: busy\n"); | ||
159 | return -EBUSY; | ||
160 | } | ||
161 | |||
162 | clk_disable(saif->clk); | ||
163 | |||
164 | /* disable MCLK output */ | ||
165 | __raw_writel(BM_SAIF_CTRL_CLKGATE, | ||
166 | saif->base + SAIF_CTRL + MXS_SET_ADDR); | ||
167 | __raw_writel(BM_SAIF_CTRL_RUN, | ||
168 | saif->base + SAIF_CTRL + MXS_CLR_ADDR); | ||
169 | |||
170 | saif->mclk_in_use = 0; | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | /* | ||
175 | * Get MCLK and set clock rate, then enable it | ||
176 | * | ||
177 | * This interface is used for codecs who are using MCLK provided | ||
178 | * by saif. | ||
179 | */ | ||
180 | int mxs_saif_get_mclk(unsigned int saif_id, unsigned int mclk, | ||
181 | unsigned int rate) | ||
182 | { | ||
183 | struct mxs_saif *saif = mxs_saif[saif_id]; | ||
184 | u32 stat; | ||
185 | int ret; | ||
186 | |||
187 | if (!saif) | ||
188 | return -EINVAL; | ||
189 | |||
190 | stat = __raw_readl(saif->base + SAIF_STAT); | ||
191 | if (stat & BM_SAIF_STAT_BUSY) { | ||
192 | dev_err(saif->dev, "error: busy\n"); | ||
193 | return -EBUSY; | ||
194 | } | ||
195 | |||
196 | /* Clear Reset */ | ||
197 | __raw_writel(BM_SAIF_CTRL_SFTRST, | ||
198 | saif->base + SAIF_CTRL + MXS_CLR_ADDR); | ||
199 | |||
200 | saif->mclk_in_use = 1; | ||
201 | ret = mxs_saif_set_clk(saif, mclk, rate); | ||
202 | if (ret) | ||
203 | return ret; | ||
204 | |||
205 | ret = clk_enable(saif->clk); | ||
206 | if (ret) | ||
207 | return ret; | ||
208 | |||
209 | /* enable MCLK output */ | ||
210 | __raw_writel(BM_SAIF_CTRL_CLKGATE, | ||
211 | saif->base + SAIF_CTRL + MXS_CLR_ADDR); | ||
212 | __raw_writel(BM_SAIF_CTRL_RUN, | ||
213 | saif->base + SAIF_CTRL + MXS_SET_ADDR); | ||
214 | |||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | /* | ||
219 | * SAIF DAI format configuration. | ||
220 | * Should only be called when port is inactive. | ||
221 | */ | ||
222 | static int mxs_saif_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) | ||
223 | { | ||
224 | u32 scr, stat; | ||
225 | u32 scr0; | ||
226 | struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); | ||
227 | |||
228 | stat = __raw_readl(saif->base + SAIF_STAT); | ||
229 | if (stat & BM_SAIF_STAT_BUSY) { | ||
230 | dev_err(cpu_dai->dev, "error: busy\n"); | ||
231 | return -EBUSY; | ||
232 | } | ||
233 | |||
234 | scr0 = __raw_readl(saif->base + SAIF_CTRL); | ||
235 | scr0 = scr0 & ~BM_SAIF_CTRL_BITCLK_EDGE & ~BM_SAIF_CTRL_LRCLK_POLARITY \ | ||
236 | & ~BM_SAIF_CTRL_JUSTIFY & ~BM_SAIF_CTRL_DELAY; | ||
237 | scr = 0; | ||
238 | |||
239 | /* DAI mode */ | ||
240 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
241 | case SND_SOC_DAIFMT_I2S: | ||
242 | /* data frame low 1clk before data */ | ||
243 | scr |= BM_SAIF_CTRL_DELAY; | ||
244 | scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY; | ||
245 | break; | ||
246 | case SND_SOC_DAIFMT_LEFT_J: | ||
247 | /* data frame high with data */ | ||
248 | scr &= ~BM_SAIF_CTRL_DELAY; | ||
249 | scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY; | ||
250 | scr &= ~BM_SAIF_CTRL_JUSTIFY; | ||
251 | break; | ||
252 | default: | ||
253 | return -EINVAL; | ||
254 | } | ||
255 | |||
256 | /* DAI clock inversion */ | ||
257 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
258 | case SND_SOC_DAIFMT_IB_IF: | ||
259 | scr |= BM_SAIF_CTRL_BITCLK_EDGE; | ||
260 | scr |= BM_SAIF_CTRL_LRCLK_POLARITY; | ||
261 | break; | ||
262 | case SND_SOC_DAIFMT_IB_NF: | ||
263 | scr |= BM_SAIF_CTRL_BITCLK_EDGE; | ||
264 | scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY; | ||
265 | break; | ||
266 | case SND_SOC_DAIFMT_NB_IF: | ||
267 | scr &= ~BM_SAIF_CTRL_BITCLK_EDGE; | ||
268 | scr |= BM_SAIF_CTRL_LRCLK_POLARITY; | ||
269 | break; | ||
270 | case SND_SOC_DAIFMT_NB_NF: | ||
271 | scr &= ~BM_SAIF_CTRL_BITCLK_EDGE; | ||
272 | scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY; | ||
273 | break; | ||
274 | } | ||
275 | |||
276 | /* | ||
277 | * Note: We simply just support master mode since SAIF TX can only | ||
278 | * work as master. | ||
279 | */ | ||
280 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
281 | case SND_SOC_DAIFMT_CBS_CFS: | ||
282 | scr &= ~BM_SAIF_CTRL_SLAVE_MODE; | ||
283 | __raw_writel(scr | scr0, saif->base + SAIF_CTRL); | ||
284 | break; | ||
285 | default: | ||
286 | return -EINVAL; | ||
287 | } | ||
288 | |||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | static int mxs_saif_startup(struct snd_pcm_substream *substream, | ||
293 | struct snd_soc_dai *cpu_dai) | ||
294 | { | ||
295 | struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); | ||
296 | snd_soc_dai_set_dma_data(cpu_dai, substream, &saif->dma_param); | ||
297 | |||
298 | /* clear error status to 0 for each re-open */ | ||
299 | saif->fifo_underrun = 0; | ||
300 | saif->fifo_overrun = 0; | ||
301 | |||
302 | /* Clear Reset for normal operations */ | ||
303 | __raw_writel(BM_SAIF_CTRL_SFTRST, | ||
304 | saif->base + SAIF_CTRL + MXS_CLR_ADDR); | ||
305 | |||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | /* | ||
310 | * Should only be called when port is inactive. | ||
311 | * although can be called multiple times by upper layers. | ||
312 | */ | ||
313 | static int mxs_saif_hw_params(struct snd_pcm_substream *substream, | ||
314 | struct snd_pcm_hw_params *params, | ||
315 | struct snd_soc_dai *cpu_dai) | ||
316 | { | ||
317 | struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); | ||
318 | u32 scr, stat; | ||
319 | int ret; | ||
320 | |||
321 | /* mclk should already be set */ | ||
322 | if (!saif->mclk && saif->mclk_in_use) { | ||
323 | dev_err(cpu_dai->dev, "set mclk first\n"); | ||
324 | return -EINVAL; | ||
325 | } | ||
326 | |||
327 | stat = __raw_readl(saif->base + SAIF_STAT); | ||
328 | if (stat & BM_SAIF_STAT_BUSY) { | ||
329 | dev_err(cpu_dai->dev, "error: busy\n"); | ||
330 | return -EBUSY; | ||
331 | } | ||
332 | |||
333 | /* | ||
334 | * Set saif clk based on sample rate. | ||
335 | * If mclk is used, we also set mclk, if not, saif->mclk is | ||
336 | * default 0, means not used. | ||
337 | */ | ||
338 | ret = mxs_saif_set_clk(saif, saif->mclk, params_rate(params)); | ||
339 | if (ret) { | ||
340 | dev_err(cpu_dai->dev, "unable to get proper clk\n"); | ||
341 | return ret; | ||
342 | } | ||
343 | |||
344 | scr = __raw_readl(saif->base + SAIF_CTRL); | ||
345 | |||
346 | scr &= ~BM_SAIF_CTRL_WORD_LENGTH; | ||
347 | scr &= ~BM_SAIF_CTRL_BITCLK_48XFS_ENABLE; | ||
348 | switch (params_format(params)) { | ||
349 | case SNDRV_PCM_FORMAT_S16_LE: | ||
350 | scr |= BF_SAIF_CTRL_WORD_LENGTH(0); | ||
351 | break; | ||
352 | case SNDRV_PCM_FORMAT_S20_3LE: | ||
353 | scr |= BF_SAIF_CTRL_WORD_LENGTH(4); | ||
354 | scr |= BM_SAIF_CTRL_BITCLK_48XFS_ENABLE; | ||
355 | break; | ||
356 | case SNDRV_PCM_FORMAT_S24_LE: | ||
357 | scr |= BF_SAIF_CTRL_WORD_LENGTH(8); | ||
358 | scr |= BM_SAIF_CTRL_BITCLK_48XFS_ENABLE; | ||
359 | break; | ||
360 | default: | ||
361 | return -EINVAL; | ||
362 | } | ||
363 | |||
364 | /* Tx/Rx config */ | ||
365 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
366 | /* enable TX mode */ | ||
367 | scr &= ~BM_SAIF_CTRL_READ_MODE; | ||
368 | } else { | ||
369 | /* enable RX mode */ | ||
370 | scr |= BM_SAIF_CTRL_READ_MODE; | ||
371 | } | ||
372 | |||
373 | __raw_writel(scr, saif->base + SAIF_CTRL); | ||
374 | return 0; | ||
375 | } | ||
376 | |||
377 | static int mxs_saif_prepare(struct snd_pcm_substream *substream, | ||
378 | struct snd_soc_dai *cpu_dai) | ||
379 | { | ||
380 | struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); | ||
381 | |||
382 | /* clear clock gate */ | ||
383 | __raw_writel(BM_SAIF_CTRL_CLKGATE, | ||
384 | saif->base + SAIF_CTRL + MXS_CLR_ADDR); | ||
385 | |||
386 | /* enable FIFO error irqs */ | ||
387 | __raw_writel(BM_SAIF_CTRL_FIFO_ERROR_IRQ_EN, | ||
388 | saif->base + SAIF_CTRL + MXS_SET_ADDR); | ||
389 | |||
390 | return 0; | ||
391 | } | ||
392 | |||
393 | static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd, | ||
394 | struct snd_soc_dai *cpu_dai) | ||
395 | { | ||
396 | struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); | ||
397 | |||
398 | switch (cmd) { | ||
399 | case SNDRV_PCM_TRIGGER_START: | ||
400 | case SNDRV_PCM_TRIGGER_RESUME: | ||
401 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
402 | dev_dbg(cpu_dai->dev, "start\n"); | ||
403 | |||
404 | clk_enable(saif->clk); | ||
405 | if (!saif->mclk_in_use) | ||
406 | __raw_writel(BM_SAIF_CTRL_RUN, | ||
407 | saif->base + SAIF_CTRL + MXS_SET_ADDR); | ||
408 | |||
409 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
410 | /* | ||
411 | * write a data to saif data register to trigger | ||
412 | * the transfer | ||
413 | */ | ||
414 | __raw_writel(0, saif->base + SAIF_DATA); | ||
415 | } else { | ||
416 | /* | ||
417 | * read a data from saif data register to trigger | ||
418 | * the receive | ||
419 | */ | ||
420 | __raw_readl(saif->base + SAIF_DATA); | ||
421 | } | ||
422 | |||
423 | dev_dbg(cpu_dai->dev, "CTRL 0x%x STAT 0x%x\n", | ||
424 | __raw_readl(saif->base + SAIF_CTRL), | ||
425 | __raw_readl(saif->base + SAIF_STAT)); | ||
426 | |||
427 | break; | ||
428 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
429 | case SNDRV_PCM_TRIGGER_STOP: | ||
430 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
431 | dev_dbg(cpu_dai->dev, "stop\n"); | ||
432 | |||
433 | clk_disable(saif->clk); | ||
434 | if (!saif->mclk_in_use) | ||
435 | __raw_writel(BM_SAIF_CTRL_RUN, | ||
436 | saif->base + SAIF_CTRL + MXS_CLR_ADDR); | ||
437 | |||
438 | break; | ||
439 | default: | ||
440 | return -EINVAL; | ||
441 | } | ||
442 | |||
443 | return 0; | ||
444 | } | ||
445 | |||
446 | #define MXS_SAIF_RATES SNDRV_PCM_RATE_8000_192000 | ||
447 | #define MXS_SAIF_FORMATS \ | ||
448 | (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ | ||
449 | SNDRV_PCM_FMTBIT_S24_LE) | ||
450 | |||
451 | static struct snd_soc_dai_ops mxs_saif_dai_ops = { | ||
452 | .startup = mxs_saif_startup, | ||
453 | .trigger = mxs_saif_trigger, | ||
454 | .prepare = mxs_saif_prepare, | ||
455 | .hw_params = mxs_saif_hw_params, | ||
456 | .set_sysclk = mxs_saif_set_dai_sysclk, | ||
457 | .set_fmt = mxs_saif_set_dai_fmt, | ||
458 | }; | ||
459 | |||
460 | static int mxs_saif_dai_probe(struct snd_soc_dai *dai) | ||
461 | { | ||
462 | struct mxs_saif *saif = dev_get_drvdata(dai->dev); | ||
463 | |||
464 | snd_soc_dai_set_drvdata(dai, saif); | ||
465 | |||
466 | return 0; | ||
467 | } | ||
468 | |||
469 | static struct snd_soc_dai_driver mxs_saif_dai = { | ||
470 | .name = "mxs-saif", | ||
471 | .probe = mxs_saif_dai_probe, | ||
472 | .playback = { | ||
473 | .channels_min = 2, | ||
474 | .channels_max = 2, | ||
475 | .rates = MXS_SAIF_RATES, | ||
476 | .formats = MXS_SAIF_FORMATS, | ||
477 | }, | ||
478 | .capture = { | ||
479 | .channels_min = 2, | ||
480 | .channels_max = 2, | ||
481 | .rates = MXS_SAIF_RATES, | ||
482 | .formats = MXS_SAIF_FORMATS, | ||
483 | }, | ||
484 | .ops = &mxs_saif_dai_ops, | ||
485 | }; | ||
486 | |||
487 | static irqreturn_t mxs_saif_irq(int irq, void *dev_id) | ||
488 | { | ||
489 | struct mxs_saif *saif = dev_id; | ||
490 | unsigned int stat; | ||
491 | |||
492 | stat = __raw_readl(saif->base + SAIF_STAT); | ||
493 | if (!(stat & (BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ | | ||
494 | BM_SAIF_STAT_FIFO_OVERFLOW_IRQ))) | ||
495 | return IRQ_NONE; | ||
496 | |||
497 | if (stat & BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ) { | ||
498 | dev_dbg(saif->dev, "underrun!!! %d\n", ++saif->fifo_underrun); | ||
499 | __raw_writel(BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ, | ||
500 | saif->base + SAIF_STAT + MXS_CLR_ADDR); | ||
501 | } | ||
502 | |||
503 | if (stat & BM_SAIF_STAT_FIFO_OVERFLOW_IRQ) { | ||
504 | dev_dbg(saif->dev, "overrun!!! %d\n", ++saif->fifo_overrun); | ||
505 | __raw_writel(BM_SAIF_STAT_FIFO_OVERFLOW_IRQ, | ||
506 | saif->base + SAIF_STAT + MXS_CLR_ADDR); | ||
507 | } | ||
508 | |||
509 | dev_dbg(saif->dev, "SAIF_CTRL %x SAIF_STAT %x\n", | ||
510 | __raw_readl(saif->base + SAIF_CTRL), | ||
511 | __raw_readl(saif->base + SAIF_STAT)); | ||
512 | |||
513 | return IRQ_HANDLED; | ||
514 | } | ||
515 | |||
516 | static int mxs_saif_probe(struct platform_device *pdev) | ||
517 | { | ||
518 | struct resource *res; | ||
519 | struct mxs_saif *saif; | ||
520 | int ret = 0; | ||
521 | |||
522 | saif = kzalloc(sizeof(*saif), GFP_KERNEL); | ||
523 | if (!saif) | ||
524 | return -ENOMEM; | ||
525 | |||
526 | if (pdev->id >= ARRAY_SIZE(mxs_saif)) | ||
527 | return -EINVAL; | ||
528 | mxs_saif[pdev->id] = saif; | ||
529 | |||
530 | saif->clk = clk_get(&pdev->dev, NULL); | ||
531 | if (IS_ERR(saif->clk)) { | ||
532 | ret = PTR_ERR(saif->clk); | ||
533 | dev_err(&pdev->dev, "Cannot get the clock: %d\n", | ||
534 | ret); | ||
535 | goto failed_clk; | ||
536 | } | ||
537 | |||
538 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
539 | if (!res) { | ||
540 | ret = -ENODEV; | ||
541 | dev_err(&pdev->dev, "failed to get io resource: %d\n", | ||
542 | ret); | ||
543 | goto failed_get_resource; | ||
544 | } | ||
545 | |||
546 | if (!request_mem_region(res->start, resource_size(res), "mxs-saif")) { | ||
547 | dev_err(&pdev->dev, "request_mem_region failed\n"); | ||
548 | ret = -EBUSY; | ||
549 | goto failed_get_resource; | ||
550 | } | ||
551 | |||
552 | saif->base = ioremap(res->start, resource_size(res)); | ||
553 | if (!saif->base) { | ||
554 | dev_err(&pdev->dev, "ioremap failed\n"); | ||
555 | ret = -ENODEV; | ||
556 | goto failed_ioremap; | ||
557 | } | ||
558 | |||
559 | res = platform_get_resource(pdev, IORESOURCE_DMA, 0); | ||
560 | if (!res) { | ||
561 | ret = -ENODEV; | ||
562 | dev_err(&pdev->dev, "failed to get dma resource: %d\n", | ||
563 | ret); | ||
564 | goto failed_ioremap; | ||
565 | } | ||
566 | saif->dma_param.chan_num = res->start; | ||
567 | |||
568 | saif->irq = platform_get_irq(pdev, 0); | ||
569 | if (saif->irq < 0) { | ||
570 | ret = saif->irq; | ||
571 | dev_err(&pdev->dev, "failed to get irq resource: %d\n", | ||
572 | ret); | ||
573 | goto failed_get_irq1; | ||
574 | } | ||
575 | |||
576 | saif->dev = &pdev->dev; | ||
577 | ret = request_irq(saif->irq, mxs_saif_irq, 0, "mxs-saif", saif); | ||
578 | if (ret) { | ||
579 | dev_err(&pdev->dev, "failed to request irq\n"); | ||
580 | goto failed_get_irq1; | ||
581 | } | ||
582 | |||
583 | saif->dma_param.chan_irq = platform_get_irq(pdev, 1); | ||
584 | if (saif->dma_param.chan_irq < 0) { | ||
585 | ret = saif->dma_param.chan_irq; | ||
586 | dev_err(&pdev->dev, "failed to get dma irq resource: %d\n", | ||
587 | ret); | ||
588 | goto failed_get_irq2; | ||
589 | } | ||
590 | |||
591 | platform_set_drvdata(pdev, saif); | ||
592 | |||
593 | ret = snd_soc_register_dai(&pdev->dev, &mxs_saif_dai); | ||
594 | if (ret) { | ||
595 | dev_err(&pdev->dev, "register DAI failed\n"); | ||
596 | goto failed_register; | ||
597 | } | ||
598 | |||
599 | saif->soc_platform_pdev = platform_device_alloc( | ||
600 | "mxs-pcm-audio", pdev->id); | ||
601 | if (!saif->soc_platform_pdev) { | ||
602 | ret = -ENOMEM; | ||
603 | goto failed_pdev_alloc; | ||
604 | } | ||
605 | |||
606 | platform_set_drvdata(saif->soc_platform_pdev, saif); | ||
607 | ret = platform_device_add(saif->soc_platform_pdev); | ||
608 | if (ret) { | ||
609 | dev_err(&pdev->dev, "failed to add soc platform device\n"); | ||
610 | goto failed_pdev_add; | ||
611 | } | ||
612 | |||
613 | return 0; | ||
614 | |||
615 | failed_pdev_add: | ||
616 | platform_device_put(saif->soc_platform_pdev); | ||
617 | failed_pdev_alloc: | ||
618 | snd_soc_unregister_dai(&pdev->dev); | ||
619 | failed_register: | ||
620 | failed_get_irq2: | ||
621 | free_irq(saif->irq, saif); | ||
622 | failed_get_irq1: | ||
623 | iounmap(saif->base); | ||
624 | failed_ioremap: | ||
625 | release_mem_region(res->start, resource_size(res)); | ||
626 | failed_get_resource: | ||
627 | clk_put(saif->clk); | ||
628 | failed_clk: | ||
629 | kfree(saif); | ||
630 | |||
631 | return ret; | ||
632 | } | ||
633 | |||
634 | static int __devexit mxs_saif_remove(struct platform_device *pdev) | ||
635 | { | ||
636 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
637 | struct mxs_saif *saif = platform_get_drvdata(pdev); | ||
638 | |||
639 | platform_device_unregister(saif->soc_platform_pdev); | ||
640 | |||
641 | snd_soc_unregister_dai(&pdev->dev); | ||
642 | |||
643 | iounmap(saif->base); | ||
644 | release_mem_region(res->start, resource_size(res)); | ||
645 | free_irq(saif->irq, saif); | ||
646 | |||
647 | clk_put(saif->clk); | ||
648 | kfree(saif); | ||
649 | |||
650 | return 0; | ||
651 | } | ||
652 | |||
653 | static struct platform_driver mxs_saif_driver = { | ||
654 | .probe = mxs_saif_probe, | ||
655 | .remove = __devexit_p(mxs_saif_remove), | ||
656 | |||
657 | .driver = { | ||
658 | .name = "mxs-saif", | ||
659 | .owner = THIS_MODULE, | ||
660 | }, | ||
661 | }; | ||
662 | |||
663 | static int __init mxs_saif_init(void) | ||
664 | { | ||
665 | return platform_driver_register(&mxs_saif_driver); | ||
666 | } | ||
667 | |||
668 | static void __exit mxs_saif_exit(void) | ||
669 | { | ||
670 | platform_driver_unregister(&mxs_saif_driver); | ||
671 | } | ||
672 | |||
673 | module_init(mxs_saif_init); | ||
674 | module_exit(mxs_saif_exit); | ||
675 | MODULE_AUTHOR("Freescale Semiconductor, Inc."); | ||
676 | MODULE_DESCRIPTION("MXS ASoC SAIF driver"); | ||
677 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/mxs/mxs-saif.h b/sound/soc/mxs/mxs-saif.h new file mode 100644 index 000000000000..0e2ff8cdbfee --- /dev/null +++ b/sound/soc/mxs/mxs-saif.h | |||
@@ -0,0 +1,130 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along | ||
15 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
17 | */ | ||
18 | |||
19 | |||
20 | #ifndef _MXS_SAIF_H | ||
21 | #define _MXS_SAIF_H | ||
22 | |||
23 | #define SAIF_CTRL 0x0 | ||
24 | #define SAIF_STAT 0x10 | ||
25 | #define SAIF_DATA 0x20 | ||
26 | #define SAIF_VERSION 0X30 | ||
27 | |||
28 | /* SAIF_CTRL */ | ||
29 | #define BM_SAIF_CTRL_SFTRST 0x80000000 | ||
30 | #define BM_SAIF_CTRL_CLKGATE 0x40000000 | ||
31 | #define BP_SAIF_CTRL_BITCLK_MULT_RATE 27 | ||
32 | #define BM_SAIF_CTRL_BITCLK_MULT_RATE 0x38000000 | ||
33 | #define BF_SAIF_CTRL_BITCLK_MULT_RATE(v) \ | ||
34 | (((v) << 27) & BM_SAIF_CTRL_BITCLK_MULT_RATE) | ||
35 | #define BM_SAIF_CTRL_BITCLK_BASE_RATE 0x04000000 | ||
36 | #define BM_SAIF_CTRL_FIFO_ERROR_IRQ_EN 0x02000000 | ||
37 | #define BM_SAIF_CTRL_FIFO_SERVICE_IRQ_EN 0x01000000 | ||
38 | #define BP_SAIF_CTRL_RSRVD2 21 | ||
39 | #define BM_SAIF_CTRL_RSRVD2 0x00E00000 | ||
40 | |||
41 | #define BP_SAIF_CTRL_DMAWAIT_COUNT 16 | ||
42 | #define BM_SAIF_CTRL_DMAWAIT_COUNT 0x001F0000 | ||
43 | #define BF_SAIF_CTRL_DMAWAIT_COUNT(v) \ | ||
44 | (((v) << 16) & BM_SAIF_CTRL_DMAWAIT_COUNT) | ||
45 | #define BP_SAIF_CTRL_CHANNEL_NUM_SELECT 14 | ||
46 | #define BM_SAIF_CTRL_CHANNEL_NUM_SELECT 0x0000C000 | ||
47 | #define BF_SAIF_CTRL_CHANNEL_NUM_SELECT(v) \ | ||
48 | (((v) << 14) & BM_SAIF_CTRL_CHANNEL_NUM_SELECT) | ||
49 | #define BM_SAIF_CTRL_LRCLK_PULSE 0x00002000 | ||
50 | #define BM_SAIF_CTRL_BIT_ORDER 0x00001000 | ||
51 | #define BM_SAIF_CTRL_DELAY 0x00000800 | ||
52 | #define BM_SAIF_CTRL_JUSTIFY 0x00000400 | ||
53 | #define BM_SAIF_CTRL_LRCLK_POLARITY 0x00000200 | ||
54 | #define BM_SAIF_CTRL_BITCLK_EDGE 0x00000100 | ||
55 | #define BP_SAIF_CTRL_WORD_LENGTH 4 | ||
56 | #define BM_SAIF_CTRL_WORD_LENGTH 0x000000F0 | ||
57 | #define BF_SAIF_CTRL_WORD_LENGTH(v) \ | ||
58 | (((v) << 4) & BM_SAIF_CTRL_WORD_LENGTH) | ||
59 | #define BM_SAIF_CTRL_BITCLK_48XFS_ENABLE 0x00000008 | ||
60 | #define BM_SAIF_CTRL_SLAVE_MODE 0x00000004 | ||
61 | #define BM_SAIF_CTRL_READ_MODE 0x00000002 | ||
62 | #define BM_SAIF_CTRL_RUN 0x00000001 | ||
63 | |||
64 | /* SAIF_STAT */ | ||
65 | #define BM_SAIF_STAT_PRESENT 0x80000000 | ||
66 | #define BP_SAIF_STAT_RSRVD2 17 | ||
67 | #define BM_SAIF_STAT_RSRVD2 0x7FFE0000 | ||
68 | #define BF_SAIF_STAT_RSRVD2(v) \ | ||
69 | (((v) << 17) & BM_SAIF_STAT_RSRVD2) | ||
70 | #define BM_SAIF_STAT_DMA_PREQ 0x00010000 | ||
71 | #define BP_SAIF_STAT_RSRVD1 7 | ||
72 | #define BM_SAIF_STAT_RSRVD1 0x0000FF80 | ||
73 | #define BF_SAIF_STAT_RSRVD1(v) \ | ||
74 | (((v) << 7) & BM_SAIF_STAT_RSRVD1) | ||
75 | |||
76 | #define BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ 0x00000040 | ||
77 | #define BM_SAIF_STAT_FIFO_OVERFLOW_IRQ 0x00000020 | ||
78 | #define BM_SAIF_STAT_FIFO_SERVICE_IRQ 0x00000010 | ||
79 | #define BP_SAIF_STAT_RSRVD0 1 | ||
80 | #define BM_SAIF_STAT_RSRVD0 0x0000000E | ||
81 | #define BF_SAIF_STAT_RSRVD0(v) \ | ||
82 | (((v) << 1) & BM_SAIF_STAT_RSRVD0) | ||
83 | #define BM_SAIF_STAT_BUSY 0x00000001 | ||
84 | |||
85 | /* SAFI_DATA */ | ||
86 | #define BP_SAIF_DATA_PCM_RIGHT 16 | ||
87 | #define BM_SAIF_DATA_PCM_RIGHT 0xFFFF0000 | ||
88 | #define BF_SAIF_DATA_PCM_RIGHT(v) \ | ||
89 | (((v) << 16) & BM_SAIF_DATA_PCM_RIGHT) | ||
90 | #define BP_SAIF_DATA_PCM_LEFT 0 | ||
91 | #define BM_SAIF_DATA_PCM_LEFT 0x0000FFFF | ||
92 | #define BF_SAIF_DATA_PCM_LEFT(v) \ | ||
93 | (((v) << 0) & BM_SAIF_DATA_PCM_LEFT) | ||
94 | |||
95 | /* SAIF_VERSION */ | ||
96 | #define BP_SAIF_VERSION_MAJOR 24 | ||
97 | #define BM_SAIF_VERSION_MAJOR 0xFF000000 | ||
98 | #define BF_SAIF_VERSION_MAJOR(v) \ | ||
99 | (((v) << 24) & BM_SAIF_VERSION_MAJOR) | ||
100 | #define BP_SAIF_VERSION_MINOR 16 | ||
101 | #define BM_SAIF_VERSION_MINOR 0x00FF0000 | ||
102 | #define BF_SAIF_VERSION_MINOR(v) \ | ||
103 | (((v) << 16) & BM_SAIF_VERSION_MINOR) | ||
104 | #define BP_SAIF_VERSION_STEP 0 | ||
105 | #define BM_SAIF_VERSION_STEP 0x0000FFFF | ||
106 | #define BF_SAIF_VERSION_STEP(v) \ | ||
107 | (((v) << 0) & BM_SAIF_VERSION_STEP) | ||
108 | |||
109 | #define MXS_SAIF_MCLK 0 | ||
110 | |||
111 | #include "mxs-pcm.h" | ||
112 | |||
113 | struct mxs_saif { | ||
114 | struct device *dev; | ||
115 | struct clk *clk; | ||
116 | unsigned int mclk; | ||
117 | unsigned int mclk_in_use; | ||
118 | void __iomem *base; | ||
119 | int irq; | ||
120 | struct mxs_pcm_dma_params dma_param; | ||
121 | |||
122 | struct platform_device *soc_platform_pdev; | ||
123 | u32 fifo_underrun; | ||
124 | u32 fifo_overrun; | ||
125 | }; | ||
126 | |||
127 | extern int mxs_saif_put_mclk(unsigned int saif_id); | ||
128 | extern int mxs_saif_get_mclk(unsigned int saif_id, unsigned int mclk, | ||
129 | unsigned int rate); | ||
130 | #endif | ||
diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c new file mode 100644 index 000000000000..a0d89c93df0f --- /dev/null +++ b/sound/soc/mxs/mxs-sgtl5000.c | |||
@@ -0,0 +1,165 @@ | |||
1 | /* | ||
2 | * Copyright 2011 Freescale Semiconductor, Inc. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along | ||
15 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
17 | */ | ||
18 | |||
19 | #include <linux/module.h> | ||
20 | #include <linux/device.h> | ||
21 | #include <sound/core.h> | ||
22 | #include <sound/pcm.h> | ||
23 | #include <sound/soc.h> | ||
24 | #include <sound/jack.h> | ||
25 | #include <sound/soc-dapm.h> | ||
26 | #include <asm/mach-types.h> | ||
27 | |||
28 | #include "../codecs/sgtl5000.h" | ||
29 | #include "mxs-saif.h" | ||
30 | |||
31 | static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream, | ||
32 | struct snd_pcm_hw_params *params) | ||
33 | { | ||
34 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
35 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||
36 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
37 | unsigned int rate = params_rate(params); | ||
38 | u32 dai_format, mclk; | ||
39 | int ret; | ||
40 | |||
41 | /* sgtl5000 does not support 512*rate when in 96000 fs */ | ||
42 | switch (rate) { | ||
43 | case 96000: | ||
44 | mclk = 256 * rate; | ||
45 | break; | ||
46 | default: | ||
47 | mclk = 512 * rate; | ||
48 | break; | ||
49 | } | ||
50 | |||
51 | /* Sgtl5000 sysclk should be >= 8MHz and <= 27M */ | ||
52 | if (mclk < 8000000 || mclk > 27000000) | ||
53 | return -EINVAL; | ||
54 | |||
55 | /* Set SGTL5000's SYSCLK (provided by SAIF MCLK) */ | ||
56 | ret = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, mclk, 0); | ||
57 | if (ret) | ||
58 | return ret; | ||
59 | |||
60 | /* The SAIF MCLK should be the same as SGTL5000_SYSCLK */ | ||
61 | ret = snd_soc_dai_set_sysclk(cpu_dai, MXS_SAIF_MCLK, mclk, 0); | ||
62 | if (ret) | ||
63 | return ret; | ||
64 | |||
65 | /* set codec to slave mode */ | ||
66 | dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | ||
67 | SND_SOC_DAIFMT_CBS_CFS; | ||
68 | |||
69 | /* set codec DAI configuration */ | ||
70 | ret = snd_soc_dai_set_fmt(codec_dai, dai_format); | ||
71 | if (ret) | ||
72 | return ret; | ||
73 | |||
74 | /* set cpu DAI configuration */ | ||
75 | ret = snd_soc_dai_set_fmt(cpu_dai, dai_format); | ||
76 | if (ret) | ||
77 | return ret; | ||
78 | |||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | static struct snd_soc_ops mxs_sgtl5000_hifi_ops = { | ||
83 | .hw_params = mxs_sgtl5000_hw_params, | ||
84 | }; | ||
85 | |||
86 | static struct snd_soc_dai_link mxs_sgtl5000_dai[] = { | ||
87 | { | ||
88 | .name = "HiFi", | ||
89 | .stream_name = "HiFi Playback", | ||
90 | .codec_dai_name = "sgtl5000", | ||
91 | .codec_name = "sgtl5000.0-000a", | ||
92 | .cpu_dai_name = "mxs-saif.0", | ||
93 | .platform_name = "mxs-pcm-audio.0", | ||
94 | .ops = &mxs_sgtl5000_hifi_ops, | ||
95 | }, | ||
96 | }; | ||
97 | |||
98 | static struct snd_soc_card mxs_sgtl5000 = { | ||
99 | .name = "mxs_sgtl5000", | ||
100 | .dai_link = mxs_sgtl5000_dai, | ||
101 | .num_links = ARRAY_SIZE(mxs_sgtl5000_dai), | ||
102 | }; | ||
103 | |||
104 | static int __devinit mxs_sgtl5000_probe(struct platform_device *pdev) | ||
105 | { | ||
106 | struct snd_soc_card *card = &mxs_sgtl5000; | ||
107 | int ret; | ||
108 | |||
109 | /* | ||
110 | * Set an init clock(11.28Mhz) for sgtl5000 initialization(i2c r/w). | ||
111 | * The Sgtl5000 sysclk is derived from saif0 mclk and it's range | ||
112 | * should be >= 8MHz and <= 27M. | ||
113 | */ | ||
114 | ret = mxs_saif_get_mclk(0, 44100 * 256, 44100); | ||
115 | if (ret) | ||
116 | return ret; | ||
117 | |||
118 | card->dev = &pdev->dev; | ||
119 | platform_set_drvdata(pdev, card); | ||
120 | |||
121 | ret = snd_soc_register_card(card); | ||
122 | if (ret) { | ||
123 | dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", | ||
124 | ret); | ||
125 | return ret; | ||
126 | } | ||
127 | |||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static int __devexit mxs_sgtl5000_remove(struct platform_device *pdev) | ||
132 | { | ||
133 | struct snd_soc_card *card = platform_get_drvdata(pdev); | ||
134 | |||
135 | mxs_saif_put_mclk(0); | ||
136 | |||
137 | snd_soc_unregister_card(card); | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | static struct platform_driver mxs_sgtl5000_audio_driver = { | ||
143 | .driver = { | ||
144 | .name = "mxs-sgtl5000", | ||
145 | .owner = THIS_MODULE, | ||
146 | }, | ||
147 | .probe = mxs_sgtl5000_probe, | ||
148 | .remove = __devexit_p(mxs_sgtl5000_remove), | ||
149 | }; | ||
150 | |||
151 | static int __init mxs_sgtl5000_init(void) | ||
152 | { | ||
153 | return platform_driver_register(&mxs_sgtl5000_audio_driver); | ||
154 | } | ||
155 | module_init(mxs_sgtl5000_init); | ||
156 | |||
157 | static void __exit mxs_sgtl5000_exit(void) | ||
158 | { | ||
159 | platform_driver_unregister(&mxs_sgtl5000_audio_driver); | ||
160 | } | ||
161 | module_exit(mxs_sgtl5000_exit); | ||
162 | |||
163 | MODULE_AUTHOR("Freescale Semiconductor, Inc."); | ||
164 | MODULE_DESCRIPTION("MXS ALSA SoC Machine driver"); | ||
165 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 83ad8ca27490..ae93aa81244c 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c | |||
@@ -105,7 +105,7 @@ static int format_register_str(struct snd_soc_codec *codec, | |||
105 | if (wordsize + regsize + 2 + 1 != len) | 105 | if (wordsize + regsize + 2 + 1 != len) |
106 | return -EINVAL; | 106 | return -EINVAL; |
107 | 107 | ||
108 | ret = snd_soc_read(codec , reg); | 108 | ret = snd_soc_read(codec, reg); |
109 | if (ret < 0) { | 109 | if (ret < 0) { |
110 | memset(regbuf, 'X', regsize); | 110 | memset(regbuf, 'X', regsize); |
111 | regbuf[regsize] = '\0'; | 111 | regbuf[regsize] = '\0'; |
@@ -3141,6 +3141,7 @@ int snd_soc_register_platform(struct device *dev, | |||
3141 | platform->driver = platform_drv; | 3141 | platform->driver = platform_drv; |
3142 | platform->dapm.dev = dev; | 3142 | platform->dapm.dev = dev; |
3143 | platform->dapm.platform = platform; | 3143 | platform->dapm.platform = platform; |
3144 | platform->dapm.stream_event = platform_drv->stream_event; | ||
3144 | 3145 | ||
3145 | mutex_lock(&client_mutex); | 3146 | mutex_lock(&client_mutex); |
3146 | list_add(&platform->list, &platform_list); | 3147 | list_add(&platform->list, &platform_list); |
@@ -3253,6 +3254,7 @@ int snd_soc_register_codec(struct device *dev, | |||
3253 | codec->dapm.dev = dev; | 3254 | codec->dapm.dev = dev; |
3254 | codec->dapm.codec = codec; | 3255 | codec->dapm.codec = codec; |
3255 | codec->dapm.seq_notifier = codec_drv->seq_notifier; | 3256 | codec->dapm.seq_notifier = codec_drv->seq_notifier; |
3257 | codec->dapm.stream_event = codec_drv->stream_event; | ||
3256 | codec->dev = dev; | 3258 | codec->dev = dev; |
3257 | codec->driver = codec_drv; | 3259 | codec->driver = codec_drv; |
3258 | codec->num_dai = num_dai; | 3260 | codec->num_dai = num_dai; |
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 7e15914b3633..c26531132c66 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c | |||
@@ -2584,7 +2584,7 @@ static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm, | |||
2584 | { | 2584 | { |
2585 | if (!w->sname || w->dapm != dapm) | 2585 | if (!w->sname || w->dapm != dapm) |
2586 | continue; | 2586 | continue; |
2587 | dev_dbg(w->dapm->dev, "widget %s\n %s stream %s event %d\n", | 2587 | dev_vdbg(w->dapm->dev, "widget %s\n %s stream %s event %d\n", |
2588 | w->name, w->sname, stream, event); | 2588 | w->name, w->sname, stream, event); |
2589 | if (strstr(w->sname, stream)) { | 2589 | if (strstr(w->sname, stream)) { |
2590 | switch(event) { | 2590 | switch(event) { |
@@ -2604,6 +2604,10 @@ static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm, | |||
2604 | } | 2604 | } |
2605 | 2605 | ||
2606 | dapm_power_widgets(dapm, event); | 2606 | dapm_power_widgets(dapm, event); |
2607 | |||
2608 | /* do we need to notify any clients that DAPM stream is complete */ | ||
2609 | if (dapm->stream_event) | ||
2610 | dapm->stream_event(dapm, event); | ||
2607 | } | 2611 | } |
2608 | 2612 | ||
2609 | /** | 2613 | /** |