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) \ 15 static 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 } \ 28 static 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 34 PMF_GPIO(headphone, 0); 35 PMF_GPIO(amp, 1); 36 PMF_GPIO(lineout, 2); 37 38 static 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 50 static 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 62 static 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 73 static 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 84 static 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 96 static 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(&rt->headphone_notify.work); 111 cancel_delayed_work(&rt->line_in_notify.work); 112 cancel_delayed_work(&rt->line_out_notify.work); 113 flush_scheduled_work(); 114 115 mutex_destroy(&rt->headphone_notify.mutex); 116 mutex_destroy(&rt->line_in_notify.mutex); 117 mutex_destroy(&rt->line_out_notify.mutex); 118 119 kfree(rt->headphone_notify.gpio_private); 120 kfree(rt->line_in_notify.gpio_private); 121 kfree(rt->line_out_notify.gpio_private); 122 } 123 124 static void pmf_handle_notify_irq(void *data) 125 { 126 struct gpio_notification *notif = data; 127 128 schedule_delayed_work(¬if->work, 0); 129 } 130 131 static int pmf_set_notify(struct gpio_runtime *rt, 132 enum notify_type type, 133 notify_func_t notify, 134 void *data) 135 { 136 struct gpio_notification *notif; 137 notify_func_t old; 138 struct pmf_irq_client *irq_client; 139 char *name; 140 int err = -EBUSY; 141 142 switch (type) { 143 case AOA_NOTIFY_HEADPHONE: 144 notif = &rt->headphone_notify; 145 name = "headphone-detect"; 146 break; 147 case AOA_NOTIFY_LINE_IN: 148 notif = &rt->line_in_notify; 149 name = "linein-detect"; 150 break; 151 case AOA_NOTIFY_LINE_OUT: 152 notif = &rt->line_out_notify; 153 name = "lineout-detect"; 154 break; 155 default: 156 return -EINVAL; 157 } 158 159 mutex_lock(¬if->mutex); 160 161 old = notif->notify; 162 163 if (!old && !notify) { 164 err = 0; 165 goto out_unlock; 166 } 167 168 if (old && notify) { 169 if (old == notify && notif->data == data) 170 err = 0; 171 goto out_unlock; 172 } 173 174 if (old && !notify) { 175 irq_client = notif->gpio_private; 176 pmf_unregister_irq_client(irq_client); 177 kfree(irq_client); 178 notif->gpio_private = NULL; 179 } 180 if (!old && notify) { 181 irq_client = kzalloc(sizeof(struct pmf_irq_client), 182 GFP_KERNEL); 183 if (!irq_client) { 184 err = -ENOMEM; 185 goto out_unlock; 186 } 187 irq_client->data = notif; 188 irq_client->handler = pmf_handle_notify_irq; 189 irq_client->owner = THIS_MODULE; 190 err = pmf_register_irq_client(rt->node, 191 name, 192 irq_client); 193 if (err) { 194 printk(KERN_ERR "snd-aoa: gpio layer failed to" 195 " register %s irq (%d)\n", name, err); 196 kfree(irq_client); 197 goto out_unlock; 198 } 199 notif->gpio_private = irq_client; 200 } 201 notif->notify = notify; 202 notif->data = data; 203 204 err = 0; 205 out_unlock: 206 mutex_unlock(¬if->mutex); 207 return err; 208 } 209 210 static int pmf_get_detect(struct gpio_runtime *rt, 211 enum notify_type type) 212 { 213 char *name; 214 int err = -EBUSY, ret; 215 struct pmf_args args = { .count = 1, .u[0].p = &ret }; 216 217 switch (type) { 218 case AOA_NOTIFY_HEADPHONE: 219 name = "headphone-detect"; 220 break; 221 case AOA_NOTIFY_LINE_IN: 222 name = "linein-detect"; 223 break; 224 case AOA_NOTIFY_LINE_OUT: 225 name = "lineout-detect"; 226 break; 227 default: 228 return -EINVAL; 229 } 230 231 err = pmf_call_function(rt->node, name, &args); 232 if (err) 233 return err; 234 return ret; 235 } 236 237 static struct gpio_methods methods = { 238 .init = pmf_gpio_init, 239 .exit = pmf_gpio_exit, 240 .all_amps_off = pmf_gpio_all_amps_off, 241 .all_amps_restore = pmf_gpio_all_amps_restore, 242 .set_headphone = pmf_gpio_set_headphone, 243 .set_speakers = pmf_gpio_set_amp, 244 .set_lineout = pmf_gpio_set_lineout, 245 .set_hw_reset = pmf_gpio_set_hw_reset, 246 .get_headphone = pmf_gpio_get_headphone, 247 .get_speakers = pmf_gpio_get_amp, 248 .get_lineout = pmf_gpio_get_lineout, 249 .set_notify = pmf_set_notify, 250 .get_detect = pmf_get_detect, 251 }; 252 253 struct gpio_methods *pmf_gpio_methods = &methods; 254 EXPORT_SYMBOL_GPL(pmf_gpio_methods); 255