1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include <linux/device.h>
16#include <linux/module.h>
17#include <linux/of.h>
18#include <linux/platform_device.h>
19
20#include <media/media-device.h>
21#include <media/v4l2-async.h>
22#include <media/v4l2-subdev.h>
23
24#include "xilinx-vip.h"
25
26#define XSW_CORE_CH_CTRL 0x0100
27#define XSW_CORE_CH_CTRL_FORCE (1 << 3)
28
29#define XSW_SWITCH_STATUS 0x0104
30
31
32
33
34
35
36
37
38
39
40struct xswitch_device {
41 struct xvip_device xvip;
42
43 struct media_pad *pads;
44 unsigned int nsinks;
45 unsigned int nsources;
46
47 int routing[8];
48
49 struct v4l2_mbus_framefmt *formats;
50};
51
52static inline struct xswitch_device *to_xsw(struct v4l2_subdev *subdev)
53{
54 return container_of(subdev, struct xswitch_device, xvip.subdev);
55}
56
57
58
59
60
61static int xsw_s_stream(struct v4l2_subdev *subdev, int enable)
62{
63 struct xswitch_device *xsw = to_xsw(subdev);
64 unsigned int unused_input;
65 unsigned int i;
66 u32 routing;
67
68 if (!enable) {
69 xvip_stop(&xsw->xvip);
70 return 0;
71 }
72
73
74
75
76
77
78 if (xsw->nsinks == 8) {
79 u32 mask;
80
81 for (i = 0, mask = 0xff; i < xsw->nsources; ++i) {
82 if (xsw->routing[i] != -1)
83 mask &= ~BIT(xsw->routing[i]);
84 }
85
86
87
88
89
90 unused_input = mask ? ffs(mask) - 1 : 0;
91 } else {
92 unused_input = 7;
93 }
94
95
96 for (i = 0, routing = 0; i < xsw->nsources; ++i) {
97 unsigned int route;
98
99 route = xsw->routing[i] == -1 ? unused_input : xsw->routing[i];
100 routing |= (XSW_CORE_CH_CTRL_FORCE | route)
101 << (i * 4);
102 }
103
104 xvip_write(&xsw->xvip, XSW_CORE_CH_CTRL, routing);
105
106 xvip_write(&xsw->xvip, XVIP_CTRL_CONTROL,
107 (((1 << xsw->nsources) - 1) << 4) |
108 XVIP_CTRL_CONTROL_SW_ENABLE);
109
110 return 0;
111}
112
113
114
115
116
117static struct v4l2_mbus_framefmt *
118xsw_get_pad_format(struct xswitch_device *xsw,
119 struct v4l2_subdev_pad_config *cfg,
120 unsigned int pad, u32 which)
121{
122 switch (which) {
123 case V4L2_SUBDEV_FORMAT_TRY:
124 return v4l2_subdev_get_try_format(&xsw->xvip.subdev, cfg, pad);
125 case V4L2_SUBDEV_FORMAT_ACTIVE:
126 return &xsw->formats[pad];
127 default:
128 return NULL;
129 }
130}
131
132static int xsw_get_format(struct v4l2_subdev *subdev,
133 struct v4l2_subdev_pad_config *cfg,
134 struct v4l2_subdev_format *fmt)
135{
136 struct xswitch_device *xsw = to_xsw(subdev);
137 int pad = fmt->pad;
138
139 if (pad >= xsw->nsinks) {
140 pad = xsw->routing[pad - xsw->nsinks];
141 if (pad < 0) {
142 memset(&fmt->format, 0, sizeof(fmt->format));
143 return 0;
144 }
145 }
146
147 fmt->format = *xsw_get_pad_format(xsw, cfg, pad, fmt->which);
148
149 return 0;
150}
151
152static int xsw_set_format(struct v4l2_subdev *subdev,
153 struct v4l2_subdev_pad_config *cfg,
154 struct v4l2_subdev_format *fmt)
155{
156 struct xswitch_device *xsw = to_xsw(subdev);
157 struct v4l2_mbus_framefmt *format;
158
159
160
161
162 if (fmt->pad >= xsw->nsinks)
163 return xsw_get_format(subdev, cfg, fmt);
164
165 format = xsw_get_pad_format(xsw, cfg, fmt->pad, fmt->which);
166
167 format->code = fmt->format.code;
168 format->width = clamp_t(unsigned int, fmt->format.width,
169 XVIP_MIN_WIDTH, XVIP_MAX_WIDTH);
170 format->height = clamp_t(unsigned int, fmt->format.height,
171 XVIP_MIN_HEIGHT, XVIP_MAX_HEIGHT);
172 format->field = V4L2_FIELD_NONE;
173 format->colorspace = V4L2_COLORSPACE_SRGB;
174
175 fmt->format = *format;
176
177 return 0;
178}
179
180static int xsw_get_routing(struct v4l2_subdev *subdev,
181 struct v4l2_subdev_routing *route)
182{
183 struct xswitch_device *xsw = to_xsw(subdev);
184 unsigned int i;
185
186 mutex_lock(&subdev->entity.graph_obj.mdev->graph_mutex);
187
188 for (i = 0; i < min(xsw->nsources, route->num_routes); ++i) {
189 route->routes[i].sink = xsw->routing[i];
190 route->routes[i].source = i;
191 }
192
193 route->num_routes = xsw->nsources;
194
195 mutex_unlock(&subdev->entity.graph_obj.mdev->graph_mutex);
196
197 return 0;
198}
199
200static int xsw_set_routing(struct v4l2_subdev *subdev,
201 struct v4l2_subdev_routing *route)
202{
203 struct xswitch_device *xsw = to_xsw(subdev);
204 unsigned int i;
205 int ret = 0;
206
207 mutex_lock(&subdev->entity.graph_obj.mdev->graph_mutex);
208
209 if (subdev->entity.stream_count) {
210 ret = -EBUSY;
211 goto done;
212 }
213
214 for (i = 0; i < xsw->nsources; ++i)
215 xsw->routing[i] = -1;
216
217 for (i = 0; i < route->num_routes; ++i)
218 xsw->routing[route->routes[i].source - xsw->nsinks] =
219 route->routes[i].sink;
220
221done:
222 mutex_unlock(&subdev->entity.graph_obj.mdev->graph_mutex);
223 return ret;
224}
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243static void xsw_init_formats(struct v4l2_subdev *subdev,
244 struct v4l2_subdev_fh *fh)
245{
246 struct xswitch_device *xsw = to_xsw(subdev);
247 struct v4l2_subdev_format format;
248 unsigned int i;
249
250 for (i = 0; i < xsw->nsinks; ++i) {
251 memset(&format, 0, sizeof(format));
252
253 format.pad = 0;
254 format.which = fh ? V4L2_SUBDEV_FORMAT_TRY
255 : V4L2_SUBDEV_FORMAT_ACTIVE;
256 format.format.width = 1920;
257 format.format.height = 1080;
258
259 xsw_set_format(subdev, fh ? fh->pad : NULL, &format);
260 }
261}
262
263static int xsw_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
264{
265 xsw_init_formats(subdev, fh);
266
267 return 0;
268}
269
270static int xsw_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
271{
272 return 0;
273}
274
275static struct v4l2_subdev_video_ops xsw_video_ops = {
276 .s_stream = xsw_s_stream,
277};
278
279static struct v4l2_subdev_pad_ops xsw_pad_ops = {
280 .enum_mbus_code = xvip_enum_mbus_code,
281 .enum_frame_size = xvip_enum_frame_size,
282 .get_fmt = xsw_get_format,
283 .set_fmt = xsw_set_format,
284 .get_routing = xsw_get_routing,
285 .set_routing = xsw_set_routing,
286};
287
288static struct v4l2_subdev_ops xsw_ops = {
289 .video = &xsw_video_ops,
290 .pad = &xsw_pad_ops,
291};
292
293static const struct v4l2_subdev_internal_ops xsw_internal_ops = {
294 .open = xsw_open,
295 .close = xsw_close,
296};
297
298
299
300
301
302static bool xsw_has_route(struct media_entity *entity, unsigned int pad0,
303 unsigned int pad1)
304{
305 struct xswitch_device *xsw = container_of(entity, struct xswitch_device,
306 xvip.subdev.entity);
307 unsigned int sink0, sink1;
308
309
310 if (pad0 < xsw->nsinks && pad1 < xsw->nsinks)
311 return false;
312
313 sink0 = pad0 < xsw->nsinks ? pad0 : xsw->routing[pad0 - xsw->nsinks];
314 sink1 = pad1 < xsw->nsinks ? pad1 : xsw->routing[pad1 - xsw->nsinks];
315
316 return sink0 == sink1;
317}
318
319static const struct media_entity_operations xsw_media_ops = {
320 .link_validate = v4l2_subdev_link_validate,
321 .has_route = xsw_has_route,
322};
323
324
325
326
327
328static int xsw_parse_of(struct xswitch_device *xsw)
329{
330 struct device_node *node = xsw->xvip.dev->of_node;
331 int ret;
332
333 ret = of_property_read_u32(node, "#xlnx,inputs", &xsw->nsinks);
334 if (ret < 0) {
335 dev_err(xsw->xvip.dev, "missing or invalid #xlnx,%s property\n",
336 "inputs");
337 return ret;
338 }
339
340 ret = of_property_read_u32(node, "#xlnx,outputs", &xsw->nsources);
341 if (ret < 0) {
342 dev_err(xsw->xvip.dev, "missing or invalid #xlnx,%s property\n",
343 "outputs");
344 return ret;
345 }
346
347 return 0;
348}
349
350static int xsw_probe(struct platform_device *pdev)
351{
352 struct v4l2_subdev *subdev;
353 struct xswitch_device *xsw;
354 unsigned int npads;
355 unsigned int i;
356 int ret;
357
358 xsw = devm_kzalloc(&pdev->dev, sizeof(*xsw), GFP_KERNEL);
359 if (!xsw)
360 return -ENOMEM;
361
362 xsw->xvip.dev = &pdev->dev;
363
364 ret = xsw_parse_of(xsw);
365 if (ret < 0)
366 return ret;
367
368 ret = xvip_init_resources(&xsw->xvip);
369 if (ret < 0)
370 return ret;
371
372
373
374
375 npads = xsw->nsinks + xsw->nsources;
376 xsw->pads = devm_kzalloc(&pdev->dev, npads * sizeof(*xsw->pads),
377 GFP_KERNEL);
378 if (!xsw->pads)
379 goto error;
380
381 for (i = 0; i < xsw->nsinks; ++i)
382 xsw->pads[i].flags = MEDIA_PAD_FL_SINK;
383 for (; i < npads; ++i)
384 xsw->pads[i].flags = MEDIA_PAD_FL_SOURCE;
385
386 xsw->formats = devm_kzalloc(&pdev->dev,
387 xsw->nsinks * sizeof(*xsw->formats),
388 GFP_KERNEL);
389 if (!xsw->formats)
390 goto error;
391
392 for (i = 0; i < xsw->nsources; ++i)
393 xsw->routing[i] = i < xsw->nsinks ? i : -1;
394
395 subdev = &xsw->xvip.subdev;
396 v4l2_subdev_init(subdev, &xsw_ops);
397 subdev->dev = &pdev->dev;
398 subdev->internal_ops = &xsw_internal_ops;
399 strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
400 v4l2_set_subdevdata(subdev, xsw);
401 subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
402 subdev->entity.ops = &xsw_media_ops;
403
404 xsw_init_formats(subdev, NULL);
405
406 ret = media_entity_pads_init(&subdev->entity, npads, xsw->pads);
407 if (ret < 0)
408 goto error;
409
410 platform_set_drvdata(pdev, xsw);
411
412 xvip_print_version(&xsw->xvip);
413
414 ret = v4l2_async_register_subdev(subdev);
415 if (ret < 0) {
416 dev_err(&pdev->dev, "failed to register subdev\n");
417 goto error;
418 }
419
420 return 0;
421
422error:
423 media_entity_cleanup(&subdev->entity);
424 xvip_cleanup_resources(&xsw->xvip);
425 return ret;
426}
427
428static int xsw_remove(struct platform_device *pdev)
429{
430 struct xswitch_device *xsw = platform_get_drvdata(pdev);
431 struct v4l2_subdev *subdev = &xsw->xvip.subdev;
432
433 v4l2_async_unregister_subdev(subdev);
434 media_entity_cleanup(&subdev->entity);
435
436 xvip_cleanup_resources(&xsw->xvip);
437
438 return 0;
439}
440
441static const struct of_device_id xsw_of_id_table[] = {
442 { .compatible = "xlnx,v-switch-1.0" },
443 { }
444};
445MODULE_DEVICE_TABLE(of, xsw_of_id_table);
446
447static struct platform_driver xsw_driver = {
448 .driver = {
449 .name = "xilinx-switch",
450 .of_match_table = xsw_of_id_table,
451 },
452 .probe = xsw_probe,
453 .remove = xsw_remove,
454};
455
456module_platform_driver(xsw_driver);
457
458MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
459MODULE_DESCRIPTION("Xilinx Video Switch Driver");
460MODULE_LICENSE("GPL v2");
461