diff options
Diffstat (limited to 'drivers/media/video/cx18/cx18-driver.c')
-rw-r--r-- | drivers/media/video/cx18/cx18-driver.c | 100 |
1 files changed, 75 insertions, 25 deletions
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 49b1c3d7b1a8..92026e82e10e 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include "cx18-irq.h" | 30 | #include "cx18-irq.h" |
31 | #include "cx18-gpio.h" | 31 | #include "cx18-gpio.h" |
32 | #include "cx18-firmware.h" | 32 | #include "cx18-firmware.h" |
33 | #include "cx18-queue.h" | ||
33 | #include "cx18-streams.h" | 34 | #include "cx18-streams.h" |
34 | #include "cx18-av-core.h" | 35 | #include "cx18-av-core.h" |
35 | #include "cx18-scb.h" | 36 | #include "cx18-scb.h" |
@@ -151,7 +152,8 @@ MODULE_PARM_DESC(cardtype, | |||
151 | "\t\t\t 4 = Yuan MPC718\n" | 152 | "\t\t\t 4 = Yuan MPC718\n" |
152 | "\t\t\t 5 = Conexant Raptor PAL/SECAM\n" | 153 | "\t\t\t 5 = Conexant Raptor PAL/SECAM\n" |
153 | "\t\t\t 6 = Toshiba Qosmio DVB-T/Analog\n" | 154 | "\t\t\t 6 = Toshiba Qosmio DVB-T/Analog\n" |
154 | "\t\t\t 7 = Leadtek WinFast PVR2100/DVR3100 H\n" | 155 | "\t\t\t 7 = Leadtek WinFast PVR2100\n" |
156 | "\t\t\t 8 = Leadtek WinFast DVR3100 H\n" | ||
155 | "\t\t\t 0 = Autodetect (default)\n" | 157 | "\t\t\t 0 = Autodetect (default)\n" |
156 | "\t\t\t-1 = Ignore this card\n\t\t"); | 158 | "\t\t\t-1 = Ignore this card\n\t\t"); |
157 | MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60"); | 159 | MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60"); |
@@ -312,7 +314,7 @@ static void cx18_process_eeprom(struct cx18 *cx) | |||
312 | CX18_INFO("Autodetected %s\n", cx->card_name); | 314 | CX18_INFO("Autodetected %s\n", cx->card_name); |
313 | 315 | ||
314 | if (tv.tuner_type == TUNER_ABSENT) | 316 | if (tv.tuner_type == TUNER_ABSENT) |
315 | CX18_ERR("tveeprom cannot autodetect tuner!"); | 317 | CX18_ERR("tveeprom cannot autodetect tuner!\n"); |
316 | 318 | ||
317 | if (cx->options.tuner == -1) | 319 | if (cx->options.tuner == -1) |
318 | cx->options.tuner = tv.tuner_type; | 320 | cx->options.tuner = tv.tuner_type; |
@@ -546,6 +548,40 @@ done: | |||
546 | cx->card_i2c = cx->card->i2c; | 548 | cx->card_i2c = cx->card->i2c; |
547 | } | 549 | } |
548 | 550 | ||
551 | static int __devinit cx18_create_in_workq(struct cx18 *cx) | ||
552 | { | ||
553 | snprintf(cx->in_workq_name, sizeof(cx->in_workq_name), "%s-in", | ||
554 | cx->v4l2_dev.name); | ||
555 | cx->in_work_queue = create_singlethread_workqueue(cx->in_workq_name); | ||
556 | if (cx->in_work_queue == NULL) { | ||
557 | CX18_ERR("Unable to create incoming mailbox handler thread\n"); | ||
558 | return -ENOMEM; | ||
559 | } | ||
560 | return 0; | ||
561 | } | ||
562 | |||
563 | static int __devinit cx18_create_out_workq(struct cx18 *cx) | ||
564 | { | ||
565 | snprintf(cx->out_workq_name, sizeof(cx->out_workq_name), "%s-out", | ||
566 | cx->v4l2_dev.name); | ||
567 | cx->out_work_queue = create_workqueue(cx->out_workq_name); | ||
568 | if (cx->out_work_queue == NULL) { | ||
569 | CX18_ERR("Unable to create outgoing mailbox handler threads\n"); | ||
570 | return -ENOMEM; | ||
571 | } | ||
572 | return 0; | ||
573 | } | ||
574 | |||
575 | static void __devinit cx18_init_in_work_orders(struct cx18 *cx) | ||
576 | { | ||
577 | int i; | ||
578 | for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) { | ||
579 | cx->in_work_order[i].cx = cx; | ||
580 | cx->in_work_order[i].str = cx->epu_debug_str; | ||
581 | INIT_WORK(&cx->in_work_order[i].work, cx18_in_work_handler); | ||
582 | } | ||
583 | } | ||
584 | |||
549 | /* Precondition: the cx18 structure has been memset to 0. Only | 585 | /* Precondition: the cx18 structure has been memset to 0. Only |
550 | the dev and instance fields have been filled in. | 586 | the dev and instance fields have been filled in. |
551 | No assumptions on the card type may be made here (see cx18_init_struct2 | 587 | No assumptions on the card type may be made here (see cx18_init_struct2 |
@@ -553,7 +589,7 @@ done: | |||
553 | */ | 589 | */ |
554 | static int __devinit cx18_init_struct1(struct cx18 *cx) | 590 | static int __devinit cx18_init_struct1(struct cx18 *cx) |
555 | { | 591 | { |
556 | int i; | 592 | int ret; |
557 | 593 | ||
558 | cx->base_addr = pci_resource_start(cx->pci_dev, 0); | 594 | cx->base_addr = pci_resource_start(cx->pci_dev, 0); |
559 | 595 | ||
@@ -562,18 +598,18 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) | |||
562 | mutex_init(&cx->epu2apu_mb_lock); | 598 | mutex_init(&cx->epu2apu_mb_lock); |
563 | mutex_init(&cx->epu2cpu_mb_lock); | 599 | mutex_init(&cx->epu2cpu_mb_lock); |
564 | 600 | ||
565 | cx->work_queue = create_singlethread_workqueue(cx->v4l2_dev.name); | 601 | ret = cx18_create_out_workq(cx); |
566 | if (cx->work_queue == NULL) { | 602 | if (ret) |
567 | CX18_ERR("Unable to create work hander thread\n"); | 603 | return ret; |
568 | return -ENOMEM; | ||
569 | } | ||
570 | 604 | ||
571 | for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) { | 605 | ret = cx18_create_in_workq(cx); |
572 | cx->epu_work_order[i].cx = cx; | 606 | if (ret) { |
573 | cx->epu_work_order[i].str = cx->epu_debug_str; | 607 | destroy_workqueue(cx->out_work_queue); |
574 | INIT_WORK(&cx->epu_work_order[i].work, cx18_epu_work_handler); | 608 | return ret; |
575 | } | 609 | } |
576 | 610 | ||
611 | cx18_init_in_work_orders(cx); | ||
612 | |||
577 | /* start counting open_id at 1 */ | 613 | /* start counting open_id at 1 */ |
578 | cx->open_id = 1; | 614 | cx->open_id = 1; |
579 | 615 | ||
@@ -759,17 +795,17 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev, | |||
759 | retval = -ENODEV; | 795 | retval = -ENODEV; |
760 | goto err; | 796 | goto err; |
761 | } | 797 | } |
762 | if (cx18_init_struct1(cx)) { | 798 | |
763 | retval = -ENOMEM; | 799 | retval = cx18_init_struct1(cx); |
800 | if (retval) | ||
764 | goto err; | 801 | goto err; |
765 | } | ||
766 | 802 | ||
767 | CX18_DEBUG_INFO("base addr: 0x%08x\n", cx->base_addr); | 803 | CX18_DEBUG_INFO("base addr: 0x%08x\n", cx->base_addr); |
768 | 804 | ||
769 | /* PCI Device Setup */ | 805 | /* PCI Device Setup */ |
770 | retval = cx18_setup_pci(cx, pci_dev, pci_id); | 806 | retval = cx18_setup_pci(cx, pci_dev, pci_id); |
771 | if (retval != 0) | 807 | if (retval != 0) |
772 | goto free_workqueue; | 808 | goto free_workqueues; |
773 | 809 | ||
774 | /* map io memory */ | 810 | /* map io memory */ |
775 | CX18_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", | 811 | CX18_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", |
@@ -943,8 +979,9 @@ free_map: | |||
943 | cx18_iounmap(cx); | 979 | cx18_iounmap(cx); |
944 | free_mem: | 980 | free_mem: |
945 | release_mem_region(cx->base_addr, CX18_MEM_SIZE); | 981 | release_mem_region(cx->base_addr, CX18_MEM_SIZE); |
946 | free_workqueue: | 982 | free_workqueues: |
947 | destroy_workqueue(cx->work_queue); | 983 | destroy_workqueue(cx->in_work_queue); |
984 | destroy_workqueue(cx->out_work_queue); | ||
948 | err: | 985 | err: |
949 | if (retval == 0) | 986 | if (retval == 0) |
950 | retval = -ENODEV; | 987 | retval = -ENODEV; |
@@ -1053,11 +1090,19 @@ int cx18_init_on_first_open(struct cx18 *cx) | |||
1053 | return 0; | 1090 | return 0; |
1054 | } | 1091 | } |
1055 | 1092 | ||
1056 | static void cx18_cancel_epu_work_orders(struct cx18 *cx) | 1093 | static void cx18_cancel_in_work_orders(struct cx18 *cx) |
1057 | { | 1094 | { |
1058 | int i; | 1095 | int i; |
1059 | for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) | 1096 | for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) |
1060 | cancel_work_sync(&cx->epu_work_order[i].work); | 1097 | cancel_work_sync(&cx->in_work_order[i].work); |
1098 | } | ||
1099 | |||
1100 | static void cx18_cancel_out_work_orders(struct cx18 *cx) | ||
1101 | { | ||
1102 | int i; | ||
1103 | for (i = 0; i < CX18_MAX_STREAMS; i++) | ||
1104 | if (&cx->streams[i].video_dev != NULL) | ||
1105 | cancel_work_sync(&cx->streams[i].out_work_order); | ||
1061 | } | 1106 | } |
1062 | 1107 | ||
1063 | static void cx18_remove(struct pci_dev *pci_dev) | 1108 | static void cx18_remove(struct pci_dev *pci_dev) |
@@ -1073,15 +1118,20 @@ static void cx18_remove(struct pci_dev *pci_dev) | |||
1073 | if (atomic_read(&cx->tot_capturing) > 0) | 1118 | if (atomic_read(&cx->tot_capturing) > 0) |
1074 | cx18_stop_all_captures(cx); | 1119 | cx18_stop_all_captures(cx); |
1075 | 1120 | ||
1076 | /* Interrupts */ | 1121 | /* Stop interrupts that cause incoming work to be queued */ |
1077 | cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); | 1122 | cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); |
1123 | |||
1124 | /* Incoming work can cause outgoing work, so clean up incoming first */ | ||
1125 | cx18_cancel_in_work_orders(cx); | ||
1126 | cx18_cancel_out_work_orders(cx); | ||
1127 | |||
1128 | /* Stop ack interrupts that may have been needed for work to finish */ | ||
1078 | cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); | 1129 | cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); |
1079 | 1130 | ||
1080 | cx18_halt_firmware(cx); | 1131 | cx18_halt_firmware(cx); |
1081 | 1132 | ||
1082 | cx18_cancel_epu_work_orders(cx); | 1133 | destroy_workqueue(cx->in_work_queue); |
1083 | 1134 | destroy_workqueue(cx->out_work_queue); | |
1084 | destroy_workqueue(cx->work_queue); | ||
1085 | 1135 | ||
1086 | cx18_streams_cleanup(cx, 1); | 1136 | cx18_streams_cleanup(cx, 1); |
1087 | 1137 | ||