コード例 #1
0
ファイル: cx18-driver.c プロジェクト: OpenStbV4l-dvb/v4l-dvb
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);
}
コード例 #2
0
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);
}
コード例 #3
0
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]);
	}
}