diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2010-03-10 10:02:37 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2010-03-10 10:02:37 -0500 |
commit | fad837c16cdd856c68ce2e1335ad0fe836ed8ecd (patch) | |
tree | 1a6babdc2ac7e5388c482e93505fdfaf5ff97f61 /sound/soc | |
parent | 51c6ab130642ed975681df843c772dda48a1d2ed (diff) | |
parent | 57d54889cd00db2752994b389ba714138652e60c (diff) |
Merge commit 'v2.6.34-rc1' into for-2.6.35
Diffstat (limited to 'sound/soc')
-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 | 14 | ||||
-rw-r--r-- | sound/soc/au1x/sample-ac97.c | 144 | ||||
-rw-r--r-- | sound/soc/codecs/ak4104.c | 6 | ||||
-rw-r--r-- | sound/soc/codecs/uda1380.c | 2 | ||||
-rw-r--r-- | sound/soc/codecs/wm8350.c | 8 | ||||
-rw-r--r-- | sound/soc/fsl/efika-audio-fabric.c | 2 | ||||
-rw-r--r-- | sound/soc/fsl/pcm030-audio-fabric.c | 2 | ||||
-rw-r--r-- | sound/soc/imx/imx-pcm-fiq.c | 40 | ||||
-rw-r--r-- | sound/soc/omap/Kconfig | 3 | ||||
-rw-r--r-- | sound/soc/omap/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/omap/mcpdm.c | 484 | ||||
-rw-r--r-- | sound/soc/omap/mcpdm.h | 151 | ||||
-rw-r--r-- | sound/soc/omap/omap-mcbsp.c | 146 | ||||
-rw-r--r-- | sound/soc/omap/omap-mcbsp.h | 4 | ||||
-rw-r--r-- | sound/soc/omap/omap-mcpdm.c | 251 | ||||
-rw-r--r-- | sound/soc/omap/omap-mcpdm.h | 29 | ||||
-rw-r--r-- | sound/soc/omap/omap-pcm.c | 15 | ||||
-rw-r--r-- | sound/soc/omap/omap-pcm.h | 4 | ||||
-rw-r--r-- | sound/soc/sh/fsi.c | 46 | ||||
-rw-r--r-- | sound/soc/sh/siu.h | 2 | ||||
-rw-r--r-- | sound/soc/sh/siu_pcm.c | 2 | ||||
-rw-r--r-- | sound/soc/soc-core.c | 20 |
25 files changed, 1313 insertions, 219 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 19e4d37eba1c..6d9f4c624949 100644 --- a/sound/soc/au1x/dbdma2.c +++ b/sound/soc/au1x/dbdma2.c | |||
@@ -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; |
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>"); | ||
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index b9ef7e45891d..b68d99fb6af0 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c | |||
@@ -90,12 +90,10 @@ static int ak4104_spi_write(struct snd_soc_codec *codec, unsigned int reg, | |||
90 | if (reg >= codec->reg_cache_size) | 90 | if (reg >= codec->reg_cache_size) |
91 | return -EINVAL; | 91 | return -EINVAL; |
92 | 92 | ||
93 | reg &= AK4104_REG_MASK; | ||
94 | reg |= AK4104_WRITE; | ||
95 | |||
96 | /* only write to the hardware if value has changed */ | 93 | /* only write to the hardware if value has changed */ |
97 | if (cache[reg] != value) { | 94 | if (cache[reg] != value) { |
98 | u8 tmp[2] = { reg, value }; | 95 | u8 tmp[2] = { (reg & AK4104_REG_MASK) | AK4104_WRITE, value }; |
96 | |||
99 | if (spi_write(spi, tmp, sizeof(tmp))) { | 97 | if (spi_write(spi, tmp, sizeof(tmp))) { |
100 | dev_err(&spi->dev, "SPI write failed\n"); | 98 | dev_err(&spi->dev, "SPI write failed\n"); |
101 | return -EIO; | 99 | return -EIO; |
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index a2763c2e7348..9cd0a66b7663 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c | |||
@@ -137,7 +137,7 @@ static void uda1380_flush_work(struct work_struct *work) | |||
137 | { | 137 | { |
138 | int bit, reg; | 138 | int bit, reg; |
139 | 139 | ||
140 | for_each_bit(bit, &uda1380_cache_dirty, UDA1380_CACHEREGNUM - 0x10) { | 140 | for_each_set_bit(bit, &uda1380_cache_dirty, UDA1380_CACHEREGNUM - 0x10) { |
141 | reg = 0x10 + bit; | 141 | reg = 0x10 + bit; |
142 | pr_debug("uda1380: flush reg %x val %x:\n", reg, | 142 | pr_debug("uda1380: flush reg %x val %x:\n", reg, |
143 | uda1380_read_reg_cache(uda1380_codec, reg)); | 143 | uda1380_read_reg_cache(uda1380_codec, reg)); |
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 718ef912e758..df2c6d9617fb 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c | |||
@@ -1349,7 +1349,7 @@ static irqreturn_t wm8350_hp_jack_handler(int irq, void *data) | |||
1349 | int mask; | 1349 | int mask; |
1350 | struct wm8350_jack_data *jack = NULL; | 1350 | struct wm8350_jack_data *jack = NULL; |
1351 | 1351 | ||
1352 | switch (irq) { | 1352 | switch (irq - wm8350->irq_base) { |
1353 | case WM8350_IRQ_CODEC_JCK_DET_L: | 1353 | case WM8350_IRQ_CODEC_JCK_DET_L: |
1354 | jack = &priv->hpl; | 1354 | jack = &priv->hpl; |
1355 | mask = WM8350_JACK_L_LVL; | 1355 | mask = WM8350_JACK_L_LVL; |
@@ -1424,7 +1424,7 @@ int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which, | |||
1424 | wm8350_set_bits(wm8350, WM8350_JACK_DETECT, ena); | 1424 | wm8350_set_bits(wm8350, WM8350_JACK_DETECT, ena); |
1425 | 1425 | ||
1426 | /* Sync status */ | 1426 | /* Sync status */ |
1427 | wm8350_hp_jack_handler(irq, priv); | 1427 | wm8350_hp_jack_handler(irq + wm8350->irq_base, priv); |
1428 | 1428 | ||
1429 | return 0; | 1429 | return 0; |
1430 | } | 1430 | } |
@@ -1521,8 +1521,8 @@ static int wm8350_remove(struct platform_device *pdev) | |||
1521 | WM8350_JDL_ENA | WM8350_JDR_ENA); | 1521 | WM8350_JDL_ENA | WM8350_JDR_ENA); |
1522 | wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA); | 1522 | wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA); |
1523 | 1523 | ||
1524 | wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L); | 1524 | wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, priv); |
1525 | wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R); | 1525 | wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, priv); |
1526 | 1526 | ||
1527 | priv->hpl.jack = NULL; | 1527 | priv->hpl.jack = NULL; |
1528 | priv->hpr.jack = NULL; | 1528 | priv->hpr.jack = NULL; |
diff --git a/sound/soc/fsl/efika-audio-fabric.c b/sound/soc/fsl/efika-audio-fabric.c index 3326e2a1e863..1a5b8e0d6a34 100644 --- a/sound/soc/fsl/efika-audio-fabric.c +++ b/sound/soc/fsl/efika-audio-fabric.c | |||
@@ -55,7 +55,7 @@ static __init int efika_fabric_init(void) | |||
55 | struct platform_device *pdev; | 55 | struct platform_device *pdev; |
56 | int rc; | 56 | int rc; |
57 | 57 | ||
58 | if (!machine_is_compatible("bplan,efika")) | 58 | if (!of_machine_is_compatible("bplan,efika")) |
59 | return -ENODEV; | 59 | return -ENODEV; |
60 | 60 | ||
61 | card.platform = &mpc5200_audio_dma_platform; | 61 | card.platform = &mpc5200_audio_dma_platform; |
diff --git a/sound/soc/fsl/pcm030-audio-fabric.c b/sound/soc/fsl/pcm030-audio-fabric.c index b928ef7d28eb..6644cba7cbf2 100644 --- a/sound/soc/fsl/pcm030-audio-fabric.c +++ b/sound/soc/fsl/pcm030-audio-fabric.c | |||
@@ -55,7 +55,7 @@ static __init int pcm030_fabric_init(void) | |||
55 | struct platform_device *pdev; | 55 | struct platform_device *pdev; |
56 | int rc; | 56 | int rc; |
57 | 57 | ||
58 | if (!machine_is_compatible("phytec,pcm030")) | 58 | if (!of_machine_is_compatible("phytec,pcm030")) |
59 | return -ENODEV; | 59 | return -ENODEV; |
60 | 60 | ||
61 | card.platform = &mpc5200_audio_dma_platform; | 61 | card.platform = &mpc5200_audio_dma_platform; |
diff --git a/sound/soc/imx/imx-pcm-fiq.c b/sound/soc/imx/imx-pcm-fiq.c index 5532579ece4d..d9cb9849b033 100644 --- a/sound/soc/imx/imx-pcm-fiq.c +++ b/sound/soc/imx/imx-pcm-fiq.c | |||
@@ -35,22 +35,25 @@ | |||
35 | struct imx_pcm_runtime_data { | 35 | struct imx_pcm_runtime_data { |
36 | int period; | 36 | int period; |
37 | int periods; | 37 | int periods; |
38 | unsigned long dma_addr; | ||
39 | int dma; | ||
40 | unsigned long offset; | 38 | unsigned long offset; |
39 | unsigned long last_offset; | ||
41 | unsigned long size; | 40 | unsigned long size; |
42 | unsigned long period_cnt; | ||
43 | void *buf; | ||
44 | struct timer_list timer; | 41 | struct timer_list timer; |
45 | int period_time; | 42 | int poll_time; |
46 | }; | 43 | }; |
47 | 44 | ||
45 | static inline void imx_ssi_set_next_poll(struct imx_pcm_runtime_data *iprtd) | ||
46 | { | ||
47 | iprtd->timer.expires = jiffies + iprtd->poll_time; | ||
48 | } | ||
49 | |||
48 | static void imx_ssi_timer_callback(unsigned long data) | 50 | static void imx_ssi_timer_callback(unsigned long data) |
49 | { | 51 | { |
50 | struct snd_pcm_substream *substream = (void *)data; | 52 | struct snd_pcm_substream *substream = (void *)data; |
51 | struct snd_pcm_runtime *runtime = substream->runtime; | 53 | struct snd_pcm_runtime *runtime = substream->runtime; |
52 | struct imx_pcm_runtime_data *iprtd = runtime->private_data; | 54 | struct imx_pcm_runtime_data *iprtd = runtime->private_data; |
53 | struct pt_regs regs; | 55 | struct pt_regs regs; |
56 | unsigned long delta; | ||
54 | 57 | ||
55 | get_fiq_regs(®s); | 58 | get_fiq_regs(®s); |
56 | 59 | ||
@@ -59,9 +62,25 @@ static void imx_ssi_timer_callback(unsigned long data) | |||
59 | else | 62 | else |
60 | iprtd->offset = regs.ARM_r9 & 0xffff; | 63 | iprtd->offset = regs.ARM_r9 & 0xffff; |
61 | 64 | ||
62 | iprtd->timer.expires = jiffies + iprtd->period_time; | 65 | /* How much data have we transferred since the last period report? */ |
66 | if (iprtd->offset >= iprtd->last_offset) | ||
67 | delta = iprtd->offset - iprtd->last_offset; | ||
68 | else | ||
69 | delta = runtime->buffer_size + iprtd->offset | ||
70 | - iprtd->last_offset; | ||
71 | |||
72 | /* If we've transferred at least a period then report it and | ||
73 | * reset our poll time */ | ||
74 | if (delta >= runtime->period_size) { | ||
75 | snd_pcm_period_elapsed(substream); | ||
76 | iprtd->last_offset = iprtd->offset; | ||
77 | |||
78 | imx_ssi_set_next_poll(iprtd); | ||
79 | } | ||
80 | |||
81 | /* Restart the timer; if we didn't report we'll run on the next tick */ | ||
63 | add_timer(&iprtd->timer); | 82 | add_timer(&iprtd->timer); |
64 | snd_pcm_period_elapsed(substream); | 83 | |
65 | } | 84 | } |
66 | 85 | ||
67 | static struct fiq_handler fh = { | 86 | static struct fiq_handler fh = { |
@@ -76,9 +95,10 @@ static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, | |||
76 | 95 | ||
77 | iprtd->size = params_buffer_bytes(params); | 96 | iprtd->size = params_buffer_bytes(params); |
78 | iprtd->periods = params_periods(params); | 97 | iprtd->periods = params_periods(params); |
79 | iprtd->period = params_period_bytes(params); | 98 | iprtd->period = params_period_bytes(params) ; |
80 | iprtd->offset = 0; | 99 | iprtd->offset = 0; |
81 | iprtd->period_time = HZ / (params_rate(params) / params_period_size(params)); | 100 | iprtd->last_offset = 0; |
101 | iprtd->poll_time = HZ / (params_rate(params) / params_period_size(params)); | ||
82 | 102 | ||
83 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | 103 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); |
84 | 104 | ||
@@ -114,7 +134,7 @@ static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | |||
114 | case SNDRV_PCM_TRIGGER_START: | 134 | case SNDRV_PCM_TRIGGER_START: |
115 | case SNDRV_PCM_TRIGGER_RESUME: | 135 | case SNDRV_PCM_TRIGGER_RESUME: |
116 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 136 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
117 | iprtd->timer.expires = jiffies + iprtd->period_time; | 137 | imx_ssi_set_next_poll(iprtd); |
118 | add_timer(&iprtd->timer); | 138 | add_timer(&iprtd->timer); |
119 | if (++fiq_enable == 1) | 139 | if (++fiq_enable == 1) |
120 | enable_fiq(imx_pcm_fiq); | 140 | enable_fiq(imx_pcm_fiq); |
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 18ebdc7d0a51..f11963c21873 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig | |||
@@ -6,6 +6,9 @@ config SND_OMAP_SOC_MCBSP | |||
6 | tristate | 6 | tristate |
7 | select OMAP_MCBSP | 7 | select OMAP_MCBSP |
8 | 8 | ||
9 | config SND_OMAP_SOC_MCPDM | ||
10 | tristate | ||
11 | |||
9 | config SND_OMAP_SOC_N810 | 12 | config SND_OMAP_SOC_N810 |
10 | tristate "SoC Audio support for Nokia N810" | 13 | tristate "SoC Audio support for Nokia N810" |
11 | depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C | 14 | depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C |
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index 19283e5edfbf..0bc00ca14b37 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile | |||
@@ -1,9 +1,11 @@ | |||
1 | # OMAP Platform Support | 1 | # OMAP Platform Support |
2 | snd-soc-omap-objs := omap-pcm.o | 2 | snd-soc-omap-objs := omap-pcm.o |
3 | snd-soc-omap-mcbsp-objs := omap-mcbsp.o | 3 | snd-soc-omap-mcbsp-objs := omap-mcbsp.o |
4 | snd-soc-omap-mcpdm-objs := omap-mcpdm.o mcpdm.o | ||
4 | 5 | ||
5 | obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o | 6 | obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o |
6 | obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o | 7 | obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o |
8 | obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o | ||
7 | 9 | ||
8 | # OMAP Machine Support | 10 | # OMAP Machine Support |
9 | snd-soc-n810-objs := n810.o | 11 | snd-soc-n810-objs := n810.o |
diff --git a/sound/soc/omap/mcpdm.c b/sound/soc/omap/mcpdm.c new file mode 100644 index 000000000000..ad8df6cfae88 --- /dev/null +++ b/sound/soc/omap/mcpdm.c | |||
@@ -0,0 +1,484 @@ | |||
1 | /* | ||
2 | * mcpdm.c -- McPDM interface driver | ||
3 | * | ||
4 | * Author: Jorge Eduardo Candelaria <x0107209@ti.com> | ||
5 | * Copyright (C) 2009 - Texas Instruments, Inc. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * version 2 as published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, but | ||
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14 | * General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
19 | * 02110-1301 USA | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #include <linux/module.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/device.h> | ||
26 | #include <linux/platform_device.h> | ||
27 | #include <linux/wait.h> | ||
28 | #include <linux/interrupt.h> | ||
29 | #include <linux/err.h> | ||
30 | #include <linux/clk.h> | ||
31 | #include <linux/delay.h> | ||
32 | #include <linux/io.h> | ||
33 | #include <linux/irq.h> | ||
34 | |||
35 | #include "mcpdm.h" | ||
36 | |||
37 | static struct omap_mcpdm *mcpdm; | ||
38 | |||
39 | static inline void omap_mcpdm_write(u16 reg, u32 val) | ||
40 | { | ||
41 | __raw_writel(val, mcpdm->io_base + reg); | ||
42 | } | ||
43 | |||
44 | static inline int omap_mcpdm_read(u16 reg) | ||
45 | { | ||
46 | return __raw_readl(mcpdm->io_base + reg); | ||
47 | } | ||
48 | |||
49 | static void omap_mcpdm_reg_dump(void) | ||
50 | { | ||
51 | dev_dbg(mcpdm->dev, "***********************\n"); | ||
52 | dev_dbg(mcpdm->dev, "IRQSTATUS_RAW: 0x%04x\n", | ||
53 | omap_mcpdm_read(MCPDM_IRQSTATUS_RAW)); | ||
54 | dev_dbg(mcpdm->dev, "IRQSTATUS: 0x%04x\n", | ||
55 | omap_mcpdm_read(MCPDM_IRQSTATUS)); | ||
56 | dev_dbg(mcpdm->dev, "IRQENABLE_SET: 0x%04x\n", | ||
57 | omap_mcpdm_read(MCPDM_IRQENABLE_SET)); | ||
58 | dev_dbg(mcpdm->dev, "IRQENABLE_CLR: 0x%04x\n", | ||
59 | omap_mcpdm_read(MCPDM_IRQENABLE_CLR)); | ||
60 | dev_dbg(mcpdm->dev, "IRQWAKE_EN: 0x%04x\n", | ||
61 | omap_mcpdm_read(MCPDM_IRQWAKE_EN)); | ||
62 | dev_dbg(mcpdm->dev, "DMAENABLE_SET: 0x%04x\n", | ||
63 | omap_mcpdm_read(MCPDM_DMAENABLE_SET)); | ||
64 | dev_dbg(mcpdm->dev, "DMAENABLE_CLR: 0x%04x\n", | ||
65 | omap_mcpdm_read(MCPDM_DMAENABLE_CLR)); | ||
66 | dev_dbg(mcpdm->dev, "DMAWAKEEN: 0x%04x\n", | ||
67 | omap_mcpdm_read(MCPDM_DMAWAKEEN)); | ||
68 | dev_dbg(mcpdm->dev, "CTRL: 0x%04x\n", | ||
69 | omap_mcpdm_read(MCPDM_CTRL)); | ||
70 | dev_dbg(mcpdm->dev, "DN_DATA: 0x%04x\n", | ||
71 | omap_mcpdm_read(MCPDM_DN_DATA)); | ||
72 | dev_dbg(mcpdm->dev, "UP_DATA: 0x%04x\n", | ||
73 | omap_mcpdm_read(MCPDM_UP_DATA)); | ||
74 | dev_dbg(mcpdm->dev, "FIFO_CTRL_DN: 0x%04x\n", | ||
75 | omap_mcpdm_read(MCPDM_FIFO_CTRL_DN)); | ||
76 | dev_dbg(mcpdm->dev, "FIFO_CTRL_UP: 0x%04x\n", | ||
77 | omap_mcpdm_read(MCPDM_FIFO_CTRL_UP)); | ||
78 | dev_dbg(mcpdm->dev, "DN_OFFSET: 0x%04x\n", | ||
79 | omap_mcpdm_read(MCPDM_DN_OFFSET)); | ||
80 | dev_dbg(mcpdm->dev, "***********************\n"); | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * Takes the McPDM module in and out of reset state. | ||
85 | * Uplink and downlink can be reset individually. | ||
86 | */ | ||
87 | static void omap_mcpdm_reset_capture(int reset) | ||
88 | { | ||
89 | int ctrl = omap_mcpdm_read(MCPDM_CTRL); | ||
90 | |||
91 | if (reset) | ||
92 | ctrl |= SW_UP_RST; | ||
93 | else | ||
94 | ctrl &= ~SW_UP_RST; | ||
95 | |||
96 | omap_mcpdm_write(MCPDM_CTRL, ctrl); | ||
97 | } | ||
98 | |||
99 | static void omap_mcpdm_reset_playback(int reset) | ||
100 | { | ||
101 | int ctrl = omap_mcpdm_read(MCPDM_CTRL); | ||
102 | |||
103 | if (reset) | ||
104 | ctrl |= SW_DN_RST; | ||
105 | else | ||
106 | ctrl &= ~SW_DN_RST; | ||
107 | |||
108 | omap_mcpdm_write(MCPDM_CTRL, ctrl); | ||
109 | } | ||
110 | |||
111 | /* | ||
112 | * Enables the transfer through the PDM interface to/from the Phoenix | ||
113 | * codec by enabling the corresponding UP or DN channels. | ||
114 | */ | ||
115 | void omap_mcpdm_start(int stream) | ||
116 | { | ||
117 | int ctrl = omap_mcpdm_read(MCPDM_CTRL); | ||
118 | |||
119 | if (stream) | ||
120 | ctrl |= mcpdm->up_channels; | ||
121 | else | ||
122 | ctrl |= mcpdm->dn_channels; | ||
123 | |||
124 | omap_mcpdm_write(MCPDM_CTRL, ctrl); | ||
125 | } | ||
126 | |||
127 | /* | ||
128 | * Disables the transfer through the PDM interface to/from the Phoenix | ||
129 | * codec by disabling the corresponding UP or DN channels. | ||
130 | */ | ||
131 | void omap_mcpdm_stop(int stream) | ||
132 | { | ||
133 | int ctrl = omap_mcpdm_read(MCPDM_CTRL); | ||
134 | |||
135 | if (stream) | ||
136 | ctrl &= ~mcpdm->up_channels; | ||
137 | else | ||
138 | ctrl &= ~mcpdm->dn_channels; | ||
139 | |||
140 | omap_mcpdm_write(MCPDM_CTRL, ctrl); | ||
141 | } | ||
142 | |||
143 | /* | ||
144 | * Configures McPDM uplink for audio recording. | ||
145 | * This function should be called before omap_mcpdm_start. | ||
146 | */ | ||
147 | int omap_mcpdm_capture_open(struct omap_mcpdm_link *uplink) | ||
148 | { | ||
149 | int irq_mask = 0; | ||
150 | int ctrl; | ||
151 | |||
152 | if (!uplink) | ||
153 | return -EINVAL; | ||
154 | |||
155 | mcpdm->uplink = uplink; | ||
156 | |||
157 | /* Enable irq request generation */ | ||
158 | irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK; | ||
159 | omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask); | ||
160 | |||
161 | /* Configure uplink threshold */ | ||
162 | if (uplink->threshold > UP_THRES_MAX) | ||
163 | uplink->threshold = UP_THRES_MAX; | ||
164 | |||
165 | omap_mcpdm_write(MCPDM_FIFO_CTRL_UP, uplink->threshold); | ||
166 | |||
167 | /* Configure DMA controller */ | ||
168 | omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_UP_ENABLE); | ||
169 | |||
170 | /* Set pdm out format */ | ||
171 | ctrl = omap_mcpdm_read(MCPDM_CTRL); | ||
172 | ctrl &= ~PDMOUTFORMAT; | ||
173 | ctrl |= uplink->format & PDMOUTFORMAT; | ||
174 | |||
175 | /* Uplink channels */ | ||
176 | mcpdm->up_channels = uplink->channels & (PDM_UP_MASK | PDM_STATUS_MASK); | ||
177 | |||
178 | omap_mcpdm_write(MCPDM_CTRL, ctrl); | ||
179 | |||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | /* | ||
184 | * Configures McPDM downlink for audio playback. | ||
185 | * This function should be called before omap_mcpdm_start. | ||
186 | */ | ||
187 | int omap_mcpdm_playback_open(struct omap_mcpdm_link *downlink) | ||
188 | { | ||
189 | int irq_mask = 0; | ||
190 | int ctrl; | ||
191 | |||
192 | if (!downlink) | ||
193 | return -EINVAL; | ||
194 | |||
195 | mcpdm->downlink = downlink; | ||
196 | |||
197 | /* Enable irq request generation */ | ||
198 | irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK; | ||
199 | omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask); | ||
200 | |||
201 | /* Configure uplink threshold */ | ||
202 | if (downlink->threshold > DN_THRES_MAX) | ||
203 | downlink->threshold = DN_THRES_MAX; | ||
204 | |||
205 | omap_mcpdm_write(MCPDM_FIFO_CTRL_DN, downlink->threshold); | ||
206 | |||
207 | /* Enable DMA request generation */ | ||
208 | omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_DN_ENABLE); | ||
209 | |||
210 | /* Set pdm out format */ | ||
211 | ctrl = omap_mcpdm_read(MCPDM_CTRL); | ||
212 | ctrl &= ~PDMOUTFORMAT; | ||
213 | ctrl |= downlink->format & PDMOUTFORMAT; | ||
214 | |||
215 | /* Downlink channels */ | ||
216 | mcpdm->dn_channels = downlink->channels & (PDM_DN_MASK | PDM_CMD_MASK); | ||
217 | |||
218 | omap_mcpdm_write(MCPDM_CTRL, ctrl); | ||
219 | |||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | /* | ||
224 | * Cleans McPDM uplink configuration. | ||
225 | * This function should be called when the stream is closed. | ||
226 | */ | ||
227 | int omap_mcpdm_capture_close(struct omap_mcpdm_link *uplink) | ||
228 | { | ||
229 | int irq_mask = 0; | ||
230 | |||
231 | if (!uplink) | ||
232 | return -EINVAL; | ||
233 | |||
234 | /* Disable irq request generation */ | ||
235 | irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK; | ||
236 | omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask); | ||
237 | |||
238 | /* Disable DMA request generation */ | ||
239 | omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_UP_ENABLE); | ||
240 | |||
241 | /* Clear Downlink channels */ | ||
242 | mcpdm->up_channels = 0; | ||
243 | |||
244 | mcpdm->uplink = NULL; | ||
245 | |||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | /* | ||
250 | * Cleans McPDM downlink configuration. | ||
251 | * This function should be called when the stream is closed. | ||
252 | */ | ||
253 | int omap_mcpdm_playback_close(struct omap_mcpdm_link *downlink) | ||
254 | { | ||
255 | int irq_mask = 0; | ||
256 | |||
257 | if (!downlink) | ||
258 | return -EINVAL; | ||
259 | |||
260 | /* Disable irq request generation */ | ||
261 | irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK; | ||
262 | omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask); | ||
263 | |||
264 | /* Disable DMA request generation */ | ||
265 | omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_DN_ENABLE); | ||
266 | |||
267 | /* clear Downlink channels */ | ||
268 | mcpdm->dn_channels = 0; | ||
269 | |||
270 | mcpdm->downlink = NULL; | ||
271 | |||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static irqreturn_t omap_mcpdm_irq_handler(int irq, void *dev_id) | ||
276 | { | ||
277 | struct omap_mcpdm *mcpdm_irq = dev_id; | ||
278 | int irq_status; | ||
279 | |||
280 | irq_status = omap_mcpdm_read(MCPDM_IRQSTATUS); | ||
281 | |||
282 | /* Acknowledge irq event */ | ||
283 | omap_mcpdm_write(MCPDM_IRQSTATUS, irq_status); | ||
284 | |||
285 | if (irq & MCPDM_DN_IRQ_FULL) { | ||
286 | dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status); | ||
287 | omap_mcpdm_reset_playback(1); | ||
288 | omap_mcpdm_playback_open(mcpdm_irq->downlink); | ||
289 | omap_mcpdm_reset_playback(0); | ||
290 | } | ||
291 | |||
292 | if (irq & MCPDM_DN_IRQ_EMPTY) { | ||
293 | dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status); | ||
294 | omap_mcpdm_reset_playback(1); | ||
295 | omap_mcpdm_playback_open(mcpdm_irq->downlink); | ||
296 | omap_mcpdm_reset_playback(0); | ||
297 | } | ||
298 | |||
299 | if (irq & MCPDM_DN_IRQ) { | ||
300 | dev_dbg(mcpdm_irq->dev, "DN write request\n"); | ||
301 | } | ||
302 | |||
303 | if (irq & MCPDM_UP_IRQ_FULL) { | ||
304 | dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status); | ||
305 | omap_mcpdm_reset_capture(1); | ||
306 | omap_mcpdm_capture_open(mcpdm_irq->uplink); | ||
307 | omap_mcpdm_reset_capture(0); | ||
308 | } | ||
309 | |||
310 | if (irq & MCPDM_UP_IRQ_EMPTY) { | ||
311 | dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status); | ||
312 | omap_mcpdm_reset_capture(1); | ||
313 | omap_mcpdm_capture_open(mcpdm_irq->uplink); | ||
314 | omap_mcpdm_reset_capture(0); | ||
315 | } | ||
316 | |||
317 | if (irq & MCPDM_UP_IRQ) { | ||
318 | dev_dbg(mcpdm_irq->dev, "UP write request\n"); | ||
319 | } | ||
320 | |||
321 | return IRQ_HANDLED; | ||
322 | } | ||
323 | |||
324 | int omap_mcpdm_request(void) | ||
325 | { | ||
326 | int ret; | ||
327 | |||
328 | clk_enable(mcpdm->clk); | ||
329 | |||
330 | spin_lock(&mcpdm->lock); | ||
331 | |||
332 | if (!mcpdm->free) { | ||
333 | dev_err(mcpdm->dev, "McPDM interface is in use\n"); | ||
334 | spin_unlock(&mcpdm->lock); | ||
335 | ret = -EBUSY; | ||
336 | goto err; | ||
337 | } | ||
338 | mcpdm->free = 0; | ||
339 | |||
340 | spin_unlock(&mcpdm->lock); | ||
341 | |||
342 | /* Disable lines while request is ongoing */ | ||
343 | omap_mcpdm_write(MCPDM_CTRL, 0x00); | ||
344 | |||
345 | ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler, | ||
346 | 0, "McPDM", (void *)mcpdm); | ||
347 | if (ret) { | ||
348 | dev_err(mcpdm->dev, "Request for McPDM IRQ failed\n"); | ||
349 | goto err; | ||
350 | } | ||
351 | |||
352 | return 0; | ||
353 | |||
354 | err: | ||
355 | clk_disable(mcpdm->clk); | ||
356 | return ret; | ||
357 | } | ||
358 | |||
359 | void omap_mcpdm_free(void) | ||
360 | { | ||
361 | spin_lock(&mcpdm->lock); | ||
362 | if (mcpdm->free) { | ||
363 | dev_err(mcpdm->dev, "McPDM interface is already free\n"); | ||
364 | spin_unlock(&mcpdm->lock); | ||
365 | return; | ||
366 | } | ||
367 | mcpdm->free = 1; | ||
368 | spin_unlock(&mcpdm->lock); | ||
369 | |||
370 | clk_disable(mcpdm->clk); | ||
371 | |||
372 | free_irq(mcpdm->irq, (void *)mcpdm); | ||
373 | } | ||
374 | |||
375 | /* Enable/disable DC offset cancelation for the analog | ||
376 | * headset path (PDM channels 1 and 2). | ||
377 | */ | ||
378 | int omap_mcpdm_set_offset(int offset1, int offset2) | ||
379 | { | ||
380 | int offset; | ||
381 | |||
382 | if ((offset1 > DN_OFST_MAX) || (offset2 > DN_OFST_MAX)) | ||
383 | return -EINVAL; | ||
384 | |||
385 | offset = (offset1 << DN_OFST_RX1) | (offset2 << DN_OFST_RX2); | ||
386 | |||
387 | /* offset cancellation for channel 1 */ | ||
388 | if (offset1) | ||
389 | offset |= DN_OFST_RX1_EN; | ||
390 | else | ||
391 | offset &= ~DN_OFST_RX1_EN; | ||
392 | |||
393 | /* offset cancellation for channel 2 */ | ||
394 | if (offset2) | ||
395 | offset |= DN_OFST_RX2_EN; | ||
396 | else | ||
397 | offset &= ~DN_OFST_RX2_EN; | ||
398 | |||
399 | omap_mcpdm_write(MCPDM_DN_OFFSET, offset); | ||
400 | |||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | static int __devinit omap_mcpdm_probe(struct platform_device *pdev) | ||
405 | { | ||
406 | struct resource *res; | ||
407 | int ret = 0; | ||
408 | |||
409 | mcpdm = kzalloc(sizeof(struct omap_mcpdm), GFP_KERNEL); | ||
410 | if (!mcpdm) { | ||
411 | ret = -ENOMEM; | ||
412 | goto exit; | ||
413 | } | ||
414 | |||
415 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
416 | if (res == NULL) { | ||
417 | dev_err(&pdev->dev, "no resource\n"); | ||
418 | goto err_resource; | ||
419 | } | ||
420 | |||
421 | spin_lock_init(&mcpdm->lock); | ||
422 | mcpdm->free = 1; | ||
423 | mcpdm->io_base = ioremap(res->start, resource_size(res)); | ||
424 | if (!mcpdm->io_base) { | ||
425 | ret = -ENOMEM; | ||
426 | goto err_resource; | ||
427 | } | ||
428 | |||
429 | mcpdm->irq = platform_get_irq(pdev, 0); | ||
430 | |||
431 | mcpdm->clk = clk_get(&pdev->dev, "pdm_ck"); | ||
432 | if (IS_ERR(mcpdm->clk)) { | ||
433 | ret = PTR_ERR(mcpdm->clk); | ||
434 | dev_err(&pdev->dev, "unable to get pdm_ck: %d\n", ret); | ||
435 | goto err_clk; | ||
436 | } | ||
437 | |||
438 | mcpdm->dev = &pdev->dev; | ||
439 | platform_set_drvdata(pdev, mcpdm); | ||
440 | |||
441 | return 0; | ||
442 | |||
443 | err_clk: | ||
444 | iounmap(mcpdm->io_base); | ||
445 | err_resource: | ||
446 | kfree(mcpdm); | ||
447 | exit: | ||
448 | return ret; | ||
449 | } | ||
450 | |||
451 | static int __devexit omap_mcpdm_remove(struct platform_device *pdev) | ||
452 | { | ||
453 | struct omap_mcpdm *mcpdm_ptr = platform_get_drvdata(pdev); | ||
454 | |||
455 | platform_set_drvdata(pdev, NULL); | ||
456 | |||
457 | clk_put(mcpdm_ptr->clk); | ||
458 | |||
459 | iounmap(mcpdm_ptr->io_base); | ||
460 | |||
461 | mcpdm_ptr->clk = NULL; | ||
462 | mcpdm_ptr->free = 0; | ||
463 | mcpdm_ptr->dev = NULL; | ||
464 | |||
465 | kfree(mcpdm_ptr); | ||
466 | |||
467 | return 0; | ||
468 | } | ||
469 | |||
470 | static struct platform_driver omap_mcpdm_driver = { | ||
471 | .probe = omap_mcpdm_probe, | ||
472 | .remove = __devexit_p(omap_mcpdm_remove), | ||
473 | .driver = { | ||
474 | .name = "omap-mcpdm", | ||
475 | }, | ||
476 | }; | ||
477 | |||
478 | static struct platform_device *omap_mcpdm_device; | ||
479 | |||
480 | static int __init omap_mcpdm_init(void) | ||
481 | { | ||
482 | return platform_driver_register(&omap_mcpdm_driver); | ||
483 | } | ||
484 | arch_initcall(omap_mcpdm_init); | ||
diff --git a/sound/soc/omap/mcpdm.h b/sound/soc/omap/mcpdm.h new file mode 100644 index 000000000000..7bb326ef0886 --- /dev/null +++ b/sound/soc/omap/mcpdm.h | |||
@@ -0,0 +1,151 @@ | |||
1 | /* | ||
2 | * mcpdm.h -- Defines for McPDM driver | ||
3 | * | ||
4 | * Author: Jorge Eduardo Candelaria <x0107209@ti.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * version 2 as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
18 | * 02110-1301 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | /* McPDM registers */ | ||
23 | |||
24 | #define MCPDM_REVISION 0x00 | ||
25 | #define MCPDM_SYSCONFIG 0x10 | ||
26 | #define MCPDM_IRQSTATUS_RAW 0x24 | ||
27 | #define MCPDM_IRQSTATUS 0x28 | ||
28 | #define MCPDM_IRQENABLE_SET 0x2C | ||
29 | #define MCPDM_IRQENABLE_CLR 0x30 | ||
30 | #define MCPDM_IRQWAKE_EN 0x34 | ||
31 | #define MCPDM_DMAENABLE_SET 0x38 | ||
32 | #define MCPDM_DMAENABLE_CLR 0x3C | ||
33 | #define MCPDM_DMAWAKEEN 0x40 | ||
34 | #define MCPDM_CTRL 0x44 | ||
35 | #define MCPDM_DN_DATA 0x48 | ||
36 | #define MCPDM_UP_DATA 0x4C | ||
37 | #define MCPDM_FIFO_CTRL_DN 0x50 | ||
38 | #define MCPDM_FIFO_CTRL_UP 0x54 | ||
39 | #define MCPDM_DN_OFFSET 0x58 | ||
40 | |||
41 | /* | ||
42 | * MCPDM_IRQ bit fields | ||
43 | * IRQSTATUS_RAW, IRQSTATUS, IRQENABLE_SET, IRQENABLE_CLR | ||
44 | */ | ||
45 | |||
46 | #define MCPDM_DN_IRQ (1 << 0) | ||
47 | #define MCPDM_DN_IRQ_EMPTY (1 << 1) | ||
48 | #define MCPDM_DN_IRQ_ALMST_EMPTY (1 << 2) | ||
49 | #define MCPDM_DN_IRQ_FULL (1 << 3) | ||
50 | |||
51 | #define MCPDM_UP_IRQ (1 << 8) | ||
52 | #define MCPDM_UP_IRQ_EMPTY (1 << 9) | ||
53 | #define MCPDM_UP_IRQ_ALMST_FULL (1 << 10) | ||
54 | #define MCPDM_UP_IRQ_FULL (1 << 11) | ||
55 | |||
56 | #define MCPDM_DOWNLINK_IRQ_MASK 0x00F | ||
57 | #define MCPDM_UPLINK_IRQ_MASK 0xF00 | ||
58 | |||
59 | /* | ||
60 | * MCPDM_DMAENABLE bit fields | ||
61 | */ | ||
62 | |||
63 | #define DMA_DN_ENABLE 0x1 | ||
64 | #define DMA_UP_ENABLE 0x2 | ||
65 | |||
66 | /* | ||
67 | * MCPDM_CTRL bit fields | ||
68 | */ | ||
69 | |||
70 | #define PDM_UP1_EN 0x0001 | ||
71 | #define PDM_UP2_EN 0x0002 | ||
72 | #define PDM_UP3_EN 0x0004 | ||
73 | #define PDM_DN1_EN 0x0008 | ||
74 | #define PDM_DN2_EN 0x0010 | ||
75 | #define PDM_DN3_EN 0x0020 | ||
76 | #define PDM_DN4_EN 0x0040 | ||
77 | #define PDM_DN5_EN 0x0080 | ||
78 | #define PDMOUTFORMAT 0x0100 | ||
79 | #define CMD_INT 0x0200 | ||
80 | #define STATUS_INT 0x0400 | ||
81 | #define SW_UP_RST 0x0800 | ||
82 | #define SW_DN_RST 0x1000 | ||
83 | #define PDM_UP_MASK 0x007 | ||
84 | #define PDM_DN_MASK 0x0F8 | ||
85 | #define PDM_CMD_MASK 0x200 | ||
86 | #define PDM_STATUS_MASK 0x400 | ||
87 | |||
88 | |||
89 | #define PDMOUTFORMAT_LJUST (0 << 8) | ||
90 | #define PDMOUTFORMAT_RJUST (1 << 8) | ||
91 | |||
92 | /* | ||
93 | * MCPDM_FIFO_CTRL bit fields | ||
94 | */ | ||
95 | |||
96 | #define UP_THRES_MAX 0xF | ||
97 | #define DN_THRES_MAX 0xF | ||
98 | |||
99 | /* | ||
100 | * MCPDM_DN_OFFSET bit fields | ||
101 | */ | ||
102 | |||
103 | #define DN_OFST_RX1_EN 0x0001 | ||
104 | #define DN_OFST_RX2_EN 0x0100 | ||
105 | |||
106 | #define DN_OFST_RX1 1 | ||
107 | #define DN_OFST_RX2 9 | ||
108 | #define DN_OFST_MAX 0x1F | ||
109 | |||
110 | #define MCPDM_UPLINK 1 | ||
111 | #define MCPDM_DOWNLINK 2 | ||
112 | |||
113 | struct omap_mcpdm_link { | ||
114 | int irq_mask; | ||
115 | int threshold; | ||
116 | int format; | ||
117 | int channels; | ||
118 | }; | ||
119 | |||
120 | struct omap_mcpdm_platform_data { | ||
121 | unsigned long phys_base; | ||
122 | u16 irq; | ||
123 | }; | ||
124 | |||
125 | struct omap_mcpdm { | ||
126 | struct device *dev; | ||
127 | unsigned long phys_base; | ||
128 | void __iomem *io_base; | ||
129 | u8 free; | ||
130 | int irq; | ||
131 | |||
132 | spinlock_t lock; | ||
133 | struct omap_mcpdm_platform_data *pdata; | ||
134 | struct clk *clk; | ||
135 | struct omap_mcpdm_link *downlink; | ||
136 | struct omap_mcpdm_link *uplink; | ||
137 | struct completion irq_completion; | ||
138 | |||
139 | int dn_channels; | ||
140 | int up_channels; | ||
141 | }; | ||
142 | |||
143 | extern void omap_mcpdm_start(int stream); | ||
144 | extern void omap_mcpdm_stop(int stream); | ||
145 | extern int omap_mcpdm_capture_open(struct omap_mcpdm_link *uplink); | ||
146 | extern int omap_mcpdm_playback_open(struct omap_mcpdm_link *downlink); | ||
147 | extern int omap_mcpdm_capture_close(struct omap_mcpdm_link *uplink); | ||
148 | extern int omap_mcpdm_playback_close(struct omap_mcpdm_link *downlink); | ||
149 | extern int omap_mcpdm_request(void); | ||
150 | extern void omap_mcpdm_free(void); | ||
151 | extern int omap_mcpdm_set_offset(int offset1, int offset2); | ||
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 6bbbd2ab0ee7..e814a9591f78 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c | |||
@@ -39,6 +39,14 @@ | |||
39 | 39 | ||
40 | #define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000) | 40 | #define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000) |
41 | 41 | ||
42 | #define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \ | ||
43 | xhandler_get, xhandler_put) \ | ||
44 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | ||
45 | .info = omap_mcbsp_st_info_volsw, \ | ||
46 | .get = xhandler_get, .put = xhandler_put, \ | ||
47 | .private_value = (unsigned long) &(struct soc_mixer_control) \ | ||
48 | {.min = xmin, .max = xmax} } | ||
49 | |||
42 | struct omap_mcbsp_data { | 50 | struct omap_mcbsp_data { |
43 | unsigned int bus_id; | 51 | unsigned int bus_id; |
44 | struct omap_mcbsp_reg_cfg regs; | 52 | struct omap_mcbsp_reg_cfg regs; |
@@ -82,11 +90,11 @@ static const int omap1_dma_reqs[][2] = {}; | |||
82 | static const unsigned long omap1_mcbsp_port[][2] = {}; | 90 | static const unsigned long omap1_mcbsp_port[][2] = {}; |
83 | #endif | 91 | #endif |
84 | 92 | ||
85 | #if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) | 93 | #if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) |
86 | static const int omap24xx_dma_reqs[][2] = { | 94 | static const int omap24xx_dma_reqs[][2] = { |
87 | { OMAP24XX_DMA_MCBSP1_TX, OMAP24XX_DMA_MCBSP1_RX }, | 95 | { OMAP24XX_DMA_MCBSP1_TX, OMAP24XX_DMA_MCBSP1_RX }, |
88 | { OMAP24XX_DMA_MCBSP2_TX, OMAP24XX_DMA_MCBSP2_RX }, | 96 | { OMAP24XX_DMA_MCBSP2_TX, OMAP24XX_DMA_MCBSP2_RX }, |
89 | #if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) | 97 | #if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) |
90 | { OMAP24XX_DMA_MCBSP3_TX, OMAP24XX_DMA_MCBSP3_RX }, | 98 | { OMAP24XX_DMA_MCBSP3_TX, OMAP24XX_DMA_MCBSP3_RX }, |
91 | { OMAP24XX_DMA_MCBSP4_TX, OMAP24XX_DMA_MCBSP4_RX }, | 99 | { OMAP24XX_DMA_MCBSP4_TX, OMAP24XX_DMA_MCBSP4_RX }, |
92 | { OMAP24XX_DMA_MCBSP5_TX, OMAP24XX_DMA_MCBSP5_RX }, | 100 | { OMAP24XX_DMA_MCBSP5_TX, OMAP24XX_DMA_MCBSP5_RX }, |
@@ -124,7 +132,7 @@ static const unsigned long omap2430_mcbsp_port[][2] = { | |||
124 | static const unsigned long omap2430_mcbsp_port[][2] = {}; | 132 | static const unsigned long omap2430_mcbsp_port[][2] = {}; |
125 | #endif | 133 | #endif |
126 | 134 | ||
127 | #if defined(CONFIG_ARCH_OMAP34XX) | 135 | #if defined(CONFIG_ARCH_OMAP3) |
128 | static const unsigned long omap34xx_mcbsp_port[][2] = { | 136 | static const unsigned long omap34xx_mcbsp_port[][2] = { |
129 | { OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR, | 137 | { OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR, |
130 | OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR }, | 138 | OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR }, |
@@ -287,6 +295,8 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
287 | omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma; | 295 | omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma; |
288 | omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port; | 296 | omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port; |
289 | omap_mcbsp_dai_dma_params[id][substream->stream].sync_mode = sync_mode; | 297 | omap_mcbsp_dai_dma_params[id][substream->stream].sync_mode = sync_mode; |
298 | omap_mcbsp_dai_dma_params[id][substream->stream].data_type = | ||
299 | OMAP_DMA_DATA_TYPE_S16; | ||
290 | cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream]; | 300 | cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream]; |
291 | 301 | ||
292 | if (mcbsp_data->configured) { | 302 | if (mcbsp_data->configured) { |
@@ -637,6 +647,136 @@ struct snd_soc_dai omap_mcbsp_dai[] = { | |||
637 | 647 | ||
638 | EXPORT_SYMBOL_GPL(omap_mcbsp_dai); | 648 | EXPORT_SYMBOL_GPL(omap_mcbsp_dai); |
639 | 649 | ||
650 | int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol, | ||
651 | struct snd_ctl_elem_info *uinfo) | ||
652 | { | ||
653 | struct soc_mixer_control *mc = | ||
654 | (struct soc_mixer_control *)kcontrol->private_value; | ||
655 | int max = mc->max; | ||
656 | int min = mc->min; | ||
657 | |||
658 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
659 | uinfo->count = 1; | ||
660 | uinfo->value.integer.min = min; | ||
661 | uinfo->value.integer.max = max; | ||
662 | return 0; | ||
663 | } | ||
664 | |||
665 | #define OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(id, channel) \ | ||
666 | static int \ | ||
667 | omap_mcbsp##id##_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \ | ||
668 | struct snd_ctl_elem_value *uc) \ | ||
669 | { \ | ||
670 | struct soc_mixer_control *mc = \ | ||
671 | (struct soc_mixer_control *)kc->private_value; \ | ||
672 | int max = mc->max; \ | ||
673 | int min = mc->min; \ | ||
674 | int val = uc->value.integer.value[0]; \ | ||
675 | \ | ||
676 | if (val < min || val > max) \ | ||
677 | return -EINVAL; \ | ||
678 | \ | ||
679 | /* OMAP McBSP implementation uses index values 0..4 */ \ | ||
680 | return omap_st_set_chgain((id)-1, channel, val); \ | ||
681 | } | ||
682 | |||
683 | #define OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(id, channel) \ | ||
684 | static int \ | ||
685 | omap_mcbsp##id##_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \ | ||
686 | struct snd_ctl_elem_value *uc) \ | ||
687 | { \ | ||
688 | s16 chgain; \ | ||
689 | \ | ||
690 | if (omap_st_get_chgain((id)-1, channel, &chgain)) \ | ||
691 | return -EAGAIN; \ | ||
692 | \ | ||
693 | uc->value.integer.value[0] = chgain; \ | ||
694 | return 0; \ | ||
695 | } | ||
696 | |||
697 | OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(2, 0) | ||
698 | OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(2, 1) | ||
699 | OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(3, 0) | ||
700 | OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(3, 1) | ||
701 | OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(2, 0) | ||
702 | OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(2, 1) | ||
703 | OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(3, 0) | ||
704 | OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(3, 1) | ||
705 | |||
706 | static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol, | ||
707 | struct snd_ctl_elem_value *ucontrol) | ||
708 | { | ||
709 | struct soc_mixer_control *mc = | ||
710 | (struct soc_mixer_control *)kcontrol->private_value; | ||
711 | u8 value = ucontrol->value.integer.value[0]; | ||
712 | |||
713 | if (value == omap_st_is_enabled(mc->reg)) | ||
714 | return 0; | ||
715 | |||
716 | if (value) | ||
717 | omap_st_enable(mc->reg); | ||
718 | else | ||
719 | omap_st_disable(mc->reg); | ||
720 | |||
721 | return 1; | ||
722 | } | ||
723 | |||
724 | static int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol, | ||
725 | struct snd_ctl_elem_value *ucontrol) | ||
726 | { | ||
727 | struct soc_mixer_control *mc = | ||
728 | (struct soc_mixer_control *)kcontrol->private_value; | ||
729 | |||
730 | ucontrol->value.integer.value[0] = omap_st_is_enabled(mc->reg); | ||
731 | return 0; | ||
732 | } | ||
733 | |||
734 | static const struct snd_kcontrol_new omap_mcbsp2_st_controls[] = { | ||
735 | SOC_SINGLE_EXT("McBSP2 Sidetone Switch", 1, 0, 1, 0, | ||
736 | omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), | ||
737 | OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP2 Sidetone Channel 0 Volume", | ||
738 | -32768, 32767, | ||
739 | omap_mcbsp2_get_st_ch0_volume, | ||
740 | omap_mcbsp2_set_st_ch0_volume), | ||
741 | OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP2 Sidetone Channel 1 Volume", | ||
742 | -32768, 32767, | ||
743 | omap_mcbsp2_get_st_ch1_volume, | ||
744 | omap_mcbsp2_set_st_ch1_volume), | ||
745 | }; | ||
746 | |||
747 | static const struct snd_kcontrol_new omap_mcbsp3_st_controls[] = { | ||
748 | SOC_SINGLE_EXT("McBSP3 Sidetone Switch", 2, 0, 1, 0, | ||
749 | omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), | ||
750 | OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP3 Sidetone Channel 0 Volume", | ||
751 | -32768, 32767, | ||
752 | omap_mcbsp3_get_st_ch0_volume, | ||
753 | omap_mcbsp3_set_st_ch0_volume), | ||
754 | OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP3 Sidetone Channel 1 Volume", | ||
755 | -32768, 32767, | ||
756 | omap_mcbsp3_get_st_ch1_volume, | ||
757 | omap_mcbsp3_set_st_ch1_volume), | ||
758 | }; | ||
759 | |||
760 | int omap_mcbsp_st_add_controls(struct snd_soc_codec *codec, int mcbsp_id) | ||
761 | { | ||
762 | if (!cpu_is_omap34xx()) | ||
763 | return -ENODEV; | ||
764 | |||
765 | switch (mcbsp_id) { | ||
766 | case 1: /* McBSP 2 */ | ||
767 | return snd_soc_add_controls(codec, omap_mcbsp2_st_controls, | ||
768 | ARRAY_SIZE(omap_mcbsp2_st_controls)); | ||
769 | case 2: /* McBSP 3 */ | ||
770 | return snd_soc_add_controls(codec, omap_mcbsp3_st_controls, | ||
771 | ARRAY_SIZE(omap_mcbsp3_st_controls)); | ||
772 | default: | ||
773 | break; | ||
774 | } | ||
775 | |||
776 | return -EINVAL; | ||
777 | } | ||
778 | EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls); | ||
779 | |||
640 | static int __init snd_omap_mcbsp_init(void) | 780 | static int __init snd_omap_mcbsp_init(void) |
641 | { | 781 | { |
642 | return snd_soc_register_dais(omap_mcbsp_dai, | 782 | return snd_soc_register_dais(omap_mcbsp_dai, |
diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h index 647d2f981ab0..6c363e5f4387 100644 --- a/sound/soc/omap/omap-mcbsp.h +++ b/sound/soc/omap/omap-mcbsp.h | |||
@@ -50,11 +50,13 @@ enum omap_mcbsp_div { | |||
50 | #undef NUM_LINKS | 50 | #undef NUM_LINKS |
51 | #define NUM_LINKS 3 | 51 | #define NUM_LINKS 3 |
52 | #endif | 52 | #endif |
53 | #if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) | 53 | #if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) |
54 | #undef NUM_LINKS | 54 | #undef NUM_LINKS |
55 | #define NUM_LINKS 5 | 55 | #define NUM_LINKS 5 |
56 | #endif | 56 | #endif |
57 | 57 | ||
58 | extern struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS]; | 58 | extern struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS]; |
59 | 59 | ||
60 | int omap_mcbsp_st_add_controls(struct snd_soc_codec *codec, int mcbsp_id); | ||
61 | |||
60 | #endif | 62 | #endif |
diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c new file mode 100644 index 000000000000..25f19e4728bf --- /dev/null +++ b/sound/soc/omap/omap-mcpdm.c | |||
@@ -0,0 +1,251 @@ | |||
1 | /* | ||
2 | * omap-mcpdm.c -- OMAP ALSA SoC DAI driver using McPDM port | ||
3 | * | ||
4 | * Copyright (C) 2009 Texas Instruments | ||
5 | * | ||
6 | * Author: Misael Lopez Cruz <x0052729@ti.com> | ||
7 | * Contact: Jorge Eduardo Candelaria <x0107209@ti.com> | ||
8 | * Margarita Olaya <magi.olaya@ti.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * version 2 as published by the Free Software Foundation. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, but | ||
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
22 | * 02110-1301 USA | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | #include <linux/init.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/device.h> | ||
29 | #include <sound/core.h> | ||
30 | #include <sound/pcm.h> | ||
31 | #include <sound/pcm_params.h> | ||
32 | #include <sound/initval.h> | ||
33 | #include <sound/soc.h> | ||
34 | |||
35 | #include <plat/control.h> | ||
36 | #include <plat/dma.h> | ||
37 | #include <plat/mcbsp.h> | ||
38 | #include "mcpdm.h" | ||
39 | #include "omap-mcpdm.h" | ||
40 | #include "omap-pcm.h" | ||
41 | |||
42 | struct omap_mcpdm_data { | ||
43 | struct omap_mcpdm_link *links; | ||
44 | int active; | ||
45 | }; | ||
46 | |||
47 | static struct omap_mcpdm_link omap_mcpdm_links[] = { | ||
48 | /* downlink */ | ||
49 | { | ||
50 | .irq_mask = MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL, | ||
51 | .threshold = 1, | ||
52 | .format = PDMOUTFORMAT_LJUST, | ||
53 | }, | ||
54 | /* uplink */ | ||
55 | { | ||
56 | .irq_mask = MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL, | ||
57 | .threshold = 1, | ||
58 | .format = PDMOUTFORMAT_LJUST, | ||
59 | }, | ||
60 | }; | ||
61 | |||
62 | static struct omap_mcpdm_data mcpdm_data = { | ||
63 | .links = omap_mcpdm_links, | ||
64 | .active = 0, | ||
65 | }; | ||
66 | |||
67 | /* | ||
68 | * Stream DMA parameters | ||
69 | */ | ||
70 | static struct omap_pcm_dma_data omap_mcpdm_dai_dma_params[] = { | ||
71 | { | ||
72 | .name = "Audio playback", | ||
73 | .dma_req = OMAP44XX_DMA_MCPDM_DL, | ||
74 | .data_type = OMAP_DMA_DATA_TYPE_S32, | ||
75 | .sync_mode = OMAP_DMA_SYNC_PACKET, | ||
76 | .packet_size = 16, | ||
77 | .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_DN_DATA, | ||
78 | }, | ||
79 | { | ||
80 | .name = "Audio capture", | ||
81 | .dma_req = OMAP44XX_DMA_MCPDM_UP, | ||
82 | .data_type = OMAP_DMA_DATA_TYPE_S32, | ||
83 | .sync_mode = OMAP_DMA_SYNC_PACKET, | ||
84 | .packet_size = 16, | ||
85 | .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_UP_DATA, | ||
86 | }, | ||
87 | }; | ||
88 | |||
89 | static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream, | ||
90 | struct snd_soc_dai *dai) | ||
91 | { | ||
92 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
93 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
94 | int err = 0; | ||
95 | |||
96 | if (!cpu_dai->active) | ||
97 | err = omap_mcpdm_request(); | ||
98 | |||
99 | return err; | ||
100 | } | ||
101 | |||
102 | static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream, | ||
103 | struct snd_soc_dai *dai) | ||
104 | { | ||
105 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
106 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
107 | |||
108 | if (!cpu_dai->active) | ||
109 | omap_mcpdm_free(); | ||
110 | } | ||
111 | |||
112 | static int omap_mcpdm_dai_trigger(struct snd_pcm_substream *substream, int cmd, | ||
113 | struct snd_soc_dai *dai) | ||
114 | { | ||
115 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
116 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
117 | struct omap_mcpdm_data *mcpdm_priv = cpu_dai->private_data; | ||
118 | int stream = substream->stream; | ||
119 | int err = 0; | ||
120 | |||
121 | switch (cmd) { | ||
122 | case SNDRV_PCM_TRIGGER_START: | ||
123 | case SNDRV_PCM_TRIGGER_RESUME: | ||
124 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
125 | if (!mcpdm_priv->active++) | ||
126 | omap_mcpdm_start(stream); | ||
127 | break; | ||
128 | |||
129 | case SNDRV_PCM_TRIGGER_STOP: | ||
130 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
131 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
132 | if (!--mcpdm_priv->active) | ||
133 | omap_mcpdm_stop(stream); | ||
134 | break; | ||
135 | default: | ||
136 | err = -EINVAL; | ||
137 | } | ||
138 | |||
139 | return err; | ||
140 | } | ||
141 | |||
142 | static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream, | ||
143 | struct snd_pcm_hw_params *params, | ||
144 | struct snd_soc_dai *dai) | ||
145 | { | ||
146 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
147 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
148 | struct omap_mcpdm_data *mcpdm_priv = cpu_dai->private_data; | ||
149 | struct omap_mcpdm_link *mcpdm_links = mcpdm_priv->links; | ||
150 | int stream = substream->stream; | ||
151 | int channels, err, link_mask = 0; | ||
152 | |||
153 | cpu_dai->dma_data = &omap_mcpdm_dai_dma_params[stream]; | ||
154 | |||
155 | channels = params_channels(params); | ||
156 | switch (channels) { | ||
157 | case 4: | ||
158 | if (stream == SNDRV_PCM_STREAM_CAPTURE) | ||
159 | /* up to 2 channels for capture */ | ||
160 | return -EINVAL; | ||
161 | link_mask |= 1 << 3; | ||
162 | case 3: | ||
163 | if (stream == SNDRV_PCM_STREAM_CAPTURE) | ||
164 | /* up to 2 channels for capture */ | ||
165 | return -EINVAL; | ||
166 | link_mask |= 1 << 2; | ||
167 | case 2: | ||
168 | link_mask |= 1 << 1; | ||
169 | case 1: | ||
170 | link_mask |= 1 << 0; | ||
171 | break; | ||
172 | default: | ||
173 | /* unsupported number of channels */ | ||
174 | return -EINVAL; | ||
175 | } | ||
176 | |||
177 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
178 | mcpdm_links[stream].channels = link_mask << 3; | ||
179 | err = omap_mcpdm_playback_open(&mcpdm_links[stream]); | ||
180 | } else { | ||
181 | mcpdm_links[stream].channels = link_mask << 0; | ||
182 | err = omap_mcpdm_capture_open(&mcpdm_links[stream]); | ||
183 | } | ||
184 | |||
185 | return err; | ||
186 | } | ||
187 | |||
188 | static int omap_mcpdm_dai_hw_free(struct snd_pcm_substream *substream, | ||
189 | struct snd_soc_dai *dai) | ||
190 | { | ||
191 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
192 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
193 | struct omap_mcpdm_data *mcpdm_priv = cpu_dai->private_data; | ||
194 | struct omap_mcpdm_link *mcpdm_links = mcpdm_priv->links; | ||
195 | int stream = substream->stream; | ||
196 | int err; | ||
197 | |||
198 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
199 | err = omap_mcpdm_playback_close(&mcpdm_links[stream]); | ||
200 | else | ||
201 | err = omap_mcpdm_capture_close(&mcpdm_links[stream]); | ||
202 | |||
203 | return err; | ||
204 | } | ||
205 | |||
206 | static struct snd_soc_dai_ops omap_mcpdm_dai_ops = { | ||
207 | .startup = omap_mcpdm_dai_startup, | ||
208 | .shutdown = omap_mcpdm_dai_shutdown, | ||
209 | .trigger = omap_mcpdm_dai_trigger, | ||
210 | .hw_params = omap_mcpdm_dai_hw_params, | ||
211 | .hw_free = omap_mcpdm_dai_hw_free, | ||
212 | }; | ||
213 | |||
214 | #define OMAP_MCPDM_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) | ||
215 | #define OMAP_MCPDM_FORMATS (SNDRV_PCM_FMTBIT_S32_LE) | ||
216 | |||
217 | struct snd_soc_dai omap_mcpdm_dai = { | ||
218 | .name = "omap-mcpdm", | ||
219 | .id = -1, | ||
220 | .playback = { | ||
221 | .channels_min = 1, | ||
222 | .channels_max = 4, | ||
223 | .rates = OMAP_MCPDM_RATES, | ||
224 | .formats = OMAP_MCPDM_FORMATS, | ||
225 | }, | ||
226 | .capture = { | ||
227 | .channels_min = 1, | ||
228 | .channels_max = 2, | ||
229 | .rates = OMAP_MCPDM_RATES, | ||
230 | .formats = OMAP_MCPDM_FORMATS, | ||
231 | }, | ||
232 | .ops = &omap_mcpdm_dai_ops, | ||
233 | .private_data = &mcpdm_data, | ||
234 | }; | ||
235 | EXPORT_SYMBOL_GPL(omap_mcpdm_dai); | ||
236 | |||
237 | static int __init snd_omap_mcpdm_init(void) | ||
238 | { | ||
239 | return snd_soc_register_dai(&omap_mcpdm_dai); | ||
240 | } | ||
241 | module_init(snd_omap_mcpdm_init); | ||
242 | |||
243 | static void __exit snd_omap_mcpdm_exit(void) | ||
244 | { | ||
245 | snd_soc_unregister_dai(&omap_mcpdm_dai); | ||
246 | } | ||
247 | module_exit(snd_omap_mcpdm_exit); | ||
248 | |||
249 | MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>"); | ||
250 | MODULE_DESCRIPTION("OMAP PDM SoC Interface"); | ||
251 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/omap/omap-mcpdm.h b/sound/soc/omap/omap-mcpdm.h new file mode 100644 index 000000000000..73b80d559345 --- /dev/null +++ b/sound/soc/omap/omap-mcpdm.h | |||
@@ -0,0 +1,29 @@ | |||
1 | /* | ||
2 | * omap-mcpdm.h | ||
3 | * | ||
4 | * Copyright (C) 2009 Texas Instruments | ||
5 | * | ||
6 | * Contact: Misael Lopez Cruz <x0052729@ti.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * version 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
20 | * 02110-1301 USA | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #ifndef __OMAP_MCPDM_H__ | ||
25 | #define __OMAP_MCPDM_H__ | ||
26 | |||
27 | extern struct snd_soc_dai omap_mcpdm_dai; | ||
28 | |||
29 | #endif /* End of __OMAP_MCPDM_H__ */ | ||
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index 9db2770e9640..825db385f01f 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c | |||
@@ -37,7 +37,8 @@ static const struct snd_pcm_hardware omap_pcm_hardware = { | |||
37 | SNDRV_PCM_INFO_INTERLEAVED | | 37 | SNDRV_PCM_INFO_INTERLEAVED | |
38 | SNDRV_PCM_INFO_PAUSE | | 38 | SNDRV_PCM_INFO_PAUSE | |
39 | SNDRV_PCM_INFO_RESUME, | 39 | SNDRV_PCM_INFO_RESUME, |
40 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | 40 | .formats = SNDRV_PCM_FMTBIT_S16_LE | |
41 | SNDRV_PCM_FMTBIT_S32_LE, | ||
41 | .period_bytes_min = 32, | 42 | .period_bytes_min = 32, |
42 | .period_bytes_max = 64 * 1024, | 43 | .period_bytes_max = 64 * 1024, |
43 | .periods_min = 2, | 44 | .periods_min = 2, |
@@ -149,6 +150,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) | |||
149 | struct omap_runtime_data *prtd = runtime->private_data; | 150 | struct omap_runtime_data *prtd = runtime->private_data; |
150 | struct omap_pcm_dma_data *dma_data = prtd->dma_data; | 151 | struct omap_pcm_dma_data *dma_data = prtd->dma_data; |
151 | struct omap_dma_channel_params dma_params; | 152 | struct omap_dma_channel_params dma_params; |
153 | int bytes; | ||
152 | 154 | ||
153 | /* return if this is a bufferless transfer e.g. | 155 | /* return if this is a bufferless transfer e.g. |
154 | * codec <--> BT codec or GSM modem -- lg FIXME */ | 156 | * codec <--> BT codec or GSM modem -- lg FIXME */ |
@@ -156,11 +158,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) | |||
156 | return 0; | 158 | return 0; |
157 | 159 | ||
158 | memset(&dma_params, 0, sizeof(dma_params)); | 160 | memset(&dma_params, 0, sizeof(dma_params)); |
159 | /* | 161 | dma_params.data_type = dma_data->data_type; |
160 | * Note: Regardless of interface data formats supported by OMAP McBSP | ||
161 | * or EAC blocks, internal representation is always fixed 16-bit/sample | ||
162 | */ | ||
163 | dma_params.data_type = OMAP_DMA_DATA_TYPE_S16; | ||
164 | dma_params.trigger = dma_data->dma_req; | 162 | dma_params.trigger = dma_data->dma_req; |
165 | dma_params.sync_mode = dma_data->sync_mode; | 163 | dma_params.sync_mode = dma_data->sync_mode; |
166 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 164 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
@@ -170,6 +168,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) | |||
170 | dma_params.src_start = runtime->dma_addr; | 168 | dma_params.src_start = runtime->dma_addr; |
171 | dma_params.dst_start = dma_data->port_addr; | 169 | dma_params.dst_start = dma_data->port_addr; |
172 | dma_params.dst_port = OMAP_DMA_PORT_MPUI; | 170 | dma_params.dst_port = OMAP_DMA_PORT_MPUI; |
171 | dma_params.dst_fi = dma_data->packet_size; | ||
173 | } else { | 172 | } else { |
174 | dma_params.src_amode = OMAP_DMA_AMODE_CONSTANT; | 173 | dma_params.src_amode = OMAP_DMA_AMODE_CONSTANT; |
175 | dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC; | 174 | dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC; |
@@ -177,6 +176,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) | |||
177 | dma_params.src_start = dma_data->port_addr; | 176 | dma_params.src_start = dma_data->port_addr; |
178 | dma_params.dst_start = runtime->dma_addr; | 177 | dma_params.dst_start = runtime->dma_addr; |
179 | dma_params.src_port = OMAP_DMA_PORT_MPUI; | 178 | dma_params.src_port = OMAP_DMA_PORT_MPUI; |
179 | dma_params.src_fi = dma_data->packet_size; | ||
180 | } | 180 | } |
181 | /* | 181 | /* |
182 | * Set DMA transfer frame size equal to ALSA period size and frame | 182 | * Set DMA transfer frame size equal to ALSA period size and frame |
@@ -184,7 +184,8 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) | |||
184 | * we can transfer the whole ALSA buffer with single DMA transfer but | 184 | * we can transfer the whole ALSA buffer with single DMA transfer but |
185 | * still can get an interrupt at each period bounary | 185 | * still can get an interrupt at each period bounary |
186 | */ | 186 | */ |
187 | dma_params.elem_count = snd_pcm_lib_period_bytes(substream) / 2; | 187 | bytes = snd_pcm_lib_period_bytes(substream); |
188 | dma_params.elem_count = bytes >> dma_data->data_type; | ||
188 | dma_params.frame_count = runtime->periods; | 189 | dma_params.frame_count = runtime->periods; |
189 | omap_set_dma_params(prtd->dma_ch, &dma_params); | 190 | omap_set_dma_params(prtd->dma_ch, &dma_params); |
190 | 191 | ||
diff --git a/sound/soc/omap/omap-pcm.h b/sound/soc/omap/omap-pcm.h index 38a821dd4118..b19975d26907 100644 --- a/sound/soc/omap/omap-pcm.h +++ b/sound/soc/omap/omap-pcm.h | |||
@@ -29,8 +29,10 @@ struct omap_pcm_dma_data { | |||
29 | char *name; /* stream identifier */ | 29 | char *name; /* stream identifier */ |
30 | int dma_req; /* DMA request line */ | 30 | int dma_req; /* DMA request line */ |
31 | unsigned long port_addr; /* transmit/receive register */ | 31 | unsigned long port_addr; /* transmit/receive register */ |
32 | int sync_mode; /* DMA sync mode */ | ||
33 | void (*set_threshold)(struct snd_pcm_substream *substream); | 32 | void (*set_threshold)(struct snd_pcm_substream *substream); |
33 | int data_type; /* data type 8,16,32 */ | ||
34 | int sync_mode; /* DMA sync mode */ | ||
35 | int packet_size; /* packet size only in PACKET mode */ | ||
34 | }; | 36 | }; |
35 | 37 | ||
36 | extern struct snd_soc_platform omap_soc_platform; | 38 | extern struct snd_soc_platform omap_soc_platform; |
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 3c36d24a6c20..993abb730dfa 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c | |||
@@ -388,7 +388,7 @@ static void fsi_soft_all_reset(struct fsi_master *master) | |||
388 | } | 388 | } |
389 | 389 | ||
390 | /* playback interrupt */ | 390 | /* playback interrupt */ |
391 | static int fsi_data_push(struct fsi_priv *fsi) | 391 | static int fsi_data_push(struct fsi_priv *fsi, int startup) |
392 | { | 392 | { |
393 | struct snd_pcm_runtime *runtime; | 393 | struct snd_pcm_runtime *runtime; |
394 | struct snd_pcm_substream *substream = NULL; | 394 | struct snd_pcm_substream *substream = NULL; |
@@ -397,7 +397,7 @@ static int fsi_data_push(struct fsi_priv *fsi) | |||
397 | int fifo_free; | 397 | int fifo_free; |
398 | int width; | 398 | int width; |
399 | u8 *start; | 399 | u8 *start; |
400 | int i, ret, over_period; | 400 | int i, over_period; |
401 | 401 | ||
402 | if (!fsi || | 402 | if (!fsi || |
403 | !fsi->substream || | 403 | !fsi->substream || |
@@ -453,24 +453,26 @@ static int fsi_data_push(struct fsi_priv *fsi) | |||
453 | 453 | ||
454 | fsi->byte_offset += send * width; | 454 | fsi->byte_offset += send * width; |
455 | 455 | ||
456 | ret = 0; | ||
457 | status = fsi_reg_read(fsi, DOFF_ST); | 456 | status = fsi_reg_read(fsi, DOFF_ST); |
458 | if (status & ERR_OVER) { | 457 | if (!startup) { |
459 | struct snd_soc_dai *dai = fsi_get_dai(substream); | 458 | struct snd_soc_dai *dai = fsi_get_dai(substream); |
460 | dev_err(dai->dev, "over run error\n"); | 459 | |
461 | fsi_reg_write(fsi, DOFF_ST, status & ~ST_ERR); | 460 | if (status & ERR_OVER) |
462 | ret = -EIO; | 461 | dev_err(dai->dev, "over run\n"); |
462 | if (status & ERR_UNDER) | ||
463 | dev_err(dai->dev, "under run\n"); | ||
463 | } | 464 | } |
465 | fsi_reg_write(fsi, DOFF_ST, 0); | ||
464 | 466 | ||
465 | fsi_irq_enable(fsi, 1); | 467 | fsi_irq_enable(fsi, 1); |
466 | 468 | ||
467 | if (over_period) | 469 | if (over_period) |
468 | snd_pcm_period_elapsed(substream); | 470 | snd_pcm_period_elapsed(substream); |
469 | 471 | ||
470 | return ret; | 472 | return 0; |
471 | } | 473 | } |
472 | 474 | ||
473 | static int fsi_data_pop(struct fsi_priv *fsi) | 475 | static int fsi_data_pop(struct fsi_priv *fsi, int startup) |
474 | { | 476 | { |
475 | struct snd_pcm_runtime *runtime; | 477 | struct snd_pcm_runtime *runtime; |
476 | struct snd_pcm_substream *substream = NULL; | 478 | struct snd_pcm_substream *substream = NULL; |
@@ -479,7 +481,7 @@ static int fsi_data_pop(struct fsi_priv *fsi) | |||
479 | int fifo_fill; | 481 | int fifo_fill; |
480 | int width; | 482 | int width; |
481 | u8 *start; | 483 | u8 *start; |
482 | int i, ret, over_period; | 484 | int i, over_period; |
483 | 485 | ||
484 | if (!fsi || | 486 | if (!fsi || |
485 | !fsi->substream || | 487 | !fsi->substream || |
@@ -534,21 +536,23 @@ static int fsi_data_pop(struct fsi_priv *fsi) | |||
534 | 536 | ||
535 | fsi->byte_offset += fifo_fill * width; | 537 | fsi->byte_offset += fifo_fill * width; |
536 | 538 | ||
537 | ret = 0; | ||
538 | status = fsi_reg_read(fsi, DIFF_ST); | 539 | status = fsi_reg_read(fsi, DIFF_ST); |
539 | if (status & ERR_UNDER) { | 540 | if (!startup) { |
540 | struct snd_soc_dai *dai = fsi_get_dai(substream); | 541 | struct snd_soc_dai *dai = fsi_get_dai(substream); |
541 | dev_err(dai->dev, "under run error\n"); | 542 | |
542 | fsi_reg_write(fsi, DIFF_ST, status & ~ST_ERR); | 543 | if (status & ERR_OVER) |
543 | ret = -EIO; | 544 | dev_err(dai->dev, "over run\n"); |
545 | if (status & ERR_UNDER) | ||
546 | dev_err(dai->dev, "under run\n"); | ||
544 | } | 547 | } |
548 | fsi_reg_write(fsi, DIFF_ST, 0); | ||
545 | 549 | ||
546 | fsi_irq_enable(fsi, 0); | 550 | fsi_irq_enable(fsi, 0); |
547 | 551 | ||
548 | if (over_period) | 552 | if (over_period) |
549 | snd_pcm_period_elapsed(substream); | 553 | snd_pcm_period_elapsed(substream); |
550 | 554 | ||
551 | return ret; | 555 | return 0; |
552 | } | 556 | } |
553 | 557 | ||
554 | static irqreturn_t fsi_interrupt(int irq, void *data) | 558 | static irqreturn_t fsi_interrupt(int irq, void *data) |
@@ -562,13 +566,13 @@ static irqreturn_t fsi_interrupt(int irq, void *data) | |||
562 | fsi_master_write(master, SOFT_RST, status | 0x00000010); | 566 | fsi_master_write(master, SOFT_RST, status | 0x00000010); |
563 | 567 | ||
564 | if (int_st & INT_A_OUT) | 568 | if (int_st & INT_A_OUT) |
565 | fsi_data_push(&master->fsia); | 569 | fsi_data_push(&master->fsia, 0); |
566 | if (int_st & INT_B_OUT) | 570 | if (int_st & INT_B_OUT) |
567 | fsi_data_push(&master->fsib); | 571 | fsi_data_push(&master->fsib, 0); |
568 | if (int_st & INT_A_IN) | 572 | if (int_st & INT_A_IN) |
569 | fsi_data_pop(&master->fsia); | 573 | fsi_data_pop(&master->fsia, 0); |
570 | if (int_st & INT_B_IN) | 574 | if (int_st & INT_B_IN) |
571 | fsi_data_pop(&master->fsib); | 575 | fsi_data_pop(&master->fsib, 0); |
572 | 576 | ||
573 | fsi_master_write(master, INT_ST, 0x0000000); | 577 | fsi_master_write(master, INT_ST, 0x0000000); |
574 | 578 | ||
@@ -726,7 +730,7 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, | |||
726 | fsi_stream_push(fsi, substream, | 730 | fsi_stream_push(fsi, substream, |
727 | frames_to_bytes(runtime, runtime->buffer_size), | 731 | frames_to_bytes(runtime, runtime->buffer_size), |
728 | frames_to_bytes(runtime, runtime->period_size)); | 732 | frames_to_bytes(runtime, runtime->period_size)); |
729 | ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi); | 733 | ret = is_play ? fsi_data_push(fsi, 1) : fsi_data_pop(fsi, 1); |
730 | break; | 734 | break; |
731 | case SNDRV_PCM_TRIGGER_STOP: | 735 | case SNDRV_PCM_TRIGGER_STOP: |
732 | fsi_irq_disable(fsi, is_play); | 736 | fsi_irq_disable(fsi, is_play); |
diff --git a/sound/soc/sh/siu.h b/sound/soc/sh/siu.h index 9cc04ab2bce7..c0bfab8fed3d 100644 --- a/sound/soc/sh/siu.h +++ b/sound/soc/sh/siu.h | |||
@@ -72,7 +72,7 @@ struct siu_firmware { | |||
72 | #include <linux/interrupt.h> | 72 | #include <linux/interrupt.h> |
73 | #include <linux/io.h> | 73 | #include <linux/io.h> |
74 | 74 | ||
75 | #include <asm/dma-sh.h> | 75 | #include <asm/dmaengine.h> |
76 | 76 | ||
77 | #include <sound/core.h> | 77 | #include <sound/core.h> |
78 | #include <sound/pcm.h> | 78 | #include <sound/pcm.h> |
diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c index c5efc30f0136..ba7f8d05d977 100644 --- a/sound/soc/sh/siu_pcm.c +++ b/sound/soc/sh/siu_pcm.c | |||
@@ -32,7 +32,7 @@ | |||
32 | #include <sound/pcm_params.h> | 32 | #include <sound/pcm_params.h> |
33 | #include <sound/soc-dai.h> | 33 | #include <sound/soc-dai.h> |
34 | 34 | ||
35 | #include <asm/dma-sh.h> | 35 | #include <asm/dmaengine.h> |
36 | #include <asm/siu.h> | 36 | #include <asm/siu.h> |
37 | 37 | ||
38 | #include "siu.h" | 38 | #include "siu.h" |
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4011ad3dc57a..06c38d1502b7 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c | |||
@@ -427,24 +427,24 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) | |||
427 | if (!runtime->hw.rates) { | 427 | if (!runtime->hw.rates) { |
428 | printk(KERN_ERR "asoc: %s <-> %s No matching rates\n", | 428 | printk(KERN_ERR "asoc: %s <-> %s No matching rates\n", |
429 | codec_dai->name, cpu_dai->name); | 429 | codec_dai->name, cpu_dai->name); |
430 | goto machine_err; | 430 | goto config_err; |
431 | } | 431 | } |
432 | if (!runtime->hw.formats) { | 432 | if (!runtime->hw.formats) { |
433 | printk(KERN_ERR "asoc: %s <-> %s No matching formats\n", | 433 | printk(KERN_ERR "asoc: %s <-> %s No matching formats\n", |
434 | codec_dai->name, cpu_dai->name); | 434 | codec_dai->name, cpu_dai->name); |
435 | goto machine_err; | 435 | goto config_err; |
436 | } | 436 | } |
437 | if (!runtime->hw.channels_min || !runtime->hw.channels_max) { | 437 | if (!runtime->hw.channels_min || !runtime->hw.channels_max) { |
438 | printk(KERN_ERR "asoc: %s <-> %s No matching channels\n", | 438 | printk(KERN_ERR "asoc: %s <-> %s No matching channels\n", |
439 | codec_dai->name, cpu_dai->name); | 439 | codec_dai->name, cpu_dai->name); |
440 | goto machine_err; | 440 | goto config_err; |
441 | } | 441 | } |
442 | 442 | ||
443 | /* Symmetry only applies if we've already got an active stream. */ | 443 | /* Symmetry only applies if we've already got an active stream. */ |
444 | if (cpu_dai->active || codec_dai->active) { | 444 | if (cpu_dai->active || codec_dai->active) { |
445 | ret = soc_pcm_apply_symmetry(substream); | 445 | ret = soc_pcm_apply_symmetry(substream); |
446 | if (ret != 0) | 446 | if (ret != 0) |
447 | goto machine_err; | 447 | goto config_err; |
448 | } | 448 | } |
449 | 449 | ||
450 | pr_debug("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name); | 450 | pr_debug("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name); |
@@ -467,10 +467,14 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) | |||
467 | mutex_unlock(&pcm_mutex); | 467 | mutex_unlock(&pcm_mutex); |
468 | return 0; | 468 | return 0; |
469 | 469 | ||
470 | machine_err: | 470 | config_err: |
471 | if (machine->ops && machine->ops->shutdown) | 471 | if (machine->ops && machine->ops->shutdown) |
472 | machine->ops->shutdown(substream); | 472 | machine->ops->shutdown(substream); |
473 | 473 | ||
474 | machine_err: | ||
475 | if (codec_dai->ops->shutdown) | ||
476 | codec_dai->ops->shutdown(substream, codec_dai); | ||
477 | |||
474 | codec_dai_err: | 478 | codec_dai_err: |
475 | if (platform->pcm_ops->close) | 479 | if (platform->pcm_ops->close) |
476 | platform->pcm_ops->close(substream); | 480 | platform->pcm_ops->close(substream); |
@@ -1002,6 +1006,12 @@ static int soc_resume(struct device *dev) | |||
1002 | struct snd_soc_card *card = socdev->card; | 1006 | struct snd_soc_card *card = socdev->card; |
1003 | struct snd_soc_dai *cpu_dai = card->dai_link[0].cpu_dai; | 1007 | struct snd_soc_dai *cpu_dai = card->dai_link[0].cpu_dai; |
1004 | 1008 | ||
1009 | /* If the initialization of this soc device failed, there is no codec | ||
1010 | * associated with it. Just bail out in this case. | ||
1011 | */ | ||
1012 | if (!card->codec) | ||
1013 | return 0; | ||
1014 | |||
1005 | /* AC97 devices might have other drivers hanging off them so | 1015 | /* AC97 devices might have other drivers hanging off them so |
1006 | * need to resume immediately. Other drivers don't have that | 1016 | * need to resume immediately. Other drivers don't have that |
1007 | * problem and may take a substantial amount of time to resume | 1017 | * problem and may take a substantial amount of time to resume |