871 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * V4L2 Capture ISI subdev driver for i.MX8QXP/QM platform4 *5 * ISI is a Image Sensor Interface of i.MX8QXP/QM platform, which6 * used to process image from camera sensor to memory or DC7 *8 * Copyright (c) 2019 NXP Semiconductor9 */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 pipeline31 * subdev conceptually includes the gasket in order to avoid exposing an extra32 * subdev between the CSIS and the ISI. We thus need to expose media bus codes33 * corresponding to the CSIS output, which is narrower.34 */35static 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 193const struct mxc_isi_bus_format_info *194mxc_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 209const struct mxc_isi_bus_format_info *210mxc_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 230static 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 235int 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 routing252 * 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_state_get_format(state, MXC_ISI_PIPE_PAD_SINK);267 src_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE);268 compose = v4l2_subdev_state_get_compose(state, MXC_ISI_PIPE_PAD_SINK);269 crop = *v4l2_subdev_state_get_crop(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 302void 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 operations318 */319 320static struct v4l2_mbus_framefmt *321mxc_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_state_get_format(state, pad);326}327 328static struct v4l2_rect *329mxc_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_state_get_crop(state, pad);334}335 336static struct v4l2_rect *337mxc_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_state_get_compose(state, pad);342}343 344static int mxc_isi_pipe_init_state(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 386static 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 codes410 * 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 format419 * conversion. Either of the two output formats can be420 * 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 450static 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 to474 * chain with.475 */476 max_width = pipe->id == pipe->isi->pdata->num_channels - 1477 ? MXC_ISI_MAX_WIDTH_UNCHAINED478 : 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 format506 * conversion. For RAW formats, the sink and source media bus507 * 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, they527 * 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 545static 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 604static 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 684static const struct v4l2_subdev_pad_ops mxc_isi_pipe_subdev_pad_ops = {685 .enum_mbus_code = mxc_isi_pipe_enum_mbus_code,686 .get_fmt = v4l2_subdev_get_fmt,687 .set_fmt = mxc_isi_pipe_set_fmt,688 .get_selection = mxc_isi_pipe_get_selection,689 .set_selection = mxc_isi_pipe_set_selection,690};691 692static const struct v4l2_subdev_ops mxc_isi_pipe_subdev_ops = {693 .pad = &mxc_isi_pipe_subdev_pad_ops,694};695 696static const struct v4l2_subdev_internal_ops mxc_isi_pipe_internal_ops = {697 .init_state = mxc_isi_pipe_init_state,698};699 700/* -----------------------------------------------------------------------------701 * IRQ handling702 */703 704static irqreturn_t mxc_isi_pipe_irq_handler(int irq, void *priv)705{706 struct mxc_isi_pipe *pipe = priv;707 const struct mxc_isi_ier_reg *ier_reg = pipe->isi->pdata->ier_reg;708 u32 status;709 710 status = mxc_isi_channel_irq_status(pipe, true);711 712 if (status & CHNL_STS_FRM_STRD) {713 if (!WARN_ON(!pipe->irq_handler))714 pipe->irq_handler(pipe, status);715 }716 717 if (status & (CHNL_STS_AXI_WR_ERR_Y |718 CHNL_STS_AXI_WR_ERR_U |719 CHNL_STS_AXI_WR_ERR_V))720 dev_dbg(pipe->isi->dev, "%s: IRQ AXI Error stat=0x%X\n",721 __func__, status);722 723 if (status & (ier_reg->panic_y_buf_en.mask |724 ier_reg->panic_u_buf_en.mask |725 ier_reg->panic_v_buf_en.mask))726 dev_dbg(pipe->isi->dev, "%s: IRQ Panic OFLW Error stat=0x%X\n",727 __func__, status);728 729 if (status & (ier_reg->oflw_y_buf_en.mask |730 ier_reg->oflw_u_buf_en.mask |731 ier_reg->oflw_v_buf_en.mask))732 dev_dbg(pipe->isi->dev, "%s: IRQ OFLW Error stat=0x%X\n",733 __func__, status);734 735 if (status & (ier_reg->excs_oflw_y_buf_en.mask |736 ier_reg->excs_oflw_u_buf_en.mask |737 ier_reg->excs_oflw_v_buf_en.mask))738 dev_dbg(pipe->isi->dev, "%s: IRQ EXCS OFLW Error stat=0x%X\n",739 __func__, status);740 741 return IRQ_HANDLED;742}743 744/* -----------------------------------------------------------------------------745 * Init & cleanup746 */747 748static const struct media_entity_operations mxc_isi_pipe_entity_ops = {749 .link_validate = v4l2_subdev_link_validate,750};751 752int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id)753{754 struct mxc_isi_pipe *pipe = &isi->pipes[id];755 struct v4l2_subdev *sd;756 int irq;757 int ret;758 759 pipe->id = id;760 pipe->isi = isi;761 pipe->regs = isi->regs + id * isi->pdata->reg_offset;762 763 mutex_init(&pipe->lock);764 765 pipe->available_res = MXC_ISI_CHANNEL_RES_LINE_BUF766 | MXC_ISI_CHANNEL_RES_OUTPUT_BUF;767 pipe->acquired_res = 0;768 pipe->chained_res = 0;769 pipe->chained = false;770 771 sd = &pipe->sd;772 v4l2_subdev_init(sd, &mxc_isi_pipe_subdev_ops);773 sd->internal_ops = &mxc_isi_pipe_internal_ops;774 sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;775 snprintf(sd->name, sizeof(sd->name), "mxc_isi.%d", pipe->id);776 sd->dev = isi->dev;777 778 sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;779 sd->entity.ops = &mxc_isi_pipe_entity_ops;780 781 pipe->pads[MXC_ISI_PIPE_PAD_SINK].flags = MEDIA_PAD_FL_SINK;782 pipe->pads[MXC_ISI_PIPE_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;783 784 ret = media_entity_pads_init(&sd->entity, MXC_ISI_PIPE_PADS_NUM,785 pipe->pads);786 if (ret)787 goto error;788 789 ret = v4l2_subdev_init_finalize(sd);790 if (ret < 0)791 goto error;792 793 /* Register IRQ handler. */794 mxc_isi_channel_irq_clear(pipe);795 796 irq = platform_get_irq(to_platform_device(isi->dev), id);797 if (irq < 0) {798 ret = irq;799 goto error;800 }801 802 ret = devm_request_irq(isi->dev, irq, mxc_isi_pipe_irq_handler,803 0, dev_name(isi->dev), pipe);804 if (ret < 0) {805 dev_err(isi->dev, "failed to request IRQ (%d)\n", ret);806 goto error;807 }808 809 return 0;810 811error:812 media_entity_cleanup(&sd->entity);813 mutex_destroy(&pipe->lock);814 815 return ret;816}817 818void mxc_isi_pipe_cleanup(struct mxc_isi_pipe *pipe)819{820 struct v4l2_subdev *sd = &pipe->sd;821 822 media_entity_cleanup(&sd->entity);823 mutex_destroy(&pipe->lock);824}825 826int mxc_isi_pipe_acquire(struct mxc_isi_pipe *pipe,827 mxc_isi_pipe_irq_t irq_handler)828{829 const struct mxc_isi_bus_format_info *sink_info;830 const struct mxc_isi_bus_format_info *src_info;831 struct v4l2_mbus_framefmt *sink_fmt;832 const struct v4l2_mbus_framefmt *src_fmt;833 struct v4l2_subdev *sd = &pipe->sd;834 struct v4l2_subdev_state *state;835 bool bypass;836 int ret;837 838 state = v4l2_subdev_lock_and_get_active_state(sd);839 sink_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SINK);840 src_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE);841 v4l2_subdev_unlock_state(state);842 843 sink_info = mxc_isi_bus_format_by_code(sink_fmt->code,844 MXC_ISI_PIPE_PAD_SINK);845 src_info = mxc_isi_bus_format_by_code(src_fmt->code,846 MXC_ISI_PIPE_PAD_SOURCE);847 848 bypass = sink_fmt->width == src_fmt->width &&849 sink_fmt->height == src_fmt->height &&850 sink_info->encoding == src_info->encoding;851 852 ret = mxc_isi_channel_acquire(pipe, irq_handler, bypass);853 if (ret)854 return ret;855 856 /* Chain the channel if needed for wide resolutions. */857 if (sink_fmt->width > MXC_ISI_MAX_WIDTH_UNCHAINED) {858 ret = mxc_isi_channel_chain(pipe, bypass);859 if (ret)860 mxc_isi_channel_release(pipe);861 }862 863 return ret;864}865 866void mxc_isi_pipe_release(struct mxc_isi_pipe *pipe)867{868 mxc_isi_channel_release(pipe);869 mxc_isi_channel_unchain(pipe);870}871