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