162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * camss-vfe.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
862306a36Sopenharmony_ci * Copyright (C) 2015-2018 Linaro Ltd.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci#include <linux/clk.h>
1162306a36Sopenharmony_ci#include <linux/completion.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/iommu.h>
1462306a36Sopenharmony_ci#include <linux/mutex.h>
1562306a36Sopenharmony_ci#include <linux/of.h>
1662306a36Sopenharmony_ci#include <linux/platform_device.h>
1762306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1862306a36Sopenharmony_ci#include <linux/spinlock_types.h>
1962306a36Sopenharmony_ci#include <linux/spinlock.h>
2062306a36Sopenharmony_ci#include <media/media-entity.h>
2162306a36Sopenharmony_ci#include <media/v4l2-device.h>
2262306a36Sopenharmony_ci#include <media/v4l2-subdev.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "camss-vfe.h"
2562306a36Sopenharmony_ci#include "camss.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define MSM_VFE_NAME "msm_vfe"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/* VFE reset timeout */
3062306a36Sopenharmony_ci#define VFE_RESET_TIMEOUT_MS 50
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define SCALER_RATIO_MAX 16
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistruct vfe_format {
3562306a36Sopenharmony_ci	u32 code;
3662306a36Sopenharmony_ci	u8 bpp;
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic const struct vfe_format formats_rdi_8x16[] = {
4062306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_UYVY8_2X8, 8 },
4162306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_VYUY8_2X8, 8 },
4262306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_YUYV8_2X8, 8 },
4362306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_YVYU8_2X8, 8 },
4462306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SBGGR8_1X8, 8 },
4562306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGBRG8_1X8, 8 },
4662306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGRBG8_1X8, 8 },
4762306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SRGGB8_1X8, 8 },
4862306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
4962306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
5062306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
5162306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
5262306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SBGGR12_1X12, 12 },
5362306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGBRG12_1X12, 12 },
5462306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGRBG12_1X12, 12 },
5562306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
5662306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_Y10_1X10, 10 },
5762306a36Sopenharmony_ci};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic const struct vfe_format formats_pix_8x16[] = {
6062306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_UYVY8_2X8, 8 },
6162306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_VYUY8_2X8, 8 },
6262306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_YUYV8_2X8, 8 },
6362306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_YVYU8_2X8, 8 },
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic const struct vfe_format formats_rdi_8x96[] = {
6762306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_UYVY8_2X8, 8 },
6862306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_VYUY8_2X8, 8 },
6962306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_YUYV8_2X8, 8 },
7062306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_YVYU8_2X8, 8 },
7162306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SBGGR8_1X8, 8 },
7262306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGBRG8_1X8, 8 },
7362306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGRBG8_1X8, 8 },
7462306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SRGGB8_1X8, 8 },
7562306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
7662306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
7762306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
7862306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
7962306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16 },
8062306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SBGGR12_1X12, 12 },
8162306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGBRG12_1X12, 12 },
8262306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGRBG12_1X12, 12 },
8362306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
8462306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SBGGR14_1X14, 14 },
8562306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGBRG14_1X14, 14 },
8662306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGRBG14_1X14, 14 },
8762306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SRGGB14_1X14, 14 },
8862306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_Y10_1X10, 10 },
8962306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 },
9062306a36Sopenharmony_ci};
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic const struct vfe_format formats_pix_8x96[] = {
9362306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_UYVY8_2X8, 8 },
9462306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_VYUY8_2X8, 8 },
9562306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_YUYV8_2X8, 8 },
9662306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_YVYU8_2X8, 8 },
9762306a36Sopenharmony_ci};
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic const struct vfe_format formats_rdi_845[] = {
10062306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_UYVY8_2X8, 8 },
10162306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_VYUY8_2X8, 8 },
10262306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_YUYV8_2X8, 8 },
10362306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_YVYU8_2X8, 8 },
10462306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SBGGR8_1X8, 8 },
10562306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGBRG8_1X8, 8 },
10662306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGRBG8_1X8, 8 },
10762306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SRGGB8_1X8, 8 },
10862306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
10962306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
11062306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
11162306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
11262306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16 },
11362306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SBGGR12_1X12, 12 },
11462306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGBRG12_1X12, 12 },
11562306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGRBG12_1X12, 12 },
11662306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
11762306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SBGGR14_1X14, 14 },
11862306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGBRG14_1X14, 14 },
11962306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SGRBG14_1X14, 14 },
12062306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_SRGGB14_1X14, 14 },
12162306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_Y8_1X8, 8 },
12262306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_Y10_1X10, 10 },
12362306a36Sopenharmony_ci	{ MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 },
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/*
12762306a36Sopenharmony_ci * vfe_get_bpp - map media bus format to bits per pixel
12862306a36Sopenharmony_ci * @formats: supported media bus formats array
12962306a36Sopenharmony_ci * @nformats: size of @formats array
13062306a36Sopenharmony_ci * @code: media bus format code
13162306a36Sopenharmony_ci *
13262306a36Sopenharmony_ci * Return number of bits per pixel
13362306a36Sopenharmony_ci */
13462306a36Sopenharmony_cistatic u8 vfe_get_bpp(const struct vfe_format *formats,
13562306a36Sopenharmony_ci		      unsigned int nformats, u32 code)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	unsigned int i;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	for (i = 0; i < nformats; i++)
14062306a36Sopenharmony_ci		if (code == formats[i].code)
14162306a36Sopenharmony_ci			return formats[i].bpp;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	WARN(1, "Unknown format\n");
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	return formats[0].bpp;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic u32 vfe_find_code(u32 *code, unsigned int n_code,
14962306a36Sopenharmony_ci			 unsigned int index, u32 req_code)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	int i;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (!req_code && (index >= n_code))
15462306a36Sopenharmony_ci		return 0;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	for (i = 0; i < n_code; i++)
15762306a36Sopenharmony_ci		if (req_code) {
15862306a36Sopenharmony_ci			if (req_code == code[i])
15962306a36Sopenharmony_ci				return req_code;
16062306a36Sopenharmony_ci		} else {
16162306a36Sopenharmony_ci			if (i == index)
16262306a36Sopenharmony_ci				return code[i];
16362306a36Sopenharmony_ci		}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	return code[0];
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
16962306a36Sopenharmony_ci			    unsigned int index, u32 src_req_code)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (vfe->camss->version == CAMSS_8x16)
17462306a36Sopenharmony_ci		switch (sink_code) {
17562306a36Sopenharmony_ci		case MEDIA_BUS_FMT_YUYV8_2X8:
17662306a36Sopenharmony_ci		{
17762306a36Sopenharmony_ci			u32 src_code[] = {
17862306a36Sopenharmony_ci				MEDIA_BUS_FMT_YUYV8_2X8,
17962306a36Sopenharmony_ci				MEDIA_BUS_FMT_YUYV8_1_5X8,
18062306a36Sopenharmony_ci			};
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
18362306a36Sopenharmony_ci					     index, src_req_code);
18462306a36Sopenharmony_ci		}
18562306a36Sopenharmony_ci		case MEDIA_BUS_FMT_YVYU8_2X8:
18662306a36Sopenharmony_ci		{
18762306a36Sopenharmony_ci			u32 src_code[] = {
18862306a36Sopenharmony_ci				MEDIA_BUS_FMT_YVYU8_2X8,
18962306a36Sopenharmony_ci				MEDIA_BUS_FMT_YVYU8_1_5X8,
19062306a36Sopenharmony_ci			};
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
19362306a36Sopenharmony_ci					     index, src_req_code);
19462306a36Sopenharmony_ci		}
19562306a36Sopenharmony_ci		case MEDIA_BUS_FMT_UYVY8_2X8:
19662306a36Sopenharmony_ci		{
19762306a36Sopenharmony_ci			u32 src_code[] = {
19862306a36Sopenharmony_ci				MEDIA_BUS_FMT_UYVY8_2X8,
19962306a36Sopenharmony_ci				MEDIA_BUS_FMT_UYVY8_1_5X8,
20062306a36Sopenharmony_ci			};
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
20362306a36Sopenharmony_ci					     index, src_req_code);
20462306a36Sopenharmony_ci		}
20562306a36Sopenharmony_ci		case MEDIA_BUS_FMT_VYUY8_2X8:
20662306a36Sopenharmony_ci		{
20762306a36Sopenharmony_ci			u32 src_code[] = {
20862306a36Sopenharmony_ci				MEDIA_BUS_FMT_VYUY8_2X8,
20962306a36Sopenharmony_ci				MEDIA_BUS_FMT_VYUY8_1_5X8,
21062306a36Sopenharmony_ci			};
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
21362306a36Sopenharmony_ci					     index, src_req_code);
21462306a36Sopenharmony_ci		}
21562306a36Sopenharmony_ci		default:
21662306a36Sopenharmony_ci			if (index > 0)
21762306a36Sopenharmony_ci				return 0;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci			return sink_code;
22062306a36Sopenharmony_ci		}
22162306a36Sopenharmony_ci	else if (vfe->camss->version == CAMSS_8x96 ||
22262306a36Sopenharmony_ci		 vfe->camss->version == CAMSS_660 ||
22362306a36Sopenharmony_ci		 vfe->camss->version == CAMSS_845 ||
22462306a36Sopenharmony_ci		 vfe->camss->version == CAMSS_8250)
22562306a36Sopenharmony_ci		switch (sink_code) {
22662306a36Sopenharmony_ci		case MEDIA_BUS_FMT_YUYV8_2X8:
22762306a36Sopenharmony_ci		{
22862306a36Sopenharmony_ci			u32 src_code[] = {
22962306a36Sopenharmony_ci				MEDIA_BUS_FMT_YUYV8_2X8,
23062306a36Sopenharmony_ci				MEDIA_BUS_FMT_YVYU8_2X8,
23162306a36Sopenharmony_ci				MEDIA_BUS_FMT_UYVY8_2X8,
23262306a36Sopenharmony_ci				MEDIA_BUS_FMT_VYUY8_2X8,
23362306a36Sopenharmony_ci				MEDIA_BUS_FMT_YUYV8_1_5X8,
23462306a36Sopenharmony_ci			};
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
23762306a36Sopenharmony_ci					     index, src_req_code);
23862306a36Sopenharmony_ci		}
23962306a36Sopenharmony_ci		case MEDIA_BUS_FMT_YVYU8_2X8:
24062306a36Sopenharmony_ci		{
24162306a36Sopenharmony_ci			u32 src_code[] = {
24262306a36Sopenharmony_ci				MEDIA_BUS_FMT_YVYU8_2X8,
24362306a36Sopenharmony_ci				MEDIA_BUS_FMT_YUYV8_2X8,
24462306a36Sopenharmony_ci				MEDIA_BUS_FMT_UYVY8_2X8,
24562306a36Sopenharmony_ci				MEDIA_BUS_FMT_VYUY8_2X8,
24662306a36Sopenharmony_ci				MEDIA_BUS_FMT_YVYU8_1_5X8,
24762306a36Sopenharmony_ci			};
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
25062306a36Sopenharmony_ci					     index, src_req_code);
25162306a36Sopenharmony_ci		}
25262306a36Sopenharmony_ci		case MEDIA_BUS_FMT_UYVY8_2X8:
25362306a36Sopenharmony_ci		{
25462306a36Sopenharmony_ci			u32 src_code[] = {
25562306a36Sopenharmony_ci				MEDIA_BUS_FMT_UYVY8_2X8,
25662306a36Sopenharmony_ci				MEDIA_BUS_FMT_YUYV8_2X8,
25762306a36Sopenharmony_ci				MEDIA_BUS_FMT_YVYU8_2X8,
25862306a36Sopenharmony_ci				MEDIA_BUS_FMT_VYUY8_2X8,
25962306a36Sopenharmony_ci				MEDIA_BUS_FMT_UYVY8_1_5X8,
26062306a36Sopenharmony_ci			};
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
26362306a36Sopenharmony_ci					     index, src_req_code);
26462306a36Sopenharmony_ci		}
26562306a36Sopenharmony_ci		case MEDIA_BUS_FMT_VYUY8_2X8:
26662306a36Sopenharmony_ci		{
26762306a36Sopenharmony_ci			u32 src_code[] = {
26862306a36Sopenharmony_ci				MEDIA_BUS_FMT_VYUY8_2X8,
26962306a36Sopenharmony_ci				MEDIA_BUS_FMT_YUYV8_2X8,
27062306a36Sopenharmony_ci				MEDIA_BUS_FMT_YVYU8_2X8,
27162306a36Sopenharmony_ci				MEDIA_BUS_FMT_UYVY8_2X8,
27262306a36Sopenharmony_ci				MEDIA_BUS_FMT_VYUY8_1_5X8,
27362306a36Sopenharmony_ci			};
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci			return vfe_find_code(src_code, ARRAY_SIZE(src_code),
27662306a36Sopenharmony_ci					     index, src_req_code);
27762306a36Sopenharmony_ci		}
27862306a36Sopenharmony_ci		default:
27962306a36Sopenharmony_ci			if (index > 0)
28062306a36Sopenharmony_ci				return 0;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci			return sink_code;
28362306a36Sopenharmony_ci		}
28462306a36Sopenharmony_ci	else
28562306a36Sopenharmony_ci		return 0;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ciint vfe_reset(struct vfe_device *vfe)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	unsigned long time;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	reinit_completion(&vfe->reset_complete);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	vfe->ops->global_reset(vfe);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	time = wait_for_completion_timeout(&vfe->reset_complete,
29762306a36Sopenharmony_ci		msecs_to_jiffies(VFE_RESET_TIMEOUT_MS));
29862306a36Sopenharmony_ci	if (!time) {
29962306a36Sopenharmony_ci		dev_err(vfe->camss->dev, "VFE reset timeout\n");
30062306a36Sopenharmony_ci		return -EIO;
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	return 0;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic void vfe_init_outputs(struct vfe_device *vfe)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	int i;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	for (i = 0; i < vfe->line_num; i++) {
31162306a36Sopenharmony_ci		struct vfe_output *output = &vfe->line[i].output;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		output->state = VFE_OUTPUT_OFF;
31462306a36Sopenharmony_ci		output->buf[0] = NULL;
31562306a36Sopenharmony_ci		output->buf[1] = NULL;
31662306a36Sopenharmony_ci		INIT_LIST_HEAD(&output->pending_bufs);
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic void vfe_reset_output_maps(struct vfe_device *vfe)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	int i;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++)
32562306a36Sopenharmony_ci		vfe->wm_output_map[i] = VFE_LINE_NONE;
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ciint vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	int ret = -EBUSY;
33162306a36Sopenharmony_ci	int i;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++) {
33462306a36Sopenharmony_ci		if (vfe->wm_output_map[i] == VFE_LINE_NONE) {
33562306a36Sopenharmony_ci			vfe->wm_output_map[i] = line_id;
33662306a36Sopenharmony_ci			ret = i;
33762306a36Sopenharmony_ci			break;
33862306a36Sopenharmony_ci		}
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	return ret;
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ciint vfe_release_wm(struct vfe_device *vfe, u8 wm)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	if (wm >= ARRAY_SIZE(vfe->wm_output_map))
34762306a36Sopenharmony_ci		return -EINVAL;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	vfe->wm_output_map[wm] = VFE_LINE_NONE;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	return 0;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistruct camss_buffer *vfe_buf_get_pending(struct vfe_output *output)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	struct camss_buffer *buffer = NULL;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	if (!list_empty(&output->pending_bufs)) {
35962306a36Sopenharmony_ci		buffer = list_first_entry(&output->pending_bufs,
36062306a36Sopenharmony_ci					  struct camss_buffer,
36162306a36Sopenharmony_ci					  queue);
36262306a36Sopenharmony_ci		list_del(&buffer->queue);
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	return buffer;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_civoid vfe_buf_add_pending(struct vfe_output *output,
36962306a36Sopenharmony_ci			 struct camss_buffer *buffer)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	INIT_LIST_HEAD(&buffer->queue);
37262306a36Sopenharmony_ci	list_add_tail(&buffer->queue, &output->pending_bufs);
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci/*
37662306a36Sopenharmony_ci * vfe_buf_flush_pending - Flush all pending buffers.
37762306a36Sopenharmony_ci * @output: VFE output
37862306a36Sopenharmony_ci * @state: vb2 buffer state
37962306a36Sopenharmony_ci */
38062306a36Sopenharmony_cistatic void vfe_buf_flush_pending(struct vfe_output *output,
38162306a36Sopenharmony_ci				  enum vb2_buffer_state state)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct camss_buffer *buf;
38462306a36Sopenharmony_ci	struct camss_buffer *t;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	list_for_each_entry_safe(buf, t, &output->pending_bufs, queue) {
38762306a36Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, state);
38862306a36Sopenharmony_ci		list_del(&buf->queue);
38962306a36Sopenharmony_ci	}
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ciint vfe_put_output(struct vfe_line *line)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
39562306a36Sopenharmony_ci	struct vfe_output *output = &line->output;
39662306a36Sopenharmony_ci	unsigned long flags;
39762306a36Sopenharmony_ci	unsigned int i;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	spin_lock_irqsave(&vfe->output_lock, flags);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	for (i = 0; i < output->wm_num; i++)
40262306a36Sopenharmony_ci		vfe_release_wm(vfe, output->wm_idx[i]);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	output->state = VFE_OUTPUT_OFF;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
40762306a36Sopenharmony_ci	return 0;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci/**
41162306a36Sopenharmony_ci * vfe_isr_comp_done() - Process composite image done interrupt
41262306a36Sopenharmony_ci * @vfe: VFE Device
41362306a36Sopenharmony_ci * @comp: Composite image id
41462306a36Sopenharmony_ci */
41562306a36Sopenharmony_civoid vfe_isr_comp_done(struct vfe_device *vfe, u8 comp)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	unsigned int i;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++)
42062306a36Sopenharmony_ci		if (vfe->wm_output_map[i] == VFE_LINE_PIX) {
42162306a36Sopenharmony_ci			vfe->isr_ops.wm_done(vfe, i);
42262306a36Sopenharmony_ci			break;
42362306a36Sopenharmony_ci		}
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_civoid vfe_isr_reset_ack(struct vfe_device *vfe)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	complete(&vfe->reset_complete);
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci/*
43262306a36Sopenharmony_ci * vfe_set_clock_rates - Calculate and set clock rates on VFE module
43362306a36Sopenharmony_ci * @vfe: VFE device
43462306a36Sopenharmony_ci *
43562306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise
43662306a36Sopenharmony_ci */
43762306a36Sopenharmony_cistatic int vfe_set_clock_rates(struct vfe_device *vfe)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	struct device *dev = vfe->camss->dev;
44062306a36Sopenharmony_ci	u64 pixel_clock[VFE_LINE_NUM_MAX];
44162306a36Sopenharmony_ci	int i, j;
44262306a36Sopenharmony_ci	int ret;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) {
44562306a36Sopenharmony_ci		ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity,
44662306a36Sopenharmony_ci					    &pixel_clock[i]);
44762306a36Sopenharmony_ci		if (ret)
44862306a36Sopenharmony_ci			pixel_clock[i] = 0;
44962306a36Sopenharmony_ci	}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	for (i = 0; i < vfe->nclocks; i++) {
45262306a36Sopenharmony_ci		struct camss_clock *clock = &vfe->clock[i];
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci		if (!strcmp(clock->name, "vfe0") ||
45562306a36Sopenharmony_ci		    !strcmp(clock->name, "vfe1") ||
45662306a36Sopenharmony_ci		    !strcmp(clock->name, "vfe_lite")) {
45762306a36Sopenharmony_ci			u64 min_rate = 0;
45862306a36Sopenharmony_ci			long rate;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci			for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) {
46162306a36Sopenharmony_ci				u32 tmp;
46262306a36Sopenharmony_ci				u8 bpp;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci				if (j == VFE_LINE_PIX) {
46562306a36Sopenharmony_ci					tmp = pixel_clock[j];
46662306a36Sopenharmony_ci				} else {
46762306a36Sopenharmony_ci					struct vfe_line *l = &vfe->line[j];
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci					bpp = vfe_get_bpp(l->formats,
47062306a36Sopenharmony_ci						l->nformats,
47162306a36Sopenharmony_ci						l->fmt[MSM_VFE_PAD_SINK].code);
47262306a36Sopenharmony_ci					tmp = pixel_clock[j] * bpp / 64;
47362306a36Sopenharmony_ci				}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci				if (min_rate < tmp)
47662306a36Sopenharmony_ci					min_rate = tmp;
47762306a36Sopenharmony_ci			}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci			camss_add_clock_margin(&min_rate);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci			for (j = 0; j < clock->nfreqs; j++)
48262306a36Sopenharmony_ci				if (min_rate < clock->freq[j])
48362306a36Sopenharmony_ci					break;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci			if (j == clock->nfreqs) {
48662306a36Sopenharmony_ci				dev_err(dev,
48762306a36Sopenharmony_ci					"Pixel clock is too high for VFE");
48862306a36Sopenharmony_ci				return -EINVAL;
48962306a36Sopenharmony_ci			}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci			/* if sensor pixel clock is not available */
49262306a36Sopenharmony_ci			/* set highest possible VFE clock rate */
49362306a36Sopenharmony_ci			if (min_rate == 0)
49462306a36Sopenharmony_ci				j = clock->nfreqs - 1;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci			rate = clk_round_rate(clock->clk, clock->freq[j]);
49762306a36Sopenharmony_ci			if (rate < 0) {
49862306a36Sopenharmony_ci				dev_err(dev, "clk round rate failed: %ld\n",
49962306a36Sopenharmony_ci					rate);
50062306a36Sopenharmony_ci				return -EINVAL;
50162306a36Sopenharmony_ci			}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci			ret = clk_set_rate(clock->clk, rate);
50462306a36Sopenharmony_ci			if (ret < 0) {
50562306a36Sopenharmony_ci				dev_err(dev, "clk set rate failed: %d\n", ret);
50662306a36Sopenharmony_ci				return ret;
50762306a36Sopenharmony_ci			}
50862306a36Sopenharmony_ci		}
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	return 0;
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci/*
51562306a36Sopenharmony_ci * vfe_check_clock_rates - Check current clock rates on VFE module
51662306a36Sopenharmony_ci * @vfe: VFE device
51762306a36Sopenharmony_ci *
51862306a36Sopenharmony_ci * Return 0 if current clock rates are suitable for a new pipeline
51962306a36Sopenharmony_ci * or a negative error code otherwise
52062306a36Sopenharmony_ci */
52162306a36Sopenharmony_cistatic int vfe_check_clock_rates(struct vfe_device *vfe)
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	u64 pixel_clock[VFE_LINE_NUM_MAX];
52462306a36Sopenharmony_ci	int i, j;
52562306a36Sopenharmony_ci	int ret;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) {
52862306a36Sopenharmony_ci		ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity,
52962306a36Sopenharmony_ci					    &pixel_clock[i]);
53062306a36Sopenharmony_ci		if (ret)
53162306a36Sopenharmony_ci			pixel_clock[i] = 0;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	for (i = 0; i < vfe->nclocks; i++) {
53562306a36Sopenharmony_ci		struct camss_clock *clock = &vfe->clock[i];
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci		if (!strcmp(clock->name, "vfe0") ||
53862306a36Sopenharmony_ci		    !strcmp(clock->name, "vfe1") ||
53962306a36Sopenharmony_ci		    !strcmp(clock->name, "vfe_lite")) {
54062306a36Sopenharmony_ci			u64 min_rate = 0;
54162306a36Sopenharmony_ci			unsigned long rate;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci			for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) {
54462306a36Sopenharmony_ci				u32 tmp;
54562306a36Sopenharmony_ci				u8 bpp;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci				if (j == VFE_LINE_PIX) {
54862306a36Sopenharmony_ci					tmp = pixel_clock[j];
54962306a36Sopenharmony_ci				} else {
55062306a36Sopenharmony_ci					struct vfe_line *l = &vfe->line[j];
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci					bpp = vfe_get_bpp(l->formats,
55362306a36Sopenharmony_ci						l->nformats,
55462306a36Sopenharmony_ci						l->fmt[MSM_VFE_PAD_SINK].code);
55562306a36Sopenharmony_ci					tmp = pixel_clock[j] * bpp / 64;
55662306a36Sopenharmony_ci				}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci				if (min_rate < tmp)
55962306a36Sopenharmony_ci					min_rate = tmp;
56062306a36Sopenharmony_ci			}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci			camss_add_clock_margin(&min_rate);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci			rate = clk_get_rate(clock->clk);
56562306a36Sopenharmony_ci			if (rate < min_rate)
56662306a36Sopenharmony_ci				return -EBUSY;
56762306a36Sopenharmony_ci		}
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	return 0;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci/*
57462306a36Sopenharmony_ci * vfe_get - Power up and reset VFE module
57562306a36Sopenharmony_ci * @vfe: VFE Device
57662306a36Sopenharmony_ci *
57762306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise
57862306a36Sopenharmony_ci */
57962306a36Sopenharmony_ciint vfe_get(struct vfe_device *vfe)
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	int ret;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	mutex_lock(&vfe->power_lock);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	if (vfe->power_count == 0) {
58662306a36Sopenharmony_ci		ret = vfe->ops->pm_domain_on(vfe);
58762306a36Sopenharmony_ci		if (ret < 0)
58862306a36Sopenharmony_ci			goto error_pm_domain;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci		ret = pm_runtime_resume_and_get(vfe->camss->dev);
59162306a36Sopenharmony_ci		if (ret < 0)
59262306a36Sopenharmony_ci			goto error_domain_off;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci		ret = vfe_set_clock_rates(vfe);
59562306a36Sopenharmony_ci		if (ret < 0)
59662306a36Sopenharmony_ci			goto error_pm_runtime_get;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci		ret = camss_enable_clocks(vfe->nclocks, vfe->clock,
59962306a36Sopenharmony_ci					  vfe->camss->dev);
60062306a36Sopenharmony_ci		if (ret < 0)
60162306a36Sopenharmony_ci			goto error_pm_runtime_get;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci		ret = vfe_reset(vfe);
60462306a36Sopenharmony_ci		if (ret < 0)
60562306a36Sopenharmony_ci			goto error_reset;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci		vfe_reset_output_maps(vfe);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci		vfe_init_outputs(vfe);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci		vfe->ops->hw_version(vfe);
61262306a36Sopenharmony_ci	} else {
61362306a36Sopenharmony_ci		ret = vfe_check_clock_rates(vfe);
61462306a36Sopenharmony_ci		if (ret < 0)
61562306a36Sopenharmony_ci			goto error_pm_domain;
61662306a36Sopenharmony_ci	}
61762306a36Sopenharmony_ci	vfe->power_count++;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	mutex_unlock(&vfe->power_lock);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	return 0;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cierror_reset:
62462306a36Sopenharmony_ci	camss_disable_clocks(vfe->nclocks, vfe->clock);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_cierror_pm_runtime_get:
62762306a36Sopenharmony_ci	pm_runtime_put_sync(vfe->camss->dev);
62862306a36Sopenharmony_cierror_domain_off:
62962306a36Sopenharmony_ci	vfe->ops->pm_domain_off(vfe);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_cierror_pm_domain:
63262306a36Sopenharmony_ci	mutex_unlock(&vfe->power_lock);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	return ret;
63562306a36Sopenharmony_ci}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci/*
63862306a36Sopenharmony_ci * vfe_put - Power down VFE module
63962306a36Sopenharmony_ci * @vfe: VFE Device
64062306a36Sopenharmony_ci */
64162306a36Sopenharmony_civoid vfe_put(struct vfe_device *vfe)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	mutex_lock(&vfe->power_lock);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	if (vfe->power_count == 0) {
64662306a36Sopenharmony_ci		dev_err(vfe->camss->dev, "vfe power off on power_count == 0\n");
64762306a36Sopenharmony_ci		goto exit;
64862306a36Sopenharmony_ci	} else if (vfe->power_count == 1) {
64962306a36Sopenharmony_ci		if (vfe->was_streaming) {
65062306a36Sopenharmony_ci			vfe->was_streaming = 0;
65162306a36Sopenharmony_ci			vfe->ops->vfe_halt(vfe);
65262306a36Sopenharmony_ci		}
65362306a36Sopenharmony_ci		camss_disable_clocks(vfe->nclocks, vfe->clock);
65462306a36Sopenharmony_ci		pm_runtime_put_sync(vfe->camss->dev);
65562306a36Sopenharmony_ci		vfe->ops->pm_domain_off(vfe);
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	vfe->power_count--;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ciexit:
66162306a36Sopenharmony_ci	mutex_unlock(&vfe->power_lock);
66262306a36Sopenharmony_ci}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci/*
66562306a36Sopenharmony_ci * vfe_flush_buffers - Return all vb2 buffers
66662306a36Sopenharmony_ci * @vid: Video device structure
66762306a36Sopenharmony_ci * @state: vb2 buffer state of the returned buffers
66862306a36Sopenharmony_ci *
66962306a36Sopenharmony_ci * Return all buffers to vb2. This includes queued pending buffers (still
67062306a36Sopenharmony_ci * unused) and any buffers given to the hardware but again still not used.
67162306a36Sopenharmony_ci *
67262306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise
67362306a36Sopenharmony_ci */
67462306a36Sopenharmony_ciint vfe_flush_buffers(struct camss_video *vid,
67562306a36Sopenharmony_ci		      enum vb2_buffer_state state)
67662306a36Sopenharmony_ci{
67762306a36Sopenharmony_ci	struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
67862306a36Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
67962306a36Sopenharmony_ci	struct vfe_output *output;
68062306a36Sopenharmony_ci	unsigned long flags;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	output = &line->output;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	spin_lock_irqsave(&vfe->output_lock, flags);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	vfe_buf_flush_pending(output, state);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	if (output->buf[0])
68962306a36Sopenharmony_ci		vb2_buffer_done(&output->buf[0]->vb.vb2_buf, state);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	if (output->buf[1])
69262306a36Sopenharmony_ci		vb2_buffer_done(&output->buf[1]->vb.vb2_buf, state);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	if (output->last_buffer) {
69562306a36Sopenharmony_ci		vb2_buffer_done(&output->last_buffer->vb.vb2_buf, state);
69662306a36Sopenharmony_ci		output->last_buffer = NULL;
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	return 0;
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci/*
70562306a36Sopenharmony_ci * vfe_set_power - Power on/off VFE module
70662306a36Sopenharmony_ci * @sd: VFE V4L2 subdevice
70762306a36Sopenharmony_ci * @on: Requested power state
70862306a36Sopenharmony_ci *
70962306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise
71062306a36Sopenharmony_ci */
71162306a36Sopenharmony_cistatic int vfe_set_power(struct v4l2_subdev *sd, int on)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	struct vfe_line *line = v4l2_get_subdevdata(sd);
71462306a36Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
71562306a36Sopenharmony_ci	int ret;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	if (on) {
71862306a36Sopenharmony_ci		ret = vfe_get(vfe);
71962306a36Sopenharmony_ci		if (ret < 0)
72062306a36Sopenharmony_ci			return ret;
72162306a36Sopenharmony_ci	} else {
72262306a36Sopenharmony_ci		vfe_put(vfe);
72362306a36Sopenharmony_ci	}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	return 0;
72662306a36Sopenharmony_ci}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci/*
72962306a36Sopenharmony_ci * vfe_set_stream - Enable/disable streaming on VFE module
73062306a36Sopenharmony_ci * @sd: VFE V4L2 subdevice
73162306a36Sopenharmony_ci * @enable: Requested streaming state
73262306a36Sopenharmony_ci *
73362306a36Sopenharmony_ci * Main configuration of VFE module is triggered here.
73462306a36Sopenharmony_ci *
73562306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise
73662306a36Sopenharmony_ci */
73762306a36Sopenharmony_cistatic int vfe_set_stream(struct v4l2_subdev *sd, int enable)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	struct vfe_line *line = v4l2_get_subdevdata(sd);
74062306a36Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
74162306a36Sopenharmony_ci	int ret;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	if (enable) {
74462306a36Sopenharmony_ci		line->output.state = VFE_OUTPUT_RESERVED;
74562306a36Sopenharmony_ci		ret = vfe->ops->vfe_enable(line);
74662306a36Sopenharmony_ci		if (ret < 0)
74762306a36Sopenharmony_ci			dev_err(vfe->camss->dev,
74862306a36Sopenharmony_ci				"Failed to enable vfe outputs\n");
74962306a36Sopenharmony_ci	} else {
75062306a36Sopenharmony_ci		ret = vfe->ops->vfe_disable(line);
75162306a36Sopenharmony_ci		if (ret < 0)
75262306a36Sopenharmony_ci			dev_err(vfe->camss->dev,
75362306a36Sopenharmony_ci				"Failed to disable vfe outputs\n");
75462306a36Sopenharmony_ci	}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	return ret;
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci/*
76062306a36Sopenharmony_ci * __vfe_get_format - Get pointer to format structure
76162306a36Sopenharmony_ci * @line: VFE line
76262306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
76362306a36Sopenharmony_ci * @pad: pad from which format is requested
76462306a36Sopenharmony_ci * @which: TRY or ACTIVE format
76562306a36Sopenharmony_ci *
76662306a36Sopenharmony_ci * Return pointer to TRY or ACTIVE format structure
76762306a36Sopenharmony_ci */
76862306a36Sopenharmony_cistatic struct v4l2_mbus_framefmt *
76962306a36Sopenharmony_ci__vfe_get_format(struct vfe_line *line,
77062306a36Sopenharmony_ci		 struct v4l2_subdev_state *sd_state,
77162306a36Sopenharmony_ci		 unsigned int pad,
77262306a36Sopenharmony_ci		 enum v4l2_subdev_format_whence which)
77362306a36Sopenharmony_ci{
77462306a36Sopenharmony_ci	if (which == V4L2_SUBDEV_FORMAT_TRY)
77562306a36Sopenharmony_ci		return v4l2_subdev_get_try_format(&line->subdev, sd_state,
77662306a36Sopenharmony_ci						  pad);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	return &line->fmt[pad];
77962306a36Sopenharmony_ci}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci/*
78262306a36Sopenharmony_ci * __vfe_get_compose - Get pointer to compose selection structure
78362306a36Sopenharmony_ci * @line: VFE line
78462306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
78562306a36Sopenharmony_ci * @which: TRY or ACTIVE format
78662306a36Sopenharmony_ci *
78762306a36Sopenharmony_ci * Return pointer to TRY or ACTIVE compose rectangle structure
78862306a36Sopenharmony_ci */
78962306a36Sopenharmony_cistatic struct v4l2_rect *
79062306a36Sopenharmony_ci__vfe_get_compose(struct vfe_line *line,
79162306a36Sopenharmony_ci		  struct v4l2_subdev_state *sd_state,
79262306a36Sopenharmony_ci		  enum v4l2_subdev_format_whence which)
79362306a36Sopenharmony_ci{
79462306a36Sopenharmony_ci	if (which == V4L2_SUBDEV_FORMAT_TRY)
79562306a36Sopenharmony_ci		return v4l2_subdev_get_try_compose(&line->subdev, sd_state,
79662306a36Sopenharmony_ci						   MSM_VFE_PAD_SINK);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	return &line->compose;
79962306a36Sopenharmony_ci}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci/*
80262306a36Sopenharmony_ci * __vfe_get_crop - Get pointer to crop selection structure
80362306a36Sopenharmony_ci * @line: VFE line
80462306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
80562306a36Sopenharmony_ci * @which: TRY or ACTIVE format
80662306a36Sopenharmony_ci *
80762306a36Sopenharmony_ci * Return pointer to TRY or ACTIVE crop rectangle structure
80862306a36Sopenharmony_ci */
80962306a36Sopenharmony_cistatic struct v4l2_rect *
81062306a36Sopenharmony_ci__vfe_get_crop(struct vfe_line *line,
81162306a36Sopenharmony_ci	       struct v4l2_subdev_state *sd_state,
81262306a36Sopenharmony_ci	       enum v4l2_subdev_format_whence which)
81362306a36Sopenharmony_ci{
81462306a36Sopenharmony_ci	if (which == V4L2_SUBDEV_FORMAT_TRY)
81562306a36Sopenharmony_ci		return v4l2_subdev_get_try_crop(&line->subdev, sd_state,
81662306a36Sopenharmony_ci						MSM_VFE_PAD_SRC);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	return &line->crop;
81962306a36Sopenharmony_ci}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci/*
82262306a36Sopenharmony_ci * vfe_try_format - Handle try format by pad subdev method
82362306a36Sopenharmony_ci * @line: VFE line
82462306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
82562306a36Sopenharmony_ci * @pad: pad on which format is requested
82662306a36Sopenharmony_ci * @fmt: pointer to v4l2 format structure
82762306a36Sopenharmony_ci * @which: wanted subdev format
82862306a36Sopenharmony_ci */
82962306a36Sopenharmony_cistatic void vfe_try_format(struct vfe_line *line,
83062306a36Sopenharmony_ci			   struct v4l2_subdev_state *sd_state,
83162306a36Sopenharmony_ci			   unsigned int pad,
83262306a36Sopenharmony_ci			   struct v4l2_mbus_framefmt *fmt,
83362306a36Sopenharmony_ci			   enum v4l2_subdev_format_whence which)
83462306a36Sopenharmony_ci{
83562306a36Sopenharmony_ci	unsigned int i;
83662306a36Sopenharmony_ci	u32 code;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	switch (pad) {
83962306a36Sopenharmony_ci	case MSM_VFE_PAD_SINK:
84062306a36Sopenharmony_ci		/* Set format on sink pad */
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci		for (i = 0; i < line->nformats; i++)
84362306a36Sopenharmony_ci			if (fmt->code == line->formats[i].code)
84462306a36Sopenharmony_ci				break;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci		/* If not found, use UYVY as default */
84762306a36Sopenharmony_ci		if (i >= line->nformats)
84862306a36Sopenharmony_ci			fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci		fmt->width = clamp_t(u32, fmt->width, 1, 8191);
85162306a36Sopenharmony_ci		fmt->height = clamp_t(u32, fmt->height, 1, 8191);
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci		fmt->field = V4L2_FIELD_NONE;
85462306a36Sopenharmony_ci		fmt->colorspace = V4L2_COLORSPACE_SRGB;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci		break;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	case MSM_VFE_PAD_SRC:
85962306a36Sopenharmony_ci		/* Set and return a format same as sink pad */
86062306a36Sopenharmony_ci		code = fmt->code;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci		*fmt = *__vfe_get_format(line, sd_state, MSM_VFE_PAD_SINK,
86362306a36Sopenharmony_ci					 which);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci		fmt->code = vfe_src_pad_code(line, fmt->code, 0, code);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci		if (line->id == VFE_LINE_PIX) {
86862306a36Sopenharmony_ci			struct v4l2_rect *rect;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci			rect = __vfe_get_crop(line, sd_state, which);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci			fmt->width = rect->width;
87362306a36Sopenharmony_ci			fmt->height = rect->height;
87462306a36Sopenharmony_ci		}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci		break;
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	fmt->colorspace = V4L2_COLORSPACE_SRGB;
88062306a36Sopenharmony_ci}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci/*
88362306a36Sopenharmony_ci * vfe_try_compose - Handle try compose selection by pad subdev method
88462306a36Sopenharmony_ci * @line: VFE line
88562306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
88662306a36Sopenharmony_ci * @rect: pointer to v4l2 rect structure
88762306a36Sopenharmony_ci * @which: wanted subdev format
88862306a36Sopenharmony_ci */
88962306a36Sopenharmony_cistatic void vfe_try_compose(struct vfe_line *line,
89062306a36Sopenharmony_ci			    struct v4l2_subdev_state *sd_state,
89162306a36Sopenharmony_ci			    struct v4l2_rect *rect,
89262306a36Sopenharmony_ci			    enum v4l2_subdev_format_whence which)
89362306a36Sopenharmony_ci{
89462306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *fmt;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	fmt = __vfe_get_format(line, sd_state, MSM_VFE_PAD_SINK, which);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	if (rect->width > fmt->width)
89962306a36Sopenharmony_ci		rect->width = fmt->width;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	if (rect->height > fmt->height)
90262306a36Sopenharmony_ci		rect->height = fmt->height;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	if (fmt->width > rect->width * SCALER_RATIO_MAX)
90562306a36Sopenharmony_ci		rect->width = (fmt->width + SCALER_RATIO_MAX - 1) /
90662306a36Sopenharmony_ci							SCALER_RATIO_MAX;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	rect->width &= ~0x1;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	if (fmt->height > rect->height * SCALER_RATIO_MAX)
91162306a36Sopenharmony_ci		rect->height = (fmt->height + SCALER_RATIO_MAX - 1) /
91262306a36Sopenharmony_ci							SCALER_RATIO_MAX;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	if (rect->width < 16)
91562306a36Sopenharmony_ci		rect->width = 16;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	if (rect->height < 4)
91862306a36Sopenharmony_ci		rect->height = 4;
91962306a36Sopenharmony_ci}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci/*
92262306a36Sopenharmony_ci * vfe_try_crop - Handle try crop selection by pad subdev method
92362306a36Sopenharmony_ci * @line: VFE line
92462306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
92562306a36Sopenharmony_ci * @rect: pointer to v4l2 rect structure
92662306a36Sopenharmony_ci * @which: wanted subdev format
92762306a36Sopenharmony_ci */
92862306a36Sopenharmony_cistatic void vfe_try_crop(struct vfe_line *line,
92962306a36Sopenharmony_ci			 struct v4l2_subdev_state *sd_state,
93062306a36Sopenharmony_ci			 struct v4l2_rect *rect,
93162306a36Sopenharmony_ci			 enum v4l2_subdev_format_whence which)
93262306a36Sopenharmony_ci{
93362306a36Sopenharmony_ci	struct v4l2_rect *compose;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	compose = __vfe_get_compose(line, sd_state, which);
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	if (rect->width > compose->width)
93862306a36Sopenharmony_ci		rect->width = compose->width;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	if (rect->width + rect->left > compose->width)
94162306a36Sopenharmony_ci		rect->left = compose->width - rect->width;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	if (rect->height > compose->height)
94462306a36Sopenharmony_ci		rect->height = compose->height;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	if (rect->height + rect->top > compose->height)
94762306a36Sopenharmony_ci		rect->top = compose->height - rect->height;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	/* wm in line based mode writes multiple of 16 horizontally */
95062306a36Sopenharmony_ci	rect->left += (rect->width & 0xf) >> 1;
95162306a36Sopenharmony_ci	rect->width &= ~0xf;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	if (rect->width < 16) {
95462306a36Sopenharmony_ci		rect->left = 0;
95562306a36Sopenharmony_ci		rect->width = 16;
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	if (rect->height < 4) {
95962306a36Sopenharmony_ci		rect->top = 0;
96062306a36Sopenharmony_ci		rect->height = 4;
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci/*
96562306a36Sopenharmony_ci * vfe_enum_mbus_code - Handle pixel format enumeration
96662306a36Sopenharmony_ci * @sd: VFE V4L2 subdevice
96762306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
96862306a36Sopenharmony_ci * @code: pointer to v4l2_subdev_mbus_code_enum structure
96962306a36Sopenharmony_ci *
97062306a36Sopenharmony_ci * return -EINVAL or zero on success
97162306a36Sopenharmony_ci */
97262306a36Sopenharmony_cistatic int vfe_enum_mbus_code(struct v4l2_subdev *sd,
97362306a36Sopenharmony_ci			      struct v4l2_subdev_state *sd_state,
97462306a36Sopenharmony_ci			      struct v4l2_subdev_mbus_code_enum *code)
97562306a36Sopenharmony_ci{
97662306a36Sopenharmony_ci	struct vfe_line *line = v4l2_get_subdevdata(sd);
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	if (code->pad == MSM_VFE_PAD_SINK) {
97962306a36Sopenharmony_ci		if (code->index >= line->nformats)
98062306a36Sopenharmony_ci			return -EINVAL;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci		code->code = line->formats[code->index].code;
98362306a36Sopenharmony_ci	} else {
98462306a36Sopenharmony_ci		struct v4l2_mbus_framefmt *sink_fmt;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci		sink_fmt = __vfe_get_format(line, sd_state, MSM_VFE_PAD_SINK,
98762306a36Sopenharmony_ci					    code->which);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci		code->code = vfe_src_pad_code(line, sink_fmt->code,
99062306a36Sopenharmony_ci					      code->index, 0);
99162306a36Sopenharmony_ci		if (!code->code)
99262306a36Sopenharmony_ci			return -EINVAL;
99362306a36Sopenharmony_ci	}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	return 0;
99662306a36Sopenharmony_ci}
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci/*
99962306a36Sopenharmony_ci * vfe_enum_frame_size - Handle frame size enumeration
100062306a36Sopenharmony_ci * @sd: VFE V4L2 subdevice
100162306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
100262306a36Sopenharmony_ci * @fse: pointer to v4l2_subdev_frame_size_enum structure
100362306a36Sopenharmony_ci *
100462306a36Sopenharmony_ci * Return -EINVAL or zero on success
100562306a36Sopenharmony_ci */
100662306a36Sopenharmony_cistatic int vfe_enum_frame_size(struct v4l2_subdev *sd,
100762306a36Sopenharmony_ci			       struct v4l2_subdev_state *sd_state,
100862306a36Sopenharmony_ci			       struct v4l2_subdev_frame_size_enum *fse)
100962306a36Sopenharmony_ci{
101062306a36Sopenharmony_ci	struct vfe_line *line = v4l2_get_subdevdata(sd);
101162306a36Sopenharmony_ci	struct v4l2_mbus_framefmt format;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	if (fse->index != 0)
101462306a36Sopenharmony_ci		return -EINVAL;
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	format.code = fse->code;
101762306a36Sopenharmony_ci	format.width = 1;
101862306a36Sopenharmony_ci	format.height = 1;
101962306a36Sopenharmony_ci	vfe_try_format(line, sd_state, fse->pad, &format, fse->which);
102062306a36Sopenharmony_ci	fse->min_width = format.width;
102162306a36Sopenharmony_ci	fse->min_height = format.height;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	if (format.code != fse->code)
102462306a36Sopenharmony_ci		return -EINVAL;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	format.code = fse->code;
102762306a36Sopenharmony_ci	format.width = -1;
102862306a36Sopenharmony_ci	format.height = -1;
102962306a36Sopenharmony_ci	vfe_try_format(line, sd_state, fse->pad, &format, fse->which);
103062306a36Sopenharmony_ci	fse->max_width = format.width;
103162306a36Sopenharmony_ci	fse->max_height = format.height;
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	return 0;
103462306a36Sopenharmony_ci}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci/*
103762306a36Sopenharmony_ci * vfe_get_format - Handle get format by pads subdev method
103862306a36Sopenharmony_ci * @sd: VFE V4L2 subdevice
103962306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
104062306a36Sopenharmony_ci * @fmt: pointer to v4l2 subdev format structure
104162306a36Sopenharmony_ci *
104262306a36Sopenharmony_ci * Return -EINVAL or zero on success
104362306a36Sopenharmony_ci */
104462306a36Sopenharmony_cistatic int vfe_get_format(struct v4l2_subdev *sd,
104562306a36Sopenharmony_ci			  struct v4l2_subdev_state *sd_state,
104662306a36Sopenharmony_ci			  struct v4l2_subdev_format *fmt)
104762306a36Sopenharmony_ci{
104862306a36Sopenharmony_ci	struct vfe_line *line = v4l2_get_subdevdata(sd);
104962306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	format = __vfe_get_format(line, sd_state, fmt->pad, fmt->which);
105262306a36Sopenharmony_ci	if (format == NULL)
105362306a36Sopenharmony_ci		return -EINVAL;
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	fmt->format = *format;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	return 0;
105862306a36Sopenharmony_ci}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_cistatic int vfe_set_selection(struct v4l2_subdev *sd,
106162306a36Sopenharmony_ci			     struct v4l2_subdev_state *sd_state,
106262306a36Sopenharmony_ci			     struct v4l2_subdev_selection *sel);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci/*
106562306a36Sopenharmony_ci * vfe_set_format - Handle set format by pads subdev method
106662306a36Sopenharmony_ci * @sd: VFE V4L2 subdevice
106762306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
106862306a36Sopenharmony_ci * @fmt: pointer to v4l2 subdev format structure
106962306a36Sopenharmony_ci *
107062306a36Sopenharmony_ci * Return -EINVAL or zero on success
107162306a36Sopenharmony_ci */
107262306a36Sopenharmony_cistatic int vfe_set_format(struct v4l2_subdev *sd,
107362306a36Sopenharmony_ci			  struct v4l2_subdev_state *sd_state,
107462306a36Sopenharmony_ci			  struct v4l2_subdev_format *fmt)
107562306a36Sopenharmony_ci{
107662306a36Sopenharmony_ci	struct vfe_line *line = v4l2_get_subdevdata(sd);
107762306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	format = __vfe_get_format(line, sd_state, fmt->pad, fmt->which);
108062306a36Sopenharmony_ci	if (format == NULL)
108162306a36Sopenharmony_ci		return -EINVAL;
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	vfe_try_format(line, sd_state, fmt->pad, &fmt->format, fmt->which);
108462306a36Sopenharmony_ci	*format = fmt->format;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	if (fmt->pad == MSM_VFE_PAD_SINK) {
108762306a36Sopenharmony_ci		struct v4l2_subdev_selection sel = { 0 };
108862306a36Sopenharmony_ci		int ret;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci		/* Propagate the format from sink to source */
109162306a36Sopenharmony_ci		format = __vfe_get_format(line, sd_state, MSM_VFE_PAD_SRC,
109262306a36Sopenharmony_ci					  fmt->which);
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci		*format = fmt->format;
109562306a36Sopenharmony_ci		vfe_try_format(line, sd_state, MSM_VFE_PAD_SRC, format,
109662306a36Sopenharmony_ci			       fmt->which);
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci		if (line->id != VFE_LINE_PIX)
109962306a36Sopenharmony_ci			return 0;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci		/* Reset sink pad compose selection */
110262306a36Sopenharmony_ci		sel.which = fmt->which;
110362306a36Sopenharmony_ci		sel.pad = MSM_VFE_PAD_SINK;
110462306a36Sopenharmony_ci		sel.target = V4L2_SEL_TGT_COMPOSE;
110562306a36Sopenharmony_ci		sel.r.width = fmt->format.width;
110662306a36Sopenharmony_ci		sel.r.height = fmt->format.height;
110762306a36Sopenharmony_ci		ret = vfe_set_selection(sd, sd_state, &sel);
110862306a36Sopenharmony_ci		if (ret < 0)
110962306a36Sopenharmony_ci			return ret;
111062306a36Sopenharmony_ci	}
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	return 0;
111362306a36Sopenharmony_ci}
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci/*
111662306a36Sopenharmony_ci * vfe_get_selection - Handle get selection by pads subdev method
111762306a36Sopenharmony_ci * @sd: VFE V4L2 subdevice
111862306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
111962306a36Sopenharmony_ci * @sel: pointer to v4l2 subdev selection structure
112062306a36Sopenharmony_ci *
112162306a36Sopenharmony_ci * Return -EINVAL or zero on success
112262306a36Sopenharmony_ci */
112362306a36Sopenharmony_cistatic int vfe_get_selection(struct v4l2_subdev *sd,
112462306a36Sopenharmony_ci			     struct v4l2_subdev_state *sd_state,
112562306a36Sopenharmony_ci			     struct v4l2_subdev_selection *sel)
112662306a36Sopenharmony_ci{
112762306a36Sopenharmony_ci	struct vfe_line *line = v4l2_get_subdevdata(sd);
112862306a36Sopenharmony_ci	struct v4l2_subdev_format fmt = { 0 };
112962306a36Sopenharmony_ci	struct v4l2_rect *rect;
113062306a36Sopenharmony_ci	int ret;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	if (line->id != VFE_LINE_PIX)
113362306a36Sopenharmony_ci		return -EINVAL;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	if (sel->pad == MSM_VFE_PAD_SINK)
113662306a36Sopenharmony_ci		switch (sel->target) {
113762306a36Sopenharmony_ci		case V4L2_SEL_TGT_COMPOSE_BOUNDS:
113862306a36Sopenharmony_ci			fmt.pad = sel->pad;
113962306a36Sopenharmony_ci			fmt.which = sel->which;
114062306a36Sopenharmony_ci			ret = vfe_get_format(sd, sd_state, &fmt);
114162306a36Sopenharmony_ci			if (ret < 0)
114262306a36Sopenharmony_ci				return ret;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci			sel->r.left = 0;
114562306a36Sopenharmony_ci			sel->r.top = 0;
114662306a36Sopenharmony_ci			sel->r.width = fmt.format.width;
114762306a36Sopenharmony_ci			sel->r.height = fmt.format.height;
114862306a36Sopenharmony_ci			break;
114962306a36Sopenharmony_ci		case V4L2_SEL_TGT_COMPOSE:
115062306a36Sopenharmony_ci			rect = __vfe_get_compose(line, sd_state, sel->which);
115162306a36Sopenharmony_ci			if (rect == NULL)
115262306a36Sopenharmony_ci				return -EINVAL;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci			sel->r = *rect;
115562306a36Sopenharmony_ci			break;
115662306a36Sopenharmony_ci		default:
115762306a36Sopenharmony_ci			return -EINVAL;
115862306a36Sopenharmony_ci		}
115962306a36Sopenharmony_ci	else if (sel->pad == MSM_VFE_PAD_SRC)
116062306a36Sopenharmony_ci		switch (sel->target) {
116162306a36Sopenharmony_ci		case V4L2_SEL_TGT_CROP_BOUNDS:
116262306a36Sopenharmony_ci			rect = __vfe_get_compose(line, sd_state, sel->which);
116362306a36Sopenharmony_ci			if (rect == NULL)
116462306a36Sopenharmony_ci				return -EINVAL;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci			sel->r.left = rect->left;
116762306a36Sopenharmony_ci			sel->r.top = rect->top;
116862306a36Sopenharmony_ci			sel->r.width = rect->width;
116962306a36Sopenharmony_ci			sel->r.height = rect->height;
117062306a36Sopenharmony_ci			break;
117162306a36Sopenharmony_ci		case V4L2_SEL_TGT_CROP:
117262306a36Sopenharmony_ci			rect = __vfe_get_crop(line, sd_state, sel->which);
117362306a36Sopenharmony_ci			if (rect == NULL)
117462306a36Sopenharmony_ci				return -EINVAL;
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci			sel->r = *rect;
117762306a36Sopenharmony_ci			break;
117862306a36Sopenharmony_ci		default:
117962306a36Sopenharmony_ci			return -EINVAL;
118062306a36Sopenharmony_ci		}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	return 0;
118362306a36Sopenharmony_ci}
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci/*
118662306a36Sopenharmony_ci * vfe_set_selection - Handle set selection by pads subdev method
118762306a36Sopenharmony_ci * @sd: VFE V4L2 subdevice
118862306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
118962306a36Sopenharmony_ci * @sel: pointer to v4l2 subdev selection structure
119062306a36Sopenharmony_ci *
119162306a36Sopenharmony_ci * Return -EINVAL or zero on success
119262306a36Sopenharmony_ci */
119362306a36Sopenharmony_cistatic int vfe_set_selection(struct v4l2_subdev *sd,
119462306a36Sopenharmony_ci			     struct v4l2_subdev_state *sd_state,
119562306a36Sopenharmony_ci			     struct v4l2_subdev_selection *sel)
119662306a36Sopenharmony_ci{
119762306a36Sopenharmony_ci	struct vfe_line *line = v4l2_get_subdevdata(sd);
119862306a36Sopenharmony_ci	struct v4l2_rect *rect;
119962306a36Sopenharmony_ci	int ret;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	if (line->id != VFE_LINE_PIX)
120262306a36Sopenharmony_ci		return -EINVAL;
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	if (sel->target == V4L2_SEL_TGT_COMPOSE &&
120562306a36Sopenharmony_ci		sel->pad == MSM_VFE_PAD_SINK) {
120662306a36Sopenharmony_ci		struct v4l2_subdev_selection crop = { 0 };
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci		rect = __vfe_get_compose(line, sd_state, sel->which);
120962306a36Sopenharmony_ci		if (rect == NULL)
121062306a36Sopenharmony_ci			return -EINVAL;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci		vfe_try_compose(line, sd_state, &sel->r, sel->which);
121362306a36Sopenharmony_ci		*rect = sel->r;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci		/* Reset source crop selection */
121662306a36Sopenharmony_ci		crop.which = sel->which;
121762306a36Sopenharmony_ci		crop.pad = MSM_VFE_PAD_SRC;
121862306a36Sopenharmony_ci		crop.target = V4L2_SEL_TGT_CROP;
121962306a36Sopenharmony_ci		crop.r = *rect;
122062306a36Sopenharmony_ci		ret = vfe_set_selection(sd, sd_state, &crop);
122162306a36Sopenharmony_ci	} else if (sel->target == V4L2_SEL_TGT_CROP &&
122262306a36Sopenharmony_ci		sel->pad == MSM_VFE_PAD_SRC) {
122362306a36Sopenharmony_ci		struct v4l2_subdev_format fmt = { 0 };
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci		rect = __vfe_get_crop(line, sd_state, sel->which);
122662306a36Sopenharmony_ci		if (rect == NULL)
122762306a36Sopenharmony_ci			return -EINVAL;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci		vfe_try_crop(line, sd_state, &sel->r, sel->which);
123062306a36Sopenharmony_ci		*rect = sel->r;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci		/* Reset source pad format width and height */
123362306a36Sopenharmony_ci		fmt.which = sel->which;
123462306a36Sopenharmony_ci		fmt.pad = MSM_VFE_PAD_SRC;
123562306a36Sopenharmony_ci		ret = vfe_get_format(sd, sd_state, &fmt);
123662306a36Sopenharmony_ci		if (ret < 0)
123762306a36Sopenharmony_ci			return ret;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci		fmt.format.width = rect->width;
124062306a36Sopenharmony_ci		fmt.format.height = rect->height;
124162306a36Sopenharmony_ci		ret = vfe_set_format(sd, sd_state, &fmt);
124262306a36Sopenharmony_ci	} else {
124362306a36Sopenharmony_ci		ret = -EINVAL;
124462306a36Sopenharmony_ci	}
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	return ret;
124762306a36Sopenharmony_ci}
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci/*
125062306a36Sopenharmony_ci * vfe_init_formats - Initialize formats on all pads
125162306a36Sopenharmony_ci * @sd: VFE V4L2 subdevice
125262306a36Sopenharmony_ci * @fh: V4L2 subdev file handle
125362306a36Sopenharmony_ci *
125462306a36Sopenharmony_ci * Initialize all pad formats with default values.
125562306a36Sopenharmony_ci *
125662306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise
125762306a36Sopenharmony_ci */
125862306a36Sopenharmony_cistatic int vfe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
125962306a36Sopenharmony_ci{
126062306a36Sopenharmony_ci	struct v4l2_subdev_format format = {
126162306a36Sopenharmony_ci		.pad = MSM_VFE_PAD_SINK,
126262306a36Sopenharmony_ci		.which = fh ? V4L2_SUBDEV_FORMAT_TRY :
126362306a36Sopenharmony_ci			      V4L2_SUBDEV_FORMAT_ACTIVE,
126462306a36Sopenharmony_ci		.format = {
126562306a36Sopenharmony_ci			.code = MEDIA_BUS_FMT_UYVY8_2X8,
126662306a36Sopenharmony_ci			.width = 1920,
126762306a36Sopenharmony_ci			.height = 1080
126862306a36Sopenharmony_ci		}
126962306a36Sopenharmony_ci	};
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	return vfe_set_format(sd, fh ? fh->state : NULL, &format);
127262306a36Sopenharmony_ci}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci/*
127562306a36Sopenharmony_ci * msm_vfe_subdev_init - Initialize VFE device structure and resources
127662306a36Sopenharmony_ci * @vfe: VFE device
127762306a36Sopenharmony_ci * @res: VFE module resources table
127862306a36Sopenharmony_ci *
127962306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise
128062306a36Sopenharmony_ci */
128162306a36Sopenharmony_ciint msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
128262306a36Sopenharmony_ci			const struct resources *res, u8 id)
128362306a36Sopenharmony_ci{
128462306a36Sopenharmony_ci	struct device *dev = camss->dev;
128562306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
128662306a36Sopenharmony_ci	int i, j;
128762306a36Sopenharmony_ci	int ret;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	switch (camss->version) {
129062306a36Sopenharmony_ci	case CAMSS_8x16:
129162306a36Sopenharmony_ci		vfe->ops = &vfe_ops_4_1;
129262306a36Sopenharmony_ci		break;
129362306a36Sopenharmony_ci	case CAMSS_8x96:
129462306a36Sopenharmony_ci		vfe->ops = &vfe_ops_4_7;
129562306a36Sopenharmony_ci		break;
129662306a36Sopenharmony_ci	case CAMSS_660:
129762306a36Sopenharmony_ci		vfe->ops = &vfe_ops_4_8;
129862306a36Sopenharmony_ci		break;
129962306a36Sopenharmony_ci	case CAMSS_845:
130062306a36Sopenharmony_ci		vfe->ops = &vfe_ops_170;
130162306a36Sopenharmony_ci		break;
130262306a36Sopenharmony_ci	case CAMSS_8250:
130362306a36Sopenharmony_ci		vfe->ops = &vfe_ops_480;
130462306a36Sopenharmony_ci		break;
130562306a36Sopenharmony_ci	default:
130662306a36Sopenharmony_ci		return -EINVAL;
130762306a36Sopenharmony_ci	}
130862306a36Sopenharmony_ci	vfe->ops->subdev_init(dev, vfe);
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	/* Memory */
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	vfe->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]);
131362306a36Sopenharmony_ci	if (IS_ERR(vfe->base)) {
131462306a36Sopenharmony_ci		dev_err(dev, "could not map memory\n");
131562306a36Sopenharmony_ci		return PTR_ERR(vfe->base);
131662306a36Sopenharmony_ci	}
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	/* Interrupt */
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	ret = platform_get_irq_byname(pdev, res->interrupt[0]);
132162306a36Sopenharmony_ci	if (ret < 0)
132262306a36Sopenharmony_ci		return ret;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	vfe->irq = ret;
132562306a36Sopenharmony_ci	snprintf(vfe->irq_name, sizeof(vfe->irq_name), "%s_%s%d",
132662306a36Sopenharmony_ci		 dev_name(dev), MSM_VFE_NAME, id);
132762306a36Sopenharmony_ci	ret = devm_request_irq(dev, vfe->irq, vfe->ops->isr,
132862306a36Sopenharmony_ci			       IRQF_TRIGGER_RISING, vfe->irq_name, vfe);
132962306a36Sopenharmony_ci	if (ret < 0) {
133062306a36Sopenharmony_ci		dev_err(dev, "request_irq failed: %d\n", ret);
133162306a36Sopenharmony_ci		return ret;
133262306a36Sopenharmony_ci	}
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	/* Clocks */
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	vfe->nclocks = 0;
133762306a36Sopenharmony_ci	while (res->clock[vfe->nclocks])
133862306a36Sopenharmony_ci		vfe->nclocks++;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	vfe->clock = devm_kcalloc(dev, vfe->nclocks, sizeof(*vfe->clock),
134162306a36Sopenharmony_ci				  GFP_KERNEL);
134262306a36Sopenharmony_ci	if (!vfe->clock)
134362306a36Sopenharmony_ci		return -ENOMEM;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	for (i = 0; i < vfe->nclocks; i++) {
134662306a36Sopenharmony_ci		struct camss_clock *clock = &vfe->clock[i];
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci		clock->clk = devm_clk_get(dev, res->clock[i]);
134962306a36Sopenharmony_ci		if (IS_ERR(clock->clk))
135062306a36Sopenharmony_ci			return PTR_ERR(clock->clk);
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci		clock->name = res->clock[i];
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci		clock->nfreqs = 0;
135562306a36Sopenharmony_ci		while (res->clock_rate[i][clock->nfreqs])
135662306a36Sopenharmony_ci			clock->nfreqs++;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci		if (!clock->nfreqs) {
135962306a36Sopenharmony_ci			clock->freq = NULL;
136062306a36Sopenharmony_ci			continue;
136162306a36Sopenharmony_ci		}
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci		clock->freq = devm_kcalloc(dev,
136462306a36Sopenharmony_ci					   clock->nfreqs,
136562306a36Sopenharmony_ci					   sizeof(*clock->freq),
136662306a36Sopenharmony_ci					   GFP_KERNEL);
136762306a36Sopenharmony_ci		if (!clock->freq)
136862306a36Sopenharmony_ci			return -ENOMEM;
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci		for (j = 0; j < clock->nfreqs; j++)
137162306a36Sopenharmony_ci			clock->freq[j] = res->clock_rate[i][j];
137262306a36Sopenharmony_ci	}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	mutex_init(&vfe->power_lock);
137562306a36Sopenharmony_ci	vfe->power_count = 0;
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	mutex_init(&vfe->stream_lock);
137862306a36Sopenharmony_ci	vfe->stream_count = 0;
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	spin_lock_init(&vfe->output_lock);
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	vfe->camss = camss;
138362306a36Sopenharmony_ci	vfe->id = id;
138462306a36Sopenharmony_ci	vfe->reg_update = 0;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) {
138762306a36Sopenharmony_ci		struct vfe_line *l = &vfe->line[i];
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci		l->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
139062306a36Sopenharmony_ci		l->video_out.camss = camss;
139162306a36Sopenharmony_ci		l->id = i;
139262306a36Sopenharmony_ci		init_completion(&l->output.sof);
139362306a36Sopenharmony_ci		init_completion(&l->output.reg_update);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci		if (camss->version == CAMSS_8x16) {
139662306a36Sopenharmony_ci			if (i == VFE_LINE_PIX) {
139762306a36Sopenharmony_ci				l->formats = formats_pix_8x16;
139862306a36Sopenharmony_ci				l->nformats = ARRAY_SIZE(formats_pix_8x16);
139962306a36Sopenharmony_ci			} else {
140062306a36Sopenharmony_ci				l->formats = formats_rdi_8x16;
140162306a36Sopenharmony_ci				l->nformats = ARRAY_SIZE(formats_rdi_8x16);
140262306a36Sopenharmony_ci			}
140362306a36Sopenharmony_ci		} else if (camss->version == CAMSS_8x96 ||
140462306a36Sopenharmony_ci			   camss->version == CAMSS_660) {
140562306a36Sopenharmony_ci			if (i == VFE_LINE_PIX) {
140662306a36Sopenharmony_ci				l->formats = formats_pix_8x96;
140762306a36Sopenharmony_ci				l->nformats = ARRAY_SIZE(formats_pix_8x96);
140862306a36Sopenharmony_ci			} else {
140962306a36Sopenharmony_ci				l->formats = formats_rdi_8x96;
141062306a36Sopenharmony_ci				l->nformats = ARRAY_SIZE(formats_rdi_8x96);
141162306a36Sopenharmony_ci			}
141262306a36Sopenharmony_ci		} else if (camss->version == CAMSS_845 ||
141362306a36Sopenharmony_ci			   camss->version == CAMSS_8250) {
141462306a36Sopenharmony_ci			l->formats = formats_rdi_845;
141562306a36Sopenharmony_ci			l->nformats = ARRAY_SIZE(formats_rdi_845);
141662306a36Sopenharmony_ci		} else {
141762306a36Sopenharmony_ci			return -EINVAL;
141862306a36Sopenharmony_ci		}
141962306a36Sopenharmony_ci	}
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	init_completion(&vfe->reset_complete);
142262306a36Sopenharmony_ci	init_completion(&vfe->halt_complete);
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	return 0;
142562306a36Sopenharmony_ci}
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci/*
142862306a36Sopenharmony_ci * vfe_link_setup - Setup VFE connections
142962306a36Sopenharmony_ci * @entity: Pointer to media entity structure
143062306a36Sopenharmony_ci * @local: Pointer to local pad
143162306a36Sopenharmony_ci * @remote: Pointer to remote pad
143262306a36Sopenharmony_ci * @flags: Link flags
143362306a36Sopenharmony_ci *
143462306a36Sopenharmony_ci * Return 0 on success
143562306a36Sopenharmony_ci */
143662306a36Sopenharmony_cistatic int vfe_link_setup(struct media_entity *entity,
143762306a36Sopenharmony_ci			  const struct media_pad *local,
143862306a36Sopenharmony_ci			  const struct media_pad *remote, u32 flags)
143962306a36Sopenharmony_ci{
144062306a36Sopenharmony_ci	if (flags & MEDIA_LNK_FL_ENABLED)
144162306a36Sopenharmony_ci		if (media_pad_remote_pad_first(local))
144262306a36Sopenharmony_ci			return -EBUSY;
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	return 0;
144562306a36Sopenharmony_ci}
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_cistatic const struct v4l2_subdev_core_ops vfe_core_ops = {
144862306a36Sopenharmony_ci	.s_power = vfe_set_power,
144962306a36Sopenharmony_ci};
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_cistatic const struct v4l2_subdev_video_ops vfe_video_ops = {
145262306a36Sopenharmony_ci	.s_stream = vfe_set_stream,
145362306a36Sopenharmony_ci};
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_cistatic const struct v4l2_subdev_pad_ops vfe_pad_ops = {
145662306a36Sopenharmony_ci	.enum_mbus_code = vfe_enum_mbus_code,
145762306a36Sopenharmony_ci	.enum_frame_size = vfe_enum_frame_size,
145862306a36Sopenharmony_ci	.get_fmt = vfe_get_format,
145962306a36Sopenharmony_ci	.set_fmt = vfe_set_format,
146062306a36Sopenharmony_ci	.get_selection = vfe_get_selection,
146162306a36Sopenharmony_ci	.set_selection = vfe_set_selection,
146262306a36Sopenharmony_ci};
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_cistatic const struct v4l2_subdev_ops vfe_v4l2_ops = {
146562306a36Sopenharmony_ci	.core = &vfe_core_ops,
146662306a36Sopenharmony_ci	.video = &vfe_video_ops,
146762306a36Sopenharmony_ci	.pad = &vfe_pad_ops,
146862306a36Sopenharmony_ci};
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_cistatic const struct v4l2_subdev_internal_ops vfe_v4l2_internal_ops = {
147162306a36Sopenharmony_ci	.open = vfe_init_formats,
147262306a36Sopenharmony_ci};
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_cistatic const struct media_entity_operations vfe_media_ops = {
147562306a36Sopenharmony_ci	.link_setup = vfe_link_setup,
147662306a36Sopenharmony_ci	.link_validate = v4l2_subdev_link_validate,
147762306a36Sopenharmony_ci};
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci/*
148062306a36Sopenharmony_ci * msm_vfe_register_entities - Register subdev node for VFE module
148162306a36Sopenharmony_ci * @vfe: VFE device
148262306a36Sopenharmony_ci * @v4l2_dev: V4L2 device
148362306a36Sopenharmony_ci *
148462306a36Sopenharmony_ci * Initialize and register a subdev node for the VFE module. Then
148562306a36Sopenharmony_ci * call msm_video_register() to register the video device node which
148662306a36Sopenharmony_ci * will be connected to this subdev node. Then actually create the
148762306a36Sopenharmony_ci * media link between them.
148862306a36Sopenharmony_ci *
148962306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise
149062306a36Sopenharmony_ci */
149162306a36Sopenharmony_ciint msm_vfe_register_entities(struct vfe_device *vfe,
149262306a36Sopenharmony_ci			      struct v4l2_device *v4l2_dev)
149362306a36Sopenharmony_ci{
149462306a36Sopenharmony_ci	struct device *dev = vfe->camss->dev;
149562306a36Sopenharmony_ci	struct v4l2_subdev *sd;
149662306a36Sopenharmony_ci	struct media_pad *pads;
149762306a36Sopenharmony_ci	struct camss_video *video_out;
149862306a36Sopenharmony_ci	int ret;
149962306a36Sopenharmony_ci	int i;
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	for (i = 0; i < vfe->line_num; i++) {
150262306a36Sopenharmony_ci		char name[32];
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci		sd = &vfe->line[i].subdev;
150562306a36Sopenharmony_ci		pads = vfe->line[i].pads;
150662306a36Sopenharmony_ci		video_out = &vfe->line[i].video_out;
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci		v4l2_subdev_init(sd, &vfe_v4l2_ops);
150962306a36Sopenharmony_ci		sd->internal_ops = &vfe_v4l2_internal_ops;
151062306a36Sopenharmony_ci		sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
151162306a36Sopenharmony_ci		if (i == VFE_LINE_PIX)
151262306a36Sopenharmony_ci			snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s",
151362306a36Sopenharmony_ci				 MSM_VFE_NAME, vfe->id, "pix");
151462306a36Sopenharmony_ci		else
151562306a36Sopenharmony_ci			snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s%d",
151662306a36Sopenharmony_ci				 MSM_VFE_NAME, vfe->id, "rdi", i);
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci		v4l2_set_subdevdata(sd, &vfe->line[i]);
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci		ret = vfe_init_formats(sd, NULL);
152162306a36Sopenharmony_ci		if (ret < 0) {
152262306a36Sopenharmony_ci			dev_err(dev, "Failed to init format: %d\n", ret);
152362306a36Sopenharmony_ci			goto error_init;
152462306a36Sopenharmony_ci		}
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci		pads[MSM_VFE_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
152762306a36Sopenharmony_ci		pads[MSM_VFE_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci		sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
153062306a36Sopenharmony_ci		sd->entity.ops = &vfe_media_ops;
153162306a36Sopenharmony_ci		ret = media_entity_pads_init(&sd->entity, MSM_VFE_PADS_NUM,
153262306a36Sopenharmony_ci					     pads);
153362306a36Sopenharmony_ci		if (ret < 0) {
153462306a36Sopenharmony_ci			dev_err(dev, "Failed to init media entity: %d\n", ret);
153562306a36Sopenharmony_ci			goto error_init;
153662306a36Sopenharmony_ci		}
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci		ret = v4l2_device_register_subdev(v4l2_dev, sd);
153962306a36Sopenharmony_ci		if (ret < 0) {
154062306a36Sopenharmony_ci			dev_err(dev, "Failed to register subdev: %d\n", ret);
154162306a36Sopenharmony_ci			goto error_reg_subdev;
154262306a36Sopenharmony_ci		}
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci		video_out->ops = &vfe->video_ops;
154562306a36Sopenharmony_ci		if (vfe->camss->version == CAMSS_845 ||
154662306a36Sopenharmony_ci		    vfe->camss->version == CAMSS_8250)
154762306a36Sopenharmony_ci			video_out->bpl_alignment = 16;
154862306a36Sopenharmony_ci		else
154962306a36Sopenharmony_ci			video_out->bpl_alignment = 8;
155062306a36Sopenharmony_ci		video_out->line_based = 0;
155162306a36Sopenharmony_ci		if (i == VFE_LINE_PIX) {
155262306a36Sopenharmony_ci			video_out->bpl_alignment = 16;
155362306a36Sopenharmony_ci			video_out->line_based = 1;
155462306a36Sopenharmony_ci		}
155562306a36Sopenharmony_ci		snprintf(name, ARRAY_SIZE(name), "%s%d_%s%d",
155662306a36Sopenharmony_ci			 MSM_VFE_NAME, vfe->id, "video", i);
155762306a36Sopenharmony_ci		ret = msm_video_register(video_out, v4l2_dev, name,
155862306a36Sopenharmony_ci					 i == VFE_LINE_PIX ? 1 : 0);
155962306a36Sopenharmony_ci		if (ret < 0) {
156062306a36Sopenharmony_ci			dev_err(dev, "Failed to register video node: %d\n",
156162306a36Sopenharmony_ci				ret);
156262306a36Sopenharmony_ci			goto error_reg_video;
156362306a36Sopenharmony_ci		}
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci		ret = media_create_pad_link(
156662306a36Sopenharmony_ci				&sd->entity, MSM_VFE_PAD_SRC,
156762306a36Sopenharmony_ci				&video_out->vdev.entity, 0,
156862306a36Sopenharmony_ci				MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
156962306a36Sopenharmony_ci		if (ret < 0) {
157062306a36Sopenharmony_ci			dev_err(dev, "Failed to link %s->%s entities: %d\n",
157162306a36Sopenharmony_ci				sd->entity.name, video_out->vdev.entity.name,
157262306a36Sopenharmony_ci				ret);
157362306a36Sopenharmony_ci			goto error_link;
157462306a36Sopenharmony_ci		}
157562306a36Sopenharmony_ci	}
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	return 0;
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_cierror_link:
158062306a36Sopenharmony_ci	msm_video_unregister(video_out);
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_cierror_reg_video:
158362306a36Sopenharmony_ci	v4l2_device_unregister_subdev(sd);
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_cierror_reg_subdev:
158662306a36Sopenharmony_ci	media_entity_cleanup(&sd->entity);
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_cierror_init:
158962306a36Sopenharmony_ci	for (i--; i >= 0; i--) {
159062306a36Sopenharmony_ci		sd = &vfe->line[i].subdev;
159162306a36Sopenharmony_ci		video_out = &vfe->line[i].video_out;
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci		msm_video_unregister(video_out);
159462306a36Sopenharmony_ci		v4l2_device_unregister_subdev(sd);
159562306a36Sopenharmony_ci		media_entity_cleanup(&sd->entity);
159662306a36Sopenharmony_ci	}
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	return ret;
159962306a36Sopenharmony_ci}
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci/*
160262306a36Sopenharmony_ci * msm_vfe_unregister_entities - Unregister VFE module subdev node
160362306a36Sopenharmony_ci * @vfe: VFE device
160462306a36Sopenharmony_ci */
160562306a36Sopenharmony_civoid msm_vfe_unregister_entities(struct vfe_device *vfe)
160662306a36Sopenharmony_ci{
160762306a36Sopenharmony_ci	int i;
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	mutex_destroy(&vfe->power_lock);
161062306a36Sopenharmony_ci	mutex_destroy(&vfe->stream_lock);
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	for (i = 0; i < vfe->line_num; i++) {
161362306a36Sopenharmony_ci		struct v4l2_subdev *sd = &vfe->line[i].subdev;
161462306a36Sopenharmony_ci		struct camss_video *video_out = &vfe->line[i].video_out;
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci		msm_video_unregister(video_out);
161762306a36Sopenharmony_ci		v4l2_device_unregister_subdev(sd);
161862306a36Sopenharmony_ci		media_entity_cleanup(&sd->entity);
161962306a36Sopenharmony_ci	}
162062306a36Sopenharmony_ci}
1621