brintos

brintos / linux-shallow public Read only

0
0
Text · 41.0 KiB · 4091f1c Raw
1513 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * V4L2 Capture ISI subdev driver for i.MX8QXP/QM platform4 *5 * ISI is a Image Sensor Interface of i.MX8QXP/QM platform, which6 * used to process image from camera sensor to memory or DC7 *8 * Copyright (c) 2019 NXP Semiconductor9 */10 11#include <linux/device.h>12#include <linux/dma-mapping.h>13#include <linux/errno.h>14#include <linux/kernel.h>15#include <linux/media-bus-format.h>16#include <linux/minmax.h>17#include <linux/pm_runtime.h>18#include <linux/string.h>19#include <linux/types.h>20#include <linux/videodev2.h>21 22#include <media/media-entity.h>23#include <media/v4l2-ctrls.h>24#include <media/v4l2-dev.h>25#include <media/v4l2-event.h>26#include <media/v4l2-fh.h>27#include <media/v4l2-ioctl.h>28#include <media/v4l2-subdev.h>29#include <media/videobuf2-core.h>30#include <media/videobuf2-dma-contig.h>31#include <media/videobuf2-v4l2.h>32 33#include "imx8-isi-core.h"34#include "imx8-isi-regs.h"35 36/* Keep the first entry matching MXC_ISI_DEF_PIXEL_FORMAT */37static const struct mxc_isi_format_info mxc_isi_formats[] = {38	/* YUV formats */39	{40		.mbus_code	= MEDIA_BUS_FMT_YUV8_1X24,41		.fourcc		= V4L2_PIX_FMT_YUYV,42		.type		= MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT43				| MXC_ISI_VIDEO_M2M_CAP,44		.isi_in_format	= CHNL_MEM_RD_CTRL_IMG_TYPE_YUV422_1P8P,45		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_YUV422_1P8P,46		.mem_planes	= 1,47		.color_planes	= 1,48		.depth		= { 16 },49		.encoding	= MXC_ISI_ENC_YUV,50	}, {51		.mbus_code	= MEDIA_BUS_FMT_YUV8_1X24,52		.fourcc		= V4L2_PIX_FMT_YUVA32,53		.type		= MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP,54		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_YUV444_1P8,55		.mem_planes	= 1,56		.color_planes	= 1,57		.depth		= { 32 },58		.encoding	= MXC_ISI_ENC_YUV,59	}, {60		.mbus_code	= MEDIA_BUS_FMT_YUV8_1X24,61		.fourcc		= V4L2_PIX_FMT_NV12,62		.type		= MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP,63		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_YUV420_2P8P,64		.color_planes	= 2,65		.mem_planes	= 1,66		.depth		= { 8, 16 },67		.hsub		= 2,68		.vsub		= 2,69		.encoding	= MXC_ISI_ENC_YUV,70	}, {71		.mbus_code	= MEDIA_BUS_FMT_YUV8_1X24,72		.fourcc		= V4L2_PIX_FMT_NV12M,73		.type		= MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP,74		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_YUV420_2P8P,75		.mem_planes	= 2,76		.color_planes	= 2,77		.depth		= { 8, 16 },78		.hsub		= 2,79		.vsub		= 2,80		.encoding	= MXC_ISI_ENC_YUV,81	}, {82		.mbus_code	= MEDIA_BUS_FMT_YUV8_1X24,83		.fourcc		= V4L2_PIX_FMT_NV16,84		.type		= MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP,85		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_YUV422_2P8P,86		.color_planes	= 2,87		.mem_planes	= 1,88		.depth		= { 8, 16 },89		.hsub		= 2,90		.vsub		= 1,91		.encoding	= MXC_ISI_ENC_YUV,92	}, {93		.mbus_code	= MEDIA_BUS_FMT_YUV8_1X24,94		.fourcc		= V4L2_PIX_FMT_NV16M,95		.type		= MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP,96		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_YUV422_2P8P,97		.mem_planes	= 2,98		.color_planes	= 2,99		.depth		= { 8, 16 },100		.hsub		= 2,101		.vsub		= 1,102		.encoding	= MXC_ISI_ENC_YUV,103	}, {104		.mbus_code	= MEDIA_BUS_FMT_YUV8_1X24,105		.fourcc		= V4L2_PIX_FMT_YUV444M,106		.type		= MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP,107		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_YUV444_3P8P,108		.mem_planes	= 3,109		.color_planes	= 3,110		.depth		= { 8, 8, 8 },111		.hsub		= 1,112		.vsub		= 1,113		.encoding	= MXC_ISI_ENC_YUV,114	},115	/* RGB formats */116	{117		.mbus_code	= MEDIA_BUS_FMT_RGB888_1X24,118		.fourcc		= V4L2_PIX_FMT_RGB565,119		.type		= MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT120				| MXC_ISI_VIDEO_M2M_CAP,121		.isi_in_format	= CHNL_MEM_RD_CTRL_IMG_TYPE_RGB565,122		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RGB565,123		.mem_planes	= 1,124		.color_planes	= 1,125		.depth		= { 16 },126		.encoding	= MXC_ISI_ENC_RGB,127	}, {128		.mbus_code	= MEDIA_BUS_FMT_RGB888_1X24,129		.fourcc		= V4L2_PIX_FMT_RGB24,130		.type		= MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT131				| MXC_ISI_VIDEO_M2M_CAP,132		.isi_in_format	= CHNL_MEM_RD_CTRL_IMG_TYPE_BGR8P,133		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_BGR888P,134		.mem_planes	= 1,135		.color_planes	= 1,136		.depth		= { 24 },137		.encoding	= MXC_ISI_ENC_RGB,138	}, {139		.mbus_code	= MEDIA_BUS_FMT_RGB888_1X24,140		.fourcc		= V4L2_PIX_FMT_BGR24,141		.type		= MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT142				| MXC_ISI_VIDEO_M2M_CAP,143		.isi_in_format	= CHNL_MEM_RD_CTRL_IMG_TYPE_RGB8P,144		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RGB888P,145		.mem_planes	= 1,146		.color_planes	= 1,147		.depth		= { 24 },148		.encoding	= MXC_ISI_ENC_RGB,149	}, {150		.mbus_code	= MEDIA_BUS_FMT_RGB888_1X24,151		.fourcc		= V4L2_PIX_FMT_XBGR32,152		.type		= MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT153				| MXC_ISI_VIDEO_M2M_CAP,154		.isi_in_format	= CHNL_MEM_RD_CTRL_IMG_TYPE_XBGR8,155		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_XRGB888,156		.mem_planes	= 1,157		.color_planes	= 1,158		.depth		= { 32 },159		.encoding	= MXC_ISI_ENC_RGB,160	}, {161		.mbus_code	= MEDIA_BUS_FMT_RGB888_1X24,162		.fourcc		= V4L2_PIX_FMT_ABGR32,163		.type		= MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP,164		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_ARGB8888,165		.mem_planes	= 1,166		.color_planes	= 1,167		.depth		= { 32 },168		.encoding	= MXC_ISI_ENC_RGB,169	},170	/*171	 * RAW formats172	 *173	 * The ISI shifts the 10-bit and 12-bit formats left by 6 and 4 bits174	 * when using CHNL_IMG_CTRL_FORMAT_RAW10 or MXC_ISI_OUT_FMT_RAW12175	 * respectively, to align the bits to the left and pad with zeros in176	 * the LSBs. The corresponding V4L2 formats are however right-aligned,177	 * we have to use CHNL_IMG_CTRL_FORMAT_RAW16 to avoid the left shift.178	 */179	{180		.mbus_code	= MEDIA_BUS_FMT_Y8_1X8,181		.fourcc		= V4L2_PIX_FMT_GREY,182		.type		= MXC_ISI_VIDEO_CAP,183		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RAW8,184		.mem_planes	= 1,185		.color_planes	= 1,186		.depth		= { 8 },187		.encoding	= MXC_ISI_ENC_RAW,188	}, {189		.mbus_code	= MEDIA_BUS_FMT_Y10_1X10,190		.fourcc		= V4L2_PIX_FMT_Y10,191		.type		= MXC_ISI_VIDEO_CAP,192		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RAW16,193		.mem_planes	= 1,194		.color_planes	= 1,195		.depth		= { 16 },196		.encoding	= MXC_ISI_ENC_RAW,197	}, {198		.mbus_code	= MEDIA_BUS_FMT_Y12_1X12,199		.fourcc		= V4L2_PIX_FMT_Y12,200		.type		= MXC_ISI_VIDEO_CAP,201		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RAW16,202		.mem_planes	= 1,203		.color_planes	= 1,204		.depth		= { 16 },205		.encoding	= MXC_ISI_ENC_RAW,206	}, {207		.mbus_code	= MEDIA_BUS_FMT_Y14_1X14,208		.fourcc		= V4L2_PIX_FMT_Y14,209		.type		= MXC_ISI_VIDEO_CAP,210		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RAW16,211		.mem_planes	= 1,212		.color_planes	= 1,213		.depth		= { 16 },214		.encoding	= MXC_ISI_ENC_RAW,215	}, {216		.mbus_code	= MEDIA_BUS_FMT_SBGGR8_1X8,217		.fourcc		= V4L2_PIX_FMT_SBGGR8,218		.type		= MXC_ISI_VIDEO_CAP,219		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RAW8,220		.mem_planes	= 1,221		.color_planes	= 1,222		.depth		= { 8 },223		.encoding	= MXC_ISI_ENC_RAW,224	}, {225		.mbus_code	= MEDIA_BUS_FMT_SGBRG8_1X8,226		.fourcc		= V4L2_PIX_FMT_SGBRG8,227		.type		= MXC_ISI_VIDEO_CAP,228		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RAW8,229		.mem_planes	= 1,230		.color_planes	= 1,231		.depth		= { 8 },232		.encoding	= MXC_ISI_ENC_RAW,233	}, {234		.mbus_code	= MEDIA_BUS_FMT_SGRBG8_1X8,235		.fourcc		= V4L2_PIX_FMT_SGRBG8,236		.type		= MXC_ISI_VIDEO_CAP,237		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RAW8,238		.mem_planes	= 1,239		.color_planes	= 1,240		.depth		= { 8 },241		.encoding	= MXC_ISI_ENC_RAW,242	}, {243		.mbus_code	= MEDIA_BUS_FMT_SRGGB8_1X8,244		.fourcc		= V4L2_PIX_FMT_SRGGB8,245		.type		= MXC_ISI_VIDEO_CAP,246		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RAW8,247		.mem_planes	= 1,248		.color_planes	= 1,249		.depth		= { 8 },250		.encoding	= MXC_ISI_ENC_RAW,251	}, {252		.mbus_code	= MEDIA_BUS_FMT_SBGGR10_1X10,253		.fourcc		= V4L2_PIX_FMT_SBGGR10,254		.type		= MXC_ISI_VIDEO_CAP,255		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RAW16,256		.mem_planes	= 1,257		.color_planes	= 1,258		.depth		= { 16 },259		.encoding	= MXC_ISI_ENC_RAW,260	}, {261		.mbus_code	= MEDIA_BUS_FMT_SGBRG10_1X10,262		.fourcc		= V4L2_PIX_FMT_SGBRG10,263		.type		= MXC_ISI_VIDEO_CAP,264		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RAW16,265		.mem_planes	= 1,266		.color_planes	= 1,267		.depth		= { 16 },268		.encoding	= MXC_ISI_ENC_RAW,269	}, {270		.mbus_code	= MEDIA_BUS_FMT_SGRBG10_1X10,271		.fourcc		= V4L2_PIX_FMT_SGRBG10,272		.type		= MXC_ISI_VIDEO_CAP,273		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RAW16,274		.mem_planes	= 1,275		.color_planes	= 1,276		.depth		= { 16 },277		.encoding	= MXC_ISI_ENC_RAW,278	}, {279		.mbus_code	= MEDIA_BUS_FMT_SRGGB10_1X10,280		.fourcc		= V4L2_PIX_FMT_SRGGB10,281		.type		= MXC_ISI_VIDEO_CAP,282		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RAW16,283		.mem_planes	= 1,284		.color_planes	= 1,285		.depth		= { 16 },286		.encoding	= MXC_ISI_ENC_RAW,287	}, {288		.mbus_code	= MEDIA_BUS_FMT_SBGGR12_1X12,289		.fourcc		= V4L2_PIX_FMT_SBGGR12,290		.type		= MXC_ISI_VIDEO_CAP,291		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RAW16,292		.mem_planes	= 1,293		.color_planes	= 1,294		.depth		= { 16 },295		.encoding	= MXC_ISI_ENC_RAW,296	}, {297		.mbus_code	= MEDIA_BUS_FMT_SGBRG12_1X12,298		.fourcc		= V4L2_PIX_FMT_SGBRG12,299		.type		= MXC_ISI_VIDEO_CAP,300		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RAW16,301		.mem_planes	= 1,302		.color_planes	= 1,303		.depth		= { 16 },304		.encoding	= MXC_ISI_ENC_RAW,305	}, {306		.mbus_code	= MEDIA_BUS_FMT_SGRBG12_1X12,307		.fourcc		= V4L2_PIX_FMT_SGRBG12,308		.type		= MXC_ISI_VIDEO_CAP,309		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RAW16,310		.mem_planes	= 1,311		.color_planes	= 1,312		.depth		= { 16 },313		.encoding	= MXC_ISI_ENC_RAW,314	}, {315		.mbus_code	= MEDIA_BUS_FMT_SRGGB12_1X12,316		.fourcc		= V4L2_PIX_FMT_SRGGB12,317		.type		= MXC_ISI_VIDEO_CAP,318		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RAW16,319		.mem_planes	= 1,320		.color_planes	= 1,321		.depth		= { 16 },322		.encoding	= MXC_ISI_ENC_RAW,323	}, {324		.mbus_code	= MEDIA_BUS_FMT_SBGGR14_1X14,325		.fourcc		= V4L2_PIX_FMT_SBGGR14,326		.type		= MXC_ISI_VIDEO_CAP,327		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RAW16,328		.mem_planes	= 1,329		.color_planes	= 1,330		.depth		= { 16 },331		.encoding	= MXC_ISI_ENC_RAW,332	}, {333		.mbus_code	= MEDIA_BUS_FMT_SGBRG14_1X14,334		.fourcc		= V4L2_PIX_FMT_SGBRG14,335		.type		= MXC_ISI_VIDEO_CAP,336		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RAW16,337		.mem_planes	= 1,338		.color_planes	= 1,339		.depth		= { 16 },340		.encoding	= MXC_ISI_ENC_RAW,341	}, {342		.mbus_code	= MEDIA_BUS_FMT_SGRBG14_1X14,343		.fourcc		= V4L2_PIX_FMT_SGRBG14,344		.type		= MXC_ISI_VIDEO_CAP,345		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RAW16,346		.mem_planes	= 1,347		.color_planes	= 1,348		.depth		= { 16 },349		.encoding	= MXC_ISI_ENC_RAW,350	}, {351		.mbus_code	= MEDIA_BUS_FMT_SRGGB14_1X14,352		.fourcc		= V4L2_PIX_FMT_SRGGB14,353		.type		= MXC_ISI_VIDEO_CAP,354		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RAW16,355		.mem_planes	= 1,356		.color_planes	= 1,357		.depth		= { 16 },358		.encoding	= MXC_ISI_ENC_RAW,359	},360	/* JPEG */361	{362		.mbus_code	= MEDIA_BUS_FMT_JPEG_1X8,363		.fourcc		= V4L2_PIX_FMT_MJPEG,364		.type		= MXC_ISI_VIDEO_CAP,365		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_RAW8,366		.mem_planes	= 1,367		.color_planes	= 1,368		.depth		= { 8 },369		.encoding	= MXC_ISI_ENC_RAW,370	}371};372 373const struct mxc_isi_format_info *374mxc_isi_format_by_fourcc(u32 fourcc, enum mxc_isi_video_type type)375{376	unsigned int i;377 378	for (i = 0; i < ARRAY_SIZE(mxc_isi_formats); i++) {379		const struct mxc_isi_format_info *fmt = &mxc_isi_formats[i];380 381		if (fmt->fourcc == fourcc && fmt->type & type)382			return fmt;383	}384 385	return NULL;386}387 388const struct mxc_isi_format_info *389mxc_isi_format_enum(unsigned int index, enum mxc_isi_video_type type)390{391	unsigned int i;392 393	for (i = 0; i < ARRAY_SIZE(mxc_isi_formats); i++) {394		const struct mxc_isi_format_info *fmt = &mxc_isi_formats[i];395 396		if (!(fmt->type & type))397			continue;398 399		if (!index)400			return fmt;401 402		index--;403	}404 405	return NULL;406}407 408const struct mxc_isi_format_info *409mxc_isi_format_try(struct mxc_isi_pipe *pipe, struct v4l2_pix_format_mplane *pix,410		   enum mxc_isi_video_type type)411{412	const struct mxc_isi_format_info *fmt;413	unsigned int max_width;414	unsigned int i;415 416	max_width = pipe->id == pipe->isi->pdata->num_channels - 1417		  ? MXC_ISI_MAX_WIDTH_UNCHAINED418		  : MXC_ISI_MAX_WIDTH_CHAINED;419 420	fmt = mxc_isi_format_by_fourcc(pix->pixelformat, type);421	if (!fmt)422		fmt = &mxc_isi_formats[0];423 424	pix->width = clamp(pix->width, MXC_ISI_MIN_WIDTH, max_width);425	pix->height = clamp(pix->height, MXC_ISI_MIN_HEIGHT, MXC_ISI_MAX_HEIGHT);426	pix->pixelformat = fmt->fourcc;427	pix->field = V4L2_FIELD_NONE;428 429	if (pix->colorspace == V4L2_COLORSPACE_DEFAULT) {430		pix->colorspace = MXC_ISI_DEF_COLOR_SPACE;431		pix->ycbcr_enc = MXC_ISI_DEF_YCBCR_ENC;432		pix->quantization = MXC_ISI_DEF_QUANTIZATION;433		pix->xfer_func = MXC_ISI_DEF_XFER_FUNC;434	}435 436	if (pix->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT)437		pix->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace);438	if (pix->quantization == V4L2_QUANTIZATION_DEFAULT) {439		bool is_rgb = fmt->encoding == MXC_ISI_ENC_RGB;440 441		pix->quantization =442			V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb, pix->colorspace,443						      pix->ycbcr_enc);444	}445	if (pix->xfer_func == V4L2_XFER_FUNC_DEFAULT)446		pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace);447 448	pix->num_planes = fmt->mem_planes;449 450	for (i = 0; i < fmt->color_planes; ++i) {451		struct v4l2_plane_pix_format *plane = &pix->plane_fmt[i];452		unsigned int bpl;453 454		/* The pitch must be identical for all planes. */455		if (i == 0)456			bpl = clamp(plane->bytesperline,457				    pix->width * fmt->depth[0] / 8,458				    65535U);459		else460			bpl = pix->plane_fmt[0].bytesperline;461 462		plane->bytesperline = bpl;463 464		plane->sizeimage = plane->bytesperline * pix->height;465		if (i >= 1)466			plane->sizeimage /= fmt->vsub;467	}468 469	/*470	 * For single-planar pixel formats with multiple color planes,471	 * concatenate the size of all planes and clear all planes but the472	 * first one.473	 */474	if (fmt->color_planes != fmt->mem_planes) {475		for (i = 1; i < fmt->color_planes; ++i) {476			struct v4l2_plane_pix_format *plane = &pix->plane_fmt[i];477 478			pix->plane_fmt[0].sizeimage += plane->sizeimage;479			plane->bytesperline = 0;480			plane->sizeimage = 0;481		}482	}483 484	return fmt;485}486 487/* -----------------------------------------------------------------------------488 * videobuf2 queue operations489 */490 491static void mxc_isi_video_frame_write_done(struct mxc_isi_pipe *pipe,492					   u32 status)493{494	struct mxc_isi_video *video = &pipe->video;495	struct device *dev = pipe->isi->dev;496	struct mxc_isi_buffer *next_buf;497	struct mxc_isi_buffer *buf;498	enum mxc_isi_buf_id buf_id;499 500	spin_lock(&video->buf_lock);501 502	/*503	 * The ISI hardware handles buffers using a ping-pong mechanism with504	 * two sets of destination addresses (with shadow registers to allow505	 * programming addresses for all planes atomically) named BUF1 and506	 * BUF2. Addresses can be loaded and copied to shadow registers at any507	 * at any time.508	 *509	 * The hardware keeps track of which buffer is being written to and510	 * automatically switches to the other buffer at frame end, copying the511	 * corresponding address to another set of shadow registers that track512	 * the address being written to. The active buffer tracking bits are513	 * accessible through the CHNL_STS register.514	 *515	 *  BUF1        BUF2  |   Event   | Action516	 *                    |           |517	 *                    |           | Program initial buffers518	 *                    |           | B0 in BUF1, B1 in BUF2519	 *                    | Start ISI |520	 * +----+             |           |521	 * | B0 |             |           |522	 * +----+             |           |523	 *             +----+ | FRM IRQ 0 | B0 complete, BUF2 now active524	 *             | B1 | |           | Program B2 in BUF1525	 *             +----+ |           |526	 * +----+             | FRM IRQ 1 | B1 complete, BUF1 now active527	 * | B2 |             |           | Program B3 in BUF2528	 * +----+             |           |529	 *             +----+ | FRM IRQ 2 | B2 complete, BUF2 now active530	 *             | B3 | |           | Program B4 in BUF1531	 *             +----+ |           |532	 * +----+             | FRM IRQ 3 | B3 complete, BUF1 now active533	 * | B4 |             |           | Program B5 in BUF2534	 * +----+             |           |535	 *        ...         |           |536	 *537	 * Races between address programming and buffer switching can be538	 * detected by checking if a frame end interrupt occurred after539	 * programming the addresses.540	 *541	 * As none of the shadow registers are accessible, races can occur542	 * between address programming and buffer switching. It is possible to543	 * detect the race condition by checking if a frame end interrupt544	 * occurred after programming the addresses, but impossible to545	 * determine if the race has been won or lost.546	 *547	 * In addition to this, we need to use discard buffers if no pending548	 * buffers are available. To simplify handling of discard buffer, we549	 * need to allocate three of them, as two can be active concurrently550	 * and we need to still be able to get hold of a next buffer. The logic551	 * could be improved to use two buffers only, but as all discard552	 * buffers share the same memory, an additional buffer is cheap.553	 */554 555	/* Check which buffer has just completed. */556	buf_id = pipe->isi->pdata->buf_active_reverse557	       ? (status & CHNL_STS_BUF1_ACTIVE ? MXC_ISI_BUF2 : MXC_ISI_BUF1)558	       : (status & CHNL_STS_BUF1_ACTIVE ? MXC_ISI_BUF1 : MXC_ISI_BUF2);559 560	buf = list_first_entry_or_null(&video->out_active,561				       struct mxc_isi_buffer, list);562 563	/* Safety check, this should really never happen. */564	if (!buf) {565		dev_warn(dev, "trying to access empty active list\n");566		goto done;567	}568 569	/*570	 * If the buffer that has completed doesn't match the buffer on the571	 * front of the active list, it means we have lost one frame end572	 * interrupt (or possibly a large odd number of interrupts, although573	 * quite unlikely).574	 *575	 * For instance, if IRQ1 is lost and we handle IRQ2, both B1 and B2576	 * have been completed, but B3 hasn't been programmed, BUF2 still577	 * addresses B1 and the ISI is now writing in B1 instead of B3. We578	 * can't complete B2 as that would result in out-of-order completion.579	 *580	 * The only option is to ignore this interrupt and try again. When IRQ3581	 * will be handled, we will complete B1 and be in sync again.582	 */583	if (buf->id != buf_id) {584		dev_dbg(dev, "buffer ID mismatch (expected %u, got %u), skipping\n",585			buf->id, buf_id);586 587		/*588		 * Increment the frame count by two to account for the missed589		 * and the ignored interrupts.590		 */591		video->frame_count += 2;592		goto done;593	}594 595	/* Pick the next buffer and queue it to the hardware. */596	next_buf = list_first_entry_or_null(&video->out_pending,597					    struct mxc_isi_buffer, list);598	if (!next_buf) {599		next_buf = list_first_entry_or_null(&video->out_discard,600						    struct mxc_isi_buffer, list);601 602		/* Safety check, this should never happen. */603		if (!next_buf) {604			dev_warn(dev, "trying to access empty discard list\n");605			goto done;606		}607	}608 609	mxc_isi_channel_set_outbuf(pipe, next_buf->dma_addrs, buf_id);610	next_buf->id = buf_id;611 612	/*613	 * Check if we have raced with the end of frame interrupt. If so, we614	 * can't tell if the ISI has recorded the new address, or is still615	 * using the previous buffer. We must assume the latter as that is the616	 * worst case.617	 *618	 * For instance, if we are handling IRQ1 and now detect the FRM619	 * interrupt, assume B2 has completed and the ISI has switched to BUF2620	 * using B1 just before we programmed B3. Unlike in the previous race621	 * condition, B3 has been programmed and will be written to the next622	 * time the ISI switches to BUF2. We can however handle this exactly as623	 * the first race condition, as we'll program B3 (still at the head of624	 * the pending list) when handling IRQ3.625	 */626	status = mxc_isi_channel_irq_status(pipe, false);627	if (status & CHNL_STS_FRM_STRD) {628		dev_dbg(dev, "raced with frame end interrupt\n");629		video->frame_count += 2;630		goto done;631	}632 633	/*634	 * The next buffer has been queued successfully, move it to the active635	 * list, and complete the current buffer.636	 */637	list_move_tail(&next_buf->list, &video->out_active);638 639	if (!buf->discard) {640		list_del_init(&buf->list);641		buf->v4l2_buf.sequence = video->frame_count;642		buf->v4l2_buf.vb2_buf.timestamp = ktime_get_ns();643		vb2_buffer_done(&buf->v4l2_buf.vb2_buf, VB2_BUF_STATE_DONE);644	} else {645		list_move_tail(&buf->list, &video->out_discard);646	}647 648	video->frame_count++;649 650done:651	spin_unlock(&video->buf_lock);652}653 654static void mxc_isi_video_free_discard_buffers(struct mxc_isi_video *video)655{656	unsigned int i;657 658	for (i = 0; i < video->pix.num_planes; i++) {659		struct mxc_isi_dma_buffer *buf = &video->discard_buffer[i];660 661		if (!buf->addr)662			continue;663 664		dma_free_coherent(video->pipe->isi->dev, buf->size, buf->addr,665				  buf->dma);666		buf->addr = NULL;667	}668}669 670static int mxc_isi_video_alloc_discard_buffers(struct mxc_isi_video *video)671{672	unsigned int i, j;673 674	/* Allocate memory for each plane. */675	for (i = 0; i < video->pix.num_planes; i++) {676		struct mxc_isi_dma_buffer *buf = &video->discard_buffer[i];677 678		buf->size = PAGE_ALIGN(video->pix.plane_fmt[i].sizeimage);679		buf->addr = dma_alloc_coherent(video->pipe->isi->dev, buf->size,680					       &buf->dma, GFP_DMA | GFP_KERNEL);681		if (!buf->addr) {682			mxc_isi_video_free_discard_buffers(video);683			return -ENOMEM;684		}685 686		dev_dbg(video->pipe->isi->dev,687			"discard buffer plane %u: %zu bytes @%pad (CPU address %p)\n",688			i, buf->size, &buf->dma, buf->addr);689	}690 691	/* Fill the DMA addresses in the discard buffers. */692	for (i = 0; i < ARRAY_SIZE(video->buf_discard); ++i) {693		struct mxc_isi_buffer *buf = &video->buf_discard[i];694 695		buf->discard = true;696 697		for (j = 0; j < video->pix.num_planes; ++j)698			buf->dma_addrs[j] = video->discard_buffer[j].dma;699	}700 701	return 0;702}703 704static int mxc_isi_video_validate_format(struct mxc_isi_video *video)705{706	const struct v4l2_mbus_framefmt *format;707	const struct mxc_isi_format_info *info;708	struct v4l2_subdev_state *state;709	struct v4l2_subdev *sd = &video->pipe->sd;710	int ret = 0;711 712	state = v4l2_subdev_lock_and_get_active_state(sd);713 714	info = mxc_isi_format_by_fourcc(video->pix.pixelformat,715					MXC_ISI_VIDEO_CAP);716	format = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE);717 718	if (format->code != info->mbus_code ||719	    format->width != video->pix.width ||720	    format->height != video->pix.height) {721		dev_dbg(video->pipe->isi->dev,722			"%s: configuration mismatch, 0x%04x/%ux%u != 0x%04x/%ux%u\n",723			__func__, format->code, format->width, format->height,724			info->mbus_code, video->pix.width, video->pix.height);725		ret = -EINVAL;726	}727 728	v4l2_subdev_unlock_state(state);729 730	return ret;731}732 733static void mxc_isi_video_return_buffers(struct mxc_isi_video *video,734					 enum vb2_buffer_state state)735{736	struct mxc_isi_buffer *buf;737 738	spin_lock_irq(&video->buf_lock);739 740	while (!list_empty(&video->out_active)) {741		buf = list_first_entry(&video->out_active,742				       struct mxc_isi_buffer, list);743		list_del_init(&buf->list);744		if (buf->discard)745			continue;746 747		vb2_buffer_done(&buf->v4l2_buf.vb2_buf, state);748	}749 750	while (!list_empty(&video->out_pending)) {751		buf = list_first_entry(&video->out_pending,752				       struct mxc_isi_buffer, list);753		list_del_init(&buf->list);754		vb2_buffer_done(&buf->v4l2_buf.vb2_buf, state);755	}756 757	while (!list_empty(&video->out_discard)) {758		buf = list_first_entry(&video->out_discard,759				       struct mxc_isi_buffer, list);760		list_del_init(&buf->list);761	}762 763	INIT_LIST_HEAD(&video->out_active);764	INIT_LIST_HEAD(&video->out_pending);765	INIT_LIST_HEAD(&video->out_discard);766 767	spin_unlock_irq(&video->buf_lock);768}769 770static void mxc_isi_video_queue_first_buffers(struct mxc_isi_video *video)771{772	unsigned int discard;773	unsigned int i;774 775	lockdep_assert_held(&video->buf_lock);776 777	/*778	 * Queue two ISI channel output buffers. We are not guaranteed to have779	 * any buffer in the pending list when this function is called from the780	 * system resume handler. Use pending buffers as much as possible, and781	 * use discard buffers to fill the remaining slots.782	 */783 784	/* How many discard buffers do we need to queue first ? */785	discard = list_empty(&video->out_pending) ? 2786		: list_is_singular(&video->out_pending) ? 1787		: 0;788 789	for (i = 0; i < 2; ++i) {790		enum mxc_isi_buf_id buf_id = i == 0 ? MXC_ISI_BUF1791					   : MXC_ISI_BUF2;792		struct mxc_isi_buffer *buf;793		struct list_head *list;794 795		list = i < discard ? &video->out_discard : &video->out_pending;796		buf = list_first_entry(list, struct mxc_isi_buffer, list);797 798		mxc_isi_channel_set_outbuf(video->pipe, buf->dma_addrs, buf_id);799		buf->id = buf_id;800		list_move_tail(&buf->list, &video->out_active);801	}802}803 804static inline struct mxc_isi_buffer *to_isi_buffer(struct vb2_v4l2_buffer *v4l2_buf)805{806	return container_of(v4l2_buf, struct mxc_isi_buffer, v4l2_buf);807}808 809int mxc_isi_video_queue_setup(const struct v4l2_pix_format_mplane *format,810			      const struct mxc_isi_format_info *info,811			      unsigned int *num_buffers,812			      unsigned int *num_planes, unsigned int sizes[])813{814	unsigned int i;815 816	if (*num_planes) {817		if (*num_planes != info->mem_planes)818			return -EINVAL;819 820		for (i = 0; i < info->mem_planes; ++i) {821			if (sizes[i] < format->plane_fmt[i].sizeimage)822				return -EINVAL;823		}824 825		return 0;826	}827 828	*num_planes = info->mem_planes;829 830	for (i = 0; i < info->mem_planes; ++i)831		sizes[i] = format->plane_fmt[i].sizeimage;832 833	return 0;834}835 836void mxc_isi_video_buffer_init(struct vb2_buffer *vb2, dma_addr_t dma_addrs[3],837			       const struct mxc_isi_format_info *info,838			       const struct v4l2_pix_format_mplane *pix)839{840	unsigned int i;841 842	for (i = 0; i < info->mem_planes; ++i)843		dma_addrs[i] = vb2_dma_contig_plane_dma_addr(vb2, i);844 845	/*846	 * For single-planar pixel formats with multiple color planes, split847	 * the buffer into color planes.848	 */849	if (info->color_planes != info->mem_planes) {850		unsigned int size = pix->plane_fmt[0].bytesperline * pix->height;851 852		for (i = 1; i < info->color_planes; ++i) {853			unsigned int vsub = i > 1 ? info->vsub : 1;854 855			dma_addrs[i] = dma_addrs[i - 1] + size / vsub;856		}857	}858}859 860int mxc_isi_video_buffer_prepare(struct mxc_isi_dev *isi, struct vb2_buffer *vb2,861				 const struct mxc_isi_format_info *info,862				 const struct v4l2_pix_format_mplane *pix)863{864	unsigned int i;865 866	for (i = 0; i < info->mem_planes; i++) {867		unsigned long size = pix->plane_fmt[i].sizeimage;868 869		if (vb2_plane_size(vb2, i) < size) {870			dev_err(isi->dev, "User buffer too small (%ld < %ld)\n",871				vb2_plane_size(vb2, i), size);872			return -EINVAL;873		}874 875		vb2_set_plane_payload(vb2, i, size);876	}877 878	return 0;879}880 881static int mxc_isi_vb2_queue_setup(struct vb2_queue *q,882				   unsigned int *num_buffers,883				   unsigned int *num_planes,884				   unsigned int sizes[],885				   struct device *alloc_devs[])886{887	struct mxc_isi_video *video = vb2_get_drv_priv(q);888 889	return mxc_isi_video_queue_setup(&video->pix, video->fmtinfo,890					 num_buffers, num_planes, sizes);891}892 893static int mxc_isi_vb2_buffer_init(struct vb2_buffer *vb2)894{895	struct mxc_isi_buffer *buf = to_isi_buffer(to_vb2_v4l2_buffer(vb2));896	struct mxc_isi_video *video = vb2_get_drv_priv(vb2->vb2_queue);897 898	mxc_isi_video_buffer_init(vb2, buf->dma_addrs, video->fmtinfo,899				  &video->pix);900 901	return 0;902}903 904static int mxc_isi_vb2_buffer_prepare(struct vb2_buffer *vb2)905{906	struct mxc_isi_video *video = vb2_get_drv_priv(vb2->vb2_queue);907 908	return mxc_isi_video_buffer_prepare(video->pipe->isi, vb2,909					    video->fmtinfo, &video->pix);910}911 912static void mxc_isi_vb2_buffer_queue(struct vb2_buffer *vb2)913{914	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb2);915	struct mxc_isi_buffer *buf = to_isi_buffer(v4l2_buf);916	struct mxc_isi_video *video = vb2_get_drv_priv(vb2->vb2_queue);917 918	spin_lock_irq(&video->buf_lock);919	list_add_tail(&buf->list, &video->out_pending);920	spin_unlock_irq(&video->buf_lock);921}922 923static void mxc_isi_video_init_channel(struct mxc_isi_video *video)924{925	struct mxc_isi_pipe *pipe = video->pipe;926 927	mxc_isi_channel_get(pipe);928 929	mutex_lock(video->ctrls.handler.lock);930	mxc_isi_channel_set_alpha(pipe, video->ctrls.alpha);931	mxc_isi_channel_set_flip(pipe, video->ctrls.hflip, video->ctrls.vflip);932	mutex_unlock(video->ctrls.handler.lock);933 934	mxc_isi_channel_set_output_format(pipe, video->fmtinfo, &video->pix);935}936 937static int mxc_isi_vb2_start_streaming(struct vb2_queue *q, unsigned int count)938{939	struct mxc_isi_video *video = vb2_get_drv_priv(q);940	unsigned int i;941	int ret;942 943	/* Initialize the ISI channel. */944	mxc_isi_video_init_channel(video);945 946	spin_lock_irq(&video->buf_lock);947 948	/* Add the discard buffers to the out_discard list. */949	for (i = 0; i < ARRAY_SIZE(video->buf_discard); ++i) {950		struct mxc_isi_buffer *buf = &video->buf_discard[i];951 952		list_add_tail(&buf->list, &video->out_discard);953	}954 955	/* Queue the first buffers. */956	mxc_isi_video_queue_first_buffers(video);957 958	/* Clear frame count */959	video->frame_count = 0;960 961	spin_unlock_irq(&video->buf_lock);962 963	ret = mxc_isi_pipe_enable(video->pipe);964	if (ret)965		goto error;966 967	return 0;968 969error:970	mxc_isi_channel_put(video->pipe);971	mxc_isi_video_return_buffers(video, VB2_BUF_STATE_QUEUED);972	return ret;973}974 975static void mxc_isi_vb2_stop_streaming(struct vb2_queue *q)976{977	struct mxc_isi_video *video = vb2_get_drv_priv(q);978 979	mxc_isi_pipe_disable(video->pipe);980	mxc_isi_channel_put(video->pipe);981 982	mxc_isi_video_return_buffers(video, VB2_BUF_STATE_ERROR);983}984 985static const struct vb2_ops mxc_isi_vb2_qops = {986	.queue_setup		= mxc_isi_vb2_queue_setup,987	.buf_init		= mxc_isi_vb2_buffer_init,988	.buf_prepare		= mxc_isi_vb2_buffer_prepare,989	.buf_queue		= mxc_isi_vb2_buffer_queue,990	.wait_prepare		= vb2_ops_wait_prepare,991	.wait_finish		= vb2_ops_wait_finish,992	.start_streaming	= mxc_isi_vb2_start_streaming,993	.stop_streaming		= mxc_isi_vb2_stop_streaming,994};995 996/* -----------------------------------------------------------------------------997 * V4L2 controls998 */999 1000static inline struct mxc_isi_video *ctrl_to_isi_video(struct v4l2_ctrl *ctrl)1001{1002	return container_of(ctrl->handler, struct mxc_isi_video, ctrls.handler);1003}1004 1005static int mxc_isi_video_s_ctrl(struct v4l2_ctrl *ctrl)1006{1007	struct mxc_isi_video *video = ctrl_to_isi_video(ctrl);1008 1009	switch (ctrl->id) {1010	case V4L2_CID_ALPHA_COMPONENT:1011		video->ctrls.alpha = ctrl->val;1012		break;1013	case V4L2_CID_VFLIP:1014		video->ctrls.vflip = ctrl->val;1015		break;1016	case V4L2_CID_HFLIP:1017		video->ctrls.hflip = ctrl->val;1018		break;1019	}1020 1021	return 0;1022}1023 1024static const struct v4l2_ctrl_ops mxc_isi_video_ctrl_ops = {1025	.s_ctrl = mxc_isi_video_s_ctrl,1026};1027 1028static int mxc_isi_video_ctrls_create(struct mxc_isi_video *video)1029{1030	struct v4l2_ctrl_handler *handler = &video->ctrls.handler;1031	int ret;1032 1033	v4l2_ctrl_handler_init(handler, 3);1034 1035	v4l2_ctrl_new_std(handler, &mxc_isi_video_ctrl_ops,1036			  V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0);1037 1038	v4l2_ctrl_new_std(handler, &mxc_isi_video_ctrl_ops,1039			  V4L2_CID_VFLIP, 0, 1, 1, 0);1040 1041	v4l2_ctrl_new_std(handler, &mxc_isi_video_ctrl_ops,1042			  V4L2_CID_HFLIP, 0, 1, 1, 0);1043 1044	if (handler->error) {1045		ret = handler->error;1046		v4l2_ctrl_handler_free(handler);1047		return ret;1048	}1049 1050	video->vdev.ctrl_handler = handler;1051 1052	return 0;1053}1054 1055static void mxc_isi_video_ctrls_delete(struct mxc_isi_video *video)1056{1057	v4l2_ctrl_handler_free(&video->ctrls.handler);1058}1059 1060/* -----------------------------------------------------------------------------1061 * V4L2 ioctls1062 */1063 1064static int mxc_isi_video_querycap(struct file *file, void *priv,1065				  struct v4l2_capability *cap)1066{1067	strscpy(cap->driver, MXC_ISI_DRIVER_NAME, sizeof(cap->driver));1068	strscpy(cap->card, MXC_ISI_CAPTURE, sizeof(cap->card));1069 1070	return 0;1071}1072 1073static int mxc_isi_video_enum_fmt(struct file *file, void *priv,1074				  struct v4l2_fmtdesc *f)1075{1076	const struct mxc_isi_format_info *fmt;1077	unsigned int index = f->index;1078	unsigned int i;1079 1080	if (f->mbus_code) {1081		/*1082		 * If a media bus code is specified, only enumerate formats1083		 * compatible with it.1084		 */1085		for (i = 0; i < ARRAY_SIZE(mxc_isi_formats); i++) {1086			fmt = &mxc_isi_formats[i];1087			if (fmt->mbus_code != f->mbus_code)1088				continue;1089 1090			if (index == 0)1091				break;1092 1093			index--;1094		}1095 1096		if (i == ARRAY_SIZE(mxc_isi_formats))1097			return -EINVAL;1098	} else {1099		/* Otherwise, enumerate all formatS. */1100		if (f->index >= ARRAY_SIZE(mxc_isi_formats))1101			return -EINVAL;1102 1103		fmt = &mxc_isi_formats[f->index];1104	}1105 1106	f->pixelformat = fmt->fourcc;1107	f->flags |= V4L2_FMT_FLAG_CSC_COLORSPACE | V4L2_FMT_FLAG_CSC_YCBCR_ENC1108		 |  V4L2_FMT_FLAG_CSC_QUANTIZATION | V4L2_FMT_FLAG_CSC_XFER_FUNC;1109 1110	return 0;1111}1112 1113static int mxc_isi_video_g_fmt(struct file *file, void *fh,1114			       struct v4l2_format *f)1115{1116	struct mxc_isi_video *video = video_drvdata(file);1117 1118	f->fmt.pix_mp = video->pix;1119 1120	return 0;1121}1122 1123static int mxc_isi_video_try_fmt(struct file *file, void *fh,1124				 struct v4l2_format *f)1125{1126	struct mxc_isi_video *video = video_drvdata(file);1127 1128	mxc_isi_format_try(video->pipe, &f->fmt.pix_mp, MXC_ISI_VIDEO_CAP);1129	return 0;1130}1131 1132static int mxc_isi_video_s_fmt(struct file *file, void *priv,1133			       struct v4l2_format *f)1134{1135	struct mxc_isi_video *video = video_drvdata(file);1136	struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;1137 1138	if (vb2_is_busy(&video->vb2_q))1139		return -EBUSY;1140 1141	video->fmtinfo = mxc_isi_format_try(video->pipe, pix, MXC_ISI_VIDEO_CAP);1142	video->pix = *pix;1143 1144	return 0;1145}1146 1147static int mxc_isi_video_streamon(struct file *file, void *priv,1148				  enum v4l2_buf_type type)1149{1150	struct mxc_isi_video *video = video_drvdata(file);1151	struct media_device *mdev = &video->pipe->isi->media_dev;1152	struct media_pipeline *pipe;1153	int ret;1154 1155	if (vb2_queue_is_busy(&video->vb2_q, file))1156		return -EBUSY;1157 1158	/*1159	 * Get a pipeline for the video node and start it. This must be done1160	 * here and not in the queue .start_streaming() handler, so that1161	 * pipeline start errors can be reported from VIDIOC_STREAMON and not1162	 * delayed until subsequent VIDIOC_QBUF calls.1163	 */1164	mutex_lock(&mdev->graph_mutex);1165 1166	ret = mxc_isi_pipe_acquire(video->pipe, &mxc_isi_video_frame_write_done);1167	if (ret) {1168		mutex_unlock(&mdev->graph_mutex);1169		return ret;1170	}1171 1172	pipe = media_entity_pipeline(&video->vdev.entity) ? : &video->pipe->pipe;1173 1174	ret = __video_device_pipeline_start(&video->vdev, pipe);1175	if (ret) {1176		mutex_unlock(&mdev->graph_mutex);1177		goto err_release;1178	}1179 1180	mutex_unlock(&mdev->graph_mutex);1181 1182	/* Verify that the video format matches the output of the subdev. */1183	ret = mxc_isi_video_validate_format(video);1184	if (ret)1185		goto err_stop;1186 1187	/* Allocate buffers for discard operation. */1188	ret = mxc_isi_video_alloc_discard_buffers(video);1189	if (ret)1190		goto err_stop;1191 1192	ret = vb2_streamon(&video->vb2_q, type);1193	if (ret)1194		goto err_free;1195 1196	video->is_streaming = true;1197 1198	return 0;1199 1200err_free:1201	mxc_isi_video_free_discard_buffers(video);1202err_stop:1203	video_device_pipeline_stop(&video->vdev);1204err_release:1205	mxc_isi_pipe_release(video->pipe);1206	return ret;1207}1208 1209static void mxc_isi_video_cleanup_streaming(struct mxc_isi_video *video)1210{1211	lockdep_assert_held(&video->lock);1212 1213	if (!video->is_streaming)1214		return;1215 1216	mxc_isi_video_free_discard_buffers(video);1217	video_device_pipeline_stop(&video->vdev);1218	mxc_isi_pipe_release(video->pipe);1219 1220	video->is_streaming = false;1221}1222 1223static int mxc_isi_video_streamoff(struct file *file, void *priv,1224				   enum v4l2_buf_type type)1225{1226	struct mxc_isi_video *video = video_drvdata(file);1227	int ret;1228 1229	ret = vb2_ioctl_streamoff(file, priv, type);1230	if (ret)1231		return ret;1232 1233	mxc_isi_video_cleanup_streaming(video);1234 1235	return 0;1236}1237 1238static int mxc_isi_video_enum_framesizes(struct file *file, void *priv,1239					 struct v4l2_frmsizeenum *fsize)1240{1241	struct mxc_isi_video *video = video_drvdata(file);1242	const struct mxc_isi_format_info *info;1243	unsigned int max_width;1244	unsigned int h_align;1245	unsigned int v_align;1246 1247	if (fsize->index)1248		return -EINVAL;1249 1250	info = mxc_isi_format_by_fourcc(fsize->pixel_format, MXC_ISI_VIDEO_CAP);1251	if (!info)1252		return -EINVAL;1253 1254	h_align = max_t(unsigned int, info->hsub, 1);1255	v_align = max_t(unsigned int, info->vsub, 1);1256 1257	max_width = video->pipe->id == video->pipe->isi->pdata->num_channels - 11258		  ? MXC_ISI_MAX_WIDTH_UNCHAINED1259		  : MXC_ISI_MAX_WIDTH_CHAINED;1260 1261	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;1262	fsize->stepwise.min_width = ALIGN(MXC_ISI_MIN_WIDTH, h_align);1263	fsize->stepwise.min_height = ALIGN(MXC_ISI_MIN_HEIGHT, v_align);1264	fsize->stepwise.max_width = ALIGN_DOWN(max_width, h_align);1265	fsize->stepwise.max_height = ALIGN_DOWN(MXC_ISI_MAX_HEIGHT, v_align);1266	fsize->stepwise.step_width = h_align;1267	fsize->stepwise.step_height = v_align;1268 1269	/*1270	 * The width can be further restricted due to line buffer sharing1271	 * between pipelines when scaling, but we have no way to know here if1272	 * the scaler will be used.1273	 */1274 1275	return 0;1276}1277 1278static const struct v4l2_ioctl_ops mxc_isi_video_ioctl_ops = {1279	.vidioc_querycap		= mxc_isi_video_querycap,1280 1281	.vidioc_enum_fmt_vid_cap	= mxc_isi_video_enum_fmt,1282	.vidioc_try_fmt_vid_cap_mplane	= mxc_isi_video_try_fmt,1283	.vidioc_s_fmt_vid_cap_mplane	= mxc_isi_video_s_fmt,1284	.vidioc_g_fmt_vid_cap_mplane	= mxc_isi_video_g_fmt,1285 1286	.vidioc_reqbufs			= vb2_ioctl_reqbufs,1287	.vidioc_querybuf		= vb2_ioctl_querybuf,1288	.vidioc_qbuf			= vb2_ioctl_qbuf,1289	.vidioc_dqbuf			= vb2_ioctl_dqbuf,1290	.vidioc_expbuf			= vb2_ioctl_expbuf,1291	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,1292	.vidioc_create_bufs		= vb2_ioctl_create_bufs,1293 1294	.vidioc_streamon		= mxc_isi_video_streamon,1295	.vidioc_streamoff		= mxc_isi_video_streamoff,1296 1297	.vidioc_enum_framesizes		= mxc_isi_video_enum_framesizes,1298 1299	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,1300	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,1301};1302 1303/* -----------------------------------------------------------------------------1304 * Video device file operations1305 */1306 1307static int mxc_isi_video_open(struct file *file)1308{1309	struct mxc_isi_video *video = video_drvdata(file);1310	int ret;1311 1312	ret = v4l2_fh_open(file);1313	if (ret)1314		return ret;1315 1316	ret = pm_runtime_resume_and_get(video->pipe->isi->dev);1317	if (ret) {1318		v4l2_fh_release(file);1319		return ret;1320	}1321 1322	return 0;1323}1324 1325static int mxc_isi_video_release(struct file *file)1326{1327	struct mxc_isi_video *video = video_drvdata(file);1328	int ret;1329 1330	ret = vb2_fop_release(file);1331	if (ret)1332		dev_err(video->pipe->isi->dev, "%s fail\n", __func__);1333 1334	mutex_lock(&video->lock);1335	mxc_isi_video_cleanup_streaming(video);1336	mutex_unlock(&video->lock);1337 1338	pm_runtime_put(video->pipe->isi->dev);1339	return ret;1340}1341 1342static const struct v4l2_file_operations mxc_isi_video_fops = {1343	.owner		= THIS_MODULE,1344	.open		= mxc_isi_video_open,1345	.release	= mxc_isi_video_release,1346	.poll		= vb2_fop_poll,1347	.unlocked_ioctl	= video_ioctl2,1348	.mmap		= vb2_fop_mmap,1349};1350 1351/* -----------------------------------------------------------------------------1352 * Suspend & resume1353 */1354 1355void mxc_isi_video_suspend(struct mxc_isi_pipe *pipe)1356{1357	struct mxc_isi_video *video = &pipe->video;1358 1359	if (!video->is_streaming)1360		return;1361 1362	mxc_isi_pipe_disable(pipe);1363	mxc_isi_channel_put(pipe);1364 1365	spin_lock_irq(&video->buf_lock);1366 1367	/*1368	 * Move the active buffers back to the pending or discard list. We must1369	 * iterate the active list backward and move the buffers to the head of1370	 * the pending list to preserve the buffer queueing order.1371	 */1372	while (!list_empty(&video->out_active)) {1373		struct mxc_isi_buffer *buf =1374			list_last_entry(&video->out_active,1375					struct mxc_isi_buffer, list);1376 1377		if (buf->discard)1378			list_move(&buf->list, &video->out_discard);1379		else1380			list_move(&buf->list, &video->out_pending);1381	}1382 1383	spin_unlock_irq(&video->buf_lock);1384}1385 1386int mxc_isi_video_resume(struct mxc_isi_pipe *pipe)1387{1388	struct mxc_isi_video *video = &pipe->video;1389 1390	if (!video->is_streaming)1391		return 0;1392 1393	mxc_isi_video_init_channel(video);1394 1395	spin_lock_irq(&video->buf_lock);1396	mxc_isi_video_queue_first_buffers(video);1397	spin_unlock_irq(&video->buf_lock);1398 1399	return mxc_isi_pipe_enable(pipe);1400}1401 1402/* -----------------------------------------------------------------------------1403 * Registration1404 */1405 1406int mxc_isi_video_register(struct mxc_isi_pipe *pipe,1407			   struct v4l2_device *v4l2_dev)1408{1409	struct mxc_isi_video *video = &pipe->video;1410	struct v4l2_pix_format_mplane *pix = &video->pix;1411	struct video_device *vdev = &video->vdev;1412	struct vb2_queue *q = &video->vb2_q;1413	int ret = -ENOMEM;1414 1415	video->pipe = pipe;1416 1417	mutex_init(&video->lock);1418	spin_lock_init(&video->buf_lock);1419 1420	pix->width = MXC_ISI_DEF_WIDTH;1421	pix->height = MXC_ISI_DEF_HEIGHT;1422	pix->pixelformat = MXC_ISI_DEF_PIXEL_FORMAT;1423	pix->colorspace = MXC_ISI_DEF_COLOR_SPACE;1424	pix->ycbcr_enc = MXC_ISI_DEF_YCBCR_ENC;1425	pix->quantization = MXC_ISI_DEF_QUANTIZATION;1426	pix->xfer_func = MXC_ISI_DEF_XFER_FUNC;1427	video->fmtinfo = mxc_isi_format_try(video->pipe, pix, MXC_ISI_VIDEO_CAP);1428 1429	memset(vdev, 0, sizeof(*vdev));1430	snprintf(vdev->name, sizeof(vdev->name), "mxc_isi.%d.capture", pipe->id);1431 1432	vdev->fops	= &mxc_isi_video_fops;1433	vdev->ioctl_ops	= &mxc_isi_video_ioctl_ops;1434	vdev->v4l2_dev	= v4l2_dev;1435	vdev->minor	= -1;1436	vdev->release	= video_device_release_empty;1437	vdev->queue	= q;1438	vdev->lock	= &video->lock;1439 1440	vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE1441			  | V4L2_CAP_IO_MC;1442	video_set_drvdata(vdev, video);1443 1444	INIT_LIST_HEAD(&video->out_pending);1445	INIT_LIST_HEAD(&video->out_active);1446	INIT_LIST_HEAD(&video->out_discard);1447 1448	memset(q, 0, sizeof(*q));1449	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;1450	q->io_modes = VB2_MMAP | VB2_DMABUF;1451	q->drv_priv = video;1452	q->ops = &mxc_isi_vb2_qops;1453	q->mem_ops = &vb2_dma_contig_memops;1454	q->buf_struct_size = sizeof(struct mxc_isi_buffer);1455	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;1456	q->min_queued_buffers = 2;1457	q->lock = &video->lock;1458	q->dev = pipe->isi->dev;1459 1460	ret = vb2_queue_init(q);1461	if (ret)1462		goto err_free_ctx;1463 1464	video->pad.flags = MEDIA_PAD_FL_SINK;1465	vdev->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;1466	ret = media_entity_pads_init(&vdev->entity, 1, &video->pad);1467	if (ret)1468		goto err_free_ctx;1469 1470	ret = mxc_isi_video_ctrls_create(video);1471	if (ret)1472		goto err_me_cleanup;1473 1474	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);1475	if (ret)1476		goto err_ctrl_free;1477 1478	ret = media_create_pad_link(&pipe->sd.entity,1479				    MXC_ISI_PIPE_PAD_SOURCE,1480				    &vdev->entity, 0,1481				    MEDIA_LNK_FL_IMMUTABLE |1482				    MEDIA_LNK_FL_ENABLED);1483	if (ret)1484		goto err_video_unreg;1485 1486	return 0;1487 1488err_video_unreg:1489	video_unregister_device(vdev);1490err_ctrl_free:1491	mxc_isi_video_ctrls_delete(video);1492err_me_cleanup:1493	media_entity_cleanup(&vdev->entity);1494err_free_ctx:1495	return ret;1496}1497 1498void mxc_isi_video_unregister(struct mxc_isi_pipe *pipe)1499{1500	struct mxc_isi_video *video = &pipe->video;1501	struct video_device *vdev = &video->vdev;1502 1503	mutex_lock(&video->lock);1504 1505	if (video_is_registered(vdev)) {1506		video_unregister_device(vdev);1507		mxc_isi_video_ctrls_delete(video);1508		media_entity_cleanup(&vdev->entity);1509	}1510 1511	mutex_unlock(&video->lock);1512}1513