linux/sound/aoa/core/gpio-feature.c
<<
>>
Prefs
   1/*
   2 * Apple Onboard Audio feature call GPIO control
   3 *
   4 * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
   5 *
   6 * GPL v2, can be found in COPYING.
   7 *
   8 * This file contains the GPIO control routines for
   9 * direct (through feature calls) access to the GPIO
  10 * registers.
  11 */
  12
  13#include <asm/pmac_feature.h>
  14#include <linux/interrupt.h>
  15#include "../aoa.h"
  16
  17/* TODO: these are lots of global variables
  18 * that aren't used on most machines...
  19 * Move them into a dynamically allocated
  20 * structure and use that.
  21 */
  22
  23/* these are the GPIO numbers (register addresses as offsets into
  24 * the GPIO space) */
  25static int headphone_mute_gpio;
  26static int master_mute_gpio;
  27static int amp_mute_gpio;
  28static int lineout_mute_gpio;
  29static int hw_reset_gpio;
  30static int lineout_detect_gpio;
  31static int headphone_detect_gpio;
  32static int linein_detect_gpio;
  33
  34/* see the SWITCH_GPIO macro */
  35static int headphone_mute_gpio_activestate;
  36static int master_mute_gpio_activestate;
  37static int amp_mute_gpio_activestate;
  38static int lineout_mute_gpio_activestate;
  39static int hw_reset_gpio_activestate;
  40static int lineout_detect_gpio_activestate;
  41static int headphone_detect_gpio_activestate;
  42static int linein_detect_gpio_activestate;
  43
  44/* node pointers that we save when getting the GPIO number
  45 * to get the interrupt later */
  46static struct device_node *lineout_detect_node;
  47static struct device_node *linein_detect_node;
  48static struct device_node *headphone_detect_node;
  49
  50static int lineout_detect_irq;
  51static int linein_detect_irq;
  52static int headphone_detect_irq;
  53
  54static struct device_node *get_gpio(char *name,
  55                                    char *altname,
  56                                    int *gpioptr,
  57                                    int *gpioactiveptr)
  58{
  59        struct device_node *np, *gpio;
  60        const u32 *reg;
  61        const char *audio_gpio;
  62
  63        *gpioptr = -1;
  64
  65        /* check if we can get it the easy way ... */
  66        np = of_find_node_by_name(NULL, name);
  67        if (!np) {
  68                /* some machines have only gpioX/extint-gpioX nodes,
  69                 * and an audio-gpio property saying what it is ...
  70                 * So what we have to do is enumerate all children
  71                 * of the gpio node and check them all. */
  72                gpio = of_find_node_by_name(NULL, "gpio");
  73                if (!gpio)
  74                        return NULL;
  75                while ((np = of_get_next_child(gpio, np))) {
  76                        audio_gpio = of_get_property(np, "audio-gpio", NULL);
  77                        if (!audio_gpio)
  78                                continue;
  79                        if (strcmp(audio_gpio, name) == 0)
  80                                break;
  81                        if (altname && (strcmp(audio_gpio, altname) == 0))
  82                                break;
  83                }
  84                /* still not found, assume not there */
  85                if (!np)
  86                        return NULL;
  87        }
  88
  89        reg = of_get_property(np, "reg", NULL);
  90        if (!reg)
  91                return NULL;
  92
  93        *gpioptr = *reg;
  94
  95        /* this is a hack, usually the GPIOs 'reg' property
  96         * should have the offset based from the GPIO space
  97         * which is at 0x50, but apparently not always... */
  98        if (*gpioptr < 0x50)
  99                *gpioptr += 0x50;
 100
 101        reg = of_get_property(np, "audio-gpio-active-state", NULL);
 102        if (!reg)
 103                /* Apple seems to default to 1, but
 104                 * that doesn't seem right at least on most
 105                 * machines. So until proven that the opposite
 106                 * is necessary, we default to 0
 107                 * (which, incidentally, snd-powermac also does...) */
 108                *gpioactiveptr = 0;
 109        else
 110                *gpioactiveptr = *reg;
 111
 112        return np;
 113}
 114
 115static void get_irq(struct device_node * np, int *irqptr)
 116{
 117        if (np)
 118                *irqptr = irq_of_parse_and_map(np, 0);
 119        else
 120                *irqptr = NO_IRQ;
 121}
 122
 123/* 0x4 is outenable, 0x1 is out, thus 4 or 5 */
 124#define SWITCH_GPIO(name, v, on)                                \
 125        (((v)&~1) | ((on)?                                      \
 126                        (name##_gpio_activestate==0?4:5):       \
 127                        (name##_gpio_activestate==0?5:4)))
 128
 129#define FTR_GPIO(name, bit)                                     \
 130static void ftr_gpio_set_##name(struct gpio_runtime *rt, int on)\
 131{                                                               \
 132        int v;                                                  \
 133                                                                \
 134        if (unlikely(!rt)) return;                              \
 135                                                                \
 136        if (name##_mute_gpio < 0)                               \
 137                return;                                         \
 138                                                                \
 139        v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL,         \
 140                              name##_mute_gpio,                 \
 141                              0);                               \
 142                                                                \
 143        /* muted = !on... */                                    \
 144        v = SWITCH_GPIO(name##_mute, v, !on);                   \
 145                                                                \
 146        pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL,            \
 147                          name##_mute_gpio, v);                 \
 148                                                                \
 149        rt->implementation_private &= ~(1<<bit);                \
 150        rt->implementation_private |= (!!on << bit);            \
 151}                                                               \
 152static int ftr_gpio_get_##name(struct gpio_runtime *rt)         \
 153{                                                               \
 154        if (unlikely(!rt)) return 0;                            \
 155        return (rt->implementation_private>>bit)&1;             \
 156}
 157
 158FTR_GPIO(headphone, 0);
 159FTR_GPIO(amp, 1);
 160FTR_GPIO(lineout, 2);
 161FTR_GPIO(master, 3);
 162
 163static void ftr_gpio_set_hw_reset(struct gpio_runtime *rt, int on)
 164{
 165        int v;
 166
 167        if (unlikely(!rt)) return;
 168        if (hw_reset_gpio < 0)
 169                return;
 170
 171        v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL,
 172                              hw_reset_gpio, 0);
 173        v = SWITCH_GPIO(hw_reset, v, on);
 174        pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL,
 175                          hw_reset_gpio, v);
 176}
 177
 178static struct gpio_methods methods;
 179
 180static void ftr_gpio_all_amps_off(struct gpio_runtime *rt)
 181{
 182        int saved;
 183
 184        if (unlikely(!rt)) return;
 185        saved = rt->implementation_private;
 186        ftr_gpio_set_headphone(rt, 0);
 187        ftr_gpio_set_amp(rt, 0);
 188        ftr_gpio_set_lineout(rt, 0);
 189        if (methods.set_master)
 190                ftr_gpio_set_master(rt, 0);
 191        rt->implementation_private = saved;
 192}
 193
 194static void ftr_gpio_all_amps_restore(struct gpio_runtime *rt)
 195{
 196        int s;
 197
 198        if (unlikely(!rt)) return;
 199        s = rt->implementation_private;
 200        ftr_gpio_set_headphone(rt, (s>>0)&1);
 201        ftr_gpio_set_amp(rt, (s>>1)&1);
 202        ftr_gpio_set_lineout(rt, (s>>2)&1);
 203        if (methods.set_master)
 204                ftr_gpio_set_master(rt, (s>>3)&1);
 205}
 206
 207static void ftr_handle_notify(struct work_struct *work)
 208{
 209        struct gpio_notification *notif =
 210                container_of(work, struct gpio_notification, work.work);
 211
 212        mutex_lock(&notif->mutex);
 213        if (notif->notify)
 214                notif->notify(notif->data);
 215        mutex_unlock(&notif->mutex);
 216}
 217
 218static void gpio_enable_dual_edge(int gpio)
 219{
 220        int v;
 221
 222        if (gpio == -1)
 223                return;
 224        v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio, 0);
 225        v |= 0x80; /* enable dual edge */
 226        pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, gpio, v);
 227}
 228
 229static void ftr_gpio_init(struct gpio_runtime *rt)
 230{
 231        get_gpio("headphone-mute", NULL,
 232                 &headphone_mute_gpio,
 233                 &headphone_mute_gpio_activestate);
 234        get_gpio("amp-mute", NULL,
 235                 &amp_mute_gpio,
 236                 &amp_mute_gpio_activestate);
 237        get_gpio("lineout-mute", NULL,
 238                 &lineout_mute_gpio,
 239                 &lineout_mute_gpio_activestate);
 240        get_gpio("hw-reset", "audio-hw-reset",
 241                 &hw_reset_gpio,
 242                 &hw_reset_gpio_activestate);
 243        if (get_gpio("master-mute", NULL,
 244                     &master_mute_gpio,
 245                     &master_mute_gpio_activestate)) {
 246                methods.set_master = ftr_gpio_set_master;
 247                methods.get_master = ftr_gpio_get_master;
 248        }
 249
 250        headphone_detect_node = get_gpio("headphone-detect", NULL,
 251                                         &headphone_detect_gpio,
 252                                         &headphone_detect_gpio_activestate);
 253        /* go Apple, and thanks for giving these different names
 254         * across the board... */
 255        lineout_detect_node = get_gpio("lineout-detect", "line-output-detect",
 256                                       &lineout_detect_gpio,
 257                                       &lineout_detect_gpio_activestate);
 258        linein_detect_node = get_gpio("linein-detect", "line-input-detect",
 259                                      &linein_detect_gpio,
 260                                      &linein_detect_gpio_activestate);
 261
 262        gpio_enable_dual_edge(headphone_detect_gpio);
 263        gpio_enable_dual_edge(lineout_detect_gpio);
 264        gpio_enable_dual_edge(linein_detect_gpio);
 265
 266        get_irq(headphone_detect_node, &headphone_detect_irq);
 267        get_irq(lineout_detect_node, &lineout_detect_irq);
 268        get_irq(linein_detect_node, &linein_detect_irq);
 269
 270        ftr_gpio_all_amps_off(rt);
 271        rt->implementation_private = 0;
 272        INIT_DELAYED_WORK(&rt->headphone_notify.work, ftr_handle_notify);
 273        INIT_DELAYED_WORK(&rt->line_in_notify.work, ftr_handle_notify);
 274        INIT_DELAYED_WORK(&rt->line_out_notify.work, ftr_handle_notify);
 275        mutex_init(&rt->headphone_notify.mutex);
 276        mutex_init(&rt->line_in_notify.mutex);
 277        mutex_init(&rt->line_out_notify.mutex);
 278}
 279
 280static void ftr_gpio_exit(struct gpio_runtime *rt)
 281{
 282        ftr_gpio_all_amps_off(rt);
 283        rt->implementation_private = 0;
 284        if (rt->headphone_notify.notify)
 285                free_irq(headphone_detect_irq, &rt->headphone_notify);
 286        if (rt->line_in_notify.gpio_private)
 287                free_irq(linein_detect_irq, &rt->line_in_notify);
 288        if (rt->line_out_notify.gpio_private)
 289                free_irq(lineout_detect_irq, &rt->line_out_notify);
 290        cancel_delayed_work_sync(&rt->headphone_notify.work);
 291        cancel_delayed_work_sync(&rt->line_in_notify.work);
 292        cancel_delayed_work_sync(&rt->line_out_notify.work);
 293        mutex_destroy(&rt->headphone_notify.mutex);
 294        mutex_destroy(&rt->line_in_notify.mutex);
 295        mutex_destroy(&rt->line_out_notify.mutex);
 296}
 297
 298static irqreturn_t ftr_handle_notify_irq(int xx, void *data)
 299{
 300        struct gpio_notification *notif = data;
 301
 302        schedule_delayed_work(&notif->work, 0);
 303
 304        return IRQ_HANDLED;
 305}
 306
 307static int ftr_set_notify(struct gpio_runtime *rt,
 308                          enum notify_type type,
 309                          notify_func_t notify,
 310                          void *data)
 311{
 312        struct gpio_notification *notif;
 313        notify_func_t old;
 314        int irq;
 315        char *name;
 316        int err = -EBUSY;
 317
 318        switch (type) {
 319        case AOA_NOTIFY_HEADPHONE:
 320                notif = &rt->headphone_notify;
 321                name = "headphone-detect";
 322                irq = headphone_detect_irq;
 323                break;
 324        case AOA_NOTIFY_LINE_IN:
 325                notif = &rt->line_in_notify;
 326                name = "linein-detect";
 327                irq = linein_detect_irq;
 328                break;
 329        case AOA_NOTIFY_LINE_OUT:
 330                notif = &rt->line_out_notify;
 331                name = "lineout-detect";
 332                irq = lineout_detect_irq;
 333                break;
 334        default:
 335                return -EINVAL;
 336        }
 337
 338        if (irq == NO_IRQ)
 339                return -ENODEV;
 340
 341        mutex_lock(&notif->mutex);
 342
 343        old = notif->notify;
 344
 345        if (!old && !notify) {
 346                err = 0;
 347                goto out_unlock;
 348        }
 349
 350        if (old && notify) {
 351                if (old == notify && notif->data == data)
 352                        err = 0;
 353                goto out_unlock;
 354        }
 355
 356        if (old && !notify)
 357                free_irq(irq, notif);
 358
 359        if (!old && notify) {
 360                err = request_irq(irq, ftr_handle_notify_irq, 0, name, notif);
 361                if (err)
 362                        goto out_unlock;
 363        }
 364
 365        notif->notify = notify;
 366        notif->data = data;
 367
 368        err = 0;
 369 out_unlock:
 370        mutex_unlock(&notif->mutex);
 371        return err;
 372}
 373
 374static int ftr_get_detect(struct gpio_runtime *rt,
 375                          enum notify_type type)
 376{
 377        int gpio, ret, active;
 378
 379        switch (type) {
 380        case AOA_NOTIFY_HEADPHONE:
 381                gpio = headphone_detect_gpio;
 382                active = headphone_detect_gpio_activestate;
 383                break;
 384        case AOA_NOTIFY_LINE_IN:
 385                gpio = linein_detect_gpio;
 386                active = linein_detect_gpio_activestate;
 387                break;
 388        case AOA_NOTIFY_LINE_OUT:
 389                gpio = lineout_detect_gpio;
 390                active = lineout_detect_gpio_activestate;
 391                break;
 392        default:
 393                return -EINVAL;
 394        }
 395
 396        if (gpio == -1)
 397                return -ENODEV;
 398
 399        ret = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio, 0);
 400        if (ret < 0)
 401                return ret;
 402        return ((ret >> 1) & 1) == active;
 403}
 404
 405static struct gpio_methods methods = {
 406        .init                   = ftr_gpio_init,
 407        .exit                   = ftr_gpio_exit,
 408        .all_amps_off           = ftr_gpio_all_amps_off,
 409        .all_amps_restore       = ftr_gpio_all_amps_restore,
 410        .set_headphone          = ftr_gpio_set_headphone,
 411        .set_speakers           = ftr_gpio_set_amp,
 412        .set_lineout            = ftr_gpio_set_lineout,
 413        .set_hw_reset           = ftr_gpio_set_hw_reset,
 414        .get_headphone          = ftr_gpio_get_headphone,
 415        .get_speakers           = ftr_gpio_get_amp,
 416        .get_lineout            = ftr_gpio_get_lineout,
 417        .set_notify             = ftr_set_notify,
 418        .get_detect             = ftr_get_detect,
 419};
 420
 421struct gpio_methods *ftr_gpio_methods = &methods;
 422EXPORT_SYMBOL_GPL(ftr_gpio_methods);
 423