mirror of
https://github.com/torvalds/linux.git
synced 2024-11-16 17:12:06 +00:00
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
This commit is contained in:
commit
25a025863e
@ -795,6 +795,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
||||
lg-lw LG LW20/LW25 laptop
|
||||
tcl TCL S700
|
||||
clevo Clevo laptops (m520G, m665n)
|
||||
medion Medion Rim 2150
|
||||
test for testing/debugging purpose, almost all controls can be
|
||||
adjusted. Appearing only when compiled with
|
||||
$CONFIG_SND_DEBUG=y
|
||||
|
@ -50,6 +50,7 @@
|
||||
#define MPU401_INFO_INTEGRATED (1 << 2) /* integrated h/w port */
|
||||
#define MPU401_INFO_MMIO (1 << 3) /* MMIO access */
|
||||
#define MPU401_INFO_TX_IRQ (1 << 4) /* independent TX irq */
|
||||
#define MPU401_INFO_NO_ACK (1 << 6) /* No ACK cmd needed */
|
||||
|
||||
#define MPU401_MODE_BIT_INPUT 0
|
||||
#define MPU401_MODE_BIT_OUTPUT 1
|
||||
|
@ -8,6 +8,8 @@ config SND_PCSP
|
||||
tristate "Internal PC speaker support"
|
||||
depends on X86_PC && HIGH_RES_TIMERS
|
||||
depends on INPUT
|
||||
depends on SND
|
||||
select SND_PCM
|
||||
help
|
||||
If you don't have a sound card in your computer, you can include a
|
||||
driver for the PC speaker which allows it to act like a primitive
|
||||
|
@ -243,7 +243,7 @@ static int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd,
|
||||
#endif
|
||||
}
|
||||
mpu->write(mpu, cmd, MPU401C(mpu));
|
||||
if (ack) {
|
||||
if (ack && !(mpu->info_flags & MPU401_INFO_NO_ACK)) {
|
||||
ok = 0;
|
||||
timeout = 10000;
|
||||
while (!ok && timeout-- > 0) {
|
||||
|
@ -60,6 +60,7 @@ enum {
|
||||
ALC880_TCL_S700,
|
||||
ALC880_LG,
|
||||
ALC880_LG_LW,
|
||||
ALC880_MEDION_RIM,
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
ALC880_TEST,
|
||||
#endif
|
||||
@ -2275,6 +2276,75 @@ static void alc880_lg_lw_unsol_event(struct hda_codec *codec, unsigned int res)
|
||||
alc880_lg_lw_automute(codec);
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new alc880_medion_rim_mixer[] = {
|
||||
HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
|
||||
HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Internal Playback Switch", 0x0b, 0x1, HDA_INPUT),
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
static struct hda_input_mux alc880_medion_rim_capture_source = {
|
||||
.num_items = 2,
|
||||
.items = {
|
||||
{ "Mic", 0x0 },
|
||||
{ "Internal Mic", 0x1 },
|
||||
},
|
||||
};
|
||||
|
||||
static struct hda_verb alc880_medion_rim_init_verbs[] = {
|
||||
{0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
|
||||
|
||||
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
||||
|
||||
/* Mic1 (rear panel) pin widget for input and vref at 80% */
|
||||
{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
||||
{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
||||
/* Mic2 (as headphone out) for HP output */
|
||||
{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
||||
{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
||||
/* Internal Speaker */
|
||||
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
||||
{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
||||
|
||||
{0x20, AC_VERB_SET_COEF_INDEX, 0x07},
|
||||
{0x20, AC_VERB_SET_PROC_COEF, 0x3060},
|
||||
|
||||
{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
|
||||
{ }
|
||||
};
|
||||
|
||||
/* toggle speaker-output according to the hp-jack state */
|
||||
static void alc880_medion_rim_automute(struct hda_codec *codec)
|
||||
{
|
||||
unsigned int present;
|
||||
unsigned char bits;
|
||||
|
||||
present = snd_hda_codec_read(codec, 0x14, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0)
|
||||
& AC_PINSENSE_PRESENCE;
|
||||
bits = present ? HDA_AMP_MUTE : 0;
|
||||
snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
|
||||
HDA_AMP_MUTE, bits);
|
||||
if (present)
|
||||
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0);
|
||||
else
|
||||
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 2);
|
||||
}
|
||||
|
||||
static void alc880_medion_rim_unsol_event(struct hda_codec *codec,
|
||||
unsigned int res)
|
||||
{
|
||||
/* Looks like the unsol event is incompatible with the standard
|
||||
* definition. 4bit tag is placed at 28 bit!
|
||||
*/
|
||||
if ((res >> 28) == ALC880_HP_EVENT)
|
||||
alc880_medion_rim_automute(codec);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
static struct hda_amp_list alc880_loopbacks[] = {
|
||||
{ 0x0b, HDA_INPUT, 0 },
|
||||
@ -2882,6 +2952,7 @@ static const char *alc880_models[ALC880_MODEL_LAST] = {
|
||||
[ALC880_F1734] = "F1734",
|
||||
[ALC880_LG] = "lg",
|
||||
[ALC880_LG_LW] = "lg-lw",
|
||||
[ALC880_MEDION_RIM] = "medion",
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
[ALC880_TEST] = "test",
|
||||
#endif
|
||||
@ -2933,6 +3004,7 @@ static struct snd_pci_quirk alc880_cfg_tbl[] = {
|
||||
SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_UNIWILL),
|
||||
SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_UNIWILL_P53),
|
||||
SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810),
|
||||
SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_MEDION_RIM),
|
||||
SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG),
|
||||
SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG),
|
||||
SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734),
|
||||
@ -3227,6 +3299,20 @@ static struct alc_config_preset alc880_presets[] = {
|
||||
.unsol_event = alc880_lg_lw_unsol_event,
|
||||
.init_hook = alc880_lg_lw_automute,
|
||||
},
|
||||
[ALC880_MEDION_RIM] = {
|
||||
.mixers = { alc880_medion_rim_mixer },
|
||||
.init_verbs = { alc880_volume_init_verbs,
|
||||
alc880_medion_rim_init_verbs,
|
||||
alc_gpio2_init_verbs },
|
||||
.num_dacs = ARRAY_SIZE(alc880_dac_nids),
|
||||
.dac_nids = alc880_dac_nids,
|
||||
.dig_out_nid = ALC880_DIGOUT_NID,
|
||||
.num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
|
||||
.channel_mode = alc880_2_jack_modes,
|
||||
.input_mux = &alc880_medion_rim_capture_source,
|
||||
.unsol_event = alc880_medion_rim_unsol_event,
|
||||
.init_hook = alc880_medion_rim_automute,
|
||||
},
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
[ALC880_TEST] = {
|
||||
.mixers = { alc880_test_mixer },
|
||||
|
@ -2429,6 +2429,7 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
|
||||
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
|
||||
ICEREG1724(ice, MPU_CTRL),
|
||||
(MPU401_INFO_INTEGRATED |
|
||||
MPU401_INFO_NO_ACK |
|
||||
MPU401_INFO_TX_IRQ),
|
||||
ice->irq, 0,
|
||||
&ice->rmidi[0])) < 0) {
|
||||
@ -2442,12 +2443,10 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
|
||||
outb(inb(ICEREG1724(ice, IRQMASK)) &
|
||||
~(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX),
|
||||
ICEREG1724(ice, IRQMASK));
|
||||
#if 0 /* for testing */
|
||||
/* set watermarks */
|
||||
outb(VT1724_MPU_RX_FIFO | 0x1,
|
||||
ICEREG1724(ice, MPU_FIFO_WM));
|
||||
outb(0x1, ICEREG1724(ice, MPU_FIFO_WM));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@ source "sound/soc/s3c24xx/Kconfig"
|
||||
source "sound/soc/sh/Kconfig"
|
||||
source "sound/soc/fsl/Kconfig"
|
||||
source "sound/soc/davinci/Kconfig"
|
||||
source "sound/soc/omap/Kconfig"
|
||||
|
||||
# Supported codecs
|
||||
source "sound/soc/codecs/Kconfig"
|
||||
|
@ -1,4 +1,4 @@
|
||||
snd-soc-core-objs := soc-core.o soc-dapm.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
|
||||
obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/
|
||||
obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/ omap/
|
||||
|
@ -37,23 +37,23 @@ static int ac97_write(struct snd_soc_codec *codec,
|
||||
* WM9712 register cache
|
||||
*/
|
||||
static const u16 wm9712_reg[] = {
|
||||
0x6174, 0x8000, 0x8000, 0x8000, // 6
|
||||
0x0f0f, 0xaaa0, 0xc008, 0x6808, // e
|
||||
0xe808, 0xaaa0, 0xad00, 0x8000, // 16
|
||||
0xe808, 0x3000, 0x8000, 0x0000, // 1e
|
||||
0x0000, 0x0000, 0x0000, 0x000f, // 26
|
||||
0x0405, 0x0410, 0xbb80, 0xbb80, // 2e
|
||||
0x0000, 0xbb80, 0x0000, 0x0000, // 36
|
||||
0x0000, 0x2000, 0x0000, 0x0000, // 3e
|
||||
0x0000, 0x0000, 0x0000, 0x0000, // 46
|
||||
0x0000, 0x0000, 0xf83e, 0xffff, // 4e
|
||||
0x0000, 0x0000, 0x0000, 0xf83e, // 56
|
||||
0x0008, 0x0000, 0x0000, 0x0000, // 5e
|
||||
0xb032, 0x3e00, 0x0000, 0x0000, // 66
|
||||
0x0000, 0x0000, 0x0000, 0x0000, // 6e
|
||||
0x0000, 0x0000, 0x0000, 0x0006, // 76
|
||||
0x0001, 0x0000, 0x574d, 0x4c12, // 7e
|
||||
0x0000, 0x0000 // virtual hp mixers
|
||||
0x6174, 0x8000, 0x8000, 0x8000, /* 6 */
|
||||
0x0f0f, 0xaaa0, 0xc008, 0x6808, /* e */
|
||||
0xe808, 0xaaa0, 0xad00, 0x8000, /* 16 */
|
||||
0xe808, 0x3000, 0x8000, 0x0000, /* 1e */
|
||||
0x0000, 0x0000, 0x0000, 0x000f, /* 26 */
|
||||
0x0405, 0x0410, 0xbb80, 0xbb80, /* 2e */
|
||||
0x0000, 0xbb80, 0x0000, 0x0000, /* 36 */
|
||||
0x0000, 0x2000, 0x0000, 0x0000, /* 3e */
|
||||
0x0000, 0x0000, 0x0000, 0x0000, /* 46 */
|
||||
0x0000, 0x0000, 0xf83e, 0xffff, /* 4e */
|
||||
0x0000, 0x0000, 0x0000, 0xf83e, /* 56 */
|
||||
0x0008, 0x0000, 0x0000, 0x0000, /* 5e */
|
||||
0xb032, 0x3e00, 0x0000, 0x0000, /* 66 */
|
||||
0x0000, 0x0000, 0x0000, 0x0000, /* 6e */
|
||||
0x0000, 0x0000, 0x0000, 0x0006, /* 76 */
|
||||
0x0001, 0x0000, 0x574d, 0x4c12, /* 7e */
|
||||
0x0000, 0x0000 /* virtual hp mixers */
|
||||
};
|
||||
|
||||
/* virtual HP mixers regs */
|
||||
@ -94,7 +94,7 @@ static const struct snd_kcontrol_new wm9712_snd_ac97_controls[] = {
|
||||
SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1),
|
||||
SOC_SINGLE("Speaker Playback Switch", AC97_MASTER, 15, 1, 1),
|
||||
SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
|
||||
SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE,15, 1, 1),
|
||||
SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1),
|
||||
SOC_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1),
|
||||
|
||||
SOC_SINGLE("Speaker Playback ZC Switch", AC97_MASTER, 7, 1, 0),
|
||||
@ -165,7 +165,8 @@ static int wm9712_add_controls(struct snd_soc_codec *codec)
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(wm9712_snd_ac97_controls); i++) {
|
||||
err = snd_ctl_add(codec->card,
|
||||
snd_soc_cnew(&wm9712_snd_ac97_controls[i],codec, NULL));
|
||||
snd_soc_cnew(&wm9712_snd_ac97_controls[i],
|
||||
codec, NULL));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
@ -363,7 +364,6 @@ static const char *audio_map[][3] = {
|
||||
{"Left HP Mixer", "PCM Playback Switch", "Left DAC"},
|
||||
{"Left HP Mixer", "Mic Sidetone Switch", "Mic PGA"},
|
||||
{"Left HP Mixer", NULL, "ALC Sidetone Mux"},
|
||||
//{"Right HP Mixer", NULL, "HP Mixer"},
|
||||
|
||||
/* Right HP mixer */
|
||||
{"Right HP Mixer", "PCBeep Bypass Switch", "PCBEEP"},
|
||||
@ -454,15 +454,13 @@ static int wm9712_add_widgets(struct snd_soc_codec *codec)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < ARRAY_SIZE(wm9712_dapm_widgets); i++) {
|
||||
for (i = 0; i < ARRAY_SIZE(wm9712_dapm_widgets); i++)
|
||||
snd_soc_dapm_new_control(codec, &wm9712_dapm_widgets[i]);
|
||||
}
|
||||
|
||||
/* set up audio path audio_mapnects */
|
||||
for(i = 0; audio_map[i][0] != NULL; i++) {
|
||||
/* set up audio path connects */
|
||||
for (i = 0; audio_map[i][0] != NULL; i++)
|
||||
snd_soc_dapm_connect_input(codec, audio_map[i][0],
|
||||
audio_map[i][1], audio_map[i][2]);
|
||||
}
|
||||
audio_map[i][1], audio_map[i][2]);
|
||||
|
||||
snd_soc_dapm_new_widgets(codec);
|
||||
return 0;
|
||||
@ -540,7 +538,8 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream)
|
||||
}
|
||||
|
||||
#define WM9712_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
|
||||
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
|
||||
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
|
||||
SNDRV_PCM_RATE_48000)
|
||||
|
||||
struct snd_soc_codec_dai wm9712_dai[] = {
|
||||
{
|
||||
@ -577,8 +576,6 @@ EXPORT_SYMBOL_GPL(wm9712_dai);
|
||||
|
||||
static int wm9712_dapm_event(struct snd_soc_codec *codec, int event)
|
||||
{
|
||||
u16 reg;
|
||||
|
||||
switch (event) {
|
||||
case SNDRV_CTL_POWER_D0: /* full On */
|
||||
case SNDRV_CTL_POWER_D1: /* partial On */
|
||||
@ -633,7 +630,7 @@ static int wm9712_soc_resume(struct platform_device *pdev)
|
||||
u16 *cache = codec->reg_cache;
|
||||
|
||||
ret = wm9712_reset(codec, 1);
|
||||
if (ret < 0){
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "could not reset AC97 codec\n");
|
||||
return ret;
|
||||
}
|
||||
@ -642,9 +639,9 @@ static int wm9712_soc_resume(struct platform_device *pdev)
|
||||
|
||||
if (ret == 0) {
|
||||
/* Sync reg_cache with the hardware after cold reset */
|
||||
for (i = 2; i < ARRAY_SIZE(wm9712_reg) << 1; i+=2) {
|
||||
for (i = 2; i < ARRAY_SIZE(wm9712_reg) << 1; i += 2) {
|
||||
if (i == AC97_INT_PAGING || i == AC97_POWERDOWN ||
|
||||
(i > 0x58 && i != 0x5c))
|
||||
(i > 0x58 && i != 0x5c))
|
||||
continue;
|
||||
soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
|
||||
}
|
||||
@ -757,7 +754,6 @@ struct snd_soc_codec_device soc_codec_dev_wm9712 = {
|
||||
.suspend = wm9712_soc_suspend,
|
||||
.resume = wm9712_soc_resume,
|
||||
};
|
||||
|
||||
EXPORT_SYMBOL_GPL(soc_codec_dev_wm9712);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC WM9711/WM9712 driver");
|
||||
|
19
sound/soc/omap/Kconfig
Normal file
19
sound/soc/omap/Kconfig
Normal file
@ -0,0 +1,19 @@
|
||||
menu "SoC Audio for the Texas Instruments OMAP"
|
||||
|
||||
config SND_OMAP_SOC
|
||||
tristate "SoC Audio for the Texas Instruments OMAP chips"
|
||||
depends on ARCH_OMAP && SND_SOC
|
||||
|
||||
config SND_OMAP_SOC_MCBSP
|
||||
tristate
|
||||
select OMAP_MCBSP
|
||||
|
||||
config SND_OMAP_SOC_N810
|
||||
tristate "SoC Audio support for Nokia N810"
|
||||
depends on SND_OMAP_SOC && MACH_NOKIA_N810
|
||||
select SND_OMAP_SOC_MCBSP
|
||||
select SND_SOC_TLV320AIC3X
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on Nokia N810.
|
||||
|
||||
endmenu
|
11
sound/soc/omap/Makefile
Normal file
11
sound/soc/omap/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
# OMAP Platform Support
|
||||
snd-soc-omap-objs := omap-pcm.o
|
||||
snd-soc-omap-mcbsp-objs := omap-mcbsp.o
|
||||
|
||||
obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o
|
||||
obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o
|
||||
|
||||
# OMAP Machine Support
|
||||
snd-soc-n810-objs := n810.o
|
||||
|
||||
obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
|
336
sound/soc/omap/n810.c
Normal file
336
sound/soc/omap/n810.c
Normal file
@ -0,0 +1,336 @@
|
||||
/*
|
||||
* n810.c -- SoC audio for Nokia N810
|
||||
*
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
*
|
||||
* Contact: Jarkko Nikula <jarkko.nikula@nokia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/arch/gpio.h>
|
||||
#include <asm/arch/mcbsp.h>
|
||||
|
||||
#include "omap-mcbsp.h"
|
||||
#include "omap-pcm.h"
|
||||
#include "../codecs/tlv320aic3x.h"
|
||||
|
||||
#define RX44_HEADSET_AMP_GPIO 10
|
||||
#define RX44_SPEAKER_AMP_GPIO 101
|
||||
|
||||
static struct clk *sys_clkout2;
|
||||
static struct clk *sys_clkout2_src;
|
||||
static struct clk *func96m_clk;
|
||||
|
||||
static int n810_spk_func;
|
||||
static int n810_jack_func;
|
||||
|
||||
static void n810_ext_control(struct snd_soc_codec *codec)
|
||||
{
|
||||
snd_soc_dapm_set_endpoint(codec, "Ext Spk", n810_spk_func);
|
||||
snd_soc_dapm_set_endpoint(codec, "Headphone Jack", n810_jack_func);
|
||||
|
||||
snd_soc_dapm_sync_endpoints(codec);
|
||||
}
|
||||
|
||||
static int n810_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_codec *codec = rtd->socdev->codec;
|
||||
|
||||
n810_ext_control(codec);
|
||||
return clk_enable(sys_clkout2);
|
||||
}
|
||||
|
||||
static void n810_shutdown(struct snd_pcm_substream *substream)
|
||||
{
|
||||
clk_disable(sys_clkout2);
|
||||
}
|
||||
|
||||
static int n810_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
|
||||
struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
|
||||
int err;
|
||||
|
||||
/* Set codec DAI configuration */
|
||||
err = codec_dai->dai_ops.set_fmt(codec_dai,
|
||||
SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBM_CFM);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Set cpu DAI configuration */
|
||||
err = cpu_dai->dai_ops.set_fmt(cpu_dai,
|
||||
SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBM_CFM);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Set the codec system clock for DAC and ADC */
|
||||
err = codec_dai->dai_ops.set_sysclk(codec_dai, 0, 12000000,
|
||||
SND_SOC_CLOCK_IN);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops n810_ops = {
|
||||
.startup = n810_startup,
|
||||
.hw_params = n810_hw_params,
|
||||
.shutdown = n810_shutdown,
|
||||
};
|
||||
|
||||
static int n810_get_spk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = n810_spk_func;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int n810_set_spk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
if (n810_spk_func == ucontrol->value.integer.value[0])
|
||||
return 0;
|
||||
|
||||
n810_spk_func = ucontrol->value.integer.value[0];
|
||||
n810_ext_control(codec);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int n810_get_jack(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = n810_jack_func;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int n810_set_jack(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
if (n810_jack_func == ucontrol->value.integer.value[0])
|
||||
return 0;
|
||||
|
||||
n810_jack_func = ucontrol->value.integer.value[0];
|
||||
n810_ext_control(codec);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int n810_spk_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *k, int event)
|
||||
{
|
||||
if (SND_SOC_DAPM_EVENT_ON(event))
|
||||
omap_set_gpio_dataout(RX44_SPEAKER_AMP_GPIO, 1);
|
||||
else
|
||||
omap_set_gpio_dataout(RX44_SPEAKER_AMP_GPIO, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int n810_jack_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *k, int event)
|
||||
{
|
||||
if (SND_SOC_DAPM_EVENT_ON(event))
|
||||
omap_set_gpio_dataout(RX44_HEADSET_AMP_GPIO, 1);
|
||||
else
|
||||
omap_set_gpio_dataout(RX44_HEADSET_AMP_GPIO, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget aic33_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_SPK("Ext Spk", n810_spk_event),
|
||||
SND_SOC_DAPM_HP("Headphone Jack", n810_jack_event),
|
||||
};
|
||||
|
||||
static const char *audio_map[][3] = {
|
||||
{"Headphone Jack", NULL, "HPLOUT"},
|
||||
{"Headphone Jack", NULL, "HPROUT"},
|
||||
|
||||
{"Ext Spk", NULL, "LLOUT"},
|
||||
{"Ext Spk", NULL, "RLOUT"},
|
||||
};
|
||||
|
||||
static const char *spk_function[] = {"Off", "On"};
|
||||
static const char *jack_function[] = {"Off", "Headphone"};
|
||||
static const struct soc_enum n810_enum[] = {
|
||||
SOC_ENUM_SINGLE_EXT(2, spk_function),
|
||||
SOC_ENUM_SINGLE_EXT(3, jack_function),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new aic33_n810_controls[] = {
|
||||
SOC_ENUM_EXT("Speaker Function", n810_enum[0],
|
||||
n810_get_spk, n810_set_spk),
|
||||
SOC_ENUM_EXT("Jack Function", n810_enum[1],
|
||||
n810_get_jack, n810_set_jack),
|
||||
};
|
||||
|
||||
static int n810_aic33_init(struct snd_soc_codec *codec)
|
||||
{
|
||||
int i, err;
|
||||
|
||||
/* Not connected */
|
||||
snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0);
|
||||
snd_soc_dapm_set_endpoint(codec, "HPLCOM", 0);
|
||||
snd_soc_dapm_set_endpoint(codec, "HPRCOM", 0);
|
||||
|
||||
/* Add N810 specific controls */
|
||||
for (i = 0; i < ARRAY_SIZE(aic33_n810_controls); i++) {
|
||||
err = snd_ctl_add(codec->card,
|
||||
snd_soc_cnew(&aic33_n810_controls[i], codec, NULL));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Add N810 specific widgets */
|
||||
for (i = 0; i < ARRAY_SIZE(aic33_dapm_widgets); i++)
|
||||
snd_soc_dapm_new_control(codec, &aic33_dapm_widgets[i]);
|
||||
|
||||
/* Set up N810 specific audio path audio_map */
|
||||
for (i = 0; i < ARRAY_SIZE(audio_map); i++)
|
||||
snd_soc_dapm_connect_input(codec, audio_map[i][0],
|
||||
audio_map[i][1], audio_map[i][2]);
|
||||
|
||||
snd_soc_dapm_sync_endpoints(codec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Digital audio interface glue - connects codec <--> CPU */
|
||||
static struct snd_soc_dai_link n810_dai = {
|
||||
.name = "TLV320AIC33",
|
||||
.stream_name = "AIC33",
|
||||
.cpu_dai = &omap_mcbsp_dai[0],
|
||||
.codec_dai = &aic3x_dai,
|
||||
.init = n810_aic33_init,
|
||||
.ops = &n810_ops,
|
||||
};
|
||||
|
||||
/* Audio machine driver */
|
||||
static struct snd_soc_machine snd_soc_machine_n810 = {
|
||||
.name = "N810",
|
||||
.dai_link = &n810_dai,
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
/* Audio private data */
|
||||
static struct aic3x_setup_data n810_aic33_setup = {
|
||||
.i2c_address = 0x18,
|
||||
};
|
||||
|
||||
/* Audio subsystem */
|
||||
static struct snd_soc_device n810_snd_devdata = {
|
||||
.machine = &snd_soc_machine_n810,
|
||||
.platform = &omap_soc_platform,
|
||||
.codec_dev = &soc_codec_dev_aic3x,
|
||||
.codec_data = &n810_aic33_setup,
|
||||
};
|
||||
|
||||
static struct platform_device *n810_snd_device;
|
||||
|
||||
static int __init n810_soc_init(void)
|
||||
{
|
||||
int err;
|
||||
struct device *dev;
|
||||
|
||||
if (!machine_is_nokia_n810())
|
||||
return -ENODEV;
|
||||
|
||||
n810_snd_device = platform_device_alloc("soc-audio", -1);
|
||||
if (!n810_snd_device)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(n810_snd_device, &n810_snd_devdata);
|
||||
n810_snd_devdata.dev = &n810_snd_device->dev;
|
||||
*(unsigned int *)n810_dai.cpu_dai->private_data = 1; /* McBSP2 */
|
||||
err = platform_device_add(n810_snd_device);
|
||||
if (err)
|
||||
goto err1;
|
||||
|
||||
dev = &n810_snd_device->dev;
|
||||
|
||||
sys_clkout2_src = clk_get(dev, "sys_clkout2_src");
|
||||
if (IS_ERR(sys_clkout2_src)) {
|
||||
dev_err(dev, "Could not get sys_clkout2_src clock\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
sys_clkout2 = clk_get(dev, "sys_clkout2");
|
||||
if (IS_ERR(sys_clkout2)) {
|
||||
dev_err(dev, "Could not get sys_clkout2\n");
|
||||
goto err1;
|
||||
}
|
||||
/*
|
||||
* Configure 12 MHz output on SYS_CLKOUT2. Therefore we must use
|
||||
* 96 MHz as its parent in order to get 12 MHz
|
||||
*/
|
||||
func96m_clk = clk_get(dev, "func_96m_ck");
|
||||
if (IS_ERR(func96m_clk)) {
|
||||
dev_err(dev, "Could not get func 96M clock\n");
|
||||
goto err2;
|
||||
}
|
||||
clk_set_parent(sys_clkout2_src, func96m_clk);
|
||||
clk_set_rate(sys_clkout2, 12000000);
|
||||
|
||||
if (omap_request_gpio(RX44_HEADSET_AMP_GPIO) < 0)
|
||||
BUG();
|
||||
if (omap_request_gpio(RX44_SPEAKER_AMP_GPIO) < 0)
|
||||
BUG();
|
||||
omap_set_gpio_direction(RX44_HEADSET_AMP_GPIO, 0);
|
||||
omap_set_gpio_direction(RX44_SPEAKER_AMP_GPIO, 0);
|
||||
|
||||
return 0;
|
||||
err2:
|
||||
clk_put(sys_clkout2);
|
||||
platform_device_del(n810_snd_device);
|
||||
err1:
|
||||
platform_device_put(n810_snd_device);
|
||||
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
static void __exit n810_soc_exit(void)
|
||||
{
|
||||
platform_device_unregister(n810_snd_device);
|
||||
}
|
||||
|
||||
module_init(n810_soc_init);
|
||||
module_exit(n810_soc_exit);
|
||||
|
||||
MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@nokia.com>");
|
||||
MODULE_DESCRIPTION("ALSA SoC Nokia N810");
|
||||
MODULE_LICENSE("GPL");
|
414
sound/soc/omap/omap-mcbsp.c
Normal file
414
sound/soc/omap/omap-mcbsp.c
Normal file
@ -0,0 +1,414 @@
|
||||
/*
|
||||
* omap-mcbsp.c -- OMAP ALSA SoC DAI driver using McBSP port
|
||||
*
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
*
|
||||
* Contact: Jarkko Nikula <jarkko.nikula@nokia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include <asm/arch/control.h>
|
||||
#include <asm/arch/dma.h>
|
||||
#include <asm/arch/mcbsp.h>
|
||||
#include "omap-mcbsp.h"
|
||||
#include "omap-pcm.h"
|
||||
|
||||
#define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_44100 | \
|
||||
SNDRV_PCM_RATE_48000 | \
|
||||
SNDRV_PCM_RATE_KNOT)
|
||||
|
||||
struct omap_mcbsp_data {
|
||||
unsigned int bus_id;
|
||||
struct omap_mcbsp_reg_cfg regs;
|
||||
/*
|
||||
* Flags indicating is the bus already activated and configured by
|
||||
* another substream
|
||||
*/
|
||||
int active;
|
||||
int configured;
|
||||
};
|
||||
|
||||
#define to_mcbsp(priv) container_of((priv), struct omap_mcbsp_data, bus_id)
|
||||
|
||||
static struct omap_mcbsp_data mcbsp_data[NUM_LINKS];
|
||||
|
||||
/*
|
||||
* Stream DMA parameters. DMA request line and port address are set runtime
|
||||
* since they are different between OMAP1 and later OMAPs
|
||||
*/
|
||||
static struct omap_pcm_dma_data omap_mcbsp_dai_dma_params[NUM_LINKS][2] = {
|
||||
{
|
||||
{ .name = "I2S PCM Stereo out", },
|
||||
{ .name = "I2S PCM Stereo in", },
|
||||
},
|
||||
};
|
||||
|
||||
#if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX)
|
||||
static const int omap1_dma_reqs[][2] = {
|
||||
{ OMAP_DMA_MCBSP1_TX, OMAP_DMA_MCBSP1_RX },
|
||||
{ OMAP_DMA_MCBSP2_TX, OMAP_DMA_MCBSP2_RX },
|
||||
{ OMAP_DMA_MCBSP3_TX, OMAP_DMA_MCBSP3_RX },
|
||||
};
|
||||
static const unsigned long omap1_mcbsp_port[][2] = {
|
||||
{ OMAP1510_MCBSP1_BASE + OMAP_MCBSP_REG_DXR1,
|
||||
OMAP1510_MCBSP1_BASE + OMAP_MCBSP_REG_DRR1 },
|
||||
{ OMAP1510_MCBSP2_BASE + OMAP_MCBSP_REG_DXR1,
|
||||
OMAP1510_MCBSP2_BASE + OMAP_MCBSP_REG_DRR1 },
|
||||
{ OMAP1510_MCBSP3_BASE + OMAP_MCBSP_REG_DXR1,
|
||||
OMAP1510_MCBSP3_BASE + OMAP_MCBSP_REG_DRR1 },
|
||||
};
|
||||
#else
|
||||
static const int omap1_dma_reqs[][2] = {};
|
||||
static const unsigned long omap1_mcbsp_port[][2] = {};
|
||||
#endif
|
||||
#if defined(CONFIG_ARCH_OMAP2420)
|
||||
static const int omap2420_dma_reqs[][2] = {
|
||||
{ OMAP24XX_DMA_MCBSP1_TX, OMAP24XX_DMA_MCBSP1_RX },
|
||||
{ OMAP24XX_DMA_MCBSP2_TX, OMAP24XX_DMA_MCBSP2_RX },
|
||||
};
|
||||
static const unsigned long omap2420_mcbsp_port[][2] = {
|
||||
{ OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR1,
|
||||
OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR1 },
|
||||
{ OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR1,
|
||||
OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR1 },
|
||||
};
|
||||
#else
|
||||
static const int omap2420_dma_reqs[][2] = {};
|
||||
static const unsigned long omap2420_mcbsp_port[][2] = {};
|
||||
#endif
|
||||
|
||||
static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
|
||||
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
|
||||
int err = 0;
|
||||
|
||||
if (!cpu_dai->active)
|
||||
err = omap_mcbsp_request(mcbsp_data->bus_id);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
|
||||
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
|
||||
|
||||
if (!cpu_dai->active) {
|
||||
omap_mcbsp_free(mcbsp_data->bus_id);
|
||||
mcbsp_data->configured = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
|
||||
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
|
||||
int err = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
if (!mcbsp_data->active++)
|
||||
omap_mcbsp_start(mcbsp_data->bus_id);
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
if (!--mcbsp_data->active)
|
||||
omap_mcbsp_stop(mcbsp_data->bus_id);
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
|
||||
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
|
||||
struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
|
||||
int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id;
|
||||
unsigned long port;
|
||||
|
||||
if (cpu_class_is_omap1()) {
|
||||
dma = omap1_dma_reqs[bus_id][substream->stream];
|
||||
port = omap1_mcbsp_port[bus_id][substream->stream];
|
||||
} else if (cpu_is_omap2420()) {
|
||||
dma = omap2420_dma_reqs[bus_id][substream->stream];
|
||||
port = omap2420_mcbsp_port[bus_id][substream->stream];
|
||||
} else {
|
||||
/*
|
||||
* TODO: Add support for 2430 and 3430
|
||||
*/
|
||||
return -ENODEV;
|
||||
}
|
||||
omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma;
|
||||
omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port;
|
||||
cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream];
|
||||
|
||||
if (mcbsp_data->configured) {
|
||||
/* McBSP already configured by another stream */
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (params_channels(params)) {
|
||||
case 2:
|
||||
/* Set 1 word per (McBPSP) frame and use dual-phase frames */
|
||||
regs->rcr2 |= RFRLEN2(1 - 1) | RPHASE;
|
||||
regs->rcr1 |= RFRLEN1(1 - 1);
|
||||
regs->xcr2 |= XFRLEN2(1 - 1) | XPHASE;
|
||||
regs->xcr1 |= XFRLEN1(1 - 1);
|
||||
break;
|
||||
default:
|
||||
/* Unsupported number of channels */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
/* Set word lengths */
|
||||
regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16);
|
||||
regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16);
|
||||
regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16);
|
||||
regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_16);
|
||||
/* Set FS period and length in terms of bit clock periods */
|
||||
regs->srgr2 |= FPER(16 * 2 - 1);
|
||||
regs->srgr1 |= FWID(16 - 1);
|
||||
break;
|
||||
default:
|
||||
/* Unsupported PCM format */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
omap_mcbsp_config(bus_id, &mcbsp_data->regs);
|
||||
mcbsp_data->configured = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This must be called before _set_clkdiv and _set_sysclk since McBSP register
|
||||
* cache is initialized here
|
||||
*/
|
||||
static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
|
||||
struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
|
||||
|
||||
if (mcbsp_data->configured)
|
||||
return 0;
|
||||
|
||||
memset(regs, 0, sizeof(*regs));
|
||||
/* Generic McBSP register settings */
|
||||
regs->spcr2 |= XINTM(3) | FREE;
|
||||
regs->spcr1 |= RINTM(3);
|
||||
regs->rcr2 |= RFIG;
|
||||
regs->xcr2 |= XFIG;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
/* 1-bit data delay */
|
||||
regs->rcr2 |= RDATDLY(1);
|
||||
regs->xcr2 |= XDATDLY(1);
|
||||
break;
|
||||
default:
|
||||
/* Unsupported data format */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
/* McBSP master. Set FS and bit clocks as outputs */
|
||||
regs->pcr0 |= FSXM | FSRM |
|
||||
CLKXM | CLKRM;
|
||||
/* Sample rate generator drives the FS */
|
||||
regs->srgr2 |= FSGM;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
/* McBSP slave */
|
||||
break;
|
||||
default:
|
||||
/* Unsupported master/slave configuration */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Set bit clock (CLKX/CLKR) and FS polarities */
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
/*
|
||||
* Normal BCLK + FS.
|
||||
* FS active low. TX data driven on falling edge of bit clock
|
||||
* and RX data sampled on rising edge of bit clock.
|
||||
*/
|
||||
regs->pcr0 |= FSXP | FSRP |
|
||||
CLKXP | CLKRP;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
regs->pcr0 |= CLKXP | CLKRP;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
regs->pcr0 |= FSXP | FSRP;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
|
||||
int div_id, int div)
|
||||
{
|
||||
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
|
||||
struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
|
||||
|
||||
if (div_id != OMAP_MCBSP_CLKGDV)
|
||||
return -ENODEV;
|
||||
|
||||
regs->srgr1 |= CLKGDV(div - 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_mcbsp_dai_set_clks_src(struct omap_mcbsp_data *mcbsp_data,
|
||||
int clk_id)
|
||||
{
|
||||
int sel_bit;
|
||||
u16 reg;
|
||||
|
||||
if (cpu_class_is_omap1()) {
|
||||
/* OMAP1's can use only external source clock */
|
||||
if (unlikely(clk_id == OMAP_MCBSP_SYSCLK_CLKS_FCLK))
|
||||
return -EINVAL;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (mcbsp_data->bus_id) {
|
||||
case 0:
|
||||
reg = OMAP2_CONTROL_DEVCONF0;
|
||||
sel_bit = 2;
|
||||
break;
|
||||
case 1:
|
||||
reg = OMAP2_CONTROL_DEVCONF0;
|
||||
sel_bit = 6;
|
||||
break;
|
||||
/* TODO: Support for ports 3 - 5 in OMAP2430 and OMAP34xx */
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cpu_class_is_omap2()) {
|
||||
if (clk_id == OMAP_MCBSP_SYSCLK_CLKS_FCLK) {
|
||||
omap_ctrl_writel(omap_ctrl_readl(reg) &
|
||||
~(1 << sel_bit), reg);
|
||||
} else {
|
||||
omap_ctrl_writel(omap_ctrl_readl(reg) |
|
||||
(1 << sel_bit), reg);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
|
||||
int clk_id, unsigned int freq,
|
||||
int dir)
|
||||
{
|
||||
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
|
||||
struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
|
||||
int err = 0;
|
||||
|
||||
switch (clk_id) {
|
||||
case OMAP_MCBSP_SYSCLK_CLK:
|
||||
regs->srgr2 |= CLKSM;
|
||||
break;
|
||||
case OMAP_MCBSP_SYSCLK_CLKS_FCLK:
|
||||
case OMAP_MCBSP_SYSCLK_CLKS_EXT:
|
||||
err = omap_mcbsp_dai_set_clks_src(mcbsp_data, clk_id);
|
||||
break;
|
||||
|
||||
case OMAP_MCBSP_SYSCLK_CLKX_EXT:
|
||||
regs->srgr2 |= CLKSM;
|
||||
case OMAP_MCBSP_SYSCLK_CLKR_EXT:
|
||||
regs->pcr0 |= SCLKME;
|
||||
break;
|
||||
default:
|
||||
err = -ENODEV;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
struct snd_soc_cpu_dai omap_mcbsp_dai[NUM_LINKS] = {
|
||||
{
|
||||
.name = "omap-mcbsp-dai",
|
||||
.id = 0,
|
||||
.type = SND_SOC_DAI_I2S,
|
||||
.playback = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = OMAP_MCBSP_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.capture = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = OMAP_MCBSP_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.ops = {
|
||||
.startup = omap_mcbsp_dai_startup,
|
||||
.shutdown = omap_mcbsp_dai_shutdown,
|
||||
.trigger = omap_mcbsp_dai_trigger,
|
||||
.hw_params = omap_mcbsp_dai_hw_params,
|
||||
},
|
||||
.dai_ops = {
|
||||
.set_fmt = omap_mcbsp_dai_set_dai_fmt,
|
||||
.set_clkdiv = omap_mcbsp_dai_set_clkdiv,
|
||||
.set_sysclk = omap_mcbsp_dai_set_dai_sysclk,
|
||||
},
|
||||
.private_data = &mcbsp_data[0].bus_id,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(omap_mcbsp_dai);
|
||||
|
||||
MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@nokia.com>");
|
||||
MODULE_DESCRIPTION("OMAP I2S SoC Interface");
|
||||
MODULE_LICENSE("GPL");
|
49
sound/soc/omap/omap-mcbsp.h
Normal file
49
sound/soc/omap/omap-mcbsp.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* omap-mcbsp.h
|
||||
*
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
*
|
||||
* Contact: Jarkko Nikula <jarkko.nikula@nokia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __OMAP_I2S_H__
|
||||
#define __OMAP_I2S_H__
|
||||
|
||||
/* Source clocks for McBSP sample rate generator */
|
||||
enum omap_mcbsp_clksrg_clk {
|
||||
OMAP_MCBSP_SYSCLK_CLKS_FCLK, /* Internal FCLK */
|
||||
OMAP_MCBSP_SYSCLK_CLKS_EXT, /* External CLKS pin */
|
||||
OMAP_MCBSP_SYSCLK_CLK, /* Internal ICLK */
|
||||
OMAP_MCBSP_SYSCLK_CLKX_EXT, /* External CLKX pin */
|
||||
OMAP_MCBSP_SYSCLK_CLKR_EXT, /* External CLKR pin */
|
||||
};
|
||||
|
||||
/* McBSP dividers */
|
||||
enum omap_mcbsp_div {
|
||||
OMAP_MCBSP_CLKGDV, /* Sample rate generator divider */
|
||||
};
|
||||
|
||||
/*
|
||||
* REVISIT: Preparation for the ASoC v2. Let the number of available links to
|
||||
* be same than number of McBSP ports found in OMAP(s) we are compiling for.
|
||||
*/
|
||||
#define NUM_LINKS 1
|
||||
|
||||
extern struct snd_soc_cpu_dai omap_mcbsp_dai[NUM_LINKS];
|
||||
|
||||
#endif
|
357
sound/soc/omap/omap-pcm.c
Normal file
357
sound/soc/omap/omap-pcm.c
Normal file
@ -0,0 +1,357 @@
|
||||
/*
|
||||
* omap-pcm.c -- ALSA PCM interface for the OMAP SoC
|
||||
*
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
*
|
||||
* Contact: Jarkko Nikula <jarkko.nikula@nokia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include <asm/arch/dma.h>
|
||||
#include "omap-pcm.h"
|
||||
|
||||
static const struct snd_pcm_hardware omap_pcm_hardware = {
|
||||
.info = SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_RESUME,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.period_bytes_min = 32,
|
||||
.period_bytes_max = 64 * 1024,
|
||||
.periods_min = 2,
|
||||
.periods_max = 255,
|
||||
.buffer_bytes_max = 128 * 1024,
|
||||
};
|
||||
|
||||
struct omap_runtime_data {
|
||||
spinlock_t lock;
|
||||
struct omap_pcm_dma_data *dma_data;
|
||||
int dma_ch;
|
||||
int period_index;
|
||||
};
|
||||
|
||||
static void omap_pcm_dma_irq(int ch, u16 stat, void *data)
|
||||
{
|
||||
struct snd_pcm_substream *substream = data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct omap_runtime_data *prtd = runtime->private_data;
|
||||
unsigned long flags;
|
||||
|
||||
if (cpu_is_omap1510()) {
|
||||
/*
|
||||
* OMAP1510 doesn't support DMA chaining so have to restart
|
||||
* the transfer after all periods are transferred
|
||||
*/
|
||||
spin_lock_irqsave(&prtd->lock, flags);
|
||||
if (prtd->period_index >= 0) {
|
||||
if (++prtd->period_index == runtime->periods) {
|
||||
prtd->period_index = 0;
|
||||
omap_start_dma(prtd->dma_ch);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&prtd->lock, flags);
|
||||
}
|
||||
|
||||
snd_pcm_period_elapsed(substream);
|
||||
}
|
||||
|
||||
/* this may get called several times by oss emulation */
|
||||
static int omap_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct omap_runtime_data *prtd = runtime->private_data;
|
||||
struct omap_pcm_dma_data *dma_data = rtd->dai->cpu_dai->dma_data;
|
||||
int err = 0;
|
||||
|
||||
if (!dma_data)
|
||||
return -ENODEV;
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
runtime->dma_bytes = params_buffer_bytes(params);
|
||||
|
||||
if (prtd->dma_data)
|
||||
return 0;
|
||||
prtd->dma_data = dma_data;
|
||||
err = omap_request_dma(dma_data->dma_req, dma_data->name,
|
||||
omap_pcm_dma_irq, substream, &prtd->dma_ch);
|
||||
if (!cpu_is_omap1510()) {
|
||||
/*
|
||||
* Link channel with itself so DMA doesn't need any
|
||||
* reprogramming while looping the buffer
|
||||
*/
|
||||
omap_dma_link_lch(prtd->dma_ch, prtd->dma_ch);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int omap_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct omap_runtime_data *prtd = runtime->private_data;
|
||||
|
||||
if (prtd->dma_data == NULL)
|
||||
return 0;
|
||||
|
||||
if (!cpu_is_omap1510())
|
||||
omap_dma_unlink_lch(prtd->dma_ch, prtd->dma_ch);
|
||||
omap_free_dma(prtd->dma_ch);
|
||||
prtd->dma_data = NULL;
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct omap_runtime_data *prtd = runtime->private_data;
|
||||
struct omap_pcm_dma_data *dma_data = prtd->dma_data;
|
||||
struct omap_dma_channel_params dma_params;
|
||||
|
||||
memset(&dma_params, 0, sizeof(dma_params));
|
||||
/*
|
||||
* Note: Regardless of interface data formats supported by OMAP McBSP
|
||||
* or EAC blocks, internal representation is always fixed 16-bit/sample
|
||||
*/
|
||||
dma_params.data_type = OMAP_DMA_DATA_TYPE_S16;
|
||||
dma_params.trigger = dma_data->dma_req;
|
||||
dma_params.sync_mode = OMAP_DMA_SYNC_ELEMENT;
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
dma_params.src_amode = OMAP_DMA_AMODE_POST_INC;
|
||||
dma_params.dst_amode = OMAP_DMA_AMODE_CONSTANT;
|
||||
dma_params.src_or_dst_synch = OMAP_DMA_DST_SYNC;
|
||||
dma_params.src_start = runtime->dma_addr;
|
||||
dma_params.dst_start = dma_data->port_addr;
|
||||
} else {
|
||||
dma_params.src_amode = OMAP_DMA_AMODE_CONSTANT;
|
||||
dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC;
|
||||
dma_params.src_or_dst_synch = OMAP_DMA_SRC_SYNC;
|
||||
dma_params.src_start = dma_data->port_addr;
|
||||
dma_params.dst_start = runtime->dma_addr;
|
||||
}
|
||||
/*
|
||||
* Set DMA transfer frame size equal to ALSA period size and frame
|
||||
* count as no. of ALSA periods. Then with DMA frame interrupt enabled,
|
||||
* we can transfer the whole ALSA buffer with single DMA transfer but
|
||||
* still can get an interrupt at each period bounary
|
||||
*/
|
||||
dma_params.elem_count = snd_pcm_lib_period_bytes(substream) / 2;
|
||||
dma_params.frame_count = runtime->periods;
|
||||
omap_set_dma_params(prtd->dma_ch, &dma_params);
|
||||
|
||||
omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct omap_runtime_data *prtd = runtime->private_data;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irq(&prtd->lock);
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
prtd->period_index = 0;
|
||||
omap_start_dma(prtd->dma_ch);
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
prtd->period_index = -1;
|
||||
omap_stop_dma(prtd->dma_ch);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
spin_unlock_irq(&prtd->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct omap_runtime_data *prtd = runtime->private_data;
|
||||
dma_addr_t ptr;
|
||||
snd_pcm_uframes_t offset;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ptr = omap_get_dma_src_pos(prtd->dma_ch);
|
||||
else
|
||||
ptr = omap_get_dma_dst_pos(prtd->dma_ch);
|
||||
|
||||
offset = bytes_to_frames(runtime, ptr - runtime->dma_addr);
|
||||
if (offset >= runtime->buffer_size)
|
||||
offset = 0;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int omap_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct omap_runtime_data *prtd;
|
||||
int ret;
|
||||
|
||||
snd_soc_set_runtime_hwparams(substream, &omap_pcm_hardware);
|
||||
|
||||
/* Ensure that buffer size is a multiple of period size */
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
prtd = kzalloc(sizeof(prtd), GFP_KERNEL);
|
||||
if (prtd == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
spin_lock_init(&prtd->lock);
|
||||
runtime->private_data = prtd;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int omap_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
kfree(runtime->private_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_pcm_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
return dma_mmap_writecombine(substream->pcm->card->dev, vma,
|
||||
runtime->dma_area,
|
||||
runtime->dma_addr,
|
||||
runtime->dma_bytes);
|
||||
}
|
||||
|
||||
struct snd_pcm_ops omap_pcm_ops = {
|
||||
.open = omap_pcm_open,
|
||||
.close = omap_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = omap_pcm_hw_params,
|
||||
.hw_free = omap_pcm_hw_free,
|
||||
.prepare = omap_pcm_prepare,
|
||||
.trigger = omap_pcm_trigger,
|
||||
.pointer = omap_pcm_pointer,
|
||||
.mmap = omap_pcm_mmap,
|
||||
};
|
||||
|
||||
static u64 omap_pcm_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static int omap_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
|
||||
int stream)
|
||||
{
|
||||
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
|
||||
struct snd_dma_buffer *buf = &substream->dma_buffer;
|
||||
size_t size = omap_pcm_hardware.buffer_bytes_max;
|
||||
|
||||
buf->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
buf->dev.dev = pcm->card->dev;
|
||||
buf->private_data = NULL;
|
||||
buf->area = dma_alloc_writecombine(pcm->card->dev, size,
|
||||
&buf->addr, GFP_KERNEL);
|
||||
if (!buf->area)
|
||||
return -ENOMEM;
|
||||
|
||||
buf->bytes = size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_dma_buffer *buf;
|
||||
int stream;
|
||||
|
||||
for (stream = 0; stream < 2; stream++) {
|
||||
substream = pcm->streams[stream].substream;
|
||||
if (!substream)
|
||||
continue;
|
||||
|
||||
buf = &substream->dma_buffer;
|
||||
if (!buf->area)
|
||||
continue;
|
||||
|
||||
dma_free_writecombine(pcm->card->dev, buf->bytes,
|
||||
buf->area, buf->addr);
|
||||
buf->area = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int omap_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
|
||||
struct snd_pcm *pcm)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!card->dev->dma_mask)
|
||||
card->dev->dma_mask = &omap_pcm_dmamask;
|
||||
if (!card->dev->coherent_dma_mask)
|
||||
card->dev->coherent_dma_mask = DMA_32BIT_MASK;
|
||||
|
||||
if (dai->playback.channels_min) {
|
||||
ret = omap_pcm_preallocate_dma_buffer(pcm,
|
||||
SNDRV_PCM_STREAM_PLAYBACK);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dai->capture.channels_min) {
|
||||
ret = omap_pcm_preallocate_dma_buffer(pcm,
|
||||
SNDRV_PCM_STREAM_CAPTURE);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct snd_soc_platform omap_soc_platform = {
|
||||
.name = "omap-pcm-audio",
|
||||
.pcm_ops = &omap_pcm_ops,
|
||||
.pcm_new = omap_pcm_new,
|
||||
.pcm_free = omap_pcm_free_dma_buffers,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(omap_soc_platform);
|
||||
|
||||
MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@nokia.com>");
|
||||
MODULE_DESCRIPTION("OMAP PCM DMA module");
|
||||
MODULE_LICENSE("GPL");
|
35
sound/soc/omap/omap-pcm.h
Normal file
35
sound/soc/omap/omap-pcm.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* omap-pcm.h
|
||||
*
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
*
|
||||
* Contact: Jarkko Nikula <jarkko.nikula@nokia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __OMAP_PCM_H__
|
||||
#define __OMAP_PCM_H__
|
||||
|
||||
struct omap_pcm_dma_data {
|
||||
char *name; /* stream identifier */
|
||||
int dma_req; /* DMA request line */
|
||||
unsigned long port_addr; /* transmit/receive register */
|
||||
};
|
||||
|
||||
extern struct snd_soc_platform omap_soc_platform;
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user