diff options
author | Manuel Lauss <manuel.lauss@googlemail.com> | 2009-10-31 15:15:08 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-11-02 06:27:07 -0500 |
commit | 0f83d639d84c99a775c60696dbde77372c2cf4ac (patch) | |
tree | b2eebe3df3686e435110f1f11074abb869f2ce0a /sound/soc/au1x | |
parent | 6c508c62f90240ef58300a5e12093ee769a44364 (diff) |
ASoC: au1x: convert to platform drivers.
Convert psc-ac97,i2s to platform drivers similar to the davinci ones.
Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/au1x')
-rw-r--r-- | sound/soc/au1x/dbdma2.c | 117 | ||||
-rw-r--r-- | sound/soc/au1x/psc-ac97.c | 194 | ||||
-rw-r--r-- | sound/soc/au1x/psc-i2s.c | 189 | ||||
-rw-r--r-- | sound/soc/au1x/psc.h | 7 |
4 files changed, 344 insertions, 163 deletions
diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c index 594c6c5b7838..fe9f4657c959 100644 --- a/sound/soc/au1x/dbdma2.c +++ b/sound/soc/au1x/dbdma2.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * Au12x0/Au1550 PSC ALSA ASoC audio support. | 2 | * Au12x0/Au1550 PSC ALSA ASoC audio support. |
3 | * | 3 | * |
4 | * (c) 2007-2008 MSC Vertriebsges.m.b.H., | 4 | * (c) 2007-2008 MSC Vertriebsges.m.b.H., |
5 | * Manuel Lauss <mano@roarinelk.homelinux.net> | 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 |
8 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
@@ -333,6 +333,30 @@ static int au1xpsc_pcm_new(struct snd_card *card, | |||
333 | 333 | ||
334 | static int au1xpsc_pcm_probe(struct platform_device *pdev) | 334 | static int au1xpsc_pcm_probe(struct platform_device *pdev) |
335 | { | 335 | { |
336 | if (!au1xpsc_audio_pcmdma[PCM_TX] || !au1xpsc_audio_pcmdma[PCM_RX]) | ||
337 | return -ENODEV; | ||
338 | |||
339 | return 0; | ||
340 | } | ||
341 | |||
342 | static int au1xpsc_pcm_remove(struct platform_device *pdev) | ||
343 | { | ||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | /* au1xpsc audio platform */ | ||
348 | struct snd_soc_platform au1xpsc_soc_platform = { | ||
349 | .name = "au1xpsc-pcm-dbdma", | ||
350 | .probe = au1xpsc_pcm_probe, | ||
351 | .remove = au1xpsc_pcm_remove, | ||
352 | .pcm_ops = &au1xpsc_pcm_ops, | ||
353 | .pcm_new = au1xpsc_pcm_new, | ||
354 | .pcm_free = au1xpsc_pcm_free_dma_buffers, | ||
355 | }; | ||
356 | EXPORT_SYMBOL_GPL(au1xpsc_soc_platform); | ||
357 | |||
358 | static int __devinit au1xpsc_pcm_drvprobe(struct platform_device *pdev) | ||
359 | { | ||
336 | struct resource *r; | 360 | struct resource *r; |
337 | int ret; | 361 | int ret; |
338 | 362 | ||
@@ -365,7 +389,9 @@ static int au1xpsc_pcm_probe(struct platform_device *pdev) | |||
365 | } | 389 | } |
366 | (au1xpsc_audio_pcmdma[PCM_RX])->ddma_id = r->start; | 390 | (au1xpsc_audio_pcmdma[PCM_RX])->ddma_id = r->start; |
367 | 391 | ||
368 | return 0; | 392 | ret = snd_soc_register_platform(&au1xpsc_soc_platform); |
393 | if (!ret) | ||
394 | return ret; | ||
369 | 395 | ||
370 | out2: | 396 | out2: |
371 | kfree(au1xpsc_audio_pcmdma[PCM_RX]); | 397 | kfree(au1xpsc_audio_pcmdma[PCM_RX]); |
@@ -376,10 +402,12 @@ out1: | |||
376 | return ret; | 402 | return ret; |
377 | } | 403 | } |
378 | 404 | ||
379 | static int au1xpsc_pcm_remove(struct platform_device *pdev) | 405 | static int __devexit au1xpsc_pcm_drvremove(struct platform_device *pdev) |
380 | { | 406 | { |
381 | int i; | 407 | int i; |
382 | 408 | ||
409 | snd_soc_unregister_platform(&au1xpsc_soc_platform); | ||
410 | |||
383 | for (i = 0; i < 2; i++) { | 411 | for (i = 0; i < 2; i++) { |
384 | if (au1xpsc_audio_pcmdma[i]) { | 412 | if (au1xpsc_audio_pcmdma[i]) { |
385 | au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[i]); | 413 | au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[i]); |
@@ -391,32 +419,83 @@ static int au1xpsc_pcm_remove(struct platform_device *pdev) | |||
391 | return 0; | 419 | return 0; |
392 | } | 420 | } |
393 | 421 | ||
394 | /* au1xpsc audio platform */ | 422 | static struct platform_driver au1xpsc_pcm_driver = { |
395 | struct snd_soc_platform au1xpsc_soc_platform = { | 423 | .driver = { |
396 | .name = "au1xpsc-pcm-dbdma", | 424 | .name = "au1xpsc-pcm", |
397 | .probe = au1xpsc_pcm_probe, | 425 | .owner = THIS_MODULE, |
398 | .remove = au1xpsc_pcm_remove, | 426 | }, |
399 | .pcm_ops = &au1xpsc_pcm_ops, | 427 | .probe = au1xpsc_pcm_drvprobe, |
400 | .pcm_new = au1xpsc_pcm_new, | 428 | .remove = __devexit_p(au1xpsc_pcm_drvremove), |
401 | .pcm_free = au1xpsc_pcm_free_dma_buffers, | ||
402 | }; | 429 | }; |
403 | EXPORT_SYMBOL_GPL(au1xpsc_soc_platform); | ||
404 | 430 | ||
405 | static int __init au1xpsc_audio_dbdma_init(void) | 431 | static int __init au1xpsc_audio_dbdma_load(void) |
406 | { | 432 | { |
407 | au1xpsc_audio_pcmdma[PCM_TX] = NULL; | 433 | au1xpsc_audio_pcmdma[PCM_TX] = NULL; |
408 | au1xpsc_audio_pcmdma[PCM_RX] = NULL; | 434 | au1xpsc_audio_pcmdma[PCM_RX] = NULL; |
409 | return snd_soc_register_platform(&au1xpsc_soc_platform); | 435 | return platform_driver_register(&au1xpsc_pcm_driver); |
410 | } | 436 | } |
411 | 437 | ||
412 | static void __exit au1xpsc_audio_dbdma_exit(void) | 438 | static void __exit au1xpsc_audio_dbdma_unload(void) |
413 | { | 439 | { |
414 | snd_soc_unregister_platform(&au1xpsc_soc_platform); | 440 | platform_driver_unregister(&au1xpsc_pcm_driver); |
441 | } | ||
442 | |||
443 | module_init(au1xpsc_audio_dbdma_load); | ||
444 | module_exit(au1xpsc_audio_dbdma_unload); | ||
445 | |||
446 | |||
447 | struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev) | ||
448 | { | ||
449 | struct resource *res, *r; | ||
450 | struct platform_device *pd; | ||
451 | int id[2]; | ||
452 | int ret; | ||
453 | |||
454 | r = platform_get_resource(pdev, IORESOURCE_DMA, 0); | ||
455 | if (!r) | ||
456 | return NULL; | ||
457 | id[0] = r->start; | ||
458 | |||
459 | r = platform_get_resource(pdev, IORESOURCE_DMA, 1); | ||
460 | if (!r) | ||
461 | return NULL; | ||
462 | id[1] = r->start; | ||
463 | |||
464 | res = kzalloc(sizeof(struct resource) * 2, GFP_KERNEL); | ||
465 | if (!res) | ||
466 | return NULL; | ||
467 | |||
468 | res[0].start = res[0].end = id[0]; | ||
469 | res[1].start = res[1].end = id[1]; | ||
470 | res[0].flags = res[1].flags = IORESOURCE_DMA; | ||
471 | |||
472 | pd = platform_device_alloc("au1xpsc-pcm", -1); | ||
473 | if (!pd) | ||
474 | goto out; | ||
475 | |||
476 | pd->resource = res; | ||
477 | pd->num_resources = 2; | ||
478 | |||
479 | ret = platform_device_add(pd); | ||
480 | if (!ret) | ||
481 | return pd; | ||
482 | |||
483 | out: | ||
484 | kfree(res); | ||
485 | return NULL; | ||
415 | } | 486 | } |
487 | EXPORT_SYMBOL_GPL(au1xpsc_pcm_add); | ||
416 | 488 | ||
417 | module_init(au1xpsc_audio_dbdma_init); | 489 | void au1xpsc_pcm_destroy(struct platform_device *dmapd) |
418 | module_exit(au1xpsc_audio_dbdma_exit); | 490 | { |
491 | if (dmapd) { | ||
492 | kfree(dmapd->resource); | ||
493 | dmapd->resource = NULL; | ||
494 | platform_device_unregister(dmapd); | ||
495 | } | ||
496 | } | ||
497 | EXPORT_SYMBOL_GPL(au1xpsc_pcm_destroy); | ||
419 | 498 | ||
420 | MODULE_LICENSE("GPL"); | 499 | MODULE_LICENSE("GPL"); |
421 | MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver"); | 500 | MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver"); |
422 | MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); | 501 | MODULE_AUTHOR("Manuel Lauss"); |
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index 2a06a9c548af..340311d7fed5 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c | |||
@@ -317,19 +317,55 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream, | |||
317 | static int au1xpsc_ac97_probe(struct platform_device *pdev, | 317 | static int au1xpsc_ac97_probe(struct platform_device *pdev, |
318 | struct snd_soc_dai *dai) | 318 | struct snd_soc_dai *dai) |
319 | { | 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 | { | ||
320 | int ret; | 356 | int ret; |
321 | struct resource *r; | 357 | struct resource *r; |
322 | unsigned long sel; | 358 | unsigned long sel; |
359 | struct au1xpsc_audio_data *wd; | ||
323 | 360 | ||
324 | if (au1xpsc_ac97_workdata) | 361 | if (au1xpsc_ac97_workdata) |
325 | return -EBUSY; | 362 | return -EBUSY; |
326 | 363 | ||
327 | au1xpsc_ac97_workdata = | 364 | wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL); |
328 | kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL); | 365 | if (!wd) |
329 | if (!au1xpsc_ac97_workdata) | ||
330 | return -ENOMEM; | 366 | return -ENOMEM; |
331 | 367 | ||
332 | mutex_init(&au1xpsc_ac97_workdata->lock); | 368 | mutex_init(&wd->lock); |
333 | 369 | ||
334 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 370 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
335 | if (!r) { | 371 | if (!r) { |
@@ -338,81 +374,95 @@ static int au1xpsc_ac97_probe(struct platform_device *pdev, | |||
338 | } | 374 | } |
339 | 375 | ||
340 | ret = -EBUSY; | 376 | ret = -EBUSY; |
341 | au1xpsc_ac97_workdata->ioarea = | 377 | wd->ioarea = request_mem_region(r->start, r->end - r->start + 1, |
342 | request_mem_region(r->start, r->end - r->start + 1, | ||
343 | "au1xpsc_ac97"); | 378 | "au1xpsc_ac97"); |
344 | if (!au1xpsc_ac97_workdata->ioarea) | 379 | if (!wd->ioarea) |
345 | goto out0; | 380 | goto out0; |
346 | 381 | ||
347 | au1xpsc_ac97_workdata->mmio = ioremap(r->start, 0xffff); | 382 | wd->mmio = ioremap(r->start, 0xffff); |
348 | if (!au1xpsc_ac97_workdata->mmio) | 383 | if (!wd->mmio) |
349 | goto out1; | 384 | goto out1; |
350 | 385 | ||
351 | /* configuration: max dma trigger threshold, enable ac97 */ | 386 | /* configuration: max dma trigger threshold, enable ac97 */ |
352 | au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 | | 387 | wd->cfg = PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8 | |
353 | PSC_AC97CFG_TT_FIFO8 | | 388 | PSC_AC97CFG_DE_ENABLE; |
354 | PSC_AC97CFG_DE_ENABLE; | ||
355 | 389 | ||
356 | /* preserve PSC clock source set up by platform (dev.platform_data | 390 | /* preserve PSC clock source set up by platform */ |
357 | * is already occupied by soc layer) | 391 | sel = au_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK; |
358 | */ | 392 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); |
359 | sel = au_readl(PSC_SEL(au1xpsc_ac97_workdata)) & PSC_SEL_CLK_MASK; | ||
360 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata)); | ||
361 | au_sync(); | 393 | au_sync(); |
362 | au_writel(0, PSC_SEL(au1xpsc_ac97_workdata)); | 394 | au_writel(0, PSC_SEL(wd)); |
363 | au_sync(); | 395 | au_sync(); |
364 | au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(au1xpsc_ac97_workdata)); | 396 | au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(wd)); |
365 | au_sync(); | 397 | au_sync(); |
366 | /* next up: cold reset. Dont check for PSC-ready now since | ||
367 | * there may not be any codec clock yet. | ||
368 | */ | ||
369 | 398 | ||
370 | return 0; | 399 | ret = snd_soc_register_dai(&au1xpsc_ac97_dai); |
400 | if (ret) | ||
401 | goto out1; | ||
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 | } | ||
371 | 409 | ||
410 | snd_soc_unregister_dai(&au1xpsc_ac97_dai); | ||
372 | out1: | 411 | out1: |
373 | release_resource(au1xpsc_ac97_workdata->ioarea); | 412 | release_resource(wd->ioarea); |
374 | kfree(au1xpsc_ac97_workdata->ioarea); | 413 | kfree(wd->ioarea); |
375 | out0: | 414 | out0: |
376 | kfree(au1xpsc_ac97_workdata); | 415 | kfree(wd); |
377 | au1xpsc_ac97_workdata = NULL; | ||
378 | return ret; | 416 | return ret; |
379 | } | 417 | } |
380 | 418 | ||
381 | static void au1xpsc_ac97_remove(struct platform_device *pdev, | 419 | static int __devexit au1xpsc_ac97_drvremove(struct platform_device *pdev) |
382 | struct snd_soc_dai *dai) | ||
383 | { | 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 | |||
384 | /* disable PSC completely */ | 428 | /* disable PSC completely */ |
385 | au_writel(0, AC97_CFG(au1xpsc_ac97_workdata)); | 429 | au_writel(0, AC97_CFG(wd)); |
386 | au_sync(); | 430 | au_sync(); |
387 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata)); | 431 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); |
388 | au_sync(); | 432 | au_sync(); |
389 | 433 | ||
390 | iounmap(au1xpsc_ac97_workdata->mmio); | 434 | iounmap(wd->mmio); |
391 | release_resource(au1xpsc_ac97_workdata->ioarea); | 435 | release_resource(wd->ioarea); |
392 | kfree(au1xpsc_ac97_workdata->ioarea); | 436 | kfree(wd->ioarea); |
393 | kfree(au1xpsc_ac97_workdata); | 437 | kfree(wd); |
394 | au1xpsc_ac97_workdata = NULL; | 438 | |
439 | au1xpsc_ac97_workdata = NULL; /* MDEV */ | ||
440 | |||
441 | return 0; | ||
395 | } | 442 | } |
396 | 443 | ||
397 | static int au1xpsc_ac97_suspend(struct snd_soc_dai *dai) | 444 | #ifdef CONFIG_PM |
445 | static int au1xpsc_ac97_drvsuspend(struct device *dev) | ||
398 | { | 446 | { |
447 | struct au1xpsc_audio_data *wd = dev_get_drvdata(dev); | ||
448 | |||
399 | /* save interesting registers and disable PSC */ | 449 | /* save interesting registers and disable PSC */ |
400 | au1xpsc_ac97_workdata->pm[0] = | 450 | wd->pm[0] = au_readl(PSC_SEL(wd)); |
401 | au_readl(PSC_SEL(au1xpsc_ac97_workdata)); | ||
402 | 451 | ||
403 | au_writel(0, AC97_CFG(au1xpsc_ac97_workdata)); | 452 | au_writel(0, AC97_CFG(wd)); |
404 | au_sync(); | 453 | au_sync(); |
405 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata)); | 454 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); |
406 | au_sync(); | 455 | au_sync(); |
407 | 456 | ||
408 | return 0; | 457 | return 0; |
409 | } | 458 | } |
410 | 459 | ||
411 | static int au1xpsc_ac97_resume(struct snd_soc_dai *dai) | 460 | static int au1xpsc_ac97_drvresume(struct device *dev) |
412 | { | 461 | { |
462 | struct au1xpsc_audio_data *wd = dev_get_drvdata(dev); | ||
463 | |||
413 | /* restore PSC clock config */ | 464 | /* restore PSC clock config */ |
414 | au_writel(au1xpsc_ac97_workdata->pm[0] | PSC_SEL_PS_AC97MODE, | 465 | au_writel(wd->pm[0] | PSC_SEL_PS_AC97MODE, PSC_SEL(wd)); |
415 | PSC_SEL(au1xpsc_ac97_workdata)); | ||
416 | au_sync(); | 466 | au_sync(); |
417 | 467 | ||
418 | /* after this point the ac97 core will cold-reset the codec. | 468 | /* after this point the ac97 core will cold-reset the codec. |
@@ -422,48 +472,44 @@ static int au1xpsc_ac97_resume(struct snd_soc_dai *dai) | |||
422 | return 0; | 472 | return 0; |
423 | } | 473 | } |
424 | 474 | ||
425 | static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = { | 475 | static struct dev_pm_ops au1xpscac97_pmops = { |
426 | .trigger = au1xpsc_ac97_trigger, | 476 | .suspend = au1xpsc_ac97_drvsuspend, |
427 | .hw_params = au1xpsc_ac97_hw_params, | 477 | .resume = au1xpsc_ac97_drvresume, |
428 | }; | 478 | }; |
429 | 479 | ||
430 | struct snd_soc_dai au1xpsc_ac97_dai = { | 480 | #define AU1XPSCAC97_PMOPS &au1xpscac97_pmops |
431 | .name = "au1xpsc_ac97", | 481 | |
432 | .ac97_control = 1, | 482 | #else |
433 | .probe = au1xpsc_ac97_probe, | 483 | |
434 | .remove = au1xpsc_ac97_remove, | 484 | #define AU1XPSCAC97_PMOPS NULL |
435 | .suspend = au1xpsc_ac97_suspend, | 485 | |
436 | .resume = au1xpsc_ac97_resume, | 486 | #endif |
437 | .playback = { | 487 | |
438 | .rates = AC97_RATES, | 488 | static struct platform_driver au1xpsc_ac97_driver = { |
439 | .formats = AC97_FMTS, | 489 | .driver = { |
440 | .channels_min = 2, | 490 | .name = "au1xpsc_ac97", |
441 | .channels_max = 2, | 491 | .owner = THIS_MODULE, |
442 | }, | 492 | .pm = AU1XPSCAC97_PMOPS, |
443 | .capture = { | ||
444 | .rates = AC97_RATES, | ||
445 | .formats = AC97_FMTS, | ||
446 | .channels_min = 2, | ||
447 | .channels_max = 2, | ||
448 | }, | 493 | }, |
449 | .ops = &au1xpsc_ac97_dai_ops, | 494 | .probe = au1xpsc_ac97_drvprobe, |
495 | .remove = __devexit_p(au1xpsc_ac97_drvremove), | ||
450 | }; | 496 | }; |
451 | EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai); | ||
452 | 497 | ||
453 | static int __init au1xpsc_ac97_init(void) | 498 | static int __init au1xpsc_ac97_load(void) |
454 | { | 499 | { |
455 | au1xpsc_ac97_workdata = NULL; | 500 | au1xpsc_ac97_workdata = NULL; |
456 | return snd_soc_register_dai(&au1xpsc_ac97_dai); | 501 | return platform_driver_register(&au1xpsc_ac97_driver); |
457 | } | 502 | } |
458 | 503 | ||
459 | static void __exit au1xpsc_ac97_exit(void) | 504 | static void __exit au1xpsc_ac97_unload(void) |
460 | { | 505 | { |
461 | snd_soc_unregister_dai(&au1xpsc_ac97_dai); | 506 | platform_driver_unregister(&au1xpsc_ac97_driver); |
462 | } | 507 | } |
463 | 508 | ||
464 | module_init(au1xpsc_ac97_init); | 509 | module_init(au1xpsc_ac97_load); |
465 | module_exit(au1xpsc_ac97_exit); | 510 | module_exit(au1xpsc_ac97_unload); |
466 | 511 | ||
467 | MODULE_LICENSE("GPL"); | 512 | MODULE_LICENSE("GPL"); |
468 | MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver"); | 513 | MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver"); |
469 | MODULE_AUTHOR("Manuel Lauss <manuel.lauss@gmail.com>"); | 514 | MODULE_AUTHOR("Manuel Lauss"); |
515 | |||
diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c index bb589327ee32..0cf2ca61c776 100644 --- a/sound/soc/au1x/psc-i2s.c +++ b/sound/soc/au1x/psc-i2s.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * Au12x0/Au1550 PSC ALSA ASoC audio support. | 2 | * Au12x0/Au1550 PSC ALSA ASoC audio support. |
3 | * | 3 | * |
4 | * (c) 2007-2008 MSC Vertriebsges.m.b.H., | 4 | * (c) 2007-2008 MSC Vertriebsges.m.b.H., |
5 | * Manuel Lauss <mano@roarinelk.homelinux.net> | 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 |
8 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
@@ -265,16 +265,52 @@ static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, | |||
265 | static int au1xpsc_i2s_probe(struct platform_device *pdev, | 265 | static int au1xpsc_i2s_probe(struct platform_device *pdev, |
266 | struct snd_soc_dai *dai) | 266 | struct snd_soc_dai *dai) |
267 | { | 267 | { |
268 | return au1xpsc_i2s_workdata ? 0 : -ENODEV; | ||
269 | } | ||
270 | |||
271 | static void au1xpsc_i2s_remove(struct platform_device *pdev, | ||
272 | struct snd_soc_dai *dai) | ||
273 | { | ||
274 | } | ||
275 | |||
276 | static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = { | ||
277 | .trigger = au1xpsc_i2s_trigger, | ||
278 | .hw_params = au1xpsc_i2s_hw_params, | ||
279 | .set_fmt = au1xpsc_i2s_set_fmt, | ||
280 | }; | ||
281 | |||
282 | struct snd_soc_dai au1xpsc_i2s_dai = { | ||
283 | .name = "au1xpsc_i2s", | ||
284 | .probe = au1xpsc_i2s_probe, | ||
285 | .remove = au1xpsc_i2s_remove, | ||
286 | .playback = { | ||
287 | .rates = AU1XPSC_I2S_RATES, | ||
288 | .formats = AU1XPSC_I2S_FMTS, | ||
289 | .channels_min = 2, | ||
290 | .channels_max = 8, /* 2 without external help */ | ||
291 | }, | ||
292 | .capture = { | ||
293 | .rates = AU1XPSC_I2S_RATES, | ||
294 | .formats = AU1XPSC_I2S_FMTS, | ||
295 | .channels_min = 2, | ||
296 | .channels_max = 8, /* 2 without external help */ | ||
297 | }, | ||
298 | .ops = &au1xpsc_i2s_dai_ops, | ||
299 | }; | ||
300 | EXPORT_SYMBOL(au1xpsc_i2s_dai); | ||
301 | |||
302 | static int __init au1xpsc_i2s_drvprobe(struct platform_device *pdev) | ||
303 | { | ||
268 | struct resource *r; | 304 | struct resource *r; |
269 | unsigned long sel; | 305 | unsigned long sel; |
270 | int ret; | 306 | int ret; |
307 | struct au1xpsc_audio_data *wd; | ||
271 | 308 | ||
272 | if (au1xpsc_i2s_workdata) | 309 | if (au1xpsc_i2s_workdata) |
273 | return -EBUSY; | 310 | return -EBUSY; |
274 | 311 | ||
275 | au1xpsc_i2s_workdata = | 312 | wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL); |
276 | kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL); | 313 | if (!wd) |
277 | if (!au1xpsc_i2s_workdata) | ||
278 | return -ENOMEM; | 314 | return -ENOMEM; |
279 | 315 | ||
280 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 316 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
@@ -284,131 +320,146 @@ static int au1xpsc_i2s_probe(struct platform_device *pdev, | |||
284 | } | 320 | } |
285 | 321 | ||
286 | ret = -EBUSY; | 322 | ret = -EBUSY; |
287 | au1xpsc_i2s_workdata->ioarea = | 323 | wd->ioarea = request_mem_region(r->start, r->end - r->start + 1, |
288 | request_mem_region(r->start, r->end - r->start + 1, | ||
289 | "au1xpsc_i2s"); | 324 | "au1xpsc_i2s"); |
290 | if (!au1xpsc_i2s_workdata->ioarea) | 325 | if (!wd->ioarea) |
291 | goto out0; | 326 | goto out0; |
292 | 327 | ||
293 | au1xpsc_i2s_workdata->mmio = ioremap(r->start, 0xffff); | 328 | wd->mmio = ioremap(r->start, 0xffff); |
294 | if (!au1xpsc_i2s_workdata->mmio) | 329 | if (!wd->mmio) |
295 | goto out1; | 330 | goto out1; |
296 | 331 | ||
297 | /* preserve PSC clock source set up by platform (dev.platform_data | 332 | /* preserve PSC clock source set up by platform (dev.platform_data |
298 | * is already occupied by soc layer) | 333 | * is already occupied by soc layer) |
299 | */ | 334 | */ |
300 | sel = au_readl(PSC_SEL(au1xpsc_i2s_workdata)) & PSC_SEL_CLK_MASK; | 335 | sel = au_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK; |
301 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata)); | 336 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); |
302 | au_sync(); | 337 | au_sync(); |
303 | au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(au1xpsc_i2s_workdata)); | 338 | au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(wd)); |
304 | au_writel(0, I2S_CFG(au1xpsc_i2s_workdata)); | 339 | au_writel(0, I2S_CFG(wd)); |
305 | au_sync(); | 340 | au_sync(); |
306 | 341 | ||
307 | /* preconfigure: set max rx/tx fifo depths */ | 342 | /* preconfigure: set max rx/tx fifo depths */ |
308 | au1xpsc_i2s_workdata->cfg |= | 343 | wd->cfg |= PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8; |
309 | PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8; | ||
310 | 344 | ||
311 | /* don't wait for I2S core to become ready now; clocks may not | 345 | /* don't wait for I2S core to become ready now; clocks may not |
312 | * be running yet; depending on clock input for PSC a wait might | 346 | * be running yet; depending on clock input for PSC a wait might |
313 | * time out. | 347 | * time out. |
314 | */ | 348 | */ |
315 | 349 | ||
316 | return 0; | 350 | ret = snd_soc_register_dai(&au1xpsc_i2s_dai); |
351 | if (ret) | ||
352 | goto out1; | ||
317 | 353 | ||
354 | /* finally add the DMA device for this PSC */ | ||
355 | wd->dmapd = au1xpsc_pcm_add(pdev); | ||
356 | if (wd->dmapd) { | ||
357 | platform_set_drvdata(pdev, wd); | ||
358 | au1xpsc_i2s_workdata = wd; | ||
359 | return 0; | ||
360 | } | ||
361 | |||
362 | snd_soc_unregister_dai(&au1xpsc_i2s_dai); | ||
318 | out1: | 363 | out1: |
319 | release_resource(au1xpsc_i2s_workdata->ioarea); | 364 | release_resource(wd->ioarea); |
320 | kfree(au1xpsc_i2s_workdata->ioarea); | 365 | kfree(wd->ioarea); |
321 | out0: | 366 | out0: |
322 | kfree(au1xpsc_i2s_workdata); | 367 | kfree(wd); |
323 | au1xpsc_i2s_workdata = NULL; | ||
324 | return ret; | 368 | return ret; |
325 | } | 369 | } |
326 | 370 | ||
327 | static void au1xpsc_i2s_remove(struct platform_device *pdev, | 371 | static int __devexit au1xpsc_i2s_drvremove(struct platform_device *pdev) |
328 | struct snd_soc_dai *dai) | ||
329 | { | 372 | { |
330 | au_writel(0, I2S_CFG(au1xpsc_i2s_workdata)); | 373 | struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev); |
374 | |||
375 | if (wd->dmapd) | ||
376 | au1xpsc_pcm_destroy(wd->dmapd); | ||
377 | |||
378 | snd_soc_unregister_dai(&au1xpsc_i2s_dai); | ||
379 | |||
380 | au_writel(0, I2S_CFG(wd)); | ||
331 | au_sync(); | 381 | au_sync(); |
332 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata)); | 382 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); |
333 | au_sync(); | 383 | au_sync(); |
334 | 384 | ||
335 | iounmap(au1xpsc_i2s_workdata->mmio); | 385 | iounmap(wd->mmio); |
336 | release_resource(au1xpsc_i2s_workdata->ioarea); | 386 | release_resource(wd->ioarea); |
337 | kfree(au1xpsc_i2s_workdata->ioarea); | 387 | kfree(wd->ioarea); |
338 | kfree(au1xpsc_i2s_workdata); | 388 | kfree(wd); |
339 | au1xpsc_i2s_workdata = NULL; | 389 | |
390 | au1xpsc_i2s_workdata = NULL; /* MDEV */ | ||
391 | |||
392 | return 0; | ||
340 | } | 393 | } |
341 | 394 | ||
342 | static int au1xpsc_i2s_suspend(struct snd_soc_dai *cpu_dai) | 395 | #ifdef CONFIG_PM |
396 | static int au1xpsc_i2s_drvsuspend(struct device *dev) | ||
343 | { | 397 | { |
398 | struct au1xpsc_audio_data *wd = dev_get_drvdata(dev); | ||
399 | |||
344 | /* save interesting register and disable PSC */ | 400 | /* save interesting register and disable PSC */ |
345 | au1xpsc_i2s_workdata->pm[0] = | 401 | wd->pm[0] = au_readl(PSC_SEL(wd)); |
346 | au_readl(PSC_SEL(au1xpsc_i2s_workdata)); | ||
347 | 402 | ||
348 | au_writel(0, I2S_CFG(au1xpsc_i2s_workdata)); | 403 | au_writel(0, I2S_CFG(wd)); |
349 | au_sync(); | 404 | au_sync(); |
350 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata)); | 405 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); |
351 | au_sync(); | 406 | au_sync(); |
352 | 407 | ||
353 | return 0; | 408 | return 0; |
354 | } | 409 | } |
355 | 410 | ||
356 | static int au1xpsc_i2s_resume(struct snd_soc_dai *cpu_dai) | 411 | static int au1xpsc_i2s_drvresume(struct device *dev) |
357 | { | 412 | { |
413 | struct au1xpsc_audio_data *wd = dev_get_drvdata(dev); | ||
414 | |||
358 | /* select I2S mode and PSC clock */ | 415 | /* select I2S mode and PSC clock */ |
359 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata)); | 416 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); |
360 | au_sync(); | 417 | au_sync(); |
361 | au_writel(0, PSC_SEL(au1xpsc_i2s_workdata)); | 418 | au_writel(0, PSC_SEL(wd)); |
362 | au_sync(); | 419 | au_sync(); |
363 | au_writel(au1xpsc_i2s_workdata->pm[0], | 420 | au_writel(wd->pm[0], PSC_SEL(wd)); |
364 | PSC_SEL(au1xpsc_i2s_workdata)); | ||
365 | au_sync(); | 421 | au_sync(); |
366 | 422 | ||
367 | return 0; | 423 | return 0; |
368 | } | 424 | } |
369 | 425 | ||
370 | static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = { | 426 | static struct dev_pm_ops au1xpsci2s_pmops = { |
371 | .trigger = au1xpsc_i2s_trigger, | 427 | .suspend = au1xpsc_i2s_drvsuspend, |
372 | .hw_params = au1xpsc_i2s_hw_params, | 428 | .resume = au1xpsc_i2s_drvresume, |
373 | .set_fmt = au1xpsc_i2s_set_fmt, | ||
374 | }; | 429 | }; |
375 | 430 | ||
376 | struct snd_soc_dai au1xpsc_i2s_dai = { | 431 | #define AU1XPSCI2S_PMOPS &au1xpsci2s_pmops |
377 | .name = "au1xpsc_i2s", | 432 | |
378 | .probe = au1xpsc_i2s_probe, | 433 | #else |
379 | .remove = au1xpsc_i2s_remove, | 434 | |
380 | .suspend = au1xpsc_i2s_suspend, | 435 | #define AU1XPSCI2S_PMOPS NULL |
381 | .resume = au1xpsc_i2s_resume, | 436 | |
382 | .playback = { | 437 | #endif |
383 | .rates = AU1XPSC_I2S_RATES, | 438 | |
384 | .formats = AU1XPSC_I2S_FMTS, | 439 | static struct platform_driver au1xpsc_i2s_driver = { |
385 | .channels_min = 2, | 440 | .driver = { |
386 | .channels_max = 8, /* 2 without external help */ | 441 | .name = "au1xpsc_i2s", |
387 | }, | 442 | .owner = THIS_MODULE, |
388 | .capture = { | 443 | .pm = AU1XPSCI2S_PMOPS, |
389 | .rates = AU1XPSC_I2S_RATES, | ||
390 | .formats = AU1XPSC_I2S_FMTS, | ||
391 | .channels_min = 2, | ||
392 | .channels_max = 8, /* 2 without external help */ | ||
393 | }, | 444 | }, |
394 | .ops = &au1xpsc_i2s_dai_ops, | 445 | .probe = au1xpsc_i2s_drvprobe, |
446 | .remove = __devexit_p(au1xpsc_i2s_drvremove), | ||
395 | }; | 447 | }; |
396 | EXPORT_SYMBOL(au1xpsc_i2s_dai); | ||
397 | 448 | ||
398 | static int __init au1xpsc_i2s_init(void) | 449 | static int __init au1xpsc_i2s_load(void) |
399 | { | 450 | { |
400 | au1xpsc_i2s_workdata = NULL; | 451 | au1xpsc_i2s_workdata = NULL; |
401 | return snd_soc_register_dai(&au1xpsc_i2s_dai); | 452 | return platform_driver_register(&au1xpsc_i2s_driver); |
402 | } | 453 | } |
403 | 454 | ||
404 | static void __exit au1xpsc_i2s_exit(void) | 455 | static void __exit au1xpsc_i2s_unload(void) |
405 | { | 456 | { |
406 | snd_soc_unregister_dai(&au1xpsc_i2s_dai); | 457 | platform_driver_unregister(&au1xpsc_i2s_driver); |
407 | } | 458 | } |
408 | 459 | ||
409 | module_init(au1xpsc_i2s_init); | 460 | module_init(au1xpsc_i2s_load); |
410 | module_exit(au1xpsc_i2s_exit); | 461 | module_exit(au1xpsc_i2s_unload); |
411 | 462 | ||
412 | MODULE_LICENSE("GPL"); | 463 | MODULE_LICENSE("GPL"); |
413 | MODULE_DESCRIPTION("Au12x0/Au1550 PSC I2S ALSA ASoC audio driver"); | 464 | MODULE_DESCRIPTION("Au12x0/Au1550 PSC I2S ALSA ASoC audio driver"); |
414 | MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); | 465 | MODULE_AUTHOR("Manuel Lauss"); |
diff --git a/sound/soc/au1x/psc.h b/sound/soc/au1x/psc.h index 3f474e8ed4f6..32d3807d3f5a 100644 --- a/sound/soc/au1x/psc.h +++ b/sound/soc/au1x/psc.h | |||
@@ -2,7 +2,7 @@ | |||
2 | * Au12x0/Au1550 PSC ALSA ASoC audio support. | 2 | * Au12x0/Au1550 PSC ALSA ASoC audio support. |
3 | * | 3 | * |
4 | * (c) 2007-2008 MSC Vertriebsges.m.b.H., | 4 | * (c) 2007-2008 MSC Vertriebsges.m.b.H., |
5 | * Manuel Lauss <mano@roarinelk.homelinux.net> | 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 |
8 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
@@ -21,6 +21,10 @@ extern struct snd_soc_dai au1xpsc_i2s_dai; | |||
21 | extern struct snd_soc_platform au1xpsc_soc_platform; | 21 | extern struct snd_soc_platform au1xpsc_soc_platform; |
22 | extern struct snd_ac97_bus_ops soc_ac97_ops; | 22 | extern struct snd_ac97_bus_ops soc_ac97_ops; |
23 | 23 | ||
24 | /* DBDMA helpers */ | ||
25 | extern struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev); | ||
26 | extern void au1xpsc_pcm_destroy(struct platform_device *dmapd); | ||
27 | |||
24 | struct au1xpsc_audio_data { | 28 | struct au1xpsc_audio_data { |
25 | void __iomem *mmio; | 29 | void __iomem *mmio; |
26 | 30 | ||
@@ -30,6 +34,7 @@ struct au1xpsc_audio_data { | |||
30 | unsigned long pm[2]; | 34 | unsigned long pm[2]; |
31 | struct resource *ioarea; | 35 | struct resource *ioarea; |
32 | struct mutex lock; | 36 | struct mutex lock; |
37 | struct platform_device *dmapd; | ||
33 | }; | 38 | }; |
34 | 39 | ||
35 | #define PCM_TX 0 | 40 | #define PCM_TX 0 |