1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright 2019-2020 NXP
6 #include <linux/delay.h>
7 #include <linux/device.h>
9 #include <linux/types.h>
11 #include "imx8-isi-core.h"
12 #include "imx8-isi-regs.h"
14 #define ISI_DOWNSCALE_THRESHOLD 0x4000
16 static inline u32 mxc_isi_read(struct mxc_isi_pipe *pipe, u32 reg)
18 return readl(pipe->regs + reg);
21 static inline void mxc_isi_write(struct mxc_isi_pipe *pipe, u32 reg, u32 val)
23 writel(val, pipe->regs + reg);
26 /* -----------------------------------------------------------------------------
27 * Buffers & M2M operation
30 void mxc_isi_channel_set_inbuf(struct mxc_isi_pipe *pipe, dma_addr_t dma_addr)
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);
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)
45 val = mxc_isi_read(pipe, CHNL_OUT_BUF_CTRL);
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,
55 mxc_isi_write(pipe, CHNL_U_BUF1_XTND_ADDR,
57 mxc_isi_write(pipe, CHNL_V_BUF1_XTND_ADDR,
61 val ^= CHNL_OUT_BUF_CTRL_LOAD_BUF1_ADDR;
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,
70 mxc_isi_write(pipe, CHNL_U_BUF2_XTND_ADDR,
72 mxc_isi_write(pipe, CHNL_V_BUF2_XTND_ADDR,
76 val ^= CHNL_OUT_BUF_CTRL_LOAD_BUF2_ADDR;
79 mxc_isi_write(pipe, CHNL_OUT_BUF_CTRL, val);
82 void mxc_isi_channel_m2m_start(struct mxc_isi_pipe *pipe)
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);
92 val |= CHNL_MEM_RD_CTRL_READ_MEM;
93 mxc_isi_write(pipe, CHNL_MEM_RD_CTRL, val);
96 /* -----------------------------------------------------------------------------
97 * Pipeline configuration
100 static u32 mxc_isi_channel_scaling_ratio(unsigned int from, unsigned int to,
103 unsigned int ratio = from / to;
114 return min_t(u32, from * 0x1000 / (to * *dec), ISI_DOWNSCALE_THRESHOLD);
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,
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);
131 xscale = mxc_isi_channel_scaling_ratio(in_size->width, out_size->width,
133 yscale = mxc_isi_channel_scaling_ratio(in_size->height, out_size->height,
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);
140 val |= CHNL_IMG_CTRL_DEC_X(ilog2(decx))
141 | CHNL_IMG_CTRL_DEC_Y(ilog2(decy));
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
149 if (encoding == MXC_ISI_ENC_YUV)
150 val |= CHNL_IMG_CTRL_YCBCR_MODE;
152 mxc_isi_write(pipe, CHNL_IMG_CTRL, val);
154 mxc_isi_write(pipe, CHNL_SCALE_FACTOR,
155 CHNL_SCALE_FACTOR_Y_SCALE(yscale) |
156 CHNL_SCALE_FACTOR_X_SCALE(xscale));
158 mxc_isi_write(pipe, CHNL_SCALE_OFFSET, 0);
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));
164 *bypass = in_size->height == out_size->height &&
165 in_size->width == out_size->width;
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)
174 val = mxc_isi_read(pipe, CHNL_IMG_CTRL);
175 val &= ~CHNL_IMG_CTRL_CROP_EN;
177 if (src->height == dst->height && src->width == dst->width) {
178 mxc_isi_write(pipe, CHNL_IMG_CTRL, val);
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);
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);
192 * A2,A1, B1, A3, B3, B2,
193 * C2, C1, D1, C3, D3, D2
195 static const u32 mxc_isi_yuv2rgb_coeffs[6] = {
197 0x0000012a, 0x012a0198, 0x0730079c,
198 0x0204012a, 0x01f00000, 0x01800180
201 static const u32 mxc_isi_rgb2yuv_coeffs[6] = {
203 0x00810041, 0x07db0019, 0x007007b6,
204 0x07a20070, 0x001007ee, 0x00800080
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,
212 static const char * const encodings[] = {
213 [MXC_ISI_ENC_RAW] = "RAW",
214 [MXC_ISI_ENC_RGB] = "RGB",
215 [MXC_ISI_ENC_YUV] = "YUV",
221 val = mxc_isi_read(pipe, CHNL_IMG_CTRL);
222 val &= ~(CHNL_IMG_CTRL_CSC_BYPASS | CHNL_IMG_CTRL_CSC_MODE_MASK);
224 if (in_encoding == MXC_ISI_ENC_YUV &&
225 out_encoding == MXC_ISI_ENC_RGB) {
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) {
233 coeffs = mxc_isi_rgb2yuv_coeffs;
234 val |= CHNL_IMG_CTRL_CSC_MODE(CHNL_IMG_CTRL_CSC_MODE_RGB2YCBCR);
238 val |= CHNL_IMG_CTRL_CSC_BYPASS;
241 dev_dbg(pipe->isi->dev, "CSC: %s -> %s\n",
242 encodings[in_encoding], encodings[out_encoding]);
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]);
253 mxc_isi_write(pipe, CHNL_IMG_CTRL, val);
258 void mxc_isi_channel_set_alpha(struct mxc_isi_pipe *pipe, u8 alpha)
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);
269 void mxc_isi_channel_set_flip(struct mxc_isi_pipe *pipe, bool hflip, bool vflip)
273 val = mxc_isi_read(pipe, CHNL_IMG_CTRL);
274 val &= ~(CHNL_IMG_CTRL_VFLIP_EN | CHNL_IMG_CTRL_HFLIP_EN);
277 val |= CHNL_IMG_CTRL_VFLIP_EN;
279 val |= CHNL_IMG_CTRL_HFLIP_EN;
281 mxc_isi_write(pipe, CHNL_IMG_CTRL, val);
284 static void mxc_isi_channel_set_panic_threshold(struct mxc_isi_pipe *pipe)
286 const struct mxc_isi_set_thd *set_thd = pipe->isi->pdata->set_thd;
289 val = mxc_isi_read(pipe, CHNL_OUT_BUF_CTRL);
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;
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;
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;
300 mxc_isi_write(pipe, CHNL_OUT_BUF_CTRL, val);
303 static void mxc_isi_channel_set_control(struct mxc_isi_pipe *pipe,
304 enum mxc_isi_input_id input,
309 mutex_lock(&pipe->lock);
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);
317 * If no scaling or color space conversion is needed, bypass the
321 val |= CHNL_CTRL_CHNL_BYPASS;
323 /* Chain line buffers if needed. */
325 val |= CHNL_CTRL_CHAIN_BUF(CHNL_CTRL_CHAIN_BUF_2_CHAIN);
327 val |= CHNL_CTRL_BLANK_PXL(0xff);
329 /* Input source (including VC configuration for CSI-2) */
330 if (input == MXC_ISI_INPUT_MEM) {
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.
339 val |= CHNL_CTRL_SRC_TYPE(CHNL_CTRL_SRC_TYPE_MEMORY);
340 val |= CHNL_CTRL_SRC_INPUT(pipe->isi->pdata->num_ports);
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 */
347 mxc_isi_write(pipe, CHNL_CTRL, val);
349 mutex_unlock(&pipe->lock);
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)
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));
369 mxc_isi_channel_set_scaling(pipe, in_encoding, in_size, scale,
371 mxc_isi_channel_set_crop(pipe, scale, crop);
374 mxc_isi_channel_set_csc(pipe, in_encoding, out_encoding, &csc_bypass);
376 /* Output buffer management */
377 mxc_isi_channel_set_panic_threshold(pipe);
379 /* Channel control */
380 mxc_isi_channel_set_control(pipe, input, csc_bypass && scaler_bypass);
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)
387 unsigned int bpl = format->plane_fmt[0].bytesperline;
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));
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)
401 /* set outbuf format */
402 dev_dbg(pipe->isi->dev, "output format %p4cc", &format->pixelformat);
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);
410 mxc_isi_write(pipe, CHNL_OUT_BUF_PITCH,
411 format->plane_fmt[0].bytesperline);
414 /* -----------------------------------------------------------------------------
418 u32 mxc_isi_channel_irq_status(struct mxc_isi_pipe *pipe, bool clear)
422 status = mxc_isi_read(pipe, CHNL_STS);
424 mxc_isi_write(pipe, CHNL_STS, status);
429 void mxc_isi_channel_irq_clear(struct mxc_isi_pipe *pipe)
431 mxc_isi_write(pipe, CHNL_STS, 0xffffffff);
434 static void mxc_isi_channel_irq_enable(struct mxc_isi_pipe *pipe)
436 const struct mxc_isi_ier_reg *ier_reg = pipe->isi->pdata->ier_reg;
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;
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;
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;
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;
459 mxc_isi_channel_irq_clear(pipe);
460 mxc_isi_write(pipe, CHNL_IER, val);
463 static void mxc_isi_channel_irq_disable(struct mxc_isi_pipe *pipe)
465 mxc_isi_write(pipe, CHNL_IER, 0);
468 /* -----------------------------------------------------------------------------
469 * Init, deinit, enable, disable
472 static void mxc_isi_channel_sw_reset(struct mxc_isi_pipe *pipe, bool enable_clk)
474 mxc_isi_write(pipe, CHNL_CTRL, CHNL_CTRL_SW_RST);
476 mxc_isi_write(pipe, CHNL_CTRL, enable_clk ? CHNL_CTRL_CLK_EN : 0);
479 static void __mxc_isi_channel_get(struct mxc_isi_pipe *pipe)
481 if (!pipe->use_count++)
482 mxc_isi_channel_sw_reset(pipe, true);
485 void mxc_isi_channel_get(struct mxc_isi_pipe *pipe)
487 mutex_lock(&pipe->lock);
488 __mxc_isi_channel_get(pipe);
489 mutex_unlock(&pipe->lock);
492 static void __mxc_isi_channel_put(struct mxc_isi_pipe *pipe)
494 if (!--pipe->use_count)
495 mxc_isi_channel_sw_reset(pipe, false);
498 void mxc_isi_channel_put(struct mxc_isi_pipe *pipe)
500 mutex_lock(&pipe->lock);
501 __mxc_isi_channel_put(pipe);
502 mutex_unlock(&pipe->lock);
505 void mxc_isi_channel_enable(struct mxc_isi_pipe *pipe)
509 mxc_isi_channel_irq_enable(pipe);
511 mutex_lock(&pipe->lock);
513 val = mxc_isi_read(pipe, CHNL_CTRL);
514 val |= CHNL_CTRL_CHNL_EN;
515 mxc_isi_write(pipe, CHNL_CTRL, val);
517 mutex_unlock(&pipe->lock);
522 void mxc_isi_channel_disable(struct mxc_isi_pipe *pipe)
526 mxc_isi_channel_irq_disable(pipe);
528 mutex_lock(&pipe->lock);
530 val = mxc_isi_read(pipe, CHNL_CTRL);
531 val &= ~CHNL_CTRL_CHNL_EN;
532 mxc_isi_write(pipe, CHNL_CTRL, val);
534 mutex_unlock(&pipe->lock);
537 /* -----------------------------------------------------------------------------
538 * Resource management & chaining
540 int mxc_isi_channel_acquire(struct mxc_isi_pipe *pipe,
541 mxc_isi_pipe_irq_t irq_handler, bool bypass)
546 mutex_lock(&pipe->lock);
548 if (pipe->irq_handler) {
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.
558 resources = MXC_ISI_CHANNEL_RES_OUTPUT_BUF
559 | (!bypass ? MXC_ISI_CHANNEL_RES_LINE_BUF : 0);
560 if ((pipe->available_res & resources) != resources) {
565 /* Acquire the channel resources. */
566 pipe->acquired_res = resources;
567 pipe->available_res &= ~resources;
568 pipe->irq_handler = irq_handler;
571 mutex_unlock(&pipe->lock);
576 void mxc_isi_channel_release(struct mxc_isi_pipe *pipe)
578 mutex_lock(&pipe->lock);
580 pipe->irq_handler = NULL;
581 pipe->available_res |= pipe->acquired_res;
582 pipe->acquired_res = 0;
584 mutex_unlock(&pipe->lock);
588 * We currently support line buffer chaining only, for handling images with a
589 * width larger than 2048 pixels.
591 * TODO: Support secondary line buffer for downscaling YUV420 images.
593 int mxc_isi_channel_chain(struct mxc_isi_pipe *pipe, bool bypass)
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;
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
607 if (WARN_ON(pipe->id == pipe->isi->pdata->num_channels - 1))
610 mutex_lock(&chained_pipe->lock);
613 if (WARN_ON(pipe->chained || chained_pipe->chained_res)) {
618 if ((chained_pipe->available_res & resources) != resources) {
623 pipe->chained = true;
624 chained_pipe->chained_res |= resources;
625 chained_pipe->available_res &= ~resources;
627 __mxc_isi_channel_get(chained_pipe);
630 mutex_unlock(&chained_pipe->lock);
635 void mxc_isi_channel_unchain(struct mxc_isi_pipe *pipe)
637 struct mxc_isi_pipe *chained_pipe = pipe + 1;
642 pipe->chained = false;
644 mutex_lock(&chained_pipe->lock);
646 chained_pipe->available_res |= chained_pipe->chained_res;
647 chained_pipe->chained_res = 0;
649 __mxc_isi_channel_put(chained_pipe);
651 mutex_unlock(&chained_pipe->lock);