brintos

brintos / linux-shallow public Read only

0
0
Text · 6.3 KiB · 9dc3618 Raw
275 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  ALSA interface to cx18 PCM capture streams4 *5 *  Copyright (C) 2009  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.9 */10 11#include <linux/init.h>12#include <linux/slab.h>13#include <linux/module.h>14#include <linux/kernel.h>15#include <linux/device.h>16#include <linux/spinlock.h>17 18#include <media/v4l2-device.h>19 20#include <sound/core.h>21#include <sound/initval.h>22 23#include "cx18-driver.h"24#include "cx18-version.h"25#include "cx18-alsa.h"26#include "cx18-alsa-pcm.h"27 28int cx18_alsa_debug;29 30#define CX18_DEBUG_ALSA_INFO(fmt, arg...) \31	do { \32		if (cx18_alsa_debug & 2) \33			printk(KERN_INFO "%s: " fmt, "cx18-alsa", ## arg); \34	} while (0);35 36module_param_named(debug, cx18_alsa_debug, int, 0644);37MODULE_PARM_DESC(debug,38		 "Debug level (bitmask). Default: 0\n"39		 "\t\t\t  1/0x0001: warning\n"40		 "\t\t\t  2/0x0002: info\n");41 42MODULE_AUTHOR("Andy Walls");43MODULE_DESCRIPTION("CX23418 ALSA Interface");44MODULE_LICENSE("GPL");45 46MODULE_VERSION(CX18_VERSION);47 48static inline49struct snd_cx18_card *to_snd_cx18_card(struct v4l2_device *v4l2_dev)50{51	return to_cx18(v4l2_dev)->alsa;52}53 54static void snd_cx18_card_free(struct snd_cx18_card *cxsc)55{56	if (cxsc == NULL)57		return;58 59	if (cxsc->v4l2_dev != NULL)60		to_cx18(cxsc->v4l2_dev)->alsa = NULL;61 62	/* FIXME - take any other stopping actions needed */63 64	kfree(cxsc);65}66 67static void snd_cx18_card_private_free(struct snd_card *sc)68{69	if (sc == NULL)70		return;71	snd_cx18_card_free(sc->private_data);72	sc->private_data = NULL;73	sc->private_free = NULL;74}75 76static int snd_cx18_card_create(struct v4l2_device *v4l2_dev,77				       struct snd_card *sc,78				       struct snd_cx18_card **cxsc)79{80	*cxsc = kzalloc(sizeof(struct snd_cx18_card), GFP_KERNEL);81	if (*cxsc == NULL)82		return -ENOMEM;83 84	(*cxsc)->v4l2_dev = v4l2_dev;85	(*cxsc)->sc = sc;86 87	sc->private_data = *cxsc;88	sc->private_free = snd_cx18_card_private_free;89 90	return 0;91}92 93static int snd_cx18_card_set_names(struct snd_cx18_card *cxsc)94{95	struct cx18 *cx = to_cx18(cxsc->v4l2_dev);96	struct snd_card *sc = cxsc->sc;97 98	/* sc->driver is used by alsa-lib's configurator: simple, unique */99	strscpy(sc->driver, "CX23418", sizeof(sc->driver));100 101	/* sc->shortname is a symlink in /proc/asound: CX18-M -> cardN */102	snprintf(sc->shortname,  sizeof(sc->shortname), "CX18-%d",103		 cx->instance);104 105	/* sc->longname is read from /proc/asound/cards */106	snprintf(sc->longname, sizeof(sc->longname),107		 "CX23418 #%d %s TV/FM Radio/Line-In Capture",108		 cx->instance, cx->card_name);109 110	return 0;111}112 113static int snd_cx18_init(struct v4l2_device *v4l2_dev)114{115	struct cx18 *cx = to_cx18(v4l2_dev);116	struct snd_card *sc = NULL;117	struct snd_cx18_card *cxsc;118	int ret;119 120	/* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */121 122	/* (1) Check and increment the device index */123	/* This is a no-op for us.  We'll use the cx->instance */124 125	/* (2) Create a card instance */126	ret = snd_card_new(&cx->pci_dev->dev,127			   SNDRV_DEFAULT_IDX1, /* use first available id */128			   SNDRV_DEFAULT_STR1, /* xid from end of shortname*/129			   THIS_MODULE, 0, &sc);130	if (ret) {131		CX18_ALSA_ERR("%s: snd_card_new() failed with err %d\n",132			      __func__, ret);133		goto err_exit;134	}135 136	/* (3) Create a main component */137	ret = snd_cx18_card_create(v4l2_dev, sc, &cxsc);138	if (ret) {139		CX18_ALSA_ERR("%s: snd_cx18_card_create() failed with err %d\n",140			      __func__, ret);141		goto err_exit_free;142	}143 144	/* (4) Set the driver ID and name strings */145	snd_cx18_card_set_names(cxsc);146 147 148	ret = snd_cx18_pcm_create(cxsc);149	if (ret) {150		CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n",151			      __func__, ret);152		goto err_exit_free;153	}154	/* FIXME - proc files */155 156	/* (7) Set the driver data and return 0 */157	/* We do this out of normal order for PCI drivers to avoid races */158	cx->alsa = cxsc;159 160	/* (6) Register the card instance */161	ret = snd_card_register(sc);162	if (ret) {163		cx->alsa = NULL;164		CX18_ALSA_ERR("%s: snd_card_register() failed with err %d\n",165			      __func__, ret);166		goto err_exit_free;167	}168 169	return 0;170 171err_exit_free:172	if (sc != NULL)173		snd_card_free(sc);174	kfree(cxsc);175err_exit:176	return ret;177}178 179static int cx18_alsa_load(struct cx18 *cx)180{181	struct v4l2_device *v4l2_dev = &cx->v4l2_dev;182	struct cx18_stream *s;183 184	if (v4l2_dev == NULL) {185		printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n",186		       __func__);187		return 0;188	}189 190	cx = to_cx18(v4l2_dev);191	if (cx == NULL) {192		printk(KERN_ERR "cx18-alsa cx is NULL\n");193		return 0;194	}195 196	s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM];197	if (s->video_dev.v4l2_dev == NULL) {198		CX18_DEBUG_ALSA_INFO("%s: PCM stream for card is disabled - skipping\n",199				     __func__);200		return 0;201	}202 203	if (cx->alsa != NULL) {204		CX18_ALSA_ERR("%s: struct snd_cx18_card * already exists\n",205			      __func__);206		return 0;207	}208 209	if (snd_cx18_init(v4l2_dev)) {210		CX18_ALSA_ERR("%s: failed to create struct snd_cx18_card\n",211			      __func__);212	} else {213		CX18_DEBUG_ALSA_INFO("%s: created cx18 ALSA interface instance\n",214				     __func__);215	}216	return 0;217}218 219static int __init cx18_alsa_init(void)220{221	printk(KERN_INFO "cx18-alsa: module loading...\n");222	cx18_ext_init = &cx18_alsa_load;223	return 0;224}225 226static void __exit snd_cx18_exit(struct snd_cx18_card *cxsc)227{228	struct cx18 *cx = to_cx18(cxsc->v4l2_dev);229 230	/* FIXME - pointer checks & shutdown cxsc */231 232	snd_card_free(cxsc->sc);233	cx->alsa = NULL;234}235 236static int __exit cx18_alsa_exit_callback(struct device *dev, void *data)237{238	struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);239	struct snd_cx18_card *cxsc;240 241	if (v4l2_dev == NULL) {242		printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n",243		       __func__);244		return 0;245	}246 247	cxsc = to_snd_cx18_card(v4l2_dev);248	if (cxsc == NULL) {249		CX18_ALSA_WARN("%s: struct snd_cx18_card * is NULL\n",250			       __func__);251		return 0;252	}253 254	snd_cx18_exit(cxsc);255	return 0;256}257 258static void __exit cx18_alsa_exit(void)259{260	struct device_driver *drv;261	int ret;262 263	printk(KERN_INFO "cx18-alsa: module unloading...\n");264 265	drv = driver_find("cx18", &pci_bus_type);266	ret = driver_for_each_device(drv, NULL, NULL, cx18_alsa_exit_callback);267	(void)ret;	/* suppress compiler warning */268 269	cx18_ext_init = NULL;270	printk(KERN_INFO "cx18-alsa: module unload complete\n");271}272 273module_init(cx18_alsa_init);274module_exit(cx18_alsa_exit);275