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 } 652