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