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 <sound/soc-dapm.h>
  17#include <linux/gpio.h>
  18#include <linux/interrupt.h>
  19#include <linux/workqueue.h>
  20#include <linux/delay.h>
  21
  22/**
  23 * snd_soc_jack_new - Create a new jack
  24 * @card:  ASoC card
  25 * @id:    an identifying string for this jack
  26 * @type:  a bitmask of enum snd_jack_type values that can be detected by
  27 *         this jack
  28 * @jack:  structure to use for the jack
  29 *
  30 * Creates a new jack object.
  31 *
  32 * Returns zero if successful, or a negative error code on failure.
  33 * On success jack will be initialised.
  34 */
  35int snd_soc_jack_new(struct snd_soc_card *card, const char *id, int type,
  36                     struct snd_soc_jack *jack)
  37{
  38        jack->card = card;
  39        INIT_LIST_HEAD(&jack->pins);
  40
  41        return snd_jack_new(card->codec->card, id, type, &jack->jack);
  42}
  43EXPORT_SYMBOL_GPL(snd_soc_jack_new);
  44
  45/**
  46 * snd_soc_jack_report - Report the current status for a jack
  47 *
  48 * @jack:   the jack
  49 * @status: a bitmask of enum snd_jack_type values that are currently detected.
  50 * @mask:   a bitmask of enum snd_jack_type values that being reported.
  51 *
  52 * If configured using snd_soc_jack_add_pins() then the associated
  53 * DAPM pins will be enabled or disabled as appropriate and DAPM
  54 * synchronised.
  55 *
  56 * Note: This function uses mutexes and should be called from a
  57 * context which can sleep (such as a workqueue).
  58 */
  59void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
  60{
  61        struct snd_soc_codec *codec = jack->card->codec;
  62        struct snd_soc_jack_pin *pin;
  63        int enable;
  64        int oldstatus;
  65
  66        if (!jack) {
  67                WARN_ON_ONCE(!jack);
  68                return;
  69        }
  70
  71        mutex_lock(&codec->mutex);
  72
  73        oldstatus = jack->status;
  74
  75        jack->status &= ~mask;
  76        jack->status |= status & mask;
  77
  78        /* The DAPM sync is expensive enough to be worth skipping.
  79         * However, empty mask means pin synchronization is desired. */
  80        if (mask && (jack->status == oldstatus))
  81                goto out;
  82
  83        list_for_each_entry(pin, &jack->pins, list) {
  84                enable = pin->mask & jack->status;
  85
  86                if (pin->invert)
  87                        enable = !enable;
  88
  89                if (enable)
  90                        snd_soc_dapm_enable_pin(codec, pin->pin);
  91                else
  92                        snd_soc_dapm_disable_pin(codec, pin->pin);
  93        }
  94
  95        snd_soc_dapm_sync(codec);
  96
  97        snd_jack_report(jack->jack, status);
  98
  99out:
 100        mutex_unlock(&codec->mutex);
 101}
 102EXPORT_SYMBOL_GPL(snd_soc_jack_report);
 103
 104/**
 105 * snd_soc_jack_add_pins - Associate DAPM pins with an ASoC jack
 106 *
 107 * @jack:  ASoC jack
 108 * @count: Number of pins
 109 * @pins:  Array of pins
 110 *
 111 * After this function has been called the DAPM pins specified in the
 112 * pins array will have their status updated to reflect the current
 113 * state of the jack whenever the jack status is updated.
 114 */
 115int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count,
 116                          struct snd_soc_jack_pin *pins)
 117{
 118        int i;
 119
 120        for (i = 0; i < count; i++) {
 121                if (!pins[i].pin) {
 122                        printk(KERN_ERR "No name for pin %d\n", i);
 123                        return -EINVAL;
 124                }
 125                if (!pins[i].mask) {
 126                        printk(KERN_ERR "No mask for pin %d (%s)\n", i,
 127                               pins[i].pin);
 128                        return -EINVAL;
 129                }
 130
 131                INIT_LIST_HEAD(&pins[i].list);
 132                list_add(&(pins[i].list), &jack->pins);
 133        }
 134
 135        /* Update to reflect the last reported status; canned jack
 136         * implementations are likely to set their state before the
 137         * card has an opportunity to associate pins.
 138         */
 139        snd_soc_jack_report(jack, 0, 0);
 140
 141        return 0;
 142}
 143EXPORT_SYMBOL_GPL(snd_soc_jack_add_pins);
 144
 145#ifdef CONFIG_GPIOLIB
 146/* gpio detect */
 147static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio)
 148{
 149        struct snd_soc_jack *jack = gpio->jack;
 150        int enable;
 151        int report;
 152
 153        if (gpio->debounce_time > 0)
 154                mdelay(gpio->debounce_time);
 155
 156        enable = gpio_get_value(gpio->gpio);
 157        if (gpio->invert)
 158                enable = !enable;
 159
 160        if (enable)
 161                report = gpio->report;
 162        else
 163                report = 0;
 164
 165        snd_soc_jack_report(jack, report, gpio->report);
 166}
 167
 168/* irq handler for gpio pin */
 169static irqreturn_t gpio_handler(int irq, void *data)
 170{
 171        struct snd_soc_jack_gpio *gpio = data;
 172
 173        schedule_work(&gpio->work);
 174
 175        return IRQ_HANDLED;
 176}
 177
 178/* gpio work */
 179static void gpio_work(struct work_struct *work)
 180{
 181        struct snd_soc_jack_gpio *gpio;
 182
 183        gpio = container_of(work, struct snd_soc_jack_gpio, work);
 184        snd_soc_jack_gpio_detect(gpio);
 185}
 186
 187/**
 188 * snd_soc_jack_add_gpios - Associate GPIO pins with an ASoC jack
 189 *
 190 * @jack:  ASoC jack
 191 * @count: number of pins
 192 * @gpios: array of gpio pins
 193 *
 194 * This function will request gpio, set data direction and request irq
 195 * for each gpio in the array.
 196 */
 197int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
 198                        struct snd_soc_jack_gpio *gpios)
 199{
 200        int i, ret;
 201
 202        for (i = 0; i < count; i++) {
 203                if (!gpio_is_valid(gpios[i].gpio)) {
 204                        printk(KERN_ERR "Invalid gpio %d\n",
 205                                gpios[i].gpio);
 206                        ret = -EINVAL;
 207                        goto undo;
 208                }
 209                if (!gpios[i].name) {
 210                        printk(KERN_ERR "No name for gpio %d\n",
 211                                gpios[i].gpio);
 212                        ret = -EINVAL;
 213                        goto undo;
 214                }
 215
 216                ret = gpio_request(gpios[i].gpio, gpios[i].name);
 217                if (ret)
 218                        goto undo;
 219
 220                ret = gpio_direction_input(gpios[i].gpio);
 221                if (ret)
 222                        goto err;
 223
 224                INIT_WORK(&gpios[i].work, gpio_work);
 225                gpios[i].jack = jack;
 226
 227                ret = request_irq(gpio_to_irq(gpios[i].gpio),
 228                                gpio_handler,
 229                                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
 230                                jack->card->dev->driver->name,
 231                                &gpios[i]);
 232                if (ret)
 233                        goto err;
 234
 235#ifdef CONFIG_GPIO_SYSFS
 236                /* Expose GPIO value over sysfs for diagnostic purposes */
 237                gpio_export(gpios[i].gpio, false);
 238#endif
 239
 240                /* Update initial jack status */
 241                snd_soc_jack_gpio_detect(&gpios[i]);
 242        }
 243
 244        return 0;
 245
 246err:
 247        gpio_free(gpios[i].gpio);
 248undo:
 249        snd_soc_jack_free_gpios(jack, i, gpios);
 250
 251        return ret;
 252}
 253EXPORT_SYMBOL_GPL(snd_soc_jack_add_gpios);
 254
 255/**
 256 * snd_soc_jack_free_gpios - Release GPIO pins' resources of an ASoC jack
 257 *
 258 * @jack:  ASoC jack
 259 * @count: number of pins
 260 * @gpios: array of gpio pins
 261 *
 262 * Release gpio and irq resources for gpio pins associated with an ASoC jack.
 263 */
 264void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
 265                        struct snd_soc_jack_gpio *gpios)
 266{
 267        int i;
 268
 269        for (i = 0; i < count; i++) {
 270#ifdef CONFIG_GPIO_SYSFS
 271                gpio_unexport(gpios[i].gpio);
 272#endif
 273                free_irq(gpio_to_irq(gpios[i].gpio), &gpios[i]);
 274                gpio_free(gpios[i].gpio);
 275                gpios[i].jack = NULL;
 276        }
 277}
 278EXPORT_SYMBOL_GPL(snd_soc_jack_free_gpios);
 279#endif  /* CONFIG_GPIOLIB */
 280