| Mark Brown | 8a2cd61 | 2009-01-07 17:31:10 +0000 | [diff] [blame] | 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> | 
| Lopez Cruz, Misael | ec67624 | 2009-03-03 15:25:04 -0600 | [diff] [blame] | 16 | #include <linux/gpio.h> | 
 | 17 | #include <linux/interrupt.h> | 
 | 18 | #include <linux/workqueue.h> | 
 | 19 | #include <linux/delay.h> | 
| Mark Brown | 3028eb8 | 2010-12-05 12:22:46 +0000 | [diff] [blame] | 20 | #include <trace/events/asoc.h> | 
| Mark Brown | 8a2cd61 | 2009-01-07 17:31:10 +0000 | [diff] [blame] | 21 |  | 
 | 22 | /** | 
 | 23 |  * snd_soc_jack_new - Create a new jack | 
 | 24 |  * @card:  ASoC card | 
 | 25 |  * @id:    an identifying string for this jack | 
 | 26 |  * @type:  a bitmask of enum snd_jack_type values that can be detected by | 
 | 27 |  *         this jack | 
 | 28 |  * @jack:  structure to use for the jack | 
 | 29 |  * | 
 | 30 |  * Creates a new jack object. | 
 | 31 |  * | 
 | 32 |  * Returns zero if successful, or a negative error code on failure. | 
 | 33 |  * On success jack will be initialised. | 
 | 34 |  */ | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 35 | int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type, | 
