static void cx18_stream_configure_mdls(struct cx18_stream *s) { cx18_unload_queues(s); switch (s->type) { case CX18_ENC_STREAM_TYPE_YUV: if (s->pixelformat == V4L2_PIX_FMT_HM12) s->mdl_size = 720 * s->cx->cxhdl.height * 3 / 2; else s->mdl_size = 720 * s->cx->cxhdl.height * 2; s->bufs_per_mdl = s->mdl_size / s->buf_size; if (s->mdl_size % s->buf_size) s->bufs_per_mdl++; break; case CX18_ENC_STREAM_TYPE_VBI: s->bufs_per_mdl = 1; if (cx18_raw_vbi(s->cx)) { s->mdl_size = (s->cx->is_60hz ? 12 : 18) * 2 * vbi_active_samples; } else { s->mdl_size = s->cx->is_60hz ? (21 - 4 + 1) * 2 * vbi_hblank_samples_60Hz : (23 - 2 + 1) * 2 * vbi_hblank_samples_50Hz; } break; default: s->bufs_per_mdl = 1; s->mdl_size = s->buf_size * s->bufs_per_mdl; break; } cx18_load_queues(s); }
/* This function tries to claim the stream for a specific file descriptor. If no one else is using this stream then the stream is claimed and associated VBI and IDX streams are also automatically claimed. Possible error returns: -EBUSY if someone else has claimed the stream or 0 on success. */ int cx18_claim_stream(struct cx18_open_id *id, int type) { struct cx18 *cx = id->cx; struct cx18_stream *s = &cx->streams[type]; struct cx18_stream *s_assoc; /* Nothing should ever try to directly claim the IDX stream */ if (type == CX18_ENC_STREAM_TYPE_IDX) { CX18_WARN("MPEG Index stream cannot be claimed " "directly, but something tried.\n"); return -EINVAL; } if (test_and_set_bit(CX18_F_S_CLAIMED, &s->s_flags)) { /* someone already claimed this stream */ if (s->id == id->open_id) { /* yes, this file descriptor did. So that's OK. */ return 0; } if (s->id == -1 && type == CX18_ENC_STREAM_TYPE_VBI) { /* VBI is handled already internally, now also assign the file descriptor to this stream for external reading of the stream. */ s->id = id->open_id; CX18_DEBUG_INFO("Start Read VBI\n"); return 0; } /* someone else is using this stream already */ CX18_DEBUG_INFO("Stream %d is busy\n", type); return -EBUSY; } s->id = id->open_id; /* * CX18_ENC_STREAM_TYPE_MPG needs to claim: * CX18_ENC_STREAM_TYPE_VBI, if VBI insertion is on for sliced VBI, or * CX18_ENC_STREAM_TYPE_IDX, if VBI insertion is off for sliced VBI * (We don't yet fix up MPEG Index entries for our inserted packets). * * For all other streams we're done. */ if (type != CX18_ENC_STREAM_TYPE_MPG) return 0; s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; if (cx->vbi.insert_mpeg && !cx18_raw_vbi(cx)) s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; else if (!cx18_stream_enabled(s_assoc)) return 0; set_bit(CX18_F_S_CLAIMED, &s_assoc->s_flags); /* mark that it is used internally */ set_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags); return 0; }
void cx18_out_work_handler(struct work_struct *work) { #else void cx18_out_work_handler(void *arg) { struct work_struct *work = arg; #endif struct cx18_stream *s = container_of(work, struct cx18_stream, out_work_order); _cx18_stream_load_fw_queue(s); } static void cx18_stream_configure_mdls(struct cx18_stream *s) { cx18_unload_queues(s); switch (s->type) { case CX18_ENC_STREAM_TYPE_YUV: /* * Height should be a multiple of 32 lines. * Set the MDL size to the exact size needed for one frame. * Use enough buffers per MDL to cover the MDL size */ s->mdl_size = 720 * s->cx->params.height * 3 / 2; s->bufs_per_mdl = s->mdl_size / s->buf_size; if (s->mdl_size % s->buf_size) s->bufs_per_mdl++; break; case CX18_ENC_STREAM_TYPE_VBI: s->bufs_per_mdl = 1; if (cx18_raw_vbi(s->cx)) { s->mdl_size = (s->cx->is_60hz ? 12 : 18) * 2 * vbi_active_samples; } else { /* * See comment in cx18_vbi_setup() below about the * extra lines we capture in sliced VBI mode due to * the lines on which EAV RP codes toggle. */ s->mdl_size = s->cx->is_60hz ? (21 - 4 + 1) * 2 * vbi_hblank_samples_60Hz : (23 - 2 + 1) * 2 * vbi_hblank_samples_50Hz; } break; default: s->bufs_per_mdl = 1; s->mdl_size = s->buf_size * s->bufs_per_mdl; break; } cx18_load_queues(s); }
static void cx18_vbi_setup(struct cx18_stream *s) { struct cx18 *cx = s->cx; int raw = cx18_raw_vbi(cx); u32 data[CX2341X_MBOX_MAX_DATA]; int lines; if (cx->is_60hz) { cx->vbi.count = 12; cx->vbi.start[0] = 10; cx->vbi.start[1] = 273; } else { cx->vbi.count = 18; cx->vbi.start[0] = 6; cx->vbi.start[1] = 318; } if (raw) v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &cx->vbi.in.fmt.vbi); else v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &cx->vbi.in.fmt.sliced); if (raw) { lines = cx->vbi.count * 2; } else { lines = cx->is_60hz ? (21 - 4 + 1) * 2 : (23 - 2 + 1) * 2; } data[0] = s->handle; data[1] = (lines / 2) | ((lines / 2) << 16); data[2] = (raw ? vbi_active_samples : (cx->is_60hz ? vbi_hblank_samples_60Hz : vbi_hblank_samples_50Hz)); data[3] = 1; if (raw) { data[4] = 0x20602060; data[5] = 0x307090d0; } else { data[4] = 0xB0F0B0F0; data[5] = 0xA0E0A0E0; } CX18_DEBUG_INFO("Setup VBI h: %d lines %x bpl %d fr %d %x %x\n", data[0], data[1], data[2], data[3], data[4], data[5]); cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data); }
/* This function tries to claim the stream for a specific file descriptor. If no one else is using this stream then the stream is claimed and associated VBI streams are also automatically claimed. Possible error returns: -EBUSY if someone else has claimed the stream or 0 on success. */ static int cx18_claim_stream(struct cx18_open_id *id, int type) { struct cx18 *cx = id->cx; struct cx18_stream *s = &cx->streams[type]; struct cx18_stream *s_vbi; int vbi_type; if (test_and_set_bit(CX18_F_S_CLAIMED, &s->s_flags)) { /* someone already claimed this stream */ if (s->id == id->open_id) { /* yes, this file descriptor did. So that's OK. */ return 0; } if (s->id == -1 && type == CX18_ENC_STREAM_TYPE_VBI) { /* VBI is handled already internally, now also assign the file descriptor to this stream for external reading of the stream. */ s->id = id->open_id; CX18_DEBUG_INFO("Start Read VBI\n"); return 0; } /* someone else is using this stream already */ CX18_DEBUG_INFO("Stream %d is busy\n", type); return -EBUSY; } s->id = id->open_id; /* CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI (provided VBI insertion is on and sliced VBI is selected), for all other streams we're done */ if (type == CX18_ENC_STREAM_TYPE_MPG && cx->vbi.insert_mpeg && !cx18_raw_vbi(cx)) { vbi_type = CX18_ENC_STREAM_TYPE_VBI; } else { return 0; } s_vbi = &cx->streams[vbi_type]; set_bit(CX18_F_S_CLAIMED, &s_vbi->s_flags); /* mark that it is used internally */ set_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags); return 0; }
static void cx18_stream_configure_mdls(struct cx18_stream *s) { cx18_unload_queues(s); switch (s->type) { case CX18_ENC_STREAM_TYPE_YUV: /* * Height should be a multiple of 32 lines. * Set the MDL size to the exact size needed for one frame. * Use enough buffers per MDL to cover the MDL size */ if (s->pixelformat == V4L2_PIX_FMT_HM12) s->mdl_size = 720 * s->cx->cxhdl.height * 3 / 2; else s->mdl_size = 720 * s->cx->cxhdl.height * 2; s->bufs_per_mdl = s->mdl_size / s->buf_size; if (s->mdl_size % s->buf_size) s->bufs_per_mdl++; break; case CX18_ENC_STREAM_TYPE_VBI: s->bufs_per_mdl = 1; if (cx18_raw_vbi(s->cx)) { s->mdl_size = (s->cx->is_60hz ? 12 : 18) * 2 * vbi_active_samples; } else { /* * See comment in cx18_vbi_setup() below about the * extra lines we capture in sliced VBI mode due to * the lines on which EAV RP codes toggle. */ s->mdl_size = s->cx->is_60hz ? (21 - 4 + 1) * 2 * vbi_hblank_samples_60Hz : (23 - 2 + 1) * 2 * vbi_hblank_samples_50Hz; } break; default: s->bufs_per_mdl = 1; s->mdl_size = s->buf_size * s->bufs_per_mdl; break; } cx18_load_queues(s); }
static int cx18_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) { 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 (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0) return -EBUSY; cx->vbi.sliced_in->service_set = 0; cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; cx18_av_cmd(cx, VIDIOC_S_FMT, fmt); return cx18_g_fmt_vbi_cap(file, fh, fmt); }
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; }
static void cx18_vbi_setup(struct cx18_stream *s) { struct cx18 *cx = s->cx; int raw = cx18_raw_vbi(cx); u32 data[CX2341X_MBOX_MAX_DATA]; int lines; if (cx->is_60hz) { cx->vbi.count = 12; cx->vbi.start[0] = 10; cx->vbi.start[1] = 273; } else { /* PAL/SECAM */ cx->vbi.count = 18; cx->vbi.start[0] = 6; cx->vbi.start[1] = 318; } /* setup VBI registers */ if (raw) v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &cx->vbi.in.fmt.vbi); else v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &cx->vbi.in.fmt.sliced); /* * Send the CX18_CPU_SET_RAW_VBI_PARAM API command to setup Encoder Raw * VBI when the first analog capture channel starts, as once it starts * (e.g. MPEG), we can't effect any change in the Encoder Raw VBI setup * (i.e. for the VBI capture channels). We also send it for each * analog capture channel anyway just to make sure we get the proper * behavior */ if (raw) { lines = cx->vbi.count * 2; } else { /* * For 525/60 systems, according to the VIP 2 & BT.656 std: * The EAV RP code's Field bit toggles on line 4, a few lines * after the Vertcal Blank bit has already toggled. * Tell the encoder to capture 21-4+1=18 lines per field, * since we want lines 10 through 21. * * For 625/50 systems, according to the VIP 2 & BT.656 std: * The EAV RP code's Field bit toggles on line 1, a few lines * after the Vertcal Blank bit has already toggled. * (We've actually set the digitizer so that the Field bit * toggles on line 2.) Tell the encoder to capture 23-2+1=22 * lines per field, since we want lines 6 through 23. */ lines = cx->is_60hz ? (21 - 4 + 1) * 2 : (23 - 2 + 1) * 2; } data[0] = s->handle; /* Lines per field */ data[1] = (lines / 2) | ((lines / 2) << 16); /* bytes per line */ data[2] = (raw ? vbi_active_samples : (cx->is_60hz ? vbi_hblank_samples_60Hz : vbi_hblank_samples_50Hz)); /* Every X number of frames a VBI interrupt arrives (frames as in 25 or 30 fps) */ data[3] = 1; /* * Set the SAV/EAV RP codes to look for as start/stop points * when in VIP-1.1 mode */ if (raw) { /* * Start codes for beginning of "active" line in vertical blank * 0x20 ( VerticalBlank ) * 0x60 ( EvenField VerticalBlank ) */ data[4] = 0x20602060; /* * End codes for end of "active" raw lines and regular lines * 0x30 ( VerticalBlank HorizontalBlank) * 0x70 ( EvenField VerticalBlank HorizontalBlank) * 0x90 (Task HorizontalBlank) * 0xd0 (Task EvenField HorizontalBlank) */ data[5] = 0x307090d0; } else { /* * End codes for active video, we want data in the hblank region * 0xb0 (Task 0 VerticalBlank HorizontalBlank) * 0xf0 (Task EvenField VerticalBlank HorizontalBlank) * * Since the V bit is only allowed to toggle in the EAV RP code, * just before the first active region line, these two * are problematic: * 0x90 (Task HorizontalBlank) * 0xd0 (Task EvenField HorizontalBlank) * * We have set the digitzer such that we don't have to worry * about these problem codes. */ data[4] = 0xB0F0B0F0; /* * Start codes for beginning of active line in vertical blank * 0xa0 (Task VerticalBlank ) * 0xe0 (Task EvenField VerticalBlank ) */ data[5] = 0xA0E0A0E0; } CX18_DEBUG_INFO("Setup VBI h: %d lines %x bpl %d fr %d %x %x\n", data[0], data[1], data[2], data[3], data[4], data[5]); cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data); }
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 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 && !cx18_raw_vbi(cx)) 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_stream_put_buf_fw(s, buf); else cx18_push(s, buf, &s->q_full); } 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; }
static size_t cx18_copy_buf_to_user(struct cx18_stream *s, struct cx18_buffer *buf, char __user *ubuf, size_t ucount) { struct cx18 *cx = s->cx; size_t len = buf->bytesused - buf->readpos; if (len > ucount) len = ucount; if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG && !cx18_raw_vbi(cx) && buf != &cx->vbi.sliced_mpeg_buf) { const char *start = buf->buf + buf->readpos; const char *p = start + 1; const u8 *q; u8 ch = cx->search_pack_header ? 0xba : 0xe0; int stuffing, i; while (start + len > p) { q = memchr(p, 0, start + len - p); if (q == NULL) break; p = q + 1; if ((char *)q + 15 >= buf->buf + buf->bytesused || q[1] != 0 || q[2] != 1 || q[3] != ch) continue; if (!cx->search_pack_header) { if ((q[6] & 0xc0) != 0x80) continue; if (((q[7] & 0xc0) == 0x80 && (q[9] & 0xf0) == 0x20) || ((q[7] & 0xc0) == 0xc0 && (q[9] & 0xf0) == 0x30)) { ch = 0xba; cx->search_pack_header = 1; p = q + 9; } continue; } stuffing = q[13] & 7; /* all stuffing bytes must be 0xff */ for (i = 0; i < stuffing; i++) if (q[14 + i] != 0xff) break; if (i == stuffing && (q[4] & 0xc4) == 0x44 && (q[12] & 3) == 3 && q[14 + stuffing] == 0 && q[15 + stuffing] == 0 && q[16 + stuffing] == 1) { cx->search_pack_header = 0; len = (char *)q - start; cx18_setup_sliced_vbi_buf(cx); break; } } } if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) { CX18_DEBUG_WARN("copy %zd bytes to user failed for %s\n", len, s->name); return -EFAULT; } buf->readpos += len; if (s->type == CX18_ENC_STREAM_TYPE_MPG && buf != &cx->vbi.sliced_mpeg_buf) cx->mpg_data_received += len; return len; }
static size_t cx18_copy_buf_to_user(struct cx18_stream *s, struct cx18_buffer *buf, char __user *ubuf, size_t ucount) { struct cx18 *cx = s->cx; size_t len = buf->bytesused - buf->readpos; if (len > ucount) len = ucount; if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG && !cx18_raw_vbi(cx) && buf != &cx->vbi.sliced_mpeg_buf) { /* * Try to find a good splice point in the PS, just before * an MPEG-2 Program Pack start code, and provide only * up to that point to the user, so it's easy to insert VBI data * the next time around. * * This will not work for an MPEG-2 TS and has only been * verified by analysis to work for an MPEG-2 PS. Helen Buus * pointed out this works for the CX23416 MPEG-2 DVD compatible * stream, and research indicates both the MPEG 2 SVCD and DVD * stream types use an MPEG-2 PS container. */ /* * An MPEG-2 Program Stream (PS) is a series of * MPEG-2 Program Packs terminated by an * MPEG Program End Code after the last Program Pack. * A Program Pack may hold a PS System Header packet and any * number of Program Elementary Stream (PES) Packets */ const char *start = buf->buf + buf->readpos; const char *p = start + 1; const u8 *q; u8 ch = cx->search_pack_header ? 0xba : 0xe0; int stuffing, i; while (start + len > p) { /* Scan for a 0 to find a potential MPEG-2 start code */ q = memchr(p, 0, start + len - p); if (q == NULL) break; p = q + 1; /* * Keep looking if not a * MPEG-2 Pack header start code: 0x00 0x00 0x01 0xba * or MPEG-2 video PES start code: 0x00 0x00 0x01 0xe0 */ if ((char *)q + 15 >= buf->buf + buf->bytesused || q[1] != 0 || q[2] != 1 || q[3] != ch) continue; /* If expecting the primary video PES */ if (!cx->search_pack_header) { /* Continue if it couldn't be a PES packet */ if ((q[6] & 0xc0) != 0x80) continue; /* Check if a PTS or PTS & DTS follow */ if (((q[7] & 0xc0) == 0x80 && /* PTS only */ (q[9] & 0xf0) == 0x20) || /* PTS only */ ((q[7] & 0xc0) == 0xc0 && /* PTS & DTS */ (q[9] & 0xf0) == 0x30)) { /* DTS follows */ /* Assume we found the video PES hdr */ ch = 0xba; /* next want a Program Pack*/ cx->search_pack_header = 1; p = q + 9; /* Skip this video PES hdr */ } continue; } /* We may have found a Program Pack start code */ /* Get the count of stuffing bytes & verify them */ stuffing = q[13] & 7; /* all stuffing bytes must be 0xff */ for (i = 0; i < stuffing; i++) if (q[14 + i] != 0xff) break; if (i == stuffing && /* right number of stuffing bytes*/ (q[4] & 0xc4) == 0x44 && /* marker check */ (q[12] & 3) == 3 && /* marker check */ q[14 + stuffing] == 0 && /* PES Pack or Sys Hdr */ q[15 + stuffing] == 0 && q[16 + stuffing] == 1) { /* We declare we actually found a Program Pack*/ cx->search_pack_header = 0; /* expect vid PES */ len = (char *)q - start; cx18_setup_sliced_vbi_buf(cx); break; } } } if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) { CX18_DEBUG_WARN("copy %zd bytes to user failed for %s\n", len, s->name); return -EFAULT; } buf->readpos += len; if (s->type == CX18_ENC_STREAM_TYPE_MPG && buf != &cx->vbi.sliced_mpeg_buf) cx->mpg_data_received += len; return len; }