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