/* * resizer_set_bilinear - Chrominance horizontal algorithm select * @res: Device context. * @type: Filtering interpolation type. * * Filtering that is same as luminance processing is * intended only for downsampling, and bilinear interpolation * is intended only for upsampling.
*/ staticvoid resizer_set_bilinear(struct isp_res_device *res, enum resizer_chroma_algo type)
{ struct isp_device *isp = to_isp_device(res);
/* * resizer_set_source - Input source select * @res: Device context. * @source: Input source type * * If this field is set to RESIZER_INPUT_VP, the resizer input is fed from * Preview/CCDC engine, otherwise from memory.
*/ staticvoid resizer_set_source(struct isp_res_device *res, enum resizer_input_entity source)
{ struct isp_device *isp = to_isp_device(res);
/* * resizer_set_dst_size - Setup the output height and width * @res: Device context. * @width: Output width. * @height: Output height. * * Width : * The value must be EVEN. * * Height: * The number of bytes written to SDRAM must be * a multiple of 16-bytes if the vertical resizing factor * is greater than 1x (upsizing)
*/ staticvoid resizer_set_output_size(struct isp_res_device *res,
u32 width, u32 height)
{ struct isp_device *isp = to_isp_device(res);
u32 rgval;
/* * resizer_set_output_offset - Setup memory offset for the output lines. * @res: Device context. * @offset: Memory offset. * * The 5 LSBs are forced to be zeros by the hardware to align on a 32-byte * boundary; the 5 LSBs are read-only. For optimal use of SDRAM bandwidth, * the SDRAM line offset must be set on a 256-byte boundary
*/ staticvoid resizer_set_output_offset(struct isp_res_device *res, u32 offset)
{ struct isp_device *isp = to_isp_device(res);
/* * resizer_set_start - Setup vertical and horizontal start position * @res: Device context. * @left: Horizontal start position. * @top: Vertical start position. * * Vertical start line: * This field makes sense only when the resizer obtains its input * from the preview engine/CCDC * * Horizontal start pixel: * Pixels are coded on 16 bits for YUV and 8 bits for color separate data. * When the resizer gets its input from SDRAM, this field must be set * to <= 15 for YUV 16-bit data and <= 31 for 8-bit color separate data
*/ staticvoid resizer_set_start(struct isp_res_device *res, u32 left, u32 top)
{ struct isp_device *isp = to_isp_device(res);
u32 rgval;
/* * resizer_set_src_offs - Setup the memory offset for the input lines * @res: Device context. * @offset: Memory offset. * * The 5 LSBs are forced to be zeros by the hardware to align on a 32-byte * boundary; the 5 LSBs are read-only. This field must be programmed to be * 0x0 if the resizer input is from preview engine/CCDC.
*/ staticvoid resizer_set_input_offset(struct isp_res_device *res, u32 offset)
{ struct isp_device *isp = to_isp_device(res);
/* * The data rate at the horizontal resizer output must not exceed half the * functional clock or 100 MP/s, whichever is lower. According to the TRM * there's no similar requirement for the vertical resizer output. However * experience showed that vertical upscaling by 4 leads to SBL overflows (with * data rates at the resizer output exceeding 300 MP/s). Limiting the resizer * output data rate to the functional clock or 200 MP/s, whichever is lower, * seems to get rid of SBL overflows. * * The maximum data rate at the output of the horizontal resizer can thus be * computed with * * max intermediate rate <= L3 clock * input height / output height * max intermediate rate <= L3 clock / 2 * * The maximum data rate at the resizer input is then * * max input rate <= max intermediate rate * input width / output width * * where the input width and height are the resizer input crop rectangle size. * The TRM doesn't clearly explain if that's a maximum instant data rate or a * maximum average data rate.
*/ void omap3isp_resizer_max_rate(struct isp_res_device *res, unsignedint *max_rate)
{ struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity); conststruct v4l2_mbus_framefmt *ofmt = &res->formats[RESZ_PAD_SOURCE]; unsignedlong limit = min(pipe->l3_ick, 200000000UL); unsignedlong clock;
/* * When the resizer processes images from memory, the driver must slow down read * requests on the input to at least comply with the internal data rate * requirements. If the application real-time requirements can cope with slower * processing, the resizer can be slowed down even more to put less pressure on * the overall system. * * When the resizer processes images on the fly (either from the CCDC or the * preview module), the same data rate requirements apply but they can't be * enforced at the resizer level. The image input module (sensor, CCP2 or * preview module) must not provide image data faster than the resizer can * process. * * For live image pipelines, the data rate is set by the frame format, size and * rate. The sensor output frame rate must not exceed the maximum resizer data * rate. * * The resizer slows down read requests by inserting wait cycles in the SBL * requests. The maximum number of 256-byte requests per second can be computed * as (the data rate is multiplied by 2 to convert from pixels per second to * bytes per second) * * request per second = data rate * 2 / 256 * cycles per request = cycles per second / requests per second * * The number of cycles per second is controlled by the L3 clock, leading to * * cycles per request = L3 frequency / 2 * 256 / data rate
*/ staticvoid resizer_adjust_bandwidth(struct isp_res_device *res)
{ struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity); struct isp_device *isp = to_isp_device(res); unsignedlong l3_ick = pipe->l3_ick; struct v4l2_fract *timeperframe; unsignedint cycles_per_frame; unsignedint requests_per_frame; unsignedint cycles_per_request; unsignedint granularity; unsignedint minimum; unsignedint maximum; unsignedint value;
switch (isp->revision) { case ISP_REVISION_1_0: case ISP_REVISION_2_0: default:
granularity = 1024; break;
case ISP_REVISION_15_0:
granularity = 32; break;
}
/* Compute the minimum number of cycles per request, based on the * pipeline maximum data rate. This is an absolute lower bound if we * don't want SBL overflows, so round the value up.
*/
cycles_per_request = div_u64((u64)l3_ick / 2 * 256 + pipe->max_rate - 1,
pipe->max_rate);
minimum = DIV_ROUND_UP(cycles_per_request, granularity);
/* Compute the maximum number of cycles per request, based on the * requested frame rate. This is a soft upper bound to achieve a frame * rate equal or higher than the requested value, so round the value * down.
*/
timeperframe = &pipe->max_timeperframe;
/* * resizer_set_inaddr - Sets the memory address of the input frame. * @addr: 32bit memory address aligned on 32byte boundary.
*/ staticvoid resizer_set_inaddr(struct isp_res_device *res, u32 addr)
{
res->addr_base = addr;
/* This will handle crop settings in stream off state */ if (res->crop_offset)
addr += res->crop_offset & ~0x1f;
__resizer_set_inaddr(res, addr);
}
/* * Configures the memory address to which the output frame is written. * @addr: 32bit memory address aligned on 32byte boundary. * Note: For SBL efficiency reasons the address should be on a 256-byte * boundary.
*/ staticvoid resizer_set_outaddr(struct isp_res_device *res, u32 addr)
{ struct isp_device *isp = to_isp_device(res);
/* * Set output address. This needs to be in its own function * because it changes often.
*/
isp_reg_writel(isp, addr << ISPRSZ_SDR_OUTADD_ADDR_SHIFT,
OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTADD);
}
/* * resizer_print_status - Prints the values of the resizer module registers.
*/ #define RSZ_PRINT_REGISTER(isp, name)\
dev_dbg(isp->dev, "###RSZ "#name"=0x%08x\n", \
isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_##name))
/* * resizer_calc_ratios - Helper function for calculating resizer ratios * @res: pointer to resizer private data structure * @input: input frame size * @output: output frame size * @ratio : return calculated ratios * return none * * The resizer uses a polyphase sample rate converter. The upsampling filter * has a fixed number of phases that depend on the resizing ratio. As the ratio * computation depends on the number of phases, we need to compute a first * approximation and then refine it. * * The input/output/ratio relationship is given by the OMAP34xx TRM: * * - 8-phase, 4-tap mode (RSZ = 64 ~ 512) * iw = (32 * sph + (ow - 1) * hrsz + 16) >> 8 + 7 * ih = (32 * spv + (oh - 1) * vrsz + 16) >> 8 + 4 * - 4-phase, 7-tap mode (RSZ = 513 ~ 1024) * iw = (64 * sph + (ow - 1) * hrsz + 32) >> 8 + 7 * ih = (64 * spv + (oh - 1) * vrsz + 32) >> 8 + 7 * * iw and ih are the input width and height after cropping. Those equations need * to be satisfied exactly for the resizer to work correctly. * * The equations can't be easily reverted, as the >> 8 operation is not linear. * In addition, not all input sizes can be achieved for a given output size. To * get the highest input size lower than or equal to the requested input size, * we need to compute the highest resizing ratio that satisfies the following * inequality (taking the 4-tap mode width equation as an example) * * iw >= (32 * sph + (ow - 1) * hrsz + 16) >> 8 - 7 * * (where iw is the requested input width) which can be rewritten as * * iw - 7 >= (32 * sph + (ow - 1) * hrsz + 16) >> 8 * (iw - 7) << 8 >= 32 * sph + (ow - 1) * hrsz + 16 - b * ((iw - 7) << 8) + b >= 32 * sph + (ow - 1) * hrsz + 16 * * where b is the value of the 8 least significant bits of the right hand side * expression of the last inequality. The highest resizing ratio value will be * achieved when b is equal to its maximum value of 255. That resizing ratio * value will still satisfy the original inequality, as b will disappear when * the expression will be shifted right by 8. * * The reverted equations thus become * * - 8-phase, 4-tap mode * hrsz = ((iw - 7) * 256 + 255 - 16 - 32 * sph) / (ow - 1) * vrsz = ((ih - 4) * 256 + 255 - 16 - 32 * spv) / (oh - 1) * - 4-phase, 7-tap mode * hrsz = ((iw - 7) * 256 + 255 - 32 - 64 * sph) / (ow - 1) * vrsz = ((ih - 7) * 256 + 255 - 32 - 64 * spv) / (oh - 1) * * The ratios are integer values, and are rounded down to ensure that the * cropped input size is not bigger than the uncropped input size. * * As the number of phases/taps, used to select the correct equations to compute * the ratio, depends on the ratio, we start with the 4-tap mode equations to * compute an approximation of the ratio, and switch to the 7-tap mode equations * if the approximation is higher than the ratio threshold. * * As the 7-tap mode equations will return a ratio smaller than or equal to the * 4-tap mode equations, the resulting ratio could become lower than or equal to * the ratio threshold. This 'equations loop' isn't an issue as long as the * correct equations are used to compute the final input size. Starting with the * 4-tap mode equations ensure that, in case of values resulting in a 'ratio * loop', the smallest of the ratio values will be used, never exceeding the * requested input size. * * We first clamp the output size according to the hardware capability to avoid * auto-cropping the input more than required to satisfy the TRM equations. The * minimum output size is achieved with a scaling factor of 1024. It is thus * computed using the 7-tap equations. * * min ow = ((iw - 7) * 256 - 32 - 64 * sph) / 1024 + 1 * min oh = ((ih - 7) * 256 - 32 - 64 * spv) / 1024 + 1 * * Similarly, the maximum output size is achieved with a scaling factor of 64 * and computed using the 4-tap equations. * * max ow = ((iw - 7) * 256 + 255 - 16 - 32 * sph) / 64 + 1 * max oh = ((ih - 4) * 256 + 255 - 16 - 32 * spv) / 64 + 1 * * The additional +255 term compensates for the round down operation performed * by the TRM equations when shifting the value right by 8 bits. * * We then compute and clamp the ratios (x1/4 ~ x4). Clamping the output size to * the maximum value guarantees that the ratio value will never be smaller than * the minimum, but it could still slightly exceed the maximum. Clamping the * ratio will thus result in a resizing factor slightly larger than the * requested value. * * To accommodate that, and make sure the TRM equations are satisfied exactly, we * compute the input crop rectangle as the last step. * * As if the situation wasn't complex enough, the maximum output width depends * on the vertical resizing ratio. Fortunately, the output height doesn't * depend on the horizontal resizing ratio. We can then start by computing the * output height and the vertical ratio, and then move to computing the output * width and the horizontal ratio.
*/ staticvoid resizer_calc_ratios(struct isp_res_device *res, struct v4l2_rect *input, struct v4l2_mbus_framefmt *output, struct resizer_ratio *ratio)
{ struct isp_device *isp = to_isp_device(res); constunsignedint spv = DEFAULT_PHASE; constunsignedint sph = DEFAULT_PHASE; unsignedint upscaled_width; unsignedint upscaled_height; unsignedint min_width; unsignedint min_height; unsignedint max_width; unsignedint max_height; unsignedint width_alignment; unsignedint width; unsignedint height;
/* * Compute the minimum and maximum output widths based on the hardware * capabilities. The maximum depends on the vertical resizing ratio.
*/
min_width = ((input->width - 7) * 256 - 32 - 64 * sph) / 1024 + 1;
min_width = max_t(unsignedint, min_width, MIN_OUT_WIDTH);
if (ratio->vert <= MID_RESIZE_VALUE) { switch (isp->revision) { case ISP_REVISION_1_0:
max_width = MAX_4TAP_OUT_WIDTH_ES1; break;
case ISP_REVISION_2_0: default:
max_width = MAX_4TAP_OUT_WIDTH_ES2; break;
case ISP_REVISION_15_0:
max_width = MAX_4TAP_OUT_WIDTH_3630; break;
}
} else { switch (isp->revision) { case ISP_REVISION_1_0:
max_width = MAX_7TAP_OUT_WIDTH_ES1; break;
case ISP_REVISION_2_0: default:
max_width = MAX_7TAP_OUT_WIDTH_ES2; break;
/* * The output width must be even, and must be a multiple of 16 bytes * when upscaling vertically. Clamp the output width to the valid range. * Take the alignment into account (the maximum width in 7-tap mode on * ES2 isn't a multiple of 8) and align the result up to make sure it * won't be smaller than the minimum.
*/
width_alignment = ratio->vert < 256 ? 8 : 2;
output->width = clamp(output->width, min_width,
max_width & ~(width_alignment - 1));
output->width = ALIGN(output->width, width_alignment);
/* Center the new crop rectangle. */
input->left += (input->width - width) / 2;
input->top += (input->height - height) / 2;
input->width = width;
input->height = height;
}
/* * resizer_set_crop_params - Setup hardware with cropping parameters * @res : resizer private structure * @input : format on sink pad * @output : format on source pad * return none
*/ staticvoid resizer_set_crop_params(struct isp_res_device *res, conststruct v4l2_mbus_framefmt *input, conststruct v4l2_mbus_framefmt *output)
{
resizer_set_ratio(res, &res->ratio);
/* Set chrominance horizontal algorithm */ if (res->ratio.horz >= RESIZE_DIVISOR)
resizer_set_bilinear(res, RSZ_THE_SAME); else
resizer_set_bilinear(res, RSZ_BILINEAR);
resizer_adjust_bandwidth(res);
if (res->input == RESIZER_INPUT_MEMORY) { /* Calculate additional offset for crop */
res->crop_offset = (res->crop.active.top * input->width +
res->crop.active.left) * 2; /* * Write lowest 4 bits of horizontal pixel offset (in pixels), * vertical start must be 0.
*/
resizer_set_start(res, (res->crop_offset / 2) & 0xf, 0);
/* * Set start (read) address for cropping, in bytes. * Lowest 5 bits must be zero.
*/
__resizer_set_inaddr(res,
res->addr_base + (res->crop_offset & ~0x1f));
} else { /* * Set vertical start line and horizontal starting pixel. * If the input is from CCDC/PREV, horizontal start field is * in bytes (twice number of pixels).
*/
resizer_set_start(res, res->crop.active.left * 2,
res->crop.active.top); /* Input address and offset must be 0 for preview/ccdc input */
__resizer_set_inaddr(res, 0);
resizer_set_input_offset(res, 0);
}
/* Set the input size */
resizer_set_input_size(res, res->crop.active.width,
res->crop.active.height);
}
void omap3isp_resizer_isr_frame_sync(struct isp_res_device *res)
{ /* * If ISP_VIDEO_DMAQUEUE_QUEUED is set, DMA queue had an underrun * condition, the module was paused and now we have a buffer queued * on the output again. Restart the pipeline if running in continuous * mode.
*/ if (res->state == ISP_PIPELINE_STREAM_CONTINUOUS &&
res->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) {
resizer_enable_oneshot(res);
isp_video_dmaqueue_flags_clr(&res->video_out);
}
}
if (res->state == ISP_PIPELINE_STREAM_STOPPED) return;
/* Complete the output buffer and, if reading from memory, the input * buffer.
*/
buffer = omap3isp_video_buffer_next(&res->video_out); if (buffer != NULL) {
resizer_set_outaddr(res, buffer->dma);
restart = 1;
}
pipe->state |= ISP_PIPELINE_IDLE_OUTPUT;
if (res->input == RESIZER_INPUT_MEMORY) {
buffer = omap3isp_video_buffer_next(&res->video_in); if (buffer != NULL)
resizer_set_inaddr(res, buffer->dma);
pipe->state |= ISP_PIPELINE_IDLE_INPUT;
}
if (res->state == ISP_PIPELINE_STREAM_SINGLESHOT) { if (isp_pipeline_ready(pipe))
omap3isp_pipeline_set_stream(pipe,
ISP_PIPELINE_STREAM_SINGLESHOT);
} else { /* If an underrun occurs, the video queue operation handler will * restart the resizer. Otherwise restart it immediately.
*/ if (restart)
resizer_enable_oneshot(res);
}
}
/* * omap3isp_resizer_isr - ISP resizer interrupt handler * * Manage the resizer video buffers and configure shadowed and busy-locked * registers.
*/ void omap3isp_resizer_isr(struct isp_res_device *res)
{ struct v4l2_mbus_framefmt *informat, *outformat; unsignedlong flags;
if (omap3isp_module_sync_is_stopping(&res->wait, &res->stopping)) return;
if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
resizer_set_inaddr(res, buffer->dma);
/* * We now have a buffer queued on the output. Despite what the * TRM says, the resizer can't be restarted immediately. * Enabling it in one shot mode in the middle of a frame (or at * least asynchronously to the frame) results in the output * being shifted randomly left/right and up/down, as if the * hardware didn't synchronize itself to the beginning of the * frame correctly. * * Restart the resizer on the next sync interrupt if running in * continuous mode or when starting the stream.
*/ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
resizer_set_outaddr(res, buffer->dma);
/* * resizer_set_stream - Enable/Disable streaming on resizer subdev * @sd: ISP resizer V4L2 subdev * @enable: 1 == Enable, 0 == Disable * * The resizer 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 state field and return immediately. * The resizer will be enabled in resizer_video_queue().
*/ staticint resizer_set_stream(struct v4l2_subdev *sd, int enable)
{ struct isp_res_device *res = v4l2_get_subdevdata(sd); struct isp_video *video_out = &res->video_out; struct isp_device *isp = to_isp_device(res); struct device *dev = to_device(res);
if (res->state == ISP_PIPELINE_STREAM_STOPPED) { if (enable == ISP_PIPELINE_STREAM_STOPPED) return 0;
switch (enable) { case ISP_PIPELINE_STREAM_CONTINUOUS:
omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE); if (video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) {
resizer_enable_oneshot(res);
isp_video_dmaqueue_flags_clr(video_out);
} break;
case ISP_PIPELINE_STREAM_SINGLESHOT: if (res->input == RESIZER_INPUT_MEMORY)
omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_READ);
omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE);
/* * resizer_set_selection - Set a selection rectangle on a pad * @sd: ISP resizer V4L2 subdevice * @sd_state: V4L2 subdev state * @sel: Selection rectangle * * The only supported rectangle is the actual crop rectangle on the sink pad. * * FIXME: This function currently behaves as if the KEEP_CONFIG selection flag * was always set. * * Return 0 on success or a negative error code otherwise.
*/ staticint resizer_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel)
{ struct isp_res_device *res = v4l2_get_subdevdata(sd); struct isp_device *isp = to_isp_device(res); conststruct v4l2_mbus_framefmt *format_sink; struct v4l2_mbus_framefmt format_source; struct resizer_ratio ratio; unsignedlong flags;
if (sel->target != V4L2_SEL_TGT_CROP ||
sel->pad != RESZ_PAD_SINK) return -EINVAL;
/* Clamp the crop rectangle to the bounds, and then mangle it further to * fulfill the TRM equations. Store the clamped but otherwise unmangled * rectangle to avoid cropping the input multiple times: when an * application sets the output format, the current crop rectangle is * mangled during crop rectangle computation, which would lead to a new, * smaller input crop rectangle every time the output size is set if we * stored the mangled rectangle.
*/
resizer_try_crop(format_sink, &format_source, &sel->r);
*__resizer_get_crop(res, sd_state, sel->which) = sel->r;
resizer_calc_ratios(res, &sel->r, &format_source, &ratio);
/* Update the source format, resizing ratios and crop rectangle. If * streaming is on the IRQ handler will reprogram the resizer after the * current frame. We thus we need to protect against race conditions.
*/
spin_lock_irqsave(&res->lock, flags);
/* Propagate the format from sink to source */
format = __resizer_get_format(res, sd_state, RESZ_PAD_SOURCE,
fmt->which);
*format = fmt->format;
resizer_try_format(res, sd_state, RESZ_PAD_SOURCE, format,
fmt->which);
}
if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { /* Compute and store the active crop rectangle and resizer * ratios. format already points to the source pad active * format.
*/
res->crop.active = res->crop.request;
resizer_calc_ratios(res, &res->crop.active, format,
&res->ratio);
}
/* * resizer_init_formats - Initialize formats on all pads * @sd: ISP resizer V4L2 subdevice * @fh: V4L2 subdev file handle * * Initialize all pad formats with default values. If fh is not NULL, try * formats are initialized on the file handle. Otherwise active formats are * initialized on the device.
*/ staticint resizer_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{ struct v4l2_subdev_format format;
int omap3isp_resizer_register_entities(struct isp_res_device *res, struct v4l2_device *vdev)
{ int ret;
/* Register the subdev and video nodes. */
res->subdev.dev = vdev->mdev->dev;
ret = v4l2_device_register_subdev(vdev, &res->subdev); if (ret < 0) goto error;
ret = omap3isp_video_register(&res->video_in, vdev); if (ret < 0) goto error;
ret = omap3isp_video_register(&res->video_out, vdev); if (ret < 0) goto error;
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.