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
mxc_isi_read(struct mxc_isi_pipe * pipe,u32 reg)16 static inline u32 mxc_isi_read(struct mxc_isi_pipe *pipe, u32 reg)
17 {
18 return readl(pipe->regs + reg);
19 }
20
mxc_isi_write(struct mxc_isi_pipe * pipe,u32 reg,u32 val)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
mxc_isi_channel_set_inbuf(struct mxc_isi_pipe * pipe,dma_addr_t dma_addr)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
mxc_isi_channel_set_outbuf(struct mxc_isi_pipe * pipe,const dma_addr_t dma_addrs[3],enum mxc_isi_buf_id buf_id)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
mxc_isi_channel_m2m_start(struct mxc_isi_pipe * pipe)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
mxc_isi_channel_scaling_ratio(unsigned int from,unsigned int to,u32 * dec)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
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)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
mxc_isi_channel_set_crop(struct mxc_isi_pipe * pipe,const struct v4l2_area * src,const struct v4l2_rect * dst)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
mxc_isi_channel_set_csc(struct mxc_isi_pipe * pipe,enum mxc_isi_encoding in_encoding,enum mxc_isi_encoding out_encoding,bool * bypass)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
mxc_isi_channel_set_alpha(struct mxc_isi_pipe * pipe,u8 alpha)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
mxc_isi_channel_set_flip(struct mxc_isi_pipe * pipe,bool hflip,bool vflip)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
mxc_isi_channel_set_panic_threshold(struct mxc_isi_pipe * pipe)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
mxc_isi_channel_set_control(struct mxc_isi_pipe * pipe,enum mxc_isi_input_id input,bool bypass)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
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)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
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)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
mxc_isi_channel_set_output_format(struct mxc_isi_pipe * pipe,const struct mxc_isi_format_info * info,struct v4l2_pix_format_mplane * format)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
mxc_isi_channel_irq_status(struct mxc_isi_pipe * pipe,bool clear)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
mxc_isi_channel_irq_clear(struct mxc_isi_pipe * pipe)430 void mxc_isi_channel_irq_clear(struct mxc_isi_pipe *pipe)
431 {
432 mxc_isi_write(pipe, CHNL_STS, 0xffffffff);
433 }
434
mxc_isi_channel_irq_enable(struct mxc_isi_pipe * pipe)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
mxc_isi_channel_irq_disable(struct mxc_isi_pipe * pipe)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
mxc_isi_channel_sw_reset(struct mxc_isi_pipe * pipe,bool enable_clk)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
__mxc_isi_channel_get(struct mxc_isi_pipe * pipe)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
mxc_isi_channel_get(struct mxc_isi_pipe * pipe)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
__mxc_isi_channel_put(struct mxc_isi_pipe * pipe)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
mxc_isi_channel_put(struct mxc_isi_pipe * pipe)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
mxc_isi_channel_enable(struct mxc_isi_pipe * pipe)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
mxc_isi_channel_disable(struct mxc_isi_pipe * pipe)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 */
mxc_isi_channel_acquire(struct mxc_isi_pipe * pipe,mxc_isi_pipe_irq_t irq_handler,bool bypass)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
mxc_isi_channel_release(struct mxc_isi_pipe * pipe)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 */
mxc_isi_channel_chain(struct mxc_isi_pipe * pipe,bool bypass)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
mxc_isi_channel_unchain(struct mxc_isi_pipe * pipe)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 }
652