diff options
author | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
---|---|---|
committer | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
commit | ada47b5fe13d89735805b566185f4885f5a3f750 (patch) | |
tree | 644b88f8a71896307d71438e9b3af49126ffb22b /sound/soc/au1x | |
parent | 43e98717ad40a4ae64545b5ba047c7b86aa44f4f (diff) | |
parent | 3280f21d43ee541f97f8cda5792150d2dbec20d5 (diff) |
Merge branch 'wip-2.6.34' into old-private-masterarchived-private-master
Diffstat (limited to 'sound/soc/au1x')
-rw-r--r-- | sound/soc/au1x/Kconfig | 10 | ||||
-rw-r--r-- | sound/soc/au1x/Makefile | 4 | ||||
-rw-r--r-- | sound/soc/au1x/db1200.c | 141 | ||||
-rw-r--r-- | sound/soc/au1x/dbdma2.c | 129 | ||||
-rw-r--r-- | sound/soc/au1x/psc-ac97.c | 244 | ||||
-rw-r--r-- | sound/soc/au1x/psc-i2s.c | 190 | ||||
-rw-r--r-- | sound/soc/au1x/psc.h | 7 | ||||
-rw-r--r-- | sound/soc/au1x/sample-ac97.c | 144 |
8 files changed, 532 insertions, 337 deletions
diff --git a/sound/soc/au1x/Kconfig b/sound/soc/au1x/Kconfig index 410a893aa66b..4b67140fdec3 100644 --- a/sound/soc/au1x/Kconfig +++ b/sound/soc/au1x/Kconfig | |||
@@ -22,11 +22,13 @@ config SND_SOC_AU1XPSC_AC97 | |||
22 | ## | 22 | ## |
23 | ## Boards | 23 | ## Boards |
24 | ## | 24 | ## |
25 | config SND_SOC_SAMPLE_PSC_AC97 | 25 | config SND_SOC_DB1200 |
26 | tristate "Sample Au12x0/Au1550 PSC AC97 sound machine" | 26 | tristate "DB1200 AC97+I2S audio support" |
27 | depends on SND_SOC_AU1XPSC | 27 | depends on SND_SOC_AU1XPSC |
28 | select SND_SOC_AU1XPSC_AC97 | 28 | select SND_SOC_AU1XPSC_AC97 |
29 | select SND_SOC_AC97_CODEC | 29 | select SND_SOC_AC97_CODEC |
30 | select SND_SOC_AU1XPSC_I2S | ||
31 | select SND_SOC_WM8731 | ||
30 | help | 32 | help |
31 | This is a sample AC97 sound machine for use in Au12x0/Au1550 | 33 | Select this option to enable audio (AC97 or I2S) on the |
32 | based systems which have audio on PSC1 (e.g. Db1200 demoboard). | 34 | Alchemy/AMD/RMI DB1200 demoboard. |
diff --git a/sound/soc/au1x/Makefile b/sound/soc/au1x/Makefile index 6c6950b8003a..16873076e8c4 100644 --- a/sound/soc/au1x/Makefile +++ b/sound/soc/au1x/Makefile | |||
@@ -8,6 +8,6 @@ obj-$(CONFIG_SND_SOC_AU1XPSC_I2S) += snd-soc-au1xpsc-i2s.o | |||
8 | obj-$(CONFIG_SND_SOC_AU1XPSC_AC97) += snd-soc-au1xpsc-ac97.o | 8 | obj-$(CONFIG_SND_SOC_AU1XPSC_AC97) += snd-soc-au1xpsc-ac97.o |
9 | 9 | ||
10 | # Boards | 10 | # Boards |
11 | snd-soc-sample-ac97-objs := sample-ac97.o | 11 | snd-soc-db1200-objs := db1200.o |
12 | 12 | ||
13 | obj-$(CONFIG_SND_SOC_SAMPLE_PSC_AC97) += snd-soc-sample-ac97.o | 13 | obj-$(CONFIG_SND_SOC_DB1200) += snd-soc-db1200.o |
diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c new file mode 100644 index 000000000000..cdf7be1b9b91 --- /dev/null +++ b/sound/soc/au1x/db1200.c | |||
@@ -0,0 +1,141 @@ | |||
1 | /* | ||
2 | * DB1200 ASoC audio fabric support code. | ||
3 | * | ||
4 | * (c) 2008-9 Manuel Lauss <manuel.lauss@gmail.com> | ||
5 | * | ||
6 | */ | ||
7 | |||
8 | #include <linux/module.h> | ||
9 | #include <linux/moduleparam.h> | ||
10 | #include <linux/timer.h> | ||
11 | #include <linux/interrupt.h> | ||
12 | #include <linux/platform_device.h> | ||
13 | #include <sound/core.h> | ||
14 | #include <sound/pcm.h> | ||
15 | #include <sound/soc.h> | ||
16 | #include <sound/soc-dapm.h> | ||
17 | #include <asm/mach-au1x00/au1000.h> | ||
18 | #include <asm/mach-au1x00/au1xxx_psc.h> | ||
19 | #include <asm/mach-au1x00/au1xxx_dbdma.h> | ||
20 | #include <asm/mach-db1x00/bcsr.h> | ||
21 | |||
22 | #include "../codecs/ac97.h" | ||
23 | #include "../codecs/wm8731.h" | ||
24 | #include "psc.h" | ||
25 | |||
26 | /*------------------------- AC97 PART ---------------------------*/ | ||
27 | |||
28 | static struct snd_soc_dai_link db1200_ac97_dai = { | ||
29 | .name = "AC97", | ||
30 | .stream_name = "AC97 HiFi", | ||
31 | .cpu_dai = &au1xpsc_ac97_dai, | ||
32 | .codec_dai = &ac97_dai, | ||
33 | }; | ||
34 | |||
35 | static struct snd_soc_card db1200_ac97_machine = { | ||
36 | .name = "DB1200_AC97", | ||
37 | .dai_link = &db1200_ac97_dai, | ||
38 | .num_links = 1, | ||
39 | .platform = &au1xpsc_soc_platform, | ||
40 | }; | ||
41 | |||
42 | static struct snd_soc_device db1200_ac97_devdata = { | ||
43 | .card = &db1200_ac97_machine, | ||
44 | .codec_dev = &soc_codec_dev_ac97, | ||
45 | }; | ||
46 | |||
47 | /*------------------------- I2S PART ---------------------------*/ | ||
48 | |||
49 | static int db1200_i2s_startup(struct snd_pcm_substream *substream) | ||
50 | { | ||
51 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
52 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
53 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
54 | int ret; | ||
55 | |||
56 | /* WM8731 has its own 12MHz crystal */ | ||
57 | snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, | ||
58 | 12000000, SND_SOC_CLOCK_IN); | ||
59 | |||
60 | /* codec is bitclock and lrclk master */ | ||
61 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_LEFT_J | | ||
62 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | ||
63 | if (ret < 0) | ||
64 | goto out; | ||
65 | |||
66 | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_LEFT_J | | ||
67 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | ||
68 | if (ret < 0) | ||
69 | goto out; | ||
70 | |||
71 | ret = 0; | ||
72 | out: | ||
73 | return ret; | ||
74 | } | ||
75 | |||
76 | static struct snd_soc_ops db1200_i2s_wm8731_ops = { | ||
77 | .startup = db1200_i2s_startup, | ||
78 | }; | ||
79 | |||
80 | static struct snd_soc_dai_link db1200_i2s_dai = { | ||
81 | .name = "WM8731", | ||
82 | .stream_name = "WM8731 PCM", | ||
83 | .cpu_dai = &au1xpsc_i2s_dai, | ||
84 | .codec_dai = &wm8731_dai, | ||
85 | .ops = &db1200_i2s_wm8731_ops, | ||
86 | }; | ||
87 | |||
88 | static struct snd_soc_card db1200_i2s_machine = { | ||
89 | .name = "DB1200_I2S", | ||
90 | .dai_link = &db1200_i2s_dai, | ||
91 | .num_links = 1, | ||
92 | .platform = &au1xpsc_soc_platform, | ||
93 | }; | ||
94 | |||
95 | static struct snd_soc_device db1200_i2s_devdata = { | ||
96 | .card = &db1200_i2s_machine, | ||
97 | .codec_dev = &soc_codec_dev_wm8731, | ||
98 | }; | ||
99 | |||
100 | /*------------------------- COMMON PART ---------------------------*/ | ||
101 | |||
102 | static struct platform_device *db1200_asoc_dev; | ||
103 | |||
104 | static int __init db1200_audio_load(void) | ||
105 | { | ||
106 | int ret; | ||
107 | |||
108 | ret = -ENOMEM; | ||
109 | db1200_asoc_dev = platform_device_alloc("soc-audio", -1); | ||
110 | if (!db1200_asoc_dev) | ||
111 | goto out; | ||
112 | |||
113 | /* DB1200 board setup set PSC1MUX to preferred audio device */ | ||
114 | if (bcsr_read(BCSR_RESETS) & BCSR_RESETS_PSC1MUX) | ||
115 | platform_set_drvdata(db1200_asoc_dev, &db1200_i2s_devdata); | ||
116 | else | ||
117 | platform_set_drvdata(db1200_asoc_dev, &db1200_ac97_devdata); | ||
118 | |||
119 | db1200_ac97_devdata.dev = &db1200_asoc_dev->dev; | ||
120 | db1200_i2s_devdata.dev = &db1200_asoc_dev->dev; | ||
121 | ret = platform_device_add(db1200_asoc_dev); | ||
122 | |||
123 | if (ret) { | ||
124 | platform_device_put(db1200_asoc_dev); | ||
125 | db1200_asoc_dev = NULL; | ||
126 | } | ||
127 | out: | ||
128 | return ret; | ||
129 | } | ||
130 | |||
131 | static void __exit db1200_audio_unload(void) | ||
132 | { | ||
133 | platform_device_unregister(db1200_asoc_dev); | ||
134 | } | ||
135 | |||
136 | module_init(db1200_audio_load); | ||
137 | module_exit(db1200_audio_unload); | ||
138 | |||
139 | MODULE_LICENSE("GPL"); | ||
140 | MODULE_DESCRIPTION("DB1200 ASoC audio support"); | ||
141 | MODULE_AUTHOR("Manuel Lauss"); | ||
diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c index 594c6c5b7838..6d9f4c624949 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 |
@@ -51,8 +51,8 @@ struct au1xpsc_audio_dmadata { | |||
51 | struct snd_pcm_substream *substream; | 51 | struct snd_pcm_substream *substream; |
52 | unsigned long curr_period; /* current segment DDMA is working on */ | 52 | unsigned long curr_period; /* current segment DDMA is working on */ |
53 | unsigned long q_period; /* queue period(s) */ | 53 | unsigned long q_period; /* queue period(s) */ |
54 | unsigned long dma_area; /* address of queued DMA area */ | 54 | dma_addr_t dma_area; /* address of queued DMA area */ |
55 | unsigned long dma_area_s; /* start address of DMA area */ | 55 | dma_addr_t dma_area_s; /* start address of DMA area */ |
56 | unsigned long pos; /* current byte position being played */ | 56 | unsigned long pos; /* current byte position being played */ |
57 | unsigned long periods; /* number of SG segments in total */ | 57 | unsigned long periods; /* number of SG segments in total */ |
58 | unsigned long period_bytes; /* size in bytes of one SG segment */ | 58 | unsigned long period_bytes; /* size in bytes of one SG segment */ |
@@ -94,8 +94,7 @@ static const struct snd_pcm_hardware au1xpsc_pcm_hardware = { | |||
94 | 94 | ||
95 | static void au1x_pcm_queue_tx(struct au1xpsc_audio_dmadata *cd) | 95 | static void au1x_pcm_queue_tx(struct au1xpsc_audio_dmadata *cd) |
96 | { | 96 | { |
97 | au1xxx_dbdma_put_source_flags(cd->ddma_chan, | 97 | au1xxx_dbdma_put_source(cd->ddma_chan, cd->dma_area, |
98 | (void *)phys_to_virt(cd->dma_area), | ||
99 | cd->period_bytes, DDMA_FLAGS_IE); | 98 | cd->period_bytes, DDMA_FLAGS_IE); |
100 | 99 | ||
101 | /* update next-to-queue period */ | 100 | /* update next-to-queue period */ |
@@ -109,9 +108,8 @@ static void au1x_pcm_queue_tx(struct au1xpsc_audio_dmadata *cd) | |||
109 | 108 | ||
110 | static void au1x_pcm_queue_rx(struct au1xpsc_audio_dmadata *cd) | 109 | static void au1x_pcm_queue_rx(struct au1xpsc_audio_dmadata *cd) |
111 | { | 110 | { |
112 | au1xxx_dbdma_put_dest_flags(cd->ddma_chan, | 111 | au1xxx_dbdma_put_dest(cd->ddma_chan, cd->dma_area, |
113 | (void *)phys_to_virt(cd->dma_area), | 112 | cd->period_bytes, DDMA_FLAGS_IE); |
114 | cd->period_bytes, DDMA_FLAGS_IE); | ||
115 | 113 | ||
116 | /* update next-to-queue period */ | 114 | /* update next-to-queue period */ |
117 | ++cd->q_period; | 115 | ++cd->q_period; |
@@ -233,7 +231,7 @@ static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream, | |||
233 | pcd->substream = substream; | 231 | pcd->substream = substream; |
234 | pcd->period_bytes = params_period_bytes(params); | 232 | pcd->period_bytes = params_period_bytes(params); |
235 | pcd->periods = params_periods(params); | 233 | pcd->periods = params_periods(params); |
236 | pcd->dma_area_s = pcd->dma_area = (unsigned long)runtime->dma_addr; | 234 | pcd->dma_area_s = pcd->dma_area = runtime->dma_addr; |
237 | pcd->q_period = 0; | 235 | pcd->q_period = 0; |
238 | pcd->curr_period = 0; | 236 | pcd->curr_period = 0; |
239 | pcd->pos = 0; | 237 | pcd->pos = 0; |
@@ -333,6 +331,30 @@ static int au1xpsc_pcm_new(struct snd_card *card, | |||
333 | 331 | ||
334 | static int au1xpsc_pcm_probe(struct platform_device *pdev) | 332 | static int au1xpsc_pcm_probe(struct platform_device *pdev) |
335 | { | 333 | { |
334 | if (!au1xpsc_audio_pcmdma[PCM_TX] || !au1xpsc_audio_pcmdma[PCM_RX]) | ||
335 | return -ENODEV; | ||
336 | |||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | static int au1xpsc_pcm_remove(struct platform_device *pdev) | ||
341 | { | ||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | /* au1xpsc audio platform */ | ||
346 | struct snd_soc_platform au1xpsc_soc_platform = { | ||
347 | .name = "au1xpsc-pcm-dbdma", | ||
348 | .probe = au1xpsc_pcm_probe, | ||
349 | .remove = au1xpsc_pcm_remove, | ||
350 | .pcm_ops = &au1xpsc_pcm_ops, | ||
351 | .pcm_new = au1xpsc_pcm_new, | ||
352 | .pcm_free = au1xpsc_pcm_free_dma_buffers, | ||
353 | }; | ||
354 | EXPORT_SYMBOL_GPL(au1xpsc_soc_platform); | ||
355 | |||
356 | static int __devinit au1xpsc_pcm_drvprobe(struct platform_device *pdev) | ||
357 | { | ||
336 | struct resource *r; | 358 | struct resource *r; |
337 | int ret; | 359 | int ret; |
338 | 360 | ||
@@ -365,7 +387,9 @@ static int au1xpsc_pcm_probe(struct platform_device *pdev) | |||
365 | } | 387 | } |
366 | (au1xpsc_audio_pcmdma[PCM_RX])->ddma_id = r->start; | 388 | (au1xpsc_audio_pcmdma[PCM_RX])->ddma_id = r->start; |
367 | 389 | ||
368 | return 0; | 390 | ret = snd_soc_register_platform(&au1xpsc_soc_platform); |
391 | if (!ret) | ||
392 | return ret; | ||
369 | 393 | ||
370 | out2: | 394 | out2: |
371 | kfree(au1xpsc_audio_pcmdma[PCM_RX]); | 395 | kfree(au1xpsc_audio_pcmdma[PCM_RX]); |
@@ -376,10 +400,12 @@ out1: | |||
376 | return ret; | 400 | return ret; |
377 | } | 401 | } |
378 | 402 | ||
379 | static int au1xpsc_pcm_remove(struct platform_device *pdev) | 403 | static int __devexit au1xpsc_pcm_drvremove(struct platform_device *pdev) |
380 | { | 404 | { |
381 | int i; | 405 | int i; |
382 | 406 | ||
407 | snd_soc_unregister_platform(&au1xpsc_soc_platform); | ||
408 | |||
383 | for (i = 0; i < 2; i++) { | 409 | for (i = 0; i < 2; i++) { |
384 | if (au1xpsc_audio_pcmdma[i]) { | 410 | if (au1xpsc_audio_pcmdma[i]) { |
385 | au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[i]); | 411 | au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[i]); |
@@ -391,32 +417,81 @@ static int au1xpsc_pcm_remove(struct platform_device *pdev) | |||
391 | return 0; | 417 | return 0; |
392 | } | 418 | } |
393 | 419 | ||
394 | /* au1xpsc audio platform */ | 420 | static struct platform_driver au1xpsc_pcm_driver = { |
395 | struct snd_soc_platform au1xpsc_soc_platform = { | 421 | .driver = { |
396 | .name = "au1xpsc-pcm-dbdma", | 422 | .name = "au1xpsc-pcm", |
397 | .probe = au1xpsc_pcm_probe, | 423 | .owner = THIS_MODULE, |
398 | .remove = au1xpsc_pcm_remove, | 424 | }, |
399 | .pcm_ops = &au1xpsc_pcm_ops, | 425 | .probe = au1xpsc_pcm_drvprobe, |
400 | .pcm_new = au1xpsc_pcm_new, | 426 | .remove = __devexit_p(au1xpsc_pcm_drvremove), |
401 | .pcm_free = au1xpsc_pcm_free_dma_buffers, | ||
402 | }; | 427 | }; |
403 | EXPORT_SYMBOL_GPL(au1xpsc_soc_platform); | ||
404 | 428 | ||
405 | static int __init au1xpsc_audio_dbdma_init(void) | 429 | static int __init au1xpsc_audio_dbdma_load(void) |
406 | { | 430 | { |
407 | au1xpsc_audio_pcmdma[PCM_TX] = NULL; | 431 | au1xpsc_audio_pcmdma[PCM_TX] = NULL; |
408 | au1xpsc_audio_pcmdma[PCM_RX] = NULL; | 432 | au1xpsc_audio_pcmdma[PCM_RX] = NULL; |
409 | return snd_soc_register_platform(&au1xpsc_soc_platform); | 433 | return platform_driver_register(&au1xpsc_pcm_driver); |
410 | } | 434 | } |
411 | 435 | ||
412 | static void __exit au1xpsc_audio_dbdma_exit(void) | 436 | static void __exit au1xpsc_audio_dbdma_unload(void) |
413 | { | 437 | { |
414 | snd_soc_unregister_platform(&au1xpsc_soc_platform); | 438 | platform_driver_unregister(&au1xpsc_pcm_driver); |
439 | } | ||
440 | |||
441 | module_init(au1xpsc_audio_dbdma_load); | ||
442 | module_exit(au1xpsc_audio_dbdma_unload); | ||
443 | |||
444 | |||
445 | struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev) | ||
446 | { | ||
447 | struct resource *res, *r; | ||
448 | struct platform_device *pd; | ||
449 | int id[2]; | ||
450 | int ret; | ||
451 | |||
452 | r = platform_get_resource(pdev, IORESOURCE_DMA, 0); | ||
453 | if (!r) | ||
454 | return NULL; | ||
455 | id[0] = r->start; | ||
456 | |||
457 | r = platform_get_resource(pdev, IORESOURCE_DMA, 1); | ||
458 | if (!r) | ||
459 | return NULL; | ||
460 | id[1] = r->start; | ||
461 | |||
462 | res = kzalloc(sizeof(struct resource) * 2, GFP_KERNEL); | ||
463 | if (!res) | ||
464 | return NULL; | ||
465 | |||
466 | res[0].start = res[0].end = id[0]; | ||
467 | res[1].start = res[1].end = id[1]; | ||
468 | res[0].flags = res[1].flags = IORESOURCE_DMA; | ||
469 | |||
470 | pd = platform_device_alloc("au1xpsc-pcm", -1); | ||
471 | if (!pd) | ||
472 | goto out; | ||
473 | |||
474 | pd->resource = res; | ||
475 | pd->num_resources = 2; | ||
476 | |||
477 | ret = platform_device_add(pd); | ||
478 | if (!ret) | ||
479 | return pd; | ||
480 | |||
481 | platform_device_put(pd); | ||
482 | out: | ||
483 | kfree(res); | ||
484 | return NULL; | ||
415 | } | 485 | } |
486 | EXPORT_SYMBOL_GPL(au1xpsc_pcm_add); | ||
416 | 487 | ||
417 | module_init(au1xpsc_audio_dbdma_init); | 488 | void au1xpsc_pcm_destroy(struct platform_device *dmapd) |
418 | module_exit(au1xpsc_audio_dbdma_exit); | 489 | { |
490 | if (dmapd) | ||
491 | platform_device_unregister(dmapd); | ||
492 | } | ||
493 | EXPORT_SYMBOL_GPL(au1xpsc_pcm_destroy); | ||
419 | 494 | ||
420 | MODULE_LICENSE("GPL"); | 495 | MODULE_LICENSE("GPL"); |
421 | MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver"); | 496 | MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver"); |
422 | MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); | 497 | MODULE_AUTHOR("Manuel Lauss"); |
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index a521aa90ddee..a61ccd2d505f 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c | |||
@@ -17,6 +17,7 @@ | |||
17 | 17 | ||
18 | #include <linux/init.h> | 18 | #include <linux/init.h> |
19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
20 | #include <linux/slab.h> | ||
20 | #include <linux/device.h> | 21 | #include <linux/device.h> |
21 | #include <linux/delay.h> | 22 | #include <linux/delay.h> |
22 | #include <linux/mutex.h> | 23 | #include <linux/mutex.h> |
@@ -61,7 +62,8 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97, | |||
61 | { | 62 | { |
62 | /* FIXME */ | 63 | /* FIXME */ |
63 | struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; | 64 | struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; |
64 | unsigned short data, retry, tmo; | 65 | unsigned short retry, tmo; |
66 | unsigned long data; | ||
65 | 67 | ||
66 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); | 68 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); |
67 | au_sync(); | 69 | au_sync(); |
@@ -74,20 +76,26 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97, | |||
74 | AC97_CDC(pscdata)); | 76 | AC97_CDC(pscdata)); |
75 | au_sync(); | 77 | au_sync(); |
76 | 78 | ||
77 | tmo = 2000; | 79 | tmo = 20; |
78 | while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) | 80 | do { |
79 | && --tmo) | 81 | udelay(21); |
80 | udelay(2); | 82 | if (au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD) |
83 | break; | ||
84 | } while (--tmo); | ||
81 | 85 | ||
82 | data = au_readl(AC97_CDC(pscdata)) & 0xffff; | 86 | data = au_readl(AC97_CDC(pscdata)); |
83 | 87 | ||
84 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); | 88 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); |
85 | au_sync(); | 89 | au_sync(); |
86 | 90 | ||
87 | mutex_unlock(&pscdata->lock); | 91 | mutex_unlock(&pscdata->lock); |
92 | |||
93 | if (reg != ((data >> 16) & 0x7f)) | ||
94 | tmo = 1; /* wrong register, try again */ | ||
95 | |||
88 | } while (--retry && !tmo); | 96 | } while (--retry && !tmo); |
89 | 97 | ||
90 | return retry ? data : 0xffff; | 98 | return retry ? data & 0xffff : 0xffff; |
91 | } | 99 | } |
92 | 100 | ||
93 | /* AC97 controller writes to codec register */ | 101 | /* AC97 controller writes to codec register */ |
@@ -109,10 +117,12 @@ static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg, | |||
109 | AC97_CDC(pscdata)); | 117 | AC97_CDC(pscdata)); |
110 | au_sync(); | 118 | au_sync(); |
111 | 119 | ||
112 | tmo = 2000; | 120 | tmo = 20; |
113 | while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) | 121 | do { |
114 | && --tmo) | 122 | udelay(21); |
115 | udelay(2); | 123 | if (au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD) |
124 | break; | ||
125 | } while (--tmo); | ||
116 | 126 | ||
117 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); | 127 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); |
118 | au_sync(); | 128 | au_sync(); |
@@ -195,7 +205,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, | |||
195 | /* FIXME */ | 205 | /* FIXME */ |
196 | struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; | 206 | struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; |
197 | unsigned long r, ro, stat; | 207 | unsigned long r, ro, stat; |
198 | int chans, stype = SUBSTREAM_TYPE(substream); | 208 | int chans, t, stype = SUBSTREAM_TYPE(substream); |
199 | 209 | ||
200 | chans = params_channels(params); | 210 | chans = params_channels(params); |
201 | 211 | ||
@@ -237,8 +247,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, | |||
237 | au_sync(); | 247 | au_sync(); |
238 | 248 | ||
239 | /* ...wait for it... */ | 249 | /* ...wait for it... */ |
240 | while (au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR) | 250 | t = 100; |
241 | asm volatile ("nop"); | 251 | while ((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR) && --t) |
252 | msleep(1); | ||
253 | |||
254 | if (!t) | ||
255 | printk(KERN_ERR "PSC-AC97: can't disable!\n"); | ||
242 | 256 | ||
243 | /* ...write config... */ | 257 | /* ...write config... */ |
244 | au_writel(r, AC97_CFG(pscdata)); | 258 | au_writel(r, AC97_CFG(pscdata)); |
@@ -249,8 +263,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, | |||
249 | au_sync(); | 263 | au_sync(); |
250 | 264 | ||
251 | /* ...and wait for ready bit */ | 265 | /* ...and wait for ready bit */ |
252 | while (!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) | 266 | t = 100; |
253 | asm volatile ("nop"); | 267 | while ((!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && --t) |
268 | msleep(1); | ||
269 | |||
270 | if (!t) | ||
271 | printk(KERN_ERR "PSC-AC97: can't enable!\n"); | ||
254 | 272 | ||
255 | mutex_unlock(&pscdata->lock); | 273 | mutex_unlock(&pscdata->lock); |
256 | 274 | ||
@@ -300,19 +318,55 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream, | |||
300 | static int au1xpsc_ac97_probe(struct platform_device *pdev, | 318 | static int au1xpsc_ac97_probe(struct platform_device *pdev, |
301 | struct snd_soc_dai *dai) | 319 | struct snd_soc_dai *dai) |
302 | { | 320 | { |
321 | return au1xpsc_ac97_workdata ? 0 : -ENODEV; | ||
322 | } | ||
323 | |||
324 | static void au1xpsc_ac97_remove(struct platform_device *pdev, | ||
325 | struct snd_soc_dai *dai) | ||
326 | { | ||
327 | } | ||
328 | |||
329 | static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = { | ||
330 | .trigger = au1xpsc_ac97_trigger, | ||
331 | .hw_params = au1xpsc_ac97_hw_params, | ||
332 | }; | ||
333 | |||
334 | struct snd_soc_dai au1xpsc_ac97_dai = { | ||
335 | .name = "au1xpsc_ac97", | ||
336 | .ac97_control = 1, | ||
337 | .probe = au1xpsc_ac97_probe, | ||
338 | .remove = au1xpsc_ac97_remove, | ||
339 | .playback = { | ||
340 | .rates = AC97_RATES, | ||
341 | .formats = AC97_FMTS, | ||
342 | .channels_min = 2, | ||
343 | .channels_max = 2, | ||
344 | }, | ||
345 | .capture = { | ||
346 | .rates = AC97_RATES, | ||
347 | .formats = AC97_FMTS, | ||
348 | .channels_min = 2, | ||
349 | .channels_max = 2, | ||
350 | }, | ||
351 | .ops = &au1xpsc_ac97_dai_ops, | ||
352 | }; | ||
353 | EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai); | ||
354 | |||
355 | static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev) | ||
356 | { | ||
303 | int ret; | 357 | int ret; |
304 | struct resource *r; | 358 | struct resource *r; |
305 | unsigned long sel; | 359 | unsigned long sel; |
360 | struct au1xpsc_audio_data *wd; | ||
306 | 361 | ||
307 | if (au1xpsc_ac97_workdata) | 362 | if (au1xpsc_ac97_workdata) |
308 | return -EBUSY; | 363 | return -EBUSY; |
309 | 364 | ||
310 | au1xpsc_ac97_workdata = | 365 | wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL); |
311 | kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL); | 366 | if (!wd) |
312 | if (!au1xpsc_ac97_workdata) | ||
313 | return -ENOMEM; | 367 | return -ENOMEM; |
314 | 368 | ||
315 | mutex_init(&au1xpsc_ac97_workdata->lock); | 369 | mutex_init(&wd->lock); |
316 | 370 | ||
317 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 371 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
318 | if (!r) { | 372 | if (!r) { |
@@ -321,81 +375,95 @@ static int au1xpsc_ac97_probe(struct platform_device *pdev, | |||
321 | } | 375 | } |
322 | 376 | ||
323 | ret = -EBUSY; | 377 | ret = -EBUSY; |
324 | au1xpsc_ac97_workdata->ioarea = | 378 | 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"); | 379 | "au1xpsc_ac97"); |
327 | if (!au1xpsc_ac97_workdata->ioarea) | 380 | if (!wd->ioarea) |
328 | goto out0; | 381 | goto out0; |
329 | 382 | ||
330 | au1xpsc_ac97_workdata->mmio = ioremap(r->start, 0xffff); | 383 | wd->mmio = ioremap(r->start, 0xffff); |
331 | if (!au1xpsc_ac97_workdata->mmio) | 384 | if (!wd->mmio) |
332 | goto out1; | 385 | goto out1; |
333 | 386 | ||
334 | /* configuration: max dma trigger threshold, enable ac97 */ | 387 | /* configuration: max dma trigger threshold, enable ac97 */ |
335 | au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 | | 388 | wd->cfg = PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8 | |
336 | PSC_AC97CFG_TT_FIFO8 | | 389 | PSC_AC97CFG_DE_ENABLE; |
337 | PSC_AC97CFG_DE_ENABLE; | ||
338 | 390 | ||
339 | /* preserve PSC clock source set up by platform (dev.platform_data | 391 | /* preserve PSC clock source set up by platform */ |
340 | * is already occupied by soc layer) | 392 | sel = au_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK; |
341 | */ | 393 | 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(); | 394 | au_sync(); |
345 | au_writel(0, PSC_SEL(au1xpsc_ac97_workdata)); | 395 | au_writel(0, PSC_SEL(wd)); |
346 | au_sync(); | 396 | au_sync(); |
347 | au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(au1xpsc_ac97_workdata)); | 397 | au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(wd)); |
348 | au_sync(); | 398 | 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 | 399 | ||
353 | return 0; | 400 | ret = snd_soc_register_dai(&au1xpsc_ac97_dai); |
401 | if (ret) | ||
402 | goto out1; | ||
354 | 403 | ||
404 | wd->dmapd = au1xpsc_pcm_add(pdev); | ||
405 | if (wd->dmapd) { | ||
406 | platform_set_drvdata(pdev, wd); | ||
407 | au1xpsc_ac97_workdata = wd; /* MDEV */ | ||
408 | return 0; | ||
409 | } | ||
410 | |||
411 | snd_soc_unregister_dai(&au1xpsc_ac97_dai); | ||
355 | out1: | 412 | out1: |
356 | release_resource(au1xpsc_ac97_workdata->ioarea); | 413 | release_resource(wd->ioarea); |
357 | kfree(au1xpsc_ac97_workdata->ioarea); | 414 | kfree(wd->ioarea); |
358 | out0: | 415 | out0: |
359 | kfree(au1xpsc_ac97_workdata); | 416 | kfree(wd); |
360 | au1xpsc_ac97_workdata = NULL; | ||
361 | return ret; | 417 | return ret; |
362 | } | 418 | } |
363 | 419 | ||
364 | static void au1xpsc_ac97_remove(struct platform_device *pdev, | 420 | static int __devexit au1xpsc_ac97_drvremove(struct platform_device *pdev) |
365 | struct snd_soc_dai *dai) | ||
366 | { | 421 | { |
422 | struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev); | ||
423 | |||
424 | if (wd->dmapd) | ||
425 | au1xpsc_pcm_destroy(wd->dmapd); | ||
426 | |||
427 | snd_soc_unregister_dai(&au1xpsc_ac97_dai); | ||
428 | |||
367 | /* disable PSC completely */ | 429 | /* disable PSC completely */ |
368 | au_writel(0, AC97_CFG(au1xpsc_ac97_workdata)); | 430 | au_writel(0, AC97_CFG(wd)); |
369 | au_sync(); | 431 | au_sync(); |
370 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata)); | 432 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); |
371 | au_sync(); | 433 | au_sync(); |
372 | 434 | ||
373 | iounmap(au1xpsc_ac97_workdata->mmio); | 435 | iounmap(wd->mmio); |
374 | release_resource(au1xpsc_ac97_workdata->ioarea); | 436 | release_resource(wd->ioarea); |
375 | kfree(au1xpsc_ac97_workdata->ioarea); | 437 | kfree(wd->ioarea); |
376 | kfree(au1xpsc_ac97_workdata); | 438 | kfree(wd); |
377 | au1xpsc_ac97_workdata = NULL; | 439 | |
440 | au1xpsc_ac97_workdata = NULL; /* MDEV */ | ||
441 | |||
442 | return 0; | ||
378 | } | 443 | } |
379 | 444 | ||
380 | static int au1xpsc_ac97_suspend(struct snd_soc_dai *dai) | 445 | #ifdef CONFIG_PM |
446 | static int au1xpsc_ac97_drvsuspend(struct device *dev) | ||
381 | { | 447 | { |
448 | struct au1xpsc_audio_data *wd = dev_get_drvdata(dev); | ||
449 | |||
382 | /* save interesting registers and disable PSC */ | 450 | /* save interesting registers and disable PSC */ |
383 | au1xpsc_ac97_workdata->pm[0] = | 451 | wd->pm[0] = au_readl(PSC_SEL(wd)); |
384 | au_readl(PSC_SEL(au1xpsc_ac97_workdata)); | ||
385 | 452 | ||
386 | au_writel(0, AC97_CFG(au1xpsc_ac97_workdata)); | 453 | au_writel(0, AC97_CFG(wd)); |
387 | au_sync(); | 454 | au_sync(); |
388 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata)); | 455 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); |
389 | au_sync(); | 456 | au_sync(); |
390 | 457 | ||
391 | return 0; | 458 | return 0; |
392 | } | 459 | } |
393 | 460 | ||
394 | static int au1xpsc_ac97_resume(struct snd_soc_dai *dai) | 461 | static int au1xpsc_ac97_drvresume(struct device *dev) |
395 | { | 462 | { |
463 | struct au1xpsc_audio_data *wd = dev_get_drvdata(dev); | ||
464 | |||
396 | /* restore PSC clock config */ | 465 | /* restore PSC clock config */ |
397 | au_writel(au1xpsc_ac97_workdata->pm[0] | PSC_SEL_PS_AC97MODE, | 466 | au_writel(wd->pm[0] | PSC_SEL_PS_AC97MODE, PSC_SEL(wd)); |
398 | PSC_SEL(au1xpsc_ac97_workdata)); | ||
399 | au_sync(); | 467 | au_sync(); |
400 | 468 | ||
401 | /* after this point the ac97 core will cold-reset the codec. | 469 | /* after this point the ac97 core will cold-reset the codec. |
@@ -405,48 +473,44 @@ static int au1xpsc_ac97_resume(struct snd_soc_dai *dai) | |||
405 | return 0; | 473 | return 0; |
406 | } | 474 | } |
407 | 475 | ||
408 | static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = { | 476 | static struct dev_pm_ops au1xpscac97_pmops = { |
409 | .trigger = au1xpsc_ac97_trigger, | 477 | .suspend = au1xpsc_ac97_drvsuspend, |
410 | .hw_params = au1xpsc_ac97_hw_params, | 478 | .resume = au1xpsc_ac97_drvresume, |
411 | }; | 479 | }; |
412 | 480 | ||
413 | struct snd_soc_dai au1xpsc_ac97_dai = { | 481 | #define AU1XPSCAC97_PMOPS &au1xpscac97_pmops |
414 | .name = "au1xpsc_ac97", | 482 | |
415 | .ac97_control = 1, | 483 | #else |
416 | .probe = au1xpsc_ac97_probe, | 484 | |
417 | .remove = au1xpsc_ac97_remove, | 485 | #define AU1XPSCAC97_PMOPS NULL |
418 | .suspend = au1xpsc_ac97_suspend, | 486 | |
419 | .resume = au1xpsc_ac97_resume, | 487 | #endif |
420 | .playback = { | 488 | |
421 | .rates = AC97_RATES, | 489 | static struct platform_driver au1xpsc_ac97_driver = { |
422 | .formats = AC97_FMTS, | 490 | .driver = { |
423 | .channels_min = 2, | 491 | .name = "au1xpsc_ac97", |
424 | .channels_max = 2, | 492 | .owner = THIS_MODULE, |
425 | }, | 493 | .pm = AU1XPSCAC97_PMOPS, |
426 | .capture = { | ||
427 | .rates = AC97_RATES, | ||
428 | .formats = AC97_FMTS, | ||
429 | .channels_min = 2, | ||
430 | .channels_max = 2, | ||
431 | }, | 494 | }, |
432 | .ops = &au1xpsc_ac97_dai_ops, | 495 | .probe = au1xpsc_ac97_drvprobe, |
496 | .remove = __devexit_p(au1xpsc_ac97_drvremove), | ||
433 | }; | 497 | }; |
434 | EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai); | ||
435 | 498 | ||
436 | static int __init au1xpsc_ac97_init(void) | 499 | static int __init au1xpsc_ac97_load(void) |
437 | { | 500 | { |
438 | au1xpsc_ac97_workdata = NULL; | 501 | au1xpsc_ac97_workdata = NULL; |
439 | return snd_soc_register_dai(&au1xpsc_ac97_dai); | 502 | return platform_driver_register(&au1xpsc_ac97_driver); |
440 | } | 503 | } |
441 | 504 | ||
442 | static void __exit au1xpsc_ac97_exit(void) | 505 | static void __exit au1xpsc_ac97_unload(void) |
443 | { | 506 | { |
444 | snd_soc_unregister_dai(&au1xpsc_ac97_dai); | 507 | platform_driver_unregister(&au1xpsc_ac97_driver); |
445 | } | 508 | } |
446 | 509 | ||
447 | module_init(au1xpsc_ac97_init); | 510 | module_init(au1xpsc_ac97_load); |
448 | module_exit(au1xpsc_ac97_exit); | 511 | module_exit(au1xpsc_ac97_unload); |
449 | 512 | ||
450 | MODULE_LICENSE("GPL"); | 513 | MODULE_LICENSE("GPL"); |
451 | MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver"); | 514 | MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver"); |
452 | MODULE_AUTHOR("Manuel Lauss <manuel.lauss@gmail.com>"); | 515 | MODULE_AUTHOR("Manuel Lauss"); |
516 | |||
diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c index bb589327ee32..495be6e71931 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 |
@@ -18,6 +18,7 @@ | |||
18 | 18 | ||
19 | #include <linux/init.h> | 19 | #include <linux/init.h> |
20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
21 | #include <linux/slab.h> | ||
21 | #include <linux/suspend.h> | 22 | #include <linux/suspend.h> |
22 | #include <sound/core.h> | 23 | #include <sound/core.h> |
23 | #include <sound/pcm.h> | 24 | #include <sound/pcm.h> |
@@ -265,16 +266,52 @@ static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, | |||
265 | static int au1xpsc_i2s_probe(struct platform_device *pdev, | 266 | static int au1xpsc_i2s_probe(struct platform_device *pdev, |
266 | struct snd_soc_dai *dai) | 267 | struct snd_soc_dai *dai) |
267 | { | 268 | { |
269 | return au1xpsc_i2s_workdata ? 0 : -ENODEV; | ||
270 | } | ||
271 | |||
272 | static void au1xpsc_i2s_remove(struct platform_device *pdev, | ||
273 | struct snd_soc_dai *dai) | ||
274 | { | ||
275 | } | ||
276 | |||
277 | static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = { | ||
278 | .trigger = au1xpsc_i2s_trigger, | ||
279 | .hw_params = au1xpsc_i2s_hw_params, | ||
280 | .set_fmt = au1xpsc_i2s_set_fmt, | ||
281 | }; | ||
282 | |||
283 | struct snd_soc_dai au1xpsc_i2s_dai = { | ||
284 | .name = "au1xpsc_i2s", | ||
285 | .probe = au1xpsc_i2s_probe, | ||
286 | .remove = au1xpsc_i2s_remove, | ||
287 | .playback = { | ||
288 | .rates = AU1XPSC_I2S_RATES, | ||
289 | .formats = AU1XPSC_I2S_FMTS, | ||
290 | .channels_min = 2, | ||
291 | .channels_max = 8, /* 2 without external help */ | ||
292 | }, | ||
293 | .capture = { | ||
294 | .rates = AU1XPSC_I2S_RATES, | ||
295 | .formats = AU1XPSC_I2S_FMTS, | ||
296 | .channels_min = 2, | ||
297 | .channels_max = 8, /* 2 without external help */ | ||
298 | }, | ||
299 | .ops = &au1xpsc_i2s_dai_ops, | ||
300 | }; | ||
301 | EXPORT_SYMBOL(au1xpsc_i2s_dai); | ||
302 | |||
303 | static int __init au1xpsc_i2s_drvprobe(struct platform_device *pdev) | ||
304 | { | ||
268 | struct resource *r; | 305 | struct resource *r; |
269 | unsigned long sel; | 306 | unsigned long sel; |
270 | int ret; | 307 | int ret; |
308 | struct au1xpsc_audio_data *wd; | ||
271 | 309 | ||
272 | if (au1xpsc_i2s_workdata) | 310 | if (au1xpsc_i2s_workdata) |
273 | return -EBUSY; | 311 | return -EBUSY; |
274 | 312 | ||
275 | au1xpsc_i2s_workdata = | 313 | wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL); |
276 | kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL); | 314 | if (!wd) |
277 | if (!au1xpsc_i2s_workdata) | ||
278 | return -ENOMEM; | 315 | return -ENOMEM; |
279 | 316 | ||
280 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 317 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
@@ -284,131 +321,146 @@ static int au1xpsc_i2s_probe(struct platform_device *pdev, | |||
284 | } | 321 | } |
285 | 322 | ||
286 | ret = -EBUSY; | 323 | ret = -EBUSY; |
287 | au1xpsc_i2s_workdata->ioarea = | 324 | 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"); | 325 | "au1xpsc_i2s"); |
290 | if (!au1xpsc_i2s_workdata->ioarea) | 326 | if (!wd->ioarea) |
291 | goto out0; | 327 | goto out0; |
292 | 328 | ||
293 | au1xpsc_i2s_workdata->mmio = ioremap(r->start, 0xffff); | 329 | wd->mmio = ioremap(r->start, 0xffff); |
294 | if (!au1xpsc_i2s_workdata->mmio) | 330 | if (!wd->mmio) |
295 | goto out1; | 331 | goto out1; |
296 | 332 | ||
297 | /* preserve PSC clock source set up by platform (dev.platform_data | 333 | /* preserve PSC clock source set up by platform (dev.platform_data |
298 | * is already occupied by soc layer) | 334 | * is already occupied by soc layer) |
299 | */ | 335 | */ |
300 | sel = au_readl(PSC_SEL(au1xpsc_i2s_workdata)) & PSC_SEL_CLK_MASK; | 336 | sel = au_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK; |
301 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata)); | 337 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); |
302 | au_sync(); | 338 | au_sync(); |
303 | au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(au1xpsc_i2s_workdata)); | 339 | au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(wd)); |
304 | au_writel(0, I2S_CFG(au1xpsc_i2s_workdata)); | 340 | au_writel(0, I2S_CFG(wd)); |
305 | au_sync(); | 341 | au_sync(); |
306 | 342 | ||
307 | /* preconfigure: set max rx/tx fifo depths */ | 343 | /* preconfigure: set max rx/tx fifo depths */ |
308 | au1xpsc_i2s_workdata->cfg |= | 344 | wd->cfg |= PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8; |
309 | PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8; | ||
310 | 345 | ||
311 | /* don't wait for I2S core to become ready now; clocks may not | 346 | /* 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 | 347 | * be running yet; depending on clock input for PSC a wait might |
313 | * time out. | 348 | * time out. |
314 | */ | 349 | */ |
315 | 350 | ||
316 | return 0; | 351 | ret = snd_soc_register_dai(&au1xpsc_i2s_dai); |
352 | if (ret) | ||
353 | goto out1; | ||
317 | 354 | ||
355 | /* finally add the DMA device for this PSC */ | ||
356 | wd->dmapd = au1xpsc_pcm_add(pdev); | ||
357 | if (wd->dmapd) { | ||
358 | platform_set_drvdata(pdev, wd); | ||
359 | au1xpsc_i2s_workdata = wd; | ||
360 | return 0; | ||
361 | } | ||
362 | |||
363 | snd_soc_unregister_dai(&au1xpsc_i2s_dai); | ||
318 | out1: | 364 | out1: |
319 | release_resource(au1xpsc_i2s_workdata->ioarea); | 365 | release_resource(wd->ioarea); |
320 | kfree(au1xpsc_i2s_workdata->ioarea); | 366 | kfree(wd->ioarea); |
321 | out0: | 367 | out0: |
322 | kfree(au1xpsc_i2s_workdata); | 368 | kfree(wd); |
323 | au1xpsc_i2s_workdata = NULL; | ||
324 | return ret; | 369 | return ret; |
325 | } | 370 | } |
326 | 371 | ||
327 | static void au1xpsc_i2s_remove(struct platform_device *pdev, | 372 | static int __devexit au1xpsc_i2s_drvremove(struct platform_device *pdev) |
328 | struct snd_soc_dai *dai) | ||
329 | { | 373 | { |
330 | au_writel(0, I2S_CFG(au1xpsc_i2s_workdata)); | 374 | struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev); |
375 | |||
376 | if (wd->dmapd) | ||
377 | au1xpsc_pcm_destroy(wd->dmapd); | ||
378 | |||
379 | snd_soc_unregister_dai(&au1xpsc_i2s_dai); | ||
380 | |||
381 | au_writel(0, I2S_CFG(wd)); | ||
331 | au_sync(); | 382 | au_sync(); |
332 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata)); | 383 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); |
333 | au_sync(); | 384 | au_sync(); |
334 | 385 | ||
335 | iounmap(au1xpsc_i2s_workdata->mmio); | 386 | iounmap(wd->mmio); |
336 | release_resource(au1xpsc_i2s_workdata->ioarea); | 387 | release_resource(wd->ioarea); |
337 | kfree(au1xpsc_i2s_workdata->ioarea); | 388 | kfree(wd->ioarea); |
338 | kfree(au1xpsc_i2s_workdata); | 389 | kfree(wd); |
339 | au1xpsc_i2s_workdata = NULL; | 390 | |
391 | au1xpsc_i2s_workdata = NULL; /* MDEV */ | ||
392 | |||
393 | return 0; | ||
340 | } | 394 | } |
341 | 395 | ||
342 | static int au1xpsc_i2s_suspend(struct snd_soc_dai *cpu_dai) | 396 | #ifdef CONFIG_PM |
397 | static int au1xpsc_i2s_drvsuspend(struct device *dev) | ||
343 | { | 398 | { |
399 | struct au1xpsc_audio_data *wd = dev_get_drvdata(dev); | ||
400 | |||
344 | /* save interesting register and disable PSC */ | 401 | /* save interesting register and disable PSC */ |
345 | au1xpsc_i2s_workdata->pm[0] = | 402 | wd->pm[0] = au_readl(PSC_SEL(wd)); |
346 | au_readl(PSC_SEL(au1xpsc_i2s_workdata)); | ||
347 | 403 | ||
348 | au_writel(0, I2S_CFG(au1xpsc_i2s_workdata)); | 404 | au_writel(0, I2S_CFG(wd)); |
349 | au_sync(); | 405 | au_sync(); |
350 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata)); | 406 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); |
351 | au_sync(); | 407 | au_sync(); |
352 | 408 | ||
353 | return 0; | 409 | return 0; |
354 | } | 410 | } |
355 | 411 | ||
356 | static int au1xpsc_i2s_resume(struct snd_soc_dai *cpu_dai) | 412 | static int au1xpsc_i2s_drvresume(struct device *dev) |
357 | { | 413 | { |
414 | struct au1xpsc_audio_data *wd = dev_get_drvdata(dev); | ||
415 | |||
358 | /* select I2S mode and PSC clock */ | 416 | /* select I2S mode and PSC clock */ |
359 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata)); | 417 | au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); |
360 | au_sync(); | 418 | au_sync(); |
361 | au_writel(0, PSC_SEL(au1xpsc_i2s_workdata)); | 419 | au_writel(0, PSC_SEL(wd)); |
362 | au_sync(); | 420 | au_sync(); |
363 | au_writel(au1xpsc_i2s_workdata->pm[0], | 421 | au_writel(wd->pm[0], PSC_SEL(wd)); |
364 | PSC_SEL(au1xpsc_i2s_workdata)); | ||
365 | au_sync(); | 422 | au_sync(); |
366 | 423 | ||
367 | return 0; | 424 | return 0; |
368 | } | 425 | } |
369 | 426 | ||
370 | static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = { | 427 | static struct dev_pm_ops au1xpsci2s_pmops = { |
371 | .trigger = au1xpsc_i2s_trigger, | 428 | .suspend = au1xpsc_i2s_drvsuspend, |
372 | .hw_params = au1xpsc_i2s_hw_params, | 429 | .resume = au1xpsc_i2s_drvresume, |
373 | .set_fmt = au1xpsc_i2s_set_fmt, | ||
374 | }; | 430 | }; |
375 | 431 | ||
376 | struct snd_soc_dai au1xpsc_i2s_dai = { | 432 | #define AU1XPSCI2S_PMOPS &au1xpsci2s_pmops |
377 | .name = "au1xpsc_i2s", | 433 | |
378 | .probe = au1xpsc_i2s_probe, | 434 | #else |
379 | .remove = au1xpsc_i2s_remove, | 435 | |
380 | .suspend = au1xpsc_i2s_suspend, | 436 | #define AU1XPSCI2S_PMOPS NULL |
381 | .resume = au1xpsc_i2s_resume, | 437 | |
382 | .playback = { | 438 | #endif |
383 | .rates = AU1XPSC_I2S_RATES, | 439 | |
384 | .formats = AU1XPSC_I2S_FMTS, | 440 | static struct platform_driver au1xpsc_i2s_driver = { |
385 | .channels_min = 2, | 441 | .driver = { |
386 | .channels_max = 8, /* 2 without external help */ | 442 | .name = "au1xpsc_i2s", |
387 | }, | 443 | .owner = THIS_MODULE, |
388 | .capture = { | 444 | .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 | }, | 445 | }, |
394 | .ops = &au1xpsc_i2s_dai_ops, | 446 | .probe = au1xpsc_i2s_drvprobe, |
447 | .remove = __devexit_p(au1xpsc_i2s_drvremove), | ||
395 | }; | 448 | }; |
396 | EXPORT_SYMBOL(au1xpsc_i2s_dai); | ||
397 | 449 | ||
398 | static int __init au1xpsc_i2s_init(void) | 450 | static int __init au1xpsc_i2s_load(void) |
399 | { | 451 | { |
400 | au1xpsc_i2s_workdata = NULL; | 452 | au1xpsc_i2s_workdata = NULL; |
401 | return snd_soc_register_dai(&au1xpsc_i2s_dai); | 453 | return platform_driver_register(&au1xpsc_i2s_driver); |
402 | } | 454 | } |
403 | 455 | ||
404 | static void __exit au1xpsc_i2s_exit(void) | 456 | static void __exit au1xpsc_i2s_unload(void) |
405 | { | 457 | { |
406 | snd_soc_unregister_dai(&au1xpsc_i2s_dai); | 458 | platform_driver_unregister(&au1xpsc_i2s_driver); |
407 | } | 459 | } |
408 | 460 | ||
409 | module_init(au1xpsc_i2s_init); | 461 | module_init(au1xpsc_i2s_load); |
410 | module_exit(au1xpsc_i2s_exit); | 462 | module_exit(au1xpsc_i2s_unload); |
411 | 463 | ||
412 | MODULE_LICENSE("GPL"); | 464 | MODULE_LICENSE("GPL"); |
413 | MODULE_DESCRIPTION("Au12x0/Au1550 PSC I2S ALSA ASoC audio driver"); | 465 | MODULE_DESCRIPTION("Au12x0/Au1550 PSC I2S ALSA ASoC audio driver"); |
414 | MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); | 466 | 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 |
diff --git a/sound/soc/au1x/sample-ac97.c b/sound/soc/au1x/sample-ac97.c deleted file mode 100644 index 27683eb7905e..000000000000 --- a/sound/soc/au1x/sample-ac97.c +++ /dev/null | |||
@@ -1,144 +0,0 @@ | |||
1 | /* | ||
2 | * Sample Au12x0/Au1550 PSC AC97 sound machine. | ||
3 | * | ||
4 | * Copyright (c) 2007-2008 Manuel Lauss <mano@roarinelk.homelinux.net> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms outlined in the file COPYING at the root of this | ||
8 | * source archive. | ||
9 | * | ||
10 | * This is a very generic AC97 sound machine driver for boards which | ||
11 | * have (AC97) audio at PSC1 (e.g. DB1200 demoboards). | ||
12 | */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/moduleparam.h> | ||
16 | #include <linux/timer.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <sound/core.h> | ||
20 | #include <sound/pcm.h> | ||
21 | #include <sound/soc.h> | ||
22 | #include <sound/soc-dapm.h> | ||
23 | #include <asm/mach-au1x00/au1000.h> | ||
24 | #include <asm/mach-au1x00/au1xxx_psc.h> | ||
25 | #include <asm/mach-au1x00/au1xxx_dbdma.h> | ||
26 | |||
27 | #include "../codecs/ac97.h" | ||
28 | #include "psc.h" | ||
29 | |||
30 | static int au1xpsc_sample_ac97_init(struct snd_soc_codec *codec) | ||
31 | { | ||
32 | snd_soc_dapm_sync(codec); | ||
33 | return 0; | ||
34 | } | ||
35 | |||
36 | static struct snd_soc_dai_link au1xpsc_sample_ac97_dai = { | ||
37 | .name = "AC97", | ||
38 | .stream_name = "AC97 HiFi", | ||
39 | .cpu_dai = &au1xpsc_ac97_dai, /* see psc-ac97.c */ | ||
40 | .codec_dai = &ac97_dai, /* see codecs/ac97.c */ | ||
41 | .init = au1xpsc_sample_ac97_init, | ||
42 | .ops = NULL, | ||
43 | }; | ||
44 | |||
45 | static struct snd_soc_card au1xpsc_sample_ac97_machine = { | ||
46 | .name = "Au1xxx PSC AC97 Audio", | ||
47 | .dai_link = &au1xpsc_sample_ac97_dai, | ||
48 | .num_links = 1, | ||
49 | }; | ||
50 | |||
51 | static struct snd_soc_device au1xpsc_sample_ac97_devdata = { | ||
52 | .card = &au1xpsc_sample_ac97_machine, | ||
53 | .platform = &au1xpsc_soc_platform, /* see dbdma2.c */ | ||
54 | .codec_dev = &soc_codec_dev_ac97, | ||
55 | }; | ||
56 | |||
57 | static struct resource au1xpsc_psc1_res[] = { | ||
58 | [0] = { | ||
59 | .start = CPHYSADDR(PSC1_BASE_ADDR), | ||
60 | .end = CPHYSADDR(PSC1_BASE_ADDR) + 0x000fffff, | ||
61 | .flags = IORESOURCE_MEM, | ||
62 | }, | ||
63 | [1] = { | ||
64 | #ifdef CONFIG_SOC_AU1200 | ||
65 | .start = AU1200_PSC1_INT, | ||
66 | .end = AU1200_PSC1_INT, | ||
67 | #elif defined(CONFIG_SOC_AU1550) | ||
68 | .start = AU1550_PSC1_INT, | ||
69 | .end = AU1550_PSC1_INT, | ||
70 | #endif | ||
71 | .flags = IORESOURCE_IRQ, | ||
72 | }, | ||
73 | [2] = { | ||
74 | .start = DSCR_CMD0_PSC1_TX, | ||
75 | .end = DSCR_CMD0_PSC1_TX, | ||
76 | .flags = IORESOURCE_DMA, | ||
77 | }, | ||
78 | [3] = { | ||
79 | .start = DSCR_CMD0_PSC1_RX, | ||
80 | .end = DSCR_CMD0_PSC1_RX, | ||
81 | .flags = IORESOURCE_DMA, | ||
82 | }, | ||
83 | }; | ||
84 | |||
85 | static struct platform_device *au1xpsc_sample_ac97_dev; | ||
86 | |||
87 | static int __init au1xpsc_sample_ac97_load(void) | ||
88 | { | ||
89 | int ret; | ||
90 | |||
91 | #ifdef CONFIG_SOC_AU1200 | ||
92 | unsigned long io; | ||
93 | |||
94 | /* modify sys_pinfunc for AC97 on PSC1 */ | ||
95 | io = au_readl(SYS_PINFUNC); | ||
96 | io |= SYS_PINFUNC_P1C; | ||
97 | io &= ~(SYS_PINFUNC_P1A | SYS_PINFUNC_P1B); | ||
98 | au_writel(io, SYS_PINFUNC); | ||
99 | au_sync(); | ||
100 | #endif | ||
101 | |||
102 | ret = -ENOMEM; | ||
103 | |||
104 | /* setup PSC clock source for AC97 part: external clock provided | ||
105 | * by codec. The psc-ac97.c driver depends on this setting! | ||
106 | */ | ||
107 | au_writel(PSC_SEL_CLK_SERCLK, PSC1_BASE_ADDR + PSC_SEL_OFFSET); | ||
108 | au_sync(); | ||
109 | |||
110 | au1xpsc_sample_ac97_dev = platform_device_alloc("soc-audio", -1); | ||
111 | if (!au1xpsc_sample_ac97_dev) | ||
112 | goto out; | ||
113 | |||
114 | au1xpsc_sample_ac97_dev->resource = | ||
115 | kmemdup(au1xpsc_psc1_res, sizeof(struct resource) * | ||
116 | ARRAY_SIZE(au1xpsc_psc1_res), GFP_KERNEL); | ||
117 | au1xpsc_sample_ac97_dev->num_resources = ARRAY_SIZE(au1xpsc_psc1_res); | ||
118 | au1xpsc_sample_ac97_dev->id = 1; | ||
119 | |||
120 | platform_set_drvdata(au1xpsc_sample_ac97_dev, | ||
121 | &au1xpsc_sample_ac97_devdata); | ||
122 | au1xpsc_sample_ac97_devdata.dev = &au1xpsc_sample_ac97_dev->dev; | ||
123 | ret = platform_device_add(au1xpsc_sample_ac97_dev); | ||
124 | |||
125 | if (ret) { | ||
126 | platform_device_put(au1xpsc_sample_ac97_dev); | ||
127 | au1xpsc_sample_ac97_dev = NULL; | ||
128 | } | ||
129 | |||
130 | out: | ||
131 | return ret; | ||
132 | } | ||
133 | |||
134 | static void __exit au1xpsc_sample_ac97_exit(void) | ||
135 | { | ||
136 | platform_device_unregister(au1xpsc_sample_ac97_dev); | ||
137 | } | ||
138 | |||
139 | module_init(au1xpsc_sample_ac97_load); | ||
140 | module_exit(au1xpsc_sample_ac97_exit); | ||
141 | |||
142 | MODULE_LICENSE("GPL"); | ||
143 | MODULE_DESCRIPTION("Au1xxx PSC sample AC97 machine"); | ||
144 | MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); | ||