diff options
author | Andy Walls <awalls@radix.net> | 2009-04-13 21:42:43 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-06-16 17:20:44 -0400 |
commit | 87116159517ecf6b9cf62a136f2935a63833c485 (patch) | |
tree | 4a52a97e9e740304ed44d4348762836284f4d100 /drivers/media/video/cx18/cx18-streams.c | |
parent | deed75ed9f7576ada4bca02e6c851833a352a38d (diff) |
V4L/DVB (11616): cx18: Add a work queue for deferring empty buffer handoffs to the firmware
This change defers sending all CX18_CPU_DE_SET_MDL commands, for a stream with
an ongoing capture, by adding a work queue to handle sending such commands when
needed. This prevents any sleeps, caused by notifying the firmware of new
usable buffers, when a V4L2 application read() is being satisfied or when
an incoming buffer is processed by the cx18-NN-in work queue thread.
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-streams.c')
-rw-r--r-- | drivers/media/video/cx18/cx18-streams.c | 99 |
1 files changed, 94 insertions, 5 deletions
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 0932b76b2373..bbeb01c5cf32 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c | |||
@@ -431,14 +431,16 @@ static void cx18_vbi_setup(struct cx18_stream *s) | |||
431 | cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data); | 431 | cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data); |
432 | } | 432 | } |
433 | 433 | ||
434 | struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, | 434 | static |
435 | struct cx18_buffer *buf) | 435 | struct cx18_queue *_cx18_stream_put_buf_fw(struct cx18_stream *s, |
436 | struct cx18_buffer *buf) | ||
436 | { | 437 | { |
437 | struct cx18 *cx = s->cx; | 438 | struct cx18 *cx = s->cx; |
438 | struct cx18_queue *q; | 439 | struct cx18_queue *q; |
439 | 440 | ||
440 | /* Don't give it to the firmware, if we're not running a capture */ | 441 | /* Don't give it to the firmware, if we're not running a capture */ |
441 | if (s->handle == CX18_INVALID_TASK_HANDLE || | 442 | if (s->handle == CX18_INVALID_TASK_HANDLE || |
443 | test_bit(CX18_F_S_STOPPING, &s->s_flags) || | ||
442 | !test_bit(CX18_F_S_STREAMING, &s->s_flags)) | 444 | !test_bit(CX18_F_S_STREAMING, &s->s_flags)) |
443 | return cx18_enqueue(s, buf, &s->q_free); | 445 | return cx18_enqueue(s, buf, &s->q_free); |
444 | 446 | ||
@@ -453,7 +455,8 @@ struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, | |||
453 | return q; | 455 | return q; |
454 | } | 456 | } |
455 | 457 | ||
456 | void cx18_stream_load_fw_queue(struct cx18_stream *s) | 458 | static |
459 | void _cx18_stream_load_fw_queue(struct cx18_stream *s) | ||
457 | { | 460 | { |
458 | struct cx18_queue *q; | 461 | struct cx18_queue *q; |
459 | struct cx18_buffer *buf; | 462 | struct cx18_buffer *buf; |
@@ -467,11 +470,93 @@ void cx18_stream_load_fw_queue(struct cx18_stream *s) | |||
467 | buf = cx18_dequeue(s, &s->q_free); | 470 | buf = cx18_dequeue(s, &s->q_free); |
468 | if (buf == NULL) | 471 | if (buf == NULL) |
469 | break; | 472 | break; |
470 | q = cx18_stream_put_buf_fw(s, buf); | 473 | q = _cx18_stream_put_buf_fw(s, buf); |
471 | } while (atomic_read(&s->q_busy.buffers) < CX18_MAX_FW_MDLS_PER_STREAM | 474 | } while (atomic_read(&s->q_busy.buffers) < CX18_MAX_FW_MDLS_PER_STREAM |
472 | && q == &s->q_busy); | 475 | && q == &s->q_busy); |
473 | } | 476 | } |
474 | 477 | ||
478 | static inline | ||
479 | void free_out_work_order(struct cx18_out_work_order *order) | ||
480 | { | ||
481 | atomic_set(&order->pending, 0); | ||
482 | } | ||
483 | |||
484 | void cx18_out_work_handler(struct work_struct *work) | ||
485 | { | ||
486 | struct cx18_out_work_order *order = | ||
487 | container_of(work, struct cx18_out_work_order, work); | ||
488 | struct cx18_stream *s = order->s; | ||
489 | struct cx18_buffer *buf = order->buf; | ||
490 | |||
491 | free_out_work_order(order); | ||
492 | |||
493 | if (buf == NULL) | ||
494 | _cx18_stream_load_fw_queue(s); | ||
495 | else | ||
496 | _cx18_stream_put_buf_fw(s, buf); | ||
497 | } | ||
498 | |||
499 | static | ||
500 | struct cx18_out_work_order *alloc_out_work_order(struct cx18 *cx) | ||
501 | { | ||
502 | int i; | ||
503 | struct cx18_out_work_order *order = NULL; | ||
504 | |||
505 | for (i = 0; i < CX18_MAX_OUT_WORK_ORDERS; i++) { | ||
506 | /* | ||
507 | * We need "pending" to be atomic to inspect & set its contents | ||
508 | * 1. "pending" is only set to 1 here, but needs multiple access | ||
509 | * protection | ||
510 | * 2. work handler threads only clear "pending" and only | ||
511 | * on one, particular work order at a time, per handler thread. | ||
512 | */ | ||
513 | if (atomic_add_unless(&cx->out_work_order[i].pending, 1, 1)) { | ||
514 | order = &cx->out_work_order[i]; | ||
515 | break; | ||
516 | } | ||
517 | } | ||
518 | return order; | ||
519 | } | ||
520 | |||
521 | struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, | ||
522 | struct cx18_buffer *buf) | ||
523 | { | ||
524 | struct cx18 *cx = s->cx; | ||
525 | struct cx18_out_work_order *order; | ||
526 | |||
527 | order = alloc_out_work_order(cx); | ||
528 | if (order == NULL) { | ||
529 | CX18_DEBUG_WARN("No blank, outgoing-mailbox, deferred-work, " | ||
530 | "order forms available; sending buffer %u back " | ||
531 | "to the firmware immediately for stream %s\n", | ||
532 | buf->id, s->name); | ||
533 | return _cx18_stream_put_buf_fw(s, buf); | ||
534 | } | ||
535 | order->s = s; | ||
536 | order->buf = buf; | ||
537 | queue_work(cx->out_work_queue, &order->work); | ||
538 | return NULL; | ||
539 | } | ||
540 | |||
541 | void cx18_stream_load_fw_queue(struct cx18_stream *s) | ||
542 | { | ||
543 | struct cx18 *cx = s->cx; | ||
544 | struct cx18_out_work_order *order; | ||
545 | |||
546 | order = alloc_out_work_order(cx); | ||
547 | if (order == NULL) { | ||
548 | CX18_DEBUG_WARN("No blank, outgoing-mailbox, deferred-work, " | ||
549 | "order forms available; filling the firmware " | ||
550 | "buffer queue immediately for stream %s\n", | ||
551 | s->name); | ||
552 | _cx18_stream_load_fw_queue(s); | ||
553 | return; | ||
554 | } | ||
555 | order->s = s; | ||
556 | order->buf = NULL; /* Indicates to load the fw queue */ | ||
557 | queue_work(cx->out_work_queue, &order->work); | ||
558 | } | ||
559 | |||
475 | int cx18_start_v4l2_encode_stream(struct cx18_stream *s) | 560 | int cx18_start_v4l2_encode_stream(struct cx18_stream *s) |
476 | { | 561 | { |
477 | u32 data[MAX_MB_ARGUMENTS]; | 562 | u32 data[MAX_MB_ARGUMENTS]; |
@@ -607,12 +692,13 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) | |||
607 | cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length); | 692 | cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length); |
608 | } | 693 | } |
609 | mutex_unlock(&s->qlock); | 694 | mutex_unlock(&s->qlock); |
610 | cx18_stream_load_fw_queue(s); | 695 | _cx18_stream_load_fw_queue(s); |
611 | 696 | ||
612 | /* begin_capture */ | 697 | /* begin_capture */ |
613 | if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) { | 698 | if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) { |
614 | CX18_DEBUG_WARN("Error starting capture!\n"); | 699 | CX18_DEBUG_WARN("Error starting capture!\n"); |
615 | /* Ensure we're really not capturing before releasing MDLs */ | 700 | /* Ensure we're really not capturing before releasing MDLs */ |
701 | set_bit(CX18_F_S_STOPPING, &s->s_flags); | ||
616 | if (s->type == CX18_ENC_STREAM_TYPE_MPG) | 702 | if (s->type == CX18_ENC_STREAM_TYPE_MPG) |
617 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1); | 703 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1); |
618 | else | 704 | else |
@@ -622,6 +708,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) | |||
622 | cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); | 708 | cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); |
623 | cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); | 709 | cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); |
624 | s->handle = CX18_INVALID_TASK_HANDLE; | 710 | s->handle = CX18_INVALID_TASK_HANDLE; |
711 | clear_bit(CX18_F_S_STOPPING, &s->s_flags); | ||
625 | if (atomic_read(&cx->tot_capturing) == 0) { | 712 | if (atomic_read(&cx->tot_capturing) == 0) { |
626 | set_bit(CX18_F_I_EOS, &cx->i_flags); | 713 | set_bit(CX18_F_I_EOS, &cx->i_flags); |
627 | cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK); | 714 | cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK); |
@@ -666,6 +753,7 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) | |||
666 | if (atomic_read(&cx->tot_capturing) == 0) | 753 | if (atomic_read(&cx->tot_capturing) == 0) |
667 | return 0; | 754 | return 0; |
668 | 755 | ||
756 | set_bit(CX18_F_S_STOPPING, &s->s_flags); | ||
669 | if (s->type == CX18_ENC_STREAM_TYPE_MPG) | 757 | if (s->type == CX18_ENC_STREAM_TYPE_MPG) |
670 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, !gop_end); | 758 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, !gop_end); |
671 | else | 759 | else |
@@ -689,6 +777,7 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) | |||
689 | 777 | ||
690 | cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); | 778 | cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); |
691 | s->handle = CX18_INVALID_TASK_HANDLE; | 779 | s->handle = CX18_INVALID_TASK_HANDLE; |
780 | clear_bit(CX18_F_S_STOPPING, &s->s_flags); | ||
692 | 781 | ||
693 | if (atomic_read(&cx->tot_capturing) > 0) | 782 | if (atomic_read(&cx->tot_capturing) > 0) |
694 | return 0; | 783 | return 0; |