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