1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include <linux/module.h>
16#include <linux/of.h>
17#include <linux/platform_device.h>
18#include <linux/slab.h>
19#include <linux/xilinx-hls.h>
20#include <linux/xilinx-v4l2-controls.h>
21
22#include <media/v4l2-async.h>
23#include <media/v4l2-ctrls.h>
24#include <media/v4l2-subdev.h>
25
26#include "xilinx-hls-common.h"
27#include "xilinx-vip.h"
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42struct xhls_device {
43 struct xvip_device xvip;
44 struct media_pad pads[2];
45
46 const char *compatible;
47
48 struct v4l2_mbus_framefmt formats[2];
49 struct v4l2_mbus_framefmt default_formats[2];
50 const struct xvip_video_format *vip_formats[2];
51
52 struct v4l2_ctrl_handler ctrl_handler;
53 struct v4l2_ctrl *model;
54
55 void __iomem *user_mem;
56 size_t user_mem_size;
57};
58
59static inline struct xhls_device *to_hls(struct v4l2_subdev *subdev)
60{
61 return container_of(subdev, struct xhls_device, xvip.subdev);
62}
63
64
65
66
67
68static const struct v4l2_ctrl_config xhls_model_ctrl = {
69 .id = V4L2_CID_XILINX_HLS_MODEL,
70 .name = "HLS Model",
71 .type = V4L2_CTRL_TYPE_STRING,
72 .step = 1,
73 .flags = V4L2_CTRL_FLAG_READ_ONLY,
74};
75
76static int xhls_create_controls(struct xhls_device *xhls)
77{
78 struct v4l2_ctrl_config model = xhls_model_ctrl;
79 struct v4l2_ctrl *ctrl;
80
81 model.max = strlen(xhls->compatible);
82 model.min = model.max;
83
84 v4l2_ctrl_handler_init(&xhls->ctrl_handler, 1);
85
86 ctrl = v4l2_ctrl_new_custom(&xhls->ctrl_handler, &model, NULL);
87
88 if (xhls->ctrl_handler.error) {
89 dev_err(xhls->xvip.dev, "failed to add controls\n");
90 return xhls->ctrl_handler.error;
91 }
92
93 v4l2_ctrl_s_ctrl_string(ctrl, xhls->compatible);
94
95 xhls->xvip.subdev.ctrl_handler = &xhls->ctrl_handler;
96
97 return 0;
98}
99
100
101
102
103
104static int xhls_user_read(struct xhls_device *xhls,
105 struct xilinx_axi_hls_registers *regs)
106{
107 unsigned int i;
108 u32 offset;
109 u32 value;
110
111 if (regs->num_regs >= xhls->user_mem_size / 4)
112 return -EINVAL;
113
114 for (i = 0; i < regs->num_regs; ++i) {
115 if (copy_from_user(&offset, ®s->regs[i].offset,
116 sizeof(offset)))
117 return -EFAULT;
118
119 if (offset >= xhls->user_mem_size || offset & 3)
120 return -EINVAL;
121
122 value = ioread32(xhls->user_mem + offset);
123
124 if (copy_to_user(®s->regs[i].value, &value, sizeof(value)))
125 return -EFAULT;
126 }
127
128 return 0;
129}
130
131static int xhls_user_write(struct xhls_device *xhls,
132 struct xilinx_axi_hls_registers *regs)
133{
134 struct xilinx_axi_hls_register reg;
135 unsigned int i;
136
137 if (regs->num_regs >= xhls->user_mem_size / 4)
138 return -EINVAL;
139
140 for (i = 0; i < regs->num_regs; ++i) {
141 if (copy_from_user(®, ®s->regs[i], sizeof(reg)))
142 return -EFAULT;
143
144 if (reg.offset >= xhls->user_mem_size || reg.offset & 3)
145 return -EINVAL;
146
147 iowrite32(reg.value, xhls->user_mem + reg.offset);
148 }
149
150 return 0;
151}
152
153static long xhls_ioctl(struct v4l2_subdev *subdev, unsigned int cmd, void *arg)
154{
155 struct xhls_device *xhls = to_hls(subdev);
156
157 switch (cmd) {
158 case XILINX_AXI_HLS_READ:
159 return xhls_user_read(xhls, arg);
160 case XILINX_AXI_HLS_WRITE:
161 return xhls_user_write(xhls, arg);
162 }
163
164 return -ENOTTY;
165}
166
167
168
169
170
171static int xhls_s_stream(struct v4l2_subdev *subdev, int enable)
172{
173 struct xhls_device *xhls = to_hls(subdev);
174 struct v4l2_mbus_framefmt *format = &xhls->formats[XVIP_PAD_SINK];
175
176 if (!enable) {
177 xvip_write(&xhls->xvip, XVIP_CTRL_CONTROL, 0);
178 return 0;
179 }
180
181 xvip_write(&xhls->xvip, XHLS_REG_COLS, format->width);
182 xvip_write(&xhls->xvip, XHLS_REG_ROWS, format->height);
183
184 xvip_write(&xhls->xvip, XVIP_CTRL_CONTROL,
185 XHLS_REG_CTRL_AUTO_RESTART | XVIP_CTRL_CONTROL_SW_ENABLE);
186
187 return 0;
188}
189
190
191
192
193
194static struct v4l2_mbus_framefmt *
195__xhls_get_pad_format(struct xhls_device *xhls,
196 struct v4l2_subdev_pad_config *cfg,
197 unsigned int pad, u32 which)
198{
199 switch (which) {
200 case V4L2_SUBDEV_FORMAT_TRY:
201 return v4l2_subdev_get_try_format(&xhls->xvip.subdev, cfg, pad);
202 case V4L2_SUBDEV_FORMAT_ACTIVE:
203 return &xhls->formats[pad];
204 default:
205 return NULL;
206 }
207}
208
209static int xhls_get_format(struct v4l2_subdev *subdev,
210 struct v4l2_subdev_pad_config *cfg,
211 struct v4l2_subdev_format *fmt)
212{
213 struct xhls_device *xhls = to_hls(subdev);
214
215 fmt->format = *__xhls_get_pad_format(xhls, cfg, fmt->pad, fmt->which);
216
217 return 0;
218}
219
220static int xhls_set_format(struct v4l2_subdev *subdev,
221 struct v4l2_subdev_pad_config *cfg,
222 struct v4l2_subdev_format *fmt)
223{
224 struct xhls_device *xhls = to_hls(subdev);
225 struct v4l2_mbus_framefmt *format;
226
227 format = __xhls_get_pad_format(xhls, cfg, fmt->pad, fmt->which);
228
229 if (fmt->pad == XVIP_PAD_SOURCE) {
230 fmt->format = *format;
231 return 0;
232 }
233
234 xvip_set_format_size(format, fmt);
235
236 fmt->format = *format;
237
238
239 format = __xhls_get_pad_format(xhls, cfg, XVIP_PAD_SOURCE,
240 fmt->which);
241
242 xvip_set_format_size(format, fmt);
243
244 return 0;
245}
246
247
248
249
250
251static int xhls_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
252{
253 struct xhls_device *xhls = to_hls(subdev);
254 struct v4l2_mbus_framefmt *format;
255
256
257 format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SINK);
258 *format = xhls->default_formats[XVIP_PAD_SINK];
259
260 format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SOURCE);
261 *format = xhls->default_formats[XVIP_PAD_SOURCE];
262
263 return 0;
264}
265
266static int xhls_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
267{
268 return 0;
269}
270
271static struct v4l2_subdev_core_ops xhls_core_ops = {
272 .ioctl = xhls_ioctl,
273};
274
275static struct v4l2_subdev_video_ops xhls_video_ops = {
276 .s_stream = xhls_s_stream,
277};
278
279static struct v4l2_subdev_pad_ops xhls_pad_ops = {
280 .enum_mbus_code = xvip_enum_mbus_code,
281 .enum_frame_size = xvip_enum_frame_size,
282 .get_fmt = xhls_get_format,
283 .set_fmt = xhls_set_format,
284};
285
286static struct v4l2_subdev_ops xhls_ops = {
287 .core = &xhls_core_ops,
288 .video = &xhls_video_ops,
289 .pad = &xhls_pad_ops,
290};
291
292static const struct v4l2_subdev_internal_ops xhls_internal_ops = {
293 .open = xhls_open,
294 .close = xhls_close,
295};
296
297
298
299
300
301static const struct media_entity_operations xhls_media_ops = {
302 .link_validate = v4l2_subdev_link_validate,
303};
304
305
306
307
308
309static void xhls_init_formats(struct xhls_device *xhls)
310{
311 struct v4l2_mbus_framefmt *format;
312
313
314 format = &xhls->default_formats[XVIP_PAD_SINK];
315 format->code = xhls->vip_formats[XVIP_PAD_SINK]->code;
316 format->field = V4L2_FIELD_NONE;
317 format->colorspace = V4L2_COLORSPACE_SRGB;
318
319 format->width = xvip_read(&xhls->xvip, XHLS_REG_COLS);
320 format->height = xvip_read(&xhls->xvip, XHLS_REG_ROWS);
321
322 xhls->formats[XVIP_PAD_SINK] = *format;
323
324 format = &xhls->default_formats[XVIP_PAD_SOURCE];
325 *format = xhls->default_formats[XVIP_PAD_SINK];
326 format->code = xhls->vip_formats[XVIP_PAD_SOURCE]->code;
327
328 xhls->formats[XVIP_PAD_SOURCE] = *format;
329}
330
331static int xhls_parse_of(struct xhls_device *xhls)
332{
333 struct device *dev = xhls->xvip.dev;
334 struct device_node *node = xhls->xvip.dev->of_node;
335 struct device_node *ports;
336 struct device_node *port;
337 u32 port_id;
338 int ret;
339
340 ret = of_property_read_string(node, "compatible", &xhls->compatible);
341 if (ret < 0)
342 return -EINVAL;
343
344 ports = of_get_child_by_name(node, "ports");
345 if (ports == NULL)
346 ports = node;
347
348
349 for_each_child_of_node(ports, port) {
350 if (port->name && (of_node_cmp(port->name, "port") == 0)) {
351 const struct xvip_video_format *vip_format;
352
353 vip_format = xvip_of_get_format(port);
354 if (IS_ERR(vip_format)) {
355 dev_err(dev, "invalid format in DT");
356 return PTR_ERR(vip_format);
357 }
358
359 ret = of_property_read_u32(port, "reg", &port_id);
360 if (ret < 0) {
361 dev_err(dev, "no reg in DT");
362 return ret;
363 }
364
365 if (port_id != 0 && port_id != 1) {
366 dev_err(dev, "invalid reg in DT");
367 return -EINVAL;
368 }
369
370 xhls->vip_formats[port_id] = vip_format;
371 }
372 }
373
374 return 0;
375}
376
377static int xhls_probe(struct platform_device *pdev)
378{
379 struct v4l2_subdev *subdev;
380 struct xhls_device *xhls;
381 struct resource *mem;
382 int ret;
383
384 xhls = devm_kzalloc(&pdev->dev, sizeof(*xhls), GFP_KERNEL);
385 if (!xhls)
386 return -ENOMEM;
387
388 xhls->xvip.dev = &pdev->dev;
389
390 ret = xhls_parse_of(xhls);
391 if (ret < 0)
392 return ret;
393
394 ret = xvip_init_resources(&xhls->xvip);
395 if (ret < 0)
396 return ret;
397
398 mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
399 xhls->user_mem = devm_ioremap_resource(&pdev->dev, mem);
400 if (IS_ERR(xhls->user_mem))
401 return PTR_ERR(xhls->user_mem);
402 xhls->user_mem_size = resource_size(mem);
403
404
405 xvip_reset(&xhls->xvip);
406
407
408 subdev = &xhls->xvip.subdev;
409 v4l2_subdev_init(subdev, &xhls_ops);
410 subdev->dev = &pdev->dev;
411 subdev->internal_ops = &xhls_internal_ops;
412 strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
413 v4l2_set_subdevdata(subdev, xhls);
414 subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
415
416 xhls_init_formats(xhls);
417
418 xhls->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
419 xhls->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
420 subdev->entity.ops = &xhls_media_ops;
421 ret = media_entity_pads_init(&subdev->entity, 2, xhls->pads);
422 if (ret < 0)
423 goto error;
424
425 ret = xhls_create_controls(xhls);
426 if (ret < 0)
427 goto error;
428
429 platform_set_drvdata(pdev, xhls);
430
431 ret = v4l2_async_register_subdev(subdev);
432 if (ret < 0) {
433 dev_err(&pdev->dev, "failed to register subdev\n");
434 goto error;
435 }
436
437 dev_info(xhls->xvip.dev, "device %s found\n", xhls->compatible);
438
439 return 0;
440
441error:
442 v4l2_ctrl_handler_free(&xhls->ctrl_handler);
443 media_entity_cleanup(&subdev->entity);
444 xvip_cleanup_resources(&xhls->xvip);
445 return ret;
446}
447
448static int xhls_remove(struct platform_device *pdev)
449{
450 struct xhls_device *xhls = platform_get_drvdata(pdev);
451 struct v4l2_subdev *subdev = &xhls->xvip.subdev;
452
453 v4l2_async_unregister_subdev(subdev);
454 v4l2_ctrl_handler_free(&xhls->ctrl_handler);
455 media_entity_cleanup(&subdev->entity);
456
457 xvip_cleanup_resources(&xhls->xvip);
458
459 return 0;
460}
461
462static const struct of_device_id xhls_of_id_table[] = {
463 { .compatible = "xlnx,v-hls" },
464 { }
465};
466MODULE_DEVICE_TABLE(of, xhls_of_id_table);
467
468static struct platform_driver xhls_driver = {
469 .driver = {
470 .name = "xilinx-hls",
471 .of_match_table = xhls_of_id_table,
472 },
473 .probe = xhls_probe,
474 .remove = xhls_remove,
475};
476
477module_platform_driver(xhls_driver);
478
479MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
480MODULE_DESCRIPTION("Xilinx HLS Core Driver");
481MODULE_LICENSE("GPL v2");
482