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 = NULL; 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 val |= CHNL_IMG_CTRL_CSC_BYPASS; 238 } 239 240 dev_dbg(pipe->isi->dev, "CSC: %s -> %s\n", 241 encodings[in_encoding], encodings[out_encoding]); 242 243 if (coeffs) { 244 mxc_isi_write(pipe, CHNL_CSC_COEFF0, coeffs[0]); 245 mxc_isi_write(pipe, CHNL_CSC_COEFF1, coeffs[1]); 246 mxc_isi_write(pipe, CHNL_CSC_COEFF2, coeffs[2]); 247 mxc_isi_write(pipe, CHNL_CSC_COEFF3, coeffs[3]); 248 mxc_isi_write(pipe, CHNL_CSC_COEFF4, coeffs[4]); 249 mxc_isi_write(pipe, CHNL_CSC_COEFF5, coeffs[5]); 250 } 251 252 mxc_isi_write(pipe, CHNL_IMG_CTRL, val); 253 254 *bypass = !coeffs; 255 } 256 257 void mxc_isi_channel_set_alpha(struct mxc_isi_pipe *pipe, u8 alpha) 258 { 259 u32 val; 260 261 val = mxc_isi_read(pipe, CHNL_IMG_CTRL); 262 val &= ~CHNL_IMG_CTRL_GBL_ALPHA_VAL_MASK; 263 val |= CHNL_IMG_CTRL_GBL_ALPHA_VAL(alpha) | 264 CHNL_IMG_CTRL_GBL_ALPHA_EN; 265 mxc_isi_write(pipe, CHNL_IMG_CTRL, val); 266 } 267 268 void mxc_isi_channel_set_flip(struct mxc_isi_pipe *pipe, bool hflip, bool vflip) 269 { 270 u32 val; 271 272 val = mxc_isi_read(pipe, CHNL_IMG_CTRL); 273 val &= ~(CHNL_IMG_CTRL_VFLIP_EN | CHNL_IMG_CTRL_HFLIP_EN); 274 275 if (vflip) 276 val |= CHNL_IMG_CTRL_VFLIP_EN; 277 if (hflip) 278 val |= CHNL_IMG_CTRL_HFLIP_EN; 279 280 mxc_isi_write(pipe, CHNL_IMG_CTRL, val); 281 } 282 283 static void mxc_isi_channel_set_panic_threshold(struct mxc_isi_pipe *pipe) 284 { 285 const struct mxc_isi_set_thd *set_thd = pipe->isi->pdata->set_thd; 286 u32 val; 287 288 val = mxc_isi_read(pipe, CHNL_OUT_BUF_CTRL); 289 290 val &= ~(set_thd->panic_set_thd_y.mask); 291 val |= set_thd->panic_set_thd_y.threshold << set_thd->panic_set_thd_y.offset; 292 293 val &= ~(set_thd->panic_set_thd_u.mask); 294 val |= set_thd->panic_set_thd_u.threshold << set_thd->panic_set_thd_u.offset; 295 296 val &= ~(set_thd->panic_set_thd_v.mask); 297 val |= set_thd->panic_set_thd_v.threshold << set_thd->panic_set_thd_v.offset; 298 299 mxc_isi_write(pipe, CHNL_OUT_BUF_CTRL, val); 300 } 301 302 static void mxc_isi_channel_set_control(struct mxc_isi_pipe *pipe, 303 enum mxc_isi_input_id input, 304 bool bypass) 305 { 306 u32 val; 307 308 mutex_lock(&pipe->lock); 309 310 val = mxc_isi_read(pipe, CHNL_CTRL); 311 val &= ~(CHNL_CTRL_CHNL_BYPASS | CHNL_CTRL_CHAIN_BUF_MASK | 312 CHNL_CTRL_BLANK_PXL_MASK | CHNL_CTRL_SRC_TYPE_MASK | 313 CHNL_CTRL_MIPI_VC_ID_MASK | CHNL_CTRL_SRC_INPUT_MASK); 314 315 /* 316 * If no scaling or color space conversion is needed, bypass the 317 * channel. 318 */ 319 if (bypass) 320 val |= CHNL_CTRL_CHNL_BYPASS; 321 322 /* Chain line buffers if needed. */ 323 if (pipe->chained) 324 val |= CHNL_CTRL_CHAIN_BUF(CHNL_CTRL_CHAIN_BUF_2_CHAIN); 325 326 val |= CHNL_CTRL_BLANK_PXL(0xff); 327 328 /* Input source (including VC configuration for CSI-2) */ 329 if (input == MXC_ISI_INPUT_MEM) { 330 /* 331 * The memory input is connected to the last port of the 332 * crossbar switch, after all pixel link inputs. The SRC_INPUT 333 * field controls the input selection and must be set 334 * accordingly, despite being documented as ignored when using 335 * the memory input in the i.MX8MP reference manual, and 336 * reserved in the i.MX8MN reference manual. 337 */ 338 val |= CHNL_CTRL_SRC_TYPE(CHNL_CTRL_SRC_TYPE_MEMORY); 339 val |= CHNL_CTRL_SRC_INPUT(pipe->isi->pdata->num_ports); 340 } else { 341 val |= CHNL_CTRL_SRC_TYPE(CHNL_CTRL_SRC_TYPE_DEVICE); 342 val |= CHNL_CTRL_SRC_INPUT(input); 343 val |= CHNL_CTRL_MIPI_VC_ID(0); /* FIXME: For CSI-2 only */ 344 } 345 346 mxc_isi_write(pipe, CHNL_CTRL, val); 347 348 mutex_unlock(&pipe->lock); 349 } 350 351 void mxc_isi_channel_config(struct mxc_isi_pipe *pipe, 352 enum mxc_isi_input_id input, 353 const struct v4l2_area *in_size, 354 const struct v4l2_area *scale, 355 const struct v4l2_rect *crop, 356 enum mxc_isi_encoding in_encoding, 357 enum mxc_isi_encoding out_encoding) 358 { 359 bool csc_bypass; 360 bool scaler_bypass; 361 362 /* Input frame size */ 363 mxc_isi_write(pipe, CHNL_IMG_CFG, 364 CHNL_IMG_CFG_HEIGHT(in_size->height) | 365 CHNL_IMG_CFG_WIDTH(in_size->width)); 366 367 /* Scaling */ 368 mxc_isi_channel_set_scaling(pipe, in_encoding, in_size, scale, 369 &scaler_bypass); 370 mxc_isi_channel_set_crop(pipe, scale, crop); 371 372 /* CSC */ 373 mxc_isi_channel_set_csc(pipe, in_encoding, out_encoding, &csc_bypass); 374 375 /* Output buffer management */ 376 mxc_isi_channel_set_panic_threshold(pipe); 377 378 /* Channel control */ 379 mxc_isi_channel_set_control(pipe, input, csc_bypass && scaler_bypass); 380 } 381 382 void mxc_isi_channel_set_input_format(struct mxc_isi_pipe *pipe, 383 const struct mxc_isi_format_info *info, 384 const struct v4l2_pix_format_mplane *format) 385 { 386 unsigned int bpl = format->plane_fmt[0].bytesperline; 387 388 mxc_isi_write(pipe, CHNL_MEM_RD_CTRL, 389 CHNL_MEM_RD_CTRL_IMG_TYPE(info->isi_in_format)); 390 mxc_isi_write(pipe, CHNL_IN_BUF_PITCH, 391 CHNL_IN_BUF_PITCH_LINE_PITCH(bpl)); 392 } 393 394 void mxc_isi_channel_set_output_format(struct mxc_isi_pipe *pipe, 395 const struct mxc_isi_format_info *info, 396 struct v4l2_pix_format_mplane *format) 397 { 398 u32 val; 399 400 /* set outbuf format */ 401 dev_dbg(pipe->isi->dev, "output format %p4cc", &format->pixelformat); 402 403 val = mxc_isi_read(pipe, CHNL_IMG_CTRL); 404 val &= ~CHNL_IMG_CTRL_FORMAT_MASK; 405 val |= CHNL_IMG_CTRL_FORMAT(info->isi_out_format); 406 mxc_isi_write(pipe, CHNL_IMG_CTRL, val); 407 408 /* line pitch */ 409 mxc_isi_write(pipe, CHNL_OUT_BUF_PITCH, 410 format->plane_fmt[0].bytesperline); 411 } 412 413 /* ----------------------------------------------------------------------------- 414 * IRQ 415 */ 416 417 u32 mxc_isi_channel_irq_status(struct mxc_isi_pipe *pipe, bool clear) 418 { 419 u32 status; 420 421 status = mxc_isi_read(pipe, CHNL_STS); 422 if (clear) 423 mxc_isi_write(pipe, CHNL_STS, status); 424 425 return status; 426 } 427 428 void mxc_isi_channel_irq_clear(struct mxc_isi_pipe *pipe) 429 { 430 mxc_isi_write(pipe, CHNL_STS, 0xffffffff); 431 } 432 433 static void mxc_isi_channel_irq_enable(struct mxc_isi_pipe *pipe) 434 { 435 const struct mxc_isi_ier_reg *ier_reg = pipe->isi->pdata->ier_reg; 436 u32 val; 437 438 val = CHNL_IER_FRM_RCVD_EN | 439 CHNL_IER_AXI_WR_ERR_U_EN | 440 CHNL_IER_AXI_WR_ERR_V_EN | 441 CHNL_IER_AXI_WR_ERR_Y_EN; 442 443 /* Y/U/V overflow enable */ 444 val |= ier_reg->oflw_y_buf_en.mask | 445 ier_reg->oflw_u_buf_en.mask | 446 ier_reg->oflw_v_buf_en.mask; 447 448 /* Y/U/V excess overflow enable */ 449 val |= ier_reg->excs_oflw_y_buf_en.mask | 450 ier_reg->excs_oflw_u_buf_en.mask | 451 ier_reg->excs_oflw_v_buf_en.mask; 452 453 /* Y/U/V panic enable */ 454 val |= ier_reg->panic_y_buf_en.mask | 455 ier_reg->panic_u_buf_en.mask | 456 ier_reg->panic_v_buf_en.mask; 457 458 mxc_isi_channel_irq_clear(pipe); 459 mxc_isi_write(pipe, CHNL_IER, val); 460 } 461 462 static void mxc_isi_channel_irq_disable(struct mxc_isi_pipe *pipe) 463 { 464 mxc_isi_write(pipe, CHNL_IER, 0); 465 } 466 467 /* ----------------------------------------------------------------------------- 468 * Init, deinit, enable, disable 469 */ 470 471 static void mxc_isi_channel_sw_reset(struct mxc_isi_pipe *pipe, bool enable_clk) 472 { 473 mxc_isi_write(pipe, CHNL_CTRL, CHNL_CTRL_SW_RST); 474 mdelay(5); 475 mxc_isi_write(pipe, CHNL_CTRL, enable_clk ? CHNL_CTRL_CLK_EN : 0); 476 } 477 478 static void __mxc_isi_channel_get(struct mxc_isi_pipe *pipe) 479 { 480 if (!pipe->use_count++) 481 mxc_isi_channel_sw_reset(pipe, true); 482 } 483 484 void mxc_isi_channel_get(struct mxc_isi_pipe *pipe) 485 { 486 mutex_lock(&pipe->lock); 487 __mxc_isi_channel_get(pipe); 488 mutex_unlock(&pipe->lock); 489 } 490 491 static void __mxc_isi_channel_put(struct mxc_isi_pipe *pipe) 492 { 493 if (!--pipe->use_count) 494 mxc_isi_channel_sw_reset(pipe, false); 495 } 496 497 void mxc_isi_channel_put(struct mxc_isi_pipe *pipe) 498 { 499 mutex_lock(&pipe->lock); 500 __mxc_isi_channel_put(pipe); 501 mutex_unlock(&pipe->lock); 502 } 503 504 void mxc_isi_channel_enable(struct mxc_isi_pipe *pipe) 505 { 506 u32 val; 507 508 mxc_isi_channel_irq_enable(pipe); 509 510 mutex_lock(&pipe->lock); 511 512 val = mxc_isi_read(pipe, CHNL_CTRL); 513 val |= CHNL_CTRL_CHNL_EN; 514 mxc_isi_write(pipe, CHNL_CTRL, val); 515 516 mutex_unlock(&pipe->lock); 517 } 518 519 void mxc_isi_channel_disable(struct mxc_isi_pipe *pipe) 520 { 521 u32 val; 522 523 mxc_isi_channel_irq_disable(pipe); 524 525 mutex_lock(&pipe->lock); 526 527 val = mxc_isi_read(pipe, CHNL_CTRL); 528 val &= ~CHNL_CTRL_CHNL_EN; 529 mxc_isi_write(pipe, CHNL_CTRL, val); 530 531 mutex_unlock(&pipe->lock); 532 } 533 534 /* ----------------------------------------------------------------------------- 535 * Resource management & chaining 536 */ 537 int mxc_isi_channel_acquire(struct mxc_isi_pipe *pipe, 538 mxc_isi_pipe_irq_t irq_handler, bool bypass) 539 { 540 u8 resources; 541 int ret = 0; 542 543 mutex_lock(&pipe->lock); 544 545 if (pipe->irq_handler) { 546 ret = -EBUSY; 547 goto unlock; 548 } 549 550 /* 551 * Make sure the resources we need are available. The output buffer is 552 * always needed to operate the channel, the line buffer is needed only 553 * when the channel isn't in bypass mode. 554 */ 555 resources = MXC_ISI_CHANNEL_RES_OUTPUT_BUF 556 | (!bypass ? MXC_ISI_CHANNEL_RES_LINE_BUF : 0); 557 if ((pipe->available_res & resources) != resources) { 558 ret = -EBUSY; 559 goto unlock; 560 } 561 562 /* Acquire the channel resources. */ 563 pipe->acquired_res = resources; 564 pipe->available_res &= ~resources; 565 pipe->irq_handler = irq_handler; 566 567 unlock: 568 mutex_unlock(&pipe->lock); 569 570 return ret; 571 } 572 573 void mxc_isi_channel_release(struct mxc_isi_pipe *pipe) 574 { 575 mutex_lock(&pipe->lock); 576 577 pipe->irq_handler = NULL; 578 pipe->available_res |= pipe->acquired_res; 579 pipe->acquired_res = 0; 580 581 mutex_unlock(&pipe->lock); 582 } 583 584 /* 585 * We currently support line buffer chaining only, for handling images with a 586 * width larger than 2048 pixels. 587 * 588 * TODO: Support secondary line buffer for downscaling YUV420 images. 589 */ 590 int mxc_isi_channel_chain(struct mxc_isi_pipe *pipe, bool bypass) 591 { 592 /* Channel chaining requires both line and output buffer. */ 593 const u8 resources = MXC_ISI_CHANNEL_RES_OUTPUT_BUF 594 | MXC_ISI_CHANNEL_RES_LINE_BUF; 595 struct mxc_isi_pipe *chained_pipe = pipe + 1; 596 int ret = 0; 597 598 /* 599 * If buffer chaining is required, make sure this channel is not the 600 * last one, otherwise there's no 'next' channel to chain with. This 601 * should be prevented by checks in the set format handlers, but let's 602 * be defensive. 603 */ 604 if (WARN_ON(pipe->id == pipe->isi->pdata->num_channels - 1)) 605 return -EINVAL; 606 607 mutex_lock(&chained_pipe->lock); 608 609 /* Safety checks. */ 610 if (WARN_ON(pipe->chained || chained_pipe->chained_res)) { 611 ret = -EINVAL; 612 goto unlock; 613 } 614 615 if ((chained_pipe->available_res & resources) != resources) { 616 ret = -EBUSY; 617 goto unlock; 618 } 619 620 pipe->chained = true; 621 chained_pipe->chained_res |= resources; 622 chained_pipe->available_res &= ~resources; 623 624 __mxc_isi_channel_get(chained_pipe); 625 626 unlock: 627 mutex_unlock(&chained_pipe->lock); 628 629 return ret; 630 } 631 632 void mxc_isi_channel_unchain(struct mxc_isi_pipe *pipe) 633 { 634 struct mxc_isi_pipe *chained_pipe = pipe + 1; 635 636 if (!pipe->chained) 637 return; 638 639 pipe->chained = false; 640 641 mutex_lock(&chained_pipe->lock); 642 643 chained_pipe->available_res |= chained_pipe->chained_res; 644 chained_pipe->chained_res = 0; 645 646 __mxc_isi_channel_put(chained_pipe); 647 648 mutex_unlock(&chained_pipe->lock); 649 } 650