xref: /kernel/linux/linux-5.10/sound/soc/soc-jack.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// soc-jack.c  --  ALSA SoC jack handling
48c2ecf20Sopenharmony_ci//
58c2ecf20Sopenharmony_ci// Copyright 2008 Wolfson Microelectronics PLC.
68c2ecf20Sopenharmony_ci//
78c2ecf20Sopenharmony_ci// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <sound/jack.h>
108c2ecf20Sopenharmony_ci#include <sound/soc.h>
118c2ecf20Sopenharmony_ci#include <linux/gpio.h>
128c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
138c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
148c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
158c2ecf20Sopenharmony_ci#include <linux/delay.h>
168c2ecf20Sopenharmony_ci#include <linux/export.h>
178c2ecf20Sopenharmony_ci#include <linux/suspend.h>
188c2ecf20Sopenharmony_ci#include <trace/events/asoc.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct jack_gpio_tbl {
218c2ecf20Sopenharmony_ci	int count;
228c2ecf20Sopenharmony_ci	struct snd_soc_jack *jack;
238c2ecf20Sopenharmony_ci	struct snd_soc_jack_gpio *gpios;
248c2ecf20Sopenharmony_ci};
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/**
278c2ecf20Sopenharmony_ci * snd_soc_jack_report - Report the current status for a jack
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci * @jack:   the jack
308c2ecf20Sopenharmony_ci * @status: a bitmask of enum snd_jack_type values that are currently detected.
318c2ecf20Sopenharmony_ci * @mask:   a bitmask of enum snd_jack_type values that being reported.
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci * If configured using snd_soc_jack_add_pins() then the associated
348c2ecf20Sopenharmony_ci * DAPM pins will be enabled or disabled as appropriate and DAPM
358c2ecf20Sopenharmony_ci * synchronised.
368c2ecf20Sopenharmony_ci *
378c2ecf20Sopenharmony_ci * Note: This function uses mutexes and should be called from a
388c2ecf20Sopenharmony_ci * context which can sleep (such as a workqueue).
398c2ecf20Sopenharmony_ci */
408c2ecf20Sopenharmony_civoid snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	struct snd_soc_dapm_context *dapm;
438c2ecf20Sopenharmony_ci	struct snd_soc_jack_pin *pin;
448c2ecf20Sopenharmony_ci	unsigned int sync = 0;
458c2ecf20Sopenharmony_ci	int enable;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	if (!jack)
488c2ecf20Sopenharmony_ci		return;
498c2ecf20Sopenharmony_ci	trace_snd_soc_jack_report(jack, mask, status);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	dapm = &jack->card->dapm;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	mutex_lock(&jack->mutex);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	jack->status &= ~mask;
568c2ecf20Sopenharmony_ci	jack->status |= status & mask;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	trace_snd_soc_jack_notify(jack, status);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	list_for_each_entry(pin, &jack->pins, list) {
618c2ecf20Sopenharmony_ci		enable = pin->mask & jack->status;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci		if (pin->invert)
648c2ecf20Sopenharmony_ci			enable = !enable;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci		if (enable)
678c2ecf20Sopenharmony_ci			snd_soc_dapm_enable_pin(dapm, pin->pin);
688c2ecf20Sopenharmony_ci		else
698c2ecf20Sopenharmony_ci			snd_soc_dapm_disable_pin(dapm, pin->pin);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci		/* we need to sync for this case only */
728c2ecf20Sopenharmony_ci		sync = 1;
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	/* Report before the DAPM sync to help users updating micbias status */
768c2ecf20Sopenharmony_ci	blocking_notifier_call_chain(&jack->notifier, jack->status, jack);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (sync)
798c2ecf20Sopenharmony_ci		snd_soc_dapm_sync(dapm);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	snd_jack_report(jack->jack, jack->status);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	mutex_unlock(&jack->mutex);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_jack_report);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/**
888c2ecf20Sopenharmony_ci * snd_soc_jack_add_zones - Associate voltage zones with jack
898c2ecf20Sopenharmony_ci *
908c2ecf20Sopenharmony_ci * @jack:  ASoC jack
918c2ecf20Sopenharmony_ci * @count: Number of zones
928c2ecf20Sopenharmony_ci * @zones:  Array of zones
938c2ecf20Sopenharmony_ci *
948c2ecf20Sopenharmony_ci * After this function has been called the zones specified in the
958c2ecf20Sopenharmony_ci * array will be associated with the jack.
968c2ecf20Sopenharmony_ci */
978c2ecf20Sopenharmony_ciint snd_soc_jack_add_zones(struct snd_soc_jack *jack, int count,
988c2ecf20Sopenharmony_ci			  struct snd_soc_jack_zone *zones)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	int i;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
1038c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&zones[i].list);
1048c2ecf20Sopenharmony_ci		list_add(&(zones[i].list), &jack->jack_zones);
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci	return 0;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_jack_add_zones);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci/**
1118c2ecf20Sopenharmony_ci * snd_soc_jack_get_type - Based on the mic bias value, this function returns
1128c2ecf20Sopenharmony_ci * the type of jack from the zones declared in the jack type
1138c2ecf20Sopenharmony_ci *
1148c2ecf20Sopenharmony_ci * @jack:  ASoC jack
1158c2ecf20Sopenharmony_ci * @micbias_voltage:  mic bias voltage at adc channel when jack is plugged in
1168c2ecf20Sopenharmony_ci *
1178c2ecf20Sopenharmony_ci * Based on the mic bias value passed, this function helps identify
1188c2ecf20Sopenharmony_ci * the type of jack from the already declared jack zones
1198c2ecf20Sopenharmony_ci */
1208c2ecf20Sopenharmony_ciint snd_soc_jack_get_type(struct snd_soc_jack *jack, int micbias_voltage)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	struct snd_soc_jack_zone *zone;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	list_for_each_entry(zone, &jack->jack_zones, list) {
1258c2ecf20Sopenharmony_ci		if (micbias_voltage >= zone->min_mv &&
1268c2ecf20Sopenharmony_ci			micbias_voltage < zone->max_mv)
1278c2ecf20Sopenharmony_ci				return zone->jack_type;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci	return 0;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_jack_get_type);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci/**
1348c2ecf20Sopenharmony_ci * snd_soc_jack_add_pins - Associate DAPM pins with an ASoC jack
1358c2ecf20Sopenharmony_ci *
1368c2ecf20Sopenharmony_ci * @jack:  ASoC jack
1378c2ecf20Sopenharmony_ci * @count: Number of pins
1388c2ecf20Sopenharmony_ci * @pins:  Array of pins
1398c2ecf20Sopenharmony_ci *
1408c2ecf20Sopenharmony_ci * After this function has been called the DAPM pins specified in the
1418c2ecf20Sopenharmony_ci * pins array will have their status updated to reflect the current
1428c2ecf20Sopenharmony_ci * state of the jack whenever the jack status is updated.
1438c2ecf20Sopenharmony_ci */
1448c2ecf20Sopenharmony_ciint snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count,
1458c2ecf20Sopenharmony_ci			  struct snd_soc_jack_pin *pins)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	int i;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
1508c2ecf20Sopenharmony_ci		if (!pins[i].pin) {
1518c2ecf20Sopenharmony_ci			dev_err(jack->card->dev, "ASoC: No name for pin %d\n",
1528c2ecf20Sopenharmony_ci				i);
1538c2ecf20Sopenharmony_ci			return -EINVAL;
1548c2ecf20Sopenharmony_ci		}
1558c2ecf20Sopenharmony_ci		if (!pins[i].mask) {
1568c2ecf20Sopenharmony_ci			dev_err(jack->card->dev, "ASoC: No mask for pin %d"
1578c2ecf20Sopenharmony_ci				" (%s)\n", i, pins[i].pin);
1588c2ecf20Sopenharmony_ci			return -EINVAL;
1598c2ecf20Sopenharmony_ci		}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&pins[i].list);
1628c2ecf20Sopenharmony_ci		list_add(&(pins[i].list), &jack->pins);
1638c2ecf20Sopenharmony_ci		snd_jack_add_new_kctl(jack->jack, pins[i].pin, pins[i].mask);
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/* Update to reflect the last reported status; canned jack
1678c2ecf20Sopenharmony_ci	 * implementations are likely to set their state before the
1688c2ecf20Sopenharmony_ci	 * card has an opportunity to associate pins.
1698c2ecf20Sopenharmony_ci	 */
1708c2ecf20Sopenharmony_ci	snd_soc_jack_report(jack, 0, 0);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	return 0;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_jack_add_pins);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci/**
1778c2ecf20Sopenharmony_ci * snd_soc_jack_notifier_register - Register a notifier for jack status
1788c2ecf20Sopenharmony_ci *
1798c2ecf20Sopenharmony_ci * @jack:  ASoC jack
1808c2ecf20Sopenharmony_ci * @nb:    Notifier block to register
1818c2ecf20Sopenharmony_ci *
1828c2ecf20Sopenharmony_ci * Register for notification of the current status of the jack.  Note
1838c2ecf20Sopenharmony_ci * that it is not possible to report additional jack events in the
1848c2ecf20Sopenharmony_ci * callback from the notifier, this is intended to support
1858c2ecf20Sopenharmony_ci * applications such as enabling electrical detection only when a
1868c2ecf20Sopenharmony_ci * mechanical detection event has occurred.
1878c2ecf20Sopenharmony_ci */
1888c2ecf20Sopenharmony_civoid snd_soc_jack_notifier_register(struct snd_soc_jack *jack,
1898c2ecf20Sopenharmony_ci				    struct notifier_block *nb)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	blocking_notifier_chain_register(&jack->notifier, nb);
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_jack_notifier_register);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci/**
1968c2ecf20Sopenharmony_ci * snd_soc_jack_notifier_unregister - Unregister a notifier for jack status
1978c2ecf20Sopenharmony_ci *
1988c2ecf20Sopenharmony_ci * @jack:  ASoC jack
1998c2ecf20Sopenharmony_ci * @nb:    Notifier block to unregister
2008c2ecf20Sopenharmony_ci *
2018c2ecf20Sopenharmony_ci * Stop notifying for status changes.
2028c2ecf20Sopenharmony_ci */
2038c2ecf20Sopenharmony_civoid snd_soc_jack_notifier_unregister(struct snd_soc_jack *jack,
2048c2ecf20Sopenharmony_ci				      struct notifier_block *nb)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	blocking_notifier_chain_unregister(&jack->notifier, nb);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_jack_notifier_unregister);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci#ifdef CONFIG_GPIOLIB
2118c2ecf20Sopenharmony_ci/* gpio detect */
2128c2ecf20Sopenharmony_cistatic void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	struct snd_soc_jack *jack = gpio->jack;
2158c2ecf20Sopenharmony_ci	int enable;
2168c2ecf20Sopenharmony_ci	int report;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	enable = gpiod_get_value_cansleep(gpio->desc);
2198c2ecf20Sopenharmony_ci	if (gpio->invert)
2208c2ecf20Sopenharmony_ci		enable = !enable;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	if (enable)
2238c2ecf20Sopenharmony_ci		report = gpio->report;
2248c2ecf20Sopenharmony_ci	else
2258c2ecf20Sopenharmony_ci		report = 0;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (gpio->jack_status_check)
2288c2ecf20Sopenharmony_ci		report = gpio->jack_status_check(gpio->data);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	snd_soc_jack_report(jack, report, gpio->report);
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci/* irq handler for gpio pin */
2348c2ecf20Sopenharmony_cistatic irqreturn_t gpio_handler(int irq, void *data)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	struct snd_soc_jack_gpio *gpio = data;
2378c2ecf20Sopenharmony_ci	struct device *dev = gpio->jack->card->dev;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	trace_snd_soc_jack_irq(gpio->name);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	if (device_may_wakeup(dev))
2428c2ecf20Sopenharmony_ci		pm_wakeup_event(dev, gpio->debounce_time + 50);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	queue_delayed_work(system_power_efficient_wq, &gpio->work,
2458c2ecf20Sopenharmony_ci			      msecs_to_jiffies(gpio->debounce_time));
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci/* gpio work */
2518c2ecf20Sopenharmony_cistatic void gpio_work(struct work_struct *work)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct snd_soc_jack_gpio *gpio;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	gpio = container_of(work, struct snd_soc_jack_gpio, work.work);
2568c2ecf20Sopenharmony_ci	snd_soc_jack_gpio_detect(gpio);
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic int snd_soc_jack_pm_notifier(struct notifier_block *nb,
2608c2ecf20Sopenharmony_ci				    unsigned long action, void *data)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	struct snd_soc_jack_gpio *gpio =
2638c2ecf20Sopenharmony_ci			container_of(nb, struct snd_soc_jack_gpio, pm_notifier);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	switch (action) {
2668c2ecf20Sopenharmony_ci	case PM_POST_SUSPEND:
2678c2ecf20Sopenharmony_ci	case PM_POST_HIBERNATION:
2688c2ecf20Sopenharmony_ci	case PM_POST_RESTORE:
2698c2ecf20Sopenharmony_ci		/*
2708c2ecf20Sopenharmony_ci		 * Use workqueue so we do not have to care about running
2718c2ecf20Sopenharmony_ci		 * concurrently with work triggered by the interrupt handler.
2728c2ecf20Sopenharmony_ci		 */
2738c2ecf20Sopenharmony_ci		queue_delayed_work(system_power_efficient_wq, &gpio->work, 0);
2748c2ecf20Sopenharmony_ci		break;
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic void jack_free_gpios(struct snd_soc_jack *jack, int count,
2818c2ecf20Sopenharmony_ci			    struct snd_soc_jack_gpio *gpios)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	int i;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
2868c2ecf20Sopenharmony_ci		gpiod_unexport(gpios[i].desc);
2878c2ecf20Sopenharmony_ci		unregister_pm_notifier(&gpios[i].pm_notifier);
2888c2ecf20Sopenharmony_ci		free_irq(gpiod_to_irq(gpios[i].desc), &gpios[i]);
2898c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&gpios[i].work);
2908c2ecf20Sopenharmony_ci		gpiod_put(gpios[i].desc);
2918c2ecf20Sopenharmony_ci		gpios[i].jack = NULL;
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic void jack_devres_free_gpios(struct device *dev, void *res)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	struct jack_gpio_tbl *tbl = res;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	jack_free_gpios(tbl->jack, tbl->count, tbl->gpios);
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci/**
3038c2ecf20Sopenharmony_ci * snd_soc_jack_add_gpios - Associate GPIO pins with an ASoC jack
3048c2ecf20Sopenharmony_ci *
3058c2ecf20Sopenharmony_ci * @jack:  ASoC jack
3068c2ecf20Sopenharmony_ci * @count: number of pins
3078c2ecf20Sopenharmony_ci * @gpios: array of gpio pins
3088c2ecf20Sopenharmony_ci *
3098c2ecf20Sopenharmony_ci * This function will request gpio, set data direction and request irq
3108c2ecf20Sopenharmony_ci * for each gpio in the array.
3118c2ecf20Sopenharmony_ci */
3128c2ecf20Sopenharmony_ciint snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
3138c2ecf20Sopenharmony_ci			struct snd_soc_jack_gpio *gpios)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	int i, ret;
3168c2ecf20Sopenharmony_ci	struct jack_gpio_tbl *tbl;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	tbl = devres_alloc(jack_devres_free_gpios, sizeof(*tbl), GFP_KERNEL);
3198c2ecf20Sopenharmony_ci	if (!tbl)
3208c2ecf20Sopenharmony_ci		return -ENOMEM;
3218c2ecf20Sopenharmony_ci	tbl->jack = jack;
3228c2ecf20Sopenharmony_ci	tbl->count = count;
3238c2ecf20Sopenharmony_ci	tbl->gpios = gpios;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
3268c2ecf20Sopenharmony_ci		if (!gpios[i].name) {
3278c2ecf20Sopenharmony_ci			dev_err(jack->card->dev,
3288c2ecf20Sopenharmony_ci				"ASoC: No name for gpio at index %d\n", i);
3298c2ecf20Sopenharmony_ci			ret = -EINVAL;
3308c2ecf20Sopenharmony_ci			goto undo;
3318c2ecf20Sopenharmony_ci		}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci		if (gpios[i].desc) {
3348c2ecf20Sopenharmony_ci			/* Already have a GPIO descriptor. */
3358c2ecf20Sopenharmony_ci			goto got_gpio;
3368c2ecf20Sopenharmony_ci		} else if (gpios[i].gpiod_dev) {
3378c2ecf20Sopenharmony_ci			/* Get a GPIO descriptor */
3388c2ecf20Sopenharmony_ci			gpios[i].desc = gpiod_get_index(gpios[i].gpiod_dev,
3398c2ecf20Sopenharmony_ci							gpios[i].name,
3408c2ecf20Sopenharmony_ci							gpios[i].idx, GPIOD_IN);
3418c2ecf20Sopenharmony_ci			if (IS_ERR(gpios[i].desc)) {
3428c2ecf20Sopenharmony_ci				ret = PTR_ERR(gpios[i].desc);
3438c2ecf20Sopenharmony_ci				dev_err(gpios[i].gpiod_dev,
3448c2ecf20Sopenharmony_ci					"ASoC: Cannot get gpio at index %d: %d",
3458c2ecf20Sopenharmony_ci					i, ret);
3468c2ecf20Sopenharmony_ci				goto undo;
3478c2ecf20Sopenharmony_ci			}
3488c2ecf20Sopenharmony_ci		} else {
3498c2ecf20Sopenharmony_ci			/* legacy GPIO number */
3508c2ecf20Sopenharmony_ci			if (!gpio_is_valid(gpios[i].gpio)) {
3518c2ecf20Sopenharmony_ci				dev_err(jack->card->dev,
3528c2ecf20Sopenharmony_ci					"ASoC: Invalid gpio %d\n",
3538c2ecf20Sopenharmony_ci					gpios[i].gpio);
3548c2ecf20Sopenharmony_ci				ret = -EINVAL;
3558c2ecf20Sopenharmony_ci				goto undo;
3568c2ecf20Sopenharmony_ci			}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci			ret = gpio_request_one(gpios[i].gpio, GPIOF_IN,
3598c2ecf20Sopenharmony_ci					       gpios[i].name);
3608c2ecf20Sopenharmony_ci			if (ret)
3618c2ecf20Sopenharmony_ci				goto undo;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci			gpios[i].desc = gpio_to_desc(gpios[i].gpio);
3648c2ecf20Sopenharmony_ci		}
3658c2ecf20Sopenharmony_cigot_gpio:
3668c2ecf20Sopenharmony_ci		INIT_DELAYED_WORK(&gpios[i].work, gpio_work);
3678c2ecf20Sopenharmony_ci		gpios[i].jack = jack;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci		ret = request_any_context_irq(gpiod_to_irq(gpios[i].desc),
3708c2ecf20Sopenharmony_ci					      gpio_handler,
3718c2ecf20Sopenharmony_ci					      IRQF_TRIGGER_RISING |
3728c2ecf20Sopenharmony_ci					      IRQF_TRIGGER_FALLING,
3738c2ecf20Sopenharmony_ci					      gpios[i].name,
3748c2ecf20Sopenharmony_ci					      &gpios[i]);
3758c2ecf20Sopenharmony_ci		if (ret < 0)
3768c2ecf20Sopenharmony_ci			goto err;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci		if (gpios[i].wake) {
3798c2ecf20Sopenharmony_ci			ret = irq_set_irq_wake(gpiod_to_irq(gpios[i].desc), 1);
3808c2ecf20Sopenharmony_ci			if (ret != 0)
3818c2ecf20Sopenharmony_ci				dev_err(jack->card->dev,
3828c2ecf20Sopenharmony_ci					"ASoC: Failed to mark GPIO at index %d as wake source: %d\n",
3838c2ecf20Sopenharmony_ci					i, ret);
3848c2ecf20Sopenharmony_ci		}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci		/*
3878c2ecf20Sopenharmony_ci		 * Register PM notifier so we do not miss state transitions
3888c2ecf20Sopenharmony_ci		 * happening while system is asleep.
3898c2ecf20Sopenharmony_ci		 */
3908c2ecf20Sopenharmony_ci		gpios[i].pm_notifier.notifier_call = snd_soc_jack_pm_notifier;
3918c2ecf20Sopenharmony_ci		register_pm_notifier(&gpios[i].pm_notifier);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci		/* Expose GPIO value over sysfs for diagnostic purposes */
3948c2ecf20Sopenharmony_ci		gpiod_export(gpios[i].desc, false);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci		/* Update initial jack status */
3978c2ecf20Sopenharmony_ci		schedule_delayed_work(&gpios[i].work,
3988c2ecf20Sopenharmony_ci				      msecs_to_jiffies(gpios[i].debounce_time));
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	devres_add(jack->card->dev, tbl);
4028c2ecf20Sopenharmony_ci	return 0;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cierr:
4058c2ecf20Sopenharmony_ci	gpio_free(gpios[i].gpio);
4068c2ecf20Sopenharmony_ciundo:
4078c2ecf20Sopenharmony_ci	jack_free_gpios(jack, i, gpios);
4088c2ecf20Sopenharmony_ci	devres_free(tbl);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	return ret;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_jack_add_gpios);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci/**
4158c2ecf20Sopenharmony_ci * snd_soc_jack_add_gpiods - Associate GPIO descriptor pins with an ASoC jack
4168c2ecf20Sopenharmony_ci *
4178c2ecf20Sopenharmony_ci * @gpiod_dev: GPIO consumer device
4188c2ecf20Sopenharmony_ci * @jack:      ASoC jack
4198c2ecf20Sopenharmony_ci * @count:     number of pins
4208c2ecf20Sopenharmony_ci * @gpios:     array of gpio pins
4218c2ecf20Sopenharmony_ci *
4228c2ecf20Sopenharmony_ci * This function will request gpio, set data direction and request irq
4238c2ecf20Sopenharmony_ci * for each gpio in the array.
4248c2ecf20Sopenharmony_ci */
4258c2ecf20Sopenharmony_ciint snd_soc_jack_add_gpiods(struct device *gpiod_dev,
4268c2ecf20Sopenharmony_ci			    struct snd_soc_jack *jack,
4278c2ecf20Sopenharmony_ci			    int count, struct snd_soc_jack_gpio *gpios)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	int i;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++)
4328c2ecf20Sopenharmony_ci		gpios[i].gpiod_dev = gpiod_dev;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	return snd_soc_jack_add_gpios(jack, count, gpios);
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_jack_add_gpiods);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci/**
4398c2ecf20Sopenharmony_ci * snd_soc_jack_free_gpios - Release GPIO pins' resources of an ASoC jack
4408c2ecf20Sopenharmony_ci *
4418c2ecf20Sopenharmony_ci * @jack:  ASoC jack
4428c2ecf20Sopenharmony_ci * @count: number of pins
4438c2ecf20Sopenharmony_ci * @gpios: array of gpio pins
4448c2ecf20Sopenharmony_ci *
4458c2ecf20Sopenharmony_ci * Release gpio and irq resources for gpio pins associated with an ASoC jack.
4468c2ecf20Sopenharmony_ci */
4478c2ecf20Sopenharmony_civoid snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
4488c2ecf20Sopenharmony_ci			struct snd_soc_jack_gpio *gpios)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci	jack_free_gpios(jack, count, gpios);
4518c2ecf20Sopenharmony_ci	devres_destroy(jack->card->dev, jack_devres_free_gpios, NULL, NULL);
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_jack_free_gpios);
4548c2ecf20Sopenharmony_ci#endif	/* CONFIG_GPIOLIB */
455