staticvoid vpfe_ccdc_setwin(struct vpfe_ccdc *ccdc, struct v4l2_rect *image_win, enum ccdc_frmfmt frm_fmt, int bpp)
{ int horz_start, horz_nr_pixels; int vert_start, vert_nr_lines; int val, mid_img;
/* * ppc - per pixel count. indicates how many pixels per cell * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. * raw capture this is 1
*/
horz_start = image_win->left * bpp;
horz_nr_pixels = (image_win->width * bpp) - 1;
vpfe_reg_write(ccdc, (horz_start << VPFE_HORZ_INFO_SPH_SHIFT) |
horz_nr_pixels, VPFE_HORZ_INFO);
vert_start = image_win->top;
if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
vert_nr_lines = (image_win->height >> 1) - 1;
vert_start >>= 1; /* configure VDINT0 */
val = (vert_start << VPFE_VDINT_VDINT0_SHIFT);
} else {
vert_nr_lines = image_win->height - 1; /* * configure VDINT0 and VDINT1. VDINT1 will be at half * of image height
*/
mid_img = vert_start + (image_win->height / 2);
val = (vert_start << VPFE_VDINT_VDINT0_SHIFT) |
(mid_img & VPFE_VDINT_VDINT1_MASK);
}
/* * vpfe_ccdc_restore_defaults() * This function will write defaults to all CCDC registers
*/ staticvoid vpfe_ccdc_restore_defaults(struct vpfe_ccdc *ccdc)
{ int i;
/* Disable CCDC */
vpfe_pcr_enable(ccdc, 0);
/* set all registers to default value */ for (i = 4; i <= 0x94; i += 4)
vpfe_reg_write(ccdc, 0, i);
pcr = vpfe_reg_read(ccdc, VPFE_PCR); if (pcr)
vpfe_dbg(1, vpfe, "VPFE_PCR is still set (%x)", pcr);
dma_cntl = vpfe_reg_read(ccdc, VPFE_DMA_CNTL); if ((dma_cntl & VPFE_DMA_CNTL_OVERFLOW))
vpfe_dbg(1, vpfe, "VPFE_DMA_CNTL_OVERFLOW is still set (%x)",
dma_cntl);
/* Disable CCDC by resetting all register to default POR values */
vpfe_ccdc_restore_defaults(ccdc);
/* Disabled the module at the CONFIG level */
vpfe_config_enable(ccdc, 0);
if (ccdc->ccdc_cfg.if_type != VPFE_RAW_BAYER) return -EINVAL;
x = copy_from_user(&raw_params, params, sizeof(raw_params)); if (x) {
vpfe_dbg(1, vpfe, "%s: error in copying ccdc params, %d\n",
__func__, x); return -EFAULT;
}
if (!vpfe_ccdc_validate_param(ccdc, &raw_params)) {
vpfe_ccdc_update_raw_params(ccdc, &raw_params); return 0;
}
return -EINVAL;
}
/* * vpfe_ccdc_config_ycbcr() * This function will configure CCDC for YCbCr video capture
*/ staticvoid vpfe_ccdc_config_ycbcr(struct vpfe_ccdc *ccdc)
{ struct ccdc_params_ycbcr *params = &ccdc->ccdc_cfg.ycbcr;
u32 syn_mode;
/* * first restore the CCDC registers to default values * This is important since we assume default values to be set in * a lot of registers that we didn't touch
*/
vpfe_ccdc_restore_defaults(ccdc);
/* * configure pixel format, frame format, configure video frame * format, enable output to SDRAM, enable internal timing generator * and 8bit pack mode
*/
syn_mode = (((params->pix_fmt & VPFE_SYN_MODE_INPMOD_MASK) <<
VPFE_SYN_MODE_INPMOD_SHIFT) |
((params->frm_fmt & VPFE_SYN_FLDMODE_MASK) <<
VPFE_SYN_FLDMODE_SHIFT) | VPFE_VDHDEN_ENABLE |
VPFE_WEN_ENABLE | VPFE_DATA_PACK_ENABLE);
/* configure video window */
vpfe_ccdc_setwin(ccdc, ¶ms->win,
params->frm_fmt, params->bytesperpixel);
/* * configure the order of y cb cr in SDRAM, and disable latch * internal register on vsync
*/ if (ccdc->ccdc_cfg.if_type == VPFE_BT656_10BIT)
vpfe_reg_write(ccdc,
(params->pix_order << VPFE_CCDCFG_Y8POS_SHIFT) |
VPFE_LATCH_ON_VSYNC_DISABLE |
VPFE_CCDCFG_BW656_10BIT, VPFE_CCDCFG); else
vpfe_reg_write(ccdc,
(params->pix_order << VPFE_CCDCFG_Y8POS_SHIFT) |
VPFE_LATCH_ON_VSYNC_DISABLE, VPFE_CCDCFG);
/* * configure the horizontal line offset. This should be a * on 32 byte boundary. So clear LSB 5 bits
*/
vpfe_reg_write(ccdc, params->bytesperline, VPFE_HSIZE_OFF);
/* configure the memory line offset */ if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) /* two fields are interleaved in memory */
vpfe_reg_write(ccdc, VPFE_SDOFST_FIELD_INTERLEAVED,
VPFE_SDOFST);
}
if (!bclamp->enable) { /* configure DCSub */
val = (bclamp->dc_sub) & VPFE_BLK_DC_SUB_MASK;
vpfe_reg_write(ccdc, val, VPFE_DCSUB);
vpfe_reg_write(ccdc, VPFE_CLAMP_DEFAULT_VAL, VPFE_CLAMP); return;
} /* * Configure gain, Start pixel, No of line to be avg, * No of pixel/line to be avg, & Enable the Black clamping
*/
val = ((bclamp->sgain & VPFE_BLK_SGAIN_MASK) |
((bclamp->start_pixel & VPFE_BLK_ST_PXL_MASK) <<
VPFE_BLK_ST_PXL_SHIFT) |
((bclamp->sample_ln & VPFE_BLK_SAMPLE_LINE_MASK) <<
VPFE_BLK_SAMPLE_LINE_SHIFT) |
((bclamp->sample_pixel & VPFE_BLK_SAMPLE_LN_MASK) <<
VPFE_BLK_SAMPLE_LN_SHIFT) | VPFE_BLK_CLAMP_ENABLE);
vpfe_reg_write(ccdc, val, VPFE_CLAMP); /* If Black clamping is enable then make dcsub 0 */
vpfe_reg_write(ccdc, VPFE_DCSUB_DEFAULT_VAL, VPFE_DCSUB);
}
/* Enable and configure aLaw register if needed */ if (config_params->alaw.enable) {
val = ((config_params->alaw.gamma_wd &
VPFE_ALAW_GAMMA_WD_MASK) | VPFE_ALAW_ENABLE);
vpfe_reg_write(ccdc, val, VPFE_ALAW);
vpfe_dbg(3, vpfe, "\nWriting 0x%x to ALAW...\n", val);
}
/* Configure video window */
vpfe_ccdc_setwin(ccdc, ¶ms->win, params->frm_fmt,
params->bytesperpixel);
/* Configure Black Clamp */
vpfe_ccdc_config_black_clamp(ccdc, &config_params->blk_clamp);
/* Configure Black level compensation */
vpfe_ccdc_config_black_compense(ccdc, &config_params->blk_comp);
/* If data size is 8 bit then pack the data */ if ((config_params->data_sz == VPFE_CCDC_DATA_8BITS) ||
config_params->alaw.enable)
syn_mode |= VPFE_DATA_PACK_ENABLE;
/* * Configure Horizontal offset register. If pack 8 is enabled then * 1 pixel will take 1 byte
*/
vpfe_reg_write(ccdc, params->bytesperline, VPFE_HSIZE_OFF);
vpfe_dbg(3, vpfe, "Writing %d (%x) to HSIZE_OFF\n",
params->bytesperline, params->bytesperline);
/* Set value for SDOFST */ if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { if (params->image_invert_enable) { /* For interlace inverse mode */
vpfe_reg_write(ccdc, VPFE_INTERLACED_IMAGE_INVERT,
VPFE_SDOFST);
} else { /* For interlace non inverse mode */
vpfe_reg_write(ccdc, VPFE_INTERLACED_NO_IMAGE_INVERT,
VPFE_SDOFST);
}
} elseif (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
vpfe_reg_write(ccdc, VPFE_PROGRESSIVE_NO_IMAGE_INVERT,
VPFE_SDOFST);
}
if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) {
ccdc->ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; /* * Need to clear it in case it was left on * after the last capture.
*/
ccdc->ccdc_cfg.bayer.config_params.alaw.enable = 0;
switch (pixfmt) { case V4L2_PIX_FMT_SBGGR8:
ccdc->ccdc_cfg.bayer.config_params.alaw.enable = 1; break;
case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_RGB565X: break;
case V4L2_PIX_FMT_SBGGR16: default: return -EINVAL;
}
} else { switch (pixfmt) { case V4L2_PIX_FMT_YUYV:
ccdc->ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; break;
case V4L2_PIX_FMT_UYVY:
ccdc->ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; break;
switch (params->if_type) { case VPFE_BT656: case VPFE_YCBCR_SYNC_16: case VPFE_YCBCR_SYNC_8: case VPFE_BT656_10BIT:
ccdc->ccdc_cfg.ycbcr.vd_pol = params->vdpol;
ccdc->ccdc_cfg.ycbcr.hd_pol = params->hdpol; break;
if (vpfe_ccdc_set_pixel_format(&vpfe->ccdc,
vpfe->fmt.fmt.pix.pixelformat) < 0) {
vpfe_err(vpfe, "couldn't set pix format in ccdc\n"); return -EINVAL;
}
switch (vpfe->fmt.fmt.pix.field) { case V4L2_FIELD_INTERLACED: /* do nothing, since it is default */
ret = vpfe_ccdc_set_buftype(
&vpfe->ccdc,
CCDC_BUFTYPE_FLD_INTERLEAVED); break;
case V4L2_FIELD_NONE:
frm_fmt = CCDC_FRMFMT_PROGRESSIVE; /* buffer type only applicable for interlaced scan */ break;
case V4L2_FIELD_SEQ_TB:
ret = vpfe_ccdc_set_buftype(
&vpfe->ccdc,
CCDC_BUFTYPE_FLD_SEPARATED); break;
/* * vpfe_config_image_format() * For a given standard, this functions sets up the default * pix format & crop values in the vpfe device and ccdc. It first * starts with defaults based values from the standard table. * It then checks if sub device supports get_fmt and then override the * values based on that.Sets crop values to match with scan resolution * starting at 0,0. It calls vpfe_config_ccdc_image_format() set the * values in ccdc
*/ staticint vpfe_config_image_format(struct vpfe_device *vpfe,
v4l2_std_id std_id)
{ struct vpfe_fmt *fmt; struct v4l2_mbus_framefmt mbus_fmt; int i, ret;
for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) { if (vpfe_standards[i].std_id & std_id) {
vpfe->std_info.active_pixels =
vpfe_standards[i].width;
vpfe->std_info.active_lines =
vpfe_standards[i].height;
vpfe->std_info.frame_format =
vpfe_standards[i].frame_format;
vpfe->std_index = i;
break;
}
}
if (i == ARRAY_SIZE(vpfe_standards)) {
vpfe_err(vpfe, "standard not supported\n"); return -EINVAL;
}
ret = __subdev_get_format(vpfe, &mbus_fmt); if (ret) return ret;
fmt = find_format_by_code(vpfe, mbus_fmt.code); if (!fmt) {
vpfe_dbg(3, vpfe, "mbus code format (0x%08x) not found.\n",
mbus_fmt.code); return -EINVAL;
}
/* Save current subdev format */
v4l2_fill_pix_format(&vpfe->fmt.fmt.pix, &mbus_fmt);
vpfe->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vpfe->fmt.fmt.pix.pixelformat = fmt->fourcc;
vpfe_calc_format_size(vpfe, fmt, &vpfe->fmt);
vpfe->current_vpfe_fmt = fmt;
/* Update the crop window based on found values */
vpfe->crop.top = 0;
vpfe->crop.left = 0;
vpfe->crop.width = mbus_fmt.width;
vpfe->crop.height = mbus_fmt.height;
return vpfe_config_ccdc_image_format(vpfe);
}
staticint vpfe_initialize_device(struct vpfe_device *vpfe)
{ struct vpfe_subdev_info *sdinfo; int ret;
sdinfo = &vpfe->cfg->sub_devs[0];
sdinfo->sd = vpfe->sd[0];
vpfe->current_input = 0;
vpfe->std_index = 0; /* Configure the default format information */
ret = vpfe_config_image_format(vpfe,
vpfe_standards[vpfe->std_index].std_id); if (ret) return ret;
ret = pm_runtime_resume_and_get(vpfe->pdev); if (ret < 0) return ret;
vpfe_config_enable(&vpfe->ccdc, 1);
vpfe_ccdc_restore_defaults(&vpfe->ccdc);
/* Clear all VPFE interrupts */
vpfe_clear_intr(&vpfe->ccdc, -1);
return ret;
}
/* * vpfe_release : This function is based on the vb2_fop_release * helper function. * It has been augmented to handle module power management, * by disabling/enabling h/w module fcntl clock when necessary.
*/ staticint vpfe_release(struct file *file)
{ struct vpfe_device *vpfe = video_drvdata(file); bool fh_singular; int ret;
mutex_lock(&vpfe->lock);
/* Save the singular status before we call the clean-up helper */
fh_singular = v4l2_fh_is_singular_file(file);
/* the release helper will cleanup any on-going streaming */
ret = _vb2_fop_release(file, NULL);
/* * If this was the last open file. * Then de-initialize hw module.
*/ if (fh_singular)
vpfe_ccdc_close(&vpfe->ccdc, vpfe->pdev);
mutex_unlock(&vpfe->lock);
return ret;
}
/* * vpfe_open : This function is based on the v4l2_fh_open helper function. * It has been augmented to handle module power management, * by disabling/enabling h/w module fcntl clock when necessary.
*/ staticint vpfe_open(struct file *file)
{ struct vpfe_device *vpfe = video_drvdata(file); int ret;
mutex_lock(&vpfe->lock);
ret = v4l2_fh_open(file); if (ret) {
vpfe_err(vpfe, "v4l2_fh_open failed\n"); goto unlock;
}
if (!v4l2_fh_is_singular_file(file)) goto unlock;
if (vpfe_initialize_device(vpfe)) {
v4l2_fh_release(file);
ret = -ENODEV;
}
unlock:
mutex_unlock(&vpfe->lock); return ret;
}
/** * vpfe_schedule_next_buffer: set next buffer address for capture * @vpfe : ptr to vpfe device * * This function will get next buffer from the dma queue and * set the buffer address in the vpfe register for capture. * the buffer is marked active
*/ staticvoid vpfe_schedule_next_buffer(struct vpfe_device *vpfe)
{
dma_addr_t addr;
spin_lock(&vpfe->dma_queue_lock); if (list_empty(&vpfe->dma_queue)) {
spin_unlock(&vpfe->dma_queue_lock); return;
}
/* * vpfe_process_buffer_complete: process a completed buffer * @vpfe : ptr to vpfe device * * 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
*/ staticinlinevoid vpfe_process_buffer_complete(struct vpfe_device *vpfe)
{
vpfe->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns();
vpfe->cur_frm->vb.field = vpfe->fmt.fmt.pix.field;
vpfe->cur_frm->vb.sequence = vpfe->sequence++;
vb2_buffer_done(&vpfe->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
vpfe->cur_frm = vpfe->next_frm;
}
staticvoid vpfe_handle_interlaced_irq(struct vpfe_device *vpfe, enum v4l2_field field)
{ int fid;
/* interlaced or TB capture check which field * we are in hardware
*/
fid = vpfe_ccdc_getfid(&vpfe->ccdc);
/* switch the software maintained field id */
vpfe->field ^= 1; if (fid == vpfe->field) { /* we are in-sync here,continue */ if (fid == 0) { /* * One frame is just being captured. If the * next frame is available, release the * current frame and move on
*/ if (vpfe->cur_frm != vpfe->next_frm)
vpfe_process_buffer_complete(vpfe);
if (vpfe->stopping) return;
/* * based on whether the two fields are stored * interleave or separately in memory, * reconfigure the CCDC memory address
*/ if (field == V4L2_FIELD_SEQ_TB)
vpfe_schedule_bottom_field(vpfe);
} else { /* * if one field is just being captured configure * the next frame get the next frame from the empty * queue if no frame is available hold on to the * current buffer
*/ if (vpfe->cur_frm == vpfe->next_frm)
vpfe_schedule_next_buffer(vpfe);
}
} elseif (fid == 0) { /* * out of sync. Recover from any hardware out-of-sync. * May loose one frame
*/
vpfe->field = fid;
}
}
/* * vpfe_isr : ISR handler for vpfe capture (VINT0) * @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 VPFE registers
*/ static irqreturn_t vpfe_isr(int irq, void *dev)
{ struct vpfe_device *vpfe = (struct vpfe_device *)dev; enum v4l2_field field = vpfe->fmt.fmt.pix.field; int intr_status, stopping = vpfe->stopping;
/* get the format set at output pad of the adjacent subdev */ staticint __subdev_get_format(struct vpfe_device *vpfe, struct v4l2_mbus_framefmt *fmt)
{ struct v4l2_subdev *sd = vpfe->current_subdev->sd; struct v4l2_subdev_format sd_fmt = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
.pad = 0,
}; struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; int ret;
ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt); if (ret) return ret;
if (!found) { /* use existing values as default */
f->fmt.pix.width = vpfe->fmt.fmt.pix.width;
f->fmt.pix.height = vpfe->fmt.fmt.pix.height;
}
/* * Use current colorspace for now, it will get * updated properly during s_fmt
*/
f->fmt.pix.colorspace = vpfe->fmt.fmt.pix.colorspace; return vpfe_calc_format_size(vpfe, fmt, f);
}
ret = __subdev_set_format(vpfe, &mbus_fmt); if (ret) return ret;
/* Just double check nothing has gone wrong */ if (mbus_fmt.code != f->code) {
vpfe_dbg(3, vpfe, "%s subdev changed format on us, this should not happen\n",
__func__); return -EINVAL;
}
/* * vpfe_get_subdev_input_index - Get subdev index and subdev input index for a * given app input index
*/ staticint
vpfe_get_subdev_input_index(struct vpfe_device *vpfe, int *subdev_index, int *subdev_input_index, int app_input_index)
{ int i, j = 0;
for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) { if (app_input_index < (j + 1)) {
*subdev_index = i;
*subdev_input_index = app_input_index - j; return 0;
}
j++;
} return -EINVAL;
}
/* * vpfe_get_app_input - Get app input index for a given subdev input index * driver stores the input index of the current sub device and translate it * when application request the current input
*/ staticint vpfe_get_app_input_index(struct vpfe_device *vpfe, int *app_input_index)
{ struct vpfe_config *cfg = vpfe->cfg; struct vpfe_subdev_info *sdinfo; struct i2c_client *client; struct i2c_client *curr_client; int i, j = 0;
curr_client = v4l2_get_subdevdata(vpfe->current_subdev->sd); for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) {
sdinfo = &cfg->sub_devs[i];
client = v4l2_get_subdevdata(sdinfo->sd); if (client->addr == curr_client->addr &&
client->adapter->nr == curr_client->adapter->nr) { if (vpfe->current_input >= 1) return -1;
*app_input_index = j + vpfe->current_input; return 0;
}
j++;
} return -EINVAL;
}
if (vpfe_get_subdev_input_index(vpfe, &subdev, &index,
inp->index) < 0) {
vpfe_dbg(1, vpfe, "input information not found for the subdev\n"); return -EINVAL;
}
sdinfo = &vpfe->cfg->sub_devs[subdev];
*inp = sdinfo->inputs[index];
/* set the bus/interface parameter for the sub device in ccdc */
ret = vpfe_ccdc_set_hw_if_params(&vpfe->ccdc, &sdinfo->vpfe_param); if (ret) return ret;
/* set the default image parameters in the device */ return vpfe_config_image_format(vpfe,
vpfe_standards[vpfe->std_index].std_id);
sdinfo = vpfe->current_subdev; if (!(sdinfo->inputs[0].capabilities & V4L2_IN_CAP_STD)) return -ENODATA;
/* if trying to set the same std then nothing to do */ if (vpfe_standards[vpfe->std_index].std_id == std_id) return 0;
/* If streaming is started, return error */ if (vb2_is_busy(&vpfe->buffer_queue)) {
vpfe_err(vpfe, "%s device busy\n", __func__);
ret = -EBUSY; return ret;
}
ret = v4l2_device_call_until_err(&vpfe->v4l2_dev, sdinfo->grp_id,
video, s_std, std_id); if (ret < 0) {
vpfe_err(vpfe, "Failed to set standard\n"); return ret;
}
ret = vpfe_config_image_format(vpfe, std_id);
sdinfo = vpfe->current_subdev; if (sdinfo->inputs[0].capabilities != V4L2_IN_CAP_STD) return -ENODATA;
*std_id = vpfe_standards[vpfe->std_index].std_id;
return 0;
}
/* * vpfe_calculate_offsets : This function calculates buffers offset * for top and bottom field
*/ staticvoid vpfe_calculate_offsets(struct vpfe_device *vpfe)
{ struct v4l2_rect image_win;
/* * vpfe_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 vpfe_queue_setup(struct vb2_queue *vq, unsignedint *nbuffers, unsignedint *nplanes, unsignedint sizes[], struct device *alloc_devs[])
{ struct vpfe_device *vpfe = vb2_get_drv_priv(vq); unsigned size = vpfe->fmt.fmt.pix.sizeimage; unsignedint q_num_bufs = vb2_get_num_buffers(vq);
/* Calculate field offset */
vpfe_calculate_offsets(vpfe);
return 0;
}
/* * vpfe_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 vpfe_buffer_prepare(struct vb2_buffer *vb)
{ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vpfe_device *vpfe = vb2_get_drv_priv(vb->vb2_queue);
if (vpfe->ccdc.ccdc_cfg.if_type == VPFE_RAW_BAYER)
vpfe_ccdc_config_raw(&vpfe->ccdc); else
vpfe_ccdc_config_ycbcr(&vpfe->ccdc);
/* Get the next frame from the buffer queue */
vpfe->next_frm = list_entry(vpfe->dma_queue.next, struct vpfe_cap_buffer, list);
vpfe->cur_frm = vpfe->next_frm; /* Remove buffer from the buffer queue */
list_del(&vpfe->cur_frm->list);
spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags);
/* * vpfe_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 vpfe_stop_streaming(struct vb2_queue *vq)
{ struct vpfe_device *vpfe = vb2_get_drv_priv(vq); struct vpfe_subdev_info *sdinfo; int ret;
vpfe_pcr_enable(&vpfe->ccdc, 0);
/* Wait for the last frame to be captured */
vpfe->stopping = true;
wait_for_completion_timeout(&vpfe->capture_stop,
msecs_to_jiffies(250));
vpfe_detach_irq(vpfe);
sdinfo = vpfe->current_subdev;
ret = v4l2_subdev_call(sdinfo->sd, video, s_stream, 0); if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
vpfe_dbg(1, vpfe, "stream off failed in subdev\n");
/* release all active buffers */
vpfe_return_all_buffers(vpfe, VB2_BUF_STATE_ERROR);
}
/* * vpfe_probe : This function creates device entries by register * itself to the V4L2 driver and initializes fields of each * device objects
*/ staticint vpfe_probe(struct platform_device *pdev)
{ struct vpfe_config *vpfe_cfg; struct vpfe_device *vpfe; struct vpfe_ccdc *ccdc; int ret;
vpfe = devm_kzalloc(&pdev->dev, sizeof(*vpfe), GFP_KERNEL); if (!vpfe) return -ENOMEM;
vpfe->pdev = &pdev->dev;
ret = v4l2_device_register(&pdev->dev, &vpfe->v4l2_dev); if (ret) {
vpfe_err(vpfe, "Unable to register v4l2 device.\n"); return ret;
}
vpfe_cfg = vpfe_get_pdata(vpfe); if (!vpfe_cfg) {
dev_err(&pdev->dev, "No platform data\n");
ret = -EINVAL; goto probe_out_cleanup;
}
vpfe->cfg = vpfe_cfg;
ccdc = &vpfe->ccdc;
ccdc->ccdc_cfg.base_addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ccdc->ccdc_cfg.base_addr)) {
ret = PTR_ERR(ccdc->ccdc_cfg.base_addr); goto probe_out_cleanup;
}
ret = platform_get_irq(pdev, 0); if (ret < 0) goto probe_out_cleanup;
vpfe->irq = ret;
ret = devm_request_irq(vpfe->pdev, vpfe->irq, vpfe_isr, 0, "vpfe_capture0", vpfe); if (ret) {
dev_err(&pdev->dev, "Unable to request interrupt\n");
ret = -EINVAL; goto probe_out_cleanup;
}
/* set the driver data in platform device */
platform_set_drvdata(pdev, vpfe); /* Enabling module functional clock */
pm_runtime_enable(&pdev->dev);
/* for now just enable it here instead of waiting for the open */
ret = pm_runtime_resume_and_get(&pdev->dev); if (ret < 0) {
vpfe_err(vpfe, "Unable to resume device.\n"); goto probe_out_cleanup;
}
vpfe_ccdc_config_defaults(ccdc);
pm_runtime_put_sync(&pdev->dev);
vpfe->sd = devm_kcalloc(&pdev->dev,
ARRAY_SIZE(vpfe->cfg->asd), sizeof(struct v4l2_subdev *),
GFP_KERNEL); if (!vpfe->sd) {
ret = -ENOMEM; goto probe_out_cleanup;
}
vpfe->notifier.ops = &vpfe_async_ops;
ret = v4l2_async_nf_register(&vpfe->notifier); if (ret) {
vpfe_err(vpfe, "Error registering async notifier\n");
ret = -EINVAL; goto probe_out_cleanup;
}
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.