Commit a984d833 authored by Dmitry Torokhov's avatar Dmitry Torokhov Committed by Mark Brown

ASoC: tlv320aic3x: switch to using gpiod API

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 fbbbcd17 ("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: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
Link: https://lore.kernel.org/r/20221102232004.1721864-3-dmitry.torokhov@gmail.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent 426c7bf4
...@@ -32,12 +32,12 @@ ...@@ -32,12 +32,12 @@
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/err.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/gpio.h> #include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/pcm.h> #include <sound/pcm.h>
...@@ -56,8 +56,6 @@ static const char *aic3x_supply_names[AIC3X_NUM_SUPPLIES] = { ...@@ -56,8 +56,6 @@ static const char *aic3x_supply_names[AIC3X_NUM_SUPPLIES] = {
"DRVDD", /* ADC Analog and Output Driver Voltage */ "DRVDD", /* ADC Analog and Output Driver Voltage */
}; };
static LIST_HEAD(reset_list);
struct aic3x_priv; struct aic3x_priv;
struct aic3x_disable_nb { struct aic3x_disable_nb {
...@@ -80,9 +78,9 @@ struct aic3x_priv { ...@@ -80,9 +78,9 @@ struct aic3x_priv {
unsigned int dai_fmt; unsigned int dai_fmt;
unsigned int tdm_delay; unsigned int tdm_delay;
unsigned int slot_width; unsigned int slot_width;
struct list_head list;
int master; int master;
int gpio_reset; struct gpio_desc *gpio_reset;
bool shared_reset;
int power; int power;
u16 model; u16 model;
...@@ -1369,8 +1367,8 @@ static int aic3x_regulator_event(struct notifier_block *nb, ...@@ -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 * Put codec to reset and require cache sync as at least one
* of the supplies was disabled * of the supplies was disabled
*/ */
if (gpio_is_valid(aic3x->gpio_reset)) if (aic3x->gpio_reset)
gpio_set_value(aic3x->gpio_reset, 0); gpiod_set_value(aic3x->gpio_reset, 1);
regcache_mark_dirty(aic3x->regmap); regcache_mark_dirty(aic3x->regmap);
} }
...@@ -1390,9 +1388,9 @@ static int aic3x_set_power(struct snd_soc_component *component, int power) ...@@ -1390,9 +1388,9 @@ static int aic3x_set_power(struct snd_soc_component *component, int power)
goto out; goto out;
aic3x->power = 1; aic3x->power = 1;
if (gpio_is_valid(aic3x->gpio_reset)) { if (aic3x->gpio_reset) {
udelay(1); udelay(1);
gpio_set_value(aic3x->gpio_reset, 1); gpiod_set_value(aic3x->gpio_reset, 0);
} }
/* Sync reg_cache with the hardware */ /* Sync reg_cache with the hardware */
...@@ -1598,19 +1596,6 @@ static int aic3x_init(struct snd_soc_component *component) ...@@ -1598,19 +1596,6 @@ static int aic3x_init(struct snd_soc_component *component)
return 0; 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) static int aic3x_component_probe(struct snd_soc_component *component)
{ {
struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(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 ...@@ -1775,19 +1760,6 @@ int aic3x_probe(struct device *dev, struct regmap *regmap, kernel_ulong_t driver
if (!ai3x_setup) if (!ai3x_setup)
return -ENOMEM; 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", if (of_property_read_u32_array(np, "ai3x-gpio-func",
ai3x_setup->gpio_func, 2) >= 0) { ai3x_setup->gpio_func, 2) >= 0) {
aic3x->setup = ai3x_setup; aic3x->setup = ai3x_setup;
...@@ -1812,29 +1784,43 @@ int aic3x_probe(struct device *dev, struct regmap *regmap, kernel_ulong_t driver ...@@ -1812,29 +1784,43 @@ int aic3x_probe(struct device *dev, struct regmap *regmap, kernel_ulong_t driver
} else { } else {
aic3x->micbias_vg = AIC3X_MICBIAS_OFF; aic3x->micbias_vg = AIC3X_MICBIAS_OFF;
} }
} else {
aic3x->gpio_reset = -1;
} }
aic3x->model = driver_data; aic3x->model = driver_data;
if (gpio_is_valid(aic3x->gpio_reset) && aic3x->gpio_reset = devm_gpiod_get_optional(dev, "reset",
!aic3x_is_shared_reset(aic3x)) { GPIOD_OUT_HIGH);
ret = gpio_request(aic3x->gpio_reset, "tlv320aic3x reset"); ret = PTR_ERR_OR_ZERO(aic3x->gpio_reset);
if (ret != 0) if (ret) {
goto err; if (ret != -EBUSY)
gpio_direction_output(aic3x->gpio_reset, 0); 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++) for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++)
aic3x->supplies[i].supply = aic3x_supply_names[i]; aic3x->supplies[i].supply = aic3x_supply_names[i];
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(aic3x->supplies), ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(aic3x->supplies),
aic3x->supplies); aic3x->supplies);
if (ret != 0) { if (ret) {
dev_err(dev, "Failed to request supplies: %d\n", ret); dev_err(dev, "Failed to request supplies: %d\n", ret);
goto err_gpio; return ret;
} }
aic3x_configure_ocmv(dev, aic3x); aic3x_configure_ocmv(dev, aic3x);
...@@ -1843,26 +1829,14 @@ int aic3x_probe(struct device *dev, struct regmap *regmap, kernel_ulong_t driver ...@@ -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, ret = regmap_register_patch(aic3x->regmap, aic3007_class_d,
ARRAY_SIZE(aic3007_class_d)); ARRAY_SIZE(aic3007_class_d));
if (ret != 0) if (ret != 0)
dev_err(dev, "Failed to init class D: %d\n", dev_err(dev, "Failed to init class D: %d\n", ret);
ret);
} }
ret = devm_snd_soc_register_component(dev, &soc_component_dev_aic3x, &aic3x_dai, 1); ret = devm_snd_soc_register_component(dev, &soc_component_dev_aic3x, &aic3x_dai, 1);
if (ret)
if (ret != 0) return ret;
goto err_gpio;
INIT_LIST_HEAD(&aic3x->list);
list_add(&aic3x->list, &reset_list);
return 0; 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); EXPORT_SYMBOL(aic3x_probe);
...@@ -1870,13 +1844,9 @@ void aic3x_remove(struct device *dev) ...@@ -1870,13 +1844,9 @@ void aic3x_remove(struct device *dev)
{ {
struct aic3x_priv *aic3x = dev_get_drvdata(dev); struct aic3x_priv *aic3x = dev_get_drvdata(dev);
list_del(&aic3x->list); /* Leave the codec in reset state */
if (aic3x->gpio_reset && !aic3x->shared_reset)
if (gpio_is_valid(aic3x->gpio_reset) && gpiod_set_value(aic3x->gpio_reset, 1);
!aic3x_is_shared_reset(aic3x)) {
gpio_set_value(aic3x->gpio_reset, 0);
gpio_free(aic3x->gpio_reset);
}
} }
EXPORT_SYMBOL(aic3x_remove); EXPORT_SYMBOL(aic3x_remove);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment