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