Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/w1-2.6
[linux-2.6] / sound / aoa / core / snd-aoa-core.c
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
15 MODULE_DESCRIPTION("Apple Onboard Audio Sound Driver");
16 MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
17 MODULE_LICENSE("GPL");
18
19 /* We allow only one fabric. This simplifies things,
20  * and more don't really make that much sense */
21 static struct aoa_fabric *fabric;
22 static LIST_HEAD(codec_list);
23
24 static 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
58 int 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 }
71 EXPORT_SYMBOL_GPL(aoa_codec_register);
72
73 void 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 }
83 EXPORT_SYMBOL_GPL(aoa_codec_unregister);
84
85 int aoa_fabric_register(struct aoa_fabric *new_fabric)
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);
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 }
114 EXPORT_SYMBOL_GPL(aoa_fabric_register);
115
116 void 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 }
132 EXPORT_SYMBOL_GPL(aoa_fabric_unregister);
133
134 void 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 }
149 EXPORT_SYMBOL_GPL(aoa_fabric_unlink_codec);
150
151 static int __init aoa_init(void)
152 {
153         return 0;
154 }
155
156 static void __exit aoa_exit(void)
157 {
158         aoa_alsa_cleanup();
159 }
160
161 module_init(aoa_init);
162 module_exit(aoa_exit);