426 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Apple Onboard Audio feature call GPIO control4 *5 * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>6 *7 * This file contains the GPIO control routines for8 * direct (through feature calls) access to the GPIO9 * registers.10 */11 12#include <linux/of_irq.h>13#include <linux/interrupt.h>14#include <asm/pmac_feature.h>15#include "../aoa.h"16 17/* TODO: these are lots of global variables18 * that aren't used on most machines...19 * Move them into a dynamically allocated20 * structure and use that.21 */22 23/* these are the GPIO numbers (register addresses as offsets into24 * the GPIO space) */25static int headphone_mute_gpio;26static int master_mute_gpio;27static int amp_mute_gpio;28static int lineout_mute_gpio;29static int hw_reset_gpio;30static int lineout_detect_gpio;31static int headphone_detect_gpio;32static int linein_detect_gpio;33 34/* see the SWITCH_GPIO macro */35static int headphone_mute_gpio_activestate;36static int master_mute_gpio_activestate;37static int amp_mute_gpio_activestate;38static int lineout_mute_gpio_activestate;39static int hw_reset_gpio_activestate;40static int lineout_detect_gpio_activestate;41static int headphone_detect_gpio_activestate;42static int linein_detect_gpio_activestate;43 44/* node pointers that we save when getting the GPIO number45 * to get the interrupt later */46static struct device_node *lineout_detect_node;47static struct device_node *linein_detect_node;48static struct device_node *headphone_detect_node;49 50static int lineout_detect_irq;51static int linein_detect_irq;52static int headphone_detect_irq;53 54static struct device_node *get_gpio(char *name,55 char *altname,56 int *gpioptr,57 int *gpioactiveptr)58{59 struct device_node *np, *gpio;60 const u32 *reg;61 const char *audio_gpio;62 63 *gpioptr = -1;64 65 /* check if we can get it the easy way ... */66 np = of_find_node_by_name(NULL, name);67 if (!np) {68 /* some machines have only gpioX/extint-gpioX nodes,69 * and an audio-gpio property saying what it is ...70 * So what we have to do is enumerate all children71 * of the gpio node and check them all. */72 gpio = of_find_node_by_name(NULL, "gpio");73 if (!gpio)74 return NULL;75 while ((np = of_get_next_child(gpio, np))) {76 audio_gpio = of_get_property(np, "audio-gpio", NULL);77 if (!audio_gpio)78 continue;79 if (strcmp(audio_gpio, name) == 0)80 break;81 if (altname && (strcmp(audio_gpio, altname) == 0))82 break;83 }84 of_node_put(gpio);85 /* still not found, assume not there */86 if (!np)87 return NULL;88 }89 90 reg = of_get_property(np, "reg", NULL);91 if (!reg) {92 of_node_put(np);93 return NULL;94 }95 96 *gpioptr = *reg;97 98 /* this is a hack, usually the GPIOs 'reg' property99 * should have the offset based from the GPIO space100 * which is at 0x50, but apparently not always... */101 if (*gpioptr < 0x50)102 *gpioptr += 0x50;103 104 reg = of_get_property(np, "audio-gpio-active-state", NULL);105 if (!reg)106 /* Apple seems to default to 1, but107 * that doesn't seem right at least on most108 * machines. So until proven that the opposite109 * is necessary, we default to 0110 * (which, incidentally, snd-powermac also does...) */111 *gpioactiveptr = 0;112 else113 *gpioactiveptr = *reg;114 115 return np;116}117 118static void get_irq(struct device_node * np, int *irqptr)119{120 if (np)121 *irqptr = irq_of_parse_and_map(np, 0);122 else123 *irqptr = 0;124}125 126/* 0x4 is outenable, 0x1 is out, thus 4 or 5 */127#define SWITCH_GPIO(name, v, on) \128 (((v)&~1) | ((on)? \129 (name##_gpio_activestate==0?4:5): \130 (name##_gpio_activestate==0?5:4)))131 132#define FTR_GPIO(name, bit) \133static void ftr_gpio_set_##name(struct gpio_runtime *rt, int on)\134{ \135 int v; \136 \137 if (unlikely(!rt)) return; \138 \139 if (name##_mute_gpio < 0) \140 return; \141 \142 v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, \143 name##_mute_gpio, \144 0); \145 \146 /* muted = !on... */ \147 v = SWITCH_GPIO(name##_mute, v, !on); \148 \149 pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, \150 name##_mute_gpio, v); \151 \152 rt->implementation_private &= ~(1<<bit); \153 rt->implementation_private |= (!!on << bit); \154} \155static int ftr_gpio_get_##name(struct gpio_runtime *rt) \156{ \157 if (unlikely(!rt)) return 0; \158 return (rt->implementation_private>>bit)&1; \159}160 161FTR_GPIO(headphone, 0);162FTR_GPIO(amp, 1);163FTR_GPIO(lineout, 2);164FTR_GPIO(master, 3);165 166static void ftr_gpio_set_hw_reset(struct gpio_runtime *rt, int on)167{168 int v;169 170 if (unlikely(!rt)) return;171 if (hw_reset_gpio < 0)172 return;173 174 v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL,175 hw_reset_gpio, 0);176 v = SWITCH_GPIO(hw_reset, v, on);177 pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL,178 hw_reset_gpio, v);179}180 181static struct gpio_methods methods;182 183static void ftr_gpio_all_amps_off(struct gpio_runtime *rt)184{185 int saved;186 187 if (unlikely(!rt)) return;188 saved = rt->implementation_private;189 ftr_gpio_set_headphone(rt, 0);190 ftr_gpio_set_amp(rt, 0);191 ftr_gpio_set_lineout(rt, 0);192 if (methods.set_master)193 ftr_gpio_set_master(rt, 0);194 rt->implementation_private = saved;195}196 197static void ftr_gpio_all_amps_restore(struct gpio_runtime *rt)198{199 int s;200 201 if (unlikely(!rt)) return;202 s = rt->implementation_private;203 ftr_gpio_set_headphone(rt, (s>>0)&1);204 ftr_gpio_set_amp(rt, (s>>1)&1);205 ftr_gpio_set_lineout(rt, (s>>2)&1);206 if (methods.set_master)207 ftr_gpio_set_master(rt, (s>>3)&1);208}209 210static void ftr_handle_notify(struct work_struct *work)211{212 struct gpio_notification *notif =213 container_of(work, struct gpio_notification, work.work);214 215 mutex_lock(¬if->mutex);216 if (notif->notify)217 notif->notify(notif->data);218 mutex_unlock(¬if->mutex);219}220 221static void gpio_enable_dual_edge(int gpio)222{223 int v;224 225 if (gpio == -1)226 return;227 v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio, 0);228 v |= 0x80; /* enable dual edge */229 pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, gpio, v);230}231 232static void ftr_gpio_init(struct gpio_runtime *rt)233{234 get_gpio("headphone-mute", NULL,235 &headphone_mute_gpio,236 &headphone_mute_gpio_activestate);237 get_gpio("amp-mute", NULL,238 &_mute_gpio,239 &_mute_gpio_activestate);240 get_gpio("lineout-mute", NULL,241 &lineout_mute_gpio,242 &lineout_mute_gpio_activestate);243 get_gpio("hw-reset", "audio-hw-reset",244 &hw_reset_gpio,245 &hw_reset_gpio_activestate);246 if (get_gpio("master-mute", NULL,247 &master_mute_gpio,248 &master_mute_gpio_activestate)) {249 methods.set_master = ftr_gpio_set_master;250 methods.get_master = ftr_gpio_get_master;251 }252 253 headphone_detect_node = get_gpio("headphone-detect", NULL,254 &headphone_detect_gpio,255 &headphone_detect_gpio_activestate);256 /* go Apple, and thanks for giving these different names257 * across the board... */258 lineout_detect_node = get_gpio("lineout-detect", "line-output-detect",259 &lineout_detect_gpio,260 &lineout_detect_gpio_activestate);261 linein_detect_node = get_gpio("linein-detect", "line-input-detect",262 &linein_detect_gpio,263 &linein_detect_gpio_activestate);264 265 gpio_enable_dual_edge(headphone_detect_gpio);266 gpio_enable_dual_edge(lineout_detect_gpio);267 gpio_enable_dual_edge(linein_detect_gpio);268 269 get_irq(headphone_detect_node, &headphone_detect_irq);270 get_irq(lineout_detect_node, &lineout_detect_irq);271 get_irq(linein_detect_node, &linein_detect_irq);272 273 ftr_gpio_all_amps_off(rt);274 rt->implementation_private = 0;275 INIT_DELAYED_WORK(&rt->headphone_notify.work, ftr_handle_notify);276 INIT_DELAYED_WORK(&rt->line_in_notify.work, ftr_handle_notify);277 INIT_DELAYED_WORK(&rt->line_out_notify.work, ftr_handle_notify);278 mutex_init(&rt->headphone_notify.mutex);279 mutex_init(&rt->line_in_notify.mutex);280 mutex_init(&rt->line_out_notify.mutex);281}282 283static void ftr_gpio_exit(struct gpio_runtime *rt)284{285 ftr_gpio_all_amps_off(rt);286 rt->implementation_private = 0;287 if (rt->headphone_notify.notify)288 free_irq(headphone_detect_irq, &rt->headphone_notify);289 if (rt->line_in_notify.gpio_private)290 free_irq(linein_detect_irq, &rt->line_in_notify);291 if (rt->line_out_notify.gpio_private)292 free_irq(lineout_detect_irq, &rt->line_out_notify);293 cancel_delayed_work_sync(&rt->headphone_notify.work);294 cancel_delayed_work_sync(&rt->line_in_notify.work);295 cancel_delayed_work_sync(&rt->line_out_notify.work);296 mutex_destroy(&rt->headphone_notify.mutex);297 mutex_destroy(&rt->line_in_notify.mutex);298 mutex_destroy(&rt->line_out_notify.mutex);299}300 301static irqreturn_t ftr_handle_notify_irq(int xx, void *data)302{303 struct gpio_notification *notif = data;304 305 schedule_delayed_work(¬if->work, 0);306 307 return IRQ_HANDLED;308}309 310static int ftr_set_notify(struct gpio_runtime *rt,311 enum notify_type type,312 notify_func_t notify,313 void *data)314{315 struct gpio_notification *notif;316 notify_func_t old;317 int irq;318 char *name;319 int err = -EBUSY;320 321 switch (type) {322 case AOA_NOTIFY_HEADPHONE:323 notif = &rt->headphone_notify;324 name = "headphone-detect";325 irq = headphone_detect_irq;326 break;327 case AOA_NOTIFY_LINE_IN:328 notif = &rt->line_in_notify;329 name = "linein-detect";330 irq = linein_detect_irq;331 break;332 case AOA_NOTIFY_LINE_OUT:333 notif = &rt->line_out_notify;334 name = "lineout-detect";335 irq = lineout_detect_irq;336 break;337 default:338 return -EINVAL;339 }340 341 if (!irq)342 return -ENODEV;343 344 mutex_lock(¬if->mutex);345 346 old = notif->notify;347 348 if (!old && !notify) {349 err = 0;350 goto out_unlock;351 }352 353 if (old && notify) {354 if (old == notify && notif->data == data)355 err = 0;356 goto out_unlock;357 }358 359 if (old && !notify)360 free_irq(irq, notif);361 362 if (!old && notify) {363 err = request_irq(irq, ftr_handle_notify_irq, 0, name, notif);364 if (err)365 goto out_unlock;366 }367 368 notif->notify = notify;369 notif->data = data;370 371 err = 0;372 out_unlock:373 mutex_unlock(¬if->mutex);374 return err;375}376 377static int ftr_get_detect(struct gpio_runtime *rt,378 enum notify_type type)379{380 int gpio, ret, active;381 382 switch (type) {383 case AOA_NOTIFY_HEADPHONE:384 gpio = headphone_detect_gpio;385 active = headphone_detect_gpio_activestate;386 break;387 case AOA_NOTIFY_LINE_IN:388 gpio = linein_detect_gpio;389 active = linein_detect_gpio_activestate;390 break;391 case AOA_NOTIFY_LINE_OUT:392 gpio = lineout_detect_gpio;393 active = lineout_detect_gpio_activestate;394 break;395 default:396 return -EINVAL;397 }398 399 if (gpio == -1)400 return -ENODEV;401 402 ret = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio, 0);403 if (ret < 0)404 return ret;405 return ((ret >> 1) & 1) == active;406}407 408static struct gpio_methods methods = {409 .init = ftr_gpio_init,410 .exit = ftr_gpio_exit,411 .all_amps_off = ftr_gpio_all_amps_off,412 .all_amps_restore = ftr_gpio_all_amps_restore,413 .set_headphone = ftr_gpio_set_headphone,414 .set_speakers = ftr_gpio_set_amp,415 .set_lineout = ftr_gpio_set_lineout,416 .set_hw_reset = ftr_gpio_set_hw_reset,417 .get_headphone = ftr_gpio_get_headphone,418 .get_speakers = ftr_gpio_get_amp,419 .get_lineout = ftr_gpio_get_lineout,420 .set_notify = ftr_set_notify,421 .get_detect = ftr_get_detect,422};423 424struct gpio_methods *ftr_gpio_methods = &methods;425EXPORT_SYMBOL_GPL(ftr_gpio_methods);426