diff options
Diffstat (limited to 'sound/soc/au1x/psc-ac97.c')
-rw-r--r-- | sound/soc/au1x/psc-ac97.c | 244 |
1 files changed, 154 insertions, 90 deletions
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index a521aa90ddee..a61ccd2d505f 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c | |||
@@ -17,6 +17,7 @@ | |||
17 | 17 | ||
18 | #include <linux/init.h> | 18 | #include <linux/init.h> |
19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
20 | #include <linux/slab.h> | ||
20 | #include <linux/device.h> | 21 | #include <linux/device.h> |
21 | #include <linux/delay.h> | 22 | #include <linux/delay.h> |
22 | #include <linux/mutex.h> | 23 | #include <linux/mutex.h> |
@@ -61,7 +62,8 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97, | |||
61 | { | 62 | { |
62 | /* FIXME */ | 63 | /* FIXME */ |
63 | struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; | 64 | struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; |
64 | unsigned short data, retry, tmo; | 65 | unsigned short retry, tmo; |
66 | unsigned long data; | ||
65 | 67 | ||
66 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); | 68 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); |
67 | au_sync(); | 69 | au_sync(); |
@@ -74,20 +76,26 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97, | |||
74 | AC97_CDC(pscdata)); | 76 | AC97_CDC(pscdata)); |
75 | au_sync(); | 77 | au_sync(); |
76 | 78 | ||
77 | tmo = 2000; | 79 | tmo = 20; |
78 | while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) | 80 | do { |
79 | && --tmo) | 81 | udelay(21); |
80 | udelay(2); | 82 | if (au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD) |
83 | break; | ||
84 | } while (--tmo); | ||
81 | 85 | ||
82 | data = au_readl(AC97_CDC(pscdata)) & 0xffff; | 86 | data = au_readl(AC97_CDC(pscdata)); |
83 | 87 | ||
84 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); | 88 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); |
85 | au_sync(); | 89 | au_sync(); |
86 | 90 | ||
87 | mutex_unlock(&pscdata->lock); | 91 | mutex_unlock(&pscdata->lock); |
92 | |||
93 | if (reg != ((data >> 16) & 0x7f)) | ||
94 | tmo = 1; /* wrong register, try again */ | ||
95 | |||
88 | } while (--retry && !tmo); | 96 | } while (--retry && !tmo); |
89 | 97 | ||
90 | return retry ? data : 0xffff; | 98 | return retry ? data & 0xffff : 0xffff; |
91 | } | 99 | } |
92 | 100 | ||
93 | /* AC97 controller writes to codec register */ | 101 | /* AC97 controller writes to codec register */ |
@@ -109,10 +117,12 @@ static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg, | |||
109 | AC97_CDC(pscdata)); | 117 | AC97_CDC(pscdata)); |
110 | au_sync(); | 118 | au_sync(); |
111 | 119 | ||
112 | tmo = 2000; | 120 | tmo = 20; |
113 | while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) | 121 | do { |
114 | && --tmo) | 122 | udelay(21); |
115 | udelay(2); | 123 | if (au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD) |
124 | break; | ||
125 | } while (--tmo); | ||
116 | 126 | ||
117 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); | 127 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); |
118 | au_sync(); | 128 | au_sync(); |
@@ -195,7 +205,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, | |||
195 | /* FIXME */ | 205 | /* FIXME */ |
196 | struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; | 206 | struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; |
197 | unsigned long r, ro, stat; | 207 | unsigned long r, ro, stat; |
198 | int chans, stype = SUBSTREAM_TYPE(substream); | 208 | int chans, t, stype = SUBSTREAM_TYPE(substream); |
199 | 209 | ||
200 | chans = params_channels(params); | 210 | chans = params_channels(params); |
201 | 211 | ||
@@ -237,8 +247,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, | |||
237 | au_sync(); | 247 | au_sync(); |
238 | 248 | ||
239 | /* ...wait for it... */ | 249 | /* ...wait for it... */ |
240 | while (au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR) | 250 | t = 100; |
241 | asm volatile ("nop"); | 251 | while ((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR) && --t) |
252 | msleep(1); | ||
253 | |||
254 | if (!t) | ||
255 | printk(KERN_ERR "PSC-AC97: can't disable!\n"); | ||
242 | 256 | ||
243 | /* ...write config... */ | 257 | /* ...write config... */ |
244 | au_writel(r, AC97_CFG(pscdata)); | 258 | au_writel(r, AC97_CFG(pscdata)); |
@@ -249,8 +263,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, | |||
249 | au_sync(); | 263 | au_sync(); |
250 | 264 | ||
251 | /* ...and wait for ready bit */ | 265 | /* ...and wait for ready bit */ |
252 | while (!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) | 266 | t = 100; |
253 | asm volatile ("nop"); | 267 | while ((!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && --t) |
268 | msleep(1); | ||
269 | |||
270 | if (!t) | ||
271 | printk(KERN_ERR "PSC-AC97: can't enable!\n"); | ||
254 | 272 | ||
255 | mutex_unlock(&pscdata->lock); | 273 | mutex_unlock(&pscdata->lock); |
256 | 274 | ||
@@ -300,19 +318,55 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream, | |||
300 | static int au1xpsc_ac97_probe(struct platform_device *pdev, | 318 | static int au1xpsc_ac97_probe(struct platform_device *pdev, |
301 | struct snd_soc_dai *dai) | 319 | struct snd_soc_dai *dai) |
302 | { | 320 | { |
321 | return au1xpsc_ac97_workdata ? 0 : -ENODEV; | ||
322 | } | ||
323 | |||
324 | static void au1xpsc_ac97_remove(struct platform_device *pdev, | ||
325 | struct snd_soc_dai *dai) | ||
326 | { | ||
327 | } | ||
328 | |||
329 | static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = { | ||
330 | .trigger = au1xpsc_ac97_trigger, | ||
331 | .hw_params = au1xpsc_ac97_hw_params, | ||
332 | }; | ||
333 | |||
334 | struct snd_soc_dai au1xpsc_ac97_dai = { | ||
335 | .name = "au1xpsc_ac97", | ||
336 | .ac97_control = 1, | ||
337 | .probe = au1xpsc_ac97_probe, | ||
338 | .remove = au1xpsc_ac97_remove, | ||
339 | .playback = { | ||
340 | .rates = AC97_RATES, | ||
341 | .formats = AC97_FMTS, | ||
342 | .channels_min = 2, | ||
343 | .channels_max = 2, | ||
344 | }, | ||
345 | .capture = { | ||
346 | .rates = AC97_RATES, | ||
347 | .formats = AC97_FMTS, | ||
348 | .channels_min = 2, | ||
349 | .channels_max = 2, | ||
350 | }, | ||
351 | .ops = &au1xpsc_ac97_dai_ops, | ||
352 | }; | ||
353 | EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai); | ||
354 | |||
355 | static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev) | ||
356 | { | ||
303 | int ret; | 357 | int ret; |
304 | struct resource *r; | 358 | struct resource *r; |
305 | unsigned long sel; | 359 | unsigned long sel; |
360 | struct au1xpsc_audio_data *wd; | ||
306 | 361 | ||
307 | if (au1xpsc_ac97_workdata) | 362 | if (au1xpsc_ac97_workdata) |
308 | return -EBUSY; | 363 | return -EBUSY; |
309 | 364 | ||
310 | au1xpsc_ac97_workdata = | 365 | wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL); |
311 | kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL); | 366 | if (!wd) |
312 | if (!au1xpsc_ac97_workdata) | ||
313 | return -ENOMEM; | 367 | return -ENOMEM; |
314 | 368 | ||
315 | mutex_init(&au1xpsc_ac97_workdata->lock); | 369 | mutex_init(&wd->lock); |
316 | 370 | ||
317 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 371 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
318 | if (!r) { | 372 | if (!r) { |
@@ -321,81 +375,95 @@ static int au1xpsc_ac97_probe(struct platform_device *pdev, | |||
321 | } | 375 | } |
322 | 376 | ||
323 | ret = -EBUSY; | 377 | ret = -EBUSY; |
324 | au1xpsc_ac97_workdata->ioarea = | 378 | wd->ioarea = request_mem_region(r->start, r->end - r->start + 1, |
325 | request_mem_region(r->start, r->end - r->start + 1, | ||
326 | "au1xpsc_ac97"); | 379 | "au1xpsc_ac97"); |
327 | if (!au1xpsc_ac97_workdata->ioarea) | 380 | if (!wd->ioarea) |
328 | goto out0; | 381 | goto out0; |
329 | 382 | ||
330 | au1xpsc_ac97_workdata->mmio = ioremap(r->start, 0xffff); | 383 | wd->mmio = ioremap(r->start, 0xffff); |
331 | if (!au1xpsc_ac97_workdata->mmio) | 384 | if (!wd->mmio) |
332 | goto out1; | 385 | goto out1; |
333 | 386 | ||
334 | /* configuration: max dma trigger threshold, enable ac97 */ | 387 | /* configuration: max dma trigger threshold, enable ac97 */ |
335 | au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 | | 388 | wd->cfg = PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8 | |
336 | PSC_AC97CFG_TT_FIFO8 | | 389 | PSC_AC97CFG_DE_ENABLE; |
337 | PSC_AC97CFG_DE_ENABLE; | ||
338 | 390 | ||
339 | /* preserve PSC clock source set up by platform (dev.platform_data | 391 | /* preserve PSC clock source set up by platform */ |
340 | * is already occupied by soc layer) | 392 | sel = au_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK; |
341 | */ | 393 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); |
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(); | 394 | au_sync(); |
345 | au_writel(0, PSC_SEL(au1xpsc_ac97_workdata)); | 395 | au_writel(0, PSC_SEL(wd)); |
346 | au_sync(); | 396 | au_sync(); |
347 | au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(au1xpsc_ac97_workdata)); | 397 | au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(wd)); |
348 | au_sync(); | 398 | 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 | 399 | ||
353 | return 0; | 400 | ret = snd_soc_register_dai(&au1xpsc_ac97_dai); |
401 | if (ret) | ||
402 | goto out1; | ||
354 | 403 | ||
404 | wd->dmapd = au1xpsc_pcm_add(pdev); | ||
405 | if (wd->dmapd) { | ||
406 | platform_set_drvdata(pdev, wd); | ||
407 | au1xpsc_ac97_workdata = wd; /* MDEV */ | ||
408 | return 0; | ||
409 | } | ||
410 | |||
411 | snd_soc_unregister_dai(&au1xpsc_ac97_dai); | ||
355 | out1: | 412 | out1: |
356 | release_resource(au1xpsc_ac97_workdata->ioarea); | 413 | release_resource(wd->ioarea); |
357 | kfree(au1xpsc_ac97_workdata->ioarea); | 414 | kfree(wd->ioarea); |
358 | out0: | 415 | out0: |
359 | kfree(au1xpsc_ac97_workdata); | 416 | kfree(wd); |
360 | au1xpsc_ac97_workdata = NULL; | ||
361 | return ret; | 417 | return ret; |
362 | } | 418 | } |
363 | 419 | ||
364 | static void au1xpsc_ac97_remove(struct platform_device *pdev, | 420 | static int __devexit au1xpsc_ac97_drvremove(struct platform_device *pdev) |
365 | struct snd_soc_dai *dai) | ||
366 | { | 421 | { |
422 | struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev); | ||
423 | |||
424 | if (wd->dmapd) | ||
425 | au1xpsc_pcm_destroy(wd->dmapd); | ||
426 | |||
427 | snd_soc_unregister_dai(&au1xpsc_ac97_dai); | ||
428 | |||
367 | /* disable PSC completely */ | 429 | /* disable PSC completely */ |
368 | au_writel(0, AC97_CFG(au1xpsc_ac97_workdata)); | 430 | au_writel(0, AC97_CFG(wd)); |
369 | au_sync(); | 431 | au_sync(); |
370 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata)); | 432 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); |
371 | au_sync(); | 433 | au_sync(); |
372 | 434 | ||
373 | iounmap(au1xpsc_ac97_workdata->mmio); | 435 | iounmap(wd->mmio); |
374 | release_resource(au1xpsc_ac97_workdata->ioarea); | 436 | release_resource(wd->ioarea); |
375 | kfree(au1xpsc_ac97_workdata->ioarea); | 437 | kfree(wd->ioarea); |
376 | kfree(au1xpsc_ac97_workdata); | 438 | kfree(wd); |
377 | au1xpsc_ac97_workdata = NULL; | 439 | |
440 | au1xpsc_ac97_workdata = NULL; /* MDEV */ | ||
441 | |||
442 | return 0; | ||
378 | } | 443 | } |
379 | 444 | ||
380 | static int au1xpsc_ac97_suspend(struct snd_soc_dai *dai) | 445 | #ifdef CONFIG_PM |
446 | static int au1xpsc_ac97_drvsuspend(struct device *dev) | ||
381 | { | 447 | { |
448 | struct au1xpsc_audio_data *wd = dev_get_drvdata(dev); | ||
449 | |||
382 | /* save interesting registers and disable PSC */ | 450 | /* save interesting registers and disable PSC */ |
383 | au1xpsc_ac97_workdata->pm[0] = | 451 | wd->pm[0] = au_readl(PSC_SEL(wd)); |
384 | au_readl(PSC_SEL(au1xpsc_ac97_workdata)); | ||
385 | 452 | ||
386 | au_writel(0, AC97_CFG(au1xpsc_ac97_workdata)); | 453 | au_writel(0, AC97_CFG(wd)); |
387 | au_sync(); | 454 | au_sync(); |
388 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata)); | 455 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); |
389 | au_sync(); | 456 | au_sync(); |
390 | 457 | ||
391 | return 0; | 458 | return 0; |
392 | } | 459 | } |
393 | 460 | ||
394 | static int au1xpsc_ac97_resume(struct snd_soc_dai *dai) | 461 | static int au1xpsc_ac97_drvresume(struct device *dev) |
395 | { | 462 | { |
463 | struct au1xpsc_audio_data *wd = dev_get_drvdata(dev); | ||
464 | |||
396 | /* restore PSC clock config */ | 465 | /* restore PSC clock config */ |
397 | au_writel(au1xpsc_ac97_workdata->pm[0] | PSC_SEL_PS_AC97MODE, | 466 | au_writel(wd->pm[0] | PSC_SEL_PS_AC97MODE, PSC_SEL(wd)); |
398 | PSC_SEL(au1xpsc_ac97_workdata)); | ||
399 | au_sync(); | 467 | au_sync(); |
400 | 468 | ||
401 | /* after this point the ac97 core will cold-reset the codec. | 469 | /* after this point the ac97 core will cold-reset the codec. |
@@ -405,48 +473,44 @@ static int au1xpsc_ac97_resume(struct snd_soc_dai *dai) | |||
405 | return 0; | 473 | return 0; |
406 | } | 474 | } |
407 | 475 | ||
408 | static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = { | 476 | static struct dev_pm_ops au1xpscac97_pmops = { |
409 | .trigger = au1xpsc_ac97_trigger, | 477 | .suspend = au1xpsc_ac97_drvsuspend, |
410 | .hw_params = au1xpsc_ac97_hw_params, | 478 | .resume = au1xpsc_ac97_drvresume, |
411 | }; | 479 | }; |
412 | 480 | ||
413 | struct snd_soc_dai au1xpsc_ac97_dai = { | 481 | #define AU1XPSCAC97_PMOPS &au1xpscac97_pmops |
414 | .name = "au1xpsc_ac97", | 482 | |
415 | .ac97_control = 1, | 483 | #else |
416 | .probe = au1xpsc_ac97_probe, | 484 | |
417 | .remove = au1xpsc_ac97_remove, | 485 | #define AU1XPSCAC97_PMOPS NULL |
418 | .suspend = au1xpsc_ac97_suspend, | 486 | |
419 | .resume = au1xpsc_ac97_resume, | 487 | #endif |
420 | .playback = { | 488 | |
421 | .rates = AC97_RATES, | 489 | static struct platform_driver au1xpsc_ac97_driver = { |
422 | .formats = AC97_FMTS, | 490 | .driver = { |
423 | .channels_min = 2, | 491 | .name = "au1xpsc_ac97", |
424 | .channels_max = 2, | 492 | .owner = THIS_MODULE, |
425 | }, | 493 | .pm = AU1XPSCAC97_PMOPS, |
426 | .capture = { | ||
427 | .rates = AC97_RATES, | ||
428 | .formats = AC97_FMTS, | ||
429 | .channels_min = 2, | ||
430 | .channels_max = 2, | ||
431 | }, | 494 | }, |
432 | .ops = &au1xpsc_ac97_dai_ops, | 495 | .probe = au1xpsc_ac97_drvprobe, |
496 | .remove = __devexit_p(au1xpsc_ac97_drvremove), | ||
433 | }; | 497 | }; |
434 | EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai); | ||
435 | 498 | ||
436 | static int __init au1xpsc_ac97_init(void) | 499 | static int __init au1xpsc_ac97_load(void) |
437 | { | 500 | { |
438 | au1xpsc_ac97_workdata = NULL; | 501 | au1xpsc_ac97_workdata = NULL; |
439 | return snd_soc_register_dai(&au1xpsc_ac97_dai); | 502 | return platform_driver_register(&au1xpsc_ac97_driver); |
440 | } | 503 | } |
441 | 504 | ||
442 | static void __exit au1xpsc_ac97_exit(void) | 505 | static void __exit au1xpsc_ac97_unload(void) |
443 | { | 506 | { |
444 | snd_soc_unregister_dai(&au1xpsc_ac97_dai); | 507 | platform_driver_unregister(&au1xpsc_ac97_driver); |
445 | } | 508 | } |
446 | 509 | ||
447 | module_init(au1xpsc_ac97_init); | 510 | module_init(au1xpsc_ac97_load); |
448 | module_exit(au1xpsc_ac97_exit); | 511 | module_exit(au1xpsc_ac97_unload); |
449 | 512 | ||
450 | MODULE_LICENSE("GPL"); | 513 | MODULE_LICENSE("GPL"); |
451 | MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver"); | 514 | MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver"); |
452 | MODULE_AUTHOR("Manuel Lauss <manuel.lauss@gmail.com>"); | 515 | MODULE_AUTHOR("Manuel Lauss"); |
516 | |||