ASoC: rt5651: Add support for jack detect using an external GPIO
authorHans de Goede <hdegoede@redhat.com>
Sat, 29 Dec 2018 23:00:21 +0000 (00:00 +0100)
committerMark Brown <broonie@kernel.org>
Mon, 7 Jan 2019 19:23:38 +0000 (19:23 +0000)
Some board designs hook the jack-detect up to an external GPIO,
rather then to one of the codec pins, add support for this.

Figuring out which GPIO to use is pretty much board specific so I've
chosen to let the machine driver pass the gpio_desc as data argument to
snd_soc_component_set_jack() rather then add support for getting the
GPIO to the codec driver. This keeps the codec code nice and clean.

Note that using an external GPIO for this conflicts with button-press
support, so this commit disables button-press support when an
external GPIO is used.

Acked-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/rt5651.c
sound/soc/codecs/rt5651.h

index 9a007c1..7599429 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
+#include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
 #include <linux/regmap.h>
 #include <linux/platform_device.h>
@@ -1621,6 +1622,12 @@ static bool rt5651_jack_inserted(struct snd_soc_component *component)
        struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
        int val;
 
+       if (rt5651->gpiod_hp_det) {
+               val = gpiod_get_value_cansleep(rt5651->gpiod_hp_det);
+               dev_dbg(component->dev, "jack-detect gpio %d\n", val);
+               return val;
+       }
+
        val = snd_soc_component_read32(component, RT5651_INT_IRQ_ST);
        dev_dbg(component->dev, "irq status %#04x\n", val);
 
@@ -1761,6 +1768,13 @@ static int rt5651_detect_headset(struct snd_soc_component *component)
        return SND_JACK_HEADPHONE;
 }
 
+static bool rt5651_support_button_press(struct rt5651_priv *rt5651)
+{
+       /* Button press support only works with internal jack-detection */
+       return (rt5651->hp_jack->status & SND_JACK_MICROPHONE) &&
+               rt5651->gpiod_hp_det == NULL;
+}
+
 static void rt5651_jack_detect_work(struct work_struct *work)
 {
        struct rt5651_priv *rt5651 =
@@ -1785,15 +1799,15 @@ static void rt5651_jack_detect_work(struct work_struct *work)
                WARN_ON(rt5651->ovcd_irq_enabled);
                rt5651_enable_micbias1_for_ovcd(component);
                report = rt5651_detect_headset(component);
-               if (report == SND_JACK_HEADSET) {
+               dev_dbg(component->dev, "detect report %#02x\n", report);
+               snd_soc_jack_report(rt5651->hp_jack, report, SND_JACK_HEADSET);
+               if (rt5651_support_button_press(rt5651)) {
                        /* Enable ovcd IRQ for button press detect. */
                        rt5651_enable_micbias1_ovcd_irq(component);
                } else {
                        /* No more need for overcurrent detect. */
                        rt5651_disable_micbias1_for_ovcd(component);
                }
-               dev_dbg(component->dev, "detect report %#02x\n", report);
-               snd_soc_jack_report(rt5651->hp_jack, report, SND_JACK_HEADSET);
        } else if (rt5651->ovcd_irq_enabled && rt5651_micbias1_ovcd(component)) {
                dev_dbg(component->dev, "OVCD IRQ\n");
 
@@ -1837,16 +1851,20 @@ static void rt5651_cancel_work(void *data)
 }
 
 static void rt5651_enable_jack_detect(struct snd_soc_component *component,
-                                     struct snd_soc_jack *hp_jack)
+                                     struct snd_soc_jack *hp_jack,
+                                     struct gpio_desc *gpiod_hp_det)
 {
        struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
-
-       /* IRQ output on GPIO1 */
-       snd_soc_component_update_bits(component, RT5651_GPIO_CTRL1,
-               RT5651_GP1_PIN_MASK, RT5651_GP1_PIN_IRQ);
+       bool using_internal_jack_detect = true;
 
        /* Select jack detect source */
        switch (rt5651->jd_src) {
+       case RT5651_JD_NULL:
+               rt5651->gpiod_hp_det = gpiod_hp_det;
+               if (!rt5651->gpiod_hp_det)
+                       return; /* No jack detect */
+               using_internal_jack_detect = false;
+               break;
        case RT5651_JD1_1:
                snd_soc_component_update_bits(component, RT5651_JD_CTRL2,
                        RT5651_JD_TRG_SEL_MASK, RT5651_JD_TRG_SEL_JD1_1);
@@ -1865,16 +1883,20 @@ static void rt5651_enable_jack_detect(struct snd_soc_component *component,
                snd_soc_component_update_bits(component, RT5651_IRQ_CTRL1,
                        RT5651_JD2_IRQ_EN, RT5651_JD2_IRQ_EN);
                break;
-       case RT5651_JD_NULL:
-               return;
        default:
                dev_err(component->dev, "Currently only JD1_1 / JD1_2 / JD2 are supported\n");
                return;
        }
 
-       /* Enable jack detect power */
-       snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
-               RT5651_PWR_JD_M, RT5651_PWR_JD_M);
+       if (using_internal_jack_detect) {
+               /* IRQ output on GPIO1 */
+               snd_soc_component_update_bits(component, RT5651_GPIO_CTRL1,
+                       RT5651_GP1_PIN_MASK, RT5651_GP1_PIN_IRQ);
+
+               /* Enable jack detect power */
+               snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
+                       RT5651_PWR_JD_M, RT5651_PWR_JD_M);
+       }
 
        /* Set OVCD threshold current and scale-factor */
        snd_soc_component_write(component, RT5651_PR_BASE + RT5651_BIAS_CUR4,
@@ -1903,7 +1925,7 @@ static void rt5651_enable_jack_detect(struct snd_soc_component *component,
                RT5651_MB1_OC_STKY_MASK, RT5651_MB1_OC_STKY_EN);
 
        rt5651->hp_jack = hp_jack;
-       if (rt5651->hp_jack->status & SND_JACK_MICROPHONE) {
+       if (rt5651_support_button_press(rt5651)) {
                rt5651_enable_micbias1_for_ovcd(component);
                rt5651_enable_micbias1_ovcd_irq(component);
        }
@@ -1920,7 +1942,7 @@ static void rt5651_disable_jack_detect(struct snd_soc_component *component)
        disable_irq(rt5651->irq);
        rt5651_cancel_work(rt5651);
 
-       if (rt5651->hp_jack->status & SND_JACK_MICROPHONE) {
+       if (rt5651_support_button_press(rt5651)) {
                rt5651_disable_micbias1_ovcd_irq(component);
                rt5651_disable_micbias1_for_ovcd(component);
                snd_soc_jack_report(rt5651->hp_jack, 0, SND_JACK_BTN_0);
@@ -1933,7 +1955,7 @@ static int rt5651_set_jack(struct snd_soc_component *component,
                           struct snd_soc_jack *jack, void *data)
 {
        if (jack)
-               rt5651_enable_jack_detect(component, jack);
+               rt5651_enable_jack_detect(component, jack, data);
        else
                rt5651_disable_jack_detect(component);
 
index ac6de6f..41fcb8b 100644 (file)
@@ -2073,6 +2073,7 @@ struct rt5651_priv {
        struct regmap *regmap;
        /* Jack and button detect data */
        struct snd_soc_jack *hp_jack;
+       struct gpio_desc *gpiod_hp_det;
        struct work_struct jack_detect_work;
        struct delayed_work bp_work;
        bool ovcd_irq_enabled;