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