diff options
-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 @@ #include "cx18-irq.h" #include "cx18-gpio.h" #include "cx18-firmware.h" +#include "cx18-queue.h" #include "cx18-streams.h" #include "cx18-av-core.h" #include "cx18-scb.h" @@ -580,13 +581,6 @@ static void __devinit cx18_init_in_work_orders(struct cx18 *cx) } } -static void __devinit cx18_init_out_work_orders(struct cx18 *cx) -{ - int i; - for (i = 0; i < CX18_MAX_OUT_WORK_ORDERS; i++) - INIT_WORK(&cx->out_work_order[i].work, cx18_out_work_handler); -} - /* Precondition: the cx18 structure has been memset to 0. Only the dev and instance fields have been filled in. 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) return ret; } - cx18_init_out_work_orders(cx); cx18_init_in_work_orders(cx); /* start counting open_id at 1 */ @@ -1103,6 +1096,14 @@ static void cx18_cancel_in_work_orders(struct cx18 *cx) cancel_work_sync(&cx->in_work_order[i].work); } +static void cx18_cancel_out_work_orders(struct cx18 *cx) +{ + int i; + for (i = 0; i < CX18_MAX_STREAMS; i++) + if (&cx->streams[i].video_dev != NULL) + cancel_work_sync(&cx->streams[i].out_work_order); +} + static void cx18_remove(struct pci_dev *pci_dev) { struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); @@ -1121,13 +1122,7 @@ static void cx18_remove(struct pci_dev *pci_dev) /* Incoming work can cause outgoing work, so clean up incoming first */ cx18_cancel_in_work_orders(cx); - - /* - * An outgoing work order can have the only pointer to a dynamically - * allocated buffer, so we need to flush outgoing work and not just - * cancel it, so we don't lose the pointer and leak memory. - */ - flush_workqueue(cx->out_work_queue); + cx18_cancel_out_work_orders(cx); /* Stop ack interrupts that may have been needed for work to finish */ 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 { char *str; }; -/* - * There are 2 types of deferrable tasks that send messages out to the firmware: - * 1. Sending individual buffers back to the firmware - * 2. Sending as many free buffers for a stream from q_free as we can to the fw - * - * The worst case scenario for multiple simultaneous streams is - * TS, YUV, PCM, VBI, MPEG, and IDX all going at once. - * - * We try to load the firmware queue with as many free buffers as possible, - * whenever we get a buffer back for a stream. For the TS we return the single - * buffer to the firmware at that time as well. For all other streams, we - * return single buffers to the firmware as the application drains them. - * - * 6 streams * 2 sets of orders * (1 single buf + 1 load fw from q_free) - * = 24 work orders should cover our needs, provided the applications read - * at a fairly steady rate. If apps don't, we fall back to non-deferred - * operation, when no cx18_out_work_orders are available for use. - */ -#define CX18_MAX_OUT_WORK_ORDERS (24) - -struct cx18_out_work_order { - struct work_struct work; - atomic_t pending; - struct cx18_stream *s; - struct cx18_buffer *buf; /* buf == NULL, means load fw from q_free */ -}; - #define CX18_INVALID_TASK_HANDLE 0xffffffff struct cx18_stream { @@ -381,6 +354,8 @@ struct cx18_stream { struct cx18_queue q_busy; /* busy buffers - in use by firmware */ struct cx18_queue q_full; /* full buffers - data for user apps */ + struct work_struct out_work_order; + /* DVB / Digital Transport */ struct cx18_dvb dvb; }; @@ -603,7 +578,6 @@ struct cx18 { struct workqueue_struct *out_work_queue; char out_workq_name[12]; /* "cx18-NN-out" */ - struct cx18_out_work_order out_work_order[CX18_MAX_OUT_WORK_ORDERS]; /* i2c */ 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 @@ #include "cx18-version.h" #include "cx18-dvb.h" #include "cx18-io.h" +#include "cx18-queue.h" #include "cx18-streams.h" #include "cx18-cards.h" #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 @@ */ #include "cx18-driver.h" -#include "cx18-streams.h" #include "cx18-queue.h" +#include "cx18-streams.h" #include "cx18-scb.h" 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) cx18_queue_init(&s->q_busy); spin_lock_init(&s->q_full.lock); cx18_queue_init(&s->q_full); + + INIT_WORK(&s->out_work_order, cx18_out_work_handler); } static int cx18_prep_dev(struct cx18 *cx, int type) @@ -477,86 +479,12 @@ void _cx18_stream_load_fw_queue(struct cx18_stream *s) && q == &s->q_busy); } -static inline -void free_out_work_order(struct cx18_out_work_order *order) -{ - atomic_set(&order->pending, 0); -} - void cx18_out_work_handler(struct work_struct *work) { - struct cx18_out_work_order *order = - container_of(work, struct cx18_out_work_order, work); - struct cx18_stream *s = order->s; - struct cx18_buffer *buf = order->buf; - - free_out_work_order(order); - - if (buf == NULL) - _cx18_stream_load_fw_queue(s); - else - _cx18_stream_put_buf_fw(s, buf); -} - -static -struct cx18_out_work_order *alloc_out_work_order(struct cx18 *cx) -{ - int i; - struct cx18_out_work_order *order = NULL; - - for (i = 0; i < CX18_MAX_OUT_WORK_ORDERS; i++) { - /* - * We need "pending" to be atomic to inspect & set its contents - * 1. "pending" is only set to 1 here, but needs multiple access - * protection - * 2. work handler threads only clear "pending" and only - * on one, particular work order at a time, per handler thread. - */ - if (atomic_add_unless(&cx->out_work_order[i].pending, 1, 1)) { - order = &cx->out_work_order[i]; - break; - } - } - return order; -} - -struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, - struct cx18_buffer *buf) -{ - struct cx18 *cx = s->cx; - struct cx18_out_work_order *order; - - order = alloc_out_work_order(cx); - if (order == NULL) { - CX18_DEBUG_WARN("No blank, outgoing-mailbox, deferred-work, " - "order forms available; sending buffer %u back " - "to the firmware immediately for stream %s\n", - buf->id, s->name); - return _cx18_stream_put_buf_fw(s, buf); - } - order->s = s; - order->buf = buf; - queue_work(cx->out_work_queue, &order->work); - return NULL; -} - -void cx18_stream_load_fw_queue(struct cx18_stream *s) -{ - struct cx18 *cx = s->cx; - struct cx18_out_work_order *order; + struct cx18_stream *s = + container_of(work, struct cx18_stream, out_work_order); - order = alloc_out_work_order(cx); - if (order == NULL) { - CX18_DEBUG_WARN("No blank, outgoing-mailbox, deferred-work, " - "order forms available; filling the firmware " - "buffer queue immediately for stream %s\n", - s->name); - _cx18_stream_load_fw_queue(s); - return; - } - order->s = s; - order->buf = NULL; /* Indicates to load the fw queue */ - queue_work(cx->out_work_queue, &order->work); + _cx18_stream_load_fw_queue(s); } 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); void cx18_streams_cleanup(struct cx18 *cx, int unregister); /* Related to submission of buffers to firmware */ -void cx18_stream_load_fw_queue(struct cx18_stream *s); -struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, - struct cx18_buffer *buf); +static inline void cx18_stream_load_fw_queue(struct cx18_stream *s) +{ + struct cx18 *cx = s->cx; + queue_work(cx->out_work_queue, &s->out_work_order); +} + +static inline void cx18_stream_put_buf_fw(struct cx18_stream *s, + struct cx18_buffer *buf) +{ + /* Put buf on q_free; the out work handler will move buf(s) to q_busy */ + cx18_enqueue(s, buf, &s->q_free); + cx18_stream_load_fw_queue(s); +} + void cx18_out_work_handler(struct work_struct *work); /* Capture related */ |