Skip to content

Commit

Permalink
V4L/DVB (6717): ivtv: Initial merge of video48 yuv handling into the …
Browse files Browse the repository at this point in the history
…IVTV_IOC_DMA_FRAME framework

Previously, all yuv data written to /dev/video48 had only basic support with
no double buffering to avoid display tearing.

With this patch, yuv frames written to video48 are now handled by the existing
IVTV_IOC_DMA_FRAME framework. As such, the frames are hardware buffered to
avoid tearing, and honour scaling mode & field order options. Unlike the
proprietary IVTV_IOC_DMA_FRAME ioctl, all parameters are controlled by the
V4L2 API.

Due to mpeg & yuv output restrictions being different, their V4L2 output
controls have been separated. To control the yuv output, the V4L2 calls must
be done via video48.

If the ivtvfb module is loaded, there will be one side effect to this merge.
The yuv output window will be constrained to the visible framebuffer area. In
the event that a virtual framebuffer size is being used, the limit to the
output size will be the virtual dimensions, but only the portion that falls
within the currently visible area of the framebuffer will be shown.

Like the IVTV_IOC_DMA_FRAME ioctl, the supplied frames must be padded to 720
pixels wide. However the height must only be padded up the nearest multiple
of 32. This would mean an image of 102 lines must be padded to 128. As long
as the true source image size is given, the padding will not be visible in
the final output.

Signed-off-by: Ian Armstrong <ian@iarmst.demon.co.uk>
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
  • Loading branch information
Ian Armstrong authored and Mauro Carvalho Chehab committed Jan 25, 2008
1 parent 3b5c1c8 commit 77aded6
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 70 deletions.
6 changes: 6 additions & 0 deletions drivers/media/video/ivtv/ivtv-driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -1117,6 +1117,12 @@ 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;
Expand Down
7 changes: 7 additions & 0 deletions drivers/media/video/ivtv/ivtv-driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,9 @@ struct yuv_playback_info
u32 osd_vis_w;
u32 osd_vis_h;

u32 osd_full_w;
u32 osd_full_h;

int decode_height;

int lace_mode;
Expand All @@ -491,6 +494,10 @@ struct yuv_playback_info

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
Expand Down
23 changes: 22 additions & 1 deletion drivers/media/video/ivtv/ivtv-fileops.c
Original file line number Diff line number Diff line change
Expand Up @@ -581,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)))
Expand Down Expand Up @@ -660,6 +678,9 @@ ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t c
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)) {
Expand Down Expand Up @@ -946,7 +967,7 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp)
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 =
itv->params.width * itv->params.height * 3 / 2;
1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31);
itv->yuv_info.stream_size = 0;
}
return 0;
Expand Down
120 changes: 78 additions & 42 deletions drivers/media/video/ivtv/ivtv-ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) ?
Expand All @@ -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 =
Expand Down Expand Up @@ -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;

Expand All @@ -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;
}
Expand Down Expand Up @@ -703,8 +716,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:
Expand Down Expand Up @@ -822,6 +839,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;
Expand All @@ -836,10 +858,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;
}
Expand All @@ -853,7 +880,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)
Expand All @@ -864,7 +894,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:2)", V4L2_PIX_FMT_HM12,
{ 0, 0, 0, 0 }
},
{ 1, 0, V4L2_FMT_FLAG_COMPRESSED,
Expand Down Expand Up @@ -1043,6 +1073,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;
}
Expand Down
26 changes: 23 additions & 3 deletions drivers/media/video/ivtv/ivtv-irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -302,15 +302,30 @@ 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;
unsigned long flags = 0;
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 &&
Expand Down Expand Up @@ -713,8 +728,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 {
Expand All @@ -728,6 +746,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);
Expand Down
17 changes: 1 addition & 16 deletions drivers/media/video/ivtv/ivtv-streams.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
Loading

0 comments on commit 77aded6

Please sign in to comment.