1 // SPDX-License-Identifier: GPL-2.0
3 * V4L2 Capture ISI subdev driver for i.MX8QXP/QM platform
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
8 * Copyright (c) 2019 NXP Semiconductor
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>
18 #include <linux/platform_device.h>
19 #include <linux/types.h>
20 #include <linux/videodev2.h>
22 #include <media/media-entity.h>
23 #include <media/v4l2-subdev.h>
24 #include <media/videobuf2-v4l2.h>
26 #include "imx8-isi-core.h"
27 #include "imx8-isi-regs.h"
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.
35 static const struct mxc_isi_bus_format_info mxc_isi_bus_formats[] = {
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
193 const struct mxc_isi_bus_format_info *
194 mxc_isi_bus_format_by_code(u32 code, unsigned int pad)
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];
202 if (info->mbus_code == code && info->pads & BIT(pad))
209 const struct mxc_isi_bus_format_info *
210 mxc_isi_bus_format_by_index(unsigned int index, unsigned int pad)
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];
218 if (!(info->pads & BIT(pad)))
230 static inline struct mxc_isi_pipe *to_isi_pipe(struct v4l2_subdev *sd)
232 return container_of(sd, struct mxc_isi_pipe, sd);
235 int mxc_isi_pipe_enable(struct mxc_isi_pipe *pipe)
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;
251 * Find the connected input by inspecting the crossbar switch routing
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,
258 v4l2_subdev_unlock_state(state);
263 /* Configure the pipeline. */
264 state = v4l2_subdev_lock_and_get_active_state(sd);
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);
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);
276 in_size.width = sink_fmt->width;
277 in_size.height = sink_fmt->height;
278 scale.width = compose->width;
279 scale.height = compose->height;
281 v4l2_subdev_unlock_state(state);
283 /* Configure the ISI channel. */
284 mxc_isi_channel_config(pipe, input, &in_size, &scale, &crop,
285 sink_info->encoding, src_info->encoding);
287 mxc_isi_channel_enable(pipe);
289 /* Enable streams on the crossbar switch. */
290 ret = v4l2_subdev_enable_streams(&xbar->sd, xbar->num_sinks + pipe->id,
293 mxc_isi_channel_disable(pipe);
294 dev_err(pipe->isi->dev, "Failed to enable pipe %u\n",
302 void mxc_isi_pipe_disable(struct mxc_isi_pipe *pipe)
304 struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar;
307 ret = v4l2_subdev_disable_streams(&xbar->sd, xbar->num_sinks + pipe->id,
310 dev_err(pipe->isi->dev, "Failed to disable pipe %u\n",
313 mxc_isi_channel_disable(pipe);
316 /* -----------------------------------------------------------------------------
317 * V4L2 subdev operations
320 static struct v4l2_mbus_framefmt *
321 mxc_isi_pipe_get_pad_format(struct mxc_isi_pipe *pipe,
322 struct v4l2_subdev_state *state,
325 return v4l2_subdev_get_try_format(&pipe->sd, state, pad);
328 static struct v4l2_rect *
329 mxc_isi_pipe_get_pad_crop(struct mxc_isi_pipe *pipe,
330 struct v4l2_subdev_state *state,
333 return v4l2_subdev_get_try_crop(&pipe->sd, state, pad);
336 static struct v4l2_rect *
337 mxc_isi_pipe_get_pad_compose(struct mxc_isi_pipe *pipe,
338 struct v4l2_subdev_state *state,
341 return v4l2_subdev_get_try_compose(&pipe->sd, state, pad);
344 static int mxc_isi_pipe_init_cfg(struct v4l2_subdev *sd,
345 struct v4l2_subdev_state *state)
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;
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);
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);
369 *fmt_source = *fmt_sink;
370 fmt_source->code = MXC_ISI_DEF_MBUS_CODE_SOURCE;
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);
378 compose->width = MXC_ISI_DEF_WIDTH;
379 compose->height = MXC_ISI_DEF_HEIGHT;
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)
390 static const u32 output_codes[] = {
391 MEDIA_BUS_FMT_YUV8_1X24,
392 MEDIA_BUS_FMT_RGB888_1X24,
394 struct mxc_isi_pipe *pipe = to_isi_pipe(sd);
395 const struct mxc_isi_bus_format_info *info;
399 if (code->pad == MXC_ISI_PIPE_PAD_SOURCE) {
400 const struct v4l2_mbus_framefmt *format;
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);
407 if (info->encoding == MXC_ISI_ENC_RAW) {
409 * For RAW formats, the sink and source media bus codes
415 code->code = info->output;
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.
425 code->code = output_codes[code->index];
433 for (i = 0; i < ARRAY_SIZE(mxc_isi_bus_formats); ++i) {
434 info = &mxc_isi_bus_formats[i];
436 if (!(info->pads & BIT(MXC_ISI_PIPE_PAD_SINK)))
440 code->code = info->mbus_code;
450 static int mxc_isi_pipe_set_fmt(struct v4l2_subdev *sd,
451 struct v4l2_subdev_state *state,
452 struct v4l2_subdev_format *fmt)
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;
460 if (vb2_is_busy(&pipe->video.vb2_q))
463 if (fmt->pad == MXC_ISI_PIPE_PAD_SINK) {
464 unsigned int max_width;
466 info = mxc_isi_bus_format_by_code(mf->code,
467 MXC_ISI_PIPE_PAD_SINK);
469 info = mxc_isi_bus_format_by_code(MXC_ISI_DEF_MBUS_CODE_SINK,
470 MXC_ISI_PIPE_PAD_SINK);
473 * Limit the max line length if there's no adjacent pipe to
476 max_width = pipe->id == pipe->isi->pdata->num_channels - 1
477 ? MXC_ISI_MAX_WIDTH_UNCHAINED
478 : MXC_ISI_MAX_WIDTH_CHAINED;
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,
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;
491 rect = mxc_isi_pipe_get_pad_crop(pipe, state,
492 MXC_ISI_PIPE_PAD_SOURCE);
495 rect->width = mf->width;
496 rect->height = mf->height;
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;
505 * For RGB or YUV formats, the ISI supports RGB <-> YUV format
506 * conversion. For RAW formats, the sink and source media bus
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);
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;
519 info = mxc_isi_bus_format_by_code(mf->code,
520 MXC_ISI_PIPE_PAD_SOURCE);
523 mf->code = info->output;
526 * The width and height on the source can't be changed, they
527 * must match the crop rectangle size.
529 rect = mxc_isi_pipe_get_pad_crop(pipe, state,
530 MXC_ISI_PIPE_PAD_SOURCE);
532 mf->width = rect->width;
533 mf->height = rect->height;
536 format = mxc_isi_pipe_get_pad_format(pipe, state, fmt->pad);
539 dev_dbg(pipe->isi->dev, "pad%u: code: 0x%04x, %ux%u",
540 fmt->pad, mf->code, mf->width, mf->height);
545 static int mxc_isi_pipe_get_selection(struct v4l2_subdev *sd,
546 struct v4l2_subdev_state *state,
547 struct v4l2_subdev_selection *sel)
549 struct mxc_isi_pipe *pipe = to_isi_pipe(sd);
550 const struct v4l2_mbus_framefmt *format;
551 const struct v4l2_rect *rect;
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. */
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);
564 sel->r.width = format->width;
565 sel->r.height = format->height;
568 case V4L2_SEL_TGT_CROP_BOUNDS:
569 if (sel->pad != MXC_ISI_PIPE_PAD_SOURCE)
570 /* No crop rectangle on sink pad. */
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);
579 case V4L2_SEL_TGT_CROP:
580 if (sel->pad != MXC_ISI_PIPE_PAD_SOURCE)
581 /* No crop rectangle on sink pad. */
584 rect = mxc_isi_pipe_get_pad_crop(pipe, state, sel->pad);
588 case V4L2_SEL_TGT_COMPOSE:
589 if (sel->pad != MXC_ISI_PIPE_PAD_SINK)
590 /* No compose rectangle on source pad. */
593 rect = mxc_isi_pipe_get_pad_compose(pipe, state, sel->pad);
604 static int mxc_isi_pipe_set_selection(struct v4l2_subdev *sd,
605 struct v4l2_subdev_state *state,
606 struct v4l2_subdev_selection *sel)
608 struct mxc_isi_pipe *pipe = to_isi_pipe(sd);
609 struct v4l2_mbus_framefmt *format;
610 struct v4l2_rect *rect;
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. */
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);
628 rect = mxc_isi_pipe_get_pad_crop(pipe, state,
629 MXC_ISI_PIPE_PAD_SOURCE);
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;
639 case V4L2_SEL_TGT_COMPOSE:
640 if (sel->pad != MXC_ISI_PIPE_PAD_SINK)
641 /* Composing is supported on the sink only. */
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);
650 sel->r.width = clamp(sel->r.width, MXC_ISI_MIN_WIDTH,
652 sel->r.height = clamp(sel->r.height, MXC_ISI_MIN_HEIGHT,
655 rect = mxc_isi_pipe_get_pad_compose(pipe, state,
656 MXC_ISI_PIPE_PAD_SINK);
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);
664 rect->width = sel->r.width;
665 rect->height = sel->r.height;
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;
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,
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,
693 static const struct v4l2_subdev_ops mxc_isi_pipe_subdev_ops = {
694 .pad = &mxc_isi_pipe_subdev_pad_ops,
697 /* -----------------------------------------------------------------------------
701 static irqreturn_t mxc_isi_pipe_irq_handler(int irq, void *priv)
703 struct mxc_isi_pipe *pipe = priv;
704 const struct mxc_isi_ier_reg *ier_reg = pipe->isi->pdata->ier_reg;
707 status = mxc_isi_channel_irq_status(pipe, true);
709 if (status & CHNL_STS_FRM_STRD) {
710 if (!WARN_ON(!pipe->irq_handler))
711 pipe->irq_handler(pipe, status);
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",
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",
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",
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",
741 /* -----------------------------------------------------------------------------
745 static const struct media_entity_operations mxc_isi_pipe_entity_ops = {
746 .link_validate = v4l2_subdev_link_validate,
749 int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id)
751 struct mxc_isi_pipe *pipe = &isi->pipes[id];
752 struct v4l2_subdev *sd;
758 pipe->regs = isi->regs + id * isi->pdata->reg_offset;
760 mutex_init(&pipe->lock);
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;
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);
774 sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
775 sd->entity.ops = &mxc_isi_pipe_entity_ops;
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;
780 ret = media_entity_pads_init(&sd->entity, MXC_ISI_PIPE_PADS_NUM,
785 ret = v4l2_subdev_init_finalize(sd);
789 /* Register IRQ handler. */
790 mxc_isi_channel_irq_clear(pipe);
792 irq = platform_get_irq(to_platform_device(isi->dev), id);
794 dev_err(pipe->isi->dev, "Failed to get IRQ (%d)\n", irq);
799 ret = devm_request_irq(isi->dev, irq, mxc_isi_pipe_irq_handler,
800 0, dev_name(isi->dev), pipe);
802 dev_err(isi->dev, "failed to request IRQ (%d)\n", ret);
809 media_entity_cleanup(&sd->entity);
810 mutex_destroy(&pipe->lock);
815 void mxc_isi_pipe_cleanup(struct mxc_isi_pipe *pipe)
817 struct v4l2_subdev *sd = &pipe->sd;
819 media_entity_cleanup(&sd->entity);
820 mutex_destroy(&pipe->lock);
823 int mxc_isi_pipe_acquire(struct mxc_isi_pipe *pipe,
824 mxc_isi_pipe_irq_t irq_handler)
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;
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);
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);
845 bypass = sink_fmt->width == src_fmt->width &&
846 sink_fmt->height == src_fmt->height &&
847 sink_info->encoding == src_info->encoding;
849 ret = mxc_isi_channel_acquire(pipe, irq_handler, bypass);
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);
857 mxc_isi_channel_release(pipe);
863 void mxc_isi_pipe_release(struct mxc_isi_pipe *pipe)
865 mxc_isi_channel_release(pipe);
866 mxc_isi_channel_unchain(pipe);