|
@@ -38,6 +38,9 @@
|
|
|
#include "ispreg.h"
|
|
|
#include "ispccdc.h"
|
|
|
|
|
|
+#define CCDC_MIN_WIDTH 32
|
|
|
+#define CCDC_MIN_HEIGHT 32
|
|
|
+
|
|
|
static struct v4l2_mbus_framefmt *
|
|
|
__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
|
|
|
unsigned int pad, enum v4l2_subdev_format_whence which);
|
|
@@ -1118,6 +1121,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
|
|
|
struct isp_parallel_platform_data *pdata = NULL;
|
|
|
struct v4l2_subdev *sensor;
|
|
|
struct v4l2_mbus_framefmt *format;
|
|
|
+ const struct v4l2_rect *crop;
|
|
|
const struct isp_format_info *fmt_info;
|
|
|
struct v4l2_subdev_format fmt_src;
|
|
|
unsigned int depth_out;
|
|
@@ -1211,14 +1215,14 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
|
|
|
OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT);
|
|
|
|
|
|
/* CCDC_PAD_SOURCE_OF */
|
|
|
- format = &ccdc->formats[CCDC_PAD_SOURCE_OF];
|
|
|
+ crop = &ccdc->crop;
|
|
|
|
|
|
- isp_reg_writel(isp, (0 << ISPCCDC_HORZ_INFO_SPH_SHIFT) |
|
|
|
- ((format->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT),
|
|
|
+ isp_reg_writel(isp, (crop->left << ISPCCDC_HORZ_INFO_SPH_SHIFT) |
|
|
|
+ ((crop->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT),
|
|
|
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);
|
|
|
- isp_reg_writel(isp, (format->height - 1)
|
|
|
+ isp_reg_writel(isp, (crop->height - 1)
|
|
|
<< ISPCCDC_VERT_LINES_NLV_SHIFT,
|
|
|
OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES);
|
|
|
|
|
@@ -1793,6 +1797,16 @@ __ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
|
|
|
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: ISP CCDC device
|
|
@@ -1809,6 +1823,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
|
|
|
const struct isp_format_info *info;
|
|
|
unsigned int width = fmt->width;
|
|
|
unsigned int height = fmt->height;
|
|
|
+ struct v4l2_rect *crop;
|
|
|
unsigned int i;
|
|
|
|
|
|
switch (pad) {
|
|
@@ -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);
|
|
|
memcpy(fmt, format, sizeof(*fmt));
|
|
|
|
|
|
- /* 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.
|
|
|
- */
|
|
|
- fmt->width = clamp_t(u32, width, 32, fmt->width + 15);
|
|
|
- fmt->width &= ~15;
|
|
|
- fmt->height = clamp_t(u32, height, 32, fmt->height);
|
|
|
+ /* Hardcode the output size to the crop rectangle size. */
|
|
|
+ crop = __ccdc_get_crop(ccdc, fh, which);
|
|
|
+ fmt->width = crop->width;
|
|
|
+ fmt->height = crop->height;
|
|
|
break;
|
|
|
|
|
|
case CCDC_PAD_SOURCE_VP:
|
|
@@ -1868,6 +1879,49 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
|
|
|
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
|
|
|
* @sd : pointer to v4l2 subdev structure
|
|
@@ -1939,6 +1993,93 @@ static int ccdc_enum_frame_size(struct v4l2_subdev *sd,
|
|
|
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
|
|
|
* @sd : ISP CCDC V4L2 subdevice
|
|
@@ -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 v4l2_mbus_framefmt *format;
|
|
|
+ struct v4l2_rect *crop;
|
|
|
|
|
|
format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which);
|
|
|
if (format == NULL)
|
|
@@ -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 */
|
|
|
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,
|
|
|
fmt->which);
|
|
|
*format = fmt->format;
|
|
@@ -2044,6 +2196,8 @@ static const struct v4l2_subdev_pad_ops ccdc_v4l2_pad_ops = {
|
|
|
.enum_frame_size = ccdc_enum_frame_size,
|
|
|
.get_fmt = ccdc_get_format,
|
|
|
.set_fmt = ccdc_set_format,
|
|
|
+ .get_selection = ccdc_get_selection,
|
|
|
+ .set_selection = ccdc_set_selection,
|
|
|
};
|
|
|
|
|
|
/* V4L2 subdev operations */
|