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

[media] omap3isp: ccdc: Add selection support on output formatter source pad

Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: default avatarSakari Ailus <sakari.ailus@iki.fi>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 79c3a07d
...@@ -38,6 +38,9 @@ ...@@ -38,6 +38,9 @@
#include "ispreg.h" #include "ispreg.h"
#include "ispccdc.h" #include "ispccdc.h"
#define CCDC_MIN_WIDTH 32
#define CCDC_MIN_HEIGHT 32
static struct v4l2_mbus_framefmt * static struct v4l2_mbus_framefmt *
__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, __ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
unsigned int pad, enum v4l2_subdev_format_whence which); unsigned int pad, enum v4l2_subdev_format_whence which);
...@@ -1118,6 +1121,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) ...@@ -1118,6 +1121,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
struct isp_parallel_platform_data *pdata = NULL; struct isp_parallel_platform_data *pdata = NULL;
struct v4l2_subdev *sensor; struct v4l2_subdev *sensor;
struct v4l2_mbus_framefmt *format; struct v4l2_mbus_framefmt *format;
const struct v4l2_rect *crop;
const struct isp_format_info *fmt_info; const struct isp_format_info *fmt_info;
struct v4l2_subdev_format fmt_src; struct v4l2_subdev_format fmt_src;
unsigned int depth_out; unsigned int depth_out;
...@@ -1211,14 +1215,14 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) ...@@ -1211,14 +1215,14 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT); OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT);
/* CCDC_PAD_SOURCE_OF */ /* CCDC_PAD_SOURCE_OF */
format = &ccdc->formats[CCDC_PAD_SOURCE_OF]; crop = &ccdc->crop;
isp_reg_writel(isp, (0 << ISPCCDC_HORZ_INFO_SPH_SHIFT) | isp_reg_writel(isp, (crop->left << ISPCCDC_HORZ_INFO_SPH_SHIFT) |
((format->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT), ((crop->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT),
OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO); OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO);
isp_reg_writel(isp, 0 << ISPCCDC_VERT_START_SLV0_SHIFT, isp_reg_writel(isp, crop->top << ISPCCDC_VERT_START_SLV0_SHIFT,
OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START); OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START);
isp_reg_writel(isp, (format->height - 1) isp_reg_writel(isp, (crop->height - 1)
<< ISPCCDC_VERT_LINES_NLV_SHIFT, << ISPCCDC_VERT_LINES_NLV_SHIFT,
OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES); OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES);
...@@ -1793,6 +1797,16 @@ __ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, ...@@ -1793,6 +1797,16 @@ __ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
return &ccdc->formats[pad]; return &ccdc->formats[pad];
} }
static struct v4l2_rect *
__ccdc_get_crop(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
return v4l2_subdev_get_try_crop(fh, CCDC_PAD_SOURCE_OF);
else
return &ccdc->crop;
}
/* /*
* ccdc_try_format - Try video format on a pad * ccdc_try_format - Try video format on a pad
* @ccdc: ISP CCDC device * @ccdc: ISP CCDC device
...@@ -1809,6 +1823,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, ...@@ -1809,6 +1823,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
const struct isp_format_info *info; const struct isp_format_info *info;
unsigned int width = fmt->width; unsigned int width = fmt->width;
unsigned int height = fmt->height; unsigned int height = fmt->height;
struct v4l2_rect *crop;
unsigned int i; unsigned int i;
switch (pad) { switch (pad) {
...@@ -1834,14 +1849,10 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, ...@@ -1834,14 +1849,10 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which);
memcpy(fmt, format, sizeof(*fmt)); memcpy(fmt, format, sizeof(*fmt));
/* The data formatter truncates the number of horizontal output /* Hardcode the output size to the crop rectangle size. */
* pixels to a multiple of 16. To avoid clipping data, allow crop = __ccdc_get_crop(ccdc, fh, which);
* callers to request an output size bigger than the input size fmt->width = crop->width;
* up to the nearest multiple of 16. fmt->height = crop->height;
*/
fmt->width = clamp_t(u32, width, 32, fmt->width + 15);
fmt->width &= ~15;
fmt->height = clamp_t(u32, height, 32, fmt->height);
break; break;
case CCDC_PAD_SOURCE_VP: case CCDC_PAD_SOURCE_VP:
...@@ -1868,6 +1879,49 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, ...@@ -1868,6 +1879,49 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
fmt->field = V4L2_FIELD_NONE; fmt->field = V4L2_FIELD_NONE;
} }
/*
* ccdc_try_crop - Validate a crop rectangle
* @ccdc: ISP CCDC device
* @sink: format on the sink pad
* @crop: crop rectangle to be validated
*/
static void ccdc_try_crop(struct isp_ccdc_device *ccdc,
const struct v4l2_mbus_framefmt *sink,
struct v4l2_rect *crop)
{
const struct isp_format_info *info;
unsigned int 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 != V4L2_MBUS_FMT_Y8_1X8) {
crop->left &= ~1;
crop->top &= ~1;
}
crop->left = clamp_t(u32, crop->left, 0, sink->width - CCDC_MIN_WIDTH);
crop->top = clamp_t(u32, crop->top, 0, sink->height - CCDC_MIN_HEIGHT);
/* 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 != V4L2_MBUS_FMT_Y8_1X8) {
crop->width &= ~1;
crop->height &= ~1;
}
}
/* /*
* ccdc_enum_mbus_code - Handle pixel format enumeration * ccdc_enum_mbus_code - Handle pixel format enumeration
* @sd : pointer to v4l2 subdev structure * @sd : pointer to v4l2 subdev structure
...@@ -1939,6 +1993,93 @@ static int ccdc_enum_frame_size(struct v4l2_subdev *sd, ...@@ -1939,6 +1993,93 @@ static int ccdc_enum_frame_size(struct v4l2_subdev *sd,
return 0; return 0;
} }
/*
* ccdc_get_selection - Retrieve a selection rectangle on a pad
* @sd: ISP CCDC V4L2 subdevice
* @fh: V4L2 subdev file handle
* @sel: Selection rectangle
*
* The only supported rectangles are the crop rectangles on the output formatter
* source pad.
*
* Return 0 on success or a negative error code otherwise.
*/
static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_selection *sel)
{
struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
if (sel->pad != CCDC_PAD_SOURCE_OF)
return -EINVAL;
switch (sel->target) {
case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
sel->r.left = 0;
sel->r.top = 0;
sel->r.width = INT_MAX;
sel->r.height = INT_MAX;
format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which);
ccdc_try_crop(ccdc, format, &sel->r);
break;
case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
sel->r = *__ccdc_get_crop(ccdc, fh, sel->which);
break;
default:
return -EINVAL;
}
return 0;
}
/*
* ccdc_set_selection - Set a selection rectangle on a pad
* @sd: ISP CCDC V4L2 subdevice
* @fh: V4L2 subdev file handle
* @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.
*/
static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_selection *sel)
{
struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL ||
sel->pad != CCDC_PAD_SOURCE_OF)
return -EINVAL;
/* The crop rectangle can't be changed while streaming. */
if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED)
return -EBUSY;
/* Modifying the crop rectangle always changes the format on the source
* pad. If the KEEP_CONFIG flag is set, just return the current crop
* rectangle.
*/
if (sel->flags & V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG) {
sel->r = *__ccdc_get_crop(ccdc, fh, sel->which);
return 0;
}
format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which);
ccdc_try_crop(ccdc, format, &sel->r);
*__ccdc_get_crop(ccdc, fh, sel->which) = sel->r;
/* Update the source format. */
format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, sel->which);
ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format, sel->which);
return 0;
}
/* /*
* ccdc_get_format - Retrieve the video format on a pad * ccdc_get_format - Retrieve the video format on a pad
* @sd : ISP CCDC V4L2 subdevice * @sd : ISP CCDC V4L2 subdevice
...@@ -1976,6 +2117,7 @@ static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ...@@ -1976,6 +2117,7 @@ static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
{ {
struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format; struct v4l2_mbus_framefmt *format;
struct v4l2_rect *crop;
format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which);
if (format == NULL) if (format == NULL)
...@@ -1986,6 +2128,16 @@ static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ...@@ -1986,6 +2128,16 @@ static int ccdc_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 == CCDC_PAD_SINK) { if (fmt->pad == CCDC_PAD_SINK) {
/* Reset the crop rectangle. */
crop = __ccdc_get_crop(ccdc, fh, fmt->which);
crop->left = 0;
crop->top = 0;
crop->width = fmt->format.width;
crop->height = fmt->format.height;
ccdc_try_crop(ccdc, &fmt->format, crop);
/* Update the source formats. */
format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF,
fmt->which); fmt->which);
*format = fmt->format; *format = fmt->format;
...@@ -2044,6 +2196,8 @@ static const struct v4l2_subdev_pad_ops ccdc_v4l2_pad_ops = { ...@@ -2044,6 +2196,8 @@ static const struct v4l2_subdev_pad_ops ccdc_v4l2_pad_ops = {
.enum_frame_size = ccdc_enum_frame_size, .enum_frame_size = ccdc_enum_frame_size,
.get_fmt = ccdc_get_format, .get_fmt = ccdc_get_format,
.set_fmt = ccdc_set_format, .set_fmt = ccdc_set_format,
.get_selection = ccdc_get_selection,
.set_selection = ccdc_set_selection,
}; };
/* V4L2 subdev operations */ /* V4L2 subdev operations */
......
...@@ -147,6 +147,7 @@ struct ispccdc_lsc { ...@@ -147,6 +147,7 @@ struct ispccdc_lsc {
* @subdev: V4L2 subdevice * @subdev: V4L2 subdevice
* @pads: Sink and source media entity pads * @pads: Sink and source media entity pads
* @formats: Active video formats * @formats: Active video formats
* @crop: Active crop rectangle on the OF source pad
* @input: Active input * @input: Active input
* @output: Active outputs * @output: Active outputs
* @video_out: Output video node * @video_out: Output video node
...@@ -173,6 +174,7 @@ struct isp_ccdc_device { ...@@ -173,6 +174,7 @@ struct isp_ccdc_device {
struct v4l2_subdev subdev; struct v4l2_subdev subdev;
struct media_pad pads[CCDC_PADS_NUM]; struct media_pad pads[CCDC_PADS_NUM];
struct v4l2_mbus_framefmt formats[CCDC_PADS_NUM]; struct v4l2_mbus_framefmt formats[CCDC_PADS_NUM];
struct v4l2_rect crop;
enum ccdc_input_entity input; enum ccdc_input_entity input;
unsigned int output; unsigned int output;
......
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