Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+196 −66
Original line number Diff line number Diff line
@@ -76,6 +76,42 @@ static struct omap3isp_prev_csc flr_prev_csc = {

#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_HEIGHT	8
#define PREV_MAX_IN_HEIGHT	16384
@@ -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
 * not true, rows and columns must be manually cropped at the preview engine
 * 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)
{
	struct isp_device *isp = to_isp_device(prev);
	struct prev_params *params = &prev->params;
	struct v4l2_mbus_framefmt *format = &prev->formats[PREV_PAD_SINK];
	unsigned int sph = 0;
	unsigned int eph = format->width - 1;
	unsigned int slv = 0;
	unsigned int elv = format->height - 1;

	if (prev->input == PREVIEW_INPUT_CCDC) {
		sph += 2;
		eph -= 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))) {
		sph += 2;
		eph -= 2;
		slv += 2;
		elv -= 2;
	}
	if (!(params->features & PREV_HORZ_MEDIAN_FILTER)) {
		sph += 2;
		eph -= 2;
	}
	if (!(params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE)))
		sph += 2;
	unsigned int sph = prev->crop.left;
	unsigned int eph = prev->crop.left + prev->crop.width - 1;
	unsigned int slv = prev->crop.top;
	unsigned int elv = prev->crop.top + prev->crop.height - 1;

	if (params->features & PREV_CFA) {
		sph -= 2;
		eph += 2;
		slv -= 2;
		elv += 2;
	}
	if (params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER)) {
		sph -= 2;
		eph += 2;
		slv -= 2;
		elv += 2;
	}
	if (params->features & PREV_HORZ_MEDIAN_FILTER) {
		sph -= 2;
		eph += 2;
	}
	if (params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE))
		sph -= 2;

	isp_reg_writel(isp, (sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | eph,
		       OMAP3_ISP_IOMEM_PREV, ISPPRV_HORZ_INFO);
@@ -1597,6 +1617,16 @@ __preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh,
		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 */
static const unsigned int preview_input_fmts[] = {
	V4L2_MBUS_FMT_SGRBG10_1X10,
@@ -1611,19 +1641,23 @@ static const unsigned int preview_output_fmts[] = {
};

/*
 * preview_try_format - Handle try format by pad subdev method
 * @prev: ISP preview device
 * preview_try_format - Validate a format
 * @prev: ISP preview engine
 * @fh: V4L2 subdev file handle
 * @pad: pad num
 * @fmt: pointer to v4l2 format structure
 * @pad: pad number
 * @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,
			       struct v4l2_subdev_fh *fh, unsigned int pad,
			       struct v4l2_mbus_framefmt *fmt,
			       enum v4l2_subdev_format_whence which)
{
	struct v4l2_mbus_framefmt *format;
	enum v4l2_mbus_pixelcode pixelcode;
	struct v4l2_rect *crop;
	unsigned int i;

	switch (pad) {
@@ -1659,15 +1693,8 @@ static void preview_try_format(struct isp_prev_device *prev,

	case PREV_PAD_SOURCE:
		pixelcode = fmt->code;
		format = __preview_get_format(prev, fh, PREV_PAD_SINK, which);
		memcpy(fmt, format, sizeof(*fmt));
		*fmt = *__preview_get_format(prev, fh, PREV_PAD_SINK, which);

		/* 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) {
		case V4L2_MBUS_FMT_YUYV8_1X16:
		case V4L2_MBUS_FMT_UYVY8_1X16:
@@ -1679,20 +1706,14 @@ static void preview_try_format(struct isp_prev_device *prev,
			break;
		}

		/* The TRM states (12.1.4.7.1.2) that 2 pixels must be cropped
		 * from the left and right sides when the input source is the
		 * CCDC. This seems not to be needed in practice, investigation
		 * is required.
		 */
		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.
		/* The preview module output size is configurable through the
		 * averager (horizontal scaling by 1/1, 1/2, 1/4 or 1/8). This
		 * is not supported yet, hardcode the output size to the crop
		 * rectangle size.
		 */
		fmt->width -= 14;
		fmt->height -= 8;
		crop = __preview_get_crop(prev, fh, which);
		fmt->width = crop->width;
		fmt->height = crop->height;

		fmt->colorspace = V4L2_COLORSPACE_JPEG;
		break;
@@ -1701,6 +1722,49 @@ static void preview_try_format(struct isp_prev_device *prev,
	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
 * @sd     : pointer to v4l2 subdev structure
@@ -1762,6 +1826,60 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd,
	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
 * @sd : pointer to v4l2 subdev structure
@@ -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 v4l2_mbus_framefmt *format;
	struct v4l2_rect *crop;

	format = __preview_get_format(prev, fh, fmt->pad, fmt->which);
	if (format == NULL)
@@ -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 */
	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,
					      fmt->which);
		*format = fmt->format;
		preview_try_format(prev, fh, PREV_PAD_SOURCE, format,
				   fmt->which);
	}
@@ -1856,6 +1984,8 @@ static const struct v4l2_subdev_pad_ops preview_v4l2_pad_ops = {
	.enum_frame_size = preview_enum_frame_size,
	.get_fmt = preview_get_format,
	.set_fmt = preview_set_format,
	.get_crop = preview_get_crop,
	.set_crop = preview_set_crop,
};

/* subdev operations */
+2 −0
Original line number Diff line number Diff line
@@ -152,6 +152,7 @@ struct isptables_update {
 * @subdev: V4L2 subdevice
 * @pads: Media entity pads
 * @formats: Active formats at the subdev pad
 * @crop: Active crop rectangle
 * @input: Module currently connected to the input pad
 * @output: Bitmask of the active output
 * @video_in: Input video entity
@@ -170,6 +171,7 @@ struct isp_prev_device {
	struct v4l2_subdev subdev;
	struct media_pad pads[PREV_PADS_NUM];
	struct v4l2_mbus_framefmt formats[PREV_PADS_NUM];
	struct v4l2_rect crop;

	struct v4l2_ctrl_handler ctrls;