summaryrefslogtreecommitdiff
path: root/drivers/media/platform/renesas/vsp1/vsp1_video.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform/renesas/vsp1/vsp1_video.c')
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_video.c274
1 files changed, 132 insertions, 142 deletions
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c
index 5a9cb0e5640e..bc66fbdde3cc 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_video.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c
@@ -78,8 +78,14 @@ static int vsp1_video_verify_format(struct vsp1_video *video)
if (video->rwpf->fmtinfo->mbus != fmt.format.code ||
video->rwpf->format.height != fmt.format.height ||
- video->rwpf->format.width != fmt.format.width)
- return -EINVAL;
+ video->rwpf->format.width != fmt.format.width) {
+ dev_dbg(video->vsp1->dev,
+ "Format mismatch: 0x%04x/%ux%u != 0x%04x/%ux%u\n",
+ video->rwpf->fmtinfo->mbus, video->rwpf->format.width,
+ video->rwpf->format.height, fmt.format.code,
+ fmt.format.width, fmt.format.height);
+ return -EPIPE;
+ }
return 0;
}
@@ -121,12 +127,24 @@ static int __vsp1_video_try_format(struct vsp1_video *video,
info = vsp1_get_format_info(video->vsp1, VSP1_VIDEO_DEF_FORMAT);
pix->pixelformat = info->fourcc;
- pix->colorspace = V4L2_COLORSPACE_SRGB;
pix->field = V4L2_FIELD_NONE;
- if (info->fourcc == V4L2_PIX_FMT_HSV24 ||
- info->fourcc == V4L2_PIX_FMT_HSV32)
- pix->hsv_enc = V4L2_HSV_ENC_256;
+ /*
+ * Adjust the colour space fields. On capture devices, userspace needs
+ * to set the V4L2_PIX_FMT_FLAG_SET_CSC to override the defaults. Reset
+ * all fields to *_DEFAULT if the flag isn't set, to then handle
+ * capture and output devices in the same way.
+ */
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ !(pix->flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
+ pix->colorspace = V4L2_COLORSPACE_DEFAULT;
+ pix->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ pix->quantization = V4L2_QUANTIZATION_DEFAULT;
+ }
+
+ vsp1_adjust_color_space(info->mbus, &pix->colorspace, &pix->xfer_func,
+ &pix->ycbcr_enc, &pix->quantization);
memset(pix->reserved, 0, sizeof(pix->reserved));
@@ -173,131 +191,6 @@ static int __vsp1_video_try_format(struct vsp1_video *video,
}
/* -----------------------------------------------------------------------------
- * VSP1 Partition Algorithm support
- */
-
-/**
- * vsp1_video_calculate_partition - Calculate the active partition output window
- *
- * @pipe: the pipeline
- * @partition: partition that will hold the calculated values
- * @div_size: pre-determined maximum partition division size
- * @index: partition index
- */
-static void vsp1_video_calculate_partition(struct vsp1_pipeline *pipe,
- struct vsp1_partition *partition,
- unsigned int div_size,
- unsigned int index)
-{
- const struct v4l2_mbus_framefmt *format;
- struct vsp1_partition_window window;
- unsigned int modulus;
-
- /*
- * Partitions are computed on the size before rotation, use the format
- * at the WPF sink.
- */
- format = vsp1_entity_get_pad_format(&pipe->output->entity,
- pipe->output->entity.state,
- RWPF_PAD_SINK);
-
- /* A single partition simply processes the output size in full. */
- if (pipe->partitions <= 1) {
- window.left = 0;
- window.width = format->width;
-
- vsp1_pipeline_propagate_partition(pipe, partition, index,
- &window);
- return;
- }
-
- /* Initialise the partition with sane starting conditions. */
- window.left = index * div_size;
- window.width = div_size;
-
- modulus = format->width % div_size;
-
- /*
- * We need to prevent the last partition from being smaller than the
- * *minimum* width of the hardware capabilities.
- *
- * If the modulus is less than half of the partition size,
- * the penultimate partition is reduced to half, which is added
- * to the final partition: |1234|1234|1234|12|341|
- * to prevent this: |1234|1234|1234|1234|1|.
- */
- if (modulus) {
- /*
- * pipe->partitions is 1 based, whilst index is a 0 based index.
- * Normalise this locally.
- */
- unsigned int partitions = pipe->partitions - 1;
-
- if (modulus < div_size / 2) {
- if (index == partitions - 1) {
- /* Halve the penultimate partition. */
- window.width = div_size / 2;
- } else if (index == partitions) {
- /* Increase the final partition. */
- window.width = (div_size / 2) + modulus;
- window.left -= div_size / 2;
- }
- } else if (index == partitions) {
- window.width = modulus;
- }
- }
-
- vsp1_pipeline_propagate_partition(pipe, partition, index, &window);
-}
-
-static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe)
-{
- struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
- const struct v4l2_mbus_framefmt *format;
- struct vsp1_entity *entity;
- unsigned int div_size;
- unsigned int i;
-
- /*
- * Partitions are computed on the size before rotation, use the format
- * at the WPF sink.
- */
- format = vsp1_entity_get_pad_format(&pipe->output->entity,
- pipe->output->entity.state,
- RWPF_PAD_SINK);
- div_size = format->width;
-
- /*
- * Only Gen3+ hardware requires image partitioning, Gen2 will operate
- * with a single partition that covers the whole output.
- */
- if (vsp1->info->gen >= 3) {
- list_for_each_entry(entity, &pipe->entities, list_pipe) {
- unsigned int entity_max;
-
- if (!entity->ops->max_width)
- continue;
-
- entity_max = entity->ops->max_width(entity, pipe);
- if (entity_max)
- div_size = min(div_size, entity_max);
- }
- }
-
- pipe->partitions = DIV_ROUND_UP(format->width, div_size);
- pipe->part_table = kcalloc(pipe->partitions, sizeof(*pipe->part_table),
- GFP_KERNEL);
- if (!pipe->part_table)
- return -ENOMEM;
-
- for (i = 0; i < pipe->partitions; ++i)
- vsp1_video_calculate_partition(pipe, &pipe->part_table[i],
- div_size, i);
-
- return 0;
-}
-
-/* -----------------------------------------------------------------------------
* Pipeline Management
*/
@@ -365,13 +258,12 @@ static void vsp1_video_pipeline_run_partition(struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl,
unsigned int partition)
{
+ struct vsp1_partition *part = &pipe->part_table[partition];
struct vsp1_dl_body *dlb = vsp1_dl_list_get_body0(dl);
struct vsp1_entity *entity;
- pipe->partition = &pipe->part_table[partition];
-
list_for_each_entry(entity, &pipe->entities, list_pipe)
- vsp1_entity_configure_partition(entity, pipe, dl, dlb);
+ vsp1_entity_configure_partition(entity, pipe, part, dl, dlb);
}
static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe)
@@ -646,11 +538,19 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe,
static int vsp1_video_pipeline_init(struct vsp1_pipeline *pipe,
struct vsp1_video *video)
{
+ int ret;
+
vsp1_pipeline_init(pipe);
pipe->frame_end = vsp1_video_pipeline_frame_end;
- return vsp1_video_pipeline_build(pipe, video);
+ ret = vsp1_video_pipeline_build(pipe, video);
+ if (ret)
+ return ret;
+
+ vsp1_pipeline_dump(pipe, "video");
+
+ return 0;
}
static struct vsp1_pipeline *vsp1_video_pipeline_get(struct vsp1_video *video)
@@ -784,6 +684,54 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb)
spin_unlock_irqrestore(&pipe->irqlock, flags);
}
+static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe)
+{
+ struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
+ const struct v4l2_mbus_framefmt *format;
+ struct vsp1_entity *entity;
+ unsigned int div_size;
+ unsigned int i;
+
+ /*
+ * Partitions are computed on the size before rotation, use the format
+ * at the WPF sink.
+ */
+ format = v4l2_subdev_state_get_format(pipe->output->entity.state,
+ RWPF_PAD_SINK);
+ div_size = format->width;
+
+ /*
+ * Only Gen3+ hardware requires image partitioning, Gen2 will operate
+ * with a single partition that covers the whole output.
+ */
+ if (vsp1->info->gen >= 3) {
+ list_for_each_entry(entity, &pipe->entities, list_pipe) {
+ unsigned int entity_max;
+
+ if (!entity->ops->max_width)
+ continue;
+
+ entity_max = entity->ops->max_width(entity,
+ entity->state,
+ pipe);
+ if (entity_max)
+ div_size = min(div_size, entity_max);
+ }
+ }
+
+ pipe->partitions = DIV_ROUND_UP(format->width, div_size);
+ pipe->part_table = kcalloc(pipe->partitions, sizeof(*pipe->part_table),
+ GFP_KERNEL);
+ if (!pipe->part_table)
+ return -ENOMEM;
+
+ for (i = 0; i < pipe->partitions; ++i)
+ vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[i],
+ div_size, i);
+
+ return 0;
+}
+
static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe)
{
struct vsp1_entity *entity;
@@ -826,7 +774,7 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe)
list_for_each_entry(entity, &pipe->entities, list_pipe) {
vsp1_entity_route_setup(entity, pipe, pipe->stream_config);
- vsp1_entity_configure_stream(entity, pipe, NULL,
+ vsp1_entity_configure_stream(entity, entity->state, pipe, NULL,
pipe->stream_config);
}
@@ -937,8 +885,6 @@ static const struct vb2_ops vsp1_video_queue_qops = {
.queue_setup = vsp1_video_queue_setup,
.buf_prepare = vsp1_video_buffer_prepare,
.buf_queue = vsp1_video_buffer_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.start_streaming = vsp1_video_start_streaming,
.stop_streaming = vsp1_video_stop_streaming,
};
@@ -954,16 +900,36 @@ vsp1_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
struct vsp1_video *video = to_vsp1_video(vfh->vdev);
cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
- | V4L2_CAP_VIDEO_CAPTURE_MPLANE
+ | V4L2_CAP_IO_MC | V4L2_CAP_VIDEO_CAPTURE_MPLANE
| V4L2_CAP_VIDEO_OUTPUT_MPLANE;
-
strscpy(cap->driver, "vsp1", sizeof(cap->driver));
strscpy(cap->card, video->video.name, sizeof(cap->card));
return 0;
}
+static int vsp1_video_enum_format(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct v4l2_fh *vfh = file->private_data;
+ struct vsp1_video *video = to_vsp1_video(vfh->vdev);
+ const struct vsp1_format_info *info;
+
+ info = vsp1_get_format_info_by_index(video->vsp1, f->index, f->mbus_code);
+ if (!info)
+ return -EINVAL;
+
+ f->pixelformat = info->fourcc;
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ info->mbus == MEDIA_BUS_FMT_AYUV8_1X32)
+ f->flags = V4L2_FMT_FLAG_CSC_YCBCR_ENC
+ | V4L2_FMT_FLAG_CSC_QUANTIZATION;
+
+ return 0;
+}
+
static int
vsp1_video_get_format(struct file *file, void *fh, struct v4l2_format *format)
{
@@ -1079,6 +1045,8 @@ err_pipe:
static const struct v4l2_ioctl_ops vsp1_video_ioctl_ops = {
.vidioc_querycap = vsp1_video_querycap,
+ .vidioc_enum_fmt_vid_cap = vsp1_video_enum_format,
+ .vidioc_enum_fmt_vid_out = vsp1_video_enum_format,
.vidioc_g_fmt_vid_cap_mplane = vsp1_video_get_format,
.vidioc_s_fmt_vid_cap_mplane = vsp1_video_set_format,
.vidioc_try_fmt_vid_cap_mplane = vsp1_video_try_format,
@@ -1146,6 +1114,27 @@ static const struct v4l2_file_operations vsp1_video_fops = {
};
/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+static int vsp1_video_link_validate(struct media_link *link)
+{
+ /*
+ * Ideally, link validation should be implemented here instead of
+ * calling vsp1_video_verify_format() in vsp1_video_streamon()
+ * manually. That would however break userspace that start one video
+ * device before configures formats on other video devices in the
+ * pipeline. This operation is just a no-op to silence the warnings
+ * from v4l2_subdev_link_validate().
+ */
+ return 0;
+}
+
+static const struct media_entity_operations vsp1_video_media_ops = {
+ .link_validate = vsp1_video_link_validate,
+};
+
+/* -----------------------------------------------------------------------------
* Suspend and Resume
*/
@@ -1252,14 +1241,14 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
video->pad.flags = MEDIA_PAD_FL_SOURCE;
video->video.vfl_dir = VFL_DIR_TX;
video->video.device_caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE |
- V4L2_CAP_STREAMING;
+ V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
} else {
direction = "output";
video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
video->pad.flags = MEDIA_PAD_FL_SINK;
video->video.vfl_dir = VFL_DIR_RX;
video->video.device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
- V4L2_CAP_STREAMING;
+ V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
}
mutex_init(&video->lock);
@@ -1279,6 +1268,7 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
/* ... and the video node... */
video->video.v4l2_dev = &video->vsp1->v4l2_dev;
+ video->video.entity.ops = &vsp1_video_media_ops;
video->video.fops = &vsp1_video_fops;
snprintf(video->video.name, sizeof(video->video.name), "%s %s",
rwpf->entity.subdev.name, direction);