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

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.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 426c7bf4
Loading
Loading
Loading
Loading
+39 −69
Original line number Diff line number Diff line
@@ -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>
@@ -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);