ASoC: codecs: wcd938x: add mux control support for hp audio mux
authorSrinivas Kandagatla <srinivas.kandagatla@linaro.org>
Thu, 27 Mar 2025 10:06:32 +0000 (10:06 +0000)
committerMark Brown <broonie@kernel.org>
Sun, 6 Apr 2025 22:24:47 +0000 (23:24 +0100)
On some platforms to minimise pop and click during switching between
CTIA and OMTP headset an additional HiFi mux is used. Most common
case is that this switch is switched on by default, but on some
platforms this needs a regulator enable.

move to using mux control to enable both regulator and handle gpios,
deprecate the usage of gpio.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Tested-by: Christopher Obbard <christopher.obbard@linaro.org>
Tested-by: Johan Hovold <johan+linaro@kernel.org>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Link: https://patch.msgid.link/20250327100633.11530-6-srinivas.kandagatla@linaro.org
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/Kconfig
sound/soc/codecs/wcd938x.c

index 40bb7a1d44bcfa4c386f2eba0f475efeeedf1bdb..870eb90116f1fe64a09900dd1670ef4286b7f99b 100644 (file)
@@ -2239,6 +2239,7 @@ config SND_SOC_WCD938X
        tristate
        depends on SOUNDWIRE || !SOUNDWIRE
        select SND_SOC_WCD_CLASSH
+       select MULTIPLEXER
 
 config SND_SOC_WCD938X_SDW
        tristate "WCD9380/WCD9385 Codec - SDW"
index d03ed9cfa5306bfc331d918e5e479025fe8caab0..585a92772c2acbdfe8c7bc390418b4f2db5d7850 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/regmap.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
+#include <linux/mux/consumer.h>
 #include <linux/regulator/consumer.h>
 
 #include "wcd-clsh-v2.h"
@@ -173,6 +174,8 @@ struct wcd938x_priv {
        int variant;
        int reset_gpio;
        struct gpio_desc *us_euro_gpio;
+       struct mux_control *us_euro_mux;
+       unsigned int mux_state;
        u32 micb1_mv;
        u32 micb2_mv;
        u32 micb3_mv;
@@ -183,6 +186,7 @@ struct wcd938x_priv {
        bool comp1_enable;
        bool comp2_enable;
        bool ldoh;
+       bool mux_setup_done;
 };
 
 static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800);
@@ -3232,15 +3236,26 @@ static void wcd938x_dt_parse_micbias_info(struct device *dev, struct wcd938x_pri
 
 static bool wcd938x_swap_gnd_mic(struct snd_soc_component *component)
 {
-       int value;
-
-       struct wcd938x_priv *wcd938x;
+       struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+       struct device *dev = component->dev;
+       int ret;
 
-       wcd938x = snd_soc_component_get_drvdata(component);
+       if (wcd938x->us_euro_mux) {
+               if (wcd938x->mux_setup_done)
+                       mux_control_deselect(wcd938x->us_euro_mux);
 
-       value = gpiod_get_value(wcd938x->us_euro_gpio);
+               ret = mux_control_try_select(wcd938x->us_euro_mux, !wcd938x->mux_state);
+               if (ret) {
+                       dev_err(dev, "Error (%d) Unable to select us/euro mux state\n", ret);
+                       wcd938x->mux_setup_done = false;
+                       return false;
+               }
+               wcd938x->mux_setup_done = true;
+       } else {
+               gpiod_set_value(wcd938x->us_euro_gpio, !wcd938x->mux_state);
+       }
 
-       gpiod_set_value(wcd938x->us_euro_gpio, !value);
+       wcd938x->mux_state = !wcd938x->mux_state;
 
        return true;
 }
@@ -3256,11 +3271,26 @@ static int wcd938x_populate_dt_data(struct wcd938x_priv *wcd938x, struct device
                return dev_err_probe(dev, wcd938x->reset_gpio,
                                     "Failed to get reset gpio\n");
 
-       wcd938x->us_euro_gpio = devm_gpiod_get_optional(dev, "us-euro",
-                                               GPIOD_OUT_LOW);
-       if (IS_ERR(wcd938x->us_euro_gpio))
-               return dev_err_probe(dev, PTR_ERR(wcd938x->us_euro_gpio),
-                                    "us-euro swap Control GPIO not found\n");
+       wcd938x->us_euro_mux = devm_mux_control_get(dev, NULL);
+       if (IS_ERR(wcd938x->us_euro_mux)) {
+               if (PTR_ERR(wcd938x->us_euro_mux) == -EPROBE_DEFER)
+                       return -EPROBE_DEFER;
+
+               /* mux is optional and now fallback to using gpio */
+               wcd938x->us_euro_mux = NULL;
+               wcd938x->us_euro_gpio = devm_gpiod_get_optional(dev, "us-euro", GPIOD_OUT_LOW);
+               if (IS_ERR(wcd938x->us_euro_gpio))
+                       return dev_err_probe(dev, PTR_ERR(wcd938x->us_euro_gpio),
+                                            "us-euro swap Control GPIO not found\n");
+       } else {
+               ret = mux_control_try_select(wcd938x->us_euro_mux, wcd938x->mux_state);
+               if (ret) {
+                       dev_err(dev, "Error (%d) Unable to select us/euro mux state\n", ret);
+                       wcd938x->mux_setup_done = false;
+                       return ret;
+               }
+               wcd938x->mux_setup_done = true;
+       }
 
        cfg->swap_gnd_mic = wcd938x_swap_gnd_mic;
 
@@ -3576,6 +3606,9 @@ static void wcd938x_remove(struct platform_device *pdev)
        pm_runtime_set_suspended(dev);
        pm_runtime_dont_use_autosuspend(dev);
 
+       if (wcd938x->us_euro_mux && wcd938x->mux_setup_done)
+               mux_control_deselect(wcd938x->us_euro_mux);
+
        regulator_bulk_disable(WCD938X_MAX_SUPPLY, wcd938x->supplies);
        regulator_bulk_free(WCD938X_MAX_SUPPLY, wcd938x->supplies);
 }