summaryrefslogtreecommitdiffstats
path: root/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c')
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c223
1 files changed, 179 insertions, 44 deletions
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
index 75984bfa4576..239875f2b530 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
@@ -227,6 +227,123 @@ struct sun6i_csi_capture_format *sun6i_csi_capture_format_find(u32 pixelformat)
return NULL;
}
+/* RAW formats need an exact match between pixel and mbus formats. */
+static const
+struct sun6i_csi_capture_format_match sun6i_csi_capture_format_matches[] = {
+ /* YUV420 */
+ {
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_YVYU,
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_YVYU,
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_VYUY,
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_VYUY,
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16,
+ },
+ /* RGB */
+ {
+ .pixelformat = V4L2_PIX_FMT_RGB565,
+ .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_RGB565X,
+ .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_BE,
+ },
+ /* Bayer */
+ {
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_SGBRG8,
+ .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_SGRBG8,
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_SRGGB8,
+ .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_SBGGR10,
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_SGBRG10,
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_SGRBG10,
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_SRGGB10,
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_SBGGR12,
+ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_SGBRG12,
+ .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_SGRBG12,
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_SRGGB12,
+ .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ },
+ /* Compressed */
+ {
+ .pixelformat = V4L2_PIX_FMT_JPEG,
+ .mbus_code = MEDIA_BUS_FMT_JPEG_1X8,
+ },
+};
+
+static bool sun6i_csi_capture_format_match(u32 pixelformat, u32 mbus_code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(sun6i_csi_capture_format_matches); i++) {
+ const struct sun6i_csi_capture_format_match *match =
+ &sun6i_csi_capture_format_matches[i];
+
+ if (match->pixelformat == pixelformat &&
+ match->mbus_code == mbus_code)
+ return true;
+ }
+
+ return false;
+}
+
/* Capture */
static void sun6i_csi_capture_irq_enable(struct sun6i_csi_device *csi_dev)
@@ -1019,63 +1136,81 @@ static const struct v4l2_file_operations sun6i_csi_capture_fops = {
/* Media Entity */
-static int
-sun6i_csi_capture_link_validate_get_format(struct media_pad *pad,
- struct v4l2_subdev_format *fmt)
+static int sun6i_csi_capture_link_validate(struct media_link *link)
{
- if (is_media_entity_v4l2_subdev(pad->entity)) {
- struct v4l2_subdev *sd =
- media_entity_to_v4l2_subdev(pad->entity);
+ struct video_device *video_dev =
+ media_entity_to_video_device(link->sink->entity);
+ struct sun6i_csi_device *csi_dev = video_get_drvdata(video_dev);
+ struct v4l2_device *v4l2_dev = &csi_dev->v4l2.v4l2_dev;
+ const struct sun6i_csi_capture_format *capture_format;
+ const struct sun6i_csi_bridge_format *bridge_format;
+ unsigned int capture_width, capture_height;
+ unsigned int bridge_width, bridge_height;
+ const struct v4l2_format_info *format_info;
+ u32 pixelformat, capture_field;
+ u32 mbus_code, bridge_field;
+ bool match;
- fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
- fmt->pad = pad->index;
- return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
- }
+ sun6i_csi_capture_dimensions(csi_dev, &capture_width, &capture_height);
- return -EINVAL;
-}
+ sun6i_csi_capture_format(csi_dev, &pixelformat, &capture_field);
+ capture_format = sun6i_csi_capture_format_find(pixelformat);
+ if (WARN_ON(!capture_format))
+ return -EINVAL;
-static int sun6i_csi_capture_link_validate(struct media_link *link)
-{
- struct video_device *vdev = container_of(link->sink->entity,
- struct video_device, entity);
- struct sun6i_csi_device *csi_dev = video_get_drvdata(vdev);
- struct sun6i_csi_capture *capture = &csi_dev->capture;
- struct v4l2_subdev_format source_fmt;
- int ret;
+ sun6i_csi_bridge_dimensions(csi_dev, &bridge_width, &bridge_height);
- if (!media_pad_remote_pad_first(link->sink->entity->pads)) {
- dev_info(csi_dev->dev, "capture node %s pad not connected\n",
- vdev->name);
- return -ENOLINK;
+ sun6i_csi_bridge_format(csi_dev, &mbus_code, &bridge_field);
+ bridge_format = sun6i_csi_bridge_format_find(mbus_code);
+ if (WARN_ON(!bridge_format))
+ return -EINVAL;
+
+ /* No cropping/scaling is supported. */
+ if (capture_width != bridge_width || capture_height != bridge_height) {
+ v4l2_err(v4l2_dev,
+ "invalid input/output dimensions: %ux%u/%ux%u\n",
+ bridge_width, bridge_height, capture_width,
+ capture_height);
+ return -EINVAL;
}
- ret = sun6i_csi_capture_link_validate_get_format(link->source,
- &source_fmt);
- if (ret < 0)
- return ret;
+ format_info = v4l2_format_info(pixelformat);
+ /* Some formats are not listed. */
+ if (!format_info)
+ return 0;
- if (!sun6i_csi_is_format_supported(csi_dev,
- capture->format.fmt.pix.pixelformat,
- source_fmt.format.code)) {
- dev_err(csi_dev->dev,
- "Unsupported pixformat: 0x%x with mbus code: 0x%x!\n",
- capture->format.fmt.pix.pixelformat,
- source_fmt.format.code);
- return -EPIPE;
+ if (format_info->pixel_enc == V4L2_PIXEL_ENC_BAYER &&
+ bridge_format->input_format != SUN6I_CSI_INPUT_FMT_RAW)
+ goto invalid;
+
+ if (format_info->pixel_enc == V4L2_PIXEL_ENC_RGB &&
+ bridge_format->input_format != SUN6I_CSI_INPUT_FMT_RAW)
+ goto invalid;
+
+ if (format_info->pixel_enc == V4L2_PIXEL_ENC_YUV) {
+ if (bridge_format->input_format != SUN6I_CSI_INPUT_FMT_YUV420 &&
+ bridge_format->input_format != SUN6I_CSI_INPUT_FMT_YUV422)
+ goto invalid;
+
+ /* YUV420 input can't produce YUV422 output. */
+ if (bridge_format->input_format == SUN6I_CSI_INPUT_FMT_YUV420 &&
+ format_info->vdiv == 1)
+ goto invalid;
}
- if (source_fmt.format.width != capture->format.fmt.pix.width ||
- source_fmt.format.height != capture->format.fmt.pix.height) {
- dev_err(csi_dev->dev,
- "Wrong width or height %ux%u (%ux%u expected)\n",
- capture->format.fmt.pix.width,
- capture->format.fmt.pix.height,
- source_fmt.format.width, source_fmt.format.height);
- return -EPIPE;
+ /* With raw input mode, we need a 1:1 match between input and output. */
+ if (bridge_format->input_format == SUN6I_CSI_INPUT_FMT_RAW ||
+ capture_format->input_format_raw) {
+ match = sun6i_csi_capture_format_match(pixelformat, mbus_code);
+ if (!match)
+ goto invalid;
}
return 0;
+
+invalid:
+ v4l2_err(v4l2_dev, "invalid input/output format combination\n");
+ return -EINVAL;
}
static const struct media_entity_operations sun6i_csi_capture_media_ops = {