| Mark Brown | 8a2cd61 | 2009-01-07 17:31:10 +0000 | [diff] [blame] | 36 | 		     struct snd_soc_jack *jack) | 
 | 37 | { | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 38 | 	jack->codec = codec; | 
| Mark Brown | 8a2cd61 | 2009-01-07 17:31:10 +0000 | [diff] [blame] | 39 | 	INIT_LIST_HEAD(&jack->pins); | 
| Vinod Koul | fa9879e | 2011-02-09 14:44:17 +0530 | [diff] [blame] | 40 | 	INIT_LIST_HEAD(&jack->jack_zones); | 
| Mark Brown | d5021ec | 2010-03-22 12:06:30 +0000 | [diff] [blame] | 41 | 	BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier); | 
| Mark Brown | 8a2cd61 | 2009-01-07 17:31:10 +0000 | [diff] [blame] | 42 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 43 | 	return snd_jack_new(codec->card->snd_card, id, type, &jack->jack); | 
| Mark Brown | 8a2cd61 | 2009-01-07 17:31:10 +0000 | [diff] [blame] | 44 | } | 
 | 45 | EXPORT_SYMBOL_GPL(snd_soc_jack_new); | 
 | 46 |  | 
 | 47 | /** | 
 | 48 |  * snd_soc_jack_report - Report the current status for a jack | 
 | 49 |  * | 
 | 50 |  * @jack:   the jack | 
 | 51 |  * @status: a bitmask of enum snd_jack_type values that are currently detected. | 
 | 52 |  * @mask:   a bitmask of enum snd_jack_type values that being reported. | 
 | 53 |  * | 
 | 54 |  * If configured using snd_soc_jack_add_pins() then the associated | 
 | 55 |  * DAPM pins will be enabled or disabled as appropriate and DAPM | 
 | 56 |  * synchronised. | 
 | 57 |  * | 
 | 58 |  * Note: This function uses mutexes and should be called from a | 
 | 59 |  * context which can sleep (such as a workqueue). | 
 | 60 |  */ | 
 | 61 | void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) | 
 | 62 | { | 
| Julia Lawall | 4f06617 | 2009-10-17 08:32:56 +0200 | [diff] [blame] | 63 | 	struct snd_soc_codec *codec; | 
| Liam Girdwood | ce6120c | 2010-11-05 15:53:46 +0200 | [diff] [blame] | 64 | 	struct snd_soc_dapm_context *dapm; | 
| Mark Brown | 8a2cd61 | 2009-01-07 17:31:10 +0000 | [diff] [blame] | 65 | 	struct snd_soc_jack_pin *pin; | 
 | 66 | 	int enable; | 
 | 67 | 	int oldstatus; | 
 | 68 |  | 
| Mark Brown | 3028eb8 | 2010-12-05 12:22:46 +0000 | [diff] [blame] | 69 | 	trace_snd_soc_jack_report(jack, mask, status); | 
 | 70 |  | 
| Mark Brown | 3a278a0 | 2010-03-29 20:31:14 +0100 | [diff] [blame] | 71 | 	if (!jack) | 
| Mark Brown | 8a2cd61 | 2009-01-07 17:31:10 +0000 | [diff] [blame] | 72 | 		return; | 
| Mark Brown | 3a278a0 | 2010-03-29 20:31:14 +0100 | [diff] [blame] | 73 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 74 | 	codec = jack->codec; | 
| Liam Girdwood | ce6120c | 2010-11-05 15:53:46 +0200 | [diff] [blame] | 75 | 	dapm =  &codec->dapm; | 
| Mark Brown | 8a2cd61 | 2009-01-07 17:31:10 +0000 | [diff] [blame] | 76 |  | 
 | 77 | 	mutex_lock(&codec->mutex); | 
 | 78 |  | 
 | 79 | 	oldstatus = jack->status; | 
 | 80 |  | 
 | 81 | 	jack->status &= ~mask; | 
| Janusz Krzysztofik | 178b699 | 2009-07-24 02:48:57 +0200 | [diff] [blame] | 82 | 	jack->status |= status & mask; | 
| Mark Brown | 8a2cd61 | 2009-01-07 17:31:10 +0000 | [diff] [blame] | 83 |  | 
| Janusz Krzysztofik | 178b699 | 2009-07-24 02:48:57 +0200 | [diff] [blame] | 84 | 	/* The DAPM sync is expensive enough to be worth skipping. | 
 | 85 | 	 * However, empty mask means pin synchronization is desired. */ | 
 | 86 | 	if (mask && (jack->status == oldstatus)) | 
| Mark Brown | 8a2cd61 | 2009-01-07 17:31:10 +0000 | [diff] [blame] | 87 | 		goto out; | 
 | 88 |  | 
| Mark Brown | 3028eb8 | 2010-12-05 12:22:46 +0000 | [diff] [blame] | 89 | 	trace_snd_soc_jack_notify(jack, status); | 
 | 90 |  | 
| Mark Brown | 8a2cd61 | 2009-01-07 17:31:10 +0000 | [diff] [blame] | 91 | 	list_for_each_entry(pin, &jack->pins, list) { | 
| Janusz Krzysztofik | 178b699 | 2009-07-24 02:48:57 +0200 | [diff] [blame] | 92 | 		enable = pin->mask & jack->status; | 
| Mark Brown | 8a2cd61 | 2009-01-07 17:31:10 +0000 | [diff] [blame] | 93 |  | 
 | 94 | 		if (pin->invert) | 
 | 95 | 			enable = !enable; | 
 | 96 |  | 
 | 97 | 		if (enable) | 
| Liam Girdwood | ce6120c | 2010-11-05 15:53:46 +0200 | [diff] [blame] | 98 | 			snd_soc_dapm_enable_pin(dapm, pin->pin); | 
| Mark Brown | 8a2cd61 | 2009-01-07 17:31:10 +0000 | [diff] [blame] | 99 | 		else | 
| Liam Girdwood | ce6120c | 2010-11-05 15:53:46 +0200 | [diff] [blame] | 100 | 			snd_soc_dapm_disable_pin(dapm, pin->pin); | 
| Mark Brown | 8a2cd61 | 2009-01-07 17:31:10 +0000 | [diff] [blame] | 101 | 	} | 
 | 102 |  | 
| Mark Brown | d5021ec | 2010-03-22 12:06:30 +0000 | [diff] [blame] | 103 | 	/* Report before the DAPM sync to help users updating micbias status */ | 
| Mark Brown | 1d2c27f | 2011-02-22 12:42:35 -0800 | [diff] [blame] | 104 | 	blocking_notifier_call_chain(&jack->notifier, status, jack); | 
| Mark Brown | d5021ec | 2010-03-22 12:06:30 +0000 | [diff] [blame] | 105 |  | 
| Liam Girdwood | ce6120c | 2010-11-05 15:53:46 +0200 | [diff] [blame] | 106 | 	snd_soc_dapm_sync(dapm); | 
| Mark Brown | 8a2cd61 | 2009-01-07 17:31:10 +0000 | [diff] [blame] | 107 |  | 
 | 108 | 	snd_jack_report(jack->jack, status); | 
 | 109 |  | 
 | 110 | out: | 
 | 111 | 	mutex_unlock(&codec->mutex); | 
 | 112 | } | 
 | 113 | EXPORT_SYMBOL_GPL(snd_soc_jack_report); | 
 | 114 |  | 
 | 115 | /** | 
| Vinod Koul | fa9879e | 2011-02-09 14:44:17 +0530 | [diff] [blame] | 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 | /** | 
| Mark Brown | 8a2cd61 | 2009-01-07 17:31:10 +0000 | [diff] [blame] | 161 |  * snd_soc_jack_add_pins - Associate DAPM pins with an ASoC jack | 
 | 162 |  * | 
 | 163 |  * @jack:  ASoC jack | 
 | 164 |  * @count: Number of pins | 
 | 165 |  * @pins:  Array of pins | 
 | 166 |  * | 
 | 167 |  * After this function has been called the DAPM pins specified in the | 
 | 168 |  * pins array will have their status updated to reflect the current | 
 | 169 |  * state of the jack whenever the jack status is updated. | 
 | 170 |  */ | 
 | 171 | int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count, | 
 | 172 | 			  struct snd_soc_jack_pin *pins) | 
 | 173 | { | 
 | 174 | 	int i; | 
 | 175 |  | 
 | 176 | 	for (i = 0; i < count; i++) { | 
 | 177 | 		if (!pins[i].pin) { | 
 | 178 | 			printk(KERN_ERR "No name for pin %d\n", i); | 
 | 179 | 			return -EINVAL; | 
 | 180 | 		} | 
 | 181 | 		if (!pins[i].mask) { | 
 | 182 | 			printk(KERN_ERR "No mask for pin %d (%s)\n", i, | 
 | 183 | 			       pins[i].pin); | 
 | 184 | 			return -EINVAL; | 
 | 185 | 		} | 
 | 186 |  | 
 | 187 | 		INIT_LIST_HEAD(&pins[i].list); | 
 | 188 | 		list_add(&(pins[i].list), &jack->pins); | 
 | 189 | 	} | 
 | 190 |  | 
 | 191 | 	/* Update to reflect the last reported status; canned jack | 
 | 192 | 	 * implementations are likely to set their state before the | 
 | 193 | 	 * card has an opportunity to associate pins. | 
 | 194 | 	 */ | 
 | 195 | 	snd_soc_jack_report(jack, 0, 0); | 
 | 196 |  | 
 | 197 | 	return 0; | 
 | 198 | } | 
 | 199 | EXPORT_SYMBOL_GPL(snd_soc_jack_add_pins); | 
