xref: /linux/drivers/media/platform/qcom/camss/camss-vfe.c (revision eb73facec2c261c92ad271e87ee37894e0bef9a8)
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[] = {
4089936bfbSMartin Dørum 	{ MEDIA_BUS_FMT_UYVY8_1X16, 8 },
4189936bfbSMartin Dørum 	{ MEDIA_BUS_FMT_VYUY8_1X16, 8 },
4289936bfbSMartin Dørum 	{ MEDIA_BUS_FMT_YUYV8_1X16, 8 },
4389936bfbSMartin Dørum 	{ MEDIA_BUS_FMT_YVYU8_1X16, 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[] = {
6089936bfbSMartin Dørum 	{ MEDIA_BUS_FMT_UYVY8_1X16, 8 },
6189936bfbSMartin Dørum 	{ MEDIA_BUS_FMT_VYUY8_1X16, 8 },
6289936bfbSMartin Dørum 	{ MEDIA_BUS_FMT_YUYV8_1X16, 8 },
6389936bfbSMartin Dørum 	{ MEDIA_BUS_FMT_YVYU8_1X16, 8 },
64cba3819dSTodor Tomov };
65cba3819dSTodor Tomov 
66cba3819dSTodor Tomov static const struct vfe_format formats_rdi_8x96[] = {
6789936bfbSMartin Dørum 	{ MEDIA_BUS_FMT_UYVY8_1X16, 8 },
6889936bfbSMartin Dørum 	{ MEDIA_BUS_FMT_VYUY8_1X16, 8 },
6989936bfbSMartin Dørum 	{ MEDIA_BUS_FMT_YUYV8_1X16, 8 },
7089936bfbSMartin Dørum 	{ MEDIA_BUS_FMT_YVYU8_1X16, 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[] = {
9389936bfbSMartin Dørum 	{ MEDIA_BUS_FMT_UYVY8_1X16, 8 },
9489936bfbSMartin Dørum 	{ MEDIA_BUS_FMT_VYUY8_1X16, 8 },
9589936bfbSMartin Dørum 	{ MEDIA_BUS_FMT_YUYV8_1X16, 8 },
9689936bfbSMartin Dørum 	{ MEDIA_BUS_FMT_YVYU8_1X16, 8 },
974c98a5f5STodor Tomov };
984c98a5f5STodor Tomov 
997319cdf1SRobert Foss static const struct vfe_format formats_rdi_845[] = {
10089936bfbSMartin Dørum 	{ MEDIA_BUS_FMT_UYVY8_1X16, 8 },
10189936bfbSMartin Dørum 	{ MEDIA_BUS_FMT_VYUY8_1X16, 8 },
10289936bfbSMartin Dørum 	{ MEDIA_BUS_FMT_YUYV8_1X16, 8 },
10389936bfbSMartin Dørum 	{ MEDIA_BUS_FMT_YVYU8_1X16, 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 },
121e53d6608SJonathan Marek 	{ MEDIA_BUS_FMT_Y8_1X8, 8 },
1227319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_Y10_1X10, 10 },
1237319cdf1SRobert Foss 	{ MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 },
1247319cdf1SRobert Foss };
1257319cdf1SRobert Foss 
126bbde3104STodor Tomov /*
127bbde3104STodor Tomov  * vfe_get_bpp - map media bus format to bits per pixel
128cba3819dSTodor Tomov  * @formats: supported media bus formats array
129cba3819dSTodor Tomov  * @nformats: size of @formats array
130bbde3104STodor Tomov  * @code: media bus format code
131bbde3104STodor Tomov  *
132bbde3104STodor Tomov  * Return number of bits per pixel
133bbde3104STodor Tomov  */
134cba3819dSTodor Tomov static u8 vfe_get_bpp(const struct vfe_format *formats,
135cba3819dSTodor Tomov 		      unsigned int nformats, u32 code)
136bbde3104STodor Tomov {
137bbde3104STodor Tomov 	unsigned int i;
138bbde3104STodor Tomov 
139cba3819dSTodor Tomov 	for (i = 0; i < nformats; i++)
140cba3819dSTodor Tomov 		if (code == formats[i].code)
141cba3819dSTodor Tomov 			return formats[i].bpp;
142bbde3104STodor Tomov 
143bbde3104STodor Tomov 	WARN(1, "Unknown format\n");
144bbde3104STodor Tomov 
145cba3819dSTodor Tomov 	return formats[0].bpp;
146bbde3104STodor Tomov }
147bbde3104STodor Tomov 
14807eeb342STodor Tomov static u32 vfe_find_code(u32 *code, unsigned int n_code,
14907eeb342STodor Tomov 			 unsigned int index, u32 req_code)
15007eeb342STodor Tomov {
15107eeb342STodor Tomov 	int i;
15207eeb342STodor Tomov 
15307eeb342STodor Tomov 	if (!req_code && (index >= n_code))
15407eeb342STodor Tomov 		return 0;
15507eeb342STodor Tomov 
15607eeb342STodor Tomov 	for (i = 0; i < n_code; i++)
15707eeb342STodor Tomov 		if (req_code) {
15807eeb342STodor Tomov 			if (req_code == code[i])
15907eeb342STodor Tomov 				return req_code;
16007eeb342STodor Tomov 		} else {
16107eeb342STodor Tomov 			if (i == index)
16207eeb342STodor Tomov 				return code[i];
16307eeb342STodor Tomov 		}
16407eeb342STodor Tomov 
16507eeb342STodor Tomov 	return code[0];
16607eeb342STodor Tomov }
16707eeb342STodor Tomov 
16807eeb342STodor Tomov static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
16907eeb342STodor Tomov 			    unsigned int index, u32 src_req_code)
17007eeb342STodor Tomov {
17107eeb342STodor Tomov 	struct vfe_device *vfe = to_vfe(line);
17207eeb342STodor Tomov 
1732de3a654SBryan O'Donoghue 	switch (vfe->camss->res->version) {
1742de3a654SBryan O'Donoghue 	case CAMSS_8x16:
17507eeb342STodor Tomov 		switch (sink_code) {
17689936bfbSMartin Dørum 		case MEDIA_BUS_FMT_YUYV8_1X16:
17707eeb342STodor Tomov 		{
17807eeb342STodor Tomov 			u32 src_code[] = {
17989936bfbSMartin Dørum 				MEDIA_BUS_FMT_YUYV8_1X16,
18007eeb342STodor Tomov 				MEDIA_BUS_FMT_YUYV8_1_5X8,
18107eeb342STodor Tomov 			};
18207eeb342STodor Tomov 
18307eeb342STodor Tomov 			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
18407eeb342STodor Tomov 					     index, src_req_code);
18507eeb342STodor Tomov 		}
18689936bfbSMartin Dørum 		case MEDIA_BUS_FMT_YVYU8_1X16:
18707eeb342STodor Tomov 		{
18807eeb342STodor Tomov 			u32 src_code[] = {
18989936bfbSMartin Dørum 				MEDIA_BUS_FMT_YVYU8_1X16,
19007eeb342STodor Tomov 				MEDIA_BUS_FMT_YVYU8_1_5X8,
19107eeb342STodor Tomov 			};
19207eeb342STodor Tomov 
19307eeb342STodor Tomov 			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
19407eeb342STodor Tomov 					     index, src_req_code);
19507eeb342STodor Tomov 		}
19689936bfbSMartin Dørum 		case MEDIA_BUS_FMT_UYVY8_1X16:
19707eeb342STodor Tomov 		{
19807eeb342STodor Tomov 			u32 src_code[] = {
19989936bfbSMartin Dørum 				MEDIA_BUS_FMT_UYVY8_1X16,
20007eeb342STodor Tomov 				MEDIA_BUS_FMT_UYVY8_1_5X8,
20107eeb342STodor Tomov 			};
20207eeb342STodor Tomov 
20307eeb342STodor Tomov 			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
20407eeb342STodor Tomov 					     index, src_req_code);
20507eeb342STodor Tomov 		}
20689936bfbSMartin Dørum 		case MEDIA_BUS_FMT_VYUY8_1X16:
20707eeb342STodor Tomov 		{
20807eeb342STodor Tomov 			u32 src_code[] = {
20989936bfbSMartin Dørum 				MEDIA_BUS_FMT_VYUY8_1X16,
21007eeb342STodor Tomov 				MEDIA_BUS_FMT_VYUY8_1_5X8,
21107eeb342STodor Tomov 			};
21207eeb342STodor Tomov 
21307eeb342STodor Tomov 			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
21407eeb342STodor Tomov 					     index, src_req_code);
21507eeb342STodor Tomov 		}
21607eeb342STodor Tomov 		default:
21707eeb342STodor Tomov 			if (index > 0)
21807eeb342STodor Tomov 				return 0;
21907eeb342STodor Tomov 
22007eeb342STodor Tomov 			return sink_code;
22107eeb342STodor Tomov 		}
2222de3a654SBryan O'Donoghue 		break;
2232de3a654SBryan O'Donoghue 	case CAMSS_8x96:
2242de3a654SBryan O'Donoghue 	case CAMSS_660:
2252de3a654SBryan O'Donoghue 	case CAMSS_845:
2262de3a654SBryan O'Donoghue 	case CAMSS_8250:
22707eeb342STodor Tomov 		switch (sink_code) {
22889936bfbSMartin Dørum 		case MEDIA_BUS_FMT_YUYV8_1X16:
22907eeb342STodor Tomov 		{
23007eeb342STodor Tomov 			u32 src_code[] = {
23189936bfbSMartin Dørum 				MEDIA_BUS_FMT_YUYV8_1X16,
23289936bfbSMartin Dørum 				MEDIA_BUS_FMT_YVYU8_1X16,
23389936bfbSMartin Dørum 				MEDIA_BUS_FMT_UYVY8_1X16,
23489936bfbSMartin Dørum 				MEDIA_BUS_FMT_VYUY8_1X16,
23507eeb342STodor Tomov 				MEDIA_BUS_FMT_YUYV8_1_5X8,
23607eeb342STodor Tomov 			};
23707eeb342STodor Tomov 
23807eeb342STodor Tomov 			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
23907eeb342STodor Tomov 					     index, src_req_code);
24007eeb342STodor Tomov 		}
24189936bfbSMartin Dørum 		case MEDIA_BUS_FMT_YVYU8_1X16:
24207eeb342STodor Tomov 		{
24307eeb342STodor Tomov 			u32 src_code[] = {
24489936bfbSMartin Dørum 				MEDIA_BUS_FMT_YVYU8_1X16,
24589936bfbSMartin Dørum 				MEDIA_BUS_FMT_YUYV8_1X16,
24689936bfbSMartin Dørum 				MEDIA_BUS_FMT_UYVY8_1X16,
24789936bfbSMartin Dørum 				MEDIA_BUS_FMT_VYUY8_1X16,
24807eeb342STodor Tomov 				MEDIA_BUS_FMT_YVYU8_1_5X8,
24907eeb342STodor Tomov 			};
25007eeb342STodor Tomov 
25107eeb342STodor Tomov 			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
25207eeb342STodor Tomov 					     index, src_req_code);
25307eeb342STodor Tomov 		}
25489936bfbSMartin Dørum 		case MEDIA_BUS_FMT_UYVY8_1X16:
25507eeb342STodor Tomov 		{
25607eeb342STodor Tomov 			u32 src_code[] = {
25789936bfbSMartin Dørum 				MEDIA_BUS_FMT_UYVY8_1X16,
25889936bfbSMartin Dørum 				MEDIA_BUS_FMT_YUYV8_1X16,
25989936bfbSMartin Dørum 				MEDIA_BUS_FMT_YVYU8_1X16,
26089936bfbSMartin Dørum 				MEDIA_BUS_FMT_VYUY8_1X16,
26107eeb342STodor Tomov 				MEDIA_BUS_FMT_UYVY8_1_5X8,
26207eeb342STodor Tomov 			};
26307eeb342STodor Tomov 
26407eeb342STodor Tomov 			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
26507eeb342STodor Tomov 					     index, src_req_code);
26607eeb342STodor Tomov 		}
26789936bfbSMartin Dørum 		case MEDIA_BUS_FMT_VYUY8_1X16:
26807eeb342STodor Tomov 		{
26907eeb342STodor Tomov 			u32 src_code[] = {
27089936bfbSMartin Dørum 				MEDIA_BUS_FMT_VYUY8_1X16,
27189936bfbSMartin Dørum 				MEDIA_BUS_FMT_YUYV8_1X16,
27289936bfbSMartin Dørum 				MEDIA_BUS_FMT_YVYU8_1X16,
27389936bfbSMartin Dørum 				MEDIA_BUS_FMT_UYVY8_1X16,
27407eeb342STodor Tomov 				MEDIA_BUS_FMT_VYUY8_1_5X8,
27507eeb342STodor Tomov 			};
27607eeb342STodor Tomov 
27707eeb342STodor Tomov 			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
27807eeb342STodor Tomov 					     index, src_req_code);
27907eeb342STodor Tomov 		}
28007eeb342STodor Tomov 		default:
28107eeb342STodor Tomov 			if (index > 0)
28207eeb342STodor Tomov 				return 0;
28307eeb342STodor Tomov 
28407eeb342STodor Tomov 			return sink_code;
28507eeb342STodor Tomov 		}
2862de3a654SBryan O'Donoghue 		break;
2872de3a654SBryan O'Donoghue 	}
28807eeb342STodor Tomov 	return 0;
28907eeb342STodor Tomov }
29007eeb342STodor Tomov 
2917319cdf1SRobert Foss int vfe_reset(struct vfe_device *vfe)
2924c98a5f5STodor Tomov {
2934c98a5f5STodor Tomov 	unsigned long time;
2944c98a5f5STodor Tomov 
2954c98a5f5STodor Tomov 	reinit_completion(&vfe->reset_complete);
2964c98a5f5STodor Tomov 
297051a01acSTodor Tomov 	vfe->ops->global_reset(vfe);
2984c98a5f5STodor Tomov 
2994c98a5f5STodor Tomov 	time = wait_for_completion_timeout(&vfe->reset_complete,
3004c98a5f5STodor Tomov 		msecs_to_jiffies(VFE_RESET_TIMEOUT_MS));
3014c98a5f5STodor Tomov 	if (!time) {
3029c3e59deSTodor Tomov 		dev_err(vfe->camss->dev, "VFE reset timeout\n");
3034c98a5f5STodor Tomov 		return -EIO;
3044c98a5f5STodor Tomov 	}
3054c98a5f5STodor Tomov 
3064c98a5f5STodor Tomov 	return 0;
3074c98a5f5STodor Tomov }
3084c98a5f5STodor Tomov 
3094c98a5f5STodor Tomov static void vfe_init_outputs(struct vfe_device *vfe)
3104c98a5f5STodor Tomov {
3114c98a5f5STodor Tomov 	int i;
3124c98a5f5STodor Tomov 
313633b388fSRobert Foss 	for (i = 0; i < vfe->line_num; i++) {
3144c98a5f5STodor Tomov 		struct vfe_output *output = &vfe->line[i].output;
3154c98a5f5STodor Tomov 
3164c98a5f5STodor Tomov 		output->state = VFE_OUTPUT_OFF;
3174c98a5f5STodor Tomov 		output->buf[0] = NULL;
3184c98a5f5STodor Tomov 		output->buf[1] = NULL;
3194c98a5f5STodor Tomov 		INIT_LIST_HEAD(&output->pending_bufs);
3204c98a5f5STodor Tomov 	}
3214c98a5f5STodor Tomov }
3224c98a5f5STodor Tomov 
3234c98a5f5STodor Tomov static void vfe_reset_output_maps(struct vfe_device *vfe)
3244c98a5f5STodor Tomov {
3254c98a5f5STodor Tomov 	int i;
3264c98a5f5STodor Tomov 
3274c98a5f5STodor Tomov 	for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++)
3284c98a5f5STodor Tomov 		vfe->wm_output_map[i] = VFE_LINE_NONE;
3294c98a5f5STodor Tomov }
3304c98a5f5STodor Tomov 
331633b388fSRobert Foss int vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id)
3324c98a5f5STodor Tomov {
3334c98a5f5STodor Tomov 	int ret = -EBUSY;
3344c98a5f5STodor Tomov 	int i;
3354c98a5f5STodor Tomov 
3364c98a5f5STodor Tomov 	for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++) {
3374c98a5f5STodor Tomov 		if (vfe->wm_output_map[i] == VFE_LINE_NONE) {
3384c98a5f5STodor Tomov 			vfe->wm_output_map[i] = line_id;
3394c98a5f5STodor Tomov 			ret = i;
3404c98a5f5STodor Tomov 			break;
3414c98a5f5STodor Tomov 		}
3424c98a5f5STodor Tomov 	}
3434c98a5f5STodor Tomov 
3444c98a5f5STodor Tomov 	return ret;
3454c98a5f5STodor Tomov }
3464c98a5f5STodor Tomov 
347633b388fSRobert Foss int vfe_release_wm(struct vfe_device *vfe, u8 wm)
3484c98a5f5STodor Tomov {
3496e893ca2SMauro Carvalho Chehab 	if (wm >= ARRAY_SIZE(vfe->wm_output_map))
3504c98a5f5STodor Tomov 		return -EINVAL;
3514c98a5f5STodor Tomov 
3524c98a5f5STodor Tomov 	vfe->wm_output_map[wm] = VFE_LINE_NONE;
3534c98a5f5STodor Tomov 
3544c98a5f5STodor Tomov 	return 0;
3554c98a5f5STodor Tomov }
3564c98a5f5STodor Tomov 
357633b388fSRobert Foss struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output)
3584c98a5f5STodor Tomov {
3594c98a5f5STodor Tomov 	struct camss_buffer *buffer = NULL;
3604c98a5f5STodor Tomov 
3614c98a5f5STodor Tomov 	if (!list_empty(&output->pending_bufs)) {
3624c98a5f5STodor Tomov 		buffer = list_first_entry(&output->pending_bufs,
3634c98a5f5STodor Tomov 					  struct camss_buffer,
3644c98a5f5STodor Tomov 					  queue);
3654c98a5f5STodor Tomov 		list_del(&buffer->queue);
3664c98a5f5STodor Tomov 	}
3674c98a5f5STodor Tomov 
3684c98a5f5STodor Tomov 	return buffer;
3694c98a5f5STodor Tomov }
3704c98a5f5STodor Tomov 
371633b388fSRobert Foss void vfe_buf_add_pending(struct vfe_output *output,
3724c98a5f5STodor Tomov 			 struct camss_buffer *buffer)
3734c98a5f5STodor Tomov {
3744c98a5f5STodor Tomov 	INIT_LIST_HEAD(&buffer->queue);
3754c98a5f5STodor Tomov 	list_add_tail(&buffer->queue, &output->pending_bufs);
3764c98a5f5STodor Tomov }
3774c98a5f5STodor Tomov 
3784c98a5f5STodor Tomov /*
3794c98a5f5STodor Tomov  * vfe_buf_flush_pending - Flush all pending buffers.
3804c98a5f5STodor Tomov  * @output: VFE output
3814c98a5f5STodor Tomov  * @state: vb2 buffer state
3824c98a5f5STodor Tomov  */
3834c98a5f5STodor Tomov static void vfe_buf_flush_pending(struct vfe_output *output,
3844c98a5f5STodor Tomov 				  enum vb2_buffer_state state)
3854c98a5f5STodor Tomov {
3864c98a5f5STodor Tomov 	struct camss_buffer *buf;
3874c98a5f5STodor Tomov 	struct camss_buffer *t;
3884c98a5f5STodor Tomov 
3894c98a5f5STodor Tomov 	list_for_each_entry_safe(buf, t, &output->pending_bufs, queue) {
3904c98a5f5STodor Tomov 		vb2_buffer_done(&buf->vb.vb2_buf, state);
3914c98a5f5STodor Tomov 		list_del(&buf->queue);
3924c98a5f5STodor Tomov 	}
3934c98a5f5STodor Tomov }
3944c98a5f5STodor Tomov 
395633b388fSRobert Foss int vfe_put_output(struct vfe_line *line)
3964c98a5f5STodor Tomov {
3974c98a5f5STodor Tomov 	struct vfe_device *vfe = to_vfe(line);
3984c98a5f5STodor Tomov 	struct vfe_output *output = &line->output;
3994c98a5f5STodor Tomov 	unsigned long flags;
4009b5833f7STodor Tomov 	unsigned int i;
4014c98a5f5STodor Tomov 
4024c98a5f5STodor Tomov 	spin_lock_irqsave(&vfe->output_lock, flags);
4034c98a5f5STodor Tomov 
4049b5833f7STodor Tomov 	for (i = 0; i < output->wm_num; i++)
4059b5833f7STodor Tomov 		vfe_release_wm(vfe, output->wm_idx[i]);
4064c98a5f5STodor Tomov 
4074c98a5f5STodor Tomov 	output->state = VFE_OUTPUT_OFF;
4084c98a5f5STodor Tomov 
4094c98a5f5STodor Tomov 	spin_unlock_irqrestore(&vfe->output_lock, flags);
4109b5833f7STodor Tomov 	return 0;
4114c98a5f5STodor Tomov }
4124c98a5f5STodor Tomov 
41390cc4555SBryan O'Donoghue static int vfe_disable_output(struct vfe_line *line)
41490cc4555SBryan O'Donoghue {
41590cc4555SBryan O'Donoghue 	struct vfe_device *vfe = to_vfe(line);
41690cc4555SBryan O'Donoghue 	struct vfe_output *output = &line->output;
41790cc4555SBryan O'Donoghue 	unsigned long flags;
41890cc4555SBryan O'Donoghue 	unsigned int i;
41990cc4555SBryan O'Donoghue 
42090cc4555SBryan O'Donoghue 	spin_lock_irqsave(&vfe->output_lock, flags);
42190cc4555SBryan O'Donoghue 	for (i = 0; i < output->wm_num; i++)
42290cc4555SBryan O'Donoghue 		vfe->ops->vfe_wm_stop(vfe, output->wm_idx[i]);
42390cc4555SBryan O'Donoghue 	output->gen2.active_num = 0;
42490cc4555SBryan O'Donoghue 	spin_unlock_irqrestore(&vfe->output_lock, flags);
42590cc4555SBryan O'Donoghue 
42690cc4555SBryan O'Donoghue 	return vfe_reset(vfe);
42790cc4555SBryan O'Donoghue }
42890cc4555SBryan O'Donoghue 
42990cc4555SBryan O'Donoghue /*
43090cc4555SBryan O'Donoghue  * vfe_disable - Disable streaming on VFE line
43190cc4555SBryan O'Donoghue  * @line: VFE line
43290cc4555SBryan O'Donoghue  *
43390cc4555SBryan O'Donoghue  * Return 0 on success or a negative error code otherwise
43490cc4555SBryan O'Donoghue  */
43590cc4555SBryan O'Donoghue int vfe_disable(struct vfe_line *line)
43690cc4555SBryan O'Donoghue {
43790cc4555SBryan O'Donoghue 	struct vfe_device *vfe = to_vfe(line);
4385c07f30bSBryan O'Donoghue 	int ret;
43990cc4555SBryan O'Donoghue 
4405c07f30bSBryan O'Donoghue 	ret = vfe_disable_output(line);
4415c07f30bSBryan O'Donoghue 	if (ret)
4425c07f30bSBryan O'Donoghue 		goto error;
44390cc4555SBryan O'Donoghue 
44490cc4555SBryan O'Donoghue 	vfe_put_output(line);
44590cc4555SBryan O'Donoghue 
44690cc4555SBryan O'Donoghue 	mutex_lock(&vfe->stream_lock);
44790cc4555SBryan O'Donoghue 
44890cc4555SBryan O'Donoghue 	vfe->stream_count--;
44990cc4555SBryan O'Donoghue 
45090cc4555SBryan O'Donoghue 	mutex_unlock(&vfe->stream_lock);
45190cc4555SBryan O'Donoghue 
4525c07f30bSBryan O'Donoghue error:
4535c07f30bSBryan O'Donoghue 	return ret;
45490cc4555SBryan O'Donoghue }
45590cc4555SBryan O'Donoghue 
456d8bdc3e4SRobert Foss /**
457d8bdc3e4SRobert Foss  * vfe_isr_comp_done() - Process composite image done interrupt
4589b5833f7STodor Tomov  * @vfe: VFE Device
4599b5833f7STodor Tomov  * @comp: Composite image id
4609b5833f7STodor Tomov  */
461633b388fSRobert Foss void vfe_isr_comp_done(struct vfe_device *vfe, u8 comp)
4629b5833f7STodor Tomov {
4639b5833f7STodor Tomov 	unsigned int i;
4649b5833f7STodor Tomov 
4659b5833f7STodor Tomov 	for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++)
4669b5833f7STodor Tomov 		if (vfe->wm_output_map[i] == VFE_LINE_PIX) {
467633b388fSRobert Foss 			vfe->isr_ops.wm_done(vfe, i);
4689b5833f7STodor Tomov 			break;
4699b5833f7STodor Tomov 		}
4709b5833f7STodor Tomov }
4719b5833f7STodor Tomov 
472633b388fSRobert Foss void vfe_isr_reset_ack(struct vfe_device *vfe)
4734c98a5f5STodor Tomov {
4744c98a5f5STodor Tomov 	complete(&vfe->reset_complete);
4754c98a5f5STodor Tomov }
4764c98a5f5STodor Tomov 
477eb73faceSBryan O'Donoghue /*
478eb73faceSBryan O'Donoghue  * vfe_pm_domain_off - Disable power domains specific to this VFE.
479eb73faceSBryan O'Donoghue  * @vfe: VFE Device
480eb73faceSBryan O'Donoghue  */
481eb73faceSBryan O'Donoghue void vfe_pm_domain_off(struct vfe_device *vfe)
482eb73faceSBryan O'Donoghue {
483eb73faceSBryan O'Donoghue 	if (!vfe->genpd)
484eb73faceSBryan O'Donoghue 		return;
485eb73faceSBryan O'Donoghue 
486eb73faceSBryan O'Donoghue 	device_link_del(vfe->genpd_link);
487eb73faceSBryan O'Donoghue 	vfe->genpd_link = NULL;
488eb73faceSBryan O'Donoghue }
489eb73faceSBryan O'Donoghue 
490eb73faceSBryan O'Donoghue /*
491eb73faceSBryan O'Donoghue  * vfe_pm_domain_on - Enable power domains specific to this VFE.
492eb73faceSBryan O'Donoghue  * @vfe: VFE Device
493eb73faceSBryan O'Donoghue  */
494eb73faceSBryan O'Donoghue int vfe_pm_domain_on(struct vfe_device *vfe)
495eb73faceSBryan O'Donoghue {
496eb73faceSBryan O'Donoghue 	struct camss *camss = vfe->camss;
497eb73faceSBryan O'Donoghue 
498eb73faceSBryan O'Donoghue 	if (!vfe->genpd)
499eb73faceSBryan O'Donoghue 		return 0;
500eb73faceSBryan O'Donoghue 
501eb73faceSBryan O'Donoghue 	vfe->genpd_link = device_link_add(camss->dev, vfe->genpd,
502eb73faceSBryan O'Donoghue 					  DL_FLAG_STATELESS |
503eb73faceSBryan O'Donoghue 					  DL_FLAG_PM_RUNTIME |
504eb73faceSBryan O'Donoghue 					  DL_FLAG_RPM_ACTIVE);
505eb73faceSBryan O'Donoghue 	if (!vfe->genpd_link)
506eb73faceSBryan O'Donoghue 		return -EINVAL;
507eb73faceSBryan O'Donoghue 
508eb73faceSBryan O'Donoghue 	return 0;
509eb73faceSBryan O'Donoghue }
510eb73faceSBryan O'Donoghue 
511bcd2adfeSBryan O'Donoghue static int vfe_match_clock_names(struct vfe_device *vfe,
512bcd2adfeSBryan O'Donoghue 				 struct camss_clock *clock)
513bcd2adfeSBryan O'Donoghue {
514bcd2adfeSBryan O'Donoghue 	char vfe_name[7]; /* vfeXXX\0 */
515bcd2adfeSBryan O'Donoghue 	char vfe_lite_name[12]; /* vfe_liteXXX\0 */
516bcd2adfeSBryan O'Donoghue 
517bcd2adfeSBryan O'Donoghue 	snprintf(vfe_name, sizeof(vfe_name), "vfe%d", vfe->id);
518bcd2adfeSBryan O'Donoghue 	snprintf(vfe_lite_name, sizeof(vfe_lite_name), "vfe_lite%d", vfe->id);
519bcd2adfeSBryan O'Donoghue 
520bcd2adfeSBryan O'Donoghue 	return (!strcmp(clock->name, vfe_name) ||
521bcd2adfeSBryan O'Donoghue 		!strcmp(clock->name, vfe_lite_name) ||
522bcd2adfeSBryan O'Donoghue 		!strcmp(clock->name, "vfe_lite"));
523bcd2adfeSBryan O'Donoghue }
524bcd2adfeSBryan O'Donoghue 
5254c98a5f5STodor Tomov /*
526bbde3104STodor Tomov  * vfe_set_clock_rates - Calculate and set clock rates on VFE module
527bbde3104STodor Tomov  * @vfe: VFE device
528bbde3104STodor Tomov  *
529bbde3104STodor Tomov  * Return 0 on success or a negative error code otherwise
530bbde3104STodor Tomov  */
531bbde3104STodor Tomov static int vfe_set_clock_rates(struct vfe_device *vfe)
532bbde3104STodor Tomov {
5339c3e59deSTodor Tomov 	struct device *dev = vfe->camss->dev;
534633b388fSRobert Foss 	u64 pixel_clock[VFE_LINE_NUM_MAX];
535bbde3104STodor Tomov 	int i, j;
536bbde3104STodor Tomov 	int ret;
537bbde3104STodor Tomov 
538633b388fSRobert Foss 	for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) {
539bbde3104STodor Tomov 		ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity,
540bbde3104STodor Tomov 					    &pixel_clock[i]);
541bbde3104STodor Tomov 		if (ret)
542bbde3104STodor Tomov 			pixel_clock[i] = 0;
543bbde3104STodor Tomov 	}
544bbde3104STodor Tomov 
545bbde3104STodor Tomov 	for (i = 0; i < vfe->nclocks; i++) {
546bbde3104STodor Tomov 		struct camss_clock *clock = &vfe->clock[i];
547bbde3104STodor Tomov 
548bcd2adfeSBryan O'Donoghue 		if (vfe_match_clock_names(vfe, clock)) {
549bbde3104STodor Tomov 			u64 min_rate = 0;
550bbde3104STodor Tomov 			long rate;
551bbde3104STodor Tomov 
552633b388fSRobert Foss 			for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) {
553bbde3104STodor Tomov 				u32 tmp;
554bbde3104STodor Tomov 				u8 bpp;
555bbde3104STodor Tomov 
556bbde3104STodor Tomov 				if (j == VFE_LINE_PIX) {
557bbde3104STodor Tomov 					tmp = pixel_clock[j];
558bbde3104STodor Tomov 				} else {
559cba3819dSTodor Tomov 					struct vfe_line *l = &vfe->line[j];
560cba3819dSTodor Tomov 
561cba3819dSTodor Tomov 					bpp = vfe_get_bpp(l->formats,
562cba3819dSTodor Tomov 						l->nformats,
563cba3819dSTodor Tomov 						l->fmt[MSM_VFE_PAD_SINK].code);
564bbde3104STodor Tomov 					tmp = pixel_clock[j] * bpp / 64;
565bbde3104STodor Tomov 				}
566bbde3104STodor Tomov 
567bbde3104STodor Tomov 				if (min_rate < tmp)
568bbde3104STodor Tomov 					min_rate = tmp;
569bbde3104STodor Tomov 			}
570bbde3104STodor Tomov 
571bbde3104STodor Tomov 			camss_add_clock_margin(&min_rate);
572bbde3104STodor Tomov 
573bbde3104STodor Tomov 			for (j = 0; j < clock->nfreqs; j++)
574bbde3104STodor Tomov 				if (min_rate < clock->freq[j])
575bbde3104STodor Tomov 					break;
576bbde3104STodor Tomov 
577bbde3104STodor Tomov 			if (j == clock->nfreqs) {
578bbde3104STodor Tomov 				dev_err(dev,
579bbde3104STodor Tomov 					"Pixel clock is too high for VFE");
580bbde3104STodor Tomov 				return -EINVAL;
581bbde3104STodor Tomov 			}
582bbde3104STodor Tomov 
583bbde3104STodor Tomov 			/* if sensor pixel clock is not available */
584bbde3104STodor Tomov 			/* set highest possible VFE clock rate */
585bbde3104STodor Tomov 			if (min_rate == 0)
586bbde3104STodor Tomov 				j = clock->nfreqs - 1;
587bbde3104STodor Tomov 
588bbde3104STodor Tomov 			rate = clk_round_rate(clock->clk, clock->freq[j]);
589bbde3104STodor Tomov 			if (rate < 0) {
590bbde3104STodor Tomov 				dev_err(dev, "clk round rate failed: %ld\n",
591bbde3104STodor Tomov 					rate);
592bbde3104STodor Tomov 				return -EINVAL;
593bbde3104STodor Tomov 			}
594bbde3104STodor Tomov 
595bbde3104STodor Tomov 			ret = clk_set_rate(clock->clk, rate);
596bbde3104STodor Tomov 			if (ret < 0) {
597bbde3104STodor Tomov 				dev_err(dev, "clk set rate failed: %d\n", ret);
598bbde3104STodor Tomov 				return ret;
599bbde3104STodor Tomov 			}
600bbde3104STodor Tomov 		}
601bbde3104STodor Tomov 	}
602bbde3104STodor Tomov 
603bbde3104STodor Tomov 	return 0;
604bbde3104STodor Tomov }
605bbde3104STodor Tomov 
606bbde3104STodor Tomov /*
607bbde3104STodor Tomov  * vfe_check_clock_rates - Check current clock rates on VFE module
608bbde3104STodor Tomov  * @vfe: VFE device
609bbde3104STodor Tomov  *
610bbde3104STodor Tomov  * Return 0 if current clock rates are suitable for a new pipeline
611bbde3104STodor Tomov  * or a negative error code otherwise
612bbde3104STodor Tomov  */
613bbde3104STodor Tomov static int vfe_check_clock_rates(struct vfe_device *vfe)
614bbde3104STodor Tomov {
615633b388fSRobert Foss 	u64 pixel_clock[VFE_LINE_NUM_MAX];
616bbde3104STodor Tomov 	int i, j;
617bbde3104STodor Tomov 	int ret;
618bbde3104STodor Tomov 
619633b388fSRobert Foss 	for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) {
620bbde3104STodor Tomov 		ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity,
621bbde3104STodor Tomov 					    &pixel_clock[i]);
622bbde3104STodor Tomov 		if (ret)
623bbde3104STodor Tomov 			pixel_clock[i] = 0;
624bbde3104STodor Tomov 	}
625bbde3104STodor Tomov 
626bbde3104STodor Tomov 	for (i = 0; i < vfe->nclocks; i++) {
627bbde3104STodor Tomov 		struct camss_clock *clock = &vfe->clock[i];
628bbde3104STodor Tomov 
629bcd2adfeSBryan O'Donoghue 		if (vfe_match_clock_names(vfe, clock)) {
630bbde3104STodor Tomov 			u64 min_rate = 0;
631bbde3104STodor Tomov 			unsigned long rate;
632bbde3104STodor Tomov 
633633b388fSRobert Foss 			for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) {
634bbde3104STodor Tomov 				u32 tmp;
635bbde3104STodor Tomov 				u8 bpp;
636bbde3104STodor Tomov 
637bbde3104STodor Tomov 				if (j == VFE_LINE_PIX) {
638bbde3104STodor Tomov 					tmp = pixel_clock[j];
639bbde3104STodor Tomov 				} else {
640cba3819dSTodor Tomov 					struct vfe_line *l = &vfe->line[j];
641cba3819dSTodor Tomov 
642cba3819dSTodor Tomov 					bpp = vfe_get_bpp(l->formats,
643cba3819dSTodor Tomov 						l->nformats,
644cba3819dSTodor Tomov 						l->fmt[MSM_VFE_PAD_SINK].code);
645bbde3104STodor Tomov 					tmp = pixel_clock[j] * bpp / 64;
646bbde3104STodor Tomov 				}
647bbde3104STodor Tomov 
648bbde3104STodor Tomov 				if (min_rate < tmp)
649bbde3104STodor Tomov 					min_rate = tmp;
650bbde3104STodor Tomov 			}
651bbde3104STodor Tomov 
652bbde3104STodor Tomov 			camss_add_clock_margin(&min_rate);
653bbde3104STodor Tomov 
654bbde3104STodor Tomov 			rate = clk_get_rate(clock->clk);
655bbde3104STodor Tomov 			if (rate < min_rate)
656bbde3104STodor Tomov 				return -EBUSY;
657bbde3104STodor Tomov 		}
658bbde3104STodor Tomov 	}
659bbde3104STodor Tomov 
660bbde3104STodor Tomov 	return 0;
661bbde3104STodor Tomov }
662bbde3104STodor Tomov 
663bbde3104STodor Tomov /*
6644c98a5f5STodor Tomov  * vfe_get - Power up and reset VFE module
6654c98a5f5STodor Tomov  * @vfe: VFE Device
6664c98a5f5STodor Tomov  *
6674c98a5f5STodor Tomov  * Return 0 on success or a negative error code otherwise
6684c98a5f5STodor Tomov  */
669c5af8db8SBryan O'Donoghue int vfe_get(struct vfe_device *vfe)
6704c98a5f5STodor Tomov {
6714c98a5f5STodor Tomov 	int ret;
6724c98a5f5STodor Tomov 
6734c98a5f5STodor Tomov 	mutex_lock(&vfe->power_lock);
6744c98a5f5STodor Tomov 
6754c98a5f5STodor Tomov 	if (vfe->power_count == 0) {
6762f6f8af6SRobert Foss 		ret = vfe->ops->pm_domain_on(vfe);
67702afa816STodor Tomov 		if (ret < 0)
67802afa816STodor Tomov 			goto error_pm_domain;
67902afa816STodor Tomov 
68009dfb36cSMauro Carvalho Chehab 		ret = pm_runtime_resume_and_get(vfe->camss->dev);
68102afa816STodor Tomov 		if (ret < 0)
68209dfb36cSMauro Carvalho Chehab 			goto error_domain_off;
68302afa816STodor Tomov 
684bbde3104STodor Tomov 		ret = vfe_set_clock_rates(vfe);
685bbde3104STodor Tomov 		if (ret < 0)
68677909691SDinghao Liu 			goto error_pm_runtime_get;
687bbde3104STodor Tomov 
6884c98a5f5STodor Tomov 		ret = camss_enable_clocks(vfe->nclocks, vfe->clock,
6899c3e59deSTodor Tomov 					  vfe->camss->dev);
6904c98a5f5STodor Tomov 		if (ret < 0)
69177909691SDinghao Liu 			goto error_pm_runtime_get;
6924c98a5f5STodor Tomov 
6934c98a5f5STodor Tomov 		ret = vfe_reset(vfe);
6944c98a5f5STodor Tomov 		if (ret < 0)
6954c98a5f5STodor Tomov 			goto error_reset;
6964c98a5f5STodor Tomov 
6974c98a5f5STodor Tomov 		vfe_reset_output_maps(vfe);
6984c98a5f5STodor Tomov 
6994c98a5f5STodor Tomov 		vfe_init_outputs(vfe);
700745b475eSRobert Foss 
701745b475eSRobert Foss 		vfe->ops->hw_version(vfe);
702bbde3104STodor Tomov 	} else {
703bbde3104STodor Tomov 		ret = vfe_check_clock_rates(vfe);
704bbde3104STodor Tomov 		if (ret < 0)
70526bda3daSBryan O'Donoghue 			goto error_pm_domain;
7064c98a5f5STodor Tomov 	}
7074c98a5f5STodor Tomov 	vfe->power_count++;
7084c98a5f5STodor Tomov 
7094c98a5f5STodor Tomov 	mutex_unlock(&vfe->power_lock);
7104c98a5f5STodor Tomov 
7114c98a5f5STodor Tomov 	return 0;
7124c98a5f5STodor Tomov 
7134c98a5f5STodor Tomov error_reset:
7144c98a5f5STodor Tomov 	camss_disable_clocks(vfe->nclocks, vfe->clock);
7154c98a5f5STodor Tomov 
71602afa816STodor Tomov error_pm_runtime_get:
71777909691SDinghao Liu 	pm_runtime_put_sync(vfe->camss->dev);
71809dfb36cSMauro Carvalho Chehab error_domain_off:
7192f6f8af6SRobert Foss 	vfe->ops->pm_domain_off(vfe);
72002afa816STodor Tomov 
72102afa816STodor Tomov error_pm_domain:
7224c98a5f5STodor Tomov 	mutex_unlock(&vfe->power_lock);
7234c98a5f5STodor Tomov 
7244c98a5f5STodor Tomov 	return ret;
7254c98a5f5STodor Tomov }
7264c98a5f5STodor Tomov 
7274c98a5f5STodor Tomov /*
7284c98a5f5STodor Tomov  * vfe_put - Power down VFE module
7294c98a5f5STodor Tomov  * @vfe: VFE Device
7304c98a5f5STodor Tomov  */
731c5af8db8SBryan O'Donoghue void vfe_put(struct vfe_device *vfe)
7324c98a5f5STodor Tomov {
7334c98a5f5STodor Tomov 	mutex_lock(&vfe->power_lock);
7344c98a5f5STodor Tomov 
7354c98a5f5STodor Tomov 	if (vfe->power_count == 0) {
7369c3e59deSTodor Tomov 		dev_err(vfe->camss->dev, "vfe power off on power_count == 0\n");
7374c98a5f5STodor Tomov 		goto exit;
7384c98a5f5STodor Tomov 	} else if (vfe->power_count == 1) {
7394c98a5f5STodor Tomov 		if (vfe->was_streaming) {
7404c98a5f5STodor Tomov 			vfe->was_streaming = 0;
741633b388fSRobert Foss 			vfe->ops->vfe_halt(vfe);
7424c98a5f5STodor Tomov 		}
7434c98a5f5STodor Tomov 		camss_disable_clocks(vfe->nclocks, vfe->clock);
74402afa816STodor Tomov 		pm_runtime_put_sync(vfe->camss->dev);
7452f6f8af6SRobert Foss 		vfe->ops->pm_domain_off(vfe);
7464c98a5f5STodor Tomov 	}
7474c98a5f5STodor Tomov 
7484c98a5f5STodor Tomov 	vfe->power_count--;
7494c98a5f5STodor Tomov 
7504c98a5f5STodor Tomov exit:
7514c98a5f5STodor Tomov 	mutex_unlock(&vfe->power_lock);
7524c98a5f5STodor Tomov }
7534c98a5f5STodor Tomov 
7544c98a5f5STodor Tomov /*
7554c98a5f5STodor Tomov  * vfe_flush_buffers - Return all vb2 buffers
7564c98a5f5STodor Tomov  * @vid: Video device structure
7574c98a5f5STodor Tomov  * @state: vb2 buffer state of the returned buffers
7584c98a5f5STodor Tomov  *
7594c98a5f5STodor Tomov  * Return all buffers to vb2. This includes queued pending buffers (still
7604c98a5f5STodor Tomov  * unused) and any buffers given to the hardware but again still not used.
7614c98a5f5STodor Tomov  *
7624c98a5f5STodor Tomov  * Return 0 on success or a negative error code otherwise
7634c98a5f5STodor Tomov  */
764633b388fSRobert Foss int vfe_flush_buffers(struct camss_video *vid,
7654c98a5f5STodor Tomov 		      enum vb2_buffer_state state)
7664c98a5f5STodor Tomov {
767a93e5f4fSTodor Tomov 	struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
768a93e5f4fSTodor Tomov 	struct vfe_device *vfe = to_vfe(line);
7694c98a5f5STodor Tomov 	struct vfe_output *output;
7704c98a5f5STodor Tomov 	unsigned long flags;
7714c98a5f5STodor Tomov 
7724c98a5f5STodor Tomov 	output = &line->output;
7734c98a5f5STodor Tomov 
7744c98a5f5STodor Tomov 	spin_lock_irqsave(&vfe->output_lock, flags);
7754c98a5f5STodor Tomov 
7764c98a5f5STodor Tomov 	vfe_buf_flush_pending(output, state);
7774c98a5f5STodor Tomov 
7784c98a5f5STodor Tomov 	if (output->buf[0])
7794c98a5f5STodor Tomov 		vb2_buffer_done(&output->buf[0]->vb.vb2_buf, state);
7804c98a5f5STodor Tomov 
7814c98a5f5STodor Tomov 	if (output->buf[1])
7824c98a5f5STodor Tomov 		vb2_buffer_done(&output->buf[1]->vb.vb2_buf, state);
7834c98a5f5STodor Tomov 
7844c98a5f5STodor Tomov 	if (output->last_buffer) {
7854c98a5f5STodor Tomov 		vb2_buffer_done(&output->last_buffer->vb.vb2_buf, state);
7864c98a5f5STodor Tomov 		output->last_buffer = NULL;
7874c98a5f5STodor Tomov 	}
7884c98a5f5STodor Tomov 
7894c98a5f5STodor Tomov 	spin_unlock_irqrestore(&vfe->output_lock, flags);
7904c98a5f5STodor Tomov 
7914c98a5f5STodor Tomov 	return 0;
7924c98a5f5STodor Tomov }
7934c98a5f5STodor Tomov 
7944c98a5f5STodor Tomov /*
7954c98a5f5STodor Tomov  * vfe_set_power - Power on/off VFE module
7964c98a5f5STodor Tomov  * @sd: VFE V4L2 subdevice
7974c98a5f5STodor Tomov  * @on: Requested power state
7984c98a5f5STodor Tomov  *
7994c98a5f5STodor Tomov  * Return 0 on success or a negative error code otherwise
8004c98a5f5STodor Tomov  */
8014c98a5f5STodor Tomov static int vfe_set_power(struct v4l2_subdev *sd, int on)
8024c98a5f5STodor Tomov {
8034c98a5f5STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
8044c98a5f5STodor Tomov 	struct vfe_device *vfe = to_vfe(line);
8054c98a5f5STodor Tomov 	int ret;
8064c98a5f5STodor Tomov 
8074c98a5f5STodor Tomov 	if (on) {
8084c98a5f5STodor Tomov 		ret = vfe_get(vfe);
8094c98a5f5STodor Tomov 		if (ret < 0)
8104c98a5f5STodor Tomov 			return ret;
8114c98a5f5STodor Tomov 	} else {
8124c98a5f5STodor Tomov 		vfe_put(vfe);
8134c98a5f5STodor Tomov 	}
8144c98a5f5STodor Tomov 
8154c98a5f5STodor Tomov 	return 0;
8164c98a5f5STodor Tomov }
8174c98a5f5STodor Tomov 
8184c98a5f5STodor Tomov /*
8194c98a5f5STodor Tomov  * vfe_set_stream - Enable/disable streaming on VFE module
8204c98a5f5STodor Tomov  * @sd: VFE V4L2 subdevice
8214c98a5f5STodor Tomov  * @enable: Requested streaming state
8224c98a5f5STodor Tomov  *
8234c98a5f5STodor Tomov  * Main configuration of VFE module is triggered here.
8244c98a5f5STodor Tomov  *
8254c98a5f5STodor Tomov  * Return 0 on success or a negative error code otherwise
8264c98a5f5STodor Tomov  */
8274c98a5f5STodor Tomov static int vfe_set_stream(struct v4l2_subdev *sd, int enable)
8284c98a5f5STodor Tomov {
8294c98a5f5STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
8304c98a5f5STodor Tomov 	struct vfe_device *vfe = to_vfe(line);
8314c98a5f5STodor Tomov 	int ret;
8324c98a5f5STodor Tomov 
8334c98a5f5STodor Tomov 	if (enable) {
8348ce158c1SMilen Mitkov 		line->output.state = VFE_OUTPUT_RESERVED;
835633b388fSRobert Foss 		ret = vfe->ops->vfe_enable(line);
8364c98a5f5STodor Tomov 		if (ret < 0)
8379c3e59deSTodor Tomov 			dev_err(vfe->camss->dev,
8384c98a5f5STodor Tomov 				"Failed to enable vfe outputs\n");
8394c98a5f5STodor Tomov 	} else {
840633b388fSRobert Foss 		ret = vfe->ops->vfe_disable(line);
8414c98a5f5STodor Tomov 		if (ret < 0)
8429c3e59deSTodor Tomov 			dev_err(vfe->camss->dev,
8434c98a5f5STodor Tomov 				"Failed to disable vfe outputs\n");
8444c98a5f5STodor Tomov 	}
8454c98a5f5STodor Tomov 
8464c98a5f5STodor Tomov 	return ret;
8474c98a5f5STodor Tomov }
8484c98a5f5STodor Tomov 
8494c98a5f5STodor Tomov /*
8504c98a5f5STodor Tomov  * __vfe_get_format - Get pointer to format structure
8514c98a5f5STodor Tomov  * @line: VFE line
852c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
8534c98a5f5STodor Tomov  * @pad: pad from which format is requested
8544c98a5f5STodor Tomov  * @which: TRY or ACTIVE format
8554c98a5f5STodor Tomov  *
8564c98a5f5STodor Tomov  * Return pointer to TRY or ACTIVE format structure
8574c98a5f5STodor Tomov  */
8584c98a5f5STodor Tomov static struct v4l2_mbus_framefmt *
8594c98a5f5STodor Tomov __vfe_get_format(struct vfe_line *line,
8600d346d2aSTomi Valkeinen 		 struct v4l2_subdev_state *sd_state,
8614c98a5f5STodor Tomov 		 unsigned int pad,
8624c98a5f5STodor Tomov 		 enum v4l2_subdev_format_whence which)
8634c98a5f5STodor Tomov {
8644c98a5f5STodor Tomov 	if (which == V4L2_SUBDEV_FORMAT_TRY)
865bc0e8d91SSakari Ailus 		return v4l2_subdev_state_get_format(sd_state, pad);
8664c98a5f5STodor Tomov 
8674c98a5f5STodor Tomov 	return &line->fmt[pad];
8684c98a5f5STodor Tomov }
8694c98a5f5STodor Tomov 
870810b6598STodor Tomov /*
871810b6598STodor Tomov  * __vfe_get_compose - Get pointer to compose selection structure
872810b6598STodor Tomov  * @line: VFE line
873c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
874810b6598STodor Tomov  * @which: TRY or ACTIVE format
875810b6598STodor Tomov  *
876810b6598STodor Tomov  * Return pointer to TRY or ACTIVE compose rectangle structure
877810b6598STodor Tomov  */
878810b6598STodor Tomov static struct v4l2_rect *
879810b6598STodor Tomov __vfe_get_compose(struct vfe_line *line,
8800d346d2aSTomi Valkeinen 		  struct v4l2_subdev_state *sd_state,
881810b6598STodor Tomov 		  enum v4l2_subdev_format_whence which)
882810b6598STodor Tomov {
883810b6598STodor Tomov 	if (which == V4L2_SUBDEV_FORMAT_TRY)
884bc0e8d91SSakari Ailus 		return v4l2_subdev_state_get_compose(sd_state,
885810b6598STodor Tomov 						     MSM_VFE_PAD_SINK);
886810b6598STodor Tomov 
887810b6598STodor Tomov 	return &line->compose;
888810b6598STodor Tomov }
8894c98a5f5STodor Tomov 
8904c98a5f5STodor Tomov /*
891780bf2feSTodor Tomov  * __vfe_get_crop - Get pointer to crop selection structure
892780bf2feSTodor Tomov  * @line: VFE line
893c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
894780bf2feSTodor Tomov  * @which: TRY or ACTIVE format
895780bf2feSTodor Tomov  *
896780bf2feSTodor Tomov  * Return pointer to TRY or ACTIVE crop rectangle structure
897780bf2feSTodor Tomov  */
898780bf2feSTodor Tomov static struct v4l2_rect *
899780bf2feSTodor Tomov __vfe_get_crop(struct vfe_line *line,
9000d346d2aSTomi Valkeinen 	       struct v4l2_subdev_state *sd_state,
901780bf2feSTodor Tomov 	       enum v4l2_subdev_format_whence which)
902780bf2feSTodor Tomov {
903780bf2feSTodor Tomov 	if (which == V4L2_SUBDEV_FORMAT_TRY)
904bc0e8d91SSakari Ailus 		return v4l2_subdev_state_get_crop(sd_state, MSM_VFE_PAD_SRC);
905780bf2feSTodor Tomov 
906780bf2feSTodor Tomov 	return &line->crop;
907780bf2feSTodor Tomov }
908780bf2feSTodor Tomov 
909780bf2feSTodor Tomov /*
9104c98a5f5STodor Tomov  * vfe_try_format - Handle try format by pad subdev method
9114c98a5f5STodor Tomov  * @line: VFE line
912c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
9134c98a5f5STodor Tomov  * @pad: pad on which format is requested
9144c98a5f5STodor Tomov  * @fmt: pointer to v4l2 format structure
9154c98a5f5STodor Tomov  * @which: wanted subdev format
9164c98a5f5STodor Tomov  */
9174c98a5f5STodor Tomov static void vfe_try_format(struct vfe_line *line,
9180d346d2aSTomi Valkeinen 			   struct v4l2_subdev_state *sd_state,
9194c98a5f5STodor Tomov 			   unsigned int pad,
9204c98a5f5STodor Tomov 			   struct v4l2_mbus_framefmt *fmt,
9214c98a5f5STodor Tomov 			   enum v4l2_subdev_format_whence which)
9224c98a5f5STodor Tomov {
9234c98a5f5STodor Tomov 	unsigned int i;
9249b5833f7STodor Tomov 	u32 code;
9254c98a5f5STodor Tomov 
9264c98a5f5STodor Tomov 	switch (pad) {
9274c98a5f5STodor Tomov 	case MSM_VFE_PAD_SINK:
9284c98a5f5STodor Tomov 		/* Set format on sink pad */
9294c98a5f5STodor Tomov 
930cba3819dSTodor Tomov 		for (i = 0; i < line->nformats; i++)
931cba3819dSTodor Tomov 			if (fmt->code == line->formats[i].code)
9324c98a5f5STodor Tomov 				break;
9334c98a5f5STodor Tomov 
9344c98a5f5STodor Tomov 		/* If not found, use UYVY as default */
935cba3819dSTodor Tomov 		if (i >= line->nformats)
93689936bfbSMartin Dørum 			fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
9374c98a5f5STodor Tomov 
9384c98a5f5STodor Tomov 		fmt->width = clamp_t(u32, fmt->width, 1, 8191);
9394c98a5f5STodor Tomov 		fmt->height = clamp_t(u32, fmt->height, 1, 8191);
9404c98a5f5STodor Tomov 
9414c98a5f5STodor Tomov 		fmt->field = V4L2_FIELD_NONE;
9424c98a5f5STodor Tomov 		fmt->colorspace = V4L2_COLORSPACE_SRGB;
9434c98a5f5STodor Tomov 
9444c98a5f5STodor Tomov 		break;
9454c98a5f5STodor Tomov 
9464c98a5f5STodor Tomov 	case MSM_VFE_PAD_SRC:
9474c98a5f5STodor Tomov 		/* Set and return a format same as sink pad */
9489b5833f7STodor Tomov 		code = fmt->code;
9499b5833f7STodor Tomov 
9500d346d2aSTomi Valkeinen 		*fmt = *__vfe_get_format(line, sd_state, MSM_VFE_PAD_SINK,
9510d346d2aSTomi Valkeinen 					 which);
95207eeb342STodor Tomov 
95307eeb342STodor Tomov 		fmt->code = vfe_src_pad_code(line, fmt->code, 0, code);
9544c98a5f5STodor Tomov 
955810b6598STodor Tomov 		if (line->id == VFE_LINE_PIX) {
956810b6598STodor Tomov 			struct v4l2_rect *rect;
957810b6598STodor Tomov 
9580d346d2aSTomi Valkeinen 			rect = __vfe_get_crop(line, sd_state, which);
959810b6598STodor Tomov 
960810b6598STodor Tomov 			fmt->width = rect->width;
961810b6598STodor Tomov 			fmt->height = rect->height;
962810b6598STodor Tomov 		}
9639b5833f7STodor Tomov 
9644c98a5f5STodor Tomov 		break;
9654c98a5f5STodor Tomov 	}
9664c98a5f5STodor Tomov 
9674c98a5f5STodor Tomov 	fmt->colorspace = V4L2_COLORSPACE_SRGB;
9684c98a5f5STodor Tomov }
9694c98a5f5STodor Tomov 
9704c98a5f5STodor Tomov /*
971810b6598STodor Tomov  * vfe_try_compose - Handle try compose selection by pad subdev method
972810b6598STodor Tomov  * @line: VFE line
973c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
974810b6598STodor Tomov  * @rect: pointer to v4l2 rect structure
975810b6598STodor Tomov  * @which: wanted subdev format
976810b6598STodor Tomov  */
977810b6598STodor Tomov static void vfe_try_compose(struct vfe_line *line,
9780d346d2aSTomi Valkeinen 			    struct v4l2_subdev_state *sd_state,
979810b6598STodor Tomov 			    struct v4l2_rect *rect,
980810b6598STodor Tomov 			    enum v4l2_subdev_format_whence which)
981810b6598STodor Tomov {
982810b6598STodor Tomov 	struct v4l2_mbus_framefmt *fmt;
983810b6598STodor Tomov 
9840d346d2aSTomi Valkeinen 	fmt = __vfe_get_format(line, sd_state, MSM_VFE_PAD_SINK, which);
985810b6598STodor Tomov 
986810b6598STodor Tomov 	if (rect->width > fmt->width)
987810b6598STodor Tomov 		rect->width = fmt->width;
988810b6598STodor Tomov 
989810b6598STodor Tomov 	if (rect->height > fmt->height)
990810b6598STodor Tomov 		rect->height = fmt->height;
991810b6598STodor Tomov 
992810b6598STodor Tomov 	if (fmt->width > rect->width * SCALER_RATIO_MAX)
993810b6598STodor Tomov 		rect->width = (fmt->width + SCALER_RATIO_MAX - 1) /
994810b6598STodor Tomov 							SCALER_RATIO_MAX;
995810b6598STodor Tomov 
996810b6598STodor Tomov 	rect->width &= ~0x1;
997810b6598STodor Tomov 
998810b6598STodor Tomov 	if (fmt->height > rect->height * SCALER_RATIO_MAX)
999810b6598STodor Tomov 		rect->height = (fmt->height + SCALER_RATIO_MAX - 1) /
1000810b6598STodor Tomov 							SCALER_RATIO_MAX;
1001810b6598STodor Tomov 
1002810b6598STodor Tomov 	if (rect->width < 16)
1003810b6598STodor Tomov 		rect->width = 16;
1004810b6598STodor Tomov 
1005810b6598STodor Tomov 	if (rect->height < 4)
1006810b6598STodor Tomov 		rect->height = 4;
1007810b6598STodor Tomov }
1008810b6598STodor Tomov 
1009810b6598STodor Tomov /*
1010780bf2feSTodor Tomov  * vfe_try_crop - Handle try crop selection by pad subdev method
1011780bf2feSTodor Tomov  * @line: VFE line
1012c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
1013780bf2feSTodor Tomov  * @rect: pointer to v4l2 rect structure
1014780bf2feSTodor Tomov  * @which: wanted subdev format
1015780bf2feSTodor Tomov  */
1016780bf2feSTodor Tomov static void vfe_try_crop(struct vfe_line *line,
10170d346d2aSTomi Valkeinen 			 struct v4l2_subdev_state *sd_state,
1018780bf2feSTodor Tomov 			 struct v4l2_rect *rect,
1019780bf2feSTodor Tomov 			 enum v4l2_subdev_format_whence which)
1020780bf2feSTodor Tomov {
1021780bf2feSTodor Tomov 	struct v4l2_rect *compose;
1022780bf2feSTodor Tomov 
10230d346d2aSTomi Valkeinen 	compose = __vfe_get_compose(line, sd_state, which);
1024780bf2feSTodor Tomov 
1025780bf2feSTodor Tomov 	if (rect->width > compose->width)
1026780bf2feSTodor Tomov 		rect->width = compose->width;
1027780bf2feSTodor Tomov 
1028780bf2feSTodor Tomov 	if (rect->width + rect->left > compose->width)
1029780bf2feSTodor Tomov 		rect->left = compose->width - rect->width;
1030780bf2feSTodor Tomov 
1031780bf2feSTodor Tomov 	if (rect->height > compose->height)
1032780bf2feSTodor Tomov 		rect->height = compose->height;
1033780bf2feSTodor Tomov 
1034780bf2feSTodor Tomov 	if (rect->height + rect->top > compose->height)
1035780bf2feSTodor Tomov 		rect->top = compose->height - rect->height;
1036780bf2feSTodor Tomov 
1037780bf2feSTodor Tomov 	/* wm in line based mode writes multiple of 16 horizontally */
1038780bf2feSTodor Tomov 	rect->left += (rect->width & 0xf) >> 1;
1039780bf2feSTodor Tomov 	rect->width &= ~0xf;
1040780bf2feSTodor Tomov 
1041780bf2feSTodor Tomov 	if (rect->width < 16) {
1042780bf2feSTodor Tomov 		rect->left = 0;
1043780bf2feSTodor Tomov 		rect->width = 16;
1044780bf2feSTodor Tomov 	}
1045780bf2feSTodor Tomov 
1046780bf2feSTodor Tomov 	if (rect->height < 4) {
1047780bf2feSTodor Tomov 		rect->top = 0;
1048780bf2feSTodor Tomov 		rect->height = 4;
1049780bf2feSTodor Tomov 	}
1050780bf2feSTodor Tomov }
1051780bf2feSTodor Tomov 
1052780bf2feSTodor Tomov /*
10534c98a5f5STodor Tomov  * vfe_enum_mbus_code - Handle pixel format enumeration
10544c98a5f5STodor Tomov  * @sd: VFE V4L2 subdevice
1055c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
10564c98a5f5STodor Tomov  * @code: pointer to v4l2_subdev_mbus_code_enum structure
10574c98a5f5STodor Tomov  *
10584c98a5f5STodor Tomov  * return -EINVAL or zero on success
10594c98a5f5STodor Tomov  */
10604c98a5f5STodor Tomov static int vfe_enum_mbus_code(struct v4l2_subdev *sd,
10610d346d2aSTomi Valkeinen 			      struct v4l2_subdev_state *sd_state,
10624c98a5f5STodor Tomov 			      struct v4l2_subdev_mbus_code_enum *code)
10634c98a5f5STodor Tomov {
10644c98a5f5STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
10654c98a5f5STodor Tomov 
10664c98a5f5STodor Tomov 	if (code->pad == MSM_VFE_PAD_SINK) {
1067cba3819dSTodor Tomov 		if (code->index >= line->nformats)
10684c98a5f5STodor Tomov 			return -EINVAL;
10694c98a5f5STodor Tomov 
1070cba3819dSTodor Tomov 		code->code = line->formats[code->index].code;
10714c98a5f5STodor Tomov 	} else {
107207eeb342STodor Tomov 		struct v4l2_mbus_framefmt *sink_fmt;
10734c98a5f5STodor Tomov 
10740d346d2aSTomi Valkeinen 		sink_fmt = __vfe_get_format(line, sd_state, MSM_VFE_PAD_SINK,
10754c98a5f5STodor Tomov 					    code->which);
10764c98a5f5STodor Tomov 
107707eeb342STodor Tomov 		code->code = vfe_src_pad_code(line, sink_fmt->code,
107807eeb342STodor Tomov 					      code->index, 0);
107907eeb342STodor Tomov 		if (!code->code)
108007eeb342STodor Tomov 			return -EINVAL;
10814c98a5f5STodor Tomov 	}
10824c98a5f5STodor Tomov 
10834c98a5f5STodor Tomov 	return 0;
10844c98a5f5STodor Tomov }
10854c98a5f5STodor Tomov 
10864c98a5f5STodor Tomov /*
10874c98a5f5STodor Tomov  * vfe_enum_frame_size - Handle frame size enumeration
10884c98a5f5STodor Tomov  * @sd: VFE V4L2 subdevice
1089c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
10904c98a5f5STodor Tomov  * @fse: pointer to v4l2_subdev_frame_size_enum structure
10914c98a5f5STodor Tomov  *
10924c98a5f5STodor Tomov  * Return -EINVAL or zero on success
10934c98a5f5STodor Tomov  */
10944c98a5f5STodor Tomov static int vfe_enum_frame_size(struct v4l2_subdev *sd,
10950d346d2aSTomi Valkeinen 			       struct v4l2_subdev_state *sd_state,
10964c98a5f5STodor Tomov 			       struct v4l2_subdev_frame_size_enum *fse)
10974c98a5f5STodor Tomov {
10984c98a5f5STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
10994c98a5f5STodor Tomov 	struct v4l2_mbus_framefmt format;
11004c98a5f5STodor Tomov 
11014c98a5f5STodor Tomov 	if (fse->index != 0)
11024c98a5f5STodor Tomov 		return -EINVAL;
11034c98a5f5STodor Tomov 
11044c98a5f5STodor Tomov 	format.code = fse->code;
11054c98a5f5STodor Tomov 	format.width = 1;
11064c98a5f5STodor Tomov 	format.height = 1;
11070d346d2aSTomi Valkeinen 	vfe_try_format(line, sd_state, fse->pad, &format, fse->which);
11084c98a5f5STodor Tomov 	fse->min_width = format.width;
11094c98a5f5STodor Tomov 	fse->min_height = format.height;
11104c98a5f5STodor Tomov 
11114c98a5f5STodor Tomov 	if (format.code != fse->code)
11124c98a5f5STodor Tomov 		return -EINVAL;
11134c98a5f5STodor Tomov 
11144c98a5f5STodor Tomov 	format.code = fse->code;
11154c98a5f5STodor Tomov 	format.width = -1;
11164c98a5f5STodor Tomov 	format.height = -1;
11170d346d2aSTomi Valkeinen 	vfe_try_format(line, sd_state, fse->pad, &format, fse->which);
11184c98a5f5STodor Tomov 	fse->max_width = format.width;
11194c98a5f5STodor Tomov 	fse->max_height = format.height;
11204c98a5f5STodor Tomov 
11214c98a5f5STodor Tomov 	return 0;
11224c98a5f5STodor Tomov }
11234c98a5f5STodor Tomov 
11244c98a5f5STodor Tomov /*
11254c98a5f5STodor Tomov  * vfe_get_format - Handle get format by pads subdev method
11264c98a5f5STodor Tomov  * @sd: VFE V4L2 subdevice
1127c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
11284c98a5f5STodor Tomov  * @fmt: pointer to v4l2 subdev format structure
11294c98a5f5STodor Tomov  *
11304c98a5f5STodor Tomov  * Return -EINVAL or zero on success
11314c98a5f5STodor Tomov  */
11324c98a5f5STodor Tomov static int vfe_get_format(struct v4l2_subdev *sd,
11330d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
11344c98a5f5STodor Tomov 			  struct v4l2_subdev_format *fmt)
11354c98a5f5STodor Tomov {
11364c98a5f5STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
11374c98a5f5STodor Tomov 	struct v4l2_mbus_framefmt *format;
11384c98a5f5STodor Tomov 
11390d346d2aSTomi Valkeinen 	format = __vfe_get_format(line, sd_state, fmt->pad, fmt->which);
11404c98a5f5STodor Tomov 	if (format == NULL)
11414c98a5f5STodor Tomov 		return -EINVAL;
11424c98a5f5STodor Tomov 
11434c98a5f5STodor Tomov 	fmt->format = *format;
11444c98a5f5STodor Tomov 
11454c98a5f5STodor Tomov 	return 0;
11464c98a5f5STodor Tomov }
11474c98a5f5STodor Tomov 
1148810b6598STodor Tomov static int vfe_set_selection(struct v4l2_subdev *sd,
11490d346d2aSTomi Valkeinen 			     struct v4l2_subdev_state *sd_state,
1150810b6598STodor Tomov 			     struct v4l2_subdev_selection *sel);
1151810b6598STodor Tomov 
11524c98a5f5STodor Tomov /*
11534c98a5f5STodor Tomov  * vfe_set_format - Handle set format by pads subdev method
11544c98a5f5STodor Tomov  * @sd: VFE V4L2 subdevice
1155c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
11564c98a5f5STodor Tomov  * @fmt: pointer to v4l2 subdev format structure
11574c98a5f5STodor Tomov  *
11584c98a5f5STodor Tomov  * Return -EINVAL or zero on success
11594c98a5f5STodor Tomov  */
11604c98a5f5STodor Tomov static int vfe_set_format(struct v4l2_subdev *sd,
11610d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
11624c98a5f5STodor Tomov 			  struct v4l2_subdev_format *fmt)
11634c98a5f5STodor Tomov {
11644c98a5f5STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
11654c98a5f5STodor Tomov 	struct v4l2_mbus_framefmt *format;
11664c98a5f5STodor Tomov 
11670d346d2aSTomi Valkeinen 	format = __vfe_get_format(line, sd_state, fmt->pad, fmt->which);
11684c98a5f5STodor Tomov 	if (format == NULL)
11694c98a5f5STodor Tomov 		return -EINVAL;
11704c98a5f5STodor Tomov 
11710d346d2aSTomi Valkeinen 	vfe_try_format(line, sd_state, fmt->pad, &fmt->format, fmt->which);
11724c98a5f5STodor Tomov 	*format = fmt->format;
11734c98a5f5STodor Tomov 
11744c98a5f5STodor Tomov 	if (fmt->pad == MSM_VFE_PAD_SINK) {
1175810b6598STodor Tomov 		struct v4l2_subdev_selection sel = { 0 };
1176810b6598STodor Tomov 		int ret;
1177810b6598STodor Tomov 
1178810b6598STodor Tomov 		/* Propagate the format from sink to source */
11790d346d2aSTomi Valkeinen 		format = __vfe_get_format(line, sd_state, MSM_VFE_PAD_SRC,
11804c98a5f5STodor Tomov 					  fmt->which);
11814c98a5f5STodor Tomov 
11824c98a5f5STodor Tomov 		*format = fmt->format;
11830d346d2aSTomi Valkeinen 		vfe_try_format(line, sd_state, MSM_VFE_PAD_SRC, format,
11844c98a5f5STodor Tomov 			       fmt->which);
1185810b6598STodor Tomov 
1186810b6598STodor Tomov 		if (line->id != VFE_LINE_PIX)
1187810b6598STodor Tomov 			return 0;
1188810b6598STodor Tomov 
1189810b6598STodor Tomov 		/* Reset sink pad compose selection */
1190810b6598STodor Tomov 		sel.which = fmt->which;
1191810b6598STodor Tomov 		sel.pad = MSM_VFE_PAD_SINK;
1192810b6598STodor Tomov 		sel.target = V4L2_SEL_TGT_COMPOSE;
1193810b6598STodor Tomov 		sel.r.width = fmt->format.width;
1194810b6598STodor Tomov 		sel.r.height = fmt->format.height;
11950d346d2aSTomi Valkeinen 		ret = vfe_set_selection(sd, sd_state, &sel);
1196810b6598STodor Tomov 		if (ret < 0)
1197810b6598STodor Tomov 			return ret;
11984c98a5f5STodor Tomov 	}
11994c98a5f5STodor Tomov 
12004c98a5f5STodor Tomov 	return 0;
12014c98a5f5STodor Tomov }
12024c98a5f5STodor Tomov 
12034c98a5f5STodor Tomov /*
1204810b6598STodor Tomov  * vfe_get_selection - Handle get selection by pads subdev method
1205810b6598STodor Tomov  * @sd: VFE V4L2 subdevice
1206c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
1207810b6598STodor Tomov  * @sel: pointer to v4l2 subdev selection structure
1208810b6598STodor Tomov  *
1209810b6598STodor Tomov  * Return -EINVAL or zero on success
1210810b6598STodor Tomov  */
1211810b6598STodor Tomov static int vfe_get_selection(struct v4l2_subdev *sd,
12120d346d2aSTomi Valkeinen 			     struct v4l2_subdev_state *sd_state,
1213810b6598STodor Tomov 			     struct v4l2_subdev_selection *sel)
1214810b6598STodor Tomov {
1215810b6598STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
1216810b6598STodor Tomov 	struct v4l2_subdev_format fmt = { 0 };
1217780bf2feSTodor Tomov 	struct v4l2_rect *rect;
1218810b6598STodor Tomov 	int ret;
1219810b6598STodor Tomov 
1220780bf2feSTodor Tomov 	if (line->id != VFE_LINE_PIX)
1221810b6598STodor Tomov 		return -EINVAL;
1222810b6598STodor Tomov 
1223780bf2feSTodor Tomov 	if (sel->pad == MSM_VFE_PAD_SINK)
1224810b6598STodor Tomov 		switch (sel->target) {
1225810b6598STodor Tomov 		case V4L2_SEL_TGT_COMPOSE_BOUNDS:
1226810b6598STodor Tomov 			fmt.pad = sel->pad;
1227810b6598STodor Tomov 			fmt.which = sel->which;
12280d346d2aSTomi Valkeinen 			ret = vfe_get_format(sd, sd_state, &fmt);
1229810b6598STodor Tomov 			if (ret < 0)
1230810b6598STodor Tomov 				return ret;
1231780bf2feSTodor Tomov 
1232810b6598STodor Tomov 			sel->r.left = 0;
1233810b6598STodor Tomov 			sel->r.top = 0;
1234810b6598STodor Tomov 			sel->r.width = fmt.format.width;
1235810b6598STodor Tomov 			sel->r.height = fmt.format.height;
1236810b6598STodor Tomov 			break;
1237810b6598STodor Tomov 		case V4L2_SEL_TGT_COMPOSE:
12380d346d2aSTomi Valkeinen 			rect = __vfe_get_compose(line, sd_state, sel->which);
1239780bf2feSTodor Tomov 			if (rect == NULL)
1240810b6598STodor Tomov 				return -EINVAL;
1241810b6598STodor Tomov 
1242780bf2feSTodor Tomov 			sel->r = *rect;
1243780bf2feSTodor Tomov 			break;
1244780bf2feSTodor Tomov 		default:
1245780bf2feSTodor Tomov 			return -EINVAL;
1246780bf2feSTodor Tomov 		}
1247780bf2feSTodor Tomov 	else if (sel->pad == MSM_VFE_PAD_SRC)
1248780bf2feSTodor Tomov 		switch (sel->target) {
1249780bf2feSTodor Tomov 		case V4L2_SEL_TGT_CROP_BOUNDS:
12500d346d2aSTomi Valkeinen 			rect = __vfe_get_compose(line, sd_state, sel->which);
1251780bf2feSTodor Tomov 			if (rect == NULL)
1252780bf2feSTodor Tomov 				return -EINVAL;
1253780bf2feSTodor Tomov 
1254780bf2feSTodor Tomov 			sel->r.left = rect->left;
1255780bf2feSTodor Tomov 			sel->r.top = rect->top;
1256780bf2feSTodor Tomov 			sel->r.width = rect->width;
1257780bf2feSTodor Tomov 			sel->r.height = rect->height;
1258780bf2feSTodor Tomov 			break;
1259780bf2feSTodor Tomov 		case V4L2_SEL_TGT_CROP:
12600d346d2aSTomi Valkeinen 			rect = __vfe_get_crop(line, sd_state, sel->which);
1261780bf2feSTodor Tomov 			if (rect == NULL)
1262780bf2feSTodor Tomov 				return -EINVAL;
1263780bf2feSTodor Tomov 
1264780bf2feSTodor Tomov 			sel->r = *rect;
1265810b6598STodor Tomov 			break;
1266810b6598STodor Tomov 		default:
1267810b6598STodor Tomov 			return -EINVAL;
1268810b6598STodor Tomov 		}
1269810b6598STodor Tomov 
1270810b6598STodor Tomov 	return 0;
1271810b6598STodor Tomov }
1272810b6598STodor Tomov 
1273810b6598STodor Tomov /*
1274810b6598STodor Tomov  * vfe_set_selection - Handle set selection by pads subdev method
1275810b6598STodor Tomov  * @sd: VFE V4L2 subdevice
1276c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
1277810b6598STodor Tomov  * @sel: pointer to v4l2 subdev selection structure
1278810b6598STodor Tomov  *
1279810b6598STodor Tomov  * Return -EINVAL or zero on success
1280810b6598STodor Tomov  */
12819b62ccdbSColin Ian King static int vfe_set_selection(struct v4l2_subdev *sd,
12820d346d2aSTomi Valkeinen 			     struct v4l2_subdev_state *sd_state,
1283810b6598STodor Tomov 			     struct v4l2_subdev_selection *sel)
1284810b6598STodor Tomov {
1285810b6598STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
1286780bf2feSTodor Tomov 	struct v4l2_rect *rect;
1287810b6598STodor Tomov 	int ret;
1288810b6598STodor Tomov 
1289780bf2feSTodor Tomov 	if (line->id != VFE_LINE_PIX)
1290810b6598STodor Tomov 		return -EINVAL;
1291810b6598STodor Tomov 
1292780bf2feSTodor Tomov 	if (sel->target == V4L2_SEL_TGT_COMPOSE &&
1293780bf2feSTodor Tomov 		sel->pad == MSM_VFE_PAD_SINK) {
1294780bf2feSTodor Tomov 		struct v4l2_subdev_selection crop = { 0 };
1295810b6598STodor Tomov 
12960d346d2aSTomi Valkeinen 		rect = __vfe_get_compose(line, sd_state, sel->which);
1297780bf2feSTodor Tomov 		if (rect == NULL)
1298810b6598STodor Tomov 			return -EINVAL;
1299810b6598STodor Tomov 
13000d346d2aSTomi Valkeinen 		vfe_try_compose(line, sd_state, &sel->r, sel->which);
1301780bf2feSTodor Tomov 		*rect = sel->r;
1302780bf2feSTodor Tomov 
1303780bf2feSTodor Tomov 		/* Reset source crop selection */
1304780bf2feSTodor Tomov 		crop.which = sel->which;
1305780bf2feSTodor Tomov 		crop.pad = MSM_VFE_PAD_SRC;
1306780bf2feSTodor Tomov 		crop.target = V4L2_SEL_TGT_CROP;
1307780bf2feSTodor Tomov 		crop.r = *rect;
13080d346d2aSTomi Valkeinen 		ret = vfe_set_selection(sd, sd_state, &crop);
1309780bf2feSTodor Tomov 	} else if (sel->target == V4L2_SEL_TGT_CROP &&
1310780bf2feSTodor Tomov 		sel->pad == MSM_VFE_PAD_SRC) {
1311780bf2feSTodor Tomov 		struct v4l2_subdev_format fmt = { 0 };
1312780bf2feSTodor Tomov 
13130d346d2aSTomi Valkeinen 		rect = __vfe_get_crop(line, sd_state, sel->which);
1314780bf2feSTodor Tomov 		if (rect == NULL)
1315780bf2feSTodor Tomov 			return -EINVAL;
1316780bf2feSTodor Tomov 
13170d346d2aSTomi Valkeinen 		vfe_try_crop(line, sd_state, &sel->r, sel->which);
1318780bf2feSTodor Tomov 		*rect = sel->r;
1319810b6598STodor Tomov 
1320810b6598STodor Tomov 		/* Reset source pad format width and height */
1321810b6598STodor Tomov 		fmt.which = sel->which;
1322810b6598STodor Tomov 		fmt.pad = MSM_VFE_PAD_SRC;
13230d346d2aSTomi Valkeinen 		ret = vfe_get_format(sd, sd_state, &fmt);
1324810b6598STodor Tomov 		if (ret < 0)
1325810b6598STodor Tomov 			return ret;
1326810b6598STodor Tomov 
1327780bf2feSTodor Tomov 		fmt.format.width = rect->width;
1328780bf2feSTodor Tomov 		fmt.format.height = rect->height;
13290d346d2aSTomi Valkeinen 		ret = vfe_set_format(sd, sd_state, &fmt);
1330780bf2feSTodor Tomov 	} else {
1331780bf2feSTodor Tomov 		ret = -EINVAL;
1332780bf2feSTodor Tomov 	}
1333810b6598STodor Tomov 
1334810b6598STodor Tomov 	return ret;
1335810b6598STodor Tomov }
1336810b6598STodor Tomov 
1337810b6598STodor Tomov /*
13384c98a5f5STodor Tomov  * vfe_init_formats - Initialize formats on all pads
13394c98a5f5STodor Tomov  * @sd: VFE V4L2 subdevice
13404c98a5f5STodor Tomov  * @fh: V4L2 subdev file handle
13414c98a5f5STodor Tomov  *
13424c98a5f5STodor Tomov  * Initialize all pad formats with default values.
13434c98a5f5STodor Tomov  *
13444c98a5f5STodor Tomov  * Return 0 on success or a negative error code otherwise
13454c98a5f5STodor Tomov  */
13464c98a5f5STodor Tomov static int vfe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
13474c98a5f5STodor Tomov {
13484c98a5f5STodor Tomov 	struct v4l2_subdev_format format = {
13494c98a5f5STodor Tomov 		.pad = MSM_VFE_PAD_SINK,
13504c98a5f5STodor Tomov 		.which = fh ? V4L2_SUBDEV_FORMAT_TRY :
13514c98a5f5STodor Tomov 			      V4L2_SUBDEV_FORMAT_ACTIVE,
13524c98a5f5STodor Tomov 		.format = {
135389936bfbSMartin Dørum 			.code = MEDIA_BUS_FMT_UYVY8_1X16,
13544c98a5f5STodor Tomov 			.width = 1920,
13554c98a5f5STodor Tomov 			.height = 1080
13564c98a5f5STodor Tomov 		}
13574c98a5f5STodor Tomov 	};
13584c98a5f5STodor Tomov 
13590d346d2aSTomi Valkeinen 	return vfe_set_format(sd, fh ? fh->state : NULL, &format);
13604c98a5f5STodor Tomov }
13614c98a5f5STodor Tomov 
13624c98a5f5STodor Tomov /*
13634c98a5f5STodor Tomov  * msm_vfe_subdev_init - Initialize VFE device structure and resources
13644c98a5f5STodor Tomov  * @vfe: VFE device
13654c98a5f5STodor Tomov  * @res: VFE module resources table
13664c98a5f5STodor Tomov  *
13674c98a5f5STodor Tomov  * Return 0 on success or a negative error code otherwise
13684c98a5f5STodor Tomov  */
13699c3e59deSTodor Tomov int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
13701643b787SBryan O'Donoghue 			const struct camss_subdev_resources *res, u8 id)
13714c98a5f5STodor Tomov {
13729c3e59deSTodor Tomov 	struct device *dev = camss->dev;
13734c98a5f5STodor Tomov 	struct platform_device *pdev = to_platform_device(dev);
1374bbde3104STodor Tomov 	int i, j;
13754c98a5f5STodor Tomov 	int ret;
13764c98a5f5STodor Tomov 
1377c23c7998SBryan O'Donoghue 	vfe->ops = res->ops;
137891719b27SBryan O'Donoghue 
137991719b27SBryan O'Donoghue 	if (!res->line_num)
138091719b27SBryan O'Donoghue 		return -EINVAL;
138191719b27SBryan O'Donoghue 
1382a409b3f0SBryan O'Donoghue 	if (res->has_pd)
1383a409b3f0SBryan O'Donoghue 		vfe->genpd = camss->genpd[id];
1384a409b3f0SBryan O'Donoghue 
138591719b27SBryan O'Donoghue 	vfe->line_num = res->line_num;
1386633b388fSRobert Foss 	vfe->ops->subdev_init(dev, vfe);
1387051a01acSTodor Tomov 
13884c98a5f5STodor Tomov 	/* Memory */
13894c98a5f5STodor Tomov 
1390414e0a64Sdingsenjie 	vfe->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]);
13914c98a5f5STodor Tomov 	if (IS_ERR(vfe->base)) {
13924c98a5f5STodor Tomov 		dev_err(dev, "could not map memory\n");
13934c98a5f5STodor Tomov 		return PTR_ERR(vfe->base);
13944c98a5f5STodor Tomov 	}
13954c98a5f5STodor Tomov 
13964c98a5f5STodor Tomov 	/* Interrupt */
13974c98a5f5STodor Tomov 
1398b416be3aSLad Prabhakar 	ret = platform_get_irq_byname(pdev, res->interrupt[0]);
1399b416be3aSLad Prabhakar 	if (ret < 0)
1400b416be3aSLad Prabhakar 		return ret;
14014c98a5f5STodor Tomov 
1402b416be3aSLad Prabhakar 	vfe->irq = ret;
14034c98a5f5STodor Tomov 	snprintf(vfe->irq_name, sizeof(vfe->irq_name), "%s_%s%d",
1404ed38a146SJonathan Marek 		 dev_name(dev), MSM_VFE_NAME, id);
1405051a01acSTodor Tomov 	ret = devm_request_irq(dev, vfe->irq, vfe->ops->isr,
14064c98a5f5STodor Tomov 			       IRQF_TRIGGER_RISING, vfe->irq_name, vfe);
14074c98a5f5STodor Tomov 	if (ret < 0) {
14084c98a5f5STodor Tomov 		dev_err(dev, "request_irq failed: %d\n", ret);
14094c98a5f5STodor Tomov 		return ret;
14104c98a5f5STodor Tomov 	}
14114c98a5f5STodor Tomov 
14124c98a5f5STodor Tomov 	/* Clocks */
14134c98a5f5STodor Tomov 
14144c98a5f5STodor Tomov 	vfe->nclocks = 0;
14154c98a5f5STodor Tomov 	while (res->clock[vfe->nclocks])
14164c98a5f5STodor Tomov 		vfe->nclocks++;
14174c98a5f5STodor Tomov 
1418a86854d0SKees Cook 	vfe->clock = devm_kcalloc(dev, vfe->nclocks, sizeof(*vfe->clock),
14194c98a5f5STodor Tomov 				  GFP_KERNEL);
14204c98a5f5STodor Tomov 	if (!vfe->clock)
14214c98a5f5STodor Tomov 		return -ENOMEM;
14224c98a5f5STodor Tomov 
14234c98a5f5STodor Tomov 	for (i = 0; i < vfe->nclocks; i++) {
1424bbde3104STodor Tomov 		struct camss_clock *clock = &vfe->clock[i];
14254c98a5f5STodor Tomov 
1426bbde3104STodor Tomov 		clock->clk = devm_clk_get(dev, res->clock[i]);
1427bbde3104STodor Tomov 		if (IS_ERR(clock->clk))
1428bbde3104STodor Tomov 			return PTR_ERR(clock->clk);
1429bbde3104STodor Tomov 
1430bbde3104STodor Tomov 		clock->name = res->clock[i];
1431bbde3104STodor Tomov 
1432bbde3104STodor Tomov 		clock->nfreqs = 0;
1433bbde3104STodor Tomov 		while (res->clock_rate[i][clock->nfreqs])
1434bbde3104STodor Tomov 			clock->nfreqs++;
1435bbde3104STodor Tomov 
1436bbde3104STodor Tomov 		if (!clock->nfreqs) {
1437bbde3104STodor Tomov 			clock->freq = NULL;
1438bbde3104STodor Tomov 			continue;
14394c98a5f5STodor Tomov 		}
1440bbde3104STodor Tomov 
1441a86854d0SKees Cook 		clock->freq = devm_kcalloc(dev,
1442a86854d0SKees Cook 					   clock->nfreqs,
1443a86854d0SKees Cook 					   sizeof(*clock->freq),
1444a86854d0SKees Cook 					   GFP_KERNEL);
1445bbde3104STodor Tomov 		if (!clock->freq)
1446bbde3104STodor Tomov 			return -ENOMEM;
1447bbde3104STodor Tomov 
1448bbde3104STodor Tomov 		for (j = 0; j < clock->nfreqs; j++)
1449bbde3104STodor Tomov 			clock->freq[j] = res->clock_rate[i][j];
14504c98a5f5STodor Tomov 	}
14514c98a5f5STodor Tomov 
14524c98a5f5STodor Tomov 	mutex_init(&vfe->power_lock);
14534c98a5f5STodor Tomov 	vfe->power_count = 0;
14544c98a5f5STodor Tomov 
14554c98a5f5STodor Tomov 	mutex_init(&vfe->stream_lock);
14564c98a5f5STodor Tomov 	vfe->stream_count = 0;
14574c98a5f5STodor Tomov 
14584c98a5f5STodor Tomov 	spin_lock_init(&vfe->output_lock);
14594c98a5f5STodor Tomov 
14609c3e59deSTodor Tomov 	vfe->camss = camss;
14619c3e59deSTodor Tomov 	vfe->id = id;
14624c98a5f5STodor Tomov 	vfe->reg_update = 0;
14634c98a5f5STodor Tomov 
1464633b388fSRobert Foss 	for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) {
1465cba3819dSTodor Tomov 		struct vfe_line *l = &vfe->line[i];
1466cba3819dSTodor Tomov 
1467cba3819dSTodor Tomov 		l->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1468cba3819dSTodor Tomov 		l->video_out.camss = camss;
1469cba3819dSTodor Tomov 		l->id = i;
1470cba3819dSTodor Tomov 		init_completion(&l->output.sof);
1471cba3819dSTodor Tomov 		init_completion(&l->output.reg_update);
1472cba3819dSTodor Tomov 
14732de3a654SBryan O'Donoghue 		switch (camss->res->version) {
14742de3a654SBryan O'Donoghue 		case CAMSS_8x16:
1475cba3819dSTodor Tomov 			if (i == VFE_LINE_PIX) {
1476cba3819dSTodor Tomov 				l->formats = formats_pix_8x16;
1477cba3819dSTodor Tomov 				l->nformats = ARRAY_SIZE(formats_pix_8x16);
1478cba3819dSTodor Tomov 			} else {
1479cba3819dSTodor Tomov 				l->formats = formats_rdi_8x16;
1480cba3819dSTodor Tomov 				l->nformats = ARRAY_SIZE(formats_rdi_8x16);
1481cba3819dSTodor Tomov 			}
14822de3a654SBryan O'Donoghue 			break;
14832de3a654SBryan O'Donoghue 		case CAMSS_8x96:
14842de3a654SBryan O'Donoghue 		case CAMSS_660:
1485cba3819dSTodor Tomov 			if (i == VFE_LINE_PIX) {
1486cba3819dSTodor Tomov 				l->formats = formats_pix_8x96;
1487cba3819dSTodor Tomov 				l->nformats = ARRAY_SIZE(formats_pix_8x96);
1488cba3819dSTodor Tomov 			} else {
1489cba3819dSTodor Tomov 				l->formats = formats_rdi_8x96;
1490cba3819dSTodor Tomov 				l->nformats = ARRAY_SIZE(formats_rdi_8x96);
1491cba3819dSTodor Tomov 			}
14922de3a654SBryan O'Donoghue 			break;
14932de3a654SBryan O'Donoghue 		case CAMSS_845:
14942de3a654SBryan O'Donoghue 		case CAMSS_8250:
14957319cdf1SRobert Foss 			l->formats = formats_rdi_845;
14967319cdf1SRobert Foss 			l->nformats = ARRAY_SIZE(formats_rdi_845);
14972de3a654SBryan O'Donoghue 			break;
1498cba3819dSTodor Tomov 		}
14994c98a5f5STodor Tomov 	}
15004c98a5f5STodor Tomov 
15014c98a5f5STodor Tomov 	init_completion(&vfe->reset_complete);
15024c98a5f5STodor Tomov 	init_completion(&vfe->halt_complete);
15034c98a5f5STodor Tomov 
15044c98a5f5STodor Tomov 	return 0;
15054c98a5f5STodor Tomov }
15064c98a5f5STodor Tomov 
15074c98a5f5STodor Tomov /*
15084c98a5f5STodor Tomov  * vfe_link_setup - Setup VFE connections
15094c98a5f5STodor Tomov  * @entity: Pointer to media entity structure
15104c98a5f5STodor Tomov  * @local: Pointer to local pad
15114c98a5f5STodor Tomov  * @remote: Pointer to remote pad
15124c98a5f5STodor Tomov  * @flags: Link flags
15134c98a5f5STodor Tomov  *
15144c98a5f5STodor Tomov  * Return 0 on success
15154c98a5f5STodor Tomov  */
15164c98a5f5STodor Tomov static int vfe_link_setup(struct media_entity *entity,
15174c98a5f5STodor Tomov 			  const struct media_pad *local,
15184c98a5f5STodor Tomov 			  const struct media_pad *remote, u32 flags)
15194c98a5f5STodor Tomov {
15204c98a5f5STodor Tomov 	if (flags & MEDIA_LNK_FL_ENABLED)
1521b2e44430SLaurent Pinchart 		if (media_pad_remote_pad_first(local))
15224c98a5f5STodor Tomov 			return -EBUSY;
15234c98a5f5STodor Tomov 
15244c98a5f5STodor Tomov 	return 0;
15254c98a5f5STodor Tomov }
15264c98a5f5STodor Tomov 
15274c98a5f5STodor Tomov static const struct v4l2_subdev_core_ops vfe_core_ops = {
15284c98a5f5STodor Tomov 	.s_power = vfe_set_power,
15294c98a5f5STodor Tomov };
15304c98a5f5STodor Tomov 
15314c98a5f5STodor Tomov static const struct v4l2_subdev_video_ops vfe_video_ops = {
15324c98a5f5STodor Tomov 	.s_stream = vfe_set_stream,
15334c98a5f5STodor Tomov };
15344c98a5f5STodor Tomov 
15354c98a5f5STodor Tomov static const struct v4l2_subdev_pad_ops vfe_pad_ops = {
15364c98a5f5STodor Tomov 	.enum_mbus_code = vfe_enum_mbus_code,
15374c98a5f5STodor Tomov 	.enum_frame_size = vfe_enum_frame_size,
15384c98a5f5STodor Tomov 	.get_fmt = vfe_get_format,
15394c98a5f5STodor Tomov 	.set_fmt = vfe_set_format,
1540810b6598STodor Tomov 	.get_selection = vfe_get_selection,
1541810b6598STodor Tomov 	.set_selection = vfe_set_selection,
15424c98a5f5STodor Tomov };
15434c98a5f5STodor Tomov 
15444c98a5f5STodor Tomov static const struct v4l2_subdev_ops vfe_v4l2_ops = {
15454c98a5f5STodor Tomov 	.core = &vfe_core_ops,
15464c98a5f5STodor Tomov 	.video = &vfe_video_ops,
15474c98a5f5STodor Tomov 	.pad = &vfe_pad_ops,
15484c98a5f5STodor Tomov };
15494c98a5f5STodor Tomov 
15504c98a5f5STodor Tomov static const struct v4l2_subdev_internal_ops vfe_v4l2_internal_ops = {
15514c98a5f5STodor Tomov 	.open = vfe_init_formats,
15524c98a5f5STodor Tomov };
15534c98a5f5STodor Tomov 
15544c98a5f5STodor Tomov static const struct media_entity_operations vfe_media_ops = {
15554c98a5f5STodor Tomov 	.link_setup = vfe_link_setup,
15564c98a5f5STodor Tomov 	.link_validate = v4l2_subdev_link_validate,
15574c98a5f5STodor Tomov };
15584c98a5f5STodor Tomov 
15594c98a5f5STodor Tomov /*
15604c98a5f5STodor Tomov  * msm_vfe_register_entities - Register subdev node for VFE module
15614c98a5f5STodor Tomov  * @vfe: VFE device
15624c98a5f5STodor Tomov  * @v4l2_dev: V4L2 device
15634c98a5f5STodor Tomov  *
15644c98a5f5STodor Tomov  * Initialize and register a subdev node for the VFE module. Then
15654c98a5f5STodor Tomov  * call msm_video_register() to register the video device node which
15664c98a5f5STodor Tomov  * will be connected to this subdev node. Then actually create the
15674c98a5f5STodor Tomov  * media link between them.
15684c98a5f5STodor Tomov  *
15694c98a5f5STodor Tomov  * Return 0 on success or a negative error code otherwise
15704c98a5f5STodor Tomov  */
15714c98a5f5STodor Tomov int msm_vfe_register_entities(struct vfe_device *vfe,
15724c98a5f5STodor Tomov 			      struct v4l2_device *v4l2_dev)
15734c98a5f5STodor Tomov {
15749c3e59deSTodor Tomov 	struct device *dev = vfe->camss->dev;
15754c98a5f5STodor Tomov 	struct v4l2_subdev *sd;
15764c98a5f5STodor Tomov 	struct media_pad *pads;
15774c98a5f5STodor Tomov 	struct camss_video *video_out;
15784c98a5f5STodor Tomov 	int ret;
15794c98a5f5STodor Tomov 	int i;
15804c98a5f5STodor Tomov 
1581633b388fSRobert Foss 	for (i = 0; i < vfe->line_num; i++) {
15824c98a5f5STodor Tomov 		char name[32];
15834c98a5f5STodor Tomov 
15844c98a5f5STodor Tomov 		sd = &vfe->line[i].subdev;
15854c98a5f5STodor Tomov 		pads = vfe->line[i].pads;
15864c98a5f5STodor Tomov 		video_out = &vfe->line[i].video_out;
15874c98a5f5STodor Tomov 
15884c98a5f5STodor Tomov 		v4l2_subdev_init(sd, &vfe_v4l2_ops);
15894c98a5f5STodor Tomov 		sd->internal_ops = &vfe_v4l2_internal_ops;
15904c98a5f5STodor Tomov 		sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
15919b5833f7STodor Tomov 		if (i == VFE_LINE_PIX)
15929b5833f7STodor Tomov 			snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s",
15939b5833f7STodor Tomov 				 MSM_VFE_NAME, vfe->id, "pix");
15949b5833f7STodor Tomov 		else
15954c98a5f5STodor Tomov 			snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s%d",
15964c98a5f5STodor Tomov 				 MSM_VFE_NAME, vfe->id, "rdi", i);
15979b5833f7STodor Tomov 
15984c98a5f5STodor Tomov 		v4l2_set_subdevdata(sd, &vfe->line[i]);
15994c98a5f5STodor Tomov 
16004c98a5f5STodor Tomov 		ret = vfe_init_formats(sd, NULL);
16014c98a5f5STodor Tomov 		if (ret < 0) {
16024c98a5f5STodor Tomov 			dev_err(dev, "Failed to init format: %d\n", ret);
16034c98a5f5STodor Tomov 			goto error_init;
16044c98a5f5STodor Tomov 		}
16054c98a5f5STodor Tomov 
16064c98a5f5STodor Tomov 		pads[MSM_VFE_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
16074c98a5f5STodor Tomov 		pads[MSM_VFE_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
16084c98a5f5STodor Tomov 
16094c98a5f5STodor Tomov 		sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
16104c98a5f5STodor Tomov 		sd->entity.ops = &vfe_media_ops;
16114c98a5f5STodor Tomov 		ret = media_entity_pads_init(&sd->entity, MSM_VFE_PADS_NUM,
16124c98a5f5STodor Tomov 					     pads);
16134c98a5f5STodor Tomov 		if (ret < 0) {
16144c98a5f5STodor Tomov 			dev_err(dev, "Failed to init media entity: %d\n", ret);
16154c98a5f5STodor Tomov 			goto error_init;
16164c98a5f5STodor Tomov 		}
16174c98a5f5STodor Tomov 
16184c98a5f5STodor Tomov 		ret = v4l2_device_register_subdev(v4l2_dev, sd);
16194c98a5f5STodor Tomov 		if (ret < 0) {
16204c98a5f5STodor Tomov 			dev_err(dev, "Failed to register subdev: %d\n", ret);
16214c98a5f5STodor Tomov 			goto error_reg_subdev;
16224c98a5f5STodor Tomov 		}
16234c98a5f5STodor Tomov 
1624633b388fSRobert Foss 		video_out->ops = &vfe->video_ops;
16255900b051SBryan O'Donoghue 		if (vfe->camss->res->version == CAMSS_845 ||
16265900b051SBryan O'Donoghue 		    vfe->camss->res->version == CAMSS_8250)
1627d5b7eb47SAndrey Konovalov 			video_out->bpl_alignment = 16;
1628d5b7eb47SAndrey Konovalov 		else
16297b4aff6fSTodor Tomov 			video_out->bpl_alignment = 8;
16307b4aff6fSTodor Tomov 		video_out->line_based = 0;
16317b4aff6fSTodor Tomov 		if (i == VFE_LINE_PIX) {
16327b4aff6fSTodor Tomov 			video_out->bpl_alignment = 16;
16337b4aff6fSTodor Tomov 			video_out->line_based = 1;
16347b4aff6fSTodor Tomov 		}
16354c98a5f5STodor Tomov 		snprintf(name, ARRAY_SIZE(name), "%s%d_%s%d",
16364c98a5f5STodor Tomov 			 MSM_VFE_NAME, vfe->id, "video", i);
16379b5833f7STodor Tomov 		ret = msm_video_register(video_out, v4l2_dev, name,
16389b5833f7STodor Tomov 					 i == VFE_LINE_PIX ? 1 : 0);
16394c98a5f5STodor Tomov 		if (ret < 0) {
16404c98a5f5STodor Tomov 			dev_err(dev, "Failed to register video node: %d\n",
16414c98a5f5STodor Tomov 				ret);
16424c98a5f5STodor Tomov 			goto error_reg_video;
16434c98a5f5STodor Tomov 		}
16444c98a5f5STodor Tomov 
16454c98a5f5STodor Tomov 		ret = media_create_pad_link(
16464c98a5f5STodor Tomov 				&sd->entity, MSM_VFE_PAD_SRC,
16474c98a5f5STodor Tomov 				&video_out->vdev.entity, 0,
16484c98a5f5STodor Tomov 				MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
16494c98a5f5STodor Tomov 		if (ret < 0) {
16504c98a5f5STodor Tomov 			dev_err(dev, "Failed to link %s->%s entities: %d\n",
16514c98a5f5STodor Tomov 				sd->entity.name, video_out->vdev.entity.name,
16524c98a5f5STodor Tomov 				ret);
16534c98a5f5STodor Tomov 			goto error_link;
16544c98a5f5STodor Tomov 		}
16554c98a5f5STodor Tomov 	}
16564c98a5f5STodor Tomov 
16574c98a5f5STodor Tomov 	return 0;
16584c98a5f5STodor Tomov 
16594c98a5f5STodor Tomov error_link:
16604c98a5f5STodor Tomov 	msm_video_unregister(video_out);
16614c98a5f5STodor Tomov 
16624c98a5f5STodor Tomov error_reg_video:
16634c98a5f5STodor Tomov 	v4l2_device_unregister_subdev(sd);
16644c98a5f5STodor Tomov 
16654c98a5f5STodor Tomov error_reg_subdev:
16664c98a5f5STodor Tomov 	media_entity_cleanup(&sd->entity);
16674c98a5f5STodor Tomov 
16684c98a5f5STodor Tomov error_init:
16694c98a5f5STodor Tomov 	for (i--; i >= 0; i--) {
16704c98a5f5STodor Tomov 		sd = &vfe->line[i].subdev;
16714c98a5f5STodor Tomov 		video_out = &vfe->line[i].video_out;
16724c98a5f5STodor Tomov 
16734c98a5f5STodor Tomov 		msm_video_unregister(video_out);
16744c98a5f5STodor Tomov 		v4l2_device_unregister_subdev(sd);
16754c98a5f5STodor Tomov 		media_entity_cleanup(&sd->entity);
16764c98a5f5STodor Tomov 	}
16774c98a5f5STodor Tomov 
16784c98a5f5STodor Tomov 	return ret;
16794c98a5f5STodor Tomov }
16804c98a5f5STodor Tomov 
16814c98a5f5STodor Tomov /*
16824c98a5f5STodor Tomov  * msm_vfe_unregister_entities - Unregister VFE module subdev node
16834c98a5f5STodor Tomov  * @vfe: VFE device
16844c98a5f5STodor Tomov  */
16854c98a5f5STodor Tomov void msm_vfe_unregister_entities(struct vfe_device *vfe)
16864c98a5f5STodor Tomov {
16874c98a5f5STodor Tomov 	int i;
16884c98a5f5STodor Tomov 
16894c98a5f5STodor Tomov 	mutex_destroy(&vfe->power_lock);
16904c98a5f5STodor Tomov 	mutex_destroy(&vfe->stream_lock);
16914c98a5f5STodor Tomov 
1692633b388fSRobert Foss 	for (i = 0; i < vfe->line_num; i++) {
16934c98a5f5STodor Tomov 		struct v4l2_subdev *sd = &vfe->line[i].subdev;
16944c98a5f5STodor Tomov 		struct camss_video *video_out = &vfe->line[i].video_out;
16954c98a5f5STodor Tomov 
16964c98a5f5STodor Tomov 		msm_video_unregister(video_out);
16974c98a5f5STodor Tomov 		v4l2_device_unregister_subdev(sd);
16984c98a5f5STodor Tomov 		media_entity_cleanup(&sd->entity);
16994c98a5f5STodor Tomov 	}
17004c98a5f5STodor Tomov }
1701