summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/include/mach/audio.h6
-rw-r--r--sound/soc/tegra/Kconfig4
-rw-r--r--sound/soc/tegra/Makefile3
-rw-r--r--sound/soc/tegra/tegra_soc.h3
-rw-r--r--sound/soc/tegra/tegra_soc_controls.c17
-rw-r--r--sound/soc/tegra/tegra_soc_wm8903.c9
-rw-r--r--sound/soc/tegra/tegra_wired_jack.c198
7 files changed, 229 insertions, 11 deletions
diff --git a/arch/arm/mach-tegra/include/mach/audio.h b/arch/arm/mach-tegra/include/mach/audio.h
index 5950ececae00..49d5f5e41503 100644
--- a/arch/arm/mach-tegra/include/mach/audio.h
+++ b/arch/arm/mach-tegra/include/mach/audio.h
@@ -54,4 +54,10 @@ struct tegra_audio_platform_data {
void *driver_data;
};
+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 */
+};
+
#endif /* __ARCH_ARM_MACH_TEGRA_AUDIO_H */
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index 4e0d9c251252..8c78ccedd6a8 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -4,6 +4,7 @@ config TEGRA_ALSA
select TEGRA_I2S
select TEGRA_IEC
select TEGRA_GENERIC_CODEC
+ select TEGRA_JACK
help
Say Y if you for ALSA SoC support
@@ -18,3 +19,6 @@ config TEGRA_IEC
config TEGRA_GENERIC_CODEC
tristate "Tegra ALSA Generic Codec support"
+
+config TEGRA_JACK
+ tristate "Tegra wired accessory jack support"
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index 303abe5a088f..0ce9b2ef5da1 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -1,9 +1,8 @@
ccflags-y += -DNV_DEBUG=0
obj-$(CONFIG_TEGRA_PCM) += tegra_pcm.o
obj-$(CONFIG_TEGRA_I2S) += tegra_i2s.o
+obj-$(CONFIG_TEGRA_JACK) += tegra_wired_jack.o
obj-$(CONFIG_TEGRA_ALSA) += tegra_soc_controls.o
obj-$(CONFIG_TEGRA_GENERIC_CODEC)+= tegra_generic_codec.o
obj-${CONFIG_SND_SOC_WM8903} += tegra_soc_wm8903.o
obj-${CONFIG_SND_SOC_WM8753} += tegra_soc_wm8753.o
-
-
diff --git a/sound/soc/tegra/tegra_soc.h b/sound/soc/tegra/tegra_soc.h
index d46fc9078c8a..7a77723c9ea9 100644
--- a/sound/soc/tegra/tegra_soc.h
+++ b/sound/soc/tegra/tegra_soc.h
@@ -102,6 +102,9 @@ struct tegra_audio_data {
int tegra_controls_init(struct snd_soc_codec *codec);
void tegra_controls_exit(void);
+int tegra_jack_init(struct snd_soc_codec *codec);
+void tegra_jack_exit(void);
+
void setup_dma_request(struct snd_pcm_substream *substream,
struct tegra_dma_req *req,
void (*dma_callback)(struct tegra_dma_req *req),
diff --git a/sound/soc/tegra/tegra_soc_controls.c b/sound/soc/tegra/tegra_soc_controls.c
index f73e80df751f..e076cf93b672 100644
--- a/sound/soc/tegra/tegra_soc_controls.c
+++ b/sound/soc/tegra/tegra_soc_controls.c
@@ -20,11 +20,8 @@
#include "tegra_soc.h"
-#include <mach/audio.h>
-static struct tegra_audio_data *audio_data;
-static int tegra_jack_func;
-static int tegra_spk_func;
+#include <mach/audio.h>
#define TEGRA_HP 0
#define TEGRA_MIC 1
@@ -34,6 +31,10 @@ static int tegra_spk_func;
#define TEGRA_SPK_ON 0
#define TEGRA_SPK_OFF 1
+static struct tegra_audio_data *audio_data;
+static int tegra_jack_func;
+static int tegra_spk_func;
+
static void tegra_ext_control(struct snd_soc_codec *codec)
{
/* set up jack connection */
@@ -427,6 +428,11 @@ int tegra_controls_init(struct snd_soc_codec *codec)
if (err < 0)
goto fail;
+ /* Add jack detection */
+ err = tegra_jack_init(codec);
+ if (err < 0)
+ goto fail;
+
/* Default to HP output */
tegra_jack_func = TEGRA_HP;
tegra_spk_func = TEGRA_SPK_ON;
@@ -436,6 +442,7 @@ int tegra_controls_init(struct snd_soc_codec *codec)
}
return 0;
+
fail:
if (audio_data) {
kfree(audio_data);
@@ -446,6 +453,8 @@ fail:
void tegra_controls_exit(void)
{
+ tegra_jack_exit();
+
if (audio_data) {
kfree(audio_data);
audio_data = 0;
diff --git a/sound/soc/tegra/tegra_soc_wm8903.c b/sound/soc/tegra/tegra_soc_wm8903.c
index fd168e221404..a409d517dd2a 100644
--- a/sound/soc/tegra/tegra_soc_wm8903.c
+++ b/sound/soc/tegra/tegra_soc_wm8903.c
@@ -55,7 +55,6 @@ extern struct snd_soc_platform tegra_soc_platform;
#define R29_DRC_1 41
#define SET_REG_VAL(r,m,l,v) (((r)&(~((m)<<(l))))|(((v)&(m))<<(l)))
-
static int tegra_hifi_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -74,7 +73,7 @@ static int tegra_hifi_hw_params(struct snd_pcm_substream *substream,
SND_SOC_DAIFMT_NB_NF | \
SND_SOC_DAIFMT_CBS_CFS);
if (err < 0) {
- printk(KERN_ERR "codec_dai fmt not set \n");
+ pr_err("codec_dai fmt not set \n");
return err;
}
@@ -83,19 +82,19 @@ static int tegra_hifi_hw_params(struct snd_pcm_substream *substream,
SND_SOC_DAIFMT_NB_NF | \
SND_SOC_DAIFMT_CBS_CFS);
if (err < 0) {
- printk(KERN_ERR "cpu_dai fmt not set \n");
+ pr_err("cpu_dai fmt not set \n");
return err;
}
err = snd_soc_dai_set_sysclk(codec_dai, 0, I2S1_CLK, SND_SOC_CLOCK_IN);
if (err < 0) {
- printk(KERN_ERR "codec_dai clock not set\n");
+ pr_err("codec_dai clock not set\n");
return err;
}
err = snd_soc_dai_set_sysclk(cpu_dai, 0, I2S1_CLK, SND_SOC_CLOCK_IN);
if (err < 0) {
- printk(KERN_ERR "cpu_dai clock not set\n");
+ pr_err("cpu_dai clock not set\n");
return err;
}
diff --git a/sound/soc/tegra/tegra_wired_jack.c b/sound/soc/tegra/tegra_wired_jack.c
new file mode 100644
index 000000000000..23eeecbb3422
--- /dev/null
+++ b/sound/soc/tegra/tegra_wired_jack.c
@@ -0,0 +1,198 @@
+/*
+ * sound/soc/tegra/tegra_wired_jack.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/types.h>
+#include <linux/switch.h>
+#include <linux/notifier.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <mach/audio.h>
+
+#include "tegra_soc.h"
+
+#define HEAD_DET_GPIO 0
+
+/* jack */
+static struct snd_soc_jack *tegra_wired_jack;
+
+static struct snd_soc_jack_pin hs_jack_pins[] = {
+ {
+ .pin = "Headset Jack",
+ .mask = SND_JACK_HEADSET,
+ },
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static struct snd_soc_jack_gpio hs_jack_gpios[] = {
+ {
+ /* gpio pin depends on board traits */
+ .name = "headphone-detect-gpio",
+ .report = SND_JACK_HEADPHONE,
+ .invert = 1,
+ .debounce_time = 200,
+ },
+};
+
+static struct switch_dev wired_switch_dev = {
+ .name = "h2w",
+};
+
+static int wired_swith_notify(struct notifier_block *self,
+ unsigned long action, void* dev)
+{
+ int state = 0;
+
+ switch (action) {
+ case SND_JACK_HEADSET:
+ state = 1;
+ break;
+ case SND_JACK_HEADPHONE:
+ state = 2;
+ break;
+ default:
+ state = 0;
+ }
+
+ switch_set_state(&wired_switch_dev, state);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block wired_switch_nb = {
+ .notifier_call = wired_swith_notify,
+};
+
+/* platform driver */
+static int tegra_wired_jack_probe(struct platform_device *pdev)
+{
+ int ret;
+ int hp_det_n;
+ struct tegra_wired_jack_conf *pdata;
+
+ pdata = (struct tegra_wired_jack_conf *)pdev->dev.platform_data;
+ if (!pdata || !pdata->hp_det_n) {
+ pr_err("Please set up gpio pins for jack.\n");
+ return -EBUSY;
+ }
+
+ hp_det_n = pdata->hp_det_n;
+ hs_jack_gpios[HEAD_DET_GPIO].gpio = hp_det_n;
+
+ ret = snd_soc_jack_add_gpios(tegra_wired_jack,
+ ARRAY_SIZE(hs_jack_gpios),
+ hs_jack_gpios);
+ if (ret) {
+ pr_err("Could NOT set up gpio pins for jack.\n");
+ snd_soc_jack_free_gpios(tegra_wired_jack,
+ ARRAY_SIZE(hs_jack_gpios),
+ hs_jack_gpios);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tegra_wired_jack_remove(struct platform_device *pdev)
+{
+ snd_soc_jack_free_gpios(tegra_wired_jack,
+ ARRAY_SIZE(hs_jack_gpios),
+ hs_jack_gpios);
+
+ return 0;
+}
+
+static struct platform_driver tegra_wired_jack_driver = {
+ .probe = tegra_wired_jack_probe,
+ .remove = __devexit_p(tegra_wired_jack_remove),
+ .driver = {
+ .name = "tegra_wired_jack",
+ .owner = THIS_MODULE,
+ },
+};
+
+
+int tegra_jack_init(struct snd_soc_codec *codec)
+{
+ int ret;
+
+ if (!codec)
+ return -1;
+
+ tegra_wired_jack = kzalloc(sizeof(*tegra_wired_jack), GFP_KERNEL);
+ if (!tegra_wired_jack) {
+ pr_err("failed to allocate tegra_wired_jack \n");
+ return -ENOMEM;
+ }
+
+ /* Add jack detection */
+ ret = snd_soc_jack_new(codec->socdev->card, "Headset Jack",
+ SND_JACK_HEADSET, tegra_wired_jack);
+ if (ret < 0)
+ goto failed;
+
+ ret = snd_soc_jack_add_pins(tegra_wired_jack,
+ ARRAY_SIZE(hs_jack_pins),
+ hs_jack_pins);
+ if (ret < 0)
+ goto failed;
+
+ /* Addd h2w swith class support */
+ ret = switch_dev_register(&wired_switch_dev);
+ if (ret < 0)
+ goto switch_dev_failed;
+
+ snd_soc_jack_notifier_register(tegra_wired_jack,
+ &wired_switch_nb);
+
+ ret = platform_driver_register(&tegra_wired_jack_driver);
+ if (ret < 0)
+ goto platform_dev_failed;
+
+ return 0;
+
+switch_dev_failed:
+ switch_dev_unregister(&wired_switch_dev);
+platform_dev_failed:
+ platform_driver_unregister(&tegra_wired_jack_driver);
+failed:
+ if (tegra_wired_jack) {
+ kfree(tegra_wired_jack);
+ tegra_wired_jack = 0;
+ }
+ return ret;
+}
+
+void tegra_jack_exit(void)
+{
+ switch_dev_unregister(&wired_switch_dev);
+ platform_driver_unregister(&tegra_wired_jack_driver);
+
+ if (tegra_wired_jack) {
+ kfree(tegra_wired_jack);
+ tegra_wired_jack = 0;
+ }
+}