~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/soc-jack.c

Version: ~ [ linux-5.11-rc3 ] ~ [ linux-5.10.7 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.89 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.167 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.215 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.251 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.251 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.85 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp