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(¬if->mutex); 79 if (notif->notify) 80 notif->notify(notif->data); 81 mutex_unlock(¬if->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(¬if->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(¬if->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(¬if->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