diff options
Diffstat (limited to 'sound/soc/au1x')
-rw-r--r-- | sound/soc/au1x/dbdma2.c | 115 | ||||
-rw-r--r-- | sound/soc/au1x/psc-ac97.c | 243 | ||||
-rw-r--r-- | sound/soc/au1x/psc-i2s.c | 189 | ||||
-rw-r--r-- | sound/soc/au1x/psc.h | 7 |
4 files changed, 375 insertions, 179 deletions
diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c index 594c6c5b7838..19e4d37eba1c 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,81 @@ 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); |
415 | } | 441 | } |
416 | 442 | ||
417 | module_init(au1xpsc_audio_dbdma_init); | 443 | module_init(au1xpsc_audio_dbdma_load); |
418 | module_exit(au1xpsc_audio_dbdma_exit); | 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 | platform_device_put(pd); | ||
484 | out: | ||
485 | kfree(res); | ||
486 | return NULL; | ||
487 | } | ||
488 | EXPORT_SYMBOL_GPL(au1xpsc_pcm_add); | ||
489 | |||
490 | void au1xpsc_pcm_destroy(struct platform_device *dmapd) | ||
491 | { | ||
492 | if (dmapd) | ||
493 | platform_device_unregister(dmapd); | ||
494 | } | ||
495 | EXPORT_SYMBOL_GPL(au1xpsc_pcm_destroy); | ||
419 | 496 | ||
420 | MODULE_LICENSE("GPL"); | 497 | MODULE_LICENSE("GPL"); |
421 | MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver"); | 498 | MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver"); |
422 | MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); | 499 | MODULE_AUTHOR("Manuel Lauss"); |
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 | |||
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 |