1/*
2 * Apple Onboard Audio driver core
3 *
4 * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
5 *
6 * GPL v2, can be found in COPYING.
7 */
8
9#include <linux/init.h>
10#include <linux/module.h>
11#include <linux/list.h>
12#include "../aoa.h"
13#include "snd-aoa-alsa.h"
14
15MODULE_DESCRIPTION("Apple Onboard Audio Sound Driver");
16MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
17MODULE_LICENSE("GPL");
18
19/* We allow only one fabric. This simplifies things,
20 * and more don't really make that much sense */
21static struct aoa_fabric *fabric;
22static LIST_HEAD(codec_list);
23
24static int attach_codec_to_fabric(struct aoa_codec *c)
25{
26	int err;
27
28	if (!try_module_get(c->owner))
29		return -EBUSY;
30	/* found_codec has to be assigned */
31	err = -ENOENT;
32	if (fabric->found_codec)
33		err = fabric->found_codec(c);
34	if (err) {
35		module_put(c->owner);
36		printk(KERN_ERR "snd-aoa: fabric didn't like codec %s\n",
37				c->name);
38		return err;
39	}
40	c->fabric = fabric;
41
42	err = 0;
43	if (c->init)
44		err = c->init(c);
45	if (err) {
46		printk(KERN_ERR "snd-aoa: codec %s didn't init\n", c->name);
47		c->fabric = NULL;
48		if (fabric->remove_codec)
49			fabric->remove_codec(c);
50		module_put(c->owner);
51		return err;
52	}
53	if (fabric->attached_codec)
54		fabric->attached_codec(c);
55	return 0;
56}
57
58int aoa_codec_register(struct aoa_codec *codec)
59{
60	int err = 0;
61
62	/* if there's a fabric already, we can tell if we
63	 * will want to have this codec, so propagate error
64	 * through. Otherwise, this will happen later... */
65	if (fabric)
66		err = attach_codec_to_fabric(codec);
67	if (!err)
68		list_add(&codec->list, &codec_list);
69	return err;
70}
71EXPORT_SYMBOL_GPL(aoa_codec_register);
72
73void aoa_codec_unregister(struct aoa_codec *codec)
74{
75	list_del(&codec->list);
76	if (codec->fabric && codec->exit)
77		codec->exit(codec);
78	if (fabric && fabric->remove_codec)
79		fabric->remove_codec(codec);
80	codec->fabric = NULL;
81	module_put(codec->owner);
82}
83EXPORT_SYMBOL_GPL(aoa_codec_unregister);
84
85int aoa_fabric_register(struct aoa_fabric *new_fabric, struct device *dev)
86{
87	struct aoa_codec *c;
88	int err;
89
90	/* allow querying for presence of fabric
91	 * (i.e. do this test first!) */
92	if (new_fabric == fabric) {
93		err = -EALREADY;
94		goto attach;
95	}
96	if (fabric)
97		return -EEXIST;
98	if (!new_fabric)
99		return -EINVAL;
100
101	err = aoa_alsa_init(new_fabric->name, new_fabric->owner, dev);
102	if (err)
103		return err;
104
105	fabric = new_fabric;
106
107 attach:
108	list_for_each_entry(c, &codec_list, list) {
109		if (c->fabric != fabric)
110			attach_codec_to_fabric(c);
111	}
112	return err;
113}
114EXPORT_SYMBOL_GPL(aoa_fabric_register);
115
116void aoa_fabric_unregister(struct aoa_fabric *old_fabric)
117{
118	struct aoa_codec *c;
119
120	if (fabric != old_fabric)
121		return;
122
123	list_for_each_entry(c, &codec_list, list) {
124		if (c->fabric)
125			aoa_fabric_unlink_codec(c);
126	}
127
128	aoa_alsa_cleanup();
129
130	fabric = NULL;
131}
132EXPORT_SYMBOL_GPL(aoa_fabric_unregister);
133
134void aoa_fabric_unlink_codec(struct aoa_codec *codec)
135{
136	if (!codec->fabric) {
137		printk(KERN_ERR "snd-aoa: fabric unassigned "
138				"in aoa_fabric_unlink_codec\n");
139		dump_stack();
140		return;
141	}
142	if (codec->exit)
143		codec->exit(codec);
144	if (codec->fabric->remove_codec)
145		codec->fabric->remove_codec(codec);
146	codec->fabric = NULL;
147	module_put(codec->owner);
148}
149EXPORT_SYMBOL_GPL(aoa_fabric_unlink_codec);
150
151static int __init aoa_init(void)
152{
153	return 0;
154}
155
156static void __exit aoa_exit(void)
157{
158	aoa_alsa_cleanup();
159}
160
161module_init(aoa_init);
162module_exit(aoa_exit);
163