xref: /linux/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c (revision d19e470b6605c900db21fc7b34c66b6891a79983)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing)
4  * All rights reserved.
5  * Author: Yong Deng <yong.deng@magewell.com>
6  */
7 
8 #include <linux/clk.h>
9 #include <linux/delay.h>
10 #include <linux/dma-mapping.h>
11 #include <linux/err.h>
12 #include <linux/fs.h>
13 #include <linux/interrupt.h>
14 #include <linux/io.h>
15 #include <linux/ioctl.h>
16 #include <linux/module.h>
17 #include <linux/of.h>
18 #include <linux/of_device.h>
19 #include <linux/platform_device.h>
20 #include <linux/pm_runtime.h>
21 #include <linux/regmap.h>
22 #include <linux/reset.h>
23 #include <linux/sched.h>
24 #include <linux/sizes.h>
25 #include <linux/slab.h>
26 
27 #include "sun6i_csi.h"
28 #include "sun6i_csi_reg.h"
29 
30 #define MODULE_NAME	"sun6i-csi"
31 
32 struct sun6i_csi_dev {
33 	struct sun6i_csi		csi;
34 	struct device			*dev;
35 
36 	struct regmap			*regmap;
37 	struct clk			*clk_mod;
38 	struct clk			*clk_ram;
39 	struct reset_control		*rstc_bus;
40 
41 	int				planar_offset[3];
42 };
43 
44 static inline struct sun6i_csi_dev *sun6i_csi_to_dev(struct sun6i_csi *csi)
45 {
46 	return container_of(csi, struct sun6i_csi_dev, csi);
47 }
48 
49 /* TODO add 10&12 bit YUV, RGB support */
50 bool sun6i_csi_is_format_supported(struct sun6i_csi *csi,
51 				   u32 pixformat, u32 mbus_code)
52 {
53 	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
54 
55 	/*
56 	 * Some video receivers have the ability to be compatible with
57 	 * 8bit and 16bit bus width.
58 	 * Identify the media bus format from device tree.
59 	 */
60 	if ((sdev->csi.v4l2_ep.bus_type == V4L2_MBUS_PARALLEL
61 	     || sdev->csi.v4l2_ep.bus_type == V4L2_MBUS_BT656)
62 	     && sdev->csi.v4l2_ep.bus.parallel.bus_width == 16) {
63 		switch (pixformat) {
64 		case V4L2_PIX_FMT_HM12:
65 		case V4L2_PIX_FMT_NV12:
66 		case V4L2_PIX_FMT_NV21:
67 		case V4L2_PIX_FMT_NV16:
68 		case V4L2_PIX_FMT_NV61:
69 		case V4L2_PIX_FMT_YUV420:
70 		case V4L2_PIX_FMT_YVU420:
71 		case V4L2_PIX_FMT_YUV422P:
72 			switch (mbus_code) {
73 			case MEDIA_BUS_FMT_UYVY8_1X16:
74 			case MEDIA_BUS_FMT_VYUY8_1X16:
75 			case MEDIA_BUS_FMT_YUYV8_1X16:
76 			case MEDIA_BUS_FMT_YVYU8_1X16:
77 				return true;
78 			default:
79 				dev_dbg(sdev->dev, "Unsupported mbus code: 0x%x\n",
80 					mbus_code);
81 				break;
82 			}
83 			break;
84 		default:
85 			dev_dbg(sdev->dev, "Unsupported pixformat: 0x%x\n",
86 				pixformat);
87 			break;
88 		}
89 		return false;
90 	}
91 
92 	switch (pixformat) {
93 	case V4L2_PIX_FMT_SBGGR8:
94 		return (mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8);
95 	case V4L2_PIX_FMT_SGBRG8:
96 		return (mbus_code == MEDIA_BUS_FMT_SGBRG8_1X8);
97 	case V4L2_PIX_FMT_SGRBG8:
98 		return (mbus_code == MEDIA_BUS_FMT_SGRBG8_1X8);
99 	case V4L2_PIX_FMT_SRGGB8:
100 		return (mbus_code == MEDIA_BUS_FMT_SRGGB8_1X8);
101 	case V4L2_PIX_FMT_SBGGR10:
102 		return (mbus_code == MEDIA_BUS_FMT_SBGGR10_1X10);
103 	case V4L2_PIX_FMT_SGBRG10:
104 		return (mbus_code == MEDIA_BUS_FMT_SGBRG10_1X10);
105 	case V4L2_PIX_FMT_SGRBG10:
106 		return (mbus_code == MEDIA_BUS_FMT_SGRBG10_1X10);
107 	case V4L2_PIX_FMT_SRGGB10:
108 		return (mbus_code == MEDIA_BUS_FMT_SRGGB10_1X10);
109 	case V4L2_PIX_FMT_SBGGR12:
110 		return (mbus_code == MEDIA_BUS_FMT_SBGGR12_1X12);
111 	case V4L2_PIX_FMT_SGBRG12:
112 		return (mbus_code == MEDIA_BUS_FMT_SGBRG12_1X12);
113 	case V4L2_PIX_FMT_SGRBG12:
114 		return (mbus_code == MEDIA_BUS_FMT_SGRBG12_1X12);
115 	case V4L2_PIX_FMT_SRGGB12:
116 		return (mbus_code == MEDIA_BUS_FMT_SRGGB12_1X12);
117 
118 	case V4L2_PIX_FMT_YUYV:
119 		return (mbus_code == MEDIA_BUS_FMT_YUYV8_2X8);
120 	case V4L2_PIX_FMT_YVYU:
121 		return (mbus_code == MEDIA_BUS_FMT_YVYU8_2X8);
122 	case V4L2_PIX_FMT_UYVY:
123 		return (mbus_code == MEDIA_BUS_FMT_UYVY8_2X8);
124 	case V4L2_PIX_FMT_VYUY:
125 		return (mbus_code == MEDIA_BUS_FMT_VYUY8_2X8);
126 
127 	case V4L2_PIX_FMT_HM12:
128 	case V4L2_PIX_FMT_NV12:
129 	case V4L2_PIX_FMT_NV21:
130 	case V4L2_PIX_FMT_NV16:
131 	case V4L2_PIX_FMT_NV61:
132 	case V4L2_PIX_FMT_YUV420:
133 	case V4L2_PIX_FMT_YVU420:
134 	case V4L2_PIX_FMT_YUV422P:
135 		switch (mbus_code) {
136 		case MEDIA_BUS_FMT_UYVY8_2X8:
137 		case MEDIA_BUS_FMT_VYUY8_2X8:
138 		case MEDIA_BUS_FMT_YUYV8_2X8:
139 		case MEDIA_BUS_FMT_YVYU8_2X8:
140 			return true;
141 		default:
142 			dev_dbg(sdev->dev, "Unsupported mbus code: 0x%x\n",
143 				mbus_code);
144 			break;
145 		}
146 		break;
147 
148 	case V4L2_PIX_FMT_RGB565:
149 		return (mbus_code == MEDIA_BUS_FMT_RGB565_2X8_LE);
150 	case V4L2_PIX_FMT_RGB565X:
151 		return (mbus_code == MEDIA_BUS_FMT_RGB565_2X8_BE);
152 
153 	case V4L2_PIX_FMT_JPEG:
154 		return (mbus_code == MEDIA_BUS_FMT_JPEG_1X8);
155 
156 	default:
157 		dev_dbg(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat);
158 		break;
159 	}
160 
161 	return false;
162 }
163 
164 int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable)
165 {
166 	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
167 	struct device *dev = sdev->dev;
168 	struct regmap *regmap = sdev->regmap;
169 	int ret;
170 
171 	if (!enable) {
172 		regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0);
173 
174 		clk_disable_unprepare(sdev->clk_ram);
175 		if (of_device_is_compatible(dev->of_node,
176 					    "allwinner,sun50i-a64-csi"))
177 			clk_rate_exclusive_put(sdev->clk_mod);
178 		clk_disable_unprepare(sdev->clk_mod);
179 		reset_control_assert(sdev->rstc_bus);
180 		return 0;
181 	}
182 
183 	ret = clk_prepare_enable(sdev->clk_mod);
184 	if (ret) {
185 		dev_err(sdev->dev, "Enable csi clk err %d\n", ret);
186 		return ret;
187 	}
188 
189 	if (of_device_is_compatible(dev->of_node, "allwinner,sun50i-a64-csi"))
190 		clk_set_rate_exclusive(sdev->clk_mod, 300000000);
191 
192 	ret = clk_prepare_enable(sdev->clk_ram);
193 	if (ret) {
194 		dev_err(sdev->dev, "Enable clk_dram_csi clk err %d\n", ret);
195 		goto clk_mod_disable;
196 	}
197 
198 	ret = reset_control_deassert(sdev->rstc_bus);
199 	if (ret) {
200 		dev_err(sdev->dev, "reset err %d\n", ret);
201 		goto clk_ram_disable;
202 	}
203 
204 	regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, CSI_EN_CSI_EN);
205 
206 	return 0;
207 
208 clk_ram_disable:
209 	clk_disable_unprepare(sdev->clk_ram);
210 clk_mod_disable:
211 	if (of_device_is_compatible(dev->of_node, "allwinner,sun50i-a64-csi"))
212 		clk_rate_exclusive_put(sdev->clk_mod);
213 	clk_disable_unprepare(sdev->clk_mod);
214 	return ret;
215 }
216 
217 static enum csi_input_fmt get_csi_input_format(struct sun6i_csi_dev *sdev,
218 					       u32 mbus_code, u32 pixformat)
219 {
220 	/* non-YUV */
221 	if ((mbus_code & 0xF000) != 0x2000)
222 		return CSI_INPUT_FORMAT_RAW;
223 
224 	switch (pixformat) {
225 	case V4L2_PIX_FMT_YUYV:
226 	case V4L2_PIX_FMT_YVYU:
227 	case V4L2_PIX_FMT_UYVY:
228 	case V4L2_PIX_FMT_VYUY:
229 		return CSI_INPUT_FORMAT_RAW;
230 	default:
231 		break;
232 	}
233 
234 	/* not support YUV420 input format yet */
235 	dev_dbg(sdev->dev, "Select YUV422 as default input format of CSI.\n");
236 	return CSI_INPUT_FORMAT_YUV422;
237 }
238 
239 static enum csi_output_fmt get_csi_output_format(struct sun6i_csi_dev *sdev,
240 						 u32 pixformat, u32 field)
241 {
242 	bool buf_interlaced = false;
243 
244 	if (field == V4L2_FIELD_INTERLACED
245 	    || field == V4L2_FIELD_INTERLACED_TB
246 	    || field == V4L2_FIELD_INTERLACED_BT)
247 		buf_interlaced = true;
248 
249 	switch (pixformat) {
250 	case V4L2_PIX_FMT_SBGGR8:
251 	case V4L2_PIX_FMT_SGBRG8:
252 	case V4L2_PIX_FMT_SGRBG8:
253 	case V4L2_PIX_FMT_SRGGB8:
254 		return buf_interlaced ? CSI_FRAME_RAW_8 : CSI_FIELD_RAW_8;
255 	case V4L2_PIX_FMT_SBGGR10:
256 	case V4L2_PIX_FMT_SGBRG10:
257 	case V4L2_PIX_FMT_SGRBG10:
258 	case V4L2_PIX_FMT_SRGGB10:
259 		return buf_interlaced ? CSI_FRAME_RAW_10 : CSI_FIELD_RAW_10;
260 	case V4L2_PIX_FMT_SBGGR12:
261 	case V4L2_PIX_FMT_SGBRG12:
262 	case V4L2_PIX_FMT_SGRBG12:
263 	case V4L2_PIX_FMT_SRGGB12:
264 		return buf_interlaced ? CSI_FRAME_RAW_12 : CSI_FIELD_RAW_12;
265 
266 	case V4L2_PIX_FMT_YUYV:
267 	case V4L2_PIX_FMT_YVYU:
268 	case V4L2_PIX_FMT_UYVY:
269 	case V4L2_PIX_FMT_VYUY:
270 		return buf_interlaced ? CSI_FRAME_RAW_8 : CSI_FIELD_RAW_8;
271 
272 	case V4L2_PIX_FMT_HM12:
273 		return buf_interlaced ? CSI_FRAME_MB_YUV420 :
274 					CSI_FIELD_MB_YUV420;
275 	case V4L2_PIX_FMT_NV12:
276 	case V4L2_PIX_FMT_NV21:
277 		return buf_interlaced ? CSI_FRAME_UV_CB_YUV420 :
278 					CSI_FIELD_UV_CB_YUV420;
279 	case V4L2_PIX_FMT_YUV420:
280 	case V4L2_PIX_FMT_YVU420:
281 		return buf_interlaced ? CSI_FRAME_PLANAR_YUV420 :
282 					CSI_FIELD_PLANAR_YUV420;
283 	case V4L2_PIX_FMT_NV16:
284 	case V4L2_PIX_FMT_NV61:
285 		return buf_interlaced ? CSI_FRAME_UV_CB_YUV422 :
286 					CSI_FIELD_UV_CB_YUV422;
287 	case V4L2_PIX_FMT_YUV422P:
288 		return buf_interlaced ? CSI_FRAME_PLANAR_YUV422 :
289 					CSI_FIELD_PLANAR_YUV422;
290 
291 	case V4L2_PIX_FMT_RGB565:
292 	case V4L2_PIX_FMT_RGB565X:
293 		return buf_interlaced ? CSI_FRAME_RGB565 : CSI_FIELD_RGB565;
294 
295 	case V4L2_PIX_FMT_JPEG:
296 		return buf_interlaced ? CSI_FRAME_RAW_8 : CSI_FIELD_RAW_8;
297 
298 	default:
299 		dev_warn(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat);
300 		break;
301 	}
302 
303 	return CSI_FIELD_RAW_8;
304 }
305 
306 static enum csi_input_seq get_csi_input_seq(struct sun6i_csi_dev *sdev,
307 					    u32 mbus_code, u32 pixformat)
308 {
309 	/* Input sequence does not apply to non-YUV formats */
310 	if ((mbus_code & 0xF000) != 0x2000)
311 		return 0;
312 
313 	switch (pixformat) {
314 	case V4L2_PIX_FMT_HM12:
315 	case V4L2_PIX_FMT_NV12:
316 	case V4L2_PIX_FMT_NV16:
317 	case V4L2_PIX_FMT_YUV420:
318 	case V4L2_PIX_FMT_YUV422P:
319 		switch (mbus_code) {
320 		case MEDIA_BUS_FMT_UYVY8_2X8:
321 		case MEDIA_BUS_FMT_UYVY8_1X16:
322 			return CSI_INPUT_SEQ_UYVY;
323 		case MEDIA_BUS_FMT_VYUY8_2X8:
324 		case MEDIA_BUS_FMT_VYUY8_1X16:
325 			return CSI_INPUT_SEQ_VYUY;
326 		case MEDIA_BUS_FMT_YUYV8_2X8:
327 		case MEDIA_BUS_FMT_YUYV8_1X16:
328 			return CSI_INPUT_SEQ_YUYV;
329 		case MEDIA_BUS_FMT_YVYU8_1X16:
330 		case MEDIA_BUS_FMT_YVYU8_2X8:
331 			return CSI_INPUT_SEQ_YVYU;
332 		default:
333 			dev_warn(sdev->dev, "Unsupported mbus code: 0x%x\n",
334 				 mbus_code);
335 			break;
336 		}
337 		break;
338 	case V4L2_PIX_FMT_NV21:
339 	case V4L2_PIX_FMT_NV61:
340 	case V4L2_PIX_FMT_YVU420:
341 		switch (mbus_code) {
342 		case MEDIA_BUS_FMT_UYVY8_2X8:
343 		case MEDIA_BUS_FMT_UYVY8_1X16:
344 			return CSI_INPUT_SEQ_VYUY;
345 		case MEDIA_BUS_FMT_VYUY8_2X8:
346 		case MEDIA_BUS_FMT_VYUY8_1X16:
347 			return CSI_INPUT_SEQ_UYVY;
348 		case MEDIA_BUS_FMT_YUYV8_2X8:
349 		case MEDIA_BUS_FMT_YUYV8_1X16:
350 			return CSI_INPUT_SEQ_YVYU;
351 		case MEDIA_BUS_FMT_YVYU8_1X16:
352 		case MEDIA_BUS_FMT_YVYU8_2X8:
353 			return CSI_INPUT_SEQ_YUYV;
354 		default:
355 			dev_warn(sdev->dev, "Unsupported mbus code: 0x%x\n",
356 				 mbus_code);
357 			break;
358 		}
359 		break;
360 
361 	case V4L2_PIX_FMT_YUYV:
362 		return CSI_INPUT_SEQ_YUYV;
363 
364 	default:
365 		dev_warn(sdev->dev, "Unsupported pixformat: 0x%x, defaulting to YUYV\n",
366 			 pixformat);
367 		break;
368 	}
369 
370 	return CSI_INPUT_SEQ_YUYV;
371 }
372 
373 static void sun6i_csi_setup_bus(struct sun6i_csi_dev *sdev)
374 {
375 	struct v4l2_fwnode_endpoint *endpoint = &sdev->csi.v4l2_ep;
376 	struct sun6i_csi *csi = &sdev->csi;
377 	unsigned char bus_width;
378 	u32 flags;
379 	u32 cfg;
380 	bool input_interlaced = false;
381 
382 	if (csi->config.field == V4L2_FIELD_INTERLACED
383 	    || csi->config.field == V4L2_FIELD_INTERLACED_TB
384 	    || csi->config.field == V4L2_FIELD_INTERLACED_BT)
385 		input_interlaced = true;
386 
387 	bus_width = endpoint->bus.parallel.bus_width;
388 
389 	regmap_read(sdev->regmap, CSI_IF_CFG_REG, &cfg);
390 
391 	cfg &= ~(CSI_IF_CFG_CSI_IF_MASK | CSI_IF_CFG_MIPI_IF_MASK |
392 		 CSI_IF_CFG_IF_DATA_WIDTH_MASK |
393 		 CSI_IF_CFG_CLK_POL_MASK | CSI_IF_CFG_VREF_POL_MASK |
394 		 CSI_IF_CFG_HREF_POL_MASK | CSI_IF_CFG_FIELD_MASK |
395 		 CSI_IF_CFG_SRC_TYPE_MASK);
396 
397 	if (input_interlaced)
398 		cfg |= CSI_IF_CFG_SRC_TYPE_INTERLACED;
399 	else
400 		cfg |= CSI_IF_CFG_SRC_TYPE_PROGRESSED;
401 
402 	switch (endpoint->bus_type) {
403 	case V4L2_MBUS_PARALLEL:
404 		cfg |= CSI_IF_CFG_MIPI_IF_CSI;
405 
406 		flags = endpoint->bus.parallel.flags;
407 
408 		cfg |= (bus_width == 16) ? CSI_IF_CFG_CSI_IF_YUV422_16BIT :
409 					   CSI_IF_CFG_CSI_IF_YUV422_INTLV;
410 
411 		if (flags & V4L2_MBUS_FIELD_EVEN_LOW)
412 			cfg |= CSI_IF_CFG_FIELD_POSITIVE;
413 
414 		if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
415 			cfg |= CSI_IF_CFG_VREF_POL_POSITIVE;
416 		if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
417 			cfg |= CSI_IF_CFG_HREF_POL_POSITIVE;
418 
419 		if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
420 			cfg |= CSI_IF_CFG_CLK_POL_FALLING_EDGE;
421 		break;
422 	case V4L2_MBUS_BT656:
423 		cfg |= CSI_IF_CFG_MIPI_IF_CSI;
424 
425 		flags = endpoint->bus.parallel.flags;
426 
427 		cfg |= (bus_width == 16) ? CSI_IF_CFG_CSI_IF_BT1120 :
428 					   CSI_IF_CFG_CSI_IF_BT656;
429 
430 		if (flags & V4L2_MBUS_FIELD_EVEN_LOW)
431 			cfg |= CSI_IF_CFG_FIELD_POSITIVE;
432 
433 		if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
434 			cfg |= CSI_IF_CFG_CLK_POL_FALLING_EDGE;
435 		break;
436 	default:
437 		dev_warn(sdev->dev, "Unsupported bus type: %d\n",
438 			 endpoint->bus_type);
439 		break;
440 	}
441 
442 	switch (bus_width) {
443 	case 8:
444 		cfg |= CSI_IF_CFG_IF_DATA_WIDTH_8BIT;
445 		break;
446 	case 10:
447 		cfg |= CSI_IF_CFG_IF_DATA_WIDTH_10BIT;
448 		break;
449 	case 12:
450 		cfg |= CSI_IF_CFG_IF_DATA_WIDTH_12BIT;
451 		break;
452 	case 16: /* No need to configure DATA_WIDTH for 16bit */
453 		break;
454 	default:
455 		dev_warn(sdev->dev, "Unsupported bus width: %u\n", bus_width);
456 		break;
457 	}
458 
459 	regmap_write(sdev->regmap, CSI_IF_CFG_REG, cfg);
460 }
461 
462 static void sun6i_csi_set_format(struct sun6i_csi_dev *sdev)
463 {
464 	struct sun6i_csi *csi = &sdev->csi;
465 	u32 cfg;
466 	u32 val;
467 
468 	regmap_read(sdev->regmap, CSI_CH_CFG_REG, &cfg);
469 
470 	cfg &= ~(CSI_CH_CFG_INPUT_FMT_MASK |
471 		 CSI_CH_CFG_OUTPUT_FMT_MASK | CSI_CH_CFG_VFLIP_EN |
472 		 CSI_CH_CFG_HFLIP_EN | CSI_CH_CFG_FIELD_SEL_MASK |
473 		 CSI_CH_CFG_INPUT_SEQ_MASK);
474 
475 	val = get_csi_input_format(sdev, csi->config.code,
476 				   csi->config.pixelformat);
477 	cfg |= CSI_CH_CFG_INPUT_FMT(val);
478 
479 	val = get_csi_output_format(sdev, csi->config.pixelformat,
480 				    csi->config.field);
481 	cfg |= CSI_CH_CFG_OUTPUT_FMT(val);
482 
483 	val = get_csi_input_seq(sdev, csi->config.code,
484 				csi->config.pixelformat);
485 	cfg |= CSI_CH_CFG_INPUT_SEQ(val);
486 
487 	if (csi->config.field == V4L2_FIELD_TOP)
488 		cfg |= CSI_CH_CFG_FIELD_SEL_FIELD0;
489 	else if (csi->config.field == V4L2_FIELD_BOTTOM)
490 		cfg |= CSI_CH_CFG_FIELD_SEL_FIELD1;
491 	else
492 		cfg |= CSI_CH_CFG_FIELD_SEL_BOTH;
493 
494 	regmap_write(sdev->regmap, CSI_CH_CFG_REG, cfg);
495 }
496 
497 static void sun6i_csi_set_window(struct sun6i_csi_dev *sdev)
498 {
499 	struct sun6i_csi_config *config = &sdev->csi.config;
500 	u32 bytesperline_y;
501 	u32 bytesperline_c;
502 	int *planar_offset = sdev->planar_offset;
503 	u32 width = config->width;
504 	u32 height = config->height;
505 	u32 hor_len = width;
506 
507 	switch (config->pixelformat) {
508 	case V4L2_PIX_FMT_YUYV:
509 	case V4L2_PIX_FMT_YVYU:
510 	case V4L2_PIX_FMT_UYVY:
511 	case V4L2_PIX_FMT_VYUY:
512 		dev_dbg(sdev->dev,
513 			"Horizontal length should be 2 times of width for packed YUV formats!\n");
514 		hor_len = width * 2;
515 		break;
516 	default:
517 		break;
518 	}
519 
520 	regmap_write(sdev->regmap, CSI_CH_HSIZE_REG,
521 		     CSI_CH_HSIZE_HOR_LEN(hor_len) |
522 		     CSI_CH_HSIZE_HOR_START(0));
523 	regmap_write(sdev->regmap, CSI_CH_VSIZE_REG,
524 		     CSI_CH_VSIZE_VER_LEN(height) |
525 		     CSI_CH_VSIZE_VER_START(0));
526 
527 	planar_offset[0] = 0;
528 	switch (config->pixelformat) {
529 	case V4L2_PIX_FMT_HM12:
530 	case V4L2_PIX_FMT_NV12:
531 	case V4L2_PIX_FMT_NV21:
532 	case V4L2_PIX_FMT_NV16:
533 	case V4L2_PIX_FMT_NV61:
534 		bytesperline_y = width;
535 		bytesperline_c = width;
536 		planar_offset[1] = bytesperline_y * height;
537 		planar_offset[2] = -1;
538 		break;
539 	case V4L2_PIX_FMT_YUV420:
540 	case V4L2_PIX_FMT_YVU420:
541 		bytesperline_y = width;
542 		bytesperline_c = width / 2;
543 		planar_offset[1] = bytesperline_y * height;
544 		planar_offset[2] = planar_offset[1] +
545 				bytesperline_c * height / 2;
546 		break;
547 	case V4L2_PIX_FMT_YUV422P:
548 		bytesperline_y = width;
549 		bytesperline_c = width / 2;
550 		planar_offset[1] = bytesperline_y * height;
551 		planar_offset[2] = planar_offset[1] +
552 				bytesperline_c * height;
553 		break;
554 	default: /* raw */
555 		dev_dbg(sdev->dev,
556 			"Calculating pixelformat(0x%x)'s bytesperline as a packed format\n",
557 			config->pixelformat);
558 		bytesperline_y = (sun6i_csi_get_bpp(config->pixelformat) *
559 				  config->width) / 8;
560 		bytesperline_c = 0;
561 		planar_offset[1] = -1;
562 		planar_offset[2] = -1;
563 		break;
564 	}
565 
566 	regmap_write(sdev->regmap, CSI_CH_BUF_LEN_REG,
567 		     CSI_CH_BUF_LEN_BUF_LEN_C(bytesperline_c) |
568 		     CSI_CH_BUF_LEN_BUF_LEN_Y(bytesperline_y));
569 }
570 
571 int sun6i_csi_update_config(struct sun6i_csi *csi,
572 			    struct sun6i_csi_config *config)
573 {
574 	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
575 
576 	if (!config)
577 		return -EINVAL;
578 
579 	memcpy(&csi->config, config, sizeof(csi->config));
580 
581 	sun6i_csi_setup_bus(sdev);
582 	sun6i_csi_set_format(sdev);
583 	sun6i_csi_set_window(sdev);
584 
585 	return 0;
586 }
587 
588 void sun6i_csi_update_buf_addr(struct sun6i_csi *csi, dma_addr_t addr)
589 {
590 	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
591 
592 	regmap_write(sdev->regmap, CSI_CH_F0_BUFA_REG,
593 		     (addr + sdev->planar_offset[0]) >> 2);
594 	if (sdev->planar_offset[1] != -1)
595 		regmap_write(sdev->regmap, CSI_CH_F1_BUFA_REG,
596 			     (addr + sdev->planar_offset[1]) >> 2);
597 	if (sdev->planar_offset[2] != -1)
598 		regmap_write(sdev->regmap, CSI_CH_F2_BUFA_REG,
599 			     (addr + sdev->planar_offset[2]) >> 2);
600 }
601 
602 void sun6i_csi_set_stream(struct sun6i_csi *csi, bool enable)
603 {
604 	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
605 	struct regmap *regmap = sdev->regmap;
606 
607 	if (!enable) {
608 		regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON, 0);
609 		regmap_write(regmap, CSI_CH_INT_EN_REG, 0);
610 		return;
611 	}
612 
613 	regmap_write(regmap, CSI_CH_INT_STA_REG, 0xFF);
614 	regmap_write(regmap, CSI_CH_INT_EN_REG,
615 		     CSI_CH_INT_EN_HB_OF_INT_EN |
616 		     CSI_CH_INT_EN_FIFO2_OF_INT_EN |
617 		     CSI_CH_INT_EN_FIFO1_OF_INT_EN |
618 		     CSI_CH_INT_EN_FIFO0_OF_INT_EN |
619 		     CSI_CH_INT_EN_FD_INT_EN |
620 		     CSI_CH_INT_EN_CD_INT_EN);
621 
622 	regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON,
623 			   CSI_CAP_CH0_VCAP_ON);
624 }
625 
626 /* -----------------------------------------------------------------------------
627  * Media Controller and V4L2
628  */
629 static int sun6i_csi_link_entity(struct sun6i_csi *csi,
630 				 struct media_entity *entity,
631 				 struct fwnode_handle *fwnode)
632 {
633 	struct media_entity *sink;
634 	struct media_pad *sink_pad;
635 	int src_pad_index;
636 	int ret;
637 
638 	ret = media_entity_get_fwnode_pad(entity, fwnode, MEDIA_PAD_FL_SOURCE);
639 	if (ret < 0) {
640 		dev_err(csi->dev, "%s: no source pad in external entity %s\n",
641 			__func__, entity->name);
642 		return -EINVAL;
643 	}
644 
645 	src_pad_index = ret;
646 
647 	sink = &csi->video.vdev.entity;
648 	sink_pad = &csi->video.pad;
649 
650 	dev_dbg(csi->dev, "creating %s:%u -> %s:%u link\n",
651 		entity->name, src_pad_index, sink->name, sink_pad->index);
652 	ret = media_create_pad_link(entity, src_pad_index, sink,
653 				    sink_pad->index,
654 				    MEDIA_LNK_FL_ENABLED |
655 				    MEDIA_LNK_FL_IMMUTABLE);
656 	if (ret < 0) {
657 		dev_err(csi->dev, "failed to create %s:%u -> %s:%u link\n",
658 			entity->name, src_pad_index,
659 			sink->name, sink_pad->index);
660 		return ret;
661 	}
662 
663 	return 0;
664 }
665 
666 static int sun6i_subdev_notify_complete(struct v4l2_async_notifier *notifier)
667 {
668 	struct sun6i_csi *csi = container_of(notifier, struct sun6i_csi,
669 					     notifier);
670 	struct v4l2_device *v4l2_dev = &csi->v4l2_dev;
671 	struct v4l2_subdev *sd;
672 	int ret;
673 
674 	dev_dbg(csi->dev, "notify complete, all subdevs registered\n");
675 
676 	sd = list_first_entry(&v4l2_dev->subdevs, struct v4l2_subdev, list);
677 	if (!sd)
678 		return -EINVAL;
679 
680 	ret = sun6i_csi_link_entity(csi, &sd->entity, sd->fwnode);
681 	if (ret < 0)
682 		return ret;
683 
684 	ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev);
685 	if (ret < 0)
686 		return ret;
687 
688 	return media_device_register(&csi->media_dev);
689 }
690 
691 static const struct v4l2_async_notifier_operations sun6i_csi_async_ops = {
692 	.complete = sun6i_subdev_notify_complete,
693 };
694 
695 static int sun6i_csi_fwnode_parse(struct device *dev,
696 				  struct v4l2_fwnode_endpoint *vep,
697 				  struct v4l2_async_subdev *asd)
698 {
699 	struct sun6i_csi *csi = dev_get_drvdata(dev);
700 
701 	if (vep->base.port || vep->base.id) {
702 		dev_warn(dev, "Only support a single port with one endpoint\n");
703 		return -ENOTCONN;
704 	}
705 
706 	switch (vep->bus_type) {
707 	case V4L2_MBUS_PARALLEL:
708 	case V4L2_MBUS_BT656:
709 		csi->v4l2_ep = *vep;
710 		return 0;
711 	default:
712 		dev_err(dev, "Unsupported media bus type\n");
713 		return -ENOTCONN;
714 	}
715 }
716 
717 static void sun6i_csi_v4l2_cleanup(struct sun6i_csi *csi)
718 {
719 	media_device_unregister(&csi->media_dev);
720 	v4l2_async_notifier_unregister(&csi->notifier);
721 	v4l2_async_notifier_cleanup(&csi->notifier);
722 	sun6i_video_cleanup(&csi->video);
723 	v4l2_device_unregister(&csi->v4l2_dev);
724 	v4l2_ctrl_handler_free(&csi->ctrl_handler);
725 	media_device_cleanup(&csi->media_dev);
726 }
727 
728 static int sun6i_csi_v4l2_init(struct sun6i_csi *csi)
729 {
730 	int ret;
731 
732 	csi->media_dev.dev = csi->dev;
733 	strscpy(csi->media_dev.model, "Allwinner Video Capture Device",
734 		sizeof(csi->media_dev.model));
735 	csi->media_dev.hw_revision = 0;
736 
737 	media_device_init(&csi->media_dev);
738 	v4l2_async_notifier_init(&csi->notifier);
739 
740 	ret = v4l2_ctrl_handler_init(&csi->ctrl_handler, 0);
741 	if (ret) {
742 		dev_err(csi->dev, "V4L2 controls handler init failed (%d)\n",
743 			ret);
744 		goto clean_media;
745 	}
746 
747 	csi->v4l2_dev.mdev = &csi->media_dev;
748 	csi->v4l2_dev.ctrl_handler = &csi->ctrl_handler;
749 	ret = v4l2_device_register(csi->dev, &csi->v4l2_dev);
750 	if (ret) {
751 		dev_err(csi->dev, "V4L2 device registration failed (%d)\n",
752 			ret);
753 		goto free_ctrl;
754 	}
755 
756 	ret = sun6i_video_init(&csi->video, csi, "sun6i-csi");
757 	if (ret)
758 		goto unreg_v4l2;
759 
760 	ret = v4l2_async_notifier_parse_fwnode_endpoints(csi->dev,
761 							 &csi->notifier,
762 							 sizeof(struct v4l2_async_subdev),
763 							 sun6i_csi_fwnode_parse);
764 	if (ret)
765 		goto clean_video;
766 
767 	csi->notifier.ops = &sun6i_csi_async_ops;
768 
769 	ret = v4l2_async_notifier_register(&csi->v4l2_dev, &csi->notifier);
770 	if (ret) {
771 		dev_err(csi->dev, "notifier registration failed\n");
772 		goto clean_video;
773 	}
774 
775 	return 0;
776 
777 clean_video:
778 	sun6i_video_cleanup(&csi->video);
779 unreg_v4l2:
780 	v4l2_device_unregister(&csi->v4l2_dev);
781 free_ctrl:
782 	v4l2_ctrl_handler_free(&csi->ctrl_handler);
783 clean_media:
784 	v4l2_async_notifier_cleanup(&csi->notifier);
785 	media_device_cleanup(&csi->media_dev);
786 
787 	return ret;
788 }
789 
790 /* -----------------------------------------------------------------------------
791  * Resources and IRQ
792  */
793 static irqreturn_t sun6i_csi_isr(int irq, void *dev_id)
794 {
795 	struct sun6i_csi_dev *sdev = (struct sun6i_csi_dev *)dev_id;
796 	struct regmap *regmap = sdev->regmap;
797 	u32 status;
798 
799 	regmap_read(regmap, CSI_CH_INT_STA_REG, &status);
800 
801 	if (!(status & 0xFF))
802 		return IRQ_NONE;
803 
804 	if ((status & CSI_CH_INT_STA_FIFO0_OF_PD) ||
805 	    (status & CSI_CH_INT_STA_FIFO1_OF_PD) ||
806 	    (status & CSI_CH_INT_STA_FIFO2_OF_PD) ||
807 	    (status & CSI_CH_INT_STA_HB_OF_PD)) {
808 		regmap_write(regmap, CSI_CH_INT_STA_REG, status);
809 		regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0);
810 		regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN,
811 				   CSI_EN_CSI_EN);
812 		return IRQ_HANDLED;
813 	}
814 
815 	if (status & CSI_CH_INT_STA_FD_PD)
816 		sun6i_video_frame_done(&sdev->csi.video);
817 
818 	regmap_write(regmap, CSI_CH_INT_STA_REG, status);
819 
820 	return IRQ_HANDLED;
821 }
822 
823 static const struct regmap_config sun6i_csi_regmap_config = {
824 	.reg_bits       = 32,
825 	.reg_stride     = 4,
826 	.val_bits       = 32,
827 	.max_register	= 0x9c,
828 };
829 
830 static int sun6i_csi_resource_request(struct sun6i_csi_dev *sdev,
831 				      struct platform_device *pdev)
832 {
833 	struct resource *res;
834 	void __iomem *io_base;
835 	int ret;
836 	int irq;
837 
838 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
839 	io_base = devm_ioremap_resource(&pdev->dev, res);
840 	if (IS_ERR(io_base))
841 		return PTR_ERR(io_base);
842 
843 	sdev->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "bus", io_base,
844 						 &sun6i_csi_regmap_config);
845 	if (IS_ERR(sdev->regmap)) {
846 		dev_err(&pdev->dev, "Failed to init register map\n");
847 		return PTR_ERR(sdev->regmap);
848 	}
849 
850 	sdev->clk_mod = devm_clk_get(&pdev->dev, "mod");
851 	if (IS_ERR(sdev->clk_mod)) {
852 		dev_err(&pdev->dev, "Unable to acquire csi clock\n");
853 		return PTR_ERR(sdev->clk_mod);
854 	}
855 
856 	sdev->clk_ram = devm_clk_get(&pdev->dev, "ram");
857 	if (IS_ERR(sdev->clk_ram)) {
858 		dev_err(&pdev->dev, "Unable to acquire dram-csi clock\n");
859 		return PTR_ERR(sdev->clk_ram);
860 	}
861 
862 	sdev->rstc_bus = devm_reset_control_get_shared(&pdev->dev, NULL);
863 	if (IS_ERR(sdev->rstc_bus)) {
864 		dev_err(&pdev->dev, "Cannot get reset controller\n");
865 		return PTR_ERR(sdev->rstc_bus);
866 	}
867 
868 	irq = platform_get_irq(pdev, 0);
869 	if (irq < 0)
870 		return -ENXIO;
871 
872 	ret = devm_request_irq(&pdev->dev, irq, sun6i_csi_isr, 0, MODULE_NAME,
873 			       sdev);
874 	if (ret) {
875 		dev_err(&pdev->dev, "Cannot request csi IRQ\n");
876 		return ret;
877 	}
878 
879 	return 0;
880 }
881 
882 /*
883  * PHYS_OFFSET isn't available on all architectures. In order to
884  * accommodate for COMPILE_TEST, let's define it to something dumb.
885  */
886 #if defined(CONFIG_COMPILE_TEST) && !defined(PHYS_OFFSET)
887 #define PHYS_OFFSET 0
888 #endif
889 
890 static int sun6i_csi_probe(struct platform_device *pdev)
891 {
892 	struct sun6i_csi_dev *sdev;
893 	int ret;
894 
895 	sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL);
896 	if (!sdev)
897 		return -ENOMEM;
898 
899 	sdev->dev = &pdev->dev;
900 	/* The DMA bus has the memory mapped at 0 */
901 	sdev->dev->dma_pfn_offset = PHYS_OFFSET >> PAGE_SHIFT;
902 
903 	ret = sun6i_csi_resource_request(sdev, pdev);
904 	if (ret)
905 		return ret;
906 
907 	platform_set_drvdata(pdev, sdev);
908 
909 	sdev->csi.dev = &pdev->dev;
910 	return sun6i_csi_v4l2_init(&sdev->csi);
911 }
912 
913 static int sun6i_csi_remove(struct platform_device *pdev)
914 {
915 	struct sun6i_csi_dev *sdev = platform_get_drvdata(pdev);
916 
917 	sun6i_csi_v4l2_cleanup(&sdev->csi);
918 
919 	return 0;
920 }
921 
922 static const struct of_device_id sun6i_csi_of_match[] = {
923 	{ .compatible = "allwinner,sun6i-a31-csi", },
924 	{ .compatible = "allwinner,sun8i-a83t-csi", },
925 	{ .compatible = "allwinner,sun8i-h3-csi", },
926 	{ .compatible = "allwinner,sun8i-v3s-csi", },
927 	{ .compatible = "allwinner,sun50i-a64-csi", },
928 	{},
929 };
930 MODULE_DEVICE_TABLE(of, sun6i_csi_of_match);
931 
932 static struct platform_driver sun6i_csi_platform_driver = {
933 	.probe = sun6i_csi_probe,
934 	.remove = sun6i_csi_remove,
935 	.driver = {
936 		.name = MODULE_NAME,
937 		.of_match_table = of_match_ptr(sun6i_csi_of_match),
938 	},
939 };
940 module_platform_driver(sun6i_csi_platform_driver);
941 
942 MODULE_DESCRIPTION("Allwinner V3s Camera Sensor Interface driver");
943 MODULE_AUTHOR("Yong Deng <yong.deng@magewell.com>");
944 MODULE_LICENSE("GPL");
945