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