Commit c2dd3f19 authored by Mauro Carvalho Chehab's avatar Mauro Carvalho Chehab Committed by Mauro Carvalho Chehab

V4L/DVB (12800): tm6000: fix tm6000 analog video buffer handling

Previously, there were no sync between video buffer filling and received
URBs. So, the presented image were a mix of frame and frame-1. Now, a
filled buffer should contain only data for that frame.

allocates a maximum of 64 KB per each URB area. This fixed two bugs:

- Sometimes, there were no available continuos block of about 1 MB;
- Image weren't not properly updated, since parts of the URBs aren't
  updated.
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent b04d7191
...@@ -39,6 +39,13 @@ struct usb_isoc_ctl { ...@@ -39,6 +39,13 @@ struct usb_isoc_ctl {
/* Last field: ODD or EVEN? */ /* Last field: ODD or EVEN? */
int field; int field;
/* Stores incomplete commands */
u32 tmp_buf; u32 tmp_buf;
int tmp_buf_len; int tmp_buf_len;
/* Stores already requested buffers */
struct tm6000_buffer *buf;
/* Stores the number of received fields */
int nfields;
}; };
...@@ -147,12 +147,6 @@ static int inline get_next_buf (struct tm6000_dmaqueue *dma_q, ...@@ -147,12 +147,6 @@ static int inline get_next_buf (struct tm6000_dmaqueue *dma_q,
*buf = list_entry(dma_q->active.next, *buf = list_entry(dma_q->active.next,
struct tm6000_buffer, vb.queue); struct tm6000_buffer, vb.queue);
/* Nobody is waiting something to be done, just return */
if (!waitqueue_active(&(*buf)->vb.done)) {
mod_timer(&dma_q->timeout, jiffies+BUFFER_TIMEOUT);
return -1;
}
return 1; return 1;
} }
...@@ -160,8 +154,19 @@ static int inline get_next_buf (struct tm6000_dmaqueue *dma_q, ...@@ -160,8 +154,19 @@ static int inline get_next_buf (struct tm6000_dmaqueue *dma_q,
* Announces that a buffer were filled and request the next * Announces that a buffer were filled and request the next
*/ */
static void inline buffer_filled (struct tm6000_core *dev, static void inline buffer_filled (struct tm6000_core *dev,
struct tm6000_dmaqueue *dma_q,
struct tm6000_buffer *buf) struct tm6000_buffer *buf)
{ {
/* Nobody is waiting something to be done, just return */
if (!waitqueue_active(&buf->vb.done)) {
mod_timer(&dma_q->timeout, jiffies+BUFFER_TIMEOUT);
printk(KERN_ERR "tm6000: buffer underrun at %ld\n",
jiffies);
return;
}
/* Advice that buffer was filled */ /* Advice that buffer was filled */
dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n",buf,buf->vb.i); dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n",buf,buf->vb.i);
buf->vb.state = STATE_DONE; buf->vb.state = STATE_DONE;
...@@ -182,23 +187,36 @@ static void inline buffer_filled (struct tm6000_core *dev, ...@@ -182,23 +187,36 @@ static void inline buffer_filled (struct tm6000_core *dev,
tm6000_err("copy_to_user failed.\n"); \ tm6000_err("copy_to_user failed.\n"); \
} }
const char *tm6000_msg_type[]= {
"unknown(0)", //0
"video", //1
"audio", //2
"vbi", //3
"pts", //4
"err", //5
"unknown(6)", //6
"unknown(7)", //7
};
/* /*
* Identify the tm5600/6000 buffer header type and properly handles * Identify the tm5600/6000 buffer header type and properly handles
*/ */
static u8 *copy_packet (struct urb *urb, u32 header, u8 *data, u8 *endp, static int copy_packet (struct urb *urb, u32 header, u8 **ptr, u8 *endp,
u8 *out_p, struct tm6000_buffer **buf) u8 *out_p, struct tm6000_buffer **buf)
{ {
struct tm6000_dmaqueue *dma_q = urb->context; struct tm6000_dmaqueue *dma_q = urb->context;
struct tm6000_core *dev= container_of(dma_q,struct tm6000_core,vidq); struct tm6000_core *dev= container_of(dma_q,struct tm6000_core,vidq);
u8 *ptr=data;
u8 c; u8 c;
unsigned int cmd, cpysize, pktsize, size, field, block, line, pos=0; unsigned int cmd, cpysize, pktsize, size, field, block, line, pos=0;
int rc=0;
/* FIXME: move to tm6000-isoc */
static int last_line=-2, start_line=-2, last_field=-2;
/* FIXME: this is the hardcoded window size /* FIXME: this is the hardcoded window size
*/ */
unsigned int linesize=720*2; unsigned int linewidth=(*buf)->vb.width<<1;
//static int last_line=-2;
if (!dev->isoc_ctl.cmd) { if (!dev->isoc_ctl.cmd) {
c=(header>>24) & 0xff; c=(header>>24) & 0xff;
...@@ -213,29 +231,71 @@ static u8 *copy_packet (struct urb *urb, u32 header, u8 *data, u8 *endp, ...@@ -213,29 +231,71 @@ static u8 *copy_packet (struct urb *urb, u32 header, u8 *data, u8 *endp,
/* Validates header fields */ /* Validates header fields */
if(size>TM6000_URB_MSG_LEN) if(size>TM6000_URB_MSG_LEN)
size = TM6000_URB_MSG_LEN; size = TM6000_URB_MSG_LEN;
if(block>=8)
cmd = TM6000_URB_MSG_ERR;
/* FIXME: Mounts the image as field0+field1 if (cmd == TM6000_URB_MSG_VIDEO) {
* It should, instead, check if the user selected if ((block+1)*TM6000_URB_MSG_LEN>linewidth)
* entrelaced or non-entrelaced mode cmd = TM6000_URB_MSG_ERR;
*/
pos=((line<<1)+field)*linesize+block*TM6000_URB_MSG_LEN; /* FIXME: Mounts the image as field0+field1
* It should, instead, check if the user selected
* entrelaced or non-entrelaced mode
*/
pos= ((line<<1)+field)*linewidth +
block*TM6000_URB_MSG_LEN;
/* Don't allow to write out of the buffer */
if (pos+TM6000_URB_MSG_LEN > (*buf)->vb.size) {
dprintk(dev, V4L2_DEBUG_ISOC,
"ERR: size=%d, num=%d, line=%d, "
"field=%d\n",
size, block, line, field);
/* Don't allow to write out of the buffer */ cmd = TM6000_URB_MSG_ERR;
if (pos+TM6000_URB_MSG_LEN > (*buf)->vb.size) }
cmd = TM6000_URB_MSG_ERR; } else {
pos=0;
}
/* Prints debug info */ /* Prints debug info */
dprintk(dev, V4L2_DEBUG_ISOC, "size=%d, num=%d, " dprintk(dev, V4L2_DEBUG_ISOC, "size=%d, num=%d, "
" line=%d, field=%d\n", " line=%d, field=%d\n",
size, block, line, field); size, block, line, field);
if ((last_line!=line)&&(last_line+1!=line) &&
(cmd != TM6000_URB_MSG_ERR) ) {
if (cmd != TM6000_URB_MSG_VIDEO) {
dprintk(dev, V4L2_DEBUG_ISOC, "cmd=%d, "
"size=%d, num=%d, line=%d, field=%d\n",
cmd, size, block, line, field);
}
if (start_line<0)
start_line=last_line;
/* Prints debug info */
dprintk(dev, V4L2_DEBUG_ISOC, "lines= %d-%d, "
"field=%d\n",
start_line, last_line, field);
if ((start_line<6 && last_line>200) &&
(last_field != field) ) {
dev->isoc_ctl.nfields++;
if (dev->isoc_ctl.nfields>=2) {
dev->isoc_ctl.nfields=0;
/* Announces that a new buffer were filled */
buffer_filled (dev, dma_q, *buf);
dprintk(dev, V4L2_DEBUG_ISOC,
"new buffer filled\n");
rc=get_next_buf (dma_q, buf);
}
}
start_line=line;
last_field=field;
}
last_line=line;
pktsize = TM6000_URB_MSG_LEN; pktsize = TM6000_URB_MSG_LEN;
/////////////////////////////
/// nao seria size???
} else { } else {
/* Continue the last copy */ /* Continue the last copy */
cmd = dev->isoc_ctl.cmd; cmd = dev->isoc_ctl.cmd;
...@@ -244,16 +304,27 @@ static u8 *copy_packet (struct urb *urb, u32 header, u8 *data, u8 *endp, ...@@ -244,16 +304,27 @@ static u8 *copy_packet (struct urb *urb, u32 header, u8 *data, u8 *endp,
pktsize = dev->isoc_ctl.pktsize; pktsize = dev->isoc_ctl.pktsize;
} }
cpysize=(endp-ptr>size)?size:endp-ptr; cpysize=(endp-(*ptr)>size)?size:endp-(*ptr);
if (cpysize) { if (cpysize) {
/* handles each different URB message */ /* handles each different URB message */
switch(cmd) { switch(cmd) {
case TM6000_URB_MSG_VIDEO: case TM6000_URB_MSG_VIDEO:
/* Fills video buffer */ /* Fills video buffer */
if (__copy_to_user(&out_p[pos],ptr,cpysize)!=0) if (__copy_to_user(&out_p[pos],*ptr,cpysize)!=0)
tm6000_err("copy_to_user failed.\n"); tm6000_err("copy_to_user failed.\n");
break;
break;
case TM6000_URB_MSG_PTS:
break;
case TM6000_URB_MSG_AUDIO:
/* Need some code to process audio */
printk ("%ld: cmd=%s, size=%d\n", jiffies,
tm6000_msg_type[cmd],size);
break;
default:
dprintk (dev, V4L2_DEBUG_ISOC, "cmd=%s, size=%d\n",
tm6000_msg_type[cmd],size);
} }
} }
if (cpysize<size) { if (cpysize<size) {
...@@ -264,13 +335,13 @@ static u8 *copy_packet (struct urb *urb, u32 header, u8 *data, u8 *endp, ...@@ -264,13 +335,13 @@ static u8 *copy_packet (struct urb *urb, u32 header, u8 *data, u8 *endp,
dev->isoc_ctl.size= size-cpysize; dev->isoc_ctl.size= size-cpysize;
dev->isoc_ctl.cmd = cmd; dev->isoc_ctl.cmd = cmd;
dev->isoc_ctl.pktsize = pktsize-cpysize; dev->isoc_ctl.pktsize = pktsize-cpysize;
ptr+=cpysize; (*ptr)+=cpysize;
} else { } else {
dev->isoc_ctl.cmd = 0; dev->isoc_ctl.cmd = 0;
ptr+=pktsize; (*ptr)+=pktsize;
} }
return ptr; return rc;
} }
static int copy_streams(u8 *data, u8 *out_p, unsigned long len, static int copy_streams(u8 *data, u8 *out_p, unsigned long len,
...@@ -328,10 +399,16 @@ static int copy_streams(u8 *data, u8 *out_p, unsigned long len, ...@@ -328,10 +399,16 @@ static int copy_streams(u8 *data, u8 *out_p, unsigned long len,
} }
HEADER: HEADER:
/* Copy or continue last copy */ /* Copy or continue last copy */
ptr=copy_packet(urb,header,ptr,endp,out_p,buf); rc=copy_packet(urb,header,&ptr,endp,out_p,buf);
if (rc<0) {
buf=NULL;
printk(KERN_ERR "tm6000: buffer underrun at %ld\n",
jiffies);
return rc;
}
} }
return rc; return 0;
} }
/* /*
* Identify the tm5600/6000 buffer header type and properly handles * Identify the tm5600/6000 buffer header type and properly handles
...@@ -354,13 +431,11 @@ static int copy_multiplexed(u8 *ptr, u8 *out_p, unsigned long len, ...@@ -354,13 +431,11 @@ static int copy_multiplexed(u8 *ptr, u8 *out_p, unsigned long len,
if (pos >= (*buf)->vb.size) { if (pos >= (*buf)->vb.size) {
pos=0; pos=0;
/* Announces that a new buffer were filled */ /* Announces that a new buffer were filled */
buffer_filled (dev, *buf); buffer_filled (dev, dma_q, *buf);
dprintk(dev, V4L2_DEBUG_ISOC, "new buffer filled\n"); dprintk(dev, V4L2_DEBUG_ISOC, "new buffer filled\n");
rc=get_next_buf (dma_q, buf); rc=get_next_buf (dma_q, buf);
if (rc<=0) { if (rc<=0) {
*buf=NULL; *buf=NULL;
printk(KERN_ERR "tm6000: buffer underrun\n");
break; break;
} }
} }
...@@ -441,7 +516,7 @@ static inline int tm6000_isoc_copy(struct urb *urb, struct tm6000_buffer **buf) ...@@ -441,7 +516,7 @@ static inline int tm6000_isoc_copy(struct urb *urb, struct tm6000_buffer **buf)
len=urb->iso_frame_desc[i].actual_length; len=urb->iso_frame_desc[i].actual_length;
if (len>=TM6000_URB_MSG_LEN) { // if (len>=TM6000_URB_MSG_LEN) {
p=urb->transfer_buffer + urb->iso_frame_desc[i].offset; p=urb->transfer_buffer + urb->iso_frame_desc[i].offset;
if (!urb->iso_frame_desc[i].status) { if (!urb->iso_frame_desc[i].status) {
if (((*buf)->fmt->fourcc)==V4L2_PIX_FMT_TM6000) { if (((*buf)->fmt->fourcc)==V4L2_PIX_FMT_TM6000) {
...@@ -449,20 +524,14 @@ static inline int tm6000_isoc_copy(struct urb *urb, struct tm6000_buffer **buf) ...@@ -449,20 +524,14 @@ static inline int tm6000_isoc_copy(struct urb *urb, struct tm6000_buffer **buf)
if (rc<=0) if (rc<=0)
return rc; return rc;
} else { } else {
rc=copy_streams(p,outp,len,urb,buf); copy_streams(p,outp,len,urb,buf);
} }
} }
copied += len; copied += len;
if (copied>=size) if (copied>=size)
break; break;
} // }
}
if (((*buf)->fmt->fourcc)!=V4L2_PIX_FMT_TM6000) {
buffer_filled (dev, *buf);
dprintk(dev, V4L2_DEBUG_ISOC, "new buffer filled\n");
} }
return rc; return rc;
} }
...@@ -483,13 +552,18 @@ static void tm6000_irq_callback(struct urb *urb) ...@@ -483,13 +552,18 @@ static void tm6000_irq_callback(struct urb *urb)
spin_lock_irqsave(&dev->slock,flags); spin_lock_irqsave(&dev->slock,flags);
rc=get_next_buf (dma_q, &buf); buf=dev->isoc_ctl.buf;
if (rc<=0)
goto ret; if (!buf) {
rc=get_next_buf (dma_q, &buf);
if (rc<=0)
goto ret;
}
/* Copy data from URB */ /* Copy data from URB */
rc=tm6000_isoc_copy(urb, &buf); rc=tm6000_isoc_copy(urb, &buf);
dev->isoc_ctl.buf=buf;
ret: ret:
/* Reset urb buffers */ /* Reset urb buffers */
for (i = 0; i < urb->number_of_packets; i++) { for (i = 0; i < urb->number_of_packets; i++) {
...@@ -504,13 +578,9 @@ ret: ...@@ -504,13 +578,9 @@ ret:
} }
if (rc>=0) { if (rc>=0) {
if (!rc) {
dprintk(dev, V4L2_DEBUG_QUEUE, "No active queue to serve\n");
del_timer(&dma_q->timeout);
} else {
/* Data filled, reset watchdog */ /* Data filled, reset watchdog */
mod_timer(&dma_q->timeout, jiffies+BUFFER_TIMEOUT); mod_timer(&dma_q->timeout, jiffies+BUFFER_TIMEOUT);
} // }
} }
spin_unlock_irqrestore(&dev->slock,flags); spin_unlock_irqrestore(&dev->slock,flags);
} }
...@@ -523,6 +593,8 @@ static void tm6000_uninit_isoc(struct tm6000_core *dev) ...@@ -523,6 +593,8 @@ static void tm6000_uninit_isoc(struct tm6000_core *dev)
struct urb *urb; struct urb *urb;
int i; int i;
dev->isoc_ctl.nfields=-1;
dev->isoc_ctl.buf=NULL;
for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
urb=dev->isoc_ctl.urb[i]; urb=dev->isoc_ctl.urb[i];
if (urb) { if (urb) {
...@@ -610,8 +682,9 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev, ...@@ -610,8 +682,9 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev,
sb_size, GFP_KERNEL, &urb->transfer_dma); sb_size, GFP_KERNEL, &urb->transfer_dma);
if (!dev->isoc_ctl.transfer_buffer[i]) { if (!dev->isoc_ctl.transfer_buffer[i]) {
tm6000_err ("unable to allocate %i bytes for transfer" tm6000_err ("unable to allocate %i bytes for transfer"
" buffer %i, in int=%i\n", " buffer %i%s\n",
sb_size, i, in_interrupt()); sb_size, i,
in_interrupt()?" while in int":"");
tm6000_uninit_isoc(dev); tm6000_uninit_isoc(dev);
return -ENOMEM; return -ENOMEM;
} }
...@@ -795,7 +868,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, ...@@ -795,7 +868,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
struct tm6000_fh *fh = vq->priv_data; struct tm6000_fh *fh = vq->priv_data;
struct tm6000_buffer *buf = container_of(vb,struct tm6000_buffer,vb); struct tm6000_buffer *buf = container_of(vb,struct tm6000_buffer,vb);
struct tm6000_core *dev = fh->dev; struct tm6000_core *dev = fh->dev;
int rc=0, urbsize, urb_init=0; int rc=0, urbsize, urb_init=0, npackets=1;
BUG_ON(NULL == fh->fmt); BUG_ON(NULL == fh->fmt);
...@@ -833,6 +906,9 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, ...@@ -833,6 +906,9 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
Vres x Vres x 2 bytes/pixel by frame */ Vres x Vres x 2 bytes/pixel by frame */
urbsize=buf->vb.size; urbsize=buf->vb.size;
/* Need also one PTS */
urbsize+=180;
/* memory for audio /* memory for audio
Should be at least Should be at least
bitrate * 2 channels * 2 bytes / frame rate */ bitrate * 2 channels * 2 bytes / frame rate */
...@@ -852,11 +928,17 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, ...@@ -852,11 +928,17 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
/* Round to an enough number of URBs */ /* Round to an enough number of URBs */
urbsize=(urbsize+dev->max_isoc_in-1)/dev->max_isoc_in; urbsize=(urbsize+dev->max_isoc_in-1)/dev->max_isoc_in;
printk("Allocating %d packets to handle %lu size\n", urbsize,buf->vb.size); /* Avoids allocating big memory areas for URB */
while ((urbsize*dev->max_isoc_in)/npackets>65535) {
npackets++;
}
urbsize/=(urbsize+npackets-1)/npackets;
dprintk(dev, V4L2_DEBUG_QUEUE, "Allocating %dx%d packets"
" of %d bytes each to handle %lu size\n",
npackets,urbsize,dev->max_isoc_in,buf->vb.size);
dprintk(dev, V4L2_DEBUG_QUEUE, "Allocating %d packets to handle " rc = tm6000_prepare_isoc(dev, urbsize, npackets);
"%lu size\n", urbsize,buf->vb.size);
rc = tm6000_prepare_isoc(dev, urbsize, 2);
if (rc<0) if (rc<0)
goto fail; goto fail;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment