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_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
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_state_get_format(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_state_get_crop(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_state_get_compose(state, pad);
342}
343
344static 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
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	.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
692static const struct v4l2_subdev_ops mxc_isi_pipe_subdev_ops = {
693	.pad = &mxc_isi_pipe_subdev_pad_ops,
694};
695
696static 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
704static 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
748static const struct media_entity_operations mxc_isi_pipe_entity_ops = {
749	.link_validate	= v4l2_subdev_link_validate,
750};
751
752int 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
811error:
812	media_entity_cleanup(&sd->entity);
813	mutex_destroy(&pipe->lock);
814
815	return ret;
816}
817
818void 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
826int 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
866void mxc_isi_pipe_release(struct mxc_isi_pipe *pipe)
867{
868	mxc_isi_channel_release(pipe);
869	mxc_isi_channel_unchain(pipe);
870}
871