xref: /linux/drivers/media/platform/qcom/camss/camss-vfe.c (revision ed38a1469b313b8427973e9b21c0e27714978293)
1b873663bSTodor Tomov // SPDX-License-Identifier: GPL-2.0
24c98a5f5STodor Tomov /*
34c98a5f5STodor Tomov  * camss-vfe.c
44c98a5f5STodor Tomov  *
5be744eecSTodor Tomov  * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module
64c98a5f5STodor Tomov  *
74c98a5f5STodor Tomov  * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
8ec6859b2STodor Tomov  * Copyright (C) 2015-2018 Linaro Ltd.
94c98a5f5STodor Tomov  */
104c98a5f5STodor Tomov #include <linux/clk.h>
114c98a5f5STodor Tomov #include <linux/completion.h>
124c98a5f5STodor Tomov #include <linux/interrupt.h>
134c98a5f5STodor Tomov #include <linux/iommu.h>
144c98a5f5STodor Tomov #include <linux/mutex.h>
154c98a5f5STodor Tomov #include <linux/of.h>
164c98a5f5STodor Tomov #include <linux/platform_device.h>
1702afa816STodor Tomov #include <linux/pm_runtime.h>
184c98a5f5STodor Tomov #include <linux/spinlock_types.h>
194c98a5f5STodor Tomov #include <linux/spinlock.h>
204c98a5f5STodor Tomov #include <media/media-entity.h>
214c98a5f5STodor Tomov #include <media/v4l2-device.h>
224c98a5f5STodor Tomov #include <media/v4l2-subdev.h>
234c98a5f5STodor Tomov 
244c98a5f5STodor Tomov #include "camss-vfe.h"
254c98a5f5STodor Tomov #include "camss.h"
264c98a5f5STodor Tomov 
274c98a5f5STodor Tomov #define MSM_VFE_NAME "msm_vfe"
284c98a5f5STodor Tomov 
294c98a5f5STodor Tomov /* VFE reset timeout */
304c98a5f5STodor Tomov #define VFE_RESET_TIMEOUT_MS 50
319b5833f7STodor Tomov 
32810b6598STodor Tomov #define SCALER_RATIO_MAX 16
33810b6598STodor Tomov 
34cba3819dSTodor Tomov struct vfe_format {
35bbde3104STodor Tomov 	u32 code;
36bbde3104STodor Tomov 	u8 bpp;
37cba3819dSTodor Tomov };
38cba3819dSTodor Tomov 
39cba3819dSTodor Tomov static const struct vfe_format formats_rdi_8x16[] = {
40cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, 8 },
41cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, 8 },
42cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, 8 },
43cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, 8 },
44cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SBGGR8_1X8, 8 },
45cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGBRG8_1X8, 8 },
46cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGRBG8_1X8, 8 },
47cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SRGGB8_1X8, 8 },
48cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
49cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
50cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
51cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
52cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SBGGR12_1X12, 12 },
53cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGBRG12_1X12, 12 },
54cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGRBG12_1X12, 12 },
55cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
56cc8fe073STodor Tomov 	{ MEDIA_BUS_FMT_Y10_1X10, 10 },
57cba3819dSTodor Tomov };
58cba3819dSTodor Tomov 
59cba3819dSTodor Tomov static const struct vfe_format formats_pix_8x16[] = {
60cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, 8 },
61cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, 8 },
62cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, 8 },
63cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, 8 },
64cba3819dSTodor Tomov };
65cba3819dSTodor Tomov 
66cba3819dSTodor Tomov static const struct vfe_format formats_rdi_8x96[] = {
67cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, 8 },
68cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, 8 },
69cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, 8 },
70cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, 8 },
71cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SBGGR8_1X8, 8 },
72cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGBRG8_1X8, 8 },
73cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGRBG8_1X8, 8 },
74cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SRGGB8_1X8, 8 },
75cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
76cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
77cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
78cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
795019d7c8STodor Tomov 	{ MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16 },
80cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SBGGR12_1X12, 12 },
81cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGBRG12_1X12, 12 },
82cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SGRBG12_1X12, 12 },
83cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
84f476fb56STodor Tomov 	{ MEDIA_BUS_FMT_SBGGR14_1X14, 14 },
85f476fb56STodor Tomov 	{ MEDIA_BUS_FMT_SGBRG14_1X14, 14 },
86f476fb56STodor Tomov 	{ MEDIA_BUS_FMT_SGRBG14_1X14, 14 },
87f476fb56STodor Tomov 	{ MEDIA_BUS_FMT_SRGGB14_1X14, 14 },
88cc8fe073STodor Tomov 	{ MEDIA_BUS_FMT_Y10_1X10, 10 },
89cc8fe073STodor Tomov 	{ MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 },
90cba3819dSTodor Tomov };
91cba3819dSTodor Tomov 
92cba3819dSTodor Tomov static const struct vfe_format formats_pix_8x96[] = {
93cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_UYVY8_2X8, 8 },
94cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_VYUY8_2X8, 8 },
95cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YUYV8_2X8, 8 },
96cba3819dSTodor Tomov 	{ MEDIA_BUS_FMT_YVYU8_2X8, 8 },
974c98a5f5STodor Tomov };
984c98a5f5STodor Tomov 
997319cdf1SRobert Foss static const struct vfe_format formats_rdi_845[] = {
1007319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_UYVY8_2X8, 8 },
1017319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_VYUY8_2X8, 8 },
1027319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_YUYV8_2X8, 8 },
1037319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_YVYU8_2X8, 8 },
1047319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SBGGR8_1X8, 8 },
1057319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SGBRG8_1X8, 8 },
1067319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SGRBG8_1X8, 8 },
1077319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SRGGB8_1X8, 8 },
1087319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
1097319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
1107319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
1117319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
1127319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16 },
1137319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SBGGR12_1X12, 12 },
1147319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SGBRG12_1X12, 12 },
1157319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SGRBG12_1X12, 12 },
1167319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
1177319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SBGGR14_1X14, 14 },
1187319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SGBRG14_1X14, 14 },
1197319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SGRBG14_1X14, 14 },
1207319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_SRGGB14_1X14, 14 },
1217319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_Y10_1X10, 10 },
1227319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 },
1237319cdf1SRobert Foss };
1247319cdf1SRobert Foss 
125bbde3104STodor Tomov /*
126bbde3104STodor Tomov  * vfe_get_bpp - map media bus format to bits per pixel
127cba3819dSTodor Tomov  * @formats: supported media bus formats array
128cba3819dSTodor Tomov  * @nformats: size of @formats array
129bbde3104STodor Tomov  * @code: media bus format code
130bbde3104STodor Tomov  *
131bbde3104STodor Tomov  * Return number of bits per pixel
132bbde3104STodor Tomov  */
133cba3819dSTodor Tomov static u8 vfe_get_bpp(const struct vfe_format *formats,
134cba3819dSTodor Tomov 		      unsigned int nformats, u32 code)
135bbde3104STodor Tomov {
136bbde3104STodor Tomov 	unsigned int i;
137bbde3104STodor Tomov 
138cba3819dSTodor Tomov 	for (i = 0; i < nformats; i++)
139cba3819dSTodor Tomov 		if (code == formats[i].code)
140cba3819dSTodor Tomov 			return formats[i].bpp;
141bbde3104STodor Tomov 
142bbde3104STodor Tomov 	WARN(1, "Unknown format\n");
143bbde3104STodor Tomov 
144cba3819dSTodor Tomov 	return formats[0].bpp;
145bbde3104STodor Tomov }
146bbde3104STodor Tomov 
14707eeb342STodor Tomov static u32 vfe_find_code(u32 *code, unsigned int n_code,
14807eeb342STodor Tomov 			 unsigned int index, u32 req_code)
14907eeb342STodor Tomov {
15007eeb342STodor Tomov 	int i;
15107eeb342STodor Tomov 
15207eeb342STodor Tomov 	if (!req_code && (index >= n_code))
15307eeb342STodor Tomov 		return 0;
15407eeb342STodor Tomov 
15507eeb342STodor Tomov 	for (i = 0; i < n_code; i++)
15607eeb342STodor Tomov 		if (req_code) {
15707eeb342STodor Tomov 			if (req_code == code[i])
15807eeb342STodor Tomov 				return req_code;
15907eeb342STodor Tomov 		} else {
16007eeb342STodor Tomov 			if (i == index)
16107eeb342STodor Tomov 				return code[i];
16207eeb342STodor Tomov 		}
16307eeb342STodor Tomov 
16407eeb342STodor Tomov 	return code[0];
16507eeb342STodor Tomov }
16607eeb342STodor Tomov 
16707eeb342STodor Tomov static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
16807eeb342STodor Tomov 			    unsigned int index, u32 src_req_code)
16907eeb342STodor Tomov {
17007eeb342STodor Tomov 	struct vfe_device *vfe = to_vfe(line);
17107eeb342STodor Tomov 
17207eeb342STodor Tomov 	if (vfe->camss->version == CAMSS_8x16)
17307eeb342STodor Tomov 		switch (sink_code) {
17407eeb342STodor Tomov 		case MEDIA_BUS_FMT_YUYV8_2X8:
17507eeb342STodor Tomov 		{
17607eeb342STodor Tomov 			u32 src_code[] = {
17707eeb342STodor Tomov 				MEDIA_BUS_FMT_YUYV8_2X8,
17807eeb342STodor Tomov 				MEDIA_BUS_FMT_YUYV8_1_5X8,
17907eeb342STodor Tomov 			};
18007eeb342STodor Tomov 
18107eeb342STodor Tomov 			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
18207eeb342STodor Tomov 					     index, src_req_code);
18307eeb342STodor Tomov 		}
18407eeb342STodor Tomov 		case MEDIA_BUS_FMT_YVYU8_2X8:
18507eeb342STodor Tomov 		{
18607eeb342STodor Tomov 			u32 src_code[] = {
18707eeb342STodor Tomov 				MEDIA_BUS_FMT_YVYU8_2X8,
18807eeb342STodor Tomov 				MEDIA_BUS_FMT_YVYU8_1_5X8,
18907eeb342STodor Tomov 			};
19007eeb342STodor Tomov 
19107eeb342STodor Tomov 			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
19207eeb342STodor Tomov 					     index, src_req_code);
19307eeb342STodor Tomov 		}
19407eeb342STodor Tomov 		case MEDIA_BUS_FMT_UYVY8_2X8:
19507eeb342STodor Tomov 		{
19607eeb342STodor Tomov 			u32 src_code[] = {
19707eeb342STodor Tomov 				MEDIA_BUS_FMT_UYVY8_2X8,
19807eeb342STodor Tomov 				MEDIA_BUS_FMT_UYVY8_1_5X8,
19907eeb342STodor Tomov 			};
20007eeb342STodor Tomov 
20107eeb342STodor Tomov 			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
20207eeb342STodor Tomov 					     index, src_req_code);
20307eeb342STodor Tomov 		}
20407eeb342STodor Tomov 		case MEDIA_BUS_FMT_VYUY8_2X8:
20507eeb342STodor Tomov 		{
20607eeb342STodor Tomov 			u32 src_code[] = {
20707eeb342STodor Tomov 				MEDIA_BUS_FMT_VYUY8_2X8,
20807eeb342STodor Tomov 				MEDIA_BUS_FMT_VYUY8_1_5X8,
20907eeb342STodor Tomov 			};
21007eeb342STodor Tomov 
21107eeb342STodor Tomov 			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
21207eeb342STodor Tomov 					     index, src_req_code);
21307eeb342STodor Tomov 		}
21407eeb342STodor Tomov 		default:
21507eeb342STodor Tomov 			if (index > 0)
21607eeb342STodor Tomov 				return 0;
21707eeb342STodor Tomov 
21807eeb342STodor Tomov 			return sink_code;
21907eeb342STodor Tomov 		}
2209e5d1581SAngeloGioacchino Del Regno 	else if (vfe->camss->version == CAMSS_8x96 ||
2217319cdf1SRobert Foss 		 vfe->camss->version == CAMSS_660 ||
2227319cdf1SRobert Foss 		 vfe->camss->version == CAMSS_845)
22307eeb342STodor Tomov 		switch (sink_code) {
22407eeb342STodor Tomov 		case MEDIA_BUS_FMT_YUYV8_2X8:
22507eeb342STodor Tomov 		{
22607eeb342STodor Tomov 			u32 src_code[] = {
22707eeb342STodor Tomov 				MEDIA_BUS_FMT_YUYV8_2X8,
228312e1c85STodor Tomov 				MEDIA_BUS_FMT_YVYU8_2X8,
229312e1c85STodor Tomov 				MEDIA_BUS_FMT_UYVY8_2X8,
230312e1c85STodor Tomov 				MEDIA_BUS_FMT_VYUY8_2X8,
23107eeb342STodor Tomov 				MEDIA_BUS_FMT_YUYV8_1_5X8,
23207eeb342STodor Tomov 			};
23307eeb342STodor Tomov 
23407eeb342STodor Tomov 			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
23507eeb342STodor Tomov 					     index, src_req_code);
23607eeb342STodor Tomov 		}
23707eeb342STodor Tomov 		case MEDIA_BUS_FMT_YVYU8_2X8:
23807eeb342STodor Tomov 		{
23907eeb342STodor Tomov 			u32 src_code[] = {
24007eeb342STodor Tomov 				MEDIA_BUS_FMT_YVYU8_2X8,
241312e1c85STodor Tomov 				MEDIA_BUS_FMT_YUYV8_2X8,
242312e1c85STodor Tomov 				MEDIA_BUS_FMT_UYVY8_2X8,
243312e1c85STodor Tomov 				MEDIA_BUS_FMT_VYUY8_2X8,
24407eeb342STodor Tomov 				MEDIA_BUS_FMT_YVYU8_1_5X8,
24507eeb342STodor Tomov 			};
24607eeb342STodor Tomov 
24707eeb342STodor Tomov 			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
24807eeb342STodor Tomov 					     index, src_req_code);
24907eeb342STodor Tomov 		}
25007eeb342STodor Tomov 		case MEDIA_BUS_FMT_UYVY8_2X8:
25107eeb342STodor Tomov 		{
25207eeb342STodor Tomov 			u32 src_code[] = {
25307eeb342STodor Tomov 				MEDIA_BUS_FMT_UYVY8_2X8,
254312e1c85STodor Tomov 				MEDIA_BUS_FMT_YUYV8_2X8,
255312e1c85STodor Tomov 				MEDIA_BUS_FMT_YVYU8_2X8,
256312e1c85STodor Tomov 				MEDIA_BUS_FMT_VYUY8_2X8,
25707eeb342STodor Tomov 				MEDIA_BUS_FMT_UYVY8_1_5X8,
25807eeb342STodor Tomov 			};
25907eeb342STodor Tomov 
26007eeb342STodor Tomov 			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
26107eeb342STodor Tomov 					     index, src_req_code);
26207eeb342STodor Tomov 		}
26307eeb342STodor Tomov 		case MEDIA_BUS_FMT_VYUY8_2X8:
26407eeb342STodor Tomov 		{
26507eeb342STodor Tomov 			u32 src_code[] = {
26607eeb342STodor Tomov 				MEDIA_BUS_FMT_VYUY8_2X8,
267312e1c85STodor Tomov 				MEDIA_BUS_FMT_YUYV8_2X8,
268312e1c85STodor Tomov 				MEDIA_BUS_FMT_YVYU8_2X8,
269312e1c85STodor Tomov 				MEDIA_BUS_FMT_UYVY8_2X8,
27007eeb342STodor Tomov 				MEDIA_BUS_FMT_VYUY8_1_5X8,
27107eeb342STodor Tomov 			};
27207eeb342STodor Tomov 
27307eeb342STodor Tomov 			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
27407eeb342STodor Tomov 					     index, src_req_code);
27507eeb342STodor Tomov 		}
27607eeb342STodor Tomov 		default:
27707eeb342STodor Tomov 			if (index > 0)
27807eeb342STodor Tomov 				return 0;
27907eeb342STodor Tomov 
28007eeb342STodor Tomov 			return sink_code;
28107eeb342STodor Tomov 		}
28207eeb342STodor Tomov 	else
28307eeb342STodor Tomov 		return 0;
28407eeb342STodor Tomov }
28507eeb342STodor Tomov 
2867319cdf1SRobert Foss int vfe_reset(struct vfe_device *vfe)
2874c98a5f5STodor Tomov {
2884c98a5f5STodor Tomov 	unsigned long time;
2894c98a5f5STodor Tomov 
2904c98a5f5STodor Tomov 	reinit_completion(&vfe->reset_complete);
2914c98a5f5STodor Tomov 
292051a01acSTodor Tomov 	vfe->ops->global_reset(vfe);
2934c98a5f5STodor Tomov 
2944c98a5f5STodor Tomov 	time = wait_for_completion_timeout(&vfe->reset_complete,
2954c98a5f5STodor Tomov 		msecs_to_jiffies(VFE_RESET_TIMEOUT_MS));
2964c98a5f5STodor Tomov 	if (!time) {
2979c3e59deSTodor Tomov 		dev_err(vfe->camss->dev, "VFE reset timeout\n");
2984c98a5f5STodor Tomov 		return -EIO;
2994c98a5f5STodor Tomov 	}
3004c98a5f5STodor Tomov 
3014c98a5f5STodor Tomov 	return 0;
3024c98a5f5STodor Tomov }
3034c98a5f5STodor Tomov 
3044c98a5f5STodor Tomov static void vfe_init_outputs(struct vfe_device *vfe)
3054c98a5f5STodor Tomov {
3064c98a5f5STodor Tomov 	int i;
3074c98a5f5STodor Tomov 
308633b388fSRobert Foss 	for (i = 0; i < vfe->line_num; i++) {
3094c98a5f5STodor Tomov 		struct vfe_output *output = &vfe->line[i].output;
3104c98a5f5STodor Tomov 
3114c98a5f5STodor Tomov 		output->state = VFE_OUTPUT_OFF;
3124c98a5f5STodor Tomov 		output->buf[0] = NULL;
3134c98a5f5STodor Tomov 		output->buf[1] = NULL;
3144c98a5f5STodor Tomov 		INIT_LIST_HEAD(&output->pending_bufs);
3154c98a5f5STodor Tomov 	}
3164c98a5f5STodor Tomov }
3174c98a5f5STodor Tomov 
3184c98a5f5STodor Tomov static void vfe_reset_output_maps(struct vfe_device *vfe)
3194c98a5f5STodor Tomov {
3204c98a5f5STodor Tomov 	int i;
3214c98a5f5STodor Tomov 
3224c98a5f5STodor Tomov 	for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++)
3234c98a5f5STodor Tomov 		vfe->wm_output_map[i] = VFE_LINE_NONE;
3244c98a5f5STodor Tomov }
3254c98a5f5STodor Tomov 
326633b388fSRobert Foss int vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id)
3274c98a5f5STodor Tomov {
3284c98a5f5STodor Tomov 	int ret = -EBUSY;
3294c98a5f5STodor Tomov 	int i;
3304c98a5f5STodor Tomov 
3314c98a5f5STodor Tomov 	for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++) {
3324c98a5f5STodor Tomov 		if (vfe->wm_output_map[i] == VFE_LINE_NONE) {
3334c98a5f5STodor Tomov 			vfe->wm_output_map[i] = line_id;
3344c98a5f5STodor Tomov 			ret = i;
3354c98a5f5STodor Tomov 			break;
3364c98a5f5STodor Tomov 		}
3374c98a5f5STodor Tomov 	}
3384c98a5f5STodor Tomov 
3394c98a5f5STodor Tomov 	return ret;
3404c98a5f5STodor Tomov }
3414c98a5f5STodor Tomov 
342633b388fSRobert Foss int vfe_release_wm(struct vfe_device *vfe, u8 wm)
3434c98a5f5STodor Tomov {
3446e893ca2SMauro Carvalho Chehab 	if (wm >= ARRAY_SIZE(vfe->wm_output_map))
3454c98a5f5STodor Tomov 		return -EINVAL;
3464c98a5f5STodor Tomov 
3474c98a5f5STodor Tomov 	vfe->wm_output_map[wm] = VFE_LINE_NONE;
3484c98a5f5STodor Tomov 
3494c98a5f5STodor Tomov 	return 0;
3504c98a5f5STodor Tomov }
3514c98a5f5STodor Tomov 
352633b388fSRobert Foss struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output)
3534c98a5f5STodor Tomov {
3544c98a5f5STodor Tomov 	struct camss_buffer *buffer = NULL;
3554c98a5f5STodor Tomov 
3564c98a5f5STodor Tomov 	if (!list_empty(&output->pending_bufs)) {
3574c98a5f5STodor Tomov 		buffer = list_first_entry(&output->pending_bufs,
3584c98a5f5STodor Tomov 					  struct camss_buffer,
3594c98a5f5STodor Tomov 					  queue);
3604c98a5f5STodor Tomov 		list_del(&buffer->queue);
3614c98a5f5STodor Tomov 	}
3624c98a5f5STodor Tomov 
3634c98a5f5STodor Tomov 	return buffer;
3644c98a5f5STodor Tomov }
3654c98a5f5STodor Tomov 
366633b388fSRobert Foss void vfe_buf_add_pending(struct vfe_output *output,
3674c98a5f5STodor Tomov 			 struct camss_buffer *buffer)
3684c98a5f5STodor Tomov {
3694c98a5f5STodor Tomov 	INIT_LIST_HEAD(&buffer->queue);
3704c98a5f5STodor Tomov 	list_add_tail(&buffer->queue, &output->pending_bufs);
3714c98a5f5STodor Tomov }
3724c98a5f5STodor Tomov 
3734c98a5f5STodor Tomov /*
3744c98a5f5STodor Tomov  * vfe_buf_flush_pending - Flush all pending buffers.
3754c98a5f5STodor Tomov  * @output: VFE output
3764c98a5f5STodor Tomov  * @state: vb2 buffer state
3774c98a5f5STodor Tomov  */
3784c98a5f5STodor Tomov static void vfe_buf_flush_pending(struct vfe_output *output,
3794c98a5f5STodor Tomov 				  enum vb2_buffer_state state)
3804c98a5f5STodor Tomov {
3814c98a5f5STodor Tomov 	struct camss_buffer *buf;
3824c98a5f5STodor Tomov 	struct camss_buffer *t;
3834c98a5f5STodor Tomov 
3844c98a5f5STodor Tomov 	list_for_each_entry_safe(buf, t, &output->pending_bufs, queue) {
3854c98a5f5STodor Tomov 		vb2_buffer_done(&buf->vb.vb2_buf, state);
3864c98a5f5STodor Tomov 		list_del(&buf->queue);
3874c98a5f5STodor Tomov 	}
3884c98a5f5STodor Tomov }
3894c98a5f5STodor Tomov 
390633b388fSRobert Foss int vfe_put_output(struct vfe_line *line)
3914c98a5f5STodor Tomov {
3924c98a5f5STodor Tomov 	struct vfe_device *vfe = to_vfe(line);
3934c98a5f5STodor Tomov 	struct vfe_output *output = &line->output;
3944c98a5f5STodor Tomov 	unsigned long flags;
3959b5833f7STodor Tomov 	unsigned int i;
3964c98a5f5STodor Tomov 
3974c98a5f5STodor Tomov 	spin_lock_irqsave(&vfe->output_lock, flags);
3984c98a5f5STodor Tomov 
3999b5833f7STodor Tomov 	for (i = 0; i < output->wm_num; i++)
4009b5833f7STodor Tomov 		vfe_release_wm(vfe, output->wm_idx[i]);
4014c98a5f5STodor Tomov 
4024c98a5f5STodor Tomov 	output->state = VFE_OUTPUT_OFF;
4034c98a5f5STodor Tomov 
4044c98a5f5STodor Tomov 	spin_unlock_irqrestore(&vfe->output_lock, flags);
4059b5833f7STodor Tomov 	return 0;
4064c98a5f5STodor Tomov }
4074c98a5f5STodor Tomov 
408d8bdc3e4SRobert Foss /**
409d8bdc3e4SRobert Foss  * vfe_isr_comp_done() - Process composite image done interrupt
4109b5833f7STodor Tomov  * @vfe: VFE Device
4119b5833f7STodor Tomov  * @comp: Composite image id
4129b5833f7STodor Tomov  */
413633b388fSRobert Foss void vfe_isr_comp_done(struct vfe_device *vfe, u8 comp)
4149b5833f7STodor Tomov {
4159b5833f7STodor Tomov 	unsigned int i;
4169b5833f7STodor Tomov 
4179b5833f7STodor Tomov 	for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++)
4189b5833f7STodor Tomov 		if (vfe->wm_output_map[i] == VFE_LINE_PIX) {
419633b388fSRobert Foss 			vfe->isr_ops.wm_done(vfe, i);
4209b5833f7STodor Tomov 			break;
4219b5833f7STodor Tomov 		}
4229b5833f7STodor Tomov }
4239b5833f7STodor Tomov 
424633b388fSRobert Foss void vfe_isr_reset_ack(struct vfe_device *vfe)
4254c98a5f5STodor Tomov {
4264c98a5f5STodor Tomov 	complete(&vfe->reset_complete);
4274c98a5f5STodor Tomov }
4284c98a5f5STodor Tomov 
4294c98a5f5STodor Tomov /*
430bbde3104STodor Tomov  * vfe_set_clock_rates - Calculate and set clock rates on VFE module
431bbde3104STodor Tomov  * @vfe: VFE device
432bbde3104STodor Tomov  *
433bbde3104STodor Tomov  * Return 0 on success or a negative error code otherwise
434bbde3104STodor Tomov  */
435bbde3104STodor Tomov static int vfe_set_clock_rates(struct vfe_device *vfe)
436bbde3104STodor Tomov {
4379c3e59deSTodor Tomov 	struct device *dev = vfe->camss->dev;
438633b388fSRobert Foss 	u64 pixel_clock[VFE_LINE_NUM_MAX];
439bbde3104STodor Tomov 	int i, j;
440bbde3104STodor Tomov 	int ret;
441bbde3104STodor Tomov 
442633b388fSRobert Foss 	for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) {
443bbde3104STodor Tomov 		ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity,
444bbde3104STodor Tomov 					    &pixel_clock[i]);
445bbde3104STodor Tomov 		if (ret)
446bbde3104STodor Tomov 			pixel_clock[i] = 0;
447bbde3104STodor Tomov 	}
448bbde3104STodor Tomov 
449bbde3104STodor Tomov 	for (i = 0; i < vfe->nclocks; i++) {
450bbde3104STodor Tomov 		struct camss_clock *clock = &vfe->clock[i];
451bbde3104STodor Tomov 
4529c3e59deSTodor Tomov 		if (!strcmp(clock->name, "vfe0") ||
4537319cdf1SRobert Foss 		    !strcmp(clock->name, "vfe1") ||
4547319cdf1SRobert Foss 		    !strcmp(clock->name, "vfe_lite")) {
455bbde3104STodor Tomov 			u64 min_rate = 0;
456bbde3104STodor Tomov 			long rate;
457bbde3104STodor Tomov 
458633b388fSRobert Foss 			for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) {
459bbde3104STodor Tomov 				u32 tmp;
460bbde3104STodor Tomov 				u8 bpp;
461bbde3104STodor Tomov 
462bbde3104STodor Tomov 				if (j == VFE_LINE_PIX) {
463bbde3104STodor Tomov 					tmp = pixel_clock[j];
464bbde3104STodor Tomov 				} else {
465cba3819dSTodor Tomov 					struct vfe_line *l = &vfe->line[j];
466cba3819dSTodor Tomov 
467cba3819dSTodor Tomov 					bpp = vfe_get_bpp(l->formats,
468cba3819dSTodor Tomov 						l->nformats,
469cba3819dSTodor Tomov 						l->fmt[MSM_VFE_PAD_SINK].code);
470bbde3104STodor Tomov 					tmp = pixel_clock[j] * bpp / 64;
471bbde3104STodor Tomov 				}
472bbde3104STodor Tomov 
473bbde3104STodor Tomov 				if (min_rate < tmp)
474bbde3104STodor Tomov 					min_rate = tmp;
475bbde3104STodor Tomov 			}
476bbde3104STodor Tomov 
477bbde3104STodor Tomov 			camss_add_clock_margin(&min_rate);
478bbde3104STodor Tomov 
479bbde3104STodor Tomov 			for (j = 0; j < clock->nfreqs; j++)
480bbde3104STodor Tomov 				if (min_rate < clock->freq[j])
481bbde3104STodor Tomov 					break;
482bbde3104STodor Tomov 
483bbde3104STodor Tomov 			if (j == clock->nfreqs) {
484bbde3104STodor Tomov 				dev_err(dev,
485bbde3104STodor Tomov 					"Pixel clock is too high for VFE");
486bbde3104STodor Tomov 				return -EINVAL;
487bbde3104STodor Tomov 			}
488bbde3104STodor Tomov 
489bbde3104STodor Tomov 			/* if sensor pixel clock is not available */
490bbde3104STodor Tomov 			/* set highest possible VFE clock rate */
491bbde3104STodor Tomov 			if (min_rate == 0)
492bbde3104STodor Tomov 				j = clock->nfreqs - 1;
493bbde3104STodor Tomov 
494bbde3104STodor Tomov 			rate = clk_round_rate(clock->clk, clock->freq[j]);
495bbde3104STodor Tomov 			if (rate < 0) {
496bbde3104STodor Tomov 				dev_err(dev, "clk round rate failed: %ld\n",
497bbde3104STodor Tomov 					rate);
498bbde3104STodor Tomov 				return -EINVAL;
499bbde3104STodor Tomov 			}
500bbde3104STodor Tomov 
501bbde3104STodor Tomov 			ret = clk_set_rate(clock->clk, rate);
502bbde3104STodor Tomov 			if (ret < 0) {
503bbde3104STodor Tomov 				dev_err(dev, "clk set rate failed: %d\n", ret);
504bbde3104STodor Tomov 				return ret;
505bbde3104STodor Tomov 			}
506bbde3104STodor Tomov 		}
507bbde3104STodor Tomov 	}
508bbde3104STodor Tomov 
509bbde3104STodor Tomov 	return 0;
510bbde3104STodor Tomov }
511bbde3104STodor Tomov 
512bbde3104STodor Tomov /*
513bbde3104STodor Tomov  * vfe_check_clock_rates - Check current clock rates on VFE module
514bbde3104STodor Tomov  * @vfe: VFE device
515bbde3104STodor Tomov  *
516bbde3104STodor Tomov  * Return 0 if current clock rates are suitable for a new pipeline
517bbde3104STodor Tomov  * or a negative error code otherwise
518bbde3104STodor Tomov  */
519bbde3104STodor Tomov static int vfe_check_clock_rates(struct vfe_device *vfe)
520bbde3104STodor Tomov {
521633b388fSRobert Foss 	u64 pixel_clock[VFE_LINE_NUM_MAX];
522bbde3104STodor Tomov 	int i, j;
523bbde3104STodor Tomov 	int ret;
524bbde3104STodor Tomov 
525633b388fSRobert Foss 	for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) {
526bbde3104STodor Tomov 		ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity,
527bbde3104STodor Tomov 					    &pixel_clock[i]);
528bbde3104STodor Tomov 		if (ret)
529bbde3104STodor Tomov 			pixel_clock[i] = 0;
530bbde3104STodor Tomov 	}
531bbde3104STodor Tomov 
532bbde3104STodor Tomov 	for (i = 0; i < vfe->nclocks; i++) {
533bbde3104STodor Tomov 		struct camss_clock *clock = &vfe->clock[i];
534bbde3104STodor Tomov 
5359c3e59deSTodor Tomov 		if (!strcmp(clock->name, "vfe0") ||
5369c3e59deSTodor Tomov 		    !strcmp(clock->name, "vfe1")) {
537bbde3104STodor Tomov 			u64 min_rate = 0;
538bbde3104STodor Tomov 			unsigned long rate;
539bbde3104STodor Tomov 
540633b388fSRobert Foss 			for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) {
541bbde3104STodor Tomov 				u32 tmp;
542bbde3104STodor Tomov 				u8 bpp;
543bbde3104STodor Tomov 
544bbde3104STodor Tomov 				if (j == VFE_LINE_PIX) {
545bbde3104STodor Tomov 					tmp = pixel_clock[j];
546bbde3104STodor Tomov 				} else {
547cba3819dSTodor Tomov 					struct vfe_line *l = &vfe->line[j];
548cba3819dSTodor Tomov 
549cba3819dSTodor Tomov 					bpp = vfe_get_bpp(l->formats,
550cba3819dSTodor Tomov 						l->nformats,
551cba3819dSTodor Tomov 						l->fmt[MSM_VFE_PAD_SINK].code);
552bbde3104STodor Tomov 					tmp = pixel_clock[j] * bpp / 64;
553bbde3104STodor Tomov 				}
554bbde3104STodor Tomov 
555bbde3104STodor Tomov 				if (min_rate < tmp)
556bbde3104STodor Tomov 					min_rate = tmp;
557bbde3104STodor Tomov 			}
558bbde3104STodor Tomov 
559bbde3104STodor Tomov 			camss_add_clock_margin(&min_rate);
560bbde3104STodor Tomov 
561bbde3104STodor Tomov 			rate = clk_get_rate(clock->clk);
562bbde3104STodor Tomov 			if (rate < min_rate)
563bbde3104STodor Tomov 				return -EBUSY;
564bbde3104STodor Tomov 		}
565bbde3104STodor Tomov 	}
566bbde3104STodor Tomov 
567bbde3104STodor Tomov 	return 0;
568bbde3104STodor Tomov }
569bbde3104STodor Tomov 
570bbde3104STodor Tomov /*
5714c98a5f5STodor Tomov  * vfe_get - Power up and reset VFE module
5724c98a5f5STodor Tomov  * @vfe: VFE Device
5734c98a5f5STodor Tomov  *
5744c98a5f5STodor Tomov  * Return 0 on success or a negative error code otherwise
5754c98a5f5STodor Tomov  */
5764c98a5f5STodor Tomov static int vfe_get(struct vfe_device *vfe)
5774c98a5f5STodor Tomov {
5784c98a5f5STodor Tomov 	int ret;
5794c98a5f5STodor Tomov 
5804c98a5f5STodor Tomov 	mutex_lock(&vfe->power_lock);
5814c98a5f5STodor Tomov 
5824c98a5f5STodor Tomov 	if (vfe->power_count == 0) {
5832f6f8af6SRobert Foss 		ret = vfe->ops->pm_domain_on(vfe);
58402afa816STodor Tomov 		if (ret < 0)
58502afa816STodor Tomov 			goto error_pm_domain;
58602afa816STodor Tomov 
58709dfb36cSMauro Carvalho Chehab 		ret = pm_runtime_resume_and_get(vfe->camss->dev);
58802afa816STodor Tomov 		if (ret < 0)
58909dfb36cSMauro Carvalho Chehab 			goto error_domain_off;
59002afa816STodor Tomov 
591bbde3104STodor Tomov 		ret = vfe_set_clock_rates(vfe);
592bbde3104STodor Tomov 		if (ret < 0)
59377909691SDinghao Liu 			goto error_pm_runtime_get;
594bbde3104STodor Tomov 
5954c98a5f5STodor Tomov 		ret = camss_enable_clocks(vfe->nclocks, vfe->clock,
5969c3e59deSTodor Tomov 					  vfe->camss->dev);
5974c98a5f5STodor Tomov 		if (ret < 0)
59877909691SDinghao Liu 			goto error_pm_runtime_get;
5994c98a5f5STodor Tomov 
6004c98a5f5STodor Tomov 		ret = vfe_reset(vfe);
6014c98a5f5STodor Tomov 		if (ret < 0)
6024c98a5f5STodor Tomov 			goto error_reset;
6034c98a5f5STodor Tomov 
6044c98a5f5STodor Tomov 		vfe_reset_output_maps(vfe);
6054c98a5f5STodor Tomov 
6064c98a5f5STodor Tomov 		vfe_init_outputs(vfe);
607745b475eSRobert Foss 
608745b475eSRobert Foss 		vfe->ops->hw_version(vfe);
609bbde3104STodor Tomov 	} else {
610bbde3104STodor Tomov 		ret = vfe_check_clock_rates(vfe);
611bbde3104STodor Tomov 		if (ret < 0)
61277909691SDinghao Liu 			goto error_pm_runtime_get;
6134c98a5f5STodor Tomov 	}
6144c98a5f5STodor Tomov 	vfe->power_count++;
6154c98a5f5STodor Tomov 
6164c98a5f5STodor Tomov 	mutex_unlock(&vfe->power_lock);
6174c98a5f5STodor Tomov 
6184c98a5f5STodor Tomov 	return 0;
6194c98a5f5STodor Tomov 
6204c98a5f5STodor Tomov error_reset:
6214c98a5f5STodor Tomov 	camss_disable_clocks(vfe->nclocks, vfe->clock);
6224c98a5f5STodor Tomov 
62302afa816STodor Tomov error_pm_runtime_get:
62477909691SDinghao Liu 	pm_runtime_put_sync(vfe->camss->dev);
62509dfb36cSMauro Carvalho Chehab error_domain_off:
6262f6f8af6SRobert Foss 	vfe->ops->pm_domain_off(vfe);
62702afa816STodor Tomov 
62802afa816STodor Tomov error_pm_domain:
6294c98a5f5STodor Tomov 	mutex_unlock(&vfe->power_lock);
6304c98a5f5STodor Tomov 
6314c98a5f5STodor Tomov 	return ret;
6324c98a5f5STodor Tomov }
6334c98a5f5STodor Tomov 
6344c98a5f5STodor Tomov /*
6354c98a5f5STodor Tomov  * vfe_put - Power down VFE module
6364c98a5f5STodor Tomov  * @vfe: VFE Device
6374c98a5f5STodor Tomov  */
6384c98a5f5STodor Tomov static void vfe_put(struct vfe_device *vfe)
6394c98a5f5STodor Tomov {
6404c98a5f5STodor Tomov 	mutex_lock(&vfe->power_lock);
6414c98a5f5STodor Tomov 
6424c98a5f5STodor Tomov 	if (vfe->power_count == 0) {
6439c3e59deSTodor Tomov 		dev_err(vfe->camss->dev, "vfe power off on power_count == 0\n");
6444c98a5f5STodor Tomov 		goto exit;
6454c98a5f5STodor Tomov 	} else if (vfe->power_count == 1) {
6464c98a5f5STodor Tomov 		if (vfe->was_streaming) {
6474c98a5f5STodor Tomov 			vfe->was_streaming = 0;
648633b388fSRobert Foss 			vfe->ops->vfe_halt(vfe);
6494c98a5f5STodor Tomov 		}
6504c98a5f5STodor Tomov 		camss_disable_clocks(vfe->nclocks, vfe->clock);
65102afa816STodor Tomov 		pm_runtime_put_sync(vfe->camss->dev);
6522f6f8af6SRobert Foss 		vfe->ops->pm_domain_off(vfe);
6534c98a5f5STodor Tomov 	}
6544c98a5f5STodor Tomov 
6554c98a5f5STodor Tomov 	vfe->power_count--;
6564c98a5f5STodor Tomov 
6574c98a5f5STodor Tomov exit:
6584c98a5f5STodor Tomov 	mutex_unlock(&vfe->power_lock);
6594c98a5f5STodor Tomov }
6604c98a5f5STodor Tomov 
6614c98a5f5STodor Tomov /*
6624c98a5f5STodor Tomov  * vfe_flush_buffers - Return all vb2 buffers
6634c98a5f5STodor Tomov  * @vid: Video device structure
6644c98a5f5STodor Tomov  * @state: vb2 buffer state of the returned buffers
6654c98a5f5STodor Tomov  *
6664c98a5f5STodor Tomov  * Return all buffers to vb2. This includes queued pending buffers (still
6674c98a5f5STodor Tomov  * unused) and any buffers given to the hardware but again still not used.
6684c98a5f5STodor Tomov  *
6694c98a5f5STodor Tomov  * Return 0 on success or a negative error code otherwise
6704c98a5f5STodor Tomov  */
671633b388fSRobert Foss int vfe_flush_buffers(struct camss_video *vid,
6724c98a5f5STodor Tomov 		      enum vb2_buffer_state state)
6734c98a5f5STodor Tomov {
674a93e5f4fSTodor Tomov 	struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
675a93e5f4fSTodor Tomov 	struct vfe_device *vfe = to_vfe(line);
6764c98a5f5STodor Tomov 	struct vfe_output *output;
6774c98a5f5STodor Tomov 	unsigned long flags;
6784c98a5f5STodor Tomov 
6794c98a5f5STodor Tomov 	output = &line->output;
6804c98a5f5STodor Tomov 
6814c98a5f5STodor Tomov 	spin_lock_irqsave(&vfe->output_lock, flags);
6824c98a5f5STodor Tomov 
6834c98a5f5STodor Tomov 	vfe_buf_flush_pending(output, state);
6844c98a5f5STodor Tomov 
6854c98a5f5STodor Tomov 	if (output->buf[0])
6864c98a5f5STodor Tomov 		vb2_buffer_done(&output->buf[0]->vb.vb2_buf, state);
6874c98a5f5STodor Tomov 
6884c98a5f5STodor Tomov 	if (output->buf[1])
6894c98a5f5STodor Tomov 		vb2_buffer_done(&output->buf[1]->vb.vb2_buf, state);
6904c98a5f5STodor Tomov 
6914c98a5f5STodor Tomov 	if (output->last_buffer) {
6924c98a5f5STodor Tomov 		vb2_buffer_done(&output->last_buffer->vb.vb2_buf, state);
6934c98a5f5STodor Tomov 		output->last_buffer = NULL;
6944c98a5f5STodor Tomov 	}
6954c98a5f5STodor Tomov 
6964c98a5f5STodor Tomov 	spin_unlock_irqrestore(&vfe->output_lock, flags);
6974c98a5f5STodor Tomov 
6984c98a5f5STodor Tomov 	return 0;
6994c98a5f5STodor Tomov }
7004c98a5f5STodor Tomov 
7014c98a5f5STodor Tomov /*
7024c98a5f5STodor Tomov  * vfe_set_power - Power on/off VFE module
7034c98a5f5STodor Tomov  * @sd: VFE V4L2 subdevice
7044c98a5f5STodor Tomov  * @on: Requested power state
7054c98a5f5STodor Tomov  *
7064c98a5f5STodor Tomov  * Return 0 on success or a negative error code otherwise
7074c98a5f5STodor Tomov  */
7084c98a5f5STodor Tomov static int vfe_set_power(struct v4l2_subdev *sd, int on)
7094c98a5f5STodor Tomov {
7104c98a5f5STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
7114c98a5f5STodor Tomov 	struct vfe_device *vfe = to_vfe(line);
7124c98a5f5STodor Tomov 	int ret;
7134c98a5f5STodor Tomov 
7144c98a5f5STodor Tomov 	if (on) {
7154c98a5f5STodor Tomov 		ret = vfe_get(vfe);
7164c98a5f5STodor Tomov 		if (ret < 0)
7174c98a5f5STodor Tomov 			return ret;
7184c98a5f5STodor Tomov 	} else {
7194c98a5f5STodor Tomov 		vfe_put(vfe);
7204c98a5f5STodor Tomov 	}
7214c98a5f5STodor Tomov 
7224c98a5f5STodor Tomov 	return 0;
7234c98a5f5STodor Tomov }
7244c98a5f5STodor Tomov 
7254c98a5f5STodor Tomov /*
7264c98a5f5STodor Tomov  * vfe_set_stream - Enable/disable streaming on VFE module
7274c98a5f5STodor Tomov  * @sd: VFE V4L2 subdevice
7284c98a5f5STodor Tomov  * @enable: Requested streaming state
7294c98a5f5STodor Tomov  *
7304c98a5f5STodor Tomov  * Main configuration of VFE module is triggered here.
7314c98a5f5STodor Tomov  *
7324c98a5f5STodor Tomov  * Return 0 on success or a negative error code otherwise
7334c98a5f5STodor Tomov  */
7344c98a5f5STodor Tomov static int vfe_set_stream(struct v4l2_subdev *sd, int enable)
7354c98a5f5STodor Tomov {
7364c98a5f5STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
7374c98a5f5STodor Tomov 	struct vfe_device *vfe = to_vfe(line);
7384c98a5f5STodor Tomov 	int ret;
7394c98a5f5STodor Tomov 
7404c98a5f5STodor Tomov 	if (enable) {
741633b388fSRobert Foss 		ret = vfe->ops->vfe_enable(line);
7424c98a5f5STodor Tomov 		if (ret < 0)
7439c3e59deSTodor Tomov 			dev_err(vfe->camss->dev,
7444c98a5f5STodor Tomov 				"Failed to enable vfe outputs\n");
7454c98a5f5STodor Tomov 	} else {
746633b388fSRobert Foss 		ret = vfe->ops->vfe_disable(line);
7474c98a5f5STodor Tomov 		if (ret < 0)
7489c3e59deSTodor Tomov 			dev_err(vfe->camss->dev,
7494c98a5f5STodor Tomov 				"Failed to disable vfe outputs\n");
7504c98a5f5STodor Tomov 	}
7514c98a5f5STodor Tomov 
7524c98a5f5STodor Tomov 	return ret;
7534c98a5f5STodor Tomov }
7544c98a5f5STodor Tomov 
7554c98a5f5STodor Tomov /*
7564c98a5f5STodor Tomov  * __vfe_get_format - Get pointer to format structure
7574c98a5f5STodor Tomov  * @line: VFE line
7584c98a5f5STodor Tomov  * @cfg: V4L2 subdev pad configuration
7594c98a5f5STodor Tomov  * @pad: pad from which format is requested
7604c98a5f5STodor Tomov  * @which: TRY or ACTIVE format
7614c98a5f5STodor Tomov  *
7624c98a5f5STodor Tomov  * Return pointer to TRY or ACTIVE format structure
7634c98a5f5STodor Tomov  */
7644c98a5f5STodor Tomov static struct v4l2_mbus_framefmt *
7654c98a5f5STodor Tomov __vfe_get_format(struct vfe_line *line,
7660d346d2aSTomi Valkeinen 		 struct v4l2_subdev_state *sd_state,
7674c98a5f5STodor Tomov 		 unsigned int pad,
7684c98a5f5STodor Tomov 		 enum v4l2_subdev_format_whence which)
7694c98a5f5STodor Tomov {
7704c98a5f5STodor Tomov 	if (which == V4L2_SUBDEV_FORMAT_TRY)
7710d346d2aSTomi Valkeinen 		return v4l2_subdev_get_try_format(&line->subdev, sd_state,
7720d346d2aSTomi Valkeinen 						  pad);
7734c98a5f5STodor Tomov 
7744c98a5f5STodor Tomov 	return &line->fmt[pad];
7754c98a5f5STodor Tomov }
7764c98a5f5STodor Tomov 
777810b6598STodor Tomov /*
778810b6598STodor Tomov  * __vfe_get_compose - Get pointer to compose selection structure
779810b6598STodor Tomov  * @line: VFE line
780810b6598STodor Tomov  * @cfg: V4L2 subdev pad configuration
781810b6598STodor Tomov  * @which: TRY or ACTIVE format
782810b6598STodor Tomov  *
783810b6598STodor Tomov  * Return pointer to TRY or ACTIVE compose rectangle structure
784810b6598STodor Tomov  */
785810b6598STodor Tomov static struct v4l2_rect *
786810b6598STodor Tomov __vfe_get_compose(struct vfe_line *line,
7870d346d2aSTomi Valkeinen 		  struct v4l2_subdev_state *sd_state,
788810b6598STodor Tomov 		  enum v4l2_subdev_format_whence which)
789810b6598STodor Tomov {
790810b6598STodor Tomov 	if (which == V4L2_SUBDEV_FORMAT_TRY)
7910d346d2aSTomi Valkeinen 		return v4l2_subdev_get_try_compose(&line->subdev, sd_state,
792810b6598STodor Tomov 						   MSM_VFE_PAD_SINK);
793810b6598STodor Tomov 
794810b6598STodor Tomov 	return &line->compose;
795810b6598STodor Tomov }
7964c98a5f5STodor Tomov 
7974c98a5f5STodor Tomov /*
798780bf2feSTodor Tomov  * __vfe_get_crop - Get pointer to crop selection structure
799780bf2feSTodor Tomov  * @line: VFE line
800780bf2feSTodor Tomov  * @cfg: V4L2 subdev pad configuration
801780bf2feSTodor Tomov  * @which: TRY or ACTIVE format
802780bf2feSTodor Tomov  *
803780bf2feSTodor Tomov  * Return pointer to TRY or ACTIVE crop rectangle structure
804780bf2feSTodor Tomov  */
805780bf2feSTodor Tomov static struct v4l2_rect *
806780bf2feSTodor Tomov __vfe_get_crop(struct vfe_line *line,
8070d346d2aSTomi Valkeinen 	       struct v4l2_subdev_state *sd_state,
808780bf2feSTodor Tomov 	       enum v4l2_subdev_format_whence which)
809780bf2feSTodor Tomov {
810780bf2feSTodor Tomov 	if (which == V4L2_SUBDEV_FORMAT_TRY)
8110d346d2aSTomi Valkeinen 		return v4l2_subdev_get_try_crop(&line->subdev, sd_state,
812780bf2feSTodor Tomov 						MSM_VFE_PAD_SRC);
813780bf2feSTodor Tomov 
814780bf2feSTodor Tomov 	return &line->crop;
815780bf2feSTodor Tomov }
816780bf2feSTodor Tomov 
817780bf2feSTodor Tomov /*
8184c98a5f5STodor Tomov  * vfe_try_format - Handle try format by pad subdev method
8194c98a5f5STodor Tomov  * @line: VFE line
8204c98a5f5STodor Tomov  * @cfg: V4L2 subdev pad configuration
8214c98a5f5STodor Tomov  * @pad: pad on which format is requested
8224c98a5f5STodor Tomov  * @fmt: pointer to v4l2 format structure
8234c98a5f5STodor Tomov  * @which: wanted subdev format
8244c98a5f5STodor Tomov  */
8254c98a5f5STodor Tomov static void vfe_try_format(struct vfe_line *line,
8260d346d2aSTomi Valkeinen 			   struct v4l2_subdev_state *sd_state,
8274c98a5f5STodor Tomov 			   unsigned int pad,
8284c98a5f5STodor Tomov 			   struct v4l2_mbus_framefmt *fmt,
8294c98a5f5STodor Tomov 			   enum v4l2_subdev_format_whence which)
8304c98a5f5STodor Tomov {
8314c98a5f5STodor Tomov 	unsigned int i;
8329b5833f7STodor Tomov 	u32 code;
8334c98a5f5STodor Tomov 
8344c98a5f5STodor Tomov 	switch (pad) {
8354c98a5f5STodor Tomov 	case MSM_VFE_PAD_SINK:
8364c98a5f5STodor Tomov 		/* Set format on sink pad */
8374c98a5f5STodor Tomov 
838cba3819dSTodor Tomov 		for (i = 0; i < line->nformats; i++)
839cba3819dSTodor Tomov 			if (fmt->code == line->formats[i].code)
8404c98a5f5STodor Tomov 				break;
8414c98a5f5STodor Tomov 
8424c98a5f5STodor Tomov 		/* If not found, use UYVY as default */
843cba3819dSTodor Tomov 		if (i >= line->nformats)
8444c98a5f5STodor Tomov 			fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
8454c98a5f5STodor Tomov 
8464c98a5f5STodor Tomov 		fmt->width = clamp_t(u32, fmt->width, 1, 8191);
8474c98a5f5STodor Tomov 		fmt->height = clamp_t(u32, fmt->height, 1, 8191);
8484c98a5f5STodor Tomov 
8494c98a5f5STodor Tomov 		fmt->field = V4L2_FIELD_NONE;
8504c98a5f5STodor Tomov 		fmt->colorspace = V4L2_COLORSPACE_SRGB;
8514c98a5f5STodor Tomov 
8524c98a5f5STodor Tomov 		break;
8534c98a5f5STodor Tomov 
8544c98a5f5STodor Tomov 	case MSM_VFE_PAD_SRC:
8554c98a5f5STodor Tomov 		/* Set and return a format same as sink pad */
8569b5833f7STodor Tomov 		code = fmt->code;
8579b5833f7STodor Tomov 
8580d346d2aSTomi Valkeinen 		*fmt = *__vfe_get_format(line, sd_state, MSM_VFE_PAD_SINK,
8590d346d2aSTomi Valkeinen 					 which);
86007eeb342STodor Tomov 
86107eeb342STodor Tomov 		fmt->code = vfe_src_pad_code(line, fmt->code, 0, code);
8624c98a5f5STodor Tomov 
863810b6598STodor Tomov 		if (line->id == VFE_LINE_PIX) {
864810b6598STodor Tomov 			struct v4l2_rect *rect;
865810b6598STodor Tomov 
8660d346d2aSTomi Valkeinen 			rect = __vfe_get_crop(line, sd_state, which);
867810b6598STodor Tomov 
868810b6598STodor Tomov 			fmt->width = rect->width;
869810b6598STodor Tomov 			fmt->height = rect->height;
870810b6598STodor Tomov 		}
8719b5833f7STodor Tomov 
8724c98a5f5STodor Tomov 		break;
8734c98a5f5STodor Tomov 	}
8744c98a5f5STodor Tomov 
8754c98a5f5STodor Tomov 	fmt->colorspace = V4L2_COLORSPACE_SRGB;
8764c98a5f5STodor Tomov }
8774c98a5f5STodor Tomov 
8784c98a5f5STodor Tomov /*
879810b6598STodor Tomov  * vfe_try_compose - Handle try compose selection by pad subdev method
880810b6598STodor Tomov  * @line: VFE line
881810b6598STodor Tomov  * @cfg: V4L2 subdev pad configuration
882810b6598STodor Tomov  * @rect: pointer to v4l2 rect structure
883810b6598STodor Tomov  * @which: wanted subdev format
884810b6598STodor Tomov  */
885810b6598STodor Tomov static void vfe_try_compose(struct vfe_line *line,
8860d346d2aSTomi Valkeinen 			    struct v4l2_subdev_state *sd_state,
887810b6598STodor Tomov 			    struct v4l2_rect *rect,
888810b6598STodor Tomov 			    enum v4l2_subdev_format_whence which)
889810b6598STodor Tomov {
890810b6598STodor Tomov 	struct v4l2_mbus_framefmt *fmt;
891810b6598STodor Tomov 
8920d346d2aSTomi Valkeinen 	fmt = __vfe_get_format(line, sd_state, MSM_VFE_PAD_SINK, which);
893810b6598STodor Tomov 
894810b6598STodor Tomov 	if (rect->width > fmt->width)
895810b6598STodor Tomov 		rect->width = fmt->width;
896810b6598STodor Tomov 
897810b6598STodor Tomov 	if (rect->height > fmt->height)
898810b6598STodor Tomov 		rect->height = fmt->height;
899810b6598STodor Tomov 
900810b6598STodor Tomov 	if (fmt->width > rect->width * SCALER_RATIO_MAX)
901810b6598STodor Tomov 		rect->width = (fmt->width + SCALER_RATIO_MAX - 1) /
902810b6598STodor Tomov 							SCALER_RATIO_MAX;
903810b6598STodor Tomov 
904810b6598STodor Tomov 	rect->width &= ~0x1;
905810b6598STodor Tomov 
906810b6598STodor Tomov 	if (fmt->height > rect->height * SCALER_RATIO_MAX)
907810b6598STodor Tomov 		rect->height = (fmt->height + SCALER_RATIO_MAX - 1) /
908810b6598STodor Tomov 							SCALER_RATIO_MAX;
909810b6598STodor Tomov 
910810b6598STodor Tomov 	if (rect->width < 16)
911810b6598STodor Tomov 		rect->width = 16;
912810b6598STodor Tomov 
913810b6598STodor Tomov 	if (rect->height < 4)
914810b6598STodor Tomov 		rect->height = 4;
915810b6598STodor Tomov }
916810b6598STodor Tomov 
917810b6598STodor Tomov /*
918780bf2feSTodor Tomov  * vfe_try_crop - Handle try crop selection by pad subdev method
919780bf2feSTodor Tomov  * @line: VFE line
920780bf2feSTodor Tomov  * @cfg: V4L2 subdev pad configuration
921780bf2feSTodor Tomov  * @rect: pointer to v4l2 rect structure
922780bf2feSTodor Tomov  * @which: wanted subdev format
923780bf2feSTodor Tomov  */
924780bf2feSTodor Tomov static void vfe_try_crop(struct vfe_line *line,
9250d346d2aSTomi Valkeinen 			 struct v4l2_subdev_state *sd_state,
926780bf2feSTodor Tomov 			 struct v4l2_rect *rect,
927780bf2feSTodor Tomov 			 enum v4l2_subdev_format_whence which)
928780bf2feSTodor Tomov {
929780bf2feSTodor Tomov 	struct v4l2_rect *compose;
930780bf2feSTodor Tomov 
9310d346d2aSTomi Valkeinen 	compose = __vfe_get_compose(line, sd_state, which);
932780bf2feSTodor Tomov 
933780bf2feSTodor Tomov 	if (rect->width > compose->width)
934780bf2feSTodor Tomov 		rect->width = compose->width;
935780bf2feSTodor Tomov 
936780bf2feSTodor Tomov 	if (rect->width + rect->left > compose->width)
937780bf2feSTodor Tomov 		rect->left = compose->width - rect->width;
938780bf2feSTodor Tomov 
939780bf2feSTodor Tomov 	if (rect->height > compose->height)
940780bf2feSTodor Tomov 		rect->height = compose->height;
941780bf2feSTodor Tomov 
942780bf2feSTodor Tomov 	if (rect->height + rect->top > compose->height)
943780bf2feSTodor Tomov 		rect->top = compose->height - rect->height;
944780bf2feSTodor Tomov 
945780bf2feSTodor Tomov 	/* wm in line based mode writes multiple of 16 horizontally */
946780bf2feSTodor Tomov 	rect->left += (rect->width & 0xf) >> 1;
947780bf2feSTodor Tomov 	rect->width &= ~0xf;
948780bf2feSTodor Tomov 
949780bf2feSTodor Tomov 	if (rect->width < 16) {
950780bf2feSTodor Tomov 		rect->left = 0;
951780bf2feSTodor Tomov 		rect->width = 16;
952780bf2feSTodor Tomov 	}
953780bf2feSTodor Tomov 
954780bf2feSTodor Tomov 	if (rect->height < 4) {
955780bf2feSTodor Tomov 		rect->top = 0;
956780bf2feSTodor Tomov 		rect->height = 4;
957780bf2feSTodor Tomov 	}
958780bf2feSTodor Tomov }
959780bf2feSTodor Tomov 
960780bf2feSTodor Tomov /*
9614c98a5f5STodor Tomov  * vfe_enum_mbus_code - Handle pixel format enumeration
9624c98a5f5STodor Tomov  * @sd: VFE V4L2 subdevice
9634c98a5f5STodor Tomov  * @cfg: V4L2 subdev pad configuration
9644c98a5f5STodor Tomov  * @code: pointer to v4l2_subdev_mbus_code_enum structure
9654c98a5f5STodor Tomov  *
9664c98a5f5STodor Tomov  * return -EINVAL or zero on success
9674c98a5f5STodor Tomov  */
9684c98a5f5STodor Tomov static int vfe_enum_mbus_code(struct v4l2_subdev *sd,
9690d346d2aSTomi Valkeinen 			      struct v4l2_subdev_state *sd_state,
9704c98a5f5STodor Tomov 			      struct v4l2_subdev_mbus_code_enum *code)
9714c98a5f5STodor Tomov {
9724c98a5f5STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
9734c98a5f5STodor Tomov 
9744c98a5f5STodor Tomov 	if (code->pad == MSM_VFE_PAD_SINK) {
975cba3819dSTodor Tomov 		if (code->index >= line->nformats)
9764c98a5f5STodor Tomov 			return -EINVAL;
9774c98a5f5STodor Tomov 
978cba3819dSTodor Tomov 		code->code = line->formats[code->index].code;
9794c98a5f5STodor Tomov 	} else {
98007eeb342STodor Tomov 		struct v4l2_mbus_framefmt *sink_fmt;
9814c98a5f5STodor Tomov 
9820d346d2aSTomi Valkeinen 		sink_fmt = __vfe_get_format(line, sd_state, MSM_VFE_PAD_SINK,
9834c98a5f5STodor Tomov 					    code->which);
9844c98a5f5STodor Tomov 
98507eeb342STodor Tomov 		code->code = vfe_src_pad_code(line, sink_fmt->code,
98607eeb342STodor Tomov 					      code->index, 0);
98707eeb342STodor Tomov 		if (!code->code)
98807eeb342STodor Tomov 			return -EINVAL;
9894c98a5f5STodor Tomov 	}
9904c98a5f5STodor Tomov 
9914c98a5f5STodor Tomov 	return 0;
9924c98a5f5STodor Tomov }
9934c98a5f5STodor Tomov 
9944c98a5f5STodor Tomov /*
9954c98a5f5STodor Tomov  * vfe_enum_frame_size - Handle frame size enumeration
9964c98a5f5STodor Tomov  * @sd: VFE V4L2 subdevice
9974c98a5f5STodor Tomov  * @cfg: V4L2 subdev pad configuration
9984c98a5f5STodor Tomov  * @fse: pointer to v4l2_subdev_frame_size_enum structure
9994c98a5f5STodor Tomov  *
10004c98a5f5STodor Tomov  * Return -EINVAL or zero on success
10014c98a5f5STodor Tomov  */
10024c98a5f5STodor Tomov static int vfe_enum_frame_size(struct v4l2_subdev *sd,
10030d346d2aSTomi Valkeinen 			       struct v4l2_subdev_state *sd_state,
10044c98a5f5STodor Tomov 			       struct v4l2_subdev_frame_size_enum *fse)
10054c98a5f5STodor Tomov {
10064c98a5f5STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
10074c98a5f5STodor Tomov 	struct v4l2_mbus_framefmt format;
10084c98a5f5STodor Tomov 
10094c98a5f5STodor Tomov 	if (fse->index != 0)
10104c98a5f5STodor Tomov 		return -EINVAL;
10114c98a5f5STodor Tomov 
10124c98a5f5STodor Tomov 	format.code = fse->code;
10134c98a5f5STodor Tomov 	format.width = 1;
10144c98a5f5STodor Tomov 	format.height = 1;
10150d346d2aSTomi Valkeinen 	vfe_try_format(line, sd_state, fse->pad, &format, fse->which);
10164c98a5f5STodor Tomov 	fse->min_width = format.width;
10174c98a5f5STodor Tomov 	fse->min_height = format.height;
10184c98a5f5STodor Tomov 
10194c98a5f5STodor Tomov 	if (format.code != fse->code)
10204c98a5f5STodor Tomov 		return -EINVAL;
10214c98a5f5STodor Tomov 
10224c98a5f5STodor Tomov 	format.code = fse->code;
10234c98a5f5STodor Tomov 	format.width = -1;
10244c98a5f5STodor Tomov 	format.height = -1;
10250d346d2aSTomi Valkeinen 	vfe_try_format(line, sd_state, fse->pad, &format, fse->which);
10264c98a5f5STodor Tomov 	fse->max_width = format.width;
10274c98a5f5STodor Tomov 	fse->max_height = format.height;
10284c98a5f5STodor Tomov 
10294c98a5f5STodor Tomov 	return 0;
10304c98a5f5STodor Tomov }
10314c98a5f5STodor Tomov 
10324c98a5f5STodor Tomov /*
10334c98a5f5STodor Tomov  * vfe_get_format - Handle get format by pads subdev method
10344c98a5f5STodor Tomov  * @sd: VFE V4L2 subdevice
10354c98a5f5STodor Tomov  * @cfg: V4L2 subdev pad configuration
10364c98a5f5STodor Tomov  * @fmt: pointer to v4l2 subdev format structure
10374c98a5f5STodor Tomov  *
10384c98a5f5STodor Tomov  * Return -EINVAL or zero on success
10394c98a5f5STodor Tomov  */
10404c98a5f5STodor Tomov static int vfe_get_format(struct v4l2_subdev *sd,
10410d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
10424c98a5f5STodor Tomov 			  struct v4l2_subdev_format *fmt)
10434c98a5f5STodor Tomov {
10444c98a5f5STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
10454c98a5f5STodor Tomov 	struct v4l2_mbus_framefmt *format;
10464c98a5f5STodor Tomov 
10470d346d2aSTomi Valkeinen 	format = __vfe_get_format(line, sd_state, fmt->pad, fmt->which);
10484c98a5f5STodor Tomov 	if (format == NULL)
10494c98a5f5STodor Tomov 		return -EINVAL;
10504c98a5f5STodor Tomov 
10514c98a5f5STodor Tomov 	fmt->format = *format;
10524c98a5f5STodor Tomov 
10534c98a5f5STodor Tomov 	return 0;
10544c98a5f5STodor Tomov }
10554c98a5f5STodor Tomov 
1056810b6598STodor Tomov static int vfe_set_selection(struct v4l2_subdev *sd,
10570d346d2aSTomi Valkeinen 			     struct v4l2_subdev_state *sd_state,
1058810b6598STodor Tomov 			     struct v4l2_subdev_selection *sel);
1059810b6598STodor Tomov 
10604c98a5f5STodor Tomov /*
10614c98a5f5STodor Tomov  * vfe_set_format - Handle set format by pads subdev method
10624c98a5f5STodor Tomov  * @sd: VFE V4L2 subdevice
10634c98a5f5STodor Tomov  * @cfg: V4L2 subdev pad configuration
10644c98a5f5STodor Tomov  * @fmt: pointer to v4l2 subdev format structure
10654c98a5f5STodor Tomov  *
10664c98a5f5STodor Tomov  * Return -EINVAL or zero on success
10674c98a5f5STodor Tomov  */
10684c98a5f5STodor Tomov static int vfe_set_format(struct v4l2_subdev *sd,
10690d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
10704c98a5f5STodor Tomov 			  struct v4l2_subdev_format *fmt)
10714c98a5f5STodor Tomov {
10724c98a5f5STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
10734c98a5f5STodor Tomov 	struct v4l2_mbus_framefmt *format;
10744c98a5f5STodor Tomov 
10750d346d2aSTomi Valkeinen 	format = __vfe_get_format(line, sd_state, fmt->pad, fmt->which);
10764c98a5f5STodor Tomov 	if (format == NULL)
10774c98a5f5STodor Tomov 		return -EINVAL;
10784c98a5f5STodor Tomov 
10790d346d2aSTomi Valkeinen 	vfe_try_format(line, sd_state, fmt->pad, &fmt->format, fmt->which);
10804c98a5f5STodor Tomov 	*format = fmt->format;
10814c98a5f5STodor Tomov 
10824c98a5f5STodor Tomov 	if (fmt->pad == MSM_VFE_PAD_SINK) {
1083810b6598STodor Tomov 		struct v4l2_subdev_selection sel = { 0 };
1084810b6598STodor Tomov 		int ret;
1085810b6598STodor Tomov 
1086810b6598STodor Tomov 		/* Propagate the format from sink to source */
10870d346d2aSTomi Valkeinen 		format = __vfe_get_format(line, sd_state, MSM_VFE_PAD_SRC,
10884c98a5f5STodor Tomov 					  fmt->which);
10894c98a5f5STodor Tomov 
10904c98a5f5STodor Tomov 		*format = fmt->format;
10910d346d2aSTomi Valkeinen 		vfe_try_format(line, sd_state, MSM_VFE_PAD_SRC, format,
10924c98a5f5STodor Tomov 			       fmt->which);
1093810b6598STodor Tomov 
1094810b6598STodor Tomov 		if (line->id != VFE_LINE_PIX)
1095810b6598STodor Tomov 			return 0;
1096810b6598STodor Tomov 
1097810b6598STodor Tomov 		/* Reset sink pad compose selection */
1098810b6598STodor Tomov 		sel.which = fmt->which;
1099810b6598STodor Tomov 		sel.pad = MSM_VFE_PAD_SINK;
1100810b6598STodor Tomov 		sel.target = V4L2_SEL_TGT_COMPOSE;
1101810b6598STodor Tomov 		sel.r.width = fmt->format.width;
1102810b6598STodor Tomov 		sel.r.height = fmt->format.height;
11030d346d2aSTomi Valkeinen 		ret = vfe_set_selection(sd, sd_state, &sel);
1104810b6598STodor Tomov 		if (ret < 0)
1105810b6598STodor Tomov 			return ret;
11064c98a5f5STodor Tomov 	}
11074c98a5f5STodor Tomov 
11084c98a5f5STodor Tomov 	return 0;
11094c98a5f5STodor Tomov }
11104c98a5f5STodor Tomov 
11114c98a5f5STodor Tomov /*
1112810b6598STodor Tomov  * vfe_get_selection - Handle get selection by pads subdev method
1113810b6598STodor Tomov  * @sd: VFE V4L2 subdevice
1114810b6598STodor Tomov  * @cfg: V4L2 subdev pad configuration
1115810b6598STodor Tomov  * @sel: pointer to v4l2 subdev selection structure
1116810b6598STodor Tomov  *
1117810b6598STodor Tomov  * Return -EINVAL or zero on success
1118810b6598STodor Tomov  */
1119810b6598STodor Tomov static int vfe_get_selection(struct v4l2_subdev *sd,
11200d346d2aSTomi Valkeinen 			     struct v4l2_subdev_state *sd_state,
1121810b6598STodor Tomov 			     struct v4l2_subdev_selection *sel)
1122810b6598STodor Tomov {
1123810b6598STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
1124810b6598STodor Tomov 	struct v4l2_subdev_format fmt = { 0 };
1125780bf2feSTodor Tomov 	struct v4l2_rect *rect;
1126810b6598STodor Tomov 	int ret;
1127810b6598STodor Tomov 
1128780bf2feSTodor Tomov 	if (line->id != VFE_LINE_PIX)
1129810b6598STodor Tomov 		return -EINVAL;
1130810b6598STodor Tomov 
1131780bf2feSTodor Tomov 	if (sel->pad == MSM_VFE_PAD_SINK)
1132810b6598STodor Tomov 		switch (sel->target) {
1133810b6598STodor Tomov 		case V4L2_SEL_TGT_COMPOSE_BOUNDS:
1134810b6598STodor Tomov 			fmt.pad = sel->pad;
1135810b6598STodor Tomov 			fmt.which = sel->which;
11360d346d2aSTomi Valkeinen 			ret = vfe_get_format(sd, sd_state, &fmt);
1137810b6598STodor Tomov 			if (ret < 0)
1138810b6598STodor Tomov 				return ret;
1139780bf2feSTodor Tomov 
1140810b6598STodor Tomov 			sel->r.left = 0;
1141810b6598STodor Tomov 			sel->r.top = 0;
1142810b6598STodor Tomov 			sel->r.width = fmt.format.width;
1143810b6598STodor Tomov 			sel->r.height = fmt.format.height;
1144810b6598STodor Tomov 			break;
1145810b6598STodor Tomov 		case V4L2_SEL_TGT_COMPOSE:
11460d346d2aSTomi Valkeinen 			rect = __vfe_get_compose(line, sd_state, sel->which);
1147780bf2feSTodor Tomov 			if (rect == NULL)
1148810b6598STodor Tomov 				return -EINVAL;
1149810b6598STodor Tomov 
1150780bf2feSTodor Tomov 			sel->r = *rect;
1151780bf2feSTodor Tomov 			break;
1152780bf2feSTodor Tomov 		default:
1153780bf2feSTodor Tomov 			return -EINVAL;
1154780bf2feSTodor Tomov 		}
1155780bf2feSTodor Tomov 	else if (sel->pad == MSM_VFE_PAD_SRC)
1156780bf2feSTodor Tomov 		switch (sel->target) {
1157780bf2feSTodor Tomov 		case V4L2_SEL_TGT_CROP_BOUNDS:
11580d346d2aSTomi Valkeinen 			rect = __vfe_get_compose(line, sd_state, sel->which);
1159780bf2feSTodor Tomov 			if (rect == NULL)
1160780bf2feSTodor Tomov 				return -EINVAL;
1161780bf2feSTodor Tomov 
1162780bf2feSTodor Tomov 			sel->r.left = rect->left;
1163780bf2feSTodor Tomov 			sel->r.top = rect->top;
1164780bf2feSTodor Tomov 			sel->r.width = rect->width;
1165780bf2feSTodor Tomov 			sel->r.height = rect->height;
1166780bf2feSTodor Tomov 			break;
1167780bf2feSTodor Tomov 		case V4L2_SEL_TGT_CROP:
11680d346d2aSTomi Valkeinen 			rect = __vfe_get_crop(line, sd_state, sel->which);
1169780bf2feSTodor Tomov 			if (rect == NULL)
1170780bf2feSTodor Tomov 				return -EINVAL;
1171780bf2feSTodor Tomov 
1172780bf2feSTodor Tomov 			sel->r = *rect;
1173810b6598STodor Tomov 			break;
1174810b6598STodor Tomov 		default:
1175810b6598STodor Tomov 			return -EINVAL;
1176810b6598STodor Tomov 		}
1177810b6598STodor Tomov 
1178810b6598STodor Tomov 	return 0;
1179810b6598STodor Tomov }
1180810b6598STodor Tomov 
1181810b6598STodor Tomov /*
1182810b6598STodor Tomov  * vfe_set_selection - Handle set selection by pads subdev method
1183810b6598STodor Tomov  * @sd: VFE V4L2 subdevice
1184810b6598STodor Tomov  * @cfg: V4L2 subdev pad configuration
1185810b6598STodor Tomov  * @sel: pointer to v4l2 subdev selection structure
1186810b6598STodor Tomov  *
1187810b6598STodor Tomov  * Return -EINVAL or zero on success
1188810b6598STodor Tomov  */
11899b62ccdbSColin Ian King static int vfe_set_selection(struct v4l2_subdev *sd,
11900d346d2aSTomi Valkeinen 			     struct v4l2_subdev_state *sd_state,
1191810b6598STodor Tomov 			     struct v4l2_subdev_selection *sel)
1192810b6598STodor Tomov {
1193810b6598STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
1194780bf2feSTodor Tomov 	struct v4l2_rect *rect;
1195810b6598STodor Tomov 	int ret;
1196810b6598STodor Tomov 
1197780bf2feSTodor Tomov 	if (line->id != VFE_LINE_PIX)
1198810b6598STodor Tomov 		return -EINVAL;
1199810b6598STodor Tomov 
1200780bf2feSTodor Tomov 	if (sel->target == V4L2_SEL_TGT_COMPOSE &&
1201780bf2feSTodor Tomov 		sel->pad == MSM_VFE_PAD_SINK) {
1202780bf2feSTodor Tomov 		struct v4l2_subdev_selection crop = { 0 };
1203810b6598STodor Tomov 
12040d346d2aSTomi Valkeinen 		rect = __vfe_get_compose(line, sd_state, sel->which);
1205780bf2feSTodor Tomov 		if (rect == NULL)
1206810b6598STodor Tomov 			return -EINVAL;
1207810b6598STodor Tomov 
12080d346d2aSTomi Valkeinen 		vfe_try_compose(line, sd_state, &sel->r, sel->which);
1209780bf2feSTodor Tomov 		*rect = sel->r;
1210780bf2feSTodor Tomov 
1211780bf2feSTodor Tomov 		/* Reset source crop selection */
1212780bf2feSTodor Tomov 		crop.which = sel->which;
1213780bf2feSTodor Tomov 		crop.pad = MSM_VFE_PAD_SRC;
1214780bf2feSTodor Tomov 		crop.target = V4L2_SEL_TGT_CROP;
1215780bf2feSTodor Tomov 		crop.r = *rect;
12160d346d2aSTomi Valkeinen 		ret = vfe_set_selection(sd, sd_state, &crop);
1217780bf2feSTodor Tomov 	} else if (sel->target == V4L2_SEL_TGT_CROP &&
1218780bf2feSTodor Tomov 		sel->pad == MSM_VFE_PAD_SRC) {
1219780bf2feSTodor Tomov 		struct v4l2_subdev_format fmt = { 0 };
1220780bf2feSTodor Tomov 
12210d346d2aSTomi Valkeinen 		rect = __vfe_get_crop(line, sd_state, sel->which);
1222780bf2feSTodor Tomov 		if (rect == NULL)
1223780bf2feSTodor Tomov 			return -EINVAL;
1224780bf2feSTodor Tomov 
12250d346d2aSTomi Valkeinen 		vfe_try_crop(line, sd_state, &sel->r, sel->which);
1226780bf2feSTodor Tomov 		*rect = sel->r;
1227810b6598STodor Tomov 
1228810b6598STodor Tomov 		/* Reset source pad format width and height */
1229810b6598STodor Tomov 		fmt.which = sel->which;
1230810b6598STodor Tomov 		fmt.pad = MSM_VFE_PAD_SRC;
12310d346d2aSTomi Valkeinen 		ret = vfe_get_format(sd, sd_state, &fmt);
1232810b6598STodor Tomov 		if (ret < 0)
1233810b6598STodor Tomov 			return ret;
1234810b6598STodor Tomov 
1235780bf2feSTodor Tomov 		fmt.format.width = rect->width;
1236780bf2feSTodor Tomov 		fmt.format.height = rect->height;
12370d346d2aSTomi Valkeinen 		ret = vfe_set_format(sd, sd_state, &fmt);
1238780bf2feSTodor Tomov 	} else {
1239780bf2feSTodor Tomov 		ret = -EINVAL;
1240780bf2feSTodor Tomov 	}
1241810b6598STodor Tomov 
1242810b6598STodor Tomov 	return ret;
1243810b6598STodor Tomov }
1244810b6598STodor Tomov 
1245810b6598STodor Tomov /*
12464c98a5f5STodor Tomov  * vfe_init_formats - Initialize formats on all pads
12474c98a5f5STodor Tomov  * @sd: VFE V4L2 subdevice
12484c98a5f5STodor Tomov  * @fh: V4L2 subdev file handle
12494c98a5f5STodor Tomov  *
12504c98a5f5STodor Tomov  * Initialize all pad formats with default values.
12514c98a5f5STodor Tomov  *
12524c98a5f5STodor Tomov  * Return 0 on success or a negative error code otherwise
12534c98a5f5STodor Tomov  */
12544c98a5f5STodor Tomov static int vfe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
12554c98a5f5STodor Tomov {
12564c98a5f5STodor Tomov 	struct v4l2_subdev_format format = {
12574c98a5f5STodor Tomov 		.pad = MSM_VFE_PAD_SINK,
12584c98a5f5STodor Tomov 		.which = fh ? V4L2_SUBDEV_FORMAT_TRY :
12594c98a5f5STodor Tomov 			      V4L2_SUBDEV_FORMAT_ACTIVE,
12604c98a5f5STodor Tomov 		.format = {
12614c98a5f5STodor Tomov 			.code = MEDIA_BUS_FMT_UYVY8_2X8,
12624c98a5f5STodor Tomov 			.width = 1920,
12634c98a5f5STodor Tomov 			.height = 1080
12644c98a5f5STodor Tomov 		}
12654c98a5f5STodor Tomov 	};
12664c98a5f5STodor Tomov 
12670d346d2aSTomi Valkeinen 	return vfe_set_format(sd, fh ? fh->state : NULL, &format);
12684c98a5f5STodor Tomov }
12694c98a5f5STodor Tomov 
12704c98a5f5STodor Tomov /*
12714c98a5f5STodor Tomov  * msm_vfe_subdev_init - Initialize VFE device structure and resources
12724c98a5f5STodor Tomov  * @vfe: VFE device
12734c98a5f5STodor Tomov  * @res: VFE module resources table
12744c98a5f5STodor Tomov  *
12754c98a5f5STodor Tomov  * Return 0 on success or a negative error code otherwise
12764c98a5f5STodor Tomov  */
12779c3e59deSTodor Tomov int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
12789c3e59deSTodor Tomov 			const struct resources *res, u8 id)
12794c98a5f5STodor Tomov {
12809c3e59deSTodor Tomov 	struct device *dev = camss->dev;
12814c98a5f5STodor Tomov 	struct platform_device *pdev = to_platform_device(dev);
12824c98a5f5STodor Tomov 	struct resource *r;
1283bbde3104STodor Tomov 	int i, j;
12844c98a5f5STodor Tomov 	int ret;
12854c98a5f5STodor Tomov 
12869e5d1581SAngeloGioacchino Del Regno 	switch (camss->version) {
12879e5d1581SAngeloGioacchino Del Regno 	case CAMSS_8x16:
1288051a01acSTodor Tomov 		vfe->ops = &vfe_ops_4_1;
12899e5d1581SAngeloGioacchino Del Regno 		break;
12909e5d1581SAngeloGioacchino Del Regno 	case CAMSS_8x96:
12914e1abf66STodor Tomov 		vfe->ops = &vfe_ops_4_7;
12929e5d1581SAngeloGioacchino Del Regno 		break;
12939e5d1581SAngeloGioacchino Del Regno 	case CAMSS_660:
12949e5d1581SAngeloGioacchino Del Regno 		vfe->ops = &vfe_ops_4_8;
12959e5d1581SAngeloGioacchino Del Regno 		break;
12967319cdf1SRobert Foss 	case CAMSS_845:
12977319cdf1SRobert Foss 		vfe->ops = &vfe_ops_170;
12987319cdf1SRobert Foss 		break;
12999e5d1581SAngeloGioacchino Del Regno 	default:
1300051a01acSTodor Tomov 		return -EINVAL;
13019e5d1581SAngeloGioacchino Del Regno 	}
1302633b388fSRobert Foss 	vfe->ops->subdev_init(dev, vfe);
1303051a01acSTodor Tomov 
13044c98a5f5STodor Tomov 	/* Memory */
13054c98a5f5STodor Tomov 
1306414e0a64Sdingsenjie 	vfe->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]);
13074c98a5f5STodor Tomov 	if (IS_ERR(vfe->base)) {
13084c98a5f5STodor Tomov 		dev_err(dev, "could not map memory\n");
13094c98a5f5STodor Tomov 		return PTR_ERR(vfe->base);
13104c98a5f5STodor Tomov 	}
13114c98a5f5STodor Tomov 
13124c98a5f5STodor Tomov 	/* Interrupt */
13134c98a5f5STodor Tomov 
13144c98a5f5STodor Tomov 	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
13154c98a5f5STodor Tomov 					 res->interrupt[0]);
13164c98a5f5STodor Tomov 	if (!r) {
13174c98a5f5STodor Tomov 		dev_err(dev, "missing IRQ\n");
13184c98a5f5STodor Tomov 		return -EINVAL;
13194c98a5f5STodor Tomov 	}
13204c98a5f5STodor Tomov 
13214c98a5f5STodor Tomov 	vfe->irq = r->start;
13224c98a5f5STodor Tomov 	snprintf(vfe->irq_name, sizeof(vfe->irq_name), "%s_%s%d",
1323*ed38a146SJonathan Marek 		 dev_name(dev), MSM_VFE_NAME, id);
1324051a01acSTodor Tomov 	ret = devm_request_irq(dev, vfe->irq, vfe->ops->isr,
13254c98a5f5STodor Tomov 			       IRQF_TRIGGER_RISING, vfe->irq_name, vfe);
13264c98a5f5STodor Tomov 	if (ret < 0) {
13274c98a5f5STodor Tomov 		dev_err(dev, "request_irq failed: %d\n", ret);
13284c98a5f5STodor Tomov 		return ret;
13294c98a5f5STodor Tomov 	}
13304c98a5f5STodor Tomov 
13314c98a5f5STodor Tomov 	/* Clocks */
13324c98a5f5STodor Tomov 
13334c98a5f5STodor Tomov 	vfe->nclocks = 0;
13344c98a5f5STodor Tomov 	while (res->clock[vfe->nclocks])
13354c98a5f5STodor Tomov 		vfe->nclocks++;
13364c98a5f5STodor Tomov 
1337a86854d0SKees Cook 	vfe->clock = devm_kcalloc(dev, vfe->nclocks, sizeof(*vfe->clock),
13384c98a5f5STodor Tomov 				  GFP_KERNEL);
13394c98a5f5STodor Tomov 	if (!vfe->clock)
13404c98a5f5STodor Tomov 		return -ENOMEM;
13414c98a5f5STodor Tomov 
13424c98a5f5STodor Tomov 	for (i = 0; i < vfe->nclocks; i++) {
1343bbde3104STodor Tomov 		struct camss_clock *clock = &vfe->clock[i];
13444c98a5f5STodor Tomov 
1345bbde3104STodor Tomov 		clock->clk = devm_clk_get(dev, res->clock[i]);
1346bbde3104STodor Tomov 		if (IS_ERR(clock->clk))
1347bbde3104STodor Tomov 			return PTR_ERR(clock->clk);
1348bbde3104STodor Tomov 
1349bbde3104STodor Tomov 		clock->name = res->clock[i];
1350bbde3104STodor Tomov 
1351bbde3104STodor Tomov 		clock->nfreqs = 0;
1352bbde3104STodor Tomov 		while (res->clock_rate[i][clock->nfreqs])
1353bbde3104STodor Tomov 			clock->nfreqs++;
1354bbde3104STodor Tomov 
1355bbde3104STodor Tomov 		if (!clock->nfreqs) {
1356bbde3104STodor Tomov 			clock->freq = NULL;
1357bbde3104STodor Tomov 			continue;
13584c98a5f5STodor Tomov 		}
1359bbde3104STodor Tomov 
1360a86854d0SKees Cook 		clock->freq = devm_kcalloc(dev,
1361a86854d0SKees Cook 					   clock->nfreqs,
1362a86854d0SKees Cook 					   sizeof(*clock->freq),
1363a86854d0SKees Cook 					   GFP_KERNEL);
1364bbde3104STodor Tomov 		if (!clock->freq)
1365bbde3104STodor Tomov 			return -ENOMEM;
1366bbde3104STodor Tomov 
1367bbde3104STodor Tomov 		for (j = 0; j < clock->nfreqs; j++)
1368bbde3104STodor Tomov 			clock->freq[j] = res->clock_rate[i][j];
13694c98a5f5STodor Tomov 	}
13704c98a5f5STodor Tomov 
13714c98a5f5STodor Tomov 	mutex_init(&vfe->power_lock);
13724c98a5f5STodor Tomov 	vfe->power_count = 0;
13734c98a5f5STodor Tomov 
13744c98a5f5STodor Tomov 	mutex_init(&vfe->stream_lock);
13754c98a5f5STodor Tomov 	vfe->stream_count = 0;
13764c98a5f5STodor Tomov 
13774c98a5f5STodor Tomov 	spin_lock_init(&vfe->output_lock);
13784c98a5f5STodor Tomov 
13799c3e59deSTodor Tomov 	vfe->camss = camss;
13809c3e59deSTodor Tomov 	vfe->id = id;
13814c98a5f5STodor Tomov 	vfe->reg_update = 0;
13824c98a5f5STodor Tomov 
1383633b388fSRobert Foss 	for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) {
1384cba3819dSTodor Tomov 		struct vfe_line *l = &vfe->line[i];
1385cba3819dSTodor Tomov 
1386cba3819dSTodor Tomov 		l->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1387cba3819dSTodor Tomov 		l->video_out.camss = camss;
1388cba3819dSTodor Tomov 		l->id = i;
1389cba3819dSTodor Tomov 		init_completion(&l->output.sof);
1390cba3819dSTodor Tomov 		init_completion(&l->output.reg_update);
1391cba3819dSTodor Tomov 
1392cba3819dSTodor Tomov 		if (camss->version == CAMSS_8x16) {
1393cba3819dSTodor Tomov 			if (i == VFE_LINE_PIX) {
1394cba3819dSTodor Tomov 				l->formats = formats_pix_8x16;
1395cba3819dSTodor Tomov 				l->nformats = ARRAY_SIZE(formats_pix_8x16);
1396cba3819dSTodor Tomov 			} else {
1397cba3819dSTodor Tomov 				l->formats = formats_rdi_8x16;
1398cba3819dSTodor Tomov 				l->nformats = ARRAY_SIZE(formats_rdi_8x16);
1399cba3819dSTodor Tomov 			}
14009e5d1581SAngeloGioacchino Del Regno 		} else if (camss->version == CAMSS_8x96 ||
14019e5d1581SAngeloGioacchino Del Regno 			   camss->version == CAMSS_660) {
1402cba3819dSTodor Tomov 			if (i == VFE_LINE_PIX) {
1403cba3819dSTodor Tomov 				l->formats = formats_pix_8x96;
1404cba3819dSTodor Tomov 				l->nformats = ARRAY_SIZE(formats_pix_8x96);
1405cba3819dSTodor Tomov 			} else {
1406cba3819dSTodor Tomov 				l->formats = formats_rdi_8x96;
1407cba3819dSTodor Tomov 				l->nformats = ARRAY_SIZE(formats_rdi_8x96);
1408cba3819dSTodor Tomov 			}
14097319cdf1SRobert Foss 		} else if (camss->version == CAMSS_845) {
14107319cdf1SRobert Foss 			l->formats = formats_rdi_845;
14117319cdf1SRobert Foss 			l->nformats = ARRAY_SIZE(formats_rdi_845);
1412cba3819dSTodor Tomov 		} else {
1413cba3819dSTodor Tomov 			return -EINVAL;
1414cba3819dSTodor Tomov 		}
14154c98a5f5STodor Tomov 	}
14164c98a5f5STodor Tomov 
14174c98a5f5STodor Tomov 	init_completion(&vfe->reset_complete);
14184c98a5f5STodor Tomov 	init_completion(&vfe->halt_complete);
14194c98a5f5STodor Tomov 
14204c98a5f5STodor Tomov 	return 0;
14214c98a5f5STodor Tomov }
14224c98a5f5STodor Tomov 
14234c98a5f5STodor Tomov /*
14244c98a5f5STodor Tomov  * msm_vfe_get_vfe_id - Get VFE HW module id
14254c98a5f5STodor Tomov  * @entity: Pointer to VFE media entity structure
14264c98a5f5STodor Tomov  * @id: Return CSID HW module id here
14274c98a5f5STodor Tomov  */
14284c98a5f5STodor Tomov void msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id)
14294c98a5f5STodor Tomov {
14304c98a5f5STodor Tomov 	struct v4l2_subdev *sd;
14314c98a5f5STodor Tomov 	struct vfe_line *line;
14324c98a5f5STodor Tomov 	struct vfe_device *vfe;
14334c98a5f5STodor Tomov 
14344c98a5f5STodor Tomov 	sd = media_entity_to_v4l2_subdev(entity);
14354c98a5f5STodor Tomov 	line = v4l2_get_subdevdata(sd);
14364c98a5f5STodor Tomov 	vfe = to_vfe(line);
14374c98a5f5STodor Tomov 
14384c98a5f5STodor Tomov 	*id = vfe->id;
14394c98a5f5STodor Tomov }
14404c98a5f5STodor Tomov 
14414c98a5f5STodor Tomov /*
14424c98a5f5STodor Tomov  * msm_vfe_get_vfe_line_id - Get VFE line id by media entity
14434c98a5f5STodor Tomov  * @entity: Pointer to VFE media entity structure
14444c98a5f5STodor Tomov  * @id: Return VFE line id here
14454c98a5f5STodor Tomov  */
14464c98a5f5STodor Tomov void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id)
14474c98a5f5STodor Tomov {
14484c98a5f5STodor Tomov 	struct v4l2_subdev *sd;
14494c98a5f5STodor Tomov 	struct vfe_line *line;
14504c98a5f5STodor Tomov 
14514c98a5f5STodor Tomov 	sd = media_entity_to_v4l2_subdev(entity);
14524c98a5f5STodor Tomov 	line = v4l2_get_subdevdata(sd);
14534c98a5f5STodor Tomov 
14544c98a5f5STodor Tomov 	*id = line->id;
14554c98a5f5STodor Tomov }
14564c98a5f5STodor Tomov 
14574c98a5f5STodor Tomov /*
14584c98a5f5STodor Tomov  * vfe_link_setup - Setup VFE connections
14594c98a5f5STodor Tomov  * @entity: Pointer to media entity structure
14604c98a5f5STodor Tomov  * @local: Pointer to local pad
14614c98a5f5STodor Tomov  * @remote: Pointer to remote pad
14624c98a5f5STodor Tomov  * @flags: Link flags
14634c98a5f5STodor Tomov  *
14644c98a5f5STodor Tomov  * Return 0 on success
14654c98a5f5STodor Tomov  */
14664c98a5f5STodor Tomov static int vfe_link_setup(struct media_entity *entity,
14674c98a5f5STodor Tomov 			  const struct media_pad *local,
14684c98a5f5STodor Tomov 			  const struct media_pad *remote, u32 flags)
14694c98a5f5STodor Tomov {
14704c98a5f5STodor Tomov 	if (flags & MEDIA_LNK_FL_ENABLED)
14714c98a5f5STodor Tomov 		if (media_entity_remote_pad(local))
14724c98a5f5STodor Tomov 			return -EBUSY;
14734c98a5f5STodor Tomov 
14744c98a5f5STodor Tomov 	return 0;
14754c98a5f5STodor Tomov }
14764c98a5f5STodor Tomov 
14774c98a5f5STodor Tomov static const struct v4l2_subdev_core_ops vfe_core_ops = {
14784c98a5f5STodor Tomov 	.s_power = vfe_set_power,
14794c98a5f5STodor Tomov };
14804c98a5f5STodor Tomov 
14814c98a5f5STodor Tomov static const struct v4l2_subdev_video_ops vfe_video_ops = {
14824c98a5f5STodor Tomov 	.s_stream = vfe_set_stream,
14834c98a5f5STodor Tomov };
14844c98a5f5STodor Tomov 
14854c98a5f5STodor Tomov static const struct v4l2_subdev_pad_ops vfe_pad_ops = {
14864c98a5f5STodor Tomov 	.enum_mbus_code = vfe_enum_mbus_code,
14874c98a5f5STodor Tomov 	.enum_frame_size = vfe_enum_frame_size,
14884c98a5f5STodor Tomov 	.get_fmt = vfe_get_format,
14894c98a5f5STodor Tomov 	.set_fmt = vfe_set_format,
1490810b6598STodor Tomov 	.get_selection = vfe_get_selection,
1491810b6598STodor Tomov 	.set_selection = vfe_set_selection,
14924c98a5f5STodor Tomov };
14934c98a5f5STodor Tomov 
14944c98a5f5STodor Tomov static const struct v4l2_subdev_ops vfe_v4l2_ops = {
14954c98a5f5STodor Tomov 	.core = &vfe_core_ops,
14964c98a5f5STodor Tomov 	.video = &vfe_video_ops,
14974c98a5f5STodor Tomov 	.pad = &vfe_pad_ops,
14984c98a5f5STodor Tomov };
14994c98a5f5STodor Tomov 
15004c98a5f5STodor Tomov static const struct v4l2_subdev_internal_ops vfe_v4l2_internal_ops = {
15014c98a5f5STodor Tomov 	.open = vfe_init_formats,
15024c98a5f5STodor Tomov };
15034c98a5f5STodor Tomov 
15044c98a5f5STodor Tomov static const struct media_entity_operations vfe_media_ops = {
15054c98a5f5STodor Tomov 	.link_setup = vfe_link_setup,
15064c98a5f5STodor Tomov 	.link_validate = v4l2_subdev_link_validate,
15074c98a5f5STodor Tomov };
15084c98a5f5STodor Tomov 
15094c98a5f5STodor Tomov /*
15104c98a5f5STodor Tomov  * msm_vfe_register_entities - Register subdev node for VFE module
15114c98a5f5STodor Tomov  * @vfe: VFE device
15124c98a5f5STodor Tomov  * @v4l2_dev: V4L2 device
15134c98a5f5STodor Tomov  *
15144c98a5f5STodor Tomov  * Initialize and register a subdev node for the VFE module. Then
15154c98a5f5STodor Tomov  * call msm_video_register() to register the video device node which
15164c98a5f5STodor Tomov  * will be connected to this subdev node. Then actually create the
15174c98a5f5STodor Tomov  * media link between them.
15184c98a5f5STodor Tomov  *
15194c98a5f5STodor Tomov  * Return 0 on success or a negative error code otherwise
15204c98a5f5STodor Tomov  */
15214c98a5f5STodor Tomov int msm_vfe_register_entities(struct vfe_device *vfe,
15224c98a5f5STodor Tomov 			      struct v4l2_device *v4l2_dev)
15234c98a5f5STodor Tomov {
15249c3e59deSTodor Tomov 	struct device *dev = vfe->camss->dev;
15254c98a5f5STodor Tomov 	struct v4l2_subdev *sd;
15264c98a5f5STodor Tomov 	struct media_pad *pads;
15274c98a5f5STodor Tomov 	struct camss_video *video_out;
15284c98a5f5STodor Tomov 	int ret;
15294c98a5f5STodor Tomov 	int i;
15304c98a5f5STodor Tomov 
1531633b388fSRobert Foss 	for (i = 0; i < vfe->line_num; i++) {
15324c98a5f5STodor Tomov 		char name[32];
15334c98a5f5STodor Tomov 
15344c98a5f5STodor Tomov 		sd = &vfe->line[i].subdev;
15354c98a5f5STodor Tomov 		pads = vfe->line[i].pads;
15364c98a5f5STodor Tomov 		video_out = &vfe->line[i].video_out;
15374c98a5f5STodor Tomov 
15384c98a5f5STodor Tomov 		v4l2_subdev_init(sd, &vfe_v4l2_ops);
15394c98a5f5STodor Tomov 		sd->internal_ops = &vfe_v4l2_internal_ops;
15404c98a5f5STodor Tomov 		sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
15419b5833f7STodor Tomov 		if (i == VFE_LINE_PIX)
15429b5833f7STodor Tomov 			snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s",
15439b5833f7STodor Tomov 				 MSM_VFE_NAME, vfe->id, "pix");
15449b5833f7STodor Tomov 		else
15454c98a5f5STodor Tomov 			snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s%d",
15464c98a5f5STodor Tomov 				 MSM_VFE_NAME, vfe->id, "rdi", i);
15479b5833f7STodor Tomov 
15484c98a5f5STodor Tomov 		v4l2_set_subdevdata(sd, &vfe->line[i]);
15494c98a5f5STodor Tomov 
15504c98a5f5STodor Tomov 		ret = vfe_init_formats(sd, NULL);
15514c98a5f5STodor Tomov 		if (ret < 0) {
15524c98a5f5STodor Tomov 			dev_err(dev, "Failed to init format: %d\n", ret);
15534c98a5f5STodor Tomov 			goto error_init;
15544c98a5f5STodor Tomov 		}
15554c98a5f5STodor Tomov 
15564c98a5f5STodor Tomov 		pads[MSM_VFE_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
15574c98a5f5STodor Tomov 		pads[MSM_VFE_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
15584c98a5f5STodor Tomov 
15594c98a5f5STodor Tomov 		sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
15604c98a5f5STodor Tomov 		sd->entity.ops = &vfe_media_ops;
15614c98a5f5STodor Tomov 		ret = media_entity_pads_init(&sd->entity, MSM_VFE_PADS_NUM,
15624c98a5f5STodor Tomov 					     pads);
15634c98a5f5STodor Tomov 		if (ret < 0) {
15644c98a5f5STodor Tomov 			dev_err(dev, "Failed to init media entity: %d\n", ret);
15654c98a5f5STodor Tomov 			goto error_init;
15664c98a5f5STodor Tomov 		}
15674c98a5f5STodor Tomov 
15684c98a5f5STodor Tomov 		ret = v4l2_device_register_subdev(v4l2_dev, sd);
15694c98a5f5STodor Tomov 		if (ret < 0) {
15704c98a5f5STodor Tomov 			dev_err(dev, "Failed to register subdev: %d\n", ret);
15714c98a5f5STodor Tomov 			goto error_reg_subdev;
15724c98a5f5STodor Tomov 		}
15734c98a5f5STodor Tomov 
1574633b388fSRobert Foss 		video_out->ops = &vfe->video_ops;
15757b4aff6fSTodor Tomov 		video_out->bpl_alignment = 8;
15767b4aff6fSTodor Tomov 		video_out->line_based = 0;
15777b4aff6fSTodor Tomov 		if (i == VFE_LINE_PIX) {
15787b4aff6fSTodor Tomov 			video_out->bpl_alignment = 16;
15797b4aff6fSTodor Tomov 			video_out->line_based = 1;
15807b4aff6fSTodor Tomov 		}
15814c98a5f5STodor Tomov 		snprintf(name, ARRAY_SIZE(name), "%s%d_%s%d",
15824c98a5f5STodor Tomov 			 MSM_VFE_NAME, vfe->id, "video", i);
15839b5833f7STodor Tomov 		ret = msm_video_register(video_out, v4l2_dev, name,
15849b5833f7STodor Tomov 					 i == VFE_LINE_PIX ? 1 : 0);
15854c98a5f5STodor Tomov 		if (ret < 0) {
15864c98a5f5STodor Tomov 			dev_err(dev, "Failed to register video node: %d\n",
15874c98a5f5STodor Tomov 				ret);
15884c98a5f5STodor Tomov 			goto error_reg_video;
15894c98a5f5STodor Tomov 		}
15904c98a5f5STodor Tomov 
15914c98a5f5STodor Tomov 		ret = media_create_pad_link(
15924c98a5f5STodor Tomov 				&sd->entity, MSM_VFE_PAD_SRC,
15934c98a5f5STodor Tomov 				&video_out->vdev.entity, 0,
15944c98a5f5STodor Tomov 				MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
15954c98a5f5STodor Tomov 		if (ret < 0) {
15964c98a5f5STodor Tomov 			dev_err(dev, "Failed to link %s->%s entities: %d\n",
15974c98a5f5STodor Tomov 				sd->entity.name, video_out->vdev.entity.name,
15984c98a5f5STodor Tomov 				ret);
15994c98a5f5STodor Tomov 			goto error_link;
16004c98a5f5STodor Tomov 		}
16014c98a5f5STodor Tomov 	}
16024c98a5f5STodor Tomov 
16034c98a5f5STodor Tomov 	return 0;
16044c98a5f5STodor Tomov 
16054c98a5f5STodor Tomov error_link:
16064c98a5f5STodor Tomov 	msm_video_unregister(video_out);
16074c98a5f5STodor Tomov 
16084c98a5f5STodor Tomov error_reg_video:
16094c98a5f5STodor Tomov 	v4l2_device_unregister_subdev(sd);
16104c98a5f5STodor Tomov 
16114c98a5f5STodor Tomov error_reg_subdev:
16124c98a5f5STodor Tomov 	media_entity_cleanup(&sd->entity);
16134c98a5f5STodor Tomov 
16144c98a5f5STodor Tomov error_init:
16154c98a5f5STodor Tomov 	for (i--; i >= 0; i--) {
16164c98a5f5STodor Tomov 		sd = &vfe->line[i].subdev;
16174c98a5f5STodor Tomov 		video_out = &vfe->line[i].video_out;
16184c98a5f5STodor Tomov 
16194c98a5f5STodor Tomov 		msm_video_unregister(video_out);
16204c98a5f5STodor Tomov 		v4l2_device_unregister_subdev(sd);
16214c98a5f5STodor Tomov 		media_entity_cleanup(&sd->entity);
16224c98a5f5STodor Tomov 	}
16234c98a5f5STodor Tomov 
16244c98a5f5STodor Tomov 	return ret;
16254c98a5f5STodor Tomov }
16264c98a5f5STodor Tomov 
16274c98a5f5STodor Tomov /*
16284c98a5f5STodor Tomov  * msm_vfe_unregister_entities - Unregister VFE module subdev node
16294c98a5f5STodor Tomov  * @vfe: VFE device
16304c98a5f5STodor Tomov  */
16314c98a5f5STodor Tomov void msm_vfe_unregister_entities(struct vfe_device *vfe)
16324c98a5f5STodor Tomov {
16334c98a5f5STodor Tomov 	int i;
16344c98a5f5STodor Tomov 
16354c98a5f5STodor Tomov 	mutex_destroy(&vfe->power_lock);
16364c98a5f5STodor Tomov 	mutex_destroy(&vfe->stream_lock);
16374c98a5f5STodor Tomov 
1638633b388fSRobert Foss 	for (i = 0; i < vfe->line_num; i++) {
16394c98a5f5STodor Tomov 		struct v4l2_subdev *sd = &vfe->line[i].subdev;
16404c98a5f5STodor Tomov 		struct camss_video *video_out = &vfe->line[i].video_out;
16414c98a5f5STodor Tomov 
16424c98a5f5STodor Tomov 		msm_video_unregister(video_out);
16434c98a5f5STodor Tomov 		v4l2_device_unregister_subdev(sd);
16444c98a5f5STodor Tomov 		media_entity_cleanup(&sd->entity);
16454c98a5f5STodor Tomov 	}
16464c98a5f5STodor Tomov }
1647