1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright 2011 Broadcom Corporation. All rights reserved. */ |
3 | |
4 | #include <linux/dma-mapping.h> |
5 | #include <linux/init.h> |
6 | #include <linux/slab.h> |
7 | #include <linux/module.h> |
8 | |
9 | #include "../interface/vchiq_arm/vchiq_bus.h" |
10 | #include "bcm2835.h" |
11 | |
12 | static bool enable_hdmi; |
13 | static bool enable_headphones = true; |
14 | static int num_channels = MAX_SUBSTREAMS; |
15 | |
16 | module_param(enable_hdmi, bool, 0444); |
17 | MODULE_PARM_DESC(enable_hdmi, "Enables HDMI virtual audio device" ); |
18 | module_param(enable_headphones, bool, 0444); |
19 | MODULE_PARM_DESC(enable_headphones, "Enables Headphones virtual audio device" ); |
20 | module_param(num_channels, int, 0644); |
21 | MODULE_PARM_DESC(num_channels, "Number of audio channels (default: 8)" ); |
22 | |
23 | static void bcm2835_devm_free_vchi_ctx(struct device *dev, void *res) |
24 | { |
25 | struct bcm2835_vchi_ctx *vchi_ctx = res; |
26 | |
27 | bcm2835_free_vchi_ctx(vchi_ctx); |
28 | } |
29 | |
30 | static int bcm2835_devm_add_vchi_ctx(struct device *dev) |
31 | { |
32 | struct bcm2835_vchi_ctx *vchi_ctx; |
33 | int ret; |
34 | |
35 | vchi_ctx = devres_alloc(bcm2835_devm_free_vchi_ctx, sizeof(*vchi_ctx), |
36 | GFP_KERNEL); |
37 | if (!vchi_ctx) |
38 | return -ENOMEM; |
39 | |
40 | ret = bcm2835_new_vchi_ctx(dev, vchi_ctx); |
41 | if (ret) { |
42 | devres_free(res: vchi_ctx); |
43 | return ret; |
44 | } |
45 | |
46 | devres_add(dev, res: vchi_ctx); |
47 | |
48 | return 0; |
49 | } |
50 | |
51 | struct bcm2835_audio_driver { |
52 | struct device_driver driver; |
53 | const char *shortname; |
54 | const char *longname; |
55 | int minchannels; |
56 | int (*newpcm)(struct bcm2835_chip *chip, const char *name, |
57 | enum snd_bcm2835_route route, u32 numchannels); |
58 | int (*newctl)(struct bcm2835_chip *chip); |
59 | enum snd_bcm2835_route route; |
60 | }; |
61 | |
62 | static int bcm2835_audio_dual_newpcm(struct bcm2835_chip *chip, |
63 | const char *name, |
64 | enum snd_bcm2835_route route, |
65 | u32 numchannels) |
66 | { |
67 | int err; |
68 | |
69 | err = snd_bcm2835_new_pcm(chip, name, idx: 0, route, |
70 | numchannels, spdif: false); |
71 | |
72 | if (err) |
73 | return err; |
74 | |
75 | err = snd_bcm2835_new_pcm(chip, name: "IEC958" , idx: 1, route, numchannels: 1, spdif: true); |
76 | if (err) |
77 | return err; |
78 | |
79 | return 0; |
80 | } |
81 | |
82 | static int bcm2835_audio_simple_newpcm(struct bcm2835_chip *chip, |
83 | const char *name, |
84 | enum snd_bcm2835_route route, |
85 | u32 numchannels) |
86 | { |
87 | return snd_bcm2835_new_pcm(chip, name, idx: 0, route, numchannels, spdif: false); |
88 | } |
89 | |
90 | static struct bcm2835_audio_driver bcm2835_audio_hdmi = { |
91 | .driver = { |
92 | .name = "bcm2835_hdmi" , |
93 | .owner = THIS_MODULE, |
94 | }, |
95 | .shortname = "bcm2835 HDMI" , |
96 | .longname = "bcm2835 HDMI" , |
97 | .minchannels = 1, |
98 | .newpcm = bcm2835_audio_dual_newpcm, |
99 | .newctl = snd_bcm2835_new_hdmi_ctl, |
100 | .route = AUDIO_DEST_HDMI |
101 | }; |
102 | |
103 | static struct bcm2835_audio_driver bcm2835_audio_headphones = { |
104 | .driver = { |
105 | .name = "bcm2835_headphones" , |
106 | .owner = THIS_MODULE, |
107 | }, |
108 | .shortname = "bcm2835 Headphones" , |
109 | .longname = "bcm2835 Headphones" , |
110 | .minchannels = 1, |
111 | .newpcm = bcm2835_audio_simple_newpcm, |
112 | .newctl = snd_bcm2835_new_headphones_ctl, |
113 | .route = AUDIO_DEST_HEADPHONES |
114 | }; |
115 | |
116 | struct bcm2835_audio_drivers { |
117 | struct bcm2835_audio_driver *audio_driver; |
118 | const bool *is_enabled; |
119 | }; |
120 | |
121 | static struct bcm2835_audio_drivers children_devices[] = { |
122 | { |
123 | .audio_driver = &bcm2835_audio_hdmi, |
124 | .is_enabled = &enable_hdmi, |
125 | }, |
126 | { |
127 | .audio_driver = &bcm2835_audio_headphones, |
128 | .is_enabled = &enable_headphones, |
129 | }, |
130 | }; |
131 | |
132 | static void bcm2835_card_free(void *data) |
133 | { |
134 | snd_card_free(card: data); |
135 | } |
136 | |
137 | static int snd_add_child_device(struct device *dev, |
138 | struct bcm2835_audio_driver *audio_driver, |
139 | u32 numchans) |
140 | { |
141 | struct bcm2835_chip *chip; |
142 | struct snd_card *card; |
143 | int err; |
144 | |
145 | err = snd_card_new(parent: dev, idx: -1, NULL, THIS_MODULE, extra_size: sizeof(*chip), card_ret: &card); |
146 | if (err < 0) { |
147 | dev_err(dev, "Failed to create card" ); |
148 | return err; |
149 | } |
150 | |
151 | chip = card->private_data; |
152 | chip->card = card; |
153 | chip->dev = dev; |
154 | mutex_init(&chip->audio_mutex); |
155 | |
156 | chip->vchi_ctx = devres_find(dev, |
157 | release: bcm2835_devm_free_vchi_ctx, NULL, NULL); |
158 | if (!chip->vchi_ctx) { |
159 | err = -ENODEV; |
160 | goto error; |
161 | } |
162 | |
163 | strscpy(card->driver, audio_driver->driver.name, sizeof(card->driver)); |
164 | strscpy(card->shortname, audio_driver->shortname, sizeof(card->shortname)); |
165 | strscpy(card->longname, audio_driver->longname, sizeof(card->longname)); |
166 | |
167 | err = audio_driver->newpcm(chip, audio_driver->shortname, |
168 | audio_driver->route, |
169 | numchans); |
170 | if (err) { |
171 | dev_err(dev, "Failed to create pcm, error %d\n" , err); |
172 | goto error; |
173 | } |
174 | |
175 | err = audio_driver->newctl(chip); |
176 | if (err) { |
177 | dev_err(dev, "Failed to create controls, error %d\n" , err); |
178 | goto error; |
179 | } |
180 | |
181 | err = snd_card_register(card); |
182 | if (err) { |
183 | dev_err(dev, "Failed to register card, error %d\n" , err); |
184 | goto error; |
185 | } |
186 | |
187 | dev_set_drvdata(dev, data: chip); |
188 | |
189 | err = devm_add_action(dev, bcm2835_card_free, card); |
190 | if (err < 0) { |
191 | dev_err(dev, "Failed to add devm action, err %d\n" , err); |
192 | goto error; |
193 | } |
194 | |
195 | dev_info(dev, "card created with %d channels\n" , numchans); |
196 | return 0; |
197 | |
198 | error: |
199 | snd_card_free(card); |
200 | return err; |
201 | } |
202 | |
203 | static int snd_add_child_devices(struct device *device, u32 numchans) |
204 | { |
205 | int = 0; |
206 | int extrachannels_remainder = 0; |
207 | int count_devices = 0; |
208 | int = 0; |
209 | int minchannels = 0; |
210 | int i; |
211 | |
212 | for (i = 0; i < ARRAY_SIZE(children_devices); i++) |
213 | if (*children_devices[i].is_enabled) |
214 | count_devices++; |
215 | |
216 | if (!count_devices) |
217 | return 0; |
218 | |
219 | for (i = 0; i < ARRAY_SIZE(children_devices); i++) |
220 | if (*children_devices[i].is_enabled) |
221 | minchannels += |
222 | children_devices[i].audio_driver->minchannels; |
223 | |
224 | if (minchannels < numchans) { |
225 | extrachannels = numchans - minchannels; |
226 | extrachannels_per_driver = extrachannels / count_devices; |
227 | extrachannels_remainder = extrachannels % count_devices; |
228 | } |
229 | |
230 | dev_dbg(device, "minchannels %d\n" , minchannels); |
231 | dev_dbg(device, "extrachannels %d\n" , extrachannels); |
232 | dev_dbg(device, "extrachannels_per_driver %d\n" , |
233 | extrachannels_per_driver); |
234 | dev_dbg(device, "extrachannels_remainder %d\n" , |
235 | extrachannels_remainder); |
236 | |
237 | for (i = 0; i < ARRAY_SIZE(children_devices); i++) { |
238 | struct bcm2835_audio_driver *audio_driver; |
239 | int numchannels_this_device; |
240 | int err; |
241 | |
242 | if (!*children_devices[i].is_enabled) |
243 | continue; |
244 | |
245 | audio_driver = children_devices[i].audio_driver; |
246 | |
247 | if (audio_driver->minchannels > numchans) { |
248 | dev_err(device, |
249 | "Out of channels, needed %d but only %d left\n" , |
250 | audio_driver->minchannels, |
251 | numchans); |
252 | continue; |
253 | } |
254 | |
255 | numchannels_this_device = |
256 | audio_driver->minchannels + extrachannels_per_driver + |
257 | extrachannels_remainder; |
258 | extrachannels_remainder = 0; |
259 | |
260 | numchans -= numchannels_this_device; |
261 | |
262 | err = snd_add_child_device(dev: device, audio_driver, |
263 | numchans: numchannels_this_device); |
264 | if (err) |
265 | return err; |
266 | } |
267 | |
268 | return 0; |
269 | } |
270 | |
271 | static int snd_bcm2835_alsa_probe(struct vchiq_device *device) |
272 | { |
273 | struct device *dev = &device->dev; |
274 | int err; |
275 | |
276 | err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); |
277 | if (err) { |
278 | dev_err(dev, "dma_set_mask_and_coherent failed: %d\n" , err); |
279 | return err; |
280 | } |
281 | |
282 | if (num_channels <= 0 || num_channels > MAX_SUBSTREAMS) { |
283 | num_channels = MAX_SUBSTREAMS; |
284 | dev_warn(dev, "Illegal num_channels value, will use %u\n" , |
285 | num_channels); |
286 | } |
287 | |
288 | err = bcm2835_devm_add_vchi_ctx(dev); |
289 | if (err) |
290 | return err; |
291 | |
292 | err = snd_add_child_devices(device: dev, numchans: num_channels); |
293 | if (err) |
294 | return err; |
295 | |
296 | return 0; |
297 | } |
298 | |
299 | #ifdef CONFIG_PM |
300 | |
301 | static int snd_bcm2835_alsa_suspend(struct vchiq_device *device, |
302 | pm_message_t state) |
303 | { |
304 | return 0; |
305 | } |
306 | |
307 | static int snd_bcm2835_alsa_resume(struct vchiq_device *device) |
308 | { |
309 | return 0; |
310 | } |
311 | |
312 | #endif |
313 | |
314 | static struct vchiq_device_id device_id_table[] = { |
315 | { .name = "bcm2835-audio" }, |
316 | {} |
317 | }; |
318 | MODULE_DEVICE_TABLE(vchiq, device_id_table); |
319 | |
320 | static struct vchiq_driver bcm2835_alsa_driver = { |
321 | .probe = snd_bcm2835_alsa_probe, |
322 | #ifdef CONFIG_PM |
323 | .suspend = snd_bcm2835_alsa_suspend, |
324 | .resume = snd_bcm2835_alsa_resume, |
325 | #endif |
326 | .id_table = device_id_table, |
327 | .driver = { |
328 | .name = "bcm2835-audio" , |
329 | }, |
330 | }; |
331 | module_vchiq_driver(bcm2835_alsa_driver); |
332 | |
333 | MODULE_AUTHOR("Dom Cobley" ); |
334 | MODULE_DESCRIPTION("Alsa driver for BCM2835 chip" ); |
335 | MODULE_LICENSE("GPL" ); |
336 | |