diff options
Diffstat (limited to 'sound/soc/soc-jack.c')
| -rw-r--r-- | sound/soc/soc-jack.c | 143 |
1 files changed, 92 insertions, 51 deletions
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index fcab80b36a3..d0d98810af9 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -14,14 +14,16 @@ #include <sound/jack.h> #include <sound/soc.h> #include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/interrupt.h> #include <linux/workqueue.h> #include <linux/delay.h> +#include <linux/export.h> #include <trace/events/asoc.h> /** * snd_soc_jack_new - Create a new jack - * @card: ASoC card + * @codec: ASoC codec * @id: an identifying string for this jack * @type: a bitmask of enum snd_jack_type values that can be detected by * this jack @@ -35,6 +37,7 @@ int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type, struct snd_soc_jack *jack) { + mutex_init(&jack->mutex); jack->codec = codec; INIT_LIST_HEAD(&jack->pins); INIT_LIST_HEAD(&jack->jack_zones); @@ -63,8 +66,8 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) struct snd_soc_codec *codec; struct snd_soc_dapm_context *dapm; struct snd_soc_jack_pin *pin; + unsigned int sync = 0; int enable; - int oldstatus; trace_snd_soc_jack_report(jack, mask, status); @@ -74,18 +77,11 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) codec = jack->codec; dapm = &codec->dapm; - mutex_lock(&codec->mutex); - - oldstatus = jack->status; + mutex_lock(&jack->mutex); jack->status &= ~mask; jack->status |= status & mask; - /* The DAPM sync is expensive enough to be worth skipping. - * However, empty mask means pin synchronization is desired. */ - if (mask && (jack->status == oldstatus)) - goto out; - trace_snd_soc_jack_notify(jack, status); list_for_each_entry(pin, &jack->pins, list) { @@ -98,17 +94,20 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) snd_soc_dapm_enable_pin(dapm, pin->pin); else snd_soc_dapm_disable_pin(dapm, pin->pin); + + /* we need to sync for this case only */ + sync = 1; } /* Report before the DAPM sync to help users updating micbias status */ - blocking_notifier_call_chain(&jack->notifier, status, jack); + blocking_notifier_call_chain(&jack->notifier, jack->status, jack); - snd_soc_dapm_sync(dapm); + if (sync) + snd_soc_dapm_sync(dapm); - snd_jack_report(jack->jack, status); + snd_jack_report(jack->jack, jack->status); -out: - mutex_unlock(&codec->mutex); + mutex_unlock(&jack->mutex); } EXPORT_SYMBOL_GPL(snd_soc_jack_report); @@ -137,12 +136,13 @@ EXPORT_SYMBOL_GPL(snd_soc_jack_add_zones); /** * snd_soc_jack_get_type - Based on the mic bias value, this function returns - * the type of jack from the zones delcared in the jack type + * the type of jack from the zones declared in the jack type * + * @jack: ASoC jack * @micbias_voltage: mic bias voltage at adc channel when jack is plugged in * * Based on the mic bias value passed, this function helps identify - * the type of jack from the already delcared jack zones + * the type of jack from the already declared jack zones */ int snd_soc_jack_get_type(struct snd_soc_jack *jack, int micbias_voltage) { @@ -175,12 +175,13 @@ int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count, for (i = 0; i < count; i++) { if (!pins[i].pin) { - printk(KERN_ERR "No name for pin %d\n", i); + dev_err(jack->codec->dev, "ASoC: No name for pin %d\n", + i); return -EINVAL; } if (!pins[i].mask) { - printk(KERN_ERR "No mask for pin %d (%s)\n", i, - pins[i].pin); + dev_err(jack->codec->dev, "ASoC: No mask for pin %d" + " (%s)\n", i, pins[i].pin); return -EINVAL; } @@ -240,7 +241,7 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio) int enable; int report; - enable = gpio_get_value_cansleep(gpio->gpio); + enable = gpiod_get_value_cansleep(gpio->desc); if (gpio->invert) enable = !enable; @@ -250,7 +251,7 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio) report = 0; if (gpio->jack_status_check) - report = gpio->jack_status_check(); + report = gpio->jack_status_check(gpio->data); snd_soc_jack_report(jack, report, gpio->report); } @@ -266,7 +267,7 @@ static irqreturn_t gpio_handler(int irq, void *data) if (device_may_wakeup(dev)) pm_wakeup_event(dev, gpio->debounce_time + 50); - schedule_delayed_work(&gpio->work, + queue_delayed_work(system_power_efficient_wq, &gpio->work, msecs_to_jiffies(gpio->debounce_time)); return IRQ_HANDLED; @@ -297,54 +298,72 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, int i, ret; for (i = 0; i < count; i++) { - if (!gpio_is_valid(gpios[i].gpio)) { - printk(KERN_ERR "Invalid gpio %d\n", - gpios[i].gpio); - ret = -EINVAL; - goto undo; - } if (!gpios[i].name) { - printk(KERN_ERR "No name for gpio %d\n", - gpios[i].gpio); + dev_err(jack->codec->dev, + "ASoC: No name for gpio at index %d\n", i); ret = -EINVAL; goto undo; } - ret = gpio_request(gpios[i].gpio, gpios[i].name); - if (ret) - goto undo; + if (gpios[i].gpiod_dev) { + /* GPIO descriptor */ + gpios[i].desc = gpiod_get_index(gpios[i].gpiod_dev, + gpios[i].name, + gpios[i].idx); + if (IS_ERR(gpios[i].desc)) { + ret = PTR_ERR(gpios[i].desc); + dev_err(gpios[i].gpiod_dev, + "ASoC: Cannot get gpio at index %d: %d", + i, ret); + goto undo; + } + } else { + /* legacy GPIO number */ + if (!gpio_is_valid(gpios[i].gpio)) { + dev_err(jack->codec->dev, + "ASoC: Invalid gpio %d\n", + gpios[i].gpio); + ret = -EINVAL; + goto undo; + } + + ret = gpio_request(gpios[i].gpio, gpios[i].name); + if (ret) + goto undo; + + gpios[i].desc = gpio_to_desc(gpios[i].gpio); + } - ret = gpio_direction_input(gpios[i].gpio); + ret = gpiod_direction_input(gpios[i].desc); if (ret) goto err; INIT_DELAYED_WORK(&gpios[i].work, gpio_work); gpios[i].jack = jack; - ret = request_any_context_irq(gpio_to_irq(gpios[i].gpio), + ret = request_any_context_irq(gpiod_to_irq(gpios[i].desc), gpio_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - jack->codec->dev->driver->name, + gpios[i].name, &gpios[i]); - if (ret) + if (ret < 0) goto err; if (gpios[i].wake) { - ret = set_irq_wake(gpio_to_irq(gpios[i].gpio), 1); + ret = irq_set_irq_wake(gpiod_to_irq(gpios[i].desc), 1); if (ret != 0) - printk(KERN_ERR - "Failed to mark GPIO %d as wake source: %d\n", - gpios[i].gpio, ret); + dev_err(jack->codec->dev, + "ASoC: Failed to mark GPIO at index %d as wake source: %d\n", + i, ret); } -#ifdef CONFIG_GPIO_SYSFS /* Expose GPIO value over sysfs for diagnostic purposes */ - gpio_export(gpios[i].gpio, false); -#endif + gpiod_export(gpios[i].desc, false); /* Update initial jack status */ - snd_soc_jack_gpio_detect(&gpios[i]); + schedule_delayed_work(&gpios[i].work, + msecs_to_jiffies(gpios[i].debounce_time)); } return 0; @@ -359,6 +378,30 @@ undo: EXPORT_SYMBOL_GPL(snd_soc_jack_add_gpios); /** + * snd_soc_jack_add_gpiods - Associate GPIO descriptor pins with an ASoC jack + * + * @gpiod_dev: GPIO consumer device + * @jack: ASoC jack + * @count: number of pins + * @gpios: array of gpio pins + * + * This function will request gpio, set data direction and request irq + * for each gpio in the array. + */ +int snd_soc_jack_add_gpiods(struct device *gpiod_dev, + struct snd_soc_jack *jack, + int count, struct snd_soc_jack_gpio *gpios) +{ + int i; + + for (i = 0; i < count; i++) + gpios[i].gpiod_dev = gpiod_dev; + + return snd_soc_jack_add_gpios(jack, count, gpios); +} +EXPORT_SYMBOL_GPL(snd_soc_jack_add_gpiods); + +/** * snd_soc_jack_free_gpios - Release GPIO pins' resources of an ASoC jack * * @jack: ASoC jack @@ -373,12 +416,10 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count, int i; for (i = 0; i < count; i++) { -#ifdef CONFIG_GPIO_SYSFS - gpio_unexport(gpios[i].gpio); -#endif - free_irq(gpio_to_irq(gpios[i].gpio), &gpios[i]); + gpiod_unexport(gpios[i].desc); + free_irq(gpiod_to_irq(gpios[i].desc), &gpios[i]); cancel_delayed_work_sync(&gpios[i].work); - gpio_free(gpios[i].gpio); + gpiod_put(gpios[i].desc); gpios[i].jack = NULL; } } |
