ASoC: tlv320aic3x: switch to using gpiod API
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Wed, 2 Nov 2022 23:20:04 +0000 (16:20 -0700)
committerMark Brown <broonie@kernel.org>
Thu, 3 Nov 2022 13:26:13 +0000 (13:26 +0000)
Switch the driver from legacy gpio API that is deprecated to the newer
gpiod API that respects line polarities described in ACPI/DT.

The driver still tries to support shared reset lines, by first trying to
allocate the reset GPIO normally, and then non-exclusively, although the
utility of such support is questionable, toggling reset line from one
driver/instance will result in all chips being reset, potentially at an
inopportune moment.

Note that this change depends on commit fbbbcd177a27 ("gpiolib: of: add
quirk for locating reset lines with legacy bindings") to translate
request for "reset" GPIO to the legacy name "gpio-reset" in case when
proper name is not used.

Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Link: https://lore.kernel.org/r/20221102232004.1721864-3-dmitry.torokhov@gmail.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/tlv320aic3x.c

index 9b2bb99..56e795a 100644 (file)
 #include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/delay.h>
+#include <linux/err.h>
 #include <linux/pm.h>
 #include <linux/i2c.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/regulator/consumer.h>
 #include <linux/of.h>
-#include <linux/of_gpio.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -56,8 +56,6 @@ static const char *aic3x_supply_names[AIC3X_NUM_SUPPLIES] = {
        "DRVDD",        /* ADC Analog and Output Driver Voltage */
 };
 
