diff options
author | Andy Walls <awalls@radix.net> | 2009-04-15 19:45:10 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-06-16 17:20:44 -0400 |
commit | 21a278b85d3c6b8064af0c03aec3205e28aad3b7 (patch) | |
tree | efa0ee9cdfc303b03faf5b080b7f5721cf13c765 | |
parent | 40c5520f55924ba87090d0d93222baad74202559 (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>
-rw-r--r-- | drivers/media/video/cx18/cx18-driver.c | 25 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-driver.h | 30 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-dvb.c | 1 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-queue.c | 2 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-streams.c | 82 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-streams.h | 17 |
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 658cfbb1b97e..f0006edc635d 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 | ||
583 | static 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 | ||
1099 | static 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 | |||
1106 | static void cx18_remove(struct pci_dev *pci_dev) | 1107 | static 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 35a6758a7aac..f89b82367963 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 | |||
349 | struct 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 | ||
358 | struct cx18_stream { | 331 | struct 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 3b86f57cd15a..e7285a1096f2 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 693a745b0858..fa1ed7897d97 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 | ||
30 | void cx18_buf_swap(struct cx18_buffer *buf) | 30 | void 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 e1934e9cfdc8..41a1b2618aac 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 | ||
129 | static int cx18_prep_dev(struct cx18 *cx, int type) | 131 | static 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 | ||
480 | static inline | ||
481 | void free_out_work_order(struct cx18_out_work_order *order) | ||
482 | { | ||
483 | atomic_set(&order->pending, 0); | ||
484 | } | ||
485 | |||
486 | void cx18_out_work_handler(struct work_struct *work) | 482 | void 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 | |||
501 | static | ||
502 | struct 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 | |||
523 | struct 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 | |||
543 | void 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 | ||
562 | int cx18_start_v4l2_encode_stream(struct cx18_stream *s) | 490 | int 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 1fdcfffb07ed..1afc3fd9d822 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); | |||
29 | void cx18_streams_cleanup(struct cx18 *cx, int unregister); | 29 | void 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 */ |
32 | void cx18_stream_load_fw_queue(struct cx18_stream *s); | 32 | static inline void cx18_stream_load_fw_queue(struct cx18_stream *s) |
33 | struct 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 | |||
38 | static 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 | |||
35 | void cx18_out_work_handler(struct work_struct *work); | 46 | void cx18_out_work_handler(struct work_struct *work); |
36 | 47 | ||
37 | /* Capture related */ | 48 | /* Capture related */ |