static void cx18_dualwatch(struct cx18 *cx) { struct v4l2_tuner vt; u16 new_bitmap; u16 new_stereo_mode; const u16 stereo_mask = 0x0300; const u16 dual = 0x0200; new_stereo_mode = cx->params.audio_properties & stereo_mask; memset(&vt, 0, sizeof(vt)); cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, &vt); if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2)) new_stereo_mode = dual; if (new_stereo_mode == cx->dualwatch_stereo_mode) return; new_bitmap = new_stereo_mode | (cx->params.audio_properties & ~stereo_mask); CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n", cx->dualwatch_stereo_mode, new_stereo_mode, new_bitmap); if (cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2, cx18_find_handle(cx), new_bitmap) == 0) { cx->dualwatch_stereo_mode = new_stereo_mode; return; } CX18_DEBUG_INFO("dualwatch: changing stereo flag failed\n"); }
static int cx18_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) { struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; if (v4l2_chip_match_host(®->match)) return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg); cx18_call_i2c_clients(cx, VIDIOC_DBG_S_REGISTER, reg); return 0; }
void cx18_audio_set_audio_clock_freq(struct cx18 *cx, u8 freq) { static u32 freqs[3] = { 44100, 48000, 32000 }; /* The audio clock of the digitizer must match the codec sample rate otherwise you get some very strange effects. */ if (freq > 2) return; cx18_call_i2c_clients(cx, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[freq]); }
static int cx18_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) { struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; if (vf->tuner != 0) return -EINVAL; cx18_call_i2c_clients(cx, VIDIOC_G_FREQUENCY, vf); return 0; }
static int cx18_serialized_open(struct cx18_stream *s, struct file *filp) { struct cx18 *cx = s->cx; struct cx18_open_id *item; CX18_DEBUG_FILE("open %s\n", s->name); /* Allocate memory */ item = kmalloc(sizeof(struct cx18_open_id), GFP_KERNEL); if (NULL == item) { CX18_DEBUG_WARN("nomem on v4l2 open\n"); return -ENOMEM; } item->cx = cx; item->type = s->type; v4l2_prio_open(&cx->prio, &item->prio); item->open_id = cx->open_id++; filp->private_data = item; if (item->type == CX18_ENC_STREAM_TYPE_RAD) { /* Try to claim this stream */ if (cx18_claim_stream(item, item->type)) { /* No, it's already in use */ kfree(item); return -EBUSY; } if (!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) { if (atomic_read(&cx->ana_capturing) > 0) { /* switching to radio while capture is in progress is not polite */ cx18_release_stream(s); kfree(item); return -EBUSY; } } /* Mark that the radio is being used. */ set_bit(CX18_F_I_RADIO_USER, &cx->i_flags); /* We have the radio */ cx18_mute(cx); /* Switch tuner to radio */ cx18_call_i2c_clients(cx, AUDC_SET_RADIO, NULL); /* Select the correct audio input (i.e. radio tuner) */ cx18_audio_set_io(cx); /* Done! Unmute and continue. */ cx18_unmute(cx); } return 0; }
static int cx18_g_chip_ident(struct file *file, void *fh, struct v4l2_dbg_chip_ident *chip) { struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; chip->ident = V4L2_IDENT_NONE; chip->revision = 0; if (v4l2_chip_match_host(&chip->match)) { chip->ident = V4L2_IDENT_CX23418; return 0; } cx18_call_i2c_clients(cx, VIDIOC_DBG_G_CHIP_IDENT, chip); return 0; }
int cx18_v4l2_close(struct inode *inode, struct file *filp) { struct cx18_open_id *id = filp->private_data; struct cx18 *cx = id->cx; struct cx18_stream *s = &cx->streams[id->type]; CX18_DEBUG_IOCTL("close() of %s\n", s->name); v4l2_prio_close(&cx->prio, &id->prio); /* Easy case first: this stream was never claimed by us */ if (s->id != id->open_id) { kfree(id); return 0; } /* 'Unclaim' this stream */ /* Stop radio */ mutex_lock(&cx->serialize_lock); if (id->type == CX18_ENC_STREAM_TYPE_RAD) { /* Closing radio device, return to TV mode */ cx18_mute(cx); /* Mark that the radio is no longer in use */ clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags); /* Switch tuner to TV */ cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std); /* Select correct audio input (i.e. TV tuner or Line in) */ cx18_audio_set_io(cx); if (atomic_read(&cx->ana_capturing) > 0) { /* Undo video mute */ cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle, cx->params.video_mute | (cx->params.video_mute_yuv << 8)); } /* Done! Unmute and continue. */ cx18_unmute(cx); cx18_release_stream(s); } else { cx18_stop_capture(id, 0); } kfree(id); mutex_unlock(&cx->serialize_lock); return 0; }
static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) { struct cx18_open_id *id = fh; struct cx18 *cx = id->cx; int ret; ret = v4l2_prio_check(&cx->prio, &id->prio); if (ret) return ret; if (vt->index != 0) return -EINVAL; /* Setting tuner can only set audio mode */ cx18_call_i2c_clients(cx, VIDIOC_S_TUNER, vt); return 0; }
static int cx18_log_status(struct file *file, void *fh) { struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; struct v4l2_input vidin; struct v4l2_audio audin; int i; CX18_INFO("================= START STATUS CARD #%d =================\n", cx->num); if (cx->hw_flags & CX18_HW_TVEEPROM) { struct tveeprom tv; cx18_read_eeprom(cx, &tv); } cx18_call_i2c_clients(cx, VIDIOC_LOG_STATUS, NULL); cx18_get_input(cx, cx->active_input, &vidin); cx18_get_audio_input(cx, cx->audio_input, &audin); CX18_INFO("Video Input: %s\n", vidin.name); CX18_INFO("Audio Input: %s\n", audin.name); mutex_lock(&cx->gpio_lock); CX18_INFO("GPIO: direction 0x%08x, value 0x%08x\n", cx->gpio_dir, cx->gpio_val); mutex_unlock(&cx->gpio_lock); CX18_INFO("Tuner: %s\n", test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ? "Radio" : "TV"); cx2341x_log_status(&cx->params, cx->name); CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags); for (i = 0; i < CX18_MAX_STREAMS; i++) { struct cx18_stream *s = &cx->streams[i]; if (s->v4l2dev == NULL || s->buffers == 0) continue; CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags, (s->buffers - atomic_read(&s->q_free.buffers)) * 100 / s->buffers, (s->buffers * s->buf_size) / 1024, s->buffers); } CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n", (long long)cx->mpg_data_received, (long long)cx->vbi_data_inserted); cx18_log_statistics(cx); CX18_INFO("================== END STATUS CARD #%d ==================\n", cx->num); return 0; }
static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) { struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; if (vt->index != 0) return -EINVAL; cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, vt); if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) { strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name)); vt->type = V4L2_TUNER_RADIO; } else { strlcpy(vt->name, "cx18 TV Tuner", sizeof(vt->name)); vt->type = V4L2_TUNER_ANALOG_TV; } return 0; }
int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) { struct cx18_open_id *id = fh; struct cx18 *cx = id->cx; int ret; ret = v4l2_prio_check(&cx->prio, &id->prio); if (ret) return ret; if (vf->tuner != 0) return -EINVAL; cx18_mute(cx); CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency); cx18_call_i2c_clients(cx, VIDIOC_S_FREQUENCY, vf); cx18_unmute(cx); return 0; }
int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std) { struct cx18_open_id *id = fh; struct cx18 *cx = id->cx; int ret; ret = v4l2_prio_check(&cx->prio, &id->prio); if (ret) return ret; if ((*std & V4L2_STD_ALL) == 0) return -EINVAL; if (*std == cx->std) return 0; if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) || atomic_read(&cx->ana_capturing) > 0) { /* Switching standard would turn off the radio or mess with already running streams, prevent that by returning EBUSY. */ return -EBUSY; } cx->std = *std; cx->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0; cx->params.is_50hz = cx->is_50hz = !cx->is_60hz; cx->params.width = 720; cx->params.height = cx->is_50hz ? 576 : 480; cx->vbi.count = cx->is_50hz ? 18 : 12; cx->vbi.start[0] = cx->is_50hz ? 6 : 10; cx->vbi.start[1] = cx->is_50hz ? 318 : 273; cx->vbi.sliced_decoder_line_size = cx->is_60hz ? 272 : 284; CX18_DEBUG_INFO("Switching standard to %llx.\n", (unsigned long long) cx->std); /* Tuner */ cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std); return 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]); } }