diff options
Diffstat (limited to 'drivers/media')
4 files changed, 184 insertions, 175 deletions
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index 60579c613ee3..b5ceae1396e9 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -28,126 +28,6 @@ #include "sun6i_csi.h" #include "sun6i_csi_reg.h" -/* Helpers */ - -/* TODO add 10&12 bit YUV, RGB support */ -bool sun6i_csi_is_format_supported(struct sun6i_csi_device *csi_dev, - u32 pixformat, u32 mbus_code) -{ - struct v4l2_fwnode_endpoint *endpoint = - &csi_dev->bridge.source_parallel.endpoint; - - /* - * Some video receivers have the ability to be compatible with - * 8bit and 16bit bus width. - * Identify the media bus format from device tree. - */ - if ((endpoint->bus_type == V4L2_MBUS_PARALLEL - || endpoint->bus_type == V4L2_MBUS_BT656) - && endpoint->bus.parallel.bus_width == 16) { - switch (pixformat) { - case V4L2_PIX_FMT_NV12_16L16: - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - case V4L2_PIX_FMT_YUV422P: - switch (mbus_code) { - case MEDIA_BUS_FMT_UYVY8_1X16: - case MEDIA_BUS_FMT_VYUY8_1X16: - case MEDIA_BUS_FMT_YUYV8_1X16: - case MEDIA_BUS_FMT_YVYU8_1X16: - return true; - default: - dev_dbg(csi_dev->dev, - "Unsupported mbus code: 0x%x\n", - mbus_code); - break; - } - break; - default: - dev_dbg(csi_dev->dev, "Unsupported pixformat: 0x%x\n", - pixformat); - break; - } - return false; - } - - switch (pixformat) { - case V4L2_PIX_FMT_SBGGR8: - return (mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8); - case V4L2_PIX_FMT_SGBRG8: - return (mbus_code == MEDIA_BUS_FMT_SGBRG8_1X8); - case V4L2_PIX_FMT_SGRBG8: - return (mbus_code == MEDIA_BUS_FMT_SGRBG8_1X8); - case V4L2_PIX_FMT_SRGGB8: - return (mbus_code == MEDIA_BUS_FMT_SRGGB8_1X8); - case V4L2_PIX_FMT_SBGGR10: - return (mbus_code == MEDIA_BUS_FMT_SBGGR10_1X10); - case V4L2_PIX_FMT_SGBRG10: - return (mbus_code == MEDIA_BUS_FMT_SGBRG10_1X10); - case V4L2_PIX_FMT_SGRBG10: - return (mbus_code == MEDIA_BUS_FMT_SGRBG10_1X10); - case V4L2_PIX_FMT_SRGGB10: - return (mbus_code == MEDIA_BUS_FMT_SRGGB10_1X10); - case V4L2_PIX_FMT_SBGGR12: - return (mbus_code == MEDIA_BUS_FMT_SBGGR12_1X12); - case V4L2_PIX_FMT_SGBRG12: - return (mbus_code == MEDIA_BUS_FMT_SGBRG12_1X12); - case V4L2_PIX_FMT_SGRBG12: - return (mbus_code == MEDIA_BUS_FMT_SGRBG12_1X12); - case V4L2_PIX_FMT_SRGGB12: - return (mbus_code == MEDIA_BUS_FMT_SRGGB12_1X12); - - case V4L2_PIX_FMT_YUYV: - return (mbus_code == MEDIA_BUS_FMT_YUYV8_2X8); - case V4L2_PIX_FMT_YVYU: - return (mbus_code == MEDIA_BUS_FMT_YVYU8_2X8); - case V4L2_PIX_FMT_UYVY: - return (mbus_code == MEDIA_BUS_FMT_UYVY8_2X8); - case V4L2_PIX_FMT_VYUY: - return (mbus_code == MEDIA_BUS_FMT_VYUY8_2X8); - - case V4L2_PIX_FMT_NV12_16L16: - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - case V4L2_PIX_FMT_YUV422P: - switch (mbus_code) { - case MEDIA_BUS_FMT_UYVY8_2X8: - case MEDIA_BUS_FMT_VYUY8_2X8: - case MEDIA_BUS_FMT_YUYV8_2X8: - case MEDIA_BUS_FMT_YVYU8_2X8: - return true; - default: - dev_dbg(csi_dev->dev, "Unsupported mbus code: 0x%x\n", - mbus_code); - break; - } - break; - - case V4L2_PIX_FMT_RGB565: - return (mbus_code == MEDIA_BUS_FMT_RGB565_2X8_LE); - case V4L2_PIX_FMT_RGB565X: - return (mbus_code == MEDIA_BUS_FMT_RGB565_2X8_BE); - - case V4L2_PIX_FMT_JPEG: - return (mbus_code == MEDIA_BUS_FMT_JPEG_1X8); - - default: - dev_dbg(csi_dev->dev, "Unsupported pixformat: 0x%x\n", - pixformat); - break; - } - - return false; -} - /* Media */ static const struct media_device_ops sun6i_csi_media_ops = { diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h index a4df8f8d2980..e3f9c49bb829 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h @@ -49,15 +49,4 @@ struct sun6i_csi_variant { unsigned long clock_mod_rate; }; -/** - * sun6i_csi_is_format_supported() - check if the format supported by csi - * @csi_dev: pointer to the csi device - * @pixformat: v4l2 pixel format (V4L2_PIX_FMT_*) - * @mbus_code: media bus format code (MEDIA_BUS_FMT_*) - * - * Return: true if format is supported, false otherwise. - */ -bool sun6i_csi_is_format_supported(struct sun6i_csi_device *csi_dev, - u32 pixformat, u32 mbus_code); - #endif /* __SUN6I_CSI_H__ */ 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 = { diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h index 4b1ff19edc2f..2605b16f091c 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h @@ -27,6 +27,11 @@ struct sun6i_csi_capture_format { u32 hsize_len_factor; }; +struct sun6i_csi_capture_format_match { + u32 pixelformat; + u32 mbus_code; +}; + #undef current struct sun6i_csi_capture_state { struct list_head queue; |