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_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
);
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_state_get_format(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_state_get_crop(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_state_get_compose(state
, pad
);
344 static int mxc_isi_pipe_init_state(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 .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
,
692 static const struct v4l2_subdev_ops mxc_isi_pipe_subdev_ops
= {
693 .pad
= &mxc_isi_pipe_subdev_pad_ops
,
696 static const struct v4l2_subdev_internal_ops mxc_isi_pipe_internal_ops
= {
697 .init_state
= mxc_isi_pipe_init_state
,
700 /* -----------------------------------------------------------------------------
704 static irqreturn_t
mxc_isi_pipe_irq_handler(int irq
, void *priv
)
706 struct mxc_isi_pipe
*pipe
= priv
;
707 const struct mxc_isi_ier_reg
*ier_reg
= pipe
->isi
->pdata
->ier_reg
;
710 status
= mxc_isi_channel_irq_status(pipe
, true);
712 if (status
& CHNL_STS_FRM_STRD
) {
713 if (!WARN_ON(!pipe
->irq_handler
))
714 pipe
->irq_handler(pipe
, status
);
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",
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",
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",
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",
744 /* -----------------------------------------------------------------------------
748 static const struct media_entity_operations mxc_isi_pipe_entity_ops
= {
749 .link_validate
= v4l2_subdev_link_validate
,
752 int mxc_isi_pipe_init(struct mxc_isi_dev
*isi
, unsigned int id
)
754 struct mxc_isi_pipe
*pipe
= &isi
->pipes
[id
];
755 struct v4l2_subdev
*sd
;
761 pipe
->regs
= isi
->regs
+ id
* isi
->pdata
->reg_offset
;
763 mutex_init(&pipe
->lock
);
765 pipe
->available_res
= MXC_ISI_CHANNEL_RES_LINE_BUF
766 | MXC_ISI_CHANNEL_RES_OUTPUT_BUF
;
767 pipe
->acquired_res
= 0;
768 pipe
->chained_res
= 0;
769 pipe
->chained
= false;
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
);
778 sd
->entity
.function
= MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER
;
779 sd
->entity
.ops
= &mxc_isi_pipe_entity_ops
;
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
;
784 ret
= media_entity_pads_init(&sd
->entity
, MXC_ISI_PIPE_PADS_NUM
,
789 ret
= v4l2_subdev_init_finalize(sd
);
793 /* Register IRQ handler. */
794 mxc_isi_channel_irq_clear(pipe
);
796 irq
= platform_get_irq(to_platform_device(isi
->dev
), id
);
802 ret
= devm_request_irq(isi
->dev
, irq
, mxc_isi_pipe_irq_handler
,
803 0, dev_name(isi
->dev
), pipe
);
805 dev_err(isi
->dev
, "failed to request IRQ (%d)\n", ret
);
812 media_entity_cleanup(&sd
->entity
);
813 mutex_destroy(&pipe
->lock
);
818 void mxc_isi_pipe_cleanup(struct mxc_isi_pipe
*pipe
)
820 struct v4l2_subdev
*sd
= &pipe
->sd
;
822 media_entity_cleanup(&sd
->entity
);
823 mutex_destroy(&pipe
->lock
);
826 int mxc_isi_pipe_acquire(struct mxc_isi_pipe
*pipe
,
827 mxc_isi_pipe_irq_t irq_handler
)
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
;
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
);
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
);
848 bypass
= sink_fmt
->width
== src_fmt
->width
&&
849 sink_fmt
->height
== src_fmt
->height
&&
850 sink_info
->encoding
== src_info
->encoding
;
852 ret
= mxc_isi_channel_acquire(pipe
, irq_handler
, bypass
);
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
);
860 mxc_isi_channel_release(pipe
);
866 void mxc_isi_pipe_release(struct mxc_isi_pipe
*pipe
)
868 mxc_isi_channel_release(pipe
);
869 mxc_isi_channel_unchain(pipe
);