summaryrefslogtreecommitdiff
path: root/sound/soc
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/tegra/tegra_soc.c281
-rw-r--r--sound/soc/tegra/tegra_soc.h9
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