summaryrefslogtreecommitdiff
path: root/linux/drivers/media/video/cx18/cx18-driver.c
diff options
context:
space:
mode:
authorAndy Walls <awalls@radix.net>2009-04-13 21:42:43 -0400
committerAndy Walls <awalls@radix.net>2009-04-13 21:42:43 -0400
commit30acb038b4822df9ff73cd379a9144314a194d94 (patch)
tree664321b15865bed9b690b1a4cceb0034fe811bc9 /linux/drivers/media/video/cx18/cx18-driver.c
parentdc85976d603c70ff9a78f6a02c1c412b87fd50d6 (diff)
downloadmediapointer-dvb-s2-30acb038b4822df9ff73cd379a9144314a194d94.tar.gz
mediapointer-dvb-s2-30acb038b4822df9ff73cd379a9144314a194d94.tar.bz2
cx18: Add a work queue for deferring empty buffer handoffs to the firmware
From: Andy Walls <awalls@radix.net> This change defers sending all CX18_CPU_DE_SET_MDL commands, for a stream with an ongoing capture, by adding a work queue to handle sending such commands when needed. This prevents any sleeps, caused by notifying the firmware of new usable buffers, when a V4L2 application read() is being satisfied or when an incoming buffer is processed by the cx18-NN-in work queue thread. Priority: normal Signed-off-by: Andy Walls <awalls@radix.net>
Diffstat (limited to 'linux/drivers/media/video/cx18/cx18-driver.c')
-rw-r--r--linux/drivers/media/video/cx18/cx18-driver.c106
1 files changed, 79 insertions, 27 deletions
diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c
index ca6f1b555..db226f412 100644
--- a/linux/drivers/media/video/cx18/cx18-driver.c
+++ b/linux/drivers/media/video/cx18/cx18-driver.c
@@ -546,22 +546,8 @@ done:
cx->card_i2c = cx->card->i2c;
}
-/* 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
- for that).
- */
-static int __devinit cx18_init_struct1(struct cx18 *cx)
+static int __devinit cx18_create_in_workq(struct cx18 *cx)
{
- int i;
-
- cx->base_addr = pci_resource_start(cx->pci_dev, 0);
-
- mutex_init(&cx->serialize_lock);
- mutex_init(&cx->gpio_lock);
- mutex_init(&cx->epu2apu_mb_lock);
- mutex_init(&cx->epu2cpu_mb_lock);
-
snprintf(cx->in_workq_name, sizeof(cx->in_workq_name), "%s-in",
cx->v4l2_dev.name);
cx->in_work_queue = create_singlethread_workqueue(cx->in_workq_name);
@@ -569,7 +555,24 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
CX18_ERR("Unable to create incoming mailbox handler thread\n");
return -ENOMEM;
}
+ return 0;
+}
+
+static int __devinit cx18_create_out_workq(struct cx18 *cx)
+{
+ snprintf(cx->out_workq_name, sizeof(cx->out_workq_name), "%s-out",
+ cx->v4l2_dev.name);
+ cx->out_work_queue = create_workqueue(cx->out_workq_name);
+ if (cx->out_work_queue == NULL) {
+ CX18_ERR("Unable to create outgoing mailbox handler threads\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+static void __devinit cx18_init_in_work_orders(struct cx18 *cx)
+{
+ int i;
for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) {
cx->in_work_order[i].cx = cx;
cx->in_work_order[i].str = cx->epu_debug_str;
@@ -580,6 +583,48 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
&cx->in_work_order[i].work);
#endif
}
+}
+
+static void __devinit cx18_init_out_work_orders(struct cx18 *cx)
+{
+ int i;
+ for (i = 0; i < CX18_MAX_OUT_WORK_ORDERS; i++)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
+ INIT_WORK(&cx->out_work_order[i].work, cx18_out_work_handler);
+#else
+ INIT_WORK(&cx->out_work_order[i].work, cx18_out_work_handler,
+ &cx->out_work_order[i].work);
+#endif
+}
+
+/* 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
+ for that).
+ */
+static int __devinit cx18_init_struct1(struct cx18 *cx)
+{
+ int ret;
+
+ cx->base_addr = pci_resource_start(cx->pci_dev, 0);
+
+ mutex_init(&cx->serialize_lock);
+ mutex_init(&cx->gpio_lock);
+ mutex_init(&cx->epu2apu_mb_lock);
+ mutex_init(&cx->epu2cpu_mb_lock);
+
+ ret = cx18_create_out_workq(cx);
+ if (ret)
+ return ret;
+
+ ret = cx18_create_in_workq(cx);
+ if (ret) {
+ destroy_workqueue(cx->out_work_queue);
+ return ret;
+ }
+
+ cx18_init_out_work_orders(cx);
+ cx18_init_in_work_orders(cx);
/* start counting open_id at 1 */
cx->open_id = 1;
@@ -766,17 +811,17 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
retval = -ENODEV;
goto err;
}
- if (cx18_init_struct1(cx)) {
- retval = -ENOMEM;
+
+ retval = cx18_init_struct1(cx);
+ if (retval)
goto err;
- }
CX18_DEBUG_INFO("base addr: 0x%08x\n", cx->base_addr);
/* PCI Device Setup */
retval = cx18_setup_pci(cx, pci_dev, pci_id);
if (retval != 0)
- goto free_workqueue;
+ goto free_workqueues;
/* map io memory */
CX18_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
@@ -950,8 +995,9 @@ free_map:
cx18_iounmap(cx);
free_mem:
release_mem_region(cx->base_addr, CX18_MEM_SIZE);
-free_workqueue:
+free_workqueues:
destroy_workqueue(cx->in_work_queue);
+ destroy_workqueue(cx->out_work_queue);
err:
if (retval == 0)
retval = -ENODEV;
@@ -1082,24 +1128,30 @@ static void cx18_remove(struct pci_dev *pci_dev)
if (atomic_read(&cx->tot_capturing) > 0)
cx18_stop_all_captures(cx);
- /* Interrupts */
+ /* Stop interrupts that cause incoming work to be queued */
cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
- cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
-
- cx18_halt_firmware(cx);
+ /* Incoming work can cause outgoing work, so clean up incoming first */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
cx18_cancel_in_work_orders(cx);
#else
-
flush_workqueue(cx->in_work_queue);
+#endif
+ /*
+ * 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);
+
+ /* 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);
cx18_halt_firmware(cx);
-#endif
destroy_workqueue(cx->in_work_queue);
+ destroy_workqueue(cx->out_work_queue);
cx18_streams_cleanup(cx, 1);