linux/drivers/media/pci/ivtv/ivtv-alsa-main.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  ALSA interface to ivtv PCM capture streams
   4 *
   5 *  Copyright (C) 2009,2012  Andy Walls <awalls@md.metrocast.net>
   6 *  Copyright (C) 2009  Devin Heitmueller <dheitmueller@kernellabs.com>
   7 *
   8 *  Portions of this work were sponsored by ONELAN Limited for the cx18 driver
   9 */
  10
  11#include "ivtv-driver.h"
  12#include "ivtv-version.h"
  13#include "ivtv-alsa.h"
  14#include "ivtv-alsa-pcm.h"
  15
  16#include <sound/core.h>
  17#include <sound/initval.h>
  18
  19int ivtv_alsa_debug;
  20static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
  21
  22#define IVTV_DEBUG_ALSA_INFO(__fmt, __arg...) \
  23        do { \
  24                if (ivtv_alsa_debug & 2) \
  25                        printk(KERN_INFO pr_fmt("%s: alsa:" __fmt),     \
  26                               __func__, ##__arg);                      \
  27        } while (0)
  28
  29module_param_named(debug, ivtv_alsa_debug, int, 0644);
  30MODULE_PARM_DESC(debug,
  31                 "Debug level (bitmask). Default: 0\n"
  32                 "\t\t\t  1/0x0001: warning\n"
  33                 "\t\t\t  2/0x0002: info\n");
  34
  35module_param_array(index, int, NULL, 0444);
  36MODULE_PARM_DESC(index,
  37                 "Index value for IVTV ALSA capture interface(s).\n");
  38
  39MODULE_AUTHOR("Andy Walls");
  40MODULE_DESCRIPTION("CX23415/CX23416 ALSA Interface");
  41MODULE_SUPPORTED_DEVICE("CX23415/CX23416 MPEG2 encoder");
  42MODULE_LICENSE("GPL");
  43
  44MODULE_VERSION(IVTV_VERSION);
  45
  46static inline
  47struct snd_ivtv_card *to_snd_ivtv_card(struct v4l2_device *v4l2_dev)
  48{
  49        return to_ivtv(v4l2_dev)->alsa;
  50}
  51
  52static inline
  53struct snd_ivtv_card *p_to_snd_ivtv_card(struct v4l2_device **v4l2_dev)
  54{
  55        return container_of(v4l2_dev, struct snd_ivtv_card, v4l2_dev);
  56}
  57
  58static void snd_ivtv_card_free(struct snd_ivtv_card *itvsc)
  59{
  60        if (itvsc == NULL)
  61                return;
  62
  63        if (itvsc->v4l2_dev != NULL)
  64                to_ivtv(itvsc->v4l2_dev)->alsa = NULL;
  65
  66        /* FIXME - take any other stopping actions needed */
  67
  68        kfree(itvsc);
  69}
  70
  71static void snd_ivtv_card_private_free(struct snd_card *sc)
  72{
  73        if (sc == NULL)
  74                return;
  75        snd_ivtv_card_free(sc->private_data);
  76        sc->private_data = NULL;
  77        sc->private_free = NULL;
  78}
  79
  80static int snd_ivtv_card_create(struct v4l2_device *v4l2_dev,
  81                                       struct snd_card *sc,
  82                                       struct snd_ivtv_card **itvsc)
  83{
  84        *itvsc = kzalloc(sizeof(struct snd_ivtv_card), GFP_KERNEL);
  85        if (*itvsc == NULL)
  86                return -ENOMEM;
  87
  88        (*itvsc)->v4l2_dev = v4l2_dev;
  89        (*itvsc)->sc = sc;
  90
  91        sc->private_data = *itvsc;
  92        sc->private_free = snd_ivtv_card_private_free;
  93
  94        return 0;
  95}
  96
  97static int snd_ivtv_card_set_names(struct snd_ivtv_card *itvsc)
  98{
  99        struct ivtv *itv = to_ivtv(itvsc->v4l2_dev);
 100        struct snd_card *sc = itvsc->sc;
 101
 102        /* sc->driver is used by alsa-lib's configurator: simple, unique */
 103        strscpy(sc->driver, "CX2341[56]", sizeof(sc->driver));
 104
 105        /* sc->shortname is a symlink in /proc/asound: IVTV-M -> cardN */
 106        snprintf(sc->shortname,  sizeof(sc->shortname), "IVTV-%d",
 107                 itv->instance);
 108
 109        /* sc->longname is read from /proc/asound/cards */
 110        snprintf(sc->longname, sizeof(sc->longname),
 111                 "CX2341[56] #%d %s TV/FM Radio/Line-In Capture",
 112                 itv->instance, itv->card_name);
 113
 114        return 0;
 115}
 116
 117static int snd_ivtv_init(struct v4l2_device *v4l2_dev)
 118{
 119        struct ivtv *itv = to_ivtv(v4l2_dev);
 120        struct snd_card *sc = NULL;
 121        struct snd_ivtv_card *itvsc;
 122        int ret, idx;
 123
 124        /* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */
 125
 126        /* (1) Check and increment the device index */
 127        /* This is a no-op for us.  We'll use the itv->instance */
 128
 129        /* (2) Create a card instance */
 130        /* use first available id if not specified otherwise*/
 131        idx = index[itv->instance] == -1 ? SNDRV_DEFAULT_IDX1 : index[itv->instance];
 132        ret = snd_card_new(&itv->pdev->dev,
 133                           idx,
 134                           SNDRV_DEFAULT_STR1, /* xid from end of shortname*/
 135                           THIS_MODULE, 0, &sc);
 136        if (ret) {
 137                IVTV_ALSA_ERR("%s: snd_card_new() failed with err %d\n",
 138                              __func__, ret);
 139                goto err_exit;
 140        }
 141
 142        /* (3) Create a main component */
 143        ret = snd_ivtv_card_create(v4l2_dev, sc, &itvsc);
 144        if (ret) {
 145                IVTV_ALSA_ERR("%s: snd_ivtv_card_create() failed with err %d\n",
 146                              __func__, ret);
 147                goto err_exit_free;
 148        }
 149
 150        /* (4) Set the driver ID and name strings */
 151        snd_ivtv_card_set_names(itvsc);
 152
 153        /* (5) Create other components: PCM, & proc files */
 154        ret = snd_ivtv_pcm_create(itvsc);
 155        if (ret) {
 156                IVTV_ALSA_ERR("%s: snd_ivtv_pcm_create() failed with err %d\n",
 157                              __func__, ret);
 158                goto err_exit_free;
 159        }
 160        /* FIXME - proc files */
 161
 162        /* (7) Set the driver data and return 0 */
 163        /* We do this out of normal order for PCI drivers to avoid races */
 164        itv->alsa = itvsc;
 165
 166        /* (6) Register the card instance */
 167        ret = snd_card_register(sc);
 168        if (ret) {
 169                itv->alsa = NULL;
 170                IVTV_ALSA_ERR("%s: snd_card_register() failed with err %d\n",
 171                              __func__, ret);
 172                goto err_exit_free;
 173        }
 174
 175        IVTV_ALSA_INFO("%s: Instance %d registered as ALSA card %d\n",
 176                         __func__, itv->instance, sc->number);
 177
 178        return 0;
 179
 180err_exit_free:
 181        if (sc != NULL)
 182                snd_card_free(sc);
 183        kfree(itvsc);
 184err_exit:
 185        return ret;
 186}
 187
 188static int ivtv_alsa_load(struct ivtv *itv)
 189{
 190        struct v4l2_device *v4l2_dev = &itv->v4l2_dev;
 191        struct ivtv_stream *s;
 192
 193        if (v4l2_dev == NULL) {
 194                pr_err("ivtv-alsa: %s: struct v4l2_device * is NULL\n",
 195                       __func__);
 196                return 0;
 197        }
 198
 199        itv = to_ivtv(v4l2_dev);
 200        if (itv == NULL) {
 201                pr_err("ivtv-alsa itv is NULL\n");
 202                return 0;
 203        }
 204
 205        s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM];
 206        if (s->vdev.v4l2_dev == NULL) {
 207                IVTV_DEBUG_ALSA_INFO("PCM stream for card is disabled - skipping\n");
 208                return 0;
 209        }
 210
 211        if (itv->alsa != NULL) {
 212                IVTV_ALSA_ERR("%s: struct snd_ivtv_card * already exists\n",
 213                              __func__);
 214                return 0;
 215        }
 216
 217        if (snd_ivtv_init(v4l2_dev)) {
 218                IVTV_ALSA_ERR("%s: failed to create struct snd_ivtv_card\n",
 219                              __func__);
 220        } else {
 221                IVTV_DEBUG_ALSA_INFO("created ivtv ALSA interface instance\n");
 222        }
 223        return 0;
 224}
 225
 226static int __init ivtv_alsa_init(void)
 227{
 228        pr_info("ivtv-alsa: module loading...\n");
 229        ivtv_ext_init = &ivtv_alsa_load;
 230        return 0;
 231}
 232
 233static void __exit snd_ivtv_exit(struct snd_ivtv_card *itvsc)
 234{
 235        struct ivtv *itv = to_ivtv(itvsc->v4l2_dev);
 236
 237        /* FIXME - pointer checks & shutdown itvsc */
 238
 239        snd_card_free(itvsc->sc);
 240        itv->alsa = NULL;
 241}
 242
 243static int __exit ivtv_alsa_exit_callback(struct device *dev, void *data)
 244{
 245        struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
 246        struct snd_ivtv_card *itvsc;
 247
 248        if (v4l2_dev == NULL) {
 249                pr_err("ivtv-alsa: %s: struct v4l2_device * is NULL\n",
 250                       __func__);
 251                return 0;
 252        }
 253
 254        itvsc = to_snd_ivtv_card(v4l2_dev);
 255        if (itvsc == NULL) {
 256                IVTV_ALSA_WARN("%s: struct snd_ivtv_card * is NULL\n",
 257                               __func__);
 258                return 0;
 259        }
 260
 261        snd_ivtv_exit(itvsc);
 262        return 0;
 263}
 264
 265static void __exit ivtv_alsa_exit(void)
 266{
 267        struct device_driver *drv;
 268        int ret;
 269
 270        pr_info("ivtv-alsa: module unloading...\n");
 271
 272        drv = driver_find("ivtv", &pci_bus_type);
 273        ret = driver_for_each_device(drv, NULL, NULL, ivtv_alsa_exit_callback);
 274        (void)ret;      /* suppress compiler warning */
 275
 276        ivtv_ext_init = NULL;
 277        pr_info("ivtv-alsa: module unload complete\n");
 278}
 279
 280module_init(ivtv_alsa_init);
 281module_exit(ivtv_alsa_exit);
 282