18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * camss-vfe.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
88c2ecf20Sopenharmony_ci * Copyright (C) 2015-2018 Linaro Ltd.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci#include <linux/clk.h>
118c2ecf20Sopenharmony_ci#include <linux/completion.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/iommu.h>
148c2ecf20Sopenharmony_ci#include <linux/mutex.h>
158c2ecf20Sopenharmony_ci#include <linux/of.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
188c2ecf20Sopenharmony_ci#include <linux/spinlock_types.h>
198c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
208c2ecf20Sopenharmony_ci#include <media/media-entity.h>
218c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
228c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "camss-vfe.h"
258c2ecf20Sopenharmony_ci#include "camss.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define MSM_VFE_NAME "msm_vfe"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define vfe_line_array(ptr_line)	\
308c2ecf20Sopenharmony_ci	((const struct vfe_line (*)[]) &(ptr_line[-(ptr_line->id)]))
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define to_vfe(ptr_line)	\
338c2ecf20Sopenharmony_ci	container_of(vfe_line_array(ptr_line), struct vfe_device, line)
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* VFE reset timeout */
368c2ecf20Sopenharmony_ci#define VFE_RESET_TIMEOUT_MS 50
378c2ecf20Sopenharmony_ci/* VFE halt timeout */
388c2ecf20Sopenharmony_ci#define VFE_HALT_TIMEOUT_MS 100
398c2ecf20Sopenharmony_ci/* Max number of frame drop updates per frame */
408c2ecf20Sopenharmony_ci#define VFE_FRAME_DROP_UPDATES 2
418c2ecf20Sopenharmony_ci/* Frame drop value. VAL + UPDATES - 1 should not exceed 31 */
428c2ecf20Sopenharmony_ci#define VFE_FRAME_DROP_VAL 30
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define VFE_NEXT_SOF_MS 500
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define SCALER_RATIO_MAX 16
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistruct vfe_format {
498c2ecf20Sopenharmony_ci	u32 code;
508c2ecf20Sopenharmony_ci	u8 bpp;
518c2ecf20Sopenharmony_ci};
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic const struct vfe_format formats_rdi_8x16[] = {
548c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_UYVY8_2X8, 8 },
558c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_VYUY8_2X8, 8 },
568c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_YUYV8_2X8, 8 },
578c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_YVYU8_2X8, 8 },
588c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SBGGR8_1X8, 8 },
598c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SGBRG8_1X8, 8 },
608c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SGRBG8_1X8, 8 },
618c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SRGGB8_1X8, 8 },
628c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
638c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
648c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
658c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
668c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SBGGR12_1X12, 12 },
678c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SGBRG12_1X12, 12 },
688c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SGRBG12_1X12, 12 },
698c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
708c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_Y10_1X10, 10 },
718c2ecf20Sopenharmony_ci};
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic const struct vfe_format formats_pix_8x16[] = {
748c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_UYVY8_2X8, 8 },
758c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_VYUY8_2X8, 8 },
768c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_YUYV8_2X8, 8 },
778c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_YVYU8_2X8, 8 },
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic const struct vfe_format formats_rdi_8x96[] = {
818c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_UYVY8_2X8, 8 },
828c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_VYUY8_2X8, 8 },
838c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_YUYV8_2X8, 8 },
848c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_YVYU8_2X8, 8 },
858c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SBGGR8_1X8, 8 },
868c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SGBRG8_1X8, 8 },
878c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SGRBG8_1X8, 8 },
888c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SRGGB8_1X8, 8 },
898c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
908c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
918c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
928c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
938c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16 },
948c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SBGGR12_1X12, 12 },
958c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SGBRG12_1X12, 12 },
968c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SGRBG12_1X12, 12 },
978c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
988c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SBGGR14_1X14, 14 },
998c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SGBRG14_1X14, 14 },
1008c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SGRBG14_1X14, 14 },
1018c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_SRGGB14_1X14, 14 },
1028c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_Y10_1X10, 10 },
1038c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 },
1048c2ecf20Sopenharmony_ci};
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic const struct vfe_format formats_pix_8x96[] = {
1078c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_UYVY8_2X8, 8 },
1088c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_VYUY8_2X8, 8 },
1098c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_YUYV8_2X8, 8 },
1108c2ecf20Sopenharmony_ci	{ MEDIA_BUS_FMT_YVYU8_2X8, 8 },
1118c2ecf20Sopenharmony_ci};
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/*
1148c2ecf20Sopenharmony_ci * vfe_get_bpp - map media bus format to bits per pixel
1158c2ecf20Sopenharmony_ci * @formats: supported media bus formats array
1168c2ecf20Sopenharmony_ci * @nformats: size of @formats array
1178c2ecf20Sopenharmony_ci * @code: media bus format code
1188c2ecf20Sopenharmony_ci *
1198c2ecf20Sopenharmony_ci * Return number of bits per pixel
1208c2ecf20Sopenharmony_ci */
1218c2ecf20Sopenharmony_cistatic u8 vfe_get_bpp(const struct vfe_format *formats,
1228c2ecf20Sopenharmony_ci		      unsigned int nformats, u32 code)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	unsigned int i;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	for (i = 0; i < nformats; i++)
1278c2ecf20Sopenharmony_ci		if (code == formats[i].code)
1288c2ecf20Sopenharmony_ci			return formats[i].bpp;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	WARN(1, "Unknown format\n");
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	return formats[0].bpp;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic u32 vfe_find_code(u32 *code, unsigned int n_code,
1368c2ecf20Sopenharmony_ci			 unsigned int index, u32 req_code)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	int i;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	if (!req_code && (index >= n_code))
1418c2ecf20Sopenharmony_ci		return 0;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	for (i = 0; i < n_code; i++)
1448c2ecf20Sopenharmony_ci		if (req_code) {
1458c2ecf20Sopenharmony_ci			if (req_code == code[i])
1468c2ecf20Sopenharmony_ci				return req_code;
1478c2ecf20Sopenharmony_ci		} else {
1488c2ecf20Sopenharmony_ci			if (i == index)
1498c2ecf20Sopenharmony_ci				return code[i];
1508c2ecf20Sopenharmony_ci		}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	return code[0];
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
1568c2ecf20Sopenharmony_ci			    unsigned int index, u32 src_req_code)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	if (vfe->camss->version == CAMSS_8x16)
1618c2ecf20Sopenharmony_ci		switch (sink_code) {
1628c2ecf20Sopenharmony_ci		case MEDIA_BUS_FMT_YUYV8_2X8:
1638c2ecf20Sopenharmony_ci		{
1648c2ecf20Sopenharmony_ci			u32 src_code[] = {
1658c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_YUYV8_2X8,
1668c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_YUYV8_1_5X8,
1678c2ecf20Sopenharmony_ci			};
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
1708c2ecf20Sopenharmony_ci					     index, src_req_code);
1718c2ecf20Sopenharmony_ci		}
1728c2ecf20Sopenharmony_ci		case MEDIA_BUS_FMT_YVYU8_2X8:
1738c2ecf20Sopenharmony_ci		{
1748c2ecf20Sopenharmony_ci			u32 src_code[] = {
1758c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_YVYU8_2X8,
1768c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_YVYU8_1_5X8,
1778c2ecf20Sopenharmony_ci			};
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
1808c2ecf20Sopenharmony_ci					     index, src_req_code);
1818c2ecf20Sopenharmony_ci		}
1828c2ecf20Sopenharmony_ci		case MEDIA_BUS_FMT_UYVY8_2X8:
1838c2ecf20Sopenharmony_ci		{
1848c2ecf20Sopenharmony_ci			u32 src_code[] = {
1858c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_UYVY8_2X8,
1868c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_UYVY8_1_5X8,
1878c2ecf20Sopenharmony_ci			};
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
1908c2ecf20Sopenharmony_ci					     index, src_req_code);
1918c2ecf20Sopenharmony_ci		}
1928c2ecf20Sopenharmony_ci		case MEDIA_BUS_FMT_VYUY8_2X8:
1938c2ecf20Sopenharmony_ci		{
1948c2ecf20Sopenharmony_ci			u32 src_code[] = {
1958c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_VYUY8_2X8,
1968c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_VYUY8_1_5X8,
1978c2ecf20Sopenharmony_ci			};
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
2008c2ecf20Sopenharmony_ci					     index, src_req_code);
2018c2ecf20Sopenharmony_ci		}
2028c2ecf20Sopenharmony_ci		default:
2038c2ecf20Sopenharmony_ci			if (index > 0)
2048c2ecf20Sopenharmony_ci				return 0;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci			return sink_code;
2078c2ecf20Sopenharmony_ci		}
2088c2ecf20Sopenharmony_ci	else if (vfe->camss->version == CAMSS_8x96)
2098c2ecf20Sopenharmony_ci		switch (sink_code) {
2108c2ecf20Sopenharmony_ci		case MEDIA_BUS_FMT_YUYV8_2X8:
2118c2ecf20Sopenharmony_ci		{
2128c2ecf20Sopenharmony_ci			u32 src_code[] = {
2138c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_YUYV8_2X8,
2148c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_YVYU8_2X8,
2158c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_UYVY8_2X8,
2168c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_VYUY8_2X8,
2178c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_YUYV8_1_5X8,
2188c2ecf20Sopenharmony_ci			};
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
2218c2ecf20Sopenharmony_ci					     index, src_req_code);
2228c2ecf20Sopenharmony_ci		}
2238c2ecf20Sopenharmony_ci		case MEDIA_BUS_FMT_YVYU8_2X8:
2248c2ecf20Sopenharmony_ci		{
2258c2ecf20Sopenharmony_ci			u32 src_code[] = {
2268c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_YVYU8_2X8,
2278c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_YUYV8_2X8,
2288c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_UYVY8_2X8,
2298c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_VYUY8_2X8,
2308c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_YVYU8_1_5X8,
2318c2ecf20Sopenharmony_ci			};
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
2348c2ecf20Sopenharmony_ci					     index, src_req_code);
2358c2ecf20Sopenharmony_ci		}
2368c2ecf20Sopenharmony_ci		case MEDIA_BUS_FMT_UYVY8_2X8:
2378c2ecf20Sopenharmony_ci		{
2388c2ecf20Sopenharmony_ci			u32 src_code[] = {
2398c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_UYVY8_2X8,
2408c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_YUYV8_2X8,
2418c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_YVYU8_2X8,
2428c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_VYUY8_2X8,
2438c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_UYVY8_1_5X8,
2448c2ecf20Sopenharmony_ci			};
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
2478c2ecf20Sopenharmony_ci					     index, src_req_code);
2488c2ecf20Sopenharmony_ci		}
2498c2ecf20Sopenharmony_ci		case MEDIA_BUS_FMT_VYUY8_2X8:
2508c2ecf20Sopenharmony_ci		{
2518c2ecf20Sopenharmony_ci			u32 src_code[] = {
2528c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_VYUY8_2X8,
2538c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_YUYV8_2X8,
2548c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_YVYU8_2X8,
2558c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_UYVY8_2X8,
2568c2ecf20Sopenharmony_ci				MEDIA_BUS_FMT_VYUY8_1_5X8,
2578c2ecf20Sopenharmony_ci			};
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
2608c2ecf20Sopenharmony_ci					     index, src_req_code);
2618c2ecf20Sopenharmony_ci		}
2628c2ecf20Sopenharmony_ci		default:
2638c2ecf20Sopenharmony_ci			if (index > 0)
2648c2ecf20Sopenharmony_ci				return 0;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci			return sink_code;
2678c2ecf20Sopenharmony_ci		}
2688c2ecf20Sopenharmony_ci	else
2698c2ecf20Sopenharmony_ci		return 0;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci/*
2738c2ecf20Sopenharmony_ci * vfe_reset - Trigger reset on VFE module and wait to complete
2748c2ecf20Sopenharmony_ci * @vfe: VFE device
2758c2ecf20Sopenharmony_ci *
2768c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise
2778c2ecf20Sopenharmony_ci */
2788c2ecf20Sopenharmony_cistatic int vfe_reset(struct vfe_device *vfe)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	unsigned long time;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	reinit_completion(&vfe->reset_complete);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	vfe->ops->global_reset(vfe);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	time = wait_for_completion_timeout(&vfe->reset_complete,
2878c2ecf20Sopenharmony_ci		msecs_to_jiffies(VFE_RESET_TIMEOUT_MS));
2888c2ecf20Sopenharmony_ci	if (!time) {
2898c2ecf20Sopenharmony_ci		dev_err(vfe->camss->dev, "VFE reset timeout\n");
2908c2ecf20Sopenharmony_ci		return -EIO;
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	return 0;
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci/*
2978c2ecf20Sopenharmony_ci * vfe_halt - Trigger halt on VFE module and wait to complete
2988c2ecf20Sopenharmony_ci * @vfe: VFE device
2998c2ecf20Sopenharmony_ci *
3008c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise
3018c2ecf20Sopenharmony_ci */
3028c2ecf20Sopenharmony_cistatic int vfe_halt(struct vfe_device *vfe)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	unsigned long time;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	reinit_completion(&vfe->halt_complete);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	vfe->ops->halt_request(vfe);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	time = wait_for_completion_timeout(&vfe->halt_complete,
3118c2ecf20Sopenharmony_ci		msecs_to_jiffies(VFE_HALT_TIMEOUT_MS));
3128c2ecf20Sopenharmony_ci	if (!time) {
3138c2ecf20Sopenharmony_ci		dev_err(vfe->camss->dev, "VFE halt timeout\n");
3148c2ecf20Sopenharmony_ci		return -EIO;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	return 0;
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic void vfe_init_outputs(struct vfe_device *vfe)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	int i;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vfe->line); i++) {
3258c2ecf20Sopenharmony_ci		struct vfe_output *output = &vfe->line[i].output;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci		output->state = VFE_OUTPUT_OFF;
3288c2ecf20Sopenharmony_ci		output->buf[0] = NULL;
3298c2ecf20Sopenharmony_ci		output->buf[1] = NULL;
3308c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&output->pending_bufs);
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cistatic void vfe_reset_output_maps(struct vfe_device *vfe)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	int i;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++)
3398c2ecf20Sopenharmony_ci		vfe->wm_output_map[i] = VFE_LINE_NONE;
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic void vfe_output_init_addrs(struct vfe_device *vfe,
3438c2ecf20Sopenharmony_ci				  struct vfe_output *output, u8 sync)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	u32 ping_addr;
3468c2ecf20Sopenharmony_ci	u32 pong_addr;
3478c2ecf20Sopenharmony_ci	unsigned int i;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	output->active_buf = 0;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	for (i = 0; i < output->wm_num; i++) {
3528c2ecf20Sopenharmony_ci		if (output->buf[0])
3538c2ecf20Sopenharmony_ci			ping_addr = output->buf[0]->addr[i];
3548c2ecf20Sopenharmony_ci		else
3558c2ecf20Sopenharmony_ci			ping_addr = 0;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci		if (output->buf[1])
3588c2ecf20Sopenharmony_ci			pong_addr = output->buf[1]->addr[i];
3598c2ecf20Sopenharmony_ci		else
3608c2ecf20Sopenharmony_ci			pong_addr = ping_addr;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci		vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i], ping_addr);
3638c2ecf20Sopenharmony_ci		vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i], pong_addr);
3648c2ecf20Sopenharmony_ci		if (sync)
3658c2ecf20Sopenharmony_ci			vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]);
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic void vfe_output_update_ping_addr(struct vfe_device *vfe,
3708c2ecf20Sopenharmony_ci					struct vfe_output *output, u8 sync)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	u32 addr;
3738c2ecf20Sopenharmony_ci	unsigned int i;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	for (i = 0; i < output->wm_num; i++) {
3768c2ecf20Sopenharmony_ci		if (output->buf[0])
3778c2ecf20Sopenharmony_ci			addr = output->buf[0]->addr[i];
3788c2ecf20Sopenharmony_ci		else
3798c2ecf20Sopenharmony_ci			addr = 0;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci		vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i], addr);
3828c2ecf20Sopenharmony_ci		if (sync)
3838c2ecf20Sopenharmony_ci			vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]);
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic void vfe_output_update_pong_addr(struct vfe_device *vfe,
3888c2ecf20Sopenharmony_ci					struct vfe_output *output, u8 sync)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	u32 addr;
3918c2ecf20Sopenharmony_ci	unsigned int i;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	for (i = 0; i < output->wm_num; i++) {
3948c2ecf20Sopenharmony_ci		if (output->buf[1])
3958c2ecf20Sopenharmony_ci			addr = output->buf[1]->addr[i];
3968c2ecf20Sopenharmony_ci		else
3978c2ecf20Sopenharmony_ci			addr = 0;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci		vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i], addr);
4008c2ecf20Sopenharmony_ci		if (sync)
4018c2ecf20Sopenharmony_ci			vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]);
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic int vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	int ret = -EBUSY;
4098c2ecf20Sopenharmony_ci	int i;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++) {
4128c2ecf20Sopenharmony_ci		if (vfe->wm_output_map[i] == VFE_LINE_NONE) {
4138c2ecf20Sopenharmony_ci			vfe->wm_output_map[i] = line_id;
4148c2ecf20Sopenharmony_ci			ret = i;
4158c2ecf20Sopenharmony_ci			break;
4168c2ecf20Sopenharmony_ci		}
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	return ret;
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic int vfe_release_wm(struct vfe_device *vfe, u8 wm)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	if (wm >= ARRAY_SIZE(vfe->wm_output_map))
4258c2ecf20Sopenharmony_ci		return -EINVAL;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	vfe->wm_output_map[wm] = VFE_LINE_NONE;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	return 0;
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_cistatic void vfe_output_frame_drop(struct vfe_device *vfe,
4338c2ecf20Sopenharmony_ci				  struct vfe_output *output,
4348c2ecf20Sopenharmony_ci				  u32 drop_pattern)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	u8 drop_period;
4378c2ecf20Sopenharmony_ci	unsigned int i;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	/* We need to toggle update period to be valid on next frame */
4408c2ecf20Sopenharmony_ci	output->drop_update_idx++;
4418c2ecf20Sopenharmony_ci	output->drop_update_idx %= VFE_FRAME_DROP_UPDATES;
4428c2ecf20Sopenharmony_ci	drop_period = VFE_FRAME_DROP_VAL + output->drop_update_idx;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	for (i = 0; i < output->wm_num; i++) {
4458c2ecf20Sopenharmony_ci		vfe->ops->wm_set_framedrop_period(vfe, output->wm_idx[i],
4468c2ecf20Sopenharmony_ci						  drop_period);
4478c2ecf20Sopenharmony_ci		vfe->ops->wm_set_framedrop_pattern(vfe, output->wm_idx[i],
4488c2ecf20Sopenharmony_ci						   drop_pattern);
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci	vfe->ops->reg_update(vfe,
4518c2ecf20Sopenharmony_ci			     container_of(output, struct vfe_line, output)->id);
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_cistatic struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	struct camss_buffer *buffer = NULL;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	if (!list_empty(&output->pending_bufs)) {
4598c2ecf20Sopenharmony_ci		buffer = list_first_entry(&output->pending_bufs,
4608c2ecf20Sopenharmony_ci					  struct camss_buffer,
4618c2ecf20Sopenharmony_ci					  queue);
4628c2ecf20Sopenharmony_ci		list_del(&buffer->queue);
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	return buffer;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci/*
4698c2ecf20Sopenharmony_ci * vfe_buf_add_pending - Add output buffer to list of pending
4708c2ecf20Sopenharmony_ci * @output: VFE output
4718c2ecf20Sopenharmony_ci * @buffer: Video buffer
4728c2ecf20Sopenharmony_ci */
4738c2ecf20Sopenharmony_cistatic void vfe_buf_add_pending(struct vfe_output *output,
4748c2ecf20Sopenharmony_ci				struct camss_buffer *buffer)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&buffer->queue);
4778c2ecf20Sopenharmony_ci	list_add_tail(&buffer->queue, &output->pending_bufs);
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci/*
4818c2ecf20Sopenharmony_ci * vfe_buf_flush_pending - Flush all pending buffers.
4828c2ecf20Sopenharmony_ci * @output: VFE output
4838c2ecf20Sopenharmony_ci * @state: vb2 buffer state
4848c2ecf20Sopenharmony_ci */
4858c2ecf20Sopenharmony_cistatic void vfe_buf_flush_pending(struct vfe_output *output,
4868c2ecf20Sopenharmony_ci				  enum vb2_buffer_state state)
4878c2ecf20Sopenharmony_ci{
4888c2ecf20Sopenharmony_ci	struct camss_buffer *buf;
4898c2ecf20Sopenharmony_ci	struct camss_buffer *t;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	list_for_each_entry_safe(buf, t, &output->pending_bufs, queue) {
4928c2ecf20Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, state);
4938c2ecf20Sopenharmony_ci		list_del(&buf->queue);
4948c2ecf20Sopenharmony_ci	}
4958c2ecf20Sopenharmony_ci}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_cistatic void vfe_buf_update_wm_on_next(struct vfe_device *vfe,
4988c2ecf20Sopenharmony_ci				      struct vfe_output *output)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	switch (output->state) {
5018c2ecf20Sopenharmony_ci	case VFE_OUTPUT_CONTINUOUS:
5028c2ecf20Sopenharmony_ci		vfe_output_frame_drop(vfe, output, 3);
5038c2ecf20Sopenharmony_ci		break;
5048c2ecf20Sopenharmony_ci	case VFE_OUTPUT_SINGLE:
5058c2ecf20Sopenharmony_ci	default:
5068c2ecf20Sopenharmony_ci		dev_err_ratelimited(vfe->camss->dev,
5078c2ecf20Sopenharmony_ci				    "Next buf in wrong state! %d\n",
5088c2ecf20Sopenharmony_ci				    output->state);
5098c2ecf20Sopenharmony_ci		break;
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_cistatic void vfe_buf_update_wm_on_last(struct vfe_device *vfe,
5148c2ecf20Sopenharmony_ci				      struct vfe_output *output)
5158c2ecf20Sopenharmony_ci{
5168c2ecf20Sopenharmony_ci	switch (output->state) {
5178c2ecf20Sopenharmony_ci	case VFE_OUTPUT_CONTINUOUS:
5188c2ecf20Sopenharmony_ci		output->state = VFE_OUTPUT_SINGLE;
5198c2ecf20Sopenharmony_ci		vfe_output_frame_drop(vfe, output, 1);
5208c2ecf20Sopenharmony_ci		break;
5218c2ecf20Sopenharmony_ci	case VFE_OUTPUT_SINGLE:
5228c2ecf20Sopenharmony_ci		output->state = VFE_OUTPUT_STOPPING;
5238c2ecf20Sopenharmony_ci		vfe_output_frame_drop(vfe, output, 0);
5248c2ecf20Sopenharmony_ci		break;
5258c2ecf20Sopenharmony_ci	default:
5268c2ecf20Sopenharmony_ci		dev_err_ratelimited(vfe->camss->dev,
5278c2ecf20Sopenharmony_ci				    "Last buff in wrong state! %d\n",
5288c2ecf20Sopenharmony_ci				    output->state);
5298c2ecf20Sopenharmony_ci		break;
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_cistatic void vfe_buf_update_wm_on_new(struct vfe_device *vfe,
5348c2ecf20Sopenharmony_ci				     struct vfe_output *output,
5358c2ecf20Sopenharmony_ci				     struct camss_buffer *new_buf)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	int inactive_idx;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	switch (output->state) {
5408c2ecf20Sopenharmony_ci	case VFE_OUTPUT_SINGLE:
5418c2ecf20Sopenharmony_ci		inactive_idx = !output->active_buf;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci		if (!output->buf[inactive_idx]) {
5448c2ecf20Sopenharmony_ci			output->buf[inactive_idx] = new_buf;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci			if (inactive_idx)
5478c2ecf20Sopenharmony_ci				vfe_output_update_pong_addr(vfe, output, 0);
5488c2ecf20Sopenharmony_ci			else
5498c2ecf20Sopenharmony_ci				vfe_output_update_ping_addr(vfe, output, 0);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci			vfe_output_frame_drop(vfe, output, 3);
5528c2ecf20Sopenharmony_ci			output->state = VFE_OUTPUT_CONTINUOUS;
5538c2ecf20Sopenharmony_ci		} else {
5548c2ecf20Sopenharmony_ci			vfe_buf_add_pending(output, new_buf);
5558c2ecf20Sopenharmony_ci			dev_err_ratelimited(vfe->camss->dev,
5568c2ecf20Sopenharmony_ci					    "Inactive buffer is busy\n");
5578c2ecf20Sopenharmony_ci		}
5588c2ecf20Sopenharmony_ci		break;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	case VFE_OUTPUT_IDLE:
5618c2ecf20Sopenharmony_ci		if (!output->buf[0]) {
5628c2ecf20Sopenharmony_ci			output->buf[0] = new_buf;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci			vfe_output_init_addrs(vfe, output, 1);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci			vfe_output_frame_drop(vfe, output, 1);
5678c2ecf20Sopenharmony_ci			output->state = VFE_OUTPUT_SINGLE;
5688c2ecf20Sopenharmony_ci		} else {
5698c2ecf20Sopenharmony_ci			vfe_buf_add_pending(output, new_buf);
5708c2ecf20Sopenharmony_ci			dev_err_ratelimited(vfe->camss->dev,
5718c2ecf20Sopenharmony_ci					    "Output idle with buffer set!\n");
5728c2ecf20Sopenharmony_ci		}
5738c2ecf20Sopenharmony_ci		break;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	case VFE_OUTPUT_CONTINUOUS:
5768c2ecf20Sopenharmony_ci	default:
5778c2ecf20Sopenharmony_ci		vfe_buf_add_pending(output, new_buf);
5788c2ecf20Sopenharmony_ci		break;
5798c2ecf20Sopenharmony_ci	}
5808c2ecf20Sopenharmony_ci}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_cistatic int vfe_get_output(struct vfe_line *line)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
5858c2ecf20Sopenharmony_ci	struct vfe_output *output;
5868c2ecf20Sopenharmony_ci	struct v4l2_format *f = &line->video_out.active_fmt;
5878c2ecf20Sopenharmony_ci	unsigned long flags;
5888c2ecf20Sopenharmony_ci	int i;
5898c2ecf20Sopenharmony_ci	int wm_idx;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vfe->output_lock, flags);
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	output = &line->output;
5948c2ecf20Sopenharmony_ci	if (output->state != VFE_OUTPUT_OFF) {
5958c2ecf20Sopenharmony_ci		dev_err(vfe->camss->dev, "Output is running\n");
5968c2ecf20Sopenharmony_ci		goto error;
5978c2ecf20Sopenharmony_ci	}
5988c2ecf20Sopenharmony_ci	output->state = VFE_OUTPUT_RESERVED;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	output->active_buf = 0;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	switch (f->fmt.pix_mp.pixelformat) {
6038c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_NV12:
6048c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_NV21:
6058c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_NV16:
6068c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_NV61:
6078c2ecf20Sopenharmony_ci		output->wm_num = 2;
6088c2ecf20Sopenharmony_ci		break;
6098c2ecf20Sopenharmony_ci	default:
6108c2ecf20Sopenharmony_ci		output->wm_num = 1;
6118c2ecf20Sopenharmony_ci		break;
6128c2ecf20Sopenharmony_ci	}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	for (i = 0; i < output->wm_num; i++) {
6158c2ecf20Sopenharmony_ci		wm_idx = vfe_reserve_wm(vfe, line->id);
6168c2ecf20Sopenharmony_ci		if (wm_idx < 0) {
6178c2ecf20Sopenharmony_ci			dev_err(vfe->camss->dev, "Can not reserve wm\n");
6188c2ecf20Sopenharmony_ci			goto error_get_wm;
6198c2ecf20Sopenharmony_ci		}
6208c2ecf20Sopenharmony_ci		output->wm_idx[i] = wm_idx;
6218c2ecf20Sopenharmony_ci	}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	output->drop_update_idx = 0;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	return 0;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_cierror_get_wm:
6308c2ecf20Sopenharmony_ci	for (i--; i >= 0; i--)
6318c2ecf20Sopenharmony_ci		vfe_release_wm(vfe, output->wm_idx[i]);
6328c2ecf20Sopenharmony_ci	output->state = VFE_OUTPUT_OFF;
6338c2ecf20Sopenharmony_cierror:
6348c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	return -EINVAL;
6378c2ecf20Sopenharmony_ci}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_cistatic int vfe_put_output(struct vfe_line *line)
6408c2ecf20Sopenharmony_ci{
6418c2ecf20Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
6428c2ecf20Sopenharmony_ci	struct vfe_output *output = &line->output;
6438c2ecf20Sopenharmony_ci	unsigned long flags;
6448c2ecf20Sopenharmony_ci	unsigned int i;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vfe->output_lock, flags);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	for (i = 0; i < output->wm_num; i++)
6498c2ecf20Sopenharmony_ci		vfe_release_wm(vfe, output->wm_idx[i]);
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	output->state = VFE_OUTPUT_OFF;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
6548c2ecf20Sopenharmony_ci	return 0;
6558c2ecf20Sopenharmony_ci}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_cistatic int vfe_enable_output(struct vfe_line *line)
6588c2ecf20Sopenharmony_ci{
6598c2ecf20Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
6608c2ecf20Sopenharmony_ci	struct vfe_output *output = &line->output;
6618c2ecf20Sopenharmony_ci	const struct vfe_hw_ops *ops = vfe->ops;
6628c2ecf20Sopenharmony_ci	struct media_entity *sensor;
6638c2ecf20Sopenharmony_ci	unsigned long flags;
6648c2ecf20Sopenharmony_ci	unsigned int frame_skip = 0;
6658c2ecf20Sopenharmony_ci	unsigned int i;
6668c2ecf20Sopenharmony_ci	u16 ub_size;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	ub_size = ops->get_ub_size(vfe->id);
6698c2ecf20Sopenharmony_ci	if (!ub_size)
6708c2ecf20Sopenharmony_ci		return -EINVAL;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	sensor = camss_find_sensor(&line->subdev.entity);
6738c2ecf20Sopenharmony_ci	if (sensor) {
6748c2ecf20Sopenharmony_ci		struct v4l2_subdev *subdev =
6758c2ecf20Sopenharmony_ci					media_entity_to_v4l2_subdev(sensor);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci		v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip);
6788c2ecf20Sopenharmony_ci		/* Max frame skip is 29 frames */
6798c2ecf20Sopenharmony_ci		if (frame_skip > VFE_FRAME_DROP_VAL - 1)
6808c2ecf20Sopenharmony_ci			frame_skip = VFE_FRAME_DROP_VAL - 1;
6818c2ecf20Sopenharmony_ci	}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vfe->output_lock, flags);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	ops->reg_update_clear(vfe, line->id);
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	if (output->state != VFE_OUTPUT_RESERVED) {
6888c2ecf20Sopenharmony_ci		dev_err(vfe->camss->dev, "Output is not in reserved state %d\n",
6898c2ecf20Sopenharmony_ci			output->state);
6908c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&vfe->output_lock, flags);
6918c2ecf20Sopenharmony_ci		return -EINVAL;
6928c2ecf20Sopenharmony_ci	}
6938c2ecf20Sopenharmony_ci	output->state = VFE_OUTPUT_IDLE;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	output->buf[0] = vfe_buf_get_pending(output);
6968c2ecf20Sopenharmony_ci	output->buf[1] = vfe_buf_get_pending(output);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	if (!output->buf[0] && output->buf[1]) {
6998c2ecf20Sopenharmony_ci		output->buf[0] = output->buf[1];
7008c2ecf20Sopenharmony_ci		output->buf[1] = NULL;
7018c2ecf20Sopenharmony_ci	}
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	if (output->buf[0])
7048c2ecf20Sopenharmony_ci		output->state = VFE_OUTPUT_SINGLE;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	if (output->buf[1])
7078c2ecf20Sopenharmony_ci		output->state = VFE_OUTPUT_CONTINUOUS;
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	switch (output->state) {
7108c2ecf20Sopenharmony_ci	case VFE_OUTPUT_SINGLE:
7118c2ecf20Sopenharmony_ci		vfe_output_frame_drop(vfe, output, 1 << frame_skip);
7128c2ecf20Sopenharmony_ci		break;
7138c2ecf20Sopenharmony_ci	case VFE_OUTPUT_CONTINUOUS:
7148c2ecf20Sopenharmony_ci		vfe_output_frame_drop(vfe, output, 3 << frame_skip);
7158c2ecf20Sopenharmony_ci		break;
7168c2ecf20Sopenharmony_ci	default:
7178c2ecf20Sopenharmony_ci		vfe_output_frame_drop(vfe, output, 0);
7188c2ecf20Sopenharmony_ci		break;
7198c2ecf20Sopenharmony_ci	}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	output->sequence = 0;
7228c2ecf20Sopenharmony_ci	output->wait_sof = 0;
7238c2ecf20Sopenharmony_ci	output->wait_reg_update = 0;
7248c2ecf20Sopenharmony_ci	reinit_completion(&output->sof);
7258c2ecf20Sopenharmony_ci	reinit_completion(&output->reg_update);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	vfe_output_init_addrs(vfe, output, 0);
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	if (line->id != VFE_LINE_PIX) {
7308c2ecf20Sopenharmony_ci		ops->set_cgc_override(vfe, output->wm_idx[0], 1);
7318c2ecf20Sopenharmony_ci		ops->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 1);
7328c2ecf20Sopenharmony_ci		ops->bus_connect_wm_to_rdi(vfe, output->wm_idx[0], line->id);
7338c2ecf20Sopenharmony_ci		ops->wm_set_subsample(vfe, output->wm_idx[0]);
7348c2ecf20Sopenharmony_ci		ops->set_rdi_cid(vfe, line->id, 0);
7358c2ecf20Sopenharmony_ci		ops->wm_set_ub_cfg(vfe, output->wm_idx[0],
7368c2ecf20Sopenharmony_ci				   (ub_size + 1) * output->wm_idx[0], ub_size);
7378c2ecf20Sopenharmony_ci		ops->wm_frame_based(vfe, output->wm_idx[0], 1);
7388c2ecf20Sopenharmony_ci		ops->wm_enable(vfe, output->wm_idx[0], 1);
7398c2ecf20Sopenharmony_ci		ops->bus_reload_wm(vfe, output->wm_idx[0]);
7408c2ecf20Sopenharmony_ci	} else {
7418c2ecf20Sopenharmony_ci		ub_size /= output->wm_num;
7428c2ecf20Sopenharmony_ci		for (i = 0; i < output->wm_num; i++) {
7438c2ecf20Sopenharmony_ci			ops->set_cgc_override(vfe, output->wm_idx[i], 1);
7448c2ecf20Sopenharmony_ci			ops->wm_set_subsample(vfe, output->wm_idx[i]);
7458c2ecf20Sopenharmony_ci			ops->wm_set_ub_cfg(vfe, output->wm_idx[i],
7468c2ecf20Sopenharmony_ci					   (ub_size + 1) * output->wm_idx[i],
7478c2ecf20Sopenharmony_ci					   ub_size);
7488c2ecf20Sopenharmony_ci			ops->wm_line_based(vfe, output->wm_idx[i],
7498c2ecf20Sopenharmony_ci					&line->video_out.active_fmt.fmt.pix_mp,
7508c2ecf20Sopenharmony_ci					i, 1);
7518c2ecf20Sopenharmony_ci			ops->wm_enable(vfe, output->wm_idx[i], 1);
7528c2ecf20Sopenharmony_ci			ops->bus_reload_wm(vfe, output->wm_idx[i]);
7538c2ecf20Sopenharmony_ci		}
7548c2ecf20Sopenharmony_ci		ops->enable_irq_pix_line(vfe, 0, line->id, 1);
7558c2ecf20Sopenharmony_ci		ops->set_module_cfg(vfe, 1);
7568c2ecf20Sopenharmony_ci		ops->set_camif_cfg(vfe, line);
7578c2ecf20Sopenharmony_ci		ops->set_realign_cfg(vfe, line, 1);
7588c2ecf20Sopenharmony_ci		ops->set_xbar_cfg(vfe, output, 1);
7598c2ecf20Sopenharmony_ci		ops->set_demux_cfg(vfe, line);
7608c2ecf20Sopenharmony_ci		ops->set_scale_cfg(vfe, line);
7618c2ecf20Sopenharmony_ci		ops->set_crop_cfg(vfe, line);
7628c2ecf20Sopenharmony_ci		ops->set_clamp_cfg(vfe);
7638c2ecf20Sopenharmony_ci		ops->set_camif_cmd(vfe, 1);
7648c2ecf20Sopenharmony_ci	}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	ops->reg_update(vfe, line->id);
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	return 0;
7718c2ecf20Sopenharmony_ci}
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_cistatic int vfe_disable_output(struct vfe_line *line)
7748c2ecf20Sopenharmony_ci{
7758c2ecf20Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
7768c2ecf20Sopenharmony_ci	struct vfe_output *output = &line->output;
7778c2ecf20Sopenharmony_ci	const struct vfe_hw_ops *ops = vfe->ops;
7788c2ecf20Sopenharmony_ci	unsigned long flags;
7798c2ecf20Sopenharmony_ci	unsigned long time;
7808c2ecf20Sopenharmony_ci	unsigned int i;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vfe->output_lock, flags);
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	output->wait_sof = 1;
7858c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	time = wait_for_completion_timeout(&output->sof,
7888c2ecf20Sopenharmony_ci					   msecs_to_jiffies(VFE_NEXT_SOF_MS));
7898c2ecf20Sopenharmony_ci	if (!time)
7908c2ecf20Sopenharmony_ci		dev_err(vfe->camss->dev, "VFE sof timeout\n");
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vfe->output_lock, flags);
7938c2ecf20Sopenharmony_ci	for (i = 0; i < output->wm_num; i++)
7948c2ecf20Sopenharmony_ci		ops->wm_enable(vfe, output->wm_idx[i], 0);
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	ops->reg_update(vfe, line->id);
7978c2ecf20Sopenharmony_ci	output->wait_reg_update = 1;
7988c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	time = wait_for_completion_timeout(&output->reg_update,
8018c2ecf20Sopenharmony_ci					   msecs_to_jiffies(VFE_NEXT_SOF_MS));
8028c2ecf20Sopenharmony_ci	if (!time)
8038c2ecf20Sopenharmony_ci		dev_err(vfe->camss->dev, "VFE reg update timeout\n");
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vfe->output_lock, flags);
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	if (line->id != VFE_LINE_PIX) {
8088c2ecf20Sopenharmony_ci		ops->wm_frame_based(vfe, output->wm_idx[0], 0);
8098c2ecf20Sopenharmony_ci		ops->bus_disconnect_wm_from_rdi(vfe, output->wm_idx[0],
8108c2ecf20Sopenharmony_ci						line->id);
8118c2ecf20Sopenharmony_ci		ops->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 0);
8128c2ecf20Sopenharmony_ci		ops->set_cgc_override(vfe, output->wm_idx[0], 0);
8138c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&vfe->output_lock, flags);
8148c2ecf20Sopenharmony_ci	} else {
8158c2ecf20Sopenharmony_ci		for (i = 0; i < output->wm_num; i++) {
8168c2ecf20Sopenharmony_ci			ops->wm_line_based(vfe, output->wm_idx[i], NULL, i, 0);
8178c2ecf20Sopenharmony_ci			ops->set_cgc_override(vfe, output->wm_idx[i], 0);
8188c2ecf20Sopenharmony_ci		}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci		ops->enable_irq_pix_line(vfe, 0, line->id, 0);
8218c2ecf20Sopenharmony_ci		ops->set_module_cfg(vfe, 0);
8228c2ecf20Sopenharmony_ci		ops->set_realign_cfg(vfe, line, 0);
8238c2ecf20Sopenharmony_ci		ops->set_xbar_cfg(vfe, output, 0);
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci		ops->set_camif_cmd(vfe, 0);
8268c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&vfe->output_lock, flags);
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci		ops->camif_wait_for_stop(vfe, vfe->camss->dev);
8298c2ecf20Sopenharmony_ci	}
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	return 0;
8328c2ecf20Sopenharmony_ci}
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci/*
8358c2ecf20Sopenharmony_ci * vfe_enable - Enable streaming on VFE line
8368c2ecf20Sopenharmony_ci * @line: VFE line
8378c2ecf20Sopenharmony_ci *
8388c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise
8398c2ecf20Sopenharmony_ci */
8408c2ecf20Sopenharmony_cistatic int vfe_enable(struct vfe_line *line)
8418c2ecf20Sopenharmony_ci{
8428c2ecf20Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
8438c2ecf20Sopenharmony_ci	int ret;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	mutex_lock(&vfe->stream_lock);
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	if (!vfe->stream_count) {
8488c2ecf20Sopenharmony_ci		vfe->ops->enable_irq_common(vfe);
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci		vfe->ops->bus_enable_wr_if(vfe, 1);
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci		vfe->ops->set_qos(vfe);
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci		vfe->ops->set_ds(vfe);
8558c2ecf20Sopenharmony_ci	}
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	vfe->stream_count++;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	mutex_unlock(&vfe->stream_lock);
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	ret = vfe_get_output(line);
8628c2ecf20Sopenharmony_ci	if (ret < 0)
8638c2ecf20Sopenharmony_ci		goto error_get_output;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	ret = vfe_enable_output(line);
8668c2ecf20Sopenharmony_ci	if (ret < 0)
8678c2ecf20Sopenharmony_ci		goto error_enable_output;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	vfe->was_streaming = 1;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	return 0;
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_cierror_enable_output:
8758c2ecf20Sopenharmony_ci	vfe_put_output(line);
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_cierror_get_output:
8788c2ecf20Sopenharmony_ci	mutex_lock(&vfe->stream_lock);
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	if (vfe->stream_count == 1)
8818c2ecf20Sopenharmony_ci		vfe->ops->bus_enable_wr_if(vfe, 0);
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	vfe->stream_count--;
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	mutex_unlock(&vfe->stream_lock);
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	return ret;
8888c2ecf20Sopenharmony_ci}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci/*
8918c2ecf20Sopenharmony_ci * vfe_disable - Disable streaming on VFE line
8928c2ecf20Sopenharmony_ci * @line: VFE line
8938c2ecf20Sopenharmony_ci *
8948c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise
8958c2ecf20Sopenharmony_ci */
8968c2ecf20Sopenharmony_cistatic int vfe_disable(struct vfe_line *line)
8978c2ecf20Sopenharmony_ci{
8988c2ecf20Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	vfe_disable_output(line);
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	vfe_put_output(line);
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	mutex_lock(&vfe->stream_lock);
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	if (vfe->stream_count == 1)
9078c2ecf20Sopenharmony_ci		vfe->ops->bus_enable_wr_if(vfe, 0);
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	vfe->stream_count--;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	mutex_unlock(&vfe->stream_lock);
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	return 0;
9148c2ecf20Sopenharmony_ci}
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci/*
9178c2ecf20Sopenharmony_ci * vfe_isr_sof - Process start of frame interrupt
9188c2ecf20Sopenharmony_ci * @vfe: VFE Device
9198c2ecf20Sopenharmony_ci * @line_id: VFE line
9208c2ecf20Sopenharmony_ci */
9218c2ecf20Sopenharmony_cistatic void vfe_isr_sof(struct vfe_device *vfe, enum vfe_line_id line_id)
9228c2ecf20Sopenharmony_ci{
9238c2ecf20Sopenharmony_ci	struct vfe_output *output;
9248c2ecf20Sopenharmony_ci	unsigned long flags;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vfe->output_lock, flags);
9278c2ecf20Sopenharmony_ci	output = &vfe->line[line_id].output;
9288c2ecf20Sopenharmony_ci	if (output->wait_sof) {
9298c2ecf20Sopenharmony_ci		output->wait_sof = 0;
9308c2ecf20Sopenharmony_ci		complete(&output->sof);
9318c2ecf20Sopenharmony_ci	}
9328c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
9338c2ecf20Sopenharmony_ci}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci/*
9368c2ecf20Sopenharmony_ci * vfe_isr_reg_update - Process reg update interrupt
9378c2ecf20Sopenharmony_ci * @vfe: VFE Device
9388c2ecf20Sopenharmony_ci * @line_id: VFE line
9398c2ecf20Sopenharmony_ci */
9408c2ecf20Sopenharmony_cistatic void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
9418c2ecf20Sopenharmony_ci{
9428c2ecf20Sopenharmony_ci	struct vfe_output *output;
9438c2ecf20Sopenharmony_ci	unsigned long flags;
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vfe->output_lock, flags);
9468c2ecf20Sopenharmony_ci	vfe->ops->reg_update_clear(vfe, line_id);
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	output = &vfe->line[line_id].output;
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	if (output->wait_reg_update) {
9518c2ecf20Sopenharmony_ci		output->wait_reg_update = 0;
9528c2ecf20Sopenharmony_ci		complete(&output->reg_update);
9538c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&vfe->output_lock, flags);
9548c2ecf20Sopenharmony_ci		return;
9558c2ecf20Sopenharmony_ci	}
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	if (output->state == VFE_OUTPUT_STOPPING) {
9588c2ecf20Sopenharmony_ci		/* Release last buffer when hw is idle */
9598c2ecf20Sopenharmony_ci		if (output->last_buffer) {
9608c2ecf20Sopenharmony_ci			vb2_buffer_done(&output->last_buffer->vb.vb2_buf,
9618c2ecf20Sopenharmony_ci					VB2_BUF_STATE_DONE);
9628c2ecf20Sopenharmony_ci			output->last_buffer = NULL;
9638c2ecf20Sopenharmony_ci		}
9648c2ecf20Sopenharmony_ci		output->state = VFE_OUTPUT_IDLE;
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci		/* Buffers received in stopping state are queued in */
9678c2ecf20Sopenharmony_ci		/* dma pending queue, start next capture here */
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci		output->buf[0] = vfe_buf_get_pending(output);
9708c2ecf20Sopenharmony_ci		output->buf[1] = vfe_buf_get_pending(output);
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci		if (!output->buf[0] && output->buf[1]) {
9738c2ecf20Sopenharmony_ci			output->buf[0] = output->buf[1];
9748c2ecf20Sopenharmony_ci			output->buf[1] = NULL;
9758c2ecf20Sopenharmony_ci		}
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci		if (output->buf[0])
9788c2ecf20Sopenharmony_ci			output->state = VFE_OUTPUT_SINGLE;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci		if (output->buf[1])
9818c2ecf20Sopenharmony_ci			output->state = VFE_OUTPUT_CONTINUOUS;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci		switch (output->state) {
9848c2ecf20Sopenharmony_ci		case VFE_OUTPUT_SINGLE:
9858c2ecf20Sopenharmony_ci			vfe_output_frame_drop(vfe, output, 2);
9868c2ecf20Sopenharmony_ci			break;
9878c2ecf20Sopenharmony_ci		case VFE_OUTPUT_CONTINUOUS:
9888c2ecf20Sopenharmony_ci			vfe_output_frame_drop(vfe, output, 3);
9898c2ecf20Sopenharmony_ci			break;
9908c2ecf20Sopenharmony_ci		default:
9918c2ecf20Sopenharmony_ci			vfe_output_frame_drop(vfe, output, 0);
9928c2ecf20Sopenharmony_ci			break;
9938c2ecf20Sopenharmony_ci		}
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci		vfe_output_init_addrs(vfe, output, 1);
9968c2ecf20Sopenharmony_ci	}
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
9998c2ecf20Sopenharmony_ci}
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci/*
10028c2ecf20Sopenharmony_ci * vfe_isr_wm_done - Process write master done interrupt
10038c2ecf20Sopenharmony_ci * @vfe: VFE Device
10048c2ecf20Sopenharmony_ci * @wm: Write master id
10058c2ecf20Sopenharmony_ci */
10068c2ecf20Sopenharmony_cistatic void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm)
10078c2ecf20Sopenharmony_ci{
10088c2ecf20Sopenharmony_ci	struct camss_buffer *ready_buf;
10098c2ecf20Sopenharmony_ci	struct vfe_output *output;
10108c2ecf20Sopenharmony_ci	dma_addr_t *new_addr;
10118c2ecf20Sopenharmony_ci	unsigned long flags;
10128c2ecf20Sopenharmony_ci	u32 active_index;
10138c2ecf20Sopenharmony_ci	u64 ts = ktime_get_ns();
10148c2ecf20Sopenharmony_ci	unsigned int i;
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	active_index = vfe->ops->wm_get_ping_pong_status(vfe, wm);
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vfe->output_lock, flags);
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	if (vfe->wm_output_map[wm] == VFE_LINE_NONE) {
10218c2ecf20Sopenharmony_ci		dev_err_ratelimited(vfe->camss->dev,
10228c2ecf20Sopenharmony_ci				    "Received wm done for unmapped index\n");
10238c2ecf20Sopenharmony_ci		goto out_unlock;
10248c2ecf20Sopenharmony_ci	}
10258c2ecf20Sopenharmony_ci	output = &vfe->line[vfe->wm_output_map[wm]].output;
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	if (output->active_buf == active_index) {
10288c2ecf20Sopenharmony_ci		dev_err_ratelimited(vfe->camss->dev,
10298c2ecf20Sopenharmony_ci				    "Active buffer mismatch!\n");
10308c2ecf20Sopenharmony_ci		goto out_unlock;
10318c2ecf20Sopenharmony_ci	}
10328c2ecf20Sopenharmony_ci	output->active_buf = active_index;
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	ready_buf = output->buf[!active_index];
10358c2ecf20Sopenharmony_ci	if (!ready_buf) {
10368c2ecf20Sopenharmony_ci		dev_err_ratelimited(vfe->camss->dev,
10378c2ecf20Sopenharmony_ci				    "Missing ready buf %d %d!\n",
10388c2ecf20Sopenharmony_ci				    !active_index, output->state);
10398c2ecf20Sopenharmony_ci		goto out_unlock;
10408c2ecf20Sopenharmony_ci	}
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	ready_buf->vb.vb2_buf.timestamp = ts;
10438c2ecf20Sopenharmony_ci	ready_buf->vb.sequence = output->sequence++;
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	/* Get next buffer */
10468c2ecf20Sopenharmony_ci	output->buf[!active_index] = vfe_buf_get_pending(output);
10478c2ecf20Sopenharmony_ci	if (!output->buf[!active_index]) {
10488c2ecf20Sopenharmony_ci		/* No next buffer - set same address */
10498c2ecf20Sopenharmony_ci		new_addr = ready_buf->addr;
10508c2ecf20Sopenharmony_ci		vfe_buf_update_wm_on_last(vfe, output);
10518c2ecf20Sopenharmony_ci	} else {
10528c2ecf20Sopenharmony_ci		new_addr = output->buf[!active_index]->addr;
10538c2ecf20Sopenharmony_ci		vfe_buf_update_wm_on_next(vfe, output);
10548c2ecf20Sopenharmony_ci	}
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	if (active_index)
10578c2ecf20Sopenharmony_ci		for (i = 0; i < output->wm_num; i++)
10588c2ecf20Sopenharmony_ci			vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i],
10598c2ecf20Sopenharmony_ci						   new_addr[i]);
10608c2ecf20Sopenharmony_ci	else
10618c2ecf20Sopenharmony_ci		for (i = 0; i < output->wm_num; i++)
10628c2ecf20Sopenharmony_ci			vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i],
10638c2ecf20Sopenharmony_ci						   new_addr[i]);
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	if (output->state == VFE_OUTPUT_STOPPING)
10688c2ecf20Sopenharmony_ci		output->last_buffer = ready_buf;
10698c2ecf20Sopenharmony_ci	else
10708c2ecf20Sopenharmony_ci		vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	return;
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ciout_unlock:
10758c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
10768c2ecf20Sopenharmony_ci}
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci/*
10798c2ecf20Sopenharmony_ci * vfe_isr_wm_done - Process composite image done interrupt
10808c2ecf20Sopenharmony_ci * @vfe: VFE Device
10818c2ecf20Sopenharmony_ci * @comp: Composite image id
10828c2ecf20Sopenharmony_ci */
10838c2ecf20Sopenharmony_cistatic void vfe_isr_comp_done(struct vfe_device *vfe, u8 comp)
10848c2ecf20Sopenharmony_ci{
10858c2ecf20Sopenharmony_ci	unsigned int i;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++)
10888c2ecf20Sopenharmony_ci		if (vfe->wm_output_map[i] == VFE_LINE_PIX) {
10898c2ecf20Sopenharmony_ci			vfe_isr_wm_done(vfe, i);
10908c2ecf20Sopenharmony_ci			break;
10918c2ecf20Sopenharmony_ci		}
10928c2ecf20Sopenharmony_ci}
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_cistatic inline void vfe_isr_reset_ack(struct vfe_device *vfe)
10958c2ecf20Sopenharmony_ci{
10968c2ecf20Sopenharmony_ci	complete(&vfe->reset_complete);
10978c2ecf20Sopenharmony_ci}
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_cistatic inline void vfe_isr_halt_ack(struct vfe_device *vfe)
11008c2ecf20Sopenharmony_ci{
11018c2ecf20Sopenharmony_ci	complete(&vfe->halt_complete);
11028c2ecf20Sopenharmony_ci	vfe->ops->halt_clear(vfe);
11038c2ecf20Sopenharmony_ci}
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci/*
11068c2ecf20Sopenharmony_ci * vfe_set_clock_rates - Calculate and set clock rates on VFE module
11078c2ecf20Sopenharmony_ci * @vfe: VFE device
11088c2ecf20Sopenharmony_ci *
11098c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise
11108c2ecf20Sopenharmony_ci */
11118c2ecf20Sopenharmony_cistatic int vfe_set_clock_rates(struct vfe_device *vfe)
11128c2ecf20Sopenharmony_ci{
11138c2ecf20Sopenharmony_ci	struct device *dev = vfe->camss->dev;
11148c2ecf20Sopenharmony_ci	u32 pixel_clock[MSM_VFE_LINE_NUM];
11158c2ecf20Sopenharmony_ci	int i, j;
11168c2ecf20Sopenharmony_ci	int ret;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) {
11198c2ecf20Sopenharmony_ci		ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity,
11208c2ecf20Sopenharmony_ci					    &pixel_clock[i]);
11218c2ecf20Sopenharmony_ci		if (ret)
11228c2ecf20Sopenharmony_ci			pixel_clock[i] = 0;
11238c2ecf20Sopenharmony_ci	}
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	for (i = 0; i < vfe->nclocks; i++) {
11268c2ecf20Sopenharmony_ci		struct camss_clock *clock = &vfe->clock[i];
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci		if (!strcmp(clock->name, "vfe0") ||
11298c2ecf20Sopenharmony_ci		    !strcmp(clock->name, "vfe1")) {
11308c2ecf20Sopenharmony_ci			u64 min_rate = 0;
11318c2ecf20Sopenharmony_ci			long rate;
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci			for (j = VFE_LINE_RDI0; j <= VFE_LINE_PIX; j++) {
11348c2ecf20Sopenharmony_ci				u32 tmp;
11358c2ecf20Sopenharmony_ci				u8 bpp;
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci				if (j == VFE_LINE_PIX) {
11388c2ecf20Sopenharmony_ci					tmp = pixel_clock[j];
11398c2ecf20Sopenharmony_ci				} else {
11408c2ecf20Sopenharmony_ci					struct vfe_line *l = &vfe->line[j];
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci					bpp = vfe_get_bpp(l->formats,
11438c2ecf20Sopenharmony_ci						l->nformats,
11448c2ecf20Sopenharmony_ci						l->fmt[MSM_VFE_PAD_SINK].code);
11458c2ecf20Sopenharmony_ci					tmp = pixel_clock[j] * bpp / 64;
11468c2ecf20Sopenharmony_ci				}
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci				if (min_rate < tmp)
11498c2ecf20Sopenharmony_ci					min_rate = tmp;
11508c2ecf20Sopenharmony_ci			}
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci			camss_add_clock_margin(&min_rate);
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci			for (j = 0; j < clock->nfreqs; j++)
11558c2ecf20Sopenharmony_ci				if (min_rate < clock->freq[j])
11568c2ecf20Sopenharmony_ci					break;
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci			if (j == clock->nfreqs) {
11598c2ecf20Sopenharmony_ci				dev_err(dev,
11608c2ecf20Sopenharmony_ci					"Pixel clock is too high for VFE");
11618c2ecf20Sopenharmony_ci				return -EINVAL;
11628c2ecf20Sopenharmony_ci			}
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci			/* if sensor pixel clock is not available */
11658c2ecf20Sopenharmony_ci			/* set highest possible VFE clock rate */
11668c2ecf20Sopenharmony_ci			if (min_rate == 0)
11678c2ecf20Sopenharmony_ci				j = clock->nfreqs - 1;
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci			rate = clk_round_rate(clock->clk, clock->freq[j]);
11708c2ecf20Sopenharmony_ci			if (rate < 0) {
11718c2ecf20Sopenharmony_ci				dev_err(dev, "clk round rate failed: %ld\n",
11728c2ecf20Sopenharmony_ci					rate);
11738c2ecf20Sopenharmony_ci				return -EINVAL;
11748c2ecf20Sopenharmony_ci			}
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci			ret = clk_set_rate(clock->clk, rate);
11778c2ecf20Sopenharmony_ci			if (ret < 0) {
11788c2ecf20Sopenharmony_ci				dev_err(dev, "clk set rate failed: %d\n", ret);
11798c2ecf20Sopenharmony_ci				return ret;
11808c2ecf20Sopenharmony_ci			}
11818c2ecf20Sopenharmony_ci		}
11828c2ecf20Sopenharmony_ci	}
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	return 0;
11858c2ecf20Sopenharmony_ci}
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci/*
11888c2ecf20Sopenharmony_ci * vfe_check_clock_rates - Check current clock rates on VFE module
11898c2ecf20Sopenharmony_ci * @vfe: VFE device
11908c2ecf20Sopenharmony_ci *
11918c2ecf20Sopenharmony_ci * Return 0 if current clock rates are suitable for a new pipeline
11928c2ecf20Sopenharmony_ci * or a negative error code otherwise
11938c2ecf20Sopenharmony_ci */
11948c2ecf20Sopenharmony_cistatic int vfe_check_clock_rates(struct vfe_device *vfe)
11958c2ecf20Sopenharmony_ci{
11968c2ecf20Sopenharmony_ci	u32 pixel_clock[MSM_VFE_LINE_NUM];
11978c2ecf20Sopenharmony_ci	int i, j;
11988c2ecf20Sopenharmony_ci	int ret;
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) {
12018c2ecf20Sopenharmony_ci		ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity,
12028c2ecf20Sopenharmony_ci					    &pixel_clock[i]);
12038c2ecf20Sopenharmony_ci		if (ret)
12048c2ecf20Sopenharmony_ci			pixel_clock[i] = 0;
12058c2ecf20Sopenharmony_ci	}
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci	for (i = 0; i < vfe->nclocks; i++) {
12088c2ecf20Sopenharmony_ci		struct camss_clock *clock = &vfe->clock[i];
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci		if (!strcmp(clock->name, "vfe0") ||
12118c2ecf20Sopenharmony_ci		    !strcmp(clock->name, "vfe1")) {
12128c2ecf20Sopenharmony_ci			u64 min_rate = 0;
12138c2ecf20Sopenharmony_ci			unsigned long rate;
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci			for (j = VFE_LINE_RDI0; j <= VFE_LINE_PIX; j++) {
12168c2ecf20Sopenharmony_ci				u32 tmp;
12178c2ecf20Sopenharmony_ci				u8 bpp;
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci				if (j == VFE_LINE_PIX) {
12208c2ecf20Sopenharmony_ci					tmp = pixel_clock[j];
12218c2ecf20Sopenharmony_ci				} else {
12228c2ecf20Sopenharmony_ci					struct vfe_line *l = &vfe->line[j];
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci					bpp = vfe_get_bpp(l->formats,
12258c2ecf20Sopenharmony_ci						l->nformats,
12268c2ecf20Sopenharmony_ci						l->fmt[MSM_VFE_PAD_SINK].code);
12278c2ecf20Sopenharmony_ci					tmp = pixel_clock[j] * bpp / 64;
12288c2ecf20Sopenharmony_ci				}
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci				if (min_rate < tmp)
12318c2ecf20Sopenharmony_ci					min_rate = tmp;
12328c2ecf20Sopenharmony_ci			}
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci			camss_add_clock_margin(&min_rate);
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci			rate = clk_get_rate(clock->clk);
12378c2ecf20Sopenharmony_ci			if (rate < min_rate)
12388c2ecf20Sopenharmony_ci				return -EBUSY;
12398c2ecf20Sopenharmony_ci		}
12408c2ecf20Sopenharmony_ci	}
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	return 0;
12438c2ecf20Sopenharmony_ci}
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci/*
12468c2ecf20Sopenharmony_ci * vfe_get - Power up and reset VFE module
12478c2ecf20Sopenharmony_ci * @vfe: VFE Device
12488c2ecf20Sopenharmony_ci *
12498c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise
12508c2ecf20Sopenharmony_ci */
12518c2ecf20Sopenharmony_cistatic int vfe_get(struct vfe_device *vfe)
12528c2ecf20Sopenharmony_ci{
12538c2ecf20Sopenharmony_ci	int ret;
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci	mutex_lock(&vfe->power_lock);
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	if (vfe->power_count == 0) {
12588c2ecf20Sopenharmony_ci		ret = camss_pm_domain_on(vfe->camss, vfe->id);
12598c2ecf20Sopenharmony_ci		if (ret < 0)
12608c2ecf20Sopenharmony_ci			goto error_pm_domain;
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci		ret = pm_runtime_get_sync(vfe->camss->dev);
12638c2ecf20Sopenharmony_ci		if (ret < 0)
12648c2ecf20Sopenharmony_ci			goto error_pm_runtime_get;
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci		ret = vfe_set_clock_rates(vfe);
12678c2ecf20Sopenharmony_ci		if (ret < 0)
12688c2ecf20Sopenharmony_ci			goto error_pm_runtime_get;
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci		ret = camss_enable_clocks(vfe->nclocks, vfe->clock,
12718c2ecf20Sopenharmony_ci					  vfe->camss->dev);
12728c2ecf20Sopenharmony_ci		if (ret < 0)
12738c2ecf20Sopenharmony_ci			goto error_pm_runtime_get;
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci		ret = vfe_reset(vfe);
12768c2ecf20Sopenharmony_ci		if (ret < 0)
12778c2ecf20Sopenharmony_ci			goto error_reset;
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci		vfe_reset_output_maps(vfe);
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ci		vfe_init_outputs(vfe);
12828c2ecf20Sopenharmony_ci	} else {
12838c2ecf20Sopenharmony_ci		ret = vfe_check_clock_rates(vfe);
12848c2ecf20Sopenharmony_ci		if (ret < 0)
12858c2ecf20Sopenharmony_ci			goto error_pm_domain;
12868c2ecf20Sopenharmony_ci	}
12878c2ecf20Sopenharmony_ci	vfe->power_count++;
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	mutex_unlock(&vfe->power_lock);
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	return 0;
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_cierror_reset:
12948c2ecf20Sopenharmony_ci	camss_disable_clocks(vfe->nclocks, vfe->clock);
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_cierror_pm_runtime_get:
12978c2ecf20Sopenharmony_ci	pm_runtime_put_sync(vfe->camss->dev);
12988c2ecf20Sopenharmony_ci	camss_pm_domain_off(vfe->camss, vfe->id);
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_cierror_pm_domain:
13018c2ecf20Sopenharmony_ci	mutex_unlock(&vfe->power_lock);
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	return ret;
13048c2ecf20Sopenharmony_ci}
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci/*
13078c2ecf20Sopenharmony_ci * vfe_put - Power down VFE module
13088c2ecf20Sopenharmony_ci * @vfe: VFE Device
13098c2ecf20Sopenharmony_ci */
13108c2ecf20Sopenharmony_cistatic void vfe_put(struct vfe_device *vfe)
13118c2ecf20Sopenharmony_ci{
13128c2ecf20Sopenharmony_ci	mutex_lock(&vfe->power_lock);
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci	if (vfe->power_count == 0) {
13158c2ecf20Sopenharmony_ci		dev_err(vfe->camss->dev, "vfe power off on power_count == 0\n");
13168c2ecf20Sopenharmony_ci		goto exit;
13178c2ecf20Sopenharmony_ci	} else if (vfe->power_count == 1) {
13188c2ecf20Sopenharmony_ci		if (vfe->was_streaming) {
13198c2ecf20Sopenharmony_ci			vfe->was_streaming = 0;
13208c2ecf20Sopenharmony_ci			vfe_halt(vfe);
13218c2ecf20Sopenharmony_ci		}
13228c2ecf20Sopenharmony_ci		camss_disable_clocks(vfe->nclocks, vfe->clock);
13238c2ecf20Sopenharmony_ci		pm_runtime_put_sync(vfe->camss->dev);
13248c2ecf20Sopenharmony_ci		camss_pm_domain_off(vfe->camss, vfe->id);
13258c2ecf20Sopenharmony_ci	}
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci	vfe->power_count--;
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ciexit:
13308c2ecf20Sopenharmony_ci	mutex_unlock(&vfe->power_lock);
13318c2ecf20Sopenharmony_ci}
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci/*
13348c2ecf20Sopenharmony_ci * vfe_queue_buffer - Add empty buffer
13358c2ecf20Sopenharmony_ci * @vid: Video device structure
13368c2ecf20Sopenharmony_ci * @buf: Buffer to be enqueued
13378c2ecf20Sopenharmony_ci *
13388c2ecf20Sopenharmony_ci * Add an empty buffer - depending on the current number of buffers it will be
13398c2ecf20Sopenharmony_ci * put in pending buffer queue or directly given to the hardware to be filled.
13408c2ecf20Sopenharmony_ci *
13418c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise
13428c2ecf20Sopenharmony_ci */
13438c2ecf20Sopenharmony_cistatic int vfe_queue_buffer(struct camss_video *vid,
13448c2ecf20Sopenharmony_ci			    struct camss_buffer *buf)
13458c2ecf20Sopenharmony_ci{
13468c2ecf20Sopenharmony_ci	struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
13478c2ecf20Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
13488c2ecf20Sopenharmony_ci	struct vfe_output *output;
13498c2ecf20Sopenharmony_ci	unsigned long flags;
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci	output = &line->output;
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vfe->output_lock, flags);
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	vfe_buf_update_wm_on_new(vfe, output, buf);
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci	return 0;
13608c2ecf20Sopenharmony_ci}
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci/*
13638c2ecf20Sopenharmony_ci * vfe_flush_buffers - Return all vb2 buffers
13648c2ecf20Sopenharmony_ci * @vid: Video device structure
13658c2ecf20Sopenharmony_ci * @state: vb2 buffer state of the returned buffers
13668c2ecf20Sopenharmony_ci *
13678c2ecf20Sopenharmony_ci * Return all buffers to vb2. This includes queued pending buffers (still
13688c2ecf20Sopenharmony_ci * unused) and any buffers given to the hardware but again still not used.
13698c2ecf20Sopenharmony_ci *
13708c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise
13718c2ecf20Sopenharmony_ci */
13728c2ecf20Sopenharmony_cistatic int vfe_flush_buffers(struct camss_video *vid,
13738c2ecf20Sopenharmony_ci			     enum vb2_buffer_state state)
13748c2ecf20Sopenharmony_ci{
13758c2ecf20Sopenharmony_ci	struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
13768c2ecf20Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
13778c2ecf20Sopenharmony_ci	struct vfe_output *output;
13788c2ecf20Sopenharmony_ci	unsigned long flags;
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	output = &line->output;
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vfe->output_lock, flags);
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci	vfe_buf_flush_pending(output, state);
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci	if (output->buf[0])
13878c2ecf20Sopenharmony_ci		vb2_buffer_done(&output->buf[0]->vb.vb2_buf, state);
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci	if (output->buf[1])
13908c2ecf20Sopenharmony_ci		vb2_buffer_done(&output->buf[1]->vb.vb2_buf, state);
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci	if (output->last_buffer) {
13938c2ecf20Sopenharmony_ci		vb2_buffer_done(&output->last_buffer->vb.vb2_buf, state);
13948c2ecf20Sopenharmony_ci		output->last_buffer = NULL;
13958c2ecf20Sopenharmony_ci	}
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	return 0;
14008c2ecf20Sopenharmony_ci}
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci/*
14038c2ecf20Sopenharmony_ci * vfe_set_power - Power on/off VFE module
14048c2ecf20Sopenharmony_ci * @sd: VFE V4L2 subdevice
14058c2ecf20Sopenharmony_ci * @on: Requested power state
14068c2ecf20Sopenharmony_ci *
14078c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise
14088c2ecf20Sopenharmony_ci */
14098c2ecf20Sopenharmony_cistatic int vfe_set_power(struct v4l2_subdev *sd, int on)
14108c2ecf20Sopenharmony_ci{
14118c2ecf20Sopenharmony_ci	struct vfe_line *line = v4l2_get_subdevdata(sd);
14128c2ecf20Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
14138c2ecf20Sopenharmony_ci	int ret;
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci	if (on) {
14168c2ecf20Sopenharmony_ci		ret = vfe_get(vfe);
14178c2ecf20Sopenharmony_ci		if (ret < 0)
14188c2ecf20Sopenharmony_ci			return ret;
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci		vfe->ops->hw_version_read(vfe, vfe->camss->dev);
14218c2ecf20Sopenharmony_ci	} else {
14228c2ecf20Sopenharmony_ci		vfe_put(vfe);
14238c2ecf20Sopenharmony_ci	}
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	return 0;
14268c2ecf20Sopenharmony_ci}
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci/*
14298c2ecf20Sopenharmony_ci * vfe_set_stream - Enable/disable streaming on VFE module
14308c2ecf20Sopenharmony_ci * @sd: VFE V4L2 subdevice
14318c2ecf20Sopenharmony_ci * @enable: Requested streaming state
14328c2ecf20Sopenharmony_ci *
14338c2ecf20Sopenharmony_ci * Main configuration of VFE module is triggered here.
14348c2ecf20Sopenharmony_ci *
14358c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise
14368c2ecf20Sopenharmony_ci */
14378c2ecf20Sopenharmony_cistatic int vfe_set_stream(struct v4l2_subdev *sd, int enable)
14388c2ecf20Sopenharmony_ci{
14398c2ecf20Sopenharmony_ci	struct vfe_line *line = v4l2_get_subdevdata(sd);
14408c2ecf20Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
14418c2ecf20Sopenharmony_ci	int ret;
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	if (enable) {
14448c2ecf20Sopenharmony_ci		ret = vfe_enable(line);
14458c2ecf20Sopenharmony_ci		if (ret < 0)
14468c2ecf20Sopenharmony_ci			dev_err(vfe->camss->dev,
14478c2ecf20Sopenharmony_ci				"Failed to enable vfe outputs\n");
14488c2ecf20Sopenharmony_ci	} else {
14498c2ecf20Sopenharmony_ci		ret = vfe_disable(line);
14508c2ecf20Sopenharmony_ci		if (ret < 0)
14518c2ecf20Sopenharmony_ci			dev_err(vfe->camss->dev,
14528c2ecf20Sopenharmony_ci				"Failed to disable vfe outputs\n");
14538c2ecf20Sopenharmony_ci	}
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_ci	return ret;
14568c2ecf20Sopenharmony_ci}
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_ci/*
14598c2ecf20Sopenharmony_ci * __vfe_get_format - Get pointer to format structure
14608c2ecf20Sopenharmony_ci * @line: VFE line
14618c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
14628c2ecf20Sopenharmony_ci * @pad: pad from which format is requested
14638c2ecf20Sopenharmony_ci * @which: TRY or ACTIVE format
14648c2ecf20Sopenharmony_ci *
14658c2ecf20Sopenharmony_ci * Return pointer to TRY or ACTIVE format structure
14668c2ecf20Sopenharmony_ci */
14678c2ecf20Sopenharmony_cistatic struct v4l2_mbus_framefmt *
14688c2ecf20Sopenharmony_ci__vfe_get_format(struct vfe_line *line,
14698c2ecf20Sopenharmony_ci		 struct v4l2_subdev_pad_config *cfg,
14708c2ecf20Sopenharmony_ci		 unsigned int pad,
14718c2ecf20Sopenharmony_ci		 enum v4l2_subdev_format_whence which)
14728c2ecf20Sopenharmony_ci{
14738c2ecf20Sopenharmony_ci	if (which == V4L2_SUBDEV_FORMAT_TRY)
14748c2ecf20Sopenharmony_ci		return v4l2_subdev_get_try_format(&line->subdev, cfg, pad);
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	return &line->fmt[pad];
14778c2ecf20Sopenharmony_ci}
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci/*
14808c2ecf20Sopenharmony_ci * __vfe_get_compose - Get pointer to compose selection structure
14818c2ecf20Sopenharmony_ci * @line: VFE line
14828c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
14838c2ecf20Sopenharmony_ci * @which: TRY or ACTIVE format
14848c2ecf20Sopenharmony_ci *
14858c2ecf20Sopenharmony_ci * Return pointer to TRY or ACTIVE compose rectangle structure
14868c2ecf20Sopenharmony_ci */
14878c2ecf20Sopenharmony_cistatic struct v4l2_rect *
14888c2ecf20Sopenharmony_ci__vfe_get_compose(struct vfe_line *line,
14898c2ecf20Sopenharmony_ci		  struct v4l2_subdev_pad_config *cfg,
14908c2ecf20Sopenharmony_ci		  enum v4l2_subdev_format_whence which)
14918c2ecf20Sopenharmony_ci{
14928c2ecf20Sopenharmony_ci	if (which == V4L2_SUBDEV_FORMAT_TRY)
14938c2ecf20Sopenharmony_ci		return v4l2_subdev_get_try_compose(&line->subdev, cfg,
14948c2ecf20Sopenharmony_ci						   MSM_VFE_PAD_SINK);
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	return &line->compose;
14978c2ecf20Sopenharmony_ci}
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci/*
15008c2ecf20Sopenharmony_ci * __vfe_get_crop - Get pointer to crop selection structure
15018c2ecf20Sopenharmony_ci * @line: VFE line
15028c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
15038c2ecf20Sopenharmony_ci * @which: TRY or ACTIVE format
15048c2ecf20Sopenharmony_ci *
15058c2ecf20Sopenharmony_ci * Return pointer to TRY or ACTIVE crop rectangle structure
15068c2ecf20Sopenharmony_ci */
15078c2ecf20Sopenharmony_cistatic struct v4l2_rect *
15088c2ecf20Sopenharmony_ci__vfe_get_crop(struct vfe_line *line,
15098c2ecf20Sopenharmony_ci	       struct v4l2_subdev_pad_config *cfg,
15108c2ecf20Sopenharmony_ci	       enum v4l2_subdev_format_whence which)
15118c2ecf20Sopenharmony_ci{
15128c2ecf20Sopenharmony_ci	if (which == V4L2_SUBDEV_FORMAT_TRY)
15138c2ecf20Sopenharmony_ci		return v4l2_subdev_get_try_crop(&line->subdev, cfg,
15148c2ecf20Sopenharmony_ci						MSM_VFE_PAD_SRC);
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci	return &line->crop;
15178c2ecf20Sopenharmony_ci}
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci/*
15208c2ecf20Sopenharmony_ci * vfe_try_format - Handle try format by pad subdev method
15218c2ecf20Sopenharmony_ci * @line: VFE line
15228c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
15238c2ecf20Sopenharmony_ci * @pad: pad on which format is requested
15248c2ecf20Sopenharmony_ci * @fmt: pointer to v4l2 format structure
15258c2ecf20Sopenharmony_ci * @which: wanted subdev format
15268c2ecf20Sopenharmony_ci */
15278c2ecf20Sopenharmony_cistatic void vfe_try_format(struct vfe_line *line,
15288c2ecf20Sopenharmony_ci			   struct v4l2_subdev_pad_config *cfg,
15298c2ecf20Sopenharmony_ci			   unsigned int pad,
15308c2ecf20Sopenharmony_ci			   struct v4l2_mbus_framefmt *fmt,
15318c2ecf20Sopenharmony_ci			   enum v4l2_subdev_format_whence which)
15328c2ecf20Sopenharmony_ci{
15338c2ecf20Sopenharmony_ci	unsigned int i;
15348c2ecf20Sopenharmony_ci	u32 code;
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci	switch (pad) {
15378c2ecf20Sopenharmony_ci	case MSM_VFE_PAD_SINK:
15388c2ecf20Sopenharmony_ci		/* Set format on sink pad */
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_ci		for (i = 0; i < line->nformats; i++)
15418c2ecf20Sopenharmony_ci			if (fmt->code == line->formats[i].code)
15428c2ecf20Sopenharmony_ci				break;
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci		/* If not found, use UYVY as default */
15458c2ecf20Sopenharmony_ci		if (i >= line->nformats)
15468c2ecf20Sopenharmony_ci			fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci		fmt->width = clamp_t(u32, fmt->width, 1, 8191);
15498c2ecf20Sopenharmony_ci		fmt->height = clamp_t(u32, fmt->height, 1, 8191);
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci		fmt->field = V4L2_FIELD_NONE;
15528c2ecf20Sopenharmony_ci		fmt->colorspace = V4L2_COLORSPACE_SRGB;
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci		break;
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ci	case MSM_VFE_PAD_SRC:
15578c2ecf20Sopenharmony_ci		/* Set and return a format same as sink pad */
15588c2ecf20Sopenharmony_ci		code = fmt->code;
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci		*fmt = *__vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, which);
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci		fmt->code = vfe_src_pad_code(line, fmt->code, 0, code);
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci		if (line->id == VFE_LINE_PIX) {
15658c2ecf20Sopenharmony_ci			struct v4l2_rect *rect;
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci			rect = __vfe_get_crop(line, cfg, which);
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci			fmt->width = rect->width;
15708c2ecf20Sopenharmony_ci			fmt->height = rect->height;
15718c2ecf20Sopenharmony_ci		}
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci		break;
15748c2ecf20Sopenharmony_ci	}
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci	fmt->colorspace = V4L2_COLORSPACE_SRGB;
15778c2ecf20Sopenharmony_ci}
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci/*
15808c2ecf20Sopenharmony_ci * vfe_try_compose - Handle try compose selection by pad subdev method
15818c2ecf20Sopenharmony_ci * @line: VFE line
15828c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
15838c2ecf20Sopenharmony_ci * @rect: pointer to v4l2 rect structure
15848c2ecf20Sopenharmony_ci * @which: wanted subdev format
15858c2ecf20Sopenharmony_ci */
15868c2ecf20Sopenharmony_cistatic void vfe_try_compose(struct vfe_line *line,
15878c2ecf20Sopenharmony_ci			    struct v4l2_subdev_pad_config *cfg,
15888c2ecf20Sopenharmony_ci			    struct v4l2_rect *rect,
15898c2ecf20Sopenharmony_ci			    enum v4l2_subdev_format_whence which)
15908c2ecf20Sopenharmony_ci{
15918c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *fmt;
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci	fmt = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, which);
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci	if (rect->width > fmt->width)
15968c2ecf20Sopenharmony_ci		rect->width = fmt->width;
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_ci	if (rect->height > fmt->height)
15998c2ecf20Sopenharmony_ci		rect->height = fmt->height;
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_ci	if (fmt->width > rect->width * SCALER_RATIO_MAX)
16028c2ecf20Sopenharmony_ci		rect->width = (fmt->width + SCALER_RATIO_MAX - 1) /
16038c2ecf20Sopenharmony_ci							SCALER_RATIO_MAX;
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_ci	rect->width &= ~0x1;
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci	if (fmt->height > rect->height * SCALER_RATIO_MAX)
16088c2ecf20Sopenharmony_ci		rect->height = (fmt->height + SCALER_RATIO_MAX - 1) /
16098c2ecf20Sopenharmony_ci							SCALER_RATIO_MAX;
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci	if (rect->width < 16)
16128c2ecf20Sopenharmony_ci		rect->width = 16;
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	if (rect->height < 4)
16158c2ecf20Sopenharmony_ci		rect->height = 4;
16168c2ecf20Sopenharmony_ci}
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci/*
16198c2ecf20Sopenharmony_ci * vfe_try_crop - Handle try crop selection by pad subdev method
16208c2ecf20Sopenharmony_ci * @line: VFE line
16218c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
16228c2ecf20Sopenharmony_ci * @rect: pointer to v4l2 rect structure
16238c2ecf20Sopenharmony_ci * @which: wanted subdev format
16248c2ecf20Sopenharmony_ci */
16258c2ecf20Sopenharmony_cistatic void vfe_try_crop(struct vfe_line *line,
16268c2ecf20Sopenharmony_ci			 struct v4l2_subdev_pad_config *cfg,
16278c2ecf20Sopenharmony_ci			 struct v4l2_rect *rect,
16288c2ecf20Sopenharmony_ci			 enum v4l2_subdev_format_whence which)
16298c2ecf20Sopenharmony_ci{
16308c2ecf20Sopenharmony_ci	struct v4l2_rect *compose;
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci	compose = __vfe_get_compose(line, cfg, which);
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	if (rect->width > compose->width)
16358c2ecf20Sopenharmony_ci		rect->width = compose->width;
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	if (rect->width + rect->left > compose->width)
16388c2ecf20Sopenharmony_ci		rect->left = compose->width - rect->width;
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci	if (rect->height > compose->height)
16418c2ecf20Sopenharmony_ci		rect->height = compose->height;
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	if (rect->height + rect->top > compose->height)
16448c2ecf20Sopenharmony_ci		rect->top = compose->height - rect->height;
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_ci	/* wm in line based mode writes multiple of 16 horizontally */
16478c2ecf20Sopenharmony_ci	rect->left += (rect->width & 0xf) >> 1;
16488c2ecf20Sopenharmony_ci	rect->width &= ~0xf;
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	if (rect->width < 16) {
16518c2ecf20Sopenharmony_ci		rect->left = 0;
16528c2ecf20Sopenharmony_ci		rect->width = 16;
16538c2ecf20Sopenharmony_ci	}
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_ci	if (rect->height < 4) {
16568c2ecf20Sopenharmony_ci		rect->top = 0;
16578c2ecf20Sopenharmony_ci		rect->height = 4;
16588c2ecf20Sopenharmony_ci	}
16598c2ecf20Sopenharmony_ci}
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_ci/*
16628c2ecf20Sopenharmony_ci * vfe_enum_mbus_code - Handle pixel format enumeration
16638c2ecf20Sopenharmony_ci * @sd: VFE V4L2 subdevice
16648c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
16658c2ecf20Sopenharmony_ci * @code: pointer to v4l2_subdev_mbus_code_enum structure
16668c2ecf20Sopenharmony_ci *
16678c2ecf20Sopenharmony_ci * return -EINVAL or zero on success
16688c2ecf20Sopenharmony_ci */
16698c2ecf20Sopenharmony_cistatic int vfe_enum_mbus_code(struct v4l2_subdev *sd,
16708c2ecf20Sopenharmony_ci			      struct v4l2_subdev_pad_config *cfg,
16718c2ecf20Sopenharmony_ci			      struct v4l2_subdev_mbus_code_enum *code)
16728c2ecf20Sopenharmony_ci{
16738c2ecf20Sopenharmony_ci	struct vfe_line *line = v4l2_get_subdevdata(sd);
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci	if (code->pad == MSM_VFE_PAD_SINK) {
16768c2ecf20Sopenharmony_ci		if (code->index >= line->nformats)
16778c2ecf20Sopenharmony_ci			return -EINVAL;
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_ci		code->code = line->formats[code->index].code;
16808c2ecf20Sopenharmony_ci	} else {
16818c2ecf20Sopenharmony_ci		struct v4l2_mbus_framefmt *sink_fmt;
16828c2ecf20Sopenharmony_ci
16838c2ecf20Sopenharmony_ci		sink_fmt = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK,
16848c2ecf20Sopenharmony_ci					    code->which);
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci		code->code = vfe_src_pad_code(line, sink_fmt->code,
16878c2ecf20Sopenharmony_ci					      code->index, 0);
16888c2ecf20Sopenharmony_ci		if (!code->code)
16898c2ecf20Sopenharmony_ci			return -EINVAL;
16908c2ecf20Sopenharmony_ci	}
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_ci	return 0;
16938c2ecf20Sopenharmony_ci}
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci/*
16968c2ecf20Sopenharmony_ci * vfe_enum_frame_size - Handle frame size enumeration
16978c2ecf20Sopenharmony_ci * @sd: VFE V4L2 subdevice
16988c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
16998c2ecf20Sopenharmony_ci * @fse: pointer to v4l2_subdev_frame_size_enum structure
17008c2ecf20Sopenharmony_ci *
17018c2ecf20Sopenharmony_ci * Return -EINVAL or zero on success
17028c2ecf20Sopenharmony_ci */
17038c2ecf20Sopenharmony_cistatic int vfe_enum_frame_size(struct v4l2_subdev *sd,
17048c2ecf20Sopenharmony_ci			       struct v4l2_subdev_pad_config *cfg,
17058c2ecf20Sopenharmony_ci			       struct v4l2_subdev_frame_size_enum *fse)
17068c2ecf20Sopenharmony_ci{
17078c2ecf20Sopenharmony_ci	struct vfe_line *line = v4l2_get_subdevdata(sd);
17088c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt format;
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_ci	if (fse->index != 0)
17118c2ecf20Sopenharmony_ci		return -EINVAL;
17128c2ecf20Sopenharmony_ci
17138c2ecf20Sopenharmony_ci	format.code = fse->code;
17148c2ecf20Sopenharmony_ci	format.width = 1;
17158c2ecf20Sopenharmony_ci	format.height = 1;
17168c2ecf20Sopenharmony_ci	vfe_try_format(line, cfg, fse->pad, &format, fse->which);
17178c2ecf20Sopenharmony_ci	fse->min_width = format.width;
17188c2ecf20Sopenharmony_ci	fse->min_height = format.height;
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_ci	if (format.code != fse->code)
17218c2ecf20Sopenharmony_ci		return -EINVAL;
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_ci	format.code = fse->code;
17248c2ecf20Sopenharmony_ci	format.width = -1;
17258c2ecf20Sopenharmony_ci	format.height = -1;
17268c2ecf20Sopenharmony_ci	vfe_try_format(line, cfg, fse->pad, &format, fse->which);
17278c2ecf20Sopenharmony_ci	fse->max_width = format.width;
17288c2ecf20Sopenharmony_ci	fse->max_height = format.height;
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_ci	return 0;
17318c2ecf20Sopenharmony_ci}
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_ci/*
17348c2ecf20Sopenharmony_ci * vfe_get_format - Handle get format by pads subdev method
17358c2ecf20Sopenharmony_ci * @sd: VFE V4L2 subdevice
17368c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
17378c2ecf20Sopenharmony_ci * @fmt: pointer to v4l2 subdev format structure
17388c2ecf20Sopenharmony_ci *
17398c2ecf20Sopenharmony_ci * Return -EINVAL or zero on success
17408c2ecf20Sopenharmony_ci */
17418c2ecf20Sopenharmony_cistatic int vfe_get_format(struct v4l2_subdev *sd,
17428c2ecf20Sopenharmony_ci			  struct v4l2_subdev_pad_config *cfg,
17438c2ecf20Sopenharmony_ci			  struct v4l2_subdev_format *fmt)
17448c2ecf20Sopenharmony_ci{
17458c2ecf20Sopenharmony_ci	struct vfe_line *line = v4l2_get_subdevdata(sd);
17468c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
17478c2ecf20Sopenharmony_ci
17488c2ecf20Sopenharmony_ci	format = __vfe_get_format(line, cfg, fmt->pad, fmt->which);
17498c2ecf20Sopenharmony_ci	if (format == NULL)
17508c2ecf20Sopenharmony_ci		return -EINVAL;
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	fmt->format = *format;
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_ci	return 0;
17558c2ecf20Sopenharmony_ci}
17568c2ecf20Sopenharmony_ci
17578c2ecf20Sopenharmony_cistatic int vfe_set_selection(struct v4l2_subdev *sd,
17588c2ecf20Sopenharmony_ci			     struct v4l2_subdev_pad_config *cfg,
17598c2ecf20Sopenharmony_ci			     struct v4l2_subdev_selection *sel);
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci/*
17628c2ecf20Sopenharmony_ci * vfe_set_format - Handle set format by pads subdev method
17638c2ecf20Sopenharmony_ci * @sd: VFE V4L2 subdevice
17648c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
17658c2ecf20Sopenharmony_ci * @fmt: pointer to v4l2 subdev format structure
17668c2ecf20Sopenharmony_ci *
17678c2ecf20Sopenharmony_ci * Return -EINVAL or zero on success
17688c2ecf20Sopenharmony_ci */
17698c2ecf20Sopenharmony_cistatic int vfe_set_format(struct v4l2_subdev *sd,
17708c2ecf20Sopenharmony_ci			  struct v4l2_subdev_pad_config *cfg,
17718c2ecf20Sopenharmony_ci			  struct v4l2_subdev_format *fmt)
17728c2ecf20Sopenharmony_ci{
17738c2ecf20Sopenharmony_ci	struct vfe_line *line = v4l2_get_subdevdata(sd);
17748c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
17758c2ecf20Sopenharmony_ci
17768c2ecf20Sopenharmony_ci	format = __vfe_get_format(line, cfg, fmt->pad, fmt->which);
17778c2ecf20Sopenharmony_ci	if (format == NULL)
17788c2ecf20Sopenharmony_ci		return -EINVAL;
17798c2ecf20Sopenharmony_ci
17808c2ecf20Sopenharmony_ci	vfe_try_format(line, cfg, fmt->pad, &fmt->format, fmt->which);
17818c2ecf20Sopenharmony_ci	*format = fmt->format;
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_ci	if (fmt->pad == MSM_VFE_PAD_SINK) {
17848c2ecf20Sopenharmony_ci		struct v4l2_subdev_selection sel = { 0 };
17858c2ecf20Sopenharmony_ci		int ret;
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_ci		/* Propagate the format from sink to source */
17888c2ecf20Sopenharmony_ci		format = __vfe_get_format(line, cfg, MSM_VFE_PAD_SRC,
17898c2ecf20Sopenharmony_ci					  fmt->which);
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_ci		*format = fmt->format;
17928c2ecf20Sopenharmony_ci		vfe_try_format(line, cfg, MSM_VFE_PAD_SRC, format,
17938c2ecf20Sopenharmony_ci			       fmt->which);
17948c2ecf20Sopenharmony_ci
17958c2ecf20Sopenharmony_ci		if (line->id != VFE_LINE_PIX)
17968c2ecf20Sopenharmony_ci			return 0;
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_ci		/* Reset sink pad compose selection */
17998c2ecf20Sopenharmony_ci		sel.which = fmt->which;
18008c2ecf20Sopenharmony_ci		sel.pad = MSM_VFE_PAD_SINK;
18018c2ecf20Sopenharmony_ci		sel.target = V4L2_SEL_TGT_COMPOSE;
18028c2ecf20Sopenharmony_ci		sel.r.width = fmt->format.width;
18038c2ecf20Sopenharmony_ci		sel.r.height = fmt->format.height;
18048c2ecf20Sopenharmony_ci		ret = vfe_set_selection(sd, cfg, &sel);
18058c2ecf20Sopenharmony_ci		if (ret < 0)
18068c2ecf20Sopenharmony_ci			return ret;
18078c2ecf20Sopenharmony_ci	}
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci	return 0;
18108c2ecf20Sopenharmony_ci}
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_ci/*
18138c2ecf20Sopenharmony_ci * vfe_get_selection - Handle get selection by pads subdev method
18148c2ecf20Sopenharmony_ci * @sd: VFE V4L2 subdevice
18158c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
18168c2ecf20Sopenharmony_ci * @sel: pointer to v4l2 subdev selection structure
18178c2ecf20Sopenharmony_ci *
18188c2ecf20Sopenharmony_ci * Return -EINVAL or zero on success
18198c2ecf20Sopenharmony_ci */
18208c2ecf20Sopenharmony_cistatic int vfe_get_selection(struct v4l2_subdev *sd,
18218c2ecf20Sopenharmony_ci			     struct v4l2_subdev_pad_config *cfg,
18228c2ecf20Sopenharmony_ci			     struct v4l2_subdev_selection *sel)
18238c2ecf20Sopenharmony_ci{
18248c2ecf20Sopenharmony_ci	struct vfe_line *line = v4l2_get_subdevdata(sd);
18258c2ecf20Sopenharmony_ci	struct v4l2_subdev_format fmt = { 0 };
18268c2ecf20Sopenharmony_ci	struct v4l2_rect *rect;
18278c2ecf20Sopenharmony_ci	int ret;
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_ci	if (line->id != VFE_LINE_PIX)
18308c2ecf20Sopenharmony_ci		return -EINVAL;
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci	if (sel->pad == MSM_VFE_PAD_SINK)
18338c2ecf20Sopenharmony_ci		switch (sel->target) {
18348c2ecf20Sopenharmony_ci		case V4L2_SEL_TGT_COMPOSE_BOUNDS:
18358c2ecf20Sopenharmony_ci			fmt.pad = sel->pad;
18368c2ecf20Sopenharmony_ci			fmt.which = sel->which;
18378c2ecf20Sopenharmony_ci			ret = vfe_get_format(sd, cfg, &fmt);
18388c2ecf20Sopenharmony_ci			if (ret < 0)
18398c2ecf20Sopenharmony_ci				return ret;
18408c2ecf20Sopenharmony_ci
18418c2ecf20Sopenharmony_ci			sel->r.left = 0;
18428c2ecf20Sopenharmony_ci			sel->r.top = 0;
18438c2ecf20Sopenharmony_ci			sel->r.width = fmt.format.width;
18448c2ecf20Sopenharmony_ci			sel->r.height = fmt.format.height;
18458c2ecf20Sopenharmony_ci			break;
18468c2ecf20Sopenharmony_ci		case V4L2_SEL_TGT_COMPOSE:
18478c2ecf20Sopenharmony_ci			rect = __vfe_get_compose(line, cfg, sel->which);
18488c2ecf20Sopenharmony_ci			if (rect == NULL)
18498c2ecf20Sopenharmony_ci				return -EINVAL;
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci			sel->r = *rect;
18528c2ecf20Sopenharmony_ci			break;
18538c2ecf20Sopenharmony_ci		default:
18548c2ecf20Sopenharmony_ci			return -EINVAL;
18558c2ecf20Sopenharmony_ci		}
18568c2ecf20Sopenharmony_ci	else if (sel->pad == MSM_VFE_PAD_SRC)
18578c2ecf20Sopenharmony_ci		switch (sel->target) {
18588c2ecf20Sopenharmony_ci		case V4L2_SEL_TGT_CROP_BOUNDS:
18598c2ecf20Sopenharmony_ci			rect = __vfe_get_compose(line, cfg, sel->which);
18608c2ecf20Sopenharmony_ci			if (rect == NULL)
18618c2ecf20Sopenharmony_ci				return -EINVAL;
18628c2ecf20Sopenharmony_ci
18638c2ecf20Sopenharmony_ci			sel->r.left = rect->left;
18648c2ecf20Sopenharmony_ci			sel->r.top = rect->top;
18658c2ecf20Sopenharmony_ci			sel->r.width = rect->width;
18668c2ecf20Sopenharmony_ci			sel->r.height = rect->height;
18678c2ecf20Sopenharmony_ci			break;
18688c2ecf20Sopenharmony_ci		case V4L2_SEL_TGT_CROP:
18698c2ecf20Sopenharmony_ci			rect = __vfe_get_crop(line, cfg, sel->which);
18708c2ecf20Sopenharmony_ci			if (rect == NULL)
18718c2ecf20Sopenharmony_ci				return -EINVAL;
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_ci			sel->r = *rect;
18748c2ecf20Sopenharmony_ci			break;
18758c2ecf20Sopenharmony_ci		default:
18768c2ecf20Sopenharmony_ci			return -EINVAL;
18778c2ecf20Sopenharmony_ci		}
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci	return 0;
18808c2ecf20Sopenharmony_ci}
18818c2ecf20Sopenharmony_ci
18828c2ecf20Sopenharmony_ci/*
18838c2ecf20Sopenharmony_ci * vfe_set_selection - Handle set selection by pads subdev method
18848c2ecf20Sopenharmony_ci * @sd: VFE V4L2 subdevice
18858c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
18868c2ecf20Sopenharmony_ci * @sel: pointer to v4l2 subdev selection structure
18878c2ecf20Sopenharmony_ci *
18888c2ecf20Sopenharmony_ci * Return -EINVAL or zero on success
18898c2ecf20Sopenharmony_ci */
18908c2ecf20Sopenharmony_cistatic int vfe_set_selection(struct v4l2_subdev *sd,
18918c2ecf20Sopenharmony_ci			     struct v4l2_subdev_pad_config *cfg,
18928c2ecf20Sopenharmony_ci			     struct v4l2_subdev_selection *sel)
18938c2ecf20Sopenharmony_ci{
18948c2ecf20Sopenharmony_ci	struct vfe_line *line = v4l2_get_subdevdata(sd);
18958c2ecf20Sopenharmony_ci	struct v4l2_rect *rect;
18968c2ecf20Sopenharmony_ci	int ret;
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci	if (line->id != VFE_LINE_PIX)
18998c2ecf20Sopenharmony_ci		return -EINVAL;
19008c2ecf20Sopenharmony_ci
19018c2ecf20Sopenharmony_ci	if (sel->target == V4L2_SEL_TGT_COMPOSE &&
19028c2ecf20Sopenharmony_ci		sel->pad == MSM_VFE_PAD_SINK) {
19038c2ecf20Sopenharmony_ci		struct v4l2_subdev_selection crop = { 0 };
19048c2ecf20Sopenharmony_ci
19058c2ecf20Sopenharmony_ci		rect = __vfe_get_compose(line, cfg, sel->which);
19068c2ecf20Sopenharmony_ci		if (rect == NULL)
19078c2ecf20Sopenharmony_ci			return -EINVAL;
19088c2ecf20Sopenharmony_ci
19098c2ecf20Sopenharmony_ci		vfe_try_compose(line, cfg, &sel->r, sel->which);
19108c2ecf20Sopenharmony_ci		*rect = sel->r;
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_ci		/* Reset source crop selection */
19138c2ecf20Sopenharmony_ci		crop.which = sel->which;
19148c2ecf20Sopenharmony_ci		crop.pad = MSM_VFE_PAD_SRC;
19158c2ecf20Sopenharmony_ci		crop.target = V4L2_SEL_TGT_CROP;
19168c2ecf20Sopenharmony_ci		crop.r = *rect;
19178c2ecf20Sopenharmony_ci		ret = vfe_set_selection(sd, cfg, &crop);
19188c2ecf20Sopenharmony_ci	} else if (sel->target == V4L2_SEL_TGT_CROP &&
19198c2ecf20Sopenharmony_ci		sel->pad == MSM_VFE_PAD_SRC) {
19208c2ecf20Sopenharmony_ci		struct v4l2_subdev_format fmt = { 0 };
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_ci		rect = __vfe_get_crop(line, cfg, sel->which);
19238c2ecf20Sopenharmony_ci		if (rect == NULL)
19248c2ecf20Sopenharmony_ci			return -EINVAL;
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci		vfe_try_crop(line, cfg, &sel->r, sel->which);
19278c2ecf20Sopenharmony_ci		*rect = sel->r;
19288c2ecf20Sopenharmony_ci
19298c2ecf20Sopenharmony_ci		/* Reset source pad format width and height */
19308c2ecf20Sopenharmony_ci		fmt.which = sel->which;
19318c2ecf20Sopenharmony_ci		fmt.pad = MSM_VFE_PAD_SRC;
19328c2ecf20Sopenharmony_ci		ret = vfe_get_format(sd, cfg, &fmt);
19338c2ecf20Sopenharmony_ci		if (ret < 0)
19348c2ecf20Sopenharmony_ci			return ret;
19358c2ecf20Sopenharmony_ci
19368c2ecf20Sopenharmony_ci		fmt.format.width = rect->width;
19378c2ecf20Sopenharmony_ci		fmt.format.height = rect->height;
19388c2ecf20Sopenharmony_ci		ret = vfe_set_format(sd, cfg, &fmt);
19398c2ecf20Sopenharmony_ci	} else {
19408c2ecf20Sopenharmony_ci		ret = -EINVAL;
19418c2ecf20Sopenharmony_ci	}
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ci	return ret;
19448c2ecf20Sopenharmony_ci}
19458c2ecf20Sopenharmony_ci
19468c2ecf20Sopenharmony_ci/*
19478c2ecf20Sopenharmony_ci * vfe_init_formats - Initialize formats on all pads
19488c2ecf20Sopenharmony_ci * @sd: VFE V4L2 subdevice
19498c2ecf20Sopenharmony_ci * @fh: V4L2 subdev file handle
19508c2ecf20Sopenharmony_ci *
19518c2ecf20Sopenharmony_ci * Initialize all pad formats with default values.
19528c2ecf20Sopenharmony_ci *
19538c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise
19548c2ecf20Sopenharmony_ci */
19558c2ecf20Sopenharmony_cistatic int vfe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
19568c2ecf20Sopenharmony_ci{
19578c2ecf20Sopenharmony_ci	struct v4l2_subdev_format format = {
19588c2ecf20Sopenharmony_ci		.pad = MSM_VFE_PAD_SINK,
19598c2ecf20Sopenharmony_ci		.which = fh ? V4L2_SUBDEV_FORMAT_TRY :
19608c2ecf20Sopenharmony_ci			      V4L2_SUBDEV_FORMAT_ACTIVE,
19618c2ecf20Sopenharmony_ci		.format = {
19628c2ecf20Sopenharmony_ci			.code = MEDIA_BUS_FMT_UYVY8_2X8,
19638c2ecf20Sopenharmony_ci			.width = 1920,
19648c2ecf20Sopenharmony_ci			.height = 1080
19658c2ecf20Sopenharmony_ci		}
19668c2ecf20Sopenharmony_ci	};
19678c2ecf20Sopenharmony_ci
19688c2ecf20Sopenharmony_ci	return vfe_set_format(sd, fh ? fh->pad : NULL, &format);
19698c2ecf20Sopenharmony_ci}
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci/*
19728c2ecf20Sopenharmony_ci * msm_vfe_subdev_init - Initialize VFE device structure and resources
19738c2ecf20Sopenharmony_ci * @vfe: VFE device
19748c2ecf20Sopenharmony_ci * @res: VFE module resources table
19758c2ecf20Sopenharmony_ci *
19768c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise
19778c2ecf20Sopenharmony_ci */
19788c2ecf20Sopenharmony_ciint msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
19798c2ecf20Sopenharmony_ci			const struct resources *res, u8 id)
19808c2ecf20Sopenharmony_ci{
19818c2ecf20Sopenharmony_ci	struct device *dev = camss->dev;
19828c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
19838c2ecf20Sopenharmony_ci	struct resource *r;
19848c2ecf20Sopenharmony_ci	int i, j;
19858c2ecf20Sopenharmony_ci	int ret;
19868c2ecf20Sopenharmony_ci
19878c2ecf20Sopenharmony_ci	vfe->isr_ops.reset_ack = vfe_isr_reset_ack;
19888c2ecf20Sopenharmony_ci	vfe->isr_ops.halt_ack = vfe_isr_halt_ack;
19898c2ecf20Sopenharmony_ci	vfe->isr_ops.reg_update = vfe_isr_reg_update;
19908c2ecf20Sopenharmony_ci	vfe->isr_ops.sof = vfe_isr_sof;
19918c2ecf20Sopenharmony_ci	vfe->isr_ops.comp_done = vfe_isr_comp_done;
19928c2ecf20Sopenharmony_ci	vfe->isr_ops.wm_done = vfe_isr_wm_done;
19938c2ecf20Sopenharmony_ci
19948c2ecf20Sopenharmony_ci	if (camss->version == CAMSS_8x16)
19958c2ecf20Sopenharmony_ci		vfe->ops = &vfe_ops_4_1;
19968c2ecf20Sopenharmony_ci	else if (camss->version == CAMSS_8x96)
19978c2ecf20Sopenharmony_ci		vfe->ops = &vfe_ops_4_7;
19988c2ecf20Sopenharmony_ci	else
19998c2ecf20Sopenharmony_ci		return -EINVAL;
20008c2ecf20Sopenharmony_ci
20018c2ecf20Sopenharmony_ci	/* Memory */
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_ci	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]);
20048c2ecf20Sopenharmony_ci	vfe->base = devm_ioremap_resource(dev, r);
20058c2ecf20Sopenharmony_ci	if (IS_ERR(vfe->base)) {
20068c2ecf20Sopenharmony_ci		dev_err(dev, "could not map memory\n");
20078c2ecf20Sopenharmony_ci		return PTR_ERR(vfe->base);
20088c2ecf20Sopenharmony_ci	}
20098c2ecf20Sopenharmony_ci
20108c2ecf20Sopenharmony_ci	/* Interrupt */
20118c2ecf20Sopenharmony_ci
20128c2ecf20Sopenharmony_ci	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
20138c2ecf20Sopenharmony_ci					 res->interrupt[0]);
20148c2ecf20Sopenharmony_ci	if (!r) {
20158c2ecf20Sopenharmony_ci		dev_err(dev, "missing IRQ\n");
20168c2ecf20Sopenharmony_ci		return -EINVAL;
20178c2ecf20Sopenharmony_ci	}
20188c2ecf20Sopenharmony_ci
20198c2ecf20Sopenharmony_ci	vfe->irq = r->start;
20208c2ecf20Sopenharmony_ci	snprintf(vfe->irq_name, sizeof(vfe->irq_name), "%s_%s%d",
20218c2ecf20Sopenharmony_ci		 dev_name(dev), MSM_VFE_NAME, vfe->id);
20228c2ecf20Sopenharmony_ci	ret = devm_request_irq(dev, vfe->irq, vfe->ops->isr,
20238c2ecf20Sopenharmony_ci			       IRQF_TRIGGER_RISING, vfe->irq_name, vfe);
20248c2ecf20Sopenharmony_ci	if (ret < 0) {
20258c2ecf20Sopenharmony_ci		dev_err(dev, "request_irq failed: %d\n", ret);
20268c2ecf20Sopenharmony_ci		return ret;
20278c2ecf20Sopenharmony_ci	}
20288c2ecf20Sopenharmony_ci
20298c2ecf20Sopenharmony_ci	/* Clocks */
20308c2ecf20Sopenharmony_ci
20318c2ecf20Sopenharmony_ci	vfe->nclocks = 0;
20328c2ecf20Sopenharmony_ci	while (res->clock[vfe->nclocks])
20338c2ecf20Sopenharmony_ci		vfe->nclocks++;
20348c2ecf20Sopenharmony_ci
20358c2ecf20Sopenharmony_ci	vfe->clock = devm_kcalloc(dev, vfe->nclocks, sizeof(*vfe->clock),
20368c2ecf20Sopenharmony_ci				  GFP_KERNEL);
20378c2ecf20Sopenharmony_ci	if (!vfe->clock)
20388c2ecf20Sopenharmony_ci		return -ENOMEM;
20398c2ecf20Sopenharmony_ci
20408c2ecf20Sopenharmony_ci	for (i = 0; i < vfe->nclocks; i++) {
20418c2ecf20Sopenharmony_ci		struct camss_clock *clock = &vfe->clock[i];
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci		clock->clk = devm_clk_get(dev, res->clock[i]);
20448c2ecf20Sopenharmony_ci		if (IS_ERR(clock->clk))
20458c2ecf20Sopenharmony_ci			return PTR_ERR(clock->clk);
20468c2ecf20Sopenharmony_ci
20478c2ecf20Sopenharmony_ci		clock->name = res->clock[i];
20488c2ecf20Sopenharmony_ci
20498c2ecf20Sopenharmony_ci		clock->nfreqs = 0;
20508c2ecf20Sopenharmony_ci		while (res->clock_rate[i][clock->nfreqs])
20518c2ecf20Sopenharmony_ci			clock->nfreqs++;
20528c2ecf20Sopenharmony_ci
20538c2ecf20Sopenharmony_ci		if (!clock->nfreqs) {
20548c2ecf20Sopenharmony_ci			clock->freq = NULL;
20558c2ecf20Sopenharmony_ci			continue;
20568c2ecf20Sopenharmony_ci		}
20578c2ecf20Sopenharmony_ci
20588c2ecf20Sopenharmony_ci		clock->freq = devm_kcalloc(dev,
20598c2ecf20Sopenharmony_ci					   clock->nfreqs,
20608c2ecf20Sopenharmony_ci					   sizeof(*clock->freq),
20618c2ecf20Sopenharmony_ci					   GFP_KERNEL);
20628c2ecf20Sopenharmony_ci		if (!clock->freq)
20638c2ecf20Sopenharmony_ci			return -ENOMEM;
20648c2ecf20Sopenharmony_ci
20658c2ecf20Sopenharmony_ci		for (j = 0; j < clock->nfreqs; j++)
20668c2ecf20Sopenharmony_ci			clock->freq[j] = res->clock_rate[i][j];
20678c2ecf20Sopenharmony_ci	}
20688c2ecf20Sopenharmony_ci
20698c2ecf20Sopenharmony_ci	mutex_init(&vfe->power_lock);
20708c2ecf20Sopenharmony_ci	vfe->power_count = 0;
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci	mutex_init(&vfe->stream_lock);
20738c2ecf20Sopenharmony_ci	vfe->stream_count = 0;
20748c2ecf20Sopenharmony_ci
20758c2ecf20Sopenharmony_ci	spin_lock_init(&vfe->output_lock);
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ci	vfe->camss = camss;
20788c2ecf20Sopenharmony_ci	vfe->id = id;
20798c2ecf20Sopenharmony_ci	vfe->reg_update = 0;
20808c2ecf20Sopenharmony_ci
20818c2ecf20Sopenharmony_ci	for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) {
20828c2ecf20Sopenharmony_ci		struct vfe_line *l = &vfe->line[i];
20838c2ecf20Sopenharmony_ci
20848c2ecf20Sopenharmony_ci		l->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
20858c2ecf20Sopenharmony_ci		l->video_out.camss = camss;
20868c2ecf20Sopenharmony_ci		l->id = i;
20878c2ecf20Sopenharmony_ci		init_completion(&l->output.sof);
20888c2ecf20Sopenharmony_ci		init_completion(&l->output.reg_update);
20898c2ecf20Sopenharmony_ci
20908c2ecf20Sopenharmony_ci		if (camss->version == CAMSS_8x16) {
20918c2ecf20Sopenharmony_ci			if (i == VFE_LINE_PIX) {
20928c2ecf20Sopenharmony_ci				l->formats = formats_pix_8x16;
20938c2ecf20Sopenharmony_ci				l->nformats = ARRAY_SIZE(formats_pix_8x16);
20948c2ecf20Sopenharmony_ci			} else {
20958c2ecf20Sopenharmony_ci				l->formats = formats_rdi_8x16;
20968c2ecf20Sopenharmony_ci				l->nformats = ARRAY_SIZE(formats_rdi_8x16);
20978c2ecf20Sopenharmony_ci			}
20988c2ecf20Sopenharmony_ci		} else if (camss->version == CAMSS_8x96) {
20998c2ecf20Sopenharmony_ci			if (i == VFE_LINE_PIX) {
21008c2ecf20Sopenharmony_ci				l->formats = formats_pix_8x96;
21018c2ecf20Sopenharmony_ci				l->nformats = ARRAY_SIZE(formats_pix_8x96);
21028c2ecf20Sopenharmony_ci			} else {
21038c2ecf20Sopenharmony_ci				l->formats = formats_rdi_8x96;
21048c2ecf20Sopenharmony_ci				l->nformats = ARRAY_SIZE(formats_rdi_8x96);
21058c2ecf20Sopenharmony_ci			}
21068c2ecf20Sopenharmony_ci		} else {
21078c2ecf20Sopenharmony_ci			return -EINVAL;
21088c2ecf20Sopenharmony_ci		}
21098c2ecf20Sopenharmony_ci	}
21108c2ecf20Sopenharmony_ci
21118c2ecf20Sopenharmony_ci	init_completion(&vfe->reset_complete);
21128c2ecf20Sopenharmony_ci	init_completion(&vfe->halt_complete);
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_ci	return 0;
21158c2ecf20Sopenharmony_ci}
21168c2ecf20Sopenharmony_ci
21178c2ecf20Sopenharmony_ci/*
21188c2ecf20Sopenharmony_ci * msm_vfe_get_vfe_id - Get VFE HW module id
21198c2ecf20Sopenharmony_ci * @entity: Pointer to VFE media entity structure
21208c2ecf20Sopenharmony_ci * @id: Return CSID HW module id here
21218c2ecf20Sopenharmony_ci */
21228c2ecf20Sopenharmony_civoid msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id)
21238c2ecf20Sopenharmony_ci{
21248c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd;
21258c2ecf20Sopenharmony_ci	struct vfe_line *line;
21268c2ecf20Sopenharmony_ci	struct vfe_device *vfe;
21278c2ecf20Sopenharmony_ci
21288c2ecf20Sopenharmony_ci	sd = media_entity_to_v4l2_subdev(entity);
21298c2ecf20Sopenharmony_ci	line = v4l2_get_subdevdata(sd);
21308c2ecf20Sopenharmony_ci	vfe = to_vfe(line);
21318c2ecf20Sopenharmony_ci
21328c2ecf20Sopenharmony_ci	*id = vfe->id;
21338c2ecf20Sopenharmony_ci}
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ci/*
21368c2ecf20Sopenharmony_ci * msm_vfe_get_vfe_line_id - Get VFE line id by media entity
21378c2ecf20Sopenharmony_ci * @entity: Pointer to VFE media entity structure
21388c2ecf20Sopenharmony_ci * @id: Return VFE line id here
21398c2ecf20Sopenharmony_ci */
21408c2ecf20Sopenharmony_civoid msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id)
21418c2ecf20Sopenharmony_ci{
21428c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd;
21438c2ecf20Sopenharmony_ci	struct vfe_line *line;
21448c2ecf20Sopenharmony_ci
21458c2ecf20Sopenharmony_ci	sd = media_entity_to_v4l2_subdev(entity);
21468c2ecf20Sopenharmony_ci	line = v4l2_get_subdevdata(sd);
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_ci	*id = line->id;
21498c2ecf20Sopenharmony_ci}
21508c2ecf20Sopenharmony_ci
21518c2ecf20Sopenharmony_ci/*
21528c2ecf20Sopenharmony_ci * vfe_link_setup - Setup VFE connections
21538c2ecf20Sopenharmony_ci * @entity: Pointer to media entity structure
21548c2ecf20Sopenharmony_ci * @local: Pointer to local pad
21558c2ecf20Sopenharmony_ci * @remote: Pointer to remote pad
21568c2ecf20Sopenharmony_ci * @flags: Link flags
21578c2ecf20Sopenharmony_ci *
21588c2ecf20Sopenharmony_ci * Return 0 on success
21598c2ecf20Sopenharmony_ci */
21608c2ecf20Sopenharmony_cistatic int vfe_link_setup(struct media_entity *entity,
21618c2ecf20Sopenharmony_ci			  const struct media_pad *local,
21628c2ecf20Sopenharmony_ci			  const struct media_pad *remote, u32 flags)
21638c2ecf20Sopenharmony_ci{
21648c2ecf20Sopenharmony_ci	if (flags & MEDIA_LNK_FL_ENABLED)
21658c2ecf20Sopenharmony_ci		if (media_entity_remote_pad(local))
21668c2ecf20Sopenharmony_ci			return -EBUSY;
21678c2ecf20Sopenharmony_ci
21688c2ecf20Sopenharmony_ci	return 0;
21698c2ecf20Sopenharmony_ci}
21708c2ecf20Sopenharmony_ci
21718c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops vfe_core_ops = {
21728c2ecf20Sopenharmony_ci	.s_power = vfe_set_power,
21738c2ecf20Sopenharmony_ci};
21748c2ecf20Sopenharmony_ci
21758c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops vfe_video_ops = {
21768c2ecf20Sopenharmony_ci	.s_stream = vfe_set_stream,
21778c2ecf20Sopenharmony_ci};
21788c2ecf20Sopenharmony_ci
21798c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops vfe_pad_ops = {
21808c2ecf20Sopenharmony_ci	.enum_mbus_code = vfe_enum_mbus_code,
21818c2ecf20Sopenharmony_ci	.enum_frame_size = vfe_enum_frame_size,
21828c2ecf20Sopenharmony_ci	.get_fmt = vfe_get_format,
21838c2ecf20Sopenharmony_ci	.set_fmt = vfe_set_format,
21848c2ecf20Sopenharmony_ci	.get_selection = vfe_get_selection,
21858c2ecf20Sopenharmony_ci	.set_selection = vfe_set_selection,
21868c2ecf20Sopenharmony_ci};
21878c2ecf20Sopenharmony_ci
21888c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops vfe_v4l2_ops = {
21898c2ecf20Sopenharmony_ci	.core = &vfe_core_ops,
21908c2ecf20Sopenharmony_ci	.video = &vfe_video_ops,
21918c2ecf20Sopenharmony_ci	.pad = &vfe_pad_ops,
21928c2ecf20Sopenharmony_ci};
21938c2ecf20Sopenharmony_ci
21948c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_internal_ops vfe_v4l2_internal_ops = {
21958c2ecf20Sopenharmony_ci	.open = vfe_init_formats,
21968c2ecf20Sopenharmony_ci};
21978c2ecf20Sopenharmony_ci
21988c2ecf20Sopenharmony_cistatic const struct media_entity_operations vfe_media_ops = {
21998c2ecf20Sopenharmony_ci	.link_setup = vfe_link_setup,
22008c2ecf20Sopenharmony_ci	.link_validate = v4l2_subdev_link_validate,
22018c2ecf20Sopenharmony_ci};
22028c2ecf20Sopenharmony_ci
22038c2ecf20Sopenharmony_cistatic const struct camss_video_ops camss_vfe_video_ops = {
22048c2ecf20Sopenharmony_ci	.queue_buffer = vfe_queue_buffer,
22058c2ecf20Sopenharmony_ci	.flush_buffers = vfe_flush_buffers,
22068c2ecf20Sopenharmony_ci};
22078c2ecf20Sopenharmony_ci
22088c2ecf20Sopenharmony_ci/*
22098c2ecf20Sopenharmony_ci * msm_vfe_register_entities - Register subdev node for VFE module
22108c2ecf20Sopenharmony_ci * @vfe: VFE device
22118c2ecf20Sopenharmony_ci * @v4l2_dev: V4L2 device
22128c2ecf20Sopenharmony_ci *
22138c2ecf20Sopenharmony_ci * Initialize and register a subdev node for the VFE module. Then
22148c2ecf20Sopenharmony_ci * call msm_video_register() to register the video device node which
22158c2ecf20Sopenharmony_ci * will be connected to this subdev node. Then actually create the
22168c2ecf20Sopenharmony_ci * media link between them.
22178c2ecf20Sopenharmony_ci *
22188c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise
22198c2ecf20Sopenharmony_ci */
22208c2ecf20Sopenharmony_ciint msm_vfe_register_entities(struct vfe_device *vfe,
22218c2ecf20Sopenharmony_ci			      struct v4l2_device *v4l2_dev)
22228c2ecf20Sopenharmony_ci{
22238c2ecf20Sopenharmony_ci	struct device *dev = vfe->camss->dev;
22248c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd;
22258c2ecf20Sopenharmony_ci	struct media_pad *pads;
22268c2ecf20Sopenharmony_ci	struct camss_video *video_out;
22278c2ecf20Sopenharmony_ci	int ret;
22288c2ecf20Sopenharmony_ci	int i;
22298c2ecf20Sopenharmony_ci
22308c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vfe->line); i++) {
22318c2ecf20Sopenharmony_ci		char name[32];
22328c2ecf20Sopenharmony_ci
22338c2ecf20Sopenharmony_ci		sd = &vfe->line[i].subdev;
22348c2ecf20Sopenharmony_ci		pads = vfe->line[i].pads;
22358c2ecf20Sopenharmony_ci		video_out = &vfe->line[i].video_out;
22368c2ecf20Sopenharmony_ci
22378c2ecf20Sopenharmony_ci		v4l2_subdev_init(sd, &vfe_v4l2_ops);
22388c2ecf20Sopenharmony_ci		sd->internal_ops = &vfe_v4l2_internal_ops;
22398c2ecf20Sopenharmony_ci		sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
22408c2ecf20Sopenharmony_ci		if (i == VFE_LINE_PIX)
22418c2ecf20Sopenharmony_ci			snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s",
22428c2ecf20Sopenharmony_ci				 MSM_VFE_NAME, vfe->id, "pix");
22438c2ecf20Sopenharmony_ci		else
22448c2ecf20Sopenharmony_ci			snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s%d",
22458c2ecf20Sopenharmony_ci				 MSM_VFE_NAME, vfe->id, "rdi", i);
22468c2ecf20Sopenharmony_ci
22478c2ecf20Sopenharmony_ci		v4l2_set_subdevdata(sd, &vfe->line[i]);
22488c2ecf20Sopenharmony_ci
22498c2ecf20Sopenharmony_ci		ret = vfe_init_formats(sd, NULL);
22508c2ecf20Sopenharmony_ci		if (ret < 0) {
22518c2ecf20Sopenharmony_ci			dev_err(dev, "Failed to init format: %d\n", ret);
22528c2ecf20Sopenharmony_ci			goto error_init;
22538c2ecf20Sopenharmony_ci		}
22548c2ecf20Sopenharmony_ci
22558c2ecf20Sopenharmony_ci		pads[MSM_VFE_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
22568c2ecf20Sopenharmony_ci		pads[MSM_VFE_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
22578c2ecf20Sopenharmony_ci
22588c2ecf20Sopenharmony_ci		sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
22598c2ecf20Sopenharmony_ci		sd->entity.ops = &vfe_media_ops;
22608c2ecf20Sopenharmony_ci		ret = media_entity_pads_init(&sd->entity, MSM_VFE_PADS_NUM,
22618c2ecf20Sopenharmony_ci					     pads);
22628c2ecf20Sopenharmony_ci		if (ret < 0) {
22638c2ecf20Sopenharmony_ci			dev_err(dev, "Failed to init media entity: %d\n", ret);
22648c2ecf20Sopenharmony_ci			goto error_init;
22658c2ecf20Sopenharmony_ci		}
22668c2ecf20Sopenharmony_ci
22678c2ecf20Sopenharmony_ci		ret = v4l2_device_register_subdev(v4l2_dev, sd);
22688c2ecf20Sopenharmony_ci		if (ret < 0) {
22698c2ecf20Sopenharmony_ci			dev_err(dev, "Failed to register subdev: %d\n", ret);
22708c2ecf20Sopenharmony_ci			goto error_reg_subdev;
22718c2ecf20Sopenharmony_ci		}
22728c2ecf20Sopenharmony_ci
22738c2ecf20Sopenharmony_ci		video_out->ops = &camss_vfe_video_ops;
22748c2ecf20Sopenharmony_ci		video_out->bpl_alignment = 8;
22758c2ecf20Sopenharmony_ci		video_out->line_based = 0;
22768c2ecf20Sopenharmony_ci		if (i == VFE_LINE_PIX) {
22778c2ecf20Sopenharmony_ci			video_out->bpl_alignment = 16;
22788c2ecf20Sopenharmony_ci			video_out->line_based = 1;
22798c2ecf20Sopenharmony_ci		}
22808c2ecf20Sopenharmony_ci		snprintf(name, ARRAY_SIZE(name), "%s%d_%s%d",
22818c2ecf20Sopenharmony_ci			 MSM_VFE_NAME, vfe->id, "video", i);
22828c2ecf20Sopenharmony_ci		ret = msm_video_register(video_out, v4l2_dev, name,
22838c2ecf20Sopenharmony_ci					 i == VFE_LINE_PIX ? 1 : 0);
22848c2ecf20Sopenharmony_ci		if (ret < 0) {
22858c2ecf20Sopenharmony_ci			dev_err(dev, "Failed to register video node: %d\n",
22868c2ecf20Sopenharmony_ci				ret);
22878c2ecf20Sopenharmony_ci			goto error_reg_video;
22888c2ecf20Sopenharmony_ci		}
22898c2ecf20Sopenharmony_ci
22908c2ecf20Sopenharmony_ci		ret = media_create_pad_link(
22918c2ecf20Sopenharmony_ci				&sd->entity, MSM_VFE_PAD_SRC,
22928c2ecf20Sopenharmony_ci				&video_out->vdev.entity, 0,
22938c2ecf20Sopenharmony_ci				MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
22948c2ecf20Sopenharmony_ci		if (ret < 0) {
22958c2ecf20Sopenharmony_ci			dev_err(dev, "Failed to link %s->%s entities: %d\n",
22968c2ecf20Sopenharmony_ci				sd->entity.name, video_out->vdev.entity.name,
22978c2ecf20Sopenharmony_ci				ret);
22988c2ecf20Sopenharmony_ci			goto error_link;
22998c2ecf20Sopenharmony_ci		}
23008c2ecf20Sopenharmony_ci	}
23018c2ecf20Sopenharmony_ci
23028c2ecf20Sopenharmony_ci	return 0;
23038c2ecf20Sopenharmony_ci
23048c2ecf20Sopenharmony_cierror_link:
23058c2ecf20Sopenharmony_ci	msm_video_unregister(video_out);
23068c2ecf20Sopenharmony_ci
23078c2ecf20Sopenharmony_cierror_reg_video:
23088c2ecf20Sopenharmony_ci	v4l2_device_unregister_subdev(sd);
23098c2ecf20Sopenharmony_ci
23108c2ecf20Sopenharmony_cierror_reg_subdev:
23118c2ecf20Sopenharmony_ci	media_entity_cleanup(&sd->entity);
23128c2ecf20Sopenharmony_ci
23138c2ecf20Sopenharmony_cierror_init:
23148c2ecf20Sopenharmony_ci	for (i--; i >= 0; i--) {
23158c2ecf20Sopenharmony_ci		sd = &vfe->line[i].subdev;
23168c2ecf20Sopenharmony_ci		video_out = &vfe->line[i].video_out;
23178c2ecf20Sopenharmony_ci
23188c2ecf20Sopenharmony_ci		msm_video_unregister(video_out);
23198c2ecf20Sopenharmony_ci		v4l2_device_unregister_subdev(sd);
23208c2ecf20Sopenharmony_ci		media_entity_cleanup(&sd->entity);
23218c2ecf20Sopenharmony_ci	}
23228c2ecf20Sopenharmony_ci
23238c2ecf20Sopenharmony_ci	return ret;
23248c2ecf20Sopenharmony_ci}
23258c2ecf20Sopenharmony_ci
23268c2ecf20Sopenharmony_ci/*
23278c2ecf20Sopenharmony_ci * msm_vfe_unregister_entities - Unregister VFE module subdev node
23288c2ecf20Sopenharmony_ci * @vfe: VFE device
23298c2ecf20Sopenharmony_ci */
23308c2ecf20Sopenharmony_civoid msm_vfe_unregister_entities(struct vfe_device *vfe)
23318c2ecf20Sopenharmony_ci{
23328c2ecf20Sopenharmony_ci	int i;
23338c2ecf20Sopenharmony_ci
23348c2ecf20Sopenharmony_ci	mutex_destroy(&vfe->power_lock);
23358c2ecf20Sopenharmony_ci	mutex_destroy(&vfe->stream_lock);
23368c2ecf20Sopenharmony_ci
23378c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vfe->line); i++) {
23388c2ecf20Sopenharmony_ci		struct v4l2_subdev *sd = &vfe->line[i].subdev;
23398c2ecf20Sopenharmony_ci		struct camss_video *video_out = &vfe->line[i].video_out;
23408c2ecf20Sopenharmony_ci
23418c2ecf20Sopenharmony_ci		msm_video_unregister(video_out);
23428c2ecf20Sopenharmony_ci		v4l2_device_unregister_subdev(sd);
23438c2ecf20Sopenharmony_ci		media_entity_cleanup(&sd->entity);
23448c2ecf20Sopenharmony_ci	}
23458c2ecf20Sopenharmony_ci}
2346