18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * vsp1_video.c  --  R-Car VSP1 Video Node
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2013-2015 Renesas Electronics Corporation
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/list.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/mutex.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/v4l2-mediabus.h>
158c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
168c2ecf20Sopenharmony_ci#include <linux/wait.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <media/media-entity.h>
198c2ecf20Sopenharmony_ci#include <media/v4l2-dev.h>
208c2ecf20Sopenharmony_ci#include <media/v4l2-fh.h>
218c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h>
228c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h>
238c2ecf20Sopenharmony_ci#include <media/videobuf2-v4l2.h>
248c2ecf20Sopenharmony_ci#include <media/videobuf2-dma-contig.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include "vsp1.h"
278c2ecf20Sopenharmony_ci#include "vsp1_brx.h"
288c2ecf20Sopenharmony_ci#include "vsp1_dl.h"
298c2ecf20Sopenharmony_ci#include "vsp1_entity.h"
308c2ecf20Sopenharmony_ci#include "vsp1_hgo.h"
318c2ecf20Sopenharmony_ci#include "vsp1_hgt.h"
328c2ecf20Sopenharmony_ci#include "vsp1_pipe.h"
338c2ecf20Sopenharmony_ci#include "vsp1_rwpf.h"
348c2ecf20Sopenharmony_ci#include "vsp1_uds.h"
358c2ecf20Sopenharmony_ci#include "vsp1_video.h"
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define VSP1_VIDEO_DEF_FORMAT		V4L2_PIX_FMT_YUYV
388c2ecf20Sopenharmony_ci#define VSP1_VIDEO_DEF_WIDTH		1024
398c2ecf20Sopenharmony_ci#define VSP1_VIDEO_DEF_HEIGHT		768
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define VSP1_VIDEO_MAX_WIDTH		8190U
428c2ecf20Sopenharmony_ci#define VSP1_VIDEO_MAX_HEIGHT		8190U
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
458c2ecf20Sopenharmony_ci * Helper functions
468c2ecf20Sopenharmony_ci */
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic struct v4l2_subdev *
498c2ecf20Sopenharmony_civsp1_video_remote_subdev(struct media_pad *local, u32 *pad)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	struct media_pad *remote;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	remote = media_entity_remote_pad(local);
548c2ecf20Sopenharmony_ci	if (!remote || !is_media_entity_v4l2_subdev(remote->entity))
558c2ecf20Sopenharmony_ci		return NULL;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	if (pad)
588c2ecf20Sopenharmony_ci		*pad = remote->index;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	return media_entity_to_v4l2_subdev(remote->entity);
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic int vsp1_video_verify_format(struct vsp1_video *video)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct v4l2_subdev_format fmt;
668c2ecf20Sopenharmony_ci	struct v4l2_subdev *subdev;
678c2ecf20Sopenharmony_ci	int ret;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	subdev = vsp1_video_remote_subdev(&video->pad, &fmt.pad);
708c2ecf20Sopenharmony_ci	if (subdev == NULL)
718c2ecf20Sopenharmony_ci		return -EINVAL;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
748c2ecf20Sopenharmony_ci	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
758c2ecf20Sopenharmony_ci	if (ret < 0)
768c2ecf20Sopenharmony_ci		return ret == -ENOIOCTLCMD ? -EINVAL : ret;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (video->rwpf->fmtinfo->mbus != fmt.format.code ||
798c2ecf20Sopenharmony_ci	    video->rwpf->format.height != fmt.format.height ||
808c2ecf20Sopenharmony_ci	    video->rwpf->format.width != fmt.format.width)
818c2ecf20Sopenharmony_ci		return -EINVAL;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	return 0;
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic int __vsp1_video_try_format(struct vsp1_video *video,
878c2ecf20Sopenharmony_ci				   struct v4l2_pix_format_mplane *pix,
888c2ecf20Sopenharmony_ci				   const struct vsp1_format_info **fmtinfo)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	static const u32 xrgb_formats[][2] = {
918c2ecf20Sopenharmony_ci		{ V4L2_PIX_FMT_RGB444, V4L2_PIX_FMT_XRGB444 },
928c2ecf20Sopenharmony_ci		{ V4L2_PIX_FMT_RGB555, V4L2_PIX_FMT_XRGB555 },
938c2ecf20Sopenharmony_ci		{ V4L2_PIX_FMT_BGR32, V4L2_PIX_FMT_XBGR32 },
948c2ecf20Sopenharmony_ci		{ V4L2_PIX_FMT_RGB32, V4L2_PIX_FMT_XRGB32 },
958c2ecf20Sopenharmony_ci	};
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	const struct vsp1_format_info *info;
988c2ecf20Sopenharmony_ci	unsigned int width = pix->width;
998c2ecf20Sopenharmony_ci	unsigned int height = pix->height;
1008c2ecf20Sopenharmony_ci	unsigned int i;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/*
1038c2ecf20Sopenharmony_ci	 * Backward compatibility: replace deprecated RGB formats by their XRGB
1048c2ecf20Sopenharmony_ci	 * equivalent. This selects the format older userspace applications want
1058c2ecf20Sopenharmony_ci	 * while still exposing the new format.
1068c2ecf20Sopenharmony_ci	 */
1078c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(xrgb_formats); ++i) {
1088c2ecf20Sopenharmony_ci		if (xrgb_formats[i][0] == pix->pixelformat) {
1098c2ecf20Sopenharmony_ci			pix->pixelformat = xrgb_formats[i][1];
1108c2ecf20Sopenharmony_ci			break;
1118c2ecf20Sopenharmony_ci		}
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	/*
1158c2ecf20Sopenharmony_ci	 * Retrieve format information and select the default format if the
1168c2ecf20Sopenharmony_ci	 * requested format isn't supported.
1178c2ecf20Sopenharmony_ci	 */
1188c2ecf20Sopenharmony_ci	info = vsp1_get_format_info(video->vsp1, pix->pixelformat);
1198c2ecf20Sopenharmony_ci	if (info == NULL)
1208c2ecf20Sopenharmony_ci		info = vsp1_get_format_info(video->vsp1, VSP1_VIDEO_DEF_FORMAT);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	pix->pixelformat = info->fourcc;
1238c2ecf20Sopenharmony_ci	pix->colorspace = V4L2_COLORSPACE_SRGB;
1248c2ecf20Sopenharmony_ci	pix->field = V4L2_FIELD_NONE;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	if (info->fourcc == V4L2_PIX_FMT_HSV24 ||
1278c2ecf20Sopenharmony_ci	    info->fourcc == V4L2_PIX_FMT_HSV32)
1288c2ecf20Sopenharmony_ci		pix->hsv_enc = V4L2_HSV_ENC_256;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	memset(pix->reserved, 0, sizeof(pix->reserved));
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/* Align the width and height for YUV 4:2:2 and 4:2:0 formats. */
1338c2ecf20Sopenharmony_ci	width = round_down(width, info->hsub);
1348c2ecf20Sopenharmony_ci	height = round_down(height, info->vsub);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/* Clamp the width and height. */
1378c2ecf20Sopenharmony_ci	pix->width = clamp(width, info->hsub, VSP1_VIDEO_MAX_WIDTH);
1388c2ecf20Sopenharmony_ci	pix->height = clamp(height, info->vsub, VSP1_VIDEO_MAX_HEIGHT);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/*
1418c2ecf20Sopenharmony_ci	 * Compute and clamp the stride and image size. While not documented in
1428c2ecf20Sopenharmony_ci	 * the datasheet, strides not aligned to a multiple of 128 bytes result
1438c2ecf20Sopenharmony_ci	 * in image corruption.
1448c2ecf20Sopenharmony_ci	 */
1458c2ecf20Sopenharmony_ci	for (i = 0; i < min(info->planes, 2U); ++i) {
1468c2ecf20Sopenharmony_ci		unsigned int hsub = i > 0 ? info->hsub : 1;
1478c2ecf20Sopenharmony_ci		unsigned int vsub = i > 0 ? info->vsub : 1;
1488c2ecf20Sopenharmony_ci		unsigned int align = 128;
1498c2ecf20Sopenharmony_ci		unsigned int bpl;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci		bpl = clamp_t(unsigned int, pix->plane_fmt[i].bytesperline,
1528c2ecf20Sopenharmony_ci			      pix->width / hsub * info->bpp[i] / 8,
1538c2ecf20Sopenharmony_ci			      round_down(65535U, align));
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci		pix->plane_fmt[i].bytesperline = round_up(bpl, align);
1568c2ecf20Sopenharmony_ci		pix->plane_fmt[i].sizeimage = pix->plane_fmt[i].bytesperline
1578c2ecf20Sopenharmony_ci					    * pix->height / vsub;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	if (info->planes == 3) {
1618c2ecf20Sopenharmony_ci		/* The second and third planes must have the same stride. */
1628c2ecf20Sopenharmony_ci		pix->plane_fmt[2].bytesperline = pix->plane_fmt[1].bytesperline;
1638c2ecf20Sopenharmony_ci		pix->plane_fmt[2].sizeimage = pix->plane_fmt[1].sizeimage;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	pix->num_planes = info->planes;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (fmtinfo)
1698c2ecf20Sopenharmony_ci		*fmtinfo = info;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	return 0;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
1758c2ecf20Sopenharmony_ci * VSP1 Partition Algorithm support
1768c2ecf20Sopenharmony_ci */
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/**
1798c2ecf20Sopenharmony_ci * vsp1_video_calculate_partition - Calculate the active partition output window
1808c2ecf20Sopenharmony_ci *
1818c2ecf20Sopenharmony_ci * @pipe: the pipeline
1828c2ecf20Sopenharmony_ci * @partition: partition that will hold the calculated values
1838c2ecf20Sopenharmony_ci * @div_size: pre-determined maximum partition division size
1848c2ecf20Sopenharmony_ci * @index: partition index
1858c2ecf20Sopenharmony_ci */
1868c2ecf20Sopenharmony_cistatic void vsp1_video_calculate_partition(struct vsp1_pipeline *pipe,
1878c2ecf20Sopenharmony_ci					   struct vsp1_partition *partition,
1888c2ecf20Sopenharmony_ci					   unsigned int div_size,
1898c2ecf20Sopenharmony_ci					   unsigned int index)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	const struct v4l2_mbus_framefmt *format;
1928c2ecf20Sopenharmony_ci	struct vsp1_partition_window window;
1938c2ecf20Sopenharmony_ci	unsigned int modulus;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	/*
1968c2ecf20Sopenharmony_ci	 * Partitions are computed on the size before rotation, use the format
1978c2ecf20Sopenharmony_ci	 * at the WPF sink.
1988c2ecf20Sopenharmony_ci	 */
1998c2ecf20Sopenharmony_ci	format = vsp1_entity_get_pad_format(&pipe->output->entity,
2008c2ecf20Sopenharmony_ci					    pipe->output->entity.config,
2018c2ecf20Sopenharmony_ci					    RWPF_PAD_SINK);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	/* A single partition simply processes the output size in full. */
2048c2ecf20Sopenharmony_ci	if (pipe->partitions <= 1) {
2058c2ecf20Sopenharmony_ci		window.left = 0;
2068c2ecf20Sopenharmony_ci		window.width = format->width;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci		vsp1_pipeline_propagate_partition(pipe, partition, index,
2098c2ecf20Sopenharmony_ci						  &window);
2108c2ecf20Sopenharmony_ci		return;
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	/* Initialise the partition with sane starting conditions. */
2148c2ecf20Sopenharmony_ci	window.left = index * div_size;
2158c2ecf20Sopenharmony_ci	window.width = div_size;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	modulus = format->width % div_size;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	/*
2208c2ecf20Sopenharmony_ci	 * We need to prevent the last partition from being smaller than the
2218c2ecf20Sopenharmony_ci	 * *minimum* width of the hardware capabilities.
2228c2ecf20Sopenharmony_ci	 *
2238c2ecf20Sopenharmony_ci	 * If the modulus is less than half of the partition size,
2248c2ecf20Sopenharmony_ci	 * the penultimate partition is reduced to half, which is added
2258c2ecf20Sopenharmony_ci	 * to the final partition: |1234|1234|1234|12|341|
2268c2ecf20Sopenharmony_ci	 * to prevent this:        |1234|1234|1234|1234|1|.
2278c2ecf20Sopenharmony_ci	 */
2288c2ecf20Sopenharmony_ci	if (modulus) {
2298c2ecf20Sopenharmony_ci		/*
2308c2ecf20Sopenharmony_ci		 * pipe->partitions is 1 based, whilst index is a 0 based index.
2318c2ecf20Sopenharmony_ci		 * Normalise this locally.
2328c2ecf20Sopenharmony_ci		 */
2338c2ecf20Sopenharmony_ci		unsigned int partitions = pipe->partitions - 1;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		if (modulus < div_size / 2) {
2368c2ecf20Sopenharmony_ci			if (index == partitions - 1) {
2378c2ecf20Sopenharmony_ci				/* Halve the penultimate partition. */
2388c2ecf20Sopenharmony_ci				window.width = div_size / 2;
2398c2ecf20Sopenharmony_ci			} else if (index == partitions) {
2408c2ecf20Sopenharmony_ci				/* Increase the final partition. */
2418c2ecf20Sopenharmony_ci				window.width = (div_size / 2) + modulus;
2428c2ecf20Sopenharmony_ci				window.left -= div_size / 2;
2438c2ecf20Sopenharmony_ci			}
2448c2ecf20Sopenharmony_ci		} else if (index == partitions) {
2458c2ecf20Sopenharmony_ci			window.width = modulus;
2468c2ecf20Sopenharmony_ci		}
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	vsp1_pipeline_propagate_partition(pipe, partition, index, &window);
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
2558c2ecf20Sopenharmony_ci	const struct v4l2_mbus_framefmt *format;
2568c2ecf20Sopenharmony_ci	struct vsp1_entity *entity;
2578c2ecf20Sopenharmony_ci	unsigned int div_size;
2588c2ecf20Sopenharmony_ci	unsigned int i;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	/*
2618c2ecf20Sopenharmony_ci	 * Partitions are computed on the size before rotation, use the format
2628c2ecf20Sopenharmony_ci	 * at the WPF sink.
2638c2ecf20Sopenharmony_ci	 */
2648c2ecf20Sopenharmony_ci	format = vsp1_entity_get_pad_format(&pipe->output->entity,
2658c2ecf20Sopenharmony_ci					    pipe->output->entity.config,
2668c2ecf20Sopenharmony_ci					    RWPF_PAD_SINK);
2678c2ecf20Sopenharmony_ci	div_size = format->width;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	/*
2708c2ecf20Sopenharmony_ci	 * Only Gen3 hardware requires image partitioning, Gen2 will operate
2718c2ecf20Sopenharmony_ci	 * with a single partition that covers the whole output.
2728c2ecf20Sopenharmony_ci	 */
2738c2ecf20Sopenharmony_ci	if (vsp1->info->gen == 3) {
2748c2ecf20Sopenharmony_ci		list_for_each_entry(entity, &pipe->entities, list_pipe) {
2758c2ecf20Sopenharmony_ci			unsigned int entity_max;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci			if (!entity->ops->max_width)
2788c2ecf20Sopenharmony_ci				continue;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci			entity_max = entity->ops->max_width(entity, pipe);
2818c2ecf20Sopenharmony_ci			if (entity_max)
2828c2ecf20Sopenharmony_ci				div_size = min(div_size, entity_max);
2838c2ecf20Sopenharmony_ci		}
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	pipe->partitions = DIV_ROUND_UP(format->width, div_size);
2878c2ecf20Sopenharmony_ci	pipe->part_table = kcalloc(pipe->partitions, sizeof(*pipe->part_table),
2888c2ecf20Sopenharmony_ci				   GFP_KERNEL);
2898c2ecf20Sopenharmony_ci	if (!pipe->part_table)
2908c2ecf20Sopenharmony_ci		return -ENOMEM;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	for (i = 0; i < pipe->partitions; ++i)
2938c2ecf20Sopenharmony_ci		vsp1_video_calculate_partition(pipe, &pipe->part_table[i],
2948c2ecf20Sopenharmony_ci					       div_size, i);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	return 0;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
3008c2ecf20Sopenharmony_ci * Pipeline Management
3018c2ecf20Sopenharmony_ci */
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci/*
3048c2ecf20Sopenharmony_ci * vsp1_video_complete_buffer - Complete the current buffer
3058c2ecf20Sopenharmony_ci * @video: the video node
3068c2ecf20Sopenharmony_ci *
3078c2ecf20Sopenharmony_ci * This function completes the current buffer by filling its sequence number,
3088c2ecf20Sopenharmony_ci * time stamp and payload size, and hands it back to the videobuf core.
3098c2ecf20Sopenharmony_ci *
3108c2ecf20Sopenharmony_ci * Return the next queued buffer or NULL if the queue is empty.
3118c2ecf20Sopenharmony_ci */
3128c2ecf20Sopenharmony_cistatic struct vsp1_vb2_buffer *
3138c2ecf20Sopenharmony_civsp1_video_complete_buffer(struct vsp1_video *video)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct vsp1_pipeline *pipe = video->rwpf->entity.pipe;
3168c2ecf20Sopenharmony_ci	struct vsp1_vb2_buffer *next = NULL;
3178c2ecf20Sopenharmony_ci	struct vsp1_vb2_buffer *done;
3188c2ecf20Sopenharmony_ci	unsigned long flags;
3198c2ecf20Sopenharmony_ci	unsigned int i;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	spin_lock_irqsave(&video->irqlock, flags);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	if (list_empty(&video->irqqueue)) {
3248c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&video->irqlock, flags);
3258c2ecf20Sopenharmony_ci		return NULL;
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	done = list_first_entry(&video->irqqueue,
3298c2ecf20Sopenharmony_ci				struct vsp1_vb2_buffer, queue);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	list_del(&done->queue);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	if (!list_empty(&video->irqqueue))
3348c2ecf20Sopenharmony_ci		next = list_first_entry(&video->irqqueue,
3358c2ecf20Sopenharmony_ci					struct vsp1_vb2_buffer, queue);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&video->irqlock, flags);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	done->buf.sequence = pipe->sequence;
3408c2ecf20Sopenharmony_ci	done->buf.vb2_buf.timestamp = ktime_get_ns();
3418c2ecf20Sopenharmony_ci	for (i = 0; i < done->buf.vb2_buf.num_planes; ++i)
3428c2ecf20Sopenharmony_ci		vb2_set_plane_payload(&done->buf.vb2_buf, i,
3438c2ecf20Sopenharmony_ci				      vb2_plane_size(&done->buf.vb2_buf, i));
3448c2ecf20Sopenharmony_ci	vb2_buffer_done(&done->buf.vb2_buf, VB2_BUF_STATE_DONE);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	return next;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic void vsp1_video_frame_end(struct vsp1_pipeline *pipe,
3508c2ecf20Sopenharmony_ci				 struct vsp1_rwpf *rwpf)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	struct vsp1_video *video = rwpf->video;
3538c2ecf20Sopenharmony_ci	struct vsp1_vb2_buffer *buf;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	buf = vsp1_video_complete_buffer(video);
3568c2ecf20Sopenharmony_ci	if (buf == NULL)
3578c2ecf20Sopenharmony_ci		return;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	video->rwpf->mem = buf->mem;
3608c2ecf20Sopenharmony_ci	pipe->buffers_ready |= 1 << video->pipe_index;
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic void vsp1_video_pipeline_run_partition(struct vsp1_pipeline *pipe,
3648c2ecf20Sopenharmony_ci					      struct vsp1_dl_list *dl,
3658c2ecf20Sopenharmony_ci					      unsigned int partition)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	struct vsp1_dl_body *dlb = vsp1_dl_list_get_body0(dl);
3688c2ecf20Sopenharmony_ci	struct vsp1_entity *entity;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	pipe->partition = &pipe->part_table[partition];
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	list_for_each_entry(entity, &pipe->entities, list_pipe)
3738c2ecf20Sopenharmony_ci		vsp1_entity_configure_partition(entity, pipe, dl, dlb);
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cistatic void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
3798c2ecf20Sopenharmony_ci	struct vsp1_entity *entity;
3808c2ecf20Sopenharmony_ci	struct vsp1_dl_body *dlb;
3818c2ecf20Sopenharmony_ci	struct vsp1_dl_list *dl;
3828c2ecf20Sopenharmony_ci	unsigned int partition;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	dl = vsp1_dl_list_get(pipe->output->dlm);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	/*
3878c2ecf20Sopenharmony_ci	 * If the VSP hardware isn't configured yet (which occurs either when
3888c2ecf20Sopenharmony_ci	 * processing the first frame or after a system suspend/resume), add the
3898c2ecf20Sopenharmony_ci	 * cached stream configuration to the display list to perform a full
3908c2ecf20Sopenharmony_ci	 * initialisation.
3918c2ecf20Sopenharmony_ci	 */
3928c2ecf20Sopenharmony_ci	if (!pipe->configured)
3938c2ecf20Sopenharmony_ci		vsp1_dl_list_add_body(dl, pipe->stream_config);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	dlb = vsp1_dl_list_get_body0(dl);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	list_for_each_entry(entity, &pipe->entities, list_pipe)
3988c2ecf20Sopenharmony_ci		vsp1_entity_configure_frame(entity, pipe, dl, dlb);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	/* Run the first partition. */
4018c2ecf20Sopenharmony_ci	vsp1_video_pipeline_run_partition(pipe, dl, 0);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	/* Process consecutive partitions as necessary. */
4048c2ecf20Sopenharmony_ci	for (partition = 1; partition < pipe->partitions; ++partition) {
4058c2ecf20Sopenharmony_ci		struct vsp1_dl_list *dl_next;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci		dl_next = vsp1_dl_list_get(pipe->output->dlm);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci		/*
4108c2ecf20Sopenharmony_ci		 * An incomplete chain will still function, but output only
4118c2ecf20Sopenharmony_ci		 * the partitions that had a dl available. The frame end
4128c2ecf20Sopenharmony_ci		 * interrupt will be marked on the last dl in the chain.
4138c2ecf20Sopenharmony_ci		 */
4148c2ecf20Sopenharmony_ci		if (!dl_next) {
4158c2ecf20Sopenharmony_ci			dev_err(vsp1->dev, "Failed to obtain a dl list. Frame will be incomplete\n");
4168c2ecf20Sopenharmony_ci			break;
4178c2ecf20Sopenharmony_ci		}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci		vsp1_video_pipeline_run_partition(pipe, dl_next, partition);
4208c2ecf20Sopenharmony_ci		vsp1_dl_list_add_chain(dl, dl_next);
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	/* Complete, and commit the head display list. */
4248c2ecf20Sopenharmony_ci	vsp1_dl_list_commit(dl, 0);
4258c2ecf20Sopenharmony_ci	pipe->configured = true;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	vsp1_pipeline_run(pipe);
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe,
4318c2ecf20Sopenharmony_ci					  unsigned int completion)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
4348c2ecf20Sopenharmony_ci	enum vsp1_pipeline_state state;
4358c2ecf20Sopenharmony_ci	unsigned long flags;
4368c2ecf20Sopenharmony_ci	unsigned int i;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	/* M2M Pipelines should never call here with an incomplete frame. */
4398c2ecf20Sopenharmony_ci	WARN_ON_ONCE(!(completion & VSP1_DL_FRAME_END_COMPLETED));
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pipe->irqlock, flags);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	/* Complete buffers on all video nodes. */
4448c2ecf20Sopenharmony_ci	for (i = 0; i < vsp1->info->rpf_count; ++i) {
4458c2ecf20Sopenharmony_ci		if (!pipe->inputs[i])
4468c2ecf20Sopenharmony_ci			continue;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci		vsp1_video_frame_end(pipe, pipe->inputs[i]);
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	vsp1_video_frame_end(pipe, pipe->output);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	state = pipe->state;
4548c2ecf20Sopenharmony_ci	pipe->state = VSP1_PIPELINE_STOPPED;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	/*
4578c2ecf20Sopenharmony_ci	 * If a stop has been requested, mark the pipeline as stopped and
4588c2ecf20Sopenharmony_ci	 * return. Otherwise restart the pipeline if ready.
4598c2ecf20Sopenharmony_ci	 */
4608c2ecf20Sopenharmony_ci	if (state == VSP1_PIPELINE_STOPPING)
4618c2ecf20Sopenharmony_ci		wake_up(&pipe->wq);
4628c2ecf20Sopenharmony_ci	else if (vsp1_pipeline_ready(pipe))
4638c2ecf20Sopenharmony_ci		vsp1_video_pipeline_run(pipe);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pipe->irqlock, flags);
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe,
4698c2ecf20Sopenharmony_ci					    struct vsp1_rwpf *input,
4708c2ecf20Sopenharmony_ci					    struct vsp1_rwpf *output)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci	struct media_entity_enum ent_enum;
4738c2ecf20Sopenharmony_ci	struct vsp1_entity *entity;
4748c2ecf20Sopenharmony_ci	struct media_pad *pad;
4758c2ecf20Sopenharmony_ci	struct vsp1_brx *brx = NULL;
4768c2ecf20Sopenharmony_ci	int ret;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	ret = media_entity_enum_init(&ent_enum, &input->entity.vsp1->media_dev);
4798c2ecf20Sopenharmony_ci	if (ret < 0)
4808c2ecf20Sopenharmony_ci		return ret;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	/*
4838c2ecf20Sopenharmony_ci	 * The main data path doesn't include the HGO or HGT, use
4848c2ecf20Sopenharmony_ci	 * vsp1_entity_remote_pad() to traverse the graph.
4858c2ecf20Sopenharmony_ci	 */
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	pad = vsp1_entity_remote_pad(&input->entity.pads[RWPF_PAD_SOURCE]);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	while (1) {
4908c2ecf20Sopenharmony_ci		if (pad == NULL) {
4918c2ecf20Sopenharmony_ci			ret = -EPIPE;
4928c2ecf20Sopenharmony_ci			goto out;
4938c2ecf20Sopenharmony_ci		}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci		/* We've reached a video node, that shouldn't have happened. */
4968c2ecf20Sopenharmony_ci		if (!is_media_entity_v4l2_subdev(pad->entity)) {
4978c2ecf20Sopenharmony_ci			ret = -EPIPE;
4988c2ecf20Sopenharmony_ci			goto out;
4998c2ecf20Sopenharmony_ci		}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci		entity = to_vsp1_entity(
5028c2ecf20Sopenharmony_ci			media_entity_to_v4l2_subdev(pad->entity));
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci		/*
5058c2ecf20Sopenharmony_ci		 * A BRU or BRS is present in the pipeline, store its input pad
5068c2ecf20Sopenharmony_ci		 * number in the input RPF for use when configuring the RPF.
5078c2ecf20Sopenharmony_ci		 */
5088c2ecf20Sopenharmony_ci		if (entity->type == VSP1_ENTITY_BRU ||
5098c2ecf20Sopenharmony_ci		    entity->type == VSP1_ENTITY_BRS) {
5108c2ecf20Sopenharmony_ci			/* BRU and BRS can't be chained. */
5118c2ecf20Sopenharmony_ci			if (brx) {
5128c2ecf20Sopenharmony_ci				ret = -EPIPE;
5138c2ecf20Sopenharmony_ci				goto out;
5148c2ecf20Sopenharmony_ci			}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci			brx = to_brx(&entity->subdev);
5178c2ecf20Sopenharmony_ci			brx->inputs[pad->index].rpf = input;
5188c2ecf20Sopenharmony_ci			input->brx_input = pad->index;
5198c2ecf20Sopenharmony_ci		}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci		/* We've reached the WPF, we're done. */
5228c2ecf20Sopenharmony_ci		if (entity->type == VSP1_ENTITY_WPF)
5238c2ecf20Sopenharmony_ci			break;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci		/* Ensure the branch has no loop. */
5268c2ecf20Sopenharmony_ci		if (media_entity_enum_test_and_set(&ent_enum,
5278c2ecf20Sopenharmony_ci						   &entity->subdev.entity)) {
5288c2ecf20Sopenharmony_ci			ret = -EPIPE;
5298c2ecf20Sopenharmony_ci			goto out;
5308c2ecf20Sopenharmony_ci		}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci		/* UDS can't be chained. */
5338c2ecf20Sopenharmony_ci		if (entity->type == VSP1_ENTITY_UDS) {
5348c2ecf20Sopenharmony_ci			if (pipe->uds) {
5358c2ecf20Sopenharmony_ci				ret = -EPIPE;
5368c2ecf20Sopenharmony_ci				goto out;
5378c2ecf20Sopenharmony_ci			}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci			pipe->uds = entity;
5408c2ecf20Sopenharmony_ci			pipe->uds_input = brx ? &brx->entity : &input->entity;
5418c2ecf20Sopenharmony_ci		}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci		/* Follow the source link, ignoring any HGO or HGT. */
5448c2ecf20Sopenharmony_ci		pad = &entity->pads[entity->source_pad];
5458c2ecf20Sopenharmony_ci		pad = vsp1_entity_remote_pad(pad);
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	/* The last entity must be the output WPF. */
5498c2ecf20Sopenharmony_ci	if (entity != &output->entity)
5508c2ecf20Sopenharmony_ci		ret = -EPIPE;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ciout:
5538c2ecf20Sopenharmony_ci	media_entity_enum_cleanup(&ent_enum);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	return ret;
5568c2ecf20Sopenharmony_ci}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_cistatic int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe,
5598c2ecf20Sopenharmony_ci				     struct vsp1_video *video)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	struct media_graph graph;
5628c2ecf20Sopenharmony_ci	struct media_entity *entity = &video->video.entity;
5638c2ecf20Sopenharmony_ci	struct media_device *mdev = entity->graph_obj.mdev;
5648c2ecf20Sopenharmony_ci	unsigned int i;
5658c2ecf20Sopenharmony_ci	int ret;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	/* Walk the graph to locate the entities and video nodes. */
5688c2ecf20Sopenharmony_ci	ret = media_graph_walk_init(&graph, mdev);
5698c2ecf20Sopenharmony_ci	if (ret)
5708c2ecf20Sopenharmony_ci		return ret;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	media_graph_walk_start(&graph, entity);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	while ((entity = media_graph_walk_next(&graph))) {
5758c2ecf20Sopenharmony_ci		struct v4l2_subdev *subdev;
5768c2ecf20Sopenharmony_ci		struct vsp1_rwpf *rwpf;
5778c2ecf20Sopenharmony_ci		struct vsp1_entity *e;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci		if (!is_media_entity_v4l2_subdev(entity))
5808c2ecf20Sopenharmony_ci			continue;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci		subdev = media_entity_to_v4l2_subdev(entity);
5838c2ecf20Sopenharmony_ci		e = to_vsp1_entity(subdev);
5848c2ecf20Sopenharmony_ci		list_add_tail(&e->list_pipe, &pipe->entities);
5858c2ecf20Sopenharmony_ci		e->pipe = pipe;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci		switch (e->type) {
5888c2ecf20Sopenharmony_ci		case VSP1_ENTITY_RPF:
5898c2ecf20Sopenharmony_ci			rwpf = to_rwpf(subdev);
5908c2ecf20Sopenharmony_ci			pipe->inputs[rwpf->entity.index] = rwpf;
5918c2ecf20Sopenharmony_ci			rwpf->video->pipe_index = ++pipe->num_inputs;
5928c2ecf20Sopenharmony_ci			break;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci		case VSP1_ENTITY_WPF:
5958c2ecf20Sopenharmony_ci			rwpf = to_rwpf(subdev);
5968c2ecf20Sopenharmony_ci			pipe->output = rwpf;
5978c2ecf20Sopenharmony_ci			rwpf->video->pipe_index = 0;
5988c2ecf20Sopenharmony_ci			break;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci		case VSP1_ENTITY_LIF:
6018c2ecf20Sopenharmony_ci			pipe->lif = e;
6028c2ecf20Sopenharmony_ci			break;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci		case VSP1_ENTITY_BRU:
6058c2ecf20Sopenharmony_ci		case VSP1_ENTITY_BRS:
6068c2ecf20Sopenharmony_ci			pipe->brx = e;
6078c2ecf20Sopenharmony_ci			break;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci		case VSP1_ENTITY_HGO:
6108c2ecf20Sopenharmony_ci			pipe->hgo = e;
6118c2ecf20Sopenharmony_ci			break;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci		case VSP1_ENTITY_HGT:
6148c2ecf20Sopenharmony_ci			pipe->hgt = e;
6158c2ecf20Sopenharmony_ci			break;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci		default:
6188c2ecf20Sopenharmony_ci			break;
6198c2ecf20Sopenharmony_ci		}
6208c2ecf20Sopenharmony_ci	}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	media_graph_walk_cleanup(&graph);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	/* We need one output and at least one input. */
6258c2ecf20Sopenharmony_ci	if (pipe->num_inputs == 0 || !pipe->output)
6268c2ecf20Sopenharmony_ci		return -EPIPE;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	/*
6298c2ecf20Sopenharmony_ci	 * Follow links downstream for each input and make sure the graph
6308c2ecf20Sopenharmony_ci	 * contains no loop and that all branches end at the output WPF.
6318c2ecf20Sopenharmony_ci	 */
6328c2ecf20Sopenharmony_ci	for (i = 0; i < video->vsp1->info->rpf_count; ++i) {
6338c2ecf20Sopenharmony_ci		if (!pipe->inputs[i])
6348c2ecf20Sopenharmony_ci			continue;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci		ret = vsp1_video_pipeline_build_branch(pipe, pipe->inputs[i],
6378c2ecf20Sopenharmony_ci						       pipe->output);
6388c2ecf20Sopenharmony_ci		if (ret < 0)
6398c2ecf20Sopenharmony_ci			return ret;
6408c2ecf20Sopenharmony_ci	}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	return 0;
6438c2ecf20Sopenharmony_ci}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_cistatic int vsp1_video_pipeline_init(struct vsp1_pipeline *pipe,
6468c2ecf20Sopenharmony_ci				    struct vsp1_video *video)
6478c2ecf20Sopenharmony_ci{
6488c2ecf20Sopenharmony_ci	vsp1_pipeline_init(pipe);
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	pipe->frame_end = vsp1_video_pipeline_frame_end;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	return vsp1_video_pipeline_build(pipe, video);
6538c2ecf20Sopenharmony_ci}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_cistatic struct vsp1_pipeline *vsp1_video_pipeline_get(struct vsp1_video *video)
6568c2ecf20Sopenharmony_ci{
6578c2ecf20Sopenharmony_ci	struct vsp1_pipeline *pipe;
6588c2ecf20Sopenharmony_ci	int ret;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	/*
6618c2ecf20Sopenharmony_ci	 * Get a pipeline object for the video node. If a pipeline has already
6628c2ecf20Sopenharmony_ci	 * been allocated just increment its reference count and return it.
6638c2ecf20Sopenharmony_ci	 * Otherwise allocate a new pipeline and initialize it, it will be freed
6648c2ecf20Sopenharmony_ci	 * when the last reference is released.
6658c2ecf20Sopenharmony_ci	 */
6668c2ecf20Sopenharmony_ci	if (!video->rwpf->entity.pipe) {
6678c2ecf20Sopenharmony_ci		pipe = kzalloc(sizeof(*pipe), GFP_KERNEL);
6688c2ecf20Sopenharmony_ci		if (!pipe)
6698c2ecf20Sopenharmony_ci			return ERR_PTR(-ENOMEM);
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci		ret = vsp1_video_pipeline_init(pipe, video);
6728c2ecf20Sopenharmony_ci		if (ret < 0) {
6738c2ecf20Sopenharmony_ci			vsp1_pipeline_reset(pipe);
6748c2ecf20Sopenharmony_ci			kfree(pipe);
6758c2ecf20Sopenharmony_ci			return ERR_PTR(ret);
6768c2ecf20Sopenharmony_ci		}
6778c2ecf20Sopenharmony_ci	} else {
6788c2ecf20Sopenharmony_ci		pipe = video->rwpf->entity.pipe;
6798c2ecf20Sopenharmony_ci		kref_get(&pipe->kref);
6808c2ecf20Sopenharmony_ci	}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	return pipe;
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_cistatic void vsp1_video_pipeline_release(struct kref *kref)
6868c2ecf20Sopenharmony_ci{
6878c2ecf20Sopenharmony_ci	struct vsp1_pipeline *pipe = container_of(kref, typeof(*pipe), kref);
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	vsp1_pipeline_reset(pipe);
6908c2ecf20Sopenharmony_ci	kfree(pipe);
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_cistatic void vsp1_video_pipeline_put(struct vsp1_pipeline *pipe)
6948c2ecf20Sopenharmony_ci{
6958c2ecf20Sopenharmony_ci	struct media_device *mdev = &pipe->output->entity.vsp1->media_dev;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	mutex_lock(&mdev->graph_mutex);
6988c2ecf20Sopenharmony_ci	kref_put(&pipe->kref, vsp1_video_pipeline_release);
6998c2ecf20Sopenharmony_ci	mutex_unlock(&mdev->graph_mutex);
7008c2ecf20Sopenharmony_ci}
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
7038c2ecf20Sopenharmony_ci * videobuf2 Queue Operations
7048c2ecf20Sopenharmony_ci */
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_cistatic int
7078c2ecf20Sopenharmony_civsp1_video_queue_setup(struct vb2_queue *vq,
7088c2ecf20Sopenharmony_ci		       unsigned int *nbuffers, unsigned int *nplanes,
7098c2ecf20Sopenharmony_ci		       unsigned int sizes[], struct device *alloc_devs[])
7108c2ecf20Sopenharmony_ci{
7118c2ecf20Sopenharmony_ci	struct vsp1_video *video = vb2_get_drv_priv(vq);
7128c2ecf20Sopenharmony_ci	const struct v4l2_pix_format_mplane *format = &video->rwpf->format;
7138c2ecf20Sopenharmony_ci	unsigned int i;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	if (*nplanes) {
7168c2ecf20Sopenharmony_ci		if (*nplanes != format->num_planes)
7178c2ecf20Sopenharmony_ci			return -EINVAL;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci		for (i = 0; i < *nplanes; i++)
7208c2ecf20Sopenharmony_ci			if (sizes[i] < format->plane_fmt[i].sizeimage)
7218c2ecf20Sopenharmony_ci				return -EINVAL;
7228c2ecf20Sopenharmony_ci		return 0;
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	*nplanes = format->num_planes;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	for (i = 0; i < format->num_planes; ++i)
7288c2ecf20Sopenharmony_ci		sizes[i] = format->plane_fmt[i].sizeimage;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	return 0;
7318c2ecf20Sopenharmony_ci}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_cistatic int vsp1_video_buffer_prepare(struct vb2_buffer *vb)
7348c2ecf20Sopenharmony_ci{
7358c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
7368c2ecf20Sopenharmony_ci	struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue);
7378c2ecf20Sopenharmony_ci	struct vsp1_vb2_buffer *buf = to_vsp1_vb2_buffer(vbuf);
7388c2ecf20Sopenharmony_ci	const struct v4l2_pix_format_mplane *format = &video->rwpf->format;
7398c2ecf20Sopenharmony_ci	unsigned int i;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	if (vb->num_planes < format->num_planes)
7428c2ecf20Sopenharmony_ci		return -EINVAL;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	for (i = 0; i < vb->num_planes; ++i) {
7458c2ecf20Sopenharmony_ci		buf->mem.addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci		if (vb2_plane_size(vb, i) < format->plane_fmt[i].sizeimage)
7488c2ecf20Sopenharmony_ci			return -EINVAL;
7498c2ecf20Sopenharmony_ci	}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	for ( ; i < 3; ++i)
7528c2ecf20Sopenharmony_ci		buf->mem.addr[i] = 0;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	return 0;
7558c2ecf20Sopenharmony_ci}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_cistatic void vsp1_video_buffer_queue(struct vb2_buffer *vb)
7588c2ecf20Sopenharmony_ci{
7598c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
7608c2ecf20Sopenharmony_ci	struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue);
7618c2ecf20Sopenharmony_ci	struct vsp1_pipeline *pipe = video->rwpf->entity.pipe;
7628c2ecf20Sopenharmony_ci	struct vsp1_vb2_buffer *buf = to_vsp1_vb2_buffer(vbuf);
7638c2ecf20Sopenharmony_ci	unsigned long flags;
7648c2ecf20Sopenharmony_ci	bool empty;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	spin_lock_irqsave(&video->irqlock, flags);
7678c2ecf20Sopenharmony_ci	empty = list_empty(&video->irqqueue);
7688c2ecf20Sopenharmony_ci	list_add_tail(&buf->queue, &video->irqqueue);
7698c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&video->irqlock, flags);
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	if (!empty)
7728c2ecf20Sopenharmony_ci		return;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pipe->irqlock, flags);
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	video->rwpf->mem = buf->mem;
7778c2ecf20Sopenharmony_ci	pipe->buffers_ready |= 1 << video->pipe_index;
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	if (vb2_is_streaming(&video->queue) &&
7808c2ecf20Sopenharmony_ci	    vsp1_pipeline_ready(pipe))
7818c2ecf20Sopenharmony_ci		vsp1_video_pipeline_run(pipe);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pipe->irqlock, flags);
7848c2ecf20Sopenharmony_ci}
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_cistatic int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe)
7878c2ecf20Sopenharmony_ci{
7888c2ecf20Sopenharmony_ci	struct vsp1_entity *entity;
7898c2ecf20Sopenharmony_ci	int ret;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	/* Determine this pipelines sizes for image partitioning support. */
7928c2ecf20Sopenharmony_ci	ret = vsp1_video_pipeline_setup_partitions(pipe);
7938c2ecf20Sopenharmony_ci	if (ret < 0)
7948c2ecf20Sopenharmony_ci		return ret;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	if (pipe->uds) {
7978c2ecf20Sopenharmony_ci		struct vsp1_uds *uds = to_uds(&pipe->uds->subdev);
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci		/*
8008c2ecf20Sopenharmony_ci		 * If a BRU or BRS is present in the pipeline before the UDS,
8018c2ecf20Sopenharmony_ci		 * the alpha component doesn't need to be scaled as the BRU and
8028c2ecf20Sopenharmony_ci		 * BRS output alpha value is fixed to 255. Otherwise we need to
8038c2ecf20Sopenharmony_ci		 * scale the alpha component only when available at the input
8048c2ecf20Sopenharmony_ci		 * RPF.
8058c2ecf20Sopenharmony_ci		 */
8068c2ecf20Sopenharmony_ci		if (pipe->uds_input->type == VSP1_ENTITY_BRU ||
8078c2ecf20Sopenharmony_ci		    pipe->uds_input->type == VSP1_ENTITY_BRS) {
8088c2ecf20Sopenharmony_ci			uds->scale_alpha = false;
8098c2ecf20Sopenharmony_ci		} else {
8108c2ecf20Sopenharmony_ci			struct vsp1_rwpf *rpf =
8118c2ecf20Sopenharmony_ci				to_rwpf(&pipe->uds_input->subdev);
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci			uds->scale_alpha = rpf->fmtinfo->alpha;
8148c2ecf20Sopenharmony_ci		}
8158c2ecf20Sopenharmony_ci	}
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	/*
8188c2ecf20Sopenharmony_ci	 * Compute and cache the stream configuration into a body. The cached
8198c2ecf20Sopenharmony_ci	 * body will be added to the display list by vsp1_video_pipeline_run()
8208c2ecf20Sopenharmony_ci	 * whenever the pipeline needs to be fully reconfigured.
8218c2ecf20Sopenharmony_ci	 */
8228c2ecf20Sopenharmony_ci	pipe->stream_config = vsp1_dlm_dl_body_get(pipe->output->dlm);
8238c2ecf20Sopenharmony_ci	if (!pipe->stream_config)
8248c2ecf20Sopenharmony_ci		return -ENOMEM;
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	list_for_each_entry(entity, &pipe->entities, list_pipe) {
8278c2ecf20Sopenharmony_ci		vsp1_entity_route_setup(entity, pipe, pipe->stream_config);
8288c2ecf20Sopenharmony_ci		vsp1_entity_configure_stream(entity, pipe, NULL,
8298c2ecf20Sopenharmony_ci					     pipe->stream_config);
8308c2ecf20Sopenharmony_ci	}
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	return 0;
8338c2ecf20Sopenharmony_ci}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_cistatic void vsp1_video_release_buffers(struct vsp1_video *video)
8368c2ecf20Sopenharmony_ci{
8378c2ecf20Sopenharmony_ci	struct vsp1_vb2_buffer *buffer;
8388c2ecf20Sopenharmony_ci	unsigned long flags;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	/* Remove all buffers from the IRQ queue. */
8418c2ecf20Sopenharmony_ci	spin_lock_irqsave(&video->irqlock, flags);
8428c2ecf20Sopenharmony_ci	list_for_each_entry(buffer, &video->irqqueue, queue)
8438c2ecf20Sopenharmony_ci		vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR);
8448c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&video->irqqueue);
8458c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&video->irqlock, flags);
8468c2ecf20Sopenharmony_ci}
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_cistatic void vsp1_video_cleanup_pipeline(struct vsp1_pipeline *pipe)
8498c2ecf20Sopenharmony_ci{
8508c2ecf20Sopenharmony_ci	lockdep_assert_held(&pipe->lock);
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	/* Release any cached configuration from our output video. */
8538c2ecf20Sopenharmony_ci	vsp1_dl_body_put(pipe->stream_config);
8548c2ecf20Sopenharmony_ci	pipe->stream_config = NULL;
8558c2ecf20Sopenharmony_ci	pipe->configured = false;
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	/* Release our partition table allocation. */
8588c2ecf20Sopenharmony_ci	kfree(pipe->part_table);
8598c2ecf20Sopenharmony_ci	pipe->part_table = NULL;
8608c2ecf20Sopenharmony_ci}
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_cistatic int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count)
8638c2ecf20Sopenharmony_ci{
8648c2ecf20Sopenharmony_ci	struct vsp1_video *video = vb2_get_drv_priv(vq);
8658c2ecf20Sopenharmony_ci	struct vsp1_pipeline *pipe = video->rwpf->entity.pipe;
8668c2ecf20Sopenharmony_ci	bool start_pipeline = false;
8678c2ecf20Sopenharmony_ci	unsigned long flags;
8688c2ecf20Sopenharmony_ci	int ret;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	mutex_lock(&pipe->lock);
8718c2ecf20Sopenharmony_ci	if (pipe->stream_count == pipe->num_inputs) {
8728c2ecf20Sopenharmony_ci		ret = vsp1_video_setup_pipeline(pipe);
8738c2ecf20Sopenharmony_ci		if (ret < 0) {
8748c2ecf20Sopenharmony_ci			vsp1_video_release_buffers(video);
8758c2ecf20Sopenharmony_ci			vsp1_video_cleanup_pipeline(pipe);
8768c2ecf20Sopenharmony_ci			mutex_unlock(&pipe->lock);
8778c2ecf20Sopenharmony_ci			return ret;
8788c2ecf20Sopenharmony_ci		}
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci		start_pipeline = true;
8818c2ecf20Sopenharmony_ci	}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	pipe->stream_count++;
8848c2ecf20Sopenharmony_ci	mutex_unlock(&pipe->lock);
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	/*
8878c2ecf20Sopenharmony_ci	 * vsp1_pipeline_ready() is not sufficient to establish that all streams
8888c2ecf20Sopenharmony_ci	 * are prepared and the pipeline is configured, as multiple streams
8898c2ecf20Sopenharmony_ci	 * can race through streamon with buffers already queued; Therefore we
8908c2ecf20Sopenharmony_ci	 * don't even attempt to start the pipeline until the last stream has
8918c2ecf20Sopenharmony_ci	 * called through here.
8928c2ecf20Sopenharmony_ci	 */
8938c2ecf20Sopenharmony_ci	if (!start_pipeline)
8948c2ecf20Sopenharmony_ci		return 0;
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pipe->irqlock, flags);
8978c2ecf20Sopenharmony_ci	if (vsp1_pipeline_ready(pipe))
8988c2ecf20Sopenharmony_ci		vsp1_video_pipeline_run(pipe);
8998c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pipe->irqlock, flags);
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	return 0;
9028c2ecf20Sopenharmony_ci}
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_cistatic void vsp1_video_stop_streaming(struct vb2_queue *vq)
9058c2ecf20Sopenharmony_ci{
9068c2ecf20Sopenharmony_ci	struct vsp1_video *video = vb2_get_drv_priv(vq);
9078c2ecf20Sopenharmony_ci	struct vsp1_pipeline *pipe = video->rwpf->entity.pipe;
9088c2ecf20Sopenharmony_ci	unsigned long flags;
9098c2ecf20Sopenharmony_ci	int ret;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	/*
9128c2ecf20Sopenharmony_ci	 * Clear the buffers ready flag to make sure the device won't be started
9138c2ecf20Sopenharmony_ci	 * by a QBUF on the video node on the other side of the pipeline.
9148c2ecf20Sopenharmony_ci	 */
9158c2ecf20Sopenharmony_ci	spin_lock_irqsave(&video->irqlock, flags);
9168c2ecf20Sopenharmony_ci	pipe->buffers_ready &= ~(1 << video->pipe_index);
9178c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&video->irqlock, flags);
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	mutex_lock(&pipe->lock);
9208c2ecf20Sopenharmony_ci	if (--pipe->stream_count == pipe->num_inputs) {
9218c2ecf20Sopenharmony_ci		/* Stop the pipeline. */
9228c2ecf20Sopenharmony_ci		ret = vsp1_pipeline_stop(pipe);
9238c2ecf20Sopenharmony_ci		if (ret == -ETIMEDOUT)
9248c2ecf20Sopenharmony_ci			dev_err(video->vsp1->dev, "pipeline stop timeout\n");
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci		vsp1_video_cleanup_pipeline(pipe);
9278c2ecf20Sopenharmony_ci	}
9288c2ecf20Sopenharmony_ci	mutex_unlock(&pipe->lock);
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	media_pipeline_stop(&video->video.entity);
9318c2ecf20Sopenharmony_ci	vsp1_video_release_buffers(video);
9328c2ecf20Sopenharmony_ci	vsp1_video_pipeline_put(pipe);
9338c2ecf20Sopenharmony_ci}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_cistatic const struct vb2_ops vsp1_video_queue_qops = {
9368c2ecf20Sopenharmony_ci	.queue_setup = vsp1_video_queue_setup,
9378c2ecf20Sopenharmony_ci	.buf_prepare = vsp1_video_buffer_prepare,
9388c2ecf20Sopenharmony_ci	.buf_queue = vsp1_video_buffer_queue,
9398c2ecf20Sopenharmony_ci	.wait_prepare = vb2_ops_wait_prepare,
9408c2ecf20Sopenharmony_ci	.wait_finish = vb2_ops_wait_finish,
9418c2ecf20Sopenharmony_ci	.start_streaming = vsp1_video_start_streaming,
9428c2ecf20Sopenharmony_ci	.stop_streaming = vsp1_video_stop_streaming,
9438c2ecf20Sopenharmony_ci};
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
9468c2ecf20Sopenharmony_ci * V4L2 ioctls
9478c2ecf20Sopenharmony_ci */
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_cistatic int
9508c2ecf20Sopenharmony_civsp1_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
9518c2ecf20Sopenharmony_ci{
9528c2ecf20Sopenharmony_ci	struct v4l2_fh *vfh = file->private_data;
9538c2ecf20Sopenharmony_ci	struct vsp1_video *video = to_vsp1_video(vfh->vdev);
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
9568c2ecf20Sopenharmony_ci			  | V4L2_CAP_VIDEO_CAPTURE_MPLANE
9578c2ecf20Sopenharmony_ci			  | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	strscpy(cap->driver, "vsp1", sizeof(cap->driver));
9618c2ecf20Sopenharmony_ci	strscpy(cap->card, video->video.name, sizeof(cap->card));
9628c2ecf20Sopenharmony_ci	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
9638c2ecf20Sopenharmony_ci		 dev_name(video->vsp1->dev));
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	return 0;
9668c2ecf20Sopenharmony_ci}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_cistatic int
9698c2ecf20Sopenharmony_civsp1_video_get_format(struct file *file, void *fh, struct v4l2_format *format)
9708c2ecf20Sopenharmony_ci{
9718c2ecf20Sopenharmony_ci	struct v4l2_fh *vfh = file->private_data;
9728c2ecf20Sopenharmony_ci	struct vsp1_video *video = to_vsp1_video(vfh->vdev);
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	if (format->type != video->queue.type)
9758c2ecf20Sopenharmony_ci		return -EINVAL;
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	mutex_lock(&video->lock);
9788c2ecf20Sopenharmony_ci	format->fmt.pix_mp = video->rwpf->format;
9798c2ecf20Sopenharmony_ci	mutex_unlock(&video->lock);
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	return 0;
9828c2ecf20Sopenharmony_ci}
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_cistatic int
9858c2ecf20Sopenharmony_civsp1_video_try_format(struct file *file, void *fh, struct v4l2_format *format)
9868c2ecf20Sopenharmony_ci{
9878c2ecf20Sopenharmony_ci	struct v4l2_fh *vfh = file->private_data;
9888c2ecf20Sopenharmony_ci	struct vsp1_video *video = to_vsp1_video(vfh->vdev);
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	if (format->type != video->queue.type)
9918c2ecf20Sopenharmony_ci		return -EINVAL;
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	return __vsp1_video_try_format(video, &format->fmt.pix_mp, NULL);
9948c2ecf20Sopenharmony_ci}
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_cistatic int
9978c2ecf20Sopenharmony_civsp1_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
9988c2ecf20Sopenharmony_ci{
9998c2ecf20Sopenharmony_ci	struct v4l2_fh *vfh = file->private_data;
10008c2ecf20Sopenharmony_ci	struct vsp1_video *video = to_vsp1_video(vfh->vdev);
10018c2ecf20Sopenharmony_ci	const struct vsp1_format_info *info;
10028c2ecf20Sopenharmony_ci	int ret;
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	if (format->type != video->queue.type)
10058c2ecf20Sopenharmony_ci		return -EINVAL;
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	ret = __vsp1_video_try_format(video, &format->fmt.pix_mp, &info);
10088c2ecf20Sopenharmony_ci	if (ret < 0)
10098c2ecf20Sopenharmony_ci		return ret;
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	mutex_lock(&video->lock);
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	if (vb2_is_busy(&video->queue)) {
10148c2ecf20Sopenharmony_ci		ret = -EBUSY;
10158c2ecf20Sopenharmony_ci		goto done;
10168c2ecf20Sopenharmony_ci	}
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	video->rwpf->format = format->fmt.pix_mp;
10198c2ecf20Sopenharmony_ci	video->rwpf->fmtinfo = info;
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_cidone:
10228c2ecf20Sopenharmony_ci	mutex_unlock(&video->lock);
10238c2ecf20Sopenharmony_ci	return ret;
10248c2ecf20Sopenharmony_ci}
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_cistatic int
10278c2ecf20Sopenharmony_civsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
10288c2ecf20Sopenharmony_ci{
10298c2ecf20Sopenharmony_ci	struct v4l2_fh *vfh = file->private_data;
10308c2ecf20Sopenharmony_ci	struct vsp1_video *video = to_vsp1_video(vfh->vdev);
10318c2ecf20Sopenharmony_ci	struct media_device *mdev = &video->vsp1->media_dev;
10328c2ecf20Sopenharmony_ci	struct vsp1_pipeline *pipe;
10338c2ecf20Sopenharmony_ci	int ret;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	if (video->queue.owner && video->queue.owner != file->private_data)
10368c2ecf20Sopenharmony_ci		return -EBUSY;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	/*
10398c2ecf20Sopenharmony_ci	 * Get a pipeline for the video node and start streaming on it. No link
10408c2ecf20Sopenharmony_ci	 * touching an entity in the pipeline can be activated or deactivated
10418c2ecf20Sopenharmony_ci	 * once streaming is started.
10428c2ecf20Sopenharmony_ci	 */
10438c2ecf20Sopenharmony_ci	mutex_lock(&mdev->graph_mutex);
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	pipe = vsp1_video_pipeline_get(video);
10468c2ecf20Sopenharmony_ci	if (IS_ERR(pipe)) {
10478c2ecf20Sopenharmony_ci		mutex_unlock(&mdev->graph_mutex);
10488c2ecf20Sopenharmony_ci		return PTR_ERR(pipe);
10498c2ecf20Sopenharmony_ci	}
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	ret = __media_pipeline_start(&video->video.entity, &pipe->pipe);
10528c2ecf20Sopenharmony_ci	if (ret < 0) {
10538c2ecf20Sopenharmony_ci		mutex_unlock(&mdev->graph_mutex);
10548c2ecf20Sopenharmony_ci		goto err_pipe;
10558c2ecf20Sopenharmony_ci	}
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	mutex_unlock(&mdev->graph_mutex);
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	/*
10608c2ecf20Sopenharmony_ci	 * Verify that the configured format matches the output of the connected
10618c2ecf20Sopenharmony_ci	 * subdev.
10628c2ecf20Sopenharmony_ci	 */
10638c2ecf20Sopenharmony_ci	ret = vsp1_video_verify_format(video);
10648c2ecf20Sopenharmony_ci	if (ret < 0)
10658c2ecf20Sopenharmony_ci		goto err_stop;
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	/* Start the queue. */
10688c2ecf20Sopenharmony_ci	ret = vb2_streamon(&video->queue, type);
10698c2ecf20Sopenharmony_ci	if (ret < 0)
10708c2ecf20Sopenharmony_ci		goto err_stop;
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	return 0;
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_cierr_stop:
10758c2ecf20Sopenharmony_ci	media_pipeline_stop(&video->video.entity);
10768c2ecf20Sopenharmony_cierr_pipe:
10778c2ecf20Sopenharmony_ci	vsp1_video_pipeline_put(pipe);
10788c2ecf20Sopenharmony_ci	return ret;
10798c2ecf20Sopenharmony_ci}
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops vsp1_video_ioctl_ops = {
10828c2ecf20Sopenharmony_ci	.vidioc_querycap		= vsp1_video_querycap,
10838c2ecf20Sopenharmony_ci	.vidioc_g_fmt_vid_cap_mplane	= vsp1_video_get_format,
10848c2ecf20Sopenharmony_ci	.vidioc_s_fmt_vid_cap_mplane	= vsp1_video_set_format,
10858c2ecf20Sopenharmony_ci	.vidioc_try_fmt_vid_cap_mplane	= vsp1_video_try_format,
10868c2ecf20Sopenharmony_ci	.vidioc_g_fmt_vid_out_mplane	= vsp1_video_get_format,
10878c2ecf20Sopenharmony_ci	.vidioc_s_fmt_vid_out_mplane	= vsp1_video_set_format,
10888c2ecf20Sopenharmony_ci	.vidioc_try_fmt_vid_out_mplane	= vsp1_video_try_format,
10898c2ecf20Sopenharmony_ci	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
10908c2ecf20Sopenharmony_ci	.vidioc_querybuf		= vb2_ioctl_querybuf,
10918c2ecf20Sopenharmony_ci	.vidioc_qbuf			= vb2_ioctl_qbuf,
10928c2ecf20Sopenharmony_ci	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
10938c2ecf20Sopenharmony_ci	.vidioc_expbuf			= vb2_ioctl_expbuf,
10948c2ecf20Sopenharmony_ci	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
10958c2ecf20Sopenharmony_ci	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
10968c2ecf20Sopenharmony_ci	.vidioc_streamon		= vsp1_video_streamon,
10978c2ecf20Sopenharmony_ci	.vidioc_streamoff		= vb2_ioctl_streamoff,
10988c2ecf20Sopenharmony_ci};
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
11018c2ecf20Sopenharmony_ci * V4L2 File Operations
11028c2ecf20Sopenharmony_ci */
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_cistatic int vsp1_video_open(struct file *file)
11058c2ecf20Sopenharmony_ci{
11068c2ecf20Sopenharmony_ci	struct vsp1_video *video = video_drvdata(file);
11078c2ecf20Sopenharmony_ci	struct v4l2_fh *vfh;
11088c2ecf20Sopenharmony_ci	int ret = 0;
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci	vfh = kzalloc(sizeof(*vfh), GFP_KERNEL);
11118c2ecf20Sopenharmony_ci	if (vfh == NULL)
11128c2ecf20Sopenharmony_ci		return -ENOMEM;
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	v4l2_fh_init(vfh, &video->video);
11158c2ecf20Sopenharmony_ci	v4l2_fh_add(vfh);
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	file->private_data = vfh;
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	ret = vsp1_device_get(video->vsp1);
11208c2ecf20Sopenharmony_ci	if (ret < 0) {
11218c2ecf20Sopenharmony_ci		v4l2_fh_del(vfh);
11228c2ecf20Sopenharmony_ci		v4l2_fh_exit(vfh);
11238c2ecf20Sopenharmony_ci		kfree(vfh);
11248c2ecf20Sopenharmony_ci	}
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	return ret;
11278c2ecf20Sopenharmony_ci}
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_cistatic int vsp1_video_release(struct file *file)
11308c2ecf20Sopenharmony_ci{
11318c2ecf20Sopenharmony_ci	struct vsp1_video *video = video_drvdata(file);
11328c2ecf20Sopenharmony_ci	struct v4l2_fh *vfh = file->private_data;
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci	mutex_lock(&video->lock);
11358c2ecf20Sopenharmony_ci	if (video->queue.owner == vfh) {
11368c2ecf20Sopenharmony_ci		vb2_queue_release(&video->queue);
11378c2ecf20Sopenharmony_ci		video->queue.owner = NULL;
11388c2ecf20Sopenharmony_ci	}
11398c2ecf20Sopenharmony_ci	mutex_unlock(&video->lock);
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci	vsp1_device_put(video->vsp1);
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	v4l2_fh_release(file);
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	file->private_data = NULL;
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	return 0;
11488c2ecf20Sopenharmony_ci}
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations vsp1_video_fops = {
11518c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
11528c2ecf20Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
11538c2ecf20Sopenharmony_ci	.open = vsp1_video_open,
11548c2ecf20Sopenharmony_ci	.release = vsp1_video_release,
11558c2ecf20Sopenharmony_ci	.poll = vb2_fop_poll,
11568c2ecf20Sopenharmony_ci	.mmap = vb2_fop_mmap,
11578c2ecf20Sopenharmony_ci};
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
11608c2ecf20Sopenharmony_ci * Suspend and Resume
11618c2ecf20Sopenharmony_ci */
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_civoid vsp1_video_suspend(struct vsp1_device *vsp1)
11648c2ecf20Sopenharmony_ci{
11658c2ecf20Sopenharmony_ci	unsigned long flags;
11668c2ecf20Sopenharmony_ci	unsigned int i;
11678c2ecf20Sopenharmony_ci	int ret;
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	/*
11708c2ecf20Sopenharmony_ci	 * To avoid increasing the system suspend time needlessly, loop over the
11718c2ecf20Sopenharmony_ci	 * pipelines twice, first to set them all to the stopping state, and
11728c2ecf20Sopenharmony_ci	 * then to wait for the stop to complete.
11738c2ecf20Sopenharmony_ci	 */
11748c2ecf20Sopenharmony_ci	for (i = 0; i < vsp1->info->wpf_count; ++i) {
11758c2ecf20Sopenharmony_ci		struct vsp1_rwpf *wpf = vsp1->wpf[i];
11768c2ecf20Sopenharmony_ci		struct vsp1_pipeline *pipe;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci		if (wpf == NULL)
11798c2ecf20Sopenharmony_ci			continue;
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci		pipe = wpf->entity.pipe;
11828c2ecf20Sopenharmony_ci		if (pipe == NULL)
11838c2ecf20Sopenharmony_ci			continue;
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci		spin_lock_irqsave(&pipe->irqlock, flags);
11868c2ecf20Sopenharmony_ci		if (pipe->state == VSP1_PIPELINE_RUNNING)
11878c2ecf20Sopenharmony_ci			pipe->state = VSP1_PIPELINE_STOPPING;
11888c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&pipe->irqlock, flags);
11898c2ecf20Sopenharmony_ci	}
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci	for (i = 0; i < vsp1->info->wpf_count; ++i) {
11928c2ecf20Sopenharmony_ci		struct vsp1_rwpf *wpf = vsp1->wpf[i];
11938c2ecf20Sopenharmony_ci		struct vsp1_pipeline *pipe;
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci		if (wpf == NULL)
11968c2ecf20Sopenharmony_ci			continue;
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci		pipe = wpf->entity.pipe;
11998c2ecf20Sopenharmony_ci		if (pipe == NULL)
12008c2ecf20Sopenharmony_ci			continue;
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci		ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe),
12038c2ecf20Sopenharmony_ci					 msecs_to_jiffies(500));
12048c2ecf20Sopenharmony_ci		if (ret == 0)
12058c2ecf20Sopenharmony_ci			dev_warn(vsp1->dev, "pipeline %u stop timeout\n",
12068c2ecf20Sopenharmony_ci				 wpf->entity.index);
12078c2ecf20Sopenharmony_ci	}
12088c2ecf20Sopenharmony_ci}
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_civoid vsp1_video_resume(struct vsp1_device *vsp1)
12118c2ecf20Sopenharmony_ci{
12128c2ecf20Sopenharmony_ci	unsigned long flags;
12138c2ecf20Sopenharmony_ci	unsigned int i;
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci	/* Resume all running pipelines. */
12168c2ecf20Sopenharmony_ci	for (i = 0; i < vsp1->info->wpf_count; ++i) {
12178c2ecf20Sopenharmony_ci		struct vsp1_rwpf *wpf = vsp1->wpf[i];
12188c2ecf20Sopenharmony_ci		struct vsp1_pipeline *pipe;
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci		if (wpf == NULL)
12218c2ecf20Sopenharmony_ci			continue;
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci		pipe = wpf->entity.pipe;
12248c2ecf20Sopenharmony_ci		if (pipe == NULL)
12258c2ecf20Sopenharmony_ci			continue;
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci		/*
12288c2ecf20Sopenharmony_ci		 * The hardware may have been reset during a suspend and will
12298c2ecf20Sopenharmony_ci		 * need a full reconfiguration.
12308c2ecf20Sopenharmony_ci		 */
12318c2ecf20Sopenharmony_ci		pipe->configured = false;
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci		spin_lock_irqsave(&pipe->irqlock, flags);
12348c2ecf20Sopenharmony_ci		if (vsp1_pipeline_ready(pipe))
12358c2ecf20Sopenharmony_ci			vsp1_video_pipeline_run(pipe);
12368c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&pipe->irqlock, flags);
12378c2ecf20Sopenharmony_ci	}
12388c2ecf20Sopenharmony_ci}
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
12418c2ecf20Sopenharmony_ci * Initialization and Cleanup
12428c2ecf20Sopenharmony_ci */
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_cistruct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
12458c2ecf20Sopenharmony_ci				     struct vsp1_rwpf *rwpf)
12468c2ecf20Sopenharmony_ci{
12478c2ecf20Sopenharmony_ci	struct vsp1_video *video;
12488c2ecf20Sopenharmony_ci	const char *direction;
12498c2ecf20Sopenharmony_ci	int ret;
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	video = devm_kzalloc(vsp1->dev, sizeof(*video), GFP_KERNEL);
12528c2ecf20Sopenharmony_ci	if (!video)
12538c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci	rwpf->video = video;
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	video->vsp1 = vsp1;
12588c2ecf20Sopenharmony_ci	video->rwpf = rwpf;
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci	if (rwpf->entity.type == VSP1_ENTITY_RPF) {
12618c2ecf20Sopenharmony_ci		direction = "input";
12628c2ecf20Sopenharmony_ci		video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
12638c2ecf20Sopenharmony_ci		video->pad.flags = MEDIA_PAD_FL_SOURCE;
12648c2ecf20Sopenharmony_ci		video->video.vfl_dir = VFL_DIR_TX;
12658c2ecf20Sopenharmony_ci		video->video.device_caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE |
12668c2ecf20Sopenharmony_ci					   V4L2_CAP_STREAMING;
12678c2ecf20Sopenharmony_ci	} else {
12688c2ecf20Sopenharmony_ci		direction = "output";
12698c2ecf20Sopenharmony_ci		video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
12708c2ecf20Sopenharmony_ci		video->pad.flags = MEDIA_PAD_FL_SINK;
12718c2ecf20Sopenharmony_ci		video->video.vfl_dir = VFL_DIR_RX;
12728c2ecf20Sopenharmony_ci		video->video.device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
12738c2ecf20Sopenharmony_ci					   V4L2_CAP_STREAMING;
12748c2ecf20Sopenharmony_ci	}
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	mutex_init(&video->lock);
12778c2ecf20Sopenharmony_ci	spin_lock_init(&video->irqlock);
12788c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&video->irqqueue);
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	/* Initialize the media entity... */
12818c2ecf20Sopenharmony_ci	ret = media_entity_pads_init(&video->video.entity, 1, &video->pad);
12828c2ecf20Sopenharmony_ci	if (ret < 0)
12838c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci	/* ... and the format ... */
12868c2ecf20Sopenharmony_ci	rwpf->format.pixelformat = VSP1_VIDEO_DEF_FORMAT;
12878c2ecf20Sopenharmony_ci	rwpf->format.width = VSP1_VIDEO_DEF_WIDTH;
12888c2ecf20Sopenharmony_ci	rwpf->format.height = VSP1_VIDEO_DEF_HEIGHT;
12898c2ecf20Sopenharmony_ci	__vsp1_video_try_format(video, &rwpf->format, &rwpf->fmtinfo);
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	/* ... and the video node... */
12928c2ecf20Sopenharmony_ci	video->video.v4l2_dev = &video->vsp1->v4l2_dev;
12938c2ecf20Sopenharmony_ci	video->video.fops = &vsp1_video_fops;
12948c2ecf20Sopenharmony_ci	snprintf(video->video.name, sizeof(video->video.name), "%s %s",
12958c2ecf20Sopenharmony_ci		 rwpf->entity.subdev.name, direction);
12968c2ecf20Sopenharmony_ci	video->video.vfl_type = VFL_TYPE_VIDEO;
12978c2ecf20Sopenharmony_ci	video->video.release = video_device_release_empty;
12988c2ecf20Sopenharmony_ci	video->video.ioctl_ops = &vsp1_video_ioctl_ops;
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	video_set_drvdata(&video->video, video);
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci	video->queue.type = video->type;
13038c2ecf20Sopenharmony_ci	video->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
13048c2ecf20Sopenharmony_ci	video->queue.lock = &video->lock;
13058c2ecf20Sopenharmony_ci	video->queue.drv_priv = video;
13068c2ecf20Sopenharmony_ci	video->queue.buf_struct_size = sizeof(struct vsp1_vb2_buffer);
13078c2ecf20Sopenharmony_ci	video->queue.ops = &vsp1_video_queue_qops;
13088c2ecf20Sopenharmony_ci	video->queue.mem_ops = &vb2_dma_contig_memops;
13098c2ecf20Sopenharmony_ci	video->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
13108c2ecf20Sopenharmony_ci	video->queue.dev = video->vsp1->bus_master;
13118c2ecf20Sopenharmony_ci	ret = vb2_queue_init(&video->queue);
13128c2ecf20Sopenharmony_ci	if (ret < 0) {
13138c2ecf20Sopenharmony_ci		dev_err(video->vsp1->dev, "failed to initialize vb2 queue\n");
13148c2ecf20Sopenharmony_ci		goto error;
13158c2ecf20Sopenharmony_ci	}
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci	/* ... and register the video device. */
13188c2ecf20Sopenharmony_ci	video->video.queue = &video->queue;
13198c2ecf20Sopenharmony_ci	ret = video_register_device(&video->video, VFL_TYPE_VIDEO, -1);
13208c2ecf20Sopenharmony_ci	if (ret < 0) {
13218c2ecf20Sopenharmony_ci		dev_err(video->vsp1->dev, "failed to register video device\n");
13228c2ecf20Sopenharmony_ci		goto error;
13238c2ecf20Sopenharmony_ci	}
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	return video;
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_cierror:
13288c2ecf20Sopenharmony_ci	vsp1_video_cleanup(video);
13298c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
13308c2ecf20Sopenharmony_ci}
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_civoid vsp1_video_cleanup(struct vsp1_video *video)
13338c2ecf20Sopenharmony_ci{
13348c2ecf20Sopenharmony_ci	if (video_is_registered(&video->video))
13358c2ecf20Sopenharmony_ci		video_unregister_device(&video->video);
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	media_entity_cleanup(&video->video.entity);
13388c2ecf20Sopenharmony_ci}
1339