drm/panel: panel-himax-hx83102: support for csot-pna957qt1-1 MIPI-DSI panel
[drm/drm-misc.git] / drivers / media / platform / nxp / imx8-isi / imx8-isi-pipe.c
blobd76eb58deb096b26fa41b1e262754faec8e3f26c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
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
9 */
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>
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[] = {
36 /* YUV 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,
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,
48 /* RGB formats */
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,
61 /* RAW formats */
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,
183 /* JPEG */
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)
196 unsigned int i;
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))
203 return info;
206 return NULL;
209 const struct mxc_isi_bus_format_info *
210 mxc_isi_bus_format_by_index(unsigned int index, unsigned int pad)
212 unsigned int i;
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)))
219 continue;
221 if (!index)
222 return info;
224 index--;
227 return NULL;
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;
247 u32 input;
248 int ret;
251 * Find the connected input by inspecting the crossbar switch routing
252 * table.
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);
260 if (ret)
261 return -EPIPE;
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,
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;
299 return 0;
302 void mxc_isi_pipe_disable(struct mxc_isi_pipe *pipe)
304 struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar;
305 int ret;
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);
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,
323 unsigned int pad)
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,
331 unsigned int pad)
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,
339 unsigned int pad)
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);
376 compose->left = 0;
377 compose->top = 0;
378 compose->width = MXC_ISI_DEF_WIDTH;
379 compose->height = MXC_ISI_DEF_HEIGHT;
381 *crop = *compose;
383 return 0;
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;
396 unsigned int index;
397 unsigned int i;
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
410 * must match.
412 if (code->index)
413 return -EINVAL;
415 code->code = info->output;
416 } else {
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.
422 if (code->index > 1)
423 return -EINVAL;
425 code->code = output_codes[code->index];
428 return 0;
431 index = 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)))
437 continue;
439 if (index == 0) {
440 code->code = info->mbus_code;
441 return 0;
444 index--;
447 return -EINVAL;
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))
461 return -EBUSY;
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);
468 if (!info)
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
474 * chain with.
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,
483 MXC_ISI_MAX_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);
493 rect->left = 0;
494 rect->top = 0;
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;
503 } else {
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.
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);
537 *format = *mf;
539 dev_dbg(pipe->isi->dev, "pad%u: code: 0x%04x, %ux%u",
540 fmt->pad, mf->code, mf->width, mf->height);
542 return 0;
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. */
557 return -EINVAL;
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;
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;
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;
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;
584 rect = mxc_isi_pipe_get_pad_crop(pipe, state, sel->pad);
585 sel->r = *rect;
586 break;
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;
593 rect = mxc_isi_pipe_get_pad_compose(pipe, state, sel->pad);
594 sel->r = *rect;
595 break;
597 default:
598 return -EINVAL;
601 return 0;
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. */
616 return -EINVAL;
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);
630 *rect = sel->r;
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;
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;
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);
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);
655 rect = mxc_isi_pipe_get_pad_compose(pipe, state,
656 MXC_ISI_PIPE_PAD_SINK);
657 *rect = sel->r;
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;
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;
673 default:
674 return -EINVAL;
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);
681 return 0;
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 /* -----------------------------------------------------------------------------
701 * IRQ handling
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;
708 u32 status;
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",
721 __func__, status);
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);
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);
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);
741 return IRQ_HANDLED;
744 /* -----------------------------------------------------------------------------
745 * Init & cleanup
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;
756 int irq;
757 int ret;
759 pipe->id = id;
760 pipe->isi = isi;
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;
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;
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,
785 pipe->pads);
786 if (ret)
787 goto error;
789 ret = v4l2_subdev_init_finalize(sd);
790 if (ret < 0)
791 goto error;
793 /* Register IRQ handler. */
794 mxc_isi_channel_irq_clear(pipe);
796 irq = platform_get_irq(to_platform_device(isi->dev), id);
797 if (irq < 0) {
798 ret = irq;
799 goto error;
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;
809 return 0;
811 error:
812 media_entity_cleanup(&sd->entity);
813 mutex_destroy(&pipe->lock);
815 return ret;
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;
835 bool bypass;
836 int ret;
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);
853 if (ret)
854 return ret;
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);
863 return ret;
866 void mxc_isi_pipe_release(struct mxc_isi_pipe *pipe)
868 mxc_isi_channel_release(pipe);
869 mxc_isi_channel_unchain(pipe);