提交 6d79468d 编写于 作者: M Mauro Carvalho Chehab

V4L/DVB (6951): Integrates em28xx-audio.c into em28xx kernel module

Also fixes the remaining CodingStyle issues that Lindent didn't fix.
Signed-off-by: NMauro Carvalho Chehab <mchehab@infradead.org>
上级 1a6f11e0
...@@ -11,3 +11,18 @@ config VIDEO_EM28XX ...@@ -11,3 +11,18 @@ config VIDEO_EM28XX
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called em28xx 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
em28xx-objs := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-core.o \ em28xx-objs := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-core.o \
em28xx-input.o em28xx-input.o
em28xx-alsa-objs := em28xx-audio.o
obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o
obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o
EXTRA_CFLAGS += -Idrivers/media/video EXTRA_CFLAGS += -Idrivers/media/video
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
......
...@@ -3,6 +3,10 @@ ...@@ -3,6 +3,10 @@
* *
* Copyright (C) 2006 Markus Rechberger <mrechberger@gmail.com> * 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 * This driver is based on my previous au600 usb pstn audio driver
* and inherits all the copyrights * and inherits all the copyrights
* *
...@@ -30,7 +34,7 @@ ...@@ -30,7 +34,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/moduleparam.h> #include <linux/module.h>
#include <sound/driver.h> #include <sound/driver.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/pcm.h> #include <sound/pcm.h>
...@@ -38,203 +42,33 @@ ...@@ -38,203 +42,33 @@
#include <sound/info.h> #include <sound/info.h>
#include <sound/initval.h> #include <sound/initval.h>
#include <sound/control.h> #include <sound/control.h>
//#include <linux/video_decoder.h>
//#include <media/tuner.h>
#include <media/v4l2-common.h> #include <media/v4l2-common.h>
#include "em28xx.h" #include "em28xx.h"
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static int debug;
static int em28xx_cmd(struct em28xx *dev, int cmd, int arg); module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "activates debug info");
static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, #define dprintk(fmt, arg...) do { \
size_t size) if (debug) \
{ printk(KERN_INFO "em28xx-audio %s: " fmt, \
struct snd_pcm_runtime *runtime = subs->runtime; __FUNCTION__, ##arg); \
if (runtime->dma_area) { } while (0)
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;
}
static struct snd_pcm_hardware snd_em28xx_hw_capture = { static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
.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_max = 12544,
.periods_min = 2,
.periods_max = 98, //12544,
};
static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
{
int ret = 0;
int mode;
struct em28xx *dev = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
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);
}
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");
}
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;
}
static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream)
{
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;
}
dev->em28xx_acquire(dev, amode, 0);
if (dev->adev->users == 0 && dev->adev->shutdown == 1) {
printk("audio users: %d\n", dev->adev->users);
printk("disabling audio stream!\n");
dev->adev->shutdown = 0;
printk("released lock\n");
em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0);
}
return 0;
}
static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream, static int em28xx_isoc_audio_deinit(struct em28xx *dev)
struct snd_pcm_hw_params *hw_params)
{ {
unsigned int channels, rate, format; int i;
int ret;
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
which shouldn't matter since analogue TV only supports mono */
return 0;
}
static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream) dprintk("Stopping isoc\n");
{ for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
struct em28xx *dev = snd_pcm_substream_chip(substream); usb_kill_urb(dev->adev->urb[i]);
if (dev->adev->capture_stream == STREAM_ON) { usb_free_urb(dev->adev->urb[i]);
em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0); dev->adev->urb[i] = NULL;
} }
return 0;
}
static int snd_em28xx_prepare(struct snd_pcm_substream *substream)
{
return 0;
}
static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct em28xx *dev = snd_pcm_substream_chip(substream);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 1);
return 0; return 0;
case SNDRV_PCM_TRIGGER_STOP:
dev->adev->shutdown = 1;
return 0;
default:
return -EINVAL;
}
} }
static void em28xx_audio_isocirq(struct urb *urb) static void em28xx_audio_isocirq(struct urb *urb)
...@@ -252,8 +86,8 @@ static void em28xx_audio_isocirq(struct urb *urb) ...@@ -252,8 +86,8 @@ static void em28xx_audio_isocirq(struct urb *urb)
if (dev->adev->capture_pcm_substream) { if (dev->adev->capture_pcm_substream) {
substream = dev->adev->capture_pcm_substream; substream = dev->adev->capture_pcm_substream;
runtime = substream->runtime; runtime = substream->runtime;
stride = runtime->frame_bits >> 3; stride = runtime->frame_bits >> 3;
for (i = 0; i < urb->number_of_packets; i++) { for (i = 0; i < urb->number_of_packets; i++) {
int length = int length =
urb->iso_frame_desc[i].actual_length / stride; urb->iso_frame_desc[i].actual_length / stride;
...@@ -264,6 +98,7 @@ static void em28xx_audio_isocirq(struct urb *urb) ...@@ -264,6 +98,7 @@ static void em28xx_audio_isocirq(struct urb *urb)
continue; continue;
spin_lock_irqsave(&dev->adev->slock, flags); spin_lock_irqsave(&dev->adev->slock, flags);
oldptr = dev->adev->hwptr_done_capture; oldptr = dev->adev->hwptr_done_capture;
dev->adev->hwptr_done_capture += length; dev->adev->hwptr_done_capture += length;
if (dev->adev->hwptr_done_capture >= if (dev->adev->hwptr_done_capture >=
...@@ -278,6 +113,7 @@ static void em28xx_audio_isocirq(struct urb *urb) ...@@ -278,6 +113,7 @@ static void em28xx_audio_isocirq(struct urb *urb)
runtime->period_size; runtime->period_size;
period_elapsed = 1; period_elapsed = 1;
} }
spin_unlock_irqrestore(&dev->adev->slock, flags); spin_unlock_irqrestore(&dev->adev->slock, flags);
if (oldptr + length >= runtime->buffer_size) { if (oldptr + length >= runtime->buffer_size) {
...@@ -292,50 +128,43 @@ static void em28xx_audio_isocirq(struct urb *urb) ...@@ -292,50 +128,43 @@ static void em28xx_audio_isocirq(struct urb *urb)
length * stride); length * stride);
} }
} }
if (period_elapsed) { if (period_elapsed)
snd_pcm_period_elapsed(substream); snd_pcm_period_elapsed(substream);
} }
}
urb->status = 0; urb->status = 0;
if (dev->adev->shutdown) if (dev->adev->shutdown)
return; return;
if ((status = usb_submit_urb(urb, GFP_ATOMIC))) { status = usb_submit_urb(urb, GFP_ATOMIC);
if (status < 0) {
em28xx_errdev("resubmit of audio urb failed (error=%i)\n", em28xx_errdev("resubmit of audio urb failed (error=%i)\n",
status); status);
} }
return; 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) static int em28xx_init_audio_isoc(struct em28xx *dev)
{ {
int i; int i, errCode;
int errCode; const int sb_size = EM28XX_NUM_AUDIO_PACKETS *
const int sb_size = EM28XX_AUDIO_MAX_PACKET_SIZE;
EM28XX_NUM_AUDIO_PACKETS * EM28XX_AUDIO_MAX_PACKET_SIZE;
dprintk("Starting isoc transfers\n");
for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
struct urb *urb; struct urb *urb;
int j, k; int j, k;
dev->adev->transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC); dev->adev->transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);
if (!dev->adev->transfer_buffer[i]) { if (!dev->adev->transfer_buffer[i])
return -ENOMEM; return -ENOMEM;
}
memset(dev->adev->transfer_buffer[i], 0x80, sb_size); memset(dev->adev->transfer_buffer[i], 0x80, sb_size);
urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC); urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);
if (urb) { if (!urb)
return -ENOMEM;
urb->dev = dev->udev; urb->dev = dev->udev;
urb->context = dev; urb->context = dev;
urb->pipe = usb_rcvisocpipe(dev->udev, 0x83); urb->pipe = usb_rcvisocpipe(dev->udev, 0x83);
...@@ -345,6 +174,7 @@ static int em28xx_init_audio_isoc(struct em28xx *dev) ...@@ -345,6 +174,7 @@ static int em28xx_init_audio_isoc(struct em28xx *dev)
urb->complete = em28xx_audio_isocirq; urb->complete = em28xx_audio_isocirq;
urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS; urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS;
urb->transfer_buffer_length = sb_size; urb->transfer_buffer_length = sb_size;
for (j = k = 0; j < EM28XX_NUM_AUDIO_PACKETS; for (j = k = 0; j < EM28XX_NUM_AUDIO_PACKETS;
j++, k += EM28XX_AUDIO_MAX_PACKET_SIZE) { j++, k += EM28XX_AUDIO_MAX_PACKET_SIZE) {
urb->iso_frame_desc[j].offset = k; urb->iso_frame_desc[j].offset = k;
...@@ -352,22 +182,25 @@ static int em28xx_init_audio_isoc(struct em28xx *dev) ...@@ -352,22 +182,25 @@ static int em28xx_init_audio_isoc(struct em28xx *dev)
EM28XX_AUDIO_MAX_PACKET_SIZE; EM28XX_AUDIO_MAX_PACKET_SIZE;
} }
dev->adev->urb[i] = urb; dev->adev->urb[i] = urb;
} else {
return -ENOMEM;
}
} }
for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
errCode = usb_submit_urb(dev->adev->urb[i], GFP_ATOMIC); errCode = usb_submit_urb(dev->adev->urb[i], GFP_ATOMIC);
if (errCode) { if (errCode) {
em28xx_isoc_audio_deinit(dev); em28xx_isoc_audio_deinit(dev);
return errCode; return errCode;
} }
} }
return 0; return 0;
} }
static int em28xx_cmd(struct em28xx *dev, int cmd, int arg) 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) { switch (cmd) {
case EM28XX_CAPTURE_STREAM_EN: case EM28XX_CAPTURE_STREAM_EN:
if (dev->adev->capture_stream == STREAM_OFF && arg == 1) { if (dev->adev->capture_stream == STREAM_OFF && arg == 1) {
...@@ -377,10 +210,162 @@ static int em28xx_cmd(struct em28xx *dev, int cmd, int arg) ...@@ -377,10 +210,162 @@ static int em28xx_cmd(struct em28xx *dev, int cmd, int arg)
dev->adev->capture_stream = STREAM_OFF; dev->adev->capture_stream = STREAM_OFF;
em28xx_isoc_audio_deinit(dev); em28xx_isoc_audio_deinit(dev);
} else { } else {
printk printk(KERN_ERR "An underrun very likely occurred. "
("An underrun occured very likely... ignoring it\n"); "Ignoring it.\n");
}
return 0;
default:
return -EINVAL;
}
}
static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
size_t size)
{
struct snd_pcm_runtime *runtime = subs->runtime;
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;
}
static struct snd_pcm_hardware snd_em28xx_hw_capture = {
.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_max = 12544,
.periods_min = 2,
.periods_max = 98, /* 12544, */
};
static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
{
struct em28xx *dev = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
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);
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;
}
static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream)
{
struct em28xx *dev = snd_pcm_substream_chip(substream);
dev->adev->users--;
dprintk("closing device\n");
dev->mute = 1;
em28xx_audio_analog_set(dev);
if (dev->adev->users == 0 && dev->adev->shutdown == 1) {
dprintk("audio users: %d\n", dev->adev->users);
dprintk("disabling audio stream!\n");
dev->adev->shutdown = 0;
dprintk("released lock\n");
em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0);
} }
return 0; return 0;
}
static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
unsigned int channels, rate, format;
int ret;
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
which shouldn't matter since analogue TV only supports mono */
return 0;
}
static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream)
{
struct em28xx *dev = snd_pcm_substream_chip(substream);
dprintk("Stop capture, if needed\n");
if (dev->adev->capture_stream == STREAM_ON)
em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0);
return 0;
}
static int snd_em28xx_prepare(struct snd_pcm_substream *substream)
{
return 0;
}
static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream,
int cmd)
{
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);
return 0;
case SNDRV_PCM_TRIGGER_STOP:
dev->adev->shutdown = 1;
return 0;
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -390,9 +375,11 @@ static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream ...@@ -390,9 +375,11 @@ static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream
*substream) *substream)
{ {
struct em28xx *dev; struct em28xx *dev;
snd_pcm_uframes_t hwptr_done; snd_pcm_uframes_t hwptr_done;
dev = snd_pcm_substream_chip(substream); dev = snd_pcm_substream_chip(substream);
hwptr_done = dev->adev->hwptr_done_capture; hwptr_done = dev->adev->hwptr_done_capture;
return hwptr_done; return hwptr_done;
} }
...@@ -400,6 +387,7 @@ static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, ...@@ -400,6 +387,7 @@ static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
unsigned long offset) unsigned long offset)
{ {
void *pageptr = subs->runtime->dma_area + offset; void *pageptr = subs->runtime->dma_area + offset;
return vmalloc_to_page(pageptr); return vmalloc_to_page(pageptr);
} }
...@@ -421,13 +409,16 @@ static int em28xx_audio_init(struct em28xx *dev) ...@@ -421,13 +409,16 @@ static int em28xx_audio_init(struct em28xx *dev)
struct snd_pcm *pcm; struct snd_pcm *pcm;
struct snd_card *card; struct snd_card *card;
static int devnr; static int devnr;
int ret; int ret, err;
int err;
printk("em28xx-audio.c: probing for em28x1 non standard usbaudio\n"); printk(KERN_INFO "em28xx-audio.c: probing for em28x1 "
printk("em28xx-audio.c: Copyright (C) 2006 Markus Rechberger\n"); "non standard usbaudio\n");
printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus "
"Rechberger\n");
adev = kzalloc(sizeof(*adev), GFP_KERNEL); adev = kzalloc(sizeof(*adev), GFP_KERNEL);
if (!adev) { if (!adev) {
printk("em28xx-audio.c: out of memory\n"); printk(KERN_ERR "em28xx-audio.c: out of memory\n");
return -1; return -1;
} }
card = snd_card_new(index[devnr], "Em28xx Audio", THIS_MODULE, 0); card = snd_card_new(index[devnr], "Em28xx Audio", THIS_MODULE, 0);
...@@ -446,13 +437,15 @@ static int em28xx_audio_init(struct em28xx *dev) ...@@ -446,13 +437,15 @@ static int em28xx_audio_init(struct em28xx *dev)
strcpy(card->shortname, "Em28xx Audio"); strcpy(card->shortname, "Em28xx Audio");
strcpy(card->longname, "Empia 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); snd_card_free(card);
return -ENOMEM; return -ENOMEM;
} }
adev->sndcard = card; adev->sndcard = card;
adev->udev = dev->udev; adev->udev = dev->udev;
dev->adev = adev; dev->adev = adev;
return 0; return 0;
} }
...@@ -460,11 +453,13 @@ static int em28xx_audio_fini(struct em28xx *dev) ...@@ -460,11 +453,13 @@ static int em28xx_audio_fini(struct em28xx *dev)
{ {
if (dev == NULL) if (dev == NULL)
return 0; return 0;
if (dev->adev) { if (dev->adev) {
snd_card_free(dev->adev->sndcard); snd_card_free(dev->adev->sndcard);
kfree(dev->adev); kfree(dev->adev);
dev->adev = NULL; dev->adev = NULL;
} }
return 0; return 0;
} }
...@@ -478,7 +473,6 @@ static struct em28xx_ops audio_ops = { ...@@ -478,7 +473,6 @@ static struct em28xx_ops audio_ops = {
static int __init em28xx_alsa_register(void) static int __init em28xx_alsa_register(void)
{ {
request_module("em28xx"); request_module("em28xx");
request_module("tuner");
return em28xx_register_extension(&audio_ops); return em28xx_register_extension(&audio_ops);
} }
...@@ -489,6 +483,7 @@ static void __exit em28xx_alsa_unregister(void) ...@@ -489,6 +483,7 @@ static void __exit em28xx_alsa_unregister(void)
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Markus Rechberger <mrechberger@gmail.com>"); MODULE_AUTHOR("Markus Rechberger <mrechberger@gmail.com>");
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
MODULE_DESCRIPTION("Em28xx Audio driver"); MODULE_DESCRIPTION("Em28xx Audio driver");
module_init(em28xx_alsa_register); module_init(em28xx_alsa_register);
......
...@@ -1599,9 +1599,48 @@ static const struct video_device em28xx_video_template = { ...@@ -1599,9 +1599,48 @@ static const struct video_device em28xx_video_template = {
.current_norm = V4L2_STD_PAL, .current_norm = V4L2_STD_PAL,
}; };
/******************************** usb interface *****************************************/ /******************************** 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() * em28xx_init_dev()
* allocates and inits the device structs, registers i2c bus and v4l device * allocates and inits the device structs, registers i2c bus and v4l device
...@@ -1609,6 +1648,7 @@ static const struct video_device em28xx_video_template = { ...@@ -1609,6 +1648,7 @@ static const struct video_device em28xx_video_template = {
static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
int minor) int minor)
{ {
struct em28xx_ops *ops = NULL;
struct em28xx *dev = *devhandle; struct em28xx *dev = *devhandle;
int retval = -ENOMEM; int retval = -ENOMEM;
int errCode; int errCode;
...@@ -1742,6 +1782,15 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, ...@@ -1742,6 +1782,15 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN, dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_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; return 0;
} }
...@@ -1862,6 +1911,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, ...@@ -1862,6 +1911,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,
static void em28xx_usb_disconnect(struct usb_interface *interface) static void em28xx_usb_disconnect(struct usb_interface *interface)
{ {
struct em28xx *dev; struct em28xx *dev;
struct em28xx_ops *ops = NULL;
dev = usb_get_intfdata(interface); dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL); usb_set_intfdata(interface, NULL);
...@@ -1891,15 +1941,20 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) ...@@ -1891,15 +1941,20 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
dev->state |= DEV_DISCONNECTED; dev->state |= DEV_DISCONNECTED;
em28xx_release_resources(dev); em28xx_release_resources(dev);
} }
mutex_unlock(&dev->lock); 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) { if (!dev->users) {
kfree(dev->alt_max_pkt_size); kfree(dev->alt_max_pkt_size);
kfree(dev); kfree(dev);
} }
} }
static struct usb_driver em28xx_usb_driver = { static struct usb_driver em28xx_usb_driver = {
......
...@@ -212,6 +212,28 @@ enum em28xx_dev_state { ...@@ -212,6 +212,28 @@ enum em28xx_dev_state {
DEV_MISCONFIGURED = 0x04, 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;
struct snd_pcm_substream *capture_pcm_substream;
unsigned int hwptr_done_capture;
struct snd_card *sndcard;
int users, shutdown;
enum em28xx_stream_state capture_stream;
spinlock_t slock;
};
/* main device struct */ /* main device struct */
struct em28xx { struct em28xx {
/* generic device properties */ /* generic device properties */
...@@ -266,6 +288,8 @@ struct em28xx { ...@@ -266,6 +288,8 @@ struct em28xx {
unsigned long hash; /* eeprom hash - for boards with generic ID */ unsigned long hash; /* eeprom hash - for boards with generic ID */
unsigned long i2c_hash; /* i2c devicelist hash - for boards with generic ID */ unsigned long i2c_hash; /* i2c devicelist hash - for boards with generic ID */
struct em28xx_audio *adev;
/* states */ /* states */
enum em28xx_dev_state state; enum em28xx_dev_state state;
enum em28xx_stream_state stream; enum em28xx_stream_state stream;
...@@ -302,6 +326,15 @@ struct em28xx { ...@@ -302,6 +326,15 @@ struct em28xx {
struct em28xx_fh { struct em28xx_fh {
struct em28xx *dev; struct em28xx *dev;
unsigned int stream_on:1; /* Locks streams */ 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 */ /* Provided by em28xx-i2c.c */
...@@ -341,6 +374,10 @@ int em28xx_init_isoc(struct em28xx *dev); ...@@ -341,6 +374,10 @@ int em28xx_init_isoc(struct em28xx *dev);
void em28xx_uninit_isoc(struct em28xx *dev); void em28xx_uninit_isoc(struct em28xx *dev);
int em28xx_set_alternate(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 */ /* Provided by em28xx-cards.c */
extern int em2800_variant_detect(struct usb_device* udev,int model); extern int em2800_variant_detect(struct usb_device* udev,int model);
extern void em28xx_pre_card_setup(struct em28xx *dev); extern void em28xx_pre_card_setup(struct em28xx *dev);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册