summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChao Jiang <chaoj@nvidia.com>2011-03-08 11:27:10 +0900
committerVarun Colbert <vcolbert@nvidia.com>2011-03-08 20:57:28 -0800
commitc339d87e3828c751edbc053907d3ce4df89c24a1 (patch)
treef88f03483f2f5657689251bf1b649672b7509afe
parent946380dfcffa007b7dcb7c976642108c483f4742 (diff)
[tegra ALSA] Support built-in speaker
Added support for built-in speaker. Although audio subsystem on different boards have various topology, Tegra boards usually have amplifier connected to codec. The patch added a control interface to expose speaker control widget to user space. Amplifier could be driven automatically if internal speaker turned on. fixes bug 766757 Change-Id: Ic6b8d0c58830a71ff8d6c09d1268cbd97982d08b Reviewed-on: http://git-master/r/21985 Reviewed-by: Chao Jiang <chaoj@nvidia.com> Tested-by: Chao Jiang <chaoj@nvidia.com> Reviewed-by: Scott Peterson <speterson@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/include/mach/audio.h1
-rw-r--r--sound/soc/tegra/tegra_soc.h7
-rw-r--r--sound/soc/tegra/tegra_soc_controls.c94
-rw-r--r--sound/soc/tegra/tegra_wired_jack.c21
4 files changed, 107 insertions, 16 deletions
diff --git a/arch/arm/mach-tegra/include/mach/audio.h b/arch/arm/mach-tegra/include/mach/audio.h
index 49d5f5e41503..5e7f48e2ed18 100644
--- a/arch/arm/mach-tegra/include/mach/audio.h
+++ b/arch/arm/mach-tegra/include/mach/audio.h
@@ -58,6 +58,7 @@ struct tegra_wired_jack_conf {
int hp_det_n; /* headphone jack detection gpio pin */
int en_mic_ext; /* external mic enable gpio pin */
int en_mic_int; /* internal mic enable gpio pin */
+ int en_spkr; /* gpio pin to drive amplifier */
};
#endif /* __ARCH_ARM_MACH_TEGRA_AUDIO_H */
diff --git a/sound/soc/tegra/tegra_soc.h b/sound/soc/tegra/tegra_soc.h
index 215b4c580edb..ea3bf4a786d1 100644
--- a/sound/soc/tegra/tegra_soc.h
+++ b/sound/soc/tegra/tegra_soc.h
@@ -104,6 +104,13 @@ struct tegra_audio_data {
bool is_call_mode;
};
+struct wired_jack_conf {
+ int hp_det_n;
+ int en_mic_int;
+ int en_mic_ext;
+ int en_spkr;
+};
+
int tegra_controls_init(struct snd_soc_codec *codec);
void tegra_controls_exit(void);
diff --git a/sound/soc/tegra/tegra_soc_controls.c b/sound/soc/tegra/tegra_soc_controls.c
index e076cf93b672..4371130e1081 100644
--- a/sound/soc/tegra/tegra_soc_controls.c
+++ b/sound/soc/tegra/tegra_soc_controls.c
@@ -18,21 +18,30 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include <linux/gpio.h>
+#include <sound/soc-dapm.h>
+#include <mach/audio.h>
#include "tegra_soc.h"
-#include <mach/audio.h>
-
#define TEGRA_HP 0
#define TEGRA_MIC 1
#define TEGRA_LINE 2
#define TEGRA_HEADSET 3
#define TEGRA_HP_OFF 4
-#define TEGRA_SPK_ON 0
-#define TEGRA_SPK_OFF 1
+
+#define TEGRA_LINEOUT_ON 0
+#define TEGRA_LINEOUT_OFF 1
+
+#define TEGRA_INT_SPK_ON 0
+#define TEGRA_INT_SPK_OFF 1
+
+extern struct wired_jack_conf tegra_wired_jack_conf;
static struct tegra_audio_data *audio_data;
+
static int tegra_jack_func;
+static int tegra_lineout_func;
static int tegra_spk_func;
static void tegra_ext_control(struct snd_soc_codec *codec)
@@ -67,11 +76,19 @@ static void tegra_ext_control(struct snd_soc_codec *codec)
break;
}
- if (tegra_spk_func == TEGRA_SPK_ON) {
- snd_soc_dapm_enable_pin(codec, "Ext Spk");
+
+ if (tegra_lineout_func == TEGRA_LINEOUT_ON) {
+ snd_soc_dapm_enable_pin(codec, "Lineout");
+ } else {
+ snd_soc_dapm_disable_pin(codec, "Lineout");
+ }
+
+ if (tegra_spk_func == TEGRA_INT_SPK_ON) {
+ snd_soc_dapm_enable_pin(codec, "Int Spk");
} else {
- snd_soc_dapm_disable_pin(codec, "Ext Spk");
+ snd_soc_dapm_disable_pin(codec, "Int Spk");
}
+
/* signal a DAPM event */
snd_soc_dapm_sync(codec);
}
@@ -96,6 +113,26 @@ static int tegra_set_jack(struct snd_kcontrol *kcontrol,
return 1;
}
+static int tegra_get_lineout(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = tegra_lineout_func;
+ return 0;
+}
+
+static int tegra_set_lineout(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (tegra_lineout_func == ucontrol->value.integer.value[0])
+ return 0;
+
+ tegra_lineout_func = ucontrol->value.integer.value[0];
+ tegra_ext_control(codec);
+ return 1;
+}
+
static int tegra_get_spk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -117,11 +154,22 @@ static int tegra_set_spk(struct snd_kcontrol *kcontrol,
return 1;
}
+static int tegra_dapm_event_int_spk(struct snd_soc_dapm_widget* w,
+ struct snd_kcontrol* k, int event)
+{
+ if (tegra_wired_jack_conf.en_spkr != -1)
+ gpio_set_value_cansleep(tegra_wired_jack_conf.en_spkr,
+ SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
/*tegra machine dapm widgets */
static const struct snd_soc_dapm_widget tegra_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Mic Jack", NULL),
- SND_SOC_DAPM_SPK("Ext Spk", NULL),
+ SND_SOC_DAPM_SPK("Lineout", NULL),
+ SND_SOC_DAPM_SPK("Int Spk", tegra_dapm_event_int_spk),
SND_SOC_DAPM_LINE("Line Jack", NULL),
SND_SOC_DAPM_HP("Headset Jack", NULL),
};
@@ -135,8 +183,15 @@ static const struct snd_soc_dapm_route audio_map[] = {
/* headphone connected to LHPOUT1, RHPOUT1 */
{"Headphone Jack", NULL, "HPOUTR"}, {"Headphone Jack", NULL, "HPOUTL"},
- /* speaker connected to LOUT, ROUT */
- {"Ext Spk", NULL, "LINEOUTR"}, {"Ext Spk", NULL, "LINEOUTL"},
+ /* build-in speaker connected to LON/P RON/P */
+ {"Int Spk", NULL, "RON"},
+ {"Int Spk", NULL, "ROP"},
+ {"Int Spk", NULL, "LON"},
+ {"Int Spk", NULL, "LOP"},
+
+ /* lineout connected to LINEOUTR and LINEOUTL */
+ {"Lineout", NULL, "LINEOUTR"},
+ {"Lineout", NULL, "LINEOUTL"},
/* mic is connected to MICIN (via right channel of headphone jack) */
{"IN1L", NULL, "Mic Jack"},
@@ -148,16 +203,21 @@ static const struct snd_soc_dapm_route audio_map[] = {
static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
"Off"
};
+static const char *lineout_function[] = {"On", "Off"};
static const char *spk_function[] = {"On", "Off"};
+
static const struct soc_enum tegra_enum[] = {
SOC_ENUM_SINGLE_EXT(5, jack_function),
+ SOC_ENUM_SINGLE_EXT(2, lineout_function),
SOC_ENUM_SINGLE_EXT(2, spk_function),
};
static const struct snd_kcontrol_new tegra_controls[] = {
SOC_ENUM_EXT("Jack Function", tegra_enum[0], tegra_get_jack,
tegra_set_jack),
- SOC_ENUM_EXT("Speaker Function", tegra_enum[1], tegra_get_spk,
+ SOC_ENUM_EXT("Lineout Function", tegra_enum[1], tegra_get_lineout,
+ tegra_set_lineout),
+ SOC_ENUM_EXT("Speaker Function", tegra_enum[2], tegra_get_spk,
tegra_set_spk),
};
@@ -184,13 +244,16 @@ static void tegra_audio_route(int device_new, int is_call_mode_new)
}
if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_SPEAKER) {
- tegra_spk_func = TEGRA_SPK_ON;
+ tegra_lineout_func = TEGRA_LINEOUT_OFF;
+ tegra_spk_func = TEGRA_INT_SPK_ON;
}
else if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_EAR_SPEAKER) {
- tegra_spk_func = TEGRA_SPK_ON;
+ tegra_spk_func = TEGRA_INT_SPK_OFF;
+ tegra_lineout_func = TEGRA_LINEOUT_ON;
}
else {
- tegra_spk_func = TEGRA_SPK_OFF;
+ tegra_lineout_func = TEGRA_LINEOUT_OFF;
+ tegra_spk_func = TEGRA_INT_SPK_OFF;
}
tegra_ext_control(audio_data->codec);
audio_data->play_device = play_device_new;
@@ -435,7 +498,8 @@ int tegra_controls_init(struct snd_soc_codec *codec)
/* Default to HP output */
tegra_jack_func = TEGRA_HP;
- tegra_spk_func = TEGRA_SPK_ON;
+ tegra_lineout_func = TEGRA_LINEOUT_ON;
+ tegra_spk_func = TEGRA_INT_SPK_ON;
tegra_ext_control(codec);
snd_soc_dapm_sync(codec);
diff --git a/sound/soc/tegra/tegra_wired_jack.c b/sound/soc/tegra/tegra_wired_jack.c
index 7967caefce33..10fb237ce64a 100644
--- a/sound/soc/tegra/tegra_wired_jack.c
+++ b/sound/soc/tegra/tegra_wired_jack.c
@@ -19,6 +19,7 @@
*/
#include <linux/types.h>
+#include <linux/gpio.h>
#include <linux/switch.h>
#include <linux/notifier.h>
#include <sound/jack.h>
@@ -29,6 +30,10 @@
#define HEAD_DET_GPIO 0
+struct wired_jack_conf tegra_wired_jack_conf = {
+ -1, -1, -1, -1
+};
+
/* jack */
static struct snd_soc_jack *tegra_wired_jack;
@@ -97,10 +102,11 @@ static int tegra_wired_jack_probe(struct platform_device *pdev)
{
int ret;
int hp_det_n;
+ int en_spkr;
struct tegra_wired_jack_conf *pdata;
pdata = (struct tegra_wired_jack_conf *)pdev->dev.platform_data;
- if (!pdata || !pdata->hp_det_n) {
+ if (!pdata || !pdata->hp_det_n || !pdata->en_spkr) {
pr_err("Please set up gpio pins for jack.\n");
return -EBUSY;
}
@@ -119,6 +125,19 @@ static int tegra_wired_jack_probe(struct platform_device *pdev)
return ret;
}
+ en_spkr = pdata->en_spkr;
+ ret = gpio_request(en_spkr, "en_spkr");
+ if (ret) {
+ pr_err("Could NOT set up gpio pin for amplifier.\n");
+ gpio_free(en_spkr);
+ }
+ gpio_direction_output(en_spkr, 0);
+ gpio_export(en_spkr, false);
+
+ /* restore configuration of these pins */
+ tegra_wired_jack_conf.hp_det_n = hp_det_n;
+ tegra_wired_jack_conf.en_spkr = en_spkr;
+
return 0;
}