| Lopez Cruz, Misael | ec67624 | 2009-03-03 15:25:04 -0600 | [diff] [blame] | 200 |  | 
| Mark Brown | d5021ec | 2010-03-22 12:06:30 +0000 | [diff] [blame] | 201 | /** | 
 | 202 |  * snd_soc_jack_notifier_register - Register a notifier for jack status | 
 | 203 |  * | 
 | 204 |  * @jack:  ASoC jack | 
 | 205 |  * @nb:    Notifier block to register | 
 | 206 |  * | 
 | 207 |  * Register for notification of the current status of the jack.  Note | 
 | 208 |  * that it is not possible to report additional jack events in the | 
 | 209 |  * callback from the notifier, this is intended to support | 
 | 210 |  * applications such as enabling electrical detection only when a | 
 | 211 |  * mechanical detection event has occurred. | 
 | 212 |  */ | 
 | 213 | void snd_soc_jack_notifier_register(struct snd_soc_jack *jack, | 
 | 214 | 				    struct notifier_block *nb) | 
 | 215 | { | 
 | 216 | 	blocking_notifier_chain_register(&jack->notifier, nb); | 
 | 217 | } | 
 | 218 | EXPORT_SYMBOL_GPL(snd_soc_jack_notifier_register); | 
 | 219 |  | 
 | 220 | /** | 
 | 221 |  * snd_soc_jack_notifier_unregister - Unregister a notifier for jack status | 
 | 222 |  * | 
 | 223 |  * @jack:  ASoC jack | 
 | 224 |  * @nb:    Notifier block to unregister | 
 | 225 |  * | 
 | 226 |  * Stop notifying for status changes. | 
 | 227 |  */ | 
 | 228 | void snd_soc_jack_notifier_unregister(struct snd_soc_jack *jack, | 
 | 229 | 				      struct notifier_block *nb) | 
 | 230 | { | 
 | 231 | 	blocking_notifier_chain_unregister(&jack->notifier, nb); | 
 | 232 | } | 
 | 233 | EXPORT_SYMBOL_GPL(snd_soc_jack_notifier_unregister); | 
 | 234 |  | 
