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 | |
23 | static int attach_codec_to_fabric(struct aoa_codec *c) |
24 | { |
25 | int err; |
26 | |
27 | if (!try_module_get(module: 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(module: 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(module: c->owner); |
50 | return err; |
51 | } |
52 | if (fabric->attached_codec) |
53 | fabric->attached_codec(c); |
54 | return 0; |
55 | } |
56 | |
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(c: codec); |
66 | if (!err) |
67 | list_add(new: &codec->list, head: &codec_list); |
68 | return err; |
69 | } |
70 | EXPORT_SYMBOL_GPL(aoa_codec_register); |
71 | |
72 | void aoa_codec_unregister(struct aoa_codec *codec) |
73 | { |
74 | list_del(entry: &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(module: codec->owner); |
81 | } |
82 | EXPORT_SYMBOL_GPL(aoa_codec_unregister); |
83 | |
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(name: new_fabric->name, mod: 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 | |
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(codec: c); |
125 | } |
126 | |
127 | aoa_alsa_cleanup(); |
128 | |
129 | fabric = NULL; |
130 | } |
131 | EXPORT_SYMBOL_GPL(aoa_fabric_unregister); |
132 | |
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(module: codec->owner); |
147 | } |
148 | EXPORT_SYMBOL_GPL(aoa_fabric_unlink_codec); |
149 | |
150 | static int __init aoa_init(void) |
151 | { |
152 | return 0; |
153 | } |
154 | |
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 | |