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