gpio-pmf.c (6269B)
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(¬if->mutex); 78 if (notif->notify) 79 notif->notify(notif->data); 80 mutex_unlock(¬if->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(¬if->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(¬if->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(¬if->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);