diff options
author | Andy Walls <awalls@radix.net> | 2008-11-19 01:24:33 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2008-12-30 09:38:10 -0200 |
commit | bca11a5721917d6d5874571813673a2669ffec4b (patch) | |
tree | a2202bdf236804a123175b12585f4aae4fdc58d7 /drivers/media/video/cx18/cx18-mailbox.c | |
parent | d6c7e5f8faad080e75bace5c4f2265e3513e3510 (diff) |
V4L/DVB (9726): cx18: Restore buffers that have fallen out of the transfer rotation
Restore buffers that have fallen out of the transfer rotation, and check
for coherent mailbox data when processing a stale mailbox.
Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/cx18/cx18-mailbox.c')
-rw-r--r-- | drivers/media/video/cx18/cx18-mailbox.c | 59 |
1 files changed, 46 insertions, 13 deletions
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index e5d4f311229..abd39aaa345 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -120,7 +120,7 @@ static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name) static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order) { - u32 handle, mdl_ack_count; + u32 handle, mdl_ack_count, id; struct cx18_mailbox *mb; struct cx18_mdl_ack *mdl_ack; struct cx18_stream *s; @@ -133,19 +133,50 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order) if (s == NULL) { CX18_WARN("Got DMA done notification for unknown/inactive" - " handle %d\n", handle); + " handle %d, %s mailbox seq no %d\n", handle, + (order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) ? + "stale" : "good", mb->request); return; } mdl_ack_count = mb->args[2]; mdl_ack = order->mdl_ack; for (i = 0; i < mdl_ack_count; i++, mdl_ack++) { - buf = cx18_queue_get_buf(s, mdl_ack->id, mdl_ack->data_used); - CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, - mdl_ack->id); + id = mdl_ack->id; + /* + * Simple integrity check for processing a stale (and possibly + * inconsistent mailbox): make sure the buffer id is in the + * valid range for the stream. + * + * We go through the trouble of dealing with stale mailboxes + * because most of the time, the mailbox data is still valid and + * unchanged (and in practice the firmware ping-pongs the + * two mdl_ack buffers so mdl_acks are not stale). + * + * There are occasions when we get a half changed mailbox, + * which this check catches for a handle & id mismatch. If the + * handle and id do correspond, the worst case is that we + * completely lost the old buffer, but pick up the new buffer + * early (but the new mdl_ack is guaranteed to be good in this + * case as the firmware wouldn't point us to a new mdl_ack until + * it's filled in). + * + * cx18_queue_get buf() will detect the lost buffers + * and put them back in rotation eventually. + */ + if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) && + !(id >= s->mdl_offset && + id < (s->mdl_offset + s->buffers))) { + CX18_WARN("Fell behind! Ignoring stale mailbox with " + " inconsistent data. Lost buffer for mailbox " + "seq no %d\n", mb->request); + break; + } + buf = cx18_queue_get_buf(s, id, mdl_ack->data_used); + CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id); if (buf == NULL) { CX18_WARN("Could not find buf %d for stream %s\n", - mdl_ack->id, s->name); + id, s->name); continue; } @@ -158,6 +189,7 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order) buf->bytesused); cx18_buf_sync_for_device(s, buf); + cx18_enqueue(s, buf, &s->q_free); if (s->handle != CX18_INVALID_TASK_HANDLE && test_bit(CX18_F_S_STREAMING, &s->s_flags)) @@ -257,10 +289,10 @@ static void mb_ack_irq(struct cx18 *cx, struct cx18_epu_work_order *order) /* Don't ack if the RPU has gotten impatient and timed us out */ if (req != cx18_readl(cx, &ack_mb->request) || req == cx18_readl(cx, &ack_mb->ack)) { - CX18_WARN("Possibly falling behind: %s self-ack'ed our incoming" - " %s to EPU mailbox (sequence no. %u) while " - "processing\n", - rpu_str[order->rpu], rpu_str[order->rpu], req); + CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our " + "incoming %s to EPU mailbox (sequence no. %u) " + "while processing\n", + rpu_str[order->rpu], rpu_str[order->rpu], req); order->flags |= CX18_F_EWO_MB_STALE_WHILE_PROC; return; } @@ -407,9 +439,10 @@ void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) 2 * sizeof(u32)); if (order_mb->request == order_mb->ack) { - CX18_WARN("Possibly falling behind: %s self-ack'ed our incoming" - " %s to EPU mailbox (sequence no. %u)\n", - rpu_str[rpu], rpu_str[rpu], order_mb->request); + CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our " + "incoming %s to EPU mailbox (sequence no. %u)" + "\n", + rpu_str[rpu], rpu_str[rpu], order_mb->request); dump_mb(cx, order_mb, "incoming"); order->flags = CX18_F_EWO_MB_STALE_UPON_RECEIPT; } |