Commit | Line | Data |
---|---|---|
4a161d23 ML |
1 | /* |
2 | * Au12x0/Au1550 PSC ALSA ASoC audio support. | |
3 | * | |
cdc65fbe ML |
4 | * (c) 2007-2009 MSC Vertriebsges.m.b.H., |
5 | * Manuel Lauss <manuel.lauss@gmail.com> | |
4a161d23 ML |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | * | |
11 | * Au1xxx-PSC AC97 glue. | |
12 | * | |
13 | * NOTE: all of these drivers can only work with a SINGLE instance | |
14 | * of a PSC. Multiple independent audio devices are impossible | |
15 | * with ASoC v1. | |
16 | */ | |
17 | ||
18 | #include <linux/init.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/device.h> | |
21 | #include <linux/delay.h> | |
cdc65fbe | 22 | #include <linux/mutex.h> |
4a161d23 ML |
23 | #include <linux/suspend.h> |
24 | #include <sound/core.h> | |
25 | #include <sound/pcm.h> | |
26 | #include <sound/initval.h> | |
27 | #include <sound/soc.h> | |
28 | #include <asm/mach-au1x00/au1000.h> | |
29 | #include <asm/mach-au1x00/au1xxx_psc.h> | |
30 | ||
31 | #include "psc.h" | |
32 | ||
cdc65fbe ML |
33 | /* how often to retry failed codec register reads/writes */ |
34 | #define AC97_RW_RETRIES 5 | |
35 | ||
4a161d23 ML |
36 | #define AC97_DIR \ |
37 | (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) | |
38 | ||
39 | #define AC97_RATES \ | |
40 | SNDRV_PCM_RATE_8000_48000 | |
41 | ||
42 | #define AC97_FMTS \ | |
43 | (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3BE) | |
44 | ||
45 | #define AC97PCR_START(stype) \ | |
46 | ((stype) == PCM_TX ? PSC_AC97PCR_TS : PSC_AC97PCR_RS) | |
47 | #define AC97PCR_STOP(stype) \ | |
48 | ((stype) == PCM_TX ? PSC_AC97PCR_TP : PSC_AC97PCR_RP) | |
49 | #define AC97PCR_CLRFIFO(stype) \ | |
50 | ((stype) == PCM_TX ? PSC_AC97PCR_TC : PSC_AC97PCR_RC) | |
51 | ||
cdc65fbe ML |
52 | #define AC97STAT_BUSY(stype) \ |
53 | ((stype) == PCM_TX ? PSC_AC97STAT_TB : PSC_AC97STAT_RB) | |
54 | ||
4a161d23 ML |
55 | /* instance data. There can be only one, MacLeod!!!! */ |
56 | static struct au1xpsc_audio_data *au1xpsc_ac97_workdata; | |
57 | ||
58 | /* AC97 controller reads codec register */ | |
59 | static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97, | |
60 | unsigned short reg) | |
61 | { | |
62 | /* FIXME */ | |
63 | struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; | |
cdc65fbe | 64 | unsigned short data, retry, tmo; |
4a161d23 | 65 | |
cdc65fbe | 66 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); |
4a161d23 ML |
67 | au_sync(); |
68 | ||
cdc65fbe ML |
69 | retry = AC97_RW_RETRIES; |
70 | do { | |
71 | mutex_lock(&pscdata->lock); | |
72 | ||
73 | au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg), | |
74 | AC97_CDC(pscdata)); | |
75 | au_sync(); | |
76 | ||
77 | tmo = 2000; | |
78 | while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) | |
79 | && --tmo) | |
80 | udelay(2); | |
4a161d23 | 81 | |
4a161d23 ML |
82 | data = au_readl(AC97_CDC(pscdata)) & 0xffff; |
83 | ||
cdc65fbe ML |
84 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); |
85 | au_sync(); | |
86 | ||
87 | mutex_unlock(&pscdata->lock); | |
88 | } while (--retry && !tmo); | |
4a161d23 | 89 | |
cdc65fbe | 90 | return retry ? data : 0xffff; |
4a161d23 ML |
91 | } |
92 | ||
93 | /* AC97 controller writes to codec register */ | |
94 | static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg, | |
95 | unsigned short val) | |
96 | { | |
97 | /* FIXME */ | |
98 | struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; | |
cdc65fbe | 99 | unsigned int tmo, retry; |
4a161d23 | 100 | |
cdc65fbe | 101 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); |
4a161d23 | 102 | au_sync(); |
cdc65fbe ML |
103 | |
104 | retry = AC97_RW_RETRIES; | |
105 | do { | |
106 | mutex_lock(&pscdata->lock); | |
107 | ||
108 | au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff), | |
109 | AC97_CDC(pscdata)); | |
4a161d23 ML |
110 | au_sync(); |
111 | ||
cdc65fbe ML |
112 | tmo = 2000; |
113 | while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) | |
114 | && --tmo) | |
115 | udelay(2); | |
116 | ||
117 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); | |
118 | au_sync(); | |
119 | ||
120 | mutex_unlock(&pscdata->lock); | |
121 | } while (--retry && !tmo); | |
4a161d23 ML |
122 | } |
123 | ||
124 | /* AC97 controller asserts a warm reset */ | |
125 | static void au1xpsc_ac97_warm_reset(struct snd_ac97 *ac97) | |
126 | { | |
127 | /* FIXME */ | |
128 | struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; | |
129 | ||
130 | au_writel(PSC_AC97RST_SNC, AC97_RST(pscdata)); | |
131 | au_sync(); | |
132 | msleep(10); | |
133 | au_writel(0, AC97_RST(pscdata)); | |
134 | au_sync(); | |
135 | } | |
136 | ||
137 | static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97) | |
138 | { | |
139 | /* FIXME */ | |
140 | struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; | |
141 | int i; | |
142 | ||
143 | /* disable PSC during cold reset */ | |
144 | au_writel(0, AC97_CFG(au1xpsc_ac97_workdata)); | |
145 | au_sync(); | |
146 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(pscdata)); | |
147 | au_sync(); | |
148 | ||
149 | /* issue cold reset */ | |
150 | au_writel(PSC_AC97RST_RST, AC97_RST(pscdata)); | |
151 | au_sync(); | |
152 | msleep(500); | |
153 | au_writel(0, AC97_RST(pscdata)); | |
154 | au_sync(); | |
155 | ||
156 | /* enable PSC */ | |
157 | au_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata)); | |
158 | au_sync(); | |
159 | ||
160 | /* wait for PSC to indicate it's ready */ | |
cdc65fbe | 161 | i = 1000; |
4a161d23 | 162 | while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_SR)) && (--i)) |
cdc65fbe | 163 | msleep(1); |
4a161d23 ML |
164 | |
165 | if (i == 0) { | |
166 | printk(KERN_ERR "au1xpsc-ac97: PSC not ready!\n"); | |
167 | return; | |
168 | } | |
169 | ||
170 | /* enable the ac97 function */ | |
171 | au_writel(pscdata->cfg | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata)); | |
172 | au_sync(); | |
173 | ||
174 | /* wait for AC97 core to become ready */ | |
cdc65fbe | 175 | i = 1000; |
4a161d23 | 176 | while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && (--i)) |
cdc65fbe | 177 | msleep(1); |
4a161d23 ML |
178 | if (i == 0) |
179 | printk(KERN_ERR "au1xpsc-ac97: AC97 ctrl not ready\n"); | |
180 | } | |
181 | ||
182 | /* AC97 controller operations */ | |
183 | struct snd_ac97_bus_ops soc_ac97_ops = { | |
184 | .read = au1xpsc_ac97_read, | |
185 | .write = au1xpsc_ac97_write, | |
186 | .reset = au1xpsc_ac97_cold_reset, | |
187 | .warm_reset = au1xpsc_ac97_warm_reset, | |
188 | }; | |
189 | EXPORT_SYMBOL_GPL(soc_ac97_ops); | |
190 | ||
191 | static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, | |
dee89c4d MB |
192 | struct snd_pcm_hw_params *params, |
193 | struct snd_soc_dai *dai) | |
4a161d23 ML |
194 | { |
195 | /* FIXME */ | |
196 | struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; | |
cdc65fbe | 197 | unsigned long r, ro, stat; |
4a161d23 ML |
198 | int chans, stype = SUBSTREAM_TYPE(substream); |
199 | ||
200 | chans = params_channels(params); | |
201 | ||
cdc65fbe | 202 | r = ro = au_readl(AC97_CFG(pscdata)); |
4a161d23 ML |
203 | stat = au_readl(AC97_STAT(pscdata)); |
204 | ||
205 | /* already active? */ | |
206 | if (stat & (PSC_AC97STAT_TB | PSC_AC97STAT_RB)) { | |
207 | /* reject parameters not currently set up */ | |
208 | if ((PSC_AC97CFG_GET_LEN(r) != params->msbits) || | |
209 | (pscdata->rate != params_rate(params))) | |
210 | return -EINVAL; | |
211 | } else { | |
4a161d23 ML |
212 | |
213 | /* set sample bitdepth: REG[24:21]=(BITS-2)/2 */ | |
214 | r &= ~PSC_AC97CFG_LEN_MASK; | |
215 | r |= PSC_AC97CFG_SET_LEN(params->msbits); | |
216 | ||
217 | /* channels: enable slots for front L/R channel */ | |
218 | if (stype == PCM_TX) { | |
219 | r &= ~PSC_AC97CFG_TXSLOT_MASK; | |
220 | r |= PSC_AC97CFG_TXSLOT_ENA(3); | |
221 | r |= PSC_AC97CFG_TXSLOT_ENA(4); | |
222 | } else { | |
223 | r &= ~PSC_AC97CFG_RXSLOT_MASK; | |
224 | r |= PSC_AC97CFG_RXSLOT_ENA(3); | |
225 | r |= PSC_AC97CFG_RXSLOT_ENA(4); | |
226 | } | |
227 | ||
cdc65fbe ML |
228 | /* do we need to poke the hardware? */ |
229 | if (!(r ^ ro)) | |
230 | goto out; | |
231 | ||
232 | /* ac97 engine is about to be disabled */ | |
233 | mutex_lock(&pscdata->lock); | |
234 | ||
235 | /* disable AC97 device controller first... */ | |
236 | au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata)); | |
237 | au_sync(); | |
238 | ||
239 | /* ...wait for it... */ | |
240 | while (au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR) | |
241 | asm volatile ("nop"); | |
242 | ||
243 | /* ...write config... */ | |
244 | au_writel(r, AC97_CFG(pscdata)); | |
245 | au_sync(); | |
246 | ||
247 | /* ...enable the AC97 controller again... */ | |
4a161d23 ML |
248 | au_writel(r | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata)); |
249 | au_sync(); | |
250 | ||
cdc65fbe ML |
251 | /* ...and wait for ready bit */ |
252 | while (!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) | |
253 | asm volatile ("nop"); | |
254 | ||
255 | mutex_unlock(&pscdata->lock); | |
256 | ||
4a161d23 ML |
257 | pscdata->cfg = r; |
258 | pscdata->rate = params_rate(params); | |
259 | } | |
260 | ||
cdc65fbe | 261 | out: |
4a161d23 ML |
262 | return 0; |
263 | } | |
264 | ||
265 | static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream, | |
dee89c4d | 266 | int cmd, struct snd_soc_dai *dai) |
4a161d23 ML |
267 | { |
268 | /* FIXME */ | |
269 | struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; | |
270 | int ret, stype = SUBSTREAM_TYPE(substream); | |
271 | ||
272 | ret = 0; | |
273 | ||
274 | switch (cmd) { | |
275 | case SNDRV_PCM_TRIGGER_START: | |
276 | case SNDRV_PCM_TRIGGER_RESUME: | |
cdc65fbe ML |
277 | au_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata)); |
278 | au_sync(); | |
4a161d23 ML |
279 | au_writel(AC97PCR_START(stype), AC97_PCR(pscdata)); |
280 | au_sync(); | |
281 | break; | |
282 | case SNDRV_PCM_TRIGGER_STOP: | |
283 | case SNDRV_PCM_TRIGGER_SUSPEND: | |
284 | au_writel(AC97PCR_STOP(stype), AC97_PCR(pscdata)); | |
285 | au_sync(); | |
cdc65fbe ML |
286 | |
287 | while (au_readl(AC97_STAT(pscdata)) & AC97STAT_BUSY(stype)) | |
288 | asm volatile ("nop"); | |
289 | ||
290 | au_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata)); | |
291 | au_sync(); | |
292 | ||
4a161d23 ML |
293 | break; |
294 | default: | |
295 | ret = -EINVAL; | |
296 | } | |
297 | return ret; | |
298 | } | |
299 | ||
300 | static int au1xpsc_ac97_probe(struct platform_device *pdev, | |
301 | struct snd_soc_dai *dai) | |
302 | { | |
303 | int ret; | |
304 | struct resource *r; | |
305 | unsigned long sel; | |
306 | ||
307 | if (au1xpsc_ac97_workdata) | |
308 | return -EBUSY; | |
309 | ||
310 | au1xpsc_ac97_workdata = | |
311 | kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL); | |
312 | if (!au1xpsc_ac97_workdata) | |
313 | return -ENOMEM; | |
314 | ||
cdc65fbe ML |
315 | mutex_init(&au1xpsc_ac97_workdata->lock); |
316 | ||
4a161d23 ML |
317 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
318 | if (!r) { | |
319 | ret = -ENODEV; | |
320 | goto out0; | |
321 | } | |
322 | ||
323 | ret = -EBUSY; | |
324 | au1xpsc_ac97_workdata->ioarea = | |
325 | request_mem_region(r->start, r->end - r->start + 1, | |
326 | "au1xpsc_ac97"); | |
327 | if (!au1xpsc_ac97_workdata->ioarea) | |
328 | goto out0; | |
329 | ||
330 | au1xpsc_ac97_workdata->mmio = ioremap(r->start, 0xffff); | |
331 | if (!au1xpsc_ac97_workdata->mmio) | |
332 | goto out1; | |
333 | ||
334 | /* configuration: max dma trigger threshold, enable ac97 */ | |
cdc65fbe ML |
335 | au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 | |
336 | PSC_AC97CFG_TT_FIFO8 | | |
337 | PSC_AC97CFG_DE_ENABLE; | |
4a161d23 ML |
338 | |
339 | /* preserve PSC clock source set up by platform (dev.platform_data | |
340 | * is already occupied by soc layer) | |
341 | */ | |
342 | sel = au_readl(PSC_SEL(au1xpsc_ac97_workdata)) & PSC_SEL_CLK_MASK; | |
343 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata)); | |
344 | au_sync(); | |
345 | au_writel(0, PSC_SEL(au1xpsc_ac97_workdata)); | |
346 | au_sync(); | |
347 | au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(au1xpsc_ac97_workdata)); | |
348 | au_sync(); | |
349 | /* next up: cold reset. Dont check for PSC-ready now since | |
350 | * there may not be any codec clock yet. | |
351 | */ | |
352 | ||
353 | return 0; | |
354 | ||
355 | out1: | |
356 | release_resource(au1xpsc_ac97_workdata->ioarea); | |
357 | kfree(au1xpsc_ac97_workdata->ioarea); | |
358 | out0: | |
359 | kfree(au1xpsc_ac97_workdata); | |
360 | au1xpsc_ac97_workdata = NULL; | |
361 | return ret; | |
362 | } | |
363 | ||
364 | static void au1xpsc_ac97_remove(struct platform_device *pdev, | |
365 | struct snd_soc_dai *dai) | |
366 | { | |
367 | /* disable PSC completely */ | |
368 | au_writel(0, AC97_CFG(au1xpsc_ac97_workdata)); | |
369 | au_sync(); | |
370 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata)); | |
371 | au_sync(); | |
372 | ||
373 | iounmap(au1xpsc_ac97_workdata->mmio); | |
374 | release_resource(au1xpsc_ac97_workdata->ioarea); | |
375 | kfree(au1xpsc_ac97_workdata->ioarea); | |
376 | kfree(au1xpsc_ac97_workdata); | |
377 | au1xpsc_ac97_workdata = NULL; | |
378 | } | |
379 | ||
dc7d7b83 | 380 | static int au1xpsc_ac97_suspend(struct snd_soc_dai *dai) |
4a161d23 ML |
381 | { |
382 | /* save interesting registers and disable PSC */ | |
383 | au1xpsc_ac97_workdata->pm[0] = | |
384 | au_readl(PSC_SEL(au1xpsc_ac97_workdata)); | |
385 | ||
386 | au_writel(0, AC97_CFG(au1xpsc_ac97_workdata)); | |
387 | au_sync(); | |
388 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata)); | |
389 | au_sync(); | |
390 | ||
391 | return 0; | |
392 | } | |
393 | ||
dc7d7b83 | 394 | static int au1xpsc_ac97_resume(struct snd_soc_dai *dai) |
4a161d23 ML |
395 | { |
396 | /* restore PSC clock config */ | |
397 | au_writel(au1xpsc_ac97_workdata->pm[0] | PSC_SEL_PS_AC97MODE, | |
398 | PSC_SEL(au1xpsc_ac97_workdata)); | |
399 | au_sync(); | |
400 | ||
401 | /* after this point the ac97 core will cold-reset the codec. | |
402 | * During cold-reset the PSC is reinitialized and the last | |
403 | * configuration set up in hw_params() is restored. | |
404 | */ | |
405 | return 0; | |
406 | } | |
407 | ||
6335d055 EM |
408 | static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = { |
409 | .trigger = au1xpsc_ac97_trigger, | |
410 | .hw_params = au1xpsc_ac97_hw_params, | |
411 | }; | |
412 | ||
4a161d23 ML |
413 | struct snd_soc_dai au1xpsc_ac97_dai = { |
414 | .name = "au1xpsc_ac97", | |
3ba9e10a | 415 | .ac97_control = 1, |
4a161d23 ML |
416 | .probe = au1xpsc_ac97_probe, |
417 | .remove = au1xpsc_ac97_remove, | |
418 | .suspend = au1xpsc_ac97_suspend, | |
419 | .resume = au1xpsc_ac97_resume, | |
420 | .playback = { | |
421 | .rates = AC97_RATES, | |
422 | .formats = AC97_FMTS, | |
423 | .channels_min = 2, | |
424 | .channels_max = 2, | |
425 | }, | |
426 | .capture = { | |
427 | .rates = AC97_RATES, | |
428 | .formats = AC97_FMTS, | |
429 | .channels_min = 2, | |
430 | .channels_max = 2, | |
431 | }, | |
6335d055 | 432 | .ops = &au1xpsc_ac97_dai_ops, |
4a161d23 ML |
433 | }; |
434 | EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai); | |
435 | ||
436 | static int __init au1xpsc_ac97_init(void) | |
437 | { | |
438 | au1xpsc_ac97_workdata = NULL; | |
3f4b783c | 439 | return snd_soc_register_dai(&au1xpsc_ac97_dai); |
4a161d23 ML |
440 | } |
441 | ||
442 | static void __exit au1xpsc_ac97_exit(void) | |
443 | { | |
3f4b783c | 444 | snd_soc_unregister_dai(&au1xpsc_ac97_dai); |
4a161d23 ML |
445 | } |
446 | ||
447 | module_init(au1xpsc_ac97_init); | |
448 | module_exit(au1xpsc_ac97_exit); | |
449 | ||
450 | MODULE_LICENSE("GPL"); | |
451 | MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver"); | |
cdc65fbe | 452 | MODULE_AUTHOR("Manuel Lauss <manuel.lauss@gmail.com>"); |