diff options
Diffstat (limited to 'linux/drivers/media/video/em28xx')
-rw-r--r-- | linux/drivers/media/video/em28xx/Kconfig | 15 | ||||
-rw-r--r-- | linux/drivers/media/video/em28xx/Makefile | 3 | ||||
-rw-r--r-- | linux/drivers/media/video/em28xx/em28xx-audio.c | 596 | ||||
-rw-r--r-- | linux/drivers/media/video/em28xx/em28xx-video.c | 63 | ||||
-rw-r--r-- | linux/drivers/media/video/em28xx/em28xx.h | 45 |
5 files changed, 418 insertions, 304 deletions
diff --git a/linux/drivers/media/video/em28xx/Kconfig b/linux/drivers/media/video/em28xx/Kconfig index 813077b6e..abbd38c1e 100644 --- a/linux/drivers/media/video/em28xx/Kconfig +++ b/linux/drivers/media/video/em28xx/Kconfig @@ -11,3 +11,18 @@ config VIDEO_EM28XX To compile this driver as a module, choose M here: the module will be called em28xx + +config VIDEO_EM28XX_ALSA + depends on VIDEO_EM28XX + tristate "Empia EM28xx ALSA audio module" + ---help--- + This is an ALSA driver for some Empia 28xx based TV cards. + + This is not required for em2800/em2820/em2821 boards. However, + newer em28xx devices uses Vendor Class for audio, instead of + implementing the USB Audio Class. For those chips, this module + will enable digital audio. + + To compile this driver as a module, choose M here: the + module will be called em28xx-alsa + diff --git a/linux/drivers/media/video/em28xx/Makefile b/linux/drivers/media/video/em28xx/Makefile index 7e7a93d75..092455099 100644 --- a/linux/drivers/media/video/em28xx/Makefile +++ b/linux/drivers/media/video/em28xx/Makefile @@ -1,7 +1,10 @@ em28xx-objs := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-core.o \ em28xx-input.o +em28xx-alsa-objs := em28xx-audio.o + obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o +obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o EXTRA_CFLAGS += -Idrivers/media/video EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c index 835633b83..8329ba287 100644 --- a/linux/drivers/media/video/em28xx/em28xx-audio.c +++ b/linux/drivers/media/video/em28xx/em28xx-audio.c @@ -3,6 +3,10 @@ * * Copyright (C) 2006 Markus Rechberger <mrechberger@gmail.com> * + * Copyright (C) 2007 Mauro Carvalho Chehab <mchehab@infradead.org> + * - Port to work with the in-kernel driver + * - Several cleanups + * * This driver is based on my previous au600 usb pstn audio driver * and inherits all the copyrights * @@ -30,7 +34,7 @@ #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/proc_fs.h> -#include <linux/moduleparam.h> +#include <linux/module.h> #include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> @@ -38,186 +42,316 @@ #include <sound/info.h> #include <sound/initval.h> #include <sound/control.h> -//#include <linux/video_decoder.h> -//#include <media/tuner.h> #include <media/v4l2-common.h> #include "em28xx.h" +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "activates debug info"); + +#define dprintk(fmt, arg...) do { \ + if (debug) \ + printk(KERN_INFO "em28xx-audio %s: " fmt, \ + __FUNCTION__, ##arg); \ + } while (0) + static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; -static int em28xx_cmd(struct em28xx *dev, int cmd, int arg); -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t * subs, size_t size) +static int em28xx_isoc_audio_deinit(struct em28xx *dev) +{ + int i; + + dprintk("Stopping isoc\n"); + for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { + usb_kill_urb(dev->adev->urb[i]); + usb_free_urb(dev->adev->urb[i]); + dev->adev->urb[i] = NULL; + } + + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +static void em28xx_audio_isocirq(struct urb *urb, struct pt_regs *regs) +#else +static void em28xx_audio_isocirq(struct urb *urb) +#endif +{ + struct em28xx *dev = urb->context; + int i; + unsigned int oldptr; + unsigned long flags; + int period_elapsed = 0; + int status; + unsigned char *cp; + unsigned int stride; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; +#else + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; +#endif + if (dev->adev->capture_pcm_substream) { + substream = dev->adev->capture_pcm_substream; + runtime = substream->runtime; + stride = runtime->frame_bits >> 3; + + for (i = 0; i < urb->number_of_packets; i++) { + int length = + urb->iso_frame_desc[i].actual_length / stride; + cp = (unsigned char *)urb->transfer_buffer + + urb->iso_frame_desc[i].offset; + + if (!length) + continue; + + spin_lock_irqsave(&dev->adev->slock, flags); + + oldptr = dev->adev->hwptr_done_capture; + dev->adev->hwptr_done_capture += length; + if (dev->adev->hwptr_done_capture >= + runtime->buffer_size) + dev->adev->hwptr_done_capture -= + runtime->buffer_size; + + dev->adev->capture_transfer_done += length; + if (dev->adev->capture_transfer_done >= + runtime->period_size) { + dev->adev->capture_transfer_done -= + runtime->period_size; + period_elapsed = 1; + } + + spin_unlock_irqrestore(&dev->adev->slock, flags); + + if (oldptr + length >= runtime->buffer_size) { + unsigned int cnt = + runtime->buffer_size - oldptr - 1; + memcpy(runtime->dma_area + oldptr * stride, cp, + cnt * stride); + memcpy(runtime->dma_area, cp + cnt, + length * stride - cnt * stride); + } else { + memcpy(runtime->dma_area + oldptr * stride, cp, + length * stride); + } + } + if (period_elapsed) + snd_pcm_period_elapsed(substream); + } + urb->status = 0; + + if (dev->adev->shutdown) + return; + + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status < 0) { + em28xx_errdev("resubmit of audio urb failed (error=%i)\n", + status); + } + return; +} + +static int em28xx_init_audio_isoc(struct em28xx *dev) +{ + int i, errCode; + const int sb_size = EM28XX_NUM_AUDIO_PACKETS * + EM28XX_AUDIO_MAX_PACKET_SIZE; + + dprintk("Starting isoc transfers\n"); + + for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { + struct urb *urb; + int j, k; + + dev->adev->transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC); + if (!dev->adev->transfer_buffer[i]) + return -ENOMEM; + + memset(dev->adev->transfer_buffer[i], 0x80, sb_size); + urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + urb->dev = dev->udev; + urb->context = dev; + urb->pipe = usb_rcvisocpipe(dev->udev, 0x83); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = dev->adev->transfer_buffer[i]; + urb->interval = 1; + urb->complete = em28xx_audio_isocirq; + urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS; + urb->transfer_buffer_length = sb_size; + + for (j = k = 0; j < EM28XX_NUM_AUDIO_PACKETS; + j++, k += EM28XX_AUDIO_MAX_PACKET_SIZE) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = + EM28XX_AUDIO_MAX_PACKET_SIZE; + } + dev->adev->urb[i] = urb; + } + + for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { + errCode = usb_submit_urb(dev->adev->urb[i], GFP_ATOMIC); + if (errCode) { + em28xx_isoc_audio_deinit(dev); + + return errCode; + } + } + + return 0; +} + +static int em28xx_cmd(struct em28xx *dev, int cmd, int arg) +{ + dprintk("%s transfer\n", (dev->adev->capture_stream == STREAM_ON)? + "stop" : "start"); + + switch (cmd) { + case EM28XX_CAPTURE_STREAM_EN: + if (dev->adev->capture_stream == STREAM_OFF && arg == 1) { + dev->adev->capture_stream = STREAM_ON; + em28xx_init_audio_isoc(dev); + } else if (dev->adev->capture_stream == STREAM_ON && arg == 0) { + dev->adev->capture_stream = STREAM_OFF; + em28xx_isoc_audio_deinit(dev); + } else { + printk(KERN_ERR "An underrun very likely occurred. " + "Ignoring it.\n"); + } + return 0; + default: + return -EINVAL; + } +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t *subs, + size_t size) #else static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, size_t size) #endif { -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) - snd_pcm_runtime_t *runtime = subs->runtime; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_runtime_t *runtime = subs->runtime; #else struct snd_pcm_runtime *runtime = subs->runtime; #endif + + dprintk("Alocating vbuffer\n"); if (runtime->dma_area) { if (runtime->dma_bytes > size) return 0; + vfree(runtime->dma_area); } runtime->dma_area = vmalloc(size); if (!runtime->dma_area) return -ENOMEM; + runtime->dma_bytes = size; + return 0; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) static snd_pcm_hardware_t snd_em28xx_hw_capture = { #else static struct snd_pcm_hardware snd_em28xx_hw_capture = { #endif - .info = - SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP_VALID, + .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT, + .rate_min = 48000, .rate_max = 48000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ - .period_bytes_min = 64, //12544/2, + .period_bytes_min = 64, /* 12544/2, */ .period_bytes_max = 12544, .periods_min = 2, - .periods_max = 98, //12544, + .periods_max = 98, /* 12544, */ }; -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static int snd_em28xx_capture_open(snd_pcm_substream_t * substream) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_em28xx_capture_open(snd_pcm_substream_t *substream) #else static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) #endif { - int ret = 0; - int mode; struct em28xx *dev = snd_pcm_substream_chip(substream); -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) snd_pcm_runtime_t *runtime = substream->runtime; #else struct snd_pcm_runtime *runtime = substream->runtime; #endif - printk("opening radio device and trying to acquire exclusive lock\n"); - switch (dev->mode) { - case TUNER_STUB_DVBC_TV: - case TUNER_STUB_DVBT_TV: - case TUNER_STUB_ATSC_TV: - /* digital has no support for analog audio */ - if (ret != 0) { - printk("device is already in use by DVB-T\n"); - return -EINVAL; - } else { - struct v4l2_tuner tuner; - printk("switching device to FM mode\n"); - - mode = TUNER_STUB_RADIO; - memset(&tuner, 0x0, sizeof(struct v4l2_tuner)); - tuner.type = V4L2_TUNER_RADIO; - - /* enable GPIO for analog TV */ - dev->em28xx_gpio_control(dev, EM28XX_MODE, - (void *)mode); - dev->mode = mode; - /* upload firmware */ - tuner_run_cmd(dev->tobj, TUNER_CMD_INIT, (void *)mode); - - /* required for devices which have kerneldriver dependencies */ -// em28xx_config(dev); -// em28xx_config_i2c(dev); - - /* this is moreover to switch the decoder to FM */ - em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, &tuner); - - dev->em28xx_write_regs(dev, 0x0f, "\x87", 1); - ret = dev->em28xx_acquire(dev, EM28XX_RADIO, 1); - em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, 0); - /* TODO switch to FM mode */ - - printk("em28xx-audio: %d mode\n", mode); - tuner_run_cmd(dev->tobj, TUNER_CMD_G_MODE, &mode); - printk("retrieved mode from tuner: %d\n", mode); - } - break; - - case TUNER_STUB_ANALOG_TV: - printk("em28xx-audio: device is currently in analog TV mode\n"); - /* unmute by default */ - dev->em28xx_write_regs(dev, 0x0f, "\x87", 1); - break; - case TUNER_STUB_RADIO: - /* check current mode and put a hard lock onto it */ - printk - ("em28xx-audio: device is currently in analogue FM mode\n"); - /* unmute by default here */ - dev->em28xx_write_regs(dev, 0x0f, "\x87", 1); - ret = dev->em28xx_acquire(dev, EM28XX_RADIO, 1); - if (ret == 0) - printk("device is locked in fmradio mode now\n"); - break; - default: - printk("em28xx-audio: unhandled mode %d\n", dev->mode); - } + int ret = 0; + + dprintk("opening device and trying to acquire exclusive lock\n"); + + /* Sets volume, mute, etc */ + dev->mute = 0; + ret = em28xx_audio_analog_set(dev); + if (ret < 0) + goto err; runtime->hw = snd_em28xx_hw_capture; if (dev->alt == 0 && dev->adev->users == 0) { int errCode; dev->alt = 7; errCode = usb_set_interface(dev->udev, 0, 7); - printk("changing alternate number to 7\n"); + dprintk("changing alternate number to 7\n"); } + dev->adev->users++; + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); dev->adev->capture_pcm_substream = substream; runtime->private_data = dev; + return 0; +err: + printk(KERN_ERR "Error while configuring em28xx mixer\n"); + return ret; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static int snd_em28xx_pcm_close(snd_pcm_substream_t * substream) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_em28xx_pcm_close(snd_pcm_substream_t *substream) #else static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream) #endif { struct em28xx *dev = snd_pcm_substream_chip(substream); - int amode = 0; dev->adev->users--; - /* decrease audio reference */ - switch (dev->mode) { - case TUNER_STUB_ANALOG_TV: - amode = EM28XX_VIDEO; - break; - case TUNER_STUB_RADIO: - amode = EM28XX_RADIO; - break; - default: - printk("invalid mode: %d\n", dev->mode); - break; - } + dprintk("closing device\n"); - dev->em28xx_acquire(dev, amode, 0); + dev->mute = 1; + em28xx_audio_analog_set(dev); if (dev->adev->users == 0 && dev->adev->shutdown == 1) { - printk("audio users: %d\n", dev->adev->users); - printk("disabling audio stream!\n"); + dprintk("audio users: %d\n", dev->adev->users); + dprintk("disabling audio stream!\n"); dev->adev->shutdown = 0; - printk("released lock\n"); + dprintk("released lock\n"); em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0); } return 0; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static int snd_em28xx_hw_capture_params(snd_pcm_substream_t * substream, - snd_pcm_hw_params_t * hw_params) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_em28xx_hw_capture_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *hw_params) #else static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) @@ -225,32 +359,39 @@ static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream, { unsigned int channels, rate, format; int ret; - ret = - snd_pcm_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); + + dprintk("Setting capture parameters\n"); + + ret = snd_pcm_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); format = params_format(hw_params); rate = params_rate(hw_params); channels = params_channels(hw_params); - /* TODO: set up em28xx audio chip to deliver the correct audio format, current default is 48000hz multiplexed => 96000hz mono + + /* TODO: set up em28xx audio chip to deliver the correct audio format, + current default is 48000hz multiplexed => 96000hz mono which shouldn't matter since analogue TV only supports mono */ return 0; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static int snd_em28xx_hw_capture_free(snd_pcm_substream_t * substream) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_em28xx_hw_capture_free(snd_pcm_substream_t *substream) #else static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream) #endif { struct em28xx *dev = snd_pcm_substream_chip(substream); - if (dev->adev->capture_stream == STREAM_ON) { + + dprintk("Stop capture, if needed\n"); + + if (dev->adev->capture_stream == STREAM_ON) em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0); - } + return 0; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static int snd_em28xx_prepare(snd_pcm_substream_t * substream) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_em28xx_prepare(snd_pcm_substream_t *substream) #else static int snd_em28xx_prepare(struct snd_pcm_substream *substream) #endif @@ -258,14 +399,17 @@ static int snd_em28xx_prepare(struct snd_pcm_substream *substream) return 0; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static int snd_em28xx_capture_trigger(snd_pcm_substream_t * substream, int cmd) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_em28xx_capture_trigger(snd_pcm_substream_t *substream, int cmd) #else static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream, int cmd) #endif { struct em28xx *dev = snd_pcm_substream_chip(substream); + + dprintk("Should %s capture\n", (cmd == SNDRV_PCM_TRIGGER_START)? + "start": "stop"); switch (cmd) { case SNDRV_PCM_TRIGGER_START: em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 1); @@ -278,181 +422,25 @@ static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream, } } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -static void em28xx_audio_isocirq(struct urb *urb, struct pt_regs *regs) -#else -static void em28xx_audio_isocirq(struct urb *urb) -#endif -{ - struct em28xx *dev = urb->context; - int i; - unsigned int oldptr; - unsigned long flags; - int period_elapsed = 0; - int status; - unsigned char *cp; - unsigned int stride; -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) - snd_pcm_substream_t *substream; - snd_pcm_runtime_t *runtime; -#else - struct snd_pcm_substream *substream; - struct snd_pcm_runtime *runtime; -#endif - if (dev->adev->capture_pcm_substream) { - substream = dev->adev->capture_pcm_substream; - runtime = substream->runtime; - - stride = runtime->frame_bits >> 3; - for (i = 0; i < urb->number_of_packets; i++) { - int length = - urb->iso_frame_desc[i].actual_length / stride; - cp = (unsigned char *)urb->transfer_buffer + - urb->iso_frame_desc[i].offset; - - if (!length) - continue; - - spin_lock_irqsave(&dev->adev->slock, flags); - oldptr = dev->adev->hwptr_done_capture; - dev->adev->hwptr_done_capture += length; - if (dev->adev->hwptr_done_capture >= - runtime->buffer_size) - dev->adev->hwptr_done_capture -= - runtime->buffer_size; - - dev->adev->capture_transfer_done += length; - if (dev->adev->capture_transfer_done >= - runtime->period_size) { - dev->adev->capture_transfer_done -= - runtime->period_size; - period_elapsed = 1; - } - spin_unlock_irqrestore(&dev->adev->slock, flags); - - if (oldptr + length >= runtime->buffer_size) { - unsigned int cnt = - runtime->buffer_size - oldptr - 1; - memcpy(runtime->dma_area + oldptr * stride, cp, - cnt * stride); - memcpy(runtime->dma_area, cp + cnt, - length * stride - cnt * stride); - } else { - memcpy(runtime->dma_area + oldptr * stride, cp, - length * stride); - } - } - if (period_elapsed) { - snd_pcm_period_elapsed(substream); - } - } - urb->status = 0; - - if (dev->adev->shutdown) - return; - - if ((status = usb_submit_urb(urb, GFP_ATOMIC))) { - em28xx_errdev("resubmit of audio urb failed (error=%i)\n", - status); - } - return; -} - -static int em28xx_isoc_audio_deinit(struct em28xx *dev) -{ - int i; - for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { - usb_kill_urb(dev->adev->urb[i]); - usb_free_urb(dev->adev->urb[i]); - dev->adev->urb[i] = NULL; - } - return 0; -} - -static int em28xx_init_audio_isoc(struct em28xx *dev) -{ - int i; - int errCode; - const int sb_size = - EM28XX_NUM_AUDIO_PACKETS * EM28XX_AUDIO_MAX_PACKET_SIZE; - - for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { - struct urb *urb; - int j, k; - dev->adev->transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC); - if (!dev->adev->transfer_buffer[i]) { - return -ENOMEM; - } - memset(dev->adev->transfer_buffer[i], 0x80, sb_size); - urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC); - if (urb) { - urb->dev = dev->udev; - urb->context = dev; - urb->pipe = usb_rcvisocpipe(dev->udev, 0x83); - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = dev->adev->transfer_buffer[i]; - urb->interval = 1; - urb->complete = em28xx_audio_isocirq; - urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS; - urb->transfer_buffer_length = sb_size; - for (j = k = 0; j < EM28XX_NUM_AUDIO_PACKETS; - j++, k += EM28XX_AUDIO_MAX_PACKET_SIZE) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = - EM28XX_AUDIO_MAX_PACKET_SIZE; - } - dev->adev->urb[i] = urb; - } else { - return -ENOMEM; - } - } - for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { - errCode = usb_submit_urb(dev->adev->urb[i], GFP_ATOMIC); - if (errCode) { - em28xx_isoc_audio_deinit(dev); - return errCode; - } - } - return 0; -} - -static int em28xx_cmd(struct em28xx *dev, int cmd, int arg) -{ - switch (cmd) { - case EM28XX_CAPTURE_STREAM_EN: - if (dev->adev->capture_stream == STREAM_OFF && arg == 1) { - dev->adev->capture_stream = STREAM_ON; - em28xx_init_audio_isoc(dev); - } else if (dev->adev->capture_stream == STREAM_ON && arg == 0) { - dev->adev->capture_stream = STREAM_OFF; - em28xx_isoc_audio_deinit(dev); - } else { - printk - ("An underrun occured very likely... ignoring it\n"); - } - return 0; - default: - return -EINVAL; - } -} - -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static snd_pcm_uframes_t snd_em28xx_capture_pointer(snd_pcm_substream_t * - substream) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static snd_pcm_uframes_t snd_em28xx_capture_pointer(snd_pcm_substream_t + *substream) #else static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream *substream) #endif { struct em28xx *dev; + snd_pcm_uframes_t hwptr_done; dev = snd_pcm_substream_chip(substream); hwptr_done = dev->adev->hwptr_done_capture; + return hwptr_done; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t * subs, +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t *subs, unsigned long offset) #else static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, @@ -460,43 +448,47 @@ static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, #endif { void *pageptr = subs->runtime->dma_area + offset; + return vmalloc_to_page(pageptr); } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) static snd_pcm_ops_t snd_em28xx_pcm_capture = { #else static struct snd_pcm_ops snd_em28xx_pcm_capture = { #endif - .open = snd_em28xx_capture_open, - .close = snd_em28xx_pcm_close, - .ioctl = snd_pcm_lib_ioctl, + .open = snd_em28xx_capture_open, + .close = snd_em28xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_em28xx_hw_capture_params, - .hw_free = snd_em28xx_hw_capture_free, - .prepare = snd_em28xx_prepare, - .trigger = snd_em28xx_capture_trigger, - .pointer = snd_em28xx_capture_pointer, - .page = snd_pcm_get_vmalloc_page, + .hw_free = snd_em28xx_hw_capture_free, + .prepare = snd_em28xx_prepare, + .trigger = snd_em28xx_capture_trigger, + .pointer = snd_em28xx_capture_pointer, + .page = snd_pcm_get_vmalloc_page, }; static int em28xx_audio_init(struct em28xx *dev) { struct em28xx_audio *adev; -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) - snd_pcm_t *pcm; - snd_card_t *card; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_t *pcm; + snd_card_t *card; #else - struct snd_pcm *pcm; - struct snd_card *card; + struct snd_pcm *pcm; + struct snd_card *card; #endif - static int devnr; - int ret; - int err; - printk("em28xx-audio.c: probing for em28x1 non standard usbaudio\n"); - printk("em28xx-audio.c: Copyright (C) 2006 Markus Rechberger\n"); + static int devnr; + int ret, err; + + printk(KERN_INFO "em28xx-audio.c: probing for em28x1 " + "non standard usbaudio\n"); + printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus " + "Rechberger\n"); + adev = kzalloc(sizeof(*adev), GFP_KERNEL); if (!adev) { - printk("em28xx-audio.c: out of memory\n"); + printk(KERN_ERR "em28xx-audio.c: out of memory\n"); return -1; } card = snd_card_new(index[devnr], "Em28xx Audio", THIS_MODULE, 0); @@ -515,13 +507,15 @@ static int em28xx_audio_init(struct em28xx *dev) strcpy(card->shortname, "Em28xx Audio"); strcpy(card->longname, "Empia Em28xx Audio"); - if ((err = snd_card_register(card)) < 0) { + err = snd_card_register(card); + if (err < 0) { snd_card_free(card); return -ENOMEM; } adev->sndcard = card; adev->udev = dev->udev; dev->adev = adev; + return 0; } @@ -529,16 +523,18 @@ static int em28xx_audio_fini(struct em28xx *dev) { if (dev == NULL) return 0; + if (dev->adev) { snd_card_free(dev->adev->sndcard); kfree(dev->adev); dev->adev = NULL; } + return 0; } static struct em28xx_ops audio_ops = { - .id = EM28XX_AUDIO, + .id = EM28XX_AUDIO, .name = "Em28xx Audio Extension", .init = em28xx_audio_init, .fini = em28xx_audio_fini, @@ -547,7 +543,6 @@ static struct em28xx_ops audio_ops = { static int __init em28xx_alsa_register(void) { request_module("em28xx"); - request_module("tuner"); return em28xx_register_extension(&audio_ops); } @@ -558,6 +553,7 @@ static void __exit em28xx_alsa_unregister(void) MODULE_LICENSE("GPL"); MODULE_AUTHOR("Markus Rechberger <mrechberger@gmail.com>"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); MODULE_DESCRIPTION("Em28xx Audio driver"); module_init(em28xx_alsa_register); diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index d9eb1bae0..a550eeb89 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -1671,9 +1671,48 @@ static const struct video_device em28xx_video_template = { .current_norm = V4L2_STD_PAL, }; - /******************************** usb interface *****************************************/ + +static LIST_HEAD(em28xx_extension_devlist); +static DEFINE_MUTEX(em28xx_extension_devlist_lock); + +int em28xx_register_extension(struct em28xx_ops *ops) +{ + struct em28xx *h, *dev = NULL; + + list_for_each_entry(h, &em28xx_devlist, devlist) + dev = h; + + mutex_lock(&em28xx_extension_devlist_lock); + list_add_tail(&ops->next, &em28xx_extension_devlist); + if (dev) + ops->init(dev); + + printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name); + mutex_unlock(&em28xx_extension_devlist_lock); + + return 0; +} +EXPORT_SYMBOL(em28xx_register_extension); + +void em28xx_unregister_extension(struct em28xx_ops *ops) +{ + struct em28xx *h, *dev = NULL; + + list_for_each_entry(h, &em28xx_devlist, devlist) + dev = h; + + if (dev) + ops->fini(dev); + + mutex_lock(&em28xx_extension_devlist_lock); + printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name); + list_del(&ops->next); + mutex_unlock(&em28xx_extension_devlist_lock); +} +EXPORT_SYMBOL(em28xx_unregister_extension); + /* * em28xx_init_dev() * allocates and inits the device structs, registers i2c bus and v4l device @@ -1681,6 +1720,7 @@ static const struct video_device em28xx_video_template = { static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, int minor) { + struct em28xx_ops *ops = NULL; struct em28xx *dev = *devhandle; int retval = -ENOMEM; int errCode; @@ -1817,6 +1857,15 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN, dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN); + mutex_lock(&em28xx_extension_devlist_lock); + if (!list_empty(&em28xx_extension_devlist)) { + list_for_each_entry(ops, &em28xx_extension_devlist, next) { + if (ops->id) + ops->init(dev); + } + } + mutex_unlock(&em28xx_extension_devlist_lock); + return 0; } @@ -1937,6 +1986,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, static void em28xx_usb_disconnect(struct usb_interface *interface) { struct em28xx *dev; + struct em28xx_ops *ops = NULL; dev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); @@ -1966,15 +2016,20 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) dev->state |= DEV_DISCONNECTED; em28xx_release_resources(dev); } - - mutex_unlock(&dev->lock); + mutex_lock(&em28xx_extension_devlist_lock); + if (!list_empty(&em28xx_extension_devlist)) { + list_for_each_entry(ops, &em28xx_extension_devlist, next) { + ops->fini(dev); + } + } + mutex_unlock(&em28xx_extension_devlist_lock); + if (!dev->users) { kfree(dev->alt_max_pkt_size); kfree(dev); } - } static struct usb_driver em28xx_usb_driver = { diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 4d318b3d5..ffe109077 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -215,6 +215,36 @@ enum em28xx_dev_state { DEV_MISCONFIGURED = 0x04, }; +#define EM28XX_AUDIO_BUFS 5 +#define EM28XX_NUM_AUDIO_PACKETS 64 +#define EM28XX_AUDIO_MAX_PACKET_SIZE 196 /* static value */ +#define EM28XX_CAPTURE_STREAM_EN 1 +#define EM28XX_AUDIO 0x10 + +struct em28xx_audio { + char name[50]; + char *transfer_buffer[EM28XX_AUDIO_BUFS]; + struct urb *urb[EM28XX_AUDIO_BUFS]; + struct usb_device *udev; + unsigned int capture_transfer_done; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_substream_t *capture_pcm_substream; +#else + struct snd_pcm_substream *capture_pcm_substream; +#endif + + unsigned int hwptr_done_capture; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_card_t *sndcard; +#else + struct snd_card *sndcard; +#endif + + int users, shutdown; + enum em28xx_stream_state capture_stream; + spinlock_t slock; +}; + /* main device struct */ struct em28xx { /* generic device properties */ @@ -269,6 +299,8 @@ struct em28xx { unsigned long hash; /* eeprom hash - for boards with generic ID */ unsigned long i2c_hash; /* i2c devicelist hash - for boards with generic ID */ + struct em28xx_audio *adev; + /* states */ enum em28xx_dev_state state; enum em28xx_stream_state stream; @@ -309,6 +341,15 @@ struct em28xx { struct em28xx_fh { struct em28xx *dev; unsigned int stream_on:1; /* Locks streams */ + int radio; +}; + +struct em28xx_ops { + struct list_head next; + char *name; + int id; + int (*init)(struct em28xx *); + int (*fini)(struct em28xx *); }; /* Provided by em28xx-i2c.c */ @@ -348,6 +389,10 @@ int em28xx_init_isoc(struct em28xx *dev); void em28xx_uninit_isoc(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); +/* Provided by em28xx-video.c */ +int em28xx_register_extension(struct em28xx_ops *dev); +void em28xx_unregister_extension(struct em28xx_ops *dev); + /* Provided by em28xx-cards.c */ extern int em2800_variant_detect(struct usb_device* udev,int model); extern void em28xx_pre_card_setup(struct em28xx *dev); |