summaryrefslogtreecommitdiffstats
path: root/drivers/media/video/em28xx
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/em28xx')
-rw-r--r--drivers/media/video/em28xx/em28xx-audio.c6
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c8
-rw-r--r--drivers/media/video/em28xx/em28xx-core.c111
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c79
-rw-r--r--drivers/media/video/em28xx/em28xx.h5
5 files changed, 152 insertions, 57 deletions
diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c
index 941357c4f3f5..8c67f678266a 100644
--- a/drivers/media/video/em28xx/em28xx-audio.c
+++ b/drivers/media/video/em28xx/em28xx-audio.c
@@ -35,7 +35,6 @@
#include <linux/vmalloc.h>
#include <linux/proc_fs.h>
#include <linux/module.h>
-#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -270,8 +269,11 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
dprintk("opening device and trying to acquire exclusive lock\n");
/* Sets volume, mute, etc */
+
dev->mute = 0;
+ mutex_lock(&dev->lock);
ret = em28xx_audio_analog_set(dev);
+ mutex_unlock(&dev->lock);
if (ret < 0)
goto err;
@@ -303,7 +305,9 @@ static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream)
dprintk("closing device\n");
dev->mute = 1;
+ mutex_lock(&dev->lock);
em28xx_audio_analog_set(dev);
+ mutex_unlock(&dev->lock);
if (dev->adev->users == 0 && dev->adev->shutdown == 1) {
dprintk("audio users: %d\n", dev->adev->users);
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index 2159d0160df2..aae7753fef11 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -393,15 +393,15 @@ struct em28xx_board em28xx_boards[] = {
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
};
@@ -441,6 +441,8 @@ struct usb_device_id em28xx_id_table [] = {
.driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
{ USB_DEVICE(0x2040, 0x6500),
.driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 },
+ { USB_DEVICE(0x2040, 0x6502),
+ .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 },
{ USB_DEVICE(0x2040, 0x6513),
.driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 },
{ USB_DEVICE(0x0ccd, 0x0042),
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index f6b78357f0e5..7d1537cab867 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -72,7 +72,8 @@ u32 em28xx_request_buffers(struct em28xx *dev, u32 count)
const size_t imagesize = PAGE_ALIGN(dev->frame_size); /*needs to be page aligned cause the buffers can be mapped individually! */
void *buff = NULL;
u32 i;
- em28xx_coredbg("requested %i buffers with size %zi", count, imagesize);
+ em28xx_coredbg("requested %i buffers with size %zi\n",
+ count, imagesize);
if (count > EM28XX_NUM_FRAMES)
count = EM28XX_NUM_FRAMES;
@@ -150,7 +151,7 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
if (reg_debug){
printk(ret < 0 ? " failed!\n" : "%02x values: ", ret);
for (byte = 0; byte < len; byte++) {
- printk(" %02x", buf[byte]);
+ printk(" %02x", (unsigned char)buf[byte]);
}
printk("\n");
}
@@ -177,7 +178,8 @@ int em28xx_read_reg_req(struct em28xx *dev, u8 req, u16 reg)
0x0000, reg, &val, 1, HZ);
if (reg_debug)
- printk(ret < 0 ? " failed!\n" : "%02x\n", val);
+ printk(ret < 0 ? " failed!\n" :
+ "%02x\n", (unsigned char) val);
if (ret < 0)
return ret;
@@ -237,7 +239,7 @@ int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len)
* sets only some bits (specified by bitmask) of a register, by first reading
* the actual value
*/
-int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
+static int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
u8 bitmask)
{
int oldval;
@@ -254,26 +256,31 @@ int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
*/
static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 *val)
{
- int ret;
+ int ret, i;
u8 addr = reg & 0x7f;
if ((ret = em28xx_write_regs(dev, AC97LSB_REG, val, 2)) < 0)
return ret;
if ((ret = em28xx_write_regs(dev, AC97ADDR_REG, &addr, 1)) < 0)
return ret;
- if ((ret = em28xx_read_reg(dev, AC97BUSY_REG)) < 0)
- return ret;
- else if (((u8) ret) & 0x01) {
- em28xx_warn ("AC97 command still being executed: not handled properly!\n");
+
+ /* Wait up to 50 ms for AC97 command to complete */
+ for (i = 0; i < 10; i++) {
+ if ((ret = em28xx_read_reg(dev, AC97BUSY_REG)) < 0)
+ return ret;
+ if (!((u8) ret) & 0x01)
+ return 0;
+ msleep(5);
}
+ em28xx_warn ("AC97 command still being executed: not handled properly!\n");
return 0;
}
-int em28xx_set_audio_source(struct em28xx *dev)
+static int em28xx_set_audio_source(struct em28xx *dev)
{
static char *enable = "\x08\x08";
static char *disable = "\x08\x88";
char *video = enable, *line = disable;
- int ret, no_ac97;
+ int ret;
u8 input;
if (dev->is_em2800) {
@@ -293,11 +300,9 @@ int em28xx_set_audio_source(struct em28xx *dev)
switch (dev->ctl_ainput) {
case EM28XX_AMUX_VIDEO:
input = EM28XX_AUDIO_SRC_TUNER;
- no_ac97 = 1;
break;
case EM28XX_AMUX_LINE_IN:
input = EM28XX_AUDIO_SRC_LINE;
- no_ac97 = 1;
break;
case EM28XX_AMUX_AC97_VIDEO:
input = EM28XX_AUDIO_SRC_LINE;
@@ -313,12 +318,11 @@ int em28xx_set_audio_source(struct em28xx *dev)
ret = em28xx_write_reg_bits(dev, AUDIOSRC_REG, input, 0xc0);
if (ret < 0)
return ret;
+ msleep(5);
- if (no_ac97)
- return 0;
-
- /* Sets AC97 mixer registers */
-
+ /* Sets AC97 mixer registers
+ This is seems to be needed, even for non-ac97 configs
+ */
ret = em28xx_write_ac97(dev, VIDEO_AC97, video);
if (ret < 0)
return ret;
@@ -337,9 +341,10 @@ int em28xx_audio_analog_set(struct em28xx *dev)
s[0] |= 0x1f - dev->volume;
s[1] |= 0x1f - dev->volume;
- if (dev->mute)
- s[1] |= 0x80;
+ /* Mute */
+ s[1] |= 0x80;
ret = em28xx_write_ac97(dev, MASTER_AC97, s);
+
if (ret < 0)
return ret;
@@ -357,6 +362,11 @@ int em28xx_audio_analog_set(struct em28xx *dev)
/* Selects the proper audio input */
ret = em28xx_set_audio_source(dev);
+ /* Unmute device */
+ if (!dev->mute)
+ s[1] &= ~0x80;
+ ret = em28xx_write_ac97(dev, MASTER_AC97, s);
+
return ret;
}
EXPORT_SYMBOL_GPL(em28xx_audio_analog_set);
@@ -667,7 +677,7 @@ static void em28xx_isocIrq(struct urb *urb)
continue;
}
if (urb->iso_frame_desc[i].actual_length >
- dev->max_pkt_size) {
+ urb->iso_frame_desc[i].length) {
em28xx_isocdbg("packet bigger than packet size");
continue;
}
@@ -713,8 +723,11 @@ void em28xx_uninit_isoc(struct em28xx *dev)
for (i = 0; i < EM28XX_NUM_BUFS; i++) {
if (dev->urb[i]) {
usb_kill_urb(dev->urb[i]);
- if (dev->transfer_buffer[i]){
- usb_buffer_free(dev->udev,(EM28XX_NUM_PACKETS*dev->max_pkt_size),dev->transfer_buffer[i],dev->urb[i]->transfer_dma);
+ if (dev->transfer_buffer[i]) {
+ usb_buffer_free(dev->udev,
+ dev->urb[i]->transfer_buffer_length,
+ dev->transfer_buffer[i],
+ dev->urb[i]->transfer_dma);
}
usb_free_urb(dev->urb[i]);
}
@@ -732,7 +745,10 @@ int em28xx_init_isoc(struct em28xx *dev)
{
/* change interface to 3 which allows the biggest packet sizes */
int i, errCode;
- const int sb_size = EM28XX_NUM_PACKETS * dev->max_pkt_size;
+ int sb_size;
+
+ em28xx_set_alternate(dev);
+ sb_size = EM28XX_NUM_PACKETS * dev->max_pkt_size;
/* reset streaming vars */
dev->frame_current = NULL;
@@ -741,7 +757,7 @@ int em28xx_init_isoc(struct em28xx *dev)
/* allocate urbs */
for (i = 0; i < EM28XX_NUM_BUFS; i++) {
struct urb *urb;
- int j, k;
+ int j;
/* allocate transfer buffer */
urb = usb_alloc_urb(EM28XX_NUM_PACKETS, GFP_KERNEL);
if (!urb){
@@ -749,7 +765,9 @@ int em28xx_init_isoc(struct em28xx *dev)
em28xx_uninit_isoc(dev);
return -ENOMEM;
}
- dev->transfer_buffer[i] = usb_buffer_alloc(dev->udev, sb_size, GFP_KERNEL,&urb->transfer_dma);
+ dev->transfer_buffer[i] = usb_buffer_alloc(dev->udev, sb_size,
+ GFP_KERNEL,
+ &urb->transfer_dma);
if (!dev->transfer_buffer[i]) {
em28xx_errdev
("unable to allocate %i bytes for transfer buffer %i\n",
@@ -762,22 +780,22 @@ int em28xx_init_isoc(struct em28xx *dev)
urb->dev = dev->udev;
urb->context = dev;
urb->pipe = usb_rcvisocpipe(dev->udev, 0x82);
- urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
urb->interval = 1;
urb->transfer_buffer = dev->transfer_buffer[i];
urb->complete = em28xx_isocIrq;
urb->number_of_packets = EM28XX_NUM_PACKETS;
urb->transfer_buffer_length = sb_size;
- for (j = k = 0; j < EM28XX_NUM_PACKETS;
- j++, k += dev->max_pkt_size) {
- urb->iso_frame_desc[j].offset = k;
- urb->iso_frame_desc[j].length =
- dev->max_pkt_size;
+ for (j = 0; j < EM28XX_NUM_PACKETS; j++) {
+ urb->iso_frame_desc[j].offset = j * dev->max_pkt_size;
+ urb->iso_frame_desc[j].length = dev->max_pkt_size;
}
dev->urb[i] = urb;
}
/* submit urbs */
+ em28xx_coredbg("Submitting %d urbs of %d packets (%d each)\n",
+ EM28XX_NUM_BUFS, EM28XX_NUM_PACKETS, dev->max_pkt_size);
for (i = 0; i < EM28XX_NUM_BUFS; i++) {
errCode = usb_submit_urb(dev->urb[i], GFP_KERNEL);
if (errCode) {
@@ -794,22 +812,31 @@ int em28xx_init_isoc(struct em28xx *dev)
int em28xx_set_alternate(struct em28xx *dev)
{
int errCode, prev_alt = dev->alt;
- dev->alt = alt;
- if (dev->alt == 0) {
- int i;
- for(i=0;i< dev->num_alt; i++)
- if(dev->alt_max_pkt_size[i]>dev->alt_max_pkt_size[dev->alt])
- dev->alt=i;
- }
+ int i;
+ unsigned int min_pkt_size = dev->bytesperline+4;
+
+ /* When image size is bigger than a ceirtain value,
+ the frame size should be increased, otherwise, only
+ green screen will be received.
+ */
+ if (dev->frame_size > 720*240*2)
+ min_pkt_size *= 2;
+
+ for (i = 0; i < dev->num_alt; i++)
+ if (dev->alt_max_pkt_size[i] >= min_pkt_size)
+ break;
+ dev->alt = i;
if (dev->alt != prev_alt) {
+ em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n",
+ min_pkt_size, dev->alt);
dev->max_pkt_size = dev->alt_max_pkt_size[dev->alt];
- em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", dev->alt,
- dev->max_pkt_size);
+ em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n",
+ dev->alt, dev->max_pkt_size);
errCode = usb_set_interface(dev->udev, 0, dev->alt);
if (errCode < 0) {
em28xx_errdev ("cannot change alternate number to %d (error=%i)\n",
- dev->alt, errCode);
+ dev->alt, errCode);
return errCode;
}
}
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index a0c334672488..4abe6701a770 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -189,7 +189,7 @@ static void video_mux(struct em28xx *dev, int index)
em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, &route);
}
- em28xx_set_audio_source(dev);
+ em28xx_audio_analog_set(dev);
}
/* Usage lock check functions */
@@ -830,6 +830,63 @@ static int vidioc_s_frequency(struct file *file, void *priv,
return 0;
}
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int em28xx_reg_len(int reg)
+{
+ switch (reg) {
+ case AC97LSB_REG:
+ case HSCALELOW_REG:
+ case VSCALELOW_REG:
+ return 2;
+ default:
+ return 1;
+ }
+}
+
+static int vidioc_g_register(struct file *file, void *priv,
+ struct v4l2_register *reg)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int ret;
+
+ if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return -EINVAL;
+
+ if (em28xx_reg_len(reg->reg) == 1) {
+ ret = em28xx_read_reg(dev, reg->reg);
+ if (ret < 0)
+ return ret;
+
+ reg->val = ret;
+ } else {
+ u64 val = 0;
+ ret = em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS,
+ reg->reg, (char *)&val, 2);
+ if (ret < 0)
+ return ret;
+
+ reg->val = cpu_to_le64((__u64)val);
+ }
+
+ return 0;
+}
+
+static int vidioc_s_register(struct file *file, void *priv,
+ struct v4l2_register *reg)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ u64 buf;
+
+ buf = le64_to_cpu((__u64)reg->val);
+
+ return em28xx_write_regs(dev, reg->reg, (char *)&buf,
+ em28xx_reg_len(reg->reg));
+}
+#endif
+
+
static int vidioc_cropcap(struct file *file, void *priv,
struct v4l2_cropcap *cc)
{
@@ -1295,8 +1352,6 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
filp->private_data = fh;
if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
- em28xx_set_alternate(dev);
-
dev->width = norm_maxw(dev);
dev->height = norm_maxh(dev);
dev->frame_size = dev->width * dev->height * 2;
@@ -1305,6 +1360,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
dev->hscale = 0;
dev->vscale = 0;
+ em28xx_set_alternate(dev);
em28xx_capture_start(dev, 1);
em28xx_resolution_set(dev);
@@ -1730,6 +1786,10 @@ static const struct video_device em28xx_video_template = {
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
.tvnorms = V4L2_STD_ALL,
.current_norm = V4L2_STD_PAL,
@@ -1752,6 +1812,10 @@ static struct video_device em28xx_radio_template = {
.vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
};
/******************************** usb interface *****************************************/
@@ -1796,10 +1860,10 @@ void em28xx_unregister_extension(struct em28xx_ops *ops)
}
EXPORT_SYMBOL(em28xx_unregister_extension);
-struct video_device *em28xx_vdev_init(struct em28xx *dev,
- const struct video_device *template,
- const int type,
- const char *type_name)
+static struct video_device *em28xx_vdev_init(struct em28xx *dev,
+ const struct video_device *template,
+ const int type,
+ const char *type_name)
{
struct video_device *vfd;
@@ -2064,6 +2128,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,
snprintf(dev->name, 29, "em28xx #%d", nr);
dev->devno = nr;
dev->model = id->driver_info;
+ dev->alt = -1;
/* Checks if audio is provided by some interface */
for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index f3bad0c1c517..04e0e48ecabe 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -33,7 +33,7 @@
#define UNSET -1
/* maximum number of em28xx boards */
-#define EM28XX_MAXBOARDS 1 /*FIXME: should be bigger */
+#define EM28XX_MAXBOARDS 4 /*FIXME: should be bigger */
/* maximum number of frames that can be queued */
#define EM28XX_NUM_FRAMES 5
@@ -345,9 +345,6 @@ int em28xx_read_reg(struct em28xx *dev, u16 reg);
int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
int len);
int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len);
-int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
- u8 bitmask);
-int em28xx_set_audio_source(struct em28xx *dev);
int em28xx_audio_analog_set(struct em28xx *dev);
int em28xx_colorlevels_set_default(struct em28xx *dev);
OpenPOWER on IntegriCloud