static struct cx18_queue *_cx18_stream_put_mdl_fw(struct cx18_stream *s, struct cx18_mdl *mdl) { struct cx18 *cx = s->cx; struct cx18_queue *q; /* Don't give it to the firmware, if we're not running a capture */ if (s->handle == CX18_INVALID_TASK_HANDLE || test_bit(CX18_F_S_STOPPING, &s->s_flags) || !test_bit(CX18_F_S_STREAMING, &s->s_flags)) return cx18_enqueue(s, mdl, &s->q_free); q = cx18_enqueue(s, mdl, &s->q_busy); if (q != &s->q_busy) return q; /* The firmware has the max MDLs it can handle */ cx18_mdl_sync_for_device(s, mdl); cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, (void __iomem *) &cx->scb->cpu_mdl[mdl->id] - cx->enc_mem, s->bufs_per_mdl, mdl->id, s->mdl_size); return q; }
int cx18_start_v4l2_encode_stream(struct cx18_stream *s) { u32 data[MAX_MB_ARGUMENTS]; struct cx18 *cx = s->cx; int captype = 0; struct cx18_stream *s_idx; if (!cx18_stream_enabled(s)) return -EINVAL; CX18_DEBUG_INFO("Start encoder stream %s\n", s->name); switch (s->type) { case CX18_ENC_STREAM_TYPE_MPG: captype = CAPTURE_CHANNEL_TYPE_MPEG; cx->mpg_data_received = cx->vbi_data_inserted = 0; cx->dualwatch_jiffies = jiffies; cx->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(cx->cxhdl.audio_mode); cx->search_pack_header = 0; break; case CX18_ENC_STREAM_TYPE_IDX: captype = CAPTURE_CHANNEL_TYPE_INDEX; break; case CX18_ENC_STREAM_TYPE_TS: captype = CAPTURE_CHANNEL_TYPE_TS; break; case CX18_ENC_STREAM_TYPE_YUV: captype = CAPTURE_CHANNEL_TYPE_YUV; break; case CX18_ENC_STREAM_TYPE_PCM: captype = CAPTURE_CHANNEL_TYPE_PCM; break; case CX18_ENC_STREAM_TYPE_VBI: #ifdef CX18_ENCODER_PARSES_SLICED captype = cx18_raw_vbi(cx) ? CAPTURE_CHANNEL_TYPE_VBI : CAPTURE_CHANNEL_TYPE_SLICED_VBI; #else /* * Currently we set things up so that Sliced VBI from the * digitizer is handled as Raw VBI by the encoder */ captype = CAPTURE_CHANNEL_TYPE_VBI; #endif cx->vbi.frame = 0; cx->vbi.inserted_frame = 0; memset(cx->vbi.sliced_mpeg_size, 0, sizeof(cx->vbi.sliced_mpeg_size)); break; default: return -EINVAL; } /* Clear Streamoff flags in case left from last capture */ clear_bit(CX18_F_S_STREAMOFF, &s->s_flags); cx18_vapi_result(cx, data, CX18_CREATE_TASK, 1, CPU_CMD_MASK_CAPTURE); s->handle = data[0]; cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype); /* * For everything but CAPTURE_CHANNEL_TYPE_TS, play it safe and * set up all the parameters, as it is not obvious which parameters the * firmware shares across capture channel types and which it does not. * * Some of the cx18_vapi() calls below apply to only certain capture * channel types. We're hoping there's no harm in calling most of them * anyway, as long as the values are all consistent. Setting some * shared parameters will have no effect once an analog capture channel * has started streaming. */ if (captype != CAPTURE_CHANNEL_TYPE_TS) { cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0); cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1); cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 8, 0); cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 4, 1); /* * Audio related reset according to * Documentation/video4linux/cx2341x/fw-encoder-api.txt */ if (atomic_read(&cx->ana_capturing) == 0) cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, s->handle, 12); /* * Number of lines for Field 1 & Field 2 according to * Documentation/video4linux/cx2341x/fw-encoder-api.txt * Field 1 is 312 for 625 line systems in BT.656 * Field 2 is 313 for 625 line systems in BT.656 */ cx18_vapi(cx, CX18_CPU_SET_CAPTURE_LINE_NO, 3, s->handle, 312, 313); if (cx->v4l2_cap & V4L2_CAP_VBI_CAPTURE) cx18_vbi_setup(s); /* * Select to receive I, P, and B frame index entries, if the * index stream is enabled. Otherwise disable index entry * generation. */ s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 2, s->handle, cx18_stream_enabled(s_idx) ? 7 : 0); /* Call out to the common CX2341x API setup for user controls */ cx->cxhdl.priv = s; cx2341x_handler_setup(&cx->cxhdl); /* * When starting a capture and we're set for radio, * ensure the video is muted, despite the user control. */ if (!cx->cxhdl.video_mute && test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle, (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8) | 1); /* Enable the Video Format Converter for UYVY 4:2:2 support, * rather than the default HM12 Macroblovk 4:2:0 support. */ if (captype == CAPTURE_CHANNEL_TYPE_YUV) { if (s->pixelformat == V4L2_PIX_FMT_UYVY) cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2, s->handle, 1); else /* If in doubt, default to HM12 */ cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2, s->handle, 0); } } if (atomic_read(&cx->tot_capturing) == 0) { cx2341x_handler_set_busy(&cx->cxhdl, 1); clear_bit(CX18_F_I_EOS, &cx->i_flags); cx18_write_reg(cx, 7, CX18_DSP0_INTERRUPT_MASK); } cx18_vapi(cx, CX18_CPU_DE_SET_MDL_ACK, 3, s->handle, (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem, (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem); /* Init all the cpu_mdls for this stream */ cx18_stream_configure_mdls(s); _cx18_stream_load_fw_queue(s); /* begin_capture */ if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) { CX18_DEBUG_WARN("Error starting capture!\n"); /* Ensure we're really not capturing before releasing MDLs */ set_bit(CX18_F_S_STOPPING, &s->s_flags); if (s->type == CX18_ENC_STREAM_TYPE_MPG) cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1); else cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle); clear_bit(CX18_F_S_STREAMING, &s->s_flags); /* FIXME - CX18_F_S_STREAMOFF as well? */ cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); s->handle = CX18_INVALID_TASK_HANDLE; clear_bit(CX18_F_S_STOPPING, &s->s_flags); if (atomic_read(&cx->tot_capturing) == 0) { set_bit(CX18_F_I_EOS, &cx->i_flags); cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK); } return -EINVAL; } /* you're live! sit back and await interrupts :) */ if (captype != CAPTURE_CHANNEL_TYPE_TS) atomic_inc(&cx->ana_capturing); atomic_inc(&cx->tot_capturing); return 0; }
int cx18_api_func(void *priv, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]) { struct cx18_api_func_private *api_priv = priv; struct cx18 *cx = api_priv->cx; struct cx18_stream *s = api_priv->s; switch (cmd) { case CX2341X_ENC_SET_OUTPUT_PORT: return 0; case CX2341X_ENC_SET_FRAME_RATE: return cx18_vapi(cx, CX18_CPU_SET_VIDEO_IN, 6, s->handle, 0, 0, 0, 0, data[0]); case CX2341X_ENC_SET_FRAME_SIZE: return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RESOLUTION, 3, s->handle, data[1], data[0]); case CX2341X_ENC_SET_STREAM_TYPE: return cx18_vapi(cx, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 2, s->handle, data[0]); case CX2341X_ENC_SET_ASPECT_RATIO: return cx18_vapi(cx, CX18_CPU_SET_ASPECT_RATIO, 2, s->handle, data[0]); case CX2341X_ENC_SET_GOP_PROPERTIES: return cx18_vapi(cx, CX18_CPU_SET_GOP_STRUCTURE, 3, s->handle, data[0], data[1]); case CX2341X_ENC_SET_GOP_CLOSURE: return 0; case CX2341X_ENC_SET_AUDIO_PROPERTIES: return cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2, s->handle, data[0]); case CX2341X_ENC_MUTE_AUDIO: return cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, s->handle, data[0]); case CX2341X_ENC_SET_BIT_RATE: return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RATE, 5, s->handle, data[0], data[1], data[2], data[3]); case CX2341X_ENC_MUTE_VIDEO: return cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle, data[0]); case CX2341X_ENC_SET_FRAME_DROP_RATE: return cx18_vapi(cx, CX18_CPU_SET_SKIP_INPUT_FRAME, 2, s->handle, data[0]); case CX2341X_ENC_MISC: return cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 4, s->handle, data[0], data[1], data[2]); case CX2341X_ENC_SET_DNR_FILTER_MODE: cx->filter_mode = (data[0] & 3) | (data[1] << 2); return cx18_set_filter_param(s); case CX2341X_ENC_SET_DNR_FILTER_PROPS: cx->spatial_strength = data[0]; cx->temporal_strength = data[1]; return cx18_set_filter_param(s); case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE: return cx18_vapi(cx, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 3, s->handle, data[0], data[1]); case CX2341X_ENC_SET_CORING_LEVELS: return cx18_vapi(cx, CX18_CPU_SET_MEDIAN_CORING, 5, s->handle, data[0], data[1], data[2], data[3]); } CX18_WARN("Unknown cmd %x\n", cmd); return 0; }
static int __devinit cx18_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) { int retval = 0; int i; u32 devtype; struct cx18 *cx; /* FIXME - module parameter arrays constrain max instances */ i = atomic_inc_return(&cx18_instance) - 1; if (i >= CX18_MAX_CARDS) { printk(KERN_ERR "cx18: cannot manage card %d, driver has a " "limit of 0 - %d\n", i, CX18_MAX_CARDS - 1); return -ENOMEM; } cx = kzalloc(sizeof(struct cx18), GFP_ATOMIC); if (cx == NULL) { printk(KERN_ERR "cx18: cannot manage card %d, out of memory\n", i); return -ENOMEM; } cx->pci_dev = pci_dev; cx->instance = i; retval = v4l2_device_register(&pci_dev->dev, &cx->v4l2_dev); if (retval) { printk(KERN_ERR "cx18: v4l2_device_register of card %d failed" "\n", cx->instance); kfree(cx); return retval; } snprintf(cx->v4l2_dev.name, sizeof(cx->v4l2_dev.name), "cx18-%d", cx->instance); CX18_INFO("Initializing card %d\n", cx->instance); cx18_process_options(cx); if (cx->options.cardtype == -1) { retval = -ENODEV; goto err; } 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_workqueues; /* 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. Can't get a window into CX23418 " "memory and register space\n"); CX18_ERR("Each capture card with a CX23418 needs 64 MB of " "vmalloc address space for the window\n"); CX18_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n"); CX18_ERR("Use the vmalloc= kernel command line option to set " "VmallocTotal to a larger value\n"); retval = -ENOMEM; goto free_mem; } cx->reg_mem = cx->enc_mem + CX18_REG_OFFSET; devtype = cx18_read_reg(cx, 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); /* Initialize integrated A/V decoder early to set PLLs, just in case */ retval = cx18_av_probe(cx); if (retval) { CX18_ERR("Could not register A/V decoder subdevice\n"); goto free_map; } /* Initialize GPIO Reset Controller to do chip resets during i2c init */ if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) { if (cx18_gpio_register(cx, CX18_HW_GPIO_RESET_CTRL) != 0) CX18_WARN("Could not register GPIO reset controller" "subdevice; proceeding anyway.\n"); else cx->hw_flags |= CX18_HW_GPIO_RESET_CTRL; } /* active i2c */ CX18_DEBUG_INFO("activating i2c...\n"); retval = init_cx18_i2c(cx); if (retval) { CX18_ERR("Could not initialize i2c\n"); goto free_map; } 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. */ const struct cx18_card *orig_card = cx->card; cx18_process_eeprom(cx); if (cx->card != orig_card) { /* Changed the cardtype; re-reset the I2C chips */ cx18_gpio_init(cx); cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, core, reset, (u32) CX18_GPIO_RESET_I2C); } } 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); cx18_init_scb(cx); /* Register IRQ */ retval = request_irq(cx->pci_dev->irq, cx18_irq_handler, IRQF_SHARED | IRQF_DISABLED, cx->v4l2_dev.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) { 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_init_subdevs(cx); if (cx->std & V4L2_STD_525_60) cx->is_60hz = 1; else cx->is_50hz = 1; cx2341x_handler_set_50hz(&cx->cxhdl, !cx->is_60hz); 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_all(cx, tuner, s_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_all(cx, tuner, s_config, &cfg); } } /* The tuner is fixed to the standard. The other inputs (e.g. S-Video) are not. */ cx->tuner_std = cx->std; if (cx->std == V4L2_STD_ALL) cx->std = V4L2_STD_NTSC_M; 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: %s\n", cx->card_name); /* Load cx18 submodules (cx18-alsa) */ request_modules(cx); return 0; free_streams: cx18_streams_cleanup(cx, 1); free_irq: free_irq(cx->pci_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_workqueues: destroy_workqueue(cx->in_work_queue); err: if (retval == 0) retval = -ENODEV; CX18_ERR("Error %d on initialization\n", retval); v4l2_device_unregister(&cx->v4l2_dev); kfree(cx); 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; v4l2_std_id std; 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 * with the digital TS. * * The second firmware load requires us to normalize the APU state, * or the audio for the first analog capture will be badly incorrect. * * I can't seem to call APU_RESETAI and have it succeed without the * APU capturing audio, so we start and stop it here to do the reset */ /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */ cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0); cx18_vapi(cx, CX18_APU_RESETAI, 0); cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG); 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; } /* * The second firmware load requires us to normalize the APU state, * or the audio for the first analog capture will be badly incorrect. * * I can't seem to call APU_RESETAI and have it succeed without the * APU capturing audio, so we start and stop it here to do the reset */ /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */ cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0); cx18_vapi(cx, CX18_APU_RESETAI, 0); 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, load_fw); 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 */ std = (cx->tuner_std == V4L2_STD_ALL) ? V4L2_STD_NTSC_M : cx->tuner_std; cx18_s_std(NULL, &fh, &std); cx18_s_frequency(NULL, &fh, &vf); return 0; } static void cx18_cancel_in_work_orders(struct cx18 *cx) { int i; for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) cancel_work_sync(&cx->in_work_order[i].work); }
int cx18_start_capture(struct cx18_open_id *id) { struct cx18 *cx = id->cx; struct cx18_stream *s = &cx->streams[id->type]; struct cx18_stream *s_vbi; if (s->type == CX18_ENC_STREAM_TYPE_RAD) { /* you cannot read from these stream types. */ return -EPERM; } /* Try to claim this stream. */ if (cx18_claim_stream(id, s->type)) return -EBUSY; /* If capture is already in progress, then we also have to do nothing extra. */ if (test_bit(CX18_F_S_STREAMOFF, &s->s_flags) || test_and_set_bit(CX18_F_S_STREAMING, &s->s_flags)) { set_bit(CX18_F_S_APPL_IO, &s->s_flags); return 0; } /* Start VBI capture if required */ s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; if (s->type == CX18_ENC_STREAM_TYPE_MPG && test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) && !test_and_set_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) { /* Note: the CX18_ENC_STREAM_TYPE_VBI is claimed automatically when the MPG stream is claimed. We only need to start the VBI capturing. */ if (cx18_start_v4l2_encode_stream(s_vbi)) { CX18_DEBUG_WARN("VBI capture start failed\n"); /* Failure, clean up and return an error */ clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags); clear_bit(CX18_F_S_STREAMING, &s->s_flags); /* also releases the associated VBI stream */ cx18_release_stream(s); return -EIO; } CX18_DEBUG_INFO("VBI insertion started\n"); } /* Tell the card to start capturing */ if (!cx18_start_v4l2_encode_stream(s)) { /* We're done */ set_bit(CX18_F_S_APPL_IO, &s->s_flags); /* Resume a possibly paused encoder */ if (test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, s->handle); return 0; } /* failure, clean up */ CX18_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name); /* Note: the CX18_ENC_STREAM_TYPE_VBI is released automatically when the MPG stream is released. We only need to stop the VBI capturing. */ if (s->type == CX18_ENC_STREAM_TYPE_MPG && test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) { cx18_stop_v4l2_encode_stream(s_vbi, 0); clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags); } clear_bit(CX18_F_S_STREAMING, &s->s_flags); cx18_release_stream(s); return -EIO; }
static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf, size_t tot_count, int non_block) { struct cx18 *cx = s->cx; size_t tot_written = 0; int single_frame = 0; if (atomic_read(&cx->ana_capturing) == 0 && s->id == -1) { /* shouldn't happen */ CX18_DEBUG_WARN("Stream %s not initialized before read\n", s->name); return -EIO; } /* Each VBI buffer is one frame, the v4l2 API says that for VBI the frames should arrive one-by-one, so make sure we never output more than one VBI frame at a time */ if (s->type == CX18_ENC_STREAM_TYPE_VBI && cx->vbi.sliced_in->service_set) single_frame = 1; for (;;) { struct cx18_buffer *buf; int rc; buf = cx18_get_buffer(s, non_block, &rc); /* if there is no data available... */ if (buf == NULL) { /* if we got data, then return that regardless */ if (tot_written) break; /* EOS condition */ if (rc == 0) { clear_bit(CX18_F_S_STREAMOFF, &s->s_flags); clear_bit(CX18_F_S_APPL_IO, &s->s_flags); cx18_release_stream(s); } /* set errno */ return rc; } rc = cx18_copy_buf_to_user(s, buf, ubuf + tot_written, tot_count - tot_written); if (buf != &cx->vbi.sliced_mpeg_buf) { if (buf->readpos == buf->bytesused) { cx18_buf_sync_for_device(s, buf); cx18_enqueue(s, buf, &s->q_free); cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, 1, buf->id, s->buf_size); } else cx18_enqueue(s, buf, &s->q_io); } else if (buf->readpos == buf->bytesused) { int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES; cx->vbi.sliced_mpeg_size[idx] = 0; cx->vbi.inserted_frame++; cx->vbi_data_inserted += buf->bytesused; } if (rc < 0) return rc; tot_written += rc; if (tot_written == tot_count || single_frame) break; } return tot_written; }
int cx18_start_v4l2_encode_stream(struct cx18_stream *s) { u32 data[MAX_MB_ARGUMENTS]; struct cx18 *cx = s->cx; int captype = 0; struct cx18_stream *s_idx; if (!cx18_stream_enabled(s)) return -EINVAL; CX18_DEBUG_INFO("Start encoder stream %s\n", s->name); switch (s->type) { case CX18_ENC_STREAM_TYPE_MPG: captype = CAPTURE_CHANNEL_TYPE_MPEG; cx->mpg_data_received = cx->vbi_data_inserted = 0; cx->dualwatch_jiffies = jiffies; cx->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(cx->cxhdl.audio_mode); cx->search_pack_header = 0; break; case CX18_ENC_STREAM_TYPE_IDX: captype = CAPTURE_CHANNEL_TYPE_INDEX; break; case CX18_ENC_STREAM_TYPE_TS: captype = CAPTURE_CHANNEL_TYPE_TS; break; case CX18_ENC_STREAM_TYPE_YUV: captype = CAPTURE_CHANNEL_TYPE_YUV; break; case CX18_ENC_STREAM_TYPE_PCM: captype = CAPTURE_CHANNEL_TYPE_PCM; break; case CX18_ENC_STREAM_TYPE_VBI: #ifdef CX18_ENCODER_PARSES_SLICED captype = cx18_raw_vbi(cx) ? CAPTURE_CHANNEL_TYPE_VBI : CAPTURE_CHANNEL_TYPE_SLICED_VBI; #else captype = CAPTURE_CHANNEL_TYPE_VBI; #endif cx->vbi.frame = 0; cx->vbi.inserted_frame = 0; memset(cx->vbi.sliced_mpeg_size, 0, sizeof(cx->vbi.sliced_mpeg_size)); break; default: return -EINVAL; } clear_bit(CX18_F_S_STREAMOFF, &s->s_flags); cx18_vapi_result(cx, data, CX18_CREATE_TASK, 1, CPU_CMD_MASK_CAPTURE); s->handle = data[0]; cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype); if (captype != CAPTURE_CHANNEL_TYPE_TS) { cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0); cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1); cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 8, 0); cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 4, 1); if (atomic_read(&cx->ana_capturing) == 0) cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, s->handle, 12); cx18_vapi(cx, CX18_CPU_SET_CAPTURE_LINE_NO, 3, s->handle, 312, 313); if (cx->v4l2_cap & V4L2_CAP_VBI_CAPTURE) cx18_vbi_setup(s); s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 2, s->handle, cx18_stream_enabled(s_idx) ? 7 : 0); cx->cxhdl.priv = s; cx2341x_handler_setup(&cx->cxhdl); if (!cx->cxhdl.video_mute && test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle, (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8) | 1); if (captype == CAPTURE_CHANNEL_TYPE_YUV) { if (s->pixelformat == V4L2_PIX_FMT_UYVY) cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2, s->handle, 1); else cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2, s->handle, 0); } } if (atomic_read(&cx->tot_capturing) == 0) { cx2341x_handler_set_busy(&cx->cxhdl, 1); clear_bit(CX18_F_I_EOS, &cx->i_flags); cx18_write_reg(cx, 7, CX18_DSP0_INTERRUPT_MASK); } cx18_vapi(cx, CX18_CPU_DE_SET_MDL_ACK, 3, s->handle, (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem, (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem); cx18_stream_configure_mdls(s); _cx18_stream_load_fw_queue(s); if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) { CX18_DEBUG_WARN("Error starting capture!\n"); set_bit(CX18_F_S_STOPPING, &s->s_flags); if (s->type == CX18_ENC_STREAM_TYPE_MPG) cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1); else cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle); clear_bit(CX18_F_S_STREAMING, &s->s_flags); cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); s->handle = CX18_INVALID_TASK_HANDLE; clear_bit(CX18_F_S_STOPPING, &s->s_flags); if (atomic_read(&cx->tot_capturing) == 0) { set_bit(CX18_F_I_EOS, &cx->i_flags); cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK); } return -EINVAL; } if (captype != CAPTURE_CHANNEL_TYPE_TS) atomic_inc(&cx->ana_capturing); atomic_inc(&cx->tot_capturing); return 0; }
static int __devinit cx18_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) { int retval = 0; int i; u32 devtype; struct cx18 *cx; i = atomic_inc_return(&cx18_instance) - 1; if (i >= CX18_MAX_CARDS) { printk(KERN_ERR "cx18: cannot manage card %d, driver has a " "limit of 0 - %d\n", i, CX18_MAX_CARDS - 1); return -ENOMEM; } cx = kzalloc(sizeof(struct cx18), GFP_ATOMIC); if (cx == NULL) { printk(KERN_ERR "cx18: cannot manage card %d, out of memory\n", i); return -ENOMEM; } cx->pci_dev = pci_dev; cx->instance = i; retval = v4l2_device_register(&pci_dev->dev, &cx->v4l2_dev); if (retval) { printk(KERN_ERR "cx18: v4l2_device_register of card %d failed" "\n", cx->instance); kfree(cx); return retval; } snprintf(cx->v4l2_dev.name, sizeof(cx->v4l2_dev.name), "cx18-%d", cx->instance); CX18_INFO("Initializing card %d\n", cx->instance); cx18_process_options(cx); if (cx->options.cardtype == -1) { retval = -ENODEV; goto err; } retval = cx18_init_struct1(cx); if (retval) goto err; CX18_DEBUG_INFO("base addr: 0x%08x\n", cx->base_addr); retval = cx18_setup_pci(cx, pci_dev, pci_id); if (retval != 0) goto free_workqueues; 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 = cx18_read_reg(cx, 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); retval = cx18_av_probe(cx); if (retval) { CX18_ERR("Could not register A/V decoder subdevice\n"); goto free_map; } cx18_call_hw(cx, CX18_HW_418_AV, core, init, 0); if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) { if (cx18_gpio_register(cx, CX18_HW_GPIO_RESET_CTRL) != 0) CX18_WARN("Could not register GPIO reset controller" "subdevice; proceeding anyway.\n"); else cx->hw_flags |= CX18_HW_GPIO_RESET_CTRL; } CX18_DEBUG_INFO("activating i2c...\n"); retval = init_cx18_i2c(cx); if (retval) { CX18_ERR("Could not initialize i2c\n"); goto free_map; } if (cx->card->hw_all & CX18_HW_TVEEPROM) { 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); cx18_init_scb(cx); retval = request_irq(cx->pci_dev->irq, cx18_irq_handler, IRQF_SHARED | IRQF_DISABLED, cx->v4l2_dev.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) { 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 (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); cx18_init_struct2(cx); cx18_init_subdevs(cx); if (cx->std & V4L2_STD_525_60) cx->is_60hz = 1; else cx->is_50hz = 1; cx->params.video_gop_size = cx->is_60hz ? 15 : 12; 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; setup.tuner_callback = (setup.type == TUNER_XC2028) ? cx18_reset_tuner_gpio : NULL; cx18_call_all(cx, tuner, s_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_all(cx, tuner, s_config, &cfg); } } 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: %s\n", cx->card_name); return 0; free_streams: cx18_streams_cleanup(cx, 1); free_irq: free_irq(cx->pci_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_workqueues: destroy_workqueue(cx->in_work_queue); destroy_workqueue(cx->out_work_queue); err: if (retval == 0) retval = -ENODEV; CX18_ERR("Error %d on initialization\n", retval); v4l2_device_unregister(&cx->v4l2_dev); kfree(cx); 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) { 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); cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0); cx18_vapi(cx, CX18_APU_RESETAI, 0); cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG); fw_retry_count = 3; while (--fw_retry_count > 0) { 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; } cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0); cx18_vapi(cx, CX18_APU_RESETAI, 0); cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG); v4l2_subdev_call(cx->sd_av, core, load_fw); vf.tuner = 0; vf.type = V4L2_TUNER_ANALOG_TV; vf.frequency = 6400; if (cx->std == V4L2_STD_NTSC_M_JP) vf.frequency = 1460; else if (cx->std & V4L2_STD_NTSC_M) vf.frequency = 1076; video_input = cx->active_input; cx->active_input++; cx18_s_input(NULL, &fh, video_input); cx->std++; cx18_s_std(NULL, &fh, &cx->tuner_std); cx18_s_frequency(NULL, &fh, &vf); return 0; } static void cx18_cancel_in_work_orders(struct cx18 *cx) { int i; for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) cancel_work_sync(&cx->in_work_order[i].work); }
int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) { struct cx18 *cx = s->cx; unsigned long then; if (!cx18_stream_enabled(s)) return -EINVAL; /* This function assumes that you are allowed to stop the capture and that we are actually capturing */ CX18_DEBUG_INFO("Stop Capture\n"); if (atomic_read(&cx->tot_capturing) == 0) return 0; set_bit(CX18_F_S_STOPPING, &s->s_flags); if (s->type == CX18_ENC_STREAM_TYPE_MPG) cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, !gop_end); else cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle); then = jiffies; if (s->type == CX18_ENC_STREAM_TYPE_MPG && gop_end) { #if 0 /* only run these if we're shutting down the last cap */ DECLARE_WAITQUEUE(wait, current); unsigned long duration; then = jiffies; add_wait_queue(&cx->cap_w, &wait); set_current_state(TASK_INTERRUPTIBLE); /* TODO: wait 2s for EOS interrupt */ while (!test_bit(CX18_F_I_EOS, &cx->i_flags) && time_before(jiffies, then + msecs_to_jiffies(2000))) schedule_timeout(msecs_to_jiffies(10)); duration = jiffies_to_msecs(jiffies - then); if (!test_bit(CX18_F_I_EOS, &cx->i_flags)) { CX18_DEBUG_WARN("%s: EOS interrupt not received! stopping anyway.\n", s->name); CX18_DEBUG_WARN("%s: waited %lu ms.\n", s->name, duration); } else { CX18_DEBUG_INFO("%s: EOS took %lu ms to occur.\n", s->name, duration); } set_current_state(TASK_RUNNING); remove_wait_queue(&cx->cap_w, &wait); #else CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n"); #endif } if (s->type != CX18_ENC_STREAM_TYPE_TS) atomic_dec(&cx->ana_capturing); atomic_dec(&cx->tot_capturing); /* Clear capture and no-read bits */ clear_bit(CX18_F_S_STREAMING, &s->s_flags); /* Tell the CX23418 it can't use our buffers anymore */ cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); s->handle = CX18_INVALID_TASK_HANDLE; clear_bit(CX18_F_S_STOPPING, &s->s_flags); if (atomic_read(&cx->tot_capturing) > 0) return 0; cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK); wake_up(&s->waitq); return 0; }