162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ispvideo.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * TI OMAP3 ISP - Generic video node 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2009-2010 Nokia Corporation 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> 1062306a36Sopenharmony_ci * Sakari Ailus <sakari.ailus@iki.fi> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/clk.h> 1462306a36Sopenharmony_ci#include <linux/mm.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/pagemap.h> 1762306a36Sopenharmony_ci#include <linux/scatterlist.h> 1862306a36Sopenharmony_ci#include <linux/sched.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/vmalloc.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <media/v4l2-dev.h> 2362306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 2462306a36Sopenharmony_ci#include <media/v4l2-mc.h> 2562306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "ispvideo.h" 2862306a36Sopenharmony_ci#include "isp.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 3262306a36Sopenharmony_ci * Helper functions 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * NOTE: When adding new media bus codes, always remember to add 3762306a36Sopenharmony_ci * corresponding in-memory formats to the table below!!! 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_cistatic struct isp_format_info formats[] = { 4062306a36Sopenharmony_ci { MEDIA_BUS_FMT_Y8_1X8, MEDIA_BUS_FMT_Y8_1X8, 4162306a36Sopenharmony_ci MEDIA_BUS_FMT_Y8_1X8, MEDIA_BUS_FMT_Y8_1X8, 4262306a36Sopenharmony_ci V4L2_PIX_FMT_GREY, 8, 1, }, 4362306a36Sopenharmony_ci { MEDIA_BUS_FMT_Y10_1X10, MEDIA_BUS_FMT_Y10_1X10, 4462306a36Sopenharmony_ci MEDIA_BUS_FMT_Y10_1X10, MEDIA_BUS_FMT_Y8_1X8, 4562306a36Sopenharmony_ci V4L2_PIX_FMT_Y10, 10, 2, }, 4662306a36Sopenharmony_ci { MEDIA_BUS_FMT_Y12_1X12, MEDIA_BUS_FMT_Y10_1X10, 4762306a36Sopenharmony_ci MEDIA_BUS_FMT_Y12_1X12, MEDIA_BUS_FMT_Y8_1X8, 4862306a36Sopenharmony_ci V4L2_PIX_FMT_Y12, 12, 2, }, 4962306a36Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR8_1X8, MEDIA_BUS_FMT_SBGGR8_1X8, 5062306a36Sopenharmony_ci MEDIA_BUS_FMT_SBGGR8_1X8, MEDIA_BUS_FMT_SBGGR8_1X8, 5162306a36Sopenharmony_ci V4L2_PIX_FMT_SBGGR8, 8, 1, }, 5262306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG8_1X8, MEDIA_BUS_FMT_SGBRG8_1X8, 5362306a36Sopenharmony_ci MEDIA_BUS_FMT_SGBRG8_1X8, MEDIA_BUS_FMT_SGBRG8_1X8, 5462306a36Sopenharmony_ci V4L2_PIX_FMT_SGBRG8, 8, 1, }, 5562306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG8_1X8, MEDIA_BUS_FMT_SGRBG8_1X8, 5662306a36Sopenharmony_ci MEDIA_BUS_FMT_SGRBG8_1X8, MEDIA_BUS_FMT_SGRBG8_1X8, 5762306a36Sopenharmony_ci V4L2_PIX_FMT_SGRBG8, 8, 1, }, 5862306a36Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB8_1X8, MEDIA_BUS_FMT_SRGGB8_1X8, 5962306a36Sopenharmony_ci MEDIA_BUS_FMT_SRGGB8_1X8, MEDIA_BUS_FMT_SRGGB8_1X8, 6062306a36Sopenharmony_ci V4L2_PIX_FMT_SRGGB8, 8, 1, }, 6162306a36Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, 6262306a36Sopenharmony_ci MEDIA_BUS_FMT_SBGGR10_1X10, 0, 6362306a36Sopenharmony_ci V4L2_PIX_FMT_SBGGR10DPCM8, 8, 1, }, 6462306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, 6562306a36Sopenharmony_ci MEDIA_BUS_FMT_SGBRG10_1X10, 0, 6662306a36Sopenharmony_ci V4L2_PIX_FMT_SGBRG10DPCM8, 8, 1, }, 6762306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, 6862306a36Sopenharmony_ci MEDIA_BUS_FMT_SGRBG10_1X10, 0, 6962306a36Sopenharmony_ci V4L2_PIX_FMT_SGRBG10DPCM8, 8, 1, }, 7062306a36Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, 7162306a36Sopenharmony_ci MEDIA_BUS_FMT_SRGGB10_1X10, 0, 7262306a36Sopenharmony_ci V4L2_PIX_FMT_SRGGB10DPCM8, 8, 1, }, 7362306a36Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SBGGR10_1X10, 7462306a36Sopenharmony_ci MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SBGGR8_1X8, 7562306a36Sopenharmony_ci V4L2_PIX_FMT_SBGGR10, 10, 2, }, 7662306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10, 7762306a36Sopenharmony_ci MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SGBRG8_1X8, 7862306a36Sopenharmony_ci V4L2_PIX_FMT_SGBRG10, 10, 2, }, 7962306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10, 8062306a36Sopenharmony_ci MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SGRBG8_1X8, 8162306a36Sopenharmony_ci V4L2_PIX_FMT_SGRBG10, 10, 2, }, 8262306a36Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10, 8362306a36Sopenharmony_ci MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SRGGB8_1X8, 8462306a36Sopenharmony_ci V4L2_PIX_FMT_SRGGB10, 10, 2, }, 8562306a36Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SBGGR10_1X10, 8662306a36Sopenharmony_ci MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SBGGR8_1X8, 8762306a36Sopenharmony_ci V4L2_PIX_FMT_SBGGR12, 12, 2, }, 8862306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG12_1X12, MEDIA_BUS_FMT_SGBRG10_1X10, 8962306a36Sopenharmony_ci MEDIA_BUS_FMT_SGBRG12_1X12, MEDIA_BUS_FMT_SGBRG8_1X8, 9062306a36Sopenharmony_ci V4L2_PIX_FMT_SGBRG12, 12, 2, }, 9162306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SGRBG10_1X10, 9262306a36Sopenharmony_ci MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SGRBG8_1X8, 9362306a36Sopenharmony_ci V4L2_PIX_FMT_SGRBG12, 12, 2, }, 9462306a36Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB12_1X12, MEDIA_BUS_FMT_SRGGB10_1X10, 9562306a36Sopenharmony_ci MEDIA_BUS_FMT_SRGGB12_1X12, MEDIA_BUS_FMT_SRGGB8_1X8, 9662306a36Sopenharmony_ci V4L2_PIX_FMT_SRGGB12, 12, 2, }, 9762306a36Sopenharmony_ci { MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_UYVY8_1X16, 9862306a36Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_1X16, 0, 9962306a36Sopenharmony_ci V4L2_PIX_FMT_UYVY, 16, 2, }, 10062306a36Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_1X16, MEDIA_BUS_FMT_YUYV8_1X16, 10162306a36Sopenharmony_ci MEDIA_BUS_FMT_YUYV8_1X16, 0, 10262306a36Sopenharmony_ci V4L2_PIX_FMT_YUYV, 16, 2, }, 10362306a36Sopenharmony_ci { MEDIA_BUS_FMT_UYVY8_2X8, MEDIA_BUS_FMT_UYVY8_2X8, 10462306a36Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_2X8, 0, 10562306a36Sopenharmony_ci V4L2_PIX_FMT_UYVY, 8, 2, }, 10662306a36Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_2X8, MEDIA_BUS_FMT_YUYV8_2X8, 10762306a36Sopenharmony_ci MEDIA_BUS_FMT_YUYV8_2X8, 0, 10862306a36Sopenharmony_ci V4L2_PIX_FMT_YUYV, 8, 2, }, 10962306a36Sopenharmony_ci /* Empty entry to catch the unsupported pixel code (0) used by the CCDC 11062306a36Sopenharmony_ci * module and avoid NULL pointer dereferences. 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ci { 0, } 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ciconst struct isp_format_info *omap3isp_video_format_info(u32 code) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci unsigned int i; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(formats); ++i) { 12062306a36Sopenharmony_ci if (formats[i].code == code) 12162306a36Sopenharmony_ci return &formats[i]; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return NULL; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/* 12862306a36Sopenharmony_ci * isp_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format 12962306a36Sopenharmony_ci * @video: ISP video instance 13062306a36Sopenharmony_ci * @mbus: v4l2_mbus_framefmt format (input) 13162306a36Sopenharmony_ci * @pix: v4l2_pix_format format (output) 13262306a36Sopenharmony_ci * 13362306a36Sopenharmony_ci * Fill the output pix structure with information from the input mbus format. 13462306a36Sopenharmony_ci * The bytesperline and sizeimage fields are computed from the requested bytes 13562306a36Sopenharmony_ci * per line value in the pix format and information from the video instance. 13662306a36Sopenharmony_ci * 13762306a36Sopenharmony_ci * Return the number of padding bytes at end of line. 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_cistatic unsigned int isp_video_mbus_to_pix(const struct isp_video *video, 14062306a36Sopenharmony_ci const struct v4l2_mbus_framefmt *mbus, 14162306a36Sopenharmony_ci struct v4l2_pix_format *pix) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci unsigned int bpl = pix->bytesperline; 14462306a36Sopenharmony_ci unsigned int min_bpl; 14562306a36Sopenharmony_ci unsigned int i; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci memset(pix, 0, sizeof(*pix)); 14862306a36Sopenharmony_ci pix->width = mbus->width; 14962306a36Sopenharmony_ci pix->height = mbus->height; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(formats); ++i) { 15262306a36Sopenharmony_ci if (formats[i].code == mbus->code) 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (WARN_ON(i == ARRAY_SIZE(formats))) 15762306a36Sopenharmony_ci return 0; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci min_bpl = pix->width * formats[i].bpp; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* Clamp the requested bytes per line value. If the maximum bytes per 16262306a36Sopenharmony_ci * line value is zero, the module doesn't support user configurable line 16362306a36Sopenharmony_ci * sizes. Override the requested value with the minimum in that case. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci if (video->bpl_max) 16662306a36Sopenharmony_ci bpl = clamp(bpl, min_bpl, video->bpl_max); 16762306a36Sopenharmony_ci else 16862306a36Sopenharmony_ci bpl = min_bpl; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (!video->bpl_zero_padding || bpl != min_bpl) 17162306a36Sopenharmony_ci bpl = ALIGN(bpl, video->bpl_alignment); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci pix->pixelformat = formats[i].pixelformat; 17462306a36Sopenharmony_ci pix->bytesperline = bpl; 17562306a36Sopenharmony_ci pix->sizeimage = pix->bytesperline * pix->height; 17662306a36Sopenharmony_ci pix->colorspace = mbus->colorspace; 17762306a36Sopenharmony_ci pix->field = mbus->field; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return bpl - min_bpl; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic void isp_video_pix_to_mbus(const struct v4l2_pix_format *pix, 18362306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mbus) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci unsigned int i; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci memset(mbus, 0, sizeof(*mbus)); 18862306a36Sopenharmony_ci mbus->width = pix->width; 18962306a36Sopenharmony_ci mbus->height = pix->height; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* Skip the last format in the loop so that it will be selected if no 19262306a36Sopenharmony_ci * match is found. 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(formats) - 1; ++i) { 19562306a36Sopenharmony_ci if (formats[i].pixelformat == pix->pixelformat) 19662306a36Sopenharmony_ci break; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci mbus->code = formats[i].code; 20062306a36Sopenharmony_ci mbus->colorspace = pix->colorspace; 20162306a36Sopenharmony_ci mbus->field = pix->field; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic struct v4l2_subdev * 20562306a36Sopenharmony_ciisp_video_remote_subdev(struct isp_video *video, u32 *pad) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct media_pad *remote; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci remote = media_pad_remote_pad_first(&video->pad); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) 21262306a36Sopenharmony_ci return NULL; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (pad) 21562306a36Sopenharmony_ci *pad = remote->index; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return media_entity_to_v4l2_subdev(remote->entity); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci/* Return a pointer to the ISP video instance at the far end of the pipeline. */ 22162306a36Sopenharmony_cistatic int isp_video_get_graph_data(struct isp_video *video, 22262306a36Sopenharmony_ci struct isp_pipeline *pipe) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct media_pipeline_entity_iter iter; 22562306a36Sopenharmony_ci struct media_entity *entity; 22662306a36Sopenharmony_ci struct isp_video *far_end = NULL; 22762306a36Sopenharmony_ci int ret; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci ret = media_pipeline_entity_iter_init(&pipe->pipe, &iter); 23062306a36Sopenharmony_ci if (ret) 23162306a36Sopenharmony_ci return ret; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci media_pipeline_for_each_entity(&pipe->pipe, &iter, entity) { 23462306a36Sopenharmony_ci struct isp_video *__video; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci media_entity_enum_set(&pipe->ent_enum, entity); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (far_end != NULL) 23962306a36Sopenharmony_ci continue; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (entity == &video->video.entity) 24262306a36Sopenharmony_ci continue; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (!is_media_entity_v4l2_video_device(entity)) 24562306a36Sopenharmony_ci continue; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci __video = to_isp_video(media_entity_to_video_device(entity)); 24862306a36Sopenharmony_ci if (__video->type != video->type) 24962306a36Sopenharmony_ci far_end = __video; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci media_pipeline_entity_iter_cleanup(&iter); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { 25562306a36Sopenharmony_ci pipe->input = far_end; 25662306a36Sopenharmony_ci pipe->output = video; 25762306a36Sopenharmony_ci } else { 25862306a36Sopenharmony_ci if (far_end == NULL) 25962306a36Sopenharmony_ci return -EPIPE; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci pipe->input = video; 26262306a36Sopenharmony_ci pipe->output = far_end; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic int 26962306a36Sopenharmony_ci__isp_video_get_format(struct isp_video *video, struct v4l2_format *format) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct v4l2_subdev_format fmt = { 27262306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 27362306a36Sopenharmony_ci }; 27462306a36Sopenharmony_ci struct v4l2_subdev *subdev; 27562306a36Sopenharmony_ci u32 pad; 27662306a36Sopenharmony_ci int ret; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci subdev = isp_video_remote_subdev(video, &pad); 27962306a36Sopenharmony_ci if (subdev == NULL) 28062306a36Sopenharmony_ci return -EINVAL; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci fmt.pad = pad; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci mutex_lock(&video->mutex); 28562306a36Sopenharmony_ci ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); 28662306a36Sopenharmony_ci mutex_unlock(&video->mutex); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (ret) 28962306a36Sopenharmony_ci return ret; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci format->type = video->type; 29262306a36Sopenharmony_ci return isp_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix); 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic int 29662306a36Sopenharmony_ciisp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct v4l2_format format; 29962306a36Sopenharmony_ci int ret; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci memcpy(&format, &vfh->format, sizeof(format)); 30262306a36Sopenharmony_ci ret = __isp_video_get_format(video, &format); 30362306a36Sopenharmony_ci if (ret < 0) 30462306a36Sopenharmony_ci return ret; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (vfh->format.fmt.pix.pixelformat != format.fmt.pix.pixelformat || 30762306a36Sopenharmony_ci vfh->format.fmt.pix.height != format.fmt.pix.height || 30862306a36Sopenharmony_ci vfh->format.fmt.pix.width != format.fmt.pix.width || 30962306a36Sopenharmony_ci vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline || 31062306a36Sopenharmony_ci vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage || 31162306a36Sopenharmony_ci vfh->format.fmt.pix.field != format.fmt.pix.field) 31262306a36Sopenharmony_ci return -EINVAL; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return 0; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 31862306a36Sopenharmony_ci * Video queue operations 31962306a36Sopenharmony_ci */ 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic int isp_video_queue_setup(struct vb2_queue *queue, 32262306a36Sopenharmony_ci unsigned int *count, unsigned int *num_planes, 32362306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct isp_video_fh *vfh = vb2_get_drv_priv(queue); 32662306a36Sopenharmony_ci struct isp_video *video = vfh->video; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci *num_planes = 1; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci sizes[0] = vfh->format.fmt.pix.sizeimage; 33162306a36Sopenharmony_ci if (sizes[0] == 0) 33262306a36Sopenharmony_ci return -EINVAL; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci *count = min(*count, video->capture_mem / PAGE_ALIGN(sizes[0])); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci return 0; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic int isp_video_buffer_prepare(struct vb2_buffer *buf) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(buf); 34262306a36Sopenharmony_ci struct isp_video_fh *vfh = vb2_get_drv_priv(buf->vb2_queue); 34362306a36Sopenharmony_ci struct isp_buffer *buffer = to_isp_buffer(vbuf); 34462306a36Sopenharmony_ci struct isp_video *video = vfh->video; 34562306a36Sopenharmony_ci dma_addr_t addr; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* Refuse to prepare the buffer is the video node has registered an 34862306a36Sopenharmony_ci * error. We don't need to take any lock here as the operation is 34962306a36Sopenharmony_ci * inherently racy. The authoritative check will be performed in the 35062306a36Sopenharmony_ci * queue handler, which can't return an error, this check is just a best 35162306a36Sopenharmony_ci * effort to notify userspace as early as possible. 35262306a36Sopenharmony_ci */ 35362306a36Sopenharmony_ci if (unlikely(video->error)) 35462306a36Sopenharmony_ci return -EIO; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci addr = vb2_dma_contig_plane_dma_addr(buf, 0); 35762306a36Sopenharmony_ci if (!IS_ALIGNED(addr, 32)) { 35862306a36Sopenharmony_ci dev_dbg(video->isp->dev, 35962306a36Sopenharmony_ci "Buffer address must be aligned to 32 bytes boundary.\n"); 36062306a36Sopenharmony_ci return -EINVAL; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci vb2_set_plane_payload(&buffer->vb.vb2_buf, 0, 36462306a36Sopenharmony_ci vfh->format.fmt.pix.sizeimage); 36562306a36Sopenharmony_ci buffer->dma = addr; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci/* 37162306a36Sopenharmony_ci * isp_video_buffer_queue - Add buffer to streaming queue 37262306a36Sopenharmony_ci * @buf: Video buffer 37362306a36Sopenharmony_ci * 37462306a36Sopenharmony_ci * In memory-to-memory mode, start streaming on the pipeline if buffers are 37562306a36Sopenharmony_ci * queued on both the input and the output, if the pipeline isn't already busy. 37662306a36Sopenharmony_ci * If the pipeline is busy, it will be restarted in the output module interrupt 37762306a36Sopenharmony_ci * handler. 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_cistatic void isp_video_buffer_queue(struct vb2_buffer *buf) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(buf); 38262306a36Sopenharmony_ci struct isp_video_fh *vfh = vb2_get_drv_priv(buf->vb2_queue); 38362306a36Sopenharmony_ci struct isp_buffer *buffer = to_isp_buffer(vbuf); 38462306a36Sopenharmony_ci struct isp_video *video = vfh->video; 38562306a36Sopenharmony_ci struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); 38662306a36Sopenharmony_ci enum isp_pipeline_state state; 38762306a36Sopenharmony_ci unsigned long flags; 38862306a36Sopenharmony_ci unsigned int empty; 38962306a36Sopenharmony_ci unsigned int start; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci spin_lock_irqsave(&video->irqlock, flags); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (unlikely(video->error)) { 39462306a36Sopenharmony_ci vb2_buffer_done(&buffer->vb.vb2_buf, VB2_BUF_STATE_ERROR); 39562306a36Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 39662306a36Sopenharmony_ci return; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci empty = list_empty(&video->dmaqueue); 40062306a36Sopenharmony_ci list_add_tail(&buffer->irqlist, &video->dmaqueue); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (empty) { 40562306a36Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 40662306a36Sopenharmony_ci state = ISP_PIPELINE_QUEUE_OUTPUT; 40762306a36Sopenharmony_ci else 40862306a36Sopenharmony_ci state = ISP_PIPELINE_QUEUE_INPUT; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci spin_lock_irqsave(&pipe->lock, flags); 41162306a36Sopenharmony_ci pipe->state |= state; 41262306a36Sopenharmony_ci video->ops->queue(video, buffer); 41362306a36Sopenharmony_ci video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci start = isp_pipeline_ready(pipe); 41662306a36Sopenharmony_ci if (start) 41762306a36Sopenharmony_ci pipe->state |= ISP_PIPELINE_STREAM; 41862306a36Sopenharmony_ci spin_unlock_irqrestore(&pipe->lock, flags); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (start) 42162306a36Sopenharmony_ci omap3isp_pipeline_set_stream(pipe, 42262306a36Sopenharmony_ci ISP_PIPELINE_STREAM_SINGLESHOT); 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci/* 42762306a36Sopenharmony_ci * omap3isp_video_return_buffers - Return all queued buffers to videobuf2 42862306a36Sopenharmony_ci * @video: ISP video object 42962306a36Sopenharmony_ci * @state: new state for the returned buffers 43062306a36Sopenharmony_ci * 43162306a36Sopenharmony_ci * Return all buffers queued on the video node to videobuf2 in the given state. 43262306a36Sopenharmony_ci * The buffer state should be VB2_BUF_STATE_QUEUED if called due to an error 43362306a36Sopenharmony_ci * when starting the stream, or VB2_BUF_STATE_ERROR otherwise. 43462306a36Sopenharmony_ci * 43562306a36Sopenharmony_ci * The function must be called with the video irqlock held. 43662306a36Sopenharmony_ci */ 43762306a36Sopenharmony_cistatic void omap3isp_video_return_buffers(struct isp_video *video, 43862306a36Sopenharmony_ci enum vb2_buffer_state state) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci while (!list_empty(&video->dmaqueue)) { 44162306a36Sopenharmony_ci struct isp_buffer *buf; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci buf = list_first_entry(&video->dmaqueue, 44462306a36Sopenharmony_ci struct isp_buffer, irqlist); 44562306a36Sopenharmony_ci list_del(&buf->irqlist); 44662306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, state); 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic int isp_video_start_streaming(struct vb2_queue *queue, 45162306a36Sopenharmony_ci unsigned int count) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct isp_video_fh *vfh = vb2_get_drv_priv(queue); 45462306a36Sopenharmony_ci struct isp_video *video = vfh->video; 45562306a36Sopenharmony_ci struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); 45662306a36Sopenharmony_ci unsigned long flags; 45762306a36Sopenharmony_ci int ret; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /* In sensor-to-memory mode, the stream can be started synchronously 46062306a36Sopenharmony_ci * to the stream on command. In memory-to-memory mode, it will be 46162306a36Sopenharmony_ci * started when buffers are queued on both the input and output. 46262306a36Sopenharmony_ci */ 46362306a36Sopenharmony_ci if (pipe->input) 46462306a36Sopenharmony_ci return 0; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci ret = omap3isp_pipeline_set_stream(pipe, 46762306a36Sopenharmony_ci ISP_PIPELINE_STREAM_CONTINUOUS); 46862306a36Sopenharmony_ci if (ret < 0) { 46962306a36Sopenharmony_ci spin_lock_irqsave(&video->irqlock, flags); 47062306a36Sopenharmony_ci omap3isp_video_return_buffers(video, VB2_BUF_STATE_QUEUED); 47162306a36Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 47262306a36Sopenharmony_ci return ret; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci spin_lock_irqsave(&video->irqlock, flags); 47662306a36Sopenharmony_ci if (list_empty(&video->dmaqueue)) 47762306a36Sopenharmony_ci video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; 47862306a36Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci return 0; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic const struct vb2_ops isp_video_queue_ops = { 48462306a36Sopenharmony_ci .queue_setup = isp_video_queue_setup, 48562306a36Sopenharmony_ci .buf_prepare = isp_video_buffer_prepare, 48662306a36Sopenharmony_ci .buf_queue = isp_video_buffer_queue, 48762306a36Sopenharmony_ci .start_streaming = isp_video_start_streaming, 48862306a36Sopenharmony_ci}; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci/* 49162306a36Sopenharmony_ci * omap3isp_video_buffer_next - Complete the current buffer and return the next 49262306a36Sopenharmony_ci * @video: ISP video object 49362306a36Sopenharmony_ci * 49462306a36Sopenharmony_ci * Remove the current video buffer from the DMA queue and fill its timestamp and 49562306a36Sopenharmony_ci * field count before handing it back to videobuf2. 49662306a36Sopenharmony_ci * 49762306a36Sopenharmony_ci * For capture video nodes the buffer state is set to VB2_BUF_STATE_DONE if no 49862306a36Sopenharmony_ci * error has been flagged in the pipeline, or to VB2_BUF_STATE_ERROR otherwise. 49962306a36Sopenharmony_ci * For video output nodes the buffer state is always set to VB2_BUF_STATE_DONE. 50062306a36Sopenharmony_ci * 50162306a36Sopenharmony_ci * The DMA queue is expected to contain at least one buffer. 50262306a36Sopenharmony_ci * 50362306a36Sopenharmony_ci * Return a pointer to the next buffer in the DMA queue, or NULL if the queue is 50462306a36Sopenharmony_ci * empty. 50562306a36Sopenharmony_ci */ 50662306a36Sopenharmony_cistruct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); 50962306a36Sopenharmony_ci enum vb2_buffer_state vb_state; 51062306a36Sopenharmony_ci struct isp_buffer *buf; 51162306a36Sopenharmony_ci unsigned long flags; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci spin_lock_irqsave(&video->irqlock, flags); 51462306a36Sopenharmony_ci if (WARN_ON(list_empty(&video->dmaqueue))) { 51562306a36Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 51662306a36Sopenharmony_ci return NULL; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci buf = list_first_entry(&video->dmaqueue, struct isp_buffer, 52062306a36Sopenharmony_ci irqlist); 52162306a36Sopenharmony_ci list_del(&buf->irqlist); 52262306a36Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci buf->vb.vb2_buf.timestamp = ktime_get_ns(); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci /* Do frame number propagation only if this is the output video node. 52762306a36Sopenharmony_ci * Frame number either comes from the CSI receivers or it gets 52862306a36Sopenharmony_ci * incremented here if H3A is not active. 52962306a36Sopenharmony_ci * Note: There is no guarantee that the output buffer will finish 53062306a36Sopenharmony_ci * first, so the input number might lag behind by 1 in some cases. 53162306a36Sopenharmony_ci */ 53262306a36Sopenharmony_ci if (video == pipe->output && !pipe->do_propagation) 53362306a36Sopenharmony_ci buf->vb.sequence = 53462306a36Sopenharmony_ci atomic_inc_return(&pipe->frame_number); 53562306a36Sopenharmony_ci else 53662306a36Sopenharmony_ci buf->vb.sequence = atomic_read(&pipe->frame_number); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (pipe->field != V4L2_FIELD_NONE) 53962306a36Sopenharmony_ci buf->vb.sequence /= 2; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci buf->vb.field = pipe->field; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* Report pipeline errors to userspace on the capture device side. */ 54462306a36Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) { 54562306a36Sopenharmony_ci vb_state = VB2_BUF_STATE_ERROR; 54662306a36Sopenharmony_ci pipe->error = false; 54762306a36Sopenharmony_ci } else { 54862306a36Sopenharmony_ci vb_state = VB2_BUF_STATE_DONE; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, vb_state); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci spin_lock_irqsave(&video->irqlock, flags); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (list_empty(&video->dmaqueue)) { 55662306a36Sopenharmony_ci enum isp_pipeline_state state; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 56162306a36Sopenharmony_ci state = ISP_PIPELINE_QUEUE_OUTPUT 56262306a36Sopenharmony_ci | ISP_PIPELINE_STREAM; 56362306a36Sopenharmony_ci else 56462306a36Sopenharmony_ci state = ISP_PIPELINE_QUEUE_INPUT 56562306a36Sopenharmony_ci | ISP_PIPELINE_STREAM; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci spin_lock_irqsave(&pipe->lock, flags); 56862306a36Sopenharmony_ci pipe->state &= ~state; 56962306a36Sopenharmony_ci if (video->pipe.stream_state == ISP_PIPELINE_STREAM_CONTINUOUS) 57062306a36Sopenharmony_ci video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; 57162306a36Sopenharmony_ci spin_unlock_irqrestore(&pipe->lock, flags); 57262306a36Sopenharmony_ci return NULL; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) { 57662306a36Sopenharmony_ci spin_lock(&pipe->lock); 57762306a36Sopenharmony_ci pipe->state &= ~ISP_PIPELINE_STREAM; 57862306a36Sopenharmony_ci spin_unlock(&pipe->lock); 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci buf = list_first_entry(&video->dmaqueue, struct isp_buffer, 58262306a36Sopenharmony_ci irqlist); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci return buf; 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci/* 59062306a36Sopenharmony_ci * omap3isp_video_cancel_stream - Cancel stream on a video node 59162306a36Sopenharmony_ci * @video: ISP video object 59262306a36Sopenharmony_ci * 59362306a36Sopenharmony_ci * Cancelling a stream returns all buffers queued on the video node to videobuf2 59462306a36Sopenharmony_ci * in the erroneous state and makes sure no new buffer can be queued. 59562306a36Sopenharmony_ci */ 59662306a36Sopenharmony_civoid omap3isp_video_cancel_stream(struct isp_video *video) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci unsigned long flags; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci spin_lock_irqsave(&video->irqlock, flags); 60162306a36Sopenharmony_ci omap3isp_video_return_buffers(video, VB2_BUF_STATE_ERROR); 60262306a36Sopenharmony_ci video->error = true; 60362306a36Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci/* 60762306a36Sopenharmony_ci * omap3isp_video_resume - Perform resume operation on the buffers 60862306a36Sopenharmony_ci * @video: ISP video object 60962306a36Sopenharmony_ci * @continuous: Pipeline is in single shot mode if 0 or continuous mode otherwise 61062306a36Sopenharmony_ci * 61162306a36Sopenharmony_ci * This function is intended to be used on suspend/resume scenario. It 61262306a36Sopenharmony_ci * requests video queue layer to discard buffers marked as DONE if it's in 61362306a36Sopenharmony_ci * continuous mode and requests ISP modules to queue again the ACTIVE buffer 61462306a36Sopenharmony_ci * if there's any. 61562306a36Sopenharmony_ci */ 61662306a36Sopenharmony_civoid omap3isp_video_resume(struct isp_video *video, int continuous) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci struct isp_buffer *buf = NULL; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (continuous && video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { 62162306a36Sopenharmony_ci mutex_lock(&video->queue_lock); 62262306a36Sopenharmony_ci vb2_discard_done(video->queue); 62362306a36Sopenharmony_ci mutex_unlock(&video->queue_lock); 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (!list_empty(&video->dmaqueue)) { 62762306a36Sopenharmony_ci buf = list_first_entry(&video->dmaqueue, 62862306a36Sopenharmony_ci struct isp_buffer, irqlist); 62962306a36Sopenharmony_ci video->ops->queue(video, buf); 63062306a36Sopenharmony_ci video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED; 63162306a36Sopenharmony_ci } else { 63262306a36Sopenharmony_ci if (continuous) 63362306a36Sopenharmony_ci video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 63862306a36Sopenharmony_ci * V4L2 ioctls 63962306a36Sopenharmony_ci */ 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic int 64262306a36Sopenharmony_ciisp_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci struct isp_video *video = video_drvdata(file); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci strscpy(cap->driver, ISP_VIDEO_DRIVER_NAME, sizeof(cap->driver)); 64762306a36Sopenharmony_ci strscpy(cap->card, video->video.name, sizeof(cap->card)); 64862306a36Sopenharmony_ci strscpy(cap->bus_info, "media", sizeof(cap->bus_info)); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT 65162306a36Sopenharmony_ci | V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci return 0; 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cistatic int 65862306a36Sopenharmony_ciisp_video_get_format(struct file *file, void *fh, struct v4l2_format *format) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(fh); 66162306a36Sopenharmony_ci struct isp_video *video = video_drvdata(file); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (format->type != video->type) 66462306a36Sopenharmony_ci return -EINVAL; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci mutex_lock(&video->mutex); 66762306a36Sopenharmony_ci *format = vfh->format; 66862306a36Sopenharmony_ci mutex_unlock(&video->mutex); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci return 0; 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic int 67462306a36Sopenharmony_ciisp_video_set_format(struct file *file, void *fh, struct v4l2_format *format) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(fh); 67762306a36Sopenharmony_ci struct isp_video *video = video_drvdata(file); 67862306a36Sopenharmony_ci struct v4l2_mbus_framefmt fmt; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci if (format->type != video->type) 68162306a36Sopenharmony_ci return -EINVAL; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci /* Replace unsupported field orders with sane defaults. */ 68462306a36Sopenharmony_ci switch (format->fmt.pix.field) { 68562306a36Sopenharmony_ci case V4L2_FIELD_NONE: 68662306a36Sopenharmony_ci /* Progressive is supported everywhere. */ 68762306a36Sopenharmony_ci break; 68862306a36Sopenharmony_ci case V4L2_FIELD_ALTERNATE: 68962306a36Sopenharmony_ci /* ALTERNATE is not supported on output nodes. */ 69062306a36Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 69162306a36Sopenharmony_ci format->fmt.pix.field = V4L2_FIELD_NONE; 69262306a36Sopenharmony_ci break; 69362306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED: 69462306a36Sopenharmony_ci /* The ISP has no concept of video standard, select the 69562306a36Sopenharmony_ci * top-bottom order when the unqualified interlaced order is 69662306a36Sopenharmony_ci * requested. 69762306a36Sopenharmony_ci */ 69862306a36Sopenharmony_ci format->fmt.pix.field = V4L2_FIELD_INTERLACED_TB; 69962306a36Sopenharmony_ci fallthrough; 70062306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED_TB: 70162306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED_BT: 70262306a36Sopenharmony_ci /* Interlaced orders are only supported at the CCDC output. */ 70362306a36Sopenharmony_ci if (video != &video->isp->isp_ccdc.video_out) 70462306a36Sopenharmony_ci format->fmt.pix.field = V4L2_FIELD_NONE; 70562306a36Sopenharmony_ci break; 70662306a36Sopenharmony_ci case V4L2_FIELD_TOP: 70762306a36Sopenharmony_ci case V4L2_FIELD_BOTTOM: 70862306a36Sopenharmony_ci case V4L2_FIELD_SEQ_TB: 70962306a36Sopenharmony_ci case V4L2_FIELD_SEQ_BT: 71062306a36Sopenharmony_ci default: 71162306a36Sopenharmony_ci /* All other field orders are currently unsupported, default to 71262306a36Sopenharmony_ci * progressive. 71362306a36Sopenharmony_ci */ 71462306a36Sopenharmony_ci format->fmt.pix.field = V4L2_FIELD_NONE; 71562306a36Sopenharmony_ci break; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci /* Fill the bytesperline and sizeimage fields by converting to media bus 71962306a36Sopenharmony_ci * format and back to pixel format. 72062306a36Sopenharmony_ci */ 72162306a36Sopenharmony_ci isp_video_pix_to_mbus(&format->fmt.pix, &fmt); 72262306a36Sopenharmony_ci isp_video_mbus_to_pix(video, &fmt, &format->fmt.pix); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci mutex_lock(&video->mutex); 72562306a36Sopenharmony_ci vfh->format = *format; 72662306a36Sopenharmony_ci mutex_unlock(&video->mutex); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci return 0; 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic int 73262306a36Sopenharmony_ciisp_video_try_format(struct file *file, void *fh, struct v4l2_format *format) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci struct isp_video *video = video_drvdata(file); 73562306a36Sopenharmony_ci struct v4l2_subdev_format fmt = { 73662306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 73762306a36Sopenharmony_ci }; 73862306a36Sopenharmony_ci struct v4l2_subdev *subdev; 73962306a36Sopenharmony_ci u32 pad; 74062306a36Sopenharmony_ci int ret; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci if (format->type != video->type) 74362306a36Sopenharmony_ci return -EINVAL; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci subdev = isp_video_remote_subdev(video, &pad); 74662306a36Sopenharmony_ci if (subdev == NULL) 74762306a36Sopenharmony_ci return -EINVAL; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci isp_video_pix_to_mbus(&format->fmt.pix, &fmt.format); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci fmt.pad = pad; 75262306a36Sopenharmony_ci ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); 75362306a36Sopenharmony_ci if (ret) 75462306a36Sopenharmony_ci return ret == -ENOIOCTLCMD ? -ENOTTY : ret; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci isp_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix); 75762306a36Sopenharmony_ci return 0; 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_cistatic int 76162306a36Sopenharmony_ciisp_video_get_selection(struct file *file, void *fh, struct v4l2_selection *sel) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci struct isp_video *video = video_drvdata(file); 76462306a36Sopenharmony_ci struct v4l2_subdev_format format = { 76562306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 76662306a36Sopenharmony_ci }; 76762306a36Sopenharmony_ci struct v4l2_subdev *subdev; 76862306a36Sopenharmony_ci struct v4l2_subdev_selection sdsel = { 76962306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 77062306a36Sopenharmony_ci .target = sel->target, 77162306a36Sopenharmony_ci }; 77262306a36Sopenharmony_ci u32 pad; 77362306a36Sopenharmony_ci int ret; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci switch (sel->target) { 77662306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP: 77762306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 77862306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 77962306a36Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 78062306a36Sopenharmony_ci return -EINVAL; 78162306a36Sopenharmony_ci break; 78262306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 78362306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 78462306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 78562306a36Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 78662306a36Sopenharmony_ci return -EINVAL; 78762306a36Sopenharmony_ci break; 78862306a36Sopenharmony_ci default: 78962306a36Sopenharmony_ci return -EINVAL; 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci subdev = isp_video_remote_subdev(video, &pad); 79262306a36Sopenharmony_ci if (subdev == NULL) 79362306a36Sopenharmony_ci return -EINVAL; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci /* Try the get selection operation first and fallback to get format if not 79662306a36Sopenharmony_ci * implemented. 79762306a36Sopenharmony_ci */ 79862306a36Sopenharmony_ci sdsel.pad = pad; 79962306a36Sopenharmony_ci ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, &sdsel); 80062306a36Sopenharmony_ci if (!ret) 80162306a36Sopenharmony_ci sel->r = sdsel.r; 80262306a36Sopenharmony_ci if (ret != -ENOIOCTLCMD) 80362306a36Sopenharmony_ci return ret; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci format.pad = pad; 80662306a36Sopenharmony_ci ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format); 80762306a36Sopenharmony_ci if (ret < 0) 80862306a36Sopenharmony_ci return ret == -ENOIOCTLCMD ? -ENOTTY : ret; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci sel->r.left = 0; 81162306a36Sopenharmony_ci sel->r.top = 0; 81262306a36Sopenharmony_ci sel->r.width = format.format.width; 81362306a36Sopenharmony_ci sel->r.height = format.format.height; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci return 0; 81662306a36Sopenharmony_ci} 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_cistatic int 81962306a36Sopenharmony_ciisp_video_set_selection(struct file *file, void *fh, struct v4l2_selection *sel) 82062306a36Sopenharmony_ci{ 82162306a36Sopenharmony_ci struct isp_video *video = video_drvdata(file); 82262306a36Sopenharmony_ci struct v4l2_subdev *subdev; 82362306a36Sopenharmony_ci struct v4l2_subdev_selection sdsel = { 82462306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 82562306a36Sopenharmony_ci .target = sel->target, 82662306a36Sopenharmony_ci .flags = sel->flags, 82762306a36Sopenharmony_ci .r = sel->r, 82862306a36Sopenharmony_ci }; 82962306a36Sopenharmony_ci u32 pad; 83062306a36Sopenharmony_ci int ret; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci switch (sel->target) { 83362306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP: 83462306a36Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 83562306a36Sopenharmony_ci return -EINVAL; 83662306a36Sopenharmony_ci break; 83762306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 83862306a36Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 83962306a36Sopenharmony_ci return -EINVAL; 84062306a36Sopenharmony_ci break; 84162306a36Sopenharmony_ci default: 84262306a36Sopenharmony_ci return -EINVAL; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci subdev = isp_video_remote_subdev(video, &pad); 84562306a36Sopenharmony_ci if (subdev == NULL) 84662306a36Sopenharmony_ci return -EINVAL; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci sdsel.pad = pad; 84962306a36Sopenharmony_ci mutex_lock(&video->mutex); 85062306a36Sopenharmony_ci ret = v4l2_subdev_call(subdev, pad, set_selection, NULL, &sdsel); 85162306a36Sopenharmony_ci mutex_unlock(&video->mutex); 85262306a36Sopenharmony_ci if (!ret) 85362306a36Sopenharmony_ci sel->r = sdsel.r; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci return ret == -ENOIOCTLCMD ? -ENOTTY : ret; 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic int 85962306a36Sopenharmony_ciisp_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(fh); 86262306a36Sopenharmony_ci struct isp_video *video = video_drvdata(file); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || 86562306a36Sopenharmony_ci video->type != a->type) 86662306a36Sopenharmony_ci return -EINVAL; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci memset(a, 0, sizeof(*a)); 86962306a36Sopenharmony_ci a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 87062306a36Sopenharmony_ci a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; 87162306a36Sopenharmony_ci a->parm.output.timeperframe = vfh->timeperframe; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci return 0; 87462306a36Sopenharmony_ci} 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_cistatic int 87762306a36Sopenharmony_ciisp_video_set_param(struct file *file, void *fh, struct v4l2_streamparm *a) 87862306a36Sopenharmony_ci{ 87962306a36Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(fh); 88062306a36Sopenharmony_ci struct isp_video *video = video_drvdata(file); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || 88362306a36Sopenharmony_ci video->type != a->type) 88462306a36Sopenharmony_ci return -EINVAL; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci if (a->parm.output.timeperframe.denominator == 0) 88762306a36Sopenharmony_ci a->parm.output.timeperframe.denominator = 1; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci vfh->timeperframe = a->parm.output.timeperframe; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci return 0; 89262306a36Sopenharmony_ci} 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_cistatic int 89562306a36Sopenharmony_ciisp_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(fh); 89862306a36Sopenharmony_ci struct isp_video *video = video_drvdata(file); 89962306a36Sopenharmony_ci int ret; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci mutex_lock(&video->queue_lock); 90262306a36Sopenharmony_ci ret = vb2_reqbufs(&vfh->queue, rb); 90362306a36Sopenharmony_ci mutex_unlock(&video->queue_lock); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci return ret; 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_cistatic int 90962306a36Sopenharmony_ciisp_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) 91062306a36Sopenharmony_ci{ 91162306a36Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(fh); 91262306a36Sopenharmony_ci struct isp_video *video = video_drvdata(file); 91362306a36Sopenharmony_ci int ret; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci mutex_lock(&video->queue_lock); 91662306a36Sopenharmony_ci ret = vb2_querybuf(&vfh->queue, b); 91762306a36Sopenharmony_ci mutex_unlock(&video->queue_lock); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci return ret; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic int 92362306a36Sopenharmony_ciisp_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) 92462306a36Sopenharmony_ci{ 92562306a36Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(fh); 92662306a36Sopenharmony_ci struct isp_video *video = video_drvdata(file); 92762306a36Sopenharmony_ci int ret; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci mutex_lock(&video->queue_lock); 93062306a36Sopenharmony_ci ret = vb2_qbuf(&vfh->queue, video->video.v4l2_dev->mdev, b); 93162306a36Sopenharmony_ci mutex_unlock(&video->queue_lock); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci return ret; 93462306a36Sopenharmony_ci} 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_cistatic int 93762306a36Sopenharmony_ciisp_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) 93862306a36Sopenharmony_ci{ 93962306a36Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(fh); 94062306a36Sopenharmony_ci struct isp_video *video = video_drvdata(file); 94162306a36Sopenharmony_ci int ret; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci mutex_lock(&video->queue_lock); 94462306a36Sopenharmony_ci ret = vb2_dqbuf(&vfh->queue, b, file->f_flags & O_NONBLOCK); 94562306a36Sopenharmony_ci mutex_unlock(&video->queue_lock); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci return ret; 94862306a36Sopenharmony_ci} 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_cistatic int isp_video_check_external_subdevs(struct isp_video *video, 95162306a36Sopenharmony_ci struct isp_pipeline *pipe) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci struct isp_device *isp = video->isp; 95462306a36Sopenharmony_ci struct media_entity *ents[] = { 95562306a36Sopenharmony_ci &isp->isp_csi2a.subdev.entity, 95662306a36Sopenharmony_ci &isp->isp_csi2c.subdev.entity, 95762306a36Sopenharmony_ci &isp->isp_ccp2.subdev.entity, 95862306a36Sopenharmony_ci &isp->isp_ccdc.subdev.entity 95962306a36Sopenharmony_ci }; 96062306a36Sopenharmony_ci struct media_pad *source_pad; 96162306a36Sopenharmony_ci struct media_entity *source = NULL; 96262306a36Sopenharmony_ci struct media_entity *sink; 96362306a36Sopenharmony_ci struct v4l2_subdev_format fmt = { 96462306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 96562306a36Sopenharmony_ci }; 96662306a36Sopenharmony_ci struct v4l2_ext_controls ctrls; 96762306a36Sopenharmony_ci struct v4l2_ext_control ctrl; 96862306a36Sopenharmony_ci unsigned int i; 96962306a36Sopenharmony_ci int ret; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci /* Memory-to-memory pipelines have no external subdev. */ 97262306a36Sopenharmony_ci if (pipe->input != NULL) 97362306a36Sopenharmony_ci return 0; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ents); i++) { 97662306a36Sopenharmony_ci /* Is the entity part of the pipeline? */ 97762306a36Sopenharmony_ci if (!media_entity_enum_test(&pipe->ent_enum, ents[i])) 97862306a36Sopenharmony_ci continue; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci /* ISP entities have always sink pad == 0. Find source. */ 98162306a36Sopenharmony_ci source_pad = media_pad_remote_pad_first(&ents[i]->pads[0]); 98262306a36Sopenharmony_ci if (source_pad == NULL) 98362306a36Sopenharmony_ci continue; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci source = source_pad->entity; 98662306a36Sopenharmony_ci sink = ents[i]; 98762306a36Sopenharmony_ci break; 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci if (!source) { 99162306a36Sopenharmony_ci dev_warn(isp->dev, "can't find source, failing now\n"); 99262306a36Sopenharmony_ci return -EINVAL; 99362306a36Sopenharmony_ci } 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci if (!is_media_entity_v4l2_subdev(source)) 99662306a36Sopenharmony_ci return 0; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci pipe->external = media_entity_to_v4l2_subdev(source); 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci fmt.pad = source_pad->index; 100162306a36Sopenharmony_ci ret = v4l2_subdev_call(media_entity_to_v4l2_subdev(sink), 100262306a36Sopenharmony_ci pad, get_fmt, NULL, &fmt); 100362306a36Sopenharmony_ci if (unlikely(ret < 0)) { 100462306a36Sopenharmony_ci dev_warn(isp->dev, "get_fmt returned null!\n"); 100562306a36Sopenharmony_ci return ret; 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci pipe->external_width = 100962306a36Sopenharmony_ci omap3isp_video_format_info(fmt.format.code)->width; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci memset(&ctrls, 0, sizeof(ctrls)); 101262306a36Sopenharmony_ci memset(&ctrl, 0, sizeof(ctrl)); 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci ctrl.id = V4L2_CID_PIXEL_RATE; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci ctrls.count = 1; 101762306a36Sopenharmony_ci ctrls.controls = &ctrl; 101862306a36Sopenharmony_ci ret = v4l2_g_ext_ctrls(pipe->external->ctrl_handler, &video->video, 101962306a36Sopenharmony_ci NULL, &ctrls); 102062306a36Sopenharmony_ci if (ret < 0) { 102162306a36Sopenharmony_ci dev_warn(isp->dev, "no pixel rate control in subdev %s\n", 102262306a36Sopenharmony_ci pipe->external->name); 102362306a36Sopenharmony_ci return ret; 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci pipe->external_rate = ctrl.value64; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci if (media_entity_enum_test(&pipe->ent_enum, 102962306a36Sopenharmony_ci &isp->isp_ccdc.subdev.entity)) { 103062306a36Sopenharmony_ci unsigned int rate = UINT_MAX; 103162306a36Sopenharmony_ci /* 103262306a36Sopenharmony_ci * Check that maximum allowed CCDC pixel rate isn't 103362306a36Sopenharmony_ci * exceeded by the pixel rate. 103462306a36Sopenharmony_ci */ 103562306a36Sopenharmony_ci omap3isp_ccdc_max_rate(&isp->isp_ccdc, &rate); 103662306a36Sopenharmony_ci if (pipe->external_rate > rate) 103762306a36Sopenharmony_ci return -ENOSPC; 103862306a36Sopenharmony_ci } 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci return 0; 104162306a36Sopenharmony_ci} 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci/* 104462306a36Sopenharmony_ci * Stream management 104562306a36Sopenharmony_ci * 104662306a36Sopenharmony_ci * Every ISP pipeline has a single input and a single output. The input can be 104762306a36Sopenharmony_ci * either a sensor or a video node. The output is always a video node. 104862306a36Sopenharmony_ci * 104962306a36Sopenharmony_ci * As every pipeline has an output video node, the ISP video objects at the 105062306a36Sopenharmony_ci * pipeline output stores the pipeline state. It tracks the streaming state of 105162306a36Sopenharmony_ci * both the input and output, as well as the availability of buffers. 105262306a36Sopenharmony_ci * 105362306a36Sopenharmony_ci * In sensor-to-memory mode, frames are always available at the pipeline input. 105462306a36Sopenharmony_ci * Starting the sensor usually requires I2C transfers and must be done in 105562306a36Sopenharmony_ci * interruptible context. The pipeline is started and stopped synchronously 105662306a36Sopenharmony_ci * to the stream on/off commands. All modules in the pipeline will get their 105762306a36Sopenharmony_ci * subdev set stream handler called. The module at the end of the pipeline must 105862306a36Sopenharmony_ci * delay starting the hardware until buffers are available at its output. 105962306a36Sopenharmony_ci * 106062306a36Sopenharmony_ci * In memory-to-memory mode, starting/stopping the stream requires 106162306a36Sopenharmony_ci * synchronization between the input and output. ISP modules can't be stopped 106262306a36Sopenharmony_ci * in the middle of a frame, and at least some of the modules seem to become 106362306a36Sopenharmony_ci * busy as soon as they're started, even if they don't receive a frame start 106462306a36Sopenharmony_ci * event. For that reason frames need to be processed in single-shot mode. The 106562306a36Sopenharmony_ci * driver needs to wait until a frame is completely processed and written to 106662306a36Sopenharmony_ci * memory before restarting the pipeline for the next frame. Pipelined 106762306a36Sopenharmony_ci * processing might be possible but requires more testing. 106862306a36Sopenharmony_ci * 106962306a36Sopenharmony_ci * Stream start must be delayed until buffers are available at both the input 107062306a36Sopenharmony_ci * and output. The pipeline must be started in the vb2 queue callback with 107162306a36Sopenharmony_ci * the buffers queue spinlock held. The modules subdev set stream operation must 107262306a36Sopenharmony_ci * not sleep. 107362306a36Sopenharmony_ci */ 107462306a36Sopenharmony_cistatic int 107562306a36Sopenharmony_ciisp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(fh); 107862306a36Sopenharmony_ci struct isp_video *video = video_drvdata(file); 107962306a36Sopenharmony_ci enum isp_pipeline_state state; 108062306a36Sopenharmony_ci struct isp_pipeline *pipe; 108162306a36Sopenharmony_ci unsigned long flags; 108262306a36Sopenharmony_ci int ret; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci if (type != video->type) 108562306a36Sopenharmony_ci return -EINVAL; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci mutex_lock(&video->stream_lock); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci /* Start streaming on the pipeline. No link touching an entity in the 109062306a36Sopenharmony_ci * pipeline can be activated or deactivated once streaming is started. 109162306a36Sopenharmony_ci */ 109262306a36Sopenharmony_ci pipe = to_isp_pipeline(&video->video.entity) ? : &video->pipe; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci ret = media_entity_enum_init(&pipe->ent_enum, &video->isp->media_dev); 109562306a36Sopenharmony_ci if (ret) 109662306a36Sopenharmony_ci goto err_enum_init; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci /* TODO: Implement PM QoS */ 109962306a36Sopenharmony_ci pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); 110062306a36Sopenharmony_ci pipe->max_rate = pipe->l3_ick; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci ret = video_device_pipeline_start(&video->video, &pipe->pipe); 110362306a36Sopenharmony_ci if (ret < 0) 110462306a36Sopenharmony_ci goto err_pipeline_start; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci /* Verify that the currently configured format matches the output of 110762306a36Sopenharmony_ci * the connected subdev. 110862306a36Sopenharmony_ci */ 110962306a36Sopenharmony_ci ret = isp_video_check_format(video, vfh); 111062306a36Sopenharmony_ci if (ret < 0) 111162306a36Sopenharmony_ci goto err_check_format; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci video->bpl_padding = ret; 111462306a36Sopenharmony_ci video->bpl_value = vfh->format.fmt.pix.bytesperline; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci ret = isp_video_get_graph_data(video, pipe); 111762306a36Sopenharmony_ci if (ret < 0) 111862306a36Sopenharmony_ci goto err_check_format; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 112162306a36Sopenharmony_ci state = ISP_PIPELINE_STREAM_OUTPUT | ISP_PIPELINE_IDLE_OUTPUT; 112262306a36Sopenharmony_ci else 112362306a36Sopenharmony_ci state = ISP_PIPELINE_STREAM_INPUT | ISP_PIPELINE_IDLE_INPUT; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci ret = isp_video_check_external_subdevs(video, pipe); 112662306a36Sopenharmony_ci if (ret < 0) 112762306a36Sopenharmony_ci goto err_check_format; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci pipe->error = false; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci spin_lock_irqsave(&pipe->lock, flags); 113262306a36Sopenharmony_ci pipe->state &= ~ISP_PIPELINE_STREAM; 113362306a36Sopenharmony_ci pipe->state |= state; 113462306a36Sopenharmony_ci spin_unlock_irqrestore(&pipe->lock, flags); 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci /* Set the maximum time per frame as the value requested by userspace. 113762306a36Sopenharmony_ci * This is a soft limit that can be overridden if the hardware doesn't 113862306a36Sopenharmony_ci * support the request limit. 113962306a36Sopenharmony_ci */ 114062306a36Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 114162306a36Sopenharmony_ci pipe->max_timeperframe = vfh->timeperframe; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci video->queue = &vfh->queue; 114462306a36Sopenharmony_ci INIT_LIST_HEAD(&video->dmaqueue); 114562306a36Sopenharmony_ci atomic_set(&pipe->frame_number, -1); 114662306a36Sopenharmony_ci pipe->field = vfh->format.fmt.pix.field; 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci mutex_lock(&video->queue_lock); 114962306a36Sopenharmony_ci ret = vb2_streamon(&vfh->queue, type); 115062306a36Sopenharmony_ci mutex_unlock(&video->queue_lock); 115162306a36Sopenharmony_ci if (ret < 0) 115262306a36Sopenharmony_ci goto err_check_format; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci mutex_unlock(&video->stream_lock); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci return 0; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_cierr_check_format: 115962306a36Sopenharmony_ci video_device_pipeline_stop(&video->video); 116062306a36Sopenharmony_cierr_pipeline_start: 116162306a36Sopenharmony_ci /* TODO: Implement PM QoS */ 116262306a36Sopenharmony_ci /* The DMA queue must be emptied here, otherwise CCDC interrupts that 116362306a36Sopenharmony_ci * will get triggered the next time the CCDC is powered up will try to 116462306a36Sopenharmony_ci * access buffers that might have been freed but still present in the 116562306a36Sopenharmony_ci * DMA queue. This can easily get triggered if the above 116662306a36Sopenharmony_ci * omap3isp_pipeline_set_stream() call fails on a system with a 116762306a36Sopenharmony_ci * free-running sensor. 116862306a36Sopenharmony_ci */ 116962306a36Sopenharmony_ci INIT_LIST_HEAD(&video->dmaqueue); 117062306a36Sopenharmony_ci video->queue = NULL; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci media_entity_enum_cleanup(&pipe->ent_enum); 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_cierr_enum_init: 117562306a36Sopenharmony_ci mutex_unlock(&video->stream_lock); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci return ret; 117862306a36Sopenharmony_ci} 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_cistatic int 118162306a36Sopenharmony_ciisp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) 118262306a36Sopenharmony_ci{ 118362306a36Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(fh); 118462306a36Sopenharmony_ci struct isp_video *video = video_drvdata(file); 118562306a36Sopenharmony_ci struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); 118662306a36Sopenharmony_ci enum isp_pipeline_state state; 118762306a36Sopenharmony_ci unsigned int streaming; 118862306a36Sopenharmony_ci unsigned long flags; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci if (type != video->type) 119162306a36Sopenharmony_ci return -EINVAL; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci mutex_lock(&video->stream_lock); 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci /* Make sure we're not streaming yet. */ 119662306a36Sopenharmony_ci mutex_lock(&video->queue_lock); 119762306a36Sopenharmony_ci streaming = vb2_is_streaming(&vfh->queue); 119862306a36Sopenharmony_ci mutex_unlock(&video->queue_lock); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci if (!streaming) 120162306a36Sopenharmony_ci goto done; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci /* Update the pipeline state. */ 120462306a36Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 120562306a36Sopenharmony_ci state = ISP_PIPELINE_STREAM_OUTPUT 120662306a36Sopenharmony_ci | ISP_PIPELINE_QUEUE_OUTPUT; 120762306a36Sopenharmony_ci else 120862306a36Sopenharmony_ci state = ISP_PIPELINE_STREAM_INPUT 120962306a36Sopenharmony_ci | ISP_PIPELINE_QUEUE_INPUT; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci spin_lock_irqsave(&pipe->lock, flags); 121262306a36Sopenharmony_ci pipe->state &= ~state; 121362306a36Sopenharmony_ci spin_unlock_irqrestore(&pipe->lock, flags); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci /* Stop the stream. */ 121662306a36Sopenharmony_ci omap3isp_pipeline_set_stream(pipe, ISP_PIPELINE_STREAM_STOPPED); 121762306a36Sopenharmony_ci omap3isp_video_cancel_stream(video); 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci mutex_lock(&video->queue_lock); 122062306a36Sopenharmony_ci vb2_streamoff(&vfh->queue, type); 122162306a36Sopenharmony_ci mutex_unlock(&video->queue_lock); 122262306a36Sopenharmony_ci video->queue = NULL; 122362306a36Sopenharmony_ci video->error = false; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci /* TODO: Implement PM QoS */ 122662306a36Sopenharmony_ci video_device_pipeline_stop(&video->video); 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci media_entity_enum_cleanup(&pipe->ent_enum); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_cidone: 123162306a36Sopenharmony_ci mutex_unlock(&video->stream_lock); 123262306a36Sopenharmony_ci return 0; 123362306a36Sopenharmony_ci} 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_cistatic int 123662306a36Sopenharmony_ciisp_video_enum_input(struct file *file, void *fh, struct v4l2_input *input) 123762306a36Sopenharmony_ci{ 123862306a36Sopenharmony_ci if (input->index > 0) 123962306a36Sopenharmony_ci return -EINVAL; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci strscpy(input->name, "camera", sizeof(input->name)); 124262306a36Sopenharmony_ci input->type = V4L2_INPUT_TYPE_CAMERA; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci return 0; 124562306a36Sopenharmony_ci} 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_cistatic int 124862306a36Sopenharmony_ciisp_video_g_input(struct file *file, void *fh, unsigned int *input) 124962306a36Sopenharmony_ci{ 125062306a36Sopenharmony_ci *input = 0; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci return 0; 125362306a36Sopenharmony_ci} 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_cistatic int 125662306a36Sopenharmony_ciisp_video_s_input(struct file *file, void *fh, unsigned int input) 125762306a36Sopenharmony_ci{ 125862306a36Sopenharmony_ci return input == 0 ? 0 : -EINVAL; 125962306a36Sopenharmony_ci} 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops isp_video_ioctl_ops = { 126262306a36Sopenharmony_ci .vidioc_querycap = isp_video_querycap, 126362306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap = isp_video_get_format, 126462306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap = isp_video_set_format, 126562306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap = isp_video_try_format, 126662306a36Sopenharmony_ci .vidioc_g_fmt_vid_out = isp_video_get_format, 126762306a36Sopenharmony_ci .vidioc_s_fmt_vid_out = isp_video_set_format, 126862306a36Sopenharmony_ci .vidioc_try_fmt_vid_out = isp_video_try_format, 126962306a36Sopenharmony_ci .vidioc_g_selection = isp_video_get_selection, 127062306a36Sopenharmony_ci .vidioc_s_selection = isp_video_set_selection, 127162306a36Sopenharmony_ci .vidioc_g_parm = isp_video_get_param, 127262306a36Sopenharmony_ci .vidioc_s_parm = isp_video_set_param, 127362306a36Sopenharmony_ci .vidioc_reqbufs = isp_video_reqbufs, 127462306a36Sopenharmony_ci .vidioc_querybuf = isp_video_querybuf, 127562306a36Sopenharmony_ci .vidioc_qbuf = isp_video_qbuf, 127662306a36Sopenharmony_ci .vidioc_dqbuf = isp_video_dqbuf, 127762306a36Sopenharmony_ci .vidioc_streamon = isp_video_streamon, 127862306a36Sopenharmony_ci .vidioc_streamoff = isp_video_streamoff, 127962306a36Sopenharmony_ci .vidioc_enum_input = isp_video_enum_input, 128062306a36Sopenharmony_ci .vidioc_g_input = isp_video_g_input, 128162306a36Sopenharmony_ci .vidioc_s_input = isp_video_s_input, 128262306a36Sopenharmony_ci}; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 128562306a36Sopenharmony_ci * V4L2 file operations 128662306a36Sopenharmony_ci */ 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_cistatic int isp_video_open(struct file *file) 128962306a36Sopenharmony_ci{ 129062306a36Sopenharmony_ci struct isp_video *video = video_drvdata(file); 129162306a36Sopenharmony_ci struct isp_video_fh *handle; 129262306a36Sopenharmony_ci struct vb2_queue *queue; 129362306a36Sopenharmony_ci int ret = 0; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci handle = kzalloc(sizeof(*handle), GFP_KERNEL); 129662306a36Sopenharmony_ci if (handle == NULL) 129762306a36Sopenharmony_ci return -ENOMEM; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci v4l2_fh_init(&handle->vfh, &video->video); 130062306a36Sopenharmony_ci v4l2_fh_add(&handle->vfh); 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci /* If this is the first user, initialise the pipeline. */ 130362306a36Sopenharmony_ci if (omap3isp_get(video->isp) == NULL) { 130462306a36Sopenharmony_ci ret = -EBUSY; 130562306a36Sopenharmony_ci goto done; 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci ret = v4l2_pipeline_pm_get(&video->video.entity); 130962306a36Sopenharmony_ci if (ret < 0) { 131062306a36Sopenharmony_ci omap3isp_put(video->isp); 131162306a36Sopenharmony_ci goto done; 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci queue = &handle->queue; 131562306a36Sopenharmony_ci queue->type = video->type; 131662306a36Sopenharmony_ci queue->io_modes = VB2_MMAP | VB2_USERPTR; 131762306a36Sopenharmony_ci queue->drv_priv = handle; 131862306a36Sopenharmony_ci queue->ops = &isp_video_queue_ops; 131962306a36Sopenharmony_ci queue->mem_ops = &vb2_dma_contig_memops; 132062306a36Sopenharmony_ci queue->buf_struct_size = sizeof(struct isp_buffer); 132162306a36Sopenharmony_ci queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 132262306a36Sopenharmony_ci queue->dev = video->isp->dev; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci ret = vb2_queue_init(&handle->queue); 132562306a36Sopenharmony_ci if (ret < 0) { 132662306a36Sopenharmony_ci omap3isp_put(video->isp); 132762306a36Sopenharmony_ci goto done; 132862306a36Sopenharmony_ci } 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci memset(&handle->format, 0, sizeof(handle->format)); 133162306a36Sopenharmony_ci handle->format.type = video->type; 133262306a36Sopenharmony_ci handle->timeperframe.denominator = 1; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci handle->video = video; 133562306a36Sopenharmony_ci file->private_data = &handle->vfh; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_cidone: 133862306a36Sopenharmony_ci if (ret < 0) { 133962306a36Sopenharmony_ci v4l2_fh_del(&handle->vfh); 134062306a36Sopenharmony_ci v4l2_fh_exit(&handle->vfh); 134162306a36Sopenharmony_ci kfree(handle); 134262306a36Sopenharmony_ci } 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci return ret; 134562306a36Sopenharmony_ci} 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_cistatic int isp_video_release(struct file *file) 134862306a36Sopenharmony_ci{ 134962306a36Sopenharmony_ci struct isp_video *video = video_drvdata(file); 135062306a36Sopenharmony_ci struct v4l2_fh *vfh = file->private_data; 135162306a36Sopenharmony_ci struct isp_video_fh *handle = to_isp_video_fh(vfh); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci /* Disable streaming and free the buffers queue resources. */ 135462306a36Sopenharmony_ci isp_video_streamoff(file, vfh, video->type); 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci mutex_lock(&video->queue_lock); 135762306a36Sopenharmony_ci vb2_queue_release(&handle->queue); 135862306a36Sopenharmony_ci mutex_unlock(&video->queue_lock); 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci v4l2_pipeline_pm_put(&video->video.entity); 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci /* Release the file handle. */ 136362306a36Sopenharmony_ci v4l2_fh_del(vfh); 136462306a36Sopenharmony_ci v4l2_fh_exit(vfh); 136562306a36Sopenharmony_ci kfree(handle); 136662306a36Sopenharmony_ci file->private_data = NULL; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci omap3isp_put(video->isp); 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci return 0; 137162306a36Sopenharmony_ci} 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_cistatic __poll_t isp_video_poll(struct file *file, poll_table *wait) 137462306a36Sopenharmony_ci{ 137562306a36Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(file->private_data); 137662306a36Sopenharmony_ci struct isp_video *video = video_drvdata(file); 137762306a36Sopenharmony_ci __poll_t ret; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci mutex_lock(&video->queue_lock); 138062306a36Sopenharmony_ci ret = vb2_poll(&vfh->queue, file, wait); 138162306a36Sopenharmony_ci mutex_unlock(&video->queue_lock); 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci return ret; 138462306a36Sopenharmony_ci} 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_cistatic int isp_video_mmap(struct file *file, struct vm_area_struct *vma) 138762306a36Sopenharmony_ci{ 138862306a36Sopenharmony_ci struct isp_video_fh *vfh = to_isp_video_fh(file->private_data); 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci return vb2_mmap(&vfh->queue, vma); 139162306a36Sopenharmony_ci} 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_cistatic const struct v4l2_file_operations isp_video_fops = { 139462306a36Sopenharmony_ci .owner = THIS_MODULE, 139562306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 139662306a36Sopenharmony_ci .open = isp_video_open, 139762306a36Sopenharmony_ci .release = isp_video_release, 139862306a36Sopenharmony_ci .poll = isp_video_poll, 139962306a36Sopenharmony_ci .mmap = isp_video_mmap, 140062306a36Sopenharmony_ci}; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 140362306a36Sopenharmony_ci * ISP video core 140462306a36Sopenharmony_ci */ 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_cistatic const struct isp_video_operations isp_video_dummy_ops = { 140762306a36Sopenharmony_ci}; 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ciint omap3isp_video_init(struct isp_video *video, const char *name) 141062306a36Sopenharmony_ci{ 141162306a36Sopenharmony_ci const char *direction; 141262306a36Sopenharmony_ci int ret; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci switch (video->type) { 141562306a36Sopenharmony_ci case V4L2_BUF_TYPE_VIDEO_CAPTURE: 141662306a36Sopenharmony_ci direction = "output"; 141762306a36Sopenharmony_ci video->pad.flags = MEDIA_PAD_FL_SINK 141862306a36Sopenharmony_ci | MEDIA_PAD_FL_MUST_CONNECT; 141962306a36Sopenharmony_ci break; 142062306a36Sopenharmony_ci case V4L2_BUF_TYPE_VIDEO_OUTPUT: 142162306a36Sopenharmony_ci direction = "input"; 142262306a36Sopenharmony_ci video->pad.flags = MEDIA_PAD_FL_SOURCE 142362306a36Sopenharmony_ci | MEDIA_PAD_FL_MUST_CONNECT; 142462306a36Sopenharmony_ci video->video.vfl_dir = VFL_DIR_TX; 142562306a36Sopenharmony_ci break; 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci default: 142862306a36Sopenharmony_ci return -EINVAL; 142962306a36Sopenharmony_ci } 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci ret = media_entity_pads_init(&video->video.entity, 1, &video->pad); 143262306a36Sopenharmony_ci if (ret < 0) 143362306a36Sopenharmony_ci return ret; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci mutex_init(&video->mutex); 143662306a36Sopenharmony_ci atomic_set(&video->active, 0); 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci spin_lock_init(&video->pipe.lock); 143962306a36Sopenharmony_ci mutex_init(&video->stream_lock); 144062306a36Sopenharmony_ci mutex_init(&video->queue_lock); 144162306a36Sopenharmony_ci spin_lock_init(&video->irqlock); 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci /* Initialize the video device. */ 144462306a36Sopenharmony_ci if (video->ops == NULL) 144562306a36Sopenharmony_ci video->ops = &isp_video_dummy_ops; 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci video->video.fops = &isp_video_fops; 144862306a36Sopenharmony_ci snprintf(video->video.name, sizeof(video->video.name), 144962306a36Sopenharmony_ci "OMAP3 ISP %s %s", name, direction); 145062306a36Sopenharmony_ci video->video.vfl_type = VFL_TYPE_VIDEO; 145162306a36Sopenharmony_ci video->video.release = video_device_release_empty; 145262306a36Sopenharmony_ci video->video.ioctl_ops = &isp_video_ioctl_ops; 145362306a36Sopenharmony_ci if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 145462306a36Sopenharmony_ci video->video.device_caps = V4L2_CAP_VIDEO_CAPTURE 145562306a36Sopenharmony_ci | V4L2_CAP_STREAMING; 145662306a36Sopenharmony_ci else 145762306a36Sopenharmony_ci video->video.device_caps = V4L2_CAP_VIDEO_OUTPUT 145862306a36Sopenharmony_ci | V4L2_CAP_STREAMING; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci video->pipe.stream_state = ISP_PIPELINE_STREAM_STOPPED; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci video_set_drvdata(&video->video, video); 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci return 0; 146562306a36Sopenharmony_ci} 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_civoid omap3isp_video_cleanup(struct isp_video *video) 146862306a36Sopenharmony_ci{ 146962306a36Sopenharmony_ci media_entity_cleanup(&video->video.entity); 147062306a36Sopenharmony_ci mutex_destroy(&video->queue_lock); 147162306a36Sopenharmony_ci mutex_destroy(&video->stream_lock); 147262306a36Sopenharmony_ci mutex_destroy(&video->mutex); 147362306a36Sopenharmony_ci} 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ciint omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev) 147662306a36Sopenharmony_ci{ 147762306a36Sopenharmony_ci int ret; 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci video->video.v4l2_dev = vdev; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci ret = video_register_device(&video->video, VFL_TYPE_VIDEO, -1); 148262306a36Sopenharmony_ci if (ret < 0) 148362306a36Sopenharmony_ci dev_err(video->isp->dev, 148462306a36Sopenharmony_ci "%s: could not register video device (%d)\n", 148562306a36Sopenharmony_ci __func__, ret); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci return ret; 148862306a36Sopenharmony_ci} 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_civoid omap3isp_video_unregister(struct isp_video *video) 149162306a36Sopenharmony_ci{ 149262306a36Sopenharmony_ci video_unregister_device(&video->video); 149362306a36Sopenharmony_ci} 1494