summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSumit Bhattacharya <sumitb@nvidia.com>2010-12-30 19:51:50 +0530
committerBharat Nihalani <bnihalani@nvidia.com>2011-01-10 05:58:51 -0800
commitef56099b6ed6d0548e7f569f42e94e09e39ef916 (patch)
tree067b9505643fff92e441f81cee1268086c096871
parentbf5a56deb588e201d95f21c00257e88ac721f90b (diff)
[tegra alsa] Add routing support
Add three alsa controls to expose selection of playback device, capture device and call mode.Alsa kernel will route pcm data to the selected output/input port. bug 771510 Change-Id: Ibdeebbd799b2ae36046a77327ec8b6a3b01553a9 Reviewed-on: http://git-master/r/14729 Tested-by: Sumit Bhattacharya <sumitb@nvidia.com> Reviewed-by: Sachin Nikam <snikam@nvidia.com> Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
-rw-r--r--include/linux/tegra_audio.h39
-rw-r--r--sound/soc/tegra/tegra_soc.c281
-rw-r--r--sound/soc/tegra/tegra_soc.h9
3 files changed, 324 insertions, 5 deletions
diff --git a/include/linux/tegra_audio.h b/include/linux/tegra_audio.h
index db4661aacb4f..215cfef62f7d 100644
--- a/include/linux/tegra_audio.h
+++ b/include/linux/tegra_audio.h
@@ -31,6 +31,45 @@ struct tegra_audio_in_config {
int stereo;
};
+enum tegra_audio_device {
+ TEGRA_AUDIO_DEVICE_NONE = 0x00000000,
+ TEGRA_AUDIO_DEVICE_OUT_HEADPHONE = 0x00000001,
+ TEGRA_AUDIO_DEVICE_OUT_HEADSET = 0x00000002,
+ TEGRA_AUDIO_DEVICE_OUT_SPEAKER = 0x00000004,
+ TEGRA_AUDIO_DEVICE_OUT_EAR_SPEAKER = 0x00000008,
+ TEGRA_AUDIO_DEVICE_OUT_LINE = 0x00000010,
+ TEGRA_AUDIO_DEVICE_OUT_BT_SCO = 0x00000020,
+ TEGRA_AUDIO_DEVICE_OUT_AUX_DIGITAL = 0x00000040,
+ TEGRA_AUDIO_DEVICE_OUT_RADIO = 0x00000080,
+ TEGRA_AUDIO_DEVICE_OUT_ALL = TEGRA_AUDIO_DEVICE_OUT_HEADPHONE |
+ TEGRA_AUDIO_DEVICE_OUT_HEADSET |
+ TEGRA_AUDIO_DEVICE_OUT_SPEAKER |
+ TEGRA_AUDIO_DEVICE_OUT_EAR_SPEAKER |
+ TEGRA_AUDIO_DEVICE_OUT_LINE |
+ TEGRA_AUDIO_DEVICE_OUT_BT_SCO |
+ TEGRA_AUDIO_DEVICE_OUT_AUX_DIGITAL |
+ TEGRA_AUDIO_DEVICE_OUT_RADIO,
+ TEGRA_AUDIO_DEVICE_IN_BUILTIN_MIC = 0x00010000,
+ TEGRA_AUDIO_DEVICE_IN_MIC = 0x00020000,
+ TEGRA_AUDIO_DEVICE_IN_HEADSET = 0x00040000,
+ TEGRA_AUDIO_DEVICE_IN_BACK_MIC = 0x00080000,
+ TEGRA_AUDIO_DEVICE_IN_LINE = 0x00100000,
+ TEGRA_AUDIO_DEVICE_IN_BT_SCO = 0x00200000,
+ TEGRA_AUDIO_DEVICE_IN_AUX_DIGITAL = 0x00400000,
+ TEGRA_AUDIO_DEVICE_IN_PHONE = 0x00800000,
+ TEGRA_AUDIO_DEVICE_IN_RADIO = 0x01000000,
+ TEGRA_AUDIO_DEVICE_IN_ALL = TEGRA_AUDIO_DEVICE_IN_BUILTIN_MIC |
+ TEGRA_AUDIO_DEVICE_IN_MIC |
+ TEGRA_AUDIO_DEVICE_IN_HEADSET |
+ TEGRA_AUDIO_DEVICE_IN_BACK_MIC |
+ TEGRA_AUDIO_DEVICE_IN_LINE |
+ TEGRA_AUDIO_DEVICE_IN_BT_SCO |
+ TEGRA_AUDIO_DEVICE_IN_AUX_DIGITAL |
+ TEGRA_AUDIO_DEVICE_IN_PHONE |
+ TEGRA_AUDIO_DEVICE_IN_RADIO,
+ TEGRA_AUDIO_DEVICE_MAX = 0x7FFFFFFF
+};
+
#define TEGRA_AUDIO_IN_SET_CONFIG _IOW(TEGRA_AUDIO_MAGIC, 2, \
const struct tegra_audio_in_config *)
#define TEGRA_AUDIO_IN_GET_CONFIG _IOR(TEGRA_AUDIO_MAGIC, 3, \
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