| Lopez Cruz, Misael | ec67624 | 2009-03-03 15:25:04 -0600 | [diff] [blame] | 235 | #ifdef CONFIG_GPIOLIB | 
 | 236 | /* gpio detect */ | 
| Mark Brown | 26bd7b4 | 2009-03-06 11:32:17 +0000 | [diff] [blame] | 237 | static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio) | 
| Lopez Cruz, Misael | ec67624 | 2009-03-03 15:25:04 -0600 | [diff] [blame] | 238 | { | 
 | 239 | 	struct snd_soc_jack *jack = gpio->jack; | 
 | 240 | 	int enable; | 
 | 241 | 	int report; | 
 | 242 |  | 
| Jarkko Nikula | 535787b | 2011-02-10 17:22:23 +0200 | [diff] [blame] | 243 | 	enable = gpio_get_value_cansleep(gpio->gpio); | 
| Lopez Cruz, Misael | ec67624 | 2009-03-03 15:25:04 -0600 | [diff] [blame] | 244 | 	if (gpio->invert) | 
 | 245 | 		enable = !enable; | 
 | 246 |  | 
 | 247 | 	if (enable) | 
 | 248 | 		report = gpio->report; | 
 | 249 | 	else | 
 | 250 | 		report = 0; | 
 | 251 |  | 
| Joonyoung Shim | c871a05 | 2009-11-12 17:14:04 +0900 | [diff] [blame] | 252 | 	if (gpio->jack_status_check) | 
 | 253 | 		report = gpio->jack_status_check(); | 
 | 254 |  | 
| Lopez Cruz, Misael | ec67624 | 2009-03-03 15:25:04 -0600 | [diff] [blame] | 255 | 	snd_soc_jack_report(jack, report, gpio->report); | 
 | 256 | } | 
 | 257 |  | 
 | 258 | /* irq handler for gpio pin */ | 
 | 259 | static irqreturn_t gpio_handler(int irq, void *data) | 
 | 260 | { | 
 | 261 | 	struct snd_soc_jack_gpio *gpio = data; | 
| Mark Brown | f9a6705 | 2010-11-14 19:25:09 +0000 | [diff] [blame] | 262 | 	struct device *dev = gpio->jack->codec->card->dev; | 
 | 263 |  | 
| Mark Brown | 3028eb8 | 2010-12-05 12:22:46 +0000 | [diff] [blame] | 264 | 	trace_snd_soc_jack_irq(gpio->name); | 
 | 265 |  | 
| Mark Brown | f9a6705 | 2010-11-14 19:25:09 +0000 | [diff] [blame] | 266 | 	if (device_may_wakeup(dev)) | 
 | 267 | 		pm_wakeup_event(dev, gpio->debounce_time + 50); | 
| Lopez Cruz, Misael | ec67624 | 2009-03-03 15:25:04 -0600 | [diff] [blame] | 268 |  | 
| Mark Brown | 4c14d78 | 2010-10-06 15:54:28 -0700 | [diff] [blame] | 269 | 	schedule_delayed_work(&gpio->work, | 
 | 270 | 			      msecs_to_jiffies(gpio->debounce_time)); | 
| Lopez Cruz, Misael | ec67624 | 2009-03-03 15:25:04 -0600 | [diff] [blame] | 271 |  | 
 | 272 | 	return IRQ_HANDLED; | 
 | 273 | } | 
 | 274 |  | 
 | 275 | /* gpio work */ | 
 | 276 | static void gpio_work(struct work_struct *work) | 
 | 277 | { | 
 | 278 | 	struct snd_soc_jack_gpio *gpio; | 
 | 279 |  | 
| Mark Brown | 4c14d78 | 2010-10-06 15:54:28 -0700 | [diff] [blame] | 280 | 	gpio = container_of(work, struct snd_soc_jack_gpio, work.work); | 
| Lopez Cruz, Misael | ec67624 | 2009-03-03 15:25:04 -0600 | [diff] [blame] | 281 | 	snd_soc_jack_gpio_detect(gpio); | 
 | 282 | } | 
 | 283 |  | 
 | 284 | /** | 
 | 285 |  * snd_soc_jack_add_gpios - Associate GPIO pins with an ASoC jack | 
 | 286 |  * | 
 | 287 |  * @jack:  ASoC jack | 
 | 288 |  * @count: number of pins | 
 | 289 |  * @gpios: array of gpio pins | 
 | 290 |  * | 
 | 291 |  * This function will request gpio, set data direction and request irq | 
 | 292 |  * for each gpio in the array. | 
 | 293 |  */ | 
 | 294 | int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, | 
 | 295 | 			struct snd_soc_jack_gpio *gpios) | 
 | 296 | { | 
 | 297 | 	int i, ret; | 
 | 298 |  | 
 | 299 | 	for (i = 0; i < count; i++) { | 
 | 300 | 		if (!gpio_is_valid(gpios[i].gpio)) { | 
 | 301 | 			printk(KERN_ERR "Invalid gpio %d\n", | 
 | 302 | 				gpios[i].gpio); | 
 | 303 | 			ret = -EINVAL; | 
 | 304 | 			goto undo; | 
 | 305 | 		} | 
 | 306 | 		if (!gpios[i].name) { | 
 | 307 | 			printk(KERN_ERR "No name for gpio %d\n", | 
 | 308 | 				gpios[i].gpio); | 
 | 309 | 			ret = -EINVAL; | 
 | 310 | 			goto undo; | 
 | 311 | 		} | 
 | 312 |  | 
 | 313 | 		ret = gpio_request(gpios[i].gpio, gpios[i].name); | 
 | 314 | 		if (ret) | 
 | 315 | 			goto undo; | 
 | 316 |  | 
 | 317 | 		ret = gpio_direction_input(gpios[i].gpio); | 
 | 318 | 		if (ret) | 
 | 319 | 			goto err; | 
 | 320 |  | 
| Mark Brown | 4c14d78 | 2010-10-06 15:54:28 -0700 | [diff] [blame] | 321 | 		INIT_DELAYED_WORK(&gpios[i].work, gpio_work); | 
| Lars-Peter Clausen | b8e22c1 | 2009-07-31 21:15:25 +0200 | [diff] [blame] | 322 | 		gpios[i].jack = jack; | 
 | 323 |  | 
| Mark Brown | 3f58fd8 | 2010-11-03 09:35:31 -0400 | [diff] [blame] | 324 | 		ret = request_any_context_irq(gpio_to_irq(gpios[i].gpio), | 
 | 325 | 					      gpio_handler, | 
 | 326 | 					      IRQF_TRIGGER_RISING | | 
 | 327 | 					      IRQF_TRIGGER_FALLING, | 
 | 328 | 					      jack->codec->dev->driver->name, | 
 | 329 | 					      &gpios[i]); | 
| Lopez Cruz, Misael | ec67624 | 2009-03-03 15:25:04 -0600 | [diff] [blame] | 330 | 		if (ret) | 
 | 331 | 			goto err; | 
 | 332 |  | 
| Mark Brown | 7887ab3 | 2011-02-17 16:35:55 -0800 | [diff] [blame] | 333 | 		if (gpios[i].wake) { | 
 | 334 | 			ret = set_irq_wake(gpio_to_irq(gpios[i].gpio), 1); | 
 | 335 | 			if (ret != 0) | 
 | 336 | 				printk(KERN_ERR | 
 | 337 | 				  "Failed to mark GPIO %d as wake source: %d\n", | 
 | 338 | 					gpios[i].gpio, ret); | 
 | 339 | 		} | 
 | 340 |  | 
| Janusz Krzysztofik | 178b699 | 2009-07-24 02:48:57 +0200 | [diff] [blame] | 341 | #ifdef CONFIG_GPIO_SYSFS | 
 | 342 | 		/* Expose GPIO value over sysfs for diagnostic purposes */ | 
 | 343 | 		gpio_export(gpios[i].gpio, false); | 
 | 344 | #endif | 
 | 345 |  | 
| Janusz Krzysztofik | 178b699 | 2009-07-24 02:48:57 +0200 | [diff] [blame] | 346 | 		/* Update initial jack status */ | 
 | 347 | 		snd_soc_jack_gpio_detect(&gpios[i]); | 
| Lopez Cruz, Misael | ec67624 | 2009-03-03 15:25:04 -0600 | [diff] [blame] | 348 | 	} | 
 | 349 |  | 
 | 350 | 	return 0; | 
 | 351 |  | 
 | 352 | err: | 
 | 353 | 	gpio_free(gpios[i].gpio); | 
 | 354 | undo: | 
 | 355 | 	snd_soc_jack_free_gpios(jack, i, gpios); | 
 | 356 |  | 
 | 357 | 	return ret; | 
 | 358 | } | 
 | 359 | EXPORT_SYMBOL_GPL(snd_soc_jack_add_gpios); | 
 | 360 |  | 
 | 361 | /** | 
 | 362 |  * snd_soc_jack_free_gpios - Release GPIO pins' resources of an ASoC jack | 
 | 363 |  * | 
 | 364 |  * @jack:  ASoC jack | 
 | 365 |  * @count: number of pins | 
 | 366 |  * @gpios: array of gpio pins | 
 | 367 |  * | 
 | 368 |  * Release gpio and irq resources for gpio pins associated with an ASoC jack. | 
 | 369 |  */ | 
 | 370 | void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count, | 
 | 371 | 			struct snd_soc_jack_gpio *gpios) | 
 | 372 | { | 
 | 373 | 	int i; | 
 | 374 |  | 
 | 375 | 	for (i = 0; i < count; i++) { | 
| Janusz Krzysztofik | 178b699 | 2009-07-24 02:48:57 +0200 | [diff] [blame] | 376 | #ifdef CONFIG_GPIO_SYSFS | 
 | 377 | 		gpio_unexport(gpios[i].gpio); | 
 | 378 | #endif | 
| Lopez Cruz, Misael | ec67624 | 2009-03-03 15:25:04 -0600 | [diff] [blame] | 379 | 		free_irq(gpio_to_irq(gpios[i].gpio), &gpios[i]); | 
| Mark Brown | 4c14d78 | 2010-10-06 15:54:28 -0700 | [diff] [blame] | 380 | 		cancel_delayed_work_sync(&gpios[i].work); | 
| Lopez Cruz, Misael | ec67624 | 2009-03-03 15:25:04 -0600 | [diff] [blame] | 381 | 		gpio_free(gpios[i].gpio); | 
 | 382 | 		gpios[i].jack = NULL; | 
 | 383 | 	} | 
 | 384 | } | 
 | 385 | EXPORT_SYMBOL_GPL(snd_soc_jack_free_gpios); | 
 | 386 | #endif	/* CONFIG_GPIOLIB */ |