From c339d87e3828c751edbc053907d3ce4df89c24a1 Mon Sep 17 00:00:00 2001 From: Chao Jiang Date: Tue, 8 Mar 2011 11:27:10 +0900 Subject: [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 Tested-by: Chao Jiang Reviewed-by: Scott Peterson --- sound/soc/tegra/tegra_soc.h | 7 +++ sound/soc/tegra/tegra_soc_controls.c | 94 ++++++++++++++++++++++++++++++------ sound/soc/tegra/tegra_wired_jack.c | 21 +++++++- 3 files changed, 106 insertions(+), 16 deletions(-) (limited to 'sound') 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 +#include +#include #include "tegra_soc.h" -#include - #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 +#include #include #include #include @@ -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; } -- cgit v1.2.3