root/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. bcm2835_devm_free_vchi_ctx
  2. bcm2835_devm_add_vchi_ctx
  3. bcm2835_audio_alsa_newpcm
  4. bcm2835_audio_simple_newpcm
  5. bcm2835_card_free
  6. snd_add_child_device
  7. snd_add_child_devices
  8. snd_bcm2835_alsa_probe
  9. snd_bcm2835_alsa_suspend
  10. snd_bcm2835_alsa_resume

   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;
  14 static bool enable_compat_alsa = true;
  15 static int num_channels = MAX_SUBSTREAMS;
  16 
  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)");
  26 
  27 static 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 
  34 static 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 
  55 typedef int (*bcm2835_audio_newpcm_func)(struct bcm2835_chip *chip,
  56                                          const char *name,
  57                                          enum snd_bcm2835_route route,
  58                                          u32 numchannels);
  59 
  60 typedef int (*bcm2835_audio_newctl_func)(struct bcm2835_chip *chip);
  61 
  62 struct 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 
  72 static 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 
  91 static 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 
  99 static 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 
 111 static 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 
 124 static 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 
 137 struct bcm2835_audio_drivers {
 138         struct bcm2835_audio_driver *audio_driver;
 139         const bool *is_enabled;
 140 };
 141 
 142 static 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 
 157 static void bcm2835_card_free(void *data)
 158 {
 159         snd_card_free(data);
 160 }
 161 
 162 static 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 
 228 static 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 
 296 static 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 
 320 static int snd_bcm2835_alsa_suspend(struct platform_device *pdev,
 321                                     pm_message_t state)
 322 {
 323         return 0;
 324 }
 325 
 326 static int snd_bcm2835_alsa_resume(struct platform_device *pdev)
 327 {
 328         return 0;
 329 }
 330 
 331 #endif
 332 
 333 static 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 };
 343 module_platform_driver(bcm2835_alsa_driver);
 344 
 345 MODULE_AUTHOR("Dom Cobley");
 346 MODULE_DESCRIPTION("Alsa driver for BCM2835 chip");
 347 MODULE_LICENSE("GPL");
 348 MODULE_ALIAS("platform:bcm2835_audio");

/* [<][>][^][v][top][bottom][index][help] */