summaryrefslogtreecommitdiff
path: root/linux/drivers/media/video/cx18/cx18-driver.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux/drivers/media/video/cx18/cx18-driver.c')
-rw-r--r--linux/drivers/media/video/cx18/cx18-driver.c163
1 files changed, 123 insertions, 40 deletions
diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c
index f688ec7db..ae13a1546 100644
--- a/linux/drivers/media/video/cx18/cx18-driver.c
+++ b/linux/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"
@@ -151,7 +152,8 @@ MODULE_PARM_DESC(cardtype,
"\t\t\t 4 = Yuan MPC718\n"
"\t\t\t 5 = Conexant Raptor PAL/SECAM\n"
"\t\t\t 6 = Toshiba Qosmio DVB-T/Analog\n"
- "\t\t\t 7 = Leadtek WinFast PVR2100/DVR3100 H\n"
+ "\t\t\t 7 = Leadtek WinFast PVR2100\n"
+ "\t\t\t 8 = Leadtek WinFast DVR3100 H\n"
"\t\t\t 0 = Autodetect (default)\n"
"\t\t\t-1 = Ignore this card\n\t\t");
MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60");
@@ -266,6 +268,20 @@ static void cx18_iounmap(struct cx18 *cx)
}
}
+static void cx18_eeprom_dump(struct cx18 *cx, unsigned char *eedata, int len)
+{
+ int i;
+
+ CX18_INFO("eeprom dump:\n");
+ for (i = 0; i < len; i++) {
+ if (0 == (i % 16))
+ CX18_INFO("eeprom %02x:", i);
+ printk(KERN_CONT " %02x", eedata[i]);
+ if (15 == (i % 16))
+ printk(KERN_CONT "\n");
+ }
+}
+
/* Hauppauge card? get values from tveeprom */
void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv)
{
@@ -277,8 +293,26 @@ void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv)
c.adapter = &cx->i2c_adap[0];
c.addr = 0xA0 >> 1;
- tveeprom_read(&c, eedata, sizeof(eedata));
- tveeprom_hauppauge_analog(&c, tv, eedata);
+ memset(tv, 0, sizeof(*tv));
+ if (tveeprom_read(&c, eedata, sizeof(eedata)))
+ return;
+
+ switch (cx->card->type) {
+ case CX18_CARD_HVR_1600_ESMT:
+ case CX18_CARD_HVR_1600_SAMSUNG:
+ tveeprom_hauppauge_analog(&c, tv, eedata);
+ break;
+ case CX18_CARD_YUAN_MPC718:
+ tv->model = 0x718;
+ cx18_eeprom_dump(cx, eedata, sizeof(eedata));
+ CX18_INFO("eeprom PCI ID: %02x%02x:%02x%02x\n",
+ eedata[2], eedata[1], eedata[4], eedata[3]);
+ break;
+ default:
+ tv->model = 0xffffffff;
+ cx18_eeprom_dump(cx, eedata, sizeof(eedata));
+ break;
+ }
}
static void cx18_process_eeprom(struct cx18 *cx)
@@ -296,6 +330,11 @@ static void cx18_process_eeprom(struct cx18 *cx)
case 74000 ... 74999:
cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
break;
+ case 0x718:
+ return;
+ case 0xffffffff:
+ CX18_INFO("Unknown EEPROM encoding\n");
+ return;
case 0:
CX18_ERR("Invalid EEPROM\n");
return;
@@ -312,7 +351,7 @@ static void cx18_process_eeprom(struct cx18 *cx)
CX18_INFO("Autodetected %s\n", cx->card_name);
if (tv.tuner_type == TUNER_ABSENT)
- CX18_ERR("tveeprom cannot autodetect tuner!");
+ CX18_ERR("tveeprom cannot autodetect tuner!\n");
if (cx->options.tuner == -1)
cx->options.tuner = tv.tuner_type;
@@ -546,6 +585,45 @@ done:
cx->card_i2c = cx->card->i2c;
}
+static int __devinit cx18_create_in_workq(struct cx18 *cx)
+{
+ 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);
+ if (cx->in_work_queue == NULL) {
+ 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;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
+ INIT_WORK(&cx->in_work_order[i].work, cx18_in_work_handler);
+#else
+ INIT_WORK(&cx->in_work_order[i].work, cx18_in_work_handler,
+ &cx->in_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
@@ -553,7 +631,7 @@ done:
*/
static int __devinit cx18_init_struct1(struct cx18 *cx)
{
- int i;
+ int ret;
cx->base_addr = pci_resource_start(cx->pci_dev, 0);
@@ -562,23 +640,18 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
mutex_init(&cx->epu2apu_mb_lock);
mutex_init(&cx->epu2cpu_mb_lock);
- cx->work_queue = create_singlethread_workqueue(cx->v4l2_dev.name);
- if (cx->work_queue == NULL) {
- CX18_ERR("Unable to create work hander thread\n");
- return -ENOMEM;
- }
+ ret = cx18_create_out_workq(cx);
+ if (ret)
+ return ret;
- for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) {
- cx->epu_work_order[i].cx = cx;
- cx->epu_work_order[i].str = cx->epu_debug_str;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
- INIT_WORK(&cx->epu_work_order[i].work, cx18_epu_work_handler);
-#else
- INIT_WORK(&cx->epu_work_order[i].work, cx18_epu_work_handler,
- &cx->epu_work_order[i].work);
-#endif
+ ret = cx18_create_in_workq(cx);
+ if (ret) {
+ destroy_workqueue(cx->out_work_queue);
+ return ret;
}
+ cx18_init_in_work_orders(cx);
+
/* start counting open_id at 1 */
cx->open_id = 1;
@@ -764,17 +837,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",
@@ -815,7 +888,7 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
CX18_ERR("Could not register A/V decoder subdevice\n");
goto free_map;
}
- cx18_call_hw(cx, CX18_HW_418_AV, core, init, (u32) CX18_AV_INIT_PLLS);
+ cx18_call_hw(cx, CX18_HW_418_AV, core, init, 0);
/* Initialize GPIO Reset Controller to do chip resets during i2c init */
if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) {
@@ -948,8 +1021,9 @@ free_map:
cx18_iounmap(cx);
free_mem:
release_mem_region(cx->base_addr, CX18_MEM_SIZE);
-free_workqueue:
- destroy_workqueue(cx->work_queue);
+free_workqueues:
+ destroy_workqueue(cx->in_work_queue);
+ destroy_workqueue(cx->out_work_queue);
err:
if (retval == 0)
retval = -ENODEV;
@@ -1033,7 +1107,7 @@ int cx18_init_on_first_open(struct cx18 *cx)
cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG);
/* Init the A/V decoder, if it hasn't been already */
- v4l2_subdev_call(cx->sd_av, core, init, (u32) CX18_AV_INIT_NORMAL);
+ v4l2_subdev_call(cx->sd_av, core, load_fw);
vf.tuner = 0;
vf.type = V4L2_TUNER_ANALOG_TV;
@@ -1059,11 +1133,19 @@ int cx18_init_on_first_open(struct cx18 *cx)
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
-static void cx18_cancel_epu_work_orders(struct cx18 *cx)
+static void cx18_cancel_in_work_orders(struct cx18 *cx)
{
int i;
- for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++)
- cancel_work_sync(&cx->epu_work_order[i].work);
+ for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++)
+ 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);
}
#endif
@@ -1080,24 +1162,25 @@ 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);
- cx18_cancel_epu_work_orders(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);
+ cx18_cancel_out_work_orders(cx);
#else
+ flush_workqueue(cx->in_work_queue);
+ flush_workqueue(cx->out_work_queue);
+#endif
- flush_workqueue(cx->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->work_queue);
+ destroy_workqueue(cx->in_work_queue);
+ destroy_workqueue(cx->out_work_queue);
cx18_streams_cleanup(cx, 1);