diff options
author | Andy Walls <awalls@radix.net> | 2008-12-12 13:50:27 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2008-12-30 06:38:32 -0500 |
commit | abb096de82f6f920a06ca935f76925261e66b556 (patch) | |
tree | 51f71ecb1d420083e54162b32733cf237b5b2393 | |
parent | 765f6f612ef69ada79f7ec2627dcbc49276bf7b5 (diff) |
V4L/DVB (9804): cx18: Avoid making firmware API calls with the queue lock held
cx18: Avoid making firmware API calls with the queue lock held. The source
of MPEG strem corruption when not holding the queue lock was found to be that
the MPEG buffer could be retrieved by the user app before it was sync'ed for
the host cpu. Incoming buffers are now sync'ed before being put on q_full and
releasing the queue lock. We can thus avoid the sometimes lengthy call to
the firmware for CPU_DE_SET_MDL while holding the queue lock, so we can get
better performance.
Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/video/cx18/cx18-mailbox.c | 15 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-queue.c | 13 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-streams.c | 37 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-streams.h | 2 |
4 files changed, 30 insertions, 37 deletions
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index 9d8220539be8..ca8d5f4b731a 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c | |||
@@ -163,7 +163,7 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order) | |||
163 | * it's filled in). | 163 | * it's filled in). |
164 | * | 164 | * |
165 | * cx18_queue_get buf() will detect the lost buffers | 165 | * cx18_queue_get buf() will detect the lost buffers |
166 | * and put them back in rotation eventually. | 166 | * and send them back to q_free for fw rotation eventually. |
167 | */ | 167 | */ |
168 | if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) && | 168 | if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) && |
169 | !(id >= s->mdl_offset && | 169 | !(id >= s->mdl_offset && |
@@ -174,24 +174,27 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order) | |||
174 | break; | 174 | break; |
175 | } | 175 | } |
176 | buf = cx18_queue_get_buf(s, id, mdl_ack->data_used); | 176 | buf = cx18_queue_get_buf(s, id, mdl_ack->data_used); |
177 | |||
177 | CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id); | 178 | CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id); |
178 | if (buf == NULL) { | 179 | if (buf == NULL) { |
179 | CX18_WARN("Could not find buf %d for stream %s\n", | 180 | CX18_WARN("Could not find buf %d for stream %s\n", |
180 | id, s->name); | 181 | id, s->name); |
182 | /* Put as many buffers as possible back into fw use */ | ||
183 | cx18_stream_load_fw_queue(s); | ||
181 | continue; | 184 | continue; |
182 | } | 185 | } |
183 | 186 | ||
184 | cx18_buf_sync_for_cpu(s, buf); | ||
185 | if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) { | 187 | if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) { |
186 | CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n", | 188 | CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n", |
187 | buf->bytesused); | 189 | buf->bytesused); |
188 | |||
189 | dvb_dmx_swfilter(&s->dvb.demux, buf->buf, | 190 | dvb_dmx_swfilter(&s->dvb.demux, buf->buf, |
190 | buf->bytesused); | 191 | buf->bytesused); |
191 | 192 | } | |
193 | /* Put as many buffers as possible back into fw use */ | ||
194 | cx18_stream_load_fw_queue(s); | ||
195 | /* Put back TS buffer, since it was removed from all queues */ | ||
196 | if (s->type == CX18_ENC_STREAM_TYPE_TS) | ||
192 | cx18_stream_put_buf_fw(s, buf); | 197 | cx18_stream_put_buf_fw(s, buf); |
193 | } else | ||
194 | set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags); | ||
195 | } | 198 | } |
196 | wake_up(&cx->dma_waitq); | 199 | wake_up(&cx->dma_waitq); |
197 | if (s->id != -1) | 200 | if (s->id != -1) |
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c index 40379d807cef..a6b0666f0ba3 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/drivers/media/video/cx18/cx18-queue.c | |||
@@ -117,16 +117,18 @@ struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, | |||
117 | } | 117 | } |
118 | 118 | ||
119 | buf->bytesused = bytesused; | 119 | buf->bytesused = bytesused; |
120 | /* Sync the buffer before we release the qlock */ | ||
121 | cx18_buf_sync_for_cpu(s, buf); | ||
120 | if (s->type == CX18_ENC_STREAM_TYPE_TS) { | 122 | if (s->type == CX18_ENC_STREAM_TYPE_TS) { |
121 | /* | 123 | /* |
122 | * TS doesn't use q_full, but for sweeping up lost | 124 | * TS doesn't use q_full. As we pull the buffer off of |
123 | * buffers, we want the TS to requeue the buffer just | 125 | * the queue here, the caller will have to put it back. |
124 | * before sending the MDL back to the firmware, so we | ||
125 | * pull it off the list here. | ||
126 | */ | 126 | */ |
127 | list_del_init(&buf->list); | 127 | list_del_init(&buf->list); |
128 | } else { | 128 | } else { |
129 | /* Move buffer from q_busy to q_full */ | ||
129 | list_move_tail(&buf->list, &s->q_full.list); | 130 | list_move_tail(&buf->list, &s->q_full.list); |
131 | set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags); | ||
130 | s->q_full.bytesused += buf->bytesused; | 132 | s->q_full.bytesused += buf->bytesused; |
131 | atomic_inc(&s->q_full.buffers); | 133 | atomic_inc(&s->q_full.buffers); |
132 | } | 134 | } |
@@ -135,9 +137,6 @@ struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, | |||
135 | ret = buf; | 137 | ret = buf; |
136 | break; | 138 | break; |
137 | } | 139 | } |
138 | |||
139 | /* Put more buffers into the transfer rotation from q_free, if we can */ | ||
140 | cx18_stream_load_fw_queue_nolock(s); | ||
141 | mutex_unlock(&s->qlock); | 140 | mutex_unlock(&s->qlock); |
142 | return ret; | 141 | return ret; |
143 | } | 142 | } |
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index d2690ccdf327..9ead4591b1d2 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c | |||
@@ -419,31 +419,22 @@ struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, | |||
419 | return q; | 419 | return q; |
420 | } | 420 | } |
421 | 421 | ||
422 | /* Must hold s->qlock when calling */ | 422 | void cx18_stream_load_fw_queue(struct cx18_stream *s) |
423 | void cx18_stream_load_fw_queue_nolock(struct cx18_stream *s) | ||
424 | { | 423 | { |
424 | struct cx18_queue *q; | ||
425 | struct cx18_buffer *buf; | 425 | struct cx18_buffer *buf; |
426 | struct cx18 *cx = s->cx; | ||
427 | 426 | ||
428 | /* Move from q_free to q_busy notifying the firmware: 63 buf limit */ | 427 | if (atomic_read(&s->q_free.buffers) == 0 || |
429 | while (s->handle != CX18_INVALID_TASK_HANDLE && | 428 | atomic_read(&s->q_busy.buffers) >= 63) |
430 | test_bit(CX18_F_S_STREAMING, &s->s_flags) && | 429 | return; |
431 | atomic_read(&s->q_busy.buffers) < 63 && | 430 | |
432 | !list_empty(&s->q_free.list)) { | 431 | /* Move from q_free to q_busy notifying the firmware, until the limit */ |
433 | 432 | do { | |
434 | /* Move from q_free to q_busy */ | 433 | buf = cx18_dequeue(s, &s->q_free); |
435 | buf = list_entry(s->q_free.list.next, struct cx18_buffer, list); | 434 | if (buf == NULL) |
436 | list_move_tail(&buf->list, &s->q_busy.list); | 435 | break; |
437 | buf->bytesused = buf->readpos = buf->b_flags = buf->skipped = 0; | 436 | q = cx18_stream_put_buf_fw(s, buf); |
438 | atomic_dec(&s->q_free.buffers); | 437 | } while (atomic_read(&s->q_busy.buffers) < 63 && q == &s->q_busy); |
439 | atomic_inc(&s->q_busy.buffers); | ||
440 | |||
441 | /* Notify firmware */ | ||
442 | cx18_buf_sync_for_device(s, buf); | ||
443 | cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, | ||
444 | (void __iomem *) &cx->scb->cpu_mdl[buf->id] - cx->enc_mem, | ||
445 | 1, buf->id, s->buf_size); | ||
446 | } | ||
447 | } | 438 | } |
448 | 439 | ||
449 | int cx18_start_v4l2_encode_stream(struct cx18_stream *s) | 440 | int cx18_start_v4l2_encode_stream(struct cx18_stream *s) |
@@ -543,8 +534,8 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) | |||
543 | &cx->scb->cpu_mdl[buf->id].paddr); | 534 | &cx->scb->cpu_mdl[buf->id].paddr); |
544 | cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length); | 535 | cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length); |
545 | } | 536 | } |
546 | cx18_stream_load_fw_queue_nolock(s); | ||
547 | mutex_unlock(&s->qlock); | 537 | mutex_unlock(&s->qlock); |
538 | cx18_stream_load_fw_queue(s); | ||
548 | 539 | ||
549 | /* begin_capture */ | 540 | /* begin_capture */ |
550 | if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) { | 541 | if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) { |
diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h index 635d34b75ab1..420e0a172945 100644 --- a/drivers/media/video/cx18/cx18-streams.h +++ b/drivers/media/video/cx18/cx18-streams.h | |||
@@ -29,7 +29,7 @@ int cx18_streams_register(struct cx18 *cx); | |||
29 | void cx18_streams_cleanup(struct cx18 *cx, int unregister); | 29 | void cx18_streams_cleanup(struct cx18 *cx, int unregister); |
30 | 30 | ||
31 | /* Capture related */ | 31 | /* Capture related */ |
32 | void cx18_stream_load_fw_queue_nolock(struct cx18_stream *s); | 32 | void cx18_stream_load_fw_queue(struct cx18_stream *s); |
33 | struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, | 33 | struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, |
34 | struct cx18_buffer *buf); | 34 | struct cx18_buffer *buf); |
35 | int cx18_start_v4l2_encode_stream(struct cx18_stream *s); | 35 | int cx18_start_v4l2_encode_stream(struct cx18_stream *s); |