1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * V4L2 Capture ISI subdev driver for i.MX8QXP/QM platform 4 * 5 * ISI is a Image Sensor Interface of i.MX8QXP/QM platform, which 6 * used to process image from camera sensor to memory or DC 7 * 8 * Copyright (c) 2019 NXP Semiconductor 9 */ 10 11 #include <linux/device.h> 12 #include <linux/errno.h> 13 #include <linux/interrupt.h> 14 #include <linux/kernel.h> 15 #include <linux/minmax.h> 16 #include <linux/mutex.h> 17 #include <linux/of.h> 18 #include <linux/platform_device.h> 19 #include <linux/types.h> 20 #include <linux/videodev2.h> 21 22 #include <media/media-entity.h> 23 #include <media/v4l2-subdev.h> 24 #include <media/videobuf2-v4l2.h> 25 26 #include "imx8-isi-core.h" 27 #include "imx8-isi-regs.h" 28 29 /* 30 * While the ISI receives data from the gasket on a 3x12-bit bus, the pipeline 31 * subdev conceptually includes the gasket in order to avoid exposing an extra 32 * subdev between the CSIS and the ISI. We thus need to expose media bus codes 33 * corresponding to the CSIS output, which is narrower. 34 */ 35 static const struct mxc_isi_bus_format_info mxc_isi_bus_formats[] = { 36 /* YUV formats */ 37 { 38 .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, 39 .output = MEDIA_BUS_FMT_YUV8_1X24, 40 .pads = BIT(MXC_ISI_PIPE_PAD_SINK), 41 .encoding = MXC_ISI_ENC_YUV, 42 }, { 43 .mbus_code = MEDIA_BUS_FMT_YUV8_1X24, 44 .output = MEDIA_BUS_FMT_YUV8_1X24, 45 .pads = BIT(MXC_ISI_PIPE_PAD_SOURCE), 46 .encoding = MXC_ISI_ENC_YUV, 47 }, 48 /* RGB formats */ 49 { 50 .mbus_code = MEDIA_BUS_FMT_RGB565_1X16, 51 .output = MEDIA_BUS_FMT_RGB888_1X24, 52 .pads = BIT(MXC_ISI_PIPE_PAD_SINK), 53 .encoding = MXC_ISI_ENC_RGB, 54 }, { 55 .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, 56 .output = MEDIA_BUS_FMT_RGB888_1X24, 57 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 58 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 59 .encoding = MXC_ISI_ENC_RGB, 60 }, 61 /* RAW formats */ 62 { 63 .mbus_code = MEDIA_BUS_FMT_Y8_1X8, 64 .output = MEDIA_BUS_FMT_Y8_1X8, 65 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 66 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 67 .encoding = MXC_ISI_ENC_RAW, 68 }, { 69 .mbus_code = MEDIA_BUS_FMT_Y10_1X10, 70 .output = MEDIA_BUS_FMT_Y10_1X10, 71 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 72 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 73 .encoding = MXC_ISI_ENC_RAW, 74 }, { 75 .mbus_code = MEDIA_BUS_FMT_Y12_1X12, 76 .output = MEDIA_BUS_FMT_Y12_1X12, 77 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 78 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 79 .encoding = MXC_ISI_ENC_RAW, 80 }, { 81 .mbus_code = MEDIA_BUS_FMT_Y14_1X14, 82 .output = MEDIA_BUS_FMT_Y14_1X14, 83 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 84 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 85 .encoding = MXC_ISI_ENC_RAW, 86 }, { 87 .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, 88 .output = MEDIA_BUS_FMT_SBGGR8_1X8, 89 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 90 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 91 .encoding = MXC_ISI_ENC_RAW, 92 }, { 93 .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, 94 .output = MEDIA_BUS_FMT_SGBRG8_1X8, 95 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 96 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 97 .encoding = MXC_ISI_ENC_RAW, 98 }, { 99 .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, 100 .output = MEDIA_BUS_FMT_SGRBG8_1X8, 101 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 102 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 103 .encoding = MXC_ISI_ENC_RAW, 104 }, { 105 .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, 106 .output = MEDIA_BUS_FMT_SRGGB8_1X8, 107 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 108 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 109 .encoding = MXC_ISI_ENC_RAW, 110 }, { 111 .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, 112 .output = MEDIA_BUS_FMT_SBGGR10_1X10, 113 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 114 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 115 .encoding = MXC_ISI_ENC_RAW, 116 }, { 117 .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, 118 .output = MEDIA_BUS_FMT_SGBRG10_1X10, 119 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 120 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 121 .encoding = MXC_ISI_ENC_RAW, 122 }, { 123 .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, 124 .output = MEDIA_BUS_FMT_SGRBG10_1X10, 125 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 126 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 127 .encoding = MXC_ISI_ENC_RAW, 128 }, { 129 .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, 130 .output = MEDIA_BUS_FMT_SRGGB10_1X10, 131 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 132 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 133 .encoding = MXC_ISI_ENC_RAW, 134 }, { 135 .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, 136 .output = MEDIA_BUS_FMT_SBGGR12_1X12, 137 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 138 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 139 .encoding = MXC_ISI_ENC_RAW, 140 }, { 141 .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, 142 .output = MEDIA_BUS_FMT_SGBRG12_1X12, 143 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 144 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 145 .encoding = MXC_ISI_ENC_RAW, 146 }, { 147 .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, 148 .output = MEDIA_BUS_FMT_SGRBG12_1X12, 149 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 150 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 151 .encoding = MXC_ISI_ENC_RAW, 152 }, { 153 .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, 154 .output = MEDIA_BUS_FMT_SRGGB12_1X12, 155 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 156 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 157 .encoding = MXC_ISI_ENC_RAW, 158 }, { 159 .mbus_code = MEDIA_BUS_FMT_SBGGR14_1X14, 160 .output = MEDIA_BUS_FMT_SBGGR14_1X14, 161 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 162 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 163 .encoding = MXC_ISI_ENC_RAW, 164 }, { 165 .mbus_code = MEDIA_BUS_FMT_SGBRG14_1X14, 166 .output = MEDIA_BUS_FMT_SGBRG14_1X14, 167 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 168 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 169 .encoding = MXC_ISI_ENC_RAW, 170 }, { 171 .mbus_code = MEDIA_BUS_FMT_SGRBG14_1X14, 172 .output = MEDIA_BUS_FMT_SGRBG14_1X14, 173 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 174 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 175 .encoding = MXC_ISI_ENC_RAW, 176 }, { 177 .mbus_code = MEDIA_BUS_FMT_SRGGB14_1X14, 178 .output = MEDIA_BUS_FMT_SRGGB14_1X14, 179 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 180 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 181 .encoding = MXC_ISI_ENC_RAW, 182 }, 183 /* JPEG */ 184 { 185 .mbus_code = MEDIA_BUS_FMT_JPEG_1X8, 186 .output = MEDIA_BUS_FMT_JPEG_1X8, 187 .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 188 | BIT(MXC_ISI_PIPE_PAD_SOURCE), 189 .encoding = MXC_ISI_ENC_RAW, 190 } 191 }; 192 193 const struct mxc_isi_bus_format_info * 194 mxc_isi_bus_format_by_code(u32 code, unsigned int pad) 195 { 196 unsigned int i; 197 198 for (i = 0; i < ARRAY_SIZE(mxc_isi_bus_formats); i++) { 199 const struct mxc_isi_bus_format_info *info = 200 &mxc_isi_bus_formats[i]; 201 202 if (info->mbus_code == code && info->pads & BIT(pad)) 203 return info; 204 } 205 206 return NULL; 207 } 208 209 const struct mxc_isi_bus_format_info * 210 mxc_isi_bus_format_by_index(unsigned int index, unsigned int pad) 211 { 212 unsigned int i; 213 214 for (i = 0; i < ARRAY_SIZE(mxc_isi_bus_formats); i++) { 215 const struct mxc_isi_bus_format_info *info = 216 &mxc_isi_bus_formats[i]; 217 218 if (!(info->pads & BIT(pad))) 219 continue; 220 221 if (!index) 222 return info; 223 224 index--; 225 } 226 227 return NULL; 228 } 229 230 static inline struct mxc_isi_pipe *to_isi_pipe(struct v4l2_subdev *sd) 231 { 232 return container_of(sd, struct mxc_isi_pipe, sd); 233 } 234 235 int mxc_isi_pipe_enable(struct mxc_isi_pipe *pipe) 236 { 237 struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar; 238 const struct mxc_isi_bus_format_info *sink_info; 239 const struct mxc_isi_bus_format_info *src_info; 240 const struct v4l2_mbus_framefmt *sink_fmt; 241 const struct v4l2_mbus_framefmt *src_fmt; 242 const struct v4l2_rect *compose; 243 struct v4l2_subdev_state *state; 244 struct v4l2_subdev *sd = &pipe->sd; 245 struct v4l2_area in_size, scale; 246 struct v4l2_rect crop; 247 u32 input; 248 int ret; 249 250 /* 251 * Find the connected input by inspecting the crossbar switch routing 252 * table. 253 */ 254 state = v4l2_subdev_lock_and_get_active_state(&xbar->sd); 255 ret = v4l2_subdev_routing_find_opposite_end(&state->routing, 256 xbar->num_sinks + pipe->id, 257 0, &input, NULL); 258 v4l2_subdev_unlock_state(state); 259 260 if (ret) 261 return -EPIPE; 262 263 /* Configure the pipeline. */ 264 state = v4l2_subdev_lock_and_get_active_state(sd); 265 266 sink_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SINK); 267 src_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SOURCE); 268 compose = v4l2_subdev_get_try_compose(sd, state, MXC_ISI_PIPE_PAD_SINK); 269 crop = *v4l2_subdev_get_try_crop(sd, state, MXC_ISI_PIPE_PAD_SOURCE); 270 271 sink_info = mxc_isi_bus_format_by_code(sink_fmt->code, 272 MXC_ISI_PIPE_PAD_SINK); 273 src_info = mxc_isi_bus_format_by_code(src_fmt->code, 274 MXC_ISI_PIPE_PAD_SOURCE); 275 276 in_size.width = sink_fmt->width; 277 in_size.height = sink_fmt->height; 278 scale.width = compose->width; 279 scale.height = compose->height; 280 281 v4l2_subdev_unlock_state(state); 282 283 /* Configure the ISI channel. */ 284 mxc_isi_channel_config(pipe, input, &in_size, &scale, &crop, 285 sink_info->encoding, src_info->encoding); 286 287 mxc_isi_channel_enable(pipe); 288 289 /* Enable streams on the crossbar switch. */ 290 ret = v4l2_subdev_enable_streams(&xbar->sd, xbar->num_sinks + pipe->id, 291 BIT(0)); 292 if (ret) { 293 mxc_isi_channel_disable(pipe); 294 dev_err(pipe->isi->dev, "Failed to enable pipe %u\n", 295 pipe->id); 296 return ret; 297 } 298 299 return 0; 300 } 301 302 void mxc_isi_pipe_disable(struct mxc_isi_pipe *pipe) 303 { 304 struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar; 305 int ret; 306 307 ret = v4l2_subdev_disable_streams(&xbar->sd, xbar->num_sinks + pipe->id, 308 BIT(0)); 309 if (ret) 310 dev_err(pipe->isi->dev, "Failed to disable pipe %u\n", 311 pipe->id); 312 313 mxc_isi_channel_disable(pipe); 314 } 315 316 /* ----------------------------------------------------------------------------- 317 * V4L2 subdev operations 318 */ 319 320 static struct v4l2_mbus_framefmt * 321 mxc_isi_pipe_get_pad_format(struct mxc_isi_pipe *pipe, 322 struct v4l2_subdev_state *state, 323 unsigned int pad) 324 { 325 return v4l2_subdev_get_try_format(&pipe->sd, state, pad); 326 } 327 328 static struct v4l2_rect * 329 mxc_isi_pipe_get_pad_crop(struct mxc_isi_pipe *pipe, 330 struct v4l2_subdev_state *state, 331 unsigned int pad) 332 { 333 return v4l2_subdev_get_try_crop(&pipe->sd, state, pad); 334 } 335 336 static struct v4l2_rect * 337 mxc_isi_pipe_get_pad_compose(struct mxc_isi_pipe *pipe, 338 struct v4l2_subdev_state *state, 339 unsigned int pad) 340 { 341 return v4l2_subdev_get_try_compose(&pipe->sd, state, pad); 342 } 343 344 static int mxc_isi_pipe_init_cfg(struct v4l2_subdev *sd, 345 struct v4l2_subdev_state *state) 346 { 347 struct mxc_isi_pipe *pipe = to_isi_pipe(sd); 348 struct v4l2_mbus_framefmt *fmt_source; 349 struct v4l2_mbus_framefmt *fmt_sink; 350 struct v4l2_rect *compose; 351 struct v4l2_rect *crop; 352 353 fmt_sink = mxc_isi_pipe_get_pad_format(pipe, state, 354 MXC_ISI_PIPE_PAD_SINK); 355 fmt_source = mxc_isi_pipe_get_pad_format(pipe, state, 356 MXC_ISI_PIPE_PAD_SOURCE); 357 358 fmt_sink->width = MXC_ISI_DEF_WIDTH; 359 fmt_sink->height = MXC_ISI_DEF_HEIGHT; 360 fmt_sink->code = MXC_ISI_DEF_MBUS_CODE_SINK; 361 fmt_sink->field = V4L2_FIELD_NONE; 362 fmt_sink->colorspace = V4L2_COLORSPACE_JPEG; 363 fmt_sink->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt_sink->colorspace); 364 fmt_sink->quantization = 365 V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt_sink->colorspace, 366 fmt_sink->ycbcr_enc); 367 fmt_sink->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt_sink->colorspace); 368 369 *fmt_source = *fmt_sink; 370 fmt_source->code = MXC_ISI_DEF_MBUS_CODE_SOURCE; 371 372 compose = mxc_isi_pipe_get_pad_compose(pipe, state, 373 MXC_ISI_PIPE_PAD_SINK); 374 crop = mxc_isi_pipe_get_pad_crop(pipe, state, MXC_ISI_PIPE_PAD_SOURCE); 375 376 compose->left = 0; 377 compose->top = 0; 378 compose->width = MXC_ISI_DEF_WIDTH; 379 compose->height = MXC_ISI_DEF_HEIGHT; 380 381 *crop = *compose; 382 383 return 0; 384 } 385 386 static int mxc_isi_pipe_enum_mbus_code(struct v4l2_subdev *sd, 387 struct v4l2_subdev_state *state, 388 struct v4l2_subdev_mbus_code_enum *code) 389 { 390 static const u32 output_codes[] = { 391 MEDIA_BUS_FMT_YUV8_1X24, 392 MEDIA_BUS_FMT_RGB888_1X24, 393 }; 394 struct mxc_isi_pipe *pipe = to_isi_pipe(sd); 395 const struct mxc_isi_bus_format_info *info; 396 unsigned int index; 397 unsigned int i; 398 399 if (code->pad == MXC_ISI_PIPE_PAD_SOURCE) { 400 const struct v4l2_mbus_framefmt *format; 401 402 format = mxc_isi_pipe_get_pad_format(pipe, state, 403 MXC_ISI_PIPE_PAD_SINK); 404 info = mxc_isi_bus_format_by_code(format->code, 405 MXC_ISI_PIPE_PAD_SINK); 406 407 if (info->encoding == MXC_ISI_ENC_RAW) { 408 /* 409 * For RAW formats, the sink and source media bus codes 410 * must match. 411 */ 412 if (code->index) 413 return -EINVAL; 414 415 code->code = info->output; 416 } else { 417 /* 418 * For RGB or YUV formats, the ISI supports format 419 * conversion. Either of the two output formats can be 420 * used regardless of the input. 421 */ 422 if (code->index > 1) 423 return -EINVAL; 424 425 code->code = output_codes[code->index]; 426 } 427 428 return 0; 429 } 430 431 index = code->index; 432 433 for (i = 0; i < ARRAY_SIZE(mxc_isi_bus_formats); ++i) { 434 info = &mxc_isi_bus_formats[i]; 435 436 if (!(info->pads & BIT(MXC_ISI_PIPE_PAD_SINK))) 437 continue; 438 439 if (index == 0) { 440 code->code = info->mbus_code; 441 return 0; 442 } 443 444 index--; 445 } 446 447 return -EINVAL; 448 } 449 450 static int mxc_isi_pipe_set_fmt(struct v4l2_subdev *sd, 451 struct v4l2_subdev_state *state, 452 struct v4l2_subdev_format *fmt) 453 { 454 struct mxc_isi_pipe *pipe = to_isi_pipe(sd); 455 struct v4l2_mbus_framefmt *mf = &fmt->format; 456 const struct mxc_isi_bus_format_info *info; 457 struct v4l2_mbus_framefmt *format; 458 struct v4l2_rect *rect; 459 460 if (vb2_is_busy(&pipe->video.vb2_q)) 461 return -EBUSY; 462 463 if (fmt->pad == MXC_ISI_PIPE_PAD_SINK) { 464 unsigned int max_width; 465 466 info = mxc_isi_bus_format_by_code(mf->code, 467 MXC_ISI_PIPE_PAD_SINK); 468 if (!info) 469 info = mxc_isi_bus_format_by_code(MXC_ISI_DEF_MBUS_CODE_SINK, 470 MXC_ISI_PIPE_PAD_SINK); 471 472 /* 473 * Limit the max line length if there's no adjacent pipe to 474 * chain with. 475 */ 476 max_width = pipe->id == pipe->isi->pdata->num_channels - 1 477 ? MXC_ISI_MAX_WIDTH_UNCHAINED 478 : MXC_ISI_MAX_WIDTH_CHAINED; 479 480 mf->code = info->mbus_code; 481 mf->width = clamp(mf->width, MXC_ISI_MIN_WIDTH, max_width); 482 mf->height = clamp(mf->height, MXC_ISI_MIN_HEIGHT, 483 MXC_ISI_MAX_HEIGHT); 484 485 /* Propagate the format to the source pad. */ 486 rect = mxc_isi_pipe_get_pad_compose(pipe, state, 487 MXC_ISI_PIPE_PAD_SINK); 488 rect->width = mf->width; 489 rect->height = mf->height; 490 491 rect = mxc_isi_pipe_get_pad_crop(pipe, state, 492 MXC_ISI_PIPE_PAD_SOURCE); 493 rect->left = 0; 494 rect->top = 0; 495 rect->width = mf->width; 496 rect->height = mf->height; 497 498 format = mxc_isi_pipe_get_pad_format(pipe, state, 499 MXC_ISI_PIPE_PAD_SOURCE); 500 format->code = info->output; 501 format->width = mf->width; 502 format->height = mf->height; 503 } else { 504 /* 505 * For RGB or YUV formats, the ISI supports RGB <-> YUV format 506 * conversion. For RAW formats, the sink and source media bus 507 * codes must match. 508 */ 509 format = mxc_isi_pipe_get_pad_format(pipe, state, 510 MXC_ISI_PIPE_PAD_SINK); 511 info = mxc_isi_bus_format_by_code(format->code, 512 MXC_ISI_PIPE_PAD_SINK); 513 514 if (info->encoding != MXC_ISI_ENC_RAW) { 515 if (mf->code != MEDIA_BUS_FMT_YUV8_1X24 && 516 mf->code != MEDIA_BUS_FMT_RGB888_1X24) 517 mf->code = info->output; 518 519 info = mxc_isi_bus_format_by_code(mf->code, 520 MXC_ISI_PIPE_PAD_SOURCE); 521 } 522 523 mf->code = info->output; 524 525 /* 526 * The width and height on the source can't be changed, they 527 * must match the crop rectangle size. 528 */ 529 rect = mxc_isi_pipe_get_pad_crop(pipe, state, 530 MXC_ISI_PIPE_PAD_SOURCE); 531 532 mf->width = rect->width; 533 mf->height = rect->height; 534 } 535 536 format = mxc_isi_pipe_get_pad_format(pipe, state, fmt->pad); 537 *format = *mf; 538 539 dev_dbg(pipe->isi->dev, "pad%u: code: 0x%04x, %ux%u", 540 fmt->pad, mf->code, mf->width, mf->height); 541 542 return 0; 543 } 544 545 static int mxc_isi_pipe_get_selection(struct v4l2_subdev *sd, 546 struct v4l2_subdev_state *state, 547 struct v4l2_subdev_selection *sel) 548 { 549 struct mxc_isi_pipe *pipe = to_isi_pipe(sd); 550 const struct v4l2_mbus_framefmt *format; 551 const struct v4l2_rect *rect; 552 553 switch (sel->target) { 554 case V4L2_SEL_TGT_COMPOSE_BOUNDS: 555 if (sel->pad != MXC_ISI_PIPE_PAD_SINK) 556 /* No compose rectangle on source pad. */ 557 return -EINVAL; 558 559 /* The sink compose is bound by the sink format. */ 560 format = mxc_isi_pipe_get_pad_format(pipe, state, 561 MXC_ISI_PIPE_PAD_SINK); 562 sel->r.left = 0; 563 sel->r.top = 0; 564 sel->r.width = format->width; 565 sel->r.height = format->height; 566 break; 567 568 case V4L2_SEL_TGT_CROP_BOUNDS: 569 if (sel->pad != MXC_ISI_PIPE_PAD_SOURCE) 570 /* No crop rectangle on sink pad. */ 571 return -EINVAL; 572 573 /* The source crop is bound by the sink compose. */ 574 rect = mxc_isi_pipe_get_pad_compose(pipe, state, 575 MXC_ISI_PIPE_PAD_SINK); 576 sel->r = *rect; 577 break; 578 579 case V4L2_SEL_TGT_CROP: 580 if (sel->pad != MXC_ISI_PIPE_PAD_SOURCE) 581 /* No crop rectangle on sink pad. */ 582 return -EINVAL; 583 584 rect = mxc_isi_pipe_get_pad_crop(pipe, state, sel->pad); 585 sel->r = *rect; 586 break; 587 588 case V4L2_SEL_TGT_COMPOSE: 589 if (sel->pad != MXC_ISI_PIPE_PAD_SINK) 590 /* No compose rectangle on source pad. */ 591 return -EINVAL; 592 593 rect = mxc_isi_pipe_get_pad_compose(pipe, state, sel->pad); 594 sel->r = *rect; 595 break; 596 597 default: 598 return -EINVAL; 599 } 600 601 return 0; 602 } 603 604 static int mxc_isi_pipe_set_selection(struct v4l2_subdev *sd, 605 struct v4l2_subdev_state *state, 606 struct v4l2_subdev_selection *sel) 607 { 608 struct mxc_isi_pipe *pipe = to_isi_pipe(sd); 609 struct v4l2_mbus_framefmt *format; 610 struct v4l2_rect *rect; 611 612 switch (sel->target) { 613 case V4L2_SEL_TGT_CROP: 614 if (sel->pad != MXC_ISI_PIPE_PAD_SOURCE) 615 /* The pipeline support cropping on the source only. */ 616 return -EINVAL; 617 618 /* The source crop is bound by the sink compose. */ 619 rect = mxc_isi_pipe_get_pad_compose(pipe, state, 620 MXC_ISI_PIPE_PAD_SINK); 621 sel->r.left = clamp_t(s32, sel->r.left, 0, rect->width - 1); 622 sel->r.top = clamp_t(s32, sel->r.top, 0, rect->height - 1); 623 sel->r.width = clamp(sel->r.width, MXC_ISI_MIN_WIDTH, 624 rect->width - sel->r.left); 625 sel->r.height = clamp(sel->r.height, MXC_ISI_MIN_HEIGHT, 626 rect->height - sel->r.top); 627 628 rect = mxc_isi_pipe_get_pad_crop(pipe, state, 629 MXC_ISI_PIPE_PAD_SOURCE); 630 *rect = sel->r; 631 632 /* Propagate the crop rectangle to the source pad. */ 633 format = mxc_isi_pipe_get_pad_format(pipe, state, 634 MXC_ISI_PIPE_PAD_SOURCE); 635 format->width = sel->r.width; 636 format->height = sel->r.height; 637 break; 638 639 case V4L2_SEL_TGT_COMPOSE: 640 if (sel->pad != MXC_ISI_PIPE_PAD_SINK) 641 /* Composing is supported on the sink only. */ 642 return -EINVAL; 643 644 /* The sink crop is bound by the sink format downscaling only). */ 645 format = mxc_isi_pipe_get_pad_format(pipe, state, 646 MXC_ISI_PIPE_PAD_SINK); 647 648 sel->r.left = 0; 649 sel->r.top = 0; 650 sel->r.width = clamp(sel->r.width, MXC_ISI_MIN_WIDTH, 651 format->width); 652 sel->r.height = clamp(sel->r.height, MXC_ISI_MIN_HEIGHT, 653 format->height); 654 655 rect = mxc_isi_pipe_get_pad_compose(pipe, state, 656 MXC_ISI_PIPE_PAD_SINK); 657 *rect = sel->r; 658 659 /* Propagate the compose rectangle to the source pad. */ 660 rect = mxc_isi_pipe_get_pad_crop(pipe, state, 661 MXC_ISI_PIPE_PAD_SOURCE); 662 rect->left = 0; 663 rect->top = 0; 664 rect->width = sel->r.width; 665 rect->height = sel->r.height; 666 667 format = mxc_isi_pipe_get_pad_format(pipe, state, 668 MXC_ISI_PIPE_PAD_SOURCE); 669 format->width = sel->r.width; 670 format->height = sel->r.height; 671 break; 672 673 default: 674 return -EINVAL; 675 } 676 677 dev_dbg(pipe->isi->dev, "%s, target %#x: (%d,%d)/%dx%d", __func__, 678 sel->target, sel->r.left, sel->r.top, sel->r.width, 679 sel->r.height); 680 681 return 0; 682 } 683 684 static const struct v4l2_subdev_pad_ops mxc_isi_pipe_subdev_pad_ops = { 685 .init_cfg = mxc_isi_pipe_init_cfg, 686 .enum_mbus_code = mxc_isi_pipe_enum_mbus_code, 687 .get_fmt = v4l2_subdev_get_fmt, 688 .set_fmt = mxc_isi_pipe_set_fmt, 689 .get_selection = mxc_isi_pipe_get_selection, 690 .set_selection = mxc_isi_pipe_set_selection, 691 }; 692 693 static const struct v4l2_subdev_ops mxc_isi_pipe_subdev_ops = { 694 .pad = &mxc_isi_pipe_subdev_pad_ops, 695 }; 696 697 /* ----------------------------------------------------------------------------- 698 * IRQ handling 699 */ 700 701 static irqreturn_t mxc_isi_pipe_irq_handler(int irq, void *priv) 702 { 703 struct mxc_isi_pipe *pipe = priv; 704 const struct mxc_isi_ier_reg *ier_reg = pipe->isi->pdata->ier_reg; 705 u32 status; 706 707 status = mxc_isi_channel_irq_status(pipe, true); 708 709 if (status & CHNL_STS_FRM_STRD) { 710 if (!WARN_ON(!pipe->irq_handler)) 711 pipe->irq_handler(pipe, status); 712 } 713 714 if (status & (CHNL_STS_AXI_WR_ERR_Y | 715 CHNL_STS_AXI_WR_ERR_U | 716 CHNL_STS_AXI_WR_ERR_V)) 717 dev_dbg(pipe->isi->dev, "%s: IRQ AXI Error stat=0x%X\n", 718 __func__, status); 719 720 if (status & (ier_reg->panic_y_buf_en.mask | 721 ier_reg->panic_u_buf_en.mask | 722 ier_reg->panic_v_buf_en.mask)) 723 dev_dbg(pipe->isi->dev, "%s: IRQ Panic OFLW Error stat=0x%X\n", 724 __func__, status); 725 726 if (status & (ier_reg->oflw_y_buf_en.mask | 727 ier_reg->oflw_u_buf_en.mask | 728 ier_reg->oflw_v_buf_en.mask)) 729 dev_dbg(pipe->isi->dev, "%s: IRQ OFLW Error stat=0x%X\n", 730 __func__, status); 731 732 if (status & (ier_reg->excs_oflw_y_buf_en.mask | 733 ier_reg->excs_oflw_u_buf_en.mask | 734 ier_reg->excs_oflw_v_buf_en.mask)) 735 dev_dbg(pipe->isi->dev, "%s: IRQ EXCS OFLW Error stat=0x%X\n", 736 __func__, status); 737 738 return IRQ_HANDLED; 739 } 740 741 /* ----------------------------------------------------------------------------- 742 * Init & cleanup 743 */ 744 745 static const struct media_entity_operations mxc_isi_pipe_entity_ops = { 746 .link_validate = v4l2_subdev_link_validate, 747 }; 748 749 int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id) 750 { 751 struct mxc_isi_pipe *pipe = &isi->pipes[id]; 752 struct v4l2_subdev *sd; 753 int irq; 754 int ret; 755 756 pipe->id = id; 757 pipe->isi = isi; 758 pipe->regs = isi->regs + id * isi->pdata->reg_offset; 759 760 mutex_init(&pipe->lock); 761 762 pipe->available_res = MXC_ISI_CHANNEL_RES_LINE_BUF 763 | MXC_ISI_CHANNEL_RES_OUTPUT_BUF; 764 pipe->acquired_res = 0; 765 pipe->chained_res = 0; 766 pipe->chained = false; 767 768 sd = &pipe->sd; 769 v4l2_subdev_init(sd, &mxc_isi_pipe_subdev_ops); 770 sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 771 snprintf(sd->name, sizeof(sd->name), "mxc_isi.%d", pipe->id); 772 sd->dev = isi->dev; 773 774 sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; 775 sd->entity.ops = &mxc_isi_pipe_entity_ops; 776 777 pipe->pads[MXC_ISI_PIPE_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 778 pipe->pads[MXC_ISI_PIPE_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; 779 780 ret = media_entity_pads_init(&sd->entity, MXC_ISI_PIPE_PADS_NUM, 781 pipe->pads); 782 if (ret) 783 goto error; 784 785 ret = v4l2_subdev_init_finalize(sd); 786 if (ret < 0) 787 goto error; 788 789 /* Register IRQ handler. */ 790 mxc_isi_channel_irq_clear(pipe); 791 792 irq = platform_get_irq(to_platform_device(isi->dev), id); 793 if (irq < 0) { 794 dev_err(pipe->isi->dev, "Failed to get IRQ (%d)\n", irq); 795 ret = irq; 796 goto error; 797 } 798 799 ret = devm_request_irq(isi->dev, irq, mxc_isi_pipe_irq_handler, 800 0, dev_name(isi->dev), pipe); 801 if (ret < 0) { 802 dev_err(isi->dev, "failed to request IRQ (%d)\n", ret); 803 goto error; 804 } 805 806 return 0; 807 808 error: 809 media_entity_cleanup(&sd->entity); 810 mutex_destroy(&pipe->lock); 811 812 return ret; 813 } 814 815 void mxc_isi_pipe_cleanup(struct mxc_isi_pipe *pipe) 816 { 817 struct v4l2_subdev *sd = &pipe->sd; 818 819 media_entity_cleanup(&sd->entity); 820 mutex_destroy(&pipe->lock); 821 } 822 823 int mxc_isi_pipe_acquire(struct mxc_isi_pipe *pipe, 824 mxc_isi_pipe_irq_t irq_handler) 825 { 826 const struct mxc_isi_bus_format_info *sink_info; 827 const struct mxc_isi_bus_format_info *src_info; 828 struct v4l2_mbus_framefmt *sink_fmt; 829 const struct v4l2_mbus_framefmt *src_fmt; 830 struct v4l2_subdev *sd = &pipe->sd; 831 struct v4l2_subdev_state *state; 832 bool bypass; 833 int ret; 834 835 state = v4l2_subdev_lock_and_get_active_state(sd); 836 sink_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SINK); 837 src_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SOURCE); 838 v4l2_subdev_unlock_state(state); 839 840 sink_info = mxc_isi_bus_format_by_code(sink_fmt->code, 841 MXC_ISI_PIPE_PAD_SINK); 842 src_info = mxc_isi_bus_format_by_code(src_fmt->code, 843 MXC_ISI_PIPE_PAD_SOURCE); 844 845 bypass = sink_fmt->width == src_fmt->width && 846 sink_fmt->height == src_fmt->height && 847 sink_info->encoding == src_info->encoding; 848 849 ret = mxc_isi_channel_acquire(pipe, irq_handler, bypass); 850 if (ret) 851 return ret; 852 853 /* Chain the channel if needed for wide resolutions. */ 854 if (sink_fmt->width > MXC_ISI_MAX_WIDTH_UNCHAINED) { 855 ret = mxc_isi_channel_chain(pipe, bypass); 856 if (ret) 857 mxc_isi_channel_release(pipe); 858 } 859 860 return ret; 861 } 862 863 void mxc_isi_pipe_release(struct mxc_isi_pipe *pipe) 864 { 865 mxc_isi_channel_release(pipe); 866 mxc_isi_channel_unchain(pipe); 867 } 868