aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/sound/alsa/ALSA-Configuration.txt1
-rw-r--r--include/sound/mpu401.h1
-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
16 files changed, 1344 insertions, 37 deletions
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt
index fd4c32a031c9..0bbee38acd26 100644
--- a/Documentation/sound/alsa/ALSA-Configuration.txt
+++ b/Documentation/sound/alsa/ALSA-Configuration.txt
@@ -795,6 +795,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
795 lg-lw LG LW20/LW25 laptop 795 lg-lw LG LW20/LW25 laptop
796 tcl TCL S700 796 tcl TCL S700
797 clevo Clevo laptops (m520G, m665n) 797 clevo Clevo laptops (m520G, m665n)
798 medion Medion Rim 2150
798 test for testing/debugging purpose, almost all controls can be 799 test for testing/debugging purpose, almost all controls can be
799 adjusted. Appearing only when compiled with 800 adjusted. Appearing only when compiled with
800 $CONFIG_SND_DEBUG=y 801 $CONFIG_SND_DEBUG=y
diff --git a/include/sound/mpu401.h b/include/sound/mpu401.h
index 68b634b75068..1f1d53f8830b 100644
--- a/include/sound/mpu401.h
+++ b/include/sound/mpu401.h
@@ -50,6 +50,7 @@
50#define MPU401_INFO_INTEGRATED (1 << 2) /* integrated h/w port */ 50#define MPU401_INFO_INTEGRATED (1 << 2) /* integrated h/w port */
51#define MPU401_INFO_MMIO (1 << 3) /* MMIO access */ 51#define MPU401_INFO_MMIO (1 << 3) /* MMIO access */
52#define MPU401_INFO_TX_IRQ (1 << 4) /* independent TX irq */ 52#define MPU401_INFO_TX_IRQ (1 << 4) /* independent TX irq */
53#define MPU401_INFO_NO_ACK (1 << 6) /* No ACK cmd needed */
53 54
54#define MPU401_MODE_BIT_INPUT 0 55#define MPU401_MODE_BIT_INPUT 0
55#define MPU401_MODE_BIT_OUTPUT 1 56#define MPU401_MODE_BIT_OUTPUT 1
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