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