linux/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c
<<
>>
Prefs
   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
  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