]> Git Repo - J-linux.git/blob - drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c
Merge tag 'amd-drm-next-6.5-2023-06-09' of https://gitlab.freedesktop.org/agd5f/linux...
[J-linux.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, 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
38 void 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
83 void 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
101 static 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
118 static 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
169 static 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  */
196 static const u32 mxc_isi_yuv2rgb_coeffs[6] = {
197         /* YUV -> RGB */
198         0x0000012a, 0x012a0198, 0x0730079c,
199         0x0204012a, 0x01f00000, 0x01800180
200 };
201
202 static const u32 mxc_isi_rgb2yuv_coeffs[6] = {
203         /* RGB->YUV */
204         0x00810041, 0x07db0019, 0x007007b6,
205         0x07a20070, 0x001007ee, 0x00800080
206 };
207
208 static 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
259 void 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
270 void 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
285 static 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
304 static 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
353 void 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
384 void 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
396 void 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
419 u32 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
430 void mxc_isi_channel_irq_clear(struct mxc_isi_pipe *pipe)
431 {
432         mxc_isi_write(pipe, CHNL_STS, 0xffffffff);
433 }
434
435 static 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
464 static 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
473 static 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
480 static 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
486 void 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
493 static 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
499 void 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
506 void 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
521 void 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  */
539 int 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
569 unlock:
570         mutex_unlock(&pipe->lock);
571
572         return ret;
573 }
574
575 void 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  */
592 int 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
628 unlock:
629         mutex_unlock(&chained_pipe->lock);
630
631         return ret;
632 }
633
634 void 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 }
This page took 0.068538 seconds and 4 git commands to generate.