Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  ALSA interface to ivtv PCM capture streams
0004  *
0005  *  Copyright (C) 2009,2012  Andy Walls <awalls@md.metrocast.net>
0006  *  Copyright (C) 2009  Devin Heitmueller <dheitmueller@kernellabs.com>
0007  *
0008  *  Portions of this work were sponsored by ONELAN Limited for the cx18 driver
0009  */
0010 
0011 #include "ivtv-driver.h"
0012 #include "ivtv-version.h"
0013 #include "ivtv-alsa.h"
0014 #include "ivtv-alsa-pcm.h"
0015 
0016 #include <sound/core.h>
0017 #include <sound/initval.h>
0018 
0019 int ivtv_alsa_debug;
0020 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
0021 
0022 #define IVTV_DEBUG_ALSA_INFO(__fmt, __arg...) \
0023     do { \
0024         if (ivtv_alsa_debug & 2) \
0025             printk(KERN_INFO pr_fmt("%s: alsa:" __fmt), \
0026                    __func__, ##__arg);          \
0027     } while (0)
0028 
0029 module_param_named(debug, ivtv_alsa_debug, int, 0644);
0030 MODULE_PARM_DESC(debug,
0031          "Debug level (bitmask). Default: 0\n"
0032          "\t\t\t  1/0x0001: warning\n"
0033          "\t\t\t  2/0x0002: info\n");
0034 
0035 module_param_array(index, int, NULL, 0444);
0036 MODULE_PARM_DESC(index,
0037          "Index value for IVTV ALSA capture interface(s).\n");
0038 
0039 MODULE_AUTHOR("Andy Walls");
0040 MODULE_DESCRIPTION("CX23415/CX23416 ALSA Interface");
0041 MODULE_LICENSE("GPL");
0042 
0043 MODULE_VERSION(IVTV_VERSION);
0044 
0045 static inline
0046 struct snd_ivtv_card *to_snd_ivtv_card(struct v4l2_device *v4l2_dev)
0047 {
0048     return to_ivtv(v4l2_dev)->alsa;
0049 }
0050 
0051 static void snd_ivtv_card_free(struct snd_ivtv_card *itvsc)
0052 {
0053     if (itvsc == NULL)
0054         return;
0055 
0056     if (itvsc->v4l2_dev != NULL)
0057         to_ivtv(itvsc->v4l2_dev)->alsa = NULL;
0058 
0059     /* FIXME - take any other stopping actions needed */
0060 
0061     kfree(itvsc);
0062 }
0063 
0064 static void snd_ivtv_card_private_free(struct snd_card *sc)
0065 {
0066     if (sc == NULL)
0067         return;
0068     snd_ivtv_card_free(sc->private_data);
0069     sc->private_data = NULL;
0070     sc->private_free = NULL;
0071 }
0072 
0073 static int snd_ivtv_card_create(struct v4l2_device *v4l2_dev,
0074                        struct snd_card *sc,
0075                        struct snd_ivtv_card **itvsc)
0076 {
0077     *itvsc = kzalloc(sizeof(struct snd_ivtv_card), GFP_KERNEL);
0078     if (*itvsc == NULL)
0079         return -ENOMEM;
0080 
0081     (*itvsc)->v4l2_dev = v4l2_dev;
0082     (*itvsc)->sc = sc;
0083 
0084     sc->private_data = *itvsc;
0085     sc->private_free = snd_ivtv_card_private_free;
0086 
0087     return 0;
0088 }
0089 
0090 static int snd_ivtv_card_set_names(struct snd_ivtv_card *itvsc)
0091 {
0092     struct ivtv *itv = to_ivtv(itvsc->v4l2_dev);
0093     struct snd_card *sc = itvsc->sc;
0094 
0095     /* sc->driver is used by alsa-lib's configurator: simple, unique */
0096     strscpy(sc->driver, "CX2341[56]", sizeof(sc->driver));
0097 
0098     /* sc->shortname is a symlink in /proc/asound: IVTV-M -> cardN */
0099     snprintf(sc->shortname,  sizeof(sc->shortname), "IVTV-%d",
0100          itv->instance);
0101 
0102     /* sc->longname is read from /proc/asound/cards */
0103     snprintf(sc->longname, sizeof(sc->longname),
0104          "CX2341[56] #%d %s TV/FM Radio/Line-In Capture",
0105          itv->instance, itv->card_name);
0106 
0107     return 0;
0108 }
0109 
0110 static int snd_ivtv_init(struct v4l2_device *v4l2_dev)
0111 {
0112     struct ivtv *itv = to_ivtv(v4l2_dev);
0113     struct snd_card *sc = NULL;
0114     struct snd_ivtv_card *itvsc;
0115     int ret, idx;
0116 
0117     /* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */
0118 
0119     /* (1) Check and increment the device index */
0120     /* This is a no-op for us.  We'll use the itv->instance */
0121 
0122     /* (2) Create a card instance */
0123     /* use first available id if not specified otherwise*/
0124     idx = index[itv->instance] == -1 ? SNDRV_DEFAULT_IDX1 : index[itv->instance];
0125     ret = snd_card_new(&itv->pdev->dev,
0126                idx,
0127                SNDRV_DEFAULT_STR1, /* xid from end of shortname*/
0128                THIS_MODULE, 0, &sc);
0129     if (ret) {
0130         IVTV_ALSA_ERR("%s: snd_card_new() failed with err %d\n",
0131                   __func__, ret);
0132         goto err_exit;
0133     }
0134 
0135     /* (3) Create a main component */
0136     ret = snd_ivtv_card_create(v4l2_dev, sc, &itvsc);
0137     if (ret) {
0138         IVTV_ALSA_ERR("%s: snd_ivtv_card_create() failed with err %d\n",
0139                   __func__, ret);
0140         goto err_exit_free;
0141     }
0142 
0143     /* (4) Set the driver ID and name strings */
0144     snd_ivtv_card_set_names(itvsc);
0145 
0146     /* (5) Create other components: PCM, & proc files */
0147     ret = snd_ivtv_pcm_create(itvsc);
0148     if (ret) {
0149         IVTV_ALSA_ERR("%s: snd_ivtv_pcm_create() failed with err %d\n",
0150                   __func__, ret);
0151         goto err_exit_free;
0152     }
0153     /* FIXME - proc files */
0154 
0155     /* (7) Set the driver data and return 0 */
0156     /* We do this out of normal order for PCI drivers to avoid races */
0157     itv->alsa = itvsc;
0158 
0159     /* (6) Register the card instance */
0160     ret = snd_card_register(sc);
0161     if (ret) {
0162         itv->alsa = NULL;
0163         IVTV_ALSA_ERR("%s: snd_card_register() failed with err %d\n",
0164                   __func__, ret);
0165         goto err_exit_free;
0166     }
0167 
0168     IVTV_ALSA_INFO("%s: Instance %d registered as ALSA card %d\n",
0169              __func__, itv->instance, sc->number);
0170 
0171     return 0;
0172 
0173 err_exit_free:
0174     if (sc != NULL)
0175         snd_card_free(sc);
0176     kfree(itvsc);
0177 err_exit:
0178     return ret;
0179 }
0180 
0181 static int ivtv_alsa_load(struct ivtv *itv)
0182 {
0183     struct v4l2_device *v4l2_dev = &itv->v4l2_dev;
0184     struct ivtv_stream *s;
0185 
0186     if (v4l2_dev == NULL) {
0187         pr_err("ivtv-alsa: %s: struct v4l2_device * is NULL\n",
0188                __func__);
0189         return 0;
0190     }
0191 
0192     itv = to_ivtv(v4l2_dev);
0193     if (itv == NULL) {
0194         pr_err("ivtv-alsa itv is NULL\n");
0195         return 0;
0196     }
0197 
0198     s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM];
0199     if (s->vdev.v4l2_dev == NULL) {
0200         IVTV_DEBUG_ALSA_INFO("PCM stream for card is disabled - skipping\n");
0201         return 0;
0202     }
0203 
0204     if (itv->alsa != NULL) {
0205         IVTV_ALSA_ERR("%s: struct snd_ivtv_card * already exists\n",
0206                   __func__);
0207         return 0;
0208     }
0209 
0210     if (snd_ivtv_init(v4l2_dev)) {
0211         IVTV_ALSA_ERR("%s: failed to create struct snd_ivtv_card\n",
0212                   __func__);
0213     } else {
0214         IVTV_DEBUG_ALSA_INFO("created ivtv ALSA interface instance\n");
0215     }
0216     return 0;
0217 }
0218 
0219 static int __init ivtv_alsa_init(void)
0220 {
0221     pr_info("ivtv-alsa: module loading...\n");
0222     ivtv_ext_init = &ivtv_alsa_load;
0223     return 0;
0224 }
0225 
0226 static void __exit snd_ivtv_exit(struct snd_ivtv_card *itvsc)
0227 {
0228     struct ivtv *itv = to_ivtv(itvsc->v4l2_dev);
0229 
0230     /* FIXME - pointer checks & shutdown itvsc */
0231 
0232     snd_card_free(itvsc->sc);
0233     itv->alsa = NULL;
0234 }
0235 
0236 static int __exit ivtv_alsa_exit_callback(struct device *dev, void *data)
0237 {
0238     struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
0239     struct snd_ivtv_card *itvsc;
0240 
0241     if (v4l2_dev == NULL) {
0242         pr_err("ivtv-alsa: %s: struct v4l2_device * is NULL\n",
0243                __func__);
0244         return 0;
0245     }
0246 
0247     itvsc = to_snd_ivtv_card(v4l2_dev);
0248     if (itvsc == NULL) {
0249         IVTV_ALSA_WARN("%s: struct snd_ivtv_card * is NULL\n",
0250                    __func__);
0251         return 0;
0252     }
0253 
0254     snd_ivtv_exit(itvsc);
0255     return 0;
0256 }
0257 
0258 static void __exit ivtv_alsa_exit(void)
0259 {
0260     struct device_driver *drv;
0261     int ret;
0262 
0263     pr_info("ivtv-alsa: module unloading...\n");
0264 
0265     drv = driver_find("ivtv", &pci_bus_type);
0266     ret = driver_for_each_device(drv, NULL, NULL, ivtv_alsa_exit_callback);
0267     (void)ret;  /* suppress compiler warning */
0268 
0269     ivtv_ext_init = NULL;
0270     pr_info("ivtv-alsa: module unload complete\n");
0271 }
0272 
0273 module_init(ivtv_alsa_init);
0274 module_exit(ivtv_alsa_exit);