/* * Apple Onboard Audio pmf GPIOs * * Copyright 2006 Johannes Berg * * GPL v2, can be found in COPYING. */ #include #include #include #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<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); -rw-r--r--drm_atomic_helper.h10607logplain -rw-r--r--drm_auth.h2260logplain -rw-r--r--drm_blend.h2700logplain -rw-r--r--drm_bridge.h7977logplain -rw-r--r--drm_cache.h1763logplain -rw-r--r--drm_color_mgmt.h2166logplain -rw-r--r--drm_connector.h28613logplain -rw-r--r--drm_crtc.h28999logplain -rw-r--r--drm_crtc_helper.h3301logplain -rw-r--r--drm_debugfs_crc.h2670logplain -rw-r--r--drm_displayid.h2850logplain -rw-r--r--drm_dp_dual_mode_helper.h4532logplain -rw-r--r--drm_dp_helper.h30638logplain -rw-r--r--drm_dp_mst_helper.h16260logplain -rw-r--r--drm_drv.h14861logplain -rw-r--r--drm_edid.h14335logplain -rw-r--r--drm_encoder.h8160logplain -rw-r--r--drm_encoder_slave.h6583logplain -rw-r--r--drm_fb_cma_helper.h1929logplain -rw-r--r--drm_fb_helper.h15125logplain -rw-r--r--drm_fixed.h4825logplain -rw-r--r--drm_flip_work.h3071logplain -rw-r--r--drm_fourcc.h2748logplain -rw-r--r--drm_framebuffer.h9623logplain -rw-r--r--drm_gem.h8052logplain -rw-r--r--drm_gem_cma_helper.h2286logplain -rw-r--r--drm_global.h2011logplain -rw-r--r--drm_hashtab.h3139logplain -rw-r--r--drm_irq.h6323logplain -rw-r--r--drm_legacy.h6933logplain -rw-r--r--drm_mem_util.h2468logplain -rw-r--r--drm_mipi_dsi.h10327logplain -rw-r--r--drm_mm.h11462logplain -rw-r--r--drm_mode_config.h23728logplain -rw-r--r--drm_mode_object.h4911logplain -rw-r--r--drm_modes.h17078logplain -rw-r--r--drm_modeset_helper.h1576logplain -rw-r--r--drm_modeset_helper_vtables.h41180logplain -rw-r--r--drm_modeset_lock.h4241logplain -rw-r--r--drm_of.h2034logplain -rw-r--r--drm_os_linux.h2230logplain -rw-r--r--drm_panel.h7079logplain -rw-r--r--drm_pciids.h68035logplain -rw-r--r--drm_plane.h19435logplain -rw-r--r--drm_plane_helper.h3186logplain -rw-r--r--drm_print.h3232logplain -rw-r--r--drm_property.h11460logplain -rw-r--r--drm_rect.h5103logplain -rw-r--r--drm_simple_kms_helper.h4090logplain -rw-r--r--drm_sysfs.h300logplain -rw-r--r--drm_vma_manager.h7840logplain -rw-r--r--gma_drm.h1033logplain d---------i2c109logplain