diff options
Diffstat (limited to 'sound/soc/au1x/psc-ac97.c')
| -rw-r--r-- | sound/soc/au1x/psc-ac97.c | 194 |
1 files changed, 120 insertions, 74 deletions
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 | |||
