[alsa-devel] [PATCH 2/2] ASoC: imx-wm8958: add headphone detected gpio support
Zidan Wang
zidan.wang at freescale.com
Tue Dec 8 10:40:14 CET 2015
Add headphone detected gpio support for sound card.
When plug in headphone Jack, it will enable headphone audio route and
disable speaker audio route.
Signed-off-by: Zidan Wang <zidan.wang at freescale.com>
---
.../devicetree/bindings/sound/imx-audio-wm8958.txt | 5 +
sound/soc/fsl/imx-wm8958.c | 121 ++++++++++++++++++++-
2 files changed, 125 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/sound/imx-audio-wm8958.txt b/Documentation/devicetree/bindings/sound/imx-audio-wm8958.txt
index 81775e4..7164ec8 100644
--- a/Documentation/devicetree/bindings/sound/imx-audio-wm8958.txt
+++ b/Documentation/devicetree/bindings/sound/imx-audio-wm8958.txt
@@ -42,6 +42,10 @@ Required properties:
(disabled) when the dai is not sending or
receiving PCM data in a frame.
+Optional properties:
+
+- hp-det-gpios : set headphone detected gpio for sound card.
+
Example:
sound {
@@ -50,6 +54,7 @@ sound {
model = "wm8960-audio";
cpu-dai = <&sai1>;
audio-codec = <&codec>;
+ hp-det-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
audio-routing =
"Headphone Jack", "HPOUT1L",
"Headphone Jack", "HPOUT1R",
diff --git a/sound/soc/fsl/imx-wm8958.c b/sound/soc/fsl/imx-wm8958.c
index 920bc9e..c3c8677 100644
--- a/sound/soc/fsl/imx-wm8958.c
+++ b/sound/soc/fsl/imx-wm8958.c
@@ -21,8 +21,11 @@
#include <sound/pcm_params.h>
#include <sound/soc-dapm.h>
#include <linux/mfd/wm8994/registers.h>
+#include <linux/gpio/machine.h>
+#include <linux/bitops.h>
#include "../fsl/fsl_sai.h"
#include "../codecs/wm8994.h"
+#include "../../../drivers/gpio/gpiolib.h"
#define DAI_NAME_SIZE 32
@@ -51,6 +54,99 @@ static const struct snd_soc_dapm_widget imx_wm8958_dapm_widgets[] = {
SND_SOC_DAPM_SPK("Ext Spk", NULL),
};
+struct gpio_data {
+ int gpio;
+ unsigned long flags;
+};
+
+struct imx_priv {
+ struct gpio_data hp_gpio;
+};
+
+static struct imx_priv card_priv;
+
+static struct snd_soc_jack imx_hp_jack;
+static struct snd_soc_jack_pin imx_hp_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+static struct snd_soc_jack_gpio imx_hp_jack_gpio = {
+ .name = "headphone detect",
+ .report = SND_JACK_HEADPHONE,
+ .debounce_time = 250,
+ .invert = 0,
+};
+
+static int hpjack_status_check(void *data)
+{
+ struct snd_soc_jack *jack = &imx_hp_jack;
+ int enable, ret;
+
+ enable = gpiod_get_value_cansleep(imx_hp_jack_gpio.desc);
+
+ if (enable) {
+ snd_soc_dapm_disable_pin(&jack->card->dapm, "Ext Spk");
+ ret = imx_hp_jack_gpio.report;
+ } else {
+ snd_soc_dapm_enable_pin(&jack->card->dapm, "Ext Spk");
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int imx_wm8958_gpio_init(struct snd_soc_card *card)
+{
+ struct imx_priv *priv = &card_priv;
+ int ret;
+
+ imx_hp_jack_gpio.gpio = priv->hp_gpio.gpio;
+ imx_hp_jack_gpio.jack_status_check = hpjack_status_check;
+
+ ret = snd_soc_card_jack_new(card, "Headphone Jack",
+ SND_JACK_HEADPHONE, &imx_hp_jack,
+ imx_hp_jack_pins, ARRAY_SIZE(imx_hp_jack_pins));
+ if (ret) {
+ dev_err(card->dev,
+ "failed to create Headphone Jack (%d)\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_jack_add_gpios(&imx_hp_jack, 1, &imx_hp_jack_gpio);
+ if (ret) {
+ dev_err(card->dev,
+ "failed to add Headphone Jack gpio (%d)\n", ret);
+ return ret;
+ }
+
+ if (priv->hp_gpio.flags & GPIO_ACTIVE_LOW)
+ set_bit(FLAG_ACTIVE_LOW, &imx_hp_jack_gpio.desc->flags);
+ else
+ clear_bit(FLAG_ACTIVE_LOW, &imx_hp_jack_gpio.desc->flags);
+
+ return 0;
+}
+
+static ssize_t show_headphone(struct device_driver *driver, char *buf)
+{
+ struct imx_priv *priv = &card_priv;
+ int enable;
+
+ /* Check if headphone is plugged in */
+ enable = gpiod_get_value_cansleep(gpio_to_desc(priv->hp_gpio.gpio));
+
+ if (enable)
+ strcpy(buf, "headphone\n");
+ else
+ strcpy(buf, "speaker\n");
+
+ return strlen(buf);
+}
+
+static DRIVER_ATTR(headphone, S_IRUGO | S_IWUSR, show_headphone, NULL);
+
static int imx_wm8958_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -87,7 +183,6 @@ static int imx_wm8958_hw_params(struct snd_pcm_substream *substream,
hifi_dai_sysclk_dir = SND_SOC_CLOCK_IN;
ret = snd_soc_dai_set_sysclk(cpu_dai, 0, 0, !hifi_dai_sysclk_dir);
-
if (ret) {
dev_err(dev, "failed to set cpu sysclk: %d\n", ret);
return ret;
@@ -282,6 +377,7 @@ static int imx_wm8958_probe(struct platform_device *pdev)
struct platform_device *cpu_pdev;
struct i2c_client *codec_dev;
struct imx_wm8958_data *data;
+ struct imx_priv *priv = &card_priv;
char tmp[8];
u32 dai_fmt;
int ret, i;
@@ -389,6 +485,21 @@ static int imx_wm8958_probe(struct platform_device *pdev)
goto fail;
}
+ priv->hp_gpio.gpio = of_get_named_gpio_flags(np, "hp-det-gpios", 0,
+ (enum of_gpio_flags *)&priv->hp_gpio.flags);
+
+ if (!gpio_is_valid(priv->hp_gpio.gpio))
+ goto out;
+
+ imx_wm8958_gpio_init(&data->card);
+
+ ret = driver_create_file(pdev->dev.driver, &driver_attr_headphone);
+ if (ret) {
+ dev_warn(&pdev->dev, "create hp attr failed (%d)\n", ret);
+ goto out;
+ }
+out:
+ ret = 0;
fail:
of_node_put(cpu_np);
of_node_put(codec_np);
@@ -396,6 +507,13 @@ fail:
return ret;
}
+static int imx_wm8958_remove(struct platform_device *pdev)
+{
+ driver_remove_file(pdev->dev.driver, &driver_attr_headphone);
+ snd_soc_jack_free_gpios(&imx_hp_jack, 1, &imx_hp_jack_gpio);
+ return 0;
+}
+
static const struct of_device_id imx_wm8958_dt_ids[] = {
{ .compatible = "fsl,imx-audio-wm8958", },
{ /* sentinel */ }
@@ -409,6 +527,7 @@ static struct platform_driver imx_wm8958_driver = {
.of_match_table = imx_wm8958_dt_ids,
},
.probe = imx_wm8958_probe,
+ .remove = imx_wm8958_remove,
};
module_platform_driver(imx_wm8958_driver);
--
1.9.1
More information about the Alsa-devel
mailing list