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