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