The patch number 14017 was added via Andy Walls <awa...@radix.net> to http://linuxtv.org/hg/v4l-dvb master development tree.
Kernel patches in this development tree may be modified to be backward compatible with older kernels. Compatibility modifications will be removed before inclusion into the mainstream Kernel If anyone has any objections, please let us know by sending a message to: Linux Media Mailing List <linux-me...@vger.kernel.org> ------ From: Andy Walls <awa...@radix.net> cx18-alsa: Initial non-working cx18-alsa files Initial cx18-alsa module files check-in to avoid losing work. Priority: normal Signed-off-by: Andy Walls <awa...@radix.net> --- linux/drivers/media/video/cx18/cx18-alsa-mixer.c | 191 +++++++++ linux/drivers/media/video/cx18/cx18-alsa-mixer.h | 23 + linux/drivers/media/video/cx18/cx18-alsa.c | 303 +++++++++++++++ linux/drivers/media/video/cx18/cx18-alsa.h | 55 ++ linux/drivers/media/video/cx18/cx18-driver.h | 1 5 files changed, 573 insertions(+) diff -r cdcf089168df -r 4e2cd4b589d8 linux/drivers/media/video/cx18/cx18-alsa-mixer.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/linux/drivers/media/video/cx18/cx18-alsa-mixer.c Mon May 25 20:40:25 2009 -0400 @@ -0,0 +1,191 @@ +/* + * ALSA mixer controls for the + * ALSA interface to cx18 PCM capture streams + * + * Copyright (C) 2009 Andy Walls <awa...@radix.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/spinlock.h> +#include <linux/videodev2.h> + +#include <media/v4l2-device.h> + +#include <sound/core.h> +#include <sound/control.h> +#include <sound/tlv.h> + +#include "cx18-alsa.h" +#include "cx18-driver.h" + +/* + * Mixer manipulations are like v4l2 ioctl() calls to manipulate controls, + * just use the same lock we use for ioctl()s for now + */ +static inline void snd_cx18_mixer_lock(struct snd_cx18_card *cxsc) +{ + struct cx18 *cx = to_cx18(cxsc->v4l2_dev); + mutex_lock(&cx->serialize_lock); +} + +static inline void snd_cx18_mixer_unlock(struct snd_cx18_card *cxsc) +{ + struct cx18 *cx = to_cx18(cxsc->v4l2_dev); + mutex_unlock(&cx->serialize_lock); +} + +/* + * Note the cx18-av-core volume scale is funny, due to the alignment of the + * scale with another chip's range: + * + * v4l2_control value /512 indicated dB actual dB reg 0x8d4 + * 0x0000 - 0x01ff 0 -119 -96 228 + * 0x0200 - 0x02ff 1 -118 -96 228 + * ... + * 0x2c00 - 0x2dff 22 -97 -96 228 + * 0x2e00 - 0x2fff 23 -96 -96 228 + * 0x3000 - 0x31ff 24 -95 -95 226 + * ... + * 0xee00 - 0xefff 119 0 0 36 + * ... + * 0xfe00 - 0xffff 127 +8 +8 20 + */ +static inline int dB_to_cx18_av_vol(int dB) +{ + if (dB < -96) + dB = -96; + else if (dB > 8) + dB = 8; + return (dB + 119) << 9; +} + +static inline int cx18_av_vol_to_dB(int v) +{ + if (v < (23 << 9)) + v = (23 << 9); + else if (v > (127 << 9)) + v = (127 << 9); + return (v >> 9) - 119; +} + +static int snd_cx18_mixer_tv_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + /* We're already translating values, just keep this control in dB */ + uinfo->value.integer.min = -96; + uinfo->value.integer.max = +8; + uinfo->value.integer.step = 1; + return 0; +} + +static int snd_cx18_mixer_tv_vol_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *uctl) +{ + struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl); + struct cx18 *cx = to_cx18(cxsc->v4l2_dev); + struct v4l2_control vctrl; + int ret; + + vctrl.id = V4L2_CID_AUDIO_VOLUME; + vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]); + + snd_cx18_mixer_lock(cxsc); + ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl); + snd_cx18_mixer_unlock(cxsc); + + if (!ret) + uctl->value.integer.value[0] = cx18_av_vol_to_dB(vctrl.value); + return ret; +} + +static int snd_cx18_mixer_tv_vol_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *uctl) +{ + struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl); + struct cx18 *cx = to_cx18(cxsc->v4l2_dev); + struct v4l2_control vctrl; + int ret; + + vctrl.id = V4L2_CID_AUDIO_VOLUME; + vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]); + + snd_cx18_mixer_lock(cxsc); + + /* Fetch current state */ + ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl); + + if (ret || + (cx18_av_vol_to_dB(vctrl.value) != uctl->value.integer.value[0])) { + + /* Set, if needed */ + vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]); + ret = v4l2_subdev_call(cx->sd_av, core, s_ctrl, &vctrl); + if (!ret) + ret = 1; /* Indicate control was changed w/o error */ + } + snd_cx18_mixer_unlock(cxsc); + + return ret; +} + + +/* This is a bit of overkill, the slider is already in dB internally */ +static DECLARE_TLV_DB_SCALE(snd_cx18_mixer_tv_vol_db_scale, -9600, 100, 0); + +static struct snd_kcontrol_new snd_cx18_mixer_tv_vol __initdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog TV Capture Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = snd_cx18_mixer_tv_volume_info, + .get = snd_cx18_mixer_tv_volume_get, + .put = snd_cx18_mixer_tv_volume_put, + .tlv.p = snd_cx18_mixer_tv_vol_db_scale +}; + +/* FIXME - add mute switch and balance, bass, treble sliders: + V4L2_CID_AUDIO_MUTE + + V4L2_CID_AUDIO_BALANCE + + V4L2_CID_AUDIO_BASS + V4L2_CID_AUDIO_TREBLE +*/ + +/* FIXME - add stereo, lang1, lang2, mono menu */ +/* FIXME - add CS5345 I2S volume for HVR-1600 */ + +int __init snd_cx18_mixer_create(struct snd_cx18_card *cxsc) +{ + struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; + struct snd_card *sc = cxsc->sc; + int ret; + + strlcpy(sc->mixername, "CX23418 Mixer", sizeof(sc->mixername)); + + ret = snd_ctl_add(sc, snd_ctl_new1(snd_cx18_mixer_tv_vol, cxsc)); + if (ret) { + CX18_ALSA_WARN("%s: failed to add %s control, err %d\n", + __func__, snd_cx18_mixer_tv_vol.name, ret); + } + return ret; +} diff -r cdcf089168df -r 4e2cd4b589d8 linux/drivers/media/video/cx18/cx18-alsa-mixer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/linux/drivers/media/video/cx18/cx18-alsa-mixer.h Mon May 25 20:40:25 2009 -0400 @@ -0,0 +1,23 @@ +/* + * ALSA mixer controls for the + * ALSA interface to cx18 PCM capture streams + * + * Copyright (C) 2009 Andy Walls <awa...@radix.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +int __init snd_cx18_mixer_create(struct snd_cx18_card *cxsc); diff -r cdcf089168df -r 4e2cd4b589d8 linux/drivers/media/video/cx18/cx18-alsa.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/linux/drivers/media/video/cx18/cx18-alsa.c Mon May 25 20:40:25 2009 -0400 @@ -0,0 +1,303 @@ +/* + * ALSA interface to cx18 PCM capture streams + * + * Copyright (C) 2009 Andy Walls <awa...@radix.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/spinlock.h> + +#include <media/v4l2-device.h> + +#include <sound/core.h> +#include <sound/initval.h> + +#include "cx18-driver.h" +#include "cx18-alsa-mixer.h" +#include "cx18-alsa-pcm.h" + +int cx18_alsa_debug; + +module_param_named(debug, cx18_alsa_debug, int, 0644); +MODULE_PARM_DESC(debug, + "Debug level (bitmask). Default: 0\n" + "\t\t\t 1/0x0001: warning\n" + "\t\t\t 2/0x0002: info\n"); + +MODULE_AUTHOR("Andy Walls"); +MODULE_DESCRIPTION("CX23418 ALSA Interface"); +MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder"); +MODULE_LICENSE("GPL"); + +MODULE_VERSION(CX18_VERSION); + +static inline +struct snd_cx18_card *to_snd_cx18_card(struct v4l2_device *v4l2_dev) +{ + return to_cx18(v4l2_dev)->alsa; +} + +static inline +struct snd_cx18_card *p_to_snd_cx18_card(struct v4l2_device **v4l2_dev) +{ + return container_of(v4l2_dev, struct snd_cx18_card, v4l2_dev); +} + +static void snd_cx18_card_free(struct snd_cx18_card *cxsc) +{ + if (cxsc == NULL) + return; + + if (cxsc->v4l2_dev != NULL) + to_cx18(cxsc->v4l2_dev)->alsa = NULL; + + /* FIXME - take any other stopping actions needed */ + + kfree(cxsc); +} + +static void snd_cx18_card_private_free(struct snd_card *sc) +{ + if (sc == NULL) + return; + snd_cx18_card_free(sc->private_data); + sc->private_data = NULL; + sc->private_free = NULL; +} + +static int __init snd_cx18_card_create(struct v4l2_device *v4l2_dev, + struct snd_card *sc, + struct snd_cx18_card **cxsc) +{ + *cxsc = kzalloc(sizeof(struct snd_cx18_card), GFP_KERNEL); + if (*cxsc == NULL) + return -ENOMEM; + + (*cxsc)->v4l2_dev = v4l2_dev; + (*cxsc)->sc = sc; + + sc->private_data = *cxsc; + sc->private_free = snd_cx18_card_private_free; + + return 0; +} + +static int __init snd_cx18_card_set_names(struct snd_cx18_card *cxsc) +{ + struct cx18 *cx = to_cx18(cxsc->v4l2_dev); + struct snd_card *sc = cxsc->sc; + + /* sc->driver is used by alsa-lib's configurator: simple, unique */ + strlcpy(sc->driver, "CX23418", sizeof(sc->driver)); + + /* sc->shortname is a symlink in /proc/asound: CX18-M -> cardN */ + snprintf(sc->shortname, sizeof(sc->shortname), "CX18-%d", + cx->instance); + + /* sc->longname is read from /proc/asound/cards */ + snprintf(sc->longname, sizeof(sc->longname), + "CX23418 #%d %s TV/FM Radio/Line-In Capture", + cx->instance, cx->card_name); +} + +static int __init snd_cx18_init(struct v4l2_device *v4l2_dev) +{ + struct cx18 *cx = to_cx18(v4l2_dev); + struct snd_card *sc; + struct snd_cx18_card *cxsc; + int ret; + + /* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */ + + /* (1) Check and increment the device index */ + /* This is a no-op for us. We'll use the cx->instance */ + + /* (2) Create a card instance */ + ret = snd_card_create(SNDRV_DEFAULT_IDX1, /* use first available id */ + SNDRV_DEFAULT_STR1, /* xid from end of shortname*/ + THIS_MODULE, 0, &sc); + if (ret) { + CX18_ALSA_ERR("%s: snd_card_create() failed with err %d\n", + __func__, ret); + goto err_exit; + } + + /* (3) Create a main component */ + ret = snd_cx18_card_create(v4l2_dev, sc, &cxsc); + if (ret) { + CX18_ALSA_ERR("%s: snd_cx18_card_create() failed with err %d\n", + __func__, ret); + goto err_exit_free; + } + + /* (4) Set the driver ID and name strings */ + snd_cx18_card_set_names(cxsc); + + /* (5) Create other components: mixer, PCM, & proc files */ + ret = snd_cx18_mixer_create(cxsc); + if (ret) { + CX18_ALSA_WARN("%s: snd_cx18_mixer_create() failed with err %d:" + "proceeding anyway\n", __func__, ret); + } + + ret = snd_cx18_pcm_create(cxsc); + if (ret) { + CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n", + __func__, ret); + goto err_exit_free; + } + /* FIXME - proc files */ + + /* (7) Set the driver data and return 0 */ + /* We do this out of normal order for PCI drivers to avoid races */ + cx->alsa = cxsc; + + /* (6) Register the card instance */ + ret = snd_card_register(sc); + if (ret) { + cx->alsa = NULL; + CX18_ALSA_ERR("%s: snd_card_register() failed with err %d\n", + __func__, ret); + goto err_exit_free; + } + + return 0; + +err_exit_free: + snd_card_free(sc); +err_exit: + return ret; +} + +static int __init cx18_alsa_init_callback(struct device *dev, void *data) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + int *count = data; + struct cx18 *cx; + struct cx18_stream *s; + + if (v4l2_dev == NULL) { + printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n", + __func__); + return 0; + } + + cx = to_cx18(v4l2_dev); + s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM]; + if (s->video_dev == NULL) { + CX18_DEBUG_ALSA_INFO("%s: PCM stream for card is disabled - " + "skipping\n", __func__); + return 0; + } + + if (cx->alsa != NULL) { + CX18_ALSA_ERR("%s: struct snd_cx18_card * already exists\n", + __func__); + return 0; + } + + if (snd_cx18_init(v4l2_dev)) { + CX18_ALSA_ERR("%s: failed to create struct snd_cx18_card\n", + __func__); + } else { + CX18_DEBUG_ALSA_INFO("%s: created cx18 ALSA interface instance " + "%d\n", __func__, *count); + (*count)++; + } + return 0; +} + +static int __init cx18_alsa_init(void) +{ + struct device_driver *drv; + int count = 0; + int ret; + + printk(KERN_INFO "cx18-alsa: module loading...\n"); + + drv = driver_find("cx18", &pci_bus_type); + ret = driver_for_each_device(drv, NULL, &count, + cx18_alsa_init_callback); + put_driver(drv); + + if (count == 0) { + printk(KERN_ERR "cx18-alsa: no cx18 cards found with a PCM " + "capture stream allocated\n"); + ret = -ENODEV; + } else { + printk(KERN_INFO "cx18-alsa: ALSA interface(s) created for %d " + "cx18 card(s)\n", count); + ret = 0; + } + + printk(KERN_INFO "cx18-alsa: module load complete\n"); + return ret; +} + +static void snd_cx18_exit(struct snd_cx18_card *cxsc) +{ + struct cx18 *cx = to_cx18(cxsc->4l2_dev); + + /* FIXME - pointer checks & shutdown cxsc */ + + snd_card_free(cxsc->sc); + cx->alsa = NULL; +} + +static int cx18_alsa_exit_callback(struct device *dev, void *data) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + struct snd_cx18_card *cxsc; + struct cx18 *cx; + + if (v4l2_dev == NULL) { + printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n", + __func__); + return 0; + } + + cxsc = to_snd_cx18_card(v4l2_dev); + if (cxsc == NULL) { + CX18_ALSA_WARN("%s: struct snd_cx18_card * is NULL\n", + __func__); + return 0; + } + + snd_cx18_exit(cxsc); + return 0; +} + +static void cx18_alsa_exit(void) +{ + struct device_driver *drv; + int ret; + + printk(KERN_INFO "cx18-alsa: module unloading...\n"); + + drv = driver_find("cx18", &pci_bus_type); + ret = driver_for_each_device(drv, NULL, NULL, cx18_alsa_exit_callback); + put_driver(drv); + + printk(KERN_INFO "cx18-alsa: module unload complete\n"); +} + +module_init(cx18_alsa_init); +module_exit(cx18_alsa_exit); diff -r cdcf089168df -r 4e2cd4b589d8 linux/drivers/media/video/cx18/cx18-alsa.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/linux/drivers/media/video/cx18/cx18-alsa.h Mon May 25 20:40:25 2009 -0400 @@ -0,0 +1,55 @@ +/* + * ALSA interface to cx18 PCM capture streams + * + * Copyright (C) 2009 Andy Walls <awa...@radix.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +struct snd_card; + +struct snd_cx18_card { + struct v4l2_device *v4l2_dev; + struct snd_card *sc; +}; + +extern int cx18_alsa_debug; + +#define CX18_ALSA_DBGFLG_WARN (1 << 0) +#define CX18_ALSA_DBGFLG_WARN (1 << 0) +#define CX18_ALSA_DBGFLG_INFO (1 << 1) + +#define CX18_ALSA_DEBUG(x, type, fmt, args...) \ + do { \ + if ((x) & cx18_alsa_debug) \ + printk(KERN_INFO "%s-alsa: " type ": " fmt, \ + v4l2_dev->name , ## args); \ + } while (0) + +#define CX18_ALSA_DEBUG_WARN(fmt, args...) \ + CX18_ALSA_DEBUG(CX18_ALSA_DBGFLG_WARN, "warning", fmt , ## args) + +#define CX18_ALSA_DEBUG_INFO(fmt, args...) \ + CX18_ALSA_DEBUG(CX18_ALSA_DBGFLG_INFO, "info", fmt , ## args) + +#define CX18_ALSA_ERR(fmt, args...) \ + printk(KERN_ERR "%s-alsa: " fmt, v4l2_dev->name , ## args) + +#define CX18_ALSA_WARN(fmt, args...) \ + printk(KERN_WARNING "%s-alsa: " fmt, v4l2_dev->name , ## args) + +#define CX18_ALSA_INFO(fmt, args...) \ + printk(KERN_INFO "%s-alsa: " fmt, v4l2_dev->name , ## args) diff -r cdcf089168df -r 4e2cd4b589d8 linux/drivers/media/video/cx18/cx18-driver.h --- a/linux/drivers/media/video/cx18/cx18-driver.h Sun Jan 17 20:42:47 2010 -0200 +++ b/linux/drivers/media/video/cx18/cx18-driver.h Mon May 25 20:40:25 2009 -0400 @@ -576,6 +576,7 @@ int stream_buffers[CX18_MAX_STREAMS]; /* # of buffers for each stream */ int stream_buf_size[CX18_MAX_STREAMS]; /* Stream buffer size */ struct cx18_stream streams[CX18_MAX_STREAMS]; /* Stream data */ + struct snd_cx18_card *alsa; /* ALSA interface for PCM capture stream */ unsigned long i_flags; /* global cx18 flags */ atomic_t ana_capturing; /* count number of active analog capture streams */ atomic_t tot_capturing; /* total count number of active capture streams */ --- Patch is available at: http://linuxtv.org/hg/v4l-dvb/rev/4e2cd4b589d8291c79e0a29968d937345cf987c3 _______________________________________________ linuxtv-commits mailing list linuxtv-commits@linuxtv.org http://www.linuxtv.org/cgi-bin/mailman/listinfo/linuxtv-commits