be12d0d2f42f7d54ce5f267277abcc9be9da3179
[platform/kernel/linux-rpi.git] / drivers / media / platform / nxp / imx8-isi / imx8-isi-hw.c
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         msleep(300);
520 }
521
522 void mxc_isi_channel_disable(struct mxc_isi_pipe *pipe)
523 {
524         u32 val;
525
526         mxc_isi_channel_irq_disable(pipe);
527
528         mutex_lock(&pipe->lock);
529
530         val = mxc_isi_read(pipe, CHNL_CTRL);
531         val &= ~CHNL_CTRL_CHNL_EN;
532         mxc_isi_write(pipe, CHNL_CTRL, val);
533
534         mutex_unlock(&pipe->lock);
535 }
536
537 /* -----------------------------------------------------------------------------
538  * Resource management & chaining
539  */
540 int mxc_isi_channel_acquire(struct mxc_isi_pipe *pipe,
541                             mxc_isi_pipe_irq_t irq_handler, bool bypass)
542 {
543         u8 resources;
544         int ret = 0;
545
546         mutex_lock(&pipe->lock);
547
548         if (pipe->irq_handler) {
549                 ret = -EBUSY;
550                 goto unlock;
551         }
552
553         /*
554          * Make sure the resources we need are available. The output buffer is
555          * always needed to operate the channel, the line buffer is needed only
556          * when the channel isn't in bypass mode.
557          */
558         resources = MXC_ISI_CHANNEL_RES_OUTPUT_BUF
559                   | (!bypass ? MXC_ISI_CHANNEL_RES_LINE_BUF : 0);
560         if ((pipe->available_res & resources) != resources) {
561                 ret = -EBUSY;
562                 goto unlock;
563         }
564
565         /* Acquire the channel resources. */
566         pipe->acquired_res = resources;
567         pipe->available_res &= ~resources;
568         pipe->irq_handler = irq_handler;
569
570 unlock:
571         mutex_unlock(&pipe->lock);
572
573         return ret;
574 }
575
576 void mxc_isi_channel_release(struct mxc_isi_pipe *pipe)
577 {
578         mutex_lock(&pipe->lock);
579
580         pipe->irq_handler = NULL;
581         pipe->available_res |= pipe->acquired_res;
582         pipe->acquired_res = 0;
583
584         mutex_unlock(&pipe->lock);
585 }
586
587 /*
588  * We currently support line buffer chaining only, for handling images with a
589  * width larger than 2048 pixels.
590  *
591  * TODO: Support secondary line buffer for downscaling YUV420 images.
592  */
593 int mxc_isi_channel_chain(struct mxc_isi_pipe *pipe, bool bypass)
594 {
595         /* Channel chaining requires both line and output buffer. */
596         const u8 resources = MXC_ISI_CHANNEL_RES_OUTPUT_BUF
597                            | MXC_ISI_CHANNEL_RES_LINE_BUF;
598         struct mxc_isi_pipe *chained_pipe = pipe + 1;
599         int ret = 0;
600
601         /*
602          * If buffer chaining is required, make sure this channel is not the
603          * last one, otherwise there's no 'next' channel to chain with. This
604          * should be prevented by checks in the set format handlers, but let's
605          * be defensive.
606          */
607         if (WARN_ON(pipe->id == pipe->isi->pdata->num_channels - 1))
608                 return -EINVAL;
609
610         mutex_lock(&chained_pipe->lock);
611
612         /* Safety checks. */
613         if (WARN_ON(pipe->chained || chained_pipe->chained_res)) {
614                 ret = -EINVAL;
615                 goto unlock;
616         }
617
618         if ((chained_pipe->available_res & resources) != resources) {
619                 ret = -EBUSY;
620                 goto unlock;
621         }
622
623         pipe->chained = true;
624         chained_pipe->chained_res |= resources;
625         chained_pipe->available_res &= ~resources;
626
627         __mxc_isi_channel_get(chained_pipe);
628
629 unlock:
630         mutex_unlock(&chained_pipe->lock);
631
632         return ret;
633 }
634
635 void mxc_isi_channel_unchain(struct mxc_isi_pipe *pipe)
636 {
637         struct mxc_isi_pipe *chained_pipe = pipe + 1;
638
639         if (!pipe->chained)
640                 return;
641
642         pipe->chained = false;
643
644         mutex_lock(&chained_pipe->lock);
645
646         chained_pipe->available_res |= chained_pipe->chained_res;
647         chained_pipe->chained_res = 0;
648
649         __mxc_isi_channel_put(chained_pipe);
650
651         mutex_unlock(&chained_pipe->lock);
652 }