xref: /openbmc/linux/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c (revision 5ee9cd065836e5934710ca35653bce7905add20b)
1e6938cc1SHelen Koike // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
2e6938cc1SHelen Koike /*
3e6938cc1SHelen Koike  * Rockchip ISP1 Driver - ISP Subdevice
4e6938cc1SHelen Koike  *
5e6938cc1SHelen Koike  * Copyright (C) 2019 Collabora, Ltd.
6e6938cc1SHelen Koike  *
7e6938cc1SHelen Koike  * Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd.
8e6938cc1SHelen Koike  * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
9e6938cc1SHelen Koike  */
10e6938cc1SHelen Koike 
11e6938cc1SHelen Koike #include <linux/iopoll.h>
12e6938cc1SHelen Koike #include <linux/pm_runtime.h>
13e6938cc1SHelen Koike #include <linux/videodev2.h>
14e6938cc1SHelen Koike #include <linux/vmalloc.h>
15117368f0SLaurent Pinchart 
16e6938cc1SHelen Koike #include <media/v4l2-event.h>
17e6938cc1SHelen Koike 
18e6938cc1SHelen Koike #include "rkisp1-common.h"
19e6938cc1SHelen Koike 
20e6938cc1SHelen Koike #define RKISP1_DEF_SINK_PAD_FMT MEDIA_BUS_FMT_SRGGB10_1X10
21e6938cc1SHelen Koike #define RKISP1_DEF_SRC_PAD_FMT MEDIA_BUS_FMT_YUYV8_2X8
22e6938cc1SHelen Koike 
23e6938cc1SHelen Koike #define RKISP1_ISP_DEV_NAME	RKISP1_DRIVER_NAME "_isp"
24e6938cc1SHelen Koike 
25e6938cc1SHelen Koike /*
26e6938cc1SHelen Koike  * NOTE: MIPI controller and input MUX are also configured in this file.
27e6938cc1SHelen Koike  * This is because ISP Subdev describes not only ISP submodule (input size,
28e6938cc1SHelen Koike  * format, output size, format), but also a virtual route device.
29e6938cc1SHelen Koike  */
30e6938cc1SHelen Koike 
31e6938cc1SHelen Koike /*
32e6938cc1SHelen Koike  * There are many variables named with format/frame in below code,
33e6938cc1SHelen Koike  * please see here for their meaning.
34e6938cc1SHelen Koike  * Cropping in the sink pad defines the image region from the sensor.
35e6938cc1SHelen Koike  * Cropping in the source pad defines the region for the Image Stabilizer (IS)
36e6938cc1SHelen Koike  *
37e6938cc1SHelen Koike  * Cropping regions of ISP
38e6938cc1SHelen Koike  *
39e6938cc1SHelen Koike  * +---------------------------------------------------------+
40e6938cc1SHelen Koike  * | Sensor image                                            |
41e6938cc1SHelen Koike  * | +---------------------------------------------------+   |
42e6938cc1SHelen Koike  * | | CIF_ISP_ACQ (for black level)                     |   |
43e6938cc1SHelen Koike  * | | sink pad format                                   |   |
44e6938cc1SHelen Koike  * | | +--------------------------------------------+    |   |
45e6938cc1SHelen Koike  * | | |    CIF_ISP_OUT                             |    |   |
46e6938cc1SHelen Koike  * | | |    sink pad crop                           |    |   |
47e6938cc1SHelen Koike  * | | |    +---------------------------------+     |    |   |
48e6938cc1SHelen Koike  * | | |    |   CIF_ISP_IS                    |     |    |   |
49e6938cc1SHelen Koike  * | | |    |   source pad crop and format    |     |    |   |
50e6938cc1SHelen Koike  * | | |    +---------------------------------+     |    |   |
51e6938cc1SHelen Koike  * | | +--------------------------------------------+    |   |
52e6938cc1SHelen Koike  * | +---------------------------------------------------+   |
53e6938cc1SHelen Koike  * +---------------------------------------------------------+
54e6938cc1SHelen Koike  */
55e6938cc1SHelen Koike 
56e6938cc1SHelen Koike /* ----------------------------------------------------------------------------
57e6938cc1SHelen Koike  * Helpers
58e6938cc1SHelen Koike  */
59e6938cc1SHelen Koike 
60e6938cc1SHelen Koike static struct v4l2_mbus_framefmt *
rkisp1_isp_get_pad_fmt(struct rkisp1_isp * isp,struct v4l2_subdev_state * sd_state,unsigned int pad,u32 which)61e6938cc1SHelen Koike rkisp1_isp_get_pad_fmt(struct rkisp1_isp *isp,
620d346d2aSTomi Valkeinen 		       struct v4l2_subdev_state *sd_state,
63e6938cc1SHelen Koike 		       unsigned int pad, u32 which)
64e6938cc1SHelen Koike {
650d346d2aSTomi Valkeinen 	struct v4l2_subdev_state state = {
660d346d2aSTomi Valkeinen 		.pads = isp->pad_cfg
670d346d2aSTomi Valkeinen 	};
68e3ab7e20SLaurent Pinchart 
69e6938cc1SHelen Koike 	if (which == V4L2_SUBDEV_FORMAT_TRY)
700d346d2aSTomi Valkeinen 		return v4l2_subdev_get_try_format(&isp->sd, sd_state, pad);
71e6938cc1SHelen Koike 	else
720d346d2aSTomi Valkeinen 		return v4l2_subdev_get_try_format(&isp->sd, &state, pad);
73e6938cc1SHelen Koike }
74e6938cc1SHelen Koike 
75e6938cc1SHelen Koike static struct v4l2_rect *
rkisp1_isp_get_pad_crop(struct rkisp1_isp * isp,struct v4l2_subdev_state * sd_state,unsigned int pad,u32 which)76e6938cc1SHelen Koike rkisp1_isp_get_pad_crop(struct rkisp1_isp *isp,
770d346d2aSTomi Valkeinen 			struct v4l2_subdev_state *sd_state,
78e6938cc1SHelen Koike 			unsigned int pad, u32 which)
79e6938cc1SHelen Koike {
800d346d2aSTomi Valkeinen 	struct v4l2_subdev_state state = {
810d346d2aSTomi Valkeinen 		.pads = isp->pad_cfg
820d346d2aSTomi Valkeinen 	};
83e3ab7e20SLaurent Pinchart 
84e6938cc1SHelen Koike 	if (which == V4L2_SUBDEV_FORMAT_TRY)
850d346d2aSTomi Valkeinen 		return v4l2_subdev_get_try_crop(&isp->sd, sd_state, pad);
86e6938cc1SHelen Koike 	else
870d346d2aSTomi Valkeinen 		return v4l2_subdev_get_try_crop(&isp->sd, &state, pad);
88e6938cc1SHelen Koike }
89e6938cc1SHelen Koike 
90e6938cc1SHelen Koike /* ----------------------------------------------------------------------------
91e6938cc1SHelen Koike  * Camera Interface registers configurations
92e6938cc1SHelen Koike  */
93e6938cc1SHelen Koike 
94e6938cc1SHelen Koike /*
95e6938cc1SHelen Koike  * Image Stabilization.
96e6938cc1SHelen Koike  * This should only be called when configuring CIF
97e6938cc1SHelen Koike  * or at the frame end interrupt
98e6938cc1SHelen Koike  */
rkisp1_config_ism(struct rkisp1_isp * isp)99bba100dfSLaurent Pinchart static void rkisp1_config_ism(struct rkisp1_isp *isp)
100e6938cc1SHelen Koike {
10176302581SLaurent Pinchart 	const struct v4l2_rect *src_crop =
102bba100dfSLaurent Pinchart 		rkisp1_isp_get_pad_crop(isp, NULL,
103e6938cc1SHelen Koike 					RKISP1_ISP_PAD_SOURCE_VIDEO,
104e6938cc1SHelen Koike 					V4L2_SUBDEV_FORMAT_ACTIVE);
105bba100dfSLaurent Pinchart 	struct rkisp1_device *rkisp1 = isp->rkisp1;
106e6938cc1SHelen Koike 	u32 val;
107e6938cc1SHelen Koike 
1080ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_RECENTER, 0);
1090ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_MAX_DX, 0);
1100ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_MAX_DY, 0);
1110ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_DISPLACE, 0);
1120ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_H_OFFS, src_crop->left);
1130ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_V_OFFS, src_crop->top);
1140ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_H_SIZE, src_crop->width);
1150ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_V_SIZE, src_crop->height);
116e6938cc1SHelen Koike 
117e6938cc1SHelen Koike 	/* IS(Image Stabilization) is always on, working as output crop */
1180ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_CTRL, 1);
119e6938cc1SHelen Koike 	val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_CTRL);
120e6938cc1SHelen Koike 	val |= RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD;
1210ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val);
122e6938cc1SHelen Koike }
123e6938cc1SHelen Koike 
124e6938cc1SHelen Koike /*
125e6938cc1SHelen Koike  * configure ISP blocks with input format, size......
126e6938cc1SHelen Koike  */
rkisp1_config_isp(struct rkisp1_isp * isp,enum v4l2_mbus_type mbus_type,u32 mbus_flags)127bba100dfSLaurent Pinchart static int rkisp1_config_isp(struct rkisp1_isp *isp,
128055972a0SLaurent Pinchart 			     enum v4l2_mbus_type mbus_type, u32 mbus_flags)
129e6938cc1SHelen Koike {
130bba100dfSLaurent Pinchart 	struct rkisp1_device *rkisp1 = isp->rkisp1;
131ce5dd024SLaurent Pinchart 	u32 isp_ctrl = 0, irq_mask = 0, acq_mult = 0, acq_prop = 0;
132c16f97acSLaurent Pinchart 	const struct rkisp1_mbus_info *sink_fmt = isp->sink_fmt;
133c16f97acSLaurent Pinchart 	const struct rkisp1_mbus_info *src_fmt = isp->src_fmt;
13476302581SLaurent Pinchart 	const struct v4l2_mbus_framefmt *sink_frm;
13576302581SLaurent Pinchart 	const struct v4l2_rect *sink_crop;
136e6938cc1SHelen Koike 
137bba100dfSLaurent Pinchart 	sink_frm = rkisp1_isp_get_pad_fmt(isp, NULL,
138e6938cc1SHelen Koike 					  RKISP1_ISP_PAD_SINK_VIDEO,
139e6938cc1SHelen Koike 					  V4L2_SUBDEV_FORMAT_ACTIVE);
140bba100dfSLaurent Pinchart 	sink_crop = rkisp1_isp_get_pad_crop(isp, NULL,
141e6938cc1SHelen Koike 					    RKISP1_ISP_PAD_SINK_VIDEO,
142e6938cc1SHelen Koike 					    V4L2_SUBDEV_FORMAT_ACTIVE);
143e6938cc1SHelen Koike 
144e6938cc1SHelen Koike 	if (sink_fmt->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
145e6938cc1SHelen Koike 		acq_mult = 1;
146e6938cc1SHelen Koike 		if (src_fmt->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
147055972a0SLaurent Pinchart 			if (mbus_type == V4L2_MBUS_BT656)
148e6938cc1SHelen Koike 				isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_RAW_PICT_ITU656;
149e6938cc1SHelen Koike 			else
150e6938cc1SHelen Koike 				isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_RAW_PICT;
151e6938cc1SHelen Koike 		} else {
1520ef7dc30SLaurent Pinchart 			rkisp1_write(rkisp1, RKISP1_CIF_ISP_DEMOSAIC,
1530ef7dc30SLaurent Pinchart 				     RKISP1_CIF_ISP_DEMOSAIC_TH(0xc));
154e6938cc1SHelen Koike 
155055972a0SLaurent Pinchart 			if (mbus_type == V4L2_MBUS_BT656)
156e6938cc1SHelen Koike 				isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_BAYER_ITU656;
157e6938cc1SHelen Koike 			else
158e6938cc1SHelen Koike 				isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_BAYER_ITU601;
159e6938cc1SHelen Koike 		}
160e6938cc1SHelen Koike 	} else if (sink_fmt->pixel_enc == V4L2_PIXEL_ENC_YUV) {
161e6938cc1SHelen Koike 		acq_mult = 2;
162055972a0SLaurent Pinchart 		if (mbus_type == V4L2_MBUS_CSI2_DPHY) {
163e6938cc1SHelen Koike 			isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_ITU601;
164e6938cc1SHelen Koike 		} else {
165055972a0SLaurent Pinchart 			if (mbus_type == V4L2_MBUS_BT656)
166e6938cc1SHelen Koike 				isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_ITU656;
167e6938cc1SHelen Koike 			else
168e6938cc1SHelen Koike 				isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_ITU601;
169e6938cc1SHelen Koike 		}
170e6938cc1SHelen Koike 
171e6938cc1SHelen Koike 		irq_mask |= RKISP1_CIF_ISP_DATA_LOSS;
172e6938cc1SHelen Koike 	}
173e6938cc1SHelen Koike 
174e6938cc1SHelen Koike 	/* Set up input acquisition properties */
175055972a0SLaurent Pinchart 	if (mbus_type == V4L2_MBUS_BT656 || mbus_type == V4L2_MBUS_PARALLEL) {
176055972a0SLaurent Pinchart 		if (mbus_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
177ce5dd024SLaurent Pinchart 			acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_POS_EDGE;
178b6ee2a5aSLaurent Pinchart 
179b6ee2a5aSLaurent Pinchart 		switch (sink_fmt->bus_width) {
180b6ee2a5aSLaurent Pinchart 		case 8:
181ce5dd024SLaurent Pinchart 			acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_8B_ZERO;
182b6ee2a5aSLaurent Pinchart 			break;
183b6ee2a5aSLaurent Pinchart 		case 10:
184ce5dd024SLaurent Pinchart 			acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_10B_ZERO;
185b6ee2a5aSLaurent Pinchart 			break;
186b6ee2a5aSLaurent Pinchart 		case 12:
187ce5dd024SLaurent Pinchart 			acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_12B;
188b6ee2a5aSLaurent Pinchart 			break;
189b6ee2a5aSLaurent Pinchart 		default:
190b6ee2a5aSLaurent Pinchart 			dev_err(rkisp1->dev, "Invalid bus width %u\n",
191b6ee2a5aSLaurent Pinchart 				sink_fmt->bus_width);
192b6ee2a5aSLaurent Pinchart 			return -EINVAL;
193b6ee2a5aSLaurent Pinchart 		}
194e6938cc1SHelen Koike 	}
195e6938cc1SHelen Koike 
196055972a0SLaurent Pinchart 	if (mbus_type == V4L2_MBUS_PARALLEL) {
197055972a0SLaurent Pinchart 		if (mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
198ce5dd024SLaurent Pinchart 			acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_VSYNC_LOW;
199e6938cc1SHelen Koike 
200055972a0SLaurent Pinchart 		if (mbus_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
201ce5dd024SLaurent Pinchart 			acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_HSYNC_LOW;
202e6938cc1SHelen Koike 	}
203e6938cc1SHelen Koike 
2040ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, isp_ctrl);
2050ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_PROP,
206ce5dd024SLaurent Pinchart 		     acq_prop | sink_fmt->yuv_seq |
207e6938cc1SHelen Koike 		     RKISP1_CIF_ISP_ACQ_PROP_BAYER_PAT(sink_fmt->bayer_pat) |
2080ef7dc30SLaurent Pinchart 		     RKISP1_CIF_ISP_ACQ_PROP_FIELD_SEL_ALL);
2090ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_NR_FRAMES, 0);
210e6938cc1SHelen Koike 
211e6938cc1SHelen Koike 	/* Acquisition Size */
2120ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_H_OFFS, 0);
2130ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_V_OFFS, 0);
2140ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_H_SIZE,
2150ef7dc30SLaurent Pinchart 		     acq_mult * sink_frm->width);
2160ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_V_SIZE, sink_frm->height);
217e6938cc1SHelen Koike 
218e6938cc1SHelen Koike 	/* ISP Out Area */
2190ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_OUT_H_OFFS, sink_crop->left);
2200ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_OUT_V_OFFS, sink_crop->top);
2210ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_OUT_H_SIZE, sink_crop->width);
2220ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_OUT_V_SIZE, sink_crop->height);
223e6938cc1SHelen Koike 
224e6938cc1SHelen Koike 	irq_mask |= RKISP1_CIF_ISP_FRAME | RKISP1_CIF_ISP_V_START |
225e6938cc1SHelen Koike 		    RKISP1_CIF_ISP_PIC_SIZE_ERROR;
2260ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_IMSC, irq_mask);
227e6938cc1SHelen Koike 
228e6938cc1SHelen Koike 	if (src_fmt->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
229e6938cc1SHelen Koike 		rkisp1_params_disable(&rkisp1->params);
230e6938cc1SHelen Koike 	} else {
231e6938cc1SHelen Koike 		struct v4l2_mbus_framefmt *src_frm;
232e6938cc1SHelen Koike 
233bba100dfSLaurent Pinchart 		src_frm = rkisp1_isp_get_pad_fmt(isp, NULL,
23481303962SLaurent Pinchart 						 RKISP1_ISP_PAD_SOURCE_VIDEO,
235e6938cc1SHelen Koike 						 V4L2_SUBDEV_FORMAT_ACTIVE);
2364b07e2b8SLaurent Pinchart 		rkisp1_params_pre_configure(&rkisp1->params, sink_fmt->bayer_pat,
237f7aa2d23SLaurent Pinchart 					    src_frm->quantization,
238f7aa2d23SLaurent Pinchart 					    src_frm->ycbcr_enc);
239e6938cc1SHelen Koike 	}
240e6938cc1SHelen Koike 
241e6938cc1SHelen Koike 	return 0;
242e6938cc1SHelen Koike }
243e6938cc1SHelen Koike 
244e6938cc1SHelen Koike /* Configure MUX */
rkisp1_config_path(struct rkisp1_isp * isp,enum v4l2_mbus_type mbus_type)245b6ee2a5aSLaurent Pinchart static void rkisp1_config_path(struct rkisp1_isp *isp,
246055972a0SLaurent Pinchart 			       enum v4l2_mbus_type mbus_type)
247e6938cc1SHelen Koike {
248bba100dfSLaurent Pinchart 	struct rkisp1_device *rkisp1 = isp->rkisp1;
249e6938cc1SHelen Koike 	u32 dpcl = rkisp1_read(rkisp1, RKISP1_CIF_VI_DPCL);
250e6938cc1SHelen Koike 
251b6ee2a5aSLaurent Pinchart 	if (mbus_type == V4L2_MBUS_BT656 || mbus_type == V4L2_MBUS_PARALLEL)
252e6938cc1SHelen Koike 		dpcl |= RKISP1_CIF_VI_DPCL_IF_SEL_PARALLEL;
253b6ee2a5aSLaurent Pinchart 	else if (mbus_type == V4L2_MBUS_CSI2_DPHY)
254e6938cc1SHelen Koike 		dpcl |= RKISP1_CIF_VI_DPCL_IF_SEL_MIPI;
255e6938cc1SHelen Koike 
2560ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_VI_DPCL, dpcl);
257e6938cc1SHelen Koike }
258e6938cc1SHelen Koike 
259e6938cc1SHelen Koike /* Hardware configure Entry */
rkisp1_config_cif(struct rkisp1_isp * isp,enum v4l2_mbus_type mbus_type,u32 mbus_flags)260bba100dfSLaurent Pinchart static int rkisp1_config_cif(struct rkisp1_isp *isp,
261055972a0SLaurent Pinchart 			     enum v4l2_mbus_type mbus_type, u32 mbus_flags)
262e6938cc1SHelen Koike {
263e6938cc1SHelen Koike 	int ret;
264e6938cc1SHelen Koike 
265bba100dfSLaurent Pinchart 	ret = rkisp1_config_isp(isp, mbus_type, mbus_flags);
266e6938cc1SHelen Koike 	if (ret)
267e6938cc1SHelen Koike 		return ret;
268b6ee2a5aSLaurent Pinchart 
269b6ee2a5aSLaurent Pinchart 	rkisp1_config_path(isp, mbus_type);
270bba100dfSLaurent Pinchart 	rkisp1_config_ism(isp);
271e6938cc1SHelen Koike 
272e6938cc1SHelen Koike 	return 0;
273e6938cc1SHelen Koike }
274e6938cc1SHelen Koike 
rkisp1_isp_stop(struct rkisp1_isp * isp)275bba100dfSLaurent Pinchart static void rkisp1_isp_stop(struct rkisp1_isp *isp)
276e6938cc1SHelen Koike {
277bba100dfSLaurent Pinchart 	struct rkisp1_device *rkisp1 = isp->rkisp1;
278e6938cc1SHelen Koike 	u32 val;
279e6938cc1SHelen Koike 
280e6938cc1SHelen Koike 	/*
281e6938cc1SHelen Koike 	 * ISP(mi) stop in mi frame end -> Stop ISP(mipi) ->
282e6938cc1SHelen Koike 	 * Stop ISP(isp) ->wait for ISP isp off
283e6938cc1SHelen Koike 	 */
284e6938cc1SHelen Koike 
285fab48343STomi Valkeinen 	/* Mask MI and ISP interrupts */
286fab48343STomi Valkeinen 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_IMSC, 0);
2870ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_MI_IMSC, 0);
288fab48343STomi Valkeinen 
289fab48343STomi Valkeinen 	/* Flush posted writes */
290fab48343STomi Valkeinen 	rkisp1_read(rkisp1, RKISP1_CIF_MI_IMSC);
291fab48343STomi Valkeinen 
292fab48343STomi Valkeinen 	/*
293fab48343STomi Valkeinen 	 * Wait until the IRQ handler has ended. The IRQ handler may get called
294fab48343STomi Valkeinen 	 * even after this, but it will return immediately as the MI and ISP
295fab48343STomi Valkeinen 	 * interrupts have been masked.
296fab48343STomi Valkeinen 	 */
297fab48343STomi Valkeinen 	synchronize_irq(rkisp1->irqs[RKISP1_IRQ_ISP]);
298fab48343STomi Valkeinen 	if (rkisp1->irqs[RKISP1_IRQ_ISP] != rkisp1->irqs[RKISP1_IRQ_MI])
299fab48343STomi Valkeinen 		synchronize_irq(rkisp1->irqs[RKISP1_IRQ_MI]);
300fab48343STomi Valkeinen 
301fab48343STomi Valkeinen 	/* Clear MI and ISP interrupt status */
302fab48343STomi Valkeinen 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_ICR, ~0);
3030ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_MI_ICR, ~0);
3048082e2f4SPaul Elder 
305e6938cc1SHelen Koike 	/* stop ISP */
306e6938cc1SHelen Koike 	val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_CTRL);
307e6938cc1SHelen Koike 	val &= ~(RKISP1_CIF_ISP_CTRL_ISP_INFORM_ENABLE |
308e6938cc1SHelen Koike 		 RKISP1_CIF_ISP_CTRL_ISP_ENABLE);
3090ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val);
310e6938cc1SHelen Koike 
311e6938cc1SHelen Koike 	val = rkisp1_read(rkisp1,	RKISP1_CIF_ISP_CTRL);
3120ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL,
3130ef7dc30SLaurent Pinchart 		     val | RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD);
314e6938cc1SHelen Koike 
315e6938cc1SHelen Koike 	readx_poll_timeout(readl, rkisp1->base_addr + RKISP1_CIF_ISP_RIS,
316e6938cc1SHelen Koike 			   val, val & RKISP1_CIF_ISP_OFF, 20, 100);
3170ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_VI_IRCL,
3183b430c2cSLaurent Pinchart 		     RKISP1_CIF_VI_IRCL_MIPI_SW_RST |
3190ef7dc30SLaurent Pinchart 		     RKISP1_CIF_VI_IRCL_ISP_SW_RST);
3200ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_VI_IRCL, 0x0);
321e6938cc1SHelen Koike }
322e6938cc1SHelen Koike 
rkisp1_config_clk(struct rkisp1_isp * isp)323bba100dfSLaurent Pinchart static void rkisp1_config_clk(struct rkisp1_isp *isp)
324e6938cc1SHelen Koike {
325bba100dfSLaurent Pinchart 	struct rkisp1_device *rkisp1 = isp->rkisp1;
326bba100dfSLaurent Pinchart 
3273b430c2cSLaurent Pinchart 	u32 val = RKISP1_CIF_VI_ICCL_ISP_CLK | RKISP1_CIF_VI_ICCL_CP_CLK |
3283b430c2cSLaurent Pinchart 		  RKISP1_CIF_VI_ICCL_MRSZ_CLK | RKISP1_CIF_VI_ICCL_SRSZ_CLK |
3293b430c2cSLaurent Pinchart 		  RKISP1_CIF_VI_ICCL_JPEG_CLK | RKISP1_CIF_VI_ICCL_MI_CLK |
3303b430c2cSLaurent Pinchart 		  RKISP1_CIF_VI_ICCL_IE_CLK | RKISP1_CIF_VI_ICCL_MIPI_CLK |
3313b430c2cSLaurent Pinchart 		  RKISP1_CIF_VI_ICCL_DCROP_CLK;
332e6938cc1SHelen Koike 
3330ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_VI_ICCL, val);
334cd42f802SHeiko Stuebner 
335cd42f802SHeiko Stuebner 	/* ensure sp and mp can run at the same time in V12 */
3361195b18cSLaurent Pinchart 	if (rkisp1->info->isp_ver == RKISP1_V12) {
337cd42f802SHeiko Stuebner 		val = RKISP1_CIF_CLK_CTRL_MI_Y12 | RKISP1_CIF_CLK_CTRL_MI_SP |
338cd42f802SHeiko Stuebner 		      RKISP1_CIF_CLK_CTRL_MI_RAW0 | RKISP1_CIF_CLK_CTRL_MI_RAW1 |
339cd42f802SHeiko Stuebner 		      RKISP1_CIF_CLK_CTRL_MI_READ | RKISP1_CIF_CLK_CTRL_MI_RAWRD |
340cd42f802SHeiko Stuebner 		      RKISP1_CIF_CLK_CTRL_CP | RKISP1_CIF_CLK_CTRL_IE;
3410ef7dc30SLaurent Pinchart 		rkisp1_write(rkisp1, RKISP1_CIF_VI_ISP_CLK_CTRL_V12, val);
342cd42f802SHeiko Stuebner 	}
343e6938cc1SHelen Koike }
344e6938cc1SHelen Koike 
rkisp1_isp_start(struct rkisp1_isp * isp)345bba100dfSLaurent Pinchart static void rkisp1_isp_start(struct rkisp1_isp *isp)
346e6938cc1SHelen Koike {
347bba100dfSLaurent Pinchart 	struct rkisp1_device *rkisp1 = isp->rkisp1;
348e6938cc1SHelen Koike 	u32 val;
349e6938cc1SHelen Koike 
350bba100dfSLaurent Pinchart 	rkisp1_config_clk(isp);
351e6938cc1SHelen Koike 
352e6938cc1SHelen Koike 	/* Activate ISP */
353e6938cc1SHelen Koike 	val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_CTRL);
354e6938cc1SHelen Koike 	val |= RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD |
355e6938cc1SHelen Koike 	       RKISP1_CIF_ISP_CTRL_ISP_ENABLE |
356e6938cc1SHelen Koike 	       RKISP1_CIF_ISP_CTRL_ISP_INFORM_ENABLE;
3570ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val);
3584b07e2b8SLaurent Pinchart 
3594b07e2b8SLaurent Pinchart 	if (isp->src_fmt->pixel_enc != V4L2_PIXEL_ENC_BAYER)
3604b07e2b8SLaurent Pinchart 		rkisp1_params_post_configure(&rkisp1->params);
361e6938cc1SHelen Koike }
362e6938cc1SHelen Koike 
363e6938cc1SHelen Koike /* ----------------------------------------------------------------------------
364e6938cc1SHelen Koike  * Subdev pad operations
365e6938cc1SHelen Koike  */
366e6938cc1SHelen Koike 
to_rkisp1_isp(struct v4l2_subdev * sd)3678c1aa197SLaurent Pinchart static inline struct rkisp1_isp *to_rkisp1_isp(struct v4l2_subdev *sd)
3688c1aa197SLaurent Pinchart {
3698c1aa197SLaurent Pinchart 	return container_of(sd, struct rkisp1_isp, sd);
3708c1aa197SLaurent Pinchart }
3718c1aa197SLaurent Pinchart 
rkisp1_isp_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)372e6938cc1SHelen Koike static int rkisp1_isp_enum_mbus_code(struct v4l2_subdev *sd,
3730d346d2aSTomi Valkeinen 				     struct v4l2_subdev_state *sd_state,
374e6938cc1SHelen Koike 				     struct v4l2_subdev_mbus_code_enum *code)
375e6938cc1SHelen Koike {
376e6938cc1SHelen Koike 	unsigned int i, dir;
377e6938cc1SHelen Koike 	int pos = 0;
378e6938cc1SHelen Koike 
379e6938cc1SHelen Koike 	if (code->pad == RKISP1_ISP_PAD_SINK_VIDEO) {
380e6938cc1SHelen Koike 		dir = RKISP1_ISP_SD_SINK;
381e6938cc1SHelen Koike 	} else if (code->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) {
382e6938cc1SHelen Koike 		dir = RKISP1_ISP_SD_SRC;
383e6938cc1SHelen Koike 	} else {
384e6938cc1SHelen Koike 		if (code->index > 0)
385e6938cc1SHelen Koike 			return -EINVAL;
386e6938cc1SHelen Koike 		code->code = MEDIA_BUS_FMT_METADATA_FIXED;
387e6938cc1SHelen Koike 		return 0;
388e6938cc1SHelen Koike 	}
389e6938cc1SHelen Koike 
3900f3c2ab2SPaul Elder 	for (i = 0; ; i++) {
3910f3c2ab2SPaul Elder 		const struct rkisp1_mbus_info *fmt =
3920f3c2ab2SPaul Elder 			rkisp1_mbus_info_get_by_index(i);
393e6938cc1SHelen Koike 
3940f3c2ab2SPaul Elder 		if (!fmt)
3950f3c2ab2SPaul Elder 			return -EINVAL;
396e6938cc1SHelen Koike 
397e6938cc1SHelen Koike 		if (fmt->direction & dir)
398e6938cc1SHelen Koike 			pos++;
399e6938cc1SHelen Koike 
400e6938cc1SHelen Koike 		if (code->index == pos - 1) {
401e6938cc1SHelen Koike 			code->code = fmt->mbus_code;
402e6938cc1SHelen Koike 			if (fmt->pixel_enc == V4L2_PIXEL_ENC_YUV &&
403e6938cc1SHelen Koike 			    dir == RKISP1_ISP_SD_SRC)
404e6938cc1SHelen Koike 				code->flags =
405e6938cc1SHelen Koike 					V4L2_SUBDEV_MBUS_CODE_CSC_QUANTIZATION;
406e6938cc1SHelen Koike 			return 0;
407e6938cc1SHelen Koike 		}
408e6938cc1SHelen Koike 	}
409e6938cc1SHelen Koike 
410e6938cc1SHelen Koike 	return -EINVAL;
411e6938cc1SHelen Koike }
412e6938cc1SHelen Koike 
rkisp1_isp_enum_frame_size(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_size_enum * fse)4134fc81486SSebastian Fricke static int rkisp1_isp_enum_frame_size(struct v4l2_subdev *sd,
4140d346d2aSTomi Valkeinen 				      struct v4l2_subdev_state *sd_state,
4154fc81486SSebastian Fricke 				      struct v4l2_subdev_frame_size_enum *fse)
4164fc81486SSebastian Fricke {
4170f3c2ab2SPaul Elder 	const struct rkisp1_mbus_info *mbus_info;
4184fc81486SSebastian Fricke 
4194fc81486SSebastian Fricke 	if (fse->pad == RKISP1_ISP_PAD_SINK_PARAMS ||
4204fc81486SSebastian Fricke 	    fse->pad == RKISP1_ISP_PAD_SOURCE_STATS)
4214fc81486SSebastian Fricke 		return -ENOTTY;
4224fc81486SSebastian Fricke 
4234fc81486SSebastian Fricke 	if (fse->index > 0)
4244fc81486SSebastian Fricke 		return -EINVAL;
4254fc81486SSebastian Fricke 
4260f3c2ab2SPaul Elder 	mbus_info = rkisp1_mbus_info_get_by_code(fse->code);
4274fc81486SSebastian Fricke 	if (!mbus_info)
4284fc81486SSebastian Fricke 		return -EINVAL;
4294fc81486SSebastian Fricke 
4304fc81486SSebastian Fricke 	if (!(mbus_info->direction & RKISP1_ISP_SD_SINK) &&
4314fc81486SSebastian Fricke 	    fse->pad == RKISP1_ISP_PAD_SINK_VIDEO)
4324fc81486SSebastian Fricke 		return -EINVAL;
4334fc81486SSebastian Fricke 
4344fc81486SSebastian Fricke 	if (!(mbus_info->direction & RKISP1_ISP_SD_SRC) &&
4354fc81486SSebastian Fricke 	    fse->pad == RKISP1_ISP_PAD_SOURCE_VIDEO)
4364fc81486SSebastian Fricke 		return -EINVAL;
4374fc81486SSebastian Fricke 
4384fc81486SSebastian Fricke 	fse->min_width = RKISP1_ISP_MIN_WIDTH;
4394fc81486SSebastian Fricke 	fse->max_width = RKISP1_ISP_MAX_WIDTH;
4404fc81486SSebastian Fricke 	fse->min_height = RKISP1_ISP_MIN_HEIGHT;
4414fc81486SSebastian Fricke 	fse->max_height = RKISP1_ISP_MAX_HEIGHT;
4424fc81486SSebastian Fricke 
4434fc81486SSebastian Fricke 	return 0;
4444fc81486SSebastian Fricke }
4454fc81486SSebastian Fricke 
rkisp1_isp_init_config(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state)446e6938cc1SHelen Koike static int rkisp1_isp_init_config(struct v4l2_subdev *sd,
4470d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state)
448e6938cc1SHelen Koike {
449e6938cc1SHelen Koike 	struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
450e6938cc1SHelen Koike 	struct v4l2_rect *sink_crop, *src_crop;
451e6938cc1SHelen Koike 
45287bfaa1aSLaurent Pinchart 	/* Video. */
4530d346d2aSTomi Valkeinen 	sink_fmt = v4l2_subdev_get_try_format(sd, sd_state,
454e6938cc1SHelen Koike 					      RKISP1_ISP_PAD_SINK_VIDEO);
455e6938cc1SHelen Koike 	sink_fmt->width = RKISP1_DEFAULT_WIDTH;
456e6938cc1SHelen Koike 	sink_fmt->height = RKISP1_DEFAULT_HEIGHT;
457e6938cc1SHelen Koike 	sink_fmt->field = V4L2_FIELD_NONE;
458e6938cc1SHelen Koike 	sink_fmt->code = RKISP1_DEF_SINK_PAD_FMT;
45987bfaa1aSLaurent Pinchart 	sink_fmt->colorspace = V4L2_COLORSPACE_RAW;
46087bfaa1aSLaurent Pinchart 	sink_fmt->xfer_func = V4L2_XFER_FUNC_NONE;
46187bfaa1aSLaurent Pinchart 	sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
46287bfaa1aSLaurent Pinchart 	sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
463e6938cc1SHelen Koike 
4640d346d2aSTomi Valkeinen 	sink_crop = v4l2_subdev_get_try_crop(sd, sd_state,
465e6938cc1SHelen Koike 					     RKISP1_ISP_PAD_SINK_VIDEO);
466e6938cc1SHelen Koike 	sink_crop->width = RKISP1_DEFAULT_WIDTH;
467e6938cc1SHelen Koike 	sink_crop->height = RKISP1_DEFAULT_HEIGHT;
468e6938cc1SHelen Koike 	sink_crop->left = 0;
469e6938cc1SHelen Koike 	sink_crop->top = 0;
470e6938cc1SHelen Koike 
4710d346d2aSTomi Valkeinen 	src_fmt = v4l2_subdev_get_try_format(sd, sd_state,
472e6938cc1SHelen Koike 					     RKISP1_ISP_PAD_SOURCE_VIDEO);
473e6938cc1SHelen Koike 	*src_fmt = *sink_fmt;
474e6938cc1SHelen Koike 	src_fmt->code = RKISP1_DEF_SRC_PAD_FMT;
47587bfaa1aSLaurent Pinchart 	src_fmt->colorspace = V4L2_COLORSPACE_SRGB;
47687bfaa1aSLaurent Pinchart 	src_fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
47787bfaa1aSLaurent Pinchart 	src_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
47887bfaa1aSLaurent Pinchart 	src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
479e6938cc1SHelen Koike 
4800d346d2aSTomi Valkeinen 	src_crop = v4l2_subdev_get_try_crop(sd, sd_state,
481e6938cc1SHelen Koike 					    RKISP1_ISP_PAD_SOURCE_VIDEO);
482e6938cc1SHelen Koike 	*src_crop = *sink_crop;
483e6938cc1SHelen Koike 
48487bfaa1aSLaurent Pinchart 	/* Parameters and statistics. */
4850d346d2aSTomi Valkeinen 	sink_fmt = v4l2_subdev_get_try_format(sd, sd_state,
486e6938cc1SHelen Koike 					      RKISP1_ISP_PAD_SINK_PARAMS);
4870d346d2aSTomi Valkeinen 	src_fmt = v4l2_subdev_get_try_format(sd, sd_state,
488e6938cc1SHelen Koike 					     RKISP1_ISP_PAD_SOURCE_STATS);
489e6938cc1SHelen Koike 	sink_fmt->width = 0;
490e6938cc1SHelen Koike 	sink_fmt->height = 0;
491e6938cc1SHelen Koike 	sink_fmt->field = V4L2_FIELD_NONE;
492e6938cc1SHelen Koike 	sink_fmt->code = MEDIA_BUS_FMT_METADATA_FIXED;
493e6938cc1SHelen Koike 	*src_fmt = *sink_fmt;
494e6938cc1SHelen Koike 
495e6938cc1SHelen Koike 	return 0;
496e6938cc1SHelen Koike }
497e6938cc1SHelen Koike 
rkisp1_isp_set_src_fmt(struct rkisp1_isp * isp,struct v4l2_subdev_state * sd_state,struct v4l2_mbus_framefmt * format,unsigned int which)498e6938cc1SHelen Koike static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp,
4990d346d2aSTomi Valkeinen 				   struct v4l2_subdev_state *sd_state,
500e6938cc1SHelen Koike 				   struct v4l2_mbus_framefmt *format,
501e6938cc1SHelen Koike 				   unsigned int which)
502e6938cc1SHelen Koike {
503cb00f3a4SLaurent Pinchart 	const struct rkisp1_mbus_info *sink_info;
504cb00f3a4SLaurent Pinchart 	const struct rkisp1_mbus_info *src_info;
505cb00f3a4SLaurent Pinchart 	struct v4l2_mbus_framefmt *sink_fmt;
506e6938cc1SHelen Koike 	struct v4l2_mbus_framefmt *src_fmt;
507e6938cc1SHelen Koike 	const struct v4l2_rect *src_crop;
508c1ec5efbSLaurent Pinchart 	bool set_csc;
509e6938cc1SHelen Koike 
510cb00f3a4SLaurent Pinchart 	sink_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state,
511cb00f3a4SLaurent Pinchart 					  RKISP1_ISP_PAD_SINK_VIDEO, which);
5120d346d2aSTomi Valkeinen 	src_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state,
513e6938cc1SHelen Koike 					 RKISP1_ISP_PAD_SOURCE_VIDEO, which);
5140d346d2aSTomi Valkeinen 	src_crop = rkisp1_isp_get_pad_crop(isp, sd_state,
515e6938cc1SHelen Koike 					   RKISP1_ISP_PAD_SOURCE_VIDEO, which);
516e6938cc1SHelen Koike 
517cb00f3a4SLaurent Pinchart 	/*
518cb00f3a4SLaurent Pinchart 	 * Media bus code. The ISP can operate in pass-through mode (Bayer in,
519cb00f3a4SLaurent Pinchart 	 * Bayer out or YUV in, YUV out) or process Bayer data to YUV, but
520cb00f3a4SLaurent Pinchart 	 * can't convert from YUV to Bayer.
521cb00f3a4SLaurent Pinchart 	 */
522cb00f3a4SLaurent Pinchart 	sink_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
523cb00f3a4SLaurent Pinchart 
524e6938cc1SHelen Koike 	src_fmt->code = format->code;
525cb00f3a4SLaurent Pinchart 	src_info = rkisp1_mbus_info_get_by_code(src_fmt->code);
526cb00f3a4SLaurent Pinchart 	if (!src_info || !(src_info->direction & RKISP1_ISP_SD_SRC)) {
527e6938cc1SHelen Koike 		src_fmt->code = RKISP1_DEF_SRC_PAD_FMT;
528cb00f3a4SLaurent Pinchart 		src_info = rkisp1_mbus_info_get_by_code(src_fmt->code);
529e6938cc1SHelen Koike 	}
530cb00f3a4SLaurent Pinchart 
531cb00f3a4SLaurent Pinchart 	if (sink_info->pixel_enc == V4L2_PIXEL_ENC_YUV &&
532cb00f3a4SLaurent Pinchart 	    src_info->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
533cb00f3a4SLaurent Pinchart 		src_fmt->code = sink_fmt->code;
534cb00f3a4SLaurent Pinchart 		src_info = sink_info;
535cb00f3a4SLaurent Pinchart 	}
536cb00f3a4SLaurent Pinchart 
537cb00f3a4SLaurent Pinchart 	/*
538cb00f3a4SLaurent Pinchart 	 * The source width and height must be identical to the source crop
539cb00f3a4SLaurent Pinchart 	 * size.
540cb00f3a4SLaurent Pinchart 	 */
541e6938cc1SHelen Koike 	src_fmt->width  = src_crop->width;
542e6938cc1SHelen Koike 	src_fmt->height = src_crop->height;
543e6938cc1SHelen Koike 
544e6938cc1SHelen Koike 	/*
545c1ec5efbSLaurent Pinchart 	 * Copy the color space for the sink pad. When converting from Bayer to
546c1ec5efbSLaurent Pinchart 	 * YUV, default to a limited quantization range.
547e6938cc1SHelen Koike 	 */
548c1ec5efbSLaurent Pinchart 	src_fmt->colorspace = sink_fmt->colorspace;
549c1ec5efbSLaurent Pinchart 	src_fmt->xfer_func = sink_fmt->xfer_func;
550c1ec5efbSLaurent Pinchart 	src_fmt->ycbcr_enc = sink_fmt->ycbcr_enc;
551c1ec5efbSLaurent Pinchart 
552c1ec5efbSLaurent Pinchart 	if (sink_info->pixel_enc == V4L2_PIXEL_ENC_BAYER &&
553cb00f3a4SLaurent Pinchart 	    src_info->pixel_enc == V4L2_PIXEL_ENC_YUV)
554e6938cc1SHelen Koike 		src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
555e6938cc1SHelen Koike 	else
556c1ec5efbSLaurent Pinchart 		src_fmt->quantization = sink_fmt->quantization;
557c1ec5efbSLaurent Pinchart 
558c1ec5efbSLaurent Pinchart 	/*
559c1ec5efbSLaurent Pinchart 	 * Allow setting the source color space fields when the SET_CSC flag is
560c1ec5efbSLaurent Pinchart 	 * set and the source format is YUV. If the sink format is YUV, don't
561c1ec5efbSLaurent Pinchart 	 * set the color primaries, transfer function or YCbCr encoding as the
562c1ec5efbSLaurent Pinchart 	 * ISP is bypassed in that case and passes YUV data through without
563c1ec5efbSLaurent Pinchart 	 * modifications.
564c1ec5efbSLaurent Pinchart 	 *
565c1ec5efbSLaurent Pinchart 	 * The color primaries and transfer function are configured through the
566c1ec5efbSLaurent Pinchart 	 * cross-talk matrix and tone curve respectively. Settings for those
567c1ec5efbSLaurent Pinchart 	 * hardware blocks are conveyed through the ISP parameters buffer, as
568c1ec5efbSLaurent Pinchart 	 * they need to combine color space information with other image tuning
569c1ec5efbSLaurent Pinchart 	 * characteristics and can't thus be computed by the kernel based on the
570c1ec5efbSLaurent Pinchart 	 * color space. The source pad colorspace and xfer_func fields are thus
571c1ec5efbSLaurent Pinchart 	 * ignored by the driver, but can be set by userspace to propagate
572c1ec5efbSLaurent Pinchart 	 * accurate color space information down the pipeline.
573c1ec5efbSLaurent Pinchart 	 */
574c1ec5efbSLaurent Pinchart 	set_csc = format->flags & V4L2_MBUS_FRAMEFMT_SET_CSC;
575c1ec5efbSLaurent Pinchart 
576c1ec5efbSLaurent Pinchart 	if (set_csc && src_info->pixel_enc == V4L2_PIXEL_ENC_YUV) {
577c1ec5efbSLaurent Pinchart 		if (sink_info->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
578c1ec5efbSLaurent Pinchart 			if (format->colorspace != V4L2_COLORSPACE_DEFAULT)
579c1ec5efbSLaurent Pinchart 				src_fmt->colorspace = format->colorspace;
580c1ec5efbSLaurent Pinchart 			if (format->xfer_func != V4L2_XFER_FUNC_DEFAULT)
581c1ec5efbSLaurent Pinchart 				src_fmt->xfer_func = format->xfer_func;
582c1ec5efbSLaurent Pinchart 			if (format->ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT)
583c1ec5efbSLaurent Pinchart 				src_fmt->ycbcr_enc = format->ycbcr_enc;
584c1ec5efbSLaurent Pinchart 		}
585c1ec5efbSLaurent Pinchart 
586c1ec5efbSLaurent Pinchart 		if (format->quantization != V4L2_QUANTIZATION_DEFAULT)
587c1ec5efbSLaurent Pinchart 			src_fmt->quantization = format->quantization;
588c1ec5efbSLaurent Pinchart 	}
589e6938cc1SHelen Koike 
590e6938cc1SHelen Koike 	*format = *src_fmt;
591cb00f3a4SLaurent Pinchart 
592c1ec5efbSLaurent Pinchart 	/*
593c1ec5efbSLaurent Pinchart 	 * Restore the SET_CSC flag if it was set to indicate support for the
594c1ec5efbSLaurent Pinchart 	 * CSC setting API.
595c1ec5efbSLaurent Pinchart 	 */
596c1ec5efbSLaurent Pinchart 	if (set_csc)
597c1ec5efbSLaurent Pinchart 		format->flags |= V4L2_MBUS_FRAMEFMT_SET_CSC;
598c1ec5efbSLaurent Pinchart 
599cb00f3a4SLaurent Pinchart 	/* Store the source format info when setting the active format. */
600cb00f3a4SLaurent Pinchart 	if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
601cb00f3a4SLaurent Pinchart 		isp->src_fmt = src_info;
602e6938cc1SHelen Koike }
603e6938cc1SHelen Koike 
rkisp1_isp_set_src_crop(struct rkisp1_isp * isp,struct v4l2_subdev_state * sd_state,struct v4l2_rect * r,unsigned int which)604e6938cc1SHelen Koike static void rkisp1_isp_set_src_crop(struct rkisp1_isp *isp,
6050d346d2aSTomi Valkeinen 				    struct v4l2_subdev_state *sd_state,
606e6938cc1SHelen Koike 				    struct v4l2_rect *r, unsigned int which)
607e6938cc1SHelen Koike {
608e6938cc1SHelen Koike 	struct v4l2_mbus_framefmt *src_fmt;
609e6938cc1SHelen Koike 	const struct v4l2_rect *sink_crop;
610e6938cc1SHelen Koike 	struct v4l2_rect *src_crop;
611e6938cc1SHelen Koike 
6120d346d2aSTomi Valkeinen 	src_crop = rkisp1_isp_get_pad_crop(isp, sd_state,
613e6938cc1SHelen Koike 					   RKISP1_ISP_PAD_SOURCE_VIDEO,
614e6938cc1SHelen Koike 					   which);
6150d346d2aSTomi Valkeinen 	sink_crop = rkisp1_isp_get_pad_crop(isp, sd_state,
616e6938cc1SHelen Koike 					    RKISP1_ISP_PAD_SINK_VIDEO,
617e6938cc1SHelen Koike 					    which);
618e6938cc1SHelen Koike 
619e6938cc1SHelen Koike 	src_crop->left = ALIGN(r->left, 2);
620e6938cc1SHelen Koike 	src_crop->width = ALIGN(r->width, 2);
621e6938cc1SHelen Koike 	src_crop->top = r->top;
622e6938cc1SHelen Koike 	src_crop->height = r->height;
623e6938cc1SHelen Koike 	rkisp1_sd_adjust_crop_rect(src_crop, sink_crop);
624e6938cc1SHelen Koike 
625e6938cc1SHelen Koike 	*r = *src_crop;
626e6938cc1SHelen Koike 
627e6938cc1SHelen Koike 	/* Propagate to out format */
6280d346d2aSTomi Valkeinen 	src_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state,
629e6938cc1SHelen Koike 					 RKISP1_ISP_PAD_SOURCE_VIDEO, which);
6300d346d2aSTomi Valkeinen 	rkisp1_isp_set_src_fmt(isp, sd_state, src_fmt, which);
631e6938cc1SHelen Koike }
632e6938cc1SHelen Koike 
rkisp1_isp_set_sink_crop(struct rkisp1_isp * isp,struct v4l2_subdev_state * sd_state,struct v4l2_rect * r,unsigned int which)633e6938cc1SHelen Koike static void rkisp1_isp_set_sink_crop(struct rkisp1_isp *isp,
6340d346d2aSTomi Valkeinen 				     struct v4l2_subdev_state *sd_state,
635e6938cc1SHelen Koike 				     struct v4l2_rect *r, unsigned int which)
636e6938cc1SHelen Koike {
637e6938cc1SHelen Koike 	struct v4l2_rect *sink_crop, *src_crop;
63876302581SLaurent Pinchart 	const struct v4l2_mbus_framefmt *sink_fmt;
639e6938cc1SHelen Koike 
6400d346d2aSTomi Valkeinen 	sink_crop = rkisp1_isp_get_pad_crop(isp, sd_state,
6410d346d2aSTomi Valkeinen 					    RKISP1_ISP_PAD_SINK_VIDEO,
642e6938cc1SHelen Koike 					    which);
6430d346d2aSTomi Valkeinen 	sink_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state,
6440d346d2aSTomi Valkeinen 					  RKISP1_ISP_PAD_SINK_VIDEO,
645e6938cc1SHelen Koike 					  which);
646e6938cc1SHelen Koike 
647e6938cc1SHelen Koike 	sink_crop->left = ALIGN(r->left, 2);
648e6938cc1SHelen Koike 	sink_crop->width = ALIGN(r->width, 2);
649e6938cc1SHelen Koike 	sink_crop->top = r->top;
650e6938cc1SHelen Koike 	sink_crop->height = r->height;
651e6938cc1SHelen Koike 	rkisp1_sd_adjust_crop(sink_crop, sink_fmt);
652e6938cc1SHelen Koike 
653e6938cc1SHelen Koike 	*r = *sink_crop;
654e6938cc1SHelen Koike 
655e6938cc1SHelen Koike 	/* Propagate to out crop */
6560d346d2aSTomi Valkeinen 	src_crop = rkisp1_isp_get_pad_crop(isp, sd_state,
657e6938cc1SHelen Koike 					   RKISP1_ISP_PAD_SOURCE_VIDEO, which);
6580d346d2aSTomi Valkeinen 	rkisp1_isp_set_src_crop(isp, sd_state, src_crop, which);
659e6938cc1SHelen Koike }
660e6938cc1SHelen Koike 
rkisp1_isp_set_sink_fmt(struct rkisp1_isp * isp,struct v4l2_subdev_state * sd_state,struct v4l2_mbus_framefmt * format,unsigned int which)661e6938cc1SHelen Koike static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp,
6620d346d2aSTomi Valkeinen 				    struct v4l2_subdev_state *sd_state,
663e6938cc1SHelen Koike 				    struct v4l2_mbus_framefmt *format,
664e6938cc1SHelen Koike 				    unsigned int which)
665e6938cc1SHelen Koike {
6660f3c2ab2SPaul Elder 	const struct rkisp1_mbus_info *mbus_info;
667e6938cc1SHelen Koike 	struct v4l2_mbus_framefmt *sink_fmt;
668e6938cc1SHelen Koike 	struct v4l2_rect *sink_crop;
6696844cebbSLaurent Pinchart 	bool is_yuv;
670e6938cc1SHelen Koike 
6710d346d2aSTomi Valkeinen 	sink_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state,
6720d346d2aSTomi Valkeinen 					  RKISP1_ISP_PAD_SINK_VIDEO,
673e6938cc1SHelen Koike 					  which);
674e6938cc1SHelen Koike 	sink_fmt->code = format->code;
6750f3c2ab2SPaul Elder 	mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
676e6938cc1SHelen Koike 	if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SINK)) {
677e6938cc1SHelen Koike 		sink_fmt->code = RKISP1_DEF_SINK_PAD_FMT;
6780f3c2ab2SPaul Elder 		mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
679e6938cc1SHelen Koike 	}
680e6938cc1SHelen Koike 	if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
681e6938cc1SHelen Koike 		isp->sink_fmt = mbus_info;
682e6938cc1SHelen Koike 
683e6938cc1SHelen Koike 	sink_fmt->width = clamp_t(u32, format->width,
684e6938cc1SHelen Koike 				  RKISP1_ISP_MIN_WIDTH,
685e6938cc1SHelen Koike 				  RKISP1_ISP_MAX_WIDTH);
686e6938cc1SHelen Koike 	sink_fmt->height = clamp_t(u32, format->height,
687e6938cc1SHelen Koike 				   RKISP1_ISP_MIN_HEIGHT,
688e6938cc1SHelen Koike 				   RKISP1_ISP_MAX_HEIGHT);
689e6938cc1SHelen Koike 
6906844cebbSLaurent Pinchart 	/*
6916844cebbSLaurent Pinchart 	 * Adjust the color space fields. Accept any color primaries and
6926844cebbSLaurent Pinchart 	 * transfer function for both YUV and Bayer. For YUV any YCbCr encoding
6936844cebbSLaurent Pinchart 	 * and quantization range is also accepted. For Bayer formats, the YCbCr
6946844cebbSLaurent Pinchart 	 * encoding isn't applicable, and the quantization range can only be
6956844cebbSLaurent Pinchart 	 * full.
6966844cebbSLaurent Pinchart 	 */
6976844cebbSLaurent Pinchart 	is_yuv = mbus_info->pixel_enc == V4L2_PIXEL_ENC_YUV;
6986844cebbSLaurent Pinchart 
6996844cebbSLaurent Pinchart 	sink_fmt->colorspace = format->colorspace ? :
7006844cebbSLaurent Pinchart 			       (is_yuv ? V4L2_COLORSPACE_SRGB :
7016844cebbSLaurent Pinchart 				V4L2_COLORSPACE_RAW);
7026844cebbSLaurent Pinchart 	sink_fmt->xfer_func = format->xfer_func ? :
7036844cebbSLaurent Pinchart 			      V4L2_MAP_XFER_FUNC_DEFAULT(sink_fmt->colorspace);
7046844cebbSLaurent Pinchart 	if (is_yuv) {
7056844cebbSLaurent Pinchart 		sink_fmt->ycbcr_enc = format->ycbcr_enc ? :
7066844cebbSLaurent Pinchart 			V4L2_MAP_YCBCR_ENC_DEFAULT(sink_fmt->colorspace);
7076844cebbSLaurent Pinchart 		sink_fmt->quantization = format->quantization ? :
7086844cebbSLaurent Pinchart 			V4L2_MAP_QUANTIZATION_DEFAULT(false, sink_fmt->colorspace,
7096844cebbSLaurent Pinchart 						      sink_fmt->ycbcr_enc);
7106844cebbSLaurent Pinchart 	} else {
7116844cebbSLaurent Pinchart 		/*
7126844cebbSLaurent Pinchart 		 * The YCbCr encoding isn't applicable for non-YUV formats, but
7136844cebbSLaurent Pinchart 		 * V4L2 has no "no encoding" value. Hardcode it to Rec. 601, it
7146844cebbSLaurent Pinchart 		 * should be ignored by userspace.
7156844cebbSLaurent Pinchart 		 */
7166844cebbSLaurent Pinchart 		sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
7176844cebbSLaurent Pinchart 		sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
7186844cebbSLaurent Pinchart 	}
7196844cebbSLaurent Pinchart 
720e6938cc1SHelen Koike 	*format = *sink_fmt;
721e6938cc1SHelen Koike 
722e6938cc1SHelen Koike 	/* Propagate to in crop */
7230d346d2aSTomi Valkeinen 	sink_crop = rkisp1_isp_get_pad_crop(isp, sd_state,
7240d346d2aSTomi Valkeinen 					    RKISP1_ISP_PAD_SINK_VIDEO,
725e6938cc1SHelen Koike 					    which);
7260d346d2aSTomi Valkeinen 	rkisp1_isp_set_sink_crop(isp, sd_state, sink_crop, which);
727e6938cc1SHelen Koike }
728e6938cc1SHelen Koike 
rkisp1_isp_get_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)729e6938cc1SHelen Koike static int rkisp1_isp_get_fmt(struct v4l2_subdev *sd,
7300d346d2aSTomi Valkeinen 			      struct v4l2_subdev_state *sd_state,
731e6938cc1SHelen Koike 			      struct v4l2_subdev_format *fmt)
732e6938cc1SHelen Koike {
7338c1aa197SLaurent Pinchart 	struct rkisp1_isp *isp = to_rkisp1_isp(sd);
734e6938cc1SHelen Koike 
735e6938cc1SHelen Koike 	mutex_lock(&isp->ops_lock);
7360d346d2aSTomi Valkeinen 	fmt->format = *rkisp1_isp_get_pad_fmt(isp, sd_state, fmt->pad,
7370d346d2aSTomi Valkeinen 					      fmt->which);
738e6938cc1SHelen Koike 	mutex_unlock(&isp->ops_lock);
739e6938cc1SHelen Koike 	return 0;
740e6938cc1SHelen Koike }
741e6938cc1SHelen Koike 
rkisp1_isp_set_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)742e6938cc1SHelen Koike static int rkisp1_isp_set_fmt(struct v4l2_subdev *sd,
7430d346d2aSTomi Valkeinen 			      struct v4l2_subdev_state *sd_state,
744e6938cc1SHelen Koike 			      struct v4l2_subdev_format *fmt)
745e6938cc1SHelen Koike {
7468c1aa197SLaurent Pinchart 	struct rkisp1_isp *isp = to_rkisp1_isp(sd);
747e6938cc1SHelen Koike 
748e6938cc1SHelen Koike 	mutex_lock(&isp->ops_lock);
749e6938cc1SHelen Koike 	if (fmt->pad == RKISP1_ISP_PAD_SINK_VIDEO)
7500d346d2aSTomi Valkeinen 		rkisp1_isp_set_sink_fmt(isp, sd_state, &fmt->format,
7510d346d2aSTomi Valkeinen 					fmt->which);
752e6938cc1SHelen Koike 	else if (fmt->pad == RKISP1_ISP_PAD_SOURCE_VIDEO)
7530d346d2aSTomi Valkeinen 		rkisp1_isp_set_src_fmt(isp, sd_state, &fmt->format,
7540d346d2aSTomi Valkeinen 				       fmt->which);
755e6938cc1SHelen Koike 	else
7560d346d2aSTomi Valkeinen 		fmt->format = *rkisp1_isp_get_pad_fmt(isp, sd_state, fmt->pad,
757e6938cc1SHelen Koike 						      fmt->which);
758e6938cc1SHelen Koike 
759e6938cc1SHelen Koike 	mutex_unlock(&isp->ops_lock);
760e6938cc1SHelen Koike 	return 0;
761e6938cc1SHelen Koike }
762e6938cc1SHelen Koike 
rkisp1_isp_get_selection(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * sel)763e6938cc1SHelen Koike static int rkisp1_isp_get_selection(struct v4l2_subdev *sd,
7640d346d2aSTomi Valkeinen 				    struct v4l2_subdev_state *sd_state,
765e6938cc1SHelen Koike 				    struct v4l2_subdev_selection *sel)
766e6938cc1SHelen Koike {
7678c1aa197SLaurent Pinchart 	struct rkisp1_isp *isp = to_rkisp1_isp(sd);
768e6938cc1SHelen Koike 	int ret = 0;
769e6938cc1SHelen Koike 
770e6938cc1SHelen Koike 	if (sel->pad != RKISP1_ISP_PAD_SOURCE_VIDEO &&
771e6938cc1SHelen Koike 	    sel->pad != RKISP1_ISP_PAD_SINK_VIDEO)
772e6938cc1SHelen Koike 		return -EINVAL;
773e6938cc1SHelen Koike 
774e6938cc1SHelen Koike 	mutex_lock(&isp->ops_lock);
775e6938cc1SHelen Koike 	switch (sel->target) {
776e6938cc1SHelen Koike 	case V4L2_SEL_TGT_CROP_BOUNDS:
777e6938cc1SHelen Koike 		if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO) {
778e6938cc1SHelen Koike 			struct v4l2_mbus_framefmt *fmt;
779e6938cc1SHelen Koike 
7800d346d2aSTomi Valkeinen 			fmt = rkisp1_isp_get_pad_fmt(isp, sd_state, sel->pad,
781e6938cc1SHelen Koike 						     sel->which);
782e6938cc1SHelen Koike 			sel->r.height = fmt->height;
783e6938cc1SHelen Koike 			sel->r.width = fmt->width;
784e6938cc1SHelen Koike 			sel->r.left = 0;
785e6938cc1SHelen Koike 			sel->r.top = 0;
786e6938cc1SHelen Koike 		} else {
7870d346d2aSTomi Valkeinen 			sel->r = *rkisp1_isp_get_pad_crop(isp, sd_state,
788e6938cc1SHelen Koike 							  RKISP1_ISP_PAD_SINK_VIDEO,
789e6938cc1SHelen Koike 							  sel->which);
790e6938cc1SHelen Koike 		}
791e6938cc1SHelen Koike 		break;
792e6938cc1SHelen Koike 	case V4L2_SEL_TGT_CROP:
7930d346d2aSTomi Valkeinen 		sel->r = *rkisp1_isp_get_pad_crop(isp, sd_state, sel->pad,
794e6938cc1SHelen Koike 						  sel->which);
795e6938cc1SHelen Koike 		break;
796e6938cc1SHelen Koike 	default:
797e6938cc1SHelen Koike 		ret = -EINVAL;
798e6938cc1SHelen Koike 	}
799e6938cc1SHelen Koike 	mutex_unlock(&isp->ops_lock);
800e6938cc1SHelen Koike 	return ret;
801e6938cc1SHelen Koike }
802e6938cc1SHelen Koike 
rkisp1_isp_set_selection(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * sel)803e6938cc1SHelen Koike static int rkisp1_isp_set_selection(struct v4l2_subdev *sd,
8040d346d2aSTomi Valkeinen 				    struct v4l2_subdev_state *sd_state,
805e6938cc1SHelen Koike 				    struct v4l2_subdev_selection *sel)
806e6938cc1SHelen Koike {
8078c1aa197SLaurent Pinchart 	struct rkisp1_isp *isp = to_rkisp1_isp(sd);
808e6938cc1SHelen Koike 	int ret = 0;
809e6938cc1SHelen Koike 
810e6938cc1SHelen Koike 	if (sel->target != V4L2_SEL_TGT_CROP)
811e6938cc1SHelen Koike 		return -EINVAL;
812e6938cc1SHelen Koike 
813fd130bc6SLaurent Pinchart 	dev_dbg(isp->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__,
814e6938cc1SHelen Koike 		sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height);
815e6938cc1SHelen Koike 	mutex_lock(&isp->ops_lock);
816e6938cc1SHelen Koike 	if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO)
8170d346d2aSTomi Valkeinen 		rkisp1_isp_set_sink_crop(isp, sd_state, &sel->r, sel->which);
818e6938cc1SHelen Koike 	else if (sel->pad == RKISP1_ISP_PAD_SOURCE_VIDEO)
8190d346d2aSTomi Valkeinen 		rkisp1_isp_set_src_crop(isp, sd_state, &sel->r, sel->which);
820e6938cc1SHelen Koike 	else
821e6938cc1SHelen Koike 		ret = -EINVAL;
822e6938cc1SHelen Koike 
823e6938cc1SHelen Koike 	mutex_unlock(&isp->ops_lock);
824e6938cc1SHelen Koike 	return ret;
825e6938cc1SHelen Koike }
826e6938cc1SHelen Koike 
rkisp1_subdev_link_validate(struct media_link * link)827e6938cc1SHelen Koike static int rkisp1_subdev_link_validate(struct media_link *link)
828e6938cc1SHelen Koike {
829e6938cc1SHelen Koike 	if (link->sink->index == RKISP1_ISP_PAD_SINK_PARAMS)
830e6938cc1SHelen Koike 		return 0;
831e6938cc1SHelen Koike 
832e6938cc1SHelen Koike 	return v4l2_subdev_link_validate(link);
833e6938cc1SHelen Koike }
834e6938cc1SHelen Koike 
835e6938cc1SHelen Koike static const struct v4l2_subdev_pad_ops rkisp1_isp_pad_ops = {
836e6938cc1SHelen Koike 	.enum_mbus_code = rkisp1_isp_enum_mbus_code,
8374fc81486SSebastian Fricke 	.enum_frame_size = rkisp1_isp_enum_frame_size,
838e6938cc1SHelen Koike 	.get_selection = rkisp1_isp_get_selection,
839e6938cc1SHelen Koike 	.set_selection = rkisp1_isp_set_selection,
840e6938cc1SHelen Koike 	.init_cfg = rkisp1_isp_init_config,
841e6938cc1SHelen Koike 	.get_fmt = rkisp1_isp_get_fmt,
842e6938cc1SHelen Koike 	.set_fmt = rkisp1_isp_set_fmt,
843e6938cc1SHelen Koike 	.link_validate = v4l2_subdev_link_validate_default,
844e6938cc1SHelen Koike };
845e6938cc1SHelen Koike 
846e6938cc1SHelen Koike /* ----------------------------------------------------------------------------
847e6938cc1SHelen Koike  * Stream operations
848e6938cc1SHelen Koike  */
849e6938cc1SHelen Koike 
rkisp1_isp_s_stream(struct v4l2_subdev * sd,int enable)850e6938cc1SHelen Koike static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable)
851e6938cc1SHelen Koike {
8528c1aa197SLaurent Pinchart 	struct rkisp1_isp *isp = to_rkisp1_isp(sd);
853fd130bc6SLaurent Pinchart 	struct rkisp1_device *rkisp1 = isp->rkisp1;
8548b52ec2dSLaurent Pinchart 	struct media_pad *source_pad;
8558b52ec2dSLaurent Pinchart 	struct media_pad *sink_pad;
856f42f4558SPaul Elder 	enum v4l2_mbus_type mbus_type;
857f42f4558SPaul Elder 	u32 mbus_flags;
858c4a1d392SLaurent Pinchart 	int ret;
859e6938cc1SHelen Koike 
860e6938cc1SHelen Koike 	if (!enable) {
861745ba74aSLaurent Pinchart 		v4l2_subdev_call(rkisp1->source, video, s_stream, false);
862bba100dfSLaurent Pinchart 		rkisp1_isp_stop(isp);
863e6938cc1SHelen Koike 		return 0;
864e6938cc1SHelen Koike 	}
865e6938cc1SHelen Koike 
8668b52ec2dSLaurent Pinchart 	sink_pad = &isp->pads[RKISP1_ISP_PAD_SINK_VIDEO];
8678b52ec2dSLaurent Pinchart 	source_pad = media_pad_remote_pad_unique(sink_pad);
8688b52ec2dSLaurent Pinchart 	if (IS_ERR(source_pad)) {
8698b52ec2dSLaurent Pinchart 		dev_dbg(rkisp1->dev, "Failed to get source for ISP: %ld\n",
8708b52ec2dSLaurent Pinchart 			PTR_ERR(source_pad));
8718b52ec2dSLaurent Pinchart 		return -EPIPE;
8728b52ec2dSLaurent Pinchart 	}
8738b52ec2dSLaurent Pinchart 
8748b52ec2dSLaurent Pinchart 	rkisp1->source = media_entity_to_v4l2_subdev(source_pad->entity);
875745ba74aSLaurent Pinchart 	if (!rkisp1->source) {
8768b52ec2dSLaurent Pinchart 		/* This should really not happen, so is not worth a message. */
8778b52ec2dSLaurent Pinchart 		return -EPIPE;
878e6938cc1SHelen Koike 	}
879e6938cc1SHelen Koike 
880f42f4558SPaul Elder 	if (rkisp1->source == &rkisp1->csi.sd) {
881f42f4558SPaul Elder 		mbus_type = V4L2_MBUS_CSI2_DPHY;
882f42f4558SPaul Elder 		mbus_flags = 0;
883f42f4558SPaul Elder 	} else {
884f42f4558SPaul Elder 		const struct rkisp1_sensor_async *asd;
885c91fd7b7SSakari Ailus 		struct v4l2_async_connection *asc;
886f42f4558SPaul Elder 
887c91fd7b7SSakari Ailus 		asc = v4l2_async_connection_unique(rkisp1->source);
888c91fd7b7SSakari Ailus 		if (!asc)
889c91fd7b7SSakari Ailus 			return -EPIPE;
890c91fd7b7SSakari Ailus 
891c91fd7b7SSakari Ailus 		asd = container_of(asc, struct rkisp1_sensor_async, asd);
892f42f4558SPaul Elder 
893f42f4558SPaul Elder 		mbus_type = asd->mbus_type;
894f42f4558SPaul Elder 		mbus_flags = asd->mbus_flags;
895f42f4558SPaul Elder 	}
896e6938cc1SHelen Koike 
897bba100dfSLaurent Pinchart 	isp->frame_sequence = -1;
898e6938cc1SHelen Koike 	mutex_lock(&isp->ops_lock);
899f42f4558SPaul Elder 	ret = rkisp1_config_cif(isp, mbus_type, mbus_flags);
900e6938cc1SHelen Koike 	if (ret)
901e6938cc1SHelen Koike 		goto mutex_unlock;
902e6938cc1SHelen Koike 
903bba100dfSLaurent Pinchart 	rkisp1_isp_start(isp);
904e6938cc1SHelen Koike 
905745ba74aSLaurent Pinchart 	ret = v4l2_subdev_call(rkisp1->source, video, s_stream, true);
906deaf1120SLaurent Pinchart 	if (ret) {
907bba100dfSLaurent Pinchart 		rkisp1_isp_stop(isp);
908deaf1120SLaurent Pinchart 		goto mutex_unlock;
909deaf1120SLaurent Pinchart 	}
910deaf1120SLaurent Pinchart 
911e6938cc1SHelen Koike mutex_unlock:
912e6938cc1SHelen Koike 	mutex_unlock(&isp->ops_lock);
913e6938cc1SHelen Koike 	return ret;
914e6938cc1SHelen Koike }
915e6938cc1SHelen Koike 
rkisp1_isp_subs_evt(struct v4l2_subdev * sd,struct v4l2_fh * fh,struct v4l2_event_subscription * sub)916e6938cc1SHelen Koike static int rkisp1_isp_subs_evt(struct v4l2_subdev *sd, struct v4l2_fh *fh,
917e6938cc1SHelen Koike 			       struct v4l2_event_subscription *sub)
918e6938cc1SHelen Koike {
919e6938cc1SHelen Koike 	if (sub->type != V4L2_EVENT_FRAME_SYNC)
920e6938cc1SHelen Koike 		return -EINVAL;
921e6938cc1SHelen Koike 
922e6938cc1SHelen Koike 	/* V4L2_EVENT_FRAME_SYNC doesn't require an id, so zero should be set */
923e6938cc1SHelen Koike 	if (sub->id != 0)
924e6938cc1SHelen Koike 		return -EINVAL;
925e6938cc1SHelen Koike 
926e6938cc1SHelen Koike 	return v4l2_event_subscribe(fh, sub, 0, NULL);
927e6938cc1SHelen Koike }
928e6938cc1SHelen Koike 
929e6938cc1SHelen Koike static const struct media_entity_operations rkisp1_isp_media_ops = {
930e6938cc1SHelen Koike 	.link_validate = rkisp1_subdev_link_validate,
931e6938cc1SHelen Koike };
932e6938cc1SHelen Koike 
933e6938cc1SHelen Koike static const struct v4l2_subdev_video_ops rkisp1_isp_video_ops = {
934e6938cc1SHelen Koike 	.s_stream = rkisp1_isp_s_stream,
935e6938cc1SHelen Koike };
936e6938cc1SHelen Koike 
937e6938cc1SHelen Koike static const struct v4l2_subdev_core_ops rkisp1_isp_core_ops = {
938e6938cc1SHelen Koike 	.subscribe_event = rkisp1_isp_subs_evt,
939e6938cc1SHelen Koike 	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
940e6938cc1SHelen Koike };
941e6938cc1SHelen Koike 
942e6938cc1SHelen Koike static const struct v4l2_subdev_ops rkisp1_isp_ops = {
943e6938cc1SHelen Koike 	.core = &rkisp1_isp_core_ops,
944e6938cc1SHelen Koike 	.video = &rkisp1_isp_video_ops,
945e6938cc1SHelen Koike 	.pad = &rkisp1_isp_pad_ops,
946e6938cc1SHelen Koike };
947e6938cc1SHelen Koike 
rkisp1_isp_register(struct rkisp1_device * rkisp1)948e6938cc1SHelen Koike int rkisp1_isp_register(struct rkisp1_device *rkisp1)
949e6938cc1SHelen Koike {
9500d346d2aSTomi Valkeinen 	struct v4l2_subdev_state state = {
9510d346d2aSTomi Valkeinen 		.pads = rkisp1->isp.pad_cfg
9520d346d2aSTomi Valkeinen 	};
953e6938cc1SHelen Koike 	struct rkisp1_isp *isp = &rkisp1->isp;
954e6938cc1SHelen Koike 	struct media_pad *pads = isp->pads;
955e6938cc1SHelen Koike 	struct v4l2_subdev *sd = &isp->sd;
956e6938cc1SHelen Koike 	int ret;
957e6938cc1SHelen Koike 
958fd130bc6SLaurent Pinchart 	isp->rkisp1 = rkisp1;
959fd130bc6SLaurent Pinchart 
960e6938cc1SHelen Koike 	v4l2_subdev_init(sd, &rkisp1_isp_ops);
961e6938cc1SHelen Koike 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
962e6938cc1SHelen Koike 	sd->entity.ops = &rkisp1_isp_media_ops;
963e6938cc1SHelen Koike 	sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
964e6938cc1SHelen Koike 	sd->owner = THIS_MODULE;
965e6938cc1SHelen Koike 	strscpy(sd->name, RKISP1_ISP_DEV_NAME, sizeof(sd->name));
966e6938cc1SHelen Koike 
967e6938cc1SHelen Koike 	pads[RKISP1_ISP_PAD_SINK_VIDEO].flags = MEDIA_PAD_FL_SINK |
968e6938cc1SHelen Koike 						MEDIA_PAD_FL_MUST_CONNECT;
969e6938cc1SHelen Koike 	pads[RKISP1_ISP_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK;
970e6938cc1SHelen Koike 	pads[RKISP1_ISP_PAD_SOURCE_VIDEO].flags = MEDIA_PAD_FL_SOURCE;
971e6938cc1SHelen Koike 	pads[RKISP1_ISP_PAD_SOURCE_STATS].flags = MEDIA_PAD_FL_SOURCE;
972e6938cc1SHelen Koike 
9730f3c2ab2SPaul Elder 	isp->sink_fmt = rkisp1_mbus_info_get_by_code(RKISP1_DEF_SINK_PAD_FMT);
9740f3c2ab2SPaul Elder 	isp->src_fmt = rkisp1_mbus_info_get_by_code(RKISP1_DEF_SRC_PAD_FMT);
975e6938cc1SHelen Koike 
976e6938cc1SHelen Koike 	mutex_init(&isp->ops_lock);
977e6938cc1SHelen Koike 	ret = media_entity_pads_init(&sd->entity, RKISP1_ISP_PAD_MAX, pads);
978e6938cc1SHelen Koike 	if (ret)
97948d77568SLaurent Pinchart 		goto error;
980e6938cc1SHelen Koike 
981e6938cc1SHelen Koike 	ret = v4l2_device_register_subdev(&rkisp1->v4l2_dev, sd);
982e6938cc1SHelen Koike 	if (ret) {
983e6938cc1SHelen Koike 		dev_err(rkisp1->dev, "Failed to register isp subdev\n");
98448d77568SLaurent Pinchart 		goto error;
985e6938cc1SHelen Koike 	}
986e6938cc1SHelen Koike 
9870d346d2aSTomi Valkeinen 	rkisp1_isp_init_config(sd, &state);
98848d77568SLaurent Pinchart 
989e6938cc1SHelen Koike 	return 0;
990e6938cc1SHelen Koike 
99148d77568SLaurent Pinchart error:
992e6938cc1SHelen Koike 	media_entity_cleanup(&sd->entity);
99348d77568SLaurent Pinchart 	mutex_destroy(&isp->ops_lock);
99448d77568SLaurent Pinchart 	isp->sd.v4l2_dev = NULL;
995e6938cc1SHelen Koike 	return ret;
996e6938cc1SHelen Koike }
997e6938cc1SHelen Koike 
rkisp1_isp_unregister(struct rkisp1_device * rkisp1)998e6938cc1SHelen Koike void rkisp1_isp_unregister(struct rkisp1_device *rkisp1)
999e6938cc1SHelen Koike {
100048d77568SLaurent Pinchart 	struct rkisp1_isp *isp = &rkisp1->isp;
1001e6938cc1SHelen Koike 
100248d77568SLaurent Pinchart 	if (!isp->sd.v4l2_dev)
100348d77568SLaurent Pinchart 		return;
100448d77568SLaurent Pinchart 
100548d77568SLaurent Pinchart 	v4l2_device_unregister_subdev(&isp->sd);
100648d77568SLaurent Pinchart 	media_entity_cleanup(&isp->sd.entity);
100748d77568SLaurent Pinchart 	mutex_destroy(&isp->ops_lock);
1008e6938cc1SHelen Koike }
1009e6938cc1SHelen Koike 
1010e6938cc1SHelen Koike /* ----------------------------------------------------------------------------
1011e6938cc1SHelen Koike  * Interrupt handlers
1012e6938cc1SHelen Koike  */
1013e6938cc1SHelen Koike 
rkisp1_isp_queue_event_sof(struct rkisp1_isp * isp)1014e6938cc1SHelen Koike static void rkisp1_isp_queue_event_sof(struct rkisp1_isp *isp)
1015e6938cc1SHelen Koike {
1016e6938cc1SHelen Koike 	struct v4l2_event event = {
1017e6938cc1SHelen Koike 		.type = V4L2_EVENT_FRAME_SYNC,
1018e6938cc1SHelen Koike 	};
1019e6938cc1SHelen Koike 
1020e3ab7e20SLaurent Pinchart 	event.u.frame_sync.frame_sequence = isp->frame_sequence;
1021e6938cc1SHelen Koike 	v4l2_event_queue(isp->sd.devnode, &event);
1022e6938cc1SHelen Koike }
1023e6938cc1SHelen Koike 
rkisp1_isp_isr(int irq,void * ctx)102408818e6aSHeiko Stuebner irqreturn_t rkisp1_isp_isr(int irq, void *ctx)
1025e6938cc1SHelen Koike {
102608818e6aSHeiko Stuebner 	struct device *dev = ctx;
102708818e6aSHeiko Stuebner 	struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
1028e6938cc1SHelen Koike 	u32 status, isp_err;
1029e6938cc1SHelen Koike 
1030*b39b4d20STomi Valkeinen 	if (!rkisp1->irqs_enabled)
1031*b39b4d20STomi Valkeinen 		return IRQ_NONE;
1032*b39b4d20STomi Valkeinen 
1033e6938cc1SHelen Koike 	status = rkisp1_read(rkisp1, RKISP1_CIF_ISP_MIS);
1034e6938cc1SHelen Koike 	if (!status)
103508818e6aSHeiko Stuebner 		return IRQ_NONE;
1036e6938cc1SHelen Koike 
10370ef7dc30SLaurent Pinchart 	rkisp1_write(rkisp1, RKISP1_CIF_ISP_ICR, status);
1038e6938cc1SHelen Koike 
1039e6938cc1SHelen Koike 	/* Vertical sync signal, starting generating new frame */
1040e6938cc1SHelen Koike 	if (status & RKISP1_CIF_ISP_V_START) {
1041e6938cc1SHelen Koike 		rkisp1->isp.frame_sequence++;
1042e6938cc1SHelen Koike 		rkisp1_isp_queue_event_sof(&rkisp1->isp);
1043e6938cc1SHelen Koike 		if (status & RKISP1_CIF_ISP_FRAME) {
1044e6938cc1SHelen Koike 			WARN_ONCE(1, "irq delay is too long, buffers might not be in sync\n");
1045e6938cc1SHelen Koike 			rkisp1->debug.irq_delay++;
1046e6938cc1SHelen Koike 		}
1047e6938cc1SHelen Koike 	}
1048e6938cc1SHelen Koike 	if (status & RKISP1_CIF_ISP_PIC_SIZE_ERROR) {
1049e6938cc1SHelen Koike 		/* Clear pic_size_error */
1050e6938cc1SHelen Koike 		isp_err = rkisp1_read(rkisp1, RKISP1_CIF_ISP_ERR);
1051e6938cc1SHelen Koike 		if (isp_err & RKISP1_CIF_ISP_ERR_INFORM_SIZE)
1052e6938cc1SHelen Koike 			rkisp1->debug.inform_size_error++;
1053e6938cc1SHelen Koike 		if (isp_err & RKISP1_CIF_ISP_ERR_IS_SIZE)
1054e6938cc1SHelen Koike 			rkisp1->debug.img_stabilization_size_error++;
1055e6938cc1SHelen Koike 		if (isp_err & RKISP1_CIF_ISP_ERR_OUTFORM_SIZE)
1056e6938cc1SHelen Koike 			rkisp1->debug.outform_size_error++;
10570ef7dc30SLaurent Pinchart 		rkisp1_write(rkisp1, RKISP1_CIF_ISP_ERR_CLR, isp_err);
1058e6938cc1SHelen Koike 	} else if (status & RKISP1_CIF_ISP_DATA_LOSS) {
1059e6938cc1SHelen Koike 		/* keep track of data_loss in debugfs */
1060e6938cc1SHelen Koike 		rkisp1->debug.data_loss++;
1061e6938cc1SHelen Koike 	}
1062e6938cc1SHelen Koike 
1063e6938cc1SHelen Koike 	if (status & RKISP1_CIF_ISP_FRAME) {
1064e6938cc1SHelen Koike 		u32 isp_ris;
1065e6938cc1SHelen Koike 
1066e6938cc1SHelen Koike 		/* New frame from the sensor received */
1067e6938cc1SHelen Koike 		isp_ris = rkisp1_read(rkisp1, RKISP1_CIF_ISP_RIS);
1068e6938cc1SHelen Koike 		if (isp_ris & RKISP1_STATS_MEAS_MASK)
1069e6938cc1SHelen Koike 			rkisp1_stats_isr(&rkisp1->stats, isp_ris);
1070e6938cc1SHelen Koike 		/*
1071e6938cc1SHelen Koike 		 * Then update changed configs. Some of them involve
1072e6938cc1SHelen Koike 		 * lot of register writes. Do those only one per frame.
1073e6938cc1SHelen Koike 		 * Do the updates in the order of the processing flow.
1074e6938cc1SHelen Koike 		 */
1075e6938cc1SHelen Koike 		rkisp1_params_isr(rkisp1);
1076e6938cc1SHelen Koike 	}
107708818e6aSHeiko Stuebner 
107808818e6aSHeiko Stuebner 	return IRQ_HANDLED;
1079e6938cc1SHelen Koike }
1080