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