diff options
Diffstat (limited to 'drivers/media/video/cx18')
-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 */ |