brintos

brintos / linux-shallow public Read only

0
0
Text · 29.1 KiB · 14c0dc8 Raw
1103 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing)4 * Author: Yong Deng <yong.deng@magewell.com>5 * Copyright 2021-2022 Bootlin6 * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>7 */8 9#include <linux/of.h>10#include <linux/regmap.h>11#include <media/v4l2-device.h>12#include <media/v4l2-event.h>13#include <media/v4l2-ioctl.h>14#include <media/v4l2-mc.h>15#include <media/videobuf2-dma-contig.h>16#include <media/videobuf2-v4l2.h>17 18#include "sun6i_csi.h"19#include "sun6i_csi_bridge.h"20#include "sun6i_csi_capture.h"21#include "sun6i_csi_reg.h"22 23/* Helpers */24 25void sun6i_csi_capture_dimensions(struct sun6i_csi_device *csi_dev,26				  unsigned int *width, unsigned int *height)27{28	if (width)29		*width = csi_dev->capture.format.fmt.pix.width;30	if (height)31		*height = csi_dev->capture.format.fmt.pix.height;32}33 34void sun6i_csi_capture_format(struct sun6i_csi_device *csi_dev,35			      u32 *pixelformat, u32 *field)36{37	if (pixelformat)38		*pixelformat = csi_dev->capture.format.fmt.pix.pixelformat;39 40	if (field)41		*field = csi_dev->capture.format.fmt.pix.field;42}43 44/* Format */45 46static const struct sun6i_csi_capture_format sun6i_csi_capture_formats[] = {47	/* Bayer */48	{49		.pixelformat		= V4L2_PIX_FMT_SBGGR8,50		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,51		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,52	},53	{54		.pixelformat		= V4L2_PIX_FMT_SGBRG8,55		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,56		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,57	},58	{59		.pixelformat		= V4L2_PIX_FMT_SGRBG8,60		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,61		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,62	},63	{64		.pixelformat		= V4L2_PIX_FMT_SRGGB8,65		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,66		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,67	},68	{69		.pixelformat		= V4L2_PIX_FMT_SBGGR10,70		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10,71		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10,72	},73	{74		.pixelformat		= V4L2_PIX_FMT_SGBRG10,75		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10,76		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10,77	},78	{79		.pixelformat		= V4L2_PIX_FMT_SGRBG10,80		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10,81		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10,82	},83	{84		.pixelformat		= V4L2_PIX_FMT_SRGGB10,85		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10,86		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10,87	},88	{89		.pixelformat		= V4L2_PIX_FMT_SBGGR12,90		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12,91		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12,92	},93	{94		.pixelformat		= V4L2_PIX_FMT_SGBRG12,95		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12,96		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12,97	},98	{99		.pixelformat		= V4L2_PIX_FMT_SGRBG12,100		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12,101		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12,102	},103	{104		.pixelformat		= V4L2_PIX_FMT_SRGGB12,105		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12,106		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12,107	},108	/* RGB */109	{110		.pixelformat		= V4L2_PIX_FMT_RGB565,111		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RGB565,112		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RGB565,113	},114	{115		.pixelformat		= V4L2_PIX_FMT_RGB565X,116		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RGB565,117		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RGB565,118	},119	/* YUV422 */120	{121		.pixelformat		= V4L2_PIX_FMT_YUYV,122		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,123		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,124		.input_format_raw	= true,125		.hsize_len_factor	= 2,126	},127	{128		.pixelformat		= V4L2_PIX_FMT_YVYU,129		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,130		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,131		.input_format_raw	= true,132		.hsize_len_factor	= 2,133	},134	{135		.pixelformat		= V4L2_PIX_FMT_UYVY,136		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,137		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,138		.input_format_raw	= true,139		.hsize_len_factor	= 2,140	},141	{142		.pixelformat		= V4L2_PIX_FMT_VYUY,143		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,144		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,145		.input_format_raw	= true,146		.hsize_len_factor	= 2,147	},148	{149		.pixelformat		= V4L2_PIX_FMT_NV16,150		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422SP,151		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422SP,152	},153	{154		.pixelformat		= V4L2_PIX_FMT_NV61,155		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422SP,156		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422SP,157		.input_yuv_seq_invert	= true,158	},159	{160		.pixelformat		= V4L2_PIX_FMT_YUV422P,161		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422P,162		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422P,163	},164	/* YUV420 */165	{166		.pixelformat		= V4L2_PIX_FMT_NV12_16L16,167		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420MB,168		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420MB,169	},170	{171		.pixelformat		= V4L2_PIX_FMT_NV12,172		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420SP,173		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420SP,174	},175	{176		.pixelformat		= V4L2_PIX_FMT_NV21,177		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420SP,178		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420SP,179		.input_yuv_seq_invert	= true,180	},181 182	{183		.pixelformat		= V4L2_PIX_FMT_YUV420,184		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420P,185		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420P,186	},187	{188		.pixelformat		= V4L2_PIX_FMT_YVU420,189		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420P,190		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420P,191		.input_yuv_seq_invert	= true,192	},193	/* Compressed */194	{195		.pixelformat		= V4L2_PIX_FMT_JPEG,196		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,197		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,198	},199};200 201const202struct sun6i_csi_capture_format *sun6i_csi_capture_format_find(u32 pixelformat)203{204	unsigned int i;205 206	for (i = 0; i < ARRAY_SIZE(sun6i_csi_capture_formats); i++)207		if (sun6i_csi_capture_formats[i].pixelformat == pixelformat)208			return &sun6i_csi_capture_formats[i];209 210	return NULL;211}212 213/* RAW formats need an exact match between pixel and mbus formats. */214static const215struct sun6i_csi_capture_format_match sun6i_csi_capture_format_matches[] = {216	/* YUV420 */217	{218		.pixelformat	= V4L2_PIX_FMT_YUYV,219		.mbus_code	= MEDIA_BUS_FMT_YUYV8_2X8,220	},221	{222		.pixelformat	= V4L2_PIX_FMT_YUYV,223		.mbus_code	= MEDIA_BUS_FMT_YUYV8_1X16,224	},225	{226		.pixelformat	= V4L2_PIX_FMT_YVYU,227		.mbus_code	= MEDIA_BUS_FMT_YVYU8_2X8,228	},229	{230		.pixelformat	= V4L2_PIX_FMT_YVYU,231		.mbus_code	= MEDIA_BUS_FMT_YVYU8_1X16,232	},233	{234		.pixelformat	= V4L2_PIX_FMT_UYVY,235		.mbus_code	= MEDIA_BUS_FMT_UYVY8_2X8,236	},237	{238		.pixelformat	= V4L2_PIX_FMT_UYVY,239		.mbus_code	= MEDIA_BUS_FMT_UYVY8_1X16,240	},241	{242		.pixelformat	= V4L2_PIX_FMT_VYUY,243		.mbus_code	= MEDIA_BUS_FMT_VYUY8_2X8,244	},245	{246		.pixelformat	= V4L2_PIX_FMT_VYUY,247		.mbus_code	= MEDIA_BUS_FMT_VYUY8_1X16,248	},249	/* RGB */250	{251		.pixelformat	= V4L2_PIX_FMT_RGB565,252		.mbus_code	= MEDIA_BUS_FMT_RGB565_2X8_LE,253	},254	{255		.pixelformat	= V4L2_PIX_FMT_RGB565X,256		.mbus_code	= MEDIA_BUS_FMT_RGB565_2X8_BE,257	},258	/* Bayer */259	{260		.pixelformat	= V4L2_PIX_FMT_SBGGR8,261		.mbus_code	= MEDIA_BUS_FMT_SBGGR8_1X8,262	},263	{264		.pixelformat	= V4L2_PIX_FMT_SGBRG8,265		.mbus_code	= MEDIA_BUS_FMT_SGBRG8_1X8,266	},267	{268		.pixelformat	= V4L2_PIX_FMT_SGRBG8,269		.mbus_code	= MEDIA_BUS_FMT_SGRBG8_1X8,270	},271	{272		.pixelformat	= V4L2_PIX_FMT_SRGGB8,273		.mbus_code	= MEDIA_BUS_FMT_SRGGB8_1X8,274	},275	{276		.pixelformat	= V4L2_PIX_FMT_SBGGR10,277		.mbus_code	= MEDIA_BUS_FMT_SBGGR10_1X10,278	},279	{280		.pixelformat	= V4L2_PIX_FMT_SGBRG10,281		.mbus_code	= MEDIA_BUS_FMT_SGBRG10_1X10,282	},283	{284		.pixelformat	= V4L2_PIX_FMT_SGRBG10,285		.mbus_code	= MEDIA_BUS_FMT_SGRBG10_1X10,286	},287	{288		.pixelformat	= V4L2_PIX_FMT_SRGGB10,289		.mbus_code	= MEDIA_BUS_FMT_SRGGB10_1X10,290	},291	{292		.pixelformat	= V4L2_PIX_FMT_SBGGR12,293		.mbus_code	= MEDIA_BUS_FMT_SBGGR12_1X12,294	},295	{296		.pixelformat	= V4L2_PIX_FMT_SGBRG12,297		.mbus_code	= MEDIA_BUS_FMT_SGBRG12_1X12,298	},299	{300		.pixelformat	= V4L2_PIX_FMT_SGRBG12,301		.mbus_code	= MEDIA_BUS_FMT_SGRBG12_1X12,302	},303	{304		.pixelformat	= V4L2_PIX_FMT_SRGGB12,305		.mbus_code	= MEDIA_BUS_FMT_SRGGB12_1X12,306	},307	/* Compressed */308	{309		.pixelformat	= V4L2_PIX_FMT_JPEG,310		.mbus_code	= MEDIA_BUS_FMT_JPEG_1X8,311	},312};313 314static bool sun6i_csi_capture_format_match(u32 pixelformat, u32 mbus_code)315{316	unsigned int i;317 318	for (i = 0; i < ARRAY_SIZE(sun6i_csi_capture_format_matches); i++) {319		const struct sun6i_csi_capture_format_match *match =320			&sun6i_csi_capture_format_matches[i];321 322		if (match->pixelformat == pixelformat &&323		    match->mbus_code == mbus_code)324			return true;325	}326 327	return false;328}329 330/* Capture */331 332static void333sun6i_csi_capture_buffer_configure(struct sun6i_csi_device *csi_dev,334				   struct sun6i_csi_buffer *csi_buffer)335{336	struct regmap *regmap = csi_dev->regmap;337	const struct v4l2_format_info *info;338	struct vb2_buffer *vb2_buffer;339	unsigned int width, height;340	dma_addr_t address;341	u32 pixelformat;342 343	vb2_buffer = &csi_buffer->v4l2_buffer.vb2_buf;344	address = vb2_dma_contig_plane_dma_addr(vb2_buffer, 0);345 346	regmap_write(regmap, SUN6I_CSI_CH_FIFO0_ADDR_REG,347		     SUN6I_CSI_ADDR_VALUE(address));348 349	sun6i_csi_capture_dimensions(csi_dev, &width, &height);350	sun6i_csi_capture_format(csi_dev, &pixelformat, NULL);351 352	info = v4l2_format_info(pixelformat);353	/* Unsupported formats are single-plane, so we can stop here. */354	if (!info)355		return;356 357	if (info->comp_planes > 1) {358		address += info->bpp[0] * width * height;359 360		regmap_write(regmap, SUN6I_CSI_CH_FIFO1_ADDR_REG,361			     SUN6I_CSI_ADDR_VALUE(address));362	}363 364	if (info->comp_planes > 2) {365		address += info->bpp[1] * DIV_ROUND_UP(width, info->hdiv) *366			   DIV_ROUND_UP(height, info->vdiv);367 368		regmap_write(regmap, SUN6I_CSI_CH_FIFO2_ADDR_REG,369			     SUN6I_CSI_ADDR_VALUE(address));370	}371}372 373void sun6i_csi_capture_configure(struct sun6i_csi_device *csi_dev)374{375	struct regmap *regmap = csi_dev->regmap;376	const struct sun6i_csi_capture_format *format;377	const struct v4l2_format_info *info;378	u32 hsize_len, vsize_len;379	u32 luma_line, chroma_line = 0;380	u32 pixelformat, field;381	u32 width, height;382 383	sun6i_csi_capture_dimensions(csi_dev, &width, &height);384	sun6i_csi_capture_format(csi_dev, &pixelformat, &field);385 386	format = sun6i_csi_capture_format_find(pixelformat);387	if (WARN_ON(!format))388		return;389 390	hsize_len = width;391	vsize_len = height;392 393	/*394	 * When using 8-bit raw input/output (for packed YUV), we need to adapt395	 * the width to account for the difference in bpp when it's not 8-bit.396	 */397	if (format->hsize_len_factor)398		hsize_len *= format->hsize_len_factor;399 400	regmap_write(regmap, SUN6I_CSI_CH_HSIZE_REG,401		     SUN6I_CSI_CH_HSIZE_LEN(hsize_len) |402		     SUN6I_CSI_CH_HSIZE_START(0));403 404	regmap_write(regmap, SUN6I_CSI_CH_VSIZE_REG,405		     SUN6I_CSI_CH_VSIZE_LEN(vsize_len) |406		     SUN6I_CSI_CH_VSIZE_START(0));407 408	switch (pixelformat) {409	case V4L2_PIX_FMT_RGB565X:410		luma_line = width * 2;411		break;412	case V4L2_PIX_FMT_NV12_16L16:413		luma_line = width;414		chroma_line = width;415		break;416	case V4L2_PIX_FMT_JPEG:417		luma_line = width;418		break;419	default:420		info = v4l2_format_info(pixelformat);421		if (WARN_ON(!info))422			return;423 424		luma_line = width * info->bpp[0];425 426		if (info->comp_planes > 1)427			chroma_line = width * info->bpp[1] / info->hdiv;428		break;429	}430 431	regmap_write(regmap, SUN6I_CSI_CH_BUF_LEN_REG,432		     SUN6I_CSI_CH_BUF_LEN_CHROMA_LINE(chroma_line) |433		     SUN6I_CSI_CH_BUF_LEN_LUMA_LINE(luma_line));434}435 436/* State */437 438static void sun6i_csi_capture_state_cleanup(struct sun6i_csi_device *csi_dev,439					    bool error)440{441	struct sun6i_csi_capture_state *state = &csi_dev->capture.state;442	struct sun6i_csi_buffer **csi_buffer_states[] = {443		&state->pending, &state->current, &state->complete,444	};445	struct sun6i_csi_buffer *csi_buffer;446	struct vb2_buffer *vb2_buffer;447	unsigned long flags;448	unsigned int i;449 450	spin_lock_irqsave(&state->lock, flags);451 452	for (i = 0; i < ARRAY_SIZE(csi_buffer_states); i++) {453		csi_buffer = *csi_buffer_states[i];454		if (!csi_buffer)455			continue;456 457		vb2_buffer = &csi_buffer->v4l2_buffer.vb2_buf;458		vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR :459				VB2_BUF_STATE_QUEUED);460 461		*csi_buffer_states[i] = NULL;462	}463 464	list_for_each_entry(csi_buffer, &state->queue, list) {465		vb2_buffer = &csi_buffer->v4l2_buffer.vb2_buf;466		vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR :467				VB2_BUF_STATE_QUEUED);468	}469 470	INIT_LIST_HEAD(&state->queue);471 472	spin_unlock_irqrestore(&state->lock, flags);473}474 475void sun6i_csi_capture_state_update(struct sun6i_csi_device *csi_dev)476{477	struct sun6i_csi_capture_state *state = &csi_dev->capture.state;478	struct sun6i_csi_buffer *csi_buffer;479	unsigned long flags;480 481	spin_lock_irqsave(&state->lock, flags);482 483	if (list_empty(&state->queue))484		goto complete;485 486	if (state->pending)487		goto complete;488 489	csi_buffer = list_first_entry(&state->queue, struct sun6i_csi_buffer,490				      list);491 492	sun6i_csi_capture_buffer_configure(csi_dev, csi_buffer);493 494	list_del(&csi_buffer->list);495 496	state->pending = csi_buffer;497 498complete:499	spin_unlock_irqrestore(&state->lock, flags);500}501 502static void sun6i_csi_capture_state_complete(struct sun6i_csi_device *csi_dev)503{504	struct sun6i_csi_capture_state *state = &csi_dev->capture.state;505	unsigned long flags;506 507	spin_lock_irqsave(&state->lock, flags);508 509	if (!state->pending)510		goto complete;511 512	state->complete = state->current;513	state->current = state->pending;514	state->pending = NULL;515 516	if (state->complete) {517		struct sun6i_csi_buffer *csi_buffer = state->complete;518		struct vb2_buffer *vb2_buffer =519			&csi_buffer->v4l2_buffer.vb2_buf;520 521		vb2_buffer->timestamp = ktime_get_ns();522		csi_buffer->v4l2_buffer.sequence = state->sequence;523 524		vb2_buffer_done(vb2_buffer, VB2_BUF_STATE_DONE);525 526		state->complete = NULL;527	}528 529complete:530	spin_unlock_irqrestore(&state->lock, flags);531}532 533void sun6i_csi_capture_frame_done(struct sun6i_csi_device *csi_dev)534{535	struct sun6i_csi_capture_state *state = &csi_dev->capture.state;536	unsigned long flags;537 538	spin_lock_irqsave(&state->lock, flags);539	state->sequence++;540	spin_unlock_irqrestore(&state->lock, flags);541}542 543void sun6i_csi_capture_sync(struct sun6i_csi_device *csi_dev)544{545	sun6i_csi_capture_state_complete(csi_dev);546	sun6i_csi_capture_state_update(csi_dev);547}548 549/* Queue */550 551static int sun6i_csi_capture_queue_setup(struct vb2_queue *queue,552					 unsigned int *buffers_count,553					 unsigned int *planes_count,554					 unsigned int sizes[],555					 struct device *alloc_devs[])556{557	struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue);558	unsigned int size = csi_dev->capture.format.fmt.pix.sizeimage;559 560	if (*planes_count)561		return sizes[0] < size ? -EINVAL : 0;562 563	*planes_count = 1;564	sizes[0] = size;565 566	return 0;567}568 569static int sun6i_csi_capture_buffer_prepare(struct vb2_buffer *buffer)570{571	struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(buffer->vb2_queue);572	struct sun6i_csi_capture *capture = &csi_dev->capture;573	struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev;574	struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(buffer);575	unsigned long size = capture->format.fmt.pix.sizeimage;576 577	if (vb2_plane_size(buffer, 0) < size) {578		v4l2_err(v4l2_dev, "buffer too small (%lu < %lu)\n",579			 vb2_plane_size(buffer, 0), size);580		return -EINVAL;581	}582 583	vb2_set_plane_payload(buffer, 0, size);584 585	v4l2_buffer->field = capture->format.fmt.pix.field;586 587	return 0;588}589 590static void sun6i_csi_capture_buffer_queue(struct vb2_buffer *buffer)591{592	struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(buffer->vb2_queue);593	struct sun6i_csi_capture_state *state = &csi_dev->capture.state;594	struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(buffer);595	struct sun6i_csi_buffer *csi_buffer =596		container_of(v4l2_buffer, struct sun6i_csi_buffer, v4l2_buffer);597	unsigned long flags;598 599	spin_lock_irqsave(&state->lock, flags);600	list_add_tail(&csi_buffer->list, &state->queue);601	spin_unlock_irqrestore(&state->lock, flags);602}603 604static int sun6i_csi_capture_start_streaming(struct vb2_queue *queue,605					     unsigned int count)606{607	struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue);608	struct sun6i_csi_capture_state *state = &csi_dev->capture.state;609	struct video_device *video_dev = &csi_dev->capture.video_dev;610	struct v4l2_subdev *subdev = &csi_dev->bridge.subdev;611	int ret;612 613	state->sequence = 0;614 615	ret = video_device_pipeline_alloc_start(video_dev);616	if (ret < 0)617		goto error_state;618 619	state->streaming = true;620 621	ret = v4l2_subdev_call(subdev, video, s_stream, 1);622	if (ret && ret != -ENOIOCTLCMD)623		goto error_streaming;624 625	return 0;626 627error_streaming:628	state->streaming = false;629 630	video_device_pipeline_stop(video_dev);631 632error_state:633	sun6i_csi_capture_state_cleanup(csi_dev, false);634 635	return ret;636}637 638static void sun6i_csi_capture_stop_streaming(struct vb2_queue *queue)639{640	struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue);641	struct sun6i_csi_capture_state *state = &csi_dev->capture.state;642	struct video_device *video_dev = &csi_dev->capture.video_dev;643	struct v4l2_subdev *subdev = &csi_dev->bridge.subdev;644 645	v4l2_subdev_call(subdev, video, s_stream, 0);646 647	state->streaming = false;648 649	video_device_pipeline_stop(video_dev);650 651	sun6i_csi_capture_state_cleanup(csi_dev, true);652}653 654static const struct vb2_ops sun6i_csi_capture_queue_ops = {655	.queue_setup		= sun6i_csi_capture_queue_setup,656	.buf_prepare		= sun6i_csi_capture_buffer_prepare,657	.buf_queue		= sun6i_csi_capture_buffer_queue,658	.start_streaming	= sun6i_csi_capture_start_streaming,659	.stop_streaming		= sun6i_csi_capture_stop_streaming,660	.wait_prepare		= vb2_ops_wait_prepare,661	.wait_finish		= vb2_ops_wait_finish,662};663 664/* V4L2 Device */665 666static void sun6i_csi_capture_format_prepare(struct v4l2_format *format)667{668	struct v4l2_pix_format *pix_format = &format->fmt.pix;669	const struct v4l2_format_info *info;670	unsigned int width, height;671 672	v4l_bound_align_image(&pix_format->width, SUN6I_CSI_CAPTURE_WIDTH_MIN,673			      SUN6I_CSI_CAPTURE_WIDTH_MAX, 1,674			      &pix_format->height, SUN6I_CSI_CAPTURE_HEIGHT_MIN,675			      SUN6I_CSI_CAPTURE_HEIGHT_MAX, 1, 0);676 677	if (!sun6i_csi_capture_format_find(pix_format->pixelformat))678		pix_format->pixelformat =679			sun6i_csi_capture_formats[0].pixelformat;680 681	width = pix_format->width;682	height = pix_format->height;683 684	info = v4l2_format_info(pix_format->pixelformat);685 686	switch (pix_format->pixelformat) {687	case V4L2_PIX_FMT_NV12_16L16:688		pix_format->bytesperline = width * 12 / 8;689		pix_format->sizeimage = pix_format->bytesperline * height;690		break;691	case V4L2_PIX_FMT_JPEG:692		pix_format->bytesperline = width;693		pix_format->sizeimage = pix_format->bytesperline * height;694		break;695	default:696		v4l2_fill_pixfmt(pix_format, pix_format->pixelformat,697				 width, height);698		break;699	}700 701	if (pix_format->field == V4L2_FIELD_ANY)702		pix_format->field = V4L2_FIELD_NONE;703 704	if (pix_format->pixelformat == V4L2_PIX_FMT_JPEG)705		pix_format->colorspace = V4L2_COLORSPACE_JPEG;706	else if (info && info->pixel_enc == V4L2_PIXEL_ENC_BAYER)707		pix_format->colorspace = V4L2_COLORSPACE_RAW;708	else709		pix_format->colorspace = V4L2_COLORSPACE_SRGB;710 711	pix_format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;712	pix_format->quantization = V4L2_QUANTIZATION_DEFAULT;713	pix_format->xfer_func = V4L2_XFER_FUNC_DEFAULT;714}715 716static int sun6i_csi_capture_querycap(struct file *file, void *private,717				      struct v4l2_capability *capability)718{719	struct sun6i_csi_device *csi_dev = video_drvdata(file);720	struct video_device *video_dev = &csi_dev->capture.video_dev;721 722	strscpy(capability->driver, SUN6I_CSI_NAME, sizeof(capability->driver));723	strscpy(capability->card, video_dev->name, sizeof(capability->card));724	snprintf(capability->bus_info, sizeof(capability->bus_info),725		 "platform:%s", dev_name(csi_dev->dev));726 727	return 0;728}729 730static int sun6i_csi_capture_enum_fmt(struct file *file, void *private,731				      struct v4l2_fmtdesc *fmtdesc)732{733	u32 index = fmtdesc->index;734 735	if (index >= ARRAY_SIZE(sun6i_csi_capture_formats))736		return -EINVAL;737 738	fmtdesc->pixelformat = sun6i_csi_capture_formats[index].pixelformat;739 740	return 0;741}742 743static int sun6i_csi_capture_g_fmt(struct file *file, void *private,744				   struct v4l2_format *format)745{746	struct sun6i_csi_device *csi_dev = video_drvdata(file);747 748	*format = csi_dev->capture.format;749 750	return 0;751}752 753static int sun6i_csi_capture_s_fmt(struct file *file, void *private,754				   struct v4l2_format *format)755{756	struct sun6i_csi_device *csi_dev = video_drvdata(file);757	struct sun6i_csi_capture *capture = &csi_dev->capture;758 759	if (vb2_is_busy(&capture->queue))760		return -EBUSY;761 762	sun6i_csi_capture_format_prepare(format);763 764	csi_dev->capture.format = *format;765 766	return 0;767}768 769static int sun6i_csi_capture_try_fmt(struct file *file, void *private,770				     struct v4l2_format *format)771{772	sun6i_csi_capture_format_prepare(format);773 774	return 0;775}776 777static int sun6i_csi_capture_enum_input(struct file *file, void *private,778					struct v4l2_input *input)779{780	if (input->index != 0)781		return -EINVAL;782 783	input->type = V4L2_INPUT_TYPE_CAMERA;784	strscpy(input->name, "Camera", sizeof(input->name));785 786	return 0;787}788 789static int sun6i_csi_capture_g_input(struct file *file, void *private,790				     unsigned int *index)791{792	*index = 0;793 794	return 0;795}796 797static int sun6i_csi_capture_s_input(struct file *file, void *private,798				     unsigned int index)799{800	if (index != 0)801		return -EINVAL;802 803	return 0;804}805 806static const struct v4l2_ioctl_ops sun6i_csi_capture_ioctl_ops = {807	.vidioc_querycap		= sun6i_csi_capture_querycap,808 809	.vidioc_enum_fmt_vid_cap	= sun6i_csi_capture_enum_fmt,810	.vidioc_g_fmt_vid_cap		= sun6i_csi_capture_g_fmt,811	.vidioc_s_fmt_vid_cap		= sun6i_csi_capture_s_fmt,812	.vidioc_try_fmt_vid_cap		= sun6i_csi_capture_try_fmt,813 814	.vidioc_enum_input		= sun6i_csi_capture_enum_input,815	.vidioc_g_input			= sun6i_csi_capture_g_input,816	.vidioc_s_input			= sun6i_csi_capture_s_input,817 818	.vidioc_create_bufs		= vb2_ioctl_create_bufs,819	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,820	.vidioc_reqbufs			= vb2_ioctl_reqbufs,821	.vidioc_querybuf		= vb2_ioctl_querybuf,822	.vidioc_expbuf			= vb2_ioctl_expbuf,823	.vidioc_qbuf			= vb2_ioctl_qbuf,824	.vidioc_dqbuf			= vb2_ioctl_dqbuf,825	.vidioc_streamon		= vb2_ioctl_streamon,826	.vidioc_streamoff		= vb2_ioctl_streamoff,827};828 829/* V4L2 File */830 831static int sun6i_csi_capture_open(struct file *file)832{833	struct sun6i_csi_device *csi_dev = video_drvdata(file);834	struct sun6i_csi_capture *capture = &csi_dev->capture;835	int ret;836 837	if (mutex_lock_interruptible(&capture->lock))838		return -ERESTARTSYS;839 840	ret = v4l2_pipeline_pm_get(&capture->video_dev.entity);841	if (ret < 0)842		goto error_lock;843 844	ret = v4l2_fh_open(file);845	if (ret < 0)846		goto error_pipeline;847 848	mutex_unlock(&capture->lock);849 850	return 0;851 852error_pipeline:853	v4l2_pipeline_pm_put(&capture->video_dev.entity);854 855error_lock:856	mutex_unlock(&capture->lock);857 858	return ret;859}860 861static int sun6i_csi_capture_close(struct file *file)862{863	struct sun6i_csi_device *csi_dev = video_drvdata(file);864	struct sun6i_csi_capture *capture = &csi_dev->capture;865 866	mutex_lock(&capture->lock);867 868	_vb2_fop_release(file, NULL);869	v4l2_pipeline_pm_put(&capture->video_dev.entity);870 871	mutex_unlock(&capture->lock);872 873	return 0;874}875 876static const struct v4l2_file_operations sun6i_csi_capture_fops = {877	.owner		= THIS_MODULE,878	.open		= sun6i_csi_capture_open,879	.release	= sun6i_csi_capture_close,880	.unlocked_ioctl	= video_ioctl2,881	.mmap		= vb2_fop_mmap,882	.poll		= vb2_fop_poll883};884 885/* Media Entity */886 887static int sun6i_csi_capture_link_validate(struct media_link *link)888{889	struct video_device *video_dev =890		media_entity_to_video_device(link->sink->entity);891	struct sun6i_csi_device *csi_dev = video_get_drvdata(video_dev);892	struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev;893	const struct sun6i_csi_capture_format *capture_format;894	const struct sun6i_csi_bridge_format *bridge_format;895	unsigned int capture_width, capture_height;896	unsigned int bridge_width, bridge_height;897	const struct v4l2_format_info *format_info;898	u32 pixelformat, capture_field;899	u32 mbus_code, bridge_field;900	bool match;901 902	sun6i_csi_capture_dimensions(csi_dev, &capture_width, &capture_height);903 904	sun6i_csi_capture_format(csi_dev, &pixelformat, &capture_field);905	capture_format = sun6i_csi_capture_format_find(pixelformat);906	if (WARN_ON(!capture_format))907		return -EINVAL;908 909	sun6i_csi_bridge_dimensions(csi_dev, &bridge_width, &bridge_height);910 911	sun6i_csi_bridge_format(csi_dev, &mbus_code, &bridge_field);912	bridge_format = sun6i_csi_bridge_format_find(mbus_code);913	if (WARN_ON(!bridge_format))914		return -EINVAL;915 916	/* No cropping/scaling is supported. */917	if (capture_width != bridge_width || capture_height != bridge_height) {918		v4l2_err(v4l2_dev,919			 "invalid input/output dimensions: %ux%u/%ux%u\n",920			 bridge_width, bridge_height, capture_width,921			 capture_height);922		return -EINVAL;923	}924 925	format_info = v4l2_format_info(pixelformat);926	/* Some formats are not listed. */927	if (!format_info)928		return 0;929 930	if (format_info->pixel_enc == V4L2_PIXEL_ENC_BAYER &&931	    bridge_format->input_format != SUN6I_CSI_INPUT_FMT_RAW)932		goto invalid;933 934	if (format_info->pixel_enc == V4L2_PIXEL_ENC_RGB &&935	    bridge_format->input_format != SUN6I_CSI_INPUT_FMT_RAW)936		goto invalid;937 938	if (format_info->pixel_enc == V4L2_PIXEL_ENC_YUV) {939		if (bridge_format->input_format != SUN6I_CSI_INPUT_FMT_YUV420 &&940		    bridge_format->input_format != SUN6I_CSI_INPUT_FMT_YUV422)941			goto invalid;942 943		/* YUV420 input can't produce YUV422 output. */944		if (bridge_format->input_format == SUN6I_CSI_INPUT_FMT_YUV420 &&945		    format_info->vdiv == 1)946			goto invalid;947	}948 949	/* With raw input mode, we need a 1:1 match between input and output. */950	if (bridge_format->input_format == SUN6I_CSI_INPUT_FMT_RAW ||951	    capture_format->input_format_raw) {952		match = sun6i_csi_capture_format_match(pixelformat, mbus_code);953		if (!match)954			goto invalid;955	}956 957	return 0;958 959invalid:960	v4l2_err(v4l2_dev, "invalid input/output format combination\n");961	return -EINVAL;962}963 964static const struct media_entity_operations sun6i_csi_capture_media_ops = {965	.link_validate = sun6i_csi_capture_link_validate966};967 968/* Capture */969 970int sun6i_csi_capture_setup(struct sun6i_csi_device *csi_dev)971{972	struct sun6i_csi_capture *capture = &csi_dev->capture;973	struct sun6i_csi_capture_state *state = &capture->state;974	struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev;975	struct v4l2_subdev *bridge_subdev = &csi_dev->bridge.subdev;976	struct video_device *video_dev = &capture->video_dev;977	struct vb2_queue *queue = &capture->queue;978	struct media_pad *pad = &capture->pad;979	struct v4l2_format *format = &csi_dev->capture.format;980	struct v4l2_pix_format *pix_format = &format->fmt.pix;981	int ret;982 983	/* This may happen with multiple bridge notifier bound calls. */984	if (state->setup)985		return 0;986 987	/* State */988 989	INIT_LIST_HEAD(&state->queue);990	spin_lock_init(&state->lock);991 992	/* Media Entity */993 994	video_dev->entity.ops = &sun6i_csi_capture_media_ops;995 996	/* Media Pad */997 998	pad->flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;999 1000	ret = media_entity_pads_init(&video_dev->entity, 1, pad);1001	if (ret < 0)1002		return ret;1003 1004	/* Queue */1005 1006	mutex_init(&capture->lock);1007 1008	queue->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;1009	queue->io_modes = VB2_MMAP | VB2_DMABUF;1010	queue->buf_struct_size = sizeof(struct sun6i_csi_buffer);1011	queue->ops = &sun6i_csi_capture_queue_ops;1012	queue->mem_ops = &vb2_dma_contig_memops;1013	queue->min_queued_buffers = 2;1014	queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;1015	queue->lock = &capture->lock;1016	queue->dev = csi_dev->dev;1017	queue->drv_priv = csi_dev;1018 1019	ret = vb2_queue_init(queue);1020	if (ret) {1021		v4l2_err(v4l2_dev, "failed to initialize vb2 queue: %d\n", ret);1022		goto error_media_entity;1023	}1024 1025	/* V4L2 Format */1026 1027	format->type = queue->type;1028	pix_format->pixelformat = sun6i_csi_capture_formats[0].pixelformat;1029	pix_format->width = 1280;1030	pix_format->height = 720;1031	pix_format->field = V4L2_FIELD_NONE;1032 1033	sun6i_csi_capture_format_prepare(format);1034 1035	/* Video Device */1036 1037	strscpy(video_dev->name, SUN6I_CSI_CAPTURE_NAME,1038		sizeof(video_dev->name));1039	video_dev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;1040	video_dev->vfl_dir = VFL_DIR_RX;1041	video_dev->release = video_device_release_empty;1042	video_dev->fops = &sun6i_csi_capture_fops;1043	video_dev->ioctl_ops = &sun6i_csi_capture_ioctl_ops;1044	video_dev->v4l2_dev = v4l2_dev;1045	video_dev->queue = queue;1046	video_dev->lock = &capture->lock;1047 1048	video_set_drvdata(video_dev, csi_dev);1049 1050	ret = video_register_device(video_dev, VFL_TYPE_VIDEO, -1);1051	if (ret < 0) {1052		v4l2_err(v4l2_dev, "failed to register video device: %d\n",1053			 ret);1054		goto error_media_entity;1055	}1056 1057	/* Media Pad Link */1058 1059	ret = media_create_pad_link(&bridge_subdev->entity,1060				    SUN6I_CSI_BRIDGE_PAD_SOURCE,1061				    &video_dev->entity, 0,1062				    csi_dev->isp_available ? 0 :1063				    MEDIA_LNK_FL_ENABLED |1064				    MEDIA_LNK_FL_IMMUTABLE);1065	if (ret < 0) {1066		v4l2_err(v4l2_dev, "failed to create %s:%u -> %s:%u link\n",1067			 bridge_subdev->entity.name,1068			 SUN6I_CSI_BRIDGE_PAD_SOURCE,1069			 video_dev->entity.name, 0);1070		goto error_video_device;1071	}1072 1073	state->setup = true;1074 1075	return 0;1076 1077error_video_device:1078	vb2_video_unregister_device(video_dev);1079 1080error_media_entity:1081	media_entity_cleanup(&video_dev->entity);1082 1083	mutex_destroy(&capture->lock);1084 1085	return ret;1086}1087 1088void sun6i_csi_capture_cleanup(struct sun6i_csi_device *csi_dev)1089{1090	struct sun6i_csi_capture *capture = &csi_dev->capture;1091	struct video_device *video_dev = &capture->video_dev;1092 1093	/* This may happen if async registration failed to complete. */1094	if (!capture->state.setup)1095		return;1096 1097	vb2_video_unregister_device(video_dev);1098	media_entity_cleanup(&video_dev->entity);1099	mutex_destroy(&capture->lock);1100 1101	capture->state.setup = false;1102}1103