aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJarkko Nikula <jarkko.nikula@nokia.com>2008-04-25 07:55:19 -0400
committerTakashi Iwai <tiwai@suse.de>2008-04-29 13:01:56 -0400
commit2e74796a45ee05bd901968ef1eb6cb884d1dca5e (patch)
treeeb674d0f0ab9b803756aa157f728e6aef6792dea
parent7f70f046af855e027f7b53ec7d214c2d0c790b6d (diff)
[ALSA] ASoC: Add drivers for the Texas Instruments OMAP processors
Add common OMAP ASoC drivers and machine driver for Nokia N810. Currently supported features are: - Covers OMAPs from 1510 to 2420 - Common DMA driver - DAI link driver using McBSP port in I2S mode - Basic machine driver for Nokia N810 Signed-off-by: Jarkko Nikula <jarkko.nikula@nokia.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/soc/Kconfig1
-rw-r--r--sound/soc/Makefile2
-rw-r--r--sound/soc/omap/Kconfig19
-rw-r--r--sound/soc/omap/Makefile11
-rw-r--r--sound/soc/omap/n810.c336
-rw-r--r--sound/soc/omap/omap-mcbsp.c414
-rw-r--r--sound/soc/omap/omap-mcbsp.h49
-rw-r--r--sound/soc/omap/omap-pcm.c357
-rw-r--r--sound/soc/omap/omap-pcm.h35
9 files changed, 1223 insertions, 1 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index a3b51df2bea1..18f28ac4bfe8 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -30,6 +30,7 @@ source "sound/soc/s3c24xx/Kconfig"
30source "sound/soc/sh/Kconfig" 30source "sound/soc/sh/Kconfig"
31source "sound/soc/fsl/Kconfig" 31source "sound/soc/fsl/Kconfig"
32source "sound/soc/davinci/Kconfig" 32source "sound/soc/davinci/Kconfig"
33source "sound/soc/omap/Kconfig"
33 34
34# Supported codecs 35# Supported codecs
35source "sound/soc/codecs/Kconfig" 36source "sound/soc/codecs/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index e489dbdde458..782db2127108 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,4 +1,4 @@
1snd-soc-core-objs := soc-core.o soc-dapm.o 1snd-soc-core-objs := soc-core.o soc-dapm.o
2 2
3obj-$(CONFIG_SND_SOC) += snd-soc-core.o 3obj-$(CONFIG_SND_SOC) += snd-soc-core.o
4obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/ 4obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/ omap/
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
new file mode 100644
index 000000000000..0230d83e8e5e
--- /dev/null
+++ b/sound/soc/omap/Kconfig
@@ -0,0 +1,19 @@
1menu "SoC Audio for the Texas Instruments OMAP"
2
3config SND_OMAP_SOC
4 tristate "SoC Audio for the Texas Instruments OMAP chips"
5 depends on ARCH_OMAP && SND_SOC
6
7config SND_OMAP_SOC_MCBSP
8 tristate
9 select OMAP_MCBSP
10
11config SND_OMAP_SOC_N810
12 tristate "SoC Audio support for Nokia N810"
13 depends on SND_OMAP_SOC && MACH_NOKIA_N810
14 select SND_OMAP_SOC_MCBSP
15 select SND_SOC_TLV320AIC3X
16 help
17 Say Y if you want to add support for SoC audio on Nokia N810.
18
19endmenu
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
new file mode 100644
index 000000000000..d8d8d58075e3
--- /dev/null
+++ b/sound/soc/omap/Makefile
@@ -0,0 +1,11 @@
1# OMAP Platform Support
2snd-soc-omap-objs := omap-pcm.o
3snd-soc-omap-mcbsp-objs := omap-mcbsp.o
4
5obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o
6obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o
7
8# OMAP Machine Support
9snd-soc-n810-objs := n810.o
10
11obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c
new file mode 100644
index 000000000000..83b1eb4e40f3
--- /dev/null
+++ b/sound/soc/omap/n810.c
@@ -0,0 +1,336 @@
1/*
2 * n810.c -- SoC audio for Nokia N810
3 *
4 * Copyright (C) 2008 Nokia Corporation
5 *
6 * Contact: Jarkko Nikula <jarkko.nikula@nokia.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#include <linux/clk.h>
25#include <linux/platform_device.h>
26#include <sound/core.h>
27#include <sound/pcm.h>
28#include <sound/soc.h>
29#include <sound/soc-dapm.h>
30
31#include <asm/mach-types.h>
32#include <asm/arch/hardware.h>
33#include <asm/arch/gpio.h>
34#include <asm/arch/mcbsp.h>
35
36#include "omap-mcbsp.h"
37#include "omap-pcm.h"
38#include "../codecs/tlv320aic3x.h"
39
40#define RX44_HEADSET_AMP_GPIO 10
41#define RX44_SPEAKER_AMP_GPIO 101
42
43static struct clk *sys_clkout2;
44static struct clk *sys_clkout2_src;
45static struct clk *func96m_clk;
46
47static int n810_spk_func;
48static int n810_jack_func;
49
50static void n810_ext_control(struct snd_soc_codec *codec)
51{
52 snd_soc_dapm_set_endpoint(codec, "Ext Spk", n810_spk_func);
53 snd_soc_dapm_set_endpoint(codec, "Headphone Jack", n810_jack_func);
54
55 snd_soc_dapm_sync_endpoints(codec);
56}
57
58static int n810_startup(struct snd_pcm_substream *substream)
59{
60 struct snd_soc_pcm_runtime *rtd = substream->private_data;
61 struct snd_soc_codec *codec = rtd->socdev->codec;
62
63 n810_ext_control(codec);
64 return clk_enable(sys_clkout2);
65}
66
67static void n810_shutdown(struct snd_pcm_substream *substream)
68{
69 clk_disable(sys_clkout2);
70}
71
72static int n810_hw_params(struct snd_pcm_substream *substream,
73 struct snd_pcm_hw_params *params)
74{
75 struct snd_soc_pcm_runtime *rtd = substream->private_data;
76 struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
77 struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
78 int err;
79
80 /* Set codec DAI configuration */
81 err = codec_dai->dai_ops.set_fmt(codec_dai,
82 SND_SOC_DAIFMT_I2S |
83 SND_SOC_DAIFMT_NB_NF |
84 SND_SOC_DAIFMT_CBM_CFM);
85 if (err < 0)
86 return err;
87
88 /* Set cpu DAI configuration */
89 err = cpu_dai->dai_ops.set_fmt(cpu_dai,
90 SND_SOC_DAIFMT_I2S |
91 SND_SOC_DAIFMT_NB_NF |
92 SND_SOC_DAIFMT_CBM_CFM);
93 if (err < 0)
94 return err;
95
96 /* Set the codec system clock for DAC and ADC */
97 err = codec_dai->dai_ops.set_sysclk(codec_dai, 0, 12000000,
98 SND_SOC_CLOCK_IN);
99
100 return err;
101}
102
103static struct snd_soc_ops n810_ops = {
104 .startup = n810_startup,
105 .hw_params = n810_hw_params,
106 .shutdown = n810_shutdown,
107};
108
109static int n810_get_spk(struct snd_kcontrol *kcontrol,
110 struct snd_ctl_elem_value *ucontrol)
111{
112 ucontrol->value.integer.value[0] = n810_spk_func;
113
114 return 0;
115}
116
117static int n810_set_spk(struct snd_kcontrol *kcontrol,
118 struct snd_ctl_elem_value *ucontrol)
119{
120 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
121
122 if (n810_spk_func == ucontrol->value.integer.value[0])
123 return 0;
124
125 n810_spk_func = ucontrol->value.integer.value[0];
126 n810_ext_control(codec);
127
128 return 1;
129}
130
131static int n810_get_jack(struct snd_kcontrol *kcontrol,
132 struct snd_ctl_elem_value *ucontrol)
133{
134 ucontrol->value.integer.value[0] = n810_jack_func;
135
136 return 0;
137}
138
139static int n810_set_jack(struct snd_kcontrol *kcontrol,
140 struct snd_ctl_elem_value *ucontrol)
141{
142 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
143
144 if (n810_jack_func == ucontrol->value.integer.value[0])
145 return 0;
146
147 n810_jack_func = ucontrol->value.integer.value[0];
148 n810_ext_control(codec);
149
150 return 1;
151}
152
153static int n810_spk_event(struct snd_soc_dapm_widget *w,
154 struct snd_kcontrol *k, int event)
155{
156 if (SND_SOC_DAPM_EVENT_ON(event))
157 omap_set_gpio_dataout(RX44_SPEAKER_AMP_GPIO, 1);
158 else
159 omap_set_gpio_dataout(RX44_SPEAKER_AMP_GPIO, 0);
160
161 return 0;
162}
163
164static int n810_jack_event(struct snd_soc_dapm_widget *w,
165 struct snd_kcontrol *k, int event)
166{
167 if (SND_SOC_DAPM_EVENT_ON(event))
168 omap_set_gpio_dataout(RX44_HEADSET_AMP_GPIO, 1);
169 else
170 omap_set_gpio_dataout(RX44_HEADSET_AMP_GPIO, 0);
171
172 return 0;
173}
174
175static const struct snd_soc_dapm_widget aic33_dapm_widgets[] = {
176 SND_SOC_DAPM_SPK("Ext Spk", n810_spk_event),
177 SND_SOC_DAPM_HP("Headphone Jack", n810_jack_event),
178};
179
180static const char *audio_map[][3] = {
181 {"Headphone Jack", NULL, "HPLOUT"},
182 {"Headphone Jack", NULL, "HPROUT"},
183
184 {"Ext Spk", NULL, "LLOUT"},
185 {"Ext Spk", NULL, "RLOUT"},
186};
187
188static const char *spk_function[] = {"Off", "On"};
189static const char *jack_function[] = {"Off", "Headphone"};
190static const struct soc_enum n810_enum[] = {
191 SOC_ENUM_SINGLE_EXT(2, spk_function),
192 SOC_ENUM_SINGLE_EXT(3, jack_function),
193};
194
195static const struct snd_kcontrol_new aic33_n810_controls[] = {
196 SOC_ENUM_EXT("Speaker Function", n810_enum[0],
197 n810_get_spk, n810_set_spk),
198 SOC_ENUM_EXT("Jack Function", n810_enum[1],
199 n810_get_jack, n810_set_jack),
200};
201
202static int n810_aic33_init(struct snd_soc_codec *codec)
203{
204 int i, err;
205
206 /* Not connected */
207 snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0);
208 snd_soc_dapm_set_endpoint(codec, "HPLCOM", 0);
209 snd_soc_dapm_set_endpoint(codec, "HPRCOM", 0);
210
211 /* Add N810 specific controls */
212 for (i = 0; i < ARRAY_SIZE(aic33_n810_controls); i++) {
213 err = snd_ctl_add(codec->card,
214 snd_soc_cnew(&aic33_n810_controls[i], codec, NULL));
215 if (err < 0)
216 return err;
217 }
218
219 /* Add N810 specific widgets */
220 for (i = 0; i < ARRAY_SIZE(aic33_dapm_widgets); i++)
221 snd_soc_dapm_new_control(codec, &aic33_dapm_widgets[i]);
222
223 /* Set up N810 specific audio path audio_map */
224 for (i = 0; i < ARRAY_SIZE(audio_map); i++)
225 snd_soc_dapm_connect_input(codec, audio_map[i][0],
226 audio_map[i][1], audio_map[i][2]);
227
228 snd_soc_dapm_sync_endpoints(codec);
229
230 return 0;
231}
232
233/* Digital audio interface glue - connects codec <--> CPU */
234static struct snd_soc_dai_link n810_dai = {
235 .name = "TLV320AIC33",
236 .stream_name = "AIC33",
237 .cpu_dai = &omap_mcbsp_dai[0],
238 .codec_dai = &aic3x_dai,
239 .init = n810_aic33_init,
240 .ops = &n810_ops,
241};
242
243/* Audio machine driver */
244static struct snd_soc_machine snd_soc_machine_n810 = {
245 .name = "N810",
246 .dai_link = &n810_dai,
247 .num_links = 1,
248};
249
250/* Audio private data */
251static struct aic3x_setup_data n810_aic33_setup = {
252 .i2c_address = 0x18,
253};
254
255/* Audio subsystem */
256static struct snd_soc_device n810_snd_devdata = {
257 .machine = &snd_soc_machine_n810,
258 .platform = &omap_soc_platform,
259 .codec_dev = &soc_codec_dev_aic3x,
260 .codec_data = &n810_aic33_setup,
261};
262
263static struct platform_device *n810_snd_device;
264
265static int __init n810_soc_init(void)
266{
267 int err;
268 struct device *dev;
269
270 if (!machine_is_nokia_n810())
271 return -ENODEV;
272
273 n810_snd_device = platform_device_alloc("soc-audio", -1);
274 if (!n810_snd_device)
275 return -ENOMEM;
276
277 platform_set_drvdata(n810_snd_device, &n810_snd_devdata);
278 n810_snd_devdata.dev = &n810_snd_device->dev;
279 *(unsigned int *)n810_dai.cpu_dai->private_data = 1; /* McBSP2 */
280 err = platform_device_add(n810_snd_device);
281 if (err)
282 goto err1;
283
284 dev = &n810_snd_device->dev;
285
286 sys_clkout2_src = clk_get(dev, "sys_clkout2_src");
287 if (IS_ERR(sys_clkout2_src)) {
288 dev_err(dev, "Could not get sys_clkout2_src clock\n");
289 return -ENODEV;
290 }
291 sys_clkout2 = clk_get(dev, "sys_clkout2");
292 if (IS_ERR(sys_clkout2)) {
293 dev_err(dev, "Could not get sys_clkout2\n");
294 goto err1;
295 }
296 /*
297 * Configure 12 MHz output on SYS_CLKOUT2. Therefore we must use
298 * 96 MHz as its parent in order to get 12 MHz
299 */
300 func96m_clk = clk_get(dev, "func_96m_ck");
301 if (IS_ERR(func96m_clk)) {
302 dev_err(dev, "Could not get func 96M clock\n");
303 goto err2;
304 }
305 clk_set_parent(sys_clkout2_src, func96m_clk);
306 clk_set_rate(sys_clkout2, 12000000);
307
308 if (omap_request_gpio(RX44_HEADSET_AMP_GPIO) < 0)
309 BUG();
310 if (omap_request_gpio(RX44_SPEAKER_AMP_GPIO) < 0)
311 BUG();
312 omap_set_gpio_direction(RX44_HEADSET_AMP_GPIO, 0);
313 omap_set_gpio_direction(RX44_SPEAKER_AMP_GPIO, 0);
314
315 return 0;
316err2:
317 clk_put(sys_clkout2);
318 platform_device_del(n810_snd_device);
319err1:
320 platform_device_put(n810_snd_device);
321
322 return err;
323
324}
325
326static void __exit n810_soc_exit(void)
327{
328 platform_device_unregister(n810_snd_device);
329}
330
331module_init(n810_soc_init);
332module_exit(n810_soc_exit);
333
334MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@nokia.com>");
335MODULE_DESCRIPTION("ALSA SoC Nokia N810");
336MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
new file mode 100644
index 000000000000..40d87e6d0de8
--- /dev/null
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -0,0 +1,414 @@
1/*
2 * omap-mcbsp.c -- OMAP ALSA SoC DAI driver using McBSP port
3 *
4 * Copyright (C) 2008 Nokia Corporation
5 *
6 * Contact: Jarkko Nikula <jarkko.nikula@nokia.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#include <linux/init.h>
25#include <linux/module.h>
26#include <linux/device.h>
27#include <sound/core.h>
28#include <sound/pcm.h>
29#include <sound/pcm_params.h>
30#include <sound/initval.h>
31#include <sound/soc.h>
32
33#include <asm/arch/control.h>
34#include <asm/arch/dma.h>
35#include <asm/arch/mcbsp.h>
36#include "omap-mcbsp.h"
37#include "omap-pcm.h"
38
39#define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_44100 | \
40 SNDRV_PCM_RATE_48000 | \
41 SNDRV_PCM_RATE_KNOT)
42
43struct omap_mcbsp_data {
44 unsigned int bus_id;
45 struct omap_mcbsp_reg_cfg regs;
46 /*
47 * Flags indicating is the bus already activated and configured by
48 * another substream
49 */
50 int active;
51 int configured;
52};
53
54#define to_mcbsp(priv) container_of((priv), struct omap_mcbsp_data, bus_id)
55
56static struct omap_mcbsp_data mcbsp_data[NUM_LINKS];
57
58/*
59 * Stream DMA parameters. DMA request line and port address are set runtime
60 * since they are different between OMAP1 and later OMAPs
61 */
62static struct omap_pcm_dma_data omap_mcbsp_dai_dma_params[NUM_LINKS][2] = {
63{
64 { .name = "I2S PCM Stereo out", },
65 { .name = "I2S PCM Stereo in", },
66},
67};
68
69#if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX)
70static const int omap1_dma_reqs[][2] = {
71 { OMAP_DMA_MCBSP1_TX, OMAP_DMA_MCBSP1_RX },
72 { OMAP_DMA_MCBSP2_TX, OMAP_DMA_MCBSP2_RX },
73 { OMAP_DMA_MCBSP3_TX, OMAP_DMA_MCBSP3_RX },
74};
75static const unsigned long omap1_mcbsp_port[][2] = {
76 { OMAP1510_MCBSP1_BASE + OMAP_MCBSP_REG_DXR1,
77 OMAP1510_MCBSP1_BASE + OMAP_MCBSP_REG_DRR1 },
78 { OMAP1510_MCBSP2_BASE + OMAP_MCBSP_REG_DXR1,
79 OMAP1510_MCBSP2_BASE + OMAP_MCBSP_REG_DRR1 },
80 { OMAP1510_MCBSP3_BASE + OMAP_MCBSP_REG_DXR1,
81 OMAP1510_MCBSP3_BASE + OMAP_MCBSP_REG_DRR1 },
82};
83#else
84static const int omap1_dma_reqs[][2] = {};
85static const unsigned long omap1_mcbsp_port[][2] = {};
86#endif
87#if defined(CONFIG_ARCH_OMAP2420)
88static const int omap2420_dma_reqs[][2] = {
89 { OMAP24XX_DMA_MCBSP1_TX, OMAP24XX_DMA_MCBSP1_RX },
90 { OMAP24XX_DMA_MCBSP2_TX, OMAP24XX_DMA_MCBSP2_RX },
91};
92static const unsigned long omap2420_mcbsp_port[][2] = {
93 { OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR1,
94 OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR1 },
95 { OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR1,
96 OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR1 },
97};
98#else
99static const int omap2420_dma_reqs[][2] = {};
100static const unsigned long omap2420_mcbsp_port[][2] = {};
101#endif
102
103static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream)
104{
105 struct snd_soc_pcm_runtime *rtd = substream->private_data;
106 struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
107 struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
108 int err = 0;
109
110 if (!cpu_dai->active)
111 err = omap_mcbsp_request(mcbsp_data->bus_id);
112
113 return err;
114}
115
116static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream)
117{
118 struct snd_soc_pcm_runtime *rtd = substream->private_data;
119 struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
120 struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
121
122 if (!cpu_dai->active) {
123 omap_mcbsp_free(mcbsp_data->bus_id);
124 mcbsp_data->configured = 0;
125 }
126}
127
128static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd)
129{
130 struct snd_soc_pcm_runtime *rtd = substream->private_data;
131 struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
132 struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
133 int err = 0;
134
135 switch (cmd) {
136 case SNDRV_PCM_TRIGGER_START:
137 case SNDRV_PCM_TRIGGER_RESUME:
138 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
139 if (!mcbsp_data->active++)
140 omap_mcbsp_start(mcbsp_data->bus_id);
141 break;
142
143 case SNDRV_PCM_TRIGGER_STOP:
144 case SNDRV_PCM_TRIGGER_SUSPEND:
145 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
146 if (!--mcbsp_data->active)
147 omap_mcbsp_stop(mcbsp_data->bus_id);
148 break;
149 default:
150 err = -EINVAL;
151 }
152
153 return err;
154}
155
156static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
157 struct snd_pcm_hw_params *params)
158{
159 struct snd_soc_pcm_runtime *rtd = substream->private_data;
160 struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
161 struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
162 struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
163 int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id;
164 unsigned long port;
165
166 if (cpu_class_is_omap1()) {
167 dma = omap1_dma_reqs[bus_id][substream->stream];
168 port = omap1_mcbsp_port[bus_id][substream->stream];
169 } else if (cpu_is_omap2420()) {
170 dma = omap2420_dma_reqs[bus_id][substream->stream];
171 port = omap2420_mcbsp_port[bus_id][substream->stream];
172 } else {
173 /*
174 * TODO: Add support for 2430 and 3430
175 */
176 return -ENODEV;
177 }
178 omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma;
179 omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port;
180 cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream];
181
182 if (mcbsp_data->configured) {
183 /* McBSP already configured by another stream */
184 return 0;
185 }
186
187 switch (params_channels(params)) {
188 case 2:
189 /* Set 1 word per (McBPSP) frame and use dual-phase frames */
190 regs->rcr2 |= RFRLEN2(1 - 1) | RPHASE;
191 regs->rcr1 |= RFRLEN1(1 - 1);
192 regs->xcr2 |= XFRLEN2(1 - 1) | XPHASE;
193 regs->xcr1 |= XFRLEN1(1 - 1);
194 break;
195 default:
196 /* Unsupported number of channels */
197 return -EINVAL;
198 }
199
200 switch (params_format(params)) {
201 case SNDRV_PCM_FORMAT_S16_LE:
202 /* Set word lengths */
203 regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16);
204 regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16);
205 regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16);
206 regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_16);
207 /* Set FS period and length in terms of bit clock periods */
208 regs->srgr2 |= FPER(16 * 2 - 1);
209 regs->srgr1 |= FWID(16 - 1);
210 break;
211 default:
212 /* Unsupported PCM format */
213 return -EINVAL;
214 }
215
216 omap_mcbsp_config(bus_id, &mcbsp_data->regs);
217 mcbsp_data->configured = 1;
218
219 return 0;
220}
221
222/*
223 * This must be called before _set_clkdiv and _set_sysclk since McBSP register
224 * cache is initialized here
225 */
226static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
227 unsigned int fmt)
228{
229 struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
230 struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
231
232 if (mcbsp_data->configured)
233 return 0;
234
235 memset(regs, 0, sizeof(*regs));
236 /* Generic McBSP register settings */
237 regs->spcr2 |= XINTM(3) | FREE;
238 regs->spcr1 |= RINTM(3);
239 regs->rcr2 |= RFIG;
240 regs->xcr2 |= XFIG;
241
242 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
243 case SND_SOC_DAIFMT_I2S:
244 /* 1-bit data delay */
245 regs->rcr2 |= RDATDLY(1);
246 regs->xcr2 |= XDATDLY(1);
247 break;
248 default:
249 /* Unsupported data format */
250 return -EINVAL;
251 }
252
253 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
254 case SND_SOC_DAIFMT_CBS_CFS:
255 /* McBSP master. Set FS and bit clocks as outputs */
256 regs->pcr0 |= FSXM | FSRM |
257 CLKXM | CLKRM;
258 /* Sample rate generator drives the FS */
259 regs->srgr2 |= FSGM;
260 break;
261 case SND_SOC_DAIFMT_CBM_CFM:
262 /* McBSP slave */
263 break;
264 default:
265 /* Unsupported master/slave configuration */
266 return -EINVAL;
267 }
268
269 /* Set bit clock (CLKX/CLKR) and FS polarities */
270 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
271 case SND_SOC_DAIFMT_NB_NF:
272 /*
273 * Normal BCLK + FS.
274 * FS active low. TX data driven on falling edge of bit clock
275 * and RX data sampled on rising edge of bit clock.
276 */
277 regs->pcr0 |= FSXP | FSRP |
278 CLKXP | CLKRP;
279 break;
280 case SND_SOC_DAIFMT_NB_IF:
281 regs->pcr0 |= CLKXP | CLKRP;
282 break;
283 case SND_SOC_DAIFMT_IB_NF:
284 regs->pcr0 |= FSXP | FSRP;
285 break;
286 case SND_SOC_DAIFMT_IB_IF:
287 break;
288 default:
289 return -EINVAL;
290 }
291
292 return 0;
293}
294
295static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
296 int div_id, int div)
297{
298 struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
299 struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
300
301 if (div_id != OMAP_MCBSP_CLKGDV)
302 return -ENODEV;
303
304 regs->srgr1 |= CLKGDV(div - 1);
305
306 return 0;
307}
308
309static int omap_mcbsp_dai_set_clks_src(struct omap_mcbsp_data *mcbsp_data,
310 int clk_id)
311{
312 int sel_bit;
313 u16 reg;
314
315 if (cpu_class_is_omap1()) {
316 /* OMAP1's can use only external source clock */
317 if (unlikely(clk_id == OMAP_MCBSP_SYSCLK_CLKS_FCLK))
318 return -EINVAL;
319 else
320 return 0;
321 }
322
323 switch (mcbsp_data->bus_id) {
324 case 0:
325 reg = OMAP2_CONTROL_DEVCONF0;
326 sel_bit = 2;
327 break;
328 case 1:
329 reg = OMAP2_CONTROL_DEVCONF0;
330 sel_bit = 6;
331 break;
332 /* TODO: Support for ports 3 - 5 in OMAP2430 and OMAP34xx */
333 default:
334 return -EINVAL;
335 }
336
337 if (cpu_class_is_omap2()) {
338 if (clk_id == OMAP_MCBSP_SYSCLK_CLKS_FCLK) {
339 omap_ctrl_writel(omap_ctrl_readl(reg) &
340 ~(1 << sel_bit), reg);
341 } else {
342 omap_ctrl_writel(omap_ctrl_readl(reg) |
343 (1 << sel_bit), reg);
344 }
345 }
346
347 return 0;
348}
349
350static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
351 int clk_id, unsigned int freq,
352 int dir)
353{
354 struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
355 struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
356 int err = 0;
357
358 switch (clk_id) {
359 case OMAP_MCBSP_SYSCLK_CLK:
360 regs->srgr2 |= CLKSM;
361 break;
362 case OMAP_MCBSP_SYSCLK_CLKS_FCLK:
363 case OMAP_MCBSP_SYSCLK_CLKS_EXT:
364 err = omap_mcbsp_dai_set_clks_src(mcbsp_data, clk_id);
365 break;
366
367 case OMAP_MCBSP_SYSCLK_CLKX_EXT:
368 regs->srgr2 |= CLKSM;
369 case OMAP_MCBSP_SYSCLK_CLKR_EXT:
370 regs->pcr0 |= SCLKME;
371 break;
372 default:
373 err = -ENODEV;
374 }
375
376 return err;
377}
378
379struct snd_soc_cpu_dai omap_mcbsp_dai[NUM_LINKS] = {
380{
381 .name = "omap-mcbsp-dai",
382 .id = 0,
383 .type = SND_SOC_DAI_I2S,
384 .playback = {
385 .channels_min = 2,
386 .channels_max = 2,
387 .rates = OMAP_MCBSP_RATES,
388 .formats = SNDRV_PCM_FMTBIT_S16_LE,
389 },
390 .capture = {
391 .channels_min = 2,
392 .channels_max = 2,
393 .rates = OMAP_MCBSP_RATES,
394 .formats = SNDRV_PCM_FMTBIT_S16_LE,
395 },
396 .ops = {
397 .startup = omap_mcbsp_dai_startup,
398 .shutdown = omap_mcbsp_dai_shutdown,
399 .trigger = omap_mcbsp_dai_trigger,
400 .hw_params = omap_mcbsp_dai_hw_params,
401 },
402 .dai_ops = {
403 .set_fmt = omap_mcbsp_dai_set_dai_fmt,
404 .set_clkdiv = omap_mcbsp_dai_set_clkdiv,
405 .set_sysclk = omap_mcbsp_dai_set_dai_sysclk,
406 },
407 .private_data = &mcbsp_data[0].bus_id,
408},
409};
410EXPORT_SYMBOL_GPL(omap_mcbsp_dai);
411
412MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@nokia.com>");
413MODULE_DESCRIPTION("OMAP I2S SoC Interface");
414MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h
new file mode 100644
index 000000000000..9965fd4b0427
--- /dev/null
+++ b/sound/soc/omap/omap-mcbsp.h
@@ -0,0 +1,49 @@
1/*
2 * omap-mcbsp.h
3 *
4 * Copyright (C) 2008 Nokia Corporation
5 *
6 * Contact: Jarkko Nikula <jarkko.nikula@nokia.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_I2S_H__
25#define __OMAP_I2S_H__
26
27/* Source clocks for McBSP sample rate generator */
28enum omap_mcbsp_clksrg_clk {
29 OMAP_MCBSP_SYSCLK_CLKS_FCLK, /* Internal FCLK */
30 OMAP_MCBSP_SYSCLK_CLKS_EXT, /* External CLKS pin */
31 OMAP_MCBSP_SYSCLK_CLK, /* Internal ICLK */
32 OMAP_MCBSP_SYSCLK_CLKX_EXT, /* External CLKX pin */
33 OMAP_MCBSP_SYSCLK_CLKR_EXT, /* External CLKR pin */
34};
35
36/* McBSP dividers */
37enum omap_mcbsp_div {
38 OMAP_MCBSP_CLKGDV, /* Sample rate generator divider */
39};
40
41/*
42 * REVISIT: Preparation for the ASoC v2. Let the number of available links to
43 * be same than number of McBSP ports found in OMAP(s) we are compiling for.
44 */
45#define NUM_LINKS 1
46
47extern struct snd_soc_cpu_dai omap_mcbsp_dai[NUM_LINKS];
48
49#endif
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
new file mode 100644
index 000000000000..62370202c649
--- /dev/null
+++ b/sound/soc/omap/omap-pcm.c
@@ -0,0 +1,357 @@
1/*
2 * omap-pcm.c -- ALSA PCM interface for the OMAP SoC
3 *
4 * Copyright (C) 2008 Nokia Corporation
5 *
6 * Contact: Jarkko Nikula <jarkko.nikula@nokia.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#include <linux/dma-mapping.h>
25#include <sound/core.h>
26#include <sound/pcm.h>
27#include <sound/pcm_params.h>
28#include <sound/soc.h>
29
30#include <asm/arch/dma.h>
31#include "omap-pcm.h"
32
33static const struct snd_pcm_hardware omap_pcm_hardware = {
34 .info = SNDRV_PCM_INFO_MMAP |
35 SNDRV_PCM_INFO_MMAP_VALID |
36 SNDRV_PCM_INFO_INTERLEAVED |
37 SNDRV_PCM_INFO_PAUSE |
38 SNDRV_PCM_INFO_RESUME,
39 .formats = SNDRV_PCM_FMTBIT_S16_LE,
40 .period_bytes_min = 32,
41 .period_bytes_max = 64 * 1024,
42 .periods_min = 2,
43 .periods_max = 255,
44 .buffer_bytes_max = 128 * 1024,
45};
46
47struct omap_runtime_data {
48 spinlock_t lock;
49 struct omap_pcm_dma_data *dma_data;
50 int dma_ch;
51 int period_index;
52};
53
54static void omap_pcm_dma_irq(int ch, u16 stat, void *data)
55{
56 struct snd_pcm_substream *substream = data;
57 struct snd_pcm_runtime *runtime = substream->runtime;
58 struct omap_runtime_data *prtd = runtime->private_data;
59 unsigned long flags;
60
61 if (cpu_is_omap1510()) {
62 /*
63 * OMAP1510 doesn't support DMA chaining so have to restart
64 * the transfer after all periods are transferred
65 */
66 spin_lock_irqsave(&prtd->lock, flags);
67 if (prtd->period_index >= 0) {
68 if (++prtd->period_index == runtime->periods) {
69 prtd->period_index = 0;
70 omap_start_dma(prtd->dma_ch);
71 }
72 }
73 spin_unlock_irqrestore(&prtd->lock, flags);
74 }
75
76 snd_pcm_period_elapsed(substream);
77}
78
79/* this may get called several times by oss emulation */
80static int omap_pcm_hw_params(struct snd_pcm_substream *substream,
81 struct snd_pcm_hw_params *params)
82{
83 struct snd_pcm_runtime *runtime = substream->runtime;
84 struct snd_soc_pcm_runtime *rtd = substream->private_data;
85 struct omap_runtime_data *prtd = runtime->private_data;
86 struct omap_pcm_dma_data *dma_data = rtd->dai->cpu_dai->dma_data;
87 int err = 0;
88
89 if (!dma_data)
90 return -ENODEV;
91
92 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
93 runtime->dma_bytes = params_buffer_bytes(params);
94
95 if (prtd->dma_data)
96 return 0;
97 prtd->dma_data = dma_data;
98 err = omap_request_dma(dma_data->dma_req, dma_data->name,
99 omap_pcm_dma_irq, substream, &prtd->dma_ch);
100 if (!cpu_is_omap1510()) {
101 /*
102 * Link channel with itself so DMA doesn't need any
103 * reprogramming while looping the buffer
104 */
105 omap_dma_link_lch(prtd->dma_ch, prtd->dma_ch);
106 }
107
108 return err;
109}
110
111static int omap_pcm_hw_free(struct snd_pcm_substream *substream)
112{
113 struct snd_pcm_runtime *runtime = substream->runtime;
114 struct omap_runtime_data *prtd = runtime->private_data;
115
116 if (prtd->dma_data == NULL)
117 return 0;
118
119 if (!cpu_is_omap1510())
120 omap_dma_unlink_lch(prtd->dma_ch, prtd->dma_ch);
121 omap_free_dma(prtd->dma_ch);
122 prtd->dma_data = NULL;
123
124 snd_pcm_set_runtime_buffer(substream, NULL);
125
126 return 0;
127}
128
129static int omap_pcm_prepare(struct snd_pcm_substream *substream)
130{
131 struct snd_pcm_runtime *runtime = substream->runtime;
132 struct omap_runtime_data *prtd = runtime->private_data;
133 struct omap_pcm_dma_data *dma_data = prtd->dma_data;
134 struct omap_dma_channel_params dma_params;
135
136 memset(&dma_params, 0, sizeof(dma_params));
137 /*
138 * Note: Regardless of interface data formats supported by OMAP McBSP
139 * or EAC blocks, internal representation is always fixed 16-bit/sample
140 */
141 dma_params.data_type = OMAP_DMA_DATA_TYPE_S16;
142 dma_params.trigger = dma_data->dma_req;
143 dma_params.sync_mode = OMAP_DMA_SYNC_ELEMENT;
144 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
145 dma_params.src_amode = OMAP_DMA_AMODE_POST_INC;
146 dma_params.dst_amode = OMAP_DMA_AMODE_CONSTANT;
147 dma_params.src_or_dst_synch = OMAP_DMA_DST_SYNC;
148 dma_params.src_start = runtime->dma_addr;
149 dma_params.dst_start = dma_data->port_addr;
150 } else {
151 dma_params.src_amode = OMAP_DMA_AMODE_CONSTANT;
152 dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC;
153 dma_params.src_or_dst_synch = OMAP_DMA_SRC_SYNC;
154 dma_params.src_start = dma_data->port_addr;
155 dma_params.dst_start = runtime->dma_addr;
156 }
157 /*
158 * Set DMA transfer frame size equal to ALSA period size and frame
159 * count as no. of ALSA periods. Then with DMA frame interrupt enabled,
160 * we can transfer the whole ALSA buffer with single DMA transfer but
161 * still can get an interrupt at each period bounary
162 */
163 dma_params.elem_count = snd_pcm_lib_period_bytes(substream) / 2;
164 dma_params.frame_count = runtime->periods;
165 omap_set_dma_params(prtd->dma_ch, &dma_params);
166
167 omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ);
168
169 return 0;
170}
171
172static int omap_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
173{
174 struct snd_pcm_runtime *runtime = substream->runtime;
175 struct omap_runtime_data *prtd = runtime->private_data;
176 int ret = 0;
177
178 spin_lock_irq(&prtd->lock);
179 switch (cmd) {
180 case SNDRV_PCM_TRIGGER_START:
181 case SNDRV_PCM_TRIGGER_RESUME:
182 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
183 prtd->period_index = 0;
184 omap_start_dma(prtd->dma_ch);
185 break;
186
187 case SNDRV_PCM_TRIGGER_STOP:
188 case SNDRV_PCM_TRIGGER_SUSPEND:
189 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
190 prtd->period_index = -1;
191 omap_stop_dma(prtd->dma_ch);
192 break;
193 default:
194 ret = -EINVAL;
195 }
196 spin_unlock_irq(&prtd->lock);
197
198 return ret;
199}
200
201static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream)
202{
203 struct snd_pcm_runtime *runtime = substream->runtime;
204 struct omap_runtime_data *prtd = runtime->private_data;
205 dma_addr_t ptr;
206 snd_pcm_uframes_t offset;
207
208 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
209 ptr = omap_get_dma_src_pos(prtd->dma_ch);
210 else
211 ptr = omap_get_dma_dst_pos(prtd->dma_ch);
212
213 offset = bytes_to_frames(runtime, ptr - runtime->dma_addr);
214 if (offset >= runtime->buffer_size)
215 offset = 0;
216
217 return offset;
218}
219
220static int omap_pcm_open(struct snd_pcm_substream *substream)
221{
222 struct snd_pcm_runtime *runtime = substream->runtime;
223 struct omap_runtime_data *prtd;
224 int ret;
225
226 snd_soc_set_runtime_hwparams(substream, &omap_pcm_hardware);
227
228 /* Ensure that buffer size is a multiple of period size */
229 ret = snd_pcm_hw_constraint_integer(runtime,
230 SNDRV_PCM_HW_PARAM_PERIODS);
231 if (ret < 0)
232 goto out;
233
234 prtd = kzalloc(sizeof(prtd), GFP_KERNEL);
235 if (prtd == NULL) {
236 ret = -ENOMEM;
237 goto out;
238 }
239 spin_lock_init(&prtd->lock);
240 runtime->private_data = prtd;
241
242out:
243 return ret;
244}
245
246static int omap_pcm_close(struct snd_pcm_substream *substream)
247{
248 struct snd_pcm_runtime *runtime = substream->runtime;
249
250 kfree(runtime->private_data);
251 return 0;
252}
253
254static int omap_pcm_mmap(struct snd_pcm_substream *substream,
255 struct vm_area_struct *vma)
256{
257 struct snd_pcm_runtime *runtime = substream->runtime;
258
259 return dma_mmap_writecombine(substream->pcm->card->dev, vma,
260 runtime->dma_area,
261 runtime->dma_addr,
262 runtime->dma_bytes);
263}
264
265struct snd_pcm_ops omap_pcm_ops = {
266 .open = omap_pcm_open,
267 .close = omap_pcm_close,
268 .ioctl = snd_pcm_lib_ioctl,
269 .hw_params = omap_pcm_hw_params,
270 .hw_free = omap_pcm_hw_free,
271 .prepare = omap_pcm_prepare,
272 .trigger = omap_pcm_trigger,
273 .pointer = omap_pcm_pointer,
274 .mmap = omap_pcm_mmap,
275};
276
277static u64 omap_pcm_dmamask = DMA_BIT_MASK(32);
278
279static int omap_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
280 int stream)
281{
282 struct snd_pcm_substream *substream = pcm->streams[stream].substream;
283 struct snd_dma_buffer *buf = &substream->dma_buffer;
284 size_t size = omap_pcm_hardware.buffer_bytes_max;
285
286 buf->dev.type = SNDRV_DMA_TYPE_DEV;
287 buf->dev.dev = pcm->card->dev;
288 buf->private_data = NULL;
289 buf->area = dma_alloc_writecombine(pcm->card->dev, size,
290 &buf->addr, GFP_KERNEL);
291 if (!buf->area)
292 return -ENOMEM;
293
294 buf->bytes = size;
295 return 0;
296}
297
298static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm)
299{
300 struct snd_pcm_substream *substream;
301 struct snd_dma_buffer *buf;
302 int stream;
303
304 for (stream = 0; stream < 2; stream++) {
305 substream = pcm->streams[stream].substream;
306 if (!substream)
307 continue;
308
309 buf = &substream->dma_buffer;
310 if (!buf->area)
311 continue;
312
313 dma_free_writecombine(pcm->card->dev, buf->bytes,
314 buf->area, buf->addr);
315 buf->area = NULL;
316 }
317}
318
319int omap_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
320 struct snd_pcm *pcm)
321{
322 int ret = 0;
323
324 if (!card->dev->dma_mask)
325 card->dev->dma_mask = &omap_pcm_dmamask;
326 if (!card->dev->coherent_dma_mask)
327 card->dev->coherent_dma_mask = DMA_32BIT_MASK;
328
329 if (dai->playback.channels_min) {
330 ret = omap_pcm_preallocate_dma_buffer(pcm,
331 SNDRV_PCM_STREAM_PLAYBACK);
332 if (ret)
333 goto out;
334 }
335
336 if (dai->capture.channels_min) {
337 ret = omap_pcm_preallocate_dma_buffer(pcm,
338 SNDRV_PCM_STREAM_CAPTURE);
339 if (ret)
340 goto out;
341 }
342
343out:
344 return ret;
345}
346
347struct snd_soc_platform omap_soc_platform = {
348 .name = "omap-pcm-audio",
349 .pcm_ops = &omap_pcm_ops,
350 .pcm_new = omap_pcm_new,
351 .pcm_free = omap_pcm_free_dma_buffers,
352};
353EXPORT_SYMBOL_GPL(omap_soc_platform);
354
355MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@nokia.com>");
356MODULE_DESCRIPTION("OMAP PCM DMA module");
357MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/omap-pcm.h b/sound/soc/omap/omap-pcm.h
new file mode 100644
index 000000000000..e4369bdfd77d
--- /dev/null
+++ b/sound/soc/omap/omap-pcm.h
@@ -0,0 +1,35 @@
1/*
2 * omap-pcm.h
3 *
4 * Copyright (C) 2008 Nokia Corporation
5 *
6 * Contact: Jarkko Nikula <jarkko.nikula@nokia.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_PCM_H__
25#define __OMAP_PCM_H__
26
27struct omap_pcm_dma_data {
28 char *name; /* stream identifier */
29 int dma_req; /* DMA request line */
30 unsigned long port_addr; /* transmit/receive register */
31};
32
33extern struct snd_soc_platform omap_soc_platform;
34
35#endif