| OLD | NEW |
| 1 /* | 1 /* |
| 2 * soc-jack.c -- ALSA SoC jack handling | 2 * soc-jack.c -- ALSA SoC jack handling |
| 3 * | 3 * |
| 4 * Copyright 2008 Wolfson Microelectronics PLC. | 4 * Copyright 2008 Wolfson Microelectronics PLC. |
| 5 * | 5 * |
| 6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> | 6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> |
| 7 * | 7 * |
| 8 * This program is free software; you can redistribute it and/or modify it | 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 | 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 | 10 * Free Software Foundation; either version 2 of the License, or (at your |
| 11 * option) any later version. | 11 * option) any later version. |
| 12 */ | 12 */ |
| 13 | 13 |
| 14 #include <sound/jack.h> | 14 #include <sound/jack.h> |
| 15 #include <sound/soc.h> | 15 #include <sound/soc.h> |
| 16 #include <sound/soc-dapm.h> | |
| 17 #include <linux/gpio.h> | 16 #include <linux/gpio.h> |
| 18 #include <linux/interrupt.h> | 17 #include <linux/interrupt.h> |
| 19 #include <linux/workqueue.h> | 18 #include <linux/workqueue.h> |
| 20 #include <linux/delay.h> | 19 #include <linux/delay.h> |
| 20 #include <trace/events/asoc.h> |
| 21 | 21 |
| 22 /** | 22 /** |
| 23 * snd_soc_jack_new - Create a new jack | 23 * snd_soc_jack_new - Create a new jack |
| 24 * @card: ASoC card | 24 * @card: ASoC card |
| 25 * @id: an identifying string for this jack | 25 * @id: an identifying string for this jack |
| 26 * @type: a bitmask of enum snd_jack_type values that can be detected by | 26 * @type: a bitmask of enum snd_jack_type values that can be detected by |
| 27 * this jack | 27 * this jack |
| 28 * @jack: structure to use for the jack | 28 * @jack: structure to use for the jack |
| 29 * | 29 * |
| 30 * Creates a new jack object. | 30 * Creates a new jack object. |
| 31 * | 31 * |
| 32 * Returns zero if successful, or a negative error code on failure. | 32 * Returns zero if successful, or a negative error code on failure. |
| 33 * On success jack will be initialised. | 33 * On success jack will be initialised. |
| 34 */ | 34 */ |
| 35 int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type, | 35 int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type, |
| 36 struct snd_soc_jack *jack) | 36 struct snd_soc_jack *jack) |
| 37 { | 37 { |
| 38 jack->codec = codec; | 38 jack->codec = codec; |
| 39 INIT_LIST_HEAD(&jack->pins); | 39 INIT_LIST_HEAD(&jack->pins); |
| 40 INIT_LIST_HEAD(&jack->jack_zones); |
| 40 BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier); | 41 BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier); |
| 41 | 42 |
| 42 return snd_jack_new(codec->card->snd_card, id, type, &jack->jack); | 43 return snd_jack_new(codec->card->snd_card, id, type, &jack->jack); |
| 43 } | 44 } |
| 44 EXPORT_SYMBOL_GPL(snd_soc_jack_new); | 45 EXPORT_SYMBOL_GPL(snd_soc_jack_new); |
| 45 | 46 |
| 46 /** | 47 /** |
| 47 * snd_soc_jack_report - Report the current status for a jack | 48 * snd_soc_jack_report - Report the current status for a jack |
| 48 * | 49 * |
| 49 * @jack: the jack | 50 * @jack: the jack |
| 50 * @status: a bitmask of enum snd_jack_type values that are currently detected. | 51 * @status: a bitmask of enum snd_jack_type values that are currently detected. |
| 51 * @mask: a bitmask of enum snd_jack_type values that being reported. | 52 * @mask: a bitmask of enum snd_jack_type values that being reported. |
| 52 * | 53 * |
| 53 * If configured using snd_soc_jack_add_pins() then the associated | 54 * If configured using snd_soc_jack_add_pins() then the associated |
| 54 * DAPM pins will be enabled or disabled as appropriate and DAPM | 55 * DAPM pins will be enabled or disabled as appropriate and DAPM |
| 55 * synchronised. | 56 * synchronised. |
| 56 * | 57 * |
| 57 * Note: This function uses mutexes and should be called from a | 58 * Note: This function uses mutexes and should be called from a |
| 58 * context which can sleep (such as a workqueue). | 59 * context which can sleep (such as a workqueue). |
| 59 */ | 60 */ |
| 60 void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) | 61 void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) |
| 61 { | 62 { |
| 62 struct snd_soc_codec *codec; | 63 struct snd_soc_codec *codec; |
| 64 struct snd_soc_dapm_context *dapm; |
| 63 struct snd_soc_jack_pin *pin; | 65 struct snd_soc_jack_pin *pin; |
| 64 int enable; | 66 int enable; |
| 65 int oldstatus; | 67 int oldstatus; |
| 66 | 68 |
| 69 trace_snd_soc_jack_report(jack, mask, status); |
| 70 |
| 67 if (!jack) | 71 if (!jack) |
| 68 return; | 72 return; |
| 69 | 73 |
| 70 codec = jack->codec; | 74 codec = jack->codec; |
| 75 dapm = &codec->dapm; |
| 71 | 76 |
| 72 mutex_lock(&codec->mutex); | 77 mutex_lock(&codec->mutex); |
| 73 | 78 |
| 74 oldstatus = jack->status; | 79 oldstatus = jack->status; |
| 75 | 80 |
| 76 jack->status &= ~mask; | 81 jack->status &= ~mask; |
| 77 jack->status |= status & mask; | 82 jack->status |= status & mask; |
| 78 | 83 |
| 79 /* The DAPM sync is expensive enough to be worth skipping. | 84 /* The DAPM sync is expensive enough to be worth skipping. |
| 80 * However, empty mask means pin synchronization is desired. */ | 85 * However, empty mask means pin synchronization is desired. */ |
| 81 if (mask && (jack->status == oldstatus)) | 86 if (mask && (jack->status == oldstatus)) |
| 82 goto out; | 87 goto out; |
| 83 | 88 |
| 89 trace_snd_soc_jack_notify(jack, status); |
| 90 |
| 84 list_for_each_entry(pin, &jack->pins, list) { | 91 list_for_each_entry(pin, &jack->pins, list) { |
| 85 enable = pin->mask & jack->status; | 92 enable = pin->mask & jack->status; |
| 86 | 93 |
| 87 if (pin->invert) | 94 if (pin->invert) |
| 88 enable = !enable; | 95 enable = !enable; |
| 89 | 96 |
| 90 if (enable) | 97 if (enable) |
| 91 » » » snd_soc_dapm_enable_pin(codec, pin->pin); | 98 » » » snd_soc_dapm_enable_pin(dapm, pin->pin); |
| 92 else | 99 else |
| 93 » » » snd_soc_dapm_disable_pin(codec, pin->pin); | 100 » » » snd_soc_dapm_disable_pin(dapm, pin->pin); |
| 94 } | 101 } |
| 95 | 102 |
| 96 /* Report before the DAPM sync to help users updating micbias status */ | 103 /* Report before the DAPM sync to help users updating micbias status */ |
| 97 blocking_notifier_call_chain(&jack->notifier, status, NULL); | 104 blocking_notifier_call_chain(&jack->notifier, status, NULL); |
| 98 | 105 |
| 99 » snd_soc_dapm_sync(codec); | 106 » snd_soc_dapm_sync(dapm); |
| 100 | 107 |
| 101 snd_jack_report(jack->jack, status); | 108 snd_jack_report(jack->jack, status); |
| 102 | 109 |
| 103 out: | 110 out: |
| 104 mutex_unlock(&codec->mutex); | 111 mutex_unlock(&codec->mutex); |
| 105 } | 112 } |
| 106 EXPORT_SYMBOL_GPL(snd_soc_jack_report); | 113 EXPORT_SYMBOL_GPL(snd_soc_jack_report); |
| 107 | 114 |
| 108 /** | 115 /** |
| 116 * snd_soc_jack_add_zones - Associate voltage zones with jack |
| 117 * |
| 118 * @jack: ASoC jack |
| 119 * @count: Number of zones |
| 120 * @zone: Array of zones |
| 121 * |
| 122 * After this function has been called the zones specified in the |
| 123 * array will be associated with the jack. |
| 124 */ |
| 125 int snd_soc_jack_add_zones(struct snd_soc_jack *jack, int count, |
| 126 struct snd_soc_jack_zone *zones) |
| 127 { |
| 128 int i; |
| 129 |
| 130 for (i = 0; i < count; i++) { |
| 131 INIT_LIST_HEAD(&zones[i].list); |
| 132 list_add(&(zones[i].list), &jack->jack_zones); |
| 133 } |
| 134 return 0; |
| 135 } |
| 136 EXPORT_SYMBOL_GPL(snd_soc_jack_add_zones); |
| 137 |
| 138 /** |
| 139 * snd_soc_jack_get_type - Based on the mic bias value, this function returns |
| 140 * the type of jack from the zones delcared in the jack type |
| 141 * |
| 142 * @micbias_voltage: mic bias voltage at adc channel when jack is plugged in |
| 143 * |
| 144 * Based on the mic bias value passed, this function helps identify |
| 145 * the type of jack from the already delcared jack zones |
| 146 */ |
| 147 int snd_soc_jack_get_type(struct snd_soc_jack *jack, int micbias_voltage) |
| 148 { |
| 149 struct snd_soc_jack_zone *zone; |
| 150 |
| 151 list_for_each_entry(zone, &jack->jack_zones, list) { |
| 152 if (micbias_voltage >= zone->min_mv && |
| 153 micbias_voltage < zone->max_mv) |
| 154 return zone->jack_type; |
| 155 } |
| 156 return 0; |
| 157 } |
| 158 EXPORT_SYMBOL_GPL(snd_soc_jack_get_type); |
| 159 |
| 160 /** |
| 109 * snd_soc_jack_add_pins - Associate DAPM pins with an ASoC jack | 161 * snd_soc_jack_add_pins - Associate DAPM pins with an ASoC jack |
| 110 * | 162 * |
| 111 * @jack: ASoC jack | 163 * @jack: ASoC jack |
| 112 * @count: Number of pins | 164 * @count: Number of pins |
| 113 * @pins: Array of pins | 165 * @pins: Array of pins |
| 114 * | 166 * |
| 115 * After this function has been called the DAPM pins specified in the | 167 * After this function has been called the DAPM pins specified in the |
| 116 * pins array will have their status updated to reflect the current | 168 * pins array will have their status updated to reflect the current |
| 117 * state of the jack whenever the jack status is updated. | 169 * state of the jack whenever the jack status is updated. |
| 118 */ | 170 */ |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 181 EXPORT_SYMBOL_GPL(snd_soc_jack_notifier_unregister); | 233 EXPORT_SYMBOL_GPL(snd_soc_jack_notifier_unregister); |
| 182 | 234 |
| 183 #ifdef CONFIG_GPIOLIB | 235 #ifdef CONFIG_GPIOLIB |
| 184 /* gpio detect */ | 236 /* gpio detect */ |
| 185 static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio) | 237 static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio) |
| 186 { | 238 { |
| 187 struct snd_soc_jack *jack = gpio->jack; | 239 struct snd_soc_jack *jack = gpio->jack; |
| 188 int enable; | 240 int enable; |
| 189 int report; | 241 int report; |
| 190 | 242 |
| 191 » enable = gpio_get_value(gpio->gpio); | 243 » enable = gpio_get_value_cansleep(gpio->gpio); |
| 192 if (gpio->invert) | 244 if (gpio->invert) |
| 193 enable = !enable; | 245 enable = !enable; |
| 194 | 246 |
| 195 if (enable) | 247 if (enable) |
| 196 report = gpio->report; | 248 report = gpio->report; |
| 197 else | 249 else |
| 198 report = 0; | 250 report = 0; |
| 199 | 251 |
| 200 if (gpio->jack_status_check) | 252 if (gpio->jack_status_check) |
| 201 report = gpio->jack_status_check(); | 253 report = gpio->jack_status_check(); |
| 202 | 254 |
| 203 snd_soc_jack_report(jack, report, gpio->report); | 255 snd_soc_jack_report(jack, report, gpio->report); |
| 204 } | 256 } |
| 205 | 257 |
| 206 /* irq handler for gpio pin */ | 258 /* irq handler for gpio pin */ |
| 207 static irqreturn_t gpio_handler(int irq, void *data) | 259 static irqreturn_t gpio_handler(int irq, void *data) |
| 208 { | 260 { |
| 209 struct snd_soc_jack_gpio *gpio = data; | 261 struct snd_soc_jack_gpio *gpio = data; |
| 262 struct device *dev = gpio->jack->codec->card->dev; |
| 263 |
| 264 trace_snd_soc_jack_irq(gpio->name); |
| 265 |
| 266 if (device_may_wakeup(dev)) |
| 267 pm_wakeup_event(dev, gpio->debounce_time + 50); |
| 210 | 268 |
| 211 schedule_delayed_work(&gpio->work, | 269 schedule_delayed_work(&gpio->work, |
| 212 msecs_to_jiffies(gpio->debounce_time)); | 270 msecs_to_jiffies(gpio->debounce_time)); |
| 213 | 271 |
| 214 return IRQ_HANDLED; | 272 return IRQ_HANDLED; |
| 215 } | 273 } |
| 216 | 274 |
| 217 /* gpio work */ | 275 /* gpio work */ |
| 218 static void gpio_work(struct work_struct *work) | 276 static void gpio_work(struct work_struct *work) |
| 219 { | 277 { |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 256 if (ret) | 314 if (ret) |
| 257 goto undo; | 315 goto undo; |
| 258 | 316 |
| 259 ret = gpio_direction_input(gpios[i].gpio); | 317 ret = gpio_direction_input(gpios[i].gpio); |
| 260 if (ret) | 318 if (ret) |
| 261 goto err; | 319 goto err; |
| 262 | 320 |
| 263 INIT_DELAYED_WORK(&gpios[i].work, gpio_work); | 321 INIT_DELAYED_WORK(&gpios[i].work, gpio_work); |
| 264 gpios[i].jack = jack; | 322 gpios[i].jack = jack; |
| 265 | 323 |
| 266 » » ret = request_irq(gpio_to_irq(gpios[i].gpio), | 324 » » ret = request_any_context_irq(gpio_to_irq(gpios[i].gpio), |
| 267 » » » » gpio_handler, | 325 » » » » » gpio_handler, |
| 268 » » » » IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, | 326 » » » » » IRQF_TRIGGER_RISING | |
| 269 » » » » jack->codec->dev->driver->name, | 327 » » » » » IRQF_TRIGGER_FALLING, |
| 270 » » » » &gpios[i]); | 328 » » » » » jack->codec->dev->driver->name, |
| 329 » » » » » &gpios[i]); |
| 271 if (ret) | 330 if (ret) |
| 272 goto err; | 331 goto err; |
| 273 | 332 |
| 274 #ifdef CONFIG_GPIO_SYSFS | 333 #ifdef CONFIG_GPIO_SYSFS |
| 275 /* Expose GPIO value over sysfs for diagnostic purposes */ | 334 /* Expose GPIO value over sysfs for diagnostic purposes */ |
| 276 gpio_export(gpios[i].gpio, false); | 335 gpio_export(gpios[i].gpio, false); |
| 277 #endif | 336 #endif |
| 278 | 337 |
| 279 /* Update initial jack status */ | 338 /* Update initial jack status */ |
| 280 snd_soc_jack_gpio_detect(&gpios[i]); | 339 snd_soc_jack_gpio_detect(&gpios[i]); |
| (...skipping 29 matching lines...) Expand all Loading... |
| 310 gpio_unexport(gpios[i].gpio); | 369 gpio_unexport(gpios[i].gpio); |
| 311 #endif | 370 #endif |
| 312 free_irq(gpio_to_irq(gpios[i].gpio), &gpios[i]); | 371 free_irq(gpio_to_irq(gpios[i].gpio), &gpios[i]); |
| 313 cancel_delayed_work_sync(&gpios[i].work); | 372 cancel_delayed_work_sync(&gpios[i].work); |
| 314 gpio_free(gpios[i].gpio); | 373 gpio_free(gpios[i].gpio); |
| 315 gpios[i].jack = NULL; | 374 gpios[i].jack = NULL; |
| 316 } | 375 } |
| 317 } | 376 } |
| 318 EXPORT_SYMBOL_GPL(snd_soc_jack_free_gpios); | 377 EXPORT_SYMBOL_GPL(snd_soc_jack_free_gpios); |
| 319 #endif /* CONFIG_GPIOLIB */ | 378 #endif /* CONFIG_GPIOLIB */ |
| OLD | NEW |