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 */
35static 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
193const struct mxc_isi_bus_format_info *
194mxc_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
209const struct mxc_isi_bus_format_info *
210mxc_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
230static 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
235int 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
302void 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
320static struct v4l2_mbus_framefmt *
321mxc_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
328static struct v4l2_rect *
329mxc_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
336static struct v4l2_rect *
337mxc_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
344static 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
386static 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
450static 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
545static 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
604static 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
684static 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
693static 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
701static 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
745static const struct media_entity_operations mxc_isi_pipe_entity_ops = {
746	.link_validate	= v4l2_subdev_link_validate,
747};
748
749int 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
807error:
808	media_entity_cleanup(&sd->entity);
809	mutex_destroy(&pipe->lock);
810
811	return ret;
812}
813
814void 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
822int 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
862void mxc_isi_pipe_release(struct mxc_isi_pipe *pipe)
863{
864	mxc_isi_channel_release(pipe);
865	mxc_isi_channel_unchain(pipe);
866}
867