summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 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 */