diff options
author | Andy Walls <awalls@radix.net> | 2009-04-13 21:42:43 -0400 |
---|---|---|
committer | Andy Walls <awalls@radix.net> | 2009-04-13 21:42:43 -0400 |
commit | 30acb038b4822df9ff73cd379a9144314a194d94 (patch) | |
tree | 664321b15865bed9b690b1a4cceb0034fe811bc9 /linux/drivers/media/video/cx18/cx18-driver.c | |
parent | dc85976d603c70ff9a78f6a02c1c412b87fd50d6 (diff) | |
download | mediapointer-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.c | 106 |
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); |