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