xref: /linux/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c (revision 6e7fd890f1d6ac83805409e9c346240de2705584)
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 *
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 *
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 
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 
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_state_get_format(state, MXC_ISI_PIPE_PAD_SINK);
267 	src_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE);
268 	compose = v4l2_subdev_state_get_compose(state, MXC_ISI_PIPE_PAD_SINK);
269 	crop = *v4l2_subdev_state_get_crop(state, MXC_ISI_PIPE_PAD_SOURCE);
270 
271 	sink_info = mxc_isi_bus_format_by_code(sink_fmt->code,
272 					       MXC_ISI_PIPE_PAD_SINK);
273 	src_info = mxc_isi_bus_format_by_code(src_fmt->code,
274 					      MXC_ISI_PIPE_PAD_SOURCE);
275 
276 	in_size.width = sink_fmt->width;
277 	in_size.height = sink_fmt->height;
278 	scale.width = compose->width;
279 	scale.height = compose->height;
280 
281 	v4l2_subdev_unlock_state(state);
282 
283 	/* Configure the ISI channel. */
284 	mxc_isi_channel_config(pipe, input, &in_size, &scale, &crop,
285 			       sink_info->encoding, src_info->encoding);
286 
287 	mxc_isi_channel_enable(pipe);
288 
289 	/* Enable streams on the crossbar switch. */
290 	ret = v4l2_subdev_enable_streams(&xbar->sd, xbar->num_sinks + pipe->id,
291 					 BIT(0));
292 	if (ret) {
293 		mxc_isi_channel_disable(pipe);
294 		dev_err(pipe->isi->dev, "Failed to enable pipe %u\n",
295 			pipe->id);
296 		return ret;
297 	}
298 
299 	return 0;
300 }
301 
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 *
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_state_get_format(state, pad);
326 }
327 
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)
332 {
333 	return v4l2_subdev_state_get_crop(state, pad);
334 }
335 
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)
340 {
341 	return v4l2_subdev_state_get_compose(state, pad);
342 }
343 
344 static int mxc_isi_pipe_init_state(struct v4l2_subdev *sd,
345 				   struct v4l2_subdev_state *state)
346 {
347 	struct mxc_isi_pipe *pipe = to_isi_pipe(sd);
348 	struct v4l2_mbus_framefmt *fmt_source;
349 	struct v4l2_mbus_framefmt *fmt_sink;
350 	struct v4l2_rect *compose;
351 	struct v4l2_rect *crop;
352 
353 	fmt_sink = mxc_isi_pipe_get_pad_format(pipe, state,
354 					       MXC_ISI_PIPE_PAD_SINK);
355 	fmt_source = mxc_isi_pipe_get_pad_format(pipe, state,
356 						 MXC_ISI_PIPE_PAD_SOURCE);
357 
358 	fmt_sink->width = MXC_ISI_DEF_WIDTH;
359 	fmt_sink->height = MXC_ISI_DEF_HEIGHT;
360 	fmt_sink->code = MXC_ISI_DEF_MBUS_CODE_SINK;
361 	fmt_sink->field = V4L2_FIELD_NONE;
362 	fmt_sink->colorspace = V4L2_COLORSPACE_JPEG;
363 	fmt_sink->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt_sink->colorspace);
364 	fmt_sink->quantization =
365 		V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt_sink->colorspace,
366 					      fmt_sink->ycbcr_enc);
367 	fmt_sink->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt_sink->colorspace);
368 
369 	*fmt_source = *fmt_sink;
370 	fmt_source->code = MXC_ISI_DEF_MBUS_CODE_SOURCE;
371 
372 	compose = mxc_isi_pipe_get_pad_compose(pipe, state,
373 					       MXC_ISI_PIPE_PAD_SINK);
374 	crop = mxc_isi_pipe_get_pad_crop(pipe, state, MXC_ISI_PIPE_PAD_SOURCE);
375 
376 	compose->left = 0;
377 	compose->top = 0;
378 	compose->width = MXC_ISI_DEF_WIDTH;
379 	compose->height = MXC_ISI_DEF_HEIGHT;
380 
381 	*crop = *compose;
382 
383 	return 0;
384 }
385 
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 
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 
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 
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 	.enum_mbus_code = mxc_isi_pipe_enum_mbus_code,
686 	.get_fmt = v4l2_subdev_get_fmt,
687 	.set_fmt = mxc_isi_pipe_set_fmt,
688 	.get_selection = mxc_isi_pipe_get_selection,
689 	.set_selection = mxc_isi_pipe_set_selection,
690 };
691 
692 static const struct v4l2_subdev_ops mxc_isi_pipe_subdev_ops = {
693 	.pad = &mxc_isi_pipe_subdev_pad_ops,
694 };
695 
696 static const struct v4l2_subdev_internal_ops mxc_isi_pipe_internal_ops = {
697 	.init_state = mxc_isi_pipe_init_state,
698 };
699 
700 /* -----------------------------------------------------------------------------
701  * IRQ handling
702  */
703 
704 static irqreturn_t mxc_isi_pipe_irq_handler(int irq, void *priv)
705 {
706 	struct mxc_isi_pipe *pipe = priv;
707 	const struct mxc_isi_ier_reg *ier_reg = pipe->isi->pdata->ier_reg;
708 	u32 status;
709 
710 	status = mxc_isi_channel_irq_status(pipe, true);
711 
712 	if (status & CHNL_STS_FRM_STRD) {
713 		if (!WARN_ON(!pipe->irq_handler))
714 			pipe->irq_handler(pipe, status);
715 	}
716 
717 	if (status & (CHNL_STS_AXI_WR_ERR_Y |
718 		      CHNL_STS_AXI_WR_ERR_U |
719 		      CHNL_STS_AXI_WR_ERR_V))
720 		dev_dbg(pipe->isi->dev, "%s: IRQ AXI Error stat=0x%X\n",
721 			__func__, status);
722 
723 	if (status & (ier_reg->panic_y_buf_en.mask |
724 		      ier_reg->panic_u_buf_en.mask |
725 		      ier_reg->panic_v_buf_en.mask))
726 		dev_dbg(pipe->isi->dev, "%s: IRQ Panic OFLW Error stat=0x%X\n",
727 			__func__, status);
728 
729 	if (status & (ier_reg->oflw_y_buf_en.mask |
730 		      ier_reg->oflw_u_buf_en.mask |
731 		      ier_reg->oflw_v_buf_en.mask))
732 		dev_dbg(pipe->isi->dev, "%s: IRQ OFLW Error stat=0x%X\n",
733 			__func__, status);
734 
735 	if (status & (ier_reg->excs_oflw_y_buf_en.mask |
736 		      ier_reg->excs_oflw_u_buf_en.mask |
737 		      ier_reg->excs_oflw_v_buf_en.mask))
738 		dev_dbg(pipe->isi->dev, "%s: IRQ EXCS OFLW Error stat=0x%X\n",
739 			__func__, status);
740 
741 	return IRQ_HANDLED;
742 }
743 
744 /* -----------------------------------------------------------------------------
745  * Init & cleanup
746  */
747 
748 static const struct media_entity_operations mxc_isi_pipe_entity_ops = {
749 	.link_validate	= v4l2_subdev_link_validate,
750 };
751 
752 int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id)
753 {
754 	struct mxc_isi_pipe *pipe = &isi->pipes[id];
755 	struct v4l2_subdev *sd;
756 	int irq;
757 	int ret;
758 
759 	pipe->id = id;
760 	pipe->isi = isi;
761 	pipe->regs = isi->regs + id * isi->pdata->reg_offset;
762 
763 	mutex_init(&pipe->lock);
764 
765 	pipe->available_res = MXC_ISI_CHANNEL_RES_LINE_BUF
766 			    | MXC_ISI_CHANNEL_RES_OUTPUT_BUF;
767 	pipe->acquired_res = 0;
768 	pipe->chained_res = 0;
769 	pipe->chained = false;
770 
771 	sd = &pipe->sd;
772 	v4l2_subdev_init(sd, &mxc_isi_pipe_subdev_ops);
773 	sd->internal_ops = &mxc_isi_pipe_internal_ops;
774 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
775 	snprintf(sd->name, sizeof(sd->name), "mxc_isi.%d", pipe->id);
776 	sd->dev = isi->dev;
777 
778 	sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
779 	sd->entity.ops = &mxc_isi_pipe_entity_ops;
780 
781 	pipe->pads[MXC_ISI_PIPE_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
782 	pipe->pads[MXC_ISI_PIPE_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
783 
784 	ret = media_entity_pads_init(&sd->entity, MXC_ISI_PIPE_PADS_NUM,
785 				     pipe->pads);
786 	if (ret)
787 		goto error;
788 
789 	ret = v4l2_subdev_init_finalize(sd);
790 	if (ret < 0)
791 		goto error;
792 
793 	/* Register IRQ handler. */
794 	mxc_isi_channel_irq_clear(pipe);
795 
796 	irq = platform_get_irq(to_platform_device(isi->dev), id);
797 	if (irq < 0) {
798 		ret = irq;
799 		goto error;
800 	}
801 
802 	ret = devm_request_irq(isi->dev, irq, mxc_isi_pipe_irq_handler,
803 			       0, dev_name(isi->dev), pipe);
804 	if (ret < 0) {
805 		dev_err(isi->dev, "failed to request IRQ (%d)\n", ret);
806 		goto error;
807 	}
808 
809 	return 0;
810 
811 error:
812 	media_entity_cleanup(&sd->entity);
813 	mutex_destroy(&pipe->lock);
814 
815 	return ret;
816 }
817 
818 void mxc_isi_pipe_cleanup(struct mxc_isi_pipe *pipe)
819 {
820 	struct v4l2_subdev *sd = &pipe->sd;
821 
822 	media_entity_cleanup(&sd->entity);
823 	mutex_destroy(&pipe->lock);
824 }
825 
826 int mxc_isi_pipe_acquire(struct mxc_isi_pipe *pipe,
827 			 mxc_isi_pipe_irq_t irq_handler)
828 {
829 	const struct mxc_isi_bus_format_info *sink_info;
830 	const struct mxc_isi_bus_format_info *src_info;
831 	struct v4l2_mbus_framefmt *sink_fmt;
832 	const struct v4l2_mbus_framefmt *src_fmt;
833 	struct v4l2_subdev *sd = &pipe->sd;
834 	struct v4l2_subdev_state *state;
835 	bool bypass;
836 	int ret;
837 
838 	state = v4l2_subdev_lock_and_get_active_state(sd);
839 	sink_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SINK);
840 	src_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE);
841 	v4l2_subdev_unlock_state(state);
842 
843 	sink_info = mxc_isi_bus_format_by_code(sink_fmt->code,
844 					       MXC_ISI_PIPE_PAD_SINK);
845 	src_info = mxc_isi_bus_format_by_code(src_fmt->code,
846 					      MXC_ISI_PIPE_PAD_SOURCE);
847 
848 	bypass = sink_fmt->width == src_fmt->width &&
849 		 sink_fmt->height == src_fmt->height &&
850 		 sink_info->encoding == src_info->encoding;
851 
852 	ret = mxc_isi_channel_acquire(pipe, irq_handler, bypass);
853 	if (ret)
854 		return ret;
855 
856 	/* Chain the channel if needed for wide resolutions. */
857 	if (sink_fmt->width > MXC_ISI_MAX_WIDTH_UNCHAINED) {
858 		ret = mxc_isi_channel_chain(pipe, bypass);
859 		if (ret)
860 			mxc_isi_channel_release(pipe);
861 	}
862 
863 	return ret;
864 }
865 
866 void mxc_isi_pipe_release(struct mxc_isi_pipe *pipe)
867 {
868 	mxc_isi_channel_release(pipe);
869 	mxc_isi_channel_unchain(pipe);
870 }
871