From: Johannes Berg <johannes@sipsolutions.net>
To: alsa-devel@alsa-project.org
Cc: linuxppc-dev@ozlabs.org
Subject: [RFC 2/8] snd-aoa: add aoa core
Date: Thu, 01 Jun 2006 13:58:46 +0200 [thread overview]
Message-ID: <20060601115845.923706000@sipsolutions.net> (raw)
In-Reply-To: 20060601115844.343214000@sipsolutions.net
[-- Attachment #1: add-core.patch --]
[-- Type: text/plain, Size: 12627 bytes --]
This patch adds the core of aoa, in itself pretty useless, but providing
useful functions to other modules.
--- /dev/null
+++ b/sound/aoa/core/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_SND_AOA) += snd-aoa.o
+snd-aoa-objs := snd-aoa-core.o \
+ snd-aoa-alsa.o \
+ snd-aoa-gpio-pmf.o
--- /dev/null
+++ b/sound/aoa/core/snd-aoa-core.c
@@ -0,0 +1,153 @@
+/*
+ * Apple Onboard Audio driver core
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include "../aoa.h"
+#include "snd-aoa-alsa.h"
+
+MODULE_DESCRIPTION("Apple Onboard Audio Sound Driver");
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_LICENSE("GPL");
+
+/* We allow only one fabric. This simplifies things,
+ * and more don't really make that much sense */
+static struct aoa_fabric *fabric;
+static LIST_HEAD(codec_list);
+
+static void attach_codec_to_fabric(struct aoa_codec *c)
+{
+ int err;
+
+ if (!try_module_get(c->owner))
+ return;
+ /* found_codec has to be assigned */
+ err = -ENOENT;
+ if (fabric->found_codec)
+ err = fabric->found_codec(c);
+ if (err) {
+ module_put(c->owner);
+ printk("snd-aoa: fabric didn't like codec %s\n", c->name);
+ return;
+ }
+ c->fabric = fabric;
+
+ err = 0;
+ if (c->init)
+ err = c->init(c);
+ if (err) {
+ printk("snd-aoa: codec %s didn't init\n", c->name);
+ c->fabric = NULL;
+ if (fabric->remove_codec)
+ fabric->remove_codec(c);
+ module_put(c->owner);
+ return;
+ }
+ if (fabric->attached_codec)
+ fabric->attached_codec(c);
+}
+
+int aoa_codec_register(struct aoa_codec *codec)
+{
+ list_add(&codec->list, &codec_list);
+ if (fabric)
+ attach_codec_to_fabric(codec);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aoa_codec_register);
+
+void aoa_codec_unregister(struct aoa_codec *codec)
+{
+ list_del(&codec->list);
+ if (codec->fabric && codec->exit)
+ codec->exit(codec);
+ if (fabric && fabric->remove_codec)
+ fabric->remove_codec(codec);
+ codec->fabric = NULL;
+ module_put(codec->owner);
+}
+EXPORT_SYMBOL_GPL(aoa_codec_unregister);
+
+int aoa_fabric_register(struct aoa_fabric *new_fabric)
+{
+ struct aoa_codec *c;
+ int err;
+
+ /* allow querying for presence of fabric
+ * (i.e. do this test first!) */
+ if (new_fabric == fabric) {
+ err = -EALREADY;
+ goto attach;
+ }
+ if (fabric)
+ return -EEXIST;
+ if (!new_fabric)
+ return -EINVAL;
+
+ err = aoa_alsa_init(new_fabric->name, new_fabric->owner);
+ if (err)
+ return err;
+
+ fabric = new_fabric;
+
+ attach:
+ list_for_each_entry(c, &codec_list, list) {
+ if (c->fabric != fabric)
+ attach_codec_to_fabric(c);
+ }
+ return err;
+}
+EXPORT_SYMBOL_GPL(aoa_fabric_register);
+
+void aoa_fabric_unregister(struct aoa_fabric *old_fabric)
+{
+ struct aoa_codec *c;
+
+ if (fabric != old_fabric)
+ return;
+
+ list_for_each_entry(c, &codec_list, list) {
+ if (c->fabric)
+ aoa_fabric_unlink_codec(c);
+ }
+
+ aoa_alsa_cleanup();
+
+ fabric = NULL;
+}
+EXPORT_SYMBOL_GPL(aoa_fabric_unregister);
+
+void aoa_fabric_unlink_codec(struct aoa_codec *codec)
+{
+ if (!codec->fabric) {
+ printk(KERN_ERR "snd-aoa: fabric unassigned in aoa_fabric_unlink_codec\n");
+ dump_stack();
+ return;
+ }
+ if (codec->exit)
+ codec->exit(codec);
+ if (codec->fabric->remove_codec)
+ codec->fabric->remove_codec(codec);
+ codec->fabric = NULL;
+ module_put(codec->owner);
+}
+EXPORT_SYMBOL_GPL(aoa_fabric_unlink_codec);
+
+static int __init aoa_init(void)
+{
+ return 0;
+}
+
+static void __exit aoa_exit(void)
+{
+ aoa_alsa_cleanup();
+}
+
+module_init(aoa_init);
+module_exit(aoa_exit);
--- /dev/null
+++ b/sound/aoa/core/snd-aoa-gpio-pmf.c
@@ -0,0 +1,231 @@
+/*
+ * Apple Onboard Audio pmf GPIOs
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#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 }; \
+ \
+ if (unlikely(!rt)) return; \
+ pmf_call_function(rt->node, #name "-mute", &args); \
+ 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 };
+
+ if (unlikely(!rt)) return;
+ pmf_call_function(rt->node, "hw-reset", &args);
+}
+
+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(void *data)
+{
+ struct gpio_notification *notif = data;
+
+ 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_WORK(&rt->headphone_notify.work, pmf_handle_notify, &rt->headphone_notify);
+ INIT_WORK(&rt->line_in_notify.work, pmf_handle_notify, &rt->line_in_notify);
+ INIT_WORK(&rt->line_out_notify.work, pmf_handle_notify, &rt->line_out_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);
+ cancel_delayed_work(&rt->headphone_notify.work);
+ cancel_delayed_work(&rt->line_in_notify.work);
+ cancel_delayed_work(&rt->line_out_notify.work);
+ flush_scheduled_work();
+ mutex_destroy(&rt->headphone_notify.mutex);
+ mutex_destroy(&rt->line_in_notify.mutex);
+ mutex_destroy(&rt->line_out_notify.mutex);
+}
+
+void pmf_handle_notify_irq(void *data)
+{
+ struct gpio_notification *notif = data;
+
+ schedule_work(¬if->work);
+}
+
+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);
+ 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);
--- /dev/null
+++ b/sound/aoa/core/snd-aoa-alsa.h
@@ -0,0 +1,17 @@
+/*
+ * Apple Onboard Audio Alsa private helpers
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#ifndef __SND_AOA_ALSA_H
+#define __SND_AOA_ALSA_H
+/* FIXME */
+#include "../aoa.h"
+
+extern int aoa_alsa_init(char *name, struct module *mod);
+extern void aoa_alsa_cleanup(void);
+
+#endif /* __SND_AOA_ALSA_H */
--- /dev/null
+++ b/sound/aoa/core/snd-aoa-alsa.c
@@ -0,0 +1,95 @@
+/*
+ * Apple Onboard Audio Alsa helpers
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+#include <linux/module.h>
+#include "snd-aoa-alsa.h"
+
+static int index = -1;
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "index for AOA sound card.");
+
+static struct aoa_card *aoa_card;
+
+int aoa_alsa_init(char *name, struct module *mod)
+{
+ struct snd_card *alsa_card;
+ int err;
+
+ if (aoa_card)
+ /* cannot be EEXIST due to usage in aoa_fabric_register */
+ return -EBUSY;
+
+ alsa_card = snd_card_new(index, name, mod, sizeof(struct aoa_card));
+ if (!alsa_card)
+ return -ENOMEM;
+ aoa_card = alsa_card->private_data;
+ aoa_card->alsa_card = alsa_card;
+ strlcpy(alsa_card->driver, "AppleOnbdAudio", sizeof(alsa_card->driver)-1);
+ strlcpy(alsa_card->shortname, name, sizeof(alsa_card->shortname)-1);
+ strlcpy(alsa_card->longname, name, sizeof(alsa_card->longname)-1);
+ strlcpy(alsa_card->mixername, name, sizeof(alsa_card->mixername)-1);
+ err = snd_card_register(aoa_card->alsa_card);
+ if (err < 0) {
+ printk(KERN_ERR "snd-aoa: couldn't register alsa card\n");
+ snd_card_free(aoa_card->alsa_card);
+ aoa_card = NULL;
+ return err;
+ }
+ return 0;
+}
+
+struct snd_card *aoa_get_card(void)
+{
+ if (aoa_card)
+ return aoa_card->alsa_card;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(aoa_get_card);
+
+void aoa_alsa_cleanup(void)
+{
+ if (aoa_card) {
+ snd_card_free(aoa_card->alsa_card);
+ aoa_card = NULL;
+ }
+}
+
+int aoa_snd_device_new(snd_device_type_t type,
+ void * device_data, struct snd_device_ops * ops)
+{
+ struct snd_card *card = aoa_get_card();
+ int err;
+
+ if (!card) return -ENOMEM;
+
+ err = snd_device_new(card, type, device_data, ops);
+ if (err) {
+ printk(KERN_ERR "snd-aoa: failed to create snd device (%d)\n", err);
+ return err;
+ }
+ err = snd_device_register(card, device_data);
+ if (err) {
+ printk(KERN_ERR "snd-aoa: failed to register snd device (%d)\n", err);
+ printk(KERN_ERR "snd-aoa: have you forgotten the dev_register callback?\n");
+ snd_device_free(card, device_data);
+ }
+ return err;
+}
+EXPORT_SYMBOL_GPL(aoa_snd_device_new);
+
+int aoa_snd_ctl_add(struct snd_kcontrol* control)
+{
+ int err;
+
+ if (!aoa_card) return -ENODEV;
+
+ err = snd_ctl_add(aoa_card->alsa_card, control);
+ if (err)
+ printk(KERN_ERR "snd-aoa: failed to add alsa control (%d)\n", err);
+ return err;
+}
+EXPORT_SYMBOL_GPL(aoa_snd_ctl_add);
--
WARNING: multiple messages have this Message-ID (diff)
From: Johannes Berg <johannes@sipsolutions.net>
To: alsa-devel@alsa-project.org
Cc: linuxppc-dev@ozlabs.org
Subject: [RFC 2/8] snd-aoa: add aoa core
Date: Thu, 01 Jun 2006 13:58:46 +0200 [thread overview]
Message-ID: <20060601115845.923706000@sipsolutions.net> (raw)
In-Reply-To: 20060601115844.343214000@sipsolutions.net
This patch adds the core of aoa, in itself pretty useless, but providing
useful functions to other modules.
--- /dev/null
+++ b/sound/aoa/core/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_SND_AOA) += snd-aoa.o
+snd-aoa-objs := snd-aoa-core.o \
+ snd-aoa-alsa.o \
+ snd-aoa-gpio-pmf.o
--- /dev/null
+++ b/sound/aoa/core/snd-aoa-core.c
@@ -0,0 +1,153 @@
+/*
+ * Apple Onboard Audio driver core
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include "../aoa.h"
+#include "snd-aoa-alsa.h"
+
+MODULE_DESCRIPTION("Apple Onboard Audio Sound Driver");
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_LICENSE("GPL");
+
+/* We allow only one fabric. This simplifies things,
+ * and more don't really make that much sense */
+static struct aoa_fabric *fabric;
+static LIST_HEAD(codec_list);
+
+static void attach_codec_to_fabric(struct aoa_codec *c)
+{
+ int err;
+
+ if (!try_module_get(c->owner))
+ return;
+ /* found_codec has to be assigned */
+ err = -ENOENT;
+ if (fabric->found_codec)
+ err = fabric->found_codec(c);
+ if (err) {
+ module_put(c->owner);
+ printk("snd-aoa: fabric didn't like codec %s\n", c->name);
+ return;
+ }
+ c->fabric = fabric;
+
+ err = 0;
+ if (c->init)
+ err = c->init(c);
+ if (err) {
+ printk("snd-aoa: codec %s didn't init\n", c->name);
+ c->fabric = NULL;
+ if (fabric->remove_codec)
+ fabric->remove_codec(c);
+ module_put(c->owner);
+ return;
+ }
+ if (fabric->attached_codec)
+ fabric->attached_codec(c);
+}
+
+int aoa_codec_register(struct aoa_codec *codec)
+{
+ list_add(&codec->list, &codec_list);
+ if (fabric)
+ attach_codec_to_fabric(codec);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aoa_codec_register);
+
+void aoa_codec_unregister(struct aoa_codec *codec)
+{
+ list_del(&codec->list);
+ if (codec->fabric && codec->exit)
+ codec->exit(codec);
+ if (fabric && fabric->remove_codec)
+ fabric->remove_codec(codec);
+ codec->fabric = NULL;
+ module_put(codec->owner);
+}
+EXPORT_SYMBOL_GPL(aoa_codec_unregister);
+
+int aoa_fabric_register(struct aoa_fabric *new_fabric)
+{
+ struct aoa_codec *c;
+ int err;
+
+ /* allow querying for presence of fabric
+ * (i.e. do this test first!) */
+ if (new_fabric == fabric) {
+ err = -EALREADY;
+ goto attach;
+ }
+ if (fabric)
+ return -EEXIST;
+ if (!new_fabric)
+ return -EINVAL;
+
+ err = aoa_alsa_init(new_fabric->name, new_fabric->owner);
+ if (err)
+ return err;
+
+ fabric = new_fabric;
+
+ attach:
+ list_for_each_entry(c, &codec_list, list) {
+ if (c->fabric != fabric)
+ attach_codec_to_fabric(c);
+ }
+ return err;
+}
+EXPORT_SYMBOL_GPL(aoa_fabric_register);
+
+void aoa_fabric_unregister(struct aoa_fabric *old_fabric)
+{
+ struct aoa_codec *c;
+
+ if (fabric != old_fabric)
+ return;
+
+ list_for_each_entry(c, &codec_list, list) {
+ if (c->fabric)
+ aoa_fabric_unlink_codec(c);
+ }
+
+ aoa_alsa_cleanup();
+
+ fabric = NULL;
+}
+EXPORT_SYMBOL_GPL(aoa_fabric_unregister);
+
+void aoa_fabric_unlink_codec(struct aoa_codec *codec)
+{
+ if (!codec->fabric) {
+ printk(KERN_ERR "snd-aoa: fabric unassigned in aoa_fabric_unlink_codec\n");
+ dump_stack();
+ return;
+ }
+ if (codec->exit)
+ codec->exit(codec);
+ if (codec->fabric->remove_codec)
+ codec->fabric->remove_codec(codec);
+ codec->fabric = NULL;
+ module_put(codec->owner);
+}
+EXPORT_SYMBOL_GPL(aoa_fabric_unlink_codec);
+
+static int __init aoa_init(void)
+{
+ return 0;
+}
+
+static void __exit aoa_exit(void)
+{
+ aoa_alsa_cleanup();
+}
+
+module_init(aoa_init);
+module_exit(aoa_exit);
--- /dev/null
+++ b/sound/aoa/core/snd-aoa-gpio-pmf.c
@@ -0,0 +1,231 @@
+/*
+ * Apple Onboard Audio pmf GPIOs
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#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 }; \
+ \
+ if (unlikely(!rt)) return; \
+ pmf_call_function(rt->node, #name "-mute", &args); \
+ 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 };
+
+ if (unlikely(!rt)) return;
+ pmf_call_function(rt->node, "hw-reset", &args);
+}
+
+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(void *data)
+{
+ struct gpio_notification *notif = data;
+
+ 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_WORK(&rt->headphone_notify.work, pmf_handle_notify, &rt->headphone_notify);
+ INIT_WORK(&rt->line_in_notify.work, pmf_handle_notify, &rt->line_in_notify);
+ INIT_WORK(&rt->line_out_notify.work, pmf_handle_notify, &rt->line_out_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);
+ cancel_delayed_work(&rt->headphone_notify.work);
+ cancel_delayed_work(&rt->line_in_notify.work);
+ cancel_delayed_work(&rt->line_out_notify.work);
+ flush_scheduled_work();
+ mutex_destroy(&rt->headphone_notify.mutex);
+ mutex_destroy(&rt->line_in_notify.mutex);
+ mutex_destroy(&rt->line_out_notify.mutex);
+}
+
+void pmf_handle_notify_irq(void *data)
+{
+ struct gpio_notification *notif = data;
+
+ schedule_work(¬if->work);
+}
+
+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);
+ 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);
--- /dev/null
+++ b/sound/aoa/core/snd-aoa-alsa.h
@@ -0,0 +1,17 @@
+/*
+ * Apple Onboard Audio Alsa private helpers
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#ifndef __SND_AOA_ALSA_H
+#define __SND_AOA_ALSA_H
+/* FIXME */
+#include "../aoa.h"
+
+extern int aoa_alsa_init(char *name, struct module *mod);
+extern void aoa_alsa_cleanup(void);
+
+#endif /* __SND_AOA_ALSA_H */
--- /dev/null
+++ b/sound/aoa/core/snd-aoa-alsa.c
@@ -0,0 +1,95 @@
+/*
+ * Apple Onboard Audio Alsa helpers
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+#include <linux/module.h>
+#include "snd-aoa-alsa.h"
+
+static int index = -1;
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "index for AOA sound card.");
+
+static struct aoa_card *aoa_card;
+
+int aoa_alsa_init(char *name, struct module *mod)
+{
+ struct snd_card *alsa_card;
+ int err;
+
+ if (aoa_card)
+ /* cannot be EEXIST due to usage in aoa_fabric_register */
+ return -EBUSY;
+
+ alsa_card = snd_card_new(index, name, mod, sizeof(struct aoa_card));
+ if (!alsa_card)
+ return -ENOMEM;
+ aoa_card = alsa_card->private_data;
+ aoa_card->alsa_card = alsa_card;
+ strlcpy(alsa_card->driver, "AppleOnbdAudio", sizeof(alsa_card->driver)-1);
+ strlcpy(alsa_card->shortname, name, sizeof(alsa_card->shortname)-1);
+ strlcpy(alsa_card->longname, name, sizeof(alsa_card->longname)-1);
+ strlcpy(alsa_card->mixername, name, sizeof(alsa_card->mixername)-1);
+ err = snd_card_register(aoa_card->alsa_card);
+ if (err < 0) {
+ printk(KERN_ERR "snd-aoa: couldn't register alsa card\n");
+ snd_card_free(aoa_card->alsa_card);
+ aoa_card = NULL;
+ return err;
+ }
+ return 0;
+}
+
+struct snd_card *aoa_get_card(void)
+{
+ if (aoa_card)
+ return aoa_card->alsa_card;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(aoa_get_card);
+
+void aoa_alsa_cleanup(void)
+{
+ if (aoa_card) {
+ snd_card_free(aoa_card->alsa_card);
+ aoa_card = NULL;
+ }
+}
+
+int aoa_snd_device_new(snd_device_type_t type,
+ void * device_data, struct snd_device_ops * ops)
+{
+ struct snd_card *card = aoa_get_card();
+ int err;
+
+ if (!card) return -ENOMEM;
+
+ err = snd_device_new(card, type, device_data, ops);
+ if (err) {
+ printk(KERN_ERR "snd-aoa: failed to create snd device (%d)\n", err);
+ return err;
+ }
+ err = snd_device_register(card, device_data);
+ if (err) {
+ printk(KERN_ERR "snd-aoa: failed to register snd device (%d)\n", err);
+ printk(KERN_ERR "snd-aoa: have you forgotten the dev_register callback?\n");
+ snd_device_free(card, device_data);
+ }
+ return err;
+}
+EXPORT_SYMBOL_GPL(aoa_snd_device_new);
+
+int aoa_snd_ctl_add(struct snd_kcontrol* control)
+{
+ int err;
+
+ if (!aoa_card) return -ENODEV;
+
+ err = snd_ctl_add(aoa_card->alsa_card, control);
+ if (err)
+ printk(KERN_ERR "snd-aoa: failed to add alsa control (%d)\n", err);
+ return err;
+}
+EXPORT_SYMBOL_GPL(aoa_snd_ctl_add);
--
next prev parent reply other threads:[~2006-06-01 13:05 UTC|newest]
Thread overview: 63+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-06-01 11:58 [RFC 0/8] snd-aoa: add snd-aoa Johannes Berg
2006-06-01 11:58 ` [RFC 1/8] snd-aoa: add aoa header files Johannes Berg
2006-06-01 11:58 ` Johannes Berg
2006-06-01 11:58 ` Johannes Berg [this message]
2006-06-01 11:58 ` [RFC 2/8] snd-aoa: add aoa core Johannes Berg
2006-06-02 13:42 ` Takashi Iwai
2006-06-02 13:42 ` [Alsa-devel] " Takashi Iwai
2006-06-02 13:46 ` Johannes Berg
2006-06-02 13:46 ` [Alsa-devel] " Johannes Berg
2006-06-01 11:58 ` [RFC 3/8] snd-aoa: add soundbus Johannes Berg
2006-06-01 11:58 ` Johannes Berg
2006-06-02 13:53 ` Takashi Iwai
2006-06-02 13:53 ` [Alsa-devel] " Takashi Iwai
2006-06-02 14:07 ` Johannes Berg
2006-06-02 14:21 ` Takashi Iwai
2006-06-02 14:21 ` [Alsa-devel] " Takashi Iwai
2006-06-02 14:41 ` Andreas Schwab
2006-06-02 14:41 ` [Alsa-devel] " Andreas Schwab
2006-06-02 14:44 ` Johannes Berg
2006-06-02 14:44 ` [Alsa-devel] " Johannes Berg
2006-06-02 14:45 ` Takashi Iwai
2006-06-02 14:45 ` [Alsa-devel] " Takashi Iwai
2006-06-02 14:07 ` Johannes Berg
2006-06-01 11:58 ` [RFC 4/8] snd-aoa: add i2sbus Johannes Berg
2006-06-01 11:58 ` Johannes Berg
2006-06-02 14:23 ` Takashi Iwai
2006-06-02 14:23 ` [Alsa-devel] " Takashi Iwai
2006-06-06 11:17 ` Johannes Berg
2006-06-06 11:17 ` [Alsa-devel] " Johannes Berg
2006-06-06 14:00 ` Takashi Iwai
2006-06-07 11:40 ` Johannes Berg
2006-06-07 11:40 ` [Alsa-devel] " Johannes Berg
2006-06-07 12:41 ` Takashi Iwai
2006-06-07 12:41 ` [Alsa-devel] " Takashi Iwai
2006-06-07 12:45 ` Johannes Berg
2006-06-07 12:45 ` [Alsa-devel] " Johannes Berg
2006-06-07 12:49 ` Takashi Iwai
2006-06-07 12:49 ` [Alsa-devel] " Takashi Iwai
2006-06-07 12:52 ` Johannes Berg
2006-06-07 12:52 ` [Alsa-devel] " Johannes Berg
2006-06-07 12:58 ` Takashi Iwai
2006-06-07 12:58 ` [Alsa-devel] " Takashi Iwai
2006-06-07 13:01 ` Johannes Berg
2006-06-07 13:01 ` [Alsa-devel] " Johannes Berg
2006-06-06 14:00 ` Takashi Iwai
2006-06-01 11:58 ` [RFC 5/8] snd-aoa: add codecs Johannes Berg
2006-06-01 11:58 ` Johannes Berg
2006-06-02 14:31 ` Takashi Iwai
2006-06-02 14:31 ` [Alsa-devel] " Takashi Iwai
2006-06-06 11:36 ` Johannes Berg
2006-06-06 11:36 ` [Alsa-devel] " Johannes Berg
2006-06-01 11:58 ` [RFC 6/8] snd-aoa: add layout-id fabric Johannes Berg
2006-06-01 11:58 ` Johannes Berg
2006-06-01 11:58 ` [RFC 7/8] snd-aoa: add Kconfig and Makefile Johannes Berg
2006-06-01 11:58 ` Johannes Berg
2006-06-01 11:58 ` [RFC 8/8] snd-aoa: wire up aoa in sound/ Johannes Berg
2006-06-01 11:58 ` Johannes Berg
2006-06-02 14:43 ` [RFC 0/8] snd-aoa: add snd-aoa Takashi Iwai
2006-06-02 14:43 ` [Alsa-devel] " Takashi Iwai
2006-06-02 14:56 ` Johannes Berg
2006-06-02 14:56 ` [Alsa-devel] " Johannes Berg
2006-06-02 15:23 ` Takashi Iwai
2006-06-02 15:23 ` Takashi Iwai
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20060601115845.923706000@sipsolutions.net \
--to=johannes@sipsolutions.net \
--cc=alsa-devel@alsa-project.org \
--cc=linuxppc-dev@ozlabs.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.