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) \ 14 static 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 } \ 27 static 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 33 PMF_GPIO(headphone, 0); 34 PMF_GPIO(amp, 1); 35 PMF_GPIO(lineout, 2); 36 37 static 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 49 static 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 61 static 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 72 static 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 guard(mutex)(¬if->mutex); 78 if (notif->notify) 79 notif->notify(notif->data); 80 } 81 82 static void pmf_gpio_init(struct gpio_runtime *rt) 83 { 84 pmf_gpio_all_amps_off(rt); 85 rt->implementation_private = 0; 86 INIT_DELAYED_WORK(&rt->headphone_notify.work, pmf_handle_notify); 87 INIT_DELAYED_WORK(&rt->line_in_notify.work, pmf_handle_notify); 88 INIT_DELAYED_WORK(&rt->line_out_notify.work, pmf_handle_notify); 89 mutex_init(&rt->headphone_notify.mutex); 90 mutex_init(&rt->line_in_notify.mutex); 91 mutex_init(&rt->line_out_notify.mutex); 92 } 93 94 static void pmf_gpio_exit(struct gpio_runtime *rt) 95 { 96 pmf_gpio_all_amps_off(rt); 97 rt->implementation_private = 0; 98 99 if (rt->headphone_notify.gpio_private) 100 pmf_unregister_irq_client(rt->headphone_notify.gpio_private); 101 if (rt->line_in_notify.gpio_private) 102 pmf_unregister_irq_client(rt->line_in_notify.gpio_private); 103 if (rt->line_out_notify.gpio_private) 104 pmf_unregister_irq_client(rt->line_out_notify.gpio_private); 105 106 /* make sure no work is pending before freeing 107 * all things */ 108 cancel_delayed_work_sync(&rt->headphone_notify.work); 109 cancel_delayed_work_sync(&rt->line_in_notify.work); 110 cancel_delayed_work_sync(&rt->line_out_notify.work); 111 112 mutex_destroy(&rt->headphone_notify.mutex); 113 mutex_destroy(&rt->line_in_notify.mutex); 114 mutex_destroy(&rt->line_out_notify.mutex); 115 116 kfree(rt->headphone_notify.gpio_private); 117 kfree(rt->line_in_notify.gpio_private); 118 kfree(rt->line_out_notify.gpio_private); 119 } 120 121 static void pmf_handle_notify_irq(void *data) 122 { 123 struct gpio_notification *notif = data; 124 125 schedule_delayed_work(¬if->work, 0); 126 } 127 128 static int pmf_set_notify(struct gpio_runtime *rt, 129 enum notify_type type, 130 notify_func_t notify, 131 void *data) 132 { 133 struct gpio_notification *notif; 134 notify_func_t old; 135 struct pmf_irq_client *irq_client; 136 char *name; 137 int err = -EBUSY; 138 139 switch (type) { 140 case AOA_NOTIFY_HEADPHONE: 141 notif = &rt->headphone_notify; 142 name = "headphone-detect"; 143 break; 144 case AOA_NOTIFY_LINE_IN: 145 notif = &rt->line_in_notify; 146 name = "linein-detect"; 147 break; 148 case AOA_NOTIFY_LINE_OUT: 149 notif = &rt->line_out_notify; 150 name = "lineout-detect"; 151 break; 152 default: 153 return -EINVAL; 154 } 155 156 guard(mutex)(¬if->mutex); 157 158 old = notif->notify; 159 160 if (!old && !notify) 161 return 0; 162 163 if (old && notify) { 164 if (old == notify && notif->data == data) 165 err = 0; 166 return err; 167 } 168 169 if (old && !notify) { 170 irq_client = notif->gpio_private; 171 pmf_unregister_irq_client(irq_client); 172 kfree(irq_client); 173 notif->gpio_private = NULL; 174 } 175 if (!old && notify) { 176 irq_client = kzalloc(sizeof(struct pmf_irq_client), 177 GFP_KERNEL); 178 if (!irq_client) 179 return -ENOMEM; 180 irq_client->data = notif; 181 irq_client->handler = pmf_handle_notify_irq; 182 irq_client->owner = THIS_MODULE; 183 err = pmf_register_irq_client(rt->node, 184 name, 185 irq_client); 186 if (err) { 187 printk(KERN_ERR "snd-aoa: gpio layer failed to" 188 " register %s irq (%d)\n", name, err); 189 kfree(irq_client); 190 return err; 191 } 192 notif->gpio_private = irq_client; 193 } 194 notif->notify = notify; 195 notif->data = data; 196 197 return 0; 198 } 199 200 static int pmf_get_detect(struct gpio_runtime *rt, 201 enum notify_type type) 202 { 203 char *name; 204 int err = -EBUSY, ret; 205 struct pmf_args args = { .count = 1, .u[0].p = &ret }; 206 207 switch (type) { 208 case AOA_NOTIFY_HEADPHONE: 209 name = "headphone-detect"; 210 break; 211 case AOA_NOTIFY_LINE_IN: 212 name = "linein-detect"; 213 break; 214 case AOA_NOTIFY_LINE_OUT: 215 name = "lineout-detect"; 216 break; 217 default: 218 return -EINVAL; 219 } 220 221 err = pmf_call_function(rt->node, name, &args); 222 if (err) 223 return err; 224 return ret; 225 } 226 227 static struct gpio_methods methods = { 228 .init = pmf_gpio_init, 229 .exit = pmf_gpio_exit, 230 .all_amps_off = pmf_gpio_all_amps_off, 231 .all_amps_restore = pmf_gpio_all_amps_restore, 232 .set_headphone = pmf_gpio_set_headphone, 233 .set_speakers = pmf_gpio_set_amp, 234 .set_lineout = pmf_gpio_set_lineout, 235 .set_hw_reset = pmf_gpio_set_hw_reset, 236 .get_headphone = pmf_gpio_get_headphone, 237 .get_speakers = pmf_gpio_get_amp, 238 .get_lineout = pmf_gpio_get_lineout, 239 .set_notify = pmf_set_notify, 240 .get_detect = pmf_get_detect, 241 }; 242 243 struct gpio_methods *pmf_gpio_methods = &methods; 244 EXPORT_SYMBOL_GPL(pmf_gpio_methods); 245