soc-jack.c (11610B)
1// SPDX-License-Identifier: GPL-2.0+ 2// 3// soc-jack.c -- ALSA SoC jack handling 4// 5// Copyright 2008 Wolfson Microelectronics PLC. 6// 7// Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 8 9#include <sound/jack.h> 10#include <sound/soc.h> 11#include <linux/gpio.h> 12#include <linux/gpio/consumer.h> 13#include <linux/interrupt.h> 14#include <linux/workqueue.h> 15#include <linux/delay.h> 16#include <linux/export.h> 17#include <linux/suspend.h> 18#include <trace/events/asoc.h> 19 20/** 21 * snd_soc_jack_report - Report the current status for a jack 22 * 23 * @jack: the jack 24 * @status: a bitmask of enum snd_jack_type values that are currently detected. 25 * @mask: a bitmask of enum snd_jack_type values that being reported. 26 * 27 * If configured using snd_soc_jack_add_pins() then the associated 28 * DAPM pins will be enabled or disabled as appropriate and DAPM 29 * synchronised. 30 * 31 * Note: This function uses mutexes and should be called from a 32 * context which can sleep (such as a workqueue). 33 */ 34void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) 35{ 36 struct snd_soc_dapm_context *dapm; 37 struct snd_soc_jack_pin *pin; 38 unsigned int sync = 0; 39 40 if (!jack) 41 return; 42 trace_snd_soc_jack_report(jack, mask, status); 43 44 dapm = &jack->card->dapm; 45 46 mutex_lock(&jack->mutex); 47 48 jack->status &= ~mask; 49 jack->status |= status & mask; 50 51 trace_snd_soc_jack_notify(jack, status); 52 53 list_for_each_entry(pin, &jack->pins, list) { 54 int enable = pin->mask & jack->status; 55 56 if (pin->invert) 57 enable = !enable; 58 59 if (enable) 60 snd_soc_dapm_enable_pin(dapm, pin->pin); 61 else 62 snd_soc_dapm_disable_pin(dapm, pin->pin); 63 64 /* we need to sync for this case only */ 65 sync = 1; 66 } 67 68 /* Report before the DAPM sync to help users updating micbias status */ 69 blocking_notifier_call_chain(&jack->notifier, jack->status, jack); 70 71 if (sync) 72 snd_soc_dapm_sync(dapm); 73 74 snd_jack_report(jack->jack, jack->status); 75 76 mutex_unlock(&jack->mutex); 77} 78EXPORT_SYMBOL_GPL(snd_soc_jack_report); 79 80/** 81 * snd_soc_jack_add_zones - Associate voltage zones with jack 82 * 83 * @jack: ASoC jack 84 * @count: Number of zones 85 * @zones: Array of zones 86 * 87 * After this function has been called the zones specified in the 88 * array will be associated with the jack. 89 */ 90int snd_soc_jack_add_zones(struct snd_soc_jack *jack, int count, 91 struct snd_soc_jack_zone *zones) 92{ 93 int i; 94 95 for (i = 0; i < count; i++) { 96 INIT_LIST_HEAD(&zones[i].list); 97 list_add(&(zones[i].list), &jack->jack_zones); 98 } 99 return 0; 100} 101EXPORT_SYMBOL_GPL(snd_soc_jack_add_zones); 102 103/** 104 * snd_soc_jack_get_type - Based on the mic bias value, this function returns 105 * the type of jack from the zones declared in the jack type 106 * 107 * @jack: ASoC jack 108 * @micbias_voltage: mic bias voltage at adc channel when jack is plugged in 109 * 110 * Based on the mic bias value passed, this function helps identify 111 * the type of jack from the already declared jack zones 112 */ 113int snd_soc_jack_get_type(struct snd_soc_jack *jack, int micbias_voltage) 114{ 115 struct snd_soc_jack_zone *zone; 116 117 list_for_each_entry(zone, &jack->jack_zones, list) { 118 if (micbias_voltage >= zone->min_mv && 119 micbias_voltage < zone->max_mv) 120 return zone->jack_type; 121 } 122 return 0; 123} 124EXPORT_SYMBOL_GPL(snd_soc_jack_get_type); 125 126/** 127 * snd_soc_jack_add_pins - Associate DAPM pins with an ASoC jack 128 * 129 * @jack: ASoC jack created with snd_soc_card_jack_new_pins() 130 * @count: Number of pins 131 * @pins: Array of pins 132 * 133 * After this function has been called the DAPM pins specified in the 134 * pins array will have their status updated to reflect the current 135 * state of the jack whenever the jack status is updated. 136 */ 137int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count, 138 struct snd_soc_jack_pin *pins) 139{ 140 int i; 141 142 for (i = 0; i < count; i++) { 143 if (!pins[i].pin) { 144 dev_err(jack->card->dev, "ASoC: No name for pin %d\n", 145 i); 146 return -EINVAL; 147 } 148 if (!pins[i].mask) { 149 dev_err(jack->card->dev, "ASoC: No mask for pin %d" 150 " (%s)\n", i, pins[i].pin); 151 return -EINVAL; 152 } 153 154 INIT_LIST_HEAD(&pins[i].list); 155 list_add(&(pins[i].list), &jack->pins); 156 snd_jack_add_new_kctl(jack->jack, pins[i].pin, pins[i].mask); 157 } 158 159 /* Update to reflect the last reported status; canned jack 160 * implementations are likely to set their state before the 161 * card has an opportunity to associate pins. 162 */ 163 snd_soc_jack_report(jack, 0, 0); 164 165 return 0; 166} 167EXPORT_SYMBOL_GPL(snd_soc_jack_add_pins); 168 169/** 170 * snd_soc_jack_notifier_register - Register a notifier for jack status 171 * 172 * @jack: ASoC jack 173 * @nb: Notifier block to register 174 * 175 * Register for notification of the current status of the jack. Note 176 * that it is not possible to report additional jack events in the 177 * callback from the notifier, this is intended to support 178 * applications such as enabling electrical detection only when a 179 * mechanical detection event has occurred. 180 */ 181void snd_soc_jack_notifier_register(struct snd_soc_jack *jack, 182 struct notifier_block *nb) 183{ 184 blocking_notifier_chain_register(&jack->notifier, nb); 185} 186EXPORT_SYMBOL_GPL(snd_soc_jack_notifier_register); 187 188/** 189 * snd_soc_jack_notifier_unregister - Unregister a notifier for jack status 190 * 191 * @jack: ASoC jack 192 * @nb: Notifier block to unregister 193 * 194 * Stop notifying for status changes. 195 */ 196void snd_soc_jack_notifier_unregister(struct snd_soc_jack *jack, 197 struct notifier_block *nb) 198{ 199 blocking_notifier_chain_unregister(&jack->notifier, nb); 200} 201EXPORT_SYMBOL_GPL(snd_soc_jack_notifier_unregister); 202 203#ifdef CONFIG_GPIOLIB 204struct jack_gpio_tbl { 205 int count; 206 struct snd_soc_jack *jack; 207 struct snd_soc_jack_gpio *gpios; 208}; 209 210/* gpio detect */ 211static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio) 212{ 213 struct snd_soc_jack *jack = gpio->jack; 214 int enable; 215 int report; 216 217 enable = gpiod_get_value_cansleep(gpio->desc); 218 if (gpio->invert) 219 enable = !enable; 220 221 if (enable) 222 report = gpio->report; 223 else 224 report = 0; 225 226 if (gpio->jack_status_check) 227 report = gpio->jack_status_check(gpio->data); 228 229 snd_soc_jack_report(jack, report, gpio->report); 230} 231 232/* irq handler for gpio pin */ 233static irqreturn_t gpio_handler(int irq, void *data) 234{ 235 struct snd_soc_jack_gpio *gpio = data; 236 struct device *dev = gpio->jack->card->dev; 237 238 trace_snd_soc_jack_irq(gpio->name); 239 240 if (device_may_wakeup(dev)) 241 pm_wakeup_event(dev, gpio->debounce_time + 50); 242 243 queue_delayed_work(system_power_efficient_wq, &gpio->work, 244 msecs_to_jiffies(gpio->debounce_time)); 245 246 return IRQ_HANDLED; 247} 248 249/* gpio work */ 250static void gpio_work(struct work_struct *work) 251{ 252 struct snd_soc_jack_gpio *gpio; 253 254 gpio = container_of(work, struct snd_soc_jack_gpio, work.work); 255 snd_soc_jack_gpio_detect(gpio); 256} 257 258static int snd_soc_jack_pm_notifier(struct notifier_block *nb, 259 unsigned long action, void *data) 260{ 261 struct snd_soc_jack_gpio *gpio = 262 container_of(nb, struct snd_soc_jack_gpio, pm_notifier); 263 264 switch (action) { 265 case PM_POST_SUSPEND: 266 case PM_POST_HIBERNATION: 267 case PM_POST_RESTORE: 268 /* 269 * Use workqueue so we do not have to care about running 270 * concurrently with work triggered by the interrupt handler. 271 */ 272 queue_delayed_work(system_power_efficient_wq, &gpio->work, 0); 273 break; 274 } 275 276 return NOTIFY_DONE; 277} 278 279static void jack_free_gpios(struct snd_soc_jack *jack, int count, 280 struct snd_soc_jack_gpio *gpios) 281{ 282 int i; 283 284 for (i = 0; i < count; i++) { 285 gpiod_unexport(gpios[i].desc); 286 unregister_pm_notifier(&gpios[i].pm_notifier); 287 free_irq(gpiod_to_irq(gpios[i].desc), &gpios[i]); 288 cancel_delayed_work_sync(&gpios[i].work); 289 gpiod_put(gpios[i].desc); 290 gpios[i].jack = NULL; 291 } 292} 293 294static void jack_devres_free_gpios(struct device *dev, void *res) 295{ 296 struct jack_gpio_tbl *tbl = res; 297 298 jack_free_gpios(tbl->jack, tbl->count, tbl->gpios); 299} 300 301/** 302 * snd_soc_jack_add_gpios - Associate GPIO pins with an ASoC jack 303 * 304 * @jack: ASoC jack 305 * @count: number of pins 306 * @gpios: array of gpio pins 307 * 308 * This function will request gpio, set data direction and request irq 309 * for each gpio in the array. 310 */ 311int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, 312 struct snd_soc_jack_gpio *gpios) 313{ 314 int i, ret; 315 struct jack_gpio_tbl *tbl; 316 317 tbl = devres_alloc(jack_devres_free_gpios, sizeof(*tbl), GFP_KERNEL); 318 if (!tbl) 319 return -ENOMEM; 320 tbl->jack = jack; 321 tbl->count = count; 322 tbl->gpios = gpios; 323 324 for (i = 0; i < count; i++) { 325 if (!gpios[i].name) { 326 dev_err(jack->card->dev, 327 "ASoC: No name for gpio at index %d\n", i); 328 ret = -EINVAL; 329 goto undo; 330 } 331 332 if (gpios[i].desc) { 333 /* Already have a GPIO descriptor. */ 334 goto got_gpio; 335 } else if (gpios[i].gpiod_dev) { 336 /* Get a GPIO descriptor */ 337 gpios[i].desc = gpiod_get_index(gpios[i].gpiod_dev, 338 gpios[i].name, 339 gpios[i].idx, GPIOD_IN); 340 if (IS_ERR(gpios[i].desc)) { 341 ret = PTR_ERR(gpios[i].desc); 342 dev_err(gpios[i].gpiod_dev, 343 "ASoC: Cannot get gpio at index %d: %d", 344 i, ret); 345 goto undo; 346 } 347 } else { 348 /* legacy GPIO number */ 349 if (!gpio_is_valid(gpios[i].gpio)) { 350 dev_err(jack->card->dev, 351 "ASoC: Invalid gpio %d\n", 352 gpios[i].gpio); 353 ret = -EINVAL; 354 goto undo; 355 } 356 357 ret = gpio_request_one(gpios[i].gpio, GPIOF_IN, 358 gpios[i].name); 359 if (ret) 360 goto undo; 361 362 gpios[i].desc = gpio_to_desc(gpios[i].gpio); 363 } 364got_gpio: 365 INIT_DELAYED_WORK(&gpios[i].work, gpio_work); 366 gpios[i].jack = jack; 367 368 ret = request_any_context_irq(gpiod_to_irq(gpios[i].desc), 369 gpio_handler, 370 IRQF_TRIGGER_RISING | 371 IRQF_TRIGGER_FALLING, 372 gpios[i].name, 373 &gpios[i]); 374 if (ret < 0) 375 goto err; 376 377 if (gpios[i].wake) { 378 ret = irq_set_irq_wake(gpiod_to_irq(gpios[i].desc), 1); 379 if (ret != 0) 380 dev_err(jack->card->dev, 381 "ASoC: Failed to mark GPIO at index %d as wake source: %d\n", 382 i, ret); 383 } 384 385 /* 386 * Register PM notifier so we do not miss state transitions 387 * happening while system is asleep. 388 */ 389 gpios[i].pm_notifier.notifier_call = snd_soc_jack_pm_notifier; 390 register_pm_notifier(&gpios[i].pm_notifier); 391 392 /* Expose GPIO value over sysfs for diagnostic purposes */ 393 gpiod_export(gpios[i].desc, false); 394 395 /* Update initial jack status */ 396 schedule_delayed_work(&gpios[i].work, 397 msecs_to_jiffies(gpios[i].debounce_time)); 398 } 399 400 devres_add(jack->card->dev, tbl); 401 return 0; 402 403err: 404 gpio_free(gpios[i].gpio); 405undo: 406 jack_free_gpios(jack, i, gpios); 407 devres_free(tbl); 408 409 return ret; 410} 411EXPORT_SYMBOL_GPL(snd_soc_jack_add_gpios); 412 413/** 414 * snd_soc_jack_add_gpiods - Associate GPIO descriptor pins with an ASoC jack 415 * 416 * @gpiod_dev: GPIO consumer device 417 * @jack: ASoC jack 418 * @count: number of pins 419 * @gpios: array of gpio pins 420 * 421 * This function will request gpio, set data direction and request irq 422 * for each gpio in the array. 423 */ 424int snd_soc_jack_add_gpiods(struct device *gpiod_dev, 425 struct snd_soc_jack *jack, 426 int count, struct snd_soc_jack_gpio *gpios) 427{ 428 int i; 429 430 for (i = 0; i < count; i++) 431 gpios[i].gpiod_dev = gpiod_dev; 432 433 return snd_soc_jack_add_gpios(jack, count, gpios); 434} 435EXPORT_SYMBOL_GPL(snd_soc_jack_add_gpiods); 436 437/** 438 * snd_soc_jack_free_gpios - Release GPIO pins' resources of an ASoC jack 439 * 440 * @jack: ASoC jack 441 * @count: number of pins 442 * @gpios: array of gpio pins 443 * 444 * Release gpio and irq resources for gpio pins associated with an ASoC jack. 445 */ 446void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count, 447 struct snd_soc_jack_gpio *gpios) 448{ 449 jack_free_gpios(jack, count, gpios); 450 devres_destroy(jack->card->dev, jack_devres_free_gpios, NULL, NULL); 451} 452EXPORT_SYMBOL_GPL(snd_soc_jack_free_gpios); 453#endif /* CONFIG_GPIOLIB */