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 | */ |
34 | void 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 || !jack->jack) |
41 | return; |
42 | trace_snd_soc_jack_report(jack, mask, val: 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, val: 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->pin); |
61 | else |
62 | snd_soc_dapm_disable_pin(dapm, pin: 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(nh: &jack->notifier, val: jack->status, v: jack); |
70 | |
71 | if (sync) |
72 | snd_soc_dapm_sync(dapm); |
73 | |
74 | snd_jack_report(jack: jack->jack, status: jack->status); |
75 | |
76 | mutex_unlock(lock: &jack->mutex); |
77 | } |
78 | EXPORT_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 | */ |
90 | int 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(list: &zones[i].list); |
97 | list_add(new: &(zones[i].list), head: &jack->jack_zones); |
98 | } |
99 | return 0; |
100 | } |
101 | EXPORT_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 | */ |
113 | int 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 | } |
124 | EXPORT_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 | */ |
137 | int 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(list: &pins[i].list); |
155 | list_add(new: &(pins[i].list), head: &jack->pins); |
156 | snd_jack_add_new_kctl(jack: jack->jack, name: pins[i].pin, mask: 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 | } |
167 | EXPORT_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 | */ |
181 | void snd_soc_jack_notifier_register(struct snd_soc_jack *jack, |
182 | struct notifier_block *nb) |
183 | { |
184 | blocking_notifier_chain_register(nh: &jack->notifier, nb); |
185 | } |
186 | EXPORT_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 | */ |
196 | void snd_soc_jack_notifier_unregister(struct snd_soc_jack *jack, |
197 | struct notifier_block *nb) |
198 | { |
199 | blocking_notifier_chain_unregister(nh: &jack->notifier, nb); |
200 | } |
201 | EXPORT_SYMBOL_GPL(snd_soc_jack_notifier_unregister); |
202 | |
203 | #ifdef CONFIG_GPIOLIB |
204 | struct jack_gpio_tbl { |
205 | int count; |
206 | struct snd_soc_jack *jack; |
207 | struct snd_soc_jack_gpio *gpios; |
208 | }; |
209 | |
210 | /* gpio detect */ |
211 | static 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(desc: 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 */ |
233 | static 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(name: gpio->name); |
239 | |
240 | if (device_may_wakeup(dev)) |
241 | pm_wakeup_event(dev, msec: gpio->debounce_time + 50); |
242 | |
243 | queue_delayed_work(wq: system_power_efficient_wq, dwork: &gpio->work, |
244 | delay: msecs_to_jiffies(m: gpio->debounce_time)); |
245 | |
246 | return IRQ_HANDLED; |
247 | } |
248 | |
249 | /* gpio work */ |
250 | static 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 | |
258 | static 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(wq: system_power_efficient_wq, dwork: &gpio->work, delay: 0); |
273 | break; |
274 | } |
275 | |
276 | return NOTIFY_DONE; |
277 | } |
278 | |
279 | static 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(desc: gpios[i].desc); |
286 | unregister_pm_notifier(nb: &gpios[i].pm_notifier); |
287 | free_irq(gpiod_to_irq(desc: gpios[i].desc), &gpios[i]); |
288 | cancel_delayed_work_sync(dwork: &gpios[i].work); |
289 | gpiod_put(desc: gpios[i].desc); |
290 | gpios[i].jack = NULL; |
291 | } |
292 | } |
293 | |
294 | static void jack_devres_free_gpios(struct device *dev, void *res) |
295 | { |
296 | struct jack_gpio_tbl *tbl = res; |
297 | |
298 | jack_free_gpios(jack: tbl->jack, count: tbl->count, gpios: 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 | */ |
311 | int 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(dev: gpios[i].gpiod_dev, |
338 | con_id: gpios[i].name, |
339 | idx: gpios[i].idx, flags: GPIOD_IN); |
340 | if (IS_ERR(ptr: gpios[i].desc)) { |
341 | ret = PTR_ERR(ptr: 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(number: 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(gpio: gpios[i].gpio, GPIOF_IN, |
358 | label: gpios[i].name); |
359 | if (ret) |
360 | goto undo; |
361 | |
362 | gpios[i].desc = gpio_to_desc(gpio: gpios[i].gpio); |
363 | } |
364 | got_gpio: |
365 | INIT_DELAYED_WORK(&gpios[i].work, gpio_work); |
366 | gpios[i].jack = jack; |
367 | |
368 | ret = request_any_context_irq(irq: gpiod_to_irq(desc: gpios[i].desc), |
369 | handler: gpio_handler, |
370 | IRQF_SHARED | |
371 | IRQF_TRIGGER_RISING | |
372 | IRQF_TRIGGER_FALLING, |
373 | name: gpios[i].name, |
374 | dev_id: &gpios[i]); |
375 | if (ret < 0) |
376 | goto err; |
377 | |
378 | if (gpios[i].wake) { |
379 | ret = irq_set_irq_wake(irq: gpiod_to_irq(desc: gpios[i].desc), on: 1); |
380 | if (ret != 0) |
381 | dev_err(jack->card->dev, |
382 | "ASoC: Failed to mark GPIO at index %d as wake source: %d\n" , |
383 | i, ret); |
384 | } |
385 | |
386 | /* |
387 | * Register PM notifier so we do not miss state transitions |
388 | * happening while system is asleep. |
389 | */ |
390 | gpios[i].pm_notifier.notifier_call = snd_soc_jack_pm_notifier; |
391 | register_pm_notifier(nb: &gpios[i].pm_notifier); |
392 | |
393 | /* Expose GPIO value over sysfs for diagnostic purposes */ |
394 | gpiod_export(desc: gpios[i].desc, direction_may_change: false); |
395 | |
396 | /* Update initial jack status */ |
397 | schedule_delayed_work(dwork: &gpios[i].work, |
398 | delay: msecs_to_jiffies(m: gpios[i].debounce_time)); |
399 | } |
400 | |
401 | devres_add(dev: jack->card->dev, res: tbl); |
402 | return 0; |
403 | |
404 | err: |
405 | gpio_free(gpio: gpios[i].gpio); |
406 | undo: |
407 | jack_free_gpios(jack, count: i, gpios); |
408 | devres_free(res: tbl); |
409 | |
410 | return ret; |
411 | } |
412 | EXPORT_SYMBOL_GPL(snd_soc_jack_add_gpios); |
413 | |
414 | /** |
415 | * snd_soc_jack_add_gpiods - Associate GPIO descriptor pins with an ASoC jack |
416 | * |
417 | * @gpiod_dev: GPIO consumer device |
418 | * @jack: ASoC jack |
419 | * @count: number of pins |
420 | * @gpios: array of gpio pins |
421 | * |
422 | * This function will request gpio, set data direction and request irq |
423 | * for each gpio in the array. |
424 | */ |
425 | int snd_soc_jack_add_gpiods(struct device *gpiod_dev, |
426 | struct snd_soc_jack *jack, |
427 | int count, struct snd_soc_jack_gpio *gpios) |
428 | { |
429 | int i; |
430 | |
431 | for (i = 0; i < count; i++) |
432 | gpios[i].gpiod_dev = gpiod_dev; |
433 | |
434 | return snd_soc_jack_add_gpios(jack, count, gpios); |
435 | } |
436 | EXPORT_SYMBOL_GPL(snd_soc_jack_add_gpiods); |
437 | |
438 | /** |
439 | * snd_soc_jack_free_gpios - Release GPIO pins' resources of an ASoC jack |
440 | * |
441 | * @jack: ASoC jack |
442 | * @count: number of pins |
443 | * @gpios: array of gpio pins |
444 | * |
445 | * Release gpio and irq resources for gpio pins associated with an ASoC jack. |
446 | */ |
447 | void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count, |
448 | struct snd_soc_jack_gpio *gpios) |
449 | { |
450 | jack_free_gpios(jack, count, gpios); |
451 | devres_destroy(dev: jack->card->dev, release: jack_devres_free_gpios, NULL, NULL); |
452 | } |
453 | EXPORT_SYMBOL_GPL(snd_soc_jack_free_gpios); |
454 | #endif /* CONFIG_GPIOLIB */ |
455 | |