Skip to content

Commit

Permalink
ASoC: tlv320aic3x: switch to using gpiod API
Browse files Browse the repository at this point in the history
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 fbbbcd1 ("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 <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Mark Brown <[email protected]>
  • Loading branch information
dtor authored and broonie committed Nov 3, 2022
1 parent 426c7bf commit a984d83
Showing 1 changed file with 39 additions and 69 deletions.
108 changes: 39 additions & 69 deletions sound/soc/codecs/tlv320aic3x.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@
#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>
Expand All @@ -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 {
Expand All @@ -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;

Expand Down Expand Up @@ -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);
}

Expand All @@ -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 */
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -1843,40 +1829,24 @@ 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);

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);

Expand Down

0 comments on commit a984d83

Please sign in to comment.