static void cx18_remove(struct pci_dev *pci_dev) { struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); struct cx18 *cx = to_cx18(v4l2_dev); int i; CX18_DEBUG_INFO("Removing Card\n"); /* Stop all captures */ CX18_DEBUG_INFO("Stopping all streams\n"); if (atomic_read(&cx->tot_capturing) > 0) cx18_stop_all_captures(cx); /* Stop interrupts that cause incoming work to be queued */ cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); /* 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 /* 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); destroy_workqueue(cx->in_work_queue); destroy_workqueue(cx->out_work_queue); cx18_streams_cleanup(cx, 1); exit_cx18_i2c(cx); free_irq(cx->pci_dev->irq, (void *)cx); cx18_iounmap(cx); release_mem_region(cx->base_addr, CX18_MEM_SIZE); pci_disable_device(cx->pci_dev); if (cx->vbi.sliced_mpeg_data[0] != NULL) for (i = 0; i < CX18_VBI_FRAMES; i++) kfree(cx->vbi.sliced_mpeg_data[i]); CX18_INFO("Removed %s\n", cx->card_name); v4l2_device_unregister(v4l2_dev); kfree(cx); }
static void cx18_remove(struct pci_dev *pci_dev) { struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); struct cx18 *cx = to_cx18(v4l2_dev); int i; CX18_DEBUG_INFO("Removing Card\n"); CX18_DEBUG_INFO("Stopping all streams\n"); if (atomic_read(&cx->tot_capturing) > 0) cx18_stop_all_captures(cx); cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); cx18_cancel_in_work_orders(cx); cx18_cancel_out_work_orders(cx); cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); cx18_halt_firmware(cx); destroy_workqueue(cx->in_work_queue); destroy_workqueue(cx->out_work_queue); cx18_streams_cleanup(cx, 1); exit_cx18_i2c(cx); free_irq(cx->pci_dev->irq, (void *)cx); cx18_iounmap(cx); release_mem_region(cx->base_addr, CX18_MEM_SIZE); pci_disable_device(cx->pci_dev); if (cx->vbi.sliced_mpeg_data[0] != NULL) for (i = 0; i < CX18_VBI_FRAMES; i++) kfree(cx->vbi.sliced_mpeg_data[i]); CX18_INFO("Removed %s\n", cx->card_name); v4l2_device_unregister(v4l2_dev); kfree(cx); }
static int __devinit cx18_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) { int retval = 0; int vbi_buf_size; u32 devtype; struct cx18 *cx; spin_lock(&cx18_cards_lock); /* Make sure we've got a place for this card */ if (cx18_cards_active == CX18_MAX_CARDS) { printk(KERN_ERR "cx18: Maximum number of cards detected (%d).\n", cx18_cards_active); spin_unlock(&cx18_cards_lock); return -ENOMEM; } cx = kzalloc(sizeof(struct cx18), GFP_ATOMIC); if (!cx) { spin_unlock(&cx18_cards_lock); return -ENOMEM; } cx18_cards[cx18_cards_active] = cx; cx->dev = dev; cx->num = cx18_cards_active++; snprintf(cx->name, sizeof(cx->name), "cx18-%d", cx->num); CX18_INFO("Initializing card #%d\n", cx->num); spin_unlock(&cx18_cards_lock); cx18_process_options(cx); if (cx->options.cardtype == -1) { retval = -ENODEV; goto err; } if (cx18_init_struct1(cx)) { retval = -ENOMEM; goto err; } CX18_DEBUG_INFO("base addr: 0x%08x\n", cx->base_addr); /* PCI Device Setup */ retval = cx18_setup_pci(cx, dev, pci_id); if (retval != 0) { if (retval == -EIO) goto free_workqueue; else if (retval == -ENXIO) goto free_mem; } /* save cx in the pci struct for later use */ pci_set_drvdata(dev, cx); /* map io memory */ CX18_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", cx->base_addr + CX18_MEM_OFFSET, CX18_MEM_SIZE); cx->enc_mem = ioremap_nocache(cx->base_addr + CX18_MEM_OFFSET, CX18_MEM_SIZE); if (!cx->enc_mem) { CX18_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n"); CX18_ERR("or disabling CONFIG_HIGHMEM4G into the kernel would help\n"); retval = -ENOMEM; goto free_mem; } cx->reg_mem = cx->enc_mem + CX18_REG_OFFSET; devtype = read_reg(0xC72028); switch (devtype & 0xff000000) { case 0xff000000: CX18_INFO("cx23418 revision %08x (A)\n", devtype); break; case 0x01000000: CX18_INFO("cx23418 revision %08x (B)\n", devtype); break; default: CX18_INFO("cx23418 revision %08x (Unknown)\n", devtype); break; } cx18_init_power(cx, 1); cx18_init_memory(cx); cx->scb = (struct cx18_scb __iomem *)(cx->enc_mem + SCB_OFFSET); cx18_init_scb(cx); cx18_gpio_init(cx); /* active i2c */ CX18_DEBUG_INFO("activating i2c...\n"); if (init_cx18_i2c(cx)) { CX18_ERR("Could not initialize i2c\n"); goto free_map; } CX18_DEBUG_INFO("Active card count: %d.\n", cx18_cards_active); if (cx->card->hw_all & CX18_HW_TVEEPROM) { /* Based on the model number the cardtype may be changed. The PCI IDs are not always reliable. */ cx18_process_eeprom(cx); } if (cx->card->comment) CX18_INFO("%s", cx->card->comment); if (cx->card->v4l2_capabilities == 0) { retval = -ENODEV; goto free_i2c; } cx18_init_memory(cx); /* Register IRQ */ retval = request_irq(cx->dev->irq, cx18_irq_handler, IRQF_SHARED | IRQF_DISABLED, cx->name, (void *)cx); if (retval) { CX18_ERR("Failed to register irq %d\n", retval); goto free_i2c; } if (cx->std == 0) cx->std = V4L2_STD_NTSC_M; if (cx->options.tuner == -1) { int i; for (i = 0; i < CX18_CARD_MAX_TUNERS; i++) { if ((cx->std & cx->card->tuners[i].std) == 0) continue; cx->options.tuner = cx->card->tuners[i].tuner; break; } } /* if no tuner was found, then pick the first tuner in the card list */ if (cx->options.tuner == -1 && cx->card->tuners[0].std) { cx->std = cx->card->tuners[0].std; if (cx->std & V4L2_STD_PAL) cx->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H; else if (cx->std & V4L2_STD_NTSC) cx->std = V4L2_STD_NTSC_M; else if (cx->std & V4L2_STD_SECAM) cx->std = V4L2_STD_SECAM_L; cx->options.tuner = cx->card->tuners[0].tuner; } if (cx->options.radio == -1) cx->options.radio = (cx->card->radio_input.audio_type != 0); /* The card is now fully identified, continue with card-specific initialization. */ cx18_init_struct2(cx); cx18_load_and_init_modules(cx); if (cx->std & V4L2_STD_525_60) { cx->is_60hz = 1; cx->is_out_60hz = 1; } else { cx->is_50hz = 1; cx->is_out_50hz = 1; } cx->params.video_gop_size = cx->is_60hz ? 15 : 12; cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = 0x08000; cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = 0x08000; cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = 0x01200; cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = 0x20000; vbi_buf_size = cx->vbi.raw_size * (cx->is_60hz ? 24 : 36) / 2; cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_buf_size; if (cx->options.radio > 0) cx->v4l2_cap |= V4L2_CAP_RADIO; if (cx->options.tuner > -1) { struct tuner_setup setup; setup.addr = ADDR_UNSET; setup.type = cx->options.tuner; setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */ setup.tuner_callback = (setup.type == TUNER_XC2028) ? cx18_reset_tuner_gpio : NULL; cx18_call_i2c_clients(cx, TUNER_SET_TYPE_ADDR, &setup); if (setup.type == TUNER_XC2028) { static struct xc2028_ctrl ctrl = { .fname = XC2028_DEFAULT_FIRMWARE, .max_len = 64, }; struct v4l2_priv_tun_config cfg = { .tuner = cx->options.tuner, .priv = &ctrl, }; cx18_call_i2c_clients(cx, TUNER_SET_CONFIG, &cfg); } } /* The tuner is fixed to the standard. The other inputs (e.g. S-Video) are not. */ cx->tuner_std = cx->std; retval = cx18_streams_setup(cx); if (retval) { CX18_ERR("Error %d setting up streams\n", retval); goto free_irq; } retval = cx18_streams_register(cx); if (retval) { CX18_ERR("Error %d registering devices\n", retval); goto free_streams; } CX18_INFO("Initialized card #%d: %s\n", cx->num, cx->card_name); return 0; free_streams: cx18_streams_cleanup(cx, 1); free_irq: free_irq(cx->dev->irq, (void *)cx); free_i2c: exit_cx18_i2c(cx); free_map: cx18_iounmap(cx); free_mem: release_mem_region(cx->base_addr, CX18_MEM_SIZE); free_workqueue: err: if (retval == 0) retval = -ENODEV; CX18_ERR("Error %d on initialization\n", retval); kfree(cx18_cards[cx18_cards_active]); cx18_cards[cx18_cards_active] = NULL; return retval; } int cx18_init_on_first_open(struct cx18 *cx) { int video_input; int fw_retry_count = 3; struct v4l2_frequency vf; struct cx18_open_id fh; fh.cx = cx; if (test_bit(CX18_F_I_FAILED, &cx->i_flags)) return -ENXIO; if (test_and_set_bit(CX18_F_I_INITED, &cx->i_flags)) return 0; while (--fw_retry_count > 0) { /* load firmware */ if (cx18_firmware_init(cx) == 0) break; if (fw_retry_count > 1) CX18_WARN("Retry loading firmware\n"); } if (fw_retry_count == 0) { set_bit(CX18_F_I_FAILED, &cx->i_flags); return -ENXIO; } set_bit(CX18_F_I_LOADED_FW, &cx->i_flags); /* Init the firmware twice to work around a silicon bug * transport related. */ fw_retry_count = 3; while (--fw_retry_count > 0) { /* load firmware */ if (cx18_firmware_init(cx) == 0) break; if (fw_retry_count > 1) CX18_WARN("Retry loading firmware\n"); } if (fw_retry_count == 0) { set_bit(CX18_F_I_FAILED, &cx->i_flags); return -ENXIO; } vf.tuner = 0; vf.type = V4L2_TUNER_ANALOG_TV; vf.frequency = 6400; /* the tuner 'baseline' frequency */ /* Set initial frequency. For PAL/SECAM broadcasts no 'default' channel exists AFAIK. */ if (cx->std == V4L2_STD_NTSC_M_JP) vf.frequency = 1460; /* ch. 1 91250*16/1000 */ else if (cx->std & V4L2_STD_NTSC_M) vf.frequency = 1076; /* ch. 4 67250*16/1000 */ video_input = cx->active_input; cx->active_input++; /* Force update of input */ cx18_s_input(NULL, &fh, video_input); /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code in one place. */ cx->std++; /* Force full standard initialization */ cx18_s_std(NULL, &fh, &cx->tuner_std); cx18_s_frequency(NULL, &fh, &vf); return 0; } static void cx18_remove(struct pci_dev *pci_dev) { struct cx18 *cx = pci_get_drvdata(pci_dev); CX18_DEBUG_INFO("Removing Card #%d\n", cx->num); /* Stop all captures */ CX18_DEBUG_INFO("Stopping all streams\n"); if (atomic_read(&cx->tot_capturing) > 0) cx18_stop_all_captures(cx); /* Interrupts */ sw1_irq_disable(IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); sw2_irq_disable(IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); cx18_halt_firmware(cx); cx18_streams_cleanup(cx, 1); exit_cx18_i2c(cx); free_irq(cx->dev->irq, (void *)cx); cx18_iounmap(cx); release_mem_region(cx->base_addr, CX18_MEM_SIZE); pci_disable_device(cx->dev); CX18_INFO("Removed %s, card #%d\n", cx->card_name, cx->num); } /* define a pci_driver for card detection */ static struct pci_driver cx18_pci_driver = { .name = "cx18", .id_table = cx18_pci_tbl, .probe = cx18_probe, .remove = cx18_remove, }; static int module_start(void) { printk(KERN_INFO "cx18: Start initialization, version %s\n", CX18_VERSION); memset(cx18_cards, 0, sizeof(cx18_cards)); /* Validate parameters */ if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) { printk(KERN_ERR "cx18: Exiting, ivtv_first_minor must be between 0 and %d\n", CX18_MAX_CARDS - 1); return -1; } if (cx18_debug < 0 || cx18_debug > 511) { cx18_debug = 0; printk(KERN_INFO "cx18: Debug value must be >= 0 and <= 511!\n"); } if (pci_register_driver(&cx18_pci_driver)) { printk(KERN_ERR "cx18: Error detecting PCI card\n"); return -ENODEV; } printk(KERN_INFO "cx18: End initialization\n"); return 0; } static void module_cleanup(void) { int i; pci_unregister_driver(&cx18_pci_driver); for (i = 0; i < cx18_cards_active; i++) { if (cx18_cards[i] == NULL) continue; kfree(cx18_cards[i]); } }