18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ispvideo.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * TI OMAP3 ISP - Generic video node 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2009-2010 Nokia Corporation 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> 108c2ecf20Sopenharmony_ci * Sakari Ailus <sakari.ailus@iki.fi> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/clk.h> 148c2ecf20Sopenharmony_ci#include <linux/mm.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 178c2ecf20Sopenharmony_ci#include <linux/scatterlist.h> 188c2ecf20Sopenharmony_ci#include <linux/sched.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <media/v4l2-dev.h> 238c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h> 248c2ecf20Sopenharmony_ci#include <media/v4l2-mc.h> 258c2ecf20Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "ispvideo.h" 288c2ecf20Sopenharmony_ci#include "isp.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 328c2ecf20Sopenharmony_ci * Helper functions 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* 368c2ecf20Sopenharmony_ci * NOTE: When adding new media bus codes, always remember to add 378c2ecf20Sopenharmony_ci * corresponding in-memory formats to the table below!!! 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_cistatic struct isp_format_info formats[] = { 408c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_Y8_1X8, MEDIA_BUS_FMT_Y8_1X8, 418c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_Y8_1X8, MEDIA_BUS_FMT_Y8_1X8, 428c2ecf20Sopenharmony_ci V4L2_PIX_FMT_GREY, 8, 1, }, 438c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_Y10_1X10, MEDIA_BUS_FMT_Y10_1X10, 448c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_Y10_1X10, MEDIA_BUS_FMT_Y8_1X8, 458c2ecf20Sopenharmony_ci V4L2_PIX_FMT_Y10, 10, 2, }, 468c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_Y12_1X12, MEDIA_BUS_FMT_Y10_1X10, 478c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_Y12_1X12, MEDIA_BUS_FMT_Y8_1X8, 488c2ecf20Sopenharmony_ci V4L2_PIX_FMT_Y12, 12, 2, }, 498c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR8_1X8, MEDIA_BUS_FMT_SBGGR8_1X8, 508c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_SBGGR8_1X8, MEDIA_BUS_FMT_SBGGR8_1X8, 518c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SBGGR8, 8, 1, }, 528c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG8_1X8, MEDIA_BUS_FMT_SGBRG8_1X8, 538c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_SGBRG8_1X8, MEDIA_BUS_FMT_SGBRG8_1X8, 548c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SGBRG8, 8, 1, }, 558c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG8_1X8, MEDIA_BUS_FMT_SGRBG8_1X8, 568c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_SGRBG8_1X8, MEDIA_BUS_FMT_SGRBG8_1X8, 578c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SGRBG8, 8, 1, }, 588c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB8_1X8, MEDIA_BUS_FMT_SRGGB8_1X8, 598c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_SRGGB8_1X8, MEDIA_BUS_FMT_SRGGB8_1X8, 608c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SRGGB8, 8, 1, }, 618c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, 628c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_SBGGR10_1X10, 0, 638c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SBGGR10DPCM8, 8, 1, }, 648c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, 658c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_SGBRG10_1X10, 0, 668c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SGBRG10DPCM8, 8, 1, }, 678c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, 688c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_SGRBG10_1X10, 0, 698c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SGRBG10DPCM8, 8, 1, }, 708c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, 718c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_SRGGB10_1X10, 0, 728c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SRGGB10DPCM8, 8, 1, }, 738c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SBGGR10_1X10, 748c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SBGGR8_1X8, 758c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SBGGR10, 10, 2, }, 768c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10, 778c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SGBRG8_1X8, 788c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SGBRG10, 10, 2, }, 798c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10, 808c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SGRBG8_1X8, 818c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SGRBG10, 10, 2, }, 828c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10, 838c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SRGGB8_1X8, 848c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SRGGB10, 10, 2, }, 858c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SBGGR10_1X10, 868c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SBGGR8_1X8, 878c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SBGGR12, 12, 2, }, 888c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG12_1X12, MEDIA_BUS_FMT_SGBRG10_1X10, 898c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_SGBRG12_1X12, MEDIA_BUS_FMT_SGBRG8_1X8, 908c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SGBRG12, 12, 2, }, 918c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SGRBG10_1X10, 928c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SGRBG8_1X8, 938c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SGRBG12, 12, 2, }, 948c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB12_1X12, MEDIA_BUS_FMT_SRGGB10_1X10, 958c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_SRGGB12_1X12, MEDIA_BUS_FMT_SRGGB8_1X8, 968c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SRGGB12, 12, 2, }, 978c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_UYVY8_1X16, 988c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_1X16, 0, 998c2ecf20Sopenharmony_ci V4L2_PIX_FMT_UYVY, 16, 2, }, 1008c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_1X16, MEDIA_BUS_FMT_YUYV8_1X16, 1018c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_YUYV8_1X16, 0, 1028c2ecf20Sopenharmony_ci V4L2_PIX_FMT_YUYV, 16, 2, }, 1038c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_UYVY8_2X8, MEDIA_BUS_FMT_UYVY8_2X8, 1048c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_2X8, 0, 1058c2ecf20Sopenharmony_ci V4L2_PIX_FMT_UYVY, 8, 2, }, 1068c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_2X8, MEDIA_BUS_FMT_YUYV8_2X8, 1078c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_YUYV8_2X8, 0, 1088c2ecf20Sopenharmony_ci V4L2_PIX_FMT_YUYV, 8, 2, }, 1098c2ecf20Sopenharmony_ci /* Empty entry to catch the unsupported pixel code (0) used by the CCDC 1108c2ecf20Sopenharmony_ci * module and avoid NULL pointer dereferences. 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_ci { 0, } 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ciconst struct isp_format_info *omap3isp_video_format_info(u32 code) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci unsigned int i; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(formats); ++i) { 1208c2ecf20Sopenharmony_ci if (formats[i].code == code) 1218c2ecf20Sopenharmony_ci return &formats[i]; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return NULL; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/* 1288c2ecf20Sopenharmony_ci * isp_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format 1298c2ecf20Sopenharmony_ci * @video: ISP video instance 1308c2ecf20Sopenharmony_ci * @mbus: v4l2_mbus_framefmt format (input) 1318c2ecf20Sopenharmony_ci * @pix: v4l2_pix_format format (output) 1328c2ecf20Sopenharmony_ci * 1338c2ecf20Sopenharmony_ci * Fill the output pix structure with information from the input mbus format. 1348c2ecf20Sopenharmony_ci * The bytesperline and sizeimage fields are computed from the requested bytes 1358c2ecf20Sopenharmony_ci * per line value in the pix format and information from the video instance. 1368c2ecf20Sopenharmony_ci * 1378c2ecf20Sopenharmony_ci * Return the number of padding bytes at end of line. 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_cistatic unsigned int isp_video_mbus_to_pix(const struct isp_video *video, 1408c2ecf20Sopenharmony_ci const struct v4l2_mbus_framefmt *mbus, 1418c2ecf20Sopenharmony_ci struct v4l2_pix_format *pix) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci unsigned int bpl = pix->bytesperline; 1448c2ecf20Sopenharmony_ci unsigned int min_bpl; 1458c2ecf20Sopenharmony_ci unsigned int i; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci memset(pix, 0, sizeof(*pix)); 1488c2ecf20Sopenharmony_ci pix->width = mbus->width; 1498c2ecf20Sopenharmony_ci pix->height = mbus->height; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(formats); ++i) { 1528c2ecf20Sopenharmony_ci if (formats[i].code == mbus->code) 1538c2ecf20Sopenharmony_ci break; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (WARN_ON(i == ARRAY_SIZE(formats))) 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci min_bpl = pix->width * formats[i].bpp; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* Clamp the requested bytes per line value. If the maximum bytes per 1628c2ecf20Sopenharmony_ci * line value is zero, the module doesn't support user configurable line 1638c2ecf20Sopenharmony_ci * sizes. Override the requested value with the minimum in that case. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_ci if (video->bpl_max) 1668c2ecf20Sopenharmony_ci bpl = clamp(bpl, min_bpl, video->bpl_max); 1678c2ecf20Sopenharmony_ci else 1688c2ecf20Sopenharmony_ci bpl = min_bpl; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (!video->bpl_zero_padding || bpl != min_bpl) 1718c2ecf20Sopenharmony_ci bpl = ALIGN(bpl, video->bpl_alignment); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci pix->pixelformat = formats[i].pixelformat; 1748c2ecf20Sopenharmony_ci pix->bytesperline = bpl; 1758c2ecf20Sopenharmony_ci pix->sizeimage = pix->bytesperline * pix->height; 1768c2ecf20Sopenharmony_ci pix->colorspace = mbus->colorspace; 1778c2ecf20Sopenharmony_ci pix->field = mbus->field; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return bpl - min_bpl; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic void isp_video_pix_to_mbus(const struct v4l2_pix_format *pix, 1838c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mbus) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci unsigned int i; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci memset(mbus, 0, sizeof(*mbus)); 1888c2ecf20Sopenharmony_ci mbus->width = pix->width; 1898c2ecf20Sopenharmony_ci mbus->height = pix->height; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* Skip the last format in the loop so that it will be selected if no 1928c2ecf20Sopenharmony_ci * match is found. 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(formats) - 1; ++i) { 1958c2ecf20Sopenharmony_ci if (formats[i].pixelformat == pix->pixelformat) 1968c2ecf20Sopenharmony_ci break; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci mbus->code = formats[i].code; 2008c2ecf20Sopenharmony_ci mbus->colorspace = pix->colorspace; 2018c2ecf20Sopenharmony_ci mbus->field = pix->field; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic struct v4l2_subdev * 2058c2ecf20Sopenharmony_ciisp_video_remote_subdev(struct isp_video *video, u32 *pad) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct media_pad *remote; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci remote = media_entity_remote_pad(&video->pad); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) 2128c2ecf20Sopenharmony_ci return NULL; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (pad) 2158c2ecf20Sopenharmony_ci *pad = remote->index; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return media_entity_to_v4l2_subdev(remote->entity); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci/* Return a pointer to the ISP video instance at the far end of the pipeline. */ 2218c2ecf20Sopenharmony_cistatic int isp_video_get_graph_data(struct isp_video *video, 2228c2ecf20Sopenharmony_ci struct isp_pipeline *pipe) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct media_graph graph; 2258c2ecf20Sopenharmony_ci struct media_entity *entity = &video->video.entity; 2268c2ecf20Sopenharmony_ci struct media_device *mdev = entity->graph_obj.mdev; 2278c2ecf20Sopenharmony_ci struct isp_video *far_end = NULL; 2288c2ecf20Sopenharmony_ci int ret; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci mutex_lock(&mdev->graph_mutex); 2318c2ecf20Sopenharmony_ci ret = media_graph_walk_init(&graph, mdev); 2328c2ecf20Sopenharmony_ci if (ret) { 2338c2ecf20Sopenharmony_ci mutex_unlock(&mdev->graph_mutex); 2348c2ecf20Sopenharmony_ci return ret; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci media_graph_walk_start(&graph, entity); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci while ((entity = media_graph_walk_next(&graph))) { 2408c2ecf20Sopenharmony_ci struct isp_video *__video; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci media_entity_enum_set(&pipe->ent_enum, entity); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (far_end != NULL) 2458c2ecf20Sopenharmony_ci continue; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (entity == &video->video.entity) 2488c2ecf20Sopenharmony_ci continue; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (!is_media_entity_v4l2_video_device(entity)) 2518c2ecf20Sopenharmony_ci continue; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci __video = to_isp_video(media_entity_to_video_device(entity)); 2548c2ecf20Sopenharmony_ci if (__video->type != video->type) 2558c2ecf20Sopenharmony_ci far_end = __video; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci mutex_unlock(&mdev->graph_mutex); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci media_graph_walk_cleanup(&graph); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { 2638c2ecf20Sopenharmony_ci pipe->input = far_end; 2648c2ecf20Sopenharmony_ci pipe->output = video; 2658c2ecf20Sopenharmony_ci } else { 2668c2ecf20Sopenharmony_ci if (far_end == NULL) 2678c2ecf20Sopenharmony_ci return -EPIPE; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci pipe->input = video; 2708c2ecf20Sopenharmony_ci pipe->output = far_end; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return 0; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic int 2778c2ecf20Sopenharmony_ci__isp_video_get_format(struct isp_video *video, struct v4l2_format *format) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct v4l2_subdev_format fmt; 2808c2ecf20Sopenharmony_ci struct v4l2_subdev *subdev; 2818c2ecf20Sopenharmony_ci u32 pad; 2828c2ecf20Sopenharmony_ci int ret; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci subdev = isp_video_remote_subdev(video, &pad); 2858c2ecf20Sopenharmony_ci if (subdev == NULL) 2868c2ecf20Sopenharmony_ci return -EINVAL; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci fmt.pad = pad; 2898c2ecf20Sopenharmony_ci fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci mutex_lock(&video->mutex); 2928c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); 2938c2ecf20Sopenharmony_ci mutex_unlock(&video->mutex); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (ret) 2968c2ecf20Sopenharmony_ci return ret; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci format->type = video->type; 2998c2ecf20Sopenharmony_ci return isp_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic int 3038c2ecf20Sopenharmony_ciisp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct v4l2_format format; 3068c2ecf20Sopenharmony_ci int ret; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci memcpy(&format, &vfh->format, sizeof(format)); 3098c2ecf20Sopenharmony_ci ret = __isp_video_get_format(video, &format); 3108c2ecf20Sopenharmony_ci if (ret < 0) 3118c2ecf20Sopenharmony_ci return ret; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (vfh->format.fmt.pix.pixelformat != format.fmt.pix.pixelformat || 3148c2ecf20Sopenharmony_ci vfh->format.fmt.pix.height != format.fmt.pix.height || 3158c2ecf20Sopenharmony_ci vfh->format.fmt.pix.width != format.fmt.pix.width || 3168c2ecf20Sopenharmony_ci vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline || 3178c2ecf20Sopenharmony_ci vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage || 3188c2ecf20Sopenharmony_ci vfh->format.fmt.pix.field != format.fmt.pix.field) 3198c2ecf20Sopenharmony_ci return -EINVAL; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 3258c2ecf20Sopenharmony_ci * Video queue operations 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic int isp_video_queue_setup(struct vb2_queue *queue, 3298c2ecf20Sopenharmony_ci unsigned int *count, unsigned int *num_planes, 3308c2ecf20Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct isp_video_fh *vfh = vb2_get_drv_priv(queue); 3338c2ecf20Sopenharmony_ci struct isp_video *video = vfh->video; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci *num_planes = 1; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci sizes[0] = vfh->format.fmt.pix.sizeimage; 3388c2ecf20Sopenharmony_ci if (sizes[0] == 0) 3398c2ecf20Sopenharmony_ci return -EINVAL; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci *count = min(*count, video->capture_mem / PAGE_ALIGN(sizes[0])); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci return 0; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic int isp_video_buffer_prepare(struct vb2_buffer *buf) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(buf); 3498c2ecf20Sopenharmony_ci struct isp_video_fh *vfh = vb2_get_drv_priv(buf->vb2_queue); 3508c2ecf20Sopenharmony_ci struct isp_buffer *buffer = to_isp_buffer(vbuf); 3518c2ecf20Sopenharmony_ci struct isp_video *video = vfh->video; 3528c2ecf20Sopenharmony_ci dma_addr_t addr; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* Refuse to prepare the buffer is the video node has registered an 3558c2ecf20Sopenharmony_ci * error. We don't need to take any lock here as the operation is 3568c2ecf20Sopenharmony_ci * inherently racy. The authoritative check will be performed in the 3578c2ecf20Sopenharmony_ci * queue handler, which can't return an error, this check is just a best 3588c2ecf20Sopenharmony_ci * effort to notify userspace as early as possible. 3598c2ecf20Sopenharmony_ci */ 3608c2ecf20Sopenharmony_ci if (unlikely(video->error)) 3618c2ecf20Sopenharmony_ci return -EIO; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci addr = vb2_dma_contig_plane_dma_addr(buf, 0); 3648c2ecf20Sopenharmony_ci if (!IS_ALIGNED(addr, 32)) { 3658c2ecf20Sopenharmony_ci dev_dbg(video->isp->dev, 3668c2ecf20Sopenharmony_ci "Buffer address must be aligned to 32 bytes boundary.\n"); 3678c2ecf20Sopenharmony_ci return -EINVAL; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci vb2_set_plane_payload(&buffer->vb.vb2_buf, 0, 3718c2ecf20Sopenharmony_ci vfh->format.fmt.pix.sizeimage); 3728c2ecf20Sopenharmony_ci buffer->dma = addr; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci return 0; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/* 3788c2ecf20Sopenharmony_ci * isp_video_buffer_queue - Add buffer to streaming queue 3798c2ecf20Sopenharmony_ci * @buf: Video buffer 3808c2ecf20Sopenharmony_ci * 3818c2ecf20Sopenharmony_ci * In memory-to-memory mode, start streaming on the pipeline if buffers are 3828c2ecf20Sopenharmony_ci * queued on both the input and the output, if the pipeline isn't already busy. 3838c2ecf20Sopenharmony_ci * If the pipeline is busy, it will be restarted in the output module interrupt 3848c2ecf20Sopenharmony_ci * handler. 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_cistatic void isp_video_buffer_queue(struct vb2_buffer *buf) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(buf); 3898c2ecf20Sopenharmony_ci struct isp_video_fh *vfh = vb2_get_drv_priv(buf->vb2_queue); 3908c2ecf20Sopenharmony_ci struct isp_buffer *buffer = to_isp_buffer(vbuf); 3918c2ecf20Sopenharmony_ci struct isp_video *video = vfh->video; 3928c2ecf20Sopenharmony_ci struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); 3938c2ecf20Sopenharmony_ci enum isp_pipeline_state state; 3948c2ecf20Sopenharmony_ci unsigned long flags; 3958c2ecf20Sopenharmony_ci unsigned int empty; 3968c2ecf20Sopenharmony_ci unsigned int start; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci spin_lock_irqsave(&video->irqlock, flags); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (unlikely(video->error)) { 4018c2ecf20Sopenharmony_ci vb2_buffer_done(&buffer->vb.vb2_buf, VB2_BUF_STATE_ERROR); 4028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 4038c2ecf20Sopenharmony_ci return; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci empty = list_empty(&video->dmaqueue); 4078c2ecf20Sopenharmony_ci list_add_tail(&buffer->irqlist, &video->dmaqueue); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (empty) { 4128c2ecf20Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 4138c2ecf20Sopenharmony_ci state = ISP_PIPELINE_QUEUE_OUTPUT; 4148c2ecf20Sopenharmony_ci else 4158c2ecf20Sopenharmony_ci state = ISP_PIPELINE_QUEUE_INPUT; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci spin_lock_irqsave(&pipe->lock, flags); 4188c2ecf20Sopenharmony_ci pipe->state |= state; 4198c2ecf20Sopenharmony_ci video->ops->queue(video, buffer); 4208c2ecf20Sopenharmony_ci video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci start = isp_pipeline_ready(pipe); 4238c2ecf20Sopenharmony_ci if (start) 4248c2ecf20Sopenharmony_ci pipe->state |= ISP_PIPELINE_STREAM; 4258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pipe->lock, flags); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (start) 4288c2ecf20Sopenharmony_ci omap3isp_pipeline_set_stream(pipe, 4298c2ecf20Sopenharmony_ci ISP_PIPELINE_STREAM_SINGLESHOT); 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci/* 4348c2ecf20Sopenharmony_ci * omap3isp_video_return_buffers - Return all queued buffers to videobuf2 4358c2ecf20Sopenharmony_ci * @video: ISP video object 4368c2ecf20Sopenharmony_ci * @state: new state for the returned buffers 4378c2ecf20Sopenharmony_ci * 4388c2ecf20Sopenharmony_ci * Return all buffers queued on the video node to videobuf2 in the given state. 4398c2ecf20Sopenharmony_ci * The buffer state should be VB2_BUF_STATE_QUEUED if called due to an error 4408c2ecf20Sopenharmony_ci * when starting the stream, or VB2_BUF_STATE_ERROR otherwise. 4418c2ecf20Sopenharmony_ci * 4428c2ecf20Sopenharmony_ci * The function must be called with the video irqlock held. 4438c2ecf20Sopenharmony_ci */ 4448c2ecf20Sopenharmony_cistatic void omap3isp_video_return_buffers(struct isp_video *video, 4458c2ecf20Sopenharmony_ci enum vb2_buffer_state state) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci while (!list_empty(&video->dmaqueue)) { 4488c2ecf20Sopenharmony_ci struct isp_buffer *buf; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci buf = list_first_entry(&video->dmaqueue, 4518c2ecf20Sopenharmony_ci struct isp_buffer, irqlist); 4528c2ecf20Sopenharmony_ci list_del(&buf->irqlist); 4538c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, state); 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic int isp_video_start_streaming(struct vb2_queue *queue, 4588c2ecf20Sopenharmony_ci unsigned int count) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci struct isp_video_fh *vfh = vb2_get_drv_priv(queue); 4618c2ecf20Sopenharmony_ci struct isp_video *video = vfh->video; 4628c2ecf20Sopenharmony_ci struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); 4638c2ecf20Sopenharmony_ci unsigned long flags; 4648c2ecf20Sopenharmony_ci int ret; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* In sensor-to-memory mode, the stream can be started synchronously 4678c2ecf20Sopenharmony_ci * to the stream on command. In memory-to-memory mode, it will be 4688c2ecf20Sopenharmony_ci * started when buffers are queued on both the input and output. 4698c2ecf20Sopenharmony_ci */ 4708c2ecf20Sopenharmony_ci if (pipe->input) 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci ret = omap3isp_pipeline_set_stream(pipe, 4748c2ecf20Sopenharmony_ci ISP_PIPELINE_STREAM_CONTINUOUS); 4758c2ecf20Sopenharmony_ci if (ret < 0) { 4768c2ecf20Sopenharmony_ci spin_lock_irqsave(&video->irqlock, flags); 4778c2ecf20Sopenharmony_ci omap3isp_video_return_buffers(video, VB2_BUF_STATE_QUEUED); 4788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 4798c2ecf20Sopenharmony_ci return ret; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci spin_lock_irqsave(&video->irqlock, flags); 4838c2ecf20Sopenharmony_ci if (list_empty(&video->dmaqueue)) 4848c2ecf20Sopenharmony_ci video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; 4858c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci return 0; 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic const struct vb2_ops isp_video_queue_ops = { 4918c2ecf20Sopenharmony_ci .queue_setup = isp_video_queue_setup, 4928c2ecf20Sopenharmony_ci .buf_prepare = isp_video_buffer_prepare, 4938c2ecf20Sopenharmony_ci .buf_queue = isp_video_buffer_queue, 4948c2ecf20Sopenharmony_ci .start_streaming = isp_video_start_streaming, 4958c2ecf20Sopenharmony_ci}; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci/* 4988c2ecf20Sopenharmony_ci * omap3isp_video_buffer_next - Complete the current buffer and return the next 4998c2ecf20Sopenharmony_ci * @video: ISP video object 5008c2ecf20Sopenharmony_ci * 5018c2ecf20Sopenharmony_ci * Remove the current video buffer from the DMA queue and fill its timestamp and 5028c2ecf20Sopenharmony_ci * field count before handing it back to videobuf2. 5038c2ecf20Sopenharmony_ci * 5048c2ecf20Sopenharmony_ci * For capture video nodes the buffer state is set to VB2_BUF_STATE_DONE if no 5058c2ecf20Sopenharmony_ci * error has been flagged in the pipeline, or to VB2_BUF_STATE_ERROR otherwise. 5068c2ecf20Sopenharmony_ci * For video output nodes the buffer state is always set to VB2_BUF_STATE_DONE. 5078c2ecf20Sopenharmony_ci * 5088c2ecf20Sopenharmony_ci * The DMA queue is expected to contain at least one buffer. 5098c2ecf20Sopenharmony_ci * 5108c2ecf20Sopenharmony_ci * Return a pointer to the next buffer in the DMA queue, or NULL if the queue is 5118c2ecf20Sopenharmony_ci * empty. 5128c2ecf20Sopenharmony_ci */ 5138c2ecf20Sopenharmony_cistruct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); 5168c2ecf20Sopenharmony_ci enum vb2_buffer_state vb_state; 5178c2ecf20Sopenharmony_ci struct isp_buffer *buf; 5188c2ecf20Sopenharmony_ci unsigned long flags; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci spin_lock_irqsave(&video->irqlock, flags); 5218c2ecf20Sopenharmony_ci if (WARN_ON(list_empty(&video->dmaqueue))) { 5228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 5238c2ecf20Sopenharmony_ci return NULL; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci buf = list_first_entry(&video->dmaqueue, struct isp_buffer, 5278c2ecf20Sopenharmony_ci irqlist); 5288c2ecf20Sopenharmony_ci list_del(&buf->irqlist); 5298c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci buf->vb.vb2_buf.timestamp = ktime_get_ns(); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci /* Do frame number propagation only if this is the output video node. 5348c2ecf20Sopenharmony_ci * Frame number either comes from the CSI receivers or it gets 5358c2ecf20Sopenharmony_ci * incremented here if H3A is not active. 5368c2ecf20Sopenharmony_ci * Note: There is no guarantee that the output buffer will finish 5378c2ecf20Sopenharmony_ci * first, so the input number might lag behind by 1 in some cases. 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_ci if (video == pipe->output && !pipe->do_propagation) 5408c2ecf20Sopenharmony_ci buf->vb.sequence = 5418c2ecf20Sopenharmony_ci atomic_inc_return(&pipe->frame_number); 5428c2ecf20Sopenharmony_ci else 5438c2ecf20Sopenharmony_ci buf->vb.sequence = atomic_read(&pipe->frame_number); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (pipe->field != V4L2_FIELD_NONE) 5468c2ecf20Sopenharmony_ci buf->vb.sequence /= 2; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci buf->vb.field = pipe->field; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* Report pipeline errors to userspace on the capture device side. */ 5518c2ecf20Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) { 5528c2ecf20Sopenharmony_ci vb_state = VB2_BUF_STATE_ERROR; 5538c2ecf20Sopenharmony_ci pipe->error = false; 5548c2ecf20Sopenharmony_ci } else { 5558c2ecf20Sopenharmony_ci vb_state = VB2_BUF_STATE_DONE; 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, vb_state); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci spin_lock_irqsave(&video->irqlock, flags); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (list_empty(&video->dmaqueue)) { 5638c2ecf20Sopenharmony_ci enum isp_pipeline_state state; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 5688c2ecf20Sopenharmony_ci state = ISP_PIPELINE_QUEUE_OUTPUT 5698c2ecf20Sopenharmony_ci | ISP_PIPELINE_STREAM; 5708c2ecf20Sopenharmony_ci else 5718c2ecf20Sopenharmony_ci state = ISP_PIPELINE_QUEUE_INPUT 5728c2ecf20Sopenharmony_ci | ISP_PIPELINE_STREAM; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci spin_lock_irqsave(&pipe->lock, flags); 5758c2ecf20Sopenharmony_ci pipe->state &= ~state; 5768c2ecf20Sopenharmony_ci if (video->pipe.stream_state == ISP_PIPELINE_STREAM_CONTINUOUS) 5778c2ecf20Sopenharmony_ci video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; 5788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pipe->lock, flags); 5798c2ecf20Sopenharmony_ci return NULL; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) { 5838c2ecf20Sopenharmony_ci spin_lock(&pipe->lock); 5848c2ecf20Sopenharmony_ci pipe->state &= ~ISP_PIPELINE_STREAM; 5858c2ecf20Sopenharmony_ci spin_unlock(&pipe->lock); 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci buf = list_first_entry(&video->dmaqueue, struct isp_buffer, 5898c2ecf20Sopenharmony_ci irqlist); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci return buf; 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci/* 5978c2ecf20Sopenharmony_ci * omap3isp_video_cancel_stream - Cancel stream on a video node 5988c2ecf20Sopenharmony_ci * @video: ISP video object 5998c2ecf20Sopenharmony_ci * 6008c2ecf20Sopenharmony_ci * Cancelling a stream returns all buffers queued on the video node to videobuf2 6018c2ecf20Sopenharmony_ci * in the erroneous state and makes sure no new buffer can be queued. 6028c2ecf20Sopenharmony_ci */ 6038c2ecf20Sopenharmony_civoid omap3isp_video_cancel_stream(struct isp_video *video) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci unsigned long flags; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci spin_lock_irqsave(&video->irqlock, flags); 6088c2ecf20Sopenharmony_ci omap3isp_video_return_buffers(video, VB2_BUF_STATE_ERROR); 6098c2ecf20Sopenharmony_ci video->error = true; 6108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci/* 6148c2ecf20Sopenharmony_ci * omap3isp_video_resume - Perform resume operation on the buffers 6158c2ecf20Sopenharmony_ci * @video: ISP video object 6168c2ecf20Sopenharmony_ci * @continuous: Pipeline is in single shot mode if 0 or continuous mode otherwise 6178c2ecf20Sopenharmony_ci * 6188c2ecf20Sopenharmony_ci * This function is intended to be used on suspend/resume scenario. It 6198c2ecf20Sopenharmony_ci * requests video queue layer to discard buffers marked as DONE if it's in 6208c2ecf20Sopenharmony_ci * continuous mode and requests ISP modules to queue again the ACTIVE buffer 6218c2ecf20Sopenharmony_ci * if there's any. 6228c2ecf20Sopenharmony_ci */ 6238c2ecf20Sopenharmony_civoid omap3isp_video_resume(struct isp_video *video, int continuous) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci struct isp_buffer *buf = NULL; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci if (continuous && video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { 6288c2ecf20Sopenharmony_ci mutex_lock(&video->queue_lock); 6298c2ecf20Sopenharmony_ci vb2_discard_done(video->queue); 6308c2ecf20Sopenharmony_ci mutex_unlock(&video->queue_lock); 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci if (!list_empty(&video->dmaqueue)) { 6348c2ecf20Sopenharmony_ci buf = list_first_entry(&video->dmaqueue, 6358c2ecf20Sopenharmony_ci struct isp_buffer, irqlist); 6368c2ecf20Sopenharmony_ci video->ops->queue(video, buf); 6378c2ecf20Sopenharmony_ci video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED; 6388c2ecf20Sopenharmony_ci } else { 6398c2ecf20Sopenharmony_ci if (continuous) 6408c2ecf20Sopenharmony_ci video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 6458c2ecf20Sopenharmony_ci * V4L2 ioctls 6468c2ecf20Sopenharmony_ci */ 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cistatic int 6498c2ecf20Sopenharmony_ciisp_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci struct isp_video *video = video_drvdata(file); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci strscpy(cap->driver, ISP_VIDEO_DRIVER_NAME, sizeof(cap->driver)); 6548c2ecf20Sopenharmony_ci strscpy(cap->card, video->video.name, sizeof(cap->card)); 6558c2ecf20Sopenharmony_ci strscpy(cap->bus_info, "media", sizeof(cap->bus_info)); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT 6588c2ecf20Sopenharmony_ci | V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci return 0; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic int 6658c2ecf20Sopenharmony_ciisp_video_get_format(struct file *file, void *fh, struct v4l2_format *format) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(fh); 6688c2ecf20Sopenharmony_ci struct isp_video *video = video_drvdata(file); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if (format->type != video->type) 6718c2ecf20Sopenharmony_ci return -EINVAL; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci mutex_lock(&video->mutex); 6748c2ecf20Sopenharmony_ci *format = vfh->format; 6758c2ecf20Sopenharmony_ci mutex_unlock(&video->mutex); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci return 0; 6788c2ecf20Sopenharmony_ci} 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_cistatic int 6818c2ecf20Sopenharmony_ciisp_video_set_format(struct file *file, void *fh, struct v4l2_format *format) 6828c2ecf20Sopenharmony_ci{ 6838c2ecf20Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(fh); 6848c2ecf20Sopenharmony_ci struct isp_video *video = video_drvdata(file); 6858c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt fmt; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci if (format->type != video->type) 6888c2ecf20Sopenharmony_ci return -EINVAL; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci /* Replace unsupported field orders with sane defaults. */ 6918c2ecf20Sopenharmony_ci switch (format->fmt.pix.field) { 6928c2ecf20Sopenharmony_ci case V4L2_FIELD_NONE: 6938c2ecf20Sopenharmony_ci /* Progressive is supported everywhere. */ 6948c2ecf20Sopenharmony_ci break; 6958c2ecf20Sopenharmony_ci case V4L2_FIELD_ALTERNATE: 6968c2ecf20Sopenharmony_ci /* ALTERNATE is not supported on output nodes. */ 6978c2ecf20Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 6988c2ecf20Sopenharmony_ci format->fmt.pix.field = V4L2_FIELD_NONE; 6998c2ecf20Sopenharmony_ci break; 7008c2ecf20Sopenharmony_ci case V4L2_FIELD_INTERLACED: 7018c2ecf20Sopenharmony_ci /* The ISP has no concept of video standard, select the 7028c2ecf20Sopenharmony_ci * top-bottom order when the unqualified interlaced order is 7038c2ecf20Sopenharmony_ci * requested. 7048c2ecf20Sopenharmony_ci */ 7058c2ecf20Sopenharmony_ci format->fmt.pix.field = V4L2_FIELD_INTERLACED_TB; 7068c2ecf20Sopenharmony_ci fallthrough; 7078c2ecf20Sopenharmony_ci case V4L2_FIELD_INTERLACED_TB: 7088c2ecf20Sopenharmony_ci case V4L2_FIELD_INTERLACED_BT: 7098c2ecf20Sopenharmony_ci /* Interlaced orders are only supported at the CCDC output. */ 7108c2ecf20Sopenharmony_ci if (video != &video->isp->isp_ccdc.video_out) 7118c2ecf20Sopenharmony_ci format->fmt.pix.field = V4L2_FIELD_NONE; 7128c2ecf20Sopenharmony_ci break; 7138c2ecf20Sopenharmony_ci case V4L2_FIELD_TOP: 7148c2ecf20Sopenharmony_ci case V4L2_FIELD_BOTTOM: 7158c2ecf20Sopenharmony_ci case V4L2_FIELD_SEQ_TB: 7168c2ecf20Sopenharmony_ci case V4L2_FIELD_SEQ_BT: 7178c2ecf20Sopenharmony_ci default: 7188c2ecf20Sopenharmony_ci /* All other field orders are currently unsupported, default to 7198c2ecf20Sopenharmony_ci * progressive. 7208c2ecf20Sopenharmony_ci */ 7218c2ecf20Sopenharmony_ci format->fmt.pix.field = V4L2_FIELD_NONE; 7228c2ecf20Sopenharmony_ci break; 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci /* Fill the bytesperline and sizeimage fields by converting to media bus 7268c2ecf20Sopenharmony_ci * format and back to pixel format. 7278c2ecf20Sopenharmony_ci */ 7288c2ecf20Sopenharmony_ci isp_video_pix_to_mbus(&format->fmt.pix, &fmt); 7298c2ecf20Sopenharmony_ci isp_video_mbus_to_pix(video, &fmt, &format->fmt.pix); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci mutex_lock(&video->mutex); 7328c2ecf20Sopenharmony_ci vfh->format = *format; 7338c2ecf20Sopenharmony_ci mutex_unlock(&video->mutex); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci return 0; 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_cistatic int 7398c2ecf20Sopenharmony_ciisp_video_try_format(struct file *file, void *fh, struct v4l2_format *format) 7408c2ecf20Sopenharmony_ci{ 7418c2ecf20Sopenharmony_ci struct isp_video *video = video_drvdata(file); 7428c2ecf20Sopenharmony_ci struct v4l2_subdev_format fmt; 7438c2ecf20Sopenharmony_ci struct v4l2_subdev *subdev; 7448c2ecf20Sopenharmony_ci u32 pad; 7458c2ecf20Sopenharmony_ci int ret; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (format->type != video->type) 7488c2ecf20Sopenharmony_ci return -EINVAL; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci subdev = isp_video_remote_subdev(video, &pad); 7518c2ecf20Sopenharmony_ci if (subdev == NULL) 7528c2ecf20Sopenharmony_ci return -EINVAL; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci isp_video_pix_to_mbus(&format->fmt.pix, &fmt.format); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci fmt.pad = pad; 7578c2ecf20Sopenharmony_ci fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; 7588c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); 7598c2ecf20Sopenharmony_ci if (ret) 7608c2ecf20Sopenharmony_ci return ret == -ENOIOCTLCMD ? -ENOTTY : ret; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci isp_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix); 7638c2ecf20Sopenharmony_ci return 0; 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_cistatic int 7678c2ecf20Sopenharmony_ciisp_video_get_selection(struct file *file, void *fh, struct v4l2_selection *sel) 7688c2ecf20Sopenharmony_ci{ 7698c2ecf20Sopenharmony_ci struct isp_video *video = video_drvdata(file); 7708c2ecf20Sopenharmony_ci struct v4l2_subdev_format format; 7718c2ecf20Sopenharmony_ci struct v4l2_subdev *subdev; 7728c2ecf20Sopenharmony_ci struct v4l2_subdev_selection sdsel = { 7738c2ecf20Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 7748c2ecf20Sopenharmony_ci .target = sel->target, 7758c2ecf20Sopenharmony_ci }; 7768c2ecf20Sopenharmony_ci u32 pad; 7778c2ecf20Sopenharmony_ci int ret; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci switch (sel->target) { 7808c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP: 7818c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 7828c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 7838c2ecf20Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 7848c2ecf20Sopenharmony_ci return -EINVAL; 7858c2ecf20Sopenharmony_ci break; 7868c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 7878c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 7888c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 7898c2ecf20Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 7908c2ecf20Sopenharmony_ci return -EINVAL; 7918c2ecf20Sopenharmony_ci break; 7928c2ecf20Sopenharmony_ci default: 7938c2ecf20Sopenharmony_ci return -EINVAL; 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci subdev = isp_video_remote_subdev(video, &pad); 7968c2ecf20Sopenharmony_ci if (subdev == NULL) 7978c2ecf20Sopenharmony_ci return -EINVAL; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci /* Try the get selection operation first and fallback to get format if not 8008c2ecf20Sopenharmony_ci * implemented. 8018c2ecf20Sopenharmony_ci */ 8028c2ecf20Sopenharmony_ci sdsel.pad = pad; 8038c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, &sdsel); 8048c2ecf20Sopenharmony_ci if (!ret) 8058c2ecf20Sopenharmony_ci sel->r = sdsel.r; 8068c2ecf20Sopenharmony_ci if (ret != -ENOIOCTLCMD) 8078c2ecf20Sopenharmony_ci return ret; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci format.pad = pad; 8108c2ecf20Sopenharmony_ci format.which = V4L2_SUBDEV_FORMAT_ACTIVE; 8118c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format); 8128c2ecf20Sopenharmony_ci if (ret < 0) 8138c2ecf20Sopenharmony_ci return ret == -ENOIOCTLCMD ? -ENOTTY : ret; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci sel->r.left = 0; 8168c2ecf20Sopenharmony_ci sel->r.top = 0; 8178c2ecf20Sopenharmony_ci sel->r.width = format.format.width; 8188c2ecf20Sopenharmony_ci sel->r.height = format.format.height; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci return 0; 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic int 8248c2ecf20Sopenharmony_ciisp_video_set_selection(struct file *file, void *fh, struct v4l2_selection *sel) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci struct isp_video *video = video_drvdata(file); 8278c2ecf20Sopenharmony_ci struct v4l2_subdev *subdev; 8288c2ecf20Sopenharmony_ci struct v4l2_subdev_selection sdsel = { 8298c2ecf20Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 8308c2ecf20Sopenharmony_ci .target = sel->target, 8318c2ecf20Sopenharmony_ci .flags = sel->flags, 8328c2ecf20Sopenharmony_ci .r = sel->r, 8338c2ecf20Sopenharmony_ci }; 8348c2ecf20Sopenharmony_ci u32 pad; 8358c2ecf20Sopenharmony_ci int ret; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci switch (sel->target) { 8388c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP: 8398c2ecf20Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 8408c2ecf20Sopenharmony_ci return -EINVAL; 8418c2ecf20Sopenharmony_ci break; 8428c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 8438c2ecf20Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 8448c2ecf20Sopenharmony_ci return -EINVAL; 8458c2ecf20Sopenharmony_ci break; 8468c2ecf20Sopenharmony_ci default: 8478c2ecf20Sopenharmony_ci return -EINVAL; 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci subdev = isp_video_remote_subdev(video, &pad); 8508c2ecf20Sopenharmony_ci if (subdev == NULL) 8518c2ecf20Sopenharmony_ci return -EINVAL; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci sdsel.pad = pad; 8548c2ecf20Sopenharmony_ci mutex_lock(&video->mutex); 8558c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(subdev, pad, set_selection, NULL, &sdsel); 8568c2ecf20Sopenharmony_ci mutex_unlock(&video->mutex); 8578c2ecf20Sopenharmony_ci if (!ret) 8588c2ecf20Sopenharmony_ci sel->r = sdsel.r; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci return ret == -ENOIOCTLCMD ? -ENOTTY : ret; 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_cistatic int 8648c2ecf20Sopenharmony_ciisp_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(fh); 8678c2ecf20Sopenharmony_ci struct isp_video *video = video_drvdata(file); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || 8708c2ecf20Sopenharmony_ci video->type != a->type) 8718c2ecf20Sopenharmony_ci return -EINVAL; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci memset(a, 0, sizeof(*a)); 8748c2ecf20Sopenharmony_ci a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 8758c2ecf20Sopenharmony_ci a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; 8768c2ecf20Sopenharmony_ci a->parm.output.timeperframe = vfh->timeperframe; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci return 0; 8798c2ecf20Sopenharmony_ci} 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_cistatic int 8828c2ecf20Sopenharmony_ciisp_video_set_param(struct file *file, void *fh, struct v4l2_streamparm *a) 8838c2ecf20Sopenharmony_ci{ 8848c2ecf20Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(fh); 8858c2ecf20Sopenharmony_ci struct isp_video *video = video_drvdata(file); 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || 8888c2ecf20Sopenharmony_ci video->type != a->type) 8898c2ecf20Sopenharmony_ci return -EINVAL; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci if (a->parm.output.timeperframe.denominator == 0) 8928c2ecf20Sopenharmony_ci a->parm.output.timeperframe.denominator = 1; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci vfh->timeperframe = a->parm.output.timeperframe; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci return 0; 8978c2ecf20Sopenharmony_ci} 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cistatic int 9008c2ecf20Sopenharmony_ciisp_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb) 9018c2ecf20Sopenharmony_ci{ 9028c2ecf20Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(fh); 9038c2ecf20Sopenharmony_ci struct isp_video *video = video_drvdata(file); 9048c2ecf20Sopenharmony_ci int ret; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci mutex_lock(&video->queue_lock); 9078c2ecf20Sopenharmony_ci ret = vb2_reqbufs(&vfh->queue, rb); 9088c2ecf20Sopenharmony_ci mutex_unlock(&video->queue_lock); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci return ret; 9118c2ecf20Sopenharmony_ci} 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_cistatic int 9148c2ecf20Sopenharmony_ciisp_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) 9158c2ecf20Sopenharmony_ci{ 9168c2ecf20Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(fh); 9178c2ecf20Sopenharmony_ci struct isp_video *video = video_drvdata(file); 9188c2ecf20Sopenharmony_ci int ret; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci mutex_lock(&video->queue_lock); 9218c2ecf20Sopenharmony_ci ret = vb2_querybuf(&vfh->queue, b); 9228c2ecf20Sopenharmony_ci mutex_unlock(&video->queue_lock); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci return ret; 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_cistatic int 9288c2ecf20Sopenharmony_ciisp_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) 9298c2ecf20Sopenharmony_ci{ 9308c2ecf20Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(fh); 9318c2ecf20Sopenharmony_ci struct isp_video *video = video_drvdata(file); 9328c2ecf20Sopenharmony_ci int ret; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci mutex_lock(&video->queue_lock); 9358c2ecf20Sopenharmony_ci ret = vb2_qbuf(&vfh->queue, video->video.v4l2_dev->mdev, b); 9368c2ecf20Sopenharmony_ci mutex_unlock(&video->queue_lock); 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci return ret; 9398c2ecf20Sopenharmony_ci} 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_cistatic int 9428c2ecf20Sopenharmony_ciisp_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) 9438c2ecf20Sopenharmony_ci{ 9448c2ecf20Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(fh); 9458c2ecf20Sopenharmony_ci struct isp_video *video = video_drvdata(file); 9468c2ecf20Sopenharmony_ci int ret; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci mutex_lock(&video->queue_lock); 9498c2ecf20Sopenharmony_ci ret = vb2_dqbuf(&vfh->queue, b, file->f_flags & O_NONBLOCK); 9508c2ecf20Sopenharmony_ci mutex_unlock(&video->queue_lock); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci return ret; 9538c2ecf20Sopenharmony_ci} 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_cistatic int isp_video_check_external_subdevs(struct isp_video *video, 9568c2ecf20Sopenharmony_ci struct isp_pipeline *pipe) 9578c2ecf20Sopenharmony_ci{ 9588c2ecf20Sopenharmony_ci struct isp_device *isp = video->isp; 9598c2ecf20Sopenharmony_ci struct media_entity *ents[] = { 9608c2ecf20Sopenharmony_ci &isp->isp_csi2a.subdev.entity, 9618c2ecf20Sopenharmony_ci &isp->isp_csi2c.subdev.entity, 9628c2ecf20Sopenharmony_ci &isp->isp_ccp2.subdev.entity, 9638c2ecf20Sopenharmony_ci &isp->isp_ccdc.subdev.entity 9648c2ecf20Sopenharmony_ci }; 9658c2ecf20Sopenharmony_ci struct media_pad *source_pad; 9668c2ecf20Sopenharmony_ci struct media_entity *source = NULL; 9678c2ecf20Sopenharmony_ci struct media_entity *sink; 9688c2ecf20Sopenharmony_ci struct v4l2_subdev_format fmt; 9698c2ecf20Sopenharmony_ci struct v4l2_ext_controls ctrls; 9708c2ecf20Sopenharmony_ci struct v4l2_ext_control ctrl; 9718c2ecf20Sopenharmony_ci unsigned int i; 9728c2ecf20Sopenharmony_ci int ret; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci /* Memory-to-memory pipelines have no external subdev. */ 9758c2ecf20Sopenharmony_ci if (pipe->input != NULL) 9768c2ecf20Sopenharmony_ci return 0; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ents); i++) { 9798c2ecf20Sopenharmony_ci /* Is the entity part of the pipeline? */ 9808c2ecf20Sopenharmony_ci if (!media_entity_enum_test(&pipe->ent_enum, ents[i])) 9818c2ecf20Sopenharmony_ci continue; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci /* ISP entities have always sink pad == 0. Find source. */ 9848c2ecf20Sopenharmony_ci source_pad = media_entity_remote_pad(&ents[i]->pads[0]); 9858c2ecf20Sopenharmony_ci if (source_pad == NULL) 9868c2ecf20Sopenharmony_ci continue; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci source = source_pad->entity; 9898c2ecf20Sopenharmony_ci sink = ents[i]; 9908c2ecf20Sopenharmony_ci break; 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci if (!source) { 9948c2ecf20Sopenharmony_ci dev_warn(isp->dev, "can't find source, failing now\n"); 9958c2ecf20Sopenharmony_ci return -EINVAL; 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci if (!is_media_entity_v4l2_subdev(source)) 9998c2ecf20Sopenharmony_ci return 0; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci pipe->external = media_entity_to_v4l2_subdev(source); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci fmt.pad = source_pad->index; 10048c2ecf20Sopenharmony_ci fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; 10058c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(media_entity_to_v4l2_subdev(sink), 10068c2ecf20Sopenharmony_ci pad, get_fmt, NULL, &fmt); 10078c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) { 10088c2ecf20Sopenharmony_ci dev_warn(isp->dev, "get_fmt returned null!\n"); 10098c2ecf20Sopenharmony_ci return ret; 10108c2ecf20Sopenharmony_ci } 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci pipe->external_width = 10138c2ecf20Sopenharmony_ci omap3isp_video_format_info(fmt.format.code)->width; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci memset(&ctrls, 0, sizeof(ctrls)); 10168c2ecf20Sopenharmony_ci memset(&ctrl, 0, sizeof(ctrl)); 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci ctrl.id = V4L2_CID_PIXEL_RATE; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci ctrls.count = 1; 10218c2ecf20Sopenharmony_ci ctrls.controls = &ctrl; 10228c2ecf20Sopenharmony_ci ret = v4l2_g_ext_ctrls(pipe->external->ctrl_handler, &video->video, 10238c2ecf20Sopenharmony_ci NULL, &ctrls); 10248c2ecf20Sopenharmony_ci if (ret < 0) { 10258c2ecf20Sopenharmony_ci dev_warn(isp->dev, "no pixel rate control in subdev %s\n", 10268c2ecf20Sopenharmony_ci pipe->external->name); 10278c2ecf20Sopenharmony_ci return ret; 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci pipe->external_rate = ctrl.value64; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci if (media_entity_enum_test(&pipe->ent_enum, 10338c2ecf20Sopenharmony_ci &isp->isp_ccdc.subdev.entity)) { 10348c2ecf20Sopenharmony_ci unsigned int rate = UINT_MAX; 10358c2ecf20Sopenharmony_ci /* 10368c2ecf20Sopenharmony_ci * Check that maximum allowed CCDC pixel rate isn't 10378c2ecf20Sopenharmony_ci * exceeded by the pixel rate. 10388c2ecf20Sopenharmony_ci */ 10398c2ecf20Sopenharmony_ci omap3isp_ccdc_max_rate(&isp->isp_ccdc, &rate); 10408c2ecf20Sopenharmony_ci if (pipe->external_rate > rate) 10418c2ecf20Sopenharmony_ci return -ENOSPC; 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci return 0; 10458c2ecf20Sopenharmony_ci} 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci/* 10488c2ecf20Sopenharmony_ci * Stream management 10498c2ecf20Sopenharmony_ci * 10508c2ecf20Sopenharmony_ci * Every ISP pipeline has a single input and a single output. The input can be 10518c2ecf20Sopenharmony_ci * either a sensor or a video node. The output is always a video node. 10528c2ecf20Sopenharmony_ci * 10538c2ecf20Sopenharmony_ci * As every pipeline has an output video node, the ISP video objects at the 10548c2ecf20Sopenharmony_ci * pipeline output stores the pipeline state. It tracks the streaming state of 10558c2ecf20Sopenharmony_ci * both the input and output, as well as the availability of buffers. 10568c2ecf20Sopenharmony_ci * 10578c2ecf20Sopenharmony_ci * In sensor-to-memory mode, frames are always available at the pipeline input. 10588c2ecf20Sopenharmony_ci * Starting the sensor usually requires I2C transfers and must be done in 10598c2ecf20Sopenharmony_ci * interruptible context. The pipeline is started and stopped synchronously 10608c2ecf20Sopenharmony_ci * to the stream on/off commands. All modules in the pipeline will get their 10618c2ecf20Sopenharmony_ci * subdev set stream handler called. The module at the end of the pipeline must 10628c2ecf20Sopenharmony_ci * delay starting the hardware until buffers are available at its output. 10638c2ecf20Sopenharmony_ci * 10648c2ecf20Sopenharmony_ci * In memory-to-memory mode, starting/stopping the stream requires 10658c2ecf20Sopenharmony_ci * synchronization between the input and output. ISP modules can't be stopped 10668c2ecf20Sopenharmony_ci * in the middle of a frame, and at least some of the modules seem to become 10678c2ecf20Sopenharmony_ci * busy as soon as they're started, even if they don't receive a frame start 10688c2ecf20Sopenharmony_ci * event. For that reason frames need to be processed in single-shot mode. The 10698c2ecf20Sopenharmony_ci * driver needs to wait until a frame is completely processed and written to 10708c2ecf20Sopenharmony_ci * memory before restarting the pipeline for the next frame. Pipelined 10718c2ecf20Sopenharmony_ci * processing might be possible but requires more testing. 10728c2ecf20Sopenharmony_ci * 10738c2ecf20Sopenharmony_ci * Stream start must be delayed until buffers are available at both the input 10748c2ecf20Sopenharmony_ci * and output. The pipeline must be started in the videobuf queue callback with 10758c2ecf20Sopenharmony_ci * the buffers queue spinlock held. The modules subdev set stream operation must 10768c2ecf20Sopenharmony_ci * not sleep. 10778c2ecf20Sopenharmony_ci */ 10788c2ecf20Sopenharmony_cistatic int 10798c2ecf20Sopenharmony_ciisp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) 10808c2ecf20Sopenharmony_ci{ 10818c2ecf20Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(fh); 10828c2ecf20Sopenharmony_ci struct isp_video *video = video_drvdata(file); 10838c2ecf20Sopenharmony_ci enum isp_pipeline_state state; 10848c2ecf20Sopenharmony_ci struct isp_pipeline *pipe; 10858c2ecf20Sopenharmony_ci unsigned long flags; 10868c2ecf20Sopenharmony_ci int ret; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci if (type != video->type) 10898c2ecf20Sopenharmony_ci return -EINVAL; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci mutex_lock(&video->stream_lock); 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci /* Start streaming on the pipeline. No link touching an entity in the 10948c2ecf20Sopenharmony_ci * pipeline can be activated or deactivated once streaming is started. 10958c2ecf20Sopenharmony_ci */ 10968c2ecf20Sopenharmony_ci pipe = video->video.entity.pipe 10978c2ecf20Sopenharmony_ci ? to_isp_pipeline(&video->video.entity) : &video->pipe; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci ret = media_entity_enum_init(&pipe->ent_enum, &video->isp->media_dev); 11008c2ecf20Sopenharmony_ci if (ret) 11018c2ecf20Sopenharmony_ci goto err_enum_init; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci /* TODO: Implement PM QoS */ 11048c2ecf20Sopenharmony_ci pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); 11058c2ecf20Sopenharmony_ci pipe->max_rate = pipe->l3_ick; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci ret = media_pipeline_start(&video->video.entity, &pipe->pipe); 11088c2ecf20Sopenharmony_ci if (ret < 0) 11098c2ecf20Sopenharmony_ci goto err_pipeline_start; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci /* Verify that the currently configured format matches the output of 11128c2ecf20Sopenharmony_ci * the connected subdev. 11138c2ecf20Sopenharmony_ci */ 11148c2ecf20Sopenharmony_ci ret = isp_video_check_format(video, vfh); 11158c2ecf20Sopenharmony_ci if (ret < 0) 11168c2ecf20Sopenharmony_ci goto err_check_format; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci video->bpl_padding = ret; 11198c2ecf20Sopenharmony_ci video->bpl_value = vfh->format.fmt.pix.bytesperline; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci ret = isp_video_get_graph_data(video, pipe); 11228c2ecf20Sopenharmony_ci if (ret < 0) 11238c2ecf20Sopenharmony_ci goto err_check_format; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 11268c2ecf20Sopenharmony_ci state = ISP_PIPELINE_STREAM_OUTPUT | ISP_PIPELINE_IDLE_OUTPUT; 11278c2ecf20Sopenharmony_ci else 11288c2ecf20Sopenharmony_ci state = ISP_PIPELINE_STREAM_INPUT | ISP_PIPELINE_IDLE_INPUT; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci ret = isp_video_check_external_subdevs(video, pipe); 11318c2ecf20Sopenharmony_ci if (ret < 0) 11328c2ecf20Sopenharmony_ci goto err_check_format; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci pipe->error = false; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci spin_lock_irqsave(&pipe->lock, flags); 11378c2ecf20Sopenharmony_ci pipe->state &= ~ISP_PIPELINE_STREAM; 11388c2ecf20Sopenharmony_ci pipe->state |= state; 11398c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pipe->lock, flags); 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci /* Set the maximum time per frame as the value requested by userspace. 11428c2ecf20Sopenharmony_ci * This is a soft limit that can be overridden if the hardware doesn't 11438c2ecf20Sopenharmony_ci * support the request limit. 11448c2ecf20Sopenharmony_ci */ 11458c2ecf20Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 11468c2ecf20Sopenharmony_ci pipe->max_timeperframe = vfh->timeperframe; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci video->queue = &vfh->queue; 11498c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&video->dmaqueue); 11508c2ecf20Sopenharmony_ci atomic_set(&pipe->frame_number, -1); 11518c2ecf20Sopenharmony_ci pipe->field = vfh->format.fmt.pix.field; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci mutex_lock(&video->queue_lock); 11548c2ecf20Sopenharmony_ci ret = vb2_streamon(&vfh->queue, type); 11558c2ecf20Sopenharmony_ci mutex_unlock(&video->queue_lock); 11568c2ecf20Sopenharmony_ci if (ret < 0) 11578c2ecf20Sopenharmony_ci goto err_check_format; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci mutex_unlock(&video->stream_lock); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci return 0; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_cierr_check_format: 11648c2ecf20Sopenharmony_ci media_pipeline_stop(&video->video.entity); 11658c2ecf20Sopenharmony_cierr_pipeline_start: 11668c2ecf20Sopenharmony_ci /* TODO: Implement PM QoS */ 11678c2ecf20Sopenharmony_ci /* The DMA queue must be emptied here, otherwise CCDC interrupts that 11688c2ecf20Sopenharmony_ci * will get triggered the next time the CCDC is powered up will try to 11698c2ecf20Sopenharmony_ci * access buffers that might have been freed but still present in the 11708c2ecf20Sopenharmony_ci * DMA queue. This can easily get triggered if the above 11718c2ecf20Sopenharmony_ci * omap3isp_pipeline_set_stream() call fails on a system with a 11728c2ecf20Sopenharmony_ci * free-running sensor. 11738c2ecf20Sopenharmony_ci */ 11748c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&video->dmaqueue); 11758c2ecf20Sopenharmony_ci video->queue = NULL; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci media_entity_enum_cleanup(&pipe->ent_enum); 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_cierr_enum_init: 11808c2ecf20Sopenharmony_ci mutex_unlock(&video->stream_lock); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci return ret; 11838c2ecf20Sopenharmony_ci} 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_cistatic int 11868c2ecf20Sopenharmony_ciisp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) 11878c2ecf20Sopenharmony_ci{ 11888c2ecf20Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(fh); 11898c2ecf20Sopenharmony_ci struct isp_video *video = video_drvdata(file); 11908c2ecf20Sopenharmony_ci struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); 11918c2ecf20Sopenharmony_ci enum isp_pipeline_state state; 11928c2ecf20Sopenharmony_ci unsigned int streaming; 11938c2ecf20Sopenharmony_ci unsigned long flags; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci if (type != video->type) 11968c2ecf20Sopenharmony_ci return -EINVAL; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci mutex_lock(&video->stream_lock); 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci /* Make sure we're not streaming yet. */ 12018c2ecf20Sopenharmony_ci mutex_lock(&video->queue_lock); 12028c2ecf20Sopenharmony_ci streaming = vb2_is_streaming(&vfh->queue); 12038c2ecf20Sopenharmony_ci mutex_unlock(&video->queue_lock); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci if (!streaming) 12068c2ecf20Sopenharmony_ci goto done; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci /* Update the pipeline state. */ 12098c2ecf20Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 12108c2ecf20Sopenharmony_ci state = ISP_PIPELINE_STREAM_OUTPUT 12118c2ecf20Sopenharmony_ci | ISP_PIPELINE_QUEUE_OUTPUT; 12128c2ecf20Sopenharmony_ci else 12138c2ecf20Sopenharmony_ci state = ISP_PIPELINE_STREAM_INPUT 12148c2ecf20Sopenharmony_ci | ISP_PIPELINE_QUEUE_INPUT; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci spin_lock_irqsave(&pipe->lock, flags); 12178c2ecf20Sopenharmony_ci pipe->state &= ~state; 12188c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pipe->lock, flags); 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci /* Stop the stream. */ 12218c2ecf20Sopenharmony_ci omap3isp_pipeline_set_stream(pipe, ISP_PIPELINE_STREAM_STOPPED); 12228c2ecf20Sopenharmony_ci omap3isp_video_cancel_stream(video); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci mutex_lock(&video->queue_lock); 12258c2ecf20Sopenharmony_ci vb2_streamoff(&vfh->queue, type); 12268c2ecf20Sopenharmony_ci mutex_unlock(&video->queue_lock); 12278c2ecf20Sopenharmony_ci video->queue = NULL; 12288c2ecf20Sopenharmony_ci video->error = false; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci /* TODO: Implement PM QoS */ 12318c2ecf20Sopenharmony_ci media_pipeline_stop(&video->video.entity); 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci media_entity_enum_cleanup(&pipe->ent_enum); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_cidone: 12368c2ecf20Sopenharmony_ci mutex_unlock(&video->stream_lock); 12378c2ecf20Sopenharmony_ci return 0; 12388c2ecf20Sopenharmony_ci} 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_cistatic int 12418c2ecf20Sopenharmony_ciisp_video_enum_input(struct file *file, void *fh, struct v4l2_input *input) 12428c2ecf20Sopenharmony_ci{ 12438c2ecf20Sopenharmony_ci if (input->index > 0) 12448c2ecf20Sopenharmony_ci return -EINVAL; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci strscpy(input->name, "camera", sizeof(input->name)); 12478c2ecf20Sopenharmony_ci input->type = V4L2_INPUT_TYPE_CAMERA; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci return 0; 12508c2ecf20Sopenharmony_ci} 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_cistatic int 12538c2ecf20Sopenharmony_ciisp_video_g_input(struct file *file, void *fh, unsigned int *input) 12548c2ecf20Sopenharmony_ci{ 12558c2ecf20Sopenharmony_ci *input = 0; 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci return 0; 12588c2ecf20Sopenharmony_ci} 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_cistatic int 12618c2ecf20Sopenharmony_ciisp_video_s_input(struct file *file, void *fh, unsigned int input) 12628c2ecf20Sopenharmony_ci{ 12638c2ecf20Sopenharmony_ci return input == 0 ? 0 : -EINVAL; 12648c2ecf20Sopenharmony_ci} 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops isp_video_ioctl_ops = { 12678c2ecf20Sopenharmony_ci .vidioc_querycap = isp_video_querycap, 12688c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_cap = isp_video_get_format, 12698c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_cap = isp_video_set_format, 12708c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_cap = isp_video_try_format, 12718c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_out = isp_video_get_format, 12728c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_out = isp_video_set_format, 12738c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_out = isp_video_try_format, 12748c2ecf20Sopenharmony_ci .vidioc_g_selection = isp_video_get_selection, 12758c2ecf20Sopenharmony_ci .vidioc_s_selection = isp_video_set_selection, 12768c2ecf20Sopenharmony_ci .vidioc_g_parm = isp_video_get_param, 12778c2ecf20Sopenharmony_ci .vidioc_s_parm = isp_video_set_param, 12788c2ecf20Sopenharmony_ci .vidioc_reqbufs = isp_video_reqbufs, 12798c2ecf20Sopenharmony_ci .vidioc_querybuf = isp_video_querybuf, 12808c2ecf20Sopenharmony_ci .vidioc_qbuf = isp_video_qbuf, 12818c2ecf20Sopenharmony_ci .vidioc_dqbuf = isp_video_dqbuf, 12828c2ecf20Sopenharmony_ci .vidioc_streamon = isp_video_streamon, 12838c2ecf20Sopenharmony_ci .vidioc_streamoff = isp_video_streamoff, 12848c2ecf20Sopenharmony_ci .vidioc_enum_input = isp_video_enum_input, 12858c2ecf20Sopenharmony_ci .vidioc_g_input = isp_video_g_input, 12868c2ecf20Sopenharmony_ci .vidioc_s_input = isp_video_s_input, 12878c2ecf20Sopenharmony_ci}; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 12908c2ecf20Sopenharmony_ci * V4L2 file operations 12918c2ecf20Sopenharmony_ci */ 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_cistatic int isp_video_open(struct file *file) 12948c2ecf20Sopenharmony_ci{ 12958c2ecf20Sopenharmony_ci struct isp_video *video = video_drvdata(file); 12968c2ecf20Sopenharmony_ci struct isp_video_fh *handle; 12978c2ecf20Sopenharmony_ci struct vb2_queue *queue; 12988c2ecf20Sopenharmony_ci int ret = 0; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci handle = kzalloc(sizeof(*handle), GFP_KERNEL); 13018c2ecf20Sopenharmony_ci if (handle == NULL) 13028c2ecf20Sopenharmony_ci return -ENOMEM; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci v4l2_fh_init(&handle->vfh, &video->video); 13058c2ecf20Sopenharmony_ci v4l2_fh_add(&handle->vfh); 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci /* If this is the first user, initialise the pipeline. */ 13088c2ecf20Sopenharmony_ci if (omap3isp_get(video->isp) == NULL) { 13098c2ecf20Sopenharmony_ci ret = -EBUSY; 13108c2ecf20Sopenharmony_ci goto done; 13118c2ecf20Sopenharmony_ci } 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci ret = v4l2_pipeline_pm_get(&video->video.entity); 13148c2ecf20Sopenharmony_ci if (ret < 0) { 13158c2ecf20Sopenharmony_ci omap3isp_put(video->isp); 13168c2ecf20Sopenharmony_ci goto done; 13178c2ecf20Sopenharmony_ci } 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci queue = &handle->queue; 13208c2ecf20Sopenharmony_ci queue->type = video->type; 13218c2ecf20Sopenharmony_ci queue->io_modes = VB2_MMAP | VB2_USERPTR; 13228c2ecf20Sopenharmony_ci queue->drv_priv = handle; 13238c2ecf20Sopenharmony_ci queue->ops = &isp_video_queue_ops; 13248c2ecf20Sopenharmony_ci queue->mem_ops = &vb2_dma_contig_memops; 13258c2ecf20Sopenharmony_ci queue->buf_struct_size = sizeof(struct isp_buffer); 13268c2ecf20Sopenharmony_ci queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 13278c2ecf20Sopenharmony_ci queue->dev = video->isp->dev; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci ret = vb2_queue_init(&handle->queue); 13308c2ecf20Sopenharmony_ci if (ret < 0) { 13318c2ecf20Sopenharmony_ci omap3isp_put(video->isp); 13328c2ecf20Sopenharmony_ci goto done; 13338c2ecf20Sopenharmony_ci } 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci memset(&handle->format, 0, sizeof(handle->format)); 13368c2ecf20Sopenharmony_ci handle->format.type = video->type; 13378c2ecf20Sopenharmony_ci handle->timeperframe.denominator = 1; 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci handle->video = video; 13408c2ecf20Sopenharmony_ci file->private_data = &handle->vfh; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_cidone: 13438c2ecf20Sopenharmony_ci if (ret < 0) { 13448c2ecf20Sopenharmony_ci v4l2_fh_del(&handle->vfh); 13458c2ecf20Sopenharmony_ci v4l2_fh_exit(&handle->vfh); 13468c2ecf20Sopenharmony_ci kfree(handle); 13478c2ecf20Sopenharmony_ci } 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci return ret; 13508c2ecf20Sopenharmony_ci} 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_cistatic int isp_video_release(struct file *file) 13538c2ecf20Sopenharmony_ci{ 13548c2ecf20Sopenharmony_ci struct isp_video *video = video_drvdata(file); 13558c2ecf20Sopenharmony_ci struct v4l2_fh *vfh = file->private_data; 13568c2ecf20Sopenharmony_ci struct isp_video_fh *handle = to_isp_video_fh(vfh); 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci /* Disable streaming and free the buffers queue resources. */ 13598c2ecf20Sopenharmony_ci isp_video_streamoff(file, vfh, video->type); 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci mutex_lock(&video->queue_lock); 13628c2ecf20Sopenharmony_ci vb2_queue_release(&handle->queue); 13638c2ecf20Sopenharmony_ci mutex_unlock(&video->queue_lock); 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci v4l2_pipeline_pm_put(&video->video.entity); 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci /* Release the file handle. */ 13688c2ecf20Sopenharmony_ci v4l2_fh_del(vfh); 13698c2ecf20Sopenharmony_ci v4l2_fh_exit(vfh); 13708c2ecf20Sopenharmony_ci kfree(handle); 13718c2ecf20Sopenharmony_ci file->private_data = NULL; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci omap3isp_put(video->isp); 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci return 0; 13768c2ecf20Sopenharmony_ci} 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_cistatic __poll_t isp_video_poll(struct file *file, poll_table *wait) 13798c2ecf20Sopenharmony_ci{ 13808c2ecf20Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(file->private_data); 13818c2ecf20Sopenharmony_ci struct isp_video *video = video_drvdata(file); 13828c2ecf20Sopenharmony_ci __poll_t ret; 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci mutex_lock(&video->queue_lock); 13858c2ecf20Sopenharmony_ci ret = vb2_poll(&vfh->queue, file, wait); 13868c2ecf20Sopenharmony_ci mutex_unlock(&video->queue_lock); 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci return ret; 13898c2ecf20Sopenharmony_ci} 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_cistatic int isp_video_mmap(struct file *file, struct vm_area_struct *vma) 13928c2ecf20Sopenharmony_ci{ 13938c2ecf20Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(file->private_data); 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci return vb2_mmap(&vfh->queue, vma); 13968c2ecf20Sopenharmony_ci} 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations isp_video_fops = { 13998c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 14008c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 14018c2ecf20Sopenharmony_ci .open = isp_video_open, 14028c2ecf20Sopenharmony_ci .release = isp_video_release, 14038c2ecf20Sopenharmony_ci .poll = isp_video_poll, 14048c2ecf20Sopenharmony_ci .mmap = isp_video_mmap, 14058c2ecf20Sopenharmony_ci}; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 14088c2ecf20Sopenharmony_ci * ISP video core 14098c2ecf20Sopenharmony_ci */ 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_cistatic const struct isp_video_operations isp_video_dummy_ops = { 14128c2ecf20Sopenharmony_ci}; 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ciint omap3isp_video_init(struct isp_video *video, const char *name) 14158c2ecf20Sopenharmony_ci{ 14168c2ecf20Sopenharmony_ci const char *direction; 14178c2ecf20Sopenharmony_ci int ret; 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci switch (video->type) { 14208c2ecf20Sopenharmony_ci case V4L2_BUF_TYPE_VIDEO_CAPTURE: 14218c2ecf20Sopenharmony_ci direction = "output"; 14228c2ecf20Sopenharmony_ci video->pad.flags = MEDIA_PAD_FL_SINK 14238c2ecf20Sopenharmony_ci | MEDIA_PAD_FL_MUST_CONNECT; 14248c2ecf20Sopenharmony_ci break; 14258c2ecf20Sopenharmony_ci case V4L2_BUF_TYPE_VIDEO_OUTPUT: 14268c2ecf20Sopenharmony_ci direction = "input"; 14278c2ecf20Sopenharmony_ci video->pad.flags = MEDIA_PAD_FL_SOURCE 14288c2ecf20Sopenharmony_ci | MEDIA_PAD_FL_MUST_CONNECT; 14298c2ecf20Sopenharmony_ci video->video.vfl_dir = VFL_DIR_TX; 14308c2ecf20Sopenharmony_ci break; 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci default: 14338c2ecf20Sopenharmony_ci return -EINVAL; 14348c2ecf20Sopenharmony_ci } 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci ret = media_entity_pads_init(&video->video.entity, 1, &video->pad); 14378c2ecf20Sopenharmony_ci if (ret < 0) 14388c2ecf20Sopenharmony_ci return ret; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci mutex_init(&video->mutex); 14418c2ecf20Sopenharmony_ci atomic_set(&video->active, 0); 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci spin_lock_init(&video->pipe.lock); 14448c2ecf20Sopenharmony_ci mutex_init(&video->stream_lock); 14458c2ecf20Sopenharmony_ci mutex_init(&video->queue_lock); 14468c2ecf20Sopenharmony_ci spin_lock_init(&video->irqlock); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci /* Initialize the video device. */ 14498c2ecf20Sopenharmony_ci if (video->ops == NULL) 14508c2ecf20Sopenharmony_ci video->ops = &isp_video_dummy_ops; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci video->video.fops = &isp_video_fops; 14538c2ecf20Sopenharmony_ci snprintf(video->video.name, sizeof(video->video.name), 14548c2ecf20Sopenharmony_ci "OMAP3 ISP %s %s", name, direction); 14558c2ecf20Sopenharmony_ci video->video.vfl_type = VFL_TYPE_VIDEO; 14568c2ecf20Sopenharmony_ci video->video.release = video_device_release_empty; 14578c2ecf20Sopenharmony_ci video->video.ioctl_ops = &isp_video_ioctl_ops; 14588c2ecf20Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 14598c2ecf20Sopenharmony_ci video->video.device_caps = V4L2_CAP_VIDEO_CAPTURE 14608c2ecf20Sopenharmony_ci | V4L2_CAP_STREAMING; 14618c2ecf20Sopenharmony_ci else 14628c2ecf20Sopenharmony_ci video->video.device_caps = V4L2_CAP_VIDEO_OUTPUT 14638c2ecf20Sopenharmony_ci | V4L2_CAP_STREAMING; 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci video->pipe.stream_state = ISP_PIPELINE_STREAM_STOPPED; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci video_set_drvdata(&video->video, video); 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci return 0; 14708c2ecf20Sopenharmony_ci} 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_civoid omap3isp_video_cleanup(struct isp_video *video) 14738c2ecf20Sopenharmony_ci{ 14748c2ecf20Sopenharmony_ci media_entity_cleanup(&video->video.entity); 14758c2ecf20Sopenharmony_ci mutex_destroy(&video->queue_lock); 14768c2ecf20Sopenharmony_ci mutex_destroy(&video->stream_lock); 14778c2ecf20Sopenharmony_ci mutex_destroy(&video->mutex); 14788c2ecf20Sopenharmony_ci} 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ciint omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev) 14818c2ecf20Sopenharmony_ci{ 14828c2ecf20Sopenharmony_ci int ret; 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci video->video.v4l2_dev = vdev; 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci ret = video_register_device(&video->video, VFL_TYPE_VIDEO, -1); 14878c2ecf20Sopenharmony_ci if (ret < 0) 14888c2ecf20Sopenharmony_ci dev_err(video->isp->dev, 14898c2ecf20Sopenharmony_ci "%s: could not register video device (%d)\n", 14908c2ecf20Sopenharmony_ci __func__, ret); 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci return ret; 14938c2ecf20Sopenharmony_ci} 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_civoid omap3isp_video_unregister(struct isp_video *video) 14968c2ecf20Sopenharmony_ci{ 14978c2ecf20Sopenharmony_ci video_unregister_device(&video->video); 14988c2ecf20Sopenharmony_ci} 1499