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