diff options
Diffstat (limited to 'sound/soc')
-rw-r--r-- | sound/soc/tegra/tegra_soc.c | 281 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_soc.h | 9 |
2 files changed, 285 insertions, 5 deletions
diff --git a/sound/soc/tegra/tegra_soc.c b/sound/soc/tegra/tegra_soc.c index 9ffbae647254..afb3d1c761a2 100644 --- a/sound/soc/tegra/tegra_soc.c +++ b/sound/soc/tegra/tegra_soc.c @@ -20,6 +20,7 @@ #include "../codecs/wm8903.h" static struct platform_device *tegra_snd_device; +static struct tegra_audio_data *audio_data; static int tegra_jack_func; static int tegra_spk_func; @@ -292,6 +293,231 @@ static const struct snd_kcontrol_new wm8903_tegra_controls[] = { tegra_set_spk), }; +static void tegra_audio_route(int device_new, int is_call_mode_new) +{ + int play_device_new = device_new & TEGRA_AUDIO_DEVICE_OUT_ALL; + int capture_device_new = device_new & TEGRA_AUDIO_DEVICE_IN_ALL; + int is_bt_sco_mode = + (play_device_new & TEGRA_AUDIO_DEVICE_OUT_BT_SCO) || + (capture_device_new & TEGRA_AUDIO_DEVICE_OUT_BT_SCO); + int was_bt_sco_mode = + (audio_data->play_device & TEGRA_AUDIO_DEVICE_OUT_BT_SCO) || + (audio_data->capture_device & TEGRA_AUDIO_DEVICE_OUT_BT_SCO); + + if (play_device_new != audio_data->play_device) { + if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_HEADPHONE) { + tegra_jack_func = TEGRA_HP; + } + else if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_HEADSET) { + tegra_jack_func = TEGRA_HEADSET; + } + else if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_LINE) { + tegra_jack_func = TEGRA_LINE; + } + + if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_SPEAKER) { + tegra_spk_func = TEGRA_SPK_ON; + } + else if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_EAR_SPEAKER) { + tegra_spk_func = TEGRA_SPK_ON; + } + else { + tegra_spk_func = TEGRA_SPK_OFF; + } + tegra_ext_control(audio_data->codec); + audio_data->play_device = play_device_new; + } + + if (capture_device_new != audio_data->capture_device) { + if (capture_device_new & (TEGRA_AUDIO_DEVICE_IN_BUILTIN_MIC | + TEGRA_AUDIO_DEVICE_IN_MIC | + TEGRA_AUDIO_DEVICE_IN_BACK_MIC)) { + if ((tegra_jack_func != TEGRA_HP) && + (tegra_jack_func != TEGRA_HEADSET)) { + tegra_jack_func = TEGRA_MIC; + } + } + else if (capture_device_new & TEGRA_AUDIO_DEVICE_IN_HEADSET) { + tegra_jack_func = TEGRA_HEADSET; + } + else if (capture_device_new & TEGRA_AUDIO_DEVICE_IN_LINE) { + tegra_jack_func = TEGRA_LINE; + } + tegra_ext_control(audio_data->codec); + audio_data->capture_device = capture_device_new; + } + + if ((is_call_mode_new != audio_data->is_call_mode) || + (is_bt_sco_mode != was_bt_sco_mode)) { + if (is_call_mode_new && is_bt_sco_mode) { + tegra_das_set_connection + (tegra_das_port_con_id_voicecall_with_bt); + } + else if (is_call_mode_new && !is_bt_sco_mode) { + tegra_das_set_connection + (tegra_das_port_con_id_voicecall_no_bt); + } + else if (!is_call_mode_new && is_bt_sco_mode) { + tegra_das_set_connection + (tegra_das_port_con_id_bt_codec); + } + else { + tegra_das_set_connection + (tegra_das_port_con_id_hifi); + } + audio_data->is_call_mode = is_call_mode_new; + } +} + +static int tegra_play_route_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = TEGRA_AUDIO_DEVICE_NONE; + uinfo->value.integer.max = TEGRA_AUDIO_DEVICE_MAX; + return 0; +} + +static int tegra_play_route_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = TEGRA_AUDIO_DEVICE_NONE; + if (audio_data) { + ucontrol->value.integer.value[0] = audio_data->play_device; + return 0; + } + return -EINVAL; +} + +static int tegra_play_route_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + if (audio_data) { + int play_device_new = ucontrol->value.integer.value[0] & + TEGRA_AUDIO_DEVICE_OUT_ALL; + + if (audio_data->play_device != play_device_new) { + tegra_audio_route( + play_device_new | audio_data->capture_device, + audio_data->is_call_mode); + return 1; + } + return 0; + } + return -EINVAL; +} + +struct snd_kcontrol_new tegra_play_route_control = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Pcm Playback Route", + .private_value = 0xffff, + .info = tegra_play_route_info, + .get = tegra_play_route_get, + .put = tegra_play_route_put +}; + +static int tegra_capture_route_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = TEGRA_AUDIO_DEVICE_NONE; + uinfo->value.integer.max = TEGRA_AUDIO_DEVICE_MAX; + return 0; +} + +static int tegra_capture_route_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = TEGRA_AUDIO_DEVICE_NONE; + if (audio_data) { + ucontrol->value.integer.value[0] = audio_data->capture_device; + return 0; + } + return -EINVAL; +} + +static int tegra_capture_route_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + if (audio_data) { + int capture_device_new = ucontrol->value.integer.value[0] & + TEGRA_AUDIO_DEVICE_IN_ALL; + + if (audio_data->capture_device != capture_device_new) { + tegra_audio_route( + audio_data->play_device | capture_device_new, + audio_data->is_call_mode); + return 1; + } + return 0; + } + return -EINVAL; +} + +struct snd_kcontrol_new tegra_capture_route_control = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Pcm Capture Route", + .private_value = 0xffff, + .info = tegra_capture_route_info, + .get = tegra_capture_route_get, + .put = tegra_capture_route_put +}; + +static int tegra_call_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int tegra_call_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = TEGRA_AUDIO_DEVICE_NONE; + if (audio_data) { + ucontrol->value.integer.value[0] = audio_data->is_call_mode; + return 0; + } + return -EINVAL; +} + +static int tegra_call_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + if (audio_data) { + int is_call_mode_new = ucontrol->value.integer.value[0]; + + if (audio_data->is_call_mode != is_call_mode_new) { + tegra_audio_route( + audio_data->play_device | + audio_data->capture_device, + is_call_mode_new); + return 1; + } + return 0; + } + return -EINVAL; +} + +struct snd_kcontrol_new tegra_call_mode_control = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Call Mode Switch", + .private_value = 0xffff, + .info = tegra_call_mode_info, + .get = tegra_call_mode_get, + .put = tegra_call_mode_put +}; static int tegra_codec_init(struct snd_soc_codec *codec) { @@ -310,6 +536,25 @@ static int tegra_codec_init(struct snd_soc_codec *codec) /* Set up tegra specific audio path audio_map */ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + audio_data->codec = codec; + /* Add play route control */ + err = snd_ctl_add(codec->card, + snd_ctl_new1(&tegra_play_route_control, NULL)); + if (err < 0) + return err; + + /* Add capture route control */ + err = snd_ctl_add(codec->card, + snd_ctl_new1(&tegra_capture_route_control, NULL)); + if (err < 0) + return err; + + /* Add call mode switch control */ + err = snd_ctl_add(codec->card, + snd_ctl_new1(&tegra_call_mode_control, NULL)); + if (err < 0) + return err; + /* Default to HP output */ tegra_jack_func = TEGRA_HP; tegra_spk_func = TEGRA_SPK_ON; @@ -351,21 +596,43 @@ static struct snd_soc_device tegra_snd_devdata = { static int __init tegra_init(void) { - int ret; + int ret = 0; struct tegra_setup_data tegra_setup; tegra_snd_device = platform_device_alloc("soc-audio", -1); - if (!tegra_snd_device) - return -ENOMEM; + if (!tegra_snd_device) { + pr_err("failed to allocate soc-audio \n"); + ret = -ENOMEM; + goto fail; + } + + audio_data = kzalloc(sizeof(*audio_data), GFP_KERNEL); + if (!audio_data) { + pr_err("failed to allocate tegra_audio_data \n"); + ret = -ENOMEM; + goto fail; + } memset(&tegra_setup,0,sizeof(struct tegra_setup_data)); platform_set_drvdata(tegra_snd_device, &tegra_snd_devdata); tegra_snd_devdata.dev = &tegra_snd_device->dev; ret = platform_device_add(tegra_snd_device); if (ret) { - printk(KERN_ERR "audio device could not be added \n"); + pr_err("audio device could not be added \n"); + goto fail; + } + + return 0; + +fail: + if (audio_data) { + kfree(audio_data); + audio_data = 0; + } + + if (tegra_snd_device) { platform_device_put(tegra_snd_device); - return ret; + tegra_snd_device = 0; } return ret; @@ -373,6 +640,10 @@ static int __init tegra_init(void) static void __exit tegra_exit(void) { + if (audio_data) { + kfree(audio_data); + audio_data = 0; + } platform_device_unregister(tegra_snd_device); } diff --git a/sound/soc/tegra/tegra_soc.h b/sound/soc/tegra/tegra_soc.h index d9f5dc5acbc3..5aeedc9fd0d4 100644 --- a/sound/soc/tegra/tegra_soc.h +++ b/sound/soc/tegra/tegra_soc.h @@ -33,11 +33,13 @@ #include <linux/timer.h> #include <linux/interrupt.h> #include <linux/i2c.h> +#include <linux/tegra_audio.h> #include <mach/iomap.h> #include <mach/tegra2_i2s.h> #include <mach/irqs.h> #include <mach/pinmux.h> #include <mach/audio.h> +#include <mach/tegra_das.h> #include <mach/dma.h> #include <sound/core.h> #include <sound/pcm.h> @@ -97,4 +99,11 @@ struct tegra_runtime_data { struct i2s_runtime_data i2s_regs; }; +struct tegra_audio_data { + struct snd_soc_codec *codec; + int play_device; + int capture_device; + bool is_call_mode; +}; + #endif |