162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2019-2020 NXP
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/delay.h>
762306a36Sopenharmony_ci#include <linux/device.h>
862306a36Sopenharmony_ci#include <linux/io.h>
962306a36Sopenharmony_ci#include <linux/types.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "imx8-isi-core.h"
1262306a36Sopenharmony_ci#include "imx8-isi-regs.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define	ISI_DOWNSCALE_THRESHOLD		0x4000
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic inline u32 mxc_isi_read(struct mxc_isi_pipe *pipe, u32 reg)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	return readl(pipe->regs + reg);
1962306a36Sopenharmony_ci}
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic inline void mxc_isi_write(struct mxc_isi_pipe *pipe, u32 reg, u32 val)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	writel(val, pipe->regs + reg);
2462306a36Sopenharmony_ci}
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
2762306a36Sopenharmony_ci * Buffers & M2M operation
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_civoid mxc_isi_channel_set_inbuf(struct mxc_isi_pipe *pipe, dma_addr_t dma_addr)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_IN_BUF_ADDR, lower_32_bits(dma_addr));
3362306a36Sopenharmony_ci	if (pipe->isi->pdata->has_36bit_dma)
3462306a36Sopenharmony_ci		mxc_isi_write(pipe, CHNL_IN_BUF_XTND_ADDR,
3562306a36Sopenharmony_ci			      upper_32_bits(dma_addr));
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_civoid mxc_isi_channel_set_outbuf(struct mxc_isi_pipe *pipe,
3962306a36Sopenharmony_ci				const dma_addr_t dma_addrs[3],
4062306a36Sopenharmony_ci				enum mxc_isi_buf_id buf_id)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	int val;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	val = mxc_isi_read(pipe, CHNL_OUT_BUF_CTRL);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (buf_id == MXC_ISI_BUF1) {
4762306a36Sopenharmony_ci		mxc_isi_write(pipe, CHNL_OUT_BUF1_ADDR_Y,
4862306a36Sopenharmony_ci			      lower_32_bits(dma_addrs[0]));
4962306a36Sopenharmony_ci		mxc_isi_write(pipe, CHNL_OUT_BUF1_ADDR_U,
5062306a36Sopenharmony_ci			      lower_32_bits(dma_addrs[1]));
5162306a36Sopenharmony_ci		mxc_isi_write(pipe, CHNL_OUT_BUF1_ADDR_V,
5262306a36Sopenharmony_ci			      lower_32_bits(dma_addrs[2]));
5362306a36Sopenharmony_ci		if (pipe->isi->pdata->has_36bit_dma) {
5462306a36Sopenharmony_ci			mxc_isi_write(pipe, CHNL_Y_BUF1_XTND_ADDR,
5562306a36Sopenharmony_ci				      upper_32_bits(dma_addrs[0]));
5662306a36Sopenharmony_ci			mxc_isi_write(pipe, CHNL_U_BUF1_XTND_ADDR,
5762306a36Sopenharmony_ci				      upper_32_bits(dma_addrs[1]));
5862306a36Sopenharmony_ci			mxc_isi_write(pipe, CHNL_V_BUF1_XTND_ADDR,
5962306a36Sopenharmony_ci				      upper_32_bits(dma_addrs[2]));
6062306a36Sopenharmony_ci		}
6162306a36Sopenharmony_ci		val ^= CHNL_OUT_BUF_CTRL_LOAD_BUF1_ADDR;
6262306a36Sopenharmony_ci	} else  {
6362306a36Sopenharmony_ci		mxc_isi_write(pipe, CHNL_OUT_BUF2_ADDR_Y,
6462306a36Sopenharmony_ci			      lower_32_bits(dma_addrs[0]));
6562306a36Sopenharmony_ci		mxc_isi_write(pipe, CHNL_OUT_BUF2_ADDR_U,
6662306a36Sopenharmony_ci			      lower_32_bits(dma_addrs[1]));
6762306a36Sopenharmony_ci		mxc_isi_write(pipe, CHNL_OUT_BUF2_ADDR_V,
6862306a36Sopenharmony_ci			      lower_32_bits(dma_addrs[2]));
6962306a36Sopenharmony_ci		if (pipe->isi->pdata->has_36bit_dma) {
7062306a36Sopenharmony_ci			mxc_isi_write(pipe, CHNL_Y_BUF2_XTND_ADDR,
7162306a36Sopenharmony_ci				      upper_32_bits(dma_addrs[0]));
7262306a36Sopenharmony_ci			mxc_isi_write(pipe, CHNL_U_BUF2_XTND_ADDR,
7362306a36Sopenharmony_ci				      upper_32_bits(dma_addrs[1]));
7462306a36Sopenharmony_ci			mxc_isi_write(pipe, CHNL_V_BUF2_XTND_ADDR,
7562306a36Sopenharmony_ci				      upper_32_bits(dma_addrs[2]));
7662306a36Sopenharmony_ci		}
7762306a36Sopenharmony_ci		val ^= CHNL_OUT_BUF_CTRL_LOAD_BUF2_ADDR;
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_OUT_BUF_CTRL, val);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_civoid mxc_isi_channel_m2m_start(struct mxc_isi_pipe *pipe)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	u32 val;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	val = mxc_isi_read(pipe, CHNL_MEM_RD_CTRL);
8862306a36Sopenharmony_ci	val &= ~CHNL_MEM_RD_CTRL_READ_MEM;
8962306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_MEM_RD_CTRL, val);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	fsleep(300);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	val |= CHNL_MEM_RD_CTRL_READ_MEM;
9462306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_MEM_RD_CTRL, val);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
9862306a36Sopenharmony_ci * Pipeline configuration
9962306a36Sopenharmony_ci */
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic u32 mxc_isi_channel_scaling_ratio(unsigned int from, unsigned int to,
10262306a36Sopenharmony_ci					 u32 *dec)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	unsigned int ratio = from / to;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (ratio < 2)
10762306a36Sopenharmony_ci		*dec = 1;
10862306a36Sopenharmony_ci	else if (ratio < 4)
10962306a36Sopenharmony_ci		*dec = 2;
11062306a36Sopenharmony_ci	else if (ratio < 8)
11162306a36Sopenharmony_ci		*dec = 4;
11262306a36Sopenharmony_ci	else
11362306a36Sopenharmony_ci		*dec = 8;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return min_t(u32, from * 0x1000 / (to * *dec), ISI_DOWNSCALE_THRESHOLD);
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic void mxc_isi_channel_set_scaling(struct mxc_isi_pipe *pipe,
11962306a36Sopenharmony_ci					enum mxc_isi_encoding encoding,
12062306a36Sopenharmony_ci					const struct v4l2_area *in_size,
12162306a36Sopenharmony_ci					const struct v4l2_area *out_size,
12262306a36Sopenharmony_ci					bool *bypass)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	u32 xscale, yscale;
12562306a36Sopenharmony_ci	u32 decx, decy;
12662306a36Sopenharmony_ci	u32 val;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	dev_dbg(pipe->isi->dev, "input %ux%u, output %ux%u\n",
12962306a36Sopenharmony_ci		in_size->width, in_size->height,
13062306a36Sopenharmony_ci		out_size->width, out_size->height);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	xscale = mxc_isi_channel_scaling_ratio(in_size->width, out_size->width,
13362306a36Sopenharmony_ci					       &decx);
13462306a36Sopenharmony_ci	yscale = mxc_isi_channel_scaling_ratio(in_size->height, out_size->height,
13562306a36Sopenharmony_ci					       &decy);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	val = mxc_isi_read(pipe, CHNL_IMG_CTRL);
13862306a36Sopenharmony_ci	val &= ~(CHNL_IMG_CTRL_DEC_X_MASK | CHNL_IMG_CTRL_DEC_Y_MASK |
13962306a36Sopenharmony_ci		 CHNL_IMG_CTRL_YCBCR_MODE);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	val |= CHNL_IMG_CTRL_DEC_X(ilog2(decx))
14262306a36Sopenharmony_ci	    |  CHNL_IMG_CTRL_DEC_Y(ilog2(decy));
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/*
14562306a36Sopenharmony_ci	 * Contrary to what the documentation states, YCBCR_MODE does not
14662306a36Sopenharmony_ci	 * control conversion between YCbCr and RGB, but whether the scaler
14762306a36Sopenharmony_ci	 * operates in YUV mode or in RGB mode. It must be set when the scaler
14862306a36Sopenharmony_ci	 * input is YUV.
14962306a36Sopenharmony_ci	 */
15062306a36Sopenharmony_ci	if (encoding == MXC_ISI_ENC_YUV)
15162306a36Sopenharmony_ci		val |= CHNL_IMG_CTRL_YCBCR_MODE;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_IMG_CTRL, val);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_SCALE_FACTOR,
15662306a36Sopenharmony_ci		      CHNL_SCALE_FACTOR_Y_SCALE(yscale) |
15762306a36Sopenharmony_ci		      CHNL_SCALE_FACTOR_X_SCALE(xscale));
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_SCALE_OFFSET, 0);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_SCL_IMG_CFG,
16262306a36Sopenharmony_ci		      CHNL_SCL_IMG_CFG_HEIGHT(out_size->height) |
16362306a36Sopenharmony_ci		      CHNL_SCL_IMG_CFG_WIDTH(out_size->width));
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	*bypass = in_size->height == out_size->height &&
16662306a36Sopenharmony_ci		  in_size->width == out_size->width;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic void mxc_isi_channel_set_crop(struct mxc_isi_pipe *pipe,
17062306a36Sopenharmony_ci				     const struct v4l2_area *src,
17162306a36Sopenharmony_ci				     const struct v4l2_rect *dst)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	u32 val, val0, val1;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	val = mxc_isi_read(pipe, CHNL_IMG_CTRL);
17662306a36Sopenharmony_ci	val &= ~CHNL_IMG_CTRL_CROP_EN;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (src->height == dst->height && src->width == dst->width) {
17962306a36Sopenharmony_ci		mxc_isi_write(pipe, CHNL_IMG_CTRL, val);
18062306a36Sopenharmony_ci		return;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	val |= CHNL_IMG_CTRL_CROP_EN;
18462306a36Sopenharmony_ci	val0 = CHNL_CROP_ULC_X(dst->left) | CHNL_CROP_ULC_Y(dst->top);
18562306a36Sopenharmony_ci	val1 = CHNL_CROP_LRC_X(dst->width) | CHNL_CROP_LRC_Y(dst->height);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_CROP_ULC, val0);
18862306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_CROP_LRC, val1 + val0);
18962306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_IMG_CTRL, val);
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci/*
19362306a36Sopenharmony_ci * A2,A1,      B1, A3,     B3, B2,
19462306a36Sopenharmony_ci * C2, C1,     D1, C3,     D3, D2
19562306a36Sopenharmony_ci */
19662306a36Sopenharmony_cistatic const u32 mxc_isi_yuv2rgb_coeffs[6] = {
19762306a36Sopenharmony_ci	/* YUV -> RGB */
19862306a36Sopenharmony_ci	0x0000012a, 0x012a0198, 0x0730079c,
19962306a36Sopenharmony_ci	0x0204012a, 0x01f00000, 0x01800180
20062306a36Sopenharmony_ci};
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic const u32 mxc_isi_rgb2yuv_coeffs[6] = {
20362306a36Sopenharmony_ci	/* RGB->YUV */
20462306a36Sopenharmony_ci	0x00810041, 0x07db0019, 0x007007b6,
20562306a36Sopenharmony_ci	0x07a20070, 0x001007ee, 0x00800080
20662306a36Sopenharmony_ci};
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic void mxc_isi_channel_set_csc(struct mxc_isi_pipe *pipe,
20962306a36Sopenharmony_ci				    enum mxc_isi_encoding in_encoding,
21062306a36Sopenharmony_ci				    enum mxc_isi_encoding out_encoding,
21162306a36Sopenharmony_ci				    bool *bypass)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	static const char * const encodings[] = {
21462306a36Sopenharmony_ci		[MXC_ISI_ENC_RAW] = "RAW",
21562306a36Sopenharmony_ci		[MXC_ISI_ENC_RGB] = "RGB",
21662306a36Sopenharmony_ci		[MXC_ISI_ENC_YUV] = "YUV",
21762306a36Sopenharmony_ci	};
21862306a36Sopenharmony_ci	const u32 *coeffs;
21962306a36Sopenharmony_ci	bool cscen = true;
22062306a36Sopenharmony_ci	u32 val;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	val = mxc_isi_read(pipe, CHNL_IMG_CTRL);
22362306a36Sopenharmony_ci	val &= ~(CHNL_IMG_CTRL_CSC_BYPASS | CHNL_IMG_CTRL_CSC_MODE_MASK);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (in_encoding == MXC_ISI_ENC_YUV &&
22662306a36Sopenharmony_ci	    out_encoding == MXC_ISI_ENC_RGB) {
22762306a36Sopenharmony_ci		/* YUV2RGB */
22862306a36Sopenharmony_ci		coeffs = mxc_isi_yuv2rgb_coeffs;
22962306a36Sopenharmony_ci		/* YCbCr enable???  */
23062306a36Sopenharmony_ci		val |= CHNL_IMG_CTRL_CSC_MODE(CHNL_IMG_CTRL_CSC_MODE_YCBCR2RGB);
23162306a36Sopenharmony_ci	} else if (in_encoding == MXC_ISI_ENC_RGB &&
23262306a36Sopenharmony_ci		   out_encoding == MXC_ISI_ENC_YUV) {
23362306a36Sopenharmony_ci		/* RGB2YUV */
23462306a36Sopenharmony_ci		coeffs = mxc_isi_rgb2yuv_coeffs;
23562306a36Sopenharmony_ci		val |= CHNL_IMG_CTRL_CSC_MODE(CHNL_IMG_CTRL_CSC_MODE_RGB2YCBCR);
23662306a36Sopenharmony_ci	} else {
23762306a36Sopenharmony_ci		/* Bypass CSC */
23862306a36Sopenharmony_ci		cscen = false;
23962306a36Sopenharmony_ci		val |= CHNL_IMG_CTRL_CSC_BYPASS;
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	dev_dbg(pipe->isi->dev, "CSC: %s -> %s\n",
24362306a36Sopenharmony_ci		encodings[in_encoding], encodings[out_encoding]);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if (cscen) {
24662306a36Sopenharmony_ci		mxc_isi_write(pipe, CHNL_CSC_COEFF0, coeffs[0]);
24762306a36Sopenharmony_ci		mxc_isi_write(pipe, CHNL_CSC_COEFF1, coeffs[1]);
24862306a36Sopenharmony_ci		mxc_isi_write(pipe, CHNL_CSC_COEFF2, coeffs[2]);
24962306a36Sopenharmony_ci		mxc_isi_write(pipe, CHNL_CSC_COEFF3, coeffs[3]);
25062306a36Sopenharmony_ci		mxc_isi_write(pipe, CHNL_CSC_COEFF4, coeffs[4]);
25162306a36Sopenharmony_ci		mxc_isi_write(pipe, CHNL_CSC_COEFF5, coeffs[5]);
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_IMG_CTRL, val);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	*bypass = !cscen;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_civoid mxc_isi_channel_set_alpha(struct mxc_isi_pipe *pipe, u8 alpha)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	u32 val;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	val = mxc_isi_read(pipe, CHNL_IMG_CTRL);
26462306a36Sopenharmony_ci	val &= ~CHNL_IMG_CTRL_GBL_ALPHA_VAL_MASK;
26562306a36Sopenharmony_ci	val |= CHNL_IMG_CTRL_GBL_ALPHA_VAL(alpha) |
26662306a36Sopenharmony_ci	       CHNL_IMG_CTRL_GBL_ALPHA_EN;
26762306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_IMG_CTRL, val);
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_civoid mxc_isi_channel_set_flip(struct mxc_isi_pipe *pipe, bool hflip, bool vflip)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	u32 val;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	val = mxc_isi_read(pipe, CHNL_IMG_CTRL);
27562306a36Sopenharmony_ci	val &= ~(CHNL_IMG_CTRL_VFLIP_EN | CHNL_IMG_CTRL_HFLIP_EN);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if (vflip)
27862306a36Sopenharmony_ci		val |= CHNL_IMG_CTRL_VFLIP_EN;
27962306a36Sopenharmony_ci	if (hflip)
28062306a36Sopenharmony_ci		val |= CHNL_IMG_CTRL_HFLIP_EN;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_IMG_CTRL, val);
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic void mxc_isi_channel_set_panic_threshold(struct mxc_isi_pipe *pipe)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	const struct mxc_isi_set_thd *set_thd = pipe->isi->pdata->set_thd;
28862306a36Sopenharmony_ci	u32 val;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	val = mxc_isi_read(pipe, CHNL_OUT_BUF_CTRL);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	val &= ~(set_thd->panic_set_thd_y.mask);
29362306a36Sopenharmony_ci	val |= set_thd->panic_set_thd_y.threshold << set_thd->panic_set_thd_y.offset;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	val &= ~(set_thd->panic_set_thd_u.mask);
29662306a36Sopenharmony_ci	val |= set_thd->panic_set_thd_u.threshold << set_thd->panic_set_thd_u.offset;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	val &= ~(set_thd->panic_set_thd_v.mask);
29962306a36Sopenharmony_ci	val |= set_thd->panic_set_thd_v.threshold << set_thd->panic_set_thd_v.offset;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_OUT_BUF_CTRL, val);
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic void mxc_isi_channel_set_control(struct mxc_isi_pipe *pipe,
30562306a36Sopenharmony_ci					enum mxc_isi_input_id input,
30662306a36Sopenharmony_ci					bool bypass)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	u32 val;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	mutex_lock(&pipe->lock);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	val = mxc_isi_read(pipe, CHNL_CTRL);
31362306a36Sopenharmony_ci	val &= ~(CHNL_CTRL_CHNL_BYPASS | CHNL_CTRL_CHAIN_BUF_MASK |
31462306a36Sopenharmony_ci		 CHNL_CTRL_BLANK_PXL_MASK | CHNL_CTRL_SRC_TYPE_MASK |
31562306a36Sopenharmony_ci		 CHNL_CTRL_MIPI_VC_ID_MASK | CHNL_CTRL_SRC_INPUT_MASK);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	/*
31862306a36Sopenharmony_ci	 * If no scaling or color space conversion is needed, bypass the
31962306a36Sopenharmony_ci	 * channel.
32062306a36Sopenharmony_ci	 */
32162306a36Sopenharmony_ci	if (bypass)
32262306a36Sopenharmony_ci		val |= CHNL_CTRL_CHNL_BYPASS;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	/* Chain line buffers if needed. */
32562306a36Sopenharmony_ci	if (pipe->chained)
32662306a36Sopenharmony_ci		val |= CHNL_CTRL_CHAIN_BUF(CHNL_CTRL_CHAIN_BUF_2_CHAIN);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	val |= CHNL_CTRL_BLANK_PXL(0xff);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/* Input source (including VC configuration for CSI-2) */
33162306a36Sopenharmony_ci	if (input == MXC_ISI_INPUT_MEM) {
33262306a36Sopenharmony_ci		/*
33362306a36Sopenharmony_ci		 * The memory input is connected to the last port of the
33462306a36Sopenharmony_ci		 * crossbar switch, after all pixel link inputs. The SRC_INPUT
33562306a36Sopenharmony_ci		 * field controls the input selection and must be set
33662306a36Sopenharmony_ci		 * accordingly, despite being documented as ignored when using
33762306a36Sopenharmony_ci		 * the memory input in the i.MX8MP reference manual, and
33862306a36Sopenharmony_ci		 * reserved in the i.MX8MN reference manual.
33962306a36Sopenharmony_ci		 */
34062306a36Sopenharmony_ci		val |= CHNL_CTRL_SRC_TYPE(CHNL_CTRL_SRC_TYPE_MEMORY);
34162306a36Sopenharmony_ci		val |= CHNL_CTRL_SRC_INPUT(pipe->isi->pdata->num_ports);
34262306a36Sopenharmony_ci	} else {
34362306a36Sopenharmony_ci		val |= CHNL_CTRL_SRC_TYPE(CHNL_CTRL_SRC_TYPE_DEVICE);
34462306a36Sopenharmony_ci		val |= CHNL_CTRL_SRC_INPUT(input);
34562306a36Sopenharmony_ci		val |= CHNL_CTRL_MIPI_VC_ID(0); /* FIXME: For CSI-2 only */
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_CTRL, val);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	mutex_unlock(&pipe->lock);
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_civoid mxc_isi_channel_config(struct mxc_isi_pipe *pipe,
35462306a36Sopenharmony_ci			    enum mxc_isi_input_id input,
35562306a36Sopenharmony_ci			    const struct v4l2_area *in_size,
35662306a36Sopenharmony_ci			    const struct v4l2_area *scale,
35762306a36Sopenharmony_ci			    const struct v4l2_rect *crop,
35862306a36Sopenharmony_ci			    enum mxc_isi_encoding in_encoding,
35962306a36Sopenharmony_ci			    enum mxc_isi_encoding out_encoding)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	bool csc_bypass;
36262306a36Sopenharmony_ci	bool scaler_bypass;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/* Input frame size */
36562306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_IMG_CFG,
36662306a36Sopenharmony_ci		      CHNL_IMG_CFG_HEIGHT(in_size->height) |
36762306a36Sopenharmony_ci		      CHNL_IMG_CFG_WIDTH(in_size->width));
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	/* Scaling */
37062306a36Sopenharmony_ci	mxc_isi_channel_set_scaling(pipe, in_encoding, in_size, scale,
37162306a36Sopenharmony_ci				    &scaler_bypass);
37262306a36Sopenharmony_ci	mxc_isi_channel_set_crop(pipe, scale, crop);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	/* CSC */
37562306a36Sopenharmony_ci	mxc_isi_channel_set_csc(pipe, in_encoding, out_encoding, &csc_bypass);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	/* Output buffer management */
37862306a36Sopenharmony_ci	mxc_isi_channel_set_panic_threshold(pipe);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	/* Channel control */
38162306a36Sopenharmony_ci	mxc_isi_channel_set_control(pipe, input, csc_bypass && scaler_bypass);
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_civoid mxc_isi_channel_set_input_format(struct mxc_isi_pipe *pipe,
38562306a36Sopenharmony_ci				      const struct mxc_isi_format_info *info,
38662306a36Sopenharmony_ci				      const struct v4l2_pix_format_mplane *format)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	unsigned int bpl = format->plane_fmt[0].bytesperline;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_MEM_RD_CTRL,
39162306a36Sopenharmony_ci		      CHNL_MEM_RD_CTRL_IMG_TYPE(info->isi_in_format));
39262306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_IN_BUF_PITCH,
39362306a36Sopenharmony_ci		      CHNL_IN_BUF_PITCH_LINE_PITCH(bpl));
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_civoid mxc_isi_channel_set_output_format(struct mxc_isi_pipe *pipe,
39762306a36Sopenharmony_ci				       const struct mxc_isi_format_info *info,
39862306a36Sopenharmony_ci				       struct v4l2_pix_format_mplane *format)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	u32 val;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	/* set outbuf format */
40362306a36Sopenharmony_ci	dev_dbg(pipe->isi->dev, "output format %p4cc", &format->pixelformat);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	val = mxc_isi_read(pipe, CHNL_IMG_CTRL);
40662306a36Sopenharmony_ci	val &= ~CHNL_IMG_CTRL_FORMAT_MASK;
40762306a36Sopenharmony_ci	val |= CHNL_IMG_CTRL_FORMAT(info->isi_out_format);
40862306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_IMG_CTRL, val);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	/* line pitch */
41162306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_OUT_BUF_PITCH,
41262306a36Sopenharmony_ci		      format->plane_fmt[0].bytesperline);
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
41662306a36Sopenharmony_ci * IRQ
41762306a36Sopenharmony_ci */
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ciu32 mxc_isi_channel_irq_status(struct mxc_isi_pipe *pipe, bool clear)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	u32 status;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	status = mxc_isi_read(pipe, CHNL_STS);
42462306a36Sopenharmony_ci	if (clear)
42562306a36Sopenharmony_ci		mxc_isi_write(pipe, CHNL_STS, status);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	return status;
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_civoid mxc_isi_channel_irq_clear(struct mxc_isi_pipe *pipe)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_STS, 0xffffffff);
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic void mxc_isi_channel_irq_enable(struct mxc_isi_pipe *pipe)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	const struct mxc_isi_ier_reg *ier_reg = pipe->isi->pdata->ier_reg;
43862306a36Sopenharmony_ci	u32 val;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	val = CHNL_IER_FRM_RCVD_EN |
44162306a36Sopenharmony_ci		CHNL_IER_AXI_WR_ERR_U_EN |
44262306a36Sopenharmony_ci		CHNL_IER_AXI_WR_ERR_V_EN |
44362306a36Sopenharmony_ci		CHNL_IER_AXI_WR_ERR_Y_EN;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	/* Y/U/V overflow enable */
44662306a36Sopenharmony_ci	val |= ier_reg->oflw_y_buf_en.mask |
44762306a36Sopenharmony_ci	       ier_reg->oflw_u_buf_en.mask |
44862306a36Sopenharmony_ci	       ier_reg->oflw_v_buf_en.mask;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	/* Y/U/V excess overflow enable */
45162306a36Sopenharmony_ci	val |= ier_reg->excs_oflw_y_buf_en.mask |
45262306a36Sopenharmony_ci	       ier_reg->excs_oflw_u_buf_en.mask |
45362306a36Sopenharmony_ci	       ier_reg->excs_oflw_v_buf_en.mask;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	/* Y/U/V panic enable */
45662306a36Sopenharmony_ci	val |= ier_reg->panic_y_buf_en.mask |
45762306a36Sopenharmony_ci	       ier_reg->panic_u_buf_en.mask |
45862306a36Sopenharmony_ci	       ier_reg->panic_v_buf_en.mask;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	mxc_isi_channel_irq_clear(pipe);
46162306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_IER, val);
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic void mxc_isi_channel_irq_disable(struct mxc_isi_pipe *pipe)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_IER, 0);
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
47062306a36Sopenharmony_ci * Init, deinit, enable, disable
47162306a36Sopenharmony_ci */
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cistatic void mxc_isi_channel_sw_reset(struct mxc_isi_pipe *pipe, bool enable_clk)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_CTRL, CHNL_CTRL_SW_RST);
47662306a36Sopenharmony_ci	mdelay(5);
47762306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_CTRL, enable_clk ? CHNL_CTRL_CLK_EN : 0);
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic void __mxc_isi_channel_get(struct mxc_isi_pipe *pipe)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	if (!pipe->use_count++)
48362306a36Sopenharmony_ci		mxc_isi_channel_sw_reset(pipe, true);
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_civoid mxc_isi_channel_get(struct mxc_isi_pipe *pipe)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	mutex_lock(&pipe->lock);
48962306a36Sopenharmony_ci	__mxc_isi_channel_get(pipe);
49062306a36Sopenharmony_ci	mutex_unlock(&pipe->lock);
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cistatic void __mxc_isi_channel_put(struct mxc_isi_pipe *pipe)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	if (!--pipe->use_count)
49662306a36Sopenharmony_ci		mxc_isi_channel_sw_reset(pipe, false);
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_civoid mxc_isi_channel_put(struct mxc_isi_pipe *pipe)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	mutex_lock(&pipe->lock);
50262306a36Sopenharmony_ci	__mxc_isi_channel_put(pipe);
50362306a36Sopenharmony_ci	mutex_unlock(&pipe->lock);
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_civoid mxc_isi_channel_enable(struct mxc_isi_pipe *pipe)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	u32 val;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	mxc_isi_channel_irq_enable(pipe);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	mutex_lock(&pipe->lock);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	val = mxc_isi_read(pipe, CHNL_CTRL);
51562306a36Sopenharmony_ci	val |= CHNL_CTRL_CHNL_EN;
51662306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_CTRL, val);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	mutex_unlock(&pipe->lock);
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_civoid mxc_isi_channel_disable(struct mxc_isi_pipe *pipe)
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	u32 val;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	mxc_isi_channel_irq_disable(pipe);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	mutex_lock(&pipe->lock);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	val = mxc_isi_read(pipe, CHNL_CTRL);
53062306a36Sopenharmony_ci	val &= ~CHNL_CTRL_CHNL_EN;
53162306a36Sopenharmony_ci	mxc_isi_write(pipe, CHNL_CTRL, val);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	mutex_unlock(&pipe->lock);
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
53762306a36Sopenharmony_ci * Resource management & chaining
53862306a36Sopenharmony_ci */
53962306a36Sopenharmony_ciint mxc_isi_channel_acquire(struct mxc_isi_pipe *pipe,
54062306a36Sopenharmony_ci			    mxc_isi_pipe_irq_t irq_handler, bool bypass)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	u8 resources;
54362306a36Sopenharmony_ci	int ret = 0;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	mutex_lock(&pipe->lock);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	if (pipe->irq_handler) {
54862306a36Sopenharmony_ci		ret = -EBUSY;
54962306a36Sopenharmony_ci		goto unlock;
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	/*
55362306a36Sopenharmony_ci	 * Make sure the resources we need are available. The output buffer is
55462306a36Sopenharmony_ci	 * always needed to operate the channel, the line buffer is needed only
55562306a36Sopenharmony_ci	 * when the channel isn't in bypass mode.
55662306a36Sopenharmony_ci	 */
55762306a36Sopenharmony_ci	resources = MXC_ISI_CHANNEL_RES_OUTPUT_BUF
55862306a36Sopenharmony_ci		  | (!bypass ? MXC_ISI_CHANNEL_RES_LINE_BUF : 0);
55962306a36Sopenharmony_ci	if ((pipe->available_res & resources) != resources) {
56062306a36Sopenharmony_ci		ret = -EBUSY;
56162306a36Sopenharmony_ci		goto unlock;
56262306a36Sopenharmony_ci	}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	/* Acquire the channel resources. */
56562306a36Sopenharmony_ci	pipe->acquired_res = resources;
56662306a36Sopenharmony_ci	pipe->available_res &= ~resources;
56762306a36Sopenharmony_ci	pipe->irq_handler = irq_handler;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ciunlock:
57062306a36Sopenharmony_ci	mutex_unlock(&pipe->lock);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	return ret;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_civoid mxc_isi_channel_release(struct mxc_isi_pipe *pipe)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	mutex_lock(&pipe->lock);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	pipe->irq_handler = NULL;
58062306a36Sopenharmony_ci	pipe->available_res |= pipe->acquired_res;
58162306a36Sopenharmony_ci	pipe->acquired_res = 0;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	mutex_unlock(&pipe->lock);
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci/*
58762306a36Sopenharmony_ci * We currently support line buffer chaining only, for handling images with a
58862306a36Sopenharmony_ci * width larger than 2048 pixels.
58962306a36Sopenharmony_ci *
59062306a36Sopenharmony_ci * TODO: Support secondary line buffer for downscaling YUV420 images.
59162306a36Sopenharmony_ci */
59262306a36Sopenharmony_ciint mxc_isi_channel_chain(struct mxc_isi_pipe *pipe, bool bypass)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	/* Channel chaining requires both line and output buffer. */
59562306a36Sopenharmony_ci	const u8 resources = MXC_ISI_CHANNEL_RES_OUTPUT_BUF
59662306a36Sopenharmony_ci			   | MXC_ISI_CHANNEL_RES_LINE_BUF;
59762306a36Sopenharmony_ci	struct mxc_isi_pipe *chained_pipe = pipe + 1;
59862306a36Sopenharmony_ci	int ret = 0;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	/*
60162306a36Sopenharmony_ci	 * If buffer chaining is required, make sure this channel is not the
60262306a36Sopenharmony_ci	 * last one, otherwise there's no 'next' channel to chain with. This
60362306a36Sopenharmony_ci	 * should be prevented by checks in the set format handlers, but let's
60462306a36Sopenharmony_ci	 * be defensive.
60562306a36Sopenharmony_ci	 */
60662306a36Sopenharmony_ci	if (WARN_ON(pipe->id == pipe->isi->pdata->num_channels - 1))
60762306a36Sopenharmony_ci		return -EINVAL;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	mutex_lock(&chained_pipe->lock);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	/* Safety checks. */
61262306a36Sopenharmony_ci	if (WARN_ON(pipe->chained || chained_pipe->chained_res)) {
61362306a36Sopenharmony_ci		ret = -EINVAL;
61462306a36Sopenharmony_ci		goto unlock;
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	if ((chained_pipe->available_res & resources) != resources) {
61862306a36Sopenharmony_ci		ret = -EBUSY;
61962306a36Sopenharmony_ci		goto unlock;
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	pipe->chained = true;
62362306a36Sopenharmony_ci	chained_pipe->chained_res |= resources;
62462306a36Sopenharmony_ci	chained_pipe->available_res &= ~resources;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	__mxc_isi_channel_get(chained_pipe);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ciunlock:
62962306a36Sopenharmony_ci	mutex_unlock(&chained_pipe->lock);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	return ret;
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_civoid mxc_isi_channel_unchain(struct mxc_isi_pipe *pipe)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	struct mxc_isi_pipe *chained_pipe = pipe + 1;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	if (!pipe->chained)
63962306a36Sopenharmony_ci		return;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	pipe->chained = false;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	mutex_lock(&chained_pipe->lock);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	chained_pipe->available_res |= chained_pipe->chained_res;
64662306a36Sopenharmony_ci	chained_pipe->chained_res = 0;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	__mxc_isi_channel_put(chained_pipe);
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	mutex_unlock(&chained_pipe->lock);
65162306a36Sopenharmony_ci}
652