aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2008-04-29 12:38:52 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-04-29 12:38:52 -0400
commit25a025863e024f6b86b48137b10b4960c50351b0 (patch)
tree72d2521585f61d904769d28cf1d7687b949a61a6 /sound
parent1f43c5393033de90bac4410352b1d2a69dcbe7ef (diff)
parent7e48bf653c37eb32c2ba4c13f15aa154aa807e61 (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6: [ALSA] soc - wm9712 - checkpatch fixes [ALSA] pcsp - Fix more dependency [ALSA] hda - Add support of Medion RIM 2150 [ALSA] ASoC: Add drivers for the Texas Instruments OMAP processors [ALSA] ice1724 - Enable watermarks [ALSA] Add MPU401_INFO_NO_ACK bitflag
Diffstat (limited to 'sound')
-rw-r--r--sound/drivers/Kconfig2
-rw-r--r--sound/drivers/mpu401/mpu401_uart.c2
-rw-r--r--sound/pci/hda/patch_realtek.c86
-rw-r--r--sound/pci/ice1712/ice1724.c3
-rw-r--r--sound/soc/Kconfig1
-rw-r--r--sound/soc/Makefile2
-rw-r--r--sound/soc/codecs/wm9712.c62
-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
14 files changed, 1342 insertions, 37 deletions
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index fe85af1c5693..a78a8d045175 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -8,6 +8,8 @@ config SND_PCSP
8 tristate "Internal PC speaker support" 8 tristate "Internal PC speaker support"
9 depends on X86_PC && HIGH_RES_TIMERS 9 depends on X86_PC && HIGH_RES_TIMERS
10 depends on INPUT 10 depends on INPUT
11 depends on SND
12 select SND_PCM
11 help 13 help
12 If you don't have a sound card in your computer, you can include a 14 If you don't have a sound card in your computer, you can include a
13 driver for the PC speaker which allows it to act like a primitive 15 driver for the PC speaker which allows it to act like a primitive
diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c
index 18cca2457d44..2af09996a3d0 100644
--- a/sound/drivers/mpu401/mpu401_uart.c
+++ b/sound/drivers/mpu401/mpu401_uart.c
@@ -243,7 +243,7 @@ static int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd,
243#endif 243#endif
244 } 244 }
245 mpu->write(mpu, cmd, MPU401C(mpu)); 245 mpu->write(mpu, cmd, MPU401C(mpu));
246 if (ack) { 246 if (ack && !(mpu->info_flags & MPU401_INFO_NO_ACK)) {
247 ok = 0; 247 ok = 0;
248 timeout = 10000; 248 timeout = 10000;
249 while (!ok && timeout-- > 0) { 249 while (!ok && timeout-- > 0) {
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index cdda64b02f46..d9783a4263e0 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -60,6 +60,7 @@ enum {
60 ALC880_TCL_S700, 60 ALC880_TCL_S700,
61 ALC880_LG, 61 ALC880_LG,
62 ALC880_LG_LW, 62 ALC880_LG_LW,
63 ALC880_MEDION_RIM,
63#ifdef CONFIG_SND_DEBUG 64#ifdef CONFIG_SND_DEBUG
64 ALC880_TEST, 65 ALC880_TEST,
65#endif 66#endif
@@ -2275,6 +2276,75 @@ static void alc880_lg_lw_unsol_event(struct hda_codec *codec, unsigned int res)
2275 alc880_lg_lw_automute(codec); 2276 alc880_lg_lw_automute(codec);
2276} 2277}
2277 2278
2279static struct snd_kcontrol_new alc880_medion_rim_mixer[] = {
2280 HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
2281 HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT),
2282 HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
2283 HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
2284 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
2285 HDA_CODEC_MUTE("Internal Playback Switch", 0x0b, 0x1, HDA_INPUT),
2286 { } /* end */
2287};
2288
2289static struct hda_input_mux alc880_medion_rim_capture_source = {
2290 .num_items = 2,
2291 .items = {
2292 { "Mic", 0x0 },
2293 { "Internal Mic", 0x1 },
2294 },
2295};
2296
2297static struct hda_verb alc880_medion_rim_init_verbs[] = {
2298 {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
2299
2300 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2301 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2302
2303 /* Mic1 (rear panel) pin widget for input and vref at 80% */
2304 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2305 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2306 /* Mic2 (as headphone out) for HP output */
2307 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2308 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2309 /* Internal Speaker */
2310 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2311 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2312
2313 {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
2314 {0x20, AC_VERB_SET_PROC_COEF, 0x3060},
2315
2316 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
2317 { }
2318};
2319
2320/* toggle speaker-output according to the hp-jack state */
2321static void alc880_medion_rim_automute(struct hda_codec *codec)
2322{
2323 unsigned int present;
2324 unsigned char bits;
2325
2326 present = snd_hda_codec_read(codec, 0x14, 0,
2327 AC_VERB_GET_PIN_SENSE, 0)
2328 & AC_PINSENSE_PRESENCE;
2329 bits = present ? HDA_AMP_MUTE : 0;
2330 snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
2331 HDA_AMP_MUTE, bits);
2332 if (present)
2333 snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0);
2334 else
2335 snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 2);
2336}
2337
2338static void alc880_medion_rim_unsol_event(struct hda_codec *codec,
2339 unsigned int res)
2340{
2341 /* Looks like the unsol event is incompatible with the standard
2342 * definition. 4bit tag is placed at 28 bit!
2343 */
2344 if ((res >> 28) == ALC880_HP_EVENT)
2345 alc880_medion_rim_automute(codec);
2346}
2347
2278#ifdef CONFIG_SND_HDA_POWER_SAVE 2348#ifdef CONFIG_SND_HDA_POWER_SAVE
2279static struct hda_amp_list alc880_loopbacks[] = { 2349static struct hda_amp_list alc880_loopbacks[] = {
2280 { 0x0b, HDA_INPUT, 0 }, 2350 { 0x0b, HDA_INPUT, 0 },
@@ -2882,6 +2952,7 @@ static const char *alc880_models[ALC880_MODEL_LAST] = {
2882 [ALC880_F1734] = "F1734", 2952 [ALC880_F1734] = "F1734",
2883 [ALC880_LG] = "lg", 2953 [ALC880_LG] = "lg",
2884 [ALC880_LG_LW] = "lg-lw", 2954 [ALC880_LG_LW] = "lg-lw",
2955 [ALC880_MEDION_RIM] = "medion",
2885#ifdef CONFIG_SND_DEBUG 2956#ifdef CONFIG_SND_DEBUG
2886 [ALC880_TEST] = "test", 2957 [ALC880_TEST] = "test",
2887#endif 2958#endif
@@ -2933,6 +3004,7 @@ static struct snd_pci_quirk alc880_cfg_tbl[] = {
2933 SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_UNIWILL), 3004 SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_UNIWILL),
2934 SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_UNIWILL_P53), 3005 SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_UNIWILL_P53),
2935 SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810), 3006 SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810),
3007 SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_MEDION_RIM),
2936 SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG), 3008 SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG),
2937 SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG), 3009 SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG),
2938 SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734), 3010 SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734),
@@ -3227,6 +3299,20 @@ static struct alc_config_preset alc880_presets[] = {
3227 .unsol_event = alc880_lg_lw_unsol_event, 3299 .unsol_event = alc880_lg_lw_unsol_event,
3228 .init_hook = alc880_lg_lw_automute, 3300 .init_hook = alc880_lg_lw_automute,
3229 }, 3301 },
3302 [ALC880_MEDION_RIM] = {
3303 .mixers = { alc880_medion_rim_mixer },
3304 .init_verbs = { alc880_volume_init_verbs,
3305 alc880_medion_rim_init_verbs,
3306 alc_gpio2_init_verbs },
3307 .num_dacs = ARRAY_SIZE(alc880_dac_nids),
3308 .dac_nids = alc880_dac_nids,
3309 .dig_out_nid = ALC880_DIGOUT_NID,
3310 .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
3311 .channel_mode = alc880_2_jack_modes,
3312 .input_mux = &alc880_medion_rim_capture_source,
3313 .unsol_event = alc880_medion_rim_unsol_event,
3314 .init_hook = alc880_medion_rim_automute,
3315 },
3230#ifdef CONFIG_SND_DEBUG 3316#ifdef CONFIG_SND_DEBUG
3231 [ALC880_TEST] = { 3317 [ALC880_TEST] = {
3232 .mixers = { alc880_test_mixer }, 3318 .mixers = { alc880_test_mixer },
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index 4490422fb930..67350901772c 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -2429,6 +2429,7 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
2429 if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712, 2429 if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
2430 ICEREG1724(ice, MPU_CTRL), 2430 ICEREG1724(ice, MPU_CTRL),
2431 (MPU401_INFO_INTEGRATED | 2431 (MPU401_INFO_INTEGRATED |
2432 MPU401_INFO_NO_ACK |
2432 MPU401_INFO_TX_IRQ), 2433 MPU401_INFO_TX_IRQ),
2433 ice->irq, 0, 2434 ice->irq, 0,
2434 &ice->rmidi[0])) < 0) { 2435 &ice->rmidi[0])) < 0) {
@@ -2442,12 +2443,10 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
2442 outb(inb(ICEREG1724(ice, IRQMASK)) & 2443 outb(inb(ICEREG1724(ice, IRQMASK)) &
2443 ~(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX), 2444 ~(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX),
2444 ICEREG1724(ice, IRQMASK)); 2445 ICEREG1724(ice, IRQMASK));
2445#if 0 /* for testing */
2446 /* set watermarks */ 2446 /* set watermarks */
2447 outb(VT1724_MPU_RX_FIFO | 0x1, 2447 outb(VT1724_MPU_RX_FIFO | 0x1,
2448 ICEREG1724(ice, MPU_FIFO_WM)); 2448 ICEREG1724(ice, MPU_FIFO_WM));
2449 outb(0x1, ICEREG1724(ice, MPU_FIFO_WM)); 2449 outb(0x1, ICEREG1724(ice, MPU_FIFO_WM));
2450#endif
2451 } 2450 }
2452 } 2451 }
2453 2452
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/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index d2d79e182a45..76c1e2d33e7d 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -37,23 +37,23 @@ static int ac97_write(struct snd_soc_codec *codec,
37 * WM9712 register cache 37 * WM9712 register cache
38 */ 38 */
39static const u16 wm9712_reg[] = { 39static const u16 wm9712_reg[] = {
40 0x6174, 0x8000, 0x8000, 0x8000, // 6 40 0x6174, 0x8000, 0x8000, 0x8000, /* 6 */
41 0x0f0f, 0xaaa0, 0xc008, 0x6808, // e 41 0x0f0f, 0xaaa0, 0xc008, 0x6808, /* e */
42 0xe808, 0xaaa0, 0xad00, 0x8000, // 16 42 0xe808, 0xaaa0, 0xad00, 0x8000, /* 16 */
43 0xe808, 0x3000, 0x8000, 0x0000, // 1e 43 0xe808, 0x3000, 0x8000, 0x0000, /* 1e */
44 0x0000, 0x0000, 0x0000, 0x000f, // 26 44 0x0000, 0x0000, 0x0000, 0x000f, /* 26 */
45 0x0405, 0x0410, 0xbb80, 0xbb80, // 2e 45 0x0405, 0x0410, 0xbb80, 0xbb80, /* 2e */
46 0x0000, 0xbb80, 0x0000, 0x0000, // 36 46 0x0000, 0xbb80, 0x0000, 0x0000, /* 36 */
47 0x0000, 0x2000, 0x0000, 0x0000, // 3e 47 0x0000, 0x2000, 0x0000, 0x0000, /* 3e */
48 0x0000, 0x0000, 0x0000, 0x0000, // 46 48 0x0000, 0x0000, 0x0000, 0x0000, /* 46 */
49 0x0000, 0x0000, 0xf83e, 0xffff, // 4e 49 0x0000, 0x0000, 0xf83e, 0xffff, /* 4e */
50 0x0000, 0x0000, 0x0000, 0xf83e, // 56 50 0x0000, 0x0000, 0x0000, 0xf83e, /* 56 */
51 0x0008, 0x0000, 0x0000, 0x0000, // 5e 51 0x0008, 0x0000, 0x0000, 0x0000, /* 5e */
52 0xb032, 0x3e00, 0x0000, 0x0000, // 66 52 0xb032, 0x3e00, 0x0000, 0x0000, /* 66 */
53 0x0000, 0x0000, 0x0000, 0x0000, // 6e 53 0x0000, 0x0000, 0x0000, 0x0000, /* 6e */
54 0x0000, 0x0000, 0x0000, 0x0006, // 76 54 0x0000, 0x0000, 0x0000, 0x0006, /* 76 */
55 0x0001, 0x0000, 0x574d, 0x4c12, // 7e 55 0x0001, 0x0000, 0x574d, 0x4c12, /* 7e */
56 0x0000, 0x0000 // virtual hp mixers 56 0x0000, 0x0000 /* virtual hp mixers */
57}; 57};
58 58
59/* virtual HP mixers regs */ 59/* virtual HP mixers regs */
@@ -94,7 +94,7 @@ static const struct snd_kcontrol_new wm9712_snd_ac97_controls[] = {
94SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1), 94SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1),
95SOC_SINGLE("Speaker Playback Switch", AC97_MASTER, 15, 1, 1), 95SOC_SINGLE("Speaker Playback Switch", AC97_MASTER, 15, 1, 1),
96SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1), 96SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
97SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE,15, 1, 1), 97SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1),
98SOC_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1), 98SOC_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1),
99 99
100SOC_SINGLE("Speaker Playback ZC Switch", AC97_MASTER, 7, 1, 0), 100SOC_SINGLE("Speaker Playback ZC Switch", AC97_MASTER, 7, 1, 0),
@@ -165,7 +165,8 @@ static int wm9712_add_controls(struct snd_soc_codec *codec)
165 165
166 for (i = 0; i < ARRAY_SIZE(wm9712_snd_ac97_controls); i++) { 166 for (i = 0; i < ARRAY_SIZE(wm9712_snd_ac97_controls); i++) {
167 err = snd_ctl_add(codec->card, 167 err = snd_ctl_add(codec->card,
168 snd_soc_cnew(&wm9712_snd_ac97_controls[i],codec, NULL)); 168 snd_soc_cnew(&wm9712_snd_ac97_controls[i],
169 codec, NULL));
169 if (err < 0) 170 if (err < 0)
170 return err; 171 return err;
171 } 172 }
@@ -363,7 +364,6 @@ static const char *audio_map[][3] = {
363 {"Left HP Mixer", "PCM Playback Switch", "Left DAC"}, 364 {"Left HP Mixer", "PCM Playback Switch", "Left DAC"},
364 {"Left HP Mixer", "Mic Sidetone Switch", "Mic PGA"}, 365 {"Left HP Mixer", "Mic Sidetone Switch", "Mic PGA"},
365 {"Left HP Mixer", NULL, "ALC Sidetone Mux"}, 366 {"Left HP Mixer", NULL, "ALC Sidetone Mux"},
366 //{"Right HP Mixer", NULL, "HP Mixer"},
367 367
368 /* Right HP mixer */ 368 /* Right HP mixer */
369 {"Right HP Mixer", "PCBeep Bypass Switch", "PCBEEP"}, 369 {"Right HP Mixer", "PCBeep Bypass Switch", "PCBEEP"},
@@ -454,15 +454,13 @@ static int wm9712_add_widgets(struct snd_soc_codec *codec)
454{ 454{
455 int i; 455 int i;
456 456
457 for(i = 0; i < ARRAY_SIZE(wm9712_dapm_widgets); i++) { 457 for (i = 0; i < ARRAY_SIZE(wm9712_dapm_widgets); i++)
458 snd_soc_dapm_new_control(codec, &wm9712_dapm_widgets[i]); 458 snd_soc_dapm_new_control(codec, &wm9712_dapm_widgets[i]);
459 }
460 459
461 /* set up audio path audio_mapnects */ 460 /* set up audio path connects */
462 for(i = 0; audio_map[i][0] != NULL; i++) { 461 for (i = 0; audio_map[i][0] != NULL; i++)
463 snd_soc_dapm_connect_input(codec, audio_map[i][0], 462 snd_soc_dapm_connect_input(codec, audio_map[i][0],
464 audio_map[i][1], audio_map[i][2]); 463 audio_map[i][1], audio_map[i][2]);
465 }
466 464
467 snd_soc_dapm_new_widgets(codec); 465 snd_soc_dapm_new_widgets(codec);
468 return 0; 466 return 0;
@@ -540,7 +538,8 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream)
540} 538}
541 539
542#define WM9712_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ 540#define WM9712_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
543 SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) 541 SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
542 SNDRV_PCM_RATE_48000)
544 543
545struct snd_soc_codec_dai wm9712_dai[] = { 544struct snd_soc_codec_dai wm9712_dai[] = {
546{ 545{
@@ -577,8 +576,6 @@ EXPORT_SYMBOL_GPL(wm9712_dai);
577 576
578static int wm9712_dapm_event(struct snd_soc_codec *codec, int event) 577static int wm9712_dapm_event(struct snd_soc_codec *codec, int event)
579{ 578{
580 u16 reg;
581
582 switch (event) { 579 switch (event) {
583 case SNDRV_CTL_POWER_D0: /* full On */ 580 case SNDRV_CTL_POWER_D0: /* full On */
584 case SNDRV_CTL_POWER_D1: /* partial On */ 581 case SNDRV_CTL_POWER_D1: /* partial On */
@@ -633,7 +630,7 @@ static int wm9712_soc_resume(struct platform_device *pdev)
633 u16 *cache = codec->reg_cache; 630 u16 *cache = codec->reg_cache;
634 631
635 ret = wm9712_reset(codec, 1); 632 ret = wm9712_reset(codec, 1);
636 if (ret < 0){ 633 if (ret < 0) {
637 printk(KERN_ERR "could not reset AC97 codec\n"); 634 printk(KERN_ERR "could not reset AC97 codec\n");
638 return ret; 635 return ret;
639 } 636 }
@@ -642,9 +639,9 @@ static int wm9712_soc_resume(struct platform_device *pdev)
642 639
643 if (ret == 0) { 640 if (ret == 0) {
644 /* Sync reg_cache with the hardware after cold reset */ 641 /* Sync reg_cache with the hardware after cold reset */
645 for (i = 2; i < ARRAY_SIZE(wm9712_reg) << 1; i+=2) { 642 for (i = 2; i < ARRAY_SIZE(wm9712_reg) << 1; i += 2) {
646 if (i == AC97_INT_PAGING || i == AC97_POWERDOWN || 643 if (i == AC97_INT_PAGING || i == AC97_POWERDOWN ||
647 (i > 0x58 && i != 0x5c)) 644 (i > 0x58 && i != 0x5c))
648 continue; 645 continue;
649 soc_ac97_ops.write(codec->ac97, i, cache[i>>1]); 646 soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
650 } 647 }
@@ -757,7 +754,6 @@ struct snd_soc_codec_device soc_codec_dev_wm9712 = {
757 .suspend = wm9712_soc_suspend, 754 .suspend = wm9712_soc_suspend,
758 .resume = wm9712_soc_resume, 755 .resume = wm9712_soc_resume,
759}; 756};
760
761EXPORT_SYMBOL_GPL(soc_codec_dev_wm9712); 757EXPORT_SYMBOL_GPL(soc_codec_dev_wm9712);
762 758
763MODULE_DESCRIPTION("ASoC WM9711/WM9712 driver"); 759MODULE_DESCRIPTION("ASoC WM9711/WM9712 driver");
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