xref: /openbmc/linux/sound/aoa/core/core.c (revision 4f3db074)
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 "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, 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 }
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);
163