aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/cx18
diff options
context:
space:
mode:
authorAndy Walls <awalls@radix.net>2009-04-15 19:45:10 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2009-06-16 17:20:44 -0400
commit21a278b85d3c6b8064af0c03aec3205e28aad3b7 (patch)
treeefa0ee9cdfc303b03faf5b080b7f5721cf13c765 /drivers/media/video/cx18
parent40c5520f55924ba87090d0d93222baad74202559 (diff)
V4L/DVB (11619): cx18: Simplify the work handler for outgoing mailbox commands
Simplify the way outgoing work handler gets scheduled to send empty buffers back to the firmware for use. Also reduced the memory required for scheduling this outgoing work, by using a single, per stream work object. Signed-off-by: Andy Walls <awalls@radix.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/cx18')
-rw-r--r--drivers/media/video/cx18/cx18-driver.c25
-rw-r--r--drivers/media/video/cx18/cx18-driver.h30
-rw-r--r--drivers/media/video/cx18/cx18-dvb.c1
-rw-r--r--drivers/media/video/cx18/cx18-queue.c2
-rw-r--r--drivers/media/video/cx18/cx18-streams.c82
-rw-r--r--drivers/media/video/cx18/cx18-streams.h17
6 files changed, 33 insertions, 124 deletions
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index 658cfbb1b97..f0006edc635 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -30,6 +30,7 @@
30#include "cx18-irq.h" 30#include "cx18-irq.h"
31#include "cx18-gpio.h" 31#include "cx18-gpio.h"
32#include "cx18-firmware.h" 32#include "cx18-firmware.h"
33#include "cx18-queue.h"
33#include "cx18-streams.h" 34#include "cx18-streams.h"
34#include "cx18-av-core.h" 35#include "cx18-av-core.h"
35#include "cx18-scb.h" 36#include "cx18-scb.h"
@@ -580,13 +581,6 @@ static void __devinit cx18_init_in_work_orders(struct cx18 *cx)
580 } 581 }
581} 582}
582 583
583static void __devinit cx18_init_out_work_orders(struct cx18 *cx)
584{
585 int i;
586 for (i = 0; i < CX18_MAX_OUT_WORK_ORDERS; i++)
587 INIT_WORK(&cx->out_work_order[i].work, cx18_out_work_handler);
588}
589
590/* Precondition: the cx18 structure has been memset to 0. Only 584/* Precondition: the cx18 structure has been memset to 0. Only
591 the dev and instance fields have been filled in. 585 the dev and instance fields have been filled in.
592 No assumptions on the card type may be made here (see cx18_init_struct2 586 No assumptions on the card type may be made here (see cx18_init_struct2
@@ -613,7 +607,6 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
613 return ret; 607 return ret;
614 } 608 }
615 609
616 cx18_init_out_work_orders(cx);
617 cx18_init_in_work_orders(cx); 610 cx18_init_in_work_orders(cx);
618 611
619 /* start counting open_id at 1 */ 612 /* start counting open_id at 1 */
@@ -1103,6 +1096,14 @@ static void cx18_cancel_in_work_orders(struct cx18 *cx)
1103 cancel_work_sync(&cx->in_work_order[i].work); 1096 cancel_work_sync(&cx->in_work_order[i].work);
1104} 1097}
1105 1098
1099static void cx18_cancel_out_work_orders(struct cx18 *cx)
1100{
1101 int i;
1102 for (i = 0; i < CX18_MAX_STREAMS; i++)
1103 if (&cx->streams[i].video_dev != NULL)
1104 cancel_work_sync(&cx->streams[i].out_work_order);
1105}
1106
1106static void cx18_remove(struct pci_dev *pci_dev) 1107static void cx18_remove(struct pci_dev *pci_dev)
1107{ 1108{
1108 struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); 1109 struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
@@ -1121,13 +1122,7 @@ static void cx18_remove(struct pci_dev *pci_dev)
1121 1122
1122 /* Incoming work can cause outgoing work, so clean up incoming first */ 1123 /* Incoming work can cause outgoing work, so clean up incoming first */
1123 cx18_cancel_in_work_orders(cx); 1124 cx18_cancel_in_work_orders(cx);
1124 1125 cx18_cancel_out_work_orders(cx);
1125 /*
1126 * An outgoing work order can have the only pointer to a dynamically
1127 * allocated buffer, so we need to flush outgoing work and not just
1128 * cancel it, so we don't lose the pointer and leak memory.
1129 */
1130 flush_workqueue(cx->out_work_queue);
1131 1126
1132 /* Stop ack interrupts that may have been needed for work to finish */ 1127 /* Stop ack interrupts that may have been needed for work to finish */
1133 cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); 1128 cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index 35a6758a7aa..f89b8236796 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -326,33 +326,6 @@ struct cx18_in_work_order {
326 char *str; 326 char *str;
327}; 327};
328 328
329/*
330 * There are 2 types of deferrable tasks that send messages out to the firmware:
331 * 1. Sending individual buffers back to the firmware
332 * 2. Sending as many free buffers for a stream from q_free as we can to the fw
333 *
334 * The worst case scenario for multiple simultaneous streams is
335 * TS, YUV, PCM, VBI, MPEG, and IDX all going at once.
336 *
337 * We try to load the firmware queue with as many free buffers as possible,
338 * whenever we get a buffer back for a stream. For the TS we return the single
339 * buffer to the firmware at that time as well. For all other streams, we
340 * return single buffers to the firmware as the application drains them.
341 *
342 * 6 streams * 2 sets of orders * (1 single buf + 1 load fw from q_free)
343 * = 24 work orders should cover our needs, provided the applications read
344 * at a fairly steady rate. If apps don't, we fall back to non-deferred
345 * operation, when no cx18_out_work_orders are available for use.
346 */
347#define CX18_MAX_OUT_WORK_ORDERS (24)
348
349struct cx18_out_work_order {
350 struct work_struct work;
351 atomic_t pending;
352 struct cx18_stream *s;
353 struct cx18_buffer *buf; /* buf == NULL, means load fw from q_free */
354};
355
356#define CX18_INVALID_TASK_HANDLE 0xffffffff 329#define CX18_INVALID_TASK_HANDLE 0xffffffff
357 330
358struct cx18_stream { 331struct cx18_stream {
@@ -381,6 +354,8 @@ struct cx18_stream {
381 struct cx18_queue q_busy; /* busy buffers - in use by firmware */ 354 struct cx18_queue q_busy; /* busy buffers - in use by firmware */
382 struct cx18_queue q_full; /* full buffers - data for user apps */ 355 struct cx18_queue q_full; /* full buffers - data for user apps */
383 356
357 struct work_struct out_work_order;
358
384 /* DVB / Digital Transport */ 359 /* DVB / Digital Transport */
385 struct cx18_dvb dvb; 360 struct cx18_dvb dvb;
386}; 361};
@@ -603,7 +578,6 @@ struct cx18 {
603 578
604 struct workqueue_struct *out_work_queue; 579 struct workqueue_struct *out_work_queue;
605 char out_workq_name[12]; /* "cx18-NN-out" */ 580 char out_workq_name[12]; /* "cx18-NN-out" */
606 struct cx18_out_work_order out_work_order[CX18_MAX_OUT_WORK_ORDERS];
607 581
608 /* i2c */ 582 /* i2c */
609 struct i2c_adapter i2c_adap[2]; 583 struct i2c_adapter i2c_adap[2];
diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c
index 3b86f57cd15..e7285a1096f 100644
--- a/drivers/media/video/cx18/cx18-dvb.c
+++ b/drivers/media/video/cx18/cx18-dvb.c
@@ -23,6 +23,7 @@
23#include "cx18-version.h" 23#include "cx18-version.h"
24#include "cx18-dvb.h" 24#include "cx18-dvb.h"
25#include "cx18-io.h" 25#include "cx18-io.h"
26#include "cx18-queue.h"
26#include "cx18-streams.h" 27#include "cx18-streams.h"
27#include "cx18-cards.h" 28#include "cx18-cards.h"
28#include "s5h1409.h" 29#include "s5h1409.h"
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c
index 693a745b085..fa1ed7897d9 100644
--- a/drivers/media/video/cx18/cx18-queue.c
+++ b/drivers/media/video/cx18/cx18-queue.c
@@ -23,8 +23,8 @@
23 */ 23 */
24 24
25#include "cx18-driver.h" 25#include "cx18-driver.h"
26#include "cx18-streams.h"
27#include "cx18-queue.h" 26#include "cx18-queue.h"
27#include "cx18-streams.h"
28#include "cx18-scb.h" 28#include "cx18-scb.h"
29 29
30void cx18_buf_swap(struct cx18_buffer *buf) 30void cx18_buf_swap(struct cx18_buffer *buf)
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index e1934e9cfdc..41a1b2618aa 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -124,6 +124,8 @@ static void cx18_stream_init(struct cx18 *cx, int type)
124 cx18_queue_init(&s->q_busy); 124 cx18_queue_init(&s->q_busy);
125 spin_lock_init(&s->q_full.lock); 125 spin_lock_init(&s->q_full.lock);
126 cx18_queue_init(&s->q_full); 126 cx18_queue_init(&s->q_full);
127
128 INIT_WORK(&s->out_work_order, cx18_out_work_handler);
127} 129}
128 130
129static int cx18_prep_dev(struct cx18 *cx, int type) 131static int cx18_prep_dev(struct cx18 *cx, int type)
@@ -477,86 +479,12 @@ void _cx18_stream_load_fw_queue(struct cx18_stream *s)
477 && q == &s->q_busy); 479 && q == &s->q_busy);
478} 480}
479 481
480static inline
481void free_out_work_order(struct cx18_out_work_order *order)
482{
483 atomic_set(&order->pending, 0);
484}
485
486void cx18_out_work_handler(struct work_struct *work) 482void cx18_out_work_handler(struct work_struct *work)
487{ 483{
488 struct cx18_out_work_order *order = 484 struct cx18_stream *s =
489 container_of(work, struct cx18_out_work_order, work); 485 container_of(work, struct cx18_stream, out_work_order);
490 struct cx18_stream *s = order->s;
491 struct cx18_buffer *buf = order->buf;
492
493 free_out_work_order(order);
494
495 if (buf == NULL)
496 _cx18_stream_load_fw_queue(s);
497 else
498 _cx18_stream_put_buf_fw(s, buf);
499}
500
501static
502struct cx18_out_work_order *alloc_out_work_order(struct cx18 *cx)
503{
504 int i;
505 struct cx18_out_work_order *order = NULL;
506
507 for (i = 0; i < CX18_MAX_OUT_WORK_ORDERS; i++) {
508 /*
509 * We need "pending" to be atomic to inspect & set its contents
510 * 1. "pending" is only set to 1 here, but needs multiple access
511 * protection
512 * 2. work handler threads only clear "pending" and only
513 * on one, particular work order at a time, per handler thread.
514 */
515 if (atomic_add_unless(&cx->out_work_order[i].pending, 1, 1)) {
516 order = &cx->out_work_order[i];
517 break;
518 }
519 }
520 return order;
521}
522
523struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s,
524 struct cx18_buffer *buf)
525{
526 struct cx18 *cx = s->cx;
527 struct cx18_out_work_order *order;
528
529 order = alloc_out_work_order(cx);
530 if (order == NULL) {
531 CX18_DEBUG_WARN("No blank, outgoing-mailbox, deferred-work, "
532 "order forms available; sending buffer %u back "
533 "to the firmware immediately for stream %s\n",
534 buf->id, s->name);
535 return _cx18_stream_put_buf_fw(s, buf);
536 }
537 order->s = s;
538 order->buf = buf;
539 queue_work(cx->out_work_queue, &order->work);
540 return NULL;
541}
542
543void cx18_stream_load_fw_queue(struct cx18_stream *s)
544{
545 struct cx18 *cx = s->cx;
546 struct cx18_out_work_order *order;
547 486
548 order = alloc_out_work_order(cx); 487 _cx18_stream_load_fw_queue(s);
549 if (order == NULL) {
550 CX18_DEBUG_WARN("No blank, outgoing-mailbox, deferred-work, "
551 "order forms available; filling the firmware "
552 "buffer queue immediately for stream %s\n",
553 s->name);
554 _cx18_stream_load_fw_queue(s);
555 return;
556 }
557 order->s = s;
558 order->buf = NULL; /* Indicates to load the fw queue */
559 queue_work(cx->out_work_queue, &order->work);
560} 488}
561 489
562int cx18_start_v4l2_encode_stream(struct cx18_stream *s) 490int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h
index 1fdcfffb07e..1afc3fd9d82 100644
--- a/drivers/media/video/cx18/cx18-streams.h
+++ b/drivers/media/video/cx18/cx18-streams.h
@@ -29,9 +29,20 @@ int cx18_streams_register(struct cx18 *cx);
29void cx18_streams_cleanup(struct cx18 *cx, int unregister); 29void cx18_streams_cleanup(struct cx18 *cx, int unregister);
30 30
31/* Related to submission of buffers to firmware */ 31/* Related to submission of buffers to firmware */
32void cx18_stream_load_fw_queue(struct cx18_stream *s); 32static inline void cx18_stream_load_fw_queue(struct cx18_stream *s)
33struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, 33{
34 struct cx18_buffer *buf); 34 struct cx18 *cx = s->cx;
35 queue_work(cx->out_work_queue, &s->out_work_order);
36}
37
38static inline void cx18_stream_put_buf_fw(struct cx18_stream *s,
39 struct cx18_buffer *buf)
40{
41 /* Put buf on q_free; the out work handler will move buf(s) to q_busy */
42 cx18_enqueue(s, buf, &s->q_free);
43 cx18_stream_load_fw_queue(s);
44}
45
35void cx18_out_work_handler(struct work_struct *work); 46void cx18_out_work_handler(struct work_struct *work);
36 47
37/* Capture related */ 48/* Capture related */