-static LIST_HEAD(reset_list);
-
 struct aic3x_priv;
 
 struct aic3x_disable_nb {
@@ -80,9 +78,9 @@ struct aic3x_priv {
        unsigned int dai_fmt;
        unsigned int tdm_delay;
        unsigned int slot_width;
-       struct list_head list;
        int master;
-       int gpio_reset;
+       struct gpio_desc *gpio_reset;
+       bool shared_reset;
        int power;
        u16 model;
 
@@ -1369,8 +1367,8 @@ static int aic3x_regulator_event(struct notifier_block *nb,
                 * Put codec to reset and require cache sync as at least one
                 * of the supplies was disabled
                 */
-               if (gpio_is_valid(aic3x->gpio_reset))
-                       gpio_set_value(aic3x->gpio_reset, 0);
+               if (aic3x->gpio_reset)
+                       gpiod_set_value(aic3x->gpio_reset, 1);
                regcache_mark_dirty(aic3x->regmap);
        }
 
@@ -1390,9 +1388,9 @@ static int aic3x_set_power(struct snd_soc_component *component, int power)
                        goto out;
                aic3x->power = 1;
 
-               if (gpio_is_valid(aic3x->gpio_reset)) {
+               if (aic3x->gpio_reset) {
                        udelay(1);
-                       gpio_set_value(aic3x->gpio_reset, 1);
+                       gpiod_set_value(aic3x->gpio_reset, 0);
                }
 
                /* Sync reg_cache with the hardware */
@@ -1598,19 +1596,6 @@ static int aic3x_init(struct snd_soc_component *component)
        return 0;
 }
 
-static bool aic3x_is_shared_reset(struct aic3x_priv *aic3x)
-{
-       struct aic3x_priv *a;
-
-       list_for_each_entry(a, &reset_list, list) {
-               if (gpio_is_valid(aic3x->gpio_reset) &&
-                   aic3x->gpio_reset == a->gpio_reset)
-                       return true;
-       }
-
-       return false;
-}
-
 static int aic3x_component_probe(struct snd_soc_component *component)
 {
        struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component);
@@ -1775,19 +1760,6 @@ int aic3x_probe(struct device *dev, struct regmap *regmap, kernel_ulong_t driver
                if (!ai3x_setup)
                        return -ENOMEM;
 
-               ret = of_get_named_gpio(np, "reset-gpios", 0);
-               if (ret >= 0) {
-                       aic3x->gpio_reset = ret;
-               } else {
-                       ret = of_get_named_gpio(np, "gpio-reset", 0);
-                       if (ret > 0) {
-                               dev_warn(dev, "Using deprecated property \"gpio-reset\", please update your DT");
-                               aic3x->gpio_reset = ret;
-                       } else {
-                               aic3x->gpio_reset = -1;
-                       }
-               }
-
                if (of_property_read_u32_array(np, "ai3x-gpio-func",
                                        ai3x_setup->gpio_func, 2) >= 0) {
                        aic3x->setup = ai3x_setup;
@@ -1812,29 +1784,43 @@ int aic3x_probe(struct device *dev, struct regmap *regmap, kernel_ulong_t driver
                } else {
                        aic3x->micbias_vg = AIC3X_MICBIAS_OFF;
                }
-
-       } else {
-               aic3x->gpio_reset = -1;
        }
 
        aic3x->model = driver_data;
 
-       if (gpio_is_valid(aic3x->gpio_reset) &&
-           !aic3x_is_shared_reset(aic3x)) {
-               ret = gpio_request(aic3x->gpio_reset, "tlv320aic3x reset");
-               if (ret != 0)
-                       goto err;
-               gpio_direction_output(aic3x->gpio_reset, 0);
+       aic3x->gpio_reset = devm_gpiod_get_optional(dev, "reset",
+                                                   GPIOD_OUT_HIGH);
+       ret = PTR_ERR_OR_ZERO(aic3x->gpio_reset);
+       if (ret) {
+               if (ret != -EBUSY)
+                       return ret;
+
+               /*
+                * Apparently there are setups where the codec is sharing
+                * its reset line. Try to get it non-exclusively, although
+                * the utility of this is unclear: how do we make sure that
+                * resetting one chip will not disturb the others that share
+                * the same line?
+                */
+               aic3x->gpio_reset = devm_gpiod_get(dev, "reset",
+                               GPIOD_ASIS | GPIOD_FLAGS_BIT_NONEXCLUSIVE);
+               ret = PTR_ERR_OR_ZERO(aic3x->gpio_reset);
+               if (ret)
+                       return ret;
+
+               aic3x->shared_reset = true;
        }
 
+       gpiod_set_consumer_name(aic3x->gpio_reset, "tlv320aic3x reset");
+
        for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++)
                aic3x->supplies[i].supply = aic3x_supply_names[i];
 
        ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(aic3x->supplies),
                                      aic3x->supplies);
-       if (ret != 0) {
+       if (ret) {
                dev_err(dev, "Failed to request supplies: %d\n", ret);
-               goto err_gpio;
+               return ret;
        }
 
        aic3x_configure_ocmv(dev, aic3x);
@@ -1843,26 +1829,14 @@ int aic3x_probe(struct device *dev, struct regmap *regmap, kernel_ulong_t driver
                ret = regmap_register_patch(aic3x->regmap, aic3007_class_d,
                                            ARRAY_SIZE(aic3007_class_d));
                if (ret != 0)
-                       dev_err(dev, "Failed to init class D: %d\n",
-                               ret);
+                       dev_err(dev, "Failed to init class D: %d\n", ret);
        }
 
        ret = devm_snd_soc_register_component(dev, &soc_component_dev_aic3x, &aic3x_dai, 1);
-
-       if (ret != 0)
-               goto err_gpio;
-
-       INIT_LIST_HEAD(&aic3x->list);
-       list_add(&aic3x->list, &reset_list);
+       if (ret)
+               return ret;
 
        return 0;
-
-err_gpio:
-       if (gpio_is_valid(aic3x->gpio_reset) &&
-           !aic3x_is_shared_reset(aic3x))
-               gpio_free(aic3x->gpio_reset);
-err:
-       return ret;
 }
 EXPORT_SYMBOL(aic3x_probe);
 
@@ -1870,13 +1844,9 @@ void aic3x_remove(struct device *dev)
 {
        struct aic3x_priv *aic3x = dev_get_drvdata(dev);
 
-       list_del(&aic3x->list);
-
-       if (gpio_is_valid(aic3x->gpio_reset) &&
-           !aic3x_is_shared_reset(aic3x)) {
-               gpio_set_value(aic3x->gpio_reset, 0);
-               gpio_free(aic3x->gpio_reset);
-       }
+       /* Leave the codec in reset state */
+       if (aic3x->gpio_reset && !aic3x->shared_reset)
+               gpiod_set_value(aic3x->gpio_reset, 1);
 }
 EXPORT_SYMBOL(aic3x_remove);