Path: blob/master/drivers/media/video/cx18/cx18-alsa-main.c
17768 views
/*1* ALSA interface to cx18 PCM capture streams2*3* Copyright (C) 2009 Andy Walls <[email protected]>4* Copyright (C) 2009 Devin Heitmueller <[email protected]>5*6* Portions of this work were sponsored by ONELAN Limited.7*8* This program is free software; you can redistribute it and/or modify9* it under the terms of the GNU General Public License as published by10* the Free Software Foundation; either version 2 of the License, or11* (at your option) any later version.12*13* This program is distributed in the hope that it will be useful,14* but WITHOUT ANY WARRANTY; without even the implied warranty of15* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the16* GNU General Public License for more details.17*18* You should have received a copy of the GNU General Public License19* along with this program; if not, write to the Free Software20* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA21* 02111-1307 USA22*/2324#include <linux/init.h>25#include <linux/slab.h>26#include <linux/module.h>27#include <linux/kernel.h>28#include <linux/device.h>29#include <linux/spinlock.h>3031#include <media/v4l2-device.h>3233#include <sound/core.h>34#include <sound/initval.h>3536#include "cx18-driver.h"37#include "cx18-version.h"38#include "cx18-alsa.h"39#include "cx18-alsa-mixer.h"40#include "cx18-alsa-pcm.h"4142int cx18_alsa_debug;4344#define CX18_DEBUG_ALSA_INFO(fmt, arg...) \45do { \46if (cx18_alsa_debug & 2) \47printk(KERN_INFO "%s: " fmt, "cx18-alsa", ## arg); \48} while (0);4950module_param_named(debug, cx18_alsa_debug, int, 0644);51MODULE_PARM_DESC(debug,52"Debug level (bitmask). Default: 0\n"53"\t\t\t 1/0x0001: warning\n"54"\t\t\t 2/0x0002: info\n");5556MODULE_AUTHOR("Andy Walls");57MODULE_DESCRIPTION("CX23418 ALSA Interface");58MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder");59MODULE_LICENSE("GPL");6061MODULE_VERSION(CX18_VERSION);6263static inline64struct snd_cx18_card *to_snd_cx18_card(struct v4l2_device *v4l2_dev)65{66return to_cx18(v4l2_dev)->alsa;67}6869static inline70struct snd_cx18_card *p_to_snd_cx18_card(struct v4l2_device **v4l2_dev)71{72return container_of(v4l2_dev, struct snd_cx18_card, v4l2_dev);73}7475static void snd_cx18_card_free(struct snd_cx18_card *cxsc)76{77if (cxsc == NULL)78return;7980if (cxsc->v4l2_dev != NULL)81to_cx18(cxsc->v4l2_dev)->alsa = NULL;8283/* FIXME - take any other stopping actions needed */8485kfree(cxsc);86}8788static void snd_cx18_card_private_free(struct snd_card *sc)89{90if (sc == NULL)91return;92snd_cx18_card_free(sc->private_data);93sc->private_data = NULL;94sc->private_free = NULL;95}9697static int snd_cx18_card_create(struct v4l2_device *v4l2_dev,98struct snd_card *sc,99struct snd_cx18_card **cxsc)100{101*cxsc = kzalloc(sizeof(struct snd_cx18_card), GFP_KERNEL);102if (*cxsc == NULL)103return -ENOMEM;104105(*cxsc)->v4l2_dev = v4l2_dev;106(*cxsc)->sc = sc;107108sc->private_data = *cxsc;109sc->private_free = snd_cx18_card_private_free;110111return 0;112}113114static int snd_cx18_card_set_names(struct snd_cx18_card *cxsc)115{116struct cx18 *cx = to_cx18(cxsc->v4l2_dev);117struct snd_card *sc = cxsc->sc;118119/* sc->driver is used by alsa-lib's configurator: simple, unique */120strlcpy(sc->driver, "CX23418", sizeof(sc->driver));121122/* sc->shortname is a symlink in /proc/asound: CX18-M -> cardN */123snprintf(sc->shortname, sizeof(sc->shortname), "CX18-%d",124cx->instance);125126/* sc->longname is read from /proc/asound/cards */127snprintf(sc->longname, sizeof(sc->longname),128"CX23418 #%d %s TV/FM Radio/Line-In Capture",129cx->instance, cx->card_name);130131return 0;132}133134static int snd_cx18_init(struct v4l2_device *v4l2_dev)135{136struct cx18 *cx = to_cx18(v4l2_dev);137struct snd_card *sc = NULL;138struct snd_cx18_card *cxsc;139int ret;140141/* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */142143/* (1) Check and increment the device index */144/* This is a no-op for us. We'll use the cx->instance */145146/* (2) Create a card instance */147ret = snd_card_create(SNDRV_DEFAULT_IDX1, /* use first available id */148SNDRV_DEFAULT_STR1, /* xid from end of shortname*/149THIS_MODULE, 0, &sc);150if (ret) {151CX18_ALSA_ERR("%s: snd_card_create() failed with err %d\n",152__func__, ret);153goto err_exit;154}155156/* (3) Create a main component */157ret = snd_cx18_card_create(v4l2_dev, sc, &cxsc);158if (ret) {159CX18_ALSA_ERR("%s: snd_cx18_card_create() failed with err %d\n",160__func__, ret);161goto err_exit_free;162}163164/* (4) Set the driver ID and name strings */165snd_cx18_card_set_names(cxsc);166167168ret = snd_cx18_pcm_create(cxsc);169if (ret) {170CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n",171__func__, ret);172goto err_exit_free;173}174/* FIXME - proc files */175176/* (7) Set the driver data and return 0 */177/* We do this out of normal order for PCI drivers to avoid races */178cx->alsa = cxsc;179180/* (6) Register the card instance */181ret = snd_card_register(sc);182if (ret) {183cx->alsa = NULL;184CX18_ALSA_ERR("%s: snd_card_register() failed with err %d\n",185__func__, ret);186goto err_exit_free;187}188189return 0;190191err_exit_free:192if (sc != NULL)193snd_card_free(sc);194err_exit:195return ret;196}197198int cx18_alsa_load(struct cx18 *cx)199{200struct v4l2_device *v4l2_dev = &cx->v4l2_dev;201struct cx18_stream *s;202203if (v4l2_dev == NULL) {204printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n",205__func__);206return 0;207}208209cx = to_cx18(v4l2_dev);210if (cx == NULL) {211printk(KERN_ERR "cx18-alsa cx is NULL\n");212return 0;213}214215s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM];216if (s->video_dev == NULL) {217CX18_DEBUG_ALSA_INFO("%s: PCM stream for card is disabled - "218"skipping\n", __func__);219return 0;220}221222if (cx->alsa != NULL) {223CX18_ALSA_ERR("%s: struct snd_cx18_card * already exists\n",224__func__);225return 0;226}227228if (snd_cx18_init(v4l2_dev)) {229CX18_ALSA_ERR("%s: failed to create struct snd_cx18_card\n",230__func__);231} else {232CX18_DEBUG_ALSA_INFO("%s: created cx18 ALSA interface instance "233"\n", __func__);234}235return 0;236}237238static int __init cx18_alsa_init(void)239{240printk(KERN_INFO "cx18-alsa: module loading...\n");241cx18_ext_init = &cx18_alsa_load;242return 0;243}244245static void __exit snd_cx18_exit(struct snd_cx18_card *cxsc)246{247struct cx18 *cx = to_cx18(cxsc->v4l2_dev);248249/* FIXME - pointer checks & shutdown cxsc */250251snd_card_free(cxsc->sc);252cx->alsa = NULL;253}254255static int __exit cx18_alsa_exit_callback(struct device *dev, void *data)256{257struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);258struct snd_cx18_card *cxsc;259260if (v4l2_dev == NULL) {261printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n",262__func__);263return 0;264}265266cxsc = to_snd_cx18_card(v4l2_dev);267if (cxsc == NULL) {268CX18_ALSA_WARN("%s: struct snd_cx18_card * is NULL\n",269__func__);270return 0;271}272273snd_cx18_exit(cxsc);274return 0;275}276277static void __exit cx18_alsa_exit(void)278{279struct device_driver *drv;280int ret;281282printk(KERN_INFO "cx18-alsa: module unloading...\n");283284drv = driver_find("cx18", &pci_bus_type);285ret = driver_for_each_device(drv, NULL, NULL, cx18_alsa_exit_callback);286put_driver(drv);287288cx18_ext_init = NULL;289printk(KERN_INFO "cx18-alsa: module unload complete\n");290}291292module_init(cx18_alsa_init);293module_exit(cx18_alsa_exit);294295296