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