xref: /openbmc/linux/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c (revision 46eeaa11bdd1bc9e077bdf741d32ca7235d263c6)
1cf21f328SLaurent Pinchart // SPDX-License-Identifier: GPL-2.0-only
2cf21f328SLaurent Pinchart /*
3cf21f328SLaurent Pinchart  * i.MX8 ISI - Input crossbar switch
4cf21f328SLaurent Pinchart  *
5cf21f328SLaurent Pinchart  * Copyright (c) 2022 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
6cf21f328SLaurent Pinchart  */
7cf21f328SLaurent Pinchart 
8cf21f328SLaurent Pinchart #include <linux/device.h>
9cf21f328SLaurent Pinchart #include <linux/errno.h>
10cf21f328SLaurent Pinchart #include <linux/kernel.h>
11cf21f328SLaurent Pinchart #include <linux/minmax.h>
12cf21f328SLaurent Pinchart #include <linux/regmap.h>
13cf21f328SLaurent Pinchart #include <linux/slab.h>
14cf21f328SLaurent Pinchart #include <linux/string.h>
15cf21f328SLaurent Pinchart #include <linux/types.h>
16cf21f328SLaurent Pinchart 
17cf21f328SLaurent Pinchart #include <media/media-entity.h>
18cf21f328SLaurent Pinchart #include <media/v4l2-subdev.h>
19cf21f328SLaurent Pinchart 
20cf21f328SLaurent Pinchart #include "imx8-isi-core.h"
21cf21f328SLaurent Pinchart 
to_isi_crossbar(struct v4l2_subdev * sd)22cf21f328SLaurent Pinchart static inline struct mxc_isi_crossbar *to_isi_crossbar(struct v4l2_subdev *sd)
23cf21f328SLaurent Pinchart {
24cf21f328SLaurent Pinchart 	return container_of(sd, struct mxc_isi_crossbar, sd);
25cf21f328SLaurent Pinchart }
26cf21f328SLaurent Pinchart 
mxc_isi_crossbar_gasket_enable(struct mxc_isi_crossbar * xbar,struct v4l2_subdev_state * state,struct v4l2_subdev * remote_sd,u32 remote_pad,unsigned int port)27cf21f328SLaurent Pinchart static int mxc_isi_crossbar_gasket_enable(struct mxc_isi_crossbar *xbar,
28cf21f328SLaurent Pinchart 					  struct v4l2_subdev_state *state,
29cf21f328SLaurent Pinchart 					  struct v4l2_subdev *remote_sd,
30cf21f328SLaurent Pinchart 					  u32 remote_pad, unsigned int port)
31cf21f328SLaurent Pinchart {
32cf21f328SLaurent Pinchart 	struct mxc_isi_dev *isi = xbar->isi;
33f48498adSGuoniu.zhou 	const struct mxc_gasket_ops *gasket_ops = isi->pdata->gasket_ops;
34cf21f328SLaurent Pinchart 	const struct v4l2_mbus_framefmt *fmt;
35cf21f328SLaurent Pinchart 	struct v4l2_mbus_frame_desc fd;
36cf21f328SLaurent Pinchart 	int ret;
37cf21f328SLaurent Pinchart 
38f48498adSGuoniu.zhou 	if (!gasket_ops)
39cf21f328SLaurent Pinchart 		return 0;
40cf21f328SLaurent Pinchart 
41cf21f328SLaurent Pinchart 	/*
42cf21f328SLaurent Pinchart 	 * Configure and enable the gasket with the frame size and CSI-2 data
43cf21f328SLaurent Pinchart 	 * type. For YUV422 8-bit, enable dual component mode unconditionally,
44cf21f328SLaurent Pinchart 	 * to match the configuration of the CSIS.
45cf21f328SLaurent Pinchart 	 */
46cf21f328SLaurent Pinchart 
47cf21f328SLaurent Pinchart 	ret = v4l2_subdev_call(remote_sd, pad, get_frame_desc, remote_pad, &fd);
48cf21f328SLaurent Pinchart 	if (ret) {
49cf21f328SLaurent Pinchart 		dev_err(isi->dev,
50cf21f328SLaurent Pinchart 			"failed to get frame descriptor from '%s':%u: %d\n",
51cf21f328SLaurent Pinchart 			remote_sd->name, remote_pad, ret);
52cf21f328SLaurent Pinchart 		return ret;
53cf21f328SLaurent Pinchart 	}
54cf21f328SLaurent Pinchart 
55cf21f328SLaurent Pinchart 	if (fd.num_entries != 1) {
56cf21f328SLaurent Pinchart 		dev_err(isi->dev, "invalid frame descriptor for '%s':%u\n",
57cf21f328SLaurent Pinchart 			remote_sd->name, remote_pad);
58cf21f328SLaurent Pinchart 		return -EINVAL;
59cf21f328SLaurent Pinchart 	}
60cf21f328SLaurent Pinchart 
61cf21f328SLaurent Pinchart 	fmt = v4l2_subdev_state_get_stream_format(state, port, 0);
62cf21f328SLaurent Pinchart 	if (!fmt)
63cf21f328SLaurent Pinchart 		return -EINVAL;
64cf21f328SLaurent Pinchart 
65f48498adSGuoniu.zhou 	gasket_ops->enable(isi, &fd, fmt, port);
66cf21f328SLaurent Pinchart 	return 0;
67cf21f328SLaurent Pinchart }
68cf21f328SLaurent Pinchart 
mxc_isi_crossbar_gasket_disable(struct mxc_isi_crossbar * xbar,unsigned int port)69cf21f328SLaurent Pinchart static void mxc_isi_crossbar_gasket_disable(struct mxc_isi_crossbar *xbar,
70cf21f328SLaurent Pinchart 					    unsigned int port)
71cf21f328SLaurent Pinchart {
72cf21f328SLaurent Pinchart 	struct mxc_isi_dev *isi = xbar->isi;
73f48498adSGuoniu.zhou 	const struct mxc_gasket_ops *gasket_ops = isi->pdata->gasket_ops;
74cf21f328SLaurent Pinchart 
75f48498adSGuoniu.zhou 	if (!gasket_ops)
76cf21f328SLaurent Pinchart 		return;
77cf21f328SLaurent Pinchart 
78f48498adSGuoniu.zhou 	gasket_ops->disable(isi, port);
79cf21f328SLaurent Pinchart }
80cf21f328SLaurent Pinchart 
81cf21f328SLaurent Pinchart /* -----------------------------------------------------------------------------
82cf21f328SLaurent Pinchart  * V4L2 subdev operations
83cf21f328SLaurent Pinchart  */
84cf21f328SLaurent Pinchart 
85cf21f328SLaurent Pinchart static const struct v4l2_mbus_framefmt mxc_isi_crossbar_default_format = {
86cf21f328SLaurent Pinchart 	.code = MXC_ISI_DEF_MBUS_CODE_SINK,
87cf21f328SLaurent Pinchart 	.width = MXC_ISI_DEF_WIDTH,
88cf21f328SLaurent Pinchart 	.height = MXC_ISI_DEF_HEIGHT,
89cf21f328SLaurent Pinchart 	.field = V4L2_FIELD_NONE,
90cf21f328SLaurent Pinchart 	.colorspace = MXC_ISI_DEF_COLOR_SPACE,
91cf21f328SLaurent Pinchart 	.ycbcr_enc = MXC_ISI_DEF_YCBCR_ENC,
92cf21f328SLaurent Pinchart 	.quantization = MXC_ISI_DEF_QUANTIZATION,
93cf21f328SLaurent Pinchart 	.xfer_func = MXC_ISI_DEF_XFER_FUNC,
94cf21f328SLaurent Pinchart };
95cf21f328SLaurent Pinchart 
__mxc_isi_crossbar_set_routing(struct v4l2_subdev * sd,struct v4l2_subdev_state * state,struct v4l2_subdev_krouting * routing)96cf21f328SLaurent Pinchart static int __mxc_isi_crossbar_set_routing(struct v4l2_subdev *sd,
97cf21f328SLaurent Pinchart 					  struct v4l2_subdev_state *state,
98cf21f328SLaurent Pinchart 					  struct v4l2_subdev_krouting *routing)
99cf21f328SLaurent Pinchart {
100cf21f328SLaurent Pinchart 	struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
101cf21f328SLaurent Pinchart 	struct v4l2_subdev_route *route;
102cf21f328SLaurent Pinchart 	int ret;
103cf21f328SLaurent Pinchart 
104cf21f328SLaurent Pinchart 	ret = v4l2_subdev_routing_validate(sd, routing,
105cf21f328SLaurent Pinchart 					   V4L2_SUBDEV_ROUTING_NO_N_TO_1);
106cf21f328SLaurent Pinchart 	if (ret)
107cf21f328SLaurent Pinchart 		return ret;
108cf21f328SLaurent Pinchart 
109cf21f328SLaurent Pinchart 	/* The memory input can be routed to the first pipeline only. */
110cf21f328SLaurent Pinchart 	for_each_active_route(&state->routing, route) {
111cf21f328SLaurent Pinchart 		if (route->sink_pad == xbar->num_sinks - 1 &&
112cf21f328SLaurent Pinchart 		    route->source_pad != xbar->num_sinks) {
113cf21f328SLaurent Pinchart 			dev_dbg(xbar->isi->dev,
114cf21f328SLaurent Pinchart 				"invalid route from memory input (%u) to pipe %u\n",
115cf21f328SLaurent Pinchart 				route->sink_pad,
116cf21f328SLaurent Pinchart 				route->source_pad - xbar->num_sinks);
117cf21f328SLaurent Pinchart 			return -EINVAL;
118cf21f328SLaurent Pinchart 		}
119cf21f328SLaurent Pinchart 	}
120cf21f328SLaurent Pinchart 
121cf21f328SLaurent Pinchart 	return v4l2_subdev_set_routing_with_fmt(sd, state, routing,
122cf21f328SLaurent Pinchart 						&mxc_isi_crossbar_default_format);
123cf21f328SLaurent Pinchart }
124cf21f328SLaurent Pinchart 
125cf21f328SLaurent Pinchart static struct v4l2_subdev *
mxc_isi_crossbar_xlate_streams(struct mxc_isi_crossbar * xbar,struct v4l2_subdev_state * state,u32 source_pad,u64 source_streams,u32 * __sink_pad,u64 * __sink_streams,u32 * remote_pad)126cf21f328SLaurent Pinchart mxc_isi_crossbar_xlate_streams(struct mxc_isi_crossbar *xbar,
127cf21f328SLaurent Pinchart 			       struct v4l2_subdev_state *state,
128cf21f328SLaurent Pinchart 			       u32 source_pad, u64 source_streams,
129cf21f328SLaurent Pinchart 			       u32 *__sink_pad, u64 *__sink_streams,
130cf21f328SLaurent Pinchart 			       u32 *remote_pad)
131cf21f328SLaurent Pinchart {
132cf21f328SLaurent Pinchart 	struct v4l2_subdev_route *route;
133cf21f328SLaurent Pinchart 	struct v4l2_subdev *sd;
134cf21f328SLaurent Pinchart 	struct media_pad *pad;
135cf21f328SLaurent Pinchart 	u64 sink_streams = 0;
136cf21f328SLaurent Pinchart 	int sink_pad = -1;
137cf21f328SLaurent Pinchart 
138cf21f328SLaurent Pinchart 	/*
139cf21f328SLaurent Pinchart 	 * Translate the source pad and streams to the sink side. The routing
140cf21f328SLaurent Pinchart 	 * validation forbids stream merging, so all matching entries in the
141cf21f328SLaurent Pinchart 	 * routing table are guaranteed to have the same sink pad.
142cf21f328SLaurent Pinchart 	 *
143cf21f328SLaurent Pinchart 	 * TODO: This is likely worth a helper function, it could perhaps be
144cf21f328SLaurent Pinchart 	 * supported by v4l2_subdev_state_xlate_streams() with pad1 set to -1.
145cf21f328SLaurent Pinchart 	 */
146cf21f328SLaurent Pinchart 	for_each_active_route(&state->routing, route) {
147cf21f328SLaurent Pinchart 		if (route->source_pad != source_pad ||
148cf21f328SLaurent Pinchart 		    !(source_streams & BIT(route->source_stream)))
149cf21f328SLaurent Pinchart 			continue;
150cf21f328SLaurent Pinchart 
151cf21f328SLaurent Pinchart 		sink_streams |= BIT(route->sink_stream);
152cf21f328SLaurent Pinchart 		sink_pad = route->sink_pad;
153cf21f328SLaurent Pinchart 	}
154cf21f328SLaurent Pinchart 
155cf21f328SLaurent Pinchart 	if (sink_pad < 0) {
156cf21f328SLaurent Pinchart 		dev_dbg(xbar->isi->dev,
157cf21f328SLaurent Pinchart 			"no stream connected to pipeline %u\n",
158cf21f328SLaurent Pinchart 			source_pad - xbar->num_sinks);
159cf21f328SLaurent Pinchart 		return ERR_PTR(-EPIPE);
160cf21f328SLaurent Pinchart 	}
161cf21f328SLaurent Pinchart 
162cf21f328SLaurent Pinchart 	pad = media_pad_remote_pad_first(&xbar->pads[sink_pad]);
163c9531860SMarek Vasut 	sd = media_entity_to_v4l2_subdev(pad->entity);
164cf21f328SLaurent Pinchart 	if (!sd) {
165cf21f328SLaurent Pinchart 		dev_dbg(xbar->isi->dev,
166cf21f328SLaurent Pinchart 			"no entity connected to crossbar input %u\n",
167cf21f328SLaurent Pinchart 			sink_pad);
168cf21f328SLaurent Pinchart 		return ERR_PTR(-EPIPE);
169cf21f328SLaurent Pinchart 	}
170cf21f328SLaurent Pinchart 
171cf21f328SLaurent Pinchart 	*__sink_pad = sink_pad;
172cf21f328SLaurent Pinchart 	*__sink_streams = sink_streams;
173cf21f328SLaurent Pinchart 	*remote_pad = pad->index;
174cf21f328SLaurent Pinchart 
175cf21f328SLaurent Pinchart 	return sd;
176cf21f328SLaurent Pinchart }
177cf21f328SLaurent Pinchart 
mxc_isi_crossbar_init_cfg(struct v4l2_subdev * sd,struct v4l2_subdev_state * state)178cf21f328SLaurent Pinchart static int mxc_isi_crossbar_init_cfg(struct v4l2_subdev *sd,
179cf21f328SLaurent Pinchart 				     struct v4l2_subdev_state *state)
180cf21f328SLaurent Pinchart {
181cf21f328SLaurent Pinchart 	struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
182cf21f328SLaurent Pinchart 	struct v4l2_subdev_krouting routing = { };
183cf21f328SLaurent Pinchart 	struct v4l2_subdev_route *routes;
184cf21f328SLaurent Pinchart 	unsigned int i;
185cf21f328SLaurent Pinchart 	int ret;
186cf21f328SLaurent Pinchart 
187cf21f328SLaurent Pinchart 	/*
188cf21f328SLaurent Pinchart 	 * Create a 1:1 mapping between pixel link inputs and outputs to
189cf21f328SLaurent Pinchart 	 * pipelines by default.
190cf21f328SLaurent Pinchart 	 */
191cf21f328SLaurent Pinchart 	routes = kcalloc(xbar->num_sources, sizeof(*routes), GFP_KERNEL);
192cf21f328SLaurent Pinchart 	if (!routes)
193cf21f328SLaurent Pinchart 		return -ENOMEM;
194cf21f328SLaurent Pinchart 
195cf21f328SLaurent Pinchart 	for (i = 0; i < xbar->num_sources; ++i) {
196cf21f328SLaurent Pinchart 		struct v4l2_subdev_route *route = &routes[i];
197cf21f328SLaurent Pinchart 
198cf21f328SLaurent Pinchart 		route->sink_pad = i;
199cf21f328SLaurent Pinchart 		route->source_pad = i + xbar->num_sinks;
200cf21f328SLaurent Pinchart 		route->flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE;
201e01c3ec7SJiapeng Chong 	}
202cf21f328SLaurent Pinchart 
203cf21f328SLaurent Pinchart 	routing.num_routes = xbar->num_sources;
204cf21f328SLaurent Pinchart 	routing.routes = routes;
205cf21f328SLaurent Pinchart 
206cf21f328SLaurent Pinchart 	ret = __mxc_isi_crossbar_set_routing(sd, state, &routing);
207cf21f328SLaurent Pinchart 
208cf21f328SLaurent Pinchart 	kfree(routes);
209cf21f328SLaurent Pinchart 
210cf21f328SLaurent Pinchart 	return ret;
211cf21f328SLaurent Pinchart }
212cf21f328SLaurent Pinchart 
mxc_isi_crossbar_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * state,struct v4l2_subdev_mbus_code_enum * code)213cf21f328SLaurent Pinchart static int mxc_isi_crossbar_enum_mbus_code(struct v4l2_subdev *sd,
214cf21f328SLaurent Pinchart 					   struct v4l2_subdev_state *state,
215cf21f328SLaurent Pinchart 					   struct v4l2_subdev_mbus_code_enum *code)
216cf21f328SLaurent Pinchart {
217cf21f328SLaurent Pinchart 	struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
218cf21f328SLaurent Pinchart 	const struct mxc_isi_bus_format_info *info;
219cf21f328SLaurent Pinchart 
220cf21f328SLaurent Pinchart 	if (code->pad >= xbar->num_sinks) {
221cf21f328SLaurent Pinchart 		const struct v4l2_mbus_framefmt *format;
222cf21f328SLaurent Pinchart 
223cf21f328SLaurent Pinchart 		/*
224cf21f328SLaurent Pinchart 		 * The media bus code on source pads is identical to the
225cf21f328SLaurent Pinchart 		 * connected sink pad.
226cf21f328SLaurent Pinchart 		 */
227cf21f328SLaurent Pinchart 		if (code->index > 0)
228cf21f328SLaurent Pinchart 			return -EINVAL;
229cf21f328SLaurent Pinchart 
230cf21f328SLaurent Pinchart 		format = v4l2_subdev_state_get_opposite_stream_format(state,
231cf21f328SLaurent Pinchart 								      code->pad,
232cf21f328SLaurent Pinchart 								      code->stream);
233cf21f328SLaurent Pinchart 		if (!format)
234cf21f328SLaurent Pinchart 			return -EINVAL;
235cf21f328SLaurent Pinchart 
236cf21f328SLaurent Pinchart 		code->code = format->code;
237cf21f328SLaurent Pinchart 
238cf21f328SLaurent Pinchart 		return 0;
239cf21f328SLaurent Pinchart 	}
240cf21f328SLaurent Pinchart 
241cf21f328SLaurent Pinchart 	info = mxc_isi_bus_format_by_index(code->index, MXC_ISI_PIPE_PAD_SINK);
242cf21f328SLaurent Pinchart 	if (!info)
243cf21f328SLaurent Pinchart 		return -EINVAL;
244cf21f328SLaurent Pinchart 
245cf21f328SLaurent Pinchart 	code->code = info->mbus_code;
246cf21f328SLaurent Pinchart 
247cf21f328SLaurent Pinchart 	return 0;
248cf21f328SLaurent Pinchart }
249cf21f328SLaurent Pinchart 
mxc_isi_crossbar_set_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * state,struct v4l2_subdev_format * fmt)250cf21f328SLaurent Pinchart static int mxc_isi_crossbar_set_fmt(struct v4l2_subdev *sd,
251cf21f328SLaurent Pinchart 				    struct v4l2_subdev_state *state,
252cf21f328SLaurent Pinchart 				    struct v4l2_subdev_format *fmt)
253cf21f328SLaurent Pinchart {
254cf21f328SLaurent Pinchart 	struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
255cf21f328SLaurent Pinchart 	struct v4l2_mbus_framefmt *sink_fmt;
256cf21f328SLaurent Pinchart 	struct v4l2_subdev_route *route;
257cf21f328SLaurent Pinchart 
258cf21f328SLaurent Pinchart 	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE &&
259cf21f328SLaurent Pinchart 	    media_pad_is_streaming(&xbar->pads[fmt->pad]))
260cf21f328SLaurent Pinchart 		return -EBUSY;
261cf21f328SLaurent Pinchart 
262cf21f328SLaurent Pinchart 	/*
263cf21f328SLaurent Pinchart 	 * The source pad format is always identical to the sink pad format and
264cf21f328SLaurent Pinchart 	 * can't be modified.
265cf21f328SLaurent Pinchart 	 */
266cf21f328SLaurent Pinchart 	if (fmt->pad >= xbar->num_sinks)
267cf21f328SLaurent Pinchart 		return v4l2_subdev_get_fmt(sd, state, fmt);
268cf21f328SLaurent Pinchart 
269cf21f328SLaurent Pinchart 	/* Validate the requested format. */
270cf21f328SLaurent Pinchart 	if (!mxc_isi_bus_format_by_code(fmt->format.code, MXC_ISI_PIPE_PAD_SINK))
271cf21f328SLaurent Pinchart 		fmt->format.code = MXC_ISI_DEF_MBUS_CODE_SINK;
272cf21f328SLaurent Pinchart 
273cf21f328SLaurent Pinchart 	fmt->format.width = clamp_t(unsigned int, fmt->format.width,
274cf21f328SLaurent Pinchart 				    MXC_ISI_MIN_WIDTH, MXC_ISI_MAX_WIDTH_CHAINED);
275cf21f328SLaurent Pinchart 	fmt->format.height = clamp_t(unsigned int, fmt->format.height,
276cf21f328SLaurent Pinchart 				     MXC_ISI_MIN_HEIGHT, MXC_ISI_MAX_HEIGHT);
277cf21f328SLaurent Pinchart 	fmt->format.field = V4L2_FIELD_NONE;
278cf21f328SLaurent Pinchart 
279cf21f328SLaurent Pinchart 	/*
280cf21f328SLaurent Pinchart 	 * Set the format on the sink stream and propagate it to the source
281cf21f328SLaurent Pinchart 	 * streams.
282cf21f328SLaurent Pinchart 	 */
283cf21f328SLaurent Pinchart 	sink_fmt = v4l2_subdev_state_get_stream_format(state, fmt->pad,
284cf21f328SLaurent Pinchart 						       fmt->stream);
285cf21f328SLaurent Pinchart 	if (!sink_fmt)
286cf21f328SLaurent Pinchart 		return -EINVAL;
287cf21f328SLaurent Pinchart 
288cf21f328SLaurent Pinchart 	*sink_fmt = fmt->format;
289cf21f328SLaurent Pinchart 
290cf21f328SLaurent Pinchart 	/* TODO: A format propagation helper would be useful. */
291cf21f328SLaurent Pinchart 	for_each_active_route(&state->routing, route) {
292cf21f328SLaurent Pinchart 		struct v4l2_mbus_framefmt *source_fmt;
293cf21f328SLaurent Pinchart 
294cf21f328SLaurent Pinchart 		if (route->sink_pad != fmt->pad ||
295cf21f328SLaurent Pinchart 		    route->sink_stream != fmt->stream)
296cf21f328SLaurent Pinchart 			continue;
297cf21f328SLaurent Pinchart 
298cf21f328SLaurent Pinchart 		source_fmt = v4l2_subdev_state_get_stream_format(state, route->source_pad,
299cf21f328SLaurent Pinchart 								 route->source_stream);
300cf21f328SLaurent Pinchart 		if (!source_fmt)
301cf21f328SLaurent Pinchart 			return -EINVAL;
302cf21f328SLaurent Pinchart 
303cf21f328SLaurent Pinchart 		*source_fmt = fmt->format;
304cf21f328SLaurent Pinchart 	}
305cf21f328SLaurent Pinchart 
306cf21f328SLaurent Pinchart 	return 0;
307cf21f328SLaurent Pinchart }
308cf21f328SLaurent Pinchart 
mxc_isi_crossbar_set_routing(struct v4l2_subdev * sd,struct v4l2_subdev_state * state,enum v4l2_subdev_format_whence which,struct v4l2_subdev_krouting * routing)309cf21f328SLaurent Pinchart static int mxc_isi_crossbar_set_routing(struct v4l2_subdev *sd,
310cf21f328SLaurent Pinchart 					struct v4l2_subdev_state *state,
311cf21f328SLaurent Pinchart 					enum v4l2_subdev_format_whence which,
312cf21f328SLaurent Pinchart 					struct v4l2_subdev_krouting *routing)
313cf21f328SLaurent Pinchart {
314cf21f328SLaurent Pinchart 	if (which == V4L2_SUBDEV_FORMAT_ACTIVE &&
315cf21f328SLaurent Pinchart 	    media_entity_is_streaming(&sd->entity))
316cf21f328SLaurent Pinchart 		return -EBUSY;
317cf21f328SLaurent Pinchart 
318cf21f328SLaurent Pinchart 	return __mxc_isi_crossbar_set_routing(sd, state, routing);
319cf21f328SLaurent Pinchart }
320cf21f328SLaurent Pinchart 
mxc_isi_crossbar_enable_streams(struct v4l2_subdev * sd,struct v4l2_subdev_state * state,u32 pad,u64 streams_mask)321cf21f328SLaurent Pinchart static int mxc_isi_crossbar_enable_streams(struct v4l2_subdev *sd,
322cf21f328SLaurent Pinchart 					   struct v4l2_subdev_state *state,
323cf21f328SLaurent Pinchart 					   u32 pad, u64 streams_mask)
324cf21f328SLaurent Pinchart {
325cf21f328SLaurent Pinchart 	struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
326cf21f328SLaurent Pinchart 	struct v4l2_subdev *remote_sd;
327cf21f328SLaurent Pinchart 	struct mxc_isi_input *input;
328cf21f328SLaurent Pinchart 	u64 sink_streams;
329cf21f328SLaurent Pinchart 	u32 sink_pad;
330cf21f328SLaurent Pinchart 	u32 remote_pad;
331cf21f328SLaurent Pinchart 	int ret;
332cf21f328SLaurent Pinchart 
333cf21f328SLaurent Pinchart 	remote_sd = mxc_isi_crossbar_xlate_streams(xbar, state, pad, streams_mask,
334cf21f328SLaurent Pinchart 						   &sink_pad, &sink_streams,
335cf21f328SLaurent Pinchart 						   &remote_pad);
336cf21f328SLaurent Pinchart 	if (IS_ERR(remote_sd))
337cf21f328SLaurent Pinchart 		return PTR_ERR(remote_sd);
338cf21f328SLaurent Pinchart 
339cf21f328SLaurent Pinchart 	input = &xbar->inputs[sink_pad];
340cf21f328SLaurent Pinchart 
341cf21f328SLaurent Pinchart 	/*
342cf21f328SLaurent Pinchart 	 * TODO: Track per-stream enable counts to support multiplexed
343cf21f328SLaurent Pinchart 	 * streams.
344cf21f328SLaurent Pinchart 	 */
345cf21f328SLaurent Pinchart 	if (!input->enable_count) {
346cf21f328SLaurent Pinchart 		ret = mxc_isi_crossbar_gasket_enable(xbar, state, remote_sd,
347cf21f328SLaurent Pinchart 						     remote_pad, sink_pad);
348cf21f328SLaurent Pinchart 		if (ret)
349cf21f328SLaurent Pinchart 			return ret;
350cf21f328SLaurent Pinchart 
351cf21f328SLaurent Pinchart 		ret = v4l2_subdev_enable_streams(remote_sd, remote_pad,
352cf21f328SLaurent Pinchart 						 sink_streams);
353cf21f328SLaurent Pinchart 		if (ret) {
354cf21f328SLaurent Pinchart 			dev_err(xbar->isi->dev,
355cf21f328SLaurent Pinchart 				"failed to %s streams 0x%llx on '%s':%u: %d\n",
356cf21f328SLaurent Pinchart 				"enable", sink_streams, remote_sd->name,
357cf21f328SLaurent Pinchart 				remote_pad, ret);
358cf21f328SLaurent Pinchart 			mxc_isi_crossbar_gasket_disable(xbar, sink_pad);
359cf21f328SLaurent Pinchart 			return ret;
360cf21f328SLaurent Pinchart 		}
361cf21f328SLaurent Pinchart 	}
362cf21f328SLaurent Pinchart 
363cf21f328SLaurent Pinchart 	input->enable_count++;
364cf21f328SLaurent Pinchart 
365cf21f328SLaurent Pinchart 	return 0;
366cf21f328SLaurent Pinchart }
367cf21f328SLaurent Pinchart 
mxc_isi_crossbar_disable_streams(struct v4l2_subdev * sd,struct v4l2_subdev_state * state,u32 pad,u64 streams_mask)368cf21f328SLaurent Pinchart static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd,
369cf21f328SLaurent Pinchart 					    struct v4l2_subdev_state *state,
370cf21f328SLaurent Pinchart 					    u32 pad, u64 streams_mask)
371cf21f328SLaurent Pinchart {
372cf21f328SLaurent Pinchart 	struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
373cf21f328SLaurent Pinchart 	struct v4l2_subdev *remote_sd;
374cf21f328SLaurent Pinchart 	struct mxc_isi_input *input;
375cf21f328SLaurent Pinchart 	u64 sink_streams;
376cf21f328SLaurent Pinchart 	u32 sink_pad;
377cf21f328SLaurent Pinchart 	u32 remote_pad;
378cf21f328SLaurent Pinchart 	int ret = 0;
379cf21f328SLaurent Pinchart 
380cf21f328SLaurent Pinchart 	remote_sd = mxc_isi_crossbar_xlate_streams(xbar, state, pad, streams_mask,
381cf21f328SLaurent Pinchart 						   &sink_pad, &sink_streams,
382cf21f328SLaurent Pinchart 						   &remote_pad);
383cf21f328SLaurent Pinchart 	if (IS_ERR(remote_sd))
384cf21f328SLaurent Pinchart 		return PTR_ERR(remote_sd);
385cf21f328SLaurent Pinchart 
386cf21f328SLaurent Pinchart 	input = &xbar->inputs[sink_pad];
387cf21f328SLaurent Pinchart 
388cf21f328SLaurent Pinchart 	input->enable_count--;
389cf21f328SLaurent Pinchart 
390cf21f328SLaurent Pinchart 	if (!input->enable_count) {
391cf21f328SLaurent Pinchart 		ret = v4l2_subdev_disable_streams(remote_sd, remote_pad,
392cf21f328SLaurent Pinchart 						  sink_streams);
393cf21f328SLaurent Pinchart 		if (ret)
394cf21f328SLaurent Pinchart 			dev_err(xbar->isi->dev,
395cf21f328SLaurent Pinchart 				"failed to %s streams 0x%llx on '%s':%u: %d\n",
396cf21f328SLaurent Pinchart 				"disable", sink_streams, remote_sd->name,
397cf21f328SLaurent Pinchart 				remote_pad, ret);
398cf21f328SLaurent Pinchart 
399cf21f328SLaurent Pinchart 		mxc_isi_crossbar_gasket_disable(xbar, sink_pad);
400cf21f328SLaurent Pinchart 	}
401cf21f328SLaurent Pinchart 
402cf21f328SLaurent Pinchart 	return ret;
403cf21f328SLaurent Pinchart }
404cf21f328SLaurent Pinchart 
405cf21f328SLaurent Pinchart static const struct v4l2_subdev_pad_ops mxc_isi_crossbar_subdev_pad_ops = {
406cf21f328SLaurent Pinchart 	.init_cfg = mxc_isi_crossbar_init_cfg,
407cf21f328SLaurent Pinchart 	.enum_mbus_code = mxc_isi_crossbar_enum_mbus_code,
408cf21f328SLaurent Pinchart 	.get_fmt = v4l2_subdev_get_fmt,
409cf21f328SLaurent Pinchart 	.set_fmt = mxc_isi_crossbar_set_fmt,
410cf21f328SLaurent Pinchart 	.set_routing = mxc_isi_crossbar_set_routing,
411cf21f328SLaurent Pinchart 	.enable_streams = mxc_isi_crossbar_enable_streams,
412cf21f328SLaurent Pinchart 	.disable_streams = mxc_isi_crossbar_disable_streams,
413cf21f328SLaurent Pinchart };
414cf21f328SLaurent Pinchart 
415cf21f328SLaurent Pinchart static const struct v4l2_subdev_ops mxc_isi_crossbar_subdev_ops = {
416cf21f328SLaurent Pinchart 	.pad = &mxc_isi_crossbar_subdev_pad_ops,
417cf21f328SLaurent Pinchart };
418cf21f328SLaurent Pinchart 
419cf21f328SLaurent Pinchart static const struct media_entity_operations mxc_isi_cross_entity_ops = {
420cf21f328SLaurent Pinchart 	.get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,
421cf21f328SLaurent Pinchart 	.link_validate	= v4l2_subdev_link_validate,
422cf21f328SLaurent Pinchart 	.has_pad_interdep = v4l2_subdev_has_pad_interdep,
423cf21f328SLaurent Pinchart };
424cf21f328SLaurent Pinchart 
425cf21f328SLaurent Pinchart /* -----------------------------------------------------------------------------
426cf21f328SLaurent Pinchart  * Init & cleanup
427cf21f328SLaurent Pinchart  */
428cf21f328SLaurent Pinchart 
mxc_isi_crossbar_init(struct mxc_isi_dev * isi)429cf21f328SLaurent Pinchart int mxc_isi_crossbar_init(struct mxc_isi_dev *isi)
430cf21f328SLaurent Pinchart {
431cf21f328SLaurent Pinchart 	struct mxc_isi_crossbar *xbar = &isi->crossbar;
432cf21f328SLaurent Pinchart 	struct v4l2_subdev *sd = &xbar->sd;
433cf21f328SLaurent Pinchart 	unsigned int num_pads;
434cf21f328SLaurent Pinchart 	unsigned int i;
435cf21f328SLaurent Pinchart 	int ret;
436cf21f328SLaurent Pinchart 
437cf21f328SLaurent Pinchart 	xbar->isi = isi;
438cf21f328SLaurent Pinchart 
439cf21f328SLaurent Pinchart 	v4l2_subdev_init(sd, &mxc_isi_crossbar_subdev_ops);
440cf21f328SLaurent Pinchart 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
441cf21f328SLaurent Pinchart 	strscpy(sd->name, "crossbar", sizeof(sd->name));
442cf21f328SLaurent Pinchart 	sd->dev = isi->dev;
443cf21f328SLaurent Pinchart 
444cf21f328SLaurent Pinchart 	sd->entity.function = MEDIA_ENT_F_VID_MUX;
445cf21f328SLaurent Pinchart 	sd->entity.ops = &mxc_isi_cross_entity_ops;
446cf21f328SLaurent Pinchart 
447cf21f328SLaurent Pinchart 	/*
448cf21f328SLaurent Pinchart 	 * The subdev has one sink and one source per port, plus one sink for
449cf21f328SLaurent Pinchart 	 * the memory input.
450cf21f328SLaurent Pinchart 	 */
451cf21f328SLaurent Pinchart 	xbar->num_sinks = isi->pdata->num_ports + 1;
452cf21f328SLaurent Pinchart 	xbar->num_sources = isi->pdata->num_ports;
453cf21f328SLaurent Pinchart 	num_pads = xbar->num_sinks + xbar->num_sources;
454cf21f328SLaurent Pinchart 
455cf21f328SLaurent Pinchart 	xbar->pads = kcalloc(num_pads, sizeof(*xbar->pads), GFP_KERNEL);
456cf21f328SLaurent Pinchart 	if (!xbar->pads)
457cf21f328SLaurent Pinchart 		return -ENOMEM;
458cf21f328SLaurent Pinchart 
459cf21f328SLaurent Pinchart 	xbar->inputs = kcalloc(xbar->num_sinks, sizeof(*xbar->inputs),
460cf21f328SLaurent Pinchart 			       GFP_KERNEL);
4614b60db99SYang Yingliang 	if (!xbar->inputs) {
462cf21f328SLaurent Pinchart 		ret = -ENOMEM;
463cf21f328SLaurent Pinchart 		goto err_free;
464cf21f328SLaurent Pinchart 	}
465cf21f328SLaurent Pinchart 
466cf21f328SLaurent Pinchart 	for (i = 0; i < xbar->num_sinks; ++i)
467*88efc309SLaurent Pinchart 		xbar->pads[i].flags = MEDIA_PAD_FL_SINK
468*88efc309SLaurent Pinchart 				    | MEDIA_PAD_FL_MUST_CONNECT;
469cf21f328SLaurent Pinchart 	for (i = 0; i < xbar->num_sources; ++i)
470cf21f328SLaurent Pinchart 		xbar->pads[i + xbar->num_sinks].flags = MEDIA_PAD_FL_SOURCE;
471cf21f328SLaurent Pinchart 
472cf21f328SLaurent Pinchart 	ret = media_entity_pads_init(&sd->entity, num_pads, xbar->pads);
473cf21f328SLaurent Pinchart 	if (ret)
474cf21f328SLaurent Pinchart 		goto err_free;
475cf21f328SLaurent Pinchart 
476cf21f328SLaurent Pinchart 	ret = v4l2_subdev_init_finalize(sd);
477cf21f328SLaurent Pinchart 	if (ret < 0)
478cf21f328SLaurent Pinchart 		goto err_entity;
479cf21f328SLaurent Pinchart 
480cf21f328SLaurent Pinchart 	return 0;
481cf21f328SLaurent Pinchart 
482cf21f328SLaurent Pinchart err_entity:
483cf21f328SLaurent Pinchart 	media_entity_cleanup(&sd->entity);
484cf21f328SLaurent Pinchart err_free:
485cf21f328SLaurent Pinchart 	kfree(xbar->pads);
486cf21f328SLaurent Pinchart 	kfree(xbar->inputs);
487cf21f328SLaurent Pinchart 
488cf21f328SLaurent Pinchart 	return ret;
489cf21f328SLaurent Pinchart }
490cf21f328SLaurent Pinchart 
mxc_isi_crossbar_cleanup(struct mxc_isi_crossbar * xbar)491cf21f328SLaurent Pinchart void mxc_isi_crossbar_cleanup(struct mxc_isi_crossbar *xbar)
492cf21f328SLaurent Pinchart {
493cf21f328SLaurent Pinchart 	media_entity_cleanup(&xbar->sd.entity);
494cf21f328SLaurent Pinchart 	kfree(xbar->pads);
495cf21f328SLaurent Pinchart 	kfree(xbar->inputs);
496cf21f328SLaurent Pinchart }
497cf21f328SLaurent Pinchart 
mxc_isi_crossbar_register(struct mxc_isi_crossbar * xbar)498cf21f328SLaurent Pinchart int mxc_isi_crossbar_register(struct mxc_isi_crossbar *xbar)
499cf21f328SLaurent Pinchart {
500cf21f328SLaurent Pinchart 	return v4l2_device_register_subdev(&xbar->isi->v4l2_dev, &xbar->sd);
501cf21f328SLaurent Pinchart }
502cf21f328SLaurent Pinchart 
mxc_isi_crossbar_unregister(struct mxc_isi_crossbar * xbar)503cf21f328SLaurent Pinchart void mxc_isi_crossbar_unregister(struct mxc_isi_crossbar *xbar)
504cf21f328SLaurent Pinchart {
505cf21f328SLaurent Pinchart }
506