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