xref: /openbmc/linux/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1cf21f328SLaurent Pinchart // SPDX-License-Identifier: GPL-2.0
2cf21f328SLaurent Pinchart /*
3cf21f328SLaurent Pinchart  * Copyright 2019-2020 NXP
4cf21f328SLaurent Pinchart  */
5cf21f328SLaurent Pinchart 
6cf21f328SLaurent Pinchart #include <linux/delay.h>
7cf21f328SLaurent Pinchart #include <linux/device.h>
8cf21f328SLaurent Pinchart #include <linux/io.h>
9cf21f328SLaurent Pinchart #include <linux/types.h>
10cf21f328SLaurent Pinchart 
11cf21f328SLaurent Pinchart #include "imx8-isi-core.h"
12cf21f328SLaurent Pinchart #include "imx8-isi-regs.h"
13cf21f328SLaurent Pinchart 
14cf21f328SLaurent Pinchart #define	ISI_DOWNSCALE_THRESHOLD		0x4000
15cf21f328SLaurent Pinchart 
mxc_isi_read(struct mxc_isi_pipe * pipe,u32 reg)16cf21f328SLaurent Pinchart static inline u32 mxc_isi_read(struct mxc_isi_pipe *pipe, u32 reg)
17cf21f328SLaurent Pinchart {
18cf21f328SLaurent Pinchart 	return readl(pipe->regs + reg);
19cf21f328SLaurent Pinchart }
20cf21f328SLaurent Pinchart 
mxc_isi_write(struct mxc_isi_pipe * pipe,u32 reg,u32 val)21cf21f328SLaurent Pinchart static inline void mxc_isi_write(struct mxc_isi_pipe *pipe, u32 reg, u32 val)
22cf21f328SLaurent Pinchart {
23cf21f328SLaurent Pinchart 	writel(val, pipe->regs + reg);
24cf21f328SLaurent Pinchart }
25cf21f328SLaurent Pinchart 
26cf21f328SLaurent Pinchart /* -----------------------------------------------------------------------------
27cf21f328SLaurent Pinchart  * Buffers & M2M operation
28cf21f328SLaurent Pinchart  */
29cf21f328SLaurent Pinchart 
mxc_isi_channel_set_inbuf(struct mxc_isi_pipe * pipe,dma_addr_t dma_addr)30cf21f328SLaurent Pinchart void mxc_isi_channel_set_inbuf(struct mxc_isi_pipe *pipe, dma_addr_t dma_addr)
31cf21f328SLaurent Pinchart {
32*ba0ad6edSArnd Bergmann 	mxc_isi_write(pipe, CHNL_IN_BUF_ADDR, lower_32_bits(dma_addr));
33cf21f328SLaurent Pinchart 	if (pipe->isi->pdata->has_36bit_dma)
34*ba0ad6edSArnd Bergmann 		mxc_isi_write(pipe, CHNL_IN_BUF_XTND_ADDR,
35*ba0ad6edSArnd Bergmann 			      upper_32_bits(dma_addr));
36cf21f328SLaurent Pinchart }
37cf21f328SLaurent Pinchart 
mxc_isi_channel_set_outbuf(struct mxc_isi_pipe * pipe,const dma_addr_t dma_addrs[3],enum mxc_isi_buf_id buf_id)38cf21f328SLaurent Pinchart void mxc_isi_channel_set_outbuf(struct mxc_isi_pipe *pipe,
39cf21f328SLaurent Pinchart 				const dma_addr_t dma_addrs[3],
40cf21f328SLaurent Pinchart 				enum mxc_isi_buf_id buf_id)
41cf21f328SLaurent Pinchart {
42cf21f328SLaurent Pinchart 	int val;
43cf21f328SLaurent Pinchart 
44cf21f328SLaurent Pinchart 	val = mxc_isi_read(pipe, CHNL_OUT_BUF_CTRL);
45cf21f328SLaurent Pinchart 
46cf21f328SLaurent Pinchart 	if (buf_id == MXC_ISI_BUF1) {
47*ba0ad6edSArnd Bergmann 		mxc_isi_write(pipe, CHNL_OUT_BUF1_ADDR_Y,
48*ba0ad6edSArnd Bergmann 			      lower_32_bits(dma_addrs[0]));
49*ba0ad6edSArnd Bergmann 		mxc_isi_write(pipe, CHNL_OUT_BUF1_ADDR_U,
50*ba0ad6edSArnd Bergmann 			      lower_32_bits(dma_addrs[1]));
51*ba0ad6edSArnd Bergmann 		mxc_isi_write(pipe, CHNL_OUT_BUF1_ADDR_V,
52*ba0ad6edSArnd Bergmann 			      lower_32_bits(dma_addrs[2]));
53cf21f328SLaurent Pinchart 		if (pipe->isi->pdata->has_36bit_dma) {
54cf21f328SLaurent Pinchart 			mxc_isi_write(pipe, CHNL_Y_BUF1_XTND_ADDR,
55*ba0ad6edSArnd Bergmann 				      upper_32_bits(dma_addrs[0]));
56cf21f328SLaurent Pinchart 			mxc_isi_write(pipe, CHNL_U_BUF1_XTND_ADDR,
57*ba0ad6edSArnd Bergmann 				      upper_32_bits(dma_addrs[1]));
58cf21f328SLaurent Pinchart 			mxc_isi_write(pipe, CHNL_V_BUF1_XTND_ADDR,
59*ba0ad6edSArnd Bergmann 				      upper_32_bits(dma_addrs[2]));
60cf21f328SLaurent Pinchart 		}
61cf21f328SLaurent Pinchart 		val ^= CHNL_OUT_BUF_CTRL_LOAD_BUF1_ADDR;
62cf21f328SLaurent Pinchart 	} else  {
63*ba0ad6edSArnd Bergmann 		mxc_isi_write(pipe, CHNL_OUT_BUF2_ADDR_Y,
64*ba0ad6edSArnd Bergmann 			      lower_32_bits(dma_addrs[0]));
65*ba0ad6edSArnd Bergmann 		mxc_isi_write(pipe, CHNL_OUT_BUF2_ADDR_U,
66*ba0ad6edSArnd Bergmann 			      lower_32_bits(dma_addrs[1]));
67*ba0ad6edSArnd Bergmann 		mxc_isi_write(pipe, CHNL_OUT_BUF2_ADDR_V,
68*ba0ad6edSArnd Bergmann 			      lower_32_bits(dma_addrs[2]));
69cf21f328SLaurent Pinchart 		if (pipe->isi->pdata->has_36bit_dma) {
70cf21f328SLaurent Pinchart 			mxc_isi_write(pipe, CHNL_Y_BUF2_XTND_ADDR,
71*ba0ad6edSArnd Bergmann 				      upper_32_bits(dma_addrs[0]));
72cf21f328SLaurent Pinchart 			mxc_isi_write(pipe, CHNL_U_BUF2_XTND_ADDR,
73*ba0ad6edSArnd Bergmann 				      upper_32_bits(dma_addrs[1]));
74cf21f328SLaurent Pinchart 			mxc_isi_write(pipe, CHNL_V_BUF2_XTND_ADDR,
75*ba0ad6edSArnd Bergmann 				      upper_32_bits(dma_addrs[2]));
76cf21f328SLaurent Pinchart 		}
77cf21f328SLaurent Pinchart 		val ^= CHNL_OUT_BUF_CTRL_LOAD_BUF2_ADDR;
78cf21f328SLaurent Pinchart 	}
79cf21f328SLaurent Pinchart 
80cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_OUT_BUF_CTRL, val);
81cf21f328SLaurent Pinchart }
82cf21f328SLaurent Pinchart 
mxc_isi_channel_m2m_start(struct mxc_isi_pipe * pipe)83cf21f328SLaurent Pinchart void mxc_isi_channel_m2m_start(struct mxc_isi_pipe *pipe)
84cf21f328SLaurent Pinchart {
85cf21f328SLaurent Pinchart 	u32 val;
86cf21f328SLaurent Pinchart 
87cf21f328SLaurent Pinchart 	val = mxc_isi_read(pipe, CHNL_MEM_RD_CTRL);
88cf21f328SLaurent Pinchart 	val &= ~CHNL_MEM_RD_CTRL_READ_MEM;
89cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_MEM_RD_CTRL, val);
902bab9442SLaurent Pinchart 
912bab9442SLaurent Pinchart 	fsleep(300);
92cf21f328SLaurent Pinchart 
93cf21f328SLaurent Pinchart 	val |= CHNL_MEM_RD_CTRL_READ_MEM;
94cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_MEM_RD_CTRL, val);
95cf21f328SLaurent Pinchart }
96cf21f328SLaurent Pinchart 
97cf21f328SLaurent Pinchart /* -----------------------------------------------------------------------------
98cf21f328SLaurent Pinchart  * Pipeline configuration
99cf21f328SLaurent Pinchart  */
100cf21f328SLaurent Pinchart 
mxc_isi_channel_scaling_ratio(unsigned int from,unsigned int to,u32 * dec)101cf21f328SLaurent Pinchart static u32 mxc_isi_channel_scaling_ratio(unsigned int from, unsigned int to,
102cf21f328SLaurent Pinchart 					 u32 *dec)
103cf21f328SLaurent Pinchart {
104cf21f328SLaurent Pinchart 	unsigned int ratio = from / to;
105cf21f328SLaurent Pinchart 
106cf21f328SLaurent Pinchart 	if (ratio < 2)
107cf21f328SLaurent Pinchart 		*dec = 1;
108cf21f328SLaurent Pinchart 	else if (ratio < 4)
109cf21f328SLaurent Pinchart 		*dec = 2;
110cf21f328SLaurent Pinchart 	else if (ratio < 8)
111cf21f328SLaurent Pinchart 		*dec = 4;
112cf21f328SLaurent Pinchart 	else
113cf21f328SLaurent Pinchart 		*dec = 8;
114cf21f328SLaurent Pinchart 
115cf21f328SLaurent Pinchart 	return min_t(u32, from * 0x1000 / (to * *dec), ISI_DOWNSCALE_THRESHOLD);
116cf21f328SLaurent Pinchart }
117cf21f328SLaurent Pinchart 
mxc_isi_channel_set_scaling(struct mxc_isi_pipe * pipe,enum mxc_isi_encoding encoding,const struct v4l2_area * in_size,const struct v4l2_area * out_size,bool * bypass)118cf21f328SLaurent Pinchart static void mxc_isi_channel_set_scaling(struct mxc_isi_pipe *pipe,
119cf21f328SLaurent Pinchart 					enum mxc_isi_encoding encoding,
120cf21f328SLaurent Pinchart 					const struct v4l2_area *in_size,
121cf21f328SLaurent Pinchart 					const struct v4l2_area *out_size,
122cf21f328SLaurent Pinchart 					bool *bypass)
123cf21f328SLaurent Pinchart {
124cf21f328SLaurent Pinchart 	u32 xscale, yscale;
125cf21f328SLaurent Pinchart 	u32 decx, decy;
126cf21f328SLaurent Pinchart 	u32 val;
127cf21f328SLaurent Pinchart 
128cf21f328SLaurent Pinchart 	dev_dbg(pipe->isi->dev, "input %ux%u, output %ux%u\n",
129cf21f328SLaurent Pinchart 		in_size->width, in_size->height,
130cf21f328SLaurent Pinchart 		out_size->width, out_size->height);
131cf21f328SLaurent Pinchart 
132cf21f328SLaurent Pinchart 	xscale = mxc_isi_channel_scaling_ratio(in_size->width, out_size->width,
133cf21f328SLaurent Pinchart 					       &decx);
134cf21f328SLaurent Pinchart 	yscale = mxc_isi_channel_scaling_ratio(in_size->height, out_size->height,
135cf21f328SLaurent Pinchart 					       &decy);
136cf21f328SLaurent Pinchart 
137cf21f328SLaurent Pinchart 	val = mxc_isi_read(pipe, CHNL_IMG_CTRL);
138cf21f328SLaurent Pinchart 	val &= ~(CHNL_IMG_CTRL_DEC_X_MASK | CHNL_IMG_CTRL_DEC_Y_MASK |
139cf21f328SLaurent Pinchart 		 CHNL_IMG_CTRL_YCBCR_MODE);
140cf21f328SLaurent Pinchart 
141cf21f328SLaurent Pinchart 	val |= CHNL_IMG_CTRL_DEC_X(ilog2(decx))
142cf21f328SLaurent Pinchart 	    |  CHNL_IMG_CTRL_DEC_Y(ilog2(decy));
143cf21f328SLaurent Pinchart 
144cf21f328SLaurent Pinchart 	/*
145cf21f328SLaurent Pinchart 	 * Contrary to what the documentation states, YCBCR_MODE does not
146cf21f328SLaurent Pinchart 	 * control conversion between YCbCr and RGB, but whether the scaler
147cf21f328SLaurent Pinchart 	 * operates in YUV mode or in RGB mode. It must be set when the scaler
148cf21f328SLaurent Pinchart 	 * input is YUV.
149cf21f328SLaurent Pinchart 	 */
150cf21f328SLaurent Pinchart 	if (encoding == MXC_ISI_ENC_YUV)
151cf21f328SLaurent Pinchart 		val |= CHNL_IMG_CTRL_YCBCR_MODE;
152cf21f328SLaurent Pinchart 
153cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_IMG_CTRL, val);
154cf21f328SLaurent Pinchart 
155cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_SCALE_FACTOR,
156cf21f328SLaurent Pinchart 		      CHNL_SCALE_FACTOR_Y_SCALE(yscale) |
157cf21f328SLaurent Pinchart 		      CHNL_SCALE_FACTOR_X_SCALE(xscale));
158cf21f328SLaurent Pinchart 
159cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_SCALE_OFFSET, 0);
160cf21f328SLaurent Pinchart 
161cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_SCL_IMG_CFG,
162cf21f328SLaurent Pinchart 		      CHNL_SCL_IMG_CFG_HEIGHT(out_size->height) |
163cf21f328SLaurent Pinchart 		      CHNL_SCL_IMG_CFG_WIDTH(out_size->width));
164cf21f328SLaurent Pinchart 
165cf21f328SLaurent Pinchart 	*bypass = in_size->height == out_size->height &&
166cf21f328SLaurent Pinchart 		  in_size->width == out_size->width;
167cf21f328SLaurent Pinchart }
168cf21f328SLaurent Pinchart 
mxc_isi_channel_set_crop(struct mxc_isi_pipe * pipe,const struct v4l2_area * src,const struct v4l2_rect * dst)169cf21f328SLaurent Pinchart static void mxc_isi_channel_set_crop(struct mxc_isi_pipe *pipe,
170cf21f328SLaurent Pinchart 				     const struct v4l2_area *src,
171cf21f328SLaurent Pinchart 				     const struct v4l2_rect *dst)
172cf21f328SLaurent Pinchart {
173cf21f328SLaurent Pinchart 	u32 val, val0, val1;
174cf21f328SLaurent Pinchart 
175cf21f328SLaurent Pinchart 	val = mxc_isi_read(pipe, CHNL_IMG_CTRL);
176cf21f328SLaurent Pinchart 	val &= ~CHNL_IMG_CTRL_CROP_EN;
177cf21f328SLaurent Pinchart 
178cf21f328SLaurent Pinchart 	if (src->height == dst->height && src->width == dst->width) {
179cf21f328SLaurent Pinchart 		mxc_isi_write(pipe, CHNL_IMG_CTRL, val);
180cf21f328SLaurent Pinchart 		return;
181cf21f328SLaurent Pinchart 	}
182cf21f328SLaurent Pinchart 
183cf21f328SLaurent Pinchart 	val |= CHNL_IMG_CTRL_CROP_EN;
184cf21f328SLaurent Pinchart 	val0 = CHNL_CROP_ULC_X(dst->left) | CHNL_CROP_ULC_Y(dst->top);
185cf21f328SLaurent Pinchart 	val1 = CHNL_CROP_LRC_X(dst->width) | CHNL_CROP_LRC_Y(dst->height);
186cf21f328SLaurent Pinchart 
187cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_CROP_ULC, val0);
188cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_CROP_LRC, val1 + val0);
189cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_IMG_CTRL, val);
190cf21f328SLaurent Pinchart }
191cf21f328SLaurent Pinchart 
192cf21f328SLaurent Pinchart /*
193cf21f328SLaurent Pinchart  * A2,A1,      B1, A3,     B3, B2,
194cf21f328SLaurent Pinchart  * C2, C1,     D1, C3,     D3, D2
195cf21f328SLaurent Pinchart  */
196cf21f328SLaurent Pinchart static const u32 mxc_isi_yuv2rgb_coeffs[6] = {
197cf21f328SLaurent Pinchart 	/* YUV -> RGB */
198cf21f328SLaurent Pinchart 	0x0000012a, 0x012a0198, 0x0730079c,
199cf21f328SLaurent Pinchart 	0x0204012a, 0x01f00000, 0x01800180
200cf21f328SLaurent Pinchart };
201cf21f328SLaurent Pinchart 
202cf21f328SLaurent Pinchart static const u32 mxc_isi_rgb2yuv_coeffs[6] = {
203cf21f328SLaurent Pinchart 	/* RGB->YUV */
204cf21f328SLaurent Pinchart 	0x00810041, 0x07db0019, 0x007007b6,
205cf21f328SLaurent Pinchart 	0x07a20070, 0x001007ee, 0x00800080
206cf21f328SLaurent Pinchart };
207cf21f328SLaurent Pinchart 
mxc_isi_channel_set_csc(struct mxc_isi_pipe * pipe,enum mxc_isi_encoding in_encoding,enum mxc_isi_encoding out_encoding,bool * bypass)208cf21f328SLaurent Pinchart static void mxc_isi_channel_set_csc(struct mxc_isi_pipe *pipe,
209cf21f328SLaurent Pinchart 				    enum mxc_isi_encoding in_encoding,
210cf21f328SLaurent Pinchart 				    enum mxc_isi_encoding out_encoding,
211cf21f328SLaurent Pinchart 				    bool *bypass)
212cf21f328SLaurent Pinchart {
213cf21f328SLaurent Pinchart 	static const char * const encodings[] = {
214cf21f328SLaurent Pinchart 		[MXC_ISI_ENC_RAW] = "RAW",
215cf21f328SLaurent Pinchart 		[MXC_ISI_ENC_RGB] = "RGB",
216cf21f328SLaurent Pinchart 		[MXC_ISI_ENC_YUV] = "YUV",
217cf21f328SLaurent Pinchart 	};
218cf21f328SLaurent Pinchart 	const u32 *coeffs;
219cf21f328SLaurent Pinchart 	bool cscen = true;
220cf21f328SLaurent Pinchart 	u32 val;
221cf21f328SLaurent Pinchart 
222cf21f328SLaurent Pinchart 	val = mxc_isi_read(pipe, CHNL_IMG_CTRL);
223cf21f328SLaurent Pinchart 	val &= ~(CHNL_IMG_CTRL_CSC_BYPASS | CHNL_IMG_CTRL_CSC_MODE_MASK);
224cf21f328SLaurent Pinchart 
225cf21f328SLaurent Pinchart 	if (in_encoding == MXC_ISI_ENC_YUV &&
226cf21f328SLaurent Pinchart 	    out_encoding == MXC_ISI_ENC_RGB) {
227cf21f328SLaurent Pinchart 		/* YUV2RGB */
228cf21f328SLaurent Pinchart 		coeffs = mxc_isi_yuv2rgb_coeffs;
229cf21f328SLaurent Pinchart 		/* YCbCr enable???  */
230cf21f328SLaurent Pinchart 		val |= CHNL_IMG_CTRL_CSC_MODE(CHNL_IMG_CTRL_CSC_MODE_YCBCR2RGB);
231cf21f328SLaurent Pinchart 	} else if (in_encoding == MXC_ISI_ENC_RGB &&
232cf21f328SLaurent Pinchart 		   out_encoding == MXC_ISI_ENC_YUV) {
233cf21f328SLaurent Pinchart 		/* RGB2YUV */
234cf21f328SLaurent Pinchart 		coeffs = mxc_isi_rgb2yuv_coeffs;
235cf21f328SLaurent Pinchart 		val |= CHNL_IMG_CTRL_CSC_MODE(CHNL_IMG_CTRL_CSC_MODE_RGB2YCBCR);
236cf21f328SLaurent Pinchart 	} else {
237cf21f328SLaurent Pinchart 		/* Bypass CSC */
238cf21f328SLaurent Pinchart 		cscen = false;
239cf21f328SLaurent Pinchart 		val |= CHNL_IMG_CTRL_CSC_BYPASS;
240cf21f328SLaurent Pinchart 	}
241cf21f328SLaurent Pinchart 
242cf21f328SLaurent Pinchart 	dev_dbg(pipe->isi->dev, "CSC: %s -> %s\n",
243cf21f328SLaurent Pinchart 		encodings[in_encoding], encodings[out_encoding]);
244cf21f328SLaurent Pinchart 
245cf21f328SLaurent Pinchart 	if (cscen) {
246cf21f328SLaurent Pinchart 		mxc_isi_write(pipe, CHNL_CSC_COEFF0, coeffs[0]);
247cf21f328SLaurent Pinchart 		mxc_isi_write(pipe, CHNL_CSC_COEFF1, coeffs[1]);
248cf21f328SLaurent Pinchart 		mxc_isi_write(pipe, CHNL_CSC_COEFF2, coeffs[2]);
249cf21f328SLaurent Pinchart 		mxc_isi_write(pipe, CHNL_CSC_COEFF3, coeffs[3]);
250cf21f328SLaurent Pinchart 		mxc_isi_write(pipe, CHNL_CSC_COEFF4, coeffs[4]);
251cf21f328SLaurent Pinchart 		mxc_isi_write(pipe, CHNL_CSC_COEFF5, coeffs[5]);
252cf21f328SLaurent Pinchart 	}
253cf21f328SLaurent Pinchart 
254cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_IMG_CTRL, val);
255cf21f328SLaurent Pinchart 
256cf21f328SLaurent Pinchart 	*bypass = !cscen;
257cf21f328SLaurent Pinchart }
258cf21f328SLaurent Pinchart 
mxc_isi_channel_set_alpha(struct mxc_isi_pipe * pipe,u8 alpha)259cf21f328SLaurent Pinchart void mxc_isi_channel_set_alpha(struct mxc_isi_pipe *pipe, u8 alpha)
260cf21f328SLaurent Pinchart {
261cf21f328SLaurent Pinchart 	u32 val;
262cf21f328SLaurent Pinchart 
263cf21f328SLaurent Pinchart 	val = mxc_isi_read(pipe, CHNL_IMG_CTRL);
264cf21f328SLaurent Pinchart 	val &= ~CHNL_IMG_CTRL_GBL_ALPHA_VAL_MASK;
265cf21f328SLaurent Pinchart 	val |= CHNL_IMG_CTRL_GBL_ALPHA_VAL(alpha) |
266cf21f328SLaurent Pinchart 	       CHNL_IMG_CTRL_GBL_ALPHA_EN;
267cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_IMG_CTRL, val);
268cf21f328SLaurent Pinchart }
269cf21f328SLaurent Pinchart 
mxc_isi_channel_set_flip(struct mxc_isi_pipe * pipe,bool hflip,bool vflip)270cf21f328SLaurent Pinchart void mxc_isi_channel_set_flip(struct mxc_isi_pipe *pipe, bool hflip, bool vflip)
271cf21f328SLaurent Pinchart {
272cf21f328SLaurent Pinchart 	u32 val;
273cf21f328SLaurent Pinchart 
274cf21f328SLaurent Pinchart 	val = mxc_isi_read(pipe, CHNL_IMG_CTRL);
275cf21f328SLaurent Pinchart 	val &= ~(CHNL_IMG_CTRL_VFLIP_EN | CHNL_IMG_CTRL_HFLIP_EN);
276cf21f328SLaurent Pinchart 
277cf21f328SLaurent Pinchart 	if (vflip)
278cf21f328SLaurent Pinchart 		val |= CHNL_IMG_CTRL_VFLIP_EN;
279cf21f328SLaurent Pinchart 	if (hflip)
280cf21f328SLaurent Pinchart 		val |= CHNL_IMG_CTRL_HFLIP_EN;
281cf21f328SLaurent Pinchart 
282cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_IMG_CTRL, val);
283cf21f328SLaurent Pinchart }
284cf21f328SLaurent Pinchart 
mxc_isi_channel_set_panic_threshold(struct mxc_isi_pipe * pipe)285cf21f328SLaurent Pinchart static void mxc_isi_channel_set_panic_threshold(struct mxc_isi_pipe *pipe)
286cf21f328SLaurent Pinchart {
287cf21f328SLaurent Pinchart 	const struct mxc_isi_set_thd *set_thd = pipe->isi->pdata->set_thd;
288cf21f328SLaurent Pinchart 	u32 val;
289cf21f328SLaurent Pinchart 
290cf21f328SLaurent Pinchart 	val = mxc_isi_read(pipe, CHNL_OUT_BUF_CTRL);
291cf21f328SLaurent Pinchart 
292cf21f328SLaurent Pinchart 	val &= ~(set_thd->panic_set_thd_y.mask);
293cf21f328SLaurent Pinchart 	val |= set_thd->panic_set_thd_y.threshold << set_thd->panic_set_thd_y.offset;
294cf21f328SLaurent Pinchart 
295cf21f328SLaurent Pinchart 	val &= ~(set_thd->panic_set_thd_u.mask);
296cf21f328SLaurent Pinchart 	val |= set_thd->panic_set_thd_u.threshold << set_thd->panic_set_thd_u.offset;
297cf21f328SLaurent Pinchart 
298cf21f328SLaurent Pinchart 	val &= ~(set_thd->panic_set_thd_v.mask);
299cf21f328SLaurent Pinchart 	val |= set_thd->panic_set_thd_v.threshold << set_thd->panic_set_thd_v.offset;
300cf21f328SLaurent Pinchart 
301cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_OUT_BUF_CTRL, val);
302cf21f328SLaurent Pinchart }
303cf21f328SLaurent Pinchart 
mxc_isi_channel_set_control(struct mxc_isi_pipe * pipe,enum mxc_isi_input_id input,bool bypass)304cf21f328SLaurent Pinchart static void mxc_isi_channel_set_control(struct mxc_isi_pipe *pipe,
305cf21f328SLaurent Pinchart 					enum mxc_isi_input_id input,
306cf21f328SLaurent Pinchart 					bool bypass)
307cf21f328SLaurent Pinchart {
308cf21f328SLaurent Pinchart 	u32 val;
309cf21f328SLaurent Pinchart 
310cf21f328SLaurent Pinchart 	mutex_lock(&pipe->lock);
311cf21f328SLaurent Pinchart 
312cf21f328SLaurent Pinchart 	val = mxc_isi_read(pipe, CHNL_CTRL);
313cf21f328SLaurent Pinchart 	val &= ~(CHNL_CTRL_CHNL_BYPASS | CHNL_CTRL_CHAIN_BUF_MASK |
314cf21f328SLaurent Pinchart 		 CHNL_CTRL_BLANK_PXL_MASK | CHNL_CTRL_SRC_TYPE_MASK |
315cf21f328SLaurent Pinchart 		 CHNL_CTRL_MIPI_VC_ID_MASK | CHNL_CTRL_SRC_INPUT_MASK);
316cf21f328SLaurent Pinchart 
317cf21f328SLaurent Pinchart 	/*
318cf21f328SLaurent Pinchart 	 * If no scaling or color space conversion is needed, bypass the
319cf21f328SLaurent Pinchart 	 * channel.
320cf21f328SLaurent Pinchart 	 */
321cf21f328SLaurent Pinchart 	if (bypass)
322cf21f328SLaurent Pinchart 		val |= CHNL_CTRL_CHNL_BYPASS;
323cf21f328SLaurent Pinchart 
324cf21f328SLaurent Pinchart 	/* Chain line buffers if needed. */
325cf21f328SLaurent Pinchart 	if (pipe->chained)
326cf21f328SLaurent Pinchart 		val |= CHNL_CTRL_CHAIN_BUF(CHNL_CTRL_CHAIN_BUF_2_CHAIN);
327cf21f328SLaurent Pinchart 
328cf21f328SLaurent Pinchart 	val |= CHNL_CTRL_BLANK_PXL(0xff);
329cf21f328SLaurent Pinchart 
330cf21f328SLaurent Pinchart 	/* Input source (including VC configuration for CSI-2) */
331cf21f328SLaurent Pinchart 	if (input == MXC_ISI_INPUT_MEM) {
332cf21f328SLaurent Pinchart 		/*
333cf21f328SLaurent Pinchart 		 * The memory input is connected to the last port of the
334cf21f328SLaurent Pinchart 		 * crossbar switch, after all pixel link inputs. The SRC_INPUT
335cf21f328SLaurent Pinchart 		 * field controls the input selection and must be set
336cf21f328SLaurent Pinchart 		 * accordingly, despite being documented as ignored when using
337cf21f328SLaurent Pinchart 		 * the memory input in the i.MX8MP reference manual, and
338cf21f328SLaurent Pinchart 		 * reserved in the i.MX8MN reference manual.
339cf21f328SLaurent Pinchart 		 */
340cf21f328SLaurent Pinchart 		val |= CHNL_CTRL_SRC_TYPE(CHNL_CTRL_SRC_TYPE_MEMORY);
341cf21f328SLaurent Pinchart 		val |= CHNL_CTRL_SRC_INPUT(pipe->isi->pdata->num_ports);
342cf21f328SLaurent Pinchart 	} else {
343cf21f328SLaurent Pinchart 		val |= CHNL_CTRL_SRC_TYPE(CHNL_CTRL_SRC_TYPE_DEVICE);
344cf21f328SLaurent Pinchart 		val |= CHNL_CTRL_SRC_INPUT(input);
345cf21f328SLaurent Pinchart 		val |= CHNL_CTRL_MIPI_VC_ID(0); /* FIXME: For CSI-2 only */
346cf21f328SLaurent Pinchart 	}
347cf21f328SLaurent Pinchart 
348cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_CTRL, val);
349cf21f328SLaurent Pinchart 
350cf21f328SLaurent Pinchart 	mutex_unlock(&pipe->lock);
351cf21f328SLaurent Pinchart }
352cf21f328SLaurent Pinchart 
mxc_isi_channel_config(struct mxc_isi_pipe * pipe,enum mxc_isi_input_id input,const struct v4l2_area * in_size,const struct v4l2_area * scale,const struct v4l2_rect * crop,enum mxc_isi_encoding in_encoding,enum mxc_isi_encoding out_encoding)353cf21f328SLaurent Pinchart void mxc_isi_channel_config(struct mxc_isi_pipe *pipe,
354cf21f328SLaurent Pinchart 			    enum mxc_isi_input_id input,
355cf21f328SLaurent Pinchart 			    const struct v4l2_area *in_size,
356cf21f328SLaurent Pinchart 			    const struct v4l2_area *scale,
357cf21f328SLaurent Pinchart 			    const struct v4l2_rect *crop,
358cf21f328SLaurent Pinchart 			    enum mxc_isi_encoding in_encoding,
359cf21f328SLaurent Pinchart 			    enum mxc_isi_encoding out_encoding)
360cf21f328SLaurent Pinchart {
361cf21f328SLaurent Pinchart 	bool csc_bypass;
362cf21f328SLaurent Pinchart 	bool scaler_bypass;
363cf21f328SLaurent Pinchart 
364cf21f328SLaurent Pinchart 	/* Input frame size */
365cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_IMG_CFG,
366cf21f328SLaurent Pinchart 		      CHNL_IMG_CFG_HEIGHT(in_size->height) |
367cf21f328SLaurent Pinchart 		      CHNL_IMG_CFG_WIDTH(in_size->width));
368cf21f328SLaurent Pinchart 
369cf21f328SLaurent Pinchart 	/* Scaling */
370cf21f328SLaurent Pinchart 	mxc_isi_channel_set_scaling(pipe, in_encoding, in_size, scale,
371cf21f328SLaurent Pinchart 				    &scaler_bypass);
372cf21f328SLaurent Pinchart 	mxc_isi_channel_set_crop(pipe, scale, crop);
373cf21f328SLaurent Pinchart 
374cf21f328SLaurent Pinchart 	/* CSC */
375cf21f328SLaurent Pinchart 	mxc_isi_channel_set_csc(pipe, in_encoding, out_encoding, &csc_bypass);
376cf21f328SLaurent Pinchart 
377cf21f328SLaurent Pinchart 	/* Output buffer management */
378cf21f328SLaurent Pinchart 	mxc_isi_channel_set_panic_threshold(pipe);
379cf21f328SLaurent Pinchart 
380cf21f328SLaurent Pinchart 	/* Channel control */
381cf21f328SLaurent Pinchart 	mxc_isi_channel_set_control(pipe, input, csc_bypass && scaler_bypass);
382cf21f328SLaurent Pinchart }
383cf21f328SLaurent Pinchart 
mxc_isi_channel_set_input_format(struct mxc_isi_pipe * pipe,const struct mxc_isi_format_info * info,const struct v4l2_pix_format_mplane * format)384cf21f328SLaurent Pinchart void mxc_isi_channel_set_input_format(struct mxc_isi_pipe *pipe,
385cf21f328SLaurent Pinchart 				      const struct mxc_isi_format_info *info,
386cf21f328SLaurent Pinchart 				      const struct v4l2_pix_format_mplane *format)
387cf21f328SLaurent Pinchart {
388cf21f328SLaurent Pinchart 	unsigned int bpl = format->plane_fmt[0].bytesperline;
389cf21f328SLaurent Pinchart 
390cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_MEM_RD_CTRL,
391cf21f328SLaurent Pinchart 		      CHNL_MEM_RD_CTRL_IMG_TYPE(info->isi_in_format));
392cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_IN_BUF_PITCH,
393cf21f328SLaurent Pinchart 		      CHNL_IN_BUF_PITCH_LINE_PITCH(bpl));
394cf21f328SLaurent Pinchart }
395cf21f328SLaurent Pinchart 
mxc_isi_channel_set_output_format(struct mxc_isi_pipe * pipe,const struct mxc_isi_format_info * info,struct v4l2_pix_format_mplane * format)396cf21f328SLaurent Pinchart void mxc_isi_channel_set_output_format(struct mxc_isi_pipe *pipe,
397cf21f328SLaurent Pinchart 				       const struct mxc_isi_format_info *info,
398cf21f328SLaurent Pinchart 				       struct v4l2_pix_format_mplane *format)
399cf21f328SLaurent Pinchart {
400cf21f328SLaurent Pinchart 	u32 val;
401cf21f328SLaurent Pinchart 
402cf21f328SLaurent Pinchart 	/* set outbuf format */
403cf21f328SLaurent Pinchart 	dev_dbg(pipe->isi->dev, "output format %p4cc", &format->pixelformat);
404cf21f328SLaurent Pinchart 
405cf21f328SLaurent Pinchart 	val = mxc_isi_read(pipe, CHNL_IMG_CTRL);
406cf21f328SLaurent Pinchart 	val &= ~CHNL_IMG_CTRL_FORMAT_MASK;
407cf21f328SLaurent Pinchart 	val |= CHNL_IMG_CTRL_FORMAT(info->isi_out_format);
408cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_IMG_CTRL, val);
409cf21f328SLaurent Pinchart 
410cf21f328SLaurent Pinchart 	/* line pitch */
411cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_OUT_BUF_PITCH,
412cf21f328SLaurent Pinchart 		      format->plane_fmt[0].bytesperline);
413cf21f328SLaurent Pinchart }
414cf21f328SLaurent Pinchart 
415cf21f328SLaurent Pinchart /* -----------------------------------------------------------------------------
416cf21f328SLaurent Pinchart  * IRQ
417cf21f328SLaurent Pinchart  */
418cf21f328SLaurent Pinchart 
mxc_isi_channel_irq_status(struct mxc_isi_pipe * pipe,bool clear)419cf21f328SLaurent Pinchart u32 mxc_isi_channel_irq_status(struct mxc_isi_pipe *pipe, bool clear)
420cf21f328SLaurent Pinchart {
421cf21f328SLaurent Pinchart 	u32 status;
422cf21f328SLaurent Pinchart 
423cf21f328SLaurent Pinchart 	status = mxc_isi_read(pipe, CHNL_STS);
424cf21f328SLaurent Pinchart 	if (clear)
425cf21f328SLaurent Pinchart 		mxc_isi_write(pipe, CHNL_STS, status);
426cf21f328SLaurent Pinchart 
427cf21f328SLaurent Pinchart 	return status;
428cf21f328SLaurent Pinchart }
429cf21f328SLaurent Pinchart 
mxc_isi_channel_irq_clear(struct mxc_isi_pipe * pipe)430cf21f328SLaurent Pinchart void mxc_isi_channel_irq_clear(struct mxc_isi_pipe *pipe)
431cf21f328SLaurent Pinchart {
432cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_STS, 0xffffffff);
433cf21f328SLaurent Pinchart }
434cf21f328SLaurent Pinchart 
mxc_isi_channel_irq_enable(struct mxc_isi_pipe * pipe)435cf21f328SLaurent Pinchart static void mxc_isi_channel_irq_enable(struct mxc_isi_pipe *pipe)
436cf21f328SLaurent Pinchart {
437cf21f328SLaurent Pinchart 	const struct mxc_isi_ier_reg *ier_reg = pipe->isi->pdata->ier_reg;
438cf21f328SLaurent Pinchart 	u32 val;
439cf21f328SLaurent Pinchart 
440cf21f328SLaurent Pinchart 	val = CHNL_IER_FRM_RCVD_EN |
441cf21f328SLaurent Pinchart 		CHNL_IER_AXI_WR_ERR_U_EN |
442cf21f328SLaurent Pinchart 		CHNL_IER_AXI_WR_ERR_V_EN |
443cf21f328SLaurent Pinchart 		CHNL_IER_AXI_WR_ERR_Y_EN;
444cf21f328SLaurent Pinchart 
445cf21f328SLaurent Pinchart 	/* Y/U/V overflow enable */
446cf21f328SLaurent Pinchart 	val |= ier_reg->oflw_y_buf_en.mask |
447cf21f328SLaurent Pinchart 	       ier_reg->oflw_u_buf_en.mask |
448cf21f328SLaurent Pinchart 	       ier_reg->oflw_v_buf_en.mask;
449cf21f328SLaurent Pinchart 
450cf21f328SLaurent Pinchart 	/* Y/U/V excess overflow enable */
451cf21f328SLaurent Pinchart 	val |= ier_reg->excs_oflw_y_buf_en.mask |
452cf21f328SLaurent Pinchart 	       ier_reg->excs_oflw_u_buf_en.mask |
453cf21f328SLaurent Pinchart 	       ier_reg->excs_oflw_v_buf_en.mask;
454cf21f328SLaurent Pinchart 
455cf21f328SLaurent Pinchart 	/* Y/U/V panic enable */
456cf21f328SLaurent Pinchart 	val |= ier_reg->panic_y_buf_en.mask |
457cf21f328SLaurent Pinchart 	       ier_reg->panic_u_buf_en.mask |
458cf21f328SLaurent Pinchart 	       ier_reg->panic_v_buf_en.mask;
459cf21f328SLaurent Pinchart 
460cf21f328SLaurent Pinchart 	mxc_isi_channel_irq_clear(pipe);
461cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_IER, val);
462cf21f328SLaurent Pinchart }
463cf21f328SLaurent Pinchart 
mxc_isi_channel_irq_disable(struct mxc_isi_pipe * pipe)464cf21f328SLaurent Pinchart static void mxc_isi_channel_irq_disable(struct mxc_isi_pipe *pipe)
465cf21f328SLaurent Pinchart {
466cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_IER, 0);
467cf21f328SLaurent Pinchart }
468cf21f328SLaurent Pinchart 
469cf21f328SLaurent Pinchart /* -----------------------------------------------------------------------------
470cf21f328SLaurent Pinchart  * Init, deinit, enable, disable
471cf21f328SLaurent Pinchart  */
472cf21f328SLaurent Pinchart 
mxc_isi_channel_sw_reset(struct mxc_isi_pipe * pipe,bool enable_clk)473cf21f328SLaurent Pinchart static void mxc_isi_channel_sw_reset(struct mxc_isi_pipe *pipe, bool enable_clk)
474cf21f328SLaurent Pinchart {
475cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_CTRL, CHNL_CTRL_SW_RST);
476cf21f328SLaurent Pinchart 	mdelay(5);
477cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_CTRL, enable_clk ? CHNL_CTRL_CLK_EN : 0);
478cf21f328SLaurent Pinchart }
479cf21f328SLaurent Pinchart 
__mxc_isi_channel_get(struct mxc_isi_pipe * pipe)480cf21f328SLaurent Pinchart static void __mxc_isi_channel_get(struct mxc_isi_pipe *pipe)
481cf21f328SLaurent Pinchart {
482cf21f328SLaurent Pinchart 	if (!pipe->use_count++)
483cf21f328SLaurent Pinchart 		mxc_isi_channel_sw_reset(pipe, true);
484cf21f328SLaurent Pinchart }
485cf21f328SLaurent Pinchart 
mxc_isi_channel_get(struct mxc_isi_pipe * pipe)486cf21f328SLaurent Pinchart void mxc_isi_channel_get(struct mxc_isi_pipe *pipe)
487cf21f328SLaurent Pinchart {
488cf21f328SLaurent Pinchart 	mutex_lock(&pipe->lock);
489cf21f328SLaurent Pinchart 	__mxc_isi_channel_get(pipe);
490cf21f328SLaurent Pinchart 	mutex_unlock(&pipe->lock);
491cf21f328SLaurent Pinchart }
492cf21f328SLaurent Pinchart 
__mxc_isi_channel_put(struct mxc_isi_pipe * pipe)493cf21f328SLaurent Pinchart static void __mxc_isi_channel_put(struct mxc_isi_pipe *pipe)
494cf21f328SLaurent Pinchart {
495cf21f328SLaurent Pinchart 	if (!--pipe->use_count)
496cf21f328SLaurent Pinchart 		mxc_isi_channel_sw_reset(pipe, false);
497cf21f328SLaurent Pinchart }
498cf21f328SLaurent Pinchart 
mxc_isi_channel_put(struct mxc_isi_pipe * pipe)499cf21f328SLaurent Pinchart void mxc_isi_channel_put(struct mxc_isi_pipe *pipe)
500cf21f328SLaurent Pinchart {
501cf21f328SLaurent Pinchart 	mutex_lock(&pipe->lock);
502cf21f328SLaurent Pinchart 	__mxc_isi_channel_put(pipe);
503cf21f328SLaurent Pinchart 	mutex_unlock(&pipe->lock);
504cf21f328SLaurent Pinchart }
505cf21f328SLaurent Pinchart 
mxc_isi_channel_enable(struct mxc_isi_pipe * pipe)506cf21f328SLaurent Pinchart void mxc_isi_channel_enable(struct mxc_isi_pipe *pipe)
507cf21f328SLaurent Pinchart {
508cf21f328SLaurent Pinchart 	u32 val;
509cf21f328SLaurent Pinchart 
510cf21f328SLaurent Pinchart 	mxc_isi_channel_irq_enable(pipe);
511cf21f328SLaurent Pinchart 
512cf21f328SLaurent Pinchart 	mutex_lock(&pipe->lock);
513cf21f328SLaurent Pinchart 
514cf21f328SLaurent Pinchart 	val = mxc_isi_read(pipe, CHNL_CTRL);
515cf21f328SLaurent Pinchart 	val |= CHNL_CTRL_CHNL_EN;
516cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_CTRL, val);
517cf21f328SLaurent Pinchart 
518cf21f328SLaurent Pinchart 	mutex_unlock(&pipe->lock);
519cf21f328SLaurent Pinchart }
520cf21f328SLaurent Pinchart 
mxc_isi_channel_disable(struct mxc_isi_pipe * pipe)521cf21f328SLaurent Pinchart void mxc_isi_channel_disable(struct mxc_isi_pipe *pipe)
522cf21f328SLaurent Pinchart {
523cf21f328SLaurent Pinchart 	u32 val;
524cf21f328SLaurent Pinchart 
525cf21f328SLaurent Pinchart 	mxc_isi_channel_irq_disable(pipe);
526cf21f328SLaurent Pinchart 
527cf21f328SLaurent Pinchart 	mutex_lock(&pipe->lock);
528cf21f328SLaurent Pinchart 
529cf21f328SLaurent Pinchart 	val = mxc_isi_read(pipe, CHNL_CTRL);
530cf21f328SLaurent Pinchart 	val &= ~CHNL_CTRL_CHNL_EN;
531cf21f328SLaurent Pinchart 	mxc_isi_write(pipe, CHNL_CTRL, val);
532cf21f328SLaurent Pinchart 
533cf21f328SLaurent Pinchart 	mutex_unlock(&pipe->lock);
534cf21f328SLaurent Pinchart }
535cf21f328SLaurent Pinchart 
536cf21f328SLaurent Pinchart /* -----------------------------------------------------------------------------
537cf21f328SLaurent Pinchart  * Resource management & chaining
538cf21f328SLaurent Pinchart  */
mxc_isi_channel_acquire(struct mxc_isi_pipe * pipe,mxc_isi_pipe_irq_t irq_handler,bool bypass)539cf21f328SLaurent Pinchart int mxc_isi_channel_acquire(struct mxc_isi_pipe *pipe,
540cf21f328SLaurent Pinchart 			    mxc_isi_pipe_irq_t irq_handler, bool bypass)
541cf21f328SLaurent Pinchart {
542cf21f328SLaurent Pinchart 	u8 resources;
543cf21f328SLaurent Pinchart 	int ret = 0;
544cf21f328SLaurent Pinchart 
545cf21f328SLaurent Pinchart 	mutex_lock(&pipe->lock);
546cf21f328SLaurent Pinchart 
547cf21f328SLaurent Pinchart 	if (pipe->irq_handler) {
548cf21f328SLaurent Pinchart 		ret = -EBUSY;
549cf21f328SLaurent Pinchart 		goto unlock;
550cf21f328SLaurent Pinchart 	}
551cf21f328SLaurent Pinchart 
552cf21f328SLaurent Pinchart 	/*
553cf21f328SLaurent Pinchart 	 * Make sure the resources we need are available. The output buffer is
554cf21f328SLaurent Pinchart 	 * always needed to operate the channel, the line buffer is needed only
555cf21f328SLaurent Pinchart 	 * when the channel isn't in bypass mode.
556cf21f328SLaurent Pinchart 	 */
557cf21f328SLaurent Pinchart 	resources = MXC_ISI_CHANNEL_RES_OUTPUT_BUF
558cf21f328SLaurent Pinchart 		  | (!bypass ? MXC_ISI_CHANNEL_RES_LINE_BUF : 0);
559cf21f328SLaurent Pinchart 	if ((pipe->available_res & resources) != resources) {
560cf21f328SLaurent Pinchart 		ret = -EBUSY;
561cf21f328SLaurent Pinchart 		goto unlock;
562cf21f328SLaurent Pinchart 	}
563cf21f328SLaurent Pinchart 
564cf21f328SLaurent Pinchart 	/* Acquire the channel resources. */
565cf21f328SLaurent Pinchart 	pipe->acquired_res = resources;
566cf21f328SLaurent Pinchart 	pipe->available_res &= ~resources;
567cf21f328SLaurent Pinchart 	pipe->irq_handler = irq_handler;
568cf21f328SLaurent Pinchart 
569cf21f328SLaurent Pinchart unlock:
570cf21f328SLaurent Pinchart 	mutex_unlock(&pipe->lock);
571cf21f328SLaurent Pinchart 
572cf21f328SLaurent Pinchart 	return ret;
573cf21f328SLaurent Pinchart }
574cf21f328SLaurent Pinchart 
mxc_isi_channel_release(struct mxc_isi_pipe * pipe)575cf21f328SLaurent Pinchart void mxc_isi_channel_release(struct mxc_isi_pipe *pipe)
576cf21f328SLaurent Pinchart {
577cf21f328SLaurent Pinchart 	mutex_lock(&pipe->lock);
578cf21f328SLaurent Pinchart 
579cf21f328SLaurent Pinchart 	pipe->irq_handler = NULL;
580cf21f328SLaurent Pinchart 	pipe->available_res |= pipe->acquired_res;
581cf21f328SLaurent Pinchart 	pipe->acquired_res = 0;
582cf21f328SLaurent Pinchart 
583cf21f328SLaurent Pinchart 	mutex_unlock(&pipe->lock);
584cf21f328SLaurent Pinchart }
585cf21f328SLaurent Pinchart 
586cf21f328SLaurent Pinchart /*
587cf21f328SLaurent Pinchart  * We currently support line buffer chaining only, for handling images with a
588cf21f328SLaurent Pinchart  * width larger than 2048 pixels.
589cf21f328SLaurent Pinchart  *
590cf21f328SLaurent Pinchart  * TODO: Support secondary line buffer for downscaling YUV420 images.
591cf21f328SLaurent Pinchart  */
mxc_isi_channel_chain(struct mxc_isi_pipe * pipe,bool bypass)592cf21f328SLaurent Pinchart int mxc_isi_channel_chain(struct mxc_isi_pipe *pipe, bool bypass)
593cf21f328SLaurent Pinchart {
594cf21f328SLaurent Pinchart 	/* Channel chaining requires both line and output buffer. */
595cf21f328SLaurent Pinchart 	const u8 resources = MXC_ISI_CHANNEL_RES_OUTPUT_BUF
596cf21f328SLaurent Pinchart 			   | MXC_ISI_CHANNEL_RES_LINE_BUF;
597cf21f328SLaurent Pinchart 	struct mxc_isi_pipe *chained_pipe = pipe + 1;
598cf21f328SLaurent Pinchart 	int ret = 0;
599cf21f328SLaurent Pinchart 
600cf21f328SLaurent Pinchart 	/*
601cf21f328SLaurent Pinchart 	 * If buffer chaining is required, make sure this channel is not the
602cf21f328SLaurent Pinchart 	 * last one, otherwise there's no 'next' channel to chain with. This
603cf21f328SLaurent Pinchart 	 * should be prevented by checks in the set format handlers, but let's
604cf21f328SLaurent Pinchart 	 * be defensive.
605cf21f328SLaurent Pinchart 	 */
606cf21f328SLaurent Pinchart 	if (WARN_ON(pipe->id == pipe->isi->pdata->num_channels - 1))
607cf21f328SLaurent Pinchart 		return -EINVAL;
608cf21f328SLaurent Pinchart 
609cf21f328SLaurent Pinchart 	mutex_lock(&chained_pipe->lock);
610cf21f328SLaurent Pinchart 
611cf21f328SLaurent Pinchart 	/* Safety checks. */
612cf21f328SLaurent Pinchart 	if (WARN_ON(pipe->chained || chained_pipe->chained_res)) {
613cf21f328SLaurent Pinchart 		ret = -EINVAL;
614cf21f328SLaurent Pinchart 		goto unlock;
615cf21f328SLaurent Pinchart 	}
616cf21f328SLaurent Pinchart 
617cf21f328SLaurent Pinchart 	if ((chained_pipe->available_res & resources) != resources) {
618cf21f328SLaurent Pinchart 		ret = -EBUSY;
619cf21f328SLaurent Pinchart 		goto unlock;
620cf21f328SLaurent Pinchart 	}
621cf21f328SLaurent Pinchart 
622cf21f328SLaurent Pinchart 	pipe->chained = true;
623cf21f328SLaurent Pinchart 	chained_pipe->chained_res |= resources;
624cf21f328SLaurent Pinchart 	chained_pipe->available_res &= ~resources;
625cf21f328SLaurent Pinchart 
626cf21f328SLaurent Pinchart 	__mxc_isi_channel_get(chained_pipe);
627cf21f328SLaurent Pinchart 
628cf21f328SLaurent Pinchart unlock:
629cf21f328SLaurent Pinchart 	mutex_unlock(&chained_pipe->lock);
630cf21f328SLaurent Pinchart 
631cf21f328SLaurent Pinchart 	return ret;
632cf21f328SLaurent Pinchart }
633cf21f328SLaurent Pinchart 
mxc_isi_channel_unchain(struct mxc_isi_pipe * pipe)634cf21f328SLaurent Pinchart void mxc_isi_channel_unchain(struct mxc_isi_pipe *pipe)
635cf21f328SLaurent Pinchart {
636cf21f328SLaurent Pinchart 	struct mxc_isi_pipe *chained_pipe = pipe + 1;
637cf21f328SLaurent Pinchart 
638cf21f328SLaurent Pinchart 	if (!pipe->chained)
639cf21f328SLaurent Pinchart 		return;
640cf21f328SLaurent Pinchart 
641cf21f328SLaurent Pinchart 	pipe->chained = false;
642cf21f328SLaurent Pinchart 
643cf21f328SLaurent Pinchart 	mutex_lock(&chained_pipe->lock);
644cf21f328SLaurent Pinchart 
645cf21f328SLaurent Pinchart 	chained_pipe->available_res |= chained_pipe->chained_res;
646cf21f328SLaurent Pinchart 	chained_pipe->chained_res = 0;
647cf21f328SLaurent Pinchart 
648cf21f328SLaurent Pinchart 	__mxc_isi_channel_put(chained_pipe);
649cf21f328SLaurent Pinchart 
650cf21f328SLaurent Pinchart 	mutex_unlock(&chained_pipe->lock);
651cf21f328SLaurent Pinchart }
652