Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

V4L/DVB (9801): cx18: Allow more than 63 capture buffers in rotation per stream

cx18: Allow more than 63 capture buffers in rotation per stream. Implement
q_busy to hold buffers the firmware has for use. q_free holds truly unused
buffers in a pool. New buffers are given to the firmware as soon as the
firmware returns one, if there are any to give to the firmware.

Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>

authored by

Andy Walls and committed by
Mauro Carvalho Chehab
66c2a6b0 b80e1074

+113 -70
+2 -1
drivers/media/video/cx18/cx18-driver.h
··· 289 289 290 290 /* Buffer Queues */ 291 291 struct cx18_queue q_free; /* free buffers */ 292 - struct cx18_queue q_full; /* full buffers */ 292 + struct cx18_queue q_busy; /* busy buffers - in use by firmware */ 293 + struct cx18_queue q_full; /* full buffers - data for user apps */ 293 294 294 295 /* DVB / Digital Transport */ 295 296 struct cx18_dvb dvb;
+4 -10
drivers/media/video/cx18/cx18-fileops.c
··· 187 187 while ((buf = cx18_dequeue(s_vbi, &s_vbi->q_full))) { 188 188 /* byteswap and process VBI data */ 189 189 /* cx18_process_vbi_data(cx, buf, s_vbi->dma_pts, s_vbi->type); */ 190 - cx18_enqueue(s_vbi, buf, &s_vbi->q_free); 190 + cx18_stream_put_buf_fw(s_vbi, buf); 191 191 } 192 192 } 193 193 buf = &cx->vbi.sliced_mpeg_buf; ··· 361 361 tot_count - tot_written); 362 362 363 363 if (buf != &cx->vbi.sliced_mpeg_buf) { 364 - if (buf->readpos == buf->bytesused) { 365 - cx18_buf_sync_for_device(s, buf); 366 - cx18_enqueue(s, buf, &s->q_free); 367 - cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, 368 - s->handle, 369 - (void __iomem *)&cx->scb->cpu_mdl[buf->id] - 370 - cx->enc_mem, 371 - 1, buf->id, s->buf_size); 372 - } else 364 + if (buf->readpos == buf->bytesused) 365 + cx18_stream_put_buf_fw(s, buf); 366 + else 373 367 cx18_push(s, buf, &s->q_full); 374 368 } else if (buf->readpos == buf->bytesused) { 375 369 int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
+1 -2
drivers/media/video/cx18/cx18-ioctl.c
··· 746 746 continue; 747 747 CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", 748 748 s->name, s->s_flags, 749 - (s->buffers - atomic_read(&s->q_free.buffers)) 750 - * 100 / s->buffers, 749 + atomic_read(&s->q_full.buffers) * 100 / s->buffers, 751 750 (s->buffers * s->buf_size) / 1024, s->buffers); 752 751 } 753 752 CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
+1 -10
drivers/media/video/cx18/cx18-mailbox.c
··· 189 189 dvb_dmx_swfilter(&s->dvb.demux, buf->buf, 190 190 buf->bytesused); 191 191 192 - cx18_buf_sync_for_device(s, buf); 193 - cx18_enqueue(s, buf, &s->q_free); 194 - 195 - if (s->handle != CX18_INVALID_TASK_HANDLE && 196 - test_bit(CX18_F_S_STREAMING, &s->s_flags)) 197 - cx18_vapi(cx, 198 - CX18_CPU_DE_SET_MDL, 5, s->handle, 199 - (void __iomem *) 200 - &cx->scb->cpu_mdl[buf->id] - cx->enc_mem, 201 - 1, buf->id, s->buf_size); 192 + cx18_stream_put_buf_fw(s, buf); 202 193 } else 203 194 set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags); 204 195 }
+29 -27
drivers/media/video/cx18/cx18-queue.c
··· 42 42 q->bytesused = 0; 43 43 } 44 44 45 - void _cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, 46 - struct cx18_queue *q, int to_front) 45 + struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, 46 + struct cx18_queue *q, int to_front) 47 47 { 48 - /* clear the buffer if it is going to be enqueued to the free queue */ 49 - if (q == &s->q_free) { 48 + /* clear the buffer if it is not to be enqueued to the full queue */ 49 + if (q != &s->q_full) { 50 50 buf->bytesused = 0; 51 51 buf->readpos = 0; 52 52 buf->b_flags = 0; 53 53 buf->skipped = 0; 54 54 } 55 + 55 56 mutex_lock(&s->qlock); 57 + 58 + /* q_busy is restricted to 63 buffers to stay within firmware limits */ 59 + if (q == &s->q_busy && atomic_read(&q->buffers) >= 63) 60 + q = &s->q_free; 61 + 56 62 if (to_front) 57 63 list_add(&buf->list, &q->list); /* LIFO */ 58 64 else 59 65 list_add_tail(&buf->list, &q->list); /* FIFO */ 60 - atomic_inc(&q->buffers); 61 66 q->bytesused += buf->bytesused - buf->readpos; 67 + atomic_inc(&q->buffers); 68 + 62 69 mutex_unlock(&s->qlock); 70 + return q; 63 71 } 64 72 65 73 struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) ··· 78 70 if (!list_empty(&q->list)) { 79 71 buf = list_entry(q->list.next, struct cx18_buffer, list); 80 72 list_del_init(q->list.next); 81 - atomic_dec(&q->buffers); 82 73 q->bytesused -= buf->bytesused - buf->readpos; 83 74 buf->skipped = 0; 75 + atomic_dec(&q->buffers); 84 76 } 85 77 mutex_unlock(&s->qlock); 86 78 return buf; ··· 93 85 struct cx18_buffer *buf; 94 86 struct cx18_buffer *ret = NULL; 95 87 struct list_head *p, *t; 96 - LIST_HEAD(r); 97 88 98 89 mutex_lock(&s->qlock); 99 - list_for_each_safe(p, t, &s->q_free.list) { 90 + list_for_each_safe(p, t, &s->q_busy.list) { 100 91 buf = list_entry(p, struct cx18_buffer, list); 101 92 102 93 if (buf->id != id) { 103 94 buf->skipped++; 104 - if (buf->skipped >= atomic_read(&s->q_free.buffers)-1) { 95 + if (buf->skipped >= atomic_read(&s->q_busy.buffers)-1) { 105 96 /* buffer must have fallen out of rotation */ 106 - atomic_dec(&s->q_free.buffers); 107 - list_move_tail(&buf->list, &r); 108 97 CX18_WARN("Skipped %s, buffer %d, %d " 109 98 "times - it must have dropped out of " 110 99 "rotation\n", s->name, buf->id, 111 100 buf->skipped); 101 + /* move it to q_free */ 102 + list_move_tail(&buf->list, &s->q_free.list); 103 + buf->bytesused = buf->readpos = buf->b_flags = 104 + buf->skipped = 0; 105 + atomic_dec(&s->q_busy.buffers); 106 + atomic_inc(&s->q_free.buffers); 112 107 } 113 108 continue; 114 109 } 115 110 116 111 buf->bytesused = bytesused; 117 - atomic_dec(&s->q_free.buffers); 118 112 if (s->type == CX18_ENC_STREAM_TYPE_TS) { 119 113 /* 120 114 * TS doesn't use q_full, but for sweeping up lost ··· 126 116 */ 127 117 list_del_init(&buf->list); 128 118 } else { 129 - atomic_inc(&s->q_full.buffers); 130 - s->q_full.bytesused += buf->bytesused; 131 119 list_move_tail(&buf->list, &s->q_full.list); 120 + s->q_full.bytesused += buf->bytesused; 121 + atomic_inc(&s->q_full.buffers); 132 122 } 123 + atomic_dec(&s->q_busy.buffers); 133 124 134 125 ret = buf; 135 126 break; 136 127 } 137 - mutex_unlock(&s->qlock); 138 128 139 - /* Put lost buffers back into firmware transfer rotation */ 140 - while (!list_empty(&r)) { 141 - buf = list_entry(r.next, struct cx18_buffer, list); 142 - list_del_init(r.next); 143 - cx18_enqueue(s, buf, &s->q_free); 144 - cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, 145 - (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, 146 - 1, buf->id, s->buf_size); 147 - CX18_INFO("Returning %s, buffer %d back to transfer rotation\n", 148 - s->name, buf->id); 149 - /* and there was much rejoicing... */ 150 - } 129 + /* Put more buffers into the transfer rotation from q_free, if we can */ 130 + cx18_stream_load_fw_queue_nolock(s); 131 + mutex_unlock(&s->qlock); 151 132 return ret; 152 133 } 153 134 ··· 163 162 164 163 void cx18_flush_queues(struct cx18_stream *s) 165 164 { 165 + cx18_queue_flush(s, &s->q_busy); 166 166 cx18_queue_flush(s, &s->q_full); 167 167 } 168 168
+8 -8
drivers/media/video/cx18/cx18-queue.h
··· 43 43 void cx18_buf_swap(struct cx18_buffer *buf); 44 44 45 45 /* cx18_queue utility functions */ 46 - void _cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, 47 - struct cx18_queue *q, int to_front); 46 + struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, 47 + struct cx18_queue *q, int to_front); 48 48 49 49 static inline 50 - void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, 51 - struct cx18_queue *q) 50 + struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, 51 + struct cx18_queue *q) 52 52 { 53 - _cx18_enqueue(s, buf, q, 0); /* FIFO */ 53 + return _cx18_enqueue(s, buf, q, 0); /* FIFO */ 54 54 } 55 55 56 56 static inline 57 - void cx18_push(struct cx18_stream *s, struct cx18_buffer *buf, 58 - struct cx18_queue *q) 57 + struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_buffer *buf, 58 + struct cx18_queue *q) 59 59 { 60 - _cx18_enqueue(s, buf, q, 1); /* LIFO */ 60 + return _cx18_enqueue(s, buf, q, 1); /* LIFO */ 61 61 } 62 62 63 63 void cx18_queue_init(struct cx18_queue *q);
+65 -12
drivers/media/video/cx18/cx18-streams.c
··· 127 127 s->buf_size = cx->stream_buf_size[type]; 128 128 if (s->buf_size) 129 129 s->buffers = max_size / s->buf_size; 130 - if (s->buffers > 63) { 131 - /* Each stream has a maximum of 63 buffers, 132 - ensure we do not exceed that. */ 133 - s->buffers = 63; 134 - s->buf_size = (max_size / s->buffers) & ~0xfff; 135 - } 136 130 mutex_init(&s->qlock); 137 131 init_waitqueue_head(&s->waitq); 138 132 s->id = -1; 139 133 cx18_queue_init(&s->q_free); 134 + cx18_queue_init(&s->q_busy); 140 135 cx18_queue_init(&s->q_full); 141 136 } 142 137 ··· 396 401 cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data); 397 402 } 398 403 404 + struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, 405 + struct cx18_buffer *buf) 406 + { 407 + struct cx18 *cx = s->cx; 408 + struct cx18_queue *q; 409 + 410 + /* Don't give it to the firmware, if we're not running a capture */ 411 + if (s->handle == CX18_INVALID_TASK_HANDLE || 412 + !test_bit(CX18_F_S_STREAMING, &s->s_flags)) 413 + return cx18_enqueue(s, buf, &s->q_free); 414 + 415 + q = cx18_enqueue(s, buf, &s->q_busy); 416 + if (q != &s->q_busy) 417 + return q; /* The firmware has the max buffers it can handle */ 418 + 419 + cx18_buf_sync_for_device(s, buf); 420 + cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, 421 + (void __iomem *) &cx->scb->cpu_mdl[buf->id] - cx->enc_mem, 422 + 1, buf->id, s->buf_size); 423 + return q; 424 + } 425 + 426 + /* Must hold s->qlock when calling */ 427 + void cx18_stream_load_fw_queue_nolock(struct cx18_stream *s) 428 + { 429 + struct cx18_buffer *buf; 430 + struct cx18 *cx = s->cx; 431 + 432 + /* Move from q_free to q_busy notifying the firmware: 63 buf limit */ 433 + while (s->handle != CX18_INVALID_TASK_HANDLE && 434 + test_bit(CX18_F_S_STREAMING, &s->s_flags) && 435 + atomic_read(&s->q_busy.buffers) < 63 && 436 + !list_empty(&s->q_free.list)) { 437 + 438 + /* Move from q_free to q_busy */ 439 + buf = list_entry(s->q_free.list.next, struct cx18_buffer, list); 440 + list_move_tail(&buf->list, &s->q_busy.list); 441 + buf->bytesused = buf->readpos = buf->b_flags = buf->skipped = 0; 442 + atomic_dec(&s->q_free.buffers); 443 + atomic_inc(&s->q_busy.buffers); 444 + 445 + /* Notify firmware */ 446 + cx18_buf_sync_for_device(s, buf); 447 + cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, 448 + (void __iomem *) &cx->scb->cpu_mdl[buf->id] - cx->enc_mem, 449 + 1, buf->id, s->buf_size); 450 + } 451 + } 452 + 399 453 int cx18_start_v4l2_encode_stream(struct cx18_stream *s) 400 454 { 401 455 u32 data[MAX_MB_ARGUMENTS]; 402 456 struct cx18 *cx = s->cx; 403 457 struct list_head *p; 458 + struct cx18_buffer *buf; 404 459 int ts = 0; 405 460 int captype = 0; 406 461 ··· 533 488 (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem, 534 489 (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem); 535 490 491 + /* Init all the cpu_mdls for this stream */ 492 + cx18_flush_queues(s); 493 + mutex_lock(&s->qlock); 536 494 list_for_each(p, &s->q_free.list) { 537 - struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list); 538 - 495 + buf = list_entry(p, struct cx18_buffer, list); 539 496 cx18_writel(cx, buf->dma_handle, 540 497 &cx->scb->cpu_mdl[buf->id].paddr); 541 498 cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length); 542 - cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, 543 - (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, 544 - 1, buf->id, s->buf_size); 545 499 } 500 + cx18_stream_load_fw_queue_nolock(s); 501 + mutex_unlock(&s->qlock); 502 + 546 503 /* begin_capture */ 547 504 if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) { 548 505 CX18_DEBUG_WARN("Error starting capture!\n"); ··· 553 506 cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1); 554 507 else 555 508 cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle); 509 + clear_bit(CX18_F_S_STREAMING, &s->s_flags); 510 + /* FIXME - CX18_F_S_STREAMOFF as well? */ 556 511 cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); 557 512 cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); 558 - /* FIXME - clean-up DSP0_INT mask, i_flags, s_flags, etc. */ 513 + s->handle = CX18_INVALID_TASK_HANDLE; 514 + if (atomic_read(&cx->tot_capturing) == 0) { 515 + set_bit(CX18_F_I_EOS, &cx->i_flags); 516 + cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK); 517 + } 559 518 return -EINVAL; 560 519 } 561 520
+3
drivers/media/video/cx18/cx18-streams.h
··· 29 29 void cx18_streams_cleanup(struct cx18 *cx, int unregister); 30 30 31 31 /* Capture related */ 32 + void cx18_stream_load_fw_queue_nolock(struct cx18_stream *s); 33 + struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, 34 + struct cx18_buffer *buf); 32 35 int cx18_start_v4l2_encode_stream(struct cx18_stream *s); 33 36 int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end); 34 37