123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- /*
- * Apple Onboard Audio pmf GPIOs
- *
- * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
- */
- #include <linux/slab.h>
- #include <asm/pmac_feature.h>
- #include <asm/pmac_pfunc.h>
- #include "../aoa.h"
- #define PMF_GPIO(name, bit) \
- static void pmf_gpio_set_##name(struct gpio_runtime *rt, int on)\
- { \
- struct pmf_args args = { .count = 1, .u[0].v = !on }; \
- int rc; \
- \
- if (unlikely(!rt)) return; \
- rc = pmf_call_function(rt->node, #name "-mute", &args); \
- if (rc && rc != -ENODEV) \
- printk(KERN_WARNING "pmf_gpio_set_" #name \
- " failed, rc: %d\n", rc); \
- rt->implementation_private &= ~(1<<bit); \
- rt->implementation_private |= (!!on << bit); \
- } \
- static int pmf_gpio_get_##name(struct gpio_runtime *rt) \
- { \
- if (unlikely(!rt)) return 0; \
- return (rt->implementation_private>>bit)&1; \
- }
- PMF_GPIO(headphone, 0);
- PMF_GPIO(amp, 1);
- PMF_GPIO(lineout, 2);
- static void pmf_gpio_set_hw_reset(struct gpio_runtime *rt, int on)
- {
- struct pmf_args args = { .count = 1, .u[0].v = !!on };
- int rc;
- if (unlikely(!rt)) return;
- rc = pmf_call_function(rt->node, "hw-reset", &args);
- if (rc)
- printk(KERN_WARNING "pmf_gpio_set_hw_reset"
- " failed, rc: %d\n", rc);
- }
- static void pmf_gpio_all_amps_off(struct gpio_runtime *rt)
- {
- int saved;
- if (unlikely(!rt)) return;
- saved = rt->implementation_private;
- pmf_gpio_set_headphone(rt, 0);
- pmf_gpio_set_amp(rt, 0);
- pmf_gpio_set_lineout(rt, 0);
- rt->implementation_private = saved;
- }
- static void pmf_gpio_all_amps_restore(struct gpio_runtime *rt)
- {
- int s;
- if (unlikely(!rt)) return;
- s = rt->implementation_private;
- pmf_gpio_set_headphone(rt, (s>>0)&1);
- pmf_gpio_set_amp(rt, (s>>1)&1);
- pmf_gpio_set_lineout(rt, (s>>2)&1);
- }
- static void pmf_handle_notify(struct work_struct *work)
- {
- struct gpio_notification *notif =
- container_of(work, struct gpio_notification, work.work);
- mutex_lock(¬if->mutex);
- if (notif->notify)
- notif->notify(notif->data);
- mutex_unlock(¬if->mutex);
- }
- static void pmf_gpio_init(struct gpio_runtime *rt)
- {
- pmf_gpio_all_amps_off(rt);
- rt->implementation_private = 0;
- INIT_DELAYED_WORK(&rt->headphone_notify.work, pmf_handle_notify);
- INIT_DELAYED_WORK(&rt->line_in_notify.work, pmf_handle_notify);
- INIT_DELAYED_WORK(&rt->line_out_notify.work, pmf_handle_notify);
- mutex_init(&rt->headphone_notify.mutex);
- mutex_init(&rt->line_in_notify.mutex);
- mutex_init(&rt->line_out_notify.mutex);
- }
- static void pmf_gpio_exit(struct gpio_runtime *rt)
- {
- pmf_gpio_all_amps_off(rt);
- rt->implementation_private = 0;
- if (rt->headphone_notify.gpio_private)
- pmf_unregister_irq_client(rt->headphone_notify.gpio_private);
- if (rt->line_in_notify.gpio_private)
- pmf_unregister_irq_client(rt->line_in_notify.gpio_private);
- if (rt->line_out_notify.gpio_private)
- pmf_unregister_irq_client(rt->line_out_notify.gpio_private);
- /* make sure no work is pending before freeing
- * all things */
- cancel_delayed_work_sync(&rt->headphone_notify.work);
- cancel_delayed_work_sync(&rt->line_in_notify.work);
- cancel_delayed_work_sync(&rt->line_out_notify.work);
- mutex_destroy(&rt->headphone_notify.mutex);
- mutex_destroy(&rt->line_in_notify.mutex);
- mutex_destroy(&rt->line_out_notify.mutex);
- kfree(rt->headphone_notify.gpio_private);
- kfree(rt->line_in_notify.gpio_private);
- kfree(rt->line_out_notify.gpio_private);
- }
- static void pmf_handle_notify_irq(void *data)
- {
- struct gpio_notification *notif = data;
- schedule_delayed_work(¬if->work, 0);
- }
- static int pmf_set_notify(struct gpio_runtime *rt,
- enum notify_type type,
- notify_func_t notify,
- void *data)
- {
- struct gpio_notification *notif;
- notify_func_t old;
- struct pmf_irq_client *irq_client;
- char *name;
- int err = -EBUSY;
- switch (type) {
- case AOA_NOTIFY_HEADPHONE:
- notif = &rt->headphone_notify;
- name = "headphone-detect";
- break;
- case AOA_NOTIFY_LINE_IN:
- notif = &rt->line_in_notify;
- name = "linein-detect";
- break;
- case AOA_NOTIFY_LINE_OUT:
- notif = &rt->line_out_notify;
- name = "lineout-detect";
- break;
- default:
- return -EINVAL;
- }
- mutex_lock(¬if->mutex);
- old = notif->notify;
- if (!old && !notify) {
- err = 0;
- goto out_unlock;
- }
- if (old && notify) {
- if (old == notify && notif->data == data)
- err = 0;
- goto out_unlock;
- }
- if (old && !notify) {
- irq_client = notif->gpio_private;
- pmf_unregister_irq_client(irq_client);
- kfree(irq_client);
- notif->gpio_private = NULL;
- }
- if (!old && notify) {
- irq_client = kzalloc(sizeof(struct pmf_irq_client),
- GFP_KERNEL);
- if (!irq_client) {
- err = -ENOMEM;
- goto out_unlock;
- }
- irq_client->data = notif;
- irq_client->handler = pmf_handle_notify_irq;
- irq_client->owner = THIS_MODULE;
- err = pmf_register_irq_client(rt->node,
- name,
- irq_client);
- if (err) {
- printk(KERN_ERR "snd-aoa: gpio layer failed to"
- " register %s irq (%d)\n", name, err);
- kfree(irq_client);
- goto out_unlock;
- }
- notif->gpio_private = irq_client;
- }
- notif->notify = notify;
- notif->data = data;
- err = 0;
- out_unlock:
- mutex_unlock(¬if->mutex);
- return err;
- }
- static int pmf_get_detect(struct gpio_runtime *rt,
- enum notify_type type)
- {
- char *name;
- int err = -EBUSY, ret;
- struct pmf_args args = { .count = 1, .u[0].p = &ret };
- switch (type) {
- case AOA_NOTIFY_HEADPHONE:
- name = "headphone-detect";
- break;
- case AOA_NOTIFY_LINE_IN:
- name = "linein-detect";
- break;
- case AOA_NOTIFY_LINE_OUT:
- name = "lineout-detect";
- break;
- default:
- return -EINVAL;
- }
- err = pmf_call_function(rt->node, name, &args);
- if (err)
- return err;
- return ret;
- }
- static struct gpio_methods methods = {
- .init = pmf_gpio_init,
- .exit = pmf_gpio_exit,
- .all_amps_off = pmf_gpio_all_amps_off,
- .all_amps_restore = pmf_gpio_all_amps_restore,
- .set_headphone = pmf_gpio_set_headphone,
- .set_speakers = pmf_gpio_set_amp,
- .set_lineout = pmf_gpio_set_lineout,
- .set_hw_reset = pmf_gpio_set_hw_reset,
- .get_headphone = pmf_gpio_get_headphone,
- .get_speakers = pmf_gpio_get_amp,
- .get_lineout = pmf_gpio_get_lineout,
- .set_notify = pmf_set_notify,
- .get_detect = pmf_get_detect,
- };
- struct gpio_methods *pmf_gpio_methods = &methods;
- EXPORT_SYMBOL_GPL(pmf_gpio_methods);
|