Commit 1f69fd97 authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Mauro Carvalho Chehab

[media] omap3isp: preview: Add crop support on the sink pad

The crop rectangle takes the preview engine internal cropping
requirements into account. The smallest allowable margins are 14 columns
and 8 rows when reading from memory, and 18 columns and 8 rows when
processing data on the fly from the CCDC.
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 059dc1d9
...@@ -76,6 +76,42 @@ static struct omap3isp_prev_csc flr_prev_csc = { ...@@ -76,6 +76,42 @@ static struct omap3isp_prev_csc flr_prev_csc = {
#define DEF_DETECT_CORRECT_VAL 0xe #define DEF_DETECT_CORRECT_VAL 0xe
/*
* Margins and image size limits.
*
* The preview engine crops several rows and columns internally depending on
* which filters are enabled. To avoid format changes when the filters are
* enabled or disabled (which would prevent them from being turned on or off
* during streaming), the driver assumes all the filters are enabled when
* computing sink crop and source format limits.
*
* If a filter is disabled, additional cropping is automatically added at the
* preview engine input by the driver to avoid overflow at line and frame end.
* This is completely transparent for applications.
*
* Median filter 4 pixels
* Noise filter,
* Faulty pixels correction 4 pixels, 4 lines
* CFA filter 4 pixels, 4 lines in Bayer mode
* 2 lines in other modes
* Color suppression 2 pixels
* or luma enhancement
* -------------------------------------------------------------
* Maximum total 14 pixels, 8 lines
*
* The color suppression and luma enhancement filters are applied after bayer to
* YUV conversion. They thus can crop one pixel on the left and one pixel on the
* right side of the image without changing the color pattern. When both those
* filters are disabled, the driver must crop the two pixels on the same side of
* the image to avoid changing the bayer pattern. The left margin is thus set to
* 8 pixels and the right margin to 6 pixels.
*/
#define PREV_MARGIN_LEFT 8
#define PREV_MARGIN_RIGHT 6
#define PREV_MARGIN_TOP 4
#define PREV_MARGIN_BOTTOM 4
#define PREV_MIN_IN_WIDTH 64 #define PREV_MIN_IN_WIDTH 64
#define PREV_MIN_IN_HEIGHT 8 #define PREV_MIN_IN_HEIGHT 8
#define PREV_MAX_IN_HEIGHT 16384 #define PREV_MAX_IN_HEIGHT 16384
...@@ -985,52 +1021,36 @@ static void preview_config_averager(struct isp_prev_device *prev, u8 average) ...@@ -985,52 +1021,36 @@ static void preview_config_averager(struct isp_prev_device *prev, u8 average)
* enabled when reporting source pad formats to userspace. If this assumption is * enabled when reporting source pad formats to userspace. If this assumption is
* not true, rows and columns must be manually cropped at the preview engine * not true, rows and columns must be manually cropped at the preview engine
* input to avoid overflows at the end of lines and frames. * input to avoid overflows at the end of lines and frames.
*
* See the explanation at the PREV_MARGIN_* definitions for more details.
*/ */
static void preview_config_input_size(struct isp_prev_device *prev) static void preview_config_input_size(struct isp_prev_device *prev)
{ {
struct isp_device *isp = to_isp_device(prev); struct isp_device *isp = to_isp_device(prev);
struct prev_params *params = &prev->params; struct prev_params *params = &prev->params;
struct v4l2_mbus_framefmt *format = &prev->formats[PREV_PAD_SINK]; unsigned int sph = prev->crop.left;
unsigned int sph = 0; unsigned int eph = prev->crop.left + prev->crop.width - 1;
unsigned int eph = format->width - 1; unsigned int slv = prev->crop.top;
unsigned int slv = 0; unsigned int elv = prev->crop.top + prev->crop.height - 1;
unsigned int elv = format->height - 1;
if (params->features & PREV_CFA) {
if (prev->input == PREVIEW_INPUT_CCDC) { sph -= 2;
sph += 2; eph += 2;
eph -= 2; slv -= 2;
} elv += 2;
/*
* Median filter 4 pixels
* Noise filter 4 pixels, 4 lines
* or faulty pixels correction
* CFA filter 4 pixels, 4 lines in Bayer mode
* 2 lines in other modes
* Color suppression 2 pixels
* or luma enhancement
* -------------------------------------------------------------
* Maximum total 14 pixels, 8 lines
*/
if (!(params->features & PREV_CFA)) {
sph += 2;
eph -= 2;
slv += 2;
elv -= 2;
} }
if (!(params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER))) { if (params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER)) {
sph += 2; sph -= 2;
eph -= 2; eph += 2;
slv += 2; slv -= 2;
elv -= 2; elv += 2;
} }
if (!(params->features & PREV_HORZ_MEDIAN_FILTER)) { if (params->features & PREV_HORZ_MEDIAN_FILTER) {
sph += 2; sph -= 2;
eph -= 2; eph += 2;
} }
if (!(params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE))) if (params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE))
sph += 2; sph -= 2;
isp_reg_writel(isp, (sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | eph, isp_reg_writel(isp, (sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | eph,
OMAP3_ISP_IOMEM_PREV, ISPPRV_HORZ_INFO); OMAP3_ISP_IOMEM_PREV, ISPPRV_HORZ_INFO);
...@@ -1597,6 +1617,16 @@ __preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh, ...@@ -1597,6 +1617,16 @@ __preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh,
return &prev->formats[pad]; return &prev->formats[pad];
} }
static struct v4l2_rect *
__preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh,
enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
return v4l2_subdev_get_try_crop(fh, PREV_PAD_SINK);
else
return &prev->crop;
}
/* previewer format descriptions */ /* previewer format descriptions */
static const unsigned int preview_input_fmts[] = { static const unsigned int preview_input_fmts[] = {
V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10,
...@@ -1611,19 +1641,23 @@ static const unsigned int preview_output_fmts[] = { ...@@ -1611,19 +1641,23 @@ static const unsigned int preview_output_fmts[] = {
}; };
/* /*
* preview_try_format - Handle try format by pad subdev method * preview_try_format - Validate a format
* @prev: ISP preview device * @prev: ISP preview engine
* @fh : V4L2 subdev file handle * @fh: V4L2 subdev file handle
* @pad: pad num * @pad: pad number
* @fmt: pointer to v4l2 format structure * @fmt: format to be validated
* @which: try/active format selector
*
* Validate and adjust the given format for the given pad based on the preview
* engine limits and the format and crop rectangles on other pads.
*/ */
static void preview_try_format(struct isp_prev_device *prev, static void preview_try_format(struct isp_prev_device *prev,
struct v4l2_subdev_fh *fh, unsigned int pad, struct v4l2_subdev_fh *fh, unsigned int pad,
struct v4l2_mbus_framefmt *fmt, struct v4l2_mbus_framefmt *fmt,
enum v4l2_subdev_format_whence which) enum v4l2_subdev_format_whence which)
{ {
struct v4l2_mbus_framefmt *format;
enum v4l2_mbus_pixelcode pixelcode; enum v4l2_mbus_pixelcode pixelcode;
struct v4l2_rect *crop;
unsigned int i; unsigned int i;
switch (pad) { switch (pad) {
...@@ -1659,15 +1693,8 @@ static void preview_try_format(struct isp_prev_device *prev, ...@@ -1659,15 +1693,8 @@ static void preview_try_format(struct isp_prev_device *prev,
case PREV_PAD_SOURCE: case PREV_PAD_SOURCE:
pixelcode = fmt->code; pixelcode = fmt->code;
format = __preview_get_format(prev, fh, PREV_PAD_SINK, which); *fmt = *__preview_get_format(prev, fh, PREV_PAD_SINK, which);
memcpy(fmt, format, sizeof(*fmt));
/* The preview module output size is configurable through the
* input interface (horizontal and vertical cropping) and the
* averager (horizontal scaling by 1/1, 1/2, 1/4 or 1/8). In
* spite of this, hardcode the output size to the biggest
* possible value for simplicity reasons.
*/
switch (pixelcode) { switch (pixelcode) {
case V4L2_MBUS_FMT_YUYV8_1X16: case V4L2_MBUS_FMT_YUYV8_1X16:
case V4L2_MBUS_FMT_UYVY8_1X16: case V4L2_MBUS_FMT_UYVY8_1X16:
...@@ -1679,20 +1706,14 @@ static void preview_try_format(struct isp_prev_device *prev, ...@@ -1679,20 +1706,14 @@ static void preview_try_format(struct isp_prev_device *prev,
break; break;
} }
/* The TRM states (12.1.4.7.1.2) that 2 pixels must be cropped /* The preview module output size is configurable through the
* from the left and right sides when the input source is the * averager (horizontal scaling by 1/1, 1/2, 1/4 or 1/8). This
* CCDC. This seems not to be needed in practice, investigation * is not supported yet, hardcode the output size to the crop
* is required. * rectangle size.
*/
if (prev->input == PREVIEW_INPUT_CCDC)
fmt->width -= 4;
/* Assume that all blocks are enabled and crop pixels and lines
* accordingly. See preview_config_input_size() for more
* information.
*/ */
fmt->width -= 14; crop = __preview_get_crop(prev, fh, which);
fmt->height -= 8; fmt->width = crop->width;
fmt->height = crop->height;
fmt->colorspace = V4L2_COLORSPACE_JPEG; fmt->colorspace = V4L2_COLORSPACE_JPEG;
break; break;
...@@ -1701,6 +1722,49 @@ static void preview_try_format(struct isp_prev_device *prev, ...@@ -1701,6 +1722,49 @@ static void preview_try_format(struct isp_prev_device *prev,
fmt->field = V4L2_FIELD_NONE; fmt->field = V4L2_FIELD_NONE;
} }
/*
* preview_try_crop - Validate a crop rectangle
* @prev: ISP preview engine
* @sink: format on the sink pad
* @crop: crop rectangle to be validated
*
* The preview engine crops lines and columns for its internal operation,
* depending on which filters are enabled. Enforce minimum crop margins to
* handle that transparently for userspace.
*
* See the explanation at the PREV_MARGIN_* definitions for more details.
*/
static void preview_try_crop(struct isp_prev_device *prev,
const struct v4l2_mbus_framefmt *sink,
struct v4l2_rect *crop)
{
unsigned int left = PREV_MARGIN_LEFT;
unsigned int right = sink->width - PREV_MARGIN_RIGHT;
unsigned int top = PREV_MARGIN_TOP;
unsigned int bottom = sink->height - PREV_MARGIN_BOTTOM;
/* When processing data on-the-fly from the CCDC, at least 2 pixels must
* be cropped from the left and right sides of the image. As we don't
* know which filters will be enabled, increase the left and right
* margins by two.
*/
if (prev->input == PREVIEW_INPUT_CCDC) {
left += 2;
right -= 2;
}
/* Restrict left/top to even values to keep the Bayer pattern. */
crop->left &= ~1;
crop->top &= ~1;
crop->left = clamp_t(u32, crop->left, left, right - PREV_MIN_OUT_WIDTH);
crop->top = clamp_t(u32, crop->top, top, bottom - PREV_MIN_OUT_HEIGHT);
crop->width = clamp_t(u32, crop->width, PREV_MIN_OUT_WIDTH,
right - crop->left);
crop->height = clamp_t(u32, crop->height, PREV_MIN_OUT_HEIGHT,
bottom - crop->top);
}
/* /*
* preview_enum_mbus_code - Handle pixel format enumeration * preview_enum_mbus_code - Handle pixel format enumeration
* @sd : pointer to v4l2 subdev structure * @sd : pointer to v4l2 subdev structure
...@@ -1762,6 +1826,60 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd, ...@@ -1762,6 +1826,60 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd,
return 0; return 0;
} }
/*
* preview_get_crop - Retrieve the crop rectangle on a pad
* @sd: ISP preview V4L2 subdevice
* @fh: V4L2 subdev file handle
* @crop: crop rectangle
*
* Return 0 on success or a negative error code otherwise.
*/
static int preview_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_crop *crop)
{
struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
/* Cropping is only supported on the sink pad. */
if (crop->pad != PREV_PAD_SINK)
return -EINVAL;
crop->rect = *__preview_get_crop(prev, fh, crop->which);
return 0;
}
/*
* preview_set_crop - Retrieve the crop rectangle on a pad
* @sd: ISP preview V4L2 subdevice
* @fh: V4L2 subdev file handle
* @crop: crop rectangle
*
* Return 0 on success or a negative error code otherwise.
*/
static int preview_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_crop *crop)
{
struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
/* Cropping is only supported on the sink pad. */
if (crop->pad != PREV_PAD_SINK)
return -EINVAL;
/* The crop rectangle can't be changed while streaming. */
if (prev->state != ISP_PIPELINE_STREAM_STOPPED)
return -EBUSY;
format = __preview_get_format(prev, fh, PREV_PAD_SINK, crop->which);
preview_try_crop(prev, format, &crop->rect);
*__preview_get_crop(prev, fh, crop->which) = crop->rect;
/* Update the source format. */
format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, crop->which);
preview_try_format(prev, fh, PREV_PAD_SOURCE, format, crop->which);
return 0;
}
/* /*
* preview_get_format - Handle get format by pads subdev method * preview_get_format - Handle get format by pads subdev method
* @sd : pointer to v4l2 subdev structure * @sd : pointer to v4l2 subdev structure
...@@ -1795,6 +1913,7 @@ static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ...@@ -1795,6 +1913,7 @@ static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
{ {
struct isp_prev_device *prev = v4l2_get_subdevdata(sd); struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format; struct v4l2_mbus_framefmt *format;
struct v4l2_rect *crop;
format = __preview_get_format(prev, fh, fmt->pad, fmt->which); format = __preview_get_format(prev, fh, fmt->pad, fmt->which);
if (format == NULL) if (format == NULL)
...@@ -1805,9 +1924,18 @@ static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ...@@ -1805,9 +1924,18 @@ static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
/* Propagate the format from sink to source */ /* Propagate the format from sink to source */
if (fmt->pad == PREV_PAD_SINK) { if (fmt->pad == PREV_PAD_SINK) {
/* Reset the crop rectangle. */
crop = __preview_get_crop(prev, fh, fmt->which);
crop->left = 0;
crop->top = 0;
crop->width = fmt->format.width;
crop->height = fmt->format.height;
preview_try_crop(prev, &fmt->format, crop);
/* Update the source format. */
format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, format = __preview_get_format(prev, fh, PREV_PAD_SOURCE,
fmt->which); fmt->which);
*format = fmt->format;
preview_try_format(prev, fh, PREV_PAD_SOURCE, format, preview_try_format(prev, fh, PREV_PAD_SOURCE, format,
fmt->which); fmt->which);
} }
...@@ -1856,6 +1984,8 @@ static const struct v4l2_subdev_pad_ops preview_v4l2_pad_ops = { ...@@ -1856,6 +1984,8 @@ static const struct v4l2_subdev_pad_ops preview_v4l2_pad_ops = {
.enum_frame_size = preview_enum_frame_size, .enum_frame_size = preview_enum_frame_size,
.get_fmt = preview_get_format, .get_fmt = preview_get_format,
.set_fmt = preview_set_format, .set_fmt = preview_set_format,
.get_crop = preview_get_crop,
.set_crop = preview_set_crop,
}; };
/* subdev operations */ /* subdev operations */
......
...@@ -152,6 +152,7 @@ struct isptables_update { ...@@ -152,6 +152,7 @@ struct isptables_update {
* @subdev: V4L2 subdevice * @subdev: V4L2 subdevice
* @pads: Media entity pads * @pads: Media entity pads
* @formats: Active formats at the subdev pad * @formats: Active formats at the subdev pad
* @crop: Active crop rectangle
* @input: Module currently connected to the input pad * @input: Module currently connected to the input pad
* @output: Bitmask of the active output * @output: Bitmask of the active output
* @video_in: Input video entity * @video_in: Input video entity
...@@ -170,6 +171,7 @@ struct isp_prev_device { ...@@ -170,6 +171,7 @@ struct isp_prev_device {
struct v4l2_subdev subdev; struct v4l2_subdev subdev;
struct media_pad pads[PREV_PADS_NUM]; struct media_pad pads[PREV_PADS_NUM];
struct v4l2_mbus_framefmt formats[PREV_PADS_NUM]; struct v4l2_mbus_framefmt formats[PREV_PADS_NUM];
struct v4l2_rect crop;
struct v4l2_ctrl_handler ctrls; struct v4l2_ctrl_handler ctrls;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment