summaryrefslogtreecommitdiff
path: root/linux/drivers/media/video
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@infradead.org>2007-12-07 18:47:38 -0200
committerMauro Carvalho Chehab <mchehab@infradead.org>2007-12-07 18:47:38 -0200
commit548e138c20f2155b8ffecde6bddcc2d591f1688f (patch)
tree6406d15182cb6f92fbf74ca5ec0487dc68cbcda3 /linux/drivers/media/video
parentea7f3e04e73a899fbd3ffb74418fbee30b9abc6c (diff)
parent47afcf131805c1773242a182777117da38956ffc (diff)
downloadmediapointer-dvb-s2-548e138c20f2155b8ffecde6bddcc2d591f1688f.tar.gz
mediapointer-dvb-s2-548e138c20f2155b8ffecde6bddcc2d591f1688f.tar.bz2
merge: http://www.linuxtv.org/hg/~hverkuil/v4l-dvb-cleanups
From: Mauro Carvalho Chehab <mchehab@infradead.org> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'linux/drivers/media/video')
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-driver.c15
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-driver.h23
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-fileops.c52
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-ioctl.c125
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-irq.c89
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-streams.c17
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-yuv.c1094
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-yuv.h12
-rw-r--r--linux/drivers/media/video/ivtv/ivtvfb.c4
-rw-r--r--linux/drivers/media/video/pvrusb2/Makefile2
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-audio.c62
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-context.c72
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-context.h5
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c97
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-debug.h39
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c177
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c125
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h121
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c1
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c73
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-encoder.h1
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h80
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c1324
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h147
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c10
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-main.c1
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-std.c7
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c60
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c7
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c55
-rw-r--r--linux/drivers/media/video/saa7134/saa7134-core.c1
-rw-r--r--linux/drivers/media/video/saa7134/saa7134-video.c1500
-rw-r--r--linux/drivers/media/video/saa7134/saa7134.h1
33 files changed, 3097 insertions, 2302 deletions
diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.c b/linux/drivers/media/video/ivtv/ivtv-driver.c
index 0a00c023c..5a5634ee0 100644
--- a/linux/drivers/media/video/ivtv/ivtv-driver.c
+++ b/linux/drivers/media/video/ivtv/ivtv-driver.c
@@ -701,6 +701,7 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv)
atomic_set(&itv->yuv_info.next_dma_frame, -1);
itv->yuv_info.lace_mode = ivtv_yuv_mode;
itv->yuv_info.lace_threshold = ivtv_yuv_threshold;
+ itv->yuv_info.max_frames_buffered = 3;
return 0;
}
@@ -974,7 +975,6 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
const struct pci_device_id *pci_id)
{
int retval = 0;
- int yuv_buf_size;
int vbi_buf_size;
struct ivtv *itv;
@@ -1121,16 +1121,19 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
itv->is_50hz = 1;
itv->is_out_50hz = 1;
}
+
+ itv->yuv_info.osd_full_w = 720;
+ itv->yuv_info.osd_full_h = itv->is_out_50hz ? 576 : 480;
+ itv->yuv_info.v4l2_src_w = itv->yuv_info.osd_full_w;
+ itv->yuv_info.v4l2_src_h = itv->yuv_info.osd_full_h;
+
itv->params.video_gop_size = itv->is_60hz ? 15 : 12;
itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_MPG] = 0x08000;
itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_PCM] = 0x01200;
itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_MPG] = 0x10000;
-
- /* 0x15180 == 720 * 480 / 4, 0x19500 == 720 * 576 / 4 */
- yuv_buf_size = itv->is_60hz ? 0x15180 : 0x19500;
- itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_YUV] = yuv_buf_size / 2;
- itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_YUV] = yuv_buf_size / 8;
+ itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_YUV] = 0x10000;
+ itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_YUV] = 0x08000;
/* Setup VBI Raw Size. Should be big enough to hold PAL.
It is possible to switch between PAL and NTSC, so we need to
diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.h b/linux/drivers/media/video/ivtv/ivtv-driver.h
index b1511cbf4..8b0074624 100644
--- a/linux/drivers/media/video/ivtv/ivtv-driver.h
+++ b/linux/drivers/media/video/ivtv/ivtv-driver.h
@@ -402,6 +402,9 @@ struct yuv_frame_info
u32 tru_h;
u32 offset_y;
s32 lace_mode;
+ u32 sync_field;
+ u32 delay;
+ u32 interlaced;
};
#define IVTV_YUV_MODE_INTERLACED 0x00
@@ -413,6 +416,8 @@ struct yuv_frame_info
#define IVTV_YUV_SYNC_ODD 0x04
#define IVTV_YUV_SYNC_MASK 0x04
+#define IVTV_YUV_BUFFERS 8
+
struct yuv_playback_info
{
u32 reg_2834;
@@ -471,9 +476,10 @@ struct yuv_playback_info
u32 osd_vis_w;
u32 osd_vis_h;
- int decode_height;
+ u32 osd_full_w;
+ u32 osd_full_h;
- int frame_interlaced;
+ int decode_height;
int lace_mode;
int lace_threshold;
@@ -485,16 +491,23 @@ struct yuv_playback_info
u32 yuv_forced_update;
int update_frame;
- int sync_field[4]; /* Field to sync on */
- int field_delay[4]; /* Flag to extend duration of previous frame */
u8 fields_lapsed; /* Counter used when delaying a frame */
- struct yuv_frame_info new_frame_info[4];
+ struct yuv_frame_info new_frame_info[IVTV_YUV_BUFFERS];
struct yuv_frame_info old_frame_info;
struct yuv_frame_info old_frame_info_args;
void *blanking_ptr;
dma_addr_t blanking_dmaptr;
+
+ int stream_size;
+
+ u8 draw_frame; /* PVR350 buffer to draw into */
+ u8 max_frames_buffered; /* Maximum number of frames to buffer */
+
+ struct v4l2_rect main_rect;
+ u32 v4l2_src_w;
+ u32 v4l2_src_h;
};
#define IVTV_VBI_FRAMES 32
diff --git a/linux/drivers/media/video/ivtv/ivtv-fileops.c b/linux/drivers/media/video/ivtv/ivtv-fileops.c
index a200a8a95..6fb96f19a 100644
--- a/linux/drivers/media/video/ivtv/ivtv-fileops.c
+++ b/linux/drivers/media/video/ivtv/ivtv-fileops.c
@@ -542,6 +542,7 @@ ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t c
struct ivtv_open_id *id = filp->private_data;
struct ivtv *itv = id->itv;
struct ivtv_stream *s = &itv->streams[id->type];
+ struct yuv_playback_info *yi = &itv->yuv_info;
struct ivtv_buffer *buf;
struct ivtv_queue q;
int bytes_written = 0;
@@ -580,6 +581,24 @@ ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t c
set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
retry:
+ /* If possible, just DMA the entire frame - Check the data transfer size
+ since we may get here before the stream has been fully set-up */
+ if (mode == OUT_YUV && s->q_full.length == 0 && itv->dma_data_req_size) {
+ while (count >= itv->dma_data_req_size) {
+ if (!ivtv_yuv_udma_stream_frame (itv, (void *)user_buf)) {
+ bytes_written += itv->dma_data_req_size;
+ user_buf += itv->dma_data_req_size;
+ count -= itv->dma_data_req_size;
+ } else {
+ break;
+ }
+ }
+ if (count == 0) {
+ IVTV_DEBUG_HI_FILE("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused);
+ return bytes_written;
+ }
+ }
+
for (;;) {
/* Gather buffers */
while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_io)))
@@ -604,9 +623,16 @@ retry:
/* copy user data into buffers */
while ((buf = ivtv_dequeue(s, &q))) {
- /* Make sure we really got all the user data */
- rc = ivtv_buf_copy_from_user(s, buf, user_buf, count);
+ /* yuv is a pain. Don't copy more data than needed for a single
+ frame, otherwise we lose sync with the incoming stream */
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV &&
+ yi->stream_size + count > itv->dma_data_req_size)
+ rc = ivtv_buf_copy_from_user(s, buf, user_buf,
+ itv->dma_data_req_size - yi->stream_size);
+ else
+ rc = ivtv_buf_copy_from_user(s, buf, user_buf, count);
+ /* Make sure we really got all the user data */
if (rc < 0) {
ivtv_queue_move(s, &q, NULL, &s->q_free, 0);
return rc;
@@ -615,6 +641,16 @@ retry:
count -= rc;
bytes_written += rc;
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV) {
+ yi->stream_size += rc;
+ /* If we have a complete yuv frame, break loop now */
+ if (yi->stream_size == itv->dma_data_req_size) {
+ ivtv_enqueue(s, buf, &s->q_full);
+ yi->stream_size = 0;
+ break;
+ }
+ }
+
if (buf->bytesused != s->buf_size) {
/* incomplete, leave in q_io for next time */
ivtv_enqueue(s, buf, &s->q_io);
@@ -642,6 +678,9 @@ retry:
if (s->q_full.length >= itv->dma_data_req_size) {
int got_sig;
+ if (mode == OUT_YUV)
+ ivtv_yuv_setup_stream_frame(itv);
+
prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
while (!(got_sig = signal_pending(current)) &&
test_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) {
@@ -922,10 +961,15 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp)
}
/* YUV or MPG Decoding Mode? */
- if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+ if (s->type == IVTV_DEC_STREAM_TYPE_MPG) {
clear_bit(IVTV_F_I_DEC_YUV, &itv->i_flags);
- else if (s->type == IVTV_DEC_STREAM_TYPE_YUV)
+ } else if (s->type == IVTV_DEC_STREAM_TYPE_YUV) {
set_bit(IVTV_F_I_DEC_YUV, &itv->i_flags);
+ /* For yuv, we need to know the dma size before we start */
+ itv->dma_data_req_size =
+ 1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31);
+ itv->yuv_info.stream_size = 0;
+ }
return 0;
}
diff --git a/linux/drivers/media/video/ivtv/ivtv-ioctl.c b/linux/drivers/media/video/ivtv/ivtv-ioctl.c
index b01aaa752..39f20dfb8 100644
--- a/linux/drivers/media/video/ivtv/ivtv-ioctl.c
+++ b/linux/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -372,7 +372,7 @@ static int ivtv_get_fmt(struct ivtv *itv, int streamtype, struct v4l2_format *fm
fmt->fmt.pix.height = itv->main_rect.height;
fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
- if (itv->output_mode == OUT_UDMA_YUV) {
+ if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
switch (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) {
case IVTV_YUV_MODE_INTERLACED:
fmt->fmt.pix.field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) ?
@@ -386,14 +386,13 @@ static int ivtv_get_fmt(struct ivtv *itv, int streamtype, struct v4l2_format *fm
break;
}
fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12;
+ fmt->fmt.pix.bytesperline = 720;
+ fmt->fmt.pix.width = itv->yuv_info.v4l2_src_w;
+ fmt->fmt.pix.height = itv->yuv_info.v4l2_src_h;
/* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
fmt->fmt.pix.sizeimage =
- fmt->fmt.pix.height * fmt->fmt.pix.width +
- fmt->fmt.pix.height * (fmt->fmt.pix.width / 2);
- }
- else if (itv->output_mode == OUT_YUV ||
- streamtype == IVTV_ENC_STREAM_TYPE_YUV ||
- streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+ 1080 * ((fmt->fmt.pix.height + 31) & ~31);
+ } else if (streamtype == IVTV_ENC_STREAM_TYPE_YUV) {
fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12;
/* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
fmt->fmt.pix.sizeimage =
@@ -490,6 +489,7 @@ static int ivtv_get_fmt(struct ivtv *itv, int streamtype, struct v4l2_format *fm
static int ivtv_try_or_set_fmt(struct ivtv *itv, int streamtype,
struct v4l2_format *fmt, int set_fmt)
{
+ struct yuv_playback_info *yi = &itv->yuv_info;
struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
u16 set;
@@ -505,39 +505,52 @@ static int ivtv_try_or_set_fmt(struct ivtv *itv, int streamtype,
r.width = fmt->fmt.pix.width;
r.height = fmt->fmt.pix.height;
ivtv_get_fmt(itv, streamtype, fmt);
- if (itv->output_mode != OUT_UDMA_YUV) {
- /* TODO: would setting the rect also be valid for this mode? */
- fmt->fmt.pix.width = r.width;
- fmt->fmt.pix.height = r.height;
- }
- if (itv->output_mode == OUT_UDMA_YUV) {
- /* TODO: add checks for validity */
+ fmt->fmt.pix.width = r.width;
+ fmt->fmt.pix.height = r.height;
+ if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
fmt->fmt.pix.field = field;
+ if (fmt->fmt.pix.width < 2)
+ fmt->fmt.pix.width = 2;
+ if (fmt->fmt.pix.width > 720)
+ fmt->fmt.pix.width = 720;
+ if (fmt->fmt.pix.height < 2)
+ fmt->fmt.pix.height = 2;
+ if (fmt->fmt.pix.height > 576)
+ fmt->fmt.pix.height = 576;
}
- if (set_fmt) {
- if (itv->output_mode == OUT_UDMA_YUV) {
- switch (field) {
- case V4L2_FIELD_NONE:
- itv->yuv_info.lace_mode = IVTV_YUV_MODE_PROGRESSIVE;
- break;
- case V4L2_FIELD_ANY:
- itv->yuv_info.lace_mode = IVTV_YUV_MODE_AUTO;
- break;
- case V4L2_FIELD_INTERLACED_BT:
- itv->yuv_info.lace_mode =
- IVTV_YUV_MODE_INTERLACED|IVTV_YUV_SYNC_ODD;
- break;
- case V4L2_FIELD_INTERLACED_TB:
- default:
- itv->yuv_info.lace_mode = IVTV_YUV_MODE_INTERLACED;
- break;
- }
- itv->yuv_info.lace_sync_field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) == IVTV_YUV_SYNC_EVEN ? 0 : 1;
+ if (set_fmt && streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+ /* Return now if we already have some frame data */
+ if (yi->stream_size)
+ return -EBUSY;
- /* Force update of yuv registers */
- itv->yuv_info.yuv_forced_update = 1;
- return 0;
+ yi->v4l2_src_w = r.width;
+ yi->v4l2_src_h = r.height;
+
+ switch (field) {
+ case V4L2_FIELD_NONE:
+ yi->lace_mode = IVTV_YUV_MODE_PROGRESSIVE;
+ break;
+ case V4L2_FIELD_ANY:
+ yi->lace_mode = IVTV_YUV_MODE_AUTO;
+ break;
+ case V4L2_FIELD_INTERLACED_BT:
+ yi->lace_mode =
+ IVTV_YUV_MODE_INTERLACED|IVTV_YUV_SYNC_ODD;
+ break;
+ case V4L2_FIELD_INTERLACED_TB:
+ default:
+ yi->lace_mode = IVTV_YUV_MODE_INTERLACED;
+ break;
}
+ yi->lace_sync_field = (yi->lace_mode & IVTV_YUV_SYNC_MASK) == IVTV_YUV_SYNC_EVEN ? 0 : 1;
+
+ if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags))
+ itv->dma_data_req_size =
+ 1080 * ((yi->v4l2_src_h + 31) & ~31);
+
+ /* Force update of yuv registers */
+ yi->yuv_forced_update = 1;
+ return 0;
}
return 0;
}
@@ -666,11 +679,8 @@ static int ivtv_debug_ioctls(struct file *filp, unsigned int cmd, void *arg)
chip->ident = V4L2_IDENT_NONE;
chip->revision = 0;
if (reg->match_type == V4L2_CHIP_MATCH_HOST) {
- if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) {
- struct v4l2_chip_ident *chip = arg;
-
+ if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416;
- }
return 0;
}
if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
@@ -709,8 +719,12 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void
{
struct ivtv_open_id *id = NULL;
u32 data[CX2341X_MBOX_MAX_DATA];
+ int streamtype = 0;
- if (filp) id = (struct ivtv_open_id *)filp->private_data;
+ if (filp) {
+ id = (struct ivtv_open_id *)filp->private_data;
+ streamtype = id->type;
+ }
switch (cmd) {
case VIDIOC_G_PRIORITY:
@@ -828,6 +842,11 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void
cropcap->bounds.height = itv->is_50hz ? 576 : 480;
cropcap->pixelaspect.numerator = itv->is_50hz ? 59 : 10;
cropcap->pixelaspect.denominator = itv->is_50hz ? 54 : 11;
+ } else if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+ cropcap->bounds.width = itv->yuv_info.osd_full_w;
+ cropcap->bounds.height = itv->yuv_info.osd_full_h;
+ cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10;
+ cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11;
} else {
cropcap->bounds.height = itv->is_out_50hz ? 576 : 480;
cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10;
@@ -842,10 +861,15 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void
if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
- if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
- crop->c.width, crop->c.height, crop->c.left, crop->c.top)) {
- itv->main_rect = crop->c;
+ if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+ itv->yuv_info.main_rect = crop->c;
return 0;
+ } else {
+ if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
+ crop->c.width, crop->c.height, crop->c.left, crop->c.top)) {
+ itv->main_rect = crop->c;
+ return 0;
+ }
}
return -EINVAL;
}
@@ -859,7 +883,10 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void
if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
- crop->c = itv->main_rect;
+ if (streamtype == IVTV_DEC_STREAM_TYPE_YUV)
+ crop->c = itv->yuv_info.main_rect;
+ else
+ crop->c = itv->main_rect;
return 0;
}
if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -870,7 +897,7 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void
case VIDIOC_ENUM_FMT: {
static struct v4l2_fmtdesc formats[] = {
{ 0, 0, 0,
- "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12,
+ "HM12 (YUV 4:2:0)", V4L2_PIX_FMT_HM12,
{ 0, 0, 0, 0 }
},
{ 1, 0, V4L2_FMT_FLAG_COMPRESSED,
@@ -1049,6 +1076,12 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void
itv->main_rect.height = itv->params.height;
ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
720, itv->main_rect.height, 0, 0);
+ itv->yuv_info.main_rect = itv->main_rect;
+ if (!itv->osd_info) {
+ itv->yuv_info.osd_full_w = 720;
+ itv->yuv_info.osd_full_h =
+ itv->is_out_50hz ? 576 : 480;
+ }
}
break;
}
diff --git a/linux/drivers/media/video/ivtv/ivtv-irq.c b/linux/drivers/media/video/ivtv/ivtv-irq.c
index e69cb5263..42f9b3402 100644
--- a/linux/drivers/media/video/ivtv/ivtv-irq.c
+++ b/linux/drivers/media/video/ivtv/ivtv-irq.c
@@ -308,8 +308,11 @@ static void dma_post(struct ivtv_stream *s)
void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock)
{
struct ivtv *itv = s->itv;
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ u8 frame = yi->draw_frame;
+ struct yuv_frame_info *f = &yi->new_frame_info[frame];
struct ivtv_buffer *buf;
- u32 y_size = itv->params.height * itv->params.width;
+ u32 y_size = 720 * ((f->src_h + 31) & ~31);
u32 uv_offset = offset + IVTV_YUV_BUFFER_UV_OFFSET;
int y_done = 0;
int bytes_written = 0;
@@ -317,17 +320,42 @@ void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock)
int idx = 0;
IVTV_DEBUG_HI_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset);
+
+ /* Insert buffer block for YUV if needed */
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV && f->offset_y) {
+ if (yi->blanking_dmaptr) {
+ s->sg_pending[idx].src = yi->blanking_dmaptr;
+ s->sg_pending[idx].dst = offset;
+ s->sg_pending[idx].size = 720 * 16;
+ }
+ offset += 720 * 16;
+ idx++;
+ }
+
list_for_each_entry(buf, &s->q_predma.list, list) {
/* YUV UV Offset from Y Buffer */
- if (s->type == IVTV_DEC_STREAM_TYPE_YUV && !y_done && bytes_written >= y_size) {
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV && !y_done &&
+ (bytes_written + buf->bytesused) >= y_size) {
+ s->sg_pending[idx].src = buf->dma_handle;
+ s->sg_pending[idx].dst = offset;
+ s->sg_pending[idx].size = y_size - bytes_written;
offset = uv_offset;
+ if (s->sg_pending[idx].size != buf->bytesused) {
+ idx++;
+ s->sg_pending[idx].src =
+ buf->dma_handle + s->sg_pending[idx - 1].size;
+ s->sg_pending[idx].dst = offset;
+ s->sg_pending[idx].size =
+ buf->bytesused - s->sg_pending[idx - 1].size;
+ offset += s->sg_pending[idx].size;
+ }
y_done = 1;
+ } else {
+ s->sg_pending[idx].src = buf->dma_handle;
+ s->sg_pending[idx].dst = offset;
+ s->sg_pending[idx].size = buf->bytesused;
+ offset += buf->bytesused;
}
- s->sg_pending[idx].src = buf->dma_handle;
- s->sg_pending[idx].dst = offset;
- s->sg_pending[idx].size = buf->bytesused;
-
- offset += buf->bytesused;
bytes_written += buf->bytesused;
/* Sync SG buffers */
@@ -706,8 +734,11 @@ static void ivtv_irq_dec_data_req(struct ivtv *itv)
ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data);
if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) {
- itv->dma_data_req_size = itv->params.width * itv->params.height * 3 / 2;
- itv->dma_data_req_offset = data[1] ? data[1] : yuv_offset[0];
+ itv->dma_data_req_size =
+ 1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31);
+ itv->dma_data_req_offset = data[1];
+ if (atomic_read(&itv->yuv_info.next_dma_frame) >= 0)
+ ivtv_yuv_frame_complete(itv);
s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
}
else {
@@ -721,6 +752,8 @@ static void ivtv_irq_dec_data_req(struct ivtv *itv)
set_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
}
else {
+ if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags))
+ ivtv_yuv_setup_stream_frame(itv);
clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size);
ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 0);
@@ -737,24 +770,26 @@ static void ivtv_irq_vsync(struct ivtv *itv)
* one vsync per frame.
*/
unsigned int frame = read_reg(0x28c0) & 1;
+ struct yuv_playback_info *yi = &itv->yuv_info;
int last_dma_frame = atomic_read(&itv->yuv_info.next_dma_frame);
+ struct yuv_frame_info *f = &yi->new_frame_info[last_dma_frame];
if (0) IVTV_DEBUG_IRQ("DEC VSYNC\n");
- if (((frame ^ itv->yuv_info.sync_field[last_dma_frame]) == 0 &&
- ((itv->last_vsync_field & 1) ^ itv->yuv_info.sync_field[last_dma_frame])) ||
- (frame != (itv->last_vsync_field & 1) && !itv->yuv_info.frame_interlaced)) {
+ if (((frame ^ f->sync_field) == 0 &&
+ ((itv->last_vsync_field & 1) ^ f->sync_field)) ||
+ (frame != (itv->last_vsync_field & 1) && !f->interlaced)) {
int next_dma_frame = last_dma_frame;
- if (!(itv->yuv_info.frame_interlaced && itv->yuv_info.field_delay[next_dma_frame] && itv->yuv_info.fields_lapsed < 1)) {
- if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&itv->yuv_info.next_fill_frame)) {
+ if (!(f->interlaced && f->delay && yi->fields_lapsed < 1)) {
+ if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&yi->next_fill_frame)) {
write_reg(yuv_offset[next_dma_frame] >> 4, 0x82c);
write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830);
write_reg(yuv_offset[next_dma_frame] >> 4, 0x834);
write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838);
- next_dma_frame = (next_dma_frame + 1) & 0x3;
- atomic_set(&itv->yuv_info.next_dma_frame, next_dma_frame);
- itv->yuv_info.fields_lapsed = -1;
+ next_dma_frame = (next_dma_frame + 1) % IVTV_YUV_BUFFERS;
+ atomic_set(&yi->next_dma_frame, next_dma_frame);
+ yi->fields_lapsed = -1;
}
}
}
@@ -787,20 +822,22 @@ static void ivtv_irq_vsync(struct ivtv *itv)
}
/* Check if we need to update the yuv registers */
- if ((itv->yuv_info.yuv_forced_update || itv->yuv_info.new_frame_info[last_dma_frame].update) && last_dma_frame != -1) {
- if (!itv->yuv_info.new_frame_info[last_dma_frame].update)
- last_dma_frame = (last_dma_frame - 1) & 3;
-
- if (itv->yuv_info.new_frame_info[last_dma_frame].src_w) {
- itv->yuv_info.update_frame = last_dma_frame;
- itv->yuv_info.new_frame_info[last_dma_frame].update = 0;
- itv->yuv_info.yuv_forced_update = 0;
+ if ((yi->yuv_forced_update || f->update) && last_dma_frame != -1) {
+ if (!f->update) {
+ last_dma_frame = (u8)(last_dma_frame - 1) % IVTV_YUV_BUFFERS;
+ f = &yi->new_frame_info[last_dma_frame];
+ }
+
+ if (f->src_w) {
+ yi->update_frame = last_dma_frame;
+ f->update = 0;
+ yi->yuv_forced_update = 0;
set_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags);
set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
}
}
- itv->yuv_info.fields_lapsed ++;
+ yi->fields_lapsed++;
}
}
diff --git a/linux/drivers/media/video/ivtv/ivtv-streams.c b/linux/drivers/media/video/ivtv/ivtv-streams.c
index a5bfbd98a..3ca2a1a62 100644
--- a/linux/drivers/media/video/ivtv/ivtv-streams.c
+++ b/linux/drivers/media/video/ivtv/ivtv-streams.c
@@ -661,27 +661,12 @@ int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset)
IVTV_DEBUG_INFO("Starting decode stream %s (gop_offset %d)\n", s->name, gop_offset);
- /* Clear Streamoff */
- if (s->type == IVTV_DEC_STREAM_TYPE_YUV) {
- /* Initialize Decoder */
- /* Reprogram Decoder YUV Buffers for YUV */
- write_reg(yuv_offset[0] >> 4, 0x82c);
- write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830);
- write_reg(yuv_offset[0] >> 4, 0x834);
- write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838);
-
- write_reg_sync(0x00000000 | (0x0c << 16) | (0x0b << 8), 0x2d24);
-
- write_reg_sync(0x00108080, 0x2898);
- /* Enable YUV decoder output */
- write_reg_sync(0x01, IVTV_REG_VDM);
- }
-
ivtv_setup_v4l2_decode_stream(s);
/* set dma size to 65536 bytes */
ivtv_vapi(itv, CX2341X_DEC_SET_DMA_BLOCK_SIZE, 1, 65536);
+ /* Clear Streamoff */
clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
/* Zero out decoder counters */
diff --git a/linux/drivers/media/video/ivtv/ivtv-yuv.c b/linux/drivers/media/video/ivtv/ivtv-yuv.c
index 9091c4837..85183480a 100644
--- a/linux/drivers/media/video/ivtv/ivtv-yuv.c
+++ b/linux/drivers/media/video/ivtv/ivtv-yuv.c
@@ -22,32 +22,37 @@
#include "ivtv-udma.h"
#include "ivtv-yuv.h"
-const u32 yuv_offset[4] = {
- IVTV_YUV_BUFFER_OFFSET,
- IVTV_YUV_BUFFER_OFFSET_1,
- IVTV_YUV_BUFFER_OFFSET_2,
- IVTV_YUV_BUFFER_OFFSET_3
+/* YUV buffer offsets */
+const u32 yuv_offset[IVTV_YUV_BUFFERS] = {
+ 0x001a8600,
+ 0x00240400,
+ 0x002d8200,
+ 0x00370000,
+ 0x00029000,
+ 0x000C0E00,
+ 0x006B0400,
+ 0x00748200
};
static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
- struct ivtv_dma_frame *args)
+ struct ivtv_dma_frame *args)
{
struct ivtv_dma_page_info y_dma;
struct ivtv_dma_page_info uv_dma;
-
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ u8 frame = yi->draw_frame;
+ struct yuv_frame_info *f = &yi->new_frame_info[frame];
int i;
int y_pages, uv_pages;
-
unsigned long y_buffer_offset, uv_buffer_offset;
int y_decode_height, uv_decode_height, y_size;
- int frame = atomic_read(&itv->yuv_info.next_fill_frame);
y_buffer_offset = IVTV_DECODER_OFFSET + yuv_offset[frame];
uv_buffer_offset = y_buffer_offset + IVTV_YUV_BUFFER_UV_OFFSET;
- y_decode_height = uv_decode_height = args->src.height + args->src.top;
+ y_decode_height = uv_decode_height = f->src_h + f->src_y;
- if (y_decode_height < 512-16)
+ if (f->offset_y)
y_buffer_offset += 720 * 16;
if (y_decode_height & 15)
@@ -60,8 +65,9 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
/* Still in USE */
if (dma->SG_length || dma->page_count) {
- IVTV_DEBUG_WARN("prep_user_dma: SG_length %d page_count %d still full?\n",
- dma->SG_length, dma->page_count);
+ IVTV_DEBUG_WARN
+ ("prep_user_dma: SG_length %d page_count %d still full?\n",
+ dma->SG_length, dma->page_count);
return -EBUSY;
}
@@ -77,8 +83,9 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
dma->page_count = y_dma.page_count + uv_dma.page_count;
if (y_pages + uv_pages != dma->page_count) {
- IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n",
- y_pages + uv_pages, dma->page_count);
+ IVTV_DEBUG_WARN
+ ("failed to map user pages, returned %d instead of %d\n",
+ y_pages + uv_pages, dma->page_count);
for (i = 0; i < dma->page_count; i++) {
put_page(dma->map[i]);
@@ -99,16 +106,14 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
/* Fill SG Array with new values */
- ivtv_udma_fill_sg_array (dma, y_buffer_offset, uv_buffer_offset, y_size);
+ ivtv_udma_fill_sg_array(dma, y_buffer_offset, uv_buffer_offset, y_size);
/* If we've offset the y plane, ensure top area is blanked */
- if (args->src.height + args->src.top < 512-16) {
- if (itv->yuv_info.blanking_dmaptr) {
- dma->SGarray[dma->SG_length].size = cpu_to_le32(720*16);
- dma->SGarray[dma->SG_length].src = cpu_to_le32(itv->yuv_info.blanking_dmaptr);
- dma->SGarray[dma->SG_length].dst = cpu_to_le32(IVTV_DECODER_OFFSET + yuv_offset[frame]);
- dma->SG_length++;
- }
+ if (f->offset_y && yi->blanking_dmaptr) {
+ dma->SGarray[dma->SG_length].size = cpu_to_le32(720*16);
+ dma->SGarray[dma->SG_length].src = cpu_to_le32(yi->blanking_dmaptr);
+ dma->SGarray[dma->SG_length].dst = cpu_to_le32(IVTV_DECODER_OFFSET + yuv_offset[frame]);
+ dma->SG_length++;
}
/* Tag SG Array with Interrupt Bit */
@@ -121,11 +126,11 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
/* We rely on a table held in the firmware - Quick check. */
int ivtv_yuv_filter_check(struct ivtv *itv)
{
- int i, offset_y, offset_uv;
+ int i, y, uv;
- for (i=0, offset_y = 16, offset_uv = 4; i<16; i++, offset_y += 24, offset_uv += 12) {
- if ((read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + offset_y) != i << 16) ||
- (read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + offset_uv) != i << 16)) {
+ for (i = 0, y = 16, uv = 4; i < 16; i++, y += 24, uv += 12) {
+ if ((read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + y) != i << 16) ||
+ (read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + uv) != i << 16)) {
IVTV_WARN ("YUV filter table not found in firmware.\n");
return -1;
}
@@ -135,69 +140,67 @@ int ivtv_yuv_filter_check(struct ivtv *itv)
static void ivtv_yuv_filter(struct ivtv *itv, int h_filter, int v_filter_1, int v_filter_2)
{
- int filter_index, filter_line;
+ u32 i, line;
/* If any filter is -1, then don't update it */
if (h_filter > -1) {
- if (h_filter > 4) h_filter = 4;
- filter_index = h_filter * 384;
- filter_line = 0;
- while (filter_line < 16) {
- write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02804);
- write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0281c);
- filter_index += 4;
- write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02808);
- write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02820);
- filter_index += 4;
- write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0280c);
- write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02824);
- filter_index += 4;
- write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02810);
- write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02828);
- filter_index += 4;
- write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02814);
- write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0282c);
- filter_index += 8;
+ if (h_filter > 4)
+ h_filter = 4;
+ i = IVTV_YUV_HORIZONTAL_FILTER_OFFSET + (h_filter * 384);
+ for (line = 0; line < 16; line++) {
+ write_reg(read_dec(i), 0x02804);
+ write_reg(read_dec(i), 0x0281c);
+ i += 4;
+ write_reg(read_dec(i), 0x02808);
+ write_reg(read_dec(i), 0x02820);
+ i += 4;
+ write_reg(read_dec(i), 0x0280c);
+ write_reg(read_dec(i), 0x02824);
+ i += 4;
+ write_reg(read_dec(i), 0x02810);
+ write_reg(read_dec(i), 0x02828);
+ i += 4;
+ write_reg(read_dec(i), 0x02814);
+ write_reg(read_dec(i), 0x0282c);
+ i += 8;
write_reg(0, 0x02818);
write_reg(0, 0x02830);
- filter_line ++;
}
- IVTV_DEBUG_YUV("h_filter -> %d\n",h_filter);
+ IVTV_DEBUG_YUV("h_filter -> %d\n", h_filter);
}
if (v_filter_1 > -1) {
- if (v_filter_1 > 4) v_filter_1 = 4;
- filter_index = v_filter_1 * 192;
- filter_line = 0;
- while (filter_line < 16) {
- write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02900);
- filter_index += 4;
- write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02904);
- filter_index += 8;
+ if (v_filter_1 > 4)
+ v_filter_1 = 4;
+ i = IVTV_YUV_VERTICAL_FILTER_OFFSET + (v_filter_1 * 192);
+ for (line = 0; line < 16; line++) {
+ write_reg(read_dec(i), 0x02900);
+ i += 4;
+ write_reg(read_dec(i), 0x02904);
+ i += 8;
write_reg(0, 0x02908);
- filter_line ++;
}
- IVTV_DEBUG_YUV("v_filter_1 -> %d\n",v_filter_1);
+ IVTV_DEBUG_YUV("v_filter_1 -> %d\n", v_filter_1);
}
if (v_filter_2 > -1) {
- if (v_filter_2 > 4) v_filter_2 = 4;
- filter_index = v_filter_2 * 192;
- filter_line = 0;
- while (filter_line < 16) {
- write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x0290c);
- filter_index += 4;
- write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02910);
- filter_index += 8;
+ if (v_filter_2 > 4)
+ v_filter_2 = 4;
+ i = IVTV_YUV_VERTICAL_FILTER_OFFSET + (v_filter_2 * 192);
+ for (line = 0; line < 16; line++) {
+ write_reg(read_dec(i), 0x0290c);
+ i += 4;
+ write_reg(read_dec(i), 0x02910);
+ i += 8;
write_reg(0, 0x02914);
- filter_line ++;
}
- IVTV_DEBUG_YUV("v_filter_2 -> %d\n",v_filter_2);
+ IVTV_DEBUG_YUV("v_filter_2 -> %d\n", v_filter_2);
}
}
-static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *window)
+static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *f)
{
+ struct yuv_playback_info *yi = &itv->yuv_info;
u32 reg_2834, reg_2838, reg_283c;
u32 reg_2844, reg_2854, reg_285c;
u32 reg_2864, reg_2874, reg_2890;
@@ -206,18 +209,19 @@ static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *
int h_filter;
u32 master_width;
- IVTV_DEBUG_WARN( "Need to adjust to width %d src_w %d dst_w %d src_x %d dst_x %d\n",
- window->tru_w, window->src_w, window->dst_w,window->src_x, window->dst_x);
+ IVTV_DEBUG_WARN
+ ("Adjust to width %d src_w %d dst_w %d src_x %d dst_x %d\n",
+ f->tru_w, f->src_w, f->dst_w, f->src_x, f->dst_x);
/* How wide is the src image */
- x_cutoff = window->src_w + window->src_x;
+ x_cutoff = f->src_w + f->src_x;
/* Set the display width */
- reg_2834 = window->dst_w;
+ reg_2834 = f->dst_w;
reg_2838 = reg_2834;
/* Set the display position */
- reg_2890 = window->dst_x;
+ reg_2890 = f->dst_x;
/* Index into the image horizontally */
reg_2870 = 0;
@@ -228,32 +232,31 @@ static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *
Gradually adjust the offset to avoid the video 'snapping'
left/right if it gets dragged through this region.
Only do this if osd is full width. */
- if (window->vis_w == 720) {
- if ((window->tru_x - window->pan_x > -1) && (window->tru_x - window->pan_x <= 40) && (window->dst_w >= 680)){
- reg_2870 = 10 - (window->tru_x - window->pan_x) / 4;
- }
- else if ((window->tru_x - window->pan_x < 0) && (window->tru_x - window->pan_x >= -20) && (window->dst_w >= 660)) {
- reg_2870 = (10 + (window->tru_x - window->pan_x) / 2);
- }
+ if (f->vis_w == 720) {
+ if ((f->tru_x - f->pan_x > -1) && (f->tru_x - f->pan_x <= 40) && (f->dst_w >= 680))
+ reg_2870 = 10 - (f->tru_x - f->pan_x) / 4;
+ else if ((f->tru_x - f->pan_x < 0) && (f->tru_x - f->pan_x >= -20) && (f->dst_w >= 660))
+ reg_2870 = (10 + (f->tru_x - f->pan_x) / 2);
- if (window->dst_w >= window->src_w)
+ if (f->dst_w >= f->src_w)
reg_2870 = reg_2870 << 16 | reg_2870;
else
reg_2870 = ((reg_2870 & ~1) << 15) | (reg_2870 & ~1);
}
- if (window->dst_w < window->src_w)
+ if (f->dst_w < f->src_w)
reg_2870 = 0x000d000e - reg_2870;
else
reg_2870 = 0x0012000e - reg_2870;
/* We're also using 2870 to shift the image left (src_x & negative dst_x) */
- reg_2870_offset = (window->src_x*((window->dst_w << 21)/window->src_w))>>19;
+ reg_2870_offset = (f->src_x * ((f->dst_w << 21) / f->src_w)) >> 19;
- if (window->dst_w >= window->src_w) {
+ if (f->dst_w >= f->src_w) {
x_cutoff &= ~1;
- master_width = (window->src_w * 0x00200000) / (window->dst_w);
- if (master_width * window->dst_w != window->src_w * 0x00200000) master_width ++;
+ master_width = (f->src_w * 0x00200000) / (f->dst_w);
+ if (master_width * f->dst_w != f->src_w * 0x00200000)
+ master_width++;
reg_2834 = (reg_2834 << 16) | x_cutoff;
reg_2838 = (reg_2838 << 16) | x_cutoff;
reg_283c = master_width >> 2;
@@ -264,17 +267,17 @@ static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *
/* We also need to factor in the scaling
(src_w - dst_w) / (src_w / 4) */
- if (window->dst_w > window->src_w)
- reg_2870_base = ((window->dst_w - window->src_w)<<16) / (window->src_w <<14);
+ if (f->dst_w > f->src_w)
+ reg_2870_base = ((f->dst_w - f->src_w)<<16) / (f->src_w <<14);
else
reg_2870_base = 0;
reg_2870 += (((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 2) + (reg_2870_base << 17 | reg_2870_base);
reg_2874 = 0;
- }
- else if (window->dst_w < window->src_w / 2) {
- master_width = (window->src_w * 0x00080000) / window->dst_w;
- if (master_width * window->dst_w != window->src_w * 0x00080000) master_width ++;
+ } else if (f->dst_w < f->src_w / 2) {
+ master_width = (f->src_w * 0x00080000) / f->dst_w;
+ if (master_width * f->dst_w != f->src_w * 0x00080000)
+ master_width++;
reg_2834 = (reg_2834 << 16) | x_cutoff;
reg_2838 = (reg_2838 << 16) | x_cutoff;
reg_283c = master_width >> 2;
@@ -282,13 +285,13 @@ static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *
reg_2854 = master_width;
reg_285c = master_width >> 1;
reg_2864 = master_width >> 1;
- reg_2870 += (((reg_2870_offset << 15) & 0xFFFF0000) | reg_2870_offset);
- reg_2870 += (5 - (((window->src_w + window->src_w / 2) - 1) / window->dst_w)) << 16;
+ reg_2870 += ((reg_2870_offset << 15) & 0xFFFF0000) | reg_2870_offset;
+ reg_2870 += (5 - (((f->src_w + f->src_w / 2) - 1) / f->dst_w)) << 16;
reg_2874 = 0x00000012;
- }
- else {
- master_width = (window->src_w * 0x00100000) / window->dst_w;
- if (master_width * window->dst_w != window->src_w * 0x00100000) master_width ++;
+ } else {
+ master_width = (f->src_w * 0x00100000) / f->dst_w;
+ if (master_width * f->dst_w != f->src_w * 0x00100000)
+ master_width++;
reg_2834 = (reg_2834 << 16) | x_cutoff;
reg_2838 = (reg_2838 << 16) | x_cutoff;
reg_283c = master_width >> 2;
@@ -296,62 +299,70 @@ static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *
reg_2854 = master_width;
reg_285c = master_width >> 1;
reg_2864 = master_width >> 1;
- reg_2870 += (((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 1);
- reg_2870 += (5 - (((window->src_w * 3) - 1) / window->dst_w)) << 16;
+ reg_2870 += ((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 1;
+ reg_2870 += (5 - (((f->src_w * 3) - 1) / f->dst_w)) << 16;
reg_2874 = 0x00000001;
}
/* Select the horizontal filter */
- if (window->src_w == window->dst_w) {
+ if (f->src_w == f->dst_w) {
/* An exact size match uses filter 0 */
h_filter = 0;
- }
- else {
+ } else {
/* Figure out which filter to use */
- h_filter = ((window->src_w << 16) / window->dst_w) >> 15;
+ h_filter = ((f->src_w << 16) / f->dst_w) >> 15;
h_filter = (h_filter >> 1) + (h_filter & 1);
/* Only an exact size match can use filter 0 */
- if (h_filter == 0) h_filter = 1;
+ h_filter += !h_filter;
}
write_reg(reg_2834, 0x02834);
write_reg(reg_2838, 0x02838);
- IVTV_DEBUG_YUV("Update reg 0x2834 %08x->%08x 0x2838 %08x->%08x\n",itv->yuv_info.reg_2834, reg_2834, itv->yuv_info.reg_2838, reg_2838);
+ IVTV_DEBUG_YUV("Update reg 0x2834 %08x->%08x 0x2838 %08x->%08x\n",
+ yi->reg_2834, reg_2834, yi->reg_2838, reg_2838);
write_reg(reg_283c, 0x0283c);
write_reg(reg_2844, 0x02844);
- IVTV_DEBUG_YUV("Update reg 0x283c %08x->%08x 0x2844 %08x->%08x\n",itv->yuv_info.reg_283c, reg_283c, itv->yuv_info.reg_2844, reg_2844);
+ IVTV_DEBUG_YUV("Update reg 0x283c %08x->%08x 0x2844 %08x->%08x\n",
+ yi->reg_283c, reg_283c, yi->reg_2844, reg_2844);
write_reg(0x00080514, 0x02840);
write_reg(0x00100514, 0x02848);
- IVTV_DEBUG_YUV("Update reg 0x2840 %08x->%08x 0x2848 %08x->%08x\n",itv->yuv_info.reg_2840, 0x00080514, itv->yuv_info.reg_2848, 0x00100514);
+ IVTV_DEBUG_YUV("Update reg 0x2840 %08x->%08x 0x2848 %08x->%08x\n",
+ yi->reg_2840, 0x00080514, yi->reg_2848, 0x00100514);
write_reg(reg_2854, 0x02854);
- IVTV_DEBUG_YUV("Update reg 0x2854 %08x->%08x \n",itv->yuv_info.reg_2854, reg_2854);
+ IVTV_DEBUG_YUV("Update reg 0x2854 %08x->%08x \n",
+ yi->reg_2854, reg_2854);
write_reg(reg_285c, 0x0285c);
write_reg(reg_2864, 0x02864);
- IVTV_DEBUG_YUV("Update reg 0x285c %08x->%08x 0x2864 %08x->%08x\n",itv->yuv_info.reg_285c, reg_285c, itv->yuv_info.reg_2864, reg_2864);
+ IVTV_DEBUG_YUV("Update reg 0x285c %08x->%08x 0x2864 %08x->%08x\n",
+ yi->reg_285c, reg_285c, yi->reg_2864, reg_2864);
write_reg(reg_2874, 0x02874);
- IVTV_DEBUG_YUV("Update reg 0x2874 %08x->%08x\n",itv->yuv_info.reg_2874, reg_2874);
+ IVTV_DEBUG_YUV("Update reg 0x2874 %08x->%08x\n",
+ yi->reg_2874, reg_2874);
write_reg(reg_2870, 0x02870);
- IVTV_DEBUG_YUV("Update reg 0x2870 %08x->%08x\n",itv->yuv_info.reg_2870, reg_2870);
+ IVTV_DEBUG_YUV("Update reg 0x2870 %08x->%08x\n",
+ yi->reg_2870, reg_2870);
- write_reg( reg_2890,0x02890);
- IVTV_DEBUG_YUV("Update reg 0x2890 %08x->%08x\n",itv->yuv_info.reg_2890, reg_2890);
+ write_reg(reg_2890, 0x02890);
+ IVTV_DEBUG_YUV("Update reg 0x2890 %08x->%08x\n",
+ yi->reg_2890, reg_2890);
/* Only update the filter if we really need to */
- if (h_filter != itv->yuv_info.h_filter) {
- ivtv_yuv_filter (itv,h_filter,-1,-1);
- itv->yuv_info.h_filter = h_filter;
+ if (h_filter != yi->h_filter) {
+ ivtv_yuv_filter(itv, h_filter, -1, -1);
+ yi->h_filter = h_filter;
}
}
-static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *window)
+static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *f)
{
+ struct yuv_playback_info *yi = &itv->yuv_info;
u32 master_height;
u32 reg_2918, reg_291c, reg_2920, reg_2928;
u32 reg_2930, reg_2934, reg_293c;
@@ -359,69 +370,59 @@ static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *wi
u32 reg_2950, reg_2954, reg_2958, reg_295c;
u32 reg_2960, reg_2964, reg_2968, reg_296c;
u32 reg_289c;
- u32 src_y_major_y, src_y_minor_y;
- u32 src_y_major_uv, src_y_minor_uv;
+ u32 src_major_y, src_minor_y;
+ u32 src_major_uv, src_minor_uv;
u32 reg_2964_base, reg_2968_base;
int v_filter_1, v_filter_2;
- IVTV_DEBUG_WARN("Need to adjust to height %d src_h %d dst_h %d src_y %d dst_y %d\n",
- window->tru_h, window->src_h, window->dst_h,window->src_y, window->dst_y);
+ IVTV_DEBUG_WARN
+ ("Adjust to height %d src_h %d dst_h %d src_y %d dst_y %d\n",
+ f->tru_h, f->src_h, f->dst_h, f->src_y, f->dst_y);
/* What scaling mode is being used... */
- if (window->interlaced_y) {
- IVTV_DEBUG_YUV("Scaling mode Y: Interlaced\n");
- }
- else {
- IVTV_DEBUG_YUV("Scaling mode Y: Progressive\n");
- }
+ IVTV_DEBUG_YUV("Scaling mode Y: %s\n",
+ f->interlaced_y ? "Interlaced" : "Progressive");
- if (window->interlaced_uv) {
- IVTV_DEBUG_YUV("Scaling mode UV: Interlaced\n");
- }
- else {
- IVTV_DEBUG_YUV("Scaling mode UV: Progressive\n");
- }
+ IVTV_DEBUG_YUV("Scaling mode UV: %s\n",
+ f->interlaced_uv ? "Interlaced" : "Progressive");
/* What is the source video being treated as... */
- if (itv->yuv_info.frame_interlaced) {
- IVTV_DEBUG_WARN("Source video: Interlaced\n");
- }
- else {
- IVTV_DEBUG_WARN("Source video: Non-interlaced\n");
- }
+ IVTV_DEBUG_WARN("Source video: %s\n",
+ f->interlaced ? "Interlaced" : "Progressive");
/* We offset into the image using two different index methods, so split
the y source coord into two parts. */
- if (window->src_y < 8) {
- src_y_minor_uv = window->src_y;
- src_y_major_uv = 0;
- }
- else {
- src_y_minor_uv = 8;
- src_y_major_uv = window->src_y - 8;
+ if (f->src_y < 8) {
+ src_minor_uv = f->src_y;
+ src_major_uv = 0;
+ } else {
+ src_minor_uv = 8;
+ src_major_uv = f->src_y - 8;
}
- src_y_minor_y = src_y_minor_uv;
- src_y_major_y = src_y_major_uv;
+ src_minor_y = src_minor_uv;
+ src_major_y = src_major_uv;
- if (window->offset_y) src_y_minor_y += 16;
+ if (f->offset_y)
+ src_minor_y += 16;
- if (window->interlaced_y)
- reg_2918 = (window->dst_h << 16) | (window->src_h + src_y_minor_y);
+ if (f->interlaced_y)
+ reg_2918 = (f->dst_h << 16) | (f->src_h + src_minor_y);
else
- reg_2918 = (window->dst_h << 16) | ((window->src_h + src_y_minor_y) << 1);
+ reg_2918 = (f->dst_h << 16) | ((f->src_h + src_minor_y) << 1);
- if (window->interlaced_uv)
- reg_291c = (window->dst_h << 16) | ((window->src_h + src_y_minor_uv) >> 1);
+ if (f->interlaced_uv)
+ reg_291c = (f->dst_h << 16) | ((f->src_h + src_minor_uv) >> 1);
else
- reg_291c = (window->dst_h << 16) | (window->src_h + src_y_minor_uv);
+ reg_291c = (f->dst_h << 16) | (f->src_h + src_minor_uv);
- reg_2964_base = (src_y_minor_y * ((window->dst_h << 16)/window->src_h)) >> 14;
- reg_2968_base = (src_y_minor_uv * ((window->dst_h << 16)/window->src_h)) >> 14;
+ reg_2964_base = (src_minor_y * ((f->dst_h << 16) / f->src_h)) >> 14;
+ reg_2968_base = (src_minor_uv * ((f->dst_h << 16) / f->src_h)) >> 14;
- if (window->dst_h / 2 >= window->src_h && !window->interlaced_y) {
- master_height = (window->src_h * 0x00400000) / window->dst_h;
- if ((window->src_h * 0x00400000) - (master_height * window->dst_h) >= window->dst_h / 2) master_height ++;
+ if (f->dst_h / 2 >= f->src_h && !f->interlaced_y) {
+ master_height = (f->src_h * 0x00400000) / f->dst_h;
+ if ((f->src_h * 0x00400000) - (master_height * f->dst_h) >= f->dst_h / 2)
+ master_height++;
reg_2920 = master_height >> 2;
reg_2928 = master_height >> 3;
reg_2930 = master_height;
@@ -429,45 +430,42 @@ static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *wi
reg_2964_base >>= 3;
reg_2968_base >>= 3;
reg_296c = 0x00000000;
- }
- else if (window->dst_h >= window->src_h) {
- master_height = (window->src_h * 0x00400000) / window->dst_h;
+ } else if (f->dst_h >= f->src_h) {
+ master_height = (f->src_h * 0x00400000) / f->dst_h;
master_height = (master_height >> 1) + (master_height & 1);
reg_2920 = master_height >> 2;
reg_2928 = master_height >> 2;
reg_2930 = master_height;
reg_2940 = master_height >> 1;
reg_296c = 0x00000000;
- if (window->interlaced_y) {
+ if (f->interlaced_y) {
reg_2964_base >>= 3;
- }
- else {
- reg_296c ++;
+ } else {
+ reg_296c++;
reg_2964_base >>= 2;
}
- if (window->interlaced_uv) reg_2928 >>= 1;
+ if (f->interlaced_uv)
+ reg_2928 >>= 1;
reg_2968_base >>= 3;
- }
- else if (window->dst_h >= window->src_h / 2) {
- master_height = (window->src_h * 0x00200000) / window->dst_h;
+ } else if (f->dst_h >= f->src_h / 2) {
+ master_height = (f->src_h * 0x00200000) / f->dst_h;
master_height = (master_height >> 1) + (master_height & 1);
reg_2920 = master_height >> 2;
reg_2928 = master_height >> 2;
reg_2930 = master_height;
reg_2940 = master_height;
reg_296c = 0x00000101;
- if (window->interlaced_y) {
+ if (f->interlaced_y) {
reg_2964_base >>= 2;
- }
- else {
- reg_296c ++;
+ } else {
+ reg_296c++;
reg_2964_base >>= 1;
}
- if (window->interlaced_uv) reg_2928 >>= 1;
+ if (f->interlaced_uv)
+ reg_2928 >>= 1;
reg_2968_base >>= 2;
- }
- else {
- master_height = (window->src_h * 0x00100000) / window->dst_h;
+ } else {
+ master_height = (f->src_h * 0x00100000) / f->dst_h;
master_height = (master_height >> 1) + (master_height & 1);
reg_2920 = master_height >> 2;
reg_2928 = master_height >> 2;
@@ -480,13 +478,12 @@ static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *wi
/* FIXME These registers change depending on scaled / unscaled output
We really need to work out what they should be */
- if (window->src_h == window->dst_h){
+ if (f->src_h == f->dst_h) {
reg_2934 = 0x00020000;
reg_293c = 0x00100000;
reg_2944 = 0x00040000;
reg_294c = 0x000b0000;
- }
- else {
+ } else {
reg_2934 = 0x00000FF0;
reg_293c = 0x00000FF0;
reg_2944 = 0x00000FF0;
@@ -494,34 +491,36 @@ static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *wi
}
/* The first line to be displayed */
- reg_2950 = 0x00010000 + src_y_major_y;
- if (window->interlaced_y) reg_2950 += 0x00010000;
+ reg_2950 = 0x00010000 + src_major_y;
+ if (f->interlaced_y)
+ reg_2950 += 0x00010000;
reg_2954 = reg_2950 + 1;
- reg_2958 = 0x00010000 + (src_y_major_y >> 1);
- if (window->interlaced_uv) reg_2958 += 0x00010000;
+ reg_2958 = 0x00010000 + (src_major_y >> 1);
+ if (f->interlaced_uv)
+ reg_2958 += 0x00010000;
reg_295c = reg_2958 + 1;
- if (itv->yuv_info.decode_height == 480)
+ if (yi->decode_height == 480)
reg_289c = 0x011e0017;
else
reg_289c = 0x01500017;
- if (window->dst_y < 0)
- reg_289c = (reg_289c - ((window->dst_y & ~1)<<15))-(window->dst_y >>1);
+ if (f->dst_y < 0)
+ reg_289c = (reg_289c - ((f->dst_y & ~1)<<15))-(f->dst_y >>1);
else
- reg_289c = (reg_289c + ((window->dst_y & ~1)<<15))+(window->dst_y >>1);
+ reg_289c = (reg_289c + ((f->dst_y & ~1)<<15))+(f->dst_y >>1);
/* How much of the source to decode.
Take into account the source offset */
- reg_2960 = ((src_y_minor_y + window->src_h + src_y_major_y) - 1 ) |
- ((((src_y_minor_uv + window->src_h + src_y_major_uv) - 1) & ~1) << 15);
+ reg_2960 = ((src_minor_y + f->src_h + src_major_y) - 1) |
+ (((src_minor_uv + f->src_h + src_major_uv - 1) & ~1) << 15);
/* Calculate correct value for register 2964 */
- if (window->src_h == window->dst_h)
+ if (f->src_h == f->dst_h) {
reg_2964 = 1;
- else {
- reg_2964 = 2 + ((window->dst_h << 1) / window->src_h);
+ } else {
+ reg_2964 = 2 + ((f->dst_h << 1) / f->src_h);
reg_2964 = (reg_2964 >> 1) + (reg_2964 & 1);
}
reg_2968 = (reg_2964 << 16) + reg_2964 + (reg_2964 >> 1);
@@ -536,283 +535,246 @@ static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *wi
/* Deviate further from what it should be. I find the flicker headache
inducing so try to reduce it slightly. Leave 2968 as-is otherwise
colours foul. */
- if ((reg_2964 != 0x00010001) && (window->dst_h / 2 <= window->src_h))
- reg_2964 = (reg_2964 & 0xFFFF0000) + ((reg_2964 & 0x0000FFFF)/2);
+ if ((reg_2964 != 0x00010001) && (f->dst_h / 2 <= f->src_h))
+ reg_2964 = (reg_2964 & 0xFFFF0000) + ((reg_2964 & 0x0000FFFF) / 2);
- if (!window->interlaced_y) reg_2964 -= 0x00010001;
- if (!window->interlaced_uv) reg_2968 -= 0x00010001;
+ if (!f->interlaced_y)
+ reg_2964 -= 0x00010001;
+ if (!f->interlaced_uv)
+ reg_2968 -= 0x00010001;
reg_2964 += ((reg_2964_base << 16) | reg_2964_base);
reg_2968 += ((reg_2968_base << 16) | reg_2968_base);
/* Select the vertical filter */
- if (window->src_h == window->dst_h) {
+ if (f->src_h == f->dst_h) {
/* An exact size match uses filter 0/1 */
v_filter_1 = 0;
v_filter_2 = 1;
- }
- else {
+ } else {
/* Figure out which filter to use */
- v_filter_1 = ((window->src_h << 16) / window->dst_h) >> 15;
+ v_filter_1 = ((f->src_h << 16) / f->dst_h) >> 15;
v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1);
/* Only an exact size match can use filter 0 */
- if (v_filter_1 == 0) v_filter_1 = 1;
+ v_filter_1 += !v_filter_1;
v_filter_2 = v_filter_1;
}
write_reg(reg_2934, 0x02934);
write_reg(reg_293c, 0x0293c);
- IVTV_DEBUG_YUV("Update reg 0x2934 %08x->%08x 0x293c %08x->%08x\n",itv->yuv_info.reg_2934, reg_2934, itv->yuv_info.reg_293c, reg_293c);
+ IVTV_DEBUG_YUV("Update reg 0x2934 %08x->%08x 0x293c %08x->%08x\n",
+ yi->reg_2934, reg_2934, yi->reg_293c, reg_293c);
write_reg(reg_2944, 0x02944);
write_reg(reg_294c, 0x0294c);
- IVTV_DEBUG_YUV("Update reg 0x2944 %08x->%08x 0x294c %08x->%08x\n",itv->yuv_info.reg_2944, reg_2944, itv->yuv_info.reg_294c, reg_294c);
+ IVTV_DEBUG_YUV("Update reg 0x2944 %08x->%08x 0x294c %08x->%08x\n",
+ yi->reg_2944, reg_2944, yi->reg_294c, reg_294c);
/* Ensure 2970 is 0 (does it ever change ?) */
/* write_reg(0,0x02970); */
-/* IVTV_DEBUG_YUV("Update reg 0x2970 %08x->%08x\n",itv->yuv_info.reg_2970, 0); */
+/* IVTV_DEBUG_YUV("Update reg 0x2970 %08x->%08x\n", yi->reg_2970, 0); */
write_reg(reg_2930, 0x02938);
write_reg(reg_2930, 0x02930);
- IVTV_DEBUG_YUV("Update reg 0x2930 %08x->%08x 0x2938 %08x->%08x\n",itv->yuv_info.reg_2930, reg_2930, itv->yuv_info.reg_2938, reg_2930);
+ IVTV_DEBUG_YUV("Update reg 0x2930 %08x->%08x 0x2938 %08x->%08x\n",
+ yi->reg_2930, reg_2930, yi->reg_2938, reg_2930);
write_reg(reg_2928, 0x02928);
- write_reg(reg_2928+0x514, 0x0292C);
- IVTV_DEBUG_YUV("Update reg 0x2928 %08x->%08x 0x292c %08x->%08x\n",itv->yuv_info.reg_2928, reg_2928, itv->yuv_info.reg_292c, reg_2928+0x514);
+ write_reg(reg_2928 + 0x514, 0x0292C);
+ IVTV_DEBUG_YUV("Update reg 0x2928 %08x->%08x 0x292c %08x->%08x\n",
+ yi->reg_2928, reg_2928, yi->reg_292c, reg_2928 + 0x514);
write_reg(reg_2920, 0x02920);
- write_reg(reg_2920+0x514, 0x02924);
- IVTV_DEBUG_YUV("Update reg 0x2920 %08x->%08x 0x2924 %08x->%08x\n",itv->yuv_info.reg_2920, reg_2920, itv->yuv_info.reg_2924, 0x514+reg_2920);
+ write_reg(reg_2920 + 0x514, 0x02924);
+ IVTV_DEBUG_YUV("Update reg 0x2920 %08x->%08x 0x2924 %08x->%08x\n",
+ yi->reg_2920, reg_2920, yi->reg_2924, reg_2920 + 0x514);
- write_reg (reg_2918,0x02918);
- write_reg (reg_291c,0x0291C);
- IVTV_DEBUG_YUV("Update reg 0x2918 %08x->%08x 0x291C %08x->%08x\n",itv->yuv_info.reg_2918,reg_2918,itv->yuv_info.reg_291c,reg_291c);
+ write_reg(reg_2918, 0x02918);
+ write_reg(reg_291c, 0x0291C);
+ IVTV_DEBUG_YUV("Update reg 0x2918 %08x->%08x 0x291C %08x->%08x\n",
+ yi->reg_2918, reg_2918, yi->reg_291c, reg_291c);
write_reg(reg_296c, 0x0296c);
- IVTV_DEBUG_YUV("Update reg 0x296c %08x->%08x\n",itv->yuv_info.reg_296c, reg_296c);
+ IVTV_DEBUG_YUV("Update reg 0x296c %08x->%08x\n",
+ yi->reg_296c, reg_296c);
write_reg(reg_2940, 0x02948);
write_reg(reg_2940, 0x02940);
- IVTV_DEBUG_YUV("Update reg 0x2940 %08x->%08x 0x2948 %08x->%08x\n",itv->yuv_info.reg_2940, reg_2940, itv->yuv_info.reg_2948, reg_2940);
+ IVTV_DEBUG_YUV("Update reg 0x2940 %08x->%08x 0x2948 %08x->%08x\n",
+ yi->reg_2940, reg_2940, yi->reg_2948, reg_2940);
write_reg(reg_2950, 0x02950);
write_reg(reg_2954, 0x02954);
- IVTV_DEBUG_YUV("Update reg 0x2950 %08x->%08x 0x2954 %08x->%08x\n",itv->yuv_info.reg_2950, reg_2950, itv->yuv_info.reg_2954, reg_2954);
+ IVTV_DEBUG_YUV("Update reg 0x2950 %08x->%08x 0x2954 %08x->%08x\n",
+ yi->reg_2950, reg_2950, yi->reg_2954, reg_2954);
write_reg(reg_2958, 0x02958);
write_reg(reg_295c, 0x0295C);
- IVTV_DEBUG_YUV("Update reg 0x2958 %08x->%08x 0x295C %08x->%08x\n",itv->yuv_info.reg_2958, reg_2958, itv->yuv_info.reg_295c, reg_295c);
+ IVTV_DEBUG_YUV("Update reg 0x2958 %08x->%08x 0x295C %08x->%08x\n",
+ yi->reg_2958, reg_2958, yi->reg_295c, reg_295c);
write_reg(reg_2960, 0x02960);
- IVTV_DEBUG_YUV("Update reg 0x2960 %08x->%08x \n",itv->yuv_info.reg_2960, reg_2960);
+ IVTV_DEBUG_YUV("Update reg 0x2960 %08x->%08x \n",
+ yi->reg_2960, reg_2960);
write_reg(reg_2964, 0x02964);
write_reg(reg_2968, 0x02968);
- IVTV_DEBUG_YUV("Update reg 0x2964 %08x->%08x 0x2968 %08x->%08x\n",itv->yuv_info.reg_2964, reg_2964, itv->yuv_info.reg_2968, reg_2968);
+ IVTV_DEBUG_YUV("Update reg 0x2964 %08x->%08x 0x2968 %08x->%08x\n",
+ yi->reg_2964, reg_2964, yi->reg_2968, reg_2968);
- write_reg( reg_289c,0x0289c);
- IVTV_DEBUG_YUV("Update reg 0x289c %08x->%08x\n",itv->yuv_info.reg_289c, reg_289c);
+ write_reg(reg_289c, 0x0289c);
+ IVTV_DEBUG_YUV("Update reg 0x289c %08x->%08x\n",
+ yi->reg_289c, reg_289c);
/* Only update filter 1 if we really need to */
- if (v_filter_1 != itv->yuv_info.v_filter_1) {
- ivtv_yuv_filter (itv,-1,v_filter_1,-1);
- itv->yuv_info.v_filter_1 = v_filter_1;
+ if (v_filter_1 != yi->v_filter_1) {
+ ivtv_yuv_filter(itv, -1, v_filter_1, -1);
+ yi->v_filter_1 = v_filter_1;
}
/* Only update filter 2 if we really need to */
- if (v_filter_2 != itv->yuv_info.v_filter_2) {
- ivtv_yuv_filter (itv,-1,-1,v_filter_2);
- itv->yuv_info.v_filter_2 = v_filter_2;
+ if (v_filter_2 != yi->v_filter_2) {
+ ivtv_yuv_filter(itv, -1, -1, v_filter_2);
+ yi->v_filter_2 = v_filter_2;
}
-
}
/* Modify the supplied coordinate information to fit the visible osd area */
-static u32 ivtv_yuv_window_setup (struct ivtv *itv, struct yuv_frame_info *window)
+static u32 ivtv_yuv_window_setup(struct ivtv *itv, struct yuv_frame_info *f)
{
- int osd_crop, lace_threshold;
+ struct yuv_frame_info *of = &itv->yuv_info.old_frame_info;
+ int osd_crop;
u32 osd_scale;
u32 yuv_update = 0;
- lace_threshold = itv->yuv_info.lace_threshold;
- if (lace_threshold < 0)
- lace_threshold = itv->yuv_info.decode_height - 1;
-
- /* Work out the lace settings */
- switch (itv->yuv_info.lace_mode) {
- case IVTV_YUV_MODE_PROGRESSIVE: /* Progressive mode */
- itv->yuv_info.frame_interlaced = 0;
- if (window->tru_h < 512 || (window->tru_h > 576 && window->tru_h < 1021))
- window->interlaced_y = 0;
- else
- window->interlaced_y = 1;
-
- if (window->tru_h < 1021 && (window->dst_h >= window->src_h /2))
- window->interlaced_uv = 0;
- else
- window->interlaced_uv = 1;
- break;
-
- case IVTV_YUV_MODE_AUTO:
- if (window->tru_h <= lace_threshold || window->tru_h > 576 || window->tru_w > 720){
- itv->yuv_info.frame_interlaced = 0;
- if ((window->tru_h < 512) ||
- (window->tru_h > 576 && window->tru_h < 1021) ||
- (window->tru_w > 720 && window->tru_h < 1021))
- window->interlaced_y = 0;
- else
- window->interlaced_y = 1;
-
- if (window->tru_h < 1021 && (window->dst_h >= window->src_h /2))
- window->interlaced_uv = 0;
- else
- window->interlaced_uv = 1;
- }
- else {
- itv->yuv_info.frame_interlaced = 1;
- window->interlaced_y = 1;
- window->interlaced_uv = 1;
- }
- break;
-
- case IVTV_YUV_MODE_INTERLACED: /* Interlace mode */
- default:
- itv->yuv_info.frame_interlaced = 1;
- window->interlaced_y = 1;
- window->interlaced_uv = 1;
- break;
- }
-
/* Sorry, but no negative coords for src */
- if (window->src_x < 0) window->src_x = 0;
- if (window->src_y < 0) window->src_y = 0;
+ if (f->src_x < 0)
+ f->src_x = 0;
+ if (f->src_y < 0)
+ f->src_y = 0;
/* Can only reduce width down to 1/4 original size */
- if ((osd_crop = window->src_w - ( 4 * window->dst_w )) > 0) {
- window->src_x += osd_crop / 2;
- window->src_w = (window->src_w - osd_crop) & ~3;
- window->dst_w = window->src_w / 4;
- window->dst_w += window->dst_w & 1;
+ if ((osd_crop = f->src_w - 4 * f->dst_w) > 0) {
+ f->src_x += osd_crop / 2;
+ f->src_w = (f->src_w - osd_crop) & ~3;
+ f->dst_w = f->src_w / 4;
+ f->dst_w += f->dst_w & 1;
}
/* Can only reduce height down to 1/4 original size */
- if (window->src_h / window->dst_h >= 2) {
- /* Overflow may be because we're running progressive, so force mode switch */
- window->interlaced_y = 1;
+ if (f->src_h / f->dst_h >= 2) {
+ /* Overflow may be because we're running progressive,
+ so force mode switch */
+ f->interlaced_y = 1;
/* Make sure we're still within limits for interlace */
- if ((osd_crop = window->src_h - ( 4 * window->dst_h )) > 0) {
+ if ((osd_crop = f->src_h - 4 * f->dst_h) > 0) {
/* If we reach here we'll have to force the height. */
- window->src_y += osd_crop / 2;
- window->src_h = (window->src_h - osd_crop) & ~3;
- window->dst_h = window->src_h / 4;
- window->dst_h += window->dst_h & 1;
+ f->src_y += osd_crop / 2;
+ f->src_h = (f->src_h - osd_crop) & ~3;
+ f->dst_h = f->src_h / 4;
+ f->dst_h += f->dst_h & 1;
}
}
/* If there's nothing to safe to display, we may as well stop now */
- if ((int)window->dst_w <= 2 || (int)window->dst_h <= 2 || (int)window->src_w <= 2 || (int)window->src_h <= 2) {
+ if ((int)f->dst_w <= 2 || (int)f->dst_h <= 2 ||
+ (int)f->src_w <= 2 || (int)f->src_h <= 2) {
return IVTV_YUV_UPDATE_INVALID;
}
/* Ensure video remains inside OSD area */
- osd_scale = (window->src_h << 16) / window->dst_h;
+ osd_scale = (f->src_h << 16) / f->dst_h;
- if ((osd_crop = window->pan_y - window->dst_y) > 0) {
+ if ((osd_crop = f->pan_y - f->dst_y) > 0) {
/* Falls off the upper edge - crop */
- window->src_y += (osd_scale * osd_crop) >> 16;
- window->src_h -= (osd_scale * osd_crop) >> 16;
- window->dst_h -= osd_crop;
- window->dst_y = 0;
- }
- else {
- window->dst_y -= window->pan_y;
+ f->src_y += (osd_scale * osd_crop) >> 16;
+ f->src_h -= (osd_scale * osd_crop) >> 16;
+ f->dst_h -= osd_crop;
+ f->dst_y = 0;
+ } else {
+ f->dst_y -= f->pan_y;
}
- if ((osd_crop = window->dst_h + window->dst_y - window->vis_h) > 0) {
+ if ((osd_crop = f->dst_h + f->dst_y - f->vis_h) > 0) {
/* Falls off the lower edge - crop */
- window->dst_h -= osd_crop;
- window->src_h -= (osd_scale * osd_crop) >> 16;
+ f->dst_h -= osd_crop;
+ f->src_h -= (osd_scale * osd_crop) >> 16;
}
- osd_scale = (window->src_w << 16) / window->dst_w;
+ osd_scale = (f->src_w << 16) / f->dst_w;
- if ((osd_crop = window->pan_x - window->dst_x) > 0) {
+ if ((osd_crop = f->pan_x - f->dst_x) > 0) {
/* Fall off the left edge - crop */
- window->src_x += (osd_scale * osd_crop) >> 16;
- window->src_w -= (osd_scale * osd_crop) >> 16;
- window->dst_w -= osd_crop;
- window->dst_x = 0;
- }
- else {
- window->dst_x -= window->pan_x;
+ f->src_x += (osd_scale * osd_crop) >> 16;
+ f->src_w -= (osd_scale * osd_crop) >> 16;
+ f->dst_w -= osd_crop;
+ f->dst_x = 0;
+ } else {
+ f->dst_x -= f->pan_x;
}
- if ((osd_crop = window->dst_w + window->dst_x - window->vis_w) > 0) {
+ if ((osd_crop = f->dst_w + f->dst_x - f->vis_w) > 0) {
/* Falls off the right edge - crop */
- window->dst_w -= osd_crop;
- window->src_w -= (osd_scale * osd_crop) >> 16;
+ f->dst_w -= osd_crop;
+ f->src_w -= (osd_scale * osd_crop) >> 16;
}
/* The OSD can be moved. Track to it */
- window->dst_x += itv->yuv_info.osd_x_offset;
- window->dst_y += itv->yuv_info.osd_y_offset;
+ f->dst_x += itv->yuv_info.osd_x_offset;
+ f->dst_y += itv->yuv_info.osd_y_offset;
/* Width & height for both src & dst must be even.
Same for coordinates. */
- window->dst_w &= ~1;
- window->dst_x &= ~1;
+ f->dst_w &= ~1;
+ f->dst_x &= ~1;
- window->src_w += window->src_x & 1;
- window->src_x &= ~1;
+ f->src_w += f->src_x & 1;
+ f->src_x &= ~1;
- window->src_w &= ~1;
- window->dst_w &= ~1;
+ f->src_w &= ~1;
+ f->dst_w &= ~1;
- window->dst_h &= ~1;
- window->dst_y &= ~1;
+ f->dst_h &= ~1;
+ f->dst_y &= ~1;
- window->src_h += window->src_y & 1;
- window->src_y &= ~1;
+ f->src_h += f->src_y & 1;
+ f->src_y &= ~1;
- window->src_h &= ~1;
- window->dst_h &= ~1;
+ f->src_h &= ~1;
+ f->dst_h &= ~1;
- /* Due to rounding, we may have reduced the output size to <1/4 of the source
- Check again, but this time just resize. Don't change source coordinates */
- if (window->dst_w < window->src_w / 4) {
- window->src_w &= ~3;
- window->dst_w = window->src_w / 4;
- window->dst_w += window->dst_w & 1;
+ /* Due to rounding, we may have reduced the output size to <1/4 of
+ the source. Check again, but this time just resize. Don't change
+ source coordinates */
+ if (f->dst_w < f->src_w / 4) {
+ f->src_w &= ~3;
+ f->dst_w = f->src_w / 4;
+ f->dst_w += f->dst_w & 1;
}
- if (window->dst_h < window->src_h / 4) {
- window->src_h &= ~3;
- window->dst_h = window->src_h / 4;
- window->dst_h += window->dst_h & 1;
+ if (f->dst_h < f->src_h / 4) {
+ f->src_h &= ~3;
+ f->dst_h = f->src_h / 4;
+ f->dst_h += f->dst_h & 1;
}
/* Check again. If there's nothing to safe to display, stop now */
- if ((int)window->dst_w <= 2 || (int)window->dst_h <= 2 || (int)window->src_w <= 2 || (int)window->src_h <= 2) {
+ if ((int)f->dst_w <= 2 || (int)f->dst_h <= 2 ||
+ (int)f->src_w <= 2 || (int)f->src_h <= 2) {
return IVTV_YUV_UPDATE_INVALID;
}
/* Both x offset & width are linked, so they have to be done together */
- if ((itv->yuv_info.old_frame_info.dst_w != window->dst_w) ||
- (itv->yuv_info.old_frame_info.src_w != window->src_w) ||
- (itv->yuv_info.old_frame_info.dst_x != window->dst_x) ||
- (itv->yuv_info.old_frame_info.src_x != window->src_x) ||
- (itv->yuv_info.old_frame_info.pan_x != window->pan_x) ||
- (itv->yuv_info.old_frame_info.vis_w != window->vis_w)) {
+ if ((of->dst_w != f->dst_w) || (of->src_w != f->src_w) ||
+ (of->dst_x != f->dst_x) || (of->src_x != f->src_x) ||
+ (of->pan_x != f->pan_x) || (of->vis_w != f->vis_w)) {
yuv_update |= IVTV_YUV_UPDATE_HORIZONTAL;
}
- if ((itv->yuv_info.old_frame_info.src_h != window->src_h) ||
- (itv->yuv_info.old_frame_info.dst_h != window->dst_h) ||
- (itv->yuv_info.old_frame_info.dst_y != window->dst_y) ||
- (itv->yuv_info.old_frame_info.src_y != window->src_y) ||
- (itv->yuv_info.old_frame_info.pan_y != window->pan_y) ||
- (itv->yuv_info.old_frame_info.vis_h != window->vis_h) ||
- (itv->yuv_info.old_frame_info.lace_mode != window->lace_mode) ||
- (itv->yuv_info.old_frame_info.interlaced_y != window->interlaced_y) ||
- (itv->yuv_info.old_frame_info.interlaced_uv != window->interlaced_uv)) {
+ if ((of->src_h != f->src_h) || (of->dst_h != f->dst_h) ||
+ (of->dst_y != f->dst_y) || (of->src_y != f->src_y) ||
+ (of->pan_y != f->pan_y) || (of->vis_h != f->vis_h) ||
+ (of->lace_mode != f->lace_mode) ||
+ (of->interlaced_y != f->interlaced_y) ||
+ (of->interlaced_uv != f->interlaced_uv)) {
yuv_update |= IVTV_YUV_UPDATE_VERTICAL;
}
@@ -820,24 +782,24 @@ static u32 ivtv_yuv_window_setup (struct ivtv *itv, struct yuv_frame_info *windo
}
/* Update the scaling register to the requested value */
-void ivtv_yuv_work_handler (struct ivtv *itv)
+void ivtv_yuv_work_handler(struct ivtv *itv)
{
- struct yuv_frame_info window;
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ struct yuv_frame_info f;
+ int frame = yi->update_frame;
u32 yuv_update;
- int frame = itv->yuv_info.update_frame;
-
-/* IVTV_DEBUG_YUV("Update yuv registers for frame %d\n",frame); */
- memcpy(&window, &itv->yuv_info.new_frame_info[frame], sizeof (window));
+ IVTV_DEBUG_YUV("Update yuv registers for frame %d\n", frame);
+ f = yi->new_frame_info[frame];
/* Update the osd pan info */
- window.pan_x = itv->yuv_info.osd_x_pan;
- window.pan_y = itv->yuv_info.osd_y_pan;
- window.vis_w = itv->yuv_info.osd_vis_w;
- window.vis_h = itv->yuv_info.osd_vis_h;
+ f.pan_x = yi->osd_x_pan;
+ f.pan_y = yi->osd_y_pan;
+ f.vis_w = yi->osd_vis_w;
+ f.vis_h = yi->osd_vis_h;
/* Calculate the display window coordinates. Exit if nothing left */
- if (!(yuv_update = ivtv_yuv_window_setup (itv, &window)))
+ if (!(yuv_update = ivtv_yuv_window_setup(itv, &f)))
return;
if (yuv_update & IVTV_YUV_UPDATE_INVALID) {
@@ -846,16 +808,15 @@ void ivtv_yuv_work_handler (struct ivtv *itv)
write_reg(0x00108080, 0x2898);
if (yuv_update & IVTV_YUV_UPDATE_HORIZONTAL)
- ivtv_yuv_handle_horizontal(itv, &window);
+ ivtv_yuv_handle_horizontal(itv, &f);
if (yuv_update & IVTV_YUV_UPDATE_VERTICAL)
- ivtv_yuv_handle_vertical(itv, &window);
+ ivtv_yuv_handle_vertical(itv, &f);
}
-
- memcpy(&itv->yuv_info.old_frame_info, &window, sizeof (itv->yuv_info.old_frame_info));
+ yi->old_frame_info = f;
}
-static void ivtv_yuv_init (struct ivtv *itv)
+static void ivtv_yuv_init(struct ivtv *itv)
{
struct yuv_playback_info *yi = &itv->yuv_info;
@@ -924,25 +885,23 @@ static void ivtv_yuv_init (struct ivtv *itv)
if (!yi->osd_vis_w)
yi->osd_vis_w = 720 - yi->osd_x_offset;
- if (!yi->osd_vis_h)
+ if (!yi->osd_vis_h) {
yi->osd_vis_h = yi->decode_height - yi->osd_y_offset;
- else {
+ } else if (yi->osd_vis_h + yi->osd_y_offset > yi->decode_height) {
/* If output video standard has changed, requested height may
- not be legal */
- if (yi->osd_vis_h + yi->osd_y_offset > yi->decode_height) {
- IVTV_DEBUG_WARN("Clipping yuv output - fb size (%d) exceeds video standard limit (%d)\n",
- yi->osd_vis_h + yi->osd_y_offset,
- yi->decode_height);
- yi->osd_vis_h = yi->decode_height - yi->osd_y_offset;
- }
+ not be legal */
+ IVTV_DEBUG_WARN("Clipping yuv output - fb size (%d) exceeds video standard limit (%d)\n",
+ yi->osd_vis_h + yi->osd_y_offset,
+ yi->decode_height);
+ yi->osd_vis_h = yi->decode_height - yi->osd_y_offset;
}
}
/* We need a buffer for blanking when Y plane is offset - non-fatal if we can't get one */
- yi->blanking_ptr = kzalloc(720*16, GFP_KERNEL);
- if (yi->blanking_ptr)
+ yi->blanking_ptr = kzalloc(720 * 16, GFP_KERNEL);
+ if (yi->blanking_ptr) {
yi->blanking_dmaptr = pci_map_single(itv->dev, yi->blanking_ptr, 720*16, PCI_DMA_TODEVICE);
- else {
+ } else {
yi->blanking_dmaptr = 0;
IVTV_DEBUG_WARN("Failed to allocate yuv blanking buffer\n");
}
@@ -954,77 +913,140 @@ static void ivtv_yuv_init (struct ivtv *itv)
atomic_set(&yi->next_dma_frame, 0);
}
-int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
+/* Get next available yuv buffer on PVR350 */
+void ivtv_yuv_next_free(struct ivtv *itv)
{
- DEFINE_WAIT(wait);
- int rc = 0;
- int got_sig = 0;
- int frame, next_fill_frame, last_fill_frame;
- int register_update = 0;
+ int draw, display;
+ struct yuv_playback_info *yi = &itv->yuv_info;
- IVTV_DEBUG_INFO("yuv_prep_frame\n");
+ if (atomic_read(&yi->next_dma_frame) == -1)
+ ivtv_yuv_init(itv);
- if (atomic_read(&itv->yuv_info.next_dma_frame) == -1) ivtv_yuv_init(itv);
+ draw = atomic_read(&yi->next_fill_frame);
+ display = atomic_read(&yi->next_dma_frame);
- frame = atomic_read(&itv->yuv_info.next_fill_frame);
- next_fill_frame = (frame + 1) & 0x3;
- last_fill_frame = (atomic_read(&itv->yuv_info.next_dma_frame)+1) & 0x3;
+ if (display > draw)
+ display -= IVTV_YUV_BUFFERS;
- if (next_fill_frame != last_fill_frame && last_fill_frame != frame) {
- /* Buffers are full - Overwrite the last frame */
- next_fill_frame = frame;
- frame = (frame - 1) & 3;
- register_update = itv->yuv_info.new_frame_info[frame].update;
- }
+ if (draw - display >= yi->max_frames_buffered)
+ draw = (u8)(draw - 1) % IVTV_YUV_BUFFERS;
+ else
+ yi->new_frame_info[draw].update = 0;
+
+ yi->draw_frame = draw;
+}
+
+/* Set up frame according to ivtv_dma_frame parameters */
+void ivtv_yuv_setup_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
+{
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ u8 frame = yi->draw_frame;
+ u8 last_frame = (u8)(frame - 1) % IVTV_YUV_BUFFERS;
+ struct yuv_frame_info *nf = &yi->new_frame_info[frame];
+ struct yuv_frame_info *of = &yi->new_frame_info[last_frame];
+ int lace_threshold = yi->lace_threshold;
+
+ /* Preserve old update flag in case we're overwriting a queued frame */
+ int update = nf->update;
/* Take a snapshot of the yuv coordinate information */
- itv->yuv_info.new_frame_info[frame].src_x = args->src.left;
- itv->yuv_info.new_frame_info[frame].src_y = args->src.top;
- itv->yuv_info.new_frame_info[frame].src_w = args->src.width;
- itv->yuv_info.new_frame_info[frame].src_h = args->src.height;
- itv->yuv_info.new_frame_info[frame].dst_x = args->dst.left;
- itv->yuv_info.new_frame_info[frame].dst_y = args->dst.top;
- itv->yuv_info.new_frame_info[frame].dst_w = args->dst.width;
- itv->yuv_info.new_frame_info[frame].dst_h = args->dst.height;
- itv->yuv_info.new_frame_info[frame].tru_x = args->dst.left;
- itv->yuv_info.new_frame_info[frame].tru_w = args->src_width;
- itv->yuv_info.new_frame_info[frame].tru_h = args->src_height;
-
- /* Snapshot field order */
- itv->yuv_info.sync_field[frame] = itv->yuv_info.lace_sync_field;
+ nf->src_x = args->src.left;
+ nf->src_y = args->src.top;
+ nf->src_w = args->src.width;
+ nf->src_h = args->src.height;
+ nf->dst_x = args->dst.left;
+ nf->dst_y = args->dst.top;
+ nf->dst_w = args->dst.width;
+ nf->dst_h = args->dst.height;
+ nf->tru_x = args->dst.left;
+ nf->tru_w = args->src_width;
+ nf->tru_h = args->src_height;
/* Are we going to offset the Y plane */
- if (args->src.height + args->src.top < 512-16)
- itv->yuv_info.new_frame_info[frame].offset_y = 1;
- else
- itv->yuv_info.new_frame_info[frame].offset_y = 0;
+ nf->offset_y = (nf->tru_h + nf->src_x < 512 - 16) ? 1 : 0;
/* Snapshot the osd pan info */
- itv->yuv_info.new_frame_info[frame].pan_x = itv->yuv_info.osd_x_pan;
- itv->yuv_info.new_frame_info[frame].pan_y = itv->yuv_info.osd_y_pan;
- itv->yuv_info.new_frame_info[frame].vis_w = itv->yuv_info.osd_vis_w;
- itv->yuv_info.new_frame_info[frame].vis_h = itv->yuv_info.osd_vis_h;
-
- itv->yuv_info.new_frame_info[frame].update = 0;
- itv->yuv_info.new_frame_info[frame].interlaced_y = 0;
- itv->yuv_info.new_frame_info[frame].interlaced_uv = 0;
- itv->yuv_info.new_frame_info[frame].lace_mode = itv->yuv_info.lace_mode;
-
- if (memcmp (&itv->yuv_info.old_frame_info_args, &itv->yuv_info.new_frame_info[frame],
- sizeof (itv->yuv_info.new_frame_info[frame]))) {
- memcpy(&itv->yuv_info.old_frame_info_args, &itv->yuv_info.new_frame_info[frame], sizeof (itv->yuv_info.old_frame_info_args));
- itv->yuv_info.new_frame_info[frame].update = 1;
-/* IVTV_DEBUG_YUV ("Requesting register update for frame %d\n",frame); */
+ nf->pan_x = yi->osd_x_pan;
+ nf->pan_y = yi->osd_y_pan;
+ nf->vis_w = yi->osd_vis_w;
+ nf->vis_h = yi->osd_vis_h;
+
+ nf->update = 0;
+ nf->interlaced_y = 0;
+ nf->interlaced_uv = 0;
+ nf->delay = 0;
+ nf->sync_field = 0;
+ nf->lace_mode = yi->lace_mode & IVTV_YUV_MODE_MASK;
+
+ if (lace_threshold < 0)
+ lace_threshold = yi->decode_height - 1;
+
+ /* Work out the lace settings */
+ switch (nf->lace_mode) {
+ case IVTV_YUV_MODE_PROGRESSIVE: /* Progressive mode */
+ nf->interlaced = 0;
+ if (nf->tru_h < 512 || (nf->tru_h > 576 && nf->tru_h < 1021))
+ nf->interlaced_y = 0;
+ else
+ nf->interlaced_y = 1;
+
+ if (nf->tru_h < 1021 && (nf->dst_h >= nf->src_h / 2))
+ nf->interlaced_uv = 0;
+ else
+ nf->interlaced_uv = 1;
+ break;
+
+ case IVTV_YUV_MODE_AUTO:
+ if (nf->tru_h <= lace_threshold || nf->tru_h > 576 || nf->tru_w > 720) {
+ nf->interlaced = 0;
+ if ((nf->tru_h < 512) ||
+ (nf->tru_h > 576 && nf->tru_h < 1021) ||
+ (nf->tru_w > 720 && nf->tru_h < 1021))
+ nf->interlaced_y = 0;
+ else
+ nf->interlaced_y = 1;
+ if (nf->tru_h < 1021 && (nf->dst_h >= nf->src_h / 2))
+ nf->interlaced_uv = 0;
+ else
+ nf->interlaced_uv = 1;
+ } else {
+ nf->interlaced = 1;
+ nf->interlaced_y = 1;
+ nf->interlaced_uv = 1;
+ }
+ break;
+
+ case IVTV_YUV_MODE_INTERLACED: /* Interlace mode */
+ default:
+ nf->interlaced = 1;
+ nf->interlaced_y = 1;
+ nf->interlaced_uv = 1;
+ break;
}
- itv->yuv_info.new_frame_info[frame].update |= register_update;
+ if (memcmp(&yi->old_frame_info_args, nf, sizeof(*nf))) {
+ yi->old_frame_info_args = *nf;
+ nf->update = 1;
+ IVTV_DEBUG_YUV("Requesting reg update for frame %d\n", frame);
+ }
- /* Should this frame be delayed ? */
- if (itv->yuv_info.sync_field[frame] != itv->yuv_info.sync_field[(frame - 1) & 3])
- itv->yuv_info.field_delay[frame] = 1;
- else
- itv->yuv_info.field_delay[frame] = 0;
+ nf->update |= update;
+ nf->sync_field = yi->lace_sync_field;
+ nf->delay = nf->sync_field != of->sync_field;
+}
+/* Frame is complete & ready for display */
+void ivtv_yuv_frame_complete(struct ivtv *itv)
+{
+ atomic_set(&itv->yuv_info.next_fill_frame,
+ (itv->yuv_info.draw_frame + 1) % IVTV_YUV_BUFFERS);
+}
+
+int ivtv_yuv_udma_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
+{
+ DEFINE_WAIT(wait);
+ int rc = 0;
+ int got_sig = 0;
/* DMA the frame */
mutex_lock(&itv->udma.lock);
@@ -1036,10 +1058,10 @@ int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
ivtv_udma_prepare(itv);
prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
/* if no UDMA is pending and no UDMA is in progress, then the DMA
- is finished */
+ is finished */
while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) {
/* don't interrupt if the DMA is in progress but break off
- a still pending DMA. */
+ a still pending DMA. */
got_sig = signal_pending(current);
if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags))
break;
@@ -1057,99 +1079,148 @@ int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
return -EINTR;
}
- atomic_set(&itv->yuv_info.next_fill_frame, next_fill_frame);
+ ivtv_yuv_frame_complete(itv);
mutex_unlock(&itv->udma.lock);
return rc;
}
+/* Setup frame according to V4L2 parameters */
+void ivtv_yuv_setup_stream_frame(struct ivtv *itv)
+{
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ struct ivtv_dma_frame dma_args;
+
+ ivtv_yuv_next_free(itv);
+
+ /* Copy V4L2 parameters to an ivtv_dma_frame struct... */
+ dma_args.y_source = 0L;
+ dma_args.uv_source = 0L;
+ dma_args.src.left = 0;
+ dma_args.src.top = 0;
+ dma_args.src.width = yi->v4l2_src_w;
+ dma_args.src.height = yi->v4l2_src_h;
+ dma_args.dst = yi->main_rect;
+ dma_args.src_width = yi->v4l2_src_w;
+ dma_args.src_height = yi->v4l2_src_h;
+
+ /* ... and use the same setup routine as ivtv_yuv_prep_frame */
+ ivtv_yuv_setup_frame(itv, &dma_args);
+
+ if (!itv->dma_data_req_offset)
+ itv->dma_data_req_offset = yuv_offset[yi->draw_frame];
+}
+
+/* Attempt to dma a frame from a user buffer */
+int ivtv_yuv_udma_stream_frame(struct ivtv *itv, void *src)
+{
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ struct ivtv_dma_frame dma_args;
+
+ ivtv_yuv_setup_stream_frame(itv);
+
+ /* We only need to supply source addresses for this */
+ dma_args.y_source = src;
+ dma_args.uv_source = src + 720 * ((yi->v4l2_src_h + 31) & ~31);
+ return ivtv_yuv_udma_frame(itv, &dma_args);
+}
+
+/* IVTV_IOC_DMA_FRAME ioctl handler */
+int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
+{
+/* IVTV_DEBUG_INFO("yuv_prep_frame\n"); */
+
+ ivtv_yuv_next_free(itv);
+ ivtv_yuv_setup_frame(itv, args);
+ return ivtv_yuv_udma_frame(itv, args);
+}
+
void ivtv_yuv_close(struct ivtv *itv)
{
+ struct yuv_playback_info *yi = &itv->yuv_info;
int h_filter, v_filter_1, v_filter_2;
IVTV_DEBUG_YUV("ivtv_yuv_close\n");
ivtv_waitq(&itv->vsync_waitq);
- atomic_set(&itv->yuv_info.next_dma_frame, -1);
- atomic_set(&itv->yuv_info.next_fill_frame, 0);
+ atomic_set(&yi->next_dma_frame, -1);
+ atomic_set(&yi->next_fill_frame, 0);
/* Reset registers we have changed so mpeg playback works */
/* If we fully restore this register, the display may remain active.
Restore, but set one bit to blank the video. Firmware will always
clear this bit when needed, so not a problem. */
- write_reg(itv->yuv_info.reg_2898 | 0x01000000, 0x2898);
-
- write_reg(itv->yuv_info.reg_2834, 0x02834);
- write_reg(itv->yuv_info.reg_2838, 0x02838);
- write_reg(itv->yuv_info.reg_283c, 0x0283c);
- write_reg(itv->yuv_info.reg_2840, 0x02840);
- write_reg(itv->yuv_info.reg_2844, 0x02844);
- write_reg(itv->yuv_info.reg_2848, 0x02848);
- write_reg(itv->yuv_info.reg_2854, 0x02854);
- write_reg(itv->yuv_info.reg_285c, 0x0285c);
- write_reg(itv->yuv_info.reg_2864, 0x02864);
- write_reg(itv->yuv_info.reg_2870, 0x02870);
- write_reg(itv->yuv_info.reg_2874, 0x02874);
- write_reg(itv->yuv_info.reg_2890, 0x02890);
- write_reg(itv->yuv_info.reg_289c, 0x0289c);
-
- write_reg(itv->yuv_info.reg_2918, 0x02918);
- write_reg(itv->yuv_info.reg_291c, 0x0291c);
- write_reg(itv->yuv_info.reg_2920, 0x02920);
- write_reg(itv->yuv_info.reg_2924, 0x02924);
- write_reg(itv->yuv_info.reg_2928, 0x02928);
- write_reg(itv->yuv_info.reg_292c, 0x0292c);
- write_reg(itv->yuv_info.reg_2930, 0x02930);
- write_reg(itv->yuv_info.reg_2934, 0x02934);
- write_reg(itv->yuv_info.reg_2938, 0x02938);
- write_reg(itv->yuv_info.reg_293c, 0x0293c);
- write_reg(itv->yuv_info.reg_2940, 0x02940);
- write_reg(itv->yuv_info.reg_2944, 0x02944);
- write_reg(itv->yuv_info.reg_2948, 0x02948);
- write_reg(itv->yuv_info.reg_294c, 0x0294c);
- write_reg(itv->yuv_info.reg_2950, 0x02950);
- write_reg(itv->yuv_info.reg_2954, 0x02954);
- write_reg(itv->yuv_info.reg_2958, 0x02958);
- write_reg(itv->yuv_info.reg_295c, 0x0295c);
- write_reg(itv->yuv_info.reg_2960, 0x02960);
- write_reg(itv->yuv_info.reg_2964, 0x02964);
- write_reg(itv->yuv_info.reg_2968, 0x02968);
- write_reg(itv->yuv_info.reg_296c, 0x0296c);
- write_reg(itv->yuv_info.reg_2970, 0x02970);
+ write_reg(yi->reg_2898 | 0x01000000, 0x2898);
+
+ write_reg(yi->reg_2834, 0x02834);
+ write_reg(yi->reg_2838, 0x02838);
+ write_reg(yi->reg_283c, 0x0283c);
+ write_reg(yi->reg_2840, 0x02840);
+ write_reg(yi->reg_2844, 0x02844);
+ write_reg(yi->reg_2848, 0x02848);
+ write_reg(yi->reg_2854, 0x02854);
+ write_reg(yi->reg_285c, 0x0285c);
+ write_reg(yi->reg_2864, 0x02864);
+ write_reg(yi->reg_2870, 0x02870);
+ write_reg(yi->reg_2874, 0x02874);
+ write_reg(yi->reg_2890, 0x02890);
+ write_reg(yi->reg_289c, 0x0289c);
+
+ write_reg(yi->reg_2918, 0x02918);
+ write_reg(yi->reg_291c, 0x0291c);
+ write_reg(yi->reg_2920, 0x02920);
+ write_reg(yi->reg_2924, 0x02924);
+ write_reg(yi->reg_2928, 0x02928);
+ write_reg(yi->reg_292c, 0x0292c);
+ write_reg(yi->reg_2930, 0x02930);
+ write_reg(yi->reg_2934, 0x02934);
+ write_reg(yi->reg_2938, 0x02938);
+ write_reg(yi->reg_293c, 0x0293c);
+ write_reg(yi->reg_2940, 0x02940);
+ write_reg(yi->reg_2944, 0x02944);
+ write_reg(yi->reg_2948, 0x02948);
+ write_reg(yi->reg_294c, 0x0294c);
+ write_reg(yi->reg_2950, 0x02950);
+ write_reg(yi->reg_2954, 0x02954);
+ write_reg(yi->reg_2958, 0x02958);
+ write_reg(yi->reg_295c, 0x0295c);
+ write_reg(yi->reg_2960, 0x02960);
+ write_reg(yi->reg_2964, 0x02964);
+ write_reg(yi->reg_2968, 0x02968);
+ write_reg(yi->reg_296c, 0x0296c);
+ write_reg(yi->reg_2970, 0x02970);
/* Prepare to restore filters */
/* First the horizontal filter */
- if ((itv->yuv_info.reg_2834 & 0x0000FFFF) == (itv->yuv_info.reg_2834 >> 16)) {
+ if ((yi->reg_2834 & 0x0000FFFF) == (yi->reg_2834 >> 16)) {
/* An exact size match uses filter 0 */
h_filter = 0;
- }
- else {
+ } else {
/* Figure out which filter to use */
- h_filter = ((itv->yuv_info.reg_2834 << 16) / (itv->yuv_info.reg_2834 >> 16)) >> 15;
+ h_filter = ((yi->reg_2834 << 16) / (yi->reg_2834 >> 16)) >> 15;
h_filter = (h_filter >> 1) + (h_filter & 1);
/* Only an exact size match can use filter 0. */
- if (h_filter < 1) h_filter = 1;
+ h_filter += !h_filter;
}
/* Now the vertical filter */
- if ((itv->yuv_info.reg_2918 & 0x0000FFFF) == (itv->yuv_info.reg_2918 >> 16)) {
+ if ((yi->reg_2918 & 0x0000FFFF) == (yi->reg_2918 >> 16)) {
/* An exact size match uses filter 0/1 */
v_filter_1 = 0;
v_filter_2 = 1;
- }
- else {
+ } else {
/* Figure out which filter to use */
- v_filter_1 = ((itv->yuv_info.reg_2918 << 16) / (itv->yuv_info.reg_2918 >> 16)) >> 15;
+ v_filter_1 = ((yi->reg_2918 << 16) / (yi->reg_2918 >> 16)) >> 15;
v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1);
/* Only an exact size match can use filter 0 */
- if (v_filter_1 == 0) v_filter_1 = 1;
+ v_filter_1 += !v_filter_1;
v_filter_2 = v_filter_1;
}
/* Now restore the filters */
- ivtv_yuv_filter (itv,h_filter,v_filter_1,v_filter_2);
+ ivtv_yuv_filter(itv, h_filter, v_filter_1, v_filter_2);
/* and clear a few registers */
write_reg(0, 0x02814);
@@ -1158,19 +1229,18 @@ void ivtv_yuv_close(struct ivtv *itv)
write_reg(0, 0x02910);
/* Release the blanking buffer */
- if (itv->yuv_info.blanking_ptr) {
- kfree (itv->yuv_info.blanking_ptr);
- itv->yuv_info.blanking_ptr = NULL;
- pci_unmap_single(itv->dev, itv->yuv_info.blanking_dmaptr, 720*16, PCI_DMA_TODEVICE);
+ if (yi->blanking_ptr) {
+ kfree(yi->blanking_ptr);
+ yi->blanking_ptr = NULL;
+ pci_unmap_single(itv->dev, yi->blanking_dmaptr, 720*16, PCI_DMA_TODEVICE);
}
/* Invalidate the old dimension information */
- itv->yuv_info.old_frame_info.src_w = 0;
- itv->yuv_info.old_frame_info.src_h = 0;
- itv->yuv_info.old_frame_info_args.src_w = 0;
- itv->yuv_info.old_frame_info_args.src_h = 0;
+ yi->old_frame_info.src_w = 0;
+ yi->old_frame_info.src_h = 0;
+ yi->old_frame_info_args.src_w = 0;
+ yi->old_frame_info_args.src_h = 0;
/* All done. */
clear_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags);
}
-
diff --git a/linux/drivers/media/video/ivtv/ivtv-yuv.h b/linux/drivers/media/video/ivtv/ivtv-yuv.h
index 3b966f0a2..2fe5f1250 100644
--- a/linux/drivers/media/video/ivtv/ivtv-yuv.h
+++ b/linux/drivers/media/video/ivtv/ivtv-yuv.h
@@ -21,11 +21,6 @@
#ifndef IVTV_YUV_H
#define IVTV_YUV_H
-/* Buffers on hardware offsets */
-#define IVTV_YUV_BUFFER_OFFSET 0x001a8600 /* First YUV Buffer */
-#define IVTV_YUV_BUFFER_OFFSET_1 0x00240400 /* Second YUV Buffer */
-#define IVTV_YUV_BUFFER_OFFSET_2 0x002d8200 /* Third YUV Buffer */
-#define IVTV_YUV_BUFFER_OFFSET_3 0x00370000 /* Fourth YUV Buffer */
#define IVTV_YUV_BUFFER_UV_OFFSET 0x65400 /* Offset to UV Buffer */
/* Offset to filter table in firmware */
@@ -36,11 +31,14 @@
#define IVTV_YUV_UPDATE_VERTICAL 0x02
#define IVTV_YUV_UPDATE_INVALID 0x04
-extern const u32 yuv_offset[4];
+extern const u32 yuv_offset[IVTV_YUV_BUFFERS];
int ivtv_yuv_filter_check(struct ivtv *itv);
+void ivtv_yuv_setup_stream_frame(struct ivtv *itv);
+int ivtv_yuv_udma_stream_frame(struct ivtv *itv, void *src);
+void ivtv_yuv_frame_complete(struct ivtv *itv);
int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args);
void ivtv_yuv_close(struct ivtv *itv);
-void ivtv_yuv_work_handler (struct ivtv *itv);
+void ivtv_yuv_work_handler(struct ivtv *itv);
#endif
diff --git a/linux/drivers/media/video/ivtv/ivtvfb.c b/linux/drivers/media/video/ivtv/ivtvfb.c
index 52ffd154a..1a73038ea 100644
--- a/linux/drivers/media/video/ivtv/ivtvfb.c
+++ b/linux/drivers/media/video/ivtv/ivtvfb.c
@@ -504,6 +504,10 @@ static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var)
ivtvfb_set_display_window(itv, &ivtv_window);
+ /* Pass screen size back to yuv handler */
+ itv->yuv_info.osd_full_w = ivtv_osd.pixel_stride;
+ itv->yuv_info.osd_full_h = ivtv_osd.lines;
+
/* Force update of yuv registers */
itv->yuv_info.yuv_forced_update = 1;
diff --git a/linux/drivers/media/video/pvrusb2/Makefile b/linux/drivers/media/video/pvrusb2/Makefile
index 69b3e43cd..47284e558 100644
--- a/linux/drivers/media/video/pvrusb2/Makefile
+++ b/linux/drivers/media/video/pvrusb2/Makefile
@@ -6,7 +6,7 @@ pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \
pvrusb2-encoder.o pvrusb2-video-v4l.o \
pvrusb2-eeprom.o pvrusb2-tuner.o \
pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \
- pvrusb2-ctrl.o pvrusb2-std.o \
+ pvrusb2-ctrl.o pvrusb2-std.o pvrusb2-devattr.o \
pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \
pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \
$(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y)
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c
index 3ed700041..3ecf38d40 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c
@@ -36,34 +36,58 @@ struct pvr2_msp3400_handler {
};
+
+struct routing_scheme {
+ const int *def;
+ unsigned int cnt;
+};
+
+static const int routing_scheme0[] = {
+ [PVR2_CVAL_INPUT_TV] = MSP_INPUT_DEFAULT,
+ [PVR2_CVAL_INPUT_RADIO] = MSP_INPUT(MSP_IN_SCART2,
+ MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART,
+ MSP_DSP_IN_SCART),
+ [PVR2_CVAL_INPUT_COMPOSITE] = MSP_INPUT(MSP_IN_SCART1,
+ MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART,
+ MSP_DSP_IN_SCART),
+ [PVR2_CVAL_INPUT_SVIDEO] = MSP_INPUT(MSP_IN_SCART1,
+ MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART,
+ MSP_DSP_IN_SCART),
+};
+
+static const struct routing_scheme routing_schemes[] = {
+ [PVR2_ROUTING_SCHEME_HAUPPAUGE] = {
+ .def = routing_scheme0,
+ .cnt = ARRAY_SIZE(routing_scheme0),
+ },
+};
+
/* This function selects the correct audio input source */
static void set_stereo(struct pvr2_msp3400_handler *ctxt)
{
struct pvr2_hdw *hdw = ctxt->hdw;
struct v4l2_routing route;
+ const struct routing_scheme *sp;
+ unsigned int sid = hdw->hdw_desc->signal_routing_scheme;
pvr2_trace(PVR2_TRACE_CHIPS,"i2c msp3400 v4l2 set_stereo");
- route.input = MSP_INPUT_DEFAULT;
- route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
- switch (hdw->input_val) {
- case PVR2_CVAL_INPUT_TV:
- break;
- case PVR2_CVAL_INPUT_RADIO:
- /* Assume that msp34xx also handle FM decoding, in which case
- we're still using the tuner. */
- /* HV: actually it is more likely to be the SCART2 input if
- the ivtv experience is any indication. */
- route.input = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
- MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
- break;
- case PVR2_CVAL_INPUT_SVIDEO:
- case PVR2_CVAL_INPUT_COMPOSITE:
- /* SCART 1 input */
- route.input = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
- MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
- break;
+ if ((sid < ARRAY_SIZE(routing_schemes)) &&
+ ((sp = routing_schemes + sid) != 0) &&
+ (hdw->input_val >= 0) &&
+ (hdw->input_val < sp->cnt)) {
+ route.input = sp->def[hdw->input_val];
+ } else {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "*** WARNING *** i2c msp3400 v4l2 set_stereo:"
+ " Invalid routing scheme (%u) and/or input (%d)",
+ sid,hdw->input_val);
+ return;
}
+ route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route);
}
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-context.c b/linux/drivers/media/video/pvrusb2/pvrusb2-context.c
index 80e2e5ea1..e59833f8f 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-context.c
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-context.c
@@ -32,64 +32,32 @@
static void pvr2_context_destroy(struct pvr2_context *mp)
{
- if (mp->hdw) pvr2_hdw_destroy(mp->hdw);
pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr_main id=%p",mp);
- if (mp->workqueue) {
- flush_workqueue(mp->workqueue);
- destroy_workqueue(mp->workqueue);
- }
+ if (mp->hdw) pvr2_hdw_destroy(mp->hdw);
kfree(mp);
}
-static void pvr2_context_trigger_poll(struct pvr2_context *mp)
-{
- queue_work(mp->workqueue,&mp->workpoll);
-}
-
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
-static void pvr2_context_poll(struct pvr2_context *mp)
-#else
-static void pvr2_context_poll(struct work_struct *work)
-#endif
-{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
- struct pvr2_context *mp =
- container_of(work, struct pvr2_context, workpoll);
-#endif
- pvr2_context_enter(mp); do {
- pvr2_hdw_poll(mp->hdw);
- } while (0); pvr2_context_exit(mp);
-}
-
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
-static void pvr2_context_setup(struct pvr2_context *mp)
-#else
-static void pvr2_context_setup(struct work_struct *work)
-#endif
+static void pvr2_context_state_check(struct pvr2_context *mp)
{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
- struct pvr2_context *mp =
- container_of(work, struct pvr2_context, workinit);
+ if (mp->init_flag) return;
+
+ switch (pvr2_hdw_get_state(mp->hdw)) {
+ case PVR2_STATE_WARM: break;
+ case PVR2_STATE_ERROR: break;
+ case PVR2_STATE_READY: break;
+ case PVR2_STATE_RUN: break;
+ default: return;
+ }
-#endif
pvr2_context_enter(mp); do {
- if (!pvr2_hdw_dev_ok(mp->hdw)) break;
- pvr2_hdw_setup(mp->hdw);
- pvr2_hdw_setup_poll_trigger(
- mp->hdw,
- (void (*)(void *))pvr2_context_trigger_poll,
- mp);
- if (!pvr2_hdw_dev_ok(mp->hdw)) break;
- if (!pvr2_hdw_init_ok(mp->hdw)) break;
+ mp->init_flag = !0;
mp->video_stream.stream = pvr2_hdw_get_video_stream(mp->hdw);
if (mp->setup_func) {
mp->setup_func(mp);
}
} while (0); pvr2_context_exit(mp);
-}
+ }
struct pvr2_context *pvr2_context_create(
@@ -109,16 +77,10 @@ struct pvr2_context *pvr2_context_create(
mp = NULL;
goto done;
}
-
- mp->workqueue = create_singlethread_workqueue("pvrusb2");
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
- INIT_WORK(&mp->workinit,(void (*)(void*))pvr2_context_setup,mp);
- INIT_WORK(&mp->workpoll,(void (*)(void*))pvr2_context_poll,mp);
-#else
- INIT_WORK(&mp->workinit, pvr2_context_setup);
- INIT_WORK(&mp->workpoll, pvr2_context_poll);
-#endif
- queue_work(mp->workqueue,&mp->workinit);
+ pvr2_hdw_set_state_callback(mp->hdw,
+ (void (*)(void *))pvr2_context_state_check,
+ mp);
+ pvr2_context_state_check(mp);
done:
return mp;
}
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-context.h b/linux/drivers/media/video/pvrusb2/pvrusb2-context.h
index 0a05ea692..237ece0b9 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-context.h
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-context.h
@@ -53,14 +53,11 @@ struct pvr2_context {
struct semaphore mutex;
#endif
int disconnect_flag;
+ int init_flag;
/* Called after pvr2_context initialization is complete */
void (*setup_func)(struct pvr2_context *);
- /* Work queue overhead for out-of-line processing */
- struct workqueue_struct *workqueue;
- struct work_struct workinit;
- struct work_struct workpoll;
};
struct pvr2_channel {
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
index c7041921a..c64bd6078 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
@@ -50,34 +50,89 @@ struct pvr2_v4l_cx2584x {
};
+struct routing_scheme_item {
+ int vid;
+ int aud;
+};
+
+struct routing_scheme {
+ const struct routing_scheme_item *def;
+ unsigned int cnt;
+};
+
+static const struct routing_scheme_item routing_scheme0[] = {
+ [PVR2_CVAL_INPUT_TV] = {
+ .vid = CX25840_COMPOSITE7,
+ .aud = CX25840_AUDIO8,
+ },
+ [PVR2_CVAL_INPUT_RADIO] = { /* Treat the same as composite */
+ .vid = CX25840_COMPOSITE3,
+ .aud = CX25840_AUDIO_SERIAL,
+ },
+ [PVR2_CVAL_INPUT_COMPOSITE] = {
+ .vid = CX25840_COMPOSITE3,
+ .aud = CX25840_AUDIO_SERIAL,
+ },
+ [PVR2_CVAL_INPUT_SVIDEO] = {
+ .vid = CX25840_SVIDEO1,
+ .aud = CX25840_AUDIO_SERIAL,
+ },
+};
+
+/* Specific to gotview device */
+static const struct routing_scheme_item routing_schemegv[] = {
+ [PVR2_CVAL_INPUT_TV] = {
+ .vid = CX25840_COMPOSITE2,
+ .aud = CX25840_AUDIO5,
+ },
+ [PVR2_CVAL_INPUT_RADIO] = { /* Treat the same as composite */
+ .vid = CX25840_COMPOSITE1,
+ .aud = CX25840_AUDIO_SERIAL,
+ },
+ [PVR2_CVAL_INPUT_COMPOSITE] = {
+ .vid = CX25840_COMPOSITE1,
+ .aud = CX25840_AUDIO_SERIAL,
+ },
+ [PVR2_CVAL_INPUT_SVIDEO] = {
+ .vid = (CX25840_SVIDEO_LUMA3|CX25840_SVIDEO_CHROMA4),
+ .aud = CX25840_AUDIO_SERIAL,
+ },
+};
+
+static const struct routing_scheme routing_schemes[] = {
+ [PVR2_ROUTING_SCHEME_HAUPPAUGE] = {
+ .def = routing_scheme0,
+ .cnt = ARRAY_SIZE(routing_scheme0),
+ },
+ [PVR2_ROUTING_SCHEME_GOTVIEW] = {
+ .def = routing_schemegv,
+ .cnt = ARRAY_SIZE(routing_schemegv),
+ },
+};
+
static void set_input(struct pvr2_v4l_cx2584x *ctxt)
{
struct pvr2_hdw *hdw = ctxt->hdw;
struct v4l2_routing route;
enum cx25840_video_input vid_input;
enum cx25840_audio_input aud_input;
+ const struct routing_scheme *sp;
+ unsigned int sid = hdw->hdw_desc->signal_routing_scheme;
memset(&route,0,sizeof(route));
- switch(hdw->input_val) {
- case PVR2_CVAL_INPUT_TV:
- vid_input = CX25840_COMPOSITE7;
- aud_input = CX25840_AUDIO8;
- break;
- case PVR2_CVAL_INPUT_RADIO: // Treat same as composite
- case PVR2_CVAL_INPUT_COMPOSITE:
- vid_input = CX25840_COMPOSITE3;
- aud_input = CX25840_AUDIO_SERIAL;
- break;
- case PVR2_CVAL_INPUT_SVIDEO:
- vid_input = CX25840_SVIDEO1;
- aud_input = CX25840_AUDIO_SERIAL;
- break;
- default:
- // Just set it to be composite input for now...
- vid_input = CX25840_COMPOSITE3;
- aud_input = CX25840_AUDIO_SERIAL;
- break;
+ if ((sid < ARRAY_SIZE(routing_schemes)) &&
+ ((sp = routing_schemes + sid) != 0) &&
+ (hdw->input_val >= 0) &&
+ (hdw->input_val < sp->cnt)) {
+ vid_input = sp->def[hdw->input_val].vid;
+ aud_input = sp->def[hdw->input_val].aud;
+ } else {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "*** WARNING *** i2c cx2584x set_input:"
+ " Invalid routing scheme (%u) and/or input (%d)",
+ sid,hdw->input_val);
+ return;
}
pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_input vid=0x%x aud=0x%x",
@@ -141,7 +196,7 @@ static const struct pvr2_v4l_cx2584x_ops decoder_ops[] = {
static void decoder_detach(struct pvr2_v4l_cx2584x *ctxt)
{
ctxt->client->handler = NULL;
- ctxt->hdw->decoder_ctrl = NULL;
+ pvr2_hdw_set_decoder(ctxt->hdw,NULL);
kfree(ctxt);
}
@@ -242,7 +297,7 @@ int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *hdw,
ctxt->client = cp;
ctxt->hdw = hdw;
ctxt->stale_mask = (1 << ARRAY_SIZE(decoder_ops)) - 1;
- hdw->decoder_ctrl = &ctxt->ctrl;
+ pvr2_hdw_set_decoder(hdw,&ctxt->ctrl);
cp->handler = &ctxt->handler;
{
/*
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-debug.h b/linux/drivers/media/video/pvrusb2/pvrusb2-debug.h
index da6441b88..fca49d8a9 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-debug.h
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-debug.h
@@ -34,25 +34,26 @@ extern int pvrusb2_debug;
#define PVR2_TRACE_INIT (1 << 5) /* misc initialization steps */
#define PVR2_TRACE_START_STOP (1 << 6) /* Streaming start / stop */
#define PVR2_TRACE_CTL (1 << 7) /* commit of control changes */
-#define PVR2_TRACE_DEBUG (1 << 8) /* Temporary debug code */
-#define PVR2_TRACE_EEPROM (1 << 9) /* eeprom parsing / report */
-#define PVR2_TRACE_STRUCT (1 << 10) /* internal struct creation */
-#define PVR2_TRACE_OPEN_CLOSE (1 << 11) /* application open / close */
-#define PVR2_TRACE_CREG (1 << 12) /* Main critical region entry / exit */
-#define PVR2_TRACE_SYSFS (1 << 13) /* Sysfs driven I/O */
-#define PVR2_TRACE_FIRMWARE (1 << 14) /* firmware upload actions */
-#define PVR2_TRACE_CHIPS (1 << 15) /* chip broadcast operation */
-#define PVR2_TRACE_I2C (1 << 16) /* I2C related stuff */
-#define PVR2_TRACE_I2C_CMD (1 << 17) /* Software commands to I2C modules */
-#define PVR2_TRACE_I2C_CORE (1 << 18) /* I2C core debugging */
-#define PVR2_TRACE_I2C_TRAF (1 << 19) /* I2C traffic through the adapter */
-#define PVR2_TRACE_V4LIOCTL (1 << 20) /* v4l ioctl details */
-#define PVR2_TRACE_ENCODER (1 << 21) /* mpeg2 encoder operation */
-#define PVR2_TRACE_BUF_POOL (1 << 22) /* Track buffer pool management */
-#define PVR2_TRACE_BUF_FLOW (1 << 23) /* Track buffer flow in system */
-#define PVR2_TRACE_DATA_FLOW (1 << 24) /* Track data flow */
-#define PVR2_TRACE_DEBUGIFC (1 << 25) /* Debug interface actions */
-#define PVR2_TRACE_GPIO (1 << 26) /* GPIO state bit changes */
+#define PVR2_TRACE_STATE (1 << 8) /* Device state changes */
+#define PVR2_TRACE_STBITS (1 << 9) /* Individual bit state changes */
+#define PVR2_TRACE_EEPROM (1 << 10) /* eeprom parsing / report */
+#define PVR2_TRACE_STRUCT (1 << 11) /* internal struct creation */
+#define PVR2_TRACE_OPEN_CLOSE (1 << 12) /* application open / close */
+#define PVR2_TRACE_CREG (1 << 13) /* Main critical region entry / exit */
+#define PVR2_TRACE_SYSFS (1 << 14) /* Sysfs driven I/O */
+#define PVR2_TRACE_FIRMWARE (1 << 15) /* firmware upload actions */
+#define PVR2_TRACE_CHIPS (1 << 16) /* chip broadcast operation */
+#define PVR2_TRACE_I2C (1 << 17) /* I2C related stuff */
+#define PVR2_TRACE_I2C_CMD (1 << 18) /* Software commands to I2C modules */
+#define PVR2_TRACE_I2C_CORE (1 << 19) /* I2C core debugging */
+#define PVR2_TRACE_I2C_TRAF (1 << 20) /* I2C traffic through the adapter */
+#define PVR2_TRACE_V4LIOCTL (1 << 21) /* v4l ioctl details */
+#define PVR2_TRACE_ENCODER (1 << 22) /* mpeg2 encoder operation */
+#define PVR2_TRACE_BUF_POOL (1 << 23) /* Track buffer pool management */
+#define PVR2_TRACE_BUF_FLOW (1 << 24) /* Track buffer flow in system */
+#define PVR2_TRACE_DATA_FLOW (1 << 25) /* Track data flow */
+#define PVR2_TRACE_DEBUGIFC (1 << 26) /* Debug interface actions */
+#define PVR2_TRACE_GPIO (1 << 27) /* GPIO state bit changes */
#endif /* __PVRUSB2_HDW_INTERNAL_H */
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
index 6f135f4a2..b0687430f 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
@@ -31,14 +31,6 @@ struct debugifc_mask_item {
unsigned long msk;
};
-static struct debugifc_mask_item mask_items[] = {
- {"ENC_FIRMWARE",(1<<PVR2_SUBSYS_B_ENC_FIRMWARE)},
- {"ENC_CFG",(1<<PVR2_SUBSYS_B_ENC_CFG)},
- {"DIG_RUN",(1<<PVR2_SUBSYS_B_DIGITIZER_RUN)},
- {"USB_RUN",(1<<PVR2_SUBSYS_B_USBSTREAM_RUN)},
- {"ENC_RUN",(1<<PVR2_SUBSYS_B_ENC_RUN)},
-};
-
static unsigned int debugifc_count_whitespace(const char *buf,
unsigned int count)
@@ -148,134 +140,14 @@ static int debugifc_match_keyword(const char *buf,unsigned int count,
}
-static unsigned long debugifc_find_mask(const char *buf,unsigned int count)
-{
- struct debugifc_mask_item *mip;
- unsigned int idx;
- for (idx = 0; idx < ARRAY_SIZE(mask_items); idx++) {
- mip = mask_items + idx;
- if (debugifc_match_keyword(buf,count,mip->name)) {
- return mip->msk;
- }
- }
- return 0;
-}
-
-
-static int debugifc_print_mask(char *buf,unsigned int sz,
- unsigned long msk,unsigned long val)
-{
- struct debugifc_mask_item *mip;
- unsigned int idx;
- int bcnt = 0;
- int ccnt;
- for (idx = 0; idx < ARRAY_SIZE(mask_items); idx++) {
- mip = mask_items + idx;
- if (!(mip->msk & msk)) continue;
- ccnt = scnprintf(buf,sz,"%s%c%s",
- (bcnt ? " " : ""),
- ((mip->msk & val) ? '+' : '-'),
- mip->name);
- sz -= ccnt;
- buf += ccnt;
- bcnt += ccnt;
- }
- return bcnt;
-}
-
-static unsigned int debugifc_parse_subsys_mask(const char *buf,
- unsigned int count,
- unsigned long *mskPtr,
- unsigned long *valPtr)
-{
- const char *wptr;
- unsigned int consume_cnt = 0;
- unsigned int scnt;
- unsigned int wlen;
- int mode;
- unsigned long m1,msk,val;
-
- msk = 0;
- val = 0;
-
- while (count) {
- scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
- if (!scnt) break;
- consume_cnt += scnt; count -= scnt; buf += scnt;
- if (!wptr) break;
-
- mode = 0;
- if (wlen) switch (wptr[0]) {
- case '+':
- wptr++;
- wlen--;
- break;
- case '-':
- mode = 1;
- wptr++;
- wlen--;
- break;
- }
- if (!wlen) continue;
- m1 = debugifc_find_mask(wptr,wlen);
- if (!m1) break;
- msk |= m1;
- if (!mode) val |= m1;
- }
- *mskPtr = msk;
- *valPtr = val;
- return consume_cnt;
-}
-
-
int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt)
{
int bcnt = 0;
int ccnt;
- struct pvr2_hdw_debug_info dbg;
-
- pvr2_hdw_get_debug_info(hdw,&dbg);
-
- ccnt = scnprintf(buf,acnt,"big lock %s; ctl lock %s",
- (dbg.big_lock_held ? "held" : "free"),
- (dbg.ctl_lock_held ? "held" : "free"));
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- if (dbg.ctl_lock_held) {
- ccnt = scnprintf(buf,acnt,"; cmd_state=%d cmd_code=%d"
- " cmd_wlen=%d cmd_rlen=%d"
- " wpend=%d rpend=%d tmout=%d rstatus=%d"
- " wstatus=%d",
- dbg.cmd_debug_state,dbg.cmd_code,
- dbg.cmd_debug_write_len,
- dbg.cmd_debug_read_len,
- dbg.cmd_debug_write_pend,
- dbg.cmd_debug_read_pend,
- dbg.cmd_debug_timeout,
- dbg.cmd_debug_rstatus,
- dbg.cmd_debug_wstatus);
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- }
- ccnt = scnprintf(buf,acnt,"\n");
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = scnprintf(
- buf,acnt,"driver flags: %s %s %s\n",
- (dbg.flag_init_ok ? "initialized" : "uninitialized"),
- (dbg.flag_ok ? "ok" : "fail"),
- (dbg.flag_disconnected ? "disconnected" : "connected"));
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = scnprintf(buf,acnt,"Subsystems enabled / configured: ");
+ ccnt = scnprintf(buf,acnt,"Driver state info:\n");
bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = debugifc_print_mask(buf,acnt,dbg.subsys_flags,dbg.subsys_flags);
+ ccnt = pvr2_hdw_state_report(hdw,buf,acnt);
bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = scnprintf(buf,acnt,"\n");
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = scnprintf(buf,acnt,"Subsystems disabled / unconfigured: ");
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = debugifc_print_mask(buf,acnt,~dbg.subsys_flags,dbg.subsys_flags);
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = scnprintf(buf,acnt,"\n");
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-
ccnt = scnprintf(buf,acnt,"Attached I2C modules:\n");
bcnt += ccnt; acnt -= ccnt; buf += ccnt;
ccnt = pvr2_i2c_report(hdw,buf,acnt);
@@ -290,7 +162,6 @@ int pvr2_debugifc_print_status(struct pvr2_hdw *hdw,
{
int bcnt = 0;
int ccnt;
- unsigned long msk;
int ret;
u32 gpio_dir,gpio_in,gpio_out;
@@ -311,28 +182,6 @@ int pvr2_debugifc_print_status(struct pvr2_hdw *hdw,
pvr2_hdw_get_streaming(hdw) ? "on" : "off");
bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- msk = pvr2_hdw_subsys_get(hdw);
- ccnt = scnprintf(buf,acnt,"Subsystems enabled / configured: ");
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = debugifc_print_mask(buf,acnt,msk,msk);
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = scnprintf(buf,acnt,"\n");
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = scnprintf(buf,acnt,"Subsystems disabled / unconfigured: ");
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = debugifc_print_mask(buf,acnt,~msk,msk);
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = scnprintf(buf,acnt,"\n");
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-
- msk = pvr2_hdw_subsys_stream_get(hdw);
- ccnt = scnprintf(buf,acnt,"Subsystems stopped on stream shutdown: ");
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = debugifc_print_mask(buf,acnt,msk,msk);
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = scnprintf(buf,acnt,"\n");
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-
return bcnt;
}
@@ -369,28 +218,10 @@ static int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf,
return pvr2_upload_firmware2(hdw);
} else if (debugifc_match_keyword(wptr,wlen,"decoder")) {
return pvr2_hdw_cmd_decoder_reset(hdw);
+ } else if (debugifc_match_keyword(wptr,wlen,"worker")) {
+ return pvr2_hdw_untrip(hdw);
}
return -EINVAL;
- } else if (debugifc_match_keyword(wptr,wlen,"subsys_flags")) {
- unsigned long msk = 0;
- unsigned long val = 0;
- if (debugifc_parse_subsys_mask(buf,count,&msk,&val) != count) {
- pvr2_trace(PVR2_TRACE_DEBUGIFC,
- "debugifc parse error on subsys mask");
- return -EINVAL;
- }
- pvr2_hdw_subsys_bit_chg(hdw,msk,val);
- return 0;
- } else if (debugifc_match_keyword(wptr,wlen,"stream_flags")) {
- unsigned long msk = 0;
- unsigned long val = 0;
- if (debugifc_parse_subsys_mask(buf,count,&msk,&val) != count) {
- pvr2_trace(PVR2_TRACE_DEBUGIFC,
- "debugifc parse error on stream mask");
- return -EINVAL;
- }
- pvr2_hdw_subsys_stream_bit_chg(hdw,msk,val);
- return 0;
} else if (debugifc_match_keyword(wptr,wlen,"cpufw")) {
scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
if (!scnt) return -EINVAL;
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c
new file mode 100644
index 000000000..eed64571c
--- /dev/null
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c
@@ -0,0 +1,125 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2007 Mike Isely <isely@pobox.com>
+ *
+ * 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
+ *
+ * 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
+ *
+ */
+
+/*
+
+This source file should encompass ALL per-device type information for the
+driver. To define a new device, add elements to the pvr2_device_table and
+pvr2_device_desc structures.
+
+*/
+
+#include "pvrusb2-devattr.h"
+#include <linux/usb.h>
+/* This is needed in order to pull in tuner type ids... */
+#include <linux/i2c.h>
+#include <media/tuner.h>
+
+/* Known major hardware variants, keyed from device ID */
+#define PVR2_HDW_TYPE_29XXX 0
+#define PVR2_HDW_TYPE_24XXX 1
+#define PVR2_HDW_TYPE_GOTVIEW_2 2
+
+struct usb_device_id pvr2_device_table[] = {
+ [PVR2_HDW_TYPE_29XXX] = { USB_DEVICE(0x2040, 0x2900) },
+ [PVR2_HDW_TYPE_24XXX] = { USB_DEVICE(0x2040, 0x2400) },
+ [PVR2_HDW_TYPE_GOTVIEW_2] = { USB_DEVICE(0x1164, 0x0622) },
+ { }
+};
+
+/* Names of other client modules to request for 24xxx model hardware */
+static const char *pvr2_client_24xxx[] = {
+ "cx25840",
+ "tuner",
+ "wm8775",
+};
+
+/* Names of other client modules to request for 29xxx model hardware */
+static const char *pvr2_client_29xxx[] = {
+ "msp3400",
+ "saa7115",
+ "tuner",
+};
+
+// Names of other client modules to request for Gotview 2 model hardware
+static const char *pvr2_client_gotview_2[] = {
+ "cx25840",
+ "tuner",
+};
+
+/* Firmware file name(s) for 29xxx devices */
+static const char *pvr2_fw1_names_29xxx[] = {
+ "v4l-pvrusb2-29xxx-01.fw",
+};
+
+/* Firmware file name(s) for 24xxx devices */
+static const char *pvr2_fw1_names_24xxx[] = {
+ "v4l-pvrusb2-24xxx-01.fw",
+};
+
+const struct pvr2_device_desc pvr2_device_descriptions[] = {
+ [PVR2_HDW_TYPE_29XXX] = {
+ .description = "WinTV PVR USB2 Model Category 29xxxx",
+ .shortname = "29xxx",
+ .client_modules.lst = pvr2_client_29xxx,
+ .client_modules.cnt = ARRAY_SIZE(pvr2_client_29xxx),
+ .fx2_firmware.lst = pvr2_fw1_names_29xxx,
+ .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_29xxx),
+ .flag_has_hauppauge_rom = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+ },
+ [PVR2_HDW_TYPE_24XXX] = {
+ .description = "WinTV PVR USB2 Model Category 24xxxx",
+ .shortname = "24xxx",
+ .client_modules.lst = pvr2_client_24xxx,
+ .client_modules.cnt = ARRAY_SIZE(pvr2_client_24xxx),
+ .fx2_firmware.lst = pvr2_fw1_names_24xxx,
+ .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_24xxx),
+ .flag_has_cx25840 = !0,
+ .flag_has_wm8775 = !0,
+ .flag_has_hauppauge_rom = !0,
+ .flag_has_hauppauge_custom_ir = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+ },
+ [PVR2_HDW_TYPE_GOTVIEW_2] = {
+ .description = "Gotview USB 2.0 DVD 2",
+ .shortname = "gv2",
+ .client_modules.lst = pvr2_client_gotview_2,
+ .client_modules.cnt = ARRAY_SIZE(pvr2_client_gotview_2),
+ .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW,
+ },
+};
+
+const unsigned int pvr2_device_count = ARRAY_SIZE(pvr2_device_descriptions);
+
+MODULE_DEVICE_TABLE(usb, pvr2_device_table);
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h
new file mode 100644
index 000000000..a9c3d99b6
--- /dev/null
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h
@@ -0,0 +1,121 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * 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
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_DEVATTR_H
+#define __PVRUSB2_DEVATTR_H
+
+#include <linux/mod_devicetable.h>
+#include <linux/videodev2.h>
+
+/*
+
+ This header defines structures used to describe attributes of a device.
+
+*/
+
+
+struct pvr2_string_table {
+ const char **lst;
+ unsigned int cnt;
+};
+
+#define PVR2_ROUTING_SCHEME_HAUPPAUGE 0
+#define PVR2_ROUTING_SCHEME_GOTVIEW 1
+
+/* This describes a particular hardware type (except for the USB device ID
+ which must live in a separate structure due to environmental
+ constraints). See the top of pvrusb2-hdw.c for where this is
+ instantiated. */
+struct pvr2_device_desc {
+ /* Single line text description of hardware */
+ const char *description;
+
+ /* Single token identifier for hardware */
+ const char *shortname;
+
+ /* List of additional client modules we need to load */
+ struct pvr2_string_table client_modules;
+
+ /* List of FX2 firmware file names we should search; if empty then
+ FX2 firmware check / load is skipped and we assume the device
+ was initialized from internal ROM. */
+ struct pvr2_string_table fx2_firmware;
+
+ /* Signal routing scheme used by device, contains one of
+ PVR2_ROUTING_SCHEME_XXX. Schemes have to be defined as we
+ encounter them. This is an arbitrary integer scheme id; its
+ meaning is contained entirely within the driver and is
+ interpreted by logic which must send commands to the chip-level
+ drivers (search for things which touch this field). */
+ unsigned int signal_routing_scheme;
+
+ /* V4L tuner type ID to use with this device (only used if the
+ driver could not discover the type any other way). */
+ int default_tuner_type;
+
+ /* Initial standard bits to use for this device, if not zero.
+ Anything set here is also implied as an available standard.
+ Note: This is ignored if overridden on the module load line via
+ the video_std module option. */
+ v4l2_std_id default_std_mask;
+
+ /* If set, we don't bother trying to load cx23416 firmware. */
+ char flag_skip_cx23416_firmware;
+
+ /* Device has a hauppauge eeprom which we can interrogate. */
+ char flag_has_hauppauge_rom;
+
+ /* Device does not require a powerup command to be issued. */
+ char flag_no_powerup;
+
+ /* Device has a cx25840 - this enables special additional logic to
+ handle it. */
+ char flag_has_cx25840;
+
+ /* Device has a wm8775 - this enables special additional logic to
+ ensure that it is found. */
+ char flag_has_wm8775;
+
+ /* Device has IR hardware that can be faked into looking like a
+ normal Hauppauge i2c IR receiver. This is currently very
+ specific to the 24xxx device, where Hauppauge had replaced their
+ 'standard' I2C IR receiver with a bunch of FPGA logic controlled
+ directly via the FX2. Turning this on tells the pvrusb2 driver
+ to virtualize the presence of the non-existant IR receiver chip and
+ implement the virtual receiver in terms of appropriate FX2
+ commands. */
+ char flag_has_hauppauge_custom_ir;
+};
+
+extern const struct pvr2_device_desc pvr2_device_descriptions[];
+extern struct usb_device_id pvr2_device_table[];
+extern const unsigned int pvr2_device_count;
+
+#endif /* __PVRUSB2_HDW_INTERNAL_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c
index bd5feef0e..b831559bd 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c
@@ -145,6 +145,7 @@ int pvr2_eeprom_analyze(struct pvr2_hdw *hdw)
trace_eeprom("serial_number=%d",tvdata.serial_number);
trace_eeprom("rev_str=%s",tvdata.rev_str);
hdw->tuner_type = tvdata.tuner_type;
+ hdw->tuner_updated = !0;
hdw->serial_number = tvdata.serial_number;
hdw->std_mask_eeprom = tvdata.tuner_formats;
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c
index 7a27dece2..d71bb3dff 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c
@@ -231,7 +231,7 @@ static int pvr2_encoder_cmd(void *ctxt,
LOCK_TAKE(hdw->ctl_lock); do {
- if (!hdw->flag_encoder_ok) {
+ if (!hdw->state_encoder_ok) {
ret = -EIO;
break;
}
@@ -300,12 +300,15 @@ static int pvr2_encoder_cmd(void *ctxt,
ret = -EBUSY;
}
if (ret) {
- hdw->flag_encoder_ok = 0;
+ hdw->state_encoder_ok = 0;
+ pvr2_trace(PVR2_TRACE_STBITS,
+ "State bit %s <-- %s",
+ "state_encoder_ok",
+ (hdw->state_encoder_ok ? "true" : "false"));
pvr2_trace(
PVR2_TRACE_ERROR_LEGS,
"Giving up on command."
- " It is likely that"
- " this is a bad idea...");
+ " This is normally recovered by the driver.");
break;
}
wrData[0] = 0x7;
@@ -399,13 +402,13 @@ static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw)
/* This ENC_MISC(3,encMisc3Arg) command is critical - without
it there will eventually be video corruption. Also, the
- 29xxx case is strange - the Windows driver is passing 1
- regardless of device type but if we have 1 for 29xxx device
- the video turns sluggish. */
- switch (hdw->hdw_type) {
- case PVR2_HDW_TYPE_24XXX: encMisc3Arg = 1; break;
- case PVR2_HDW_TYPE_29XXX: encMisc3Arg = 0; break;
- default: break;
+ saa7115 case is strange - the Windows driver is passing 1
+ regardless of device type but if we have 1 for saa7115
+ devices the video turns sluggish. */
+ if (hdw->hdw_desc->flag_has_cx25840) {
+ encMisc3Arg = 1;
+ } else {
+ encMisc3Arg = 0;
}
ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3,
encMisc3Arg,0,0);
@@ -427,6 +430,24 @@ static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw)
return ret;
}
+int pvr2_encoder_adjust(struct pvr2_hdw *hdw)
+{
+ int ret;
+ ret = cx2341x_update(hdw,pvr2_encoder_cmd,
+ (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL),
+ &hdw->enc_ctl_state);
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Error from cx2341x module code=%d",ret);
+ } else {
+ memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state,
+ sizeof(struct cx2341x_mpeg_params));
+ hdw->enc_cur_valid = !0;
+ }
+ return ret;
+}
+
+
int pvr2_encoder_configure(struct pvr2_hdw *hdw)
{
int ret;
@@ -445,7 +466,7 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw)
/* saa7115: 0xf0 */
val = 0xf0;
- if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) {
+ if (hdw->hdw_desc->flag_has_cx25840) {
/* ivtv cx25840: 0x140 */
val = 0x140;
}
@@ -469,18 +490,10 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw)
return ret;
}
- ret = cx2341x_update(hdw,pvr2_encoder_cmd,
- (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL),
- &hdw->enc_ctl_state);
- if (ret) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Error from cx2341x module code=%d",ret);
- return ret;
- }
-
- ret = 0;
+ ret = pvr2_encoder_adjust(hdw);
+ if (ret) return ret;
- if (!ret) ret = pvr2_encoder_vcmd(
+ ret = pvr2_encoder_vcmd(
hdw, CX2341X_ENC_INITIALIZE_INPUT, 0);
if (ret) {
@@ -489,10 +502,6 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw)
return ret;
}
- hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG);
- memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state,
- sizeof(struct cx2341x_mpeg_params));
- hdw->enc_cur_valid = !0;
return 0;
}
@@ -511,7 +520,7 @@ int pvr2_encoder_start(struct pvr2_hdw *hdw)
pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1,
hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0);
- switch (hdw->config) {
+ switch (hdw->active_stream_type) {
case pvr2_config_vbi:
status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
0x01,0x14);
@@ -525,9 +534,6 @@ int pvr2_encoder_start(struct pvr2_hdw *hdw)
0,0x13);
break;
}
- if (!status) {
- hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_RUN);
- }
return status;
}
@@ -538,7 +544,7 @@ int pvr2_encoder_stop(struct pvr2_hdw *hdw)
/* mask all interrupts */
pvr2_write_register(hdw, 0x0048, 0xffffffff);
- switch (hdw->config) {
+ switch (hdw->active_stream_type) {
case pvr2_config_vbi:
status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
0x01,0x01,0x14);
@@ -559,9 +565,6 @@ int pvr2_encoder_stop(struct pvr2_hdw *hdw)
pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000401);
pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000);
- if (!status) {
- hdw->subsys_enabled_mask &= ~(1<<PVR2_SUBSYS_B_ENC_RUN);
- }
return status;
}
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.h b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.h
index 01b5a0b89..54caf2e3c 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.h
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.h
@@ -25,6 +25,7 @@
struct pvr2_hdw;
+int pvr2_encoder_adjust(struct pvr2_hdw *);
int pvr2_encoder_configure(struct pvr2_hdw *);
int pvr2_encoder_start(struct pvr2_hdw *);
int pvr2_encoder_stop(struct pvr2_hdw *);
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
index fbe5d46d6..dfaa347b1 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
@@ -35,6 +35,7 @@
#include <linux/videodev2.h>
#include <linux/i2c.h>
+#include <linux/workqueue.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
#include <linux/mutex.h>
#else
@@ -43,6 +44,7 @@
#include "pvrusb2-hdw.h"
#include "pvrusb2-io.h"
#include <media/cx2341x.h>
+#include "pvrusb2-devattr.h"
/* Legal values for PVR2_CID_HSM */
#define PVR2_CVAL_HSM_FAIL 0
@@ -165,10 +167,6 @@ struct pvr2_decoder_ctrl {
#define FW1_STATE_RELOAD 3
#define FW1_STATE_OK 4
-/* Known major hardware variants, keyed from device ID */
-#define PVR2_HDW_TYPE_29XXX 0
-#define PVR2_HDW_TYPE_24XXX 1
-
typedef int (*pvr2_i2c_func)(struct pvr2_hdw *,u8,u8 *,u16,u8 *, u16);
#define PVR2_I2C_FUNC_CNT 128
@@ -180,8 +178,15 @@ struct pvr2_hdw {
struct usb_device *usb_dev;
struct usb_interface *usb_intf;
- /* Device type, one of PVR2_HDW_TYPE_xxxxx */
- unsigned int hdw_type;
+ /* Device description, anything that must adjust behavior based on
+ device specific info will use information held here. */
+ const struct pvr2_device_desc *hdw_desc;
+
+ /* Kernel worker thread handling */
+ struct workqueue_struct *workqueue;
+ struct work_struct workpoll; /* Update driver state */
+ struct work_struct worki2csync; /* Update i2c clients */
+ struct work_struct workinit; /* Driver initialization sequence */
/* Video spigot */
struct pvr2_stream *vid_stream;
@@ -194,9 +199,6 @@ struct pvr2_hdw {
#endif
int big_lock_held; /* For debugging */
- void (*poll_trigger_func)(void *);
- void *poll_trigger_data;
-
char name[32];
/* I2C stuff */
@@ -231,9 +233,9 @@ struct pvr2_hdw {
struct urb *ctl_read_urb;
unsigned char *ctl_write_buffer;
unsigned char *ctl_read_buffer;
- volatile int ctl_write_pend_flag;
- volatile int ctl_read_pend_flag;
- volatile int ctl_timeout_flag;
+ int ctl_write_pend_flag;
+ int ctl_read_pend_flag;
+ int ctl_timeout_flag;
struct completion ctl_done;
unsigned char cmd_buffer[PVR2_CTL_BUFFSIZE];
int cmd_debug_state; // Low level command debugging info
@@ -241,14 +243,48 @@ struct pvr2_hdw {
unsigned int cmd_debug_write_len; //
unsigned int cmd_debug_read_len; //
+ /* Bits of state that describe what is going on with various parts
+ of the driver. */
+ int state_encoder_ok; /* Encoder is operational */
+ int state_encoder_run; /* Encoder is running */
+ int state_encoder_config; /* Encoder is configured */
+ int state_encoder_waitok; /* Encoder pre-wait done */
+ int state_decoder_run; /* Decoder is running */
+ int state_usbstream_run; /* FX2 is streaming */
+ int state_decoder_quiescent; /* Decoder idle for > 50msec */
+ int state_pipeline_config; /* Pipeline is configured */
+ int state_pipeline_req; /* Somebody wants to stream */
+ int state_pipeline_pause; /* Pipeline must be paused */
+ int state_pipeline_idle; /* Pipeline not running */
+
+ /* This is the master state of the driver. It is the combined
+ result of other bits of state. Examining this will indicate the
+ overall state of the driver. Values here are one of
+ PVR2_STATE_xxxx */
+ unsigned int master_state;
+
+ /* True if states must be re-evaluated */
+ int state_stale;
+
+ void (*state_func)(void *);
+ void *state_data;
+
+ /* Timer for measuring decoder settling time */
+ struct timer_list quiescent_timer;
+
+ /* Timer for measuring encoder pre-wait time */
+ struct timer_list encoder_wait_timer;
+
+ /* Place to block while waiting for state changes */
+ wait_queue_head_t state_wait_data;
+
+
int flag_ok; /* device in known good state */
int flag_disconnected; /* flag_ok == 0 due to disconnect */
int flag_init_ok; /* true if structure is fully initialized */
- int flag_streaming_enabled; /* true if streaming should be on */
int fw1_state; /* current situation with fw1 */
- int flag_encoder_ok; /* True if encoder is healthy */
-
- int flag_decoder_is_tuned;
+ int flag_decoder_missed;/* We've noticed missing decoder */
+ int flag_tripped; /* Indicates overall failure to start */
struct pvr2_decoder_ctrl *decoder_ctrl;
@@ -257,12 +293,6 @@ struct pvr2_hdw {
unsigned int fw_size;
int fw_cpu_flag; /* True if we are dealing with the CPU */
- // Which subsystem pieces have been enabled / configured
- unsigned long subsys_enabled_mask;
-
- // Which subsystems are manipulated to enable streaming
- unsigned long subsys_stream_mask;
-
// True if there is a request to trigger logging of state in each
// module.
int log_requested;
@@ -312,13 +342,16 @@ struct pvr2_hdw {
/* Location of eeprom or a negative number if none */
int eeprom_addr;
- enum pvr2_config config;
+ enum pvr2_config active_stream_type;
+ enum pvr2_config desired_stream_type;
/* Control state needed for cx2341x module */
struct cx2341x_mpeg_params enc_cur_state;
struct cx2341x_mpeg_params enc_ctl_state;
/* True if an encoder attribute has changed */
int enc_stale;
+ /* True if an unsafe encoder attribute has changed */
+ int enc_unsafe_stale;
/* True if enc_cur_state is valid */
int enc_cur_valid;
@@ -348,6 +381,7 @@ struct pvr2_hdw {
/* This function gets the current frequency */
unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *);
+void pvr2_hdw_set_decoder(struct pvr2_hdw *,struct pvr2_decoder_ctrl *);
#endif /* __PVRUSB2_HDW_INTERNAL_H */
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index 328bd1ba6..22d9c8f5c 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -42,47 +42,6 @@
#define TV_MIN_FREQ 55250000L
#define TV_MAX_FREQ 850000000L
-struct usb_device_id pvr2_device_table[] = {
- [PVR2_HDW_TYPE_29XXX] = { USB_DEVICE(0x2040, 0x2900) },
- [PVR2_HDW_TYPE_24XXX] = { USB_DEVICE(0x2040, 0x2400) },
- { }
-};
-
-MODULE_DEVICE_TABLE(usb, pvr2_device_table);
-
-static const char *pvr2_device_names[] = {
- [PVR2_HDW_TYPE_29XXX] = "WinTV PVR USB2 Model Category 29xxxx",
- [PVR2_HDW_TYPE_24XXX] = "WinTV PVR USB2 Model Category 24xxxx",
-};
-
-struct pvr2_string_table {
- const char **lst;
- unsigned int cnt;
-};
-
-// Names of other client modules to request for 24xxx model hardware
-static const char *pvr2_client_24xxx[] = {
- "cx25840",
- "tuner",
- "wm8775",
-};
-
-// Names of other client modules to request for 29xxx model hardware
-static const char *pvr2_client_29xxx[] = {
- "msp3400",
- "saa7115",
- "tuner",
-};
-
-static struct pvr2_string_table pvr2_client_lists[] = {
- [PVR2_HDW_TYPE_29XXX] = {
- pvr2_client_29xxx, ARRAY_SIZE(pvr2_client_29xxx)
- },
- [PVR2_HDW_TYPE_24XXX] = {
- pvr2_client_24xxx, ARRAY_SIZE(pvr2_client_24xxx)
- },
-};
-
static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL};
static DEFINE_MUTEX(pvr2_unit_mtx);
@@ -247,32 +206,46 @@ static const char *control_values_hsm[] = {
};
-static const char *control_values_subsystem[] = {
- [PVR2_SUBSYS_B_ENC_FIRMWARE] = "enc_firmware",
- [PVR2_SUBSYS_B_ENC_CFG] = "enc_config",
- [PVR2_SUBSYS_B_DIGITIZER_RUN] = "digitizer_run",
- [PVR2_SUBSYS_B_USBSTREAM_RUN] = "usbstream_run",
- [PVR2_SUBSYS_B_ENC_RUN] = "enc_run",
+static const char *pvr2_state_names[] = {
+ [PVR2_STATE_NONE] = "none",
+ [PVR2_STATE_DEAD] = "dead",
+ [PVR2_STATE_COLD] = "cold",
+ [PVR2_STATE_WARM] = "warm",
+ [PVR2_STATE_ERROR] = "error",
+ [PVR2_STATE_READY] = "ready",
+ [PVR2_STATE_RUN] = "run",
};
+
+static void pvr2_hdw_state_sched(struct pvr2_hdw *);
+static int pvr2_hdw_state_eval(struct pvr2_hdw *);
static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long);
+static void pvr2_hdw_worker_i2c(struct work_struct *work);
+static void pvr2_hdw_worker_poll(struct work_struct *work);
+static void pvr2_hdw_worker_init(struct work_struct *work);
+static int pvr2_hdw_wait(struct pvr2_hdw *,int state);
+static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *);
+static void pvr2_hdw_state_log_state(struct pvr2_hdw *);
static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl);
-static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw);
+static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw);
static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw);
static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw);
static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw);
-static void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *hdw);
-static void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw,
- unsigned long msk,
- unsigned long val);
-static void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw,
- unsigned long msk,
- unsigned long val);
+static void pvr2_hdw_quiescent_timeout(unsigned long);
+static void pvr2_hdw_encoder_wait_timeout(unsigned long);
static int pvr2_send_request_ex(struct pvr2_hdw *hdw,
unsigned int timeout,int probe_fl,
void *write_data,unsigned int write_len,
void *read_data,unsigned int read_len);
+
+static void trace_stbit(const char *name,int val)
+{
+ pvr2_trace(PVR2_TRACE_STBITS,
+ "State bit %s <-- %s",
+ name,(val ? "true" : "false"));
+}
+
static int ctrl_channelfreq_get(struct pvr2_ctrl *cptr,int *vp)
{
struct pvr2_hdw *hdw = cptr->hdw;
@@ -381,8 +354,8 @@ static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp)
static int ctrl_vres_min_get(struct pvr2_ctrl *cptr,int *vp)
{
- /* Actual minimum depends on device type. */
- if (cptr->hdw->hdw_type == PVR2_HDW_TYPE_24XXX) {
+ /* Actual minimum depends on device digitizer type. */
+ if (cptr->hdw->hdw_desc->flag_has_cx25840) {
*vp = 75;
} else {
*vp = 17;
@@ -481,6 +454,7 @@ static int ctrl_cx2341x_is_dirty(struct pvr2_ctrl *cptr)
static void ctrl_cx2341x_clear_dirty(struct pvr2_ctrl *cptr)
{
cptr->hdw->enc_stale = 0;
+ cptr->hdw->enc_unsafe_stale = 0;
}
static int ctrl_cx2341x_get(struct pvr2_ctrl *cptr,int *vp)
@@ -503,6 +477,7 @@ static int ctrl_cx2341x_get(struct pvr2_ctrl *cptr,int *vp)
static int ctrl_cx2341x_set(struct pvr2_ctrl *cptr,int m,int v)
{
int ret;
+ struct pvr2_hdw *hdw = cptr->hdw;
struct v4l2_ext_controls cs;
struct v4l2_ext_control c1;
memset(&cs,0,sizeof(cs));
@@ -511,10 +486,22 @@ static int ctrl_cx2341x_set(struct pvr2_ctrl *cptr,int m,int v)
cs.count = 1;
c1.id = cptr->info->v4l_id;
c1.value = v;
- ret = cx2341x_ext_ctrls(&cptr->hdw->enc_ctl_state, 0, &cs,
+ ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state,
+ hdw->state_encoder_run, &cs,
VIDIOC_S_EXT_CTRLS);
+ if (ret == -EBUSY) {
+ /* Oops. cx2341x is telling us it's not safe to change
+ this control while we're capturing. Make a note of this
+ fact so that the pipeline will be stopped the next time
+ controls are committed. Then go on ahead and store this
+ change anyway. */
+ ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state,
+ 0, &cs,
+ VIDIOC_S_EXT_CTRLS);
+ if (!ret) hdw->enc_unsafe_stale = !0;
+ }
if (ret) return ret;
- cptr->hdw->enc_stale = !0;
+ hdw->enc_stale = !0;
return 0;
}
@@ -545,7 +532,13 @@ static unsigned int ctrl_cx2341x_getv4lflags(struct pvr2_ctrl *cptr)
static int ctrl_streamingenabled_get(struct pvr2_ctrl *cptr,int *vp)
{
- *vp = cptr->hdw->flag_streaming_enabled;
+ *vp = cptr->hdw->state_pipeline_req;
+ return 0;
+}
+
+static int ctrl_masterstate_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ *vp = cptr->hdw->master_state;
return 0;
}
@@ -658,29 +651,6 @@ static int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp)
return 0;
}
-static int ctrl_subsys_get(struct pvr2_ctrl *cptr,int *vp)
-{
- *vp = cptr->hdw->subsys_enabled_mask;
- return 0;
-}
-
-static int ctrl_subsys_set(struct pvr2_ctrl *cptr,int m,int v)
-{
- pvr2_hdw_subsys_bit_chg_no_lock(cptr->hdw,m,v);
- return 0;
-}
-
-static int ctrl_subsys_stream_get(struct pvr2_ctrl *cptr,int *vp)
-{
- *vp = cptr->hdw->subsys_stream_mask;
- return 0;
-}
-
-static int ctrl_subsys_stream_set(struct pvr2_ctrl *cptr,int m,int v)
-{
- pvr2_hdw_subsys_stream_bit_chg_no_lock(cptr->hdw,m,v);
- return 0;
-}
static int ctrl_stdenumcur_set(struct pvr2_ctrl *cptr,int m,int v)
{
@@ -916,6 +886,11 @@ static const struct pvr2_ctl_info control_defs[] = {
.get_value = ctrl_hsm_get,
DEFENUM(control_values_hsm),
},{
+ .desc = "Master State",
+ .name = "master_state",
+ .get_value = ctrl_masterstate_get,
+ DEFENUM(pvr2_state_names),
+ },{
.desc = "Signal Present",
.name = "signal_present",
.get_value = ctrl_signal_get,
@@ -956,20 +931,6 @@ static const struct pvr2_ctl_info control_defs[] = {
.sym_to_val = ctrl_std_sym_to_val,
.type = pvr2_ctl_bitmask,
},{
- .desc = "Subsystem enabled mask",
- .name = "debug_subsys_mask",
- .skip_init = !0,
- .get_value = ctrl_subsys_get,
- .set_value = ctrl_subsys_set,
- DEFMASK(PVR2_SUBSYS_ALL,control_values_subsystem),
- },{
- .desc = "Subsystem stream mask",
- .name = "debug_subsys_stream_mask",
- .skip_init = !0,
- .get_value = ctrl_subsys_stream_get,
- .set_value = ctrl_subsys_stream_set,
- DEFMASK(PVR2_SUBSYS_ALL,control_values_subsystem),
- },{
.desc = "Video Standard Name",
.name = "video_standard",
.internal_id = PVR2_CID_STDENUM,
@@ -1139,25 +1100,13 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
unsigned int pipe;
int ret;
u16 address;
- static const char *fw_files_29xxx[] = {
- "v4l-pvrusb2-29xxx-01.fw",
- };
- static const char *fw_files_24xxx[] = {
- "v4l-pvrusb2-24xxx-01.fw",
- };
- static const struct pvr2_string_table fw_file_defs[] = {
- [PVR2_HDW_TYPE_29XXX] = {
- fw_files_29xxx, ARRAY_SIZE(fw_files_29xxx)
- },
- [PVR2_HDW_TYPE_24XXX] = {
- fw_files_24xxx, ARRAY_SIZE(fw_files_24xxx)
- },
- };
- if ((hdw->hdw_type >= ARRAY_SIZE(fw_file_defs)) ||
- (!fw_file_defs[hdw->hdw_type].lst)) {
+ if (!hdw->hdw_desc->fx2_firmware.cnt) {
hdw->fw1_state = FW1_STATE_OK;
- return 0;
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Connected device type defines"
+ " no firmware to upload; ignoring firmware");
+ return -ENOTTY;
}
hdw->fw1_state = FW1_STATE_FAILED; // default result
@@ -1165,8 +1114,8 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
trace_firmware("pvr2_upload_firmware1");
ret = pvr2_locate_firmware(hdw,&fw_entry,"fx2 controller",
- fw_file_defs[hdw->hdw_type].cnt,
- fw_file_defs[hdw->hdw_type].lst);
+ hdw->hdw_desc->fx2_firmware.cnt,
+ hdw->hdw_desc->fx2_firmware.lst);
if (ret < 0) {
if (ret == -ENOENT) hdw->fw1_state = FW1_STATE_MISSING;
return ret;
@@ -1241,8 +1190,7 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
CX2341X_FIRM_ENC_FILENAME,
};
- if ((hdw->hdw_type != PVR2_HDW_TYPE_29XXX) &&
- (hdw->hdw_type != PVR2_HDW_TYPE_24XXX)) {
+ if (hdw->hdw_desc->flag_skip_cx23416_firmware) {
return 0;
}
@@ -1258,8 +1206,6 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
time we configure the encoder, then we'll fully configure it. */
hdw->enc_cur_valid = 0;
- hdw->flag_encoder_ok = 0;
-
/* First prepare firmware loading */
ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/
ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/
@@ -1357,310 +1303,129 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
if (ret) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"firmware2 upload post-proc failure");
- } else {
- hdw->flag_encoder_ok = !0;
- hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_FIRMWARE);
}
return ret;
}
-#define FIRMWARE_RECOVERY_BITS \
- ((1<<PVR2_SUBSYS_B_ENC_CFG) | \
- (1<<PVR2_SUBSYS_B_ENC_RUN) | \
- (1<<PVR2_SUBSYS_B_ENC_FIRMWARE) | \
- (1<<PVR2_SUBSYS_B_USBSTREAM_RUN))
-
-/*
-
- This single function is key to pretty much everything. The pvrusb2
- device can logically be viewed as a series of subsystems which can be
- stopped / started or unconfigured / configured. To get things streaming,
- one must configure everything and start everything, but there may be
- various reasons over time to deconfigure something or stop something.
- This function handles all of this activity. Everything EVERYWHERE that
- must affect a subsystem eventually comes here to do the work.
-
- The current state of all subsystems is represented by a single bit mask,
- known as subsys_enabled_mask. The bit positions are defined by the
- PVR2_SUBSYS_xxxx macros, with one subsystem per bit position. At any
- time the set of configured or active subsystems can be queried just by
- looking at that mask. To change bits in that mask, this function here
- must be called. The "msk" argument indicates which bit positions to
- change, and the "val" argument defines the new values for the positions
- defined by "msk".
-
- There is a priority ordering of starting / stopping things, and for
- multiple requested changes, this function implements that ordering.
- (Thus we will act on a request to load encoder firmware before we
- configure the encoder.) In addition to priority ordering, there is a
- recovery strategy implemented here. If a particular step fails and we
- detect that failure, this function will clear the affected subsystem bits
- and restart. Thus we have a means for recovering from a dead encoder:
- Clear all bits that correspond to subsystems that we need to restart /
- reconfigure and start over.
-
-*/
-static void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw,
- unsigned long msk,
- unsigned long val)
-{
- unsigned long nmsk;
- unsigned long vmsk;
- int ret;
- unsigned int tryCount = 0;
-
- if (!hdw->flag_ok) return;
-
- msk &= PVR2_SUBSYS_ALL;
- nmsk = (hdw->subsys_enabled_mask & ~msk) | (val & msk);
- nmsk &= PVR2_SUBSYS_ALL;
-
- for (;;) {
- tryCount++;
- if (!((nmsk ^ hdw->subsys_enabled_mask) &
- PVR2_SUBSYS_ALL)) break;
- if (tryCount > 4) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Too many retries when configuring device;"
- " giving up");
- pvr2_hdw_render_useless(hdw);
- break;
- }
- if (tryCount > 1) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Retrying device reconfiguration");
- }
- pvr2_trace(PVR2_TRACE_INIT,
- "subsys mask changing 0x%lx:0x%lx"
- " from 0x%lx to 0x%lx",
- msk,val,hdw->subsys_enabled_mask,nmsk);
-
- vmsk = (nmsk ^ hdw->subsys_enabled_mask) &
- hdw->subsys_enabled_mask;
- if (vmsk) {
- if (vmsk & (1<<PVR2_SUBSYS_B_ENC_RUN)) {
- pvr2_trace(PVR2_TRACE_CTL,
- "/*---TRACE_CTL----*/"
- " pvr2_encoder_stop");
- ret = pvr2_encoder_stop(hdw);
- if (ret) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Error recovery initiated");
- hdw->subsys_enabled_mask &=
- ~FIRMWARE_RECOVERY_BITS;
- continue;
- }
- }
- if (vmsk & (1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) {
- pvr2_trace(PVR2_TRACE_CTL,
- "/*---TRACE_CTL----*/"
- " pvr2_hdw_cmd_usbstream(0)");
- pvr2_hdw_cmd_usbstream(hdw,0);
- }
- if (vmsk & (1<<PVR2_SUBSYS_B_DIGITIZER_RUN)) {
- pvr2_trace(PVR2_TRACE_CTL,
- "/*---TRACE_CTL----*/"
- " decoder disable");
- if (hdw->decoder_ctrl) {
- hdw->decoder_ctrl->enable(
- hdw->decoder_ctrl->ctxt,0);
- } else {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "WARNING:"
- " No decoder present");
- }
- hdw->subsys_enabled_mask &=
- ~(1<<PVR2_SUBSYS_B_DIGITIZER_RUN);
- }
- if (vmsk & PVR2_SUBSYS_CFG_ALL) {
- hdw->subsys_enabled_mask &=
- ~(vmsk & PVR2_SUBSYS_CFG_ALL);
- }
- }
- vmsk = (nmsk ^ hdw->subsys_enabled_mask) & nmsk;
- if (vmsk) {
- if (vmsk & (1<<PVR2_SUBSYS_B_ENC_FIRMWARE)) {
- pvr2_trace(PVR2_TRACE_CTL,
- "/*---TRACE_CTL----*/"
- " pvr2_upload_firmware2");
- ret = pvr2_upload_firmware2(hdw);
- if (ret) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Failure uploading encoder"
- " firmware");
- pvr2_hdw_render_useless(hdw);
- break;
- }
- }
- if (vmsk & (1<<PVR2_SUBSYS_B_ENC_CFG)) {
- pvr2_trace(PVR2_TRACE_CTL,
- "/*---TRACE_CTL----*/"
- " pvr2_encoder_configure");
- ret = pvr2_encoder_configure(hdw);
- if (ret) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Error recovery initiated");
- hdw->subsys_enabled_mask &=
- ~FIRMWARE_RECOVERY_BITS;
- continue;
- }
- }
- if (vmsk & (1<<PVR2_SUBSYS_B_DIGITIZER_RUN)) {
- pvr2_trace(PVR2_TRACE_CTL,
- "/*---TRACE_CTL----*/"
- " decoder enable");
- if (hdw->decoder_ctrl) {
- hdw->decoder_ctrl->enable(
- hdw->decoder_ctrl->ctxt,!0);
- } else {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "WARNING:"
- " No decoder present");
- }
- hdw->subsys_enabled_mask |=
- (1<<PVR2_SUBSYS_B_DIGITIZER_RUN);
- }
- if (vmsk & (1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) {
- pvr2_trace(PVR2_TRACE_CTL,
- "/*---TRACE_CTL----*/"
- " pvr2_hdw_cmd_usbstream(1)");
- pvr2_hdw_cmd_usbstream(hdw,!0);
- }
- if (vmsk & (1<<PVR2_SUBSYS_B_ENC_RUN)) {
- pvr2_trace(PVR2_TRACE_CTL,
- "/*---TRACE_CTL----*/"
- " pvr2_encoder_start");
- ret = pvr2_encoder_start(hdw);
- if (ret) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Error recovery initiated");
- hdw->subsys_enabled_mask &=
- ~FIRMWARE_RECOVERY_BITS;
- continue;
- }
- }
- }
- }
-}
-
-
-void pvr2_hdw_subsys_bit_chg(struct pvr2_hdw *hdw,
- unsigned long msk,unsigned long val)
+static const char *pvr2_get_state_name(unsigned int st)
{
- LOCK_TAKE(hdw->big_lock); do {
- pvr2_hdw_subsys_bit_chg_no_lock(hdw,msk,val);
- } while (0); LOCK_GIVE(hdw->big_lock);
-}
-
-#if 0
-/* Shortcut for pvr2_hdw_subsys_bit_chg(hdw,msk,msk) */
-void pvr2_hdw_subsys_bit_set(struct pvr2_hdw *hdw,unsigned long msk)
-{
- pvr2_hdw_subsys_bit_chg(hdw,msk,msk);
+ if (st < ARRAY_SIZE(pvr2_state_names)) {
+ return pvr2_state_names[st];
+ }
+ return "???";
}
-
-#endif /* 0 */
-#if 0
-/* Shortcut for pvr2_hdw_subsys_bit_chg(hdw,msk,0) */
-void pvr2_hdw_subsys_bit_clr(struct pvr2_hdw *hdw,unsigned long msk)
+static int pvr2_decoder_enable(struct pvr2_hdw *hdw,int enablefl)
{
- pvr2_hdw_subsys_bit_chg(hdw,msk,0);
+ if (!hdw->decoder_ctrl) {
+ if (!hdw->flag_decoder_missed) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "WARNING: No decoder present");
+ hdw->flag_decoder_missed = !0;
+ trace_stbit("flag_decoder_missed",
+ hdw->flag_decoder_missed);
+ }
+ return -EIO;
+ }
+ hdw->decoder_ctrl->enable(hdw->decoder_ctrl->ctxt,enablefl);
+ return 0;
}
-#endif /* 0 */
-unsigned long pvr2_hdw_subsys_get(struct pvr2_hdw *hdw)
+void pvr2_hdw_set_decoder(struct pvr2_hdw *hdw,struct pvr2_decoder_ctrl *ptr)
{
- return hdw->subsys_enabled_mask;
+ if (hdw->decoder_ctrl == ptr) return;
+ hdw->decoder_ctrl = ptr;
+ if (hdw->decoder_ctrl && hdw->flag_decoder_missed) {
+ hdw->flag_decoder_missed = 0;
+ trace_stbit("flag_decoder_missed",
+ hdw->flag_decoder_missed);
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Decoder has appeared");
+ pvr2_hdw_state_sched(hdw);
+ }
}
-unsigned long pvr2_hdw_subsys_stream_get(struct pvr2_hdw *hdw)
+int pvr2_hdw_get_state(struct pvr2_hdw *hdw)
{
- return hdw->subsys_stream_mask;
+ return hdw->master_state;
}
-static void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw,
- unsigned long msk,
- unsigned long val)
+static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *hdw)
{
- unsigned long val2;
- msk &= PVR2_SUBSYS_ALL;
- val2 = ((hdw->subsys_stream_mask & ~msk) | (val & msk));
- pvr2_trace(PVR2_TRACE_INIT,
- "stream mask changing 0x%lx:0x%lx from 0x%lx to 0x%lx",
- msk,val,hdw->subsys_stream_mask,val2);
- hdw->subsys_stream_mask = val2;
+ if (!hdw->flag_tripped) return 0;
+ hdw->flag_tripped = 0;
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Clearing driver error statuss");
+ return !0;
}
-void pvr2_hdw_subsys_stream_bit_chg(struct pvr2_hdw *hdw,
- unsigned long msk,
- unsigned long val)
+int pvr2_hdw_untrip(struct pvr2_hdw *hdw)
{
+ int fl;
LOCK_TAKE(hdw->big_lock); do {
- pvr2_hdw_subsys_stream_bit_chg_no_lock(hdw,msk,val);
+ fl = pvr2_hdw_untrip_unlocked(hdw);
} while (0); LOCK_GIVE(hdw->big_lock);
+ if (fl) pvr2_hdw_state_sched(hdw);
+ return 0;
}
-static int pvr2_hdw_set_streaming_no_lock(struct pvr2_hdw *hdw,int enableFl)
+const char *pvr2_hdw_get_state_name(unsigned int id)
{
- if ((!enableFl) == !(hdw->flag_streaming_enabled)) return 0;
- if (enableFl) {
- pvr2_trace(PVR2_TRACE_START_STOP,
- "/*--TRACE_STREAM--*/ enable");
- pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,~0);
- } else {
- pvr2_trace(PVR2_TRACE_START_STOP,
- "/*--TRACE_STREAM--*/ disable");
- pvr2_hdw_subsys_bit_chg_no_lock(hdw,hdw->subsys_stream_mask,0);
- }
- if (!hdw->flag_ok) return -EIO;
- hdw->flag_streaming_enabled = enableFl != 0;
- return 0;
+ if (id >= ARRAY_SIZE(pvr2_state_names)) return NULL;
+ return pvr2_state_names[id];
}
int pvr2_hdw_get_streaming(struct pvr2_hdw *hdw)
{
- return hdw->flag_streaming_enabled != 0;
+ return hdw->state_pipeline_req != 0;
}
int pvr2_hdw_set_streaming(struct pvr2_hdw *hdw,int enable_flag)
{
- int ret;
+ int ret,st;
LOCK_TAKE(hdw->big_lock); do {
- ret = pvr2_hdw_set_streaming_no_lock(hdw,enable_flag);
+ pvr2_hdw_untrip_unlocked(hdw);
+ if ((!enable_flag) != !(hdw->state_pipeline_req)) {
+ hdw->state_pipeline_req = enable_flag != 0;
+ pvr2_trace(PVR2_TRACE_START_STOP,
+ "/*--TRACE_STREAM--*/ %s",
+ enable_flag ? "enable" : "disable");
+ }
+ pvr2_hdw_state_sched(hdw);
} while (0); LOCK_GIVE(hdw->big_lock);
- return ret;
-}
-
-
-static int pvr2_hdw_set_stream_type_no_lock(struct pvr2_hdw *hdw,
- enum pvr2_config config)
-{
- unsigned long sm = hdw->subsys_enabled_mask;
- if (!hdw->flag_ok) return -EIO;
- pvr2_hdw_subsys_bit_chg_no_lock(hdw,hdw->subsys_stream_mask,0);
- hdw->config = config;
- pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,sm);
+ if ((ret = pvr2_hdw_wait(hdw,0)) < 0) return ret;
+ if (enable_flag) {
+ while ((st = hdw->master_state) != PVR2_STATE_RUN) {
+ if (st != PVR2_STATE_READY) return -EIO;
+ if ((ret = pvr2_hdw_wait(hdw,st)) < 0) return ret;
+ }
+ }
return 0;
}
int pvr2_hdw_set_stream_type(struct pvr2_hdw *hdw,enum pvr2_config config)
{
- int ret;
- if (!hdw->flag_ok) return -EIO;
+ int fl;
LOCK_TAKE(hdw->big_lock);
- ret = pvr2_hdw_set_stream_type_no_lock(hdw,config);
+ if ((fl = (hdw->desired_stream_type != config)) != 0) {
+ hdw->desired_stream_type = config;
+ hdw->state_pipeline_config = 0;
+ trace_stbit("state_pipeline_config",
+ hdw->state_pipeline_config);
+ pvr2_hdw_state_sched(hdw);
+ }
LOCK_GIVE(hdw->big_lock);
- return ret;
+ if (fl) return 0;
+ return pvr2_hdw_wait(hdw,0);
}
@@ -1673,6 +1438,7 @@ static int get_default_tuner_type(struct pvr2_hdw *hdw)
}
if (tp < 0) return -EINVAL;
hdw->tuner_type = tp;
+ hdw->tuner_updated = !0;
return 0;
}
@@ -1683,8 +1449,9 @@ static v4l2_std_id get_default_standard(struct pvr2_hdw *hdw)
int tp = 0;
if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
tp = video_std[unit_number];
+ if (tp) return tp;
}
- return tp;
+ return 0;
}
@@ -1758,7 +1525,7 @@ const static struct pvr2_std_hack std_eeprom_maps[] = {
},
{ /* PAL(D/D1/K) */
.pat = V4L2_STD_DK,
- .std = V4L2_STD_PAL_D/V4L2_STD_PAL_D1|V4L2_STD_PAL_K,
+ .std = V4L2_STD_PAL_D|V4L2_STD_PAL_D1|V4L2_STD_PAL_K,
},
};
@@ -1766,18 +1533,20 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw)
{
char buf[40];
unsigned int bcnt;
- v4l2_std_id std1,std2;
+ v4l2_std_id std1,std2,std3;
std1 = get_default_standard(hdw);
+ std3 = std1 ? 0 : hdw->hdw_desc->default_std_mask;
bcnt = pvr2_std_id_to_str(buf,sizeof(buf),hdw->std_mask_eeprom);
pvr2_trace(PVR2_TRACE_STD,
- "Supported video standard(s) reported by eeprom: %.*s",
+ "Supported video standard(s) reported available"
+ " in hardware: %.*s",
bcnt,buf);
hdw->std_mask_avail = hdw->std_mask_eeprom;
- std2 = std1 & ~hdw->std_mask_avail;
+ std2 = (std1|std3) & ~hdw->std_mask_avail;
if (std2) {
bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std2);
pvr2_trace(PVR2_TRACE_STD,
@@ -1799,6 +1568,16 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw)
pvr2_hdw_internal_find_stdenum(hdw);
return;
}
+ if (std3) {
+ bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std3);
+ pvr2_trace(PVR2_TRACE_STD,
+ "Initial video standard"
+ " (determined by device type): %.*s",bcnt,buf);
+ hdw->std_mask_cur = std3;
+ hdw->std_dirty = !0;
+ pvr2_hdw_internal_find_stdenum(hdw);
+ return;
+ }
{
unsigned int idx;
@@ -1843,8 +1622,7 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
unsigned int idx;
struct pvr2_ctrl *cptr;
int reloadFl = 0;
- if ((hdw->hdw_type == PVR2_HDW_TYPE_29XXX) ||
- (hdw->hdw_type == PVR2_HDW_TYPE_24XXX)) {
+ if (hdw->hdw_desc->fx2_firmware.cnt) {
if (!reloadFl) {
reloadFl =
(hdw->usb_intf->cur_altsetting->desc.bNumEndpoints
@@ -1880,25 +1658,13 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
}
if (!pvr2_hdw_dev_ok(hdw)) return;
- if (hdw->hdw_type < ARRAY_SIZE(pvr2_client_lists)) {
- for (idx = 0;
- idx < pvr2_client_lists[hdw->hdw_type].cnt;
- idx++) {
- request_module(
- pvr2_client_lists[hdw->hdw_type].lst[idx]);
- }
+ for (idx = 0; idx < hdw->hdw_desc->client_modules.cnt; idx++) {
+ request_module(hdw->hdw_desc->client_modules.lst[idx]);
}
- if ((hdw->hdw_type == PVR2_HDW_TYPE_29XXX) ||
- (hdw->hdw_type == PVR2_HDW_TYPE_24XXX)) {
+ if (!hdw->hdw_desc->flag_no_powerup) {
pvr2_hdw_cmd_powerup(hdw);
if (!pvr2_hdw_dev_ok(hdw)) return;
-
- if (pvr2_upload_firmware2(hdw)){
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,"device unstable!!");
- pvr2_hdw_render_useless(hdw);
- return;
- }
}
// This step MUST happen after the earlier powerup step.
@@ -1926,15 +1692,22 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
// thread-safe against the normal pvr2_send_request() mechanism.
// (We should make it thread safe).
- ret = pvr2_hdw_get_eeprom_addr(hdw);
- if (!pvr2_hdw_dev_ok(hdw)) return;
- if (ret < 0) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Unable to determine location of eeprom, skipping");
- } else {
- hdw->eeprom_addr = ret;
- pvr2_eeprom_analyze(hdw);
+ if (hdw->hdw_desc->flag_has_hauppauge_rom) {
+ ret = pvr2_hdw_get_eeprom_addr(hdw);
if (!pvr2_hdw_dev_ok(hdw)) return;
+ if (ret < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Unable to determine location of eeprom,"
+ " skipping");
+ } else {
+ hdw->eeprom_addr = ret;
+ pvr2_eeprom_analyze(hdw);
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+ }
+ } else {
+ hdw->tuner_type = hdw->hdw_desc->default_tuner_type;
+ hdw->tuner_updated = !0;
+ hdw->std_mask_eeprom = V4L2_STD_ALL;
}
pvr2_hdw_setup_std(hdw);
@@ -1945,14 +1718,12 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
hdw->tuner_type);
}
- hdw->tuner_updated = !0;
pvr2_i2c_core_check_stale(hdw);
hdw->tuner_updated = 0;
if (!pvr2_hdw_dev_ok(hdw)) return;
- pvr2_hdw_commit_ctl_internal(hdw);
- if (!pvr2_hdw_dev_ok(hdw)) return;
+ pvr2_hdw_commit_setup(hdw);
hdw->vid_stream = pvr2_stream_create();
if (!pvr2_hdw_dev_ok(hdw)) return;
@@ -1972,25 +1743,25 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
if (!pvr2_hdw_dev_ok(hdw)) return;
- /* Make sure everything is up to date */
- pvr2_i2c_core_sync(hdw);
-
- if (!pvr2_hdw_dev_ok(hdw)) return;
-
hdw->flag_init_ok = !0;
+
+ pvr2_hdw_state_sched(hdw);
}
-int pvr2_hdw_setup(struct pvr2_hdw *hdw)
+/* Set up the structure and attempt to put the device into a usable state.
+ This can be a time-consuming operation, which is why it is not done
+ internally as part of the create() step. */
+static void pvr2_hdw_setup(struct pvr2_hdw *hdw)
{
pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) begin",hdw);
- LOCK_TAKE(hdw->big_lock); do {
+ do {
pvr2_hdw_setup_low(hdw);
pvr2_trace(PVR2_TRACE_INIT,
"pvr2_hdw_setup(hdw=%p) done, ok=%d init_ok=%d",
- hdw,hdw->flag_ok,hdw->flag_init_ok);
+ hdw,pvr2_hdw_dev_ok(hdw),hdw->flag_init_ok);
if (pvr2_hdw_dev_ok(hdw)) {
- if (pvr2_hdw_init_ok(hdw)) {
+ if (hdw->flag_init_ok) {
pvr2_trace(
PVR2_TRACE_INFO,
"Device initialization"
@@ -2040,9 +1811,8 @@ int pvr2_hdw_setup(struct pvr2_hdw *hdw)
" the pvrusb2 device"
" in order to recover.");
}
- } while (0); LOCK_GIVE(hdw->big_lock);
+ } while (0);
pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) end",hdw);
- return hdw->flag_init_ok;
}
@@ -2056,21 +1826,36 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
unsigned int hdw_type;
int valid_std_mask;
struct pvr2_ctrl *cptr;
+ const struct pvr2_device_desc *hdw_desc;
__u8 ifnum;
struct v4l2_queryctrl qctrl;
struct pvr2_ctl_info *ciptr;
hdw_type = devid - pvr2_device_table;
- if (hdw_type >= ARRAY_SIZE(pvr2_device_names)) {
+ if (hdw_type >= pvr2_device_count) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"Bogus device type of %u reported",hdw_type);
return NULL;
}
+ hdw_desc = pvr2_device_descriptions + hdw_type;
hdw = kzalloc(sizeof(*hdw),GFP_KERNEL);
pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"",
- hdw,pvr2_device_names[hdw_type]);
+ hdw,hdw_desc->description);
if (!hdw) goto fail;
+
+ init_timer(&hdw->quiescent_timer);
+ hdw->quiescent_timer.data = (unsigned long)hdw;
+ hdw->quiescent_timer.function = pvr2_hdw_quiescent_timeout;
+
+ init_timer(&hdw->encoder_wait_timer);
+ hdw->encoder_wait_timer.data = (unsigned long)hdw;
+ hdw->encoder_wait_timer.function = pvr2_hdw_encoder_wait_timeout;
+
+ hdw->master_state = PVR2_STATE_DEAD;
+
+ init_waitqueue_head(&hdw->state_wait_data);
+
hdw->tuner_signal_stale = !0;
cx2341x_fill_defaults(&hdw->enc_ctl_state);
@@ -2079,7 +1864,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
hdw->controls = kzalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt,
GFP_KERNEL);
if (!hdw->controls) goto fail;
- hdw->hdw_type = hdw_type;
+ hdw->hdw_desc = hdw_desc;
for (idx = 0; idx < hdw->control_cnt; idx++) {
cptr = hdw->controls + idx;
cptr->hdw = hdw;
@@ -2211,18 +1996,25 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
if (cnt1 >= sizeof(hdw->name)) cnt1 = sizeof(hdw->name)-1;
hdw->name[cnt1] = 0;
+ hdw->workqueue = create_singlethread_workqueue(hdw->name);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+ INIT_WORK(&hdw->workpoll,(void (*)(void*))pvr2_hdw_worker_poll,
+ &hdw->workpoll);
+ INIT_WORK(&hdw->worki2csync,(void (*)(void*))pvr2_hdw_worker_i2c,
+ &hdw->worki2csync);
+ INIT_WORK(&hdw->workinit,(void (*)(void*))pvr2_hdw_worker_init,
+ &hdw->workinit);
+#else
+ INIT_WORK(&hdw->workpoll,pvr2_hdw_worker_poll);
+ INIT_WORK(&hdw->worki2csync,pvr2_hdw_worker_i2c);
+ INIT_WORK(&hdw->workinit,pvr2_hdw_worker_init);
+#endif
+
pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s",
hdw->unit_number,hdw->name);
hdw->tuner_type = -1;
hdw->flag_ok = !0;
- /* Initialize the mask of subsystems that we will shut down when we
- stop streaming. */
- hdw->subsys_stream_mask = PVR2_SUBSYS_RUN_ALL;
- hdw->subsys_stream_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG);
-
- pvr2_trace(PVR2_TRACE_INIT,"subsys_stream_mask: 0x%lx",
- hdw->subsys_stream_mask);
hdw->usb_intf = intf;
hdw->usb_dev = interface_to_usbdev(intf);
@@ -2238,15 +2030,25 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
mutex_init(&hdw->ctl_lock_mutex);
mutex_init(&hdw->big_lock_mutex);
+ queue_work(hdw->workqueue,&hdw->workinit);
return hdw;
fail:
if (hdw) {
+ del_timer_sync(&hdw->quiescent_timer);
+ del_timer_sync(&hdw->encoder_wait_timer);
+ if (hdw->workqueue) {
+ flush_workqueue(hdw->workqueue);
+ destroy_workqueue(hdw->workqueue);
+ hdw->workqueue = NULL;
+ }
usb_free_urb(hdw->ctl_read_urb);
usb_free_urb(hdw->ctl_write_urb);
kfree(hdw->ctl_read_buffer);
kfree(hdw->ctl_write_buffer);
kfree(hdw->controls);
kfree(hdw->mpeg_ctrl_info);
+ kfree(hdw->std_defs);
+ kfree(hdw->std_enum_names);
kfree(hdw);
}
return NULL;
@@ -2277,10 +2079,10 @@ static void pvr2_hdw_remove_usb_stuff(struct pvr2_hdw *hdw)
kfree(hdw->ctl_write_buffer);
hdw->ctl_write_buffer = NULL;
}
- pvr2_hdw_render_useless_unlocked(hdw);
hdw->flag_disconnected = !0;
hdw->usb_dev = NULL;
hdw->usb_intf = NULL;
+ pvr2_hdw_render_useless(hdw);
}
@@ -2289,6 +2091,13 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
{
if (!hdw) return;
pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_destroy: hdw=%p",hdw);
+ del_timer_sync(&hdw->quiescent_timer);
+ del_timer_sync(&hdw->encoder_wait_timer);
+ if (hdw->workqueue) {
+ flush_workqueue(hdw->workqueue);
+ destroy_workqueue(hdw->workqueue);
+ hdw->workqueue = NULL;
+ }
if (hdw->fw_buffer) {
kfree(hdw->fw_buffer);
hdw->fw_buffer = NULL;
@@ -2317,12 +2126,6 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
}
-int pvr2_hdw_init_ok(struct pvr2_hdw *hdw)
-{
- return hdw->flag_init_ok;
-}
-
-
int pvr2_hdw_dev_ok(struct pvr2_hdw *hdw)
{
return (hdw && hdw->flag_ok);
@@ -2500,17 +2303,11 @@ static const char *get_ctrl_typename(enum pvr2_ctl_type tp)
}
-/* Commit all control changes made up to this point. Subsystems can be
- indirectly affected by these changes. For a given set of things being
- committed, we'll clear the affected subsystem bits and then once we're
- done committing everything we'll make a request to restore the subsystem
- state(s) back to their previous value before this function was called.
- Thus we can automatically reconfigure affected pieces of the driver as
- controls are changed. */
-static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw)
+/* Figure out if we need to commit control changes. If so, mark internal
+ state flags to indicate this fact and return true. Otherwise do nothing
+ else and return false. */
+static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw)
{
- unsigned long saved_subsys_mask = hdw->subsys_enabled_mask;
- unsigned long stale_subsys_mask = 0;
unsigned int idx;
struct pvr2_ctrl *cptr;
int value;
@@ -2545,6 +2342,25 @@ static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw)
return 0;
}
+ hdw->state_pipeline_config = 0;
+ trace_stbit("state_pipeline_config",hdw->state_pipeline_config);
+ pvr2_hdw_state_sched(hdw);
+
+ return !0;
+}
+
+
+/* Perform all operations needed to commit all control changes. This must
+ be performed in synchronization with the pipeline state and is thus
+ expected to be called as part of the driver's worker thread. Return
+ true if commit successful, otherwise return false to indicate that
+ commit isn't possible at this time. */
+static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw)
+{
+ unsigned int idx;
+ struct pvr2_ctrl *cptr;
+ int disruptive_change;
+
/* When video standard changes, reset the hres and vres values -
but if the user has pending changes there, then let the changes
take priority. */
@@ -2563,27 +2379,26 @@ static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw)
}
}
- if (hdw->std_dirty ||
- hdw->enc_stale ||
- hdw->srate_dirty ||
- hdw->res_ver_dirty ||
- hdw->res_hor_dirty ||
- 0) {
- /* If any of this changes, then the encoder needs to be
- reconfigured, and we need to reset the stream. */
- stale_subsys_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG);
-#if 0 /* Don't toggle encoder for now - seems to work better this way */
- stale_subsys_mask |= hdw->subsys_stream_mask;
-#endif
- }
-
- if (hdw->input_dirty) {
- /* pk: If input changes to or from radio, then the encoder
- needs to be restarted (for ENC_MUTE_VIDEO to work) */
- stale_subsys_mask |= (1<<PVR2_SUBSYS_B_ENC_RUN);
+ /* If any of the below has changed, then we can't do the update
+ while the pipeline is running. Pipeline must be paused first
+ and decoder -> encoder connection be made quiescent before we
+ can proceed. */
+ disruptive_change =
+ (hdw->std_dirty ||
+ hdw->enc_unsafe_stale ||
+ hdw->srate_dirty ||
+ hdw->res_ver_dirty ||
+ hdw->res_hor_dirty ||
+ hdw->input_dirty ||
+ (hdw->active_stream_type != hdw->desired_stream_type));
+ if (disruptive_change && !hdw->state_pipeline_idle) {
+ /* Pipeline is not idle; we can't proceed. Arrange to
+ cause pipeline to stop so that we can try this again
+ later.... */
+ hdw->state_pipeline_pause = !0;
+ return 0;
}
-
if (hdw->srate_dirty) {
/* Write new sample rate into control structure since
* the master copy is stale. We must track srate
@@ -2612,60 +2427,88 @@ static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw)
cptr->info->clear_dirty(cptr);
}
+ if (hdw->active_stream_type != hdw->desired_stream_type) {
+ /* Handle any side effects of stream config here */
+ hdw->active_stream_type = hdw->desired_stream_type;
+ }
+
/* Now execute i2c core update */
pvr2_i2c_core_sync(hdw);
- pvr2_hdw_subsys_bit_chg_no_lock(hdw,stale_subsys_mask,0);
- pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,saved_subsys_mask);
+ if (hdw->state_encoder_run) {
+ /* If encoder isn't running, then this will get worked out
+ later when we start the encoder. */
+ if (pvr2_encoder_adjust(hdw) < 0) return !0;
+ }
- return 0;
+ hdw->state_pipeline_config = !0;
+ trace_stbit("state_pipeline_config",hdw->state_pipeline_config);
+ return !0;
}
int pvr2_hdw_commit_ctl(struct pvr2_hdw *hdw)
{
+ int fl;
+ LOCK_TAKE(hdw->big_lock);
+ fl = pvr2_hdw_commit_setup(hdw);
+ LOCK_GIVE(hdw->big_lock);
+ if (!fl) return 0;
+ return pvr2_hdw_wait(hdw,0);
+}
+
+
+static void pvr2_hdw_worker_i2c(struct work_struct *work)
+{
+ struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,worki2csync);
LOCK_TAKE(hdw->big_lock); do {
- pvr2_hdw_commit_ctl_internal(hdw);
+ pvr2_i2c_core_sync(hdw);
} while (0); LOCK_GIVE(hdw->big_lock);
- return 0;
}
-void pvr2_hdw_poll(struct pvr2_hdw *hdw)
+static void pvr2_hdw_worker_poll(struct work_struct *work)
{
+ int fl = 0;
+ struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,workpoll);
LOCK_TAKE(hdw->big_lock); do {
- pvr2_i2c_core_sync(hdw);
+ fl = pvr2_hdw_state_eval(hdw);
} while (0); LOCK_GIVE(hdw->big_lock);
+ if (fl && hdw->state_func) {
+ hdw->state_func(hdw->state_data);
+ }
}
-void pvr2_hdw_setup_poll_trigger(struct pvr2_hdw *hdw,
- void (*func)(void *),
- void *data)
+static void pvr2_hdw_worker_init(struct work_struct *work)
{
+ struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,workinit);
LOCK_TAKE(hdw->big_lock); do {
- hdw->poll_trigger_func = func;
- hdw->poll_trigger_data = data;
+ pvr2_hdw_setup(hdw);
} while (0); LOCK_GIVE(hdw->big_lock);
}
-void pvr2_hdw_poll_trigger_unlocked(struct pvr2_hdw *hdw)
+static int pvr2_hdw_wait(struct pvr2_hdw *hdw,int state)
{
- if (hdw->poll_trigger_func) {
- hdw->poll_trigger_func(hdw->poll_trigger_data);
- }
+ return wait_event_interruptible(
+ hdw->state_wait_data,
+ (hdw->state_stale == 0) &&
+ (!state || (hdw->master_state != state)));
}
-#if 0
-void pvr2_hdw_poll_trigger(struct pvr2_hdw *hdw)
+
+void pvr2_hdw_set_state_callback(struct pvr2_hdw *hdw,
+ void (*callback_func)(void *),
+ void *callback_data)
{
LOCK_TAKE(hdw->big_lock); do {
- pvr2_hdw_poll_trigger_unlocked(hdw);
+ hdw->state_data = callback_data;
+ hdw->state_func = callback_func;
} while (0); LOCK_GIVE(hdw->big_lock);
}
-#endif /* 0 */
+
/* Return name for this driver instance */
const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw)
{
@@ -2673,6 +2516,18 @@ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw)
}
+const char *pvr2_hdw_get_desc(struct pvr2_hdw *hdw)
+{
+ return hdw->hdw_desc->description;
+}
+
+
+const char *pvr2_hdw_get_type(struct pvr2_hdw *hdw)
+{
+ return hdw->hdw_desc->shortname;
+}
+
+
int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw)
{
int result;
@@ -2728,6 +2583,7 @@ void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw)
pvr2_i2c_core_sync(hdw);
pvr2_trace(PVR2_TRACE_INFO,"cx2341x config:");
cx2341x_log_status(&hdw->enc_ctl_state, "pvrusb2");
+ pvr2_hdw_state_log_state(hdw);
printk(KERN_INFO "pvrusb2: ================== END STATUS CARD #%d ==================\n", nr);
} while (0); LOCK_GIVE(hdw->big_lock);
}
@@ -3026,7 +2882,7 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw,
" without lock!!");
return -EDEADLK;
}
- if ((!hdw->flag_ok) && !probe_fl) {
+ if (!hdw->flag_ok && !probe_fl) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"Attempted to execute control transfer"
" when device not ok");
@@ -3280,7 +3136,7 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw,
hdw->cmd_debug_state = 0;
if ((status < 0) && (!probe_fl)) {
- pvr2_hdw_render_useless_unlocked(hdw);
+ pvr2_hdw_render_useless(hdw);
}
return status;
}
@@ -3340,24 +3196,17 @@ static int pvr2_read_register(struct pvr2_hdw *hdw, u16 reg, u32 *data)
}
-static void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *hdw)
+void pvr2_hdw_render_useless(struct pvr2_hdw *hdw)
{
if (!hdw->flag_ok) return;
- pvr2_trace(PVR2_TRACE_INIT,"render_useless");
- hdw->flag_ok = 0;
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Device being rendered inoperable");
if (hdw->vid_stream) {
pvr2_stream_setup(hdw->vid_stream,NULL,0,0);
}
- hdw->flag_streaming_enabled = 0;
- hdw->subsys_enabled_mask = 0;
-}
-
-
-void pvr2_hdw_render_useless(struct pvr2_hdw *hdw)
-{
- LOCK_TAKE(hdw->ctl_lock);
- pvr2_hdw_render_useless_unlocked(hdw);
- LOCK_GIVE(hdw->ctl_lock);
+ hdw->flag_ok = 0;
+ trace_stbit("flag_ok",hdw->flag_ok);
+ pvr2_hdw_state_sched(hdw);
}
@@ -3412,7 +3261,6 @@ int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw)
int status;
LOCK_TAKE(hdw->ctl_lock); do {
pvr2_trace(PVR2_TRACE_INIT,"Requesting uproc hard reset");
- hdw->flag_ok = !0;
hdw->cmd_buffer[0] = FX2CMD_DEEP_RESET;
status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0);
} while (0); LOCK_GIVE(hdw->ctl_lock);
@@ -3462,26 +3310,473 @@ static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl)
(runFl ? FX2CMD_STREAMING_ON : FX2CMD_STREAMING_OFF);
status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0);
} while (0); LOCK_GIVE(hdw->ctl_lock);
- if (!status) {
- hdw->subsys_enabled_mask =
- ((hdw->subsys_enabled_mask &
- ~(1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) |
- (runFl ? (1<<PVR2_SUBSYS_B_USBSTREAM_RUN) : 0));
- }
return status;
}
-void pvr2_hdw_get_debug_info(const struct pvr2_hdw *hdw,
- struct pvr2_hdw_debug_info *ptr)
+/* Evaluate whether or not state_encoder_ok can change */
+static int state_eval_encoder_ok(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_encoder_ok) return 0;
+ if (hdw->flag_tripped) return 0;
+ if (hdw->state_encoder_run) return 0;
+ if (hdw->state_encoder_config) return 0;
+ if (hdw->state_decoder_run) return 0;
+ if (hdw->state_usbstream_run) return 0;
+ if (pvr2_upload_firmware2(hdw) < 0) {
+ hdw->flag_tripped = !0;
+ trace_stbit("flag_tripped",hdw->flag_tripped);
+ return !0;
+ }
+ hdw->state_encoder_ok = !0;
+ trace_stbit("state_encoder_ok",hdw->state_encoder_ok);
+ return !0;
+}
+
+
+/* Evaluate whether or not state_encoder_config can change */
+static int state_eval_encoder_config(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_encoder_config) {
+ if (hdw->state_encoder_ok) {
+ if (hdw->state_pipeline_req &&
+ !hdw->state_pipeline_pause) return 0;
+ }
+ hdw->state_encoder_config = 0;
+ hdw->state_encoder_waitok = 0;
+ trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok);
+ /* paranoia - solve race if timer just completed */
+ del_timer_sync(&hdw->encoder_wait_timer);
+ } else {
+ if (!hdw->state_encoder_ok ||
+ !hdw->state_pipeline_idle ||
+ hdw->state_pipeline_pause ||
+ !hdw->state_pipeline_req ||
+ !hdw->state_pipeline_config) {
+ /* We must reset the enforced wait interval if
+ anything has happened that might have disturbed
+ the encoder. This should be a rare case. */
+ if (timer_pending(&hdw->encoder_wait_timer)) {
+ del_timer_sync(&hdw->encoder_wait_timer);
+ }
+ if (hdw->state_encoder_waitok) {
+ /* Must clear the state - therefore we did
+ something to a state bit and must also
+ return true. */
+ hdw->state_encoder_waitok = 0;
+ trace_stbit("state_encoder_waitok",
+ hdw->state_encoder_waitok);
+ return !0;
+ }
+ return 0;
+ }
+ if (!hdw->state_encoder_waitok) {
+ if (!timer_pending(&hdw->encoder_wait_timer)) {
+ /* waitok flag wasn't set and timer isn't
+ running. Check flag once more to avoid
+ a race then start the timer. This is
+ the point when we measure out a minimal
+ quiet interval before doing something to
+ the encoder. */
+ if (!hdw->state_encoder_waitok) {
+ hdw->encoder_wait_timer.expires =
+ jiffies + (HZ*50/1000);
+ add_timer(&hdw->encoder_wait_timer);
+ }
+ }
+ /* We can't continue until we know we have been
+ quiet for the interval measured by this
+ timer. */
+ return 0;
+ }
+ pvr2_encoder_configure(hdw);
+ if (hdw->state_encoder_ok) hdw->state_encoder_config = !0;
+ }
+ trace_stbit("state_encoder_config",hdw->state_encoder_config);
+ return !0;
+}
+
+
+/* Evaluate whether or not state_encoder_run can change */
+static int state_eval_encoder_run(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_encoder_run) {
+ if (hdw->state_encoder_ok) {
+ if (hdw->state_decoder_run) return 0;
+ if (pvr2_encoder_stop(hdw) < 0) return !0;
+ }
+ hdw->state_encoder_run = 0;
+ } else {
+ if (!hdw->state_encoder_ok) return 0;
+ if (!hdw->state_decoder_run) return 0;
+ if (pvr2_encoder_start(hdw) < 0) return !0;
+ hdw->state_encoder_run = !0;
+ }
+ trace_stbit("state_encoder_run",hdw->state_encoder_run);
+ return !0;
+}
+
+
+/* Timeout function for quiescent timer. */
+static void pvr2_hdw_quiescent_timeout(unsigned long data)
+{
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)data;
+ hdw->state_decoder_quiescent = !0;
+ trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent);
+ hdw->state_stale = !0;
+ queue_work(hdw->workqueue,&hdw->workpoll);
+}
+
+
+/* Timeout function for encoder wait timer. */
+static void pvr2_hdw_encoder_wait_timeout(unsigned long data)
+{
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)data;
+ hdw->state_encoder_waitok = !0;
+ trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok);
+ hdw->state_stale = !0;
+ queue_work(hdw->workqueue,&hdw->workpoll);
+}
+
+
+/* Evaluate whether or not state_decoder_run can change */
+static int state_eval_decoder_run(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_decoder_run) {
+ if (hdw->state_encoder_ok) {
+ if (hdw->state_pipeline_req &&
+ !hdw->state_pipeline_pause) return 0;
+ }
+ if (!hdw->flag_decoder_missed) {
+ pvr2_decoder_enable(hdw,0);
+ }
+ hdw->state_decoder_quiescent = 0;
+ hdw->state_decoder_run = 0;
+ /* paranoia - solve race if timer just completed */
+ del_timer_sync(&hdw->quiescent_timer);
+ } else {
+ if (!hdw->state_decoder_quiescent) {
+ if (!timer_pending(&hdw->quiescent_timer)) {
+ /* We don't do something about the
+ quiescent timer until right here because
+ we also want to catch cases where the
+ decoder was already not running (like
+ after initialization) as opposed to
+ knowing that we had just stopped it.
+ The second flag check is here to cover a
+ race - the timer could have run and set
+ this flag just after the previous check
+ but before we did the pending check. */
+ if (!hdw->state_decoder_quiescent) {
+ hdw->quiescent_timer.expires =
+ jiffies + (HZ*50/1000);
+ add_timer(&hdw->quiescent_timer);
+ }
+ }
+ /* Don't allow decoder to start again until it has
+ been quiesced first. This little detail should
+ hopefully further stabilize the encoder. */
+ return 0;
+ }
+ if (!hdw->state_pipeline_req ||
+ hdw->state_pipeline_pause ||
+ !hdw->state_pipeline_config ||
+ !hdw->state_encoder_config ||
+ !hdw->state_encoder_ok) return 0;
+ del_timer_sync(&hdw->quiescent_timer);
+ if (hdw->flag_decoder_missed) return 0;
+ if (pvr2_decoder_enable(hdw,!0) < 0) return 0;
+ hdw->state_decoder_quiescent = 0;
+ hdw->state_decoder_run = !0;
+ }
+ trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent);
+ trace_stbit("state_decoder_run",hdw->state_decoder_run);
+ return !0;
+}
+
+
+/* Evaluate whether or not state_usbstream_run can change */
+static int state_eval_usbstream_run(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_usbstream_run) {
+ if (hdw->state_encoder_ok) {
+ if (hdw->state_encoder_run) return 0;
+ }
+ pvr2_hdw_cmd_usbstream(hdw,0);
+ hdw->state_usbstream_run = 0;
+ } else {
+ if (!hdw->state_encoder_ok ||
+ !hdw->state_encoder_run ||
+ !hdw->state_pipeline_req ||
+ hdw->state_pipeline_pause) return 0;
+ if (pvr2_hdw_cmd_usbstream(hdw,!0) < 0) return 0;
+ hdw->state_usbstream_run = !0;
+ }
+ trace_stbit("state_usbstream_run",hdw->state_usbstream_run);
+ return !0;
+}
+
+
+/* Attempt to configure pipeline, if needed */
+static int state_eval_pipeline_config(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_pipeline_config ||
+ hdw->state_pipeline_pause) return 0;
+ pvr2_hdw_commit_execute(hdw);
+ return !0;
+}
+
+
+/* Update pipeline idle and pipeline pause tracking states based on other
+ inputs. This must be called whenever the other relevant inputs have
+ changed. */
+static int state_update_pipeline_state(struct pvr2_hdw *hdw)
+{
+ unsigned int st;
+ int updatedFl = 0;
+ /* Update pipeline state */
+ st = !(hdw->state_encoder_run ||
+ hdw->state_decoder_run ||
+ hdw->state_usbstream_run ||
+ (!hdw->state_decoder_quiescent));
+ if (!st != !hdw->state_pipeline_idle) {
+ hdw->state_pipeline_idle = st;
+ updatedFl = !0;
+ }
+ if (hdw->state_pipeline_idle && hdw->state_pipeline_pause) {
+ hdw->state_pipeline_pause = 0;
+ updatedFl = !0;
+ }
+ return updatedFl;
+}
+
+
+typedef int (*state_eval_func)(struct pvr2_hdw *);
+
+/* Set of functions to be run to evaluate various states in the driver. */
+const static state_eval_func eval_funcs[] = {
+ state_eval_pipeline_config,
+ state_eval_encoder_ok,
+ state_eval_encoder_config,
+ state_eval_decoder_run,
+ state_eval_encoder_run,
+ state_eval_usbstream_run,
+};
+
+
+/* Process various states and return true if we did anything interesting. */
+static int pvr2_hdw_state_update(struct pvr2_hdw *hdw)
+{
+ unsigned int i;
+ int state_updated = 0;
+ int check_flag;
+
+ if (!hdw->state_stale) return 0;
+ if ((hdw->fw1_state != FW1_STATE_OK) ||
+ !hdw->flag_ok) {
+ hdw->state_stale = 0;
+ return !0;
+ }
+ /* This loop is the heart of the entire driver. It keeps trying to
+ evaluate various bits of driver state until nothing changes for
+ one full iteration. Each "bit of state" tracks some global
+ aspect of the driver, e.g. whether decoder should run, if
+ pipeline is configured, usb streaming is on, etc. We separately
+ evaluate each of those questions based on other driver state to
+ arrive at the correct running configuration. */
+ do {
+ check_flag = 0;
+ state_update_pipeline_state(hdw);
+ /* Iterate over each bit of state */
+ for (i = 0; (i<ARRAY_SIZE(eval_funcs)) && hdw->flag_ok; i++) {
+ if ((*eval_funcs[i])(hdw)) {
+ check_flag = !0;
+ state_updated = !0;
+ state_update_pipeline_state(hdw);
+ }
+ }
+ } while (check_flag && hdw->flag_ok);
+ hdw->state_stale = 0;
+ trace_stbit("state_stale",hdw->state_stale);
+ return state_updated;
+}
+
+
+static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which,
+ char *buf,unsigned int acnt)
+{
+ switch (which) {
+ case 0:
+ return scnprintf(
+ buf,acnt,
+ "driver:%s%s%s%s%s",
+ (hdw->flag_ok ? " <ok>" : " <fail>"),
+ (hdw->flag_init_ok ? " <init>" : " <uninitialized>"),
+ (hdw->flag_disconnected ? " <disconnected>" :
+ " <connected>"),
+ (hdw->flag_tripped ? " <tripped>" : ""),
+ (hdw->flag_decoder_missed ? " <no decoder>" : ""));
+ case 1:
+ return scnprintf(
+ buf,acnt,
+ "pipeline:%s%s%s%s",
+ (hdw->state_pipeline_idle ? " <idle>" : ""),
+ (hdw->state_pipeline_config ?
+ " <configok>" : " <stale>"),
+ (hdw->state_pipeline_req ? " <req>" : ""),
+ (hdw->state_pipeline_pause ? " <pause>" : ""));
+ case 2:
+ return scnprintf(
+ buf,acnt,
+ "worker:%s%s%s%s%s%s",
+ (hdw->state_decoder_run ?
+ " <decode:run>" :
+ (hdw->state_decoder_quiescent ?
+ "" : " <decode:stop>")),
+ (hdw->state_decoder_quiescent ?
+ " <decode:quiescent>" : ""),
+ (hdw->state_encoder_ok ?
+ "" : " <encode:init>"),
+ (hdw->state_encoder_run ?
+ " <encode:run>" : " <encode:stop>"),
+ (hdw->state_encoder_config ?
+ " <encode:configok>" :
+ (hdw->state_encoder_waitok ?
+ "" : " <encode:wait>")),
+ (hdw->state_usbstream_run ?
+ " <usb:run>" : " <usb:stop>"));
+ break;
+ case 3:
+ return scnprintf(
+ buf,acnt,
+ "state: %s",
+ pvr2_get_state_name(hdw->master_state));
+ break;
+ default: break;
+ }
+ return 0;
+}
+
+
+unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw,
+ char *buf,unsigned int acnt)
+{
+ unsigned int bcnt,ccnt,idx;
+ bcnt = 0;
+ LOCK_TAKE(hdw->big_lock);
+ for (idx = 0; ; idx++) {
+ ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,acnt);
+ if (!ccnt) break;
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ if (!acnt) break;
+ buf[0] = '\n'; ccnt = 1;
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ }
+ LOCK_GIVE(hdw->big_lock);
+ return bcnt;
+}
+
+
+static void pvr2_hdw_state_log_state(struct pvr2_hdw *hdw)
+{
+ char buf[128];
+ unsigned int idx,ccnt;
+
+ for (idx = 0; ; idx++) {
+ ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,sizeof(buf));
+ if (!ccnt) break;
+ printk(KERN_INFO "%s %.*s\n",hdw->name,ccnt,buf);
+ }
+}
+
+
+/* Evaluate and update the driver's current state, taking various actions
+ as appropriate for the update. */
+static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw)
+{
+ unsigned int st;
+ int state_updated = 0;
+ int callback_flag = 0;
+
+ pvr2_trace(PVR2_TRACE_STBITS,
+ "Drive state check START");
+ if (pvrusb2_debug & PVR2_TRACE_STBITS) {
+ pvr2_hdw_state_log_state(hdw);
+ }
+
+ /* Process all state and get back over disposition */
+ state_updated = pvr2_hdw_state_update(hdw);
+
+ /* Update master state based upon all other states. */
+ if (!hdw->flag_ok) {
+ st = PVR2_STATE_DEAD;
+ } else if (hdw->fw1_state != FW1_STATE_OK) {
+ st = PVR2_STATE_COLD;
+ } else if (!hdw->state_encoder_ok) {
+ st = PVR2_STATE_WARM;
+ } else if (hdw->flag_tripped || hdw->flag_decoder_missed) {
+ st = PVR2_STATE_ERROR;
+ } else if (hdw->state_encoder_run &&
+ hdw->state_decoder_run &&
+ hdw->state_usbstream_run) {
+ st = PVR2_STATE_RUN;
+ } else {
+ st = PVR2_STATE_READY;
+ }
+ if (hdw->master_state != st) {
+ pvr2_trace(PVR2_TRACE_STATE,
+ "Device state change from %s to %s",
+ pvr2_get_state_name(hdw->master_state),
+ pvr2_get_state_name(st));
+ hdw->master_state = st;
+ state_updated = !0;
+ callback_flag = !0;
+ }
+ if (state_updated) {
+ /* Trigger anyone waiting on any state changes here. */
+ wake_up(&hdw->state_wait_data);
+ }
+
+ if (pvrusb2_debug & PVR2_TRACE_STBITS) {
+ pvr2_hdw_state_log_state(hdw);
+ }
+ pvr2_trace(PVR2_TRACE_STBITS,
+ "Drive state check DONE callback=%d",callback_flag);
+
+ return callback_flag;
+}
+
+
+/* Cause kernel thread to check / update driver state */
+static void pvr2_hdw_state_sched(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_stale) return;
+ hdw->state_stale = !0;
+ trace_stbit("state_stale",hdw->state_stale);
+ queue_work(hdw->workqueue,&hdw->workpoll);
+}
+
+
+void pvr2_hdw_get_debug_info_unlocked(const struct pvr2_hdw *hdw,
+ struct pvr2_hdw_debug_info *ptr)
{
ptr->big_lock_held = hdw->big_lock_held;
ptr->ctl_lock_held = hdw->ctl_lock_held;
- ptr->flag_ok = hdw->flag_ok;
ptr->flag_disconnected = hdw->flag_disconnected;
ptr->flag_init_ok = hdw->flag_init_ok;
- ptr->flag_streaming_enabled = hdw->flag_streaming_enabled;
- ptr->subsys_flags = hdw->subsys_enabled_mask;
+ ptr->flag_ok = hdw->flag_ok;
+ ptr->fw1_state = hdw->fw1_state;
+ ptr->flag_decoder_missed = hdw->flag_decoder_missed;
+ ptr->flag_tripped = hdw->flag_tripped;
+ ptr->state_encoder_ok = hdw->state_encoder_ok;
+ ptr->state_encoder_run = hdw->state_encoder_run;
+ ptr->state_decoder_run = hdw->state_decoder_run;
+ ptr->state_usbstream_run = hdw->state_usbstream_run;
+ ptr->state_decoder_quiescent = hdw->state_decoder_quiescent;
+ ptr->state_pipeline_config = hdw->state_pipeline_config;
+ ptr->state_pipeline_req = hdw->state_pipeline_req;
+ ptr->state_pipeline_pause = hdw->state_pipeline_pause;
+ ptr->state_pipeline_idle = hdw->state_pipeline_idle;
ptr->cmd_debug_state = hdw->cmd_debug_state;
ptr->cmd_code = hdw->cmd_debug_code;
ptr->cmd_debug_write_len = hdw->cmd_debug_write_len;
@@ -3494,6 +3789,15 @@ void pvr2_hdw_get_debug_info(const struct pvr2_hdw *hdw,
}
+void pvr2_hdw_get_debug_info_locked(struct pvr2_hdw *hdw,
+ struct pvr2_hdw_debug_info *ptr)
+{
+ LOCK_TAKE(hdw->ctl_lock); do {
+ pvr2_hdw_get_debug_info_unlocked(hdw,ptr);
+ } while(0); LOCK_GIVE(hdw->ctl_lock);
+}
+
+
int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *dp)
{
return pvr2_read_register(hdw,PVR2_GPIO_DIR,dp);
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h
index 6bc05192a..b5fb55952 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h
@@ -45,27 +45,6 @@
#define PVR2_CVAL_INPUT_COMPOSITE 2
#define PVR2_CVAL_INPUT_RADIO 3
-/* Subsystem definitions - these are various pieces that can be
- independently stopped / started. Usually you don't want to mess with
- this directly (let the driver handle things itself), but it is useful
- for debugging. */
-#define PVR2_SUBSYS_B_ENC_FIRMWARE 0
-#define PVR2_SUBSYS_B_ENC_CFG 1
-#define PVR2_SUBSYS_B_DIGITIZER_RUN 2
-#define PVR2_SUBSYS_B_USBSTREAM_RUN 3
-#define PVR2_SUBSYS_B_ENC_RUN 4
-
-#define PVR2_SUBSYS_CFG_ALL ( \
- (1 << PVR2_SUBSYS_B_ENC_FIRMWARE) | \
- (1 << PVR2_SUBSYS_B_ENC_CFG) )
-#define PVR2_SUBSYS_RUN_ALL ( \
- (1 << PVR2_SUBSYS_B_DIGITIZER_RUN) | \
- (1 << PVR2_SUBSYS_B_USBSTREAM_RUN) | \
- (1 << PVR2_SUBSYS_B_ENC_RUN) )
-#define PVR2_SUBSYS_ALL ( \
- PVR2_SUBSYS_CFG_ALL | \
- PVR2_SUBSYS_RUN_ALL )
-
enum pvr2_config {
pvr2_config_empty, /* No configuration */
pvr2_config_mpeg, /* Encoded / compressed video */
@@ -80,8 +59,41 @@ enum pvr2_v4l_type {
pvr2_v4l_type_radio,
};
+/* Major states that we can be in:
+ *
+ * DEAD - Device is in an unusable state and cannot be recovered. This
+ * can happen if we completely lose the ability to communicate with it
+ * (but it might still on the bus). In this state there's nothing we can
+ * do; it must be replugged in order to recover.
+ *
+ * COLD - Device is in an unusuable state, needs microcontroller firmware.
+ *
+ * WARM - We can communicate with the device and the proper
+ * microcontroller firmware is running, but other device initialization is
+ * still needed (e.g. encoder firmware).
+ *
+ * ERROR - A problem prevents capture operation (e.g. encoder firmware
+ * missing).
+ *
+ * READY - Device is operational, but not streaming.
+ *
+ * RUN - Device is streaming.
+ *
+ */
+#define PVR2_STATE_NONE 0
+#define PVR2_STATE_DEAD 1
+#define PVR2_STATE_COLD 2
+#define PVR2_STATE_WARM 3
+#define PVR2_STATE_ERROR 4
+#define PVR2_STATE_READY 5
+#define PVR2_STATE_RUN 6
+
+/* Translate configuration enum to a string label */
const char *pvr2_config_get_name(enum pvr2_config);
+/* Translate a master state enum to a string label */
+const char *pvr2_hdw_get_state_name(unsigned int);
+
struct pvr2_hdw;
/* Create and return a structure for interacting with the underlying
@@ -89,20 +101,6 @@ struct pvr2_hdw;
struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
const struct usb_device_id *devid);
-/* Poll for background activity (if any) */
-void pvr2_hdw_poll(struct pvr2_hdw *);
-
-/* Trigger a poll to take place later at a convenient time */
-#if 0
-void pvr2_hdw_poll_trigger(struct pvr2_hdw *);
-#endif /* 0 */
-void pvr2_hdw_poll_trigger_unlocked(struct pvr2_hdw *);
-
-/* Register a callback used to trigger a future poll */
-void pvr2_hdw_setup_poll_trigger(struct pvr2_hdw *,
- void (*func)(void *),
- void *data);
-
#if 0
/* Get pointer to structure given unit number */
struct pvr2_hdw *pvr2_hdw_find(int unit_number);
@@ -111,14 +109,10 @@ struct pvr2_hdw *pvr2_hdw_find(int unit_number);
/* Destroy hardware interaction structure */
void pvr2_hdw_destroy(struct pvr2_hdw *);
-/* Set up the structure and attempt to put the device into a usable state.
- This can be a time-consuming operation, which is why it is not done
- internally as part of the create() step. Return value is exactly the
- same as pvr2_hdw_init_ok(). */
-int pvr2_hdw_setup(struct pvr2_hdw *);
-
-/* Initialization succeeded */
-int pvr2_hdw_init_ok(struct pvr2_hdw *);
+/* Register a function to be called whenever the master state changes. */
+void pvr2_hdw_set_state_callback(struct pvr2_hdw *,
+ void (*callback_func)(void *),
+ void *callback_data);
/* Return true if in the ready (normal) state */
int pvr2_hdw_dev_ok(struct pvr2_hdw *);
@@ -170,12 +164,21 @@ int pvr2_hdw_get_tuner_status(struct pvr2_hdw *,struct v4l2_tuner *);
/* Query device and see if it thinks it is on a high-speed USB link */
int pvr2_hdw_is_hsm(struct pvr2_hdw *);
+/* Return a string token representative of the hardware type */
+const char *pvr2_hdw_get_type(struct pvr2_hdw *);
+
+/* Return a single line description of the hardware type */
+const char *pvr2_hdw_get_desc(struct pvr2_hdw *);
+
/* Turn streaming on/off */
int pvr2_hdw_set_streaming(struct pvr2_hdw *,int);
/* Find out if streaming is on */
int pvr2_hdw_get_streaming(struct pvr2_hdw *);
+/* Retrieve driver overall state */
+int pvr2_hdw_get_state(struct pvr2_hdw *);
+
/* Configure the type of stream to generate */
int pvr2_hdw_set_stream_type(struct pvr2_hdw *, enum pvr2_config);
@@ -186,26 +189,6 @@ struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *);
int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std,
unsigned int idx);
-/* Enable / disable various pieces of hardware. Items to change are
- identified by bit positions within msk, and new state for each item is
- identified by corresponding bit positions within val. */
-void pvr2_hdw_subsys_bit_chg(struct pvr2_hdw *hdw,
- unsigned long msk,unsigned long val);
-
-/* Retrieve mask indicating which pieces of hardware are currently enabled
- / configured. */
-unsigned long pvr2_hdw_subsys_get(struct pvr2_hdw *);
-
-/* Adjust mask of what get shut down when streaming is stopped. This is a
- debugging aid. */
-void pvr2_hdw_subsys_stream_bit_chg(struct pvr2_hdw *hdw,
- unsigned long msk,unsigned long val);
-
-/* Retrieve mask indicating which pieces of hardware are disabled when
- streaming is turned off. */
-unsigned long pvr2_hdw_subsys_stream_get(struct pvr2_hdw *);
-
-
/* Enable / disable retrieval of CPU firmware or prom contents. This must
be enabled before pvr2_hdw_cpufw_get() will function. Note that doing
this may prevent the device from running (and leaving this mode may
@@ -262,6 +245,9 @@ void pvr2_hdw_cpureset_assert(struct pvr2_hdw *,int);
/* Execute a USB-commanded device reset */
void pvr2_hdw_device_reset(struct pvr2_hdw *);
+/* Reset worker's error trapping circuit breaker */
+int pvr2_hdw_untrip(struct pvr2_hdw *);
+
/* Execute hard reset command (after this point it's likely that the
encoder will have to be reconfigured). This also clears the "useless"
state. */
@@ -284,11 +270,21 @@ int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val);
struct pvr2_hdw_debug_info {
int big_lock_held;
int ctl_lock_held;
- int flag_ok;
int flag_disconnected;
int flag_init_ok;
- int flag_streaming_enabled;
- unsigned long subsys_flags;
+ int flag_ok;
+ int fw1_state;
+ int flag_decoder_missed;
+ int flag_tripped;
+ int state_encoder_ok;
+ int state_encoder_run;
+ int state_decoder_run;
+ int state_usbstream_run;
+ int state_decoder_quiescent;
+ int state_pipeline_config;
+ int state_pipeline_req;
+ int state_pipeline_pause;
+ int state_pipeline_idle;
int cmd_debug_state;
int cmd_debug_write_len;
int cmd_debug_read_len;
@@ -304,8 +300,20 @@ struct pvr2_hdw_debug_info {
diagnosing lockups. Note that this operation is completed without any
kind of locking and so it is not atomic and may yield inconsistent
results. This is *purely* a debugging aid. */
-void pvr2_hdw_get_debug_info(const struct pvr2_hdw *hdw,
- struct pvr2_hdw_debug_info *);
+void pvr2_hdw_get_debug_info_unlocked(const struct pvr2_hdw *hdw,
+ struct pvr2_hdw_debug_info *);
+
+/* Intrusively retrieve internal state info - this is useful for
+ diagnosing overall driver state. This operation synchronizes against
+ the overall driver mutex - so if there are locking problems this will
+ likely hang! This is *purely* a debugging aid. */
+void pvr2_hdw_get_debug_info_locked(struct pvr2_hdw *hdw,
+ struct pvr2_hdw_debug_info *);
+
+/* Report out several lines of text that describes driver internal state.
+ Results are written into the passed-in buffer. */
+unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw,
+ char *buf_ptr,unsigned int buf_size);
/* Cause modules to log their state once */
void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw);
@@ -315,9 +323,6 @@ void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw);
a debugging aid. */
int pvr2_upload_firmware2(struct pvr2_hdw *hdw);
-/* List of device types that we can match */
-extern struct usb_device_id pvr2_device_table[];
-
#endif /* __PVRUSB2_HDW_H */
/*
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
index 97f94e598..98d918420 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
@@ -978,7 +978,7 @@ static int pvr2_i2c_attach_inform(struct i2c_client *client)
list_add_tail(&cp->list,&hdw->i2c_clients);
hdw->i2c_pend_types |= PVR2_I2C_PEND_DETECT;
} while (0); mutex_unlock(&hdw->i2c_list_lock);
- if (fl) pvr2_hdw_poll_trigger_unlocked(hdw);
+ if (fl) queue_work(hdw->workqueue,&hdw->worki2csync);
return 0;
}
@@ -1066,14 +1066,16 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw)
printk(KERN_INFO "%s: IR disabled\n",hdw->name);
hdw->i2c_func[0x18] = i2c_black_hole;
} else if (ir_mode[hdw->unit_number] == 1) {
- if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) {
+ if (hdw->hdw_desc->flag_has_hauppauge_custom_ir) {
hdw->i2c_func[0x18] = i2c_24xxx_ir;
}
}
- if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) {
- hdw->i2c_func[0x1b] = i2c_hack_wm8775;
+ if (hdw->hdw_desc->flag_has_cx25840) {
hdw->i2c_func[0x44] = i2c_hack_cx25840;
}
+ if (hdw->hdw_desc->flag_has_wm8775) {
+ hdw->i2c_func[0x1b] = i2c_hack_wm8775;
+ }
// Configure the adapter and set up everything else related to it.
memcpy(&hdw->i2c_adap,&pvr2_i2c_adap_template,sizeof(hdw->i2c_adap));
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c
index 687096d1f..ea69ce6a8 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c
@@ -28,6 +28,7 @@
#include <linux/videodev2.h>
#include "pvrusb2-hdw.h"
+#include "pvrusb2-devattr.h"
#include "pvrusb2-context.h"
#include "pvrusb2-debug.h"
#include "pvrusb2-v4l2.h"
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-std.c b/linux/drivers/media/video/pvrusb2/pvrusb2-std.c
index dd49a60ec..b37854b06 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-std.c
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-std.c
@@ -51,6 +51,10 @@ struct std_name {
V4L2_STD_NTSC_M_KR| \
V4L2_STD_NTSC_443)
+#define CSTD_ATSC \
+ (V4L2_STD_ATSC_8_VSB| \
+ V4L2_STD_ATSC_16_VSB)
+
#define CSTD_SECAM \
(V4L2_STD_SECAM_B| \
V4L2_STD_SECAM_D| \
@@ -83,6 +87,7 @@ static const struct std_name std_groups[] = {
{"PAL",CSTD_PAL},
{"NTSC",CSTD_NTSC},
{"SECAM",CSTD_SECAM},
+ {"ATSC",CSTD_ATSC},
};
/* Mapping of standard bits to modulation system */
@@ -105,6 +110,8 @@ static const struct std_name std_items[] = {
{"N",TSTD_N},
{"Nc",TSTD_Nc},
{"60",TSTD_60},
+ {"8VSB",V4L2_STD_ATSC_8_VSB},
+ {"16VSB",V4L2_STD_ATSC_16_VSB},
};
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
index 7bcf0996f..7825d5b6f 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
@@ -44,10 +44,14 @@ struct pvr2_sysfs {
struct device_attribute attr_v4l_radio_minor_number;
struct device_attribute attr_unit_number;
struct device_attribute attr_bus_info;
+ struct device_attribute attr_hdw_name;
+ struct device_attribute attr_hdw_desc;
int v4l_minor_number_created_ok;
int v4l_radio_minor_number_created_ok;
int unit_number_created_ok;
int bus_info_created_ok;
+ int hdw_name_created_ok;
+ int hdw_desc_created_ok;
};
#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
@@ -721,6 +725,14 @@ static void class_dev_destroy(struct pvr2_sysfs *sfp)
pvr2_sysfs_tear_down_debugifc(sfp);
#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
pvr2_sysfs_tear_down_controls(sfp);
+ if (sfp->hdw_desc_created_ok) {
+ device_remove_file(sfp->class_dev,
+ &sfp->attr_hdw_desc);
+ }
+ if (sfp->hdw_name_created_ok) {
+ device_remove_file(sfp->class_dev,
+ &sfp->attr_hdw_name);
+ }
if (sfp->bus_info_created_ok) {
device_remove_file(sfp->class_dev,
&sfp->attr_bus_info);
@@ -767,6 +779,28 @@ static ssize_t bus_info_show(struct device *class_dev,
}
+static ssize_t hdw_name_show(struct device *class_dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = (struct pvr2_sysfs *)class_dev->driver_data;
+ if (!sfp) return -EINVAL;
+ return scnprintf(buf,PAGE_SIZE,"%s\n",
+ pvr2_hdw_get_type(sfp->channel.hdw));
+}
+
+
+static ssize_t hdw_desc_show(struct device *class_dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = (struct pvr2_sysfs *)class_dev->driver_data;
+ if (!sfp) return -EINVAL;
+ return scnprintf(buf,PAGE_SIZE,"%s\n",
+ pvr2_hdw_get_desc(sfp->channel.hdw));
+}
+
+
static ssize_t v4l_radio_minor_number_show(struct device *class_dev,
struct device_attribute *attr,
char *buf)
@@ -880,6 +914,32 @@ static void class_dev_create(struct pvr2_sysfs *sfp,
sfp->bus_info_created_ok = !0;
}
+ sfp->attr_hdw_name.attr.name = "device_hardware_type";
+ sfp->attr_hdw_name.attr.mode = S_IRUGO;
+ sfp->attr_hdw_name.show = hdw_name_show;
+ sfp->attr_hdw_name.store = NULL;
+ ret = device_create_file(sfp->class_dev,
+ &sfp->attr_hdw_name);
+ if (ret < 0) {
+ printk(KERN_WARNING "%s: device_create_file error: %d\n",
+ __FUNCTION__, ret);
+ } else {
+ sfp->hdw_name_created_ok = !0;
+ }
+
+ sfp->attr_hdw_desc.attr.name = "device_hardware_description";
+ sfp->attr_hdw_desc.attr.mode = S_IRUGO;
+ sfp->attr_hdw_desc.show = hdw_desc_show;
+ sfp->attr_hdw_desc.store = NULL;
+ ret = device_create_file(sfp->class_dev,
+ &sfp->attr_hdw_desc);
+ if (ret < 0) {
+ printk(KERN_WARNING "%s: device_create_file error: %d\n",
+ __FUNCTION__, ret);
+ } else {
+ sfp->hdw_desc_created_ok = !0;
+ }
+
pvr2_sysfs_add_controls(sfp);
#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
pvr2_sysfs_add_debugifc(sfp);
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
index b192db67e..69f2a7d32 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -206,6 +206,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability));
strlcpy(cap->bus_info,pvr2_hdw_get_bus_info(hdw),
sizeof(cap->bus_info));
+ strlcpy(cap->card,pvr2_hdw_get_desc(hdw),sizeof(cap->card));
ret = 0;
break;
@@ -1016,10 +1017,8 @@ static int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh)
sp = fh->dev_info->stream->stream;
pvr2_stream_set_callback(sp,(pvr2_stream_callback)pvr2_v4l2_notify,fh);
pvr2_hdw_set_stream_type(hdw,fh->dev_info->config);
- pvr2_hdw_set_streaming(hdw,!0);
- ret = pvr2_ioread_set_enabled(fh->rhp,!0);
-
- return ret;
+ if ((ret = pvr2_hdw_set_streaming(hdw,!0)) < 0) return ret;
+ return pvr2_ioread_set_enabled(fh->rhp,!0);
}
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
index b063110de..36ce6dc66 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
@@ -50,29 +50,50 @@ struct pvr2_v4l_decoder {
};
+struct routing_scheme {
+ const int *def;
+ unsigned int cnt;
+};
+
+
+static const int routing_scheme0[] = {
+ [PVR2_CVAL_INPUT_TV] = SAA7115_COMPOSITE4,
+ /* In radio mode, we mute the video, but point at one
+ spot just to stay consistent */
+ [PVR2_CVAL_INPUT_RADIO] = SAA7115_COMPOSITE5,
+ [PVR2_CVAL_INPUT_COMPOSITE] = SAA7115_COMPOSITE5,
+ [PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2,
+};
+
+static const struct routing_scheme routing_schemes[] = {
+ [PVR2_ROUTING_SCHEME_HAUPPAUGE] = {
+ .def = routing_scheme0,
+ .cnt = ARRAY_SIZE(routing_scheme0),
+ },
+};
+
static void set_input(struct pvr2_v4l_decoder *ctxt)
{
struct pvr2_hdw *hdw = ctxt->hdw;
struct v4l2_routing route;
+ const struct routing_scheme *sp;
+ unsigned int sid = hdw->hdw_desc->signal_routing_scheme;
pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_input(%d)",hdw->input_val);
- switch(hdw->input_val) {
- case PVR2_CVAL_INPUT_TV:
- route.input = SAA7115_COMPOSITE4;
- break;
- case PVR2_CVAL_INPUT_COMPOSITE:
- route.input = SAA7115_COMPOSITE5;
- break;
- case PVR2_CVAL_INPUT_SVIDEO:
- route.input = SAA7115_SVIDEO2;
- break;
- case PVR2_CVAL_INPUT_RADIO:
- // In radio mode, we mute the video, but point at one
- // spot just to stay consistent
- route.input = SAA7115_COMPOSITE5;
- default:
+
+ if ((sid < ARRAY_SIZE(routing_schemes)) &&
+ ((sp = routing_schemes + sid) != 0) &&
+ (hdw->input_val >= 0) &&
+ (hdw->input_val < sp->cnt)) {
+ route.input = sp->def[hdw->input_val];
+ } else {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "*** WARNING *** i2c v4l2 set_input:"
+ " Invalid routing scheme (%u) and/or input (%d)",
+ sid,hdw->input_val);
return;
}
+
route.output = 0;
pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_VIDEO_ROUTING,&route);
}
@@ -130,7 +151,7 @@ static const struct pvr2_v4l_decoder_ops decoder_ops[] = {
static void decoder_detach(struct pvr2_v4l_decoder *ctxt)
{
ctxt->client->handler = NULL;
- ctxt->hdw->decoder_ctrl = NULL;
+ pvr2_hdw_set_decoder(ctxt->hdw,NULL);
kfree(ctxt);
}
@@ -218,7 +239,7 @@ int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *hdw,
ctxt->client = cp;
ctxt->hdw = hdw;
ctxt->stale_mask = (1 << ARRAY_SIZE(decoder_ops)) - 1;
- hdw->decoder_ctrl = &ctxt->ctrl;
+ pvr2_hdw_set_decoder(hdw,&ctxt->ctrl);
cp->handler = &ctxt->handler;
pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x saa711x V4L2 handler set up",
cp->client->addr);
diff --git a/linux/drivers/media/video/saa7134/saa7134-core.c b/linux/drivers/media/video/saa7134/saa7134-core.c
index 639f5851b..74b881864 100644
--- a/linux/drivers/media/video/saa7134/saa7134-core.c
+++ b/linux/drivers/media/video/saa7134/saa7134-core.c
@@ -930,6 +930,7 @@ static struct video_device *vdev_init(struct saa7134_dev *dev,
vfd->dev = &dev->pci->dev;
vfd->release = video_device_release;
#endif
+ vfd->debug = video_debug;
snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
dev->name, type, saa7134_boards[dev->board].name);
return vfd;
diff --git a/linux/drivers/media/video/saa7134/saa7134-video.c b/linux/drivers/media/video/saa7134/saa7134-video.c
index b8523aa91..7d04a24e0 100644
--- a/linux/drivers/media/video/saa7134/saa7134-video.c
+++ b/linux/drivers/media/video/saa7134/saa7134-video.c
@@ -38,7 +38,7 @@
/* ------------------------------------------------------------------ */
-static unsigned int video_debug = 0;
+unsigned int video_debug;
static unsigned int gbuffers = 8;
static unsigned int noninterlaced = 0;
static unsigned int gbufsize = 720*576*4;
@@ -54,7 +54,7 @@ module_param_string(secam, secam, sizeof(secam), 0644);
MODULE_PARM_DESC(secam, "force SECAM variant, either DK,L or Lc");
-#define dprintk(fmt, arg...) if (video_debug) \
+#define dprintk(fmt, arg...) if (video_debug&0x04) \
printk(KERN_DEBUG "%s/video: " fmt, dev->name , ## arg)
/* ------------------------------------------------------------------ */
@@ -217,6 +217,12 @@ static struct saa7134_format formats[] = {
.vbi_v_start_1 = 273, \
.src_timing = 7
+#define SAA7134_NORMS \
+ V4L2_STD_PAL | V4L2_STD_PAL_N | \
+ V4L2_STD_PAL_Nc | V4L2_STD_SECAM | \
+ V4L2_STD_NTSC | V4L2_STD_PAL_M | \
+ V4L2_STD_PAL_60
+
static struct saa7134_tvnorm tvnorms[] = {
{
.name = "PAL", /* autodetect */
@@ -542,7 +548,6 @@ void res_free(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bits)
static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm)
{
-
dprintk("set tv norm = %s\n",norm->name);
dev->tvnorm = norm;
@@ -561,7 +566,6 @@ static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm)
dev->crop_current = dev->crop_defrect;
saa7134_set_tvnorm_hw(dev);
-
}
static void video_mux(struct saa7134_dev *dev, int input)
@@ -1119,8 +1123,11 @@ static struct videobuf_queue_ops video_qops = {
/* ------------------------------------------------------------------ */
-static int get_control(struct saa7134_dev *dev, struct v4l2_control *c)
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *c)
{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
const struct v4l2_queryctrl* ctrl;
ctrl = ctrl_by_id(c->id);
@@ -1166,16 +1173,26 @@ static int get_control(struct saa7134_dev *dev, struct v4l2_control *c)
return 0;
}
-static int set_control(struct saa7134_dev *dev, struct saa7134_fh *fh,
- struct v4l2_control *c)
+static int vidioc_s_ctrl(struct file *file, void *f,
+ struct v4l2_control *c)
{
const struct v4l2_queryctrl* ctrl;
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
unsigned long flags;
int restart_overlay = 0;
+ int err = -EINVAL;
+
+ err = v4l2_prio_check(&dev->prio, &fh->prio);
+ if (0 != err)
+ return err;
+
+ mutex_lock(&dev->lock);
ctrl = ctrl_by_id(c->id);
if (NULL == ctrl)
- return -EINVAL;
+ goto error;
+
dprintk("set_control name=%s val=%d\n",ctrl->name,c->value);
switch (ctrl->type) {
case V4L2_CTRL_TYPE_BOOLEAN:
@@ -1255,7 +1272,7 @@ static int set_control(struct saa7134_dev *dev, struct saa7134_fh *fh,
break;
}
default:
- return -EINVAL;
+ goto error;
}
if (restart_overlay && fh && res_check(fh, RESOURCE_OVERLAY)) {
spin_lock_irqsave(&dev->slock,flags);
@@ -1263,7 +1280,11 @@ static int set_control(struct saa7134_dev *dev, struct saa7134_fh *fh,
start_preview(dev,fh);
spin_unlock_irqrestore(&dev->slock,flags);
}
- return 0;
+ err = 0;
+
+error:
+ mutex_unlock(&dev->lock);
+ return err;
}
/* ------------------------------------------------------------------ */
@@ -1486,8 +1507,11 @@ static int video_mmap(struct file *file, struct vm_area_struct * vma)
/* ------------------------------------------------------------------ */
-static void saa7134_vbi_fmt(struct saa7134_dev *dev, struct v4l2_format *f)
+static int vidioc_try_get_set_fmt_vbi(struct file *file, void *priv,
+ struct v4l2_format *f)
{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
struct saa7134_tvnorm *norm = dev->tvnorm;
f->fmt.vbi.sampling_rate = 6750000 * 4;
@@ -1507,837 +1531,805 @@ static void saa7134_vbi_fmt(struct saa7134_dev *dev, struct v4l2_format *f)
f->fmt.vbi.start[1] += 3*2;
}
#endif
+ return 0;
}
-static int saa7134_g_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh,
- struct v4l2_format *f)
+static int vidioc_g_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
{
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- memset(&f->fmt.pix,0,sizeof(f->fmt.pix));
- f->fmt.pix.width = fh->width;
- f->fmt.pix.height = fh->height;
- f->fmt.pix.field = fh->cap.field;
- f->fmt.pix.pixelformat = fh->fmt->fourcc;
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * fh->fmt->depth) >> 3;
- f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
- return 0;
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (saa7134_no_overlay > 0) {
- printk ("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
- return -EINVAL;
- }
- f->fmt.win = fh->win;
- return 0;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- saa7134_vbi_fmt(dev,f);
- return 0;
- default:
- return -EINVAL;
- }
+ struct saa7134_fh *fh = priv;
+
+ f->fmt.pix.width = fh->width;
+ f->fmt.pix.height = fh->height;
+ f->fmt.pix.field = fh->cap.field;
+ f->fmt.pix.pixelformat = fh->fmt->fourcc;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fh->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+ return 0;
}
-static int saa7134_try_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh,
- struct v4l2_format *f)
+static int vidioc_g_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
{
- int err;
+ struct saa7134_fh *fh = priv;
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- {
- struct saa7134_format *fmt;
- enum v4l2_field field;
- unsigned int maxw, maxh;
+ if (saa7134_no_overlay > 0) {
+ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
+ }
+ f->fmt.win = fh->win;
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- if (NULL == fmt)
- return -EINVAL;
+ return 0;
+}
- field = f->fmt.pix.field;
- maxw = min(dev->crop_current.width*4, dev->crop_bounds.width);
- maxh = min(dev->crop_current.height*4, dev->crop_bounds.height);
+static int vidioc_try_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa7134_format *fmt;
+ enum v4l2_field field;
+ unsigned int maxw, maxh;
- if (V4L2_FIELD_ANY == field) {
- field = (f->fmt.pix.height > maxh/2)
- ? V4L2_FIELD_INTERLACED
- : V4L2_FIELD_BOTTOM;
- }
- switch (field) {
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- maxh = maxh / 2;
- break;
- case V4L2_FIELD_INTERLACED:
- break;
- default:
- return -EINVAL;
- }
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
- f->fmt.pix.field = field;
- if (f->fmt.pix.width < 48)
- f->fmt.pix.width = 48;
- if (f->fmt.pix.height < 32)
- f->fmt.pix.height = 32;
- if (f->fmt.pix.width > maxw)
- f->fmt.pix.width = maxw;
- if (f->fmt.pix.height > maxh)
- f->fmt.pix.height = maxh;
- f->fmt.pix.width &= ~0x03;
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * fmt->depth) >> 3;
- f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
+ field = f->fmt.pix.field;
+ maxw = min(dev->crop_current.width*4, dev->crop_bounds.width);
+ maxh = min(dev->crop_current.height*4, dev->crop_bounds.height);
- return 0;
+ if (V4L2_FIELD_ANY == field) {
+ field = (f->fmt.pix.height > maxh/2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
}
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (saa7134_no_overlay > 0) {
- printk ("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
- return -EINVAL;
- }
- err = verify_preview(dev,&f->fmt.win);
- if (0 != err)
- return err;
- return 0;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- saa7134_vbi_fmt(dev,f);
- return 0;
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
default:
return -EINVAL;
}
+
+ f->fmt.pix.field = field;
+ if (f->fmt.pix.width < 48)
+ f->fmt.pix.width = 48;
+ if (f->fmt.pix.height < 32)
+ f->fmt.pix.height = 32;
+ if (f->fmt.pix.width > maxw)
+ f->fmt.pix.width = maxw;
+ if (f->fmt.pix.height > maxh)
+ f->fmt.pix.height = maxh;
+ f->fmt.pix.width &= ~0x03;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
}
-static int saa7134_s_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh,
- struct v4l2_format *f)
+static int vidioc_try_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
{
- unsigned long flags;
- int err;
-
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- err = saa7134_try_fmt(dev,fh,f);
- if (0 != err)
- return err;
-
- fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- fh->width = f->fmt.pix.width;
- fh->height = f->fmt.pix.height;
- fh->cap.field = f->fmt.pix.field;
- return 0;
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (saa7134_no_overlay > 0) {
- printk ("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
- return -EINVAL;
- }
- err = verify_preview(dev,&f->fmt.win);
- if (0 != err)
- return err;
-
- mutex_lock(&dev->lock);
- fh->win = f->fmt.win;
- fh->nclips = f->fmt.win.clipcount;
- if (fh->nclips > 8)
- fh->nclips = 8;
- if (copy_from_user(fh->clips,f->fmt.win.clips,
- sizeof(struct v4l2_clip)*fh->nclips)) {
- mutex_unlock(&dev->lock);
- return -EFAULT;
- }
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
- if (res_check(fh, RESOURCE_OVERLAY)) {
- spin_lock_irqsave(&dev->slock,flags);
- stop_preview(dev,fh);
- start_preview(dev,fh);
- spin_unlock_irqrestore(&dev->slock,flags);
- }
- mutex_unlock(&dev->lock);
- return 0;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- saa7134_vbi_fmt(dev,f);
- return 0;
- default:
+ if (saa7134_no_overlay > 0) {
+ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
return -EINVAL;
}
+
+ return verify_preview(dev, &f->fmt.win);
}
-int saa7134_common_ioctl(struct saa7134_dev *dev,
- unsigned int cmd, void *arg)
+static int vidioc_s_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
{
+ struct saa7134_fh *fh = priv;
int err;
- switch (cmd) {
- case VIDIOC_QUERYCTRL:
- {
- const struct v4l2_queryctrl *ctrl;
- struct v4l2_queryctrl *c = arg;
+ err = vidioc_try_fmt_cap(file, priv, f);
+ if (0 != err)
+ return err;
- if ((c->id < V4L2_CID_BASE ||
- c->id >= V4L2_CID_LASTP1) &&
- (c->id < V4L2_CID_PRIVATE_BASE ||
- c->id >= V4L2_CID_PRIVATE_LASTP1))
- return -EINVAL;
- ctrl = ctrl_by_id(c->id);
- *c = (NULL != ctrl) ? *ctrl : no_ctrl;
- return 0;
+ fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ fh->width = f->fmt.pix.width;
+ fh->height = f->fmt.pix.height;
+ fh->cap.field = f->fmt.pix.field;
+ return 0;
+}
+
+static int vidioc_s_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int err;
+ unsigned int flags;
+
+ if (saa7134_no_overlay > 0) {
+ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
}
- case VIDIOC_G_CTRL:
- return get_control(dev,arg);
- case VIDIOC_S_CTRL:
- {
- mutex_lock(&dev->lock);
- err = set_control(dev,NULL,arg);
- mutex_unlock(&dev->lock);
+ err = verify_preview(dev, &f->fmt.win);
+ if (0 != err)
return err;
- }
- /* --- input switching --------------------------------------- */
- case VIDIOC_ENUMINPUT:
- {
- struct v4l2_input *i = arg;
- unsigned int n;
- n = i->index;
- if (n >= SAA7134_INPUT_MAX)
- return -EINVAL;
- if (NULL == card_in(dev,i->index).name)
- return -EINVAL;
- memset(i,0,sizeof(*i));
- i->index = n;
- i->type = V4L2_INPUT_TYPE_CAMERA;
- strcpy(i->name,card_in(dev,n).name);
- if (card_in(dev,n).tv)
- i->type = V4L2_INPUT_TYPE_TUNER;
- i->audioset = 1;
- if (n == dev->ctl_input) {
- int v1 = saa_readb(SAA7134_STATUS_VIDEO1);
- int v2 = saa_readb(SAA7134_STATUS_VIDEO2);
-
- if (0 != (v1 & 0x40))
- i->status |= V4L2_IN_ST_NO_H_LOCK;
- if (0 != (v2 & 0x40))
- i->status |= V4L2_IN_ST_NO_SYNC;
- if (0 != (v2 & 0x0e))
- i->status |= V4L2_IN_ST_MACROVISION;
- }
- for (n = 0; n < TVNORMS; n++)
- i->std |= tvnorms[n].id;
- return 0;
- }
- case VIDIOC_G_INPUT:
- {
- int *i = arg;
- *i = dev->ctl_input;
- return 0;
- }
- case VIDIOC_S_INPUT:
- {
- int *i = arg;
+ mutex_lock(&dev->lock);
- if (*i < 0 || *i >= SAA7134_INPUT_MAX)
- return -EINVAL;
- if (NULL == card_in(dev,*i).name)
- return -EINVAL;
- mutex_lock(&dev->lock);
- video_mux(dev,*i);
+ fh->win = f->fmt.win;
+ fh->nclips = f->fmt.win.clipcount;
+
+ if (fh->nclips > 8)
+ fh->nclips = 8;
+
+ if (copy_from_user(fh->clips, f->fmt.win.clips,
+ sizeof(struct v4l2_clip)*fh->nclips)) {
mutex_unlock(&dev->lock);
- return 0;
+ return -EFAULT;
}
+ if (res_check(fh, RESOURCE_OVERLAY)) {
+ spin_lock_irqsave(&dev->slock, flags);
+ stop_preview(dev, fh);
+ start_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
}
+
+ mutex_unlock(&dev->lock);
return 0;
}
-EXPORT_SYMBOL(saa7134_common_ioctl);
-/*
- * This function is _not_ called directly, but from
- * video_generic_ioctl (and maybe others). userspace
- * copying is done already, arg is a kernel pointer.
- */
-static int video_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
{
- struct saa7134_fh *fh = file->private_data;
+ const struct v4l2_queryctrl *ctrl;
+
+ if ((c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1) &&
+ (c->id < V4L2_CID_PRIVATE_BASE ||
+ c->id >= V4L2_CID_PRIVATE_LASTP1))
+ return -EINVAL;
+ ctrl = ctrl_by_id(c->id);
+ *c = (NULL != ctrl) ? *ctrl : no_ctrl;
+ return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ unsigned int n;
+
+ n = i->index;
+ if (n >= SAA7134_INPUT_MAX)
+ return -EINVAL;
+ if (NULL == card_in(dev, i->index).name)
+ return -EINVAL;
+ memset(i, 0, sizeof(*i));
+ i->index = n;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ strcpy(i->name, card_in(dev, n).name);
+ if (card_in(dev, n).tv)
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ i->audioset = 1;
+ if (n == dev->ctl_input) {
+ int v1 = saa_readb(SAA7134_STATUS_VIDEO1);
+ int v2 = saa_readb(SAA7134_STATUS_VIDEO2);
+
+ if (0 != (v1 & 0x40))
+ i->status |= V4L2_IN_ST_NO_H_LOCK;
+ if (0 != (v2 & 0x40))
+ i->status |= V4L2_IN_ST_NO_SYNC;
+ if (0 != (v2 & 0x0e))
+ i->status |= V4L2_IN_ST_MACROVISION;
+ }
+ i->std = SAA7134_NORMS;
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ *i = dev->ctl_input;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct saa7134_fh *fh = priv;
struct saa7134_dev *dev = fh->dev;
- unsigned long flags;
int err;
- if (video_debug > 1)
- v4l_print_ioctl(dev->name,cmd);
-
- switch (cmd) {
- case VIDIOC_S_CTRL:
- case VIDIOC_S_STD:
- case VIDIOC_S_INPUT:
- case VIDIOC_S_TUNER:
- case VIDIOC_S_FREQUENCY:
- err = v4l2_prio_check(&dev->prio,&fh->prio);
- if (0 != err)
- return err;
- }
+ err = v4l2_prio_check(&dev->prio, &fh->prio);
+ if (0 != err)
+ return err;
- switch (cmd) {
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *cap = arg;
- unsigned int tuner_type = dev->tuner_type;
-
- memset(cap,0,sizeof(*cap));
- strcpy(cap->driver, "saa7134");
- strlcpy(cap->card, saa7134_boards[dev->board].name,
- sizeof(cap->card));
- sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
- cap->version = SAA7134_VERSION_CODE;
- cap->capabilities =
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_VBI_CAPTURE |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING |
- V4L2_CAP_TUNER;
- if (saa7134_no_overlay <= 0) {
- cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
- }
+ if (i < 0 || i >= SAA7134_INPUT_MAX)
+ return -EINVAL;
+ if (NULL == card_in(dev, i).name)
+ return -EINVAL;
+ mutex_lock(&dev->lock);
+ video_mux(dev, i);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
- if ((tuner_type == TUNER_ABSENT) || (tuner_type == UNSET))
- cap->capabilities &= ~V4L2_CAP_TUNER;
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ unsigned int tuner_type = dev->tuner_type;
+
+ strcpy(cap->driver, "saa7134");
+ strlcpy(cap->card, saa7134_boards[dev->board].name,
+ sizeof(cap->card));
+ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+ cap->version = SAA7134_VERSION_CODE;
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VBI_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_TUNER;
+ if (saa7134_no_overlay <= 0)
+ cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
+
+ if ((tuner_type == TUNER_ABSENT) || (tuner_type == UNSET))
+ cap->capabilities &= ~V4L2_CAP_TUNER;
return 0;
- }
+}
- /* --- tv standards ------------------------------------------ */
- case VIDIOC_ENUMSTD:
- {
- struct v4l2_standard *e = arg;
- unsigned int i;
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * id)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ unsigned long flags;
+ unsigned int i;
+ v4l2_std_id fixup;
+ int err;
- i = e->index;
- if (i >= TVNORMS)
- return -EINVAL;
- err = v4l2_video_std_construct(e, tvnorms[e->index].id,
- tvnorms[e->index].name);
- e->index = i;
- if (err < 0)
- return err;
- return 0;
- }
- case VIDIOC_G_STD:
- {
- v4l2_std_id *id = arg;
+ err = v4l2_prio_check(&dev->prio, &fh->prio);
+ if (0 != err)
+ return err;
- *id = dev->tvnorm->id;
- return 0;
- }
- case VIDIOC_S_STD:
- {
- v4l2_std_id *id = arg;
- unsigned int i;
- v4l2_std_id fixup;
+ for (i = 0; i < TVNORMS; i++)
+ if (*id == tvnorms[i].id)
+ break;
+ if (i == TVNORMS)
for (i = 0; i < TVNORMS; i++)
- if (*id == tvnorms[i].id)
+ if (*id & tvnorms[i].id)
break;
- if (i == TVNORMS)
- for (i = 0; i < TVNORMS; i++)
- if (*id & tvnorms[i].id)
- break;
- if (i == TVNORMS)
- return -EINVAL;
- if ((*id & V4L2_STD_SECAM) && (secam[0] != '-')) {
- if (secam[0] == 'L' || secam[0] == 'l') {
- if (secam[1] == 'C' || secam[1] == 'c')
- fixup = V4L2_STD_SECAM_LC;
- else
- fixup = V4L2_STD_SECAM_L;
- } else {
- if (secam[0] == 'D' || secam[0] == 'd')
- fixup = V4L2_STD_SECAM_DK;
- else
- fixup = V4L2_STD_SECAM;
- }
- for (i = 0; i < TVNORMS; i++)
- if (fixup == tvnorms[i].id)
- break;
+ if (i == TVNORMS)
+ return -EINVAL;
+
+ if ((*id & V4L2_STD_SECAM) && (secam[0] != '-')) {
+ if (secam[0] == 'L' || secam[0] == 'l') {
+ if (secam[1] == 'C' || secam[1] == 'c')
+ fixup = V4L2_STD_SECAM_LC;
+ else
+ fixup = V4L2_STD_SECAM_L;
+ } else {
+ if (secam[0] == 'D' || secam[0] == 'd')
+ fixup = V4L2_STD_SECAM_DK;
+ else
+ fixup = V4L2_STD_SECAM;
}
- mutex_lock(&dev->lock);
- if (res_check(fh, RESOURCE_OVERLAY)) {
- spin_lock_irqsave(&dev->slock,flags);
- stop_preview(dev,fh);
- spin_unlock_irqrestore(&dev->slock, flags);
-
- set_tvnorm(dev,&tvnorms[i]);
-
- spin_lock_irqsave(&dev->slock, flags);
- start_preview(dev,fh);
- spin_unlock_irqrestore(&dev->slock,flags);
- } else
- set_tvnorm(dev,&tvnorms[i]);
- saa7134_tvaudio_do_scan(dev);
- mutex_unlock(&dev->lock);
- return 0;
+ for (i = 0; i < TVNORMS; i++)
+ if (fixup == tvnorms[i].id)
+ break;
}
- case VIDIOC_CROPCAP:
- {
- struct v4l2_cropcap *cap = arg;
+ *id = tvnorms[i].id;
- if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
- return -EINVAL;
- cap->bounds = dev->crop_bounds;
- cap->defrect = dev->crop_defrect;
- cap->pixelaspect.numerator = 1;
- cap->pixelaspect.denominator = 1;
- if (dev->tvnorm->id & V4L2_STD_525_60) {
- cap->pixelaspect.numerator = 11;
- cap->pixelaspect.denominator = 10;
- }
- if (dev->tvnorm->id & V4L2_STD_625_50) {
- cap->pixelaspect.numerator = 54;
- cap->pixelaspect.denominator = 59;
- }
- return 0;
- }
+ mutex_lock(&dev->lock);
+ if (res_check(fh, RESOURCE_OVERLAY)) {
+ spin_lock_irqsave(&dev->slock, flags);
+ stop_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
- case VIDIOC_G_CROP:
- {
- struct v4l2_crop * crop = arg;
+ set_tvnorm(dev, &tvnorms[i]);
- if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
- return -EINVAL;
- crop->c = dev->crop_current;
- return 0;
- }
- case VIDIOC_S_CROP:
- {
- struct v4l2_crop *crop = arg;
- struct v4l2_rect *b = &dev->crop_bounds;
+ spin_lock_irqsave(&dev->slock, flags);
+ start_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
+ } else
+ set_tvnorm(dev, &tvnorms[i]);
- if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
- return -EINVAL;
- if (crop->c.height < 0)
- return -EINVAL;
- if (crop->c.width < 0)
- return -EINVAL;
+ saa7134_tvaudio_do_scan(dev);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
- if (res_locked(fh->dev,RESOURCE_OVERLAY))
- return -EBUSY;
- if (res_locked(fh->dev,RESOURCE_VIDEO))
- return -EBUSY;
+static int vidioc_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cap)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
- if (crop->c.top < b->top)
- crop->c.top = b->top;
- if (crop->c.top > b->top + b->height)
- crop->c.top = b->top + b->height;
- if (crop->c.height > b->top - crop->c.top + b->height)
- crop->c.height = b->top - crop->c.top + b->height;
-
- if (crop->c.left < b->left)
- crop->c.left = b->left;
- if (crop->c.left > b->left + b->width)
- crop->c.left = b->left + b->width;
- if (crop->c.width > b->left - crop->c.left + b->width)
- crop->c.width = b->left - crop->c.left + b->width;
-
- dev->crop_current = crop->c;
- return 0;
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+ cap->bounds = dev->crop_bounds;
+ cap->defrect = dev->crop_defrect;
+ cap->pixelaspect.numerator = 1;
+ cap->pixelaspect.denominator = 1;
+ if (dev->tvnorm->id & V4L2_STD_525_60) {
+ cap->pixelaspect.numerator = 11;
+ cap->pixelaspect.denominator = 10;
+ }
+ if (dev->tvnorm->id & V4L2_STD_625_50) {
+ cap->pixelaspect.numerator = 54;
+ cap->pixelaspect.denominator = 59;
}
+ return 0;
+}
- /* --- tuner ioctls ------------------------------------------ */
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *t = arg;
- int n;
+static int vidioc_g_crop(struct file *file, void *f, struct v4l2_crop *crop)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
- if (0 != t->index)
- return -EINVAL;
- memset(t,0,sizeof(*t));
- for (n = 0; n < SAA7134_INPUT_MAX; n++)
- if (card_in(dev,n).tv)
- break;
- if (NULL != card_in(dev,n).name) {
- strcpy(t->name, "Television");
- t->type = V4L2_TUNER_ANALOG_TV;
- t->capability = V4L2_TUNER_CAP_NORM |
- V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_LANG1 |
- V4L2_TUNER_CAP_LANG2;
- t->rangehigh = 0xffffffffUL;
- t->rxsubchans = saa7134_tvaudio_getstereo(dev);
- t->audmode = saa7134_tvaudio_rx2mode(t->rxsubchans);
- }
- if (0 != (saa_readb(SAA7134_STATUS_VIDEO1) & 0x03))
- t->signal = 0xffff;
- return 0;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *t = arg;
- int rx,mode;
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+ crop->c = dev->crop_current;
+ return 0;
+}
- mode = dev->thread.mode;
- if (UNSET == mode) {
- rx = saa7134_tvaudio_getstereo(dev);
- mode = saa7134_tvaudio_rx2mode(t->rxsubchans);
- }
- if (mode != t->audmode) {
- dev->thread.mode = t->audmode;
- }
- return 0;
- }
- case VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+static int vidioc_s_crop(struct file *file, void *f, struct v4l2_crop *crop)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+ struct v4l2_rect *b = &dev->crop_bounds;
- memset(f,0,sizeof(*f));
- f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- f->frequency = dev->ctl_freq;
- return 0;
- }
- case VIDIOC_S_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+ if (crop->c.height < 0)
+ return -EINVAL;
+ if (crop->c.width < 0)
+ return -EINVAL;
- if (0 != f->tuner)
- return -EINVAL;
- if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type)
- return -EINVAL;
- if (1 == fh->radio && V4L2_TUNER_RADIO != f->type)
- return -EINVAL;
- mutex_lock(&dev->lock);
- dev->ctl_freq = f->frequency;
+ if (res_locked(fh->dev, RESOURCE_OVERLAY))
+ return -EBUSY;
+ if (res_locked(fh->dev, RESOURCE_VIDEO))
+ return -EBUSY;
+
+ if (crop->c.top < b->top)
+ crop->c.top = b->top;
+ if (crop->c.top > b->top + b->height)
+ crop->c.top = b->top + b->height;
+ if (crop->c.height > b->top - crop->c.top + b->height)
+ crop->c.height = b->top - crop->c.top + b->height;
+
+ if (crop->c.left < b->left)
+ crop->c.left = b->left;
+ if (crop->c.left > b->left + b->width)
+ crop->c.left = b->left + b->width;
+ if (crop->c.width > b->left - crop->c.left + b->width)
+ crop->c.width = b->left - crop->c.left + b->width;
+
+ dev->crop_current = crop->c;
+ return 0;
+}
- saa7134_i2c_call_clients(dev,VIDIOC_S_FREQUENCY,f);
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int n;
- saa7134_tvaudio_do_scan(dev);
- mutex_unlock(&dev->lock);
- return 0;
- }
+ if (0 != t->index)
+ return -EINVAL;
+ memset(t, 0, sizeof(*t));
+ for (n = 0; n < SAA7134_INPUT_MAX; n++)
+ if (card_in(dev, n).tv)
+ break;
+ if (NULL != card_in(dev, n).name) {
+ strcpy(t->name, "Television");
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM |
+ V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 |
+ V4L2_TUNER_CAP_LANG2;
+ t->rangehigh = 0xffffffffUL;
+ t->rxsubchans = saa7134_tvaudio_getstereo(dev);
+ t->audmode = saa7134_tvaudio_rx2mode(t->rxsubchans);
+ }
+ if (0 != (saa_readb(SAA7134_STATUS_VIDEO1) & 0x03))
+ t->signal = 0xffff;
+ return 0;
+}
- /* --- control ioctls ---------------------------------------- */
- case VIDIOC_ENUMINPUT:
- case VIDIOC_G_INPUT:
- case VIDIOC_S_INPUT:
- case VIDIOC_QUERYCTRL:
- case VIDIOC_G_CTRL:
- case VIDIOC_S_CTRL:
- return saa7134_common_ioctl(dev, cmd, arg);
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int rx, mode, err;
- case VIDIOC_G_AUDIO:
- {
- struct v4l2_audio *a = arg;
+ err = v4l2_prio_check(&dev->prio, &fh->prio);
+ if (0 != err)
+ return err;
- memset(a,0,sizeof(*a));
- strcpy(a->name,"audio");
- return 0;
- }
- case VIDIOC_S_AUDIO:
- return 0;
- case VIDIOC_G_PARM:
- {
- struct v4l2_captureparm *parm = arg;
- memset(parm,0,sizeof(*parm));
- return 0;
+ mode = dev->thread.mode;
+ if (UNSET == mode) {
+ rx = saa7134_tvaudio_getstereo(dev);
+ mode = saa7134_tvaudio_rx2mode(t->rxsubchans);
}
+ if (mode != t->audmode)
+ dev->thread.mode = t->audmode;
- case VIDIOC_G_PRIORITY:
- {
- enum v4l2_priority *p = arg;
+ return 0;
+}
- *p = v4l2_prio_max(&dev->prio);
- return 0;
- }
- case VIDIOC_S_PRIORITY:
- {
- enum v4l2_priority *prio = arg;
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
- return v4l2_prio_change(&dev->prio, &fh->prio, *prio);
- }
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = dev->ctl_freq;
- /* --- preview ioctls ---------------------------------------- */
- case VIDIOC_ENUM_FMT:
- {
- struct v4l2_fmtdesc *f = arg;
- enum v4l2_buf_type type;
- unsigned int index;
-
- index = f->index;
- type = f->type;
- switch (type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (saa7134_no_overlay > 0) {
- printk ("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
- return -EINVAL;
- }
- if (index >= FORMATS)
- return -EINVAL;
- if (f->type == V4L2_BUF_TYPE_VIDEO_OVERLAY &&
- formats[index].planar)
- return -EINVAL;
- memset(f,0,sizeof(*f));
- f->index = index;
- f->type = type;
- strlcpy(f->description,formats[index].name,sizeof(f->description));
- f->pixelformat = formats[index].fourcc;
- break;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- if (0 != index)
- return -EINVAL;
- memset(f,0,sizeof(*f));
- f->index = index;
- f->type = type;
- f->pixelformat = V4L2_PIX_FMT_GREY;
- strcpy(f->description,"vbi data");
- break;
- default:
- return -EINVAL;
- }
- return 0;
- }
- case VIDIOC_G_FBUF:
- {
- struct v4l2_framebuffer *fb = arg;
+ return 0;
+}
- *fb = dev->ovbuf;
- fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
- return 0;
- }
- case VIDIOC_S_FBUF:
- {
- struct v4l2_framebuffer *fb = arg;
- struct saa7134_format *fmt;
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int err;
- if(!capable(CAP_SYS_ADMIN) &&
- !capable(CAP_SYS_RAWIO))
- return -EPERM;
+ err = v4l2_prio_check(&dev->prio, &fh->prio);
+ if (0 != err)
+ return err;
- /* check args */
- fmt = format_by_fourcc(fb->fmt.pixelformat);
- if (NULL == fmt)
- return -EINVAL;
+ if (0 != f->tuner)
+ return -EINVAL;
+ if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type)
+ return -EINVAL;
+ if (1 == fh->radio && V4L2_TUNER_RADIO != f->type)
+ return -EINVAL;
+ mutex_lock(&dev->lock);
+ dev->ctl_freq = f->frequency;
- /* ok, accept it */
- dev->ovbuf = *fb;
- dev->ovfmt = fmt;
- if (0 == dev->ovbuf.fmt.bytesperline)
- dev->ovbuf.fmt.bytesperline =
- dev->ovbuf.fmt.width*fmt->depth/8;
- return 0;
+ saa7134_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f);
+
+ saa7134_tvaudio_do_scan(dev);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ strcpy(a->name, "audio");
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ return 0;
+}
+
+static int vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+
+ *p = v4l2_prio_max(&dev->prio);
+ return 0;
+}
+
+static int vidioc_s_priority(struct file *file, void *f,
+ enum v4l2_priority prio)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+
+ return v4l2_prio_change(&dev->prio, &fh->prio, prio);
+}
+
+static int vidioc_enum_fmt_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index >= FORMATS)
+ return -EINVAL;
+
+ strlcpy(f->description, formats[f->index].name,
+ sizeof(f->description));
+
+ f->pixelformat = formats[f->index].fourcc;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (saa7134_no_overlay > 0) {
+ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
}
- case VIDIOC_OVERLAY:
- {
- int *on = arg;
- if (*on) {
- if (saa7134_no_overlay > 0) {
- printk ("no_overlay\n");
- return -EINVAL;
- }
+ if ((f->index >= FORMATS) || formats[f->index].planar)
+ return -EINVAL;
- if (!res_get(dev,fh,RESOURCE_OVERLAY))
- return -EBUSY;
- spin_lock_irqsave(&dev->slock,flags);
- start_preview(dev,fh);
- spin_unlock_irqrestore(&dev->slock,flags);
- }
- if (!*on) {
- if (!res_check(fh, RESOURCE_OVERLAY))
- return -EINVAL;
- spin_lock_irqsave(&dev->slock,flags);
- stop_preview(dev,fh);
- spin_unlock_irqrestore(&dev->slock,flags);
- res_free(dev,fh,RESOURCE_OVERLAY);
+ strlcpy(f->description, formats[f->index].name,
+ sizeof(f->description));
+
+ f->pixelformat = formats[f->index].fourcc;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vbi(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (0 != f->index)
+ return -EINVAL;
+
+ f->pixelformat = V4L2_PIX_FMT_GREY;
+ strcpy(f->description, "vbi data");
+
+ return 0;
+}
+
+static int vidioc_g_fbuf(struct file *file, void *f,
+ struct v4l2_framebuffer *fb)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+
+ *fb = dev->ovbuf;
+ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+
+ return 0;
+}
+
+static int vidioc_s_fbuf(struct file *file, void *f,
+ struct v4l2_framebuffer *fb)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa7134_format *fmt;
+
+ if (!capable(CAP_SYS_ADMIN) &&
+ !capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ /* check args */
+ fmt = format_by_fourcc(fb->fmt.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ /* ok, accept it */
+ dev->ovbuf = *fb;
+ dev->ovfmt = fmt;
+ if (0 == dev->ovbuf.fmt.bytesperline)
+ dev->ovbuf.fmt.bytesperline =
+ dev->ovbuf.fmt.width*fmt->depth/8;
+ return 0;
+}
+
+static int vidioc_overlay(struct file *file, void *f, unsigned int on)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+ unsigned long flags;
+
+ if (on) {
+ if (saa7134_no_overlay > 0) {
+ dprintk("no_overlay\n");
+ return -EINVAL;
}
- return 0;
- }
- /* --- capture ioctls ---------------------------------------- */
- case VIDIOC_G_FMT:
- {
- struct v4l2_format *f = arg;
- return saa7134_g_fmt(dev,fh,f);
- }
- case VIDIOC_S_FMT:
- {
- struct v4l2_format *f = arg;
- return saa7134_s_fmt(dev,fh,f);
+ if (!res_get(dev, fh, RESOURCE_OVERLAY))
+ return -EBUSY;
+ spin_lock_irqsave(&dev->slock, flags);
+ start_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
}
- case VIDIOC_TRY_FMT:
- {
- struct v4l2_format *f = arg;
- return saa7134_try_fmt(dev,fh,f);
+ if (!on) {
+ if (!res_check(fh, RESOURCE_OVERLAY))
+ return -EINVAL;
+ spin_lock_irqsave(&dev->slock, flags);
+ stop_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
+ res_free(dev, fh, RESOURCE_OVERLAY);
}
+ return 0;
+}
+
#ifdef CONFIG_VIDEO_V4L1_COMPAT
- case VIDIOCGMBUF:
- return videobuf_cgmbuf(saa7134_queue(fh), arg, gbuffers);
+static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
+{
+ struct saa7134_fh *fh = file->private_data;
+ return videobuf_cgmbuf(saa7134_queue(fh), mbuf, 8);
+}
#endif
- case VIDIOC_REQBUFS:
- return videobuf_reqbufs(saa7134_queue(fh),arg);
- case VIDIOC_QUERYBUF:
- return videobuf_querybuf(saa7134_queue(fh),arg);
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct saa7134_fh *fh = priv;
+ return videobuf_reqbufs(saa7134_queue(fh), p);
+}
- case VIDIOC_QBUF:
- return videobuf_qbuf(saa7134_queue(fh),arg);
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct saa7134_fh *fh = priv;
+ return videobuf_querybuf(saa7134_queue(fh), b);
+}
- case VIDIOC_DQBUF:
- return videobuf_dqbuf(saa7134_queue(fh),arg,
- file->f_flags & O_NONBLOCK);
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct saa7134_fh *fh = priv;
+ return videobuf_qbuf(saa7134_queue(fh), b);
+}
- case VIDIOC_STREAMON:
- {
- int res = saa7134_resource(fh);
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct saa7134_fh *fh = priv;
+ return videobuf_dqbuf(saa7134_queue(fh), b,
+ file->f_flags & O_NONBLOCK);
+}
- if (!res_get(dev,fh,res))
- return -EBUSY;
- return videobuf_streamon(saa7134_queue(fh));
- }
- case VIDIOC_STREAMOFF:
- {
- int res = saa7134_resource(fh);
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int res = saa7134_resource(fh);
- err = videobuf_streamoff(saa7134_queue(fh));
- if (err < 0)
- return err;
- res_free(dev,fh,res);
- return 0;
- }
+ if (!res_get(dev, fh, res))
+ return -EBUSY;
- default:
- return v4l_compat_translate_ioctl(inode,file,cmd,arg,
- video_do_ioctl);
- }
+ return videobuf_streamon(saa7134_queue(fh));
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ int err;
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int res = saa7134_resource(fh);
+
+ err = videobuf_streamoff(saa7134_queue(fh));
+ if (err < 0)
+ return err;
+ res_free(dev, fh, res);
return 0;
}
-static int video_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int vidioc_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *parm)
{
- return video_usercopy(inode, file, cmd, arg, video_do_ioctl);
+ return 0;
}
-static int radio_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int radio_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
{
struct saa7134_fh *fh = file->private_data;
struct saa7134_dev *dev = fh->dev;
- if (video_debug > 1)
- v4l_print_ioctl(dev->name,cmd);
- switch (cmd) {
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *cap = arg;
-
- memset(cap,0,sizeof(*cap));
- strcpy(cap->driver, "saa7134");
- strlcpy(cap->card, saa7134_boards[dev->board].name,
- sizeof(cap->card));
- sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
- cap->version = SAA7134_VERSION_CODE;
- cap->capabilities = V4L2_CAP_TUNER;
- return 0;
- }
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *t = arg;
+ strcpy(cap->driver, "saa7134");
+ strlcpy(cap->card, saa7134_boards[dev->board].name, sizeof(cap->card));
+ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+ cap->version = SAA7134_VERSION_CODE;
+ cap->capabilities = V4L2_CAP_TUNER;
+ return 0;
+}
- if (0 != t->index)
- return -EINVAL;
+static int radio_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct saa7134_dev *dev = fh->dev;
- memset(t,0,sizeof(*t));
- strcpy(t->name, "Radio");
- t->type = V4L2_TUNER_RADIO;
+ if (0 != t->index)
+ return -EINVAL;
- saa7134_i2c_call_clients(dev, VIDIOC_G_TUNER, t);
- if (dev->input->amux == TV) {
- t->signal = 0xf800 - ((saa_readb(0x581) & 0x1f) << 11);
- t->rxsubchans = (saa_readb(0x529) & 0x08) ?
- V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
- }
- return 0;
+ memset(t, 0, sizeof(*t));
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
+
+ saa7134_i2c_call_clients(dev, VIDIOC_G_TUNER, t);
+ if (dev->input->amux == TV) {
+ t->signal = 0xf800 - ((saa_readb(0x581) & 0x1f) << 11);
+ t->rxsubchans = (saa_readb(0x529) & 0x08) ?
+ V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
}
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *t = arg;
+ return 0;
+}
+static int radio_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct saa7134_dev *dev = fh->dev;
- if (0 != t->index)
- return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
- saa7134_i2c_call_clients(dev,VIDIOC_S_TUNER,t);
+ saa7134_i2c_call_clients(dev, VIDIOC_S_TUNER, t);
+ return 0;
+}
- return 0;
- }
- case VIDIOC_ENUMINPUT:
- {
- struct v4l2_input *i = arg;
+static int radio_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
- if (i->index != 0)
- return -EINVAL;
- strcpy(i->name,"Radio");
- i->type = V4L2_INPUT_TYPE_TUNER;
- return 0;
- }
- case VIDIOC_G_INPUT:
- {
- int *i = arg;
- *i = 0;
- return 0;
- }
- case VIDIOC_G_AUDIO:
- {
- struct v4l2_audio *a = arg;
+ strcpy(i->name, "Radio");
+ i->type = V4L2_INPUT_TYPE_TUNER;
- memset(a,0,sizeof(*a));
- strcpy(a->name,"Radio");
- return 0;
- }
- case VIDIOC_G_STD:
- {
- v4l2_std_id *id = arg;
- *id = 0;
- return 0;
- }
- case VIDIOC_S_AUDIO:
- case VIDIOC_S_INPUT:
- case VIDIOC_S_STD:
- return 0;
+ return 0;
+}
- case VIDIOC_QUERYCTRL:
- {
- const struct v4l2_queryctrl *ctrl;
- struct v4l2_queryctrl *c = arg;
+static int radio_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
- if (c->id < V4L2_CID_BASE ||
- c->id >= V4L2_CID_LASTP1)
- return -EINVAL;
- if (c->id == V4L2_CID_AUDIO_MUTE) {
- ctrl = ctrl_by_id(c->id);
- *c = *ctrl;
- } else
- *c = no_ctrl;
- return 0;
- }
+static int radio_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ memset(a, 0, sizeof(*a));
+ strcpy(a->name, "Radio");
+ return 0;
+}
+
+static int radio_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ return 0;
+}
- case VIDIOC_G_CTRL:
- case VIDIOC_S_CTRL:
- case VIDIOC_G_FREQUENCY:
- case VIDIOC_S_FREQUENCY:
- return video_do_ioctl(inode,file,cmd,arg);
+static int radio_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ return 0;
+}
- default:
- return v4l_compat_translate_ioctl(inode,file,cmd,arg,
- radio_do_ioctl);
- }
+static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
return 0;
}
-static int radio_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int radio_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
{
- return video_usercopy(inode, file, cmd, arg, radio_do_ioctl);
+ const struct v4l2_queryctrl *ctrl;
+
+ if (c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ if (c->id == V4L2_CID_AUDIO_MUTE) {
+ ctrl = ctrl_by_id(c->id);
+ *c = *ctrl;
+ } else
+ *c = no_ctrl;
+ return 0;
}
static const struct file_operations video_fops =
@@ -2348,7 +2340,7 @@ static const struct file_operations video_fops =
.read = video_read,
.poll = video_poll,
.mmap = video_mmap,
- .ioctl = video_ioctl,
+ .ioctl = video_ioctl2,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
.compat_ioctl = v4l_compat_ioctl32,
#endif
@@ -2360,7 +2352,7 @@ static const struct file_operations radio_fops =
.owner = THIS_MODULE,
.open = video_open,
.release = video_release,
- .ioctl = radio_ioctl,
+ .ioctl = video_ioctl2,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
.compat_ioctl = v4l_compat_ioctl32,
#endif
@@ -2372,11 +2364,57 @@ static const struct file_operations radio_fops =
struct video_device saa7134_video_template =
{
- .name = "saa7134-video",
- .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|
- VID_TYPE_CLIPPING|VID_TYPE_SCALES,
- .fops = &video_fops,
- .minor = -1,
+ .name = "saa7134-video",
+ .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER |
+ VID_TYPE_CLIPPING|VID_TYPE_SCALES,
+ .fops = &video_fops,
+ .minor = -1,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap,
+ .vidioc_g_fmt_cap = vidioc_g_fmt_cap,
+ .vidioc_try_fmt_cap = vidioc_try_fmt_cap,
+ .vidioc_s_fmt_cap = vidioc_s_fmt_cap,
+ .vidioc_enum_fmt_overlay = vidioc_enum_fmt_overlay,
+ .vidioc_g_fmt_overlay = vidioc_g_fmt_overlay,
+ .vidioc_try_fmt_overlay = vidioc_try_fmt_overlay,
+ .vidioc_s_fmt_overlay = vidioc_s_fmt_overlay,
+ .vidioc_enum_fmt_vbi = vidioc_enum_fmt_vbi,
+ .vidioc_g_fmt_vbi = vidioc_try_get_set_fmt_vbi,
+ .vidioc_try_fmt_vbi = vidioc_try_get_set_fmt_vbi,
+ .vidioc_s_fmt_vbi = vidioc_try_get_set_fmt_vbi,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_cropcap = vidioc_cropcap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+ .vidiocgmbuf = vidiocgmbuf,
+#endif
+ .vidioc_g_crop = vidioc_g_crop,
+ .vidioc_s_crop = vidioc_s_crop,
+ .vidioc_g_fbuf = vidioc_g_fbuf,
+ .vidioc_s_fbuf = vidioc_s_fbuf,
+ .vidioc_overlay = vidioc_overlay,
+ .vidioc_g_priority = vidioc_g_priority,
+ .vidioc_s_priority = vidioc_s_priority,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .tvnorms = SAA7134_NORMS,
+ .current_norm = V4L2_STD_PAL,
};
struct video_device saa7134_vbi_template =
@@ -2393,6 +2431,20 @@ struct video_device saa7134_radio_template =
.type = VID_TYPE_TUNER,
.fops = &radio_fops,
.minor = -1,
+ .vidioc_querycap = radio_querycap,
+ .vidioc_g_tuner = radio_g_tuner,
+ .vidioc_enum_input = radio_enum_input,
+ .vidioc_g_audio = radio_g_audio,
+ .vidioc_s_tuner = radio_s_tuner,
+ .vidioc_s_audio = radio_s_audio,
+ .vidioc_s_input = radio_s_input,
+ .vidioc_s_std = radio_s_std,
+ .vidioc_queryctrl = radio_queryctrl,
+ .vidioc_g_input = radio_g_input,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
};
int saa7134_video_init1(struct saa7134_dev *dev)
diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h
index 0df575366..336fe002e 100644
--- a/linux/drivers/media/video/saa7134/saa7134.h
+++ b/linux/drivers/media/video/saa7134/saa7134.h
@@ -646,6 +646,7 @@ void saa7134_i2c_call_clients(struct saa7134_dev *dev,
/* ----------------------------------------------------------- */
/* saa7134-video.c */
+extern unsigned int video_debug;
extern struct video_device saa7134_video_template;
extern struct video_device saa7134_radio_template;