brintos

brintos / linux-shallow public Read only

0
0
Text · 6.1 KiB · 3786603 Raw
253 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Apple Onboard Audio pmf GPIOs4 *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(&notif->mutex);78	if (notif->notify)79		notif->notify(notif->data);80	mutex_unlock(&notif->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 freeing108	 * 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(&notif->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(&notif->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(&notif->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);253