From ed1166865825a993c16a7dcf3f59b126c863586a Mon Sep 17 00:00:00 2001 From: Chao Jiang Date: Wed, 9 Feb 2011 17:57:09 +0900 Subject: [tegra ALSA] Added headphone jack detection Headphone jack detection is enabled with this patch. The jack will be reported via /sys/class/switch interface to user space. fixes bug 766757 Change-Id: I58908e7de1025b17cdf37079d5650aa5f503dcdd Reviewed-on: http://git-master/r/18707 Reviewed-by: Sachin Nikam Reviewed-by: Chao Jiang Tested-by: Chao Jiang Reviewed-by: Scott Peterson Reviewed-by: Bharat Nihalani --- sound/soc/tegra/Kconfig | 4 + sound/soc/tegra/Makefile | 3 +- sound/soc/tegra/tegra_soc.h | 3 + sound/soc/tegra/tegra_soc_controls.c | 17 ++- sound/soc/tegra/tegra_soc_wm8903.c | 9 +- sound/soc/tegra/tegra_wired_jack.c | 198 +++++++++++++++++++++++++++++++++++ 6 files changed, 223 insertions(+), 11 deletions(-) create mode 100644 sound/soc/tegra/tegra_wired_jack.c (limited to 'sound') 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 -static struct tegra_audio_data *audio_data; -static int tegra_jack_func; -static int tegra_spk_func; +#include #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 +#include +#include +#include +#include +#include + +#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; + } +} -- cgit v1.2.3