/* * tegra_soc_controls.c -- alsa controls for tegra SoC * * Copyright (c) 2010-2011, NVIDIA Corporation. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "tegra_soc.h" int tegra_i2sloopback_func = TEGRA_INT_I2SLOOPBACK_OFF; static void tegra_audio_route(struct tegra_audio_data* audio_data, 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 codec_con = audio_data->codec_con; int is_bt_sco_mode = (play_device_new & TEGRA_AUDIO_DEVICE_OUT_BT_SCO) || (capture_device_new & TEGRA_AUDIO_DEVICE_IN_BT_SCO); int was_bt_sco_mode = (audio_data->play_device & TEGRA_AUDIO_DEVICE_OUT_BT_SCO) || (audio_data->capture_device & TEGRA_AUDIO_DEVICE_IN_BT_SCO); if (play_device_new != audio_data->play_device) { codec_con &= ~(TEGRA_HEADPHONE | TEGRA_LINEOUT | TEGRA_SPK | TEGRA_EAR_SPK | TEGRA_HEADSET); if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_HEADPHONE) codec_con |= TEGRA_HEADPHONE; if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_LINE) codec_con |= TEGRA_LINEOUT; if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_SPEAKER) codec_con |= TEGRA_SPK; if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_EAR_SPEAKER) codec_con |= TEGRA_EAR_SPK; if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_HEADSET) codec_con |= TEGRA_HEADSET; tegra_ext_control(audio_data->codec, codec_con); audio_data->play_device = play_device_new; } if (capture_device_new != audio_data->capture_device) { codec_con &= ~(TEGRA_INT_MIC | TEGRA_EXT_MIC | TEGRA_LINEIN | TEGRA_HEADSET); if (capture_device_new & (TEGRA_AUDIO_DEVICE_IN_BUILTIN_MIC | TEGRA_AUDIO_DEVICE_IN_BACK_MIC)) codec_con |= TEGRA_INT_MIC; if (capture_device_new & TEGRA_AUDIO_DEVICE_IN_MIC) codec_con |= TEGRA_EXT_MIC; if (capture_device_new & TEGRA_AUDIO_DEVICE_IN_LINE) codec_con |= TEGRA_LINEIN; if (capture_device_new & TEGRA_AUDIO_DEVICE_IN_HEADSET) codec_con |= TEGRA_HEADSET; tegra_ext_control(audio_data->codec, codec_con); 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) { struct tegra_audio_data* audio_data = snd_kcontrol_chip(kcontrol); 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) { struct tegra_audio_data* audio_data = snd_kcontrol_chip(kcontrol); 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(audio_data, 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) { struct tegra_audio_data* audio_data = snd_kcontrol_chip(kcontrol); 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) { struct tegra_audio_data* audio_data = snd_kcontrol_chip(kcontrol); 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, 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) { struct tegra_audio_data* audio_data = snd_kcontrol_chip(kcontrol); 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) { struct tegra_audio_data* audio_data = snd_kcontrol_chip(kcontrol); 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, 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_i2s_loopback_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_i2s_loopback_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { ucontrol->value.integer.value[0] = tegra_i2sloopback_func; return 0; } static int tegra_i2s_loopback_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { if (tegra_i2sloopback_func == ucontrol->value.integer.value[0]) return 0; tegra_i2sloopback_func = ucontrol->value.integer.value[0]; return 1; } struct snd_kcontrol_new tegra_i2s_loopback_control = { .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "I2S loopback", .private_value = 0xffff, .info = tegra_i2s_loopback_info, .get = tegra_i2s_loopback_get, .put = tegra_i2s_loopback_put }; int tegra_controls_init(struct snd_soc_codec *codec) { struct tegra_audio_data* audio_data = codec->socdev->codec_data; int err; /* Add play route control */ err = snd_ctl_add(codec->card, snd_ctl_new1(&tegra_play_route_control, audio_data)); if (err < 0) return err; /* Add capture route control */ err = snd_ctl_add(codec->card, snd_ctl_new1(&tegra_capture_route_control, audio_data)); if (err < 0) return err; /* Add call mode switch control */ err = snd_ctl_add(codec->card, snd_ctl_new1(&tegra_call_mode_control, audio_data)); if (err < 0) return err; /* Add I2S loopback control */ err = snd_ctl_add(codec->card, snd_ctl_new1(&tegra_i2s_loopback_control, audio_data)); if (err < 0) return err; return 0; }