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