[tegra ALSA] Support built-in speaker
authorChao Jiang <chaoj@nvidia.com>
Tue, 8 Mar 2011 02:27:10 +0000 (11:27 +0900)
committerVarun Colbert <vcolbert@nvidia.com>
Wed, 9 Mar 2011 04:57:28 +0000 (20:57 -0800)
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>
arch/arm/mach-tegra/include/mach/audio.h
sound/soc/tegra/tegra_soc.h
sound/soc/tegra/tegra_soc_controls.c
sound/soc/tegra/tegra_wired_jack.c

index 49d5f5e4150328e88f13dc893d1f30010f2b0042..5e7f48e2ed18d1ae9c238acffcbd72b439df2839 100644 (file)
@@ -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 */
index 215b4c580edb27d2b3ca80493bd657f1fbb68fd5..ea3bf4a786d118aeb801a17bd4be9bc7d4b6352b 100644 (file)
@@ -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);
 
index e076cf93b6729a3fb285d65d96a2eb4f00d4c45f..4371130e1081bd1c17a044c16c8acd4d9adf08c6 100644 (file)
  * 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);
index 7967caefce336f3c53a16cc73abd3a9c6d453ecf..10fb237ce64a7f6b0bd5061c616c1a76b61209a4 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 #include <linux/types.h>
+#include <linux/gpio.h>
 #include <linux/switch.h>
 #include <linux/notifier.h>
 #include <sound/jack.h>
 
 #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;
 }