xref: /openbmc/linux/sound/aoa/core/core.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*d6869352SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2888dcb7cSJohannes Berg /*
3888dcb7cSJohannes Berg  * Apple Onboard Audio driver core
4888dcb7cSJohannes Berg  *
5888dcb7cSJohannes Berg  * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
6888dcb7cSJohannes Berg  */
7888dcb7cSJohannes Berg 
8888dcb7cSJohannes Berg #include <linux/init.h>
9888dcb7cSJohannes Berg #include <linux/module.h>
10888dcb7cSJohannes Berg #include <linux/list.h>
11888dcb7cSJohannes Berg #include "../aoa.h"
12888dcb7cSJohannes Berg #include "alsa.h"
13888dcb7cSJohannes Berg 
14888dcb7cSJohannes Berg MODULE_DESCRIPTION("Apple Onboard Audio Sound Driver");
15888dcb7cSJohannes Berg MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
16888dcb7cSJohannes Berg MODULE_LICENSE("GPL");
17888dcb7cSJohannes Berg 
18888dcb7cSJohannes Berg /* We allow only one fabric. This simplifies things,
19888dcb7cSJohannes Berg  * and more don't really make that much sense */
20888dcb7cSJohannes Berg static struct aoa_fabric *fabric;
21888dcb7cSJohannes Berg static LIST_HEAD(codec_list);
22888dcb7cSJohannes Berg 
attach_codec_to_fabric(struct aoa_codec * c)23888dcb7cSJohannes Berg static int attach_codec_to_fabric(struct aoa_codec *c)
24888dcb7cSJohannes Berg {
25888dcb7cSJohannes Berg 	int err;
26888dcb7cSJohannes Berg 
27888dcb7cSJohannes Berg 	if (!try_module_get(c->owner))
28888dcb7cSJohannes Berg 		return -EBUSY;
29888dcb7cSJohannes Berg 	/* found_codec has to be assigned */
30888dcb7cSJohannes Berg 	err = -ENOENT;
31888dcb7cSJohannes Berg 	if (fabric->found_codec)
32888dcb7cSJohannes Berg 		err = fabric->found_codec(c);
33888dcb7cSJohannes Berg 	if (err) {
34888dcb7cSJohannes Berg 		module_put(c->owner);
35888dcb7cSJohannes Berg 		printk(KERN_ERR "snd-aoa: fabric didn't like codec %s\n",
36888dcb7cSJohannes Berg 				c->name);
37888dcb7cSJohannes Berg 		return err;
38888dcb7cSJohannes Berg 	}
39888dcb7cSJohannes Berg 	c->fabric = fabric;
40888dcb7cSJohannes Berg 
41888dcb7cSJohannes Berg 	err = 0;
42888dcb7cSJohannes Berg 	if (c->init)
43888dcb7cSJohannes Berg 		err = c->init(c);
44888dcb7cSJohannes Berg 	if (err) {
45888dcb7cSJohannes Berg 		printk(KERN_ERR "snd-aoa: codec %s didn't init\n", c->name);
46888dcb7cSJohannes Berg 		c->fabric = NULL;
47888dcb7cSJohannes Berg 		if (fabric->remove_codec)
48888dcb7cSJohannes Berg 			fabric->remove_codec(c);
49888dcb7cSJohannes Berg 		module_put(c->owner);
50888dcb7cSJohannes Berg 		return err;
51888dcb7cSJohannes Berg 	}
52888dcb7cSJohannes Berg 	if (fabric->attached_codec)
53888dcb7cSJohannes Berg 		fabric->attached_codec(c);
54888dcb7cSJohannes Berg 	return 0;
55888dcb7cSJohannes Berg }
56888dcb7cSJohannes Berg 
aoa_codec_register(struct aoa_codec * codec)57888dcb7cSJohannes Berg int aoa_codec_register(struct aoa_codec *codec)
58888dcb7cSJohannes Berg {
59888dcb7cSJohannes Berg 	int err = 0;
60888dcb7cSJohannes Berg 
61888dcb7cSJohannes Berg 	/* if there's a fabric already, we can tell if we
62888dcb7cSJohannes Berg 	 * will want to have this codec, so propagate error
63888dcb7cSJohannes Berg 	 * through. Otherwise, this will happen later... */
64888dcb7cSJohannes Berg 	if (fabric)
65888dcb7cSJohannes Berg 		err = attach_codec_to_fabric(codec);
66888dcb7cSJohannes Berg 	if (!err)
67888dcb7cSJohannes Berg 		list_add(&codec->list, &codec_list);
68888dcb7cSJohannes Berg 	return err;
69888dcb7cSJohannes Berg }
70888dcb7cSJohannes Berg EXPORT_SYMBOL_GPL(aoa_codec_register);
71888dcb7cSJohannes Berg 
aoa_codec_unregister(struct aoa_codec * codec)72888dcb7cSJohannes Berg void aoa_codec_unregister(struct aoa_codec *codec)
73888dcb7cSJohannes Berg {
74888dcb7cSJohannes Berg 	list_del(&codec->list);
75888dcb7cSJohannes Berg 	if (codec->fabric && codec->exit)
76888dcb7cSJohannes Berg 		codec->exit(codec);
77888dcb7cSJohannes Berg 	if (fabric && fabric->remove_codec)
78888dcb7cSJohannes Berg 		fabric->remove_codec(codec);
79888dcb7cSJohannes Berg 	codec->fabric = NULL;
80888dcb7cSJohannes Berg 	module_put(codec->owner);
81888dcb7cSJohannes Berg }
82888dcb7cSJohannes Berg EXPORT_SYMBOL_GPL(aoa_codec_unregister);
83888dcb7cSJohannes Berg 
aoa_fabric_register(struct aoa_fabric * new_fabric,struct device * dev)84888dcb7cSJohannes Berg int aoa_fabric_register(struct aoa_fabric *new_fabric, struct device *dev)
85888dcb7cSJohannes Berg {
86888dcb7cSJohannes Berg 	struct aoa_codec *c;
87888dcb7cSJohannes Berg 	int err;
88888dcb7cSJohannes Berg 
89888dcb7cSJohannes Berg 	/* allow querying for presence of fabric
90888dcb7cSJohannes Berg 	 * (i.e. do this test first!) */
91888dcb7cSJohannes Berg 	if (new_fabric == fabric) {
92888dcb7cSJohannes Berg 		err = -EALREADY;
93888dcb7cSJohannes Berg 		goto attach;
94888dcb7cSJohannes Berg 	}
95888dcb7cSJohannes Berg 	if (fabric)
96888dcb7cSJohannes Berg 		return -EEXIST;
97888dcb7cSJohannes Berg 	if (!new_fabric)
98888dcb7cSJohannes Berg 		return -EINVAL;
99888dcb7cSJohannes Berg 
100888dcb7cSJohannes Berg 	err = aoa_alsa_init(new_fabric->name, new_fabric->owner, dev);
101888dcb7cSJohannes Berg 	if (err)
102888dcb7cSJohannes Berg 		return err;
103888dcb7cSJohannes Berg 
104888dcb7cSJohannes Berg 	fabric = new_fabric;
105888dcb7cSJohannes Berg 
106888dcb7cSJohannes Berg  attach:
107888dcb7cSJohannes Berg 	list_for_each_entry(c, &codec_list, list) {
108888dcb7cSJohannes Berg 		if (c->fabric != fabric)
109888dcb7cSJohannes Berg 			attach_codec_to_fabric(c);
110888dcb7cSJohannes Berg 	}
111888dcb7cSJohannes Berg 	return err;
112888dcb7cSJohannes Berg }
113888dcb7cSJohannes Berg EXPORT_SYMBOL_GPL(aoa_fabric_register);
114888dcb7cSJohannes Berg 
aoa_fabric_unregister(struct aoa_fabric * old_fabric)115888dcb7cSJohannes Berg void aoa_fabric_unregister(struct aoa_fabric *old_fabric)
116888dcb7cSJohannes Berg {
117888dcb7cSJohannes Berg 	struct aoa_codec *c;
118888dcb7cSJohannes Berg 
119888dcb7cSJohannes Berg 	if (fabric != old_fabric)
120888dcb7cSJohannes Berg 		return;
121888dcb7cSJohannes Berg 
122888dcb7cSJohannes Berg 	list_for_each_entry(c, &codec_list, list) {
123888dcb7cSJohannes Berg 		if (c->fabric)
124888dcb7cSJohannes Berg 			aoa_fabric_unlink_codec(c);
125888dcb7cSJohannes Berg 	}
126888dcb7cSJohannes Berg 
127888dcb7cSJohannes Berg 	aoa_alsa_cleanup();
128888dcb7cSJohannes Berg 
129888dcb7cSJohannes Berg 	fabric = NULL;
130888dcb7cSJohannes Berg }
131888dcb7cSJohannes Berg EXPORT_SYMBOL_GPL(aoa_fabric_unregister);
132888dcb7cSJohannes Berg 
aoa_fabric_unlink_codec(struct aoa_codec * codec)133888dcb7cSJohannes Berg void aoa_fabric_unlink_codec(struct aoa_codec *codec)
134888dcb7cSJohannes Berg {
135888dcb7cSJohannes Berg 	if (!codec->fabric) {
136888dcb7cSJohannes Berg 		printk(KERN_ERR "snd-aoa: fabric unassigned "
137888dcb7cSJohannes Berg 				"in aoa_fabric_unlink_codec\n");
138888dcb7cSJohannes Berg 		dump_stack();
139888dcb7cSJohannes Berg 		return;
140888dcb7cSJohannes Berg 	}
141888dcb7cSJohannes Berg 	if (codec->exit)
142888dcb7cSJohannes Berg 		codec->exit(codec);
143888dcb7cSJohannes Berg 	if (codec->fabric->remove_codec)
144888dcb7cSJohannes Berg 		codec->fabric->remove_codec(codec);
145888dcb7cSJohannes Berg 	codec->fabric = NULL;
146888dcb7cSJohannes Berg 	module_put(codec->owner);
147888dcb7cSJohannes Berg }
148888dcb7cSJohannes Berg EXPORT_SYMBOL_GPL(aoa_fabric_unlink_codec);
149888dcb7cSJohannes Berg 
aoa_init(void)150888dcb7cSJohannes Berg static int __init aoa_init(void)
151888dcb7cSJohannes Berg {
152888dcb7cSJohannes Berg 	return 0;
153888dcb7cSJohannes Berg }
154888dcb7cSJohannes Berg 
aoa_exit(void)155888dcb7cSJohannes Berg static void __exit aoa_exit(void)
156888dcb7cSJohannes Berg {
157888dcb7cSJohannes Berg 	aoa_alsa_cleanup();
158888dcb7cSJohannes Berg }
159888dcb7cSJohannes Berg 
160888dcb7cSJohannes Berg module_init(aoa_init);
161888dcb7cSJohannes Berg module_exit(aoa_exit);
162