1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright 2011 Broadcom Corporation. All rights reserved. */
4 #include <linux/platform_device.h>
6 #include <linux/init.h>
7 #include <linux/slab.h>
8 #include <linux/module.h>
12 static bool enable_hdmi;
13 static bool enable_headphones;
14 static bool enable_compat_alsa = true;
15 static int num_channels = MAX_SUBSTREAMS;
17 module_param(enable_hdmi, bool, 0444);
18 MODULE_PARM_DESC(enable_hdmi, "Enables HDMI virtual audio device");
19 module_param(enable_headphones, bool, 0444);
20 MODULE_PARM_DESC(enable_headphones, "Enables Headphones virtual audio device");
21 module_param(enable_compat_alsa, bool, 0444);
22 MODULE_PARM_DESC(enable_compat_alsa,
23 "Enables ALSA compatibility virtual audio device");
24 module_param(num_channels, int, 0644);
25 MODULE_PARM_DESC(num_channels, "Number of audio channels (default: 8)");
27 static void bcm2835_devm_free_vchi_ctx(struct device *dev, void *res)
29 struct bcm2835_vchi_ctx *vchi_ctx = res;
31 bcm2835_free_vchi_ctx(vchi_ctx);
34 static int bcm2835_devm_add_vchi_ctx(struct device *dev)
36 struct bcm2835_vchi_ctx *vchi_ctx;
39 vchi_ctx = devres_alloc(bcm2835_devm_free_vchi_ctx, sizeof(*vchi_ctx),
44 ret = bcm2835_new_vchi_ctx(dev, vchi_ctx);
46 devres_free(vchi_ctx);
50 devres_add(dev, vchi_ctx);
55 typedef int (*bcm2835_audio_newpcm_func)(struct bcm2835_chip *chip,
57 enum snd_bcm2835_route route,
60 typedef int (*bcm2835_audio_newctl_func)(struct bcm2835_chip *chip);
62 struct bcm2835_audio_driver {
63 struct device_driver driver;
64 const char *shortname;
67 bcm2835_audio_newpcm_func newpcm;
68 bcm2835_audio_newctl_func newctl;
69 enum snd_bcm2835_route route;
72 static int bcm2835_audio_alsa_newpcm(struct bcm2835_chip *chip,
74 enum snd_bcm2835_route route,
79 err = snd_bcm2835_new_pcm(chip, "bcm2835 ALSA", 0, AUDIO_DEST_AUTO,
80 numchannels - 1, false);
84 err = snd_bcm2835_new_pcm(chip, "bcm2835 IEC958/HDMI", 1, 0, 1, true);
91 static int bcm2835_audio_simple_newpcm(struct bcm2835_chip *chip,
93 enum snd_bcm2835_route route,
96 return snd_bcm2835_new_pcm(chip, name, 0, route, numchannels, false);
99 static struct bcm2835_audio_driver bcm2835_audio_alsa = {
101 .name = "bcm2835_alsa",
102 .owner = THIS_MODULE,
104 .shortname = "bcm2835 ALSA",
105 .longname = "bcm2835 ALSA",
107 .newpcm = bcm2835_audio_alsa_newpcm,
108 .newctl = snd_bcm2835_new_ctl,
111 static struct bcm2835_audio_driver bcm2835_audio_hdmi = {
113 .name = "bcm2835_hdmi",
114 .owner = THIS_MODULE,
116 .shortname = "bcm2835 HDMI",
117 .longname = "bcm2835 HDMI",
119 .newpcm = bcm2835_audio_simple_newpcm,
120 .newctl = snd_bcm2835_new_hdmi_ctl,
121 .route = AUDIO_DEST_HDMI
124 static struct bcm2835_audio_driver bcm2835_audio_headphones = {
126 .name = "bcm2835_headphones",
127 .owner = THIS_MODULE,
129 .shortname = "bcm2835 Headphones",
130 .longname = "bcm2835 Headphones",
132 .newpcm = bcm2835_audio_simple_newpcm,
133 .newctl = snd_bcm2835_new_headphones_ctl,
134 .route = AUDIO_DEST_HEADPHONES
137 struct bcm2835_audio_drivers {
138 struct bcm2835_audio_driver *audio_driver;
139 const bool *is_enabled;
142 static struct bcm2835_audio_drivers children_devices[] = {
144 .audio_driver = &bcm2835_audio_alsa,
145 .is_enabled = &enable_compat_alsa,
148 .audio_driver = &bcm2835_audio_hdmi,
149 .is_enabled = &enable_hdmi,
152 .audio_driver = &bcm2835_audio_headphones,
153 .is_enabled = &enable_headphones,
157 static void bcm2835_card_free(void *data)
162 static int snd_add_child_device(struct device *dev,
163 struct bcm2835_audio_driver *audio_driver,
166 struct bcm2835_chip *chip;
167 struct snd_card *card;
170 err = snd_card_new(dev, -1, NULL, THIS_MODULE, sizeof(*chip), &card);
172 dev_err(dev, "Failed to create card");
176 chip = card->private_data;
179 mutex_init(&chip->audio_mutex);
181 chip->vchi_ctx = devres_find(dev,
182 bcm2835_devm_free_vchi_ctx, NULL, NULL);
183 if (!chip->vchi_ctx) {
188 strscpy(card->driver, audio_driver->driver.name, sizeof(card->driver));
189 strscpy(card->shortname, audio_driver->shortname, sizeof(card->shortname));
190 strscpy(card->longname, audio_driver->longname, sizeof(card->longname));
192 err = audio_driver->newpcm(chip, audio_driver->shortname,
196 dev_err(dev, "Failed to create pcm, error %d\n", err);
200 err = audio_driver->newctl(chip);
202 dev_err(dev, "Failed to create controls, error %d\n", err);
206 err = snd_card_register(card);
208 dev_err(dev, "Failed to register card, error %d\n", err);
212 dev_set_drvdata(dev, chip);
214 err = devm_add_action(dev, bcm2835_card_free, card);
216 dev_err(dev, "Failed to add devm action, err %d\n", err);
220 dev_info(dev, "card created with %d channels\n", numchans);
228 static int snd_add_child_devices(struct device *device, u32 numchans)
230 int extrachannels_per_driver = 0;
231 int extrachannels_remainder = 0;
232 int count_devices = 0;
233 int extrachannels = 0;
237 for (i = 0; i < ARRAY_SIZE(children_devices); i++)
238 if (*children_devices[i].is_enabled)
244 for (i = 0; i < ARRAY_SIZE(children_devices); i++)
245 if (*children_devices[i].is_enabled)
247 children_devices[i].audio_driver->minchannels;
249 if (minchannels < numchans) {
250 extrachannels = numchans - minchannels;
251 extrachannels_per_driver = extrachannels / count_devices;
252 extrachannels_remainder = extrachannels % count_devices;
255 dev_dbg(device, "minchannels %d\n", minchannels);
256 dev_dbg(device, "extrachannels %d\n", extrachannels);
257 dev_dbg(device, "extrachannels_per_driver %d\n",
258 extrachannels_per_driver);
259 dev_dbg(device, "extrachannels_remainder %d\n",
260 extrachannels_remainder);
262 for (i = 0; i < ARRAY_SIZE(children_devices); i++) {
263 struct bcm2835_audio_driver *audio_driver;
264 int numchannels_this_device;
267 if (!*children_devices[i].is_enabled)
270 audio_driver = children_devices[i].audio_driver;
272 if (audio_driver->minchannels > numchans) {
274 "Out of channels, needed %d but only %d left\n",
275 audio_driver->minchannels,
280 numchannels_this_device =
281 audio_driver->minchannels + extrachannels_per_driver +
282 extrachannels_remainder;
283 extrachannels_remainder = 0;
285 numchans -= numchannels_this_device;
287 err = snd_add_child_device(device, audio_driver,
288 numchannels_this_device);
296 static int snd_bcm2835_alsa_probe(struct platform_device *pdev)
298 struct device *dev = &pdev->dev;
301 if (num_channels <= 0 || num_channels > MAX_SUBSTREAMS) {
302 num_channels = MAX_SUBSTREAMS;
303 dev_warn(dev, "Illegal num_channels value, will use %u\n",
307 err = bcm2835_devm_add_vchi_ctx(dev);
311 err = snd_add_child_devices(dev, num_channels);
320 static int snd_bcm2835_alsa_suspend(struct platform_device *pdev,
326 static int snd_bcm2835_alsa_resume(struct platform_device *pdev)
333 static struct platform_driver bcm2835_alsa_driver = {
334 .probe = snd_bcm2835_alsa_probe,
336 .suspend = snd_bcm2835_alsa_suspend,
337 .resume = snd_bcm2835_alsa_resume,
340 .name = "bcm2835_audio",
343 module_platform_driver(bcm2835_alsa_driver);
345 MODULE_AUTHOR("Dom Cobley");
346 MODULE_DESCRIPTION("Alsa driver for BCM2835 chip");
347 MODULE_LICENSE("GPL");
348 MODULE_ALIAS("platform:bcm2835_audio");