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 ((((unsigned long)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 struct v4l2_mbus_framefmt *format;
123
124 switch (which) {
125 case V4L2_SUBDEV_FORMAT_TRY:
126 format = v4l2_subdev_get_try_format(&xsw->xvip.subdev,
127 cfg, pad);
128 break;
129 case V4L2_SUBDEV_FORMAT_ACTIVE:
130 format = &xsw->formats[pad];
131 break;
132 default:
133 format = NULL;
134 break;
135 }
136
137 return format;
138}
139
140static int xsw_get_format(struct v4l2_subdev *subdev,
141 struct v4l2_subdev_pad_config *cfg,
142 struct v4l2_subdev_format *fmt)
143{
144 struct xswitch_device *xsw = to_xsw(subdev);
145 int pad = fmt->pad;
146 struct v4l2_mbus_framefmt *format;
147
148 if (pad >= xsw->nsinks) {
149 pad = xsw->routing[pad - xsw->nsinks];
150 if (pad < 0) {
151 memset(&fmt->format, 0, sizeof(fmt->format));
152 return 0;
153 }
154 }
155
156 format = xsw_get_pad_format(xsw, cfg, pad, fmt->which);
157 if (!format)
158 return -EINVAL;
159
160 fmt->format = *format;
161
162 return 0;
163}
164
165static int xsw_set_format(struct v4l2_subdev *subdev,
166 struct v4l2_subdev_pad_config *cfg,
167 struct v4l2_subdev_format *fmt)
168{
169 struct xswitch_device *xsw = to_xsw(subdev);
170 struct v4l2_mbus_framefmt *format;
171
172
173
174
175 if (fmt->pad >= xsw->nsinks)
176 return xsw_get_format(subdev, cfg, fmt);
177
178 format = xsw_get_pad_format(xsw, cfg, fmt->pad, fmt->which);
179 if (!format)
180 return -EINVAL;
181
182 format->code = fmt->format.code;
183 format->width = clamp_t(unsigned int, fmt->format.width,
184 XVIP_MIN_WIDTH, XVIP_MAX_WIDTH);
185 format->height = clamp_t(unsigned int, fmt->format.height,
186 XVIP_MIN_HEIGHT, XVIP_MAX_HEIGHT);
187 format->field = V4L2_FIELD_NONE;
188 format->colorspace = V4L2_COLORSPACE_SRGB;
189
190 fmt->format = *format;
191
192 return 0;
193}
194
195static int xsw_get_routing(struct v4l2_subdev *subdev,
196 struct v4l2_subdev_routing *route)
197{
198 struct xswitch_device *xsw = to_xsw(subdev);
199 unsigned int i;
200
201 mutex_lock(&subdev->entity.graph_obj.mdev->graph_mutex);
202
203 for (i = 0; i < min(xsw->nsources, route->num_routes); ++i) {
204 route->routes[i].sink = xsw->routing[i];
205 route->routes[i].source = i;
206 }
207
208 route->num_routes = xsw->nsources;
209
210 mutex_unlock(&subdev->entity.graph_obj.mdev->graph_mutex);
211
212 return 0;
213}
214
215static int xsw_set_routing(struct v4l2_subdev *subdev,
216 struct v4l2_subdev_routing *route)
217{
218 struct xswitch_device *xsw = to_xsw(subdev);
219 unsigned int i;
220 int ret = 0;
221
222 mutex_lock(&subdev->entity.graph_obj.mdev->graph_mutex);
223
224 if (subdev->entity.stream_count) {
225 ret = -EBUSY;
226 goto done;
227 }
228
229 for (i = 0; i < xsw->nsources; ++i)
230 xsw->routing[i] = -1;
231
232 for (i = 0; i < route->num_routes; ++i)
233 xsw->routing[route->routes[i].source - xsw->nsinks] =
234 route->routes[i].sink;
235
236done:
237 mutex_unlock(&subdev->entity.graph_obj.mdev->graph_mutex);
238 return ret;
239}
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258static void xsw_init_formats(struct v4l2_subdev *subdev,
259 struct v4l2_subdev_fh *fh)
260{
261 struct xswitch_device *xsw = to_xsw(subdev);
262 struct v4l2_subdev_format format;
263 unsigned int i;
264
265 for (i = 0; i < xsw->nsinks; ++i) {
266 memset(&format, 0, sizeof(format));
267
268 format.pad = 0;
269 format.which = fh ? V4L2_SUBDEV_FORMAT_TRY
270 : V4L2_SUBDEV_FORMAT_ACTIVE;
271 format.format.width = 1920;
272 format.format.height = 1080;
273
274 xsw_set_format(subdev, fh ? fh->pad : NULL, &format);
275 }
276}
277
278static int xsw_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
279{
280 xsw_init_formats(subdev, fh);
281
282 return 0;
283}
284
285static int xsw_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
286{
287 return 0;
288}
289
290static struct v4l2_subdev_video_ops xsw_video_ops = {
291 .s_stream = xsw_s_stream,
292};
293
294static struct v4l2_subdev_pad_ops xsw_pad_ops = {
295 .enum_mbus_code = xvip_enum_mbus_code,
296 .enum_frame_size = xvip_enum_frame_size,
297 .get_fmt = xsw_get_format,
298 .set_fmt = xsw_set_format,
299 .get_routing = xsw_get_routing,
300 .set_routing = xsw_set_routing,
301};
302
303static struct v4l2_subdev_ops xsw_ops = {
304 .video = &xsw_video_ops,
305 .pad = &xsw_pad_ops,
306};
307
308static const struct v4l2_subdev_internal_ops xsw_internal_ops = {
309 .open = xsw_open,
310 .close = xsw_close,
311};
312
313
314
315
316
317static bool xsw_has_route(struct media_entity *entity, unsigned int pad0,
318 unsigned int pad1)
319{
320 struct xswitch_device *xsw = container_of(entity, struct xswitch_device,
321 xvip.subdev.entity);
322 unsigned int sink0, sink1;
323
324
325 if (pad0 < xsw->nsinks && pad1 < xsw->nsinks)
326 return false;
327
328 sink0 = pad0 < xsw->nsinks ? pad0 : xsw->routing[pad0 - xsw->nsinks];
329 sink1 = pad1 < xsw->nsinks ? pad1 : xsw->routing[pad1 - xsw->nsinks];
330
331 return sink0 == sink1;
332}
333
334static const struct media_entity_operations xsw_media_ops = {
335 .link_validate = v4l2_subdev_link_validate,
336 .has_route = xsw_has_route,
337};
338
339
340
341
342
343static int xsw_parse_of(struct xswitch_device *xsw)
344{
345 struct device_node *node = xsw->xvip.dev->of_node;
346 int ret;
347
348 ret = of_property_read_u32(node, "#xlnx,inputs", &xsw->nsinks);
349 if (ret < 0) {
350 dev_err(xsw->xvip.dev, "missing or invalid #xlnx,%s property\n",
351 "inputs");
352 return ret;
353 }
354
355 ret = of_property_read_u32(node, "#xlnx,outputs", &xsw->nsources);
356 if (ret < 0) {
357 dev_err(xsw->xvip.dev, "missing or invalid #xlnx,%s property\n",
358 "outputs");
359 return ret;
360 }
361
362 return 0;
363}
364
365static int xsw_probe(struct platform_device *pdev)
366{
367 struct v4l2_subdev *subdev;
368 struct xswitch_device *xsw;
369 unsigned int npads;
370 unsigned int i;
371 int ret;
372
373 xsw = devm_kzalloc(&pdev->dev, sizeof(*xsw), GFP_KERNEL);
374 if (!xsw)
375 return -ENOMEM;
376
377 xsw->xvip.dev = &pdev->dev;
378
379 ret = xsw_parse_of(xsw);
380 if (ret < 0)
381 return ret;
382
383 ret = xvip_init_resources(&xsw->xvip);
384 if (ret < 0)
385 return ret;
386
387
388
389
390 npads = xsw->nsinks + xsw->nsources;
391 xsw->pads = devm_kzalloc(&pdev->dev, npads * sizeof(*xsw->pads),
392 GFP_KERNEL);
393 if (!xsw->pads)
394 goto error_resources;
395
396 for (i = 0; i < xsw->nsinks; ++i)
397 xsw->pads[i].flags = MEDIA_PAD_FL_SINK;
398 for (; i < npads; ++i)
399 xsw->pads[i].flags = MEDIA_PAD_FL_SOURCE;
400
401 xsw->formats = devm_kzalloc(&pdev->dev,
402 xsw->nsinks * sizeof(*xsw->formats),
403 GFP_KERNEL);
404 if (!xsw->formats)
405 goto error_resources;
406
407 for (i = 0; i < xsw->nsources; ++i)
408 xsw->routing[i] = i < xsw->nsinks ? i : -1;
409
410 subdev = &xsw->xvip.subdev;
411 v4l2_subdev_init(subdev, &xsw_ops);
412 subdev->dev = &pdev->dev;
413 subdev->internal_ops = &xsw_internal_ops;
414 strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
415 v4l2_set_subdevdata(subdev, xsw);
416 subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
417 subdev->entity.ops = &xsw_media_ops;
418
419 xsw_init_formats(subdev, NULL);
420
421 ret = media_entity_pads_init(&subdev->entity, npads, xsw->pads);
422 if (ret < 0)
423 goto error;
424
425 platform_set_drvdata(pdev, xsw);
426
427 xvip_print_version(&xsw->xvip);
428
429 ret = v4l2_async_register_subdev(subdev);
430 if (ret < 0) {
431 dev_err(&pdev->dev, "failed to register subdev\n");
432 goto error;
433 }
434
435 return 0;
436
437error:
438 media_entity_cleanup(&subdev->entity);
439error_resources:
440 xvip_cleanup_resources(&xsw->xvip);
441 return ret;
442}
443
444static int xsw_remove(struct platform_device *pdev)
445{
446 struct xswitch_device *xsw = platform_get_drvdata(pdev);
447 struct v4l2_subdev *subdev = &xsw->xvip.subdev;
448
449 v4l2_async_unregister_subdev(subdev);
450 media_entity_cleanup(&subdev->entity);
451
452 xvip_cleanup_resources(&xsw->xvip);
453
454 return 0;
455}
456
457static const struct of_device_id xsw_of_id_table[] = {
458 { .compatible = "xlnx,v-switch-1.0" },
459 { }
460};
461MODULE_DEVICE_TABLE(of, xsw_of_id_table);
462
463static struct platform_driver xsw_driver = {
464 .driver = {
465 .name = "xilinx-switch",
466 .of_match_table = xsw_of_id_table,
467 },
468 .probe = xsw_probe,
469 .remove = xsw_remove,
470};
471
472module_platform_driver(xsw_driver);
473
474MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
475MODULE_DESCRIPTION("Xilinx Video Switch Driver");
476MODULE_LICENSE("GPL v2");
477