/* * ccdc_print_status - Print current CCDC Module register values. * @ccdc: Pointer to ISP CCDC device. * * Also prints other debug information stored in the CCDC module.
*/ #define CCDC_PRINT_REGISTER(isp, name)\
dev_dbg(isp->dev, "###CCDC "#name"=0x%08x\n", \
isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_##name))
/* * omap3isp_ccdc_busy - Get busy state of the CCDC. * @ccdc: Pointer to ISP CCDC device.
*/ int omap3isp_ccdc_busy(struct isp_ccdc_device *ccdc)
{ struct isp_device *isp = to_isp_device(ccdc);
/* * ccdc_lsc_error_handler - Handle LSC prefetch error scenario. * @ccdc: Pointer to ISP CCDC device. * * Disables LSC, and defers enablement to shadow registers update time.
*/ staticvoid ccdc_lsc_error_handler(struct isp_ccdc_device *ccdc)
{ struct isp_device *isp = to_isp_device(ccdc); /* * From OMAP3 TRM: When this event is pending, the module * goes into transparent mode (output =input). Normal * operation can be resumed at the start of the next frame * after: * 1) Clearing this event * 2) Disabling the LSC module * 3) Enabling it
*/
isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG,
ISPCCDC_LSC_ENABLE);
ccdc->lsc.state = LSC_STATE_STOPPED;
}
/* * ccdc_lsc_config - Configure the LSC module from a userspace request * * Store the request LSC configuration in the LSC engine request pointer. The * configuration will be applied to the hardware when the CCDC will be enabled, * or at the next LSC interrupt if the CCDC is already running.
*/ staticint ccdc_lsc_config(struct isp_ccdc_device *ccdc, struct omap3isp_ccdc_update_config *config)
{ struct isp_device *isp = to_isp_device(ccdc); struct ispccdc_lsc_config_req *req; unsignedlong flags;
u16 update; int ret;
if (update != (OMAP3ISP_CCDC_CONFIG_LSC | OMAP3ISP_CCDC_TBL_LSC)) {
dev_dbg(to_device(ccdc), "%s: Both LSC configuration and table need to be supplied\n",
__func__); return -EINVAL;
}
req = kzalloc(sizeof(*req), GFP_KERNEL); if (req == NULL) return -ENOMEM;
if (config->flag & OMAP3ISP_CCDC_CONFIG_LSC) { if (copy_from_user(&req->config, config->lsc_cfg, sizeof(req->config))) {
ret = -EFAULT; goto done;
}
req->enable = 1;
req->table.addr = dma_alloc_coherent(isp->dev, req->config.size,
&req->table.dma,
GFP_KERNEL); if (req->table.addr == NULL) {
ret = -ENOMEM; goto done;
}
ret = dma_get_sgtable(isp->dev, &req->table.sgt,
req->table.addr, req->table.dma,
req->config.size); if (ret < 0) goto done;
isp_reg_writel(isp, ccdc->fpc.dma, OMAP3_ISP_IOMEM_CCDC,
ISPCCDC_FPC_ADDR); /* The FPNUM field must be set before enabling FPC. */
isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT),
OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC);
isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT) |
ISPCCDC_FPC_FPCEN, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC);
}
/* * ccdc_config - Set CCDC configuration from userspace * @ccdc: Pointer to ISP CCDC device. * @ccdc_struct: Structure containing CCDC configuration sent from userspace. * * Returns 0 if successful, -EINVAL if the pointer to the configuration * structure is null, or the copy_from_user function fails to copy user space * memory to kernel space memory.
*/ staticint ccdc_config(struct isp_ccdc_device *ccdc, struct omap3isp_ccdc_update_config *ccdc_struct)
{ struct isp_device *isp = to_isp_device(ccdc); unsignedlong flags;
if (ccdc->fpc_en) { if (copy_from_user(&fpc, ccdc_struct->fpc, sizeof(fpc))) return -EFAULT;
size = fpc.fpnum * 4;
/* * The table address must be 64-bytes aligned, which is * guaranteed by dma_alloc_coherent().
*/
fpc_new.fpnum = fpc.fpnum;
fpc_new.addr = dma_alloc_coherent(isp->dev, size,
&fpc_new.dma,
GFP_KERNEL); if (fpc_new.addr == NULL) return -ENOMEM;
if (!format->code) { /* Disable the video port when the input format isn't supported. * This is indicated by a pixel code set to 0.
*/
isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG); return;
}
/* * ccdc_config_outlineoffset - Configure memory saving output line offset * @ccdc: Pointer to ISP CCDC device. * @bpl: Number of bytes per line when stored in memory. * @field: Field order when storing interlaced formats in memory. * * Configure the offsets for the line output control: * * - The horizontal line offset is defined as the number of bytes between the * start of two consecutive lines in memory. Set it to the given bytes per * line value. * * - The field offset value is defined as the number of lines to offset the * start of the field identified by FID = 1. Set it to one. * * - The line offset values are defined as the number of lines (as defined by * the horizontal line offset) between the start of two consecutive lines for * all combinations of odd/even lines in odd/even fields. When interleaving * fields set them all to two lines, and to one line otherwise.
*/ staticvoid ccdc_config_outlineoffset(struct isp_ccdc_device *ccdc, unsignedint bpl, enum v4l2_field field)
{ struct isp_device *isp = to_isp_device(ccdc);
u32 sdofst = 0;
switch (field) { case V4L2_FIELD_INTERLACED_TB: case V4L2_FIELD_INTERLACED_BT: /* When interleaving fields in memory offset field one by one * line and set the line offset to two lines.
*/
sdofst |= (1 << ISPCCDC_SDOFST_LOFST0_SHIFT)
| (1 << ISPCCDC_SDOFST_LOFST1_SHIFT)
| (1 << ISPCCDC_SDOFST_LOFST2_SHIFT)
| (1 << ISPCCDC_SDOFST_LOFST3_SHIFT); break;
default: /* In all other cases set the line offsets to one line. */ break;
}
/* * omap3isp_ccdc_max_rate - Calculate maximum input data rate based on the input * @ccdc: Pointer to ISP CCDC device. * @max_rate: Maximum calculated data rate. * * Returns in *max_rate less value between calculated and passed
*/ void omap3isp_ccdc_max_rate(struct isp_ccdc_device *ccdc, unsignedint *max_rate)
{ struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); unsignedint rate;
if (pipe == NULL) return;
/* * TRM says that for parallel sensors the maximum data rate * should be 90% form L3/2 clock, otherwise just L3/2.
*/ if (ccdc->input == CCDC_INPUT_PARALLEL)
rate = pipe->l3_ick / 2 * 9 / 10; else
rate = pipe->l3_ick / 2;
if (format->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
format->code == MEDIA_BUS_FMT_UYVY8_2X8) { /* According to the OMAP3 TRM the input mode only affects SYNC * mode, enabling BT.656 mode should take precedence. However, * in practice setting the input mode to YCbCr data on 8 bits * seems to be required in BT.656 mode. In SYNC mode set it to * YCbCr on 16 bits as the bridge is enabled in that case.
*/ if (ccdc->bt656)
syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR8; else
syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR16;
}
switch (data_size) { case 8:
syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_8; break; case 10:
syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_10; break; case 11:
syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_11; break; case 12:
syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_12; break;
}
if (parcfg && parcfg->data_pol)
syn_mode |= ISPCCDC_SYN_MODE_DATAPOL;
if (parcfg && parcfg->hs_pol)
syn_mode |= ISPCCDC_SYN_MODE_HDPOL;
/* The polarity of the vertical sync signal output by the BT.656 * decoder is not documented and seems to be active low.
*/ if ((parcfg && parcfg->vs_pol) || ccdc->bt656)
syn_mode |= ISPCCDC_SYN_MODE_VDPOL;
if (parcfg && parcfg->fld_pol)
syn_mode |= ISPCCDC_SYN_MODE_FLDPOL;
/* The CCDC_CFG.Y8POS bit is used in YCbCr8 input mode only. The * hardware seems to ignore it in all other input modes.
*/ if (format->code == MEDIA_BUS_FMT_UYVY8_2X8)
isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
ISPCCDC_CFG_Y8POS); else
isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
ISPCCDC_CFG_Y8POS);
/* Enable or disable BT.656 mode, including error correction for the * synchronization codes.
*/ if (ccdc->bt656)
isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF,
ISPCCDC_REC656IF_R656ON | ISPCCDC_REC656IF_ECCFVH); else
isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF,
ISPCCDC_REC656IF_R656ON | ISPCCDC_REC656IF_ECCFVH);
/* CCDC_PAD_SINK */
format = &ccdc->formats[CCDC_PAD_SINK];
/* Compute the lane shifter shift value and enable the bridge when the * input format is a non-BT.656 YUV variant.
*/
fmt_src.pad = pad->index; if (!v4l2_subdev_call(sensor, pad, get_fmt, NULL, &fmt_src)) {
fmt_info = omap3isp_video_format_info(fmt_src.format.code);
depth_in = fmt_info->width;
}
/* Use the raw, unprocessed data when writing to memory. The H3A and * histogram modules are still fed with lens shading corrected data.
*/
syn_mode &= ~ISPCCDC_SYN_MODE_VP2SDR;
/* Mosaic filter */ switch (format->code) { case MEDIA_BUS_FMT_SRGGB10_1X10: case MEDIA_BUS_FMT_SRGGB12_1X12:
ccdc_pattern = ccdc_srggb_pattern; break; case MEDIA_BUS_FMT_SBGGR10_1X10: case MEDIA_BUS_FMT_SBGGR12_1X12:
ccdc_pattern = ccdc_sbggr_pattern; break; case MEDIA_BUS_FMT_SGBRG10_1X10: case MEDIA_BUS_FMT_SGBRG12_1X12:
ccdc_pattern = ccdc_sgbrg_pattern; break; default: /* Use GRBG */
ccdc_pattern = ccdc_sgrbg_pattern; break;
}
ccdc_config_imgattr(ccdc, ccdc_pattern);
/* Generate VD0 on the last line of the image and VD1 on the * 2/3 height line.
*/
isp_reg_writel(isp, ((format->height - 2) << ISPCCDC_VDINT_0_SHIFT) |
((format->height * 2 / 3) << ISPCCDC_VDINT_1_SHIFT),
OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT);
/* CCDC_PAD_SOURCE_OF */
format = &ccdc->formats[CCDC_PAD_SOURCE_OF];
crop = &ccdc->crop;
/* The horizontal coordinates are expressed in pixel clock cycles. We * need two cycles per pixel in BT.656 mode, and one cycle per pixel in * SYNC mode regardless of the format as the bridge is enabled for YUV * formats in that case.
*/ if (ccdc->bt656) {
sph = crop->left * 2;
nph = crop->width * 2 - 1;
} else {
sph = crop->left;
nph = crop->width - 1;
}
/* When interleaving fields enable processing of the field input signal. * This will cause the line output control module to apply the field * offset to field 1.
*/ if (ccdc->formats[CCDC_PAD_SINK].field == V4L2_FIELD_ALTERNATE &&
(format->field == V4L2_FIELD_INTERLACED_TB ||
format->field == V4L2_FIELD_INTERLACED_BT))
syn_mode |= ISPCCDC_SYN_MODE_FLDMODE;
/* The CCDC outputs data in UYVY order by default. Swap bytes to get * YUYV.
*/ if (format->code == MEDIA_BUS_FMT_YUYV8_1X16)
isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
ISPCCDC_CFG_BSWD); else
isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
ISPCCDC_CFG_BSWD);
/* Use PACK8 mode for 1byte per pixel formats. Check for BT.656 mode * explicitly as the driver reports 1X16 instead of 2X8 at the OF pad * for simplicity.
*/ if (omap3isp_video_format_info(format->code)->width <= 8 || ccdc->bt656)
syn_mode |= ISPCCDC_SYN_MODE_PACK8; else
syn_mode &= ~ISPCCDC_SYN_MODE_PACK8;
/* Get last good LSC configuration. If it is not supported for * the current active resolution discard it.
*/ if (ccdc->lsc.active == NULL &&
__ccdc_lsc_configure(ccdc, ccdc->lsc.request) == 0) {
ccdc->lsc.active = ccdc->lsc.request;
} else {
list_add_tail(&ccdc->lsc.request->list, &ccdc->lsc.free_queue);
schedule_work(&ccdc->lsc.table_work);
}
/* * ccdc_sbl_busy - Poll idle state of CCDC and related SBL memory write bits * @ccdc: Pointer to ISP CCDC device. * * Returns zero if the CCDC is idle and the image has been written to * memory, too.
*/ staticint ccdc_sbl_busy(struct isp_ccdc_device *ccdc)
{ struct isp_device *isp = to_isp_device(ccdc);
/* * ccdc_sbl_wait_idle - Wait until the CCDC and related SBL are idle * @ccdc: Pointer to ISP CCDC device. * @max_wait: Max retry count in us for wait for idle/busy transition.
*/ staticint ccdc_sbl_wait_idle(struct isp_ccdc_device *ccdc, unsignedint max_wait)
{ unsignedint wait = 0;
if (max_wait == 0)
max_wait = 10000; /* 10 ms */
for (wait = 0; wait <= max_wait; wait++) { if (!ccdc_sbl_busy(ccdc)) return 0;
rmb();
udelay(1);
}
return -EBUSY;
}
/* ccdc_handle_stopping - Handle CCDC and/or LSC stopping sequence * @ccdc: Pointer to ISP CCDC device. * @event: Pointing which event trigger handler * * Return 1 when the event and stopping request combination is satisfied, * zero otherwise.
*/ staticint ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event)
{ int rval = 0;
if (!(events & IRQ0STATUS_CCDC_LSC_DONE_IRQ)) return;
/* LSC_DONE interrupt occur, there are two cases * 1. stopping for reconfiguration * 2. stopping because of STREAM OFF command
*/
spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
if (ccdc->lsc.state == LSC_STATE_STOPPING)
ccdc->lsc.state = LSC_STATE_STOPPED;
if (ccdc_handle_stopping(ccdc, CCDC_EVENT_LSC_DONE)) goto done;
if (ccdc->lsc.state != LSC_STATE_RECONFIG) goto done;
/* LSC is in STOPPING state, change to the new state */
ccdc->lsc.state = LSC_STATE_STOPPED;
/* This is an exception. Start of frame and LSC_DONE interrupt * have been received on the same time. Skip this event and wait * for better times.
*/ if (events & IRQ0STATUS_HS_VS_IRQ) goto done;
/* The LSC engine is stopped at this point. Enable it if there's a * pending request.
*/ if (ccdc->lsc.request == NULL) goto done;
/* * Check whether the CCDC has captured all fields necessary to complete the * buffer.
*/ staticbool ccdc_has_all_fields(struct isp_ccdc_device *ccdc)
{ struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); struct isp_device *isp = to_isp_device(ccdc); enum v4l2_field of_field = ccdc->formats[CCDC_PAD_SOURCE_OF].field; enum v4l2_field field;
/* When the input is progressive fields don't matter. */ if (of_field == V4L2_FIELD_NONE) returntrue;
/* Read the current field identifier. */
field = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE)
& ISPCCDC_SYN_MODE_FLDSTAT
? V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP;
/* When capturing fields in alternate order just store the current field * identifier in the pipeline.
*/ if (of_field == V4L2_FIELD_ALTERNATE) {
pipe->field = field; returntrue;
}
/* The format is interlaced. Make sure we've captured both fields. */
ccdc->fields |= field == V4L2_FIELD_BOTTOM
? CCDC_FIELD_BOTTOM : CCDC_FIELD_TOP;
if (ccdc->fields != CCDC_FIELD_BOTH) returnfalse;
/* Verify that the field just captured corresponds to the last field * needed based on the desired field order.
*/ if ((of_field == V4L2_FIELD_INTERLACED_TB && field == V4L2_FIELD_TOP) ||
(of_field == V4L2_FIELD_INTERLACED_BT && field == V4L2_FIELD_BOTTOM)) returnfalse;
/* The buffer can be completed, reset the fields for the next buffer. */
ccdc->fields = 0;
/* The CCDC generates VD0 interrupts even when disabled (the datasheet * doesn't explicitly state if that's supposed to happen or not, so it * can be considered as a hardware bug or as a feature, but we have to * deal with it anyway). Disabling the CCDC when no buffer is available * would thus not be enough, we need to handle the situation explicitly.
*/ if (list_empty(&ccdc->video_out.dmaqueue)) return 0;
/* We're in continuous mode, and memory writes were disabled due to a * buffer underrun. Re-enable them now that we have a buffer. The buffer * address has been set in ccdc_video_queue.
*/ if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && ccdc->underrun) {
ccdc->underrun = 0; return 1;
}
/* Wait for the CCDC to become idle. */ if (ccdc_sbl_wait_idle(ccdc, 1000)) {
dev_info(isp->dev, "CCDC won't become idle!\n");
media_entity_enum_set(&isp->crashed, &ccdc->subdev.entity);
omap3isp_pipeline_cancel_stream(pipe); return 0;
}
/* Don't restart CCDC if we're just about to stop streaming. */ if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS &&
ccdc->stopping & CCDC_STOP_REQUEST) return 0;
if (!ccdc_has_all_fields(ccdc)) return 1;
buffer = omap3isp_video_buffer_next(&ccdc->video_out); if (buffer != NULL)
ccdc_set_outaddr(ccdc, buffer->dma);
pipe->state |= ISP_PIPELINE_IDLE_OUTPUT;
if (ccdc->state == ISP_PIPELINE_STREAM_SINGLESHOT &&
isp_pipeline_ready(pipe))
omap3isp_pipeline_set_stream(pipe,
ISP_PIPELINE_STREAM_SINGLESHOT);
return buffer != NULL;
}
/* * ccdc_vd0_isr - Handle VD0 event * @ccdc: Pointer to ISP CCDC device. * * Executes LSC deferred enablement before next frame starts.
*/ staticvoid ccdc_vd0_isr(struct isp_ccdc_device *ccdc)
{ unsignedlong flags; int restart = 0;
/* In BT.656 mode the CCDC doesn't generate an HS/VS interrupt. We thus * need to increment the frame counter here.
*/ if (ccdc->bt656) { struct isp_pipeline *pipe =
to_isp_pipeline(&ccdc->subdev.entity);
atomic_inc(&pipe->frame_number);
}
/* Emulate a VD1 interrupt for BT.656 mode, as we can't stop the CCDC in * the VD1 interrupt handler in that mode without risking a CCDC stall * if a short frame is received.
*/ if (ccdc->bt656) {
spin_lock_irqsave(&ccdc->lock, flags); if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS &&
ccdc->output & CCDC_OUTPUT_MEMORY) { if (ccdc->lsc.state != LSC_STATE_STOPPED)
__ccdc_lsc_enable(ccdc, 0);
__ccdc_enable(ccdc, 0);
}
ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1);
spin_unlock_irqrestore(&ccdc->lock, flags);
}
spin_lock_irqsave(&ccdc->lock, flags); if (ccdc_handle_stopping(ccdc, CCDC_EVENT_VD0)) {
spin_unlock_irqrestore(&ccdc->lock, flags); return;
}
if (ccdc->output & CCDC_OUTPUT_MEMORY)
restart = ccdc_isr_buffer(ccdc);
if (!ccdc->shadow_update)
ccdc_apply_controls(ccdc);
spin_unlock_irqrestore(&ccdc->lock, flags);
/* In BT.656 mode the synchronization signals are generated by the CCDC * from the embedded sync codes. The VD0 and VD1 interrupts are thus * only triggered when the CCDC is enabled, unlike external sync mode * where the line counter runs even when the CCDC is stopped. We can't * disable the CCDC at VD1 time, as no VD0 interrupt would be generated * for a short frame, which would result in the CCDC being stopped and * no VD interrupt generated anymore. The CCDC is stopped from the VD0 * interrupt handler instead for BT.656.
*/ if (ccdc->bt656) return;
spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
/* * Depending on the CCDC pipeline state, CCDC stopping should be * handled differently. In SINGLESHOT we emulate an internal CCDC * stopping because the CCDC hw works only in continuous mode. * When CONTINUOUS pipeline state is used and the CCDC writes it's * data to memory the CCDC and LSC are stopped immediately but * without change the CCDC stopping state machine. The CCDC * stopping state machine should be used only when user request * for stopping is received (SINGLESHOT is an exception).
*/ switch (ccdc->state) { case ISP_PIPELINE_STREAM_SINGLESHOT:
ccdc->stopping = CCDC_STOP_REQUEST; break;
case ISP_PIPELINE_STREAM_CONTINUOUS: if (ccdc->output & CCDC_OUTPUT_MEMORY) { if (ccdc->lsc.state != LSC_STATE_STOPPED)
__ccdc_lsc_enable(ccdc, 0);
__ccdc_enable(ccdc, 0);
} break;
case ISP_PIPELINE_STREAM_STOPPED: break;
}
if (ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1)) goto done;
if (ccdc->lsc.request == NULL) goto done;
/* * LSC need to be reconfigured. Stop it here and on next LSC_DONE IRQ * do the appropriate changes in registers
*/ if (ccdc->lsc.state == LSC_STATE_RUNNING) {
__ccdc_lsc_enable(ccdc, 0);
ccdc->lsc.state = LSC_STATE_RECONFIG; goto done;
}
/* LSC has been in STOPPED state, enable it */ if (ccdc->lsc.state == LSC_STATE_STOPPED)
ccdc_lsc_enable(ccdc);
if (!(ccdc->output & CCDC_OUTPUT_MEMORY)) return -ENODEV;
ccdc_set_outaddr(ccdc, buffer->dma);
/* We now have a buffer queued on the output, restart the pipeline * on the next CCDC interrupt if running in continuous mode (or when * starting the stream) in external sync mode, or immediately in BT.656 * sync mode as no CCDC interrupt is generated when the CCDC is stopped * in that case.
*/
spin_lock_irqsave(&ccdc->lock, flags); if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && !ccdc->running &&
ccdc->bt656)
restart = true; else
ccdc->underrun = 1;
spin_unlock_irqrestore(&ccdc->lock, flags);
/* * ccdc_set_stream - Enable/Disable streaming on the CCDC module * @sd: ISP CCDC V4L2 subdevice * @enable: Enable/disable stream * * When writing to memory, the CCDC hardware can't be enabled without a memory * buffer to write to. As the s_stream operation is called in response to a * STREAMON call without any buffer queued yet, just update the enabled field * and return immediately. The CCDC will be enabled in ccdc_isr_buffer(). * * When not writing to memory enable the CCDC immediately.
*/ staticint ccdc_set_stream(struct v4l2_subdev *sd, int enable)
{ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct isp_device *isp = to_isp_device(ccdc); int ret = 0;
if (ccdc->state == ISP_PIPELINE_STREAM_STOPPED) { if (enable == ISP_PIPELINE_STREAM_STOPPED) return 0;
/* Default to progressive field order. */ if (fmt->field == V4L2_FIELD_ANY)
fmt->field = V4L2_FIELD_NONE;
break;
case CCDC_PAD_SOURCE_OF:
pixelcode = fmt->code;
field = fmt->field;
*fmt = *__ccdc_get_format(ccdc, sd_state, CCDC_PAD_SINK,
which);
/* In SYNC mode the bridge converts YUV formats from 2X8 to * 1X16. In BT.656 no such conversion occurs. As we don't know * at this point whether the source will use SYNC or BT.656 mode * let's pretend the conversion always occurs. The CCDC will be * configured to pack bytes in BT.656, hiding the inaccuracy. * In all cases bytes can be swapped.
*/ if (fmt->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
fmt->code == MEDIA_BUS_FMT_UYVY8_2X8) { /* Use the user requested format if YUV. */ if (pixelcode == MEDIA_BUS_FMT_YUYV8_2X8 ||
pixelcode == MEDIA_BUS_FMT_UYVY8_2X8 ||
pixelcode == MEDIA_BUS_FMT_YUYV8_1X16 ||
pixelcode == MEDIA_BUS_FMT_UYVY8_1X16)
fmt->code = pixelcode;
/* Hardcode the output size to the crop rectangle size. */
crop = __ccdc_get_crop(ccdc, sd_state, which);
fmt->width = crop->width;
fmt->height = crop->height;
/* When input format is interlaced with alternating fields the * CCDC can interleave the fields.
*/ if (fmt->field == V4L2_FIELD_ALTERNATE &&
(field == V4L2_FIELD_INTERLACED_TB ||
field == V4L2_FIELD_INTERLACED_BT)) {
fmt->field = field;
fmt->height *= 2;
}
break;
case CCDC_PAD_SOURCE_VP:
*fmt = *__ccdc_get_format(ccdc, sd_state, CCDC_PAD_SINK,
which);
/* The video port interface truncates the data to 10 bits. */
info = omap3isp_video_format_info(fmt->code);
fmt->code = info->truncated;
/* YUV formats are not supported by the video port. */ if (fmt->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
fmt->code == MEDIA_BUS_FMT_UYVY8_2X8)
fmt->code = 0;
/* The number of lines that can be clocked out from the video * port output must be at least one line less than the number * of input lines.
*/
fmt->width = clamp_t(u32, width, 32, fmt->width);
fmt->height = clamp_t(u32, height, 32, fmt->height - 1); break;
}
/* Data is written to memory unpacked, each 10-bit or 12-bit pixel is * stored on 2 bytes.
*/
fmt->colorspace = V4L2_COLORSPACE_SRGB;
}
/* * ccdc_try_crop - Validate a crop rectangle * @ccdc: ISP CCDC device * @sink: format on the sink pad * @crop: crop rectangle to be validated
*/ staticvoid ccdc_try_crop(struct isp_ccdc_device *ccdc, conststruct v4l2_mbus_framefmt *sink, struct v4l2_rect *crop)
{ conststruct isp_format_info *info; unsignedint max_width;
/* For Bayer formats, restrict left/top and width/height to even values * to keep the Bayer pattern.
*/
info = omap3isp_video_format_info(sink->code); if (info->flavor != MEDIA_BUS_FMT_Y8_1X8) {
crop->left &= ~1;
crop->top &= ~1;
}
/* The data formatter truncates the number of horizontal output pixels * to a multiple of 16. To avoid clipping data, allow callers to request * an output size bigger than the input size up to the nearest multiple * of 16.
*/
max_width = (sink->width - crop->left + 15) & ~15;
crop->width = clamp_t(u32, crop->width, CCDC_MIN_WIDTH, max_width)
& ~15;
crop->height = clamp_t(u32, crop->height, CCDC_MIN_HEIGHT,
sink->height - crop->top);
/* Odd width/height values don't make sense for Bayer formats. */ if (info->flavor != MEDIA_BUS_FMT_Y8_1X8) {
crop->width &= ~1;
crop->height &= ~1;
}
}
/* * ccdc_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure * @sd_state: V4L2 subdev state * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success
*/ staticint ccdc_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code)
{ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format;
switch (code->pad) { case CCDC_PAD_SINK: if (code->index >= ARRAY_SIZE(ccdc_fmts)) return -EINVAL;
code->code = ccdc_fmts[code->index]; break;
case CCDC_PAD_SOURCE_OF:
format = __ccdc_get_format(ccdc, sd_state, code->pad,
code->which);
if (format->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
format->code == MEDIA_BUS_FMT_UYVY8_2X8) { /* In YUV mode the CCDC can swap bytes. */ if (code->index == 0)
code->code = MEDIA_BUS_FMT_YUYV8_1X16; elseif (code->index == 1)
code->code = MEDIA_BUS_FMT_UYVY8_1X16; else return -EINVAL;
} else { /* In raw mode, no configurable format confversion is * available.
*/ if (code->index == 0)
code->code = format->code; else return -EINVAL;
} break;
case CCDC_PAD_SOURCE_VP: /* The CCDC supports no configurable format conversion * compatible with the video port. Enumerate a single output * format code.
*/ if (code->index != 0) return -EINVAL;
format = __ccdc_get_format(ccdc, sd_state, code->pad,
code->which);
/* A pixel code equal to 0 means that the video port doesn't * support the input format. Don't enumerate any pixel code.
*/ if (format->code == 0) return -EINVAL;
format = __ccdc_get_format(ccdc, sd_state, CCDC_PAD_SINK,
sel->which);
ccdc_try_crop(ccdc, format, &sel->r); break;
case V4L2_SEL_TGT_CROP:
sel->r = *__ccdc_get_crop(ccdc, sd_state, sel->which); break;
default: return -EINVAL;
}
return 0;
}
/* * ccdc_set_selection - Set a selection rectangle on a pad * @sd: ISP CCDC V4L2 subdevice * @sd_state: V4L2 subdev state * @sel: Selection rectangle * * The only supported rectangle is the actual crop rectangle on the output * formatter source pad. * * Return 0 on success or a negative error code otherwise.
*/ staticint ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel)
{ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format;
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet)
¤
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.