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