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