162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * camss-vfe-480.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v480 (SM8250)
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2020-2021 Linaro Ltd.
862306a36Sopenharmony_ci * Copyright (C) 2021 Jonathan Marek
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/interrupt.h>
1262306a36Sopenharmony_ci#include <linux/io.h>
1362306a36Sopenharmony_ci#include <linux/iopoll.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "camss.h"
1662306a36Sopenharmony_ci#include "camss-vfe.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/* VFE 2/3 are lite and have a different register layout */
1962306a36Sopenharmony_ci#define IS_LITE		(vfe->id >= 2 ? 1 : 0)
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define VFE_HW_VERSION			(0x00)
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define VFE_GLOBAL_RESET_CMD		(IS_LITE ? 0x0c : 0x1c)
2462306a36Sopenharmony_ci#define	    GLOBAL_RESET_HW_AND_REG	(IS_LITE ? BIT(1) : BIT(0))
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define VFE_REG_UPDATE_CMD		(IS_LITE ? 0x20 : 0x34)
2762306a36Sopenharmony_cistatic inline int reg_update_rdi(struct vfe_device *vfe, int n)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	return IS_LITE ? BIT(n) : BIT(1 + (n));
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define	    REG_UPDATE_RDI		reg_update_rdi
3362306a36Sopenharmony_ci#define VFE_IRQ_CMD			(IS_LITE ? 0x24 : 0x38)
3462306a36Sopenharmony_ci#define     IRQ_CMD_GLOBAL_CLEAR	BIT(0)
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define VFE_IRQ_MASK(n)			((IS_LITE ? 0x28 : 0x3c) + (n) * 4)
3762306a36Sopenharmony_ci#define	    IRQ_MASK_0_RESET_ACK	(IS_LITE ? BIT(17) : BIT(0))
3862306a36Sopenharmony_ci#define	    IRQ_MASK_0_BUS_TOP_IRQ	(IS_LITE ? BIT(4) : BIT(7))
3962306a36Sopenharmony_ci#define VFE_IRQ_CLEAR(n)		((IS_LITE ? 0x34 : 0x48) + (n) * 4)
4062306a36Sopenharmony_ci#define VFE_IRQ_STATUS(n)		((IS_LITE ? 0x40 : 0x54) + (n) * 4)
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define BUS_REG_BASE			(IS_LITE ? 0x1a00 : 0xaa00)
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define VFE_BUS_WM_CGC_OVERRIDE		(BUS_REG_BASE + 0x08)
4562306a36Sopenharmony_ci#define		WM_CGC_OVERRIDE_ALL	(0x3FFFFFF)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define VFE_BUS_WM_TEST_BUS_CTRL	(BUS_REG_BASE + 0xdc)
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define VFE_BUS_IRQ_MASK(n)		(BUS_REG_BASE + 0x18 + (n) * 4)
5062306a36Sopenharmony_cistatic inline int bus_irq_mask_0_rdi_rup(struct vfe_device *vfe, int n)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	return IS_LITE ? BIT(n) : BIT(3 + (n));
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define     BUS_IRQ_MASK_0_RDI_RUP	bus_irq_mask_0_rdi_rup
5662306a36Sopenharmony_cistatic inline int bus_irq_mask_0_comp_done(struct vfe_device *vfe, int n)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	return IS_LITE ? BIT(4 + (n)) : BIT(6 + (n));
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define     BUS_IRQ_MASK_0_COMP_DONE	bus_irq_mask_0_comp_done
6262306a36Sopenharmony_ci#define VFE_BUS_IRQ_CLEAR(n)		(BUS_REG_BASE + 0x20 + (n) * 4)
6362306a36Sopenharmony_ci#define VFE_BUS_IRQ_STATUS(n)		(BUS_REG_BASE + 0x28 + (n) * 4)
6462306a36Sopenharmony_ci#define VFE_BUS_IRQ_CLEAR_GLOBAL	(BUS_REG_BASE + 0x30)
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci#define VFE_BUS_WM_CFG(n)		(BUS_REG_BASE + 0x200 + (n) * 0x100)
6762306a36Sopenharmony_ci#define		WM_CFG_EN			(0)
6862306a36Sopenharmony_ci#define		WM_CFG_MODE			(16)
6962306a36Sopenharmony_ci#define			MODE_QCOM_PLAIN	(0)
7062306a36Sopenharmony_ci#define			MODE_MIPI_RAW	(1)
7162306a36Sopenharmony_ci#define VFE_BUS_WM_IMAGE_ADDR(n)	(BUS_REG_BASE + 0x204 + (n) * 0x100)
7262306a36Sopenharmony_ci#define VFE_BUS_WM_FRAME_INCR(n)	(BUS_REG_BASE + 0x208 + (n) * 0x100)
7362306a36Sopenharmony_ci#define VFE_BUS_WM_IMAGE_CFG_0(n)	(BUS_REG_BASE + 0x20c + (n) * 0x100)
7462306a36Sopenharmony_ci#define		WM_IMAGE_CFG_0_DEFAULT_WIDTH	(0xFFFF)
7562306a36Sopenharmony_ci#define VFE_BUS_WM_IMAGE_CFG_1(n)	(BUS_REG_BASE + 0x210 + (n) * 0x100)
7662306a36Sopenharmony_ci#define VFE_BUS_WM_IMAGE_CFG_2(n)	(BUS_REG_BASE + 0x214 + (n) * 0x100)
7762306a36Sopenharmony_ci#define VFE_BUS_WM_PACKER_CFG(n)	(BUS_REG_BASE + 0x218 + (n) * 0x100)
7862306a36Sopenharmony_ci#define VFE_BUS_WM_HEADER_ADDR(n)	(BUS_REG_BASE + 0x220 + (n) * 0x100)
7962306a36Sopenharmony_ci#define VFE_BUS_WM_HEADER_INCR(n)	(BUS_REG_BASE + 0x224 + (n) * 0x100)
8062306a36Sopenharmony_ci#define VFE_BUS_WM_HEADER_CFG(n)	(BUS_REG_BASE + 0x228 + (n) * 0x100)
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci#define VFE_BUS_WM_IRQ_SUBSAMPLE_PERIOD(n)	(BUS_REG_BASE + 0x230 + (n) * 0x100)
8362306a36Sopenharmony_ci#define VFE_BUS_WM_IRQ_SUBSAMPLE_PATTERN(n)	(BUS_REG_BASE + 0x234 + (n) * 0x100)
8462306a36Sopenharmony_ci#define VFE_BUS_WM_FRAMEDROP_PERIOD(n)		(BUS_REG_BASE + 0x238 + (n) * 0x100)
8562306a36Sopenharmony_ci#define VFE_BUS_WM_FRAMEDROP_PATTERN(n)		(BUS_REG_BASE + 0x23c + (n) * 0x100)
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#define VFE_BUS_WM_SYSTEM_CACHE_CFG(n)	(BUS_REG_BASE + 0x260 + (n) * 0x100)
8862306a36Sopenharmony_ci#define VFE_BUS_WM_BURST_LIMIT(n)	(BUS_REG_BASE + 0x264 + (n) * 0x100)
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/* for titan 480, each bus client is hardcoded to a specific path
9162306a36Sopenharmony_ci * and each bus client is part of a hardcoded "comp group"
9262306a36Sopenharmony_ci */
9362306a36Sopenharmony_ci#define RDI_WM(n)			((IS_LITE ? 0 : 23) + (n))
9462306a36Sopenharmony_ci#define RDI_COMP_GROUP(n)		((IS_LITE ? 0 : 11) + (n))
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci#define MAX_VFE_OUTPUT_LINES	4
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic u32 vfe_hw_version(struct vfe_device *vfe)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	u32 hw_version = readl_relaxed(vfe->base + VFE_HW_VERSION);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	u32 gen = (hw_version >> 28) & 0xF;
10362306a36Sopenharmony_ci	u32 rev = (hw_version >> 16) & 0xFFF;
10462306a36Sopenharmony_ci	u32 step = hw_version & 0xFFFF;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	dev_dbg(vfe->camss->dev, "VFE HW Version = %u.%u.%u\n", gen, rev, step);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return hw_version;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic void vfe_global_reset(struct vfe_device *vfe)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	writel_relaxed(IRQ_MASK_0_RESET_ACK, vfe->base + VFE_IRQ_MASK(0));
11462306a36Sopenharmony_ci	writel_relaxed(GLOBAL_RESET_HW_AND_REG, vfe->base + VFE_GLOBAL_RESET_CMD);
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	struct v4l2_pix_format_mplane *pix =
12062306a36Sopenharmony_ci		&line->video_out.active_fmt.fmt.pix_mp;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	wm = RDI_WM(wm); /* map to actual WM used (from wm=RDI index) */
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	/* no clock gating at bus input */
12562306a36Sopenharmony_ci	writel_relaxed(WM_CGC_OVERRIDE_ALL, vfe->base + VFE_BUS_WM_CGC_OVERRIDE);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	writel_relaxed(0x0, vfe->base + VFE_BUS_WM_TEST_BUS_CTRL);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	writel_relaxed(pix->plane_fmt[0].bytesperline * pix->height,
13062306a36Sopenharmony_ci		       vfe->base + VFE_BUS_WM_FRAME_INCR(wm));
13162306a36Sopenharmony_ci	writel_relaxed(0xf, vfe->base + VFE_BUS_WM_BURST_LIMIT(wm));
13262306a36Sopenharmony_ci	writel_relaxed(WM_IMAGE_CFG_0_DEFAULT_WIDTH,
13362306a36Sopenharmony_ci		       vfe->base + VFE_BUS_WM_IMAGE_CFG_0(wm));
13462306a36Sopenharmony_ci	writel_relaxed(pix->plane_fmt[0].bytesperline,
13562306a36Sopenharmony_ci		       vfe->base + VFE_BUS_WM_IMAGE_CFG_2(wm));
13662306a36Sopenharmony_ci	writel_relaxed(0, vfe->base + VFE_BUS_WM_PACKER_CFG(wm));
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/* no dropped frames, one irq per frame */
13962306a36Sopenharmony_ci	writel_relaxed(0, vfe->base + VFE_BUS_WM_FRAMEDROP_PERIOD(wm));
14062306a36Sopenharmony_ci	writel_relaxed(1, vfe->base + VFE_BUS_WM_FRAMEDROP_PATTERN(wm));
14162306a36Sopenharmony_ci	writel_relaxed(0, vfe->base + VFE_BUS_WM_IRQ_SUBSAMPLE_PERIOD(wm));
14262306a36Sopenharmony_ci	writel_relaxed(1, vfe->base + VFE_BUS_WM_IRQ_SUBSAMPLE_PATTERN(wm));
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	writel_relaxed(1 << WM_CFG_EN | MODE_MIPI_RAW << WM_CFG_MODE,
14562306a36Sopenharmony_ci		       vfe->base + VFE_BUS_WM_CFG(wm));
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic void vfe_wm_stop(struct vfe_device *vfe, u8 wm)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	wm = RDI_WM(wm); /* map to actual WM used (from wm=RDI index) */
15162306a36Sopenharmony_ci	writel_relaxed(0, vfe->base + VFE_BUS_WM_CFG(wm));
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic void vfe_wm_update(struct vfe_device *vfe, u8 wm, u32 addr,
15562306a36Sopenharmony_ci			  struct vfe_line *line)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	wm = RDI_WM(wm); /* map to actual WM used (from wm=RDI index) */
15862306a36Sopenharmony_ci	writel_relaxed(addr, vfe->base + VFE_BUS_WM_IMAGE_ADDR(wm));
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	vfe->reg_update |= REG_UPDATE_RDI(vfe, line_id);
16462306a36Sopenharmony_ci	writel_relaxed(vfe->reg_update, vfe->base + VFE_REG_UPDATE_CMD);
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic inline void vfe_reg_update_clear(struct vfe_device *vfe,
16862306a36Sopenharmony_ci					enum vfe_line_id line_id)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	vfe->reg_update &= ~REG_UPDATE_RDI(vfe, line_id);
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic void vfe_enable_irq_common(struct vfe_device *vfe)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	/* enable reset ack IRQ and top BUS status IRQ */
17662306a36Sopenharmony_ci	writel_relaxed(IRQ_MASK_0_RESET_ACK | IRQ_MASK_0_BUS_TOP_IRQ,
17762306a36Sopenharmony_ci		       vfe->base + VFE_IRQ_MASK(0));
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic void vfe_enable_lines_irq(struct vfe_device *vfe)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	int i;
18362306a36Sopenharmony_ci	u32 bus_irq_mask = 0;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	for (i = 0; i < MAX_VFE_OUTPUT_LINES; i++) {
18662306a36Sopenharmony_ci		/* Enable IRQ for newly added lines, but also keep already running lines's IRQ */
18762306a36Sopenharmony_ci		if (vfe->line[i].output.state == VFE_OUTPUT_RESERVED ||
18862306a36Sopenharmony_ci		    vfe->line[i].output.state == VFE_OUTPUT_ON) {
18962306a36Sopenharmony_ci			bus_irq_mask |= BUS_IRQ_MASK_0_RDI_RUP(vfe, i)
19062306a36Sopenharmony_ci					| BUS_IRQ_MASK_0_COMP_DONE(vfe, RDI_COMP_GROUP(i));
19162306a36Sopenharmony_ci			}
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	writel_relaxed(bus_irq_mask, vfe->base + VFE_BUS_IRQ_MASK(0));
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id);
19862306a36Sopenharmony_cistatic void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci/*
20162306a36Sopenharmony_ci * vfe_isr - VFE module interrupt handler
20262306a36Sopenharmony_ci * @irq: Interrupt line
20362306a36Sopenharmony_ci * @dev: VFE device
20462306a36Sopenharmony_ci *
20562306a36Sopenharmony_ci * Return IRQ_HANDLED on success
20662306a36Sopenharmony_ci */
20762306a36Sopenharmony_cistatic irqreturn_t vfe_isr(int irq, void *dev)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	struct vfe_device *vfe = dev;
21062306a36Sopenharmony_ci	u32 status;
21162306a36Sopenharmony_ci	int i;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	status = readl_relaxed(vfe->base + VFE_IRQ_STATUS(0));
21462306a36Sopenharmony_ci	writel_relaxed(status, vfe->base + VFE_IRQ_CLEAR(0));
21562306a36Sopenharmony_ci	writel_relaxed(IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_IRQ_CMD);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	if (status & IRQ_MASK_0_RESET_ACK)
21862306a36Sopenharmony_ci		vfe_isr_reset_ack(vfe);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (status & IRQ_MASK_0_BUS_TOP_IRQ) {
22162306a36Sopenharmony_ci		u32 status = readl_relaxed(vfe->base + VFE_BUS_IRQ_STATUS(0));
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci		writel_relaxed(status, vfe->base + VFE_BUS_IRQ_CLEAR(0));
22462306a36Sopenharmony_ci		writel_relaxed(1, vfe->base + VFE_BUS_IRQ_CLEAR_GLOBAL);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci		/* Loop through all WMs IRQs */
22762306a36Sopenharmony_ci		for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++) {
22862306a36Sopenharmony_ci			if (status & BUS_IRQ_MASK_0_RDI_RUP(vfe, i))
22962306a36Sopenharmony_ci				vfe_isr_reg_update(vfe, i);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci			if (status & BUS_IRQ_MASK_0_COMP_DONE(vfe, RDI_COMP_GROUP(i)))
23262306a36Sopenharmony_ci				vfe_isr_wm_done(vfe, i);
23362306a36Sopenharmony_ci		}
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	return IRQ_HANDLED;
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci/*
24062306a36Sopenharmony_ci * vfe_halt - Trigger halt on VFE module and wait to complete
24162306a36Sopenharmony_ci * @vfe: VFE device
24262306a36Sopenharmony_ci *
24362306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise
24462306a36Sopenharmony_ci */
24562306a36Sopenharmony_cistatic int vfe_halt(struct vfe_device *vfe)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	/* rely on vfe_disable_output() to stop the VFE */
24862306a36Sopenharmony_ci	return 0;
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic int vfe_get_output(struct vfe_line *line)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
25462306a36Sopenharmony_ci	struct vfe_output *output;
25562306a36Sopenharmony_ci	unsigned long flags;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	spin_lock_irqsave(&vfe->output_lock, flags);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	output = &line->output;
26062306a36Sopenharmony_ci	if (output->state > VFE_OUTPUT_RESERVED) {
26162306a36Sopenharmony_ci		dev_err(vfe->camss->dev, "Output is running\n");
26262306a36Sopenharmony_ci		goto error;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	output->wm_num = 1;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	/* Correspondence between VFE line number and WM number.
26862306a36Sopenharmony_ci	 * line 0 -> RDI 0, line 1 -> RDI1, line 2 -> RDI2, line 3 -> PIX/RDI3
26962306a36Sopenharmony_ci	 * Note this 1:1 mapping will not work for PIX streams.
27062306a36Sopenharmony_ci	 */
27162306a36Sopenharmony_ci	output->wm_idx[0] = line->id;
27262306a36Sopenharmony_ci	vfe->wm_output_map[line->id] = line->id;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	output->drop_update_idx = 0;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	return 0;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cierror:
28162306a36Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
28262306a36Sopenharmony_ci	output->state = VFE_OUTPUT_OFF;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	return -EINVAL;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic int vfe_enable_output(struct vfe_line *line)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
29062306a36Sopenharmony_ci	struct vfe_output *output = &line->output;
29162306a36Sopenharmony_ci	unsigned long flags;
29262306a36Sopenharmony_ci	unsigned int i;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	spin_lock_irqsave(&vfe->output_lock, flags);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	vfe_reg_update_clear(vfe, line->id);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	if (output->state > VFE_OUTPUT_RESERVED) {
29962306a36Sopenharmony_ci		dev_err(vfe->camss->dev, "Output is not in reserved state %d\n",
30062306a36Sopenharmony_ci			output->state);
30162306a36Sopenharmony_ci		spin_unlock_irqrestore(&vfe->output_lock, flags);
30262306a36Sopenharmony_ci		return -EINVAL;
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	WARN_ON(output->gen2.active_num);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	output->state = VFE_OUTPUT_ON;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	output->sequence = 0;
31062306a36Sopenharmony_ci	output->wait_reg_update = 0;
31162306a36Sopenharmony_ci	reinit_completion(&output->reg_update);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	vfe_wm_start(vfe, output->wm_idx[0], line);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
31662306a36Sopenharmony_ci		output->buf[i] = vfe_buf_get_pending(output);
31762306a36Sopenharmony_ci		if (!output->buf[i])
31862306a36Sopenharmony_ci			break;
31962306a36Sopenharmony_ci		output->gen2.active_num++;
32062306a36Sopenharmony_ci		vfe_wm_update(vfe, output->wm_idx[0], output->buf[i]->addr[0], line);
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	vfe_reg_update(vfe, line->id);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	return 0;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic void vfe_disable_output(struct vfe_line *line)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
33362306a36Sopenharmony_ci	struct vfe_output *output = &line->output;
33462306a36Sopenharmony_ci	unsigned long flags;
33562306a36Sopenharmony_ci	unsigned int i;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	spin_lock_irqsave(&vfe->output_lock, flags);
33862306a36Sopenharmony_ci	for (i = 0; i < output->wm_num; i++)
33962306a36Sopenharmony_ci		vfe_wm_stop(vfe, output->wm_idx[i]);
34062306a36Sopenharmony_ci	output->gen2.active_num = 0;
34162306a36Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	vfe_reset(vfe);
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci/*
34762306a36Sopenharmony_ci * vfe_enable - Enable streaming on VFE line
34862306a36Sopenharmony_ci * @line: VFE line
34962306a36Sopenharmony_ci *
35062306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise
35162306a36Sopenharmony_ci */
35262306a36Sopenharmony_cistatic int vfe_enable(struct vfe_line *line)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
35562306a36Sopenharmony_ci	int ret;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	mutex_lock(&vfe->stream_lock);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (!vfe->stream_count)
36062306a36Sopenharmony_ci		vfe_enable_irq_common(vfe);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	vfe->stream_count++;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	vfe_enable_lines_irq(vfe);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	mutex_unlock(&vfe->stream_lock);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	ret = vfe_get_output(line);
36962306a36Sopenharmony_ci	if (ret < 0)
37062306a36Sopenharmony_ci		goto error_get_output;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	ret = vfe_enable_output(line);
37362306a36Sopenharmony_ci	if (ret < 0)
37462306a36Sopenharmony_ci		goto error_enable_output;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	vfe->was_streaming = 1;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	return 0;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cierror_enable_output:
38162306a36Sopenharmony_ci	vfe_put_output(line);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cierror_get_output:
38462306a36Sopenharmony_ci	mutex_lock(&vfe->stream_lock);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	vfe->stream_count--;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	mutex_unlock(&vfe->stream_lock);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	return ret;
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci/*
39462306a36Sopenharmony_ci * vfe_disable - Disable streaming on VFE line
39562306a36Sopenharmony_ci * @line: VFE line
39662306a36Sopenharmony_ci *
39762306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise
39862306a36Sopenharmony_ci */
39962306a36Sopenharmony_cistatic int vfe_disable(struct vfe_line *line)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	vfe_disable_output(line);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	vfe_put_output(line);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	mutex_lock(&vfe->stream_lock);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	vfe->stream_count--;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	mutex_unlock(&vfe->stream_lock);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	return 0;
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci/*
41762306a36Sopenharmony_ci * vfe_isr_reg_update - Process reg update interrupt
41862306a36Sopenharmony_ci * @vfe: VFE Device
41962306a36Sopenharmony_ci * @line_id: VFE line
42062306a36Sopenharmony_ci */
42162306a36Sopenharmony_cistatic void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	struct vfe_output *output;
42462306a36Sopenharmony_ci	unsigned long flags;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	spin_lock_irqsave(&vfe->output_lock, flags);
42762306a36Sopenharmony_ci	vfe_reg_update_clear(vfe, line_id);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	output = &vfe->line[line_id].output;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (output->wait_reg_update) {
43262306a36Sopenharmony_ci		output->wait_reg_update = 0;
43362306a36Sopenharmony_ci		complete(&output->reg_update);
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci/*
44062306a36Sopenharmony_ci * vfe_isr_wm_done - Process write master done interrupt
44162306a36Sopenharmony_ci * @vfe: VFE Device
44262306a36Sopenharmony_ci * @wm: Write master id
44362306a36Sopenharmony_ci */
44462306a36Sopenharmony_cistatic void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct vfe_line *line = &vfe->line[vfe->wm_output_map[wm]];
44762306a36Sopenharmony_ci	struct camss_buffer *ready_buf;
44862306a36Sopenharmony_ci	struct vfe_output *output;
44962306a36Sopenharmony_ci	unsigned long flags;
45062306a36Sopenharmony_ci	u32 index;
45162306a36Sopenharmony_ci	u64 ts = ktime_get_ns();
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	spin_lock_irqsave(&vfe->output_lock, flags);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	if (vfe->wm_output_map[wm] == VFE_LINE_NONE) {
45662306a36Sopenharmony_ci		dev_err_ratelimited(vfe->camss->dev,
45762306a36Sopenharmony_ci				    "Received wm done for unmapped index\n");
45862306a36Sopenharmony_ci		goto out_unlock;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci	output = &vfe->line[vfe->wm_output_map[wm]].output;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	ready_buf = output->buf[0];
46362306a36Sopenharmony_ci	if (!ready_buf) {
46462306a36Sopenharmony_ci		dev_err_ratelimited(vfe->camss->dev,
46562306a36Sopenharmony_ci				    "Missing ready buf %d!\n", output->state);
46662306a36Sopenharmony_ci		goto out_unlock;
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	ready_buf->vb.vb2_buf.timestamp = ts;
47062306a36Sopenharmony_ci	ready_buf->vb.sequence = output->sequence++;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	index = 0;
47362306a36Sopenharmony_ci	output->buf[0] = output->buf[1];
47462306a36Sopenharmony_ci	if (output->buf[0])
47562306a36Sopenharmony_ci		index = 1;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	output->buf[index] = vfe_buf_get_pending(output);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	if (output->buf[index])
48062306a36Sopenharmony_ci		vfe_wm_update(vfe, output->wm_idx[0], output->buf[index]->addr[0], line);
48162306a36Sopenharmony_ci	else
48262306a36Sopenharmony_ci		output->gen2.active_num--;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	return;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ciout_unlock:
49162306a36Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci/*
49562306a36Sopenharmony_ci * vfe_pm_domain_off - Disable power domains specific to this VFE.
49662306a36Sopenharmony_ci * @vfe: VFE Device
49762306a36Sopenharmony_ci */
49862306a36Sopenharmony_cistatic void vfe_pm_domain_off(struct vfe_device *vfe)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	struct camss *camss = vfe->camss;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if (vfe->id >= camss->vfe_num)
50362306a36Sopenharmony_ci		return;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	device_link_del(camss->genpd_link[vfe->id]);
50662306a36Sopenharmony_ci}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci/*
50962306a36Sopenharmony_ci * vfe_pm_domain_on - Enable power domains specific to this VFE.
51062306a36Sopenharmony_ci * @vfe: VFE Device
51162306a36Sopenharmony_ci */
51262306a36Sopenharmony_cistatic int vfe_pm_domain_on(struct vfe_device *vfe)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	struct camss *camss = vfe->camss;
51562306a36Sopenharmony_ci	enum vfe_line_id id = vfe->id;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	if (id >= camss->vfe_num)
51862306a36Sopenharmony_ci		return 0;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	camss->genpd_link[id] = device_link_add(camss->dev, camss->genpd[id],
52162306a36Sopenharmony_ci						DL_FLAG_STATELESS |
52262306a36Sopenharmony_ci						DL_FLAG_PM_RUNTIME |
52362306a36Sopenharmony_ci						DL_FLAG_RPM_ACTIVE);
52462306a36Sopenharmony_ci	if (!camss->genpd_link[id])
52562306a36Sopenharmony_ci		return -EINVAL;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	return 0;
52862306a36Sopenharmony_ci}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci/*
53162306a36Sopenharmony_ci * vfe_queue_buffer - Add empty buffer
53262306a36Sopenharmony_ci * @vid: Video device structure
53362306a36Sopenharmony_ci * @buf: Buffer to be enqueued
53462306a36Sopenharmony_ci *
53562306a36Sopenharmony_ci * Add an empty buffer - depending on the current number of buffers it will be
53662306a36Sopenharmony_ci * put in pending buffer queue or directly given to the hardware to be filled.
53762306a36Sopenharmony_ci *
53862306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise
53962306a36Sopenharmony_ci */
54062306a36Sopenharmony_cistatic int vfe_queue_buffer(struct camss_video *vid,
54162306a36Sopenharmony_ci			    struct camss_buffer *buf)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
54462306a36Sopenharmony_ci	struct vfe_device *vfe = to_vfe(line);
54562306a36Sopenharmony_ci	struct vfe_output *output;
54662306a36Sopenharmony_ci	unsigned long flags;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	output = &line->output;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	spin_lock_irqsave(&vfe->output_lock, flags);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	if (output->state == VFE_OUTPUT_ON && output->gen2.active_num < 2) {
55362306a36Sopenharmony_ci		output->buf[output->gen2.active_num++] = buf;
55462306a36Sopenharmony_ci		vfe_wm_update(vfe, output->wm_idx[0], buf->addr[0], line);
55562306a36Sopenharmony_ci	} else {
55662306a36Sopenharmony_ci		vfe_buf_add_pending(output, buf);
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	spin_unlock_irqrestore(&vfe->output_lock, flags);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	return 0;
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cistatic const struct camss_video_ops vfe_video_ops_480 = {
56562306a36Sopenharmony_ci	.queue_buffer = vfe_queue_buffer,
56662306a36Sopenharmony_ci	.flush_buffers = vfe_flush_buffers,
56762306a36Sopenharmony_ci};
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_cistatic void vfe_subdev_init(struct device *dev, struct vfe_device *vfe)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci	vfe->video_ops = vfe_video_ops_480;
57262306a36Sopenharmony_ci	vfe->line_num = MAX_VFE_OUTPUT_LINES;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ciconst struct vfe_hw_ops vfe_ops_480 = {
57662306a36Sopenharmony_ci	.global_reset = vfe_global_reset,
57762306a36Sopenharmony_ci	.hw_version = vfe_hw_version,
57862306a36Sopenharmony_ci	.isr = vfe_isr,
57962306a36Sopenharmony_ci	.pm_domain_off = vfe_pm_domain_off,
58062306a36Sopenharmony_ci	.pm_domain_on = vfe_pm_domain_on,
58162306a36Sopenharmony_ci	.subdev_init = vfe_subdev_init,
58262306a36Sopenharmony_ci	.vfe_disable = vfe_disable,
58362306a36Sopenharmony_ci	.vfe_enable = vfe_enable,
58462306a36Sopenharmony_ci	.vfe_halt = vfe_halt,
58562306a36Sopenharmony_ci};
586