linux/sound/aoa/core/gpio-pmf.c
<<
>>
Prefs
   1/*
   2 * Apple Onboard Audio pmf GPIOs
   3 *
   4 * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
   5 *
   6 * GPL v2, can be found in COPYING.
   7 */
   8
   9#include <linux/slab.h>
  10#include <asm/pmac_feature.h>
  11#include <asm/pmac_pfunc.h>
  12#include "../aoa.h"
  13
  14#define PMF_GPIO(name, bit)                                     \
  15static void pmf_gpio_set_##name(struct gpio_runtime *rt, int on)\
  16{                                                               \
  17        struct pmf_args args = { .count = 1, .u[0].v = !on };   \
  18        int rc;                                                 \
  19                                                        \
  20        if (unlikely(!rt)) return;                              \
  21        rc = pmf_call_function(rt->node, #name "-mute", &args); \
  22        if (rc && rc != -ENODEV)                                \
  23                printk(KERN_WARNING "pmf_gpio_set_" #name       \
  24                " failed, rc: %d\n", rc);                       \
  25        rt->implementation_private &= ~(1<<bit);                \
  26        rt->implementation_private |= (!!on << bit);            \
  27}                                                               \
  28static int pmf_gpio_get_##name(struct gpio_runtime *rt)         \
  29{                                                               \
  30        if (unlikely(!rt)) return 0;                            \
  31        return (rt->implementation_private>>bit)&1;             \
  32}
  33
  34PMF_GPIO(headphone, 0);
  35PMF_GPIO(amp, 1);
  36PMF_GPIO(lineout, 2);
  37
  38static void pmf_gpio_set_hw_reset(struct gpio_runtime *rt, int on)
  39{
  40        struct pmf_args args = { .count = 1, .u[0].v = !!on };
  41        int rc;
  42
  43        if (unlikely(!rt)) return;
  44        rc = pmf_call_function(rt->node, "hw-reset", &args);
  45        if (rc)
  46                printk(KERN_WARNING "pmf_gpio_set_hw_reset"
  47                       " failed, rc: %d\n", rc);
  48}
  49
  50static void pmf_gpio_all_amps_off(struct gpio_runtime *rt)
  51{
  52        int saved;
  53
  54        if (unlikely(!rt)) return;
  55        saved = rt->implementation_private;
  56        pmf_gpio_set_headphone(rt, 0);
  57        pmf_gpio_set_amp(rt, 0);
  58        pmf_gpio_set_lineout(rt, 0);
  59        rt->implementation_private = saved;
  60}
  61
  62static void pmf_gpio_all_amps_restore(struct gpio_runtime *rt)
  63{
  64        int s;
  65
  66        if (unlikely(!rt)) return;
  67        s = rt->implementation_private;
  68        pmf_gpio_set_headphone(rt, (s>>0)&1);
  69        pmf_gpio_set_amp(rt, (s>>1)&1);
  70        pmf_gpio_set_lineout(rt, (s>>2)&1);
  71}
  72
  73static void pmf_handle_notify(struct work_struct *work)
  74{
  75        struct gpio_notification *notif =
  76                container_of(work, struct gpio_notification, work.work);
  77
  78        mutex_lock(&notif->mutex);
  79        if (notif->notify)
  80                notif->notify(notif->data);
  81        mutex_unlock(&notif->mutex);
  82}
  83
  84static void pmf_gpio_init(struct gpio_runtime *rt)
  85{
  86        pmf_gpio_all_amps_off(rt);
  87        rt->implementation_private = 0;
  88        INIT_DELAYED_WORK(&rt->headphone_notify.work, pmf_handle_notify);
  89        INIT_DELAYED_WORK(&rt->line_in_notify.work, pmf_handle_notify);
  90        INIT_DELAYED_WORK(&rt->line_out_notify.work, pmf_handle_notify);
  91        mutex_init(&rt->headphone_notify.mutex);
  92        mutex_init(&rt->line_in_notify.mutex);
  93        mutex_init(&rt->line_out_notify.mutex);
  94}
  95
  96static void pmf_gpio_exit(struct gpio_runtime *rt)
  97{
  98        pmf_gpio_all_amps_off(rt);
  99        rt->implementation_private = 0;
 100
 101        if (rt->headphone_notify.gpio_private)
 102                pmf_unregister_irq_client(rt->headphone_notify.gpio_private);
 103        if (rt->line_in_notify.gpio_private)
 104                pmf_unregister_irq_client(rt->line_in_notify.gpio_private);
 105        if (rt->line_out_notify.gpio_private)
 106                pmf_unregister_irq_client(rt->line_out_notify.gpio_private);
 107
 108        /* make sure no work is pending before freeing
 109         * all things */
 110        cancel_delayed_work_sync(&rt->headphone_notify.work);
 111        cancel_delayed_work_sync(&rt->line_in_notify.work);
 112        cancel_delayed_work_sync(&rt->line_out_notify.work);
 113
 114        mutex_destroy(&rt->headphone_notify.mutex);
 115        mutex_destroy(&rt->line_in_notify.mutex);
 116        mutex_destroy(&rt->line_out_notify.mutex);
 117
 118        kfree(rt->headphone_notify.gpio_private);
 119        kfree(rt->line_in_notify.gpio_private);
 120        kfree(rt->line_out_notify.gpio_private);
 121}
 122
 123static void pmf_handle_notify_irq(void *data)
 124{
 125        struct gpio_notification *notif = data;
 126
 127        schedule_delayed_work(&notif->work, 0);
 128}
 129
 130static int pmf_set_notify(struct gpio_runtime *rt,
 131                          enum notify_type type,
 132                          notify_func_t notify,
 133                          void *data)
 134{
 135        struct gpio_notification *notif;
 136        notify_func_t old;
 137        struct pmf_irq_client *irq_client;
 138        char *name;
 139        int err = -EBUSY;
 140
 141        switch (type) {
 142        case AOA_NOTIFY_HEADPHONE:
 143                notif = &rt->headphone_notify;
 144                name = "headphone-detect";
 145                break;
 146        case AOA_NOTIFY_LINE_IN:
 147                notif = &rt->line_in_notify;
 148                name = "linein-detect";
 149                break;
 150        case AOA_NOTIFY_LINE_OUT:
 151                notif = &rt->line_out_notify;
 152                name = "lineout-detect";
 153                break;
 154        default:
 155                return -EINVAL;
 156        }
 157
 158        mutex_lock(&notif->mutex);
 159
 160        old = notif->notify;
 161
 162        if (!old && !notify) {
 163                err = 0;
 164                goto out_unlock;
 165        }
 166
 167        if (old && notify) {
 168                if (old == notify && notif->data == data)
 169                        err = 0;
 170                goto out_unlock;
 171        }
 172
 173        if (old && !notify) {
 174                irq_client = notif->gpio_private;
 175                pmf_unregister_irq_client(irq_client);
 176                kfree(irq_client);
 177                notif->gpio_private = NULL;
 178        }
 179        if (!old && notify) {
 180                irq_client = kzalloc(sizeof(struct pmf_irq_client),
 181                                     GFP_KERNEL);
 182                if (!irq_client) {
 183                        err = -ENOMEM;
 184                        goto out_unlock;
 185                }
 186                irq_client->data = notif;
 187                irq_client->handler = pmf_handle_notify_irq;
 188                irq_client->owner = THIS_MODULE;
 189                err = pmf_register_irq_client(rt->node,
 190                                              name,
 191                                              irq_client);
 192                if (err) {
 193                        printk(KERN_ERR "snd-aoa: gpio layer failed to"
 194                                        " register %s irq (%d)\n", name, err);
 195                        kfree(irq_client);
 196                        goto out_unlock;
 197                }
 198                notif->gpio_private = irq_client;
 199        }
 200        notif->notify = notify;
 201        notif->data = data;
 202
 203        err = 0;
 204 out_unlock:
 205        mutex_unlock(&notif->mutex);
 206        return err;
 207}
 208
 209static int pmf_get_detect(struct gpio_runtime *rt,
 210                          enum notify_type type)
 211{
 212        char *name;
 213        int err = -EBUSY, ret;
 214        struct pmf_args args = { .count = 1, .u[0].p = &ret };
 215
 216        switch (type) {
 217        case AOA_NOTIFY_HEADPHONE:
 218                name = "headphone-detect";
 219                break;
 220        case AOA_NOTIFY_LINE_IN:
 221                name = "linein-detect";
 222                break;
 223        case AOA_NOTIFY_LINE_OUT:
 224                name = "lineout-detect";
 225                break;
 226        default:
 227                return -EINVAL;
 228        }
 229
 230        err = pmf_call_function(rt->node, name, &args);
 231        if (err)
 232                return err;
 233        return ret;
 234}
 235
 236static struct gpio_methods methods = {
 237        .init                   = pmf_gpio_init,
 238        .exit                   = pmf_gpio_exit,
 239        .all_amps_off           = pmf_gpio_all_amps_off,
 240        .all_amps_restore       = pmf_gpio_all_amps_restore,
 241        .set_headphone          = pmf_gpio_set_headphone,
 242        .set_speakers           = pmf_gpio_set_amp,
 243        .set_lineout            = pmf_gpio_set_lineout,
 244        .set_hw_reset           = pmf_gpio_set_hw_reset,
 245        .get_headphone          = pmf_gpio_get_headphone,
 246        .get_speakers           = pmf_gpio_get_amp,
 247        .get_lineout            = pmf_gpio_get_lineout,
 248        .set_notify             = pmf_set_notify,
 249        .get_detect             = pmf_get_detect,
 250};
 251
 252struct gpio_methods *pmf_gpio_methods = &methods;
 253EXPORT_SYMBOL_GPL(pmf_gpio_methods);
 254