/** * vpif_buffer_prepare : callback function for buffer prepare * @vb: ptr to vb2_buffer * * This is the callback function for buffer prepare when vb2_qbuf() * function is called. The buffer is prepared and user space virtual address * or user address is converted into physical address
*/ staticint vpif_buffer_prepare(struct vb2_buffer *vb)
{ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vb2_queue *q = vb->vb2_queue; struct channel_obj *ch = vb2_get_drv_priv(q); struct common_obj *common; unsignedlong addr;
/** * vpif_buffer_queue_setup : Callback function for buffer setup. * @vq: vb2_queue ptr * @nbuffers: ptr to number of buffers requested by application * @nplanes: contains number of distinct video planes needed to hold a frame * @sizes: contains the size (in bytes) of each plane. * @alloc_devs: ptr to allocation context * * This callback function is called when reqbuf() is called to adjust * the buffer count and buffer size
*/ staticint vpif_buffer_queue_setup(struct vb2_queue *vq, unsignedint *nbuffers, unsignedint *nplanes, unsignedint sizes[], struct device *alloc_devs[])
{ struct channel_obj *ch = vb2_get_drv_priv(vq); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; unsigned size = common->fmt.fmt.pix.sizeimage; unsignedint q_num_bufs = vb2_get_num_buffers(vq);
vpif_dbg(2, debug, "vpif_buffer_setup\n");
if (*nplanes) { if (sizes[0] < size) return -EINVAL;
size = sizes[0];
}
spin_lock_irqsave(&common->irqlock, flags); /* add the buffer to the DMA queue */
list_add_tail(&buf->list, &common->dma_queue);
spin_unlock_irqrestore(&common->irqlock, flags);
}
/** * vpif_start_streaming : Starts the DMA engine for streaming * @vq: ptr to vb2_buffer * @count: number of buffers
*/ staticint vpif_start_streaming(struct vb2_queue *vq, unsignedint count)
{ struct vpif_capture_config *vpif_config_data =
vpif_dev->platform_data; struct channel_obj *ch = vb2_get_drv_priv(vq); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; struct vpif_params *vpif = &ch->vpifparams; struct vpif_cap_buffer *buf, *tmp; unsignedlong addr, flags; int ret;
/* Initialize field_id */
ch->field_id = 0;
/* configure 1 or 2 channel mode */ if (vpif_config_data->setup_input_channel_mode) {
ret = vpif_config_data->
setup_input_channel_mode(vpif->std_info.ycmux_mode); if (ret < 0) {
vpif_dbg(1, debug, "can't set vpif channel mode\n"); goto err;
}
}
ret = v4l2_subdev_call(ch->sd, video, s_stream, 1); if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) {
vpif_dbg(1, debug, "stream on failed in subdev\n"); goto err;
}
/* Call vpif_set_params function to set the parameters and addresses */
ret = vpif_set_video_params(vpif, ch->channel_id); if (ret < 0) {
vpif_dbg(1, debug, "can't set video params\n"); goto err;
}
ycmux_mode = ret;
vpif_config_addr(ch, ret);
/* Get the next frame from the buffer queue */
spin_lock_irqsave(&common->irqlock, flags);
common->cur_frm = common->next_frm = list_entry(common->dma_queue.next, struct vpif_cap_buffer, list); /* Remove buffer from the buffer queue */
list_del(&common->cur_frm->list);
spin_unlock_irqrestore(&common->irqlock, flags);
/** * vpif_stop_streaming : Stop the DMA engine * @vq: ptr to vb2_queue * * This callback stops the DMA engine and any remaining buffers * in the DMA queue are released.
*/ staticvoid vpif_stop_streaming(struct vb2_queue *vq)
{ struct channel_obj *ch = vb2_get_drv_priv(vq); struct common_obj *common; unsignedlong flags; int ret;
common = &ch->common[VPIF_VIDEO_INDEX];
/* Disable channel as per its device type and channel id */ if (VPIF_CHANNEL0_VIDEO == ch->channel_id) {
enable_channel0(0);
channel0_intr_enable(0);
} if (VPIF_CHANNEL1_VIDEO == ch->channel_id ||
ycmux_mode == 2) {
enable_channel1(0);
channel1_intr_enable(0);
}
ycmux_mode = 0;
ret = v4l2_subdev_call(ch->sd, video, s_stream, 0); if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
vpif_dbg(1, debug, "stream off failed in subdev\n");
/* release all active buffers */ if (common->cur_frm == common->next_frm) {
vb2_buffer_done(&common->cur_frm->vb.vb2_buf,
VB2_BUF_STATE_ERROR);
} else { if (common->cur_frm)
vb2_buffer_done(&common->cur_frm->vb.vb2_buf,
VB2_BUF_STATE_ERROR); if (common->next_frm)
vb2_buffer_done(&common->next_frm->vb.vb2_buf,
VB2_BUF_STATE_ERROR);
}
/** * vpif_process_buffer_complete: process a completed buffer * @common: ptr to common channel object * * This function time stamp the buffer and mark it as DONE. It also * wake up any process waiting on the QUEUE and set the next buffer * as current
*/ staticvoid vpif_process_buffer_complete(struct common_obj *common)
{
common->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns();
vb2_buffer_done(&common->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); /* Make curFrm pointing to nextFrm */
common->cur_frm = common->next_frm;
}
/** * vpif_schedule_next_buffer: set next buffer address for capture * @common : ptr to common channel object * * This function will get next buffer from the dma queue and * set the buffer address in the vpif register for capture. * the buffer is marked active
*/ staticvoid vpif_schedule_next_buffer(struct common_obj *common)
{ unsignedlong addr = 0;
spin_lock(&common->irqlock);
common->next_frm = list_entry(common->dma_queue.next, struct vpif_cap_buffer, list); /* Remove that buffer from the buffer queue */
list_del(&common->next_frm->list);
spin_unlock(&common->irqlock);
addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb.vb2_buf, 0);
/* Set top and bottom field addresses in VPIF registers */
common->set_addr(addr + common->ytop_off,
addr + common->ybtm_off,
addr + common->ctop_off,
addr + common->cbtm_off);
}
/** * vpif_channel_isr : ISR handler for vpif capture * @irq: irq number * @dev_id: dev_id ptr * * It changes status of the captured buffer, takes next buffer from the queue * and sets its address in VPIF registers
*/ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
{ struct vpif_device *dev = &vpif_obj; struct common_obj *common; struct channel_obj *ch; int channel_id; int fid = -1, i;
channel_id = *(int *)(dev_id); if (!vpif_intr_status(channel_id)) return IRQ_NONE;
ch = dev->dev[channel_id];
for (i = 0; i < VPIF_NUMBER_OF_OBJECTS; i++) {
common = &ch->common[i]; /* skip If streaming is not started in this channel */ /* Check the field format */ if (1 == ch->vpifparams.std_info.frm_fmt ||
common->fmt.fmt.pix.field == V4L2_FIELD_NONE) { /* Progressive mode */
spin_lock(&common->irqlock); if (list_empty(&common->dma_queue)) {
spin_unlock(&common->irqlock); continue;
}
spin_unlock(&common->irqlock);
if (!channel_first_int[i][channel_id])
vpif_process_buffer_complete(common);
channel_first_int[i][channel_id] = 0;
vpif_schedule_next_buffer(common);
channel_first_int[i][channel_id] = 0;
} else { /** * Interlaced mode. If it is first interrupt, ignore * it
*/ if (channel_first_int[i][channel_id]) {
channel_first_int[i][channel_id] = 0; continue;
} if (0 == i) {
ch->field_id ^= 1; /* Get field id from VPIF registers */
fid = vpif_channel_getfid(ch->channel_id); if (fid != ch->field_id) { /** * If field id does not match stored * field id, make them in sync
*/ if (0 == fid)
ch->field_id = fid; return IRQ_HANDLED;
}
} /* device field id and local field id are in sync */ if (0 == fid) { /* this is even field */ if (common->cur_frm == common->next_frm) continue;
/* mark the current buffer as done */
vpif_process_buffer_complete(common);
} elseif (1 == fid) { /* odd field */
spin_lock(&common->irqlock); if (list_empty(&common->dma_queue) ||
(common->cur_frm != common->next_frm)) {
spin_unlock(&common->irqlock); continue;
}
spin_unlock(&common->irqlock);
/** * vpif_update_std_info() - update standard related info * @ch: ptr to channel object * * For a given standard selected by application, update values * in the device data structures
*/ staticint vpif_update_std_info(struct channel_obj *ch)
{ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; struct vpif_params *vpifparams = &ch->vpifparams; conststruct vpif_channel_config_params *config; struct vpif_channel_config_params *std_info = &vpifparams->std_info; struct video_obj *vid_ch = &ch->video; int index; struct v4l2_pix_format *pixfmt = &common->fmt.fmt.pix;
vpif_dbg(2, debug, "vpif_update_std_info\n");
/* * if called after try_fmt or g_fmt, there will already be a size * so use that by default.
*/ if (pixfmt->width && pixfmt->height) { if (pixfmt->field == V4L2_FIELD_ANY ||
pixfmt->field == V4L2_FIELD_NONE)
pixfmt->field = V4L2_FIELD_NONE;
/* * For raw formats from camera sensors, we don't need * the std_info from table lookup, so nothing else to do here.
*/ if (vpifparams->iface.if_type == VPIF_IF_RAW_BAYER) {
memset(std_info, 0, sizeof(struct vpif_channel_config_params));
vpifparams->std_info.capture_format = 1; /* CCD/raw mode */ return 0;
}
}
for (index = 0; index < vpif_ch_params_count; index++) {
config = &vpif_ch_params[index]; if (config->hd_sd == 0) {
vpif_dbg(2, debug, "SD format\n"); if (config->stdid & vid_ch->stdid) {
memcpy(std_info, config, sizeof(*config)); break;
}
} else {
vpif_dbg(2, debug, "HD format\n"); if (!memcmp(&config->dv_timings, &vid_ch->dv_timings, sizeof(vid_ch->dv_timings))) {
memcpy(std_info, config, sizeof(*config)); break;
}
}
}
/* standard not found */ if (index == vpif_ch_params_count) return -EINVAL;
/** * vpif_calculate_offsets : This function calculates buffers offsets * @ch : ptr to channel object * * This function calculates buffer offsets for Y and C in the top and * bottom field
*/ staticvoid vpif_calculate_offsets(struct channel_obj *ch)
{ unsignedint hpitch, sizeimage; struct video_obj *vid_ch = &(ch->video); struct vpif_params *vpifparams = &ch->vpifparams; struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; enum v4l2_field field = common->fmt.fmt.pix.field;
vpif_dbg(2, debug, "vpif_calculate_offsets\n");
if (V4L2_FIELD_ANY == field) { if (vpifparams->std_info.frm_fmt)
vid_ch->buf_field = V4L2_FIELD_NONE; else
vid_ch->buf_field = V4L2_FIELD_INTERLACED;
} else
vid_ch->buf_field = common->fmt.fmt.pix.field;
sizeimage = common->fmt.fmt.pix.sizeimage;
hpitch = common->fmt.fmt.pix.bytesperline;
if ((V4L2_FIELD_NONE == vid_ch->buf_field) ||
(V4L2_FIELD_INTERLACED == vid_ch->buf_field)) { /* Calculate offsets for Y top, Y Bottom, C top and C Bottom */
common->ytop_off = 0;
common->ybtm_off = hpitch;
common->ctop_off = sizeimage / 2;
common->cbtm_off = sizeimage / 2 + hpitch;
} elseif (V4L2_FIELD_SEQ_TB == vid_ch->buf_field) { /* Calculate offsets for Y top, Y Bottom, C top and C Bottom */
common->ytop_off = 0;
common->ybtm_off = sizeimage / 4;
common->ctop_off = sizeimage / 2;
common->cbtm_off = common->ctop_off + sizeimage / 4;
} elseif (V4L2_FIELD_SEQ_BT == vid_ch->buf_field) { /* Calculate offsets for Y top, Y Bottom, C top and C Bottom */
common->ybtm_off = 0;
common->ytop_off = sizeimage / 4;
common->cbtm_off = sizeimage / 2;
common->ctop_off = common->cbtm_off + sizeimage / 4;
} if ((V4L2_FIELD_NONE == vid_ch->buf_field) ||
(V4L2_FIELD_INTERLACED == vid_ch->buf_field))
vpifparams->video_params.storage_mode = 1; else
vpifparams->video_params.storage_mode = 0;
/** * vpif_input_to_subdev() - Maps input to sub device * @vpif_cfg: global config ptr * @chan_cfg: channel config ptr * @input_index: Given input index from application * * lookup the sub device information for a given input index. * we report all the inputs to application. inputs table also * has sub device name for the each input
*/ staticint vpif_input_to_subdev( struct vpif_capture_config *vpif_cfg, struct vpif_capture_chan_config *chan_cfg, int input_index)
{ struct vpif_subdev_info *subdev_info; constchar *subdev_name; int i;
vpif_dbg(2, debug, "vpif_input_to_subdev\n");
if (!chan_cfg) return -1; if (input_index >= chan_cfg->input_count) return -1;
subdev_name = chan_cfg->inputs[input_index].subdev_name; if (!subdev_name) return -1;
/* loop through the sub device list to get the sub device info */ for (i = 0; i < vpif_cfg->subdev_count; i++) {
subdev_info = &vpif_cfg->subdev_info[i]; if (subdev_info && !strcmp(subdev_info->name, subdev_name)) return i;
} return -1;
}
/** * vpif_set_input() - Select an input * @vpif_cfg: global config ptr * @ch: channel * @index: Given input index from application * * Select the given input.
*/ staticint vpif_set_input( struct vpif_capture_config *vpif_cfg, struct channel_obj *ch, int index)
{ struct vpif_capture_chan_config *chan_cfg =
&vpif_cfg->chan_config[ch->channel_id]; struct vpif_subdev_info *subdev_info = NULL; struct v4l2_subdev *sd = NULL;
u32 input = 0, output = 0; int sd_index; int ret;
sd_index = vpif_input_to_subdev(vpif_cfg, chan_cfg, index); if (sd_index >= 0) {
sd = vpif_obj.sd[sd_index];
subdev_info = &vpif_cfg->subdev_info[sd_index];
} else { /* no subdevice, no input to setup */ return 0;
}
/* first setup input path from sub device to vpif */ if (sd && vpif_cfg->setup_input_path) {
ret = vpif_cfg->setup_input_path(ch->channel_id,
subdev_info->name); if (ret < 0) {
vpif_dbg(1, debug, "couldn't setup input path for the" \ " sub device %s, for input index %d\n",
subdev_info->name, index); return ret;
}
}
if (sd) {
input = chan_cfg->inputs[index].input_route;
output = chan_cfg->inputs[index].output_route;
ret = v4l2_subdev_call(sd, video, s_routing,
input, output, 0); if (ret < 0 && ret != -ENOIOCTLCMD) {
vpif_dbg(1, debug, "Failed to set input\n"); return ret;
}
}
ch->input_idx = index;
ch->sd = sd; /* copy interface parameters to vpif */
ch->vpifparams.iface = chan_cfg->vpif_if;
/* update tvnorms from the sub device input info */
ch->video_dev.tvnorms = chan_cfg->inputs[index].input.std; return 0;
}
/** * vpif_querystd() - querystd handler * @file: file ptr * @priv: file handle * @std_id: ptr to std id * * This function is called to detect standard at the selected input
*/ staticint vpif_querystd(struct file *file, void *priv, v4l2_std_id *std_id)
{ struct video_device *vdev = video_devdata(file); struct channel_obj *ch = video_get_drvdata(vdev); int ret;
vpif_dbg(2, debug, "vpif_querystd\n");
/* Call querystd function of decoder device */
ret = v4l2_subdev_call(ch->sd, video, querystd, std_id);
if (ret == -ENOIOCTLCMD || ret == -ENODEV) return -ENODATA; if (ret) {
vpif_dbg(1, debug, "Failed to query standard for sub devices\n"); return ret;
}
if (vb2_is_busy(&common->buffer_queue)) return -EBUSY;
/* Call encoder subdevice function to set the standard */
ch->video.stdid = std_id;
memset(&ch->video.dv_timings, 0, sizeof(ch->video.dv_timings));
/* Get the information about the standard */ if (vpif_update_std_info(ch)) {
vpif_err("Error getting the standard info\n"); return -EINVAL;
}
/* set standard in the sub device */
ret = v4l2_subdev_call(ch->sd, video, s_std, std_id); if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) {
vpif_dbg(1, debug, "Failed to set standard for sub devices\n"); return ret;
} return 0;
}
if (fmt->index != 0) {
vpif_dbg(1, debug, "Invalid format index\n"); return -EINVAL;
}
/* Fill in the information about format */ if (ch->vpifparams.iface.if_type == VPIF_IF_RAW_BAYER)
fmt->pixelformat = V4L2_PIX_FMT_SBGGR8; else
fmt->pixelformat = V4L2_PIX_FMT_NV16; return 0;
}
if (timings->type != V4L2_DV_BT_656_1120) {
vpif_dbg(2, debug, "Timing type not defined\n"); return -EINVAL;
}
if (vb2_is_busy(&common->buffer_queue)) return -EBUSY;
/* Configure subdevice timings, if any */
ret = v4l2_subdev_call(ch->sd, pad, s_dv_timings, 0, timings); if (ret == -ENOIOCTLCMD || ret == -ENODEV)
ret = 0; if (ret < 0) {
vpif_dbg(2, debug, "Error setting custom DV timings\n"); return ret;
}
if (!(timings->bt.width && timings->bt.height &&
(timings->bt.hbackporch ||
timings->bt.hfrontporch ||
timings->bt.hsync) &&
timings->bt.vfrontporch &&
(timings->bt.vbackporch ||
timings->bt.vsync))) {
vpif_dbg(2, debug, "Timings for width, height, horizontal back porch, horizontal sync, horizontal front porch, vertical back porch, vertical sync and vertical back porch must be defined\n"); return -EINVAL;
}
/** * initialize_vpif() - Initialize vpif data structures * * Allocate memory for data structures and initialize them
*/ staticint initialize_vpif(void)
{ int err, i, j; int free_channel_objects_index;
/* Allocate memory for six channel objects */ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
vpif_obj.dev[i] =
kzalloc(sizeof(*vpif_obj.dev[i]), GFP_KERNEL); /* If memory allocation fails, return error */ if (!vpif_obj.dev[i]) {
free_channel_objects_index = i;
err = -ENOMEM; goto vpif_init_free_channel_objects;
}
} return 0;
for (i = 0; i < vpif_obj.config->subdev_count; i++) if (!strcmp(vpif_obj.config->subdev_info[i].name,
subdev->name)) {
vpif_obj.sd[i] = subdev; return 0;
}
probe_out: for (k = 0; k < j; k++) { /* Get the pointer to the channel object */
ch = vpif_obj.dev[k]; /* Unregister video device */
video_unregister_device(&ch->video_dev);
}
/** * vpif_probe : This function probes the vpif capture driver * @pdev: platform device pointer * * This creates device entries by register itself to the V4L2 driver and * initializes fields of each channel objects
*/ static __init int vpif_probe(struct platform_device *pdev)
{ struct vpif_subdev_info *subdevdata; struct i2c_adapter *i2c_adap; int subdev_count; int res_idx = 0; int i, err;
kfree(vpif_obj.sd); /* un-register device */ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { /* Get the pointer to the channel object */
ch = vpif_obj.dev[i]; /* Unregister video device */
video_unregister_device(&ch->video_dev);
kfree(vpif_obj.dev[i]);
}
}
struct common_obj *common; struct channel_obj *ch; int i;
for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { /* Get the pointer to the channel object */
ch = vpif_obj.dev[i];
common = &ch->common[VPIF_VIDEO_INDEX];
if (!vb2_start_streaming_called(&common->buffer_queue)) continue;
for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { /* Get the pointer to the channel object */
ch = vpif_obj.dev[i];
common = &ch->common[VPIF_VIDEO_INDEX];
if (!vb2_start_streaming_called(&common->buffer_queue)) continue;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.