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 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(vchi_ctx); 43 return ret; 44 } 45 46 devres_add(dev, 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, 0, route, 70 numchannels, false); 71 72 if (err) 73 return err; 74 75 err = snd_bcm2835_new_pcm(chip, "IEC958", 1, route, 1, 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, 0, route, numchannels, 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(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(dev, -1, NULL, THIS_MODULE, sizeof(*chip), &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 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, 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 extrachannels_per_driver = 0; 206 int extrachannels_remainder = 0; 207 int count_devices = 0; 208 int extrachannels = 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(device, audio_driver, 263 numchannels_this_device); 264 if (err) 265 return err; 266 } 267 268 return 0; 269 } 270 271 static int snd_bcm2835_alsa_probe(struct platform_device *pdev) 272 { 273 struct device *dev = &pdev->dev; 274 int err; 275 276 if (num_channels <= 0 || num_channels > MAX_SUBSTREAMS) { 277 num_channels = MAX_SUBSTREAMS; 278 dev_warn(dev, "Illegal num_channels value, will use %u\n", 279 num_channels); 280 } 281 282 err = bcm2835_devm_add_vchi_ctx(dev); 283 if (err) 284 return err; 285 286 err = snd_add_child_devices(dev, num_channels); 287 if (err) 288 return err; 289 290 return 0; 291 } 292 293 #ifdef CONFIG_PM 294 295 static int snd_bcm2835_alsa_suspend(struct platform_device *pdev, 296 pm_message_t state) 297 { 298 return 0; 299 } 300 301 static int snd_bcm2835_alsa_resume(struct platform_device *pdev) 302 { 303 return 0; 304 } 305 306 #endif 307 308 static struct platform_driver bcm2835_alsa_driver = { 309 .probe = snd_bcm2835_alsa_probe, 310 #ifdef CONFIG_PM 311 .suspend = snd_bcm2835_alsa_suspend, 312 .resume = snd_bcm2835_alsa_resume, 313 #endif 314 .driver = { 315 .name = "bcm2835_audio", 316 }, 317 }; 318 module_platform_driver(bcm2835_alsa_driver); 319 320 MODULE_AUTHOR("Dom Cobley"); 321 MODULE_DESCRIPTION("Alsa driver for BCM2835 chip"); 322 MODULE_LICENSE("GPL"); 323 MODULE_ALIAS("platform:bcm2835_audio"); 324