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 | |
| 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>
| -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 |
