Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : /* Copyright 2011 Broadcom Corporation. All rights reserved. */
3 : :
4 : : #include <linux/platform_device.h>
5 : :
6 : : #include <linux/init.h>
7 : : #include <linux/slab.h>
8 : : #include <linux/module.h>
9 : : #include <linux/of.h>
10 : :
11 : : #include "bcm2835.h"
12 : : #include <soc/bcm2835/raspberrypi-firmware.h>
13 : :
14 : : static bool enable_hdmi, enable_hdmi0, enable_hdmi1;
15 : : static bool enable_headphones;
16 : : static bool enable_compat_alsa = true;
17 : :
18 : : module_param(enable_hdmi, bool, 0444);
19 : : MODULE_PARM_DESC(enable_hdmi, "Enables HDMI virtual audio device");
20 : : module_param(enable_headphones, bool, 0444);
21 : : MODULE_PARM_DESC(enable_headphones, "Enables Headphones virtual audio device");
22 : : module_param(enable_compat_alsa, bool, 0444);
23 : : MODULE_PARM_DESC(enable_compat_alsa,
24 : : "Enables ALSA compatibility virtual audio device");
25 : :
26 : 0 : static void bcm2835_devm_free_vchi_ctx(struct device *dev, void *res)
27 : : {
28 : : struct bcm2835_vchi_ctx *vchi_ctx = res;
29 : :
30 : 0 : bcm2835_free_vchi_ctx(vchi_ctx);
31 : 0 : }
32 : :
33 : 3 : static int bcm2835_devm_add_vchi_ctx(struct device *dev)
34 : : {
35 : : struct bcm2835_vchi_ctx *vchi_ctx;
36 : : int ret;
37 : :
38 : : vchi_ctx = devres_alloc(bcm2835_devm_free_vchi_ctx, sizeof(*vchi_ctx),
39 : : GFP_KERNEL);
40 : 3 : if (!vchi_ctx)
41 : : return -ENOMEM;
42 : :
43 : 3 : ret = bcm2835_new_vchi_ctx(dev, vchi_ctx);
44 : 3 : if (ret) {
45 : 0 : devres_free(vchi_ctx);
46 : 0 : return ret;
47 : : }
48 : :
49 : 3 : devres_add(dev, vchi_ctx);
50 : :
51 : 3 : return 0;
52 : : }
53 : :
54 : : typedef int (*bcm2835_audio_newpcm_func)(struct bcm2835_chip *chip,
55 : : const char *name,
56 : : enum snd_bcm2835_route route,
57 : : u32 numchannels);
58 : :
59 : : typedef int (*bcm2835_audio_newctl_func)(struct bcm2835_chip *chip);
60 : :
61 : : struct bcm2835_audio_driver {
62 : : struct device_driver driver;
63 : : const char *shortname;
64 : : const char *longname;
65 : : int minchannels;
66 : : bcm2835_audio_newpcm_func newpcm;
67 : : bcm2835_audio_newctl_func newctl;
68 : : enum snd_bcm2835_route route;
69 : : };
70 : :
71 : 0 : static int bcm2835_audio_alsa_newpcm(struct bcm2835_chip *chip,
72 : : const char *name,
73 : : enum snd_bcm2835_route route,
74 : : u32 numchannels)
75 : : {
76 : : int err;
77 : :
78 : 0 : err = snd_bcm2835_new_pcm(chip, "bcm2835 ALSA", 0, AUDIO_DEST_AUTO,
79 : : numchannels - 1, false);
80 : 0 : if (err)
81 : : return err;
82 : :
83 : 0 : err = snd_bcm2835_new_pcm(chip, "bcm2835 IEC958/HDMI", 1, AUDIO_DEST_HDMI0, 1, true);
84 : 0 : if (err)
85 : : return err;
86 : :
87 : 0 : err = snd_bcm2835_new_pcm(chip, "bcm2835 IEC958/HDMI1", 2, AUDIO_DEST_HDMI1, 1, true);
88 : 0 : if (err)
89 : 0 : return err;
90 : :
91 : : return 0;
92 : : }
93 : :
94 : 3 : static int bcm2835_audio_simple_newpcm(struct bcm2835_chip *chip,
95 : : const char *name,
96 : : enum snd_bcm2835_route route,
97 : : u32 numchannels)
98 : : {
99 : 3 : return snd_bcm2835_new_pcm(chip, name, 0, route, numchannels, false);
100 : : }
101 : :
102 : : static struct bcm2835_audio_driver bcm2835_audio_alsa = {
103 : : .driver = {
104 : : .name = "bcm2835_alsa",
105 : : .owner = THIS_MODULE,
106 : : },
107 : : .shortname = "bcm2835 ALSA",
108 : : .longname = "bcm2835 ALSA",
109 : : .minchannels = 2,
110 : : .newpcm = bcm2835_audio_alsa_newpcm,
111 : : .newctl = snd_bcm2835_new_ctl,
112 : : };
113 : :
114 : : static struct bcm2835_audio_driver bcm2835_audio_hdmi0 = {
115 : : .driver = {
116 : : .name = "bcm2835_hdmi",
117 : : .owner = THIS_MODULE,
118 : : },
119 : : .shortname = "bcm2835 HDMI 1",
120 : : .longname = "bcm2835 HDMI 1",
121 : : .minchannels = 1,
122 : : .newpcm = bcm2835_audio_simple_newpcm,
123 : : .newctl = snd_bcm2835_new_hdmi_ctl,
124 : : .route = AUDIO_DEST_HDMI0
125 : : };
126 : :
127 : : static struct bcm2835_audio_driver bcm2835_audio_hdmi1 = {
128 : : .driver = {
129 : : .name = "bcm2835_hdmi",
130 : : .owner = THIS_MODULE,
131 : : },
132 : : .shortname = "bcm2835 HDMI 2",
133 : : .longname = "bcm2835 HDMI 2",
134 : : .minchannels = 1,
135 : : .newpcm = bcm2835_audio_simple_newpcm,
136 : : .newctl = snd_bcm2835_new_hdmi_ctl,
137 : : .route = AUDIO_DEST_HDMI1
138 : : };
139 : :
140 : : static struct bcm2835_audio_driver bcm2835_audio_headphones = {
141 : : .driver = {
142 : : .name = "bcm2835_headphones",
143 : : .owner = THIS_MODULE,
144 : : },
145 : : .shortname = "bcm2835 Headphones",
146 : : .longname = "bcm2835 Headphones",
147 : : .minchannels = 1,
148 : : .newpcm = bcm2835_audio_simple_newpcm,
149 : : .newctl = snd_bcm2835_new_headphones_ctl,
150 : : .route = AUDIO_DEST_HEADPHONES
151 : : };
152 : :
153 : : struct bcm2835_audio_drivers {
154 : : struct bcm2835_audio_driver *audio_driver;
155 : : const bool *is_enabled;
156 : : };
157 : :
158 : : static struct bcm2835_audio_drivers children_devices[] = {
159 : : {
160 : : .audio_driver = &bcm2835_audio_alsa,
161 : : .is_enabled = &enable_compat_alsa,
162 : : },
163 : : {
164 : : .audio_driver = &bcm2835_audio_hdmi0,
165 : : .is_enabled = &enable_hdmi0,
166 : : },
167 : : {
168 : : .audio_driver = &bcm2835_audio_hdmi1,
169 : : .is_enabled = &enable_hdmi1,
170 : : },
171 : : {
172 : : .audio_driver = &bcm2835_audio_headphones,
173 : : .is_enabled = &enable_headphones,
174 : : },
175 : : };
176 : :
177 : 0 : static void bcm2835_card_free(void *data)
178 : : {
179 : 0 : snd_card_free(data);
180 : 0 : }
181 : :
182 : 3 : static int snd_add_child_device(struct device *dev,
183 : : struct bcm2835_audio_driver *audio_driver,
184 : : u32 numchans)
185 : : {
186 : : struct bcm2835_chip *chip;
187 : : struct snd_card *card;
188 : : int err;
189 : :
190 : 3 : err = snd_card_new(dev, -1, NULL, THIS_MODULE, sizeof(*chip), &card);
191 : 3 : if (err < 0) {
192 : 0 : dev_err(dev, "Failed to create card");
193 : 0 : return err;
194 : : }
195 : :
196 : 3 : chip = card->private_data;
197 : 3 : chip->card = card;
198 : 3 : chip->dev = dev;
199 : 3 : mutex_init(&chip->audio_mutex);
200 : :
201 : 3 : chip->vchi_ctx = devres_find(dev,
202 : : bcm2835_devm_free_vchi_ctx, NULL, NULL);
203 : 3 : if (!chip->vchi_ctx) {
204 : : err = -ENODEV;
205 : : goto error;
206 : : }
207 : :
208 : 3 : strcpy(card->driver, audio_driver->driver.name);
209 : 3 : strcpy(card->shortname, audio_driver->shortname);
210 : 3 : strcpy(card->longname, audio_driver->longname);
211 : :
212 : 3 : err = audio_driver->newpcm(chip, audio_driver->shortname,
213 : : audio_driver->route,
214 : : numchans);
215 : 3 : if (err) {
216 : 0 : dev_err(dev, "Failed to create pcm, error %d\n", err);
217 : 0 : goto error;
218 : : }
219 : :
220 : 3 : err = audio_driver->newctl(chip);
221 : 3 : if (err) {
222 : 0 : dev_err(dev, "Failed to create controls, error %d\n", err);
223 : 0 : goto error;
224 : : }
225 : :
226 : 3 : err = snd_card_register(card);
227 : 3 : if (err) {
228 : 0 : dev_err(dev, "Failed to register card, error %d\n", err);
229 : 0 : goto error;
230 : : }
231 : :
232 : : dev_set_drvdata(dev, chip);
233 : :
234 : 3 : err = devm_add_action(dev, bcm2835_card_free, card);
235 : 3 : if (err < 0) {
236 : 0 : dev_err(dev, "Failed to add devm action, err %d\n", err);
237 : 0 : goto error;
238 : : }
239 : :
240 : 3 : dev_info(dev, "card created with %d channels\n", numchans);
241 : 3 : return 0;
242 : :
243 : : error:
244 : 0 : snd_card_free(card);
245 : 0 : return err;
246 : : }
247 : :
248 : 3 : static int snd_add_child_devices(struct device *device, u32 numchans)
249 : : {
250 : : int extrachannels_per_driver = 0;
251 : : int extrachannels_remainder = 0;
252 : : int count_devices = 0;
253 : : int extrachannels = 0;
254 : : int minchannels = 0;
255 : : int i;
256 : :
257 : 3 : for (i = 0; i < ARRAY_SIZE(children_devices); i++)
258 : 3 : if (*children_devices[i].is_enabled)
259 : 3 : count_devices++;
260 : :
261 : 3 : if (!count_devices)
262 : : return 0;
263 : :
264 : 3 : for (i = 0; i < ARRAY_SIZE(children_devices); i++)
265 : 3 : if (*children_devices[i].is_enabled)
266 : 3 : minchannels +=
267 : 3 : children_devices[i].audio_driver->minchannels;
268 : :
269 : 3 : if (minchannels < numchans) {
270 : 3 : extrachannels = numchans - minchannels;
271 : 3 : extrachannels_per_driver = extrachannels / count_devices;
272 : 3 : extrachannels_remainder = extrachannels % count_devices;
273 : : }
274 : :
275 : : dev_dbg(device, "minchannels %d\n", minchannels);
276 : : dev_dbg(device, "extrachannels %d\n", extrachannels);
277 : : dev_dbg(device, "extrachannels_per_driver %d\n",
278 : : extrachannels_per_driver);
279 : : dev_dbg(device, "extrachannels_remainder %d\n",
280 : : extrachannels_remainder);
281 : :
282 : 3 : for (i = 0; i < ARRAY_SIZE(children_devices); i++) {
283 : : struct bcm2835_audio_driver *audio_driver;
284 : : int numchannels_this_device;
285 : : int err;
286 : :
287 : 3 : if (!*children_devices[i].is_enabled)
288 : 3 : continue;
289 : :
290 : 3 : audio_driver = children_devices[i].audio_driver;
291 : :
292 : 3 : if (audio_driver->minchannels > numchans) {
293 : 0 : dev_err(device,
294 : : "Out of channels, needed %d but only %d left\n",
295 : : audio_driver->minchannels,
296 : : numchans);
297 : 0 : continue;
298 : : }
299 : :
300 : 3 : numchannels_this_device =
301 : 3 : audio_driver->minchannels + extrachannels_per_driver +
302 : : extrachannels_remainder;
303 : : extrachannels_remainder = 0;
304 : :
305 : 3 : numchans -= numchannels_this_device;
306 : :
307 : 3 : err = snd_add_child_device(device, audio_driver,
308 : : numchannels_this_device);
309 : 3 : if (err)
310 : 0 : return err;
311 : : }
312 : :
313 : : return 0;
314 : : }
315 : :
316 : 3 : static void set_hdmi_enables(struct device *dev)
317 : : {
318 : : struct device_node *firmware_node;
319 : : struct rpi_firmware *firmware;
320 : : u32 num_displays, i, display_id;
321 : : int ret;
322 : :
323 : 3 : firmware_node = of_parse_phandle(dev->of_node, "brcm,firmware", 0);
324 : 3 : firmware = rpi_firmware_get(firmware_node);
325 : :
326 : 3 : if (!firmware)
327 : 0 : return;
328 : :
329 : 3 : of_node_put(firmware_node);
330 : :
331 : 3 : ret = rpi_firmware_property(firmware,
332 : : RPI_FIRMWARE_FRAMEBUFFER_GET_NUM_DISPLAYS,
333 : : &num_displays, sizeof(u32));
334 : :
335 : 3 : if (ret)
336 : : return;
337 : :
338 : 3 : for (i = 0; i < num_displays; i++) {
339 : 3 : display_id = i;
340 : 3 : ret = rpi_firmware_property(firmware,
341 : : RPI_FIRMWARE_FRAMEBUFFER_GET_DISPLAY_ID,
342 : : &display_id, sizeof(display_id));
343 : 3 : if (!ret) {
344 : 3 : if (display_id == 2)
345 : 3 : enable_hdmi0 = true;
346 : 3 : if (display_id == 7)
347 : 0 : enable_hdmi1 = true;
348 : : }
349 : : }
350 : :
351 : 3 : if (!enable_hdmi0 && enable_hdmi1) {
352 : : /* Swap them over and reassign route. This means
353 : : * that if we only have one connected, it is always named
354 : : * HDMI1, irrespective of if its on port HDMI0 or HDMI1.
355 : : * This should match with the naming of HDMI ports in DRM
356 : : */
357 : 0 : enable_hdmi0 = true;
358 : 0 : enable_hdmi1 = false;
359 : 0 : bcm2835_audio_hdmi0.route = AUDIO_DEST_HDMI1;
360 : : }
361 : : }
362 : :
363 : 3 : static int snd_bcm2835_alsa_probe(struct platform_device *pdev)
364 : : {
365 : 3 : struct device *dev = &pdev->dev;
366 : : u32 numchans;
367 : : int err;
368 : :
369 : 3 : err = of_property_read_u32(dev->of_node, "brcm,pwm-channels",
370 : : &numchans);
371 : 3 : if (err) {
372 : 0 : dev_err(dev, "Failed to get DT property 'brcm,pwm-channels'");
373 : 0 : return err;
374 : : }
375 : :
376 : 3 : if (numchans == 0 || numchans > MAX_SUBSTREAMS) {
377 : 0 : numchans = MAX_SUBSTREAMS;
378 : 0 : dev_warn(dev,
379 : : "Illegal 'brcm,pwm-channels' value, will use %u\n",
380 : : numchans);
381 : : }
382 : :
383 : 3 : if (!enable_compat_alsa) {
384 : 3 : if (!of_property_read_bool(dev->of_node, "brcm,disable-hdmi"))
385 : 3 : set_hdmi_enables(dev);
386 : :
387 : : // In this mode, always enable analog output
388 : 3 : enable_headphones = true;
389 : : } else {
390 : 0 : enable_hdmi0 = enable_hdmi;
391 : : }
392 : :
393 : 3 : err = bcm2835_devm_add_vchi_ctx(dev);
394 : 3 : if (err)
395 : : return err;
396 : :
397 : 3 : err = snd_add_child_devices(dev, numchans);
398 : 3 : if (err)
399 : 0 : return err;
400 : :
401 : : return 0;
402 : : }
403 : :
404 : : #ifdef CONFIG_PM
405 : :
406 : 0 : static int snd_bcm2835_alsa_suspend(struct platform_device *pdev,
407 : : pm_message_t state)
408 : : {
409 : 0 : return 0;
410 : : }
411 : :
412 : 0 : static int snd_bcm2835_alsa_resume(struct platform_device *pdev)
413 : : {
414 : 0 : return 0;
415 : : }
416 : :
417 : : #endif
418 : :
419 : : static const struct of_device_id snd_bcm2835_of_match_table[] = {
420 : : { .compatible = "brcm,bcm2835-audio",},
421 : : {},
422 : : };
423 : : MODULE_DEVICE_TABLE(of, snd_bcm2835_of_match_table);
424 : :
425 : : static struct platform_driver bcm2835_alsa_driver = {
426 : : .probe = snd_bcm2835_alsa_probe,
427 : : #ifdef CONFIG_PM
428 : : .suspend = snd_bcm2835_alsa_suspend,
429 : : .resume = snd_bcm2835_alsa_resume,
430 : : #endif
431 : : .driver = {
432 : : .name = "bcm2835_audio",
433 : : .of_match_table = snd_bcm2835_of_match_table,
434 : : },
435 : : };
436 : 3 : module_platform_driver(bcm2835_alsa_driver);
437 : :
438 : : MODULE_AUTHOR("Dom Cobley");
439 : : MODULE_DESCRIPTION("Alsa driver for BCM2835 chip");
440 : : MODULE_LICENSE("GPL");
441 : : MODULE_ALIAS("platform:bcm2835_audio");
|